//go:build linux /* * ============================================================================ * Projekt.....: rs2322tcp * Datei.......: internal/client/virtual_port_manager_linux.go * Copyright (C) 2026 Dieter Lang * * SPDX-License-Identifier: GPL-3.0-or-later * * Beschreibung: * Linux-spezifische Erzeugung und Verwaltung virtueller rs2322tcp-Ports. * * Der öffentliche /dev/ttyUSBxxx-Eintrag wird bei der Installation * bereitgestellt. Der Client verwaltet ausschließlich den internen Link * unter ~/.rs2322tcp/virtual/. * ============================================================================ */ package client import "fmt" /////////////////////////////////////////////////////////////////////////////// // Managed virtual port /////////////////////////////////////////////////////////////////////////////// // ManagedVirtualPort verbindet einen reservierten Port mit einer // VirtualSerial-Instanz und verwaltet deren Lebenszyklus. type ManagedVirtualPort struct { port *VirtualPort manager *VirtualPortManager number int } // Path returns the device path visible to the external application. func (p *ManagedVirtualPort) Path() string { if p == nil || p.port == nil { return "" } return p.port.Path() } // Read reads data from the underlying virtual serial device. func (p *ManagedVirtualPort) Read(b []byte) (int, error) { if p == nil || p.port == nil { return 0, fmt.Errorf("managed virtual port is nil") } return p.port.Read(b) } // Write writes data to the underlying virtual serial device. func (p *ManagedVirtualPort) Write(b []byte) (int, error) { if p == nil || p.port == nil { return 0, fmt.Errorf("managed virtual port is nil") } return p.port.Write(b) } // Close removes the internal link, closes the PTY and releases the // reservation. func (p *ManagedVirtualPort) Close() error { if p == nil || p.port == nil { return nil } var firstErr error if err := removeVirtualPortLink(p.port.Path()); err != nil { firstErr = err } if err := p.port.Close(); err != nil && firstErr == nil { firstErr = err } if p.manager != nil { p.manager.Release(p.number) } p.port = nil return firstErr } /////////////////////////////////////////////////////////////////////////////// // Open /////////////////////////////////////////////////////////////////////////////// // Open creates a new virtual serial port using the next free port number. // // The internal PTY is created by newVirtualSerial(). The internal // ~/.rs2322tcp/virtual/ttyUSBxxx link is then pointed at the PTY. func (m *VirtualPortManager) Open() (*ManagedVirtualPort, error) { if m == nil { return nil, fmt.Errorf("virtual port manager is nil") } number, err := m.Reserve() if err != nil { return nil, err } portPath := m.PortPath(number) serial, err := newVirtualSerial() if err != nil { m.Release(number) return nil, fmt.Errorf( "create virtual serial for %s: %w", portPath, err, ) } if err := setVirtualPortLink(portPath, serial.Path()); err != nil { _ = serial.Close() m.Release(number) return nil, fmt.Errorf( "bind %s to %s: %w", portPath, serial.Path(), err, ) } port := NewVirtualPort(portPath, serial) return &ManagedVirtualPort{ port: port, manager: m, number: number, }, nil }