• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     #[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)]
84 const 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 /// ```
signal_name(signal: c_int) -> Option<&'static str>105 pub 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))]
restore_default(signal: c_int) -> Result<(), Error>110 fn 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(signal, &action, ptr::null_mut()) == 0 {
124             Ok(())
125         } else {
126             Err(Error::last_os_error())
127         }
128     }
129 }
130 
131 #[cfg(windows)]
restore_default(signal: c_int) -> Result<(), Error>132 fn 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).
emulate_default_handler(signal: c_int) -> Result<(), Error>174 pub 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)]
243 mod test {
244     use super::*;
245 
246     #[test]
existing()247     fn existing() {
248         assert_eq!("SIGTERM", signal_name(SIGTERM).unwrap());
249     }
250 
251     #[test]
unknown()252     fn unknown() {
253         assert!(signal_name(128).is_none());
254     }
255 }
256