Replace most uses of assert by ovs_assert.
[sliver-openvswitch.git] / lib / process.c
index 0c7f424..3fc2e18 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
 
 #include <config.h>
 #include "process.h"
-#include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
 #include "fatal-signal.h"
 #include "list.h"
 #include "poll-loop.h"
+#include "signals.h"
 #include "socket-util.h"
 #include "util.h"
-
-#define THIS_MODULE VLM_process
 #include "vlog.h"
 
+VLOG_DEFINE_THIS_MODULE(process);
+
+COVERAGE_DEFINE(process_run);
+COVERAGE_DEFINE(process_run_capture);
+COVERAGE_DEFINE(process_sigchld);
+COVERAGE_DEFINE(process_start);
+
 struct process {
     struct list node;
     char *name;
@@ -55,7 +60,7 @@ static struct list all_processes = LIST_INITIALIZER(&all_processes);
 static bool sigchld_is_blocked(void);
 static void block_sigchld(sigset_t *);
 static void unblock_sigchld(const sigset_t *);
-static void sigchld_handler(int signr UNUSED);
+static void sigchld_handler(int signr OVS_UNUSED);
 static bool is_member(int x, const int *array, size_t);
 
 /* Initializes the process subsystem (if it is not already initialized).  Calls
@@ -76,20 +81,14 @@ process_init(void)
     inited = true;
 
     /* Create notification pipe. */
-    if (pipe(fds)) {
-        ovs_fatal(errno, "could not create pipe");
-    }
-    set_nonblocking(fds[0]);
-    set_nonblocking(fds[1]);
+    xpipe_nonblocking(fds);
 
     /* Set up child termination signal handler. */
     memset(&sa, 0, sizeof sa);
     sa.sa_handler = sigchld_handler;
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
-    if (sigaction(SIGCHLD, &sa, NULL)) {
-        ovs_fatal(errno, "sigaction(SIGCHLD) failed");
-    }
+    xsigaction(SIGCHLD, &sa, NULL);
 }
 
 char *
@@ -103,7 +102,7 @@ process_escape_args(char **argv)
         if (argp != argv) {
             ds_put_char(&ds, ' ');
         }
-        if (arg[strcspn(arg, " \t\r\n\v\\")]) {
+        if (arg[strcspn(arg, " \t\r\n\v\\\'\"")]) {
             ds_put_char(&ds, '"');
             for (p = arg; *p; p++) {
                 if (*p == '\\' || *p == '\"') {
@@ -159,7 +158,7 @@ process_register(const char *name, pid_t pid)
     struct process *p;
     const char *slash;
 
-    assert(sigchld_is_blocked());
+    ovs_assert(sigchld_is_blocked());
 
     p = xzalloc(sizeof *p);
     p->pid = pid;
@@ -190,6 +189,7 @@ process_start(char **argv,
               struct process **pp)
 {
     sigset_t oldsigs;
+    int nullfd;
     pid_t pid;
     int error;
 
@@ -200,18 +200,24 @@ process_start(char **argv,
         return error;
     }
 
+    if (n_null_fds) {
+        nullfd = get_null_fd();
+        if (nullfd < 0) {
+            return -nullfd;
+        }
+    } else {
+        nullfd = -1;
+    }
+
     block_sigchld(&oldsigs);
-    fatal_signal_block();
     pid = fork();
     if (pid < 0) {
-        fatal_signal_unblock();
         unblock_sigchld(&oldsigs);
         VLOG_WARN("fork failed: %s", strerror(errno));
         return errno;
     } else if (pid) {
         /* Running in parent process. */
         *pp = process_register(argv[0], pid);
-        fatal_signal_unblock();
         unblock_sigchld(&oldsigs);
         return 0;
     } else {
@@ -220,19 +226,20 @@ process_start(char **argv,
         int fd;
 
         fatal_signal_fork();
-        fatal_signal_unblock();
         unblock_sigchld(&oldsigs);
         for (fd = 0; fd < fd_max; fd++) {
             if (is_member(fd, null_fds, n_null_fds)) {
-                /* We can't use get_null_fd() here because we might have
-                 * already closed its fd. */
-                int nullfd = open("/dev/null", O_RDWR);
                 dup2(nullfd, fd);
-                close(nullfd);
-            } else if (fd >= 3 && !is_member(fd, keep_fds, n_keep_fds)) {
+            } else if (fd >= 3 && fd != nullfd
+                       && !is_member(fd, keep_fds, n_keep_fds)) {
                 close(fd);
             }
         }
+        if (nullfd >= 0
+            && !is_member(nullfd, keep_fds, n_keep_fds)
+            && !is_member(nullfd, null_fds, n_null_fds)) {
+            close(nullfd);
+        }
         execvp(argv[0], argv);
         fprintf(stderr, "execvp(\"%s\") failed: %s\n",
                 argv[0], strerror(errno));
@@ -289,7 +296,7 @@ process_exited(struct process *p)
         return true;
     } else {
         char buf[_POSIX_PIPE_BUF];
-        read(fds[0], buf, sizeof buf);
+        ignore(read(fds[0], buf, sizeof buf));
         return false;
     }
 }
@@ -300,7 +307,7 @@ process_exited(struct process *p)
 int
 process_status(const struct process *p)
 {
-    assert(p->exited);
+    ovs_assert(p->exited);
     return p->status;
 }
 
@@ -340,17 +347,10 @@ process_status_msg(int status)
     struct ds ds = DS_EMPTY_INITIALIZER;
     if (WIFEXITED(status)) {
         ds_put_format(&ds, "exit status %d", WEXITSTATUS(status));
-    } else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
-        int signr = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
-        const char *name = NULL;
-#ifdef HAVE_STRSIGNAL
-        name = strsignal(signr);
-#endif
-        ds_put_format(&ds, "%s by signal %d",
-                      WIFSIGNALED(status) ? "killed" : "stopped", signr);
-        if (name) {
-            ds_put_format(&ds, " (%s)", name);
-        }
+    } else if (WIFSIGNALED(status)) {
+        ds_put_format(&ds, "killed (%s)", signal_name(WTERMSIG(status)));
+    } else if (WIFSTOPPED(status)) {
+        ds_put_format(&ds, "stopped (%s)", signal_name(WSTOPSIG(status)));
     } else {
         ds_put_format(&ds, "terminated abnormally (%x)", status);
     }
@@ -400,13 +400,15 @@ process_search_path(const char *name)
 /* process_run_capture() and supporting functions. */
 
 struct stream {
+    size_t max_size;
     struct ds log;
     int fds[2];
 };
 
 static int
-stream_open(struct stream *s)
+stream_open(struct stream *s, size_t max_size)
 {
+    s->max_size = max_size;
     ds_init(&s->log);
     if (pipe(s->fds)) {
         VLOG_WARN("failed to create pipe: %s", strerror(errno));
@@ -419,15 +421,13 @@ stream_open(struct stream *s)
 static void
 stream_read(struct stream *s)
 {
-    int error = 0;
-
     if (s->fds[0] < 0) {
         return;
     }
 
-    error = 0;
     for (;;) {
         char buffer[512];
+        int error;
         size_t n;
 
         error = read_fully(s->fds[0], buffer, sizeof buffer, &n);
@@ -442,9 +442,9 @@ stream_read(struct stream *s)
                 }
                 break;
             }
-        } else if (s->log.length > PROCESS_MAX_CAPTURE) {
-            VLOG_WARN("subprocess output overflowed %d-byte buffer",
-                      PROCESS_MAX_CAPTURE);
+        } else if (s->log.length > s->max_size) {
+            VLOG_WARN("subprocess output overflowed %zu-byte buffer",
+                      s->max_size);
             break;
         }
     }
@@ -479,7 +479,7 @@ stream_close(struct stream *s)
  * '*status'.
  *
  * If 'stdout_log' is nonnull, then the subprocess's output to stdout (up to a
- * limit of PROCESS_MAX_CAPTURE bytes) is captured in a memory buffer, which
+ * limit of 'log_max' bytes) is captured in a memory buffer, which
  * when this function returns 0 is stored as a null-terminated string in
  * '*stdout_log'.  The caller is responsible for freeing '*stdout_log' (by
  * passing it to free()).  When this function returns an error, '*stdout_log'
@@ -489,7 +489,7 @@ stream_close(struct stream *s)
  * that it captures the subprocess's output to stderr. */
 int
 process_run_capture(char **argv, char **stdout_log, char **stderr_log,
-                    int *status)
+                    size_t max_log, int *status)
 {
     struct stream s_stdout, s_stderr;
     sigset_t oldsigs;
@@ -509,24 +509,22 @@ process_run_capture(char **argv, char **stdout_log, char **stderr_log,
         return error;
     }
 
-    error = stream_open(&s_stdout);
+    error = stream_open(&s_stdout, max_log);
     if (error) {
         return error;
     }
 
-    error = stream_open(&s_stderr);
+    error = stream_open(&s_stderr, max_log);
     if (error) {
         stream_close(&s_stdout);
         return error;
     }
 
     block_sigchld(&oldsigs);
-    fatal_signal_block();
     pid = fork();
     if (pid < 0) {
-        int error = errno;
+        error = errno;
 
-        fatal_signal_unblock();
         unblock_sigchld(&oldsigs);
         VLOG_WARN("fork failed: %s", strerror(error));
 
@@ -539,7 +537,6 @@ process_run_capture(char **argv, char **stdout_log, char **stderr_log,
         struct process *p;
 
         p = process_register(argv[0], pid);
-        fatal_signal_unblock();
         unblock_sigchld(&oldsigs);
 
         close(s_stdout.fds[1]);
@@ -575,7 +572,6 @@ process_run_capture(char **argv, char **stdout_log, char **stderr_log,
         int i;
 
         fatal_signal_fork();
-        fatal_signal_unblock();
         unblock_sigchld(&oldsigs);
 
         dup2(get_null_fd(), 0);
@@ -595,12 +591,12 @@ process_run_capture(char **argv, char **stdout_log, char **stderr_log,
 }
 \f
 static void
-sigchld_handler(int signr UNUSED)
+sigchld_handler(int signr OVS_UNUSED)
 {
     struct process *p;
 
     COVERAGE_INC(process_sigchld);
-    LIST_FOR_EACH (p, struct process, node, &all_processes) {
+    LIST_FOR_EACH (p, node, &all_processes) {
         if (!p->exited) {
             int retval, status;
             do {
@@ -617,7 +613,7 @@ sigchld_handler(int signr UNUSED)
             }
         }
     }
-    write(fds[1], "", 1);
+    ignore(write(fds[1], "", 1));
 }
 
 static bool
@@ -637,9 +633,8 @@ static bool
 sigchld_is_blocked(void)
 {
     sigset_t sigs;
-    if (sigprocmask(SIG_SETMASK, NULL, &sigs)) {
-        ovs_fatal(errno, "sigprocmask");
-    }
+
+    xsigprocmask(SIG_SETMASK, NULL, &sigs);
     return sigismember(&sigs, SIGCHLD);
 }
 
@@ -647,17 +642,14 @@ static void
 block_sigchld(sigset_t *oldsigs)
 {
     sigset_t sigchld;
+
     sigemptyset(&sigchld);
     sigaddset(&sigchld, SIGCHLD);
-    if (sigprocmask(SIG_BLOCK, &sigchld, oldsigs)) {
-        ovs_fatal(errno, "sigprocmask");
-    }
+    xsigprocmask(SIG_BLOCK, &sigchld, oldsigs);
 }
 
 static void
 unblock_sigchld(const sigset_t *oldsigs)
 {
-    if (sigprocmask(SIG_SETMASK, oldsigs, NULL)) {
-        ovs_fatal(errno, "sigprocmask");
-    }
+    xsigprocmask(SIG_SETMASK, oldsigs, NULL);
 }