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
21 use rt::rtio::{RtioProcess, IoFactory, LocalIo};
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;
36 /// Representation of a running or exited child process.
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.
45 /// use std::io::Process;
47 /// let mut child = match Process::new("/bin/cat", [~"file.txt"]) {
48 /// Ok(child) => child,
49 /// Err(e) => fail!("failed to execute child: {}", e),
52 /// let contents = child.stdout.get_mut_ref().read_to_end();
53 /// assert!(child.wait().success());
56 priv handle: ~RtioProcess:Send,
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 stdin: Option<io::PipeStream>,
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 stdout: Option<io::PipeStream>,
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 stderr: Option<io::PipeStream>,
70 /// Extra I/O handles as configured by the original `ProcessConfig` when
71 /// this process was created. This is by default empty.
72 extra_io: ~[Option<io::PipeStream>],
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
81 /// use std::io::ProcessConfig;
83 /// let config = ProcessConfig {
84 /// program: "/bin/sh",
85 /// args: &[~"-c", ~"true"],
86 /// .. ProcessConfig::new()
89 pub struct ProcessConfig<'a> {
90 /// Path to the program to run
93 /// Arguments to pass to the program (doesn't include the program itself)
96 /// Optional environment to specify for the program. If this is None, then
97 /// it will inherit the current process's environment.
98 env: Option<&'a [(~str, ~str)]>,
100 /// Optional working directory for the new process. If this is None, then
101 /// the current directory of the running process is inherited.
102 cwd: Option<&'a Path>,
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
107 stdin: StdioContainer,
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
112 stdout: StdioContainer,
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
117 stderr: StdioContainer,
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
125 extra_io: &'a [StdioContainer],
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
133 /// Similar to `uid`, but sets the group id of the child process. This has
134 /// the same semantics as the `uid` field.
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.
142 /// The output of a finished process.
143 pub struct ProcessOutput {
144 /// The status (exit code) of the process.
146 /// The data that the process wrote to stdout.
148 /// The data that the process wrote to stderr.
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`
158 /// The specified file descriptor is inherited for the stream which it is
160 InheritFd(libc::c_int),
162 /// Creates a pipe for the specified file descriptor which will be created
163 /// when the process is spawned.
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 */),
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.
178 /// Termination by signal, with the signal number.
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 {
186 ExitStatus(code) => write!(f.buf, "exit code: {}", code),
187 ExitSignal(code) => write!(f.buf, "signal: {}", code),
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);
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)
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:
211 /// use std::io::process::{ProcessConfig, Process};
213 /// let config = ProcessConfig {
214 /// program: "/bin/sh",
215 /// args: &[~"-c", ~"echo hello"],
216 /// .. ProcessConfig::new()
219 /// let p = Process::configure(config);
222 pub fn new<'a>() -> ProcessConfig<'a> {
228 stdin: CreatePipe(true, false),
229 stdout: CreatePipe(false, true),
230 stderr: CreatePipe(false, true),
240 /// Creates a new process for the specified program/arguments, using
241 /// otherwise default configuration.
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`.
250 /// use std::io::Process;
252 /// let mut process = match Process::new("sh", &[~"c", ~"echo hello"]) {
254 /// Err(e) => fail!("failed to execute process: {}", e),
257 /// let output = process.stdout.get_mut_ref().read_to_end();
259 pub fn new(prog: &str, args: &[~str]) -> IoResult<Process> {
260 Process::configure(ProcessConfig {
263 .. ProcessConfig::new()
267 /// Executes the specified program with arguments, waiting for it to finish
268 /// and collecting all of its output.
273 /// use std::io::Process;
276 /// let output = match Process::output("cat", [~"foo.txt"]) {
277 /// Ok(output) => output,
278 /// Err(e) => fail!("failed to execute process: {}", e),
281 /// println!("status: {}", output.status);
282 /// println!("stdout: {}", str::from_utf8_lossy(output.output));
283 /// println!("stderr: {}", str::from_utf8_lossy(output.error));
285 pub fn output(prog: &str, args: &[~str]) -> IoResult<ProcessOutput> {
286 Process::new(prog, args).map(|mut p| p.wait_with_output())
289 /// Executes a child process and collects its exit status. This will block
290 /// waiting for the child to exit.
295 /// use std::io::Process;
297 /// let status = match Process::status("ls", []) {
298 /// Ok(status) => status,
299 /// Err(e) => fail!("failed to execute process: {}", e),
302 /// println!("process exited with: {}", status);
304 pub fn status(prog: &str, args: &[~str]) -> IoResult<ProcessExit> {
305 Process::new(prog, args).map(|mut p| p.wait())
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))
318 stdin: io.next().unwrap(),
319 stdout: io.next().unwrap(),
320 stderr: io.next().unwrap(),
321 extra_io: io.collect(),
327 /// Sends `signal` to another process in the system identified by `id`.
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`.
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
337 pub fn kill(id: libc::pid_t, signal: int) -> IoResult<()> {
338 LocalIo::maybe_raise(|io| io.kill(id, signal))
341 /// Returns the process id of this child process
342 pub fn id(&self) -> libc::pid_t { self.handle.id() }
344 /// Sends the specified signal to the child process, returning whether the
345 /// signal could be delivered or not.
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.
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.
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)
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)
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)
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.
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());
385 /// Simultaneously wait for the child to exit and collect all remaining
386 /// output on the stdout/stderr handles, returning a `ProcessOutput`
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<~[u8]>> {
393 let (tx, rx) = channel();
395 Some(stream) => spawn(proc() {
396 let mut stream = stream;
397 tx.send(stream.read_to_end())
399 None => tx.send(Ok(~[]))
403 let stdout = read(self.stdout.take());
404 let stderr = read(self.stderr.take());
406 let status = self.wait();
408 ProcessOutput { status: status,
409 output: stdout.recv().ok().unwrap_or(~[]),
410 error: stderr.recv().ok().unwrap_or(~[]) }
414 impl Drop for Process {
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());
422 match self.extra_io.pop() {
434 use io::process::{ProcessConfig, Process};
437 // FIXME(#10380) these tests should not all be ignored on android.
439 #[cfg(not(target_os="android"))]
441 let args = ProcessConfig {
443 .. ProcessConfig::new()
445 let p = Process::configure(args);
447 let mut p = p.unwrap();
448 assert!(p.wait().success());
451 #[cfg(not(target_os="android"))]
452 iotest!(fn smoke_failure() {
453 let args = ProcessConfig {
454 program: "if-this-is-a-binary-then-the-world-has-ended",
455 .. ProcessConfig::new()
457 match Process::configure(args) {
463 #[cfg(not(target_os="android"))]
464 iotest!(fn exit_reported_right() {
465 let args = ProcessConfig {
467 .. ProcessConfig::new()
469 let p = Process::configure(args);
471 let mut p = p.unwrap();
472 assert!(p.wait().matches_exit_status(1));
473 drop(p.wait().clone());
476 #[cfg(unix, not(target_os="android"))]
477 iotest!(fn signal_reported_right() {
478 let args = ProcessConfig {
480 args: &[~"-c", ~"kill -1 $$"],
481 .. ProcessConfig::new()
483 let p = Process::configure(args);
485 let mut p = p.unwrap();
487 process::ExitSignal(1) => {},
488 result => fail!("not terminated by signal 1 (instead, {})", result),
492 pub fn read_all(input: &mut Reader) -> ~str {
493 input.read_to_str().unwrap()
496 pub fn run_output(args: ProcessConfig) -> ~str {
497 let p = Process::configure(args);
499 let mut p = p.unwrap();
500 assert!(p.stdout.is_some());
501 let ret = read_all(p.stdout.get_mut_ref() as &mut Reader);
502 assert!(p.wait().success());
506 #[cfg(not(target_os="android"))]
507 iotest!(fn stdout_works() {
508 let args = ProcessConfig {
511 stdout: CreatePipe(false, true),
512 .. ProcessConfig::new()
514 assert_eq!(run_output(args), ~"foobar\n");
517 #[cfg(unix, not(target_os="android"))]
518 iotest!(fn set_cwd_works() {
519 let cwd = Path::new("/");
520 let args = ProcessConfig {
522 args: &[~"-c", ~"pwd"],
524 stdout: CreatePipe(false, true),
525 .. ProcessConfig::new()
527 assert_eq!(run_output(args), ~"/\n");
530 #[cfg(unix, not(target_os="android"))]
531 iotest!(fn stdin_works() {
532 let args = ProcessConfig {
534 args: &[~"-c", ~"read line; echo $line"],
535 stdin: CreatePipe(true, false),
536 stdout: CreatePipe(false, true),
537 .. ProcessConfig::new()
539 let mut p = Process::configure(args).unwrap();
540 p.stdin.get_mut_ref().write("foobar".as_bytes()).unwrap();
541 drop(p.stdin.take());
542 let out = read_all(p.stdout.get_mut_ref() as &mut Reader);
543 assert!(p.wait().success());
544 assert_eq!(out, ~"foobar\n");
547 #[cfg(not(target_os="android"))]
548 iotest!(fn detach_works() {
549 let args = ProcessConfig {
552 .. ProcessConfig::new()
554 let mut p = Process::configure(args).unwrap();
555 assert!(p.wait().success());
559 iotest!(fn uid_fails_on_windows() {
560 let args = ProcessConfig {
563 .. ProcessConfig::new()
565 assert!(Process::configure(args).is_err());
568 #[cfg(unix, not(target_os="android"))]
569 iotest!(fn uid_works() {
571 let args = ProcessConfig {
573 args: &[~"-c", ~"true"],
574 uid: Some(unsafe { libc::getuid() as uint }),
575 gid: Some(unsafe { libc::getgid() as uint }),
576 .. ProcessConfig::new()
578 let mut p = Process::configure(args).unwrap();
579 assert!(p.wait().success());
582 #[cfg(unix, not(target_os="android"))]
583 iotest!(fn uid_to_root_fails() {
586 // if we're already root, this isn't a valid test. Most of the bots run
587 // as non-root though (android is an exception).
588 if unsafe { libc::getuid() == 0 } { return }
589 let args = ProcessConfig {
593 .. ProcessConfig::new()
595 assert!(Process::configure(args).is_err());
598 #[cfg(not(target_os="android"))]
599 iotest!(fn test_process_status() {
600 let mut status = Process::status("false", []).unwrap();
601 assert!(status.matches_exit_status(1));
603 status = Process::status("true", []).unwrap();
604 assert!(status.success());
607 iotest!(fn test_process_output_fail_to_start() {
608 match Process::output("/no-binary-by-this-name-should-exist", []) {
609 Err(e) => assert_eq!(e.kind, FileNotFound),
614 #[cfg(not(target_os="android"))]
615 iotest!(fn test_process_output_output() {
617 let ProcessOutput {status, output, error}
618 = Process::output("echo", [~"hello"]).unwrap();
619 let output_str = str::from_utf8_owned(output).unwrap();
621 assert!(status.success());
622 assert_eq!(output_str.trim().to_owned(), ~"hello");
624 if !running_on_valgrind() {
625 assert_eq!(error, ~[]);
629 #[cfg(not(target_os="android"))]
630 iotest!(fn test_process_output_error() {
631 let ProcessOutput {status, output, error}
632 = Process::output("mkdir", [~"."]).unwrap();
634 assert!(status.matches_exit_status(1));
635 assert_eq!(output, ~[]);
636 assert!(!error.is_empty());
639 #[cfg(not(target_os="android"))]
640 iotest!(fn test_finish_once() {
641 let mut prog = Process::new("false", []).unwrap();
642 assert!(prog.wait().matches_exit_status(1));
645 #[cfg(not(target_os="android"))]
646 iotest!(fn test_finish_twice() {
647 let mut prog = Process::new("false", []).unwrap();
648 assert!(prog.wait().matches_exit_status(1));
649 assert!(prog.wait().matches_exit_status(1));
652 #[cfg(not(target_os="android"))]
653 iotest!(fn test_wait_with_output_once() {
655 let mut prog = Process::new("echo", [~"hello"]).unwrap();
656 let ProcessOutput {status, output, error} = prog.wait_with_output();
657 let output_str = str::from_utf8_owned(output).unwrap();
659 assert!(status.success());
660 assert_eq!(output_str.trim().to_owned(), ~"hello");
662 if !running_on_valgrind() {
663 assert_eq!(error, ~[]);
667 #[cfg(not(target_os="android"))]
668 iotest!(fn test_wait_with_output_twice() {
669 let mut prog = Process::new("echo", [~"hello"]).unwrap();
670 let ProcessOutput {status, output, error} = prog.wait_with_output();
672 let output_str = str::from_utf8_owned(output).unwrap();
674 assert!(status.success());
675 assert_eq!(output_str.trim().to_owned(), ~"hello");
677 if !running_on_valgrind() {
678 assert_eq!(error, ~[]);
681 let ProcessOutput {status, output, error} = prog.wait_with_output();
683 assert!(status.success());
684 assert_eq!(output, ~[]);
686 if !running_on_valgrind() {
687 assert_eq!(error, ~[]);
691 #[cfg(unix,not(target_os="android"))]
692 pub fn run_pwd(dir: Option<&Path>) -> Process {
693 Process::configure(ProcessConfig {
696 .. ProcessConfig::new()
699 #[cfg(target_os="android")]
700 pub fn run_pwd(dir: Option<&Path>) -> Process {
701 Process::configure(ProcessConfig {
702 program: "/system/bin/sh",
703 args: &[~"-c",~"pwd"],
704 cwd: dir.map(|a| &*a),
705 .. ProcessConfig::new()
710 pub fn run_pwd(dir: Option<&Path>) -> Process {
711 Process::configure(ProcessConfig {
713 args: &[~"/c", ~"cd"],
714 cwd: dir.map(|a| &*a),
715 .. ProcessConfig::new()
719 iotest!(fn test_keep_current_working_dir() {
721 let mut prog = run_pwd(None);
723 let output = str::from_utf8_owned(prog.wait_with_output().output).unwrap();
724 let parent_dir = os::getcwd();
725 let child_dir = Path::new(output.trim());
727 let parent_stat = parent_dir.stat().unwrap();
728 let child_stat = child_dir.stat().unwrap();
730 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
731 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
734 iotest!(fn test_change_working_directory() {
736 // test changing to the parent of os::getcwd() because we know
737 // the path exists (and os::getcwd() is not expected to be root)
738 let parent_dir = os::getcwd().dir_path();
739 let mut prog = run_pwd(Some(&parent_dir));
741 let output = str::from_utf8_owned(prog.wait_with_output().output).unwrap();
742 let child_dir = Path::new(output.trim());
744 let parent_stat = parent_dir.stat().unwrap();
745 let child_stat = child_dir.stat().unwrap();
747 assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
748 assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
751 #[cfg(unix,not(target_os="android"))]
752 pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
753 Process::configure(ProcessConfig {
755 env: env.as_ref().map(|e| e.as_slice()),
756 .. ProcessConfig::new()
759 #[cfg(target_os="android")]
760 pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
761 Process::configure(ProcessConfig {
762 program: "/system/bin/sh",
763 args: &[~"-c",~"set"],
764 env: env.as_ref().map(|e| e.as_slice()),
765 .. ProcessConfig::new()
770 pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
771 Process::configure(ProcessConfig {
773 args: &[~"/c", ~"set"],
774 env: env.as_ref().map(|e| e.as_slice()),
775 .. ProcessConfig::new()
779 #[cfg(not(target_os="android"))]
780 iotest!(fn test_inherit_env() {
782 if running_on_valgrind() { return; }
784 let mut prog = run_env(None);
785 let output = str::from_utf8_owned(prog.wait_with_output().output).unwrap();
788 for &(ref k, ref v) in r.iter() {
789 // don't check windows magical empty-named variables
790 assert!(k.is_empty() || output.contains(format!("{}={}", *k, *v)));
793 #[cfg(target_os="android")]
794 iotest!(fn test_inherit_env() {
796 if running_on_valgrind() { return; }
798 let mut prog = run_env(None);
799 let output = str::from_utf8_owned(prog.wait_with_output().output).unwrap();
802 for &(ref k, ref v) in r.iter() {
803 // don't check android RANDOM variables
805 assert!(output.contains(format!("{}={}", *k, *v)) ||
806 output.contains(format!("{}=\'{}\'", *k, *v)));
811 iotest!(fn test_add_to_env() {
812 let new_env = ~[(~"RUN_TEST_NEW_ENV", ~"123")];
814 let mut prog = run_env(Some(new_env));
815 let result = prog.wait_with_output();
816 let output = str::from_utf8_lossy(result.output).into_owned();
818 assert!(output.contains("RUN_TEST_NEW_ENV=123"),
819 "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
823 pub fn sleeper() -> Process {
824 Process::new("sleep", [~"1000"]).unwrap()
827 pub fn sleeper() -> Process {
828 // There's a `timeout` command on windows, but it doesn't like having
829 // its output piped, so instead just ping ourselves a few times with
830 // gaps inbetweeen so we're sure this process is alive for awhile
831 Process::new("ping", [~"127.0.0.1", ~"-n", ~"1000"]).unwrap()
834 iotest!(fn test_kill() {
835 let mut p = sleeper();
836 Process::kill(p.id(), PleaseExitSignal).unwrap();
837 assert!(!p.wait().success());
840 iotest!(fn test_exists() {
841 let mut p = sleeper();
842 assert!(Process::kill(p.id(), 0).is_ok());
843 p.signal_kill().unwrap();
844 assert!(!p.wait().success());
847 iotest!(fn test_zero() {
848 let mut p = sleeper();
849 p.signal_kill().unwrap();
850 for _ in range(0, 20) {
851 if p.signal(0).is_err() {
852 assert!(!p.wait().success());
857 fail!("never saw the child go away");