#[cfg(unix)]
pub fn is_process_alive(pid: u32) -> bool {
let rc = unsafe { libc::kill(pid as i32, 0) };
(rc == 0 || std::io::Error::last_os_error().raw_os_error() == Some(libc::EPERM))
&& !is_process_zombie(pid)
}
#[cfg(unix)]
pub fn is_process_zombie(pid: u32) -> bool {
let pid = pid.to_string();
let output = std::process::Command::new("ps")
.args(["-o", "stat=", "-p", pid.as_str()])
.output();
match output {
Ok(output) if output.status.success() => {
process_stat_indicates_zombie(&String::from_utf8_lossy(&output.stdout))
}
_ => false,
}
}
#[cfg(unix)]
fn process_stat_indicates_zombie(stat: &str) -> bool {
stat.trim_start().starts_with('Z')
}
#[cfg(windows)]
pub fn is_process_alive(pid: u32) -> bool {
use windows_sys::Win32::Foundation::{CloseHandle, STILL_ACTIVE};
use windows_sys::Win32::System::Threading::{
GetExitCodeProcess, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION,
};
let handle = unsafe { OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid) };
if handle.is_null() {
return false;
}
let mut code: u32 = 0;
let ok = unsafe { GetExitCodeProcess(handle, &mut code) };
unsafe { CloseHandle(handle) };
ok != 0 && code as i32 == STILL_ACTIVE
}
pub fn terminate_process(pid: u32) {
#[cfg(unix)]
unsafe {
libc::kill(pid as i32, libc::SIGTERM);
}
#[cfg(windows)]
{
windows_terminate(pid);
}
}
pub fn kill_process(pid: u32) {
#[cfg(unix)]
unsafe {
libc::kill(pid as i32, libc::SIGKILL);
}
#[cfg(windows)]
{
windows_terminate(pid);
}
}
#[cfg(windows)]
fn windows_terminate(pid: u32) {
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_TERMINATE, TerminateProcess};
let handle = unsafe { OpenProcess(PROCESS_TERMINATE, 0, pid) };
if handle.is_null() {
return;
}
unsafe {
TerminateProcess(handle, 1);
CloseHandle(handle);
}
}
#[cfg(unix)]
pub fn current_uid() -> u32 {
unsafe { libc::getuid() }
}
#[cfg(not(unix))]
pub fn current_uid() -> u32 {
0
}
pub async fn wait_for_shutdown() {
#[cfg(unix)]
{
use tokio::signal::unix::{SignalKind, signal};
let mut sigterm = signal(SignalKind::terminate()).expect("SIGTERM handler");
let mut sigint = signal(SignalKind::interrupt()).expect("SIGINT handler");
tokio::select! {
_ = sigterm.recv() => {}
_ = sigint.recv() => {}
}
}
#[cfg(windows)]
{
use tokio::signal::windows::{ctrl_break, ctrl_c, ctrl_close, ctrl_shutdown};
let mut cc = ctrl_c().expect("ctrl_c handler");
let mut cb = ctrl_break().expect("ctrl_break handler");
let mut cl = ctrl_close().expect("ctrl_close handler");
let mut cs = ctrl_shutdown().expect("ctrl_shutdown handler");
tokio::select! {
_ = cc.recv() => {}
_ = cb.recv() => {}
_ = cl.recv() => {}
_ = cs.recv() => {}
}
}
}
#[cfg(test)]
mod tests {
#[cfg(unix)]
#[test]
fn process_stat_zombie_detection_uses_leading_state() {
assert!(super::process_stat_indicates_zombie("Z"));
assert!(super::process_stat_indicates_zombie("Z+"));
assert!(super::process_stat_indicates_zombie(" ZN"));
assert!(!super::process_stat_indicates_zombie("S"));
assert!(!super::process_stat_indicates_zombie("Ss"));
assert!(!super::process_stat_indicates_zombie("R+"));
}
}