use std::{fmt, io};
#[cfg(test)]
mod tests;
#[derive(Debug)]
pub struct SignalActionError {
pub signal: libc::c_int,
pub source: io::Error,
pub rollback_error: Option<io::Error>,
}
impl fmt::Display for SignalActionError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"signal {} action failed: {}",
self.signal, self.source
)
}
}
impl std::error::Error for SignalActionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
pub struct SignalActions {
previous: Vec<(libc::c_int, libc::sigaction)>,
}
impl SignalActions {
#[allow(unsafe_code)]
pub unsafe fn install(
signals: &[libc::c_int],
handler: extern "C" fn(libc::c_int),
) -> Result<Self, SignalActionError> {
for (index, &signal) in signals.iter().enumerate() {
if signals[..index].contains(&signal) {
return Err(SignalActionError {
signal,
source: io::Error::new(io::ErrorKind::InvalidInput, "duplicate signal"),
rollback_error: None,
});
}
}
let mut guard = Self {
previous: Vec::new(),
};
for &signal in signals {
let mut action: libc::sigaction = unsafe { std::mem::zeroed() };
action.sa_sigaction = handler as *const () as libc::sighandler_t;
let mut previous = unsafe { std::mem::zeroed() };
if unsafe { libc::sigemptyset(&raw mut action.sa_mask) } != 0
|| unsafe { libc::sigaction(signal, &raw const action, &raw mut previous) } != 0
{
let source = io::Error::last_os_error();
return Err(SignalActionError {
signal,
source,
rollback_error: guard.restore().err(),
});
}
guard.previous.push((signal, previous));
}
Ok(guard)
}
#[allow(unsafe_code)]
pub fn restore(&mut self) -> io::Result<()> {
let mut first_error = None;
let previous = std::mem::take(&mut self.previous);
for (signal, action) in previous.into_iter().rev() {
if unsafe { libc::sigaction(signal, &raw const action, std::ptr::null_mut()) } != 0 {
first_error.get_or_insert_with(io::Error::last_os_error);
self.previous.push((signal, action));
}
}
self.previous.reverse();
first_error.map_or(Ok(()), Err)
}
}
impl Drop for SignalActions {
fn drop(&mut self) {
let _ = self.restore();
}
}