Merge branch 'collectd-4.5'
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Fri, 3 Oct 2008 21:18:28 +0000 (23:18 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Fri, 3 Oct 2008 21:18:28 +0000 (23:18 +0200)
23 files changed:
README
configure.in
src/Makefile.am
src/collectd.conf.in
src/collectd.conf.pod
src/interface.c
src/libiptc/Makefile.am
src/libiptc/README.collectd [new file with mode: 0644]
src/libiptc/libip6tc.c
src/libiptc/libip6tc.h
src/libiptc/libiptc.c
src/libiptc/libiptc.h
src/libiptc/libxtc.h [new file with mode: 0644]
src/libiptc/xtables.h [new file with mode: 0644]
src/plugin.c
src/postgresql.c
src/rrdcached.c [new file with mode: 0644]
src/rrdtool.c
src/snmp.c
src/types.db
src/utils_complain.h
src/utils_rrdcreate.c [new file with mode: 0644]
src/utils_rrdcreate.h [new file with mode: 0644]

diff --git a/README b/README
index 58ce45d..c179cd1 100644 (file)
--- a/README
+++ b/README
@@ -76,6 +76,9 @@ Features
       Iptables' counters: Number of bytes that were matched by a certain
       iptables rule.
 
+    - ipmi
+      IPMI (Intelligent Platform Management Interface) sensors information.
+
     - ipvs
       IPVS connection statistics (number of connections, octets and packets
       for each service and destination).
@@ -152,9 +155,15 @@ Features
       PostgreSQL database statistics: active server connections, transaction
       numbers, block IO, table row manipulations.
 
+    - powerdns
+      PowerDNS name server statistics.
+
     - processes
       Process counts: Number of running, sleeping, zombie, ... processes.
 
+    - rrdcached
+      RRDtool caching daemon (RRDcacheD) statistics.
+
     - sensors
       System sensors, accessed using lm_sensors: Voltages, temperatures and
       fan rotation speeds.
@@ -180,6 +189,12 @@ Features
     - tcpconns
       Number of TCP connections to specific local and remote ports.
 
+    - teamspeak2
+      TeamSpeak2 server statistics.
+
+    - thermal
+      Linux ACPI thermal zone information.
+
     - users
       Users currently logged in.
 
@@ -214,6 +229,11 @@ Features
       you can easily do weird stuff with the plugins we didn't dare think of
       ;) See collectd-perl(5).
 
+    - rrdcached
+      Output to round-robin-database (RRD) files using the RRDtool caching
+      daemon (RRDcacheD) - see rrdcached(1). That daemon provides a general
+      implementation of the caching done by the `rrdtool' plugin.
+
     - rrdtool
       Output to round-robin-database (RRD) files using librrd. See rrdtool(1).
       This is likely the most popular destination for such values. Since
@@ -279,7 +299,7 @@ Features
     since collectd is programmed multithreaded it benefits from hyperthreading
     and multicore processors and makes sure that the daemon isn't idle if only
     one plugins waits for an IO-operation to complete.
-    
+
   * Once set up, hardly any maintenance is necessary. Setup is kept as easy
     as possible and the default values should be okay for most users.
 
@@ -349,7 +369,8 @@ Prerequisites
   * libhal (optional)
     If present, the uuid plugin will check for UUID from HAL.
 
-  * libiptc (optional)
+  * libiptc (optional, if not found a version shipped with this distribution
+    can be used if the Linux kernel headers are available)
     For querying iptables counters.
 
   * libmysqlclient (optional)
@@ -382,11 +403,10 @@ Prerequisites
   * libpq (optional)
     The PostgreSQL C client library used by the `postgresql' plugin.
 
-  * librrd (optional; headers and library; rrdtool 1.0 and 1.2 both work fine)
-    If built without `librrd' the resulting binary will be `client only', i.e.
-    will send its values via multicast and not create any RRD files itself.
-    Alternatively you can chose to write CSV-files (Comma Separated Values)
-    instead.
+  * librrd (optional)
+    Used by the `rrdtool' and `rrdcached' plugins. The latter requires RRDtool
+    client support which was added after version 1.3 of RRDtool. Versions 1.0,
+    1.2 and 1.3 are known to work with the `rrdtool' plugin.
 
   * librt, libsocket, libkstat, libdevinfo (optional)
     Various standard Solaris libraries which provide system functions.
@@ -394,10 +414,10 @@ Prerequisites
   * libsensors (optional)
     To read from `lm_sensors', see the `sensors' plugin.
 
-  * libstatgrab (optional) may be used to collect statistics on systems other
-    than Linux and/or Solaris. Note that CPU- and disk-statistics, while being
-    provided by this library, are not supported in collectd right now..
-    <http://www.i-scream.org/libstatgrab/> 
+  * libstatgrab (optional)
+    Used by various plugins to collect statistics on systems other than Linux
+    and/or Solaris.
+    <http://www.i-scream.org/libstatgrab/>
 
   * libupsclient/nut (optional)
     For the `nut' plugin which queries nut's `upsd'.
@@ -418,7 +438,7 @@ Configuring / Compiling / Installing
   `./configure && make && make install'.  For detailed, generic instructions
   see INSTALL. For a complete list of configure options and their description,
   run `./configure --help'.
-  
+
   By default, the configure script will check for all build dependencies and
   disable all plugins whose requirements cannot be fulfilled (any other plugin
   will be enabled). To enable a plugin, install missing dependencies (see
index 8051489..9c54aba 100644 (file)
@@ -926,6 +926,7 @@ collectd additional packages:])
 librrd_cflags=""
 librrd_ldflags=""
 librrd_threadsafe="yes"
+librrd_rrdc_update="no"
 AC_ARG_WITH(rrdtool, [AS_HELP_STRING([--with-rrdtool@<:@=PREFIX@:>@], [Path to rrdtool.])],
 [      if test "x$withval" != "xno" && test "x$withval" != "xyes"
        then
@@ -971,6 +972,13 @@ then
        ],
        [-lm])
 
+       if test "x$librrd_threadsafe" = "xyes"
+       then
+               AC_CHECK_LIB(rrd_th, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"])
+       else
+               AC_CHECK_LIB(rrd, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"])
+       fi
+
        CPPFLAGS="$SAVE_CPPFLAGS"
        LDFLAGS="$SAVE_LDFLAGS"
 fi
@@ -2723,6 +2731,7 @@ AC_PLUGIN([postgresql],  [$with_libpq],        [PostgreSQL database statistics])
 AC_PLUGIN([powerdns],    [yes],                [PowerDNS statistics])
 AC_PLUGIN([processes],   [$plugin_processes],  [Process statistics])
 AC_PLUGIN([rrdtool],     [$with_rrdtool],      [RRDTool output plugin])
+AC_PLUGIN([rrdcached],   [$librrd_rrdc_update], [RRDTool output plugin])
 AC_PLUGIN([sensors],     [$with_lm_sensors],   [lm_sensors statistics])
 AC_PLUGIN([serial],      [$plugin_serial],     [serial port traffic])
 AC_PLUGIN([snmp],        [$with_libnetsnmp],   [SNMP querying plugin])
@@ -2891,6 +2900,7 @@ Configuration:
     powerdns  . . . . . . $enable_powerdns
     processes . . . . . . $enable_processes
     rrdtool . . . . . . . $enable_rrdtool
+    rrdcached . . . . . . $enable_rrdcached
     sensors . . . . . . . $enable_sensors
     serial  . . . . . . . $enable_serial
     snmp  . . . . . . . . $enable_snmp
index f93c506..4412b3b 100644 (file)
@@ -588,9 +588,19 @@ processes_la_LIBADD += -lkvm
 endif
 endif
 
+if BUILD_PLUGIN_RRDCACHED
+pkglib_LTLIBRARIES += rrdcached.la
+rrdcached_la_SOURCES = rrdcached.c utils_rrdcreate.c utils_rrdcreate.h
+rrdcached_la_LDFLAGS = -module -avoid-version
+rrdcached_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS)
+rrdcached_la_LIBADD = $(BUILD_WITH_LIBRRD_LDFLAGS)
+collectd_LDADD += "-dlopen" rrdcached.la
+collectd_DEPENDENCIES += rrdcached.la
+endif
+
 if BUILD_PLUGIN_RRDTOOL
 pkglib_LTLIBRARIES += rrdtool.la
-rrdtool_la_SOURCES = rrdtool.c
+rrdtool_la_SOURCES = rrdtool.c utils_rrdcreate.c utils_rrdcreate.h
 rrdtool_la_LDFLAGS = -module -avoid-version
 rrdtool_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS)
 rrdtool_la_LIBADD = $(BUILD_WITH_LIBRRD_LDFLAGS)
index 36f0014..a09522c 100644 (file)
@@ -68,6 +68,7 @@ FQDNLookup   true
 @BUILD_PLUGIN_POSTGRESQL_TRUE@LoadPlugin postgresql
 @BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns
 @BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes
+@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached
 @BUILD_PLUGIN_RRDTOOL_TRUE@LoadPlugin rrdtool
 @BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors
 @BUILD_PLUGIN_SERIAL_TRUE@LoadPlugin serial
@@ -313,6 +314,13 @@ FQDNLookup   true
 #      Process "name"
 #</Plugin>
 
+#<Plugin rrdcached>
+#      DaemonAddress "unix:/tmp/rrdcached.sock"
+#      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/rrd"
+#      CreateFiles true
+#      CollectStatistics true
+#</Plugin>
+
 #<Plugin rrdtool>
 #      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/rrd"
 #      CacheTimeout 120
index 0df5c3b..0cf1c24 100644 (file)
@@ -1500,6 +1500,56 @@ and minor and major pagefaults.
 
 =back
 
+=head2 Plugin C<rrdcached>
+
+The C<rrdcached> plugin uses the RRDTool accelerator daemon, L<rrdcached(1)>,
+to store values to RRD files in an efficient manner. The combination of the
+C<rrdcached> B<plugin> and the C<rrdcached> B<daemon> is very similar to the
+way the C<rrdtool> plugin works (see below). The added abstraction layer
+provides a number of benefits, though: Because the cache is not within
+C<collectd> anymore, it does not need to be flushed when C<collectd> is to be
+restarted. This results in much shorter (if any) gaps in graphs, especially
+under heavy load. Also, the C<rrdtool> command line utility is aware of the
+daemon so that it can flush values to disk automatically when needed. This
+allows to integrate automated flushing of values into graphing solutions much
+more easily.
+
+There are disadvantages, though: The daemon may reside on a different host, so
+it may not be possible for C<collectd> to create the appropriate RRD files
+anymore. And even if C<rrdcached> runs on the same host, it may run in a
+different base directory, so relative paths may do weird stuff if you're not
+careful.
+
+So the B<recommended configuration> is to let C<collectd> and C<rrdcached> run
+on the same host, communicating via a UNIX domain socket. The B<DataDir>
+setting should be set to an absolute path, so that a changed base directory
+does not result in RRD files being createdE<nbsp>/ expected in the wrong place.
+
+=over 4
+
+=item B<DaemonAddress> I<Address>
+
+Address of the daemon as understood by the C<rrdc_connect> function of the RRD
+library. See L<rrdcached(1)> for details. Example:
+
+  <Plugin "rrdcached">
+    DaemonAddress "unix:/var/run/rrdcached.sock"
+  </Plugin>
+
+=item B<DataDir> I<Directory>
+
+Set the base directory in which the RRD files reside. If this is a relative
+path, it is relative to the working base directory of the C<rrdcached> daemon!
+Use of an absolute path is recommended.
+
+=item B<CreateFiles> B<true>|B<false>
+
+Enables or disables the creation of RRD files. If the daemon is not running
+locally, or B<DataDir> is set to a relative path, this will not work as
+expected. Default is B<true>.
+
+=back
+
 =head2 Plugin C<rrdtool>
 
 You can use the settings B<StepSize>, B<HeartBeat>, B<RRARows>, and B<XFF> to
index 806a336..7512c4e 100644 (file)
@@ -24,6 +24,7 @@
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
+#include "utils_ignorelist.h"
 
 #if HAVE_SYS_TYPES_H
 #  include <sys/types.h>
@@ -77,14 +78,7 @@ static const char *config_keys[] =
 };
 static int config_keys_num = 2;
 
-static char **if_list = NULL;
-static int    if_list_num = 0;
-/* 
- * if_list_action:
- * 0 => default is to collect selected interface
- * 1 => ignore selcted interfaces
- */
-static int    if_list_action = 0;
+static ignorelist_t *ignorelist = NULL;
 
 #ifdef HAVE_LIBKSTAT
 #define MAX_NUMIF 256
@@ -95,33 +89,21 @@ static int numif = 0;
 
 static int interface_config (const char *key, const char *value)
 {
-       char **temp;
+       if (ignorelist == NULL)
+               ignorelist = ignorelist_create (/* invert = */ 1);
 
        if (strcasecmp (key, "Interface") == 0)
        {
-               temp = (char **) realloc (if_list, (if_list_num + 1) * sizeof (char *));
-               if (temp == NULL)
-               {
-                       ERROR ("Cannot allocate more memory.");
-                       return (1);
-               }
-               if_list = temp;
-
-               if ((if_list[if_list_num] = strdup (value)) == NULL)
-               {
-                       ERROR ("Cannot allocate memory.");
-                       return (1);
-               }
-               if_list_num++;
+               ignorelist_add (ignorelist, value);
        }
        else if (strcasecmp (key, "IgnoreSelected") == 0)
        {
+               int invert = 1;
                if ((strcasecmp (value, "True") == 0)
                                || (strcasecmp (value, "Yes") == 0)
                                || (strcasecmp (value, "On") == 0))
-                       if_list_action = 1;
-               else
-                       if_list_action = 0;
+                       invert = 0;
+               ignorelist_set_invert (ignorelist, invert);
        }
        else
        {
@@ -161,26 +143,6 @@ static int interface_init (void)
 } /* int interface_init */
 #endif /* HAVE_LIBKSTAT */
 
-/*
- * Check if this interface/instance should be ignored. This is called from
- * both, `submit' and `write' to give client and server the ability to
- * ignore certain stuff..
- */
-static int check_ignore_if (const char *interface)
-{
-       int i;
-
-       /* If no interfaces are given collect all interfaces. Mostly to be
-        * backwards compatible, but also because this is much easier. */
-       if (if_list_num < 1)
-               return (0);
-
-       for (i = 0; i < if_list_num; i++)
-               if (strcasecmp (interface, if_list[i]) == 0)
-                       return (if_list_action);
-       return (1 - if_list_action);
-} /* int check_ignore_if */
-
 static void if_submit (const char *dev, const char *type,
                unsigned long long rx,
                unsigned long long tx)
@@ -188,7 +150,7 @@ static void if_submit (const char *dev, const char *type,
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       if (check_ignore_if (dev))
+       if (ignorelist_match (ignorelist, dev) != 0)
                return;
 
        values[0].counter = rx;
index 338aecb..3a6ef67 100644 (file)
@@ -1,6 +1,6 @@
 AUTOMAKE_OPTIONS = foreign no-dependencies
 
-EXTRA_DIST = libiptc.c
+EXTRA_DIST = libiptc.c README.collectd
 
 if COMPILER_IS_GCC
 AM_CFLAGS = -Wall -Werror
@@ -8,7 +8,7 @@ endif
 
 noinst_LTLIBRARIES = libiptc.la
 
-libiptc_la_CFLAGS = -DIPTABLES_VERSION=\"1.4.0\" -I$(KERNEL_DIR)/include
+libiptc_la_CFLAGS = -I$(KERNEL_DIR)/include
 libiptc_la_SOURCES = libip4tc.c libip6tc.c \
                ipt_kernel_headers.h libip6tc.h libiptc.h linux_list.h
 
diff --git a/src/libiptc/README.collectd b/src/libiptc/README.collectd
new file mode 100644 (file)
index 0000000..adb53b0
--- /dev/null
@@ -0,0 +1,25 @@
+ libiptc (IPTables Chains) in collectd
+=======================================
+http://netfilter.org/
+http://collectd.org/
+
+About
+-----
+
+  This is libiptc taken from the iptables source distribution. As it is not
+  meant to be a public interface by upstream it is not shipped in some binary
+  distributions. Thus, collectd ships its own copy as a fall-back.
+
+  The presently available version was imported from iptables 1.4.1.1.
+
+Changes to the iptables upstream sources:
+-----------------------------------------
+
+  * Added copyright headers mentioning the "Netfilter Core Team" as copyright
+    holder.
+
+  * Changed "libiptc/*" includes to "*".
+
+  * Use the shipped copy of "xtables.h" instead of the one possibly available
+    on the system.
+
index b30bb28..276b7af 100644 (file)
@@ -131,7 +131,7 @@ typedef unsigned int socklen_t;
 #include "libiptc.c"
 
 #define BIT6(a, l) \
- ((ntohl(a->in6_u.u6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+ ((ntohl(a->s6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
 
 int
 ipv6_prefix_length(const struct in6_addr *a)
index 103267b..9253e11 100644 (file)
@@ -162,7 +162,7 @@ int ip6tc_set_counter(const ip6t_chainlabel chain,
 int ip6tc_commit(ip6tc_handle_t *handle);
 
 /* Get raw socket. */
-int ip6tc_get_raw_socket();
+int ip6tc_get_raw_socket(void);
 
 /* Translates errno numbers into more human-readable form than strerror. */
 const char *ip6tc_strerror(int err);
@@ -170,4 +170,6 @@ const char *ip6tc_strerror(int err);
 /* Return prefix length, or -1 if not contiguous */
 int ipv6_prefix_length(const struct in6_addr *a);
 
+extern void dump_entries6(const ip6tc_handle_t);
+
 #endif /* _LIBIP6TC_H */
index f8e88b2..f7a6640 100644 (file)
@@ -44,6 +44,7 @@
  */
 #include <sys/types.h>
 #include <sys/socket.h>
+#include "xtables.h"
 
 #include "linux_list.h"
 
 #define DEBUGP_C(x, args...)
 #endif
 
-#ifndef IPT_LIB_DIR
-#define IPT_LIB_DIR "/usr/local/lib/iptables"
+#ifdef DEBUG
+#define debug(x, args...)      fprintf(stderr, x, ## args)
+#else
+#define debug(x, args...)
 #endif
 
 static int sockfd = -1;
@@ -150,6 +153,11 @@ STRUCT_TC_HANDLE
        struct chain_head *chain_iterator_cur;
        struct rule_head *rule_iterator_cur;
 
+       unsigned int num_chains;         /* number of user defined chains */
+
+       struct chain_head **chain_index;   /* array for fast chain list access*/
+       unsigned int        chain_index_sz;/* size of chain index array */
+
        STRUCT_GETINFO info;
        STRUCT_GET_ENTRIES *entries;
 };
@@ -184,7 +192,7 @@ static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int siz
 }
 
 /* notify us that the ruleset has been modified by the user */
-static void
+static inline void
 set_changed(TC_HANDLE_T h)
 {
        h->changed = 1;
@@ -282,11 +290,307 @@ iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
 
 
 /**********************************************************************
+ * Chain index (cache utility) functions
+ **********************************************************************
+ * The chain index is an array with pointers into the chain list, with
+ * CHAIN_INDEX_BUCKET_LEN spacing.  This facilitates the ability to
+ * speedup chain list searching, by find a more optimal starting
+ * points when searching the linked list.
+ *
+ * The starting point can be found fast by using a binary search of
+ * the chain index. Thus, reducing the previous search complexity of
+ * O(n) to O(log(n/k) + k) where k is CHAIN_INDEX_BUCKET_LEN.
+ *
+ * A nice property of the chain index, is that the "bucket" list
+ * length is max CHAIN_INDEX_BUCKET_LEN (when just build, inserts will
+ * change this). Oppose to hashing, where the "bucket" list length can
+ * vary a lot.
+ */
+#ifndef CHAIN_INDEX_BUCKET_LEN
+#define CHAIN_INDEX_BUCKET_LEN 40
+#endif
+
+/* Another nice property of the chain index is that inserting/creating
+ * chains in chain list don't change the correctness of the chain
+ * index, it only causes longer lists in the buckets.
+ *
+ * To mitigate the performance penalty of longer bucket lists and the
+ * penalty of rebuilding, the chain index is rebuild only when
+ * CHAIN_INDEX_INSERT_MAX chains has been added.
+ */
+#ifndef CHAIN_INDEX_INSERT_MAX
+#define CHAIN_INDEX_INSERT_MAX 355
+#endif
+
+static inline unsigned int iptcc_is_builtin(struct chain_head *c);
+
+
+/* Use binary search in the chain index array, to find a chain_head
+ * pointer closest to the place of the searched name element.
+ *
+ * Notes that, binary search (obviously) requires that the chain list
+ * is sorted by name.
+ */
+static struct list_head *
+iptcc_bsearch_chain_index(const char *name, unsigned int *idx, TC_HANDLE_T handle)
+{
+       unsigned int pos, end;
+       int res;
+
+       struct list_head *list_pos;
+       list_pos=&handle->chains;
+
+       /* Check for empty array, e.g. no user defined chains */
+       if (handle->chain_index_sz == 0) {
+               debug("WARNING: handle->chain_index_sz == 0\n");
+               return list_pos;
+       }
+
+       /* Init */
+       end = handle->chain_index_sz;
+       pos = end / 2;
+
+       debug("bsearch Find chain:%s (pos:%d end:%d)\n", name, pos, end);
+
+       /* Loop */
+ loop:
+       if (!handle->chain_index[pos]) {
+               fprintf(stderr, "ERROR: NULL pointer chain_index[%d]\n", pos);
+               return &handle->chains; /* Be safe, return orig start pos */
+       }
+
+       res = strcmp(name, handle->chain_index[pos]->name);
+       list_pos = &handle->chain_index[pos]->list;
+       *idx = pos;
+
+       debug("bsearch Index[%d] name:%s res:%d ",
+             pos, handle->chain_index[pos]->name, res);
+
+       if (res == 0) { /* Found element, by direct hit */
+               debug("[found] Direct hit pos:%d end:%d\n", pos, end);
+               return list_pos;
+       } else if (res < 0) { /* Too far, jump back */
+               end = pos;
+               pos = pos / 2;
+
+               /* Exit case: First element of array */
+               if (end == 0) {
+                       debug("[found] Reached first array elem (end%d)\n",end);
+                       return list_pos;
+               }
+               debug("jump back to pos:%d (end:%d)\n", pos, end);
+               goto loop;
+       } else if (res > 0 ){ /* Not far enough, jump forward */
+
+               /* Exit case: Last element of array */
+               if (pos == handle->chain_index_sz-1) {
+                       debug("[found] Last array elem (end:%d)\n", end);
+                       return list_pos;
+               }
+
+               /* Exit case: Next index less, thus elem in this list section */
+               res = strcmp(name, handle->chain_index[pos+1]->name);
+               if (res < 0) {
+                       debug("[found] closest list (end:%d)\n", end);
+                       return list_pos;
+               }
+
+               pos = (pos+end)/2;
+               debug("jump forward to pos:%d (end:%d)\n", pos, end);
+               goto loop;
+       }
+
+       return list_pos;
+}
+
+#ifdef DEBUG
+/* Trivial linear search of chain index. Function used for verifying
+   the output of bsearch function */
+static struct list_head *
+iptcc_linearly_search_chain_index(const char *name, TC_HANDLE_T handle)
+{
+       unsigned int i=0;
+       int res=0;
+
+       struct list_head *list_pos;
+       list_pos = &handle->chains;
+
+       if (handle->chain_index_sz)
+               list_pos = &handle->chain_index[0]->list;
+
+       /* Linearly walk of chain index array */
+
+       for (i=0; i < handle->chain_index_sz; i++) {
+               if (handle->chain_index[i]) {
+                       res = strcmp(handle->chain_index[i]->name, name);
+                       if (res > 0)
+                               break; // One step too far
+                       list_pos = &handle->chain_index[i]->list;
+                       if (res == 0)
+                               break; // Direct hit
+               }
+       }
+
+       return list_pos;
+}
+#endif
+
+static int iptcc_chain_index_alloc(TC_HANDLE_T h)
+{
+       unsigned int list_length = CHAIN_INDEX_BUCKET_LEN;
+       unsigned int array_elems;
+       unsigned int array_mem;
+
+       /* Allocate memory for the chain index array */
+       array_elems = (h->num_chains / list_length) +
+                      (h->num_chains % list_length ? 1 : 0);
+       array_mem   = sizeof(h->chain_index) * array_elems;
+
+       debug("Alloc Chain index, elems:%d mem:%d bytes\n",
+             array_elems, array_mem);
+
+       h->chain_index = malloc(array_mem);
+       if (!h->chain_index) {
+               h->chain_index_sz = 0;
+               return -ENOMEM;
+       }
+       memset(h->chain_index, 0, array_mem);
+       h->chain_index_sz = array_elems;
+
+       return 1;
+}
+
+static void iptcc_chain_index_free(TC_HANDLE_T h)
+{
+       h->chain_index_sz = 0;
+       free(h->chain_index);
+}
+
+
+#ifdef DEBUG
+static void iptcc_chain_index_dump(TC_HANDLE_T h)
+{
+       unsigned int i = 0;
+
+       /* Dump: contents of chain index array */
+       for (i=0; i < h->chain_index_sz; i++) {
+               if (h->chain_index[i]) {
+                       fprintf(stderr, "Chain index[%d].name: %s\n",
+                               i, h->chain_index[i]->name);
+               }
+       }
+}
+#endif
+
+/* Build the chain index */
+static int iptcc_chain_index_build(TC_HANDLE_T h)
+{
+       unsigned int list_length = CHAIN_INDEX_BUCKET_LEN;
+       unsigned int chains = 0;
+       unsigned int cindex = 0;
+       struct chain_head *c;
+
+       /* Build up the chain index array here */
+       debug("Building chain index\n");
+
+       debug("Number of user defined chains:%d bucket_sz:%d array_sz:%d\n",
+               h->num_chains, list_length, h->chain_index_sz);
+
+       if (h->chain_index_sz == 0)
+               return 0;
+
+       list_for_each_entry(c, &h->chains, list) {
+
+               /* Issue: The index array needs to start after the
+                * builtin chains, as they are not sorted */
+               if (!iptcc_is_builtin(c)) {
+                       cindex=chains / list_length;
+
+                       /* Safe guard, break out on array limit, this
+                        * is useful if chains are added and array is
+                        * rebuild, without realloc of memory. */
+                       if (cindex >= h->chain_index_sz)
+                               break;
+
+                       if ((chains % list_length)== 0) {
+                               debug("\nIndex[%d] Chains:", cindex);
+                               h->chain_index[cindex] = c;
+                       }
+                       chains++;
+               }
+               debug("%s, ", c->name);
+       }
+       debug("\n");
+
+       return 1;
+}
+
+static int iptcc_chain_index_rebuild(TC_HANDLE_T h)
+{
+       debug("REBUILD chain index array\n");
+       iptcc_chain_index_free(h);
+       if ((iptcc_chain_index_alloc(h)) < 0)
+               return -ENOMEM;
+       iptcc_chain_index_build(h);
+       return 1;
+}
+
+/* Delete chain (pointer) from index array.  Removing an element from
+ * the chain list only affects the chain index array, if the chain
+ * index points-to/uses that list pointer.
+ *
+ * There are different strategies, the simple and safe is to rebuild
+ * the chain index every time.  The more advanced is to update the
+ * array index to point to the next element, but that requires some
+ * house keeping and boundry checks.  The advanced is implemented, as
+ * the simple approach behaves badly when all chains are deleted
+ * because list_for_each processing will always hit the first chain
+ * index, thus causing a rebuild for every chain.
+ */
+static int iptcc_chain_index_delete_chain(struct chain_head *c, TC_HANDLE_T h)
+{
+       struct list_head *index_ptr, *index_ptr2, *next;
+       struct chain_head *c2;
+       unsigned int idx, idx2;
+
+       index_ptr = iptcc_bsearch_chain_index(c->name, &idx, h);
+
+       debug("Del chain[%s] c->list:%p index_ptr:%p\n",
+             c->name, &c->list, index_ptr);
+
+       /* Save the next pointer */
+       next = c->list.next;
+       list_del(&c->list);
+
+       if (index_ptr == &c->list) { /* Chain used as index ptr */
+
+               /* See if its possible to avoid a rebuild, by shifting
+                * to next pointer.  Its possible if the next pointer
+                * is located in the same index bucket.
+                */
+               c2         = list_entry(next, struct chain_head, list);
+               index_ptr2 = iptcc_bsearch_chain_index(c2->name, &idx2, h);
+               if (idx != idx2) {
+                       /* Rebuild needed */
+                       return iptcc_chain_index_rebuild(h);
+               } else {
+                       /* Avoiding rebuild */
+                       debug("Update cindex[%d] with next ptr name:[%s]\n",
+                             idx, c2->name);
+                       h->chain_index[idx]=c2;
+                       return 0;
+               }
+       }
+       return 0;
+}
+
+
+/**********************************************************************
  * iptc cache utility functions (iptcc_*)
  **********************************************************************/
 
 /* Is the given chain builtin (1) or user-defined (0) */
-static unsigned int iptcc_is_builtin(struct chain_head *c)
+static inline unsigned int iptcc_is_builtin(struct chain_head *c)
 {
        return (c->hooknum ? 1 : 0);
 }
@@ -338,21 +642,81 @@ iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset)
 
        return NULL;
 }
+
 /* Returns chain head if found, otherwise NULL. */
 static struct chain_head *
 iptcc_find_label(const char *name, TC_HANDLE_T handle)
 {
        struct list_head *pos;
+       struct list_head *list_start_pos;
+       unsigned int i=0;
+       int res;
 
        if (list_empty(&handle->chains))
                return NULL;
 
+       /* First look at builtin chains */
        list_for_each(pos, &handle->chains) {
                struct chain_head *c = list_entry(pos, struct chain_head, list);
+               if (!iptcc_is_builtin(c))
+                       break;
                if (!strcmp(c->name, name))
                        return c;
        }
 
+       /* Find a smart place to start the search via chain index */
+       //list_start_pos = iptcc_linearly_search_chain_index(name, handle);
+       list_start_pos = iptcc_bsearch_chain_index(name, &i, handle);
+
+       /* Handel if bsearch bails out early */
+       if (list_start_pos == &handle->chains) {
+               list_start_pos = pos;
+       }
+#ifdef DEBUG
+       else {
+               /* Verify result of bsearch against linearly index search */
+               struct list_head *test_pos;
+               struct chain_head *test_c, *tmp_c;
+               test_pos = iptcc_linearly_search_chain_index(name, handle);
+               if (list_start_pos != test_pos) {
+                       debug("BUG in chain_index search\n");
+                       test_c=list_entry(test_pos,      struct chain_head,list);
+                       tmp_c =list_entry(list_start_pos,struct chain_head,list);
+                       debug("Verify search found:\n");
+                       debug(" Chain:%s\n", test_c->name);
+                       debug("BSearch found:\n");
+                       debug(" Chain:%s\n", tmp_c->name);
+                       exit(42);
+               }
+       }
+#endif
+
+       /* Initial/special case, no user defined chains */
+       if (handle->num_chains == 0)
+               return NULL;
+
+       /* Start searching through the chain list */
+       list_for_each(pos, list_start_pos->prev) {
+               struct chain_head *c = list_entry(pos, struct chain_head, list);
+               res = strcmp(c->name, name);
+               debug("List search name:%s == %s res:%d\n", name, c->name, res);
+               if (res==0)
+                       return c;
+
+               /* We can stop earlier as we know list is sorted */
+               if (res>0 && !iptcc_is_builtin(c)) { /* Walked too far*/
+                       debug(" Not in list, walked too far, sorted list\n");
+                       return NULL;
+               }
+
+               /* Stop on wrap around, if list head is reached */
+               if (pos == &handle->chains) {
+                       debug("Stop, list head reached\n");
+                       return NULL;
+               }
+       }
+
+       debug("List search NOT found name:%s\n", name);
        return NULL;
 }
 
@@ -413,14 +777,37 @@ static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num)
 static inline void iptc_insert_chain(TC_HANDLE_T h, struct chain_head *c)
 {
        struct chain_head *tmp;
+       struct list_head  *list_start_pos;
+       unsigned int i=1;
+
+       /* Find a smart place to start the insert search */
+       list_start_pos = iptcc_bsearch_chain_index(c->name, &i, h);
+
+       /* Handle the case, where chain.name is smaller than index[0] */
+       if (i==0 && strcmp(c->name, h->chain_index[0]->name) <= 0) {
+               h->chain_index[0] = c; /* Update chain index head */
+               list_start_pos = h->chains.next;
+               debug("Update chain_index[0] with %s\n", c->name);
+       }
+
+       /* Handel if bsearch bails out early */
+       if (list_start_pos == &h->chains) {
+               list_start_pos = h->chains.next;
+       }
 
        /* sort only user defined chains */
        if (!c->hooknum) {
-               list_for_each_entry(tmp, &h->chains, list) {
+               list_for_each_entry(tmp, list_start_pos->prev, list) {
                        if (!tmp->hooknum && strcmp(c->name, tmp->name) <= 0) {
                                list_add(&c->list, tmp->list.prev);
                                return;
                        }
+
+                       /* Stop if list head is reached */
+                       if (&tmp->list == &h->chains) {
+                               debug("Insert, list head reached add to tail\n");
+                               break;
+                       }
                }
        }
 
@@ -493,6 +880,7 @@ static int cache_add_entry(STRUCT_ENTRY *e,
                        errno = -ENOMEM;
                        return -1;
                }
+               h->num_chains++; /* New user defined chain */
 
                __iptcc_p_add_chain(h, c, offset, num);
 
@@ -580,22 +968,27 @@ static int parse_table(TC_HANDLE_T h)
        ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
                        cache_add_entry, h, &prev, &num);
 
+       /* Build the chain index, used for chain list search speedup */
+       if ((iptcc_chain_index_alloc(h)) < 0)
+               return -ENOMEM;
+       iptcc_chain_index_build(h);
+
        /* Second pass: fixup parsed data from first pass */
        list_for_each_entry(c, &h->chains, list) {
                struct rule_head *r;
                list_for_each_entry(r, &c->rules, list) {
-                       struct chain_head *c;
+                       struct chain_head *lc;
                        STRUCT_STANDARD_TARGET *t;
 
                        if (r->type != IPTCC_R_JUMP)
                                continue;
 
                        t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
-                       c = iptcc_find_chain_by_offset(h, t->verdict);
-                       if (!c)
+                       lc = iptcc_find_chain_by_offset(h, t->verdict);
+                       if (!lc)
                                return -1;
-                       r->jump = c;
-                       c->references++;
+                       r->jump = lc;
+                       lc->references++;
                }
        }
 
@@ -848,7 +1241,7 @@ TC_INIT(const char *tablename)
                        return NULL;
        }
        sockfd_use++;
-
+retry:
        s = sizeof(info);
 
        strcpy(info.name, tablename);
@@ -901,6 +1294,9 @@ TC_INIT(const char *tablename)
        return h;
 error:
        TC_FREE(&h);
+       /* A different process changed the ruleset size, retry */
+       if (errno == EAGAIN)
+               goto retry;
        return NULL;
 }
 
@@ -925,6 +1321,8 @@ TC_FREE(TC_HANDLE_T *h)
                free(c);
        }
 
+       iptcc_chain_index_free(*h);
+
        free((*h)->entries);
        free(*h);
 
@@ -947,7 +1345,7 @@ TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
        CHECK(handle);
 
        printf("libiptc v%s. %u bytes.\n",
-              IPTABLES_VERSION, handle->entries->size);
+              XTABLES_VERSION, handle->entries->size);
        printf("Table `%s'\n", handle->info.name);
        printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
               handle->info.hook_entry[HOOK_PRE_ROUTING],
@@ -1091,7 +1489,7 @@ TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
 }
 
 /* How many rules in this chain? */
-unsigned int
+static unsigned int
 TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
 {
        struct chain_head *c;
@@ -1107,9 +1505,8 @@ TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
        return c->num_rules;
 }
 
-const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
-                               unsigned int n,
-                               TC_HANDLE_T *handle)
+static const STRUCT_ENTRY *
+TC_GET_RULE(const char *chain, unsigned int n, TC_HANDLE_T *handle)
 {
        struct chain_head *c;
        struct rule_head *r;
@@ -1791,6 +2188,8 @@ int
 TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
 {
        static struct chain_head *c;
+       int capacity;
+       int exceeded;
 
        iptc_fn = TC_CREATE_CHAIN;
 
@@ -1819,10 +2218,25 @@ TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
                return 0;
 
        }
+       (*handle)->num_chains++; /* New user defined chain */
 
        DEBUGP("Creating chain `%s'\n", chain);
        iptc_insert_chain(*handle, c); /* Insert sorted */
 
+       /* Inserting chains don't change the correctness of the chain
+        * index (except if its smaller than index[0], but that
+        * handled by iptc_insert_chain).  It only causes longer lists
+        * in the buckets. Thus, only rebuild chain index when the
+        * capacity is exceed with CHAIN_INDEX_INSERT_MAX chains.
+        */
+       capacity = (*handle)->chain_index_sz * CHAIN_INDEX_BUCKET_LEN;
+       exceeded = ((((*handle)->num_chains)-capacity));
+       if (exceeded > CHAIN_INDEX_INSERT_MAX) {
+               debug("Capacity(%d) exceeded(%d) rebuild (chains:%d)\n",
+                     capacity, exceeded, (*handle)->num_chains);
+               iptcc_chain_index_rebuild(*handle);
+       }
+
        set_changed(*handle);
 
        return 1;
@@ -1885,11 +2299,14 @@ TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
        }
 
        /* If we are about to delete the chain that is the current
-        * iterator, move chain iterator firward. */
+        * iterator, move chain iterator forward. */
        if (c == (*handle)->chain_iterator_cur)
                iptcc_chain_iterator_advance(*handle);
 
-       list_del(&c->list);
+       (*handle)->num_chains--; /* One user defined chain deleted */
+
+       //list_del(&c->list); /* Done in iptcc_chain_index_delete_chain() */
+       iptcc_chain_index_delete_chain(c, *handle);
        free(c);
 
        DEBUGP("chain `%s' deleted\n", chain);
@@ -1997,16 +2414,14 @@ subtract_counters(STRUCT_COUNTERS *answer,
 }
 
 
-static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters,
-                          unsigned int index)
+static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters, unsigned int idx)
 {
-       newcounters->counters[index] = ((STRUCT_COUNTERS) { 0, 0});
+       newcounters->counters[idx] = ((STRUCT_COUNTERS) { 0, 0});
        DEBUGP_C("NOMAP => zero\n");
 }
 
 static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
-                               STRUCT_REPLACE *repl,
-                               unsigned int index,
+                               STRUCT_REPLACE *repl, unsigned int idx,
                                unsigned int mappos)
 {
        /* Original read: X.
@@ -2016,15 +2431,13 @@ static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
         * => Add in X + Y
         * => Add in replacement read.
         */
-       newcounters->counters[index] = repl->counters[mappos];
+       newcounters->counters[idx] = repl->counters[mappos];
        DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
 }
 
 static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
-                               STRUCT_REPLACE *repl,
-                               unsigned int index,
-                               unsigned int mappos,
-                               STRUCT_COUNTERS *counters)
+                               STRUCT_REPLACE *repl, unsigned int idx,
+                               unsigned int mappos, STRUCT_COUNTERS *counters)
 {
        /* Original read: X.
         * Atomic read on replacement: X + Y.
@@ -2033,19 +2446,18 @@ static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
         * => Add in Y.
         * => Add in (replacement read - original read).
         */
-       subtract_counters(&newcounters->counters[index],
+       subtract_counters(&newcounters->counters[idx],
                          &repl->counters[mappos],
                          counters);
        DEBUGP_C("ZEROED => mappos %u\n", mappos);
 }
 
 static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
-                            unsigned int index,
-                            STRUCT_COUNTERS *counters)
+                             unsigned int idx, STRUCT_COUNTERS *counters)
 {
        /* Want to set counter (iptables-restore) */
 
-       memcpy(&newcounters->counters[index], counters,
+       memcpy(&newcounters->counters[idx], counters,
                sizeof(STRUCT_COUNTERS));
 
        DEBUGP_C("SET\n");
index d9f7423..3fc25b6 100644 (file)
@@ -172,11 +172,13 @@ int iptc_set_counter(const ipt_chainlabel chain,
 int iptc_commit(iptc_handle_t *handle);
 
 /* Get raw socket. */
-int iptc_get_raw_socket();
+int iptc_get_raw_socket(void);
 
 /* Translates errno numbers into more human-readable form than strerror. */
 const char *iptc_strerror(int err);
 
+extern void dump_entries(const iptc_handle_t);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/libiptc/libxtc.h b/src/libiptc/libxtc.h
new file mode 100644 (file)
index 0000000..2ed03f4
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * This file was imported from the iptables sources.
+ * Copyright (C) 1999-2008 Netfilter Core Team
+ *
+ * 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
+ */
+
+#ifndef _LIBXTC_H
+#define _LIBXTC_H
+/* Library which manipulates filtering rules. */
+
+#include "ipt_kernel_headers.h"
+#include <linux/netfilter/x_tables.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XT_MIN_ALIGN
+/* xt_entry has pointers and u_int64_t's in it, so if you align to
+   it, you'll also align to any crazy matches and targets someone
+   might write */
+#define XT_MIN_ALIGN (__alignof__(struct xt_entry))
+#endif
+
+#ifndef XT_ALIGN
+#define XT_ALIGN(s) (((s) + ((XT_MIN_ALIGN)-1)) & ~((XT_MIN_ALIGN)-1))
+#endif
+
+typedef char xt_chainlabel[32];
+
+#define XTC_LABEL_ACCEPT  "ACCEPT"
+#define XTC_LABEL_DROP    "DROP"
+#define XTC_LABEL_QUEUE   "QUEUE"
+#define XTC_LABEL_RETURN  "RETURN"
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBXTC_H */
diff --git a/src/libiptc/xtables.h b/src/libiptc/xtables.h
new file mode 100644 (file)
index 0000000..47e47dc
--- /dev/null
@@ -0,0 +1,239 @@
+/**
+ * This file was imported from the iptables sources.
+ * Copyright (C) 1999-2008 Netfilter Core Team
+ *
+ * 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
+ */
+
+#ifndef _XTABLES_H
+#define _XTABLES_H
+
+#include <sys/types.h>
+#include <linux/types.h>
+#include <linux/netfilter/x_tables.h>
+#include "libxtc.h"
+#include <stdbool.h>
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+#ifndef IPPROTO_DCCP
+#define IPPROTO_DCCP 33
+#endif
+#ifndef IPPROTO_UDPLITE
+#define IPPROTO_UDPLITE        136
+#endif
+
+#define XTABLES_VERSION "1.4.1.1"
+#define XTABLES_VERSION_CODE (0x10000 * 1 + 0x100 * 4 + 1)
+
+#define XTABLES_API_VERSION(x,y,z)    (0x10000*(x) + 0x100*(y) + z)
+
+/* Include file for additions: new matches and targets. */
+struct xtables_match
+{
+       struct xtables_match *next;
+
+       xt_chainlabel name;
+
+       /* Revision of match (0 by default). */
+       u_int8_t revision;
+
+       u_int16_t family;
+
+       const char *version;
+
+       /* Size of match data. */
+       size_t size;
+
+       /* Size of match data relevent for userspace comparison purposes */
+       size_t userspacesize;
+
+       /* Function which prints out usage message. */
+       void (*help)(void);
+
+       /* Initialize the match. */
+       void (*init)(struct xt_entry_match *m);
+
+       /* Function which parses command options; returns true if it
+           ate an option */
+       /* entry is struct ipt_entry for example */
+       int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+                    const void *entry,
+                    struct xt_entry_match **match);
+
+       /* Final check; exit if not ok. */
+       void (*final_check)(unsigned int flags);
+
+       /* Prints out the match iff non-NULL: put space at end */
+       /* ip is struct ipt_ip * for example */
+       void (*print)(const void *ip,
+                     const struct xt_entry_match *match, int numeric);
+
+       /* Saves the match info in parsable form to stdout. */
+       /* ip is struct ipt_ip * for example */
+       void (*save)(const void *ip, const struct xt_entry_match *match);
+
+       /* Pointer to list of extra command-line options */
+       const struct option *extra_opts;
+
+       /* Ignore these men behind the curtain: */
+       unsigned int option_offset;
+       struct xt_entry_match *m;
+       unsigned int mflags;
+#ifdef NO_SHARED_LIBS
+       unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+struct xtables_target
+{
+       struct xtables_target *next;
+
+       xt_chainlabel name;
+
+       /* Revision of target (0 by default). */
+       u_int8_t revision;
+
+       u_int16_t family;
+
+       const char *version;
+
+       /* Size of target data. */
+       size_t size;
+
+       /* Size of target data relevent for userspace comparison purposes */
+       size_t userspacesize;
+
+       /* Function which prints out usage message. */
+       void (*help)(void);
+
+       /* Initialize the target. */
+       void (*init)(struct xt_entry_target *t);
+
+       /* Function which parses command options; returns true if it
+           ate an option */
+       /* entry is struct ipt_entry for example */
+       int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+                    const void *entry,
+                    struct xt_entry_target **targetinfo);
+
+       /* Final check; exit if not ok. */
+       void (*final_check)(unsigned int flags);
+
+       /* Prints out the target iff non-NULL: put space at end */
+       void (*print)(const void *ip,
+                     const struct xt_entry_target *target, int numeric);
+
+       /* Saves the targinfo in parsable form to stdout. */
+       void (*save)(const void *ip,
+                    const struct xt_entry_target *target);
+
+       /* Pointer to list of extra command-line options */
+       const struct option *extra_opts;
+
+       /* Ignore these men behind the curtain: */
+       unsigned int option_offset;
+       struct xt_entry_target *t;
+       unsigned int tflags;
+       unsigned int used;
+#ifdef NO_SHARED_LIBS
+       unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+/* Your shared library should call one of these. */
+extern void xtables_register_match(struct xtables_match *me);
+extern void xtables_register_target(struct xtables_target *me);
+
+extern int string_to_number_ll(const char *s,
+                              unsigned long long min,
+                              unsigned long long max,
+                              unsigned long long *ret);
+extern int string_to_number_l(const char *s,
+                             unsigned long min,
+                             unsigned long max,
+                             unsigned long *ret);
+extern int string_to_number(const char *s,
+                           unsigned int min,
+                           unsigned int max,
+                           unsigned int *ret);
+extern bool strtonuml(const char *, char **, unsigned long *,
+       unsigned long, unsigned long);
+extern bool strtonum(const char *, char **, unsigned int *,
+       unsigned int, unsigned int);
+extern int service_to_port(const char *name, const char *proto);
+extern u_int16_t parse_port(const char *port, const char *proto);
+extern void
+parse_interface(const char *arg, char *vianame, unsigned char *mask);
+
+enum exittype {
+       OTHER_PROBLEM = 1,
+       PARAMETER_PROBLEM,
+       VERSION_PROBLEM,
+       RESOURCE_PROBLEM,
+       P_ONLY_ONCE,
+       P_NO_INVERT,
+       P_BAD_VALUE,
+       P_ONE_ACTION,
+};
+
+/* this is a special 64bit data type that is 8-byte aligned */
+#define aligned_u64 u_int64_t __attribute__((aligned(8)))
+
+int check_inverse(const char option[], int *invert, int *my_optind, int argc);
+void exit_error(enum exittype, const char *, ...)__attribute__((noreturn,
+                                                         format(printf,2,3)));
+extern void param_act(unsigned int, const char *, ...);
+extern const char *program_name, *program_version;
+
+extern const char *ipaddr_to_numeric(const struct in_addr *);
+extern const char *ipaddr_to_anyname(const struct in_addr *);
+extern const char *ipmask_to_numeric(const struct in_addr *);
+extern struct in_addr *numeric_to_ipaddr(const char *);
+extern struct in_addr *numeric_to_ipmask(const char *);
+extern void ipparse_hostnetworkmask(const char *, struct in_addr **,
+       struct in_addr *, unsigned int *);
+
+extern struct in6_addr *numeric_to_ip6addr(const char *);
+extern const char *ip6addr_to_numeric(const struct in6_addr *);
+extern const char *ip6addr_to_anyname(const struct in6_addr *);
+extern const char *ip6mask_to_numeric(const struct in6_addr *);
+extern void ip6parse_hostnetworkmask(const char *, struct in6_addr **,
+       struct in6_addr *, unsigned int *);
+
+/**
+ * Print the specified value to standard output, quoting dangerous
+ * characters if required.
+ */
+extern void save_string(const char *value);
+
+#ifdef NO_SHARED_LIBS
+#      ifdef _INIT
+#              undef _init
+#              define _init _INIT
+#      endif
+       extern void init_extensions(void);
+#else
+#      define _init __attribute__((constructor)) _INIT
+#endif
+
+/* Present in both iptables.c and ip6tables.c */
+extern u_int16_t parse_protocol(const char *s);
+
+#ifdef XTABLES_INTERNAL
+#      include <xtables/internal.h>
+#endif
+
+#endif /* _XTABLES_H */
index 1ec2544..0ae6e44 100644 (file)
@@ -718,7 +718,7 @@ void plugin_shutdown_all (void)
 
 int plugin_dispatch_values (value_list_t *vl)
 {
-       static c_complain_t no_write_complaint = C_COMPLAIN_INIT;
+       static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC;
 
        int (*callback) (const data_set_t *, const value_list_t *);
        data_set_t *ds;
index 9438c57..43f5af5 100644 (file)
@@ -217,8 +217,7 @@ static c_psql_database_t *c_psql_database_new (const char *name)
 
        db->conn = NULL;
 
-       db->conn_complaint.last     = 0;
-       db->conn_complaint.interval = 0;
+       C_COMPLAIN_INIT (&db->conn_complaint);
 
        db->proto_version = 0;
 
diff --git a/src/rrdcached.c b/src/rrdcached.c
new file mode 100644 (file)
index 0000000..f9d65ca
--- /dev/null
@@ -0,0 +1,415 @@
+/**
+ * collectd - src/rrdcached.c
+ * Copyright (C) 2008  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public 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_rrdcreate.h"
+
+#include <rrd_client.h>
+
+/*
+ * Private variables
+ */
+static const char *config_keys[] =
+{
+  "DaemonAddress",
+  "DataDir",
+  "CreateFiles",
+  "CollectStatistics"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static char *datadir = NULL;
+static char *daemon_address = NULL;
+static int config_create_files = 1;
+static int config_collect_stats = 1;
+static rrdcreate_config_t rrdcreate_config =
+{
+       /* stepsize = */ 0,
+       /* heartbeat = */ 0,
+       /* rrarows = */ 1200,
+       /* xff = */ 0.1,
+
+       /* timespans = */ NULL,
+       /* timespans_num = */ 0,
+
+       /* consolidation_functions = */ NULL,
+       /* consolidation_functions_num = */ 0
+};
+
+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;
+
+  assert (0 == strcmp (ds->type, vl->type));
+
+  memset (buffer, '\0', buffer_len);
+
+  status = ssnprintf (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 = ssnprintf (buffer + offset, buffer_len - offset,
+          ":%llu", vl->values[i].counter);
+    }
+    else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
+    {
+      status = ssnprintf (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;
+
+  assert (0 == strcmp (ds->type, vl->type));
+
+  if (datadir != NULL)
+  {
+    status = ssnprintf (buffer + offset, buffer_len - offset,
+        "%s/", datadir);
+    if ((status < 1) || (status >= buffer_len - offset))
+      return (-1);
+    offset += status;
+  }
+
+  status = ssnprintf (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 = ssnprintf (buffer + offset, buffer_len - offset,
+        "%s-%s/", vl->plugin, vl->plugin_instance);
+  else
+    status = ssnprintf (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 = ssnprintf (buffer + offset, buffer_len - offset,
+        "%s-%s", vl->type, vl->type_instance);
+  else
+    status = ssnprintf (buffer + offset, buffer_len - offset,
+        "%s", vl->type);
+  if ((status < 1) || (status >= buffer_len - offset))
+    return (-1);
+  offset += status;
+
+  strncpy (buffer + offset, ".rrd", buffer_len - offset);
+  buffer[buffer_len - 1] = 0;
+
+  return (0);
+} /* int value_list_to_filename */
+
+static int rc_config (const char *key, const char *value)
+{
+  if (strcasecmp ("DataDir", key) == 0)
+  {
+    if (datadir != NULL)
+      free (datadir);
+    datadir = strdup (value);
+    if (datadir != NULL)
+    {
+      int len = strlen (datadir);
+      while ((len > 0) && (datadir[len - 1] == '/'))
+      {
+        len--;
+        datadir[len] = '\0';
+      }
+      if (len <= 0)
+      {
+        free (datadir);
+        datadir = NULL;
+      }
+    }
+  }
+  else if (strcasecmp ("DaemonAddress", key) == 0)
+  {
+    sfree (daemon_address);
+    daemon_address = strdup (value);
+    if (daemon_address == NULL)
+    {
+      ERROR ("rrdcached plugin: strdup failed.");
+      return (1);
+    }
+  }
+  else if (strcasecmp ("CreateFiles", key) == 0)
+  {
+    if ((strcasecmp ("false", value) == 0)
+        || (strcasecmp ("no", value) == 0)
+        || (strcasecmp ("off", value) == 0))
+      config_create_files = 0;
+    else
+      config_create_files = 1;
+  }
+  else if (strcasecmp ("CollectStatistics", key) == 0)
+  {
+    if ((strcasecmp ("false", value) == 0)
+        || (strcasecmp ("no", value) == 0)
+        || (strcasecmp ("off", value) == 0))
+      config_collect_stats = 0;
+    else
+      config_collect_stats = 1;
+  }
+  else
+  {
+    return (-1);
+  }
+  return (0);
+} /* int rc_config */
+
+static int rc_read (void)
+{
+  int status;
+  rrdc_stats_t *head;
+  rrdc_stats_t *ptr;
+
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  if (daemon_address == NULL)
+    return (-1);
+
+  if (config_collect_stats == 0)
+    return (-1);
+
+  vl.values = values;
+  vl.values_len = 1;
+  vl.time = time (NULL);
+
+  if ((strncmp ("unix:", daemon_address, strlen ("unix:")) == 0)
+      || (daemon_address[0] == '/'))
+    sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  else
+    sstrncpy (vl.host, daemon_address, sizeof (vl.host));
+  sstrncpy (vl.plugin, "rrdcached", sizeof (vl.plugin));
+
+  head = NULL;
+  status = rrdc_stats_get (&head);
+  if (status != 0)
+  {
+    ERROR ("rrdcached plugin: rrdc_stats_get failed with status %i.", status);
+    return (-1);
+  }
+
+  for (ptr = head; ptr != NULL; ptr = ptr->next)
+  {
+    if (ptr->type == RRDC_STATS_TYPE_GAUGE)
+      values[0].gauge = (gauge_t) ptr->value.gauge;
+    else if (ptr->type == RRDC_STATS_TYPE_COUNTER)
+      values[0].counter = (counter_t) ptr->value.counter;
+    else
+      continue;
+
+    if (strcasecmp ("QueueLength", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "queue_length", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("UpdatesWritten", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "operations", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "write-updates", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("DataSetsWritten", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "operations", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "write-data_sets",
+          sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("TreeNodesNumber", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "gauge", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "tree_nodes", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("TreeDepth", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "gauge", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "tree_depth", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("FlushesReceived", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "operations", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "receive-flush", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("JournalBytes", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "counter", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "journal-bytes", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("JournalRotate", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "counter", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "journal-rotates", sizeof (vl.type_instance));
+    }
+    else if (strcasecmp ("UpdatesReceived", ptr->name) == 0)
+    {
+      sstrncpy (vl.type, "operations", sizeof (vl.type));
+      sstrncpy (vl.type_instance, "receive-update", sizeof (vl.type_instance));
+    }
+    else
+    {
+      DEBUG ("rrdcached plugin: rc_read: Unknown statistic `%s'.", ptr->name);
+      continue;
+    }
+
+    plugin_dispatch_values (&vl);
+  } /* for (ptr = head; ptr != NULL; ptr = ptr->next) */
+
+  rrdc_stats_free (head);
+
+  return (0);
+} /* int rc_read */
+
+static int rc_init (void)
+{
+  if (config_collect_stats != 0)
+    plugin_register_read ("rrdcached", rc_read);
+
+  return (0);
+} /* int rc_init */
+
+static int rc_write (const data_set_t *ds, const value_list_t *vl)
+{
+  char filename[512];
+  char values[512];
+  char *values_array[2];
+  int status;
+
+  if (daemon_address == NULL)
+  {
+    ERROR ("rrdcached plugin: daemon_address == NULL.");
+    plugin_unregister_write ("rrdcached");
+    return (-1);
+  }
+
+  if (strcmp (ds->type, vl->type) != 0)
+  {
+    ERROR ("rrdcached plugin: DS type does not match value list type");
+    return (-1);
+  }
+
+  if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+  {
+    ERROR ("rrdcached plugin: value_list_to_filename failed.");
+    return (-1);
+  }
+
+  if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
+  {
+    ERROR ("rrdcached plugin: value_list_to_string failed.");
+    return (-1);
+  }
+
+  values_array[0] = values;
+  values_array[1] = NULL;
+
+  if (config_create_files != 0)
+  {
+    struct stat statbuf;
+
+    status = stat (filename, &statbuf);
+    if (status != 0)
+    {
+      if (errno != ENOENT)
+      {
+        char errbuf[1024];
+        ERROR ("rrdcached plugin: stat (%s) failed: %s",
+            filename, sstrerror (errno, errbuf, sizeof (errbuf)));
+        return (-1);
+      }
+
+      status = cu_rrd_create_file (filename, ds, vl, &rrdcreate_config);
+      if (status != 0)
+      {
+        ERROR ("rrdcached plugin: cu_rrd_create_file (%s) failed.",
+            filename);
+        return (-1);
+      }
+    }
+  }
+
+  status = rrdc_connect (daemon_address);
+  if (status != 0)
+  {
+    ERROR ("rrdcached plugin: rrdc_connect (%s) failed with status %i.",
+        daemon_address, status);
+    return (-1);
+  }
+
+  status = rrdc_update (filename, /* values_num = */ 1, (void *) values_array);
+  if (status != 0)
+  {
+    ERROR ("rrdcached plugin: rrdc_update (%s, [%s], 1) failed with "
+        "status %i.",
+        filename, values_array[0], status);
+    return (-1);
+  }
+
+  return (0);
+} /* int rc_write */
+
+static int rc_shutdown (void)
+{
+  rrdc_disconnect ();
+  return (0);
+} /* int rc_shutdown */
+
+void module_register (void)
+{
+  plugin_register_config ("rrdcached", rc_config,
+      config_keys, config_keys_num);
+  plugin_register_init ("rrdcached", rc_init);
+  plugin_register_write ("rrdcached", rc_write);
+  plugin_register_shutdown ("rrdcached", rc_shutdown);
+} /* void module_register */
+
+/*
+ * vim: set sw=2 sts=2 et :
+ */
index 2e1727a..9b236cb 100644 (file)
@@ -23,6 +23,7 @@
 #include "plugin.h"
 #include "common.h"
 #include "utils_avltree.h"
+#include "utils_rrdcreate.h"
 
 #include <rrd.h>
 
@@ -65,27 +66,6 @@ typedef struct rrd_queue_s rrd_queue_t;
 /*
  * Private variables
  */
-static int rra_timespans[] =
-{
-       3600,
-       86400,
-       604800,
-       2678400,
-       31622400
-};
-static int rra_timespans_num = STATIC_ARRAY_SIZE (rra_timespans);
-
-static int *rra_timespans_custom = NULL;
-static int rra_timespans_custom_num = 0;
-
-static char *rra_types[] =
-{
-       "AVERAGE",
-       "MIN",
-       "MAX"
-};
-static int rra_types_num = STATIC_ARRAY_SIZE (rra_types);
-
 static const char *config_keys[] =
 {
        "CacheTimeout",
@@ -103,12 +83,21 @@ static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 /* If datadir is zero, the daemon's basedir is used. If stepsize or heartbeat
  * is zero a default, depending on the `interval' member of the value list is
  * being used. */
-static char   *datadir   = NULL;
-static int     stepsize  = 0;
-static int     heartbeat = 0;
-static int     rrarows   = 1200;
-static double  xff       = 0.1;
-static double  write_rate = 0.0;
+static char *datadir   = NULL;
+static double write_rate = 0.0;
+static rrdcreate_config_t rrdcreate_config =
+{
+       /* stepsize = */ 0,
+       /* heartbeat = */ 0,
+       /* rrarows = */ 1200,
+       /* xff = */ 0.1,
+
+       /* timespans = */ NULL,
+       /* timespans_num = */ 0,
+
+       /* consolidation_functions = */ NULL,
+       /* consolidation_functions_num = */ 0
+};
 
 /* XXX: If you need to lock both, cache_lock and queue_lock, at the same time,
  * ALWAYS lock `cache_lock' first! */
@@ -132,233 +121,7 @@ static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static int do_shutdown = 0;
 
-/* * * * * * * * * *
- * WARNING:  Magic *
- * * * * * * * * * */
-
-static void rra_free (int rra_num, char **rra_def)
-{
-       int i;
-
-       for (i = 0; i < rra_num; i++)
-       {
-               sfree (rra_def[i]);
-       }
-       sfree (rra_def);
-} /* void rra_free */
-
-static int rra_get (char ***ret, const value_list_t *vl)
-{
-       char **rra_def;
-       int rra_num;
-
-       int *rts;
-       int  rts_num;
-
-       int rra_max;
-
-       int span;
-
-       int cdp_num;
-       int cdp_len;
-       int i, j;
-
-       char buffer[64];
-
-       /* The stepsize we use here: If it is user-set, use it. If not, use the
-        * interval of the value-list. */
-       int ss;
-
-       if (rrarows <= 0)
-       {
-               *ret = NULL;
-               return (-1);
-       }
-
-       ss = (stepsize > 0) ? stepsize : vl->interval;
-       if (ss <= 0)
-       {
-               *ret = NULL;
-               return (-1);
-       }
-
-       /* Use the configured timespans or fall back to the built-in defaults */
-       if (rra_timespans_custom_num != 0)
-       {
-               rts = rra_timespans_custom;
-               rts_num = rra_timespans_custom_num;
-       }
-       else
-       {
-               rts = rra_timespans;
-               rts_num = rra_timespans_num;
-       }
-
-       rra_max = rts_num * rra_types_num;
-
-       if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
-               return (-1);
-       memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
-       rra_num = 0;
-
-       cdp_len = 0;
-       for (i = 0; i < rts_num; i++)
-       {
-               span = rts[i];
-
-               if ((span / ss) < rrarows)
-                       span = ss * rrarows;
-
-               if (cdp_len == 0)
-                       cdp_len = 1;
-               else
-                       cdp_len = (int) floor (((double) span)
-                                       / ((double) (rrarows * ss)));
-
-               cdp_num = (int) ceil (((double) span)
-                               / ((double) (cdp_len * ss)));
-
-               for (j = 0; j < rra_types_num; j++)
-               {
-                       if (rra_num >= rra_max)
-                               break;
-
-                       if (ssnprintf (buffer, sizeof (buffer), "RRA:%s:%3.1f:%u:%u",
-                                               rra_types[j], xff,
-                                               cdp_len, cdp_num) >= sizeof (buffer))
-                       {
-                               ERROR ("rra_get: Buffer would have been truncated.");
-                               continue;
-                       }
-
-                       rra_def[rra_num++] = sstrdup (buffer);
-               }
-       }
-
-#if COLLECT_DEBUG
-       DEBUG ("rra_num = %i", rra_num);
-       for (i = 0; i < rra_num; i++)
-               DEBUG ("  %s", rra_def[i]);
-#endif
-
-       *ret = rra_def;
-       return (rra_num);
-} /* int rra_get */
-
-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, const value_list_t *vl)
-{
-       char **ds_def;
-       int ds_num;
-
-       char min[32];
-       char max[32];
-       char buffer[128];
-
-       DEBUG ("ds->ds_num = %i", ds->ds_num);
-
-       ds_def = (char **) malloc (ds->ds_num * sizeof (char *));
-       if (ds_def == NULL)
-       {
-               char errbuf[1024];
-               ERROR ("rrdtool plugin: malloc failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               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
-               {
-                       ERROR ("rrdtool plugin: Unknown DS type: %i",
-                                       d->type);
-                       break;
-               }
-
-               if (isnan (d->min))
-               {
-                       sstrncpy (min, "U", sizeof (min));
-               }
-               else
-                       ssnprintf (min, sizeof (min), "%lf", d->min);
-
-               if (isnan (d->max))
-               {
-                       sstrncpy (max, "U", sizeof (max));
-               }
-               else
-                       ssnprintf (max, sizeof (max), "%lf", d->max);
-
-               status = ssnprintf (buffer, sizeof (buffer),
-                               "DS:%s:%s:%i:%s:%s",
-                               d->name, type,
-                               (heartbeat > 0) ? heartbeat : (2 * vl->interval),
-                               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;
-       DEBUG ("ds_num = %i", ds_num);
-       for (i = 0; i < ds_num; i++)
-               DEBUG ("  %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);
-}
-
 #if HAVE_THREADSAFE_LIBRRD
-static int srrd_create (char *filename, unsigned long pdp_step, time_t last_up,
-               int argc, const char **argv)
-{
-       int status;
-
-       optind = 0; /* bug in librrd? */
-       rrd_clear_error ();
-
-       status = rrd_create_r (filename, pdp_step, last_up, argc, (void *) argv);
-
-       if (status != 0)
-       {
-               WARNING ("rrdtool plugin: rrd_create_r (%s) failed: %s",
-                               filename, rrd_get_error ());
-       }
-
-       return (status);
-} /* int srrd_create */
-
 static int srrd_update (char *filename, char *template,
                int argc, const char **argv)
 {
@@ -380,59 +143,6 @@ static int srrd_update (char *filename, char *template,
 /* #endif HAVE_THREADSAFE_LIBRRD */
 
 #else /* !HAVE_THREADSAFE_LIBRRD */
-static int srrd_create (char *filename, unsigned long pdp_step, time_t last_up,
-               int argc, const char **argv)
-{
-       int status;
-
-       int new_argc;
-       char **new_argv;
-
-       char pdp_step_str[16];
-       char last_up_str[16];
-
-       new_argc = 6 + argc;
-       new_argv = (char **) malloc ((new_argc + 1) * sizeof (char *));
-       if (new_argv == NULL)
-       {
-               ERROR ("rrdtool plugin: malloc failed.");
-               return (-1);
-       }
-
-       if (last_up == 0)
-               last_up = time (NULL) - 10;
-
-       ssnprintf (pdp_step_str, sizeof (pdp_step_str), "%lu", pdp_step);
-       ssnprintf (last_up_str, sizeof (last_up_str), "%u", (unsigned int) last_up);
-
-       new_argv[0] = "create";
-       new_argv[1] = filename;
-       new_argv[2] = "-s";
-       new_argv[3] = pdp_step_str;
-       new_argv[4] = "-b";
-       new_argv[5] = last_up_str;
-
-       memcpy (new_argv + 6, argv, argc * sizeof (char *));
-       new_argv[new_argc] = NULL;
-       
-       pthread_mutex_lock (&librrd_lock);
-       optind = 0; /* bug in librrd? */
-       rrd_clear_error ();
-
-       status = rrd_create (new_argc, new_argv);
-       pthread_mutex_unlock (&librrd_lock);
-
-       if (status != 0)
-       {
-               WARNING ("rrdtool plugin: rrd_create (%s) failed: %s",
-                               filename, rrd_get_error ());
-       }
-
-       sfree (new_argv);
-
-       return (status);
-} /* int srrd_create */
-
 static int srrd_update (char *filename, char *template,
                int argc, const char **argv)
 {
@@ -476,58 +186,6 @@ static int srrd_update (char *filename, char *template,
 } /* int srrd_update */
 #endif /* !HAVE_THREADSAFE_LIBRRD */
 
-static int rrd_create_file (char *filename, const data_set_t *ds, const value_list_t *vl)
-{
-       char **argv;
-       int argc;
-       char **rra_def;
-       int rra_num;
-       char **ds_def;
-       int ds_num;
-       int status = 0;
-
-       if (check_create_dir (filename))
-               return (-1);
-
-       if ((rra_num = rra_get (&rra_def, vl)) < 1)
-       {
-               ERROR ("rrd_create_file failed: Could not calculate RRAs");
-               return (-1);
-       }
-
-       if ((ds_num = ds_get (&ds_def, ds, vl)) < 1)
-       {
-               ERROR ("rrd_create_file failed: Could not calculate DSes");
-               return (-1);
-       }
-
-       argc = ds_num + rra_num;
-
-       if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
-       {
-               char errbuf[1024];
-               ERROR ("rrd_create failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (-1);
-       }
-
-       memcpy (argv, ds_def, ds_num * sizeof (char *));
-       memcpy (argv + ds_num, rra_def, rra_num * sizeof (char *));
-       argv[ds_num + rra_num] = NULL;
-
-       assert (vl->time > 10);
-       status = srrd_create (filename,
-                       (stepsize > 0) ? stepsize : vl->interval,
-                       vl->time - 10,
-                       argc, (const char **)argv);
-
-       free (argv);
-       ds_free (ds_num, ds_def);
-       rra_free (rra_num, rra_def);
-
-       return (status);
-}
-
 static int value_list_to_string (char *buffer, int buffer_len,
                const data_set_t *ds, const value_list_t *vl)
 {
@@ -1111,7 +769,9 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl)
        {
                if (errno == ENOENT)
                {
-                       if (rrd_create_file (filename, ds, vl))
+                       status = cu_rrd_create_file (filename,
+                                       ds, vl, &rrdcreate_config);
+                       if (status != 0)
                                return (-1);
                }
                else
@@ -1196,15 +856,15 @@ static int rrd_config (const char *key, const char *value)
        }
        else if (strcasecmp ("StepSize", key) == 0)
        {
-               stepsize = atoi (value);
-               if (stepsize < 0)
-                       stepsize = 0;
+               int temp = atoi (value);
+               if (temp > 0)
+                       rrdcreate_config.stepsize = temp;
        }
        else if (strcasecmp ("HeartBeat", key) == 0)
        {
-               heartbeat = atoi (value);
-               if (heartbeat < 0)
-                       heartbeat = 0;
+               int temp = atoi (value);
+               if (temp > 0)
+                       rrdcreate_config.heartbeat = temp;
        }
        else if (strcasecmp ("RRARows", key) == 0)
        {
@@ -1215,7 +875,7 @@ static int rrd_config (const char *key, const char *value)
                                        "be greater than 0.\n");
                        return (1);
                }
-               rrarows = tmp;
+               rrdcreate_config.rrarows = tmp;
        }
        else if (strcasecmp ("RRATimespan", key) == 0)
        {
@@ -1234,23 +894,23 @@ static int rrd_config (const char *key, const char *value)
                {
                        dummy = NULL;
                        
-                       tmp_alloc = realloc (rra_timespans_custom,
-                                       sizeof (int) * (rra_timespans_custom_num + 1));
+                       tmp_alloc = realloc (rrdcreate_config.timespans,
+                                       sizeof (int) * (rrdcreate_config.timespans_num + 1));
                        if (tmp_alloc == NULL)
                        {
                                fprintf (stderr, "rrdtool: realloc failed.\n");
                                free (value_copy);
                                return (1);
                        }
-                       rra_timespans_custom = tmp_alloc;
-                       rra_timespans_custom[rra_timespans_custom_num] = atoi (ptr);
-                       if (rra_timespans_custom[rra_timespans_custom_num] != 0)
-                               rra_timespans_custom_num++;
+                       rrdcreate_config.timespans = tmp_alloc;
+                       rrdcreate_config.timespans[rrdcreate_config.timespans_num] = atoi (ptr);
+                       if (rrdcreate_config.timespans[rrdcreate_config.timespans_num] != 0)
+                               rrdcreate_config.timespans_num++;
                } /* while (strtok_r) */
 
-               qsort (/* base = */ rra_timespans_custom,
-                               /* nmemb  = */ rra_timespans_custom_num,
-                               /* size   = */ sizeof (rra_timespans_custom[0]),
+               qsort (/* base = */ rrdcreate_config.timespans,
+                               /* nmemb  = */ rrdcreate_config.timespans_num,
+                               /* size   = */ sizeof (rrdcreate_config.timespans[0]),
                                /* compar = */ rrd_compare_numeric);
 
                free (value_copy);
@@ -1264,7 +924,7 @@ static int rrd_config (const char *key, const char *value)
                                        "be in the range 0 to 1 (exclusive).");
                        return (1);
                }
-               xff = tmp;
+               rrdcreate_config.xff = tmp;
        }
        else if (strcasecmp ("WritesPerSecond", key) == 0)
        {
@@ -1318,16 +978,18 @@ static int rrd_init (void)
 {
        int status;
 
-       if (stepsize < 0)
-               stepsize = 0;
-       if (heartbeat <= 0)
-               heartbeat = 2 * stepsize;
+       if (rrdcreate_config.stepsize < 0)
+               rrdcreate_config.stepsize = 0;
+       if (rrdcreate_config.heartbeat <= 0)
+               rrdcreate_config.heartbeat = 2 * rrdcreate_config.stepsize;
 
-       if ((heartbeat > 0) && (heartbeat < interval_g))
+       if ((rrdcreate_config.heartbeat > 0)
+                       && (rrdcreate_config.heartbeat < interval_g))
                WARNING ("rrdtool plugin: Your `heartbeat' is "
                                "smaller than your `interval'. This will "
                                "likely cause problems.");
-       else if ((stepsize > 0) && (stepsize < interval_g))
+       else if ((rrdcreate_config.stepsize > 0)
+                       && (rrdcreate_config.stepsize < interval_g))
                WARNING ("rrdtool plugin: Your `stepsize' is "
                                "smaller than your `interval'. This will "
                                "create needlessly big RRD-files.");
@@ -1363,7 +1025,10 @@ static int rrd_init (void)
        DEBUG ("rrdtool plugin: rrd_init: datadir = %s; stepsize = %i;"
                        " heartbeat = %i; rrarows = %i; xff = %lf;",
                        (datadir == NULL) ? "(null)" : datadir,
-                       stepsize, heartbeat, rrarows, xff);
+                       rrdcreate_config.stepsize,
+                       rrdcreate_config.heartbeat,
+                       rrdcreate_config.rrarows,
+                       rrdcreate_config.xff);
 
        return (0);
 } /* int rrd_init */
index 4d6e769..09aca56 100644 (file)
@@ -22,6 +22,7 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
+#include "utils_complain.h"
 
 #include <pthread.h>
 
@@ -67,6 +68,7 @@ struct host_definition_s
   char *community;
   int version;
   void *sess_handle;
+  c_complain_t complaint;
   uint32_t interval;
   time_t next_update;
   data_definition_t **data_list;
@@ -551,6 +553,7 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
     return (-1);
   memset (hd, '\0', sizeof (host_definition_t));
   hd->version = 2;
+  C_COMPLAIN_INIT (&hd->complaint);
 
   hd->name = strdup (ci->values[0].value.string);
   if (hd->name == NULL)
@@ -1146,7 +1149,9 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       char *errstr = NULL;
 
       snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
-      ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s",
+
+      c_complain (LOG_ERR, &host->complaint,
+         "snmp plugin: host %s: snmp_sess_synch_response failed: %s",
          host->name, (errstr == NULL) ? "Unknown problem" : errstr);
 
       if (res != NULL)
@@ -1161,6 +1166,9 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
     }
     status = 0;
     assert (res != NULL);
+    c_release (LOG_INFO, &host->complaint,
+       "snmp plugin: host %s: snmp_sess_synch_response successful.",
+       host->name);
 
     vb = res->variables;
     if (vb == NULL)
index c8d2f8a..21d15d6 100644 (file)
@@ -64,6 +64,7 @@ mysql_threads         running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created
 nfs_procedure          value:COUNTER:0:4294967295
 nginx_connections      value:GAUGE:0:U
 nginx_requests         value:COUNTER:0:134217728
+operations             value:COUNTER:0:4294967295
 percent                        percent:GAUGE:0:100.1
 pg_blks                        value:COUNTER:0:U
 pg_db_size             value:GAUGE:0:U
@@ -80,6 +81,7 @@ ps_cputime            user:COUNTER:0:16000000, syst:COUNTER:0:16000000
 ps_pagefaults          minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807
 ps_rss                 value:GAUGE:0:9223372036854775807
 ps_state               value:GAUGE:0:65535
+queue_length           value:GAUGE:0:U
 serial_octets          rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
 signal_noise           value:GAUGE:U:0
 signal_power           value:GAUGE:U:0
index e93d823..b51a81c 100644 (file)
@@ -39,7 +39,8 @@ typedef struct
        int interval;
 } c_complain_t;
 
-#define C_COMPLAIN_INIT { 0, 0 }
+#define C_COMPLAIN_INIT_STATIC { 0, 0 }
+#define C_COMPLAIN_INIT(c) do { (c)->last = 0; (c)->interval = 0; } while (0)
 
 /*
  * NAME
diff --git a/src/utils_rrdcreate.c b/src/utils_rrdcreate.c
new file mode 100644 (file)
index 0000000..99feda2
--- /dev/null
@@ -0,0 +1,396 @@
+/**
+ * collectd - src/utils_rrdcreate.c
+ * Copyright (C) 2006-2008  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public 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 "common.h"
+#include "utils_rrdcreate.h"
+
+#include <rrd.h>
+
+/*
+ * Private variables
+ */
+static int rra_timespans[] =
+{
+  3600,
+  86400,
+  604800,
+  2678400,
+  31622400
+};
+static int rra_timespans_num = STATIC_ARRAY_SIZE (rra_timespans);
+
+static char *rra_types[] =
+{
+  "AVERAGE",
+  "MIN",
+  "MAX"
+};
+static int rra_types_num = STATIC_ARRAY_SIZE (rra_types);
+
+/*
+ * Private functions
+ */
+static void rra_free (int rra_num, char **rra_def) /* {{{ */
+{
+  int i;
+
+  for (i = 0; i < rra_num; i++)
+  {
+    sfree (rra_def[i]);
+  }
+  sfree (rra_def);
+} /* }}} void rra_free */
+
+/* * * * * * * * * *
+ * WARNING:  Magic *
+ * * * * * * * * * */
+static int rra_get (char ***ret, const value_list_t *vl, /* {{{ */
+    const rrdcreate_config_t *cfg)
+{
+  char **rra_def;
+  int rra_num;
+
+  int *rts;
+  int  rts_num;
+
+  int rra_max;
+
+  int span;
+
+  int cdp_num;
+  int cdp_len;
+  int i, j;
+
+  char buffer[128];
+
+  /* The stepsize we use here: If it is user-set, use it. If not, use the
+   * interval of the value-list. */
+  int ss;
+
+  if (cfg->rrarows <= 0)
+  {
+    *ret = NULL;
+    return (-1);
+  }
+
+  if ((cfg->xff < 0) || (cfg->xff >= 1.0))
+  {
+    *ret = NULL;
+    return (-1);
+  }
+
+  ss = (cfg->stepsize > 0) ? cfg->stepsize : vl->interval;
+  if (ss <= 0)
+  {
+    *ret = NULL;
+    return (-1);
+  }
+
+  /* Use the configured timespans or fall back to the built-in defaults */
+  if (cfg->timespans_num != 0)
+  {
+    rts = cfg->timespans;
+    rts_num = cfg->timespans_num;
+  }
+  else
+  {
+    rts = rra_timespans;
+    rts_num = rra_timespans_num;
+  }
+
+  rra_max = rts_num * rra_types_num;
+
+  if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
+    return (-1);
+  memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
+  rra_num = 0;
+
+  cdp_len = 0;
+  for (i = 0; i < rts_num; i++)
+  {
+    span = rts[i];
+
+    if ((span / ss) < cfg->rrarows)
+      span = ss * cfg->rrarows;
+
+    if (cdp_len == 0)
+      cdp_len = 1;
+    else
+      cdp_len = (int) floor (((double) span)
+          / ((double) (cfg->rrarows * ss)));
+
+    cdp_num = (int) ceil (((double) span)
+        / ((double) (cdp_len * ss)));
+
+    for (j = 0; j < rra_types_num; j++)
+    {
+      if (rra_num >= rra_max)
+        break;
+
+      if (ssnprintf (buffer, sizeof (buffer), "RRA:%s:%3.1f:%u:%u",
+            rra_types[j], cfg->xff,
+            cdp_len, cdp_num) >= sizeof (buffer))
+      {
+        ERROR ("rra_get: Buffer would have been truncated.");
+        continue;
+      }
+
+      rra_def[rra_num++] = sstrdup (buffer);
+    }
+  }
+
+  *ret = rra_def;
+  return (rra_num);
+} /* }}} int rra_get */
+
+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);
+} /* }}} void ds_free */
+
+static int ds_get (char ***ret, /* {{{ */
+    const data_set_t *ds, const value_list_t *vl,
+    const rrdcreate_config_t *cfg)
+{
+  char **ds_def;
+  int ds_num;
+
+  char min[32];
+  char max[32];
+  char buffer[128];
+
+  ds_def = (char **) malloc (ds->ds_num * sizeof (char *));
+  if (ds_def == NULL)
+  {
+    char errbuf[1024];
+    ERROR ("rrdtool plugin: malloc failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    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
+    {
+      ERROR ("rrdtool plugin: Unknown DS type: %i",
+          d->type);
+      break;
+    }
+
+    if (isnan (d->min))
+    {
+      sstrncpy (min, "U", sizeof (min));
+    }
+    else
+      ssnprintf (min, sizeof (min), "%lf", d->min);
+
+    if (isnan (d->max))
+    {
+      sstrncpy (max, "U", sizeof (max));
+    }
+    else
+      ssnprintf (max, sizeof (max), "%lf", d->max);
+
+    status = ssnprintf (buffer, sizeof (buffer),
+        "DS:%s:%s:%i:%s:%s",
+        d->name, type,
+        (cfg->heartbeat > 0) ? cfg->heartbeat : (2 * vl->interval),
+        min, max);
+    if ((status < 1) || (status >= sizeof (buffer)))
+      break;
+
+    ds_def[ds_num] = sstrdup (buffer);
+  } /* for ds_num = 0 .. ds->ds_num */
+
+  if (ds_num != ds->ds_num)
+  {
+    ds_free (ds_num, ds_def);
+    return (-1);
+  }
+
+  *ret = ds_def;
+  return (ds_num);
+} /* }}} int ds_get */
+
+#if HAVE_THREADSAFE_LIBRRD
+static int srrd_create (const char *filename, /* {{{ */
+    unsigned long pdp_step, time_t last_up,
+    int argc, const char **argv)
+{
+  int status;
+
+  optind = 0; /* bug in librrd? */
+  rrd_clear_error ();
+
+  status = rrd_create_r (filename, pdp_step, last_up, argc, (void *) argv);
+
+  if (status != 0)
+  {
+    WARNING ("rrdtool plugin: rrd_create_r (%s) failed: %s",
+        filename, rrd_get_error ());
+  }
+
+  return (status);
+} /* }}} int srrd_create */
+/* #endif HAVE_THREADSAFE_LIBRRD */
+
+#else /* !HAVE_THREADSAFE_LIBRRD */
+static int srrd_create (const char *filename, /* {{{ */
+    unsigned long pdp_step, time_t last_up,
+    int argc, const char **argv)
+{
+  int status;
+
+  int new_argc;
+  char **new_argv;
+
+  char pdp_step_str[16];
+  char last_up_str[16];
+
+  new_argc = 6 + argc;
+  new_argv = (char **) malloc ((new_argc + 1) * sizeof (char *));
+  if (new_argv == NULL)
+  {
+    ERROR ("rrdtool plugin: malloc failed.");
+    return (-1);
+  }
+
+  if (last_up == 0)
+    last_up = time (NULL) - 10;
+
+  ssnprintf (pdp_step_str, sizeof (pdp_step_str), "%lu", pdp_step);
+  ssnprintf (last_up_str, sizeof (last_up_str), "%u", (unsigned int) last_up);
+
+  new_argv[0] = "create";
+  new_argv[1] = filename;
+  new_argv[2] = "-s";
+  new_argv[3] = pdp_step_str;
+  new_argv[4] = "-b";
+  new_argv[5] = last_up_str;
+
+  memcpy (new_argv + 6, argv, argc * sizeof (char *));
+  new_argv[new_argc] = NULL;
+
+  pthread_mutex_lock (&librrd_lock);
+  optind = 0; /* bug in librrd? */
+  rrd_clear_error ();
+
+  status = rrd_create (new_argc, new_argv);
+  pthread_mutex_unlock (&librrd_lock);
+
+  if (status != 0)
+  {
+    WARNING ("rrdtool plugin: rrd_create (%s) failed: %s",
+        filename, rrd_get_error ());
+  }
+
+  sfree (new_argv);
+
+  return (status);
+} /* }}} int srrd_create */
+#endif /* !HAVE_THREADSAFE_LIBRRD */
+
+/*
+ * Public functions
+ */
+int cu_rrd_create_file (const char *filename, /* {{{ */
+    const data_set_t *ds, const value_list_t *vl,
+    const rrdcreate_config_t *cfg)
+{
+  char **argv;
+  int argc;
+  char **rra_def;
+  int rra_num;
+  char **ds_def;
+  int ds_num;
+  int status = 0;
+
+  if (check_create_dir (filename))
+    return (-1);
+
+  if ((rra_num = rra_get (&rra_def, vl, cfg)) < 1)
+  {
+    ERROR ("cu_rrd_create_file failed: Could not calculate RRAs");
+    return (-1);
+  }
+
+  if ((ds_num = ds_get (&ds_def, ds, vl, cfg)) < 1)
+  {
+    ERROR ("cu_rrd_create_file failed: Could not calculate DSes");
+    return (-1);
+  }
+
+  argc = ds_num + rra_num;
+
+  if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
+  {
+    char errbuf[1024];
+    ERROR ("cu_rrd_create_file failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  memcpy (argv, ds_def, ds_num * sizeof (char *));
+  memcpy (argv + ds_num, rra_def, rra_num * sizeof (char *));
+  argv[ds_num + rra_num] = NULL;
+
+  assert (vl->time > 10);
+  status = srrd_create (filename,
+      (cfg->stepsize > 0) ? cfg->stepsize : vl->interval,
+      vl->time - 10,
+      argc, (const char **) argv);
+
+  free (argv);
+  ds_free (ds_num, ds_def);
+  rra_free (rra_num, rra_def);
+
+  if (status != 0)
+  {
+    WARNING ("cu_rrd_create_file: srrd_create (%s) returned status %i.",
+        filename, status);
+  }
+  else
+  {
+    DEBUG ("cu_rrd_create_file: Successfully created RRD file \"%s\".",
+        filename);
+  }
+
+  return (status);
+} /* }}} int cu_rrd_create_file */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/utils_rrdcreate.h b/src/utils_rrdcreate.h
new file mode 100644 (file)
index 0000000..6208a6f
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * collectd - src/utils_rrdcreate.h
+ * Copyright (C) 2008  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public 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>
+ **/
+
+#ifndef UTILS_RRDCREATE_H
+#define UTILS_RRDCREATE_H 1
+
+struct rrdcreate_config_s
+{
+  int    stepsize;
+  int    heartbeat;
+  int    rrarows;
+  double xff;
+
+  int *timespans;
+  size_t timespans_num;
+
+  char **consolidation_functions;
+  size_t consolidation_functions_num;
+};
+typedef struct rrdcreate_config_s rrdcreate_config_t;
+
+int cu_rrd_create_file (const char *filename,
+    const data_set_t *ds, const value_list_t *vl,
+    const rrdcreate_config_t *cfg);
+
+#endif /* UTILS_RRDCREATE_H */
+
+/* vim: set sw=2 sts=2 et : */