use std::process::ExitStatus;
pub(crate) async fn spawn_and_wait(
mut cmd: tokio::process::Command,
) -> std::io::Result<ExitStatus> {
set_parent_death_signal(&mut cmd);
#[cfg(unix)]
{
use tokio::signal::unix::{SignalKind, signal};
let mut sigterm = signal(SignalKind::terminate())?;
let mut sigint = signal(SignalKind::interrupt())?;
let mut sighup = signal(SignalKind::hangup())?;
let mut sigquit = signal(SignalKind::quit())?;
let mut child = cmd.spawn()?;
let pid = child.id();
loop {
tokio::select! {
status = child.wait() => return status,
_ = sigterm.recv() => forward_signal(pid, libc::SIGTERM),
_ = sigint.recv() => forward_signal(pid, libc::SIGINT),
_ = sighup.recv() => forward_signal(pid, libc::SIGHUP),
_ = sigquit.recv() => forward_signal(pid, libc::SIGQUIT),
}
}
}
#[cfg(not(unix))]
{
let mut child = cmd.spawn()?;
child.wait().await
}
}
fn set_parent_death_signal(cmd: &mut tokio::process::Command) {
#[cfg(target_os = "linux")]
{
let parent_pid = std::process::id() as libc::pid_t;
unsafe {
cmd.pre_exec(move || {
if libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM, 0, 0, 0) != 0 {
return Err(std::io::Error::last_os_error());
}
if libc::getppid() != parent_pid {
libc::raise(libc::SIGTERM);
}
Ok(())
});
}
}
#[cfg(not(target_os = "linux"))]
{
let _ = cmd;
}
}
#[cfg(unix)]
fn forward_signal(pid: Option<u32>, sig: libc::c_int) {
let Some(pid) = pid else {
return;
};
unsafe {
libc::kill(pid as libc::pid_t, sig);
}
}