1 | //! A signal notifier that uses an asynchronous pipe. |
2 | |
3 | use crate::Signal; |
4 | |
5 | use async_io::Async; |
6 | use futures_core::ready; |
7 | use futures_io::AsyncRead; |
8 | |
9 | use std::io::{self, prelude::*}; |
10 | use std::mem; |
11 | use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; |
12 | use std::os::unix::net::UnixStream; |
13 | use std::pin::Pin; |
14 | use std::task::{Context, Poll}; |
15 | |
16 | const BUFFER_LEN: usize = mem::size_of::<std::os::raw::c_int>(); |
17 | |
18 | /// The notifier that uses an asynchronous pipe. |
19 | #[derive (Debug)] |
20 | pub(super) struct Notifier { |
21 | /// The read end of the signal pipe. |
22 | read: Async<UnixStream>, |
23 | |
24 | /// The write end of the signal pipe. |
25 | write: UnixStream, |
26 | } |
27 | |
28 | impl Notifier { |
29 | /// Create a new signal notifier. |
30 | pub(super) fn new() -> io::Result<Self> { |
31 | let (read, write) = UnixStream::pair()?; |
32 | let read = Async::new(read)?; |
33 | write.set_nonblocking(true)?; |
34 | |
35 | Ok(Self { read, write }) |
36 | } |
37 | |
38 | /// Add a signal to the notifier. |
39 | /// |
40 | /// Returns a closure to be passed to signal-hook. |
41 | pub(super) fn add_signal( |
42 | &mut self, |
43 | signal: Signal, |
44 | ) -> io::Result<impl Fn() + Send + Sync + 'static> { |
45 | let number = signal.number(); |
46 | let write = self.write.try_clone()?; |
47 | |
48 | Ok(move || { |
49 | // SAFETY: to_ne_bytes() and write() are both signal safe. |
50 | let bytes = number.to_ne_bytes(); |
51 | let _ = (&write).write(&bytes); |
52 | }) |
53 | } |
54 | |
55 | /// Remove a signal from the notifier. |
56 | pub(super) fn remove_signal(&mut self, _signal: Signal) -> io::Result<()> { |
57 | Ok(()) |
58 | } |
59 | |
60 | /// Get the next signal. |
61 | pub(super) fn poll_next(&self, cx: &mut Context<'_>) -> Poll<io::Result<Signal>> { |
62 | let mut buffer = [0; BUFFER_LEN]; |
63 | let mut buffer_len = 0; |
64 | |
65 | // Read into the buffer. |
66 | loop { |
67 | if buffer_len >= BUFFER_LEN { |
68 | break; |
69 | } |
70 | |
71 | // Try to fill up the entire buffer. |
72 | let buf_range = buffer_len..BUFFER_LEN; |
73 | let res = ready!(Pin::new(&mut &self.read).poll_read(cx, &mut buffer[buf_range])); |
74 | |
75 | match res { |
76 | Ok(0) => return Poll::Ready(Err(io::Error::from(io::ErrorKind::UnexpectedEof))), |
77 | Ok(n) => buffer_len += n, |
78 | Err(e) => return Poll::Ready(Err(e)), |
79 | } |
80 | } |
81 | |
82 | // Convert the buffer into a signal number. |
83 | let number = std::os::raw::c_int::from_ne_bytes(buffer); |
84 | |
85 | // Convert the signal number into a signal. |
86 | let signal = match Signal::from_number(number) { |
87 | Some(signal) => signal, |
88 | None => return Poll::Ready(Err(io::Error::from(io::ErrorKind::InvalidData))), |
89 | }; |
90 | |
91 | // Return the signal. |
92 | Poll::Ready(Ok(signal)) |
93 | } |
94 | } |
95 | |
96 | impl AsRawFd for Notifier { |
97 | fn as_raw_fd(&self) -> RawFd { |
98 | self.read.as_raw_fd() |
99 | } |
100 | } |
101 | |
102 | impl AsFd for Notifier { |
103 | fn as_fd(&self) -> BorrowedFd<'_> { |
104 | self.read.as_fd() |
105 | } |
106 | } |
107 | |