2016-12-15 02:19:58 +01:00
|
|
|
use std::sync::Mutex;
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
static ref CLEANUP: Mutex<Option<Box<Fn(self::Signal) + Send>>> = Mutex::new(None);
|
|
|
|
}
|
|
|
|
|
2017-03-14 17:30:19 +01:00
|
|
|
#[cfg(unix)]
|
|
|
|
pub use nix::sys::signal::Signal;
|
|
|
|
|
|
|
|
// This is a dummy enum for Windows
|
|
|
|
#[cfg(windows)]
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
pub enum Signal {
|
|
|
|
SIGKILL,
|
|
|
|
SIGTERM,
|
|
|
|
SIGINT,
|
|
|
|
SIGHUP,
|
|
|
|
SIGSTOP,
|
|
|
|
SIGCONT,
|
|
|
|
SIGCHLD,
|
|
|
|
SIGUSR1,
|
|
|
|
SIGUSR2,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
use libc::*;
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
2017-03-14 17:18:43 +01:00
|
|
|
pub trait ConvertToLibc {
|
|
|
|
fn convert_to_libc(self) -> c_int;
|
|
|
|
}
|
|
|
|
|
2017-03-14 17:30:19 +01:00
|
|
|
#[cfg(unix)]
|
2017-03-14 17:18:43 +01:00
|
|
|
impl ConvertToLibc for Signal {
|
|
|
|
fn convert_to_libc(self) -> c_int {
|
|
|
|
// Convert from signal::Signal enum to libc::* c_int constants
|
|
|
|
match self {
|
|
|
|
Signal::SIGKILL => SIGKILL,
|
|
|
|
Signal::SIGTERM => SIGTERM,
|
|
|
|
Signal::SIGINT => SIGINT,
|
|
|
|
Signal::SIGHUP => SIGHUP,
|
|
|
|
Signal::SIGSTOP => SIGSTOP,
|
|
|
|
Signal::SIGCONT => SIGCONT,
|
|
|
|
Signal::SIGCHLD => SIGCHLD,
|
|
|
|
Signal::SIGUSR1 => SIGUSR1,
|
|
|
|
Signal::SIGUSR2 => SIGUSR2,
|
|
|
|
_ => panic!("unsupported signal: {:?}", self),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-23 23:50:39 +01:00
|
|
|
pub fn new(signal_name: Option<String>) -> Option<Signal> {
|
|
|
|
if let Some(signame) = signal_name {
|
|
|
|
let signal = match signame.as_ref() {
|
|
|
|
"SIGKILL" | "KILL" => Signal::SIGKILL,
|
|
|
|
"SIGTERM" | "TERM" => Signal::SIGTERM,
|
|
|
|
"SIGINT" | "INT" => Signal::SIGINT,
|
|
|
|
"SIGHUP" | "HUP" => Signal::SIGHUP,
|
|
|
|
"SIGSTOP" | "STOP" => Signal::SIGSTOP,
|
|
|
|
"SIGCONT" | "CONT" => Signal::SIGCONT,
|
|
|
|
"SIGCHLD" | "CHLD" => Signal::SIGCHLD,
|
|
|
|
"SIGUSR1" | "USR1" => Signal::SIGUSR1,
|
|
|
|
"SIGUSR2" | "USR2" => Signal::SIGUSR2,
|
|
|
|
_ => panic!("unsupported signal: {}", signame),
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(signal)
|
|
|
|
} else {
|
|
|
|
None
|
2017-03-14 00:52:50 +01:00
|
|
|
}
|
2016-12-15 02:19:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
pub fn install_handler<F>(handler: F)
|
|
|
|
where F: Fn(self::Signal) + 'static + Send + Sync
|
|
|
|
{
|
|
|
|
use std::thread;
|
2016-12-20 17:44:18 +01:00
|
|
|
use libc::c_int;
|
2016-12-15 02:19:58 +01:00
|
|
|
use nix::sys::signal::*;
|
|
|
|
|
|
|
|
// Mask all signals interesting to us. The mask propagates
|
|
|
|
// to all threads started after this point.
|
|
|
|
let mut mask = SigSet::empty();
|
2017-03-14 00:52:14 +01:00
|
|
|
mask.add(SIGKILL);
|
2016-12-15 02:19:58 +01:00
|
|
|
mask.add(SIGTERM);
|
|
|
|
mask.add(SIGINT);
|
2017-03-14 00:52:14 +01:00
|
|
|
mask.add(SIGHUP);
|
|
|
|
mask.add(SIGSTOP);
|
2016-12-15 02:19:58 +01:00
|
|
|
mask.add(SIGCONT);
|
2016-12-20 17:44:18 +01:00
|
|
|
mask.add(SIGCHLD);
|
2017-03-14 00:52:14 +01:00
|
|
|
mask.add(SIGUSR1);
|
|
|
|
mask.add(SIGUSR2);
|
2017-03-23 23:39:38 +01:00
|
|
|
mask.thread_set_mask()
|
|
|
|
.expect("unable to set signal mask");
|
2016-12-15 02:19:58 +01:00
|
|
|
|
|
|
|
set_handler(handler);
|
|
|
|
|
2016-12-20 17:44:18 +01:00
|
|
|
// Indicate interest in SIGCHLD by setting a dummy handler
|
2016-12-20 18:20:21 +01:00
|
|
|
pub extern "C" fn sigchld_handler(_: c_int) {}
|
2016-12-20 17:44:18 +01:00
|
|
|
|
|
|
|
unsafe {
|
2016-12-20 18:20:21 +01:00
|
|
|
let _ = sigaction(SIGCHLD,
|
|
|
|
&SigAction::new(SigHandler::Handler(sigchld_handler),
|
|
|
|
SaFlags::empty(),
|
|
|
|
SigSet::empty()));
|
2016-12-20 17:44:18 +01:00
|
|
|
}
|
|
|
|
|
2016-12-15 02:19:58 +01:00
|
|
|
// Spawn a thread to catch these signals
|
|
|
|
thread::spawn(move || {
|
|
|
|
loop {
|
2017-03-13 19:00:45 +01:00
|
|
|
let signal = mask.wait().expect("Unable to sigwait");
|
|
|
|
debug!("Received {:?}", signal);
|
2016-12-15 02:19:58 +01:00
|
|
|
|
|
|
|
// Invoke closure
|
2017-03-13 19:00:45 +01:00
|
|
|
invoke(signal);
|
2016-12-15 02:19:58 +01:00
|
|
|
|
|
|
|
// Restore default behavior for received signal and unmask it
|
2017-03-13 19:00:45 +01:00
|
|
|
if signal != SIGCHLD {
|
2016-12-20 18:20:21 +01:00
|
|
|
let default_action =
|
|
|
|
SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty());
|
2016-12-15 02:19:58 +01:00
|
|
|
|
2016-12-20 17:44:18 +01:00
|
|
|
unsafe {
|
2017-03-13 19:00:45 +01:00
|
|
|
let _ = sigaction(signal, &default_action);
|
2016-12-20 17:44:18 +01:00
|
|
|
}
|
2016-12-15 02:19:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut new_mask = SigSet::empty();
|
2017-03-13 19:00:45 +01:00
|
|
|
new_mask.add(signal);
|
2016-12-15 02:19:58 +01:00
|
|
|
|
2016-12-15 15:41:10 +01:00
|
|
|
// Re-raise with signal unmasked
|
|
|
|
let _ = new_mask.thread_unblock();
|
2017-03-13 19:00:45 +01:00
|
|
|
let _ = raise(signal);
|
2016-12-15 15:41:10 +01:00
|
|
|
let _ = new_mask.thread_block();
|
2016-12-15 02:19:58 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
pub fn install_handler<F>(handler: F)
|
|
|
|
where F: Fn(self::Signal) + 'static + Send + Sync
|
|
|
|
{
|
|
|
|
use kernel32::SetConsoleCtrlHandler;
|
|
|
|
use winapi::{BOOL, DWORD, FALSE, TRUE};
|
|
|
|
|
|
|
|
pub unsafe extern "system" fn ctrl_handler(_: DWORD) -> BOOL {
|
2017-03-13 19:00:45 +01:00
|
|
|
invoke(self::Signal::SIGTERM);
|
2016-12-15 02:19:58 +01:00
|
|
|
|
|
|
|
FALSE
|
|
|
|
}
|
|
|
|
|
|
|
|
set_handler(handler);
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
SetConsoleCtrlHandler(Some(ctrl_handler), TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn invoke(sig: self::Signal) {
|
|
|
|
if let Some(ref handler) = *CLEANUP.lock().unwrap() {
|
|
|
|
handler(sig)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_handler<F>(handler: F)
|
|
|
|
where F: Fn(self::Signal) + 'static + Send + Sync
|
|
|
|
{
|
|
|
|
*CLEANUP.lock().unwrap() = Some(Box::new(handler));
|
|
|
|
}
|