#[cfg(feature = "tokio")]
#[path = "linux/tokio.rs"]
pub mod tokio;
use core::mem::{size_of_val, MaybeUninit};
use core::ptr;
use core::sync::atomic::{AtomicI32, Ordering};
use heveanly::syscall::{self, syscall, Check};
use heveanly::{retry_eintr, AsUninitBytes, Fd};
use libc::sigemptyset;
pub type Signal = libc::c_int;
fn signals_new<T>(sigs: &[Signal], from_sigset: fn(&mut libc::sigset_t) -> T) -> T {
let mut sigset = MaybeUninit::uninit();
from_sigset(unsafe {
libc::sigemptyset(sigset.as_mut_ptr());
for &sig in sigs {
libc::sigaddset(sigset.as_mut_ptr(), sig);
}
sigset.assume_init_mut()
})
}
fn fill_all(sigs: *mut libc::sigset_t) {
unsafe {
libc::sigfillset(sigs);
libc::sigdelset(sigs, libc::SIGBUS);
libc::sigdelset(sigs, libc::SIGFPE);
libc::sigdelset(sigs, libc::SIGILL);
libc::sigdelset(sigs, libc::SIGSEGV);
libc::sigdelset(sigs, libc::SIGPIPE);
}
}
fn signals_all<T>(from_sigset: fn(&mut libc::sigset_t) -> T) -> T {
let mut sigs = MaybeUninit::uninit();
from_sigset(unsafe {
fill_all(sigs.as_mut_ptr());
sigs.assume_init_mut()
})
}
fn signals_deadly<T>(from_sigset: fn(&mut libc::sigset_t) -> T) -> T {
let mut sigs = MaybeUninit::uninit();
from_sigset(unsafe {
fill_all(sigs.as_mut_ptr());
libc::sigdelset(sigs.as_mut_ptr(), libc::SIGCHLD);
libc::sigdelset(sigs.as_mut_ptr(), libc::SIGURG);
libc::sigdelset(sigs.as_mut_ptr(), libc::SIGWINCH);
sigs.assume_init_mut()
})
}
fn signals_benign<T>(from_sigset: fn(&mut libc::sigset_t) -> T) -> T {
let mut sigs = MaybeUninit::uninit();
from_sigset(unsafe {
libc::sigemptyset(sigs.as_mut_ptr());
libc::sigaddset(sigs.as_mut_ptr(), libc::SIGCHLD);
libc::sigaddset(sigs.as_mut_ptr(), libc::SIGURG);
libc::sigaddset(sigs.as_mut_ptr(), libc::SIGWINCH);
sigs.assume_init_mut()
})
}
macro_rules! libc_unwrap {
($f:path, $($xs:expr),*$(,)?) => {
{
let rc = $f($($xs),+);
if rc < 0 {
panic!("`{}` failed with error code {}", stringify!($f), *libc::__errno_location());
}
rc
}
}
}
macro_rules! libc_unwrap_fd {
($f:path, $($xs:expr),*$(,)?) => {
Fd::new_unchecked(libc_unwrap!($f, $($xs),*))
}
}
static SIGINT_EFD: AtomicI32 = AtomicI32::new(-1);
extern "C" fn sigint_efd_handler(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void) {
let _ =
unsafe { Fd::new_unchecked(SIGINT_EFD.load(Ordering::Relaxed)) }.write(&1u64.to_ne_bytes());
}
fn sigint_efd() -> Fd {
let efd = unsafe {
libc_unwrap_fd!(
libc::eventfd,
0,
libc::EFD_CLOEXEC | libc::EFD_NONBLOCK | libc::EFD_SEMAPHORE,
)
};
match SIGINT_EFD.compare_exchange(-1, efd.get(), Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => {
let mut act = MaybeUninit::<libc::sigaction>::uninit();
unsafe {
(*act.as_mut_ptr()).sa_sigaction = sigint_efd_handler as usize;
sigemptyset(&mut (*act.as_mut_ptr()).sa_mask);
(*act.as_mut_ptr()).sa_flags = libc::SA_SIGINFO;
libc_unwrap!(libc::sigaction, libc::SIGINT, act.assume_init_ref(), ptr::null_mut());
}
efd
},
Err(fd) => {
let _ = efd.close();
unsafe { Fd::new_unchecked(fd) }
},
}
}
pub struct Signals {
sigint_efd: i32, sigfd: Fd,
}
impl Signals {
fn from_sigset(sigs: &mut libc::sigset_t) -> Self {
unsafe {
let (sigint_efd, flags) = match libc::sigismember(sigs, libc::SIGINT) {
1 => {
libc::sigdelset(sigs, libc::SIGINT);
(sigint_efd().get(), libc::SFD_CLOEXEC | libc::SFD_NONBLOCK)
},
_ => (-1, libc::SFD_CLOEXEC),
};
libc::pthread_sigmask(libc::SIG_BLOCK, sigs, ptr::null_mut());
Self { sigint_efd, sigfd: libc_unwrap_fd!(libc::signalfd, -1, sigs, flags) }
}
}
pub fn new(sigs: &[Signal]) -> Self {
signals_new(sigs, Self::from_sigset)
}
pub fn all() -> Self {
signals_all(Self::from_sigset)
}
pub fn deadly() -> Self {
signals_deadly(Self::from_sigset)
}
pub fn benign() -> Self {
signals_benign(Self::from_sigset)
}
}
impl Drop for Signals {
fn drop(&mut self) {
let _ = self.sigfd.close();
}
}
fn next(sigfd: Fd) -> Option<Signal> {
let mut info = MaybeUninit::<libc::signalfd_siginfo>::uninit();
match retry_eintr(|| sigfd.read(info.as_uninit_bytes_mut())).ok()? == size_of_val(&info) {
true => Some(unsafe { info.assume_init_ref() }.ssi_signo as Signal),
false => None,
}
}
fn next_with_sigint(sigint_efd: Fd, sigfd: Fd) -> Option<Signal> {
let mut pfds = MaybeUninit::<[libc::pollfd; 2]>::uninit();
let pfds = unsafe {
(*pfds.as_mut_ptr())[0].fd = sigint_efd.get();
(*pfds.as_mut_ptr())[0].events = libc::POLLIN;
(*pfds.as_mut_ptr())[1].fd = sigfd.get();
(*pfds.as_mut_ptr())[1].events = libc::POLLIN;
#[cfg(any(target_arch = "arm", target_arch = "x86", target_arch = "x86_64"))]
retry_eintr(|| syscall!(syscall::SYS_poll, pfds.as_mut_ptr(), 2, -1).check()).ok()?;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
retry_eintr(|| syscall!(syscall::SYS_ppoll, pfds.as_mut_ptr(), 2, 0, 0, 0).check()).ok()?;
pfds.assume_init_ref()
};
if pfds[0].revents != 0 {
let _ = sigint_efd.read(MaybeUninit::<[u8; 8]>::uninit().as_uninit_bytes_mut());
return Some(libc::SIGINT);
}
debug_assert_ne!(pfds[1].revents, 0);
next(sigfd)
}
impl Iterator for Signals {
type Item = Signal;
fn next(&mut self) -> Option<Self::Item> {
if self.sigint_efd < 0 {
next(self.sigfd)
} else {
next_with_sigint(unsafe { Fd::new_unchecked(self.sigint_efd) }, self.sigfd)
}
}
}