Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(436)

Side by Side Diff: src/trusted/platform/posix/nacl_process.c

Issue 10832400: Process abstraction layer for NaCl Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client
Patch Set: Provide consistent interface across all platforms. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 <errno.h>
10 #include <fcntl.h>
11 #include <limits.h>
12 #include <signal.h>
13 #include <stdlib.h>
14 #include <sys/resource.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <unistd.h>
19
20 #if NACL_LINUX
21 #include <dirent.h>
22 #include <sys/stat.h>
23 #include <sys/syscall.h>
24 #elif NACL_OSX
25 #include <mach/mach.h>
26 #endif
27
28 #include "native_client/src/include/nacl_macros.h"
29 #include "native_client/src/include/portability.h"
30 #include "native_client/src/include/portability_string.h"
31
32 #include "native_client/src/shared/platform/nacl_check.h"
33 #include "native_client/src/shared/platform/nacl_log.h"
34 #include "native_client/src/shared/platform/nacl_sync.h"
35 #include "native_client/src/shared/platform/nacl_sync_checked.h"
36
37 #include "native_client/src/trusted/service_runtime/nacl_signal.h"
38
39 #if NACL_OSX
40 #include <crt_externs.h>
41 #include <sys/event.h>
42 #else
43 extern char **environ;
44 #endif
45
46 #if NACL_LINUX
47 struct linux_dirent {
48 long d_ino;
49 off_t d_off;
50 unsigned short d_reclen;
51 char d_name[];
52 };
53 #endif
54
55 static const int kSignals[] = {
Mark Seaborn 2012/08/24 00:22:42 This is duplicating code from nacl_signal.c.
56 #if NACL_LINUX
57 SIGSTKFLT,
58 NACL_THREAD_SUSPEND_SIGNAL,
59 #endif
60 SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV
61 };
62
63 #if NACL_OSX
64 #define NACL_MACH_EXCEPTION_MASK EXC_MASK_BAD_ACCESS
Mark Seaborn 2012/08/24 00:22:42 This is duplicating code from osx/mach_exception_h
65 #endif
66
67 #if NACL_LINUX
68 static const rlim_t kSystemDefaultMaxFds = 8192;
69 static const char *kFDDir = "/proc/self/fd";
70 #else
71 static const rlim_t kSystemDefaultMaxFds = 256;
72 /*static const char *kFDDir = "/dev/fd";*/
73 #endif
74
75 static void NaClResetSignalHandlers() {
76 struct sigaction dfl;
77 size_t i;
78 #if NACL_OSX
79 int rv = task_set_exception_ports(mach_task_self(), NACL_MACH_EXCEPTION_MASK,
80 MACH_PORT_NULL, EXCEPTION_DEFAULT,
81 THREAD_STATE_NONE);
82 if (rv != KERN_SUCCESS) {
83 NaClLog(LOG_FATAL, "Failed to unregister default exception handler.\n");
84 }
85 #endif
86
87 memset(&dfl, 0, sizeof dfl);
88 dfl.sa_handler = SIG_DFL;
89 CHECK(sigemptyset(&dfl.sa_mask) == 0);
90
91 for (i = 0; i < NACL_ARRAY_SIZE(kSignals); i++) {
92 if (sigaction(kSignals[i], &dfl, NULL) != 0) {
93 NaClLog(LOG_FATAL,
94 "Failed to unregister handler for %d with error %d\n",
95 kSignals[i], errno);
96 }
97 }
98 }
99
100 static void NaClCloseAllFds() {
101 struct rlimit nofile;
102 rlim_t max_fds;
103 int fd;
104 #if NACL_LINUX
105 unsigned char buf[512];
106 size_t offset = 0;
107 size_t size = 0;
108 int dir_fd;
109 #endif
110
111 /* Get the maximum number of FDs possible. */
112 if (getrlimit(RLIMIT_NOFILE, &nofile)) {
113 NaClLog(LOG_ERROR, "NaClProcessFork: getrlimit failed.\n");
114 max_fds = kSystemDefaultMaxFds;
115 } else {
116 max_fds = nofile.rlim_cur;
117 }
118
119 if (max_fds > INT_MAX) {
120 max_fds = INT_MAX;
121 }
122
123 #if NACL_LINUX
124 dir_fd = open(kFDDir, O_RDONLY | O_DIRECTORY);
Mark Seaborn 2012/08/24 00:22:42 I am sceptical you really want to close all FDs.
125 if (-1 == dir_fd) {
126 NaClLog(LOG_FATAL,
127 "NaClProcessFork: failed to open %s, error %d.\n",
128 kFDDir, errno);
129 }
130
131 for (;;) {
132 struct linux_dirent *dirent;
133 char *endptr;
134 int rv;
135
136 if (size != 0) {
137 dirent = (struct linux_dirent *)&buf[offset];
138 offset += dirent->d_reclen;
139 }
140 if (offset == size) {
141 rv = syscall(__NR_getdents64, dir_fd, buf, sizeof buf);
Mark Seaborn 2012/08/24 00:22:42 Why are you using the getdents syscall directly?
142 if (rv == 0) {
143 /* We are done, there are no more entries */
144 break;
145 } else if (rv == -1) {
146 NaClLog(LOG_ERROR,
147 "NaClCloseAllFds: getdents64 failed error %d\n", errno);
148 break;
149 }
150 size = rv;
151 offset = 0;
152 }
153 dirent = (struct linux_dirent *)&buf[offset];
154
155 /* Skip . and .. entries. */
156 if (dirent->d_name[0] == '.') {
157 continue;
158 }
159
160 fd = strtol(dirent->d_name, &endptr, 10);
161 if (endptr != NULL || fd < 0) {
162 continue;
163 }
164
165 if (fd == STDIN_FILENO ||
166 fd == STDOUT_FILENO ||
167 fd == STDERR_FILENO ||
168 fd == dir_fd) {
169 continue;
170 }
171
172 /* Valgrind opens FDs >= |max_fds|, handle them here. */
173 if (fd < (int) max_fds) {
174 DCHECK(close(fd) == 0);
175 }
176 }
177
178 DCHECK(close(dir_fd) == 0);
179 #else
180 for (fd = 0; fd < (int) max_fds; ++fd) {
181 if (fd == STDIN_FILENO ||
182 fd == STDOUT_FILENO ||
183 fd == STDERR_FILENO) {
184 continue;
185 }
186
187 /*
188 * We deliberately ignore any errors because we do
Mark Seaborn 2012/08/24 00:22:42 It would be better to check for EBADF and fail if
189 * not know whether the filedescriptor is valid.
190 */
191 close(fd);
192 }
193 #endif
194 }
195
196 int NaClProcessLaunch(struct NaClProcess *npp,
197 char *const *argv,
198 char *const *envp,
199 int flags) {
200 pid_t pid;
201
202 NaClLog(2,
203 "NaClProcessLaunch(0x%08"NACL_PRIxPTR")\n",
204 (uintptr_t) npp);
205
206 CHECK(npp != NULL);
207
208 pid = fork();
209 if (pid < 0) {
210 NaClLog(LOG_ERROR, "NaClProcessFork: fork failed\n");
211 return 0;
212 } else if (pid == 0) {
213 /* Child process */
214 int null_fd;
215 int new_fd;
216
217 /* We do not want parent and child to share standard input. */
218 null_fd = open("/dev/null", O_RDONLY);
Mark Seaborn 2012/08/24 00:22:42 This doesn't work inside an outer sandbox...
219 if (null_fd < 0) {
220 NaClLog(LOG_ERROR,
221 "NaClProcessFork: failed to open /dev/null\n");
222 _exit(127);
223 }
224
225 new_fd = dup2(null_fd, STDIN_FILENO);
226 if (new_fd != STDIN_FILENO) {
227 NaClLog(LOG_ERROR,
228 "NaClProcessFork: failed to dup /dev/null for stdin\n");
229 _exit(127);
230 }
231 DCHECK(close(null_fd) != 0);
232
233 if (0 != (flags & NACL_PROCESS_LAUNCH_NEW_GROUP)) {
234 /* Setup new process group. */
235 if (setpgid(0, 0) < 0) {
236 NaClLog(LOG_ERROR,
237 "NaClProcessFork: setpgid failed error %d\n", errno);
238 _exit(127);
239 }
240 }
241
242 /*
243 * The previous signal handlers are likely to be meaningless in
244 * the child's context so we reset them to the defaults.
245 */
246 NaClResetSignalHandlers();
247
248 if (0 != (flags & NACL_PROCESS_LAUNCH_CLOSE_FDS)) {
249 NaClCloseAllFds();
250 }
251
252 if (NULL != envp) {
253 #if NACL_OSX
254 *_NSGetEnviron() = (char **) envp;
255 #else
256 environ = (char **) envp;
257 #endif
258 }
259
260 NaClLog(4,
261 "NaClProcessLaunch: exec application '%s'\n",
262 argv[0]);
263
264 execvp(argv[0], argv);
265
266 /*
267 * When successful, exec* does not return, so if we reached
268 * here, there must have been an error; report it.
269 */
270 NaClLog(LOG_FATAL,
271 "NaclProcessSpawn: failed to execvp, error %d\n",
272 errno);
273 }
274
275 /* Parent process */
276 NaClLog(4, "NaClProcessFork: forked child process %d\n", pid);
277
278 if (npp != NULL) {
Mark Seaborn 2012/08/24 00:22:42 You already did CHECK(npp != NULL) earlier
279 npp->pid = pid;
280 }
281
282 return 1;
283 }
284
285 /*
286 * Attempts to kill the process identified by the given process handle.
287 * The exit_code is ignored since POSIX can't enforce that.
288 */
289 int NaClProcessKill(struct NaClProcess *npp, int exit_code, int wait) {
Mark Seaborn 2012/08/24 00:22:42 I don't see any tests that cover this function...
290 static unsigned int kMaxSleepMs = 1000;
291 int retval;
292 UNREFERENCED_PARAMETER(exit_code);
293
294 NaClLog(2,
295 "NaClProcessKill(0x%08"NACL_PRIxPTR", %d, %d)\n",
296 (uintptr_t) npp, exit_code, wait);
297
298 CHECK(npp != NULL);
299 CHECK(npp->pid > 1);
300
301 retval = kill(npp->pid, SIGTERM);
Mark Seaborn 2012/08/24 00:22:42 Why are you trying SIGTERM and then trying SIGKILL
302 if (-1 == retval) {
303 NaClLog(LOG_ERROR,
304 "NaClProcessKill: unable to terminate process %d\n", errno);
305 goto done;
306 }
307
308 if (wait != 0) {
309 unsigned int sleep_ms = 4;
310 int retries = 60;
311 int exited = 0;
312
313 /* The process may not end immediately due to pending I/O */
314 while (retries-- > 0) {
315 pid_t pid = waitpid(npp->pid, NULL, WNOHANG);
316 if (pid == npp->pid) {
317 exited = 1;
318 break;
319 } else if (pid == -1) {
320 if (ECHILD == errno) {
321 /*
322 * The wait may fail with ECHILD if another process also waited for
323 * the same pid, causing the process state to get cleaned up.
324 */
325 exited = 1;
326 break;
327 }
328 NaClLog(LOG_ERROR,
329 "NaClProcessKill: waitpid(%d) returned error %d\n",
330 npp->pid, errno);
331 }
332
333 usleep(sleep_ms * 1000);
334 if (sleep_ms < kMaxSleepMs) {
335 sleep_ms *= 2;
336 }
337 }
338
339 /*
340 * If we're waiting and the child hasn't died by now, force it
341 * with a SIGKILL.
342 */
343 if (!exited) {
344 if (-1 == (retval = kill(npp->pid, SIGKILL))) {
345 NaClLog(LOG_ERROR,
346 "NaClProcessKill: failed to kill process %d\n", errno);
347 }
348 }
349 }
350
351 done:
352 return retval;
353 }
354
355 int NaClProcessGetStatus(struct NaClProcess *npp,
356 int *status) {
357 int tmp_status = 0;
358 pid_t pid;
359
360 NaClLog(2,
361 ("NaClProcessGetStatus(0x%08"NACL_PRIxPTR
362 ", 0x%08"NACL_PRIxPTR")\n"),
363 (uintptr_t) npp, (uintptr_t) status);
364
365 CHECK(npp != NULL);
366
367 NaClLog(4,
368 "NaClProcessGetStatus: checking status of process %d\n",
369 (int) npp->pid);
370
371 pid = waitpid(npp->pid, &tmp_status, WNOHANG);
372 if (pid == -1) {
373 NaClLog(LOG_ERROR,
374 "NaClProcessGetStatus: waitpid(%d) returned error %d\n",
375 npp->pid, errno);
376 return 0;
377 } else if (pid == 0) {
378 *status = NACL_PROCESS_STATUS_STILL_RUNNING;
379 goto done;
380 }
381
382 if (WIFSIGNALED(tmp_status)) {
383 switch (WTERMSIG(tmp_status)) {
384 case SIGABRT:
385 case SIGBUS:
386 case SIGFPE:
387 case SIGILL:
388 case SIGSEGV:
389 *status = NACL_PROCESS_STATUS_CRASHED;
390 goto done;
391 case SIGINT:
392 case SIGKILL:
393 case SIGTERM:
394 *status = NACL_PROCESS_STATUS_KILLED;
395 goto done;
396 default:
397 break;
398 }
399 }
400
401 if (WIFEXITED(tmp_status) != 0 && WEXITSTATUS(tmp_status) != 0) {
402 *status = NACL_PROCESS_STATUS_ABNORMAL_EXIT;
403 goto done;
404 }
405 *status = NACL_PROCESS_STATUS_NORMAL_EXIT;
406
407 done:
408 return 1;
409 }
410
411 int NaClProcessWaitForExitCode(struct NaClProcess *npp,
412 int *exit_code) {
413 int status;
414
415 NaClLog(2,
416 ("NaClProcessWaitForExitCode(0x%08"NACL_PRIxPTR
417 ", 0x%08"NACL_PRIxPTR")\n"),
418 (uintptr_t) npp, (uintptr_t) exit_code);
419
420 CHECK(npp != NULL);
421
422 if (-1 == waitpid(npp->pid, &status, 0)) {
423 return 0;
424 }
425
426 if (WIFEXITED(status) != 0) {
427 *exit_code = WEXITSTATUS(status);
428 return 1;
429 }
430
431 /* Check whether the process signaled */
432 CHECK(WIFSIGNALED(status) != 0);
433 return 0;
434 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698