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