#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/wait.h>
#include <unistd.h>
#include "fatal-signal.h"
#include "dirs.h"
/* Create pidfile even if one already exists and is locked? */
static bool overwrite_pidfile;
-/* Should we chdir to "/". */
+/* Should we chdir to "/"? */
static bool chdir_ = true;
+/* File descriptors used by daemonize_start() and daemonize_complete(). */
+static int daemonize_fds[2];
+
/* Returns the file name that would be used for a pidfile if 'name' were
* provided to set_pidfile(). The caller must free the returned string. */
char *
* detaches from the foreground session. */
void
daemonize(void)
+{
+ daemonize_start();
+ daemonize_complete();
+}
+
+/* If daemonization is configured, then starts daemonization, by forking and
+ * returning in the child process. The parent process hangs around until the
+ * child lets it know either that it completed startup successfully (by calling
+ * daemon_complete()) or that it failed to start up (by exiting with a nonzero
+ * exit code). */
+void
+daemonize_start(void)
{
if (detach) {
- char c = 0;
- int fds[2];
- if (pipe(fds) < 0) {
+ pid_t pid;
+
+ if (pipe(daemonize_fds) < 0) {
ovs_fatal(errno, "pipe failed");
}
- switch (fork()) {
- default:
- /* Parent process: wait for child to create pidfile, then exit. */
- close(fds[1]);
+ pid = fork();
+ if (pid > 0) {
+ /* Running in parent process. */
+ char c;
+
+ close(daemonize_fds[1]);
fatal_signal_fork();
- if (read(fds[0], &c, 1) != 1) {
+ if (read(daemonize_fds[0], &c, 1) != 1) {
+ int retval;
+ int status;
+
+ do {
+ retval = waitpid(pid, &status, 0);
+ } while (retval == -1 && errno == EINTR);
+
+ if (retval == pid
+ && WIFEXITED(status)
+ && WEXITSTATUS(status)) {
+ /* Child exited with an error. Convey the same error to
+ * our parent process as a courtesy. */
+ exit(WEXITSTATUS(status));
+ }
+
ovs_fatal(errno, "daemon child failed to signal startup");
}
exit(0);
-
- case 0:
- /* Child process. */
- close(fds[0]);
+ } else if (!pid) {
+ /* Running in child process. */
+ close(daemonize_fds[0]);
make_pidfile();
- ignore(write(fds[1], &c, 1));
- close(fds[1]);
- setsid();
- if (chdir_) {
- ignore(chdir("/"));
- }
time_postfork();
lockfile_postfork();
- break;
-
- case -1:
- /* Error. */
+ } else {
ovs_fatal(errno, "could not fork");
- break;
}
} else {
make_pidfile();
}
}
+/* If daemonization is configured, then this function notifies the parent
+ * process that the child process has completed startup successfully. */
+void
+daemonize_complete(void)
+{
+ if (detach) {
+ char c = 0;
+
+ ignore(write(daemonize_fds[1], &c, 1));
+ close(daemonize_fds[1]);
+ setsid();
+ if (chdir_) {
+ ignore(chdir("/"));
+ }
+ }
+}
+
void
daemon_usage(void)
{
void set_detach(void);
bool get_detach(void);
void daemonize(void);
+void daemonize_start(void);
+void daemonize_complete(void);
void die_if_already_running(void);
void ignore_existing_pidfile(void);
void daemon_usage(void);
struct unixctl_server *unixctl;
struct ovsdb_jsonrpc_server *jsonrpc;
struct svec active, passive;
- struct pstream **listeners;
struct ovsdb_error *error;
struct ovsdb *db;
const char *name;
char *file_name;
- bool do_chdir;
bool exiting;
int retval;
size_t i;
parse_options(argc, argv, &file_name, &active, &passive, &unixctl_path);
- /* Open all the passive sockets before detaching, to avoid race with
- * processes that start up later. */
- listeners = xmalloc(passive.n * sizeof *listeners);
- for (i = 0; i < passive.n; i++) {
- int error;
-
- error = pstream_open(passive.names[i], &listeners[i]);
- if (error) {
- ovs_fatal(error, "failed to listen on \"%s\"", passive.names[i]);
- }
- }
-
- if (get_detach() && is_chdir_enabled()) {
- /* We need to skip chdir("/") in daemonize() and do it later, because
- * we need to open the database and possible set up up Unix domain
- * sockets in the current working directory after we daemonize. We
- * can't open the database before we daemonize because file locks
- * aren't inherited by child processes. */
- do_chdir = true;
- set_no_chdir();
- } else {
- do_chdir = false;
- }
die_if_already_running();
- daemonize();
+ daemonize_start();
error = ovsdb_file_open(file_name, false, &db);
if (error) {
ovsdb_jsonrpc_server_connect(jsonrpc, name);
}
for (i = 0; i < passive.n; i++) {
- ovsdb_jsonrpc_server_listen(jsonrpc, listeners[i]);
+ struct pstream *pstream;
+ int error;
+
+ error = pstream_open(passive.names[i], &pstream);
+ if (error) {
+ ovs_fatal(error, "failed to listen on \"%s\"", passive.names[i]);
+ }
+ ovsdb_jsonrpc_server_listen(jsonrpc, pstream);
}
svec_destroy(&active);
svec_destroy(&passive);
ovs_fatal(retval, "could not listen for control connections");
}
- unixctl_command_register("exit", ovsdb_server_exit, &exiting);
+ daemonize_complete();
- if (do_chdir) {
- ignore(chdir("/"));
- }
+ unixctl_command_register("exit", ovsdb_server_exit, &exiting);
exiting = false;
while (!exiting) {
}
die_if_already_running();
- daemonize();
+ daemonize_start();
retval = unixctl_server_create(NULL, &unixctl);
if (retval) {
ovs_fatal(retval, "Could not listen for unixctl connections");
}
+ daemonize_complete();
+
while (n_switches > 0 || n_listeners > 0) {
int iteration;
int i;
signal(SIGPIPE, SIG_IGN);
die_if_already_running();
- daemonize();
+ daemonize_start();
/* Start listening for ovs-appctl requests. */
error = unixctl_server_create(NULL, &unixctl);
}
}
+ daemonize_complete();
+
while (ofproto_is_alive(ofproto)) {
error = ofproto_run(ofproto);
if (error) {
process_init();
die_if_already_running();
- daemonize();
+ daemonize_start();
retval = unixctl_server_create(NULL, &unixctl);
if (retval) {
}
}
+ daemonize_complete();
+
idl = ovsdb_idl_create(remote, &ovsrec_idl_class);
for (;;) {
process_init();
die_if_already_running();
- daemonize();
+ daemonize_start();
retval = unixctl_server_create(NULL, &unixctl);
if (retval) {
ovs_fatal(retval, "could not listen for control connections");
}
+ daemonize_complete();
+
idl = ovsdb_idl_create(remote, &ovsrec_idl_class);
idl_seqno = ovsdb_idl_get_seqno(idl);