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