aft/bash_background/
process.rs1use std::process::Child;
8#[cfg(windows)]
9use std::process::{Command, Stdio};
10#[cfg(unix)]
11use std::thread;
12use std::time::Duration;
13#[cfg(unix)]
14use std::time::Instant;
15
16pub const TERMINATE_GRACE: Duration = Duration::from_secs(2);
17
18#[cfg(unix)]
19pub fn terminate_process(child: &mut Child) {
20 let pgid = child.id() as i32;
21 terminate_pgid(pgid, Some(child));
22}
23
24#[cfg(unix)]
25pub fn terminate_pgid(pgid: i32, mut child: Option<&mut Child>) {
26 unsafe {
27 libc::killpg(pgid, libc::SIGTERM);
28 }
29 let grace_started = Instant::now();
30 while grace_started.elapsed() < TERMINATE_GRACE {
31 if let Some(child) = child.as_deref_mut() {
32 if matches!(child.try_wait(), Ok(Some(_))) {
33 return;
34 }
35 }
36 thread::sleep(Duration::from_millis(50));
37 }
38 unsafe {
39 libc::killpg(pgid, libc::SIGKILL);
40 }
41}
42
43#[cfg(windows)]
44pub fn terminate_process(child: &mut Child) {
45 terminate_pid(child.id());
46}
47
48#[cfg(windows)]
49pub fn terminate_pid(pid: u32) {
50 let pid = pid.to_string();
51 let _ = Command::new("taskkill")
52 .args(["/PID", &pid, "/T", "/F"])
53 .stdout(Stdio::null())
54 .stderr(Stdio::null())
55 .status();
56}
57
58#[cfg(unix)]
59pub fn is_process_alive(pid: u32) -> bool {
60 let Ok(pid) = i32::try_from(pid) else {
61 return false;
62 };
63 if pid <= 0 {
64 return false;
65 }
66 (unsafe { libc::kill(pid, 0) == 0 })
67 || std::io::Error::last_os_error().raw_os_error() == Some(libc::EPERM)
68}
69
70#[cfg(windows)]
71pub fn is_process_alive(pid: u32) -> bool {
72 use std::ffi::c_void;
73
74 type Handle = *mut c_void;
75
76 extern "system" {
77 fn OpenProcess(dwDesiredAccess: u32, bInheritHandle: i32, dwProcessId: u32) -> Handle;
78 fn GetExitCodeProcess(hProcess: Handle, lpExitCode: *mut u32) -> i32;
79 fn CloseHandle(hObject: Handle) -> i32;
80 }
81
82 const FALSE: i32 = 0;
83 const PROCESS_QUERY_LIMITED_INFORMATION: u32 = 0x1000;
84 const STILL_ACTIVE: u32 = 0x103;
85
86 if pid == 0 {
87 return false;
88 }
89
90 unsafe {
91 let handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
92 if handle.is_null() {
93 return false;
94 }
95 let mut exit_code = 0;
96 let ok = GetExitCodeProcess(handle, &mut exit_code) != 0 && exit_code == STILL_ACTIVE;
97 let _ = CloseHandle(handle);
98 ok
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn is_process_alive_returns_true_for_self() {
108 assert!(is_process_alive(std::process::id()));
109 }
110
111 #[test]
112 fn is_process_alive_returns_false_for_dead_pid() {
113 #[cfg(unix)]
114 let mut child = std::process::Command::new("/bin/sh")
115 .args(["-c", "true"])
116 .spawn()
117 .expect("spawn true");
118
119 #[cfg(windows)]
120 let mut child = std::process::Command::new("cmd.exe")
121 .args(["/D", "/C", "exit 0"])
122 .spawn()
123 .expect("spawn cmd");
124
125 let pid = child.id();
126 child.wait().expect("wait for child");
127
128 assert!(!is_process_alive(pid));
129 }
130}