From fd14ba67d5f6dfcc7dab413e6275d56f82fc699f Mon Sep 17 00:00:00 2001 From: Tim Hellhake Date: Sat, 23 Sep 2023 21:40:15 +0200 Subject: [PATCH] Make exclusive access optional --- serial.go | 9 +++++++++ serial_linux_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ serial_unix.go | 4 +++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/serial.go b/serial.go index abfd7f9..8b878d2 100644 --- a/serial.go +++ b/serial.go @@ -101,6 +101,7 @@ type Mode struct { Parity Parity // Parity (see Parity type for more info) StopBits StopBits // Stop bits (see StopBits type for more info) InitialStatusBits *ModemOutputBits // Initial output modem bits status (if nil defaults to DTR=true and RTS=true) + AccessMode AccessMode // Access mode on unix systems (Default: Exclusive) } // Parity describes a serial port parity setting @@ -131,6 +132,14 @@ const ( TwoStopBits ) +// AccessMode whether to access the port exclusively +type AccessMode int + +const ( + Exclusive = 0 + Shared = 1 +) + // PortError is a platform independent error type for serial ports type PortError struct { code PortErrorCode diff --git a/serial_linux_test.go b/serial_linux_test.go index 1a7e1a3..3e37806 100644 --- a/serial_linux_test.go +++ b/serial_linux_test.go @@ -12,6 +12,7 @@ package serial import ( "context" "os/exec" + "syscall" "testing" "time" @@ -62,3 +63,46 @@ func TestDoubleCloseIsNoop(t *testing.T) { require.NoError(t, port.Close()) require.NoError(t, port.Close()) } + +func TestAccessModeDefault(t *testing.T) { + AccessModeExclusive(t, &Mode{}) +} + +func TestAccessModeExclusive(t *testing.T) { + mode := &Mode{ + AccessMode: Exclusive, + } + + AccessModeExclusive(t, mode) +} + +func AccessModeExclusive(t *testing.T, mode *Mode) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + cmd := startSocatAndWaitForPort(t, ctx) + go cmd.Wait() + + port, err := Open("/tmp/faketty", mode) + require.NoError(t, err) + _, err2 := Open("/tmp/faketty", mode) + require.Error(t, err2, syscall.ENOENT) + require.NoError(t, port.Close()) +} + +func TestAccessModeShared(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + cmd := startSocatAndWaitForPort(t, ctx) + go cmd.Wait() + + mode := &Mode{ + AccessMode: Shared, + } + + port, err := Open("/tmp/faketty", mode) + require.NoError(t, err) + port2, err2 := Open("/tmp/faketty", mode) + require.NoError(t, err2) + require.NoError(t, port.Close()) + require.NoError(t, port2.Close()) +} diff --git a/serial_unix.go b/serial_unix.go index f6ec896..1bb745b 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -275,7 +275,9 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { unix.SetNonblock(h, false) - port.acquireExclusiveAccess() + if mode.AccessMode == Exclusive { + port.acquireExclusiveAccess() + } // This pipe is used as a signal to cancel blocking Read pipe := &unixutils.Pipe{}