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}