1mod error;
23pub mod signal;
24
25use std::io;
26use std::process::Child;
27
28pub use error::InvalidSignal;
29use signal::Signal;
30
31extern "C" {
32 fn kill(pid: i32, sig: Signal) -> i32;
33}
34
35pub fn signal(pid: i32, signal: Signal) -> io::Result<()> {
37 let ret = unsafe { kill(pid, signal) };
38 if ret == 0 {
39 Ok(())
40 } else {
41 Err(io::Error::last_os_error())
42 }
43}
44
45pub trait Signalable {
62 fn signal(&mut self, signal: Signal) -> io::Result<()>;
64
65 fn term(&mut self) -> io::Result<()> {
67 self.signal(Signal::SIGTERM)
68 }
69
70 fn interrupt(&mut self) -> io::Result<()> {
72 self.signal(Signal::SIGINT)
73 }
74
75 fn hangup(&mut self) -> io::Result<()> {
77 self.signal(Signal::SIGHUP)
78 }
79}
80
81impl Signalable for Child {
82 fn signal(&mut self, signal: Signal) -> io::Result<()> {
83 if self.try_wait()?.is_some() {
84 Err(io::Error::new(
85 io::ErrorKind::InvalidInput,
86 "invalid argument: can't signal an exited process",
87 ))
88 } else {
89 crate::signal(self.id() as i32, signal)
90 }
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use std::{
97 process::Command,
98 time::{Duration, Instant},
99 };
100
101 use crate::error::InvalidSignal;
102
103 use super::*;
104
105 #[test]
106 fn test_from_str_round_trips() {
107 for signal in Signal::iterator() {
108 assert_eq!(signal.as_ref().parse::<Signal>().unwrap(), signal);
109 assert_eq!(signal.to_string().parse::<Signal>().unwrap(), signal);
110 }
111 }
112
113 #[test]
114 fn test_from_str_invalid_value() {
115 let err_val = Err(InvalidSignal);
116 assert_eq!("NOSIGNAL".parse::<Signal>(), err_val);
117 assert_eq!("kill".parse::<Signal>(), err_val);
118 assert_eq!("9".parse::<Signal>(), err_val);
119 }
120
121 #[test]
122 fn test_termite_child() {
123 let mut child = Command::new("sleep")
124 .arg("1000")
125 .spawn()
126 .expect("Error spawning sleep process");
127 if let Ok(Some(_)) | Err(_) = child.try_wait() {
128 panic!("Child exited too early");
129 }
130 assert!(child.interrupt().is_ok());
131 let to_wait = Duration::from_millis(500);
132 let start = Instant::now();
133 while child.try_wait().unwrap().is_none() {
134 std::thread::sleep(Duration::from_millis(10));
135 if start.elapsed() > to_wait {
136 panic!("Sleep process didn't exit");
137 }
138 }
139 assert!(!child.try_wait().unwrap().unwrap().success());
140 }
141}