From fbfd207fe83333c1533786b23fcb30e1ea6c7c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniele=20Vigan=C3=B2?= Date: Sun, 1 Mar 2026 21:47:28 +0100 Subject: [PATCH] Manage dcc-usb-connector auto-stop on removal --- connector/99-dcc-usb-connector.rules | 12 ++- connector/INSTALL.md | 11 ++ connector/README.md | 44 +++++++- connector/dcc-usb-connector.service | 3 + connector/test-udev-autostart.sh | 147 +++++++++++++++++++++++++++ 5 files changed, 208 insertions(+), 9 deletions(-) create mode 100755 connector/test-udev-autostart.sh diff --git a/connector/99-dcc-usb-connector.rules b/connector/99-dcc-usb-connector.rules index e9a45b4..2677a73 100644 --- a/connector/99-dcc-usb-connector.rules +++ b/connector/99-dcc-usb-connector.rules @@ -1,7 +1,7 @@ -# Udev rule to auto-start dcc-usb-connector.service when USB device is connected +# Udev rule to auto-start/stop dcc-usb-connector.service when USB device is connected/removed # # This rule detects when a CH340 USB-to-serial adapter (ID 1a86:7523) -# is connected and assigned to /dev/ttyUSB0, then automatically starts +# is connected/removed on /dev/ttyUSB0, then automatically starts/stops # the dcc-usb-connector.service (user systemd service). # # Installation: @@ -13,8 +13,10 @@ # udevadm test /sys/class/tty/ttyUSB0 # udevadm monitor --property --subsystem-match=tty # -# The service will be started/stopped automatically when the device -# is plugged in/unplugged. +# The service will be started when the device is plugged in and stopped +# when the device is unplugged. -# Match USB device 1a86:7523 on ttyUSB0 and trigger user systemd service +# Match USB device 1a86:7523 on ttyUSB0 +# TAG+="systemd" tells systemd to track this device +# ENV{SYSTEMD_USER_WANTS} starts the service on "add" and stops it on "remove" SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", KERNEL=="ttyUSB0", TAG+="systemd", ENV{SYSTEMD_USER_WANTS}="dcc-usb-connector.service" diff --git a/connector/INSTALL.md b/connector/INSTALL.md index fe8bd79..8eaae23 100644 --- a/connector/INSTALL.md +++ b/connector/INSTALL.md @@ -161,6 +161,17 @@ The service (`dcc-usb-connector.service`): 2. Executes `ncat -n -k -l 2560 /dev/ttyUSB0` to bridge serial ↔ network 3. Uses `KillMode=mixed` for proper process cleanup 4. Terminates within 5 seconds when stopped +5. **Uses `StopWhenUnneeded=yes`** - This ensures the service stops when the device is removed + +### Auto-Stop Mechanism + +When the USB device is unplugged: +1. **Udev detects** the removal event +2. **Systemd removes** the device dependency from the service +3. **StopWhenUnneeded=yes** tells systemd to automatically stop the service when no longer needed +4. **Service terminates** gracefully within 5 seconds + +This combination ensures clean automatic stop without requiring manual intervention or custom scripts. ## Troubleshooting diff --git a/connector/README.md b/connector/README.md index a7b8ad8..b3cfc2e 100644 --- a/connector/README.md +++ b/connector/README.md @@ -1,17 +1,53 @@ -# Use a container to implement a serial to net bridge +# DCC Serial-to-Network Bridge -This uses `ncat` from [nmap](https://nmap.org/ncat/) to bridge a serial port to a network port. The serial port is passed to the Podman command (eg. `/dev/ttyACM0`) and the network port is `2560`. +This directory provides two ways to bridge a serial port to a network port using `ncat` from [nmap](https://nmap.org/ncat/): + +1. **Auto-Start with systemd + udev** (Recommended) - Automatically starts/stops when USB device is plugged/unplugged +2. **Container-based** - Manual control using Podman/Docker > [!IMPORTANT] > Other variants of `nc` or `ncat` may not work as expected. -## Build and run the container +## Option 1: Auto-Start with systemd + udev (Recommended) + +Automatically start the bridge when USB device `1a86:7523` is connected to `/dev/ttyUSB0` and stop it when removed. + +### Quick Install ```bash -$ podman buil -t dcc/bridge . +./install-udev-rule.sh +``` + +### Features +- ✅ Auto-start when device connected +- ✅ Auto-stop when device removed +- ✅ User-level service (no root needed) +- ✅ Runs on boot (with lingering enabled) + +See [INSTALL.md](INSTALL.md) for detailed documentation. + +### Test + +```bash +# Run the test script +./test-udev-autostart.sh + +# Or manually check +systemctl --user status dcc-usb-connector.service +telnet localhost 2560 +``` + +## Option 2: Container-based (Manual) + +### Build and run the container + +```bash +$ podman build -t dcc/bridge . $ podman run -d --group-add keep-groups --device=/dev/ttyACM0:/dev/arduino -p 2560:2560 --name dcc-bridge dcc/bridge ``` +### Test + It can be tested with `telnet`: ```bash diff --git a/connector/dcc-usb-connector.service b/connector/dcc-usb-connector.service index cc18c5e..5516ecc 100644 --- a/connector/dcc-usb-connector.service +++ b/connector/dcc-usb-connector.service @@ -1,7 +1,10 @@ [Unit] Description=DCC USB-to-Network Bridge Daemon After=network.target +# Device will be available via udev rule, but add condition as safety check ConditionPathIsReadWrite=/dev/ttyUSB0 +# Stop this service when the device is no longer needed (removed) +StopWhenUnneeded=yes [Service] ExecStartPre=/usr/bin/stty -F /dev/ttyUSB0 -echo 115200 diff --git a/connector/test-udev-autostart.sh b/connector/test-udev-autostart.sh new file mode 100755 index 0000000..6439610 --- /dev/null +++ b/connector/test-udev-autostart.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# +# Test script for DCC USB-to-Network Bridge auto-start/stop functionality +# +# This script helps verify that the service starts when the USB device +# is connected and stops when it's removed. +# +# Usage: +# ./test-udev-autostart.sh +# + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}=== DCC USB-to-Network Bridge Auto-Start/Stop Test ===${NC}" +echo + +# Check if udev rule is installed +echo -e "${BLUE}1. Checking udev rule installation...${NC}" +if [ -f /etc/udev/rules.d/99-dcc-usb-connector.rules ]; then + echo -e "${GREEN}✓ Udev rule is installed${NC}" + echo " Location: /etc/udev/rules.d/99-dcc-usb-connector.rules" +else + echo -e "${RED}✗ Udev rule is NOT installed${NC}" + echo " Run: sudo cp 99-dcc-usb-connector.rules /etc/udev/rules.d/" + exit 1 +fi +echo + +# Check if service is installed +echo -e "${BLUE}2. Checking systemd service installation...${NC}" +if [ -f ~/.config/systemd/user/dcc-usb-connector.service ]; then + echo -e "${GREEN}✓ Systemd service is installed${NC}" + echo " Location: ~/.config/systemd/user/dcc-usb-connector.service" +else + echo -e "${RED}✗ Systemd service is NOT installed${NC}" + echo " Run: cp dcc-usb-connector.service ~/.config/systemd/user/" + exit 1 +fi +echo + +# Check lingering +echo -e "${BLUE}3. Checking systemd lingering...${NC}" +if loginctl show-user "$USER" | grep -q "Linger=yes"; then + echo -e "${GREEN}✓ Lingering is enabled${NC}" +else + echo -e "${YELLOW}⚠ Lingering is NOT enabled${NC}" + echo " Services may not start automatically when you're not logged in" + echo " Run: sudo loginctl enable-linger $USER" +fi +echo + +# Check if device is connected +echo -e "${BLUE}4. Checking USB device...${NC}" +if lsusb | grep -q "1a86:7523"; then + echo -e "${GREEN}✓ USB device 1a86:7523 is connected${NC}" + lsusb | grep "1a86:7523" + + if [ -e /dev/ttyUSB0 ]; then + echo -e "${GREEN}✓ /dev/ttyUSB0 exists${NC}" + ls -l /dev/ttyUSB0 + else + echo -e "${YELLOW}⚠ /dev/ttyUSB0 does NOT exist${NC}" + echo " The device may be on a different port" + echo " Available ttyUSB devices:" + ls -l /dev/ttyUSB* 2>/dev/null || echo " (none found)" + fi +else + echo -e "${YELLOW}⚠ USB device 1a86:7523 is NOT connected${NC}" + echo " Please plug in the device to test" +fi +echo + +# Check service status +echo -e "${BLUE}5. Checking service status...${NC}" +if systemctl --user is-active --quiet dcc-usb-connector.service; then + echo -e "${GREEN}✓ Service is RUNNING${NC}" + systemctl --user status dcc-usb-connector.service --no-pager -l +else + echo -e "${YELLOW}⚠ Service is NOT running${NC}" + echo " Status:" + systemctl --user status dcc-usb-connector.service --no-pager -l || true +fi +echo + +# Test udev rule +echo -e "${BLUE}6. Testing udev rule (if device is connected)...${NC}" +if [ -e /dev/ttyUSB0 ]; then + echo " Running: udevadm test /sys/class/tty/ttyUSB0" + echo " Looking for SYSTEMD_USER_WANTS..." + if udevadm test /sys/class/tty/ttyUSB0 2>&1 | grep -q "SYSTEMD_USER_WANTS"; then + echo -e "${GREEN}✓ Udev rule is triggering systemd${NC}" + udevadm test /sys/class/tty/ttyUSB0 2>&1 | grep "SYSTEMD_USER_WANTS" + else + echo -e "${RED}✗ Udev rule is NOT triggering systemd${NC}" + echo " The rule may not be matching correctly" + fi +else + echo -e "${YELLOW}⚠ Cannot test udev rule - device not connected${NC}" +fi +echo + +# Check network port +echo -e "${BLUE}7. Checking network port 2560...${NC}" +if netstat -tuln 2>/dev/null | grep -q ":2560" || ss -tuln 2>/dev/null | grep -q ":2560"; then + echo -e "${GREEN}✓ Port 2560 is listening${NC}" + netstat -tuln 2>/dev/null | grep ":2560" || ss -tuln 2>/dev/null | grep ":2560" +else + echo -e "${YELLOW}⚠ Port 2560 is NOT listening${NC}" + echo " Service may not be running or ncat failed to start" +fi +echo + +# Summary and instructions +echo -e "${BLUE}=== Test Summary ===${NC}" +echo +echo "To test auto-start/stop behavior:" +echo +echo "1. ${YELLOW}Monitor the service in one terminal:${NC}" +echo " watch -n 1 'systemctl --user status dcc-usb-connector.service'" +echo +echo "2. ${YELLOW}Monitor udev events in another terminal:${NC}" +echo " udevadm monitor --property --subsystem-match=tty" +echo +echo "3. ${YELLOW}Plug in the USB device${NC} and watch:" +echo " - Udev should detect the device" +echo " - Service should automatically start" +echo " - Port 2560 should become available" +echo +echo "4. ${YELLOW}Unplug the USB device${NC} and watch:" +echo " - Udev should detect device removal" +echo " - Service should automatically stop (thanks to StopWhenUnneeded=yes)" +echo " - Port 2560 should close" +echo +echo "5. ${YELLOW}Check logs:${NC}" +echo " journalctl --user -u dcc-usb-connector.service -f" +echo +echo "Expected behavior:" +echo " • Device connected → Service starts → Port 2560 opens" +echo " • Device removed → Service stops → Port 2560 closes" +echo