1 // Copyright 2012-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.
13 #[allow(missing_doc)];
17 use io::process::ProcessExit;
20 use libc::{pid_t, c_int};
25 * A value representing a child process.
27 * The lifetime of this value is linked to the lifetime of the actual
28 * process - the Process destructor calls self.finish() which waits
29 * for the process to terminate.
32 priv inner: process::Process,
35 /// Options that can be given when starting a Process.
36 pub struct ProcessOptions<'a> {
38 * If this is None then the new process will have the same initial
39 * environment as the parent process.
41 * If this is Some(vec-of-names-and-values) then the new process will
42 * have an environment containing the given named values only.
44 env: Option<~[(~str, ~str)]>,
47 * If this is None then the new process will use the same initial working
48 * directory as the parent process.
50 * If this is Some(path) then the new process will use the given path
51 * for its initial working directory.
53 dir: Option<&'a Path>,
56 * If this is None then a new pipe will be created for the new process's
57 * input and Process.input() will provide a Writer to write to this pipe.
59 * If this is Some(file-descriptor) then the new process will read its input
60 * from the given file descriptor, Process.input_redirected() will return
61 * true, and Process.input() will fail.
66 * If this is None then a new pipe will be created for the new program's
67 * output and Process.output() will provide a Reader to read from this pipe.
69 * If this is Some(file-descriptor) then the new process will write its output
70 * to the given file descriptor, Process.output_redirected() will return
71 * true, and Process.output() will fail.
73 out_fd: Option<c_int>,
76 * If this is None then a new pipe will be created for the new program's
77 * error stream and Process.error() will provide a Reader to read from this pipe.
79 * If this is Some(file-descriptor) then the new process will write its error output
80 * to the given file descriptor, Process.error_redirected() will return true, and
81 * and Process.error() will fail.
83 err_fd: Option<c_int>,
86 impl <'a> ProcessOptions<'a> {
87 /// Return a ProcessOptions that has None in every field.
88 pub fn new<'a>() -> ProcessOptions<'a> {
99 /// The output of a finished process.
100 pub struct ProcessOutput {
101 /// The status (exit code) of the process.
104 /// The data that the process wrote to stdout.
107 /// The data that the process wrote to stderr.
113 * Spawns a new Process.
117 * * prog - The path to an executable.
118 * * args - Vector of arguments to pass to the child process.
119 * * options - Options to configure the environment of the process,
120 * the working directory and the standard IO streams.
122 pub fn new(prog: &str, args: &[~str], options: ProcessOptions) -> Option<Process> {
123 let ProcessOptions { env, dir, in_fd, out_fd, err_fd } = options;
124 let env = env.as_ref().map(|a| a.as_slice());
125 let cwd = dir.as_ref().map(|a| a.as_str().unwrap());
126 fn rtify(fd: Option<c_int>, input: bool) -> process::StdioContainer {
128 Some(fd) => process::InheritFd(fd),
129 None => process::CreatePipe(input, !input),
132 let rtio = [rtify(in_fd, true), rtify(out_fd, false),
133 rtify(err_fd, false)];
134 let rtconfig = process::ProcessConfig {
141 match process::Process::new(rtconfig) {
142 Some(inner) => Some(Process { inner: inner }),
147 /// Returns the unique id of the process
148 pub fn get_id(&self) -> pid_t { self.inner.id() }
151 * Returns an io::Writer that can be used to write to this Process's stdin.
153 * Fails if there is no stdin available (it's already been removed by
156 pub fn input<'a>(&'a mut self) -> &'a mut io::Writer {
157 self.inner.io[0].get_mut_ref() as &mut io::Writer
161 * Returns an io::Reader that can be used to read from this Process's stdout.
163 * Fails if there is no stdout available (it's already been removed by
166 pub fn output<'a>(&'a mut self) -> &'a mut io::Reader {
167 self.inner.io[1].get_mut_ref() as &mut io::Reader
171 * Returns an io::Reader that can be used to read from this Process's stderr.
173 * Fails if there is no stderr available (it's already been removed by
176 pub fn error<'a>(&'a mut self) -> &'a mut io::Reader {
177 self.inner.io[2].get_mut_ref() as &mut io::Reader
181 * Closes the handle to the child process's stdin.
183 pub fn close_input(&mut self) {
184 self.inner.io[0].take();
188 * Closes the handle to stdout and stderr.
190 pub fn close_outputs(&mut self) {
191 self.inner.io[1].take();
192 self.inner.io[2].take();
196 * Closes the handle to stdin, waits for the child process to terminate,
197 * and returns the exit code.
199 * If the child has already been finished then the exit code is returned.
201 pub fn finish(&mut self) -> ProcessExit { self.inner.wait() }
204 * Closes the handle to stdin, waits for the child process to terminate, and
205 * reads and returns all remaining output of stdout and stderr, along with
208 * If the child has already been finished then the exit code and any
209 * remaining unread output of stdout and stderr will be returned.
211 * This method will fail if the child process's stdout or stderr streams
212 * were redirected to existing file descriptors.
214 pub fn finish_with_output(&mut self) -> ProcessOutput {
216 let output = self.inner.io[1].take();
217 let error = self.inner.io[2].take();
219 // Spawn two entire schedulers to read both stdout and sterr
220 // in parallel so we don't deadlock while blocking on one
221 // or the other. FIXME (#2625): Surely there's a much more
222 // clever way to do this.
223 let (p, ch) = SharedChan::new();
224 let ch_clone = ch.clone();
227 let _guard = io::ignore_io_error();
228 let mut error = error;
230 Some(ref mut e) => ch.send((2, e.read_to_end())),
231 None => ch.send((2, ~[]))
235 let _guard = io::ignore_io_error();
236 let mut output = output;
238 Some(ref mut e) => ch_clone.send((1, e.read_to_end())),
239 None => ch_clone.send((1, ~[]))
243 let status = self.finish();
245 let (errs, outs) = match (p.recv(), p.recv()) {
246 ((1, o), (2, e)) => (e, o),
247 ((2, e), (1, o)) => (e, o),
248 ((x, _), (y, _)) => {
249 fail!("unexpected file numbers: {}, {}", x, y);
253 return ProcessOutput {status: status,
259 * Terminates the process, giving it a chance to clean itself up if
260 * this is supported by the operating system.
262 * On Posix OSs SIGTERM will be sent to the process. On Win32
263 * TerminateProcess(..) will be called.
265 pub fn destroy(&mut self) {
266 self.inner.signal(io::process::PleaseExitSignal);
271 * Terminates the process as soon as possible without giving it a
272 * chance to clean itself up.
274 * On Posix OSs SIGKILL will be sent to the process. On Win32
275 * TerminateProcess(..) will be called.
277 pub fn force_destroy(&mut self) {
278 self.inner.signal(io::process::MustDieSignal);
284 * Spawns a process and waits for it to terminate. The process will
285 * inherit the current stdin/stdout/stderr file descriptors.
289 * * prog - The path to an executable
290 * * args - Vector of arguments to pass to the child process
294 * The process's exit code, or None if the child process could not be started
296 pub fn process_status(prog: &str, args: &[~str]) -> Option<ProcessExit> {
297 let mut opt_prog = Process::new(prog, args, ProcessOptions {
300 in_fd: Some(unsafe { libc::dup(libc::STDIN_FILENO) }),
301 out_fd: Some(unsafe { libc::dup(libc::STDOUT_FILENO) }),
302 err_fd: Some(unsafe { libc::dup(libc::STDERR_FILENO) })
305 Some(ref mut prog) => Some(prog.finish()),
311 * Spawns a process, records all its output, and waits for it to terminate.
315 * * prog - The path to an executable
316 * * args - Vector of arguments to pass to the child process
320 * The process's stdout/stderr output and exit code, or None if the child process could not be
323 pub fn process_output(prog: &str, args: &[~str]) -> Option<ProcessOutput> {
324 let mut opt_prog = Process::new(prog, args, ProcessOptions::new());
326 Some(ref mut prog) => Some(prog.finish_with_output()),
334 use option::{Option, None, Some};
340 use unstable::running_on_valgrind;
341 use io::native::file;
342 use io::{FileNotFound, OtherIoError, Reader, Writer, io_error};
345 #[cfg(not(target_os="android"))] // FIXME(#10380)
346 fn test_process_status() {
347 let mut status = run::process_status("false", []).expect("failed to exec `false`");
348 assert!(status.matches_exit_status(1));
350 status = run::process_status("true", []).expect("failed to exec `true`");
351 assert!(status.success());
355 fn test_process_output_fail_to_start() {
356 // If the executable does not exist, then the io_error condition should be raised with
357 // IoErrorKind FileNotFound.
359 let mut trapped_io_error = false;
360 let opt_outp = io_error::cond.trap(|e| {
361 trapped_io_error = true;
363 assert_eq!(e.kind, if cfg!(windows) { OtherIoError } else { FileNotFound });
364 }).inside(|| -> Option<run::ProcessOutput> {
365 run::process_output("no-binary-by-this-name-should-exist", [])
367 assert!(trapped_io_error);
368 assert!(opt_outp.is_none());
372 #[cfg(not(target_os="android"))] // FIXME(#10380)
373 fn test_process_output_output() {
375 let run::ProcessOutput {status, output, error}
376 = run::process_output("echo", [~"hello"]).expect("failed to exec `echo`");
377 let output_str = str::from_utf8_owned(output);
379 assert!(status.success());
380 assert_eq!(output_str.trim().to_owned(), ~"hello");
382 if !running_on_valgrind() {
383 assert_eq!(error, ~[]);
388 #[cfg(not(target_os="android"))] // FIXME(#10380)
389 fn test_process_output_error() {
391 let run::ProcessOutput {status, output, error}
392 = run::process_output("mkdir", [~"."]).expect("failed to exec `mkdir`");
394 assert!(status.matches_exit_status(1));
395 assert_eq!(output, ~[]);
396 assert!(!error.is_empty());
400 #[ignore] // FIXME(#10016) cat never sees stdin close
403 let pipe_in = os::pipe();
404 let pipe_out = os::pipe();
405 let pipe_err = os::pipe();
407 let mut process = run::Process::new("cat", [], run::ProcessOptions {
410 in_fd: Some(pipe_in.input),
411 out_fd: Some(pipe_out.out),
412 err_fd: Some(pipe_err.out)
413 }).expect("failed to exec `cat`");
415 os::close(pipe_in.input);
416 os::close(pipe_out.out);
417 os::close(pipe_err.out);
420 writeclose(pipe_in.out, "test");
422 let actual = readclose(pipe_out.input);
423 readclose(pipe_err.input);
426 assert_eq!(~"test", actual);
429 fn writeclose(fd: c_int, s: &str) {
430 let mut writer = file::FileDesc::new(fd, true);
431 writer.write(s.as_bytes());
434 fn readclose(fd: c_int) -> ~str {
436 let mut reader = file::FileDesc::new(fd, true);
437 let mut buf = [0, ..1024];
439 match reader.read(buf) {
440 Some(n) => { res.push_all(buf.slice_to(n)); }
444 str::from_utf8_owned(res)
448 #[cfg(not(target_os="android"))] // FIXME(#10380)
449 fn test_finish_once() {
450 let mut prog = run::Process::new("false", [], run::ProcessOptions::new())
451 .expect("failed to exec `false`");
452 assert!(prog.finish().matches_exit_status(1));
456 #[cfg(not(target_os="android"))] // FIXME(#10380)
457 fn test_finish_twice() {
458 let mut prog = run::Process::new("false", [], run::ProcessOptions::new())
459 .expect("failed to exec `false`");
460 assert!(prog.finish().matches_exit_status(1));
461 assert!(prog.finish().matches_exit_status(1));
465 #[cfg(not(target_os="android"))] // FIXME(#10380)
466 fn test_finish_with_output_once() {
468 let mut prog = run::Process::new("echo", [~"hello"], run::ProcessOptions::new())
469 .expect("failed to exec `echo`");
470 let run::ProcessOutput {status, output, error}
471 = prog.finish_with_output();
472 let output_str = str::from_utf8_owned(output);
474 assert!(status.success());
475 assert_eq!(output_str.trim().to_owned(), ~"hello");
477 if !running_on_valgrind() {
478 assert_eq!(error, ~[]);
483 #[cfg(not(target_os="android"))] // FIXME(#10380)
484 fn test_finish_with_output_twice() {
486 let mut prog = run::Process::new("echo", [~"hello"], run::ProcessOptions::new())
487 .expect("failed to exec `echo`");
488 let run::ProcessOutput {status, output, error}
489 = prog.finish_with_output();
491 let output_str = str::from_utf8_owned(output);
493 assert!(status.success());
494 assert_eq!(output_str.trim().to_owned(), ~"hello");
496 if !running_on_valgrind() {
497 assert_eq!(error, ~[]);
500 let run::ProcessOutput {status, output, error}
501 = prog.finish_with_output();
503 assert!(status.success());
504 assert_eq!(output, ~[]);
506 if !running_on_valgrind() {
507 assert_eq!(error, ~[]);
511 #[cfg(unix,not(target_os="android"))]
512 fn run_pwd(dir: Option<&Path>) -> run::Process {
513 run::Process::new("pwd", [], run::ProcessOptions {
515 .. run::ProcessOptions::new()
516 }).expect("failed to exec `pwd`")
518 #[cfg(unix,target_os="android")]
519 fn run_pwd(dir: Option<&Path>) -> run::Process {
520 run::Process::new("/system/bin/sh", [~"-c",~"pwd"], run::ProcessOptions {
522 .. run::ProcessOptions::new()
523 }).expect("failed to exec `/system/bin/sh`")
527 fn run_pwd(dir: Option<&Path>) -> run::Process {
528 run::Process::new("cmd", [~"/c", ~"cd"], run::ProcessOptions {
530 .. run::ProcessOptions::new()
531 }).expect("failed to run `cmd`")
535 fn test_keep_current_working_dir() {
536 let mut prog = run_pwd(None);
538 let output = str::from_utf8_owned(prog.finish_with_output().output);
539 let parent_dir = os::getcwd();
540 let child_dir = Path::new(output.trim());
542 let parent_stat = parent_dir.stat();
543 let child_stat = child_dir.stat();
545 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
546 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
550 fn test_change_working_directory() {
551 // test changing to the parent of os::getcwd() because we know
552 // the path exists (and os::getcwd() is not expected to be root)
553 let parent_dir = os::getcwd().dir_path();
554 let mut prog = run_pwd(Some(&parent_dir));
556 let output = str::from_utf8_owned(prog.finish_with_output().output);
557 let child_dir = Path::new(output.trim());
559 let parent_stat = parent_dir.stat();
560 let child_stat = child_dir.stat();
562 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
563 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
566 #[cfg(unix,not(target_os="android"))]
567 fn run_env(env: Option<~[(~str, ~str)]>) -> run::Process {
568 run::Process::new("env", [], run::ProcessOptions {
570 .. run::ProcessOptions::new()
571 }).expect("failed to exec `env`")
573 #[cfg(unix,target_os="android")]
574 fn run_env(env: Option<~[(~str, ~str)]>) -> run::Process {
575 run::Process::new("/system/bin/sh", [~"-c",~"set"], run::ProcessOptions {
577 .. run::ProcessOptions::new()
578 }).expect("failed to exec `/system/bin/sh`")
582 fn run_env(env: Option<~[(~str, ~str)]>) -> run::Process {
583 run::Process::new("cmd", [~"/c", ~"set"], run::ProcessOptions {
585 .. run::ProcessOptions::new()
586 }).expect("failed to run `cmd`")
590 #[cfg(not(target_os="android"))]
591 fn test_inherit_env() {
592 if running_on_valgrind() { return; }
594 let mut prog = run_env(None);
595 let output = str::from_utf8_owned(prog.finish_with_output().output);
598 for &(ref k, ref v) in r.iter() {
599 // don't check windows magical empty-named variables
600 assert!(k.is_empty() || output.contains(format!("{}={}", *k, *v)));
604 #[cfg(target_os="android")]
605 fn test_inherit_env() {
606 if running_on_valgrind() { return; }
608 let mut prog = run_env(None);
609 let output = str::from_utf8_owned(prog.finish_with_output().output);
612 for &(ref k, ref v) in r.iter() {
613 // don't check android RANDOM variables
615 assert!(output.contains(format!("{}={}", *k, *v)) ||
616 output.contains(format!("{}=\'{}\'", *k, *v)));
622 fn test_add_to_env() {
624 let mut new_env = os::env();
625 new_env.push((~"RUN_TEST_NEW_ENV", ~"123"));
627 let mut prog = run_env(Some(new_env));
628 let output = str::from_utf8_owned(prog.finish_with_output().output);
630 assert!(output.contains("RUN_TEST_NEW_ENV=123"));