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
20 use rt::rtio::{RtioProcess, IoFactory, LocalIo};
22 /// Signal a process to exit, without forcibly killing it. Corresponds to
23 /// SIGTERM on unix platforms.
24 #[cfg(windows)] pub static PleaseExitSignal: int = 15;
25 /// Signal a process to exit immediately, forcibly killing it. Corresponds to
26 /// SIGKILL on unix platforms.
27 #[cfg(windows)] pub static MustDieSignal: int = 9;
28 /// Signal a process to exit, without forcibly killing it. Corresponds to
29 /// SIGTERM on unix platforms.
30 #[cfg(not(windows))] pub static PleaseExitSignal: int = libc::SIGTERM as int;
31 /// Signal a process to exit immediately, forcibly killing it. Corresponds to
32 /// SIGKILL on unix platforms.
33 #[cfg(not(windows))] pub static MustDieSignal: int = libc::SIGKILL as int;
35 /// Representation of a running or exited child process.
37 /// This structure is used to create, run, and manage child processes. A process
38 /// is configured with the `ProcessConfig` struct which contains specific
39 /// options for dictating how the child is spawned.
44 /// use std::io::Process;
46 /// let mut child = match Process::new("/bin/cat", [~"file.txt"]) {
47 /// Ok(child) => child,
48 /// Err(e) => fail!("failed to execute child: {}", e),
51 /// let contents = child.stdout.get_mut_ref().read_to_end();
52 /// assert!(child.wait().success());
55 handle: ~RtioProcess:Send,
57 /// Handle to the child's stdin, if the `stdin` field of this process's
58 /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
59 pub stdin: Option<io::PipeStream>,
61 /// Handle to the child's stdout, if the `stdout` field of this process's
62 /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
63 pub stdout: Option<io::PipeStream>,
65 /// Handle to the child's stderr, if the `stderr` field of this process's
66 /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
67 pub stderr: Option<io::PipeStream>,
69 /// Extra I/O handles as configured by the original `ProcessConfig` when
70 /// this process was created. This is by default empty.
71 pub extra_io: ~[Option<io::PipeStream>],
74 /// This configuration describes how a new process should be spawned. A blank
75 /// configuration can be created with `ProcessConfig::new()`. It is also
76 /// recommented to use a functional struct update pattern when creating process
80 /// use std::io::ProcessConfig;
82 /// let config = ProcessConfig {
83 /// program: "/bin/sh",
84 /// args: &[~"-c", ~"true"],
85 /// .. ProcessConfig::new()
88 pub struct ProcessConfig<'a> {
89 /// Path to the program to run
92 /// Arguments to pass to the program (doesn't include the program itself)
95 /// Optional environment to specify for the program. If this is None, then
96 /// it will inherit the current process's environment.
97 pub env: Option<&'a [(~str, ~str)]>,
99 /// Optional working directory for the new process. If this is None, then
100 /// the current directory of the running process is inherited.
101 pub cwd: Option<&'a Path>,
103 /// Configuration for the child process's stdin handle (file descriptor 0).
104 /// This field defaults to `CreatePipe(true, false)` so the input can be
106 pub stdin: StdioContainer,
108 /// Configuration for the child process's stdout handle (file descriptor 1).
109 /// This field defaults to `CreatePipe(false, true)` so the output can be
111 pub stdout: StdioContainer,
113 /// Configuration for the child process's stdout handle (file descriptor 2).
114 /// This field defaults to `CreatePipe(false, true)` so the output can be
116 pub stderr: StdioContainer,
118 /// Any number of streams/file descriptors/pipes may be attached to this
119 /// process. This list enumerates the file descriptors and such for the
120 /// process to be spawned, and the file descriptors inherited will start at
121 /// 3 and go to the length of this array. The first three file descriptors
122 /// (stdin/stdout/stderr) are configured with the `stdin`, `stdout`, and
124 pub extra_io: &'a [StdioContainer],
126 /// Sets the child process's user id. This translates to a `setuid` call in
127 /// the child process. Setting this value on windows will cause the spawn to
128 /// fail. Failure in the `setuid` call on unix will also cause the spawn to
130 pub uid: Option<uint>,
132 /// Similar to `uid`, but sets the group id of the child process. This has
133 /// the same semantics as the `uid` field.
134 pub gid: Option<uint>,
136 /// If true, the child process is spawned in a detached state. On unix, this
137 /// means that the child is the leader of a new process group.
141 /// The output of a finished process.
142 pub struct ProcessOutput {
143 /// The status (exit code) of the process.
144 pub status: ProcessExit,
145 /// The data that the process wrote to stdout.
147 /// The data that the process wrote to stderr.
151 /// Describes what to do with a standard io stream for a child process.
152 pub enum StdioContainer {
153 /// This stream will be ignored. This is the equivalent of attaching the
154 /// stream to `/dev/null`
157 /// The specified file descriptor is inherited for the stream which it is
159 InheritFd(libc::c_int),
161 /// Creates a pipe for the specified file descriptor which will be created
162 /// when the process is spawned.
164 /// The first boolean argument is whether the pipe is readable, and the
165 /// second is whether it is writable. These properties are from the view of
166 /// the *child* process, not the parent process.
167 CreatePipe(bool /* readable */, bool /* writable */),
170 /// Describes the result of a process after it has terminated.
171 /// Note that Windows have no signals, so the result is usually ExitStatus.
172 #[deriving(Eq, TotalEq, Clone)]
173 pub enum ProcessExit {
174 /// Normal termination with an exit status.
177 /// Termination by signal, with the signal number.
181 impl fmt::Show for ProcessExit {
182 /// Format a ProcessExit enum, to nicely present the information.
183 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 ExitStatus(code) => write!(f.buf, "exit code: {}", code),
186 ExitSignal(code) => write!(f.buf, "signal: {}", code),
192 /// Was termination successful? Signal termination not considered a success,
193 /// and success is defined as a zero exit status.
194 pub fn success(&self) -> bool {
195 return self.matches_exit_status(0);
198 /// Checks whether this ProcessExit matches the given exit status.
199 /// Termination by signal will never match an exit code.
200 pub fn matches_exit_status(&self, wanted: int) -> bool {
201 *self == ExitStatus(wanted)
205 impl<'a> ProcessConfig<'a> {
206 /// Creates a new configuration with blanks as all of the defaults. This is
207 /// useful when using functional struct updates:
210 /// use std::io::process::{ProcessConfig, Process};
212 /// let config = ProcessConfig {
213 /// program: "/bin/sh",
214 /// args: &[~"-c", ~"echo hello"],
215 /// .. ProcessConfig::new()
218 /// let p = Process::configure(config);
221 pub fn new<'a>() -> ProcessConfig<'a> {
227 stdin: CreatePipe(true, false),
228 stdout: CreatePipe(false, true),
229 stderr: CreatePipe(false, true),
239 /// Creates a new process for the specified program/arguments, using
240 /// otherwise default configuration.
242 /// By default, new processes have their stdin/stdout/stderr handles created
243 /// as pipes the can be manipulated through the respective fields of the
244 /// returned `Process`.
249 /// use std::io::Process;
251 /// let mut process = match Process::new("sh", &[~"c", ~"echo hello"]) {
253 /// Err(e) => fail!("failed to execute process: {}", e),
256 /// let output = process.stdout.get_mut_ref().read_to_end();
258 pub fn new(prog: &str, args: &[~str]) -> IoResult<Process> {
259 Process::configure(ProcessConfig {
262 .. ProcessConfig::new()
266 /// Executes the specified program with arguments, waiting for it to finish
267 /// and collecting all of its output.
272 /// use std::io::Process;
275 /// let output = match Process::output("cat", [~"foo.txt"]) {
276 /// Ok(output) => output,
277 /// Err(e) => fail!("failed to execute process: {}", e),
280 /// println!("status: {}", output.status);
281 /// println!("stdout: {}", str::from_utf8_lossy(output.output.as_slice()));
282 /// println!("stderr: {}", str::from_utf8_lossy(output.error.as_slice()));
284 pub fn output(prog: &str, args: &[~str]) -> IoResult<ProcessOutput> {
285 Process::new(prog, args).map(|mut p| p.wait_with_output())
288 /// Executes a child process and collects its exit status. This will block
289 /// waiting for the child to exit.
294 /// use std::io::Process;
296 /// let status = match Process::status("ls", []) {
297 /// Ok(status) => status,
298 /// Err(e) => fail!("failed to execute process: {}", e),
301 /// println!("process exited with: {}", status);
303 pub fn status(prog: &str, args: &[~str]) -> IoResult<ProcessExit> {
304 Process::new(prog, args).map(|mut p| p.wait())
307 /// Creates a new process with the specified configuration.
308 pub fn configure(config: ProcessConfig) -> IoResult<Process> {
309 let mut config = Some(config);
310 LocalIo::maybe_raise(|io| {
311 io.spawn(config.take_unwrap()).map(|(p, io)| {
312 let mut io = io.move_iter().map(|p| {
313 p.map(|p| io::PipeStream::new(p))
317 stdin: io.next().unwrap(),
318 stdout: io.next().unwrap(),
319 stderr: io.next().unwrap(),
320 extra_io: io.collect(),
326 /// Sends `signal` to another process in the system identified by `id`.
328 /// Note that windows doesn't quite have the same model as unix, so some
329 /// unix signals are mapped to windows signals. Notably, unix termination
330 /// signals (SIGTERM/SIGKILL/SIGINT) are translated to `TerminateProcess`.
332 /// Additionally, a signal number of 0 can check for existence of the target
333 /// process. Note, though, that on some platforms signals will continue to
334 /// be successfully delivered if the child has exited, but not yet been
336 pub fn kill(id: libc::pid_t, signal: int) -> IoResult<()> {
337 LocalIo::maybe_raise(|io| io.kill(id, signal))
340 /// Returns the process id of this child process
341 pub fn id(&self) -> libc::pid_t { self.handle.id() }
343 /// Sends the specified signal to the child process, returning whether the
344 /// signal could be delivered or not.
346 /// Note that signal 0 is interpreted as a poll to check whether the child
347 /// process is still alive or not. If an error is returned, then the child
348 /// process has exited.
350 /// On some unix platforms signals will continue to be received after a
351 /// child has exited but not yet been reaped. In order to report the status
352 /// of signal delivery correctly, unix implementations may invoke
353 /// `waitpid()` with `WNOHANG` in order to reap the child as necessary.
357 /// If the signal delivery fails, the corresponding error is returned.
358 pub fn signal(&mut self, signal: int) -> IoResult<()> {
359 self.handle.kill(signal)
362 /// Sends a signal to this child requesting that it exits. This is
363 /// equivalent to sending a SIGTERM on unix platforms.
364 pub fn signal_exit(&mut self) -> IoResult<()> {
365 self.signal(PleaseExitSignal)
368 /// Sends a signal to this child forcing it to exit. This is equivalent to
369 /// sending a SIGKILL on unix platforms.
370 pub fn signal_kill(&mut self) -> IoResult<()> {
371 self.signal(MustDieSignal)
374 /// Wait for the child to exit completely, returning the status that it
375 /// exited with. This function will continue to have the same return value
376 /// after it has been called at least once.
378 /// The stdin handle to the child process will be closed before waiting.
379 pub fn wait(&mut self) -> ProcessExit {
380 drop(self.stdin.take());
384 /// Simultaneously wait for the child to exit and collect all remaining
385 /// output on the stdout/stderr handles, returning a `ProcessOutput`
388 /// The stdin handle to the child is closed before waiting.
389 pub fn wait_with_output(&mut self) -> ProcessOutput {
390 drop(self.stdin.take());
391 fn read(stream: Option<io::PipeStream>) -> Receiver<IoResult<Vec<u8>>> {
392 let (tx, rx) = channel();
394 Some(stream) => spawn(proc() {
395 let mut stream = stream;
396 tx.send(stream.read_to_end())
398 None => tx.send(Ok(Vec::new()))
402 let stdout = read(self.stdout.take());
403 let stderr = read(self.stderr.take());
405 let status = self.wait();
407 ProcessOutput { status: status,
408 output: stdout.recv().ok().unwrap_or(Vec::new()),
409 error: stderr.recv().ok().unwrap_or(Vec::new()) }
413 impl Drop for Process {
415 // Close all I/O before exiting to ensure that the child doesn't wait
416 // forever to print some text or something similar.
417 drop(self.stdin.take());
418 drop(self.stdout.take());
419 drop(self.stderr.take());
420 drop(mem::replace(&mut self.extra_io, ~[]));
428 use io::process::{ProcessConfig, Process};
431 // FIXME(#10380) these tests should not all be ignored on android.
433 #[cfg(not(target_os="android"))]
435 let args = ProcessConfig {
437 .. ProcessConfig::new()
439 let p = Process::configure(args);
441 let mut p = p.unwrap();
442 assert!(p.wait().success());
445 #[cfg(not(target_os="android"))]
446 iotest!(fn smoke_failure() {
447 let args = ProcessConfig {
448 program: "if-this-is-a-binary-then-the-world-has-ended",
449 .. ProcessConfig::new()
451 match Process::configure(args) {
457 #[cfg(not(target_os="android"))]
458 iotest!(fn exit_reported_right() {
459 let args = ProcessConfig {
461 .. ProcessConfig::new()
463 let p = Process::configure(args);
465 let mut p = p.unwrap();
466 assert!(p.wait().matches_exit_status(1));
467 drop(p.wait().clone());
470 #[cfg(unix, not(target_os="android"))]
471 iotest!(fn signal_reported_right() {
472 let args = ProcessConfig {
474 args: &[~"-c", ~"kill -1 $$"],
475 .. ProcessConfig::new()
477 let p = Process::configure(args);
479 let mut p = p.unwrap();
481 process::ExitSignal(1) => {},
482 result => fail!("not terminated by signal 1 (instead, {})", result),
486 pub fn read_all(input: &mut Reader) -> ~str {
487 input.read_to_str().unwrap()
490 pub fn run_output(args: ProcessConfig) -> ~str {
491 let p = Process::configure(args);
493 let mut p = p.unwrap();
494 assert!(p.stdout.is_some());
495 let ret = read_all(p.stdout.get_mut_ref() as &mut Reader);
496 assert!(p.wait().success());
500 #[cfg(not(target_os="android"))]
501 iotest!(fn stdout_works() {
502 let args = ProcessConfig {
505 stdout: CreatePipe(false, true),
506 .. ProcessConfig::new()
508 assert_eq!(run_output(args), ~"foobar\n");
511 #[cfg(unix, not(target_os="android"))]
512 iotest!(fn set_cwd_works() {
513 let cwd = Path::new("/");
514 let args = ProcessConfig {
516 args: &[~"-c", ~"pwd"],
518 stdout: CreatePipe(false, true),
519 .. ProcessConfig::new()
521 assert_eq!(run_output(args), ~"/\n");
524 #[cfg(unix, not(target_os="android"))]
525 iotest!(fn stdin_works() {
526 let args = ProcessConfig {
528 args: &[~"-c", ~"read line; echo $line"],
529 stdin: CreatePipe(true, false),
530 stdout: CreatePipe(false, true),
531 .. ProcessConfig::new()
533 let mut p = Process::configure(args).unwrap();
534 p.stdin.get_mut_ref().write("foobar".as_bytes()).unwrap();
535 drop(p.stdin.take());
536 let out = read_all(p.stdout.get_mut_ref() as &mut Reader);
537 assert!(p.wait().success());
538 assert_eq!(out, ~"foobar\n");
541 #[cfg(not(target_os="android"))]
542 iotest!(fn detach_works() {
543 let args = ProcessConfig {
546 .. ProcessConfig::new()
548 let mut p = Process::configure(args).unwrap();
549 assert!(p.wait().success());
553 iotest!(fn uid_fails_on_windows() {
554 let args = ProcessConfig {
557 .. ProcessConfig::new()
559 assert!(Process::configure(args).is_err());
562 #[cfg(unix, not(target_os="android"))]
563 iotest!(fn uid_works() {
565 let args = ProcessConfig {
567 args: &[~"-c", ~"true"],
568 uid: Some(unsafe { libc::getuid() as uint }),
569 gid: Some(unsafe { libc::getgid() as uint }),
570 .. ProcessConfig::new()
572 let mut p = Process::configure(args).unwrap();
573 assert!(p.wait().success());
576 #[cfg(unix, not(target_os="android"))]
577 iotest!(fn uid_to_root_fails() {
580 // if we're already root, this isn't a valid test. Most of the bots run
581 // as non-root though (android is an exception).
582 if unsafe { libc::getuid() == 0 } { return }
583 let args = ProcessConfig {
587 .. ProcessConfig::new()
589 assert!(Process::configure(args).is_err());
592 #[cfg(not(target_os="android"))]
593 iotest!(fn test_process_status() {
594 let mut status = Process::status("false", []).unwrap();
595 assert!(status.matches_exit_status(1));
597 status = Process::status("true", []).unwrap();
598 assert!(status.success());
601 iotest!(fn test_process_output_fail_to_start() {
602 match Process::output("/no-binary-by-this-name-should-exist", []) {
603 Err(e) => assert_eq!(e.kind, FileNotFound),
608 #[cfg(not(target_os="android"))]
609 iotest!(fn test_process_output_output() {
611 let ProcessOutput {status, output, error}
612 = Process::output("echo", [~"hello"]).unwrap();
613 let output_str = str::from_utf8(output.as_slice()).unwrap();
615 assert!(status.success());
616 assert_eq!(output_str.trim().to_owned(), ~"hello");
618 if !running_on_valgrind() {
619 assert_eq!(error, Vec::new());
623 #[cfg(not(target_os="android"))]
624 iotest!(fn test_process_output_error() {
625 let ProcessOutput {status, output, error}
626 = Process::output("mkdir", [~"."]).unwrap();
628 assert!(status.matches_exit_status(1));
629 assert_eq!(output, Vec::new());
630 assert!(!error.is_empty());
633 #[cfg(not(target_os="android"))]
634 iotest!(fn test_finish_once() {
635 let mut prog = Process::new("false", []).unwrap();
636 assert!(prog.wait().matches_exit_status(1));
639 #[cfg(not(target_os="android"))]
640 iotest!(fn test_finish_twice() {
641 let mut prog = Process::new("false", []).unwrap();
642 assert!(prog.wait().matches_exit_status(1));
643 assert!(prog.wait().matches_exit_status(1));
646 #[cfg(not(target_os="android"))]
647 iotest!(fn test_wait_with_output_once() {
649 let mut prog = Process::new("echo", [~"hello"]).unwrap();
650 let ProcessOutput {status, output, error} = prog.wait_with_output();
651 let output_str = str::from_utf8(output.as_slice()).unwrap();
653 assert!(status.success());
654 assert_eq!(output_str.trim().to_owned(), ~"hello");
656 if !running_on_valgrind() {
657 assert_eq!(error, Vec::new());
661 #[cfg(not(target_os="android"))]
662 iotest!(fn test_wait_with_output_twice() {
663 let mut prog = Process::new("echo", [~"hello"]).unwrap();
664 let ProcessOutput {status, output, error} = prog.wait_with_output();
666 let output_str = str::from_utf8(output.as_slice()).unwrap();
668 assert!(status.success());
669 assert_eq!(output_str.trim().to_owned(), ~"hello");
671 if !running_on_valgrind() {
672 assert_eq!(error, Vec::new());
675 let ProcessOutput {status, output, error} = prog.wait_with_output();
677 assert!(status.success());
678 assert_eq!(output, Vec::new());
680 if !running_on_valgrind() {
681 assert_eq!(error, Vec::new());
685 #[cfg(unix,not(target_os="android"))]
686 pub fn run_pwd(dir: Option<&Path>) -> Process {
687 Process::configure(ProcessConfig {
690 .. ProcessConfig::new()
693 #[cfg(target_os="android")]
694 pub fn run_pwd(dir: Option<&Path>) -> Process {
695 Process::configure(ProcessConfig {
696 program: "/system/bin/sh",
697 args: &[~"-c",~"pwd"],
698 cwd: dir.map(|a| &*a),
699 .. ProcessConfig::new()
704 pub fn run_pwd(dir: Option<&Path>) -> Process {
705 Process::configure(ProcessConfig {
707 args: &[~"/c", ~"cd"],
708 cwd: dir.map(|a| &*a),
709 .. ProcessConfig::new()
713 iotest!(fn test_keep_current_working_dir() {
715 let mut prog = run_pwd(None);
717 let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
718 let parent_dir = os::getcwd();
719 let child_dir = Path::new(output.trim());
721 let parent_stat = parent_dir.stat().unwrap();
722 let child_stat = child_dir.stat().unwrap();
724 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
725 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
728 iotest!(fn test_change_working_directory() {
730 // test changing to the parent of os::getcwd() because we know
731 // the path exists (and os::getcwd() is not expected to be root)
732 let parent_dir = os::getcwd().dir_path();
733 let mut prog = run_pwd(Some(&parent_dir));
735 let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
736 let child_dir = Path::new(output.trim());
738 let parent_stat = parent_dir.stat().unwrap();
739 let child_stat = child_dir.stat().unwrap();
741 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
742 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
745 #[cfg(unix,not(target_os="android"))]
746 pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
747 Process::configure(ProcessConfig {
749 env: env.as_ref().map(|e| e.as_slice()),
750 .. ProcessConfig::new()
753 #[cfg(target_os="android")]
754 pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
755 Process::configure(ProcessConfig {
756 program: "/system/bin/sh",
757 args: &[~"-c",~"set"],
758 env: env.as_ref().map(|e| e.as_slice()),
759 .. ProcessConfig::new()
764 pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
765 Process::configure(ProcessConfig {
767 args: &[~"/c", ~"set"],
768 env: env.as_ref().map(|e| e.as_slice()),
769 .. ProcessConfig::new()
773 #[cfg(not(target_os="android"))]
774 iotest!(fn test_inherit_env() {
776 if running_on_valgrind() { return; }
778 let mut prog = run_env(None);
779 let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
782 for &(ref k, ref v) in r.iter() {
783 // don't check windows magical empty-named variables
784 assert!(k.is_empty() || output.contains(format!("{}={}", *k, *v)));
787 #[cfg(target_os="android")]
788 iotest!(fn test_inherit_env() {
790 if running_on_valgrind() { return; }
792 let mut prog = run_env(None);
793 let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
796 for &(ref k, ref v) in r.iter() {
797 // don't check android RANDOM variables
799 assert!(output.contains(format!("{}={}", *k, *v)) ||
800 output.contains(format!("{}=\'{}\'", *k, *v)));
805 iotest!(fn test_add_to_env() {
806 let new_env = ~[(~"RUN_TEST_NEW_ENV", ~"123")];
808 let mut prog = run_env(Some(new_env));
809 let result = prog.wait_with_output();
810 let output = str::from_utf8_lossy(result.output.as_slice()).into_owned();
812 assert!(output.contains("RUN_TEST_NEW_ENV=123"),
813 "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
817 pub fn sleeper() -> Process {
818 Process::new("sleep", [~"1000"]).unwrap()
821 pub fn sleeper() -> Process {
822 // There's a `timeout` command on windows, but it doesn't like having
823 // its output piped, so instead just ping ourselves a few times with
824 // gaps inbetweeen so we're sure this process is alive for awhile
825 Process::new("ping", [~"127.0.0.1", ~"-n", ~"1000"]).unwrap()
828 iotest!(fn test_kill() {
829 let mut p = sleeper();
830 Process::kill(p.id(), PleaseExitSignal).unwrap();
831 assert!(!p.wait().success());
834 iotest!(fn test_exists() {
835 let mut p = sleeper();
836 assert!(Process::kill(p.id(), 0).is_ok());
837 p.signal_kill().unwrap();
838 assert!(!p.wait().success());
841 iotest!(fn test_zero() {
842 let mut p = sleeper();
843 p.signal_kill().unwrap();
844 for _ in range(0, 20) {
845 if p.signal(0).is_err() {
846 assert!(!p.wait().success());
851 fail!("never saw the child go away");