Coverage Report

Created: 2026-08-31 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/src/signal/unix.rs
Line
Count
Source
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
9
use crate::runtime::scheduler;
10
use crate::runtime::signal::Handle;
11
use crate::signal::registry::{globals, EventId, EventInfo, Globals, Storage};
12
use crate::signal::RxFuture;
13
use crate::sync::watch;
14
15
use mio::net::UnixStream;
16
use std::io::{self, Error, ErrorKind, Write};
17
use std::sync::OnceLock;
18
use std::task::{Context, Poll};
19
20
#[cfg(not(any(target_os = "linux", target_os = "illumos")))]
21
pub(crate) struct OsStorage([SignalInfo; 33]);
22
23
#[cfg(any(target_os = "linux", target_os = "illumos"))]
24
pub(crate) struct OsStorage(Box<[SignalInfo]>);
25
26
impl OsStorage {
27
0
    fn get(&self, id: EventId) -> Option<&SignalInfo> {
28
0
        self.0.get(id - 1)
29
0
    }
30
}
31
32
impl Default for OsStorage {
33
2
    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
2
        let inner = std::iter::repeat_with(SignalInfo::default)
43
2
            .take(libc::SIGRTMAX() as usize)
44
2
            .collect();
45
46
2
        Self(inner)
47
2
    }
48
}
49
50
impl Storage for OsStorage {
51
0
    fn event_info(&self, id: EventId) -> Option<&EventInfo> {
52
0
        self.get(id).map(|si| &si.event_info)
53
0
    }
54
55
0
    fn for_each<'a, F>(&'a self, f: F)
56
0
    where
57
0
        F: FnMut(&'a EventInfo),
58
    {
59
0
        self.0.iter().map(|si| &si.event_info).for_each(f);
60
0
    }
61
}
62
63
#[derive(Debug)]
64
pub(crate) struct OsExtraData {
65
    sender: UnixStream,
66
    pub(crate) receiver: UnixStream,
67
}
68
69
impl Default for OsExtraData {
70
2
    fn default() -> Self {
71
2
        let (receiver, sender) = UnixStream::pair().expect("failed to create UnixStream");
72
73
2
        Self { sender, receiver }
74
2
    }
75
}
76
77
/// Represents the specific kind of signal to listen for.
78
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
79
pub struct SignalKind(libc::c_int);
80
81
impl 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
0
    pub const fn from_raw(signum: std::os::raw::c_int) -> Self {
99
0
        Self(signum as libc::c_int)
100
0
    }
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
0
    pub const fn as_raw_value(&self) -> std::os::raw::c_int {
110
0
        self.0
111
0
    }
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
0
    pub const fn alarm() -> Self {
118
0
        Self(libc::SIGALRM)
119
0
    }
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
0
    pub const fn child() -> Self {
126
0
        Self(libc::SIGCHLD)
127
0
    }
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
0
    pub const fn hangup() -> Self {
134
0
        Self(libc::SIGHUP)
135
0
    }
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
0
    pub const fn interrupt() -> Self {
158
0
        Self(libc::SIGINT)
159
0
    }
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
0
    pub const fn io() -> Self {
175
0
        Self(libc::SIGIO)
176
0
    }
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
0
    pub const fn pipe() -> Self {
184
0
        Self(libc::SIGPIPE)
185
0
    }
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
0
    pub const fn quit() -> Self {
193
0
        Self(libc::SIGQUIT)
194
0
    }
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
0
    pub const fn terminate() -> Self {
201
0
        Self(libc::SIGTERM)
202
0
    }
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
0
    pub const fn user_defined1() -> Self {
209
0
        Self(libc::SIGUSR1)
210
0
    }
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
0
    pub const fn user_defined2() -> Self {
217
0
        Self(libc::SIGUSR2)
218
0
    }
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
0
    pub const fn window_change() -> Self {
225
0
        Self(libc::SIGWINCH)
226
0
    }
227
}
228
229
impl From<std::os::raw::c_int> for SignalKind {
230
0
    fn from(signum: std::os::raw::c_int) -> Self {
231
0
        Self::from_raw(signum as libc::c_int)
232
0
    }
233
}
234
235
impl From<SignalKind> for std::os::raw::c_int {
236
0
    fn from(kind: SignalKind) -> Self {
237
0
        kind.as_raw_value()
238
0
    }
239
}
240
241
#[derive(Default)]
242
pub(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.
255
0
fn action(globals: &'static Globals, signal: libc::c_int) {
256
0
    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
0
    let mut sender = &globals.sender;
261
0
    drop(sender.write(&[1]));
262
0
}
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.
269
0
fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> {
270
0
    let signal = signal.0;
271
0
    if signal <= 0 || signal_hook_registry::FORBIDDEN.contains(&signal) {
272
0
        return Err(Error::new(
273
0
            ErrorKind::Other,
274
0
            format!("Refusing to register signal {signal}"),
275
0
        ));
276
0
    }
277
278
    // Check that we have a signal driver running
279
0
    handle.check_inner()?;
280
281
0
    let globals = globals();
282
0
    let siginfo = match globals.storage().get(signal as EventId) {
283
0
        Some(slot) => slot,
284
0
        None => return Err(io::Error::new(io::ErrorKind::Other, "signal too large")),
285
    };
286
287
0
    siginfo
288
0
        .init
289
0
        .get_or_init(|| {
290
0
            unsafe { signal_hook_registry::register(signal, move || action(globals, signal)) }
291
0
                .map(|_| ())
292
0
                .map_err(|e| e.raw_os_error())
293
0
        })
294
0
        .map_err(|e| {
295
0
            e.map_or_else(
296
0
                || Error::new(ErrorKind::Other, "registering signal handler failed"),
297
                Error::from_raw_os_error,
298
            )
299
0
        })
300
0
}
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)]
370
pub 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]
402
0
pub fn signal(kind: SignalKind) -> io::Result<Signal> {
403
0
    let handle = scheduler::Handle::current();
404
0
    let rx = signal_with_handle(kind, handle.driver().signal())?;
405
406
0
    Ok(Signal {
407
0
        inner: RxFuture::new(rx),
408
0
    })
409
0
}
410
411
0
pub(crate) fn signal_with_handle(
412
0
    kind: SignalKind,
413
0
    handle: &Handle,
414
0
) -> io::Result<watch::Receiver<()>> {
415
    // Turn the signal delivery on once we are ready for it
416
0
    signal_enable(kind, handle)?;
417
418
0
    Ok(globals().register_listener(kind.0 as EventId))
419
0
}
420
421
impl 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 a branch in
430
    /// [`tokio::select!`](crate::select) and another 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
0
    pub async fn recv(&mut self) -> Option<()> {
453
0
        self.inner.recv().await;
454
0
        Some(())
455
0
    }
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
0
    pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
487
0
        self.inner.poll_recv(cx).map(Some)
488
0
    }
489
}
490
491
// Work around for abstracting streams internally
492
#[cfg(feature = "process")]
493
pub(crate) trait InternalStream {
494
    fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>>;
495
}
496
497
#[cfg(feature = "process")]
498
impl InternalStream for Signal {
499
0
    fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
500
0
        self.poll_recv(cx)
501
0
    }
502
}
503
504
0
pub(crate) fn ctrl_c() -> io::Result<Signal> {
505
0
    signal(SignalKind::interrupt())
506
0
}
507
508
#[cfg(all(test, not(loom)))]
509
mod 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
}