Library for safe and correct Unix signal handling in Rust.
Unix signals are inherently hard to handle correctly, for several reasons:
This library aims to solve some of the problems. It provides a global registry of actions performed on arrival of signals. It is possible to register multiple actions for the same signal and it is possible to remove the actions later on. If there was a previous signal handler when the first action for a signal is registered, it is chained (but the original one can't be removed).
Besides the basic registration of an arbitrary action, several helper actions are provided to cover the needs of the most common use cases, available from safe Rust.
For further details, see the documentation.
(This likely does a lot more than you need in each individual application, it's more of a show-case of what everything is possible, not of what you need to do each time).
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; 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)?; // 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, by a little trick. low_level::raise(SIGSTOP)?; } } 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(()) }
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.