Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+283
View File
@@ -0,0 +1,283 @@
//! Module for actions setting flags.
//!
//! This contains helper functions to set flags whenever a signal happens. The flags are atomic
//! bools or numbers and the library manipulates them with the `SeqCst` ordering, in case someone
//! cares about relative order to some *other* atomic variables. If you don't care about the
//! relative order, you are free to use `Ordering::Relaxed` when reading and resetting the flags.
//!
//! # When to use
//!
//! The flags in this module allow for polling if a signal arrived since the previous poll. The do
//! not allow blocking until something arrives.
//!
//! Therefore, the natural way to use them is in applications that have some kind of iterative work
//! with both some upper and lower time limit on one iteration. If one iteration could block for
//! arbitrary time, the handling of the signal would be postponed for a long time. If the iteration
//! didn't block at all, the checking for the signal would turn into a busy-loop.
//!
//! If what you need is blocking until a signal comes, you might find better tools in the
//! [`pipe`][crate::low_level::pipe] and [`iterator`][crate::iterator] modules.
//!
//! # Examples
//!
//! Doing something until terminated. This also knows by which signal it was terminated. In case
//! multiple termination signals arrive before it is handled, it recognizes the last one.
//!
//! ```rust
//! use std::io::Error;
//! use std::sync::Arc;
//! use std::sync::atomic::{AtomicUsize, Ordering};
//!
//! use signal_hook::consts::signal::*;
//! use signal_hook::flag as signal_flag;
//!
//! fn main() -> Result<(), Error> {
//! let term = Arc::new(AtomicUsize::new(0));
//! const SIGTERM_U: usize = SIGTERM as usize;
//! const SIGINT_U: usize = SIGINT as usize;
//! # #[cfg(not(windows))]
//! const SIGQUIT_U: usize = SIGQUIT as usize;
//! signal_flag::register_usize(SIGTERM, Arc::clone(&term), SIGTERM_U)?;
//! signal_flag::register_usize(SIGINT, Arc::clone(&term), SIGINT_U)?;
//! # #[cfg(not(windows))]
//! signal_flag::register_usize(SIGQUIT, Arc::clone(&term), SIGQUIT_U)?;
//!
//! # // Hack to terminate the example when run as a doc-test.
//! # term.store(SIGTERM_U, Ordering::Relaxed);
//! loop {
//! match term.load(Ordering::Relaxed) {
//! 0 => {
//! // Do some useful stuff here
//! }
//! SIGTERM_U => {
//! eprintln!("Terminating on the TERM signal");
//! break;
//! }
//! SIGINT_U => {
//! eprintln!("Terminating on the INT signal");
//! break;
//! }
//! # #[cfg(not(windows))]
//! SIGQUIT_U => {
//! eprintln!("Terminating on the QUIT signal");
//! break;
//! }
//! _ => unreachable!(),
//! }
//! }
//!
//! Ok(())
//! }
//! ```
//!
//! Sending a signal to self and seeing it arrived (not of a practical usage on itself):
//!
//! ```rust
//! use std::io::Error;
//! use std::sync::Arc;
//! use std::sync::atomic::{AtomicBool, Ordering};
//! use std::thread;
//! use std::time::Duration;
//!
//! use signal_hook::consts::signal::*;
//! use signal_hook::low_level::raise;
//!
//! fn main() -> Result<(), Error> {
//! let got = Arc::new(AtomicBool::new(false));
//! # #[cfg(not(windows))]
//! signal_hook::flag::register(SIGUSR1, Arc::clone(&got))?;
//! # #[cfg(windows)]
//! # signal_hook::flag::register(SIGTERM, Arc::clone(&got))?;
//! # #[cfg(not(windows))]
//! raise(SIGUSR1).unwrap();
//! # #[cfg(windows)]
//! # raise(SIGTERM).unwrap();
//! // A sleep here, because it could run the signal handler in another thread and we may not
//! // see the flag right away. This is still a hack and not guaranteed to work, it is just an
//! // example!
//! thread::sleep(Duration::from_secs(1));
//! assert!(got.load(Ordering::Relaxed));
//! Ok(())
//! }
//! ```
//!
//! Reloading a configuration on `SIGHUP` (which is a common behaviour of many UNIX daemons,
//! together with reopening the log file).
//!
//! ```rust
//! use std::io::Error;
//! use std::sync::Arc;
//! use std::sync::atomic::{AtomicBool, Ordering};
//!
//! use signal_hook::consts::signal::*;
//! use signal_hook::flag as signal_flag;
//!
//! fn main() -> Result<(), Error> {
//! // We start with true, to load the configuration in the very first iteration too.
//! let reload = Arc::new(AtomicBool::new(true));
//! let term = Arc::new(AtomicBool::new(false));
//! # #[cfg(not(windows))]
//! signal_flag::register(SIGHUP, Arc::clone(&reload))?;
//! signal_flag::register(SIGINT, Arc::clone(&term))?;
//! signal_flag::register(SIGTERM, Arc::clone(&term))?;
//! # #[cfg(not(windows))]
//! signal_flag::register(SIGQUIT, Arc::clone(&term))?;
//! while !term.load(Ordering::Relaxed) {
//! // Using swap here, not load, to reset it back to false once it is reloaded.
//! if reload.swap(false, Ordering::Relaxed) {
//! // Reload the config here
//! #
//! # // Hiden hack to make the example terminate when run as doc-test. Not part of the
//! # // real code.
//! # term.store(true, Ordering::Relaxed);
//! }
//! // Serve one request
//! }
//! Ok(())
//! }
//! ```
use std::io::Error;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use libc::{c_int, EINVAL};
use crate::{low_level, SigId};
/// Registers an action to set the flag to `true` whenever the given signal arrives.
///
/// # Panics
///
/// If the signal is one of the forbidden.
pub fn register(signal: c_int, flag: Arc<AtomicBool>) -> Result<SigId, Error> {
// We use SeqCst for two reasons:
// * Signals should not come very often, so the performance does not really matter.
// * We promise the order of actions, but setting different atomics with Relaxed or similar
// would not guarantee the effective order.
unsafe { low_level::register(signal, move || flag.store(true, Ordering::SeqCst)) }
}
/// Registers an action to set the flag to the given value whenever the signal arrives.
pub fn register_usize(signal: c_int, flag: Arc<AtomicUsize>, value: usize) -> Result<SigId, Error> {
unsafe { low_level::register(signal, move || flag.store(value, Ordering::SeqCst)) }
}
/// Terminate the application on a signal if the given condition is true.
///
/// This can be used for different use cases. One of them (with the condition being always true) is
/// just unconditionally terminate on the given signal.
///
/// Another is being able to turn on and off the behaviour by the shared flag.
///
/// The last one is handling double CTRL+C ‒ if the user presses CTRL+C, we would like to start a
/// graceful shutdown. But if anything ever gets stuck in the shutdown, second CTRL+C (or other
/// such termination signal) should terminate the application without further delay.
///
/// To do that, one can combine this with [`register`]. On the first run, the flag is `false` and
/// this doesn't terminate. But then the flag is set to true during the first run and „arms“ the
/// shutdown on the second run. Note that it matters in which order the actions are registered (the
/// shutdown must go first). And yes, this also allows asking the user „Do you want to terminate“
/// and disarming the abrupt shutdown if the user answers „No“.
///
/// # Panics
///
/// If the signal is one of the forbidden.
pub fn register_conditional_shutdown(
signal: c_int,
status: c_int,
condition: Arc<AtomicBool>,
) -> Result<SigId, Error> {
let action = move || {
if condition.load(Ordering::SeqCst) {
low_level::exit(status);
}
};
unsafe { low_level::register(signal, action) }
}
/// Conditionally runs an emulation of the default action on the given signal.
///
/// If the provided condition is true at the time of invoking the signal handler, the equivalent of
/// the default action of the given signal is run. It is a bit similar to
/// [`register_conditional_shutdown`], except that it doesn't terminate for non-termination
/// signals, it runs their default handler.
///
/// # Panics
///
/// If the signal is one of the forbidden
///
/// # Errors
///
/// Similarly to the [`emulate_default_handler`][low_level::emulate_default_handler] function, this
/// one looks the signal up in a table. If it is unknown, an error is returned.
///
/// Additionally to that, any errors that can be caused by a registration of a handler can happen
/// too.
pub fn register_conditional_default(
signal: c_int,
condition: Arc<AtomicBool>,
) -> Result<SigId, Error> {
// Verify we know about this particular signal.
low_level::signal_name(signal).ok_or_else(|| Error::from_raw_os_error(EINVAL))?;
let action = move || {
if condition.load(Ordering::SeqCst) {
let _ = low_level::emulate_default_handler(signal);
}
};
unsafe { low_level::register(signal, action) }
}
#[cfg(test)]
mod tests {
use std::sync::atomic;
use std::time::{Duration, Instant};
use super::*;
use crate::consts::signal::*;
fn self_signal() {
#[cfg(not(windows))]
const SIG: c_int = SIGUSR1;
#[cfg(windows)]
const SIG: c_int = SIGTERM;
crate::low_level::raise(SIG).unwrap();
}
fn wait_flag(flag: &AtomicBool) -> bool {
let start = Instant::now();
while !flag.load(Ordering::Relaxed) {
// Replaced by hint::spin_loop, but we want to support older compiler
#[allow(deprecated)]
atomic::spin_loop_hint();
if Instant::now() - start > Duration::from_secs(1) {
// We reached a timeout and nothing happened yet.
// In theory, using timeouts for thread-synchronization tests is wrong, but a
// second should be enough in practice.
return false;
}
}
true
}
#[test]
fn register_unregister() {
// When we register the action, it is active.
let flag = Arc::new(AtomicBool::new(false));
#[cfg(not(windows))]
let signal = register(SIGUSR1, Arc::clone(&flag)).unwrap();
#[cfg(windows)]
let signal = register(crate::SIGTERM, Arc::clone(&flag)).unwrap();
self_signal();
assert!(wait_flag(&flag));
// But stops working after it is unregistered.
assert!(crate::low_level::unregister(signal));
flag.store(false, Ordering::Relaxed);
self_signal();
assert!(!wait_flag(&flag));
// And the unregistration actually dropped its copy of the Arc
assert_eq!(1, Arc::strong_count(&flag));
}
// The shutdown is tested in tests/shutdown.rs
}
+494
View File
@@ -0,0 +1,494 @@
//! A backend module for implementing the iterator like
//! [`iterator`][crate::iterator] module and the asynchronous
//! adapter crates.
//!
//! This module contains generic types which abstract over the concrete
//! IO type for the self-pipe. The motivation for having this abstraction
//! are the adapter crates for different asynchronous runtimes. The runtimes
//! provide their own wrappers for [`std::os::unix::net::UnixStream`]
//! which should be used as the internal self pipe. But large parts of the
//! remaining functionality doesn't depend directly onto the IO type and can
//! be reused.
//!
//! See also the [`SignalDelivery::with_pipe`] method for more information
//! about requirements the IO types have to fulfill.
//!
//! As a regular user you shouldn't need to use the types in this module.
//! Use the [`Signals`][crate::iterator::Signals] struct or one of the types
//! contained in the adapter libraries instead.
use std::borrow::{Borrow, BorrowMut};
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::io::Error;
use std::mem::MaybeUninit;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use libc::{self, c_int};
use super::exfiltrator::Exfiltrator;
use crate::low_level::pipe::{self, WakeMethod};
use crate::SigId;
/// Maximal signal number we support.
const MAX_SIGNUM: usize = 128;
trait SelfPipeWrite: Debug + Send + Sync {
fn wake_readers(&self);
}
impl<W: AsRawFd + Debug + Send + Sync> SelfPipeWrite for W {
fn wake_readers(&self) {
pipe::wake(self.as_raw_fd(), WakeMethod::Send);
}
}
#[derive(Debug)]
struct DeliveryState {
closed: AtomicBool,
registered_signal_ids: Mutex<Vec<Option<SigId>>>,
}
impl DeliveryState {
fn new() -> Self {
let ids = (0..MAX_SIGNUM).map(|_| None).collect();
Self {
closed: AtomicBool::new(false),
registered_signal_ids: Mutex::new(ids),
}
}
}
impl Drop for DeliveryState {
fn drop(&mut self) {
let lock = self.registered_signal_ids.lock().unwrap();
for id in lock.iter().filter_map(|s| *s) {
crate::low_level::unregister(id);
}
}
}
struct PendingSignals<E: Exfiltrator> {
exfiltrator: E,
slots: [E::Storage; MAX_SIGNUM],
}
impl<E: Exfiltrator> PendingSignals<E> {
fn new(exfiltrator: E) -> Self {
// Unfortunately, Default is not implemented for long arrays :-(
//
// Note that if the default impl panics, the already existing instances are leaked.
let mut slots = MaybeUninit::<[E::Storage; MAX_SIGNUM]>::uninit();
for i in 0..MAX_SIGNUM {
unsafe {
let slot: *mut E::Storage = slots.as_mut_ptr() as *mut _;
let slot = slot.add(i);
ptr::write(slot, E::Storage::default());
}
}
Self {
exfiltrator,
slots: unsafe { slots.assume_init() },
}
}
}
/// An internal trait to hide adding new signals into a Handle behind a dynamic dispatch.
trait AddSignal: Debug + Send + Sync {
fn add_signal(
self: Arc<Self>,
write: Arc<dyn SelfPipeWrite>,
signal: c_int,
) -> Result<SigId, Error>;
}
// Implemented manually because 1.36.0 doesn't yet support Debug for [X; BIG_NUMBER].
impl<E: Exfiltrator> Debug for PendingSignals<E> {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
fmt.debug_struct("PendingSignals")
.field("exfiltrator", &self.exfiltrator)
// While the array does not, the slice does implement Debug
.field("slots", &&self.slots[..])
.finish()
}
}
impl<E: Exfiltrator> AddSignal for PendingSignals<E> {
fn add_signal(
self: Arc<Self>,
write: Arc<dyn SelfPipeWrite>,
signal: c_int,
) -> Result<SigId, Error> {
assert!(signal >= 0);
assert!(
(signal as usize) < MAX_SIGNUM,
"Signal number {} too large. If your OS really supports such signal, file a bug",
signal,
);
assert!(
self.exfiltrator.supports_signal(signal),
"Signal {} not supported by exfiltrator {:?}",
signal,
self.exfiltrator,
);
self.exfiltrator.init(&self.slots[signal as usize], signal);
let action = move |act: &_| {
let slot = &self.slots[signal as usize];
let ex = &self.exfiltrator;
ex.store(slot, signal, act);
write.wake_readers();
};
let id = unsafe { signal_hook_registry::register_sigaction(signal, action) }?;
Ok(id)
}
}
/// A struct to control an instance of an associated type
/// (like for example [`Signals`][super::Signals]).
///
/// It allows to register more signal handlers and to shutdown the signal
/// delivery. You can [`clone`][Handle::clone] this type which isn't a
/// very expensive operation. The cloned instances can be shared between
/// multiple threads.
#[derive(Debug, Clone)]
pub struct Handle {
pending: Arc<dyn AddSignal>,
write: Arc<dyn SelfPipeWrite>,
delivery_state: Arc<DeliveryState>,
}
impl Handle {
fn new<W>(write: W, pending: Arc<dyn AddSignal>) -> Self
where
W: 'static + SelfPipeWrite,
{
Self {
pending,
write: Arc::new(write),
delivery_state: Arc::new(DeliveryState::new()),
}
}
/// Registers another signal to the set watched by the associated instance.
///
/// # Notes
///
/// * This is safe to call concurrently from whatever thread.
/// * This is *not* safe to call from within a signal handler.
/// * If the signal number was already registered previously, this is a no-op.
/// * If this errors, the original set of signals is left intact.
///
/// # Panics
///
/// * If the given signal is [forbidden][crate::FORBIDDEN].
/// * If the signal number is negative or larger than internal limit. The limit should be
/// larger than any supported signal the OS supports.
/// * If the relevant [`Exfiltrator`] does not support this particular signal. The default
/// [`SignalOnly`] one supports all signals.
pub fn add_signal(&self, signal: c_int) -> Result<(), Error> {
let mut lock = self.delivery_state.registered_signal_ids.lock().unwrap();
// Already registered, ignoring
if lock[signal as usize].is_some() {
return Ok(());
}
let id = Arc::clone(&self.pending).add_signal(Arc::clone(&self.write), signal)?;
lock[signal as usize] = Some(id);
Ok(())
}
/// Closes the associated instance.
///
/// This is meant to signalize termination of the signal delivery process.
/// After calling close:
///
/// * [`is_closed`][Handle::is_closed] will return true.
/// * All currently blocking operations of associated instances
/// are interrupted and terminate.
/// * Any further operations will not block.
/// * Further signals may or may not be returned from the iterators. However, if any are
/// returned, these are real signals that happened.
///
/// The goal is to be able to shut down any background thread that handles only the signals.
pub fn close(&self) {
self.delivery_state.closed.store(true, Ordering::SeqCst);
self.write.wake_readers();
}
/// Is it closed?
///
/// See [`close`][Handle::close].
pub fn is_closed(&self) -> bool {
self.delivery_state.closed.load(Ordering::SeqCst)
}
}
/// A struct for delivering received signals to the main program flow.
/// The self-pipe IO type is generic. See the
/// [`with_pipe`][SignalDelivery::with_pipe] method for requirements
/// for the IO type.
#[derive(Debug)]
pub struct SignalDelivery<R, E: Exfiltrator> {
read: R,
handle: Handle,
pending: Arc<PendingSignals<E>>,
}
impl<R, E: Exfiltrator> SignalDelivery<R, E>
where
R: 'static + AsRawFd + Send + Sync,
{
/// Creates the `SignalDelivery` structure.
///
/// The read and write arguments must be the ends of a suitable pipe type. These are used
/// for communication between the signal handler and main program flow.
///
/// Registers all the signals listed. The same restrictions (panics, errors) apply as with
/// [`add_signal`][Handle::add_signal].
///
/// # Requirements for the pipe type
///
/// * Must support [`send`](https://man7.org/linux/man-pages/man2/send.2.html) for
/// asynchronously writing bytes to the write end
/// * Must support [`recv`](https://man7.org/linux/man-pages/man2/recv.2.html) for
/// reading bytes from the read end
///
/// So UnixStream is a good choice for this.
pub fn with_pipe<I, S, W>(read: R, write: W, exfiltrator: E, signals: I) -> Result<Self, Error>
where
I: IntoIterator<Item = S>,
S: Borrow<c_int>,
W: 'static + AsRawFd + Debug + Send + Sync,
{
let pending = Arc::new(PendingSignals::new(exfiltrator));
let pending_add_signal = Arc::clone(&pending);
let handle = Handle::new(write, pending_add_signal);
let me = Self {
read,
handle,
pending,
};
for sig in signals {
me.handle.add_signal(*sig.borrow())?;
}
Ok(me)
}
/// Get a reference to the read end of the self pipe
///
/// You may use this method to register the underlying file descriptor
/// with an eventing system (e. g. epoll) to get notified if there are
/// bytes in the pipe. If the event system reports the file descriptor
/// ready for reading you can then call [`pending`][SignalDelivery::pending]
/// to get the arrived signals.
pub fn get_read(&self) -> &R {
&self.read
}
/// Get a mutable reference to the read end of the self pipe
///
/// See the [`get_read`][SignalDelivery::get_read] method for some additional
/// information.
pub fn get_read_mut(&mut self) -> &mut R {
&mut self.read
}
/// Drains all data from the internal self-pipe. This method will never block.
fn flush(&mut self) {
const SIZE: usize = 1024;
let mut buff = [0u8; SIZE];
unsafe {
// Draining the data in the self pipe. We ignore all errors on purpose. This
// should not be something like closed file descriptor. It could EAGAIN, but
// that's OK in case we say MSG_DONTWAIT. If it's EINTR, then it's OK too,
// it'll only create a spurious wakeup.
#[cfg(target_os = "aix")]
let nowait_flag = libc::MSG_NONBLOCK;
#[cfg(not(target_os = "aix"))]
let nowait_flag = libc::MSG_DONTWAIT;
while libc::recv(
self.read.as_raw_fd(),
buff.as_mut_ptr() as *mut libc::c_void,
SIZE,
nowait_flag,
) > 0
{}
}
}
/// Returns an iterator of already received signals.
///
/// This returns an iterator over all the signal numbers of the signals received since last
/// time they were read (out of the set registered by this `SignalDelivery` instance). Note
/// that they are returned in arbitrary order and a signal number is returned only once even
/// if it was received multiple times.
///
/// This method returns immediately (does not block) and may produce an empty iterator if
/// there are no signals ready.
pub fn pending(&mut self) -> Pending<E> {
self.flush();
Pending::new(Arc::clone(&self.pending))
}
/// Checks the reading end of the self pipe for available signals.
///
/// If there are no signals available or this instance was already closed it returns
/// [`Option::None`]. If there are some signals it returns a [`Pending`]
/// instance wrapped inside a [`Option::Some`]. However, due to implementation details,
/// this still can produce an empty iterator.
///
/// This method doesn't check the reading end by itself but uses the passed in callback.
/// This method blocks if and only if the callback blocks trying to read some bytes.
pub fn poll_pending<F>(&mut self, has_signals: &mut F) -> Result<Option<Pending<E>>, Error>
where
F: FnMut(&mut R) -> Result<bool, Error>,
{
if self.handle.is_closed() {
return Ok(None);
}
match has_signals(self.get_read_mut()) {
Ok(false) => Ok(None),
Ok(true) => Ok(Some(self.pending())),
Err(err) => Err(err),
}
}
/// Get a [`Handle`] for this `SignalDelivery` instance.
///
/// This can be used to add further signals or close the whole
/// signal delivery mechanism.
pub fn handle(&self) -> Handle {
self.handle.clone()
}
}
/// The iterator of one batch of signals.
///
/// This is returned by the [`pending`][SignalDelivery::pending] method.
#[derive(Debug)]
pub struct Pending<E: Exfiltrator> {
pending: Arc<PendingSignals<E>>,
position: usize,
}
impl<E: Exfiltrator> Pending<E> {
fn new(pending: Arc<PendingSignals<E>>) -> Self {
Self {
pending,
position: 0,
}
}
}
impl<E: Exfiltrator> Iterator for Pending<E> {
type Item = E::Output;
fn next(&mut self) -> Option<E::Output> {
while self.position < self.pending.slots.len() {
let sig = self.position;
let slot = &self.pending.slots[sig];
let result = self.pending.exfiltrator.load(slot, sig as c_int);
if result.is_some() {
return result;
} else {
self.position += 1;
}
}
None
}
}
/// Possible results of the [`poll_signal`][SignalIterator::poll_signal] function.
pub enum PollResult<O> {
/// A signal arrived
Signal(O),
/// There are no signals yet but there may arrive some in the future
Pending,
/// The iterator was closed. There won't be any signals reported from now on.
Closed,
/// An error happened during polling for arrived signals.
Err(Error),
}
/// An infinite iterator of received signals.
pub struct SignalIterator<SD, E: Exfiltrator> {
signals: SD,
iter: Pending<E>,
}
impl<SD, E: Exfiltrator> SignalIterator<SD, E> {
/// Create a new infinite iterator for signals registered with the passed
/// in [`SignalDelivery`] instance.
pub fn new<R>(mut signals: SD) -> Self
where
SD: BorrowMut<SignalDelivery<R, E>>,
R: 'static + AsRawFd + Send + Sync,
{
let iter = signals.borrow_mut().pending();
Self { signals, iter }
}
/// Return a signal if there is one or tell the caller that there is none at the moment.
///
/// You have to pass in a callback which checks the underlying reading end of the pipe if
/// there may be any pending signals. This callback may or may not block. If the callback
/// returns [`true`] this method will try to fetch the next signal and return it as a
/// [`PollResult::Signal`]. If the callback returns [`false`] the method will return
/// [`PollResult::Pending`] and assume it will be called again at a later point in time.
/// The callback may be called any number of times by this function.
///
/// If the iterator was closed by the [`close`][Handle::close] method of the associated
/// [`Handle`] this method will return [`PollResult::Closed`].
pub fn poll_signal<R, F>(&mut self, has_signals: &mut F) -> PollResult<E::Output>
where
SD: BorrowMut<SignalDelivery<R, E>>,
R: 'static + AsRawFd + Send + Sync,
F: FnMut(&mut R) -> Result<bool, Error>,
{
// The loop is necessary because it is possible that a signal was already consumed
// by a previous pending iterator due to the asynchronous nature of signals and
// always moving to the end of the iterator before calling has_more.
while !self.signals.borrow_mut().handle.is_closed() {
if let Some(result) = self.iter.next() {
return PollResult::Signal(result);
}
match self.signals.borrow_mut().poll_pending(has_signals) {
Ok(Some(pending)) => self.iter = pending,
Ok(None) => return PollResult::Pending,
Err(err) => return PollResult::Err(err),
}
}
PollResult::Closed
}
/// Get a shareable [`Handle`] for this instance.
///
/// This can be used to add further signals or terminate the whole
/// signal iteration using the [`close`][Handle::close] method.
pub fn handle<R>(&self) -> Handle
where
SD: Borrow<SignalDelivery<R, E>>,
R: 'static + AsRawFd + Send + Sync,
{
self.signals.borrow().handle()
}
}
/// A signal iterator which consumes a [`SignalDelivery`] instance and takes
/// ownership of it.
pub type OwningSignalIterator<R, E> = SignalIterator<SignalDelivery<R, E>, E>;
/// A signal iterator which takes a mutable reference to a [`SignalDelivery`]
/// instance.
pub type RefSignalIterator<'a, R, E> = SignalIterator<&'a mut SignalDelivery<R, E>, E>;
@@ -0,0 +1,152 @@
//! An abstraction over exfiltrating information out of signal handlers.
//!
//! The [`Exfiltrator`] trait provides a way to abstract the information extracted from a signal
//! handler and the way it is extracted out of it.
//!
//! The implementations can be used to parametrize the
//! [`SignalsInfo`][crate::iterator::SignalsInfo] to specify what results are returned.
//!
//! # Sealed
//!
//! Currently, the trait is sealed and all methods hidden. This is likely temporary, until some
//! experience with them is gained.
#[cfg(feature = "extended-siginfo")]
#[cfg_attr(docsrs, doc(cfg(feature = "extended-siginfo")))]
pub mod origin;
pub mod raw;
#[cfg(feature = "extended-siginfo")]
pub use origin::WithOrigin;
pub use raw::WithRawSiginfo;
use std::sync::atomic::{AtomicBool, Ordering};
use libc::{c_int, siginfo_t};
mod sealed {
use std::fmt::Debug;
use libc::{c_int, siginfo_t};
/// The actual implementation of the [`Exfiltrator`][super::Exfiltrator].
///
/// For now, this is hidden from the public API, but the intention is to move it to a public
/// place so users can implement it eventually, once we verify that it works well.
///
/// # Safety
///
/// The trait is unsafe as the [`Exfiltrator::store`] is called inside the signal handler and
/// must be async-signal-safe. Implementing this correctly may be difficult, therefore care
/// needs to be taken. One method known to work is encoding the data into an atomic variable.
/// Other, less limiting approaches, will be eventually explored.
pub unsafe trait Exfiltrator: Debug + Send + Sync + 'static {
/// One slot for storing the data.
///
/// Each signal will get its one slot of this type, independent of other signals. It can
/// store the information in there inside the signal handler and will be loaded from it in
/// load.
///
/// Each slot is initialized to the [`Default`] value. It is expected this value represents
/// „no signal delivered“ state.
type Storage: Debug + Default + Send + Sync + 'static;
/// The type returned to the user.
type Output;
/// If the given signal is supported by this specific exfiltrator.
///
/// Not all information is available to all signals, therefore not all exfiltrators must
/// support all signals. If `false` is returned, the user is prevented for registering such
/// signal number with the given exfiltrator.
fn supports_signal(&self, sig: c_int) -> bool;
/// Puts the signal information inside the slot.
///
/// It needs to somehow store the relevant information and the fact that a signal happened.
///
/// # Warning
///
/// This will be called inside the signal handler. It needs to be async-signal-safe. In
/// particular, very small amount of operations are allowed in there. This namely does
/// *not* include any locking nor allocation.
///
/// It is also possible that multiple store methods are called concurrently; it is up to
/// the implementor to deal with that.
fn store(&self, slot: &Self::Storage, signal: c_int, info: &siginfo_t);
/// Loads the signal information from the given slot.
///
/// The method shall check if the signal happened (it may be possible to be called without
/// the signal previously being delivered; it is up to the implementer to recognize it). It
/// is assumed the [`Default`] value is recognized as no signal delivered.
///
/// If it was delivered, the method shall extract the relevant information *and reset the
/// slot* to the no signal delivered state.
///
/// It shall return `Some(value)` if the signal was successfully received and `None` in
/// case no signal was delivered.
///
/// No blocking shall happen inside this method. It may be called concurrently with
/// [`store`][Exfiltrator::store] (due to how signals work, concurrently even inside the
/// same thread ‒ a `store` may „interrupt“ a call to `load`). It is up to the implementer
/// to deal with that.
fn load(&self, slot: &Self::Storage, signal: c_int) -> Option<Self::Output>;
/// Initialize the given slot for the given signal before the first use.
///
/// This is called before the first use of the given slot (and it is annotated with the
/// corresponding signal). The default does nothing, this is just an opportunity to
/// allocate data lazily (this is called outside of the signal handler, so it doesn't have
/// to be async-signal-safe). It will be called at most once for each slot.
///
/// Note that you can rely on this being called for correctness, but not for safety (this
/// crate calls it before the first use, but a user abusing the trait might not and in such
/// case it is OK to eg. lose signals, but not segfault).
fn init(&self, slot: &Self::Storage, signal: c_int) {
// Suppress unused variable warning without putting the underscores into public
// signature.
let _ = slot;
let _ = signal;
}
}
}
/// A trait describing what and how is extracted from signal handlers.
///
/// By choosing a specific implementor as the type parameter for
/// [`SignalsInfo`][crate::iterator::SignalsInfo], one can pick how much and what information is
/// returned from the iterator.
pub trait Exfiltrator: sealed::Exfiltrator {}
impl<E: sealed::Exfiltrator> Exfiltrator for E {}
/// An [`Exfiltrator`] providing just the signal numbers.
///
/// This is the basic exfiltrator for most needs. For that reason, there's the
/// [`crate::iterator::Signals`] type alias, to simplify the type names for usual needs.
#[derive(Clone, Copy, Debug, Default)]
pub struct SignalOnly;
unsafe impl sealed::Exfiltrator for SignalOnly {
type Storage = AtomicBool;
fn supports_signal(&self, _: c_int) -> bool {
true
}
type Output = c_int;
fn store(&self, slot: &Self::Storage, _: c_int, _: &siginfo_t) {
slot.store(true, Ordering::SeqCst);
}
fn load(&self, slot: &Self::Storage, signal: c_int) -> Option<Self::Output> {
if slot
.compare_exchange(true, false, Ordering::SeqCst, Ordering::Relaxed)
.is_ok()
{
Some(signal)
} else {
None
}
}
}
@@ -0,0 +1,66 @@
//! An exfiltrator providing the process that caused the signal.
//!
//! The [`WithOrigin`] is an [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that
//! provides the information about sending process in addition to the signal number, through the
//! [`Origin`] type.
//!
//! See the [`WithOrigin`] example.
use libc::{c_int, siginfo_t};
pub use super::raw::Slot;
use super::sealed::Exfiltrator;
use super::WithRawSiginfo;
pub use crate::low_level::siginfo::{Origin, Process};
/// The [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that produces [`Origin`] of
/// signals.
///
/// # Examples
///
/// ```rust
/// # use signal_hook::consts::SIGUSR1;
/// # use signal_hook::iterator::SignalsInfo;
/// # use signal_hook::iterator::exfiltrator::WithOrigin;
/// #
/// # fn main() -> Result<(), std::io::Error> {
/// // Subscribe to SIGUSR1, with information about the process.
/// let mut signals = SignalsInfo::<WithOrigin>::new(&[SIGUSR1])?;
///
/// // Send a signal to ourselves.
/// let my_pid = unsafe { libc::getpid() };
/// unsafe { libc::kill(my_pid, SIGUSR1) };
///
/// // Grab the signal and look into the details.
/// let received = signals.wait().next().unwrap();
///
/// assert_eq!(SIGUSR1, received.signal);
/// assert_eq!(my_pid, received.process.unwrap().pid);
/// # Ok(()) }
/// ```
#[derive(Copy, Clone, Debug, Default)]
pub struct WithOrigin(WithRawSiginfo);
// Safety: We need to be async-signal-safe. We delegate to other Exfiltrator, which already is and
// call a function that promises to be (Origin::extract)
unsafe impl Exfiltrator for WithOrigin {
type Storage = Slot;
type Output = Origin;
fn supports_signal(&self, signal: c_int) -> bool {
self.0.supports_signal(signal)
}
fn store(&self, slot: &Slot, signal: c_int, info: &siginfo_t) {
self.0.store(slot, signal, info)
}
fn load(&self, slot: &Self::Storage, signal: c_int) -> Option<Origin> {
self.0
.load(slot, signal)
.map(|info| unsafe { Origin::extract(&info) })
}
fn init(&self, slot: &Self::Storage, signal: c_int) {
self.0.init(slot, signal)
}
}
@@ -0,0 +1,95 @@
//! An exfiltrator providing the raw [`siginfo_t`].
// Note on unsafety in this module:
// * Implementing an unsafe trait, that one needs to ensure at least store is async-signal-safe.
// That's done by delegating to the Channel (and reading an atomic pointer, but that one is
// primitive op).
// * A bit of juggling with atomic and raw pointers. In effect, that is just late lazy
// initialization, the Slot is in line with Option would be, except that it is set atomically
// during the init. Lifetime is ensured by not dropping until the Drop of the whole slot and that
// is checked by taking `&mut self`.
use std::sync::atomic::{AtomicPtr, Ordering};
use libc::{c_int, siginfo_t};
use super::sealed::Exfiltrator;
use crate::low_level::channel::Channel;
#[doc(hidden)]
#[derive(Default, Debug)]
pub struct Slot(AtomicPtr<Channel<siginfo_t>>);
impl Drop for Slot {
fn drop(&mut self) {
let ptr = self.0.load(Ordering::Acquire);
if !ptr.is_null() {
drop(unsafe { Box::from_raw(ptr) });
}
}
}
/// The [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that produces the raw
/// [`libc::siginfo_t`]. Note that it might look differently on different OSes and its API is a
/// little bit more limited than its C counterpart.
///
/// You might prefer the [`WithOrigin`][super::WithOrigin] if you simply need information about the
/// origin of the signal.
///
/// # Examples
///
/// ```rust
/// # use signal_hook::consts::SIGUSR1;
/// # use signal_hook::iterator::SignalsInfo;
/// # use signal_hook::iterator::exfiltrator::WithRawSiginfo;
/// #
/// # fn main() -> Result<(), std::io::Error> {
/// // Subscribe to SIGUSR1, with information about the process.
/// let mut signals = SignalsInfo::<WithRawSiginfo>::new(&[SIGUSR1])?;
///
/// // Send ourselves a signal.
/// signal_hook::low_level::raise(SIGUSR1)?;
///
/// // Grab the signal and look into the details.
/// let received = signals.wait().next().unwrap();
///
/// // Not much else is useful in a cross-platform way :-(
/// assert_eq!(SIGUSR1, received.si_signo);
/// # Ok(()) }
/// ```
#[derive(Copy, Clone, Debug, Default)]
pub struct WithRawSiginfo;
unsafe impl Exfiltrator for WithRawSiginfo {
type Storage = Slot;
type Output = siginfo_t;
fn supports_signal(&self, _: c_int) -> bool {
true
}
fn store(&self, slot: &Slot, _: c_int, info: &siginfo_t) {
let info = *info;
// Condition just not to crash if someone forgot to call init.
//
// Lifetime is from init to our own drop, and drop needs &mut self.
if let Some(slot) = unsafe { slot.0.load(Ordering::Acquire).as_ref() } {
slot.send(info);
}
}
fn load(&self, slot: &Slot, _: libc::c_int) -> Option<siginfo_t> {
let slot = unsafe { slot.0.load(Ordering::Acquire).as_ref() };
// Condition just not to crash if someone forgot to call init.
slot.and_then(|s| s.recv())
}
fn init(&self, slot: &Self::Storage, _: c_int) {
let new = Box::default();
let old = slot.0.swap(Box::into_raw(new), Ordering::Release);
// We leak the pointer on purpose here. This is invalid state anyway and must not happen,
// but if it still does, we can't drop that while some other thread might still be having
// the raw pointer.
assert!(old.is_null(), "Init called multiple times");
}
}
+326
View File
@@ -0,0 +1,326 @@
//! An iterator over incoming signals.
//!
//! This provides a higher abstraction over the signals, providing
//! the [`SignalsInfo`] structure which is able to iterate over the
//! incoming signals. The structure is parametrized by an
//! [`Exfiltrator`][self::exfiltrator::Exfiltrator], which specifies what information is returned
//! for each delivered signal. Note that some exfiltrators are behind a feature flag.
//!
//! The [`Signals`] is a type alias for the common case when it is enough to get the signal number.
//!
//! This module (and everything in it) is turned by the `iterator` feature. It is **on** by
//! default, the possibility to turn off is mostly possible for very special purposes (compiling on
//! `<rustc-1.36`, minimizing the amount of code compiled, …). In a sense, this is the highest
//! level abstraction of the crate and the API expected to be used by most of the people.
//!
//! # Examples
//!
//! ```rust
//! extern crate libc;
//! extern crate signal_hook;
//!
//! use std::io::Error;
//!
//! use signal_hook::consts::signal::*;
//! use signal_hook::iterator::Signals;
//!
//! fn main() -> Result<(), Error> {
//! let mut signals = Signals::new(&[
//! SIGHUP,
//! SIGTERM,
//! SIGINT,
//! SIGQUIT,
//! # SIGUSR1,
//! ])?;
//! # // A trick to terminate the example when run as doc-test. Not part of the real code.
//! # signal_hook::low_level::raise(SIGUSR1).unwrap();
//! 'outer: loop {
//! // Pick up signals that arrived since last time
//! for signal in signals.pending() {
//! match signal as libc::c_int {
//! SIGHUP => {
//! // Reload configuration
//! // Reopen the log file
//! }
//! SIGTERM | SIGINT | SIGQUIT => {
//! break 'outer;
//! },
//! # SIGUSR1 => return Ok(()),
//! _ => unreachable!(),
//! }
//! }
//! // Do some bit of work ‒ something with upper limit on waiting, so we don't block
//! // forever with a SIGTERM already waiting.
//! }
//! println!("Terminating. Bye bye");
//! Ok(())
//! }
//! ```
pub mod backend;
pub mod exfiltrator;
use std::borrow::Borrow;
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::io::{Error, ErrorKind, Read};
use std::os::unix::net::UnixStream;
use libc::{self, c_int};
pub use self::backend::{Handle, Pending};
use self::backend::{PollResult, RefSignalIterator, SignalDelivery};
use self::exfiltrator::{Exfiltrator, SignalOnly};
/// The main structure of the module, representing interest in some signals.
///
/// Unlike the helpers in other modules, this registers the signals when created and unregisters
/// them on drop. It provides the pending signals during its lifetime, either in batches or as an
/// infinite iterator.
///
/// Most users will want to use it through the [`Signals`] type alias for simplicity.
///
/// # Multiple threads
///
/// Instances of this struct can be [sent][std::marker::Send] to other threads. In a multithreaded
/// application this can be used to dedicate a separate thread for signal handling. In this case
/// you should get a [`Handle`] using the [`handle`][Signals::handle] method before sending the
/// `Signals` instance to a background thread. With the handle you will be able to shut down the
/// background thread later, or to operatively add more signals.
///
/// The controller handle can be shared between as many threads as you like using its
/// [`clone`][Handle::clone] method.
///
/// # Exfiltrators
///
/// The [`SignalOnly`] provides only the signal number. There are further exfiltrators available in
/// the [`exfiltrator`] module. Note that some of them are behind feature flags that need to be
/// enabled.
///
/// # Examples
///
/// ```rust
/// # extern crate signal_hook;
/// #
/// # use std::io::Error;
/// # use std::thread;
/// use signal_hook::consts::signal::*;
/// use signal_hook::iterator::Signals;
///
/// #
/// # fn main() -> Result<(), Error> {
/// let mut signals = Signals::new(&[SIGUSR1, SIGUSR2])?;
/// let handle = signals.handle();
/// let thread = thread::spawn(move || {
/// for signal in &mut signals {
/// match signal {
/// SIGUSR1 => {},
/// SIGUSR2 => {},
/// _ => unreachable!(),
/// }
/// }
/// });
///
/// // Some time later...
/// handle.close();
/// thread.join().unwrap();
/// # Ok(())
/// # }
/// ```
pub struct SignalsInfo<E: Exfiltrator = SignalOnly>(SignalDelivery<UnixStream, E>);
impl<E: Exfiltrator> SignalsInfo<E> {
/// Creates the `Signals` structure.
///
/// This registers all the signals listed. The same restrictions (panics, errors) apply as
/// for the [`Handle::add_signal`] method.
pub fn new<I, S>(signals: I) -> Result<Self, Error>
where
I: IntoIterator<Item = S>,
S: Borrow<c_int>,
E: Default,
{
Self::with_exfiltrator(signals, E::default())
}
/// An advanced constructor with explicit [`Exfiltrator`].
pub fn with_exfiltrator<I, S>(signals: I, exfiltrator: E) -> Result<Self, Error>
where
I: IntoIterator<Item = S>,
S: Borrow<c_int>,
{
let (read, write) = UnixStream::pair()?;
Ok(SignalsInfo(SignalDelivery::with_pipe(
read,
write,
exfiltrator,
signals,
)?))
}
/// Registers another signal to the set watched by this [`Signals`] instance.
///
/// The same restrictions (panics, errors) apply as for the [`Handle::add_signal`]
/// method.
pub fn add_signal(&self, signal: c_int) -> Result<(), Error> {
self.handle().add_signal(signal)
}
/// Returns an iterator of already received signals.
///
/// This returns an iterator over all the signal numbers of the signals received since last
/// time they were read (out of the set registered by this `Signals` instance). Note that they
/// are returned in arbitrary order and a signal instance may returned only once even if it was
/// received multiple times.
///
/// This method returns immediately (does not block) and may produce an empty iterator if there
/// are no signals ready.
pub fn pending(&mut self) -> Pending<E> {
self.0.pending()
}
/// Block until the stream contains some bytes.
///
/// Returns true if it was possible to read a byte and false otherwise.
fn has_signals(read: &mut UnixStream) -> Result<bool, Error> {
loop {
match read.read(&mut [0u8]) {
Ok(num_read) => break Ok(num_read > 0),
// If we get an EINTR error it is fine to retry reading from the stream.
// Otherwise we should pass on the error to the caller.
Err(error) => {
if error.kind() != ErrorKind::Interrupted {
break Err(error);
}
}
}
}
}
/// Waits for some signals to be available and returns an iterator.
///
/// This is similar to [`pending`][SignalsInfo::pending]. If there are no signals available, it
/// tries to wait for some to arrive. However, due to implementation details, this still can
/// produce an empty iterator.
///
/// This can block for arbitrary long time. If the [`Handle::close`] method is used in
/// another thread this method will return immediately.
///
/// Note that the blocking is done in this method, not in the iterator.
pub fn wait(&mut self) -> Pending<E> {
match self.0.poll_pending(&mut Self::has_signals) {
Ok(Some(pending)) => pending,
// Because of the blocking has_signals method the poll_pending method
// only returns None if the instance is closed. But we want to return
// a possibly empty pending object anyway.
Ok(None) => self.pending(),
// Users can't manipulate the internal file descriptors and the way we use them
// shouldn't produce any errors. So it is OK to panic.
Err(error) => panic!("Unexpected error: {}", error),
}
}
/// Is it closed?
///
/// See [`close`][Handle::close].
pub fn is_closed(&self) -> bool {
self.handle().is_closed()
}
/// Get an infinite iterator over arriving signals.
///
/// The iterator's `next()` blocks as necessary to wait for signals to arrive. This is adequate
/// if you want to designate a thread solely to handling signals. If multiple signals come at
/// the same time (between two values produced by the iterator), they will be returned in
/// arbitrary order. Multiple instances of the same signal may be collated.
///
/// This is also the iterator returned by `IntoIterator` implementation on `&mut Signals`.
///
/// This iterator terminates only if explicitly [closed][Handle::close].
///
/// # Examples
///
/// ```rust
/// # extern crate libc;
/// # extern crate signal_hook;
/// #
/// # use std::io::Error;
/// # use std::thread;
/// #
/// use signal_hook::consts::signal::*;
/// use signal_hook::iterator::Signals;
///
/// # fn main() -> Result<(), Error> {
/// let mut signals = Signals::new(&[SIGUSR1, SIGUSR2])?;
/// let handle = signals.handle();
/// thread::spawn(move || {
/// for signal in signals.forever() {
/// match signal {
/// SIGUSR1 => {},
/// SIGUSR2 => {},
/// _ => unreachable!(),
/// }
/// }
/// });
/// handle.close();
/// # Ok(())
/// # }
/// ```
pub fn forever(&mut self) -> Forever<E> {
Forever(RefSignalIterator::new(&mut self.0))
}
/// Get a shareable handle to a [`Handle`] for this instance.
///
/// This can be used to add further signals or close the [`Signals`] instance.
pub fn handle(&self) -> Handle {
self.0.handle()
}
}
impl<E> Debug for SignalsInfo<E>
where
E: Debug + Exfiltrator,
E::Storage: Debug,
{
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
fmt.debug_tuple("Signals").field(&self.0).finish()
}
}
impl<'a, E: Exfiltrator> IntoIterator for &'a mut SignalsInfo<E> {
type Item = E::Output;
type IntoIter = Forever<'a, E>;
fn into_iter(self) -> Self::IntoIter {
self.forever()
}
}
/// An infinite iterator of arriving signals.
pub struct Forever<'a, E: Exfiltrator>(RefSignalIterator<'a, UnixStream, E>);
impl<'a, E: Exfiltrator> Iterator for Forever<'a, E> {
type Item = E::Output;
fn next(&mut self) -> Option<E::Output> {
loop {
match self.0.poll_signal(&mut SignalsInfo::<E>::has_signals) {
PollResult::Signal(result) => break Some(result),
PollResult::Closed => break None,
// In theory, the poll_signal should not return PollResult::Pending. Nevertheless,
// there's a race condition - if the other side closes the pipe/socket after
// checking for it being closed, then the `read` there returns 0 as EOF. That
// appears as pending here. Next time we should get Closed.
PollResult::Pending => continue,
// Users can't manipulate the internal file descriptors and the way we use them
// shouldn't produce any errors. So it is OK to panic.
PollResult::Err(error) => panic!("Unexpected error: {}", error),
}
}
}
}
/// A type alias for an iterator returning just the signal numbers.
///
/// This is the simplified version for most of the use cases. For advanced usages, the
/// [`SignalsInfo`] with explicit [`Exfiltrator`] type can be used.
pub type Signals = SignalsInfo<SignalOnly>;
+414
View File
@@ -0,0 +1,414 @@
#![doc(
test(attr(deny(warnings))),
test(attr(allow(bare_trait_objects, unknown_lints)))
)]
#![warn(missing_docs)]
// Don't fail on links to things not enabled in features
#![allow(
unknown_lints,
renamed_and_removed_lints,
intra_doc_link_resolution_failure,
broken_intra_doc_links
)]
// These little nifty labels saying that something needs a feature to be enabled
#![cfg_attr(docsrs, feature(doc_cfg))]
//! Library for easier and safe Unix signal handling
//!
//! Unix signals are inherently hard to handle correctly, for several reasons:
//!
//! * They are a global resource. If a library wants to set its own signal handlers, it risks
//! disrupting some other library. It is possible to chain the previous signal handler, but then
//! it is impossible to remove the old signal handlers from the chains in any practical manner.
//! * They can be called from whatever thread, requiring synchronization. Also, as they can
//! interrupt a thread at any time, making most handling race-prone.
//! * According to the POSIX standard, the set of functions one may call inside a signal handler is
//! limited to very few of them. To highlight, mutexes (or other locking mechanisms) and memory
//! allocation and deallocation is *not* allowed.
//!
//! # The goal of the library
//!
//! The aim is to subscriptions to signals a „structured“ resource, in a similar way memory
//! allocation is ‒ parts of the program can independently subscribe and it's the same part of the
//! program that can give them up, independently of what the other parts do. Therefore, it is
//! possible to register multiple actions to the same signal.
//!
//! Another goal is to shield applications away from differences between platforms. Various Unix
//! systems have little quirks and differences that need to be worked around and that's not
//! something every application should be dealing with. We even try to provide some support for
//! Windows, but we lack the expertise in that area, so that one is not complete and is a bit rough
//! (if you know how it works there and are willing to either contribute the code or consult,
//! please get in touch).
//!
//! Furthermore, it provides implementation of certain common signal-handling patterns, usable from
//! safe Rust, without the application author needing to learn about *all* the traps.
//!
//! Note that despite everything, there are still some quirks around signal handling that are not
//! possible to paper over and need to be considered. Also, there are some signal use cases that
//! are inherently unsafe and they are not covered by this crate.
//!
//! # Anatomy of the crate
//!
//! The crate is split into several modules.
//!
//! The easiest way to handle signals is using the [`Signals`][crate::iterator::Signals] iterator
//! thing. It can register for a set of signals and produce them one by one, in a blocking manner.
//! You can reserve a thread for handling them as they come. If you want something asynchronous,
//! there are adaptor crates for the most common asynchronous runtimes. The module also contains
//! ways to build iterators that produce a bit more information than just the signal number.
//!
//! The [`flag`] module contains routines to set a flag based on incoming signals and to do
//! certain actions inside the signal handlers based on the flags (the flags can also be
//! manipulated by the rest of the application). This allows building things like checking if a
//! signal happened on each loop iteration or making sure application shuts down on the second
//! CTRL+C if it got stuck in graceful shutdown requested by the first.
//!
//! The [`consts`] module contains some constants, most importantly the signal numbers themselves
//! (these are just re-exports from [`libc`] and if your OS has some extra ones, you can use them
//! too, this is just for convenience).
//!
//! And last, there is the [`low_level`] module. It contains routines to directly register and
//! unregister arbitrary actions. Some of the patterns in the above modules return a [`SigId`],
//! which can be used with the [`low_level::unregister`] to remove the action. There are also some
//! other utilities that are more suited to build other abstractions with than to use directly.
//!
//! Certain parts of the library can be enabled or disabled with use flags:
//!
//! * `channel`: The [low_level::channel] module (on by default).
//! * `iterator`: The [iterator] module (on by default).
//! * `extended-sig-info`: Support for providing more information in the iterators or from the
//! async adaptor crates. This is off by default.
//!
//! # Limitations
//!
//! * OS limitations still apply. Certain signals are not possible to override or subscribe to ‒
//! `SIGKILL` or `SIGSTOP`.
//! * Overriding some others is probably a very stupid idea (or very unusual needs) ‒ handling eg.
//! `SIGSEGV` is not something done lightly. For that reason, the crate will panic in case
//! registering of these is attempted (see [`FORBIDDEN`][crate::consts::FORBIDDEN]. If you still
//! need to do so, you can find such APIs in the `signal-hook-registry` backend crate, but
//! additional care must be taken.
//! * Interaction with other signal-handling libraries is limited. If signal-hook finds an existing
//! handler present, it chain-calls it from the signal it installs and assumes other libraries
//! would do the same, but that's everything that can be done to make it work with libraries not
//! based on [`signal-hook-registry`](https://lib.rs/signal-hook-registry)
//! (the backend of this crate).
//! * The above chaining contains a race condition in multi-threaded programs, where the previous
//! handler might not get called if it is received during the registration process. This is
//! handled (at least on non-windows platforms) on the same thread where the registration
//! happens, therefore it is advised to register at least one action for each signal of interest
//! early, before any additional threads are started. Registering any additional (or removing and
//! registering again) action on the same signal is without the race condition.
//! * Once at least one action is registered for a signal, the default action is replaced (this is
//! how signals work in the OS). Even if all actions of that signal are removed, `signal-hook`
//! does not restore the default handler (such behaviour would be at times inconsistent with
//! making the actions independent and there's no reasonable way to do so in a race-free way in a
//! multi-threaded program while also dealing with signal handlers registered with other
//! libraries). It is, however, possible to *emulate* the default handler (see the
//! [`emulate_default_handler`][low_level::emulate_default_handler]) ‒ there are only 4
//! default handlers:
//! - Ignore. This is easy to emulate.
//! - Abort. Depending on if you call it from within a signal handler of from outside, the
//! [`low_level::abort`] or [`std::process::abort`] can be used.
//! - Terminate. This can be done with `exit` ([`low_level::exit`] or [`std::process::exit`]).
//! - Stop. It is possible to [`raise`][low_level::raise] the [`SIGSTOP`][consts::SIGSTOP] signal.
//! That one can't be replaced and always stops the application.
//! * Many of the patterns here can collate multiple instances of the same signal into fewer
//! instances, if the application doesn't consume them fast enough. This is consistent with what
//! the kernel does if the application doesn't keep up with them (at least for non-realtime
//! signals, see below), so it is something one needs to deal with anyway.
//! * (By design) the library mostly _postpones_ or helps the user postpone acting on the signals
//! until later. This, in combination with the above collating inside the library may make it
//! unsuitable for realtime signals. These usually want to be handled directly inside the signal
//! handler ‒ which still can be done with [signal_hook_registry::register], but using unsafe and
//! due care. Patterns for working safely with realtime signals are not unwanted in the library,
//! but nobody contributed them yet.
//!
//! # Signal masks
//!
//! As the library uses `sigaction` under the hood, signal masking works as expected (eg. with
//! `pthread_sigmask`). This means, signals will *not* be delivered if the signal is masked in all
//! program's threads.
//!
//! By the way, if you do want to modify the signal mask (or do other Unix-specific magic), the
//! [nix](https://lib.rs/crates/nix) crate offers safe interface to many low-level functions,
//! including
//! [`pthread_sigmask`](https://docs.rs/nix/0.11.0/nix/sys/signal/fn.pthread_sigmask.html).
//!
//! # Portability
//!
//! It should work on any POSIX.1-2001 system, which are all the major big OSes with the notable
//! exception of Windows.
//!
//! Non-standard signals are also supported. Pass the signal value directly from `libc` or use
//! the numeric value directly.
//!
//! ```rust
//! use std::sync::Arc;
//! use std::sync::atomic::{AtomicBool};
//! let term = Arc::new(AtomicBool::new(false));
//! let _ = signal_hook::flag::register(libc::SIGINT, Arc::clone(&term));
//! ```
//!
//! This crate includes a limited support for Windows, based on `signal`/`raise` in the CRT.
//! There are differences in both API and behavior:
//!
//! - Many parts of the library are not available there.
//! - We have only a few signals: `SIGABRT`, `SIGABRT_COMPAT`, `SIGBREAK`,
//! `SIGFPE`, `SIGILL`, `SIGINT`, `SIGSEGV` and `SIGTERM`.
//! - Due to lack of signal blocking, there's a race condition.
//! After the call to `signal`, there's a moment where we miss a signal.
//! That means when you register a handler, there may be a signal which invokes
//! neither the default handler or the handler you register.
//! - Handlers registered by `signal` in Windows are cleared on first signal.
//! To match behavior in other platforms, we re-register the handler each time the handler is
//! called, but there's a moment where we miss a handler.
//! That means when you receive two signals in a row, there may be a signal which invokes
//! the default handler, nevertheless you certainly have registered the handler.
//!
//! Moreover, signals won't work as you expected. `SIGTERM` isn't actually used and
//! not all `Ctrl-C`s are turned into `SIGINT`.
//!
//! Patches to improve Windows support in this library are welcome.
//!
//! # Features
//!
//! There are several feature flags that control how much is available as part of the crate, some
//! enabled by default.
//!
//! * `channel`: (enabled by default) The [Channel][crate::low_level::channel] synchronization
//! primitive for exporting data out of signal handlers.
//! * `iterator`: (enabled by default) An [Signals iterator][crate::iterator::Signals] that
//! provides a convenient interface for receiving signals in rust-friendly way.
//! * `extended-siginfo` adds support for providing extra information as part of the iterator
//! interface.
//!
//! # Examples
//!
//! ## Using a flag to terminate a loop-based application
//!
//! ```rust
//! use std::io::Error;
//! use std::sync::Arc;
//! use std::sync::atomic::{AtomicBool, Ordering};
//!
//! fn main() -> Result<(), Error> {
//! let term = Arc::new(AtomicBool::new(false));
//! signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&term))?;
//! while !term.load(Ordering::Relaxed) {
//! // Do some time-limited stuff here
//! // (if this could block forever, then there's no guarantee the signal will have any
//! // effect).
//! #
//! # // Hack to terminate the example, not part of the real code.
//! # term.store(true, Ordering::Relaxed);
//! }
//! Ok(())
//! }
//! ```
//!
//! ## A complex signal handling with a background thread
//!
//! This also handles the double CTRL+C situation (eg. the second CTRL+C kills) and resetting the
//! terminal on `SIGTSTP` (CTRL+Z, curses-based applications should do something like this).
//!
//! ```rust
//! # #[cfg(feature = "extended-siginfo")] pub mod test {
//! use std::io::Error;
//! use std::sync::Arc;
//! use std::sync::atomic::AtomicBool;
//!
//! use signal_hook::consts::signal::*;
//! use signal_hook::consts::TERM_SIGNALS;
//! use signal_hook::flag;
//! // A friend of the Signals iterator, but can be customized by what we want yielded about each
//! // signal.
//! use signal_hook::iterator::SignalsInfo;
//! use signal_hook::iterator::exfiltrator::WithOrigin;
//! use signal_hook::low_level;
//!
//! # struct App;
//! # impl App {
//! # fn run_background() -> Self { Self }
//! # fn wait_for_stop(self) {}
//! # fn restore_term(&self) {}
//! # fn claim_term(&self) {}
//! # fn resize_term(&self) {}
//! # fn reload_config(&self) {}
//! # fn print_stats(&self) {}
//! # }
//! # pub
//! fn main() -> Result<(), Error> {
//! // Make sure double CTRL+C and similar kills
//! let term_now = Arc::new(AtomicBool::new(false));
//! for sig in TERM_SIGNALS {
//! // When terminated by a second term signal, exit with exit code 1.
//! // This will do nothing the first time (because term_now is false).
//! flag::register_conditional_shutdown(*sig, 1, Arc::clone(&term_now))?;
//! // But this will "arm" the above for the second time, by setting it to true.
//! // The order of registering these is important, if you put this one first, it will
//! // first arm and then terminate ‒ all in the first round.
//! flag::register(*sig, Arc::clone(&term_now))?;
//! }
//!
//! // Subscribe to all these signals with information about where they come from. We use the
//! // extra info only for logging in this example (it is not available on all the OSes or at
//! // all the occasions anyway, it may return `Unknown`).
//! let mut sigs = vec![
//! // Some terminal handling
//! SIGTSTP, SIGCONT, SIGWINCH,
//! // Reload of configuration for daemons ‒ um, is this example for a TUI app or a daemon
//! // O:-)? You choose...
//! SIGHUP,
//! // Application-specific action, to print some statistics.
//! SIGUSR1,
//! ];
//! sigs.extend(TERM_SIGNALS);
//! let mut signals = SignalsInfo::<WithOrigin>::new(&sigs)?;
//! # low_level::raise(SIGTERM)?; // Trick to terminate the example
//!
//! // This is the actual application that'll start in its own thread. We'll control it from
//! // this thread based on the signals, but it keeps running.
//! // This is called after all the signals got registered, to avoid the short race condition
//! // in the first registration of each signal in multi-threaded programs.
//! let app = App::run_background();
//!
//! // Consume all the incoming signals. This happens in "normal" Rust thread, not in the
//! // signal handlers. This means that we are allowed to do whatever we like in here, without
//! // restrictions, but it also means the kernel believes the signal already got delivered, we
//! // handle them in delayed manner. This is in contrast with eg the above
//! // `register_conditional_shutdown` where the shutdown happens *inside* the handler.
//! let mut has_terminal = true;
//! for info in &mut signals {
//! // Will print info about signal + where it comes from.
//! eprintln!("Received a signal {:?}", info);
//! match info.signal {
//! SIGTSTP => {
//! // Restore the terminal to non-TUI mode
//! if has_terminal {
//! app.restore_term();
//! has_terminal = false;
//! // And actually stop ourselves.
//! low_level::emulate_default_handler(SIGTSTP)?;
//! }
//! }
//! SIGCONT => {
//! if !has_terminal {
//! app.claim_term();
//! has_terminal = true;
//! }
//! }
//! SIGWINCH => app.resize_term(),
//! SIGHUP => app.reload_config(),
//! SIGUSR1 => app.print_stats(),
//! term_sig => { // These are all the ones left
//! eprintln!("Terminating");
//! assert!(TERM_SIGNALS.contains(&term_sig));
//! break;
//! }
//! }
//! }
//!
//! // If during this another termination signal comes, the trick at the top would kick in and
//! // terminate early. But if it doesn't, the application shuts down gracefully.
//! app.wait_for_stop();
//!
//! Ok(())
//! }
//! # }
//! # fn main() {
//! # #[cfg(feature = "extended-siginfo")] test::main().unwrap();
//! # }
//! ```
//!
//! # Asynchronous runtime support
//!
//! If you are looking for integration with an asynchronous runtime take a look at one of the
//! following adapter crates:
//!
//! * [`signal-hook-async-std`](https://docs.rs/signal-hook-async-std) for async-std support
//! * [`signal-hook-mio`](https://docs.rs/signal-hook-mio) for MIO support
//! * [`signal-hook-tokio`](https://docs.rs/signal-hook-tokio) for Tokio support
//!
//! Feel free to open a pull requests if you want to add support for runtimes not mentioned above.
//!
//! # Porting from previous versions
//!
//! There were some noisy changes when going from 0.2 version to the 0.3 version. In particular:
//!
//! * A lot of things moved around to make the structure of the crate a bit more understandable.
//! Most of the time it should be possible to just search the documentation for the name that
//! can't be resolved to discover the new location.
//! - The signal constants (`SIGTERM`, for example) are in [`consts`] submodule (individual
//! imports) and in the [`consts::signal`] (for wildcard import of all of them).
//! - Some APIs that are considered more of a low-level building blocks than for casual day to
//! day use are now in the [`low_level`] submodule.
//! * The previous version contained the `cleanup` module that allowed for removal of the actions
//! in rather destructive way (nuking actions of arbitrary other parts of the program). This is
//! completely gone in this version. The use case of shutting down the application on second
//! CTRL+C is now supported by a pattern described in the [`flag`] submodule. For other similar
//! needs, refer above for emulating default handlers.
pub mod flag;
#[cfg(all(not(windows), feature = "iterator"))]
#[cfg_attr(docsrs, doc(cfg(all(not(windows), feature = "iterator"))))]
pub mod iterator;
pub mod low_level;
/// The low-level constants.
///
/// Like the signal numbers.
pub mod consts {
use libc::c_int;
/// The signal constants.
///
/// Can be mass-imported by `use signal_hook::consts::signal::*`, without polluting the
/// namespace with other names. Also available in the [`consts`][crate::consts] directly (but
/// with more constants around).
pub mod signal {
#[cfg(not(windows))]
pub use libc::{
SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGKILL,
SIGPIPE, SIGPROF, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTERM, SIGTRAP, SIGTSTP,
SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH, SIGXCPU, SIGXFSZ,
};
#[cfg(not(any(windows, target_os = "haiku")))]
pub use libc::SIGIO;
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "macos"
))]
pub use libc::SIGINFO;
#[cfg(windows)]
pub use libc::{SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM};
// NOTE: they perhaps deserve backport to libc.
#[cfg(windows)]
/// Same as `SIGABRT`, but the number is compatible to other platforms.
pub const SIGABRT_COMPAT: libc::c_int = 6;
#[cfg(windows)]
/// Ctrl-Break is pressed for Windows Console processes.
pub const SIGBREAK: libc::c_int = 21;
}
pub use self::signal::*;
pub use signal_hook_registry::FORBIDDEN;
/// Various signals commonly requesting shutdown of an application.
#[cfg(not(windows))]
pub const TERM_SIGNALS: &[c_int] = &[SIGTERM, SIGQUIT, SIGINT];
/// Various signals commonly requesting shutdown of an application.
#[cfg(windows)]
pub const TERM_SIGNALS: &[c_int] = &[SIGTERM, SIGINT];
}
pub use signal_hook_registry::SigId;
@@ -0,0 +1,235 @@
//! A restricted channel to pass data from signal handler.
//!
//! When trying to communicate data from signal handler to the outside world, one can use an atomic
//! variable (as it doesn't lock, so it can be made async-signal-safe). But this won't work for
//! larger data.
//!
//! This module provides a channel that can be used for that purpose. It is used by certain
//! [exfiltrators][crate::iterator::exfiltrator], but can be used as building block for custom
//! actions. In general, this is not a ready-made end-user API.
//!
//! # How does it work
//!
//! Each channel has a fixed number of slots and two queues (one for empty slots, one for full
//! slots). A signal handler takes a slot out of the empty one, fills it and passes it into the
//! full one. Outside of signal handler, it can take the value out of the full queue and return the
//! slot to the empty queue.
//!
//! The queues are implemented as bit-encoded indexes of the slots in the storage. The bits are
//! stored in an atomic variable.
//!
//! Note that the algorithm allows for a slot to be in neither queue (when it is being emptied or
//! filled).
//!
//! # Fallible allocation of a slot
//!
//! It is apparent that allocation of a new slot can fail (there's nothing in the empty slot). In
//! such case, there's no way to send the new value out of the handler (there's no way to safely
//! wait for a slot to appear, because the handler can be blocking the thread that is responsible
//! for emptying them). But that's considered acceptable ‒ even the kernel collates the same kinds
//! of signals together if they are not consumed by application fast enough and there are no free
//! slots exactly because some are being filled, emptied or are full ‒ in particular, the whole
//! system will yield a signal.
//!
//! This assumes that separate signals don't share the same buffer and that there's only one reader
//! (using multiple readers is still safe, but it is possible that all slots would be inside the
//! readers, but already empty, so the above argument would not hold).
// TODO: Other sizes? Does anyone need more than 5 slots?
use std::cell::UnsafeCell;
use std::sync::atomic::{AtomicU16, Ordering};
const SLOTS: usize = 5;
const BITS: u16 = 3;
const MASK: u16 = 0b111;
fn get(n: u16, idx: u16) -> u16 {
(n >> (BITS * idx)) & MASK
}
fn set(n: u16, idx: u16, v: u16) -> u16 {
let v = v << (BITS * idx);
let mask = MASK << (BITS * idx);
(n & !mask) | v
}
fn enqueue(q: &AtomicU16, val: u16) {
let mut current = q.load(Ordering::Relaxed);
loop {
let empty = (0..SLOTS as u16)
.find(|i| get(current, *i) == 0)
.expect("No empty slot available");
let modified = set(current, empty, val);
match q.compare_exchange_weak(current, modified, Ordering::Release, Ordering::Relaxed) {
Ok(_) => break,
Err(changed) => current = changed, // And retry with the changed value
}
}
}
fn dequeue(q: &AtomicU16) -> Option<u16> {
let mut current = q.load(Ordering::Relaxed);
loop {
let val = current & MASK;
// It's completely empty
if val == 0 {
break None;
}
let modified = current >> BITS;
match q.compare_exchange_weak(current, modified, Ordering::Acquire, Ordering::Relaxed) {
Ok(_) => break Some(val),
Err(changed) => current = changed,
}
}
}
/// A restricted async-signal-safe channel
///
/// This is a bit like the usual channel used for inter-thread communication, but with several
/// restrictions:
///
/// * There's a limited number of slots (currently 5).
/// * There's no way to wait for a place in it or for a value. If value is not available, `None` is
/// returned. If there's no space for a value, the value is silently dropped.
///
/// In exchange for that, all the operations on that channel are async-signal-safe. That means it
/// is possible to use it to communicate between a signal handler and the rest of the world with it
/// (specifically, it's designed to send information from the handler to the rest of the
/// application). The throwing out of values when full is in line with collating of the same type
/// in kernel (you should not use the same channel for multiple different signals).
///
/// Technically, this is a MPMC queue which preserves order, but it is expected to be used in MPSC
/// mode mostly (in theory, multiple threads can be executing a signal handler for the same signal
/// at the same time). The channel is not responsible for wakeups.
///
/// While the channel is async-signal-safe, you still need to make sure *creating* of the values is
/// too (it should not contain anything that allocates, for example ‒ so no `String`s inside, etc).
///
/// The code was *not* tuned for performance (signals are not expected to happen often).
pub struct Channel<T> {
storage: [UnsafeCell<Option<T>>; SLOTS],
empty: AtomicU16,
full: AtomicU16,
}
impl<T> Channel<T> {
/// Creates a new channel with nothing in it.
pub fn new() -> Self {
let storage = Default::default();
let me = Self {
storage,
empty: AtomicU16::new(0),
full: AtomicU16::new(0),
};
for i in 1..SLOTS + 1 {
enqueue(&me.empty, i as u16);
}
me
}
/// Inserts a value into the channel.
///
/// If the value doesn't fit, it is silently dropped. Never blocks.
pub fn send(&self, val: T) {
if let Some(empty_idx) = dequeue(&self.empty) {
unsafe { *self.storage[empty_idx as usize - 1].get() = Some(val) };
enqueue(&self.full, empty_idx);
}
}
/// Takes a value from the channel.
///
/// Or returns `None` if the channel is empty. Never blocks.
pub fn recv(&self) -> Option<T> {
dequeue(&self.full).map(|idx| {
let result = unsafe { &mut *self.storage[idx as usize - 1].get() }
.take()
.expect("Full slot with nothing in it");
enqueue(&self.empty, idx);
result
})
}
}
impl<T> Default for Channel<T> {
fn default() -> Self {
Self::new()
}
}
unsafe impl<T: Send> Send for Channel<T> {}
// Yes, really Send -> Sync. Having a reference to Channel allows Sending Ts, but not having refs
// on them.
unsafe impl<T: Send> Sync for Channel<T> {}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::thread;
use super::*;
#[test]
fn new_empty() {
let channel = Channel::<usize>::new();
assert!(channel.recv().is_none());
assert!(channel.recv().is_none());
}
#[test]
fn pass_value() {
let channel = Channel::new();
channel.send(42);
assert_eq!(42, channel.recv().unwrap());
assert!(channel.recv().is_none());
}
#[test]
fn multiple() {
let channel = Channel::new();
for i in 0..1000 {
channel.send(i);
assert_eq!(i, channel.recv().unwrap());
assert!(channel.recv().is_none());
}
}
#[test]
fn overflow() {
let channel = Channel::new();
for i in 0..10 {
channel.send(i);
}
for i in 0..5 {
assert_eq!(i, channel.recv().unwrap());
}
assert!(channel.recv().is_none());
}
#[test]
fn multi_thread() {
let channel = Arc::new(Channel::<usize>::new());
let sender = thread::spawn({
let channel = Arc::clone(&channel);
move || {
for i in 0..4 {
channel.send(i);
}
}
});
let mut results = Vec::new();
while results.len() < 4 {
results.extend(channel.recv());
}
assert_eq!(vec![0, 1, 2, 3], results);
sender.join().unwrap();
}
}
@@ -0,0 +1,57 @@
/*
* Low-level extraction code to overcome rust's libc not having the best access
* to siginfo_t details.
*/
#include <stdbool.h>
#include <signal.h>
#include <stdint.h>
struct Const {
int native;
// The signal this applies to, or -1 if it applies to anything.
int signal;
uint8_t translated;
};
// Warning: must be in sync with the rust source code
struct Const consts[] = {
#ifdef SI_KERNEL
{ SI_KERNEL, -1, 1 },
#endif
{ SI_USER, -1, 2 },
#ifdef SI_TKILL
{ SI_TKILL, -1, 3 },
#endif
{ SI_QUEUE, -1, 4 },
#ifdef SI_MESGQ
{ SI_MESGQ, -1, 5 },
#endif
{ CLD_EXITED, SIGCHLD, 6 },
{ CLD_KILLED, SIGCHLD, 7 },
{ CLD_DUMPED, SIGCHLD, 8 },
{ CLD_TRAPPED, SIGCHLD, 9 },
{ CLD_STOPPED, SIGCHLD, 10 },
{ CLD_CONTINUED, SIGCHLD, 11 },
};
uint8_t sighook_signal_cause(const siginfo_t *info) {
const size_t const_len = sizeof consts / sizeof *consts;
size_t i;
for (i = 0; i < const_len; i ++) {
if (
consts[i].native == info->si_code &&
(consts[i].signal == -1 || consts[i].signal == info->si_signo)
) {
return consts[i].translated;
}
}
return 0; // The "Unknown" variant
}
pid_t sighook_signal_pid(const siginfo_t *info) {
return info->si_pid;
}
uid_t sighook_signal_uid(const siginfo_t *info) {
return info->si_uid;
}
+59
View File
@@ -0,0 +1,59 @@
//! Some low level utilities
//!
//! More often to build other abstractions than used directly.
use std::io::Error;
use libc::c_int;
#[cfg(feature = "channel")]
#[cfg_attr(docsrs, doc(cfg(feature = "channel")))]
pub mod channel;
#[cfg(not(windows))]
#[cfg_attr(docsrs, doc(cfg(not(windows))))]
pub mod pipe;
#[cfg(feature = "extended-siginfo-raw")]
#[cfg_attr(docsrs, doc(cfg(feature = "extended-siginfo-raw")))]
pub mod siginfo;
mod signal_details;
pub use signal_hook_registry::{register, unregister};
pub use self::signal_details::{emulate_default_handler, signal_name};
/// The usual raise, just the safe wrapper around it.
///
/// This is async-signal-safe.
pub fn raise(sig: c_int) -> Result<(), Error> {
let result = unsafe { libc::raise(sig) };
if result == -1 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
/// A bare libc abort.
///
/// Unlike the [std::process::abort], this one is guaranteed to contain no additions or wrappers
/// and therefore is async-signal-safe. You can use this to terminate the application from within a
/// signal handler.
pub fn abort() -> ! {
unsafe {
libc::abort();
}
}
/// A bare libc exit.
///
/// Unlike the [std::process::exit], this one is guaranteed to contain no additions or wrappers and
/// therefore is async-signal-safe. You can use this to terminate the application from within a
/// signal handler.
///
/// Also, see [`register_conditional_shutdown`][crate::flag::register_conditional_shutdown].
pub fn exit(status: c_int) -> ! {
unsafe {
// Yes, the one with underscore. That one doesn't call the at-exit hooks.
libc::_exit(status);
}
}
+266
View File
@@ -0,0 +1,266 @@
//! Module with the self-pipe pattern.
//!
//! One of the common patterns around signals is to have a pipe with both ends in the same program.
//! Whenever there's a signal, the signal handler writes one byte of garbage data to the write end,
//! unless the pipe's already full. The application then can handle the read end.
//!
//! This has two advantages. First, the real signal action moves outside of the signal handler
//! where there are a lot less restrictions. Second, it fits nicely in all kinds of asynchronous
//! loops and has less chance of race conditions.
//!
//! This module offers premade functions for the write end (and doesn't insist that it must be a
//! pipe ‒ anything that can be written to is fine ‒ sockets too, therefore `UnixStream::pair` is a
//! good candidate).
//!
//! If you want to integrate with some asynchronous library, plugging streams from `mio-uds` or
//! `tokio-uds` libraries should work.
//!
//! If it looks too low-level for your needs, the [`iterator`][crate::iterator] module contains some
//! higher-lever interface that also uses a self-pipe pattern under the hood.
//!
//! # Correct order of handling
//!
//! A care needs to be taken to avoid race conditions, especially when handling the same signal in
//! a loop. Specifically, another signal might come when the action for the previous signal is
//! being taken. The correct order is first to clear the content of the pipe (read some/all data
//! from it) and then take the action. This way a spurious wakeup can happen (the pipe could wake
//! up even when no signal came after the signal was taken, because ‒ it arrived between cleaning
//! the pipe and taking the action). Note that some OS primitives (eg. `select`) suffer from
//! spurious wakeups themselves (they can claim a FD is readable when it is not true) and blocking
//! `read` might return prematurely (with eg. `EINTR`).
//!
//! The reverse order of first taking the action and then clearing the pipe might lose signals,
//! which is usually worse.
//!
//! This is not a problem with blocking on reading from the pipe (because both the blocking and
//! cleaning is the same action), but in case of asynchronous handling it matters.
//!
//! If you want to combine setting some flags with a self-pipe pattern, the flag needs to be set
//! first, then the pipe written. On the read end, first the pipe needs to be cleaned, then the
//! flag and then the action taken. This is what the [`SignalsInfo`][crate::iterator::SignalsInfo]
//! structure does internally.
//!
//! # Write collating
//!
//! While unlikely if handled correctly, it is possible the write end is full when a signal comes.
//! In such case the signal handler simply does nothing. If the write end is full, the read end is
//! readable and therefore will wake up. On the other hand, blocking in the signal handler would
//! definitely be a bad idea.
//!
//! However, this also means the number of bytes read from the end might be lower than the number
//! of signals that arrived. This should not generally be a problem, since the OS already collates
//! signals of the same kind together.
//!
//! # Examples
//!
//! This example waits for at last one `SIGUSR1` signal to come before continuing (and
//! terminating). It sends the signal to itself, so it correctly terminates.
//!
//! ```rust
//! use std::io::{Error, Read};
//! use std::os::unix::net::UnixStream;
//!
//! use signal_hook::consts::SIGUSR1;
//! use signal_hook::low_level::{pipe, raise};
//!
//! fn main() -> Result<(), Error> {
//! let (mut read, write) = UnixStream::pair()?;
//! pipe::register(SIGUSR1, write)?;
//! // This will write into the pipe write end through the signal handler
//! raise(SIGUSR1).unwrap();
//! let mut buff = [0];
//! read.read_exact(&mut buff)?;
//! println!("Happily terminating");
//! Ok(())
//! }
//! ```
use std::io::{Error, ErrorKind};
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use libc::{self, c_int};
use crate::SigId;
#[cfg(target_os = "aix")]
const MSG_NOWAIT: i32 = libc::MSG_NONBLOCK;
#[cfg(not(target_os = "aix"))]
const MSG_NOWAIT: i32 = libc::MSG_DONTWAIT;
#[derive(Copy, Clone)]
pub(crate) enum WakeMethod {
Send,
Write,
}
struct WakeFd {
fd: RawFd,
method: WakeMethod,
}
impl WakeFd {
/// Sets close on exec and nonblock on the inner file descriptor.
fn set_flags(&self) -> Result<(), Error> {
unsafe {
let flags = libc::fcntl(self.as_raw_fd(), libc::F_GETFL, 0);
if flags == -1 {
return Err(Error::last_os_error());
}
let flags = flags | libc::O_NONBLOCK | libc::O_CLOEXEC;
if libc::fcntl(self.as_raw_fd(), libc::F_SETFL, flags) == -1 {
return Err(Error::last_os_error());
}
}
Ok(())
}
fn wake(&self) {
wake(self.fd, self.method);
}
}
impl AsRawFd for WakeFd {
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
impl Drop for WakeFd {
fn drop(&mut self) {
unsafe {
libc::close(self.fd);
}
}
}
pub(crate) fn wake(pipe: RawFd, method: WakeMethod) {
unsafe {
// This writes some data into the pipe.
//
// There are two tricks:
// * First, the crazy cast. The first part turns reference into pointer. The second part
// turns pointer to u8 into a pointer to void, which is what write requires.
// * Second, we ignore errors, on purpose. We don't have any means to handling them. The
// two conceivable errors are EBADFD, if someone passes a non-existent file descriptor or
// if it is closed. The second is EAGAIN, in which case the pipe is full ‒ there were
// many signals, but the reader didn't have time to read the data yet. It'll still get
// woken up, so not fitting another letter in it is fine.
let data = b"X" as *const _ as *const _;
match method {
WakeMethod::Write => libc::write(pipe, data, 1),
WakeMethod::Send => libc::send(pipe, data, 1, MSG_NOWAIT),
};
}
}
/// Registers a write to a self-pipe whenever there's the signal.
///
/// In this case, the pipe is taken as the `RawFd`. It'll be closed on deregistration. Effectively,
/// the function takes ownership of the file descriptor. This includes feeling free to set arbitrary
/// flags on it, including file status flags (that are shared across file descriptors created by
/// `dup`).
///
/// Note that passing the wrong file descriptor won't cause UB, but can still lead to severe bugs ‒
/// like data corruptions in files. Prefer using [`register`] if possible.
///
/// Also, it is perfectly legal for multiple writes to be collated together (if not consumed) and
/// to generate spurious wakeups (but will not generate spurious *bytes* in the pipe).
///
/// # Internal details
///
/// Internally, it *currently* does following. Note that this is *not* part of the stability
/// guarantees and may change if necessary.
///
/// * If the file descriptor can be used with [`send`][libc::send], it'll be used together with
/// [`MSG_DONTWAIT`][libc::MSG_DONTWAIT]. This is tested by sending `0` bytes of data (depending
/// on the socket type, this might wake the read end with an empty message).
/// * If it is not possible, the [`O_NONBLOCK`][libc::O_NONBLOCK] will be set on the file
/// descriptor and [`write`][libc::write] will be used instead.
pub fn register_raw(signal: c_int, pipe: RawFd) -> Result<SigId, Error> {
let res = unsafe { libc::send(pipe, &[] as *const _, 0, MSG_NOWAIT) };
let fd = match (res, Error::last_os_error().kind()) {
(0, _) | (-1, ErrorKind::WouldBlock) => WakeFd {
fd: pipe,
method: WakeMethod::Send,
},
_ => {
let fd = WakeFd {
fd: pipe,
method: WakeMethod::Write,
};
fd.set_flags()?;
fd
}
};
let action = move || fd.wake();
unsafe { super::register(signal, action) }
}
/// Registers a write to a self-pipe whenever there's the signal.
///
/// The ownership of pipe is taken and will be closed whenever the created action is unregistered.
///
/// Note that if you want to register the same pipe for multiple signals, there's `try_clone`
/// method on many unix socket primitives.
///
/// See [`register_raw`] for further details.
pub fn register<P>(signal: c_int, pipe: P) -> Result<SigId, Error>
where
P: IntoRawFd + 'static,
{
register_raw(signal, pipe.into_raw_fd())
}
#[cfg(test)]
mod tests {
use std::io::Read;
use std::os::unix::net::{UnixDatagram, UnixStream};
use super::*;
// Note: multiple tests share the SIGUSR1 signal. This is fine, we only need to know the signal
// arrives. It's OK to arrive multiple times, from multiple tests.
fn wakeup() {
crate::low_level::raise(libc::SIGUSR1).unwrap();
}
#[test]
fn register_with_socket() -> Result<(), Error> {
let (mut read, write) = UnixStream::pair()?;
register(libc::SIGUSR1, write)?;
wakeup();
let mut buff = [0; 1];
read.read_exact(&mut buff)?;
assert_eq!(b"X", &buff);
Ok(())
}
#[test]
#[cfg(not(target_os = "haiku"))]
fn register_dgram_socket() -> Result<(), Error> {
let (read, write) = UnixDatagram::pair()?;
register(libc::SIGUSR1, write)?;
wakeup();
let mut buff = [0; 1];
// The attempt to detect if it is socket can generate an empty message. Therefore, do a few
// retries.
for _ in 0..3 {
let len = read.recv(&mut buff)?;
if len == 1 && &buff == b"X" {
return Ok(());
}
}
panic!("Haven't received the right data");
}
#[test]
fn register_with_pipe() -> Result<(), Error> {
let mut fds = [0; 2];
unsafe { assert_eq!(0, libc::pipe(fds.as_mut_ptr())) };
register_raw(libc::SIGUSR1, fds[1])?;
wakeup();
let mut buff = [0; 1];
unsafe { assert_eq!(1, libc::read(fds[0], buff.as_mut_ptr() as *mut _, 1)) }
assert_eq!(b"X", &buff);
Ok(())
}
}
@@ -0,0 +1,261 @@
//! Extracting more information from the C [`siginfo_t`] structure.
//!
//! See [`Origin`].
use std::fmt::{Debug, Formatter, Result as FmtResult};
use libc::{c_int, pid_t, siginfo_t, uid_t};
use crate::low_level;
// Careful: make sure the signature and the constants match the C source
extern "C" {
fn sighook_signal_cause(info: &siginfo_t) -> ICause;
fn sighook_signal_pid(info: &siginfo_t) -> pid_t;
fn sighook_signal_uid(info: &siginfo_t) -> uid_t;
}
// Warning: must be in sync with the C code
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
#[repr(u8)]
// For some reason, the fact it comes from the C makes rustc emit warning that *some* of these are
// not constructed. No idea why only some of them.
#[allow(dead_code)]
enum ICause {
Unknown = 0,
Kernel = 1,
User = 2,
TKill = 3,
Queue = 4,
MesgQ = 5,
Exited = 6,
Killed = 7,
Dumped = 8,
Trapped = 9,
Stopped = 10,
Continued = 11,
}
impl ICause {
// The MacOs doesn't use the SI_* constants and leaves si_code at 0. But it doesn't use an
// union, it has a good-behaved struct with fields and therefore we *can* read the values,
// even though they'd contain nonsense (zeroes). We wipe that out later.
#[cfg(target_os = "macos")]
fn has_process(self) -> bool {
true
}
#[cfg(not(target_os = "macos"))]
fn has_process(self) -> bool {
use ICause::*;
match self {
Unknown | Kernel => false,
User | TKill | Queue | MesgQ | Exited | Killed | Dumped | Trapped | Stopped
| Continued => true,
}
}
}
/// Information about process, as presented in the signal metadata.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct Process {
/// The process ID.
pub pid: pid_t,
/// The user owning the process.
pub uid: uid_t,
}
impl Process {
/**
* Extract the process information.
*
* # Safety
*
* The `info` must have a `si_code` corresponding to some situation that has the `si_pid`
* and `si_uid` filled in.
*/
unsafe fn extract(info: &siginfo_t) -> Self {
Self {
pid: sighook_signal_pid(info),
uid: sighook_signal_uid(info),
}
}
}
/// The means by which a signal was sent by other process.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Sent {
/// The `kill` call.
User,
/// The `tkill` call.
///
/// This is likely linux specific.
TKill,
/// `sigqueue`.
Queue,
/// `mq_notify`.
MesgQ,
}
/// A child changed its state.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Chld {
/// The child exited normally.
Exited,
/// It got killed by a signal.
Killed,
/// It got killed by a signal and dumped core.
Dumped,
/// The child was trapped by a `SIGTRAP` signal.
Trapped,
/// The child got stopped.
Stopped,
/// The child continued (after being stopped).
Continued,
}
/// What caused a signal.
///
/// This is a best-effort (and possibly incomplete) representation of the C `siginfo_t::si_code`.
/// It may differ between OSes and may be extended in future versions.
///
/// Note that this doesn't contain all the „fault“ signals (`SIGILL`, `SIGSEGV` and similar).
/// There's no reasonable way to use the exfiltrators with them, since the handler either needs to
/// terminate the process or somehow recover from the situation. Things based on exfiltrators do
/// neither, which would cause an UB and therefore these values just don't make sense.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Cause {
/// The cause is unknown.
///
/// Some systems don't fill this in. Some systems have values we don't understand. Some signals
/// don't have specific reasons to come to being.
Unknown,
/// Sent by the kernel.
///
/// This probably exists only on Linux.
Kernel,
/// The signal was sent by other process.
Sent(Sent),
/// A `SIGCHLD`, caused by a child process changing state.
Chld(Chld),
}
impl From<ICause> for Cause {
fn from(c: ICause) -> Cause {
match c {
ICause::Kernel => Cause::Kernel,
ICause::User => Cause::Sent(Sent::User),
ICause::TKill => Cause::Sent(Sent::TKill),
ICause::Queue => Cause::Sent(Sent::Queue),
ICause::MesgQ => Cause::Sent(Sent::MesgQ),
ICause::Exited => Cause::Chld(Chld::Exited),
ICause::Killed => Cause::Chld(Chld::Killed),
ICause::Dumped => Cause::Chld(Chld::Dumped),
ICause::Trapped => Cause::Chld(Chld::Trapped),
ICause::Stopped => Cause::Chld(Chld::Stopped),
ICause::Continued => Cause::Chld(Chld::Continued),
// Unknown and possibly others if the underlying lib is updated
_ => Cause::Unknown,
}
}
}
/// Information about a signal and its origin.
///
/// This is produced by the [`WithOrigin`] exfiltrator (or can be [extracted][Origin::extract] from
/// `siginfo_t` by hand).
#[derive(Clone, Eq, PartialEq)]
#[non_exhaustive]
pub struct Origin {
/// The signal that happened.
pub signal: c_int,
/// Information about the process that caused the signal.
///
/// Note that not all signals are caused by a specific process or have the information
/// available („fault“ signals like `SIGBUS` don't have, any signal may be sent by the kernel
/// instead of a specific process).
///
/// This is filled in whenever available. For most signals, this is the process that sent the
/// signal (by `kill` or similar), for `SIGCHLD` it is the child that caused the signal.
pub process: Option<Process>,
/// How the signal happened.
///
/// This is a best-effort value. In particular, some systems may have causes not known to this
/// library. Some other systems (MacOS) does not fill the value in so there's no way to know.
/// In all these cases, this will contain [`Cause::Unknown`].
///
/// Some values are platform specific and not available on other systems.
///
/// Future versions may enrich the enum by further values.
pub cause: Cause,
}
impl Debug for Origin {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
fn named_signal(sig: c_int) -> String {
low_level::signal_name(sig)
.map(|n| format!("{} ({})", n, sig))
.unwrap_or_else(|| sig.to_string())
}
fmt.debug_struct("Origin")
.field("signal", &named_signal(self.signal))
.field("process", &self.process)
.field("cause", &self.cause)
.finish()
}
}
impl Origin {
/// Extracts the Origin from a raw `siginfo_t` structure.
///
/// This function is async-signal-safe, can be called inside a signal handler.
///
/// # Safety
///
/// On systems where the structure is backed by an union on the C side, this requires the
/// `si_code` and `si_signo` fields must be set properly according to what fields are
/// available.
///
/// The value passed by kernel satisfies this, care must be taken only when constructed
/// manually.
pub unsafe fn extract(info: &siginfo_t) -> Self {
let cause = sighook_signal_cause(info);
let process = if cause.has_process() {
let process = Process::extract(info);
// On macos we don't have the si_code to go by, but we can go by the values being
// empty there.
if cfg!(target_os = "macos") && process.pid == 0 && process.uid == 0 {
None
} else {
Some(process)
}
} else {
None
};
let signal = info.si_signo;
Origin {
cause: cause.into(),
signal,
process,
}
}
}
@@ -0,0 +1,247 @@
//! Providing auxiliary information for signals.
use std::io::Error;
use std::mem;
use std::ptr;
use libc::{c_int, EINVAL};
#[cfg(not(windows))]
use libc::{sigset_t, SIG_UNBLOCK};
use crate::consts::signal::*;
use crate::low_level;
#[derive(Clone, Copy, Debug)]
enum DefaultKind {
Ignore,
#[cfg(not(windows))]
Stop,
Term,
}
struct Details {
signal: c_int,
name: &'static str,
default_kind: DefaultKind,
}
macro_rules! s {
($name: expr, $kind: ident) => {
Details {
signal: $name,
name: stringify!($name),
default_kind: DefaultKind::$kind,
}
};
}
#[cfg(not(windows))]
const DETAILS: &[Details] = &[
s!(SIGABRT, Term),
s!(SIGALRM, Term),
s!(SIGBUS, Term),
s!(SIGCHLD, Ignore),
// Technically, continue the process... but this is not done *by* the process.
s!(SIGCONT, Ignore),
s!(SIGFPE, Term),
s!(SIGHUP, Term),
s!(SIGILL, Term),
s!(SIGINT, Term),
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "macos"
))]
s!(SIGINFO, Ignore),
#[cfg(not(target_os = "haiku"))]
s!(SIGIO, Ignore),
// Can't override anyway, but...
s!(SIGKILL, Term),
s!(SIGPIPE, Term),
s!(SIGPROF, Term),
s!(SIGQUIT, Term),
s!(SIGSEGV, Term),
// Can't override anyway, but...
s!(SIGSTOP, Stop),
s!(SIGSYS, Term),
s!(SIGTERM, Term),
s!(SIGTRAP, Term),
s!(SIGTSTP, Stop),
s!(SIGTTIN, Stop),
s!(SIGTTOU, Stop),
s!(SIGURG, Ignore),
s!(SIGUSR1, Term),
s!(SIGUSR2, Term),
s!(SIGVTALRM, Term),
s!(SIGWINCH, Ignore),
s!(SIGXCPU, Term),
s!(SIGXFSZ, Term),
];
#[cfg(windows)]
const DETAILS: &[Details] = &[
s!(SIGABRT, Term),
s!(SIGFPE, Term),
s!(SIGILL, Term),
s!(SIGINT, Term),
s!(SIGSEGV, Term),
s!(SIGTERM, Term),
];
/// Provides a human-readable name of a signal.
///
/// Note that the name does not have to be known (in case it is some less common, or non-standard
/// signal).
///
/// # Examples
///
/// ```
/// # use signal_hook::low_level::signal_name;
/// assert_eq!("SIGKILL", signal_name(9).unwrap());
/// assert!(signal_name(142).is_none());
/// ```
pub fn signal_name(signal: c_int) -> Option<&'static str> {
DETAILS.iter().find(|d| d.signal == signal).map(|d| d.name)
}
#[cfg(not(windows))]
fn restore_default(signal: c_int) -> Result<(), Error> {
unsafe {
// A C structure, supposed to be memset to 0 before use.
let mut action: libc::sigaction = mem::zeroed();
action.sa_sigaction = libc::SIG_DFL as _;
if libc::sigaction(signal, &action, ptr::null_mut()) == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
}
#[cfg(windows)]
fn restore_default(signal: c_int) -> Result<(), Error> {
unsafe {
// SIG_DFL = 0, but not in libc :-(
if libc::signal(signal, 0) == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
}
/// Emulates the behaviour of a default handler for the provided signal.
///
/// This function does its best to provide the same action as the default handler would do, without
/// disrupting the rest of the handling of such signal in the application. It is also
/// async-signal-safe.
///
/// This function necessarily looks up the appropriate action in a table. That means it is possible
/// your system has a signal that is not known to this function. In such case an error is returned
/// (equivalent of `EINVAL`).
///
/// See also the [`register_conditional_default`][crate::flag::register_conditional_default].
///
/// # Warning
///
/// There's a short race condition in case of signals that terminate (either with or without a core
/// dump). The emulation first resets the signal handler back to default (as the application is
/// going to end, it's not a problem) and invokes it. But if some other thread installs a signal
/// handler in the meantime (without assistance from `signal-hook`), it can happen this will be
/// invoked by the re-raised signal.
///
/// This function will still terminate the application (there's a fallback on `abort`), the risk is
/// invoking the newly installed signal handler. Note that manipulating the low-level signals is
/// always racy in a multi-threaded program, therefore the described situation is already
/// discouraged.
///
/// If you are uneasy about such race condition, the recommendation is to run relevant termination
/// routine manually ([`exit`][super::exit] or [`abort`][super::abort]); they always do what they
/// say, but slightly differ in externally observable behaviour from termination by a signal (the
/// exit code will specify that the application exited, not that it terminated with a signal in the
/// first case, and `abort` terminates on `SIGABRT`, so the detected termination signal may be
/// different).
pub fn emulate_default_handler(signal: c_int) -> Result<(), Error> {
#[cfg(not(windows))]
{
if signal == SIGSTOP || signal == SIGKILL {
return low_level::raise(signal);
}
}
let kind = DETAILS
.iter()
.find(|d| d.signal == signal)
.map(|d| d.default_kind)
.ok_or_else(|| Error::from_raw_os_error(EINVAL))?;
match kind {
DefaultKind::Ignore => Ok(()),
#[cfg(not(windows))]
DefaultKind::Stop => low_level::raise(SIGSTOP),
DefaultKind::Term => {
if let Ok(()) = restore_default(signal) {
#[cfg(not(windows))]
unsafe {
#[allow(deprecated)]
let mut newsigs: sigset_t = mem::zeroed();
// Some android versions don't have the sigemptyset and sigaddset.
// Unfortunately, we don't have an access to the android _version_. We just
// know that 64bit versions are all OK, so this is a best-effort guess.
//
// For the affected/guessed versions, we provide our own implementation. We
// hope it to be correct (it's inspired by a libc implementation and we assume
// the kernel uses the same format ‒ it's unlikely to be different both because
// of compatibility and because there's really nothing to invent about a
// bitarray).
//
// We use the proper way for other systems.
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
unsafe fn prepare_sigset(set: *mut sigset_t, mut signal: c_int) {
signal -= 1;
let set_raw: *mut libc::c_ulong = set.cast();
let size = mem::size_of::<libc::c_ulong>();
assert_eq!(set_raw as usize % mem::align_of::<libc::c_ulong>(), 0);
let pos = signal as usize / size;
assert!(pos < mem::size_of::<sigset_t>() / size);
let bit = 1 << (signal as usize % size);
set_raw.add(pos).write(bit);
}
#[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
unsafe fn prepare_sigset(set: *mut sigset_t, signal: c_int) {
libc::sigemptyset(set);
libc::sigaddset(set, signal);
}
prepare_sigset(&mut newsigs, signal);
// Ignore the result, if it doesn't work, we try anyway
// Also, sigprocmask is unspecified, but available on more systems. And we want
// to just enable _something_. And if it doesn't work, we'll terminate
// anyway... It's not UB, so we are good.
libc::sigprocmask(SIG_UNBLOCK, &newsigs, ptr::null_mut());
}
let _ = low_level::raise(signal);
}
// Fallback if anything failed or someone managed to put some other action in in
// between.
unsafe { libc::abort() }
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn existing() {
assert_eq!("SIGTERM", signal_name(SIGTERM).unwrap());
}
#[test]
fn unknown() {
assert!(signal_name(128).is_none());
}
}