Describe dummy test model. Work on OpenFlow intro.
[openvswitch] / lib / daemon.c
index c4a82647f8ace909101e36d89409a024cce9b70d..25f2db72b2bd1ca8fb082df216f95e94380e0958 100644 (file)
@@ -39,7 +39,8 @@
 VLOG_DEFINE_THIS_MODULE(daemon);
 
 /* --detach: Should we run in the background? */
-static bool detach;
+static bool detach;             /* Was --detach specified? */
+static bool detached;           /* Have we already detached? */
 
 /* --pidfile: Name of pidfile (null if none). */
 static char *pidfile;
@@ -174,57 +175,63 @@ make_pidfile(void)
     int error;
 
     /* Create a temporary pidfile. */
-    tmpfile = xasprintf("%s.tmp%ld", pidfile, pid);
-    fatal_signal_add_file_to_unlink(tmpfile);
-    file = fopen(tmpfile, "w+");
+    if (overwrite_pidfile) {
+        tmpfile = xasprintf("%s.tmp%ld", pidfile, pid);
+        fatal_signal_add_file_to_unlink(tmpfile);
+    } else {
+        /* Everyone shares the same file which will be treated as a lock.  To
+         * avoid some uncomfortable race conditions, we can't set up the fatal
+         * signal unlink until we've acquired it. */
+        tmpfile = xasprintf("%s.tmp", pidfile);
+    }
+
+    file = fopen(tmpfile, "a+");
     if (!file) {
         VLOG_FATAL("%s: create failed (%s)", tmpfile, strerror(errno));
     }
 
+    error = lock_pidfile(file, F_SETLK);
+    if (error) {
+        /* Looks like we failed to acquire the lock.  Note that, if we failed
+         * for some other reason (and '!overwrite_pidfile'), we will have
+         * left 'tmpfile' as garbage in the file system. */
+        VLOG_FATAL("%s: fcntl(F_SETLK) failed (%s)", tmpfile, strerror(error));
+    }
+
+    if (!overwrite_pidfile) {
+        /* We acquired the lock.  Make sure to clean up on exit, and verify
+         * that we're allowed to create the actual pidfile. */
+        fatal_signal_add_file_to_unlink(tmpfile);
+        check_already_running();
+    }
+
     if (fstat(fileno(file), &s) == -1) {
         VLOG_FATAL("%s: fstat failed (%s)", tmpfile, strerror(errno));
     }
 
+    if (ftruncate(fileno(file), 0) == -1) {
+        VLOG_FATAL("%s: truncate failed (%s)", tmpfile, strerror(errno));
+    }
+
     fprintf(file, "%ld\n", pid);
     if (fflush(file) == EOF) {
         VLOG_FATAL("%s: write failed (%s)", tmpfile, strerror(errno));
     }
 
-    error = lock_pidfile(file, F_SETLK);
-    if (error) {
-        VLOG_FATAL("%s: fcntl(F_SETLK) failed (%s)", tmpfile, strerror(error));
-    }
+    error = rename(tmpfile, pidfile);
 
-    /* Rename or link it to the correct name. */
-    if (overwrite_pidfile) {
-        if (rename(tmpfile, pidfile) < 0) {
-            VLOG_FATAL("failed to rename \"%s\" to \"%s\" (%s)",
-                       tmpfile, pidfile, strerror(errno));
-        }
-    } else {
-        do {
-            error = link(tmpfile, pidfile) == -1 ? errno : 0;
-            if (error == EEXIST) {
-                check_already_running();
-            }
-        } while (error == EINTR || error == EEXIST);
-        if (error) {
-            VLOG_FATAL("failed to link \"%s\" as \"%s\" (%s)",
-                       tmpfile, pidfile, strerror(error));
-        }
+    /* Due to a race, 'tmpfile' may be owned by a different process, so we
+     * shouldn't delete it on exit. */
+    fatal_signal_remove_file_to_unlink(tmpfile);
+
+    if (error < 0) {
+        VLOG_FATAL("failed to rename \"%s\" to \"%s\" (%s)",
+                   tmpfile, pidfile, strerror(errno));
     }
 
     /* Ensure that the pidfile will get deleted on exit. */
     fatal_signal_add_file_to_unlink(pidfile);
 
-    /* Delete the temporary pidfile if it still exists. */
-    if (!overwrite_pidfile) {
-        error = fatal_signal_unlink_file_now(tmpfile);
-        if (error) {
-            VLOG_FATAL("%s: unlink failed (%s)", tmpfile, strerror(error));
-        }
-    }
-
     /* Clean up.
      *
      * We don't close 'file' because its file descriptor must remain open to
@@ -245,6 +252,43 @@ daemonize(void)
     daemonize_complete();
 }
 
+/* Calls fork() and on success returns its return value.  On failure, logs an
+ * error and exits unsuccessfully.
+ *
+ * Post-fork, but before returning, this function calls a few other functions
+ * that are generally useful if the child isn't planning to exec a new
+ * process. */
+pid_t
+fork_and_clean_up(void)
+{
+    pid_t pid;
+
+    pid = fork();
+    if (pid > 0) {
+        /* Running in parent process. */
+        fatal_signal_fork();
+    } else if (!pid) {
+        /* Running in child process. */
+        time_postfork();
+        lockfile_postfork();
+    } else {
+        VLOG_FATAL("fork failed (%s)", strerror(errno));
+    }
+
+    return pid;
+}
+
+/* Forks, then:
+ *
+ *   - In the parent, waits for the child to signal that it has completed its
+ *     startup sequence.  Then stores -1 in '*fdp' and returns the child's pid.
+ *
+ *   - In the child, stores a fd in '*fdp' and returns 0.  The caller should
+ *     pass the fd to fork_notify_startup() after it finishes its startup
+ *     sequence.
+ *
+ * If something goes wrong with the fork, logs a critical error and aborts the
+ * process. */
 static pid_t
 fork_and_wait_for_startup(int *fdp)
 {
@@ -253,14 +297,13 @@ fork_and_wait_for_startup(int *fdp)
 
     xpipe(fds);
 
-    pid = fork();
+    pid = fork_and_clean_up();
     if (pid > 0) {
         /* Running in parent process. */
         size_t bytes_read;
         char c;
 
         close(fds[1]);
-        fatal_signal_fork();
         if (read_fully(fds[0], &c, 1, &bytes_read) != 0) {
             int retval;
             int status;
@@ -290,11 +333,7 @@ fork_and_wait_for_startup(int *fdp)
     } else if (!pid) {
         /* Running in child process. */
         close(fds[0]);
-        time_postfork();
-        lockfile_postfork();
         *fdp = fds[1];
-    } else {
-        VLOG_FATAL("fork failed (%s)", strerror(errno));
     }
 
     return pid;
@@ -340,13 +379,11 @@ static void
 monitor_daemon(pid_t daemon_pid)
 {
     /* XXX Should log daemon's stderr output at startup time. */
-    const char *saved_program_name;
     time_t last_restart;
     char *status_msg;
     int crashes;
 
-    saved_program_name = program_name;
-    program_name = xasprintf("monitor(%s)", program_name);
+    subprogram_name = "monitor";
     status_msg = xstrdup("healthy");
     last_restart = TIME_MIN;
     crashes = 0;
@@ -354,9 +391,8 @@ monitor_daemon(pid_t daemon_pid)
         int retval;
         int status;
 
-        proctitle_set("%s: monitoring pid %lu (%s)",
-                      saved_program_name, (unsigned long int) daemon_pid,
-                      status_msg);
+        proctitle_set("monitoring pid %lu (%s)",
+                      (unsigned long int) daemon_pid, status_msg);
 
         do {
             retval = waitpid(daemon_pid, &status, 0);
@@ -417,8 +453,7 @@ monitor_daemon(pid_t daemon_pid)
 
     /* Running in new daemon process. */
     proctitle_restore();
-    free((char *) program_name);
-    program_name = saved_program_name;
+    subprogram_name = "";
 }
 
 /* Close standard file descriptors (except any that the client has requested we
@@ -484,22 +519,38 @@ daemonize_start(void)
 }
 
 /* If daemonization is configured, then this function notifies the parent
- * process that the child process has completed startup successfully.
+ * process that the child process has completed startup successfully.  It also
+ * call daemonize_post_detach().
  *
  * Calling this function more than once has no additional effect. */
 void
 daemonize_complete(void)
 {
-    fork_notify_startup(daemonize_fd);
-    daemonize_fd = -1;
+    if (!detached) {
+        detached = true;
+
+        fork_notify_startup(daemonize_fd);
+        daemonize_fd = -1;
+        daemonize_post_detach();
+    }
+}
 
+/* If daemonization is configured, then this function does traditional Unix
+ * daemonization behavior: join a new session, chdir to the root (if not
+ * disabled), and close the standard file descriptors.
+ *
+ * It only makes sense to call this function as part of an implementation of a
+ * special daemon subprocess.  A normal daemon should just call
+ * daemonize_complete(). */
+void
+daemonize_post_detach(void)
+{
     if (detach) {
         setsid();
         if (chdir_) {
             ignore(chdir("/"));
         }
         close_standard_fds();
-        detach = false;
     }
 }