shpool_pty 0.4.0

Fork with new pseudo-terminal (PTY)
Documentation
mod pty;

pub use self::pty::{Master, MasterError};
pub use self::pty::{Slave, SlaveError};
use crate::descriptor::DescriptorError;
use std::error::Error;
use std::ffi::CStr;
use std::ffi::CString;
use std::fmt;

/// The alias `Result` learns `ForkError` possibility.
pub type Result<T> = ::std::result::Result<T, ForkError>;

/// The enum `ForkError` defines the possible errors from constructor Fork.
#[derive(Clone, Copy, Debug)]
pub enum ForkError {
    /// Can't creates the child.
    Failure,
    /// Can't set the id group.
    SetsidFail,
    /// Can't suspending the calling process.
    WaitpidFail,
    /// Is child and not parent.
    IsChild,
    /// Is parent and not child.
    IsParent,
    /// The Master occured a error.
    BadMaster(MasterError),
    /// The Slave occured a error.
    BadSlave(SlaveError),
    /// The Master's Descriptor occured a error.
    BadDescriptorMaster(DescriptorError),
    /// The Slave's Descriptor occured a error.
    BadDescriptorSlave(DescriptorError),
}

impl fmt::Display for ForkError {
    /// The function `fmt` formats the value using the given formatter.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", ::errno::errno())
    }
}

impl Error for ForkError {
    /// The function `description` returns a short description of the error.
    fn description(&self) -> &str {
        match *self {
            ForkError::Failure => {
                "On failure, -1 is returned in the parent,no child process is created, and errno \
                 isset appropriately."
            }
            ForkError::SetsidFail => {
                "fails if the calling process is alreadya process group leader."
            }
            ForkError::WaitpidFail => "Can't suspending the calling process.",
            ForkError::IsChild => "is child and not parent",
            ForkError::IsParent => "is parent and not child",
            ForkError::BadMaster(_) => "the master as occured an error",
            ForkError::BadSlave(_) => "the slave as occured an error",
            ForkError::BadDescriptorMaster(_) => "the master's descriptor as occured an error",
            ForkError::BadDescriptorSlave(_) => "the slave's descriptor as occured an error",
        }
    }

    /// The function `cause` returns the lower-level cause of this error, if
    /// any.
    fn cause(&self) -> Option<&dyn Error> {
        match *self {
            ForkError::BadMaster(ref err) => Some(err),
            ForkError::BadSlave(ref err) => Some(err),
            ForkError::BadDescriptorMaster(ref err) => Some(err),
            ForkError::BadDescriptorSlave(ref err) => Some(err),
            _ => None,
        }
    }
}

const MAX_PTS_NAME: usize = 1024;

#[derive(Debug, Clone)]
pub enum Fork {
    // Parent child's pid and master's pty.
    Parent(libc::pid_t, Master),
    // Child pid 0.
    Child(Slave),
}

impl Fork {
    /// The constructor function `new` forks the program
    /// and returns the current pid.
    pub fn new(path: &'static str) -> Result<Self> {
        match Master::new(CString::new(path).ok().unwrap_or_default().as_c_str()) {
            Err(cause) => Err(ForkError::BadMaster(cause)),
            Ok(master) => {
                if let Some(cause) = master.grantpt().err().or(master.unlockpt().err()) {
                    Err(ForkError::BadMaster(cause))
                } else {
                    // Safety: no params to worry about, just an ffi call
                    let fork_ret = unsafe { libc::fork() };
                    match fork_ret {
                        -1 => Err(ForkError::Failure),
                        0 => {
                            let mut ptsname_buf = vec![0; MAX_PTS_NAME];
                            if let Err(cause) = master.ptsname_r(&mut ptsname_buf) {
                                return Err(ForkError::BadMaster(cause));
                            }
                            // ensure null termination
                            let last_idx = ptsname_buf.len() - 1;
                            ptsname_buf[last_idx] = 0;

                            let name_ptr: *const u8 = &ptsname_buf[0];
                            // Safety: ptsname_r returns a valid c string when it returns a 0
                            // (success) code, and we make double extra sure there is a null
                            // terminator by adding one ourselves.
                            let name: &CStr =
                                unsafe { CStr::from_ptr(name_ptr as *const libc::c_char) };
                            Fork::from_pts(name)
                        }
                        pid => Ok(Fork::Parent(pid, master)),
                    }
                }
            }
        }
    }

    /// The constructor function `from_pts` is a private
    /// extention from the constructor function `new` who
    /// prepares and returns the child.
    fn from_pts(ptsname: &CStr) -> Result<Self> {
        unsafe {
            if libc::setsid() == -1 {
                Err(ForkError::SetsidFail)
            } else {
                match Slave::new(ptsname) {
                    Err(cause) => Err(ForkError::BadSlave(cause)),
                    Ok(slave) => {
                        if let Some(cause) = slave.dup2(libc::STDIN_FILENO).err().or(slave
                            .dup2(libc::STDOUT_FILENO)
                            .err()
                            .or(slave.dup2(libc::STDERR_FILENO).err()))
                        {
                            Err(ForkError::BadSlave(cause))
                        } else {
                            Ok(Fork::Child(slave))
                        }
                    }
                }
            }
        }
    }

    /// The constructor function `from_ptmx` forks the program
    /// and returns the current pid for a default PTMX's path.
    pub fn from_ptmx() -> Result<Self> {
        Fork::new(crate::DEFAULT_PTMX)
    }

    /// Waits until it's terminated.
    pub fn wait(&self) -> Result<libc::pid_t> {
        self.wait_for_exit().map(|(p, _)| p)
    }

    /// Waits until it's terminated, returning the exit status if there is one
    pub fn wait_for_exit(&self) -> Result<(libc::pid_t, Option<i32>)> {
        match *self {
            Fork::Child(_) => Err(ForkError::IsChild),
            Fork::Parent(pid, _) => loop {
                unsafe {
                    let mut status = 0;
                    match libc::waitpid(pid, &mut status, 0) {
                        0 => continue,
                        -1 => return Err(ForkError::WaitpidFail),
                        _ => {
                            if libc::WIFEXITED(status) {
                                return Ok((pid, Some(libc::WEXITSTATUS(status))));
                            } else {
                                return Ok((pid, None));
                            }
                        }
                    }
                }
            },
        }
    }

    /// The function `child_pid` returns the pid of the child process if
    /// this instance of Fork represents the parent process and None
    /// in the child process.
    pub fn child_pid(&self) -> Option<libc::pid_t> {
        match *self {
            Fork::Child(_) => None,
            Fork::Parent(pid, _) => Some(pid),
        }
    }

    /// The function `is_parent` returns the pid or parent
    /// or none.
    pub fn is_parent(&self) -> Result<Master> {
        match *self {
            Fork::Child(_) => Err(ForkError::IsChild),
            Fork::Parent(_, ref master) => Ok(master.clone()),
        }
    }

    /// The function `is_child` returns the pid or child
    /// or none.
    pub fn is_child(&self) -> Result<&Slave> {
        match *self {
            Fork::Parent(_, _) => Err(ForkError::IsParent),
            Fork::Child(ref slave) => Ok(slave),
        }
    }
}