diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs index 53aaf5726dd0b..59b06faf5a251 100644 --- a/src/libcore/libc.rs +++ b/src/libcore/libc.rs @@ -582,12 +582,16 @@ pub mod types { 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 types { 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 consts { 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 consts { 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 @@ pub mod funcs { 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 funcs { 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 @@ pub mod funcs { 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; } } } diff --git a/src/libcore/os.rs b/src/libcore/os.rs index 8efae3e0e6890..c4b03d76cefec 100644 --- a/src/libcore/os.rs +++ b/src/libcore/os.rs @@ -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, diff --git a/src/libcore/run.rs b/src/libcore/run.rs index d0c495dd19e44..7e73b3a3f80be 100644 --- a/src/libcore/run.rs +++ b/src/libcore/run.rs @@ -22,31 +22,6 @@ use str; 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 { + + let mut si = zeroed_startupinfo(); + si.cb = sys::size_of::() 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 { - 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 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(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(env: &Option<~[(~str,~str)]>, #[cfg(windows)] fn with_envp(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(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(d: &Option<~str>, cb: &fn(*libc::c_char) -> T) -> T { match *d { @@ -312,8 +529,6 @@ priv fn free_handle(_handle: *()) { 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)] @@ -539,10 +781,30 @@ mod tests { use libc; use option::None; use os; - use path::Path; 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() { @@ -607,34 +869,59 @@ mod tests { p.destroy(); // ...and nor should this (and nor should the destructor) } - #[cfg(unix)] // there is no way to sleep on windows from inside libcore... fn test_destroy_actually_kills(force: bool) { - let path = Path(fmt!("test/core-run-test-destroy-actually-kills-%?.tmp", force)); - os::remove_file(&path); + #[cfg(unix)] + static BLOCK_COMMAND: &'static str = "cat"; - let cmd = fmt!("sleep 5 && echo MurderDeathKill > %s", path.to_str()); - let mut p = run::start_program("sh", [~"-c", cmd]); + #[cfg(windows)] + static BLOCK_COMMAND: &'static str = "cmd"; - p.destroy(); // destroy the program before it has a chance to echo its message + #[cfg(unix)] + fn process_exists(pid: libc::pid_t) -> bool { + run::program_output("ps", [~"-p", pid.to_str()]).out.contains(pid.to_str()) + } - unsafe { - // wait to ensure the program is really destroyed and not just waiting itself - libc::sleep(10); + #[cfg(windows)] + fn process_exists(pid: libc::pid_t) -> bool { + + use libc::types::os::arch::extra::DWORD; + use libc::funcs::extra::kernel32::{CloseHandle, GetExitCodeProcess, OpenProcess}; + use libc::consts::os::extra::{FALSE, PROCESS_QUERY_INFORMATION, STILL_ACTIVE }; + + unsafe { + let proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD); + if proc.is_null() { + return false; + } + // proc will be non-null if the process is alive, or if it died recently + let mut status = 0; + GetExitCodeProcess(proc, &mut status); + CloseHandle(proc); + return status == STILL_ACTIVE; + } + } + + // this program will stay alive indefinitely trying to read from stdin + let mut p = run::start_program(BLOCK_COMMAND, []); + + assert!(process_exists(p.get_id())); + + if force { + p.force_destroy(); + } else { + p.destroy(); } - // the program should not have had chance to echo its message - assert!(!path.exists()); + assert!(!process_exists(p.get_id())); } #[test] - #[cfg(unix)] fn test_unforced_destroy_actually_kills() { test_destroy_actually_kills(false); } #[test] - #[cfg(unix)] fn test_forced_destroy_actually_kills() { test_destroy_actually_kills(true); } diff --git a/src/rt/rust_run_program.cpp b/src/rt/rust_run_program.cpp index cf4beed1a00c6..0ba7607869140 100644 --- a/src/rt/rust_run_program.cpp +++ b/src/rt/rust_run_program.cpp @@ -15,212 +15,44 @@ #include #endif -struct RunProgramResult { - pid_t pid; - void* handle; -}; - #if defined(__WIN32__) -#include -#include - -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 #include -#include #include -#include #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 diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 977e0248ca206..408e2e9a81671 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -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