summary refs log tree commit diff
path: root/src/plugin/process.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugin/process.rs')
-rw-r--r--src/plugin/process.rs66
1 files changed, 44 insertions, 22 deletions
diff --git a/src/plugin/process.rs b/src/plugin/process.rs
index fb9ad7d..5fd31b6 100644
--- a/src/plugin/process.rs
+++ b/src/plugin/process.rs
@@ -3,21 +3,22 @@
 // found in the LICENSE file.
 
 use std::collections::hash_map::{HashMap, Entry, VacantEntry};
+use std::env::set_var;
 use std::fs::File;
 use std::net::Shutdown;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::os::unix::net::UnixDatagram;
-use std::os::unix::process::ExitStatusExt;
 use std::path::Path;
-use std::process::{Command, Child};
+use std::process::Command;
 use std::sync::{Arc, Mutex, RwLock};
 use std::thread::JoinHandle;
 
-use libc::EINVAL;
+use libc::{waitpid, pid_t, EINVAL, WNOHANG, WIFEXITED, WEXITSTATUS, WTERMSIG};
 
 use protobuf;
 use protobuf::Message;
 
+use io_jail::Minijail;
 use kvm::{Vm, IoeventAddress, NoDatamatch, IrqSource, IrqRoute, dirty_log_bitmap_size};
 use sys_util::{EventFd, MemoryMapping, Killable, Scm, Poller, Pollable, SharedMemory,
                GuestAddress, Result as SysResult, Error as SysError};
@@ -44,7 +45,7 @@ pub enum ProcessStatus {
 /// an unprivileged manner as a child process spawned via a path to a arbitrary executable.
 pub struct Process {
     started: bool,
-    plugin_proc: Child,
+    plugin_pid: pid_t,
     request_sockets: Vec<UnixDatagram>,
     objects: HashMap<u32, PluginObject>,
     shared_vcpu_state: Arc<RwLock<SharedVcpuState>>,
@@ -66,7 +67,11 @@ impl Process {
     ///
     /// This will immediately spawn the plugin process and wait for the child to signal that it is
     /// ready to start. This call may block indefinitely.
-    pub fn new(cpu_count: u32, vm: &mut Vm, cmd_path: &Path) -> Result<Process> {
+    ///
+    /// Set the `jail` argument to spawn the plugin process within the preconfigured jail.
+    /// Due to an API limitation in libminijail necessitating that this function set an environment
+    /// variable, this function is not thread-safe.
+    pub fn new(cpu_count: u32, vm: &mut Vm, cmd: &Path, jail: Option<Minijail>) -> Result<Process> {
         let (request_socket, child_socket) = new_seqpacket_pair().map_err(Error::CreateMainSocket)?;
 
         let mut vcpu_sockets: Vec<(UnixDatagram, UnixDatagram)> = Vec::with_capacity(cpu_count as
@@ -77,11 +82,20 @@ impl Process {
         let mut per_vcpu_states: Vec<Arc<Mutex<PerVcpuState>>> = Vec::new();
         per_vcpu_states.resize(cpu_count as usize, Default::default());
 
-        // TODO(zachr): use a minijail
-        let plugin_proc = Command::new(cmd_path)
-            .env("CROSVM_SOCKET", child_socket.as_raw_fd().to_string())
-            .spawn()
-            .map_err(Error::PluginSpawn)?;
+        let plugin_pid = match jail {
+            Some(jail) => {
+                set_var("CROSVM_SOCKET", child_socket.as_raw_fd().to_string());
+                jail.run(cmd, &[0, 1, 2, child_socket.as_raw_fd()], &[])
+                    .map_err(Error::PluginRunJail)?
+            }
+            None => {
+                Command::new(cmd)
+                    .env("CROSVM_SOCKET", child_socket.as_raw_fd().to_string())
+                    .spawn()
+                    .map_err(Error::PluginSpawn)?
+                    .id() as pid_t
+            }
+        };
 
         // Very important to drop the child socket so that the pair will properly hang up if the
         // plugin process exits or closes its end.
@@ -91,7 +105,7 @@ impl Process {
 
         let mut plugin = Process {
             started: false,
-            plugin_proc,
+            plugin_pid,
             request_sockets,
             objects: Default::default(),
             shared_vcpu_state: Default::default(),
@@ -160,8 +174,8 @@ impl Process {
     }
 
     /// Returns the process ID of the plugin process.
-    pub fn pid(&self) -> u32 {
-        self.plugin_proc.id()
+    pub fn pid(&self) -> pid_t {
+        self.plugin_pid
     }
 
     /// Returns a slice of each socket that should be polled.
@@ -211,17 +225,25 @@ impl Process {
 
     /// Waits without blocking for the plugin process to exit and returns the status.
     pub fn try_wait(&mut self) -> SysResult<ProcessStatus> {
-        match self.plugin_proc.try_wait() {
-            Ok(Some(status)) => {
-                match status.code() {
-                    Some(0) => Ok(ProcessStatus::Success),
-                    Some(code) => Ok(ProcessStatus::Fail(code)),
-                    // If there was no exit code there must be a signal.
-                    None => Ok(ProcessStatus::Signal(status.signal().unwrap())),
+        let mut status = 0;
+        // Safe because waitpid is given a valid pointer of correct size and mutability, and the
+        // return value is checked.
+        let ret = unsafe { waitpid(self.plugin_pid, &mut status, WNOHANG) };
+        match ret {
+            -1 => Err(SysError::last()),
+            0 => Ok(ProcessStatus::Running),
+            _ => {
+                // Trivially safe
+                if unsafe { WIFEXITED(status) } {
+                    match unsafe { WEXITSTATUS(status) } { // Trivially safe
+                        0 => Ok(ProcessStatus::Success),
+                        code => Ok(ProcessStatus::Fail(code)),
+                    }
+                } else {
+                    // Plugin terminated but has no exit status, so it must have been signaled.
+                    Ok(ProcessStatus::Signal(unsafe { WTERMSIG(status) })) // Trivially safe
                 }
             }
-            Ok(None) => Ok(ProcessStatus::Running),
-            Err(e) => Err(io_to_sys_err(e)),
         }
     }