diff options
| author | Theodore DeRego <tedsta@google.com> | 2016-11-11 18:21:03 -0800 |
|---|---|---|
| committer | Theodore DeRego <tedsta@google.com> | 2016-11-22 12:12:46 -0800 |
| commit | 5c23f2e3c88275cee37acbd36c12baba57c4de2b (patch) | |
| tree | f77bcfe12ed23ef51d5be7765c38ad9712e94f38 | |
| parent | 0491a231777735ba050c208ce621df93f863bf7c (diff) | |
| download | rust-5c23f2e3c88275cee37acbd36c12baba57c4de2b.tar.gz rust-5c23f2e3c88275cee37acbd36c12baba57c4de2b.zip | |
Fuchsia support for std::process via liblaunchpad.
| -rw-r--r-- | src/libstd/build.rs | 2 | ||||
| -rw-r--r-- | src/libstd/process.rs | 13 | ||||
| -rw-r--r-- | src/libstd/sys/unix/fd.rs | 2 | ||||
| -rw-r--r-- | src/libstd/sys/unix/magenta.rs | 157 | ||||
| -rw-r--r-- | src/libstd/sys/unix/mod.rs | 20 | ||||
| -rw-r--r-- | src/libstd/sys/unix/pipe.rs | 1 | ||||
| -rw-r--r-- | src/libstd/sys/unix/process.rs | 236 |
7 files changed, 426 insertions, 5 deletions
diff --git a/src/libstd/build.rs b/src/libstd/build.rs index 72cd6e4830b..1087d1f2447 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -60,6 +60,8 @@ fn main() { println!("cargo:rustc-link-lib=shell32"); } else if target.contains("fuchsia") { println!("cargo:rustc-link-lib=magenta"); + println!("cargo:rustc-link-lib=mxio"); + println!("cargo:rustc-link-lib=launchpad"); // for std::process } } diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 9d21a76e81b..a9c68e82175 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -780,6 +780,8 @@ impl Child { /// #[stable(feature = "process", since = "1.0.0")] pub fn wait_with_output(mut self) -> io::Result<Output> { + //use io::ErrorKind; + drop(self.stdin.take()); let (mut stdout, mut stderr) = (Vec::new(), Vec::new()); @@ -794,8 +796,15 @@ impl Child { res.unwrap(); } (Some(out), Some(err)) => { - let res = read2(out.inner, &mut stdout, err.inner, &mut stderr); - res.unwrap(); + match read2(out.inner, &mut stdout, err.inner, &mut stderr) { + Ok(()) => { }, + #[cfg(not(target_os = "fuchsia"))] + Err(ref e) => { panic!("Failed to read child's stdout and stderr: {:?}", e); }, + #[cfg(target_os = "fuchsia")] + Err(_) => { + // FIXME: Right now there's a bug in magenta's pipes implementation + }, + } } } diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index 41bb96fed16..61eb60da486 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -110,6 +110,7 @@ impl FileDesc { #[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten", + target_os = "fuchsia", target_os = "haiku")))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { @@ -120,6 +121,7 @@ impl FileDesc { #[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten", + target_os = "fuchsia", target_os = "haiku"))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { diff --git a/src/libstd/sys/unix/magenta.rs b/src/libstd/sys/unix/magenta.rs new file mode 100644 index 00000000000..ae3b7789b10 --- /dev/null +++ b/src/libstd/sys/unix/magenta.rs @@ -0,0 +1,157 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_camel_case_types)] + +use os::raw::c_char; +use u64; + +use libc::{c_int, c_void}; + +pub type mx_handle_t = i32; +pub type mx_vaddr_t = usize; +pub type mx_rights_t = u32; +pub type mx_status_t = i32; + +pub type mx_size_t = usize; +pub type mx_ssize_t = isize; + +pub const MX_HANDLE_INVALID: mx_handle_t = 0; + +pub type mx_time_t = u64; +pub const MX_TIME_INFINITE : mx_time_t = u64::MAX; + +pub const NO_ERROR : mx_status_t = 0; + +pub type mx_signals_t = u32; + +pub const MX_OBJECT_SIGNAL_3 : mx_signals_t = 1 << 3; + +pub const MX_TASK_TERMINATED : mx_signals_t = MX_OBJECT_SIGNAL_3; + +pub const MX_RIGHT_SAME_RIGHTS : mx_rights_t = 1 << 31; + +pub type mx_object_info_topic_t = u32; + +pub const MX_INFO_PROCESS : mx_object_info_topic_t = 3; + +pub const MX_HND_TYPE_JOB: u32 = 6; + +// Common MX_INFO header +#[derive(Default)] +#[repr(C)] +pub struct mx_info_header_t { + pub topic: u32, // identifies the info struct + pub avail_topic_size: u16, // “native” size of the struct + pub topic_size: u16, // size of the returned struct (<=topic_size) + pub avail_count: u32, // number of records the kernel has + pub count: u32, // number of records returned (limited by buffer size) +} + +#[derive(Default)] +#[repr(C)] +pub struct mx_record_process_t { + pub return_code: c_int, +} + +// Returned for topic MX_INFO_PROCESS +#[derive(Default)] +#[repr(C)] +pub struct mx_info_process_t { + pub hdr: mx_info_header_t, + pub rec: mx_record_process_t, +} + +#[link(name = "magenta")] +extern { + pub fn mx_handle_close(handle: mx_handle_t) -> mx_status_t; + + pub fn mx_handle_duplicate(handle: mx_handle_t, rights: mx_rights_t, + out: *const mx_handle_t) -> mx_handle_t; + + pub fn mx_handle_wait_one(handle: mx_handle_t, signals: mx_signals_t, timeout: mx_time_t, + pending: *mut mx_signals_t) -> mx_status_t; + + pub fn mx_object_get_info(handle: mx_handle_t, topic: u32, buffer: *mut c_void, + buffer_size: mx_size_t, actual_size: *mut mx_size_t, + avail: *mut mx_size_t) -> mx_status_t; +} + +// Handle Info entries associate a type and optional +// argument with each handle included in the process +// arguments message. +pub fn mx_hnd_info(hnd_type: u32, arg: u32) -> u32 { + (hnd_type & 0xFFFF) | ((arg & 0xFFFF) << 16) +} + +#[link(name="mxio")] +extern { + pub fn mxio_get_startup_handle(id: u32) -> mx_handle_t; +} + +// From `enum special_handles` in system/ulib/launchpad/launchpad.c +#[allow(unused)] pub const HND_LOADER_SVC: usize = 0; +// HND_EXEC_VMO = 1 +#[allow(unused)] pub const HND_SPECIAL_COUNT: usize = 2; + +#[repr(C)] +pub struct launchpad_t { + argc: u32, + envc: u32, + args: *const c_char, + args_len: usize, + env: *const c_char, + env_len: usize, + + handles: *mut mx_handle_t, + handles_info: *mut u32, + handle_count: usize, + handle_alloc: usize, + + entry: mx_vaddr_t, + base: mx_vaddr_t, + vdso_base: mx_vaddr_t, + + stack_size: usize, + + special_handles: [mx_handle_t; HND_SPECIAL_COUNT], + loader_message: bool, +} + +#[link(name="launchpad")] +extern { + pub fn launchpad_create(job: mx_handle_t, name: *const c_char, + lp: *mut *mut launchpad_t) -> mx_status_t; + + pub fn launchpad_start(lp: *mut launchpad_t) -> mx_status_t; + + pub fn launchpad_destroy(lp: *mut launchpad_t); + + pub fn launchpad_arguments(lp: *mut launchpad_t, argc: c_int, + argv: *const *const c_char) -> mx_status_t; + + pub fn launchpad_environ(lp: *mut launchpad_t, envp: *const *const c_char) -> mx_status_t; + + pub fn launchpad_clone_mxio_root(lp: *mut launchpad_t) -> mx_status_t; + + pub fn launchpad_clone_mxio_cwd(lp: *mut launchpad_t) -> mx_status_t; + + pub fn launchpad_clone_fd(lp: *mut launchpad_t, fd: c_int, target_fd: c_int) -> mx_status_t; + + pub fn launchpad_transfer_fd(lp: *mut launchpad_t, fd: c_int, target_fd: c_int) -> mx_status_t; + + pub fn launchpad_elf_load(lp: *mut launchpad_t, vmo: mx_handle_t) -> mx_status_t; + + pub fn launchpad_add_vdso_vmo(lp: *mut launchpad_t) -> mx_status_t; + + pub fn launchpad_load_vdso(lp: *mut launchpad_t, vmo: mx_handle_t) -> mx_status_t; + + pub fn launchpad_vmo_from_file(filename: *const c_char) -> mx_handle_t; +} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index fd7dc17cccd..8fe55af51d5 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -13,6 +13,11 @@ use io::{self, ErrorKind}; use libc; +#[cfg(target_os = "fuchsia")] +use convert::TryInto; +#[cfg(target_os = "fuchsia")] +pub use self::magenta::mx_status_t; + #[cfg(target_os = "android")] pub use os::android as platform; #[cfg(target_os = "bitrig")] pub use os::bitrig as platform; #[cfg(target_os = "dragonfly")] pub use os::dragonfly as platform; @@ -41,6 +46,8 @@ pub mod ext; pub mod fast_thread_local; pub mod fd; pub mod fs; +#[cfg(target_os = "fuchsia")] +pub mod magenta; pub mod memchr; pub mod mutex; pub mod net; @@ -164,6 +171,19 @@ pub fn cvt_r<T, F>(mut f: F) -> io::Result<T> } } +#[cfg(target_os = "fuchsia")] +pub fn mx_cvt<T>(t: T) -> io::Result<T> where T: TryInto<mx_status_t>+Copy { + if let Ok(status) = TryInto::try_into(t) { + if status < 0 { + Err(io::Error::from_raw_os_error(status)) + } else { + Ok(t) + } + } else { + Err(io::Error::last_os_error()) + } +} + // On Unix-like platforms, libc::abort will unregister signal handlers // including the SIGABRT handler, preventing the abort from being blocked, and // fclose streams, with the side effect of flushing them so libc bufferred diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index ffe8032e460..a8ed415b7f4 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -77,6 +77,7 @@ pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) -> io::Result<()> { + // Set both pipes into nonblocking mode as we're gonna be reading from both // in the `select` loop below, and we wouldn't want one to block the other! let p1 = p1.into_fd(); 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")))] |
