//! Forking utils use super::*; use errno::Errno; use libc::{ fork, setgid, setuid, }; use std::{ fmt, }; use cfg_if::cfg_if; use super::pipe; /// Forking error #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] #[repr(u32)] pub enum Error { Fork = 1, SetUid = 2, SetGid = 3, Pipe = 4, PipeRead = 5, PipeWrite = 6, PipeBroken = 7, WaitPid = 8, Unknown = 0, } impl From for Error { fn from(from: pipe::Error) -> Self { use pipe::Error::*; match from { Create => Self::Pipe, Read => Self::PipeRead, Write => Self::PipeWrite, Broken => Self::PipeBroken, _ => Self::Unknown, } } } impl Error { #[inline] fn from_u32(from: u32) -> Self { use Error::*; match from { x if x == Fork as u32 => Fork, x if x == SetUid as u32 => SetUid, x if x == SetGid as u32 => SetGid, x if x == Pipe as u32 => Pipe, x if x == PipeRead as u32 => PipeRead, x if x == PipeWrite as u32 => PipeWrite, x if x == PipeBroken as u32 => PipeBroken, x if x == WaitPid as u32 => WaitPid, _ => Self::Unknown, } } } impl From for Error { #[inline] fn from(from: u32) -> Self { Self::from_u32(from) } } impl From for u32 { #[inline] fn from(from: Error) -> Self { from as Self } } impl std::error::Error for Error{} impl std::fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Fork => write!(f, "fork() failed"), Self::Pipe => write!(f, "pipe() failed"), Self::PipeBroken => write!(f, "broken pipe with child"), Self::PipeRead => write!(f, "pipe read failed"), Self::PipeWrite => write!(f, "pipe write failed"), Self::SetGid => write!(f, "child reported setgid() failed"), Self::SetUid => write!(f, "child reported setuid() failed"), Self::WaitPid => write!(f, "waitpid() failed unexpectedly"), _ => write!(f, "child reported unknown error"), } } } use pipe::Pipe; /// Represents the detached child #[derive(Debug)] pub struct Child { pid: i32, comm: Pipe, } #[derive(Debug)] pub struct Parent { pid: i32, comm: Pipe, } /// Run a closure as a fork and then exit, optionally trying to set `uid` and `gid`. /// /// # Notes /// This seems to corrupt the async runtime for the child, do not use it from the child. pub async fn detach_closure(as_uid: Option, as_gid: Option, into: F) -> Result> { // let (rx, tx) = unix_pipe().map_inner(|x| Error::from(x))?; let (mut ttx,mut trx) = pipe::multi().map_err(|x| Error::from(x))?; let (comm_p, comm_c) = pipe::multi().map_err(|x| Error::from(x))?; let child = unsafe{fork()}; if child == 0 { // Is child use tokio::prelude::*; let complete = || { async { unsafe { if let Ok(_) = ttx.write_u32(!0u32).await { into(Parent{pid: libc::getppid(),comm:comm_c}); } } } }; unsafe { loop { if let Some(as_uid) = as_uid { // Set UID if setuid(as_uid) != 0 { let _ = ttx.write_u32(Error::SetUid.into()).await; break; } } if let Some(as_gid) = as_gid { // Set GID if setgid(as_gid) != 0 { let _ = ttx.write_u32(Error::SetGid.into()).await; break; } } complete().await; break; } } std::process::exit(0); } else if child > 0 { // Fork succeeded use tokio::prelude::*; let err: u32 = unsafe{ timeout!(trx.read_u32(), tokio::time::Duration::from_secs(1)).unwrap_or(Ok(Error::Unknown as u32)).unwrap_or(Error::Unknown as u32) }; if err == !0u32 { Ok(Child{pid:child, comm: comm_p}) } else { Err(Error::from(err).into()) } } else { let rval = Error::Fork.into(); Err(rval) } } impl Child { /// Call `waitpid` on this child. Returns the status code if possible, or `Error` if error. /// /// `Child` implements `Future`, and waiting on that directly is probably preferable to calling this method as it does no blocking. /// /// # Notes /// - When `threaded` feature is disabled, this function will not block at all, instead returning error if there is not status available, this is to prevent deadlock. /// - This function (at present) will return `Error` when the child process exits, too. This is because we don't have `errno` access yet, I'm working on it. #[deprecated] pub async fn waitpid(&self) -> Result> { cfg_if! { if #[cfg(feature="threaded")] { let pid = self.pid; let waiter = tokio::task::spawn_blocking(move || { let mut status: i32 = 0; let (out, status) = (unsafe{libc::waitpid(pid, &mut status as *mut i32, 0)}, status); if out != pid { Err(Errno::from(Error::WaitPid)) } else { Ok(status) } }); waiter.await.expect("Waiter panicked") } else { let pid = self.pid; let mut status: i32 = 0; if unsafe{libc::waitpid(pid, &mut status as *mut i32, 1)} == pid { // We can't afford to block here Ok(status) } else { Err(Error::WaitPid.into()) } } } } /// Wait for the child process to end, ignoring any other signals. /// /// `Child` implements `Future`, and waiting on that directly is probably preferable to calling this method as it does no blocking. /// /// # Notes /// - When `threaded` feature is disabled, this function will return immediately, this is an error-handling bug because we don't have `errno` access as of yet. /// - This function will call `waitpid` untill it returns status `ECHILD` (child has exited). Other status values are ignored, if any. #[deprecated] pub async fn wait(&self) -> Result<(), Errno> { loop { #[allow(deprecated)] match self.waitpid().await { Ok(v) => { // keep going until we get `no child process' debug!("Got status {} from child {}, ignoring. ", v, self.pid); }, Err(e) if e.internal == Error::WaitPid && e.error() == 10 /* No child processes `ECHILD` */ => { //debug!("Error: {}", e); break Ok(()); }, Err(e) => { break Err(e); }, } } } } use futures::Future; use std::{ pin::Pin, task::{ Context, Poll, }, }; impl Future for Child { type Output = Result<(), Errno>; fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll { let pid = self.pid; let future = async { loop { let mut status: i32 = 0; if unsafe{libc::waitpid(pid, &mut status as *mut i32, 1)} == pid { if status == 0 { tokio::task::yield_now().await; } else { //We got a signal, but we don't care. debug!("Got status {} from child {}, ignoring. ", status, pid); tokio::task::yield_now().await; } } else { let err: Errno = Errno::from(Error::WaitPid).into(); if err.error() == 0 { tokio::task::yield_now().await; } else if err.error() == 10 { // Child exited break Ok(()); }else { break Err(err); } } } }; tokio::pin!(future); future.poll(_cx) } } // -- boilerplate impl Child { /// Get the pipe for talking to parent pub fn pipe(&mut self) -> &mut Pipe { &mut self.comm } } impl Parent { /// Get the pipe for talking to child pub fn pipe(&mut self) -> &mut Pipe { &mut self.comm } }