signal_hook_async_std/
lib.rs

1#![doc(test(attr(deny(warnings))))]
2#![warn(missing_docs)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5//! A module for integrating signal handling with the async-std runtime.
6//!
7//! This provides the [`Signals`] struct which acts as a
8//! [`Stream`] of signals.
9//!
10//! # Example
11//!
12//! ```rust
13//! use std::io::Error;
14//!
15//! use async_std::prelude::*;
16//!
17//! use signal_hook::consts::signal::*;
18//! use signal_hook_async_std::Signals;
19//!
20//! async fn handle_signals(mut signals: Signals) {
21//!     while let Some(signal) = signals.next().await {
22//!         match signal {
23//!             SIGHUP => {
24//!                 // Reload configuration
25//!                 // Reopen the log file
26//!             }
27//!             SIGTERM | SIGINT | SIGQUIT => {
28//!                 // Shutdown the system;
29//!             },
30//!             _ => unreachable!(),
31//!         }
32//!     }
33//! }
34//!
35//! #[async_std::main]
36//! async fn main() -> Result<(), Error> {
37//!     let signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])?;
38//!     let handle = signals.handle();
39//!
40//!     let signals_task = async_std::task::spawn(handle_signals(signals));
41//!
42//!     // Execute your main program logic
43//!
44//!     // Terminate the signal stream.
45//!     handle.close();
46//!     signals_task.await;
47//!
48//!     Ok(())
49//! }
50//! ```
51
52use std::borrow::Borrow;
53use std::io::Error;
54use std::os::unix::net::UnixStream;
55use std::pin::Pin;
56use std::task::{Context, Poll};
57
58use libc::c_int;
59
60pub use signal_hook::iterator::backend::Handle;
61use signal_hook::iterator::backend::{OwningSignalIterator, PollResult, SignalDelivery};
62use signal_hook::iterator::exfiltrator::{Exfiltrator, SignalOnly};
63
64use async_io::Async;
65use futures_lite::io::AsyncRead;
66use futures_lite::stream::Stream;
67
68/// An asynchronous [`Stream`] of arriving signals.
69///
70/// The stream doesn't return the signals in the order they were recieved by
71/// the process and may merge signals received multiple times.
72pub struct SignalsInfo<E: Exfiltrator = SignalOnly>(OwningSignalIterator<Async<UnixStream>, E>);
73
74impl<E: Exfiltrator> SignalsInfo<E> {
75    /// Create a `Signals` instance.
76    ///
77    /// This registers all the signals listed. The same restrictions (panics, errors) apply
78    /// as with [`Handle::add_signal`].
79    pub fn new<I, S>(signals: I) -> Result<Self, Error>
80    where
81        I: IntoIterator<Item = S>,
82        S: Borrow<c_int>,
83        E: Default,
84    {
85        Self::with_exfiltrator(signals, E::default())
86    }
87
88    /// A constructor with explicit exfiltrator.
89    pub fn with_exfiltrator<I, S>(signals: I, exfiltrator: E) -> Result<Self, Error>
90    where
91        I: IntoIterator<Item = S>,
92        S: Borrow<c_int>,
93    {
94        let (read, write) = Async::<UnixStream>::pair()?;
95        let inner = SignalDelivery::with_pipe(read, write, exfiltrator, signals)?;
96        Ok(Self(OwningSignalIterator::new(inner)))
97    }
98
99    /// Get a shareable [`Handle`] for this `Signals` instance.
100    ///
101    /// This can be used to add further signals or close the [`Signals`] instance
102    /// which terminates the whole signal stream.
103    pub fn handle(&self) -> Handle {
104        self.0.handle()
105    }
106}
107
108impl<E: Exfiltrator> SignalsInfo<E> {
109    fn has_signals(read: &mut Async<UnixStream>, ctx: &mut Context<'_>) -> Result<bool, Error> {
110        match Pin::new(read).poll_read(ctx, &mut [0u8]) {
111            Poll::Pending => Ok(false),
112            Poll::Ready(Ok(num_read)) => Ok(num_read > 0),
113            Poll::Ready(Err(error)) => Err(error),
114        }
115    }
116}
117
118impl<E: Exfiltrator> Stream for SignalsInfo<E> {
119    type Item = E::Output;
120
121    fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
122        match self.0.poll_signal(&mut |read| Self::has_signals(read, ctx)) {
123            PollResult::Signal(sig) => Poll::Ready(Some(sig)),
124            PollResult::Closed => Poll::Ready(None),
125            PollResult::Pending => Poll::Pending,
126            PollResult::Err(error) => panic!("Unexpected error: {}", error),
127        }
128    }
129}
130
131/// Simplified version of the signals stream.
132///
133/// This one simply returns the signal numbers, while [`SignalsInfo`] can provide additional
134/// information.
135pub type Signals = SignalsInfo<SignalOnly>;