about summary refs log tree commit diff
path: root/src/libstd/sys/unix/process.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sys/unix/process.rs')
-rw-r--r--src/libstd/sys/unix/process.rs236
1 files changed, 233 insertions, 3 deletions
diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs
index dafc11d9cc8..aa203dc6215 100644
--- a/src/libstd/sys/unix/process.rs
+++ b/src/libstd/sys/unix/process.rs
@@ -15,13 +15,23 @@ use env;
 use ffi::{OsString, OsStr, CString, CStr};
 use fmt;
 use io::{self, Error, ErrorKind};
-use libc::{self, pid_t, c_int, gid_t, uid_t, c_char};
+use libc::{self, c_int, gid_t, uid_t, c_char};
 use mem;
 use ptr;
 use sys::fd::FileDesc;
 use sys::fs::{File, OpenOptions};
 use sys::pipe::{self, AnonPipe};
-use sys::{self, cvt, cvt_r};
+
+#[cfg(not(target_os = "fuchsia"))]
+use sys::cvt;
+#[cfg(target_os = "fuchsia")]
+use sys::mx_cvt;
+
+#[cfg(target_os = "fuchsia")]
+use sys::magenta::{launchpad_t, mx_handle_t};
+
+#[cfg(not(target_os = "fuchsia"))]
+use libc::pid_t;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Command
@@ -210,8 +220,11 @@ impl Command {
         self.stderr = Some(stderr);
     }
 
+    #[cfg(not(target_os = "fuchsia"))]
     pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
                  -> io::Result<(Process, StdioPipes)> {
+        use sys;
+
         const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
 
         if self.saw_nul {
@@ -286,6 +299,31 @@ impl Command {
         }
     }
 
+    #[cfg(target_os = "fuchsia")]
+    pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
+                 -> io::Result<(Process, StdioPipes)> {
+        if self.saw_nul {
+            return Err(io::Error::new(ErrorKind::InvalidInput,
+                                      "nul byte found in provided data"));
+        }
+
+        let (ours, theirs) = self.setup_io(default, needs_stdin)?;
+
+        let (maybe_process, err) = unsafe { self.do_exec(&theirs) };
+        // We don't want FileDesc::drop to be called on any stdio. It would close their handles.
+        let ChildPipes { stdin: their_stdin, stdout: their_stdout, stderr: their_stderr } = theirs;
+        their_stdin.fd();
+        their_stdout.fd();
+        their_stderr.fd();
+
+        if let Some((launchpad, process_handle)) = maybe_process {
+            Ok((Process { launchpad: launchpad, handle: process_handle, status: None }, ours))
+        } else {
+            Err(err)
+        }
+    }
+
+    #[cfg(not(target_os = "fuchsia"))]
     pub fn exec(&mut self, default: Stdio) -> io::Error {
         if self.saw_nul {
             return io::Error::new(ErrorKind::InvalidInput,
@@ -298,6 +336,22 @@ impl Command {
         }
     }
 
+    #[cfg(target_os = "fuchsia")]
+    pub fn exec(&mut self, default: Stdio) -> io::Error {
+        if self.saw_nul {
+            return io::Error::new(ErrorKind::InvalidInput,
+                                  "nul byte found in provided data")
+        }
+
+        match self.setup_io(default, true) {
+            Ok((_, _)) => {
+                // FIXME: This is tough because we don't support the exec syscalls
+                unimplemented!();
+            },
+            Err(e) => e,
+        }
+    }
+
     // And at this point we've reached a special time in the life of the
     // child. The child must now be considered hamstrung and unable to
     // do anything other than syscalls really. Consider the following
@@ -328,7 +382,10 @@ impl Command {
     // allocation). Instead we just close it manually. This will never
     // have the drop glue anyway because this code never returns (the
     // child will either exec() or invoke libc::exit)
+    #[cfg(not(target_os = "fuchsia"))]
     unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Error {
+        use sys::{self, cvt_r};
+
         macro_rules! t {
             ($e:expr) => (match $e {
                 Ok(e) => e,
@@ -395,6 +452,108 @@ impl Command {
         io::Error::last_os_error()
     }
 
+    #[cfg(target_os = "fuchsia")]
+    unsafe fn do_exec(&mut self, stdio: &ChildPipes)
+                      -> (Option<(*mut launchpad_t, mx_handle_t)>, io::Error) {
+        use sys::magenta::*;
+
+        macro_rules! t {
+            ($e:expr) => (match $e {
+                Ok(e) => e,
+                Err(e) => return (None, e),
+            })
+        }
+
+        macro_rules! tlp {
+            ($lp:expr, $e:expr) => (match $e {
+                Ok(e) => e,
+                Err(e) => {
+                    launchpad_destroy($lp);
+                    return (None, e);
+                },
+            })
+        }
+
+        let job_handle = mxio_get_startup_handle(mx_hnd_info(MX_HND_TYPE_JOB, 0));
+        let envp = match self.envp {
+            Some(ref envp) => envp.as_ptr(),
+            None => ptr::null(),
+        };
+
+        let mut launchpad: *mut launchpad_t = ptr::null_mut();
+        let mut job_copy: mx_handle_t = MX_HANDLE_INVALID;
+
+        // Duplicate the job handle
+        t!(mx_cvt(mx_handle_duplicate(job_handle, MX_RIGHT_SAME_RIGHTS,
+                                   &mut job_copy as *mut mx_handle_t)));
+        // Create a launchpad
+        t!(mx_cvt(launchpad_create(job_copy, self.argv[0],
+                                &mut launchpad as *mut *mut launchpad_t)));
+        // Set the process argv
+        tlp!(launchpad, mx_cvt(launchpad_arguments(launchpad, self.argv.len() as i32 - 1,
+                                                self.argv.as_ptr())));
+        // Setup the environment vars
+        let status = launchpad_environ(launchpad, envp);
+        if status != NO_ERROR {
+            launchpad_destroy(launchpad);
+            return (None, io::Error::last_os_error());
+        }
+        let status = launchpad_add_vdso_vmo(launchpad);
+        if status != NO_ERROR {
+            launchpad_destroy(launchpad);
+            return (None, io::Error::last_os_error());
+        }
+        let status = launchpad_clone_mxio_root(launchpad);
+        if status != NO_ERROR {
+            launchpad_destroy(launchpad);
+            return (None, io::Error::last_os_error());
+        }
+        // Load the executable
+        let status = launchpad_elf_load(launchpad, launchpad_vmo_from_file(self.argv[0]));
+        if status != NO_ERROR {
+            launchpad_destroy(launchpad);
+            return (None, io::Error::last_os_error());
+        }
+        let status = launchpad_load_vdso(launchpad, MX_HANDLE_INVALID);
+        if status != NO_ERROR {
+            launchpad_destroy(launchpad);
+            return (None, io::Error::last_os_error());
+        }
+        let status = launchpad_clone_mxio_cwd(launchpad);
+        if status != NO_ERROR {
+            launchpad_destroy(launchpad);
+            return (None, io::Error::last_os_error());
+        }
+
+        // Clone stdin, stdout, and stderr
+        if let Some(fd) = stdio.stdin.fd() {
+            launchpad_transfer_fd(launchpad, fd, 0);
+        } else {
+            launchpad_clone_fd(launchpad, 0, 0);
+        }
+        if let Some(fd) = stdio.stdout.fd() {
+            launchpad_transfer_fd(launchpad, fd, 1);
+        } else {
+            launchpad_clone_fd(launchpad, 1, 1);
+        }
+        if let Some(fd) = stdio.stderr.fd() {
+            launchpad_transfer_fd(launchpad, fd, 2);
+        } else {
+            launchpad_clone_fd(launchpad, 2, 2);
+        }
+
+        for callback in self.closures.iter_mut() {
+            t!(callback());
+        }
+
+        let process_handle = launchpad_start(launchpad);
+        if process_handle < 0 {
+            launchpad_destroy(launchpad);
+            return (None, io::Error::last_os_error());
+        }
+
+        (Some((launchpad, process_handle)), io::Error::last_os_error())
+    }
 
     fn setup_io(&self, default: Stdio, needs_stdin: bool)
                 -> io::Result<(StdioPipes, ChildPipes)> {
@@ -431,7 +590,9 @@ impl Stdio {
     fn to_child_stdio(&self, readable: bool)
                       -> io::Result<(ChildStdio, Option<AnonPipe>)> {
         match *self {
-            Stdio::Inherit => Ok((ChildStdio::Inherit, None)),
+            Stdio::Inherit => {
+                Ok((ChildStdio::Inherit, None))
+            },
 
             // Make sure that the source descriptors are not an stdio
             // descriptor, otherwise the order which we set the child's
@@ -556,16 +717,31 @@ impl fmt::Display for ExitStatus {
 }
 
 /// The unique id of the process (this should never be negative).
+#[cfg(not(target_os = "fuchsia"))]
 pub struct Process {
     pid: pid_t,
     status: Option<ExitStatus>,
 }
 
+#[cfg(target_os = "fuchsia")]
+pub struct Process {
+    launchpad: *mut launchpad_t,
+    handle: mx_handle_t,
+    status: Option<ExitStatus>,
+}
+
 impl Process {
+    #[cfg(not(target_os = "fuchsia"))]
     pub fn id(&self) -> u32 {
         self.pid as u32
     }
 
+    #[cfg(target_os = "fuchsia")]
+    pub fn id(&self) -> u32 {
+        0
+    }
+
+    #[cfg(not(target_os = "fuchsia"))]
     pub fn kill(&mut self) -> io::Result<()> {
         // If we've already waited on this process then the pid can be recycled
         // and used for another process, and we probably shouldn't be killing
@@ -578,7 +754,28 @@ impl Process {
         }
     }
 
+    #[cfg(target_os = "fuchsia")]
+    pub fn kill(&mut self) -> io::Result<()> {
+        use sys::magenta::*;
+
+        // If we've already waited on this process then the pid can be recycled
+        // and used for another process, and we probably shouldn't be killing
+        // random processes, so just return an error.
+        if self.status.is_some() {
+            Err(Error::new(ErrorKind::InvalidInput,
+                           "invalid argument: can't kill an exited process"))
+        } else {
+            unsafe {
+                mx_cvt(mx_handle_close(self.handle))?;
+                launchpad_destroy(self.launchpad);
+            }
+            Ok(())
+        }
+    }
+
+    #[cfg(not(target_os = "fuchsia"))]
     pub fn wait(&mut self) -> io::Result<ExitStatus> {
+        use sys::cvt_r;
         if let Some(status) = self.status {
             return Ok(status)
         }
@@ -587,6 +784,39 @@ impl Process {
         self.status = Some(ExitStatus(status));
         Ok(ExitStatus(status))
     }
+
+    #[cfg(target_os = "fuchsia")]
+    pub fn wait(&mut self) -> io::Result<ExitStatus> {
+        use default::Default;
+        use sys::magenta::*;
+
+        if let Some(status) = self.status {
+            return Ok(status)
+        }
+
+        let mut proc_info: mx_info_process_t = Default::default();
+        let mut actual: mx_size_t = 0;
+        let mut avail: mx_size_t = 0;
+
+        unsafe {
+            mx_cvt(mx_handle_wait_one(self.handle, MX_TASK_TERMINATED,
+                                      MX_TIME_INFINITE, ptr::null_mut()))?;
+            mx_cvt(mx_object_get_info(self.handle, MX_INFO_PROCESS,
+                                      &mut proc_info as *mut _ as *mut libc::c_void,
+                                      mem::size_of::<mx_info_process_t>(), &mut actual,
+                                      &mut avail))?;
+        }
+        if actual != 1 {
+            return Err(Error::new(ErrorKind::InvalidInput,
+                                  "Failed to get exit status of process"));
+        }
+        self.status = Some(ExitStatus(proc_info.rec.return_code));
+        unsafe {
+            mx_cvt(mx_handle_close(self.handle))?;
+            launchpad_destroy(self.launchpad);
+        }
+        Ok(ExitStatus(proc_info.rec.return_code))
+    }
 }
 
 #[cfg(all(test, not(target_os = "emscripten")))]