Skip to content

Commit

Permalink
break: Remove nix from the public API
Browse files Browse the repository at this point in the history
This commit removes nix from the public API. nix has frequent breaking
changes due to the large API surface it covers, which makes it a semver
hazard to keep it in the public API.

Thus far only two items from nix are exposed: Signal and siginfo. Signal
is an enum consisting of most real-life Unix signals. I have replaced it
with a hand rolled enum primarily taken from the one in async-signal.
For the other case, the Event structure exposes most of the relevant
fields. So I've turned these fields into accessors. We can PR more
later.

This is a breaking change, but it's in service of avoiding breaking
changes in the future.

Closes #179

Signed-off-by: John Nunley <[email protected]>
  • Loading branch information
notgull authored May 1, 2024
1 parent dd30cfe commit a3a4941
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 16 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

#### Breaking Changes

- Remove `nix` from the public API. This replaces `Signal` with a home-grown
`Signal` enum and `siginfo` with a structure that provides most of its fields.

## 0.13.0 -- 2024-02-25

#### Breaking changes
Expand Down
161 changes: 149 additions & 12 deletions src/sources/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,165 @@ use std::io::Error as IoError;
use std::os::raw::c_int;

use nix::sys::signal::SigSet;
pub use nix::sys::signal::Signal;
pub use nix::sys::signalfd::siginfo;
use nix::sys::signalfd::{SfdFlags, SignalFd};
use nix::sys::signalfd::{siginfo, SfdFlags, SignalFd};

use super::generic::{FdWrapper, Generic};
use crate::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};

// Code taken from async-signal
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 {
/// Returns the signal number.
fn as_nix(self) -> nix::sys::signal::Signal {
match self {
$(
Signal::$value => nix::sys::signal::Signal::$value,
)*
}
}

/// Generate from a signal number.
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! {
// Copied from https://github.com/bytecodealliance/rustix/blob/main/src/backend/linux_raw/process/types.rs#L81-L161

/// The signal types that we are able to listen for.
pub enum Signal {
/// `SIGHUP`
SIGHUP,
/// `SIGINT`
SIGINT,
/// `SIGQUIT`
SIGQUIT,
/// `SIGILL`
SIGILL,
/// `SIGTRAP`
SIGTRAP,
/// `SIGABRT`, aka `SIGIOT`
#[doc(alias = "Iot")]
#[doc(alias = "Abrt")]
SIGABRT,
/// `SIGBUS`
SIGBUS,
/// `SIGFPE`
SIGFPE,
/// `SIGKILL`
SIGKILL,
/// `SIGUSR1`
SIGUSR1,
/// `SIGSEGV`
SIGSEGV,
/// `SIGUSR2`
SIGUSR2,
/// `SIGPIPE`
SIGPIPE,
/// `SIGALRM`
#[doc(alias = "Alrm")]
SIGALRM,
/// `SIGTERM`
SIGTERM,
/// `SIGCHLD`
#[doc(alias = "Chld")]
SIGCHLD,
/// `SIGCONT`
SIGCONT,
/// `SIGSTOP`
SIGSTOP,
/// `SIGTSTP`
SIGTSTP,
/// `SIGTTIN`
SIGTTIN,
/// `SIGTTOU`
SIGTTOU,
/// `SIGURG`
SIGURG,
/// `SIGXCPU`
SIGXCPU,
/// `SIGXFSZ`
SIGXFSZ,
/// `SIGVTALRM`
#[doc(alias = "Vtalrm")]
SIGVTALRM,
/// `SIGPROF`
SIGPROF,
/// `SIGWINCH`
SIGWINCH,
/// `SIGIO`, aka `SIGPOLL`
#[doc(alias = "Poll")]
SIGIO,
/// `SIGSYS`, aka `SIGUNUSED`
#[doc(alias = "Unused")]
SIGSYS,
}
}

/// An event generated by the signal event source
#[derive(Copy, Clone, Debug)]
pub struct Event {
info: siginfo,
}

macro_rules! inner_accessors {
($($name:ident => $sname:ident: $ty:ty),*) => {$(
/// Gets the `
#[doc = stringify!($sname)]
/// ` field.
pub fn $name(&self) -> $ty {
use core::convert::TryInto;
self.info.$sname.try_into().unwrap()
}
)*}
}

impl Event {
/// Retrieve the signal number that was receive
pub fn signal(&self) -> Signal {
Signal::try_from(self.info.ssi_signo as c_int).unwrap()
Signal::from_num(self.info.ssi_signo as c_int)
}

/// Access the full `siginfo_t` associated with this signal event
pub fn full_info(&self) -> siginfo {
self.info
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
}
}

Expand All @@ -52,7 +189,7 @@ impl Signals {
pub fn new(signals: &[Signal]) -> crate::Result<Signals> {
let mut mask = SigSet::empty();
for &s in signals {
mask.add(s);
mask.add(s.as_nix());
}

// Mask the signals for this thread
Expand All @@ -73,7 +210,7 @@ impl Signals {
/// have still been changed.
pub fn add_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
for &s in signals {
self.mask.add(s);
self.mask.add(s.as_nix());
}
self.mask.thread_block().map_err(IoError::from)?;

Expand All @@ -94,8 +231,8 @@ impl Signals {
pub fn remove_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
let mut removed = SigSet::empty();
for &s in signals {
self.mask.remove(s);
removed.add(s);
self.mask.remove(s.as_nix());
removed.add(s.as_nix());
}
removed.thread_unblock().map_err(IoError::from)?;

Expand All @@ -116,7 +253,7 @@ impl Signals {
pub fn set_signals(&mut self, signals: &[Signal]) -> crate::Result<()> {
let mut new_mask = SigSet::empty();
for &s in signals {
new_mask.add(s);
new_mask.add(s.as_nix());
}

self.mask.thread_unblock().map_err(IoError::from)?;
Expand Down
8 changes: 4 additions & 4 deletions tests/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ mod test {
.unwrap();

// send ourselves a SIGUSR1
kill(Pid::this(), Signal::SIGUSR1).unwrap();
kill(Pid::this(), nix::sys::signal::Signal::SIGUSR1).unwrap();

event_loop
.dispatch(Some(Duration::from_millis(10)), &mut signal_received)
Expand Down Expand Up @@ -79,7 +79,7 @@ mod test {
.unwrap();

// send ourselves a SIGUSR2
kill(Pid::this(), Signal::SIGUSR2).unwrap();
kill(Pid::this(), nix::sys::signal::Signal::SIGUSR2).unwrap();

event_loop
.dispatch(Some(Duration::from_millis(10)), &mut signal_received)
Expand Down Expand Up @@ -110,11 +110,11 @@ mod test {

// block sigusr2 anyway, to not be killed by it
let mut set = SigSet::empty();
set.add(Signal::SIGUSR2);
set.add(nix::sys::signal::Signal::SIGUSR2);
set.thread_block().unwrap();

// send ourselves a SIGUSR2
kill(Pid::this(), Signal::SIGUSR2).unwrap();
kill(Pid::this(), nix::sys::signal::Signal::SIGUSR2).unwrap();

event_loop
.dispatch(Some(Duration::from_millis(10)), &mut signal_received)
Expand Down

0 comments on commit a3a4941

Please sign in to comment.