rrdcached: pull in rrd_config.h so we can use its defines
[rrdtool.git] / src / rrd_daemon.c
index b2cee95..c6211a6 100644 (file)
 /*
  * Now for some includes..
  */
-#include "rrd.h" /* {{{ */
+/* {{{ */
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(HAVE_CONFIG_H)
+#include "../win32/config.h"
+#else
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+#endif
+
+#include "rrd.h"
 #include "rrd_client.h"
 
 #include <stdlib.h>
@@ -117,7 +126,7 @@ struct listen_socket_s
   socket_privilege privilege;
 
   /* state for BATCH processing */
-  int batch_mode;
+  time_t batch_start;
   int batch_cmd;
 
   /* buffered IO */
@@ -286,7 +295,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 +304,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;
@@ -405,7 +452,7 @@ static int add_response_info(listen_socket_t *sock, char *fmt, ...) /* {{{ */
   int len;
 
   if (sock == NULL) return 0; /* journal replay mode */
-  if (sock->batch_mode) return 0; /* no extra info returned when in BATCH */
+  if (sock->batch_start) return 0; /* no extra info returned when in BATCH */
 
   va_start(argp, fmt);
 #ifdef HAVE_VSNPRINTF
@@ -453,7 +500,7 @@ static int send_response (listen_socket_t *sock, response_code rc,
 
   if (sock == NULL) return rc;  /* journal replay mode */
 
-  if (sock->batch_mode)
+  if (sock->batch_start)
   {
     if (rc == RESP_OK)
       return rc; /* no response on success during BATCH */
@@ -478,7 +525,7 @@ static int send_response (listen_socket_t *sock, response_code rc,
   len += rclen;
 
   /* append the result to the wbuf, don't write to the user */
-  if (sock->batch_mode)
+  if (sock->batch_start)
     return add_to_wbuf(sock, buffer, len);
 
   /* first write must be complete */
@@ -488,7 +535,7 @@ static int send_response (listen_socket_t *sock, response_code rc,
     return -1;
   }
 
-  if (sock->wbuf != NULL)
+  if (sock->wbuf != NULL && rc == RESP_OK)
   {
     wrote = 0;
     while (wrote < sock->wbuf_len)
@@ -526,6 +573,7 @@ static void wipe_ci_values(cache_item_t *ci, time_t when)
 static void remove_from_queue(cache_item_t *ci) /* {{{ */
 {
   if (ci == NULL) return;
+  if ((ci->flags & CI_FLAGS_IN_QUEUE) == 0) return; /* not queued */
 
   if (ci->prev == NULL)
     cache_queue_head = ci->next; /* reset head */
@@ -587,9 +635,8 @@ static int enqueue_cache_item (cache_item_t *ci, /* {{{ */
     if (cache_queue_head == ci)
       return 0;
 
-    /* remove from the double linked list */
-    if (ci->flags & CI_FLAGS_IN_QUEUE)
-      remove_from_queue(ci);
+    /* remove if further down in queue */
+    remove_from_queue(ci);
 
     ci->prev = NULL;
     ci->next = cache_queue_head;
@@ -643,20 +690,20 @@ static gboolean tree_callback_flush (gpointer key, gpointer value, /* {{{ */
   ci = (cache_item_t *) value;
   cfd = (callback_flush_data_t *) data;
 
+  if (ci->flags & CI_FLAGS_IN_QUEUE)
+    return FALSE;
+
   if ((ci->last_flush_time <= cfd->abs_timeout)
-      && ((ci->flags & CI_FLAGS_IN_QUEUE) == 0)
       && (ci->values_num > 0))
   {
     enqueue_cache_item (ci, TAIL);
   }
   else if ((do_shutdown != 0)
-      && ((ci->flags & CI_FLAGS_IN_QUEUE) == 0)
       && (ci->values_num > 0))
   {
     enqueue_cache_item (ci, TAIL);
   }
   else if (((cfd->now - ci->last_flush_time) >= config_flush_interval)
-      && ((ci->flags & CI_FLAGS_IN_QUEUE) == 0)
       && (ci->values_num <= 0))
   {
     char **temp;
@@ -748,8 +795,8 @@ static void *queue_thread_main (void *args __attribute__((unused))) /* {{{ */
       flush_old_values (config_write_interval);
 
       /* Determine the time of the next cache flush. */
-      while (next_flush.tv_sec <= now.tv_sec)
-        next_flush.tv_sec += config_flush_interval;
+      next_flush.tv_sec =
+        now.tv_sec + next_flush.tv_sec % config_flush_interval;
 
       /* unlock the cache while we rotate so we don't block incoming
        * updates if the fsync() blocks on disk I/O */
@@ -956,6 +1003,26 @@ err:
   return 0;
 } /* }}} static int check_file_access */
 
+/* when using a base dir, convert relative paths to absolute paths.
+ * if necessary, modifies the "filename" pointer to point
+ * to the new path created in "tmp".  "tmp" is provided
+ * by the caller and sizeof(tmp) must be >= PATH_MAX.
+ *
+ * this allows us to optimize for the expected case (absolute path)
+ * with a no-op.
+ */
+static void get_abs_path(char **filename, char *tmp)
+{
+  assert(tmp != NULL);
+  assert(filename != NULL && *filename != NULL);
+
+  if (config_base_dir == NULL || **filename == '/')
+    return;
+
+  snprintf(tmp, PATH_MAX, "%s/%s", config_base_dir, *filename);
+  *filename = tmp;
+} /* }}} static int get_abs_path */
+
 /* returns 1 if we have the required privilege level,
  * otherwise issue an error to the user on sock */
 static int has_privilege (listen_socket_t *sock, /* {{{ */
@@ -1183,7 +1250,7 @@ static int handle_request_stats (listen_socket_t *sock) /* {{{ */
 static int handle_request_flush (listen_socket_t *sock, /* {{{ */
     char *buffer, size_t buffer_size)
 {
-  char *file;
+  char *file, file_tmp[PATH_MAX];
   int status;
 
   status = buffer_get_field (&buffer, &buffer_size, &file);
@@ -1197,6 +1264,7 @@ static int handle_request_flush (listen_socket_t *sock, /* {{{ */
     stats_flush_received++;
     pthread_mutex_unlock(&stats_lock);
 
+    get_abs_path(&file, file_tmp);
     if (!check_file_access(file, sock)) return 0;
 
     status = flush_file (file);
@@ -1221,7 +1289,7 @@ static int handle_request_flush (listen_socket_t *sock, /* {{{ */
 
   /* NOTREACHED */
   assert(1==0);
-} /* }}} int handle_request_slurp */
+} /* }}} int handle_request_flush */
 
 static int handle_request_flushall(listen_socket_t *sock) /* {{{ */
 {
@@ -1244,7 +1312,7 @@ static int handle_request_pending(listen_socket_t *sock, /* {{{ */
                                   char *buffer, size_t buffer_size)
 {
   int status;
-  char *file;
+  char *file, file_tmp[PATH_MAX];
   cache_item_t *ci;
 
   status = buffer_get_field(&buffer, &buffer_size, &file);
@@ -1256,6 +1324,8 @@ static int handle_request_pending(listen_socket_t *sock, /* {{{ */
   if (status <= 0)
     return status;
 
+  get_abs_path(&file, file_tmp);
+
   pthread_mutex_lock(&cache_lock);
   ci = g_tree_lookup(cache_tree, file);
   if (ci == NULL)
@@ -1275,7 +1345,7 @@ static int handle_request_forget(listen_socket_t *sock, /* {{{ */
                                  char *buffer, size_t buffer_size)
 {
   int status;
-  char *file;
+  char *file, file_tmp[PATH_MAX];
 
   status = buffer_get_field(&buffer, &buffer_size, &file);
   if (status != 0)
@@ -1286,6 +1356,7 @@ static int handle_request_forget(listen_socket_t *sock, /* {{{ */
   if (status <= 0)
     return status;
 
+  get_abs_path(&file, file_tmp);
   if (!check_file_access(file, sock)) return 0;
 
   pthread_mutex_lock(&cache_lock);
@@ -1308,19 +1379,17 @@ static int handle_request_forget(listen_socket_t *sock, /* {{{ */
 } /* }}} static int handle_request_forget */
 
 static int handle_request_update (listen_socket_t *sock, /* {{{ */
-    char *buffer, size_t buffer_size)
+                                  time_t now,
+                                  char *buffer, size_t buffer_size)
 {
-  char *file;
+  char *file, file_tmp[PATH_MAX];
   int values_num = 0;
   int bad_timestamps = 0;
   int status;
   char orig_buf[CMD_MAX];
 
-  time_t now;
   cache_item_t *ci;
 
-  now = time (NULL);
-
   status = has_privilege(sock, PRIV_HIGH);
   if (status <= 0)
     return status;
@@ -1337,6 +1406,7 @@ static int handle_request_update (listen_socket_t *sock, /* {{{ */
   stats_updates_received++;
   pthread_mutex_unlock(&stats_lock);
 
+  get_abs_path(&file, file_tmp);
   if (!check_file_access(file, sock)) return 0;
 
   pthread_mutex_lock (&cache_lock);
@@ -1389,6 +1459,7 @@ static int handle_request_update (listen_socket_t *sock, /* {{{ */
 
     wipe_ci_values(ci, now);
     ci->flags = CI_FLAGS_IN_TREE;
+    pthread_cond_init(&ci->flushed, NULL);
 
     pthread_mutex_lock(&cache_lock);
     g_tree_insert (cache_tree, (void *) ci->file, (void *) ci);
@@ -1464,6 +1535,9 @@ static int handle_request_update (listen_socket_t *sock, /* {{{ */
 
   if (values_num < 1)
   {
+    /* journal replay mode */
+    if (sock == NULL) return RESP_ERR;
+
     /* if we had only one update attempt, then return the full
        error message... try to get the most information out
        of the limited error space allowed by the protocol
@@ -1487,7 +1561,7 @@ static int handle_request_update (listen_socket_t *sock, /* {{{ */
 /* we came across a "WROTE" entry during journal replay.
  * throw away any values that we have accumulated for this file
  */
-static int handle_request_wrote (const char *buffer) /* {{{ */
+static int handle_request_wrote (const char *buffer, time_t now) /* {{{ */
 {
   int i;
   cache_item_t *ci;
@@ -1510,7 +1584,7 @@ static int handle_request_wrote (const char *buffer) /* {{{ */
     free(ci->values);
   }
 
-  wipe_ci_values(ci, time(NULL));
+  wipe_ci_values(ci, now);
   remove_from_queue(ci);
 
   pthread_mutex_unlock(&cache_lock);
@@ -1521,12 +1595,12 @@ static int handle_request_wrote (const char *buffer) /* {{{ */
 static int batch_start (listen_socket_t *sock) /* {{{ */
 {
   int status;
-  if (sock->batch_mode)
+  if (sock->batch_start)
     return send_response(sock, RESP_ERR, "Already in BATCH\n");
 
   status = send_response(sock, RESP_OK,
                          "Go ahead.  End with dot '.' on its own line.\n");
-  sock->batch_mode = 1;
+  sock->batch_start = time(NULL);
   sock->batch_cmd = 0;
 
   return status;
@@ -1535,14 +1609,15 @@ static int batch_start (listen_socket_t *sock) /* {{{ */
 /* finish "BATCH" processing and return results to the client */
 static int batch_done (listen_socket_t *sock) /* {{{ */
 {
-  assert(sock->batch_mode);
-  sock->batch_mode = 0;
+  assert(sock->batch_start);
+  sock->batch_start = 0;
   sock->batch_cmd  = 0;
   return send_response(sock, RESP_OK, "errors\n");
 } /* }}} static int batch_done */
 
 /* if sock==NULL, we are in journal replay mode */
 static int handle_request (listen_socket_t *sock, /* {{{ */
+                           time_t now,
                            char *buffer, size_t buffer_size)
 {
   char *buffer_ptr;
@@ -1560,15 +1635,15 @@ static int handle_request (listen_socket_t *sock, /* {{{ */
     return (-1);
   }
 
-  if (sock != NULL && sock->batch_mode)
+  if (sock != NULL && sock->batch_start)
     sock->batch_cmd++;
 
   if (strcasecmp (command, "update") == 0)
-    return (handle_request_update (sock, buffer_ptr, buffer_size));
+    return (handle_request_update (sock, now, buffer_ptr, buffer_size));
   else if (strcasecmp (command, "wrote") == 0 && sock == NULL)
   {
     /* this is only valid in replay mode */
-    return (handle_request_wrote (buffer_ptr));
+    return (handle_request_wrote (buffer_ptr, now));
   }
   else if (strcasecmp (command, "flush") == 0)
     return (handle_request_flush (sock, buffer_ptr, buffer_size));
@@ -1584,7 +1659,7 @@ static int handle_request (listen_socket_t *sock, /* {{{ */
     return (handle_request_help (sock, buffer_ptr, buffer_size));
   else if (strcasecmp (command, "batch") == 0 && sock != NULL)
     return batch_start(sock);
-  else if (strcasecmp (command, ".") == 0 && sock != NULL && sock->batch_mode)
+  else if (strcasecmp (command, ".") == 0 && sock != NULL && sock->batch_start)
     return batch_done(sock);
   else
     return send_response(sock, RESP_ERR, "Unknown command: %s\n", command);
@@ -1700,6 +1775,7 @@ static int journal_replay (const char *file) /* {{{ */
   int fail_cnt = 0;
   uint64_t line = 0;
   char entry[CMD_MAX];
+  time_t now;
 
   if (file == NULL) return 0;
 
@@ -1752,6 +1828,8 @@ static int journal_replay (const char *file) /* {{{ */
   else
     RRDD_LOG(LOG_NOTICE, "replaying from journal: %s", file);
 
+  now = time(NULL);
+
   while(!feof(fh))
   {
     size_t entry_len;
@@ -1773,7 +1851,7 @@ static int journal_replay (const char *file) /* {{{ */
 
     entry[entry_len - 1] = '\0';
 
-    if (handle_request(NULL, entry, entry_len) == 0)
+    if (handle_request(NULL, now, entry, entry_len) == 0)
       ++entry_cnt;
     else
       ++fail_cnt;
@@ -1864,6 +1942,7 @@ static void *connection_thread_main (void *args) /* {{{ */
     char *cmd;
     ssize_t cmd_len;
     ssize_t rbytes;
+    time_t now;
 
     struct pollfd pollfd;
     int status;
@@ -1907,9 +1986,14 @@ static void *connection_thread_main (void *args) /* {{{ */
 
     sock->next_read += rbytes;
 
+    if (sock->batch_start)
+      now = sock->batch_start;
+    else
+      now = time(NULL);
+
     while ((cmd = next_cmd(sock, &cmd_len)) != NULL)
     {
-      status = handle_request (sock, cmd, cmd_len+1);
+      status = handle_request (sock, now, cmd, cmd_len+1);
       if (status != 0)
         goto out_close;
     }
@@ -1957,7 +2041,7 @@ static int open_listen_socket_unix (const listen_socket_t *sock) /* {{{ */
       sizeof (listen_fds[0]) * (listen_fds_num + 1));
   if (temp == NULL)
   {
-    RRDD_LOG (LOG_ERR, "open_listen_socket_unix: realloc failed.");
+    fprintf (stderr, "rrdcached: open_listen_socket_unix: realloc failed.\n");
     return (-1);
   }
   listen_fds = temp;
@@ -1966,7 +2050,8 @@ static int open_listen_socket_unix (const listen_socket_t *sock) /* {{{ */
   fd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0);
   if (fd < 0)
   {
-    RRDD_LOG (LOG_ERR, "open_listen_socket_unix: socket(2) failed.");
+    fprintf (stderr, "rrdcached: unix socket(2) failed: %s\n",
+             rrd_strerror(errno));
     return (-1);
   }
 
@@ -1974,19 +2059,26 @@ static int open_listen_socket_unix (const listen_socket_t *sock) /* {{{ */
   sa.sun_family = AF_UNIX;
   strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
 
+  /* if we've gotten this far, we own the pid file.  any daemon started
+   * with the same args must not be alive.  therefore, ensure that we can
+   * create the socket...
+   */
+  unlink(path);
+
   status = bind (fd, (struct sockaddr *) &sa, sizeof (sa));
   if (status != 0)
   {
-    RRDD_LOG (LOG_ERR, "open_listen_socket_unix: bind(2) failed.");
+    fprintf (stderr, "rrdcached: bind(%s) failed: %s.\n",
+             path, rrd_strerror(errno));
     close (fd);
-    unlink (path);
     return (-1);
   }
 
   status = listen (fd, /* backlog = */ 10);
   if (status != 0)
   {
-    RRDD_LOG (LOG_ERR, "open_listen_socket_unix: listen(2) failed.");
+    fprintf (stderr, "rrdcached: listen(%s) failed: %s.\n",
+             path, rrd_strerror(errno));
     close (fd);
     unlink (path);
     return (-1);
@@ -2032,8 +2124,7 @@ static int open_listen_socket_network(const listen_socket_t *sock) /* {{{ */
     port = strchr (addr, ']');
     if (port == NULL)
     {
-      RRDD_LOG (LOG_ERR, "open_listen_socket_network: Malformed address: %s",
-          sock->addr);
+      fprintf (stderr, "rrdcached: Malformed address: %s\n", sock->addr);
       return (-1);
     }
     *port = 0;
@@ -2045,8 +2136,7 @@ static int open_listen_socket_network(const listen_socket_t *sock) /* {{{ */
       port = NULL;
     else
     {
-      RRDD_LOG (LOG_ERR, "open_listen_socket_network: Garbage after address: %s",
-          port);
+      fprintf (stderr, "rrdcached: Garbage after address: %s\n", port);
       return (-1);
     }
   } /* if (*addr = ']') */
@@ -2065,8 +2155,8 @@ static int open_listen_socket_network(const listen_socket_t *sock) /* {{{ */
                         &ai_hints, &ai_res);
   if (status != 0)
   {
-    RRDD_LOG (LOG_ERR, "open_listen_socket_network: getaddrinfo(%s) failed: "
-        "%s", addr, gai_strerror (status));
+    fprintf (stderr, "rrdcached: getaddrinfo(%s) failed: %s\n",
+             addr, gai_strerror (status));
     return (-1);
   }
 
@@ -2080,7 +2170,8 @@ static int open_listen_socket_network(const listen_socket_t *sock) /* {{{ */
         sizeof (listen_fds[0]) * (listen_fds_num + 1));
     if (temp == NULL)
     {
-      RRDD_LOG (LOG_ERR, "open_listen_socket_network: realloc failed.");
+      fprintf (stderr,
+               "rrdcached: open_listen_socket_network: realloc failed.\n");
       continue;
     }
     listen_fds = temp;
@@ -2089,7 +2180,8 @@ static int open_listen_socket_network(const listen_socket_t *sock) /* {{{ */
     fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (fd < 0)
     {
-      RRDD_LOG (LOG_ERR, "open_listen_socket_network: socket(2) failed.");
+      fprintf (stderr, "rrdcached: network socket(2) failed: %s.\n",
+               rrd_strerror(errno));
       continue;
     }
 
@@ -2098,7 +2190,8 @@ static int open_listen_socket_network(const listen_socket_t *sock) /* {{{ */
     status = bind (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
     if (status != 0)
     {
-      RRDD_LOG (LOG_ERR, "open_listen_socket_network: bind(2) failed.");
+      fprintf (stderr, "rrdcached: bind(%s) failed: %s.\n",
+               sock->addr, rrd_strerror(errno));
       close (fd);
       continue;
     }
@@ -2106,7 +2199,8 @@ static int open_listen_socket_network(const listen_socket_t *sock) /* {{{ */
     status = listen (fd, /* backlog = */ 10);
     if (status != 0)
     {
-      RRDD_LOG (LOG_ERR, "open_listen_socket_network: listen(2) failed.");
+      fprintf (stderr, "rrdcached: listen(%s) failed: %s\n.",
+               sock->addr, rrd_strerror(errno));
       close (fd);
       return (-1);
     }
@@ -2157,21 +2251,9 @@ static void *listen_thread_main (void *args __attribute__((unused))) /* {{{ */
   int status;
   int i;
 
-  for (i = 0; i < config_listen_address_list_len; i++)
-    open_listen_socket (config_listen_address_list[i]);
-
-  if (config_listen_address_list_len < 1)
-  {
-    listen_socket_t sock;
-    memset(&sock, 0, sizeof(sock));
-    strncpy(sock.addr, RRDCACHED_DEFAULT_ADDRESS, sizeof(sock.addr));
-    open_listen_socket (&sock);
-  }
-
   if (listen_fds_num < 1)
   {
-    RRDD_LOG (LOG_ERR, "listen_thread_main: No listen sockets "
-        "could be opened. Sorry.");
+    RRDD_LOG(LOG_ERR, "listen_thread_main: no listen_fds !");
     return (NULL);
   }
 
@@ -2284,14 +2366,36 @@ 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;
+
+  /* open all the listen sockets */
+  if (config_listen_address_list_len > 0)
+  {
+    for (int i = 0; i < config_listen_address_list_len; i++)
+      open_listen_socket (config_listen_address_list[i]);
+  }
+  else
+  {
+    listen_socket_t sock;
+    memset(&sock, 0, sizeof(sock));
+    strncpy(sock.addr, RRDCACHED_DEFAULT_ADDRESS, sizeof(sock.addr));
+    open_listen_socket (&sock);
+  }
+
+  if (listen_fds_num < 1)
+  {
+    fprintf (stderr, "rrdcached: FATAL: cannot open any listen sockets\n");
+    goto error;
+  }
 
   if (!stay_foreground)
   {
@@ -2301,12 +2405,10 @@ static int daemonize (void) /* {{{ */
     if (child < 0)
     {
       fprintf (stderr, "daemonize: fork(2) failed.\n");
-      return (-1);
+      goto error;
     }
     else if (child > 0)
-    {
-      return (1);
-    }
+      exit(0);
 
     /* Become session leader */
     setsid ();
@@ -2325,11 +2427,11 @@ static int daemonize (void) /* {{{ */
   base_dir = (config_base_dir != NULL)
     ? config_base_dir
     : "/tmp";
-  status = chdir (base_dir);
-  if (status != 0)
+
+  if (chdir (base_dir) != 0)
   {
     fprintf (stderr, "daemonize: chdir (%s) failed.\n", base_dir);
-    return (-1);
+    goto error;
   }
 
   install_signal_handlers();
@@ -2341,11 +2443,14 @@ static int daemonize (void) /* {{{ */
   if (cache_tree == NULL)
   {
     RRDD_LOG (LOG_ERR, "daemonize: g_tree_new failed.");
-    return (-1);
+    goto error;
   }
 
-  status = write_pidfile (fd);
-  return status;
+  return write_pidfile (pid_fd);
+
+error:
+  remove_pidfile();
+  return -1;
 } /* }}} int daemonize */
 
 static int cleanup (void) /* {{{ */
@@ -2460,6 +2565,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);
@@ -2470,6 +2576,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] == '/'))
         {
@@ -2598,19 +2725,9 @@ int main (int argc, char **argv)
   }
 
   status = daemonize ();
-  if (status == 1)
-  {
-    struct sigaction sigchld;
-
-    memset (&sigchld, 0, sizeof (sigchld));
-    sigchld.sa_handler = SIG_IGN;
-    sigaction (SIGCHLD, &sigchld, NULL);
-
-    return (0);
-  }
-  else if (status != 0)
+  if (status != 0)
   {
-    fprintf (stderr, "daemonize failed, exiting.\n");
+    fprintf (stderr, "rrdcached: daemonize failed, exiting.\n");
     return (1);
   }