use std::io::Error;
use std::mem;
use std::ptr;
use libc::{c_int, EINVAL};
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),
s!(SIGCONT, Ignore),
s!(SIGFPE, Term),
s!(SIGHUP, Term),
s!(SIGILL, Term),
s!(SIGINT, Term),
s!(SIGIO, Ignore),
s!(SIGKILL, Term),
s!(SIGPIPE, Term),
s!(SIGPROF, Term),
s!(SIGQUIT, Term),
s!(SIGSEGV, Term),
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),
];
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 {
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 {
if libc::signal(signal, 0) == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
}
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) {
let _ = low_level::raise(signal);
}
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());
}
}