1use std::io;
2use std::process::Command as CommandSys;
3
4pub fn kill_by_pid(pid: i64) -> Result<(), KillByPidError> {
6 let mut cmd = build_kill_command(true, std::iter::once(pid), None);
7
8 let output = cmd.output().map_err(KillByPidError::Output)?;
9
10 match output.status.success() {
11 true => Ok(()),
12 false => Err(KillByPidError::KillProcess),
13 }
14}
15
16pub enum KillByPidError {
18 Output(io::Error),
20
21 KillProcess,
23}
24
25pub fn build_kill_command(
28 force: bool,
29 pids: impl Iterator<Item = i64>,
30 signal: Option<u32>,
31) -> CommandSys {
32 if cfg!(windows) {
33 let mut cmd = CommandSys::new("taskkill");
34
35 if force {
36 cmd.arg("/F");
37 }
38
39 for id in pids {
42 cmd.arg("/PID");
43 cmd.arg(id.to_string());
44 }
45
46 cmd
47 } else {
48 let mut cmd = CommandSys::new("kill");
49 if let Some(signal_value) = signal {
50 cmd.arg(format!("-{signal_value}"));
51 } else if force {
52 cmd.arg("-9");
53 }
54
55 cmd.arg("--");
56 cmd.args(pids.map(|id| id.to_string()));
57
58 cmd
59 }
60}
61
62#[cfg(all(test, unix))]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn build_kill_command_separates_options_from_negative_pids() {
68 let cmd = build_kill_command(false, std::iter::once(-9), None);
69 let args = cmd
70 .get_args()
71 .map(|arg| arg.to_string_lossy().into_owned())
72 .collect::<Vec<_>>();
73
74 assert_eq!(args, ["--", "-9"]);
75 }
76
77 #[test]
78 fn build_kill_command_keeps_signal_and_pid_separator() {
79 let cmd = build_kill_command(false, std::iter::once(123), Some(9));
80 let args = cmd
81 .get_args()
82 .map(|arg| arg.to_string_lossy().into_owned())
83 .collect::<Vec<_>>();
84
85 assert_eq!(args, ["-9", "--", "123"]);
86 }
87}