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}