use std::convert::TryFrom;
use std::fmt;
use std::io::Error as IoError;
use std::os::raw::c_int;
use nix::sys::signal::SigSet;
use nix::sys::signalfd::{siginfo, SfdFlags, SignalFd};
use tracing::warn;
use super::generic::{FdWrapper, Generic};
use crate::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
macro_rules! define_signal_enum {
(
$(#[$outer:meta])*
pub enum Signal {
$(
$(#[$inner:meta])*
$value:ident,
)*
}
) => {
$(#[$outer])*
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[repr(i32)]
pub enum Signal {
$(
$(#[$inner])*
$value = nix::sys::signal::Signal::$value as i32,
)*
}
impl Signal {
fn as_nix(self) -> nix::sys::signal::Signal {
match self {
$(
Signal::$value => nix::sys::signal::Signal::$value,
)*
}
}
fn from_num(id: i32) -> Self {
match nix::sys::signal::Signal::try_from(id) {
$(
Ok(nix::sys::signal::Signal::$value) => Self::$value,
)*
Ok(sig) => panic!("unknown signal: {:?}", sig),
Err(_) => panic!("unknown signal: {}", id)
}
}
}
}
}
define_signal_enum! {
pub enum Signal {
SIGHUP,
SIGINT,
SIGQUIT,
SIGILL,
SIGTRAP,
#[doc(alias = "Iot")]
#[doc(alias = "Abrt")]
SIGABRT,
SIGBUS,
SIGFPE,
SIGKILL,
SIGUSR1,
SIGSEGV,
SIGUSR2,
SIGPIPE,
#[doc(alias = "Alrm")]
SIGALRM,
SIGTERM,
#[doc(alias = "Chld")]
SIGCHLD,
SIGCONT,
SIGSTOP,
SIGTSTP,
SIGTTIN,
SIGTTOU,
SIGURG,
SIGXCPU,
SIGXFSZ,
#[doc(alias = "Vtalrm")]
SIGVTALRM,
SIGPROF,
SIGWINCH,
#[doc(alias = "Poll")]
SIGIO,
#[doc(alias = "Unused")]
SIGSYS,
}
}
#[derive(Copy, Clone, Debug)]
pub struct Event {
info: siginfo,
}
macro_rules! inner_accessors {
($($name:ident => $sname:ident: $ty:ty),*) => {$(
#[doc = stringify!($sname)]
pub fn $name(&self) -> $ty {
use core::convert::TryInto;
self.info.$sname.try_into().unwrap()
}
)*}
}
impl Event {
pub fn signal(&self) -> Signal {
Signal::from_num(self.info.ssi_signo as c_int)
}
inner_accessors! {
errno => ssi_errno: i32,
code => ssi_code: i32,
pid => ssi_pid: u32,
uid => ssi_uid: u32,
fd => ssi_fd: i32,
tid => ssi_tid: u32,
bad => ssi_band: u32,
overrun => ssi_overrun: u32,
trapno => ssi_trapno: u32,
status => ssi_status: u32
}
}
#[derive(Debug)]
pub struct Signals {
sfd: Generic<FdWrapper<SignalFd>>,
mask: SigSet,
}
impl Signals {
pub fn new(signals: &[Signal]) -> crate::Result<Signals> {
let mut mask = SigSet::empty();
for &s in signals {
mask.add(s.as_nix());
}
mask.thread_block().map_err(IoError::from)?;
let sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK | SfdFlags::SFD_CLOEXEC)
.map_err(IoError::from)?;
Ok(Signals {
sfd: Generic::new(unsafe { FdWrapper::new(sfd) }, Interest::READ, Mode::Level),
mask,
})
}
pub fn add_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
for &s in signals {
self.mask.add(s.as_nix());
}
self.mask.thread_block().map_err(IoError::from)?;
unsafe {
self.sfd
.get_mut()
.set_mask(&self.mask)
.map_err(IoError::from)?;
}
Ok(())
}
pub fn remove_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
let mut removed = SigSet::empty();
for &s in signals {
self.mask.remove(s.as_nix());
removed.add(s.as_nix());
}
removed.thread_unblock().map_err(IoError::from)?;
unsafe {
self.sfd
.get_mut()
.set_mask(&self.mask)
.map_err(IoError::from)?;
}
Ok(())
}
pub fn set_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
let mut new_mask = SigSet::empty();
for &s in signals {
new_mask.add(s.as_nix());
}
self.mask.thread_unblock().map_err(IoError::from)?;
new_mask.thread_block().map_err(IoError::from)?;
unsafe {
self.sfd
.get_mut()
.set_mask(&new_mask)
.map_err(IoError::from)?;
}
self.mask = new_mask;
Ok(())
}
}
impl Drop for Signals {
fn drop(&mut self) {
if let Err(e) = self.mask.thread_unblock() {
warn!("Failed to unmask signals: {e:?}");
}
}
}
impl EventSource for Signals {
type Event = Event;
type Metadata = ();
type Ret = ();
type Error = SignalError;
fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
self.sfd
.process_events(readiness, token, |_, sfd| {
loop {
match unsafe { sfd.get_mut().read_signal() } {
Ok(Some(info)) => callback(Event { info }, &mut ()),
Ok(None) => break,
Err(e) => {
warn!("Error reading from signalfd: {e}");
return Err(e.into());
}
}
}
Ok(PostAction::Continue)
})
.map_err(|e| SignalError(e.into()))
}
fn register(&mut self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
self.sfd.register(poll, token_factory)
}
fn reregister(
&mut self,
poll: &mut Poll,
token_factory: &mut TokenFactory,
) -> crate::Result<()> {
self.sfd.reregister(poll, token_factory)
}
fn unregister(&mut self, poll: &mut Poll) -> crate::Result<()> {
self.sfd.unregister(poll)
}
}
#[derive(Debug)]
pub struct SignalError(Box<dyn std::error::Error + Sync + Send>);
impl fmt::Display for SignalError {
#[cfg_attr(feature = "nightly_coverage", coverage(off))]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl std::error::Error for SignalError {
#[cfg_attr(feature = "nightly_coverage", coverage(off))]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&*self.0)
}
}