//! `pipe()` related operations //! The async pipe is kinda broke, but that's fine because we don't really need it anyways. use super::*; use errno::Errno; use std::{ fmt, }; /// Represents an error on `pipe()` related operations #[derive(Debug)] pub enum Error { Create, Read, Write, Broken, Unknown, } impl std::error::Error for Error{} impl std::fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Create => write!(f, "failed to create pipe"), Self::Read => write!(f, "failed to read from pipe"), Self::Write => write!(f, "failed to write to pipe"), Self::Broken => write!(f, "broken pipe"), _ => write!(f, "unknown error"), } } } /// Create with `pipe()` pub(super) fn unix_pipe() -> Result<(i32,i32), Errno> { use libc::{ pipe, }; let mut pipe_fd: [libc::c_int; 2] = [0;2]; if unsafe{pipe(&mut pipe_fd[0] as *mut libc::c_int)} == 0{ Ok((pipe_fd[0], pipe_fd[1])) } else { Err(Error::Create.into()) } } /// Write to a pipe pub(super) fn pipe_write(output: i32, buf: impl AsRef<[u8]>) -> Result> { let buf = buf.as_ref(); let len = buf.len(); let read = unsafe{libc::write(output, &buf[0] as *const u8 as *const libc::c_void, len)}; if read < 0 { Err(Error::Write.into()) } else { Ok(read as usize) } } pub(super) fn pipe_read(input: i32, mut buf: impl AsMut<[u8]>) -> Result> { let buf = buf.as_mut(); let len = buf.len(); let read = unsafe{libc::read(input, &mut buf[0] as *mut u8 as *mut libc::c_void, len)}; if read < 0 { Err(Error::Read.into()) } else { Ok(read as usize) } } pub(super) unsafe fn pipe_write_value(fd: i32, value: &T) -> Result<(), Errno> { let sz = std::mem::size_of_val(value); let write = pipe_write(fd, std::slice::from_raw_parts(value as *const T as *const u8, sz))?; if write == sz { Ok(()) } else { Err(Error::Broken.into()) } } pub(super) unsafe fn pipe_read_value(fd: i32) -> Result> { use malloc_array::heap; let mut buf = heap![unsafe 0u8; std::mem::size_of::()]; let read = pipe_read(fd, &mut buf)?; if read == buf.len() { let ptr = buf.reinterpret::(); Ok(ptr.as_ptr().read()) } else { Err(Error::Broken.into()) } } #[derive(Debug, PartialEq, Eq)] pub struct WriteHalf(i32); #[derive(Debug, PartialEq, Eq)] pub struct ReadHalf(i32); #[derive(Debug, PartialEq, Eq)] pub struct Pipe { tx: WriteHalf, rx: ReadHalf, } impl Pipe { /// Create from a split pub fn from_split(tx: WriteHalf, rx: ReadHalf) -> Self { Self{ tx, rx, } } /// Create a new pipe pub fn new() -> Result { let (tx,rx) = pipe()?; Ok(Self::from_split(tx,rx)) } /// Split into write and read half pub fn split(self) -> (WriteHalf, ReadHalf) { (self.tx, self.rx) } } const GETFL: i32 = 3; const SETFL: i32 = 4; const NOBLOCK: i32 = 2048; fn set_non_blocking(fd: i32) { use libc::fcntl; unsafe { fcntl(fd, SETFL, fcntl(fd, GETFL, 0) | NOBLOCK); } } impl WriteHalf { /// Create from a raw file descriptor pub unsafe fn from_raw(fd: i32) -> Self { set_non_blocking(fd); Self(fd) } /// Consume and return the output file descriptor pub fn into_raw(self) -> i32 { self.0 } } impl ReadHalf { /// Create from a raw file descriptor pub unsafe fn from_raw(fd: i32) -> Self { set_non_blocking(fd); Self(fd) } /// Consume and return the output file descriptor pub fn into_raw(self) -> i32 { self.0 } } /// Create a new pipe's `Read` and `Write` halfs pub fn pipe() -> Result<(WriteHalf, ReadHalf), Error> { let (rx, tx) = unix_pipe().map_err(|x| x.into_inner())?; if rx <= 0 || tx <= 0 { return Err(Error::Create); } unsafe { Ok((WriteHalf::from_raw(tx), ReadHalf::from_raw(rx))) } } /// Create 2 linked together pipes. /// /// # Usage /// Useful for `fork()`. 1st is parent, 2nd moved to client pub fn multi() -> Result<(Pipe, Pipe), Error> { let (tx_c, rx_p) = pipe()?; let (tx_p, rx_c) = pipe()?; Ok((Pipe::from_split(tx_p, rx_p), Pipe::from_split(tx_c, rx_c))) } use tokio::{ io::{ AsyncWrite, AsyncRead, }, }; use tokio::io::Error as AsyncError; use std::{ pin::Pin, task::{ Context, Poll, }, }; const POLL_IN: i16 = 1; const POLL_OUT: i16 = 4; impl AsyncWrite for WriteHalf { #[inline] fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { // as far as i can tell this is a no-op with `write()`? Poll::Ready(Ok(())) } #[inline] fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { unsafe { libc::close(self.0); } Poll::Ready(Ok(())) } fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { use libc::{ poll, write, pollfd, c_void, }; use tokio::task::yield_now; let future = async { loop { let mut fd = pollfd { fd: self.0, revents: 0, events: POLL_OUT, }; let poll = unsafe { poll(&mut fd as *mut pollfd, 1, 0) }; if poll < 0 { break Err(AsyncError::from_raw_os_error(errno::raw())); } else if poll > 0 { if fd.revents & POLL_OUT == POLL_OUT { // Write ready let wr = unsafe{write(self.0, &buf[0] as *const u8 as *const c_void, buf.len())}; if wr < 0 { break Err(AsyncError::from_raw_os_error(errno::raw())); } else { break Ok(wr as usize); } } } // Either no poll, or no POLLOUT event yield_now().await; } }; tokio::pin!(future); future.poll(cx) } } impl AsyncRead for ReadHalf { fn poll_read(self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll> { use libc::{ poll, read, pollfd, c_void, }; use tokio::task::yield_now; let future = async { loop { let mut fd = pollfd { fd: self.0, revents: 0, events: POLL_IN, }; let poll = unsafe { poll(&mut fd as *mut pollfd, 1, 0) }; if poll < 0 { break Err(AsyncError::from_raw_os_error(errno::raw())); } else if poll > 0 { if fd.revents & POLL_IN == POLL_IN { // Read ready let wr = unsafe{read(self.0, &mut buf[0] as *mut u8 as *mut c_void, buf.len())}; if wr < 0 { break Err(AsyncError::from_raw_os_error(errno::raw())); } else { break Ok(wr as usize); } } } // Either no poll, or no POLLIN event yield_now().await; } }; tokio::pin!(future); future.poll(cx) } } use std::ops::Drop; impl Drop for WriteHalf { fn drop(&mut self) { unsafe { libc::close(self.0); } } } impl Drop for ReadHalf { fn drop(&mut self) { unsafe { libc::close(self.0); } } } use tokio::prelude::*; use futures::Future; impl AsyncWrite for Pipe { #[inline] fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let future = async { self.tx.flush().await }; tokio::pin!(future); future.poll(cx) } fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let future = async { self.tx.shutdown().await }; tokio::pin!(future); future.poll(cx) } fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { let future = async { self.tx.write(buf).await }; tokio::pin!(future); future.poll(cx) } } impl AsyncRead for Pipe { fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8]) -> Poll> { let future = async { self.rx.read(buf).await }; tokio::pin!(future); future.poll(cx) } }