use {
super::*,
nix::{
errno::Errno,
fcntl::{FcntlArg, FdFlag},
sys::signal::{SaFlags, SigAction, SigHandler, SigSet},
},
std::{
fs::File,
io::Read,
os::fd::{BorrowedFd, IntoRawFd, OwnedFd},
sync::atomic::{self, AtomicI32},
},
};
const INVALID_FILENO: i32 = -1;
static WRITE: AtomicI32 = AtomicI32::new(INVALID_FILENO);
fn die(message: &str) -> ! {
const STDERR: BorrowedFd = unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) };
let mut i = 0;
let mut buffer = [0; 512];
let mut append = |s: &str| {
let remaining = buffer.len() - i;
let n = s.len().min(remaining);
let end = i + n;
buffer[i..end].copy_from_slice(&s.as_bytes()[0..n]);
i = end;
};
append("error: ");
append(message);
append("\n");
nix::unistd::write(STDERR, &buffer[0..i]).ok();
process::abort();
}
extern "C" fn handler(signal: libc::c_int) {
let errno = Errno::last();
let Ok(signal) = u8::try_from(signal) else {
die("unexpected signal");
};
let fd = unsafe { BorrowedFd::borrow_raw(WRITE.load(atomic::Ordering::Relaxed)) };
if let Err(err) = nix::unistd::write(fd, &[signal]) {
die(err.desc());
}
errno.set();
}
fn fcntl(fd: &OwnedFd, arg: FcntlArg) -> RunResult<'static, libc::c_int> {
nix::fcntl::fcntl(fd, arg).map_err(|errno| Error::SignalHandlerPipeCloexec {
io_error: errno.into(),
})
}
fn set_cloexec(fd: &OwnedFd) -> RunResult<'static> {
let existing_flags = fcntl(fd, FcntlArg::F_GETFD)?;
let combined_flags = FdFlag::from_bits_retain(existing_flags) | FdFlag::FD_CLOEXEC;
fcntl(fd, FcntlArg::F_SETFD(combined_flags))?;
Ok(())
}
pub(crate) struct Signals(File);
impl Signals {
pub(crate) fn new() -> RunResult<'static, Self> {
let (read, write) = nix::unistd::pipe().map_err(|errno| Error::SignalHandlerPipeOpen {
io_error: errno.into(),
})?;
set_cloexec(&read)?;
set_cloexec(&write)?;
if WRITE
.compare_exchange(
INVALID_FILENO,
write.into_raw_fd(),
atomic::Ordering::Relaxed,
atomic::Ordering::Relaxed,
)
.is_err()
{
panic!("signal iterator cannot be initialized twice");
}
let sa = SigAction::new(
SigHandler::Handler(handler),
SaFlags::SA_RESTART,
SigSet::empty(),
);
for &signal in Signal::ALL {
unsafe {
nix::sys::signal::sigaction(signal.into(), &sa).map_err(|errno| {
Error::SignalHandlerSigaction {
signal,
io_error: errno.into(),
}
})?;
}
}
Ok(Self(File::from(read)))
}
}
impl Iterator for Signals {
type Item = io::Result<Signal>;
fn next(&mut self) -> Option<Self::Item> {
let mut signal = [0];
Some(
self
.0
.read_exact(&mut signal)
.and_then(|()| Signal::try_from(signal[0])),
)
}
}