khive_runtime/
process_group.rs1#[cfg(unix)]
14pub fn signal_process_group(pgid: i32, signal: i32) -> std::io::Result<()> {
15 if pgid <= 1 {
16 return Err(std::io::Error::new(
17 std::io::ErrorKind::InvalidInput,
18 format!("refusing to signal process group {pgid}"),
19 ));
20 }
21 if unsafe { libc::kill(-pgid, signal) } == 0 {
25 Ok(())
26 } else {
27 Err(std::io::Error::last_os_error())
28 }
29}
30
31#[cfg(all(test, unix))]
32mod tests {
33 use std::os::unix::process::{CommandExt, ExitStatusExt};
34 use std::process::Command;
35
36 use super::signal_process_group;
37
38 #[test]
39 fn signal_process_group_kills_a_child_group_then_reports_esrch() {
40 let mut child = Command::new("/bin/sleep")
41 .arg("30")
42 .process_group(0)
43 .spawn()
44 .expect("spawn a child into its own process group");
45 let pgid = child.id() as i32;
46
47 signal_process_group(pgid, libc::SIGKILL).expect("signal the live group");
48 let status = child.wait().expect("reap the killed child");
49 assert_eq!(
50 status.signal(),
51 Some(libc::SIGKILL),
52 "the child must die to the group signal"
53 );
54
55 let error = signal_process_group(pgid, 0).expect_err("the group is gone");
57 assert_eq!(error.raw_os_error(), Some(libc::ESRCH));
58 }
59
60 #[test]
61 fn signal_process_group_refuses_ids_that_do_not_name_one_group() {
62 for pgid in [0, 1, -5] {
63 match signal_process_group(pgid, 0) {
65 Ok(()) => panic!("process group id {pgid} reached the OS"),
66 Err(error) => {
67 assert_eq!(
68 error.kind(),
69 std::io::ErrorKind::InvalidInput,
70 "process group id {pgid} must be refused as invalid input"
71 );
72 assert_eq!(
73 error.raw_os_error(),
74 None,
75 "process group id {pgid} must be refused without an OS call"
76 );
77 }
78 }
79 }
80 }
81}