| 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 |  | 
|---|