Merge branch 'pull/collectd-4'
authorFlorian Forster <octo@crystal.wlan.home.verplant.org>
Wed, 31 Jan 2007 12:48:21 +0000 (13:48 +0100)
committerFlorian Forster <octo@crystal.wlan.home.verplant.org>
Wed, 31 Jan 2007 12:48:21 +0000 (13:48 +0100)
configure.in
src/Makefile.am
src/battery.c
src/collectd-nagios.c [new file with mode: 0644]
src/cpu.c
src/cpufreq.c
src/df.c
src/disk.c
src/plugin.c
src/plugin.h
src/unixsock.c [new file with mode: 0644]

index 0b0aa89..23f0e59 100644 (file)
@@ -1005,6 +1005,7 @@ AC_COLLECTD([serial],    [disable], [module], [serial statistics])
 AC_COLLECTD([swap],      [disable], [module], [swap statistics])
 AC_COLLECTD([tape],      [disable], [module], [tape statistics])
 AC_COLLECTD([traffic],   [disable], [module], [system traffic statistics])
+AC_COLLECTD([unixsock],  [disable], [module], [UNIX socket plugin])
 AC_COLLECTD([users],     [disable], [module], [user count statistics])
 AC_COLLECTD([vserver],   [disable], [module], [vserver statistics])
 AC_COLLECTD([wireless],  [disable], [module], [wireless link statistics])
@@ -1061,6 +1062,7 @@ Configuration:
     swap  . . . . . . . $enable_swap
     tape  . . . . . . . $enable_tape
     traffic . . . . . . $enable_traffic
+    unixsock  . . . . . $enable_unixsock
     users . . . . . . . $enable_users
     vserver . . . . . . $enable_vserver
     wireless  . . . . . $enable_wireless
index 87dcec0..1efdb1a 100644 (file)
@@ -8,6 +8,7 @@ AM_CFLAGS = -Wall -Werror
 endif
 
 sbin_PROGRAMS = collectd
+bin_PROGRAMS = collectd-nagios
 
 collectd_SOURCES = collectd.c collectd.h \
                   utils_debug.c utils_debug.h \
@@ -18,6 +19,7 @@ collectd_SOURCES = collectd.c collectd.h \
                   plugin.c plugin.h \
                   configfile.c configfile.h
 collectd_CPPFLAGS = $(LTDLINCL)
+collectd_CPPFLAGS += -DPREFIX='"${prefix}"'
 collectd_CPPFLAGS += -DCONFIGFILE='"${sysconfdir}/${PACKAGE_NAME}.conf"'
 collectd_CPPFLAGS += -DPKGLOCALSTATEDIR='"${localstatedir}/lib/${PACKAGE_NAME}"'
 if BUILD_FEATURE_DAEMON
@@ -60,6 +62,8 @@ endif
 collectd_LDADD = $(LIBLTDL) libconfig/libconfig.la "-dlopen" self
 collectd_DEPENDENCIES = $(LIBLTDL) libconfig/libconfig.la
 
+collectd_nagios_SOURCES = collectd-nagios.c
+
 pkglib_LTLIBRARIES = 
 
 if BUILD_MODULE_APACHE
@@ -430,6 +434,17 @@ traffic_la_LDFLAGS += -lstatgrab
 endif
 endif
 
+if BUILD_MODULE_UNIXSOCK
+pkglib_LTLIBRARIES += unixsock.la
+unixsock_la_SOURCES = unixsock.c
+unixsock_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBPTHREAD
+unixsock_la_LDFLAGS += -lpthread
+endif
+collectd_LDADD += "-dlopen" unixsock.la
+collectd_DEPENDENCIES += unixsock.la
+endif
+
 if BUILD_MODULE_USERS
 pkglib_LTLIBRARIES += users.la
 users_la_SOURCES = users.c
index 2141a58..64ff994 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/battery.c
- * Copyright (C) 2006  Florian octo Forster
+ * Copyright (C) 2006,2007  Florian octo Forster
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * Free Software Foundation; only version 2 of the License is applicable.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,9 +24,6 @@
 #include "plugin.h"
 #include "utils_debug.h"
 
-#define MODULE_NAME "battery"
-#define BUFSIZE 512
-
 #if HAVE_MACH_MACH_TYPES_H
 #  include <mach/mach_types.h>
 #endif
 
 #define INVALID_VALUE 47841.29
 
-static char *battery_current_file = "battery-%s/current.rrd";
-static char *battery_voltage_file = "battery-%s/voltage.rrd";
-static char *battery_charge_file  = "battery-%s/charge.rrd";
+static data_source_t data_source_charge[1] =
+{
+       {"charge", DS_TYPE_GAUGE, 0, NAN}
+};
+
+static data_set_t charge_ds =
+{
+       "charge", 1, data_source_charge
+};
+
+static data_source_t data_source_current[1] =
+{
+       {"current", DS_TYPE_GAUGE, NAN, NAN}
+};
 
-static char *ds_def_current[] =
+static data_set_t current_ds =
 {
-       "DS:current:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
+       "current", 1, data_source_current
 };
-static int ds_num_current = 1;
 
-static char *ds_def_voltage[] =
+static data_source_t data_source_voltage[1] =
 {
-       "DS:voltage:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
+       {"voltage", DS_TYPE_GAUGE, NAN, NAN}
 };
-static int ds_num_voltage = 1;
 
-static char *ds_def_charge[] =
+static data_set_t voltage_ds =
 {
-       "DS:charge:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
+       "voltage", 1, data_source_voltage
 };
-static int ds_num_charge = 1;
 
+#if BATTERY_HAVE_READ
 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
        /* No global variables */
 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
@@ -95,7 +97,7 @@ static int   battery_pmu_num = 0;
 static char *battery_pmu_file = "/proc/pmu/battery_%i";
 #endif /* KERNEL_LINUX */
 
-static void battery_init (void)
+static int battery_init (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
        /* No init neccessary */
@@ -103,13 +105,13 @@ static void battery_init (void)
 
 #elif KERNEL_LINUX
        int len;
-       char filename[BUFSIZE];
+       char filename[128];
 
        for (battery_pmu_num = 0; ; battery_pmu_num++)
        {
-               len = snprintf (filename, BUFSIZE, battery_pmu_file, battery_pmu_num);
+               len = snprintf (filename, sizeof (filename), battery_pmu_file, battery_pmu_num);
 
-               if ((len >= BUFSIZE) || (len < 0))
+               if ((len >= sizeof (filename)) || (len < 0))
                        break;
 
                if (access (filename, R_OK))
@@ -117,90 +119,25 @@ static void battery_init (void)
        }
 #endif /* KERNEL_LINUX */
 
-       return;
-}
-
-static void battery_current_write (char *host, char *inst, char *val)
-{
-       char filename[BUFSIZE];
-       int len;
-
-       len = snprintf (filename, BUFSIZE, battery_current_file, inst);
-       if ((len >= BUFSIZE) || (len < 0))
-               return;
-
-       rrd_update_file (host, filename, val,
-                       ds_def_current, ds_num_current);
-}
-
-static void battery_voltage_write (char *host, char *inst, char *val)
-{
-       char filename[BUFSIZE];
-       int len;
-
-       len = snprintf (filename, BUFSIZE, battery_voltage_file, inst);
-       if ((len >= BUFSIZE) || (len < 0))
-               return;
-
-       rrd_update_file (host, filename, val,
-                       ds_def_voltage, ds_num_voltage);
+       return (0);
 }
 
-static void battery_charge_write (char *host, char *inst, char *val)
+static void battery_submit (const char *plugin_instance, const char *type, double value)
 {
-       char filename[BUFSIZE];
-       int len;
-
-       len = snprintf (filename, BUFSIZE, battery_charge_file, inst);
-       if ((len >= BUFSIZE) || (len < 0))
-               return;
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       rrd_update_file (host, filename, val,
-                       ds_def_charge, ds_num_charge);
-}
+       values[0].gauge = value;
 
-#if BATTERY_HAVE_READ
-static void battery_submit (char *inst, double current, double voltage, double charge)
-{
-       int len;
-       char buffer[BUFSIZE];
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname);
+       strcpy (vl.plugin, "battery");
+       strcpy (vl.plugin_instance, plugin_instance);
 
-       if (current != INVALID_VALUE)
-       {
-               len = snprintf (buffer, BUFSIZE, "N:%.3f", current);
-
-               if ((len > 0) && (len < BUFSIZE))
-                       plugin_submit ("battery_current", inst, buffer);
-       }
-       else
-       {
-               plugin_submit ("battery_current", inst, "N:U");
-       }
-
-       if (voltage != INVALID_VALUE)
-       {
-               len = snprintf (buffer, BUFSIZE, "N:%.3f", voltage);
-
-               if ((len > 0) && (len < BUFSIZE))
-                       plugin_submit ("battery_voltage", inst, buffer);
-       }
-       else
-       {
-               plugin_submit ("battery_voltage", inst, "N:U");
-       }
-
-       if (charge != INVALID_VALUE)
-       {
-               len = snprintf (buffer, BUFSIZE, "N:%.3f", charge);
-
-               if ((len > 0) && (len < BUFSIZE))
-                       plugin_submit ("battery_charge", inst, buffer);
-       }
-       else
-       {
-               plugin_submit ("battery_charge", inst, "N:U");
-       }
-}
+       plugin_dispatch_values (type, &vl);
+} /* void battery_submit */
 
 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H || HAVE_IOKIT_IOKITLIB_H
 double dict_get_double (CFDictionaryRef dict, char *key_string)
@@ -409,7 +346,7 @@ static void get_via_generic_iokit (double *ret_charge,
 }
 #endif /* HAVE_IOKIT_IOKITLIB_H */
 
-static void battery_read (void)
+static int battery_read (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
        double charge  = INVALID_VALUE; /* Current charge in Ah */
@@ -429,16 +366,18 @@ static void battery_read (void)
        if ((charge_rel != INVALID_VALUE) && (charge_abs != INVALID_VALUE))
                charge = charge_abs * charge_rel / 100.0;
 
-       if ((charge != INVALID_VALUE)
-                       || (current != INVALID_VALUE)
-                       || (voltage != INVALID_VALUE))
-               battery_submit ("0", current, voltage, charge);
+       if (charge != INVALID_VALUE)
+               battery_submit ("0", "charge", charge);
+       if (current != INVALID_VALUE)
+               battery_submit ("0", "current", current);
+       if (voltage != INVALID_VALUE)
+               battery_submit ("0", "voltage", voltage);
 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
 
 #elif KERNEL_LINUX
        FILE *fh;
-       char buffer[BUFSIZE];
-       char filename[BUFSIZE];
+       char buffer[1024];
+       char filename[256];
        
        char *fields[8];
        int numfields;
@@ -448,24 +387,24 @@ static void battery_read (void)
 
        for (i = 0; i < battery_pmu_num; i++)
        {
-               char    batnum_str[BUFSIZE];
+               char    batnum_str[256];
                double  current = INVALID_VALUE;
                double  voltage = INVALID_VALUE;
                double  charge  = INVALID_VALUE;
                double *valptr = NULL;
 
-               len = snprintf (filename, BUFSIZE, battery_pmu_file, i);
-               if ((len >= BUFSIZE) || (len < 0))
+               len = snprintf (filename, sizeof (filename), battery_pmu_file, i);
+               if ((len >= sizeof (filename)) || (len < 0))
                        continue;
 
-               len = snprintf (batnum_str, BUFSIZE, "%i", i);
-               if ((len >= BUFSIZE) || (len < 0))
+               len = snprintf (batnum_str, sizeof (batnum_str), "%i", i);
+               if ((len >= sizeof (batnum_str)) || (len < 0))
                        continue;
 
                if ((fh = fopen (filename, "r")) == NULL)
                        continue;
 
-               while (fgets (buffer, BUFSIZE, fh) != NULL)
+               while (fgets (buffer, sizeof (buffer), fh) != NULL)
                {
                        numfields = strsplit (buffer, fields, 8);
 
@@ -495,13 +434,15 @@ static void battery_read (void)
                        }
                }
 
-               if ((current != INVALID_VALUE)
-                               || (voltage != INVALID_VALUE)
-                               || (charge  != INVALID_VALUE))
-                       battery_submit (batnum_str, current, voltage, charge);
-
                fclose (fh);
                fh = NULL;
+
+               if (charge != INVALID_VALUE)
+                       battery_submit ("0", "charge", charge);
+               if (current != INVALID_VALUE)
+                       battery_submit ("0", "current", current);
+               if (voltage != INVALID_VALUE)
+                       battery_submit ("0", "voltage", voltage);
        }
 
        if (access ("/proc/acpi/battery", R_OK | X_OK) == 0)
@@ -518,7 +459,7 @@ static void battery_read (void)
                if ((dh = opendir ("/proc/acpi/battery")) == NULL)
                {
                        syslog (LOG_ERR, "Cannot open `/proc/acpi/battery': %s", strerror (errno));
-                       return;
+                       return (-1);
                }
 
                while ((ent = readdir (dh)) != NULL)
@@ -526,8 +467,10 @@ static void battery_read (void)
                        if (ent->d_name[0] == '.')
                                continue;
 
-                       len = snprintf (filename, BUFSIZE, "/proc/acpi/battery/%s/state", ent->d_name);
-                       if ((len >= BUFSIZE) || (len < 0))
+                       len = snprintf (filename, sizeof (filename),
+                                       "/proc/acpi/battery/%s/state",
+                                       ent->d_name);
+                       if ((len >= sizeof (filename)) || (len < 0))
                                continue;
 
                        if ((fh = fopen (filename, "r")) == NULL)
@@ -545,7 +488,7 @@ static void battery_read (void)
                         * [11:00] <@tokkee> remaining capacity:      4136 mAh
                         * [11:00] <@tokkee> present voltage:         12428 mV
                         */
-                       while (fgets (buffer, BUFSIZE, fh) != NULL)
+                       while (fgets (buffer, sizeof (buffer), fh) != NULL)
                        {
                                numfields = strsplit (buffer, fields, 8);
 
@@ -585,34 +528,37 @@ static void battery_read (void)
                                        if ((fields[2] == endptr) || (errno != 0))
                                                *valptr = INVALID_VALUE;
                                }
-                       }
+                       } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
+
+                       fclose (fh);
 
                        if ((current != INVALID_VALUE) && (charging == 0))
                                        current *= -1;
 
-                       if ((current != INVALID_VALUE)
-                                       || (voltage != INVALID_VALUE)
-                                       || (charge  != INVALID_VALUE))
-                               battery_submit (ent->d_name, current, voltage, charge);
-
-                       fclose (fh);
+                       if (charge != INVALID_VALUE)
+                               battery_submit ("0", "charge", charge);
+                       if (current != INVALID_VALUE)
+                               battery_submit ("0", "current", current);
+                       if (voltage != INVALID_VALUE)
+                               battery_submit ("0", "voltage", voltage);
                }
 
                closedir (dh);
        }
 #endif /* KERNEL_LINUX */
+
+       return (0);
 }
-#else
-# define battery_read NULL
 #endif /* BATTERY_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, battery_init, battery_read, NULL);
-       plugin_register ("battery_current", NULL, NULL, battery_current_write);
-       plugin_register ("battery_voltage", NULL, NULL, battery_voltage_write);
-       plugin_register ("battery_charge",  NULL, NULL, battery_charge_write);
-}
+       plugin_register_data_set (&charge_ds);
+       plugin_register_data_set (&current_ds);
+       plugin_register_data_set (&voltage_ds);
 
-#undef BUFSIZE
-#undef MODULE_NAME
+#if BATTERY_HAVE_READ
+       plugin_register_init ("battery", battery_init);
+       plugin_register_read ("battery", battery_read);
+#endif /* BATTERY_HAVE_READ */
+}
diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c
new file mode 100644 (file)
index 0000000..160412c
--- /dev/null
@@ -0,0 +1,492 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/*
+ * This weird macro cascade forces the glibc to define `NAN'. I don't know
+ * another way to solve this, so more intelligent solutions are welcome. -octo
+ */
+#ifndef __USE_ISOC99
+# define DISABLE__USE_ISOC99 1
+# define __USE_ISOC99 1
+#endif
+#include <math.h>
+#ifdef DISABLE__USE_ISOC99
+# undef DISABLE__USE_ISOC99
+# undef __USE_ISOC99
+#endif
+
+#define RET_OKAY     0
+#define RET_WARNING  1
+#define RET_CRITICAL 2
+#define RET_UNKNOWN  3
+
+#define CON_NONE     0
+#define CON_AVERAGE  1
+#define CON_SUM      2
+
+struct range_s
+{
+       double min;
+       double max;
+       int    invert;
+};
+typedef struct range_s range_t;
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+static char *socket_file_g = NULL;
+static char *value_string_g = NULL;
+static char *hostname_g = NULL;
+
+static range_t range_critical_g;
+static range_t range_warning_g;
+static int consolitation_g = CON_NONE;
+
+static char **match_ds_g = NULL;
+static int    match_ds_num_g = 0;
+
+static int ignore_ds (const char *name)
+{
+       int i;
+
+       if (match_ds_g == NULL)
+               return (0);
+
+       for (i = 0; i < match_ds_num_g; i++)
+               if (strcasecmp (match_ds_g[i], name) == 0)
+                       return (0);
+
+       return (1);
+} /* int ignore_ds */
+
+static void parse_range (char *string, range_t *range)
+{
+       char *min_ptr;
+       char *max_ptr;
+
+       if (*string == '@')
+       {
+               range->invert = 1;
+               string++;
+       }
+
+       max_ptr = strchr (string, ':');
+       if (max_ptr == NULL)
+       {
+               min_ptr = NULL;
+               max_ptr = string;
+       }
+       else
+       {
+               min_ptr = string;
+               *max_ptr = '\0';
+               max_ptr++;
+       }
+
+       assert (max_ptr != NULL);
+
+       /* `10' == `0:10' */
+       if (min_ptr == NULL)
+               range->min = 0.0;
+       /* :10 == ~:10 == -inf:10 */
+       else if ((*min_ptr == '\0') || (*min_ptr == '~'))
+               range->min = NAN;
+       else
+               range->min = atof (min_ptr);
+
+       if ((*max_ptr == '\0') || (*max_ptr == '~'))
+               range->max = NAN;
+       else
+               range->max = atof (max_ptr);
+} /* void parse_range */
+
+int match_range (range_t *range, double value)
+{
+       int ret = 0;
+
+       if ((range->min != NAN) && (range->min > value))
+               ret = 1;
+       if ((range->max != NAN) && (range->max < value))
+               ret = 1;
+
+       return (((ret - range->invert) == 0) ? 0 : 1);
+}
+
+static int get_values (int *ret_values_num, double **ret_values,
+               char ***ret_values_names)
+{
+       struct sockaddr_un sa;
+       int status;
+       int fd;
+       FILE *fh;
+       char buffer[4096];
+
+       int values_num;
+       double *values;
+       char **values_names;
+
+       int i;
+
+       fd = socket (PF_UNIX, SOCK_STREAM, 0);
+       if (fd < 0)
+       {
+               fprintf (stderr, "socket failed: %s\n",
+                               strerror (errno));
+               return (-1);
+       }
+
+       memset (&sa, '\0', sizeof (sa));
+       sa.sun_family = AF_UNIX;
+       strncpy (sa.sun_path, socket_file_g,
+                       sizeof (sa.sun_path) - 1);
+
+       status = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
+       if (status != 0)
+       {
+               fprintf (stderr, "connect failed: %s\n",
+                               strerror (errno));
+               return (-1);
+       }
+
+       fh = fdopen (fd, "r+");
+       if (fh == NULL)
+       {
+               fprintf (stderr, "fdopen failed: %s\n",
+                               strerror (errno));
+               close (fd);
+               return (-1);
+       }
+
+       fprintf (fh, "GETVAL %s/%s\n", hostname_g, value_string_g);
+       fflush (fh);
+
+       if (fgets (buffer, sizeof (buffer), fh) == NULL)
+       {
+               fprintf (stderr, "fgets failed: %s\n",
+                               strerror (errno));
+               close (fd);
+               return (-1);
+       }
+       close (fd); fd = -1;
+
+       values_num = atoi (buffer);
+       if (values_num < 1)
+               return (-1);
+
+       values = (double *) malloc (values_num * sizeof (double));
+       if (values == NULL)
+       {
+               fprintf (stderr, "malloc failed: %s\n",
+                               strerror (errno));
+               return (-1);
+       }
+
+       values_names = (char **) malloc (values_num * sizeof (char *));
+       if (values_names == NULL)
+       {
+               fprintf (stderr, "malloc failed: %s\n",
+                               strerror (errno));
+               free (values);
+               return (-1);
+       }
+
+       {
+               char *ptr = strchr (buffer, ' ') + 1;
+               char *key;
+               char *value;
+
+               i = 0;
+               while ((key = strtok (ptr, " \t")) != NULL)
+               {
+                       ptr = NULL;
+                       value = strchr (key, '=');
+                       if (value == NULL)
+                               continue;
+                       *value = '\0'; value++;
+
+                       if (ignore_ds (key) != 0)
+                               continue;
+
+                       values_names[i] = strdup (key);
+                       values[i] = atof (value);
+
+                       i++;
+                       if (i >= values_num)
+                               break;
+               }
+               values_num = i;
+       }
+
+       *ret_values_num = values_num;
+       *ret_values = values;
+       *ret_values_names = values_names;
+
+       return (0);
+} /* int get_values */
+
+static void usage (const char *name)
+{
+       fprintf (stderr, "Usage: %s <-s socket> <-n value_spec> <-H hostname> [options]\n"
+                       "\n"
+                       "Valid options are:\n"
+                       "  -s <socket>    Path to collectd's UNIX-socket.\n"
+                       "  -n <v_spec>    Value specification to get from collectd.\n"
+                       "                 Format: `plugin-instance/type-instance'\n"
+                       "  -d <ds>        Select the DS to examine. May be repeated to examine multiple\n"
+                       "                 DSes. By default all DSes are used.\n"
+                       "  -g <consol>    Method to use to consolidate several DSes.\n"
+                       "                 Valid arguments are `none', `average' and `sum'\n"
+                       "  -H <host>      Hostname to query the values for.\n"
+                       "  -c <range>     Critical range\n"
+                       "  -w <range>     Warning range\n"
+                       "\n"
+                       "Consolidation functions:\n"
+                       "  none:          Apply the warning- and critical-ranges to each data-source\n"
+                       "                 individually.\n"
+                       "  average:       Calculate the average of all matching DSes and apply the\n"
+                       "                 warning- and critical-ranges to the calculated average.\n"
+                       "  sum:           Apply the ranges to the sum of all DSes.\n"
+                       "\n", name);
+       exit (1);
+} /* void usage */
+
+int do_check_con_none (int values_num, double *values, char **values_names)
+{
+       int i;
+
+       int num_critical = 0;
+       int num_warning  = 0;
+       int num_okay = 0;
+
+       for (i = 0; i < values_num; i++)
+       {
+               if (values[i] == NAN)
+                       num_warning++;
+               else if (match_range (&range_critical_g, values[i]) != 0)
+                       num_critical++;
+               else if (match_range (&range_warning_g, values[i]) != 0)
+                       num_warning++;
+               else
+                       num_okay++;
+       }
+
+       if ((num_critical != 0) || (values_num == 0))
+       {
+               printf ("CRITICAL: %i critical, %i warning, %i okay\n",
+                               num_critical, num_warning, num_okay);
+               return (RET_CRITICAL);
+       }
+       else if (num_warning != 0)
+       {
+               printf ("WARNING: %i warning, %i okay\n",
+                               num_warning, num_okay);
+               return (RET_WARNING);
+       }
+       else
+       {
+               printf ("OKAY: %i okay\n", num_okay);
+               return (RET_OKAY);
+       }
+
+       return (RET_UNKNOWN);
+} /* int do_check_con_none */
+
+int do_check_con_average (int values_num, double *values, char **values_names)
+{
+       int i;
+       double total;
+       int total_num;
+
+       total = 0.0;
+       total_num = 0;
+       for (i = 0; i < values_num; i++)
+       {
+               if (values[i] != NAN)
+               {
+                       total += values[i];
+                       total_num++;
+               }
+       }
+
+       if (total_num == 0)
+       {
+               printf ("WARNING: No defined values found\n");
+               return (RET_WARNING);
+       }
+
+       if (match_range (&range_critical_g, total / total_num) != 0)
+       {
+               printf ("CRITICAL: Average = %lf\n",
+                               (double) (total / total_num));
+               return (RET_CRITICAL);
+       }
+       else if (match_range (&range_warning_g, total / total_num) != 0)
+       {
+               printf ("WARNING: Average = %lf\n",
+                               (double) (total / total_num));
+               return (RET_WARNING);
+       }
+       else
+       {
+               printf ("OKAY: Average = %lf\n",
+                               (double) (total / total_num));
+               return (RET_OKAY);
+       }
+
+       return (RET_UNKNOWN);
+} /* int do_check_con_average */
+
+int do_check_con_sum (int values_num, double *values, char **values_names)
+{
+       int i;
+       double total;
+       int total_num;
+
+       total = 0.0;
+       total_num = 0;
+       for (i = 0; i < values_num; i++)
+       {
+               if (values[i] != NAN)
+               {
+                       total += values[i];
+                       total_num++;
+               }
+       }
+
+       if (total_num == 0)
+       {
+               printf ("WARNING: No defined values found\n");
+               return (RET_WARNING);
+       }
+
+       if (match_range (&range_critical_g, total) != 0)
+       {
+               printf ("CRITICAL: Sum = %lf\n", total);
+               return (RET_CRITICAL);
+       }
+       else if (match_range (&range_warning_g, total) != 0)
+       {
+               printf ("WARNING: Sum = %lf\n", total);
+               return (RET_WARNING);
+       }
+       else
+       {
+               printf ("OKAY: Sum = %lf\n", total);
+               return (RET_OKAY);
+       }
+
+       return (RET_UNKNOWN);
+} /* int do_check_con_sum */
+
+int do_check (void)
+{
+       double  *values;
+       char   **values_names;
+       int      values_num;
+
+       if (get_values (&values_num, &values, &values_names) != 0)
+       {
+               fputs ("ERROR: Cannot get values from daemon\n", stdout);
+               return (RET_CRITICAL);
+       }
+
+       if (consolitation_g == CON_NONE)
+               return (do_check_con_none (values_num, values, values_names));
+       else if (consolitation_g == CON_AVERAGE)
+               return (do_check_con_average (values_num, values, values_names));
+       else if (consolitation_g == CON_SUM)
+               return (do_check_con_sum (values_num, values, values_names));
+
+       free (values);
+       free (values_names);
+
+       return (RET_UNKNOWN);
+}
+
+int main (int argc, char **argv)
+{
+       range_critical_g.min = NAN;
+       range_critical_g.max = NAN;
+       range_critical_g.invert = 0;
+
+       range_warning_g.min = NAN;
+       range_warning_g.max = NAN;
+       range_warning_g.invert = 0;
+
+       while (42)
+       {
+               int c;
+
+               c = getopt (argc, argv, "w:c:s:n:H:g:d:h");
+               if (c < 0)
+                       break;
+
+               switch (c)
+               {
+                       case 'c':
+                               parse_range (optarg, &range_critical_g);
+                               break;
+                       case 'w':
+                               parse_range (optarg, &range_warning_g);
+                               break;
+                       case 's':
+                               socket_file_g = optarg;
+                               break;
+                       case 'n':
+                               value_string_g = optarg;
+                               break;
+                       case 'H':
+                               hostname_g = optarg;
+                               break;
+                       case 'g':
+                               if (strcasecmp (optarg, "none") == 0)
+                                       consolitation_g = CON_NONE;
+                               else if (strcasecmp (optarg, "average") == 0)
+                                       consolitation_g = CON_AVERAGE;
+                               else if (strcasecmp (optarg, "sum") == 0)
+                                       consolitation_g = CON_SUM;
+                               else
+                                       usage (argv[0]);
+                               break;
+                       case 'd':
+                       {
+                               char **tmp;
+                               tmp = (char **) realloc (match_ds_g,
+                                               (match_ds_num_g + 1)
+                                               * sizeof (char *));
+                               if (tmp == NULL)
+                               {
+                                       fprintf (stderr, "realloc failed: %s\n",
+                                                       strerror (errno));
+                                       return (RET_UNKNOWN);
+                               }
+                               match_ds_g = tmp;
+                               match_ds_g[match_ds_num_g] = strdup (optarg);
+                               if (match_ds_g[match_ds_num_g] == NULL)
+                               {
+                                       fprintf (stderr, "strdup failed: %s\n",
+                                                       strerror (errno));
+                                       return (RET_UNKNOWN);
+                               }
+                               match_ds_num_g++;
+                               break;
+                       }
+                       default:
+                               usage (argv[0]);
+               } /* switch (c) */
+       }
+
+       if ((socket_file_g == NULL) || (value_string_g == NULL)
+                       || (hostname_g == NULL))
+               usage (argv[0]);
+
+       return (do_check ());
+} /* int main */
index 0651559..437f758 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
 # define CPU_HAVE_READ 0
 #endif
 
-static data_source_t dsrc[5] =
+static data_source_t dsrc[1] =
 {
-       {"user", DS_TYPE_COUNTER, 0, 4294967295.0},
-       {"nice", DS_TYPE_COUNTER, 0, 4294967295.0},
-       {"syst", DS_TYPE_COUNTER, 0, 4294967295.0},
-       {"idle", DS_TYPE_COUNTER, 0, 4294967295.0},
-       {"wait", DS_TYPE_COUNTER, 0, 4294967295.0}
+       {"value", DS_TYPE_COUNTER, 0, 4294967295.0}
 };
 
 static data_set_t ds =
 {
-       "cpu", 5, dsrc
+       "cpu", 1, dsrc
 };
 
 #if CPU_HAVE_READ
@@ -177,28 +173,22 @@ static int init (void)
        return (0);
 } /* int init */
 
-static void submit (int cpu_num, unsigned long long user,
-               unsigned long long nice, unsigned long long syst,
-               unsigned long long idle, unsigned long long wait)
+static void submit (int cpu_num, const char *type_instance, counter_t value)
 {
-       value_t values[5];
+       value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = user;
-       values[1].counter = nice;
-       values[2].counter = syst;
-       values[3].counter = idle;
-       values[4].counter = wait;
+       values[0].counter = value;
 
        vl.values = values;
-       vl.values_len = 2;
+       vl.values_len = 1;
        vl.time = time (NULL);
        strcpy (vl.host, hostname);
        strcpy (vl.plugin, "cpu");
-       strcpy (vl.plugin_instance, "");
-       snprintf (vl.type_instance, sizeof (vl.type_instance),
+       snprintf (vl.plugin_instance, sizeof (vl.type_instance),
                        "%i", cpu_num);
-       vl.type_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+       vl.plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+       strcpy (vl.type_instance, type_instance);
 
        plugin_dispatch_values ("cpu", &vl);
 }
@@ -241,11 +231,10 @@ static int cpu_read (void)
                        continue;
                }
 
-               submit (cpu, cpu_info.cpu_ticks[CPU_STATE_USER],
-                               cpu_info.cpu_ticks[CPU_STATE_NICE],
-                               cpu_info.cpu_ticks[CPU_STATE_SYSTEM],
-                               cpu_info.cpu_ticks[CPU_STATE_IDLE],
-                               0ULL);
+               submit (cpu, "user", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]);
+               submit (cpu, "nice", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]);
+               submit (cpu, "system", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]);
+               submit (cpu, "idle", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]);
 #endif /* PROCESSOR_CPU_LOAD_INFO */
 #if PROCESSOR_TEMPERATURE
                /*
@@ -296,8 +285,8 @@ static int cpu_read (void)
 
 #elif defined(KERNEL_LINUX)
        int cpu;
-       unsigned long long user, nice, syst, idle;
-       unsigned long long wait, intr, sitr; /* sitr == soft interrupt */
+       counter_t user, nice, syst, idle;
+       counter_t wait, intr, sitr; /* sitr == soft interrupt */
        FILE *fh;
        char buf[1024];
 
@@ -334,22 +323,21 @@ static int cpu_read (void)
                syst = atoll (fields[3]);
                idle = atoll (fields[4]);
 
+               submit (cpu, "user", user);
+               submit (cpu, "nice", nice);
+               submit (cpu, "system", syst);
+               submit (cpu, "idle", idle);
+
                if (numfields >= 8)
                {
                        wait = atoll (fields[5]);
                        intr = atoll (fields[6]);
                        sitr = atoll (fields[7]);
 
-                       /* I doubt anyone cares about the time spent in
-                        * interrupt handlers.. */
-                       syst += intr + sitr;
-               }
-               else
-               {
-                       wait = 0LL;
+                       submit (cpu, "wait", wait);
+                       submit (cpu, "interrupt", intr);
+                       submit (cpu, "softirq", sitr);
                }
-
-               submit (cpu, user, nice, syst, idle, wait);
        }
 
        fclose (fh);
@@ -357,7 +345,7 @@ static int cpu_read (void)
 
 #elif defined(HAVE_LIBKSTAT)
        int cpu;
-       unsigned long long user, syst, idle, wait;
+       counter_t user, syst, idle, wait;
        static cpu_stat_t cs;
 
        if (kc == NULL)
@@ -368,13 +356,15 @@ static int cpu_read (void)
                if (kstat_read (kc, ksp[cpu], &cs) == -1)
                        continue; /* error message? */
 
-               idle = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_IDLE];
-               user = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_USER];
-               syst = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_KERNEL];
-               wait = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_WAIT];
+               idle = (counter_t) cs.cpu_sysinfo.cpu[CPU_IDLE];
+               user = (counter_t) cs.cpu_sysinfo.cpu[CPU_USER];
+               syst = (counter_t) cs.cpu_sysinfo.cpu[CPU_KERNEL];
+               wait = (counter_t) cs.cpu_sysinfo.cpu[CPU_WAIT];
 
-               submit (ksp[cpu]->ks_instance,
-                               user, 0LL, syst, idle, wait);
+               submit (ksp[cpu]->ks_instance, "user", user);
+               submit (ksp[cpu]->ks_instance, "system", syst);
+               submit (ksp[cpu]->ks_instance, "idle", idle);
+               submit (ksp[cpu]->ks_instance, "wait", wait);
        }
 /* #endif defined(HAVE_LIBKSTAT) */
 
@@ -399,8 +389,10 @@ static int cpu_read (void)
 
        cpuinfo[CP_SYS] += cpuinfo[CP_INTR];
 
-       /* FIXME: Instance is always `0' */
-       submit (0, cpuinfo[CP_USER], cpuinfo[CP_NICE], cpuinfo[CP_SYS], cpuinfo[CP_IDLE], 0LL);
+       submit (0, "user", cpuinfo[CP_USER]);
+       submit (0, "nice", cpuinfo[CP_NICE]);
+       submit (0, "system", cpuinfo[CP_SYS]);
+       submit (0, "idle", cpuinfo[CP_IDLE]);
 #endif
 
        return (0);
index ba0149a..e53e495 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/cpufreq.c
- * Copyright (C) 2005,2006  Peter Holik
+ * Copyright (C) 2005-2007  Peter Holik
  *
  * 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
 # define CPUFREQ_HAVE_READ 0
 #endif
 
-static char *cpufreq_file = "cpufreq-%s.rrd";
+static data_source_t data_source[1] =
+{
+       {"value", DS_TYPE_GAUGE, 0, NAN}
+};
 
-static char *ds_def[] =
+static data_set_t data_set =
 {
-       "DS:value:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
+       "cpufreq", 1, data_source
 };
-static int ds_num = 1;
 
+#if CPUFREQ_HAVE_READ
 #ifdef KERNEL_LINUX
 static int num_cpu = 0;
 #endif
 
-#define BUFSIZE 256
-
-static void cpufreq_init (void)
+static int cpufreq_init (void)
 {
 #ifdef KERNEL_LINUX
         int status;
-       char filename[BUFSIZE];
+       char filename[256];
 
        num_cpu = 0;
 
        while (1)
        {
-               status = snprintf (filename, BUFSIZE, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", num_cpu);
-               if (status < 1 || status >= BUFSIZE)
+               status = snprintf (filename, sizeof (filename),
+                               "/sys/devices/system/cpu/cpu%d/cpufreq/"
+                               "scaling_cur_freq", num_cpu);
+               if (status < 1 || status >= sizeof (filename))
                        break;
 
-               if (access(filename, R_OK))
+               if (access (filename, R_OK))
                        break;
 
                num_cpu++;
        }
 
-       syslog (LOG_INFO, MODULE_NAME" found %d cpu(s)", num_cpu);
+       syslog (LOG_INFO, "cpufreq plugin: Found %d CPU%s", num_cpu,
+                       (num_cpu == 1) ? "" : "s");
+
+       if (num_cpu == 0)
+               plugin_unregister_read ("cpufreq");
 #endif /* defined(KERNEL_LINUX) */
 
-       return;
-}
+       return (0);
+} /* int cpufreq_init */
 
-static void cpufreq_write (char *host, char *inst, char *val)
+static void cpufreq_submit (int cpu_num, double value)
 {
-        int status;
-        char file[BUFSIZE];
-
-        status = snprintf (file, BUFSIZE, cpufreq_file, inst);
-        if (status < 1 || status >= BUFSIZE)
-                return;
-
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-#if CPUFREQ_HAVE_READ
-static void cpufreq_submit (int cpu_num, unsigned long long val)
-{
-       char buf[BUFSIZE];
-       char cpu[16];
+       values[0].gauge = value;
 
-       if (snprintf (buf, BUFSIZE, "%u:%llu", (unsigned int) curtime, val) >= BUFSIZE)
-               return;
-        snprintf (cpu, 16, "%i", cpu_num);
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname);
+       strcpy (vl.plugin, "cpufreq");
+       snprintf (vl.type_instance, sizeof (vl.type_instance),
+                       "%i", cpu_num);
 
-       plugin_submit (MODULE_NAME, cpu, buf);
+       plugin_dispatch_values ("cpufreq", &vl);
 }
 
-static void cpufreq_read (void)
+static int cpufreq_read (void)
 {
 #ifdef KERNEL_LINUX
         int status;
        unsigned long long val;
        int i = 0;
        FILE *fp;
-       char filename[BUFSIZE];
+       char filename[256];
        char buffer[16];
 
        for (i = 0; i < num_cpu; i++)
        {
-               status = snprintf (filename, BUFSIZE, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
-               if (status < 1 || status >= BUFSIZE)
-                       return;
+               status = snprintf (filename, sizeof (filename),
+                               "/sys/devices/system/cpu/cpu%d/cpufreq/"
+                               "scaling_cur_freq", i);
+               if (status < 1 || status >= sizeof (filename))
+                       return (-1);
 
                if ((fp = fopen (filename, "r")) == NULL)
                {
                        syslog (LOG_WARNING, "cpufreq: fopen: %s", strerror (errno));
-                       return;
+                       return (-1);
                }
 
                if (fgets (buffer, 16, fp) == NULL)
                {
                        syslog (LOG_WARNING, "cpufreq: fgets: %s", strerror (errno));
                        fclose (fp);
-                       return;
+                       return (-1);
                }
 
                if (fclose (fp))
@@ -137,16 +138,17 @@ static void cpufreq_read (void)
        }
 #endif /* defined(KERNEL_LINUX) */
 
-       return;
-}
-#else
-#define cpufreq_read NULL
-#endif
+       return (0);
+} /* int cpufreq_read */
+#endif /* CPUFREQ_HAVE_READ */
 #undef BUFSIZE
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, cpufreq_init, cpufreq_read, cpufreq_write);
-}
+       plugin_register_data_set (&data_set);
 
-#undef MODULE_NAME
+#if CPUFREQ_HAVE_READ
+       plugin_register_init ("cpufreq", cpufreq_init);
+       plugin_register_read ("cpufreq", cpufreq_read);
+#endif /* CPUFREQ_HAVE_READ */
+}
index d327164..dd5e138 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -1,11 +1,10 @@
 /**
  * collectd - src/df.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * Free Software Foundation; only version 2 of the License is applicable.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -27,8 +26,6 @@
 #include "utils_mount.h"
 #include "utils_ignorelist.h"
 
-#define MODULE_NAME "df"
-
 #if HAVE_STATFS || HAVE_STATVFS
 # define DF_HAVE_READ 1
 #else
 # define BLOCKSIZE(s) (s).f_bsize
 #endif
 
-static char *filename_template = "df-%s.rrd";
+/* 2^50 - 1 == 1125899906842623 = 1 Petabyte */
+static data_source_t dsrc[2] =
+{
+       {"free", DS_TYPE_GAUGE, 0, 1125899906842623.0},
+       {"used", DS_TYPE_GAUGE, 0, 1125899906842623.0}
+};
 
-static char *ds_def[] =
+static data_set_t ds =
 {
-       "DS:used:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       "DS:free:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
+       "df", 2, dsrc
 };
-static int ds_num = 2;
 
-static char *config_keys[] =
+#if DF_HAVE_READ
+static const char *config_keys[] =
 {
        "Device",
        "MountPoint",
@@ -73,9 +73,7 @@ static ignorelist_t *il_device = NULL;
 static ignorelist_t *il_mountpoint = NULL;
 static ignorelist_t *il_fstype = NULL;
 
-#define BUFSIZE 512
-
-static void df_init (void)
+static int df_init (void)
 {
        if (il_device == NULL)
                il_device = ignorelist_create (1);
@@ -84,10 +82,10 @@ static void df_init (void)
        if (il_fstype == NULL)
                il_fstype = ignorelist_create (1);
 
-       return;
+       return (0);
 }
 
-static int df_config (char *key, char *value)
+static int df_config (const char *key, const char *value)
 {
        df_init ();
 
@@ -131,35 +129,28 @@ static int df_config (char *key, char *value)
        return (-1);
 }
 
-static void df_write (char *host, char *inst, char *val)
-{
-       char file[BUFSIZE];
-       int status;
-
-       status = snprintf (file, BUFSIZE, filename_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= BUFSIZE)
-               return;
-
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
-
-#if DF_HAVE_READ
 static void df_submit (char *df_name,
-               unsigned long long df_used,
-               unsigned long long df_free)
+               gauge_t df_used,
+               gauge_t df_free)
 {
-       char buf[BUFSIZE];
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf (buf, BUFSIZE, "%u:%llu:%llu", (unsigned int) curtime,
-                               df_used, df_free) >= BUFSIZE)
-               return;
+       values[0].gauge = df_used;
+       values[1].gauge = df_free;
 
-       plugin_submit (MODULE_NAME, df_name, buf);
-}
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname);
+       strcpy (vl.plugin, "df");
+       strcpy (vl.plugin_instance, "");
+       strncpy (vl.type_instance, df_name, sizeof (vl.type_instance));
 
-static void df_read (void)
+       plugin_dispatch_values ("df", &vl);
+} /* void df_submit */
+
+static int df_read (void)
 {
 #if HAVE_STATVFS
        struct statvfs statbuf;
@@ -171,13 +162,13 @@ static void df_read (void)
        cu_mount_t *mnt_ptr;
 
        unsigned long long blocksize;
-       unsigned long long df_free;
-       unsigned long long df_used;
-       char mnt_name[BUFSIZE];
+       gauge_t df_free;
+       gauge_t df_used;
+       char mnt_name[256];
 
        mnt_list = NULL;
        if (cu_mount_getlist (&mnt_list) == NULL)
-               return;
+               return (-1);
 
        for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next)
        {
@@ -196,13 +187,13 @@ static void df_read (void)
 
                if (strcmp (mnt_ptr->dir, "/") == 0)
                {
-                       strncpy (mnt_name, "root", BUFSIZE);
+                       strncpy (mnt_name, "root", sizeof (mnt_name));
                }
                else
                {
                        int i, len;
 
-                       strncpy (mnt_name, mnt_ptr->dir + 1, BUFSIZE);
+                       strncpy (mnt_name, mnt_ptr->dir + 1, sizeof (mnt_name));
                        len = strlen (mnt_name);
 
                        for (i = 0; i < len; i++)
@@ -224,16 +215,18 @@ static void df_read (void)
        }
 
        cu_mount_freelist (mnt_list);
-} /* static void df_read (void) */
-#else
-# define df_read NULL
+
+       return (0);
+} /* int df_read */
 #endif /* DF_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, df_init, df_read, df_write);
-       cf_register (MODULE_NAME, df_config, config_keys, config_keys_num);
-}
+       plugin_register_data_set (&ds);
 
-#undef BUFSIZE
-#undef MODULE_NAME
+#if DF_HAVE_READ
+       plugin_register_config ("df", df_config, config_keys, config_keys_num);
+       plugin_register_init ("df", df_init);
+       plugin_register_read ("df", df_read);
+#endif
+} /* void module_register */
index c809fdb..3cb86e7 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/disk.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * Free Software Foundation; only version 2 of the License is applicable.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,8 +24,6 @@
 #include "plugin.h"
 #include "utils_debug.h"
 
-#define MODULE_NAME "disk"
-
 #if HAVE_MACH_MACH_TYPES_H
 #  include <mach/mach_types.h>
 #endif
 # define DISK_HAVE_READ 0
 #endif
 
-static char *disk_filename_template = "disk-%s.rrd";
-static char *part_filename_template = "partition-%s.rrd";
+/* 2^34 = 17179869184 = ~17.2GByte/s */
+static data_source_t octets_dsrc[2] =
+{
+       {"read",  DS_TYPE_COUNTER, 0, 17179869183.0},
+       {"write", DS_TYPE_COUNTER, 0, 17179869183.0}
+};
+
+static data_set_t octets_ds =
+{
+       "disk_octets", 2, octets_dsrc
+};
+
+static data_source_t operations_dsrc[2] =
+{
+       {"read",  DS_TYPE_COUNTER, 0, 4294967295.0},
+       {"write", DS_TYPE_COUNTER, 0, 4294967295.0}
+};
 
-/* 104857600 == 100 MB */
-static char *disk_ds_def[] =
+static data_set_t operations_ds =
 {
-       "DS:rcount:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rmerged:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600",
-       "DS:rtime:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wcount:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wmerged:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600",
-       "DS:wtime:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
+       "disk_ops", 2, operations_dsrc
 };
-static int disk_ds_num = 8;
 
-static char *part_ds_def[] =
+static data_source_t merged_dsrc[2] =
 {
-       "DS:rcount:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600",
-       "DS:wcount:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600",
-       NULL
+       {"read",  DS_TYPE_COUNTER, 0, 4294967295.0},
+       {"write", DS_TYPE_COUNTER, 0, 4294967295.0}
 };
-static int part_ds_num = 4;
 
+static data_set_t merged_ds =
+{
+       "disk_merged", 2, merged_dsrc
+};
+
+/* max is 1000000us per second. */
+static data_source_t time_dsrc[2] =
+{
+       {"read",  DS_TYPE_COUNTER, 0, 1000000.0},
+       {"write", DS_TYPE_COUNTER, 0, 1000000.0}
+};
+
+static data_set_t time_ds =
+{
+       "disk_time", 2, time_dsrc
+};
+
+#if DISK_HAVE_READ
 #if HAVE_IOKIT_IOKITLIB_H
 static mach_port_t io_master_port = MACH_PORT_NULL;
 /* #endif HAVE_IOKIT_IOKITLIB_H */
@@ -101,11 +117,11 @@ typedef struct diskstats
        /* This overflows in roughly 1361 year */
        unsigned int poll_count;
 
-       unsigned long long read_sectors;
-       unsigned long long write_sectors;
+       counter_t read_sectors;
+       counter_t write_sectors;
 
-       unsigned long long read_bytes;
-       unsigned long long write_bytes;
+       counter_t read_bytes;
+       counter_t write_bytes;
 
        struct diskstats *next;
 } diskstats_t;
@@ -121,7 +137,7 @@ static kstat_t *ksp[MAX_NUMDISK];
 static int numdisk = 0;
 #endif /* HAVE_LIBKSTAT */
 
-static void disk_init (void)
+static int disk_init (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H
        kern_return_t status;
@@ -139,7 +155,7 @@ static void disk_init (void)
                syslog (LOG_ERR, "IOMasterPort failed: %s",
                                mach_error_string (status));
                io_master_port = MACH_PORT_NULL;
-               return;
+               return (-1);
        }
 /* #endif HAVE_IOKIT_IOKITLIB_H */
 
@@ -163,7 +179,7 @@ static void disk_init (void)
        numdisk = 0;
 
        if (kc == NULL)
-               return;
+               return (-1);
 
        for (numdisk = 0, ksp_chain = kc->kc_chain;
                        (numdisk < MAX_NUMDISK) && (ksp_chain != NULL);
@@ -178,83 +194,29 @@ static void disk_init (void)
        }
 #endif /* HAVE_LIBKSTAT */
 
-       return;
-}
+       return (0);
+} /* int disk_init */
 
-static void disk_write (char *host, char *inst, char *val)
+static void disk_submit (const char *plugin_instance,
+               const char *type,
+               counter_t read, counter_t write)
 {
-       char file[512];
-       int status;
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       status = snprintf (file, 512, disk_filename_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
+       values[0].counter = read;
+       values[1].counter = write;
 
-       rrd_update_file (host, file, val, disk_ds_def, disk_ds_num);
-}
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname);
+       strcpy (vl.plugin, "disk");
+       strncpy (vl.plugin_instance, plugin_instance,
+                       sizeof (vl.plugin_instance));
 
-static void partition_write (char *host, char *inst, char *val)
-{
-       char file[512];
-       int status;
-
-       status = snprintf (file, 512, part_filename_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
-
-       rrd_update_file (host, file, val, part_ds_def, part_ds_num);
-}
-
-#if DISK_HAVE_READ
-#define BUFSIZE 512
-static void disk_submit (char *disk_name,
-               unsigned long long read_count,
-               unsigned long long read_merged,
-               unsigned long long read_bytes,
-               unsigned long long read_time,
-               unsigned long long write_count,
-               unsigned long long write_merged,
-               unsigned long long write_bytes,
-               unsigned long long write_time)
-{
-       char buf[BUFSIZE];
-
-       if (snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu",
-                               (unsigned int) curtime,
-                               read_count, read_merged, read_bytes, read_time,
-                               write_count, write_merged, write_bytes,
-                               write_time) >= BUFSIZE)
-               return;
-
-       DBG ("disk_name = %s; buf = %s;",
-                       disk_name, buf);
-
-       plugin_submit (MODULE_NAME, disk_name, buf);
-}
-
-#if KERNEL_LINUX || HAVE_LIBKSTAT
-static void partition_submit (char *part_name,
-               unsigned long long read_count,
-               unsigned long long read_bytes,
-               unsigned long long write_count,
-               unsigned long long write_bytes)
-{
-       char buf[BUFSIZE];
-
-       if (snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu",
-                               (unsigned int) curtime,
-                               read_count, read_bytes, write_count,
-                               write_bytes) >= BUFSIZE)
-               return;
-
-       plugin_submit ("partition", part_name, buf);
-}
-#endif /* KERNEL_LINUX || HAVE_LIBKSTAT */
-#undef BUFSIZE
+       plugin_dispatch_values (type, &vl);
+} /* void disk_submit */
 
 #if HAVE_IOKIT_IOKITLIB_H
 static signed long long dict_get_value (CFDictionaryRef dict, const char *key)
@@ -293,7 +255,7 @@ static signed long long dict_get_value (CFDictionaryRef dict, const char *key)
 }
 #endif /* HAVE_IOKIT_IOKITLIB_H */
 
-static void disk_read (void)
+static int disk_read (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H
        io_registry_entry_t     disk;
@@ -324,7 +286,7 @@ static void disk_read (void)
        {
                plugin_complain (LOG_ERR, &complain_obj, "disk plugin: "
                                "IOServiceGetMatchingServices failed.");
-               return;
+               return (-1);
        }
        else if (complain_obj.interval != 0)
        {
@@ -395,6 +357,7 @@ static void disk_read (void)
                        continue;
                }
 
+               /* kIOBSDNameKey */
                disk_major = (int) dict_get_value (child_dict,
                                kIOBSDMajorKey);
                disk_minor = (int) dict_get_value (child_dict,
@@ -409,6 +372,11 @@ static void disk_read (void)
                                kIOBlockStorageDriverStatisticsWritesKey);
                write_byt = dict_get_value (stats_dict,
                                kIOBlockStorageDriverStatisticsBytesWrittenKey);
+               /* This property describes the number of nanoseconds spent
+                * performing writes since the block storage driver was
+                * instantiated. It is one of the statistic entries listed
+                * under the top-level kIOBlockStorageDriverStatisticsKey
+                * property table. It has an OSNumber value. */
                write_tme = dict_get_value (stats_dict,
                                kIOBlockStorageDriverStatisticsTotalWriteTimeKey);
 
@@ -423,15 +391,14 @@ static void disk_read (void)
                }
                DBG ("disk_name = %s", disk_name);
 
-               if ((read_ops != -1LL)
-                               || (read_byt != -1LL)
-                               || (read_tme != -1LL)
-                               || (write_ops != -1LL)
-                               || (write_byt != -1LL)
-                               || (write_tme != -1LL))
-                       disk_submit (disk_name,
-                                       read_ops, 0ULL, read_byt, read_tme,
-                                       write_ops, 0ULL, write_byt, write_tme);
+               if ((read_byt != -1LL) || (write_byt != -1LL))
+                       disk_submit (disk_name, "disk_octets", read_byt, write_byt);
+               if ((read_ops != -1LL) || (write_ops != -1LL))
+                       disk_submit (disk_name, "disk_ops", read_ops, write_ops);
+               if ((read_tme != -1LL) || (write_tme != -1LL))
+                       disk_submit (disk_name, "disk_time",
+                                       read_tme / 1000,
+                                       write_tme / 1000);
 
                CFRelease (child_dict);
                IOObjectRelease (disk_child);
@@ -444,7 +411,6 @@ static void disk_read (void)
 #elif KERNEL_LINUX
        FILE *fh;
        char buffer[1024];
-       char disk_name[128];
        
        char *fields[32];
        int numfields;
@@ -453,17 +419,17 @@ static void disk_read (void)
        int major = 0;
        int minor = 0;
 
-       unsigned long long read_sectors  = 0ULL;
-       unsigned long long write_sectors = 0ULL;
-
-       unsigned long long read_count    = 0ULL;
-       unsigned long long read_merged   = 0ULL;
-       unsigned long long read_bytes    = 0ULL;
-       unsigned long long read_time     = 0ULL;
-       unsigned long long write_count   = 0ULL;
-       unsigned long long write_merged  = 0ULL;
-       unsigned long long write_bytes   = 0ULL;
-       unsigned long long write_time    = 0ULL;
+       counter_t read_sectors  = 0;
+       counter_t write_sectors = 0;
+
+       counter_t read_count    = 0;
+       counter_t read_merged   = 0;
+       counter_t read_bytes    = 0;
+       counter_t read_time     = 0;
+       counter_t write_count   = 0;
+       counter_t write_merged  = 0;
+       counter_t write_bytes   = 0;
+       counter_t write_time    = 0;
        int is_disk = 0;
 
        diskstats_t *ds, *pre_ds;
@@ -474,18 +440,23 @@ static void disk_read (void)
        {
                if ((fh = fopen ("/proc/partitions", "r")) == NULL)
                {
-                       plugin_complain (LOG_ERR, &complain_obj, "disk plugin: Failed to open /proc/{diskstats,partitions}.");
-                       return;
+                       plugin_complain (LOG_ERR, &complain_obj,
+                                       "disk plugin: Failed to open /proc/"
+                                       "{diskstats,partitions}.");
+                       return (-1);
                }
 
                /* Kernel is 2.4.* */
                fieldshift = 1;
        }
 
-       plugin_relief (LOG_NOTICE, &complain_obj, "disk plugin: Succeeded to open /proc/{diskstats,partitions}.");
+       plugin_relief (LOG_NOTICE, &complain_obj, "disk plugin: "
+                       "Succeeded to open /proc/{diskstats,partitions}.");
 
-       while (fgets (buffer, 1024, fh) != NULL)
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
+               char *disk_name;
+
                numfields = strsplit (buffer, fields, 32);
 
                if ((numfields != (14 + fieldshift)) && (numfields != 7))
@@ -494,9 +465,7 @@ static void disk_read (void)
                major = atoll (fields[0]);
                minor = atoll (fields[1]);
 
-               if (snprintf (disk_name, 128, "%i-%i", major, minor) < 1)
-                       continue;
-               disk_name[127] = '\0';
+               disk_name = fields[2];
 
                for (ds = disklist, pre_ds = disklist; ds != NULL; pre_ds = ds, ds = ds->next)
                        if (strcmp (disk_name, ds->name) == 0)
@@ -582,12 +551,21 @@ static void disk_read (void)
                        continue;
                }
 
+               if ((read_bytes != -1LL) || (write_bytes != -1LL))
+                       disk_submit (disk_name, "disk_octets", read_bytes, write_bytes);
+               if ((read_count != -1LL) || (write_count != -1LL))
+                       disk_submit (disk_name, "disk_ops", read_count, write_count);
                if (is_disk)
-                       disk_submit (disk_name, read_count, read_merged, read_bytes, read_time,
-                                       write_count, write_merged, write_bytes, write_time);
-               else
-                       partition_submit (disk_name, read_count, read_bytes, write_count, write_bytes);
-       }
+               {
+                       if ((read_merged != -1LL) || (write_merged != -1LL))
+                               disk_submit (disk_name, "disk_merged",
+                                               read_merged, write_merged);
+                       if ((read_time != -1LL) || (write_time != -1LL))
+                               disk_submit (disk_name, "disk_time",
+                                               read_time * 1000,
+                                               write_time * 1000);
+               }
+       } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
 
        fclose (fh);
 /* #endif defined(KERNEL_LINUX) */
@@ -597,7 +575,7 @@ static void disk_read (void)
        int i;
 
        if (kc == NULL)
-               return;
+               return (-1);
 
        for (i = 0; i < numdisk; i++)
        {
@@ -605,24 +583,33 @@ static void disk_read (void)
                        continue;
 
                if (strncmp (ksp[i]->ks_class, "disk", 4) == 0)
-                       disk_submit (ksp[i]->ks_name,
-                                       kio.reads,  0LL, kio.nread,    kio.rtime,
-                                       kio.writes, 0LL, kio.nwritten, kio.wtime);
+               {
+                       disk_submit (ksp[i]->ks_name, "disk_octets", kio.reads, kio.writes);
+                       disk_submit (ksp[i]->ks_name, "disk_ops", kio.nreads, kio.nwrites);
+                       /* FIXME: Convert this to microseconds if necessary */
+                       disk_submit (ksp[i]->ks_name, "disk_time", kio.rtime, kio.wtime);
+               }
                else if (strncmp (ksp[i]->ks_class, "partition", 9) == 0)
-                       partition_submit (ksp[i]->ks_name,
-                                       kio.reads, kio.nread,
-                                       kio.writes,kio.nwritten);
+               {
+                       disk_submit (ksp[i]->ks_name, "disk_octets", kio.reads, kio.writes);
+                       disk_submit (ksp[i]->ks_name, "disk_ops", kio.nreads, kio.nwrites);
+               }
        }
 #endif /* defined(HAVE_LIBKSTAT) */
-} /* static void disk_read (void) */
-#else
-# define disk_read NULL
+
+       return (0);
+} /* int disk_read */
 #endif /* DISK_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register ("partition", NULL, NULL, partition_write);
-       plugin_register (MODULE_NAME, disk_init, disk_read, disk_write);
-}
+       plugin_register_data_set (&octets_ds);
+       plugin_register_data_set (&operations_ds);
+       plugin_register_data_set (&merged_ds);
+       plugin_register_data_set (&time_ds);
 
-#undef MODULE_NAME
+#if DISK_HAVE_READ
+       plugin_register_init ("disk", disk_init);
+       plugin_register_read ("disk", disk_read);
+#endif /* DISK_HAVE_READ */
+}
index b40d62a..2daeea9 100644 (file)
 #include "utils_debug.h"
 
 /*
+ * Private structures
+ */
+struct read_func_s
+{
+       int wait_time;
+       int wait_left;
+       int (*callback) (void);
+};
+typedef struct read_func_s read_func_t;
+
+/*
  * Private variables
  */
 static llist_t *list_init;
@@ -77,6 +88,21 @@ static int register_callback (llist_t **list, const char *name, void *callback)
        return (0);
 } /* int register_callback */
 
+static int plugin_unregister (llist_t *list, const char *name)
+{
+       llentry_t *e;
+
+       e = llist_search (list, name);
+
+       if (e == NULL)
+               return (-1);
+
+       llist_remove (list, e);
+       llentry_destroy (e);
+
+       return (0);
+} /* int plugin_unregister */
+
 /*
  * (Try to) load the shared object `file'. Won't complain if it isn't a shared
  * object, but it will bitch about a shared object not having a
@@ -215,7 +241,22 @@ int plugin_register_init (const char *name,
 int plugin_register_read (const char *name,
                int (*callback) (void))
 {
-       return (register_callback (&list_read, name, (void *) callback));
+       read_func_t *rf;
+
+       rf = (read_func_t *) malloc (sizeof (read_func_t));
+       if (rf == NULL)
+       {
+               syslog (LOG_ERR, "plugin_register_read: malloc failed: %s",
+                               strerror (errno));
+               return (-1);
+       }
+
+       memset (rf, '\0', sizeof (read_func_t));
+       rf->wait_time = atoi (COLLECTD_STEP);
+       rf->wait_left = 0;
+       rf->callback = callback;
+
+       return (register_callback (&list_read, name, (void *) rf));
 } /* int plugin_register_read */
 
 int plugin_register_write (const char *name,
@@ -235,6 +276,43 @@ int plugin_register_data_set (const data_set_t *ds)
        return (register_callback (&list_data_set, ds->type, (void *) ds));
 } /* int plugin_register_data_set */
 
+int plugin_unregister_init (const char *name)
+{
+       return (plugin_unregister (list_init, name));
+}
+
+int plugin_unregister_read (const char *name)
+{
+       return (plugin_unregister (list_read, name));
+       llentry_t *e;
+
+       e = llist_search (list_read, name);
+
+       if (e == NULL)
+               return (-1);
+
+       llist_remove (list_read, e);
+       free (e->value);
+       llentry_destroy (e);
+
+       return (0);
+}
+
+int plugin_unregister_write (const char *name)
+{
+       return (plugin_unregister (list_write, name));
+}
+
+int plugin_unregister_shutdown (const char *name)
+{
+       return (plugin_unregister (list_shutdown, name));
+}
+
+int plugin_unregister_data_set (const char *name)
+{
+       return (plugin_unregister (list_data_set, name));
+}
+
 void plugin_init_all (void)
 {
        int (*callback) (void);
@@ -257,20 +335,49 @@ void plugin_init_all (void)
 
 void plugin_read_all (const int *loop)
 {
-       int (*callback) (void);
-       llentry_t *le;
+       llentry_t   *le;
+       read_func_t *rf;
+       int          status;
+       int          step;
 
        if (list_read == NULL)
                return;
 
+       step = atoi (COLLECTD_STEP);
+
        le = llist_head (list_read);
        while ((*loop == 0) && (le != NULL))
        {
-               callback = le->value;
-               (*callback) ();
+               rf = (read_func_t *) le->value;
+
+               if (rf->wait_left > 0)
+                       rf->wait_left -= step;
+               if (rf->wait_left > 0)
+               {
+                       le = le->next;
+                       continue;
+               }
+
+               status = rf->callback ();
+               if (status != 0)
+               {
+                       rf->wait_left = rf->wait_time;
+                       rf->wait_time = rf->wait_time * 2;
+                       if (rf->wait_time > 86400)
+                               rf->wait_time = 86400;
+
+                       syslog (LOG_NOTICE, "read-function of plugin `%s' "
+                                       "failed. Will syspend it for %i "
+                                       "seconds.", le->key, rf->wait_left);
+               }
+               else
+               {
+                       rf->wait_left = 0;
+                       rf->wait_time = step;
+               }
 
                le = le->next;
-       }
+       } /* while ((*loop == 0) && (le != NULL)) */
 } /* void plugin_read_all */
 
 void plugin_shutdown_all (void)
index 107078e..b0bdbee 100644 (file)
@@ -138,6 +138,12 @@ int plugin_register_shutdown (char *name,
                int (*callback) (void));
 int plugin_register_data_set (const data_set_t *ds);
 
+int plugin_unregister_init (const char *name);
+int plugin_unregister_read (const char *name);
+int plugin_unregister_write (const char *name);
+int plugin_unregister_shutdown (const char *name);
+int plugin_unregister_data_set (const char *name);
+
 /*
  * NAME
  *  plugin_dispatch_values
diff --git a/src/unixsock.c b/src/unixsock.c
new file mode 100644 (file)
index 0000000..de1fa4f
--- /dev/null
@@ -0,0 +1,687 @@
+/**
+ * collectd - src/unixsock.c
+ * Copyright (C) 2007  Florian octo Forster
+ *
+ * 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 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 License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_debug.h"
+
+/* Folks without pthread will need to disable this plugin. */
+#include <pthread.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+
+#include <grp.h>
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
+#endif
+
+#define US_DEFAULT_PATH PREFIX"/var/run/"PACKAGE_NAME"-unixsock"
+
+/*
+ * Private data structures
+ */
+/* linked list of cached values */
+typedef struct value_cache_s
+{
+       char       name[4*DATA_MAX_NAME_LEN];
+       int        values_num;
+       gauge_t   *gauge;
+       counter_t *counter;
+       const data_set_t *ds;
+       time_t     time;
+       struct value_cache_s *next;
+} value_cache_t;
+
+/*
+ * Private variables
+ */
+/* valid configuration file keys */
+static const char *config_keys[] =
+{
+       "SocketFile",
+       "SocketGroup",
+       "SocketPerms",
+       NULL
+};
+static int config_keys_num = 3;
+
+/* socket configuration */
+static int   sock_fd    = -1;
+static char *sock_file  = NULL;
+static char *sock_group = NULL;
+static int   sock_perms = S_IRWXU | S_IRWXG;
+
+static pthread_t listen_thread = -1;
+
+/* Linked list and auxilliary variables for saving values */
+static value_cache_t   *cache_head = NULL;
+static pthread_mutex_t  cache_lock = PTHREAD_MUTEX_INITIALIZER;
+static unsigned int     cache_oldest = UINT_MAX;
+
+/*
+ * Functions
+ */
+static value_cache_t *cache_search (const char *name)
+{
+       value_cache_t *vc;
+
+       for (vc = cache_head; vc != NULL; vc = vc->next)
+       {
+               if (strcmp (vc->name, name) == 0)
+                       break;
+       } /* for vc = cache_head .. NULL */
+
+       return (vc);
+} /* value_cache_t *cache_search */
+
+static int cache_alloc_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 = snprintf (ret, ret_len, "%s/%s/%s",
+                                       hostname, plugin, type);
+               else
+                       status = snprintf (ret, ret_len, "%s/%s/%s-%s",
+                                       hostname, plugin, type, type_instance);
+       }
+       else
+       {
+               if ((type_instance == NULL) || (strlen (type_instance) == 0))
+                       status = snprintf (ret, ret_len, "%s/%s-%s/%s",
+                                       hostname, plugin, plugin_instance, type);
+               else
+                       status = snprintf (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 cache_alloc_name */
+
+static int cache_insert (const data_set_t *ds, const value_list_t *vl)
+{
+       /* We're called from `cache_update' so we don't need to lock the mutex */
+       value_cache_t *vc;
+       int i;
+
+       DBG ("ds->ds_num = %i; vl->values_len = %i;",
+                       ds->ds_num, vl->values_len);
+       assert (ds->ds_num == vl->values_len);
+
+       vc = (value_cache_t *) malloc (sizeof (value_cache_t));
+       if (vc == NULL)
+       {
+               pthread_mutex_unlock (&cache_lock);
+               syslog (LOG_ERR, "unixsock plugin: malloc failed: %s",
+                               strerror (errno));
+               return (-1);
+       }
+
+       vc->gauge = (gauge_t *) malloc (sizeof (gauge_t) * vl->values_len);
+       if (vc->gauge == NULL)
+       {
+               pthread_mutex_unlock (&cache_lock);
+               syslog (LOG_ERR, "unixsock plugin: malloc failed: %s",
+                               strerror (errno));
+               free (vc);
+               return (-1);
+       }
+
+       vc->counter = (counter_t *) malloc (sizeof (counter_t) * vl->values_len);
+       if (vc->counter == NULL)
+       {
+               pthread_mutex_unlock (&cache_lock);
+               syslog (LOG_ERR, "unixsock plugin: malloc failed: %s",
+                               strerror (errno));
+               free (vc->gauge);
+               free (vc);
+               return (-1);
+       }
+
+       if (cache_alloc_name (vc->name, sizeof (vc->name),
+                               vl->host, vl->plugin, vl->plugin_instance,
+                               ds->type, vl->type_instance) != 0)
+       {
+               pthread_mutex_unlock (&cache_lock);
+               syslog (LOG_ERR, "unixsock plugin: cache_alloc_name failed.");
+               free (vc->counter);
+               free (vc->gauge);
+               free (vc);
+               return (-1);
+       }
+
+       for (i = 0; i < ds->ds_num; i++)
+       {
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+               {
+                       vc->gauge[i] = 0.0;
+                       vc->counter[i] = vl->values[i].counter;
+               }
+               else if (ds->ds[i].type == DS_TYPE_GAUGE)
+               {
+                       vc->gauge[i] = vl->values[i].gauge;
+                       vc->counter[i] = 0;
+               }
+               else
+               {
+                       vc->gauge[i] = 0.0;
+                       vc->counter[i] = 0;
+               }
+       }
+       vc->values_num = ds->ds_num;
+       vc->ds = ds;
+
+       vc->next = cache_head;
+       cache_head = vc;
+
+       vc->time = vl->time;
+       if (vc->time < cache_oldest)
+               cache_oldest = vc->time;
+
+       pthread_mutex_unlock (&cache_lock);
+       return (0);
+} /* int cache_insert */
+
+static int cache_update (const data_set_t *ds, const value_list_t *vl)
+{
+       char name[4*DATA_MAX_NAME_LEN];;
+       value_cache_t *vc;
+       int i;
+
+       if (cache_alloc_name (name, sizeof (name),
+                               vl->host,
+                               vl->plugin, vl->plugin_instance,
+                               ds->type, vl->type_instance) != 0)
+               return (-1);
+
+       pthread_mutex_lock (&cache_lock);
+
+       vc = cache_search (name);
+
+       if (vc == NULL)
+               return (cache_insert (ds, vl));
+
+       assert (vc->values_num == ds->ds_num);
+       assert (vc->values_num == vl->values_len);
+
+       /*
+        * Update the values. This is possibly a lot more that you'd expect
+        * because we honor min and max values and handle counter overflows here.
+        */
+       for (i = 0; i < ds->ds_num; i++)
+       {
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+               {
+                       if (vl->values[i].counter < vc->counter[i])
+                       {
+                               if (vl->values[i].counter <= 4294967295U)
+                               {
+                                       vc->gauge[i] = ((4294967295U - vl->values[i].counter)
+                                                       + vc->counter[i]) / (vl->time - vc->time);
+                               }
+                               else
+                               {
+                                       vc->gauge[i] = ((18446744073709551615ULL - vl->values[i].counter)
+                                               + vc->counter[i]) / (vl->time - vc->time);
+                               }
+                       }
+                       else
+                       {
+                               vc->gauge[i] = (vl->values[i].counter - vc->counter[i])
+                                       / (vl->time - vc->time);
+                       }
+
+                       vc->counter[i] = vl->values[i].counter;
+               }
+               else if (ds->ds[i].type == DS_TYPE_GAUGE)
+               {
+                       vc->gauge[i] = vl->values[i].gauge;
+                       vc->counter[i] = 0;
+               }
+               else
+               {
+                       vc->gauge[i] = NAN;
+                       vc->counter[i] = 0;
+               }
+
+               if ((vc->gauge[i] == NAN)
+                               || ((ds->ds[i].min != NAN) && (vc->gauge[i] < ds->ds[i].min))
+                               || ((ds->ds[i].max != NAN) && (vc->gauge[i] > ds->ds[i].max)))
+                       vc->gauge[i] = NAN;
+       } /* for i = 0 .. ds->ds_num */
+
+       vc->ds = ds;
+       vc->time = vl->time;
+
+       if (vc->time < cache_oldest)
+               cache_oldest = vc->time;
+
+       pthread_mutex_unlock (&cache_lock);
+       return (0);
+} /* int cache_update */
+
+static void cache_flush (int max_age)
+{
+       value_cache_t *this;
+       value_cache_t *prev;
+       time_t now;
+
+       pthread_mutex_lock (&cache_lock);
+
+       now = time (NULL);
+
+       if ((now - cache_oldest) <= max_age)
+       {
+               pthread_mutex_unlock (&cache_lock);
+               return;
+       }
+       
+       cache_oldest = now;
+
+       prev = NULL;
+       this = cache_head;
+
+       while (this != NULL)
+       {
+               if ((now - this->time) <= max_age)
+               {
+                       if (this->time < cache_oldest)
+                               cache_oldest = this->time;
+
+                       prev = this;
+                       this = this->next;
+                       continue;
+               }
+
+               if (prev == NULL)
+                       cache_head = this->next;
+               else
+                       prev->next = this->next;
+
+               free (this->gauge);
+               free (this->counter);
+               free (this);
+
+               if (prev == NULL)
+                       this = cache_head;
+               else
+                       this = prev->next;
+       } /* while (this != NULL) */
+
+       pthread_mutex_unlock (&cache_lock);
+} /* int cache_flush */
+
+static int us_open_socket (void)
+{
+       struct sockaddr_un sa;
+       int status;
+
+       sock_fd = socket (PF_UNIX, SOCK_STREAM, 0);
+       if (sock_fd < 0)
+       {
+               syslog (LOG_ERR, "unixsock plugin: socket failed: %s",
+                               strerror (errno));
+               return (-1);
+       }
+
+       memset (&sa, '\0', sizeof (sa));
+       sa.sun_family = AF_UNIX;
+       strncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
+                       sizeof (sa.sun_path) - 1);
+       /* unlink (sa.sun_path); */
+
+       status = bind (sock_fd, (struct sockaddr *) &sa, sizeof (sa));
+       if (status != 0)
+       {
+               DBG ("bind failed: %s; sa.sun_path = %s",
+                               strerror (errno), sa.sun_path);
+               syslog (LOG_ERR, "unixsock plugin: bind failed: %s",
+                               strerror (errno));
+               close (sock_fd);
+               sock_fd = -1;
+               return (-1);
+       }
+
+       status = listen (sock_fd, 8);
+       if (status != 0)
+       {
+               syslog (LOG_ERR, "unixsock plugin: listen failed: %s",
+                               strerror (errno));
+               close (sock_fd);
+               sock_fd = -1;
+               return (-1);
+       }
+
+       do
+       {
+               struct group *g;
+
+               errno = 0;
+               g = getgrnam ((sock_group != NULL) ? sock_group : COLLECTD_GRP_NAME);
+
+               if (errno != 0)
+               {
+                       syslog (LOG_WARNING, "unixsock plugin: getgrnam (%s) failed: %s",
+                                       (sock_group != NULL) ? sock_group : COLLECTD_GRP_NAME,
+                                       strerror (errno));
+                       break;
+               }
+
+               if (g == NULL)
+                       break;
+
+               if (chown ((sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
+                                       (uid_t) -1, g->gr_gid) != 0)
+               {
+                       syslog (LOG_WARNING, "unixsock plugin: chown (%s, -1, %i) failed: %s",
+                                       (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
+                                       (int) g->gr_gid,
+                                       strerror (errno));
+               }
+       } while (0);
+
+       return (0);
+} /* int us_open_socket */
+
+static int us_handle_getval (FILE *fh, char **fields, int fields_num)
+{
+       char *hostname = fields[1];
+       char *plugin;
+       char *plugin_instance;
+       char *type;
+       char *type_instance;
+       char  name[4*DATA_MAX_NAME_LEN];
+       value_cache_t *vc;
+       int   status;
+       int   i;
+
+       if (fields_num != 2)
+               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++;
+       }
+
+       status = cache_alloc_name (name, sizeof (name),
+                       hostname, plugin, plugin_instance, type, type_instance);
+       if (status != 0)
+               return (-1);
+
+       pthread_mutex_lock (&cache_lock);
+
+       DBG ("vc = cache_search (%s)", name);
+       vc = cache_search (name);
+
+       if (vc == NULL)
+       {
+               DBG ("Did not find cache entry.");
+               fprintf (fh, "-1 No such value");
+       }
+       else
+       {
+               DBG ("Found cache entry.");
+               fprintf (fh, "%i", vc->values_num);
+               for (i = 0; i < vc->values_num; i++)
+               {
+                       fprintf (fh, " %s=", vc->ds->ds[i].name);
+                       if (vc->gauge[i] == NAN)
+                               fprintf (fh, "NaN");
+                       else
+                               fprintf (fh, "%12e", vc->gauge[i]);
+               }
+       }
+
+       /* Free the mutex as soon as possible and definitely before flushing */
+       pthread_mutex_unlock (&cache_lock);
+
+       fprintf (fh, "\n");
+       fflush (fh);
+
+       return (0);
+} /* int us_handle_getval */
+
+static void *us_handle_client (void *arg)
+{
+       int fd;
+       FILE *fh;
+       char buffer[1024];
+       char *fields[128];
+       int   fields_num;
+
+       fd = *((int *) arg);
+       free (arg);
+       arg = NULL;
+
+       DBG ("Reading from fd #%i", fd);
+
+       fh = fdopen (fd, "r+");
+       if (fh == NULL)
+       {
+               syslog (LOG_ERR, "unixsock plugin: fdopen failed: %s",
+                               strerror (errno));
+               close (fd);
+               pthread_exit ((void *) 1);
+       }
+
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               int len;
+
+               len = strlen (buffer);
+               while ((len > 0)
+                               && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
+                       buffer[--len] = '\0';
+
+               if (len == 0)
+                       continue;
+
+               DBG ("fgets -> buffer = %s; len = %i;", buffer, len);
+
+               fields_num = strsplit (buffer, fields,
+                               sizeof (fields) / sizeof (fields[0]));
+
+               if (fields_num < 1)
+               {
+                       close (fd);
+                       break;
+               }
+
+               if (strcasecmp (fields[0], "getval") == 0)
+               {
+                       us_handle_getval (fh, fields, fields_num);
+               }
+               else
+               {
+                       fprintf (fh, "Unknown command: %s\n", fields[0]);
+                       fflush (fh);
+               }
+       } /* while (fgets) */
+
+       DBG ("Exiting..");
+       close (fd);
+
+       pthread_exit ((void *) 0);
+} /* void *us_handle_client */
+
+static void *us_server_thread (void *arg)
+{
+       int  status;
+       int *remote_fd;
+       pthread_t th;
+       pthread_attr_t th_attr;
+
+       if (us_open_socket () != 0)
+               pthread_exit ((void *) 1);
+
+       while (42)
+       {
+               DBG ("Calling accept..");
+               status = accept (sock_fd, NULL, NULL);
+               if (status < 0)
+               {
+                       if (errno == EINTR)
+                               continue;
+
+                       syslog (LOG_ERR, "unixsock plugin: accept failed: %s",
+                                       strerror (errno));
+                       close (sock_fd);
+                       sock_fd = -1;
+                       pthread_exit ((void *) 1);
+               }
+
+               remote_fd = (int *) malloc (sizeof (int));
+               if (remote_fd == NULL)
+               {
+                       syslog (LOG_WARNING, "unixsock plugin: malloc failed: %s",
+                                       strerror (errno));
+                       close (status);
+                       continue;
+               }
+               *remote_fd = status;
+
+               DBG ("Spawning child to handle connection on fd #%i", *remote_fd);
+
+               pthread_attr_init (&th_attr);
+               pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED);
+
+               status = pthread_create (&th, &th_attr, us_handle_client, (void *) remote_fd);
+               if (status != 0)
+               {
+                       syslog (LOG_WARNING, "unixsock plugin: pthread_create failed: %s",
+                                       strerror (status));
+                       close (*remote_fd);
+                       free (remote_fd);
+                       continue;
+               }
+       } /* while (42) */
+
+       return ((void *) 0);
+} /* void *us_server_thread */
+
+static int us_config (const char *key, const char *val)
+{
+       if (strcasecmp (key, "SocketFile") == 0)
+       {
+               sfree (sock_file);
+               sock_file = strdup (val);
+       }
+       else if (strcasecmp (key, "SocketGroup") == 0)
+       {
+               sfree (sock_group);
+               sock_group = strdup (val);
+       }
+       else if (strcasecmp (key, "SocketPerms") == 0)
+       {
+               sock_perms = (int) strtol (val, NULL, 8);
+       }
+       else
+       {
+               return (-1);
+       }
+
+       return (0);
+} /* int us_config */
+
+static int us_init (void)
+{
+       int status;
+
+       status = pthread_create (&listen_thread, NULL, us_server_thread, NULL);
+       if (status != 0)
+       {
+               syslog (LOG_ERR, "unixsock plugin: pthread_create failed: %s",
+                               strerror (status));
+               return (-1);
+       }
+
+       return (0);
+} /* int us_init */
+
+static int us_shutdown (void)
+{
+       void *ret;
+
+       if (listen_thread >= 0)
+       {
+               pthread_kill (listen_thread, SIGTERM);
+               pthread_join (listen_thread, &ret);
+       }
+
+       plugin_unregister_init ("unixsock");
+       plugin_unregister_write ("unixsock");
+       plugin_unregister_shutdown ("unixsock");
+
+       return (0);
+} /* int us_shutdown */
+
+static int us_write (const data_set_t *ds, const value_list_t *vl)
+{
+       cache_update (ds, vl);
+       cache_flush (2 * atoi (COLLECTD_STEP));
+
+       return (0);
+}
+
+void module_register (void)
+{
+       plugin_register_config ("unixsock", us_config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("unixsock", us_init);
+       plugin_register_write ("unixsock", us_write);
+       plugin_register_shutdown ("unixsock", us_shutdown);
+} /* void module_register (void) */
+
+/* vim: set sw=4 ts=4 sts=4 tw=78 : */