Under most circumstances, rrdcached can detect a stale pid file.
[rrdtool.git] / src / rrd_daemon.c
index 8b23e52..9fbbc11 100644 (file)
@@ -286,7 +286,7 @@ static void install_signal_handlers(void) /* {{{ */
 
 } /* }}} void install_signal_handlers */
 
-static int open_pidfile(void) /* {{{ */
+static int open_pidfile(char *action, int oflag) /* {{{ */
 {
   int fd;
   char *file;
@@ -295,14 +295,52 @@ static int open_pidfile(void) /* {{{ */
     ? config_pid_file
     : LOCALSTATEDIR "/run/rrdcached.pid";
 
-  fd = open(file, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IRGRP|S_IROTH);
+  fd = open(file, oflag, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
   if (fd < 0)
-    fprintf(stderr, "FATAL: cannot create '%s' (%s)\n",
-            file, rrd_strerror(errno));
+    fprintf(stderr, "rrdcached: can't %s pid file '%s' (%s)\n",
+            action, file, rrd_strerror(errno));
 
   return(fd);
 } /* }}} static int open_pidfile */
 
+/* check existing pid file to see whether a daemon is running */
+static int check_pidfile(void)
+{
+  int pid_fd;
+  pid_t pid;
+  char pid_str[16];
+
+  pid_fd = open_pidfile("open", O_RDWR);
+  if (pid_fd < 0)
+    return pid_fd;
+
+  if (read(pid_fd, pid_str, sizeof(pid_str)) <= 0)
+    return -1;
+
+  pid = atoi(pid_str);
+  if (pid <= 0)
+    return -1;
+
+  /* another running process that we can signal COULD be
+   * a competing rrdcached */
+  if (pid != getpid() && kill(pid, 0) == 0)
+  {
+    fprintf(stderr,
+            "FATAL: Another rrdcached daemon is running?? (pid %d)\n", pid);
+    close(pid_fd);
+    return -1;
+  }
+
+  lseek(pid_fd, 0, SEEK_SET);
+  ftruncate(pid_fd, 0);
+
+  fprintf(stderr,
+          "rrdcached: removed stale PID file (no rrdcached on pid %d)\n"
+          "rrdcached: starting normally.\n", pid);
+
+  return pid_fd;
+} /* }}} static int check_pidfile */
+
 static int write_pidfile (int fd) /* {{{ */
 {
   pid_t pid;
@@ -2318,13 +2356,16 @@ static void *listen_thread_main (void *args __attribute__((unused))) /* {{{ */
 static int daemonize (void) /* {{{ */
 {
   int status;
-  int fd;
+  int pid_fd;
   char *base_dir;
 
   daemon_uid = geteuid();
 
-  fd = open_pidfile();
-  if (fd < 0) return fd;
+  pid_fd = open_pidfile("create", O_CREAT|O_EXCL|O_WRONLY);
+  if (pid_fd < 0)
+    pid_fd = check_pidfile();
+  if (pid_fd < 0)
+    return pid_fd;
 
   if (!stay_foreground)
   {
@@ -2377,7 +2418,7 @@ static int daemonize (void) /* {{{ */
     return (-1);
   }
 
-  status = write_pidfile (fd);
+  status = write_pidfile (pid_fd);
   return status;
 } /* }}} int daemonize */
 
@@ -2493,6 +2534,7 @@ static int read_options (int argc, char **argv) /* {{{ */
       case 'b':
       {
         size_t len;
+        char base_realpath[PATH_MAX];
 
         if (config_base_dir != NULL)
           free (config_base_dir);
@@ -2503,6 +2545,27 @@ static int read_options (int argc, char **argv) /* {{{ */
           return (3);
         }
 
+        /* make sure that the base directory is not resolved via
+         * symbolic links.  this makes some performance-enhancing
+         * assumptions possible (we don't have to resolve paths
+         * that start with a "/")
+         */
+        if (realpath(config_base_dir, base_realpath) == NULL)
+        {
+          fprintf (stderr, "Invalid base directory '%s'.\n", config_base_dir);
+          return 5;
+        }
+        else if (strncmp(config_base_dir,
+                         base_realpath, sizeof(base_realpath)) != 0)
+        {
+          fprintf(stderr,
+                  "Base directory (-b) resolved via file system links!\n"
+                  "Please consult rrdcached '-b' documentation!\n"
+                  "Consider specifying the real directory (%s)\n",
+                  base_realpath);
+          return 5;
+        }
+
         len = strlen (config_base_dir);
         while ((len > 0) && (config_base_dir[len - 1] == '/'))
         {