| 1 | //! Asynchronous signal handling. |
| 2 | //! |
| 3 | //! This crate provides the [`Signals`] type, which can be used to listen for POSIX signals asynchronously. |
| 4 | //! It can be seen as an asynchronous version of [`signal_hook::iterator::Signals`]. |
| 5 | //! |
| 6 | //! [`signal_hook::iterator::Signals`]: https://docs.rs/signal-hook/latest/signal_hook/iterator/struct.Signals.html |
| 7 | //! |
| 8 | //! # Implementation |
| 9 | //! |
| 10 | //! This crate uses the [`signal_hook_registry`] crate to register a listener for each signal. That |
| 11 | //! listener will then send a message through a Unix socket to the [`Signals`] type, which will |
| 12 | //! receive it and notify the user. Asynchronous notification is done through the [`async-io`] crate. |
| 13 | //! |
| 14 | //! Note that the internal pipe has a limited capacity. Once it has reached capacity, additional |
| 15 | //! signals will be dropped. |
| 16 | //! |
| 17 | //! On Windows, a different implementation that only supports `SIGINT` is used. This implementation |
| 18 | //! uses a channel to notify the user. |
| 19 | //! |
| 20 | //! [`signal_hook_registry`]: https://crates.io/crates/signal-hook-registry |
| 21 | //! [`async-io`]: https://crates.io/crates/async-io |
| 22 | //! |
| 23 | //! # Examples |
| 24 | //! |
| 25 | //! ```no_run |
| 26 | //! use async_signal::{Signal, Signals}; |
| 27 | //! use futures_lite::prelude::*; |
| 28 | //! use signal_hook::low_level; |
| 29 | //! |
| 30 | //! # fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 31 | //! # async_io::block_on(async { |
| 32 | //! // Register the signals we want to receive. |
| 33 | //! let mut signals = Signals::new(&[ |
| 34 | //! Signal::Term, |
| 35 | //! Signal::Quit, |
| 36 | //! Signal::Int, |
| 37 | //! ])?; |
| 38 | //! |
| 39 | //! // Wait for a signal to be received. |
| 40 | //! while let Some(signal) = signals.next().await { |
| 41 | //! // Print the signal. |
| 42 | //! eprintln!("Received signal {:?}" , signal); |
| 43 | //! |
| 44 | //! // After printing it, do whatever the signal was supposed to do in the first place. |
| 45 | //! low_level::emulate_default_handler(signal.unwrap() as i32).unwrap(); |
| 46 | //! } |
| 47 | //! # Ok(()) |
| 48 | //! # }) |
| 49 | //! # } |
| 50 | //! ``` |
| 51 | |
| 52 | #![doc ( |
| 53 | html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" |
| 54 | )] |
| 55 | #![doc ( |
| 56 | html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" |
| 57 | )] |
| 58 | |
| 59 | cfg_if::cfg_if! { |
| 60 | if #[cfg(windows)] { |
| 61 | mod channel; |
| 62 | use channel as sys; |
| 63 | } else { |
| 64 | mod pipe; |
| 65 | use pipe as sys; |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | cfg_if::cfg_if! { |
| 70 | if #[cfg(unix)] { |
| 71 | use signal_hook_registry as registry; |
| 72 | } else if #[cfg(windows)] { |
| 73 | mod windows_registry; |
| 74 | use windows_registry as registry; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | use futures_core::ready; |
| 79 | use futures_core::stream::Stream; |
| 80 | use registry::SigId; |
| 81 | |
| 82 | use std::borrow::Borrow; |
| 83 | use std::collections::HashMap; |
| 84 | use std::fmt; |
| 85 | use std::io; |
| 86 | use std::pin::Pin; |
| 87 | use std::task::{Context, Poll}; |
| 88 | |
| 89 | #[cfg (unix)] |
| 90 | use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; |
| 91 | |
| 92 | mod signum { |
| 93 | pub(crate) use std::os::raw::c_int; |
| 94 | |
| 95 | macro_rules! sig { |
| 96 | ($rustix_name:ident, $raw_value:literal) => {{ |
| 97 | #[cfg(unix)] |
| 98 | { |
| 99 | rustix::process::Signal::$rustix_name as c_int |
| 100 | } |
| 101 | |
| 102 | #[cfg(windows)] |
| 103 | { |
| 104 | $raw_value |
| 105 | } |
| 106 | }}; |
| 107 | } |
| 108 | |
| 109 | // Define these ourselves. |
| 110 | pub const SIGHUP: c_int = sig!(Hup, 1); |
| 111 | pub const SIGINT: c_int = sig!(Int, 2); |
| 112 | pub const SIGQUIT: c_int = sig!(Quit, 3); |
| 113 | pub const SIGILL: c_int = sig!(Ill, 4); |
| 114 | pub const SIGTRAP: c_int = sig!(Trap, 5); |
| 115 | pub const SIGABRT: c_int = sig!(Abort, 6); |
| 116 | pub const SIGFPE: c_int = sig!(Fpe, 8); |
| 117 | pub const SIGKILL: c_int = sig!(Kill, 9); |
| 118 | pub const SIGSEGV: c_int = sig!(Segv, 11); |
| 119 | pub const SIGPIPE: c_int = sig!(Pipe, 13); |
| 120 | pub const SIGALRM: c_int = sig!(Alarm, 14); |
| 121 | pub const SIGTERM: c_int = sig!(Term, 15); |
| 122 | pub const SIGTTIN: c_int = sig!(Ttin, 21); |
| 123 | pub const SIGTTOU: c_int = sig!(Ttou, 22); |
| 124 | pub const SIGXCPU: c_int = sig!(Xcpu, 24); |
| 125 | pub const SIGXFSZ: c_int = sig!(Xfsz, 25); |
| 126 | pub const SIGVTALRM: c_int = sig!(Vtalarm, 26); |
| 127 | pub const SIGPROF: c_int = sig!(Prof, 27); |
| 128 | pub const SIGWINCH: c_int = sig!(Winch, 28); |
| 129 | pub const SIGCHLD: c_int = sig!(Child, 17); |
| 130 | pub const SIGBUS: c_int = sig!(Bus, 7); |
| 131 | pub const SIGUSR1: c_int = sig!(Usr1, 10); |
| 132 | pub const SIGUSR2: c_int = sig!(Usr2, 12); |
| 133 | pub const SIGCONT: c_int = sig!(Cont, 18); |
| 134 | pub const SIGSTOP: c_int = sig!(Stop, 19); |
| 135 | pub const SIGTSTP: c_int = sig!(Tstp, 20); |
| 136 | pub const SIGURG: c_int = sig!(Urg, 23); |
| 137 | pub const SIGIO: c_int = sig!(Io, 29); |
| 138 | pub const SIGSYS: c_int = sig!(Sys, 31); |
| 139 | } |
| 140 | |
| 141 | macro_rules! define_signal_enum { |
| 142 | ( |
| 143 | $(#[$outer:meta])* |
| 144 | pub enum Signal { |
| 145 | $( |
| 146 | $(#[$inner:meta])* |
| 147 | $name:ident = $value:ident, |
| 148 | )* |
| 149 | } |
| 150 | ) => { |
| 151 | $(#[$outer])* |
| 152 | #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] |
| 153 | #[repr(i32)] |
| 154 | pub enum Signal { |
| 155 | $( |
| 156 | $(#[$inner])* |
| 157 | $name = signum::$value, |
| 158 | )* |
| 159 | } |
| 160 | |
| 161 | impl Signal { |
| 162 | /// Returns the signal number. |
| 163 | fn number(self) -> std::os::raw::c_int { |
| 164 | match self { |
| 165 | $( |
| 166 | Signal::$name => signum::$value, |
| 167 | )* |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | /// Parse a signal from its number. |
| 172 | #[cfg(unix)] |
| 173 | fn from_number(number: std::os::raw::c_int) -> Option<Self> { |
| 174 | match number { |
| 175 | $( |
| 176 | signum::$value => Some(Signal::$name), |
| 177 | )* |
| 178 | _ => None, |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | define_signal_enum! { |
| 186 | // Copied from https://github.com/bytecodealliance/rustix/blob/main/src/backend/linux_raw/process/types.rs#L81-L161 |
| 187 | |
| 188 | /// The signal types that we are able to listen for. |
| 189 | pub enum Signal { |
| 190 | /// `SIGHUP` |
| 191 | Hup = SIGHUP, |
| 192 | /// `SIGINT` |
| 193 | Int = SIGINT, |
| 194 | /// `SIGQUIT` |
| 195 | Quit = SIGQUIT, |
| 196 | /// `SIGILL` |
| 197 | Ill = SIGILL, |
| 198 | /// `SIGTRAP` |
| 199 | Trap = SIGTRAP, |
| 200 | /// `SIGABRT`, aka `SIGIOT` |
| 201 | #[doc (alias = "Iot" )] |
| 202 | #[doc (alias = "Abrt" )] |
| 203 | Abort = SIGABRT, |
| 204 | /// `SIGBUS` |
| 205 | Bus = SIGBUS, |
| 206 | /// `SIGFPE` |
| 207 | Fpe = SIGFPE, |
| 208 | /// `SIGKILL` |
| 209 | Kill = SIGKILL, |
| 210 | /// `SIGUSR1` |
| 211 | Usr1 = SIGUSR1, |
| 212 | /// `SIGSEGV` |
| 213 | Segv = SIGSEGV, |
| 214 | /// `SIGUSR2` |
| 215 | Usr2 = SIGUSR2, |
| 216 | /// `SIGPIPE` |
| 217 | Pipe = SIGPIPE, |
| 218 | /// `SIGALRM` |
| 219 | #[doc (alias = "Alrm" )] |
| 220 | Alarm = SIGALRM, |
| 221 | /// `SIGTERM` |
| 222 | Term = SIGTERM, |
| 223 | /// `SIGCHLD` |
| 224 | #[doc (alias = "Chld" )] |
| 225 | Child = SIGCHLD, |
| 226 | /// `SIGCONT` |
| 227 | Cont = SIGCONT, |
| 228 | /// `SIGSTOP` |
| 229 | Stop = SIGSTOP, |
| 230 | /// `SIGTSTP` |
| 231 | Tstp = SIGTSTP, |
| 232 | /// `SIGTTIN` |
| 233 | Ttin = SIGTTIN, |
| 234 | /// `SIGTTOU` |
| 235 | Ttou = SIGTTOU, |
| 236 | /// `SIGURG` |
| 237 | Urg = SIGURG, |
| 238 | /// `SIGXCPU` |
| 239 | Xcpu = SIGXCPU, |
| 240 | /// `SIGXFSZ` |
| 241 | Xfsz = SIGXFSZ, |
| 242 | /// `SIGVTALRM` |
| 243 | #[doc (alias = "Vtalrm" )] |
| 244 | Vtalarm = SIGVTALRM, |
| 245 | /// `SIGPROF` |
| 246 | Prof = SIGPROF, |
| 247 | /// `SIGWINCH` |
| 248 | Winch = SIGWINCH, |
| 249 | /// `SIGIO`, aka `SIGPOLL` |
| 250 | #[doc (alias = "Poll" )] |
| 251 | Io = SIGIO, |
| 252 | /// `SIGSYS`, aka `SIGUNUSED` |
| 253 | #[doc (alias = "Unused" )] |
| 254 | Sys = SIGSYS, |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | /// Wait for a specific set of signals. |
| 259 | /// |
| 260 | /// See the [module-level documentation](index.html) for more details. |
| 261 | pub struct Signals { |
| 262 | /// The strategy used to read the signals. |
| 263 | notifier: sys::Notifier, |
| 264 | |
| 265 | /// The map between signal numbers and signal IDs. |
| 266 | signal_ids: HashMap<Signal, SigId>, |
| 267 | } |
| 268 | |
| 269 | impl Drop for Signals { |
| 270 | fn drop(&mut self) { |
| 271 | for signal: &SigId in self.signal_ids.values() { |
| 272 | registry::unregister(*signal); |
| 273 | } |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | impl fmt::Debug for Signals { |
| 278 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 279 | struct RegisteredSignals<'a>(&'a HashMap<Signal, SigId>); |
| 280 | |
| 281 | impl fmt::Debug for RegisteredSignals<'_> { |
| 282 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 283 | f.debug_set().entries(self.0.keys()).finish() |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | f&mut DebugStruct<'_, '_>.debug_struct("Signals" ) |
| 288 | .field("notifier" , &self.notifier) |
| 289 | .field(name:"signal_ids" , &RegisteredSignals(&self.signal_ids)) |
| 290 | .finish() |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | impl Signals { |
| 295 | /// Create a new `Signals` instance with a set of signals. |
| 296 | pub fn new<B>(signals: impl IntoIterator<Item = B>) -> io::Result<Self> |
| 297 | where |
| 298 | B: Borrow<Signal>, |
| 299 | { |
| 300 | let mut this = Self { |
| 301 | notifier: sys::Notifier::new()?, |
| 302 | signal_ids: HashMap::new(), |
| 303 | }; |
| 304 | |
| 305 | // Add the signals to the set of signals to wait for. |
| 306 | this.add_signals(signals)?; |
| 307 | |
| 308 | Ok(this) |
| 309 | } |
| 310 | |
| 311 | /// Add signals to the set of signals to wait for. |
| 312 | /// |
| 313 | /// One signal cannot be added twice. If a signal that has already been added is passed to this |
| 314 | /// method, it will be ignored. |
| 315 | /// |
| 316 | /// Registering a signal prevents the default behavior of that signal from occurring. For |
| 317 | /// example, if you register `SIGINT`, pressing `Ctrl+C` will no longer terminate the process. |
| 318 | /// To run the default signal handler, use [`signal_hook::low_level::emulate_default_handler`] |
| 319 | /// instead. |
| 320 | /// |
| 321 | /// [`signal_hook::low_level::emulate_default_handler`]: https://docs.rs/signal-hook/latest/signal_hook/low_level/fn.emulate_default_handler.html |
| 322 | pub fn add_signals<B>(&mut self, signals: impl IntoIterator<Item = B>) -> io::Result<()> |
| 323 | where |
| 324 | B: Borrow<Signal>, |
| 325 | { |
| 326 | for signal in signals { |
| 327 | let signal = signal.borrow(); |
| 328 | |
| 329 | // If we've already registered this signal, skip it. |
| 330 | if self.signal_ids.contains_key(signal) { |
| 331 | continue; |
| 332 | } |
| 333 | |
| 334 | // Get the closure to call when the signal is received. |
| 335 | let closure = self.notifier.add_signal(*signal)?; |
| 336 | |
| 337 | let id = unsafe { |
| 338 | // SAFETY: Closure is guaranteed to be signal-safe. |
| 339 | registry::register(signal.number(), closure)? |
| 340 | }; |
| 341 | |
| 342 | // Add the signal ID to the map. |
| 343 | self.signal_ids.insert(*signal, id); |
| 344 | } |
| 345 | |
| 346 | Ok(()) |
| 347 | } |
| 348 | |
| 349 | /// Remove signals from the set of signals to wait for. |
| 350 | /// |
| 351 | /// This function can be used to opt out of listening to signals previously registered via |
| 352 | /// [`add_signals`](Self::add_signals) or [`new`](Self::new). If a signal that has not been |
| 353 | /// registered is passed to this method, it will be ignored. |
| 354 | pub fn remove_signals<B>(&mut self, signals: impl IntoIterator<Item = B>) -> io::Result<()> |
| 355 | where |
| 356 | B: Borrow<Signal>, |
| 357 | { |
| 358 | for signal in signals { |
| 359 | let signal = signal.borrow(); |
| 360 | |
| 361 | // If we haven't registered this signal, skip it. |
| 362 | let id = match self.signal_ids.remove(signal) { |
| 363 | Some(id) => id, |
| 364 | None => continue, |
| 365 | }; |
| 366 | |
| 367 | // Remove the signal from the notifier. |
| 368 | self.notifier.remove_signal(*signal)?; |
| 369 | |
| 370 | // Use `signal-hook-registry` to unregister the signal. |
| 371 | registry::unregister(id); |
| 372 | } |
| 373 | |
| 374 | Ok(()) |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | #[cfg (unix)] |
| 379 | impl AsRawFd for Signals { |
| 380 | fn as_raw_fd(&self) -> RawFd { |
| 381 | self.notifier.as_raw_fd() |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | #[cfg (unix)] |
| 386 | impl AsFd for Signals { |
| 387 | fn as_fd(&self) -> BorrowedFd<'_> { |
| 388 | self.notifier.as_fd() |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | impl Unpin for Signals {} |
| 393 | |
| 394 | impl Stream for Signals { |
| 395 | type Item = io::Result<Signal>; |
| 396 | |
| 397 | #[inline ] |
| 398 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { |
| 399 | Pin::new(&mut &*self).poll_next(cx) |
| 400 | } |
| 401 | |
| 402 | #[inline ] |
| 403 | fn size_hint(&self) -> (usize, Option<usize>) { |
| 404 | // This stream is expected to never end. |
| 405 | (usize::MAX, None) |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | impl Stream for &Signals { |
| 410 | type Item = io::Result<Signal>; |
| 411 | |
| 412 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { |
| 413 | let signal: Signal = ready!(self.notifier.poll_next(cx))?; |
| 414 | Poll::Ready(Some(Ok(signal))) |
| 415 | } |
| 416 | |
| 417 | #[inline ] |
| 418 | fn size_hint(&self) -> (usize, Option<usize>) { |
| 419 | // This stream is expected to never end. |
| 420 | (usize::MAX, None) |
| 421 | } |
| 422 | } |
| 423 | |