Skip to main content

sac/
process.rs

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}