Skip to main content

lean_ctx/ipc/
process.rs

1use anyhow::Result;
2
3/// Check whether a process with the given PID is still running.
4pub fn is_alive(pid: u32) -> bool {
5    #[cfg(unix)]
6    {
7        unsafe { libc::kill(pid as libc::pid_t, 0) == 0 }
8    }
9    #[cfg(windows)]
10    {
11        use windows_sys::Win32::Foundation::{CloseHandle, STILL_ACTIVE, WAIT_TIMEOUT};
12        use windows_sys::Win32::System::Threading::{
13            GetExitCodeProcess, OpenProcess, WaitForSingleObject, PROCESS_QUERY_LIMITED_INFORMATION,
14        };
15
16        unsafe {
17            let handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid);
18            if handle.is_null() {
19                return false;
20            }
21            let wait = WaitForSingleObject(handle, 0);
22            if wait == WAIT_TIMEOUT {
23                CloseHandle(handle);
24                return true;
25            }
26            let mut exit_code: u32 = 0;
27            GetExitCodeProcess(handle, &mut exit_code);
28            CloseHandle(handle);
29            exit_code == STILL_ACTIVE as u32
30        }
31    }
32}
33
34/// Ask a process to terminate gracefully (SIGTERM on Unix, nothing on Windows
35/// since we prefer HTTP shutdown; the caller should have already tried that).
36pub fn terminate_gracefully(pid: u32) -> Result<()> {
37    #[cfg(unix)]
38    {
39        let ret = unsafe { libc::kill(pid as libc::pid_t, libc::SIGTERM) };
40        if ret != 0 {
41            anyhow::bail!(
42                "Failed to send SIGTERM to PID {pid}: {}",
43                std::io::Error::last_os_error()
44            );
45        }
46        Ok(())
47    }
48    #[cfg(windows)]
49    {
50        force_kill(pid)
51    }
52}
53
54/// Unconditionally kill a process.
55pub fn force_kill(pid: u32) -> Result<()> {
56    #[cfg(unix)]
57    {
58        let ret = unsafe { libc::kill(pid as libc::pid_t, libc::SIGKILL) };
59        if ret != 0 {
60            anyhow::bail!(
61                "Failed to send SIGKILL to PID {pid}: {}",
62                std::io::Error::last_os_error()
63            );
64        }
65        Ok(())
66    }
67    #[cfg(windows)]
68    {
69        use windows_sys::Win32::Foundation::CloseHandle;
70        use windows_sys::Win32::System::Threading::{
71            OpenProcess, TerminateProcess, PROCESS_TERMINATE,
72        };
73
74        unsafe {
75            let handle = OpenProcess(PROCESS_TERMINATE, 0, pid);
76            if handle.is_null() {
77                anyhow::bail!(
78                    "Failed to open PID {pid} for termination: {}",
79                    std::io::Error::last_os_error()
80                );
81            }
82            let ok = TerminateProcess(handle, 1);
83            CloseHandle(handle);
84            if ok == 0 {
85                anyhow::bail!(
86                    "Failed to terminate PID {pid}: {}",
87                    std::io::Error::last_os_error()
88                );
89            }
90            Ok(())
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn current_process_is_alive() {
101        assert!(is_alive(std::process::id()));
102    }
103
104    #[test]
105    fn bogus_pid_is_not_alive() {
106        assert!(!is_alive(u32::MAX - 42));
107    }
108}