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 |