Skip to main content

aft/bash_background/
process.rs

1/// Shared process-termination helpers for both foreground bash and background
2/// bash tasks. Extracted to avoid duplication between `commands/bash.rs` and
3/// `bash_background/registry.rs`.
4///
5/// Termination is graceful-first: SIGTERM + 3-second grace period, then
6/// SIGKILL on Unix. On Windows, `taskkill /T /F` kills the entire process tree.
7use std::process::Child;
8#[cfg(windows)]
9use std::process::{Command, Stdio};
10use std::thread;
11use std::time::{Duration, Instant};
12
13pub const TERMINATE_GRACE: Duration = Duration::from_secs(2);
14
15#[cfg(unix)]
16pub fn terminate_process(child: &mut Child) {
17    let pgid = child.id() as i32;
18    terminate_pgid(pgid, Some(child));
19}
20
21#[cfg(unix)]
22pub fn terminate_pgid(pgid: i32, mut child: Option<&mut Child>) {
23    unsafe {
24        libc::killpg(pgid, libc::SIGTERM);
25    }
26    let grace_started = Instant::now();
27    while grace_started.elapsed() < TERMINATE_GRACE {
28        if let Some(child) = child.as_deref_mut() {
29            if matches!(child.try_wait(), Ok(Some(_))) {
30                return;
31            }
32        }
33        thread::sleep(Duration::from_millis(50));
34    }
35    unsafe {
36        libc::killpg(pgid, libc::SIGKILL);
37    }
38}
39
40#[cfg(windows)]
41pub fn terminate_process(child: &mut Child) {
42    let pid = child.id().to_string();
43    let _ = Command::new("taskkill")
44        .args(["/PID", &pid, "/T", "/F"])
45        .stdout(Stdio::null())
46        .stderr(Stdio::null())
47        .status();
48}