use crate::errno::Errno;
pub use crate::sys::signal::{self, SigSet};
use crate::Result;
pub use libc::signalfd_siginfo as siginfo;
use std::mem;
use std::os::unix::io::{AsRawFd, RawFd, FromRawFd, OwnedFd, AsFd, BorrowedFd};
libc_bitflags! {
pub struct SfdFlags: libc::c_int {
SFD_NONBLOCK;
SFD_CLOEXEC;
}
}
#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>();
#[deprecated(since = "0.27.0", note = "Use SignalFd instead")]
pub fn signalfd<F: AsFd>(fd: Option<F>, mask: &SigSet, flags: SfdFlags) -> Result<OwnedFd> {
_signalfd(fd, mask, flags)
}
fn _signalfd<F: AsFd>(fd: Option<F>, mask: &SigSet, flags: SfdFlags) -> Result<OwnedFd> {
let raw_fd = fd.map_or(-1, |x|x.as_fd().as_raw_fd());
unsafe {
Errno::result(libc::signalfd(
raw_fd,
mask.as_ref(),
flags.bits(),
)).map(|raw_fd|FromRawFd::from_raw_fd(raw_fd))
}
}
#[derive(Debug)]
pub struct SignalFd(OwnedFd);
impl SignalFd {
pub fn new(mask: &SigSet) -> Result<SignalFd> {
Self::with_flags(mask, SfdFlags::empty())
}
pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> {
let fd = _signalfd(None::<OwnedFd>, mask, flags)?;
Ok(SignalFd(fd))
}
pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
_signalfd(Some(self.0.as_fd()), mask, SfdFlags::empty()).map(drop)
}
pub fn read_signal(&mut self) -> Result<Option<siginfo>> {
let mut buffer = mem::MaybeUninit::<siginfo>::uninit();
let size = mem::size_of_val(&buffer);
let res = Errno::result(unsafe {
libc::read(self.0.as_raw_fd(), buffer.as_mut_ptr() as *mut libc::c_void, size)
})
.map(|r| r as usize);
match res {
Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })),
Ok(_) => unreachable!("partial read on signalfd"),
Err(Errno::EAGAIN) => Ok(None),
Err(error) => Err(error),
}
}
}
impl AsFd for SignalFd {
fn as_fd(&self) -> BorrowedFd {
self.0.as_fd()
}
}
impl AsRawFd for SignalFd {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl Iterator for SignalFd {
type Item = siginfo;
fn next(&mut self) -> Option<Self::Item> {
match self.read_signal() {
Ok(Some(sig)) => Some(sig),
Ok(None) | Err(_) => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_signalfd() {
let mask = SigSet::empty();
SignalFd::new(&mask).unwrap();
}
#[test]
fn create_signalfd_with_opts() {
let mask = SigSet::empty();
SignalFd::with_flags(
&mask,
SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK,
)
.unwrap();
}
#[test]
fn read_empty_signalfd() {
let mask = SigSet::empty();
let mut fd =
SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
let res = fd.read_signal();
assert!(res.unwrap().is_none());
}
}