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