stream: Add stream_run(), stream_run_wait() functions.
[openvswitch] / lib / daemon.c
index a011d37fe02d9e3cf5bfb6daf265c11e089b85ed..9a1be55dbfc868d14274a138e9bd28c1491dc521 100644 (file)
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/wait.h>
 #include <unistd.h>
 #include "fatal-signal.h"
 #include "dirs.h"
+#include "lockfile.h"
+#include "socket-util.h"
+#include "timeval.h"
 #include "util.h"
 
 #define THIS_MODULE VLM_daemon
@@ -35,11 +39,14 @@ static bool detach;
 static char *pidfile;
 
 /* Create pidfile even if one already exists and is locked? */
-static bool force;
+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 *
@@ -79,13 +86,20 @@ set_no_chdir(void)
     chdir_ = false;
 }
 
+/* Will we chdir to "/" as part of daemonizing? */
+bool
+is_chdir_enabled(void)
+{
+    return chdir_;
+}
+
 /* Normally, die_if_already_running() will terminate the program with a message
  * if a locked pidfile already exists.  If this function is called,
  * die_if_already_running() will merely log a warning. */
 void
 ignore_existing_pidfile(void)
 {
-    force = true;
+    overwrite_pidfile = true;
 }
 
 /* Sets up a following call to daemonize() to detach from the foreground
@@ -96,6 +110,13 @@ set_detach(void)
     detach = true;
 }
 
+/* Will daemonize() really detach? */
+bool
+get_detach(void)
+{
+    return detach;
+}
+
 /* If a pidfile has been configured and that pidfile already exists and is
  * locked by a running process, returns the pid of the running process.
  * Otherwise, returns 0. */
@@ -127,7 +148,7 @@ die_if_already_running(void)
 {
     pid_t pid = already_running();
     if (pid) {
-        if (!force) {
+        if (!overwrite_pidfile) {
             ovs_fatal(0, "%s: already running as pid %ld",
                       get_pidfile(), (long int) pid);
         } else {
@@ -194,55 +215,98 @@ make_pidfile(void)
  * 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();
-            write(fds[1], &c, 1);
-            close(fds[1]);
-            setsid();
-            if (chdir_) {
-                chdir("/");
-            }
-            break;
-
-        case -1:
-            /* Error. */
+            time_postfork();
+            lockfile_postfork();
+        } 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) {
+        size_t bytes_written;
+        int error;
+
+        error = write_fully(daemonize_fds[1], "", 1, &bytes_written);
+        if (error) {
+            ovs_fatal(error, "could not write to pipe");
+        }
+
+        close(daemonize_fds[1]);
+        setsid();
+        if (chdir_) {
+            ignore(chdir("/"));
+        }
+    }
+}
+
 void
 daemon_usage(void)
 {
     printf(
         "\nDaemon options:\n"
-        "  -D, --detach            run in background as daemon\n"
+        "  --detach                run in background as daemon\n"
         "  --no-chdir              do not chdir to '/'\n"
-        "  -P, --pidfile[=FILE]    create pidfile (default: %s/%s.pid)\n"
-        "  -f, --force             with -P, start even if already running\n",
+        "  --pidfile[=FILE]        create pidfile (default: %s/%s.pid)\n"
+        "  --overwrite-pidfile     with --pidfile, start even if already "
+                                   "running\n",
         ovs_rundir, program_name);
 }