You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
403 lines
7.8 KiB
403 lines
7.8 KiB
//! `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<Error>>
|
|
{
|
|
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<usize, Errno<Error>>
|
|
{
|
|
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<usize, Errno<Error>>
|
|
{
|
|
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<T: ?Sized>(fd: i32, value: &T) -> Result<(), Errno<Error>>
|
|
{
|
|
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<T>(fd: i32) -> Result<T, Errno<Error>>
|
|
{
|
|
use malloc_array::heap;
|
|
|
|
let mut buf = heap![unsafe 0u8; std::mem::size_of::<T>()];
|
|
|
|
let read = pipe_read(fd, &mut buf)?;
|
|
|
|
if read == buf.len() {
|
|
let ptr = buf.reinterpret::<T>();
|
|
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<Self, Error>
|
|
{
|
|
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<Result<(), AsyncError>>
|
|
{
|
|
// 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<Result<(), AsyncError>>
|
|
{
|
|
unsafe {
|
|
libc::close(self.0);
|
|
}
|
|
|
|
Poll::Ready(Ok(()))
|
|
}
|
|
fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<Result<usize, AsyncError>>
|
|
{
|
|
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<Result<usize, AsyncError>>
|
|
{
|
|
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<Result<(), AsyncError>>
|
|
{
|
|
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<Result<(), AsyncError>>
|
|
{
|
|
|
|
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<Result<usize, AsyncError>>
|
|
{
|
|
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<Result<usize, AsyncError>>
|
|
{
|
|
let future = async {
|
|
self.rx.read(buf).await
|
|
};
|
|
tokio::pin!(future);
|
|
future.poll(cx)
|
|
}
|
|
}
|