use std::io; use std::os::unix::process::CommandExt; use std::process::{Command, Stdio}; /// Spawn a child process fully detached from this process group. /// /// The child gets its own session via `setsid(2)` and null stdio, so it /// survives the parent exiting and doesn't take a SIGKILL cascade when /// pixelpass dies. /// /// A detached reaper thread `wait()`s the child so it doesn't linger as a /// `` zombie under a long-lived parent — the `--gui` front-end launches /// players itself and lives for the whole session, and `std::process::Child` /// (unlike tokio's) has no orphan reaping, so simply dropping the handle would /// leak a zombie per closed player. If the parent exits while the player is /// still up, the reaper thread dies with it but the `setsid`'d player survives /// and is reaped by init. (A double-fork would also avoid the zombie, but /// `fork(2)` followed by non-trivial work in this multithreaded process is /// unsound — the reaper thread is the safe equivalent.) pub fn spawn_detached(prog: &str, args: &[&str]) -> io::Result<()> { let child = unsafe { Command::new(prog) .args(args) .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) .pre_exec(|| { nix::unistd::setsid().ok(); Ok(()) }) .spawn()? }; std::thread::spawn(move || { let mut child = child; let _ = child.wait(); }); Ok(()) }