commit e64abf4f06cad5ed0d2898c31f21f3072f5fbdc7 Author: Avril Date: Wed Aug 25 13:28:03 2021 +0100 Initial commit Started C API wrappers. Added C errno Rust interface. Fortune for comfork's current commit: Small curse − 小凶 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80aca69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +*~ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..405373d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "comfork" +description = "Communicate between forks of the process" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cfg-if = "1.0.0" +libc = "0.2.99" +once_cell = "1.8.0" diff --git a/README.md b/README.md new file mode 100644 index 0000000..9283df7 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# comfork - Communicative Forking + +Creates privately owned pipes for communicating between parent and child processes through Rust streams. diff --git a/src/ext/macros.rs b/src/ext/macros.rs new file mode 100644 index 0000000..18f28d4 --- /dev/null +++ b/src/ext/macros.rs @@ -0,0 +1,48 @@ +use super::*; + +/// Inherit imports from outer package. +#[macro_export] macro_rules! inherit { + () => { + #[allow(unused_imports)] use super::*; + }; +} + +/// Conditional compilation of code depending on if debug assertions are enabled. +#[macro_export] macro_rules! debug_if { + (if {$($debug:tt)*} else {$($prod:tt)*}) => { + cfg_if! { + if #[cfg(debug_assertions)] { + { $($debug)* } + } else { + { $($prod)* } + } + } + }; + (if {$($debug:tt)*}) => { + debug_if!(if {$($debug)*} else {}) + }; + (else {$($prod:tt)*}) => { + debug_if!(if {} else {$($prod)*}) + }; +} + +fn _debug_if_test() +{ + debug_if!{ + if { + println!("Debug"); + } else { + println!("Release"); + } + } + debug_if!{ + if { + println!("Debug 2"); + } + } + debug_if!{ + else { + println!("Release 2"); + } + } +} diff --git a/src/ext/mod.rs b/src/ext/mod.rs new file mode 100644 index 0000000..f1225b4 --- /dev/null +++ b/src/ext/mod.rs @@ -0,0 +1,4 @@ +//! Extensions and macros + +#[macro_export] +pub mod macros; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4acd06f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,14 @@ +#![allow(dead_code)] + +#[macro_use] extern crate cfg_if; + +#[macro_use] mod ext; use ext::*; +mod sys; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/src/sys/errno.rs b/src/sys/errno.rs new file mode 100644 index 0000000..cf0a732 --- /dev/null +++ b/src/sys/errno.rs @@ -0,0 +1,121 @@ +//! Handling C `errno` error values in Rust +use super::*; + +use std::{ + fmt, error, +}; +use libc::c_char; +use libc::__errno_location; +use once_cell::sync::OnceCell; +use std::num::NonZeroI32; + +/// Size of errno string message buffer +const MESSAGE_BUF_SIZE: usize = 512; + +/// A raw errno error code. +pub type RawError = i32; + +/// Get the raw errno error value. +#[inline] pub fn raw() -> RawError +{ + unsafe { + *__errno_location() + } +} + +/// Convert raw error code into its message +fn get_message(raw: RawError) -> String +{ + use libc::strerror_r; + use std::ffi::CStr; + let mut buffer = [0u8; MESSAGE_BUF_SIZE+1]; + let cstr = unsafe { + strerror_r(raw, buffer.as_mut_ptr() as *mut c_char, MESSAGE_BUF_SIZE); + debug_if!(if { + buffer[MESSAGE_BUF_SIZE] = 0; + }); + CStr::from_ptr(buffer.as_mut_ptr() as *const c_char) + }; + + cstr.to_string_lossy().into_owned() +} + +/// A raw C error +#[derive(Debug)] +pub struct Errno +{ + errno: NonZeroI32, + message: OnceCell, +} + +impl Errno +{ + /// Get the raw errno value of this error. + pub const fn raw(&self) -> i32 + { + self.errno.get() + } + /// Get the errno message for this error + pub fn message(&self) -> &str + { + &self.message.get_or_init(|| get_message(self.errno.into())) + } + /// Create a new error wrapper for this errno value without checking if it was success. + /// + /// # Safety + /// The caller **must** verify that `raw` is not 0 (success) or memory corruption may be possible. + pub const unsafe fn new_unchecked(raw: RawError) -> Self + { + Self{errno: NonZeroI32::new_unchecked(raw), message: OnceCell::new()} + } + + /// Create a new error wrapper for this errno value if it was not success. + pub const fn new(raw: RawError) -> Result<(), Self> + { + if raw == 0 { + Ok(()) + } else { + Err(unsafe {Self::new_unchecked(raw)}) + } + } + /// Create a new error wrapper for the last errno value set on this thread if it was not success. + pub fn last() -> Result<(), Self> + { + Self::new(raw()) + } + + /// 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 + { + Self::last().map(move |_| fun()) + } + + /// Create a new `Result` that returns the last errno value set if it was an error, or returns the provided value if it was success. + #[inline] pub fn or_last(val: T) -> Result + { + Self::or_last_with(move || val) + } + + /// Create a new `Result` that returns the this errno value if it was an error, or calls this function and returns the value returned from it if it was success. + pub fn or_new_with(raw: RawError, fun: F) -> Result + where F: FnOnce() -> T + { + Self::new(raw).map(move |_| fun()) + } + /// Create a new `Result` that returns this errno value if it was an error, or returns the provided value if it was success. + #[inline] pub fn or_new(raw: RawError, val: T) -> Result + { + Self::or_new_with(raw, move || val) + } +} + +impl error::Error for Errno{} +impl fmt::Display for Errno +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "{}", self.message()) + } +} + diff --git a/src/sys/mod.rs b/src/sys/mod.rs new file mode 100644 index 0000000..10bb66b --- /dev/null +++ b/src/sys/mod.rs @@ -0,0 +1,4 @@ +//! Interface with system APIs +use super::*; + +pub mod errno;