mdlayher / consrv

Command consrv is a SSH to serial console bridge server, originally designed for deployment on gokrazy.org devices. Apache 2.0 Licensed.
Other
136 stars 4 forks source link

consrv does not gracefully handle disappearing / re-appearing serial device #3

Open stapelberg opened 2 years ago

stapelberg commented 2 years ago

In my setup, for whichever reason, the USB-to-serial adapter occasionally drops off the USB bus and comes back as ttyUSB1 instead of ttyUSB0:

[776891.671139] cp210x ttyUSB1: usb_serial_generic_read_bulk_callback - urb stopped: -32
[776891.680238] cp210x ttyUSB1: usb_serial_generic_read_bulk_callback - urb stopped: -32
[776891.764819] usb 1-1-port3: disabled by hub (EMI?), re-enabling...
[776891.772885] usb 1-1.3: USB disconnect, device number 6
[776891.779671] cp210x ttyUSB1: failed set request 0x12 status: -19
[776891.786843] cp210x ttyUSB1: failed set request 0x0 status: -19
[776891.794229] cp210x ttyUSB1: cp210x converter now disconnected from ttyUSB1
[776891.802492] cp210x 1-1.3:1.0: device disconnected
[776892.031977] usb 1-1.3: new full-speed USB device number 7 using dwc2
[776892.143732] cp210x 1-1.3:1.0: cp210x converter detected
[776892.151568] usb 1-1.3: cp210x converter now attached to ttyUSB0

I don’t know yet if this correlates to any specific trigger (perhaps rebooting my router?), but it happens frequently enough to be noticeable.

This behavior can be reproduced by physically un-plugging the USB-to-serial adapter and plugging it back in.

On the Go side, the symptom is that Read() returns an io.EOF error.

Currently, consrv connections just hang indefinitely until you send a byte, which triggers a write to the ttyUSB0 device, which triggers a write /dev/ttyUSB1: input/output error that then closes the SSH session.

There are a number of things subtly wrong that result in the silent swallowing of the error. I can send a PR that addresses enough of them to make the SSH session close immediately when un-plugging the adapter.

What’s still left to be done is closing the mux device and underlying serial port, and then re-opening it on the next connection.

stapelberg commented 2 years ago

What’s still left to be done is closing the mux device and underlying serial port, and then re-opening it on the next connection.

A pragmatic workaround for this is to enable logtostdout (see PR #5) and make consrv log.Fatalf when reading EOF:

diff --git i/cmd/consrv/main.go w/cmd/consrv/main.go
index da918a0..18cef58 100644
--- i/cmd/consrv/main.go
+++ w/cmd/consrv/main.go
@@ -119,6 +119,10 @@ func main() {
                if err := scanner.Err(); err != nil {
                    ll.Printf("copying serial to stdout: %v", err)
                }
+               // io.EOF (not considered an error by scanner.Err()) is
+               // unexpected here, as we expect an infinite serial
+               // stream. Encountering io.EOF means the device has disappeared.
+               log.Fatalf("device disappeared: %s", d.Name)
            }()
        }
    }

When unplugging the serial adapter, consrv starts crashlooping until the adapter is plugged back in.

Obviously this is only doable when you have precisely 1 serial adapter you care about, otherwise the failure of one results in all other adapters being unavailable, too.