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, Init, 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::atomic::{AtomicBool, Ordering};
18 use std::sync::Once;
19 use std::task::{Context, Poll};
20
21 pub(crate) type OsStorage = Vec<SignalInfo>;
22
23 impl Init for OsStorage {
init() -> Self24 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
37 impl Storage for OsStorage {
event_info(&self, id: EventId) -> Option<&EventInfo>38 fn event_info(&self, id: EventId) -> Option<&EventInfo> {
39 self.get(id).map(|si| &si.event_info)
40 }
41
for_each<'a, F>(&'a self, f: F) where F: FnMut(&'a EventInfo),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)]
51 pub(crate) struct OsExtraData {
52 sender: UnixStream,
53 pub(crate) receiver: UnixStream,
54 }
55
56 impl Init for OsExtraData {
init() -> Self57 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)]
66 pub struct SignalKind(libc::c_int);
67
68 impl 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.
from_raw(signum: std::os::raw::c_int) -> Self85 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 /// ```
as_raw_value(&self) -> std::os::raw::c_int96 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.
alarm() -> Self104 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.
child() -> Self112 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.
hangup() -> Self120 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 ))]
info() -> Self135 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.
interrupt() -> Self143 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.
io() -> Self151 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.
pipe() -> Self160 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.
quit() -> Self169 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.
terminate() -> Self177 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.
user_defined1() -> Self185 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.
user_defined2() -> Self193 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.
window_change() -> Self201 pub const fn window_change() -> Self {
202 Self(libc::SIGWINCH)
203 }
204 }
205
206 impl From<std::os::raw::c_int> for SignalKind {
from(signum: std::os::raw::c_int) -> Self207 fn from(signum: std::os::raw::c_int) -> Self {
208 Self::from_raw(signum as libc::c_int)
209 }
210 }
211
212 impl From<SignalKind> for std::os::raw::c_int {
from(kind: SignalKind) -> Self213 fn from(kind: SignalKind) -> Self {
214 kind.as_raw_value()
215 }
216 }
217
218 pub(crate) struct SignalInfo {
219 event_info: EventInfo,
220 init: Once,
221 initialized: AtomicBool,
222 }
223
224 impl Default for SignalInfo {
default() -> SignalInfo225 fn default() -> SignalInfo {
226 SignalInfo {
227 event_info: Default::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.
action(globals: &'static Globals, signal: libc::c_int)242 fn 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.
signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()>256 fn 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 /// A stream of events for receiving a particular type of OS signal.
296 ///
297 /// In general signal handling on Unix is a pretty tricky topic, and this
298 /// structure is no exception! There are some important limitations to keep in
299 /// mind when using `Signal` streams:
300 ///
301 /// * Signals handling in Unix already necessitates coalescing signals
302 /// together sometimes. This `Signal` stream is also no exception here in
303 /// that it will also coalesce signals. That is, even if the signal handler
304 /// for this process runs multiple times, the `Signal` stream may only return
305 /// one signal notification. Specifically, before `poll` is called, all
306 /// signal notifications are coalesced into one item returned from `poll`.
307 /// Once `poll` has been called, however, a further signal is guaranteed to
308 /// be yielded as an item.
309 ///
310 /// Put another way, any element pulled off the returned stream corresponds to
311 /// *at least one* signal, but possibly more.
312 ///
313 /// * Signal handling in general is relatively inefficient. Although some
314 /// improvements are possible in this crate, it's recommended to not plan on
315 /// having millions of signal channels open.
316 ///
317 /// If you've got any questions about this feel free to open an issue on the
318 /// repo! New approaches to alleviate some of these limitations are always
319 /// appreciated!
320 ///
321 /// # Caveats
322 ///
323 /// The first time that a `Signal` instance is registered for a particular
324 /// signal kind, an OS signal-handler is installed which replaces the default
325 /// platform behavior when that signal is received, **for the duration of the
326 /// entire process**.
327 ///
328 /// For example, Unix systems will terminate a process by default when it
329 /// receives SIGINT. But, when a `Signal` instance is created to listen for
330 /// this signal, the next SIGINT that arrives will be translated to a stream
331 /// event, and the process will continue to execute. **Even if this `Signal`
332 /// instance is dropped, subsequent SIGINT deliveries will end up captured by
333 /// Tokio, and the default platform behavior will NOT be reset**.
334 ///
335 /// Thus, applications should take care to ensure the expected signal behavior
336 /// occurs as expected after listening for specific signals.
337 ///
338 /// # Examples
339 ///
340 /// Wait for SIGHUP
341 ///
342 /// ```rust,no_run
343 /// use tokio::signal::unix::{signal, SignalKind};
344 ///
345 /// #[tokio::main]
346 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
347 /// // An infinite stream of hangup signals.
348 /// let mut stream = signal(SignalKind::hangup())?;
349 ///
350 /// // Print whenever a HUP signal is received
351 /// loop {
352 /// stream.recv().await;
353 /// println!("got signal HUP");
354 /// }
355 /// }
356 /// ```
357 #[must_use = "streams do nothing unless polled"]
358 #[derive(Debug)]
359 pub struct Signal {
360 inner: RxFuture,
361 }
362
363 /// Creates a new stream which will receive notifications when the current
364 /// process receives the specified signal `kind`.
365 ///
366 /// This function will create a new stream which binds to the default reactor.
367 /// The `Signal` stream is an infinite stream which will receive
368 /// notifications whenever a signal is received. More documentation can be
369 /// found on `Signal` itself, but to reiterate:
370 ///
371 /// * Signals may be coalesced beyond what the kernel already does.
372 /// * Once a signal handler is registered with the process the underlying
373 /// libc signal handler is never unregistered.
374 ///
375 /// A `Signal` stream can be created for a particular signal number
376 /// multiple times. When a signal is received then all the associated
377 /// channels will receive the signal notification.
378 ///
379 /// # Errors
380 ///
381 /// * If the lower-level C functions fail for some reason.
382 /// * If the previous initialization of this specific signal failed.
383 /// * If the signal is one of
384 /// [`signal_hook::FORBIDDEN`](fn@signal_hook_registry::register#panics)
385 ///
386 /// # Panics
387 ///
388 /// This function panics if there is no current reactor set, or if the `rt`
389 /// feature flag is not enabled.
390 #[track_caller]
signal(kind: SignalKind) -> io::Result<Signal>391 pub fn signal(kind: SignalKind) -> io::Result<Signal> {
392 let handle = scheduler::Handle::current();
393 let rx = signal_with_handle(kind, handle.driver().signal())?;
394
395 Ok(Signal {
396 inner: RxFuture::new(rx),
397 })
398 }
399
signal_with_handle( kind: SignalKind, handle: &Handle, ) -> io::Result<watch::Receiver<()>>400 pub(crate) fn signal_with_handle(
401 kind: SignalKind,
402 handle: &Handle,
403 ) -> io::Result<watch::Receiver<()>> {
404 // Turn the signal delivery on once we are ready for it
405 signal_enable(kind, handle)?;
406
407 Ok(globals().register_listener(kind.0 as EventId))
408 }
409
410 impl Signal {
411 /// Receives the next signal notification event.
412 ///
413 /// `None` is returned if no more events can be received by this stream.
414 ///
415 /// # Cancel safety
416 ///
417 /// This method is cancel safe. If you use it as the event in a
418 /// [`tokio::select!`](crate::select) statement and some other branch
419 /// completes first, then it is guaranteed that no signal is lost.
420 ///
421 /// # Examples
422 ///
423 /// Wait for SIGHUP
424 ///
425 /// ```rust,no_run
426 /// use tokio::signal::unix::{signal, SignalKind};
427 ///
428 /// #[tokio::main]
429 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
430 /// // An infinite stream of hangup signals.
431 /// let mut stream = signal(SignalKind::hangup())?;
432 ///
433 /// // Print whenever a HUP signal is received
434 /// loop {
435 /// stream.recv().await;
436 /// println!("got signal HUP");
437 /// }
438 /// }
439 /// ```
recv(&mut self) -> Option<()>440 pub async fn recv(&mut self) -> Option<()> {
441 self.inner.recv().await
442 }
443
444 /// Polls to receive the next signal notification event, outside of an
445 /// `async` context.
446 ///
447 /// This method returns:
448 ///
449 /// * `Poll::Pending` if no signals are available but the channel is not
450 /// closed.
451 /// * `Poll::Ready(Some(()))` if a signal is available.
452 /// * `Poll::Ready(None)` if the channel has been closed and all signals
453 /// sent before it was closed have been received.
454 ///
455 /// # Examples
456 ///
457 /// Polling from a manually implemented future
458 ///
459 /// ```rust,no_run
460 /// use std::pin::Pin;
461 /// use std::future::Future;
462 /// use std::task::{Context, Poll};
463 /// use tokio::signal::unix::Signal;
464 ///
465 /// struct MyFuture {
466 /// signal: Signal,
467 /// }
468 ///
469 /// impl Future for MyFuture {
470 /// type Output = Option<()>;
471 ///
472 /// fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
473 /// println!("polling MyFuture");
474 /// self.signal.poll_recv(cx)
475 /// }
476 /// }
477 /// ```
poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>>478 pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
479 self.inner.poll_recv(cx)
480 }
481 }
482
483 // Work around for abstracting streams internally
484 pub(crate) trait InternalStream {
poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>>485 fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>>;
486 }
487
488 impl InternalStream for Signal {
poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>>489 fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
490 self.poll_recv(cx)
491 }
492 }
493
ctrl_c() -> io::Result<Signal>494 pub(crate) fn ctrl_c() -> io::Result<Signal> {
495 signal(SignalKind::interrupt())
496 }
497
498 #[cfg(all(test, not(loom)))]
499 mod tests {
500 use super::*;
501
502 #[test]
signal_enable_error_on_invalid_input()503 fn signal_enable_error_on_invalid_input() {
504 signal_enable(SignalKind::from_raw(-1), &Handle::default()).unwrap_err();
505 }
506
507 #[test]
signal_enable_error_on_forbidden_input()508 fn signal_enable_error_on_forbidden_input() {
509 signal_enable(
510 SignalKind::from_raw(signal_hook_registry::FORBIDDEN[0]),
511 &Handle::default(),
512 )
513 .unwrap_err();
514 }
515
516 #[test]
from_c_int()517 fn from_c_int() {
518 assert_eq!(SignalKind::from(2), SignalKind::interrupt());
519 }
520
521 #[test]
into_c_int()522 fn into_c_int() {
523 let value: std::os::raw::c_int = SignalKind::interrupt().into();
524 assert_eq!(value, libc::SIGINT as _);
525 }
526 }
527