1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
// Copyright (c) 2017 CtrlC developers // Licensed under the Apache License, Version 2.0 // <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT // license <LICENSE-MIT or http://opensource.org/licenses/MIT>, // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. #![warn(missing_docs)] //! Cross platform handling of Ctrl-C signals. //! //! [HandlerRoutine]:https://msdn.microsoft.com/en-us/library/windows/desktop/ms683242.aspx //! //! [set_handler()](fn.set_handler.html) allows setting a handler closure which is executed on //! `Ctrl+C`. On Unix, this corresponds to a `SIGINT` signal. On windows, `Ctrl+C` corresponds to //! [`CTRL_C_EVENT`][HandlerRoutine] or [`CTRL_BREAK_EVENT`][HandlerRoutine]. //! //! Setting a handler will start a new dedicated signal handling thread where we //! execute the handler each time we receive a `Ctrl+C` signal. There can only be //! one handler, you would typically set one at the start of your program. //! //! # Example //! ```no_run //! use std::sync::atomic::{AtomicBool, Ordering}; //! use std::sync::Arc; //! //! fn main() { //! let running = Arc::new(AtomicBool::new(true)); //! let r = running.clone(); //! //! ctrlc::set_handler(move || { //! r.store(false, Ordering::SeqCst); //! }).expect("Error setting Ctrl-C handler"); //! //! println!("Waiting for Ctrl-C..."); //! while running.load(Ordering::SeqCst) {} //! println!("Got it! Exiting..."); //! } //! ``` //! //! # Handling SIGTERM //! Handling of `SIGTERM` can be enabled with `termination` feature. If this is enabled, //! the handler specified by `set_handler()` will be executed for both `SIGINT` and `SIGTERM`. //! #[macro_use] mod error; mod platform; pub use platform::Signal; mod signal; pub use signal::*; pub use error::Error; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; static INIT: AtomicBool = AtomicBool::new(false); /// Register signal handler for Ctrl-C. /// /// Starts a new dedicated signal handling thread. Should only be called once, /// typically at the start of your program. /// /// # Example /// ```no_run /// ctrlc::set_handler(|| println!("Hello world!")).expect("Error setting Ctrl-C handler"); /// ``` /// /// # Warning /// On Unix, any existing `SIGINT`, `SIGTERM`(if termination feature is enabled) or `SA_SIGINFO` /// posix signal handlers will be overwritten. On Windows, multiple handler routines are allowed, /// but they are called on a last-registered, first-called basis until the signal is handled. /// /// On Unix, signal dispositions and signal handlers are inherited by child processes created via /// `fork(2)` on, but not by child processes created via `execve(2)`. /// Signal handlers are not inherited on Windows. /// /// # Errors /// Will return an error if another `ctrlc::set_handler()` handler exists or if a /// system error occurred while setting the handler. /// /// # Panics /// Any panic in the handler will not be caught and will cause the signal handler thread to stop. /// pub fn set_handler<F>(mut user_handler: F) -> Result<(), Error> where F: FnMut() -> () + 'static + Send, { if INIT.compare_and_swap(false, true, Ordering::SeqCst) { return Err(Error::MultipleHandlers); } unsafe { match platform::init_os_handler() { Ok(_) => {} Err(err) => { INIT.store(false, Ordering::SeqCst); return Err(err.into()); } } } thread::Builder::new() .name("ctrl-c".into()) .spawn(move || loop { unsafe { platform::block_ctrl_c().expect("Critical system error while waiting for Ctrl-C"); } user_handler(); }) .expect("failed to spawn thread"); Ok(()) }