diff --git a/src/ext/macros.rs b/src/ext/macros.rs index 18f28d4..f9081e1 100644 --- a/src/ext/macros.rs +++ b/src/ext/macros.rs @@ -46,3 +46,13 @@ fn _debug_if_test() } } } + +/// Do not compile the code in this block +#[macro_export] macro_rules! remove { + ($($tt:tt)*) => { + const _:() = { + $($tt)* + () + }; + } +} diff --git a/src/sys/errno.rs b/src/sys/errno.rs index cf0a732..de4abcd 100644 --- a/src/sys/errno.rs +++ b/src/sys/errno.rs @@ -41,7 +41,7 @@ fn get_message(raw: RawError) -> String } /// A raw C error -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Errno { errno: NonZeroI32, @@ -86,7 +86,7 @@ impl Errno /// Create a new `Result` that returns the last errno value set if it was an error, or calls this function and returns the value returned from it if it was success. pub fn or_last_with(fun: F) -> Result - where F: FnOnce() -> T + where F: FnOnce() -> T { Self::last().map(move |_| fun()) } @@ -115,7 +115,80 @@ impl fmt::Display for Errno { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.message()) + write!(f, "{} ({})", self.message(), self.errno.get()) } } +remove! { + /// Wrapping of errno around another Rust error type. + #[derive(Debug)] + pub struct ErrnoWrap + { + error: Errno, + inner: T, + } + + impl error::Error for ErrnoWrap + { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(&self.inner) + } + } + + impl fmt::Display for ErrnoWrap + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "{}: {}", self.inner, self.error) + } + } + + impl ErrnoWrap + { + pub fn into_inner(self) -> T + { + self.inner + } + + pub fn map_inner(self, map: F) -> ErrnoWrap + where F: FnOnce(T) -> U, + U: error::Error + 'static + { + ErrnoWrap { + error: self.error, + inner: map(self.inner) + } + } + } + + impl ErrnoWrap + { + pub fn errno(&self) -> &Errno + { + &self.error + } + pub fn inner(&self) -> &T + { + &self.inner + } + } + + impl ErrnoWrap + { + pub fn with_errno(error: T, errno: Errno) -> Self + { + Self { + error: errno, + inner: error + } + } + + pub fn last(error: T) -> Result<(), Self> + { + Err(Self::with_errno(error, match Errno::last() { + Ok(()) => return Ok(()), + Err(e) => e, + })) + } + } +} diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 10bb66b..7b2d59a 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -2,3 +2,5 @@ use super::*; pub mod errno; + +pub mod pipe; diff --git a/src/sys/pipe.rs b/src/sys/pipe.rs new file mode 100644 index 0000000..3ff61d7 --- /dev/null +++ b/src/sys/pipe.rs @@ -0,0 +1,137 @@ +//! Wrapper around internal pipes fds. +//! +//! These pipe fds are valid across forks, and can be used for IPC between parent and non-exec()d child process forks. +use super::*; +use errno::Errno; +use std::{error, fmt}; + +/// Kind of pipe error +#[derive(Debug)] +#[non_exhaustive] +pub enum ErrorKind +{ + /// Failed to create pipes + Create, + /// Unknown error + Unknown, +} + +/// A raw unix pipe +pub type RawPipe = i32; + +/// Create a raw pipe pair. +pub(crate) fn unix_pipe() -> Result<(i32, i32), Error> +{ + use libc::pipe; + let mut pipe_fds: [libc::c_int; 2] = [-1;2]; + if unsafe{pipe(pipe_fds.as_mut_ptr())} == 0 { + Ok((i32::from(pipe_fds[0]), i32::from(pipe_fds[1]))) + } else { + Err(ErrorKind::Create.into_error()) + } +} + +impl fmt::Display for ErrorKind +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + Self::Create => write!(f, "failed to create raw pipes"), + _ => write!(f, "unknown error"), + } + } +} + +/// An error on a pipe operation +#[derive(Debug)] +pub struct Error(ErrorKind, Errno); + +impl ErrorKind +{ + /// Consume into `Error` using the last `errno` value, if it's not set then return `val`. + #[inline] pub(crate) fn try_into_error_or(self, val: T) -> Result + { + Error::or_last(self, val) + } + /// Consume into `Error` using the last `errno` value, if it's not set then return the result of `fun`. + #[inline] pub(crate) fn try_into_error_or_with(self, fun: F) -> Result + where F: FnOnce() -> T + { + Error::or_last_with(self, fun) + } + /// Consume into `Error` using the last `errno` value. + pub(crate) fn try_into_error(self) -> Result<(), Error> + { + Error::last(self) + } + + /// Consume into an `Error` using the last `errno` value. + /// + /// # Panics + /// If `errno` was success. + #[inline] pub(crate) fn into_error(self) -> Error + { + Self::try_into_error(self).expect_err("Expected an error to be set in errno") + } +} + +impl Error +{ + /// Create a new error of this kind from the last set errno if it's not success. + /// If it is, return `val`. + #[inline] pub(crate) fn or_last(err: ErrorKind, val: T) -> Result + { + Self::or_last_with(err, move || val) + } + /// Create a new error of this kind from the last set errno if it's not success. + /// If it is, return the result of `fun`. + pub(crate) fn or_last_with(err: ErrorKind, fun: F) -> Result + where F: FnOnce() -> T + { + Err(Self(err, match Errno::or_last_with(fun) { + Ok(v) => return Ok(v), + Err(e) => e, + })) + } + /// Create a new error of this kind from the last set errno (if it's not success.) + pub(crate) fn last(err: ErrorKind) -> Result<(), Self> + { + Err(Self(err, match Errno::last() { + Ok(()) => return Ok(()), + Err(e) => e, + })) + } + + /// Create a new error of this kind with this errno value + #[inline] pub fn new(kind: ErrorKind, err: Errno) -> Self + { + Self(kind, err) + } + + /// The kind of pipe error + #[inline] pub fn kind(&self) -> &ErrorKind + { + &self.0 + } + + /// The errno value + #[inline] pub fn error(&self) -> &Errno + { + &self.1 + } +} + +impl error::Error for Error +{ + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(&self.1) + } +} +impl fmt::Display for Error +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "{}: {}", self.0, self.1) + } +} +