• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Module for actions setting flags.
2 //!
3 //! This contains helper functions to set flags whenever a signal happens. The flags are atomic
4 //! bools or numbers and the library manipulates them with the `SeqCst` ordering, in case someone
5 //! cares about relative order to some *other* atomic variables. If you don't care about the
6 //! relative order, you are free to use `Ordering::Relaxed` when reading and resetting the flags.
7 //!
8 //! # When to use
9 //!
10 //! The flags in this module allow for polling if a signal arrived since the previous poll. The do
11 //! not allow blocking until something arrives.
12 //!
13 //! Therefore, the natural way to use them is in applications that have some kind of iterative work
14 //! with both some upper and lower time limit on one iteration. If one iteration could block for
15 //! arbitrary time, the handling of the signal would be postponed for a long time. If the iteration
16 //! didn't block at all, the checking for the signal would turn into a busy-loop.
17 //!
18 //! If what you need is blocking until a signal comes, you might find better tools in the
19 //! [`pipe`][crate::low_level::pipe] and [`iterator`][crate::iterator] modules.
20 //!
21 //! # Examples
22 //!
23 //! Doing something until terminated. This also knows by which signal it was terminated. In case
24 //! multiple termination signals arrive before it is handled, it recognizes the last one.
25 //!
26 //! ```rust
27 //! use std::io::Error;
28 //! use std::sync::Arc;
29 //! use std::sync::atomic::{AtomicUsize, Ordering};
30 //!
31 //! use signal_hook::consts::signal::*;
32 //! use signal_hook::flag as signal_flag;
33 //!
34 //! fn main() -> Result<(), Error> {
35 //!     let term = Arc::new(AtomicUsize::new(0));
36 //!     const SIGTERM_U: usize = SIGTERM as usize;
37 //!     const SIGINT_U: usize = SIGINT as usize;
38 //! #   #[cfg(not(windows))]
39 //!     const SIGQUIT_U: usize = SIGQUIT as usize;
40 //!     signal_flag::register_usize(SIGTERM, Arc::clone(&term), SIGTERM_U)?;
41 //!     signal_flag::register_usize(SIGINT, Arc::clone(&term), SIGINT_U)?;
42 //! #   #[cfg(not(windows))]
43 //!     signal_flag::register_usize(SIGQUIT, Arc::clone(&term), SIGQUIT_U)?;
44 //!
45 //! #   // Hack to terminate the example when run as a doc-test.
46 //! #   term.store(SIGTERM_U, Ordering::Relaxed);
47 //!     loop {
48 //!         match term.load(Ordering::Relaxed) {
49 //!             0 => {
50 //!                 // Do some useful stuff here
51 //!             }
52 //!             SIGTERM_U => {
53 //!                 eprintln!("Terminating on the TERM signal");
54 //!                 break;
55 //!             }
56 //!             SIGINT_U => {
57 //!                 eprintln!("Terminating on the INT signal");
58 //!                 break;
59 //!             }
60 //! #           #[cfg(not(windows))]
61 //!             SIGQUIT_U => {
62 //!                 eprintln!("Terminating on the QUIT signal");
63 //!                 break;
64 //!             }
65 //!             _ => unreachable!(),
66 //!         }
67 //!     }
68 //!
69 //!     Ok(())
70 //! }
71 //! ```
72 //!
73 //! Sending a signal to self and seeing it arrived (not of a practical usage on itself):
74 //!
75 //! ```rust
76 //! use std::io::Error;
77 //! use std::sync::Arc;
78 //! use std::sync::atomic::{AtomicBool, Ordering};
79 //! use std::thread;
80 //! use std::time::Duration;
81 //!
82 //! use signal_hook::consts::signal::*;
83 //! use signal_hook::low_level::raise;
84 //!
85 //! fn main() -> Result<(), Error> {
86 //!     let got = Arc::new(AtomicBool::new(false));
87 //! #   #[cfg(not(windows))]
88 //!     signal_hook::flag::register(SIGUSR1, Arc::clone(&got))?;
89 //! #   #[cfg(windows)]
90 //! #   signal_hook::flag::register(SIGTERM, Arc::clone(&got))?;
91 //! #   #[cfg(not(windows))]
92 //!     raise(SIGUSR1).unwrap();
93 //! #   #[cfg(windows)]
94 //! #   raise(SIGTERM).unwrap();
95 //!     // A sleep here, because it could run the signal handler in another thread and we may not
96 //!     // see the flag right away. This is still a hack and not guaranteed to work, it is just an
97 //!     // example!
98 //!     thread::sleep(Duration::from_secs(1));
99 //!     assert!(got.load(Ordering::Relaxed));
100 //!     Ok(())
101 //! }
102 //! ```
103 //!
104 //! Reloading a configuration on `SIGHUP` (which is a common behaviour of many UNIX daemons,
105 //! together with reopening the log file).
106 //!
107 //! ```rust
108 //! use std::io::Error;
109 //! use std::sync::Arc;
110 //! use std::sync::atomic::{AtomicBool, Ordering};
111 //!
112 //! use signal_hook::consts::signal::*;
113 //! use signal_hook::flag as signal_flag;
114 //!
115 //! fn main() -> Result<(), Error> {
116 //!     // We start with true, to load the configuration in the very first iteration too.
117 //!     let reload = Arc::new(AtomicBool::new(true));
118 //!     let term = Arc::new(AtomicBool::new(false));
119 //! #   #[cfg(not(windows))]
120 //!     signal_flag::register(SIGHUP, Arc::clone(&reload))?;
121 //!     signal_flag::register(SIGINT, Arc::clone(&term))?;
122 //!     signal_flag::register(SIGTERM, Arc::clone(&term))?;
123 //! #   #[cfg(not(windows))]
124 //!     signal_flag::register(SIGQUIT, Arc::clone(&term))?;
125 //!     while !term.load(Ordering::Relaxed) {
126 //!         // Using swap here, not load, to reset it back to false once it is reloaded.
127 //!         if reload.swap(false, Ordering::Relaxed) {
128 //!             // Reload the config here
129 //! #
130 //! #           // Hiden hack to make the example terminate when run as doc-test. Not part of the
131 //! #           // real code.
132 //! #           term.store(true, Ordering::Relaxed);
133 //!         }
134 //!         // Serve one request
135 //!     }
136 //!     Ok(())
137 //! }
138 //! ```
139 
140 use std::io::Error;
141 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
142 use std::sync::Arc;
143 
144 use libc::{c_int, EINVAL};
145 
146 use crate::{low_level, SigId};
147 
148 /// Registers an action to set the flag to `true` whenever the given signal arrives.
149 ///
150 /// # Panics
151 ///
152 /// If the signal is one of the forbidden.
register(signal: c_int, flag: Arc<AtomicBool>) -> Result<SigId, Error>153 pub fn register(signal: c_int, flag: Arc<AtomicBool>) -> Result<SigId, Error> {
154     // We use SeqCst for two reasons:
155     // * Signals should not come very often, so the performance does not really matter.
156     // * We promise the order of actions, but setting different atomics with Relaxed or similar
157     //   would not guarantee the effective order.
158     unsafe { low_level::register(signal, move || flag.store(true, Ordering::SeqCst)) }
159 }
160 
161 /// Registers an action to set the flag to the given value whenever the signal arrives.
register_usize(signal: c_int, flag: Arc<AtomicUsize>, value: usize) -> Result<SigId, Error>162 pub fn register_usize(signal: c_int, flag: Arc<AtomicUsize>, value: usize) -> Result<SigId, Error> {
163     unsafe { low_level::register(signal, move || flag.store(value, Ordering::SeqCst)) }
164 }
165 
166 /// Terminate the application on a signal if the given condition is true.
167 ///
168 /// This can be used for different use cases. One of them (with the condition being always true) is
169 /// just unconditionally terminate on the given signal.
170 ///
171 /// Another is being able to turn on and off the behaviour by the shared flag.
172 ///
173 /// The last one is handling double CTRL+C ‒ if the user presses CTRL+C, we would like to start a
174 /// graceful shutdown. But if anything ever gets stuck in the shutdown, second CTRL+C (or other
175 /// such termination signal) should terminate the application without further delay.
176 ///
177 /// To do that, one can combine this with [`register`]. On the first run, the flag is `false` and
178 /// this doesn't terminate. But then the flag is set to true during the first run and „arms“ the
179 /// shutdown on the second run. Note that it matters in which order the actions are registered (the
180 /// shutdown must go first). And yes, this also allows asking the user „Do you want to terminate“
181 /// and disarming the abrupt shutdown if the user answers „No“.
182 ///
183 /// # Panics
184 ///
185 /// If the signal is one of the forbidden.
register_conditional_shutdown( signal: c_int, status: c_int, condition: Arc<AtomicBool>, ) -> Result<SigId, Error>186 pub fn register_conditional_shutdown(
187     signal: c_int,
188     status: c_int,
189     condition: Arc<AtomicBool>,
190 ) -> Result<SigId, Error> {
191     let action = move || {
192         if condition.load(Ordering::SeqCst) {
193             low_level::exit(status);
194         }
195     };
196     unsafe { low_level::register(signal, action) }
197 }
198 
199 /// Conditionally runs an emulation of the default action on the given signal.
200 ///
201 /// If the provided condition is true at the time of invoking the signal handler, the equivalent of
202 /// the default action of the given signal is run. It is a bit similar to
203 /// [`register_conditional_shutdown`], except that it doesn't terminate for non-termination
204 /// signals, it runs their default handler.
205 ///
206 /// # Panics
207 ///
208 /// If the signal is one of the forbidden
209 ///
210 /// # Errors
211 ///
212 /// Similarly to the [`emulate_default_handler`][low_level::emulate_default_handler] function, this
213 /// one looks the signal up in a table. If it is unknown, an error is returned.
214 ///
215 /// Additionally to that, any errors that can be caused by a registration of a handler can happen
216 /// too.
register_conditional_default( signal: c_int, condition: Arc<AtomicBool>, ) -> Result<SigId, Error>217 pub fn register_conditional_default(
218     signal: c_int,
219     condition: Arc<AtomicBool>,
220 ) -> Result<SigId, Error> {
221     // Verify we know about this particular signal.
222     low_level::signal_name(signal).ok_or_else(|| Error::from_raw_os_error(EINVAL))?;
223     let action = move || {
224         if condition.load(Ordering::SeqCst) {
225             let _ = low_level::emulate_default_handler(signal);
226         }
227     };
228     unsafe { low_level::register(signal, action) }
229 }
230 
231 #[cfg(test)]
232 mod tests {
233     use std::sync::atomic;
234     use std::time::{Duration, Instant};
235 
236     use super::*;
237     use crate::consts::signal::*;
238 
self_signal()239     fn self_signal() {
240         #[cfg(not(windows))]
241         const SIG: c_int = SIGUSR1;
242         #[cfg(windows)]
243         const SIG: c_int = SIGTERM;
244         crate::low_level::raise(SIG).unwrap();
245     }
246 
wait_flag(flag: &AtomicBool) -> bool247     fn wait_flag(flag: &AtomicBool) -> bool {
248         let start = Instant::now();
249         while !flag.load(Ordering::Relaxed) {
250             // Replaced by hint::spin_loop, but we want to support older compiler
251             #[allow(deprecated)]
252             atomic::spin_loop_hint();
253             if Instant::now() - start > Duration::from_secs(1) {
254                 // We reached a timeout and nothing happened yet.
255                 // In theory, using timeouts for thread-synchronization tests is wrong, but a
256                 // second should be enough in practice.
257                 return false;
258             }
259         }
260         true
261     }
262 
263     #[test]
register_unregister()264     fn register_unregister() {
265         // When we register the action, it is active.
266         let flag = Arc::new(AtomicBool::new(false));
267         #[cfg(not(windows))]
268         let signal = register(SIGUSR1, Arc::clone(&flag)).unwrap();
269         #[cfg(windows)]
270         let signal = register(crate::SIGTERM, Arc::clone(&flag)).unwrap();
271         self_signal();
272         assert!(wait_flag(&flag));
273         // But stops working after it is unregistered.
274         assert!(crate::low_level::unregister(signal));
275         flag.store(false, Ordering::Relaxed);
276         self_signal();
277         assert!(!wait_flag(&flag));
278         // And the unregistration actually dropped its copy of the Arc
279         assert_eq!(1, Arc::strong_count(&flag));
280     }
281 
282     // The shutdown is tested in tests/shutdown.rs
283 }
284