Convert most of rust_run_program.cpp to rust (issue #2674).
This commit is contained in:
parent
b6988843e8
commit
544ac620ba
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
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 @@ 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)]
|
||||
@ -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() {
|
||||
|
@ -15,212 +15,44 @@
|
||||
#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 == '"';
|
||||
extern "C" CDECL void
|
||||
rust_unset_sigprocmask() {
|
||||
// empty stub for windows to keep linker happy
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
#ifdef __APPLE__
|
||||
*_NSGetEnviron() = (char **)envp;
|
||||
#else
|
||||
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;
|
||||
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;
|
||||
#else
|
||||
environ = (char **) envp;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user