In ofp_packet_to_string(), make tcpdump print Ethernet headers also.
[sliver-openvswitch.git] / lib / daemon.c
index e14b776..c0b42bc 100644 (file)
@@ -34,6 +34,7 @@
 #include <config.h>
 #include "daemon.h"
 #include <errno.h>
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -49,6 +50,19 @@ static bool detach;
 /* Name of pidfile (null if none). */
 static char *pidfile;
 
+/* Create pidfile even if one already exists and is locked? */
+static bool force;
+
+/* 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 *
+make_pidfile_name(const char *name) 
+{
+    return (!name ? xasprintf("%s/%s.pid", RUNDIR, program_name)
+            : *name == '/' ? xstrdup(name)
+            : xasprintf("%s/%s", RUNDIR, name));
+}
+
 /* Sets up a following call to daemonize() to create a pidfile named 'name'.
  * If 'name' begins with '/', then it is treated as an absolute path.
  * Otherwise, it is taken relative to RUNDIR, which is $(prefix)/var/run by
@@ -59,9 +73,25 @@ void
 set_pidfile(const char *name)
 {
     free(pidfile);
-    pidfile = (!name ? xasprintf("%s/%s.pid", RUNDIR, program_name)
-               : *name == '/' ? xstrdup(name)
-               : xasprintf("%s/%s", RUNDIR, name));
+    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;
+}
+
+/* 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;
 }
 
 /* Sets up a following call to daemonize() to detach from the foreground
@@ -72,25 +102,97 @@ set_detach(void)
     detach = true;
 }
 
-/* If a pidfile has been configured, creates it and stores 'pid' in it.  It is
- * the caller's responsibility to make sure that the pidfile will eventually
- * be deleted. */
+/* 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. */
+static pid_t
+already_running(void)
+{
+    pid_t pid = 0;
+    if (pidfile) {
+        int fd = open(pidfile, O_RDWR);
+        if (fd >= 0) {
+            struct flock lck;
+            lck.l_type = F_WRLCK;
+            lck.l_whence = SEEK_SET;
+            lck.l_start = 0;
+            lck.l_len = 0;
+            if (fcntl(fd, F_GETLK, &lck) != -1 && lck.l_type != F_UNLCK) {
+                pid = lck.l_pid;
+            }
+            close(fd);
+        }
+    }
+    return pid;
+}
+
+/* If a locked pidfile exists, issue a warning message and, unless
+ * ignore_existing_pidfile() has been called, terminate the program. */
+void
+die_if_already_running(void)
+{
+    pid_t pid = already_running();
+    if (pid) {
+        if (!force) {
+            ofp_fatal(0, "%s: already running as pid %ld",
+                      get_pidfile(), (long int) pid);
+        } else {
+            VLOG_WARN("%s: %s already running as pid %ld",
+                      get_pidfile(), program_name, (long int) pid);
+        }
+    }
+}
+
+/* If a pidfile has been configured, creates it and stores the running process'
+ * pid init.  Ensures that the pidfile will be deleted when the process
+ * exits. */
 static void
-make_pidfile(pid_t pid)
+make_pidfile(void)
 {
     if (pidfile) {
-        FILE *file;
+        /* Create pidfile via temporary file, so that observers never see an
+         * empty pidfile or an unlocked pidfile. */
+        long int pid = getpid();
+        char *tmpfile;
+        int fd;
 
-        file = fopen(pidfile, "w");
-        if (file) {
-            fprintf(file, "%ld\n", (long int) pid);
-            fclose(file);
+        tmpfile = xasprintf("%s.tmp%ld", pidfile, pid);
+        fatal_signal_add_file_to_unlink(tmpfile);
+        fd = open(tmpfile, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+        if (fd >= 0) {
+            struct flock lck;
+            lck.l_type = F_WRLCK;
+            lck.l_whence = SEEK_SET;
+            lck.l_start = 0;
+            lck.l_len = 0;
+            if (fcntl(fd, F_SETLK, &lck) != -1) {
+                char *text = xasprintf("%ld\n", pid);
+                if (write(fd, text, strlen(text)) == strlen(text)) {
+                    fatal_signal_add_file_to_unlink(pidfile);
+                    if (rename(tmpfile, pidfile) < 0) {
+                        VLOG_ERR("failed to rename \"%s\" to \"%s\": %s",
+                                 tmpfile, pidfile, strerror(errno));
+                        fatal_signal_remove_file_to_unlink(pidfile);
+                        close(fd);
+                    } else {
+                        /* Keep 'fd' open to retain the lock. */
+                    }
+                } else {
+                    VLOG_ERR("%s: write failed: %s", tmpfile, strerror(errno));
+                    close(fd);
+                }
+            } else {
+                VLOG_ERR("%s: fcntl failed: %s", tmpfile, strerror(errno));
+                close(fd);
+            }
         } else {
-            VLOG_ERR("failed to create \"%s\": %s", pidfile, strerror(errno));
+            VLOG_ERR("%s: create failed: %s", tmpfile, strerror(errno));
         }
-        free(pidfile);
-        pidfile = NULL;
+        fatal_signal_remove_file_to_unlink(tmpfile);
+        free(tmpfile);
     }
+    free(pidfile);
+    pidfile = NULL;
 }
 
 /* If configured with set_pidfile() or set_detach(), creates the pid file and
@@ -99,28 +201,37 @@ void
 daemonize(void)
 {
     if (detach) {
-        pid_t pid;
+        char c = 0;
+        int fds[2];
+        if (pipe(fds) < 0) {
+            ofp_fatal(errno, "pipe failed");
+        }
 
-        /* Fork and exit from the parent. */
-        pid = fork();
-        if (pid < 0) {
-            fatal(errno, "could not fork");
-        } else if (pid) {
+        switch (fork()) {
+        default:
+            /* Parent process: wait for child to create pidfile, then exit. */
+            close(fds[1]);
             fatal_signal_fork();
-            make_pidfile(pid);
+            read(fds[0], &c, 1);
             exit(0);
-        }
 
-        if (pidfile) {
-            fatal_signal_add_file_to_unlink(pidfile);
+        case 0:
+            /* Child process. */
+            close(fds[0]);
+            make_pidfile();
+            write(fds[1], &c, 1);
+            close(fds[1]);
+            setsid();
+            chdir("/");
+            break;
+
+        case -1:
+            /* Error. */
+            ofp_fatal(errno, "could not fork");
+            break;
         }
-        setsid();
-        chdir("/");
     } else {
-        if (pidfile) {
-            fatal_signal_add_file_to_unlink(pidfile);
-        }
-        make_pidfile(getpid());
+        make_pidfile();
     }
 }