From 469b0203b74f5d64768ef871f6cab609599545fd Mon Sep 17 00:00:00 2001 From: archipelago Date: Wed, 1 Jul 2026 16:14:55 -0400 Subject: [PATCH] fix(reticulum-daemon): die with parent to stop RNode-jamming pile-ups The daemon ships as a PyInstaller one-file binary; its direct parent is the bootloader, which the Rust supervisor (mesh/reticulum.rs Drop) stops via start_kill() == SIGKILL. SIGKILL can't be forwarded, so the Python child was orphaned on every link recreation and kept holding the RNode serial port. These stale daemons piled up (9 seen on one node), all clutching /dev/ttyUSB0 and garbling the RNode so it silently stopped transmitting (txb frozen, interface status False). Set PR_SET_PDEATHSIG(SIGTERM) at daemon startup so the kernel signals us when the parent exits; our existing SIGTERM handler then shuts down cleanly and frees the port. Linux-only, best-effort, no-op elsewhere. Co-Authored-By: Claude Opus 4.8 (1M context) --- reticulum-daemon/reticulum_daemon.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/reticulum-daemon/reticulum_daemon.py b/reticulum-daemon/reticulum_daemon.py index 2fd17985..3fb15eef 100644 --- a/reticulum-daemon/reticulum_daemon.py +++ b/reticulum-daemon/reticulum_daemon.py @@ -502,7 +502,32 @@ def _parse_args(argv): return p.parse_args(argv) +def _install_parent_death_signal() -> None: + """Die when our parent process does. + + The daemon ships as a PyInstaller one-file binary: our direct parent is the + bootloader, and the Rust supervisor (mesh/reticulum.rs) stops us by SIGKILL- + ing that bootloader. SIGKILL can't be forwarded, so without this the Python + child is orphaned and keeps holding the RNode serial port — which piles up + stale daemons that jam the radio (observed: 9 instances on one node). Asking + the kernel to send us SIGTERM on parent death lets our existing SIGTERM + handler shut down cleanly and free the port. Linux-only; no-op elsewhere. + """ + if sys.platform != "linux": + return + try: + import ctypes + + PR_SET_PDEATHSIG = 1 + ctypes.CDLL("libc.so.6", use_errno=True).prctl( + PR_SET_PDEATHSIG, signal.SIGTERM + ) + except Exception: + pass # best-effort; never block startup on this + + def main(argv=None) -> int: + _install_parent_death_signal() args = _parse_args(argv if argv is not None else sys.argv[1:]) if args.check: