]> git.lizzy.rs Git - rust.git/blob - src/libcore/run.rs
0a8ccc3c5e9b916b7df4b4b0b139f73b802df4b2
[rust.git] / src / libcore / 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 use cast;
13 use io;
14 use libc;
15 use libc::{pid_t, c_void, c_int};
16 use comm::{stream, SharedChan, GenericChan, GenericPort};
17 use option::{Some, None};
18 use os;
19 use prelude::*;
20 use ptr;
21 use str;
22 use task;
23 use vec;
24
25 /// A value representing a child process
26 pub struct Program {
27     priv pid: pid_t,
28     priv handle: *(),
29     priv in_fd: c_int,
30     priv out_file: *libc::FILE,
31     priv err_file: *libc::FILE,
32     priv finished: bool,
33 }
34
35 impl Drop for Program {
36     fn finalize(&self) {
37         // FIXME #4943: transmute is bad.
38         let mut_self: &mut Program = unsafe { cast::transmute(self) };
39
40         mut_self.finish();
41         mut_self.close_outputs();
42         free_handle(self.handle);
43     }
44 }
45
46 pub impl Program {
47
48     /// Returns the process id of the program
49     fn get_id(&mut self) -> pid_t { self.pid }
50
51     /// Returns an io::Writer that can be used to write to stdin
52     fn input(&mut self) -> @io::Writer {
53         io::fd_writer(self.in_fd, false)
54     }
55
56     /// Returns an io::Reader that can be used to read from stdout
57     fn output(&mut self) -> @io::Reader {
58         io::FILE_reader(self.out_file, false)
59     }
60
61     /// Returns an io::Reader that can be used to read from stderr
62     fn err(&mut self) -> @io::Reader {
63         io::FILE_reader(self.err_file, false)
64     }
65
66     /// Closes the handle to the child processes standard input
67     fn close_input(&mut self) {
68         let invalid_fd = -1i32;
69         if self.in_fd != invalid_fd {
70             unsafe {
71                 libc::close(self.in_fd);
72             }
73             self.in_fd = invalid_fd;
74         }
75     }
76
77     priv fn close_outputs(&mut self) {
78         unsafe {
79             fclose_and_null(&mut self.out_file);
80             fclose_and_null(&mut self.err_file);
81         }
82     }
83
84     /**
85      * Waits for the child process to terminate. Closes the handle
86      * to stdin if necessary.
87      */
88     fn finish(&mut self) -> int {
89         if self.finished { return 0; }
90         self.finished = true;
91         self.close_input();
92         return waitpid(self.pid);
93     }
94
95     priv fn destroy_internal(&mut self, force: bool) {
96         killpid(self.pid, force);
97         self.finish();
98         self.close_outputs();
99
100         #[cfg(windows)]
101         fn killpid(pid: pid_t, _force: bool) {
102             unsafe {
103                 libc::funcs::extra::kernel32::TerminateProcess(
104                     cast::transmute(pid), 1);
105             }
106         }
107
108         #[cfg(unix)]
109         fn killpid(pid: pid_t, force: bool) {
110             let signal = if force {
111                 libc::consts::os::posix88::SIGKILL
112             } else {
113                 libc::consts::os::posix88::SIGTERM
114             };
115
116             unsafe {
117                 libc::funcs::posix88::signal::kill(pid, signal as c_int);
118             }
119         }
120     }
121
122     /**
123      * Terminate the program, giving it a chance to clean itself up if
124      * this is supported by the operating system.
125      *
126      * On Posix OSs SIGTERM will be sent to the process. On Win32
127      * TerminateProcess(..) will be called.
128      */
129     fn destroy(&mut self) { self.destroy_internal(false); }
130
131     /**
132      * Terminate the program as soon as possible without giving it a
133      * chance to clean itself up.
134      *
135      * On Posix OSs SIGKILL will be sent to the process. On Win32
136      * TerminateProcess(..) will be called.
137      */
138     fn force_destroy(&mut self) { self.destroy_internal(true); }
139 }
140
141
142 /**
143  * Run a program, providing stdin, stdout and stderr handles
144  *
145  * # Arguments
146  *
147  * * prog - The path to an executable
148  * * args - Vector of arguments to pass to the child process
149  * * env - optional env-modification for child
150  * * dir - optional dir to run child in (default current dir)
151  * * in_fd - A file descriptor for the child to use as std input
152  * * out_fd - A file descriptor for the child to use as std output
153  * * err_fd - A file descriptor for the child to use as std error
154  *
155  * # Return value
156  *
157  * The process id of the spawned process
158  */
159 pub fn spawn_process(prog: &str, args: &[~str],
160                      env: &Option<~[(~str,~str)]>,
161                      dir: &Option<~str>,
162                      in_fd: c_int, out_fd: c_int, err_fd: c_int) -> pid_t {
163
164     let res = spawn_process_internal(prog, args, env, dir, in_fd, out_fd, err_fd);
165     free_handle(res.handle);
166     return res.pid;
167 }
168
169 struct RunProgramResult {
170     // the process id of the program (this should never be negative)
171     pid: pid_t,
172     // a handle to the process - on unix this will always be NULL, but on windows it will be a
173     // HANDLE to the process, which will prevent the pid being re-used until the handle is closed.
174     handle: *(),
175 }
176
177 #[cfg(windows)]
178 fn spawn_process_internal(prog: &str, args: &[~str],
179                           env: &Option<~[(~str,~str)]>,
180                           dir: &Option<~str>,
181                           in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult {
182
183     use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
184     use libc::consts::os::extra::{
185         TRUE, FALSE,
186         STARTF_USESTDHANDLES,
187         INVALID_HANDLE_VALUE,
188         DUPLICATE_SAME_ACCESS
189     };
190     use libc::funcs::extra::kernel32::{
191         GetCurrentProcess,
192         DuplicateHandle,
193         CloseHandle,
194         CreateProcessA
195     };
196     use libc::funcs::extra::msvcrt::get_osfhandle;
197
198     unsafe {
199
200         let mut si = zeroed_startupinfo();
201         si.cb = sys::size_of::<STARTUPINFO>() as DWORD;
202         si.dwFlags = STARTF_USESTDHANDLES;
203
204         let cur_proc = GetCurrentProcess();
205
206         let orig_std_in = get_osfhandle(if in_fd > 0 { in_fd } else { 0 }) as HANDLE;
207         if orig_std_in == INVALID_HANDLE_VALUE as HANDLE {
208             fail!(fmt!("failure in get_osfhandle: %s", os::last_os_error()));
209         }
210         if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput,
211                            0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
212             fail!(fmt!("failure in DuplicateHandle: %s", os::last_os_error()));
213         }
214
215         let orig_std_out = get_osfhandle(if out_fd > 0 { out_fd } else { 1 }) as HANDLE;
216         if orig_std_out == INVALID_HANDLE_VALUE as HANDLE {
217             fail!(fmt!("failure in get_osfhandle: %s", os::last_os_error()));
218         }
219         if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput,
220                            0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
221             fail!(fmt!("failure in DuplicateHandle: %s", os::last_os_error()));
222         }
223
224         let orig_std_err = get_osfhandle(if err_fd > 0 { err_fd } else { 2 }) as HANDLE;
225         if orig_std_err as HANDLE == INVALID_HANDLE_VALUE as HANDLE {
226             fail!(fmt!("failure in get_osfhandle: %s", os::last_os_error()));
227         }
228         if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError,
229                            0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
230             fail!(fmt!("failure in DuplicateHandle: %s", os::last_os_error()));
231         }
232
233         let cmd = make_command_line(prog, args);
234         let mut pi = zeroed_process_information();
235         let mut create_err = None;
236
237         do with_envp(env) |envp| {
238             do with_dirp(dir) |dirp| {
239                 do str::as_c_str(cmd) |cmdp| {
240                     let created = CreateProcessA(ptr::null(), cast::transmute(cmdp),
241                                                  ptr::mut_null(), ptr::mut_null(), TRUE,
242                                                  0, envp, dirp, &mut si, &mut pi);
243                     if created == FALSE {
244                         create_err = Some(os::last_os_error());
245                     }
246                 }
247             }
248         }
249
250         CloseHandle(si.hStdInput);
251         CloseHandle(si.hStdOutput);
252         CloseHandle(si.hStdError);
253
254         for create_err.each |msg| {
255             fail!(fmt!("failure in CreateProcess: %s", *msg));
256         }
257
258         // We close the thread handle because we don't care about keeping the thread id valid,
259         // and we aren't keeping the thread handle around to be able to close it later. We don't
260         // close the process handle however because we want the process id to stay valid at least
261         // until the calling code closes the process handle.
262         CloseHandle(pi.hThread);
263
264         RunProgramResult {
265             pid: pi.dwProcessId as pid_t,
266             handle: pi.hProcess as *()
267         }
268     }
269 }
270
271 #[cfg(windows)]
272 fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
273     libc::types::os::arch::extra::STARTUPINFO {
274         cb: 0,
275         lpReserved: ptr::mut_null(),
276         lpDesktop: ptr::mut_null(),
277         lpTitle: ptr::mut_null(),
278         dwX: 0,
279         dwY: 0,
280         dwXSize: 0,
281         dwYSize: 0,
282         dwXCountChars: 0,
283         dwYCountCharts: 0,
284         dwFillAttribute: 0,
285         dwFlags: 0,
286         wShowWindow: 0,
287         cbReserved2: 0,
288         lpReserved2: ptr::mut_null(),
289         hStdInput: ptr::mut_null(),
290         hStdOutput: ptr::mut_null(),
291         hStdError: ptr::mut_null()
292     }
293 }
294
295 #[cfg(windows)]
296 fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
297     libc::types::os::arch::extra::PROCESS_INFORMATION {
298         hProcess: ptr::mut_null(),
299         hThread: ptr::mut_null(),
300         dwProcessId: 0,
301         dwThreadId: 0
302     }
303 }
304
305 // FIXME: this is only pub so it can be tested (see issue #4536)
306 #[cfg(windows)]
307 pub fn make_command_line(prog: &str, args: &[~str]) -> ~str {
308
309     let mut cmd = ~"";
310     append_arg(&mut cmd, prog);
311     for args.each |arg| {
312         cmd.push_char(' ');
313         append_arg(&mut cmd, *arg);
314     }
315     return cmd;
316
317     fn append_arg(cmd: &mut ~str, arg: &str) {
318         let quote = arg.any(|c| c == ' ' || c == '\t');
319         if quote {
320             cmd.push_char('"');
321         }
322         for uint::range(0, arg.len()) |i| {
323             append_char_at(cmd, arg, i);
324         }
325         if quote {
326             cmd.push_char('"');
327         }
328     }
329
330     fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) {
331         match arg[i] as char {
332             '"' => {
333                 // Escape quotes.
334                 cmd.push_str("\\\"");
335             }
336             '\\' => {
337                 if backslash_run_ends_in_quote(arg, i) {
338                     // Double all backslashes that are in runs before quotes.
339                     cmd.push_str("\\\\");
340                 } else {
341                     // Pass other backslashes through unescaped.
342                     cmd.push_char('\\');
343                 }
344             }
345             c => {
346                 cmd.push_char(c);
347             }
348         }
349     }
350
351     fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool {
352         while i < s.len() && s[i] as char == '\\' {
353             i += 1;
354         }
355         return i < s.len() && s[i] as char == '"';
356     }
357 }
358
359 #[cfg(unix)]
360 fn spawn_process_internal(prog: &str, args: &[~str],
361                           env: &Option<~[(~str,~str)]>,
362                           dir: &Option<~str>,
363                           in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult {
364
365     use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
366     use libc::funcs::bsd44::getdtablesize;
367
368     mod rustrt {
369         use libc::c_void;
370
371         #[abi = "cdecl"]
372         pub extern {
373             unsafe fn rust_unset_sigprocmask();
374             unsafe fn rust_set_environ(envp: *c_void);
375         }
376     }
377     
378     unsafe {
379
380         let pid = fork();
381         if pid < 0 {
382             fail!(fmt!("failure in fork: %s", os::last_os_error()));
383         } else if pid > 0 {
384             return RunProgramResult {pid: pid, handle: ptr::null()};
385         }
386
387         rustrt::rust_unset_sigprocmask();
388
389         if in_fd > 0 && dup2(in_fd, 0) == -1 {
390             fail!(fmt!("failure in dup2(in_fd, 0): %s", os::last_os_error()));
391         }
392         if out_fd > 0 && dup2(out_fd, 1) == -1 {
393             fail!(fmt!("failure in dup2(out_fd, 1): %s", os::last_os_error()));
394         }
395         if err_fd > 0 && dup2(err_fd, 2) == -1 {
396             fail!(fmt!("failure in dup3(err_fd, 2): %s", os::last_os_error()));
397         }
398         // close all other fds
399         for int::range_rev(getdtablesize() as int - 1, 2) |fd| {
400             close(fd as c_int);
401         }
402
403         for dir.each |dir| {
404             do str::as_c_str(*dir) |dirp| {
405                 if chdir(dirp) == -1 {
406                     fail!(fmt!("failure in chdir: %s", os::last_os_error()));
407                 }
408             }
409         }
410
411         do with_envp(env) |envp| {
412             if !envp.is_null() {
413                 rustrt::rust_set_environ(envp);
414             }
415             do with_argv(prog, args) |argv| {
416                 execvp(*argv, argv);
417                 // execvp only returns if an error occurred
418                 fail!(fmt!("failure in execvp: %s", os::last_os_error()));
419             }
420         }
421     }
422 }
423
424 #[cfg(unix)]
425 fn with_argv<T>(prog: &str, args: &[~str],
426                 cb: &fn(**libc::c_char) -> T) -> T {
427     let mut argptrs = str::as_c_str(prog, |b| ~[b]);
428     let mut tmps = ~[];
429     for vec::each(args) |arg| {
430         let t = @copy *arg;
431         tmps.push(t);
432         argptrs.push_all(str::as_c_str(*t, |b| ~[b]));
433     }
434     argptrs.push(ptr::null());
435     vec::as_imm_buf(argptrs, |buf, _len| cb(buf))
436 }
437
438 #[cfg(unix)]
439 fn with_envp<T>(env: &Option<~[(~str,~str)]>,
440                 cb: &fn(*c_void) -> T) -> T {
441     // On posixy systems we can pass a char** for envp, which is
442     // a null-terminated array of "k=v\n" strings.
443     match *env {
444       Some(ref es) if !vec::is_empty(*es) => {
445         let mut tmps = ~[];
446         let mut ptrs = ~[];
447
448         for vec::each(*es) |e| {
449             let (k,v) = copy *e;
450             let t = @(fmt!("%s=%s", k, v));
451             tmps.push(t);
452             ptrs.push_all(str::as_c_str(*t, |b| ~[b]));
453         }
454         ptrs.push(ptr::null());
455         vec::as_imm_buf(ptrs, |p, _len|
456             unsafe { cb(::cast::transmute(p)) }
457         )
458       }
459       _ => cb(ptr::null())
460     }
461 }
462
463 #[cfg(windows)]
464 fn with_envp<T>(env: &Option<~[(~str,~str)]>,
465                 cb: &fn(*mut c_void) -> T) -> T {
466     // On win32 we pass an "environment block" which is not a char**, but
467     // rather a concatenation of null-terminated k=v\0 sequences, with a final
468     // \0 to terminate.
469     unsafe {
470         match *env {
471           Some(ref es) if !vec::is_empty(*es) => {
472             let mut blk : ~[u8] = ~[];
473             for vec::each(*es) |e| {
474                 let (k,v) = copy *e;
475                 let t = fmt!("%s=%s", k, v);
476                 let mut v : ~[u8] = ::cast::transmute(t);
477                 blk += v;
478                 ::cast::forget(v);
479             }
480             blk += ~[0_u8];
481             vec::as_imm_buf(blk, |p, _len| cb(::cast::transmute(p)))
482           }
483           _ => cb(ptr::mut_null())
484         }
485     }
486 }
487
488 #[cfg(windows)]
489 fn with_dirp<T>(d: &Option<~str>,
490                 cb: &fn(*libc::c_char) -> T) -> T {
491     match *d {
492       Some(ref dir) => str::as_c_str(*dir, cb),
493       None => cb(ptr::null())
494     }
495 }
496
497 /// helper function that closes non-NULL files and then makes them NULL
498 priv unsafe fn fclose_and_null(f: &mut *libc::FILE) {
499     if *f != 0 as *libc::FILE {
500         libc::fclose(*f);
501         *f = 0 as *libc::FILE;
502     }
503 }
504
505 #[cfg(windows)]
506 priv fn free_handle(handle: *()) {
507     unsafe {
508         libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle));
509     }
510 }
511
512 #[cfg(unix)]
513 priv fn free_handle(_handle: *()) {
514     // unix has no process handle object, just a pid
515 }
516
517 /**
518  * Spawns a process and waits for it to terminate
519  *
520  * # Arguments
521  *
522  * * prog - The path to an executable
523  * * args - Vector of arguments to pass to the child process
524  *
525  * # Return value
526  *
527  * The process's exit code
528  */
529 pub fn run_program(prog: &str, args: &[~str]) -> int {
530     let res = spawn_process_internal(prog, args, &None, &None,
531                                      0i32, 0i32, 0i32);
532     let code = waitpid(res.pid);
533     free_handle(res.handle);
534     return code;
535 }
536
537 /**
538  * Spawns a process and returns a Program
539  *
540  * The returned value is a <Program> object that can be used for sending and
541  * receiving data over the standard file descriptors.  The class will ensure
542  * that file descriptors are closed properly.
543  *
544  * # Arguments
545  *
546  * * prog - The path to an executable
547  * * args - Vector of arguments to pass to the child process
548  *
549  * # Return value
550  *
551  * A <Program> object
552  */
553 pub fn start_program(prog: &str, args: &[~str]) -> Program {
554     let pipe_input = os::pipe();
555     let pipe_output = os::pipe();
556     let pipe_err = os::pipe();
557     let res =
558         spawn_process_internal(prog, args, &None, &None,
559                                pipe_input.in, pipe_output.out,
560                                pipe_err.out);
561
562     unsafe {
563         libc::close(pipe_input.in);
564         libc::close(pipe_output.out);
565         libc::close(pipe_err.out);
566     }
567
568     Program {
569         pid: res.pid,
570         handle: res.handle,
571         in_fd: pipe_input.out,
572         out_file: os::fdopen(pipe_output.in),
573         err_file: os::fdopen(pipe_err.in),
574         finished: false,
575     }
576 }
577
578 fn read_all(rd: @io::Reader) -> ~str {
579     let buf = io::with_bytes_writer(|wr| {
580         let mut bytes = [0, ..4096];
581         while !rd.eof() {
582             let nread = rd.read(bytes, bytes.len());
583             wr.write(bytes.slice(0, nread));
584         }
585     });
586     str::from_bytes(buf)
587 }
588
589 pub struct ProgramOutput {status: int, out: ~str, err: ~str}
590
591 /**
592  * Spawns a process, waits for it to exit, and returns the exit code, and
593  * contents of stdout and stderr.
594  *
595  * # Arguments
596  *
597  * * prog - The path to an executable
598  * * args - Vector of arguments to pass to the child process
599  *
600  * # Return value
601  *
602  * A record, {status: int, out: str, err: str} containing the exit code,
603  * the contents of stdout and the contents of stderr.
604  */
605 pub fn program_output(prog: &str, args: &[~str]) -> ProgramOutput {
606     let pipe_in = os::pipe();
607     let pipe_out = os::pipe();
608     let pipe_err = os::pipe();
609     let res = spawn_process_internal(prog, args, &None, &None,
610                                      pipe_in.in, pipe_out.out, pipe_err.out);
611
612     os::close(pipe_in.in);
613     os::close(pipe_out.out);
614     os::close(pipe_err.out);
615     os::close(pipe_in.out);
616
617     // Spawn two entire schedulers to read both stdout and sterr
618     // in parallel so we don't deadlock while blocking on one
619     // or the other. FIXME (#2625): Surely there's a much more
620     // clever way to do this.
621     let (p, ch) = stream();
622     let ch = SharedChan::new(ch);
623     let ch_clone = ch.clone();
624     do task::spawn_sched(task::SingleThreaded) {
625         let errput = readclose(pipe_err.in);
626         ch.send((2, errput));
627     };
628     do task::spawn_sched(task::SingleThreaded) {
629         let output = readclose(pipe_out.in);
630         ch_clone.send((1, output));
631     };
632
633     let status = waitpid(res.pid);
634     free_handle(res.handle);
635
636     let mut errs = ~"";
637     let mut outs = ~"";
638     let mut count = 2;
639     while count > 0 {
640         let stream = p.recv();
641         match stream {
642             (1, copy s) => {
643                 outs = s;
644             }
645             (2, copy s) => {
646                 errs = s;
647             }
648             (n, _) => {
649                 fail!(fmt!("program_output received an unexpected file \
650                            number: %u", n));
651             }
652         };
653         count -= 1;
654     };
655     return ProgramOutput {status: status,
656                           out: outs,
657                           err: errs};
658 }
659
660 pub fn writeclose(fd: c_int, s: ~str) {
661     use io::WriterUtil;
662
663     error!("writeclose %d, %s", fd as int, s);
664     let writer = io::fd_writer(fd, false);
665     writer.write_str(s);
666
667     os::close(fd);
668 }
669
670 pub fn readclose(fd: c_int) -> ~str {
671     unsafe {
672         let file = os::fdopen(fd);
673         let reader = io::FILE_reader(file, false);
674         let buf = io::with_bytes_writer(|writer| {
675             let mut bytes = [0, ..4096];
676             while !reader.eof() {
677                 let nread = reader.read(bytes, bytes.len());
678                 writer.write(bytes.slice(0, nread));
679             }
680         });
681         os::fclose(file);
682         str::from_bytes(buf)
683     }
684 }
685
686 /**
687  * Waits for a process to exit and returns the exit code, failing
688  * if there is no process with the specified id.
689  */
690 pub fn waitpid(pid: pid_t) -> int {
691     return waitpid_os(pid);
692
693     #[cfg(windows)]
694     fn waitpid_os(pid: pid_t) -> int {
695
696         use libc::types::os::arch::extra::DWORD;
697         use libc::consts::os::extra::{
698             SYNCHRONIZE,
699             PROCESS_QUERY_INFORMATION,
700             FALSE,
701             STILL_ACTIVE,
702             INFINITE,
703             WAIT_FAILED
704         };
705         use libc::funcs::extra::kernel32::{
706             OpenProcess,
707             GetExitCodeProcess,
708             CloseHandle,
709             WaitForSingleObject
710         };
711
712         unsafe {
713
714             let proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD);
715             if proc.is_null() {
716                 fail!(fmt!("failure in OpenProcess: %s", os::last_os_error()));
717             }
718
719             loop {
720                 let mut status = 0;
721                 if GetExitCodeProcess(proc, &mut status) == FALSE {
722                     CloseHandle(proc);
723                     fail!(fmt!("failure in GetExitCodeProcess: %s", os::last_os_error()));
724                 }
725                 if status != STILL_ACTIVE {
726                     CloseHandle(proc);
727                     return status as int;
728                 }
729                 if WaitForSingleObject(proc, INFINITE) == WAIT_FAILED {
730                     CloseHandle(proc);
731                     fail!(fmt!("failure in WaitForSingleObject: %s", os::last_os_error()));
732                 }
733             }
734         }
735     }
736
737     #[cfg(unix)]
738     fn waitpid_os(pid: pid_t) -> int {
739
740         use libc::funcs::posix01::wait::*;
741
742         #[cfg(target_os = "linux")]
743         #[cfg(target_os = "android")]
744         fn WIFEXITED(status: i32) -> bool {
745             (status & 0xffi32) == 0i32
746         }
747
748         #[cfg(target_os = "macos")]
749         #[cfg(target_os = "freebsd")]
750         fn WIFEXITED(status: i32) -> bool {
751             (status & 0x7fi32) == 0i32
752         }
753
754         #[cfg(target_os = "linux")]
755         #[cfg(target_os = "android")]
756         fn WEXITSTATUS(status: i32) -> i32 {
757             (status >> 8i32) & 0xffi32
758         }
759
760         #[cfg(target_os = "macos")]
761         #[cfg(target_os = "freebsd")]
762         fn WEXITSTATUS(status: i32) -> i32 {
763             status >> 8i32
764         }
765
766         let mut status = 0 as c_int;
767         if unsafe { waitpid(pid, &mut status, 0) } == -1 {
768             fail!(fmt!("failure in waitpid: %s", os::last_os_error()));
769         }
770
771         return if WIFEXITED(status) {
772             WEXITSTATUS(status) as int
773         } else {
774             1
775         };
776     }
777 }
778
779 #[cfg(test)]
780 mod tests {
781     use libc;
782     use option::None;
783     use os;
784     use path::Path;
785     use run::{readclose, writeclose};
786     use run;
787
788     #[test]
789     #[cfg(windows)]
790     fn test_make_command_line() {
791         assert_eq!(
792             run::make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]),
793             ~"prog aaa bbb ccc"
794         );
795         assert_eq!(
796             run::make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]),
797             ~"\"C:\\Program Files\\blah\\blah.exe\" aaa"
798         );
799         assert_eq!(
800             run::make_command_line("C:\\Program Files\\test", [~"aa\"bb"]),
801             ~"\"C:\\Program Files\\test\" aa\\\"bb"
802         );
803         assert_eq!(
804             run::make_command_line("echo", [~"a b c"]),
805             ~"echo \"a b c\""
806         );
807     }
808
809     // Regression test for memory leaks
810     #[test]
811     fn test_leaks() {
812         run::run_program("echo", []);
813         run::start_program("echo", []);
814         run::program_output("echo", []);
815     }
816
817     #[test]
818     #[allow(non_implicitly_copyable_typarams)]
819     fn test_pipes() {
820         let pipe_in = os::pipe();
821         let pipe_out = os::pipe();
822         let pipe_err = os::pipe();
823
824         let pid =
825             run::spawn_process(
826                 "cat", [], &None, &None,
827                 pipe_in.in, pipe_out.out, pipe_err.out);
828         os::close(pipe_in.in);
829         os::close(pipe_out.out);
830         os::close(pipe_err.out);
831
832         if pid == -1i32 { fail!(); }
833         let expected = ~"test";
834         writeclose(pipe_in.out, copy expected);
835         let actual = readclose(pipe_out.in);
836         readclose(pipe_err.in);
837         run::waitpid(pid);
838
839         debug!(copy expected);
840         debug!(copy actual);
841         assert!((expected == actual));
842     }
843
844     #[test]
845     fn waitpid() {
846         let pid = run::spawn_process("false", [],
847                                      &None, &None,
848                                      0i32, 0i32, 0i32);
849         let status = run::waitpid(pid);
850         assert!(status == 1);
851     }
852
853     #[test]
854     #[should_fail]
855     #[ignore(cfg(windows))]
856     fn waitpid_non_existant_pid() {
857         run::waitpid(123456789); // assume that this pid doesn't exist
858     }
859
860     #[test]
861     fn test_destroy_once() {
862         let mut p = run::start_program("echo", []);
863         p.destroy(); // this shouldn't crash (and nor should the destructor)
864     }
865
866     #[test]
867     fn test_destroy_twice() {
868         let mut p = run::start_program("echo", []);
869         p.destroy(); // this shouldnt crash...
870         p.destroy(); // ...and nor should this (and nor should the destructor)
871     }
872
873     #[cfg(unix)] // there is no way to sleep on windows from inside libcore...
874     fn test_destroy_actually_kills(force: bool) {
875         let path = Path(fmt!("test/core-run-test-destroy-actually-kills-%?.tmp", force));
876
877         os::remove_file(&path);
878
879         let cmd = fmt!("sleep 5 && echo MurderDeathKill > %s", path.to_str());
880         let mut p = run::start_program("sh", [~"-c", cmd]);
881
882         p.destroy(); // destroy the program before it has a chance to echo its message
883
884         unsafe {
885             // wait to ensure the program is really destroyed and not just waiting itself
886             libc::sleep(10);
887         }
888
889         // the program should not have had chance to echo its message
890         assert!(!path.exists());
891     }
892
893     #[test]
894     #[cfg(unix)]
895     fn test_unforced_destroy_actually_kills() {
896         test_destroy_actually_kills(false);
897     }
898
899     #[test]
900     #[cfg(unix)]
901     fn test_forced_destroy_actually_kills() {
902         test_destroy_actually_kills(true);
903     }
904 }