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     s!(SIGIO, Ignore),
51     // Can't override anyway, but...
52     s!(SIGKILL, Term),
53     s!(SIGPIPE, Term),
54     s!(SIGPROF, Term),
55     s!(SIGQUIT, Term),
56     s!(SIGSEGV, Term),
57     // Can't override anyway, but...
58     s!(SIGSTOP, Stop),
59     s!(SIGSYS, Term),
60     s!(SIGTERM, Term),
61     s!(SIGTRAP, Term),
62     s!(SIGTSTP, Stop),
63     s!(SIGTTIN, Stop),
64     s!(SIGTTOU, Stop),
65     s!(SIGURG, Ignore),
66     s!(SIGUSR1, Term),
67     s!(SIGUSR2, Term),
68     s!(SIGVTALRM, Term),
69     s!(SIGWINCH, Ignore),
70     s!(SIGXCPU, Term),
71     s!(SIGXFSZ, Term),
72 ];
73 
74 #[cfg(windows)]
75 const DETAILS: &[Details] = &[
76     s!(SIGABRT, Term),
77     s!(SIGFPE, Term),
78     s!(SIGILL, Term),
79     s!(SIGINT, Term),
80     s!(SIGSEGV, Term),
81     s!(SIGTERM, Term),
82 ];
83 
84 /// Provides a human-readable name of a signal.
85 ///
86 /// Note that the name does not have to be known (in case it is some less common, or non-standard
87 /// signal).
88 ///
89 /// # Examples
90 ///
91 /// ```
92 /// # use signal_hook::low_level::signal_name;
93 /// assert_eq!("SIGKILL", signal_name(9).unwrap());
94 /// assert!(signal_name(142).is_none());
95 /// ```
signal_name(signal: c_int) -> Option<&'static str>96 pub fn signal_name(signal: c_int) -> Option<&'static str> {
97     DETAILS.iter().find(|d| d.signal == signal).map(|d| d.name)
98 }
99 
100 #[cfg(not(windows))]
restore_default(signal: c_int) -> Result<(), Error>101 fn restore_default(signal: c_int) -> Result<(), Error> {
102     unsafe {
103         // A C structure, supposed to be memset to 0 before use.
104         let mut action: libc::sigaction = mem::zeroed();
105         action.sa_sigaction = libc::SIG_DFL as _;
106         if libc::sigaction(signal, &action, ptr::null_mut()) == 0 {
107             Ok(())
108         } else {
109             Err(Error::last_os_error())
110         }
111     }
112 }
113 
114 #[cfg(windows)]
restore_default(signal: c_int) -> Result<(), Error>115 fn restore_default(signal: c_int) -> Result<(), Error> {
116     unsafe {
117         // SIG_DFL = 0, but not in libc :-(
118         if libc::signal(signal, 0) == 0 {
119             Ok(())
120         } else {
121             Err(Error::last_os_error())
122         }
123     }
124 }
125 
126 /// Emulates the behaviour of a default handler for the provided signal.
127 ///
128 /// This function does its best to provide the same action as the default handler would do, without
129 /// disrupting the rest of the handling of such signal in the application. It is also
130 /// async-signal-safe.
131 ///
132 /// This function necessarily looks up the appropriate action in a table. That means it is possible
133 /// your system has a signal that is not known to this function. In such case an error is returned
134 /// (equivalent of `EINVAL`).
135 ///
136 /// See also the [`register_conditional_default`][crate::flag::register_conditional_default].
137 ///
138 /// # Warning
139 ///
140 /// There's a short race condition in case of signals that terminate (either with or without a core
141 /// dump). The emulation first resets the signal handler back to default (as the application is
142 /// going to end, it's not a problem) and invokes it. But if some other thread installs a signal
143 /// handler in the meantime (without assistance from `signal-hook`), it can happen this will be
144 /// invoked by the re-raised signal.
145 ///
146 /// This function will still terminate the application (there's a fallback on `abort`), the risk is
147 /// invoking the newly installed signal handler. Note that manipulating the low-level signals is
148 /// always racy in a multi-threaded program, therefore the described situation is already
149 /// discouraged.
150 ///
151 /// If you are uneasy about such race condition, the recommendation is to run relevant termination
152 /// routine manually ([`exit`][super::exit] or [`abort`][super::abort]); they always do what they
153 /// say, but slightly differ in externally observable behaviour from termination by a signal (the
154 /// exit code will specify that the application exited, not that it terminated with a signal in the
155 /// first case, and `abort` terminates on `SIGABRT`, so the detected termination signal may be
156 /// different).
emulate_default_handler(signal: c_int) -> Result<(), Error>157 pub fn emulate_default_handler(signal: c_int) -> Result<(), Error> {
158     #[cfg(not(windows))]
159     {
160         if signal == SIGSTOP || signal == SIGKILL {
161             return low_level::raise(signal);
162         }
163     }
164     let kind = DETAILS
165         .iter()
166         .find(|d| d.signal == signal)
167         .map(|d| d.default_kind)
168         .ok_or_else(|| Error::from_raw_os_error(EINVAL))?;
169     match kind {
170         DefaultKind::Ignore => Ok(()),
171         #[cfg(not(windows))]
172         DefaultKind::Stop => low_level::raise(SIGSTOP),
173         DefaultKind::Term => {
174             if let Ok(()) = restore_default(signal) {
175                 #[cfg(not(windows))]
176                 unsafe {
177                     #[allow(deprecated)]
178                     let mut newsigs: sigset_t = mem::uninitialized();
179                     libc::sigemptyset(&mut newsigs);
180                     libc::sigaddset(&mut newsigs, signal);
181                     // Ignore the result, if it doesn't work, we try anyway
182                     // Also, sigprocmask is unspecified, but available on more systems. And we want
183                     // to just enable _something_. And if it doesn't work, we'll terminate
184                     // anyway... It's not UB, so we are good.
185                     libc::sigprocmask(SIG_UNBLOCK, &newsigs, ptr::null_mut());
186                 }
187                 let _ = low_level::raise(signal);
188             }
189             // Fallback if anything failed or someone managed to put some other action in in
190             // between.
191             unsafe { libc::abort() }
192         }
193     }
194 }
195 
196 #[cfg(test)]
197 mod test {
198     use super::*;
199 
200     #[test]
existing()201     fn existing() {
202         assert_eq!("SIGTERM", signal_name(SIGTERM).unwrap());
203     }
204 
205     #[test]
unknown()206     fn unknown() {
207         assert!(signal_name(128).is_none());
208     }
209 }
210