1 | //! Unix-specific types for signal handling. |
2 | //! |
3 | //! This module is only defined on Unix platforms and contains the primary |
4 | //! `Signal` type for receiving notifications of signals. |
5 | |
6 | #![cfg (unix)] |
7 | #![cfg_attr (docsrs, doc(cfg(all(unix, feature = "signal" ))))] |
8 | |
9 | use crate::runtime::scheduler; |
10 | use crate::runtime::signal::Handle; |
11 | use crate::signal::registry::{globals, EventId, EventInfo, Globals, Init, Storage}; |
12 | use crate::signal::RxFuture; |
13 | use crate::sync::watch; |
14 | |
15 | use mio::net::UnixStream; |
16 | use std::io::{self, Error, ErrorKind, Write}; |
17 | use std::sync::atomic::{AtomicBool, Ordering}; |
18 | use std::sync::Once; |
19 | use std::task::{Context, Poll}; |
20 | |
21 | pub(crate) type OsStorage = Vec<SignalInfo>; |
22 | |
23 | impl Init for OsStorage { |
24 | fn init() -> Self { |
25 | // There are reliable signals ranging from 1 to 33 available on every Unix platform. |
26 | #[cfg (not(target_os = "linux" ))] |
27 | let possible = 0..=33; |
28 | |
29 | // On Linux, there are additional real-time signals available. |
30 | #[cfg (target_os = "linux" )] |
31 | let possible = 0..=libc::SIGRTMAX(); |
32 | |
33 | possible.map(|_| SignalInfo::default()).collect() |
34 | } |
35 | } |
36 | |
37 | impl Storage for OsStorage { |
38 | fn event_info(&self, id: EventId) -> Option<&EventInfo> { |
39 | self.get(id).map(|si| &si.event_info) |
40 | } |
41 | |
42 | fn for_each<'a, F>(&'a self, f: F) |
43 | where |
44 | F: FnMut(&'a EventInfo), |
45 | { |
46 | self.iter().map(|si| &si.event_info).for_each(f); |
47 | } |
48 | } |
49 | |
50 | #[derive(Debug)] |
51 | pub(crate) struct OsExtraData { |
52 | sender: UnixStream, |
53 | pub(crate) receiver: UnixStream, |
54 | } |
55 | |
56 | impl Init for OsExtraData { |
57 | fn init() -> Self { |
58 | let (receiver, sender) = UnixStream::pair().expect("failed to create UnixStream" ); |
59 | |
60 | Self { sender, receiver } |
61 | } |
62 | } |
63 | |
64 | /// Represents the specific kind of signal to listen for. |
65 | #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] |
66 | pub struct SignalKind(libc::c_int); |
67 | |
68 | impl SignalKind { |
69 | /// Allows for listening to any valid OS signal. |
70 | /// |
71 | /// For example, this can be used for listening for platform-specific |
72 | /// signals. |
73 | /// ```rust,no_run |
74 | /// # use tokio::signal::unix::SignalKind; |
75 | /// # let signum = -1; |
76 | /// // let signum = libc::OS_SPECIFIC_SIGNAL; |
77 | /// let kind = SignalKind::from_raw(signum); |
78 | /// ``` |
79 | // Use `std::os::raw::c_int` on public API to prevent leaking a non-stable |
80 | // type alias from libc. |
81 | // `libc::c_int` and `std::os::raw::c_int` are currently the same type, and are |
82 | // unlikely to change to other types, but technically libc can change this |
83 | // in the future minor version. |
84 | // See https://github.com/tokio-rs/tokio/issues/3767 for more. |
85 | pub const fn from_raw(signum: std::os::raw::c_int) -> Self { |
86 | Self(signum as libc::c_int) |
87 | } |
88 | |
89 | /// Get the signal's numeric value. |
90 | /// |
91 | /// ```rust |
92 | /// # use tokio::signal::unix::SignalKind; |
93 | /// let kind = SignalKind::interrupt(); |
94 | /// assert_eq!(kind.as_raw_value(), libc::SIGINT); |
95 | /// ``` |
96 | pub const fn as_raw_value(&self) -> std::os::raw::c_int { |
97 | self.0 |
98 | } |
99 | |
100 | /// Represents the SIGALRM signal. |
101 | /// |
102 | /// On Unix systems this signal is sent when a real-time timer has expired. |
103 | /// By default, the process is terminated by this signal. |
104 | pub const fn alarm() -> Self { |
105 | Self(libc::SIGALRM) |
106 | } |
107 | |
108 | /// Represents the SIGCHLD signal. |
109 | /// |
110 | /// On Unix systems this signal is sent when the status of a child process |
111 | /// has changed. By default, this signal is ignored. |
112 | pub const fn child() -> Self { |
113 | Self(libc::SIGCHLD) |
114 | } |
115 | |
116 | /// Represents the SIGHUP signal. |
117 | /// |
118 | /// On Unix systems this signal is sent when the terminal is disconnected. |
119 | /// By default, the process is terminated by this signal. |
120 | pub const fn hangup() -> Self { |
121 | Self(libc::SIGHUP) |
122 | } |
123 | |
124 | /// Represents the SIGINFO signal. |
125 | /// |
126 | /// On Unix systems this signal is sent to request a status update from the |
127 | /// process. By default, this signal is ignored. |
128 | #[cfg (any( |
129 | target_os = "dragonfly" , |
130 | target_os = "freebsd" , |
131 | target_os = "macos" , |
132 | target_os = "netbsd" , |
133 | target_os = "openbsd" |
134 | ))] |
135 | pub const fn info() -> Self { |
136 | Self(libc::SIGINFO) |
137 | } |
138 | |
139 | /// Represents the SIGINT signal. |
140 | /// |
141 | /// On Unix systems this signal is sent to interrupt a program. |
142 | /// By default, the process is terminated by this signal. |
143 | pub const fn interrupt() -> Self { |
144 | Self(libc::SIGINT) |
145 | } |
146 | |
147 | /// Represents the SIGIO signal. |
148 | /// |
149 | /// On Unix systems this signal is sent when I/O operations are possible |
150 | /// on some file descriptor. By default, this signal is ignored. |
151 | pub const fn io() -> Self { |
152 | Self(libc::SIGIO) |
153 | } |
154 | |
155 | /// Represents the SIGPIPE signal. |
156 | /// |
157 | /// On Unix systems this signal is sent when the process attempts to write |
158 | /// to a pipe which has no reader. By default, the process is terminated by |
159 | /// this signal. |
160 | pub const fn pipe() -> Self { |
161 | Self(libc::SIGPIPE) |
162 | } |
163 | |
164 | /// Represents the SIGQUIT signal. |
165 | /// |
166 | /// On Unix systems this signal is sent to issue a shutdown of the |
167 | /// process, after which the OS will dump the process core. |
168 | /// By default, the process is terminated by this signal. |
169 | pub const fn quit() -> Self { |
170 | Self(libc::SIGQUIT) |
171 | } |
172 | |
173 | /// Represents the SIGTERM signal. |
174 | /// |
175 | /// On Unix systems this signal is sent to issue a shutdown of the |
176 | /// process. By default, the process is terminated by this signal. |
177 | pub const fn terminate() -> Self { |
178 | Self(libc::SIGTERM) |
179 | } |
180 | |
181 | /// Represents the SIGUSR1 signal. |
182 | /// |
183 | /// On Unix systems this is a user defined signal. |
184 | /// By default, the process is terminated by this signal. |
185 | pub const fn user_defined1() -> Self { |
186 | Self(libc::SIGUSR1) |
187 | } |
188 | |
189 | /// Represents the SIGUSR2 signal. |
190 | /// |
191 | /// On Unix systems this is a user defined signal. |
192 | /// By default, the process is terminated by this signal. |
193 | pub const fn user_defined2() -> Self { |
194 | Self(libc::SIGUSR2) |
195 | } |
196 | |
197 | /// Represents the SIGWINCH signal. |
198 | /// |
199 | /// On Unix systems this signal is sent when the terminal window is resized. |
200 | /// By default, this signal is ignored. |
201 | pub const fn window_change() -> Self { |
202 | Self(libc::SIGWINCH) |
203 | } |
204 | } |
205 | |
206 | impl From<std::os::raw::c_int> for SignalKind { |
207 | fn from(signum: std::os::raw::c_int) -> Self { |
208 | Self::from_raw(signum as libc::c_int) |
209 | } |
210 | } |
211 | |
212 | impl From<SignalKind> for std::os::raw::c_int { |
213 | fn from(kind: SignalKind) -> Self { |
214 | kind.as_raw_value() |
215 | } |
216 | } |
217 | |
218 | pub(crate) struct SignalInfo { |
219 | event_info: EventInfo, |
220 | init: Once, |
221 | initialized: AtomicBool, |
222 | } |
223 | |
224 | impl Default for SignalInfo { |
225 | fn default() -> SignalInfo { |
226 | SignalInfo { |
227 | event_info: EventInfo::default(), |
228 | init: Once::new(), |
229 | initialized: AtomicBool::new(false), |
230 | } |
231 | } |
232 | } |
233 | |
234 | /// Our global signal handler for all signals registered by this module. |
235 | /// |
236 | /// The purpose of this signal handler is to primarily: |
237 | /// |
238 | /// 1. Flag that our specific signal was received (e.g. store an atomic flag) |
239 | /// 2. Wake up the driver by writing a byte to a pipe |
240 | /// |
241 | /// Those two operations should both be async-signal safe. |
242 | fn action(globals: &'static Globals, signal: libc::c_int) { |
243 | globals.record_event(signal as EventId); |
244 | |
245 | // Send a wakeup, ignore any errors (anything reasonably possible is |
246 | // full pipe and then it will wake up anyway). |
247 | let mut sender = &globals.sender; |
248 | drop(sender.write(&[1])); |
249 | } |
250 | |
251 | /// Enables this module to receive signal notifications for the `signal` |
252 | /// provided. |
253 | /// |
254 | /// This will register the signal handler if it hasn't already been registered, |
255 | /// returning any error along the way if that fails. |
256 | fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> { |
257 | let signal = signal.0; |
258 | if signal < 0 || signal_hook_registry::FORBIDDEN.contains(&signal) { |
259 | return Err(Error::new( |
260 | ErrorKind::Other, |
261 | format!("Refusing to register signal {}" , signal), |
262 | )); |
263 | } |
264 | |
265 | // Check that we have a signal driver running |
266 | handle.check_inner()?; |
267 | |
268 | let globals = globals(); |
269 | let siginfo = match globals.storage().get(signal as EventId) { |
270 | Some(slot) => slot, |
271 | None => return Err(io::Error::new(io::ErrorKind::Other, "signal too large" )), |
272 | }; |
273 | let mut registered = Ok(()); |
274 | siginfo.init.call_once(|| { |
275 | registered = unsafe { |
276 | signal_hook_registry::register(signal, move || action(globals, signal)).map(|_| ()) |
277 | }; |
278 | if registered.is_ok() { |
279 | siginfo.initialized.store(true, Ordering::Relaxed); |
280 | } |
281 | }); |
282 | registered?; |
283 | // If the call_once failed, it won't be retried on the next attempt to register the signal. In |
284 | // such case it is not run, registered is still `Ok(())`, initialized is still `false`. |
285 | if siginfo.initialized.load(Ordering::Relaxed) { |
286 | Ok(()) |
287 | } else { |
288 | Err(Error::new( |
289 | ErrorKind::Other, |
290 | "Failed to register signal handler" , |
291 | )) |
292 | } |
293 | } |
294 | |
295 | /// An listener for receiving a particular type of OS signal. |
296 | /// |
297 | /// The listener can be turned into a `Stream` using [`SignalStream`]. |
298 | /// |
299 | /// [`SignalStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.SignalStream.html |
300 | /// |
301 | /// In general signal handling on Unix is a pretty tricky topic, and this |
302 | /// structure is no exception! There are some important limitations to keep in |
303 | /// mind when using `Signal` streams: |
304 | /// |
305 | /// * Signals handling in Unix already necessitates coalescing signals |
306 | /// together sometimes. This `Signal` stream is also no exception here in |
307 | /// that it will also coalesce signals. That is, even if the signal handler |
308 | /// for this process runs multiple times, the `Signal` stream may only return |
309 | /// one signal notification. Specifically, before `poll` is called, all |
310 | /// signal notifications are coalesced into one item returned from `poll`. |
311 | /// Once `poll` has been called, however, a further signal is guaranteed to |
312 | /// be yielded as an item. |
313 | /// |
314 | /// Put another way, any element pulled off the returned listener corresponds to |
315 | /// *at least one* signal, but possibly more. |
316 | /// |
317 | /// * Signal handling in general is relatively inefficient. Although some |
318 | /// improvements are possible in this crate, it's recommended to not plan on |
319 | /// having millions of signal channels open. |
320 | /// |
321 | /// If you've got any questions about this feel free to open an issue on the |
322 | /// repo! New approaches to alleviate some of these limitations are always |
323 | /// appreciated! |
324 | /// |
325 | /// # Caveats |
326 | /// |
327 | /// The first time that a `Signal` instance is registered for a particular |
328 | /// signal kind, an OS signal-handler is installed which replaces the default |
329 | /// platform behavior when that signal is received, **for the duration of the |
330 | /// entire process**. |
331 | /// |
332 | /// For example, Unix systems will terminate a process by default when it |
333 | /// receives SIGINT. But, when a `Signal` instance is created to listen for |
334 | /// this signal, the next SIGINT that arrives will be translated to a stream |
335 | /// event, and the process will continue to execute. **Even if this `Signal` |
336 | /// instance is dropped, subsequent SIGINT deliveries will end up captured by |
337 | /// Tokio, and the default platform behavior will NOT be reset**. |
338 | /// |
339 | /// Thus, applications should take care to ensure the expected signal behavior |
340 | /// occurs as expected after listening for specific signals. |
341 | /// |
342 | /// # Examples |
343 | /// |
344 | /// Wait for SIGHUP |
345 | /// |
346 | /// ```rust,no_run |
347 | /// use tokio::signal::unix::{signal, SignalKind}; |
348 | /// |
349 | /// #[tokio::main] |
350 | /// async fn main() -> Result<(), Box<dyn std::error::Error>> { |
351 | /// // An infinite stream of hangup signals. |
352 | /// let mut sig = signal(SignalKind::hangup())?; |
353 | /// |
354 | /// // Print whenever a HUP signal is received |
355 | /// loop { |
356 | /// sig.recv().await; |
357 | /// println!("got signal HUP" ); |
358 | /// } |
359 | /// } |
360 | /// ``` |
361 | #[must_use = "streams do nothing unless polled" ] |
362 | #[derive(Debug)] |
363 | pub struct Signal { |
364 | inner: RxFuture, |
365 | } |
366 | |
367 | /// Creates a new listener which will receive notifications when the current |
368 | /// process receives the specified signal `kind`. |
369 | /// |
370 | /// This function will create a new stream which binds to the default reactor. |
371 | /// The `Signal` stream is an infinite stream which will receive |
372 | /// notifications whenever a signal is received. More documentation can be |
373 | /// found on `Signal` itself, but to reiterate: |
374 | /// |
375 | /// * Signals may be coalesced beyond what the kernel already does. |
376 | /// * Once a signal handler is registered with the process the underlying |
377 | /// libc signal handler is never unregistered. |
378 | /// |
379 | /// A `Signal` stream can be created for a particular signal number |
380 | /// multiple times. When a signal is received then all the associated |
381 | /// channels will receive the signal notification. |
382 | /// |
383 | /// # Errors |
384 | /// |
385 | /// * If the lower-level C functions fail for some reason. |
386 | /// * If the previous initialization of this specific signal failed. |
387 | /// * If the signal is one of |
388 | /// [`signal_hook::FORBIDDEN`](fn@signal_hook_registry::register#panics) |
389 | /// |
390 | /// # Panics |
391 | /// |
392 | /// This function panics if there is no current reactor set, or if the `rt` |
393 | /// feature flag is not enabled. |
394 | #[track_caller ] |
395 | pub fn signal(kind: SignalKind) -> io::Result<Signal> { |
396 | let handle = scheduler::Handle::current(); |
397 | let rx = signal_with_handle(kind, handle.driver().signal())?; |
398 | |
399 | Ok(Signal { |
400 | inner: RxFuture::new(rx), |
401 | }) |
402 | } |
403 | |
404 | pub(crate) fn signal_with_handle( |
405 | kind: SignalKind, |
406 | handle: &Handle, |
407 | ) -> io::Result<watch::Receiver<()>> { |
408 | // Turn the signal delivery on once we are ready for it |
409 | signal_enable(kind, handle)?; |
410 | |
411 | Ok(globals().register_listener(kind.0 as EventId)) |
412 | } |
413 | |
414 | impl Signal { |
415 | /// Receives the next signal notification event. |
416 | /// |
417 | /// `None` is returned if no more events can be received by this stream. |
418 | /// |
419 | /// # Cancel safety |
420 | /// |
421 | /// This method is cancel safe. If you use it as the event in a |
422 | /// [`tokio::select!`](crate::select) statement and some other branch |
423 | /// completes first, then it is guaranteed that no signal is lost. |
424 | /// |
425 | /// # Examples |
426 | /// |
427 | /// Wait for SIGHUP |
428 | /// |
429 | /// ```rust,no_run |
430 | /// use tokio::signal::unix::{signal, SignalKind}; |
431 | /// |
432 | /// #[tokio::main] |
433 | /// async fn main() -> Result<(), Box<dyn std::error::Error>> { |
434 | /// // An infinite stream of hangup signals. |
435 | /// let mut stream = signal(SignalKind::hangup())?; |
436 | /// |
437 | /// // Print whenever a HUP signal is received |
438 | /// loop { |
439 | /// stream.recv().await; |
440 | /// println!("got signal HUP" ); |
441 | /// } |
442 | /// } |
443 | /// ``` |
444 | pub async fn recv(&mut self) -> Option<()> { |
445 | self.inner.recv().await |
446 | } |
447 | |
448 | /// Polls to receive the next signal notification event, outside of an |
449 | /// `async` context. |
450 | /// |
451 | /// This method returns: |
452 | /// |
453 | /// * `Poll::Pending` if no signals are available but the channel is not |
454 | /// closed. |
455 | /// * `Poll::Ready(Some(()))` if a signal is available. |
456 | /// * `Poll::Ready(None)` if the channel has been closed and all signals |
457 | /// sent before it was closed have been received. |
458 | /// |
459 | /// # Examples |
460 | /// |
461 | /// Polling from a manually implemented future |
462 | /// |
463 | /// ```rust,no_run |
464 | /// use std::pin::Pin; |
465 | /// use std::future::Future; |
466 | /// use std::task::{Context, Poll}; |
467 | /// use tokio::signal::unix::Signal; |
468 | /// |
469 | /// struct MyFuture { |
470 | /// signal: Signal, |
471 | /// } |
472 | /// |
473 | /// impl Future for MyFuture { |
474 | /// type Output = Option<()>; |
475 | /// |
476 | /// fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
477 | /// println!("polling MyFuture" ); |
478 | /// self.signal.poll_recv(cx) |
479 | /// } |
480 | /// } |
481 | /// ``` |
482 | pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { |
483 | self.inner.poll_recv(cx) |
484 | } |
485 | } |
486 | |
487 | // Work around for abstracting streams internally |
488 | pub(crate) trait InternalStream { |
489 | fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>>; |
490 | } |
491 | |
492 | impl InternalStream for Signal { |
493 | fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { |
494 | self.poll_recv(cx) |
495 | } |
496 | } |
497 | |
498 | pub(crate) fn ctrl_c() -> io::Result<Signal> { |
499 | signal(SignalKind::interrupt()) |
500 | } |
501 | |
502 | #[cfg (all(test, not(loom)))] |
503 | mod tests { |
504 | use super::*; |
505 | |
506 | #[test] |
507 | fn signal_enable_error_on_invalid_input() { |
508 | signal_enable(SignalKind::from_raw(-1), &Handle::default()).unwrap_err(); |
509 | } |
510 | |
511 | #[test] |
512 | fn signal_enable_error_on_forbidden_input() { |
513 | signal_enable( |
514 | SignalKind::from_raw(signal_hook_registry::FORBIDDEN[0]), |
515 | &Handle::default(), |
516 | ) |
517 | .unwrap_err(); |
518 | } |
519 | |
520 | #[test] |
521 | fn from_c_int() { |
522 | assert_eq!(SignalKind::from(2), SignalKind::interrupt()); |
523 | } |
524 | |
525 | #[test] |
526 | fn into_c_int() { |
527 | let value: std::os::raw::c_int = SignalKind::interrupt().into(); |
528 | assert_eq!(value, libc::SIGINT as _); |
529 | } |
530 | } |
531 | |