use anyhow::Result;
pub fn is_alive(pid: u32) -> bool {
#[cfg(unix)]
{
unsafe { libc::kill(pid as libc::pid_t, 0) == 0 }
}
#[cfg(windows)]
{
use windows_sys::Win32::Foundation::{CloseHandle, STILL_ACTIVE, WAIT_TIMEOUT};
use windows_sys::Win32::System::Threading::{
GetExitCodeProcess, OpenProcess, WaitForSingleObject, PROCESS_QUERY_LIMITED_INFORMATION,
};
unsafe {
let handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid);
if handle.is_null() {
return false;
}
let wait = WaitForSingleObject(handle, 0);
if wait == WAIT_TIMEOUT {
CloseHandle(handle);
return true;
}
let mut exit_code: u32 = 0;
GetExitCodeProcess(handle, &mut exit_code);
CloseHandle(handle);
exit_code == STILL_ACTIVE as u32
}
}
}
pub fn terminate_gracefully(pid: u32) -> Result<()> {
#[cfg(unix)]
{
let ret = unsafe { libc::kill(pid as libc::pid_t, libc::SIGTERM) };
if ret != 0 {
anyhow::bail!(
"Failed to send SIGTERM to PID {pid}: {}",
std::io::Error::last_os_error()
);
}
Ok(())
}
#[cfg(windows)]
{
force_kill(pid)
}
}
pub fn force_kill(pid: u32) -> Result<()> {
#[cfg(unix)]
{
let ret = unsafe { libc::kill(pid as libc::pid_t, libc::SIGKILL) };
if ret != 0 {
anyhow::bail!(
"Failed to send SIGKILL to PID {pid}: {}",
std::io::Error::last_os_error()
);
}
Ok(())
}
#[cfg(windows)]
{
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::System::Threading::{
OpenProcess, TerminateProcess, PROCESS_TERMINATE,
};
unsafe {
let handle = OpenProcess(PROCESS_TERMINATE, 0, pid);
if handle.is_null() {
anyhow::bail!(
"Failed to open PID {pid} for termination: {}",
std::io::Error::last_os_error()
);
}
let ok = TerminateProcess(handle, 1);
CloseHandle(handle);
if ok == 0 {
anyhow::bail!(
"Failed to terminate PID {pid}: {}",
std::io::Error::last_os_error()
);
}
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn current_process_is_alive() {
assert!(is_alive(std::process::id()));
}
#[test]
fn bogus_pid_is_not_alive() {
assert!(!is_alive(u32::MAX - 42));
}
}