blob: 6af194a376ddbea90119f8e4c8d594725449afbd [file] [log] [blame]
#![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 that 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 patters 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, so it is something one needs to
//! deal with anyway.
//!
//! # 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, SIGIO,
SIGKILL, SIGPIPE, SIGPROF, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTERM, SIGTRAP,
SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH, SIGXCPU,
SIGXFSZ,
};
#[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;