Skip to main content

cli/lib/utils/
tree_kill.rs

1//! Upstream source: `../nest-cli/lib/utils/tree-kill.ts`.
2
3use std::io;
4use std::process::Command;
5
6pub fn tree_kill_sync(pid: u32, _signal: Option<&str>) -> io::Result<()> {
7    #[cfg(windows)]
8    {
9        let status = Command::new("taskkill")
10            .args(["/pid", &pid.to_string(), "/T", "/F"])
11            .status()?;
12        return status.success().then_some(()).ok_or_else(|| {
13            io::Error::new(
14                io::ErrorKind::Other,
15                format!("taskkill failed for pid {pid}"),
16            )
17        });
18    }
19
20    #[cfg(not(windows))]
21    {
22        for child_pid in get_all_childs(pid)? {
23            kill_pid(child_pid, _signal)?;
24        }
25        kill_pid(pid, _signal)
26    }
27}
28
29#[cfg(not(windows))]
30fn get_all_pid() -> io::Result<Vec<(u32, u32)>> {
31    let output = Command::new("ps").args(["-A", "-o", "pid,ppid"]).output()?;
32    if !output.status.success() {
33        return Ok(Vec::new());
34    }
35
36    let stdout = String::from_utf8_lossy(&output.stdout);
37    Ok(stdout
38        .lines()
39        .skip(1)
40        .filter_map(|line| {
41            let mut parts = line.split_whitespace();
42            let pid = parts.next()?.parse::<u32>().ok()?;
43            let ppid = parts.next()?.parse::<u32>().ok()?;
44            Some((pid, ppid))
45        })
46        .collect())
47}
48
49#[cfg(not(windows))]
50fn get_all_childs(pid: u32) -> io::Result<Vec<u32>> {
51    let all_pid = get_all_pid()?;
52    let mut result = Vec::new();
53    collect_childs(pid, &all_pid, &mut result);
54    Ok(result)
55}
56
57#[cfg(not(windows))]
58fn collect_childs(pid: u32, all_pid: &[(u32, u32)], result: &mut Vec<u32>) {
59    for (child_pid, ppid) in all_pid.iter().copied().filter(|(_, ppid)| *ppid == pid) {
60        result.push(child_pid);
61        collect_childs(child_pid, all_pid, result);
62    }
63}
64
65#[cfg(not(windows))]
66fn kill_pid(pid: u32, signal: Option<&str>) -> io::Result<()> {
67    let signal = signal.unwrap_or("TERM").trim_start_matches("SIG");
68    let status = Command::new("kill")
69        .args([format!("-{signal}"), pid.to_string()])
70        .status()?;
71
72    if status.success() {
73        Ok(())
74    } else {
75        Err(io::Error::new(
76            io::ErrorKind::Other,
77            format!("kill failed for pid {pid}"),
78        ))
79    }
80}