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