signal_notify/
lib.rs

1//! `signal-notify` crate provides a simple way to wait for signals in *nix systems through standard
2//! `std::sync::mpsc` API.
3//!
4//! ```no_run
5//! use signal_notify::{notify, Signal};
6//!
7//! let rx = notify(&[Signal::INT, Signal::HUP]);
8//! // block unitl receiving SIGINT or SIGHUP.
9//! // recv always return Ok because the sender channel will be never closed.
10//! rx.recv().unwrap();
11//! ```
12//!
13//! `signal-notify` doesn't support Windows. I'm not familiar with Windows, so I'd be happy if you
14//! could help me about it.
15
16extern 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}