use std::io;
use std::mem;
use std::ptr;
use std::os::unix::io::{RawFd, AsRawFd};
use libc::{self, SFD_CLOEXEC, SIG_SETMASK, c_void, sigset_t, signalfd_siginfo,
signalfd, sigemptyset, sigaddset, sigdelset, pthread_sigmask};
use unsafe_cell::{UnsafeRefCell};
use error::{ErrCode, READY, EINTR, EAGAIN, last_error, eof, stopped};
use io_service::{IoObject, IoService, Handler, AsyncResult, IoActor};
use fd_ops::{AsIoActor, getnonblock, setnonblock, cancel};
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum Signal {
SIGHUP = libc::SIGHUP as isize,
SIGINT = libc::SIGINT as isize,
SIGQUIT = libc::SIGQUIT as isize,
SIGILL = libc::SIGILL as isize,
SIGABRT = libc::SIGABRT as isize,
SIGFPE = libc::SIGFPE as isize,
SIGKILL = libc::SIGKILL as isize,
SIGSEGV = libc::SIGSEGV as isize,
SIGPIPE = libc::SIGPIPE as isize,
SIGALRM = libc::SIGALRM as isize,
SIGTERM = libc::SIGTERM as isize,
SIGUSR1 = libc::SIGUSR1 as isize,
SIGUSR2 = libc::SIGUSR2 as isize,
SIGCHLD = libc::SIGCHLD as isize,
SIGCONT = libc::SIGCONT as isize,
SIGSTOP = libc::SIGSTOP as isize,
SIGTSTP = libc::SIGTSTP as isize,
SIGTTIN = libc::SIGTTIN as isize,
SIGTTOU = libc::SIGTTOU as isize,
SIGBUS = libc::SIGBUS as isize,
SIGPOLL = libc::SIGPOLL as isize,
SIGPROF = libc::SIGPROF as isize,
SIGSYS = libc::SIGSYS as isize,
SIGTRAP = libc::SIGTRAP as isize,
SIGURG = libc::SIGURG as isize,
SIGVTALRM = libc::SIGVTALRM as isize,
SIGXCPU = libc::SIGXCPU as isize,
SIGXFSZ = libc::SIGXFSZ as isize,
}
fn signalfd_init() -> io::Result<(RawFd, sigset_t)> {
let mut mask: sigset_t = unsafe { mem::uninitialized() };
libc_ign!(sigemptyset(&mut mask));
let fd = libc_try!(signalfd(-1, &mask, SFD_CLOEXEC));
Ok((fd, mask))
}
fn signalfd_add(fd: RawFd, mask: &mut sigset_t, signal: Signal) -> io::Result<()> {
libc_try!(sigaddset(mask, signal as i32));
libc_ign!(pthread_sigmask(SIG_SETMASK, mask, ptr::null_mut()));
libc_ign!(signalfd(fd, mask, 0));
Ok(())
}
fn signalfd_del(fd: RawFd, mask: &mut sigset_t, signal: Signal) -> io::Result<()> {
libc_try!(sigdelset(mask, signal as i32));
libc_ign!(pthread_sigmask(SIG_SETMASK, mask, ptr::null_mut()));
libc_ign!(signalfd(fd, mask, 0));
Ok(())
}
fn signalfd_reset(fd: RawFd, mask: &mut sigset_t) -> io::Result<()> {
libc_try!(sigemptyset(mask));
libc_ign!(pthread_sigmask(SIG_SETMASK, mask, ptr::null_mut()));
libc_ign!(signalfd(fd, mask, 0));
Ok(())
}
fn signalfd_read<T>(fd: &T) -> io::Result<Signal>
where T: AsIoActor,
{
while !fd.io_service().stopped() {
let mut ssi: signalfd_siginfo = unsafe { mem::uninitialized() };
let len = unsafe { libc::read(
fd.as_raw_fd(),
&mut ssi as *mut _ as *mut c_void,
mem::size_of::<signalfd_siginfo>()
) };
if len > 0 {
return Ok(unsafe { mem::transmute(ssi.ssi_signo as i8) });
}
if len == 0 {
return Err(eof());
}
let ec = last_error();
if ec != EINTR {
return Err(ec.into());
}
}
Err(stopped())
}
fn signalfd_async_read<T, F>(fd: &T, handler: F, ec: ErrCode)
where T: AsIoActor,
F: Handler<Signal>,
{
let fd_ptr = UnsafeRefCell::new(fd);
fd.as_io_actor().add_input(handler.wrap(move |io, ec, handler| {
let fd = unsafe { fd_ptr.as_ref() };
match ec {
READY => {
let mut ssi: signalfd_siginfo = unsafe { mem::uninitialized() };
let mode = getnonblock(fd).unwrap();
setnonblock(fd, true).unwrap();
while !io.stopped() {
let len = unsafe { libc::read(
fd.as_raw_fd(),
&mut ssi as *mut _ as *mut c_void,
mem::size_of::<signalfd_siginfo>()
) };
if len > 0 {
fd.as_io_actor().next_input();
setnonblock(fd, mode).unwrap();
handler.callback(io, Ok(unsafe { mem::transmute(ssi.ssi_signo as u8) }));
return;
}
if len == 0 {
fd.as_io_actor().next_input();
handler.callback(io, Err(eof()));
return;
}
let ec = last_error();
if ec == EAGAIN {
setnonblock(fd, mode).unwrap();
signalfd_async_read(fd, handler, ec);
return;
}
if ec != EINTR {
fd.as_io_actor().next_input();
setnonblock(fd, mode).unwrap();
handler.callback(io, Err(ec.into()));
return;
}
}
fd.as_io_actor().next_input();
setnonblock(fd, mode).unwrap();
handler.callback(io, Err(stopped()));
},
ec => {
fd.as_io_actor().next_input();
handler.callback(io, Err(ec.into()));
},
}
}), ec)
}
pub struct SignalSet {
act: IoActor,
mask: sigset_t,
}
impl SignalSet {
pub fn new(io: &IoService) -> io::Result<SignalSet> {
let (fd, mask) = try!(signalfd_init());
Ok(SignalSet {
act: IoActor::new(io, fd),
mask: mask,
})
}
pub fn add(&mut self, signal: Signal) -> io::Result<()> {
signalfd_add(self.act.as_raw_fd(), &mut self.mask, signal)
}
pub fn async_wait<F>(&self, handler: F) -> F::Output
where F: Handler<Signal>,
{
let out = handler.async_result();
signalfd_async_read(self, handler, READY);
out.get(self.io_service())
}
pub fn cancel(&self) {
cancel(self)
}
pub fn clear(&mut self) -> io::Result<()> {
signalfd_reset(self.act.as_raw_fd(), &mut self.mask)
}
pub fn remove(&mut self, signal: Signal) -> io::Result<()> {
signalfd_del(self.act.as_raw_fd(), &mut self.mask, signal)
}
pub fn wait(&self) -> io::Result<Signal> {
signalfd_read(self)
}
}
unsafe impl IoObject for SignalSet {
fn io_service(&self) -> &IoService {
self.act.io_service()
}
}
impl AsRawFd for SignalSet {
fn as_raw_fd(&self) -> RawFd {
self.act.as_raw_fd()
}
}
impl AsIoActor for SignalSet {
fn as_io_actor(&self) -> &IoActor {
&self.act
}
}
impl Drop for SignalSet {
fn drop(&mut self) {
signalfd_reset(self.act.as_raw_fd(), &mut self.mask).unwrap();
}
}
pub fn raise(signal: Signal) -> io::Result<()> {
libc_try!(libc::raise(signal as i32));
Ok(())
}
#[test]
fn test_signal_set() {
use IoService;
let io = &IoService::new();
let mut sig = SignalSet::new(io).unwrap();
sig.add(Signal::SIGHUP).unwrap();
sig.add(Signal::SIGUSR1).unwrap();
sig.remove(Signal::SIGUSR1).unwrap();
sig.remove(Signal::SIGUSR2).unwrap();
}
#[test]
fn test_signal_set_wait() {
use IoService;
let io = &IoService::new();
let mut sig = SignalSet::new(io).unwrap();
sig.add(Signal::SIGHUP).unwrap();
sig.add(Signal::SIGUSR1).unwrap();
raise(Signal::SIGHUP).unwrap();
raise(Signal::SIGUSR1).unwrap();
assert_eq!(sig.wait().unwrap(), Signal::SIGHUP);
assert_eq!(sig.wait().unwrap(), Signal::SIGUSR1);
}