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.
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.
15 use libc::{pid_t, c_void, c_int};
16 use comm::{stream, SharedChan, GenericChan, GenericPort};
17 use option::{Some, None};
25 /// A value representing a child process
30 priv out_file: *libc::FILE,
31 priv err_file: *libc::FILE,
35 impl Drop for Program {
37 // FIXME #4943: transmute is bad.
38 let mut_self: &mut Program = unsafe { cast::transmute(self) };
41 mut_self.close_outputs();
42 free_handle(self.handle);
48 /// Returns the process id of the program
49 fn get_id(&mut self) -> pid_t { self.pid }
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)
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)
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)
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 {
71 libc::close(self.in_fd);
73 self.in_fd = invalid_fd;
77 priv fn close_outputs(&mut self) {
79 fclose_and_null(&mut self.out_file);
80 fclose_and_null(&mut self.err_file);
85 * Waits for the child process to terminate. Closes the handle
86 * to stdin if necessary.
88 fn finish(&mut self) -> int {
89 if self.finished { return 0; }
92 return waitpid(self.pid);
95 priv fn destroy_internal(&mut self, force: bool) {
96 killpid(self.pid, force);
101 fn killpid(pid: pid_t, _force: bool) {
103 libc::funcs::extra::kernel32::TerminateProcess(
104 cast::transmute(pid), 1);
109 fn killpid(pid: pid_t, force: bool) {
110 let signal = if force {
111 libc::consts::os::posix88::SIGKILL
113 libc::consts::os::posix88::SIGTERM
117 libc::funcs::posix88::signal::kill(pid, signal as c_int);
123 * Terminate the program, giving it a chance to clean itself up if
124 * this is supported by the operating system.
126 * On Posix OSs SIGTERM will be sent to the process. On Win32
127 * TerminateProcess(..) will be called.
129 fn destroy(&mut self) { self.destroy_internal(false); }
132 * Terminate the program as soon as possible without giving it a
133 * chance to clean itself up.
135 * On Posix OSs SIGKILL will be sent to the process. On Win32
136 * TerminateProcess(..) will be called.
138 fn force_destroy(&mut self) { self.destroy_internal(true); }
143 * Run a program, providing stdin, stdout and stderr handles
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
157 * The process id of the spawned process
159 pub fn spawn_process(prog: &str, args: &[~str],
160 env: &Option<~[(~str,~str)]>,
162 in_fd: c_int, out_fd: c_int, err_fd: c_int) -> pid_t {
164 let res = spawn_process_internal(prog, args, env, dir, in_fd, out_fd, err_fd);
165 free_handle(res.handle);
169 struct RunProgramResult {
170 // the process id of the program (this should never be negative)
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.
178 fn spawn_process_internal(prog: &str, args: &[~str],
179 env: &Option<~[(~str,~str)]>,
181 in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult {
183 use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
184 use libc::consts::os::extra::{
186 STARTF_USESTDHANDLES,
187 INVALID_HANDLE_VALUE,
188 DUPLICATE_SAME_ACCESS
190 use libc::funcs::extra::kernel32::{
196 use libc::funcs::extra::msvcrt::get_osfhandle;
200 let mut si = zeroed_startupinfo();
201 si.cb = sys::size_of::<STARTUPINFO>() as DWORD;
202 si.dwFlags = STARTF_USESTDHANDLES;
204 let cur_proc = GetCurrentProcess();
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()));
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()));
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()));
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()));
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()));
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()));
233 let cmd = make_command_line(prog, args);
234 let mut pi = zeroed_process_information();
235 let mut create_err = None;
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());
250 CloseHandle(si.hStdInput);
251 CloseHandle(si.hStdOutput);
252 CloseHandle(si.hStdError);
254 for create_err.each |msg| {
255 fail!(fmt!("failure in CreateProcess: %s", *msg));
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);
265 pid: pi.dwProcessId as pid_t,
266 handle: pi.hProcess as *()
272 fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
273 libc::types::os::arch::extra::STARTUPINFO {
275 lpReserved: ptr::mut_null(),
276 lpDesktop: ptr::mut_null(),
277 lpTitle: ptr::mut_null(),
288 lpReserved2: ptr::mut_null(),
289 hStdInput: ptr::mut_null(),
290 hStdOutput: ptr::mut_null(),
291 hStdError: ptr::mut_null()
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(),
305 // FIXME: this is only pub so it can be tested (see issue #4536)
307 pub fn make_command_line(prog: &str, args: &[~str]) -> ~str {
310 append_arg(&mut cmd, prog);
311 for args.each |arg| {
313 append_arg(&mut cmd, *arg);
317 fn append_arg(cmd: &mut ~str, arg: &str) {
318 let quote = arg.any(|c| c == ' ' || c == '\t');
322 for uint::range(0, arg.len()) |i| {
323 append_char_at(cmd, arg, i);
330 fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) {
331 match arg[i] as char {
334 cmd.push_str("\\\"");
337 if backslash_run_ends_in_quote(arg, i) {
338 // Double all backslashes that are in runs before quotes.
339 cmd.push_str("\\\\");
341 // Pass other backslashes through unescaped.
351 fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool {
352 while i < s.len() && s[i] as char == '\\' {
355 return i < s.len() && s[i] as char == '"';
360 fn spawn_process_internal(prog: &str, args: &[~str],
361 env: &Option<~[(~str,~str)]>,
363 in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult {
365 use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
366 use libc::funcs::bsd44::getdtablesize;
373 unsafe fn rust_unset_sigprocmask();
374 unsafe fn rust_set_environ(envp: *c_void);
382 fail!(fmt!("failure in fork: %s", os::last_os_error()));
384 return RunProgramResult {pid: pid, handle: ptr::null()};
387 rustrt::rust_unset_sigprocmask();
389 if in_fd > 0 && dup2(in_fd, 0) == -1 {
390 fail!(fmt!("failure in dup2(in_fd, 0): %s", os::last_os_error()));
392 if out_fd > 0 && dup2(out_fd, 1) == -1 {
393 fail!(fmt!("failure in dup2(out_fd, 1): %s", os::last_os_error()));
395 if err_fd > 0 && dup2(err_fd, 2) == -1 {
396 fail!(fmt!("failure in dup3(err_fd, 2): %s", os::last_os_error()));
398 // close all other fds
399 for int::range_rev(getdtablesize() as int - 1, 2) |fd| {
404 do str::as_c_str(*dir) |dirp| {
405 if chdir(dirp) == -1 {
406 fail!(fmt!("failure in chdir: %s", os::last_os_error()));
411 do with_envp(env) |envp| {
413 rustrt::rust_set_environ(envp);
415 do with_argv(prog, args) |argv| {
417 // execvp only returns if an error occurred
418 fail!(fmt!("failure in execvp: %s", os::last_os_error()));
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]);
429 for vec::each(args) |arg| {
432 argptrs.push_all(str::as_c_str(*t, |b| ~[b]));
434 argptrs.push(ptr::null());
435 vec::as_imm_buf(argptrs, |buf, _len| cb(buf))
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.
444 Some(ref es) if !vec::is_empty(*es) => {
448 for vec::each(*es) |e| {
450 let t = @(fmt!("%s=%s", k, v));
452 ptrs.push_all(str::as_c_str(*t, |b| ~[b]));
454 ptrs.push(ptr::null());
455 vec::as_imm_buf(ptrs, |p, _len|
456 unsafe { cb(::cast::transmute(p)) }
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
471 Some(ref es) if !vec::is_empty(*es) => {
472 let mut blk : ~[u8] = ~[];
473 for vec::each(*es) |e| {
475 let t = fmt!("%s=%s", k, v);
476 let mut v : ~[u8] = ::cast::transmute(t);
481 vec::as_imm_buf(blk, |p, _len| cb(::cast::transmute(p)))
483 _ => cb(ptr::mut_null())
489 fn with_dirp<T>(d: &Option<~str>,
490 cb: &fn(*libc::c_char) -> T) -> T {
492 Some(ref dir) => str::as_c_str(*dir, cb),
493 None => cb(ptr::null())
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 {
501 *f = 0 as *libc::FILE;
506 priv fn free_handle(handle: *()) {
508 libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle));
513 priv fn free_handle(_handle: *()) {
514 // unix has no process handle object, just a pid
518 * Spawns a process and waits for it to terminate
522 * * prog - The path to an executable
523 * * args - Vector of arguments to pass to the child process
527 * The process's exit code
529 pub fn run_program(prog: &str, args: &[~str]) -> int {
530 let res = spawn_process_internal(prog, args, &None, &None,
532 let code = waitpid(res.pid);
533 free_handle(res.handle);
538 * Spawns a process and returns a Program
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.
546 * * prog - The path to an executable
547 * * args - Vector of arguments to pass to the child process
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();
558 spawn_process_internal(prog, args, &None, &None,
559 pipe_input.in, pipe_output.out,
563 libc::close(pipe_input.in);
564 libc::close(pipe_output.out);
565 libc::close(pipe_err.out);
571 in_fd: pipe_input.out,
572 out_file: os::fdopen(pipe_output.in),
573 err_file: os::fdopen(pipe_err.in),
578 fn read_all(rd: @io::Reader) -> ~str {
579 let buf = io::with_bytes_writer(|wr| {
580 let mut bytes = [0, ..4096];
582 let nread = rd.read(bytes, bytes.len());
583 wr.write(bytes.slice(0, nread));
589 pub struct ProgramOutput {status: int, out: ~str, err: ~str}
592 * Spawns a process, waits for it to exit, and returns the exit code, and
593 * contents of stdout and stderr.
597 * * prog - The path to an executable
598 * * args - Vector of arguments to pass to the child process
602 * A record, {status: int, out: str, err: str} containing the exit code,
603 * the contents of stdout and the contents of stderr.
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);
612 os::close(pipe_in.in);
613 os::close(pipe_out.out);
614 os::close(pipe_err.out);
615 os::close(pipe_in.out);
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));
628 do task::spawn_sched(task::SingleThreaded) {
629 let output = readclose(pipe_out.in);
630 ch_clone.send((1, output));
633 let status = waitpid(res.pid);
634 free_handle(res.handle);
640 let stream = p.recv();
649 fail!(fmt!("program_output received an unexpected file \
655 return ProgramOutput {status: status,
660 pub fn writeclose(fd: c_int, s: ~str) {
663 error!("writeclose %d, %s", fd as int, s);
664 let writer = io::fd_writer(fd, false);
670 pub fn readclose(fd: c_int) -> ~str {
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));
687 * Waits for a process to exit and returns the exit code, failing
688 * if there is no process with the specified id.
690 pub fn waitpid(pid: pid_t) -> int {
691 return waitpid_os(pid);
694 fn waitpid_os(pid: pid_t) -> int {
696 use libc::types::os::arch::extra::DWORD;
697 use libc::consts::os::extra::{
699 PROCESS_QUERY_INFORMATION,
705 use libc::funcs::extra::kernel32::{
714 let proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD);
716 fail!(fmt!("failure in OpenProcess: %s", os::last_os_error()));
721 if GetExitCodeProcess(proc, &mut status) == FALSE {
723 fail!(fmt!("failure in GetExitCodeProcess: %s", os::last_os_error()));
725 if status != STILL_ACTIVE {
727 return status as int;
729 if WaitForSingleObject(proc, INFINITE) == WAIT_FAILED {
731 fail!(fmt!("failure in WaitForSingleObject: %s", os::last_os_error()));
738 fn waitpid_os(pid: pid_t) -> int {
740 use libc::funcs::posix01::wait::*;
742 #[cfg(target_os = "linux")]
743 #[cfg(target_os = "android")]
744 fn WIFEXITED(status: i32) -> bool {
745 (status & 0xffi32) == 0i32
748 #[cfg(target_os = "macos")]
749 #[cfg(target_os = "freebsd")]
750 fn WIFEXITED(status: i32) -> bool {
751 (status & 0x7fi32) == 0i32
754 #[cfg(target_os = "linux")]
755 #[cfg(target_os = "android")]
756 fn WEXITSTATUS(status: i32) -> i32 {
757 (status >> 8i32) & 0xffi32
760 #[cfg(target_os = "macos")]
761 #[cfg(target_os = "freebsd")]
762 fn WEXITSTATUS(status: i32) -> i32 {
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()));
771 return if WIFEXITED(status) {
772 WEXITSTATUS(status) as int
785 use run::{readclose, writeclose};
790 fn test_make_command_line() {
792 run::make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]),
796 run::make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]),
797 ~"\"C:\\Program Files\\blah\\blah.exe\" aaa"
800 run::make_command_line("C:\\Program Files\\test", [~"aa\"bb"]),
801 ~"\"C:\\Program Files\\test\" aa\\\"bb"
804 run::make_command_line("echo", [~"a b c"]),
809 // Regression test for memory leaks
812 run::run_program("echo", []);
813 run::start_program("echo", []);
814 run::program_output("echo", []);
818 #[allow(non_implicitly_copyable_typarams)]
820 let pipe_in = os::pipe();
821 let pipe_out = os::pipe();
822 let pipe_err = os::pipe();
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);
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);
839 debug!(copy expected);
841 assert!((expected == actual));
846 let pid = run::spawn_process("false", [],
849 let status = run::waitpid(pid);
850 assert!(status == 1);
855 #[ignore(cfg(windows))]
856 fn waitpid_non_existant_pid() {
857 run::waitpid(123456789); // assume that this pid doesn't exist
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)
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)
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));
877 os::remove_file(&path);
879 let cmd = fmt!("sleep 5 && echo MurderDeathKill > %s", path.to_str());
880 let mut p = run::start_program("sh", [~"-c", cmd]);
882 p.destroy(); // destroy the program before it has a chance to echo its message
885 // wait to ensure the program is really destroyed and not just waiting itself
889 // the program should not have had chance to echo its message
890 assert!(!path.exists());
895 fn test_unforced_destroy_actually_kills() {
896 test_destroy_actually_kills(false);
901 fn test_forced_destroy_actually_kills() {
902 test_destroy_actually_kills(true);