use std::process::Command as OsCommand;
pub(crate) fn kill_tree(pid: u32) {
#[cfg(windows)]
{
use std::process::Stdio;
let _ = OsCommand::new("taskkill")
.args(["/T", "/F", "/PID", &pid.to_string()])
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
}
#[cfg(unix)]
{
unsafe {
libc::kill(-(pid as i32), libc::SIGKILL);
}
}
}
pub(crate) fn detach_process_group(cmd: &mut OsCommand) {
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
unsafe {
cmd.pre_exec(|| {
libc::setsid();
Ok(())
});
}
}
#[cfg(windows)]
{
let _ = cmd;
}
}
pub(crate) fn shell_command(command_line: &str) -> OsCommand {
#[cfg(windows)]
{
let mut c = OsCommand::new("cmd.exe");
c.arg("/c").arg(command_line);
c
}
#[cfg(unix)]
{
let mut c = OsCommand::new("/bin/sh");
c.arg("-c").arg(command_line);
c
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::process::Stdio;
#[test]
fn shell_command_runs_a_trivial_command() {
let output = shell_command("echo shell-tunnel")
.stdin(Stdio::null())
.output()
.expect("shell should be available");
assert!(output.status.success());
assert!(String::from_utf8_lossy(&output.stdout).contains("shell-tunnel"));
}
#[test]
fn kill_tree_on_a_dead_pid_is_harmless() {
let mut child = shell_command("exit 0")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.expect("spawn");
let pid = child.id();
let _ = child.wait();
kill_tree(pid);
}
}