about summary refs log tree commit diff
path: root/src/libstd/run_program.rs
diff options
context:
space:
mode:
authorGraydon Hoare <graydon@mozilla.com>2011-12-05 16:46:37 -0800
committerGraydon Hoare <graydon@mozilla.com>2011-12-06 12:13:04 -0800
commit447414f00774d37d934867f5a476cf00e1f95423 (patch)
treed328aa66e62f345cb5d7c7d01c8215e8c9324e79 /src/libstd/run_program.rs
parentb513a5a5001b850a153db12d9621d00a70ff929a (diff)
downloadrust-447414f00774d37d934867f5a476cf00e1f95423.tar.gz
rust-447414f00774d37d934867f5a476cf00e1f95423.zip
Establish 'core' library separate from 'std'.
Diffstat (limited to 'src/libstd/run_program.rs')
-rw-r--r--src/libstd/run_program.rs304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/libstd/run_program.rs b/src/libstd/run_program.rs
new file mode 100644
index 00000000000..6f4ad2da3d6
--- /dev/null
+++ b/src/libstd/run_program.rs
@@ -0,0 +1,304 @@
+/*
+Module: run
+
+Process spawning
+*/
+import str::sbuf;
+import ctypes::{fd_t, pid_t};
+
+export program;
+export run_program;
+export start_program;
+export program_output;
+export spawn_process;
+export waitpid;
+
+#[abi = "cdecl"]
+native mod rustrt {
+    fn rust_run_program(argv: *sbuf, in_fd: fd_t,
+                        out_fd: fd_t, err_fd: fd_t) -> pid_t;
+}
+
+/* Section: Types */
+
+/*
+Resource: program_res
+
+A resource that manages the destruction of a <program> object
+
+program_res ensures that the destroy method is called on a
+program object in order to close open file descriptors.
+*/
+resource program_res(p: program) { p.destroy(); }
+
+/*
+Obj: program
+
+An object representing a child process
+*/
+type program = obj {
+    /*
+    Method: get_id
+
+    Returns the process id of the program
+    */
+    fn get_id() -> pid_t;
+
+    /*
+    Method: input
+
+    Returns an io::writer that can be used to write to stdin
+    */
+    fn input() -> io::writer;
+
+    /*
+    Method: output
+
+    Returns an io::reader that can be used to read from stdout
+    */
+    fn output() -> io::reader;
+
+    /*
+    Method: err
+
+    Returns an io::reader that can be used to read from stderr
+    */
+    fn err() -> io::reader;
+
+    /*
+    Method: close_input
+
+    Closes the handle to the child processes standard input
+    */
+    fn close_input();
+
+    /*
+    Method: finish
+
+    Waits for the child process to terminate. Closes the handle
+    to stdin if necessary.
+    */
+    fn finish() -> int;
+
+    /*
+    Method: destroy
+
+    Closes open handles
+    */
+    fn destroy();
+};
+
+
+/* Section: Operations */
+
+fn arg_vec(prog: str, args: [@str]) -> [sbuf] {
+    let argptrs = str::as_buf(prog, {|buf| [buf] });
+    for arg in args { argptrs += str::as_buf(*arg, {|buf| [buf] }); }
+    argptrs += [ptr::null()];
+    ret argptrs;
+}
+
+/*
+Function: spawn_process
+
+Run a program, providing stdin, stdout and stderr handles
+
+Parameters:
+
+prog - The path to an executable
+args - Vector of arguments to pass to the child process
+in_fd - A file descriptor for the child to use as std input
+out_fd - A file descriptor for the child to use as std output
+err_fd - A file descriptor for the child to use as std error
+
+Returns:
+
+The process id of the spawned process
+*/
+fn spawn_process(prog: str, args: [str], in_fd: fd_t,
+                 out_fd: fd_t, err_fd: fd_t)
+   -> pid_t unsafe {
+    // Note: we have to hold on to these vector references while we hold a
+    // pointer to their buffers
+    let prog = prog;
+    let args = vec::map({|arg| @arg }, args);
+    let argv = arg_vec(prog, args);
+    let pid =
+        rustrt::rust_run_program(vec::unsafe::to_ptr(argv), in_fd, out_fd,
+                                 err_fd);
+    ret pid;
+}
+
+/*
+Function: run_program
+
+Spawns a process and waits for it to terminate
+
+Parameters:
+
+prog - The path to an executable
+args - Vector of arguments to pass to the child process
+
+Returns:
+
+The process id
+*/
+fn run_program(prog: str, args: [str]) -> int {
+    ret waitpid(spawn_process(prog, args, 0i32, 0i32, 0i32));
+}
+
+/*
+Function: start_program
+
+Spawns a process and returns a boxed <program_res>
+
+The returned value is a boxed resource containing a <program> object that can
+be used for sending and recieving data over the standard file descriptors.
+The resource will ensure that file descriptors are closed properly.
+
+Parameters:
+
+prog - The path to an executable
+args - Vector of arguments to pass to the child process
+
+Returns:
+
+A boxed resource of <program>
+*/
+fn start_program(prog: str, args: [str]) -> @program_res {
+    let pipe_input = os::pipe();
+    let pipe_output = os::pipe();
+    let pipe_err = os::pipe();
+    let pid =
+        spawn_process(prog, args, pipe_input.in, pipe_output.out,
+                      pipe_err.out);
+
+    if pid == -1i32 { fail; }
+    os::libc::close(pipe_input.in);
+    os::libc::close(pipe_output.out);
+    os::libc::close(pipe_err.out);
+    obj new_program(pid: pid_t,
+                    mutable in_fd: fd_t,
+                    out_file: os::libc::FILE,
+                    err_file: os::libc::FILE,
+                    mutable finished: bool) {
+        fn get_id() -> pid_t { ret pid; }
+        fn input() -> io::writer {
+            ret io::new_writer(io::fd_buf_writer(in_fd, option::none));
+        }
+        fn output() -> io::reader {
+            ret io::new_reader(io::FILE_buf_reader(out_file, option::none));
+        }
+        fn err() -> io::reader {
+            ret io::new_reader(io::FILE_buf_reader(err_file, option::none));
+        }
+        fn close_input() {
+            let invalid_fd = -1i32;
+            if in_fd != invalid_fd {
+                os::libc::close(in_fd);
+                in_fd = invalid_fd;
+            }
+        }
+        fn finish() -> int {
+            if finished { ret 0; }
+            finished = true;
+            self.close_input();
+            ret waitpid(pid);
+        }
+        fn destroy() {
+            self.finish();
+            os::libc::fclose(out_file);
+            os::libc::fclose(err_file);
+        }
+    }
+    ret @program_res(new_program(pid, pipe_input.out,
+                                 os::fd_FILE(pipe_output.in),
+                                 os::fd_FILE(pipe_err.in), false));
+}
+
+fn read_all(rd: io::reader) -> str {
+    let buf = "";
+    while !rd.eof() {
+        let bytes = rd.read_bytes(4096u);
+        buf += str::unsafe_from_bytes(bytes);
+    }
+    ret buf;
+}
+
+/*
+Function: program_output
+
+Spawns a process, waits for it to exit, and returns the exit code, and
+contents of stdout and stderr.
+
+Parameters:
+
+prog - The path to an executable
+args - Vector of arguments to pass to the child process
+
+Returns:
+
+A record, {status: int, out: str, err: str} containing the exit code,
+the contents of stdout and the contents of stderr.
+*/
+fn program_output(prog: str, args: [str]) ->
+   {status: int, out: str, err: str} {
+    let pr = start_program(prog, args);
+    pr.close_input();
+    let out = read_all(pr.output());
+    let err = read_all(pr.err());
+    ret {status: pr.finish(), out: out, err: err};
+}
+
+/*
+Function: waitpid
+
+Waits for a process to exit and returns the exit code
+*/
+fn waitpid(pid: pid_t) -> int {
+    ret waitpid_os(pid);
+
+    #[cfg(target_os = "win32")]
+    fn waitpid_os(pid: pid_t) -> int {
+        os::waitpid(pid) as int
+    }
+
+    #[cfg(target_os = "linux")]
+    #[cfg(target_os = "macos")]
+    fn waitpid_os(pid: pid_t) -> int {
+        #[cfg(target_os = "linux")]
+        fn WIFEXITED(status: i32) -> bool {
+            (status & 0xffi32) == 0i32
+        }
+
+        #[cfg(target_os = "macos")]
+        fn WIFEXITED(status: i32) -> bool {
+            (status & 0x7fi32) == 0i32
+        }
+
+        #[cfg(target_os = "linux")]
+        fn WEXITSTATUS(status: i32) -> i32 {
+            (status >> 8i32) & 0xffi32
+        }
+
+        #[cfg(target_os = "macos")]
+        fn WEXITSTATUS(status: i32) -> i32 {
+            status >> 8i32
+        }
+
+        let status = os::waitpid(pid);
+        ret if WIFEXITED(status) {
+            WEXITSTATUS(status) as int
+        } else {
+            1
+        };
+    }
+}
+
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End: