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