use libc;
use unistd;
use {Error, Errno, Result};
pub use sys::signal::{self, SigSet};
pub use libc::signalfd_siginfo as siginfo;
use std::os::unix::io::{RawFd, AsRawFd};
use std::mem;
libc_bitflags!{
pub flags SfdFlags: libc::c_int {
SFD_NONBLOCK,
SFD_CLOEXEC,
}
}
pub const SIGNALFD_NEW: RawFd = -1;
pub const SIGNALFD_SIGINFO_SIZE: usize = 128;
pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> {
unsafe {
Errno::result(libc::signalfd(fd as libc::c_int, mask.as_ref(), flags.bits()))
}
}
#[derive(Debug)]
pub struct SignalFd(RawFd);
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 = try!(signalfd(SIGNALFD_NEW, mask, flags));
Ok(SignalFd(fd))
}
pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
signalfd(self.0, mask, SfdFlags::empty()).map(|_| ())
}
pub fn read_signal(&mut self) -> Result<Option<siginfo>> {
let mut buffer: [u8; SIGNALFD_SIGINFO_SIZE] = unsafe { mem::uninitialized() };
match unistd::read(self.0, &mut buffer) {
Ok(SIGNALFD_SIGINFO_SIZE) => Ok(Some(unsafe { mem::transmute(buffer) })),
Ok(_) => unreachable!("partial read on signalfd"),
Err(Error::Sys(Errno::EAGAIN)) => Ok(None),
Err(error) => Err(error)
}
}
}
impl Drop for SignalFd {
fn drop(&mut self) {
let _ = unistd::close(self.0);
}
}
impl AsRawFd for SignalFd {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
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) => None,
Err(..) => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
use libc;
#[test]
fn check_siginfo_size() {
assert_eq!(mem::size_of::<libc::signalfd_siginfo>(), SIGNALFD_SIGINFO_SIZE);
}
#[test]
fn create_signalfd() {
let mask = SigSet::empty();
let fd = SignalFd::new(&mask);
assert!(fd.is_ok());
}
#[test]
fn create_signalfd_with_opts() {
let mask = SigSet::empty();
let fd = SignalFd::with_flags(&mask, SFD_CLOEXEC | SFD_NONBLOCK);
assert!(fd.is_ok());
}
#[test]
fn read_empty_signalfd() {
let mask = SigSet::empty();
let mut fd = SignalFd::with_flags(&mask, SFD_NONBLOCK).unwrap();
let res = fd.read_signal();
assert!(res.unwrap().is_none());
}
}