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