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

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

Powered by Google App Engine
This is Rietveld 408576698