Auto merge of #31409 - alexcrichton:command-exec, r=aturon

These commits are an implementation of https://github.com/rust-lang/rfcs/pull/1359 which is tracked via https://github.com/rust-lang/rust/issues/31398. The `before_exec` implementation fit easily with the current process spawning framework we have, but unfortunately the `exec` implementation required a bit of a larger refactoring. The stdio handles were all largely managed as implementation details of `std::process` and the `exec` function lived in `std::sys`, so the two didn't have access to one another.

I took this as a sign that a deeper refactoring was necessary, and I personally feel that the end result is cleaner for both Windows and Unix. The commits should be separated nicely for reviewing (or all at once if you're feeling ambitious), but the changes made here were:

* The process spawning on Unix was refactored in to a pre-exec and post-exec function. The post-exec function isn't allowed to do any allocations of any form, and management of transmitting errors back to the parent is managed by the pre-exec function (as it's the one that actually forks).
* Some management of the exit status was pushed into platform-specific modules. On Unix we must cache the return value of `wait` as the pid is consumed after we wait on it, but on Windows we can just keep querying the system because the handle stays valid.
* The `Stdio::None` variant was renamed to `Stdio::Null` to better reflect what it's doing.
* The global lock on `CreateProcess` is now correctly positioned to avoid unintended inheritance of pipe handles that other threads are sending to their child processes. After a more careful reading of the article referenced the race is not in `CreateProcess` itself, but rather the property that handles are unintentionally shared.
* All stdio management now happens in platform-specific modules. This provides a cleaner implementation/interpretation for `FromFraw{Fd,Handle}` for each platform as well as a cleaner transition from a configuration to what-to-do once we actually need to do the spawn.

With these refactorings in place, implementing `before_exec` and `exec` ended up both being pretty trivial! (each in their own commit)
This commit is contained in:
bors 2016-02-10 22:51:43 +00:00
commit 3f4227af13
9 changed files with 861 additions and 597 deletions

View File

@ -11,17 +11,16 @@
//! Working with processes.
#![stable(feature = "process", since = "1.0.0")]
#![allow(non_upper_case_globals)]
use prelude::v1::*;
use io::prelude::*;
use ffi::OsStr;
use fmt;
use io::{self, Error, ErrorKind};
use path;
use io;
use path::Path;
use str;
use sys::pipe::{self, AnonPipe};
use sys::pipe::AnonPipe;
use sys::process as imp;
use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
use thread::{self, JoinHandle};
@ -61,9 +60,6 @@ use thread::{self, JoinHandle};
pub struct Child {
handle: imp::Process,
/// None until wait() or wait_with_output() is called.
status: Option<imp::ExitStatus>,
/// The handle for writing to the child's stdin, if it has been captured
#[stable(feature = "process", since = "1.0.0")]
pub stdin: Option<ChildStdin>,
@ -81,6 +77,17 @@ impl AsInner<imp::Process> for Child {
fn as_inner(&self) -> &imp::Process { &self.handle }
}
impl FromInner<(imp::Process, imp::StdioPipes)> for Child {
fn from_inner((handle, io): (imp::Process, imp::StdioPipes)) -> Child {
Child {
handle: handle,
stdin: io.stdin.map(ChildStdin::from_inner),
stdout: io.stdout.map(ChildStdout::from_inner),
stderr: io.stderr.map(ChildStderr::from_inner),
}
}
}
impl IntoInner<imp::Process> for Child {
fn into_inner(self) -> imp::Process { self.handle }
}
@ -110,6 +117,12 @@ impl IntoInner<AnonPipe> for ChildStdin {
fn into_inner(self) -> AnonPipe { self.inner }
}
impl FromInner<AnonPipe> for ChildStdin {
fn from_inner(pipe: AnonPipe) -> ChildStdin {
ChildStdin { inner: pipe }
}
}
/// A handle to a child process's stdout
#[stable(feature = "process", since = "1.0.0")]
pub struct ChildStdout {
@ -131,6 +144,12 @@ impl IntoInner<AnonPipe> for ChildStdout {
fn into_inner(self) -> AnonPipe { self.inner }
}
impl FromInner<AnonPipe> for ChildStdout {
fn from_inner(pipe: AnonPipe) -> ChildStdout {
ChildStdout { inner: pipe }
}
}
/// A handle to a child process's stderr
#[stable(feature = "process", since = "1.0.0")]
pub struct ChildStderr {
@ -152,6 +171,12 @@ impl IntoInner<AnonPipe> for ChildStderr {
fn into_inner(self) -> AnonPipe { self.inner }
}
impl FromInner<AnonPipe> for ChildStderr {
fn from_inner(pipe: AnonPipe) -> ChildStderr {
ChildStderr { inner: pipe }
}
}
/// The `Command` type acts as a process builder, providing fine-grained control
/// over how a new process should be spawned. A default configuration can be
/// generated using `Command::new(program)`, where `program` gives a path to the
@ -171,11 +196,6 @@ impl IntoInner<AnonPipe> for ChildStderr {
#[stable(feature = "process", since = "1.0.0")]
pub struct Command {
inner: imp::Command,
// Details explained in the builder methods
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
}
impl Command {
@ -191,12 +211,7 @@ impl Command {
/// otherwise configure the process.
#[stable(feature = "process", since = "1.0.0")]
pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
Command {
inner: imp::Command::new(program.as_ref()),
stdin: None,
stdout: None,
stderr: None,
}
Command { inner: imp::Command::new(program.as_ref()) }
}
/// Add an argument to pass to the program.
@ -209,7 +224,9 @@ impl Command {
/// Add multiple arguments to pass to the program.
#[stable(feature = "process", since = "1.0.0")]
pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Command {
self.inner.args(args.iter().map(AsRef::as_ref));
for arg in args {
self.arg(arg.as_ref());
}
self
}
@ -241,7 +258,7 @@ impl Command {
/// Sets the working directory for the child process.
#[stable(feature = "process", since = "1.0.0")]
pub fn current_dir<P: AsRef<path::Path>>(&mut self, dir: P) -> &mut Command {
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command {
self.inner.cwd(dir.as_ref().as_ref());
self
}
@ -249,57 +266,30 @@ impl Command {
/// Configuration for the child process's stdin handle (file descriptor 0).
#[stable(feature = "process", since = "1.0.0")]
pub fn stdin(&mut self, cfg: Stdio) -> &mut Command {
self.stdin = Some(cfg);
self.inner.stdin(cfg.0);
self
}
/// Configuration for the child process's stdout handle (file descriptor 1).
#[stable(feature = "process", since = "1.0.0")]
pub fn stdout(&mut self, cfg: Stdio) -> &mut Command {
self.stdout = Some(cfg);
self.inner.stdout(cfg.0);
self
}
/// Configuration for the child process's stderr handle (file descriptor 2).
#[stable(feature = "process", since = "1.0.0")]
pub fn stderr(&mut self, cfg: Stdio) -> &mut Command {
self.stderr = Some(cfg);
self.inner.stderr(cfg.0);
self
}
fn spawn_inner(&self, default_io: StdioImp) -> io::Result<Child> {
let default_io = Stdio(default_io);
// See comment on `setup_io` for what `_drop_later` is.
let (their_stdin, our_stdin, _drop_later) = try!(
setup_io(self.stdin.as_ref().unwrap_or(&default_io), true)
);
let (their_stdout, our_stdout, _drop_later) = try!(
setup_io(self.stdout.as_ref().unwrap_or(&default_io), false)
);
let (their_stderr, our_stderr, _drop_later) = try!(
setup_io(self.stderr.as_ref().unwrap_or(&default_io), false)
);
match imp::Process::spawn(&self.inner, their_stdin, their_stdout,
their_stderr) {
Err(e) => Err(e),
Ok(handle) => Ok(Child {
handle: handle,
status: None,
stdin: our_stdin.map(|fd| ChildStdin { inner: fd }),
stdout: our_stdout.map(|fd| ChildStdout { inner: fd }),
stderr: our_stderr.map(|fd| ChildStderr { inner: fd }),
})
}
}
/// Executes the command as a child process, returning a handle to it.
///
/// By default, stdin, stdout and stderr are inherited from the parent.
#[stable(feature = "process", since = "1.0.0")]
pub fn spawn(&mut self) -> io::Result<Child> {
self.spawn_inner(StdioImp::Inherit)
self.inner.spawn(imp::Stdio::Inherit).map(Child::from_inner)
}
/// Executes the command as a child process, waiting for it to finish and
@ -322,7 +312,8 @@ impl Command {
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub fn output(&mut self) -> io::Result<Output> {
self.spawn_inner(StdioImp::MakePipe).and_then(|p| p.wait_with_output())
self.inner.spawn(imp::Stdio::MakePipe).map(Child::from_inner)
.and_then(|p| p.wait_with_output())
}
/// Executes a command as a child process, waiting for it to finish and
@ -365,33 +356,6 @@ impl AsInnerMut<imp::Command> for Command {
fn as_inner_mut(&mut self) -> &mut imp::Command { &mut self.inner }
}
// Takes a `Stdio` configuration (this module) and whether the to-be-owned
// handle will be readable.
//
// Returns a triple of (stdio to spawn with, stdio to store, stdio to drop). The
// stdio to spawn with is passed down to the `sys` module and indicates how the
// stdio stream should be set up. The "stdio to store" is an object which
// should be returned in the `Child` that makes its way out. The "stdio to drop"
// represents the raw value of "stdio to spawn with", but is the owned variant
// for it. This needs to be dropped after the child spawns
fn setup_io(io: &Stdio, readable: bool)
-> io::Result<(imp::Stdio, Option<AnonPipe>, Option<AnonPipe>)>
{
Ok(match io.0 {
StdioImp::MakePipe => {
let (reader, writer) = try!(pipe::anon_pipe());
if readable {
(imp::Stdio::Raw(reader.raw()), Some(writer), Some(reader))
} else {
(imp::Stdio::Raw(writer.raw()), Some(reader), Some(writer))
}
}
StdioImp::Raw(ref owned) => (imp::Stdio::Raw(owned.raw()), None, None),
StdioImp::Inherit => (imp::Stdio::Inherit, None, None),
StdioImp::None => (imp::Stdio::None, None, None),
})
}
/// The output of a finished process.
#[derive(PartialEq, Eq, Clone)]
#[stable(feature = "process", since = "1.0.0")]
@ -435,34 +399,26 @@ impl fmt::Debug for Output {
/// Describes what to do with a standard I/O stream for a child process.
#[stable(feature = "process", since = "1.0.0")]
pub struct Stdio(StdioImp);
// The internal enum for stdio setup; see below for descriptions.
enum StdioImp {
MakePipe,
Raw(imp::RawStdio),
Inherit,
None,
}
pub struct Stdio(imp::Stdio);
impl Stdio {
/// A new pipe should be arranged to connect the parent and child processes.
#[stable(feature = "process", since = "1.0.0")]
pub fn piped() -> Stdio { Stdio(StdioImp::MakePipe) }
pub fn piped() -> Stdio { Stdio(imp::Stdio::MakePipe) }
/// The child inherits from the corresponding parent descriptor.
#[stable(feature = "process", since = "1.0.0")]
pub fn inherit() -> Stdio { Stdio(StdioImp::Inherit) }
pub fn inherit() -> Stdio { Stdio(imp::Stdio::Inherit) }
/// This stream will be ignored. This is the equivalent of attaching the
/// stream to `/dev/null`
#[stable(feature = "process", since = "1.0.0")]
pub fn null() -> Stdio { Stdio(StdioImp::None) }
pub fn null() -> Stdio { Stdio(imp::Stdio::Null) }
}
impl FromInner<imp::RawStdio> for Stdio {
fn from_inner(inner: imp::RawStdio) -> Stdio {
Stdio(StdioImp::Raw(inner))
impl FromInner<imp::Stdio> for Stdio {
fn from_inner(inner: imp::Stdio) -> Stdio {
Stdio(inner)
}
}
@ -506,34 +462,7 @@ impl Child {
/// SIGKILL on unix platforms.
#[stable(feature = "process", since = "1.0.0")]
pub fn kill(&mut self) -> io::Result<()> {
#[cfg(unix)] fn collect_status(p: &mut Child) {
// On Linux (and possibly other unices), a process that has exited will
// continue to accept signals because it is "defunct". The delivery of
// signals will only fail once the child has been reaped. For this
// reason, if the process hasn't exited yet, then we attempt to collect
// their status with WNOHANG.
if p.status.is_none() {
match p.handle.try_wait() {
Some(status) => { p.status = Some(status); }
None => {}
}
}
}
#[cfg(windows)] fn collect_status(_p: &mut Child) {}
collect_status(self);
// if the process has finished, and therefore had waitpid called,
// and we kill it, then on unix we might ending up killing a
// newer process that happens to have the same (re-used) id
if self.status.is_some() {
return Err(Error::new(
ErrorKind::InvalidInput,
"invalid argument: can't kill an exited process",
))
}
unsafe { self.handle.kill() }
self.handle.kill()
}
/// Returns the OS-assigned process identifier associated with this child.
@ -553,14 +482,7 @@ impl Child {
#[stable(feature = "process", since = "1.0.0")]
pub fn wait(&mut self) -> io::Result<ExitStatus> {
drop(self.stdin.take());
match self.status {
Some(code) => Ok(ExitStatus(code)),
None => {
let status = try!(self.handle.wait());
self.status = Some(status);
Ok(ExitStatus(status))
}
}
self.handle.wait().map(ExitStatus)
}
/// Simultaneously waits for the child to exit and collect all remaining

View File

@ -12,8 +12,11 @@
#![stable(feature = "rust1", since = "1.0.0")]
use os::unix::raw::{uid_t, gid_t};
use prelude::v1::*;
use io;
use os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd};
use os::unix::raw::{uid_t, gid_t};
use process;
use sys;
use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner};
@ -44,6 +47,56 @@ pub trait CommandExt {
#[unstable(feature = "process_session_leader", reason = "recently added",
issue = "27811")]
fn session_leader(&mut self, on: bool) -> &mut process::Command;
/// Schedules a closure to be run just before the `exec` function is
/// invoked.
///
/// The closure is allowed to return an I/O error whose OS error code will
/// be communicated back to the parent and returned as an error from when
/// the spawn was requested.
///
/// Multiple closures can be registered and they will be called in order of
/// their registration. If a closure returns `Err` then no further closures
/// will be called and the spawn operation will immediately return with a
/// failure.
///
/// # Notes
///
/// This closure will be run in the context of the child process after a
/// `fork`. This primarily means that any modificatons made to memory on
/// behalf of this closure will **not** be visible to the parent process.
/// This is often a very constrained environment where normal operations
/// like `malloc` or acquiring a mutex are not guaranteed to work (due to
/// other threads perhaps still running when the `fork` was run).
///
/// When this closure is run, aspects such as the stdio file descriptors and
/// working directory have successfully been changed, so output to these
/// locations may not appear where intended.
#[unstable(feature = "process_exec", issue = "31398")]
fn before_exec<F>(&mut self, f: F) -> &mut process::Command
where F: FnMut() -> io::Result<()> + Send + Sync + 'static;
/// Performs all the required setup by this `Command`, followed by calling
/// the `execvp` syscall.
///
/// On success this function will not return, and otherwise it will return
/// an error indicating why the exec (or another part of the setup of the
/// `Command`) failed.
///
/// This function, unlike `spawn`, will **not** `fork` the process to create
/// a new child. Like spawn, however, the default behavior for the stdio
/// descriptors will be to inherited from the current process.
///
/// # Notes
///
/// The process may be in a "broken state" if this function returns in
/// error. For example the working directory, environment variables, signal
/// handling settings, various user/group information, or aspects of stdio
/// file descriptors may have changed. If a "transactional spawn" is
/// required to gracefully handle errors it is recommended to use the
/// cross-platform `spawn` instead.
#[unstable(feature = "process_exec", issue = "31398")]
fn exec(&mut self) -> io::Error;
}
#[stable(feature = "rust1", since = "1.0.0")]
@ -62,6 +115,17 @@ impl CommandExt for process::Command {
self.as_inner_mut().session_leader(on);
self
}
fn before_exec<F>(&mut self, f: F) -> &mut process::Command
where F: FnMut() -> io::Result<()> + Send + Sync + 'static
{
self.as_inner_mut().before_exec(Box::new(f));
self
}
fn exec(&mut self) -> io::Error {
self.as_inner_mut().exec(sys::process::Stdio::Inherit)
}
}
/// Unix-specific extensions to `std::process::ExitStatus`
@ -82,7 +146,9 @@ impl ExitStatusExt for process::ExitStatus {
#[stable(feature = "process_extensions", since = "1.2.0")]
impl FromRawFd for process::Stdio {
unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
process::Stdio::from_inner(sys::fd::FileDesc::new(fd))
let fd = sys::fd::FileDesc::new(fd);
let io = sys::process::Stdio::Fd(fd);
process::Stdio::from_inner(io)
}
}

View File

@ -61,7 +61,6 @@ impl AnonPipe {
self.0.write(buf)
}
pub fn raw(&self) -> libc::c_int { self.0.raw() }
pub fn fd(&self) -> &FileDesc { &self.0 }
pub fn into_fd(self) -> FileDesc { self.0 }
}

View File

@ -8,89 +8,186 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(non_snake_case)]
use prelude::v1::*;
use os::unix::prelude::*;
use collections::HashMap;
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_void, c_int, gid_t, uid_t};
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::AnonPipe;
use sys::pipe::{self, AnonPipe};
use sys::{self, cvt, cvt_r};
////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////
#[derive(Clone)]
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, OsString>>, // Guaranteed to have no NULs.
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>,
session_leader: bool,
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 {
program: os2c(program, &mut saw_nul),
argv: vec![program.as_ptr(), 0 as *const _],
program: program,
args: Vec::new(),
env: None,
envp: None,
cwd: None,
uid: None,
gid: None,
session_leader: false,
saw_nul: saw_nul,
closures: Vec::new(),
stdin: None,
stdout: None,
stderr: None,
}
}
pub fn arg(&mut self, arg: &OsStr) {
self.args.push(os2c(arg, &mut self.saw_nul));
// 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(0 as *const _);
// Also make sure we keep track of the owned value to schedule a
// destructor for this memory.
self.args.push(arg);
}
pub fn args<'a, I: Iterator<Item = &'a OsStr>>(&mut self, args: I) {
let mut saw_nul = self.saw_nul;
self.args.extend(args.map(|arg| os2c(arg, &mut saw_nul)));
self.saw_nul = saw_nul;
}
fn init_env_map(&mut self) {
fn init_env_map(&mut self) -> (&mut HashMap<OsString, (usize, CString)>,
&mut Vec<*const c_char>) {
if self.env.is_none() {
// Will not add NULs to env: preexisting environment will not contain any.
self.env = Some(env::vars_os().collect());
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(0 as *const _);
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 k = OsString::from_vec(os2c(key, &mut self.saw_nul).into_bytes());
let v = OsString::from_vec(os2c(val, &mut self.saw_nul).into_bytes());
let new_key = pair_to_key(key, val, &mut self.saw_nul);
let (map, envp) = self.init_env_map();
// Will not add NULs to env: return without inserting if any were seen.
if self.saw_nul {
return;
// If `key` is already present then we 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(0 as *const _);
e.insert((len - 1, new_key));
}
}
}
self.init_env_map();
self.env.as_mut()
.unwrap()
.insert(k, v);
}
pub fn env_remove(&mut self, key: &OsStr) {
self.init_env_map();
self.env.as_mut().unwrap().remove(key);
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.env = Some(HashMap::new());
self.envp = Some(vec![0 as *const _]);
}
pub fn cwd(&mut self, dir: &OsStr) {
self.cwd = Some(os2c(dir, &mut self.saw_nul));
}
@ -103,147 +200,66 @@ impl Command {
pub fn session_leader(&mut self, session_leader: bool) {
self.session_leader = session_leader;
}
}
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 fmt::Debug for Command {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "{:?}", self.program));
for arg in &self.args {
try!(write!(f, " {:?}", arg));
}
Ok(())
}
}
////////////////////////////////////////////////////////////////////////////////
// Processes
////////////////////////////////////////////////////////////////////////////////
/// Unix exit statuses
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ExitStatus(c_int);
#[cfg(any(target_os = "linux", target_os = "android",
target_os = "nacl", target_os = "solaris",
target_os = "emscripten"))]
mod status_imp {
pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
}
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "bitrig",
target_os = "netbsd",
target_os = "openbsd"))]
mod status_imp {
pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
}
impl ExitStatus {
fn exited(&self) -> bool {
status_imp::WIFEXITED(self.0)
pub fn before_exec(&mut self,
f: Box<FnMut() -> io::Result<()> + Send + Sync>) {
self.closures.push(f);
}
pub fn success(&self) -> bool {
self.code() == Some(0)
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 code(&self) -> Option<i32> {
if self.exited() {
Some(status_imp::WEXITSTATUS(self.0))
} else {
None
}
}
pub fn spawn(&mut self, default: Stdio)
-> io::Result<(Process, StdioPipes)> {
const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
pub fn signal(&self) -> Option<i32> {
if !self.exited() {
Some(status_imp::WTERMSIG(self.0))
} else {
None
}
}
}
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
}
pub enum Stdio {
Inherit,
None,
Raw(c_int),
}
pub type RawStdio = FileDesc;
const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
impl Process {
pub unsafe fn kill(&self) -> io::Result<()> {
try!(cvt(libc::kill(self.pid, libc::SIGKILL)));
Ok(())
}
pub fn spawn(cfg: &Command,
in_fd: Stdio,
out_fd: Stdio,
err_fd: Stdio) -> io::Result<Process> {
if cfg.saw_nul {
return Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data"));
if self.saw_nul {
return Err(io::Error::new(ErrorKind::InvalidInput,
"nul byte found in provided data"));
}
let dirp = cfg.cwd.as_ref().map(|c| c.as_ptr()).unwrap_or(ptr::null());
let (envp, _a, _b) = make_envp(cfg.env.as_ref());
let (argv, _a) = make_argv(&cfg.program, &cfg.args);
let (ours, theirs) = try!(self.setup_io(default));
let (input, output) = try!(sys::pipe::anon_pipe());
let pid = unsafe {
match libc::fork() {
match try!(cvt(libc::fork())) {
0 => {
drop(input);
Process::child_after_fork(cfg, output, argv, envp, dirp,
in_fd, out_fd, err_fd)
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 if n < 0 => return Err(Error::last_os_error()),
n => n,
}
};
let p = Process{ pid: pid };
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),
Ok(0) => return Ok((p, ours)),
Ok(8) => {
assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
"Validation on the CLOEXEC pipe failed: {:?}", bytes);
@ -276,6 +292,18 @@ impl Process {
}
}
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) {
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
@ -306,98 +334,28 @@ impl Process {
// 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 child_after_fork(cfg: &Command,
mut output: AnonPipe,
argv: *const *const libc::c_char,
envp: *const libc::c_void,
dirp: *const libc::c_char,
in_fd: Stdio,
out_fd: Stdio,
err_fd: Stdio) -> ! {
fn fail(output: &mut AnonPipe) -> ! {
let errno = sys::os::errno() 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());
unsafe { libc::_exit(1) }
unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Error {
macro_rules! try {
($e:expr) => (match $e {
Ok(e) => e,
Err(e) => return e,
})
}
// 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.
let maybe_migrate = |src: Stdio, output: &mut AnonPipe| {
match src {
Stdio::Raw(fd @ libc::STDIN_FILENO) |
Stdio::Raw(fd @ libc::STDOUT_FILENO) |
Stdio::Raw(fd @ libc::STDERR_FILENO) => {
let fd = match cvt_r(|| libc::dup(fd)) {
Ok(fd) => fd,
Err(_) => fail(output),
};
let fd = FileDesc::new(fd);
fd.set_cloexec();
Stdio::Raw(fd.into_raw())
},
s @ Stdio::None |
s @ Stdio::Inherit |
s @ Stdio::Raw(_) => s,
}
};
let setup = |src: Stdio, dst: c_int| {
match src {
Stdio::Inherit => true,
Stdio::Raw(fd) => cvt_r(|| libc::dup2(fd, dst)).is_ok(),
// If a stdio file descriptor is set to be ignored, we open up
// /dev/null into that file descriptor. Otherwise, the first
// file descriptor opened up in the child would be numbered as
// one of the stdio file descriptors, which is likely to wreak
// havoc.
Stdio::None => {
let mut opts = OpenOptions::new();
opts.read(dst == libc::STDIN_FILENO);
opts.write(dst != libc::STDIN_FILENO);
let devnull = CStr::from_ptr(b"/dev/null\0".as_ptr()
as *const _);
if let Ok(f) = File::open_c(devnull, &opts) {
cvt_r(|| libc::dup2(f.fd().raw(), dst)).is_ok()
} else {
false
}
}
}
};
// Make sure we migrate all source descriptors before
// we start overwriting them
let in_fd = maybe_migrate(in_fd, &mut output);
let out_fd = maybe_migrate(out_fd, &mut output);
let err_fd = maybe_migrate(err_fd, &mut output);
if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) }
if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) }
if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) }
if let Some(u) = cfg.gid {
if libc::setgid(u as libc::gid_t) != 0 {
fail(&mut output);
}
if let Some(fd) = stdio.stdin.fd() {
try!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
}
if let Some(u) = cfg.uid {
if let Some(fd) = stdio.stdout.fd() {
try!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
}
if let Some(fd) = stdio.stderr.fd() {
try!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
}
if let Some(u) = self.gid {
try!(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
@ -407,26 +365,23 @@ impl Process {
// privilege dropping function.
let _ = libc::setgroups(0, ptr::null());
if libc::setuid(u as libc::uid_t) != 0 {
fail(&mut output);
}
try!(cvt(libc::setuid(u as uid_t)));
}
if cfg.session_leader {
if self.session_leader {
// Don't check the error of setsid because it fails if we're the
// process leader already. We just forked so it shouldn't return
// error, but ignore it anyway.
let _ = libc::setsid();
}
if !dirp.is_null() && libc::chdir(dirp) == -1 {
fail(&mut output);
if let Some(ref cwd) = self.cwd {
try!(cvt(libc::chdir(cwd.as_ptr())));
}
if !envp.is_null() {
*sys::os::environ() = envp as *const _;
if let Some(ref envp) = self.envp {
*sys::os::environ() = envp.as_ptr();
}
#[cfg(not(target_os = "nacl"))]
unsafe fn reset_signal_handling(output: &mut AnonPipe) {
use mem;
// NaCl has no signal support.
if cfg!(not(target_os = "nacl")) {
// 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
@ -435,94 +390,206 @@ impl Process {
// need to clean things up now to avoid confusing the program
// we're about to run.
let mut set: libc::sigset_t = mem::uninitialized();
if libc::sigemptyset(&mut set) != 0 ||
libc::pthread_sigmask(libc::SIG_SETMASK, &set, ptr::null_mut()) != 0 ||
libc::signal(
libc::SIGPIPE, mem::transmute(libc::SIG_DFL)
) == mem::transmute(libc::SIG_ERR)
{
fail(output);
try!(cvt(libc::sigemptyset(&mut set)));
try!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
ptr::null_mut())));
let ret = libc::signal(libc::SIGPIPE, libc::SIG_DFL);
if ret == libc::SIG_ERR {
return io::Error::last_os_error()
}
}
#[cfg(target_os = "nacl")]
unsafe fn reset_signal_handling(_output: &mut AnonPipe) {
// NaCl has no signal support.
}
reset_signal_handling(&mut output);
let _ = libc::execvp(*argv, argv);
fail(&mut output)
for callback in self.closures.iter_mut() {
try!(callback());
}
libc::execvp(self.argv[0], self.argv.as_ptr());
io::Error::last_os_error()
}
fn setup_io(&self, default: Stdio) -> io::Result<(StdioPipes, ChildPipes)> {
let stdin = self.stdin.as_ref().unwrap_or(&default);
let stdout = self.stdout.as_ref().unwrap_or(&default);
let stderr = self.stderr.as_ref().unwrap_or(&default);
let (their_stdin, our_stdin) = try!(stdin.to_child_stdio(true));
let (their_stdout, our_stdout) = try!(stdout.to_child_stdio(false));
let (their_stderr, our_stderr) = try!(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(try!(fd.duplicate())), None))
} else {
Ok((ChildStdio::Explicit(fd.raw()), None))
}
}
Stdio::MakePipe => {
let (reader, writer) = try!(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 = try!(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 {
try!(write!(f, "{:?}", self.program));
for arg in &self.args {
try!(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 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 wait(&self) -> io::Result<ExitStatus> {
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;
try!(cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) }));
self.status = Some(ExitStatus(status));
Ok(ExitStatus(status))
}
pub fn try_wait(&self) -> Option<ExitStatus> {
let mut status = 0 as c_int;
match cvt_r(|| unsafe {
libc::waitpid(self.pid, &mut status, libc::WNOHANG)
}) {
Ok(0) => None,
Ok(n) if n == self.pid => Some(ExitStatus(status)),
Ok(n) => panic!("unknown pid: {}", n),
Err(e) => panic!("unknown waitpid error: {}", e),
}
}
}
fn make_argv(prog: &CString, args: &[CString])
-> (*const *const libc::c_char, Vec<*const libc::c_char>)
{
let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1);
// Convert the CStrings into an array of pointers. Also return the
// vector that owns the raw pointers, to ensure they live long
// enough.
ptrs.push(prog.as_ptr());
ptrs.extend(args.iter().map(|tmp| tmp.as_ptr()));
// Add a terminating null pointer (required by libc).
ptrs.push(ptr::null());
(ptrs.as_ptr(), ptrs)
}
fn make_envp(env: Option<&HashMap<OsString, OsString>>)
-> (*const c_void, Vec<Vec<u8>>, Vec<*const libc::c_char>)
{
// On posixy systems we can pass a char** for envp, which is a
// null-terminated array of "k=v\0" strings. As with make_argv, we
// return two vectors that own the data to ensure that they live
// long enough.
if let Some(env) = env {
let mut tmps = Vec::with_capacity(env.len());
for pair in env {
let mut kv = Vec::new();
kv.extend_from_slice(pair.0.as_bytes());
kv.push('=' as u8);
kv.extend_from_slice(pair.1.as_bytes());
kv.push(0); // terminating null
tmps.push(kv);
}
let mut ptrs: Vec<*const libc::c_char> =
tmps.iter()
.map(|tmp| tmp.as_ptr() as *const libc::c_char)
.collect();
ptrs.push(ptr::null());
(ptrs.as_ptr() as *const _, tmps, ptrs)
} else {
(ptr::null(), Vec::new(), Vec::new())
}
}
#[cfg(test)]
@ -534,7 +601,7 @@ mod tests {
use mem;
use ptr;
use libc;
use sys::{self, cvt};
use sys::cvt;
macro_rules! t {
($e:expr) => {
@ -570,9 +637,7 @@ mod tests {
fn test_process_mask() {
unsafe {
// Test to make sure that a signal mask does not get inherited.
let cmd = Command::new(OsStr::new("cat"));
let (stdin_read, stdin_write) = t!(sys::pipe::anon_pipe());
let (stdout_read, stdout_write) = t!(sys::pipe::anon_pipe());
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();
@ -580,11 +645,12 @@ mod tests {
t!(cvt(sigaddset(&mut set, libc::SIGINT)));
t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, &mut old_set)));
let cat = t!(Process::spawn(&cmd, Stdio::Raw(stdin_read.raw()),
Stdio::Raw(stdout_write.raw()),
Stdio::None));
drop(stdin_read);
drop(stdout_write);
cmd.stdin(Stdio::MakePipe);
cmd.stdout(Stdio::MakePipe);
let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null));
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())));

View File

@ -21,7 +21,8 @@ use sys_common::{AsInner, FromInner, IntoInner};
impl FromRawHandle for process::Stdio {
unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio {
let handle = sys::handle::Handle::new(handle as *mut _);
process::Stdio::from_inner(handle)
let io = sys::process::Stdio::Handle(handle);
process::Stdio::from_inner(io)
}
}

View File

@ -37,8 +37,6 @@ impl AnonPipe {
pub fn handle(&self) -> &Handle { &self.inner }
pub fn into_handle(self) -> Handle { self.inner }
pub fn raw(&self) -> c::HANDLE { self.inner.raw() }
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}

View File

@ -26,9 +26,9 @@ use path::Path;
use ptr;
use sync::StaticMutex;
use sys::c;
use sys::fs::{OpenOptions, File};
use sys::handle::{Handle, RawHandle};
use sys::handle::Handle;
use sys::pipe::{self, AnonPipe};
use sys::stdio;
use sys::{self, cvt};
use sys_common::{AsInner, FromInner};
@ -51,13 +51,28 @@ fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
}
}
#[derive(Clone)]
pub struct Command {
program: OsString,
args: Vec<OsString>,
env: Option<HashMap<OsString, OsString>>,
cwd: Option<OsString>,
detach: bool, // not currently exposed in std::process
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
}
pub enum Stdio {
Inherit,
Null,
MakePipe,
Handle(Handle),
}
pub struct StdioPipes {
pub stdin: Option<AnonPipe>,
pub stdout: Option<AnonPipe>,
pub stderr: Option<AnonPipe>,
}
impl Command {
@ -68,15 +83,15 @@ impl Command {
env: None,
cwd: None,
detach: false,
stdin: None,
stdout: None,
stderr: None,
}
}
pub fn arg(&mut self, arg: &OsStr) {
self.args.push(arg.to_os_string())
}
pub fn args<'a, I: Iterator<Item = &'a OsStr>>(&mut self, args: I) {
self.args.extend(args.map(OsStr::to_os_string))
}
fn init_env_map(&mut self){
if self.env.is_none() {
self.env = Some(env::vars_os().map(|(key, val)| {
@ -98,6 +113,103 @@ impl Command {
pub fn cwd(&mut self, dir: &OsStr) {
self.cwd = Some(dir.to_os_string())
}
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)
-> io::Result<(Process, StdioPipes)> {
// To have the spawning semantics of unix/windows stay the same, we need
// to read the *child's* PATH if one is provided. See #15149 for more
// details.
let program = self.env.as_ref().and_then(|env| {
for (key, v) in env {
if OsStr::new("PATH") != &**key { continue }
// Split the value and test each path to see if the
// program exists.
for path in split_paths(&v) {
let path = path.join(self.program.to_str().unwrap())
.with_extension(env::consts::EXE_EXTENSION);
if fs::metadata(&path).is_ok() {
return Some(path.into_os_string())
}
}
break
}
None
});
let mut si = zeroed_startupinfo();
si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
si.dwFlags = c::STARTF_USESTDHANDLES;
let program = program.as_ref().unwrap_or(&self.program);
let mut cmd_str = try!(make_command_line(program, &self.args));
cmd_str.push(0); // add null terminator
// stolen from the libuv code.
let mut flags = c::CREATE_UNICODE_ENVIRONMENT;
if self.detach {
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
}
let (envp, _data) = try!(make_envp(self.env.as_ref()));
let (dirp, _data) = try!(make_dirp(self.cwd.as_ref()));
let mut pi = zeroed_process_information();
// Prepare all stdio handles to be inherited by the child. This
// currently involves duplicating any existing ones with the ability to
// be inherited by child processes. Note, however, that once an
// inheritable handle is created, *any* spawned child will inherit that
// handle. We only want our own child to inherit this handle, so we wrap
// the remaining portion of this spawn in a mutex.
//
// For more information, msdn also has an article about this race:
// http://support.microsoft.com/kb/315939
static CREATE_PROCESS_LOCK: StaticMutex = StaticMutex::new();
let _lock = CREATE_PROCESS_LOCK.lock();
let mut pipes = StdioPipes {
stdin: None,
stdout: None,
stderr: None,
};
let stdin = self.stdin.as_ref().unwrap_or(&default);
let stdout = self.stdout.as_ref().unwrap_or(&default);
let stderr = self.stderr.as_ref().unwrap_or(&default);
let stdin = try!(stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin));
let stdout = try!(stdout.to_handle(c::STD_OUTPUT_HANDLE,
&mut pipes.stdout));
let stderr = try!(stderr.to_handle(c::STD_ERROR_HANDLE,
&mut pipes.stderr));
si.hStdInput = stdin.raw();
si.hStdOutput = stdout.raw();
si.hStdError = stderr.raw();
try!(unsafe {
cvt(c::CreateProcessW(ptr::null(),
cmd_str.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
c::TRUE, flags, envp, dirp,
&mut si, &mut pi))
});
// We close the thread handle because we don't care about keeping
// the thread id valid, and we aren't keeping the thread handle
// around to be able to close it later.
drop(Handle::new(pi.hThread));
Ok((Process { handle: Handle::new(pi.hProcess) }, pipes))
}
}
impl fmt::Debug for Command {
@ -110,6 +222,63 @@ impl fmt::Debug for Command {
}
}
impl Stdio {
fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>)
-> io::Result<Handle> {
match *self {
// If no stdio handle is available, then inherit means that it
// should still be unavailable so propagate the
// INVALID_HANDLE_VALUE.
Stdio::Inherit => {
match stdio::get(stdio_id) {
Ok(io) => io.handle().duplicate(0, true,
c::DUPLICATE_SAME_ACCESS),
Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)),
}
}
Stdio::MakePipe => {
let (reader, writer) = try!(pipe::anon_pipe());
let (ours, theirs) = if stdio_id == c::STD_INPUT_HANDLE {
(writer, reader)
} else {
(reader, writer)
};
*pipe = Some(ours);
try!(cvt(unsafe {
c::SetHandleInformation(theirs.handle().raw(),
c::HANDLE_FLAG_INHERIT,
c::HANDLE_FLAG_INHERIT)
}));
Ok(theirs.into_handle())
}
Stdio::Handle(ref handle) => {
handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)
}
// Open up a reference to NUL with appropriate read/write
// permissions as well as the ability to be inherited to child
// processes (as this is about to be inherited).
Stdio::Null => {
let size = mem::size_of::<c::SECURITY_ATTRIBUTES>();
let mut sa = c::SECURITY_ATTRIBUTES {
nLength: size as c::DWORD,
lpSecurityDescriptor: ptr::null_mut(),
bInheritHandle: 1,
};
let mut opts = OpenOptions::new();
opts.read(stdio_id == c::STD_INPUT_HANDLE);
opts.write(stdio_id != c::STD_INPUT_HANDLE);
opts.security_attributes(&mut sa);
File::open(Path::new("NUL"), &opts).map(|file| {
file.into_handle()
})
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Processes
////////////////////////////////////////////////////////////////////////////////
@ -123,90 +292,11 @@ pub struct Process {
handle: Handle,
}
pub enum Stdio {
Inherit,
None,
Raw(c::HANDLE),
}
pub type RawStdio = Handle;
impl Process {
pub fn spawn(cfg: &Command,
in_handle: Stdio,
out_handle: Stdio,
err_handle: Stdio) -> io::Result<Process>
{
// To have the spawning semantics of unix/windows stay the same, we need
// to read the *child's* PATH if one is provided. See #15149 for more
// details.
let program = cfg.env.as_ref().and_then(|env| {
for (key, v) in env {
if OsStr::new("PATH") != &**key { continue }
// Split the value and test each path to see if the
// program exists.
for path in split_paths(&v) {
let path = path.join(cfg.program.to_str().unwrap())
.with_extension(env::consts::EXE_EXTENSION);
if fs::metadata(&path).is_ok() {
return Some(path.into_os_string())
}
}
break
}
None
});
let mut si = zeroed_startupinfo();
si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
si.dwFlags = c::STARTF_USESTDHANDLES;
let stdin = try!(in_handle.to_handle(c::STD_INPUT_HANDLE));
let stdout = try!(out_handle.to_handle(c::STD_OUTPUT_HANDLE));
let stderr = try!(err_handle.to_handle(c::STD_ERROR_HANDLE));
si.hStdInput = stdin.raw();
si.hStdOutput = stdout.raw();
si.hStdError = stderr.raw();
let program = program.as_ref().unwrap_or(&cfg.program);
let mut cmd_str = try!(make_command_line(program, &cfg.args));
cmd_str.push(0); // add null terminator
// stolen from the libuv code.
let mut flags = c::CREATE_UNICODE_ENVIRONMENT;
if cfg.detach {
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
}
let (envp, _data) = try!(make_envp(cfg.env.as_ref()));
let (dirp, _data) = try!(make_dirp(cfg.cwd.as_ref()));
let mut pi = zeroed_process_information();
try!(unsafe {
// `CreateProcess` is racy!
// http://support.microsoft.com/kb/315939
static CREATE_PROCESS_LOCK: StaticMutex = StaticMutex::new();
let _lock = CREATE_PROCESS_LOCK.lock();
cvt(c::CreateProcessW(ptr::null(),
cmd_str.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
c::TRUE, flags, envp, dirp,
&mut si, &mut pi))
});
// We close the thread handle because we don't care about keeping
// the thread id valid, and we aren't keeping the thread handle
// around to be able to close it later.
drop(Handle::new(pi.hThread));
Ok(Process { handle: Handle::new(pi.hProcess) })
}
pub unsafe fn kill(&self) -> io::Result<()> {
try!(cvt(c::TerminateProcess(self.handle.raw(), 1)));
pub fn kill(&mut self) -> io::Result<()> {
try!(cvt(unsafe {
c::TerminateProcess(self.handle.raw(), 1)
}));
Ok(())
}
@ -216,7 +306,7 @@ impl Process {
}
}
pub fn wait(&self) -> io::Result<ExitStatus> {
pub fn wait(&mut self) -> io::Result<ExitStatus> {
unsafe {
let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE);
if res != c::WAIT_OBJECT_0 {
@ -370,46 +460,6 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
}
}
impl Stdio {
fn to_handle(&self, stdio_id: c::DWORD) -> io::Result<Handle> {
match *self {
// If no stdio handle is available, then inherit means that it
// should still be unavailable so propagate the
// INVALID_HANDLE_VALUE.
Stdio::Inherit => {
match stdio::get(stdio_id) {
Ok(io) => io.handle().duplicate(0, true,
c::DUPLICATE_SAME_ACCESS),
Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)),
}
}
Stdio::Raw(handle) => {
RawHandle::new(handle).duplicate(0, true, c::DUPLICATE_SAME_ACCESS)
}
// Similarly to unix, we don't actually leave holes for the
// stdio file descriptors, but rather open up /dev/null
// equivalents. These equivalents are drawn from libuv's
// windows process spawning.
Stdio::None => {
let size = mem::size_of::<c::SECURITY_ATTRIBUTES>();
let mut sa = c::SECURITY_ATTRIBUTES {
nLength: size as c::DWORD,
lpSecurityDescriptor: ptr::null_mut(),
bInheritHandle: 1,
};
let mut opts = OpenOptions::new();
opts.read(stdio_id == c::STD_INPUT_HANDLE);
opts.write(stdio_id != c::STD_INPUT_HANDLE);
opts.security_attributes(&mut sa);
File::open(Path::new("NUL"), &opts).map(|file| {
file.into_handle()
})
}
}
}
}
#[cfg(test)]
mod tests {
use prelude::v1::*;

View File

@ -0,0 +1,90 @@
// 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.
// ignore-windows - this is a unix-specific test
#![feature(process_exec, libc)]
extern crate libc;
use std::env;
use std::io::Error;
use std::os::unix::process::CommandExt;
use std::process::Command;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
if let Some(arg) = env::args().skip(1).next() {
match &arg[..] {
"test1" => println!("hello2"),
"test2" => assert_eq!(env::var("FOO").unwrap(), "BAR"),
"test3" => assert_eq!(env::current_dir().unwrap()
.to_str().unwrap(), "/"),
"empty" => {}
_ => panic!("unknown argument: {}", arg),
}
return
}
let me = env::current_exe().unwrap();
let output = Command::new(&me).arg("test1").before_exec(|| {
println!("hello");
Ok(())
}).output().unwrap();
assert!(output.status.success());
assert!(output.stderr.is_empty());
assert_eq!(output.stdout, b"hello\nhello2\n");
let output = Command::new(&me).arg("test2").before_exec(|| {
env::set_var("FOO", "BAR");
Ok(())
}).output().unwrap();
assert!(output.status.success());
assert!(output.stderr.is_empty());
assert!(output.stdout.is_empty());
let output = Command::new(&me).arg("test3").before_exec(|| {
env::set_current_dir("/").unwrap();
Ok(())
}).output().unwrap();
assert!(output.status.success());
assert!(output.stderr.is_empty());
assert!(output.stdout.is_empty());
let output = Command::new(&me).arg("bad").before_exec(|| {
Err(Error::from_raw_os_error(102))
}).output().err().unwrap();
assert_eq!(output.raw_os_error(), Some(102));
let pid = unsafe { libc::getpid() };
assert!(pid >= 0);
let output = Command::new(&me).arg("empty").before_exec(move || {
let child = unsafe { libc::getpid() };
assert!(child >= 0);
assert!(pid != child);
Ok(())
}).output().unwrap();
assert!(output.status.success());
assert!(output.stderr.is_empty());
assert!(output.stdout.is_empty());
let mem = Arc::new(AtomicUsize::new(0));
let mem2 = mem.clone();
let output = Command::new(&me).arg("empty").before_exec(move || {
assert_eq!(mem2.fetch_add(1, Ordering::SeqCst), 0);
Ok(())
}).output().unwrap();
assert!(output.status.success());
assert!(output.stderr.is_empty());
assert!(output.stdout.is_empty());
assert_eq!(mem.load(Ordering::SeqCst), 0);
}

View File

@ -0,0 +1,72 @@
// 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.
// ignore-windows - this is a unix-specific test
// ignore-pretty
#![feature(process_exec)]
use std::env;
use std::os::unix::process::CommandExt;
use std::process::Command;
fn main() {
let mut args = env::args();
let me = args.next().unwrap();
if let Some(arg) = args.next() {
match &arg[..] {
"test1" => println!("passed"),
"exec-test1" => {
let err = Command::new(&me).arg("test1").exec();
panic!("failed to spawn: {}", err);
}
"exec-test2" => {
Command::new("/path/to/nowhere").exec();
println!("passed");
}
"exec-test3" => {
Command::new(&me).arg("bad\0").exec();
println!("passed");
}
"exec-test4" => {
Command::new(&me).current_dir("/path/to/nowhere").exec();
println!("passed");
}
_ => panic!("unknown argument: {}", arg),
}
return
}
let output = Command::new(&me).arg("exec-test1").output().unwrap();
assert!(output.status.success());
assert!(output.stderr.is_empty());
assert_eq!(output.stdout, b"passed\n");
let output = Command::new(&me).arg("exec-test2").output().unwrap();
assert!(output.status.success());
assert!(output.stderr.is_empty());
assert_eq!(output.stdout, b"passed\n");
let output = Command::new(&me).arg("exec-test3").output().unwrap();
assert!(output.status.success());
assert!(output.stderr.is_empty());
assert_eq!(output.stdout, b"passed\n");
let output = Command::new(&me).arg("exec-test4").output().unwrap();
assert!(output.status.success());
assert!(output.stderr.is_empty());
assert_eq!(output.stdout, b"passed\n");
}