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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std::io;
use std::os::unix::io::{RawFd, AsRawFd, IntoRawFd};

use nix;
use nix::fcntl::{fcntl, FcntlArg};
use libc;


/// An enumeration that is used to configure stdio file descritors
///
/// The enumeration members might be non-stable, it's better to use
/// one of the constructors to create an instance
pub enum Stdio {
    /// This fd will use pipe to/from the appliation
    Pipe,
    /// This fd will be inherited from the parent application
    Inherit,
    /// This fd will open /dev/null in read or write mode
    Null,
    /// This is fd passed by application (and closed by `unshare`)
    Fd(Closing),
}

/// An enumeration that is used to configure non-stdio file descriptors. It
/// differs from stdio one because we must differentiate from readable and
/// writable file descriptors for things open by the library
///
/// The enumeration members might be non-stable, it's better to use
/// one of the constructors to create an instance
// TODO(tailhook) should this object be hidden?
pub enum Fd {
    /// This fd is a reading end of a pipe
    ReadPipe,
    /// This fd is a writing end of a pipe
    WritePipe,
    /// This fd is inherited from parent (current) process
    Inherit,
    /// This fd is redirected from `/dev/null`
    ReadNull,
    /// This fd is redirected to `/dev/null`
    WriteNull,
    /// This is fd passed by application (and closed by `unshare`)
    Fd(Closing),
}

pub struct Closing(RawFd);

pub fn dup_file_cloexec<F: AsRawFd>(file: &F) -> io::Result<Closing> {
    match fcntl(file.as_raw_fd(), FcntlArg::F_DUPFD_CLOEXEC(3)) {
        Ok(fd) => Ok(Closing::new(fd)),
        Err(nix::Error::Sys(errno)) => {
            return Err(io::Error::from_raw_os_error(errno as i32));
        }
        Err(nix::Error::InvalidPath) => unreachable!(),
        Err(nix::Error::InvalidUtf8) => unreachable!(),
        Err(nix::Error::UnsupportedOperation) => {
            return Err(io::Error::new(io::ErrorKind::Other,
                "nix error: unsupported operation"));
        }
    }
}

impl Stdio {
    /// Pipe is created for child process
    pub fn piped() -> Stdio { Stdio::Pipe }
    /// The child inherits file descriptor from the parent process
    pub fn inherit() -> Stdio { Stdio::Inherit }
    /// Stream is attached to `/dev/null`
    pub fn null() -> Stdio { Stdio::Null }
    /// Converts stdio definition to file descriptor definition
    /// (mostly needed internally)
    pub fn to_fd(self, write: bool) -> Fd {
        match (self, write) {
            (Stdio::Fd(x), _) => Fd::Fd(x),
            (Stdio::Pipe, false) => Fd::ReadPipe,
            (Stdio::Pipe, true) => Fd::WritePipe,
            (Stdio::Inherit, _) => Fd::Inherit,
            (Stdio::Null, false) => Fd::ReadNull,
            (Stdio::Null, true) => Fd::WriteNull,
        }
    }
    /// A simpler helper method for `from_raw_fd`, that does dup of file
    /// descriptor, so is actually safe to use (but can fail)
    pub fn dup_file<F: AsRawFd>(file: &F) -> io::Result<Stdio> {
        dup_file_cloexec(file).map(|f| Stdio::Fd(f))
    }
    /// A simpler helper method for `from_raw_fd`, that consumes file
    ///
    /// Note: we assume that file descriptor **already has** the `CLOEXEC`
    /// flag. This is by default for all files opened by rust.
    pub fn from_file<F: IntoRawFd>(file: F) -> Stdio {
        Stdio::Fd(Closing(file.into_raw_fd()))
    }
}

impl Fd {
    /// Create a pipe so that child can read from it
    pub fn piped_read() -> Fd { Fd::ReadPipe }
    /// Create a pipe so that child can write to it
    pub fn piped_write() -> Fd { Fd::WritePipe }
    /// Inherit the child descriptor from parent
    ///
    /// Not very useful for custom file descriptors better use `from_file()`
    pub fn inherit() -> Fd { Fd::Inherit }
    /// Create a readable pipe that always has end of file condition
    pub fn read_null() -> Fd { Fd::ReadNull }
    /// Create a writable pipe that ignores all the input
    pub fn write_null() -> Fd { Fd::WriteNull }
    /// A simpler helper method for `from_raw_fd`, that does dup of file
    /// descriptor, so is actually safe to use (but can fail)
    pub fn dup_file<F: AsRawFd>(file: &F) -> io::Result<Fd> {
        dup_file_cloexec(file).map(|f| Fd::Fd(f))
    }
    /// A simpler helper method for `from_raw_fd`, that consumes file
    pub fn from_file<F: IntoRawFd>(file: F) -> Fd {
        Fd::Fd(Closing(file.into_raw_fd()))
    }
}

impl Closing {
    pub fn new(fd: RawFd) -> Closing {
        Closing(fd)
    }
}

impl AsRawFd for Closing {
    fn as_raw_fd(&self) -> RawFd {
        return self.0;
    }
}

impl Drop for Closing {
    fn drop(&mut self) {
        unsafe {
            libc::close(self.0);
        }
    }
}