1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Bindings for executing child processes
13 #![allow(experimental)]
19 use io::{IoResult, IoError};
23 use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo};
26 use collections::HashMap;
30 use std::hash::sip::SipState;
32 /// Signal a process to exit, without forcibly killing it. Corresponds to
33 /// SIGTERM on unix platforms.
34 #[cfg(windows)] pub static PleaseExitSignal: int = 15;
35 /// Signal a process to exit immediately, forcibly killing it. Corresponds to
36 /// SIGKILL on unix platforms.
37 #[cfg(windows)] pub static MustDieSignal: int = 9;
38 /// Signal a process to exit, without forcibly killing it. Corresponds to
39 /// SIGTERM on unix platforms.
40 #[cfg(not(windows))] pub static PleaseExitSignal: int = libc::SIGTERM as int;
41 /// Signal a process to exit immediately, forcibly killing it. Corresponds to
42 /// SIGKILL on unix platforms.
43 #[cfg(not(windows))] pub static MustDieSignal: int = libc::SIGKILL as int;
45 /// Representation of a running or exited child process.
47 /// This structure is used to represent and manage child processes. A child
48 /// process is created via the `Command` struct, which configures the spawning
49 /// process and can itself be constructed using a builder-style interface.
54 /// use std::io::Command;
56 /// let mut child = match Command::new("/bin/cat").arg("file.txt").spawn() {
57 /// Ok(child) => child,
58 /// Err(e) => fail!("failed to execute child: {}", e),
61 /// let contents = child.stdout.as_mut().unwrap().read_to_end();
62 /// assert!(child.wait().unwrap().success());
65 handle: Box<RtioProcess + Send>,
68 /// Handle to the child's stdin, if the `stdin` field of this process's
69 /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
70 pub stdin: Option<io::PipeStream>,
72 /// Handle to the child's stdout, if the `stdout` field of this process's
73 /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
74 pub stdout: Option<io::PipeStream>,
76 /// Handle to the child's stderr, if the `stderr` field of this process's
77 /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
78 pub stderr: Option<io::PipeStream>,
80 /// Extra I/O handles as configured by the original `ProcessConfig` when
81 /// this process was created. This is by default empty.
82 pub extra_io: Vec<Option<io::PipeStream>>,
85 /// A representation of environment variable name
86 /// It compares case-insensitive on Windows and case-sensitive everywhere else.
88 #[deriving(PartialEq, Eq, Hash, Clone, Show)]
89 struct EnvKey(CString);
93 #[deriving(Eq, Clone, Show)]
94 struct EnvKey(CString);
97 impl Hash for EnvKey {
98 fn hash(&self, state: &mut SipState) {
99 let &EnvKey(ref x) = self;
101 Some(s) => for ch in s.chars() {
102 (ch as u8 as char).to_lowercase().hash(state);
104 None => x.hash(state)
110 impl PartialEq for EnvKey {
111 fn eq(&self, other: &EnvKey) -> bool {
112 let &EnvKey(ref x) = self;
113 let &EnvKey(ref y) = other;
114 match (x.as_str(), y.as_str()) {
115 (Some(xs), Some(ys)) => {
116 if xs.len() != ys.len() {
119 for (xch, ych) in xs.chars().zip(ys.chars()) {
120 if xch.to_lowercase() != ych.to_lowercase() {
127 // If either is not a valid utf8 string, just compare them byte-wise
133 /// A HashMap representation of environment variables.
134 pub type EnvMap = HashMap<EnvKey, CString>;
136 /// The `Command` type acts as a process builder, providing fine-grained control
137 /// over how a new process should be spawned. A default configuration can be
138 /// generated using `Command::new(program)`, where `program` gives a path to the
139 /// program to be executed. Additional builder methods allow the configuration
140 /// to be changed (for example, by adding arguments) prior to spawning:
143 /// use std::io::Command;
145 /// let mut process = match Command::new("sh").arg("-c").arg("echo hello").spawn() {
147 /// Err(e) => fail!("failed to execute process: {}", e),
150 /// let output = process.stdout.as_mut().unwrap().read_to_end();
154 // The internal data for the builder. Documented by the builder
155 // methods below, and serialized into rt::rtio::ProcessConfig.
159 cwd: Option<CString>,
160 stdin: StdioContainer,
161 stdout: StdioContainer,
162 stderr: StdioContainer,
163 extra_io: Vec<StdioContainer>,
169 // FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so
170 // we cannot usefully take ToCStr arguments by reference (without forcing an
171 // additional & around &str). So we are instead temporarily adding an instance
172 // for &Path, so that we can take ToCStr as owned. When DST lands, the &Path
173 // instance should be removed, and arguments bound by ToCStr should be passed by
174 // reference. (Here: {new, arg, args, env}.)
177 /// Constructs a new `Command` for launching the program at
178 /// path `program`, with the following default configuration:
180 /// * No arguments to the program
181 /// * Inherit the current process's environment
182 /// * Inherit the current process's working directory
183 /// * A readable pipe for stdin (file descriptor 0)
184 /// * A writeable pipe for stdout and stderr (file descriptors 1 and 2)
186 /// Builder methods are provided to change these defaults and
187 /// otherwise configure the process.
188 pub fn new<T:ToCStr>(program: T) -> Command {
190 program: program.to_c_str(),
194 stdin: CreatePipe(true, false),
195 stdout: CreatePipe(false, true),
196 stderr: CreatePipe(false, true),
197 extra_io: Vec::new(),
204 /// Add an argument to pass to the program.
205 pub fn arg<'a, T: ToCStr>(&'a mut self, arg: T) -> &'a mut Command {
206 self.args.push(arg.to_c_str());
210 /// Add multiple arguments to pass to the program.
211 pub fn args<'a, T: ToCStr>(&'a mut self, args: &[T]) -> &'a mut Command {
212 self.args.extend(args.iter().map(|arg| arg.to_c_str()));;
215 // Get a mutable borrow of the environment variable map for this `Command`.
216 fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap {
218 Some(ref mut map) => map,
220 // if the env is currently just inheriting from the parent's,
221 // materialize the parent's env into a hashtable.
222 self.env = Some(os::env_as_bytes().into_iter()
223 .map(|(k, v)| (EnvKey(k.as_slice().to_c_str()),
224 v.as_slice().to_c_str()))
226 self.env.as_mut().unwrap()
231 /// Inserts or updates an environment variable mapping.
233 /// Note that environment variable names are case-insensitive (but case-preserving) on Windows,
234 /// and case-sensitive on all other platforms.
235 pub fn env<'a, T: ToCStr, U: ToCStr>(&'a mut self, key: T, val: U)
237 self.get_env_map().insert(EnvKey(key.to_c_str()), val.to_c_str());
241 /// Removes an environment variable mapping.
242 pub fn env_remove<'a, T: ToCStr>(&'a mut self, key: T) -> &'a mut Command {
243 self.get_env_map().remove(&EnvKey(key.to_c_str()));
247 /// Sets the entire environment map for the child process.
249 /// If the given slice contains multiple instances of an environment
250 /// variable, the *rightmost* instance will determine the value.
251 pub fn env_set_all<'a, T: ToCStr, U: ToCStr>(&'a mut self, env: &[(T,U)])
253 self.env = Some(env.iter().map(|&(ref k, ref v)| (EnvKey(k.to_c_str()), v.to_c_str()))
258 /// Set the working directory for the child process.
259 pub fn cwd<'a>(&'a mut self, dir: &Path) -> &'a mut Command {
260 self.cwd = Some(dir.to_c_str());
264 /// Configuration for the child process's stdin handle (file descriptor 0).
265 /// Defaults to `CreatePipe(true, false)` so the input can be written to.
266 pub fn stdin<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
271 /// Configuration for the child process's stdout handle (file descriptor 1).
272 /// Defaults to `CreatePipe(false, true)` so the output can be collected.
273 pub fn stdout<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
278 /// Configuration for the child process's stderr handle (file descriptor 2).
279 /// Defaults to `CreatePipe(false, true)` so the output can be collected.
280 pub fn stderr<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
284 /// Attaches a stream/file descriptor/pipe to the child process. Inherited
285 /// file descriptors are numbered consecutively, starting at 3; the first
286 /// three file descriptors (stdin/stdout/stderr) are configured with the
287 /// `stdin`, `stdout`, and `stderr` methods.
288 pub fn extra_io<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
289 self.extra_io.push(cfg);
293 /// Sets the child process's user id. This translates to a `setuid` call in
294 /// the child process. Setting this value on windows will cause the spawn to
295 /// fail. Failure in the `setuid` call on unix will also cause the spawn to
297 pub fn uid<'a>(&'a mut self, id: uint) -> &'a mut Command {
302 /// Similar to `uid`, but sets the group id of the child process. This has
303 /// the same semantics as the `uid` field.
304 pub fn gid<'a>(&'a mut self, id: uint) -> &'a mut Command {
309 /// Sets the child process to be spawned in a detached state. On unix, this
310 /// means that the child is the leader of a new process group.
311 pub fn detached<'a>(&'a mut self) -> &'a mut Command {
316 /// Executes the command as a child process, which is returned.
317 pub fn spawn(&self) -> IoResult<Process> {
318 fn to_rtio(p: StdioContainer) -> rtio::StdioContainer {
320 Ignored => rtio::Ignored,
321 InheritFd(fd) => rtio::InheritFd(fd),
322 CreatePipe(a, b) => rtio::CreatePipe(a, b),
325 let extra_io: Vec<rtio::StdioContainer> =
326 self.extra_io.iter().map(|x| to_rtio(*x)).collect();
327 LocalIo::maybe_raise(|io| {
328 let env = match self.env {
332 .map(|(&EnvKey(ref key), val)| (key, val))
333 .collect::<Vec<_>>())
335 let cfg = ProcessConfig {
336 program: &self.program,
337 args: self.args.as_slice(),
338 env: env.as_ref().map(|e| e.as_slice()),
339 cwd: self.cwd.as_ref(),
340 stdin: to_rtio(self.stdin),
341 stdout: to_rtio(self.stdout),
342 stderr: to_rtio(self.stderr),
343 extra_io: extra_io.as_slice(),
348 io.spawn(cfg).map(|(p, io)| {
349 let mut io = io.into_iter().map(|p| {
350 p.map(|p| io::PipeStream::new(p))
355 stdin: io.next().unwrap(),
356 stdout: io.next().unwrap(),
357 stderr: io.next().unwrap(),
358 extra_io: io.collect(),
361 }).map_err(IoError::from_rtio_error)
364 /// Executes the command as a child process, waiting for it to finish and
365 /// collecting all of its output.
370 /// use std::io::Command;
372 /// let output = match Command::new("cat").arg("foot.txt").output() {
373 /// Ok(output) => output,
374 /// Err(e) => fail!("failed to execute process: {}", e),
377 /// println!("status: {}", output.status);
378 /// println!("stdout: {}", String::from_utf8_lossy(output.output.as_slice()));
379 /// println!("stderr: {}", String::from_utf8_lossy(output.error.as_slice()));
381 pub fn output(&self) -> IoResult<ProcessOutput> {
382 self.spawn().and_then(|p| p.wait_with_output())
385 /// Executes a command as a child process, waiting for it to finish and
386 /// collecting its exit status.
391 /// use std::io::Command;
393 /// let status = match Command::new("ls").status() {
394 /// Ok(status) => status,
395 /// Err(e) => fail!("failed to execute process: {}", e),
398 /// println!("process exited with: {}", status);
400 pub fn status(&self) -> IoResult<ProcessExit> {
401 self.spawn().and_then(|mut p| p.wait())
405 impl fmt::Show for Command {
406 /// Format the program and arguments of a Command for display. Any
407 /// non-utf8 data is lossily converted using the utf8 replacement
409 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
410 try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes_no_nul())));
411 for arg in self.args.iter() {
412 try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes_no_nul())));
418 /// The output of a finished process.
419 #[deriving(PartialEq, Eq, Clone)]
420 pub struct ProcessOutput {
421 /// The status (exit code) of the process.
422 pub status: ProcessExit,
423 /// The data that the process wrote to stdout.
425 /// The data that the process wrote to stderr.
429 /// Describes what to do with a standard io stream for a child process.
431 pub enum StdioContainer {
432 /// This stream will be ignored. This is the equivalent of attaching the
433 /// stream to `/dev/null`
436 /// The specified file descriptor is inherited for the stream which it is
437 /// specified for. Ownership of the file descriptor is *not* taken, so the
438 /// caller must clean it up.
439 InheritFd(libc::c_int),
441 /// Creates a pipe for the specified file descriptor which will be created
442 /// when the process is spawned.
444 /// The first boolean argument is whether the pipe is readable, and the
445 /// second is whether it is writable. These properties are from the view of
446 /// the *child* process, not the parent process.
447 CreatePipe(bool /* readable */, bool /* writable */),
450 /// Describes the result of a process after it has terminated.
451 /// Note that Windows have no signals, so the result is usually ExitStatus.
452 #[deriving(PartialEq, Eq, Clone)]
453 pub enum ProcessExit {
454 /// Normal termination with an exit status.
457 /// Termination by signal, with the signal number.
461 impl fmt::Show for ProcessExit {
462 /// Format a ProcessExit enum, to nicely present the information.
463 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
465 ExitStatus(code) => write!(f, "exit code: {}", code),
466 ExitSignal(code) => write!(f, "signal: {}", code),
472 /// Was termination successful? Signal termination not considered a success,
473 /// and success is defined as a zero exit status.
474 pub fn success(&self) -> bool {
475 return self.matches_exit_status(0);
478 /// Checks whether this ProcessExit matches the given exit status.
479 /// Termination by signal will never match an exit code.
480 pub fn matches_exit_status(&self, wanted: int) -> bool {
481 *self == ExitStatus(wanted)
486 /// Sends `signal` to another process in the system identified by `id`.
488 /// Note that windows doesn't quite have the same model as unix, so some
489 /// unix signals are mapped to windows signals. Notably, unix termination
490 /// signals (SIGTERM/SIGKILL/SIGINT) are translated to `TerminateProcess`.
492 /// Additionally, a signal number of 0 can check for existence of the target
493 /// process. Note, though, that on some platforms signals will continue to
494 /// be successfully delivered if the child has exited, but not yet been
496 pub fn kill(id: libc::pid_t, signal: int) -> IoResult<()> {
497 LocalIo::maybe_raise(|io| {
499 }).map_err(IoError::from_rtio_error)
502 /// Returns the process id of this child process
503 pub fn id(&self) -> libc::pid_t { self.handle.id() }
505 /// Sends the specified signal to the child process, returning whether the
506 /// signal could be delivered or not.
508 /// Note that signal 0 is interpreted as a poll to check whether the child
509 /// process is still alive or not. If an error is returned, then the child
510 /// process has exited.
512 /// On some unix platforms signals will continue to be received after a
513 /// child has exited but not yet been reaped. In order to report the status
514 /// of signal delivery correctly, unix implementations may invoke
515 /// `waitpid()` with `WNOHANG` in order to reap the child as necessary.
519 /// If the signal delivery fails, the corresponding error is returned.
520 pub fn signal(&mut self, signal: int) -> IoResult<()> {
521 self.handle.kill(signal).map_err(IoError::from_rtio_error)
524 /// Sends a signal to this child requesting that it exits. This is
525 /// equivalent to sending a SIGTERM on unix platforms.
526 pub fn signal_exit(&mut self) -> IoResult<()> {
527 self.signal(PleaseExitSignal)
530 /// Sends a signal to this child forcing it to exit. This is equivalent to
531 /// sending a SIGKILL on unix platforms.
532 pub fn signal_kill(&mut self) -> IoResult<()> {
533 self.signal(MustDieSignal)
536 /// Wait for the child to exit completely, returning the status that it
537 /// exited with. This function will continue to have the same return value
538 /// after it has been called at least once.
540 /// The stdin handle to the child process will be closed before waiting.
544 /// This function can fail if a timeout was previously specified via
545 /// `set_timeout` and the timeout expires before the child exits.
546 pub fn wait(&mut self) -> IoResult<ProcessExit> {
547 drop(self.stdin.take());
548 match self.handle.wait() {
549 Ok(rtio::ExitSignal(s)) => Ok(ExitSignal(s)),
550 Ok(rtio::ExitStatus(s)) => Ok(ExitStatus(s)),
551 Err(e) => Err(IoError::from_rtio_error(e)),
555 /// Sets a timeout, in milliseconds, for future calls to wait().
557 /// The argument specified is a relative distance into the future, in
558 /// milliseconds, after which any call to wait() will return immediately
559 /// with a timeout error, and all future calls to wait() will not block.
561 /// A value of `None` will clear any previous timeout, and a value of `Some`
562 /// will override any previously set timeout.
567 /// # #![allow(experimental)]
568 /// use std::io::{Command, IoResult};
569 /// use std::io::process::ProcessExit;
571 /// fn run_gracefully(prog: &str) -> IoResult<ProcessExit> {
572 /// let mut p = try!(Command::new("long-running-process").spawn());
574 /// // give the process 10 seconds to finish completely
575 /// p.set_timeout(Some(10_000));
577 /// Ok(status) => return Ok(status),
581 /// // Attempt to exit gracefully, but don't wait for it too long
582 /// try!(p.signal_exit());
583 /// p.set_timeout(Some(1_000));
585 /// Ok(status) => return Ok(status),
589 /// // Well, we did our best, forcefully kill the process
590 /// try!(p.signal_kill());
591 /// p.set_timeout(None);
595 #[experimental = "the type of the timeout is likely to change"]
596 pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
597 self.handle.set_timeout(timeout_ms)
600 /// Simultaneously wait for the child to exit and collect all remaining
601 /// output on the stdout/stderr handles, returning a `ProcessOutput`
604 /// The stdin handle to the child is closed before waiting.
608 /// This function can fail for any of the same reasons that `wait()` can
610 pub fn wait_with_output(mut self) -> IoResult<ProcessOutput> {
611 drop(self.stdin.take());
612 fn read(stream: Option<io::PipeStream>) -> Receiver<IoResult<Vec<u8>>> {
613 let (tx, rx) = channel();
615 Some(stream) => spawn(proc() {
616 let mut stream = stream;
617 tx.send(stream.read_to_end())
619 None => tx.send(Ok(Vec::new()))
623 let stdout = read(self.stdout.take());
624 let stderr = read(self.stderr.take());
626 let status = try!(self.wait());
630 output: stdout.recv().ok().unwrap_or(Vec::new()),
631 error: stderr.recv().ok().unwrap_or(Vec::new()),
635 /// Forgets this process, allowing it to outlive the parent
637 /// This function will forcefully prevent calling `wait()` on the child
638 /// process in the destructor, allowing the child to outlive the
639 /// parent. Note that this operation can easily lead to leaking the
640 /// resources of the child process, so care must be taken when
641 /// invoking this method.
642 pub fn forget(mut self) {
647 impl Drop for Process {
649 if self.forget { return }
651 // Close all I/O before exiting to ensure that the child doesn't wait
652 // forever to print some text or something similar.
653 drop(self.stdin.take());
654 drop(self.stdout.take());
655 drop(self.stderr.take());
656 drop(mem::replace(&mut self.extra_io, Vec::new()));
658 self.set_timeout(None);
659 let _ = self.wait().unwrap();
666 use io::process::{Command, Process};
669 // FIXME(#10380) these tests should not all be ignored on android.
671 #[cfg(not(target_os="android"))]
673 let p = Command::new("true").spawn();
675 let mut p = p.unwrap();
676 assert!(p.wait().unwrap().success());
679 #[cfg(not(target_os="android"))]
680 iotest!(fn smoke_failure() {
681 match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
687 #[cfg(not(target_os="android"))]
688 iotest!(fn exit_reported_right() {
689 let p = Command::new("false").spawn();
691 let mut p = p.unwrap();
692 assert!(p.wait().unwrap().matches_exit_status(1));
693 drop(p.wait().clone());
696 #[cfg(unix, not(target_os="android"))]
697 iotest!(fn signal_reported_right() {
698 let p = Command::new("/bin/sh").arg("-c").arg("kill -1 $$").spawn();
700 let mut p = p.unwrap();
701 match p.wait().unwrap() {
702 process::ExitSignal(1) => {},
703 result => fail!("not terminated by signal 1 (instead, {})", result),
707 pub fn read_all(input: &mut Reader) -> String {
708 input.read_to_string().unwrap()
711 pub fn run_output(cmd: Command) -> String {
714 let mut p = p.unwrap();
715 assert!(p.stdout.is_some());
716 let ret = read_all(p.stdout.get_mut_ref() as &mut Reader);
717 assert!(p.wait().unwrap().success());
721 #[cfg(not(target_os="android"))]
722 iotest!(fn stdout_works() {
723 let mut cmd = Command::new("echo");
724 cmd.arg("foobar").stdout(CreatePipe(false, true));
725 assert_eq!(run_output(cmd), "foobar\n".to_string());
728 #[cfg(unix, not(target_os="android"))]
729 iotest!(fn set_cwd_works() {
730 let mut cmd = Command::new("/bin/sh");
731 cmd.arg("-c").arg("pwd")
732 .cwd(&Path::new("/"))
733 .stdout(CreatePipe(false, true));
734 assert_eq!(run_output(cmd), "/\n".to_string());
737 #[cfg(unix, not(target_os="android"))]
738 iotest!(fn stdin_works() {
739 let mut p = Command::new("/bin/sh")
740 .arg("-c").arg("read line; echo $line")
741 .stdin(CreatePipe(true, false))
742 .stdout(CreatePipe(false, true))
744 p.stdin.get_mut_ref().write("foobar".as_bytes()).unwrap();
745 drop(p.stdin.take());
746 let out = read_all(p.stdout.get_mut_ref() as &mut Reader);
747 assert!(p.wait().unwrap().success());
748 assert_eq!(out, "foobar\n".to_string());
751 #[cfg(not(target_os="android"))]
752 iotest!(fn detach_works() {
753 let mut p = Command::new("true").detached().spawn().unwrap();
754 assert!(p.wait().unwrap().success());
758 iotest!(fn uid_fails_on_windows() {
759 assert!(Command::new("test").uid(10).spawn().is_err());
762 #[cfg(unix, not(target_os="android"))]
763 iotest!(fn uid_works() {
765 let mut p = Command::new("/bin/sh")
766 .arg("-c").arg("true")
767 .uid(unsafe { libc::getuid() as uint })
768 .gid(unsafe { libc::getgid() as uint })
770 assert!(p.wait().unwrap().success());
773 #[cfg(unix, not(target_os="android"))]
774 iotest!(fn uid_to_root_fails() {
777 // if we're already root, this isn't a valid test. Most of the bots run
778 // as non-root though (android is an exception).
779 if unsafe { libc::getuid() == 0 } { return }
780 assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err());
783 #[cfg(not(target_os="android"))]
784 iotest!(fn test_process_status() {
785 let mut status = Command::new("false").status().unwrap();
786 assert!(status.matches_exit_status(1));
788 status = Command::new("true").status().unwrap();
789 assert!(status.success());
792 iotest!(fn test_process_output_fail_to_start() {
793 match Command::new("/no-binary-by-this-name-should-exist").output() {
794 Err(e) => assert_eq!(e.kind, FileNotFound),
799 #[cfg(not(target_os="android"))]
800 iotest!(fn test_process_output_output() {
801 let ProcessOutput {status, output, error}
802 = Command::new("echo").arg("hello").output().unwrap();
803 let output_str = str::from_utf8(output.as_slice()).unwrap();
805 assert!(status.success());
806 assert_eq!(output_str.trim().to_string(), "hello".to_string());
808 if !running_on_valgrind() {
809 assert_eq!(error, Vec::new());
813 #[cfg(not(target_os="android"))]
814 iotest!(fn test_process_output_error() {
815 let ProcessOutput {status, output, error}
816 = Command::new("mkdir").arg(".").output().unwrap();
818 assert!(status.matches_exit_status(1));
819 assert_eq!(output, Vec::new());
820 assert!(!error.is_empty());
823 #[cfg(not(target_os="android"))]
824 iotest!(fn test_finish_once() {
825 let mut prog = Command::new("false").spawn().unwrap();
826 assert!(prog.wait().unwrap().matches_exit_status(1));
829 #[cfg(not(target_os="android"))]
830 iotest!(fn test_finish_twice() {
831 let mut prog = Command::new("false").spawn().unwrap();
832 assert!(prog.wait().unwrap().matches_exit_status(1));
833 assert!(prog.wait().unwrap().matches_exit_status(1));
836 #[cfg(not(target_os="android"))]
837 iotest!(fn test_wait_with_output_once() {
838 let prog = Command::new("echo").arg("hello").spawn().unwrap();
839 let ProcessOutput {status, output, error} = prog.wait_with_output().unwrap();
840 let output_str = str::from_utf8(output.as_slice()).unwrap();
842 assert!(status.success());
843 assert_eq!(output_str.trim().to_string(), "hello".to_string());
845 if !running_on_valgrind() {
846 assert_eq!(error, Vec::new());
850 #[cfg(unix,not(target_os="android"))]
851 pub fn pwd_cmd() -> Command {
854 #[cfg(target_os="android")]
855 pub fn pwd_cmd() -> Command {
856 let mut cmd = Command::new("/system/bin/sh");
857 cmd.arg("-c").arg("pwd");
862 pub fn pwd_cmd() -> Command {
863 let mut cmd = Command::new("cmd");
864 cmd.arg("/c").arg("cd");
868 iotest!(fn test_keep_current_working_dir() {
870 let prog = pwd_cmd().spawn().unwrap();
872 let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap();
873 let parent_dir = os::getcwd();
874 let child_dir = Path::new(output.as_slice().trim());
876 let parent_stat = parent_dir.stat().unwrap();
877 let child_stat = child_dir.stat().unwrap();
879 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
880 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
883 iotest!(fn test_change_working_directory() {
885 // test changing to the parent of os::getcwd() because we know
886 // the path exists (and os::getcwd() is not expected to be root)
887 let parent_dir = os::getcwd().dir_path();
888 let prog = pwd_cmd().cwd(&parent_dir).spawn().unwrap();
890 let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap();
891 let child_dir = Path::new(output.as_slice().trim().into_string());
893 let parent_stat = parent_dir.stat().unwrap();
894 let child_stat = child_dir.stat().unwrap();
896 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
897 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
900 #[cfg(unix,not(target_os="android"))]
901 pub fn env_cmd() -> Command {
904 #[cfg(target_os="android")]
905 pub fn env_cmd() -> Command {
906 let mut cmd = Command::new("/system/bin/sh");
907 cmd.arg("-c").arg("set");
912 pub fn env_cmd() -> Command {
913 let mut cmd = Command::new("cmd");
914 cmd.arg("/c").arg("set");
918 #[cfg(not(target_os="android"))]
919 iotest!(fn test_inherit_env() {
921 if running_on_valgrind() { return; }
923 let prog = env_cmd().spawn().unwrap();
924 let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap();
927 for &(ref k, ref v) in r.iter() {
928 // don't check windows magical empty-named variables
929 assert!(k.is_empty() ||
931 .contains(format!("{}={}", *k, *v).as_slice()));
934 #[cfg(target_os="android")]
935 iotest!(fn test_inherit_env() {
937 if running_on_valgrind() { return; }
939 let mut prog = env_cmd().spawn().unwrap();
940 let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap();
943 for &(ref k, ref v) in r.iter() {
944 // don't check android RANDOM variables
945 if *k != "RANDOM".to_string() {
946 assert!(output.as_slice()
947 .contains(format!("{}={}",
951 .contains(format!("{}=\'{}\'",
958 iotest!(fn test_override_env() {
959 let new_env = vec![("RUN_TEST_NEW_ENV", "123")];
960 let prog = env_cmd().env_set_all(new_env.as_slice()).spawn().unwrap();
961 let result = prog.wait_with_output().unwrap();
962 let output = String::from_utf8_lossy(result.output.as_slice()).into_string();
964 assert!(output.as_slice().contains("RUN_TEST_NEW_ENV=123"),
965 "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
968 iotest!(fn test_add_to_env() {
969 let prog = env_cmd().env("RUN_TEST_NEW_ENV", "123").spawn().unwrap();
970 let result = prog.wait_with_output().unwrap();
971 let output = str::from_utf8_lossy(result.output.as_slice()).into_string();
973 assert!(output.as_slice().contains("RUN_TEST_NEW_ENV=123"),
974 "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
977 iotest!(fn test_remove_from_env() {
980 // save original environment
981 let old_env = os::getenv("RUN_TEST_NEW_ENV");
983 os::setenv("RUN_TEST_NEW_ENV", "123");
984 let prog = env_cmd().env_remove("RUN_TEST_NEW_ENV").spawn().unwrap();
985 let result = prog.wait_with_output().unwrap();
986 let output = str::from_utf8_lossy(result.output.as_slice()).into_string();
988 // restore original environment
991 os::unsetenv("RUN_TEST_NEW_ENV");
994 os::setenv("RUN_TEST_NEW_ENV", val.as_slice());
998 assert!(!output.as_slice().contains("RUN_TEST_NEW_ENV"),
999 "found RUN_TEST_NEW_ENV inside of:\n\n{}", output);
1003 pub fn sleeper() -> Process {
1004 Command::new("sleep").arg("1000").spawn().unwrap()
1007 pub fn sleeper() -> Process {
1008 // There's a `timeout` command on windows, but it doesn't like having
1009 // its output piped, so instead just ping ourselves a few times with
1010 // gaps in between so we're sure this process is alive for awhile
1011 Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn().unwrap()
1014 iotest!(fn test_kill() {
1015 let mut p = sleeper();
1016 Process::kill(p.id(), PleaseExitSignal).unwrap();
1017 assert!(!p.wait().unwrap().success());
1020 iotest!(fn test_exists() {
1021 let mut p = sleeper();
1022 assert!(Process::kill(p.id(), 0).is_ok());
1023 p.signal_kill().unwrap();
1024 assert!(!p.wait().unwrap().success());
1027 iotest!(fn test_zero() {
1028 let mut p = sleeper();
1029 p.signal_kill().unwrap();
1030 for _ in range(0i, 20) {
1031 if p.signal(0).is_err() {
1032 assert!(!p.wait().unwrap().success());
1035 timer::sleep(Duration::milliseconds(100));
1037 fail!("never saw the child go away");
1040 iotest!(fn wait_timeout() {
1041 let mut p = sleeper();
1042 p.set_timeout(Some(10));
1043 assert_eq!(p.wait().err().unwrap().kind, TimedOut);
1044 assert_eq!(p.wait().err().unwrap().kind, TimedOut);
1045 p.signal_kill().unwrap();
1046 p.set_timeout(None);
1047 assert!(p.wait().is_ok());
1050 iotest!(fn wait_timeout2() {
1051 let (tx, rx) = channel();
1052 let tx2 = tx.clone();
1054 let mut p = sleeper();
1055 p.set_timeout(Some(10));
1056 assert_eq!(p.wait().err().unwrap().kind, TimedOut);
1057 p.signal_kill().unwrap();
1061 let mut p = sleeper();
1062 p.set_timeout(Some(10));
1063 assert_eq!(p.wait().err().unwrap().kind, TimedOut);
1064 p.signal_kill().unwrap();
1071 iotest!(fn forget() {
1075 assert!(Process::kill(id, 0).is_ok());
1076 assert!(Process::kill(id, PleaseExitSignal).is_ok());
1079 iotest!(fn dont_close_fd_on_command_spawn() {
1080 use std::rt::rtio::{Truncate, Write};
1081 use native::io::file;
1083 let path = if cfg!(windows) {
1086 Path::new("/dev/null")
1089 let mut fdes = match file::open(&path.to_c_str(), Truncate, Write) {
1091 Err(_) => fail!("failed to open file descriptor"),
1094 let mut cmd = pwd_cmd();
1095 let _ = cmd.stdout(InheritFd(fdes.fd()));
1096 assert!(cmd.status().unwrap().success());
1097 assert!(fdes.inner_write("extra write\n".as_bytes()).is_ok());
1102 fn env_map_keys_ci() {
1104 let mut cmd = Command::new("");
1105 cmd.env("path", "foo");
1106 cmd.env("Path", "bar");
1107 let env = &cmd.env.unwrap();
1108 let val = env.find(&EnvKey("PATH".to_c_str()));
1109 assert!(val.unwrap() == &"bar".to_c_str());