testsuite.at: Workaround for carriage returns on windows.
[sliver-openvswitch.git] / lib / daemon.c
index 84ed614..c1c6550 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
 
 #include <config.h>
 #include "daemon.h"
-#include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
@@ -30,6 +29,7 @@
 #include "fatal-signal.h"
 #include "dirs.h"
 #include "lockfile.h"
+#include "ovs-thread.h"
 #include "process.h"
 #include "socket-util.h"
 #include "timeval.h"
@@ -69,10 +69,13 @@ static bool save_fds[3];
 
 static void check_already_running(void);
 static int lock_pidfile(FILE *, int command);
+static char *make_pidfile_name(const char *name);
+static pid_t fork_and_clean_up(void);
+static void daemonize_post_detach(void);
 
 /* 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 *
+static char *
 make_pidfile_name(const char *name)
 {
     return (!name
@@ -89,19 +92,11 @@ make_pidfile_name(const char *name)
 void
 set_pidfile(const char *name)
 {
+    assert_single_threaded();
     free(pidfile);
     pidfile = make_pidfile_name(name);
 }
 
-/* Returns an absolute path to the configured pidfile, or a null pointer if no
- * pidfile is configured.  The caller must not modify or free the returned
- * string. */
-const char *
-get_pidfile(void)
-{
-    return pidfile;
-}
-
 /* Sets that we do not chdir to "/". */
 void
 set_no_chdir(void)
@@ -109,13 +104,6 @@ set_no_chdir(void)
     chdir_ = false;
 }
 
-/* Will we chdir to "/" as part of daemonizing? */
-bool
-is_chdir_enabled(void)
-{
-    return chdir_;
-}
-
 /* Normally, daemonize() or damonize_start() will terminate the program with a
  * message if a locked pidfile already exists.  If this function is called, an
  * existing pidfile will be replaced, with a warning. */
@@ -158,7 +146,9 @@ daemon_set_monitor(void)
 void
 daemon_save_fd(int fd)
 {
-    assert(fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO);
+    ovs_assert(fd == STDIN_FILENO ||
+               fd == STDOUT_FILENO ||
+               fd == STDERR_FILENO);
     save_fds[fd] = true;
 }
 
@@ -175,57 +165,64 @@ 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));
+        VLOG_FATAL("%s: create failed (%s)", tmpfile, ovs_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,
+                   ovs_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));
+        VLOG_FATAL("%s: fstat failed (%s)", tmpfile, ovs_strerror(errno));
+    }
+
+    if (ftruncate(fileno(file), 0) == -1) {
+        VLOG_FATAL("%s: truncate failed (%s)", tmpfile, ovs_strerror(errno));
     }
 
     fprintf(file, "%ld\n", pid);
     if (fflush(file) == EOF) {
-        VLOG_FATAL("%s: write failed (%s)", tmpfile, strerror(errno));
+        VLOG_FATAL("%s: write failed (%s)", tmpfile, ovs_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, ovs_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
@@ -233,8 +230,6 @@ make_pidfile(void)
     pidfile_dev = s.st_dev;
     pidfile_ino = s.st_ino;
     free(tmpfile);
-    free(pidfile);
-    pidfile = NULL;
 }
 
 /* If configured with set_pidfile() or set_detach(), creates the pid file and
@@ -252,23 +247,17 @@ daemonize(void)
  * 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
+static pid_t
 fork_and_clean_up(void)
 {
-    pid_t pid;
-
-    pid = fork();
+    pid_t pid = xfork();
     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;
 }
 
@@ -317,9 +306,9 @@ fork_and_wait_for_startup(int *fdp)
                                status_msg);
                 }
             } else if (retval < 0) {
-                VLOG_FATAL("waitpid failed (%s)", strerror(errno));
+                VLOG_FATAL("waitpid failed (%s)", ovs_strerror(errno));
             } else {
-                NOT_REACHED();
+                OVS_NOT_REACHED();
             }
         }
         close(fds[0]);
@@ -342,7 +331,7 @@ fork_notify_startup(int fd)
 
         error = write_fully(fd, "", 1, &bytes_written);
         if (error) {
-            VLOG_FATAL("pipe write failed (%s)", strerror(error));
+            VLOG_FATAL("pipe write failed (%s)", ovs_strerror(error));
         }
 
         close(fd);
@@ -354,6 +343,8 @@ should_restart(int status)
 {
     if (WIFSIGNALED(status)) {
         static const int error_signals[] = {
+            /* This list of signals is documented in daemon.man.  If you
+             * change the list, update the documentation too. */
             SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGILL, SIGPIPE, SIGSEGV,
             SIGXCPU, SIGXFSZ
         };
@@ -377,7 +368,7 @@ monitor_daemon(pid_t daemon_pid)
     char *status_msg;
     int crashes;
 
-    subprogram_name = "monitor";
+    set_subprogram_name("monitor");
     status_msg = xstrdup("healthy");
     last_restart = TIME_MIN;
     crashes = 0;
@@ -385,16 +376,15 @@ monitor_daemon(pid_t daemon_pid)
         int retval;
         int status;
 
-        proctitle_set("%s: monitoring pid %lu (%s)",
-                      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);
         } while (retval == -1 && errno == EINTR);
 
         if (retval == -1) {
-            VLOG_FATAL("waitpid failed (%s)", strerror(errno));
+            VLOG_FATAL("waitpid failed (%s)", ovs_strerror(errno));
         } else if (retval == daemon_pid) {
             char *s = process_status_msg(status);
             if (should_restart(status)) {
@@ -412,7 +402,7 @@ monitor_daemon(pid_t daemon_pid)
                     r.rlim_max = 0;
                     if (setrlimit(RLIMIT_CORE, &r) == -1) {
                         VLOG_WARN("failed to disable core dumps: %s",
-                                  strerror(errno));
+                                  ovs_strerror(errno));
                     }
                 }
 
@@ -426,7 +416,7 @@ monitor_daemon(pid_t daemon_pid)
                         if (now >= wakeup) {
                             break;
                         }
-                        sleep(wakeup - now);
+                        xsleep(wakeup - now);
                     }
                 }
                 last_restart = time(NULL);
@@ -448,7 +438,27 @@ monitor_daemon(pid_t daemon_pid)
 
     /* Running in new daemon process. */
     proctitle_restore();
-    subprogram_name = "";
+    set_subprogram_name("");
+}
+
+/* Returns a readable and writable fd for /dev/null, if successful, otherwise
+ * a negative errno value.  The caller must not close the returned fd (because
+ * the same fd will be handed out to subsequent callers). */
+static int
+get_null_fd(void)
+{
+    static int null_fd;
+
+    if (!null_fd) {
+        null_fd = open("/dev/null", O_RDWR);
+        if (null_fd < 0) {
+            int error = errno;
+            VLOG_ERR("could not open /dev/null: %s", ovs_strerror(error));
+            null_fd = -error;
+        }
+    }
+
+    return null_fd;
 }
 
 /* Close standard file descriptors (except any that the client has requested we
@@ -480,6 +490,7 @@ close_standard_fds(void)
 void
 daemonize_start(void)
 {
+    assert_single_threaded();
     daemonize_fd = -1;
 
     if (detach) {
@@ -487,7 +498,9 @@ daemonize_start(void)
             /* Running in parent process. */
             exit(0);
         }
+
         /* Running in daemon or monitor process. */
+        setsid();
     }
 
     if (monitor) {
@@ -504,6 +517,8 @@ daemonize_start(void)
         /* Running in daemon process. */
     }
 
+    forbid_forking("running in daemon process");
+
     if (pidfile) {
         make_pidfile();
     }
@@ -521,6 +536,11 @@ daemonize_start(void)
 void
 daemonize_complete(void)
 {
+    if (pidfile) {
+        free(pidfile);
+        pidfile = NULL;
+    }
+
     if (!detached) {
         detached = true;
 
@@ -537,11 +557,10 @@ daemonize_complete(void)
  * 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
+static void
 daemonize_post_detach(void)
 {
     if (detach) {
-        setsid();
         if (chdir_) {
             ignore(chdir("/"));
         }
@@ -613,13 +632,13 @@ read_pidfile__(const char *pidfile, bool delete_if_stale)
             return 0;
         }
         error = errno;
-        VLOG_WARN("%s: open: %s", pidfile, strerror(error));
+        VLOG_WARN("%s: open: %s", pidfile, ovs_strerror(error));
         goto error;
     }
 
     error = lock_pidfile__(file, F_GETLK, &lck);
     if (error) {
-        VLOG_WARN("%s: fcntl: %s", pidfile, strerror(error));
+        VLOG_WARN("%s: fcntl: %s", pidfile, ovs_strerror(error));
         goto error;
     }
     if (lck.l_type == F_UNLCK) {
@@ -658,7 +677,7 @@ read_pidfile__(const char *pidfile, bool delete_if_stale)
         if (unlink(pidfile)) {
             error = errno;
             VLOG_WARN("%s: failed to delete stale pidfile (%s)",
-                      pidfile, strerror(error));
+                      pidfile, ovs_strerror(error));
             goto error;
         }
         VLOG_DBG("%s: deleted stale pidfile", pidfile);
@@ -669,7 +688,7 @@ read_pidfile__(const char *pidfile, bool delete_if_stale)
     if (!fgets(line, sizeof line, file)) {
         if (ferror(file)) {
             error = errno;
-            VLOG_WARN("%s: read: %s", pidfile, strerror(error));
+            VLOG_WARN("%s: read: %s", pidfile, ovs_strerror(error));
         } else {
             error = ESRCH;
             VLOG_WARN("%s: read: unexpected end of file", pidfile);
@@ -715,6 +734,25 @@ check_already_running(void)
         VLOG_FATAL("%s: already running as pid %ld, aborting", pidfile, pid);
     } else if (pid < 0) {
         VLOG_FATAL("%s: pidfile check failed (%s), aborting",
-                   pidfile, strerror(-pid));
+                   pidfile, ovs_strerror(-pid));
     }
 }
+
+\f
+/* stub functions for non-windows platform. */
+
+void
+service_start(int *argc OVS_UNUSED, char **argv[] OVS_UNUSED)
+{
+}
+
+void
+service_stop(void)
+{
+}
+
+bool
+should_service_stop(void)
+{
+    return false;
+}