Merge branch 'collectd-4.5'
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 8 Nov 2008 08:00:00 +0000 (09:00 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 8 Nov 2008 08:00:00 +0000 (09:00 +0100)
41 files changed:
.mailmap [new file with mode: 0644]
README
TODO
configure.in
contrib/oracle/create_schema.ddl [new file with mode: 0644]
contrib/oracle/db_systat.sql [new file with mode: 0644]
src/Makefile.am
src/collectd.conf.in
src/collectd.conf.pod
src/dbi.c [new file with mode: 0644]
src/filecount.c
src/filter_ignore.c [new file with mode: 0644]
src/filter_pcre.c [new file with mode: 0644]
src/interface.c
src/libiptc/Makefile.am
src/libiptc/README.collectd [new file with mode: 0644]
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/oracle.c [new file with mode: 0644]
src/perl.c
src/plugin.c
src/plugin.h
src/postgresql.c
src/rrdcached.c [new file with mode: 0644]
src/rrdtool.c
src/snmp.c
src/types.db
src/utils_cmd_flush.h
src/utils_cmd_getval.h
src/utils_cmd_listval.h
src/utils_cmd_putnotif.h
src/utils_cmd_putval.h
src/utils_complain.h
src/utils_dns.h
src/utils_rrdcreate.c [new file with mode: 0644]
src/utils_rrdcreate.h [new file with mode: 0644]
src/utils_subst.c [new file with mode: 0644]
src/utils_subst.h [new file with mode: 0644]

diff --git a/.mailmap b/.mailmap
new file mode 100644 (file)
index 0000000..9bb4399
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,8 @@
+Anthony <anthony>
+Florian Forster <octo>
+Florian Forster <octo@dev4.office.noris.de>
+Luboš Staněk <kolektor@atlas.cz>
+Luboš Staněk <lubek@users.sourceforge.net>
+Niki W. Waibel <niki>
+Sebastian Harl <tokkee>
+
diff --git a/README b/README
index e698786..5c9eed0 100644 (file)
--- a/README
+++ b/README
@@ -40,6 +40,10 @@ Features
     - cpufreq
       CPU frequency (For laptops with speed step or a similar technology)
 
+    - dbi
+      Executes SQL statements on various databases and interprets the returned
+      data.
+
     - df
       Mountpoint usage (Basically the values `df(1)' delivers)
 
@@ -76,6 +80,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 +159,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 +193,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 +233,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
@@ -226,6 +250,13 @@ Features
       needed. Please read collectd-unixsock(5) for a description on how that's
       done.
 
+  * Filtering and rewriting values dispatched to collectd can be done by the
+    following plugins:
+
+    - filter_pcre
+      Filter and rewrite value lists based on Perl-compatible regular
+      expressions.
+
   * Logging is, as everything in collectd, provided by plugins. The following
     plugins keep up informed about what's going on:
 
@@ -279,7 +310,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.
 
@@ -339,76 +370,105 @@ Prerequisites
   * CoreFoundation.framework and IOKit.framework (optional)
     For compiling on Darwin in general and the `apple_sensors' plugin in
     particular.
+    <http://developer.apple.com/corefoundation/>
 
   * libcurl (optional)
     If you want to use the `apache', `ascent', or `nginx' plugin.
+    <http://curl.haxx.se/>
+
+  * libdbi (optional)
+    Used by the `dbi' plugin to connect to various databases.
+    <http://libdbi.sourceforge.net/>
 
   * libesmtp (optional)
     For the `notify_email' plugin.
+    <http://www.stafford.uklinux.net/libesmtp/>
 
   * libhal (optional)
     If present, the uuid plugin will check for UUID from HAL.
+    <http://hal.freedesktop.org/>
 
-  * 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.
+    <http://netfilter.org/>
 
   * libmysqlclient (optional)
     Unsurprisingly used by the `mysql' plugin.
+    <http://dev.mysql.com/>
 
   * libnetlink (optional)
     Used, obviously, for the `netlink' plugin.
+    <http://www.linuxfoundation.org/en/Net:Iproute2>
 
   * libnetsnmp (optional)
     For the `snmp' plugin.
+    <http://www.net-snmp.org/>
 
   * libnotify (optional)
     For the `notify_desktop' plugin.
+    <http://www.galago-project.org/>
 
   * liboping (optional, if not found a version shipped with this distribution
     can be used)
     Used by the `ping' plugin to send and receive ICMP packets.
+    <http://verplant.org/liboping/>
 
   * libowcapi (optional)
     Used by the `onewire' plugin to read values from onewire sensors (or the
     owserver(1) daemon).
+    <http://www.owfs.org/>
 
   * libpcap (optional)
     Used to capture packets by the `dns' plugin.
+    <http://www.tcpdump.org/>
+
+  * libpcre (optional)
+    Used by the `filter_pcre' plugin.
+    <http://www.pcre.org/>
 
   * libperl (optional)
     Obviously used by the `perl' plugin. The library has to be compiled with
     ithread support (introduced in Perl 5.6.0).
+    <http://www.perl.org/>
 
   * libpq (optional)
     The PostgreSQL C client library used by the `postgresql' plugin.
+    <http://www.postgresql.org/>
 
-  * 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.
+    <http://oss.oetiker.ch/rrdtool/>
 
   * librt, libsocket, libkstat, libdevinfo (optional)
     Various standard Solaris libraries which provide system functions.
+    <http://developers.sun.com/solaris/>
 
   * libsensors (optional)
     To read from `lm_sensors', see the `sensors' plugin.
+    <http://www.lm-sensors.org/>
 
-  * 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'.
+    <http://networkupstools.org/>
 
   * libvirt (optional)
     Collect statistics from virtual machines.
+    <http://libvirt.org/>
 
   * libxml2 (optional)
     Parse XML data. This is needed for the `ascent' and `libvirt' plugins.
+    <http://xmlsoft.org/>
 
   * libxmms (optional)
+    <http://www.xmms.org/>
 
 
 Configuring / Compiling / Installing
@@ -418,7 +478,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
diff --git a/TODO b/TODO
index b5cd6b5..3c8f49e 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,6 +1,6 @@
-* Parse options/identifiers with spaces in them correctly.
-* Figure out what to do with the onewire plugin.
+* Finalize the onewire plugin.
 * Custom notification messages?
+* Implement moving-average calculation for the threshold stuff.
 
 src/battery.c: commend not working code.
 
@@ -10,6 +10,7 @@ Wishlist:
   - be free of syntax errors.
 * Port nfs module to solaris
 * Port tape module to Linux
+* Port the apple_sensors plugin to Linux/PPC.
 * Maybe look into porting the serial module
 * Build Darwin package
 * Maybe let the network plugin configure whether or not notifications should be
index 0aa87ff..21af0b5 100644 (file)
@@ -922,102 +922,6 @@ fi
 m4_divert_once([HELP_WITH], [
 collectd additional packages:])
 
-# AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given])
-librrd_cflags=""
-librrd_ldflags=""
-librrd_threadsafe="yes"
-AC_ARG_WITH(rrdtool, [AS_HELP_STRING([--with-rrdtool@<:@=PREFIX@:>@], [Path to rrdtool.])],
-[      if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               librrd_cflags="-I$withval/include"
-               librrd_ldflags="-L$withval/lib"
-               with_rrdtool="yes"
-       else
-               with_rrdtool="$withval"
-       fi
-], [with_rrdtool="yes"])
-if test "x$with_rrdtool" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-
-       CPPFLAGS="$CPPFLAGS $librrd_cflags"
-       LDFLAGS="$LDFLAGS $librrd_ldflags"
-
-       AC_CHECK_HEADERS(rrd.h,, [with_rrdtool="no (rrd.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_rrdtool" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-
-       CPPFLAGS="$CPPFLAGS $librrd_cflags"
-       LDFLAGS="$LDFLAGS $librrd_ldflags"
-
-       AC_CHECK_LIB(rrd_th, rrd_update_r,
-       [with_rrdtool="yes"
-        librrd_ldflags="$librrd_ldflags -lrrd_th -lm"
-       ],
-       [librrd_threadsafe="no"
-        AC_CHECK_LIB(rrd, rrd_update,
-        [with_rrdtool="yes"
-         librrd_ldflags="$librrd_ldflags -lrrd -lm"
-        ],
-        [with_rrdtool="no (symbol 'rrd_update' not found)"],
-        [-lm])
-       ],
-       [-lm])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_rrdtool" = "xyes"
-then
-       BUILD_WITH_LIBRRD_CFLAGS="$librrd_cflags"
-       BUILD_WITH_LIBRRD_LDFLAGS="$librrd_ldflags"
-       AC_SUBST(BUILD_WITH_LIBRRD_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBRRD_LDFLAGS)
-fi
-if test "x$librrd_threadsafe" = "xyes"
-then
-       AC_DEFINE(HAVE_THREADSAFE_LIBRRD, 1, [Define to 1 if you have the threadsafe rrd library (-lrrd_th).])
-fi
-
-AC_ARG_WITH(libpthread, [AS_HELP_STRING([--with-libpthread=@<:@=PREFIX@:>@], [Path to libpthread.])],
-[      if test "x$withval" != "xno" \
-               && test "x$withval" != "xyes"
-       then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include"
-               with_libpthread="yes"
-       else
-               if test "x$withval" = "xno"
-               then
-                       with_libpthread="no (disabled)"
-               fi
-       fi
-], [with_libpthread="yes"])
-if test "x$with_libpthread" = "xyes"
-then
-       AC_CHECK_LIB(pthread, pthread_create, [with_libpthread="yes"], [with_libpthread="no (libpthread not found)"], [])
-fi
-if test "x$with_libpthread" = "xyes"
-then
-       AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"])
-fi
-if test "x$with_libpthread" = "xyes"
-then
-       collect_pthread=1
-else
-       collect_pthread=0
-fi
-AC_DEFINE_UNQUOTED(HAVE_LIBPTHREAD, [$collect_pthread],
-       [Wether or not to use pthread (POSIX threads) library])
-AM_CONDITIONAL(BUILD_WITH_LIBPTHREAD, test "x$with_libpthread" = "xyes")
-
 if test "x$ac_system" = "xSolaris"
 then
        with_kstat="yes"
@@ -1044,7 +948,45 @@ fi
 AM_CONDITIONAL(BUILD_WITH_LIBKSTAT, test "x$with_kstat" = "xyes")
 AM_CONDITIONAL(BUILD_WITH_LIBDEVINFO, test "x$with_devinfo" = "xyes")
 
-### BEGIN of check for libcurl ###
+with_libiokit="no"
+AC_CHECK_LIB(IOKit, IOServiceGetMatchingServices,
+[
+       with_libiokit="yes"
+], 
+[
+       with_libiokit="no"
+])
+AM_CONDITIONAL(BUILD_WITH_LIBIOKIT, test "x$with_libiokit" = "xyes")
+
+with_libkvm="no"
+AC_CHECK_LIB(kvm, kvm_getprocs, [with_kvm_getprocs="yes"], [with_kvm_getprocs="no"])
+if test "x$with_kvm_getprocs" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBKVM_GETPROCS, 1,
+                 [Define to 1 if you have the 'kvm' library with the 'kvm_getprocs' symbol (-lkvm)])
+       with_libkvm="yes"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETPROCS, test "x$with_kvm_getprocs" = "xyes")
+
+AC_CHECK_LIB(kvm, kvm_getswapinfo, [with_kvm_getswapinfo="yes"], [with_kvm_getswapinfo="no"])
+if test "x$with_kvm_getswapinfo" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBKVM_GETSWAPINFO, 1,
+                 [Define to 1 if you have the 'kvm' library with the 'kvm_getswapinfo' symbol (-lkvm)])
+       with_libkvm="yes"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETSWAPINFO, test "x$with_kvm_getswapinfo" = "xyes")
+
+AC_CHECK_LIB(kvm, kvm_nlist, [with_kvm_nlist="yes"], [with_kvm_nlist="no"])
+if test "x$with_kvm_nlist" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBKVM_NLIST, 1,
+                 [Define to 1 if you have the 'kvm' library with the 'kvm_nlist' symbol (-lkvm)])
+       with_libkvm="yes"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_NLIST, test "x$with_kvm_nlist" = "xyes")
+
+# --with-libcurl {{{
 with_curl_config="curl-config"
 with_curl_cflags=""
 with_curl_libs=""
@@ -1112,221 +1054,166 @@ then
        AC_SUBST(BUILD_WITH_LIBCURL_LIBS)
 fi
 AM_CONDITIONAL(BUILD_WITH_LIBCURL, test "x$with_libcurl" = "xyes")
-### END of check for libcurl ###
-
-with_libiokit="no"
-AC_CHECK_LIB(IOKit, IOServiceGetMatchingServices,
-[
-       with_libiokit="yes"
-], 
-[
-       with_libiokit="no"
-])
-AM_CONDITIONAL(BUILD_WITH_LIBIOKIT, test "x$with_libiokit" = "xyes")
+# }}}
 
-with_libstatgrab_cflags=""
-with_libstatgrab_ldflags=""
-AC_ARG_WITH(libstatgrab, [AS_HELP_STRING([--with-libstatgrab@<:@=PREFIX@:>@], [Path to libstatgrab.])],
+# --with-libdbi {{{
+with_libdbi_cppflags=""
+with_libdbi_ldflags=""
+AC_ARG_WITH(libdbi, [AS_HELP_STRING([--with-libdbi@<:@=PREFIX@:>@], [Path to libdbi.])],
 [
-       if test "x$withval" != "xno" \
-               && test "x$withval" != "xyes"
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
        then
-               with_libstatgrab_cflags="-I$withval/include"
-               with_libstatgrab_ldflags="-L$withval/lib"
-               with_libstatgrab="yes"
+               with_libdbi_cppflags="-I$withval/include"
+               with_libdbi_ldflags="-L$withval/lib"
+               with_libdbi="yes"
        else
-               with_libstatgrab="$withval"
+               with_libdbi="$withval"
        fi
 ],
 [
-       if test "x$ac_system" = "xunknown"
-       then
-               with_libstatgrab="yes"
-       else
-               with_libstatgrab="no"
-       fi
+       with_libdbi="yes"
 ])
-with_libstatgrab_pkg_config="yes"
-if test "x$with_libstatgrab" = "xyes" \
-  && test "x$PKG_CONFIG" != "x"
+if test "x$with_libdbi" = "xyes"
 then
-  AC_MSG_CHECKING([pkg-config for libstatgrab])
-  temp_result="found"
-  $PKG_CONFIG --exists libstatgrab 2>/dev/null
-  if test "$?" != "0"
-  then
-    with_libstatgrab_pkg_config="no"
-    temp_result="not found"
-  fi
-  AC_MSG_RESULT([$temp_result])
-else
-  AC_MSG_NOTICE([pkg-config not available, trying to guess flags for the statgrab library.])
-  with_libstatgrab_pkg_config="no"
-  with_libstatgrab_ldflags="$with_libstatgrab_ldflags -lstatgrab"
-fi
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libdbi_cppflags"
 
-if test "x$with_libstatgrab" = "xyes" \
-  && test "x$with_libstatgrab_pkg_config" = "xyes" \
-  && test "x$with_libstatgrab_cflags" = "x"
-then
-  AC_MSG_CHECKING([for libstatgrab CFLAGS])
-  temp_result="`$PKG_CONFIG --cflags libstatgrab`"
-  if test "$?" = "0"
-  then
-    with_libstatgrab_cflags="$temp_result"
-  else
-    with_libstatgrab="no ($PKG_CONFIG --cflags libstatgrab failed)"
-    temp_result="$PKG_CONFIG --cflags libstatgrab failed"
-  fi
-  AC_MSG_RESULT([$temp_result])
-fi
+       AC_CHECK_HEADERS(dbi/dbi.h, [with_libdbi="yes"], [with_libdbi="no (dbi/dbi.h not found)"])
 
-if test "x$with_libstatgrab" = "xyes" \
-  && test "x$with_libstatgrab_pkg_config" = "xyes" \
-  && test "x$with_libstatgrab_ldflags" = "x"
-then
-  AC_MSG_CHECKING([for libstatgrab LDFLAGS])
-  temp_result="`$PKG_CONFIG --libs libstatgrab`"
-  if test "$?" = "0"
-  then
-    with_libstatgrab_ldflags="$temp_result"
-  else
-    with_libstatgrab="no ($PKG_CONFIG --libs libstatgrab failed)"
-    temp_result="$PKG_CONFIG --libs libstatgrab failed"
-  fi
-  AC_MSG_RESULT([$temp_result])
+       CPPFLAGS="$SAVE_CPPFLAGS"
 fi
-
-if test "x$with_libstatgrab" = "xyes"
+if test "x$with_libdbi" = "xyes"
 then
-  SAVE_CPPFLAGS="$CPPFLAGS"
-  CPPFLAGS="$CPPFLAGS $with_libstatgrab_cflags"
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libdbi_cppflags"
+       LDFLAGS="$LDFLAGS $with_libdbi_ldflags"
 
-  AC_CHECK_HEADERS(statgrab.h,
-                  [with_libstatgrab="yes"],
-                  [with_libstatgrab="no (statgrab.h not found)"])
+       AC_CHECK_LIB(dbi, dbi_initialize, [with_libdbi="yes"], [with_libdbi="no (Symbol 'dbi_initialize' not found)"])
 
-  CPPFLAGS="$SAVE_CPPFLAGS"
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
 fi
-
-if test "x$with_libstatgrab" = "xyes"
+if test "x$with_libdbi" = "xyes"
 then
-  SAVE_CFLAGS="$CFLAGS"
-  SAVE_LDFLAGS="$LDFLAGS"
-
-  CFLAGS="$CFLAGS $with_libstatgrab_cflags"
-  LDFLAGS="$LDFLAGS $with_libstatgrab_ldflags"
-
-  AC_CHECK_LIB(statgrab, sg_init,
-              [with_libstatgrab="yes"],
-              [with_libstatgrab="no (symbol sg_init not found)"])
-
-  CFLAGS="$SAVE_CFLAGS"
-  LDFLAGS="$SAVE_LDFLAGS"
+       BUILD_WITH_LIBDBI_CPPFLAGS="$with_libdbi_cppflags"
+       BUILD_WITH_LIBDBI_LDFLAGS="$with_libdbi_ldflags"
+       BUILD_WITH_LIBDBI_LIBS="-ldbi"
+       AC_SUBST(BUILD_WITH_LIBDBI_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBDBI_LDFLAGS)
+       AC_SUBST(BUILD_WITH_LIBDBI_LIBS)
 fi
+AM_CONDITIONAL(BUILD_WITH_LIBDBI, test "x$with_libdbi" = "xyes")
+# }}}
 
-AM_CONDITIONAL(BUILD_WITH_LIBSTATGRAB, test "x$with_libstatgrab" = "xyes")
-if test "x$with_libstatgrab" = "xyes"
+# --with-libesmtp {{{
+AC_ARG_WITH(libesmtp, [AS_HELP_STRING([--with-libesmtp@<:@=PREFIX@:>@], [Path to libesmtp.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include -D_THREAD_SAFE"
+               with_libesmtp="yes"
+       else
+               with_libesmtp="$withval"
+       fi
+],
+[
+       with_libesmtp="yes"
+])
+if test "x$with_libesmtp" = "xyes"
 then
-  AC_DEFINE(HAVE_LIBSTATGRAB, 1, [Define to 1 if you have the 'statgrab' library (-lstatgrab)])
-  BUILD_WITH_LIBSTATGRAB_CFLAGS="$with_libstatgrab_cflags"
-  BUILD_WITH_LIBSTATGRAB_LDFLAGS="$with_libstatgrab_ldflags"
-  AC_SUBST(BUILD_WITH_LIBSTATGRAB_CFLAGS)
-  AC_SUBST(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
+       AC_CHECK_LIB(esmtp, smtp_create_session,
+       [
+               AC_DEFINE(HAVE_LIBESMTP, 1, [Define to 1 if you have the esmtp library (-lesmtp).])
+       ], [with_libesmtp="no (libesmtp not found)"])
 fi
-
-with_libkvm="no"
-AC_CHECK_LIB(kvm, kvm_getprocs, [with_kvm_getprocs="yes"], [with_kvm_getprocs="no"])
-if test "x$with_kvm_getprocs" = "xyes"
+if test "x$with_libesmtp" = "xyes"
 then
-       AC_DEFINE(HAVE_LIBKVM_GETPROCS, 1,
-                 [Define to 1 if you have the 'kvm' library with the 'kvm_getprocs' symbol (-lkvm)])
-       with_libkvm="yes"
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETPROCS, test "x$with_kvm_getprocs" = "xyes")
-
-AC_CHECK_LIB(kvm, kvm_getswapinfo, [with_kvm_getswapinfo="yes"], [with_kvm_getswapinfo="no"])
-if test "x$with_kvm_getswapinfo" = "xyes"
-then
-       AC_DEFINE(HAVE_LIBKVM_GETSWAPINFO, 1,
-                 [Define to 1 if you have the 'kvm' library with the 'kvm_getswapinfo' symbol (-lkvm)])
-       with_libkvm="yes"
+       AC_CHECK_HEADERS(libesmtp.h,
+       [
+               AC_DEFINE(HAVE_LIBESMTP_H, 1, [Define to 1 if you have the <libesmtp.h> header file.])
+       ], [with_libesmtp="no (libesmtp.h not found)"])
 fi
-AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETSWAPINFO, test "x$with_kvm_getswapinfo" = "xyes")
-
-AC_CHECK_LIB(kvm, kvm_nlist, [with_kvm_nlist="yes"], [with_kvm_nlist="no"])
-if test "x$with_kvm_nlist" = "xyes"
+if test "x$with_libesmtp" = "xyes"
 then
-       AC_DEFINE(HAVE_LIBKVM_NLIST, 1,
-                 [Define to 1 if you have the 'kvm' library with the 'kvm_nlist' symbol (-lkvm)])
-       with_libkvm="yes"
+       collect_libesmtp=1
+else
+       collect_libesmtp=0
 fi
-AM_CONDITIONAL(BUILD_WITH_LIBKVM_NLIST, test "x$with_kvm_nlist" = "xyes")
+AC_DEFINE_UNQUOTED(COLLECT_LIBESMTP, [$collect_libesmtp],
+       [Wether or not to use the esmtp library])
+AM_CONDITIONAL(BUILD_WITH_LIBESMTP, test "x$with_libesmtp" = "xyes")
+# }}}
 
-with_sensors_cflags=""
-with_sensors_ldflags=""
-AC_ARG_WITH(lm-sensors, [AS_HELP_STRING([--with-lm-sensors@<:@=PREFIX@:>@], [Path to lm_sensors.])],
+# --with-libiptc {{{
+with_own_libiptc="no"
+AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])],
 [
-       if test "x$withval" = "xno"
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
        then
-               with_lm_sensors="no"
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_libiptc="yes"
        else
-               with_lm_sensors="yes"
-               if test "x$withval" != "xyes"
-               then
-                       with_sensors_cflags="-I$withval/include"
-                       with_sensors_ldflags="-L$withval/lib"
-                       with_lm_sensors="yes"
-               fi
+               with_libiptc="$withval"
        fi
 ],
 [
        if test "x$ac_system" = "xLinux"
        then
-               with_lm_sensors="yes"
+               with_libiptc="yes"
        else
-               with_lm_sensors="no (Linux only library)"
+               with_libiptc="no (Linux only)"
        fi
 ])
-if test "x$with_lm_sensors" = "xyes"
+if test "x$with_libiptc" = "xyes"
 then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
-
-#      AC_CHECK_HEADERS(sensors/sensors.h,
-#      [
-#              AC_DEFINE(HAVE_SENSORS_SENSORS_H, 1, [Define to 1 if you have the <sensors/sensors.h> header file.])
-#      ],
-#      [with_lm_sensors="no (sensors/sensors.h not found)"])
-       AC_CHECK_HEADERS(sensors/sensors.h, [], [with_lm_sensors="no (sensors/sensors.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
+       AC_CHECK_LIB(iptc, iptc_init,
+       [
+               AC_DEFINE(HAVE_LIBIPTC, 1, [Define to 1 if you have the iptc library (-liptc).])
+       ],
+       [
+               with_libiptc="yes"
+               with_own_libiptc="yes"
+       ])
 fi
-if test "x$with_lm_sensors" = "xyes"
+if test "x$with_libiptc" = "xyes" -a "x$with_own_libiptc" != "xyes"
 then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
-       LDFLAGS="$LDFLAGS $with_sensors_ldflags"
+       AC_CHECK_HEADERS(libiptc/libiptc.h,
+       [
+               AC_DEFINE(HAVE_LIBIPTC_LIBIPTC_H, 1, [Define to 1 if you have the <libiptc/libiptc.h> header file.])
+       ],
+       [
+               with_libiptc="yes"
+               with_own_libiptc="yes"
+       ])
+fi
+if test "x$with_libiptc" = "xyes"
+then
+       SAVE_CFLAGS=$CFLAGS
+       CFLAGS="$CFLAGS $KERNEL_CFLAGS"
 
-       AC_CHECK_LIB(sensors, sensors_init,
+       AC_CHECK_HEADERS(linux/netfilter_ipv4/ip_tables.h linux/netfilter_ipv6/ip6_tables.h, [],
        [
-               AC_DEFINE(HAVE_LIBSENSORS, 1, [Define to 1 if you have the sensors library (-lsensors).])
+               with_libiptc="no (Linux iptables headers not found - check KERNEL_DIR)"
+               with_own_libiptc="no"
        ],
-       [with_lm_sensors="no (libsensors not found)"])
+       [
+#include "$srcdir/src/libiptc/ipt_kernel_headers.h"
+       ])
 
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
+       CFLAGS=$SAVE_CFLAGS
 fi
-if test "x$with_lm_sensors" = "xyes"
+AM_CONDITIONAL(BUILD_WITH_LIBIPTC, test "x$with_libiptc" = "xyes")
+AM_CONDITIONAL(BUILD_WITH_OWN_LIBIPTC, test "x$with_own_libiptc" = "xyes")
+if test "x$with_own_libiptc" = "xyes"
 then
-       BUILD_WITH_LIBSENSORS_CFLAGS="$with_sensors_cflags"
-       BUILD_WITH_LIBSENSORS_LDFLAGS="$with_sensors_ldflags"
-       AC_SUBST(BUILD_WITH_LIBSENSORS_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBSENSORS_LDFLAGS)
+       AC_DEFINE(OWN_LIBIPTC, 1, [Define to 1 if we use the shipped iptc library.])
 fi
-AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_lm_sensors" = "xyes")
+# }}}
 
+# --with-libmysql {{{
 with_mysql_config="mysql_config"
 with_mysql_cflags=""
 with_mysql_libs=""
@@ -1404,75 +1291,250 @@ then
        AC_SUBST(BUILD_WITH_LIBMYSQL_LIBS)
 fi
 AM_CONDITIONAL(BUILD_WITH_LIBMYSQL, test "x$with_libmysql" = "xyes")
+# }}}
 
-with_own_liboconfig="no"
-liboconfig_LDFLAGS="$LDFLAGS"
-liboconfig_CPPFLAGS="$CPPFLAGS"
-AC_ARG_WITH(liboconfig, [AS_HELP_STRING([--with-liboconfig@<:@=PREFIX@:>@], [Path to liboconfig.])],
+# --with-libnetlink {{{
+with_libnetlink_cflags=""
+with_libnetlink_libs="-lnetlink"
+AC_ARG_WITH(libnetlink, [AS_HELP_STRING([--with-libnetlink@<:@=PREFIX@:>@], [Path to libnetlink.])],
 [
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               if test -d "$withval/lib"
-               then
-                       liboconfig_LDFLAGS="$LDFLAGS -L$withval/lib"
-               fi
-               if test -d "$withval/include"
-               then
-                       liboconfig_CPPFLAGS="$CPPFLAGS -I$withval/include"
-               fi
-       fi
-       if test "x$withval" = "xno"
-       then
-               AC_MSG_ERROR("liboconfig is required")
-       fi
+ echo "libnetlink: withval = $withval"
+ if test "x$withval" = "xyes"
+ then
+        with_libnetlink="yes"
+ else if test "x$withval" = "xno"
+ then
+        with_libnetlink="no"
+ else
+        if test -d "$withval/include"
+        then
+                with_libnetlink_cflags="-I$withval/include"
+                with_libnetlink_libs="-L$withval/lib -lnetlink"
+                with_libnetlink="yes"
+        else
+                AC_MSG_ERROR("no such directory: $withval/include")
+        fi
+ fi; fi
 ],
 [
-       with_liboconfig="yes"
+ if test "x$ac_system" = "xLinux"
+ then
+        with_libnetlink="yes"
+ else
+        with_libnetlink="no (Linux only library)"
+ fi
 ])
+if test "x$with_libnetlink" = "xyes"
+then
+       SAVE_CFLAGS=$CFLAGS
+       CFLAGS="$CFLAGS $with_libnetlink_cflags"
 
-save_LDFLAGS="$LDFLAGS"
-save_CPPFLAGS="$CPPFLAGS"
-LDFLAGS="$liboconfig_LDFLAGS"
-CPPFLAGS="$liboconfig_CPPFLAGS"
-AC_CHECK_LIB(oconfig, oconfig_parse_fh,
-[
-       with_liboconfig="yes"
-       with_own_liboconfig="no"
-],
-[
-       with_liboconfig="yes"
-       with_own_liboconfig="yes"
-       LDFLAGS="$save_LDFLAGS"
-       CPPFLAGS="$save_CPPFLAGS"
-])
+       with_libnetlink="no (libnetlink.h not found)"
 
-AM_CONDITIONAL(BUILD_WITH_OWN_LIBOCONFIG, test "x$with_own_liboconfig" = "xyes")
-if test "x$with_own_liboconfig" = "xyes"
+       AC_CHECK_HEADERS(libnetlink.h iproute/libnetlink.h linux/libnetlink.h,
+       [
+        with_libnetlink="yes"
+        break
+       ], [],
+[#include <stdio.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>])
+       AC_CHECK_HEADERS(linux/gen_stats.h linux/pkt_sched.h, [], [],
+[#include <stdio.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <sys/socket.h>])
+
+       AC_COMPILE_IFELSE(
+[#include <stdio.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+int main (void)
+{
+       int retval = TCA_STATS2;
+       return (retval);
+}],
+       [AC_DEFINE([HAVE_TCA_STATS2], 1, [True if the enum-member TCA_STATS2 exists])]
+       []);
+
+       AC_COMPILE_IFELSE(
+[#include <stdio.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+int main (void)
+{
+       int retval = TCA_STATS;
+       return (retval);
+}],
+       [AC_DEFINE([HAVE_TCA_STATS], 1, [True if the enum-member TCA_STATS exists])]
+       []);
+
+       CFLAGS="$SAVE_CFLAGS"
+fi
+if test "x$with_libnetlink" = "xyes"
 then
-       with_liboconfig="yes (shipped version)"
+       AC_CHECK_LIB(netlink, rtnl_open,
+                    [with_libnetlink="yes"],
+                    [with_libnetlink="no (symbol 'rtnl_open' not found)"],
+                    [$with_libnetlink_libs])
+fi
+if test "x$with_libnetlink" = "xyes"
+then
+       BUILD_WITH_LIBNETLINK_CFLAGS="$with_libnetlink_cflags"
+       BUILD_WITH_LIBNETLINK_LIBS="$with_libnetlink_libs"
+       AC_SUBST(BUILD_WITH_LIBNETLINK_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBNETLINK_LIBS)
 fi
+AM_CONDITIONAL(BUILD_WITH_LIBNETLINK, test "x$with_libnetlink" = "xyes")
+# }}}
 
-#with_liboping="yes"
-with_own_liboping="no"
-liboping_LDFLAGS="$LDFLAGS"
-liboping_CPPFLAGS="$CPPFLAGS"
-AC_ARG_WITH(liboping, [AS_HELP_STRING([--with-liboping@<:@=PREFIX@:>@], [Path to liboping.])],
+# --with-libnetsnmp {{{
+with_snmp_config="net-snmp-config"
+with_snmp_cflags=""
+with_snmp_libs=""
+AC_ARG_WITH(libnetsnmp, [AS_HELP_STRING([--with-libnetsnmp@<:@=PREFIX@:>@], [Path to the Net-SNMPD library.])],
 [
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               if test -d "$withval/lib"
-               then
-                       liboping_LDFLAGS="$LDFLAGS -L$withval/lib"
-               fi
-               if test -d "$withval/include"
-               then
-                       liboping_CPPFLAGS="$CPPFLAGS -I$withval/include"
-               fi
-       fi
        if test "x$withval" = "xno"
        then
-               with_liboping="no"
-               with_own_liboping="no"
+               with_libnetsnmp="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libnetsnmp="yes"
+       else
+               if test -x "$withval"
+               then
+                       with_snmp_config="$withval"
+                       with_libnetsnmp="yes"
+               else
+                       with_snmp_config="$withval/bin/net-snmp-config"
+                       with_libnetsnmp="yes"
+               fi
+       fi; fi
+],
+[with_libnetsnmp="yes"])
+if test "x$with_libnetsnmp" = "xyes"
+then
+       with_snmp_cflags=`$with_snmp_config --cflags 2>/dev/null`
+       snmp_config_status=$?
+
+       if test $snmp_config_status -ne 0
+       then
+               with_libnetsnmp="no ($with_snmp_config failed)"
+       else
+               SAVE_CPPFLAGS="$CPPFLAGS"
+               CPPFLAGS="$CPPFLAGS $with_snmp_cflags"
+               
+               AC_CHECK_HEADERS(net-snmp/net-snmp-config.h, [], [with_libnetsnmp="no (net-snmp/net-snmp-config.h not found)"])
+
+               CPPFLAGS="$SAVE_CPPFLAGS"
+       fi
+fi
+if test "x$with_libnetsnmp" = "xyes"
+then
+       with_snmp_libs=`$with_snmp_config --libs 2>/dev/null`
+       snmp_config_status=$?
+
+       if test $snmp_config_status -ne 0
+       then
+               with_libnetsnmp="no ($with_snmp_config failed)"
+       else
+               AC_CHECK_LIB(netsnmp, init_snmp,
+               [with_libnetsnmp="yes"],
+               [with_libnetsnmp="no (libnetsnmp not found)"],
+               [$with_snmp_libs])
+       fi
+fi
+if test "x$with_libnetsnmp" = "xyes"
+then
+       BUILD_WITH_LIBSNMP_CFLAGS="$with_snmp_cflags"
+       BUILD_WITH_LIBSNMP_LIBS="$with_snmp_libs"
+       AC_SUBST(BUILD_WITH_LIBSNMP_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBSNMP_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBNETSNMP, test "x$with_libnetsnmp" = "xyes")
+# }}}
+
+# --with-liboconfig {{{
+with_own_liboconfig="no"
+liboconfig_LDFLAGS="$LDFLAGS"
+liboconfig_CPPFLAGS="$CPPFLAGS"
+AC_ARG_WITH(liboconfig, [AS_HELP_STRING([--with-liboconfig@<:@=PREFIX@:>@], [Path to liboconfig.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               if test -d "$withval/lib"
+               then
+                       liboconfig_LDFLAGS="$LDFLAGS -L$withval/lib"
+               fi
+               if test -d "$withval/include"
+               then
+                       liboconfig_CPPFLAGS="$CPPFLAGS -I$withval/include"
+               fi
+       fi
+       if test "x$withval" = "xno"
+       then
+               AC_MSG_ERROR("liboconfig is required")
+       fi
+],
+[
+       with_liboconfig="yes"
+])
+
+save_LDFLAGS="$LDFLAGS"
+save_CPPFLAGS="$CPPFLAGS"
+LDFLAGS="$liboconfig_LDFLAGS"
+CPPFLAGS="$liboconfig_CPPFLAGS"
+AC_CHECK_LIB(oconfig, oconfig_parse_fh,
+[
+       with_liboconfig="yes"
+       with_own_liboconfig="no"
+],
+[
+       with_liboconfig="yes"
+       with_own_liboconfig="yes"
+       LDFLAGS="$save_LDFLAGS"
+       CPPFLAGS="$save_CPPFLAGS"
+])
+
+AM_CONDITIONAL(BUILD_WITH_OWN_LIBOCONFIG, test "x$with_own_liboconfig" = "xyes")
+if test "x$with_own_liboconfig" = "xyes"
+then
+       with_liboconfig="yes (shipped version)"
+fi
+# }}}
+
+# --with-liboping {{{
+with_own_liboping="no"
+liboping_LDFLAGS="$LDFLAGS"
+liboping_CPPFLAGS="$CPPFLAGS"
+AC_ARG_WITH(liboping, [AS_HELP_STRING([--with-liboping@<:@=PREFIX@:>@], [Path to liboping.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               if test -d "$withval/lib"
+               then
+                       liboping_LDFLAGS="$LDFLAGS -L$withval/lib"
+               fi
+               if test -d "$withval/include"
+               then
+                       liboping_CPPFLAGS="$CPPFLAGS -I$withval/include"
+               fi
+       fi
+       if test "x$withval" = "xno"
+       then
+               with_liboping="no"
+               with_own_liboping="no"
        else if test "x$withval" = "xyes"
        then
                with_liboping="yes"
@@ -1502,7 +1564,78 @@ then
 fi
 AM_CONDITIONAL(BUILD_WITH_LIBOPING, test "x$with_liboping" = "xyes")
 AM_CONDITIONAL(BUILD_WITH_OWN_LIBOPING, test "x$with_own_liboping" = "xyes")
+# }}}
+
+# --with-oracle {{{
+with_oracle_cppflags=""
+with_oracle_libs=""
+AC_ARG_WITH(oracle, [AS_HELP_STRING([--with-oracle@<:@=ORACLE_HOME@:>@], [Path to Oracle.])],
+[
+       if test "x$withval" = "xyes"
+       then
+               if test "x$ORACLE_HOME" = "x"
+               then
+                       AC_MSG_WARN([Use of the Oracle library has been forced, but the environment variable ORACLE_HOME is not set.])
+               fi
+               with_oracle="yes"
+       else if test "x$withval" = "xno"
+       then
+               with_oracle="no"
+       else
+               with_oracle="yes"
+               ORACLE_HOME="$withval"
+       fi; fi
+],
+[
+       if test "x$ORACLE_HOME" = "x"
+       then
+               with_oracle="no (ORACLE_HOME is not set)"
+       else
+               with_oracle="yes"
+       fi
+])
+if test "x$ORACLE_HOME" != "x"
+then
+       with_oracle_cppflags="-I$ORACLE_HOME/rdbms/public"
+
+       if test -e "$ORACLE_HOME/lib/ldflags"
+       then
+               with_oracle_libs=`cat "$ORACLE_HOME/lib/ldflags"`
+       fi
+       #with_oracle_libs="-L$ORACLE_HOME/lib $with_oracle_libs -lclntsh"
+       with_oracle_libs="-L$ORACLE_HOME/lib -lclntsh"
+fi
+if test "x$with_oracle" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_oracle_cppflags"
+
+       AC_CHECK_HEADERS(oci.h, [with_oracle="yes"], [with_oracle="no (oci.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_oracle" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_oracle_cppflags"
+       LDFLAGS="$LDFLAGS $with_oracle_libs"
 
+       AC_CHECK_FUNC(OCIEnvCreate, [with_oracle="yes"], [with_oracle="no (Symbol 'OCIEnvCreate' not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_oracle" = "xyes"
+then
+       BUILD_WITH_ORACLE_CFLAGS="$with_oracle_cppflags"
+       BUILD_WITH_ORACLE_LIBS="$with_oracle_libs"
+       AC_SUBST(BUILD_WITH_ORACLE_CFLAGS)
+       AC_SUBST(BUILD_WITH_ORACLE_LIBS)
+fi
+# }}}
+
+# --with-libowcapi {{{
 with_libowcapi_cppflags=""
 with_libowcapi_libs="-lowcapi"
 AC_ARG_WITH(libowcapi, [AS_HELP_STRING([--with-libowcapi@<:@=PREFIX@:>@], [Path to libowcapi.])],
@@ -1547,8 +1680,9 @@ then
        AC_SUBST(BUILD_WITH_LIBOWCAPI_CPPFLAGS)
        AC_SUBST(BUILD_WITH_LIBOWCAPI_LIBS)
 fi
+# }}}
 
-
+# --with-libpcap {{{
 AC_ARG_WITH(libpcap, [AS_HELP_STRING([--with-libpcap@<:@=PREFIX@:>@], [Path to libpcap.])],
 [
        if test "x$withval" != "xno" && test "x$withval" != "xyes"
@@ -1586,45 +1720,81 @@ fi
 AC_DEFINE_UNQUOTED(COLLECT_LIBPCAP, [$collect_libpcap],
        [Wether or not to use the pcap library])
 AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes")
+# }}}
+
+# --with-libpcre {{{
+with_pcre_config="pcre-config"
+with_pcre_cflags=""
+with_pcre_libs=""
+AC_ARG_WITH(libpcre, [AS_HELP_STRING([--with-libpcre@<:@=PREFIX@:>@],
+       [Path to libpcre.])],
+       [
+               if test "x$withval" = "xno"
+               then
+                       with_libpcre="no"
+               else if test "x$withval" = "xyes"
+               then
+                       with_libpcre="yes"
+               else
+                       if test -f "$withval" && test -x "$withval"
+                       then
+                               with_pcre_config="$withval"
+                       else if test -x "$withval/bin/pcre-config"
+                       then
+                               with_pcre_config="$withval/bin/pcre-config"
+                       fi; fi
+                       with_libpcre="yes"
+               fi; fi
+       ],
+       [
+               with_libpcre="yes"
+       ])
 
-AC_ARG_WITH(libesmtp, [AS_HELP_STRING([--with-libesmtp@<:@=PREFIX@:>@], [Path to libesmtp.])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+if test "x$with_libpcre" = "xyes"
+then
+       with_pcre_cflags=`$with_pcre_config --cflags 2>/dev/null`
+       pcre_config_status=$?
+
+       if test $pcre_config_status -ne 0
        then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include -D_THREAD_SAFE"
-               with_libesmtp="yes"
+               with_libpcre="no ($with_pcre_config failed)"
        else
-               with_libesmtp="$withval"
+               SAVE_CPPFLAGS="$CPPFLAGS"
+               CPPFLAGS="$CPPFLAGS $with_pcre_cflags"
+
+               AC_CHECK_HEADERS(pcre.h, [], [with_libpcre="no (pcre.h not found)"], [])
+
+               CPPFLAGS="$SAVE_CPPFLAGS"
        fi
-],
-[
-       with_libesmtp="yes"
-])
-if test "x$with_libesmtp" = "xyes"
-then
-       AC_CHECK_LIB(esmtp, smtp_create_session,
-       [
-               AC_DEFINE(HAVE_LIBESMTP, 1, [Define to 1 if you have the esmtp library (-lesmtp).])
-       ], [with_libesmtp="no (libesmtp not found)"])
 fi
-if test "x$with_libesmtp" = "xyes"
+
+if test "x$with_libpcre" = "xyes"
 then
-       AC_CHECK_HEADERS(libesmtp.h,
-       [
-               AC_DEFINE(HAVE_LIBESMTP_H, 1, [Define to 1 if you have the <libesmtp.h> header file.])
-       ], [with_libesmtp="no (libesmtp.h not found)"])
+       with_pcre_libs=`$with_pcre_config --libs 2>/dev/null`
+       pcre_config_status=$?
+
+       if test $pcre_config_status -ne 0
+       then
+               with_libpcre="no ($with_pcre_config failed)"
+       else
+               AC_CHECK_LIB(pcre, pcre_compile,
+                       [with_libpcre="yes"],
+                       [with_libpcre="no (symbol 'pcre_compile' not found)"],
+                       [$with_pcre_libs])
+       fi
 fi
-if test "x$with_libesmtp" = "xyes"
+
+if test "x$with_libpcre" = "xyes"
 then
-       collect_libesmtp=1
-else
-       collect_libesmtp=0
+       BUILD_WITH_LIBPCRE_CFLAGS="$with_pcre_cflags"
+       BUILD_WITH_LIBPCRE_LIBS="$with_pcre_libs"
+       AC_SUBST(BUILD_WITH_LIBPCRE_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBPCRE_LIBS)
 fi
-AC_DEFINE_UNQUOTED(COLLECT_LIBESMTP, [$collect_libesmtp],
-       [Wether or not to use the esmtp library])
-AM_CONDITIONAL(BUILD_WITH_LIBESMTP, test "x$with_libesmtp" = "xyes")
+AM_CONDITIONAL(BUILD_WITH_LIBPCRE, test "x$with_libpcre" = "xyes")
+# }}}
 
+# --with-libperl {{{
 perl_interpreter="perl"
 AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
 [
@@ -1740,551 +1910,526 @@ then
        CFLAGS=$SAVE_CFLAGS
        LDFLAGS=$SAVE_LDFLAGS
 fi
+# }}}
 
-with_own_libiptc="no"
-AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])],
+# --with-libpq {{{
+with_pg_config="pg_config"
+with_libpq_includedir=""
+with_libpq_libdir=""
+with_libpq_cppflags=""
+with_libpq_ldflags=""
+AC_ARG_WITH(libpq, [AS_HELP_STRING([--with-libpq@<:@=PREFIX@:>@],
+       [Path to libpq.])],
 [
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       if test "x$withval" = "xno"
        then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include"
-               with_libiptc="yes"
+               with_libpq="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libpq="yes"
        else
-               with_libiptc="$withval"
-       fi
+               if test -f "$withval" && test -x "$withval";
+               then
+                       with_pg_config="$withval"
+               else if test -x "$withval/bin/pg_config"
+               then
+                       with_pg_config="$withval/bin/pg_config"
+               fi; fi
+               with_libpq="yes"
+       fi; fi
 ],
 [
-       if test "x$ac_system" = "xLinux"
+       with_libpq="yes"
+])
+if test "x$with_libpq" = "xyes"
+then
+       with_libpq_includedir=`$with_pg_config --includedir 2> /dev/null`
+       pg_config_status=$?
+
+       if test $pg_config_status -eq 0
        then
-               with_libiptc="yes"
+               if test -n "$with_libpq_includedir"; then
+                       for dir in $with_libpq_includedir; do
+                               with_libpq_cppflags="$with_libpq_cppflags -I$dir"
+                       done
+               fi
        else
-               with_libiptc="no (Linux only)"
+               AC_MSG_WARN([$with_pg_config returned with status $pg_config_status])
        fi
-])
-if test "x$with_libiptc" = "xyes"
-then
-       AC_CHECK_LIB(iptc, iptc_init,
-       [
-               AC_DEFINE(HAVE_LIBIPTC, 1, [Define to 1 if you have the iptc library (-liptc).])
-       ],
-       [
-               with_libiptc="yes"
-               with_own_libiptc="yes"
-       ])
-fi
-if test "x$with_libiptc" = "xyes" -a "x$with_own_libiptc" != "xyes"
-then
-       AC_CHECK_HEADERS(libiptc/libiptc.h,
-       [
-               AC_DEFINE(HAVE_LIBIPTC_LIBIPTC_H, 1, [Define to 1 if you have the <libiptc/libiptc.h> header file.])
-       ],
-       [
-               with_libiptc="yes"
-               with_own_libiptc="yes"
-       ])
+
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libpq_cppflags"
+
+       AC_CHECK_HEADERS(libpq-fe.h, [],
+               [with_libpq="no (libpq-fe.h not found)"], [])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
 fi
-if test "x$with_libiptc" = "xyes"
+if test "x$with_libpq" = "xyes"
 then
-       SAVE_CFLAGS=$CFLAGS
-       CFLAGS="$CFLAGS $KERNEL_CFLAGS"
+       with_libpq_libdir=`$with_pg_config --libdir 2> /dev/null`
+       pg_config_status=$?
 
-       AC_CHECK_HEADERS(linux/netfilter_ipv4/ip_tables.h linux/netfilter_ipv6/ip6_tables.h, [],
-       [
-               with_libiptc="no (Linux iptables headers not found - check KERNEL_DIR)"
-               with_own_libiptc="no"
-       ],
-       [
-#include "$srcdir/src/libiptc/ipt_kernel_headers.h"
-       ])
+       if test $pg_config_status -eq 0
+       then
+               if test -n "$with_libpq_libdir"; then
+                       for dir in $with_libpq_libdir; do
+                               with_libpq_ldflags="$with_libpq_ldflags -L$dir"
+                       done
+               fi
+       else
+               AC_MSG_WARN([$with_pg_config returned with status $pg_config_status])
+       fi
 
-       CFLAGS=$SAVE_CFLAGS
+       SAVE_LDFLAGS="$LDFLAGS"
+       LDFLAGS="$LDFLAGS $with_libpq_ldflags"
+
+       AC_CHECK_LIB(pq, PQconnectdb,
+               [with_libpq="yes"],
+               [with_libpq="no (symbol 'PQconnectdb' not found)"])
+
+       LDFLAGS="$SAVE_LDFLAGS"
 fi
-AM_CONDITIONAL(BUILD_WITH_LIBIPTC, test "x$with_libiptc" = "xyes")
-AM_CONDITIONAL(BUILD_WITH_OWN_LIBIPTC, test "x$with_own_libiptc" = "xyes")
-if test "x$with_own_libiptc" = "xyes"
+if test "x$with_libpq" = "xyes"
 then
-       AC_DEFINE(OWN_LIBIPTC, 1, [Define to 1 if we use the shipped iptc library.])
+       BUILD_WITH_LIBPQ_CPPFLAGS="$with_libpq_cppflags"
+       BUILD_WITH_LIBPQ_LDFLAGS="$with_libpq_ldflags"
+       AC_SUBST(BUILD_WITH_LIBPQ_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBPQ_LDFLAGS)
 fi
+AM_CONDITIONAL(BUILD_WITH_LIBPQ, test "x$with_libpq" = "xyes")
+# }}}
 
-with_snmp_config="net-snmp-config"
-with_snmp_cflags=""
-with_snmp_libs=""
-AC_ARG_WITH(libnetsnmp, [AS_HELP_STRING([--with-libnetsnmp@<:@=PREFIX@:>@], [Path to the Net-SNMPD library.])],
-[
-       if test "x$withval" = "xno"
-       then
-               with_libnetsnmp="no"
-       else if test "x$withval" = "xyes"
+# --with-libpthread {{{
+AC_ARG_WITH(libpthread, [AS_HELP_STRING([--with-libpthread=@<:@=PREFIX@:>@], [Path to libpthread.])],
+[      if test "x$withval" != "xno" \
+               && test "x$withval" != "xyes"
        then
-               with_libnetsnmp="yes"
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_libpthread="yes"
        else
-               if test -x "$withval"
+               if test "x$withval" = "xno"
                then
-                       with_snmp_config="$withval"
-                       with_libnetsnmp="yes"
-               else
-                       with_snmp_config="$withval/bin/net-snmp-config"
-                       with_libnetsnmp="yes"
+                       with_libpthread="no (disabled)"
                fi
-       fi; fi
-],
-[with_libnetsnmp="yes"])
-if test "x$with_libnetsnmp" = "xyes"
-then
-       with_snmp_cflags=`$with_snmp_config --cflags 2>/dev/null`
-       snmp_config_status=$?
-
-       if test $snmp_config_status -ne 0
-       then
-               with_libnetsnmp="no ($with_snmp_config failed)"
-       else
-               SAVE_CPPFLAGS="$CPPFLAGS"
-               CPPFLAGS="$CPPFLAGS $with_snmp_cflags"
-               
-               AC_CHECK_HEADERS(net-snmp/net-snmp-config.h, [], [with_libnetsnmp="no (net-snmp/net-snmp-config.h not found)"])
-
-               CPPFLAGS="$SAVE_CPPFLAGS"
        fi
+], [with_libpthread="yes"])
+if test "x$with_libpthread" = "xyes"
+then
+       AC_CHECK_LIB(pthread, pthread_create, [with_libpthread="yes"], [with_libpthread="no (libpthread not found)"], [])
 fi
-if test "x$with_libnetsnmp" = "xyes"
+if test "x$with_libpthread" = "xyes"
 then
-       with_snmp_libs=`$with_snmp_config --libs 2>/dev/null`
-       snmp_config_status=$?
+       AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"])
+fi
+if test "x$with_libpthread" = "xyes"
+then
+       collect_pthread=1
+else
+       collect_pthread=0
+fi
+AC_DEFINE_UNQUOTED(HAVE_LIBPTHREAD, [$collect_pthread],
+       [Wether or not to use pthread (POSIX threads) library])
+AM_CONDITIONAL(BUILD_WITH_LIBPTHREAD, test "x$with_libpthread" = "xyes")
+# }}}
 
-       if test $snmp_config_status -ne 0
+# --with-librrd {{{
+# AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given])
+librrd_cflags=""
+librrd_ldflags=""
+librrd_threadsafe="yes"
+librrd_rrdc_update="no"
+AC_ARG_WITH(librrd, [AS_HELP_STRING([--with-librrd@<:@=PREFIX@:>@], [Path to rrdtool.])],
+[      if test "x$withval" != "xno" && test "x$withval" != "xyes"
        then
-               with_libnetsnmp="no ($with_snmp_config failed)"
+               librrd_cflags="-I$withval/include"
+               librrd_ldflags="-L$withval/lib"
+               with_librrd="yes"
        else
-               AC_CHECK_LIB(netsnmp, init_snmp,
-               [with_libnetsnmp="yes"],
-               [with_libnetsnmp="no (libnetsnmp not found)"],
-               [$with_snmp_libs])
+               with_librrd="$withval"
        fi
-fi
-if test "x$with_libnetsnmp" = "xyes"
+], [with_librrd="yes"])
+if test "x$with_librrd" = "xyes"
 then
-       BUILD_WITH_LIBSNMP_CFLAGS="$with_snmp_cflags"
-       BUILD_WITH_LIBSNMP_LIBS="$with_snmp_libs"
-       AC_SUBST(BUILD_WITH_LIBSNMP_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBSNMP_LIBS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBNETSNMP, test "x$with_libnetsnmp" = "xyes")
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
 
-PKG_CHECK_MODULES([LIBNOTIFY], [libnotify],
-               [with_libnotify="yes"],
-               [with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"])
+       CPPFLAGS="$CPPFLAGS $librrd_cflags"
+       LDFLAGS="$LDFLAGS $librrd_ldflags"
 
-with_libupsclient="no (pkg-config isn't available)"
-with_libupsclient_cflags=""
-with_libupsclient_libs=""
-if test "x$PKG_CONFIG" != "x"
+       AC_CHECK_HEADERS(rrd.h,, [with_librrd="no (rrd.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_librrd" = "xyes"
 then
-       pkg-config --exists 'libupsclient' 2>/dev/null
-       if test "$?" = "0"
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CPPFLAGS="$CPPFLAGS $librrd_cflags"
+       LDFLAGS="$LDFLAGS $librrd_ldflags"
+
+       AC_CHECK_LIB(rrd_th, rrd_update_r,
+       [with_librrd="yes"
+        librrd_ldflags="$librrd_ldflags -lrrd_th -lm"
+       ],
+       [librrd_threadsafe="no"
+        AC_CHECK_LIB(rrd, rrd_update,
+        [with_librrd="yes"
+         librrd_ldflags="$librrd_ldflags -lrrd -lm"
+        ],
+        [with_librrd="no (symbol 'rrd_update' not found)"],
+        [-lm])
+       ],
+       [-lm])
+
+       if test "x$librrd_threadsafe" = "xyes"
        then
-               with_libupsclient="yes"
+               AC_CHECK_LIB(rrd_th, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"])
        else
-               with_libupsclient="no (pkg-config doesn't know library)"
+               AC_CHECK_LIB(rrd, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"])
        fi
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
 fi
-if test "x$with_libupsclient" = "xyes"
+if test "x$with_librrd" = "xyes"
 then
-       with_libupsclient_cflags="`pkg-config --cflags 'libupsclient'`"
-       if test $? -ne 0
+       BUILD_WITH_LIBRRD_CFLAGS="$librrd_cflags"
+       BUILD_WITH_LIBRRD_LDFLAGS="$librrd_ldflags"
+       AC_SUBST(BUILD_WITH_LIBRRD_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBRRD_LDFLAGS)
+fi
+if test "x$librrd_threadsafe" = "xyes"
+then
+       AC_DEFINE(HAVE_THREADSAFE_LIBRRD, 1, [Define to 1 if you have the threadsafe rrd library (-lrrd_th).])
+fi
+# }}}
+
+# --with-libsensors {{{
+with_sensors_cflags=""
+with_sensors_ldflags=""
+AC_ARG_WITH(libsensors, [AS_HELP_STRING([--with-libsensors@<:@=PREFIX@:>@], [Path to lm_sensors.])],
+[
+       if test "x$withval" = "xno"
        then
-               with_libupsclient="no"
+               with_libsensors="no"
+       else
+               with_libsensors="yes"
+               if test "x$withval" != "xyes"
+               then
+                       with_sensors_cflags="-I$withval/include"
+                       with_sensors_ldflags="-L$withval/lib"
+                       with_libsensors="yes"
+               fi
        fi
-       with_libupsclient_libs="`pkg-config --libs 'libupsclient'`"
-       if test $? -ne 0
+],
+[
+       if test "x$ac_system" = "xLinux"
        then
-               with_libupsclient="no"
+               with_libsensors="yes"
+       else
+               with_libsensors="no (Linux only library)"
        fi
-fi
-if test "x$with_libupsclient" = "xyes"
+])
+if test "x$with_libsensors" = "xyes"
 then
        SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
+       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
 
-       AC_CHECK_HEADERS(upsclient.h, [], [with_libupsclient="no (upsclient.h not found)"])
+#      AC_CHECK_HEADERS(sensors/sensors.h,
+#      [
+#              AC_DEFINE(HAVE_SENSORS_SENSORS_H, 1, [Define to 1 if you have the <sensors/sensors.h> header file.])
+#      ],
+#      [with_libsensors="no (sensors/sensors.h not found)"])
+       AC_CHECK_HEADERS(sensors/sensors.h, [], [with_libsensors="no (sensors/sensors.h not found)"])
 
        CPPFLAGS="$SAVE_CPPFLAGS"
 fi
-if test "x$with_libupsclient" = "xyes"
+if test "x$with_libsensors" = "xyes"
 then
        SAVE_CPPFLAGS="$CPPFLAGS"
        SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
+       LDFLAGS="$LDFLAGS $with_sensors_ldflags"
 
-       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
-       LDFLAGS="$LDFLAGS $with_libupsclient_libs"
-
-       AC_CHECK_LIB(upsclient, upscli_connect,
-                    [with_libupsclient="yes"],
-                    [with_libupsclient="no (symbol upscli_connect not found)"])
+       AC_CHECK_LIB(sensors, sensors_init,
+       [
+               AC_DEFINE(HAVE_LIBSENSORS, 1, [Define to 1 if you have the sensors library (-lsensors).])
+       ],
+       [with_libsensors="no (libsensors not found)"])
 
        CPPFLAGS="$SAVE_CPPFLAGS"
        LDFLAGS="$SAVE_LDFLAGS"
 fi
-if test "x$with_libupsclient" = "xyes"
+if test "x$with_libsensors" = "xyes"
 then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
+       BUILD_WITH_LIBSENSORS_CFLAGS="$with_sensors_cflags"
+       BUILD_WITH_LIBSENSORS_LDFLAGS="$with_sensors_ldflags"
+       AC_SUBST(BUILD_WITH_LIBSENSORS_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBSENSORS_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_libsensors" = "xyes")
+# }}}
 
-       AC_CHECK_TYPES([UPSCONN_t, UPSCONN], [], [],
-[#include <stdlib.h>
-#include <stdio.h>
-#include <upsclient.h>])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libupsclient" = "xyes"
-then
-       BUILD_WITH_LIBUPSCLIENT_CFLAGS="$with_libupsclient_cflags"
-       BUILD_WITH_LIBUPSCLIENT_LIBS="$with_libupsclient_libs"
-       AC_SUBST(BUILD_WITH_LIBUPSCLIENT_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBUPSCLIENT_LIBS)
-fi
-
-### BEGIN of check for libxmms ###
-with_xmms_config="xmms-config"
-with_xmms_cflags=""
-with_xmms_libs=""
-AC_ARG_WITH(libxmms, [AS_HELP_STRING([--with-libxmms@<:@=PREFIX@:>@], [Path to libxmms.])],
+# --with-libstatgrab {{{
+with_libstatgrab_cflags=""
+with_libstatgrab_ldflags=""
+AC_ARG_WITH(libstatgrab, [AS_HELP_STRING([--with-libstatgrab@<:@=PREFIX@:>@], [Path to libstatgrab.])],
 [
        if test "x$withval" != "xno" \
                && test "x$withval" != "xyes"
        then
-               if test -f "$withval" && test -x "$withval";
-               then
-                       with_xmms_config="$withval"
-               else if test -x "$withval/bin/xmms-config"
-               then
-                       with_xmms_config="$withval/bin/xmms-config"
-               fi; fi
-               with_libxmms="yes"
-       else if test "x$withval" = "xno"
-       then
-               with_libxmms="no"
+               with_libstatgrab_cflags="-I$withval/include"
+               with_libstatgrab_ldflags="-L$withval/lib"
+               with_libstatgrab="yes"
        else
-               with_libxmms="yes"
-       fi; fi
+               with_libstatgrab="$withval"
+       fi
 ],
 [
-       with_libxmms="yes"
-])
-if test "x$with_libxmms" = "xyes"
-then
-       with_xmms_cflags=`$with_xmms_config --cflags 2>/dev/null`
-       xmms_config_status=$?
-
-       if test $xmms_config_status -ne 0
-       then
-               with_libxmms="no"
-       fi
-fi
-if test "x$with_libxmms" = "xyes"
-then
-       with_xmms_libs=`$with_xmms_config --libs 2>/dev/null`
-       xmms_config_status=$?
-
-       if test $xmms_config_status -ne 0
+       if test "x$ac_system" = "xunknown"
        then
-               with_libxmms="no"
+               with_libstatgrab="yes"
+       else
+               with_libstatgrab="no"
        fi
-fi
-if test "x$with_libxmms" = "xyes"
-then
-       AC_CHECK_LIB(xmms, xmms_remote_get_info,
-       [
-               BUILD_WITH_LIBXMMS_CFLAGS="$with_xmms_cflags"
-               BUILD_WITH_LIBXMMS_LIBS="$with_xmms_libs"
-               AC_SUBST(BUILD_WITH_LIBXMMS_CFLAGS)
-               AC_SUBST(BUILD_WITH_LIBXMMS_LIBS)
-       ],
-       [
-               with_libxmms="no"
-       ],
-       [$with_xmms_libs])
-fi
-with_libxmms_numeric=0
-if test "x$with_libxmms" = "xyes"
-then
-       with_libxmms_numeric=1
-fi
-AC_DEFINE_UNQUOTED(HAVE_LIBXMMS, [$with_libxmms_numeric], [Define to 1 if you have the 'xmms' library (-lxmms).])
-AM_CONDITIONAL(BUILD_WITH_LIBXMMS, test "x$with_libxmms" = "xyes")
-### END of check for libxmms ###
-
-with_libnetlink_cflags=""
-with_libnetlink_libs="-lnetlink"
-AC_ARG_WITH(libnetlink, [AS_HELP_STRING([--with-libnetlink@<:@=PREFIX@:>@], [Path to libnetlink.])],
-[
- echo "libnetlink: withval = $withval"
- if test "x$withval" = "xyes"
- then
-        with_libnetlink="yes"
- else if test "x$withval" = "xno"
- then
-        with_libnetlink="no"
- else
-        if test -d "$withval/include"
-        then
-                with_libnetlink_cflags="-I$withval/include"
-                with_libnetlink_libs="-L$withval/lib -lnetlink"
-                with_libnetlink="yes"
-        else
-                AC_MSG_ERROR("no such directory: $withval/include")
-        fi
- fi; fi
-],
-[
- if test "x$ac_system" = "xLinux"
- then
-        with_libnetlink="yes"
- else
-        with_libnetlink="no (Linux only library)"
- fi
 ])
-if test "x$with_libnetlink" = "xyes"
-then
-       SAVE_CFLAGS=$CFLAGS
-       CFLAGS="$CFLAGS $with_libnetlink_cflags"
-
-       with_libnetlink="no (libnetlink.h not found)"
-
-       AC_CHECK_HEADERS(libnetlink.h iproute/libnetlink.h linux/libnetlink.h,
-       [
-        with_libnetlink="yes"
-        break
-       ], [],
-[#include <stdio.h>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>])
-       AC_CHECK_HEADERS(linux/gen_stats.h linux/pkt_sched.h, [], [],
-[#include <stdio.h>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>])
-
-       AC_COMPILE_IFELSE(
-[#include <stdio.h>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
-int main (void)
-{
-       int retval = TCA_STATS2;
-       return (retval);
-}],
-       [AC_DEFINE([HAVE_TCA_STATS2], 1, [True if the enum-member TCA_STATS2 exists])]
-       []);
-
-       AC_COMPILE_IFELSE(
-[#include <stdio.h>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
-int main (void)
-{
-       int retval = TCA_STATS;
-       return (retval);
-}],
-       [AC_DEFINE([HAVE_TCA_STATS], 1, [True if the enum-member TCA_STATS exists])]
-       []);
-
-       CFLAGS="$SAVE_CFLAGS"
-fi
-if test "x$with_libnetlink" = "xyes"
-then
-       AC_CHECK_LIB(netlink, rtnl_open,
-                    [with_libnetlink="yes"],
-                    [with_libnetlink="no (symbol 'rtnl_open' not found)"],
-                    [$with_libnetlink_libs])
-fi
-if test "x$with_libnetlink" = "xyes"
-then
-       BUILD_WITH_LIBNETLINK_CFLAGS="$with_libnetlink_cflags"
-       BUILD_WITH_LIBNETLINK_LIBS="$with_libnetlink_libs"
-       AC_SUBST(BUILD_WITH_LIBNETLINK_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBNETLINK_LIBS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBNETLINK, test "x$with_libnetlink" = "xyes")
-
-with_libopenipmipthread="yes"
-with_libopenipmipthread_cflags=""
-with_libopenipmipthread_libs=""
-
-AC_MSG_CHECKING([for pkg-config])
-temp_result="no"
-if test "x$PKG_CONFIG" = "x"
+with_libstatgrab_pkg_config="yes"
+if test "x$with_libstatgrab" = "xyes" \
+  && test "x$PKG_CONFIG" != "x"
 then
-       with_libopenipmipthread="no"
-       temp_result="no"
+  AC_MSG_CHECKING([pkg-config for libstatgrab])
+  temp_result="found"
+  $PKG_CONFIG --exists libstatgrab 2>/dev/null
+  if test "$?" != "0"
+  then
+    with_libstatgrab_pkg_config="no"
+    temp_result="not found"
+  fi
+  AC_MSG_RESULT([$temp_result])
 else
-       temp_result="$PKG_CONFIG"
+  AC_MSG_NOTICE([pkg-config not available, trying to guess flags for the statgrab library.])
+  with_libstatgrab_pkg_config="no"
+  with_libstatgrab_ldflags="$with_libstatgrab_ldflags -lstatgrab"
 fi
-AC_MSG_RESULT([$temp_result])
 
-if test "x$with_libopenipmipthread" = "xyes"
+if test "x$with_libstatgrab" = "xyes" \
+  && test "x$with_libstatgrab_pkg_config" = "xyes" \
+  && test "x$with_libstatgrab_cflags" = "x"
 then
-       AC_MSG_CHECKING([for libOpenIPMIpthread])
-       $PKG_CONFIG --exists OpenIPMIpthread 2>/dev/null
-       if test "$?" != "0"
-       then
-               with_libopenipmipthread="no ($PKG_CONFIG doesn't know OpenIPMIpthread)"
-       fi
-       AC_MSG_RESULT([$with_libopenipmipthread])
+  AC_MSG_CHECKING([for libstatgrab CFLAGS])
+  temp_result="`$PKG_CONFIG --cflags libstatgrab`"
+  if test "$?" = "0"
+  then
+    with_libstatgrab_cflags="$temp_result"
+  else
+    with_libstatgrab="no ($PKG_CONFIG --cflags libstatgrab failed)"
+    temp_result="$PKG_CONFIG --cflags libstatgrab failed"
+  fi
+  AC_MSG_RESULT([$temp_result])
 fi
 
-if test "x$with_libopenipmipthread" = "xyes"
+if test "x$with_libstatgrab" = "xyes" \
+  && test "x$with_libstatgrab_pkg_config" = "xyes" \
+  && test "x$with_libstatgrab_ldflags" = "x"
 then
-       AC_MSG_CHECKING([for libOpenIPMIpthread CFLAGS])
-       temp_result="`$PKG_CONFIG --cflags OpenIPMIpthread`"
-       if test "$?" = "0"
-       then
-               with_libopenipmipthread_cflags="$temp_result"
-       else
-               with_libopenipmipthread="no ($PKG_CONFIG --cflags OpenIPMIpthread failed)"
-               temp_result="$PKG_CONFIG --cflags OpenIPMIpthread failed"
-       fi
-       AC_MSG_RESULT([$temp_result])
+  AC_MSG_CHECKING([for libstatgrab LDFLAGS])
+  temp_result="`$PKG_CONFIG --libs libstatgrab`"
+  if test "$?" = "0"
+  then
+    with_libstatgrab_ldflags="$temp_result"
+  else
+    with_libstatgrab="no ($PKG_CONFIG --libs libstatgrab failed)"
+    temp_result="$PKG_CONFIG --libs libstatgrab failed"
+  fi
+  AC_MSG_RESULT([$temp_result])
 fi
 
-if test "x$with_libopenipmipthread" = "xyes"
+if test "x$with_libstatgrab" = "xyes"
 then
-       AC_MSG_CHECKING([for libOpenIPMIpthread LDFLAGS])
-       temp_result="`$PKG_CONFIG --libs OpenIPMIpthread`"
-       if test "$?" = "0"
-       then
-               with_libopenipmipthread_ldflags="$temp_result"
-       else
-               with_libopenipmipthread="no ($PKG_CONFIG --libs OpenIPMIpthread failed)"
-               temp_result="$PKG_CONFIG --libs OpenIPMIpthread failed"
-       fi
-       AC_MSG_RESULT([$temp_result])
+  SAVE_CPPFLAGS="$CPPFLAGS"
+  CPPFLAGS="$CPPFLAGS $with_libstatgrab_cflags"
+
+  AC_CHECK_HEADERS(statgrab.h,
+                  [with_libstatgrab="yes"],
+                  [with_libstatgrab="no (statgrab.h not found)"])
+
+  CPPFLAGS="$SAVE_CPPFLAGS"
 fi
 
-if test "x$with_libopenipmipthread" = "xyes"
+if test "x$with_libstatgrab" = "xyes"
 then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libopenipmipthread_cflags"
+  SAVE_CFLAGS="$CFLAGS"
+  SAVE_LDFLAGS="$LDFLAGS"
 
-       AC_CHECK_HEADERS(OpenIPMI/ipmi_smi.h,
-                        [with_libopenipmipthread="yes"],
-                        [with_libopenipmipthread="no (OpenIPMI/ipmi_smi.h not found)"],
-[#include <OpenIPMI/ipmiif.h>
-#include <OpenIPMI/ipmi_err.h>
-#include <OpenIPMI/ipmi_posix.h>
-#include <OpenIPMI/ipmi_conn.h>
-])
+  CFLAGS="$CFLAGS $with_libstatgrab_cflags"
+  LDFLAGS="$LDFLAGS $with_libstatgrab_ldflags"
 
-       CPPFLAGS="$SAVE_CPPFLAGS"
+  AC_CHECK_LIB(statgrab, sg_init,
+              [with_libstatgrab="yes"],
+              [with_libstatgrab="no (symbol sg_init not found)"])
+
+  CFLAGS="$SAVE_CFLAGS"
+  LDFLAGS="$SAVE_LDFLAGS"
 fi
 
-if test "x$with_libopenipmipthread" = "xyes"
+AM_CONDITIONAL(BUILD_WITH_LIBSTATGRAB, test "x$with_libstatgrab" = "xyes")
+if test "x$with_libstatgrab" = "xyes"
 then
-       BUILD_WITH_OPENIPMI_CFLAGS="$with_libopenipmipthread_cflags"
-       BUILD_WITH_OPENIPMI_LIBS="$with_libopenipmipthread_ldflags"
-       AC_SUBST(BUILD_WITH_OPENIPMI_CFLAGS)
-       AC_SUBST(BUILD_WITH_OPENIPMI_LIBS)
+  AC_DEFINE(HAVE_LIBSTATGRAB, 1, [Define to 1 if you have the 'statgrab' library (-lstatgrab)])
+  BUILD_WITH_LIBSTATGRAB_CFLAGS="$with_libstatgrab_cflags"
+  BUILD_WITH_LIBSTATGRAB_LDFLAGS="$with_libstatgrab_ldflags"
+  AC_SUBST(BUILD_WITH_LIBSTATGRAB_CFLAGS)
+  AC_SUBST(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
 fi
+# }}}
 
-dnl Check for libpq.
-with_pg_config="pg_config"
-with_libpq_includedir=""
-with_libpq_libdir=""
-with_libpq_cppflags=""
-with_libpq_ldflags=""
-AC_ARG_WITH(libpq, [AS_HELP_STRING([--with-libpq@<:@=PREFIX@:>@],
-       [Path to libpq.])],
+# --with-libxmms {{{
+with_xmms_config="xmms-config"
+with_xmms_cflags=""
+with_xmms_libs=""
+AC_ARG_WITH(libxmms, [AS_HELP_STRING([--with-libxmms@<:@=PREFIX@:>@], [Path to libxmms.])],
 [
-       if test "x$withval" = "xno"
-       then
-               with_libpq="no"
-       else if test "x$withval" = "xyes"
+       if test "x$withval" != "xno" \
+               && test "x$withval" != "xyes"
        then
-               with_libpq="yes"
-       else
                if test -f "$withval" && test -x "$withval";
                then
-                       with_pg_config="$withval"
-               else if test -x "$withval/bin/pg_config"
+                       with_xmms_config="$withval"
+               else if test -x "$withval/bin/xmms-config"
                then
-                       with_pg_config="$withval/bin/pg_config"
+                       with_xmms_config="$withval/bin/xmms-config"
                fi; fi
-               with_libpq="yes"
+               with_libxmms="yes"
+       else if test "x$withval" = "xno"
+       then
+               with_libxmms="no"
+       else
+               with_libxmms="yes"
        fi; fi
 ],
 [
-       with_libpq="yes"
+       with_libxmms="yes"
 ])
-if test "x$with_libpq" = "xyes"
+if test "x$with_libxmms" = "xyes"
 then
-       with_libpq_includedir=`$with_pg_config --includedir 2> /dev/null`
-       pg_config_status=$?
+       with_xmms_cflags=`$with_xmms_config --cflags 2>/dev/null`
+       xmms_config_status=$?
 
-       if test $pg_config_status -eq 0
+       if test $xmms_config_status -ne 0
        then
-               if test -n "$with_libpq_includedir"; then
-                       for dir in $with_libpq_includedir; do
-                               with_libpq_cppflags="$with_libpq_cppflags -I$dir"
-                       done
-               fi
-       else
-               AC_MSG_WARN([$with_pg_config returned with status $pg_config_status])
+               with_libxmms="no"
        fi
+fi
+if test "x$with_libxmms" = "xyes"
+then
+       with_xmms_libs=`$with_xmms_config --libs 2>/dev/null`
+       xmms_config_status=$?
 
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libpq_cppflags"
-
-       AC_CHECK_HEADERS(libpq-fe.h, [],
-               [with_libpq="no (libpq-fe.h not found)"], [])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
+       if test $xmms_config_status -ne 0
+       then
+               with_libxmms="no"
+       fi
 fi
-if test "x$with_libpq" = "xyes"
+if test "x$with_libxmms" = "xyes"
 then
-       with_libpq_libdir=`$with_pg_config --libdir 2> /dev/null`
-       pg_config_status=$?
+       AC_CHECK_LIB(xmms, xmms_remote_get_info,
+       [
+               BUILD_WITH_LIBXMMS_CFLAGS="$with_xmms_cflags"
+               BUILD_WITH_LIBXMMS_LIBS="$with_xmms_libs"
+               AC_SUBST(BUILD_WITH_LIBXMMS_CFLAGS)
+               AC_SUBST(BUILD_WITH_LIBXMMS_LIBS)
+       ],
+       [
+               with_libxmms="no"
+       ],
+       [$with_xmms_libs])
+fi
+with_libxmms_numeric=0
+if test "x$with_libxmms" = "xyes"
+then
+       with_libxmms_numeric=1
+fi
+AC_DEFINE_UNQUOTED(HAVE_LIBXMMS, [$with_libxmms_numeric], [Define to 1 if you have the 'xmms' library (-lxmms).])
+AM_CONDITIONAL(BUILD_WITH_LIBXMMS, test "x$with_libxmms" = "xyes")
+# }}}
 
-       if test $pg_config_status -eq 0
+# pkg-config --exists 'libupsclient' {{{
+with_libupsclient="no (pkg-config isn't available)"
+with_libupsclient_cflags=""
+with_libupsclient_libs=""
+if test "x$PKG_CONFIG" != "x"
+then
+       pkg-config --exists 'libupsclient' 2>/dev/null
+       if test "$?" = "0"
        then
-               if test -n "$with_libpq_libdir"; then
-                       for dir in $with_libpq_libdir; do
-                               with_libpq_ldflags="$with_libpq_ldflags -L$dir"
-                       done
-               fi
+               with_libupsclient="yes"
        else
-               AC_MSG_WARN([$with_pg_config returned with status $pg_config_status])
+               with_libupsclient="no (pkg-config doesn't know library)"
+       fi
+fi
+if test "x$with_libupsclient" = "xyes"
+then
+       with_libupsclient_cflags="`pkg-config --cflags 'libupsclient'`"
+       if test $? -ne 0
+       then
+               with_libupsclient="no"
+       fi
+       with_libupsclient_libs="`pkg-config --libs 'libupsclient'`"
+       if test $? -ne 0
+       then
+               with_libupsclient="no"
        fi
+fi
+if test "x$with_libupsclient" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
 
+       AC_CHECK_HEADERS(upsclient.h, [], [with_libupsclient="no (upsclient.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libupsclient" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
        SAVE_LDFLAGS="$LDFLAGS"
-       LDFLAGS="$LDFLAGS $with_libpq_ldflags"
 
-       AC_CHECK_LIB(pq, PQconnectdb,
-               [with_libpq="yes"],
-               [with_libpq="no (symbol 'PQconnectdb' not found)"])
+       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
+       LDFLAGS="$LDFLAGS $with_libupsclient_libs"
+
+       AC_CHECK_LIB(upsclient, upscli_connect,
+                    [with_libupsclient="yes"],
+                    [with_libupsclient="no (symbol upscli_connect not found)"])
 
+       CPPFLAGS="$SAVE_CPPFLAGS"
        LDFLAGS="$SAVE_LDFLAGS"
 fi
-if test "x$with_libpq" = "xyes"
+if test "x$with_libupsclient" = "xyes"
 then
-       BUILD_WITH_LIBPQ_CPPFLAGS="$with_libpq_cppflags"
-       BUILD_WITH_LIBPQ_LDFLAGS="$with_libpq_ldflags"
-       AC_SUBST(BUILD_WITH_LIBPQ_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBPQ_LDFLAGS)
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
+
+       AC_CHECK_TYPES([UPSCONN_t, UPSCONN], [], [],
+[#include <stdlib.h>
+#include <stdio.h>
+#include <upsclient.h>])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
 fi
-AM_CONDITIONAL(BUILD_WITH_LIBPQ, test "x$with_libpq" = "xyes")
+if test "x$with_libupsclient" = "xyes"
+then
+       BUILD_WITH_LIBUPSCLIENT_CFLAGS="$with_libupsclient_cflags"
+       BUILD_WITH_LIBUPSCLIENT_LIBS="$with_libupsclient_libs"
+       AC_SUBST(BUILD_WITH_LIBUPSCLIENT_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBUPSCLIENT_LIBS)
+fi
+# }}}
 
-dnl Check for libvirt and libxml2 libraries.
+# pkg-config --exists 'libxml-2.0'; pkg-config --exists libvirt {{{
 with_libxml2="no (pkg-config isn't available)"
 with_libxml2_cflags=""
 with_libxml2_ldflags=""
@@ -2399,8 +2544,92 @@ if test "x$with_libvirt" = "xyes"; then
        AC_SUBST(BUILD_WITH_LIBVIRT_CFLAGS)
        AC_SUBST(BUILD_WITH_LIBVIRT_LIBS)
 fi
+# }}}
+
+# $PKG_CONFIG --exists OpenIPMIpthread {{{
+with_libopenipmipthread="yes"
+with_libopenipmipthread_cflags=""
+with_libopenipmipthread_libs=""
+
+AC_MSG_CHECKING([for pkg-config])
+temp_result="no"
+if test "x$PKG_CONFIG" = "x"
+then
+       with_libopenipmipthread="no"
+       temp_result="no"
+else
+       temp_result="$PKG_CONFIG"
+fi
+AC_MSG_RESULT([$temp_result])
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       AC_MSG_CHECKING([for libOpenIPMIpthread])
+       $PKG_CONFIG --exists OpenIPMIpthread 2>/dev/null
+       if test "$?" != "0"
+       then
+               with_libopenipmipthread="no ($PKG_CONFIG doesn't know OpenIPMIpthread)"
+       fi
+       AC_MSG_RESULT([$with_libopenipmipthread])
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       AC_MSG_CHECKING([for libOpenIPMIpthread CFLAGS])
+       temp_result="`$PKG_CONFIG --cflags OpenIPMIpthread`"
+       if test "$?" = "0"
+       then
+               with_libopenipmipthread_cflags="$temp_result"
+       else
+               with_libopenipmipthread="no ($PKG_CONFIG --cflags OpenIPMIpthread failed)"
+               temp_result="$PKG_CONFIG --cflags OpenIPMIpthread failed"
+       fi
+       AC_MSG_RESULT([$temp_result])
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       AC_MSG_CHECKING([for libOpenIPMIpthread LDFLAGS])
+       temp_result="`$PKG_CONFIG --libs OpenIPMIpthread`"
+       if test "$?" = "0"
+       then
+               with_libopenipmipthread_ldflags="$temp_result"
+       else
+               with_libopenipmipthread="no ($PKG_CONFIG --libs OpenIPMIpthread failed)"
+               temp_result="$PKG_CONFIG --libs OpenIPMIpthread failed"
+       fi
+       AC_MSG_RESULT([$temp_result])
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libopenipmipthread_cflags"
+
+       AC_CHECK_HEADERS(OpenIPMI/ipmi_smi.h,
+                        [with_libopenipmipthread="yes"],
+                        [with_libopenipmipthread="no (OpenIPMI/ipmi_smi.h not found)"],
+[#include <OpenIPMI/ipmiif.h>
+#include <OpenIPMI/ipmi_err.h>
+#include <OpenIPMI/ipmi_posix.h>
+#include <OpenIPMI/ipmi_conn.h>
+])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       BUILD_WITH_OPENIPMI_CFLAGS="$with_libopenipmipthread_cflags"
+       BUILD_WITH_OPENIPMI_LIBS="$with_libopenipmipthread_ldflags"
+       AC_SUBST(BUILD_WITH_OPENIPMI_CFLAGS)
+       AC_SUBST(BUILD_WITH_OPENIPMI_LIBS)
+fi
+# }}}
 
-dnl End of check for libvirt and libxml2 libraries.
+PKG_CHECK_MODULES([LIBNOTIFY], [libnotify],
+               [with_libnotify="yes"],
+               [with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"])
 
 # Check for enabled/disabled features
 #
@@ -2707,6 +2936,7 @@ AC_PLUGIN([cpu],         [$plugin_cpu],        [CPU usage statistics])
 AC_PLUGIN([cpufreq],     [$plugin_cpufreq],    [CPU frequency statistics])
 AC_PLUGIN([csv],         [yes],                [CSV output plugin])
 AC_PLUGIN([notify_desktop], [$with_libnotify], [Desktop notifications])
+AC_PLUGIN([dbi],         [$with_libdbi],       [General database statistics])
 AC_PLUGIN([df],          [$plugin_df],         [Filesystem usage statistics])
 AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
 AC_PLUGIN([dns],         [$with_libpcap],      [DNS traffic analysis])
@@ -2714,6 +2944,8 @@ AC_PLUGIN([email],       [yes],                [EMail statistics])
 AC_PLUGIN([entropy],     [$plugin_entropy],    [Entropy statistics])
 AC_PLUGIN([exec],        [yes],                [Execution of external programs])
 AC_PLUGIN([filecount],   [yes],                [Count files in directories])
+AC_PLUGIN([filter_ignore], [yes],                [Ignore specific values])
+AC_PLUGIN([filter_pcre], [$with_libpcre],      [Filter based on PCRE])
 AC_PLUGIN([hddtemp],     [yes],                [Query hddtempd])
 AC_PLUGIN([interface],   [$plugin_interface],  [Interface traffic statistics])
 AC_PLUGIN([iptables],    [$with_libiptc],      [IPTables rule counters])
@@ -2736,13 +2968,15 @@ AC_PLUGIN([notify_email], [$with_libesmtp],    [Email notifier])
 AC_PLUGIN([ntpd],        [yes],                [NTPd statistics])
 AC_PLUGIN([nut],         [$with_libupsclient], [Network UPS tools statistics])
 AC_PLUGIN([onewire],     [$with_libowcapi],    [OneWire sensor statistics])
+AC_PLUGIN([oracle],      [$with_oracle],       [Oracle plugin])
 AC_PLUGIN([perl],        [$plugin_perl],       [Embed a Perl interpreter])
 AC_PLUGIN([ping],        [$with_liboping],     [Network latency statistics])
 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([sensors],     [$with_lm_sensors],   [lm_sensors statistics])
+AC_PLUGIN([rrdtool],     [$with_librrd],       [RRDTool output plugin])
+AC_PLUGIN([rrdcached],   [$librrd_rrdc_update], [RRDTool output plugin])
+AC_PLUGIN([sensors],     [$with_libsensors],   [lm_sensors statistics])
 AC_PLUGIN([serial],      [$plugin_serial],     [serial port traffic])
 AC_PLUGIN([snmp],        [$with_libnetsnmp],   [SNMP querying plugin])
 AC_PLUGIN([swap],        [$plugin_swap],       [Swap usage statistics])
@@ -2799,10 +3033,10 @@ AC_SUBST(PERL_BINDINGS_OPTIONS)
 
 AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libiptc/Makefile src/liboconfig/Makefile src/liboping/Makefile bindings/Makefile)
 
-if test "x$with_rrdtool" = "xyes" \
+if test "x$with_librrd" = "xyes" \
        && test "x$librrd_threadsafe" != "xyes"
 then
-       with_rrdtool="yes (warning: librrd is not thread-safe)"
+       with_librrd="yes (warning: librrd is not thread-safe)"
 fi
 
 if test "x$with_liboping" = "xyes" \
@@ -2834,6 +3068,7 @@ cat <<EOF;
 Configuration:
   Libraries:
     libcurl . . . . . . . $with_libcurl
+    libdbi  . . . . . . . $with_libdbi
     libesmtp  . . . . . . $with_libesmtp
     libiokit  . . . . . . $with_libiokit
     libiptc . . . . . . . $with_libiptc
@@ -2847,16 +3082,18 @@ Configuration:
     libopenipmi . . . . . $with_libopenipmipthread
     liboping  . . . . . . $with_liboping
     libpcap . . . . . . . $with_libpcap
+    libpcre . . . . . . . $with_libpcre
     libperl . . . . . . . $with_libperl
     libpthread  . . . . . $with_libpthread
     libpq . . . . . . . . $with_libpq
-    librrd  . . . . . . . $with_rrdtool
-    libsensors  . . . . . $with_lm_sensors
+    librrd  . . . . . . . $with_librrd
+    libsensors  . . . . . $with_libsensors
     libstatgrab . . . . . $with_libstatgrab
     libupsclient  . . . . $with_libupsclient
     libvirt . . . . . . . $with_libvirt
     libxml2 . . . . . . . $with_libxml2
     libxmms . . . . . . . $with_libxmms
+    oracle  . . . . . . . $with_oracle
 
   Features:
     daemon mode . . . . . $enable_daemon
@@ -2874,6 +3111,7 @@ Configuration:
     cpu . . . . . . . . . $enable_cpu
     cpufreq . . . . . . . $enable_cpufreq
     csv . . . . . . . . . $enable_csv
+    dbi . . . . . . . . . $enable_dbi
     df  . . . . . . . . . $enable_df
     disk  . . . . . . . . $enable_disk
     dns . . . . . . . . . $enable_dns
@@ -2881,6 +3119,8 @@ Configuration:
     entropy . . . . . . . $enable_entropy
     exec  . . . . . . . . $enable_exec
     filecount . . . . . . $enable_filecount
+    filter_ignore . . . . $enable_filter_ignore
+    filter_pcre . . . . . $enable_filter_pcre
     hddtemp . . . . . . . $enable_hddtemp
     interface . . . . . . $enable_interface
     iptables  . . . . . . $enable_iptables
@@ -2904,12 +3144,14 @@ Configuration:
     ntpd  . . . . . . . . $enable_ntpd
     nut . . . . . . . . . $enable_nut
     onewire . . . . . . . $enable_onewire
+    oracle  . . . . . . . $enable_oracle
     perl  . . . . . . . . $enable_perl
     ping  . . . . . . . . $enable_ping
     postgresql  . . . . . $enable_postgresql
     powerdns  . . . . . . $enable_powerdns
     processes . . . . . . $enable_processes
     rrdtool . . . . . . . $enable_rrdtool
+    rrdcached . . . . . . $enable_rrdcached
     sensors . . . . . . . $enable_sensors
     serial  . . . . . . . $enable_serial
     snmp  . . . . . . . . $enable_snmp
@@ -2933,3 +3175,5 @@ EOF
 if test "x$dependency_error" = "xyes"; then
        AC_MSG_ERROR("Some plugins are missing dependencies - see above summary for details")
 fi
+
+# vim: set fdm=marker :
diff --git a/contrib/oracle/create_schema.ddl b/contrib/oracle/create_schema.ddl
new file mode 100644 (file)
index 0000000..e4dc54f
--- /dev/null
@@ -0,0 +1,243 @@
+-- Description
+--------------
+-- This will create a schema to provide collectd with the required permissions
+-- and space for statistic data.
+-- The idea is to store the output of some expensive queries in static tables
+-- and fill these tables with dbms_scheduler jobs as often as necessary.
+-- collectd will then just read from the static tables. This will reduces the
+-- chance that your system will be killed by excessive monitoring queries and
+-- gives the dba control on the interval the information provided to collectd
+-- will be refreshed. You have to create a dbms_scheduler job for each of the
+-- schemas you what to monitor for object-space-usage. See the example below.
+--
+-- Requirements
+---------------
+-- make sure you have: 
+--             write permission in $PWD
+--             you have GID of oracle software owner
+--             set $ORACLE_HOME 
+--             set $ORACLE_SID
+--             DB is up an running in RW mode
+-- execute like this:
+-- sqlplus /nolog @ create_collectd-schema.dll
+
+spool create_collectd-schema.log
+connect / as sysdba
+
+-- Create user, tablespace and permissions
+CREATE TABLESPACE "COLLECTD-TBS" 
+       DATAFILE SIZE 30M 
+       AUTOEXTEND ON 
+       NEXT 10M 
+       MAXSIZE 300M
+       LOGGING 
+       EXTENT MANAGEMENT LOCAL 
+       SEGMENT SPACE MANAGEMENT AUTO 
+       DEFAULT NOCOMPRESS;
+
+CREATE ROLE "CREATE_COLLECTD_SCHEMA" NOT IDENTIFIED;
+GRANT CREATE JOB TO "CREATE_COLLECTD_SCHEMA";
+GRANT CREATE SEQUENCE TO "CREATE_COLLECTD_SCHEMA";
+GRANT CREATE SYNONYM TO "CREATE_COLLECTD_SCHEMA";
+GRANT CREATE TABLE TO "CREATE_COLLECTD_SCHEMA";
+GRANT CREATE VIEW TO "CREATE_COLLECTD_SCHEMA";
+GRANT CREATE PROCEDURE TO "CREATE_COLLECTD_SCHEMA";
+
+CREATE USER "COLLECTDU" 
+       PROFILE "DEFAULT" 
+       IDENTIFIED BY "Change_me-1st" 
+       PASSWORD EXPIRE 
+       DEFAULT TABLESPACE "COLLECTD-TBS"
+       TEMPORARY TABLESPACE "TEMP"
+       QUOTA UNLIMITED ON "COLLECTD-TBS"
+       ACCOUNT UNLOCK;
+
+GRANT "CONNECT" TO "COLLECTDU";
+GRANT "SELECT_CATALOG_ROLE" TO "COLLECTDU";
+GRANT "CREATE_COLLECTD_SCHEMA" TO "COLLECTDU";
+GRANT analyze any TO "COLLECTDU";
+GRANT select on dba_tables TO "COLLECTDU";
+GRANT select on dba_lobs TO "COLLECTDU";
+GRANT select on dba_indexes TO "COLLECTDU";
+GRANT select on dba_segments TO "COLLECTDU";
+GRANT select on dba_tab_columns TO "COLLECTDU";
+GRANT select on dba_free_space TO "COLLECTDU";
+GRANT select on dba_data_files TO "COLLECTDU";
+-- Create tables and indexes
+
+alter session set current_schema=collectdu;
+
+create table c_tbs_usage (
+       tablespace_name varchar2(30),
+       bytes_free number,
+    bytes_used  number,
+        CONSTRAINT "C_TBS_USAGE_UK1" UNIQUE ("TABLESPACE_NAME") USING INDEX
+        TABLESPACE "COLLECTD-TBS"  ENABLE)
+        TABLESPACE "COLLECTD-TBS";
+
+CREATE TABLE "COLLECTDU"."C_TBL_SIZE" (
+    "OWNER" VARCHAR2(30 BYTE), 
+       "TABLE_NAME" VARCHAR2(30 BYTE), 
+       "BYTES" NUMBER, 
+        CONSTRAINT "C_TBL_SIZE_UK1" UNIQUE ("OWNER", "TABLE_NAME")
+         USING INDEX TABLESPACE "COLLECTD-TBS"  ENABLE)
+         TABLESPACE "COLLECTD-TBS" ;
+
+create or replace PROCEDURE get_object_size(owner IN VARCHAR2) AS
+
+v_owner VARCHAR2(30) := owner;
+
+l_free_blks NUMBER;
+l_total_blocks NUMBER;
+l_total_bytes NUMBER;
+l_unused_blocks NUMBER;
+l_unused_bytes NUMBER;
+l_lastusedextfileid NUMBER;
+l_lastusedextblockid NUMBER;
+l_last_used_block NUMBER;
+
+CURSOR cur_tbl IS
+SELECT owner,
+  TABLE_NAME
+FROM dba_tables
+WHERE owner = v_owner;
+
+CURSOR cur_idx IS
+SELECT owner,
+  index_name,
+  TABLE_NAME
+FROM dba_indexes
+WHERE owner = v_owner;
+
+CURSOR cur_lob IS
+SELECT owner,
+  segment_name,
+  TABLE_NAME
+FROM dba_lobs
+WHERE owner = v_owner;
+
+BEGIN
+
+  DELETE FROM c_tbl_size
+  WHERE owner = v_owner;
+  COMMIT;
+
+  FOR r_tbl IN cur_tbl
+  LOOP
+    BEGIN
+      dbms_space.unused_space(segment_owner => r_tbl.owner,   segment_name => r_tbl.TABLE_NAME,   segment_type => 'TABLE',   total_blocks => l_total_blocks,   total_bytes => l_total_bytes,   unused_blocks => l_unused_blocks,   unused_bytes => l_unused_bytes,   last_used_extent_file_id => l_lastusedextfileid,   last_used_extent_block_id => l_lastusedextblockid,   last_used_block => l_last_used_block);
+
+    EXCEPTION
+    WHEN others THEN
+      DBMS_OUTPUT.PUT_LINE('tbl_name: ' || r_tbl.TABLE_NAME);
+    END;
+    INSERT
+    INTO c_tbl_size
+    VALUES(r_tbl.owner,   r_tbl.TABLE_NAME,   l_total_bytes -l_unused_bytes);
+  END LOOP;
+
+  COMMIT;
+
+  FOR r_idx IN cur_idx
+  LOOP
+    BEGIN
+      dbms_space.unused_space(segment_owner => r_idx.owner,   segment_name => r_idx.index_name,   segment_type => 'INDEX',   total_blocks => l_total_blocks,   total_bytes => l_total_bytes,   unused_blocks => l_unused_blocks,   unused_bytes => l_unused_bytes,   last_used_extent_file_id => l_lastusedextfileid,   last_used_extent_block_id => l_lastusedextblockid,   last_used_block => l_last_used_block);
+
+    EXCEPTION
+    WHEN others THEN
+      DBMS_OUTPUT.PUT_LINE('idx_name: ' || r_idx.index_name);
+    END;
+
+    UPDATE c_tbl_size
+    SET bytes = bytes + l_total_bytes -l_unused_bytes
+    WHERE owner = r_idx.owner
+     AND TABLE_NAME = r_idx.TABLE_NAME;
+  END LOOP;
+
+  COMMIT;
+
+  FOR r_lob IN cur_lob
+  LOOP
+    BEGIN
+      dbms_space.unused_space(segment_owner => r_lob.owner,   segment_name => r_lob.segment_name,   segment_type => 'LOB',   total_blocks => l_total_blocks,   total_bytes => l_total_bytes,   unused_blocks => l_unused_blocks,   unused_bytes => l_unused_bytes,   last_used_extent_file_id => l_lastusedextfileid,   last_used_extent_block_id => l_lastusedextblockid,   last_used_block => l_last_used_block);
+
+    EXCEPTION
+    WHEN others THEN
+      DBMS_OUTPUT.PUT_LINE('lob_name: ' || r_lob.segment_name);
+    END;
+
+    UPDATE c_tbl_size
+    SET bytes = bytes + l_total_bytes -l_unused_bytes
+    WHERE owner = r_lob.owner
+     AND TABLE_NAME = r_lob.TABLE_NAME;
+  END LOOP;
+
+  COMMIT;
+
+END get_object_size;
+/
+
+create or replace PROCEDURE get_tbs_size AS
+BEGIN
+
+execute immediate 'truncate table c_tbs_usage';
+
+insert into c_tbs_usage (
+select df.tablespace_name as tablespace_name, 
+       decode(df.maxbytes,
+               0,
+               sum(fs.bytes),
+               (df.maxbytes-(df.bytes-sum(fs.bytes)))) as bytes_free,
+       decode(df.maxbytes,
+               0,
+               round((df.bytes-sum(fs.bytes))),
+               round(df.maxbytes-(df.maxbytes-(df.bytes-sum(fs.bytes))))) as bytes_used
+from dba_free_space fs inner join 
+       (select 
+               tablespace_name, 
+               sum(bytes) bytes, 
+               sum(decode(maxbytes,0,bytes,maxbytes))  maxbytes
+        from dba_data_files
+        group by tablespace_name ) df          
+on fs.tablespace_name = df.tablespace_name
+group by df.tablespace_name,df.maxbytes,df.bytes);
+
+COMMIT;
+
+END get_tbs_size;
+/
+
+BEGIN
+sys.dbms_scheduler.create_job(
+job_name => '"COLLECTDU"."C_TBSSIZE_JOB"',
+job_type => 'PLSQL_BLOCK',
+job_action => 'begin
+   get_tbs_size();
+end;',
+repeat_interval => 'FREQ=MINUTELY;INTERVAL=5',
+start_date => systimestamp at time zone 'Europe/Berlin',
+job_class => '"DEFAULT_JOB_CLASS"',
+auto_drop => FALSE,
+enabled => TRUE);
+END;
+/
+
+BEGIN
+sys.dbms_scheduler.create_job(
+job_name => '"COLLECTDU"."C_TBLSIZE_COLLECTDU_JOB"',
+job_type => 'PLSQL_BLOCK',
+job_action => 'begin
+   get_object_size( owner => ''COLLECTDU'' );
+end;',
+repeat_interval => 'FREQ=HOURLY;INTERVAL=12',
+start_date => systimestamp at time zone 'Europe/Berlin',
+job_class => '"DEFAULT_JOB_CLASS"',
+auto_drop => FALSE,
+enabled => TRUE);
+END;
+/
+
+spool off
+quit
diff --git a/contrib/oracle/db_systat.sql b/contrib/oracle/db_systat.sql
new file mode 100644 (file)
index 0000000..cf2a3c4
--- /dev/null
@@ -0,0 +1,55 @@
+-- Table sizes
+SELECT owner,
+  TABLE_NAME,
+  bytes
+FROM collectdu.c_tbl_size;
+
+-- Tablespace sizes
+SELECT tablespace_name,
+  bytes_free,
+  bytes_used
+FROM collectdu.c_tbs_usage;
+
+-- IO per Tablespace
+SELECT SUM(vf.phyblkrd) *8192 AS
+phy_blk_r,
+  SUM(vf.phyblkwrt) *8192 AS
+phy_blk_w,
+  'tablespace' AS
+i_prefix,
+  dt.tablespace_name
+FROM((dba_data_files dd JOIN v$filestat vf ON dd.file_id = vf.file#) JOIN dba_tablespaces dt ON dd.tablespace_name = dt.tablespace_name)
+GROUP BY dt.tablespace_name;
+
+-- Buffer Pool Hit Ratio:
+SELECT DISTINCT 100 *ROUND(1 -((MAX(decode(name,   'physical reads cache',   VALUE))) /(MAX(decode(name,   'db block gets from cache',   VALUE)) + MAX(decode(name,   'consistent gets from cache',   VALUE)))),   4) AS
+VALUE,
+  'BUFFER_CACHE_HIT_RATIO' AS
+buffer_cache_hit_ratio
+FROM v$sysstat;
+
+-- Shared Pool Hit Ratio:
+SELECT 
+  100.0 * sum(PINHITS) / sum(pins) as VALUE,
+  'SHAREDPOOL_HIT_RATIO' AS SHAREDPOOL_HIT_RATIO
+FROM V$LIBRARYCACHE;
+
+-- PGA Hit Ratio:
+SELECT VALUE,
+  'PGA_HIT_RATIO' AS
+pga_hit_ratio
+FROM v$pgastat
+WHERE name = 'cache hit percentage';
+
+-- DB Efficiency
+SELECT ROUND(SUM(decode(metric_name,   'Database Wait Time Ratio',   VALUE)),   2) AS
+database_wait_time_ratio,
+  ROUND(SUM(decode(metric_name,   'Database CPU Time Ratio',   VALUE)),   2) AS
+database_cpu_time_ratio,
+  'DB_EFFICIENCY' AS
+db_efficiency
+FROM sys.v_$sysmetric
+WHERE metric_name IN('Database CPU Time Ratio',   'Database Wait Time Ratio')
+ AND intsize_csec =
+  (SELECT MAX(intsize_csec)
+   FROM sys.v_$sysmetric);
index 47268f5..cf89c9a 100644 (file)
@@ -38,6 +38,7 @@ collectd_SOURCES = collectd.c collectd.h \
                   utils_tail_match.c utils_tail_match.h \
                   utils_match.c utils_match.h \
                   utils_mount.c utils_mount.h \
+                  utils_subst.c utils_subst.h \
                   utils_tail.c utils_tail.h \
                   utils_threshold.c utils_threshold.h \
                   types_list.c types_list.h
@@ -184,6 +185,16 @@ collectd_LDADD += "-dlopen" csv.la
 collectd_DEPENDENCIES += csv.la
 endif
 
+if BUILD_PLUGIN_DBI
+pkglib_LTLIBRARIES += dbi.la
+dbi_la_SOURCES = dbi.c
+dbi_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBDBI_CPPFLAGS)
+dbi_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBDBI_LDFLAGS)
+dbi_la_LIBADD = $(BUILD_WITH_LIBDBI_LIBS)
+collectd_LDADD += "-dlopen" dbi.la
+collectd_DEPENDENCIES += dbi.la
+endif
+
 if BUILD_PLUGIN_DF
 pkglib_LTLIBRARIES += df.la
 df_la_SOURCES = df.c
@@ -264,6 +275,24 @@ collectd_LDADD += "-dlopen" filecount.la
 collectd_DEPENDENCIES += filecount.la
 endif
 
+if BUILD_PLUGIN_FILTER_IGNORE
+pkglib_LTLIBRARIES += filter_ignore.la
+filter_ignore_la_SOURCES = filter_ignore.c
+filter_ignore_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" filter_ignore.la
+collectd_DEPENDENCIES += filter_ignore.la
+endif
+
+if BUILD_PLUGIN_FILTER_PCRE
+pkglib_LTLIBRARIES += filter_pcre.la
+filter_pcre_la_SOURCES = filter_pcre.c
+filter_pcre_la_CPPFLAGS = $(BUILD_WITH_LIBPCRE_CFLAGS)
+filter_pcre_la_LDFLAGS = -module -avoid-version \
+               $(BUILD_WITH_LIBPCRE_LIBS)
+collectd_LDADD += "-dlopen" filter_pcre.la
+collectd_DEPENDENCIES += filter_pcre.la
+endif
+
 if BUILD_PLUGIN_HDDTEMP
 pkglib_LTLIBRARIES += hddtemp.la
 hddtemp_la_SOURCES = hddtemp.c
@@ -529,6 +558,17 @@ collectd_LDADD += "-dlopen" onewire.la
 collectd_DEPENDENCIES += onewire.la
 endif
 
+if BUILD_PLUGIN_ORACLE
+pkglib_LTLIBRARIES += oracle.la
+oracle_la_SOURCES = oracle.c
+oracle_la_CFLAGS = $(AM_CFLAGS)
+oracle_la_CPPFLAGS = $(BUILD_WITH_ORACLE_CFLAGS)
+oracle_la_LIBADD = $(BUILD_WITH_ORACLE_LIBS)
+oracle_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" oracle.la
+collectd_DEPENDENCIES += oracle.la
+endif
+
 if BUILD_PLUGIN_PERL
 pkglib_LTLIBRARIES += perl.la
 perl_la_SOURCES = perl.c
@@ -588,9 +628,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 eaafff1..28004af 100644 (file)
@@ -34,6 +34,7 @@ FQDNLookup   true
 @BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 @BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq
 @BUILD_PLUGIN_CSV_TRUE@LoadPlugin csv
+@BUILD_PLUGIN_DBI_TRUE@LoadPlugin dbi
 @BUILD_PLUGIN_DF_TRUE@LoadPlugin df
 @BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk
 @BUILD_PLUGIN_DNS_TRUE@LoadPlugin dns
@@ -41,6 +42,7 @@ FQDNLookup   true
 @BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy
 @BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec
 @BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount
+@BUILD_PLUGIN_FILTER_PCRE_TRUE@LoadPlugin filter_pcre
 @BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
 @BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
 @BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables
@@ -68,6 +70,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
@@ -110,6 +113,25 @@ FQDNLookup   true
 #      StoreRates false
 #</Plugin>
 
+#<Plugin dbi>
+#      <Query "num_of_customers">
+#              Statement "SELECT 'customers' AS c_key, COUNT(*) AS c_value FROM customers_tbl"
+#              Type "gauge"
+#              InstancesFrom "c_key"
+#              ValuesFrom "c_value"
+#      </Query>
+#      <Database "customers_db">
+#              Driver "mysql"
+#              DriverOption "host" "localhost"
+#              DriverOption "username" "collectd"
+#              DriverOption "password" "AeXohy0O"
+#              DriverOption "dbname" "custdb0"
+#              #SelectDB "custdb0"
+#              Query "num_of_customers"
+#              #Query "..."
+#      </Database>
+#</Plugin>
+
 #<Plugin df>
 #      Device "/dev/hda1"
 #      Device "192.168.0.2:/mnt/nfs"
@@ -146,9 +168,27 @@ FQDNLookup   true
 #              Name "*.conf"
 #              MTime "-5m"
 #              Size "+10k"
+#              Recursive true
 #      </Directory>
 #</Plugin>
 
+#<Plugin filter_pcre>
+#      <RegEx>
+#              Host "^mail\d+$"
+#              Plugin "^tcpconns$"
+#              TypeInstance "^SYN_"
+#
+#              Action NoWrite
+#      </RegEx>
+#
+#      <RegEx>
+#              Plugin "^sensors$"
+#              PluginInstance "^Some Weird Sensor Chip Name Prefix"
+#
+#              SubstitutePluginInstance "foo"
+#      </RegEx>
+#</Plugin>
+
 @BUILD_PLUGIN_HDDTEMP_TRUE@<Plugin hddtemp>
 #      Host "127.0.0.1"
 #      Port "7634"
@@ -318,6 +358,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..c6fc5a3 100644 (file)
@@ -282,6 +282,161 @@ number.
 
 =back
 
+=head2 Plugin C<dbi>
+
+This plugin uses the "B<dbi>" library (L<http://libdbi.sourceforge.net/>) to
+connect to various databases, execute SQL statements and read back the results.
+You can configure how each column is to be interpreted and the plugin will
+generate one data set from each row returned according to these rules.
+
+Because the plugin is very generic, the configuration is a little more complex
+than those of other plugins. It usually looks something like this:
+
+  <Plugin dbi>
+    <Query "out_of_stock">
+      Statement "SELECT category, COUNT(*) AS value FROM products WHERE in_stock = 0 GROUP BY category"
+      Type "gauge"
+      InstancesFrom "category"
+      ValuesFrom "value"
+    </Query>
+    <Database "product_information">
+      Driver "mysql"
+      DriverOption "host" "localhost"
+      DriverOption "username" "collectd"
+      DriverOption "password" "aZo6daiw"
+      DriverOption "dbname" "prod_info"
+      SelectDB "prod_info"
+      Query "out_of_stock"
+    </Database>
+  </Plugin>
+
+The configuration above defines one query and one database. The query is then
+linked to the database with the B<Query> option I<within> the
+B<E<lt>DatabaseE<gt>> block. You can have any number of queries and databases
+and you can also use the B<Include> statement to split up the configuration
+file in multiple, smaller files. However, the B<E<lt>QueryE<gt>> block I<must>
+precede the B<E<lt>DatabaseE<gt>> blocks, because the file is interpreted from
+top to bottom!
+
+The following is a complete list of options:
+
+=head3 B<Query> blocks
+
+Query blocks define SQL statements and how the returned data should be
+interpreted. They are identified by the name that is given in the opening line
+of the block. Thus the name needs to be unique. Other than that, the name is
+not used in collectd.
+
+=over 4
+
+=item B<Statement> I<SQL>
+
+Sets the statement that should be executed on the server. This is B<not>
+interpreted by collectd, but simply passed to the database server. Therefore,
+the SQL dialect that's used depends on the server collectd is connected to.
+
+The query has to return at least two columns, one for the instance and one
+value. You cannot omit the instance, even if the statement is guaranteed to
+always return exactly one line. In that case, you can usually specify something
+like this:
+
+  Statement "SELECT \"instance\", COUNT(*) AS value FROM table"
+
+(That works with MySQL but may not be valid SQL according to the spec. If you
+use a more strict database server, you may have to select from a dummy table or
+something.)
+
+=item B<Type> I<Type>
+
+The B<type> that's used for each line returned. See L<types.db(5)> for more
+details on how types are defined. In short: A type is a predefined layout of
+data and the number of values and type of values has to match the type
+definition.
+
+If you specify "temperature" here, you need exactly one gauge column. If you
+specify "if_octets", you will need two counter columns. See the B<ValuesFrom>
+setting below.
+
+=item B<InstancesFrom> I<column0> [I<column1> ...]
+
+Specifies the columns whose values will be used to create the "TypeInstance"
+for each row. You need to specify at least one column for each query. If you
+specify more than one column, the value of all columns will be join together
+with the hyphen as separation character.
+
+The plugin itself does not check whether or not all built instances are
+different. It's your responsibility to assure that each is unique.
+
+=item B<ValuesFrom> I<column0> [I<column1> ...]
+
+Names the columns whose content is used as the actual data for the data sets
+that are dispatched to the daemon. How many such columns you need is determined
+by the B<Type> setting above. If you specify too many or not enough columns,
+the plugin will complain about that and no data will be submitted to the
+daemon.
+
+The actual data type in the columns is not that important. The plugin will
+automatically cast the values to the right type if it know how to do that. So
+it should be able to handle integer an floating point types, as well as strings
+(if they include a number at the beginning).
+
+=back
+
+=head3 B<Database> blocks
+
+Database blocks define a connection to a database and which queries should be
+sent to that database. Since the used "dbi" library can handle a wide variety
+of databases, the configuration is very generic. If in doubt, refer to libdbi's
+documentationE<nbsp>- we stick as close to the terminology used there.
+
+Each database needs a "name" as string argument in the starting tag of the
+block. This name will be used as "PluginInstance" in the values submitted to
+the daemon. Other than that, that name is not used.
+
+=over 4
+
+=item B<Driver> I<Driver>
+
+Specifies the driver to use to connect to the database. In many cases those
+drivers are named after the database they can connect to, but this is not a
+technical necessity. These drivers are sometimes referred to as "DBD",
+B<D>ataB<B>ase B<D>river, and some distributions ship them in separate
+packages. Drivers for the "dbi" library are developed by the B<libdbi-drivers>
+project at L<http://libdbi-drivers.sourceforge.net/>.
+
+You need to give the driver name as expected by the "dbi" library here. You
+should be able to find that in the documentation for each driver. If you
+mistype the driver name, the plugin will dump a list of all known driver names
+to the log.
+
+=item B<DriverOption> I<Key> I<Value>
+
+Sets driver-specific options. What option a driver supports can be found in the
+documentation for each driver, somewhere at
+L<http://libdbi-drivers.sourceforge.net/>. However, the options "host",
+"username", "password", and "dbname" seem to be deE<nbsp>facto standards.
+
+Unfortunately, drivers are not too keen to report errors when an unknown option
+is passed to them, so invalid settings here may go unnoticed. This is not the
+plugin's fault, it will report errors if it gets them from the libraryE<nbsp>/
+the driver. If a driver complains about an option, the plugin will dump a
+complete list of all options understood by that driver to the log.
+
+=item B<SelectDB> I<Database>
+
+In some cases, the database name you connect with is not the database name you
+want to use for querying data. If this option is set, the plugin will "select"
+(switch to) that database after the connection is established.
+
+=item B<Query> I<QueryName>
+
+Associates the query named I<QueryName> with this database connection. The
+query needs to be defined I<before> this statement, i.E<nbsp>e. all query
+blocks you want to refer to must be placed above the database block you want to
+refer to them from.
+
+=back
+
 =head2 Plugin C<df>
 
 =over 4
@@ -491,6 +646,101 @@ description see above. Valid multipliers here are C<b> (byte), C<k> (kilobyte),
 C<m> (megabyte), C<g> (gigabyte), C<t> (terabyte), and C<p> (petabyte). Please
 note that there are 1000 bytes in a kilobyte, not 1024.
 
+=item B<Recursive> I<true>|I<false>
+
+Controls whether or not to recurse into subdirectories. Enabled by default.
+
+=back
+
+=head2 Plugin C<filter_pcre>
+
+This plugin allows you to filter and rewrite value lists based on
+Perl-compatible regular expressions whose syntax and semantics are as close as
+possible to those of the Perl 5 language. See L<pcre(3)> for details.
+
+  <Plugin filter_pcre>
+    <RegEx>
+      Host "^mail\d+$"
+      Plugin "^tcpconns$"
+      TypeInstance "^SYN_"
+
+      Action NoWrite
+    </RegEx>
+
+    <RegEx>
+      Plugin "^sensors$"
+      PluginInstance "^Some Weird Sensor Chip Name Prefix"
+
+      SubstitutePluginInstance "foo"
+    </RegEx>
+  </Plugin>
+
+The configuration consists of one or more C<RegEx> blocks, each of which
+specifies a regular expression identifying a set of value lists and how to
+handle successful matches. A value list keeps the values of a single data-set
+and is identified by the tuple (host, plugin, plugin instance, type, type
+instance). The plugin and type instances are optional components. If they are
+missing they are treated as empty strings. Within those blocks, the following
+options are recognized:
+
+=over 4
+
+=item B<Host> I<regex>
+
+=item B<Plugin> I<regex>
+
+=item B<PluginInstance> I<regex>
+
+=item B<Type> I<regex>
+
+=item B<TypeInstance> I<regex>
+
+Specifies the regular expression for each component of the identifier. If any
+of these options is missing it is interpreted as a pattern which matches any
+string. All five components of a value list have to match the appropriate
+regular expression to trigger the specified action.
+
+=item B<Action> I<NoWrite>|I<NoThresholdCheck>|I<Ignore>
+
+Specify how to handle successful matches:
+
+=over 4
+
+=item B<NoWrite>
+
+Do not send the value list to any output (a.k.a. write) plugins.
+
+=item B<NoThresholdCheck>
+
+Skip threshold checking for this value list.
+
+=item B<Ignore>
+
+Completely ignore this value list.
+
+=back
+
+Two or more actions may be combined by specifying multiple B<Action> options.
+
+=item B<SubstituteHost> I<replacement>
+
+=item B<SubstitutePlugin> I<replacement>
+
+=item B<SubstitutePluginInstance> I<replacement>
+
+=item B<SubstituteType> I<replacement>
+
+=item B<SubstituteTypeInstance> I<replacement>
+
+Upon a successful match, the matching substring will be replaced by the
+specified I<replacement> text. These options require that an appropriate regex
+has been specified before, e.E<nbsp>g. B<SubstituteHost> requires that the
+B<Host> option has been specified before.
+
+B<Note>: It is not recommended to modify the type unless you really know what
+you are doing. The type is used to identify the data-set definition of the
+dispatched values.
+
 =back
 
 =head2 Plugin C<hddtemp>
@@ -1099,6 +1349,65 @@ short: If it works for you: Great! But kaap in mind that the config I<might>
 change, though this is unlikely. Oh, and if you want to help improving this
 plugin, just send a short notice to the mailing list. ThanksE<nbsp>:)
 
+=head2 Plugin C<oracle>
+
+The "oracle" plugin uses the Oracle® Call Interface (OCI) to connect to an
+Oracle® Database and lets you execute SQL statements there. It is very similar
+to the "dbi" plugin, because it was written around the same time. See the "dbi"
+plugin's documentation above for details.
+
+  <Plugin oracle>
+    <Query "out_of_stock">
+      Statement "SELECT category, COUNT(*) AS value FROM products WHERE in_stock = 0 GROUP BY category"
+      Type "gauge"
+      InstancesFrom "category"
+      ValuesFrom "value"
+    </Query>
+    <Database "product_information">
+      ConnectID "db01"
+      Username "oracle"
+      Password "secret"
+      Query "out_of_stock"
+    </Database>
+  </Plugin>
+
+=head3 B<Query> blocks
+
+The Query blocks are handled identically to the Query blocks of the "dbi"
+plugin. Please see its documentation above for details on how to specify
+queries.
+
+=head3 B<Database> blocks
+
+Database blocks define a connection to a database and which queries should be
+sent to that database. Each database needs a "name" as string argument in the
+starting tag of the block. This name will be used as "PluginInstance" in the
+values submitted to the daemon. Other than that, that name is not used.
+
+=over 4
+
+=item B<ConnectID> I<ID>
+
+Defines the "database alias" or "service name" to connect to. Usually, these
+names are defined in the file named C<$ORACLE_HOME/network/admin/tnsnames.ora>.
+
+=item B<Username> I<Username>
+
+Username used for authentication.
+
+=item B<Password> I<Password>
+
+Password used for authentication.
+
+=item B<Query> I<QueryName>
+
+Associates the query named I<QueryName> with this database connection. The
+query needs to be defined I<before> this statement, i.E<nbsp>e. all query
+blocks you want to refer to must be placed above the database block you want to
+refer to them from.
+
+=back
+
 =head2 Plugin C<perl>
 
 This plugin embeds a Perl-interpreter into collectd and provides an interface
@@ -1500,6 +1809,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
@@ -2085,6 +2444,7 @@ L<types.db(5)>,
 L<hddtemp(8)>,
 L<kstat(3KSTAT)>,
 L<mbmon(1)>,
+L<pcre(3)>,
 L<psql(1)>,
 L<rrdtool(1)>,
 L<sensors(1)>
diff --git a/src/dbi.c b/src/dbi.c
new file mode 100644 (file)
index 0000000..484c604
--- /dev/null
+++ b/src/dbi.c
@@ -0,0 +1,1059 @@
+/**
+ * collectd - src/dbi.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 "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <dbi/dbi.h>
+
+/*
+ * Data types
+ */
+struct cdbi_driver_option_s
+{
+  char *key;
+  char *value;
+};
+typedef struct cdbi_driver_option_s cdbi_driver_option_t;
+
+struct cdbi_query_s
+{
+  char    *name;
+  char    *statement;
+  char    *type;
+  char   **instances;
+  size_t   instances_num;
+  char   **values;
+  size_t   values_num;
+};
+typedef struct cdbi_query_s cdbi_query_t;
+
+struct cdbi_database_s
+{
+  char *name;
+  char *select_db;
+
+  char *driver;
+  cdbi_driver_option_t *driver_options;
+  size_t driver_options_num;
+
+  cdbi_query_t **queries;
+  size_t      queries_num;
+
+  dbi_conn connection;
+};
+typedef struct cdbi_database_s cdbi_database_t;
+
+/*
+ * Global variables
+ */
+static cdbi_query_t    **queries       = NULL;
+static size_t         queries_num   = 0;
+static cdbi_database_t **databases     = NULL;
+static size_t         databases_num = 0;
+
+/*
+ * Functions
+ */
+static const char *cdbi_strerror (dbi_conn conn, /* {{{ */
+    char *buffer, size_t buffer_size)
+{
+  const char *msg;
+  int status;
+
+  if (conn == NULL)
+  {
+    sstrncpy (buffer, "connection is NULL", buffer_size);
+    return (buffer);
+  }
+
+  msg = NULL;
+  status = dbi_conn_error (conn, &msg);
+  if ((status >= 0) && (msg != NULL))
+    ssnprintf (buffer, buffer_size, "%s (status %i)", msg, status);
+  else
+    ssnprintf (buffer, buffer_size, "dbi_conn_error failed with status %i",
+        status);
+
+  return (buffer);
+} /* }}} const char *cdbi_conn_error */
+
+static int cdbi_result_get_field (dbi_result res, /* {{{ */
+    const char *name, int dst_type, value_t *ret_value)
+{
+  value_t value;
+  unsigned int index;
+  unsigned short src_type;
+  dbi_conn connection;
+
+  index = dbi_result_get_field_idx (res, name);
+  if (index < 1)
+  {
+    ERROR ("dbi plugin: cdbi_result_get: No such column: %s.", name);
+    return (-1);
+  }
+
+  src_type = dbi_result_get_field_type_idx (res, index);
+  if (src_type == DBI_TYPE_ERROR)
+  {
+    ERROR ("dbi plugin: cdbi_result_get: "
+        "dbi_result_get_field_type_idx failed.");
+    return (-1);
+  }
+
+  if ((dst_type != DS_TYPE_COUNTER) && (dst_type != DS_TYPE_GAUGE))
+  {
+    ERROR ("dbi plugin: cdbi_result_get: Don't know how to handle "
+        "destination type %i.", dst_type);
+    return (-1);
+  }
+
+  if (src_type == DBI_TYPE_INTEGER)
+  {
+    if (dst_type == DS_TYPE_COUNTER)
+      value.counter = dbi_result_get_ulonglong_idx (res, index);
+    else
+      value.gauge = (gauge_t) dbi_result_get_longlong_idx (res, index);
+  }
+  else if (src_type == DBI_TYPE_DECIMAL)
+  {
+    value.gauge = dbi_result_get_double_idx (res, index);
+    if (dst_type == DS_TYPE_COUNTER)
+      value.counter = (counter_t) round (value.gauge);
+  }
+  else if (src_type == DBI_TYPE_STRING)
+  {
+    const char *string = dbi_result_get_string_idx (res, index);
+    char *endptr = NULL;
+
+    if (string == NULL)
+      value.gauge = NAN;
+    else if (dst_type == DS_TYPE_COUNTER)
+      value.counter = (counter_t) strtoll (string, &endptr, 0);
+    else
+      value.gauge = (gauge_t) strtod (string, &endptr);
+
+    if (string == endptr)
+    {
+      ERROR ("dbi plugin: cdbi_result_get: Can't parse string as number: %s.",
+          string);
+      return (-1);
+    }
+  }
+  else
+  {
+    ERROR ("dbi plugin: cdbi_result_get: Don't know how to handle "
+        "source type %hu.", src_type);
+    return (-1);
+  }
+
+  connection = dbi_result_get_conn (res);
+  if (dbi_conn_error_flag (connection) != 0)
+  {
+    char errbuf[1024];
+    ERROR ("dbi plugin: cdbi_result_get: dbi_result_get_*_idx failed: %s.",
+        cdbi_strerror (connection, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  *ret_value = value;
+  return (0);
+} /* }}} int cdbi_result_get_field */
+
+static void cdbi_query_free (cdbi_query_t *q) /* {{{ */
+{
+  size_t i;
+
+  if (q == NULL)
+    return;
+
+  sfree (q->name);
+  sfree (q->statement);
+  sfree (q->type);
+
+  for (i = 0; i < q->instances_num; i++)
+    sfree (q->instances[i]);
+  sfree (q->instances);
+
+  for (i = 0; i < q->values_num; i++)
+    sfree (q->values[i]);
+  sfree (q->values);
+
+  sfree (q);
+} /* }}} void cdbi_query_free */
+
+static void cdbi_database_free (cdbi_database_t *db) /* {{{ */
+{
+  size_t i;
+
+  if (db == NULL)
+    return;
+
+  sfree (db->name);
+  sfree (db->driver);
+
+  for (i = 0; i < db->driver_options_num; i++)
+  {
+    sfree (db->driver_options[i].key);
+    sfree (db->driver_options[i].value);
+  }
+  sfree (db->driver_options);
+
+  sfree (db);
+} /* }}} void cdbi_database_free */
+
+static void cdbi_submit (cdbi_database_t *db, cdbi_query_t *q, /* {{{ */
+    char **instances, value_t *values)
+{
+  value_list_t vl = VALUE_LIST_INIT;
+
+  vl.values = values;
+  vl.values_len = (int) q->values_num;
+  vl.time = time (NULL);
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "dbi", sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, db->name, sizeof (vl.type_instance));
+  sstrncpy (vl.type, q->type, sizeof (vl.type));
+  strjoin (vl.type_instance, sizeof (vl.type_instance),
+      instances, q->instances_num, "-");
+  vl.type_instance[sizeof (vl.type_instance) - 1] = 0;
+
+  plugin_dispatch_values (&vl);
+} /* }}} void cdbi_submit */
+
+/* Configuration handling functions {{{
+ *
+ * <Plugin dbi>
+ *   <Query "plugin_instance0">
+ *     Statement "SELECT name, value FROM table"
+ *     Type "gauge"
+ *     InstancesFrom "name"
+ *     ValuesFrom "value"
+ *   </Query>
+ *     
+ *   <Database "plugin_instance1">
+ *     Driver "mysql"
+ *     DriverOption "hostname" "localhost"
+ *     ...
+ *     Query "plugin_instance0"
+ *   </Database>
+ * </Plugin>
+ */
+
+static int cdbi_config_set_string (char **ret_string, /* {{{ */
+    oconfig_item_t *ci)
+{
+  char *string;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("dbi plugin: The `%s' config option "
+        "needs exactly one string argument.", ci->key);
+    return (-1);
+  }
+
+  string = strdup (ci->values[0].value.string);
+  if (string == NULL)
+  {
+    ERROR ("dbi plugin: strdup failed.");
+    return (-1);
+  }
+
+  if (*ret_string != NULL)
+    free (*ret_string);
+  *ret_string = string;
+
+  return (0);
+} /* }}} int cdbi_config_set_string */
+
+static int cdbi_config_add_string (char ***ret_array, /* {{{ */
+    size_t *ret_array_len, oconfig_item_t *ci)
+{
+  char **array;
+  size_t array_len;
+  int i;
+
+  if (ci->values_num < 1)
+  {
+    WARNING ("dbi plugin: The `%s' config option "
+        "needs at least one argument.", ci->key);
+    return (-1);
+  }
+
+  for (i = 0; i < ci->values_num; i++)
+  {
+    if (ci->values[i].type != OCONFIG_TYPE_STRING)
+    {
+      WARNING ("dbi plugin: Argument %i to the `%s' option "
+          "is not a string.", i + 1, ci->key);
+      return (-1);
+    }
+  }
+
+  array_len = *ret_array_len;
+  array = (char **) realloc (*ret_array,
+      sizeof (char *) * (array_len + ci->values_num));
+  if (array == NULL)
+  {
+    ERROR ("dbi plugin: realloc failed.");
+    return (-1);
+  }
+  *ret_array = array;
+
+  for (i = 0; i < ci->values_num; i++)
+  {
+    array[array_len] = strdup (ci->values[i].value.string);
+    if (array[array_len] == NULL)
+    {
+      ERROR ("dbi plugin: strdup failed.");
+      *ret_array_len = array_len;
+      return (-1);
+    }
+    array_len++;
+  }
+
+  *ret_array_len = array_len;
+  return (0);
+} /* }}} int cdbi_config_add_string */
+
+static int cdbi_config_add_query (oconfig_item_t *ci) /* {{{ */
+{
+  cdbi_query_t *q;
+  int status;
+  int i;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("dbi plugin: The `Query' block "
+        "needs exactly one string argument.");
+    return (-1);
+  }
+
+  q = (cdbi_query_t *) malloc (sizeof (*q));
+  if (q == NULL)
+  {
+    ERROR ("dbi plugin: malloc failed.");
+    return (-1);
+  }
+  memset (q, 0, sizeof (*q));
+
+  status = cdbi_config_set_string (&q->name, ci);
+  if (status != 0)
+  {
+    sfree (q);
+    return (status);
+  }
+
+  /* Fill the `cdbi_query_t' structure.. */
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Statement", child->key) == 0)
+      status = cdbi_config_set_string (&q->statement, child);
+    else if (strcasecmp ("Type", child->key) == 0)
+      status = cdbi_config_set_string (&q->type, child);
+    else if (strcasecmp ("InstancesFrom", child->key) == 0)
+      status = cdbi_config_add_string (&q->instances, &q->instances_num, child);
+    else if (strcasecmp ("ValuesFrom", child->key) == 0)
+      status = cdbi_config_add_string (&q->values, &q->values_num, child);
+    else
+    {
+      WARNING ("dbi plugin: Option `%s' not allowed here.", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  /* Check that all necessary options have been given. */
+  while (status == 0)
+  {
+    if (q->statement == NULL)
+    {
+      WARNING ("dbi plugin: `Statement' not given for query `%s'", q->name);
+      status = -1;
+    }
+    if (q->type == NULL)
+    {
+      WARNING ("dbi plugin: `Type' not given for query `%s'", q->name);
+      status = -1;
+    }
+    if (q->instances == NULL)
+    {
+      WARNING ("dbi plugin: `InstancesFrom' not given for query `%s'", q->name);
+      status = -1;
+    }
+    if (q->values == NULL)
+    {
+      WARNING ("dbi plugin: `ValuesFrom' not given for query `%s'", q->name);
+      status = -1;
+    }
+
+    break;
+  } /* while (status == 0) */
+
+  /* If all went well, add this query to the list of queries within the
+   * database structure. */
+  if (status == 0)
+  {
+    cdbi_query_t **temp;
+
+    temp = (cdbi_query_t **) realloc (queries,
+        sizeof (*queries) * (queries_num + 1));
+    if (temp == NULL)
+    {
+      ERROR ("dbi plugin: realloc failed");
+      status = -1;
+    }
+    else
+    {
+      queries = temp;
+      queries[queries_num] = q;
+      queries_num++;
+    }
+  }
+
+  if (status != 0)
+  {
+    cdbi_query_free (q);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int cdbi_config_add_query */
+
+static int cdbi_config_add_database_driver_option (cdbi_database_t *db, /* {{{ */
+    oconfig_item_t *ci)
+{
+  cdbi_driver_option_t *option;
+
+  if ((ci->values_num != 2)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING)
+      || (ci->values[1].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("dbi plugin: The `DriverOption' config option "
+        "needs exactly two string arguments.");
+    return (-1);
+  }
+
+  option = (cdbi_driver_option_t *) realloc (db->driver_options,
+      sizeof (*option) * (db->driver_options_num + 1));
+  if (option == NULL)
+  {
+    ERROR ("dbi plugin: realloc failed");
+    return (-1);
+  }
+
+  db->driver_options = option;
+  option = db->driver_options + db->driver_options_num;
+
+  option->key = strdup (ci->values[0].value.string);
+  if (option->key == NULL)
+  {
+    ERROR ("dbi plugin: strdup failed.");
+    return (-1);
+  }
+
+  option->value = strdup (ci->values[1].value.string);
+  if (option->value == NULL)
+  {
+    ERROR ("dbi plugin: strdup failed.");
+    sfree (option->key);
+    return (-1);
+  }
+
+  db->driver_options_num++;
+  return (0);
+} /* }}} int cdbi_config_add_database_driver_option */
+
+static int cdbi_config_add_database_query (cdbi_database_t *db, /* {{{ */
+    oconfig_item_t *ci)
+{
+  cdbi_query_t *q;
+  cdbi_query_t **temp;
+  size_t i;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("dbi plugin: The `Query' config option "
+        "needs exactly one string argument.");
+    return (-1);
+  }
+
+  q = NULL;
+  for (i = 0; i < queries_num; i++)
+  {
+    if (strcasecmp (queries[i]->name, ci->values[0].value.string) == 0)
+    {
+      q = queries[i];
+      break;
+    }
+  }
+
+  if (q == NULL)
+  {
+    WARNING ("dbi plugin: Database `%s': Unknown query `%s'. "
+        "Please make sure that the <Query \"%s\"> block comes before "
+        "the <Database \"%s\"> block.",
+        db->name, ci->values[0].value.string,
+        ci->values[0].value.string, db->name);
+    return (-1);
+  }
+
+  temp = (cdbi_query_t **) realloc (db->queries,
+      sizeof (*db->queries) * (db->queries_num + 1));
+  if (temp == NULL)
+  {
+    ERROR ("dbi plugin: realloc failed");
+    return (-1);
+  }
+  else
+  {
+    db->queries = temp;
+    db->queries[db->queries_num] = q;
+    db->queries_num++;
+  }
+
+  return (0);
+} /* }}} int cdbi_config_add_database_query */
+
+static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
+{
+  cdbi_database_t *db;
+  int status;
+  int i;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("dbi plugin: The `Database' block "
+        "needs exactly one string argument.");
+    return (-1);
+  }
+
+  db = (cdbi_database_t *) malloc (sizeof (*db));
+  if (db == NULL)
+  {
+    ERROR ("dbi plugin: malloc failed.");
+    return (-1);
+  }
+  memset (db, 0, sizeof (*db));
+
+  status = cdbi_config_set_string (&db->name, ci);
+  if (status != 0)
+  {
+    sfree (db);
+    return (status);
+  }
+
+  /* Fill the `cdbi_database_t' structure.. */
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Driver", child->key) == 0)
+      status = cdbi_config_set_string (&db->driver, child);
+    else if (strcasecmp ("DriverOption", child->key) == 0)
+      status = cdbi_config_add_database_driver_option (db, child);
+    else if (strcasecmp ("SelectDB", child->key) == 0)
+      status = cdbi_config_set_string (&db->select_db, child);
+    else if (strcasecmp ("Query", child->key) == 0)
+      status = cdbi_config_add_database_query (db, child);
+    else
+    {
+      WARNING ("dbi plugin: Option `%s' not allowed here.", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  /* Check that all necessary options have been given. */
+  while (status == 0)
+  {
+    if (db->driver == NULL)
+    {
+      WARNING ("dbi plugin: `Driver' not given for database `%s'", db->name);
+      status = -1;
+    }
+    if (db->driver_options_num == 0)
+    {
+      WARNING ("dbi plugin: No `DriverOption' given for database `%s'. "
+          "This will likely not work.", db->name);
+    }
+
+    break;
+  } /* while (status == 0) */
+
+  /* If all went well, add this database to the global list of databases. */
+  if (status == 0)
+  {
+    cdbi_database_t **temp;
+
+    temp = (cdbi_database_t **) realloc (databases,
+        sizeof (*databases) * (databases_num + 1));
+    if (temp == NULL)
+    {
+      ERROR ("dbi plugin: realloc failed");
+      status = -1;
+    }
+    else
+    {
+      databases = temp;
+      databases[databases_num] = db;
+      databases_num++;
+    }
+  }
+
+  if (status != 0)
+  {
+    cdbi_database_free (db);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int cdbi_config_add_database */
+
+static int cdbi_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+    if (strcasecmp ("Query", child->key) == 0)
+      cdbi_config_add_query (child);
+    else if (strcasecmp ("Database", child->key) == 0)
+      cdbi_config_add_database (child);
+    else
+    {
+      WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
+    }
+  } /* for (ci->children) */
+
+  return (0);
+} /* }}} int cdbi_config */
+
+/* }}} End of configuration handling functions */
+
+static int cdbi_init (void) /* {{{ */
+{
+  static int did_init = 0;
+  int status;
+
+  if (did_init != 0)
+    return (0);
+
+  if (queries_num == 0)
+  {
+    ERROR ("dbi plugin: No <Query> blocks have been found. Without them, "
+        "this plugin can't do anything useful, so we will returns an error.");
+    return (-1);
+  }
+
+  if (databases_num == 0)
+  {
+    ERROR ("dbi plugin: No <Database> blocks have been found. Without them, "
+        "this plugin can't do anything useful, so we will returns an error.");
+    return (-1);
+  }
+
+  status = dbi_initialize (NULL);
+  if (status < 0)
+  {
+    ERROR ("dbi plugin: cdbi_init: dbi_initialize failed with status %i.",
+        status);
+    return (-1);
+  }
+  else if (status == 0)
+  {
+    ERROR ("dbi plugin: `dbi_initialize' could not load any drivers. Please "
+        "install at least one `DBD' or check your installation.");
+    return (-1);
+  }
+  DEBUG ("dbi plugin: cdbi_init: dbi_initialize reports %i driver%s.",
+      status, (status == 1) ? "" : "s");
+
+  return (0);
+} /* }}} int cdbi_init */
+
+static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
+    cdbi_query_t *q)
+{
+  dbi_result res;
+  char **instances;
+  value_t *values;
+  const data_set_t *ds;
+  size_t i;
+  int status;
+
+  res = NULL;
+  instances = NULL;
+  values = NULL;
+
+  /* Macro that cleans up dynamically allocated memory and returns the
+   * specified status. */
+#define BAIL_OUT(status) \
+  if (res != NULL) { dbi_result_free (res); res = NULL; } \
+  if (instances != NULL) { sfree (instances[0]); sfree (instances); } \
+  sfree (values); \
+  return (status)
+
+  ds = plugin_get_ds (q->type);
+  if (ds == NULL)
+  {
+    ERROR ("dbi plugin: cdbi_read_database_query: Query `%s': Type `%s' is not "
+        "known by the daemon. See types.db(5) for details.",
+        q->name, q->type);
+    BAIL_OUT (-1);
+  }
+
+  if (((size_t) ds->ds_num) != q->values_num)
+  {
+    ERROR ("dbi plugin: cdbi_read_database_query: Query `%s': The type `%s' "
+        "requires exactly %i value%s, but the configuration specifies %zu.",
+        q->name, q->type,
+        ds->ds_num, (ds->ds_num == 1) ? "" : "s",
+        q->values_num);
+    BAIL_OUT (-1);
+  }
+
+  /* Allocate `instances' and `values' {{{ */
+  instances = (char **) malloc (sizeof (*instances) * q->instances_num);
+  if (instances == NULL)
+  {
+    ERROR ("dbi plugin: malloc failed.");
+    BAIL_OUT (-1);
+  }
+
+  instances[0] = (char *) malloc (q->instances_num * DATA_MAX_NAME_LEN);
+  if (instances[0] == NULL)
+  {
+    ERROR ("dbi plugin: malloc failed.");
+    BAIL_OUT (-1);
+  }
+  for (i = 1; i < q->instances_num; i++)
+    instances[i] = instances[i - 1] + DATA_MAX_NAME_LEN;
+
+  values = (value_t *) malloc (sizeof (*values) * q->values_num);
+  if (values == NULL)
+  {
+    ERROR ("dbi plugin: malloc failed.");
+    BAIL_OUT (-1);
+  }
+  /* }}} */
+
+  res = dbi_conn_query (db->connection, q->statement);
+  if (res == NULL)
+  {
+    char errbuf[1024];
+    ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): "
+        "dbi_conn_query failed: %s",
+        db->name, q->name,
+        cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
+    BAIL_OUT (-1);
+  }
+
+  status = dbi_result_first_row (res);
+  if (status != 1)
+  {
+    char errbuf[1024];
+    ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): "
+        "dbi_result_first_row failed: %s. Maybe the statement didn't "
+        "return any rows?",
+        db->name, q->name, 
+        cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
+    BAIL_OUT (-1);
+  }
+
+  while (42)
+  {
+    /* Get instance names and values from the result: */
+    for (i = 0; i < q->instances_num; i++) /* {{{ */
+    {
+      const char *inst;
+
+      inst = dbi_result_get_string (res, q->instances[i]);
+      if (dbi_conn_error_flag (db->connection) != 0)
+      {
+        char errbuf[1024];
+        ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): "
+            "dbi_result_get_string (%s) failed: %s",
+            db->name, q->name, q->instances[i],
+            cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
+        BAIL_OUT (-1);
+      }
+
+      sstrncpy (instances[i], (inst == NULL) ? "" : inst, DATA_MAX_NAME_LEN);
+      DEBUG ("dbi plugin: cdbi_read_database_query (%s, %s): "
+          "instances[%zu] = %s;",
+          db->name, q->name, i, instances[i]);
+    } /* }}} for (i = 0; i < q->instances_num; i++) */
+
+    for (i = 0; i < q->values_num; i++) /* {{{ */
+    {
+      status = cdbi_result_get_field (res, q->values[i], ds->ds[i].type,
+          values + i);
+      if (status != 0)
+      {
+        BAIL_OUT (-1);
+      }
+
+      if (ds->ds[i].type == DS_TYPE_COUNTER)
+      {
+        DEBUG ("dbi plugin: cdbi_read_database_query (%s, %s): values[%zu] = %llu;",
+            db->name, q->name, i, values[i].counter);
+      }
+      else
+      {
+        DEBUG ("dbi plugin: cdbi_read_database_query (%s, %s): values[%zu] = %g;",
+            db->name, q->name, i, values[i].gauge);
+      }
+    } /* }}} for (i = 0; i < q->values_num; i++) */
+
+    /* Dispatch this row to the daemon. */
+    cdbi_submit (db, q, instances, values);
+
+    /* Get the next row from the database. */
+    status = dbi_result_next_row (res);
+    if (status != 1)
+    {
+      if (dbi_conn_error_flag (db->connection) != 0)
+      {
+        char errbuf[1024];
+        WARNING ("dbi plugin: cdbi_read_database_query (%s, %s): "
+            "dbi_result_next_row failed: %s.",
+            db->name, q->name,
+            cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
+      }
+      break;
+    }
+  } /* while (42) */
+
+  BAIL_OUT (0);
+#undef BAIL_OUT
+} /* }}} int cdbi_read_database_query */
+
+static int cdbi_connect_database (cdbi_database_t *db) /* {{{ */
+{
+  dbi_driver driver;
+  dbi_conn connection;
+  size_t i;
+  int status;
+
+  if (db->connection != NULL)
+  {
+    status = dbi_conn_ping (db->connection);
+    if (status != 0) /* connection is alive */
+      return (0);
+
+    dbi_conn_close (db->connection);
+    db->connection = NULL;
+  }
+
+  driver = dbi_driver_open (db->driver);
+  if (driver == NULL)
+  {
+    ERROR ("dbi plugin: cdbi_connect_database: dbi_driver_open (%s) failed.",
+        db->driver);
+    INFO ("dbi plugin: Maybe the driver isn't installed? "
+        "Known drivers are:");
+    for (driver = dbi_driver_list (NULL);
+        driver != NULL;
+        driver = dbi_driver_list (driver))
+    {
+      INFO ("dbi plugin: * %s", dbi_driver_get_name (driver));
+    }
+    return (-1);
+  }
+
+  connection = dbi_conn_open (driver);
+  if (connection == NULL)
+  {
+    ERROR ("dbi plugin: cdbi_connect_database: dbi_conn_open (%s) failed.",
+        db->driver);
+    return (-1);
+  }
+
+  /* Set all the driver options. Because this is a very very very generic
+   * interface, the error handling is kind of long. If an invalid option is
+   * encountered, it will get a list of options understood by the driver and
+   * report that as `INFO'. This way, users hopefully don't have too much
+   * trouble finding out how to configure the plugin correctly.. */
+  for (i = 0; i < db->driver_options_num; i++)
+  {
+    DEBUG ("dbi plugin: cdbi_connect_database (%s): "
+        "key = %s; value = %s;",
+        db->name,
+        db->driver_options[i].key,
+        db->driver_options[i].value);
+
+    status = dbi_conn_set_option (connection,
+        db->driver_options[i].key, db->driver_options[i].value);
+    if (status != 0)
+    {
+      char errbuf[1024];
+      const char *opt;
+
+      ERROR ("dbi plugin: cdbi_connect_database (%s): "
+          "dbi_conn_set_option (%s, %s) failed: %s.",
+          db->name,
+          db->driver_options[i].key, db->driver_options[i].value,
+          cdbi_strerror (connection, errbuf, sizeof (errbuf)));
+
+      INFO ("dbi plugin: This is a list of all options understood "
+          "by the `%s' driver:", db->driver);
+      for (opt = dbi_conn_get_option_list (connection, NULL);
+          opt != NULL;
+          opt = dbi_conn_get_option_list (connection, opt))
+      {
+        INFO ("dbi plugin: * %s", opt);
+      }
+
+      dbi_conn_close (connection);
+      return (-1);
+    }
+  } /* for (i = 0; i < db->driver_options_num; i++) */
+
+  status = dbi_conn_connect (connection);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("dbi plugin: cdbi_connect_database (%s): "
+        "dbi_conn_connect failed: %s",
+        db->name, cdbi_strerror (connection, errbuf, sizeof (errbuf)));
+    dbi_conn_close (connection);
+    return (-1);
+  }
+
+  if (db->select_db != NULL)
+  {
+    status = dbi_conn_select_db (connection, db->select_db);
+    if (status != 0)
+    {
+      char errbuf[1024];
+      WARNING ("dbi plugin: cdbi_connect_database (%s): "
+          "dbi_conn_select_db (%s) failed: %s. Check the `SelectDB' option.",
+          db->name, db->select_db,
+          cdbi_strerror (connection, errbuf, sizeof (errbuf)));
+      dbi_conn_close (connection);
+      return (-1);
+    }
+  }
+
+  db->connection = connection;
+  return (0);
+} /* }}} int cdbi_connect_database */
+
+static int cdbi_read_database (cdbi_database_t *db) /* {{{ */
+{
+  size_t i;
+  int success;
+  int status;
+
+  status = cdbi_connect_database (db);
+  if (status != 0)
+    return (status);
+  assert (db->connection != NULL);
+
+  success = 0;
+  for (i = 0; i < db->queries_num; i++)
+  {
+    status = cdbi_read_database_query (db, db->queries[i]);
+    if (status == 0)
+      success++;
+  }
+
+  if (success == 0)
+  {
+    ERROR ("dbi plugin: All queries failed for database `%s'.", db->name);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int cdbi_read_database */
+
+static int cdbi_read (void) /* {{{ */
+{
+  size_t i;
+  int success = 0;
+  int status;
+
+  for (i = 0; i < databases_num; i++)
+  {
+    status = cdbi_read_database (databases[i]);
+    if (status == 0)
+      success++;
+  }
+
+  if (success == 0)
+  {
+    ERROR ("dbi plugin: No database could be read. Will return an error so "
+        "the plugin will be delayed.");
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int cdbi_read */
+
+static int cdbi_shutdown (void) /* {{{ */
+{
+  size_t i;
+
+  for (i = 0; i < databases_num; i++)
+  {
+    if (databases[i]->connection != NULL)
+    {
+      dbi_conn_close (databases[i]->connection);
+      databases[i]->connection = NULL;
+    }
+    cdbi_database_free (databases[i]);
+  }
+  sfree (databases);
+  databases_num = 0;
+
+  for (i = 0; i < queries_num; i++)
+    cdbi_query_free (queries[i]);
+  sfree (queries);
+  queries_num = 0;
+
+  return (0);
+} /* }}} int cdbi_shutdown */
+
+void module_register (void) /* {{{ */
+{
+  plugin_register_complex_config ("dbi", cdbi_config);
+  plugin_register_init ("dbi", cdbi_init);
+  plugin_register_read ("dbi", cdbi_read);
+  plugin_register_shutdown ("dbi", cdbi_shutdown);
+} /* }}} void module_register */
+
+/*
+ * vim: shiftwidth=2 softtabstop=2 et fdm=marker
+ */
index f071379..e2b0020 100644 (file)
 #include <dirent.h>
 #include <fnmatch.h>
 
+#define FC_RECURSIVE 1
+
 struct fc_directory_conf_s
 {
   char *path;
   char *instance;
 
+  int options;
+
   /* Data counters */
   uint64_t files_num;
   uint64_t files_size;
@@ -307,6 +311,25 @@ static int fc_config_add_dir_size (fc_directory_conf_t *dir,
   return (0);
 } /* int fc_config_add_dir_size */
 
+static int fc_config_add_dir_recursive (fc_directory_conf_t *dir,
+    oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+  {
+    WARNING ("filecount plugin: The `Recursive' config options needs exactly "
+        "one boolean argument.");
+    return (-1);
+  }
+
+  if (ci->values[0].value.boolean)
+    dir->options |= FC_RECURSIVE;
+  else
+    dir->options &= ~FC_RECURSIVE;
+
+  return (0);
+} /* int fc_config_add_dir_recursive */
+
 static int fc_config_add_dir (oconfig_item_t *ci)
 {
   fc_directory_conf_t *dir;
@@ -338,6 +361,8 @@ static int fc_config_add_dir (oconfig_item_t *ci)
 
   fc_config_set_instance (dir, dir->path);
 
+  dir->options = FC_RECURSIVE;
+
   dir->name = NULL;
   dir->mtime = 0;
   dir->size = 0;
@@ -355,6 +380,8 @@ static int fc_config_add_dir (oconfig_item_t *ci)
       status = fc_config_add_dir_mtime (dir, option);
     else if (strcasecmp ("Size", option->key) == 0)
       status = fc_config_add_dir_size (dir, option);
+    else if (strcasecmp ("Recursive", option->key) == 0)
+      status = fc_config_add_dir_recursive (dir, option);
     else
     {
       WARNING ("filecount plugin: fc_config_add_dir: "
@@ -447,7 +474,7 @@ static int fc_read_dir_callback (const char *dirname, const char *filename,
     return (-1);
   }
 
-  if (S_ISDIR (statbuf.st_mode))
+  if (S_ISDIR (statbuf.st_mode) && (dir->options & FC_RECURSIVE))
   {
     status = walk_directory (abs_path, fc_read_dir_callback, dir);
     return (status);
diff --git a/src/filter_ignore.c b/src/filter_ignore.c
new file mode 100644 (file)
index 0000000..1958884
--- /dev/null
@@ -0,0 +1,147 @@
+/**
+ * collectd - src/entropy.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 "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_ignorelist.h"
+
+/*
+ * Variables
+ */
+static ignorelist_t *il_host   = NULL;
+static ignorelist_t *il_plugin = NULL;
+static ignorelist_t *il_type   = NULL;
+
+static const char *config_keys[] =
+{
+  "IgnoreHost",
+  "IgnorePlugin",
+  "IgnoreType"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+/*
+ * Functions
+ */
+static int ignorelist_add_create (ignorelist_t **il_ptr, const char *entry)
+{
+  ignorelist_t *il;
+  int status;
+
+  il = *il_ptr;
+
+  if (il == NULL)
+  {
+    il = ignorelist_create (/* ignore = */ 0);
+    if (il == NULL)
+    {
+      ERROR ("filter_ignore plugin: ignorelist_create failed.");
+      return (-1);
+    }
+    *il_ptr = il;
+  }
+
+  status = ignorelist_add (il, entry);
+  if (status != 0)
+  {
+    ERROR ("filter_ignore plugin: ignorelist_add failed with error %i.",
+        status);
+    return (status);
+  }
+
+  return (0);
+} /* int ignorelist_add_create */
+
+static int fi_config (const char *key, const char *value)
+{
+  int status;
+
+  status = 0;
+
+  if (strcasecmp ("IgnoreHost", key) == 0)
+    status = ignorelist_add_create (&il_host, value);
+  else if (strcasecmp ("IgnorePlugin", key) == 0)
+    status = ignorelist_add_create (&il_plugin, value);
+  else if (strcasecmp ("IgnoreType", key) == 0)
+    status = ignorelist_add_create (&il_type, value);
+  else
+    return (-1);
+    
+  if (status < 0)
+    status = status * (-1);
+
+  return (status);
+} /* int fi_config */
+
+static int fi_filter (const data_set_t *ds, value_list_t *vl)
+{
+  int status;
+
+  if (il_host != NULL)
+  {
+    status = ignorelist_match (il_host, vl->host);
+    if (status != 0)
+      return (FILTER_IGNORE);
+  }
+
+  if (il_plugin != NULL)
+  {
+    char buffer[2 * DATA_MAX_NAME_LEN];
+
+    if (vl->plugin_instance[0] == 0)
+      sstrncpy (buffer, vl->plugin, sizeof (buffer));
+    else
+      ssnprintf (buffer, sizeof (buffer), "%s-%s",
+          vl->plugin, vl->plugin_instance);
+
+    status = ignorelist_match (il_plugin, buffer);
+    if (status != 0)
+      return (FILTER_IGNORE);
+  }
+
+  if (il_type != NULL)
+  {
+    char buffer[2 * DATA_MAX_NAME_LEN];
+
+    if (vl->type_instance[0] == 0)
+      sstrncpy (buffer, vl->type, sizeof (buffer));
+    else
+      ssnprintf (buffer, sizeof (buffer), "%s-%s",
+          vl->type, vl->type_instance);
+
+    status = ignorelist_match (il_type, buffer);
+    if (status != 0)
+      return (FILTER_IGNORE);
+  }
+
+  return (0);
+} /* int fi_filter */
+
+void module_register (void)
+{
+  plugin_register_config ("filter_ignore", fi_config,
+      config_keys, config_keys_num);
+  plugin_register_filter ("filter_ignore", fi_filter);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/filter_pcre.c b/src/filter_pcre.c
new file mode 100644 (file)
index 0000000..7b223e7
--- /dev/null
@@ -0,0 +1,426 @@
+/**
+ * collectd - src/filter_pcre.c
+ * Copyright (C) 2008  Sebastian Harl
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Sebastian Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This module allows to filter and rewrite value lists based on
+ * Perl-compatible regular expressions.
+ */
+
+#include "collectd.h"
+#include "configfile.h"
+#include "plugin.h"
+#include "common.h"
+
+#include "utils_subst.h"
+
+#include <pcre.h>
+
+#define log_err(...) ERROR ("filter_pcre: " __VA_ARGS__)
+#define log_warn(...) WARNING ("filter_pcre: " __VA_ARGS__)
+
+/*
+ * private data types
+ */
+
+typedef struct {
+       /* regular expression */
+       pcre       *re;
+       const char *re_str;
+
+       /* extra information from studying the pattern */
+       pcre_extra *extra;
+
+       /* replacment text for string substitution */
+       const char *replacement;
+} c_pcre_t;
+
+#define C_PCRE_INIT(regex) do { \
+               (regex).re     = NULL; \
+               (regex).re_str = NULL; \
+               (regex).extra  = NULL; \
+               (regex).replacement = NULL; \
+       } while (0)
+
+#define C_PCRE_FREE(regex) do { \
+               pcre_free ((regex).re); \
+               free ((void *)(regex).re_str); \
+               pcre_free ((regex).extra); \
+               free ((void *)(regex).replacement); \
+               C_PCRE_INIT (regex); \
+       } while (0)
+
+typedef struct {
+       c_pcre_t host;
+       c_pcre_t plugin;
+       c_pcre_t plugin_instance;
+       c_pcre_t type;
+       c_pcre_t type_instance;
+
+       int action;
+} regex_t;
+
+typedef struct {
+       int vec[30];
+       int status;
+} ovec_t;
+
+typedef struct {
+       ovec_t host;
+       ovec_t plugin;
+       ovec_t plugin_instance;
+       ovec_t type;
+       ovec_t type_instance;
+} ovectors_t;
+
+/*
+ * private variables
+ */
+
+static regex_t *regexes     = NULL;
+static int      regexes_num = 0;
+
+/*
+ * internal helper functions
+ */
+
+/* returns true if string matches the regular expression */
+static int c_pcre_match (c_pcre_t *re, const char *string, ovec_t *ovec)
+{
+       if ((NULL == re) || (NULL == re->re))
+               return 1;
+
+       if (NULL == string)
+               string = "";
+
+       ovec->status = pcre_exec (re->re,
+                       /* extra       = */ re->extra,
+                       /* subject     = */ string,
+                       /* length      = */ strlen (string),
+                       /* startoffset = */ 0,
+                       /* options     = */ 0,
+                       /* ovector     = */ ovec->vec,
+                       /* ovecsize    = */ STATIC_ARRAY_SIZE (ovec->vec));
+
+       if (0 <= ovec->status)
+               return 1;
+
+       if (PCRE_ERROR_NOMATCH != ovec->status)
+               log_err ("PCRE matching of string \"%s\" failed with status %d",
+                               string, ovec->status);
+       return 0;
+} /* c_pcre_match */
+
+static int c_pcre_subst (c_pcre_t *re, char *string, size_t strlen,
+               ovec_t *ovec)
+{
+       char buffer[strlen];
+
+       if ((NULL == re) || (NULL == re->replacement))
+               return 0;
+
+       assert (0 <= ovec->status);
+
+       if (NULL == subst (buffer, sizeof (buffer), string,
+                               ovec->vec[0], ovec->vec[1], re->replacement)) {
+               log_err ("Substitution in string \"%s\" (using regex \"%s\" and "
+                               "replacement string \"%s\") failed.",
+                               string, re->re_str, re->replacement);
+               return -1;
+       }
+
+       sstrncpy (string, buffer, strlen);
+       return 0;
+} /* c_pcre_subst */
+
+static regex_t *regex_new (void)
+{
+       regex_t *re;
+
+       ++regexes_num;
+       regexes = (regex_t *)realloc (regexes, regexes_num * sizeof (*regexes));
+       if (NULL == regexes) {
+               log_err ("Out of memory.");
+               exit (5);
+       }
+
+       re = regexes + (regexes_num - 1);
+
+       C_PCRE_INIT (re->host);
+       C_PCRE_INIT (re->plugin);
+       C_PCRE_INIT (re->plugin_instance);
+       C_PCRE_INIT (re->type);
+       C_PCRE_INIT (re->type_instance);
+
+       re->action = 0;
+       return re;
+} /* regex_new */
+
+static void regex_delete (regex_t *re)
+{
+       if (NULL == re)
+               return;
+
+       C_PCRE_FREE (re->host);
+       C_PCRE_FREE (re->plugin);
+       C_PCRE_FREE (re->plugin_instance);
+       C_PCRE_FREE (re->type);
+       C_PCRE_FREE (re->type_instance);
+
+       re->action = 0;
+} /* regex_delete */
+
+/* returns true if the value list matches the regular expression */
+static int regex_match (regex_t *re, value_list_t *vl, ovectors_t *ovectors)
+{
+       int matches = 0;
+
+       if (NULL == re)
+               return 1;
+
+       if (c_pcre_match (&re->host, vl->host, &ovectors->host))
+               ++matches;
+
+       if (c_pcre_match (&re->plugin, vl->plugin, &ovectors->plugin))
+               ++matches;
+
+       if (c_pcre_match (&re->plugin_instance, vl->plugin_instance,
+                               &ovectors->plugin_instance))
+               ++matches;
+
+       if (c_pcre_match (&re->type, vl->type, &ovectors->type))
+               ++matches;
+
+       if (c_pcre_match (&re->type_instance, vl->type_instance,
+                               &ovectors->type_instance))
+               ++matches;
+
+       if (5 == matches)
+               return 1;
+       return 0;
+} /* regex_match */
+
+static int regex_subst (regex_t *re, value_list_t *vl, ovectors_t *ovectors)
+{
+       if (NULL == re)
+               return 0;
+
+       c_pcre_subst (&re->host, vl->host, sizeof (vl->host),
+                       &ovectors->host);
+       c_pcre_subst (&re->plugin, vl->plugin, sizeof (vl->plugin),
+                       &ovectors->plugin);
+       c_pcre_subst (&re->plugin_instance, vl->plugin_instance,
+                       sizeof (vl->plugin_instance), &ovectors->plugin_instance);
+       c_pcre_subst (&re->type, vl->type, sizeof (vl->type),
+                       &ovectors->type);
+       c_pcre_subst (&re->type_instance, vl->type_instance,
+                       sizeof (vl->type_instance), &ovectors->type_instance);
+       return 0;
+} /* regex_subst */
+
+/*
+ * interface to collectd
+ */
+
+static int c_pcre_filter (const data_set_t *ds, value_list_t *vl)
+{
+       int i;
+
+       ovectors_t ovectors;
+
+       for (i = 0; i < regexes_num; ++i)
+               if (regex_match (regexes + i, vl, &ovectors)) {
+                       regex_subst (regexes + i, vl, &ovectors);
+                       return regexes[i].action;
+               }
+       return 0;
+} /* c_pcre_filter */
+
+static int c_pcre_shutdown (void)
+{
+       int i;
+
+       plugin_unregister_filter ("filter_pcre");
+       plugin_unregister_shutdown ("filter_pcre");
+
+       for (i = 0; i < regexes_num; ++i)
+               regex_delete (regexes + i);
+
+       sfree (regexes);
+       regexes_num = 0;
+       return 0;
+} /* c_pcre_shutdown */
+
+static int config_set_regex (c_pcre_t *re, oconfig_item_t *ci)
+{
+       const char *pattern;
+       const char *errptr;
+       int erroffset;
+
+       if ((0 != ci->children_num) || (1 != ci->values_num)
+                       || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+               log_err ("<RegEx>: %s expects a single string argument.", ci->key);
+               return 1;
+       }
+
+       pattern = ci->values[0].value.string;
+
+       re->re = pcre_compile (pattern,
+                       /* options   = */ 0,
+                       /* errptr    = */ &errptr,
+                       /* erroffset = */ &erroffset,
+                       /* tableptr  = */ NULL);
+
+       if (NULL == re->re) {
+               log_err ("<RegEx>: PCRE compilation of pattern \"%s\" failed "
+                               "at offset %d: %s", pattern, erroffset, errptr);
+               return 1;
+       }
+
+       re->re_str = sstrdup (pattern);
+
+       re->extra = pcre_study (re->re,
+                       /* options = */ 0,
+                       /* errptr  = */ &errptr);
+
+       if (NULL != errptr) {
+               log_err ("<RegEx>: PCRE studying of pattern \"%s\" failed: %s",
+                               pattern, errptr);
+               return 1;
+       }
+       return 0;
+} /* config_set_regex */
+
+static int config_set_replacement (c_pcre_t *re, oconfig_item_t *ci)
+{
+       if ((0 != ci->children_num) || (1 != ci->values_num)
+                       || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+               log_err ("<RegEx>: %s expects a single string argument.", ci->key);
+               return 1;
+       }
+
+       if (NULL == re->re) {
+               log_err ("<RegEx>: %s without an appropriate regex (%s) "
+                               "is not allowed.", ci->key, ci->key + strlen ("Substitute"));
+               return 1;
+       }
+
+       re->replacement = sstrdup (ci->values[0].value.string);
+       return 0;
+} /* config_set_replacement */
+
+static int config_set_action (int *action, oconfig_item_t *ci)
+{
+       const char *action_str;
+
+       if ((0 != ci->children_num) || (1 != ci->values_num)
+                       || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+               log_err ("<RegEx>: Action expects a single string argument.");
+               return 1;
+       }
+
+       action_str = ci->values[0].value.string;
+
+       if (0 == strcasecmp (action_str, "NoWrite"))
+               *action |= FILTER_NOWRITE;
+       else if (0 == strcasecmp (action_str, "NoThresholdCheck"))
+               *action |= FILTER_NOTHRESHOLD_CHECK;
+       else if (0 == strcasecmp (action_str, "Ignore"))
+               *action |= FILTER_IGNORE;
+       else
+               log_warn ("<Regex>: Ignoring unknown action \"%s\".", action_str);
+       return 0;
+} /* config_set_action */
+
+static int c_pcre_config_regex (oconfig_item_t *ci)
+{
+       regex_t *re;
+       int i;
+
+       if (0 != ci->values_num) {
+               log_err ("<RegEx> expects no arguments.");
+               return 1;
+       }
+
+       re = regex_new ();
+
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *c = ci->children + i;
+               int status = 0;
+
+               if (0 == strcasecmp (c->key, "Host"))
+                       status = config_set_regex (&re->host, c);
+               else if (0 == strcasecmp (c->key, "Plugin"))
+                       status = config_set_regex (&re->plugin, c);
+               else if (0 == strcasecmp (c->key, "PluginInstance"))
+                       status = config_set_regex (&re->plugin_instance, c);
+               else if (0 == strcasecmp (c->key, "Type"))
+                       status = config_set_regex (&re->type, c);
+               else if (0 == strcasecmp (c->key, "TypeInstance"))
+                       status = config_set_regex (&re->type_instance, c);
+               else if (0 == strcasecmp (c->key, "Action"))
+                       status = config_set_action (&re->action, c);
+               else if (0 == strcasecmp (c->key, "SubstituteHost"))
+                       status = config_set_replacement (&re->host, c);
+               else if (0 == strcasecmp (c->key, "SubstitutePlugin"))
+                       status = config_set_replacement (&re->plugin, c);
+               else if (0 == strcasecmp (c->key, "SubstitutePluginInstance"))
+                       status = config_set_replacement (&re->plugin_instance, c);
+               else if (0 == strcasecmp (c->key, "SubstituteType"))
+                       status = config_set_replacement (&re->type, c);
+               else if (0 == strcasecmp (c->key, "SubstituteTypeInstance"))
+                       status = config_set_replacement (&re->type_instance, c);
+               else
+                       log_warn ("<RegEx>: Ignoring unknown config key \"%s\".", c->key);
+
+               if (0 != status) {
+                       log_err ("Ignoring regular expression definition.");
+                       regex_delete (re);
+                       --regexes_num;
+               }
+       }
+       return 0;
+} /* c_pcre_config_regex */
+
+static int c_pcre_config (oconfig_item_t *ci)
+{
+       int i;
+
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *c = ci->children + i;
+
+               if (0 == strcasecmp (c->key, "RegEx"))
+                       c_pcre_config_regex (c);
+               else
+                       log_warn ("Ignoring unknown config key \"%s\".", c->key);
+       }
+
+       plugin_register_filter ("filter_pcre", c_pcre_filter);
+       plugin_register_shutdown ("filter_pcre", c_pcre_shutdown);
+       return 0;
+} /* c_pcre_config */
+
+void module_register (void)
+{
+       plugin_register_complex_config ("filter_pcre", c_pcre_config);
+} /* module_register */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
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 ffa9887..a8ed93c 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 \
                xtables.h libxtc.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 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 */
diff --git a/src/oracle.c b/src/oracle.c
new file mode 100644 (file)
index 0000000..fa7e72f
--- /dev/null
@@ -0,0 +1,1027 @@
+/**
+ * collectd - src/oracle.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
+ *
+ * Linking src/oracle.c ("the oracle plugin") statically or dynamically with
+ * other modules is making a combined work based on the oracle plugin. Thus,
+ * the terms and conditions of the GNU General Public License cover the whole
+ * combination.
+ *
+ * In addition, as a special exception, the copyright holders of the oracle
+ * plugin give you permission to combine the oracle plugin with free software
+ * programs or libraries that are released under the GNU LGPL and with code
+ * included in the standard release of the Oracle® Call Interface (OCI) under
+ * the Oracle® Technology Network (OTN) License (or modified versions of such
+ * code, with unchanged license). You may copy and distribute such a system
+ * following the terms of the GNU GPL for the oracle plugin and the licenses of
+ * the other code concerned.
+ *
+ * Note that people who make modified versions of the oracle plugin are not
+ * obligated to grant this special exception for their modified versions; it is
+ * their choice whether to do so. The GNU General Public License gives
+ * permission to release a modified version without this exception; this
+ * exception also makes it possible to release a modified version which carries
+ * forward this exception. However, without this exception the OTN License does
+ * not allow linking with code licensed under the GNU General Public License.
+ *
+ * Oracle® is a registered trademark of Oracle Corporation and/or its
+ * affiliates. Other names may be trademarks of their respective owners.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at noris.net>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <oci.h>
+
+/*
+ * Data types
+ */
+struct o_query_s
+{
+  char    *name;
+  char    *statement;
+  char    *type;
+  char   **instances;
+  size_t   instances_num;
+  char   **values;
+  size_t   values_num;
+
+  OCIStmt *oci_statement;
+};
+typedef struct o_query_s o_query_t;
+
+struct o_database_s
+{
+  char *name;
+  char *connect_id;
+  char *username;
+  char *password;
+
+  o_query_t **queries;
+  size_t      queries_num;
+
+  OCISvcCtx *oci_service_context;
+};
+typedef struct o_database_s o_database_t;
+
+/*
+ * Global variables
+ */
+static o_query_t    **queries       = NULL;
+static size_t         queries_num   = 0;
+static o_database_t **databases     = NULL;
+static size_t         databases_num = 0;
+
+OCIEnv   *oci_env = NULL;
+OCIError *oci_error = NULL;
+
+/*
+ * Functions
+ */
+static void o_report_error (const char *where, /* {{{ */
+    const char *what, OCIError *eh)
+{
+  char buffer[2048];
+  sb4 error_code;
+  int status;
+
+  status = OCIErrorGet (eh, /* record number = */ 1,
+      /* sqlstate = */ NULL,
+      &error_code,
+      (text *) &buffer[0],
+      (ub4) sizeof (buffer),
+      OCI_HTYPE_ERROR);
+  buffer[sizeof (buffer) - 1] = 0;
+
+  if (status == OCI_SUCCESS)
+  {
+    size_t buffer_length;
+
+    buffer_length = strlen (buffer);
+    while ((buffer_length > 0) && (buffer[buffer_length - 1] < 32))
+    {
+      buffer_length--;
+      buffer[buffer_length] = 0;
+    }
+
+    ERROR ("oracle plugin: %s: %s failed: %s",
+        where, what, buffer);
+  }
+  else
+  {
+    ERROR ("oracle plugin: %s: %s failed. Additionally, OCIErrorGet failed with status %i.",
+        where, what, status);
+  }
+} /* }}} void o_report_error */
+
+static void o_query_free (o_query_t *q) /* {{{ */
+{
+  size_t i;
+
+  if (q == NULL)
+    return;
+
+  sfree (q->name);
+  sfree (q->statement);
+  sfree (q->type);
+
+  for (i = 0; i < q->instances_num; i++)
+    sfree (q->instances[i]);
+  sfree (q->instances);
+
+  for (i = 0; i < q->values_num; i++)
+    sfree (q->values[i]);
+  sfree (q->values);
+
+  sfree (q);
+} /* }}} void o_query_free */
+
+static void o_database_free (o_database_t *db) /* {{{ */
+{
+  if (db == NULL)
+    return;
+
+  sfree (db->name);
+  sfree (db->connect_id);
+  sfree (db->username);
+  sfree (db->password);
+  sfree (db->queries);
+
+  sfree (db);
+} /* }}} void o_database_free */
+
+/* Configuration handling functions {{{
+ *
+ * <Plugin oracle>
+ *   <Query "plugin_instance0">
+ *     Statement "SELECT name, value FROM table"
+ *     Type "gauge"
+ *     InstancesFrom "name"
+ *     ValuesFrom "value"
+ *   </Query>
+ *     
+ *   <Database "plugin_instance1">
+ *     ConnectID "db01"
+ *     Username "oracle"
+ *     Password "secret"
+ *     Query "plugin_instance0"
+ *   </Database>
+ * </Plugin>
+ */
+
+static int o_config_set_string (char **ret_string, /* {{{ */
+    oconfig_item_t *ci)
+{
+  char *string;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("oracle plugin: The `%s' config option "
+        "needs exactly one string argument.", ci->key);
+    return (-1);
+  }
+
+  string = strdup (ci->values[0].value.string);
+  if (string == NULL)
+  {
+    ERROR ("oracle plugin: strdup failed.");
+    return (-1);
+  }
+
+  if (*ret_string != NULL)
+    free (*ret_string);
+  *ret_string = string;
+
+  return (0);
+} /* }}} int o_config_set_string */
+
+static int o_config_add_string (char ***ret_array, /* {{{ */
+    size_t *ret_array_len, oconfig_item_t *ci)
+{
+  char **array;
+  size_t array_len;
+  int i;
+
+  if (ci->values_num < 1)
+  {
+    WARNING ("oracle plugin: The `%s' config option "
+        "needs at least one argument.", ci->key);
+    return (-1);
+  }
+
+  for (i = 0; i < ci->values_num; i++)
+  {
+    if (ci->values[i].type != OCONFIG_TYPE_STRING)
+    {
+      WARNING ("oracle plugin: Argument %i to the `%s' option "
+          "is not a string.", i + 1, ci->key);
+      return (-1);
+    }
+  }
+
+  array_len = *ret_array_len;
+  array = (char **) realloc (*ret_array,
+      sizeof (char *) * (array_len + ci->values_num));
+  if (array == NULL)
+  {
+    ERROR ("oracle plugin: realloc failed.");
+    return (-1);
+  }
+  *ret_array = array;
+
+  for (i = 0; i < ci->values_num; i++)
+  {
+    array[array_len] = strdup (ci->values[i].value.string);
+    if (array[array_len] == NULL)
+    {
+      ERROR ("oracle plugin: strdup failed.");
+      *ret_array_len = array_len;
+      return (-1);
+    }
+    array_len++;
+  }
+
+  *ret_array_len = array_len;
+  return (0);
+} /* }}} int o_config_add_string */
+
+static int o_config_add_query (oconfig_item_t *ci) /* {{{ */
+{
+  o_query_t *q;
+  int status;
+  int i;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("oracle plugin: The `Query' block "
+        "needs exactly one string argument.");
+    return (-1);
+  }
+
+  q = (o_query_t *) malloc (sizeof (*q));
+  if (q == NULL)
+  {
+    ERROR ("oracle plugin: malloc failed.");
+    return (-1);
+  }
+  memset (q, 0, sizeof (*q));
+
+  status = o_config_set_string (&q->name, ci);
+  if (status != 0)
+  {
+    sfree (q);
+    return (status);
+  }
+
+  /* Fill the `o_query_t' structure.. */
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Statement", child->key) == 0)
+      status = o_config_set_string (&q->statement, child);
+    else if (strcasecmp ("Type", child->key) == 0)
+      status = o_config_set_string (&q->type, child);
+    else if (strcasecmp ("InstancesFrom", child->key) == 0)
+      status = o_config_add_string (&q->instances, &q->instances_num, child);
+    else if (strcasecmp ("ValuesFrom", child->key) == 0)
+      status = o_config_add_string (&q->values, &q->values_num, child);
+    else
+    {
+      WARNING ("oracle plugin: Option `%s' not allowed here.", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  /* Check that all necessary options have been given. */
+  while (status == 0)
+  {
+    if (q->statement == NULL)
+    {
+      WARNING ("oracle plugin: `Statement' not given for query `%s'", q->name);
+      status = -1;
+    }
+    if (q->type == NULL)
+    {
+      WARNING ("oracle plugin: `Type' not given for query `%s'", q->name);
+      status = -1;
+    }
+    if (q->instances == NULL)
+    {
+      WARNING ("oracle plugin: `InstancesFrom' not given for query `%s'", q->name);
+      status = -1;
+    }
+    if (q->values == NULL)
+    {
+      WARNING ("oracle plugin: `ValuesFrom' not given for query `%s'", q->name);
+      status = -1;
+    }
+
+    break;
+  } /* while (status == 0) */
+
+  /* If all went well, add this query to the list of queries within the
+   * database structure. */
+  if (status == 0)
+  {
+    o_query_t **temp;
+
+    temp = (o_query_t **) realloc (queries,
+        sizeof (*queries) * (queries_num + 1));
+    if (temp == NULL)
+    {
+      ERROR ("oracle plugin: realloc failed");
+      status = -1;
+    }
+    else
+    {
+      queries = temp;
+      queries[queries_num] = q;
+      queries_num++;
+    }
+  }
+
+  if (status != 0)
+  {
+    o_query_free (q);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int o_config_add_query */
+
+static int o_config_add_database_query (o_database_t *db, /* {{{ */
+    oconfig_item_t *ci)
+{
+  o_query_t *q;
+  o_query_t **temp;
+  size_t i;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("oracle plugin: The `Query' config option "
+        "needs exactly one string argument.");
+    return (-1);
+  }
+
+  q = NULL;
+  for (i = 0; i < queries_num; i++)
+  {
+    if (strcasecmp (queries[i]->name, ci->values[0].value.string) == 0)
+    {
+      q = queries[i];
+      break;
+    }
+  }
+
+  if (q == NULL)
+  {
+    WARNING ("oracle plugin: Database `%s': Unknown query `%s'. "
+        "Please make sure that the <Query \"%s\"> block comes before "
+        "the <Database \"%s\"> block.",
+        db->name, ci->values[0].value.string,
+        ci->values[0].value.string, db->name);
+    return (-1);
+  }
+
+  temp = (o_query_t **) realloc (db->queries,
+      sizeof (*db->queries) * (db->queries_num + 1));
+  if (temp == NULL)
+  {
+    ERROR ("oracle plugin: realloc failed");
+    return (-1);
+  }
+  else
+  {
+    db->queries = temp;
+    db->queries[db->queries_num] = q;
+    db->queries_num++;
+  }
+
+  return (0);
+} /* }}} int o_config_add_database_query */
+
+static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
+{
+  o_database_t *db;
+  int status;
+  int i;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("oracle plugin: The `Database' block "
+        "needs exactly one string argument.");
+    return (-1);
+  }
+
+  db = (o_database_t *) malloc (sizeof (*db));
+  if (db == NULL)
+  {
+    ERROR ("oracle plugin: malloc failed.");
+    return (-1);
+  }
+  memset (db, 0, sizeof (*db));
+
+  status = o_config_set_string (&db->name, ci);
+  if (status != 0)
+  {
+    sfree (db);
+    return (status);
+  }
+
+  /* Fill the `o_database_t' structure.. */
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("ConnectID", child->key) == 0)
+      status = o_config_set_string (&db->connect_id, child);
+    else if (strcasecmp ("Username", child->key) == 0)
+      status = o_config_set_string (&db->username, child);
+    else if (strcasecmp ("Password", child->key) == 0)
+      status = o_config_set_string (&db->password, child);
+    else if (strcasecmp ("Query", child->key) == 0)
+      status = o_config_add_database_query (db, child);
+    else
+    {
+      WARNING ("oracle plugin: Option `%s' not allowed here.", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  /* Check that all necessary options have been given. */
+  while (status == 0)
+  {
+    if (db->connect_id == NULL)
+    {
+      WARNING ("oracle plugin: `ConnectID' not given for query `%s'", db->name);
+      status = -1;
+    }
+    if (db->username == NULL)
+    {
+      WARNING ("oracle plugin: `Username' not given for query `%s'", db->name);
+      status = -1;
+    }
+    if (db->password == NULL)
+    {
+      WARNING ("oracle plugin: `Password' not given for query `%s'", db->name);
+      status = -1;
+    }
+
+    break;
+  } /* while (status == 0) */
+
+  /* If all went well, add this query to the list of queries within the
+   * database structure. */
+  if (status == 0)
+  {
+    o_database_t **temp;
+
+    temp = (o_database_t **) realloc (databases,
+        sizeof (*databases) * (databases_num + 1));
+    if (temp == NULL)
+    {
+      ERROR ("oracle plugin: realloc failed");
+      status = -1;
+    }
+    else
+    {
+      databases = temp;
+      databases[databases_num] = db;
+      databases_num++;
+    }
+  }
+
+  if (status != 0)
+  {
+    o_database_free (db);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int o_config_add_database */
+
+static int o_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+    if (strcasecmp ("Query", child->key) == 0)
+      o_config_add_query (child);
+    else if (strcasecmp ("Database", child->key) == 0)
+      o_config_add_database (child);
+    else
+    {
+      WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
+    }
+  } /* for (ci->children) */
+
+  return (0);
+} /* }}} int o_config */
+
+/* }}} End of configuration handling functions */
+
+static int o_init (void) /* {{{ */
+{
+  int status;
+
+  if (oci_env != NULL)
+    return (0);
+
+  status = OCIEnvCreate (&oci_env,
+      /* mode = */ OCI_THREADED,
+      /* context        = */ NULL,
+      /* malloc         = */ NULL,
+      /* realloc        = */ NULL,
+      /* free           = */ NULL,
+      /* user_data_size = */ 0,
+      /* user_data_ptr  = */ NULL);
+  if (status != 0)
+  {
+    ERROR ("oracle plugin: OCIEnvCreate failed with status %i.", status);
+    return (-1);
+  }
+
+  status = OCIHandleAlloc (oci_env, (void *) &oci_error, OCI_HTYPE_ERROR,
+      /* user_data_size = */ 0, /* user_data = */ NULL);
+  if (status != OCI_SUCCESS)
+  {
+    ERROR ("oracle plugin: OCIHandleAlloc (OCI_HTYPE_ERROR) failed "
+        "with status %i.", status);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int o_init */
+
+static void o_submit (o_database_t *db, o_query_t *q, /* {{{ */
+    const data_set_t *ds, char **buffer_instances, char **buffer_values)
+{
+  value_list_t vl = VALUE_LIST_INIT;
+  size_t i;
+
+  assert (((size_t) ds->ds_num) == q->values_num);
+
+  vl.values = (value_t *) malloc (sizeof (value_t) * q->values_num);
+  if (vl.values == NULL)
+  {
+    ERROR ("oracle plugin: malloc failed.");
+    return;
+  }
+  vl.values_len = ds->ds_num;
+
+  for (i = 0; i < q->values_num; i++)
+  {
+    char *endptr;
+
+    endptr = NULL;
+    errno = 0;
+    if (ds->ds[i].type == DS_TYPE_COUNTER)
+      vl.values[i].counter = (counter_t) strtoll (buffer_values[i],
+          &endptr, /* base = */ 0);
+    else if (ds->ds[i].type == DS_TYPE_GAUGE)
+      vl.values[i].gauge = (gauge_t) strtod (buffer_values[i], &endptr);
+    else
+      errno = EINVAL;
+
+    if ((endptr == buffer_values[i]) || (errno != 0))
+    {
+      WARNING ("oracle plugin: o_submit: Parsing `%s' as %s failed.",
+          buffer_values[i],
+          (ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
+      vl.values[i].gauge = NAN;
+    }
+  }
+
+  vl.time = time (NULL);
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "oracle", sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, db->name, sizeof (vl.type_instance));
+  sstrncpy (vl.type, q->type, sizeof (vl.type));
+  strjoin (vl.type_instance, sizeof (vl.type_instance),
+      buffer_instances, q->instances_num, "-");
+  vl.type_instance[sizeof (vl.type_instance) - 1] = 0;
+
+  plugin_dispatch_values (&vl);
+} /* }}} void o_submit */
+
+static int o_read_database_query (o_database_t *db, /* {{{ */
+    o_query_t *q)
+{
+  const data_set_t *ds;
+  ub4 param_counter; /* == number of columns */
+  int status;
+  size_t i;
+  ub4 j;
+
+  /* Scratch area for OCI to write values to */
+  char **buffer_instances;
+  char **buffer_values;
+
+  /* List of indizes of the instance and value columns. Only used for error
+   * checking. */
+  size_t *index_instances;
+  size_t *index_values;
+
+  /* List of `OCIDefine' pointers. These defines map columns to the buffer
+ * space declared above. */
+  OCIDefine **oci_defines;
+
+  ds = plugin_get_ds (q->type); /* {{{ */
+  if (ds == NULL)
+  {
+    WARNING ("oracle plugin: o_read_database_query (%s, %s): "
+        "plugin_get_ds (%s) failed. Please check if the type exists, "
+        "see types.db(5).",
+        db->name, q->name, q->type);
+    return (-1);
+  } /* }}} */
+
+  if (((size_t) ds->ds_num) != q->values_num)
+  {
+    ERROR ("oracle plugin: o_read_database_query (%s, %s): "
+        "The query `%s' uses the type `%s' which requires exactly "
+        "%i value%s, but you specified %zu value-column%s. "
+        "See types.db(5) for details.",
+        db->name, q->name,
+        q->name, q->type,
+        ds->ds_num, (ds->ds_num == 1) ? "" : "s",
+        q->values_num, (q->values_num == 1) ? "" : "s");
+    return (-1);
+  }
+
+  /* Prepare the statement */
+  if (q->oci_statement == NULL) /* {{{ */
+  {
+    status = OCIHandleAlloc (oci_env, (void *) &q->oci_statement,
+        OCI_HTYPE_STMT, /* user_data_size = */ 0, /* user_data = */ NULL);
+    if (status != OCI_SUCCESS)
+    {
+      o_report_error ("o_read_database_query", "OCIHandleAlloc", oci_error);
+      q->oci_statement = NULL;
+      return (-1);
+    }
+
+    status = OCIStmtPrepare (q->oci_statement, oci_error,
+        (text *) q->statement, (ub4) strlen (q->statement),
+        /* language = */ OCI_NTV_SYNTAX,
+        /* mode     = */ OCI_DEFAULT);
+    if (status != OCI_SUCCESS)
+    {
+      o_report_error ("o_read_database_query", "OCIStmtPrepare", oci_error);
+      OCIHandleFree (q->oci_statement, OCI_HTYPE_STMT);
+      q->oci_statement = NULL;
+      return (-1);
+    }
+    assert (q->oci_statement != NULL);
+  } /* }}} */
+
+  /* Execute the statement */
+  status = OCIStmtExecute (db->oci_service_context, /* {{{ */
+      q->oci_statement,
+      oci_error,
+      /* iters = */ 0,
+      /* rowoff = */ 0,
+      /* snap_in = */ NULL, /* snap_out = */ NULL,
+      /* mode = */ OCI_DEFAULT);
+  if (status != OCI_SUCCESS)
+  {
+    o_report_error ("o_read_database_query", "OCIStmtExecute", oci_error);
+    ERROR ("oracle plugin: o_read_database_query: "
+        "Failing statement was: %s", q->statement);
+    return (-1);
+  } /* }}} */
+
+  /* Acquire the number of columns returned. */
+  param_counter = 0;
+  status = OCIAttrGet (q->oci_statement, OCI_HTYPE_STMT, /* {{{ */
+      &param_counter, /* size pointer = */ NULL, 
+      OCI_ATTR_PARAM_COUNT, oci_error);
+  if (status != OCI_SUCCESS)
+  {
+    o_report_error ("o_read_database_query", "OCIAttrGet", oci_error);
+    return (-1);
+  } /* }}} */
+
+  /* Allocate the following buffers:
+   * 
+   *  - buffer_instances    q->instances_num x DATA_MAX_NAME_LEN
+   *  - buffer_values       q->values_num    x NUMBER_BUFFER_SIZE
+   *  - index_instances     q->instances_num
+   *  - index_values        q->values_num
+   *  - oci_defines         q->instances_num+q->values_num
+   * {{{ */
+#define NUMBER_BUFFER_SIZE 64
+
+#define FREE_ALL \
+  if (buffer_instances != NULL) { \
+    sfree (buffer_instances[0]); \
+    sfree (buffer_instances); \
+  } \
+  if (buffer_values != NULL) { \
+    sfree (buffer_values[0]); \
+    sfree (buffer_values); \
+  } \
+  sfree (index_instances); \
+  sfree (index_values); \
+  sfree (oci_defines)
+
+#define ALLOC_OR_FAIL(ptr, ptr_size) \
+  do { \
+    size_t alloc_size = (size_t) ((ptr_size)); \
+    (ptr) = malloc (alloc_size); \
+    if ((ptr) == NULL) { \
+      FREE_ALL; \
+      ERROR ("oracle plugin: o_read_database_query: malloc failed."); \
+      return (-1); \
+    } \
+    memset ((ptr), 0, alloc_size); \
+  } while (0)
+
+  /* Initialize everything to NULL so the above works. */
+  buffer_instances = NULL;
+  buffer_values    = NULL;
+  index_instances  = NULL;
+  index_values     = NULL;
+  oci_defines      = NULL;
+
+  ALLOC_OR_FAIL (buffer_instances, q->instances_num * sizeof (char *));
+  ALLOC_OR_FAIL (buffer_instances[0], q->instances_num * DATA_MAX_NAME_LEN
+      * sizeof (char));
+  for (i = 1; i < q->instances_num; i++)
+    buffer_instances[i] = buffer_instances[i - 1] + DATA_MAX_NAME_LEN;
+
+  ALLOC_OR_FAIL (buffer_values, q->values_num * sizeof (char *));
+  ALLOC_OR_FAIL (buffer_values[0], q->values_num * NUMBER_BUFFER_SIZE
+      * sizeof (char));
+  for (i = 1; i < q->values_num; i++)
+    buffer_values[i] = buffer_values[i - 1] + NUMBER_BUFFER_SIZE;
+
+  ALLOC_OR_FAIL (index_instances, q->instances_num * sizeof (size_t));
+  ALLOC_OR_FAIL (index_values, q->values_num * sizeof (size_t));
+
+  ALLOC_OR_FAIL (oci_defines, (q->instances_num + q->values_num)
+      * sizeof (OCIDefine *));
+  /* }}} End of buffer allocations. */
+
+  /* ``Define'' the returned data, i. e. bind the columns to the buffers
+   * returned above. */
+  for (j = 1; j <= param_counter; j++) /* {{{ */
+  {
+    char *column_name;
+    size_t column_name_length;
+    char column_name_copy[DATA_MAX_NAME_LEN];
+    size_t i;
+    OCIParam *oci_param;
+
+    oci_param = NULL;
+
+    status = OCIParamGet (q->oci_statement, OCI_HTYPE_STMT, oci_error,
+        (void *) &oci_param, j);
+    if (status != OCI_SUCCESS)
+    {
+      /* This is probably alright */
+      DEBUG ("oracle plugin: o_read_database_query: status = %#x (= %i);", status, status);
+      o_report_error ("o_read_database_query", "OCIParamGet", oci_error);
+      status = OCI_SUCCESS;
+      break;
+    }
+
+    column_name = NULL;
+    column_name_length = 0;
+    status = OCIAttrGet (oci_param, OCI_DTYPE_PARAM,
+        &column_name, &column_name_length, OCI_ATTR_NAME, oci_error);
+    if (status != OCI_SUCCESS)
+    {
+      o_report_error ("o_read_database_query", "OCIAttrGet (OCI_ATTR_NAME)",
+          oci_error);
+      continue;
+    }
+
+    /* Ensure null-termination. */
+    memset (column_name_copy, 0, sizeof (column_name_copy));
+    if (column_name_length >= sizeof (column_name_copy))
+      column_name_length = sizeof (column_name_copy) - 1;
+    memcpy (column_name_copy, column_name, column_name_length);
+    column_name_copy[column_name_length] = 0;
+
+    DEBUG ("oracle plugin: o_read_database_query: column_name[%u] = %s; column_name_length = %zu;",
+        (unsigned int) j, column_name_copy, column_name_length);
+
+    for (i = 0; i < q->instances_num; i++)
+    {
+      if (strcasecmp (q->instances[i], column_name_copy) != 0)
+        continue;
+
+      status = OCIDefineByPos (q->oci_statement,
+          &oci_defines[i], oci_error, j,
+          buffer_instances[i], DATA_MAX_NAME_LEN, SQLT_STR,
+          NULL, NULL, NULL, OCI_DEFAULT);
+      index_instances[i] = j;
+
+      DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> instances[%zu]",
+          (unsigned int) j, column_name_copy, i);
+      break;
+    }
+
+    for (i = 0; i < q->values_num; i++)
+    {
+      if (strcasecmp (q->values[i], column_name_copy) != 0)
+        continue;
+
+      status = OCIDefineByPos (q->oci_statement,
+          &oci_defines[q->instances_num + i], oci_error, j,
+          buffer_values[i], NUMBER_BUFFER_SIZE, SQLT_STR,
+          NULL, NULL, NULL, OCI_DEFAULT);
+      index_values[i] = j;
+
+      DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> values[%zu]",
+          (unsigned int) j, column_name_copy, i);
+      break;
+    }
+  } /* for (j = 1; j <= param_counter; j++) */
+  /* }}} End of the ``define'' stuff. */
+
+  /* Iterate over all indizes and check that all columns from which we're
+   * supposed to read instances or values have been found. */
+  /* {{{ */
+  status = 0;
+  for (i = 0; i < q->instances_num; i++)
+  {
+    if (index_instances[i] > 0)
+      continue;
+
+    ERROR ("oracle plugin: o_read_database_query (%s, %s): "
+        "Instance %zu of the `%s' query should be read from the column `%s', "
+        "but that column wasn't returned by the SQL statement. Please check "
+        "your configuration.",
+        db->name, q->name, (i + 1), q->name, q->instances[i]);
+    status++;
+  }
+
+  for (i = 0; i < q->values_num; i++)
+  {
+    if (index_values[i] > 0)
+      continue;
+
+    ERROR ("oracle plugin: o_read_database_query (%s, %s): "
+        "Value %zu of the `%s' query should be read from the column `%s', "
+        "but that column wasn't returned by the SQL statement. Please check "
+        "your configuration.",
+        db->name, q->name, (i + 1), q->name, q->values[i]);
+    status++;
+  }
+
+  if (status != 0)
+  {
+    FREE_ALL;
+    return (-1);
+  }
+  /* }}} */
+
+  /* Fetch and handle all the rows that matched the query. */
+  while (42) /* {{{ */
+  {
+    status = OCIStmtFetch2 (q->oci_statement, oci_error,
+        /* nrows = */ 1, /* orientation = */ OCI_FETCH_NEXT,
+        /* fetch offset = */ 0, /* mode = */ OCI_DEFAULT);
+    if (status == OCI_NO_DATA)
+    {
+      status = OCI_SUCCESS;
+      break;
+    }
+    else if ((status != OCI_SUCCESS) && (status != OCI_SUCCESS_WITH_INFO))
+    {
+      o_report_error ("o_read_database_query", "OCIStmtFetch2", oci_error);
+      break;
+    }
+
+    for (i = 0; i < q->instances_num; i++)
+    {
+      DEBUG ("oracle plugin: o_read_database_query: "
+          "buffer_instances[%zu] = %s;",
+           i, buffer_instances[i]);
+    }
+
+    for (i = 0; i < q->values_num; i++)
+    {
+      DEBUG ("oracle plugin: o_read_database_query: "
+          "buffer_values[%zu] = %s;",
+           i, buffer_values[i]);
+    }
+
+    o_submit (db, q, ds, buffer_instances, buffer_values);
+  } /* }}} while (42) */
+
+  /* DEBUG ("oracle plugin: o_read_database_query: This statement succeeded: %s", q->statement); */
+  FREE_ALL;
+
+  return (0);
+#undef FREE_ALL
+#undef ALLOC_OR_FAIL
+} /* }}} int o_read_database_query */
+
+static int o_read_database (o_database_t *db) /* {{{ */
+{
+  size_t i;
+  int status;
+
+  if (db->oci_service_context == NULL)
+  {
+    status = OCILogon (oci_env, oci_error,
+        &db->oci_service_context,
+        (OraText *) db->username, (ub4) strlen (db->username),
+        (OraText *) db->password, (ub4) strlen (db->password),
+        (OraText *) db->connect_id, (ub4) strlen (db->connect_id));
+    if (status != OCI_SUCCESS)
+    {
+      o_report_error ("o_read_database", "OCILogon", oci_error);
+      DEBUG ("oracle plugin: OCILogon (%s): db->oci_service_context = %p;",
+          db->connect_id, db->oci_service_context);
+      db->oci_service_context = NULL;
+      return (-1);
+    }
+    assert (db->oci_service_context != NULL);
+  }
+
+  DEBUG ("oracle plugin: o_read_database: db->connect_id = %s; db->oci_service_context = %p;",
+      db->connect_id, db->oci_service_context);
+
+  for (i = 0; i < db->queries_num; i++)
+    o_read_database_query (db, db->queries[i]);
+
+  return (0);
+} /* }}} int o_read_database */
+
+static int o_read (void) /* {{{ */
+{
+  size_t i;
+
+  for (i = 0; i < databases_num; i++)
+    o_read_database (databases[i]);
+
+  return (0);
+} /* }}} int o_read */
+
+static int o_shutdown (void) /* {{{ */
+{
+  size_t i;
+
+  for (i = 0; i < databases_num; i++)
+    if (databases[i]->oci_service_context != NULL)
+    {
+      OCIHandleFree (databases[i]->oci_service_context, OCI_HTYPE_SVCCTX);
+      databases[i]->oci_service_context = NULL;
+    }
+  
+  for (i = 0; i < queries_num; i++)
+    if (queries[i]->oci_statement != NULL)
+    {
+      OCIHandleFree (queries[i]->oci_statement, OCI_HTYPE_STMT);
+      queries[i]->oci_statement = NULL;
+    }
+  
+  OCIHandleFree (oci_env, OCI_HTYPE_ENV);
+  return (0);
+} /* }}} int o_shutdown */
+
+void module_register (void) /* {{{ */
+{
+  plugin_register_complex_config ("oracle", o_config);
+  plugin_register_init ("oracle", o_init);
+  plugin_register_read ("oracle", o_read);
+  plugin_register_shutdown ("oracle", o_shutdown);
+} /* }}} void module_register */
+
+/*
+ * vim: shiftwidth=2 softtabstop=2 et fdm=marker
+ */
index 400057b..cdfb8d5 100644 (file)
@@ -1806,7 +1806,10 @@ static int perl_config (oconfig_item_t *ci)
                else if (0 == strcasecmp (c->key, "Plugin"))
                        current_status = perl_config_plugin (aTHX_ c);
                else
+               {
                        log_warn ("Ignoring unknown config key \"%s\".", c->key);
+                       current_status = 0;
+               }
 
                /* fatal error - it's up to perl_config_* to clean up */
                if (0 > current_status) {
index 1ec2544..4d503f7 100644 (file)
@@ -55,6 +55,7 @@ typedef struct read_func_s read_func_t;
 static llist_t *list_init;
 static llist_t *list_read;
 static llist_t *list_write;
+static llist_t *list_filter;
 static llist_t *list_flush;
 static llist_t *list_shutdown;
 static llist_t *list_log;
@@ -442,6 +443,12 @@ int plugin_register_write (const char *name,
        return (register_callback (&list_write, name, (void *) callback));
 } /* int plugin_register_write */
 
+int plugin_register_filter (const char *name,
+               int (*callback) (const data_set_t *ds, value_list_t *vl))
+{
+       return (register_callback (&list_filter, name, (void *) callback));
+} /* int plugin_register_filter */
+
 int plugin_register_flush (const char *name,
                int (*callback) (const int timeout, const char *identifier))
 {
@@ -542,6 +549,11 @@ int plugin_unregister_write (const char *name)
        return (plugin_unregister (list_write, name));
 }
 
+int plugin_unregister_filter (const char *name)
+{
+       return (plugin_unregister (list_filter, name));
+}
+
 int plugin_unregister_flush (const char *name)
 {
        return (plugin_unregister (list_flush, name));
@@ -718,12 +730,13 @@ 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;
        llentry_t *le;
 
+       int filter = 0;
+
        if ((vl == NULL) || (*vl->type == '\0')) {
                ERROR ("plugin_dispatch_values: Invalid value list.");
                return (-1);
@@ -785,15 +798,36 @@ int plugin_dispatch_values (value_list_t *vl)
        escape_slashes (vl->type, sizeof (vl->type));
        escape_slashes (vl->type_instance, sizeof (vl->type_instance));
 
+       le = llist_head (list_filter);
+       while (le != NULL)
+       {
+               int (*filter_callback) (const data_set_t *, value_list_t *) =
+                               (int (*) (const data_set_t *, value_list_t *)) le->value;
+
+               filter |= (*filter_callback) (ds, vl);
+
+               if (filter == FILTER_IGNORE)
+                       return (-1);
+
+               le = le->next;
+       }
+
        /* Update the value cache */
        uc_update (ds, vl);
-       ut_check_threshold (ds, vl);
+
+       if ((filter & FILTER_NOTHRESHOLD_CHECK) == 0)
+               ut_check_threshold (ds, vl);
+
+       if (filter & FILTER_NOWRITE)
+               return (0);
 
        le = llist_head (list_write);
        while (le != NULL)
        {
-               callback = (int (*) (const data_set_t *, const value_list_t *)) le->value;
-               (*callback) (ds, vl);
+               int (*write_callback) (const data_set_t *, const value_list_t *) =
+                               (int (*) (const data_set_t *, const value_list_t *)) le->value;
+
+               (*write_callback) (ds, vl);
 
                le = le->next;
        }
index 3ffde46..b90811c 100644 (file)
 #define NOTIF_WARNING 2
 #define NOTIF_OKAY    4
 
+#define FILTER_NOWRITE 1
+#define FILTER_NOTHRESHOLD_CHECK 2
+/* FILTER_IGNORE has to equal the bitwise or of all other filter flags */
+#define FILTER_IGNORE (FILTER_NOWRITE | FILTER_NOTHRESHOLD_CHECK)
+
 /*
  * Public data types
  */
@@ -196,6 +201,8 @@ int plugin_register_read (const char *name,
                int (*callback) (void));
 int plugin_register_write (const char *name,
                int (*callback) (const data_set_t *ds, const value_list_t *vl));
+int plugin_register_filter (const char *name,
+               int (*callback) (const data_set_t *ds, value_list_t *vl));
 int plugin_register_flush (const char *name,
                int (*callback) (const int timeout, const char *identifier));
 int plugin_register_shutdown (char *name,
@@ -211,6 +218,7 @@ int plugin_unregister_complex_config (const char *name);
 int plugin_unregister_init (const char *name);
 int plugin_unregister_read (const char *name);
 int plugin_unregister_write (const char *name);
+int plugin_unregister_filter (const char *name);
 int plugin_unregister_flush (const char *name);
 int plugin_unregister_shutdown (const char *name);
 int plugin_unregister_data_set (const char *name);
index faad16c..a5bacf4 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..3ba93fe 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;
@@ -116,6 +118,7 @@ static pthread_cond_t  host_cond = PTHREAD_COND_INITIALIZER;
 /*
  * Private functions
  */
+/* Many functions to handle the configuration. {{{ */
 /* First there are many functions which do configuration stuff. It's a big
  * bloated and messy, I'm afraid. */
 
@@ -551,6 +554,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)
@@ -653,7 +657,7 @@ static int csnmp_config (oconfig_item_t *ci)
   return (0);
 } /* int csnmp_config */
 
-/* End of the config stuff. Now the interesting part begins */
+/* }}} End of the config stuff. Now the interesting part begins */
 
 static void csnmp_host_close_session (host_definition_t *host)
 {
@@ -1146,7 +1150,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 +1167,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)
@@ -1626,5 +1635,5 @@ void module_register (void)
 } /* void module_register */
 
 /*
- * vim: shiftwidth=2 softtabstop=2 tabstop=8
+ * vim: shiftwidth=2 softtabstop=2 tabstop=8 fdm=marker
  */
index a3b51dc..67d8f0d 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 dccafd1..6b54ace 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef UTILS_CMD_FLUSH_H
 #define UTILS_CMD_FLUSH_H 1
 
+#include <stdio.h>
+
 int handle_flush (FILE *fh, char *buffer);
 
 #endif /* UTILS_CMD_FLUSH_H */
index 86134cd..ed9ca9a 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef UTILS_CMD_GETVAL_H
 #define UTILS_CMD_GETVAL_H 1
 
+#include <stdio.h>
+
 int handle_getval (FILE *fh, char *buffer);
 
 #endif /* UTILS_CMD_GETVAL_H */
index 73146e7..0c72d67 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef UTILS_CMD_LISTVAL_H
 #define UTILS_CMD_LISTVAL_H 1
 
+#include <stdio.h>
+
 int handle_listval (FILE *fh, char *buffer);
 
 #endif /* UTILS_CMD_LISTVAL_H */
index 8d5475b..7e900b5 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef UTILS_CMD_PUTNOTIF_H
 #define UTILS_CMD_PUTNOTIF_H 1
 
+#include <stdio.h>
+
 int handle_putnotif (FILE *fh, char *buffer);
 
 /* vim: set shiftwidth=2 softtabstop=2 tabstop=8 : */
index 9ba52cc..8460b13 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef UTILS_CMD_PUTVAL_H
 #define UTILS_CMD_PUTVAL_H 1
 
+#include <stdio.h>
+
 int handle_putval (FILE *fh, char *buffer);
 
 #endif /* UTILS_CMD_PUTVAL_H */
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
index b3f08ac..efc7903 100644 (file)
@@ -36,6 +36,7 @@
 #include "config.h"
 
 #include <arpa/nameser.h>
+#include <stdint.h>
 
 #if HAVE_PCAP_H
 # include <pcap.h>
diff --git a/src/utils_rrdcreate.c b/src/utils_rrdcreate.c
new file mode 100644 (file)
index 0000000..8ff025b
--- /dev/null
@@ -0,0 +1,413 @@
+/**
+ * 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;
+  char *filename_copy;
+
+  if ((filename == NULL) || (argv == NULL))
+    return (-EINVAL);
+
+  /* Some versions of librrd don't have the `const' qualifier for the first
+   * argument, so we have to copy the pointer here to avoid warnings. It sucks,
+   * but what else can we do? :(  -octo */
+  filename_copy = strdup (filename);
+  if (filename_copy == NULL)
+  {
+    ERROR ("srrd_create: strdup failed.");
+    return (-ENOMEM);
+  }
+
+  optind = 0; /* bug in librrd? */
+  rrd_clear_error ();
+
+  status = rrd_create_r (filename_copy, pdp_step, last_up,
+      argc, (void *) argv);
+
+  if (status != 0)
+  {
+    WARNING ("rrdtool plugin: rrd_create_r (%s) failed: %s",
+        filename, rrd_get_error ());
+  }
+
+  sfree (filename_copy);
+
+  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..935e4e0
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * 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
+
+#include "plugin.h"
+
+#include <stddef.h>
+
+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 : */
diff --git a/src/utils_subst.c b/src/utils_subst.c
new file mode 100644 (file)
index 0000000..05dc844
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+ * collectd - src/utils_subst.c
+ * Copyright (C) 2008  Sebastian Harl
+ *
+ * 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:
+ *   Sebastian "tokkee" Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This module provides functions for string substitution.
+ */
+
+#include "collectd.h"
+#include "common.h"
+
+char *subst (char *buf, size_t buflen, const char *string, int off1, int off2,
+               const char *replacement)
+{
+       char  *buf_ptr = buf;
+       size_t len     = buflen;
+
+       if ((NULL == buf) || (0 >= buflen) || (NULL == string)
+                       || (0 > off1) || (0 > off2) || (off1 > off2)
+                       || (NULL == replacement))
+               return NULL;
+
+       sstrncpy (buf_ptr, string, (off1 + 1 > buflen) ? buflen : off1 + 1);
+       buf_ptr += off1;
+       len     -= off1;
+
+       if (0 >= len)
+               return buf;
+
+       sstrncpy (buf_ptr, replacement, len);
+       buf_ptr += strlen (replacement);
+       len     -= strlen (replacement);
+
+       if (0 >= len)
+               return buf;
+
+       sstrncpy (buf_ptr, string + off2, len);
+       return buf;
+} /* subst */
+
+char *asubst (const char *string, int off1, int off2, const char *replacement)
+{
+       char *buf;
+       int   len;
+
+       char *ret;
+
+       if ((NULL == string) || (0 > off1) || (0 > off2) || (off1 > off2)
+                       || (NULL ==replacement))
+               return NULL;
+
+       len = off1 + strlen (replacement) + strlen (string) - off2 + 1;
+
+       buf = (char *)malloc (len);
+       if (NULL == buf)
+               return NULL;
+
+       ret = subst (buf, len, string, off1, off2, replacement);
+       if (NULL == ret)
+               free (buf);
+       return ret;
+} /* asubst */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
diff --git a/src/utils_subst.h b/src/utils_subst.h
new file mode 100644 (file)
index 0000000..bba33af
--- /dev/null
@@ -0,0 +1,81 @@
+/**
+ * collectd - src/utils_subst.h
+ * Copyright (C) 2008  Sebastian Harl
+ *
+ * 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:
+ *   Sebastian "tokkee" Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This module provides functions for string substitution.
+ */
+
+#ifndef UTILS_SUBST_H
+#define UTILS_SUBST_H 1
+
+#include <stddef.h>
+
+/*
+ * subst:
+ *
+ * Replace a substring of a string with the specified replacement text. The
+ * resulting string is stored in the buffer pointed to by 'buf' of length
+ * 'buflen'. Upon success, the buffer will always be null-terminated. The
+ * result may be truncated if the buffer is too small.
+ *
+ * The substring to be replaces is identified by the two offsets 'off1' and
+ * 'off2' where 'off1' specifies the offset to the beginning of the substring
+ * and 'off2' specifies the offset to the first byte after the substring.
+ *
+ * The minimum buffer size to store the complete return value (including the
+ * terminating '\0' character) thus has to be:
+ * off1 + strlen(replacement) + strlen(string) - off2 + 1
+ *
+ * Example:
+ *
+ *             01234567890
+ *   string = "foo_____bar"
+ *                ^    ^
+ *                |    |
+ *              off1  off2
+ *
+ *   off1 = 3
+ *   off2 = 8
+ *
+ *   replacement = " - "
+ *
+ *   -> "foo - bar"
+ *
+ * The function returns 'buf' on success, NULL else.
+ */
+char *subst (char *buf, size_t buflen, const char *string, int off1, int off2,
+               const char *replacement);
+
+/*
+ * asubst:
+ *
+ * This function is very similar to subst(). It differs in that it
+ * automatically allocates the memory required for the return value which the
+ * user then has to free himself.
+ *
+ * Returns the newly allocated result string on success, NULL else.
+ */
+char *asubst (const char *string, int off1, int off2, const char *replacement);
+
+#endif /* UTILS_SUBST_H */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+