1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use std::io;
use std::os::unix::io::RawFd;

use nix::Error;
use nix::unistd::Pid;
use nix::sys::wait::waitpid;
use nix::sys::signal::{Signal, SIGKILL, kill};
use nix::errno::Errno::EINTR;
use libc::pid_t;

use crate::pipe::PipeHolder;
use crate::{Child, ExitStatus, PipeReader, PipeWriter};


impl Child {

    /// Returns pid of the process (a mirror of std method)
    pub fn id(&self) -> u32 {
        self.pid as u32
    }

    /// Returns pid of process with correct pid_t type
    pub fn pid(&self) -> pid_t {
        self.pid
    }

    /// Synchronously wait for child to complete and return exit status
    pub fn wait(&mut self) -> Result<ExitStatus, io::Error> {
        if let Some(x) = self.status {
            return Ok(x);
        }
        let status = self._wait()?;
        self.status = Some(status);
        Ok(status)
    }


    fn _wait(&mut self) -> Result<ExitStatus, io::Error> {
        use nix::sys::wait::WaitStatus::*;
        loop {
            match waitpid(Some(Pid::from_raw(self.pid)), None) {
                Ok(PtraceEvent(..)) => {}
                Ok(PtraceSyscall(..)) => {}
                Ok(Exited(x, status)) => {
                    assert!(i32::from(x) == self.pid);
                    return Ok(ExitStatus::Exited(status as i8));
                }
                Ok(Signaled(x, sig, core)) => {
                    assert!(i32::from(x) == self.pid);
                    return Ok(ExitStatus::Signaled(sig, core));
                }
                Ok(Stopped(_, _)) => unreachable!(),
                Ok(Continued(_)) => unreachable!(),
                Ok(StillAlive) => unreachable!(),
                Err(Error::Sys(EINTR)) => continue,
                Err(Error::InvalidPath) => unreachable!(),
                Err(Error::InvalidUtf8) => unreachable!(),
                Err(Error::UnsupportedOperation) => {
                    return Err(io::Error::new(io::ErrorKind::Other,
                               "nix error: unsupported operation"));
                }
                Err(Error::Sys(x)) => {
                    return Err(io::Error::from_raw_os_error(x as i32))
                }
            }
        }
    }

    /// Send arbitrary unix signal to the process
    pub fn signal(&self, signal: Signal) -> Result<(), io::Error> {
        // This prevents (somewhat not-reliable) killing some other process
        // with same pid
        if self.status.is_some() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "invalid argument: can't kill an exited process",
            ))
        }
        kill(Pid::from_raw(self.pid), signal)
        .map_err(|e| match e {
            Error::Sys(x) => io::Error::from_raw_os_error(x as i32),
            Error::InvalidPath => unreachable!(),
            Error::InvalidUtf8 => unreachable!(),
            Error::UnsupportedOperation => {
                io::Error::new(io::ErrorKind::Other,
                           "nix error: unsupported operation")
            }
        })
    }

    /// Kill process with SIGKILL signal
    pub fn kill(&self) -> Result<(), io::Error> {
        self.signal(SIGKILL)
    }

    /// Returns pipe reader for a pipe declared with `file_descriptor()`
    ///
    /// Returns None for wrong configuration or when called twice for same
    /// descriptor
    pub fn take_pipe_reader(&mut self, fd: RawFd) -> Option<PipeReader> {
        match self.fds.remove(&fd) {
            Some(PipeHolder::Reader(x)) => Some(x),
            _ => None,
        }
    }

    /// Returns pipe writer for a pipe declared with `file_descriptor()`
    ///
    /// Returns None for wrong configuration or when called twice for same
    /// descriptor
    pub fn take_pipe_writer(&mut self, fd: RawFd) -> Option<PipeWriter> {
        match self.fds.remove(&fd) {
            Some(PipeHolder::Writer(x)) => Some(x),
            _ => None,
        }
    }
}