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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: src/trusted/platform/posix/nacl_process.c
diff --git a/src/trusted/platform/posix/nacl_process.c b/src/trusted/platform/posix/nacl_process.c
new file mode 100644
index 0000000000000000000000000000000000000000..ca970aecf7636f44fe6ea840e3c781c7bbd7316c
--- /dev/null
+++ b/src/trusted/platform/posix/nacl_process.c
@@ -0,0 +1,434 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#if NACL_LINUX
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#elif NACL_OSX
+#include <mach/mach.h>
+#endif
+
+#include "native_client/src/include/nacl_macros.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"
+
+#include "native_client/src/trusted/service_runtime/nacl_signal.h"
+
+#if NACL_OSX
+#include <crt_externs.h>
+#include <sys/event.h>
+#else
+extern char **environ;
+#endif
+
+#if NACL_LINUX
+struct linux_dirent {
+ long d_ino;
+ off_t d_off;
+ unsigned short d_reclen;
+ char d_name[];
+};
+#endif
+
+static const int kSignals[] = {
Mark Seaborn 2012/08/24 00:22:42 This is duplicating code from nacl_signal.c.
+#if NACL_LINUX
+ SIGSTKFLT,
+ NACL_THREAD_SUSPEND_SIGNAL,
+#endif
+ SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV
+};
+
+#if NACL_OSX
+#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
+#endif
+
+#if NACL_LINUX
+static const rlim_t kSystemDefaultMaxFds = 8192;
+static const char *kFDDir = "/proc/self/fd";
+#else
+static const rlim_t kSystemDefaultMaxFds = 256;
+/*static const char *kFDDir = "/dev/fd";*/
+#endif
+
+static void NaClResetSignalHandlers() {
+ struct sigaction dfl;
+ size_t i;
+#if NACL_OSX
+ int rv = task_set_exception_ports(mach_task_self(), NACL_MACH_EXCEPTION_MASK,
+ MACH_PORT_NULL, EXCEPTION_DEFAULT,
+ THREAD_STATE_NONE);
+ if (rv != KERN_SUCCESS) {
+ NaClLog(LOG_FATAL, "Failed to unregister default exception handler.\n");
+ }
+#endif
+
+ memset(&dfl, 0, sizeof dfl);
+ dfl.sa_handler = SIG_DFL;
+ CHECK(sigemptyset(&dfl.sa_mask) == 0);
+
+ for (i = 0; i < NACL_ARRAY_SIZE(kSignals); i++) {
+ if (sigaction(kSignals[i], &dfl, NULL) != 0) {
+ NaClLog(LOG_FATAL,
+ "Failed to unregister handler for %d with error %d\n",
+ kSignals[i], errno);
+ }
+ }
+}
+
+static void NaClCloseAllFds() {
+ struct rlimit nofile;
+ rlim_t max_fds;
+ int fd;
+#if NACL_LINUX
+ unsigned char buf[512];
+ size_t offset = 0;
+ size_t size = 0;
+ int dir_fd;
+#endif
+
+ /* Get the maximum number of FDs possible. */
+ if (getrlimit(RLIMIT_NOFILE, &nofile)) {
+ NaClLog(LOG_ERROR, "NaClProcessFork: getrlimit failed.\n");
+ max_fds = kSystemDefaultMaxFds;
+ } else {
+ max_fds = nofile.rlim_cur;
+ }
+
+ if (max_fds > INT_MAX) {
+ max_fds = INT_MAX;
+ }
+
+#if NACL_LINUX
+ 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.
+ if (-1 == dir_fd) {
+ NaClLog(LOG_FATAL,
+ "NaClProcessFork: failed to open %s, error %d.\n",
+ kFDDir, errno);
+ }
+
+ for (;;) {
+ struct linux_dirent *dirent;
+ char *endptr;
+ int rv;
+
+ if (size != 0) {
+ dirent = (struct linux_dirent *)&buf[offset];
+ offset += dirent->d_reclen;
+ }
+ if (offset == size) {
+ 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?
+ if (rv == 0) {
+ /* We are done, there are no more entries */
+ break;
+ } else if (rv == -1) {
+ NaClLog(LOG_ERROR,
+ "NaClCloseAllFds: getdents64 failed error %d\n", errno);
+ break;
+ }
+ size = rv;
+ offset = 0;
+ }
+ dirent = (struct linux_dirent *)&buf[offset];
+
+ /* Skip . and .. entries. */
+ if (dirent->d_name[0] == '.') {
+ continue;
+ }
+
+ fd = strtol(dirent->d_name, &endptr, 10);
+ if (endptr != NULL || fd < 0) {
+ continue;
+ }
+
+ if (fd == STDIN_FILENO ||
+ fd == STDOUT_FILENO ||
+ fd == STDERR_FILENO ||
+ fd == dir_fd) {
+ continue;
+ }
+
+ /* Valgrind opens FDs >= |max_fds|, handle them here. */
+ if (fd < (int) max_fds) {
+ DCHECK(close(fd) == 0);
+ }
+ }
+
+ DCHECK(close(dir_fd) == 0);
+#else
+ for (fd = 0; fd < (int) max_fds; ++fd) {
+ if (fd == STDIN_FILENO ||
+ fd == STDOUT_FILENO ||
+ fd == STDERR_FILENO) {
+ continue;
+ }
+
+ /*
+ * 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
+ * not know whether the filedescriptor is valid.
+ */
+ close(fd);
+ }
+#endif
+}
+
+int NaClProcessLaunch(struct NaClProcess *npp,
+ char *const *argv,
+ char *const *envp,
+ int flags) {
+ pid_t pid;
+
+ NaClLog(2,
+ "NaClProcessLaunch(0x%08"NACL_PRIxPTR")\n",
+ (uintptr_t) npp);
+
+ CHECK(npp != NULL);
+
+ pid = fork();
+ if (pid < 0) {
+ NaClLog(LOG_ERROR, "NaClProcessFork: fork failed\n");
+ return 0;
+ } else if (pid == 0) {
+ /* Child process */
+ int null_fd;
+ int new_fd;
+
+ /* We do not want parent and child to share standard input. */
+ null_fd = open("/dev/null", O_RDONLY);
Mark Seaborn 2012/08/24 00:22:42 This doesn't work inside an outer sandbox...
+ if (null_fd < 0) {
+ NaClLog(LOG_ERROR,
+ "NaClProcessFork: failed to open /dev/null\n");
+ _exit(127);
+ }
+
+ new_fd = dup2(null_fd, STDIN_FILENO);
+ if (new_fd != STDIN_FILENO) {
+ NaClLog(LOG_ERROR,
+ "NaClProcessFork: failed to dup /dev/null for stdin\n");
+ _exit(127);
+ }
+ DCHECK(close(null_fd) != 0);
+
+ if (0 != (flags & NACL_PROCESS_LAUNCH_NEW_GROUP)) {
+ /* Setup new process group. */
+ if (setpgid(0, 0) < 0) {
+ NaClLog(LOG_ERROR,
+ "NaClProcessFork: setpgid failed error %d\n", errno);
+ _exit(127);
+ }
+ }
+
+ /*
+ * The previous signal handlers are likely to be meaningless in
+ * the child's context so we reset them to the defaults.
+ */
+ NaClResetSignalHandlers();
+
+ if (0 != (flags & NACL_PROCESS_LAUNCH_CLOSE_FDS)) {
+ NaClCloseAllFds();
+ }
+
+ if (NULL != envp) {
+#if NACL_OSX
+ *_NSGetEnviron() = (char **) envp;
+#else
+ environ = (char **) envp;
+#endif
+ }
+
+ NaClLog(4,
+ "NaClProcessLaunch: exec application '%s'\n",
+ argv[0]);
+
+ execvp(argv[0], argv);
+
+ /*
+ * When successful, exec* does not return, so if we reached
+ * here, there must have been an error; report it.
+ */
+ NaClLog(LOG_FATAL,
+ "NaclProcessSpawn: failed to execvp, error %d\n",
+ errno);
+ }
+
+ /* Parent process */
+ NaClLog(4, "NaClProcessFork: forked child process %d\n", pid);
+
+ if (npp != NULL) {
Mark Seaborn 2012/08/24 00:22:42 You already did CHECK(npp != NULL) earlier
+ npp->pid = pid;
+ }
+
+ return 1;
+}
+
+/*
+ * Attempts to kill the process identified by the given process handle.
+ * The exit_code is ignored since POSIX can't enforce that.
+ */
+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...
+ static unsigned int kMaxSleepMs = 1000;
+ int retval;
+ UNREFERENCED_PARAMETER(exit_code);
+
+ NaClLog(2,
+ "NaClProcessKill(0x%08"NACL_PRIxPTR", %d, %d)\n",
+ (uintptr_t) npp, exit_code, wait);
+
+ CHECK(npp != NULL);
+ CHECK(npp->pid > 1);
+
+ retval = kill(npp->pid, SIGTERM);
Mark Seaborn 2012/08/24 00:22:42 Why are you trying SIGTERM and then trying SIGKILL
+ if (-1 == retval) {
+ NaClLog(LOG_ERROR,
+ "NaClProcessKill: unable to terminate process %d\n", errno);
+ goto done;
+ }
+
+ if (wait != 0) {
+ unsigned int sleep_ms = 4;
+ int retries = 60;
+ int exited = 0;
+
+ /* The process may not end immediately due to pending I/O */
+ while (retries-- > 0) {
+ pid_t pid = waitpid(npp->pid, NULL, WNOHANG);
+ if (pid == npp->pid) {
+ exited = 1;
+ break;
+ } else if (pid == -1) {
+ if (ECHILD == errno) {
+ /*
+ * The wait may fail with ECHILD if another process also waited for
+ * the same pid, causing the process state to get cleaned up.
+ */
+ exited = 1;
+ break;
+ }
+ NaClLog(LOG_ERROR,
+ "NaClProcessKill: waitpid(%d) returned error %d\n",
+ npp->pid, errno);
+ }
+
+ usleep(sleep_ms * 1000);
+ if (sleep_ms < kMaxSleepMs) {
+ sleep_ms *= 2;
+ }
+ }
+
+ /*
+ * If we're waiting and the child hasn't died by now, force it
+ * with a SIGKILL.
+ */
+ if (!exited) {
+ if (-1 == (retval = kill(npp->pid, SIGKILL))) {
+ NaClLog(LOG_ERROR,
+ "NaClProcessKill: failed to kill process %d\n", errno);
+ }
+ }
+ }
+
+ done:
+ return retval;
+}
+
+int NaClProcessGetStatus(struct NaClProcess *npp,
+ int *status) {
+ int tmp_status = 0;
+ pid_t pid;
+
+ 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->pid);
+
+ pid = waitpid(npp->pid, &tmp_status, WNOHANG);
+ if (pid == -1) {
+ NaClLog(LOG_ERROR,
+ "NaClProcessGetStatus: waitpid(%d) returned error %d\n",
+ npp->pid, errno);
+ return 0;
+ } else if (pid == 0) {
+ *status = NACL_PROCESS_STATUS_STILL_RUNNING;
+ goto done;
+ }
+
+ if (WIFSIGNALED(tmp_status)) {
+ switch (WTERMSIG(tmp_status)) {
+ case SIGABRT:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGSEGV:
+ *status = NACL_PROCESS_STATUS_CRASHED;
+ goto done;
+ case SIGINT:
+ case SIGKILL:
+ case SIGTERM:
+ *status = NACL_PROCESS_STATUS_KILLED;
+ goto done;
+ default:
+ break;
+ }
+ }
+
+ if (WIFEXITED(tmp_status) != 0 && WEXITSTATUS(tmp_status) != 0) {
+ *status = NACL_PROCESS_STATUS_ABNORMAL_EXIT;
+ goto done;
+ }
+ *status = NACL_PROCESS_STATUS_NORMAL_EXIT;
+
+ done:
+ return 1;
+}
+
+int NaClProcessWaitForExitCode(struct NaClProcess *npp,
+ int *exit_code) {
+ int status;
+
+ NaClLog(2,
+ ("NaClProcessWaitForExitCode(0x%08"NACL_PRIxPTR
+ ", 0x%08"NACL_PRIxPTR")\n"),
+ (uintptr_t) npp, (uintptr_t) exit_code);
+
+ CHECK(npp != NULL);
+
+ if (-1 == waitpid(npp->pid, &status, 0)) {
+ return 0;
+ }
+
+ if (WIFEXITED(status) != 0) {
+ *exit_code = WEXITSTATUS(status);
+ return 1;
+ }
+
+ /* Check whether the process signaled */
+ CHECK(WIFSIGNALED(status) != 0);
+ return 0;
+}

Powered by Google App Engine
This is Rietveld 408576698