use fmt;
use io::{self, Error, ErrorKind};
use path;
-use sync::mpsc::{channel, Receiver};
-use sys::pipe2::{self, AnonPipe};
-use sys::process2::Command as CommandImp;
-use sys::process2::Process as ProcessImp;
-use sys::process2::ExitStatus as ExitStatusImp;
-use sys::process2::Stdio as StdioImp2;
-use sys_common::{AsInner, AsInnerMut};
-use thread;
+use sys::pipe::{self, AnonPipe};
+use sys::process as imp;
+use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
+use thread::{self, JoinHandle};
/// Representation of a running or exited child process.
///
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub struct Child {
- handle: ProcessImp,
+ handle: imp::Process,
/// None until wait() or wait_with_output() is called.
- status: Option<ExitStatusImp>,
+ 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 stderr: Option<ChildStderr>,
}
-/// A handle to a child procesess's stdin
+impl AsInner<imp::Process> for Child {
+ fn as_inner(&self) -> &imp::Process { &self.handle }
+}
+
+impl IntoInner<imp::Process> for Child {
+ fn into_inner(self) -> imp::Process { self.handle }
+}
+
+/// A handle to a child process's stdin
#[stable(feature = "process", since = "1.0.0")]
pub struct ChildStdin {
inner: AnonPipe
}
}
-/// A handle to a child procesess's stdout
+impl AsInner<AnonPipe> for ChildStdin {
+ fn as_inner(&self) -> &AnonPipe { &self.inner }
+}
+
+impl IntoInner<AnonPipe> for ChildStdin {
+ fn into_inner(self) -> AnonPipe { self.inner }
+}
+
+/// A handle to a child process's stdout
#[stable(feature = "process", since = "1.0.0")]
pub struct ChildStdout {
inner: AnonPipe
}
}
-/// A handle to a child procesess's stderr
+impl AsInner<AnonPipe> for ChildStdout {
+ fn as_inner(&self) -> &AnonPipe { &self.inner }
+}
+
+impl IntoInner<AnonPipe> for ChildStdout {
+ fn into_inner(self) -> AnonPipe { self.inner }
+}
+
+/// A handle to a child process's stderr
#[stable(feature = "process", since = "1.0.0")]
pub struct ChildStderr {
inner: AnonPipe
}
}
+impl AsInner<AnonPipe> for ChildStderr {
+ fn as_inner(&self) -> &AnonPipe { &self.inner }
+}
+
+impl IntoInner<AnonPipe> for ChildStderr {
+ fn into_inner(self) -> AnonPipe { self.inner }
+}
+
/// 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
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub struct Command {
- inner: CommandImp,
+ inner: imp::Command,
// Details explained in the builder methods
- stdin: Option<StdioImp>,
- stdout: Option<StdioImp>,
- stderr: Option<StdioImp>,
+ stdin: Option<Stdio>,
+ stdout: Option<Stdio>,
+ stderr: Option<Stdio>,
}
impl Command {
#[stable(feature = "process", since = "1.0.0")]
pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
Command {
- inner: CommandImp::new(program.as_ref()),
+ inner: imp::Command::new(program.as_ref()),
stdin: None,
stdout: None,
stderr: None,
/// 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.0);
+ self.stdin = Some(cfg);
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.0);
+ self.stdout = Some(cfg);
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.0);
+ self.stderr = Some(cfg);
self
}
fn spawn_inner(&self, default_io: StdioImp) -> io::Result<Child> {
- let (their_stdin, our_stdin) = try!(
+ 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) = try!(
+ let (their_stdout, our_stdout, _drop_later) = try!(
setup_io(self.stdout.as_ref().unwrap_or(&default_io), false)
);
- let (their_stderr, our_stderr) = try!(
+ let (their_stderr, our_stderr, _drop_later) = try!(
setup_io(self.stderr.as_ref().unwrap_or(&default_io), false)
);
- match ProcessImp::spawn(&self.inner, their_stdin, their_stdout, their_stderr) {
+ match imp::Process::spawn(&self.inner, their_stdin, their_stdout,
+ their_stderr) {
Err(e) => Err(e),
Ok(handle) => Ok(Child {
handle: handle,
/// Executes the command as a child process, returning a handle to it.
///
- /// By default, stdin, stdout and stderr are inherited by the parent.
+ /// 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)
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub fn output(&mut self) -> io::Result<Output> {
- self.spawn_inner(StdioImp::Piped).and_then(|p| p.wait_with_output())
+ self.spawn_inner(StdioImp::MakePipe).and_then(|p| p.wait_with_output())
}
/// Executes a command as a child process, waiting for it to finish and
/// collecting its exit status.
///
- /// By default, stdin, stdout and stderr are inherited by the parent.
+ /// By default, stdin, stdout and stderr are inherited from the parent.
///
/// # Examples
///
}
}
-impl AsInner<CommandImp> for Command {
- fn as_inner(&self) -> &CommandImp { &self.inner }
+impl AsInner<imp::Command> for Command {
+ fn as_inner(&self) -> &imp::Command { &self.inner }
}
-impl AsInnerMut<CommandImp> for Command {
- fn as_inner_mut(&mut self) -> &mut CommandImp { &mut self.inner }
+impl AsInnerMut<imp::Command> for Command {
+ fn as_inner_mut(&mut self) -> &mut imp::Command { &mut self.inner }
}
-fn setup_io(io: &StdioImp, readable: bool)
- -> io::Result<(StdioImp2, Option<AnonPipe>)>
+// 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>)>
{
- use self::StdioImp::*;
- Ok(match *io {
- Null => (StdioImp2::None, None),
- Inherit => (StdioImp2::Inherit, None),
- Piped => {
- let (reader, writer) = try!(pipe2::anon_pipe());
+ Ok(match io.0 {
+ StdioImp::MakePipe => {
+ let (reader, writer) = try!(pipe::anon_pipe());
if readable {
- (StdioImp2::Piped(reader), Some(writer))
+ (imp::Stdio::Raw(reader.raw()), Some(writer), Some(reader))
} else {
- (StdioImp2::Piped(writer), Some(reader))
+ (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),
})
}
pub struct Stdio(StdioImp);
// The internal enum for stdio setup; see below for descriptions.
-#[derive(Clone)]
enum StdioImp {
- Piped,
+ MakePipe,
+ Raw(imp::RawStdio),
Inherit,
- Null,
+ None,
}
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::Piped) }
+ pub fn piped() -> Stdio { Stdio(StdioImp::MakePipe) }
/// The child inherits from the corresponding parent descriptor.
#[stable(feature = "process", since = "1.0.0")]
/// 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::Null) }
+ pub fn null() -> Stdio { Stdio(StdioImp::None) }
+}
+
+impl FromInner<imp::RawStdio> for Stdio {
+ fn from_inner(inner: imp::RawStdio) -> Stdio {
+ Stdio(StdioImp::Raw(inner))
+ }
}
/// Describes the result of a process after it has terminated.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[stable(feature = "process", since = "1.0.0")]
-pub struct ExitStatus(ExitStatusImp);
+pub struct ExitStatus(imp::ExitStatus);
impl ExitStatus {
/// Was termination successful? Signal termination not considered a success,
}
}
-impl AsInner<ExitStatusImp> for ExitStatus {
- fn as_inner(&self) -> &ExitStatusImp { &self.0 }
+impl AsInner<imp::ExitStatus> for ExitStatus {
+ fn as_inner(&self) -> &imp::ExitStatus { &self.0 }
}
#[stable(feature = "process", since = "1.0.0")]
unsafe { self.handle.kill() }
}
+ /// Returns the OS-assigned process identifier associated with this child.
+ #[stable(feature = "process_id", since = "1.3.0")]
+ pub fn id(&self) -> u32 {
+ self.handle.id()
+ }
+
/// Waits for the child to exit completely, returning the status that it
/// exited with. This function will continue to have the same return value
/// after it has been called at least once.
}
/// Simultaneously waits for the child to exit and collect all remaining
- /// output on the stdout/stderr handles, returning a `Output`
+ /// output on the stdout/stderr handles, returning an `Output`
/// instance.
///
/// The stdin handle to the child process, if any, will be closed
#[stable(feature = "process", since = "1.0.0")]
pub fn wait_with_output(mut self) -> io::Result<Output> {
drop(self.stdin.take());
- fn read<T: Read + Send + 'static>(stream: Option<T>) -> Receiver<io::Result<Vec<u8>>> {
- let (tx, rx) = channel();
- match stream {
- Some(stream) => {
- thread::spawn(move || {
- let mut stream = stream;
- let mut ret = Vec::new();
- let res = stream.read_to_end(&mut ret);
- tx.send(res.map(|_| ret)).unwrap();
- });
- }
- None => tx.send(Ok(Vec::new())).unwrap()
- }
- rx
+ fn read<R>(mut input: R) -> JoinHandle<io::Result<Vec<u8>>>
+ where R: Read + Send + 'static
+ {
+ thread::spawn(move || {
+ let mut ret = Vec::new();
+ input.read_to_end(&mut ret).map(|_| ret)
+ })
}
- let stdout = read(self.stdout.take());
- let stderr = read(self.stderr.take());
+ let stdout = self.stdout.take().map(read);
+ let stderr = self.stderr.take().map(read);
let status = try!(self.wait());
+ let stdout = stdout.and_then(|t| t.join().unwrap().ok());
+ let stderr = stderr.and_then(|t| t.join().unwrap().ok());
Ok(Output {
status: status,
- stdout: stdout.recv().unwrap().unwrap_or(Vec::new()),
- stderr: stderr.recv().unwrap().unwrap_or(Vec::new()),
+ stdout: stdout.unwrap_or(Vec::new()),
+ stderr: stderr.unwrap_or(Vec::new()),
})
}
}
/// to run.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn exit(code: i32) -> ! {
+ ::sys_common::cleanup();
::sys::os::exit(code)
}
use io::prelude::*;
use io::ErrorKind;
- use rt::running_on_valgrind;
use str;
use super::{Command, Output, Stdio};
fn signal_reported_right() {
use os::unix::process::ExitStatusExt;
- let p = Command::new("/bin/sh").arg("-c").arg("kill -9 $$").spawn();
- assert!(p.is_ok());
- let mut p = p.unwrap();
+ let mut p = Command::new("/bin/sh")
+ .arg("-c").arg("read a")
+ .stdin(Stdio::piped())
+ .spawn().unwrap();
+ p.kill().unwrap();
match p.wait().unwrap().signal() {
Some(9) => {},
- result => panic!("not terminated by signal 9 (instead, {:?})", result),
+ result => panic!("not terminated by signal 9 (instead, {:?})",
+ result),
}
}
assert!(status.success());
assert_eq!(output_str.trim().to_string(), "hello");
- // FIXME #7224
- if !running_on_valgrind() {
- assert_eq!(stderr, Vec::new());
- }
+ assert_eq!(stderr, Vec::new());
}
#[cfg(not(target_os="android"))]
assert!(status.success());
assert_eq!(output_str.trim().to_string(), "hello");
- // FIXME #7224
- if !running_on_valgrind() {
- assert_eq!(stderr, Vec::new());
- }
- }
-
- #[cfg(all(unix, not(target_os="android")))]
- pub fn pwd_cmd() -> Command {
- Command::new("pwd")
- }
- #[cfg(target_os="android")]
- pub fn pwd_cmd() -> Command {
- let mut cmd = Command::new("/system/bin/sh");
- cmd.arg("-c").arg("pwd");
- cmd
- }
-
- #[cfg(windows)]
- pub fn pwd_cmd() -> Command {
- let mut cmd = Command::new("cmd");
- cmd.arg("/c").arg("cd");
- cmd
+ assert_eq!(stderr, Vec::new());
}
#[cfg(all(unix, not(target_os="android")))]
#[cfg(not(target_os="android"))]
#[test]
fn test_inherit_env() {
- use std::env;
- if running_on_valgrind() { return; }
+ use env;
let result = env_cmd().output().unwrap();
let output = String::from_utf8(result.stdout).unwrap();
for (ref k, ref v) in env::vars() {
- // don't check windows magical empty-named variables
- assert!(k.is_empty() ||
+ // Windows has hidden environment variables whose names start with
+ // equals signs (`=`). Those do not show up in the output of the
+ // `set` command.
+ assert!((cfg!(windows) && k.starts_with("=")) ||
+ k.starts_with("DYLD") ||
output.contains(&format!("{}={}", *k, *v)),
"output doesn't contain `{}={}`\n{}",
k, v, output);
#[cfg(target_os="android")]
#[test]
fn test_inherit_env() {
- use std::env;
- if running_on_valgrind() { return; }
+ use env;
let mut result = env_cmd().output().unwrap();
let output = String::from_utf8(result.stdout).unwrap();