rust/minijail: add minijail::Command

It aims to become what std::process::Command is, but at this stage, it
is internal only.

Bug: 210909628
Test: crosvm
Change-Id: I56a50f0cdf54e3f435070f9743e5d9748e05c0c4
diff --git a/rust/minijail/src/lib.rs b/rust/minijail/src/lib.rs
index 66edc80..3f2c05a 100644
--- a/rust/minijail/src/lib.rs
+++ b/rust/minijail/src/lib.rs
@@ -15,25 +15,76 @@
 use libc::pid_t;
 use minijail_sys::*;
 
+enum Program {
+    Filename(PathBuf),
+    FileDescriptor(RawFd),
+}
+
+/// Configuration of a command to be run in a jail.
+struct Command {
+    program: Program,
+    preserve_fds: Vec<(RawFd, RawFd)>,
+
+    // Ownership of the backing data of args_cptr is provided by args_cstr.
+    args_cstr: Vec<CString>,
+    args_cptr: Vec<*const c_char>,
+}
+
+impl Command {
+    fn new(program: Program) -> Command {
+        Command {
+            program,
+            preserve_fds: Vec::new(),
+            args_cstr: Vec::new(),
+            args_cptr: Vec::new(),
+        }
+    }
+
+    fn keep_fds(mut self, keep_fds: &[RawFd]) -> Command {
+        self.preserve_fds = keep_fds
+            .iter()
+            .map(|&a| (a, a))
+            .collect::<Vec<(RawFd, RawFd)>>();
+        self
+    }
+
+    fn remap_fds(mut self, remap_fds: &[(RawFd, RawFd)]) -> Command {
+        self.preserve_fds = remap_fds.to_vec();
+        self
+    }
+
+    fn args<S: AsRef<str>>(mut self, args: &[S]) -> Result<Command> {
+        let (args_cstr, args_cptr) = to_execve_cstring_array(args)?;
+        self.args_cstr = args_cstr;
+        self.args_cptr = args_cptr;
+        Ok(self)
+    }
+
+    fn argv(&self) -> *const *mut c_char {
+        self.args_cptr.as_ptr() as *const *mut c_char
+    }
+}
+
 /// Abstracts paths and executable file descriptors in a way that the run implementation can cover
 /// both.
 trait Runnable {
-    fn run_pid_pipes(&self, jail: &Minijail, argv: &[*const c_char]) -> Result<pid_t>;
+    fn run_command(&self, jail: &Minijail, cmd: &Command) -> Result<pid_t>;
 }
 
 impl Runnable for &Path {
-    fn run_pid_pipes(&self, jail: &Minijail, argv: &[*const c_char]) -> Result<pid_t> {
-        let cmd_os = self
+    fn run_command(&self, jail: &Minijail, cmd: &Command) -> Result<pid_t> {
+        let path_str = self
             .to_str()
             .ok_or_else(|| Error::PathToCString(self.to_path_buf()))?;
-        let cmd_cstr = CString::new(cmd_os).map_err(|_| Error::StrToCString(cmd_os.to_owned()))?;
+        let path_cstr =
+            CString::new(path_str).map_err(|_| Error::StrToCString(path_str.to_owned()))?;
 
         let mut pid: pid_t = 0;
         let ret = unsafe {
             minijail_run_pid_pipes(
                 jail.jail,
-                cmd_cstr.as_ptr(),
-                argv.as_ptr() as *const *mut c_char,
+                path_cstr.as_ptr(),
+                cmd.argv(),
                 &mut pid,
                 null_mut(),
                 null_mut(),
@@ -48,13 +99,13 @@
 }
 
 impl Runnable for RawFd {
-    fn run_pid_pipes(&self, jail: &Minijail, argv: &[*const c_char]) -> Result<pid_t> {
+    fn run_command(&self, jail: &Minijail, cmd: &Command) -> Result<pid_t> {
         let mut pid: pid_t = 0;
         let ret = unsafe {
             minijail_run_fd_env_pid_pipes(
                 jail.jail,
                 *self,
-                argv.as_ptr() as *const *mut c_char,
+                cmd.argv(),
                 null_mut(),
                 &mut pid,
                 null_mut(),
@@ -725,12 +776,9 @@
         args: &[S],
     ) -> Result<pid_t> {
         self.run_internal(
-            cmd.as_ref(),
-            &inheritable_fds
-                .iter()
-                .map(|&a| (a, a))
-                .collect::<Vec<(RawFd, RawFd)>>(),
-            args,
+            Command::new(Program::Filename(cmd.as_ref().to_path_buf()))
+                .keep_fds(inheritable_fds)
+                .args(args)?,
         )
     }
 
@@ -742,7 +790,11 @@
         inheritable_fds: &[(RawFd, RawFd)],
         args: &[S],
     ) -> Result<pid_t> {
-        self.run_internal(cmd.as_ref(), &inheritable_fds, args)
+        self.run_internal(
+            Command::new(Program::Filename(cmd.as_ref().to_path_buf()))
+                .remap_fds(inheritable_fds)
+                .args(args)?,
+        )
     }
 
     /// Behaves the same as `run()` except cmd is a file descriptor to the executable.
@@ -753,12 +805,9 @@
         args: &[S],
     ) -> Result<pid_t> {
         self.run_internal(
-            cmd.as_raw_fd(),
-            &inheritable_fds
-                .iter()
-                .map(|&a| (a, a))
-                .collect::<Vec<(RawFd, RawFd)>>(),
-            args,
+            Command::new(Program::FileDescriptor(cmd.as_raw_fd()))
+                .keep_fds(inheritable_fds)
+                .args(args)?,
         )
     }
 
@@ -770,28 +819,15 @@
         inheritable_fds: &[(RawFd, RawFd)],
         args: &[S],
     ) -> Result<pid_t> {
-        self.run_internal(cmd.as_raw_fd(), &inheritable_fds, args)
+        self.run_internal(
+            Command::new(Program::FileDescriptor(cmd.as_raw_fd()))
+                .remap_fds(inheritable_fds)
+                .args(args)?,
+        )
     }
 
-    fn run_internal<R: Runnable, S: AsRef<str>>(
-        &self,
-        cmd: R,
-        inheritable_fds: &[(RawFd, RawFd)],
-        args: &[S],
-    ) -> Result<pid_t> {
-        // Converts each incoming `args` string to a `CString`, and then puts each `CString` pointer
-        // into a null terminated array, suitable for use as an argv parameter to `execve`.
-        let mut args_cstr = Vec::with_capacity(args.len());
-        let mut args_array = Vec::with_capacity(args.len());
-        for arg in args {
-            let arg_cstr = CString::new(arg.as_ref())
-                .map_err(|_| Error::StrToCString(arg.as_ref().to_owned()))?;
-            args_array.push(arg_cstr.as_ptr());
-            args_cstr.push(arg_cstr);
-        }
-        args_array.push(null());
-
-        for (src_fd, dst_fd) in inheritable_fds {
+    fn run_internal(&self, cmd: Command) -> Result<pid_t> {
+        for (src_fd, dst_fd) in cmd.preserve_fds.iter() {
             let ret = unsafe { minijail_preserve_fd(self.jail, *src_fd, *dst_fd) };
             if ret < 0 {
                 return Err(Error::PreservingFd(ret));
@@ -806,7 +842,7 @@
         // Set stdin, stdout, and stderr to /dev/null unless they are in the inherit list.
         // These will only be closed when this process exits.
         for io_fd in &[libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO] {
-            if !inheritable_fds.iter().any(|(_, fd)| *fd == *io_fd) {
+            if !cmd.preserve_fds.iter().any(|(_, fd)| *fd == *io_fd) {
                 let ret = unsafe { minijail_preserve_fd(self.jail, dev_null.as_raw_fd(), *io_fd) };
                 if ret < 0 {
                     return Err(Error::PreservingFd(ret));
@@ -818,7 +854,10 @@
             minijail_close_open_fds(self.jail);
         }
 
-        cmd.run_pid_pipes(&self, &args_array)
+        match cmd.program {
+            Program::Filename(ref path) => path.as_path().run_command(&self, &cmd),
+            Program::FileDescriptor(fd) => fd.run_command(&self, &cmd),
+        }
     }
 
     /// Forks a child and puts it in the previously configured minijail.
@@ -938,6 +977,26 @@
     }
 }
 
+fn to_execve_cstring_array<S: AsRef<str>>(
+    slice: &[S],
+) -> Result<(Vec<CString>, Vec<*const c_char>)> {
+    // Converts each incoming `str` to a `CString`, and then puts each `CString` pointer into a
+    // null terminated array, suitable for use as an argv or envp parameter to `execve`.
+    let mut vec_cstr = Vec::with_capacity(slice.len());
+    let mut vec_cptr = Vec::with_capacity(slice.len() + 1);
+    for s in slice {
+        let cstr =
+            CString::new(s.as_ref()).map_err(|_| Error::StrToCString(s.as_ref().to_owned()))?;
+
+        vec_cstr.push(cstr);
+        vec_cptr.push(vec_cstr.last().unwrap().as_ptr());
+    }
+
+    vec_cptr.push(null());
+
+    Ok((vec_cstr, vec_cptr))
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;