Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* | |
| 2 * Copyright (c) 2012 The Native Client Authors. All rights reserved. | |
| 3 * Use of this source code is governed by a BSD-style license that can be | |
| 4 * found in the LICENSE file. | |
| 5 */ | |
| 6 | |
| 7 #include "native_client/src/trusted/platform/nacl_process.h" | |
| 8 | |
| 9 #include <fcntl.h> | |
| 10 #include <io.h> | |
| 11 #include <windows.h> | |
| 12 #include <userenv.h> | |
| 13 #include <psapi.h> | |
| 14 | |
| 15 #include "native_client/src/include/portability.h" | |
| 16 #include "native_client/src/include/portability_string.h" | |
| 17 | |
| 18 #include "native_client/src/shared/platform/nacl_check.h" | |
| 19 #include "native_client/src/shared/platform/nacl_log.h" | |
| 20 #include "native_client/src/shared/platform/nacl_sync.h" | |
| 21 #include "native_client/src/shared/platform/nacl_sync_checked.h" | |
| 22 | |
| 23 #ifndef STATUS_SUCCESS | |
|
Mark Seaborn
2012/08/24 00:22:42
Aren't these defined by the Windows headers? If s
| |
| 24 #define STATUS_SUCCESS ((NTSTATUS) 0) | |
| 25 #endif | |
| 26 #ifndef STATUS_WAIT_1 | |
| 27 #define STATUS_WAIT_1 ((NTSTATUS) 0x00000001) | |
| 28 #endif | |
| 29 #ifndef STATUS_DEBUGGER_INACTIVE | |
| 30 #define STATUS_DEBUGGER_INACTIVE ((NTSTATUS) 0xC0000354) | |
| 31 #endif | |
| 32 #ifndef STATUS_CONTROL_C_EXIT | |
| 33 #define STATUS_CONTROL_C_EXIT ((NTSTATUS) 0xC000013A) | |
| 34 #endif | |
| 35 #ifndef DBG_TERMINATE_PROCESS | |
| 36 #define DBG_TERMINATE_PROCESS ((NTSTATUS) 0x40010004) | |
| 37 #endif | |
| 38 | |
| 39 static char *NaClEscapeShellArg(const char *str) { | |
|
Mark Seaborn
2012/08/24 00:22:42
I don't know whether this properly implements Wind
| |
| 40 const char *p; | |
| 41 char *esc; | |
| 42 char *q; | |
| 43 | |
| 44 esc = calloc(2 * strlen(str) + 1, sizeof *esc); | |
| 45 for (p = str, q = esc; 0 != *p; ++p) { | |
| 46 if (*p == '"') { | |
| 47 *q++ = '\\'; | |
| 48 } else if (*p == '\\' && *(p + 1) == '"') { | |
| 49 *q++ = '\\'; | |
| 50 } | |
| 51 *q++ = *p; | |
| 52 } | |
| 53 | |
| 54 return esc; | |
| 55 } | |
| 56 | |
| 57 int NaClProcessLaunch(struct NaClProcess *npp, | |
| 58 char *const *argv, | |
| 59 char *const *envp, | |
| 60 int flags) { | |
| 61 char cmd[MAX_PATH]; | |
| 62 char const *const *p; | |
| 63 size_t off; | |
| 64 STARTUPINFO startup_info; | |
| 65 PROCESS_INFORMATION process_info; | |
| 66 DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT; | |
| 67 BOOL inherit; | |
| 68 | |
| 69 NaClLog(2, | |
| 70 ("NaClProcessLaunch(0x%08"NACL_PRIxPTR", 0x%08"NACL_PRIxPTR | |
| 71 ", 0x%08"NACL_PRIxPTR", 0x%x)\n"), | |
| 72 (uintptr_t) npp, (uintptr_t) argv, (uintptr_t) envp, flags); | |
| 73 | |
| 74 CHECK(npp != NULL); | |
| 75 | |
| 76 ZeroMemory(&startup_info, sizeof startup_info); | |
|
Mark Seaborn
2012/08/24 00:22:42
Using memset() is more usual, since it's a better-
| |
| 77 startup_info.cb = sizeof startup_info; | |
| 78 startup_info.dwFlags = STARTF_USESTDHANDLES; | |
| 79 | |
| 80 /* Convert arguments to a single command string */ | |
| 81 off = 0; | |
| 82 for (p = argv; NULL != *p; ++p) { | |
| 83 size_t len; | |
| 84 char *arg; | |
| 85 if (p != argv) { | |
| 86 cmd[off++] = ' '; | |
| 87 } | |
| 88 arg = NaClEscapeShellArg(*p); | |
| 89 len = _snprintf(cmd + off, MAX_PATH - off, "\"%s\"", arg); | |
| 90 free(arg); | |
| 91 if (MAX_PATH - off < len) { | |
| 92 break; | |
| 93 } | |
| 94 off += len; | |
| 95 } | |
| 96 cmd[off] = '\0'; | |
| 97 | |
| 98 if ((flags & NACL_PROCESS_LAUNCH_CLOSE_FDS) != 0) { | |
| 99 inherit = FALSE; | |
| 100 } else { | |
| 101 inherit = TRUE; | |
| 102 } | |
| 103 | |
| 104 if (0 != (flags & NACL_PROCESS_LAUNCH_NEW_GROUP)) { | |
| 105 creation_flags |= CREATE_NEW_PROCESS_GROUP; | |
| 106 } | |
| 107 | |
| 108 if (!CreateProcess(/* lpApplicationName= */ NULL, | |
| 109 cmd, | |
| 110 /* lpProcessAttributes= */ NULL, | |
| 111 /* lpThreadAttributes= */ NULL, | |
| 112 inherit, | |
| 113 creation_flags, | |
| 114 envp, | |
| 115 /* lpCurrentDirectory= */NULL, | |
| 116 &startup_info, | |
| 117 &process_info)) { | |
| 118 NaClLog(LOG_ERROR, | |
| 119 "NaClProcessSpawn: CreateProcess failed, error %d\n", | |
| 120 GetLastError()); | |
| 121 return 0; | |
| 122 } | |
| 123 | |
| 124 /* TODO(phosek): might we ever need this handle? */ | |
| 125 DCHECK(CloseHandle(process_info.hThread)); | |
| 126 | |
| 127 if (npp != NULL) { | |
| 128 npp->handle = process_info.hProcess; | |
| 129 } | |
| 130 | |
| 131 return 1; | |
| 132 } | |
| 133 | |
| 134 int NaClProcessKill(struct NaClProcess *npp, int exit_code, int wait) { | |
| 135 BOOL retval; | |
| 136 | |
| 137 NaClLog(2, | |
| 138 "NaClProcessKill(0x%08"NACL_PRIxPTR", %d, %d)\n", | |
| 139 (uintptr_t) npp, exit_code, wait); | |
| 140 | |
| 141 CHECK(npp != NULL); | |
| 142 | |
| 143 retval = TerminateProcess(npp->handle, exit_code); | |
| 144 if (FALSE == retval) { | |
| 145 NaClLog(LOG_ERROR, | |
| 146 "NaClProcessKill: unable to terminate process, error %d\n", | |
| 147 GetLastError()); | |
| 148 goto done; | |
| 149 } | |
| 150 | |
| 151 if (wait != 0) { | |
| 152 /* The process may not end immediately due to pending I/O */ | |
| 153 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?
| |
| 154 NaClLog(LOG_ERROR, | |
| 155 "NaClProcessKill: waiting for process failed, error %d\n", | |
| 156 GetLastError()); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 done: | |
| 161 return retval != FALSE; | |
| 162 } | |
| 163 | |
| 164 int NaClProcessGetStatus( | |
| 165 struct NaClProcess *npp, | |
| 166 int *status) { | |
| 167 DWORD exit_code = 0; | |
| 168 | |
| 169 NaClLog(2, | |
| 170 ("NaClProcessGetStatus(0x%08"NACL_PRIxPTR | |
| 171 ", 0x%08"NACL_PRIxPTR")\n"), | |
| 172 (uintptr_t) npp, (uintptr_t) status); | |
| 173 | |
| 174 CHECK(npp != NULL); | |
| 175 | |
| 176 NaClLog(4, | |
| 177 "NaClProcessGetStatus: checking status of process %d\n", | |
| 178 (int) npp->handle); | |
| 179 | |
| 180 if (!GetExitCodeProcess(npp->handle, &exit_code)) { | |
| 181 NaClLog(LOG_ERROR, | |
| 182 ("NaClProcessGetStatus: GetExitCodeProcess" | |
| 183 "returned error %d\n"), | |
| 184 GetLastError()); | |
| 185 return 0; | |
| 186 } | |
| 187 | |
| 188 if (STILL_ACTIVE == exit_code) { | |
| 189 DWORD wait_result = WaitForSingleObject(npp->handle, 0); | |
| 190 if (WAIT_TIMEOUT == wait_result) { | |
| 191 *status = NACL_PROCESS_STATUS_STILL_RUNNING; | |
| 192 return 1; | |
| 193 } | |
| 194 | |
| 195 CHECK(WAIT_OBJECT_0 == wait_result); | |
| 196 /* The process used 0x103 (STILL_ACTIVE) as exit code. */ | |
| 197 *status = NACL_PROCESS_STATUS_ABNORMAL_EXIT; | |
| 198 return 1; | |
| 199 } | |
| 200 | |
| 201 switch (exit_code) { | |
| 202 case STATUS_SUCCESS: /* Normal termination */ | |
| 203 *status = NACL_PROCESS_STATUS_NORMAL_EXIT; | |
| 204 break; | |
| 205 case STATUS_DEBUGGER_INACTIVE: /* Debugger inactive */ | |
| 206 case STATUS_CONTROL_C_EXIT: /* Keyboard interrupt */ | |
| 207 case DBG_TERMINATE_PROCESS: /* Debugger terminated */ | |
| 208 case STATUS_WAIT_1: /* Task manager killed */ | |
| 209 *status = NACL_PROCESS_STATUS_KILLED; | |
| 210 break; | |
| 211 default: | |
| 212 /* All other exit codes indicate crashes */ | |
| 213 *status = NACL_PROCESS_STATUS_CRASHED; | |
| 214 break; | |
| 215 } | |
| 216 | |
| 217 return 1; | |
| 218 } | |
| 219 | |
| 220 int NaClProcessWaitForExitCode(struct NaClProcess *npp, | |
| 221 int *exit_code) { | |
| 222 DWORD tmp_exit_code; | |
| 223 | |
| 224 NaClLog(2, | |
| 225 ("NaClProcessWaitForExitCode(0x%08"NACL_PRIxPTR | |
| 226 ", 0x%08"NACL_PRIxPTR")\n"), | |
| 227 (uintptr_t) npp, (uintptr_t) exit_code); | |
| 228 | |
| 229 CHECK(npp != NULL); | |
| 230 | |
| 231 if (WAIT_OBJECT_0 != WaitForSingleObject(npp->handle, INFINITE)) { | |
| 232 return 0; | |
| 233 } | |
| 234 | |
| 235 if (!GetExitCodeProcess(npp->handle, &tmp_exit_code)) { | |
| 236 return 0; | |
| 237 } | |
| 238 | |
| 239 *exit_code = tmp_exit_code; | |
| 240 return 1; | |
| 241 } | |
| OLD | NEW |