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 //! use std::sync::atomic::{AtomicBool, Ordering};
27 //! use std::sync::Arc;
28 //!
29 //! fn main() {
30 //!     let running = Arc::new(AtomicBool::new(true));
31 //!     let r = running.clone();
32 //!
33 //!     ctrlc::set_handler(move || {
34 //!         r.store(false, Ordering::SeqCst);
35 //!     }).expect("Error setting Ctrl-C handler");
36 //!
37 //!     println!("Waiting for Ctrl-C...");
38 //!     while running.load(Ordering::SeqCst) {}
39 //!     println!("Got it! Exiting...");
40 //! }
41 //! ```
42 //!
43 //! # Handling SIGTERM and SIGHUP
44 //! Handling of `SIGTERM and SIGHUP` can be enabled with `termination` feature. If this is enabled,
45 //! the handler specified by `set_handler()` will be executed for `SIGINT`, `SIGTERM` and `SIGHUP`.
46 //!
47 
48 #[macro_use]
49 
50 mod error;
51 mod platform;
52 pub use platform::Signal;
53 mod signal;
54 pub use signal::*;
55 
56 pub use error::Error;
57 use std::sync::atomic::{AtomicBool, Ordering};
58 use std::thread;
59 
60 static INIT: AtomicBool = AtomicBool::new(false);
61 
62 /// Register signal handler for Ctrl-C.
63 ///
64 /// Starts a new dedicated signal handling thread. Should only be called once,
65 /// typically at the start of your program.
66 ///
67 /// # Example
68 /// ```no_run
69 /// ctrlc::set_handler(|| println!("Hello world!")).expect("Error setting Ctrl-C handler");
70 /// ```
71 ///
72 /// # Warning
73 /// On Unix, any existing `SIGINT`, `SIGTERM` and `SIGHUP` (if termination feature is enabled) or `SA_SIGINFO`
74 /// posix signal handlers will be overwritten. On Windows, multiple handler routines are allowed,
75 /// but they are called on a last-registered, first-called basis until the signal is handled.
76 ///
77 /// On Unix, signal dispositions and signal handlers are inherited by child processes created via
78 /// `fork(2)` on, but not by child processes created via `execve(2)`.
79 /// Signal handlers are not inherited on Windows.
80 ///
81 /// # Errors
82 /// Will return an error if another `ctrlc::set_handler()` handler exists or if a
83 /// system error occurred while setting the handler.
84 ///
85 /// # Panics
86 /// Any panic in the handler will not be caught and will cause the signal handler thread to stop.
87 ///
set_handler<F>(mut user_handler: F) -> Result<(), Error> where F: FnMut() -> () + 'static + Send,88 pub fn set_handler<F>(mut user_handler: F) -> Result<(), Error>
89 where
90     F: FnMut() -> () + 'static + Send,
91 {
92     if INIT.compare_and_swap(false, true, Ordering::SeqCst) {
93         return Err(Error::MultipleHandlers);
94     }
95 
96     unsafe {
97         match platform::init_os_handler() {
98             Ok(_) => {}
99             Err(err) => {
100                 INIT.store(false, Ordering::SeqCst);
101                 return Err(err.into());
102             }
103         }
104     }
105 
106     thread::Builder::new()
107         .name("ctrl-c".into())
108         .spawn(move || loop {
109             unsafe {
110                 platform::block_ctrl_c().expect("Critical system error while waiting for Ctrl-C");
111             }
112             user_handler();
113         })
114         .expect("failed to spawn thread");
115 
116     Ok(())
117 }
118