1extern crate libc;
17#[macro_use]
18extern crate lazy_static;
19
20use std::io;
21use std::sync::{Mutex, mpsc};
22use std::collections::HashMap;
23
24use libc::{SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGKILL, SIGSEGV, SIGPIPE, SIGALRM,
25 SIGTERM, SIGUSR1, SIGUSR2, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU,
26 SIGBUS, SIGPROF, SIGSYS, SIGTRAP, SIGURG, SIGVTALRM, SIGXCPU, SIGXFSZ, SIGIO, SIGWINCH};
27
28pub fn notify(signal: &[Signal]) -> mpsc::Receiver<Signal> {
29 let (tx, rx) = mpsc::channel();
30 notify_on(tx, signal);
31 rx
32}
33
34pub fn notify_on(tx: mpsc::Sender<Signal>, signal: &[Signal]) {
35 unsafe {
36 let mut sa: libc::sigaction = ::std::mem::zeroed();
37 let f: extern "C" fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) =
38 signal_handler;
39 sa.sa_sigaction = ::std::mem::transmute(f);
40 sa.sa_flags |= libc::SA_SIGINFO;
41 for sig in signal {
42 ok_or_errno(
43 (),
44 libc::sigaction(
45 sig.as_sig(),
46 &sa as *const libc::sigaction,
47 ::std::ptr::null_mut(),
48 ),
49 ).unwrap();
50 }
51 let mut notifiers = NOTIFIER.lock().unwrap();
52 for sig in signal {
53 notifiers.entry(*sig).or_insert(Vec::new()).push(tx.clone());
54 }
55 }
56}
57
58extern "C" fn signal_handler(
59 signal: libc::c_int,
60 _siginfo: *const libc::siginfo_t,
61 _ctx: *const libc::c_void,
62) {
63 let n = signal as i32;
64 unsafe {
65 libc::write(
66 PIPE[1],
67 &n as *const _ as *const _,
68 ::std::mem::size_of::<i32>(),
69 );
70 }
71}
72
73static mut PIPE: [libc::c_int; 2] = [0, 0];
74
75lazy_static! {
76 static ref NOTIFIER: Mutex<HashMap<Signal, Vec<mpsc::Sender<Signal>>>> = {
77 unsafe {
78 ok_or_errno((), libc::pipe(PIPE.as_mut_ptr())).unwrap();
79 }
80 start();
81 Mutex::new(HashMap::new())
82 };
83}
84
85type Sig = libc::c_int;
86
87#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
88pub enum Signal {
89 HUP,
90 INT,
91 QUIT,
92 ILL,
93 ABRT,
94 FPE,
95 KILL,
96 SEGV,
97 PIPE,
98 ALRM,
99 TERM,
100 USR1,
101 USR2,
102 CHLD,
103 CONT,
104 STOP,
105 TSTP,
106 TTIN,
107 TTOU,
108 BUS,
109 PROF,
110 SYS,
111 TRAP,
112 URG,
113 VTALRM,
114 XCPU,
115 XFSZ,
116 IO,
117 WINCH,
118}
119
120impl Signal {
121 fn new(sig: Sig) -> Signal {
122 match sig {
123 SIGHUP => Signal::HUP,
124 SIGINT => Signal::INT,
125 SIGQUIT => Signal::QUIT,
126 SIGILL => Signal::ILL,
127 SIGABRT => Signal::ABRT,
128 SIGFPE => Signal::FPE,
129 SIGKILL => Signal::KILL,
130 SIGSEGV => Signal::SEGV,
131 SIGPIPE => Signal::PIPE,
132 SIGALRM => Signal::ALRM,
133 SIGTERM => Signal::TERM,
134 SIGUSR1 => Signal::USR1,
135 SIGUSR2 => Signal::USR2,
136 SIGCHLD => Signal::CHLD,
137 SIGCONT => Signal::CONT,
138 SIGSTOP => Signal::STOP,
139 SIGTSTP => Signal::TSTP,
140 SIGTTIN => Signal::TTIN,
141 SIGTTOU => Signal::TTOU,
142 SIGBUS => Signal::BUS,
143 SIGPROF => Signal::PROF,
144 SIGSYS => Signal::SYS,
145 SIGTRAP => Signal::TRAP,
146 SIGURG => Signal::URG,
147 SIGVTALRM => Signal::VTALRM,
148 SIGXCPU => Signal::XCPU,
149 SIGXFSZ => Signal::XFSZ,
150 SIGIO => Signal::IO,
151 SIGWINCH => Signal::WINCH,
152 sig => panic!("unsupported signal number: {}", sig),
153 }
154 }
155
156 fn as_sig(self) -> Sig {
157 match self {
158 Signal::HUP => SIGHUP,
159 Signal::INT => SIGINT,
160 Signal::QUIT => SIGQUIT,
161 Signal::ILL => SIGILL,
162 Signal::ABRT => SIGABRT,
163 Signal::FPE => SIGFPE,
164 Signal::KILL => SIGKILL,
165 Signal::SEGV => SIGSEGV,
166 Signal::PIPE => SIGPIPE,
167 Signal::ALRM => SIGALRM,
168 Signal::TERM => SIGTERM,
169 Signal::USR1 => SIGUSR1,
170 Signal::USR2 => SIGUSR2,
171 Signal::CHLD => SIGCHLD,
172 Signal::CONT => SIGCONT,
173 Signal::STOP => SIGSTOP,
174 Signal::TSTP => SIGTSTP,
175 Signal::TTIN => SIGTTIN,
176 Signal::TTOU => SIGTTOU,
177 Signal::BUS => SIGBUS,
178 Signal::PROF => SIGPROF,
179 Signal::SYS => SIGSYS,
180 Signal::TRAP => SIGTRAP,
181 Signal::URG => SIGURG,
182 Signal::VTALRM => SIGVTALRM,
183 Signal::XCPU => SIGXCPU,
184 Signal::XFSZ => SIGXFSZ,
185 Signal::IO => SIGIO,
186 Signal::WINCH => SIGWINCH,
187 }
188 }
189}
190
191fn start() {
192 ::std::thread::spawn(|| loop {
193 let signal = match read_signal() {
194 None => break,
195 Some(signal) => signal,
196 };
197 let notifier = NOTIFIER.lock().unwrap();
198 if let Some(senders) = notifier.get(&signal) {
199 for tx in senders {
200 let _ = tx.send(signal);
201 }
202 }
203 });
204}
205
206fn read_signal() -> Option<Signal> {
207 let mut buf: [u8; 4] = [0; 4];
208 unsafe {
209 loop {
210 let n = libc::read(PIPE[0], buf.as_mut_ptr() as *mut _, 4);
211 if n == 0 {
212 return None;
213 } else if n == -1 {
214 let err = io::Error::last_os_error();
215 match err.kind() {
216 io::ErrorKind::WouldBlock |
217 io::ErrorKind::Interrupted => continue,
218 _ => panic!("read error in signal_notify: {}", err),
219 }
220 } else {
221 return Some(Signal::new(std::mem::transmute(buf)));
222 }
223 }
224 }
225}
226
227fn ok_or_errno<T>(ok: T, errcode: libc::c_int) -> io::Result<T> {
228 if errcode != 0 {
229 Err(io::Error::from_raw_os_error(errcode))
230 } else {
231 Ok(ok)
232 }
233}