1use std::time::Duration;
2
3use tokio::process::{Child, Command};
4use tokio::time::sleep;
5
6const TERMINATE_GRACE: Duration = Duration::from_millis(500);
7const EXIT_POLL_INTERVAL: Duration = Duration::from_millis(25);
8
9pub fn isolate_process_group(command: &mut Command) {
10 #[cfg(unix)]
11 {
12 command.process_group(0);
13 }
14}
15
16pub async fn terminate_child_tree(child: &mut Child) {
17 #[cfg(unix)]
18 {
19 if let Some(pid) = child.id() {
20 let pgid = pid as libc::pid_t;
21 let term_sent = unsafe { libc::killpg(pgid, libc::SIGTERM) == 0 };
22 if term_sent && wait_for_exit(child, TERMINATE_GRACE).await {
23 return;
24 }
25
26 let kill_sent = unsafe { libc::killpg(pgid, libc::SIGKILL) == 0 };
27 if kill_sent {
28 let _ = child.wait().await;
29 return;
30 }
31 }
32 }
33
34 let _ = child.kill().await;
35 let _ = child.wait().await;
36}
37
38async fn wait_for_exit(child: &mut Child, grace: Duration) -> bool {
39 let deadline = sleep(grace);
40 tokio::pin!(deadline);
41
42 loop {
43 match child.try_wait() {
44 Ok(Some(_)) => return true,
45 Ok(None) => {}
46 Err(_) => return true,
47 }
48
49 tokio::select! {
50 _ = &mut deadline => return false,
51 _ = sleep(EXIT_POLL_INTERVAL) => {}
52 }
53 }
54}