return NULL;
}
\f
+/* process_run_capture() and supporting functions. */
+
+struct stream {
+ struct ds log;
+ int fds[2];
+};
+
+static int
+stream_open(struct stream *s)
+{
+ ds_init(&s->log);
+ if (pipe(s->fds)) {
+ VLOG_WARN("failed to create pipe: %s", strerror(errno));
+ return errno;
+ }
+ set_nonblocking(s->fds[0]);
+ return 0;
+}
+
+static void
+stream_read(struct stream *s)
+{
+ int error = 0;
+
+ if (s->fds[0] < 0) {
+ return;
+ }
+
+ error = 0;
+ for (;;) {
+ char buffer[512];
+ size_t n;
+
+ error = read_fully(s->fds[0], buffer, sizeof buffer, &n);
+ ds_put_buffer(&s->log, buffer, n);
+ if (error) {
+ if (error == EAGAIN || error == EWOULDBLOCK) {
+ return;
+ } else {
+ if (error != EOF) {
+ VLOG_WARN("error reading subprocess pipe: %s",
+ strerror(error));
+ }
+ break;
+ }
+ } else if (s->log.length > PROCESS_MAX_CAPTURE) {
+ VLOG_WARN("subprocess output overflowed %d-byte buffer",
+ PROCESS_MAX_CAPTURE);
+ break;
+ }
+ }
+ close(s->fds[0]);
+ s->fds[0] = -1;
+}
+
+static void
+stream_wait(struct stream *s)
+{
+ if (s->fds[0] >= 0) {
+ poll_fd_wait(s->fds[0], POLLIN);
+ }
+}
+
+static void
+stream_close(struct stream *s)
+{
+ ds_destroy(&s->log);
+ if (s->fds[0] >= 0) {
+ close(s->fds[0]);
+ }
+ if (s->fds[1] >= 0) {
+ close(s->fds[1]);
+ }
+}
+
+/* Starts the process whose arguments are given in the null-terminated array
+ * 'argv' and waits for it to exit. On success returns 0 and stores the
+ * process exit value (suitable for passing to process_status_msg()) in
+ * '*status'. On failure, returns a positive errno value and stores 0 in
+ * '*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
+ * 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'
+ * is set to NULL.
+ *
+ * If 'stderr_log' is nonnull, then it is treated like 'stdout_log' except
+ * that it captures the subprocess's output to stderr. */
+int
+process_run_capture(char **argv, char **stdout_log, char **stderr_log,
+ int *status)
+{
+ struct stream s_stdout, s_stderr;
+ sigset_t oldsigs;
+ pid_t pid;
+ int error;
+
+ COVERAGE_INC(process_run_capture);
+ if (stdout_log) {
+ *stdout_log = NULL;
+ }
+ if (stderr_log) {
+ *stderr_log = NULL;
+ }
+ *status = 0;
+ error = process_prestart(argv);
+ if (error) {
+ return error;
+ }
+
+ error = stream_open(&s_stdout);
+ if (error) {
+ return error;
+ }
+
+ error = stream_open(&s_stderr);
+ if (error) {
+ stream_close(&s_stdout);
+ return error;
+ }
+
+ block_sigchld(&oldsigs);
+ fatal_signal_block();
+ pid = fork();
+ if (pid < 0) {
+ int error = errno;
+
+ fatal_signal_unblock();
+ unblock_sigchld(&oldsigs);
+ VLOG_WARN("fork failed: %s", strerror(error));
+
+ stream_close(&s_stdout);
+ stream_close(&s_stderr);
+ *status = 0;
+ return error;
+ } else if (pid) {
+ /* Running in parent process. */
+ struct process *p;
+
+ p = process_register(argv[0], pid);
+ fatal_signal_unblock();
+ unblock_sigchld(&oldsigs);
+
+ close(s_stdout.fds[1]);
+ close(s_stderr.fds[1]);
+ while (!process_exited(p)) {
+ stream_read(&s_stdout);
+ stream_read(&s_stderr);
+
+ stream_wait(&s_stdout);
+ stream_wait(&s_stderr);
+ process_wait(p);
+ poll_block();
+ }
+ stream_read(&s_stdout);
+ stream_read(&s_stderr);
+
+ if (stdout_log) {
+ *stdout_log = ds_steal_cstr(&s_stdout.log);
+ }
+ if (stderr_log) {
+ *stderr_log = ds_steal_cstr(&s_stderr.log);
+ }
+
+ stream_close(&s_stdout);
+ stream_close(&s_stderr);
+
+ *status = process_status(p);
+ process_destroy(p);
+ return 0;
+ } else {
+ /* Running in child process. */
+ int max_fds;
+ int i;
+
+ fatal_signal_fork();
+ fatal_signal_unblock();
+ unblock_sigchld(&oldsigs);
+
+ dup2(get_null_fd(), 0);
+ dup2(s_stdout.fds[1], 1);
+ dup2(s_stderr.fds[1], 2);
+
+ max_fds = get_max_fds();
+ for (i = 3; i < max_fds; i++) {
+ close(i);
+ }
+
+ execvp(argv[0], argv);
+ fprintf(stderr, "execvp(\"%s\") failed: %s\n",
+ argv[0], strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+\f
static void
sigchld_handler(int signr UNUSED)
{