signal_hook/low_level/
signal_details.rs

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| d.signal == signal).map(|d| 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        action.sa_sigaction = libc::SIG_DFL as _;
115        if libc::sigaction(signal, &action, ptr::null_mut()) == 0 {
116            Ok(())
117        } else {
118            Err(Error::last_os_error())
119        }
120    }
121}
122
123#[cfg(windows)]
124fn restore_default(signal: c_int) -> Result<(), Error> {
125    unsafe {
126        // SIG_DFL = 0, but not in libc :-(
127        if libc::signal(signal, 0) == 0 {
128            Ok(())
129        } else {
130            Err(Error::last_os_error())
131        }
132    }
133}
134
135/// Emulates the behaviour of a default handler for the provided signal.
136///
137/// This function does its best to provide the same action as the default handler would do, without
138/// disrupting the rest of the handling of such signal in the application. It is also
139/// async-signal-safe.
140///
141/// This function necessarily looks up the appropriate action in a table. That means it is possible
142/// your system has a signal that is not known to this function. In such case an error is returned
143/// (equivalent of `EINVAL`).
144///
145/// See also the [`register_conditional_default`][crate::flag::register_conditional_default].
146///
147/// # Warning
148///
149/// There's a short race condition in case of signals that terminate (either with or without a core
150/// dump). The emulation first resets the signal handler back to default (as the application is
151/// going to end, it's not a problem) and invokes it. But if some other thread installs a signal
152/// handler in the meantime (without assistance from `signal-hook`), it can happen this will be
153/// invoked by the re-raised signal.
154///
155/// This function will still terminate the application (there's a fallback on `abort`), the risk is
156/// invoking the newly installed signal handler. Note that manipulating the low-level signals is
157/// always racy in a multi-threaded program, therefore the described situation is already
158/// discouraged.
159///
160/// If you are uneasy about such race condition, the recommendation is to run relevant termination
161/// routine manually ([`exit`][super::exit] or [`abort`][super::abort]); they always do what they
162/// say, but slightly differ in externally observable behaviour from termination by a signal (the
163/// exit code will specify that the application exited, not that it terminated with a signal in the
164/// first case, and `abort` terminates on `SIGABRT`, so the detected termination signal may be
165/// different).
166pub fn emulate_default_handler(signal: c_int) -> Result<(), Error> {
167    #[cfg(not(windows))]
168    {
169        if signal == SIGSTOP || signal == SIGKILL {
170            return low_level::raise(signal);
171        }
172    }
173    let kind = DETAILS
174        .iter()
175        .find(|d| d.signal == signal)
176        .map(|d| d.default_kind)
177        .ok_or_else(|| Error::from_raw_os_error(EINVAL))?;
178    match kind {
179        DefaultKind::Ignore => Ok(()),
180        #[cfg(not(windows))]
181        DefaultKind::Stop => low_level::raise(SIGSTOP),
182        DefaultKind::Term => {
183            if let Ok(()) = restore_default(signal) {
184                #[cfg(not(windows))]
185                unsafe {
186                    #[allow(deprecated)]
187                    let mut newsigs: sigset_t = mem::zeroed();
188
189                    // Some android versions don't have the sigemptyset and sigaddset.
190                    // Unfortunately, we don't have an access to the android _version_. We just
191                    // know that 64bit versions are all OK, so this is a best-effort guess.
192                    //
193                    // For the affected/guessed versions, we provide our own implementation. We
194                    // hope it to be correct (it's inspired by a libc implementation and we assume
195                    // the kernel uses the same format ‒ it's unlikely to be different both because
196                    // of compatibility and because there's really nothing to invent about a
197                    // bitarray).
198                    //
199                    // We use the proper way for other systems.
200                    #[cfg(all(target_os = "android", target_pointer_width = "32"))]
201                    unsafe fn prepare_sigset(set: *mut sigset_t, mut signal: c_int) {
202                        signal -= 1;
203                        let set_raw: *mut libc::c_ulong = set.cast();
204                        let size = mem::size_of::<libc::c_ulong>();
205                        assert_eq!(set_raw as usize % mem::align_of::<libc::c_ulong>(), 0);
206                        let pos = signal as usize / size;
207                        assert!(pos < mem::size_of::<sigset_t>() / size);
208                        let bit = 1 << (signal as usize % size);
209                        set_raw.add(pos).write(bit);
210                    }
211
212                    #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
213                    unsafe fn prepare_sigset(set: *mut sigset_t, signal: c_int) {
214                        libc::sigemptyset(set);
215                        libc::sigaddset(set, signal);
216                    }
217
218                    prepare_sigset(&mut newsigs, signal);
219                    // Ignore the result, if it doesn't work, we try anyway
220                    // Also, sigprocmask is unspecified, but available on more systems. And we want
221                    // to just enable _something_. And if it doesn't work, we'll terminate
222                    // anyway... It's not UB, so we are good.
223                    libc::sigprocmask(SIG_UNBLOCK, &newsigs, ptr::null_mut());
224                }
225                let _ = low_level::raise(signal);
226            }
227            // Fallback if anything failed or someone managed to put some other action in in
228            // between.
229            unsafe { libc::abort() }
230        }
231    }
232}
233
234#[cfg(test)]
235mod test {
236    use super::*;
237
238    #[test]
239    fn existing() {
240        assert_eq!("SIGTERM", signal_name(SIGTERM).unwrap());
241    }
242
243    #[test]
244    fn unknown() {
245        assert!(signal_name(128).is_none());
246    }
247}