diff --git a/src/sys/fork.rs b/src/sys/fork.rs index 2c64c49..5b88904 100644 --- a/src/sys/fork.rs +++ b/src/sys/fork.rs @@ -7,6 +7,17 @@ use libc::{ setuid, }; use std::{fmt, error}; +use std::io::{ + self, + Read, + Write, +}; +use std::marker::Unpin; +use std::panic::{ + AssertUnwindSafe, + catch_unwind, + resume_unwind, +}; use errno::Errno; @@ -27,6 +38,49 @@ pub enum ErrorKind Pipe(pipe::ErrorKind), } +// TODO: Add non-`sys` type: `Channel`, which can be used to send/recv typed data between parent and child. +// It should leverage the two below functions: `move_*_stream()`. + +/// Move a value into a stream. +/// +/// This function will return `Ok` if and only if all bytes from `val` were successfully written to the stream `to`. +/// If only a subset of them were written, then the data is corrupt and that is counted as an error. +/// +/// # Safety +/// The object `val` is written into the stream `to` verbatim. Its destructor is not run. +/// The object should be read verbatim from the stream with no errors or corruption before its dropped, or it will leak resources. +/// +/// This function is safe, but reading from the stream into a new object is not. +/// +/// ## Unpin +/// `T` should be `Unpin` in case it contains self-referrential data, as those references will no longer be valid after being moved across a stream. +// TODO: Should we expose these to the user somehow, with an unsafe helper module maybe? They could be useful. +// Or perhaps: Add the functionality to move types to/from the child/parent into the library, similar to `mpsc`. +pub(crate) fn move_write_value(mut to: S, val: T) -> io::Result<()> +{ + let bytes = unsafe { + std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::()) + }; + // Catch any panics that may happen in `write`. + let res = catch_unwind(AssertUnwindSafe(move || { + to.write_all(bytes) + })); + // Ensure this happens even if the above panics. + drop(bytes); // Make sure `bytes` isn't dangling after we move `val` into `forget()`. + std::mem::forget(val); + // Resume unwind if one occoured, if not return the value + match res { + Ok(v) => v, + Err(panic) => resume_unwind(panic), + } +} + +//TODO: Counterpart to `move_write_value()`. This function should be `unsafe` as it is extermely unsafe. +pub(crate) unsafe fn move_read_value(mut from: S) -> io::Result +{ + todo!() +} + /// An error in either forking or communicating with the child process during its setup. #[derive(Debug)] #[repr(transparent)]