1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
//! Providing auxiliary information for signals.
use std::io::Error;
use std::mem;
use std::ptr;
use libc::{c_int, EINVAL};
#[cfg(not(windows))]
use libc::{sigset_t, SIG_UNBLOCK};
use crate::consts::signal::*;
use crate::low_level;
#[derive(Clone, Copy, Debug)]
enum DefaultKind {
Ignore,
#[cfg(not(windows))]
Stop,
Term,
}
struct Details {
signal: c_int,
name: &'static str,
default_kind: DefaultKind,
}
macro_rules! s {
($name: expr, $kind: ident) => {
Details {
signal: $name,
name: stringify!($name),
default_kind: DefaultKind::$kind,
}
};
}
#[cfg(not(windows))]
const DETAILS: &[Details] = &[
s!(SIGABRT, Term),
s!(SIGALRM, Term),
s!(SIGBUS, Term),
s!(SIGCHLD, Ignore),
// Technically, continue the process... but this is not done *by* the process.
s!(SIGCONT, Ignore),
s!(SIGFPE, Term),
s!(SIGHUP, Term),
s!(SIGILL, Term),
s!(SIGINT, Term),
#[cfg(not(target_os = "haiku"))]
s!(SIGIO, Ignore),
// Can't override anyway, but...
s!(SIGKILL, Term),
s!(SIGPIPE, Term),
s!(SIGPROF, Term),
s!(SIGQUIT, Term),
s!(SIGSEGV, Term),
// Can't override anyway, but...
s!(SIGSTOP, Stop),
s!(SIGSYS, Term),
s!(SIGTERM, Term),
s!(SIGTRAP, Term),
s!(SIGTSTP, Stop),
s!(SIGTTIN, Stop),
s!(SIGTTOU, Stop),
s!(SIGURG, Ignore),
s!(SIGUSR1, Term),
s!(SIGUSR2, Term),
s!(SIGVTALRM, Term),
s!(SIGWINCH, Ignore),
s!(SIGXCPU, Term),
s!(SIGXFSZ, Term),
];
#[cfg(windows)]
const DETAILS: &[Details] = &[
s!(SIGABRT, Term),
s!(SIGFPE, Term),
s!(SIGILL, Term),
s!(SIGINT, Term),
s!(SIGSEGV, Term),
s!(SIGTERM, Term),
];
/// Provides a human-readable name of a signal.
///
/// Note that the name does not have to be known (in case it is some less common, or non-standard
/// signal).
///
/// # Examples
///
/// ```
/// # use signal_hook::low_level::signal_name;
/// assert_eq!("SIGKILL", signal_name(9).unwrap());
/// assert!(signal_name(142).is_none());
/// ```
pub fn signal_name(signal: c_int) -> Option<&'static str> {
DETAILS.iter().find(|d| d.signal == signal).map(|d| d.name)
}
#[cfg(not(windows))]
fn restore_default(signal: c_int) -> Result<(), Error> {
unsafe {
// A C structure, supposed to be memset to 0 before use.
let mut action: libc::sigaction = mem::zeroed();
action.sa_sigaction = libc::SIG_DFL as _;
if libc::sigaction(signal, &action, ptr::null_mut()) == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
}
#[cfg(windows)]
fn restore_default(signal: c_int) -> Result<(), Error> {
unsafe {
// SIG_DFL = 0, but not in libc :-(
if libc::signal(signal, 0) == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
}
/// Emulates the behaviour of a default handler for the provided signal.
///
/// This function does its best to provide the same action as the default handler would do, without
/// disrupting the rest of the handling of such signal in the application. It is also
/// async-signal-safe.
///
/// This function necessarily looks up the appropriate action in a table. That means it is possible
/// your system has a signal that is not known to this function. In such case an error is returned
/// (equivalent of `EINVAL`).
///
/// See also the [`register_conditional_default`][crate::flag::register_conditional_default].
///
/// # Warning
///
/// There's a short race condition in case of signals that terminate (either with or without a core
/// dump). The emulation first resets the signal handler back to default (as the application is
/// going to end, it's not a problem) and invokes it. But if some other thread installs a signal
/// handler in the meantime (without assistance from `signal-hook`), it can happen this will be
/// invoked by the re-raised signal.
///
/// This function will still terminate the application (there's a fallback on `abort`), the risk is
/// invoking the newly installed signal handler. Note that manipulating the low-level signals is
/// always racy in a multi-threaded program, therefore the described situation is already
/// discouraged.
///
/// If you are uneasy about such race condition, the recommendation is to run relevant termination
/// routine manually ([`exit`][super::exit] or [`abort`][super::abort]); they always do what they
/// say, but slightly differ in externally observable behaviour from termination by a signal (the
/// exit code will specify that the application exited, not that it terminated with a signal in the
/// first case, and `abort` terminates on `SIGABRT`, so the detected termination signal may be
/// different).
pub fn emulate_default_handler(signal: c_int) -> Result<(), Error> {
#[cfg(not(windows))]
{
if signal == SIGSTOP || signal == SIGKILL {
return low_level::raise(signal);
}
}
let kind = DETAILS
.iter()
.find(|d| d.signal == signal)
.map(|d| d.default_kind)
.ok_or_else(|| Error::from_raw_os_error(EINVAL))?;
match kind {
DefaultKind::Ignore => Ok(()),
#[cfg(not(windows))]
DefaultKind::Stop => low_level::raise(SIGSTOP),
DefaultKind::Term => {
if let Ok(()) = restore_default(signal) {
#[cfg(not(windows))]
unsafe {
#[allow(deprecated)]
let mut newsigs: sigset_t = mem::zeroed();
// Some android versions don't have the sigemptyset and sigaddset.
// Unfortunately, we don't have an access to the android _version_. We just
// know that 64bit versions are all OK, so this is a best-effort guess.
//
// For the affected/guessed versions, we provide our own implementation. We
// hope it to be correct (it's inspired by a libc implementation and we assume
// the kernel uses the same format ‒ it's unlikely to be different both because
// of compatibility and because there's really nothing to invent about a
// bitarray).
//
// We use the proper way for other systems.
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
unsafe fn prepare_sigset(set: *mut sigset_t, mut signal: c_int) {
signal -= 1;
let set_raw: *mut libc::c_ulong = set.cast();
let size = mem::size_of::<libc::c_ulong>();
assert_eq!(set_raw as usize % mem::align_of::<libc::c_ulong>(), 0);
let pos = signal as usize / size;
assert!(pos < mem::size_of::<sigset_t>() / size);
let bit = 1 << (signal as usize % size);
set_raw.add(pos).write(bit);
}
#[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
unsafe fn prepare_sigset(set: *mut sigset_t, signal: c_int) {
libc::sigemptyset(set);
libc::sigaddset(set, signal);
}
prepare_sigset(&mut newsigs, signal);
// Ignore the result, if it doesn't work, we try anyway
// Also, sigprocmask is unspecified, but available on more systems. And we want
// to just enable _something_. And if it doesn't work, we'll terminate
// anyway... It's not UB, so we are good.
libc::sigprocmask(SIG_UNBLOCK, &newsigs, ptr::null_mut());
}
let _ = low_level::raise(signal);
}
// Fallback if anything failed or someone managed to put some other action in in
// between.
unsafe { libc::abort() }
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn existing() {
assert_eq!("SIGTERM", signal_name(SIGTERM).unwrap());
}
#[test]
fn unknown() {
assert!(signal_name(128).is_none());
}
}