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