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

//! `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)
}
}