use std::sync::Mutex; lazy_static! { static ref CLEANUP: Mutex>> = Mutex::new(None); } #[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)] pub trait ConvertToLibc { fn convert_to_libc(self) -> c_int; } #[cfg(unix)] 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), } } } pub fn new(signal_name: &str) -> Signal { match signal_name { "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: {}", signal_name), } } #[cfg(unix)] pub fn install_handler(handler: F) where F: Fn(self::Signal) + 'static + Send + Sync { use std::thread; use libc::c_int; 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(); mask.add(SIGKILL); mask.add(SIGTERM); mask.add(SIGINT); mask.add(SIGHUP); mask.add(SIGSTOP); mask.add(SIGCONT); mask.add(SIGCHLD); mask.add(SIGUSR1); mask.add(SIGUSR2); mask.thread_set_mask().expect("unable to set signal mask"); set_handler(handler); // Indicate interest in SIGCHLD by setting a dummy handler pub extern "C" fn sigchld_handler(_: c_int) {} unsafe { let _ = sigaction(SIGCHLD, &SigAction::new(SigHandler::Handler(sigchld_handler), SaFlags::empty(), SigSet::empty())); } // Spawn a thread to catch these signals thread::spawn(move || { loop { let signal = mask.wait().expect("Unable to sigwait"); debug!("Received {:?}", signal); // Invoke closure invoke(signal); // Restore default behavior for received signal and unmask it if signal != SIGCHLD { let default_action = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty()); unsafe { let _ = sigaction(signal, &default_action); } } let mut new_mask = SigSet::empty(); new_mask.add(signal); // Re-raise with signal unmasked let _ = new_mask.thread_unblock(); let _ = raise(signal); let _ = new_mask.thread_block(); } }); } #[cfg(windows)] pub fn install_handler(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 { invoke(self::Signal::SIGTERM); 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(handler: F) where F: Fn(self::Signal) + 'static + Send + Sync { *CLEANUP.lock().unwrap() = Some(Box::new(handler)); }