Allow IO redirection on windows.

This commit is contained in:
Calvin Rose 2020-09-02 19:07:45 -05:00
parent 0485078c6c
commit b0c09153c2
1 changed files with 54 additions and 33 deletions

View File

@ -321,7 +321,8 @@ static const JanetAbstractType ProcAT;
typedef struct { typedef struct {
int flags; int flags;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
HANDLE pid; HANDLE pHandle;
HANDLE tHandle;
#else #else
int pid; int pid;
#endif #endif
@ -347,8 +348,13 @@ static Janet os_proc_wait_impl(JanetProc *proc) {
proc->flags |= JANET_PROC_WAITED; proc->flags |= JANET_PROC_WAITED;
int status = 0; int status = 0;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
WaitForSingleObject(proc->pid, INFINITE); WaitForSingleObject(proc->pHandle, INFINITE);
GetExitCodeProcess(proc->pid, &status); GetExitCodeProcess(proc->pHandle, &status);
if (!(proc->flags & JANET_PROC_CLOSED)) {
proc->flags |= JANET_PROC_CLOSED;
CloseHandle(proc->pHandle);
CloseHandle(proc->tHandle);
}
#else #else
waitpid(proc->pid, &status, 0); waitpid(proc->pid, &status, 0);
#endif #endif
@ -370,13 +376,14 @@ static Janet os_proc_kill(int32_t argc, Janet *argv) {
janet_panicf("cannot close process handle that is already closed"); janet_panicf("cannot close process handle that is already closed");
} }
proc->flags |= JANET_PROC_CLOSED; proc->flags |= JANET_PROC_CLOSED;
int status = CloseHandle(proc->pid); CloseHandle(proc->pHandle);
CloseHandle(proc->tHandle);
#else #else
int status = kill(proc->pid, SIGKILL); int status = kill(proc->pid, SIGKILL);
#endif
if (status) { if (status) {
janet_panic(strerror(errno)); janet_panic(strerror(errno));
} }
#endif
/* After killing process we wait on it. */ /* After killing process we wait on it. */
if (argc > 1 && janet_truthy(argv[1])) { if (argc > 1 && janet_truthy(argv[1])) {
return os_proc_wait_impl(proc); return os_proc_wait_impl(proc);
@ -442,7 +449,6 @@ static Janet os_execute(int32_t argc, Janet *argv) {
/* Optional stdio redirections */ /* Optional stdio redirections */
JanetFile *new_in = NULL, *new_out = NULL, *new_err = NULL; JanetFile *new_in = NULL, *new_out = NULL, *new_err = NULL;
#ifndef JANET_WINDOWS
/* Get optional redirections */ /* Get optional redirections */
if (argc > 2) { if (argc > 2) {
JanetDictView tab = janet_getdictionary(argv, 2); JanetDictView tab = janet_getdictionary(argv, 2);
@ -453,7 +459,6 @@ static Janet os_execute(int32_t argc, Janet *argv) {
if (!janet_checktype(maybe_stdout, JANET_NIL)) new_out = janet_getjfile(&maybe_stdout, 0); if (!janet_checktype(maybe_stdout, JANET_NIL)) new_out = janet_getjfile(&maybe_stdout, 0);
if (!janet_checktype(maybe_stderr, JANET_NIL)) new_err = janet_getjfile(&maybe_stderr, 0); if (!janet_checktype(maybe_stderr, JANET_NIL)) new_err = janet_getjfile(&maybe_stderr, 0);
} }
#endif
/* Result */ /* Result */
int status = 0; int status = 0;
@ -461,45 +466,56 @@ static Janet os_execute(int32_t argc, Janet *argv) {
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
HANDLE pid; HANDLE pHandle, tHandle;
PROCESS_INFORMATION processInfo;
STARTUPINFO startupInfo;
memset(&processInfo, 0, sizeof(processInfo));
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
JanetBuffer *buf = os_exec_escape(exargs); JanetBuffer *buf = os_exec_escape(exargs);
if (buf->count > 8191) { if (buf->count > 8191) {
janet_panic("command line string too long (max 8191 characters)"); janet_panic("command line string too long (max 8191 characters)");
} }
const char *path = (const char *) janet_unwrap_string(exargs.items[0]); const char *path = (const char *) janet_unwrap_string(exargs.items[0]);
char *cargv[2] = {(char *) buf->data, NULL};
/* Do IO redirection */
startupInfo.hStdInput = (HANDLE) _get_osfhandle((new_in == NULL) ? 0 : _fileno(new_in->file));
startupInfo.hStdOutput = (HANDLE) _get_osfhandle((new_out == NULL) ? 1 : _fileno(new_out->file));
startupInfo.hStdError = (HANDLE) _get_osfhandle((new_err == NULL) ? 2 : _fileno(new_err->file));
/* Use _spawn family of functions. */ /* Use _spawn family of functions. */
/* Windows docs say do this before any spawns. */ /* Windows docs say do this before any spawns. */
_flushall(); _flushall();
/* Use an empty env instead when envp is NULL to be consistent with other implementation. */ /* TODO - redirection, :p flag */
char *empty_env[1] = {NULL}; if(!CreateProcess(janet_flag_at(flags, 1) ? NULL : path, /* NULL? */
char **envp1 = (NULL == envp) ? empty_env : envp; (char *) buf->data, /* Single CLI argument */
NULL, /* no proc inheritance */
int spawn_type = is_async ? _P_NOWAIT : _P_WAIT; NULL, /* no thread inheritance */
intptr_t spawn_result; TRUE, /* handle inheritance */
if (janet_flag_at(flags, 1) && janet_flag_at(flags, 0)) { 0, /* flags */
spawn_result = (int) _spawnvpe(spawn_type, path, cargv, envp1); envp, /* pass in environment */
} else if (janet_flag_at(flags, 1)) { NULL, /* use parents starting directory */
spawn_result = (int) _spawnvp(spawn_type, path, cargv); &startupInfo,
} else if (janet_flag_at(flags, 0)) { &processInfo)) {
spawn_result = (int) _spawnve(spawn_type, path, cargv, envp1); janet_panic("failed to create process");
} else {
spawn_result = (int) _spawnv(spawn_type, path, cargv);
} }
pHandle = processInfo.hProcess;
tHandle = processInfo.hThread;
os_execute_cleanup(envp, NULL); os_execute_cleanup(envp, NULL);
/* Check error */ /* Wait and cleanup immedaitely */
if (-1 == spawn_result) { if (!is_async) {
janet_panicf("%p: %s", argv[0], strerror(errno)); DWORD code;
} WaitForSingleObject(pHandle, INFINITE);
GetExitCodeProcess(pHandle, &code);
if (is_async) { status = (int) code;
pid = (HANDLE) spawn_result; CloseHandle(pHandle);
} else { CloseHandle(tHandle);
status = (int) spawn_result;
} }
#else #else
@ -574,7 +590,12 @@ static Janet os_execute(int32_t argc, Janet *argv) {
if (is_async) { if (is_async) {
JanetProc *proc = janet_abstract(&ProcAT, sizeof(JanetProc)); JanetProc *proc = janet_abstract(&ProcAT, sizeof(JanetProc));
proc->return_code = -1; proc->return_code = -1;
proc->pid = pid;; #ifdef JANET_WINDOWS
proc->pHandle = pHandle;
proc->tHandle = tHandle;
#else
proc->pid = pid;
#endif
proc->in = new_in; proc->in = new_in;
proc->out = new_out; proc->out = new_out;
proc->err = new_err; proc->err = new_err;