/*
- * Copyright (c) 2008, 2009 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 <stdlib.h>
#include <string.h>
+#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "coverage.h"
-#include "dynamic-string.h"
+#include "openvswitch/dynamic-string.h"
#include "fatal-signal.h"
-#include "list.h"
-#include "poll-loop.h"
+#include "openvswitch/list.h"
+#include "ovs-thread.h"
+#include "openvswitch/poll-loop.h"
+#include "signals.h"
#include "socket-util.h"
+#include "timeval.h"
#include "util.h"
+#include "openvswitch/vlog.h"
-#define THIS_MODULE VLM_process
-#include "vlog.h"
+VLOG_DEFINE_THIS_MODULE(process);
+
+COVERAGE_DEFINE(process_start);
+
+#ifdef __linux__
+#define LINUX 1
+#include <asm/param.h>
+#else
+#define LINUX 0
+#endif
struct process {
- struct list node;
+ struct ovs_list node;
char *name;
pid_t pid;
- /* Modified by signal handler. */
- volatile bool exited;
- volatile int status;
+ /* State. */
+ bool exited;
+ int status;
+};
+
+struct raw_process_info {
+ unsigned long int vsz; /* Virtual size, in kB. */
+ unsigned long int rss; /* Resident set size, in kB. */
+ long long int uptime; /* ms since started. */
+ long long int cputime; /* ms of CPU used during 'uptime'. */
+ pid_t ppid; /* Parent. */
+ int core_id; /* Core id last executed on. */
+ char name[18]; /* Name. */
};
/* Pipe used to signal child termination. */
static int fds[2];
/* All processes. */
-static struct list all_processes = LIST_INITIALIZER(&all_processes);
+static struct ovs_list all_processes = OVS_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 bool is_member(int x, const int *array, size_t);
+static void sigchld_handler(int signr OVS_UNUSED);
/* Initializes the process subsystem (if it is not already initialized). Calls
* exit() if initialization fails.
*
+ * This function may not be called after creating any additional threads.
+ *
* Calling this function is optional; it will be called automatically by
* process_start() if necessary. Calling it explicitly allows the client to
* prevent the process from exiting at an unexpected time. */
void
process_init(void)
{
+#ifndef _WIN32
static bool inited;
struct sigaction sa;
+ assert_single_threaded();
if (inited) {
return;
}
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);
+#endif
}
char *
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 == '\"') {
}
/* Creates and returns a new struct process with the specified 'name' and
- * 'pid'.
- *
- * This is racy unless SIGCHLD is blocked (and has been blocked since before
- * the fork()) that created the subprocess. */
+ * 'pid'. */
static struct process *
process_register(const char *name, pid_t pid)
{
struct process *p;
const char *slash;
- assert(sigchld_is_blocked());
-
p = xzalloc(sizeof *p);
p->pid = pid;
slash = strrchr(name, '/');
p->name = xstrdup(slash ? slash + 1 : name);
p->exited = false;
- list_push_back(&all_processes, &p->node);
+ ovs_list_push_back(&all_processes, &p->node);
return p;
}
+#ifndef _WIN32
+static bool
+rlim_is_finite(rlim_t limit)
+{
+ if (limit == RLIM_INFINITY) {
+ return false;
+ }
+
+#ifdef RLIM_SAVED_CUR /* FreeBSD 8.0 lacks RLIM_SAVED_CUR. */
+ if (limit == RLIM_SAVED_CUR) {
+ return false;
+ }
+#endif
+
+#ifdef RLIM_SAVED_MAX /* FreeBSD 8.0 lacks RLIM_SAVED_MAX. */
+ if (limit == RLIM_SAVED_MAX) {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+/* Returns the maximum valid FD value, plus 1. */
+static int
+get_max_fds(void)
+{
+ static int max_fds;
+
+ if (!max_fds) {
+ struct rlimit r;
+ if (!getrlimit(RLIMIT_NOFILE, &r) && rlim_is_finite(r.rlim_cur)) {
+ max_fds = r.rlim_cur;
+ } else {
+ VLOG_WARN("failed to obtain fd limit, defaulting to 1024");
+ max_fds = 1024;
+ }
+ }
+
+ return max_fds;
+}
+#endif /* _WIN32 */
+
/* Starts a subprocess with the arguments in the null-terminated argv[] array.
* argv[0] is used as the name of the process. Searches the PATH environment
* variable to find the program to execute.
*
+ * This function may not be called after creating any additional threads.
+ *
* All file descriptors are closed before executing the subprocess, except for
- * fds 0, 1, and 2 and the 'n_keep_fds' fds listed in 'keep_fds'. Also, any of
- * the 'n_null_fds' fds listed in 'null_fds' are replaced by /dev/null.
+ * fds 0, 1, and 2.
*
* Returns 0 if successful, otherwise a positive errno value indicating the
* error. If successful, '*pp' is assigned a new struct process that may be
* used to query the process's status. On failure, '*pp' is set to NULL. */
int
-process_start(char **argv,
- const int keep_fds[], size_t n_keep_fds,
- const int null_fds[], size_t n_null_fds,
- struct process **pp)
+process_start(char **argv, struct process **pp)
{
- sigset_t oldsigs;
+#ifndef _WIN32
pid_t pid;
int error;
+ sigset_t prev_mask;
+
+ assert_single_threaded();
*pp = NULL;
COVERAGE_INC(process_start);
return error;
}
- block_sigchld(&oldsigs);
+ fatal_signal_block(&prev_mask);
pid = fork();
if (pid < 0) {
- unblock_sigchld(&oldsigs);
- VLOG_WARN("fork failed: %s", strerror(errno));
- return errno;
+ VLOG_WARN("fork failed: %s", ovs_strerror(errno));
+ error = errno;
} else if (pid) {
/* Running in parent process. */
*pp = process_register(argv[0], pid);
- unblock_sigchld(&oldsigs);
- return 0;
+ error = 0;
} else {
/* Running in child process. */
int fd_max = get_max_fds();
int fd;
fatal_signal_fork();
- 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)) {
- close(fd);
- }
+ for (fd = 3; fd < fd_max; fd++) {
+ close(fd);
}
+ xpthread_sigmask(SIG_SETMASK, &prev_mask, NULL);
execvp(argv[0], argv);
fprintf(stderr, "execvp(\"%s\") failed: %s\n",
- argv[0], strerror(errno));
+ argv[0], ovs_strerror(errno));
_exit(1);
}
+ xpthread_sigmask(SIG_SETMASK, &prev_mask, NULL);
+ return error;
+#else
+ *pp = NULL;
+ return ENOSYS;
+#endif
}
/* Destroys process 'p'. */
process_destroy(struct process *p)
{
if (p) {
- sigset_t oldsigs;
-
- block_sigchld(&oldsigs);
- list_remove(&p->node);
- unblock_sigchld(&oldsigs);
-
+ ovs_list_remove(&p->node);
free(p->name);
free(p);
}
int
process_kill(const struct process *p, int signr)
{
+#ifndef _WIN32
return (p->exited ? ESRCH
: !kill(p->pid, signr) ? 0
: errno);
+#else
+ return ENOSYS;
+#endif
}
/* Returns the pid of process 'p'. */
bool
process_exited(struct process *p)
{
- if (p->exited) {
- return true;
- } else {
- char buf[_POSIX_PIPE_BUF];
- ignore(read(fds[0], buf, sizeof buf));
- return false;
- }
+ return p->exited;
}
/* Returns process 'p''s exit status, as reported by waitpid(2).
int
process_status(const struct process *p)
{
- assert(p->exited);
+ ovs_assert(p->exited);
return p->status;
}
int
-process_run(char **argv,
- const int keep_fds[], size_t n_keep_fds,
- const int null_fds[], size_t n_null_fds,
- int *status)
+count_crashes(pid_t pid)
{
- struct process *p;
- int retval;
-
- COVERAGE_INC(process_run);
- retval = process_start(argv, keep_fds, n_keep_fds, null_fds, n_null_fds,
- &p);
- if (retval) {
- *status = 0;
- return retval;
+ char file_name[128];
+ const char *paren;
+ char line[128];
+ int crashes = 0;
+ FILE *stream;
+
+ ovs_assert(LINUX);
+
+ sprintf(file_name, "/proc/%lu/cmdline", (unsigned long int) pid);
+ stream = fopen(file_name, "r");
+ if (!stream) {
+ VLOG_WARN_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno));
+ goto exit;
}
- while (!process_exited(p)) {
- process_wait(p);
- poll_block();
+ if (!fgets(line, sizeof line, stream)) {
+ VLOG_WARN_ONCE("%s: read failed (%s)", file_name,
+ feof(stream) ? "end of file" : ovs_strerror(errno));
+ goto exit_close;
}
- *status = process_status(p);
- process_destroy(p);
- return 0;
+
+ paren = strchr(line, '(');
+ if (paren) {
+ int x;
+ if (ovs_scan(paren + 1, "%d", &x)) {
+ crashes = x;
+ }
+ }
+
+exit_close:
+ fclose(stream);
+exit:
+ return crashes;
+}
+
+static unsigned long long int
+ticks_to_ms(unsigned long long int ticks)
+{
+ ovs_assert(LINUX);
+
+#ifndef USER_HZ
+#define USER_HZ 100
+#endif
+
+#if USER_HZ == 100 /* Common case. */
+ return ticks * (1000 / USER_HZ);
+#else /* Alpha and some other architectures. */
+ double factor = 1000.0 / USER_HZ;
+ return ticks * factor + 0.5;
+#endif
+}
+
+static bool
+get_raw_process_info(pid_t pid, struct raw_process_info *raw)
+{
+ unsigned long long int vsize, rss, start_time, utime, stime;
+ long long int start_msec;
+ unsigned long ppid;
+ char file_name[128];
+ FILE *stream;
+ int n;
+
+ ovs_assert(LINUX);
+
+ sprintf(file_name, "/proc/%lu/stat", (unsigned long int) pid);
+ stream = fopen(file_name, "r");
+ if (!stream) {
+ VLOG_ERR_ONCE("%s: open failed (%s)",
+ file_name, ovs_strerror(errno));
+ return false;
+ }
+
+ n = fscanf(stream,
+ "%*d " /* (1. pid) */
+ "(%17[^)]) " /* 2. process name */
+ "%*c " /* (3. state) */
+ "%lu " /* 4. ppid */
+ "%*d " /* (5. pgid) */
+ "%*d " /* (6. sid) */
+ "%*d " /* (7. tty_nr) */
+ "%*d " /* (8. tty_pgrp) */
+ "%*u " /* (9. flags) */
+ "%*u " /* (10. min_flt) */
+ "%*u " /* (11. cmin_flt) */
+ "%*u " /* (12. maj_flt) */
+ "%*u " /* (13. cmaj_flt) */
+ "%llu " /* 14. utime */
+ "%llu " /* 15. stime */
+ "%*d " /* (16. cutime) */
+ "%*d " /* (17. cstime) */
+ "%*d " /* (18. priority) */
+ "%*d " /* (19. nice) */
+ "%*d " /* (20. num_threads) */
+ "%*d " /* (21. always 0) */
+ "%llu " /* 22. start_time */
+ "%llu " /* 23. vsize */
+ "%llu " /* 24. rss */
+ "%*u " /* (25. rsslim) */
+ "%*u " /* (26. start_code) */
+ "%*u " /* (27. end_code) */
+ "%*u " /* (28. start_stack) */
+ "%*u " /* (29. esp) */
+ "%*u " /* (30. eip) */
+ "%*u " /* (31. pending signals) */
+ "%*u " /* (32. blocked signals) */
+ "%*u " /* (33. ignored signals) */
+ "%*u " /* (34. caught signals) */
+ "%*u " /* (35. whcan) */
+ "%*u " /* (36. always 0) */
+ "%*u " /* (37. always 0) */
+ "%*d " /* (38. exit_signal) */
+ "%d " /* 39. task_cpu */
+#if 0
+ /* These are here for documentation but #if'd out to save
+ * actually parsing them from the stream for no benefit. */
+ "%*u " /* (40. rt_priority) */
+ "%*u " /* (41. policy) */
+ "%*llu " /* (42. blkio_ticks) */
+ "%*lu " /* (43. gtime) */
+ "%*ld" /* (44. cgtime) */
+#endif
+ , raw->name, &ppid, &utime, &stime, &start_time,
+ &vsize, &rss, &raw->core_id);
+ fclose(stream);
+ if (n != 8) {
+ VLOG_ERR_ONCE("%s: fscanf failed", file_name);
+ return false;
+ }
+
+ start_msec = get_boot_time() + ticks_to_ms(start_time);
+
+ raw->vsz = vsize / 1024;
+ raw->rss = rss * (get_page_size() / 1024);
+ raw->uptime = time_wall_msec() - start_msec;
+ raw->cputime = ticks_to_ms(utime + stime);
+ raw->ppid = ppid;
+
+ return true;
+}
+
+bool
+get_process_info(pid_t pid, struct process_info *pinfo)
+{
+ struct raw_process_info child;
+
+ ovs_assert(LINUX);
+ if (!get_raw_process_info(pid, &child)) {
+ return false;
+ }
+
+ ovs_strlcpy(pinfo->name, child.name, sizeof pinfo->name);
+ pinfo->vsz = child.vsz;
+ pinfo->rss = child.rss;
+ pinfo->booted = child.uptime;
+ pinfo->crashes = 0;
+ pinfo->uptime = child.uptime;
+ pinfo->cputime = child.cputime;
+ pinfo->core_id = child.core_id;
+
+ if (child.ppid) {
+ struct raw_process_info parent;
+
+ get_raw_process_info(child.ppid, &parent);
+ if (!strcmp(child.name, parent.name)) {
+ pinfo->booted = parent.uptime;
+ pinfo->crashes = count_crashes(child.ppid);
+ }
+ }
+
+ return true;
}
/* Given 'status', which is a process status in the form reported by waitpid(2)
process_status_msg(int status)
{
struct ds ds = DS_EMPTY_INITIALIZER;
+#ifndef _WIN32
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)) {
+ char namebuf[SIGNAL_NAME_BUFSIZE];
+
+ ds_put_format(&ds, "killed (%s)",
+ signal_name(WTERMSIG(status), namebuf, sizeof namebuf));
+ } else if (WIFSTOPPED(status)) {
+ char namebuf[SIGNAL_NAME_BUFSIZE];
+
+ ds_put_format(&ds, "stopped (%s)",
+ signal_name(WSTOPSIG(status), namebuf, sizeof namebuf));
} else {
ds_put_format(&ds, "terminated abnormally (%x)", status);
}
if (WCOREDUMP(status)) {
ds_put_cstr(&ds, ", core dumped");
}
+#else
+ ds_put_cstr(&ds, "function not supported.");
+#endif
return ds_cstr(&ds);
}
+/* Executes periodic maintenance activities required by the process module. */
+void
+process_run(void)
+{
+#ifndef _WIN32
+ char buf[_POSIX_PIPE_BUF];
+
+ if (!ovs_list_is_empty(&all_processes) && read(fds[0], buf, sizeof buf) > 0) {
+ struct process *p;
+
+ LIST_FOR_EACH (p, node, &all_processes) {
+ if (!p->exited) {
+ int retval, status;
+ do {
+ retval = waitpid(p->pid, &status, WNOHANG);
+ } while (retval == -1 && errno == EINTR);
+ if (retval == p->pid) {
+ p->exited = true;
+ p->status = status;
+ } else if (retval < 0) {
+ VLOG_WARN("waitpid: %s", ovs_strerror(errno));
+ p->exited = true;
+ p->status = -1;
+ }
+ }
+ }
+ }
+#endif
+}
+
/* Causes the next call to poll_block() to wake up when process 'p' has
* exited. */
void
process_wait(struct process *p)
{
+#ifndef _WIN32
if (p->exited) {
poll_immediate_wake();
} else {
poll_fd_wait(fds[0], POLLIN);
}
+#else
+ OVS_NOT_REACHED();
+#endif
}
char *
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);
- pid = fork();
- if (pid < 0) {
- int error = errno;
-
- 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);
- 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();
- 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)
+sigchld_handler(int signr OVS_UNUSED)
{
- struct process *p;
-
- COVERAGE_INC(process_sigchld);
- LIST_FOR_EACH (p, struct process, node, &all_processes) {
- if (!p->exited) {
- int retval, status;
- do {
- retval = waitpid(p->pid, &status, WNOHANG);
- } while (retval == -1 && errno == EINTR);
- if (retval == p->pid) {
- p->exited = true;
- p->status = status;
- } else if (retval < 0) {
- /* XXX We want to log something but we're in a signal
- * handler. */
- p->exited = true;
- p->status = -1;
- }
- }
- }
ignore(write(fds[1], "", 1));
}
-
-static bool
-is_member(int x, const int *array, size_t n)
-{
- size_t i;
-
- for (i = 0; i < n; i++) {
- if (array[i] == x) {
- return true;
- }
- }
- return false;
-}
-
-static bool
-sigchld_is_blocked(void)
-{
- sigset_t sigs;
- if (sigprocmask(SIG_SETMASK, NULL, &sigs)) {
- ovs_fatal(errno, "sigprocmask");
- }
- return sigismember(&sigs, SIGCHLD);
-}
-
-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");
- }
-}
-
-static void
-unblock_sigchld(const sigset_t *oldsigs)
-{
- if (sigprocmask(SIG_SETMASK, oldsigs, NULL)) {
- ovs_fatal(errno, "sigprocmask");
- }
-}