1#![allow(unused)]
3
4use std::sync::Mutex;
5use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
6
7lazy_static! {
8 static ref CLEANUP: Mutex<Option<Box<Fn(self::Signal) + Send>>> = Mutex::new(None);
9}
10
11#[cfg(unix)]
12pub use nix::sys::signal::Signal;
13
14#[cfg(windows)]
15use winapi;
16
17#[cfg(windows)]
19#[derive(Debug, Copy, Clone)]
20pub enum Signal {
21 SIGKILL,
22 SIGTERM,
23 SIGINT,
24 SIGHUP,
25 SIGSTOP,
26 SIGCONT,
27 SIGCHLD,
28 SIGUSR1,
29 SIGUSR2,
30}
31
32#[cfg(unix)]
33use nix::libc::*;
34
35#[cfg(unix)]
36pub trait ConvertToLibc {
37 fn convert_to_libc(self) -> c_int;
38}
39
40#[cfg(unix)]
41impl ConvertToLibc for Signal {
42 fn convert_to_libc(self) -> c_int {
43 match self {
45 Signal::SIGKILL => SIGKILL,
46 Signal::SIGTERM => SIGTERM,
47 Signal::SIGINT => SIGINT,
48 Signal::SIGHUP => SIGHUP,
49 Signal::SIGSTOP => SIGSTOP,
50 Signal::SIGCONT => SIGCONT,
51 Signal::SIGCHLD => SIGCHLD,
52 Signal::SIGUSR1 => SIGUSR1,
53 Signal::SIGUSR2 => SIGUSR2,
54 _ => panic!("unsupported signal: {:?}", self),
55 }
56 }
57}
58
59pub fn new(signal_name: Option<String>) -> Option<Signal> {
60 if let Some(signame) = signal_name {
61 let signal = match signame.as_ref() {
62 "SIGKILL" | "KILL" => Signal::SIGKILL,
63 "SIGTERM" | "TERM" => Signal::SIGTERM,
64 "SIGINT" | "INT" => Signal::SIGINT,
65 "SIGHUP" | "HUP" => Signal::SIGHUP,
66 "SIGSTOP" | "STOP" => Signal::SIGSTOP,
67 "SIGCONT" | "CONT" => Signal::SIGCONT,
68 "SIGCHLD" | "CHLD" => Signal::SIGCHLD,
69 "SIGUSR1" | "USR1" => Signal::SIGUSR1,
70 "SIGUSR2" | "USR2" => Signal::SIGUSR2,
71 _ => panic!("unsupported signal: {}", signame),
72 };
73
74 Some(signal)
75 } else {
76 None
77 }
78}
79
80static GLOBAL_HANDLER_ID: AtomicUsize = ATOMIC_USIZE_INIT;
81
82#[cfg(unix)]
83pub fn uninstall_handler() {
84 GLOBAL_HANDLER_ID.fetch_add(1, Ordering::Relaxed) + 1;
85
86 use nix::libc::c_int;
87 use nix::sys::signal::*;
88 use nix::unistd::Pid;
89
90 kill(Pid::this(), Signal::SIGUSR2);
92}
93
94#[cfg(unix)]
95pub fn install_handler<F>(handler: F)
96where
97 F: Fn(self::Signal) + 'static + Send + Sync,
98{
99 use nix::libc::c_int;
100 use nix::sys::signal::*;
101 use std::thread;
102
103 let mut mask = SigSet::empty();
106 mask.add(SIGKILL);
107 mask.add(SIGTERM);
108 mask.add(SIGINT);
109 mask.add(SIGHUP);
110 mask.add(SIGSTOP);
111 mask.add(SIGCONT);
112 mask.add(SIGCHLD);
113 mask.add(SIGUSR1);
114 mask.add(SIGUSR2);
115 mask.thread_swap_mask(SigmaskHow::SIG_SETMASK).expect("unable to set signal mask");
116
117 set_handler(handler);
118
119 pub extern "C" fn sigchld_handler(_: c_int) {}
121
122 unsafe {
123 let _ = sigaction(
124 SIGCHLD,
125 &SigAction::new(
126 SigHandler::Handler(sigchld_handler),
127 SaFlags::empty(),
128 SigSet::empty(),
129 ),
130 );
131 }
132
133 let id = GLOBAL_HANDLER_ID.fetch_add(1, Ordering::Relaxed) + 1;
135 thread::spawn(move || {
136 let mut is_current = true;
137 while is_current {
138 let signal = mask.wait().expect("Unable to sigwait");
139 debug!("Received {:?}", signal);
140
141 if id != GLOBAL_HANDLER_ID.load(Ordering::Relaxed) {
142 return;
143 }
144 invoke(signal);
146
147 if signal != SIGCHLD {
149 let default_action =
150 SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty());
151
152 unsafe {
153 let _ = sigaction(signal, &default_action);
154 }
155 }
156
157 let mut new_mask = SigSet::empty();
158 new_mask.add(signal);
159
160 let _ = new_mask.thread_unblock();
162 let _ = raise(signal);
163 let _ = new_mask.thread_block();
164 }
165 });
166}
167
168#[cfg(windows)]
169pub fn uninstall_handler() {
170 use kernel32::SetConsoleCtrlHandler;
171 use winapi::{BOOL, DWORD, FALSE, TRUE};
172
173 unsafe {
174 SetConsoleCtrlHandler(Some(ctrl_handler), FALSE);
175 }
176 debug!("Removed ConsoleCtrlHandler.");
177}
178
179#[cfg(windows)]
180pub unsafe extern "system" fn ctrl_handler(_: winapi::DWORD) -> winapi::BOOL {
181 invoke(self::Signal::SIGTERM);
182
183 winapi::FALSE
184}
185
186#[cfg(windows)]
187pub fn install_handler<F>(handler: F)
188where
189 F: Fn(self::Signal) + 'static + Send + Sync,
190{
191 use kernel32::SetConsoleCtrlHandler;
192 use winapi::{BOOL, DWORD, FALSE, TRUE};
193
194 set_handler(handler);
195
196 unsafe {
197 SetConsoleCtrlHandler(Some(ctrl_handler), TRUE);
198 }
199}
200
201fn invoke(sig: self::Signal) {
202 if let Some(ref handler) = *CLEANUP.lock().unwrap() {
203 handler(sig)
204 }
205}
206
207fn set_handler<F>(handler: F)
208where
209 F: Fn(self::Signal) + 'static + Send + Sync,
210{
211 *CLEANUP.lock().unwrap() = Some(Box::new(handler));
212}