use std::io::Error;
#[cfg(not(windows))]
use std::mem;
#[cfg(not(windows))]
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 {
#[cfg(not(windows))]
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),
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "macos"
))]
s!(SIGINFO, Ignore),
#[cfg(not(target_os = "haiku"))]
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 {
#[cfg(not(windows))]
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();
#[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);
libc::sigprocmask(SIG_UNBLOCK, &newsigs, ptr::null_mut());
}
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());
}
}