Chromium Code Reviews| Index: src/trusted/platform/win/nacl_process.c |
| diff --git a/src/trusted/platform/win/nacl_process.c b/src/trusted/platform/win/nacl_process.c |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..021a875b45f2b831c95773e126bcab36d607d380 |
| --- /dev/null |
| +++ b/src/trusted/platform/win/nacl_process.c |
| @@ -0,0 +1,241 @@ |
| +/* |
| + * Copyright (c) 2012 The Native Client Authors. All rights reserved. |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#include "native_client/src/trusted/platform/nacl_process.h" |
| + |
| +#include <fcntl.h> |
| +#include <io.h> |
| +#include <windows.h> |
| +#include <userenv.h> |
| +#include <psapi.h> |
| + |
| +#include "native_client/src/include/portability.h" |
| +#include "native_client/src/include/portability_string.h" |
| + |
| +#include "native_client/src/shared/platform/nacl_check.h" |
| +#include "native_client/src/shared/platform/nacl_log.h" |
| +#include "native_client/src/shared/platform/nacl_sync.h" |
| +#include "native_client/src/shared/platform/nacl_sync_checked.h" |
| + |
| +#ifndef STATUS_SUCCESS |
|
Mark Seaborn
2012/08/24 00:22:42
Aren't these defined by the Windows headers? If s
|
| +#define STATUS_SUCCESS ((NTSTATUS) 0) |
| +#endif |
| +#ifndef STATUS_WAIT_1 |
| +#define STATUS_WAIT_1 ((NTSTATUS) 0x00000001) |
| +#endif |
| +#ifndef STATUS_DEBUGGER_INACTIVE |
| +#define STATUS_DEBUGGER_INACTIVE ((NTSTATUS) 0xC0000354) |
| +#endif |
| +#ifndef STATUS_CONTROL_C_EXIT |
| +#define STATUS_CONTROL_C_EXIT ((NTSTATUS) 0xC000013A) |
| +#endif |
| +#ifndef DBG_TERMINATE_PROCESS |
| +#define DBG_TERMINATE_PROCESS ((NTSTATUS) 0x40010004) |
| +#endif |
| + |
| +static char *NaClEscapeShellArg(const char *str) { |
|
Mark Seaborn
2012/08/24 00:22:42
I don't know whether this properly implements Wind
|
| + const char *p; |
| + char *esc; |
| + char *q; |
| + |
| + esc = calloc(2 * strlen(str) + 1, sizeof *esc); |
| + for (p = str, q = esc; 0 != *p; ++p) { |
| + if (*p == '"') { |
| + *q++ = '\\'; |
| + } else if (*p == '\\' && *(p + 1) == '"') { |
| + *q++ = '\\'; |
| + } |
| + *q++ = *p; |
| + } |
| + |
| + return esc; |
| +} |
| + |
| +int NaClProcessLaunch(struct NaClProcess *npp, |
| + char *const *argv, |
| + char *const *envp, |
| + int flags) { |
| + char cmd[MAX_PATH]; |
| + char const *const *p; |
| + size_t off; |
| + STARTUPINFO startup_info; |
| + PROCESS_INFORMATION process_info; |
| + DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT; |
| + BOOL inherit; |
| + |
| + NaClLog(2, |
| + ("NaClProcessLaunch(0x%08"NACL_PRIxPTR", 0x%08"NACL_PRIxPTR |
| + ", 0x%08"NACL_PRIxPTR", 0x%x)\n"), |
| + (uintptr_t) npp, (uintptr_t) argv, (uintptr_t) envp, flags); |
| + |
| + CHECK(npp != NULL); |
| + |
| + ZeroMemory(&startup_info, sizeof startup_info); |
|
Mark Seaborn
2012/08/24 00:22:42
Using memset() is more usual, since it's a better-
|
| + startup_info.cb = sizeof startup_info; |
| + startup_info.dwFlags = STARTF_USESTDHANDLES; |
| + |
| + /* Convert arguments to a single command string */ |
| + off = 0; |
| + for (p = argv; NULL != *p; ++p) { |
| + size_t len; |
| + char *arg; |
| + if (p != argv) { |
| + cmd[off++] = ' '; |
| + } |
| + arg = NaClEscapeShellArg(*p); |
| + len = _snprintf(cmd + off, MAX_PATH - off, "\"%s\"", arg); |
| + free(arg); |
| + if (MAX_PATH - off < len) { |
| + break; |
| + } |
| + off += len; |
| + } |
| + cmd[off] = '\0'; |
| + |
| + if ((flags & NACL_PROCESS_LAUNCH_CLOSE_FDS) != 0) { |
| + inherit = FALSE; |
| + } else { |
| + inherit = TRUE; |
| + } |
| + |
| + if (0 != (flags & NACL_PROCESS_LAUNCH_NEW_GROUP)) { |
| + creation_flags |= CREATE_NEW_PROCESS_GROUP; |
| + } |
| + |
| + if (!CreateProcess(/* lpApplicationName= */ NULL, |
| + cmd, |
| + /* lpProcessAttributes= */ NULL, |
| + /* lpThreadAttributes= */ NULL, |
| + inherit, |
| + creation_flags, |
| + envp, |
| + /* lpCurrentDirectory= */NULL, |
| + &startup_info, |
| + &process_info)) { |
| + NaClLog(LOG_ERROR, |
| + "NaClProcessSpawn: CreateProcess failed, error %d\n", |
| + GetLastError()); |
| + return 0; |
| + } |
| + |
| + /* TODO(phosek): might we ever need this handle? */ |
| + DCHECK(CloseHandle(process_info.hThread)); |
| + |
| + if (npp != NULL) { |
| + npp->handle = process_info.hProcess; |
| + } |
| + |
| + return 1; |
| +} |
| + |
| +int NaClProcessKill(struct NaClProcess *npp, int exit_code, int wait) { |
| + BOOL retval; |
| + |
| + NaClLog(2, |
| + "NaClProcessKill(0x%08"NACL_PRIxPTR", %d, %d)\n", |
| + (uintptr_t) npp, exit_code, wait); |
| + |
| + CHECK(npp != NULL); |
| + |
| + retval = TerminateProcess(npp->handle, exit_code); |
| + if (FALSE == retval) { |
| + NaClLog(LOG_ERROR, |
| + "NaClProcessKill: unable to terminate process, error %d\n", |
| + GetLastError()); |
| + goto done; |
| + } |
| + |
| + if (wait != 0) { |
| + /* The process may not end immediately due to pending I/O */ |
| + if (WAIT_OBJECT_0 != WaitForSingleObject(npp->handle, 60 * 1000)) { |
|
Mark Seaborn
2012/08/24 00:22:42
Why a hard-coded timeout? What is the use case?
|
| + NaClLog(LOG_ERROR, |
| + "NaClProcessKill: waiting for process failed, error %d\n", |
| + GetLastError()); |
| + } |
| + } |
| + |
| + done: |
| + return retval != FALSE; |
| +} |
| + |
| +int NaClProcessGetStatus( |
| + struct NaClProcess *npp, |
| + int *status) { |
| + DWORD exit_code = 0; |
| + |
| + NaClLog(2, |
| + ("NaClProcessGetStatus(0x%08"NACL_PRIxPTR |
| + ", 0x%08"NACL_PRIxPTR")\n"), |
| + (uintptr_t) npp, (uintptr_t) status); |
| + |
| + CHECK(npp != NULL); |
| + |
| + NaClLog(4, |
| + "NaClProcessGetStatus: checking status of process %d\n", |
| + (int) npp->handle); |
| + |
| + if (!GetExitCodeProcess(npp->handle, &exit_code)) { |
| + NaClLog(LOG_ERROR, |
| + ("NaClProcessGetStatus: GetExitCodeProcess" |
| + "returned error %d\n"), |
| + GetLastError()); |
| + return 0; |
| + } |
| + |
| + if (STILL_ACTIVE == exit_code) { |
| + DWORD wait_result = WaitForSingleObject(npp->handle, 0); |
| + if (WAIT_TIMEOUT == wait_result) { |
| + *status = NACL_PROCESS_STATUS_STILL_RUNNING; |
| + return 1; |
| + } |
| + |
| + CHECK(WAIT_OBJECT_0 == wait_result); |
| + /* The process used 0x103 (STILL_ACTIVE) as exit code. */ |
| + *status = NACL_PROCESS_STATUS_ABNORMAL_EXIT; |
| + return 1; |
| + } |
| + |
| + switch (exit_code) { |
| + case STATUS_SUCCESS: /* Normal termination */ |
| + *status = NACL_PROCESS_STATUS_NORMAL_EXIT; |
| + break; |
| + case STATUS_DEBUGGER_INACTIVE: /* Debugger inactive */ |
| + case STATUS_CONTROL_C_EXIT: /* Keyboard interrupt */ |
| + case DBG_TERMINATE_PROCESS: /* Debugger terminated */ |
| + case STATUS_WAIT_1: /* Task manager killed */ |
| + *status = NACL_PROCESS_STATUS_KILLED; |
| + break; |
| + default: |
| + /* All other exit codes indicate crashes */ |
| + *status = NACL_PROCESS_STATUS_CRASHED; |
| + break; |
| + } |
| + |
| + return 1; |
| +} |
| + |
| +int NaClProcessWaitForExitCode(struct NaClProcess *npp, |
| + int *exit_code) { |
| + DWORD tmp_exit_code; |
| + |
| + NaClLog(2, |
| + ("NaClProcessWaitForExitCode(0x%08"NACL_PRIxPTR |
| + ", 0x%08"NACL_PRIxPTR")\n"), |
| + (uintptr_t) npp, (uintptr_t) exit_code); |
| + |
| + CHECK(npp != NULL); |
| + |
| + if (WAIT_OBJECT_0 != WaitForSingleObject(npp->handle, INFINITE)) { |
| + return 0; |
| + } |
| + |
| + if (!GetExitCodeProcess(npp->handle, &tmp_exit_code)) { |
| + return 0; |
| + } |
| + |
| + *exit_code = tmp_exit_code; |
| + return 1; |
| +} |