| 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 { |
| 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 | |
| 37 | impl 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)] |
| 51 | pub(crate) struct OsExtraData { |
| 52 | sender: UnixStream, |
| 53 | pub(crate) receiver: UnixStream, |
| 54 | } |
| 55 | |
| 56 | impl 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)] |
| 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. |
| 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 | |
| 206 | impl 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 | |
| 212 | impl From<SignalKind> for std::os::raw::c_int { |
| 213 | 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 { |
| 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. |
| 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. |
| 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 | /// 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)] |
| 363 | pub 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 ] |
| 395 | pub 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 | |
| 404 | pub(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 | |
| 414 | impl 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 | pub(crate) trait InternalStream { |
| 489 | fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>>; |
| 490 | } |
| 491 | |
| 492 | impl InternalStream for Signal { |
| 493 | fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { |
| 494 | self.poll_recv(cx) |
| 495 | } |
| 496 | } |
| 497 | |
| 498 | pub(crate) fn ctrl_c() -> io::Result<Signal> { |
| 499 | signal(SignalKind::interrupt()) |
| 500 | } |
| 501 | |
| 502 | #[cfg (all(test, not(loom)))] |
| 503 | mod tests { |
| 504 | use super::*; |
| 505 | |
| 506 | #[test] |
| 507 | fn signal_enable_error_on_invalid_input() { |
| 508 | signal_enable(SignalKind::from_raw(-1), &Handle::default()).unwrap_err(); |
| 509 | } |
| 510 | |
| 511 | #[test] |
| 512 | fn signal_enable_error_on_forbidden_input() { |
| 513 | signal_enable( |
| 514 | SignalKind::from_raw(signal_hook_registry::FORBIDDEN[0]), |
| 515 | &Handle::default(), |
| 516 | ) |
| 517 | .unwrap_err(); |
| 518 | } |
| 519 | |
| 520 | #[test] |
| 521 | fn from_c_int() { |
| 522 | assert_eq!(SignalKind::from(2), SignalKind::interrupt()); |
| 523 | } |
| 524 | |
| 525 | #[test] |
| 526 | fn into_c_int() { |
| 527 | let value: std::os::raw::c_int = SignalKind::interrupt().into(); |
| 528 | assert_eq!(value, libc::SIGINT as _); |
| 529 | } |
| 530 | } |
| 531 | |