1 | //! Providing auxiliary information for signals. |
2 | |
3 | use std::io::Error; |
4 | use std::mem; |
5 | use std::ptr; |
6 | |
7 | use libc::{c_int, EINVAL}; |
8 | #[cfg (not(windows))] |
9 | use libc::{sigset_t, SIG_UNBLOCK}; |
10 | |
11 | use crate::consts::signal::*; |
12 | use crate::low_level; |
13 | |
14 | #[derive (Clone, Copy, Debug)] |
15 | enum DefaultKind { |
16 | Ignore, |
17 | #[cfg (not(windows))] |
18 | Stop, |
19 | Term, |
20 | } |
21 | |
22 | struct Details { |
23 | signal: c_int, |
24 | name: &'static str, |
25 | default_kind: DefaultKind, |
26 | } |
27 | |
28 | macro_rules! s { |
29 | ($name: expr, $kind: ident) => { |
30 | Details { |
31 | signal: $name, |
32 | name: stringify!($name), |
33 | default_kind: DefaultKind::$kind, |
34 | } |
35 | }; |
36 | } |
37 | |
38 | #[cfg (not(windows))] |
39 | const DETAILS: &[Details] = &[ |
40 | s!(SIGABRT, Term), |
41 | s!(SIGALRM, Term), |
42 | s!(SIGBUS, Term), |
43 | s!(SIGCHLD, Ignore), |
44 | // Technically, continue the process... but this is not done *by* the process. |
45 | s!(SIGCONT, Ignore), |
46 | s!(SIGFPE, Term), |
47 | s!(SIGHUP, Term), |
48 | s!(SIGILL, Term), |
49 | s!(SIGINT, Term), |
50 | #[cfg (any( |
51 | target_os = "freebsd" , |
52 | target_os = "dragonfly" , |
53 | target_os = "netbsd" , |
54 | target_os = "openbsd" , |
55 | target_os = "macos" |
56 | ))] |
57 | s!(SIGINFO, Ignore), |
58 | #[cfg (not(target_os = "haiku" ))] |
59 | s!(SIGIO, Ignore), |
60 | // Can't override anyway, but... |
61 | s!(SIGKILL, Term), |
62 | s!(SIGPIPE, Term), |
63 | s!(SIGPROF, Term), |
64 | s!(SIGQUIT, Term), |
65 | s!(SIGSEGV, Term), |
66 | // Can't override anyway, but... |
67 | s!(SIGSTOP, Stop), |
68 | s!(SIGSYS, Term), |
69 | s!(SIGTERM, Term), |
70 | s!(SIGTRAP, Term), |
71 | s!(SIGTSTP, Stop), |
72 | s!(SIGTTIN, Stop), |
73 | s!(SIGTTOU, Stop), |
74 | s!(SIGURG, Ignore), |
75 | s!(SIGUSR1, Term), |
76 | s!(SIGUSR2, Term), |
77 | s!(SIGVTALRM, Term), |
78 | s!(SIGWINCH, Ignore), |
79 | s!(SIGXCPU, Term), |
80 | s!(SIGXFSZ, Term), |
81 | ]; |
82 | |
83 | #[cfg (windows)] |
84 | const DETAILS: &[Details] = &[ |
85 | s!(SIGABRT, Term), |
86 | s!(SIGFPE, Term), |
87 | s!(SIGILL, Term), |
88 | s!(SIGINT, Term), |
89 | s!(SIGSEGV, Term), |
90 | s!(SIGTERM, Term), |
91 | ]; |
92 | |
93 | /// Provides a human-readable name of a signal. |
94 | /// |
95 | /// Note that the name does not have to be known (in case it is some less common, or non-standard |
96 | /// signal). |
97 | /// |
98 | /// # Examples |
99 | /// |
100 | /// ``` |
101 | /// # use signal_hook::low_level::signal_name; |
102 | /// assert_eq!("SIGKILL" , signal_name(9).unwrap()); |
103 | /// assert!(signal_name(142).is_none()); |
104 | /// ``` |
105 | pub fn signal_name(signal: c_int) -> Option<&'static str> { |
106 | DETAILS.iter().find(|d: &&Details| d.signal == signal).map(|d: &Details| d.name) |
107 | } |
108 | |
109 | #[cfg (not(windows))] |
110 | fn restore_default(signal: c_int) -> Result<(), Error> { |
111 | unsafe { |
112 | // A C structure, supposed to be memset to 0 before use. |
113 | let mut action: libc::sigaction = mem::zeroed(); |
114 | #[cfg (target_os = "aix" )] |
115 | { |
116 | action.sa_union.__su_sigaction = mem::transmute::< |
117 | usize, |
118 | extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void), |
119 | >(libc::SIG_DFL); |
120 | } |
121 | #[cfg (not(target_os = "aix" ))] |
122 | { action.sa_sigaction = libc::SIG_DFL as _; } |
123 | if libc::sigaction(signum:signal, &action, oldact:ptr::null_mut()) == 0 { |
124 | Ok(()) |
125 | } else { |
126 | Err(Error::last_os_error()) |
127 | } |
128 | } |
129 | } |
130 | |
131 | #[cfg (windows)] |
132 | fn restore_default(signal: c_int) -> Result<(), Error> { |
133 | unsafe { |
134 | // SIG_DFL = 0, but not in libc :-( |
135 | if libc::signal(signal, 0) == 0 { |
136 | Ok(()) |
137 | } else { |
138 | Err(Error::last_os_error()) |
139 | } |
140 | } |
141 | } |
142 | |
143 | /// Emulates the behaviour of a default handler for the provided signal. |
144 | /// |
145 | /// This function does its best to provide the same action as the default handler would do, without |
146 | /// disrupting the rest of the handling of such signal in the application. It is also |
147 | /// async-signal-safe. |
148 | /// |
149 | /// This function necessarily looks up the appropriate action in a table. That means it is possible |
150 | /// your system has a signal that is not known to this function. In such case an error is returned |
151 | /// (equivalent of `EINVAL`). |
152 | /// |
153 | /// See also the [`register_conditional_default`][crate::flag::register_conditional_default]. |
154 | /// |
155 | /// # Warning |
156 | /// |
157 | /// There's a short race condition in case of signals that terminate (either with or without a core |
158 | /// dump). The emulation first resets the signal handler back to default (as the application is |
159 | /// going to end, it's not a problem) and invokes it. But if some other thread installs a signal |
160 | /// handler in the meantime (without assistance from `signal-hook`), it can happen this will be |
161 | /// invoked by the re-raised signal. |
162 | /// |
163 | /// This function will still terminate the application (there's a fallback on `abort`), the risk is |
164 | /// invoking the newly installed signal handler. Note that manipulating the low-level signals is |
165 | /// always racy in a multi-threaded program, therefore the described situation is already |
166 | /// discouraged. |
167 | /// |
168 | /// If you are uneasy about such race condition, the recommendation is to run relevant termination |
169 | /// routine manually ([`exit`][super::exit] or [`abort`][super::abort]); they always do what they |
170 | /// say, but slightly differ in externally observable behaviour from termination by a signal (the |
171 | /// exit code will specify that the application exited, not that it terminated with a signal in the |
172 | /// first case, and `abort` terminates on `SIGABRT`, so the detected termination signal may be |
173 | /// different). |
174 | pub fn emulate_default_handler(signal: c_int) -> Result<(), Error> { |
175 | #[cfg (not(windows))] |
176 | { |
177 | if signal == SIGSTOP || signal == SIGKILL { |
178 | return low_level::raise(signal); |
179 | } |
180 | } |
181 | let kind = DETAILS |
182 | .iter() |
183 | .find(|d| d.signal == signal) |
184 | .map(|d| d.default_kind) |
185 | .ok_or_else(|| Error::from_raw_os_error(EINVAL))?; |
186 | match kind { |
187 | DefaultKind::Ignore => Ok(()), |
188 | #[cfg (not(windows))] |
189 | DefaultKind::Stop => low_level::raise(SIGSTOP), |
190 | DefaultKind::Term => { |
191 | if let Ok(()) = restore_default(signal) { |
192 | #[cfg (not(windows))] |
193 | unsafe { |
194 | #[allow (deprecated)] |
195 | let mut newsigs: sigset_t = mem::zeroed(); |
196 | |
197 | // Some android versions don't have the sigemptyset and sigaddset. |
198 | // Unfortunately, we don't have an access to the android _version_. We just |
199 | // know that 64bit versions are all OK, so this is a best-effort guess. |
200 | // |
201 | // For the affected/guessed versions, we provide our own implementation. We |
202 | // hope it to be correct (it's inspired by a libc implementation and we assume |
203 | // the kernel uses the same format ‒ it's unlikely to be different both because |
204 | // of compatibility and because there's really nothing to invent about a |
205 | // bitarray). |
206 | // |
207 | // We use the proper way for other systems. |
208 | #[cfg (all(target_os = "android" , target_pointer_width = "32" ))] |
209 | unsafe fn prepare_sigset(set: *mut sigset_t, mut signal: c_int) { |
210 | signal -= 1; |
211 | let set_raw: *mut libc::c_ulong = set.cast(); |
212 | let size = mem::size_of::<libc::c_ulong>(); |
213 | assert_eq!(set_raw as usize % mem::align_of::<libc::c_ulong>(), 0); |
214 | let pos = signal as usize / size; |
215 | assert!(pos < mem::size_of::<sigset_t>() / size); |
216 | let bit = 1 << (signal as usize % size); |
217 | set_raw.add(pos).write(bit); |
218 | } |
219 | |
220 | #[cfg (not(all(target_os = "android" , target_pointer_width = "32" )))] |
221 | unsafe fn prepare_sigset(set: *mut sigset_t, signal: c_int) { |
222 | libc::sigemptyset(set); |
223 | libc::sigaddset(set, signal); |
224 | } |
225 | |
226 | prepare_sigset(&mut newsigs, signal); |
227 | // Ignore the result, if it doesn't work, we try anyway |
228 | // Also, sigprocmask is unspecified, but available on more systems. And we want |
229 | // to just enable _something_. And if it doesn't work, we'll terminate |
230 | // anyway... It's not UB, so we are good. |
231 | libc::sigprocmask(SIG_UNBLOCK, &newsigs, ptr::null_mut()); |
232 | } |
233 | let _ = low_level::raise(signal); |
234 | } |
235 | // Fallback if anything failed or someone managed to put some other action in in |
236 | // between. |
237 | unsafe { libc::abort() } |
238 | } |
239 | } |
240 | } |
241 | |
242 | #[cfg (test)] |
243 | mod test { |
244 | use super::*; |
245 | |
246 | #[test ] |
247 | fn existing() { |
248 | assert_eq!("SIGTERM" , signal_name(SIGTERM).unwrap()); |
249 | } |
250 | |
251 | #[test ] |
252 | fn unknown() { |
253 | assert!(signal_name(128).is_none()); |
254 | } |
255 | } |
256 | |