use std::process::{Child, Command};
pub fn spawn(make: impl Fn() -> Command) -> std::io::Result<Child> {
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
make().process_group(0).spawn()
}
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const DETACHED_PROCESS: u32 = 0x0000_0008;
const CREATE_NEW_PROCESS_GROUP: u32 = 0x0000_0200;
const CREATE_BREAKAWAY_FROM_JOB: u32 = 0x0100_0000;
let flags = DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
make()
.creation_flags(flags | CREATE_BREAKAWAY_FROM_JOB)
.spawn()
.or_else(|_| make().creation_flags(flags).spawn())
}
#[cfg(not(any(unix, windows)))]
{
make().spawn()
}
}
#[cfg(test)]
mod tests {
#[cfg(unix)]
#[test]
fn a_detached_process_is_out_of_reach_of_the_terminals_ctrl_c() {
let mut child = super::spawn(|| {
let mut c = std::process::Command::new("sleep");
c.arg("5");
c
})
.unwrap();
let pgid_of = |pid: u32| {
let out = std::process::Command::new("ps")
.args(["-o", "pgid=", "-p", &pid.to_string()])
.output()
.unwrap();
String::from_utf8_lossy(&out.stdout).trim().to_string()
};
let child_group = pgid_of(child.id());
let _ = child.kill();
let _ = child.wait();
assert_eq!(child_group, child.id().to_string(), "leads its own group");
assert_ne!(child_group, pgid_of(std::process::id()));
}
}