(index<- ) ./libstd/io/process.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
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.
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 //! Bindings for executing child processes
12
13 use prelude::*;
14
15 use fmt;
16 use io::IoResult;
17 use io;
18 use libc;
19 use mem;
20 use owned::Box;
21 use rt::rtio::{RtioProcess, IoFactory, LocalIo};
22
23 /// Signal a process to exit, without forcibly killing it. Corresponds to
24 /// SIGTERM on unix platforms.
25 #[cfg(windows)] pub static PleaseExitSignal: int = 15;
26 /// Signal a process to exit immediately, forcibly killing it. Corresponds to
27 /// SIGKILL on unix platforms.
28 #[cfg(windows)] pub static MustDieSignal: int = 9;
29 /// Signal a process to exit, without forcibly killing it. Corresponds to
30 /// SIGTERM on unix platforms.
31 #[cfg(not(windows))] pub static PleaseExitSignal: int = libc::SIGTERM as int;
32 /// Signal a process to exit immediately, forcibly killing it. Corresponds to
33 /// SIGKILL on unix platforms.
34 #[cfg(not(windows))] pub static MustDieSignal: int = libc::SIGKILL as int;
35
36 /// Representation of a running or exited child process.
37 ///
38 /// This structure is used to create, run, and manage child processes. A process
39 /// is configured with the `ProcessConfig` struct which contains specific
40 /// options for dictating how the child is spawned.
41 ///
42 /// # Example
43 ///
44 /// ```should_fail
45 /// use std::io::Process;
46 ///
47 /// let mut child = match Process::new("/bin/cat", ["file.txt".to_owned()]) {
48 /// Ok(child) => child,
49 /// Err(e) => fail!("failed to execute child: {}", e),
50 /// };
51 ///
52 /// let contents = child.stdout.get_mut_ref().read_to_end();
53 /// assert!(child.wait().success());
54 /// ```
55 pub struct Process {
56 handle: Box<RtioProcess:Send>,
57
58 /// Handle to the child's stdin, if the `stdin` field of this process's
59 /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
60 pub stdin: Option<io::PipeStream>,
61
62 /// Handle to the child's stdout, if the `stdout` field of this process's
63 /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
64 pub stdout: Option<io::PipeStream>,
65
66 /// Handle to the child's stderr, if the `stderr` field of this process's
67 /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
68 pub stderr: Option<io::PipeStream>,
69
70 /// Extra I/O handles as configured by the original `ProcessConfig` when
71 /// this process was created. This is by default empty.
72 pub extra_io: Vec<Option<io::PipeStream>>,
73 }
74
75 /// This configuration describes how a new process should be spawned. A blank
76 /// configuration can be created with `ProcessConfig::new()`. It is also
77 /// recommented to use a functional struct update pattern when creating process
78 /// configuration:
79 ///
80 /// ```
81 /// use std::io::ProcessConfig;
82 ///
83 /// let config = ProcessConfig {
84 /// program: "/bin/sh",
85 /// args: &["-c".to_owned(), "true".to_owned()],
86 /// .. ProcessConfig::new()
87 /// };
88 /// ```
89 pub struct ProcessConfig<'a> {
90 /// Path to the program to run
91 pub program: &'a str,
92
93 /// Arguments to pass to the program (doesn't include the program itself)
94 pub args: &'a [~str],
95
96 /// Optional environment to specify for the program. If this is None, then
97 /// it will inherit the current process's environment.
98 pub env: Option<&'a [(~str, ~str)]>,
99
100 /// Optional working directory for the new process. If this is None, then
101 /// the current directory of the running process is inherited.
102 pub cwd: Option<&'a Path>,
103
104 /// Configuration for the child process's stdin handle (file descriptor 0).
105 /// This field defaults to `CreatePipe(true, false)` so the input can be
106 /// written to.
107 pub stdin: StdioContainer,
108
109 /// Configuration for the child process's stdout handle (file descriptor 1).
110 /// This field defaults to `CreatePipe(false, true)` so the output can be
111 /// collected.
112 pub stdout: StdioContainer,
113
114 /// Configuration for the child process's stdout handle (file descriptor 2).
115 /// This field defaults to `CreatePipe(false, true)` so the output can be
116 /// collected.
117 pub stderr: StdioContainer,
118
119 /// Any number of streams/file descriptors/pipes may be attached to this
120 /// process. This list enumerates the file descriptors and such for the
121 /// process to be spawned, and the file descriptors inherited will start at
122 /// 3 and go to the length of this array. The first three file descriptors
123 /// (stdin/stdout/stderr) are configured with the `stdin`, `stdout`, and
124 /// `stderr` fields.
125 pub extra_io: &'a [StdioContainer],
126
127 /// Sets the child process's user id. This translates to a `setuid` call in
128 /// the child process. Setting this value on windows will cause the spawn to
129 /// fail. Failure in the `setuid` call on unix will also cause the spawn to
130 /// fail.
131 pub uid: Option<uint>,
132
133 /// Similar to `uid`, but sets the group id of the child process. This has
134 /// the same semantics as the `uid` field.
135 pub gid: Option<uint>,
136
137 /// If true, the child process is spawned in a detached state. On unix, this
138 /// means that the child is the leader of a new process group.
139 pub detach: bool,
140 }
141
142 /// The output of a finished process.
143 pub struct ProcessOutput {
144 /// The status (exit code) of the process.
145 pub status: ProcessExit,
146 /// The data that the process wrote to stdout.
147 pub output: Vec<u8>,
148 /// The data that the process wrote to stderr.
149 pub error: Vec<u8>,
150 }
151
152 /// Describes what to do with a standard io stream for a child process.
153 pub enum StdioContainer {
154 /// This stream will be ignored. This is the equivalent of attaching the
155 /// stream to `/dev/null`
156 Ignored,
157
158 /// The specified file descriptor is inherited for the stream which it is
159 /// specified for.
160 InheritFd(libc::c_int),
161
162 /// Creates a pipe for the specified file descriptor which will be created
163 /// when the process is spawned.
164 ///
165 /// The first boolean argument is whether the pipe is readable, and the
166 /// second is whether it is writable. These properties are from the view of
167 /// the *child* process, not the parent process.
168 CreatePipe(bool /* readable */, bool /* writable */),
169 }
170
171 /// Describes the result of a process after it has terminated.
172 /// Note that Windows have no signals, so the result is usually ExitStatus.
173 #[deriving(Eq, TotalEq, Clone)]
174 pub enum ProcessExit {
175 /// Normal termination with an exit status.
176 ExitStatus(int),
177
178 /// Termination by signal, with the signal number.
179 ExitSignal(int),
180 }
181
182 impl fmt::Show for ProcessExit {
183 /// Format a ProcessExit enum, to nicely present the information.
184 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 match *self {
186 ExitStatus(code) => write!(f.buf, "exit code: {}", code),
187 ExitSignal(code) => write!(f.buf, "signal: {}", code),
188 }
189 }
190 }
191
192 impl ProcessExit {
193 /// Was termination successful? Signal termination not considered a success,
194 /// and success is defined as a zero exit status.
195 pub fn success(&self) -> bool {
196 return self.matches_exit_status(0);
197 }
198
199 /// Checks whether this ProcessExit matches the given exit status.
200 /// Termination by signal will never match an exit code.
201 pub fn matches_exit_status(&self, wanted: int) -> bool {
202 *self == ExitStatus(wanted)
203 }
204 }
205
206 impl<'a> ProcessConfig<'a> {
207 /// Creates a new configuration with blanks as all of the defaults. This is
208 /// useful when using functional struct updates:
209 ///
210 /// ```rust
211 /// use std::io::process::{ProcessConfig, Process};
212 ///
213 /// let config = ProcessConfig {
214 /// program: "/bin/sh",
215 /// args: &["-c".to_owned(), "echo hello".to_owned()],
216 /// .. ProcessConfig::new()
217 /// };
218 ///
219 /// let p = Process::configure(config);
220 /// ```
221 ///
222 pub fn new<'a>() -> ProcessConfig<'a> {
223 ProcessConfig {
224 program: "",
225 args: &[],
226 env: None,
227 cwd: None,
228 stdin: CreatePipe(true, false),
229 stdout: CreatePipe(false, true),
230 stderr: CreatePipe(false, true),
231 extra_io: &[],
232 uid: None,
233 gid: None,
234 detach: false,
235 }
236 }
237 }
238
239 impl Process {
240 /// Creates a new process for the specified program/arguments, using
241 /// otherwise default configuration.
242 ///
243 /// By default, new processes have their stdin/stdout/stderr handles created
244 /// as pipes the can be manipulated through the respective fields of the
245 /// returned `Process`.
246 ///
247 /// # Example
248 ///
249 /// ```
250 /// use std::io::Process;
251 ///
252 /// let mut process = match Process::new("sh", &["c".to_owned(), "echo hello".to_owned()]) {
253 /// Ok(p) => p,
254 /// Err(e) => fail!("failed to execute process: {}", e),
255 /// };
256 ///
257 /// let output = process.stdout.get_mut_ref().read_to_end();
258 /// ```
259 pub fn new(prog: &str, args: &[~str]) -> IoResult<Process> {
260 Process::configure(ProcessConfig {
261 program: prog,
262 args: args,
263 .. ProcessConfig::new()
264 })
265 }
266
267 /// Executes the specified program with arguments, waiting for it to finish
268 /// and collecting all of its output.
269 ///
270 /// # Example
271 ///
272 /// ```
273 /// use std::io::Process;
274 /// use std::str;
275 ///
276 /// let output = match Process::output("cat", ["foo.txt".to_owned()]) {
277 /// Ok(output) => output,
278 /// Err(e) => fail!("failed to execute process: {}", e),
279 /// };
280 ///
281 /// println!("status: {}", output.status);
282 /// println!("stdout: {}", str::from_utf8_lossy(output.output.as_slice()));
283 /// println!("stderr: {}", str::from_utf8_lossy(output.error.as_slice()));
284 /// ```
285 pub fn output(prog: &str, args: &[~str]) -> IoResult<ProcessOutput> {
286 Process::new(prog, args).map(|mut p| p.wait_with_output())
287 }
288
289 /// Executes a child process and collects its exit status. This will block
290 /// waiting for the child to exit.
291 ///
292 /// # Example
293 ///
294 /// ```
295 /// use std::io::Process;
296 ///
297 /// let status = match Process::status("ls", []) {
298 /// Ok(status) => status,
299 /// Err(e) => fail!("failed to execute process: {}", e),
300 /// };
301 ///
302 /// println!("process exited with: {}", status);
303 /// ```
304 pub fn status(prog: &str, args: &[~str]) -> IoResult<ProcessExit> {
305 Process::new(prog, args).map(|mut p| p.wait())
306 }
307
308 /// Creates a new process with the specified configuration.
309 pub fn configure(config: ProcessConfig) -> IoResult<Process> {
310 let mut config = Some(config);
311 LocalIo::maybe_raise(|io| {
312 io.spawn(config.take_unwrap()).map(|(p, io)| {
313 let mut io = io.move_iter().map(|p| {
314 p.map(|p| io::PipeStream::new(p))
315 });
316 Process {
317 handle: p,
318 stdin: io.next().unwrap(),
319 stdout: io.next().unwrap(),
320 stderr: io.next().unwrap(),
321 extra_io: io.collect(),
322 }
323 })
324 })
325 }
326
327 /// Sends `signal` to another process in the system identified by `id`.
328 ///
329 /// Note that windows doesn't quite have the same model as unix, so some
330 /// unix signals are mapped to windows signals. Notably, unix termination
331 /// signals (SIGTERM/SIGKILL/SIGINT) are translated to `TerminateProcess`.
332 ///
333 /// Additionally, a signal number of 0 can check for existence of the target
334 /// process. Note, though, that on some platforms signals will continue to
335 /// be successfully delivered if the child has exited, but not yet been
336 /// reaped.
337 pub fn kill(id: libc::pid_t, signal: int) -> IoResult<()> {
338 LocalIo::maybe_raise(|io| io.kill(id, signal))
339 }
340
341 /// Returns the process id of this child process
342 pub fn id(&self) -> libc::pid_t { self.handle.id() }
343
344 /// Sends the specified signal to the child process, returning whether the
345 /// signal could be delivered or not.
346 ///
347 /// Note that signal 0 is interpreted as a poll to check whether the child
348 /// process is still alive or not. If an error is returned, then the child
349 /// process has exited.
350 ///
351 /// On some unix platforms signals will continue to be received after a
352 /// child has exited but not yet been reaped. In order to report the status
353 /// of signal delivery correctly, unix implementations may invoke
354 /// `waitpid()` with `WNOHANG` in order to reap the child as necessary.
355 ///
356 /// # Errors
357 ///
358 /// If the signal delivery fails, the corresponding error is returned.
359 pub fn signal(&mut self, signal: int) -> IoResult<()> {
360 self.handle.kill(signal)
361 }
362
363 /// Sends a signal to this child requesting that it exits. This is
364 /// equivalent to sending a SIGTERM on unix platforms.
365 pub fn signal_exit(&mut self) -> IoResult<()> {
366 self.signal(PleaseExitSignal)
367 }
368
369 /// Sends a signal to this child forcing it to exit. This is equivalent to
370 /// sending a SIGKILL on unix platforms.
371 pub fn signal_kill(&mut self) -> IoResult<()> {
372 self.signal(MustDieSignal)
373 }
374
375 /// Wait for the child to exit completely, returning the status that it
376 /// exited with. This function will continue to have the same return value
377 /// after it has been called at least once.
378 ///
379 /// The stdin handle to the child process will be closed before waiting.
380 pub fn wait(&mut self) -> ProcessExit {
381 drop(self.stdin.take());
382 self.handle.wait()
383 }
384
385 /// Simultaneously wait for the child to exit and collect all remaining
386 /// output on the stdout/stderr handles, returning a `ProcessOutput`
387 /// instance.
388 ///
389 /// The stdin handle to the child is closed before waiting.
390 pub fn wait_with_output(&mut self) -> ProcessOutput {
391 drop(self.stdin.take());
392 fn read(stream: Option<io::PipeStream>) -> Receiver<IoResult<Vec<u8>>> {
393 let (tx, rx) = channel();
394 match stream {
395 Some(stream) => spawn(proc() {
396 let mut stream = stream;
397 tx.send(stream.read_to_end())
398 }),
399 None => tx.send(Ok(Vec::new()))
400 }
401 rx
402 }
403 let stdout = read(self.stdout.take());
404 let stderr = read(self.stderr.take());
405
406 let status = self.wait();
407
408 ProcessOutput { status: status,
409 output: stdout.recv().ok().unwrap_or(Vec::new()),
410 error: stderr.recv().ok().unwrap_or(Vec::new()) }
411 }
412 }
413
414 impl Drop for Process {
415 fn drop(&mut self) {
416 // Close all I/O before exiting to ensure that the child doesn't wait
417 // forever to print some text or something similar.
418 drop(self.stdin.take());
419 drop(self.stdout.take());
420 drop(self.stderr.take());
421 drop(mem::replace(&mut self.extra_io, Vec::new()));
422
423 self.wait();
424 }
425 }
426
427 #[cfg(test)]
428 mod tests {
429 use io::process::{ProcessConfig, Process};
430 use prelude::*;
431
432 // FIXME(#10380) these tests should not all be ignored on android.
433
434 #[cfg(not(target_os="android"))]
435 iotest!(fn smoke() {
436 let args = ProcessConfig {
437 program: "true",
438 .. ProcessConfig::new()
439 };
440 let p = Process::configure(args);
441 assert!(p.is_ok());
442 let mut p = p.unwrap();
443 assert!(p.wait().success());
444 })
445
446 #[cfg(not(target_os="android"))]
447 iotest!(fn smoke_failure() {
448 let args = ProcessConfig {
449 program: "if-this-is-a-binary-then-the-world-has-ended",
450 .. ProcessConfig::new()
451 };
452 match Process::configure(args) {
453 Ok(..) => fail!(),
454 Err(..) => {}
455 }
456 })
457
458 #[cfg(not(target_os="android"))]
459 iotest!(fn exit_reported_right() {
460 let args = ProcessConfig {
461 program: "false",
462 .. ProcessConfig::new()
463 };
464 let p = Process::configure(args);
465 assert!(p.is_ok());
466 let mut p = p.unwrap();
467 assert!(p.wait().matches_exit_status(1));
468 drop(p.wait().clone());
469 })
470
471 #[cfg(unix, not(target_os="android"))]
472 iotest!(fn signal_reported_right() {
473 let args = ProcessConfig {
474 program: "/bin/sh",
475 args: &["-c".to_owned(), "kill -1 $$".to_owned()],
476 .. ProcessConfig::new()
477 };
478 let p = Process::configure(args);
479 assert!(p.is_ok());
480 let mut p = p.unwrap();
481 match p.wait() {
482 process::ExitSignal(1) => {},
483 result => fail!("not terminated by signal 1 (instead, {})", result),
484 }
485 })
486
487 pub fn read_all(input: &mut Reader) -> ~str {
488 input.read_to_str().unwrap()
489 }
490
491 pub fn run_output(args: ProcessConfig) -> ~str {
492 let p = Process::configure(args);
493 assert!(p.is_ok());
494 let mut p = p.unwrap();
495 assert!(p.stdout.is_some());
496 let ret = read_all(p.stdout.get_mut_ref() as &mut Reader);
497 assert!(p.wait().success());
498 return ret;
499 }
500
501 #[cfg(not(target_os="android"))]
502 iotest!(fn stdout_works() {
503 let args = ProcessConfig {
504 program: "echo",
505 args: &["foobar".to_owned()],
506 stdout: CreatePipe(false, true),
507 .. ProcessConfig::new()
508 };
509 assert_eq!(run_output(args), "foobar\n".to_owned());
510 })
511
512 #[cfg(unix, not(target_os="android"))]
513 iotest!(fn set_cwd_works() {
514 let cwd = Path::new("/");
515 let args = ProcessConfig {
516 program: "/bin/sh",
517 args: &["-c".to_owned(), "pwd".to_owned()],
518 cwd: Some(&cwd),
519 stdout: CreatePipe(false, true),
520 .. ProcessConfig::new()
521 };
522 assert_eq!(run_output(args), "/\n".to_owned());
523 })
524
525 #[cfg(unix, not(target_os="android"))]
526 iotest!(fn stdin_works() {
527 let args = ProcessConfig {
528 program: "/bin/sh",
529 args: &["-c".to_owned(), "read line; echo $line".to_owned()],
530 stdin: CreatePipe(true, false),
531 stdout: CreatePipe(false, true),
532 .. ProcessConfig::new()
533 };
534 let mut p = Process::configure(args).unwrap();
535 p.stdin.get_mut_ref().write("foobar".as_bytes()).unwrap();
536 drop(p.stdin.take());
537 let out = read_all(p.stdout.get_mut_ref() as &mut Reader);
538 assert!(p.wait().success());
539 assert_eq!(out, "foobar\n".to_owned());
540 })
541
542 #[cfg(not(target_os="android"))]
543 iotest!(fn detach_works() {
544 let args = ProcessConfig {
545 program: "true",
546 detach: true,
547 .. ProcessConfig::new()
548 };
549 let mut p = Process::configure(args).unwrap();
550 assert!(p.wait().success());
551 })
552
553 #[cfg(windows)]
554 iotest!(fn uid_fails_on_windows() {
555 let args = ProcessConfig {
556 program: "test",
557 uid: Some(10),
558 .. ProcessConfig::new()
559 };
560 assert!(Process::configure(args).is_err());
561 })
562
563 #[cfg(unix, not(target_os="android"))]
564 iotest!(fn uid_works() {
565 use libc;
566 let args = ProcessConfig {
567 program: "/bin/sh",
568 args: &["-c".to_owned(), "true".to_owned()],
569 uid: Some(unsafe { libc::getuid() as uint }),
570 gid: Some(unsafe { libc::getgid() as uint }),
571 .. ProcessConfig::new()
572 };
573 let mut p = Process::configure(args).unwrap();
574 assert!(p.wait().success());
575 })
576
577 #[cfg(unix, not(target_os="android"))]
578 iotest!(fn uid_to_root_fails() {
579 use libc;
580
581 // if we're already root, this isn't a valid test. Most of the bots run
582 // as non-root though (android is an exception).
583 if unsafe { libc::getuid() == 0 } { return }
584 let args = ProcessConfig {
585 program: "/bin/ls",
586 uid: Some(0),
587 gid: Some(0),
588 .. ProcessConfig::new()
589 };
590 assert!(Process::configure(args).is_err());
591 })
592
593 #[cfg(not(target_os="android"))]
594 iotest!(fn test_process_status() {
595 let mut status = Process::status("false", []).unwrap();
596 assert!(status.matches_exit_status(1));
597
598 status = Process::status("true", []).unwrap();
599 assert!(status.success());
600 })
601
602 iotest!(fn test_process_output_fail_to_start() {
603 match Process::output("/no-binary-by-this-name-should-exist", []) {
604 Err(e) => assert_eq!(e.kind, FileNotFound),
605 Ok(..) => fail!()
606 }
607 })
608
609 #[cfg(not(target_os="android"))]
610 iotest!(fn test_process_output_output() {
611
612 let ProcessOutput {status, output, error}
613 = Process::output("echo", ["hello".to_owned()]).unwrap();
614 let output_str = str::from_utf8(output.as_slice()).unwrap();
615
616 assert!(status.success());
617 assert_eq!(output_str.trim().to_owned(), "hello".to_owned());
618 // FIXME #7224
619 if !running_on_valgrind() {
620 assert_eq!(error, Vec::new());
621 }
622 })
623
624 #[cfg(not(target_os="android"))]
625 iotest!(fn test_process_output_error() {
626 let ProcessOutput {status, output, error}
627 = Process::output("mkdir", [".".to_owned()]).unwrap();
628
629 assert!(status.matches_exit_status(1));
630 assert_eq!(output, Vec::new());
631 assert!(!error.is_empty());
632 })
633
634 #[cfg(not(target_os="android"))]
635 iotest!(fn test_finish_once() {
636 let mut prog = Process::new("false", []).unwrap();
637 assert!(prog.wait().matches_exit_status(1));
638 })
639
640 #[cfg(not(target_os="android"))]
641 iotest!(fn test_finish_twice() {
642 let mut prog = Process::new("false", []).unwrap();
643 assert!(prog.wait().matches_exit_status(1));
644 assert!(prog.wait().matches_exit_status(1));
645 })
646
647 #[cfg(not(target_os="android"))]
648 iotest!(fn test_wait_with_output_once() {
649
650 let mut prog = Process::new("echo", ["hello".to_owned()]).unwrap();
651 let ProcessOutput {status, output, error} = prog.wait_with_output();
652 let output_str = str::from_utf8(output.as_slice()).unwrap();
653
654 assert!(status.success());
655 assert_eq!(output_str.trim().to_owned(), "hello".to_owned());
656 // FIXME #7224
657 if !running_on_valgrind() {
658 assert_eq!(error, Vec::new());
659 }
660 })
661
662 #[cfg(not(target_os="android"))]
663 iotest!(fn test_wait_with_output_twice() {
664 let mut prog = Process::new("echo", ["hello".to_owned()]).unwrap();
665 let ProcessOutput {status, output, error} = prog.wait_with_output();
666
667 let output_str = str::from_utf8(output.as_slice()).unwrap();
668
669 assert!(status.success());
670 assert_eq!(output_str.trim().to_owned(), "hello".to_owned());
671 // FIXME #7224
672 if !running_on_valgrind() {
673 assert_eq!(error, Vec::new());
674 }
675
676 let ProcessOutput {status, output, error} = prog.wait_with_output();
677
678 assert!(status.success());
679 assert_eq!(output, Vec::new());
680 // FIXME #7224
681 if !running_on_valgrind() {
682 assert_eq!(error, Vec::new());
683 }
684 })
685
686 #[cfg(unix,not(target_os="android"))]
687 pub fn run_pwd(dir: Option<&Path>) -> Process {
688 Process::configure(ProcessConfig {
689 program: "pwd",
690 cwd: dir,
691 .. ProcessConfig::new()
692 }).unwrap()
693 }
694 #[cfg(target_os="android")]
695 pub fn run_pwd(dir: Option<&Path>) -> Process {
696 Process::configure(ProcessConfig {
697 program: "/system/bin/sh",
698 args: &["-c".to_owned(),"pwd".to_owned()],
699 cwd: dir.map(|a| &*a),
700 .. ProcessConfig::new()
701 }).unwrap()
702 }
703
704 #[cfg(windows)]
705 pub fn run_pwd(dir: Option<&Path>) -> Process {
706 Process::configure(ProcessConfig {
707 program: "cmd",
708 args: &["/c".to_owned(), "cd".to_owned()],
709 cwd: dir.map(|a| &*a),
710 .. ProcessConfig::new()
711 }).unwrap()
712 }
713
714 iotest!(fn test_keep_current_working_dir() {
715 use os;
716 let mut prog = run_pwd(None);
717
718 let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
719 let parent_dir = os::getcwd();
720 let child_dir = Path::new(output.trim());
721
722 let parent_stat = parent_dir.stat().unwrap();
723 let child_stat = child_dir.stat().unwrap();
724
725 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
726 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
727 })
728
729 iotest!(fn test_change_working_directory() {
730 use os;
731 // test changing to the parent of os::getcwd() because we know
732 // the path exists (and os::getcwd() is not expected to be root)
733 let parent_dir = os::getcwd().dir_path();
734 let mut prog = run_pwd(Some(&parent_dir));
735
736 let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
737 let child_dir = Path::new(output.trim());
738
739 let parent_stat = parent_dir.stat().unwrap();
740 let child_stat = child_dir.stat().unwrap();
741
742 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
743 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
744 })
745
746 #[cfg(unix,not(target_os="android"))]
747 pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
748 Process::configure(ProcessConfig {
749 program: "env",
750 env: env.as_ref().map(|e| e.as_slice()),
751 .. ProcessConfig::new()
752 }).unwrap()
753 }
754 #[cfg(target_os="android")]
755 pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
756 Process::configure(ProcessConfig {
757 program: "/system/bin/sh",
758 args: &["-c".to_owned(),"set".to_owned()],
759 env: env.as_ref().map(|e| e.as_slice()),
760 .. ProcessConfig::new()
761 }).unwrap()
762 }
763
764 #[cfg(windows)]
765 pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
766 Process::configure(ProcessConfig {
767 program: "cmd",
768 args: &["/c".to_owned(), "set".to_owned()],
769 env: env.as_ref().map(|e| e.as_slice()),
770 .. ProcessConfig::new()
771 }).unwrap()
772 }
773
774 #[cfg(not(target_os="android"))]
775 iotest!(fn test_inherit_env() {
776 use os;
777 if running_on_valgrind() { return; }
778
779 let mut prog = run_env(None);
780 let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
781
782 let r = os::env();
783 for &(ref k, ref v) in r.iter() {
784 // don't check windows magical empty-named variables
785 assert!(k.is_empty() || output.contains(format!("{}={}", *k, *v)));
786 }
787 })
788 #[cfg(target_os="android")]
789 iotest!(fn test_inherit_env() {
790 use os;
791 if running_on_valgrind() { return; }
792
793 let mut prog = run_env(None);
794 let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
795
796 let r = os::env();
797 for &(ref k, ref v) in r.iter() {
798 // don't check android RANDOM variables
799 if *k != "RANDOM".to_owned() {
800 assert!(output.contains(format!("{}={}", *k, *v)) ||
801 output.contains(format!("{}=\'{}\'", *k, *v)));
802 }
803 }
804 })
805
806 iotest!(fn test_add_to_env() {
807 let new_env = box [("RUN_TEST_NEW_ENV".to_owned(), "123".to_owned())];
808
809 let mut prog = run_env(Some(new_env));
810 let result = prog.wait_with_output();
811 let output = str::from_utf8_lossy(result.output.as_slice()).into_owned();
812
813 assert!(output.contains("RUN_TEST_NEW_ENV=123"),
814 "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
815 })
816
817 #[cfg(unix)]
818 pub fn sleeper() -> Process {
819 Process::new("sleep", ["1000".to_owned()]).unwrap()
820 }
821 #[cfg(windows)]
822 pub fn sleeper() -> Process {
823 // There's a `timeout` command on windows, but it doesn't like having
824 // its output piped, so instead just ping ourselves a few times with
825 // gaps inbetweeen so we're sure this process is alive for awhile
826 Process::new("ping", ["127.0.0.1".to_owned(), "-n".to_owned(), "1000".to_owned()]).unwrap()
827 }
828
829 iotest!(fn test_kill() {
830 let mut p = sleeper();
831 Process::kill(p.id(), PleaseExitSignal).unwrap();
832 assert!(!p.wait().success());
833 })
834
835 iotest!(fn test_exists() {
836 let mut p = sleeper();
837 assert!(Process::kill(p.id(), 0).is_ok());
838 p.signal_kill().unwrap();
839 assert!(!p.wait().success());
840 })
841
842 iotest!(fn test_zero() {
843 let mut p = sleeper();
844 p.signal_kill().unwrap();
845 for _ in range(0, 20) {
846 if p.signal(0).is_err() {
847 assert!(!p.wait().success());
848 return
849 }
850 timer::sleep(100);
851 }
852 fail!("never saw the child go away");
853 })
854 }
libstd/io/process.rs:142:38-142:38 -struct- definition:
/// The output of a finished process.
pub struct ProcessOutput {
/// The status (exit code) of the process.
references:- 3408: ProcessOutput { status: status,
409: output: stdout.recv().ok().unwrap_or(Vec::new()),
libstd/io/process.rs:88:8-88:8 -struct- definition:
/// ```
pub struct ProcessConfig<'a> {
/// Path to the program to run
references:- 6222: pub fn new<'a>() -> ProcessConfig<'a> {
223: ProcessConfig {
224: program: "",
--
259: pub fn new(prog: &str, args: &[~str]) -> IoResult<Process> {
260: Process::configure(ProcessConfig {
261: program: prog,
--
308: /// Creates a new process with the specified configuration.
309: pub fn configure(config: ProcessConfig) -> IoResult<Process> {
310: let mut config = Some(config);
libstd/rt/rtio.rs:
191: fn timer_init(&mut self) -> IoResult<Box<RtioTimer:Send>>;
192: fn spawn(&mut self, config: ProcessConfig)
193: -> IoResult<(Box<RtioProcess:Send>,
libstd/io/process.rs:54:8-54:8 -struct- definition:
/// ```
pub struct Process {
handle: Box<RtioProcess:Send>,
references:- 5414: impl Drop for Process {
415: fn drop(&mut self) {
libstd/io/process.rs:152:72-152:72 -enum- definition:
/// Describes what to do with a standard io stream for a child process.
pub enum StdioContainer {
/// This stream will be ignored. This is the equivalent of attaching the
references:- 4124: /// `stderr` fields.
125: pub extra_io: &'a [StdioContainer],
libstd/io/process.rs:173:32-173:32 -enum- definition:
pub enum ProcessExit {
/// Normal termination with an exit status.
ExitStatus(int),
references:- 12172: /// Note that Windows have no signals, so the result is usually ExitStatus.
174: pub enum ProcessExit {
--
182: impl fmt::Show for ProcessExit {
183: /// Format a ProcessExit enum, to nicely present the information.
--
379: /// The stdin handle to the child process will be closed before waiting.
380: pub fn wait(&mut self) -> ProcessExit {
381: drop(self.stdin.take());
libstd/rt/rtio.rs:
276: fn kill(&mut self, signal: int) -> IoResult<()>;
277: fn wait(&mut self) -> ProcessExit;
278: }
libstd/io/process.rs:
192: impl ProcessExit {
193: /// Was termination successful? Signal termination not considered a success,
libstd/io/process.rs:392:8-392:8 -fn- definition:
fn read(stream: Option<io::PipeStream>) -> Receiver<IoResult<Vec<u8>>> {
let (tx, rx) = channel();
match stream {
references:- 2403: let stdout = read(self.stdout.take());
404: let stderr = read(self.stderr.take());