Convert most of rust_run_program.cpp to rust (issue #2674).

This commit is contained in:
gareth 2013-04-30 23:00:07 +01:00
parent b6988843e8
commit 544ac620ba
5 changed files with 456 additions and 238 deletions

View File

@ -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;
}
}
}

View 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,

View File

@ -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() {

View File

@ -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

View 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