| 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 = Box<[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(any(target_os = "linux" , target_os = "illumos" )))] |
| 27 | let possible = 0..=33; |
| 28 | |
| 29 | // On Linux and illumos, there are additional real-time signals |
| 30 | // available. (This is also likely true on Solaris, but this should be |
| 31 | // verified before being enabled.) |
| 32 | #[cfg (any(target_os = "linux" , target_os = "illumos" ))] |
| 33 | let possible: RangeInclusive = 0..=libc::SIGRTMAX(); |
| 34 | |
| 35 | possible.map(|_| SignalInfo::default()).collect() |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | impl Storage for OsStorage { |
| 40 | fn event_info(&self, id: EventId) -> Option<&EventInfo> { |
| 41 | self.get(index:id).map(|si: &SignalInfo| &si.event_info) |
| 42 | } |
| 43 | |
| 44 | fn for_each<'a, F>(&'a self, f: F) |
| 45 | where |
| 46 | F: FnMut(&'a EventInfo), |
| 47 | { |
| 48 | self.iter().map(|si: &SignalInfo| &si.event_info).for_each(f); |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | #[derive (Debug)] |
| 53 | pub(crate) struct OsExtraData { |
| 54 | sender: UnixStream, |
| 55 | pub(crate) receiver: UnixStream, |
| 56 | } |
| 57 | |
| 58 | impl Init for OsExtraData { |
| 59 | fn init() -> Self { |
| 60 | let (receiver: UnixStream, sender: UnixStream) = UnixStream::pair().expect(msg:"failed to create UnixStream" ); |
| 61 | |
| 62 | Self { sender, receiver } |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | /// Represents the specific kind of signal to listen for. |
| 67 | #[derive (Debug, Clone, Copy, Hash, PartialEq, Eq)] |
| 68 | pub struct SignalKind(libc::c_int); |
| 69 | |
| 70 | impl SignalKind { |
| 71 | /// Allows for listening to any valid OS signal. |
| 72 | /// |
| 73 | /// For example, this can be used for listening for platform-specific |
| 74 | /// signals. |
| 75 | /// ```rust,no_run |
| 76 | /// # use tokio::signal::unix::SignalKind; |
| 77 | /// # let signum = -1; |
| 78 | /// // let signum = libc::OS_SPECIFIC_SIGNAL; |
| 79 | /// let kind = SignalKind::from_raw(signum); |
| 80 | /// ``` |
| 81 | // Use `std::os::raw::c_int` on public API to prevent leaking a non-stable |
| 82 | // type alias from libc. |
| 83 | // `libc::c_int` and `std::os::raw::c_int` are currently the same type, and are |
| 84 | // unlikely to change to other types, but technically libc can change this |
| 85 | // in the future minor version. |
| 86 | // See https://github.com/tokio-rs/tokio/issues/3767 for more. |
| 87 | pub const fn from_raw(signum: std::os::raw::c_int) -> Self { |
| 88 | Self(signum as libc::c_int) |
| 89 | } |
| 90 | |
| 91 | /// Get the signal's numeric value. |
| 92 | /// |
| 93 | /// ```rust |
| 94 | /// # use tokio::signal::unix::SignalKind; |
| 95 | /// let kind = SignalKind::interrupt(); |
| 96 | /// assert_eq!(kind.as_raw_value(), libc::SIGINT); |
| 97 | /// ``` |
| 98 | pub const fn as_raw_value(&self) -> std::os::raw::c_int { |
| 99 | self.0 |
| 100 | } |
| 101 | |
| 102 | /// Represents the `SIGALRM` signal. |
| 103 | /// |
| 104 | /// On Unix systems this signal is sent when a real-time timer has expired. |
| 105 | /// By default, the process is terminated by this signal. |
| 106 | pub const fn alarm() -> Self { |
| 107 | Self(libc::SIGALRM) |
| 108 | } |
| 109 | |
| 110 | /// Represents the `SIGCHLD` signal. |
| 111 | /// |
| 112 | /// On Unix systems this signal is sent when the status of a child process |
| 113 | /// has changed. By default, this signal is ignored. |
| 114 | pub const fn child() -> Self { |
| 115 | Self(libc::SIGCHLD) |
| 116 | } |
| 117 | |
| 118 | /// Represents the `SIGHUP` signal. |
| 119 | /// |
| 120 | /// On Unix systems this signal is sent when the terminal is disconnected. |
| 121 | /// By default, the process is terminated by this signal. |
| 122 | pub const fn hangup() -> Self { |
| 123 | Self(libc::SIGHUP) |
| 124 | } |
| 125 | |
| 126 | /// Represents the `SIGINFO` signal. |
| 127 | /// |
| 128 | /// On Unix systems this signal is sent to request a status update from the |
| 129 | /// process. By default, this signal is ignored. |
| 130 | #[cfg (any( |
| 131 | target_os = "dragonfly" , |
| 132 | target_os = "freebsd" , |
| 133 | target_os = "macos" , |
| 134 | target_os = "netbsd" , |
| 135 | target_os = "openbsd" , |
| 136 | target_os = "illumos" |
| 137 | ))] |
| 138 | pub const fn info() -> Self { |
| 139 | Self(libc::SIGINFO) |
| 140 | } |
| 141 | |
| 142 | /// Represents the `SIGINT` signal. |
| 143 | /// |
| 144 | /// On Unix systems this signal is sent to interrupt a program. |
| 145 | /// By default, the process is terminated by this signal. |
| 146 | pub const fn interrupt() -> Self { |
| 147 | Self(libc::SIGINT) |
| 148 | } |
| 149 | |
| 150 | #[cfg (target_os = "haiku" )] |
| 151 | /// Represents the `SIGPOLL` signal. |
| 152 | /// |
| 153 | /// On POSIX systems this signal is sent when I/O operations are possible |
| 154 | /// on some file descriptor. By default, this signal is ignored. |
| 155 | pub const fn io() -> Self { |
| 156 | Self(libc::SIGPOLL) |
| 157 | } |
| 158 | #[cfg (not(target_os = "haiku" ))] |
| 159 | /// Represents the `SIGIO` signal. |
| 160 | /// |
| 161 | /// On Unix 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::SIGIO) |
| 165 | } |
| 166 | |
| 167 | /// Represents the `SIGPIPE` signal. |
| 168 | /// |
| 169 | /// On Unix systems this signal is sent when the process attempts to write |
| 170 | /// to a pipe which has no reader. By default, the process is terminated by |
| 171 | /// this signal. |
| 172 | pub const fn pipe() -> Self { |
| 173 | Self(libc::SIGPIPE) |
| 174 | } |
| 175 | |
| 176 | /// Represents the `SIGQUIT` signal. |
| 177 | /// |
| 178 | /// On Unix systems this signal is sent to issue a shutdown of the |
| 179 | /// process, after which the OS will dump the process core. |
| 180 | /// By default, the process is terminated by this signal. |
| 181 | pub const fn quit() -> Self { |
| 182 | Self(libc::SIGQUIT) |
| 183 | } |
| 184 | |
| 185 | /// Represents the `SIGTERM` signal. |
| 186 | /// |
| 187 | /// On Unix systems this signal is sent to issue a shutdown of the |
| 188 | /// process. By default, the process is terminated by this signal. |
| 189 | pub const fn terminate() -> Self { |
| 190 | Self(libc::SIGTERM) |
| 191 | } |
| 192 | |
| 193 | /// Represents the `SIGUSR1` signal. |
| 194 | /// |
| 195 | /// On Unix systems this is a user defined signal. |
| 196 | /// By default, the process is terminated by this signal. |
| 197 | pub const fn user_defined1() -> Self { |
| 198 | Self(libc::SIGUSR1) |
| 199 | } |
| 200 | |
| 201 | /// Represents the `SIGUSR2` 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_defined2() -> Self { |
| 206 | Self(libc::SIGUSR2) |
| 207 | } |
| 208 | |
| 209 | /// Represents the `SIGWINCH` signal. |
| 210 | /// |
| 211 | /// On Unix systems this signal is sent when the terminal window is resized. |
| 212 | /// By default, this signal is ignored. |
| 213 | pub const fn window_change() -> Self { |
| 214 | Self(libc::SIGWINCH) |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | impl From<std::os::raw::c_int> for SignalKind { |
| 219 | fn from(signum: std::os::raw::c_int) -> Self { |
| 220 | Self::from_raw(signum as libc::c_int) |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | impl From<SignalKind> for std::os::raw::c_int { |
| 225 | fn from(kind: SignalKind) -> Self { |
| 226 | kind.as_raw_value() |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | pub(crate) struct SignalInfo { |
| 231 | event_info: EventInfo, |
| 232 | init: Once, |
| 233 | initialized: AtomicBool, |
| 234 | } |
| 235 | |
| 236 | impl Default for SignalInfo { |
| 237 | fn default() -> SignalInfo { |
| 238 | SignalInfo { |
| 239 | event_info: EventInfo::default(), |
| 240 | init: Once::new(), |
| 241 | initialized: AtomicBool::new(false), |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | /// Our global signal handler for all signals registered by this module. |
| 247 | /// |
| 248 | /// The purpose of this signal handler is to primarily: |
| 249 | /// |
| 250 | /// 1. Flag that our specific signal was received (e.g. store an atomic flag) |
| 251 | /// 2. Wake up the driver by writing a byte to a pipe |
| 252 | /// |
| 253 | /// Those two operations should both be async-signal safe. |
| 254 | fn action(globals: &'static Globals, signal: libc::c_int) { |
| 255 | globals.record_event(event_id:signal as EventId); |
| 256 | |
| 257 | // Send a wakeup, ignore any errors (anything reasonably possible is |
| 258 | // full pipe and then it will wake up anyway). |
| 259 | let mut sender: &UnixStream = &globals.sender; |
| 260 | drop(sender.write(&[1])); |
| 261 | } |
| 262 | |
| 263 | /// Enables this module to receive signal notifications for the `signal` |
| 264 | /// provided. |
| 265 | /// |
| 266 | /// This will register the signal handler if it hasn't already been registered, |
| 267 | /// returning any error along the way if that fails. |
| 268 | fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> { |
| 269 | let signal = signal.0; |
| 270 | if signal < 0 || signal_hook_registry::FORBIDDEN.contains(&signal) { |
| 271 | return Err(Error::new( |
| 272 | ErrorKind::Other, |
| 273 | format!("Refusing to register signal {signal}" ), |
| 274 | )); |
| 275 | } |
| 276 | |
| 277 | // Check that we have a signal driver running |
| 278 | handle.check_inner()?; |
| 279 | |
| 280 | let globals = globals(); |
| 281 | let siginfo = match globals.storage().get(signal as EventId) { |
| 282 | Some(slot) => slot, |
| 283 | None => return Err(io::Error::new(io::ErrorKind::Other, "signal too large" )), |
| 284 | }; |
| 285 | let mut registered = Ok(()); |
| 286 | siginfo.init.call_once(|| { |
| 287 | registered = unsafe { |
| 288 | signal_hook_registry::register(signal, move || action(globals, signal)).map(|_| ()) |
| 289 | }; |
| 290 | if registered.is_ok() { |
| 291 | siginfo.initialized.store(true, Ordering::Relaxed); |
| 292 | } |
| 293 | }); |
| 294 | registered?; |
| 295 | // If the call_once failed, it won't be retried on the next attempt to register the signal. In |
| 296 | // such case it is not run, registered is still `Ok(())`, initialized is still `false`. |
| 297 | if siginfo.initialized.load(Ordering::Relaxed) { |
| 298 | Ok(()) |
| 299 | } else { |
| 300 | Err(Error::new( |
| 301 | ErrorKind::Other, |
| 302 | "Failed to register signal handler" , |
| 303 | )) |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | /// An listener for receiving a particular type of OS signal. |
| 308 | /// |
| 309 | /// The listener can be turned into a `Stream` using [`SignalStream`]. |
| 310 | /// |
| 311 | /// [`SignalStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.SignalStream.html |
| 312 | /// |
| 313 | /// In general signal handling on Unix is a pretty tricky topic, and this |
| 314 | /// structure is no exception! There are some important limitations to keep in |
| 315 | /// mind when using `Signal` streams: |
| 316 | /// |
| 317 | /// * Signals handling in Unix already necessitates coalescing signals |
| 318 | /// together sometimes. This `Signal` stream is also no exception here in |
| 319 | /// that it will also coalesce signals. That is, even if the signal handler |
| 320 | /// for this process runs multiple times, the `Signal` stream may only return |
| 321 | /// one signal notification. Specifically, before `poll` is called, all |
| 322 | /// signal notifications are coalesced into one item returned from `poll`. |
| 323 | /// Once `poll` has been called, however, a further signal is guaranteed to |
| 324 | /// be yielded as an item. |
| 325 | /// |
| 326 | /// Put another way, any element pulled off the returned listener corresponds to |
| 327 | /// *at least one* signal, but possibly more. |
| 328 | /// |
| 329 | /// * Signal handling in general is relatively inefficient. Although some |
| 330 | /// improvements are possible in this crate, it's recommended to not plan on |
| 331 | /// having millions of signal channels open. |
| 332 | /// |
| 333 | /// If you've got any questions about this feel free to open an issue on the |
| 334 | /// repo! New approaches to alleviate some of these limitations are always |
| 335 | /// appreciated! |
| 336 | /// |
| 337 | /// # Caveats |
| 338 | /// |
| 339 | /// The first time that a `Signal` instance is registered for a particular |
| 340 | /// signal kind, an OS signal-handler is installed which replaces the default |
| 341 | /// platform behavior when that signal is received, **for the duration of the |
| 342 | /// entire process**. |
| 343 | /// |
| 344 | /// For example, Unix systems will terminate a process by default when it |
| 345 | /// receives `SIGINT`. But, when a `Signal` instance is created to listen for |
| 346 | /// this signal, the next `SIGINT` that arrives will be translated to a stream |
| 347 | /// event, and the process will continue to execute. **Even if this `Signal` |
| 348 | /// instance is dropped, subsequent `SIGINT` deliveries will end up captured by |
| 349 | /// Tokio, and the default platform behavior will NOT be reset**. |
| 350 | /// |
| 351 | /// Thus, applications should take care to ensure the expected signal behavior |
| 352 | /// occurs as expected after listening for specific signals. |
| 353 | /// |
| 354 | /// # Examples |
| 355 | /// |
| 356 | /// Wait for `SIGHUP` |
| 357 | /// |
| 358 | /// ```rust,no_run |
| 359 | /// use tokio::signal::unix::{signal, SignalKind}; |
| 360 | /// |
| 361 | /// #[tokio::main] |
| 362 | /// async fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 363 | /// // An infinite stream of hangup signals. |
| 364 | /// let mut sig = signal(SignalKind::hangup())?; |
| 365 | /// |
| 366 | /// // Print whenever a HUP signal is received |
| 367 | /// loop { |
| 368 | /// sig.recv().await; |
| 369 | /// println!("got signal HUP" ); |
| 370 | /// } |
| 371 | /// } |
| 372 | /// ``` |
| 373 | #[must_use = "streams do nothing unless polled" ] |
| 374 | #[derive (Debug)] |
| 375 | pub struct Signal { |
| 376 | inner: RxFuture, |
| 377 | } |
| 378 | |
| 379 | /// Creates a new listener which will receive notifications when the current |
| 380 | /// process receives the specified signal `kind`. |
| 381 | /// |
| 382 | /// This function will create a new stream which binds to the default reactor. |
| 383 | /// The `Signal` stream is an infinite stream which will receive |
| 384 | /// notifications whenever a signal is received. More documentation can be |
| 385 | /// found on `Signal` itself, but to reiterate: |
| 386 | /// |
| 387 | /// * Signals may be coalesced beyond what the kernel already does. |
| 388 | /// * Once a signal handler is registered with the process the underlying |
| 389 | /// libc signal handler is never unregistered. |
| 390 | /// |
| 391 | /// A `Signal` stream can be created for a particular signal number |
| 392 | /// multiple times. When a signal is received then all the associated |
| 393 | /// channels will receive the signal notification. |
| 394 | /// |
| 395 | /// # Errors |
| 396 | /// |
| 397 | /// * If the lower-level C functions fail for some reason. |
| 398 | /// * If the previous initialization of this specific signal failed. |
| 399 | /// * If the signal is one of |
| 400 | /// [`signal_hook::FORBIDDEN`](fn@signal_hook_registry::register#panics) |
| 401 | /// |
| 402 | /// # Panics |
| 403 | /// |
| 404 | /// This function panics if there is no current reactor set, or if the `rt` |
| 405 | /// feature flag is not enabled. |
| 406 | #[track_caller ] |
| 407 | pub fn signal(kind: SignalKind) -> io::Result<Signal> { |
| 408 | let handle: Handle = scheduler::Handle::current(); |
| 409 | let rx: Receiver<()> = signal_with_handle(kind, handle.driver().signal())?; |
| 410 | |
| 411 | Ok(Signal { |
| 412 | inner: RxFuture::new(rx), |
| 413 | }) |
| 414 | } |
| 415 | |
| 416 | pub(crate) fn signal_with_handle( |
| 417 | kind: SignalKind, |
| 418 | handle: &Handle, |
| 419 | ) -> io::Result<watch::Receiver<()>> { |
| 420 | // Turn the signal delivery on once we are ready for it |
| 421 | signal_enable(signal:kind, handle)?; |
| 422 | |
| 423 | Ok(globals().register_listener(event_id:kind.0 as EventId)) |
| 424 | } |
| 425 | |
| 426 | impl Signal { |
| 427 | /// Receives the next signal notification event. |
| 428 | /// |
| 429 | /// `None` is returned if no more events can be received by this stream. |
| 430 | /// |
| 431 | /// # Cancel safety |
| 432 | /// |
| 433 | /// This method is cancel safe. If you use it as the event in a |
| 434 | /// [`tokio::select!`](crate::select) statement and some other branch |
| 435 | /// completes first, then it is guaranteed that no signal is lost. |
| 436 | /// |
| 437 | /// # Examples |
| 438 | /// |
| 439 | /// Wait for `SIGHUP` |
| 440 | /// |
| 441 | /// ```rust,no_run |
| 442 | /// use tokio::signal::unix::{signal, SignalKind}; |
| 443 | /// |
| 444 | /// #[tokio::main] |
| 445 | /// async fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 446 | /// // An infinite stream of hangup signals. |
| 447 | /// let mut stream = signal(SignalKind::hangup())?; |
| 448 | /// |
| 449 | /// // Print whenever a HUP signal is received |
| 450 | /// loop { |
| 451 | /// stream.recv().await; |
| 452 | /// println!("got signal HUP" ); |
| 453 | /// } |
| 454 | /// } |
| 455 | /// ``` |
| 456 | pub async fn recv(&mut self) -> Option<()> { |
| 457 | self.inner.recv().await |
| 458 | } |
| 459 | |
| 460 | /// Polls to receive the next signal notification event, outside of an |
| 461 | /// `async` context. |
| 462 | /// |
| 463 | /// This method returns: |
| 464 | /// |
| 465 | /// * `Poll::Pending` if no signals are available but the channel is not |
| 466 | /// closed. |
| 467 | /// * `Poll::Ready(Some(()))` if a signal is available. |
| 468 | /// * `Poll::Ready(None)` if the channel has been closed and all signals |
| 469 | /// sent before it was closed have been received. |
| 470 | /// |
| 471 | /// # Examples |
| 472 | /// |
| 473 | /// Polling from a manually implemented future |
| 474 | /// |
| 475 | /// ```rust,no_run |
| 476 | /// use std::pin::Pin; |
| 477 | /// use std::future::Future; |
| 478 | /// use std::task::{Context, Poll}; |
| 479 | /// use tokio::signal::unix::Signal; |
| 480 | /// |
| 481 | /// struct MyFuture { |
| 482 | /// signal: Signal, |
| 483 | /// } |
| 484 | /// |
| 485 | /// impl Future for MyFuture { |
| 486 | /// type Output = Option<()>; |
| 487 | /// |
| 488 | /// fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 489 | /// println!("polling MyFuture" ); |
| 490 | /// self.signal.poll_recv(cx) |
| 491 | /// } |
| 492 | /// } |
| 493 | /// ``` |
| 494 | pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { |
| 495 | self.inner.poll_recv(cx) |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | // Work around for abstracting streams internally |
| 500 | #[cfg (feature = "process" )] |
| 501 | pub(crate) trait InternalStream { |
| 502 | fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>>; |
| 503 | } |
| 504 | |
| 505 | #[cfg (feature = "process" )] |
| 506 | impl InternalStream for Signal { |
| 507 | fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { |
| 508 | self.poll_recv(cx) |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | pub(crate) fn ctrl_c() -> io::Result<Signal> { |
| 513 | signal(kind:SignalKind::interrupt()) |
| 514 | } |
| 515 | |
| 516 | #[cfg (all(test, not(loom)))] |
| 517 | mod tests { |
| 518 | use super::*; |
| 519 | |
| 520 | #[test ] |
| 521 | fn signal_enable_error_on_invalid_input() { |
| 522 | signal_enable(SignalKind::from_raw(-1), &Handle::default()).unwrap_err(); |
| 523 | } |
| 524 | |
| 525 | #[test ] |
| 526 | fn signal_enable_error_on_forbidden_input() { |
| 527 | signal_enable( |
| 528 | SignalKind::from_raw(signal_hook_registry::FORBIDDEN[0]), |
| 529 | &Handle::default(), |
| 530 | ) |
| 531 | .unwrap_err(); |
| 532 | } |
| 533 | |
| 534 | #[test ] |
| 535 | fn from_c_int() { |
| 536 | assert_eq!(SignalKind::from(2), SignalKind::interrupt()); |
| 537 | } |
| 538 | |
| 539 | #[test ] |
| 540 | fn into_c_int() { |
| 541 | let value: std::os::raw::c_int = SignalKind::interrupt().into(); |
| 542 | assert_eq!(value, libc::SIGINT as _); |
| 543 | } |
| 544 | } |
| 545 | |