memfd_exec/
stdio.rs

1use std::ffi::CStr;
2use std::fs::{File, OpenOptions};
3use std::io::Result;
4use std::os::raw::c_int;
5use std::os::unix::prelude::{AsRawFd, FromRawFd};
6use std::path::Path;
7
8use crate::anon_pipe::{anon_pipe, AnonPipe};
9use crate::file_desc::FileDesc;
10
11const DEV_NULL: &str = "/dev/null\0";
12
13pub struct StdioPipes {
14    pub stdin: Option<AnonPipe>,
15    pub stdout: Option<AnonPipe>,
16    pub stderr: Option<AnonPipe>,
17}
18
19pub struct ChildPipes {
20    pub stdin: ChildStdio,
21    pub stdout: ChildStdio,
22    pub stderr: ChildStdio,
23}
24
25pub enum ChildStdio {
26    Inherit,
27    Explicit(c_int),
28    Owned(FileDesc),
29}
30
31/// Description of a stdio stream for a child process
32#[derive(Debug)]
33pub enum Stdio {
34    /// Inherit the parent's stdio stream
35    Inherit,
36    /// Use a null stream, like /dev/null
37    Null,
38    /// Use a pipe to the input or output of the child process
39    MakePipe,
40    /// Use an existing file descriptor as the stdio stream
41    Fd(FileDesc),
42}
43
44impl Stdio {
45    pub fn to_child_stdio(&self, readable: bool) -> Result<(ChildStdio, Option<AnonPipe>)> {
46        match *self {
47            Stdio::Inherit => Ok((ChildStdio::Inherit, None)),
48
49            // Make sure that the source descriptors are not an stdio
50            // descriptor, otherwise the order which we set the child's
51            // descriptors may blow away a descriptor which we are hoping to
52            // save. For example, suppose we want the child's stderr to be the
53            // parent's stdout, and the child's stdout to be the parent's
54            // stderr. No matter which we dup first, the second will get
55            // overwritten prematurely.
56            Stdio::Fd(ref fd) => {
57                if fd.as_raw_fd() >= 0 && fd.as_raw_fd() <= libc::STDERR_FILENO {
58                    Ok((ChildStdio::Owned(fd.duplicate()?), None))
59                } else {
60                    Ok((ChildStdio::Explicit(fd.as_raw_fd()), None))
61                }
62            }
63
64            Stdio::MakePipe => {
65                let (reader, writer) = anon_pipe()?;
66                let (ours, theirs) = if readable {
67                    (writer, reader)
68                } else {
69                    (reader, writer)
70                };
71                Ok((ChildStdio::Owned(theirs.into()), Some(ours)))
72            }
73
74            Stdio::Null => {
75                let mut opts = OpenOptions::new();
76                opts.read(readable);
77                opts.write(!readable);
78                let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) };
79                let path = Path::new(path.to_str().unwrap());
80                let fd = File::open(path)?.as_raw_fd();
81                Ok((
82                    ChildStdio::Owned(unsafe { FileDesc::from_raw_fd(fd) }),
83                    None,
84                ))
85            }
86        }
87    }
88
89    /// Create a pipe for this file descriptor and use it in the child process as
90    /// the given file descriptor to facilitate input or output redirection. See
91    /// `MemFdExecutable::stdin` for an example.
92    pub fn piped() -> Stdio {
93        Stdio::MakePipe
94    }
95
96    /// Use a null file descriptor, like /dev/null, to either provide no input or to
97    /// discard output. See `MemFdExecutable::stdout` for an example.
98    pub fn null() -> Stdio {
99        Stdio::Null
100    }
101
102    /// Inherit the parent's file descriptor. this is the default behavior, but is
103    /// generally not the desired behavior.
104    pub fn inherit() -> Stdio {
105        Stdio::Inherit
106    }
107}
108
109impl From<AnonPipe> for Stdio {
110    fn from(pipe: AnonPipe) -> Stdio {
111        Stdio::Fd(pipe.into())
112    }
113}
114
115impl ChildStdio {
116    pub fn fd(&self) -> Option<c_int> {
117        match *self {
118            ChildStdio::Inherit => None,
119            ChildStdio::Explicit(fd) => Some(fd),
120            ChildStdio::Owned(ref fd) => Some(fd.as_raw_fd()),
121        }
122    }
123}