1//! Providing auxiliary information for signals.
2
3use std::io::Error;
4use std::mem;
5use std::ptr;
6
7use libc::{c_int, EINVAL};
8#[cfg(not(windows))]
9use libc::{sigset_t, SIG_UNBLOCK};
10
11use crate::consts::signal::*;
12use crate::low_level;
13
14#[derive(Clone, Copy, Debug)]
15enum DefaultKind {
16 Ignore,
17 #[cfg(not(windows))]
18 Stop,
19 Term,
20}
21
22struct Details {
23 signal: c_int,
24 name: &'static str,
25 default_kind: DefaultKind,
26}
27
28macro_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))]
39const 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)]
84const 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/// ```
105pub 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))]
110fn 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)]
132fn 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).
174pub 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)]
243mod 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