Fuchsia support for std::process via liblaunchpad.
Now we can launch processes on Fuchsia via the Rust standard library! ... Mostly.
Right now, ~5% of the time, reading the stdout/stderr off the pipes will fail. Some Magenta kernel people think it's probably a bug in Magenta's pipes. I wrote a unit test that demonstrates the issue in C, which I was told will expedite a fix. https://fuchsia-review.googlesource.com/#/c/15628/
Hopefully this can get merged once the issue is fixed :)
@raphlinus
println!("cargo:rustc-link-lib=shell32");
} else if target.contains("fuchsia") {
println!("cargo:rustc-link-lib=magenta");
+ println!("cargo:rustc-link-lib=mxio");
+ println!("cargo:rustc-link-lib=launchpad"); // for std::process
}
}
#[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 {
#[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 {
v1: &mut Vec<u8>,
p2: AnonPipe,
v2: &mut Vec<u8>) -> io::Result<()> {
+
// Set both pipes into nonblocking mode as we're gonna be reading from both
// in the `select` loop below, and we wouldn't want one to block the other!
let p1 = p1.into_fd();
+++ /dev/null
-// 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-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, pid_t, 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};
-
-////////////////////////////////////////////////////////////////////////////////
-// 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<CString>,
- env: Option<HashMap<OsString, (usize, CString)>>,
- argv: Vec<*const c_char>,
- envp: Option<Vec<*const c_char>>,
-
- cwd: Option<CString>,
- uid: Option<uid_t>,
- gid: Option<gid_t>,
- saw_nul: bool,
- closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>,
- stdin: Option<Stdio>,
- stdout: Option<Stdio>,
- stderr: Option<Stdio>,
-}
-
-// passed back to std::process with the pipes connected to the child, if any
-// were requested
-pub struct StdioPipes {
- pub stdin: Option<AnonPipe>,
- pub stdout: Option<AnonPipe>,
- pub stderr: Option<AnonPipe>,
-}
-
-// 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<OsString, (usize, CString)>,
- &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<FnMut() -> 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 spawn(&mut self, default: Stdio, needs_stdin: bool)
- -> io::Result<(Process, StdioPipes)> {
- 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 {
- 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()
- }
-
-
- 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("<string-with-nul>").unwrap()
- })
-}
-
-impl Stdio {
- fn to_child_stdio(&self, readable: bool)
- -> io::Result<(ChildStdio, Option<AnonPipe>)> {
- 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<c_int> {
- 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<i32> {
- if self.exited() {
- Some(unsafe { libc::WEXITSTATUS(self.0) })
- } else {
- None
- }
- }
-
- pub fn signal(&self) -> Option<i32> {
- if !self.exited() {
- Some(unsafe { libc::WTERMSIG(self.0) })
- } else {
- None
- }
- }
-}
-
-impl From<c_int> 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).
-pub struct Process {
- pid: pid_t,
- status: Option<ExitStatus>,
-}
-
-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<ExitStatus> {
- 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(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::<libc::sigset_t>());
- 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());
- }
- }
-}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(non_camel_case_types)]
+
+use 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: T) -> io::Result<T> where T: TryInto<mx_status_t>+Copy {
+ if let Ok(status) = TryInto::try_into(t) {
+ if status < 0 {
+ Err(io::Error::from_raw_os_error(status))
+ } else {
+ Ok(t)
+ }
+ } else {
+ Err(io::Error::last_os_error())
+ }
+}
+
+// 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;
+}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+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;
+#[cfg(target_os = "fuchsia")]
+mod magenta;
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+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<CString>,
+ env: Option<HashMap<OsString, (usize, CString)>>,
+ argv: Vec<*const c_char>,
+ envp: Option<Vec<*const c_char>>,
+
+ cwd: Option<CString>,
+ uid: Option<uid_t>,
+ gid: Option<gid_t>,
+ saw_nul: bool,
+ closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>,
+ stdin: Option<Stdio>,
+ stdout: Option<Stdio>,
+ stderr: Option<Stdio>,
+}
+
+// passed back to std::process with the pipes connected to the child, if any
+// were requested
+pub struct StdioPipes {
+ pub stdin: Option<AnonPipe>,
+ pub stdout: Option<AnonPipe>,
+ pub stderr: Option<AnonPipe>,
+}
+
+// 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<OsString, (usize, CString)>,
+ &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<Vec<*const c_char>> {
+ &self.envp
+ }
+ pub fn get_argv(&self) -> &Vec<*const c_char> {
+ &self.argv
+ }
+
+ #[allow(dead_code)]
+ pub fn get_cwd(&self) -> &Option<CString> {
+ &self.cwd
+ }
+ #[allow(dead_code)]
+ pub fn get_uid(&self) -> Option<uid_t> {
+ self.uid
+ }
+ #[allow(dead_code)]
+ pub fn get_gid(&self) -> Option<gid_t> {
+ self.gid
+ }
+
+ pub fn get_closures(&mut self) -> &mut Vec<Box<FnMut() -> io::Result<()> + Send + Sync>> {
+ &mut self.closures
+ }
+
+ pub fn before_exec(&mut self,
+ f: Box<FnMut() -> 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("<string-with-nul>").unwrap()
+ })
+}
+
+impl Stdio {
+ pub fn to_child_stdio(&self, readable: bool)
+ -> io::Result<(ChildStdio, Option<AnonPipe>)> {
+ 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<c_int> {
+ 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<i32> {
+ if self.exited() {
+ Some(unsafe { libc::WEXITSTATUS(self.0) })
+ } else {
+ None
+ }
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ if !self.exited() {
+ Some(unsafe { libc::WTERMSIG(self.0) })
+ } else {
+ None
+ }
+ }
+}
+
+impl From<c_int> 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::<libc::sigset_t>());
+ 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());
+ }
+ }
+}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use io;
+use libc;
+use mem;
+use ptr;
+
+use sys::process::magenta::{Handle, 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: Handle::new(process_handle) }, 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::process::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))?;
+ // Create a launchpad
+ 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()))?;
+ // 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
+ mem::forget(launchpad_destructor);
+
+ Ok((launchpad, process_handle))
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Process {
+ launchpad: *mut launchpad_t,
+ handle: Handle,
+}
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ self.handle.raw() as u32
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ use sys::process::magenta::*;
+
+ unsafe { mx_cvt(mx_task_kill(self.handle.raw()))?; }
+
+ Ok(())
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ use default::Default;
+ use sys::process::magenta::*;
+
+ 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.raw(), MX_TASK_TERMINATED,
+ MX_TIME_INFINITE, ptr::null_mut()))?;
+ 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::<mx_info_process_t>(), &mut actual,
+ &mut avail))?;
+ }
+ if actual != 1 {
+ 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::process::magenta::launchpad_destroy;
+ unsafe { launchpad_destroy(self.launchpad); }
+ }
+}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+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<ExitStatus>,
+}
+
+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<ExitStatus> {
+ 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))
+ }
+}