| 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 | |