Merge remote-tracking branch 'gitea-ai/fix/reticulum-daemon-process-group'
This commit is contained in:
commit
7a7fec21d4
1
core/Cargo.lock
generated
1
core/Cargo.lock
generated
@ -129,6 +129,7 @@ dependencies = [
|
|||||||
"hyper-ws-listener",
|
"hyper-ws-listener",
|
||||||
"iroh",
|
"iroh",
|
||||||
"iroh-blobs",
|
"iroh-blobs",
|
||||||
|
"libc",
|
||||||
"mainline",
|
"mainline",
|
||||||
"mdns-sd",
|
"mdns-sd",
|
||||||
"nostr-sdk",
|
"nostr-sdk",
|
||||||
|
|||||||
@ -22,6 +22,7 @@ iroh-swarm = ["dep:iroh", "dep:iroh-blobs"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# Core dependencies
|
# Core dependencies
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
libc = "0.2" # process-group signalling for the supervised reticulum daemon
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
|||||||
@ -104,6 +104,11 @@ fn daemon_command(
|
|||||||
.arg(x);
|
.arg(x);
|
||||||
}
|
}
|
||||||
cmd.kill_on_drop(true)
|
cmd.kill_on_drop(true)
|
||||||
|
// Run the daemon as its own process-group leader. The packaged binary is
|
||||||
|
// a PyInstaller one-file bootloader that forks the real Python process;
|
||||||
|
// making it a group leader lets Drop signal the WHOLE group so the
|
||||||
|
// forked child can't be orphaned and keep holding the serial port.
|
||||||
|
.process_group(0)
|
||||||
.stdin(std::process::Stdio::null())
|
.stdin(std::process::Stdio::null())
|
||||||
.stdout(std::process::Stdio::piped())
|
.stdout(std::process::Stdio::piped())
|
||||||
.stderr(std::process::Stdio::piped());
|
.stderr(std::process::Stdio::piped());
|
||||||
@ -945,9 +950,21 @@ fn contains_detect_resp(buf: &[u8]) -> bool {
|
|||||||
|
|
||||||
impl Drop for ReticulumLink {
|
impl Drop for ReticulumLink {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Best-effort: ask the daemon to shut down cleanly (frees the serial
|
// The packaged daemon is a PyInstaller one-file bootloader that forks the
|
||||||
// port promptly); `kill_on_drop` on the Command is the hard backstop
|
// real Python process into the same (leader) group we spawned it in.
|
||||||
// if the daemon doesn't exit in time.
|
// SIGKILLing only the bootloader (what `start_kill`/`kill_on_drop` do)
|
||||||
|
// orphans that child, which keeps holding the serial port — the root
|
||||||
|
// cause of daemons piling up across reconnects and jamming the RNode.
|
||||||
|
// Signal the whole process group instead: SIGTERM is caught by the
|
||||||
|
// daemon's handler (clean RNode + socket release), and SIGKILL is the
|
||||||
|
// hard backstop so a wedged daemon can never survive.
|
||||||
|
if let Some(pid) = self.child.id() {
|
||||||
|
let pgid = -(pid as i32);
|
||||||
|
unsafe {
|
||||||
|
libc::kill(pgid, libc::SIGTERM);
|
||||||
|
libc::kill(pgid, libc::SIGKILL);
|
||||||
|
}
|
||||||
|
}
|
||||||
let _ = self.child.start_kill();
|
let _ = self.child.start_kill();
|
||||||
let _ = std::fs::remove_file(&self.socket_path);
|
let _ = std::fs::remove_file(&self.socket_path);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user