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