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