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