pub mod env;
pub mod path;
pub mod old_path;
+pub mod process;
pub mod rand;
pub mod time;
true
}
+
+ /// Consume the `PathBuf`, yielding its internal `OsString` storage
+ pub fn into_os_string(self) -> OsString {
+ self.inner
+ }
}
impl<'a, P: ?Sized + 'a> iter::FromIterator<&'a P> for PathBuf where P: AsPath {
--- /dev/null
+// Copyright 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.
+
+//! Working with processes.
+
+#![unstable(feature = "process", reason = "recently added via RFC 579")]
+#![allow(non_upper_case_globals)]
+
+use prelude::v1::*;
+use io::prelude::*;
+
+use ffi::AsOsStr;
+use fmt;
+use io::{self, Error, ErrorKind};
+use path::AsPath;
+use libc;
+use sync::mpsc::{channel, Receiver};
+use sys::pipe2::{self, AnonPipe};
+use sys::process2::Process as ProcessImp;
+use sys::process2::Command as CommandImp;
+use sys::process2::ExitStatus as ExitStatusImp;
+use sys_common::{AsInner, AsInnerMut};
+use thread::Thread;
+
+/// Representation of a running or exited child process.
+///
+/// This structure is used to represent and manage child processes. A child
+/// process is created via the `Command` struct, which configures the spawning
+/// process and can itself be constructed using a builder-style interface.
+///
+/// # Example
+///
+/// ```should_fail
+/// # #![feature(process)]
+///
+/// use std::process::Command;
+///
+/// let output = Command::new("/bin/cat").arg("file.txt").output().unwrap_or_else(|e| {
+/// panic!("failed to execute child: {}", e)
+/// });
+/// let contents = output.stdout;
+/// assert!(output.status.success());
+/// ```
+pub struct Child {
+ handle: ProcessImp,
+
+ /// None until wait() or wait_with_output() is called.
+ status: Option<ExitStatusImp>,
+
+ /// The handle for writing to the child's stdin, if it has been captured
+ pub stdin: Option<ChildStdin>,
+
+ /// The handle for reading from the child's stdout, if it has been captured
+ pub stdout: Option<ChildStdout>,
+
+ /// The handle for reading from the child's stderr, if it has been captured
+ pub stderr: Option<ChildStderr>,
+}
+
+/// A handle to a child procesess's stdin
+pub struct ChildStdin {
+ inner: AnonPipe
+}
+
+impl Write for ChildStdin {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.inner.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+/// A handle to a child procesess's stdout
+pub struct ChildStdout {
+ inner: AnonPipe
+}
+
+impl Read for ChildStdout {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.inner.read(buf)
+ }
+}
+
+/// A handle to a child procesess's stderr
+pub struct ChildStderr {
+ inner: AnonPipe
+}
+
+impl Read for ChildStderr {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.inner.read(buf)
+ }
+}
+
+/// 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
+/// program to be executed. Additional builder methods allow the configuration
+/// to be changed (for example, by adding arguments) prior to spawning:
+///
+/// ```
+/// # #![feature(process)]
+///
+/// use std::process::Command;
+///
+/// let output = Command::new("sh").arg("-c").arg("echo hello").output().unwrap_or_else(|e| {
+/// panic!("failed to execute process: {}", e)
+/// });
+/// let hello = output.stdout;
+/// ```
+pub struct Command {
+ inner: CommandImp,
+
+ // Details explained in the builder methods
+ stdin: Option<StdioImp>,
+ stdout: Option<StdioImp>,
+ stderr: Option<StdioImp>,
+}
+
+impl Command {
+ /// Constructs a new `Command` for launching the program at
+ /// path `program`, with the following default configuration:
+ ///
+ /// * No arguments to the program
+ /// * Inherit the current process's environment
+ /// * Inherit the current process's working directory
+ /// * Inherit stdin/stdout/stderr for `run` or `status`, but create pipes for `output`
+ ///
+ /// Builder methods are provided to change these defaults and
+ /// otherwise configure the process.
+ pub fn new<S: AsOsStr + ?Sized>(program: &S) -> Command {
+ Command {
+ inner: CommandImp::new(program.as_os_str()),
+ stdin: None,
+ stdout: None,
+ stderr: None,
+ }
+ }
+
+ /// Add an argument to pass to the program.
+ pub fn arg<S: AsOsStr + ?Sized>(&mut self, arg: &S) -> &mut Command {
+ self.inner.arg(arg.as_os_str());
+ self
+ }
+
+ /// Add multiple arguments to pass to the program.
+ pub fn args<S: AsOsStr>(&mut self, args: &[S]) -> &mut Command {
+ self.inner.args(args.iter().map(AsOsStr::as_os_str));
+ self
+ }
+
+ /// Inserts or updates an environment variable mapping.
+ ///
+ /// Note that environment variable names are case-insensitive (but case-preserving) on Windows,
+ /// and case-sensitive on all other platforms.
+ pub fn env<S: ?Sized, T: ?Sized>(&mut self, key: &S, val: &T) -> &mut Command where
+ S: AsOsStr, T: AsOsStr
+ {
+ self.inner.env(key.as_os_str(), val.as_os_str());
+ self
+ }
+
+ /// Removes an environment variable mapping.
+ pub fn env_remove<S: ?Sized + AsOsStr>(&mut self, key: &S) -> &mut Command {
+ self.inner.env_remove(key.as_os_str());
+ self
+ }
+
+ /// Clears the entire environment map for the child process.
+ pub fn env_clear(&mut self) -> &mut Command {
+ self.inner.env_clear();
+ self
+ }
+
+ /// Set the working directory for the child process.
+ pub fn current_dir<P: AsPath + ?Sized>(&mut self, dir: &P) -> &mut Command {
+ self.inner.cwd(dir.as_path().as_os_str());
+ self
+ }
+
+ /// Configuration for the child process's stdin handle (file descriptor 0).
+ /// Defaults to `CreatePipe(true, false)` so the input can be written to.
+ pub fn stdin(&mut self, cfg: Stdio) -> &mut Command {
+ self.stdin = Some(cfg.0);
+ self
+ }
+
+ /// Configuration for the child process's stdout handle (file descriptor 1).
+ /// Defaults to `CreatePipe(false, true)` so the output can be collected.
+ pub fn stdout(&mut self, cfg: Stdio) -> &mut Command {
+ self.stdout = Some(cfg.0);
+ self
+ }
+
+ /// Configuration for the child process's stderr handle (file descriptor 2).
+ /// Defaults to `CreatePipe(false, true)` so the output can be collected.
+ pub fn stderr(&mut self, cfg: Stdio) -> &mut Command {
+ self.stderr = Some(cfg.0);
+ self
+ }
+
+ fn spawn_inner(&self, default_io: StdioImp) -> io::Result<Child> {
+ let (their_stdin, our_stdin) = try!(
+ setup_io(self.stdin.as_ref().unwrap_or(&default_io), 0, true)
+ );
+ let (their_stdout, our_stdout) = try!(
+ setup_io(self.stdout.as_ref().unwrap_or(&default_io), 1, false)
+ );
+ let (their_stderr, our_stderr) = try!(
+ setup_io(self.stderr.as_ref().unwrap_or(&default_io), 2, false)
+ );
+
+ match ProcessImp::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 by the parent.
+ pub fn spawn(&mut self) -> io::Result<Child> {
+ self.spawn_inner(StdioImp::Inherit)
+ }
+
+ /// Executes the command as a child process, waiting for it to finish and
+ /// collecting all of its output.
+ ///
+ /// By default, stdin, stdout and stderr are captured (and used to
+ /// provide the resulting output).
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # #![feature(process)]
+ /// use std::process::Command;
+ ///
+ /// let output = Command::new("cat").arg("foot.txt").output().unwrap_or_else(|e| {
+ /// panic!("failed to execute process: {}", e)
+ /// });
+ ///
+ /// println!("status: {}", output.status);
+ /// println!("stdout: {}", String::from_utf8_lossy(output.stdout.as_slice()));
+ /// println!("stderr: {}", String::from_utf8_lossy(output.stderr.as_slice()));
+ /// ```
+ pub fn output(&mut self) -> io::Result<Output> {
+ self.spawn_inner(StdioImp::Capture).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.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # #![feature(process)]
+ /// use std::process::Command;
+ ///
+ /// let status = Command::new("ls").status().unwrap_or_else(|e| {
+ /// panic!("failed to execute process: {}", e)
+ /// });
+ ///
+ /// println!("process exited with: {}", status);
+ /// ```
+ pub fn status(&mut self) -> io::Result<ExitStatus> {
+ self.spawn().and_then(|mut p| p.wait())
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Debug for Command {
+ /// Format the program and arguments of a Command for display. Any
+ /// non-utf8 data is lossily converted using the utf8 replacement
+ /// character.
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ try!(write!(f, "{:?}", self.inner.program));
+ for arg in &self.inner.args {
+ try!(write!(f, " {:?}", arg));
+ }
+ Ok(())
+ }
+}
+
+impl AsInner<CommandImp> for Command {
+ fn as_inner(&self) -> &CommandImp { &self.inner }
+}
+
+impl AsInnerMut<CommandImp> for Command {
+ fn as_inner_mut(&mut self) -> &mut CommandImp { &mut self.inner }
+}
+
+fn setup_io(io: &StdioImp, fd: libc::c_int, readable: bool)
+ -> io::Result<(Option<AnonPipe>, Option<AnonPipe>)>
+{
+ use self::StdioImp::*;
+ Ok(match *io {
+ Null => {
+ (None, None)
+ }
+ Inherit => {
+ (Some(AnonPipe::from_fd(fd)), None)
+ }
+ Capture => {
+ let (reader, writer) = try!(unsafe { pipe2::anon_pipe() });
+ if readable {
+ (Some(reader), Some(writer))
+ } else {
+ (Some(writer), Some(reader))
+ }
+ }
+ })
+}
+
+/// The output of a finished process.
+#[derive(PartialEq, Eq, Clone)]
+pub struct Output {
+ /// The status (exit code) of the process.
+ pub status: ExitStatus,
+ /// The data that the process wrote to stdout.
+ pub stdout: Vec<u8>,
+ /// The data that the process wrote to stderr.
+ pub stderr: Vec<u8>,
+}
+
+/// Describes what to do with a standard io stream for a child process.
+pub struct Stdio(StdioImp);
+
+// The internal enum for stdio setup; see below for descriptions.
+#[derive(Clone)]
+enum StdioImp {
+ Capture,
+ Inherit,
+ Null,
+}
+
+impl Stdio {
+ /// A new pipe should be arranged to connect the parent and child processes.
+ pub fn capture() -> Stdio { Stdio(StdioImp::Capture) }
+
+ /// The child inherits from the corresponding parent descriptor.
+ pub fn inherit() -> Stdio { Stdio(StdioImp::Capture) }
+
+ /// This stream will be ignored. This is the equivalent of attaching the
+ /// stream to `/dev/null`
+ pub fn null() -> Stdio { Stdio(StdioImp::Capture) }
+}
+
+/// Describes the result of a process after it has terminated.
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatus(ExitStatusImp);
+
+impl ExitStatus {
+ /// Was termination successful? Signal termination not considered a success,
+ /// and success is defined as a zero exit status.
+ pub fn success(&self) -> bool {
+ self.0.success()
+ }
+
+ /// Return the exit code of the process, if any.
+ ///
+ /// On Unix, this will return `None` if the process was terminated
+ /// by a signal; `std::os::unix` provides an extension trait for
+ /// extracting the signal and other details from the `ExitStatus`.
+ pub fn code(&self) -> Option<i32> {
+ self.0.code()
+ }
+}
+
+impl AsInner<ExitStatusImp> for ExitStatus {
+ fn as_inner(&self) -> &ExitStatusImp { &self.0 }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl Child {
+ /// Forces the child to exit. This is equivalent to sending a
+ /// SIGKILL on unix platforms.
+ 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",
+ None
+ ))
+ }
+
+ unsafe { self.handle.kill() }
+ }
+
+ /// Wait 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.
+ ///
+ /// The stdin handle to the child process, if any, will be closed
+ /// before waiting. This helps avoid deadlock: it ensures that the
+ /// child does not block waiting for input from the parent, while
+ /// the parent waits for the child to exit.
+ 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))
+ }
+ }
+ }
+
+ /// Simultaneously wait for the child to exit and collect all remaining
+ /// output on the stdout/stderr handles, returning a `Output`
+ /// instance.
+ ///
+ /// The stdin handle to the child process, if any, will be closed
+ /// before waiting. This helps avoid deadlock: it ensures that the
+ /// child does not block waiting for input from the parent, while
+ /// the parent waits for the child to exit.
+ pub fn wait_with_output(mut self) -> io::Result<Output> {
+ drop(self.stdin.take());
+ fn read<T: Read + Send>(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
+ }
+ let stdout = read(self.stdout.take());
+ let stderr = read(self.stderr.take());
+ let status = try!(self.wait());
+
+ Ok(Output {
+ status: status,
+ stdout: stdout.recv().unwrap().unwrap_or(Vec::new()),
+ stderr: stderr.recv().unwrap().unwrap_or(Vec::new()),
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use io::ErrorKind;
+ use io::prelude::*;
+ use prelude::v1::{Ok, Err, range, drop, Some, None, Vec};
+ use prelude::v1::{String, Clone};
+ use prelude::v1::{SliceExt, Str, StrExt, AsSlice, ToString, GenericPath};
+ use path::Path;
+ use old_path;
+ use old_io::fs::PathExtensions;
+ use rt::running_on_valgrind;
+ use str;
+ use super::{Child, Command, Output, ExitStatus, Stdio};
+ use sync::mpsc::channel;
+ use thread::Thread;
+ use time::Duration;
+
+ // FIXME(#10380) these tests should not all be ignored on android.
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn smoke() {
+ let p = Command::new("true").spawn();
+ assert!(p.is_ok());
+ let mut p = p.unwrap();
+ assert!(p.wait().unwrap().success());
+ }
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn smoke_failure() {
+ match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
+ Ok(..) => panic!(),
+ Err(..) => {}
+ }
+ }
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn exit_reported_right() {
+ let p = Command::new("false").spawn();
+ assert!(p.is_ok());
+ let mut p = p.unwrap();
+ assert!(p.wait().unwrap().code() == Some(1));
+ drop(p.wait().clone());
+ }
+
+ #[cfg(all(unix, not(target_os="android")))]
+ #[test]
+ fn signal_reported_right() {
+ use os::unix::ExitStatusExt;
+
+ let p = Command::new("/bin/sh").arg("-c").arg("kill -1 $$").spawn();
+ assert!(p.is_ok());
+ let mut p = p.unwrap();
+ match p.wait().unwrap().signal() {
+ Some(1) => {},
+ result => panic!("not terminated by signal 1 (instead, {:?})", result),
+ }
+ }
+
+ pub fn run_output(mut cmd: Command) -> String {
+ let p = cmd.spawn();
+ assert!(p.is_ok());
+ let mut p = p.unwrap();
+ assert!(p.stdout.is_some());
+ let mut ret = String::new();
+ p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap();
+ assert!(p.wait().unwrap().success());
+ return ret;
+ }
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn stdout_works() {
+ let mut cmd = Command::new("echo");
+ cmd.arg("foobar").stdout(Stdio::capture());
+ assert_eq!(run_output(cmd), "foobar\n");
+ }
+
+ #[cfg(all(unix, not(target_os="android")))]
+ #[test]
+ fn set_current_dir_works() {
+ let mut cmd = Command::new("/bin/sh");
+ cmd.arg("-c").arg("pwd")
+ .current_dir("/")
+ .stdout(Stdio::capture());
+ assert_eq!(run_output(cmd), "/\n");
+ }
+
+ #[cfg(all(unix, not(target_os="android")))]
+ #[test]
+ fn stdin_works() {
+ let mut p = Command::new("/bin/sh")
+ .arg("-c").arg("read line; echo $line")
+ .stdin(Stdio::capture())
+ .stdout(Stdio::capture())
+ .spawn().unwrap();
+ p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap();
+ drop(p.stdin.take());
+ let mut out = String::new();
+ p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap();
+ assert!(p.wait().unwrap().success());
+ assert_eq!(out, "foobar\n");
+ }
+
+
+ #[cfg(all(unix, not(target_os="android")))]
+ #[test]
+ fn uid_works() {
+ use os::unix::*;
+ use libc;
+ let mut p = Command::new("/bin/sh")
+ .arg("-c").arg("true")
+ .uid(unsafe { libc::getuid() })
+ .gid(unsafe { libc::getgid() })
+ .spawn().unwrap();
+ assert!(p.wait().unwrap().success());
+ }
+
+ #[cfg(all(unix, not(target_os="android")))]
+ #[test]
+ fn uid_to_root_fails() {
+ use os::unix::*;
+ use libc;
+
+ // if we're already root, this isn't a valid test. Most of the bots run
+ // as non-root though (android is an exception).
+ if unsafe { libc::getuid() == 0 } { return }
+ assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err());
+ }
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn test_process_status() {
+ let mut status = Command::new("false").status().unwrap();
+ assert!(status.code() == Some(1));
+
+ status = Command::new("true").status().unwrap();
+ assert!(status.success());
+ }
+
+ #[test]
+ fn test_process_output_fail_to_start() {
+ match Command::new("/no-binary-by-this-name-should-exist").output() {
+ Err(e) => assert_eq!(e.kind(), ErrorKind::FileNotFound),
+ Ok(..) => panic!()
+ }
+ }
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn test_process_output_output() {
+ let Output {status, stdout, stderr}
+ = Command::new("echo").arg("hello").output().unwrap();
+ let output_str = str::from_utf8(stdout.as_slice()).unwrap();
+
+ assert!(status.success());
+ assert_eq!(output_str.trim().to_string(), "hello");
+ // FIXME #7224
+ if !running_on_valgrind() {
+ assert_eq!(stderr, Vec::new());
+ }
+ }
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn test_process_output_error() {
+ let Output {status, stdout, stderr}
+ = Command::new("mkdir").arg(".").output().unwrap();
+
+ assert!(status.code() == Some(1));
+ assert_eq!(stdout, Vec::new());
+ assert!(!stderr.is_empty());
+ }
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn test_finish_once() {
+ let mut prog = Command::new("false").spawn().unwrap();
+ assert!(prog.wait().unwrap().code() == Some(1));
+ }
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn test_finish_twice() {
+ let mut prog = Command::new("false").spawn().unwrap();
+ assert!(prog.wait().unwrap().code() == Some(1));
+ assert!(prog.wait().unwrap().code() == Some(1));
+ }
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn test_wait_with_output_once() {
+ let prog = Command::new("echo").arg("hello").stdout(Stdio::capture())
+ .spawn().unwrap();
+ let Output {status, stdout, stderr} = prog.wait_with_output().unwrap();
+ let output_str = str::from_utf8(stdout.as_slice()).unwrap();
+
+ 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
+ }
+
+ #[test]
+ fn test_keep_current_working_dir() {
+ use os;
+ let prog = pwd_cmd().spawn().unwrap();
+
+ let output = String::from_utf8(prog.wait_with_output().unwrap().stdout).unwrap();
+ let parent_dir = os::getcwd().unwrap();
+ let child_dir = old_path::Path::new(output.trim());
+
+ let parent_stat = parent_dir.stat().unwrap();
+ let child_stat = child_dir.stat().unwrap();
+
+ assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
+ assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
+ }
+
+ #[test]
+ fn test_change_working_directory() {
+ use os;
+ // test changing to the parent of os::getcwd() because we know
+ // the path exists (and os::getcwd() is not expected to be root)
+ let parent_dir = os::getcwd().unwrap().dir_path();
+ let result = pwd_cmd().current_dir(&parent_dir).output().unwrap();
+
+ let output = String::from_utf8(result.stdout).unwrap();
+ let child_dir = old_path::Path::new(output.trim());
+
+ let parent_stat = parent_dir.stat().unwrap();
+ let child_stat = child_dir.stat().unwrap();
+
+ assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
+ assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
+ }
+
+ #[cfg(all(unix, not(target_os="android")))]
+ pub fn env_cmd() -> Command {
+ Command::new("env")
+ }
+ #[cfg(target_os="android")]
+ pub fn env_cmd() -> Command {
+ let mut cmd = Command::new("/system/bin/sh");
+ cmd.arg("-c").arg("set");
+ cmd
+ }
+
+ #[cfg(windows)]
+ pub fn env_cmd() -> Command {
+ let mut cmd = Command::new("cmd");
+ cmd.arg("/c").arg("set");
+ cmd
+ }
+
+ #[cfg(not(target_os="android"))]
+ #[test]
+ fn test_inherit_env() {
+ use os;
+ if running_on_valgrind() { return; }
+
+ let result = env_cmd().output().unwrap();
+ let output = String::from_utf8(result.stdout).unwrap();
+
+ let r = os::env();
+ for &(ref k, ref v) in &r {
+ // don't check windows magical empty-named variables
+ assert!(k.is_empty() ||
+ output.contains(format!("{}={}", *k, *v).as_slice()),
+ "output doesn't contain `{}={}`\n{}",
+ k, v, output);
+ }
+ }
+ #[cfg(target_os="android")]
+ #[test]
+ fn test_inherit_env() {
+ use os;
+ if running_on_valgrind() { return; }
+
+ let mut result = env_cmd().output().unwrap();
+ let output = String::from_utf8(result.stdout).unwrap();
+
+ let r = os::env();
+ for &(ref k, ref v) in &r {
+ // don't check android RANDOM variables
+ if *k != "RANDOM".to_string() {
+ assert!(output.contains(format!("{}={}",
+ *k,
+ *v).as_slice()) ||
+ output.contains(format!("{}=\'{}\'",
+ *k,
+ *v).as_slice()));
+ }
+ }
+ }
+
+ #[test]
+ fn test_override_env() {
+ use env;
+
+ // In some build environments (such as chrooted Nix builds), `env` can
+ // only be found in the explicitly-provided PATH env variable, not in
+ // default places such as /bin or /usr/bin. So we need to pass through
+ // PATH to our sub-process.
+ let mut cmd = env_cmd();
+ cmd.env_clear().env("RUN_TEST_NEW_ENV", "123");
+ if let Some(p) = env::var_os("PATH") {
+ cmd.env("PATH", &p);
+ }
+ let result = cmd.output().unwrap();
+ let output = String::from_utf8_lossy(result.stdout.as_slice()).to_string();
+
+ assert!(output.contains("RUN_TEST_NEW_ENV=123"),
+ "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
+ }
+
+ #[test]
+ fn test_add_to_env() {
+ let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap();
+ let output = String::from_utf8_lossy(result.stdout.as_slice()).to_string();
+
+ assert!(output.contains("RUN_TEST_NEW_ENV=123"),
+ "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
+ }
+}
use core::str::{char_range_at_raw, next_code_point};
use core::raw::Slice as RawSlice;
+use ascii::*;
use borrow::Cow;
use cmp;
use fmt;
use slice;
use str;
use string::{String, CowString};
+use sys_common::AsInner;
use unicode::str::{Utf16Item, utf16_items};
use vec::Vec;
bytes: [u8]
}
+impl AsInner<[u8]> for Wtf8 {
+ fn as_inner(&self) -> &[u8] { &self.bytes }
+}
+
// FIXME: https://github.com/rust-lang/rust/issues/18805
impl PartialEq for Wtf8 {
fn eq(&self, other: &Wtf8) -> bool { self.bytes.eq(&other.bytes) }
}
}
+impl AsciiExt<Wtf8Buf> for Wtf8 {
+ fn is_ascii(&self) -> bool {
+ self.bytes.is_ascii()
+ }
+ fn to_ascii_uppercase(&self) -> Wtf8Buf {
+ Wtf8Buf { bytes: self.bytes.to_ascii_uppercase() }
+ }
+ fn to_ascii_lowercase(&self) -> Wtf8Buf {
+ Wtf8Buf { bytes: self.bytes.to_ascii_lowercase() }
+ }
+ fn eq_ignore_ascii_case(&self, other: &Wtf8) -> bool {
+ self.bytes.eq_ignore_ascii_case(&other.bytes)
+ }
+}
+
#[cfg(test)]
mod tests {
use prelude::v1::*;
#![unstable(feature = "std_misc")]
-use ffi::{OsStr, OsString};
+use prelude::v1::*;
+
+use ffi::{CString, OsStr, OsString};
use fs::{self, Permissions, OpenOptions};
use net;
-use libc;
use mem;
+use process;
+use sys;
use sys::os_str::Buf;
use sys_common::{AsInner, AsInnerMut, IntoInner, FromInner};
-use vec::Vec;
+use libc::{self, gid_t, uid_t};
use old_io;
fn as_raw_fd(&self) -> Fd { *self.as_inner().socket().as_inner() }
}
-// Unix-specific extensions to `OsString`.
+////////////////////////////////////////////////////////////////////////////////
+// OsString and OsStr
+////////////////////////////////////////////////////////////////////////////////
+
+/// Unix-specific extensions to `OsString`.
pub trait OsStringExt {
/// Create an `OsString` from a byte vector.
fn from_vec(vec: Vec<u8>) -> Self;
}
}
-// Unix-specific extensions to `OsStr`.
+/// Unix-specific extensions to `OsStr`.
pub trait OsStrExt {
- fn from_byte_slice(slice: &[u8]) -> &OsStr;
- fn as_byte_slice(&self) -> &[u8];
+ fn from_bytes(slice: &[u8]) -> &OsStr;
+
+ /// Get the underlying byte view of the `OsStr` slice.
+ fn as_bytes(&self) -> &[u8];
+
+ /// Convert the `OsStr` slice into a `CString`.
+ fn to_cstring(&self) -> CString;
}
impl OsStrExt for OsStr {
- fn from_byte_slice(slice: &[u8]) -> &OsStr {
+ fn from_bytes(slice: &[u8]) -> &OsStr {
unsafe { mem::transmute(slice) }
}
- fn as_byte_slice(&self) -> &[u8] {
+ fn as_bytes(&self) -> &[u8] {
&self.as_inner().inner
}
+
+ fn to_cstring(&self) -> CString {
+ CString::from_slice(self.as_bytes())
+ }
}
// Unix-specific extensions to `Permissions`
}
}
+////////////////////////////////////////////////////////////////////////////////
+// Process and Command
+////////////////////////////////////////////////////////////////////////////////
+
+/// Unix-specific extensions to the `std::process::Command` builder
+pub trait CommandExt {
+ /// Sets the child process's user id. This translates to a
+ /// `setuid` call in the child process. Failure in the `setuid`
+ /// call will cause the spawn to fail.
+ fn uid(&mut self, id: uid_t) -> &mut process::Command;
+
+ /// Similar to `uid`, but sets the group id of the child process. This has
+ /// the same semantics as the `uid` field.
+ fn gid(&mut self, id: gid_t) -> &mut process::Command;
+}
+
+impl CommandExt for process::Command {
+ fn uid(&mut self, id: uid_t) -> &mut process::Command {
+ self.as_inner_mut().uid = Some(id);
+ self
+ }
+
+ fn gid(&mut self, id: gid_t) -> &mut process::Command {
+ self.as_inner_mut().gid = Some(id);
+ self
+ }
+}
+
+/// Unix-specific extensions to `std::process::ExitStatus`
+pub trait ExitStatusExt {
+ /// If the process was terminated by a signal, returns that signal.
+ fn signal(&self) -> Option<i32>;
+}
+
+impl ExitStatusExt for process::ExitStatus {
+ fn signal(&self) -> Option<i32> {
+ match *self.as_inner() {
+ sys::process2::ExitStatus::Signal(s) => Some(s),
+ _ => None
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Prelude
+////////////////////////////////////////////////////////////////////////////////
+
/// A prelude for conveniently writing platform-specific code.
///
/// Includes all extension traits, and some important type definitions.
pub mod prelude {
#[doc(no_inline)]
- pub use super::{Fd, AsRawFd, OsStrExt, OsStringExt, PermissionsExt};
+ pub use super::{Fd, AsRawFd, OsStrExt, OsStringExt, PermissionsExt, CommandExt, ExitStatusExt};
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
- self.root.join(<OsStr as OsStrExt>::from_byte_slice(self.name_bytes()))
+ self.root.join(<OsStr as OsStrExt>::from_bytes(self.name_bytes()))
}
fn name_bytes(&self) -> &[u8] {
}
fn cstr(path: &Path) -> CString {
- CString::from_slice(path.as_os_str().as_byte_slice())
+ CString::from_slice(path.as_os_str().as_bytes())
}
pub fn mkdir(p: &Path) -> io::Result<()> {
pub mod os;
pub mod os_str;
pub mod pipe;
+pub mod pipe2;
pub mod process;
+pub mod process2;
pub mod rwlock;
pub mod stack_overflow;
pub mod sync;
use iter;
use libc::{self, c_int, c_char, c_void};
use mem;
+use io;
use old_io::{IoResult, IoError, fs};
use ptr;
use slice;
use str;
use sys::c;
+use sys::fd;
use sys::fs::FileDesc;
use vec;
pub fn split_paths<'a>(unparsed: &'a OsStr) -> SplitPaths<'a> {
fn is_colon(b: &u8) -> bool { *b == b':' }
- let unparsed = unparsed.as_byte_slice();
+ let unparsed = unparsed.as_bytes();
SplitPaths {
iter: unparsed.split(is_colon as fn(&u8) -> bool)
.map(Path::new as fn(&'a [u8]) -> Path)
let sep = b':';
for (i, path) in paths.enumerate() {
- let path = path.as_os_str().as_byte_slice();
+ let path = path.as_os_str().as_bytes();
if i > 0 { joined.push(sep) }
if path.contains(&sep) {
return Err(JoinPathsError)
pub fn getenv(k: &OsStr) -> Option<OsString> {
unsafe {
- let s = CString::from_slice(k.as_byte_slice());
+ let s = CString::from_slice(k.as_bytes());
let s = libc::getenv(s.as_ptr()) as *const _;
if s.is_null() {
None
pub fn setenv(k: &OsStr, v: &OsStr) {
unsafe {
- let k = CString::from_slice(k.as_byte_slice());
- let v = CString::from_slice(v.as_byte_slice());
+ let k = CString::from_slice(k.as_bytes());
+ let v = CString::from_slice(v.as_bytes());
if libc::funcs::posix01::unistd::setenv(k.as_ptr(), v.as_ptr(), 1) != 0 {
panic!("failed setenv: {}", IoError::last_error());
}
pub fn unsetenv(n: &OsStr) {
unsafe {
- let nbuf = CString::from_slice(n.as_byte_slice());
+ let nbuf = CString::from_slice(n.as_bytes());
if libc::funcs::posix01::unistd::unsetenv(nbuf.as_ptr()) != 0 {
panic!("failed unsetenv: {}", IoError::last_error());
}
--- /dev/null
+// Copyright 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 prelude::v1::*;
+
+use sys::fd::FileDesc;
+use io;
+use libc;
+
+////////////////////////////////////////////////////////////////////////////////
+// Anonymous pipes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct AnonPipe(FileDesc);
+
+pub unsafe fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
+ let mut fds = [0; 2];
+ if libc::pipe(fds.as_mut_ptr()) == 0 {
+ Ok((AnonPipe::from_fd(fds[0]),
+ AnonPipe::from_fd(fds[1])))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+}
+
+impl AnonPipe {
+ pub fn from_fd(fd: libc::c_int) -> AnonPipe {
+ AnonPipe(FileDesc::new(fd))
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn raw(&self) -> libc::c_int {
+ self.0.raw()
+ }
+}
helper_init! { static HELPER: Helper<Req> }
+/// Unix-specific extensions to the Command builder
+pub struct CommandExt {
+ uid: Option<u32>,
+ gid: Option<u32>,
+}
+
/// The unique id of the process (this should never be negative).
pub struct Process {
pub pid: pid_t
--- /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 prelude::v1::*;
+
+use collections::HashMap;
+use collections::hash_map::Hasher;
+use env;
+use ffi::{OsString, OsStr, CString};
+use fmt;
+use hash::Hash;
+use io::{self, Error, ErrorKind};
+use libc::{self, pid_t, c_void, c_int, gid_t, uid_t};
+use mem;
+use old_io;
+use os;
+use os::unix::OsStrExt;
+use ptr;
+use sync::mpsc::{channel, Sender, Receiver};
+use sys::pipe2::AnonPipe;
+use sys::{self, retry, c, wouldblock, set_nonblocking, ms_to_timeval, cvt};
+use sys_common::AsInner;
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+#[derive(Clone)]
+pub struct Command {
+ pub program: CString,
+ pub args: Vec<CString>,
+ pub env: Option<HashMap<OsString, OsString>>,
+ pub cwd: Option<CString>,
+ pub uid: Option<uid_t>,
+ pub gid: Option<gid_t>,
+ pub detach: bool, // not currently exposed in std::process
+}
+
+impl Command {
+ pub fn new(program: &OsStr) -> Command {
+ Command {
+ program: program.to_cstring(),
+ args: Vec::new(),
+ env: None,
+ cwd: None,
+ uid: None,
+ gid: None,
+ detach: false,
+ }
+ }
+
+ pub fn arg(&mut self, arg: &OsStr) {
+ self.args.push(arg.to_cstring())
+ }
+ pub fn args<'a, I: Iterator<Item = &'a OsStr>>(&mut self, args: I) {
+ self.args.extend(args.map(OsStrExt::to_cstring))
+ }
+ fn init_env_map(&mut self) {
+ if self.env.is_none() {
+ self.env = Some(env::vars_os().collect());
+ }
+ }
+ pub fn env(&mut self, key: &OsStr, val: &OsStr) {
+ self.init_env_map();
+ self.env.as_mut().unwrap().insert(key.to_os_string(), val.to_os_string());
+ }
+ pub fn env_remove(&mut self, key: &OsStr) {
+ self.init_env_map();
+ self.env.as_mut().unwrap().remove(&key.to_os_string());
+ }
+ pub fn env_clear(&mut self) {
+ self.env = Some(HashMap::new())
+ }
+ pub fn cwd(&mut self, dir: &OsStr) {
+ self.cwd = Some(dir.to_cstring())
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+/// Unix exit statuses
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub enum ExitStatus {
+ /// Normal termination with an exit code.
+ Code(i32),
+
+ /// Termination by signal, with the signal number.
+ ///
+ /// Never generated on Windows.
+ Signal(i32),
+}
+
+impl ExitStatus {
+ pub fn success(&self) -> bool {
+ *self == ExitStatus::Code(0)
+ }
+ pub fn code(&self) -> Option<i32> {
+ match *self {
+ ExitStatus::Code(c) => Some(c),
+ _ => None
+ }
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ ExitStatus::Code(code) => write!(f, "exit code: {}", code),
+ ExitStatus::Signal(code) => write!(f, "signal: {}", code),
+ }
+ }
+}
+
+/// The unique id of the process (this should never be negative).
+pub struct Process {
+ pid: pid_t
+}
+
+const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
+
+impl Process {
+ pub fn id(&self) -> pid_t {
+ self.pid
+ }
+
+ pub unsafe fn kill(&self) -> io::Result<()> {
+ try!(cvt(libc::funcs::posix88::signal::kill(self.pid, libc::SIGKILL)));
+ Ok(())
+ }
+
+ pub fn spawn(cfg: &Command,
+ in_fd: Option<AnonPipe>, out_fd: Option<AnonPipe>, err_fd: Option<AnonPipe>)
+ -> io::Result<Process>
+ {
+ use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
+ use libc::funcs::bsd44::getdtablesize;
+
+ mod rustrt {
+ extern {
+ pub fn rust_unset_sigprocmask();
+ }
+ }
+
+ unsafe fn set_cloexec(fd: c_int) {
+ let ret = c::ioctl(fd, c::FIOCLEX);
+ assert_eq!(ret, 0);
+ }
+
+ let dirp = cfg.cwd.as_ref().map(|c| c.as_ptr()).unwrap_or(ptr::null());
+
+ with_envp(cfg.env.as_ref(), |envp: *const c_void| {
+ with_argv(&cfg.program, &cfg.args, |argv: *const *const libc::c_char| unsafe {
+ let (input, mut output) = try!(sys::pipe2::anon_pipe());
+
+ // We may use this in the child, so perform allocations before the
+ // fork
+ let devnull = b"/dev/null\0";
+
+ set_cloexec(output.raw());
+
+ let pid = fork();
+ if pid < 0 {
+ return Err(Error::last_os_error())
+ } else if pid > 0 {
+ #[inline]
+ 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
+ }
+
+ let p = Process{ pid: pid };
+ drop(output);
+ let mut bytes = [0; 8];
+
+ // loop to handle EINTER
+ loop {
+ match input.read(&mut bytes) {
+ 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_os_error(errno))
+ }
+ Ok(0) => return Ok(p),
+ 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")
+ }
+ }
+ }
+ }
+
+ // 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)
+ let _ = libc::close(input.raw());
+
+ 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
+ assert!(output.write(&bytes).is_ok());
+ unsafe { libc::_exit(1) }
+ }
+
+ rustrt::rust_unset_sigprocmask();
+
+ // If a stdio file descriptor is set to be ignored, we don't
+ // actually close it, but rather 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.
+ let setup = |&: src: Option<AnonPipe>, dst: c_int| {
+ let src = match src {
+ None => {
+ let flags = if dst == libc::STDIN_FILENO {
+ libc::O_RDONLY
+ } else {
+ libc::O_RDWR
+ };
+ libc::open(devnull.as_ptr() as *const _, flags, 0)
+ }
+ Some(obj) => {
+ let fd = obj.raw();
+ // Leak the memory and the file descriptor. We're in the
+ // child now an all our resources are going to be
+ // cleaned up very soon
+ mem::forget(obj);
+ fd
+ }
+ };
+ src != -1 && retry(|| dup2(src, dst)) != -1
+ };
+
+ 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) }
+
+ // close all other fds
+ for fd in (3..getdtablesize()).rev() {
+ if fd != output.raw() {
+ let _ = close(fd as c_int);
+ }
+ }
+
+ match cfg.gid {
+ Some(u) => {
+ if libc::setgid(u as libc::gid_t) != 0 {
+ fail(&mut output);
+ }
+ }
+ None => {}
+ }
+ match cfg.uid {
+ Some(u) => {
+ // 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.
+ extern {
+ fn setgroups(ngroups: libc::c_int,
+ ptr: *const libc::c_void) -> libc::c_int;
+ }
+ let _ = setgroups(0, ptr::null());
+
+ if libc::setuid(u as libc::uid_t) != 0 {
+ fail(&mut output);
+ }
+ }
+ None => {}
+ }
+ if cfg.detach {
+ // 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() && chdir(dirp) == -1 {
+ fail(&mut output);
+ }
+ if !envp.is_null() {
+ *sys::os::environ() = envp as *const _;
+ }
+ let _ = execvp(*argv, argv as *mut _);
+ fail(&mut output);
+ })
+ })
+ }
+
+ pub fn wait(&self) -> io::Result<ExitStatus> {
+ let mut status = 0 as c_int;
+ try!(cvt(retry(|| unsafe { c::waitpid(self.pid, &mut status, 0) })));
+ Ok(translate_status(status))
+ }
+
+ pub fn try_wait(&self) -> Option<ExitStatus> {
+ let mut status = 0 as c_int;
+ match retry(|| unsafe {
+ c::waitpid(self.pid, &mut status, c::WNOHANG)
+ }) {
+ n if n == self.pid => Some(translate_status(status)),
+ 0 => None,
+ n => panic!("unknown waitpid error `{:?}`: {:?}", n,
+ super::last_error()),
+ }
+ }
+}
+
+fn with_argv<T,F>(prog: &CString, args: &[CString], cb: F) -> T
+ where F : FnOnce(*const *const libc::c_char) -> T
+{
+ let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1);
+
+ // Convert the CStrings into an array of pointers. Note: the
+ // lifetime of the various CStrings involved is guaranteed to be
+ // larger than the lifetime of our invocation of cb, but this is
+ // technically unsafe as the callback could leak these pointers
+ // out of our scope.
+ 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());
+
+ cb(ptrs.as_ptr())
+}
+
+fn with_envp<T, F>(env: Option<&HashMap<OsString, OsString>>, cb: F) -> T
+ where F : FnOnce(*const c_void) -> T
+{
+ // On posixy systems we can pass a char** for envp, which is a
+ // null-terminated array of "k=v\0" strings. Since we must create
+ // these strings locally, yet expose a raw pointer to them, we
+ // create a temporary vector to own the CStrings that outlives the
+ // call to cb.
+ match env {
+ Some(env) => {
+ let mut tmps = Vec::with_capacity(env.len());
+
+ for pair in env {
+ let mut kv = Vec::new();
+ kv.push_all(pair.0.as_bytes());
+ kv.push('=' as u8);
+ kv.push_all(pair.1.as_bytes());
+ kv.push(0); // terminating null
+ tmps.push(kv);
+ }
+
+ // As with `with_argv`, this is unsafe, since cb could leak the pointers.
+ 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());
+
+ cb(ptrs.as_ptr() as *const c_void)
+ }
+ _ => cb(ptr::null())
+ }
+}
+
+fn translate_status(status: c_int) -> ExitStatus {
+ #![allow(non_snake_case)]
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ mod 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 = "openbsd"))]
+ mod 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 }
+ }
+
+ if imp::WIFEXITED(status) {
+ ExitStatus::Code(imp::WEXITSTATUS(status))
+ } else {
+ ExitStatus::Signal(imp::WTERMSIG(status))
+ }
+}
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
- let mut read = 0;
- try!(cvt(unsafe {
- libc::ReadFile(self.handle.raw(),
- buf.as_ptr() as libc::LPVOID,
- buf.len() as libc::DWORD,
- &mut read,
- ptr::null_mut())
- }));
- Ok(read as usize)
+ self.handle.read(buf)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
- let mut amt = 0;
- try!(cvt(unsafe {
- libc::WriteFile(self.handle.raw(),
- buf.as_ptr() as libc::LPVOID,
- buf.len() as libc::DWORD,
- &mut amt,
- ptr::null_mut())
- }));
- Ok(amt as usize)
+ self.handle.write(buf)
}
pub fn flush(&self) -> io::Result<()> { Ok(()) }
use prelude::v1::*;
use libc::{self, HANDLE};
+use io;
+use io::ErrorKind;
+use ptr;
+use sys::cvt;
pub struct Handle(HANDLE);
pub fn new(handle: HANDLE) -> Handle {
Handle(handle)
}
+
pub fn raw(&self) -> HANDLE { self.0 }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ read(self.0, buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ write(self.0, buf)
+ }
}
impl Drop for Handle {
}
}
+
+pub fn read(h: HANDLE, buf: &mut [u8]) -> io::Result<usize> {
+ let mut read = 0;
+ let res = cvt(unsafe {
+ libc::ReadFile(h, buf.as_ptr() as libc::LPVOID,
+ buf.len() as libc::DWORD, &mut read,
+ ptr::null_mut())
+ });
+
+ match res {
+ Ok(_) => Ok(read as usize),
+
+ // The special treatment of BrokenPipe is to deal with Windows
+ // pipe semantics, which yields this error when *reading* from
+ // a pipe after the other end has closed; we interpret that as
+ // EOF on the pipe.
+ Err(ref e) if e.kind() == ErrorKind::BrokenPipe => Ok(0),
+
+ Err(e) => Err(e)
+ }
+}
+
+pub fn write(h: HANDLE, buf: &[u8]) -> io::Result<usize> {
+ let mut amt = 0;
+ try!(cvt(unsafe {
+ libc::WriteFile(h, buf.as_ptr() as libc::LPVOID,
+ buf.len() as libc::DWORD, &mut amt,
+ ptr::null_mut())
+ }));
+ Ok(amt as usize)
+}
pub mod os;
pub mod os_str;
pub mod pipe;
+pub mod pipe2;
pub mod process;
+pub mod process2;
pub mod rwlock;
pub mod stack_overflow;
pub mod sync;
--- /dev/null
+// Copyright 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 prelude::v1::*;
+
+use sys::handle;
+use io;
+use libc::{self, c_int, HANDLE};
+
+////////////////////////////////////////////////////////////////////////////////
+// Anonymous pipes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct AnonPipe {
+ fd: c_int
+}
+
+pub unsafe fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
+ // Windows pipes work subtly differently than unix pipes, and their
+ // inheritance has to be handled in a different way that I do not
+ // fully understand. Here we explicitly make the pipe non-inheritable,
+ // which means to pass it to a subprocess they need to be duplicated
+ // first, as in std::run.
+ let mut fds = [0; 2];
+ match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
+ (libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
+ 0 => {
+ assert!(fds[0] != -1 && fds[0] != 0);
+ assert!(fds[1] != -1 && fds[1] != 0);
+
+ Ok((AnonPipe::from_fd(fds[0]), AnonPipe::from_fd(fds[1])))
+ }
+ _ => Err(io::Error::last_os_error()),
+ }
+}
+
+impl AnonPipe {
+ pub fn from_fd(fd: libc::c_int) -> AnonPipe {
+ AnonPipe { fd: fd }
+ }
+
+ pub fn raw(&self) -> HANDLE {
+ unsafe { libc::get_osfhandle(self.fd) as libc::HANDLE }
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ handle::read(self.raw(), buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ handle::write(self.raw(), buf)
+ }
+}
+
+impl Drop for AnonPipe {
+ fn drop(&mut self) {
+ // closing stdio file handles makes no sense, so never do it. Also, note
+ // that errors are ignored when closing a file descriptor. The reason
+ // for this is that if an error occurs we don't actually know if the
+ // file descriptor was closed or not, and if we retried (for something
+ // like EINTR), we might close another valid file descriptor (opened
+ // after we closed ours.
+ if self.fd > libc::STDERR_FILENO {
+ let n = unsafe { libc::close(self.fd) };
+ if n != 0 {
+ println!("error {} when closing file descriptor {}", n, self.fd);
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright 2012-2014 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 prelude::v1::*;
+
+use ascii::*;
+use collections::HashMap;
+use collections;
+use env;
+use ffi::{OsString, OsStr};
+use fmt;
+use io::{self, Error};
+use libc::{self, c_void};
+use old_io::fs;
+use old_path;
+use os::windows::OsStrExt;
+use ptr;
+use sync::{StaticMutex, MUTEX_INIT};
+use sys::pipe2::AnonPipe;
+use sys::{self, cvt};
+use sys::handle::Handle;
+use sys_common::{AsInner, FromInner};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+fn mk_key(s: &OsStr) -> OsString {
+ FromInner::from_inner(sys::os_str::Buf {
+ inner: s.as_inner().inner.to_ascii_uppercase()
+ })
+}
+
+#[derive(Clone)]
+pub struct Command {
+ pub program: OsString,
+ pub args: Vec<OsString>,
+ pub env: Option<HashMap<OsString, OsString>>,
+ pub cwd: Option<OsString>,
+ pub detach: bool, // not currently exposed in std::process
+}
+
+impl Command {
+ pub fn new(program: &OsStr) -> Command {
+ Command {
+ program: program.to_os_string(),
+ args: Vec::new(),
+ env: None,
+ cwd: None,
+ detach: false,
+ }
+ }
+
+ 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)| {
+ (mk_key(&key), val)
+ }).collect());
+ }
+ }
+ pub fn env(&mut self, key: &OsStr, val: &OsStr) {
+ self.init_env_map();
+ self.env.as_mut().unwrap().insert(mk_key(key), val.to_os_string());
+ }
+ pub fn env_remove(&mut self, key: &OsStr) {
+ self.init_env_map();
+ self.env.as_mut().unwrap().remove(&mk_key(key));
+ }
+ pub fn env_clear(&mut self) {
+ self.env = Some(HashMap::new())
+ }
+ pub fn cwd(&mut self, dir: &OsStr) {
+ self.cwd = Some(dir.to_os_string())
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+// `CreateProcess` is racy!
+// http://support.microsoft.com/kb/315939
+static CREATE_PROCESS_LOCK: StaticMutex = MUTEX_INIT;
+
+/// A value representing a child process.
+///
+/// The lifetime of this value is linked to the lifetime of the actual
+/// process - the Process destructor calls self.finish() which waits
+/// for the process to terminate.
+pub struct Process {
+ /// A HANDLE to the process, which will prevent the pid being
+ /// re-used until the handle is closed.
+ handle: Handle,
+}
+
+impl Process {
+ #[allow(deprecated)]
+ pub fn spawn(cfg: &Command,
+ in_fd: Option<AnonPipe>, out_fd: Option<AnonPipe>, err_fd: Option<AnonPipe>)
+ -> io::Result<Process>
+ {
+ use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
+ use libc::consts::os::extra::{
+ TRUE, FALSE,
+ STARTF_USESTDHANDLES,
+ INVALID_HANDLE_VALUE,
+ DUPLICATE_SAME_ACCESS
+ };
+ use libc::funcs::extra::kernel32::{
+ GetCurrentProcess,
+ DuplicateHandle,
+ CloseHandle,
+ CreateProcessW
+ };
+
+ use env::split_paths;
+ use mem;
+ use iter::IteratorExt;
+ use str::StrExt;
+
+ // 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::from_str("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);
+ // FIXME: update with new fs module once it lands
+ if fs::stat(&old_path::Path::new(&path)).is_ok() {
+ return Some(OsString::from_str(path.as_str().unwrap()))
+ }
+ }
+ break
+ }
+ None
+ });
+
+ unsafe {
+ let mut si = zeroed_startupinfo();
+ si.cb = mem::size_of::<STARTUPINFO>() as DWORD;
+ si.dwFlags = STARTF_USESTDHANDLES;
+
+ let cur_proc = GetCurrentProcess();
+
+ // 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.
+ let set_fd = |&: fd: &Option<AnonPipe>, slot: &mut HANDLE,
+ is_stdin: bool| {
+ match *fd {
+ None => {
+ let access = if is_stdin {
+ libc::FILE_GENERIC_READ
+ } else {
+ libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES
+ };
+ let size = mem::size_of::<libc::SECURITY_ATTRIBUTES>();
+ let mut sa = libc::SECURITY_ATTRIBUTES {
+ nLength: size as libc::DWORD,
+ lpSecurityDescriptor: ptr::null_mut(),
+ bInheritHandle: 1,
+ };
+ let mut filename: Vec<u16> = "NUL".utf16_units().collect();
+ filename.push(0);
+ *slot = libc::CreateFileW(filename.as_ptr(),
+ access,
+ libc::FILE_SHARE_READ |
+ libc::FILE_SHARE_WRITE,
+ &mut sa,
+ libc::OPEN_EXISTING,
+ 0,
+ ptr::null_mut());
+ if *slot == INVALID_HANDLE_VALUE {
+ return Err(Error::last_os_error())
+ }
+ }
+ Some(ref pipe) => {
+ let orig = pipe.raw();
+ if orig == INVALID_HANDLE_VALUE {
+ return Err(Error::last_os_error())
+ }
+ if DuplicateHandle(cur_proc, orig, cur_proc, slot,
+ 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
+ return Err(Error::last_os_error())
+ }
+ }
+ }
+ Ok(())
+ };
+
+ try!(set_fd(&in_fd, &mut si.hStdInput, true));
+ try!(set_fd(&out_fd, &mut si.hStdOutput, false));
+ try!(set_fd(&err_fd, &mut si.hStdError, false));
+
+ let mut cmd_str = make_command_line(program.as_ref().unwrap_or(&cfg.program),
+ &cfg.args);
+ cmd_str.push(0); // add null terminator
+
+ let mut pi = zeroed_process_information();
+ let mut create_err = None;
+
+ // stolen from the libuv code.
+ let mut flags = libc::CREATE_UNICODE_ENVIRONMENT;
+ if cfg.detach {
+ flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP;
+ }
+
+ with_envp(cfg.env.as_ref(), |envp| {
+ with_dirp(cfg.cwd.as_ref(), |dirp| {
+ let _lock = CREATE_PROCESS_LOCK.lock().unwrap();
+ let created = CreateProcessW(ptr::null(),
+ cmd_str.as_mut_ptr(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ TRUE,
+ flags, envp, dirp,
+ &mut si, &mut pi);
+ if created == FALSE {
+ create_err = Some(Error::last_os_error());
+ }
+ })
+ });
+
+ assert!(CloseHandle(si.hStdInput) != 0);
+ assert!(CloseHandle(si.hStdOutput) != 0);
+ assert!(CloseHandle(si.hStdError) != 0);
+
+ match create_err {
+ Some(err) => return Err(err),
+ None => {}
+ }
+
+ // 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. We don't close the process handle however
+ // because std::we want the process id to stay valid at least until the
+ // calling code closes the process handle.
+ assert!(CloseHandle(pi.hThread) != 0);
+
+ Ok(Process {
+ handle: Handle::new(pi.hProcess)
+ })
+ }
+ }
+
+ pub unsafe fn kill(&self) -> io::Result<()> {
+ try!(cvt(libc::TerminateProcess(self.handle.raw(), 1)));
+ Ok(())
+ }
+
+ pub fn wait(&self) -> io::Result<ExitStatus> {
+ use libc::consts::os::extra::{
+ FALSE,
+ STILL_ACTIVE,
+ INFINITE,
+ WAIT_OBJECT_0,
+ };
+ use libc::funcs::extra::kernel32::{
+ GetExitCodeProcess,
+ WaitForSingleObject,
+ };
+
+ unsafe {
+ loop {
+ let mut status = 0;
+ if GetExitCodeProcess(self.handle.raw(), &mut status) == FALSE {
+ let err = Err(Error::last_os_error());
+ return err;
+ }
+ if status != STILL_ACTIVE {
+ return Ok(ExitStatus(status as i32));
+ }
+ match WaitForSingleObject(self.handle.raw(), INFINITE) {
+ WAIT_OBJECT_0 => {}
+ _ => {
+ let err = Err(Error::last_os_error());
+ return err
+ }
+ }
+ }
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatus(i32);
+
+impl ExitStatus {
+ pub fn success(&self) -> bool {
+ self.0 == 0
+ }
+ pub fn code(&self) -> Option<i32> {
+ Some(self.0)
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "exit code: {}", self.0)
+ }
+}
+
+fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
+ libc::types::os::arch::extra::STARTUPINFO {
+ cb: 0,
+ lpReserved: ptr::null_mut(),
+ lpDesktop: ptr::null_mut(),
+ lpTitle: ptr::null_mut(),
+ dwX: 0,
+ dwY: 0,
+ dwXSize: 0,
+ dwYSize: 0,
+ dwXCountChars: 0,
+ dwYCountCharts: 0,
+ dwFillAttribute: 0,
+ dwFlags: 0,
+ wShowWindow: 0,
+ cbReserved2: 0,
+ lpReserved2: ptr::null_mut(),
+ hStdInput: libc::INVALID_HANDLE_VALUE,
+ hStdOutput: libc::INVALID_HANDLE_VALUE,
+ hStdError: libc::INVALID_HANDLE_VALUE,
+ }
+}
+
+fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
+ libc::types::os::arch::extra::PROCESS_INFORMATION {
+ hProcess: ptr::null_mut(),
+ hThread: ptr::null_mut(),
+ dwProcessId: 0,
+ dwThreadId: 0
+ }
+}
+
+// Produces a wide string *without terminating null*
+fn make_command_line(prog: &OsStr, args: &[OsString]) -> Vec<u16> {
+ let mut cmd: Vec<u16> = Vec::new();
+ append_arg(&mut cmd, prog);
+ for arg in args {
+ cmd.push(' ' as u16);
+ append_arg(&mut cmd, arg);
+ }
+ return cmd;
+
+ fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr) {
+ // If an argument has 0 characters then we need to quote it to ensure
+ // that it actually gets passed through on the command line or otherwise
+ // it will be dropped entirely when parsed on the other end.
+ let arg_bytes = &arg.as_inner().inner.as_inner();
+ let quote = arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t')
+ || arg_bytes.len() == 0;
+ if quote {
+ cmd.push('"' as u16);
+ }
+
+ let mut iter = arg.encode_wide();
+ while let Some(x) = iter.next() {
+ if x == '"' as u16 {
+ // escape quotes
+ cmd.push('\\' as u16);
+ cmd.push('"' as u16);
+ } else if x == '\\' as u16 {
+ // is this a run of backslashes followed by a " ?
+ if iter.clone().skip_while(|y| *y == '\\' as u16).next() == Some('"' as u16) {
+ // Double it ... NOTE: this behavior is being
+ // preserved as it's been part of Rust for a long
+ // time, but no one seems to know exactly why this
+ // is the right thing to do.
+ cmd.push('\\' as u16);
+ cmd.push('\\' as u16);
+ } else {
+ // Push it through unescaped
+ cmd.push('\\' as u16);
+ }
+ } else {
+ cmd.push(x)
+ }
+ }
+
+ if quote {
+ cmd.push('"' as u16);
+ }
+ }
+}
+
+fn with_envp<F, T>(env: Option<&collections::HashMap<OsString, OsString>>, cb: F) -> T
+ where F: FnOnce(*mut c_void) -> T,
+{
+ // On Windows we pass an "environment block" which is not a char**, but
+ // rather a concatenation of null-terminated k=v\0 sequences, with a final
+ // \0 to terminate.
+ match env {
+ Some(env) => {
+ let mut blk = Vec::new();
+
+ for pair in env {
+ blk.extend(pair.0.encode_wide());
+ blk.push('=' as u16);
+ blk.extend(pair.1.encode_wide());
+ blk.push(0);
+ }
+ blk.push(0);
+ cb(blk.as_mut_ptr() as *mut c_void)
+ }
+ _ => cb(ptr::null_mut())
+ }
+}
+
+fn with_dirp<T, F>(d: Option<&OsString>, cb: F) -> T where
+ F: FnOnce(*const u16) -> T,
+{
+ match d {
+ Some(dir) => {
+ let mut dir_str: Vec<u16> = dir.encode_wide().collect();
+ dir_str.push(0);
+ cb(dir_str.as_ptr())
+ },
+ None => cb(ptr::null())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use prelude::v1::*;
+ use str;
+ use ffi::{OsStr, OsString};
+ use super::make_command_line;
+
+ #[test]
+ fn test_make_command_line() {
+ fn test_wrapper(prog: &str, args: &[&str]) -> String {
+ String::from_utf16(
+ &make_command_line(OsStr::from_str(prog),
+ args.iter()
+ .map(|a| OsString::from_str(a))
+ .collect::<Vec<OsString>>()
+ .as_slice())).unwrap()
+ }
+
+ assert_eq!(
+ test_wrapper("prog", &["aaa", "bbb", "ccc"]),
+ "prog aaa bbb ccc"
+ );
+
+ assert_eq!(
+ test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
+ "\"C:\\Program Files\\blah\\blah.exe\" aaa"
+ );
+ assert_eq!(
+ test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
+ "\"C:\\Program Files\\test\" aa\\\"bb"
+ );
+ assert_eq!(
+ test_wrapper("echo", &["a b c"]),
+ "echo \"a b c\""
+ );
+ assert_eq!(
+ test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
+ "\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}"
+ );
+ }
+}