]> git.lizzy.rs Git - rust.git/blob - src/libstd/run.rs
auto merge of #10977 : brson/rust/androidtest, r=brson
[rust.git] / src / libstd / run.rs
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.
4 //
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.
10
11 //! Process spawning.
12
13 #[allow(missing_doc)];
14
15 use comm::SharedChan;
16 use io::Reader;
17 use io::process::ProcessExit;
18 use io::process;
19 use io;
20 use libc::{pid_t, c_int};
21 use libc;
22 use prelude::*;
23
24 /**
25  * A value representing a child process.
26  *
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.
30  */
31 pub struct Process {
32     priv inner: process::Process,
33 }
34
35 /// Options that can be given when starting a Process.
36 pub struct ProcessOptions<'a> {
37     /**
38      * If this is None then the new process will have the same initial
39      * environment as the parent process.
40      *
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.
43      */
44     env: Option<~[(~str, ~str)]>,
45
46     /**
47      * If this is None then the new process will use the same initial working
48      * directory as the parent process.
49      *
50      * If this is Some(path) then the new process will use the given path
51      * for its initial working directory.
52      */
53     dir: Option<&'a Path>,
54
55     /**
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.
58      *
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.
62      */
63     in_fd: Option<c_int>,
64
65     /**
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.
68      *
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.
72      */
73     out_fd: Option<c_int>,
74
75     /**
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.
78      *
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.
82      */
83     err_fd: Option<c_int>,
84 }
85
86 impl <'a> ProcessOptions<'a> {
87     /// Return a ProcessOptions that has None in every field.
88     pub fn new<'a>() -> ProcessOptions<'a> {
89         ProcessOptions {
90             env: None,
91             dir: None,
92             in_fd: None,
93             out_fd: None,
94             err_fd: None,
95         }
96     }
97 }
98
99 /// The output of a finished process.
100 pub struct ProcessOutput {
101     /// The status (exit code) of the process.
102     status: ProcessExit,
103
104     /// The data that the process wrote to stdout.
105     output: ~[u8],
106
107     /// The data that the process wrote to stderr.
108     error: ~[u8],
109 }
110
111 impl Process {
112     /**
113      * Spawns a new Process.
114      *
115      * # Arguments
116      *
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.
121      */
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 {
127             match fd {
128                 Some(fd) => process::InheritFd(fd),
129                 None => process::CreatePipe(input, !input),
130             }
131         }
132         let rtio = [rtify(in_fd, true), rtify(out_fd, false),
133                     rtify(err_fd, false)];
134         let rtconfig = process::ProcessConfig {
135             program: prog,
136             args: args,
137             env: env,
138             cwd: cwd,
139             io: rtio,
140         };
141         match process::Process::new(rtconfig) {
142             Some(inner) => Some(Process { inner: inner }),
143             None => None
144         }
145     }
146
147     /// Returns the unique id of the process
148     pub fn get_id(&self) -> pid_t { self.inner.id() }
149
150     /**
151      * Returns an io::Writer that can be used to write to this Process's stdin.
152      *
153      * Fails if there is no stdin available (it's already been removed by
154      * take_input)
155      */
156     pub fn input<'a>(&'a mut self) -> &'a mut io::Writer {
157         self.inner.io[0].get_mut_ref() as &mut io::Writer
158     }
159
160     /**
161      * Returns an io::Reader that can be used to read from this Process's stdout.
162      *
163      * Fails if there is no stdout available (it's already been removed by
164      * take_output)
165      */
166     pub fn output<'a>(&'a mut self) -> &'a mut io::Reader {
167         self.inner.io[1].get_mut_ref() as &mut io::Reader
168     }
169
170     /**
171      * Returns an io::Reader that can be used to read from this Process's stderr.
172      *
173      * Fails if there is no stderr available (it's already been removed by
174      * take_error)
175      */
176     pub fn error<'a>(&'a mut self) -> &'a mut io::Reader {
177         self.inner.io[2].get_mut_ref() as &mut io::Reader
178     }
179
180     /**
181      * Closes the handle to the child process's stdin.
182      */
183     pub fn close_input(&mut self) {
184         self.inner.io[0].take();
185     }
186
187     /**
188      * Closes the handle to stdout and stderr.
189      */
190     pub fn close_outputs(&mut self) {
191         self.inner.io[1].take();
192         self.inner.io[2].take();
193     }
194
195     /**
196      * Closes the handle to stdin, waits for the child process to terminate,
197      * and returns the exit code.
198      *
199      * If the child has already been finished then the exit code is returned.
200      */
201     pub fn finish(&mut self) -> ProcessExit { self.inner.wait() }
202
203     /**
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
206      * the exit code.
207      *
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.
210      *
211      * This method will fail if the child process's stdout or stderr streams
212      * were redirected to existing file descriptors.
213      */
214     pub fn finish_with_output(&mut self) -> ProcessOutput {
215         self.close_input();
216         let output = self.inner.io[1].take();
217         let error = self.inner.io[2].take();
218
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();
225
226         do spawn {
227             let _guard = io::ignore_io_error();
228             let mut error = error;
229             match error {
230                 Some(ref mut e) => ch.send((2, e.read_to_end())),
231                 None => ch.send((2, ~[]))
232             }
233         }
234         do spawn {
235             let _guard = io::ignore_io_error();
236             let mut output = output;
237             match output {
238                 Some(ref mut e) => ch_clone.send((1, e.read_to_end())),
239                 None => ch_clone.send((1, ~[]))
240             }
241         }
242
243         let status = self.finish();
244
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);
250             }
251         };
252
253         return ProcessOutput {status: status,
254                               output: outs,
255                               error: errs};
256     }
257
258     /**
259      * Terminates the process, giving it a chance to clean itself up if
260      * this is supported by the operating system.
261      *
262      * On Posix OSs SIGTERM will be sent to the process. On Win32
263      * TerminateProcess(..) will be called.
264      */
265     pub fn destroy(&mut self) {
266         self.inner.signal(io::process::PleaseExitSignal);
267         self.finish();
268     }
269
270     /**
271      * Terminates the process as soon as possible without giving it a
272      * chance to clean itself up.
273      *
274      * On Posix OSs SIGKILL will be sent to the process. On Win32
275      * TerminateProcess(..) will be called.
276      */
277     pub fn force_destroy(&mut self) {
278         self.inner.signal(io::process::MustDieSignal);
279         self.finish();
280     }
281 }
282
283 /**
284  * Spawns a process and waits for it to terminate. The process will
285  * inherit the current stdin/stdout/stderr file descriptors.
286  *
287  * # Arguments
288  *
289  * * prog - The path to an executable
290  * * args - Vector of arguments to pass to the child process
291  *
292  * # Return value
293  *
294  * The process's exit code, or None if the child process could not be started
295  */
296 pub fn process_status(prog: &str, args: &[~str]) -> Option<ProcessExit> {
297     let mut opt_prog = Process::new(prog, args, ProcessOptions {
298         env: None,
299         dir: None,
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) })
303     });
304     match opt_prog {
305         Some(ref mut prog) => Some(prog.finish()),
306         None => None
307     }
308 }
309
310 /**
311  * Spawns a process, records all its output, and waits for it to terminate.
312  *
313  * # Arguments
314  *
315  * * prog - The path to an executable
316  * * args - Vector of arguments to pass to the child process
317  *
318  * # Return value
319  *
320  * The process's stdout/stderr output and exit code, or None if the child process could not be
321  * started.
322  */
323 pub fn process_output(prog: &str, args: &[~str]) -> Option<ProcessOutput> {
324     let mut opt_prog = Process::new(prog, args, ProcessOptions::new());
325     match opt_prog {
326         Some(ref mut prog) => Some(prog.finish_with_output()),
327         None => None
328     }
329 }
330
331 #[cfg(test)]
332 mod tests {
333     use libc::c_int;
334     use option::{Option, None, Some};
335     use os;
336     use path::Path;
337     use run;
338     use str;
339     use task::spawn;
340     use unstable::running_on_valgrind;
341     use io::native::file;
342     use io::{FileNotFound, OtherIoError, Reader, Writer, io_error};
343
344     #[test]
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));
349
350         status = run::process_status("true", []).expect("failed to exec `true`");
351         assert!(status.success());
352     }
353
354     #[test]
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.
358
359         let mut trapped_io_error = false;
360         let opt_outp = io_error::cond.trap(|e| {
361             trapped_io_error = true;
362             // FIXME(#11023)
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", [])
366         });
367         assert!(trapped_io_error);
368         assert!(opt_outp.is_none());
369     }
370
371     #[test]
372     #[cfg(not(target_os="android"))] // FIXME(#10380)
373     fn test_process_output_output() {
374
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);
378
379         assert!(status.success());
380         assert_eq!(output_str.trim().to_owned(), ~"hello");
381         // FIXME #7224
382         if !running_on_valgrind() {
383             assert_eq!(error, ~[]);
384         }
385     }
386
387     #[test]
388     #[cfg(not(target_os="android"))] // FIXME(#10380)
389     fn test_process_output_error() {
390
391         let run::ProcessOutput {status, output, error}
392              = run::process_output("mkdir", [~"."]).expect("failed to exec `mkdir`");
393
394         assert!(status.matches_exit_status(1));
395         assert_eq!(output, ~[]);
396         assert!(!error.is_empty());
397     }
398
399     #[test]
400     #[ignore] // FIXME(#10016) cat never sees stdin close
401     fn test_pipes() {
402
403         let pipe_in = os::pipe();
404         let pipe_out = os::pipe();
405         let pipe_err = os::pipe();
406
407         let mut process = run::Process::new("cat", [], run::ProcessOptions {
408             dir: None,
409             env: None,
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`");
414
415         os::close(pipe_in.input);
416         os::close(pipe_out.out);
417         os::close(pipe_err.out);
418
419         do spawn {
420             writeclose(pipe_in.out, "test");
421         }
422         let actual = readclose(pipe_out.input);
423         readclose(pipe_err.input);
424         process.finish();
425
426         assert_eq!(~"test", actual);
427     }
428
429     fn writeclose(fd: c_int, s: &str) {
430         let mut writer = file::FileDesc::new(fd, true);
431         writer.write(s.as_bytes());
432     }
433
434     fn readclose(fd: c_int) -> ~str {
435         let mut res = ~[];
436         let mut reader = file::FileDesc::new(fd, true);
437         let mut buf = [0, ..1024];
438         loop {
439             match reader.read(buf) {
440                 Some(n) => { res.push_all(buf.slice_to(n)); }
441                 None => break
442             }
443         }
444         str::from_utf8_owned(res)
445     }
446
447     #[test]
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));
453     }
454
455     #[test]
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));
462     }
463
464     #[test]
465     #[cfg(not(target_os="android"))] // FIXME(#10380)
466     fn test_finish_with_output_once() {
467
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);
473
474         assert!(status.success());
475         assert_eq!(output_str.trim().to_owned(), ~"hello");
476         // FIXME #7224
477         if !running_on_valgrind() {
478             assert_eq!(error, ~[]);
479         }
480     }
481
482     #[test]
483     #[cfg(not(target_os="android"))] // FIXME(#10380)
484     fn test_finish_with_output_twice() {
485
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();
490
491         let output_str = str::from_utf8_owned(output);
492
493         assert!(status.success());
494         assert_eq!(output_str.trim().to_owned(), ~"hello");
495         // FIXME #7224
496         if !running_on_valgrind() {
497             assert_eq!(error, ~[]);
498         }
499
500         let run::ProcessOutput {status, output, error}
501             = prog.finish_with_output();
502
503         assert!(status.success());
504         assert_eq!(output, ~[]);
505         // FIXME #7224
506         if !running_on_valgrind() {
507             assert_eq!(error, ~[]);
508         }
509     }
510
511     #[cfg(unix,not(target_os="android"))]
512     fn run_pwd(dir: Option<&Path>) -> run::Process {
513         run::Process::new("pwd", [], run::ProcessOptions {
514             dir: dir,
515             .. run::ProcessOptions::new()
516         }).expect("failed to exec `pwd`")
517     }
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 {
521             dir: dir,
522             .. run::ProcessOptions::new()
523         }).expect("failed to exec `/system/bin/sh`")
524     }
525
526     #[cfg(windows)]
527     fn run_pwd(dir: Option<&Path>) -> run::Process {
528         run::Process::new("cmd", [~"/c", ~"cd"], run::ProcessOptions {
529             dir: dir,
530             .. run::ProcessOptions::new()
531         }).expect("failed to run `cmd`")
532     }
533
534     #[test]
535     fn test_keep_current_working_dir() {
536         let mut prog = run_pwd(None);
537
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());
541
542         let parent_stat = parent_dir.stat();
543         let child_stat = child_dir.stat();
544
545         assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
546         assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
547     }
548
549     #[test]
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));
555
556         let output = str::from_utf8_owned(prog.finish_with_output().output);
557         let child_dir = Path::new(output.trim());
558
559         let parent_stat = parent_dir.stat();
560         let child_stat = child_dir.stat();
561
562         assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
563         assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
564     }
565
566     #[cfg(unix,not(target_os="android"))]
567     fn run_env(env: Option<~[(~str, ~str)]>) -> run::Process {
568         run::Process::new("env", [], run::ProcessOptions {
569             env: env,
570             .. run::ProcessOptions::new()
571         }).expect("failed to exec `env`")
572     }
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 {
576             env: env,
577             .. run::ProcessOptions::new()
578         }).expect("failed to exec `/system/bin/sh`")
579     }
580
581     #[cfg(windows)]
582     fn run_env(env: Option<~[(~str, ~str)]>) -> run::Process {
583         run::Process::new("cmd", [~"/c", ~"set"], run::ProcessOptions {
584             env: env,
585             .. run::ProcessOptions::new()
586         }).expect("failed to run `cmd`")
587     }
588
589     #[test]
590     #[cfg(not(target_os="android"))]
591     fn test_inherit_env() {
592         if running_on_valgrind() { return; }
593
594         let mut prog = run_env(None);
595         let output = str::from_utf8_owned(prog.finish_with_output().output);
596
597         let r = os::env();
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)));
601         }
602     }
603     #[test]
604     #[cfg(target_os="android")]
605     fn test_inherit_env() {
606         if running_on_valgrind() { return; }
607
608         let mut prog = run_env(None);
609         let output = str::from_utf8_owned(prog.finish_with_output().output);
610
611         let r = os::env();
612         for &(ref k, ref v) in r.iter() {
613             // don't check android RANDOM variables
614             if *k != ~"RANDOM" {
615                 assert!(output.contains(format!("{}={}", *k, *v)) ||
616                         output.contains(format!("{}=\'{}\'", *k, *v)));
617             }
618         }
619     }
620
621     #[test]
622     fn test_add_to_env() {
623
624         let mut new_env = os::env();
625         new_env.push((~"RUN_TEST_NEW_ENV", ~"123"));
626
627         let mut prog = run_env(Some(new_env));
628         let output = str::from_utf8_owned(prog.finish_with_output().output);
629
630         assert!(output.contains("RUN_TEST_NEW_ENV=123"));
631     }
632 }