cli/lib/utils/
tree_kill.rs1use 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}