aboutsummaryrefslogtreecommitdiff
path: root/lib/daemon.c
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2009-12-17 10:56:01 -0800
committerBen Pfaff <blp@nicira.com>2009-12-18 13:37:44 -0800
commit95440284bdf8ac9a94c3e119d011d76acab577a7 (patch)
tree503a473672b4bfd0a6dee762480bcca9f10e1d75 /lib/daemon.c
parent058fd2a2741deff874db53f0d7cdbf743e1f2a0f (diff)
daemon: Allow daemon child process to report success or failure to parent.
There are conflicting pressures in startup of a daemon process: * The parent process should exit with an error code if the daemon cannot start up successfully. * Some startup actions must be performed in the child process, not in the parent. The most obvious of these are file locking, since child processes do not inherit locks, and anything that requires knowing the child process's PID (e.g. unixctl sockets). Until now, this conflict has usually been handled by giving up part of the first property, i.e. in some cases the parent process would exit successfully and the child immediately afterward exit with a failure code. This commit introduces a better approach, by allowing daemons to perform startup work in the child and only then signal the parent that they have successfully started. If the child instead exits without signaling success, the parent passes this exit code along to its own parent. This commit also modifies the daemons that can usefully take advantage of this new feature to do so.
Diffstat (limited to 'lib/daemon.c')
-rw-r--r--lib/daemon.c87
1 files changed, 63 insertions, 24 deletions
diff --git a/lib/daemon.c b/lib/daemon.c
index 50cc3352..c4effa9b 100644
--- a/lib/daemon.c
+++ b/lib/daemon.c
@@ -20,6 +20,7 @@
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/wait.h>
#include <unistd.h>
#include "fatal-signal.h"
#include "dirs.h"
@@ -39,9 +40,12 @@ static char *pidfile;
/* 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 *
@@ -211,47 +215,82 @@ make_pidfile(void)
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)
{