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