From 5c23f2e3c88275cee37acbd36c12baba57c4de2b Mon Sep 17 00:00:00 2001 From: Theodore DeRego Date: Fri, 11 Nov 2016 18:21:03 -0800 Subject: Fuchsia support for std::process via liblaunchpad. --- src/libstd/sys/unix/fd.rs | 2 + src/libstd/sys/unix/magenta.rs | 157 +++++++++++++++++++++++++++ src/libstd/sys/unix/mod.rs | 20 ++++ src/libstd/sys/unix/pipe.rs | 1 + src/libstd/sys/unix/process.rs | 236 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 413 insertions(+), 3 deletions(-) create mode 100644 src/libstd/sys/unix/magenta.rs (limited to 'src/libstd/sys') 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 or the MIT license +// , 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(mut f: F) -> io::Result } } +#[cfg(target_os = "fuchsia")] +pub fn mx_cvt(t: T) -> io::Result where T: TryInto+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, p2: AnonPipe, v2: &mut Vec) -> 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)> { 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, } +#[cfg(target_os = "fuchsia")] +pub struct Process { + launchpad: *mut launchpad_t, + handle: mx_handle_t, + status: Option, +} + 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 { + 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 { + 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::(), &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")))] -- cgit 1.4.1-3-g733a5 From fae86b92dec2a7439a071270193c3c72b65b7ccd Mon Sep 17 00:00:00 2001 From: Theodore DeRego Date: Tue, 22 Nov 2016 16:08:48 -0800 Subject: Cleaned up and appeased the linter --- src/libstd/process.rs | 13 +----- src/libstd/sys/unix/magenta.rs | 2 - src/libstd/sys/unix/process.rs | 92 +++++++++++++----------------------------- 3 files changed, 30 insertions(+), 77 deletions(-) (limited to 'src/libstd/sys') diff --git a/src/libstd/process.rs b/src/libstd/process.rs index a9c68e82175..0b59de55cc6 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -780,8 +780,6 @@ impl Child { /// #[stable(feature = "process", since = "1.0.0")] pub fn wait_with_output(mut self) -> io::Result { - //use io::ErrorKind; - drop(self.stdin.take()); let (mut stdout, mut stderr) = (Vec::new(), Vec::new()); @@ -796,15 +794,8 @@ impl Child { res.unwrap(); } (Some(out), Some(err)) => { - 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 - }, - } + let res = read2(out.inner, &mut stdout, err.inner, &mut stderr); + res.update(); } } diff --git a/src/libstd/sys/unix/magenta.rs b/src/libstd/sys/unix/magenta.rs index ae3b7789b10..9da827c7d31 100644 --- a/src/libstd/sys/unix/magenta.rs +++ b/src/libstd/sys/unix/magenta.rs @@ -28,8 +28,6 @@ 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; diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index aa203dc6215..d660514a983 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -309,18 +309,9 @@ impl Command { 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) - } + let (launchpad, process_handle) = unsafe { self.do_exec(theirs)? }; + + Ok((Process { launchpad: launchpad, handle: process_handle, status: None }, ours)) } #[cfg(not(target_os = "fuchsia"))] @@ -453,23 +444,16 @@ impl Command { } #[cfg(target_os = "fuchsia")] - unsafe fn do_exec(&mut self, stdio: &ChildPipes) - -> (Option<(*mut launchpad_t, mx_handle_t)>, io::Error) { + unsafe fn do_exec(&mut self, stdio: ChildPipes) + -> io::Result<(*mut launchpad_t, mx_handle_t)> { 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); + return Err(e); }, }) } @@ -484,46 +468,23 @@ impl Command { 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))); + 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))); + 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()))); + 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()); - } + tlp!(launchpad, mx_cvt(launchpad_environ(launchpad, envp))); + tlp!(launchpad, mx_cvt(launchpad_add_vdso_vmo(launchpad))); + tlp!(launchpad, mx_cvt(launchpad_clone_mxio_root(launchpad))); // 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()); - } + tlp!(launchpad, + mx_cvt(launchpad_elf_load(launchpad, launchpad_vmo_from_file(self.argv[0])))); + tlp!(launchpad, mx_cvt(launchpad_load_vdso(launchpad, MX_HANDLE_INVALID))); + tlp!(launchpad, mx_cvt(launchpad_clone_mxio_cwd(launchpad))); // Clone stdin, stdout, and stderr if let Some(fd) = stdio.stdin.fd() { @@ -542,17 +503,20 @@ impl Command { launchpad_clone_fd(launchpad, 2, 2); } + // We don't want FileDesc::drop to be called on any stdio. It would close their fds. The + // fds will be closed once the child process finishes. + let ChildPipes { stdin: child_stdin, stdout: child_stdout, stderr: child_stderr } = stdio; + if let ChildStdio::Owned(fd) = child_stdin { fd.into_raw(); } + if let ChildStdio::Owned(fd) = child_stdout { fd.into_raw(); } + if let ChildStdio::Owned(fd) = child_stderr { fd.into_raw(); } + for callback in self.closures.iter_mut() { - t!(callback()); + callback()?; } - let process_handle = launchpad_start(launchpad); - if process_handle < 0 { - launchpad_destroy(launchpad); - return (None, io::Error::last_os_error()); - } + let process_handle = tlp!(launchpad, mx_cvt(launchpad_start(launchpad))); - (Some((launchpad, process_handle)), io::Error::last_os_error()) + Ok((launchpad, process_handle)) } fn setup_io(&self, default: Stdio, needs_stdin: bool) -- cgit 1.4.1-3-g733a5 From 5c1c48532f1f5ce726d1704d33366f8fb371cca0 Mon Sep 17 00:00:00 2001 From: Theodore DeRego Date: Wed, 23 Nov 2016 13:58:13 -0800 Subject: Separated fuchsia-specific process stuff into 'process_fuchsia.rs' and refactored out some now-duplicated code into a 'process_common.rs' --- src/libstd/sys/unix/magenta.rs | 3 - src/libstd/sys/unix/process.rs | 865 ------------------------- src/libstd/sys/unix/process/mod.rs | 20 + src/libstd/sys/unix/process/process_common.rs | 480 ++++++++++++++ src/libstd/sys/unix/process/process_fuchsia.rs | 190 ++++++ src/libstd/sys/unix/process/process_unix.rs | 250 +++++++ 6 files changed, 940 insertions(+), 868 deletions(-) delete mode 100644 src/libstd/sys/unix/process.rs create mode 100644 src/libstd/sys/unix/process/mod.rs create mode 100644 src/libstd/sys/unix/process/process_common.rs create mode 100644 src/libstd/sys/unix/process/process_fuchsia.rs create mode 100644 src/libstd/sys/unix/process/process_unix.rs (limited to 'src/libstd/sys') diff --git a/src/libstd/sys/unix/magenta.rs b/src/libstd/sys/unix/magenta.rs index 9da827c7d31..155259d2645 100644 --- a/src/libstd/sys/unix/magenta.rs +++ b/src/libstd/sys/unix/magenta.rs @@ -67,7 +67,6 @@ pub struct mx_info_process_t { pub rec: mx_record_process_t, } -#[link(name = "magenta")] extern { pub fn mx_handle_close(handle: mx_handle_t) -> mx_status_t; @@ -89,7 +88,6 @@ 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; } @@ -123,7 +121,6 @@ pub struct launchpad_t { 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; diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs deleted file mode 100644 index d660514a983..00000000000 --- a/src/libstd/sys/unix/process.rs +++ /dev/null @@ -1,865 +0,0 @@ -// Copyright 2014-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use os::unix::prelude::*; - -use collections::hash_map::{HashMap, Entry}; -use env; -use ffi::{OsString, OsStr, CString, CStr}; -use fmt; -use io::{self, Error, ErrorKind}; -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}; - -#[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 -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - // Currently we try hard to ensure that the call to `.exec()` doesn't - // actually allocate any memory. While many platforms try to ensure that - // memory allocation works after a fork in a multithreaded process, it's - // been observed to be buggy and somewhat unreliable, so we do our best to - // just not do it at all! - // - // Along those lines, the `argv` and `envp` raw pointers here are exactly - // what's gonna get passed to `execvp`. The `argv` array starts with the - // `program` and ends with a NULL, and the `envp` pointer, if present, is - // also null-terminated. - // - // Right now we don't support removing arguments, so there's no much fancy - // support there, but we support adding and removing environment variables, - // so a side table is used to track where in the `envp` array each key is - // located. Whenever we add a key we update it in place if it's already - // present, and whenever we remove a key we update the locations of all - // other keys. - program: CString, - args: Vec, - env: Option>, - argv: Vec<*const c_char>, - envp: Option>, - - cwd: Option, - uid: Option, - gid: Option, - saw_nul: bool, - closures: Vec io::Result<()> + Send + Sync>>, - stdin: Option, - stdout: Option, - stderr: Option, -} - -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - -// passed to do_exec() with configuration of what the child stdio should look -// like -struct ChildPipes { - stdin: ChildStdio, - stdout: ChildStdio, - stderr: ChildStdio, -} - -enum ChildStdio { - Inherit, - Explicit(c_int), - Owned(FileDesc), -} - -pub enum Stdio { - Inherit, - Null, - MakePipe, - Fd(FileDesc), -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - let mut saw_nul = false; - let program = os2c(program, &mut saw_nul); - Command { - argv: vec![program.as_ptr(), ptr::null()], - program: program, - args: Vec::new(), - env: None, - envp: None, - cwd: None, - uid: None, - gid: None, - saw_nul: saw_nul, - closures: Vec::new(), - stdin: None, - stdout: None, - stderr: None, - } - } - - pub fn arg(&mut self, arg: &OsStr) { - // Overwrite the trailing NULL pointer in `argv` and then add a new null - // pointer. - let arg = os2c(arg, &mut self.saw_nul); - self.argv[self.args.len() + 1] = arg.as_ptr(); - self.argv.push(ptr::null()); - - // Also make sure we keep track of the owned value to schedule a - // destructor for this memory. - self.args.push(arg); - } - - fn init_env_map(&mut self) -> (&mut HashMap, - &mut Vec<*const c_char>) { - if self.env.is_none() { - let mut map = HashMap::new(); - let mut envp = Vec::new(); - for (k, v) in env::vars_os() { - let s = pair_to_key(&k, &v, &mut self.saw_nul); - envp.push(s.as_ptr()); - map.insert(k, (envp.len() - 1, s)); - } - envp.push(ptr::null()); - self.env = Some(map); - self.envp = Some(envp); - } - (self.env.as_mut().unwrap(), self.envp.as_mut().unwrap()) - } - - pub fn env(&mut self, key: &OsStr, val: &OsStr) { - let new_key = pair_to_key(key, val, &mut self.saw_nul); - let (map, envp) = self.init_env_map(); - - // If `key` is already present then we just update `envp` in place - // (and store the owned value), but if it's not there we override the - // trailing NULL pointer, add a new NULL pointer, and store where we - // were located. - match map.entry(key.to_owned()) { - Entry::Occupied(mut e) => { - let (i, ref mut s) = *e.get_mut(); - envp[i] = new_key.as_ptr(); - *s = new_key; - } - Entry::Vacant(e) => { - let len = envp.len(); - envp[len - 1] = new_key.as_ptr(); - envp.push(ptr::null()); - e.insert((len - 1, new_key)); - } - } - } - - pub fn env_remove(&mut self, key: &OsStr) { - let (map, envp) = self.init_env_map(); - - // If we actually ended up removing a key, then we need to update the - // position of all keys that come after us in `envp` because they're all - // one element sooner now. - if let Some((i, _)) = map.remove(key) { - envp.remove(i); - - for (_, &mut (ref mut j, _)) in map.iter_mut() { - if *j >= i { - *j -= 1; - } - } - } - } - - pub fn env_clear(&mut self) { - self.env = Some(HashMap::new()); - self.envp = Some(vec![ptr::null()]); - } - - pub fn cwd(&mut self, dir: &OsStr) { - self.cwd = Some(os2c(dir, &mut self.saw_nul)); - } - pub fn uid(&mut self, id: uid_t) { - self.uid = Some(id); - } - pub fn gid(&mut self, id: gid_t) { - self.gid = Some(id); - } - - pub fn before_exec(&mut self, - f: Box io::Result<()> + Send + Sync>) { - self.closures.push(f); - } - - pub fn stdin(&mut self, stdin: Stdio) { - self.stdin = Some(stdin); - } - pub fn stdout(&mut self, stdout: Stdio) { - self.stdout = Some(stdout); - } - pub fn stderr(&mut self, stderr: Stdio) { - 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 { - return Err(io::Error::new(ErrorKind::InvalidInput, - "nul byte found in provided data")); - } - - let (ours, theirs) = self.setup_io(default, needs_stdin)?; - let (input, output) = sys::pipe::anon_pipe()?; - - let pid = unsafe { - match cvt(libc::fork())? { - 0 => { - drop(input); - let err = self.do_exec(theirs); - let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; - let bytes = [ - (errno >> 24) as u8, - (errno >> 16) as u8, - (errno >> 8) as u8, - (errno >> 0) as u8, - CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], - CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] - ]; - // pipe I/O up to PIPE_BUF bytes should be atomic, and then - // we want to be sure we *don't* run at_exit destructors as - // we're being torn down regardless - assert!(output.write(&bytes).is_ok()); - libc::_exit(1) - } - n => n, - } - }; - - let mut p = Process { pid: pid, status: None }; - drop(output); - let mut bytes = [0; 8]; - - // loop to handle EINTR - loop { - match input.read(&mut bytes) { - Ok(0) => return Ok((p, ours)), - Ok(8) => { - assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), - "Validation on the CLOEXEC pipe failed: {:?}", bytes); - let errno = combine(&bytes[0.. 4]); - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - return Err(Error::from_raw_os_error(errno)) - } - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => { - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - panic!("the CLOEXEC pipe failed: {:?}", e) - }, - Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - panic!("short read on the CLOEXEC pipe") - } - } - } - - fn combine(arr: &[u8]) -> i32 { - let a = arr[0] as u32; - let b = arr[1] as u32; - let c = arr[2] as u32; - let d = arr[3] as u32; - - ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 - } - } - - #[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 (launchpad, process_handle) = unsafe { self.do_exec(theirs)? }; - - Ok((Process { launchpad: launchpad, handle: process_handle, status: None }, ours)) - } - - #[cfg(not(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((_, theirs)) => unsafe { self.do_exec(theirs) }, - Err(e) => e, - } - } - - #[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 - // scenario: - // - // 1. Thread A of process 1 grabs the malloc() mutex - // 2. Thread B of process 1 forks(), creating thread C - // 3. Thread C of process 2 then attempts to malloc() - // 4. The memory of process 2 is the same as the memory of - // process 1, so the mutex is locked. - // - // This situation looks a lot like deadlock, right? It turns out - // that this is what pthread_atfork() takes care of, which is - // presumably implemented across platforms. The first thing that - // threads to *before* forking is to do things like grab the malloc - // mutex, and then after the fork they unlock it. - // - // Despite this information, libnative's spawn has been witnessed to - // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but - // all collected backtraces point at malloc/free traffic in the - // child spawned process. - // - // For this reason, the block of code below should contain 0 - // invocations of either malloc of free (or their related friends). - // - // As an example of not having malloc/free traffic, we don't close - // this file descriptor by dropping the FileDesc (which contains an - // 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, - Err(e) => return e, - }) - } - - if let Some(fd) = stdio.stdin.fd() { - t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); - } - if let Some(fd) = stdio.stdout.fd() { - t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); - } - if let Some(fd) = stdio.stderr.fd() { - t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); - } - - if let Some(u) = self.gid { - t!(cvt(libc::setgid(u as gid_t))); - } - if let Some(u) = self.uid { - // When dropping privileges from root, the `setgroups` call - // will remove any extraneous groups. If we don't call this, - // then even though our uid has dropped, we may still have - // groups that enable us to do super-user things. This will - // fail if we aren't root, so don't bother checking the - // return value, this is just done as an optimistic - // privilege dropping function. - let _ = libc::setgroups(0, ptr::null()); - - t!(cvt(libc::setuid(u as uid_t))); - } - if let Some(ref cwd) = self.cwd { - t!(cvt(libc::chdir(cwd.as_ptr()))); - } - if let Some(ref envp) = self.envp { - *sys::os::environ() = envp.as_ptr(); - } - - // NaCl has no signal support. - if cfg!(not(any(target_os = "nacl", target_os = "emscripten"))) { - // Reset signal handling so the child process starts in a - // standardized state. libstd ignores SIGPIPE, and signal-handling - // libraries often set a mask. Child processes inherit ignored - // signals and the signal mask from their parent, but most - // UNIX programs do not reset these things on their own, so we - // need to clean things up now to avoid confusing the program - // we're about to run. - let mut set: libc::sigset_t = mem::uninitialized(); - t!(cvt(libc::sigemptyset(&mut set))); - t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, - ptr::null_mut()))); - let ret = super::signal(libc::SIGPIPE, libc::SIG_DFL); - if ret == libc::SIG_ERR { - return io::Error::last_os_error() - } - } - - for callback in self.closures.iter_mut() { - t!(callback()); - } - - libc::execvp(self.argv[0], self.argv.as_ptr()); - io::Error::last_os_error() - } - - #[cfg(target_os = "fuchsia")] - unsafe fn do_exec(&mut self, stdio: ChildPipes) - -> io::Result<(*mut launchpad_t, mx_handle_t)> { - use sys::magenta::*; - - macro_rules! tlp { - ($lp:expr, $e:expr) => (match $e { - Ok(e) => e, - Err(e) => { - launchpad_destroy($lp); - return Err(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 - mx_cvt(mx_handle_duplicate(job_handle, MX_RIGHT_SAME_RIGHTS, - &mut job_copy as *mut mx_handle_t))?; - // Create a launchpad - 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 - tlp!(launchpad, mx_cvt(launchpad_environ(launchpad, envp))); - tlp!(launchpad, mx_cvt(launchpad_add_vdso_vmo(launchpad))); - tlp!(launchpad, mx_cvt(launchpad_clone_mxio_root(launchpad))); - // Load the executable - tlp!(launchpad, - mx_cvt(launchpad_elf_load(launchpad, launchpad_vmo_from_file(self.argv[0])))); - tlp!(launchpad, mx_cvt(launchpad_load_vdso(launchpad, MX_HANDLE_INVALID))); - tlp!(launchpad, mx_cvt(launchpad_clone_mxio_cwd(launchpad))); - - // 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); - } - - // We don't want FileDesc::drop to be called on any stdio. It would close their fds. The - // fds will be closed once the child process finishes. - let ChildPipes { stdin: child_stdin, stdout: child_stdout, stderr: child_stderr } = stdio; - if let ChildStdio::Owned(fd) = child_stdin { fd.into_raw(); } - if let ChildStdio::Owned(fd) = child_stdout { fd.into_raw(); } - if let ChildStdio::Owned(fd) = child_stderr { fd.into_raw(); } - - for callback in self.closures.iter_mut() { - callback()?; - } - - let process_handle = tlp!(launchpad, mx_cvt(launchpad_start(launchpad))); - - Ok((launchpad, process_handle)) - } - - fn setup_io(&self, default: Stdio, needs_stdin: bool) - -> io::Result<(StdioPipes, ChildPipes)> { - let null = Stdio::Null; - let default_stdin = if needs_stdin {&default} else {&null}; - let stdin = self.stdin.as_ref().unwrap_or(default_stdin); - let stdout = self.stdout.as_ref().unwrap_or(&default); - let stderr = self.stderr.as_ref().unwrap_or(&default); - let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; - let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; - let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; - let ours = StdioPipes { - stdin: our_stdin, - stdout: our_stdout, - stderr: our_stderr, - }; - let theirs = ChildPipes { - stdin: their_stdin, - stdout: their_stdout, - stderr: their_stderr, - }; - Ok((ours, theirs)) - } -} - -fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { - CString::new(s.as_bytes()).unwrap_or_else(|_e| { - *saw_nul = true; - CString::new("").unwrap() - }) -} - -impl Stdio { - fn to_child_stdio(&self, readable: bool) - -> io::Result<(ChildStdio, Option)> { - match *self { - 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 - // descriptors may blow away a descriptor which we are hoping to - // save. For example, suppose we want the child's stderr to be the - // parent's stdout, and the child's stdout to be the parent's - // stderr. No matter which we dup first, the second will get - // overwritten prematurely. - Stdio::Fd(ref fd) => { - if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO { - Ok((ChildStdio::Owned(fd.duplicate()?), None)) - } else { - Ok((ChildStdio::Explicit(fd.raw()), None)) - } - } - - Stdio::MakePipe => { - let (reader, writer) = pipe::anon_pipe()?; - let (ours, theirs) = if readable { - (writer, reader) - } else { - (reader, writer) - }; - Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) - } - - Stdio::Null => { - let mut opts = OpenOptions::new(); - opts.read(readable); - opts.write(!readable); - let path = unsafe { - CStr::from_ptr("/dev/null\0".as_ptr() as *const _) - }; - let fd = File::open_c(&path, &opts)?; - Ok((ChildStdio::Owned(fd.into_fd()), None)) - } - } - } -} - -impl ChildStdio { - fn fd(&self) -> Option { - match *self { - ChildStdio::Inherit => None, - ChildStdio::Explicit(fd) => Some(fd), - ChildStdio::Owned(ref fd) => Some(fd.raw()), - } - } -} - -fn pair_to_key(key: &OsStr, value: &OsStr, saw_nul: &mut bool) -> CString { - let (key, value) = (key.as_bytes(), value.as_bytes()); - let mut v = Vec::with_capacity(key.len() + value.len() + 1); - v.extend(key); - v.push(b'='); - v.extend(value); - CString::new(v).unwrap_or_else(|_e| { - *saw_nul = true; - CString::new("foo=bar").unwrap() - }) -} - -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.program)?; - for arg in &self.args { - write!(f, " {:?}", arg)?; - } - Ok(()) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -/// Unix exit statuses -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatus(c_int); - -impl ExitStatus { - fn exited(&self) -> bool { - unsafe { libc::WIFEXITED(self.0) } - } - - pub fn success(&self) -> bool { - self.code() == Some(0) - } - - pub fn code(&self) -> Option { - if self.exited() { - Some(unsafe { libc::WEXITSTATUS(self.0) }) - } else { - None - } - } - - pub fn signal(&self) -> Option { - if !self.exited() { - Some(unsafe { libc::WTERMSIG(self.0) }) - } else { - None - } - } -} - -impl From for ExitStatus { - fn from(a: c_int) -> ExitStatus { - ExitStatus(a) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(code) = self.code() { - write!(f, "exit code: {}", code) - } else { - let signal = self.signal().unwrap(); - write!(f, "signal: {}", signal) - } - } -} - -/// The unique id of the process (this should never be negative). -#[cfg(not(target_os = "fuchsia"))] -pub struct Process { - pid: pid_t, - status: Option, -} - -#[cfg(target_os = "fuchsia")] -pub struct Process { - launchpad: *mut launchpad_t, - handle: mx_handle_t, - status: Option, -} - -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 - // 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 { - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ()) - } - } - - #[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 { - use sys::cvt_r; - if let Some(status) = self.status { - return Ok(status) - } - let mut status = 0 as c_int; - cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; - self.status = Some(ExitStatus(status)); - Ok(ExitStatus(status)) - } - - #[cfg(target_os = "fuchsia")] - pub fn wait(&mut self) -> io::Result { - 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::(), &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")))] -mod tests { - use super::*; - - use ffi::OsStr; - use mem; - use ptr; - use libc; - use sys::cvt; - - macro_rules! t { - ($e:expr) => { - match $e { - Ok(t) => t, - Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), - } - } - } - - #[cfg(not(target_os = "android"))] - extern { - #[cfg_attr(target_os = "netbsd", link_name = "__sigaddset14")] - fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int; - } - - #[cfg(target_os = "android")] - unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int { - use slice; - - let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::()); - let bit = (signum - 1) as usize; - raw[bit / 8] |= 1 << (bit % 8); - return 0; - } - - // See #14232 for more information, but it appears that signal delivery to a - // newly spawned process may just be raced in the OSX, so to prevent this - // test from being flaky we ignore it on OSX. - #[test] - #[cfg_attr(target_os = "macos", ignore)] - #[cfg_attr(target_os = "nacl", ignore)] // no signals on NaCl. - fn test_process_mask() { - unsafe { - // Test to make sure that a signal mask does not get inherited. - let mut cmd = Command::new(OsStr::new("cat")); - - let mut set: libc::sigset_t = mem::uninitialized(); - let mut old_set: libc::sigset_t = mem::uninitialized(); - t!(cvt(libc::sigemptyset(&mut set))); - t!(cvt(sigaddset(&mut set, libc::SIGINT))); - t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, &mut old_set))); - - cmd.stdin(Stdio::MakePipe); - cmd.stdout(Stdio::MakePipe); - - let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true)); - let stdin_write = pipes.stdin.take().unwrap(); - let stdout_read = pipes.stdout.take().unwrap(); - - t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &old_set, - ptr::null_mut()))); - - t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT))); - // We need to wait until SIGINT is definitely delivered. The - // easiest way is to write something to cat, and try to read it - // back: if SIGINT is unmasked, it'll get delivered when cat is - // next scheduled. - let _ = stdin_write.write(b"Hello"); - drop(stdin_write); - - // Either EOF or failure (EPIPE) is okay. - let mut buf = [0; 5]; - if let Ok(ret) = stdout_read.read(&mut buf) { - assert!(ret == 0); - } - - t!(cat.wait()); - } - } -} diff --git a/src/libstd/sys/unix/process/mod.rs b/src/libstd/sys/unix/process/mod.rs new file mode 100644 index 00000000000..82c3971ee40 --- /dev/null +++ b/src/libstd/sys/unix/process/mod.rs @@ -0,0 +1,20 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::process_common::{Command, ExitStatus, Stdio, StdioPipes}; +pub use self::process_inner::Process; + +mod process_common; +#[cfg(not(target_os = "fuchsia"))] +#[path = "process_unix.rs"] +mod process_inner; +#[cfg(target_os = "fuchsia")] +#[path = "process_fuchsia.rs"] +mod process_inner; diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs new file mode 100644 index 00000000000..24b8b61edea --- /dev/null +++ b/src/libstd/sys/unix/process/process_common.rs @@ -0,0 +1,480 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use os::unix::prelude::*; + +use collections::hash_map::{HashMap, Entry}; +use env; +use ffi::{OsString, OsStr, CString, CStr}; +use fmt; +use io; +use libc::{self, c_int, gid_t, uid_t, c_char}; +use ptr; +use sys::fd::FileDesc; +use sys::fs::{File, OpenOptions}; +use sys::pipe::{self, AnonPipe}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + // Currently we try hard to ensure that the call to `.exec()` doesn't + // actually allocate any memory. While many platforms try to ensure that + // memory allocation works after a fork in a multithreaded process, it's + // been observed to be buggy and somewhat unreliable, so we do our best to + // just not do it at all! + // + // Along those lines, the `argv` and `envp` raw pointers here are exactly + // what's gonna get passed to `execvp`. The `argv` array starts with the + // `program` and ends with a NULL, and the `envp` pointer, if present, is + // also null-terminated. + // + // Right now we don't support removing arguments, so there's no much fancy + // support there, but we support adding and removing environment variables, + // so a side table is used to track where in the `envp` array each key is + // located. Whenever we add a key we update it in place if it's already + // present, and whenever we remove a key we update the locations of all + // other keys. + program: CString, + args: Vec, + env: Option>, + argv: Vec<*const c_char>, + envp: Option>, + + cwd: Option, + uid: Option, + gid: Option, + saw_nul: bool, + closures: Vec io::Result<()> + Send + Sync>>, + stdin: Option, + stdout: Option, + stderr: Option, +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +// passed to do_exec() with configuration of what the child stdio should look +// like +pub struct ChildPipes { + pub stdin: ChildStdio, + pub stdout: ChildStdio, + pub stderr: ChildStdio, +} + +pub enum ChildStdio { + Inherit, + Explicit(c_int), + Owned(FileDesc), +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, + Fd(FileDesc), +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + let mut saw_nul = false; + let program = os2c(program, &mut saw_nul); + Command { + argv: vec![program.as_ptr(), ptr::null()], + program: program, + args: Vec::new(), + env: None, + envp: None, + cwd: None, + uid: None, + gid: None, + saw_nul: saw_nul, + closures: Vec::new(), + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + // Overwrite the trailing NULL pointer in `argv` and then add a new null + // pointer. + let arg = os2c(arg, &mut self.saw_nul); + self.argv[self.args.len() + 1] = arg.as_ptr(); + self.argv.push(ptr::null()); + + // Also make sure we keep track of the owned value to schedule a + // destructor for this memory. + self.args.push(arg); + } + + fn init_env_map(&mut self) -> (&mut HashMap, + &mut Vec<*const c_char>) { + if self.env.is_none() { + let mut map = HashMap::new(); + let mut envp = Vec::new(); + for (k, v) in env::vars_os() { + let s = pair_to_key(&k, &v, &mut self.saw_nul); + envp.push(s.as_ptr()); + map.insert(k, (envp.len() - 1, s)); + } + envp.push(ptr::null()); + self.env = Some(map); + self.envp = Some(envp); + } + (self.env.as_mut().unwrap(), self.envp.as_mut().unwrap()) + } + + pub fn env(&mut self, key: &OsStr, val: &OsStr) { + let new_key = pair_to_key(key, val, &mut self.saw_nul); + let (map, envp) = self.init_env_map(); + + // If `key` is already present then we just update `envp` in place + // (and store the owned value), but if it's not there we override the + // trailing NULL pointer, add a new NULL pointer, and store where we + // were located. + match map.entry(key.to_owned()) { + Entry::Occupied(mut e) => { + let (i, ref mut s) = *e.get_mut(); + envp[i] = new_key.as_ptr(); + *s = new_key; + } + Entry::Vacant(e) => { + let len = envp.len(); + envp[len - 1] = new_key.as_ptr(); + envp.push(ptr::null()); + e.insert((len - 1, new_key)); + } + } + } + + pub fn env_remove(&mut self, key: &OsStr) { + let (map, envp) = self.init_env_map(); + + // If we actually ended up removing a key, then we need to update the + // position of all keys that come after us in `envp` because they're all + // one element sooner now. + if let Some((i, _)) = map.remove(key) { + envp.remove(i); + + for (_, &mut (ref mut j, _)) in map.iter_mut() { + if *j >= i { + *j -= 1; + } + } + } + } + + pub fn env_clear(&mut self) { + self.env = Some(HashMap::new()); + self.envp = Some(vec![ptr::null()]); + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(os2c(dir, &mut self.saw_nul)); + } + pub fn uid(&mut self, id: uid_t) { + self.uid = Some(id); + } + pub fn gid(&mut self, id: gid_t) { + self.gid = Some(id); + } + + pub fn saw_nul(&self) -> bool { + self.saw_nul + } + pub fn get_envp(&self) -> &Option> { + &self.envp + } + pub fn get_argv(&self) -> &Vec<*const c_char> { + &self.argv + } + + #[cfg(not(target_os="fuchsia"))] + pub fn get_cwd(&self) -> &Option { + &self.cwd + } + #[cfg(not(target_os="fuchsia"))] + pub fn get_uid(&self) -> Option { + self.uid + } + #[cfg(not(target_os="fuchsia"))] + pub fn get_gid(&self) -> Option { + self.gid + } + + pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { + &mut self.closures + } + + pub fn before_exec(&mut self, + f: Box io::Result<()> + Send + Sync>) { + self.closures.push(f); + } + + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn setup_io(&self, default: Stdio, needs_stdin: bool) + -> io::Result<(StdioPipes, ChildPipes)> { + let null = Stdio::Null; + let default_stdin = if needs_stdin {&default} else {&null}; + let stdin = self.stdin.as_ref().unwrap_or(default_stdin); + let stdout = self.stdout.as_ref().unwrap_or(&default); + let stderr = self.stderr.as_ref().unwrap_or(&default); + let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; + let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; + let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; + let ours = StdioPipes { + stdin: our_stdin, + stdout: our_stdout, + stderr: our_stderr, + }; + let theirs = ChildPipes { + stdin: their_stdin, + stdout: their_stdout, + stderr: their_stderr, + }; + Ok((ours, theirs)) + } +} + +fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { + CString::new(s.as_bytes()).unwrap_or_else(|_e| { + *saw_nul = true; + CString::new("").unwrap() + }) +} + +impl Stdio { + pub fn to_child_stdio(&self, readable: bool) + -> io::Result<(ChildStdio, Option)> { + match *self { + 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 + // descriptors may blow away a descriptor which we are hoping to + // save. For example, suppose we want the child's stderr to be the + // parent's stdout, and the child's stdout to be the parent's + // stderr. No matter which we dup first, the second will get + // overwritten prematurely. + Stdio::Fd(ref fd) => { + if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO { + Ok((ChildStdio::Owned(fd.duplicate()?), None)) + } else { + Ok((ChildStdio::Explicit(fd.raw()), None)) + } + } + + Stdio::MakePipe => { + let (reader, writer) = pipe::anon_pipe()?; + let (ours, theirs) = if readable { + (writer, reader) + } else { + (reader, writer) + }; + Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) + } + + Stdio::Null => { + let mut opts = OpenOptions::new(); + opts.read(readable); + opts.write(!readable); + let path = unsafe { + CStr::from_ptr("/dev/null\0".as_ptr() as *const _) + }; + let fd = File::open_c(&path, &opts)?; + Ok((ChildStdio::Owned(fd.into_fd()), None)) + } + } + } +} + +impl ChildStdio { + pub fn fd(&self) -> Option { + match *self { + ChildStdio::Inherit => None, + ChildStdio::Explicit(fd) => Some(fd), + ChildStdio::Owned(ref fd) => Some(fd.raw()), + } + } +} + +fn pair_to_key(key: &OsStr, value: &OsStr, saw_nul: &mut bool) -> CString { + let (key, value) = (key.as_bytes(), value.as_bytes()); + let mut v = Vec::with_capacity(key.len() + value.len() + 1); + v.extend(key); + v.push(b'='); + v.extend(value); + CString::new(v).unwrap_or_else(|_e| { + *saw_nul = true; + CString::new("foo=bar").unwrap() + }) +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.program)?; + for arg in &self.args { + write!(f, " {:?}", arg)?; + } + Ok(()) + } +} + +/// Unix exit statuses +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(c_int); + +impl ExitStatus { + pub fn new(status: c_int) -> ExitStatus { + ExitStatus(status) + } + + fn exited(&self) -> bool { + unsafe { libc::WIFEXITED(self.0) } + } + + pub fn success(&self) -> bool { + self.code() == Some(0) + } + + pub fn code(&self) -> Option { + if self.exited() { + Some(unsafe { libc::WEXITSTATUS(self.0) }) + } else { + None + } + } + + pub fn signal(&self) -> Option { + if !self.exited() { + Some(unsafe { libc::WTERMSIG(self.0) }) + } else { + None + } + } +} + +impl From for ExitStatus { + fn from(a: c_int) -> ExitStatus { + ExitStatus(a) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(code) = self.code() { + write!(f, "exit code: {}", code) + } else { + let signal = self.signal().unwrap(); + write!(f, "signal: {}", signal) + } + } +} + +#[cfg(all(test, not(target_os = "emscripten")))] +mod tests { + use super::*; + + use ffi::OsStr; + use mem; + use ptr; + use libc; + use sys::cvt; + + macro_rules! t { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), + } + } + } + + #[cfg(not(target_os = "android"))] + extern { + #[cfg_attr(target_os = "netbsd", link_name = "__sigaddset14")] + fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int; + } + + #[cfg(target_os = "android")] + unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int { + use slice; + + let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::()); + let bit = (signum - 1) as usize; + raw[bit / 8] |= 1 << (bit % 8); + return 0; + } + + // See #14232 for more information, but it appears that signal delivery to a + // newly spawned process may just be raced in the OSX, so to prevent this + // test from being flaky we ignore it on OSX. + #[test] + #[cfg_attr(target_os = "macos", ignore)] + #[cfg_attr(target_os = "nacl", ignore)] // no signals on NaCl. + fn test_process_mask() { + unsafe { + // Test to make sure that a signal mask does not get inherited. + let mut cmd = Command::new(OsStr::new("cat")); + + let mut set: libc::sigset_t = mem::uninitialized(); + let mut old_set: libc::sigset_t = mem::uninitialized(); + t!(cvt(libc::sigemptyset(&mut set))); + t!(cvt(sigaddset(&mut set, libc::SIGINT))); + t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, &mut old_set))); + + cmd.stdin(Stdio::MakePipe); + cmd.stdout(Stdio::MakePipe); + + let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true)); + let stdin_write = pipes.stdin.take().unwrap(); + let stdout_read = pipes.stdout.take().unwrap(); + + t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &old_set, + ptr::null_mut()))); + + t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT))); + // We need to wait until SIGINT is definitely delivered. The + // easiest way is to write something to cat, and try to read it + // back: if SIGINT is unmasked, it'll get delivered when cat is + // next scheduled. + let _ = stdin_write.write(b"Hello"); + drop(stdin_write); + + // Either EOF or failure (EPIPE) is okay. + let mut buf = [0; 5]; + if let Ok(ret) = stdout_read.read(&mut buf) { + assert!(ret == 0); + } + + t!(cat.wait()); + } + } +} diff --git a/src/libstd/sys/unix/process/process_fuchsia.rs b/src/libstd/sys/unix/process/process_fuchsia.rs new file mode 100644 index 00000000000..30e5555df69 --- /dev/null +++ b/src/libstd/sys/unix/process/process_fuchsia.rs @@ -0,0 +1,190 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use libc; +use mem; +use ptr; + +use sys::mx_cvt; +use sys::magenta::{launchpad_t, mx_handle_t}; +use sys::process::process_common::*; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + if self.saw_nul() { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "nul byte found in provided data")); + } + + let (ours, theirs) = self.setup_io(default, needs_stdin)?; + + let (launchpad, process_handle) = unsafe { self.do_exec(theirs)? }; + + Ok((Process { launchpad: launchpad, handle: process_handle, status: None }, ours)) + } + + pub fn exec(&mut self, default: Stdio) -> io::Error { + if self.saw_nul() { + return io::Error::new(io::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, + } + } + + unsafe fn do_exec(&mut self, stdio: ChildPipes) + -> io::Result<(*mut launchpad_t, mx_handle_t)> { + use sys::magenta::*; + + let job_handle = mxio_get_startup_handle(mx_hnd_info(MX_HND_TYPE_JOB, 0)); + let envp = match *self.get_envp() { + Some(ref envp) => envp.as_ptr(), + None => ptr::null(), + }; + + // To make sure launchpad_destroy gets called on the launchpad if this function fails + struct LaunchpadDestructor(*mut launchpad_t); + impl Drop for LaunchpadDestructor { + fn drop(&mut self) { unsafe { launchpad_destroy(self.0); } } + } + + let mut launchpad: *mut launchpad_t = ptr::null_mut(); + let launchpad_destructor = LaunchpadDestructor(launchpad); + + // Duplicate the job handle + let mut job_copy: mx_handle_t = MX_HANDLE_INVALID; + mx_cvt(mx_handle_duplicate(job_handle, MX_RIGHT_SAME_RIGHTS, + &mut job_copy as *mut mx_handle_t))?; + // Create a launchpad + mx_cvt(launchpad_create(job_copy, self.get_argv()[0], + &mut launchpad as *mut *mut launchpad_t))?; + // Set the process argv + mx_cvt(launchpad_arguments(launchpad, self.get_argv().len() as i32 - 1, + self.get_argv().as_ptr()))?; + // Setup the environment vars + mx_cvt(launchpad_environ(launchpad, envp))?; + mx_cvt(launchpad_add_vdso_vmo(launchpad))?; + mx_cvt(launchpad_clone_mxio_root(launchpad))?; + // Load the executable + mx_cvt(launchpad_elf_load(launchpad, launchpad_vmo_from_file(self.get_argv()[0])))?; + mx_cvt(launchpad_load_vdso(launchpad, MX_HANDLE_INVALID))?; + mx_cvt(launchpad_clone_mxio_cwd(launchpad))?; + + // 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); + } + + // We don't want FileDesc::drop to be called on any stdio. It would close their fds. The + // fds will be closed once the child process finishes. + mem::forget(stdio); + + for callback in self.get_closures().iter_mut() { + callback()?; + } + + let process_handle = mx_cvt(launchpad_start(launchpad))?; + + // Successfully started the launchpad, so launchpad_destroy shouldn't get called + mem::forget(launchpad_destructor); + + Ok((launchpad, process_handle)) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +pub struct Process { + launchpad: *mut launchpad_t, + handle: mx_handle_t, + status: Option, +} + +impl Process { + pub fn id(&self) -> u32 { + self.handle as u32 + } + + 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(io::Error::new(io::ErrorKind::InvalidInput, + "invalid argument: can't kill an exited process")) + } else { + unsafe { + mx_cvt(mx_handle_close(self.handle))?; + launchpad_destroy(self.launchpad); + } + Ok(()) + } + } + + pub fn wait(&mut self) -> io::Result { + 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::(), &mut actual, + &mut avail))?; + } + if actual != 1 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "Failed to get exit status of process")); + } + self.status = Some(ExitStatus::new(proc_info.rec.return_code)); + unsafe { + mx_cvt(mx_handle_close(self.handle))?; + launchpad_destroy(self.launchpad); + } + Ok(ExitStatus::new(proc_info.rec.return_code)) + } +} diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs new file mode 100644 index 00000000000..aa426722025 --- /dev/null +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -0,0 +1,250 @@ +// Copyright 2014-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io::{self, Error, ErrorKind}; +use libc::{self, c_int, gid_t, pid_t, uid_t}; +use mem; +use ptr; + +use sys::cvt; +use sys::process::process_common::*; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + 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() { + return Err(io::Error::new(ErrorKind::InvalidInput, + "nul byte found in provided data")); + } + + let (ours, theirs) = self.setup_io(default, needs_stdin)?; + let (input, output) = sys::pipe::anon_pipe()?; + + let pid = unsafe { + match cvt(libc::fork())? { + 0 => { + drop(input); + let err = self.do_exec(theirs); + let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; + let bytes = [ + (errno >> 24) as u8, + (errno >> 16) as u8, + (errno >> 8) as u8, + (errno >> 0) as u8, + CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], + CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] + ]; + // pipe I/O up to PIPE_BUF bytes should be atomic, and then + // we want to be sure we *don't* run at_exit destructors as + // we're being torn down regardless + assert!(output.write(&bytes).is_ok()); + libc::_exit(1) + } + n => n, + } + }; + + let mut p = Process { pid: pid, status: None }; + drop(output); + let mut bytes = [0; 8]; + + // loop to handle EINTR + loop { + match input.read(&mut bytes) { + Ok(0) => return Ok((p, ours)), + Ok(8) => { + assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), + "Validation on the CLOEXEC pipe failed: {:?}", bytes); + let errno = combine(&bytes[0.. 4]); + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + return Err(Error::from_raw_os_error(errno)) + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => { + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + panic!("the CLOEXEC pipe failed: {:?}", e) + }, + Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + panic!("short read on the CLOEXEC pipe") + } + } + } + + fn combine(arr: &[u8]) -> i32 { + let a = arr[0] as u32; + let b = arr[1] as u32; + let c = arr[2] as u32; + let d = arr[3] as u32; + + ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 + } + } + + 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((_, theirs)) => unsafe { self.do_exec(theirs) }, + 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 + // scenario: + // + // 1. Thread A of process 1 grabs the malloc() mutex + // 2. Thread B of process 1 forks(), creating thread C + // 3. Thread C of process 2 then attempts to malloc() + // 4. The memory of process 2 is the same as the memory of + // process 1, so the mutex is locked. + // + // This situation looks a lot like deadlock, right? It turns out + // that this is what pthread_atfork() takes care of, which is + // presumably implemented across platforms. The first thing that + // threads to *before* forking is to do things like grab the malloc + // mutex, and then after the fork they unlock it. + // + // Despite this information, libnative's spawn has been witnessed to + // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but + // all collected backtraces point at malloc/free traffic in the + // child spawned process. + // + // For this reason, the block of code below should contain 0 + // invocations of either malloc of free (or their related friends). + // + // As an example of not having malloc/free traffic, we don't close + // this file descriptor by dropping the FileDesc (which contains an + // 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) + 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, + Err(e) => return e, + }) + } + + if let Some(fd) = stdio.stdin.fd() { + t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); + } + if let Some(fd) = stdio.stdout.fd() { + t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); + } + if let Some(fd) = stdio.stderr.fd() { + t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); + } + + if let Some(u) = self.get_gid() { + t!(cvt(libc::setgid(u as gid_t))); + } + if let Some(u) = self.get_uid() { + // When dropping privileges from root, the `setgroups` call + // will remove any extraneous groups. If we don't call this, + // then even though our uid has dropped, we may still have + // groups that enable us to do super-user things. This will + // fail if we aren't root, so don't bother checking the + // return value, this is just done as an optimistic + // privilege dropping function. + let _ = libc::setgroups(0, ptr::null()); + + t!(cvt(libc::setuid(u as uid_t))); + } + if let Some(ref cwd) = *self.get_cwd() { + t!(cvt(libc::chdir(cwd.as_ptr()))); + } + if let Some(ref envp) = *self.get_envp() { + *sys::os::environ() = envp.as_ptr(); + } + + // NaCl has no signal support. + if cfg!(not(any(target_os = "nacl", target_os = "emscripten"))) { + // Reset signal handling so the child process starts in a + // standardized state. libstd ignores SIGPIPE, and signal-handling + // libraries often set a mask. Child processes inherit ignored + // signals and the signal mask from their parent, but most + // UNIX programs do not reset these things on their own, so we + // need to clean things up now to avoid confusing the program + // we're about to run. + let mut set: libc::sigset_t = mem::uninitialized(); + t!(cvt(libc::sigemptyset(&mut set))); + t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, + ptr::null_mut()))); + let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL); + if ret == libc::SIG_ERR { + return io::Error::last_os_error() + } + } + + for callback in self.get_closures().iter_mut() { + t!(callback()); + } + + libc::execvp(self.get_argv()[0], self.get_argv().as_ptr()); + io::Error::last_os_error() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// The unique id of the process (this should never be negative). +pub struct Process { + pid: pid_t, + status: Option, +} + +impl Process { + pub fn id(&self) -> u32 { + self.pid as u32 + } + + 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 + // 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 { + cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ()) + } + } + pub fn wait(&mut self) -> io::Result { + use sys::cvt_r; + if let Some(status) = self.status { + return Ok(status) + } + let mut status = 0 as c_int; + cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; + self.status = Some(ExitStatus::new(status)); + Ok(ExitStatus::new(status)) + } +} -- cgit 1.4.1-3-g733a5 From 8d9d07a1ca25d003b57b08f5930ae3e9a27cd37c Mon Sep 17 00:00:00 2001 From: Theodore DeRego Date: Wed, 30 Nov 2016 14:20:44 -0800 Subject: Removed Option member from fuchsia Process struct. Destroy launchpads and close handles in Drop impls rather than manually --- src/libstd/sys/unix/magenta.rs | 26 +++++++++++++ src/libstd/sys/unix/process/process_fuchsia.rs | 51 ++++++++++---------------- 2 files changed, 45 insertions(+), 32 deletions(-) (limited to 'src/libstd/sys') diff --git a/src/libstd/sys/unix/magenta.rs b/src/libstd/sys/unix/magenta.rs index 155259d2645..20e077ccaca 100644 --- a/src/libstd/sys/unix/magenta.rs +++ b/src/libstd/sys/unix/magenta.rs @@ -42,6 +42,30 @@ pub const MX_INFO_PROCESS : mx_object_info_topic_t = 3; pub const MX_HND_TYPE_JOB: u32 = 6; +// Safe wrapper around mx_handle_t +pub struct Handle { + raw: mx_handle_t, +} + +impl Handle { + pub fn new(raw: mx_handle_t) -> Handle { + Handle { + raw: raw, + } + } + + pub fn raw(&self) -> mx_handle_t { + self.raw + } +} + +impl Drop for Handle { + fn drop(&mut self) { + use sys::mx_cvt; + unsafe { mx_cvt(mx_handle_close(self.raw)).expect("Failed to close mx_handle_t"); } + } +} + // Common MX_INFO header #[derive(Default)] #[repr(C)] @@ -68,6 +92,8 @@ pub struct mx_info_process_t { } extern { + pub fn mx_task_kill(handle: mx_handle_t) -> mx_status_t; + 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, diff --git a/src/libstd/sys/unix/process/process_fuchsia.rs b/src/libstd/sys/unix/process/process_fuchsia.rs index 30e5555df69..77340664b6a 100644 --- a/src/libstd/sys/unix/process/process_fuchsia.rs +++ b/src/libstd/sys/unix/process/process_fuchsia.rs @@ -14,7 +14,7 @@ use mem; use ptr; use sys::mx_cvt; -use sys::magenta::{launchpad_t, mx_handle_t}; +use sys::magenta::{Handle, launchpad_t, mx_handle_t}; use sys::process::process_common::*; //////////////////////////////////////////////////////////////////////////////// @@ -33,7 +33,7 @@ impl Command { let (launchpad, process_handle) = unsafe { self.do_exec(theirs)? }; - Ok((Process { launchpad: launchpad, handle: process_handle, status: None }, ours)) + Ok((Process { launchpad: launchpad, handle: Handle::new(process_handle) }, ours)) } pub fn exec(&mut self, default: Stdio) -> io::Error { @@ -116,7 +116,7 @@ impl Command { let process_handle = mx_cvt(launchpad_start(launchpad))?; - // Successfully started the launchpad, so launchpad_destroy shouldn't get called + // Successfully started the launchpad mem::forget(launchpad_destructor); Ok((launchpad, process_handle)) @@ -129,62 +129,49 @@ impl Command { pub struct Process { launchpad: *mut launchpad_t, - handle: mx_handle_t, - status: Option, + handle: Handle, } impl Process { pub fn id(&self) -> u32 { - self.handle as u32 + self.handle.raw() as u32 } 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(io::Error::new(io::ErrorKind::InvalidInput, - "invalid argument: can't kill an exited process")) - } else { - unsafe { - mx_cvt(mx_handle_close(self.handle))?; - launchpad_destroy(self.launchpad); - } - Ok(()) - } + unsafe { mx_cvt(mx_task_kill(self.handle.raw()))?; } + + Ok(()) } pub fn wait(&mut self) -> io::Result { 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_cvt(mx_handle_wait_one(self.handle.raw(), MX_TASK_TERMINATED, MX_TIME_INFINITE, ptr::null_mut()))?; - mx_cvt(mx_object_get_info(self.handle, MX_INFO_PROCESS, + mx_cvt(mx_object_get_info(self.handle.raw(), MX_INFO_PROCESS, &mut proc_info as *mut _ as *mut libc::c_void, mem::size_of::(), &mut actual, &mut avail))?; } if actual != 1 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, - "Failed to get exit status of process")); - } - self.status = Some(ExitStatus::new(proc_info.rec.return_code)); - unsafe { - mx_cvt(mx_handle_close(self.handle))?; - launchpad_destroy(self.launchpad); + return Err(io::Error::new(io::ErrorKind::InvalidData, + "Failed to get exit status of process")); } Ok(ExitStatus::new(proc_info.rec.return_code)) } } + +impl Drop for Process { + fn drop(&mut self) { + use sys::magenta::launchpad_destroy; + unsafe { launchpad_destroy(self.launchpad); } + } +} -- cgit 1.4.1-3-g733a5 From 8b1c4cbbaf0252ed68f62b0613a8da9725141262 Mon Sep 17 00:00:00 2001 From: Ted Mielczarek Date: Wed, 30 Nov 2016 19:44:07 -0500 Subject: Add std::os::windows::process::CommandExt, with set_creation_flags and add_creation_flags methods. Fixes #37827 This adds a CommandExt trait for Windows along with an implementation of it for std::process::Command with methods to set the process creation flags that are passed to CreateProcess. --- src/libstd/process.rs | 57 +++++++++++++++++++++++++++++++++++ src/libstd/sys/windows/ext/process.rs | 31 ++++++++++++++++++- src/libstd/sys/windows/process.rs | 10 +++++- 3 files changed, 96 insertions(+), 2 deletions(-) (limited to 'src/libstd/sys') diff --git a/src/libstd/process.rs b/src/libstd/process.rs index c99fda9febc..912cc122e92 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -1159,4 +1159,61 @@ mod tests { Ok(_) => panic!(), } } + + /// Test that process creation flags work by debugging a process. + /// Other creation flags make it hard or impossible to detect + /// behavioral changes in the process. + #[test] + #[cfg(windows)] + fn test_creation_flags() { + use os::windows::process::CommandExt; + use sys::c::{BOOL, DWORD, INFINITE}; + #[repr(C, packed)] + struct DEBUG_EVENT { + pub event_code: DWORD, + pub process_id: DWORD, + pub thread_id: DWORD, + // This is a union in the real struct, but we don't + // need this data for the purposes of this test. + pub _junk: [u8; 164], + } + + extern "system" { + fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL; + fn ContinueDebugEvent(dwProcessId: DWORD, dwThreadId: DWORD, dwContinueStatus: DWORD) -> BOOL; + } + + const DEBUG_PROCESS: DWORD = 1; + const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5; + const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001; + + let mut child = Command::new("cmd") + .add_creation_flags(DEBUG_PROCESS) + .stdin(Stdio::piped()).spawn().unwrap(); + child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); + let mut events = 0; + let mut event = DEBUG_EVENT { + event_code: 0, + process_id: 0, + thread_id: 0, + _junk: [0; 164], + }; + loop { + if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 { + panic!("WaitForDebugEvent failed!"); + } + events += 1; + + if event.event_code == EXIT_PROCESS_DEBUG_EVENT { + break; + } + + if unsafe { ContinueDebugEvent(event.process_id, + event.thread_id, + DBG_EXCEPTION_NOT_HANDLED) } == 0 { + panic!("ContinueDebugEvent failed!"); + } + } + assert!(events > 0); + } } diff --git a/src/libstd/sys/windows/ext/process.rs b/src/libstd/sys/windows/ext/process.rs index bce32959a23..f5bf3354637 100644 --- a/src/libstd/sys/windows/ext/process.rs +++ b/src/libstd/sys/windows/ext/process.rs @@ -15,7 +15,7 @@ use os::windows::io::{FromRawHandle, RawHandle, AsRawHandle, IntoRawHandle}; use process; use sys; -use sys_common::{AsInner, FromInner, IntoInner}; +use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner}; #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawHandle for process::Stdio { @@ -97,3 +97,32 @@ impl ExitStatusExt for process::ExitStatus { process::ExitStatus::from_inner(From::from(raw)) } } + +/// Windows-specific extensions to the `std::process::Command` builder +#[unstable(feature = "windows_process_extensions", issue = "37827")] +pub trait CommandExt { + /// Sets the [process creation flags][1] to be passed to `CreateProcess`. + /// + /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`. + /// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx + #[unstable(feature = "windows_process_extensions", issue = "37827")] + fn set_creation_flags(&mut self, flags: u32) -> &mut process::Command; + /// Add `flags` to the the [process creation flags][1] to be passed to `CreateProcess`. + /// + /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`. + /// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx + #[unstable(feature = "windows_process_extensions", issue = "37827")] + fn add_creation_flags(&mut self, flags: u32) -> &mut process::Command; +} + +#[unstable(feature = "windows_process_extensions", issue = "37827")] +impl CommandExt for process::Command { + fn set_creation_flags(&mut self, flags: u32) -> &mut process::Command { + self.as_inner_mut().set_creation_flags(flags); + self + } + fn add_creation_flags(&mut self, flags: u32) -> &mut process::Command { + self.as_inner_mut().add_creation_flags(flags); + self + } +} diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index d371714ff0e..a221c67efd9 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -54,6 +54,7 @@ pub struct Command { args: Vec, env: Option>, cwd: Option, + flags: u32, detach: bool, // not currently exposed in std::process stdin: Option, stdout: Option, @@ -84,6 +85,7 @@ impl Command { args: Vec::new(), env: None, cwd: None, + flags: 0, detach: false, stdin: None, stdout: None, @@ -124,6 +126,12 @@ impl Command { pub fn stderr(&mut self, stderr: Stdio) { self.stderr = Some(stderr); } + pub fn set_creation_flags(&mut self, flags: u32) { + self.flags = flags; + } + pub fn add_creation_flags(&mut self, flags: u32) { + self.flags = self.flags | flags; + } pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) -> io::Result<(Process, StdioPipes)> { @@ -157,7 +165,7 @@ impl Command { cmd_str.push(0); // add null terminator // stolen from the libuv code. - let mut flags = c::CREATE_UNICODE_ENVIRONMENT; + let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT; if self.detach { flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP; } -- cgit 1.4.1-3-g733a5 From e6975e974841c59a6e67e8a158b306f29d35d513 Mon Sep 17 00:00:00 2001 From: Ted Mielczarek Date: Wed, 30 Nov 2016 21:31:47 -0500 Subject: just add one method named creation_flags, fix the tidy error --- src/libstd/process.rs | 5 +++-- src/libstd/sys/windows/ext/process.rs | 16 +++------------- src/libstd/sys/windows/process.rs | 5 +---- 3 files changed, 7 insertions(+), 19 deletions(-) (limited to 'src/libstd/sys') diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 912cc122e92..f41fbcc66b4 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -1180,7 +1180,8 @@ mod tests { extern "system" { fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL; - fn ContinueDebugEvent(dwProcessId: DWORD, dwThreadId: DWORD, dwContinueStatus: DWORD) -> BOOL; + fn ContinueDebugEvent(dwProcessId: DWORD, dwThreadId: DWORD, + dwContinueStatus: DWORD) -> BOOL; } const DEBUG_PROCESS: DWORD = 1; @@ -1188,7 +1189,7 @@ mod tests { const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001; let mut child = Command::new("cmd") - .add_creation_flags(DEBUG_PROCESS) + .creation_flags(DEBUG_PROCESS) .stdin(Stdio::piped()).spawn().unwrap(); child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); let mut events = 0; diff --git a/src/libstd/sys/windows/ext/process.rs b/src/libstd/sys/windows/ext/process.rs index f5bf3354637..0a3221aeae6 100644 --- a/src/libstd/sys/windows/ext/process.rs +++ b/src/libstd/sys/windows/ext/process.rs @@ -106,23 +106,13 @@ pub trait CommandExt { /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`. /// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx #[unstable(feature = "windows_process_extensions", issue = "37827")] - fn set_creation_flags(&mut self, flags: u32) -> &mut process::Command; - /// Add `flags` to the the [process creation flags][1] to be passed to `CreateProcess`. - /// - /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`. - /// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx - #[unstable(feature = "windows_process_extensions", issue = "37827")] - fn add_creation_flags(&mut self, flags: u32) -> &mut process::Command; + fn creation_flags(&mut self, flags: u32) -> &mut process::Command; } #[unstable(feature = "windows_process_extensions", issue = "37827")] impl CommandExt for process::Command { - fn set_creation_flags(&mut self, flags: u32) -> &mut process::Command { - self.as_inner_mut().set_creation_flags(flags); - self - } - fn add_creation_flags(&mut self, flags: u32) -> &mut process::Command { - self.as_inner_mut().add_creation_flags(flags); + fn creation_flags(&mut self, flags: u32) -> &mut process::Command { + self.as_inner_mut().creation_flags(flags); self } } diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index a221c67efd9..969de6b85a6 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -126,12 +126,9 @@ impl Command { pub fn stderr(&mut self, stderr: Stdio) { self.stderr = Some(stderr); } - pub fn set_creation_flags(&mut self, flags: u32) { + pub fn creation_flags(&mut self, flags: u32) { self.flags = flags; } - pub fn add_creation_flags(&mut self, flags: u32) { - self.flags = self.flags | flags; - } pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) -> io::Result<(Process, StdioPipes)> { -- cgit 1.4.1-3-g733a5 From e1b752b2a1bea9c05e89e52632f2f87ee9777062 Mon Sep 17 00:00:00 2001 From: Theodore DeRego Date: Thu, 1 Dec 2016 12:01:07 -0800 Subject: std::process fuchsia support cleanup --- src/libstd/sys/unix/magenta.rs | 178 ----------------------- src/libstd/sys/unix/mod.rs | 20 --- src/libstd/sys/unix/process/magenta.rs | 191 +++++++++++++++++++++++++ src/libstd/sys/unix/process/mod.rs | 2 + src/libstd/sys/unix/process/process_common.rs | 6 +- src/libstd/sys/unix/process/process_fuchsia.rs | 17 +-- 6 files changed, 203 insertions(+), 211 deletions(-) delete mode 100644 src/libstd/sys/unix/magenta.rs create mode 100644 src/libstd/sys/unix/process/magenta.rs (limited to 'src/libstd/sys') diff --git a/src/libstd/sys/unix/magenta.rs b/src/libstd/sys/unix/magenta.rs deleted file mode 100644 index 20e077ccaca..00000000000 --- a/src/libstd/sys/unix/magenta.rs +++ /dev/null @@ -1,178 +0,0 @@ -// 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 or the MIT license -// , 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 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; - -// Safe wrapper around mx_handle_t -pub struct Handle { - raw: mx_handle_t, -} - -impl Handle { - pub fn new(raw: mx_handle_t) -> Handle { - Handle { - raw: raw, - } - } - - pub fn raw(&self) -> mx_handle_t { - self.raw - } -} - -impl Drop for Handle { - fn drop(&mut self) { - use sys::mx_cvt; - unsafe { mx_cvt(mx_handle_close(self.raw)).expect("Failed to close mx_handle_t"); } - } -} - -// 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, -} - -extern { - pub fn mx_task_kill(handle: mx_handle_t) -> mx_status_t; - - 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) -} - -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, -} - -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 8fe55af51d5..fd7dc17cccd 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -13,11 +13,6 @@ 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; @@ -46,8 +41,6 @@ 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; @@ -171,19 +164,6 @@ pub fn cvt_r(mut f: F) -> io::Result } } -#[cfg(target_os = "fuchsia")] -pub fn mx_cvt(t: T) -> io::Result where T: TryInto+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/process/magenta.rs b/src/libstd/sys/unix/process/magenta.rs new file mode 100644 index 00000000000..319fbce35cd --- /dev/null +++ b/src/libstd/sys/unix/process/magenta.rs @@ -0,0 +1,191 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_camel_case_types)] + +use convert::TryInto; +use io; +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 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; + +pub fn mx_cvt(t: T) -> io::Result where T: TryInto+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()) + } +} + +// Safe wrapper around mx_handle_t +pub struct Handle { + raw: mx_handle_t, +} + +impl Handle { + pub fn new(raw: mx_handle_t) -> Handle { + Handle { + raw: raw, + } + } + + pub fn raw(&self) -> mx_handle_t { + self.raw + } +} + +impl Drop for Handle { + fn drop(&mut self) { + unsafe { mx_cvt(mx_handle_close(self.raw)).expect("Failed to close mx_handle_t"); } + } +} + +// 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, +} + +extern { + pub fn mx_task_kill(handle: mx_handle_t) -> mx_status_t; + + 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) +} + +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, +} + +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/process/mod.rs b/src/libstd/sys/unix/process/mod.rs index 82c3971ee40..b50384d8eee 100644 --- a/src/libstd/sys/unix/process/mod.rs +++ b/src/libstd/sys/unix/process/mod.rs @@ -18,3 +18,5 @@ mod process_inner; #[cfg(target_os = "fuchsia")] #[path = "process_fuchsia.rs"] mod process_inner; +#[cfg(target_os = "fuchsia")] +mod magenta; diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs index 24b8b61edea..3497b266340 100644 --- a/src/libstd/sys/unix/process/process_common.rs +++ b/src/libstd/sys/unix/process/process_common.rs @@ -203,15 +203,15 @@ impl Command { &self.argv } - #[cfg(not(target_os="fuchsia"))] + #[allow(dead_code)] pub fn get_cwd(&self) -> &Option { &self.cwd } - #[cfg(not(target_os="fuchsia"))] + #[allow(dead_code)] pub fn get_uid(&self) -> Option { self.uid } - #[cfg(not(target_os="fuchsia"))] + #[allow(dead_code)] pub fn get_gid(&self) -> Option { self.gid } diff --git a/src/libstd/sys/unix/process/process_fuchsia.rs b/src/libstd/sys/unix/process/process_fuchsia.rs index 77340664b6a..f0a42b12799 100644 --- a/src/libstd/sys/unix/process/process_fuchsia.rs +++ b/src/libstd/sys/unix/process/process_fuchsia.rs @@ -13,8 +13,7 @@ use libc; use mem; use ptr; -use sys::mx_cvt; -use sys::magenta::{Handle, launchpad_t, mx_handle_t}; +use sys::process::magenta::{Handle, launchpad_t, mx_handle_t}; use sys::process::process_common::*; //////////////////////////////////////////////////////////////////////////////// @@ -53,7 +52,7 @@ impl Command { unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Result<(*mut launchpad_t, mx_handle_t)> { - use sys::magenta::*; + use sys::process::magenta::*; let job_handle = mxio_get_startup_handle(mx_hnd_info(MX_HND_TYPE_JOB, 0)); let envp = match *self.get_envp() { @@ -72,11 +71,9 @@ impl Command { // Duplicate the job handle let mut job_copy: mx_handle_t = MX_HANDLE_INVALID; - mx_cvt(mx_handle_duplicate(job_handle, MX_RIGHT_SAME_RIGHTS, - &mut job_copy as *mut mx_handle_t))?; + mx_cvt(mx_handle_duplicate(job_handle, MX_RIGHT_SAME_RIGHTS, &mut job_copy))?; // Create a launchpad - mx_cvt(launchpad_create(job_copy, self.get_argv()[0], - &mut launchpad as *mut *mut launchpad_t))?; + mx_cvt(launchpad_create(job_copy, self.get_argv()[0], &mut launchpad))?; // Set the process argv mx_cvt(launchpad_arguments(launchpad, self.get_argv().len() as i32 - 1, self.get_argv().as_ptr()))?; @@ -138,7 +135,7 @@ impl Process { } pub fn kill(&mut self) -> io::Result<()> { - use sys::magenta::*; + use sys::process::magenta::*; unsafe { mx_cvt(mx_task_kill(self.handle.raw()))?; } @@ -147,7 +144,7 @@ impl Process { pub fn wait(&mut self) -> io::Result { use default::Default; - use sys::magenta::*; + use sys::process::magenta::*; let mut proc_info: mx_info_process_t = Default::default(); let mut actual: mx_size_t = 0; @@ -171,7 +168,7 @@ impl Process { impl Drop for Process { fn drop(&mut self) { - use sys::magenta::launchpad_destroy; + use sys::process::magenta::launchpad_destroy; unsafe { launchpad_destroy(self.launchpad); } } } -- cgit 1.4.1-3-g733a5 From a1882ca769ed82fa16a06f113f386fa48366c331 Mon Sep 17 00:00:00 2001 From: Mathieu Poumeyrol Date: Sat, 3 Dec 2016 19:47:27 +0100 Subject: fix objc ABI in std::env::args --- src/libstd/sys/unix/args.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'src/libstd/sys') diff --git a/src/libstd/sys/unix/args.rs b/src/libstd/sys/unix/args.rs index c04fd863674..0f447ff4ec4 100644 --- a/src/libstd/sys/unix/args.rs +++ b/src/libstd/sys/unix/args.rs @@ -172,10 +172,23 @@ mod imp { extern { fn sel_registerName(name: *const libc::c_uchar) -> Sel; - fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; } + #[cfg(target_arch="aarch64")] + extern { + fn objc_msgSend(obj: NsId, sel: Sel) -> NsId; + #[link_name="objc_msgSend"] + fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId; + } + + #[cfg(not(target_arch="aarch64"))] + extern { + fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; + #[link_name="objc_msgSend"] + fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId; + } + #[link(name = "Foundation", kind = "framework")] #[link(name = "objc")] #[cfg(not(cargobuild))] @@ -199,7 +212,7 @@ mod imp { let cnt: usize = mem::transmute(objc_msgSend(args, count_sel)); for i in 0..cnt { - let tmp = objc_msgSend(args, object_at_sel, i); + let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong); let utf_c_str: *const libc::c_char = mem::transmute(objc_msgSend(tmp, utf8_sel)); let bytes = CStr::from_ptr(utf_c_str).to_bytes(); -- cgit 1.4.1-3-g733a5 From b5ea8c00a3927acbee9452d2623b28b076bfc6c4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 4 Dec 2016 00:48:11 -0800 Subject: Fix small typo --- src/libstd/sys/unix/ext/io.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libstd/sys') diff --git a/src/libstd/sys/unix/ext/io.rs b/src/libstd/sys/unix/ext/io.rs index 4163ede46af..296235e173d 100644 --- a/src/libstd/sys/unix/ext/io.rs +++ b/src/libstd/sys/unix/ext/io.rs @@ -43,7 +43,7 @@ pub trait AsRawFd { /// descriptor. #[stable(feature = "from_raw_os", since = "1.1.0")] pub trait FromRawFd { - /// Constructs a new instances of `Self` from the given raw file + /// Constructs a new instance of `Self` from the given raw file /// descriptor. /// /// This function **consumes ownership** of the specified file -- cgit 1.4.1-3-g733a5 From dfd3b908f5de623ae3fee4b2451b15f6cd6f0e87 Mon Sep 17 00:00:00 2001 From: Michael Neumann Date: Tue, 6 Dec 2016 19:31:48 +0100 Subject: Fix current_exe() on DragonFly (again) This is a follow up on [this pull request][1]. Since DragonFly 4.6.1 ([this commit][2]), the "kern.proc.pathname" sysctl works correctly, i.e. it does not return paths including a ":" (see [here][3]). Use it and don't try to fix old versions of DragonFly! There are not many DragonFly installations out there that we can't control and no one is compiling Rust from source. If someone wants to run Rust on a pre-4.6.1 DragonFly system, the ports system should provide a patch. [1]: https://github.com/rust-lang/rust/pull/35494 [2]: https://gitweb.dragonflybsd.org/dragonfly.git/commit/726f7ca07e193db73635e9c4e24e40c96087d6d9 [3]: https://gist.github.com/mneumann/a2f6b6a0a03935b561d6185872a4b222 --- src/libstd/sys/unix/os.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'src/libstd/sys') diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index e591f25cac1..6992a17832e 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -78,7 +78,7 @@ pub fn errno() -> i32 { static errno: c_int; } - errno as i32 + unsafe { errno as i32 } } /// Gets a detailed string description for the given error number. @@ -193,7 +193,7 @@ impl StdError for JoinPathsError { fn description(&self) -> &str { "failed to join paths" } } -#[cfg(target_os = "freebsd")] +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] pub fn current_exe() -> io::Result { unsafe { let mut mib = [libc::CTL_KERN as c_int, @@ -218,11 +218,6 @@ pub fn current_exe() -> io::Result { } } -#[cfg(target_os = "dragonfly")] -pub fn current_exe() -> io::Result { - ::fs::read_link("/proc/curproc/file") -} - #[cfg(target_os = "netbsd")] pub fn current_exe() -> io::Result { ::fs::read_link("/proc/curproc/exe") -- cgit 1.4.1-3-g733a5