Merge branch 'master' into collectd-4
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Mon, 15 Jan 2007 17:36:26 +0000 (18:36 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Mon, 15 Jan 2007 17:36:26 +0000 (18:36 +0100)
Conflicts:

src/sensors.c

22 files changed:
configure.in
src/Makefile.am
src/collectd.c
src/collectd.conf.pod
src/collectd.h
src/common.c
src/common.h
src/configfile.c
src/configfile.h
src/dns.c
src/load.c
src/network.c
src/network.h
src/nfs.c
src/ntpd.c
src/plugin.c
src/plugin.h
src/rrdtool.c [new file with mode: 0644]
src/sensors.c
src/traffic.c
src/utils_llist.c [new file with mode: 0644]
src/utils_llist.h [new file with mode: 0644]

index 760c918..6a0838b 100644 (file)
@@ -44,7 +44,7 @@ AC_CHECK_HEADERS(assert.h)
 AC_CHECK_HEADERS(sys/types.h)
 AC_CHECK_HEADERS(sys/socket.h)
 AC_CHECK_HEADERS(sys/select.h)
-AC_CHECK_HEADERS(sys/poll.h)
+AC_CHECK_HEADERS(poll.h)
 AC_CHECK_HEADERS(netdb.h)
 AC_CHECK_HEADERS(arpa/inet.h)
 AC_CHECK_HEADERS(sys/resource.h)
@@ -994,6 +994,7 @@ AC_COLLECTD([mbmon],     [disable], [module], [motherboard monitor statistics])
 AC_COLLECTD([memory],    [disable], [module], [memory statistics])
 AC_COLLECTD([multimeter],[disable], [module], [multimeter statistics])
 AC_COLLECTD([mysql],     [disable], [module], [mysql statistics])
+AC_COLLECTD([network],   [disable], [module], [network functionality])
 AC_COLLECTD([nfs],       [disable], [module], [nfs statistics])
 AC_COLLECTD([ntpd],      [disable], [module], [nfs statistics])
 AC_COLLECTD([ping],      [disable], [module], [ping statistics])
@@ -1047,6 +1048,7 @@ Configuration:
     memory  . . . . . . $enable_memory
     multimeter  . . . . $enable_multimeter
     mysql . . . . . . . $enable_mysql
+    network . . . . . . $enable_network
     nfs . . . . . . . . $enable_nfs
     ntpd  . . . . . . . $enable_ntpd
     ping  . . . . . . . $enable_ping
index d9b3b9c..4ea3be0 100644 (file)
@@ -12,9 +12,9 @@ sbin_PROGRAMS = collectd
 collectd_SOURCES = collectd.c collectd.h \
                   utils_debug.c utils_debug.h \
                   utils_mount.c utils_mount.h \
+                  utils_llist.c utils_llist.h \
                   utils_ignorelist.c utils_ignorelist.h \
                   common.c common.h \
-                  network.c network.h \
                   plugin.c plugin.h \
                   configfile.c configfile.h
 collectd_CPPFLAGS = $(LTDLINCL)
@@ -30,9 +30,6 @@ endif
 
 # Link to these libraries..
 collectd_LDFLAGS = -export-dynamic
-if BUILD_WITH_RRDTOOL
-collectd_LDFLAGS += -lm -lrrd
-endif
 if BUILD_WITH_LIBRT
 collectd_LDFLAGS += -lrt
 endif
@@ -273,6 +270,20 @@ collectd_LDADD += "-dlopen" mysql.la
 collectd_DEPENDENCIES += mysql.la
 endif
 
+if BUILD_MODULE_NETWORK
+pkglib_LTLIBRARIES += network.la
+network_la_SOURCES = network.c
+network_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBSOCKET
+network_la_LDFLAGS += -lsocket
+endif
+if BUILD_WITH_LIBPTHREAD
+network_la_LDFLAGS += -lpthread
+endif
+collectd_LDADD += "-dlopen" network.la
+collectd_DEPENDENCIES += network.la
+endif
+
 if BUILD_MODULE_NFS
 pkglib_LTLIBRARIES += nfs.la
 nfs_la_SOURCES = nfs.c
@@ -316,6 +327,14 @@ collectd_LDADD += "-dlopen" processes.la
 collectd_DEPENDENCIES += processes.la
 endif
 
+if BUILD_WITH_RRDTOOL
+pkglib_LTLIBRARIES += rrdtool.la
+rrdtool_la_SOURCES = rrdtool.c
+rrdtool_la_LDFLAGS = -module -avoid-version -lrrd
+collectd_LDADD += "-dlopen" rrdtool.la
+collectd_DEPENDENCIES += rrdtool.la
+endif
+
 if BUILD_MODULE_SENSORS
 pkglib_LTLIBRARIES += sensors.la
 sensors_la_SOURCES = sensors.c
index a93cc28..e130647 100644 (file)
@@ -4,8 +4,7 @@
  *
  * 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
@@ -223,6 +222,8 @@ static int start_client (void)
 #if HAVE_LIBRRD
 static int start_server (void)
 {
+       /* FIXME */
+#if 0
        char *host;
        char *type;
        char *instance;
@@ -251,6 +252,7 @@ static int start_server (void)
                if (values   != NULL) free (values);   values   = NULL;
        }
        
+#endif
        return (0);
 } /* static int start_server (void) */
 #endif /* HAVE_LIBRRD */
index cffb7ad..5c8de4f 100644 (file)
@@ -306,6 +306,20 @@ Sets the Time-To-Live of generated ICMP packets.
 
 =back
 
+=head2 Plugin C<rrdtool>
+
+=over 4
+
+=item B<CacheTimeout> I<Seconds>
+
+If this option is set to a value greater than zero, the C<rrdtool>-plugin will
+write values every I<Seconds> seconds to the RRD-files. Writing multiple values
+at once reduces IO-operations and thus lessens the load produced by updating
+the files. The tradeoff is that the graphs kind of "drag behind" and that more
+memory is used.
+
+=back
+
 =head2 Plugin C<sensors>
 
 =over 4
index a5c8169..3c8d351 100644 (file)
@@ -4,8 +4,7 @@
  *
  * 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
index 5487b32..cf2a639 100644 (file)
 #  include <math.h>
 #endif
 
+/* for ntohl and htonl */
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
 extern int operating_mode;
 
 #ifdef HAVE_LIBKSTAT
 extern kstat_ctl_t *kc;
 #endif
 
-#ifdef HAVE_LIBRRD
-#if 0
-static char *rra_def[] =
-{
-               "RRA:AVERAGE:0.0:1:1500",
-               "RRA:AVERAGE:0.2:6:1500",
-               "RRA:AVERAGE:0.1:180:1680",
-               "RRA:AVERAGE:0.1:2160:1520",
-               "RRA:MIN:0.0:1:1500",
-               "RRA:MIN:0.2:6:1500",
-               "RRA:MIN:0.1:180:1680",
-               "RRA:MIN:0.1:2160:1520",
-               "RRA:MAX:0.0:1:1500",
-               "RRA:MAX:0.2:6:1500",
-               "RRA:MAX:0.1:180:1680",
-               "RRA:MAX:0.1:2160:1520",
-               NULL
-};
-static int rra_num = 12;
-#endif
-
-static int rra_timespans[] =
-{
-       3600,
-       86400,
-       604800,
-       2678400,
-       31622400,
-       0
-};
-static int rra_timespans_num = 5;
-
-static char *rra_types[] =
-{
-       "AVERAGE",
-       "MIN",
-       "MAX",
-       NULL
-};
-static int rra_types_num = 3;
-#endif /* HAVE_LIBRRD */
-
 void sstrncpy (char *d, const char *s, int len)
 {
        strncpy (d, s, len);
@@ -320,7 +283,7 @@ int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct times
        return (0);
 }
 
-static int check_create_dir (const char *file_orig)
+int check_create_dir (const char *file_orig)
 {
        struct stat statbuf;
 
@@ -424,89 +387,6 @@ static int check_create_dir (const char *file_orig)
        return (0);
 }
 
-/* * * * *
- * Magic *
- * * * * */
-#if HAVE_LIBRRD
-static int rra_get (char ***ret)
-{
-       static char **rra_def = NULL;
-       static int rra_num = 0;
-
-       int rra_max = rra_timespans_num * rra_types_num;
-
-       int step;
-       int rows;
-       int span;
-
-       int cdp_num;
-       int cdp_len;
-       int i, j;
-
-       char buffer[64];
-
-       if ((rra_num != 0) && (rra_def != NULL))
-       {
-               *ret = rra_def;
-               return (rra_num);
-       }
-
-       if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
-               return (-1);
-       memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
-
-       step = atoi (COLLECTD_STEP);
-       rows = atoi (COLLECTD_ROWS);
-
-       if ((step <= 0) || (rows <= 0))
-       {
-               *ret = NULL;
-               return (-1);
-       }
-
-       cdp_len = 0;
-       for (i = 0; i < rra_timespans_num; i++)
-       {
-               span = rra_timespans[i];
-
-               if ((span / step) < rows)
-                       continue;
-
-               if (cdp_len == 0)
-                       cdp_len = 1;
-               else
-                       cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
-
-               cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
-
-               for (j = 0; j < rra_types_num; j++)
-               {
-                       if (rra_num >= rra_max)
-                               break;
-
-                       if (snprintf (buffer, sizeof(buffer), "RRA:%s:%3.1f:%u:%u",
-                                               rra_types[j], COLLECTD_XFF,
-                                               cdp_len, cdp_num) >= sizeof (buffer))
-                       {
-                               syslog (LOG_ERR, "rra_get: Buffer would have been truncated.");
-                               continue;
-                       }
-
-                       rra_def[rra_num++] = sstrdup (buffer);
-               }
-       }
-
-#if COLLECT_DEBUG
-       DBG ("rra_num = %i", rra_num);
-       for (i = 0; i < rra_num; i++)
-               DBG ("  %s", rra_def[i]);
-#endif
-
-       *ret = rra_def;
-       return (rra_num);
-}
-#endif /* HAVE_LIBRRD */
-
 static int log_create_file (char *filename, char **ds_def, int ds_num)
 {
        FILE *log;
@@ -560,7 +440,7 @@ static int log_create_file (char *filename, char **ds_def, int ds_num)
        return 0;
 }
 
-static int log_update_file (char *host, char *file, char *values,
+int log_update_file (char *host, char *file, char *values,
                char **ds_def, int ds_num)
 {
        char *tmp;
@@ -640,121 +520,6 @@ static int log_update_file (char *host, char *file, char *values,
        return (0);
 } /* int log_update_file */
 
-#if HAVE_LIBRRD
-static int rrd_create_file (char *filename, char **ds_def, int ds_num)
-{
-       char **argv;
-       int argc;
-       char **rra_def;
-       int rra_num;
-       int i, j;
-       int status = 0;
-
-       if (check_create_dir (filename))
-               return (-1);
-
-       if ((rra_num = rra_get (&rra_def)) < 1)
-       {
-               syslog (LOG_ERR, "rra_create failed: Could not calculate RRAs");
-               return (-1);
-       }
-
-       argc = ds_num + rra_num + 4;
-
-       if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
-       {
-               syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno));
-               return (-1);
-       }
-
-       argv[0] = "create";
-       argv[1] = filename;
-       argv[2] = "-s";
-       argv[3] = COLLECTD_STEP;
-
-       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;
-
-       optind = 0; /* bug in librrd? */
-       rrd_clear_error ();
-       if (rrd_create (argc, argv) == -1)
-       {
-               syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ());
-               status = -1;
-       }
-
-       free (argv);
-
-       return (status);
-}
-#endif /* HAVE_LIBRRD */
-
-int rrd_update_file (char *host, char *file, char *values,
-               char **ds_def, int ds_num)
-{
-#if HAVE_LIBRRD
-       struct stat statbuf;
-       char full_file[1024];
-       char *argv[4] = { "update", full_file, values, NULL };
-#endif /* HAVE_LIBRRD */
-
-       /* I'd rather have a function `common_update_file' to make this
-        * decission, but for that we'd need to touch all plugins.. */
-       if (operating_mode == MODE_LOG)
-               return (log_update_file (host, file, values,
-                                       ds_def, ds_num));
-
-#if HAVE_LIBRRD
-       /* host == NULL => local mode */
-       if (host != NULL)
-       {
-               if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
-                       return (-1);
-       }
-       else
-       {
-               if (snprintf (full_file, 1024, "%s", file) >= 1024)
-                       return (-1);
-       }
-
-       if (stat (full_file, &statbuf) == -1)
-       {
-               if (errno == ENOENT)
-               {
-                       if (rrd_create_file (full_file, ds_def, ds_num))
-                               return (-1);
-               }
-               else
-               {
-                       syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
-                       return (-1);
-               }
-       }
-       else if (!S_ISREG (statbuf.st_mode))
-       {
-               syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
-               return (-1);
-       }
-
-       optind = 0; /* bug in librrd? */
-       rrd_clear_error ();
-       if (rrd_update (3, argv) == -1)
-       {
-               syslog (LOG_WARNING, "rrd_update failed: %s: %s", full_file, rrd_get_error ());
-               return (-1);
-       }
-       return (0);
-/* #endif HAVE_LIBRRD */
-
-#else
-       syslog (LOG_ERR, "`rrd_update_file' was called, but collectd isn't linked against librrd!");
-       return (-1);
-#endif
-}
 
 #ifdef HAVE_LIBKSTAT
 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
@@ -841,3 +606,21 @@ long long get_kstat_value (kstat_t *ksp, char *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 htonll (unsigned long long n)
+{
+#if __BYTE_ORDER == __BIG_ENDIAN
+       return (n);
+#else
+       return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
+#endif
+}
index 2be2ff5..6cf8667 100644 (file)
@@ -145,12 +145,14 @@ int escape_slashes (char *buf, int buf_len);
 /* FIXME: `timeval_sub_timespec' needs a description */
 int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret);
 
-int rrd_update_file (char *host, char *file, char *values,
-               char **ds_def, int ds_num);
+int check_create_dir (const char *file_orig);
 
 #ifdef HAVE_LIBKSTAT
 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name);
 long long get_kstat_value (kstat_t *ksp, char *name);
 #endif
 
+unsigned long long ntohll (unsigned long long n);
+unsigned long long htonll (unsigned long long n);
+
 #endif /* COMMON_H */
index 40b46fa..5472de4 100644 (file)
@@ -50,9 +50,9 @@ extern int operating_mode;
 
 typedef struct cf_callback
 {
-       char  *type;
-       int  (*callback) (char *, char *);
-       char **keys;
+       const char  *type;
+       int  (*callback) (const char *, const char *);
+       const char **keys;
        int    keys_num;
        struct cf_callback *next;
 } cf_callback_t;
@@ -152,7 +152,7 @@ static int cf_dispatch (char *type, const char *orig_key, const char *orig_value
        return (ret);
 }
 
-void cf_unregister (char *type)
+void cf_unregister (const char *type)
 {
        cf_callback_t *this, *prev;
 
@@ -171,9 +171,9 @@ void cf_unregister (char *type)
                }
 }
 
-void cf_register (char *type,
-               int (*callback) (char *, char *),
-               char **keys, int keys_num)
+void cf_register (const char *type,
+               int (*callback) (const char *, const char *),
+               const char **keys, int keys_num)
 {
        cf_callback_t *cf_cb;
        char buf[64];
@@ -375,48 +375,6 @@ static int cf_callback_mode_loadmodule (const char *shortvar, const char *var,
        return (LC_CBRET_OKAY);
 }
 
-static int cf_callback_socket (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
-{
-       char *buffer;
-
-       char *fields[3];
-       int   numfields;
-
-       char *node;
-       char *service = NET_DEFAULT_PORT;
-
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
-
-       buffer = strdup (value);
-       if (buffer == NULL)
-               return (LC_CBRET_ERROR);
-
-       numfields = strsplit (buffer, fields, 3);
-
-       if ((numfields != 1) && (numfields != 2))
-       {
-               syslog (LOG_ERR, "Invalid number of arguments to `%s'",
-                               shortvar);
-               free (buffer);
-               return (LC_CBRET_ERROR);
-       }
-
-       node = fields[0];
-       if (numfields == 2)
-               service = fields[1];
-
-       /* Still return `LC_CBRET_OKAY' because this is not an syntax error */
-       if (network_create_socket (node, service) < 1)
-               syslog (LOG_ERR, "network_create_socket (%s, %s) failed",
-                               node, service);
-
-       free (buffer);
-
-       return (LC_CBRET_OKAY);
-}
-
 /*
  * `cf_callback_plugin'
  *   Start/end section `plugin'
@@ -523,11 +481,6 @@ static void cf_init (void)
        lc_register_callback ("LoadPlugin", SHORTOPT_NONE,
                        LC_VAR_STRING, cf_callback_mode_loadmodule, NULL);
 
-       lc_register_callback ("Listen", SHORTOPT_NONE,
-                       LC_VAR_STRING, cf_callback_socket, NULL);
-       lc_register_callback ("Server", SHORTOPT_NONE,
-                       LC_VAR_STRING, cf_callback_socket, NULL);
-
        for (i = 0; i < cf_mode_num; i++)
        {
                cf_mode_item_t *item;
index 0bb11b2..fec1cf4 100644 (file)
@@ -31,7 +31,7 @@
  *  `type'      Name of the plugin (must be the same as passed to
  *              `plugin_register'
  */
-void cf_unregister (char *type);
+void cf_unregister (const char *type);
 
 /*
  * DESCRIPTION
@@ -57,9 +57,9 @@ void cf_unregister (char *type);
  *  exists for each `type' at any time. This means that `cf_register' may be
  *  called multiple times, but only the last call will have an effect.
  */
-void cf_register (char *type,
-               int (*callback) (char *, char *),
-               char **keys, int keys_num);
+void cf_register (const char *type,
+               int (*callback) (const char *, const char *),
+               const char **keys, int keys_num);
 
 /*
  * DESCRIPTION
index cd068fb..5e114b1 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -32,7 +32,7 @@
 #if HAVE_LIBPCAP && HAVE_LIBPTHREAD
 # include <pthread.h>
 # include <pcap.h>
-# include <sys/poll.h>
+# include <poll.h>
 # define DNS_HAVE_READ 1
 #else
 # define DNS_HAVE_READ 0
index 019f952..3e60a43 100644 (file)
 #endif
 #endif /* defined(HAVE_GETLOADAVG) */
 
-static char *load_file = "load.rrd";
-
-static char *ds_def[] =
+static data_source_t dsrc[3] =
 {
-       "DS:shortterm:GAUGE:"COLLECTD_HEARTBEAT":0:100",
-       "DS:midterm:GAUGE:"COLLECTD_HEARTBEAT":0:100",
-       "DS:longterm:GAUGE:"COLLECTD_HEARTBEAT":0:100",
-       NULL
+       {"shortterm", DS_TYPE_GAUGE, 0.0, 100.0},
+       {"midterm",   DS_TYPE_GAUGE, 0.0, 100.0},
+       {"longterm",  DS_TYPE_GAUGE, 0.0, 100.0}
 };
-static int ds_num = 3;
-
-static void load_init (void)
-{
-       return;
-}
 
-static void load_write (char *host, char *inst, char *val)
+static data_set_t ds =
 {
-       rrd_update_file (host, load_file, val, ds_def, ds_num);
-}
+       "load", 3, dsrc
+};
 
 #if LOAD_HAVE_READ
-#define BUFSIZE 256
 static void load_submit (double snum, double mnum, double lnum)
 {
-       char buf[BUFSIZE];
-
-       if (snprintf (buf, BUFSIZE, "%u:%.2f:%.2f:%.2f", (unsigned int) curtime,
-                               snum, mnum, lnum) >= BUFSIZE)
-               return;
-
-       plugin_submit (MODULE_NAME, "-", buf);
+       value_t values[3];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = snum;
+       values[1].gauge = mnum;
+       values[2].gauge = lnum;
+
+       vl.values = values;
+       vl.values_len = 3;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname);
+       strcpy (vl.plugin, "load");
+       strcpy (vl.plugin_instance, "");
+       strcpy (vl.type_instance, "");
+
+       plugin_dispatch_values ("load", &vl);
 }
-#undef BUFSIZE
 
-static void load_read (void)
+static int load_read (void)
 {
 #if defined(HAVE_GETLOADAVG)
        double load[3];
@@ -139,14 +137,17 @@ static void load_read (void)
 
        load_submit (snum, mnum, lnum);
 #endif /* HAVE_LIBSTATGRAB */
+
+       return (0);
 }
-#else
-# define load_read NULL
 #endif /* LOAD_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, load_init, load_read, load_write);
+       plugin_register_data_set (&ds);
+#if LOAD_HAVE_READ
+       plugin_register_read ("load", load_read);
+#endif
 }
 
 #undef MODULE_NAME
index e9ba84b..11c9718 100644 (file)
@@ -4,8 +4,7 @@
  *
  * 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
  *   Florian octo Forster <octo at verplant.org>
  **/
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <syslog.h>
-#include <errno.h>
-
-#include "network.h"
+#include "collectd.h"
+#include "plugin.h"
 #include "common.h"
 #include "configfile.h"
 #include "utils_debug.h"
 
+#include "network.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#if HAVE_NETDB_H
+# include <netdb.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#if HAVE_POLL_H
+# include <poll.h>
+#endif
+
 /* 1500 - 40 - 8  =  Ethernet packet - IPv6 header - UDP header */
 /* #define BUFF_SIZE 1452 */
 
 
 #define BUFF_SIZE 4096
 
-extern int operating_mode;
-
+/*
+ * Private data types
+ */
 typedef struct sockent
 {
        int                      fd;
-       int                      mode;
        struct sockaddr_storage *addr;
        socklen_t                addrlen;
        struct sockent          *next;
 } sockent_t;
 
-static sockent_t *socklist_head = NULL;
+/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-------+-----------------------+-------------------------------+
+ * ! Ver.  !                       ! Length                        !
+ * +-------+-----------------------+-------------------------------+
+ */
+struct part_header_s
+{
+       uint16_t type;
+       uint16_t length;
+};
+typedef struct part_header_s part_header_t;
+
+/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-------------------------------+-------------------------------+
+ * ! Type                          ! Length                        !
+ * +-------------------------------+-------------------------------+
+ * : (Length - 4) Bytes                                            :
+ * +---------------------------------------------------------------+
+ */
+struct part_string_s
+{
+       part_header_t *head;
+       char *value;
+};
+typedef struct part_string_s part_string_t;
+
+/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-------------------------------+-------------------------------+
+ * ! Type                          ! Length                        !
+ * +-------------------------------+-------------------------------+
+ * : (Length - 4 == 2 || 4 || 8) Bytes                             :
+ * +---------------------------------------------------------------+
+ */
+struct part_number_s
+{
+       part_header_t *head;
+       uint64_t *value;
+};
+typedef struct part_number_s part_number_t;
+
+/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-------------------------------+-------------------------------+
+ * ! Type                          ! Length                        !
+ * +-------------------------------+---------------+---------------+
+ * ! Num of values                 ! Type0         ! Type1         !
+ * +-------------------------------+---------------+---------------+
+ * ! Value0                                                        !
+ * !                                                               !
+ * +---------------------------------------------------------------+
+ * ! Value1                                                        !
+ * !                                                               !
+ * +---------------------------------------------------------------+
+ */
+struct part_values_s
+{
+       part_header_t *head;
+       uint16_t *num_values;
+       uint8_t  *values_types;
+       value_t  *values;
+};
+typedef struct part_values_s part_values_t;
+
+/*
+ * Private variables
+ */
+static const char *config_keys[] =
+{
+       "Listen",
+       "Server",
+       "TimeToLive",
+       NULL
+};
+static int config_keys_num = 3;
+
+static int network_config_ttl = 0;
+
+static sockent_t *sending_sockets = NULL;
+
+static struct pollfd *listen_sockets = NULL;
+static int listen_sockets_num = 0;
+static pthread_t listen_thread = 0;
+static int listen_loop = 0;
+
+/*
+ * Private functions
+ */
+static int write_part_values (char **ret_buffer, int *ret_buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       part_values_t pv;
+       int i;
+
+       i = 6 + (9 * vl->values_len);
+       if (*ret_buffer_len < i)
+               return (-1);
+       *ret_buffer_len -= i;
+
+       pv.head = (part_header_t *) *ret_buffer;
+       pv.num_values = (uint16_t *) (pv.head + 1);
+       pv.values_types = (uint8_t *) (pv.num_values + 1);
+       pv.values = (value_t *) (pv.values_types + vl->values_len);
+       *ret_buffer = (void *) (pv.values + vl->values_len);
+
+       pv.head->type = htons (TYPE_VALUES);
+       pv.head->length = htons (6 + (9 * vl->values_len));
+       *pv.num_values = htons ((uint16_t) vl->values_len);
+       
+       for (i = 0; i < vl->values_len; i++)
+       {
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+               {
+                       pv.values_types[i] = DS_TYPE_COUNTER;
+                       pv.values[i].counter = htonll (vl->values[i].counter);
+               }
+               else
+               {
+                       pv.values_types[i] = DS_TYPE_GAUGE;
+                       pv.values[i].gauge = vl->values[i].gauge;
+               }
+       } /* for (values) */
 
-static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
+       return (0);
+} /* int write_part_values */
+
+static int write_part_number (char **ret_buffer, int *ret_buffer_len,
+               int type, uint64_t value)
 {
-       char *ttl_str;
-       int   ttl_int;
+       part_number_t pn;
 
-       ttl_str = cf_get_option ("TimeToLive", NULL);
-       if (ttl_str == NULL)
+       if (*ret_buffer_len < 12)
                return (-1);
 
-       ttl_int = atoi (ttl_str);
-       if ((ttl_int < 1) || (ttl_int > 255))
+       pn.head = (part_header_t *) *ret_buffer;
+       pn.value = (uint64_t *) (pn.head + 1);
+
+       pn.head->type = htons (type);
+       pn.head->length = htons (12);
+       *pn.value = htonll (value);
+
+       *ret_buffer = (char *) (pn.value + 1);
+       *ret_buffer_len -= 12;
+
+       return (0);
+} /* int write_part_number */
+
+static int write_part_string (char **ret_buffer, int *ret_buffer_len,
+               int type, const char *str, int str_len)
+{
+       part_string_t ps;
+       int len;
+
+       if (str_len < 1)
+               return (-1);
+
+       len = 4 + str_len + 1;
+       if (*ret_buffer_len < len)
+               return (-1);
+       *ret_buffer_len -= len;
+
+       ps.head = (part_header_t *) *ret_buffer;
+       ps.value = (char *) (ps.head + 1);
+
+       ps.head->type = htons ((uint16_t) type);
+       ps.head->length = htons ((uint16_t) str_len + 5);
+       memcpy (ps.value, str, str_len);
+       ps.value[str_len] = '\0';
+       *ret_buffer = (void *) (ps.value + (str_len + 1));
+
+       return (0);
+} /* int write_part_string */
+
+static int parse_part_values (void **ret_buffer, int *ret_buffer_len,
+               value_t **ret_values, int *ret_num_values)
+{
+       char *buffer = *ret_buffer;
+       int   buffer_len = *ret_buffer_len;
+       part_values_t pv;
+       int   i;
+
+       uint16_t h_length;
+       uint16_t h_type;
+       uint16_t h_num;
+
+       if (buffer_len < (15))
+       {
+               DBG ("packet is too short: buffer_len = %i", buffer_len);
+               return (-1);
+       }
+
+       pv.head = (part_header_t *) buffer;
+       h_length = ntohs (pv.head->length);
+       h_type = ntohs (pv.head->type);
+
+       assert (h_type == TYPE_VALUES);
+
+       pv.num_values = (uint16_t *) (pv.head + 1);
+       h_num = ntohs (*pv.num_values);
+
+       if (h_num != ((h_length - 6) / 9))
        {
-               syslog (LOG_WARNING, "A TTL value of %i is invalid.", ttl_int);
+               DBG ("`length' and `num of values' don't match");
                return (-1);
        }
 
-       DBG ("ttl = %i", ttl_int);
+       pv.values_types = (uint8_t *) (pv.num_values + 1);
+       pv.values = (value_t *) (pv.values_types + h_num);
+
+       for (i = 0; i < h_num; i++)
+               if (pv.values_types[i] == DS_TYPE_COUNTER)
+                       pv.values[i].counter = ntohll (pv.values[i].counter);
+
+       *ret_buffer     = (void *) (pv.values + h_num);
+       *ret_buffer_len = buffer_len - h_length;
+       *ret_num_values = h_num;
+       *ret_values     = pv.values;
+
+       return (0);
+} /* int parse_part_values */
+
+static int parse_part_number (void **ret_buffer, int *ret_buffer_len,
+               uint64_t *value)
+{
+       part_number_t pn;
+       uint16_t len;
+
+       pn.head = (part_header_t *) *ret_buffer;
+       pn.value = (uint64_t *) (pn.head + 1);
+
+       len = ntohs (pn.head->length);
+       if (len != 12)
+               return (-1);
+       if (len > *ret_buffer_len)
+               return (-1);
+       *value = ntohll (*pn.value);
+
+       *ret_buffer = (void *) (pn.value + 1);
+       *ret_buffer_len -= len;
+
+       return (0);
+} /* int parse_part_number */
+
+static int parse_part_string (void **ret_buffer, int *ret_buffer_len,
+               char *output, int output_len)
+{
+       char *buffer = *ret_buffer;
+       int   buffer_len = *ret_buffer_len;
+       part_string_t ps;
+
+       uint16_t h_length;
+       uint16_t h_type;
+
+       DBG ("ret_buffer = %p; ret_buffer_len = %i; output = %p; output_len = %i;",
+                       *ret_buffer, *ret_buffer_len,
+                       (void *) output, output_len);
+
+       ps.head = (part_header_t *) buffer;
+
+       h_length = ntohs (ps.head->length);
+       h_type = ntohs (ps.head->type);
+
+       DBG ("length = %hu; type = %hu;", h_length, h_type);
+
+       if (buffer_len < h_length)
+       {
+               DBG ("packet is too short");
+               return (-1);
+       }
+       assert ((h_type == TYPE_HOST)
+                       || (h_type == TYPE_PLUGIN)
+                       || (h_type == TYPE_PLUGIN_INSTANCE)
+                       || (h_type == TYPE_TYPE)
+                       || (h_type == TYPE_TYPE_INSTANCE));
+
+       ps.value = buffer + 4;
+       if (ps.value[h_length - 5] != '\0')
+       {
+               DBG ("String does not end with a nullbyte");
+               return (-1);
+       }
+
+       if (output_len < (h_length - 4))
+       {
+               DBG ("output buffer is too small");
+               return (-1);
+       }
+       strcpy (output, ps.value);
+
+       DBG ("output = %s", output);
+
+       *ret_buffer = (void *) (buffer + h_length);
+       *ret_buffer_len = buffer_len - h_length;
+
+       return (0);
+} /* int parse_part_string */
+
+static int parse_packet (void *buffer, int buffer_len)
+{
+       part_header_t *header;
+       int status;
+
+       value_list_t vl = VALUE_LIST_INIT;
+       char type[DATA_MAX_NAME_LEN];
+
+       DBG ("buffer = %p; buffer_len = %i;", buffer, buffer_len);
+
+       memset (&vl, '\0', sizeof (vl));
+       memset (&type, '\0', sizeof (type));
+       status = 0;
+
+       while ((status == 0) && (buffer_len > sizeof (part_header_t)))
+       {
+               header = (part_header_t *) buffer;
+
+               if (ntohs (header->length) > buffer_len)
+                       break;
+
+               if (header->type == htons (TYPE_VALUES))
+               {
+                       status = parse_part_values (&buffer, &buffer_len,
+                                       &vl.values, &vl.values_len);
+
+                       if (status != 0)
+                       {
+                               DBG ("parse_part_values failed.");
+                               break;
+                       }
+
+                       if ((vl.time > 0)
+                                       && (strlen (vl.host) > 0)
+                                       && (strlen (vl.plugin) > 0)
+                                       && (strlen (type) > 0))
+                       {
+                               DBG ("dispatching values");
+                               plugin_dispatch_values (type, &vl);
+                       }
+                       else
+                       {
+                               DBG ("NOT dispatching values");
+                       }
+               }
+               else if (header->type == ntohs (TYPE_TIME))
+               {
+                       uint64_t tmp = 0;
+                       status = parse_part_number (&buffer, &buffer_len, &tmp);
+                       if (status == 0)
+                               vl.time = (time_t) tmp;
+               }
+               else if (header->type == ntohs (TYPE_HOST))
+               {
+                       status = parse_part_string (&buffer, &buffer_len,
+                                       vl.host, sizeof (vl.host));
+               }
+               else if (header->type == ntohs (TYPE_PLUGIN))
+               {
+                       status = parse_part_string (&buffer, &buffer_len,
+                                       vl.plugin, sizeof (vl.plugin));
+               }
+               else if (header->type == ntohs (TYPE_PLUGIN_INSTANCE))
+               {
+                       status = parse_part_string (&buffer, &buffer_len,
+                                       vl.plugin_instance, sizeof (vl.plugin_instance));
+               }
+               else if (header->type == ntohs (TYPE_TYPE))
+               {
+                       status = parse_part_string (&buffer, &buffer_len,
+                                       type, sizeof (type));
+               }
+               else if (header->type == ntohs (TYPE_TYPE_INSTANCE))
+               {
+                       status = parse_part_string (&buffer, &buffer_len,
+                                       vl.type_instance, sizeof (vl.type_instance));
+               }
+               else
+               {
+                       DBG ("Unknown part type: 0x%0hx", header->type);
+                       buffer = ((char *) buffer) + header->length;
+               }
+       } /* while (buffer_len > sizeof (part_header_t)) */
+
+       return (0);
+} /* int parse_packet */
+
+static void free_sockent (sockent_t *se)
+{
+       sockent_t *next;
+       while (se != NULL)
+       {
+               next = se->next;
+               free (se->addr);
+               free (se);
+               se = next;
+       }
+} /* void free_sockent */
+
+/*
+ * int network_set_ttl
+ *
+ * Set the `IP_MULTICAST_TTL', `IP_TTL', `IPV6_MULTICAST_HOPS' or
+ * `IPV6_UNICAST_HOPS', depending on which option is applicable.
+ *
+ * The `struct addrinfo' is used to destinguish between unicast and multicast
+ * sockets.
+ */
+static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
+{
+       if ((network_config_ttl < 1) || (network_config_ttl > 255))
+               return (-1);
+
+       DBG ("ttl = %i", network_config_ttl);
 
        if (ai->ai_family == AF_INET)
        {
@@ -91,7 +495,8 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
                        optname = IP_TTL;
 
                if (setsockopt (se->fd, IPPROTO_IP, optname,
-                                       &ttl_int, sizeof (ttl_int)) == -1)
+                                       &network_config_ttl,
+                                       sizeof (network_config_ttl)) == -1)
                {
                        syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
                        return (-1);
@@ -109,7 +514,8 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
                        optname = IPV6_UNICAST_HOPS;
 
                if (setsockopt (se->fd, IPPROTO_IPV6, optname,
-                                       &ttl_int, sizeof (ttl_int)) == -1)
+                                       &network_config_ttl,
+                                       sizeof (network_config_ttl)) == -1)
                {
                        syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
                        return (-1);
@@ -117,7 +523,7 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
        }
 
        return (0);
-}
+} /* int network_set_ttl */
 
 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
 {
@@ -200,31 +606,21 @@ static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
        }
 
        return (0);
-}
+} /* int network_bind_socket */
 
-int network_create_socket (const char *node, const char *service)
+static sockent_t *network_create_socket (const char *node,
+               const char *service,
+               int listen)
 {
-       sockent_t *socklist_tail;
-
        struct addrinfo  ai_hints;
        struct addrinfo *ai_list, *ai_ptr;
        int              ai_return;
 
-       int num_added = 0;
+       sockent_t *se_head = NULL;
+       sockent_t *se_tail = NULL;
 
        DBG ("node = %s, service = %s", node, service);
 
-       if (operating_mode == MODE_LOCAL || operating_mode == MODE_LOG)
-       {
-               syslog (LOG_WARNING, "network_create_socket: There is no point opening a socket when you are in mode `%s'.",
-                               operating_mode == MODE_LOCAL ? "Local" : "Log");
-               return (-1);
-       }
-
-       socklist_tail = socklist_head;
-       while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
-               socklist_tail = socklist_tail->next;
-
        memset (&ai_hints, '\0', sizeof (ai_hints));
        ai_hints.ai_flags    = 0;
 #ifdef AI_PASSIVE
@@ -233,17 +629,20 @@ int network_create_socket (const char *node, const char *service)
 #ifdef AI_ADDRCONFIG
        ai_hints.ai_flags |= AI_ADDRCONFIG;
 #endif
-       ai_hints.ai_family   = PF_UNSPEC;
+       ai_hints.ai_family   = AF_UNSPEC;
        ai_hints.ai_socktype = SOCK_DGRAM;
        ai_hints.ai_protocol = IPPROTO_UDP;
 
-       if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
+       ai_return = getaddrinfo (node, service, &ai_hints, &ai_list);
+       if (ai_return != 0)
        {
                syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
-                               node == NULL ? "(null)" : node,
-                               service == NULL ? "(null)" : service,
-                               ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
-               return (-1);
+                               (node == NULL) ? "(null)" : node,
+                               (service == NULL) ? "(null)" : service,
+                               (ai_return == EAI_SYSTEM)
+                               ? strerror (errno)
+                               : gai_strerror (ai_return));
+               return (NULL);
        }
 
        for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
@@ -268,8 +667,9 @@ int network_create_socket (const char *node, const char *service)
                memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
                se->addrlen = ai_ptr->ai_addrlen;
 
-               se->mode = operating_mode;
-               se->fd   = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+               se->fd   = socket (ai_ptr->ai_family,
+                               ai_ptr->ai_socktype,
+                               ai_ptr->ai_protocol);
                se->next = NULL;
 
                if (se->fd == -1)
@@ -280,7 +680,7 @@ int network_create_socket (const char *node, const char *service)
                        continue;
                }
 
-               if (operating_mode == MODE_SERVER)
+               if (listen != 0)
                {
                        if (network_bind_socket (se, ai_ptr) != 0)
                        {
@@ -290,201 +690,185 @@ int network_create_socket (const char *node, const char *service)
                                continue;
                        }
                }
-               else if (operating_mode == MODE_CLIENT)
+               else /* listen == 0 */
                {
                        network_set_ttl (se, ai_ptr);
                }
 
-               if (socklist_tail == NULL)
+               if (se_tail == NULL)
                {
-                       socklist_head = se;
-                       socklist_tail = se;
+                       se_head = se;
+                       se_tail = se;
                }
                else
                {
-                       socklist_tail->next = se;
-                       socklist_tail = se;
+                       se_tail->next = se;
+                       se_tail = se;
                }
 
-               num_added++;
-
                /* We don't open more than one write-socket per node/service pair.. */
-               if (operating_mode == MODE_CLIENT)
+               if (listen == 0)
                        break;
        }
 
        freeaddrinfo (ai_list);
 
-       return (num_added);
-}
+       return (se_head);
+} /* sockent_t *network_create_socket */
 
-static int network_connect_default (void)
+static sockent_t *network_create_default_socket (int listen)
 {
-       int ret;
+       sockent_t *se_ptr  = NULL;
+       sockent_t *se_head = NULL;
+       sockent_t *se_tail = NULL;
 
-       if (socklist_head != NULL)
-               return (0);
+       se_ptr = network_create_socket (NET_DEFAULT_V6_ADDR,
+                       NET_DEFAULT_PORT, listen);
 
-       DBG ("socklist_head is NULL");
+       /* Don't send to the same machine in IPv6 and IPv4 if both are available. */
+       if ((listen == 0) && (se_ptr != NULL))
+               return (se_ptr);
 
-       ret = 0;
+       if (se_ptr != NULL)
+       {
+               se_head = se_ptr;
+               se_tail = se_ptr;
+               while (se_tail->next != NULL)
+                       se_tail = se_tail->next;
+       }
 
-       if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
-               ret++;
+       se_ptr = network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT, listen);
 
-       /* Don't use IPv4 and IPv6 in parallel by default.. */
-       if ((operating_mode == MODE_CLIENT) && (ret != 0))
-               return (ret);
+       if (se_tail == NULL)
+               return (se_ptr);
 
-       if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
-               ret++;
+       se_tail->next = se_ptr;
+       return (se_head);
+} /* sockent_t *network_create_default_socket */
 
-       if (ret == 0)
-               ret = -1;
+static int network_add_listen_socket (const char *node, const char *service)
+{
+       sockent_t *se;
+       sockent_t *se_ptr;
+       int se_num = 0;
 
-       return (ret);
-}
+       if (service == NULL)
+               service = NET_DEFAULT_PORT;
 
-static int network_get_listen_socket (void)
-{
-       int fd;
-       int max_fd;
-       int status;
+       if (node == NULL)
+               se = network_create_default_socket (1 /* listen == true */);
+       else
+               se = network_create_socket (node, service, 1 /* listen == true */);
 
-       fd_set readfds;
-       sockent_t *se;
+       if (se == NULL)
+               return (-1);
 
-       if (socklist_head == NULL)
-               network_connect_default ();
+       for (se_ptr = se; se_ptr != NULL; se_ptr = se_ptr->next)
+               se_num++;
 
-       FD_ZERO (&readfds);
-       max_fd = -1;
-       for (se = socklist_head; se != NULL; se = se->next)
+       listen_sockets = (struct pollfd *) realloc (listen_sockets,
+                       (listen_sockets_num + se_num)
+                       * sizeof (struct pollfd));
+
+       for (se_ptr = se; se_ptr != NULL; se_ptr = se_ptr->next)
        {
-               if (se->mode != operating_mode)
-                       continue;
+               listen_sockets[listen_sockets_num].fd = se_ptr->fd;
+               listen_sockets[listen_sockets_num].events = POLLIN | POLLPRI;
+               listen_sockets[listen_sockets_num].revents = 0;
+               listen_sockets_num++;
+       } /* for (se) */
 
-               FD_SET (se->fd, &readfds);
-               if (se->fd >= max_fd)
-                       max_fd = se->fd + 1;
-       }
+       free_sockent (se);
+       return (0);
+} /* int network_add_listen_socket */
 
-       if (max_fd == -1)
-       {
-               syslog (LOG_WARNING, "No listen sockets found!");
-               return (-1);
-       }
+static int network_add_sending_socket (const char *node, const char *service)
+{
+       sockent_t *se;
+       sockent_t *se_ptr;
 
-       status = select (max_fd, &readfds, NULL, NULL, NULL);
+       if (service == NULL)
+               service = NET_DEFAULT_PORT;
 
-       if (status == -1)
-       {
-               if (errno != EINTR)
-                       syslog (LOG_ERR, "select: %s", strerror (errno));
+       if (node == NULL)
+               se = network_create_default_socket (0 /* listen == false */);
+       else
+               se = network_create_socket (node, service, 0 /* listen == false */);
+
+       if (se == NULL)
                return (-1);
-       }
 
-       fd = -1;
-       for (se = socklist_head; se != NULL; se = se->next)
+       if (sending_sockets == NULL)
        {
-               if (se->mode != operating_mode)
-                       continue;
-
-               if (FD_ISSET (se->fd, &readfds))
-               {
-                       fd = se->fd;
-                       break;
-               }
+               sending_sockets = se;
+               return (0);
        }
 
-       if (fd == -1)
-               syslog (LOG_WARNING, "No socket ready..?");
+       for (se_ptr = sending_sockets; se_ptr->next != NULL; se_ptr = se_ptr->next)
+               /* seek end */;
 
-       DBG ("fd = %i", fd);
-       return (fd);
-}
+       se_ptr->next = se;
+       return (0);
+} /* int network_get_listen_socket */
 
-int network_receive (char **host, char **type, char **inst, char **value)
+int network_receive (void)
 {
-       int fd;
        char buffer[BUFF_SIZE];
+       int  buffer_len;
 
-       struct sockaddr_storage addr;
-       socklen_t               addrlen;
+       int i;
        int status;
 
-       char *fields[4];
-
-       assert (operating_mode == MODE_SERVER);
-
-       *host  = NULL;
-       *type  = NULL;
-       *inst  = NULL;
-       *value = NULL;
-
-       if ((fd = network_get_listen_socket ()) < 0)
-               return (-1);
+       if (listen_sockets_num == 0)
+               network_add_listen_socket (NULL, NULL);
 
-       addrlen = sizeof (addr);
-       if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
+       if (listen_sockets_num == 0)
        {
-               syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
+               syslog (LOG_ERR, "network: Failed to open a listening socket.");
                return (-1);
        }
 
-       if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
+       while (listen_loop == 0)
        {
-               syslog (LOG_EMERG, "malloc: %s", strerror (errno));
-               return (-1);
-       }
+               status = poll (listen_sockets, listen_sockets_num, -1);
 
-       status = getnameinfo ((struct sockaddr *) &addr, addrlen,
-                       *host, BUFF_SIZE, NULL, 0, 0);
-       if (status != 0)
-       {
-               free (*host); *host = NULL;
-               syslog (LOG_ERR, "getnameinfo: %s",
-                               status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
-               return (-1);
-       }
-
-       if (strsplit (buffer, fields, 4) != 3)
-       {
-               syslog (LOG_WARNING, "Invalid message from `%s'", *host);
-               free (*host); *host = NULL;
-               return (1);
-       }
-
-       if ((*type = strdup (fields[0])) == NULL)
-       {
-               syslog (LOG_EMERG, "strdup: %s", strerror (errno));
-               free (*host); *host = NULL;
-               return (-1);
-       }
+               if (status <= 0)
+               {
+                       if (errno == EINTR)
+                               continue;
+                       syslog (LOG_ERR, "poll failed: %s",
+                                       strerror (errno));
+                       return (-1);
+               }
 
-       if ((*inst = strdup (fields[1])) == NULL)
-       {
-               syslog (LOG_EMERG, "strdup: %s", strerror (errno));
-               free (*host); *host = NULL;
-               free (*type); *type = NULL;
-               return (-1);
-       }
+               for (i = 0; (i < listen_sockets_num) && (status > 0); i++)
+               {
+                       if ((listen_sockets[i].revents & (POLLIN | POLLPRI)) == 0)
+                               continue;
+                       status--;
 
-       if ((*value = strdup (fields[2])) == NULL)
-       {
-               syslog (LOG_EMERG, "strdup: %s", strerror (errno));
-               free (*host); *host = NULL;
-               free (*type); *type = NULL;
-               free (*inst); *inst = NULL;
-               return (-1);
-       }
+                       buffer_len = recv (listen_sockets[i].fd,
+                                       buffer, sizeof (buffer),
+                                       0 /* no flags */);
+                       if (buffer_len < 0)
+                       {
+                               syslog (LOG_ERR, "recv failed: %s", strerror (errno));
+                               return (-1);
+                       }
 
-       DBG ("host = %s, type = %s, inst = %s, value = %s",
-                       *host, *type, *inst, *value);
+                       parse_packet (buffer, buffer_len);
+               } /* for (listen_sockets) */
+       } /* while (listen_loop == 0) */
 
        return (0);
 }
 
+static void *receive_thread (void *arg)
+{
+       return (network_receive () ? (void *) 1 : (void *) 0);
+} /* void *receive_thread */
+
+#if 0
 int network_send (char *type, char *inst, char *value)
 {
        char buf[BUFF_SIZE];
@@ -509,14 +893,11 @@ int network_send (char *type, char *inst, char *value)
        buflen++;
 
        if (socklist_head == NULL)
-               network_connect_default ();
+               network_create_default_socket (0 /* listen == false */);
 
        ret = 0;
        for (se = socklist_head; se != NULL; se = se->next)
        {
-               if (se->mode != operating_mode)
-                       continue;
-
                while (1)
                {
                        status = sendto (se->fd, buf, buflen, 0,
@@ -546,4 +927,160 @@ int network_send (char *type, char *inst, char *value)
                syslog (LOG_WARNING, "Message wasn't sent to anybody..");
 
        return (ret);
+} /* int network_send */
+#endif
+
+static int network_write (const data_set_t *ds, const value_list_t *vl)
+{
+       char  buf[BUFF_SIZE];
+       char *buf_ptr;
+       int   buf_len;
+
+       sockent_t *se;
+
+       DBG ("time = %u; host = %s; "
+                       "plugin = %s; plugin_instance = %s; "
+                       "type = %s; type_instance = %s;",
+                       (unsigned int) vl->time, vl->host,
+                       vl->plugin, vl->plugin_instance,
+                       ds->type, vl->type_instance);
+
+       buf_len = sizeof (buf);
+       buf_ptr = buf;
+       if (write_part_string (&buf_ptr, &buf_len, TYPE_HOST,
+                               vl->host, strlen (vl->host)) != 0)
+               return (-1);
+       if (write_part_number (&buf_ptr, &buf_len, TYPE_TIME,
+                               (uint64_t) vl->time))
+               return (-1);
+       if (write_part_string (&buf_ptr, &buf_len, TYPE_PLUGIN,
+                               vl->plugin, strlen (vl->plugin)) != 0)
+               return (-1);
+       if (strlen (vl->plugin_instance) > 0)
+               if (write_part_string (&buf_ptr, &buf_len, TYPE_PLUGIN_INSTANCE,
+                                       vl->plugin_instance,
+                                       strlen (vl->plugin_instance)) != 0)
+                       return (-1);
+       if (write_part_string (&buf_ptr, &buf_len, TYPE_TYPE,
+                               ds->type, strlen (ds->type)) != 0)
+               return (-1);
+       if (strlen (vl->type_instance) > 0)
+               if (write_part_string (&buf_ptr, &buf_len, TYPE_PLUGIN_INSTANCE,
+                                       vl->type_instance,
+                                       strlen (vl->type_instance)) != 0)
+                       return (-1);
+       
+       write_part_values (&buf_ptr, &buf_len, ds, vl);
+
+       buf_len = sizeof (buf) - buf_len;
+
+       for (se = sending_sockets; se != NULL; se = se->next)
+       {
+               int status;
+
+               while (42)
+               {
+                       status = sendto (se->fd, buf, buf_len, 0 /* no flags */,
+                                       (struct sockaddr *) se->addr, se->addrlen);
+                       if (status < 0)
+                       {
+                               if (errno == EINTR)
+                                       continue;
+                               syslog (LOG_ERR, "network: sendto failed: %s",
+                                               strerror (errno));
+                               break;
+                       }
+
+                       break;
+               } /* while (42) */
+       } /* for (sending_sockets) */
+
+       return 0;
+}
+
+static int network_config (const char *key, const char *val)
+{
+       char *node;
+       char *service;
+
+       char *fields[3];
+       int   fields_num;
+
+       if ((strcasecmp ("Listen", key) == 0)
+                       || (strcasecmp ("Server", key) == 0))
+       {
+               char *val_cpy = strdup (val);
+               if (val_cpy == NULL)
+                       return (1);
+
+               service = NET_DEFAULT_PORT;
+               fields_num = strsplit (val_cpy, fields, 3);
+               if ((fields_num != 1)
+                               && (fields_num != 2))
+                       return (1);
+               else if (fields_num == 2)
+                       service = fields[1];
+               node = fields[0];
+
+               if (strcasecmp ("Listen", key) == 0)
+                       network_add_listen_socket (node, service);
+               else
+                       network_add_sending_socket (node, service);
+       }
+       else if (strcasecmp ("TimeToLive", key) == 0)
+       {
+               int tmp = atoi (val);
+               if ((tmp > 0) && (tmp < 256))
+                       network_config_ttl = tmp;
+               else
+                       return (1);
+       }
+       else
+       {
+               return (-1);
+       }
+       return (0);
+}
+
+static int network_shutdown (void)
+{
+       DBG ("Shutting down.");
+
+       listen_loop++;
+
+       pthread_kill (listen_thread, SIGTERM);
+       pthread_join (listen_thread, NULL /* no return value */);
+
+       listen_thread = 0;
+
+       return (0);
+}
+
+static int network_init (void)
+{
+       plugin_register_shutdown ("network", network_shutdown);
+
+       /* setup socket(s) and so on */
+       if (sending_sockets != NULL)
+               plugin_register_write ("network", network_write);
+
+       if ((listen_sockets_num != 0) && (listen_thread == 0))
+       {
+               int status;
+
+               status = pthread_create (&listen_thread, NULL /* no attributes */,
+                               receive_thread, NULL /* no argument */);
+
+               if (status != 0)
+                       syslog (LOG_ERR, "network: pthread_create failed: %s",
+                                       strerror (errno));
+       }
+       return (0);
+} /* int network_init */
+
+void module_register (void)
+{
+       plugin_register_config ("network", network_config,
+                       config_keys, config_keys_num);
+       plugin_register_init   ("network", network_init);
 }
index 2df989b..b20fc75 100644 (file)
@@ -4,8 +4,7 @@
  *
  * 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
 #define NET_DEFAULT_V6_ADDR "ff18::efc0:4a42"
 #define NET_DEFAULT_PORT    "25826"
 
-int network_create_socket (const char *node, const char *service);
-int network_receive (char **host, char **type, char **instance, char **value);
-int network_send (char *type, char *instance, char *value);
+#define TYPE_HOST            0x0000
+#define TYPE_TIME            0x0001
+#define TYPE_PLUGIN          0x0002
+#define TYPE_PLUGIN_INSTANCE 0x0003
+#define TYPE_TYPE            0x0004
+#define TYPE_TYPE_INSTANCE   0x0005
+#define TYPE_VALUES          0x0006
 
 #endif /* NETWORK_H */
index d0f33c5..f585b97 100644 (file)
--- a/src/nfs.c
+++ b/src/nfs.c
@@ -4,8 +4,7 @@
  *
  * 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
@@ -24,6 +23,7 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
+#include "utils_debug.h"
 
 #define MODULE_NAME "nfs"
 
@@ -34,9 +34,6 @@
 # define NFS_HAVE_READ 0
 #endif
 
-static char *nfs2_procedures_file  = "nfs2_procedures-%s.rrd";
-static char *nfs3_procedures_file  = "nfs3_procedures-%s.rrd";
-
 /*
 see /proc/net/rpc/nfs
 see http://www.missioncriticallinux.com/orph/NFS-Statistics
@@ -82,57 +79,67 @@ Number      Procedures  Procedures
 21                      commit
 */
 
-static char *nfs2_procedures_ds_def[] =
+static data_source_t procedure_dsrc[1] =
+{
+       {"value", DS_TYPE_COUNTER, 0, 4294967295.0}
+};
+
+static data_set_t procedure_ds =
+{
+       "nfs_procedure", 1, procedure_dsrc
+};
+
+static const char *nfs2_procedures_names[] =
 {
-       "DS:null:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:getattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:setattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:root:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:lookup:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:readlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:read:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wrcache:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:write:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:create:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:remove:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rename:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:link:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:symlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:mkdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rmdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:readdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:fsstat:COUNTER:"COLLECTD_HEARTBEAT":0:U",
+       "null",
+       "getattr",
+       "setattr",
+       "root",
+       "lookup",
+       "readlink",
+       "read",
+       "wrcache",
+       "write",
+       "create",
+       "remove",
+       "rename",
+       "link",
+       "symlink",
+       "mkdir",
+       "rmdir",
+       "readdir",
+       "fsstat",
        NULL
 };
-static int nfs2_procedures_ds_num = 18;
+static int nfs2_procedures_names_num = 18;
 
-static char *nfs3_procedures_ds_def[] =
+static const char *nfs3_procedures_names[] =
 {
-       "DS:null:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:getattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:setattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:lookup:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:access:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:readlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:read:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:write:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:create:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:mkdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:symlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:mknod:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:remove:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rmdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rename:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:link:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:readdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:readdirplus:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:fsstat:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:fsinfo:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:pathconf:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:commit:COUNTER:"COLLECTD_HEARTBEAT":0:U",
+       "null",
+       "getattr",
+       "setattr",
+       "lookup",
+       "access",
+       "readlink",
+       "read",
+       "write",
+       "create",
+       "mkdir",
+       "symlink",
+       "mknod",
+       "remove",
+       "rmdir",
+       "rename",
+       "link",
+       "readdir",
+       "readdirplus",
+       "fsstat",
+       "fsinfo",
+       "pathconf",
+       "commit",
        NULL
 };
-static int nfs3_procedures_ds_num = 22;
+static int nfs3_procedures_names_num = 22;
 
 #if HAVE_LIBKSTAT && 0
 extern kstat_ctl_t *kc;
@@ -146,7 +153,8 @@ static kstat_t *nfs4_ksp_server;
 
 /* Possibly TODO: NFSv4 statistics */
 
-static void nfs_init (void)
+#if 0
+static int nfs_init (void)
 {
 #if HAVE_LIBKSTAT && 0
        kstat_t *ksp_chain;
@@ -181,82 +189,38 @@ static void nfs_init (void)
        }
 #endif
 
-       return;
-}
+       return (0);
+} /* int nfs_init */
+#endif
 
 #define BUFSIZE 1024
-static void nfs2_procedures_write (char *host, char *inst, char *val)
-{
-       char filename[BUFSIZE];
-
-       if (snprintf (filename, BUFSIZE, nfs2_procedures_file, inst) > BUFSIZE)
-               return;
-
-       rrd_update_file (host, filename, val, nfs2_procedures_ds_def,
-                       nfs2_procedures_ds_num);
-}
-
-static void nfs3_procedures_write (char *host, char *inst, char *val)
-{
-       char filename[BUFSIZE];
-
-       if (snprintf (filename, BUFSIZE, nfs3_procedures_file, inst) > BUFSIZE)
-               return;
-
-       rrd_update_file (host, filename, val, nfs3_procedures_ds_def,
-                       nfs3_procedures_ds_num);
-}
-
 #if NFS_HAVE_READ
-static void nfs2_procedures_submit (unsigned long long *val, char *inst)
-{
-       char buf[BUFSIZE];
-       int retval = 0;
-
-       retval = snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
-                       "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
-                       "%llu:%llu:%llu", /* 18x %llu */
-                       (unsigned int) curtime,
-                       val[0], val[1], val[2], val[3], val[4], val[5], val[6],
-                       val[7], val[8], val[9], val[10], val[11], val[12],
-                       val[13], val[14], val[15], val[16], val[17]);
-
-
-       if (retval >= BUFSIZE)
-               return;
-       else if (retval < 0)
-       {
-               syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
-               return;
-       }
-
-       plugin_submit ("nfs2_procedures", inst, buf);
-}
-
-static void nfs3_procedures_submit (unsigned long long *val, char *inst)
+static void nfs_procedures_submit (const char *plugin_instance,
+               unsigned long long *val, const char **names, int len)
 {
-       char buf[BUFSIZE];
-       int retval = 0;
-
-       retval = snprintf(buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
-                       "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
-                       "%llu:%llu:%llu:%llu:%llu:%llu:%llu", /* 22x %llu */
-                       (unsigned int) curtime,
-                       val[0], val[1], val[2], val[3], val[4], val[5], val[6],
-                       val[7], val[8], val[9], val[10], val[11], val[12],
-                       val[13], val[14], val[15], val[16], val[17], val[18],
-                       val[19], val[20], val[21]);
-
-       if (retval >= BUFSIZE)
-               return;
-       else if (retval < 0)
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+       int i;
+
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname);
+       strcpy (vl.plugin, "nfs");
+       strncpy (vl.plugin_instance, plugin_instance,
+                       sizeof (vl.plugin_instance));
+
+       for (i = 0; i < len; i++)
        {
-               syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
-               return;
+               values[0].counter = val[i];
+               strncpy (vl.type_instance, names[i],
+                               sizeof (vl.type_instance));
+               DBG ("%s-%s/nfs_procedure-%s = %llu",
+                               vl.plugin, vl.plugin_instance,
+                               vl.type_instance, val[i]);
+               plugin_dispatch_values ("nfs_procedure", &vl);
        }
-
-       plugin_submit("nfs3_procedures", inst, buf);
-}
+} /* void nfs_procedures_submit */
 #endif /* NFS_HAVE_READ */
 
 #if KERNEL_LINUX
@@ -264,6 +228,8 @@ static void nfs_read_stats_file (FILE *fh, char *inst)
 {
        char buffer[BUFSIZE];
 
+       char plugin_instance[DATA_MAX_NAME_LEN];
+
        char *fields[48];
        int numfields = 0;
 
@@ -274,30 +240,44 @@ static void nfs_read_stats_file (FILE *fh, char *inst)
        {
                numfields = strsplit (buffer, fields, 48);
 
-               if (numfields < 2)
+               if (((numfields - 2) != nfs2_procedures_names_num)
+                               && ((numfields - 2)
+                                       != nfs3_procedures_names_num))
                        continue;
 
-               if (strncmp (fields[0], "proc2", 5) == 0)
+               if (strcmp (fields[0], "proc2") == 0)
                {
                        int i;
                        unsigned long long *values;
 
-                       if (numfields - 2 != nfs2_procedures_ds_num)
+                       if ((numfields - 2) != nfs2_procedures_names_num)
                        {
-                               syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS2 statistics.", numfields - 2);
+                               syslog (LOG_WARNING, "nfs plugin: Wrong "
+                                               "number of fields (= %i) "
+                                               "for NFSv2 statistics.",
+                                               numfields - 2);
                                continue;
                        }
 
-                       if ((values = (unsigned long long *) malloc (nfs2_procedures_ds_num * sizeof (unsigned long long))) == NULL)
+                       snprintf (plugin_instance, sizeof (plugin_instance),
+                                       "v2%s", inst);
+                       plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+
+                       values = (unsigned long long *) malloc (nfs2_procedures_names_num * sizeof (unsigned long long));
+                       if (values == NULL)
                        {
-                               syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
+                               syslog (LOG_ERR, "nfs plugin: malloc "
+                                               "failed: %s",
+                                               strerror (errno));
                                continue;
                        }
 
-                       for (i = 0; i < nfs2_procedures_ds_num; i++)
+                       for (i = 0; i < nfs2_procedures_names_num; i++)
                                values[i] = atoll (fields[i + 2]);
 
-                       nfs2_procedures_submit (values, inst);
+                       nfs_procedures_submit (plugin_instance, values,
+                                       nfs2_procedures_names,
+                                       nfs2_procedures_names_num);
 
                        free (values);
                }
@@ -306,27 +286,39 @@ static void nfs_read_stats_file (FILE *fh, char *inst)
                        int i;
                        unsigned long long *values;
 
-                       if (numfields - 2 != nfs3_procedures_ds_num)
+                       if ((numfields - 2) != nfs3_procedures_names_num)
                        {
-                               syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS3 statistics.", numfields - 2);
+                               syslog (LOG_WARNING, "nfs plugin: Wrong "
+                                               "number of fields (= %i) "
+                                               "for NFSv3 statistics.",
+                                               numfields - 2);
                                continue;
                        }
 
-                       if ((values = (unsigned long long *) malloc (nfs3_procedures_ds_num * sizeof (unsigned long long))) == NULL)
+                       snprintf (plugin_instance, sizeof (plugin_instance),
+                                       "v3%s", inst);
+                       plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+
+                       values = (unsigned long long *) malloc (nfs3_procedures_names_num * sizeof (unsigned long long));
+                       if (values == NULL)
                        {
-                               syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
+                               syslog (LOG_ERR, "nfs plugin: malloc "
+                                               "failed: %s",
+                                               strerror (errno));
                                continue;
                        }
 
-                       for (i = 0; i < nfs3_procedures_ds_num; i++)
+                       for (i = 0; i < nfs3_procedures_names_num; i++)
                                values[i] = atoll (fields[i + 2]);
 
-                       nfs3_procedures_submit (values, inst);
+                       nfs_procedures_submit (plugin_instance, values,
+                                       nfs3_procedures_names,
+                                       nfs3_procedures_names_num);
 
                        free (values);
                }
-       }
-}
+       } /* while (fgets (buffer, BUFSIZE, fh) != NULL) */
+} /* void nfs_read_stats_file */
 #endif /* defined(KERNEL_LINUX) */
 #undef BUFSIZE
 
@@ -359,7 +351,7 @@ static void nfs2_read_kstat (kstat_t *ksp, char *inst)
 #endif
 
 #if NFS_HAVE_READ
-static void nfs_read (void)
+static int nfs_read (void)
 {
 #if KERNEL_LINUX
        FILE *fh;
@@ -384,16 +376,18 @@ static void nfs_read (void)
        if (nfs2_ksp_server != NULL)
                nfs2_read_kstat (nfs2_ksp_server, "server");
 #endif /* defined(HAVE_LIBKSTAT) */
+
+       return (0);
 }
-#else
-# define nfs_read NULL
 #endif /* NFS_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, nfs_init, nfs_read, NULL);
-       plugin_register ("nfs2_procedures", NULL, NULL, nfs2_procedures_write);
-       plugin_register ("nfs3_procedures", NULL, NULL, nfs3_procedures_write);
+       plugin_register_data_set (&procedure_ds);
+
+#if NFS_HAVE_READ
+       plugin_register_read ("nfs", nfs_read);
+#endif
 }
 
 #undef MODULE_NAME
index b65a9e8..e37b3a1 100644 (file)
@@ -51,8 +51,8 @@
 #if HAVE_NETINET_TCP_H
 # include <netinet/tcp.h>
 #endif
-#if HAVE_SYS_POLL_H
-# include <sys/poll.h>
+#if HAVE_POLL_H
+# include <poll.h>
 #endif
 
 static char *config_keys[] =
index 46a1c61..b40d62a 100644 (file)
@@ -4,8 +4,7 @@
  *
  * 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
 #include <ltdl.h>
 
 #include "plugin.h"
-#include "network.h"
+#include "configfile.h"
+#include "utils_llist.h"
 #include "utils_debug.h"
 
-typedef struct plugin
-{
-       char *type;
-       void (*init) (void);
-       void (*read) (void);
-       void (*write) (char *host, char *inst, char *val);
-       void (*shutdown) (void);
-       struct plugin *next;
-} plugin_t;
-
-static plugin_t *first_plugin = NULL;
-
-extern int operating_mode;
+/*
+ * Private variables
+ */
+static llist_t *list_init;
+static llist_t *list_read;
+static llist_t *list_write;
+static llist_t *list_shutdown;
+static llist_t *list_data_set;
 
 static char *plugindir = NULL;
 
-char *plugin_get_dir (void)
+char hostname[DATA_MAX_NAME_LEN] = "localhost";
+
+/*
+ * Static functions
+ */
+static const char *plugin_get_dir (void)
 {
        if (plugindir == NULL)
                return (PLUGINDIR);
@@ -52,66 +52,37 @@ char *plugin_get_dir (void)
                return (plugindir);
 }
 
-void plugin_set_dir (const char *dir)
-{
-       if (plugindir != NULL)
-               free (plugindir);
-
-       if (dir == NULL)
-               plugindir = NULL;
-       else if ((plugindir = strdup (dir)) == NULL)
-               syslog (LOG_ERR, "strdup: %s", strerror (errno));
-}
-
-/*
- * Returns the number of plugins registered
- */
-int plugin_count (void)
-{
-       int i;
-       plugin_t *p;
-
-       for (i = 0, p = first_plugin; p != NULL; p = p->next)
-               i++;
-
-       return (i);
-}
-
-/*
- * Returns the plugins with the type `type' or NULL if it's not found.
- */
-plugin_t *plugin_search (const char *type)
+static int register_callback (llist_t **list, const char *name, void *callback)
 {
-       plugin_t *ret;
+       llentry_t *le;
 
-       if (type == NULL)
-               return (NULL);
-
-       for (ret = first_plugin; ret != NULL; ret = ret->next)
-               if (strcmp (ret->type, type) == 0)
-                       break;
+       if ((*list == NULL)
+                       && ((*list = llist_create ()) == NULL))
+               return (-1);
 
-       return (ret);
-}
+       le = llist_search (*list, name);
+       if (le == NULL)
+       {
+               le = llentry_create (name, callback);
+               if (le == NULL)
+                       return (-1);
 
-/*
- * Returns true if the plugin is loaded (i.e. `exists') and false otherwise.
- * This is used in `configfile.c' to skip sections that are not needed..
- */
-int plugin_exists (char *type)
-{
-       if (plugin_search (type) == NULL)
-               return (0);
+               llist_append (*list, le);
+       }
        else
-               return (1);
-}
+       {
+               le->value = callback;
+       }
+
+       return (0);
+} /* int register_callback */
 
 /*
  * (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
  * ``module_register'' symbol..
  */
-int plugin_load_file (char *file)
+static int plugin_load_file (char *file)
 {
        lt_dlhandle dlh;
        void (*reg_handle) (void);
@@ -143,11 +114,25 @@ int plugin_load_file (char *file)
        return (0);
 }
 
+/*
+ * Public functions
+ */
+void plugin_set_dir (const char *dir)
+{
+       if (plugindir != NULL)
+               free (plugindir);
+
+       if (dir == NULL)
+               plugindir = NULL;
+       else if ((plugindir = strdup (dir)) == NULL)
+               syslog (LOG_ERR, "strdup failed: %s", strerror (errno));
+}
+
 #define BUFSIZE 512
 int plugin_load (const char *type)
 {
        DIR  *dh;
-       char *dir;
+       const char *dir;
        char  filename[BUFSIZE];
        char  typename[BUFSIZE];
        int   typename_len;
@@ -160,10 +145,6 @@ int plugin_load (const char *type)
        dir = plugin_get_dir ();
        ret = 1;
 
-       /* don't load twice */
-       if (plugin_search (type) != NULL)
-               return (0);
-
        /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
         * type when matching the filename */
        if (snprintf (typename, BUFSIZE, "%s.so", type) >= BUFSIZE)
@@ -215,170 +196,136 @@ int plugin_load (const char *type)
 }
 
 /*
- * (Try to) load all plugins in `dir'. Returns the number of loaded plugins..
+ * The `register_*' functions follow
  */
-int plugin_load_all (char *dir)
+int plugin_register_config (const char *name,
+               int (*callback) (const char *key, const char *val),
+               const char **keys, int keys_num)
 {
-       DIR *dh;
-       struct dirent *de;
-       char filename[BUFSIZE];
-       struct stat statbuf;
-
-       if (dir == NULL)
-               dir = plugin_get_dir ();
-       else
-               plugin_set_dir (dir);
-
-       if ((dh = opendir (dir)) == NULL)
-       {
-               syslog (LOG_ERR, "opendir (%s): %s", dir, strerror (errno));
-               return (0);
-       }
+       cf_register (name, callback, keys, keys_num);
+       return (0);
+} /* int plugin_register_config */
 
-       while ((de = readdir (dh)) != NULL)
-       {
-               if (snprintf (filename, BUFSIZE, "%s/%s", dir, de->d_name) >= BUFSIZE)
-               {
-                       syslog (LOG_WARNING, "snprintf: truncated: %s/%s", dir, de->d_name);
-                       continue;
-               }
+int plugin_register_init (const char *name,
+               int (*callback) (void))
+{
+       return (register_callback (&list_init, name, (void *) callback));
+} /* plugin_register_init */
 
-               if (lstat (filename, &statbuf) == -1)
-               {
-                       syslog (LOG_WARNING, "stat %s: %s", filename, strerror (errno));
-                       continue;
-               }
-               else if (!S_ISREG (statbuf.st_mode))
-               {
-                       continue;
-               }
+int plugin_register_read (const char *name,
+               int (*callback) (void))
+{
+       return (register_callback (&list_read, name, (void *) callback));
+} /* int plugin_register_read */
 
-               plugin_load_file (filename);
-       }
+int plugin_register_write (const char *name,
+               int (*callback) (const data_set_t *ds, const value_list_t *vl))
+{
+       return (register_callback (&list_write, name, (void *) callback));
+} /* int plugin_register_write */
 
-       closedir (dh);
+int plugin_register_shutdown (char *name,
+               int (*callback) (void))
+{
+       return (register_callback (&list_shutdown, name, (void *) callback));
+} /* int plugin_register_shutdown */
 
-       return (plugin_count ());
-}
-#undef BUFSIZE
+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 */
 
-/*
- * Call `init' on all plugins (if given)
- */
 void plugin_init_all (void)
 {
-       plugin_t *p;
+       int (*callback) (void);
+       llentry_t *le;
 
-       for (p = first_plugin; p != NULL; p = p->next)
-               if (p->init != NULL)
-                       (*p->init) ();
-}
+       gethostname (hostname, sizeof (hostname));
 
-/*
- * Call `read' on all plugins (if given)
- */
-void plugin_read_all (const int *loop)
-{
-       plugin_t *p;
-
-       for (p = first_plugin; (*loop == 0) && (p != NULL); p = p->next)
-               if (p->read != NULL)
-                       (*p->read) ();
-}
+       if (list_init == NULL)
+               return;
 
-/*
- * Call `shutdown' on all plugins (if given)
- */
-void plugin_shutdown_all (void)
-{
-       plugin_t *p;
+       le = llist_head (list_init);
+       while (le != NULL)
+       {
+               callback = le->value;
+               (*callback) ();
 
-       for (p = first_plugin; NULL != p; p = p->next)
-               if (NULL != p->shutdown)
-                       (*p->shutdown) ();
-       return;
-}
+               le = le->next;
+       }
+} /* void plugin_init_all */
 
-/*
- * Add plugin to the linked list of registered plugins.
- */
-void plugin_register (char *type,
-               void (*init) (void),
-               void (*read) (void),
-               void (*write) (char *, char *, char *))
+void plugin_read_all (const int *loop)
 {
-       plugin_t *p;
+       int (*callback) (void);
+       llentry_t *le;
 
-       if (plugin_search (type) != NULL)
+       if (list_read == NULL)
                return;
 
-#ifdef HAVE_LIBRRD
-       if (operating_mode != MODE_SERVER)
-#endif
-               if ((init != NULL) && (read == NULL))
-                       syslog (LOG_NOTICE, "Plugin `%s' doesn't provide a read function.", type);
-
-       if ((p = (plugin_t *) malloc (sizeof (plugin_t))) == NULL)
-               return;
-
-       if ((p->type = strdup (type)) == NULL)
+       le = llist_head (list_read);
+       while ((*loop == 0) && (le != NULL))
        {
-               free (p);
-               return;
+               callback = le->value;
+               (*callback) ();
+
+               le = le->next;
        }
+} /* void plugin_read_all */
+
+void plugin_shutdown_all (void)
+{
+       int (*callback) (void);
+       llentry_t *le;
 
-       p->init  = init;
-       p->read  = read;
-       p->write = write;
+       if (list_shutdown == NULL)
+               return;
 
-       p->shutdown = NULL;
+       le = llist_head (list_shutdown);
+       while (le != NULL)
+       {
+               callback = le->value;
+               (*callback) ();
 
-       p->next = first_plugin;
-       first_plugin = p;
-}
+               le = le->next;
+       }
+} /* void plugin_shutdown_all */
 
-/*
- * Register the shutdown function (optional).
- */
-int plugin_register_shutdown (char *type, void (*shutdown) (void))
+int plugin_dispatch_values (const char *name, const value_list_t *vl)
 {
-       plugin_t *p = plugin_search (type);
+       int (*callback) (const data_set_t *, const value_list_t *);
+       data_set_t *ds;
+       llentry_t *le;
 
-       if (NULL == p)
-               return -1;
+       if (list_write == NULL)
+               return (-1);
 
-       p->shutdown = shutdown;
-       return 0;
-}
+       le = llist_search (list_data_set, name);
+       if (le == NULL)
+       {
+               DBG ("No such dataset registered: %s", name);
+               return (-1);
+       }
 
-/*
- * Send received data back to the plugin/module which will append DS
- * definitions and pass it on to ``rrd_update_file''.
- */
-void plugin_write (char *host, char *type, char *inst, char *val)
-{
-       plugin_t *p;
+       ds = (data_set_t *) le->value;
 
-       if ((p = plugin_search (type)) == NULL)
-               return;
+       DBG ("time = %u; host = %s; "
+                       "plugin = %s; plugin_instance = %s; "
+                       "type = %s; type_instance = %s;",
+                       (unsigned int) vl->time, vl->host,
+                       vl->plugin, vl->plugin_instance,
+                       ds->type, vl->type_instance);
 
-       if (p->write == NULL)
-               return;
+       le = llist_head (list_write);
+       while (le != NULL)
+       {
+               callback = le->value;
+               (*callback) (ds, vl);
 
-       (*p->write) (host, inst, val);
-}
+               le = le->next;
+       }
 
-/*
- * Receive data from the plugin/module and get it somehow to ``plugin_write'':
- * Either using ``network_send'' (when in network/client mode) or call it
- * directly (in local mode).
- */
-void plugin_submit (char *type, char *inst, char *val)
-{
-        if (operating_mode == MODE_CLIENT)
-               network_send (type, inst, val);
-       else
-               plugin_write (NULL, type, inst, val);
+       return (0);
 }
 
 void plugin_complain (int level, complain_t *c, const char *format, ...)
index 45e9b49..107078e 100644 (file)
@@ -7,8 +7,7 @@
  *
  * 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
  *   Florian octo Forster <octo at verplant.org>
  **/
 
+#define DATA_MAX_NAME_LEN 64
+
+#define DS_TYPE_COUNTER 0
+#define DS_TYPE_GAUGE   1
+
 /*
- *
+ * Public data types
  */
+typedef unsigned long long counter_t;
+typedef double gauge_t;
+
+union value_u
+{
+       counter_t counter;
+       gauge_t   gauge;
+};
+typedef union value_u value_t;
+
+struct value_list_s
+{
+       value_t *values;
+       int      values_len;
+       time_t   time;
+       char     host[DATA_MAX_NAME_LEN];
+       char     plugin[DATA_MAX_NAME_LEN];
+       char     plugin_instance[DATA_MAX_NAME_LEN];
+       char     type_instance[DATA_MAX_NAME_LEN];
+};
+typedef struct value_list_s value_list_t;
+
+#define VALUE_LIST_INIT { NULL, 0, 0, "localhost", "", "", "" }
+
+struct data_source_s
+{
+       char   name[DATA_MAX_NAME_LEN];
+       int    type;
+       double min;
+       double max;
+};
+typedef struct data_source_s data_source_t;
+
+struct data_set_s
+{
+       char           type[DATA_MAX_NAME_LEN];
+       int            ds_num;
+       data_source_t *ds;
+};
+typedef struct data_set_s data_set_t;
+
 typedef struct complain_s
 {
        unsigned int interval; /* how long we wait for reporting this error again */
        unsigned int delay;    /* how many more iterations we still need to wait */
 } complain_t;
 
+extern char hostname[DATA_MAX_NAME_LEN];
+
 /*
  * NAME
  *  plugin_set_dir
@@ -49,34 +96,6 @@ void plugin_set_dir (const char *dir);
 
 /*
  * NAME
- *  plugin_count
- *
- * DESCRIPTION
- *  trivial
- *
- * RETURN VALUE
- *  The number of currently loaded plugins
- */
-int plugin_count (void);
-
-/*
- * NAME
- *  plugin_exists
- *
- * DESCRIPTION
- *  trivial
- *
- * ARGUMENTS
- *  `type'      Name of the plugin.
- *
- * RETURN VALUE
- *  Returns non-zero if a plugin with the name $type is found and zero
- *  otherwise.
- */
-int plugin_exists (char *type);
-
-/*
- * NAME
  *  plugin_load
  *
  * DESCRIPTION
@@ -95,42 +114,48 @@ int plugin_exists (char *type);
  * NOTES
  *  No attempt is made to re-load an already loaded module.
  */
-int  plugin_load (const char *type);
+int  plugin_load (const char *name);
 
-int  plugin_load_all (char *dir);
 void plugin_init_all (void);
 void plugin_read_all (const int *loop);
-
 void plugin_shutdown_all (void);
 
-void plugin_register (char *type,
-               void (*init) (void),
-               void (*read) (void),
-               void (*write) (char *, char *, char *));
-
-int plugin_register_shutdown (char *, void (*) (void));
+/*
+ * The `plugin_register_*' functions are used to make `config', `init',
+ * `read', `write' and `shutdown' functions known to the plugin
+ * infrastructure. Also, the data-formats are made public like this.
+ */
+int plugin_register_config (const char *name,
+               int (*callback) (const char *key, const char *val),
+               const char **keys, int keys_num);
+int plugin_register_init (const char *name,
+               int (*callback) (void));
+int plugin_register_read (const char *name,
+               int (*callback) (void));
+int plugin_register_write (const char *name,
+               int (*callback) (const data_set_t *ds, const value_list_t *vl));
+int plugin_register_shutdown (char *name,
+               int (*callback) (void));
+int plugin_register_data_set (const data_set_t *ds);
 
 /*
  * NAME
- *  plugin_write
+ *  plugin_dispatch_values
  *
  * DESCRIPTION
- *  Searches the plugin for `type' in the plugin-list. If found, and a `write'
- *  function is registered, it's called. If either the plugin is not found or
- *  the plugin doesn't provide a `write' function this function will return
- *  without further notice.
+ *  This function is called by reading processes with the values they've
+ *  aquired. The function fetches the data-set definition (that has been
+ *  registered using `plugin_register_data_set') and calls _all_ registered
+ *  write-functions.
  *
  * ARGUMENTS
- *  `host'      Host(name) from which the data originates.
- *  `type'      Name of the plugin.
- *  `inst'      Instance (passed to the plugin's `write' function.
- *  `val'       Values for the RRD files. Also passed to the plugin.
+ *  `name'      Name/type of the data-set that describe the values in `vl'.
+ *  `vl'        Value list of the values that have been read by a `read'
+ *              function.
  */
-void plugin_write    (char *host, char *type, char *inst, char *val);
-
-void plugin_submit   (char *type, char *inst, char *val);
-
+int plugin_dispatch_values (const char *name, const value_list_t *vl);
 
+/* TODO: Move plugin_{complain,relief} into `utils_complain.[ch]'. -octo */
 void plugin_complain (int level, complain_t *c, const char *format, ...);
 void plugin_relief (int level, complain_t *c, const char *format, ...);
 
diff --git a/src/rrdtool.c b/src/rrdtool.c
new file mode 100644 (file)
index 0000000..5a9fa8c
--- /dev/null
@@ -0,0 +1,655 @@
+/**
+ * collectd - src/rrdtool.c
+ * Copyright (C) 2006  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
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "utils_llist.h"
+#include "utils_debug.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
+
+/*
+ * Private types
+ */
+struct rrd_cache_s
+{
+       int    values_num;
+       char **values;
+       time_t first_value;
+};
+typedef struct rrd_cache_s rrd_cache_t;
+
+/*
+ * Private variables
+ */
+static int rra_timespans[] =
+{
+       3600,
+       86400,
+       604800,
+       2678400,
+       31622400,
+       0
+};
+static int rra_timespans_num = 5;
+
+static char *rra_types[] =
+{
+       "AVERAGE",
+       "MIN",
+       "MAX",
+       NULL
+};
+static int rra_types_num = 3;
+
+static const char *config_keys[] =
+{
+       "CacheTimeout",
+       NULL
+};
+static int config_keys_num = 1;
+
+static int      cache_timeout = 0;
+static time_t   cache_flush;
+static llist_t *cache = NULL;
+
+/* * * * * * * * * *
+ * WARNING:  Magic *
+ * * * * * * * * * */
+static int rra_get (char ***ret)
+{
+       static char **rra_def = NULL;
+       static int rra_num = 0;
+
+       int rra_max = rra_timespans_num * rra_types_num;
+
+       int step;
+       int rows;
+       int span;
+
+       int cdp_num;
+       int cdp_len;
+       int i, j;
+
+       char buffer[64];
+
+       if ((rra_num != 0) && (rra_def != NULL))
+       {
+               *ret = rra_def;
+               return (rra_num);
+       }
+
+       if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
+               return (-1);
+       memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
+
+       step = atoi (COLLECTD_STEP);
+       rows = atoi (COLLECTD_ROWS);
+
+       if ((step <= 0) || (rows <= 0))
+       {
+               *ret = NULL;
+               return (-1);
+       }
+
+       cdp_len = 0;
+       for (i = 0; i < rra_timespans_num; i++)
+       {
+               span = rra_timespans[i];
+
+               if ((span / step) < rows)
+                       continue;
+
+               if (cdp_len == 0)
+                       cdp_len = 1;
+               else
+                       cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
+
+               cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
+
+               for (j = 0; j < rra_types_num; j++)
+               {
+                       if (rra_num >= rra_max)
+                               break;
+
+                       if (snprintf (buffer, sizeof(buffer), "RRA:%s:%3.1f:%u:%u",
+                                               rra_types[j], COLLECTD_XFF,
+                                               cdp_len, cdp_num) >= sizeof (buffer))
+                       {
+                               syslog (LOG_ERR, "rra_get: Buffer would have been truncated.");
+                               continue;
+                       }
+
+                       rra_def[rra_num++] = sstrdup (buffer);
+               }
+       }
+
+#if COLLECT_DEBUG
+       DBG ("rra_num = %i", rra_num);
+       for (i = 0; i < rra_num; i++)
+               DBG ("  %s", rra_def[i]);
+#endif
+
+       *ret = rra_def;
+       return (rra_num);
+}
+
+static void ds_free (int ds_num, char **ds_def)
+{
+       int i;
+
+       for (i = 0; i < ds_num; i++)
+               if (ds_def[i] != NULL)
+                       free (ds_def[i]);
+       free (ds_def);
+}
+
+static int ds_get (char ***ret, const data_set_t *ds)
+{
+       char **ds_def;
+       int ds_num;
+
+       char min[32];
+       char max[32];
+       char buffer[128];
+
+       DBG ("ds->ds_num = %i", ds->ds_num);
+
+       ds_def = (char **) malloc (ds->ds_num * sizeof (char *));
+       if (ds_def == NULL)
+       {
+               syslog (LOG_ERR, "rrdtool plugin: malloc failed: %s",
+                               strerror (errno));
+               return (-1);
+       }
+       memset (ds_def, '\0', ds->ds_num * sizeof (char *));
+
+       for (ds_num = 0; ds_num < ds->ds_num; ds_num++)
+       {
+               data_source_t *d = ds->ds + ds_num;
+               char *type;
+               int status;
+
+               ds_def[ds_num] = NULL;
+
+               if (d->type == DS_TYPE_COUNTER)
+                       type = "COUNTER";
+               else if (d->type == DS_TYPE_GAUGE)
+                       type = "GAUGE";
+               else
+               {
+                       syslog (LOG_ERR, "rrdtool plugin: Unknown DS type: %i",
+                                       d->type);
+                       break;
+               }
+
+               if (d->min == NAN)
+               {
+                       strcpy (min, "U");
+               }
+               else
+               {
+                       snprintf (min, sizeof (min), "%lf", d->min);
+                       min[sizeof (min) - 1] = '\0';
+               }
+
+               if (d->max == NAN)
+               {
+                       strcpy (max, "U");
+               }
+               else
+               {
+                       snprintf (max, sizeof (max), "%lf", d->max);
+                       max[sizeof (max) - 1] = '\0';
+               }
+
+               status = snprintf (buffer, sizeof (buffer),
+                               "DS:%s:%s:%s:%s:%s",
+                               d->name, type, COLLECTD_HEARTBEAT,
+                               min, max);
+               if ((status < 1) || (status >= sizeof (buffer)))
+                       break;
+
+               ds_def[ds_num] = sstrdup (buffer);
+       } /* for ds_num = 0 .. ds->ds_num */
+
+#if COLLECT_DEBUG
+{
+       int i;
+       DBG ("ds_num = %i", ds_num);
+       for (i = 0; i < ds_num; i++)
+               DBG ("  %s", ds_def[i]);
+}
+#endif
+
+       if (ds_num != ds->ds_num)
+       {
+               ds_free (ds_num, ds_def);
+               return (-1);
+       }
+
+       *ret = ds_def;
+       return (ds_num);
+}
+
+static int rrd_create_file (char *filename, const data_set_t *ds)
+{
+       char **argv;
+       int argc;
+       char **rra_def;
+       int rra_num;
+       char **ds_def;
+       int ds_num;
+       int i, j;
+       int status = 0;
+
+       if (check_create_dir (filename))
+               return (-1);
+
+       if ((rra_num = rra_get (&rra_def)) < 1)
+       {
+               syslog (LOG_ERR, "rrd_create_file failed: Could not calculate RRAs");
+               return (-1);
+       }
+
+       if ((ds_num = ds_get (&ds_def, ds)) < 1)
+       {
+               syslog (LOG_ERR, "rrd_create_file failed: Could not calculate DSes");
+               return (-1);
+       }
+
+       argc = ds_num + rra_num + 4;
+
+       if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
+       {
+               syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno));
+               return (-1);
+       }
+
+       argv[0] = "create";
+       argv[1] = filename;
+       argv[2] = "-s";
+       argv[3] = COLLECTD_STEP;
+
+       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;
+
+       optind = 0; /* bug in librrd? */
+       rrd_clear_error ();
+       if (rrd_create (argc, argv) == -1)
+       {
+               syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ());
+               status = -1;
+       }
+
+       free (argv);
+       ds_free (ds_num, ds_def);
+
+       return (status);
+}
+
+static int value_list_to_string (char *buffer, int buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       int offset;
+       int status;
+       int i;
+
+       memset (buffer, '\0', sizeof (buffer_len));
+
+       status = snprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
+       if ((status < 1) || (status >= buffer_len))
+               return (-1);
+       offset = status;
+
+       for (i = 0; i < ds->ds_num; i++)
+       {
+               if ((ds->ds[i].type != DS_TYPE_COUNTER)
+                               && (ds->ds[i].type != DS_TYPE_GAUGE))
+                       return (-1);
+
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+                       status = snprintf (buffer + offset, buffer_len - offset,
+                                       ":%llu", vl->values[i].counter);
+               else
+                       status = snprintf (buffer + offset, buffer_len - offset,
+                                       ":%lf", vl->values[i].gauge);
+
+               if ((status < 1) || (status >= (buffer_len - offset)))
+                       return (-1);
+
+               offset += status;
+       } /* for ds->ds_num */
+
+       return (0);
+} /* int value_list_to_string */
+
+static int value_list_to_filename (char *buffer, int buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       int offset = 0;
+       int status;
+
+       status = snprintf (buffer + offset, buffer_len - offset,
+                       "%s/", vl->host);
+       if ((status < 1) || (status >= buffer_len - offset))
+               return (-1);
+       offset += status;
+
+       if (strlen (vl->plugin_instance) > 0)
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s-%s/", vl->plugin, vl->plugin_instance);
+       else
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s/", vl->plugin);
+       if ((status < 1) || (status >= buffer_len - offset))
+               return (-1);
+       offset += status;
+
+       if (strlen (vl->type_instance) > 0)
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s-%s.rrd", ds->type, vl->type_instance);
+       else
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s.rrd", ds->type);
+       if ((status < 1) || (status >= buffer_len - offset))
+               return (-1);
+       offset += status;
+
+       return (0);
+} /* int value_list_to_filename */
+
+static rrd_cache_t *rrd_cache_insert (const char *filename,
+               const char *value)
+{
+       rrd_cache_t *rc = NULL;
+       llentry_t   *le = NULL;
+
+       if (cache != NULL)
+       {
+               le = llist_search (cache, filename);
+               if (le != NULL)
+                       rc = (rrd_cache_t *) le->value;
+       }
+
+       if (rc == NULL)
+       {
+               rc = (rrd_cache_t *) malloc (sizeof (rrd_cache_t));
+               if (rc == NULL)
+                       return (NULL);
+               rc->values_num = 0;
+               rc->values = NULL;
+               rc->first_value = 0;
+       }
+
+       rc->values = (char **) realloc ((void *) rc->values,
+                       (rc->values_num + 1) * sizeof (char *));
+       if (rc->values == NULL)
+       {
+               syslog (LOG_ERR, "rrdtool plugin: realloc failed: %s",
+                               strerror (errno));
+               free (rc);
+               if (le != NULL)
+               {
+                       llist_remove (cache, le);
+                       llentry_destroy (le);
+               }
+               return (NULL);
+       }
+
+       rc->values[rc->values_num] = strdup (value);
+       if (rc->values[rc->values_num] != NULL)
+               rc->values_num++;
+
+       if (rc->values_num == 1)
+               rc->first_value = time (NULL);
+
+       if ((cache != NULL) && (le == NULL))
+       {
+               le = llentry_create (filename, (void *) rc);
+               if (le != NULL)
+                       llist_prepend (cache, le);
+       }
+
+       DBG ("rrd_cache_insert (%s, %s) = %p", filename, value, (void *) rc);
+
+       return (rc);
+} /* rrd_cache_t *rrd_cache_insert */
+
+static int rrd_write_cache_entry (const char *filename, rrd_cache_t *rc)
+{
+       char **argv;
+       int    argc;
+
+       char *fn;
+       int status;
+
+       argc = rc->values_num + 2;
+       argv = (char **) malloc ((argc + 1) * sizeof (char *));
+       if (argv == NULL)
+               return (-1);
+
+       fn = strdup (filename);
+       if (fn == NULL)
+       {
+               free (argv);
+               return (-1);
+       }
+
+       argv[0] = "update";
+       argv[1] = fn;
+       memcpy (argv + 2, rc->values, rc->values_num * sizeof (char *));
+       argv[argc] = NULL;
+
+       DBG ("rrd_update (argc = %i, argv = %p)", argc, (void *) argv);
+
+       optind = 0; /* bug in librrd? */
+       rrd_clear_error ();
+       status = rrd_update (argc, argv);
+
+       free (argv);
+       free (fn);
+
+       free (rc->values);
+       rc->values = NULL;
+       rc->values_num = 0;
+
+       if (status != 0)
+       {
+               syslog (LOG_WARNING, "rrd_update failed: %s: %s",
+                               filename, rrd_get_error ());
+               return (-1);
+       }
+
+       return (0);
+} /* int rrd_update_file */
+
+static void rrd_cache_flush (int timeout)
+{
+       llentry_t   *le;
+       rrd_cache_t *rc;
+       time_t       now;
+
+       if (cache == NULL)
+               return;
+
+       DBG ("Flushing cache, timeout = %i", timeout);
+
+       now = time (NULL);
+
+       /* Remove empty entries */
+       le = llist_head (cache);
+       while (le != NULL)
+       {
+               llentry_t *next = le->next;
+               rc = (rrd_cache_t *) le->value;
+               if (rc->values_num == 0)
+               {
+                       DBG ("Removing cache entry for `%s'", le->key);
+                       free (rc->values);
+                       free (rc);
+                       llist_remove (cache, le);
+               }
+               le = next;
+       }
+
+       /* Write timed out entries */
+       le = llist_head (cache);
+       while (le != NULL)
+       {
+               rc = (rrd_cache_t *) le->value;
+               if ((now - rc->first_value) >= timeout)
+                       rrd_write_cache_entry (le->key, rc);
+
+               le = le->next;
+       }
+
+       cache_flush = now;
+} /* void rrd_cache_flush */
+
+static int rrd_write (const data_set_t *ds, const value_list_t *vl)
+{
+       struct stat  statbuf;
+       char         filename[512];
+       char         values[512];
+       rrd_cache_t *rc;
+       time_t       now;
+
+       if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+               return (-1);
+
+       if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
+               return (-1);
+
+       if (stat (filename, &statbuf) == -1)
+       {
+               if (errno == ENOENT)
+               {
+                       if (rrd_create_file (filename, ds))
+                               return (-1);
+               }
+               else
+               {
+                       syslog (LOG_ERR, "stat(%s) failed: %s",
+                                       filename, strerror (errno));
+                       return (-1);
+               }
+       }
+       else if (!S_ISREG (statbuf.st_mode))
+       {
+               syslog (LOG_ERR, "stat(%s): Not a regular file!",
+                               filename);
+               return (-1);
+       }
+
+       rc = rrd_cache_insert (filename, values);
+       if (rc == NULL)
+               return (-1);
+
+       if (cache == NULL)
+       {
+               rrd_write_cache_entry (filename, rc);
+               free (rc->values);
+               free (rc);
+               return (0);
+       }
+
+       now = time (NULL);
+
+       DBG ("age (%s) = %i", filename, now - rc->first_value);
+
+       if ((now - rc->first_value) >= cache_timeout)
+               rrd_write_cache_entry (filename, rc);
+
+       if ((time (NULL) - cache_flush) >= cache_timeout)
+       {
+               rrd_cache_flush (cache_timeout);
+       }
+
+       return (0);
+} /* int rrd_dispatch */
+
+static int rrd_config (const char *key, const char *val)
+{
+       if (strcasecmp ("CacheTimeout", key) == 0)
+       {
+               int tmp = atoi (val);
+               if (tmp < 0)
+               {
+                       fprintf (stderr, "rrdtool: `CacheTimeout' must "
+                                       "be greater than 0.\n");
+                       return (1);
+               }
+               cache_timeout = tmp;
+       }
+       else
+       {
+               return (-1);
+       }
+       return (0);
+} /* int rrd_config */
+
+static int rrd_shutdown (void)
+{
+       rrd_cache_flush (-1);
+
+       return (0);
+} /* int rrd_shutdown */
+
+static int rrd_init (void)
+{
+       if (cache_timeout < 2)
+       {
+               cache_timeout = 0;
+       }
+       else
+       {
+               cache = llist_create ();
+               cache_flush = time (NULL);
+               plugin_register_shutdown ("rrdtool", rrd_shutdown);
+       }
+       return (0);
+} /* int rrd_init */
+
+void module_register (void)
+{
+       plugin_register_config ("rrdtool", rrd_config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("rrdtool", rrd_init);
+       plugin_register_write ("rrdtool", rrd_write);
+}
index 66cd5b2..14d8abd 100644 (file)
@@ -29,6 +29,8 @@
  *   - config IgnoreSelected option
  **/
 
+
+
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 #include "utils_ignorelist.h"
 #include "utils_debug.h"
 
-#define MODULE_NAME "sensors"
-#define MODULE_NAME_VOLTAGE MODULE_NAME"_voltage"
+/*
+ * 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
 
 #if defined(HAVE_SENSORS_SENSORS_H)
 # include <sensors/sensors.h>
 # define SENSORS_HAVE_READ 0
 #endif
 
-#define BUFSIZE 512
-
-/* temperature and fan sensors */
-static char *ds_def[] =
+static data_source_t data_source[1] =
 {
-       "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
+       {"value", DS_TYPE_GAUGE, NAN, NAN}
 };
-static int ds_num = 1;
-
-/* voltage sensors */
-static char *sensor_voltage_ds_def[] = 
+static data_set_t fanspeed_ds =
 {
-       "DS:voltage:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
+       "fanspeed", 1, data_source
 };
-static int sensor_voltage_ds_num = 1;
-
-/* old naming */
-static char *old_filename_format = "sensors-%s.rrd";
-/* end old naming */
 
-/* new naming <chip-bus-address/type-feature */
-static char *extended_filename_format = "lm_sensors-%s.rrd";
+static data_set_t temperature_ds =
+{
+       "temperature", 1, data_source
+};
 
-#define SENSOR_TYPE_UNKNOWN 0
-#define SENSOR_TYPE_VOLTAGE 1
-#define SENSOR_TYPE_FANSPEED 2
-#define SENSOR_TYPE_TEMPERATURE 3
+static data_set_t voltage_ds =
+{
+       "voltage", 1, data_source
+};
 
 #if SENSORS_HAVE_READ
-static char *sensor_type_prefix[] =
+#define SENSOR_TYPE_VOLTAGE     0
+#define SENSOR_TYPE_FANSPEED    1
+#define SENSOR_TYPE_TEMPERATURE 2
+#define SENSOR_TYPE_UNKNOWN     3
+
+static char *sensor_to_type[] =
 {
-       "unknown",
        "voltage",
        "fanspeed",
        "temperature",
        NULL
 };
-#endif
 
-typedef struct sensors_labeltypes {
+struct sensors_labeltypes_s
+{
        char *label;
        int type;
-} sensors_labeltypes;
+};
+typedef struct sensors_labeltypes_s sensors_labeltypes_t;
 
 /*
  * finite list of known labels extracted from lm_sensors
  */
-#if SENSORS_HAVE_READ
-static sensors_labeltypes known_features[] = 
+static sensors_labeltypes_t known_features[] = 
 {
        { "fan1", SENSOR_TYPE_FANSPEED },
        { "fan2", SENSOR_TYPE_FANSPEED },
@@ -155,33 +161,23 @@ static sensors_labeltypes known_features[] =
        { "2.5V", SENSOR_TYPE_VOLTAGE },
        { "2.0V", SENSOR_TYPE_VOLTAGE },
        { "12V", SENSOR_TYPE_VOLTAGE },
-       { 0, -1 }
+       { (char *) 0, SENSOR_TYPE_UNKNOWN }
 };
-#endif
 /* end new naming */
 
-static char *config_keys[] =
+static const char *config_keys[] =
 {
        "Sensor",
        "IgnoreSelected",
-       "ExtendedSensorNaming",
        NULL
 };
-static int config_keys_num = 3;
+static int config_keys_num = 2;
 
 static ignorelist_t *sensor_list;
 
-/* 
- * sensor_extended_naming:
- * 0 => default is to create chip-feature
- * 1 => use new naming scheme chip-bus-address/type-feature
- */
-static int sensor_extended_naming = 0;
-
-#if SENSORS_HAVE_READ
-#  ifndef SENSORS_CONF_PATH
-#    define SENSORS_CONF_PATH "/etc/sensors.conf"
-#  endif
+#ifndef SENSORS_CONF_PATH
+# define SENSORS_CONF_PATH "/etc/sensors.conf"
+#endif
 
 static const char *conffile = SENSORS_CONF_PATH;
 /* SENSORS_CONF_PATH */
@@ -200,9 +196,8 @@ typedef struct featurelist
 } featurelist_t;
 
 featurelist_t *first_feature = NULL;
-#endif /* if SENSORS_HAVE_READ */
 
-static int sensors_config (char *key, char *value)
+static int sensors_config (const char *key, const char *value)
 {
        if (sensor_list == NULL)
                sensor_list = ignorelist_create (1);
@@ -211,7 +206,8 @@ static int sensors_config (char *key, char *value)
        {
                if (ignorelist_add (sensor_list, value))
                {
-                       syslog (LOG_EMERG, MODULE_NAME": Cannot add value to ignorelist.");
+                       syslog (LOG_ERR, "sensors plugin: "
+                                       "Cannot add value to ignorelist.");
                        return (1);
                }
        }
@@ -223,15 +219,6 @@ static int sensors_config (char *key, char *value)
                                || (strcasecmp (value, "On") == 0))
                        ignorelist_set_invert (sensor_list, 0);
        }
-       else if (strcasecmp (key, "ExtendedSensorNaming") == 0)
-       {
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
-                       sensor_extended_naming = 1;
-               else
-                       sensor_extended_naming = 0;
-       }
        else
        {
                return (-1);
@@ -240,7 +227,6 @@ static int sensors_config (char *key, char *value)
        return (0);
 }
 
-#if SENSORS_HAVE_READ
 void sensors_free_features (void)
 {
        featurelist_t *thisft;
@@ -277,7 +263,7 @@ static void sensors_load_conf (void)
        status = stat (conffile, &statbuf);
        if (status != 0)
        {
-               syslog (LOG_ERR, MODULE_NAME": stat(%s) failed: %s",
+               syslog (LOG_ERR, "sensors plugin: stat (%s) failed: %s",
                                conffile, strerror (errno));
                sensors_config_mtime = 0;
        }
@@ -288,7 +274,7 @@ static void sensors_load_conf (void)
 
        if (sensors_config_mtime != 0)
        {
-               syslog (LOG_NOTICE, MODULE_NAME": Reloading config from %s",
+               syslog (LOG_NOTICE, "sensors plugin: Reloading config from %s",
                                conffile);
                sensors_free_features ();
                sensors_config_mtime = 0;
@@ -297,7 +283,7 @@ static void sensors_load_conf (void)
        fh = fopen (conffile, "r");
        if (fh == NULL)
        {
-               syslog (LOG_ERR, MODULE_NAME": fopen(%s) failed: %s",
+               syslog (LOG_ERR, "sensors plugin: fopen(%s) failed: %s",
                                conffile, strerror(errno));
                return;
        }
@@ -306,7 +292,7 @@ static void sensors_load_conf (void)
        fclose (fh);
        if (status != 0)
        {
-               syslog (LOG_ERR, MODULE_NAME": Cannot initialize sensors. "
+               syslog (LOG_ERR, "sensors plugin: Cannot initialize sensors. "
                                "Data will not be collected.");
                return;
        }
@@ -346,7 +332,7 @@ static void sensors_load_conf (void)
                                if ((new_feature = (featurelist_t *) malloc (sizeof (featurelist_t))) == NULL)
                                {
                                        DBG ("malloc: %s", strerror (errno));
-                                       syslog (LOG_ERR, MODULE_NAME":  malloc: %s",
+                                       syslog (LOG_ERR, "sensors plugin:  malloc: %s",
                                                        strerror (errno));
                                        break;
                                }
@@ -376,107 +362,50 @@ static void sensors_load_conf (void)
        if (first_feature == NULL)
        {
                sensors_cleanup ();
-               syslog (LOG_INFO, MODULE_NAME": lm_sensors reports no features. "
-                       "Data will not be collected.");
+               syslog (LOG_INFO, "sensors plugin: lm_sensors reports no "
+                               "features. Data will not be collected.");
        }
 } /* void sensors_load_conf */
-#endif /* if SENSORS_HAVE_READ */
-
-static void collectd_sensors_init (void)
-{
-       return;
-}
 
-static void sensors_shutdown (void)
+static int sensors_shutdown (void)
 {
-#if SENSORS_HAVE_READ
        sensors_free_features ();
-#endif /* if SENSORS_HAVE_READ */
-
-       if (NULL != sensor_list)
-               ignorelist_free (sensor_list);
-}
-
-static void sensors_voltage_write (char *host, char *inst, char *val)
-{
-       char file[BUFSIZE];
-       int status;
-
-       /* skip ignored in our config */
-       if ((NULL != sensor_list) && ignorelist_match (sensor_list, inst))
-               return;
-
-       /* extended sensor naming */
-       if(sensor_extended_naming)
-               status = snprintf (file, BUFSIZE, extended_filename_format, inst);
-       else
-               status = snprintf (file, BUFSIZE, old_filename_format, inst);
-
-       if ((status < 1) || (status >= BUFSIZE))
-               return;
-
-       rrd_update_file (host, file, val, sensor_voltage_ds_def, sensor_voltage_ds_num);
-}
-
-static void sensors_write (char *host, char *inst, char *val)
-{
-       char file[BUFSIZE];
-       int status;
-
-       /* skip ignored in our config */
-       if ((NULL != sensor_list) && ignorelist_match (sensor_list, inst))
-               return;
-
-       /* extended sensor naming */
-       if (sensor_extended_naming)
-               status = snprintf (file, BUFSIZE, extended_filename_format, inst);
-       else
-               status = snprintf (file, BUFSIZE, old_filename_format, inst);
-
-       if ((status < 1) || (status >= BUFSIZE))
-               return;
+       ignorelist_free (sensor_list);
 
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
+       return (0);
+} /* int sensors_shutdown */
 
-#if SENSORS_HAVE_READ
-static void sensors_submit (const char *feat_name,
-               const char *chip_prefix, double value, int type)
+static void sensors_submit (const char *plugin_instance,
+               const char *type, const char *type_instance,
+               double val)
 {
-       char buf[BUFSIZE];
-       char inst[BUFSIZE];
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf (inst, BUFSIZE, "%s-%s", chip_prefix, feat_name)
-                       >= BUFSIZE)
+       if (ignorelist_match (sensor_list, type_instance))
                return;
 
-       /* skip ignored in our config */
-       if ((NULL != sensor_list) && ignorelist_match (sensor_list, inst))
-               return;
+       values[0].gauge = val;
 
-       if (snprintf (buf, BUFSIZE, "%u:%.3f", (unsigned int) curtime,
-                               value) >= BUFSIZE)
-               return;
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname);
+       strcpy (vl.plugin, "sensors");
+       strcpy (vl.plugin_instance, plugin_instance);
+       strcpy (vl.type_instance, type_instance);
 
-       if (type == SENSOR_TYPE_VOLTAGE)
-       {
-               DBG ("%s: %s/%s, %s", MODULE_NAME_VOLTAGE,
-                               sensor_type_prefix[type], inst, buf);
-               plugin_submit (MODULE_NAME_VOLTAGE, inst, buf);
-       }
-       else
-       {
-               DBG ("%s: %s/%s, %s", MODULE_NAME,
-                               sensor_type_prefix[type], inst, buf);
-               plugin_submit (MODULE_NAME, inst, buf);
-       }
-}
+       plugin_dispatch_values (type, &vl);
+} /* void sensors_submit */
 
-static void sensors_read (void)
+static int sensors_read (void)
 {
        featurelist_t *feature;
        double value;
-       char chip_fullprefix[BUFSIZE];
+       char chip_fullprefix[512];
+
+       char plugin_instance[DATA_MAX_NAME_LEN];
+       char type_instance[DATA_MAX_NAME_LEN];
 
        sensors_load_conf ();
 
@@ -485,62 +414,54 @@ static void sensors_read (void)
                if (sensors_get_feature (*feature->chip, feature->data->number, &value) < 0)
                        continue;
 
-               if (sensor_extended_naming)
+               /* full chip name logic borrowed from lm_sensors */
+               if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_ISA)
                {
-                       /* full chip name logic borrowed from lm_sensors */
-                       if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_ISA)
-                       {
-                               if (snprintf (chip_fullprefix, BUFSIZE, "%s-isa-%04x/%s",
-                                                       feature->chip->prefix,
-                                                       feature->chip->addr,
-                                                       sensor_type_prefix[feature->type])
-                                               >= BUFSIZE)
-                                       continue;
-                       }
-                       else if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_DUMMY)
-                       {
-                               if (snprintf (chip_fullprefix, BUFSIZE, "%s-%s-%04x/%s",
-                                                       feature->chip->prefix,
-                                                       feature->chip->busname,
-                                                       feature->chip->addr,
-                                                       sensor_type_prefix[feature->type])
-                                               >= BUFSIZE)
-                                       continue;
-                       }
-                       else
-                       {
-                               if (snprintf (chip_fullprefix, BUFSIZE, "%s-i2c-%d-%02x/%s",
-                                                       feature->chip->prefix,
-                                                       feature->chip->bus,
-                                                       feature->chip->addr,
-                                                       sensor_type_prefix[feature->type])
-                                               >= BUFSIZE)
-                                       continue;
-                       }
-
-                       sensors_submit (feature->data->name,
-                                       chip_fullprefix,
-                                       value, feature->type);
+                       if (snprintf (plugin_instance, DATA_MAX_NAME_LEN, "%s-isa-%04x",
+                                               feature->chip->prefix,
+                                               feature->chip->addr)
+                                       >= 512)
+                               continue;
+               }
+               else if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_DUMMY)
+               {
+                       if (snprintf (plugin_instance, 512, "%s-%s-%04x",
+                                               feature->chip->prefix,
+                                               feature->chip->busname,
+                                               feature->chip->addr)
+                                       >= 512)
+                               continue;
                }
                else
                {
-                       sensors_submit (feature->data->name,
-                                       feature->chip->prefix,
-                                       value, feature->type);
+                       if (snprintf (plugin_instance, 512, "%s-i2c-%d-%02x",
+                                               feature->chip->prefix,
+                                               feature->chip->bus,
+                                               feature->chip->addr)
+                                       >= 512)
+                               continue;
                }
+
+               strncpy (type_instance, feature->data->name, DATA_MAX_NAME_LEN);
+
+               sensors_submit (plugin_instance,
+                               sensor_to_type[feature->type]
+                               type_instance,
+                               value);
        } /* for feature = first_feature .. NULL */
-} /* void sensors_read */
-#else
-# define sensors_read NULL
+} /* int sensors_read */
 #endif /* SENSORS_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, collectd_sensors_init, sensors_read, sensors_write);
-       plugin_register (MODULE_NAME_VOLTAGE, NULL, NULL, sensors_voltage_write);
-       plugin_register_shutdown (MODULE_NAME, sensors_shutdown);
-       cf_register (MODULE_NAME, sensors_config, config_keys, config_keys_num);
-}
+       plugin_register_data_set (&fanspeed_ds);
+       plugin_register_data_set (&temperature_ds);
+       plugin_register_data_set (&voltage_ds);
 
-#undef BUFSIZE
-#undef MODULE_NAME
+#if SENSORS_HAVE_READ
+       plugin_register_config ("sensors", sensors_config,
+                       config_keys, config_keys_num);
+       plugin_register_read ("sensors", sensors_read);
+       plugin_register_shutdown ("sensors", sensors_shutdown);
+#endif
+} /* void module_register */
index 14b7dc3..5c5e4f7 100644 (file)
 /*
  * (Module-)Global variables
  */
-/* TODO: Move this to `interface-%s/<blah>.rrd' in version 4. */
-static char *bytes_file   = "traffic-%s.rrd";
-static char *packets_file = "interface-%s/if_packets.rrd";
-static char *errors_file  = "interface-%s/if_errors.rrd";
-/* TODO: Maybe implement multicast and broadcast counters */
+/* 2^32 = 4294967296 = ~4.2GByte/s = ~34GBit/s */
+static data_source_t octets_dsrc[2] =
+{
+       {"rx", DS_TYPE_COUNTER, 0, 4294967295.0},
+       {"tx", DS_TYPE_COUNTER, 0, 4294967295.0}
+};
 
-static char *config_keys[] =
+static data_set_t octets_ds =
 {
-       "Interface",
-       "IgnoreSelected",
-       NULL
+       "if_octets", 2, octets_dsrc
 };
-static int config_keys_num = 2;
 
-static char *bytes_ds_def[] =
+static data_source_t packets_dsrc[2] =
 {
-       "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
+       {"rx", DS_TYPE_COUNTER, 0, 4294967295.0},
+       {"tx", DS_TYPE_COUNTER, 0, 4294967295.0}
 };
-static int bytes_ds_num = 2;
 
-static char *packets_ds_def[] =
+static data_set_t packets_ds =
 {
-       "DS:rx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:tx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
+       "if_packets", 2, packets_dsrc
+};
+
+static data_source_t errors_dsrc[2] =
+{
+       {"rx", DS_TYPE_COUNTER, 0, 4294967295.0},
+       {"tx", DS_TYPE_COUNTER, 0, 4294967295.0}
 };
-static int packets_ds_num = 2;
 
-static char *errors_ds_def[] =
+static data_set_t errors_ds =
 {
-       "DS:rx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:tx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
+       "if_errors", 2, errors_dsrc
+};
+
+static const char *config_keys[] =
+{
+       "Interface",
+       "IgnoreSelected",
        NULL
 };
-static int errors_ds_num = 2;
+static int config_keys_num = 2;
 
 static char **if_list = NULL;
 static int    if_list_num = 0;
@@ -126,7 +130,7 @@ static kstat_t *ksp[MAX_NUMIF];
 static int numif = 0;
 #endif /* HAVE_LIBKSTAT */
 
-static int traffic_config (char *key, char *value)
+static int interface_config (const char *key, const char *value)
 {
        char **temp;
 
@@ -164,24 +168,17 @@ static int traffic_config (char *key, char *value)
        return (0);
 }
 
-static void traffic_init (void)
+#if HAVE_LIBKSTAT
+static int traffic_init (void)
 {
-#if HAVE_GETIFADDRS
-       /* nothing */
-/* #endif HAVE_GETIFADDRS */
-
-#elif KERNEL_LINUX
-       /* nothing */
-/* #endif KERNEL_LINUX */
-
-#elif HAVE_LIBKSTAT
+#if HAVE_LIBKSTAT
        kstat_t *ksp_chain;
        unsigned long long val;
 
        numif = 0;
 
        if (kc == NULL)
-               return;
+               return (-1);
 
        for (numif = 0, ksp_chain = kc->kc_chain;
                        (numif < MAX_NUMIF) && (ksp_chain != NULL);
@@ -197,14 +194,11 @@ static void traffic_init (void)
                        continue;
                ksp[numif++] = ksp_chain;
        }
-/* #endif HAVE_LIBKSTAT */
-
-#elif HAVE_LIBSTATG
-       /* nothing */
-#endif /* HAVE_LIBSTATG */
+#endif /* HAVE_LIBKSTAT */
 
-       return;
-}
+       return (0);
+} /* int traffic_init */
+#endif /* HAVE_LIBKSTAT */
 
 /*
  * Check if this interface/instance should be ignored. This is called from
@@ -224,101 +218,34 @@ static int check_ignore_if (const char *interface)
                if (strcasecmp (interface, if_list[i]) == 0)
                        return (if_list_action);
        return (1 - if_list_action);
-}
-
-static void generic_write (char *host, char *inst, char *val,
-               char *file_template,
-               char **ds_def, int ds_num)
-{
-       char file[512];
-       int status;
-
-       if (check_ignore_if (inst))
-               return;
-
-       status = snprintf (file, BUFSIZE, file_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
-
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
-
-static void bytes_write (char *host, char *inst, char *val)
-{
-       generic_write (host, inst, val, bytes_file, bytes_ds_def, bytes_ds_num);
-}
-
-static void packets_write (char *host, char *inst, char *val)
-{
-       generic_write (host, inst, val, packets_file, packets_ds_def, packets_ds_num);
-}
-
-static void errors_write (char *host, char *inst, char *val)
-{
-       generic_write (host, inst, val, errors_file, errors_ds_def, errors_ds_num);
-}
+} /* int check_ignore_if */
 
 #if TRAFFIC_HAVE_READ
-static void bytes_submit (char *dev,
+static void if_submit (const char *dev, const char *type,
                unsigned long long rx,
                unsigned long long tx)
 {
-       char buf[512];
-       int  status;
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
 
        if (check_ignore_if (dev))
                return;
 
-       status = snprintf (buf, 512, "%u:%lld:%lld",
-                               (unsigned int) curtime,
-                               rx, tx);
-       if ((status >= 512) || (status < 1))
-               return;
-
-       plugin_submit (MODULE_NAME, dev, buf);
-}
-
-#if HAVE_GETIFADDRS || KERNEL_LINUX || HAVE_LIBKSTAT
-static void packets_submit (char *dev,
-               unsigned long long rx,
-               unsigned long long tx)
-{
-       char buf[512];
-       int  status;
-
-       if (check_ignore_if (dev))
-               return;
-
-       status = snprintf (buf, 512, "%u:%lld:%lld",
-                       (unsigned int) curtime,
-                       rx, tx);
-       if ((status >= 512) || (status < 1))
-               return;
-       plugin_submit ("if_packets", dev, buf);
-}
-
-static void errors_submit (char *dev,
-               unsigned long long rx,
-               unsigned long long tx)
-{
-       char buf[512];
-       int  status;
+       values[0].counter = rx;
+       values[1].counter = tx;
 
-       if (check_ignore_if (dev))
-               return;
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname);
+       strcpy (vl.plugin, "interface");
+       strcpy (vl.plugin_instance, "");
+       strncpy (vl.type_instance, dev, sizeof (vl.type_instance));
 
-       status = snprintf (buf, 512, "%u:%lld:%lld",
-                       (unsigned int) curtime,
-                       rx, tx);
-       if ((status >= 512) || (status < 1))
-               return;
-       plugin_submit ("if_errors", dev, buf);
-}
-#endif /* HAVE_GETIFADDRS || KERNEL_LINUX || HAVE_LIBKSTAT */
+       plugin_dispatch_values (type, &vl);
+} /* void if_submit */
 
-static void traffic_read (void)
+static int traffic_read (void)
 {
 #if HAVE_GETIFADDRS
        struct ifaddrs *if_list;
@@ -357,13 +284,13 @@ static void traffic_read (void)
                if ((if_data = (struct IFA_DATA *) if_ptr->ifa_data) == NULL)
                        continue;
 
-               bytes_submit (if_ptr->ifa_name,
+               if_submit (if_ptr->ifa_name, "if_octets",
                                if_data->IFA_RX_BYTES,
                                if_data->IFA_TX_BYTES);
-               packets_submit (if_ptr->ifa_name,
+               if_submit (if_ptr->ifa_name, "if_packets",
                                if_data->IFA_RX_PACKT,
                                if_data->IFA_TX_PACKT);
-               errors_submit (if_ptr->ifa_name,
+               if_submit (if_ptr->ifa_name, "if_errors",
                                if_data->IFA_RX_ERROR,
                                if_data->IFA_TX_ERROR);
        }
@@ -384,7 +311,7 @@ static void traffic_read (void)
        if ((fh = fopen ("/proc/net/dev", "r")) == NULL)
        {
                syslog (LOG_WARNING, "traffic: fopen: %s", strerror (errno));
-               return;
+               return (-1);
        }
 
        while (fgets (buffer, 1024, fh) != NULL)
@@ -408,15 +335,15 @@ static void traffic_read (void)
 
                incoming = atoll (fields[0]);
                outgoing = atoll (fields[8]);
-               bytes_submit (device, incoming, outgoing);
+               if_submit (device, "if_octets", incoming, outgoing);
 
                incoming = atoll (fields[1]);
                outgoing = atoll (fields[9]);
-               packets_submit (device, incoming, outgoing);
+               if_submit (device, "if_packets", incoming, outgoing);
 
                incoming = atoll (fields[2]);
                outgoing = atoll (fields[10]);
-               errors_submit (device, incoming, outgoing);
+               if_submit (device, "if_errors", incoming, outgoing);
        }
 
        fclose (fh);
@@ -438,17 +365,17 @@ static void traffic_read (void)
                rx = get_kstat_value (ksp[i], "rbytes");
                tx = get_kstat_value (ksp[i], "obytes");
                if ((rx != -1LL) || (tx != -1LL))
-                       bytes_submit (ksp[i]->ks_name, rx, tx);
+                       if_submit (ksp[i]->ks_name, "if_octets", rx, tx);
 
                rx = get_kstat_value (ksp[i], "ipackets");
                tx = get_kstat_value (ksp[i], "opackets");
                if ((rx != -1LL) || (tx != -1LL))
-                       packets_submit (ksp[i]->ks_name, rx, tx);
+                       if_submit (ksp[i]->ks_name, "if_packets", rx, tx);
 
                rx = get_kstat_value (ksp[i], "ierrors");
                tx = get_kstat_value (ksp[i], "oerrors");
                if ((rx != -1LL) || (tx != -1LL))
-                       errors_submit (ksp[i]->ks_name, rx, tx);
+                       if_submit (ksp[i]->ks_name, "if_errors", rx, tx);
        }
 /* #endif HAVE_LIBKSTAT */
 
@@ -459,19 +386,29 @@ static void traffic_read (void)
        ios = sg_get_network_io_stats (&num);
 
        for (i = 0; i < num; i++)
-               bytes_submit (ios[i].interface_name, ios[i].rx, ios[i].tx);
+               if_submit (ios[i].interface_name, "if_octets", ios[i].rx, ios[i].tx);
 #endif /* HAVE_LIBSTATGRAB */
-}
-#else
-#define traffic_read NULL
+
+       return (0);
+} /* int traffic_read */
 #endif /* TRAFFIC_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, traffic_init, traffic_read, bytes_write);
-       plugin_register ("if_packets", NULL, NULL, packets_write);
-       plugin_register ("if_errors",  NULL, NULL, errors_write);
-       cf_register (MODULE_NAME, traffic_config, config_keys, config_keys_num);
+       plugin_register_data_set (&octets_ds);
+       plugin_register_data_set (&packets_ds);
+       plugin_register_data_set (&errors_ds);
+
+       plugin_register_config ("interface", interface_config,
+                       config_keys, config_keys_num);
+
+#if HAVE_LIBKSTAT
+       plugin_register_init ("interface", traffic_init);
+#endif
+
+#if TRAFFIC_HAVE_READ
+       plugin_register_read ("interface", traffic_read);
+#endif
 }
 
 #undef BUFSIZE
diff --git a/src/utils_llist.c b/src/utils_llist.c
new file mode 100644 (file)
index 0000000..a815035
--- /dev/null
@@ -0,0 +1,148 @@
+/**
+ * collectd - src/utils_llist.c
+ * Copyright (C) 2006 Florian Forster <octo at verplant.org>
+ *
+ * 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; only
+ * version 2 of the Licence 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.
+ *
+ * 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.
+ *
+ * Authors:
+ *   Florian Forster <octo at verplant.org>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils_llist.h"
+
+/*
+ * Private data types
+ */
+struct llist_s
+{
+       llentry_t *head;
+       llentry_t *tail;
+};
+
+/*
+ * Public functions
+ */
+llist_t *llist_create (void)
+{
+       llist_t *ret;
+
+       ret = (llist_t *) malloc (sizeof (llist_t));
+       if (ret == NULL)
+               return (NULL);
+
+       memset (ret, '\0', sizeof (llist_t));
+
+       return (ret);
+}
+
+void llist_destroy (llist_t *l)
+{
+       llentry_t *e_this;
+       llentry_t *e_next;
+
+       for (e_this = l->head; e_this != NULL; e_this = e_next)
+       {
+               e_next = e_this->next;
+               llentry_destroy (e_this);
+       }
+
+       free (l);
+}
+
+llentry_t *llentry_create (const char *key, void *value)
+{
+       llentry_t *e;
+
+       e = (llentry_t *) malloc (sizeof (llentry_t));
+       if (e == NULL)
+               return (NULL);
+
+       e->key   = strdup (key);
+       e->value = value;
+       e->next  = NULL;
+
+       if (e->key == NULL)
+       {
+               free (e);
+               return (NULL);
+       }
+
+       return (e);
+}
+
+void llentry_destroy (llentry_t *e)
+{
+       free (e->key);
+       free (e);
+}
+
+void llist_append (llist_t *l, llentry_t *e)
+{
+       e->next = NULL;
+
+       if (l->tail == NULL)
+               l->head = e;
+       else
+               l->tail->next = e;
+
+       l->tail = e;
+}
+
+void llist_prepend (llist_t *l, llentry_t *e)
+{
+       e->next = l->head;
+       l->head = e;
+}
+
+void llist_remove (llist_t *l, llentry_t *e)
+{
+       llentry_t *prev;
+
+       prev = l->head;
+       while ((prev != NULL) && (prev->next != e))
+               prev = prev->next;
+
+       if (prev != NULL)
+               prev->next = e->next;
+       if (l->head == e)
+               l->head = e->next;
+       if (l->tail == e)
+               l->tail = prev;
+}
+
+llentry_t *llist_search (llist_t *l, const char *key)
+{
+       llentry_t *e;
+
+       for (e = l->head; e != NULL; e = e->next)
+               if (strcmp (key, e->key) == 0)
+                       break;
+
+       return (e);
+}
+
+llentry_t *llist_head (llist_t *l)
+{
+       return (l->head);
+}
+
+llentry_t *llist_tail (llist_t *l)
+{
+       return (l->tail);
+}
diff --git a/src/utils_llist.h b/src/utils_llist.h
new file mode 100644 (file)
index 0000000..603fc87
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * collectd - src/utils_llist.h
+ * Copyright (C) 2006 Florian Forster <octo at verplant.org>
+ *
+ * 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; only
+ * version 2 of the Licence 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.
+ *
+ * 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.
+ *
+ * Authors:
+ *   Florian Forster <octo at verplant.org>
+ */
+
+#ifndef UTILS_LLIST_H
+#define UTILS_LLIST_H 1
+
+/*
+ * Data types
+ */
+struct llentry_s
+{
+       char *key;
+       void *value;
+       struct llentry_s *next;
+};
+typedef struct llentry_s llentry_t;
+
+struct llist_s;
+typedef struct llist_s llist_t;
+
+/*
+ * Functions
+ */
+llist_t *llist_create (void);
+void llist_destroy (llist_t *l);
+
+llentry_t *llentry_create (const char *key, void *value);
+void llentry_destroy (llentry_t *e);
+
+void llist_append (llist_t *l, llentry_t *e);
+void llist_prepend (llist_t *l, llentry_t *e);
+void llist_remove (llist_t *l, llentry_t *e);
+
+llentry_t *llist_search (llist_t *l, const char *key);
+
+llentry_t *llist_head (llist_t *l);
+llentry_t *llist_tail (llist_t *l);
+
+#endif /* UTILS_LLIST_H */