2009-07-08 13:19:16 -07:00
|
|
|
|
/*
|
2013-01-24 13:19:52 -08:00
|
|
|
|
* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at:
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include "process.h"
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include "coverage.h"
|
|
|
|
|
#include "dynamic-string.h"
|
2009-07-15 12:05:04 -07:00
|
|
|
|
#include "fatal-signal.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "list.h"
|
|
|
|
|
#include "poll-loop.h"
|
2011-04-01 10:22:51 -07:00
|
|
|
|
#include "signals.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "socket-util.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "vlog.h"
|
|
|
|
|
|
2010-10-19 14:47:01 -07:00
|
|
|
|
VLOG_DEFINE_THIS_MODULE(process);
|
2010-07-16 11:02:49 -07:00
|
|
|
|
|
coverage: Make the coverage counters catalog program-specific.
Until now, the collection of coverage counters supported by a given OVS
program was not specific to that program. That means that, for example,
even though ovs-dpctl does not have anything to do with mac_learning, it
still has a coverage counter for it. This is confusing, at best.
This commit fixes the problem on some systems, in particular on ones that
use GCC and the GNU linker. It uses the feature of the GNU linker
described in its manual as:
If an orphaned section's name is representable as a C identifier then
the linker will automatically see PROVIDE two symbols: __start_SECNAME
and __end_SECNAME, where SECNAME is the name of the section. These
indicate the start address and end address of the orphaned section
respectively.
Systems that don't support these features retain the earlier behavior.
This commit also fixes the annoyance that files that include coverage
counters must be listed on COVERAGE_FILES in lib/automake.mk.
This commit also fixes the annoyance that modifying any source file that
includes a coverage counter caused all programs that link against
libopenvswitch.a to relink, even programs that the source file was not
linked into. For example, modifying ofproto/ofproto.c (which includes
coverage counters) caused tests/test-aes128 to relink, even though
test-aes128 does not link again ofproto.o.
2010-11-01 14:14:27 -07:00
|
|
|
|
COVERAGE_DEFINE(process_sigchld);
|
|
|
|
|
COVERAGE_DEFINE(process_start);
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
struct process {
|
|
|
|
|
struct list node;
|
|
|
|
|
char *name;
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
2013-06-06 16:26:34 -07:00
|
|
|
|
/* State. */
|
|
|
|
|
bool exited;
|
|
|
|
|
int status;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Pipe used to signal child termination. */
|
|
|
|
|
static int fds[2];
|
|
|
|
|
|
|
|
|
|
/* All processes. */
|
|
|
|
|
static struct list all_processes = LIST_INITIALIZER(&all_processes);
|
|
|
|
|
|
2010-02-11 10:59:47 -08:00
|
|
|
|
static void sigchld_handler(int signr OVS_UNUSED);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
/* Initializes the process subsystem (if it is not already initialized). Calls
|
|
|
|
|
* exit() if initialization fails.
|
|
|
|
|
*
|
2013-05-08 15:02:45 -07:00
|
|
|
|
* This function may not be called after creating any additional threads.
|
|
|
|
|
*
|
2009-07-08 13:19:16 -07:00
|
|
|
|
* 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)
|
|
|
|
|
{
|
|
|
|
|
static bool inited;
|
|
|
|
|
struct sigaction sa;
|
|
|
|
|
|
|
|
|
|
if (inited) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
inited = true;
|
|
|
|
|
|
|
|
|
|
/* Create notification pipe. */
|
2012-09-28 21:06:41 +00:00
|
|
|
|
xpipe_nonblocking(fds);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
/* 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;
|
2011-03-31 16:23:50 -07:00
|
|
|
|
xsigaction(SIGCHLD, &sa, NULL);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
process_escape_args(char **argv)
|
|
|
|
|
{
|
|
|
|
|
struct ds ds = DS_EMPTY_INITIALIZER;
|
|
|
|
|
char **argp;
|
|
|
|
|
for (argp = argv; *argp; argp++) {
|
|
|
|
|
const char *arg = *argp;
|
|
|
|
|
const char *p;
|
|
|
|
|
if (argp != argv) {
|
|
|
|
|
ds_put_char(&ds, ' ');
|
|
|
|
|
}
|
2010-03-01 17:52:12 -08:00
|
|
|
|
if (arg[strcspn(arg, " \t\r\n\v\\\'\"")]) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_char(&ds, '"');
|
|
|
|
|
for (p = arg; *p; p++) {
|
|
|
|
|
if (*p == '\\' || *p == '\"') {
|
|
|
|
|
ds_put_char(&ds, '\\');
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(&ds, *p);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(&ds, '"');
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_cstr(&ds, arg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ds_cstr(&ds);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-15 12:07:02 -07:00
|
|
|
|
/* Prepare to start a process whose command-line arguments are given by the
|
|
|
|
|
* null-terminated 'argv' array. Returns 0 if successful, otherwise a
|
|
|
|
|
* positive errno value. */
|
|
|
|
|
static int
|
|
|
|
|
process_prestart(char **argv)
|
|
|
|
|
{
|
|
|
|
|
char *binary;
|
|
|
|
|
|
|
|
|
|
process_init();
|
|
|
|
|
|
|
|
|
|
/* Log the process to be started. */
|
|
|
|
|
if (VLOG_IS_DBG_ENABLED()) {
|
|
|
|
|
char *args = process_escape_args(argv);
|
|
|
|
|
VLOG_DBG("starting subprocess: %s", args);
|
|
|
|
|
free(args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* execvp() will search PATH too, but the error in that case is more
|
|
|
|
|
* obscure, since it is only reported post-fork. */
|
|
|
|
|
binary = process_search_path(argv[0]);
|
|
|
|
|
if (!binary) {
|
|
|
|
|
VLOG_ERR("%s not found in PATH", argv[0]);
|
|
|
|
|
return ENOENT;
|
|
|
|
|
}
|
|
|
|
|
free(binary);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Creates and returns a new struct process with the specified 'name' and
|
2013-06-06 16:26:34 -07:00
|
|
|
|
* 'pid'. */
|
2009-07-15 12:07:02 -07:00
|
|
|
|
static struct process *
|
|
|
|
|
process_register(const char *name, pid_t pid)
|
|
|
|
|
{
|
|
|
|
|
struct process *p;
|
|
|
|
|
const char *slash;
|
|
|
|
|
|
2009-09-28 13:56:42 -07:00
|
|
|
|
p = xzalloc(sizeof *p);
|
2009-07-15 12:07:02 -07:00
|
|
|
|
p->pid = pid;
|
|
|
|
|
slash = strrchr(name, '/');
|
|
|
|
|
p->name = xstrdup(slash ? slash + 1 : name);
|
|
|
|
|
p->exited = false;
|
|
|
|
|
|
|
|
|
|
list_push_back(&all_processes, &p->node);
|
|
|
|
|
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
/* 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.
|
|
|
|
|
*
|
2013-05-08 15:02:45 -07:00
|
|
|
|
* This function may not be called after creating any additional threads.
|
|
|
|
|
*
|
2009-07-08 13:19:16 -07:00
|
|
|
|
* All file descriptors are closed before executing the subprocess, except for
|
2013-05-08 14:31:55 -07:00
|
|
|
|
* fds 0, 1, and 2.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
|
|
|
|
* 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
|
2013-05-08 14:31:55 -07:00
|
|
|
|
process_start(char **argv, struct process **pp)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
pid_t pid;
|
2009-07-15 12:07:02 -07:00
|
|
|
|
int error;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
*pp = NULL;
|
|
|
|
|
COVERAGE_INC(process_start);
|
2009-07-15 12:07:02 -07:00
|
|
|
|
error = process_prestart(argv);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
|
if (pid < 0) {
|
|
|
|
|
VLOG_WARN("fork failed: %s", strerror(errno));
|
|
|
|
|
return errno;
|
|
|
|
|
} else if (pid) {
|
|
|
|
|
/* Running in parent process. */
|
2009-07-15 12:07:02 -07:00
|
|
|
|
*pp = process_register(argv[0], pid);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
/* Running in child process. */
|
|
|
|
|
int fd_max = get_max_fds();
|
|
|
|
|
int fd;
|
|
|
|
|
|
2009-07-15 12:05:04 -07:00
|
|
|
|
fatal_signal_fork();
|
2013-05-08 14:31:55 -07:00
|
|
|
|
for (fd = 3; fd < fd_max; fd++) {
|
|
|
|
|
close(fd);
|
2011-02-23 11:16:07 -08:00
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
execvp(argv[0], argv);
|
|
|
|
|
fprintf(stderr, "execvp(\"%s\") failed: %s\n",
|
|
|
|
|
argv[0], strerror(errno));
|
|
|
|
|
_exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Destroys process 'p'. */
|
|
|
|
|
void
|
|
|
|
|
process_destroy(struct process *p)
|
|
|
|
|
{
|
|
|
|
|
if (p) {
|
|
|
|
|
list_remove(&p->node);
|
|
|
|
|
free(p->name);
|
|
|
|
|
free(p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Sends signal 'signr' to process 'p'. Returns 0 if successful, otherwise a
|
|
|
|
|
* positive errno value. */
|
|
|
|
|
int
|
|
|
|
|
process_kill(const struct process *p, int signr)
|
|
|
|
|
{
|
|
|
|
|
return (p->exited ? ESRCH
|
|
|
|
|
: !kill(p->pid, signr) ? 0
|
|
|
|
|
: errno);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns the pid of process 'p'. */
|
|
|
|
|
pid_t
|
|
|
|
|
process_pid(const struct process *p)
|
|
|
|
|
{
|
|
|
|
|
return p->pid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns the name of process 'p' (the name passed to process_start() with any
|
|
|
|
|
* leading directories stripped). */
|
|
|
|
|
const char *
|
|
|
|
|
process_name(const struct process *p)
|
|
|
|
|
{
|
|
|
|
|
return p->name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns true if process 'p' has exited, false otherwise. */
|
|
|
|
|
bool
|
|
|
|
|
process_exited(struct process *p)
|
|
|
|
|
{
|
2013-06-06 16:26:34 -07:00
|
|
|
|
return p->exited;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns process 'p''s exit status, as reported by waitpid(2).
|
|
|
|
|
* process_status(p) may be called only after process_exited(p) has returned
|
|
|
|
|
* true. */
|
|
|
|
|
int
|
|
|
|
|
process_status(const struct process *p)
|
|
|
|
|
{
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert(p->exited);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
return p->status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given 'status', which is a process status in the form reported by waitpid(2)
|
|
|
|
|
* and returned by process_status(), returns a string describing how the
|
|
|
|
|
* process terminated. The caller is responsible for freeing the string when
|
|
|
|
|
* it is no longer needed. */
|
|
|
|
|
char *
|
|
|
|
|
process_status_msg(int status)
|
|
|
|
|
{
|
|
|
|
|
struct ds ds = DS_EMPTY_INITIALIZER;
|
|
|
|
|
if (WIFEXITED(status)) {
|
|
|
|
|
ds_put_format(&ds, "exit status %d", WEXITSTATUS(status));
|
2011-04-01 10:22:51 -07:00
|
|
|
|
} else if (WIFSIGNALED(status)) {
|
2013-04-16 15:25:10 -07:00
|
|
|
|
char namebuf[SIGNAL_NAME_BUFSIZE];
|
|
|
|
|
|
|
|
|
|
ds_put_format(&ds, "killed (%s)",
|
|
|
|
|
signal_name(WTERMSIG(status), namebuf, sizeof namebuf));
|
2011-04-01 10:22:51 -07:00
|
|
|
|
} else if (WIFSTOPPED(status)) {
|
2013-04-16 15:25:10 -07:00
|
|
|
|
char namebuf[SIGNAL_NAME_BUFSIZE];
|
|
|
|
|
|
|
|
|
|
ds_put_format(&ds, "stopped (%s)",
|
|
|
|
|
signal_name(WSTOPSIG(status), namebuf, sizeof namebuf));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(&ds, "terminated abnormally (%x)", status);
|
|
|
|
|
}
|
|
|
|
|
if (WCOREDUMP(status)) {
|
|
|
|
|
ds_put_cstr(&ds, ", core dumped");
|
|
|
|
|
}
|
|
|
|
|
return ds_cstr(&ds);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-06 16:26:34 -07:00
|
|
|
|
/* Executes periodic maintenance activities required by the process module. */
|
|
|
|
|
void
|
|
|
|
|
process_run(void)
|
|
|
|
|
{
|
|
|
|
|
char buf[_POSIX_PIPE_BUF];
|
|
|
|
|
|
|
|
|
|
if (!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", strerror(errno));
|
|
|
|
|
p->exited = true;
|
|
|
|
|
p->status = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
/* Causes the next call to poll_block() to wake up when process 'p' has
|
|
|
|
|
* exited. */
|
|
|
|
|
void
|
|
|
|
|
process_wait(struct process *p)
|
|
|
|
|
{
|
|
|
|
|
if (p->exited) {
|
|
|
|
|
poll_immediate_wake();
|
|
|
|
|
} else {
|
|
|
|
|
poll_fd_wait(fds[0], POLLIN);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
process_search_path(const char *name)
|
|
|
|
|
{
|
|
|
|
|
char *save_ptr = NULL;
|
|
|
|
|
char *path, *dir;
|
|
|
|
|
struct stat s;
|
|
|
|
|
|
|
|
|
|
if (strchr(name, '/') || !getenv("PATH")) {
|
|
|
|
|
return stat(name, &s) == 0 ? xstrdup(name) : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path = xstrdup(getenv("PATH"));
|
|
|
|
|
for (dir = strtok_r(path, ":", &save_ptr); dir;
|
|
|
|
|
dir = strtok_r(NULL, ":", &save_ptr)) {
|
|
|
|
|
char *file = xasprintf("%s/%s", dir, name);
|
|
|
|
|
if (stat(file, &s) == 0) {
|
|
|
|
|
free(path);
|
|
|
|
|
return file;
|
|
|
|
|
}
|
|
|
|
|
free(file);
|
|
|
|
|
}
|
|
|
|
|
free(path);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-02-11 10:59:47 -08:00
|
|
|
|
sigchld_handler(int signr OVS_UNUSED)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2009-12-14 23:08:10 -08:00
|
|
|
|
ignore(write(fds[1], "", 1));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|