Skip to main content

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