/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#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"
#include "vlog.h"
-VLOG_DEFINE_THIS_MODULE(process)
+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;
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 *
struct process *p;
const char *slash;
- assert(sigchld_is_blocked());
+ ovs_assert(sigchld_is_blocked());
p = xzalloc(sizeof *p);
p->pid = pid;
struct process **pp)
{
sigset_t oldsigs;
+ int nullfd;
pid_t pid;
int error;
return error;
}
+ if (n_null_fds) {
+ nullfd = get_null_fd();
+ if (nullfd < 0) {
+ return -nullfd;
+ }
+ } else {
+ nullfd = -1;
+ }
+
block_sigchld(&oldsigs);
pid = fork();
if (pid < 0) {
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));
int
process_status(const struct process *p)
{
- assert(p->exited);
+ ovs_assert(p->exited);
return p->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);
}
/* 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)
{
+ int error;
+
+ s->max_size = max_size;
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;
+ error = set_nonblocking(s->fds[0]);
+ if (error) {
+ close(s->fds[0]);
+ close(s->fds[1]);
+ }
+ return error;
}
static void
}
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;
}
}
* '*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'
* 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;
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;
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 {
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);
}
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);
}