use std::fs::File;
use std::mem;
use std::os::raw::c_int;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::RawFd;
use std::result;
use libc::c_void;
use libc::read;
use libc::signalfd;
use libc::signalfd_siginfo;
use libc::EAGAIN;
use libc::SFD_CLOEXEC;
use libc::SFD_NONBLOCK;
use log::error;
use remain::sorted;
use thiserror::Error;
use super::signal;
use super::Error as ErrnoError;
use super::RawDescriptor;
use crate::descriptor::AsRawDescriptor;
#[sorted]
#[derive(Error, Debug)]
pub enum Error {
#[error("failed to block the signal when creating signalfd: {0}")]
CreateBlockSignal(signal::Error),
#[error("failed to create a new signalfd: {0}")]
CreateSignalFd(ErrnoError),
#[error("failed to construct sigset when creating signalfd: {0}")]
CreateSigset(ErrnoError),
#[error("signalfd failed to return a full siginfo struct, read only {0} bytes")]
SignalFdPartialRead(usize),
#[error("unable to read from signalfd: {0}")]
SignalFdRead(ErrnoError),
}
pub type Result<T> = result::Result<T, Error>;
pub struct SignalFd {
signalfd: File,
signal: c_int,
}
impl SignalFd {
pub fn new(signal: c_int) -> Result<SignalFd> {
let sigset = signal::create_sigset(&[signal]).map_err(Error::CreateSigset)?;
let fd = unsafe { signalfd(-1, &sigset, SFD_CLOEXEC | SFD_NONBLOCK) };
if fd < 0 {
return Err(Error::CreateSignalFd(ErrnoError::last()));
}
signal::block_signal(signal).map_err(Error::CreateBlockSignal)?;
unsafe {
Ok(SignalFd {
signalfd: File::from_raw_fd(fd),
signal,
})
}
}
pub fn read(&self) -> Result<Option<signalfd_siginfo>> {
let mut siginfo: signalfd_siginfo = unsafe { mem::zeroed() };
let siginfo_size = mem::size_of::<signalfd_siginfo>();
let ret = unsafe {
read(
self.signalfd.as_raw_fd(),
&mut siginfo as *mut signalfd_siginfo as *mut c_void,
siginfo_size,
)
};
if ret < 0 {
let err = ErrnoError::last();
if err.errno() == EAGAIN {
Ok(None)
} else {
Err(Error::SignalFdRead(err))
}
} else if ret == (siginfo_size as isize) {
Ok(Some(siginfo))
} else {
Err(Error::SignalFdPartialRead(ret as usize))
}
}
}
impl AsRawFd for SignalFd {
fn as_raw_fd(&self) -> RawFd {
self.signalfd.as_raw_fd()
}
}
impl AsRawDescriptor for SignalFd {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.signalfd.as_raw_descriptor()
}
}
impl Drop for SignalFd {
fn drop(&mut self) {
let res = signal::unblock_signal(self.signal);
if let Err(e) = res {
error!("signalfd failed to unblock signal {}: {}", self.signal, e);
}
}
}
#[cfg(test)]
mod tests {
use std::mem;
use std::ptr::null;
use libc::pthread_sigmask;
use libc::raise;
use libc::sigismember;
use libc::sigset_t;
use super::super::signal::SIGRTMIN;
use super::*;
#[test]
fn new() {
SignalFd::new(SIGRTMIN()).unwrap();
}
#[test]
fn read() {
let sigid = SIGRTMIN() + 1;
let sigrt_fd = SignalFd::new(sigid).unwrap();
let ret = unsafe { raise(sigid) };
assert_eq!(ret, 0);
let siginfo = sigrt_fd.read().unwrap().unwrap();
assert_eq!(siginfo.ssi_signo, sigid as u32);
}
#[test]
fn drop() {
let sigid = SIGRTMIN() + 2;
let sigrt_fd = SignalFd::new(sigid).unwrap();
unsafe {
let mut sigset: sigset_t = mem::zeroed();
pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
assert_eq!(sigismember(&sigset, sigid), 1);
}
mem::drop(sigrt_fd);
unsafe {
let mut sigset: sigset_t = mem::zeroed();
pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
assert_eq!(sigismember(&sigset, sigid), 0);
}
}
}