/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/src/signal/unix.rs
Line | Count | Source |
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, 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::OnceLock; |
18 | | use std::task::{Context, Poll}; |
19 | | |
20 | | #[cfg(not(any(target_os = "linux", target_os = "illumos")))] |
21 | | pub(crate) struct OsStorage([SignalInfo; 33]); |
22 | | |
23 | | #[cfg(any(target_os = "linux", target_os = "illumos"))] |
24 | | pub(crate) struct OsStorage(Box<[SignalInfo]>); |
25 | | |
26 | | impl OsStorage { |
27 | 0 | fn get(&self, id: EventId) -> Option<&SignalInfo> { |
28 | 0 | self.0.get(id - 1) |
29 | 0 | } |
30 | | } |
31 | | |
32 | | impl Default for OsStorage { |
33 | 2 | 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 | 2 | let inner = std::iter::repeat_with(SignalInfo::default) |
43 | 2 | .take(libc::SIGRTMAX() as usize) |
44 | 2 | .collect(); |
45 | | |
46 | 2 | Self(inner) |
47 | 2 | } |
48 | | } |
49 | | |
50 | | impl Storage for OsStorage { |
51 | 0 | fn event_info(&self, id: EventId) -> Option<&EventInfo> { |
52 | 0 | self.get(id).map(|si| &si.event_info) |
53 | 0 | } |
54 | | |
55 | 0 | fn for_each<'a, F>(&'a self, f: F) |
56 | 0 | where |
57 | 0 | F: FnMut(&'a EventInfo), |
58 | | { |
59 | 0 | self.0.iter().map(|si| &si.event_info).for_each(f); |
60 | 0 | } |
61 | | } |
62 | | |
63 | | #[derive(Debug)] |
64 | | pub(crate) struct OsExtraData { |
65 | | sender: UnixStream, |
66 | | pub(crate) receiver: UnixStream, |
67 | | } |
68 | | |
69 | | impl Default for OsExtraData { |
70 | 2 | fn default() -> Self { |
71 | 2 | let (receiver, sender) = UnixStream::pair().expect("failed to create UnixStream"); |
72 | | |
73 | 2 | Self { sender, receiver } |
74 | 2 | } |
75 | | } |
76 | | |
77 | | /// Represents the specific kind of signal to listen for. |
78 | | #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] |
79 | | pub struct SignalKind(libc::c_int); |
80 | | |
81 | | impl 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 | 0 | pub const fn from_raw(signum: std::os::raw::c_int) -> Self { |
99 | 0 | Self(signum as libc::c_int) |
100 | 0 | } |
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 | 0 | pub const fn as_raw_value(&self) -> std::os::raw::c_int { |
110 | 0 | self.0 |
111 | 0 | } |
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 | 0 | pub const fn alarm() -> Self { |
118 | 0 | Self(libc::SIGALRM) |
119 | 0 | } |
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 | 0 | pub const fn child() -> Self { |
126 | 0 | Self(libc::SIGCHLD) |
127 | 0 | } |
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 | 0 | pub const fn hangup() -> Self { |
134 | 0 | Self(libc::SIGHUP) |
135 | 0 | } |
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 | 0 | pub const fn interrupt() -> Self { |
158 | 0 | Self(libc::SIGINT) |
159 | 0 | } |
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 | 0 | pub const fn io() -> Self { |
175 | 0 | Self(libc::SIGIO) |
176 | 0 | } |
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 | 0 | pub const fn pipe() -> Self { |
184 | 0 | Self(libc::SIGPIPE) |
185 | 0 | } |
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 | 0 | pub const fn quit() -> Self { |
193 | 0 | Self(libc::SIGQUIT) |
194 | 0 | } |
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 | 0 | pub const fn terminate() -> Self { |
201 | 0 | Self(libc::SIGTERM) |
202 | 0 | } |
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 | 0 | pub const fn user_defined1() -> Self { |
209 | 0 | Self(libc::SIGUSR1) |
210 | 0 | } |
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 | 0 | pub const fn user_defined2() -> Self { |
217 | 0 | Self(libc::SIGUSR2) |
218 | 0 | } |
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 | 0 | pub const fn window_change() -> Self { |
225 | 0 | Self(libc::SIGWINCH) |
226 | 0 | } |
227 | | } |
228 | | |
229 | | impl From<std::os::raw::c_int> for SignalKind { |
230 | 0 | fn from(signum: std::os::raw::c_int) -> Self { |
231 | 0 | Self::from_raw(signum as libc::c_int) |
232 | 0 | } |
233 | | } |
234 | | |
235 | | impl From<SignalKind> for std::os::raw::c_int { |
236 | 0 | fn from(kind: SignalKind) -> Self { |
237 | 0 | kind.as_raw_value() |
238 | 0 | } |
239 | | } |
240 | | |
241 | | #[derive(Default)] |
242 | | pub(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. |
255 | 0 | fn action(globals: &'static Globals, signal: libc::c_int) { |
256 | 0 | 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 | 0 | let mut sender = &globals.sender; |
261 | 0 | drop(sender.write(&[1])); |
262 | 0 | } |
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. |
269 | 0 | fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> { |
270 | 0 | let signal = signal.0; |
271 | 0 | if signal <= 0 || signal_hook_registry::FORBIDDEN.contains(&signal) { |
272 | 0 | return Err(Error::new( |
273 | 0 | ErrorKind::Other, |
274 | 0 | format!("Refusing to register signal {signal}"), |
275 | 0 | )); |
276 | 0 | } |
277 | | |
278 | | // Check that we have a signal driver running |
279 | 0 | handle.check_inner()?; |
280 | | |
281 | 0 | let globals = globals(); |
282 | 0 | let siginfo = match globals.storage().get(signal as EventId) { |
283 | 0 | Some(slot) => slot, |
284 | 0 | None => return Err(io::Error::new(io::ErrorKind::Other, "signal too large")), |
285 | | }; |
286 | | |
287 | 0 | siginfo |
288 | 0 | .init |
289 | 0 | .get_or_init(|| { |
290 | 0 | unsafe { signal_hook_registry::register(signal, move || action(globals, signal)) } |
291 | 0 | .map(|_| ()) |
292 | 0 | .map_err(|e| e.raw_os_error()) |
293 | 0 | }) |
294 | 0 | .map_err(|e| { |
295 | 0 | e.map_or_else( |
296 | 0 | || Error::new(ErrorKind::Other, "registering signal handler failed"), |
297 | | Error::from_raw_os_error, |
298 | | ) |
299 | 0 | }) |
300 | 0 | } |
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)] |
370 | | pub 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] |
402 | 0 | pub fn signal(kind: SignalKind) -> io::Result<Signal> { |
403 | 0 | let handle = scheduler::Handle::current(); |
404 | 0 | let rx = signal_with_handle(kind, handle.driver().signal())?; |
405 | | |
406 | 0 | Ok(Signal { |
407 | 0 | inner: RxFuture::new(rx), |
408 | 0 | }) |
409 | 0 | } |
410 | | |
411 | 0 | pub(crate) fn signal_with_handle( |
412 | 0 | kind: SignalKind, |
413 | 0 | handle: &Handle, |
414 | 0 | ) -> io::Result<watch::Receiver<()>> { |
415 | | // Turn the signal delivery on once we are ready for it |
416 | 0 | signal_enable(kind, handle)?; |
417 | | |
418 | 0 | Ok(globals().register_listener(kind.0 as EventId)) |
419 | 0 | } |
420 | | |
421 | | impl 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 a branch in |
430 | | /// [`tokio::select!`](crate::select) and another 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 | 0 | pub async fn recv(&mut self) -> Option<()> { |
453 | 0 | self.inner.recv().await; |
454 | 0 | Some(()) |
455 | 0 | } |
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 | 0 | pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { |
487 | 0 | self.inner.poll_recv(cx).map(Some) |
488 | 0 | } |
489 | | } |
490 | | |
491 | | // Work around for abstracting streams internally |
492 | | #[cfg(feature = "process")] |
493 | | pub(crate) trait InternalStream { |
494 | | fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>>; |
495 | | } |
496 | | |
497 | | #[cfg(feature = "process")] |
498 | | impl InternalStream for Signal { |
499 | 0 | fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { |
500 | 0 | self.poll_recv(cx) |
501 | 0 | } |
502 | | } |
503 | | |
504 | 0 | pub(crate) fn ctrl_c() -> io::Result<Signal> { |
505 | 0 | signal(SignalKind::interrupt()) |
506 | 0 | } |
507 | | |
508 | | #[cfg(all(test, not(loom)))] |
509 | | mod 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 | | } |