]> git.lizzy.rs Git - rust.git/commitdiff
Convert most of rust_run_program.cpp to rust (issue #2674).
authorgareth <gareth@gareth-N56VM.(none)>
Tue, 30 Apr 2013 22:00:07 +0000 (23:00 +0100)
committergareth <gareth@gareth-N56VM.(none)>
Thu, 2 May 2013 18:26:52 +0000 (19:26 +0100)
src/libcore/libc.rs
src/libcore/os.rs
src/libcore/run.rs
src/rt/rust_run_program.cpp
src/rt/rustrt.def.in

index 53aaf5726dd0bf92aef2b6aa4c3067c67dcad0ac..59b06faf5a2517471fdc76a4a999f830bcef8312 100644 (file)
@@ -582,12 +582,16 @@ pub mod extra {
 
                 pub type LPWSTR = *mut WCHAR;
                 pub type LPSTR = *mut CHAR;
+                pub type LPTSTR = *mut CHAR;
 
                 // Not really, but opaque to us.
                 pub type LPSECURITY_ATTRIBUTES = LPVOID;
 
                 pub type LPVOID = *mut c_void;
+                pub type LPBYTE = *mut BYTE;
                 pub type LPWORD = *mut WORD;
+                pub type LPDWORD = *mut DWORD;
+                pub type LPHANDLE = *mut HANDLE;
 
                 pub type LRESULT = LONG_PTR;
                 pub type PBOOL = *mut BOOL;
@@ -596,6 +600,36 @@ pub mod extra {
 
                 pub type time64_t = i64;
                 pub type int64 = i64;
+
+                pub struct STARTUPINFO {
+                    cb: DWORD,
+                    lpReserved: LPTSTR,
+                    lpDesktop: LPTSTR,
+                    lpTitle: LPTSTR,
+                    dwX: DWORD,
+                    dwY: DWORD,
+                    dwXSize: DWORD,
+                    dwYSize: DWORD,
+                    dwXCountChars: DWORD,
+                    dwYCountCharts: DWORD,
+                    dwFillAttribute: DWORD,
+                    dwFlags: DWORD,
+                    wShowWindow: WORD,
+                    cbReserved2: WORD,
+                    lpReserved2: LPBYTE,
+                    hStdInput: HANDLE,
+                    hStdOutput: HANDLE,
+                    hStdError: HANDLE
+                }
+                pub type LPSTARTUPINFO = *mut STARTUPINFO;
+
+                pub struct PROCESS_INFORMATION {
+                    hProcess: HANDLE,
+                    hThread: HANDLE,
+                    dwProcessId: DWORD,
+                    dwThreadId: DWORD
+                }
+                pub type LPPROCESS_INFORMATION = *mut PROCESS_INFORMATION;
             }
         }
     }
@@ -848,6 +882,11 @@ pub mod posix08 {
         pub mod bsd44 {
         }
         pub mod extra {
+            use libc::types::os::arch::extra::{DWORD, BOOL};
+
+            pub static TRUE : BOOL = 1;
+            pub static FALSE : BOOL = 0;
+
             pub static O_TEXT : int = 16384;
             pub static O_BINARY : int = 32768;
             pub static O_NOINHERIT: int = 128;
@@ -855,6 +894,50 @@ pub mod extra {
             pub static ERROR_SUCCESS : int = 0;
             pub static ERROR_INSUFFICIENT_BUFFER : int = 122;
             pub static INVALID_HANDLE_VALUE: int = -1;
+
+            pub static DELETE : DWORD = 0x00010000;
+            pub static READ_CONTROL : DWORD = 0x00020000;
+            pub static SYNCHRONIZE : DWORD = 0x00100000;
+            pub static WRITE_DAC : DWORD = 0x00040000;
+            pub static WRITE_OWNER : DWORD = 0x00080000;
+
+            pub static PROCESS_CREATE_PROCESS : DWORD = 0x0080;
+            pub static PROCESS_CREATE_THREAD : DWORD = 0x0002;
+            pub static PROCESS_DUP_HANDLE : DWORD = 0x0040;
+            pub static PROCESS_QUERY_INFORMATION : DWORD = 0x0400;
+            pub static PROCESS_QUERY_LIMITED_INFORMATION : DWORD = 0x1000;
+            pub static PROCESS_SET_INFORMATION : DWORD = 0x0200;
+            pub static PROCESS_SET_QUOTA : DWORD = 0x0100;
+            pub static PROCESS_SUSPEND_RESUME : DWORD = 0x0800;
+            pub static PROCESS_TERMINATE : DWORD = 0x0001;
+            pub static PROCESS_VM_OPERATION : DWORD = 0x0008;
+            pub static PROCESS_VM_READ : DWORD = 0x0010;
+            pub static PROCESS_VM_WRITE : DWORD = 0x0020;
+
+            pub static STARTF_FORCEONFEEDBACK : DWORD = 0x00000040;
+            pub static STARTF_FORCEOFFFEEDBACK : DWORD = 0x00000080;
+            pub static STARTF_PREVENTPINNING : DWORD = 0x00002000;
+            pub static STARTF_RUNFULLSCREEN : DWORD = 0x00000020;
+            pub static STARTF_TITLEISAPPID : DWORD = 0x00001000;
+            pub static STARTF_TITLEISLINKNAME : DWORD = 0x00000800;
+            pub static STARTF_USECOUNTCHARS : DWORD = 0x00000008;
+            pub static STARTF_USEFILLATTRIBUTE : DWORD = 0x00000010;
+            pub static STARTF_USEHOTKEY : DWORD = 0x00000200;
+            pub static STARTF_USEPOSITION : DWORD = 0x00000004;
+            pub static STARTF_USESHOWWINDOW : DWORD = 0x00000001;
+            pub static STARTF_USESIZE : DWORD = 0x00000002;
+            pub static STARTF_USESTDHANDLES : DWORD = 0x00000100;
+
+            pub static WAIT_ABANDONED : DWORD = 0x00000080;
+            pub static WAIT_OBJECT_0 : DWORD = 0x00000000;
+            pub static WAIT_TIMEOUT : DWORD = 0x00000102;
+            pub static WAIT_FAILED : DWORD = -1;
+
+            pub static DUPLICATE_CLOSE_SOURCE : DWORD = 0x00000001;
+            pub static DUPLICATE_SAME_ACCESS : DWORD = 0x00000002;
+
+            pub static INFINITE : DWORD = -1;
+            pub static STILL_ACTIVE : DWORD = 259;
         }
     }
 
@@ -1751,12 +1834,24 @@ unsafe fn sysctlbyname(name: *c_char,
 
             unsafe fn sysctlnametomib(name: *c_char, mibp: *mut c_int,
                                sizep: *mut size_t) -> c_int;
+
+            unsafe fn getdtablesize() -> c_int;
         }
     }
 
 
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "android")]
+    pub mod bsd44 {
+        use libc::types::os::arch::c95::{c_int};
+
+        #[abi = "cdecl"]
+        pub extern {
+            unsafe fn getdtablesize() -> c_int;
+        }
+    }
+
+
     #[cfg(target_os = "win32")]
     pub mod bsd44 {
     }
@@ -1790,9 +1885,11 @@ pub mod extra {
         pub mod kernel32 {
             use libc::types::os::arch::c95::{c_uint};
             use libc::types::os::arch::extra::{BOOL, DWORD, HMODULE};
-            use libc::types::os::arch::extra::{LPCWSTR, LPWSTR, LPTCH};
-            use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES};
-            use libc::types::os::arch::extra::{HANDLE};
+            use libc::types::os::arch::extra::{LPCWSTR, LPWSTR, LPCTSTR,
+                                               LPTSTR, LPTCH, LPDWORD, LPVOID};
+            use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, LPSTARTUPINFO,
+                                               LPPROCESS_INFORMATION};
+            use libc::types::os::arch::extra::{HANDLE, LPHANDLE};
 
             #[abi = "stdcall"]
             pub extern "stdcall" {
@@ -1829,19 +1926,45 @@ unsafe fn FindNextFileW(findFile: HANDLE,
                                        findFileData: HANDLE)
                     -> BOOL;
                 unsafe fn FindClose(findFile: HANDLE) -> BOOL;
+                unsafe fn DuplicateHandle(hSourceProcessHandle: HANDLE,
+                                          hSourceHandle: HANDLE,
+                                          hTargetProcessHandle: HANDLE,
+                                          lpTargetHandle: LPHANDLE,
+                                          dwDesiredAccess: DWORD,
+                                          bInheritHandle: BOOL,
+                                          dwOptions: DWORD) -> BOOL;
                 unsafe fn CloseHandle(hObject: HANDLE) -> BOOL;
+                unsafe fn OpenProcess(dwDesiredAccess: DWORD,
+                                      bInheritHandle: BOOL,
+                                      dwProcessId: DWORD) -> HANDLE;
+                unsafe fn GetCurrentProcess() -> HANDLE;
+                unsafe fn CreateProcessA(lpApplicationName: LPCTSTR,
+                                         lpCommandLine: LPTSTR,
+                                         lpProcessAttributes: LPSECURITY_ATTRIBUTES,
+                                         lpThreadAttributes: LPSECURITY_ATTRIBUTES,
+                                         bInheritHandles: BOOL,
+                                         dwCreationFlags: DWORD,
+                                         lpEnvironment: LPVOID,
+                                         lpCurrentDirectory: LPCTSTR,
+                                         lpStartupInfo: LPSTARTUPINFO,
+                                         lpProcessInformation: LPPROCESS_INFORMATION) -> BOOL;
+                unsafe fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
                 unsafe fn TerminateProcess(hProcess: HANDLE, uExitCode: c_uint) -> BOOL;
+                unsafe fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: LPDWORD) -> BOOL;
             }
         }
 
         pub mod msvcrt {
-            use libc::types::os::arch::c95::c_int;
+            use libc::types::os::arch::c95::{c_int, c_long};
 
             #[abi = "cdecl"]
             #[nolink]
             pub extern {
                 #[link_name = "_commit"]
                 unsafe fn commit(fd: c_int) -> c_int;
+
+                #[link_name = "_get_osfhandle"]
+                unsafe fn get_osfhandle(fd: c_int) -> c_long;
             }
         }
     }
index 8efae3e0e6890caad1725693c9ba4e31fc11d307..c4b03d76cefec7bf44447df6fff04feade145b62 100644 (file)
@@ -373,7 +373,7 @@ pub fn pipe() -> Pipe {
         // inheritance has to be handled in a different way that I do not
         // fully understand. Here we explicitly make the pipe non-inheritable,
         // which means to pass it to a subprocess they need to be duplicated
-        // first, as in rust_run_program.
+        // first, as in core::run.
         let mut fds = Pipe {in: 0 as c_int,
                     out: 0 as c_int };
         let res = libc::pipe(&mut fds.in, 1024 as ::libc::c_uint,
index d0c495dd19e44f77606b97f98fbdca87fcfba268..0a8ccc3c5e9b916b7df4b4b0b139f73b802df4b2 100644 (file)
 use task;
 use vec;
 
-pub mod rustrt {
-    use libc::{c_int, c_void};
-    use libc;
-    use run;
-
-    #[abi = "cdecl"]
-    pub extern {
-        unsafe fn rust_run_program(argv: **libc::c_char,
-                                   envp: *c_void,
-                                   dir: *libc::c_char,
-                                   in_fd: c_int,
-                                   out_fd: c_int,
-                                   err_fd: c_int) -> run::RunProgramResult;
-        unsafe fn rust_process_wait(pid: c_int) -> c_int;
-    }
-}
-
-pub struct RunProgramResult {
-    // the process id of the program, or -1 if in case of errors
-    pid: pid_t,
-    // a handle to the process - on unix this will always be NULL, but on windows it will be a
-    // HANDLE to the process, which will prevent the pid being re-used until the handle is closed.
-    handle: *(),
-}
-
 /// A value representing a child process
 pub struct Program {
     priv pid: pid_t,
@@ -191,21 +166,262 @@ pub fn spawn_process(prog: &str, args: &[~str],
     return res.pid;
 }
 
+struct RunProgramResult {
+    // the process id of the program (this should never be negative)
+    pid: pid_t,
+    // a handle to the process - on unix this will always be NULL, but on windows it will be a
+    // HANDLE to the process, which will prevent the pid being re-used until the handle is closed.
+    handle: *(),
+}
+
+#[cfg(windows)]
 fn spawn_process_internal(prog: &str, args: &[~str],
                           env: &Option<~[(~str,~str)]>,
                           dir: &Option<~str>,
                           in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult {
+
+    use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
+    use libc::consts::os::extra::{
+        TRUE, FALSE,
+        STARTF_USESTDHANDLES,
+        INVALID_HANDLE_VALUE,
+        DUPLICATE_SAME_ACCESS
+    };
+    use libc::funcs::extra::kernel32::{
+        GetCurrentProcess,
+        DuplicateHandle,
+        CloseHandle,
+        CreateProcessA
+    };
+    use libc::funcs::extra::msvcrt::get_osfhandle;
+
     unsafe {
-        do with_argv(prog, args) |argv| {
-            do with_envp(env) |envp| {
-                do with_dirp(dir) |dirp| {
-                    rustrt::rust_run_program(argv, envp, dirp, in_fd, out_fd, err_fd)
+
+        let mut si = zeroed_startupinfo();
+        si.cb = sys::size_of::<STARTUPINFO>() as DWORD;
+        si.dwFlags = STARTF_USESTDHANDLES;
+
+        let cur_proc = GetCurrentProcess();
+
+        let orig_std_in = get_osfhandle(if in_fd > 0 { in_fd } else { 0 }) as HANDLE;
+        if orig_std_in == INVALID_HANDLE_VALUE as HANDLE {
+            fail!(fmt!("failure in get_osfhandle: %s", os::last_os_error()));
+        }
+        if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput,
+                           0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
+            fail!(fmt!("failure in DuplicateHandle: %s", os::last_os_error()));
+        }
+
+        let orig_std_out = get_osfhandle(if out_fd > 0 { out_fd } else { 1 }) as HANDLE;
+        if orig_std_out == INVALID_HANDLE_VALUE as HANDLE {
+            fail!(fmt!("failure in get_osfhandle: %s", os::last_os_error()));
+        }
+        if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput,
+                           0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
+            fail!(fmt!("failure in DuplicateHandle: %s", os::last_os_error()));
+        }
+
+        let orig_std_err = get_osfhandle(if err_fd > 0 { err_fd } else { 2 }) as HANDLE;
+        if orig_std_err as HANDLE == INVALID_HANDLE_VALUE as HANDLE {
+            fail!(fmt!("failure in get_osfhandle: %s", os::last_os_error()));
+        }
+        if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError,
+                           0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
+            fail!(fmt!("failure in DuplicateHandle: %s", os::last_os_error()));
+        }
+
+        let cmd = make_command_line(prog, args);
+        let mut pi = zeroed_process_information();
+        let mut create_err = None;
+
+        do with_envp(env) |envp| {
+            do with_dirp(dir) |dirp| {
+                do str::as_c_str(cmd) |cmdp| {
+                    let created = CreateProcessA(ptr::null(), cast::transmute(cmdp),
+                                                 ptr::mut_null(), ptr::mut_null(), TRUE,
+                                                 0, envp, dirp, &mut si, &mut pi);
+                    if created == FALSE {
+                        create_err = Some(os::last_os_error());
+                    }
                 }
             }
         }
+
+        CloseHandle(si.hStdInput);
+        CloseHandle(si.hStdOutput);
+        CloseHandle(si.hStdError);
+
+        for create_err.each |msg| {
+            fail!(fmt!("failure in CreateProcess: %s", *msg));
+        }
+
+        // We close the thread handle because we don't care about keeping the thread id valid,
+        // and we aren't keeping the thread handle around to be able to close it later. We don't
+        // close the process handle however because we want the process id to stay valid at least
+        // until the calling code closes the process handle.
+        CloseHandle(pi.hThread);
+
+        RunProgramResult {
+            pid: pi.dwProcessId as pid_t,
+            handle: pi.hProcess as *()
+        }
+    }
+}
+
+#[cfg(windows)]
+fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
+    libc::types::os::arch::extra::STARTUPINFO {
+        cb: 0,
+        lpReserved: ptr::mut_null(),
+        lpDesktop: ptr::mut_null(),
+        lpTitle: ptr::mut_null(),
+        dwX: 0,
+        dwY: 0,
+        dwXSize: 0,
+        dwYSize: 0,
+        dwXCountChars: 0,
+        dwYCountCharts: 0,
+        dwFillAttribute: 0,
+        dwFlags: 0,
+        wShowWindow: 0,
+        cbReserved2: 0,
+        lpReserved2: ptr::mut_null(),
+        hStdInput: ptr::mut_null(),
+        hStdOutput: ptr::mut_null(),
+        hStdError: ptr::mut_null()
+    }
+}
+
+#[cfg(windows)]
+fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
+    libc::types::os::arch::extra::PROCESS_INFORMATION {
+        hProcess: ptr::mut_null(),
+        hThread: ptr::mut_null(),
+        dwProcessId: 0,
+        dwThreadId: 0
     }
 }
 
+// FIXME: this is only pub so it can be tested (see issue #4536)
+#[cfg(windows)]
+pub fn make_command_line(prog: &str, args: &[~str]) -> ~str {
+
+    let mut cmd = ~"";
+    append_arg(&mut cmd, prog);
+    for args.each |arg| {
+        cmd.push_char(' ');
+        append_arg(&mut cmd, *arg);
+    }
+    return cmd;
+
+    fn append_arg(cmd: &mut ~str, arg: &str) {
+        let quote = arg.any(|c| c == ' ' || c == '\t');
+        if quote {
+            cmd.push_char('"');
+        }
+        for uint::range(0, arg.len()) |i| {
+            append_char_at(cmd, arg, i);
+        }
+        if quote {
+            cmd.push_char('"');
+        }
+    }
+
+    fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) {
+        match arg[i] as char {
+            '"' => {
+                // Escape quotes.
+                cmd.push_str("\\\"");
+            }
+            '\\' => {
+                if backslash_run_ends_in_quote(arg, i) {
+                    // Double all backslashes that are in runs before quotes.
+                    cmd.push_str("\\\\");
+                } else {
+                    // Pass other backslashes through unescaped.
+                    cmd.push_char('\\');
+                }
+            }
+            c => {
+                cmd.push_char(c);
+            }
+        }
+    }
+
+    fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool {
+        while i < s.len() && s[i] as char == '\\' {
+            i += 1;
+        }
+        return i < s.len() && s[i] as char == '"';
+    }
+}
+
+#[cfg(unix)]
+fn spawn_process_internal(prog: &str, args: &[~str],
+                          env: &Option<~[(~str,~str)]>,
+                          dir: &Option<~str>,
+                          in_fd: c_int, out_fd: c_int, err_fd: c_int) -> RunProgramResult {
+
+    use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
+    use libc::funcs::bsd44::getdtablesize;
+
+    mod rustrt {
+        use libc::c_void;
+
+        #[abi = "cdecl"]
+        pub extern {
+            unsafe fn rust_unset_sigprocmask();
+            unsafe fn rust_set_environ(envp: *c_void);
+        }
+    }
+    
+    unsafe {
+
+        let pid = fork();
+        if pid < 0 {
+            fail!(fmt!("failure in fork: %s", os::last_os_error()));
+        } else if pid > 0 {
+            return RunProgramResult {pid: pid, handle: ptr::null()};
+        }
+
+        rustrt::rust_unset_sigprocmask();
+
+        if in_fd > 0 && dup2(in_fd, 0) == -1 {
+            fail!(fmt!("failure in dup2(in_fd, 0): %s", os::last_os_error()));
+        }
+        if out_fd > 0 && dup2(out_fd, 1) == -1 {
+            fail!(fmt!("failure in dup2(out_fd, 1): %s", os::last_os_error()));
+        }
+        if err_fd > 0 && dup2(err_fd, 2) == -1 {
+            fail!(fmt!("failure in dup3(err_fd, 2): %s", os::last_os_error()));
+        }
+        // close all other fds
+        for int::range_rev(getdtablesize() as int - 1, 2) |fd| {
+            close(fd as c_int);
+        }
+
+        for dir.each |dir| {
+            do str::as_c_str(*dir) |dirp| {
+                if chdir(dirp) == -1 {
+                    fail!(fmt!("failure in chdir: %s", os::last_os_error()));
+                }
+            }
+        }
+
+        do with_envp(env) |envp| {
+            if !envp.is_null() {
+                rustrt::rust_set_environ(envp);
+            }
+            do with_argv(prog, args) |argv| {
+                execvp(*argv, argv);
+                // execvp only returns if an error occurred
+                fail!(fmt!("failure in execvp: %s", os::last_os_error()));
+            }
+        }
+    }
+}
+
+#[cfg(unix)]
 fn with_argv<T>(prog: &str, args: &[~str],
                 cb: &fn(**libc::c_char) -> T) -> T {
     let mut argptrs = str::as_c_str(prog, |b| ~[b]);
@@ -246,7 +462,7 @@ fn with_envp<T>(env: &Option<~[(~str,~str)]>,
 
 #[cfg(windows)]
 fn with_envp<T>(env: &Option<~[(~str,~str)]>,
-                cb: &fn(*c_void) -> T) -> T {
+                cb: &fn(*mut c_void) -> T) -> T {
     // On win32 we pass an "environment block" which is not a char**, but
     // rather a concatenation of null-terminated k=v\0 sequences, with a final
     // \0 to terminate.
@@ -264,11 +480,12 @@ fn with_envp<T>(env: &Option<~[(~str,~str)]>,
             blk += ~[0_u8];
             vec::as_imm_buf(blk, |p, _len| cb(::cast::transmute(p)))
           }
-          _ => cb(ptr::null())
+          _ => cb(ptr::mut_null())
         }
     }
 }
 
+#[cfg(windows)]
 fn with_dirp<T>(d: &Option<~str>,
                 cb: &fn(*libc::c_char) -> T) -> T {
     match *d {
@@ -312,8 +529,6 @@ fn with_dirp<T>(d: &Option<~str>,
 pub fn run_program(prog: &str, args: &[~str]) -> int {
     let res = spawn_process_internal(prog, args, &None, &None,
                                      0i32, 0i32, 0i32);
-    if res.pid == -1 as pid_t { fail!(); }
-
     let code = waitpid(res.pid);
     free_handle(res.handle);
     return code;
@@ -345,7 +560,6 @@ pub fn start_program(prog: &str, args: &[~str]) -> Program {
                                pipe_err.out);
 
     unsafe {
-        if res.pid == -1 as pid_t { fail!(); }
         libc::close(pipe_input.in);
         libc::close(pipe_output.out);
         libc::close(pipe_err.out);
@@ -398,13 +612,6 @@ pub fn program_output(prog: &str, args: &[~str]) -> ProgramOutput {
     os::close(pipe_in.in);
     os::close(pipe_out.out);
     os::close(pipe_err.out);
-    if res.pid == -1i32 {
-        os::close(pipe_in.out);
-        os::close(pipe_out.in);
-        os::close(pipe_err.in);
-        fail!();
-    }
-
     os::close(pipe_in.out);
 
     // Spawn two entire schedulers to read both stdout and sterr
@@ -485,11 +692,46 @@ pub fn waitpid(pid: pid_t) -> int {
 
     #[cfg(windows)]
     fn waitpid_os(pid: pid_t) -> int {
-        let status = unsafe { rustrt::rust_process_wait(pid) };
-        if status < 0 {
-            fail!(fmt!("failure in rust_process_wait: %s", os::last_os_error()));
+
+        use libc::types::os::arch::extra::DWORD;
+        use libc::consts::os::extra::{
+            SYNCHRONIZE,
+            PROCESS_QUERY_INFORMATION,
+            FALSE,
+            STILL_ACTIVE,
+            INFINITE,
+            WAIT_FAILED
+        };
+        use libc::funcs::extra::kernel32::{
+            OpenProcess,
+            GetExitCodeProcess,
+            CloseHandle,
+            WaitForSingleObject
+        };
+
+        unsafe {
+
+            let proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD);
+            if proc.is_null() {
+                fail!(fmt!("failure in OpenProcess: %s", os::last_os_error()));
+            }
+
+            loop {
+                let mut status = 0;
+                if GetExitCodeProcess(proc, &mut status) == FALSE {
+                    CloseHandle(proc);
+                    fail!(fmt!("failure in GetExitCodeProcess: %s", os::last_os_error()));
+                }
+                if status != STILL_ACTIVE {
+                    CloseHandle(proc);
+                    return status as int;
+                }
+                if WaitForSingleObject(proc, INFINITE) == WAIT_FAILED {
+                    CloseHandle(proc);
+                    fail!(fmt!("failure in WaitForSingleObject: %s", os::last_os_error()));
+                }
+            }
         }
-        return status as int;
     }
 
     #[cfg(unix)]
@@ -543,6 +785,27 @@ mod tests {
     use run::{readclose, writeclose};
     use run;
 
+    #[test]
+    #[cfg(windows)]
+    fn test_make_command_line() {
+        assert_eq!(
+            run::make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]),
+            ~"prog aaa bbb ccc"
+        );
+        assert_eq!(
+            run::make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]),
+            ~"\"C:\\Program Files\\blah\\blah.exe\" aaa"
+        );
+        assert_eq!(
+            run::make_command_line("C:\\Program Files\\test", [~"aa\"bb"]),
+            ~"\"C:\\Program Files\\test\" aa\\\"bb"
+        );
+        assert_eq!(
+            run::make_command_line("echo", [~"a b c"]),
+            ~"echo \"a b c\""
+        );
+    }
+
     // Regression test for memory leaks
     #[test]
     fn test_leaks() {
index cf4beed1a00c6fea11b258f4c40202b83c0d3f78..0ba760786914006dc1f58c5660a52c9e18bec374 100644 (file)
 #include <crt_externs.h>
 #endif
 
-struct RunProgramResult {
-    pid_t pid;
-    void* handle;
-};
-
 #if defined(__WIN32__)
 
-#include <process.h>
-#include <io.h>
-
-bool backslash_run_ends_in_quote(char const *c) {
-    while (*c == '\\') ++c;
-    return *c == '"';
-}
-
-void append_first_char(char *&buf, char const *c) {
-    switch (*c) {
-
-    case '"':
-        // Escape quotes.
-        *buf++ = '\\';
-        *buf++ = '"';
-        break;
-
-
-    case '\\':
-        if (backslash_run_ends_in_quote(c)) {
-            // Double all backslashes that are in runs before quotes.
-            *buf++ = '\\';
-            *buf++ = '\\';
-        } else {
-            // Pass other backslashes through unescaped.
-            *buf++ = '\\';
-        }
-        break;
-
-    default:
-        *buf++ = *c;
-    }
+extern "C" CDECL void
+rust_unset_sigprocmask() {
+    // empty stub for windows to keep linker happy
 }
 
-bool contains_whitespace(char const *arg) {
-    while (*arg) {
-        switch (*arg++) {
-        case ' ':
-        case '\t':
-            return true;
-        }
-    }
-    return false;
-}
-
-void append_arg(char *& buf, char const *arg, bool last) {
-    bool quote = contains_whitespace(arg);
-    if (quote)
-        *buf++ = '"';
-    while (*arg)
-        append_first_char(buf, arg++);
-    if (quote)
-        *buf++ = '"';
-
-    if (! last) {
-        *buf++ = ' ';
-    } else {
-        *buf++ = '\0';
-    }
-}
-
-extern "C" CDECL RunProgramResult
-rust_run_program(const char* argv[],
-                 void* envp,
-                 const char* dir,
-                 int in_fd, int out_fd, int err_fd) {
-    STARTUPINFO si;
-    ZeroMemory(&si, sizeof(STARTUPINFO));
-    si.cb = sizeof(STARTUPINFO);
-    si.dwFlags = STARTF_USESTDHANDLES;
-
-    RunProgramResult result = {-1, NULL};
-
-    HANDLE curproc = GetCurrentProcess();
-    HANDLE origStdin = (HANDLE)_get_osfhandle(in_fd ? in_fd : 0);
-    if (!DuplicateHandle(curproc, origStdin,
-        curproc, &si.hStdInput, 0, 1, DUPLICATE_SAME_ACCESS))
-        return result;
-    HANDLE origStdout = (HANDLE)_get_osfhandle(out_fd ? out_fd : 1);
-    if (!DuplicateHandle(curproc, origStdout,
-        curproc, &si.hStdOutput, 0, 1, DUPLICATE_SAME_ACCESS))
-        return result;
-    HANDLE origStderr = (HANDLE)_get_osfhandle(err_fd ? err_fd : 2);
-    if (!DuplicateHandle(curproc, origStderr,
-        curproc, &si.hStdError, 0, 1, DUPLICATE_SAME_ACCESS))
-        return result;
-
-    size_t cmd_len = 0;
-    for (const char** arg = argv; *arg; arg++) {
-        cmd_len += strlen(*arg);
-        cmd_len += 3; // Two quotes plus trailing space or \0
-    }
-    cmd_len *= 2; // Potentially backslash-escape everything.
-
-    char* cmd = (char*)malloc(cmd_len);
-    char* pos = cmd;
-    for (const char** arg = argv; *arg; arg++) {
-        append_arg(pos, *arg, *(arg+1) == NULL);
-    }
-
-    PROCESS_INFORMATION pi;
-    BOOL created = CreateProcess(NULL, cmd, NULL, NULL, TRUE,
-                                 0, envp, dir, &si, &pi);
-
-    CloseHandle(si.hStdInput);
-    CloseHandle(si.hStdOutput);
-    CloseHandle(si.hStdError);
-    free(cmd);
-
-    if (!created) {
-        return result;
-    }
-
-    // We close the thread handle because we don't care about keeping the thread id valid,
-    // and we aren't keeping the thread handle around to be able to close it later. We don't
-    // close the process handle however because we want the process id to stay valid at least
-    // until the calling rust code closes the process handle.
-    CloseHandle(pi.hThread);
-    result.pid = pi.dwProcessId;
-    result.handle = pi.hProcess;
-    return result;
-}
-
-extern "C" CDECL int
-rust_process_wait(int pid) {
-
-    HANDLE proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid);
-    if (proc == NULL) {
-        return -1;
-    }
-
-    DWORD status;
-    while (true) {
-        if (!GetExitCodeProcess(proc, &status)) {
-            CloseHandle(proc);
-            return -1;
-        }
-        if (status != STILL_ACTIVE) {
-            CloseHandle(proc);
-            return (int) status;
-        }
-        WaitForSingleObject(proc, INFINITE);
-    }
+extern "C" CDECL void
+rust_set_environ(void* envp) {
+    // empty stub for windows to keep linker happy
 }
 
 #elif defined(__GNUC__)
 
-#include <sys/file.h>
 #include <signal.h>
-#include <sys/ioctl.h>
 #include <unistd.h>
-#include <termios.h>
 
 #ifdef __FreeBSD__
 extern char **environ;
 #endif
 
-extern "C" CDECL RunProgramResult
-rust_run_program(const char* argv[],
-                 void* envp,
-                 const char* dir,
-                 int in_fd, int out_fd, int err_fd) {
-    int pid = fork();
-    if (pid != 0) {
-        RunProgramResult result = {pid, NULL};
-        return result;
-    }
-
+extern "C" CDECL void
+rust_unset_sigprocmask() {
+    // this can't be safely converted to rust code because the
+    // representation of sigset_t is platform-dependent
     sigset_t sset;
     sigemptyset(&sset);
     sigprocmask(SIG_SETMASK, &sset, NULL);
+}
 
-    if (in_fd) dup2(in_fd, 0);
-    if (out_fd) dup2(out_fd, 1);
-    if (err_fd) dup2(err_fd, 2);
-    /* Close all other fds. */
-    for (int fd = getdtablesize() - 1; fd >= 3; fd--) close(fd);
-    if (dir) {
-        int result = chdir(dir);
-        // FIXME (#2674): need error handling
-        assert(!result && "chdir failed");
-    }
-
-    if (envp) {
+extern "C" CDECL void
+rust_set_environ(void* envp) {
+    // FIXME: this could actually be converted to rust (see issue #2674)
 #ifdef __APPLE__
-        *_NSGetEnviron() = (char **)envp;
+    *_NSGetEnviron() = (char **) envp;
 #else
-        environ = (char **)envp;
+    environ = (char **) envp;
 #endif
-    }
-
-    execvp(argv[0], (char * const *)argv);
-    exit(1);
-}
-
-extern "C" CDECL int
-rust_process_wait(int pid) {
-    // FIXME: stub; exists to placate linker. (#2692)
-    return 0;
 }
 
 #else
index 977e0248ca206dcc535ec58cf5d3d82226a3323a..408e2e9a816711899e7940b1ea6e5ed90eccb266 100644 (file)
@@ -37,8 +37,8 @@ rust_list_dir_wfd_size
 rust_list_dir_wfd_fp_buf
 rust_log_console_on
 rust_log_console_off
-rust_process_wait
-rust_run_program
+rust_set_environ
+rust_unset_sigprocmask
 rust_sched_current_nonlazy_threads
 rust_sched_threads
 rust_set_exit_status