Merge branch 'collectd-4.5' into collectd-4.6
[collectd.git] / src / common.c
index d946cfb..1af2f14 100644 (file)
 /**
  * collectd - src/common.c
- * Copyright (C) 2005  Florian octo Forster
+ * Copyright (C) 2005-2008  Florian octo Forster
  *
- * This program is free software; you can redistribute it and/
- * or modify it under the terms of the GNU General Public Li-
- * cence as published by the Free Software Foundation; either
- * version 2 of the Licence, or any later version.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
  *
- * This program is distributed in the hope that it will be use-
- * ful, but WITHOUT ANY WARRANTY; without even the implied war-
- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public Licence for more details.
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
  *
- * You should have received a copy of the GNU General Public
- * Licence along with this program; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
- * USA.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
  *   Niki W. Waibel <niki.waibel@gmx.net>
+ *   Sebastian Harl <sh at tokkee.org>
+ *   Michał Mirosław <mirq-linux at rere.qmqm.pl>
 **/
 
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "collectd.h"
 #include "common.h"
-#include "utils_debug.h"
+#include "plugin.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+#ifdef HAVE_MATH_H
+# include <math.h>
+#endif
+
+/* for ntohl and htonl */
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
 
 #ifdef HAVE_LIBKSTAT
 extern kstat_ctl_t *kc;
 #endif
 
-#ifdef HAVE_LIBRRD
-static char *rra_def[] =
+#if !HAVE_GETPWNAM_R
+static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+#if !HAVE_STRERROR_R
+static pthread_mutex_t strerror_r_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+char *sstrncpy (char *dest, const char *src, size_t n)
+{
+       strncpy (dest, src, n);
+       dest[n - 1] = '\0';
+
+       return (dest);
+} /* char *sstrncpy */
+
+int ssnprintf (char *dest, size_t n, const char *format, ...)
+{
+       int ret = 0;
+       va_list ap;
+
+       va_start (ap, format);
+       ret = vsnprintf (dest, n, format, ap);
+       dest[n - 1] = '\0';
+       va_end (ap);
+
+       return (ret);
+} /* int ssnprintf */
+
+char *sstrdup (const char *s)
+{
+       char *r;
+       size_t sz;
+
+       if (s == NULL)
+               return (NULL);
+
+       /* Do not use `strdup' here, because it's not specified in POSIX. It's
+        * ``only'' an XSI extension. */
+       sz = strlen (s) + 1;
+       r = (char *) malloc (sizeof (char) * sz);
+       if (r == NULL)
+       {
+               ERROR ("sstrdup: Out of memory.");
+               exit (3);
+       }
+       memcpy (r, s, sizeof (char) * sz);
+
+       return (r);
+} /* char *sstrdup */
+
+/* Even though Posix requires "strerror_r" to return an "int",
+ * some systems (e.g. the GNU libc) return a "char *" _and_
+ * ignore the second argument ... -tokkee */
+char *sstrerror (int errnum, char *buf, size_t buflen)
+{
+       buf[0] = '\0';
+
+#if !HAVE_STRERROR_R
+       {
+               char *temp;
+
+               pthread_mutex_lock (&strerror_r_lock);
+
+               temp = strerror (errnum);
+               sstrncpy (buf, temp, buflen);
+
+               pthread_mutex_unlock (&strerror_r_lock);
+       }
+/* #endif !HAVE_STRERROR_R */
+
+#elif STRERROR_R_CHAR_P
+       {
+               char *temp;
+               temp = strerror_r (errnum, buf, buflen);
+               if (buf[0] == '\0')
+               {
+                       if ((temp != NULL) && (temp != buf) && (temp[0] != '\0'))
+                               sstrncpy (buf, temp, buflen);
+                       else
+                               sstrncpy (buf, "strerror_r did not return "
+                                               "an error message", buflen);
+               }
+       }
+/* #endif STRERROR_R_CHAR_P */
+
+#else
+       if (strerror_r (errnum, buf, buflen) != 0)
+       {
+               ssnprintf (buf, buflen, "Error #%i; "
+                               "Additionally, strerror_r failed.",
+                               errnum);
+       }
+#endif /* STRERROR_R_CHAR_P */
+
+       return (buf);
+} /* char *sstrerror */
+
+void *smalloc (size_t size)
 {
-               "RRA:AVERAGE:0.2:6:1500",
-               "RRA:AVERAGE:0.1:180:1680",
-               "RRA:AVERAGE:0.1:2160:1520",
-               "RRA:MIN:0.2:6:1500",
-               "RRA:MIN:0.1:180:1680",
-               "RRA:MIN:0.1:2160:1520",
-               "RRA:MAX:0.2:6:1500",
-               "RRA:MAX:0.1:180:1680",
-               "RRA:MAX:0.1:2160:1520",
-               NULL
-};
-static int rra_num = 9;
-#endif /* HAVE_LIBRRD */
-
-void
-sstrncpy(char *d, const char *s, int len)
+       void *r;
+
+       if ((r = malloc (size)) == NULL)
+       {
+               ERROR ("Not enough memory.");
+               exit (3);
+       }
+
+       return (r);
+} /* void *smalloc */
+
+#if 0
+void sfree (void **ptr)
 {
-       strncpy(d, s, len);
-       d[len - 1] = 0;
+       if (ptr == NULL)
+               return;
+
+       if (*ptr != NULL)
+               free (*ptr);
+
+       *ptr = NULL;
 }
+#endif
 
-char *
-sstrdup(const char *s)
+ssize_t sread (int fd, void *buf, size_t count)
 {
-       char *r = strdup(s);
-       if(r == NULL) {
-               DBG("Not enough memory.");
-               exit(3);
+       char    *ptr;
+       size_t   nleft;
+       ssize_t  status;
+
+       ptr   = (char *) buf;
+       nleft = count;
+
+       while (nleft > 0)
+       {
+               status = read (fd, (void *) ptr, nleft);
+
+               if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
+                       continue;
+
+               if (status < 0)
+                       return (status);
+
+               if (status == 0)
+               {
+                       DEBUG ("Received EOF from fd %i. "
+                                       "Closing fd and returning error.",
+                                       fd);
+                       close (fd);
+                       return (-1);
+               }
+
+               assert ((0 > status) || (nleft >= (size_t)status));
+
+               nleft = nleft - status;
+               ptr   = ptr   + status;
        }
-       return r;
+
+       return (0);
 }
 
-void *
-smalloc(size_t size)
+
+ssize_t swrite (int fd, const void *buf, size_t count)
 {
-       void *r = malloc(size);
-       if(r == NULL) {
-               DBG("Not enough memory.");
-               exit(3);
+       const char *ptr;
+       size_t      nleft;
+       ssize_t     status;
+
+       ptr   = (const char *) buf;
+       nleft = count;
+
+       while (nleft > 0)
+       {
+               status = write (fd, (const void *) ptr, nleft);
+
+               if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
+                       continue;
+
+               if (status < 0)
+                       return (status);
+
+               nleft = nleft - status;
+               ptr   = ptr   + status;
        }
-       return r;
+
+       return (0);
 }
 
 int strsplit (char *string, char **fields, size_t size)
 {
        size_t i;
        char *ptr;
+       char *saveptr;
 
        i = 0;
        ptr = string;
-       while ((fields[i] = strtok (ptr, " \t")) != NULL)
+       saveptr = NULL;
+       while ((fields[i] = strtok_r (ptr, " \t\r\n", &saveptr)) != NULL)
        {
                ptr = NULL;
                i++;
@@ -94,156 +256,301 @@ int strsplit (char *string, char **fields, size_t size)
        return (i);
 }
 
-#ifdef HAVE_LIBRRD
-int check_create_dir (char *dir)
+int strjoin (char *dst, size_t dst_len,
+               char **fields, size_t fields_num,
+               const char *sep)
 {
-       struct stat statbuf;
+       size_t field_len;
+       size_t sep_len;
+       int i;
+
+       memset (dst, '\0', dst_len);
 
-       if (stat (dir, &statbuf) == -1)
+       if (fields_num <= 0)
+               return (-1);
+
+       sep_len = 0;
+       if (sep != NULL)
+               sep_len = strlen (sep);
+
+       for (i = 0; i < (int)fields_num; i++)
        {
-               if (errno == ENOENT)
+               if ((i > 0) && (sep_len > 0))
                {
-                       if (mkdir (dir, 0755) == -1)
-                       {
-                               syslog (LOG_ERR, "mkdir %s: %s", dir, strerror (errno));
+                       if (dst_len <= sep_len)
                                return (-1);
-                       }
+
+                       strncat (dst, sep, dst_len);
+                       dst_len -= sep_len;
                }
-               else
-               {
-                       syslog (LOG_ERR, "stat %s: %s", dir, strerror (errno));
+
+               field_len = strlen (fields[i]);
+
+               if (dst_len <= field_len)
                        return (-1);
-               }
-       }
-       else if (!S_ISDIR (statbuf.st_mode))
-       {
-               syslog (LOG_ERR, "stat %s: Not a directory!", dir);
-               return (-1);
+
+               strncat (dst, fields[i], dst_len);
+               dst_len -= field_len;
        }
 
-       return (0);
+       return (strlen (dst));
 }
 
-int rrd_create_file (char *filename, char **ds_def, int ds_num)
+int strsubstitute (char *str, char c_from, char c_to)
 {
-       char **argv;
-       int argc;
-       int i, j;
-       int status = 0;
+       int ret;
 
-       argc = ds_num + rra_num + 4;
+       if (str == NULL)
+               return (-1);
 
-       if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
+       ret = 0;
+       while (*str != '\0')
        {
-               syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno));
-               return (-1);
+               if (*str == c_from)
+               {
+                       *str = c_to;
+                       ret++;
+               }
+               str++;
        }
 
-       argv[0] = "create";
-       argv[1] = filename;
-       argv[2] = "-s";
-       argv[3] = "10";
+       return (ret);
+} /* int strsubstitute */
 
-       j = 4;
-       for (i = 0; i < ds_num; i++)
-               argv[j++] = ds_def[i];
-       for (i = 0; i < rra_num; i++)
-               argv[j++] = rra_def[i];
-       argv[j] = NULL;
+int escape_slashes (char *buf, int buf_len)
+{
+       int i;
 
-       optind = 0; /* bug in librrd? */
-       rrd_clear_error ();
-       if (rrd_create (argc, argv) == -1)
+       if (strcmp (buf, "/") == 0)
        {
-               syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ());
-               status = -1;
+               if (buf_len < 5)
+                       return (-1);
+
+               strncpy (buf, "root", buf_len);
+               return (0);
        }
 
-       free (argv);
-       
-       return (status);
-}
-#endif /* HAVE_LIBRRD */
+       if (buf_len <= 1)
+               return (0);
 
-int rrd_update_file (char *host, char *file, char *values,
-               char **ds_def, int ds_num)
+       /* Move one to the left */
+       if (buf[0] == '/')
+               memmove (buf, buf + 1, buf_len - 1);
+
+       for (i = 0; i < buf_len - 1; i++)
+       {
+               if (buf[i] == '\0')
+                       break;
+               else if (buf[i] == '/')
+                       buf[i] = '_';
+       }
+       buf[i] = '\0';
+
+       return (0);
+} /* int escape_slashes */
+
+int timeval_cmp (struct timeval tv0, struct timeval tv1, struct timeval *delta)
 {
-#ifdef HAVE_LIBRRD
-       struct stat statbuf;
-       char full_file[1024];
-       char *argv[4] = { "update", full_file, values, NULL };
+       struct timeval *larger;
+       struct timeval *smaller;
 
-       /* host == NULL => local mode */
-       if (host != NULL)
+       int status;
+
+       NORMALIZE_TIMEVAL (tv0);
+       NORMALIZE_TIMEVAL (tv1);
+
+       if ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec == tv1.tv_usec))
        {
-               if (check_create_dir (host))
-                       return (-1);
+               if (delta != NULL) {
+                       delta->tv_sec  = 0;
+                       delta->tv_usec = 0;
+               }
+               return (0);
+       }
 
-               if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
-                       return (-1);
+       if ((tv0.tv_sec < tv1.tv_sec)
+                       || ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec < tv1.tv_usec)))
+       {
+               larger  = &tv1;
+               smaller = &tv0;
+               status  = -1;
        }
        else
        {
-               if (snprintf (full_file, 1024, "%s", file) >= 1024)
-                       return (-1);
+               larger  = &tv0;
+               smaller = &tv1;
+               status  = 1;
        }
 
-       if (stat (full_file, &statbuf) == -1)
-       {
-               if (errno == ENOENT)
-               {
-                       if (rrd_create_file (full_file, ds_def, ds_num))
-                               return (-1);
-               }
+       if (delta != NULL) {
+               delta->tv_sec = larger->tv_sec - smaller->tv_sec;
+
+               if (smaller->tv_usec <= larger->tv_usec)
+                       delta->tv_usec = larger->tv_usec - smaller->tv_usec;
                else
                {
-                       syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
-                       return (-1);
+                       --delta->tv_sec;
+                       delta->tv_usec = 1000000 + larger->tv_usec - smaller->tv_usec;
                }
        }
-       else if (!S_ISREG (statbuf.st_mode))
-       {
-               syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
+
+       assert ((delta == NULL)
+                       || ((0 <= delta->tv_usec) && (delta->tv_usec < 1000000)));
+
+       return (status);
+} /* int timeval_cmp */
+
+int check_create_dir (const char *file_orig)
+{
+       struct stat statbuf;
+
+       char  file_copy[512];
+       char  dir[512];
+       int   dir_len = 512;
+       char *fields[16];
+       int   fields_num;
+       char *ptr;
+       char *saveptr;
+       int   last_is_file = 1;
+       int   path_is_absolute = 0;
+       size_t len;
+       int   i;
+
+       /*
+        * Sanity checks first
+        */
+       if (file_orig == NULL)
+               return (-1);
+
+       if ((len = strlen (file_orig)) < 1)
+               return (-1);
+       else if (len >= sizeof (file_copy))
                return (-1);
+
+       /*
+        * If `file_orig' ends in a slash the last component is a directory,
+        * otherwise it's a file. Act accordingly..
+        */
+       if (file_orig[len - 1] == '/')
+               last_is_file = 0;
+       if (file_orig[0] == '/')
+               path_is_absolute = 1;
+
+       /*
+        * Create a copy for `strtok_r' to destroy
+        */
+       sstrncpy (file_copy, file_orig, sizeof (file_copy));
+
+       /*
+        * Break into components. This will eat up several slashes in a row and
+        * remove leading and trailing slashes..
+        */
+       ptr = file_copy;
+       saveptr = NULL;
+       fields_num = 0;
+       while ((fields[fields_num] = strtok_r (ptr, "/", &saveptr)) != NULL)
+       {
+               ptr = NULL;
+               fields_num++;
+
+               if (fields_num >= 16)
+                       break;
        }
 
-       optind = 0; /* bug in librrd? */
-       rrd_clear_error ();
-       if (rrd_update (3, argv) == -1)
+       /*
+        * For each component, do..
+        */
+       for (i = 0; i < (fields_num - last_is_file); i++)
        {
-               syslog (LOG_WARNING, "rrd_update failed: %s: %s", full_file, rrd_get_error ());
-               return (-1);
+               /*
+                * Do not create directories that start with a dot. This
+                * prevents `../../' attacks and other likely malicious
+                * behavior.
+                */
+               if (fields[i][0] == '.')
+               {
+                       ERROR ("Cowardly refusing to create a directory that "
+                                       "begins with a `.' (dot): `%s'", file_orig);
+                       return (-2);
+               }
+
+               /*
+                * Join the components together again
+                */
+               dir[0] = '/';
+               if (strjoin (dir + path_is_absolute, dir_len - path_is_absolute,
+                                       fields, i + 1, "/") < 0)
+               {
+                       ERROR ("strjoin failed: `%s', component #%i", file_orig, i);
+                       return (-1);
+               }
+
+               while (42) {
+                       if (stat (dir, &statbuf) == -1)
+                       {
+                               if (errno == ENOENT)
+                               {
+                                       if (mkdir (dir, 0755) == 0)
+                                               break;
+
+                                       /* this might happen, if a different thread created
+                                        * the directory in the meantime
+                                        * => call stat() again to check for S_ISDIR() */
+                                       if (EEXIST == errno)
+                                               continue;
+
+                                       char errbuf[1024];
+                                       ERROR ("check_create_dir: mkdir (%s): %s", dir,
+                                                       sstrerror (errno,
+                                                               errbuf, sizeof (errbuf)));
+                                       return (-1);
+                               }
+                               else
+                               {
+                                       char errbuf[1024];
+                                       ERROR ("check_create_dir: stat (%s): %s", dir,
+                                                       sstrerror (errno, errbuf,
+                                                               sizeof (errbuf)));
+                                       return (-1);
+                               }
+                       }
+                       else if (!S_ISDIR (statbuf.st_mode))
+                       {
+                               ERROR ("check_create_dir: `%s' exists but is not "
+                                               "a directory!", dir);
+                               return (-1);
+                       }
+                       break;
+               }
        }
-#endif /* HAVE_LIBRRD */
 
        return (0);
-}
+} /* check_create_dir */
 
 #ifdef HAVE_LIBKSTAT
 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
 {
        char ident[128];
+
+       *ksp_ptr = NULL;
        
        if (kc == NULL)
                return (-1);
 
-       snprintf (ident, 128, "%s,%i,%s", module, instance, name);
-       ident[127] = '\0';
+       ssnprintf (ident, sizeof (ident), "%s,%i,%s", module, instance, name);
 
+       *ksp_ptr = kstat_lookup (kc, module, instance, name);
        if (*ksp_ptr == NULL)
        {
-               if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
-               {
-                       syslog (LOG_ERR, "Cound not find kstat %s", ident);
-                       return (-1);
-               }
+               ERROR ("get_kstat: Cound not find kstat %s", ident);
+               return (-1);
+       }
 
-               if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
-               {
-                       syslog (LOG_WARNING, "kstat %s has wrong type", ident);
-                       *ksp_ptr = NULL;
-                       return (-1);
-               }
+       if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
+       {
+               ERROR ("get_kstat: kstat %s has wrong type", ident);
+               *ksp_ptr = NULL;
+               return (-1);
        }
 
 #ifdef assert
@@ -253,13 +560,13 @@ int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
 
        if (kstat_read (kc, *ksp_ptr, NULL) == -1)
        {
-               syslog (LOG_WARNING, "kstat %s could not be read", ident);
+               ERROR ("get_kstat: kstat %s could not be read", ident);
                return (-1);
        }
 
        if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
        {
-               syslog (LOG_WARNING, "kstat %s has wrong type", ident);
+               ERROR ("get_kstat: kstat %s has wrong type", ident);
                return (-1);
        }
 
@@ -277,12 +584,12 @@ long long get_kstat_value (kstat_t *ksp, char *name)
 #else
        if (ksp == NULL)
        {
-               fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
+               ERROR ("ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
                return (-1LL);
        }
        else if (ksp->ks_type != KSTAT_TYPE_NAMED)
        {
-               fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
+               ERROR ("ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
                return (-1LL);
        }
 #endif
@@ -299,8 +606,382 @@ long long get_kstat_value (kstat_t *ksp, char *name)
        else if (kn->data_type == KSTAT_DATA_UINT64)
                retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
        else
-               syslog (LOG_WARNING, "get_kstat_value: Not a numeric value: %s", name);
+               WARNING ("get_kstat_value: Not a numeric value: %s", name);
                 
        return (retval);
 }
 #endif /* HAVE_LIBKSTAT */
+
+unsigned long long ntohll (unsigned long long n)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+       return (n);
+#else
+       return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32);
+#endif
+} /* unsigned long long ntohll */
+
+unsigned long long htonll (unsigned long long n)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+       return (n);
+#else
+       return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
+#endif
+} /* unsigned long long htonll */
+
+#if FP_LAYOUT_NEED_NOTHING
+/* Well, we need nothing.. */
+/* #endif FP_LAYOUT_NEED_NOTHING */
+
+#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
+# if FP_LAYOUT_NEED_ENDIANFLIP
+#  define FP_CONVERT(A) ((((uint64_t)(A) & 0xff00000000000000LL) >> 56) | \
+                         (((uint64_t)(A) & 0x00ff000000000000LL) >> 40) | \
+                         (((uint64_t)(A) & 0x0000ff0000000000LL) >> 24) | \
+                         (((uint64_t)(A) & 0x000000ff00000000LL) >> 8)  | \
+                         (((uint64_t)(A) & 0x00000000ff000000LL) << 8)  | \
+                         (((uint64_t)(A) & 0x0000000000ff0000LL) << 24) | \
+                         (((uint64_t)(A) & 0x000000000000ff00LL) << 40) | \
+                         (((uint64_t)(A) & 0x00000000000000ffLL) << 56))
+# else
+#  define FP_CONVERT(A) ((((uint64_t)(A) & 0xffffffff00000000LL) >> 32) | \
+                         (((uint64_t)(A) & 0x00000000ffffffffLL) << 32))
+# endif
+
+double ntohd (double d)
+{
+       union
+       {
+               uint8_t  byte[8];
+               uint64_t integer;
+               double   floating;
+       } ret;
+
+       ret.floating = d;
+
+       /* NAN in x86 byte order */
+       if ((ret.byte[0] == 0x00) && (ret.byte[1] == 0x00)
+                       && (ret.byte[2] == 0x00) && (ret.byte[3] == 0x00)
+                       && (ret.byte[4] == 0x00) && (ret.byte[5] == 0x00)
+                       && (ret.byte[6] == 0xf8) && (ret.byte[7] == 0x7f))
+       {
+               return (NAN);
+       }
+       else
+       {
+               uint64_t tmp;
+
+               tmp = ret.integer;
+               ret.integer = FP_CONVERT (tmp);
+               return (ret.floating);
+       }
+} /* double ntohd */
+
+double htond (double d)
+{
+       union
+       {
+               uint8_t  byte[8];
+               uint64_t integer;
+               double   floating;
+       } ret;
+
+       if (isnan (d))
+       {
+               ret.byte[0] = ret.byte[1] = ret.byte[2] = ret.byte[3] = 0x00;
+               ret.byte[4] = ret.byte[5] = 0x00;
+               ret.byte[6] = 0xf8;
+               ret.byte[7] = 0x7f;
+               return (ret.floating);
+       }
+       else
+       {
+               uint64_t tmp;
+
+               ret.floating = d;
+               tmp = FP_CONVERT (ret.integer);
+               ret.integer = tmp;
+               return (ret.floating);
+       }
+} /* double htond */
+#endif /* FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP */
+
+int format_name (char *ret, int ret_len,
+               const char *hostname,
+               const char *plugin, const char *plugin_instance,
+               const char *type, const char *type_instance)
+{
+       int  status;
+
+       assert (plugin != NULL);
+       assert (type != NULL);
+
+       if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0))
+       {
+               if ((type_instance == NULL) || (strlen (type_instance) == 0))
+                       status = ssnprintf (ret, ret_len, "%s/%s/%s",
+                                       hostname, plugin, type);
+               else
+                       status = ssnprintf (ret, ret_len, "%s/%s/%s-%s",
+                                       hostname, plugin, type,
+                                       type_instance);
+       }
+       else
+       {
+               if ((type_instance == NULL) || (strlen (type_instance) == 0))
+                       status = ssnprintf (ret, ret_len, "%s/%s-%s/%s",
+                                       hostname, plugin, plugin_instance,
+                                       type);
+               else
+                       status = ssnprintf (ret, ret_len, "%s/%s-%s/%s-%s",
+                                       hostname, plugin, plugin_instance,
+                                       type, type_instance);
+       }
+
+       if ((status < 1) || (status >= ret_len))
+               return (-1);
+       return (0);
+} /* int format_name */
+
+int parse_identifier (char *str, char **ret_host,
+               char **ret_plugin, char **ret_plugin_instance,
+               char **ret_type, char **ret_type_instance)
+{
+       char *hostname = NULL;
+       char *plugin = NULL;
+       char *plugin_instance = NULL;
+       char *type = NULL;
+       char *type_instance = NULL;
+
+       hostname = str;
+       if (hostname == NULL)
+               return (-1);
+
+       plugin = strchr (hostname, '/');
+       if (plugin == NULL)
+               return (-1);
+       *plugin = '\0'; plugin++;
+
+       type = strchr (plugin, '/');
+       if (type == NULL)
+               return (-1);
+       *type = '\0'; type++;
+
+       plugin_instance = strchr (plugin, '-');
+       if (plugin_instance != NULL)
+       {
+               *plugin_instance = '\0';
+               plugin_instance++;
+       }
+
+       type_instance = strchr (type, '-');
+       if (type_instance != NULL)
+       {
+               *type_instance = '\0';
+               type_instance++;
+       }
+
+       *ret_host = hostname;
+       *ret_plugin = plugin;
+       *ret_plugin_instance = plugin_instance;
+       *ret_type = type;
+       *ret_type_instance = type_instance;
+       return (0);
+} /* int parse_identifier */
+
+int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
+{
+       int i;
+       char *dummy;
+       char *ptr;
+       char *saveptr;
+
+       i = -1;
+       dummy = buffer;
+       saveptr = NULL;
+       while ((ptr = strtok_r (dummy, ":", &saveptr)) != NULL)
+       {
+               dummy = NULL;
+
+               if (i >= vl->values_len)
+                       break;
+
+               if (i == -1)
+               {
+                       if (strcmp ("N", ptr) == 0)
+                               vl->time = time (NULL);
+                       else
+                               vl->time = (time_t) atoi (ptr);
+               }
+               else
+               {
+                       if (strcmp ("U", ptr) == 0)
+                               vl->values[i].gauge = NAN;
+                       else if (ds->ds[i].type == DS_TYPE_COUNTER)
+                               vl->values[i].counter = atoll (ptr);
+                       else if (ds->ds[i].type == DS_TYPE_GAUGE)
+                               vl->values[i].gauge = atof (ptr);
+               }
+
+               i++;
+       } /* while (strtok_r) */
+
+       if ((ptr != NULL) || (i != vl->values_len))
+               return (-1);
+       return (0);
+} /* int parse_values */
+
+#if !HAVE_GETPWNAM_R
+int getpwnam_r (const char *name, struct passwd *pwbuf, char *buf,
+               size_t buflen, struct passwd **pwbufp)
+{
+       int status = 0;
+       struct passwd *pw;
+
+       memset (pwbuf, '\0', sizeof (struct passwd));
+
+       pthread_mutex_lock (&getpwnam_r_lock);
+
+       do
+       {
+               pw = getpwnam (name);
+               if (pw == NULL)
+               {
+                       status = (errno != 0) ? errno : ENOENT;
+                       break;
+               }
+
+#define GETPWNAM_COPY_MEMBER(member) \
+               if (pw->member != NULL) \
+               { \
+                       int len = strlen (pw->member); \
+                       if (len >= buflen) \
+                       { \
+                               status = ENOMEM; \
+                               break; \
+                       } \
+                       sstrncpy (buf, pw->member, buflen); \
+                       pwbuf->member = buf; \
+                       buf    += (len + 1); \
+                       buflen -= (len + 1); \
+               }
+               GETPWNAM_COPY_MEMBER(pw_name);
+               GETPWNAM_COPY_MEMBER(pw_passwd);
+               GETPWNAM_COPY_MEMBER(pw_gecos);
+               GETPWNAM_COPY_MEMBER(pw_dir);
+               GETPWNAM_COPY_MEMBER(pw_shell);
+
+               pwbuf->pw_uid = pw->pw_uid;
+               pwbuf->pw_gid = pw->pw_gid;
+
+               if (pwbufp != NULL)
+                       *pwbufp = pwbuf;
+       } while (0);
+
+       pthread_mutex_unlock (&getpwnam_r_lock);
+
+       return (status);
+} /* int getpwnam_r */
+#endif /* !HAVE_GETPWNAM_R */
+
+int notification_init (notification_t *n, int severity, const char *message,
+               const char *host,
+               const char *plugin, const char *plugin_instance,
+               const char *type, const char *type_instance)
+{
+       memset (n, '\0', sizeof (notification_t));
+
+       n->severity = severity;
+
+       if (message != NULL)
+               sstrncpy (n->message, message, sizeof (n->message));
+       if (host != NULL)
+               sstrncpy (n->host, host, sizeof (n->host));
+       if (plugin != NULL)
+               sstrncpy (n->plugin, plugin, sizeof (n->plugin));
+       if (plugin_instance != NULL)
+               sstrncpy (n->plugin_instance, plugin_instance,
+                               sizeof (n->plugin_instance));
+       if (type != NULL)
+               sstrncpy (n->type, type, sizeof (n->type));
+       if (type_instance != NULL)
+               sstrncpy (n->type_instance, type_instance,
+                               sizeof (n->type_instance));
+
+       return (0);
+} /* int notification_init */
+
+int walk_directory (const char *dir, dirwalk_callback_f callback,
+               void *user_data)
+{
+       struct dirent *ent;
+       DIR *dh;
+       int success;
+       int failure;
+
+       success = 0;
+       failure = 0;
+
+       if ((dh = opendir (dir)) == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("walk_directory: Cannot open '%s': %s", dir,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return -1;
+       }
+
+       while ((ent = readdir (dh)) != NULL)
+       {
+               int status;
+
+               if (ent->d_name[0] == '.')
+                       continue;
+
+               status = (*callback) (dir, ent->d_name, user_data);
+               if (status != 0)
+                       failure++;
+               else
+                       success++;
+       }
+
+       closedir (dh);
+
+       if ((success == 0) && (failure > 0))
+               return (-1);
+       return (0);
+}
+
+int read_file_contents (const char *filename, char *buf, int bufsize)
+{
+       FILE *fh;
+       int n;
+
+       if ((fh = fopen (filename, "r")) == NULL)
+               return -1;
+
+       n = fread(buf, 1, bufsize, fh);
+       fclose(fh);
+
+       return n;
+}
+
+counter_t counter_diff (counter_t old_value, counter_t new_value)
+{
+       counter_t diff;
+
+       if (old_value > new_value)
+       {
+               if (old_value <= 4294967295U)
+                       diff = (4294967295U - old_value) + new_value;
+               else
+                       diff = (18446744073709551615ULL - old_value)
+                               + new_value;
+       }
+       else
+       {
+               diff = new_value - old_value;
+       }
+
+       return (diff);
+} /* counter_t counter_to_gauge */