tokio/signal/
unix.rs

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
9use crate::runtime::scheduler;
10use crate::runtime::signal::Handle;
11use crate::signal::registry::{globals, EventId, EventInfo, Globals, Init, Storage};
12use crate::signal::RxFuture;
13use crate::sync::watch;
14
15use mio::net::UnixStream;
16use std::io::{self, Error, ErrorKind, Write};
17use std::sync::atomic::{AtomicBool, Ordering};
18use std::sync::Once;
19use std::task::{Context, Poll};
20
21pub(crate) type OsStorage = Box<[SignalInfo]>;
22
23impl 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 = 0..=libc::SIGRTMAX();
34
35        possible.map(|_| SignalInfo::default()).collect()
36    }
37}
38
39impl Storage for OsStorage {
40    fn event_info(&self, id: EventId) -> Option<&EventInfo> {
41        self.get(id).map(|si| &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| &si.event_info).for_each(f);
49    }
50}
51
52#[derive(Debug)]
53pub(crate) struct OsExtraData {
54    sender: UnixStream,
55    pub(crate) receiver: UnixStream,
56}
57
58impl Init for OsExtraData {
59    fn init() -> Self {
60        let (receiver, sender) = UnixStream::pair().expect("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)]
68pub struct SignalKind(libc::c_int);
69
70impl 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
218impl 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
224impl From<SignalKind> for std::os::raw::c_int {
225    fn from(kind: SignalKind) -> Self {
226        kind.as_raw_value()
227    }
228}
229
230pub(crate) struct SignalInfo {
231    event_info: EventInfo,
232    init: Once,
233    initialized: AtomicBool,
234}
235
236impl 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.
254fn action(globals: &'static Globals, signal: libc::c_int) {
255    globals.record_event(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 = &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.
268fn 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)]
375pub 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]
407pub fn signal(kind: SignalKind) -> io::Result<Signal> {
408    let handle = scheduler::Handle::current();
409    let rx = signal_with_handle(kind, handle.driver().signal())?;
410
411    Ok(Signal {
412        inner: RxFuture::new(rx),
413    })
414}
415
416pub(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(kind, handle)?;
422
423    Ok(globals().register_listener(kind.0 as EventId))
424}
425
426impl 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")]
501pub(crate) trait InternalStream {
502    fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>>;
503}
504
505#[cfg(feature = "process")]
506impl InternalStream for Signal {
507    fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
508        self.poll_recv(cx)
509    }
510}
511
512pub(crate) fn ctrl_c() -> io::Result<Signal> {
513    signal(SignalKind::interrupt())
514}
515
516#[cfg(all(test, not(loom)))]
517mod 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}