mirror of
https://github.com/openharmony/third_party_rust_signal-hook.git
synced 2026-07-01 20:44:18 -04:00
05bf2a660b
For C code that extracts stuff. Because the libc crate is not really very helpful there: * The SI_* constants are missing. * The si_pid/si_uid methods are available only on few selected targets, not everywhere.
329 lines
12 KiB
Rust
329 lines
12 KiB
Rust
//! 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::iterator::Signals;
|
||
//!
|
||
//! fn main() -> Result<(), Error> {
|
||
//! let mut signals = Signals::new(&[
|
||
//! signal_hook::SIGHUP,
|
||
//! signal_hook::SIGTERM,
|
||
//! signal_hook::SIGINT,
|
||
//! signal_hook::SIGQUIT,
|
||
//! # signal_hook::SIGUSR1,
|
||
//! ])?;
|
||
//! # // A trick to terminate the example when run as doc-test. Not part of the real code.
|
||
//! # unsafe { libc::raise(signal_hook::SIGUSR1) };
|
||
//! 'outer: loop {
|
||
//! // Pick up signals that arrived since last time
|
||
//! for signal in signals.pending() {
|
||
//! match signal as libc::c_int {
|
||
//! signal_hook::SIGHUP => {
|
||
//! // Reload configuration
|
||
//! // Reopen the log file
|
||
//! }
|
||
//! signal_hook::SIGTERM | signal_hook::SIGINT | signal_hook::SIGQUIT => {
|
||
//! break 'outer;
|
||
//! },
|
||
//! # signal_hook::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, SignalDelivery, SignalIterator};
|
||
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::iterator::Signals;
|
||
///
|
||
/// #
|
||
/// # fn main() -> Result<(), Error> {
|
||
/// let signals = Signals::new(&[signal_hook::SIGUSR1, signal_hook::SIGUSR2])?;
|
||
/// let handle = signals.handle();
|
||
/// let thread = thread::spawn(move || {
|
||
/// for signal in signals {
|
||
/// match signal {
|
||
/// signal_hook::SIGUSR1 => {},
|
||
/// signal_hook::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`][Signals::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()
|
||
}
|
||
|
||
/// Consume this instance and return 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 `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::iterator::Signals;
|
||
///
|
||
/// # fn main() -> Result<(), Error> {
|
||
/// let signals = Signals::new(&[signal_hook::SIGUSR1, signal_hook::SIGUSR2])?;
|
||
/// let handle = signals.handle();
|
||
/// thread::spawn(move || {
|
||
/// for signal in signals.forever() {
|
||
/// match signal {
|
||
/// signal_hook::SIGUSR1 => {},
|
||
/// signal_hook::SIGUSR2 => {},
|
||
/// _ => unreachable!(),
|
||
/// }
|
||
/// }
|
||
/// });
|
||
/// handle.close();
|
||
/// # Ok(())
|
||
/// # }
|
||
/// ```
|
||
pub fn forever(self) -> Forever<E> {
|
||
Forever(SignalIterator::new(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<E: Exfiltrator> IntoIterator for SignalsInfo<E> {
|
||
type Item = E::Output;
|
||
type IntoIter = Forever<E>;
|
||
fn into_iter(self) -> Self::IntoIter {
|
||
self.forever()
|
||
}
|
||
}
|
||
|
||
/// An infinit iterator of arriving signals.
|
||
pub struct Forever<E: Exfiltrator>(SignalIterator<UnixStream, E>);
|
||
|
||
impl<E: Exfiltrator> Forever<E> {
|
||
/// Consume this iterator and return the inner
|
||
/// [`Signals`] instance.
|
||
pub fn into_inner(self) -> SignalsInfo<E> {
|
||
SignalsInfo(self.0.into_inner())
|
||
}
|
||
}
|
||
|
||
impl<E: Exfiltrator> Iterator for Forever<E> {
|
||
type Item = E::Output;
|
||
|
||
fn next(&mut self) -> Option<E::Output> {
|
||
match self.0.poll_signal(&mut SignalsInfo::<E>::has_signals) {
|
||
PollResult::Signal(result) => Some(result),
|
||
PollResult::Closed => None,
|
||
PollResult::Pending => unreachable!(
|
||
"Because of the blocking has_signals method the \
|
||
poll_signal method never returns Poll::Pending but blocks until a signal arrived"
|
||
),
|
||
// 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>;
|