use std::time::Duration;
use tokio::process::Child;
pub async fn graceful_kill(child: &mut Child) -> anyhow::Result<()> {
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
if let Some(id) = child.id() {
if let Ok(Some(_)) = child.try_wait() {
return Ok(());
}
let sigterm_sent = signal_process_group(id, libc::SIGTERM);
if !sigterm_sent {
return Ok(());
}
let timeout = Duration::from_secs(5);
match tokio::time::timeout(timeout, child.wait()).await {
Ok(Ok(status)) => {
let _ = status.signal();
return Ok(());
}
Ok(Err(e)) => {
return Err(anyhow::anyhow!("error waiting after SIGTERM: {e}"));
}
Err(_) => {
signal_process_group(id, libc::SIGKILL);
child.kill().await?;
}
}
} else {
child.kill().await.ok();
}
}
#[cfg(not(unix))]
{
child.kill().await?;
}
Ok(())
}
#[cfg(unix)]
fn signal_process_group(pid: u32, signal: libc::c_int) -> bool {
let ret = unsafe { libc::kill(-(pid as libc::pid_t), signal) };
if ret == -1 {
let err = std::io::Error::last_os_error();
if err.raw_os_error() == Some(libc::ESRCH) {
return true; }
return try_pidfd_signal(pid, signal).unwrap_or_else(|| {
let ret = unsafe { libc::kill(pid as libc::pid_t, signal) };
if ret == -1 {
let err = std::io::Error::last_os_error();
return err.raw_os_error() == Some(libc::ESRCH);
}
true
});
}
true
}
#[cfg(target_os = "linux")]
fn try_pidfd_signal(pid: u32, signal: libc::c_int) -> Option<bool> {
let pidfd =
unsafe { libc::syscall(libc::SYS_pidfd_open, pid as libc::c_int, 0 as libc::c_uint) };
if pidfd < 0 {
let err = std::io::Error::last_os_error();
if err.raw_os_error() == Some(libc::ESRCH) {
return Some(false); }
return None;
}
let ret = unsafe {
libc::syscall(
libc::SYS_pidfd_send_signal,
pidfd as libc::c_int,
signal,
std::ptr::null::<libc::siginfo_t>(),
0 as libc::c_uint,
)
};
unsafe { libc::close(pidfd as libc::c_int) };
if ret < 0 {
let err = std::io::Error::last_os_error();
if err.raw_os_error() == Some(libc::ESRCH) {
return Some(false);
}
return None;
}
Some(true)
}
#[cfg(all(unix, not(target_os = "linux")))]
fn try_pidfd_signal(_pid: u32, _signal: libc::c_int) -> Option<bool> {
None
}