use std::io;
use crate::raw::last_errno;
const RESET_SIGNALS: &[libc::c_int] = &[
libc::SIGHUP,
libc::SIGINT,
libc::SIGQUIT,
libc::SIGILL,
libc::SIGABRT,
libc::SIGFPE,
libc::SIGBUS,
libc::SIGSEGV,
libc::SIGPIPE,
libc::SIGALRM,
libc::SIGTERM,
libc::SIGUSR1,
libc::SIGUSR2,
libc::SIGCHLD,
libc::SIGCONT,
libc::SIGTSTP,
libc::SIGTTIN,
libc::SIGTTOU,
libc::SIGWINCH,
];
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
pub enum ChildSignalState {
#[default]
Reset,
Inherit,
}
pub(crate) struct Prepared {
default_action: libc::sigaction,
empty_mask: libc::sigset_t,
}
impl std::fmt::Debug for Prepared {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("PreparedChildSignalState")
}
}
impl Prepared {
pub(crate) fn new(state: ChildSignalState) -> io::Result<Option<Self>> {
if state == ChildSignalState::Inherit {
return Ok(None);
}
let mut default_action: libc::sigaction = unsafe { std::mem::zeroed() };
let mut empty_mask: libc::sigset_t = unsafe { std::mem::zeroed() };
default_action.sa_sigaction = libc::SIG_DFL;
default_action.sa_flags = 0;
unsafe {
if libc::sigemptyset(&raw mut default_action.sa_mask) == -1
|| libc::sigemptyset(&raw mut empty_mask) == -1
{
return Err(io::Error::last_os_error());
}
}
Ok(Some(Self {
default_action,
empty_mask,
}))
}
pub(crate) fn apply_in_child(&self) -> Result<(), libc::c_int> {
loop {
if unsafe {
libc::sigprocmask(
libc::SIG_SETMASK,
&raw const self.empty_mask,
std::ptr::null_mut(),
)
} == 0
{
break;
}
let errno = last_errno();
if errno != libc::EINTR {
return Err(errno);
}
}
for signal in RESET_SIGNALS {
loop {
if unsafe {
libc::sigaction(
*signal,
&raw const self.default_action,
std::ptr::null_mut(),
)
} == 0
{
break;
}
let errno = last_errno();
if errno == libc::EINTR {
continue;
}
if errno == libc::EINVAL {
break;
}
return Err(errno);
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{ChildSignalState, Prepared, RESET_SIGNALS};
#[test]
fn reset_list_excludes_uncatchable_signals() {
assert!(!RESET_SIGNALS.contains(&libc::SIGKILL));
assert!(!RESET_SIGNALS.contains(&libc::SIGSTOP));
}
#[test]
fn explicit_inheritance_requires_no_child_operations() -> Result<(), Box<dyn std::error::Error>>
{
assert!(Prepared::new(ChildSignalState::Inherit)?.is_none());
assert!(Prepared::new(ChildSignalState::Reset)?.is_some());
Ok(())
}
}