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) <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-07-01 16:14:55 -04:00
parent 81444ab4a8
commit 469b0203b7

View File

@ -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: