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}