Code

Merge branch 'collectd-4.5'
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 3 Jan 2009 00:36:04 +0000 (01:36 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sat, 3 Jan 2009 00:36:04 +0000 (01:36 +0100)
Conflicts:

src/types.db.pod

68 files changed:
.mailmap [new file with mode: 0644]
README
TODO
configure.in
contrib/README
contrib/exec-munin.px
contrib/fedora/collectd.spec
contrib/oracle/create_schema.ddl [new file with mode: 0644]
contrib/oracle/db_systat.sql [new file with mode: 0644]
contrib/redhat/collectd.conf
contrib/redhat/collectd.spec
contrib/solaris-smf/README [new file with mode: 0644]
contrib/solaris-smf/collectd [new file with mode: 0755]
contrib/solaris-smf/collectd.xml [new file with mode: 0644]
src/Makefile.am
src/collectd-nagios.c
src/collectd-nagios.pod
src/collectd.conf.in
src/collectd.conf.pod
src/common.c
src/configfile.c
src/dbi.c [new file with mode: 0644]
src/filecount.c
src/filter_chain.c [new file with mode: 0644]
src/filter_chain.h [new file with mode: 0644]
src/filter_ignore.c [new file with mode: 0644]
src/interface.c
src/libcollectdclient/Makefile.am [new file with mode: 0644]
src/libcollectdclient/client.c [new file with mode: 0644]
src/libcollectdclient/client.h [new file with mode: 0644]
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/match_regex.c [new file with mode: 0644]
src/match_value.c [new file with mode: 0644]
src/meta_data.c [new file with mode: 0644]
src/meta_data.h [new file with mode: 0644]
src/mysql.c
src/openvpn.c [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/processes.c
src/rrdcached.c [new file with mode: 0644]
src/rrdtool.c
src/snmp.c
src/target_notification.c [new file with mode: 0644]
src/target_replace.c [new file with mode: 0644]
src/target_set.c [new file with mode: 0644]
src/types.db
src/types.db.pod
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 e698786a1cb86baafacbc838375d334875e3b10a..9b06f64280162c8aa3ef376b72b6aea00e145c0c 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).
@@ -139,6 +146,10 @@ Features
       Read onewire sensors using the owcapu library of the owfs project.
       Please read in collectd.conf(5) why this plugin is experimental.
 
+    - openvpn
+      RX and TX of each client in openvpn-status.log (status-version 2).
+      <http://openvpn.net/index.php/documentation/howto.html>
+
     - perl
       The perl plugin implements a Perl-interpreter into collectd. You can
       write your own plugins in Perl and return arbitrary values using this
@@ -152,9 +163,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 +197,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 +237,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 +254,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 +314,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 +374,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 +482,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 b5cd6b50154de7461475dc7a0741477916480060..3c8f49ebaff7e6ea616b96b71bfedba6f639315c 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 7a4c151708fd59cb5cdd003d71c4e70b2552cdb6..55ce936bd0a519952c63a7101e82df808550020c 100644 (file)
@@ -621,8 +621,13 @@ if test "x$fp_layout_type" = "xunknown"; then
       [[[[
 #include <stdlib.h>
 #include <stdio.h>
-#include <stdint.h>
 #include <string.h>
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif
       ]]]],
       [[[[
        uint64_t i0;
@@ -660,8 +665,13 @@ if test "x$fp_layout_type" = "xunknown"; then
       [[[[
 #include <stdlib.h>
 #include <stdio.h>
-#include <stdint.h>
 #include <string.h>
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif
 #define endianflip(A) ((((uint64_t)(A) & 0xff00000000000000LL) >> 56) | \
                        (((uint64_t)(A) & 0x00ff000000000000LL) >> 40) | \
                        (((uint64_t)(A) & 0x0000ff0000000000LL) >> 24) | \
@@ -707,8 +717,13 @@ if test "x$fp_layout_type" = "xunknown"; then
       [[[[
 #include <stdlib.h>
 #include <stdio.h>
-#include <stdint.h>
 #include <string.h>
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif
 #define intswap(A)    ((((uint64_t)(A) & 0xffffffff00000000LL) >> 32) | \
                        (((uint64_t)(A) & 0x00000000ffffffffLL) << 32))
       ]]]],
@@ -933,102 +948,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"
@@ -1055,7 +974,54 @@ 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")
+
+AC_CHECK_LIB(kvm, kvm_openfiles, [with_kvm_openfiles="yes"], [with_kvm_openfiles="no"])
+if test "x$with_kvm_openfiles" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBKVM_NLIST, 1,
+                 [Define to 1 if you have the 'kvm' library with the 'kvm_openfiles' symbol (-lkvm)])
+       with_libkvm="yes"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_OPENFILES, test "x$with_kvm_openfiles" = "xyes")
+
+# --with-libcurl {{{
 with_curl_config="curl-config"
 with_curl_cflags=""
 with_curl_libs=""
@@ -1123,230 +1089,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"
-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
-
-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
-
-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])
-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"
+       CPPFLAGS="$CPPFLAGS $with_libdbi_cppflags"
 
-  AC_CHECK_HEADERS(statgrab.h,
-                  [with_libstatgrab="yes"],
-                  [with_libstatgrab="no (statgrab.h not found)"])
+       AC_CHECK_HEADERS(dbi/dbi.h, [with_libdbi="yes"], [with_libdbi="no (dbi/dbi.h not found)"])
 
-  CPPFLAGS="$SAVE_CPPFLAGS"
+       CPPFLAGS="$SAVE_CPPFLAGS"
 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"
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libdbi_cppflags"
+       LDFLAGS="$LDFLAGS $with_libdbi_ldflags"
 
-  AC_CHECK_LIB(statgrab, sg_init,
-              [with_libstatgrab="yes"],
-              [with_libstatgrab="no (symbol sg_init not found)"])
+       AC_CHECK_LIB(dbi, dbi_initialize, [with_libdbi="yes"], [with_libdbi="no (Symbol 'dbi_initialize' not found)"])
 
-  CFLAGS="$SAVE_CFLAGS"
-  LDFLAGS="$SAVE_LDFLAGS"
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
 fi
-
-AM_CONDITIONAL(BUILD_WITH_LIBSTATGRAB, test "x$with_libstatgrab" = "xyes")
-if test "x$with_libstatgrab" = "xyes"
+if test "x$with_libdbi" = "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)
+       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")
+# }}}
 
-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"
+# --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_LIBKVM_GETSWAPINFO, 1,
-                 [Define to 1 if you have the 'kvm' library with the 'kvm_getswapinfo' symbol (-lkvm)])
-       with_libkvm="yes"
+       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
-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"
+       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_NLIST, test "x$with_kvm_nlist" = "xyes")
-
-AC_CHECK_LIB(kvm, kvm_openfiles, [with_kvm_openfiles="yes"], [with_kvm_openfiles="no"])
-if test "x$with_kvm_openfiles" = "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_openfiles' symbol (-lkvm)])
-       with_libkvm="yes"
+       collect_libesmtp=1
+else
+       collect_libesmtp=0
 fi
-AM_CONDITIONAL(BUILD_WITH_LIBKVM_OPENFILES, test "x$with_kvm_openfiles" = "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=""
@@ -1424,261 +1326,544 @@ 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.])],
-[
-       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-libnetlink {{{
+with_libnetlink_cflags=""
+with_libnetlink_libs="-lnetlink"
+AC_ARG_WITH(libnetlink, [AS_HELP_STRING([--with-libnetlink@<:@=PREFIX@:>@], [Path to libnetlink.])],
 [
-       with_liboconfig="yes"
-       with_own_liboconfig="no"
+ 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"
-       with_own_liboconfig="yes"
-       LDFLAGS="$save_LDFLAGS"
-       CPPFLAGS="$save_CPPFLAGS"
+ if test "x$ac_system" = "xLinux"
+ then
+        with_libnetlink="yes"
+ else
+        with_libnetlink="no (Linux only library)"
+ fi
 ])
-
-AM_CONDITIONAL(BUILD_WITH_OWN_LIBOCONFIG, test "x$with_own_liboconfig" = "xyes")
-if test "x$with_own_liboconfig" = "xyes"
+if test "x$with_libnetlink" = "xyes"
 then
-       with_liboconfig="yes (shipped version)"
-fi
+       SAVE_CFLAGS=$CFLAGS
+       CFLAGS="$CFLAGS $with_libnetlink_cflags"
 
-#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.])],
-[
-       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"
-       fi; fi
-],
-[
-       with_liboping="yes"
-])
+       with_libnetlink="no (libnetlink.h not found)"
 
-if test "x$with_liboping" = "xyes"
-then
-       save_LDFLAGS="$LDFLAGS"
-       save_CPPFLAGS="$CPPFLAGS"
-       LDFLAGS="$liboping_LDFLAGS"
-       CPPFLAGS="$liboping_CPPFLAGS"
-       AC_CHECK_LIB(oping, ping_construct,
-       [
-               with_liboping="yes"
-               with_own_liboping="no"
-       ],
+       AC_CHECK_HEADERS(libnetlink.h iproute/libnetlink.h linux/libnetlink.h,
        [
-               with_liboping="yes"
-               with_own_liboping="yes"
-               LDFLAGS="$save_LDFLAGS"
-               CPPFLAGS="$save_CPPFLAGS"
-       ])
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBOPING, test "x$with_liboping" = "xyes")
-AM_CONDITIONAL(BUILD_WITH_OWN_LIBOPING, test "x$with_own_liboping" = "xyes")
-
-with_libowcapi_cppflags=""
-with_libowcapi_libs="-lowcapi"
-AC_ARG_WITH(libowcapi, [AS_HELP_STRING([--with-libowcapi@<:@=PREFIX@:>@], [Path to libowcapi.])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               with_libowcapi_cppflags="-I$withval/include"
-               with_libowcapi_libs="-L$withval/lib -lowcapi"
-               with_libowcapi="yes"
-       else
-               with_libowcapi="$withval"
-       fi
-],
-[
-       with_libowcapi="yes"
-])
-if test "x$with_libowcapi" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$with_libowcapi_cppflags"
-       
-       AC_CHECK_HEADERS(owcapi.h, [with_libowcapi="yes"], [with_libowcapi="no (owcapi.h not found)"])
+        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>])
 
-       CPPFLAGS="$SAVE_CPPFLAGS"
+       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_libowcapi" = "xyes"
+if test "x$with_libnetlink" = "xyes"
 then
-       SAVE_LDFLAGS="$LDFLAGS"
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       LDFLAGS="$with_libowcapi_libs"
-       CPPFLAGS="$with_libowcapi_cppflags"
-       
-       AC_CHECK_LIB(owcapi, OW_get, [with_libowcapi="yes"], [with_libowcapi="no (libowcapi not found)"])
-
-       LDFLAGS="$SAVE_LDFLAGS"
-       CPPFLAGS="$SAVE_CPPFLAGS"
+       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_libowcapi" = "xyes"
+if test "x$with_libnetlink" = "xyes"
 then
-       BUILD_WITH_LIBOWCAPI_CPPFLAGS="$with_libowcapi_cppflags"
-       BUILD_WITH_LIBOWCAPI_LIBS="$with_libowcapi_libs"
-       AC_SUBST(BUILD_WITH_LIBOWCAPI_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBOWCAPI_LIBS)
+       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")
+# }}}
 
-
-AC_ARG_WITH(libpcap, [AS_HELP_STRING([--with-libpcap@<:@=PREFIX@:>@], [Path to libpcap.])],
+# --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"
+       if test "x$withval" = "xno"
        then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include"
-               with_libpcap="yes"
+               with_libnetsnmp="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libnetsnmp="yes"
        else
-               with_libpcap="$withval"
-       fi
+               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_libpcap="yes"
-])
-if test "x$with_libpcap" = "xyes"
+[with_libnetsnmp="yes"])
+if test "x$with_libnetsnmp" = "xyes"
 then
-       AC_CHECK_LIB(pcap, pcap_open_live,
-       [
-               AC_DEFINE(HAVE_LIBPCAP, 1, [Define to 1 if you have the pcap library (-lpcap).])
-       ], [with_libpcap="no (libpcap not found)"])
+       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_libpcap" = "xyes"
+if test "x$with_libnetsnmp" = "xyes"
 then
-       AC_CHECK_HEADERS(pcap.h,
-       [
-               AC_DEFINE(HAVE_PCAP_H, 1, [Define to 1 if you have the <pcap.h> header file.])
-       ], [with_libpcap="no (pcap.h not found)"])
+       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_libpcap" = "xyes"
+if test "x$with_libnetsnmp" = "xyes"
 then
-       collect_libpcap=1
-else
-       collect_libpcap=0
+       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
-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")
+AM_CONDITIONAL(BUILD_WITH_LIBNETSNMP, test "x$with_libnetsnmp" = "xyes")
+# }}}
 
-AC_ARG_WITH(libesmtp, [AS_HELP_STRING([--with-libesmtp@<:@=PREFIX@:>@], [Path to libesmtp.])],
+# --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
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include -D_THREAD_SAFE"
-               with_libesmtp="yes"
-       else
-               with_libesmtp="$withval"
+               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_libesmtp="yes"
+       with_liboconfig="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"
-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)"])
-fi
-if test "x$with_libesmtp" = "xyes"
-then
-       collect_libesmtp=1
-else
-       collect_libesmtp=0
-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")
 
-perl_interpreter="perl"
-AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
+save_LDFLAGS="$LDFLAGS"
+save_CPPFLAGS="$CPPFLAGS"
+LDFLAGS="$liboconfig_LDFLAGS"
+CPPFLAGS="$liboconfig_CPPFLAGS"
+AC_CHECK_LIB(oconfig, oconfig_parse_fh,
 [
-       if test -x "$withval"
-       then
-               perl_interpreter="$withval"
-               with_libperl="yes"
-       else if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include"
-               perl_interpreter="$withval/bin/perl"
-               with_libperl="yes"
-       else
-               with_libperl="$withval"
-       fi; fi
+       with_liboconfig="yes"
+       with_own_liboconfig="no"
 ],
 [
-       with_libperl="yes"
+       with_liboconfig="yes"
+       with_own_liboconfig="yes"
+       LDFLAGS="$save_LDFLAGS"
+       CPPFLAGS="$save_CPPFLAGS"
 ])
 
-AC_MSG_CHECKING([for perl])
-perl_interpreter=`which "$perl_interpreter" 2> /dev/null`
-if test -x "$perl_interpreter"
+AM_CONDITIONAL(BUILD_WITH_OWN_LIBOCONFIG, test "x$with_own_liboconfig" = "xyes")
+if test "x$with_own_liboconfig" = "xyes"
 then
-       AC_MSG_RESULT([yes ($perl_interpreter)])
-else
-       perl_interpreter=""
-       AC_MSG_RESULT([no])
+       with_liboconfig="yes (shipped version)"
 fi
+# }}}
 
-AC_SUBST(PERL, "$perl_interpreter")
-
-if test "x$with_libperl" = "xyes" \
+# --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"
+       fi; fi
+],
+[
+       with_liboping="yes"
+])
+
+if test "x$with_liboping" = "xyes"
+then
+       save_LDFLAGS="$LDFLAGS"
+       save_CPPFLAGS="$CPPFLAGS"
+       LDFLAGS="$liboping_LDFLAGS"
+       CPPFLAGS="$liboping_CPPFLAGS"
+       AC_CHECK_LIB(oping, ping_construct,
+       [
+               with_liboping="yes"
+               with_own_liboping="no"
+       ],
+       [
+               with_liboping="yes"
+               with_own_liboping="yes"
+               LDFLAGS="$save_LDFLAGS"
+               CPPFLAGS="$save_CPPFLAGS"
+       ])
+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.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               with_libowcapi_cppflags="-I$withval/include"
+               with_libowcapi_libs="-L$withval/lib -lowcapi"
+               with_libowcapi="yes"
+       else
+               with_libowcapi="$withval"
+       fi
+],
+[
+       with_libowcapi="yes"
+])
+if test "x$with_libowcapi" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$with_libowcapi_cppflags"
+       
+       AC_CHECK_HEADERS(owcapi.h, [with_libowcapi="yes"], [with_libowcapi="no (owcapi.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libowcapi" = "xyes"
+then
+       SAVE_LDFLAGS="$LDFLAGS"
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       LDFLAGS="$with_libowcapi_libs"
+       CPPFLAGS="$with_libowcapi_cppflags"
+       
+       AC_CHECK_LIB(owcapi, OW_get, [with_libowcapi="yes"], [with_libowcapi="no (libowcapi not found)"])
+
+       LDFLAGS="$SAVE_LDFLAGS"
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libowcapi" = "xyes"
+then
+       BUILD_WITH_LIBOWCAPI_CPPFLAGS="$with_libowcapi_cppflags"
+       BUILD_WITH_LIBOWCAPI_LIBS="$with_libowcapi_libs"
+       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"
+       then
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_libpcap="yes"
+       else
+               with_libpcap="$withval"
+       fi
+],
+[
+       with_libpcap="yes"
+])
+if test "x$with_libpcap" = "xyes"
+then
+       AC_CHECK_LIB(pcap, pcap_open_live,
+       [
+               AC_DEFINE(HAVE_LIBPCAP, 1, [Define to 1 if you have the pcap library (-lpcap).])
+       ], [with_libpcap="no (libpcap not found)"])
+fi
+if test "x$with_libpcap" = "xyes"
+then
+       AC_CHECK_HEADERS(pcap.h,
+       [
+               AC_DEFINE(HAVE_PCAP_H, 1, [Define to 1 if you have the <pcap.h> header file.])
+       ], [with_libpcap="no (pcap.h not found)"])
+fi
+if test "x$with_libpcap" = "xyes"
+then
+       collect_libpcap=1
+else
+       collect_libpcap=0
+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"
+       ])
+
+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
+               with_libpcre="no ($with_pcre_config failed)"
+       else
+               SAVE_CPPFLAGS="$CPPFLAGS"
+               CPPFLAGS="$CPPFLAGS $with_pcre_cflags"
+
+               AC_CHECK_HEADERS(pcre.h, [], [with_libpcre="no (pcre.h not found)"], [])
+
+               CPPFLAGS="$SAVE_CPPFLAGS"
+       fi
+fi
+
+if test "x$with_libpcre" = "xyes"
+then
+       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_libpcre" = "xyes"
+then
+       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
+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.])],
+[
+       if test -x "$withval"
+       then
+               perl_interpreter="$withval"
+               with_libperl="yes"
+       else if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               perl_interpreter="$withval/bin/perl"
+               with_libperl="yes"
+       else
+               with_libperl="$withval"
+       fi; fi
+],
+[
+       with_libperl="yes"
+])
+
+AC_MSG_CHECKING([for perl])
+perl_interpreter=`which "$perl_interpreter" 2> /dev/null`
+if test -x "$perl_interpreter"
+then
+       AC_MSG_RESULT([yes ($perl_interpreter)])
+else
+       perl_interpreter=""
+       AC_MSG_RESULT([no])
+fi
+
+AC_SUBST(PERL, "$perl_interpreter")
+
+if test "x$with_libperl" = "xyes" \
        && test -n "$perl_interpreter"
 then
   SAVE_CFLAGS=$CFLAGS
@@ -1760,551 +1945,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"
-       else
-               with_libiptc="$withval"
-       fi
-],
-[
-       if test "x$ac_system" = "xLinux"
+               with_libpq="no"
+       else if test "x$withval" = "xyes"
        then
-               with_libiptc="yes"
-       else
-               with_libiptc="no (Linux only)"
-       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"
-       ])
-fi
-if test "x$with_libiptc" = "xyes"
-then
-       SAVE_CFLAGS=$CFLAGS
-       CFLAGS="$CFLAGS $KERNEL_CFLAGS"
-
-       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"
-       ])
-
-       CFLAGS=$SAVE_CFLAGS
-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"
-then
-       AC_DEFINE(OWN_LIBIPTC, 1, [Define to 1 if we use the shipped iptc library.])
-fi
-
-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"
-       then
-               with_libnetsnmp="yes"
+               with_libpq="yes"
        else
-               if test -x "$withval"
+               if test -f "$withval" && test -x "$withval";
                then
-                       with_snmp_config="$withval"
-                       with_libnetsnmp="yes"
-               else
-                       with_snmp_config="$withval/bin/net-snmp-config"
-                       with_libnetsnmp="yes"
-               fi
+                       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
 ],
-[with_libnetsnmp="yes"])
-if test "x$with_libnetsnmp" = "xyes"
+[
+       with_libpq="yes"
+])
+if test "x$with_libpq" = "xyes"
 then
-       with_snmp_cflags=`$with_snmp_config --cflags 2>/dev/null`
-       snmp_config_status=$?
+       with_libpq_includedir=`$with_pg_config --includedir 2> /dev/null`
+       pg_config_status=$?
 
-       if test $snmp_config_status -ne 0
+       if test $pg_config_status -eq 0
        then
-               with_libnetsnmp="no ($with_snmp_config failed)"
+               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
-               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"
+               AC_MSG_WARN([$with_pg_config returned with status $pg_config_status])
        fi
+
+       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_libnetsnmp" = "xyes"
+if test "x$with_libpq" = "xyes"
 then
-       with_snmp_libs=`$with_snmp_config --libs 2>/dev/null`
-       snmp_config_status=$?
+       with_libpq_libdir=`$with_pg_config --libdir 2> /dev/null`
+       pg_config_status=$?
 
-       if test $snmp_config_status -ne 0
+       if test $pg_config_status -eq 0
        then
-               with_libnetsnmp="no ($with_snmp_config failed)"
+               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_CHECK_LIB(netsnmp, init_snmp,
-               [with_libnetsnmp="yes"],
-               [with_libnetsnmp="no (libnetsnmp not found)"],
-               [$with_snmp_libs])
+               AC_MSG_WARN([$with_pg_config returned with status $pg_config_status])
        fi
+
+       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
-if test "x$with_libnetsnmp" = "xyes"
+if test "x$with_libpq" = "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)
+       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_LIBNETSNMP, test "x$with_libnetsnmp" = "xyes")
-
-PKG_CHECK_MODULES([LIBNOTIFY], [libnotify],
-               [with_libnotify="yes"],
-               [with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"])
+AM_CONDITIONAL(BUILD_WITH_LIBPQ, test "x$with_libpq" = "xyes")
+# }}}
 
-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"
+# --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_libupsclient="yes"
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_libpthread="yes"
        else
-               with_libupsclient="no (pkg-config doesn't know library)"
+               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_libupsclient" = "xyes"
+if test "x$with_libpthread" = "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
+       AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"])
 fi
-if test "x$with_libupsclient" = "xyes"
+if test "x$with_libpthread" = "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"
+       collect_pthread=1
+else
+       collect_pthread=0
 fi
-if test "x$with_libupsclient" = "xyes"
+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")
+# }}}
+
+# --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
+               librrd_cflags="-I$withval/include"
+               librrd_ldflags="-L$withval/lib"
+               with_librrd="yes"
+       else
+               with_librrd="$withval"
+       fi
+], [with_librrd="yes"])
+if test "x$with_librrd" = "xyes"
 then
        SAVE_CPPFLAGS="$CPPFLAGS"
        SAVE_LDFLAGS="$LDFLAGS"
 
-       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
-       LDFLAGS="$LDFLAGS $with_libupsclient_libs"
+       CPPFLAGS="$CPPFLAGS $librrd_cflags"
+       LDFLAGS="$LDFLAGS $librrd_ldflags"
 
-       AC_CHECK_LIB(upsclient, upscli_connect,
-                    [with_libupsclient="yes"],
-                    [with_libupsclient="no (symbol upscli_connect not found)"])
+       AC_CHECK_HEADERS(rrd.h,, [with_librrd="no (rrd.h not found)"])
 
        CPPFLAGS="$SAVE_CPPFLAGS"
        LDFLAGS="$SAVE_LDFLAGS"
 fi
-if test "x$with_libupsclient" = "xyes"
+if test "x$with_librrd" = "xyes"
 then
        SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
+       SAVE_LDFLAGS="$LDFLAGS"
 
-       AC_CHECK_TYPES([UPSCONN_t, UPSCONN], [], [],
-[#include <stdlib.h>
-#include <stdio.h>
-#include <upsclient.h>])
+       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
+               AC_CHECK_LIB(rrd_th, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"])
+       else
+               AC_CHECK_LIB(rrd, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"])
+       fi
 
        CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
 fi
-if test "x$with_libupsclient" = "xyes"
+if test "x$with_librrd" = "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)
+       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
-
-### 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.])],
+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" \
-               && 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"
+       if test "x$withval" = "xno"
        then
-               with_libxmms="no"
+               with_libsensors="no"
        else
-               with_libxmms="yes"
-       fi; fi
+               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_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
+       if test "x$ac_system" = "xLinux"
        then
-               with_libxmms="no"
+               with_libsensors="yes"
+       else
+               with_libsensors="no (Linux only library)"
        fi
-fi
-if test "x$with_libxmms" = "xyes"
+])
+if test "x$with_libsensors" = "xyes"
 then
-       with_xmms_libs=`$with_xmms_config --libs 2>/dev/null`
-       xmms_config_status=$?
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
 
-       if test $xmms_config_status -ne 0
-       then
-               with_libxmms="no"
-       fi
+#      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_libxmms" = "xyes"
+if test "x$with_libsensors" = "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)
-       ],
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
+       LDFLAGS="$LDFLAGS $with_sensors_ldflags"
+
+       AC_CHECK_LIB(sensors, sensors_init,
        [
-               with_libxmms="no"
+               AC_DEFINE(HAVE_LIBSENSORS, 1, [Define to 1 if you have the sensors library (-lsensors).])
        ],
-       [$with_xmms_libs])
+       [with_libsensors="no (libsensors not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
 fi
-with_libxmms_numeric=0
-if test "x$with_libxmms" = "xyes"
+if test "x$with_libsensors" = "xyes"
 then
-       with_libxmms_numeric=1
+       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
-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 ###
+AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_libsensors" = "xyes")
+# }}}
 
-with_libnetlink_cflags=""
-with_libnetlink_libs="-lnetlink"
-AC_ARG_WITH(libnetlink, [AS_HELP_STRING([--with-libnetlink@<:@=PREFIX@:>@], [Path to libnetlink.])],
+# --with-libstatgrab {{{
+with_libstatgrab_cflags=""
+with_libstatgrab_ldflags=""
+AC_ARG_WITH(libstatgrab, [AS_HELP_STRING([--with-libstatgrab@<:@=PREFIX@:>@], [Path to libstatgrab.])],
 [
- 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$withval" != "xno" \
+               && test "x$withval" != "xyes"
+       then
+               with_libstatgrab_cflags="-I$withval/include"
+               with_libstatgrab_ldflags="-L$withval/lib"
+               with_libstatgrab="yes"
+       else
+               with_libstatgrab="$withval"
+       fi
 ],
 [
if test "x$ac_system" = "xLinux"
- then
-        with_libnetlink="yes"
- else
-        with_libnetlink="no (Linux only library)"
- fi
      if test "x$ac_system" = "xunknown"
      then
+               with_libstatgrab="yes"
      else
+               with_libstatgrab="no"
      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=""
@@ -2419,8 +2579,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
 #
@@ -2731,6 +2975,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])
@@ -2738,6 +2983,7 @@ 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([hddtemp],     [yes],                [Query hddtempd])
 AC_PLUGIN([interface],   [$plugin_interface],  [Interface traffic statistics])
 AC_PLUGIN([iptables],    [$with_libiptc],      [IPTables rule counters])
@@ -2747,6 +2993,8 @@ AC_PLUGIN([irq],         [$plugin_irq],        [IRQ statistics])
 AC_PLUGIN([libvirt],     [$plugin_libvirt],    [Virtual machine statistics])
 AC_PLUGIN([load],        [$plugin_load],       [System load])
 AC_PLUGIN([logfile],     [yes],                [File logging plugin])
+AC_PLUGIN([match_regex], [yes],                [The regex match])
+AC_PLUGIN([match_value], [yes],                [The value match])
 AC_PLUGIN([mbmon],       [yes],                [Query mbmond])
 AC_PLUGIN([memcached],   [yes],                [memcached statistics])
 AC_PLUGIN([memory],      [$plugin_memory],     [Memory usage])
@@ -2760,19 +3008,25 @@ 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([openvpn],     [yes],                [OpenVPN client 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])
 AC_PLUGIN([syslog],      [$have_syslog],       [Syslog logging plugin])
 AC_PLUGIN([tail],        [yes],                [Parsing of logfiles])
 AC_PLUGIN([tape],        [$plugin_tape],       [Tape drive statistics])
+AC_PLUGIN([target_notification], [yes],        [The notification target])
+AC_PLUGIN([target_replace], [yes],             [The replace target])
+AC_PLUGIN([target_set],  [yes],                [The set target])
 AC_PLUGIN([tcpconns],    [$plugin_tcpconns],   [TCP connection statistics])
 AC_PLUGIN([teamspeak2],  [yes],                [TeamSpeak2 server statistics])
 AC_PLUGIN([thermal],     [$plugin_thermal],    [Linux ACPI thermal zone statistics])
@@ -2821,12 +3075,12 @@ fi
 AC_SUBST(PERL_BINDINGS)
 AC_SUBST(PERL_BINDINGS_OPTIONS)
 
-AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libiptc/Makefile src/liboconfig/Makefile src/liboping/Makefile bindings/Makefile)
+AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libiptc/Makefile src/libcollectdclient/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" \
@@ -2858,6 +3112,7 @@ cat <<EOF;
 Configuration:
   Libraries:
     libcurl . . . . . . . $with_libcurl
+    libdbi  . . . . . . . $with_libdbi
     libesmtp  . . . . . . $with_libesmtp
     libiokit  . . . . . . $with_libiokit
     libiptc . . . . . . . $with_libiptc
@@ -2871,16 +3126,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
@@ -2898,6 +3155,7 @@ Configuration:
     cpu . . . . . . . . . $enable_cpu
     cpufreq . . . . . . . $enable_cpufreq
     csv . . . . . . . . . $enable_csv
+    dbi . . . . . . . . . $enable_dbi
     df  . . . . . . . . . $enable_df
     disk  . . . . . . . . $enable_disk
     dns . . . . . . . . . $enable_dns
@@ -2905,6 +3163,7 @@ Configuration:
     entropy . . . . . . . $enable_entropy
     exec  . . . . . . . . $enable_exec
     filecount . . . . . . $enable_filecount
+    filter_ignore . . . . $enable_filter_ignore
     hddtemp . . . . . . . $enable_hddtemp
     interface . . . . . . $enable_interface
     iptables  . . . . . . $enable_iptables
@@ -2914,6 +3173,8 @@ Configuration:
     libvirt . . . . . . . $enable_libvirt
     load  . . . . . . . . $enable_load
     logfile . . . . . . . $enable_logfile
+    match_regex . . . . . $enable_match_regex
+    match_value . . . . . $enable_match_value
     mbmon . . . . . . . . $enable_mbmon
     memcached . . . . . . $enable_memcached
     memory  . . . . . . . $enable_memory
@@ -2928,12 +3189,15 @@ Configuration:
     ntpd  . . . . . . . . $enable_ntpd
     nut . . . . . . . . . $enable_nut
     onewire . . . . . . . $enable_onewire
+    openvpn . . . . . . . $enable_openvpn
+    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
@@ -2941,6 +3205,9 @@ Configuration:
     syslog  . . . . . . . $enable_syslog
     tail  . . . . . . . . $enable_tail
     tape  . . . . . . . . $enable_tape
+    target_notification . $enable_target_notification
+    target_replace  . . . $enable_target_replace
+    target_set  . . . . . $enable_target_set
     tcpconns  . . . . . . $enable_tcpconns
     teamspeak2  . . . . . $enable_teamspeak2
     thermal . . . . . . . $enable_thermal
@@ -2957,3 +3224,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 :
index 1c348120b0c6a4c777f5a020ccea1a323cca9040..01b9e2cb5f02e2f63388ba8ae68393c794319305 100644 (file)
@@ -80,3 +80,8 @@ snmp-data.conf
 data that is defined in the IF-MIB, e. g. octet or packet counters, UPS-MIB and
 whatever people have send in. If you have some more definitions please send
 them in, so others can profit from it.
+
+solaris-smf
+-----------
+  Manifest file for the Solaris SMF system and detailed information on how to
+register collectd as a service with this system.
index 223a8a5ecf9c84e811a427751abe69ed4ca89b06..9163730c6e8c61041b288a9e538f4c8956948dbe 100755 (executable)
@@ -59,7 +59,7 @@ the C<nut> plugin above provides C<in> and C<out> which are both voltages, you
 can use a hyphen to add a "type instance" to the type.
 
 For a list of already defined "types" look at the F<types.db> file in
-collectd's library path, e.E<nbsp>g. F</usr/lib/collectd/>.
+collectd's shared data directory, e.E<nbsp>g. F</usr/share/collectd/>.
 
 =item B<Interval> I<Seconds>
 
index 89ce1237499395aaad888badce63528810ebf103..35eb8bec1eb61e1d74bd63f9cfa326cd0d3e15b6 100644 (file)
@@ -232,7 +232,7 @@ exit 0
 %attr(0644,root,root) %{_libdir}/%{name}/wireless.so*
 %attr(0644,root,root) %{_libdir}/%{name}/wireless.la
 
-%attr(0644,root,root) %{_libdir}/%{name}/types.db
+%attr(0644,root,root) %{_datadir}/%{name}/types.db
 
 %dir /var/lib/collectd
 
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 7a026fac0cfd6484a74145ed52f83fe0b8935a09..0aaaee317bcfb71c7689e4181fe1e873fe33cc2c 100644 (file)
@@ -9,7 +9,7 @@ FQDNLookup   true
 BaseDir     "/var/lib/collectd"
 PIDFile     "/var/run/collectd.pid"
 PluginDir   "/usr/lib/collectd"
-TypesDB     "/usr/lib/collectd/types.db"
+TypesDB     "/usr/share/collectd/types.db"
 Interval     10
 ReadThreads  5
 
index bce76473382112f2f32714ef593dd00fee7624b3..62acc184acf7c31c4cf368d00187b55c62afd69c 100644 (file)
@@ -85,7 +85,7 @@ cp contrib/redhat/collectd.conf $RPM_BUILD_ROOT/etc/collectd.conf
 sed -i 's:#BaseDir     "/usr/var/lib/collectd":BaseDir     "/var/lib/collectd":' $RPM_BUILD_ROOT/etc/collectd.conf
 sed -i 's:#PIDFile     "/usr/var/run/collectd.pid":PIDFile     "/var/run/collectd.pid":' $RPM_BUILD_ROOT/etc/collectd.conf
 sed -i 's:#PluginDir   "/usr/lib/collectd":PluginDir   "/usr/lib/collectd":' $RPM_BUILD_ROOT/etc/collectd.conf
-sed -i 's:#TypesDB     "/usr/lib/collectd/types.db":TypesDB     "/usr/lib/collectd/types.db":' $RPM_BUILD_ROOT/etc/collectd.conf
+sed -i 's:#TypesDB     "/usr/share/collectd/types.db":TypesDB     "/usr/share/collectd/types.db":' $RPM_BUILD_ROOT/etc/collectd.conf
 sed -i 's:#Interval     10:Interval     10:' $RPM_BUILD_ROOT/etc/collectd.conf
 sed -i 's:#ReadThreads  5:ReadThreads  5:' $RPM_BUILD_ROOT/etc/collectd.conf
 ##Move config contribs
@@ -247,7 +247,7 @@ exit 0
 %attr(0644,root,root) %{_libdir}/%{name}/wireless.so*
 %attr(0644,root,root) %{_libdir}/%{name}/wireless.la
 
-%attr(0644,root,root) %{_libdir}/%{name}/types.db
+%attr(0644,root,root) %{_datadir}/%{name}/types.db
 
 %exclude %{_libdir}/perl5/5.8.8/%{_arch}-linux-thread-multi/perllocal.pod
 %attr(0644,root,root) %{_libdir}/perl5/site_perl/5.8.8/Collectd.pm
diff --git a/contrib/solaris-smf/README b/contrib/solaris-smf/README
new file mode 100644 (file)
index 0000000..dfd990b
--- /dev/null
@@ -0,0 +1,331 @@
+SMF is the way Solaris 10 and later preferably manages services. It is intended
+to replace the old init.d process and provides advances features, such as
+automatic restarting of failing services.
+
+The following blog entry by Piotr Hosowicz describes the process in more
+detail. The original entry can be found at
+<http://phosowicz.jogger.pl/2008/12/21/smf-izing-collectd/>.
+
+The files in this directory are:
+
+  README          This file
+  collectd        Start / stop script
+  collectd.xml    SMF manifest for collectd.
+
+------------------------------------------------------------------------
+
+      SMF-izing collectd <#>
+
+Wpis na 0. poziomie, wysłany 21 grudnia 2008 o 16:30:49.
+
+My two previous blog entries were about building and running collectd
+<http://collectd.org/> on Sun Solaris 10. After the first one Octo
+contacted me and was so kind as to release a packaged version for x86_64
+<http://collectd.org/download.shtml#solaris>. I have put aside the build
+I rolled on my own and decided to install and run the packaged one on
+the production servers. This blog entry is about SMF-izing the collectd
+daemon.
+
+A few words about the SMF – the Solaris'es Service Management Facility.
+I think it appeared in Solaris 10. From then on the good old |/etc/rcN.d
+|| /etc/init.d| services are called /legacy services/. They still can be
+run, but are not fully supported by SMF. SMF enables you to start and
+stop services in the unified way, can direct you to man pages in case a
+service enters maintenance mode, resolves dependencies between services,
+can store properties of services and so on. A nice feature is that SMF
+will take care of restarting services in case they terminate
+unexpectedly, we will use it at the end to check that things are working
+as they should.
+
+The 3V|L thing about SMF is that each service needs so called SMF
+manifest written in XML and a script or scripts that are executed, when
+the service needs to be stopped or started. It can be one script, which
+should accept respective parameters. Even more 3V|L is the fact that the
+manifest is imported into the SMF database and kept there in SQLite format.
+
+Below you will find collectd manifest and the script. I will post them
+to collectd mailing list in matter of minutes with this blog entry
+serving as a README. Please read all down to the bottom, including the
+remarks.
+
+Manifest (based on the work of Kangurek, thanks!), see the "collectd.xml"
+file:
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<service_bundle type='manifest' name='collectd'>
+<service
+        name='application/collectd'
+        type='service'
+        version='1'>
+       <create_default_instance enabled='true' />
+       <single_instance/>
+        <dependency
+                name='network'
+                grouping='require_all'
+                restart_on='none'
+                type='service'>
+                <service_fmri value='svc:/milestone/network:default' />
+        </dependency> 
+        <dependency
+                name='filesystem-local'
+                grouping='require_all'
+                restart_on='none'
+                type='service'>
+                <service_fmri value='svc:/system/filesystem/local:default' />
+        </dependency> 
+        <exec_method
+                type='method'
+                name='start'
+                exec='/lib/svc/method/collectd start'
+                timeout_seconds='60'>
+           <method_context>
+               <method_credential user='root' group='root' />
+           </method_context>
+       </exec_method>
+        <exec_method
+                type='method'
+                name='stop'
+                exec='/lib/svc/method/collectd stop'
+                timeout_seconds='60'>
+           <method_context>
+               <method_credential user='root' group='root' />
+           </method_context>
+       </exec_method>
+        <stability value='Evolving' />
+</service>
+</service_bundle>
+
+Script, see the "collectd" file:
+
+#!/sbin/sh
+PIDFILE=/opt/collectd/var/run/collectd.pid
+DAEMON=/opt/collectd/sbin/collectd
+. /lib/svc/share/smf_include.sh
+case "$1" in
+  start)
+    if [ -f $PIDFILE ] ; then
+      echo "Already running. Stale PID file?"
+      PID=`cat $PIDFILE`
+      echo "$PIDFILE contains $PID"
+      ps -p $PID
+      exit $SMF_EXIT_ERR_FATAL
+    fi
+    $DAEMON
+    if [ $? -ne 0 ] ; then
+      echo $DAEMON faild to start
+      exit $SMF_EXIT_ERR_FATAL
+    fi
+  ;;
+  stop)
+    PID=`cat $PIDFILE 2>/dev/null`
+    kill -15 $PID 2>/dev/null
+    pwait $PID 1> /dev/null 2>/dev/null
+  ;;
+  restart)
+    $0 stop
+    $0 start
+  ;;
+  status)
+    ps -ef | grep collectd | grep -v status | grep -v grep
+  ;;
+  *)
+    echo "Usage: $0 [ start | stop | restart | status ]"
+    exit 1
+  ;;
+esac
+exit $SMF_EXIT_OK
+
+So you have two files: |collectd| script and |collectd.xml| manifest.
+What do you do with these files?
+
+First – before you begin – make sure that collectd is not running, close
+it down. My script above assumes that you are using the default place
+for PID file. Second: remove / move away collectd's |/etc/rcN.d| and
+|/etc/init.d| stuff, you won't need it from now on, because collectd
+will be SMF-ized. Tada!
+
+Next – install the script in place. It took me a minute or two to figure
+out why Solaris'es |install| tool does not work as expected. It turned
+out that the switches and parameters must be in exactly same order as in
+man page, especially the -c parameter must be first:
+
+|# install -c /lib/svc/method/ -m 755 -u root -g bin collectd|
+
+Now is the moment to test once again that the script is working OK. Try
+running:
+
+|# /lib/svc/method/collectd start
+# /lib/svc/method/collectd stop
+# /lib/svc/method/collectd restart
+|
+
+|pgrep| and |kill| are your friends here, also collectd logs. At last
+stop the collectd service and continue.
+
+Now is the time to /slurp/ attached XML manifest into the SMF database.
+This is done using the |svccfg| tool. Transcript follows:
+
+|# svccfg
+svc:> validate collectd.xml
+svc:> import collectd.xml
+svc:>
+|
+
+It is good to run |validate| command first, especially if you copied and
+pasted the XML manifest from this HTML document opened in your
+browser!!! Second thing worth noting is that |svccfg| starts the service
+immediately upon importing the manifest. It might be not what you want.
+For example it will start collecting data on remote collectd server if
+you use network plugin and it will do it under the hostname, that is not
+right. So be sure to configure collectd prior to running it from SMF.
+
+Now a few words about SMF tools. To see the state of all services issue
+|svcs -a| command. To see state of collectd service issue |svcs
+collectd| command. Quite normal states are enabled and disabled. If you
+see maintenance state then something is wrong. Be sure that you stopped
+all non-SMF collectd processes before you follow the procedure described
+here. To stop collectd the SMF way issue the |svcadm disable
+collectd|command. To start collectd the SMF way issue the |svcadm enable
+collectd|command. Be aware that setting it this way makes the change
+persistent across OS reboots, if you want to enable / disable the
+service only temporarily then add |-t| switch after the |enable| /
+|disable| keywords.
+
+And now is time for a grand finale – seeing if SMF can take care of
+collectd in case it crashes. See PID of collectd either using |pgrep| or
+seeing the contents of the PID file and kill it using |kill|. Then check
+with |svcs collectd| command that SMF has restarted collectd soon
+afterwards. You should see that the service is once again enabled in the
+first column, without your intervention.
+
+Things that could or should be clarified:
+
+    * How hard is it to correct mistakes in manifests? Does svccfg just
+      overwrite entries for specified service FMRI or does it require to
+      delete bad entry (and how to do it?!) and import the correct one?
+    * How does SMF know that a service has crashed / terminated? I
+      attended Sun's trainings and the trainer didn't know how to
+      explain this, we formed a hypothesis that it watches new PIDs
+      after starting a service or something like that. I think it is a
+      bad hypothesis, because SMF can watch PID for the startup script
+      itself but I think not the processes launched inside the script. I
+      have a slight idea that it is done based on so called contracts.
+
+------------------------------------------------------------------------
+
+
+        Komentarze do notki “SMF-izing collectd” <#comments>
+
+
+        Zostaw odpowiedź
+
+Nick
+
+------------------------------------------------------------------------
+
+
+    Archiwum
+
+    * Grudzień 2008 (5) </2008/12/>
+    * Październik 2008 (4) </2008/10/>
+    * Wrzesień 2008 (7) </2008/09/>
+    * Sierpień 2008 (5) </2008/08/>
+    * Lipiec 2008 (12) </2008/07/>
+    * Czerwiec 2008 (11) </2008/06/>
+    * Maj 2008 (3) </2008/05/>
+    * Kwiecień 2008 (10) </2008/04/>
+    * Marzec 2008 (3) </2008/03/>
+    * Luty 2008 (1) </2008/02/>
+    * Styczeń 2008 (1) </2008/01/>
+    * Listopad 2007 (4) </2007/11/>
+    * Październik 2007 (6) </2007/10/>
+    * Wrzesień 2007 (10) </2007/09/>
+    * Sierpień 2007 (3) </2007/08/>
+    * Lipiec 2007 (8) </2007/07/>
+    * Czerwiec 2007 (10) </2007/06/>
+    * Maj 2007 (12) </2007/05/>
+    * Kwiecień 2007 (17) </2007/04/>
+
+
+    Kategorie
+
+    * Film (8) <http://phosowicz.jogger.pl/kategoria/film/>
+    * Książki (35) <http://phosowicz.jogger.pl/kategoria/ksiazki/>
+    * Muzyka (15) <http://phosowicz.jogger.pl/kategoria/muzyka/>
+    * Ogólne (20) <http://phosowicz.jogger.pl/kategoria/ogolne/>
+    * Polityka (59) <http://phosowicz.jogger.pl/kategoria/polityka/>
+    * Sprzedam (5) <http://phosowicz.jogger.pl/kategoria/sprzedam/>
+    * Techblog (20) <http://phosowicz.jogger.pl/kategoria/techblog/>
+    * Technikalia (34) <http://phosowicz.jogger.pl/kategoria/technikalia/>
+    * Wystawy (3) <http://phosowicz.jogger.pl/kategoria/wystawy/>
+
+
+    Czytuję
+
+    * ArsTechnica.com <http://arstechnica.com/>
+    * Fund. Orientacja <http://www.abcnet.com.pl/>
+    * Techblog Jogger'a <http://techblog.pl/>
+    * The Register <http://www.theregister.co.uk/>
+
+
+    Inne blogi
+
+    * Bloody.Users <http://bloody.users.jogger.pl/>
+    * Dandys <http://dandys.jogger.pl/>
+    * Derin <http://derin.jogger.pl>
+    * Kefir87 <http://kefir87.jogger.pl/>
+    * Klisu <http://klisu.jogger.pl/>
+    * Kwantowe Krajobrazy <http://kwantowekrajobrazy.jogger.pl/>
+    * Paczor <http://paczor.fubar.pl/>
+    * Prestidigitator <http://prestidigitator.jogger.pl>
+    * Remiq <http://remiq.jogger.pl>
+    * Torero <http://torero.jogger.pl/>
+    * Zdzichubg <http://zdzichubg.jogger.pl/>
+
+
+    Inne moje strony
+
+    * www.delphi.org.pl <http://www.delphi.org.pl>
+    * www.hosowicz.com <http://www.hosowicz.com>
+
+
+    Pajacyk.pl
+
+    * Nakarm dzieci! <http://pajacyk.pl/>
+
+
+    Meta
+
+    * Panel administracyjny <https://login.jogger.pl/>
+
+
+------------------------------------------------------------------------
+
+phosowicz is powered by Jogger <http://jogger.pl> and K2
+<http://binarybonsai.com/k2/> by Michael <http://binarybonsai.com> and
+Chris <http://chrisjdavis.org>, ported by Patryk Zawadzki.
+<http://patrys.jogger.pl/>
+Notki w RSS <http://phosowicz.jogger.pl/rss/content/html/20>
+
diff --git a/contrib/solaris-smf/collectd b/contrib/solaris-smf/collectd
new file mode 100755 (executable)
index 0000000..5ffdb24
--- /dev/null
@@ -0,0 +1,42 @@
+#!/sbin/sh
+
+PIDFILE=/opt/collectd/var/run/collectd.pid
+DAEMON=/opt/collectd/sbin/collectd
+
+. /lib/svc/share/smf_include.sh
+
+case "$1" in
+  start)
+    if [ -f $PIDFILE ] ; then
+      echo "Already running. Stale PID file?"
+      PID=`cat $PIDFILE`
+      echo "$PIDFILE contains $PID"
+      ps -p $PID
+      exit $SMF_EXIT_ERR_FATAL
+    fi
+    $DAEMON
+    if [ $? -ne 0 ] ; then
+      echo $DAEMON faild to start
+      exit $SMF_EXIT_ERR_FATAL
+    fi
+  ;;
+  stop)
+    PID=`cat $PIDFILE 2>/dev/null`
+    kill -15 $PID 2>/dev/null
+    pwait $PID 1> /dev/null 2>/dev/null
+  ;;
+  restart)
+    $0 stop
+    $0 start
+  ;;
+  status)
+    ps -ef | grep collectd | grep -v status | grep -v grep
+  ;;
+  *)
+    echo "Usage: $0 [ start | stop | restart | status ]"
+    exit 1
+  ;;
+esac
+
+
+exit $SMF_EXIT_OK
diff --git a/contrib/solaris-smf/collectd.xml b/contrib/solaris-smf/collectd.xml
new file mode 100644 (file)
index 0000000..d1ae3a4
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+
+<service_bundle type='manifest' name='collectd'>
+<service
+        name='application/collectd'
+        type='service'
+        version='1'>
+
+       <create_default_instance enabled='true' />
+       
+       <single_instance/>
+
+        <dependency
+                name='network'
+                grouping='require_all'
+                restart_on='none'
+                type='service'>
+                <service_fmri value='svc:/milestone/network:default' />
+        </dependency> 
+
+        <dependency
+                name='filesystem-local'
+                grouping='require_all'
+                restart_on='none'
+                type='service'>
+                <service_fmri value='svc:/system/filesystem/local:default' />
+        </dependency> 
+
+        <exec_method
+                type='method'
+                name='start'
+                exec='/lib/svc/method/collectd start'
+                timeout_seconds='60'>
+           <method_context>
+               <method_credential user='root' group='root' />
+           </method_context>
+       </exec_method>
+
+
+        <exec_method
+                type='method'
+                name='stop'
+                exec='/lib/svc/method/collectd stop'
+                timeout_seconds='60'>
+           <method_context>
+               <method_credential user='root' group='root' />
+           </method_context>
+       </exec_method>
+
+        <stability value='Evolving' />
+
+</service>
+
+</service_bundle>
+
index 1b46b9ba9e3fa12fae3d8f6d5990af931a7819ab..892ddea1a56aab1ec37e564821dcd7309a399384 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS =
+SUBDIRS = libcollectdclient
 if BUILD_WITH_OWN_LIBIPTC
 SUBDIRS += libiptc
 endif
@@ -29,6 +29,8 @@ bin_PROGRAMS = collectd-nagios
 collectd_SOURCES = collectd.c collectd.h \
                   common.c common.h \
                   configfile.c configfile.h \
+                  filter_chain.c filter_chain.h \
+                  meta_data.c meta_data.h \
                   plugin.c plugin.h \
                   utils_avltree.c utils_avltree.h \
                   utils_cache.c utils_cache.h \
@@ -38,6 +40,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
@@ -90,6 +93,8 @@ collectd_nagios_LDFLAGS =
 if BUILD_WITH_LIBSOCKET
 collectd_nagios_LDFLAGS += -lsocket
 endif
+collectd_nagios_LDADD = libcollectdclient/libcollectdclient.la
+collectd_nagios_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
 pkglib_LTLIBRARIES = 
 
@@ -184,6 +189,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 +279,14 @@ 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_HDDTEMP
 pkglib_LTLIBRARIES += hddtemp.la
 hddtemp_la_SOURCES = hddtemp.c
@@ -370,6 +393,24 @@ collectd_LDADD += "-dlopen" logfile.la
 collectd_DEPENDENCIES += logfile.la
 endif
 
+if BUILD_PLUGIN_MATCH_REGEX
+pkglib_LTLIBRARIES += match_regex.la
+match_regex_la_SOURCES = match_regex.c
+match_regex_la_CPPFLAGS = $(BUILD_WITH_LIBPCRE_CFLAGS)
+match_regex_la_LDFLAGS = -module -avoid-version \
+               $(BUILD_WITH_LIBPCRE_LIBS)
+collectd_LDADD += "-dlopen" match_regex.la
+collectd_DEPENDENCIES += match_regex.la
+endif
+
+if BUILD_PLUGIN_MATCH_VALUE
+pkglib_LTLIBRARIES += match_value.la
+match_value_la_SOURCES = match_value.c
+match_value_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" match_value.la
+collectd_DEPENDENCIES += match_value.la
+endif
+
 if BUILD_PLUGIN_MBMON
 pkglib_LTLIBRARIES += mbmon.la
 mbmon_la_SOURCES = mbmon.c
@@ -529,6 +570,26 @@ collectd_LDADD += "-dlopen" onewire.la
 collectd_DEPENDENCIES += onewire.la
 endif
 
+if BUILD_PLUGIN_OPENVPN
+pkglib_LTLIBRARIES += openvpn.la
+openvpn_la_SOURCES = openvpn.c
+openvpn_la_CFLAGS = $(AM_CFLAGS)
+openvpn_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" openvpn.la
+collectd_DEPENDENCIES += openvpn.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 +649,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)
@@ -680,6 +751,30 @@ collectd_LDADD += "-dlopen" tape.la
 collectd_DEPENDENCIES += tape.la
 endif
 
+if BUILD_PLUGIN_TARGET_NOTIFICATION
+pkglib_LTLIBRARIES += target_notification.la
+target_notification_la_SOURCES = target_notification.c
+target_notification_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" target_notification.la
+collectd_DEPENDENCIES += target_notification.la
+endif
+
+if BUILD_PLUGIN_TARGET_REPLACE
+pkglib_LTLIBRARIES += target_replace.la
+target_replace_la_SOURCES = target_replace.c
+target_replace_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" target_replace.la
+collectd_DEPENDENCIES += target_replace.la
+endif
+
+if BUILD_PLUGIN_TARGET_SET
+pkglib_LTLIBRARIES += target_set.la
+target_set_la_SOURCES = target_set.c
+target_set_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" target_set.la
+collectd_DEPENDENCIES += target_set.la
+endif
+
 if BUILD_PLUGIN_TCPCONNS
 pkglib_LTLIBRARIES += tcpconns.la
 tcpconns_la_SOURCES = tcpconns.c
@@ -818,7 +913,7 @@ install-exec-hook:
        else \
                $(INSTALL) -m 0640 collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \
        fi; \
-       cp -f $(srcdir)/types.db $(DESTDIR)$(pkglibdir)/;
        $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)
+       $(INSTALL) -m 0644 $(srcdir)/types.db $(DESTDIR)$(pkgdatadir)/types.db;
        $(INSTALL) -m 0644 $(srcdir)/postgresql_default.conf \
                $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
index ca72a68cfa0fde685e59a84ec3cd20df3fc4d2d2..b995b1c58efa28af61a912a7bb86b24597788e31 100644 (file)
@@ -1,3 +1,53 @@
+/**
+ * collectd-nagios - src/collectd-nagios.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>
+ **/
+
+/* Set to C99 and POSIX code */
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+#ifndef _POSIX_SOURCE
+# define _POSIX_SOURCE
+#endif
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200112L
+#endif
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+/* Disable non-standard extensions */
+#ifdef _BSD_SOURCE
+# undef _BSD_SOURCE
+#endif
+#ifdef _SVID_SOURCE
+# undef _SVID_SOURCE
+#endif
+#ifdef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+
+#if !defined(__GNUC__) || !__GNUC__
+# define __attribute__(x) /**/
+#endif
+
 #include "config.h"
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
+#include <strings.h>
 #include <assert.h>
 
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#include "libcollectdclient/client.h"
+
 /*
  * This is copied directly from collectd.h. Make changes there!
  */
@@ -46,6 +99,7 @@
 #define CON_NONE     0
 #define CON_AVERAGE  1
 #define CON_SUM      2
+#define CON_PERCENTAGE  3
 
 struct range_s
 {
@@ -69,19 +123,91 @@ static int consolitation_g = CON_NONE;
 static char **match_ds_g = NULL;
 static int    match_ds_num_g = 0;
 
-static int ignore_ds (const char *name)
+/* `strdup' is an XSI extension. I don't want to pull in all of XSI just for
+ * that, so here's an own implementation.. It's easy enough. The GCC attributes
+ * are supposed to get good performance..  -octo */
+__attribute__((malloc, nonnull (1)))
+static char *cn_strdup (const char *str) /* {{{ */
 {
-       int i;
+  size_t strsize;
+  char *ret;
+
+  strsize = strlen (str) + 1;
+  ret = (char *) malloc (strsize);
+  if (ret != NULL)
+    memcpy (ret, str, strsize);
+  return (ret);
+} /* }}} char *cn_strdup */
+
+static int filter_ds (size_t *values_num,
+               double **values, char ***values_names)
+{
+       gauge_t *new_values;
+       char   **new_names;
+
+       size_t i;
 
        if (match_ds_g == NULL)
-               return (0);
+               return (RET_OKAY);
+
+       new_values = (gauge_t *)calloc (match_ds_num_g, sizeof (*new_values));
+       if (new_values == NULL)
+       {
+               fprintf (stderr, "malloc failed: %s\n", strerror (errno));
+               return (RET_UNKNOWN);
+       }
+
+       new_names = (char **)calloc (match_ds_num_g, sizeof (*new_names));
+       if (new_names == NULL)
+       {
+               fprintf (stderr, "malloc failed: %s\n", strerror (errno));
+               free (new_values);
+               return (RET_UNKNOWN);
+       }
 
        for (i = 0; i < match_ds_num_g; i++)
-               if (strcasecmp (match_ds_g[i], name) == 0)
-                       return (0);
+       {
+               size_t j;
+
+               /* match_ds_g keeps pointers into argv but the names will be freed */
+               new_names[i] = cn_strdup (match_ds_g[i]);
+               if (new_names[i] == NULL)
+               {
+                       fprintf (stderr, "cn_strdup failed: %s\n", strerror (errno));
+                       free (new_values);
+                       for (j = 0; j < i; j++)
+                               free (new_names[j]);
+                       free (new_names);
+                       return (RET_UNKNOWN);
+               }
+
+               for (j = 0; j < *values_num; j++)
+                       if (strcasecmp (new_names[i], (*values_names)[j]) == 0)
+                               break;
+
+               if (j == *values_num)
+               {
+                       printf ("ERROR: DS `%s' is not available.\n", new_names[i]);
+                       free (new_values);
+                       for (j = 0; j <= i; j++)
+                               free (new_names[j]);
+                       free (new_names);
+                       return (RET_CRITICAL);
+               }
+
+               new_values[i] = (*values)[j];
+       }
 
-       return (1);
-} /* int ignore_ds */
+       free (*values);
+       for (i = 0; i < *values_num; i++)
+               free ((*values_names)[i]);
+       free (*values_names);
+
+       *values       = new_values;
+       *values_names = new_names;
+       *values_num   = match_ds_num_g;
+       return (RET_OKAY);
+} /* int filter_ds */
 
 static void parse_range (char *string, range_t *range)
 {
@@ -124,7 +250,7 @@ static void parse_range (char *string, range_t *range)
                range->max = atof (max_ptr);
 } /* void parse_range */
 
-int match_range (range_t *range, double value)
+static int match_range (range_t *range, double value)
 {
        int ret = 0;
 
@@ -134,164 +260,7 @@ int match_range (range_t *range, double value)
                ret = 1;
 
        return (((ret - range->invert) == 0) ? 0 : 1);
-}
-
-static int get_values (int *ret_values_num, double **ret_values,
-               char ***ret_values_names)
-{
-       struct sockaddr_un sa;
-       int status;
-       int fd;
-       FILE *fh_in, *fh_out;
-       char buffer[4096];
-
-       int values_num;
-       double *values;
-       char **values_names;
-
-       int i;
-       int j;
-
-       fd = socket (PF_UNIX, SOCK_STREAM, 0);
-       if (fd < 0)
-       {
-               fprintf (stderr, "socket failed: %s\n",
-                               strerror (errno));
-               return (-1);
-       }
-
-       memset (&sa, '\0', sizeof (sa));
-       sa.sun_family = AF_UNIX;
-       strncpy (sa.sun_path, socket_file_g,
-                       sizeof (sa.sun_path) - 1);
-
-       status = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
-       if (status != 0)
-       {
-               fprintf (stderr, "connect failed: %s\n",
-                               strerror (errno));
-               return (-1);
-       }
-
-       fh_in = fdopen (fd, "r");
-       if (fh_in == NULL)
-       {
-               fprintf (stderr, "fdopen failed: %s\n",
-                               strerror (errno));
-               close (fd);
-               return (-1);
-       }
-
-       fh_out = fdopen (fd, "w");
-       if (fh_out == NULL)
-       {
-               fprintf (stderr, "fdopen failed: %s\n",
-                               strerror (errno));
-               fclose (fh_in);
-               return (-1);
-       }
-
-       fprintf (fh_out, "GETVAL %s/%s\n", hostname_g, value_string_g);
-       fflush (fh_out);
-
-       if (fgets (buffer, sizeof (buffer), fh_in) == NULL)
-       {
-               fprintf (stderr, "fgets failed: %s\n",
-                               strerror (errno));
-               fclose (fh_in);
-               fclose (fh_out);
-               return (-1);
-       }
-
-       {
-               char *ptr = strchr (buffer, ' ');
-
-               if (ptr != NULL)
-                       *ptr = '\0';
-
-               values_num = atoi (buffer);
-               if (values_num < 1)
-                       return (-1);
-       }
-
-       values = (double *) malloc (values_num * sizeof (double));
-       if (values == NULL)
-       {
-               fprintf (stderr, "malloc failed: %s\n",
-                               strerror (errno));
-               return (-1);
-       }
-
-       values_names = (char **) malloc (values_num * sizeof (char *));
-       if (values_names == NULL)
-       {
-               fprintf (stderr, "malloc failed: %s\n",
-                               strerror (errno));
-               free (values);
-               return (-1);
-       }
-       memset (values_names, 0, values_num * sizeof (char *));
-
-       i = 0; /* index of the values returned by the server */
-       j = 0; /* number of values in `values_names' and `values' */
-       while (fgets (buffer, sizeof (buffer), fh_in) != NULL)
-       {
-               do /* while (0) */
-               {
-                       char *key;
-                       char *value;
-                       char *endptr;
-
-                       key = buffer;
-
-                       value = strchr (key, '=');
-                       if (value == NULL)
-                       {
-                               fprintf (stderr, "Cannot parse line: %s\n", buffer);
-                               break;
-                       }
-                       *value = 0;
-                       value++;
-
-                       if (ignore_ds (key) != 0)
-                               break;
-
-                       endptr = NULL;
-                       errno = 0;
-                       values[j] = strtod (value, &endptr);
-                       if ((endptr == value) || (errno != 0))
-                       {
-                               fprintf (stderr, "Could not parse buffer "
-                                               "as number: %s\n", value);
-                               break;
-                       }
-
-                       values_names[j] = strdup (key);
-                       if (values_names[j] == NULL)
-                       {
-                               fprintf (stderr, "strdup failed.\n");
-                               break;
-                       }
-                       j++;
-               } while (0);
-
-               i++;
-               if (i >= values_num)
-                       break;
-       }
-       /* Set `values_num' to the number of values actually stored in the
-        * array. */
-       values_num = j;
-
-       fclose (fh_in); fh_in = NULL; fd = -1;
-       fclose (fh_out); fh_out = NULL;
-
-       *ret_values_num = values_num;
-       *ret_values = values;
-       *ret_values_names = values_names;
-
-       return (0);
-} /* int get_values */
+} /* int match_range */
 
 static void usage (const char *name)
 {
@@ -304,7 +273,7 @@ static void usage (const char *name)
                        "  -d <ds>        Select the DS to examine. May be repeated to examine multiple\n"
                        "                 DSes. By default all DSes are used.\n"
                        "  -g <consol>    Method to use to consolidate several DSes.\n"
-                       "                 Valid arguments are `none', `average' and `sum'\n"
+                       "                 See below for a list of valid arguments.\n"
                        "  -H <host>      Hostname to query the values for.\n"
                        "  -c <range>     Critical range\n"
                        "  -w <range>     Warning range\n"
@@ -315,17 +284,21 @@ static void usage (const char *name)
                        "  average:       Calculate the average of all matching DSes and apply the\n"
                        "                 warning- and critical-ranges to the calculated average.\n"
                        "  sum:           Apply the ranges to the sum of all DSes.\n"
+                       "  percentage:    Apply the ranges to the ratio (in percent) of the first value\n"
+                       "                 and the sum of all values."
                        "\n", name);
        exit (1);
 } /* void usage */
 
-int do_check_con_none (int values_num, double *values, char **values_names)
+static int do_check_con_none (size_t values_num,
+               double *values, char **values_names)
 {
-       int i;
-
        int num_critical = 0;
        int num_warning  = 0;
        int num_okay = 0;
+       const char *status_str = "UNKNOWN";
+       int status_code = RET_UNKNOWN;
+       int i;
 
        for (i = 0; i < values_num; i++)
        {
@@ -339,30 +312,49 @@ int do_check_con_none (int values_num, double *values, char **values_names)
                        num_okay++;
        }
 
-       printf ("%i critical, %i warning, %i okay",
+       if ((num_critical == 0) && (num_warning == 0) && (num_okay == 0))
+       {
+               printf ("WARNING: No defined values found\n");
+               return (RET_WARNING);
+       }
+       else if ((num_critical == 0) && (num_warning == 0))
+       {
+               status_str = "OKAY";
+               status_code = RET_OKAY;
+       }
+       else if (num_critical == 0)
+       {
+               status_str = "WARNING";
+               status_code = RET_WARNING;
+       }
+       else
+       {
+               status_str = "CRITICAL";
+               status_code = RET_CRITICAL;
+       }
+
+       printf ("%s: %i critical, %i warning, %i okay", status_str,
                        num_critical, num_warning, num_okay);
        if (values_num > 0)
        {
                printf (" |");
                for (i = 0; i < values_num; i++)
-                       printf (" %s=%lf;;;;", values_names[i], values[i]);
+                       printf (" %s=%g;;;;", values_names[i], values[i]);
        }
        printf ("\n");
 
-       if ((num_critical != 0) || (values_num == 0))
-               return (RET_CRITICAL);
-       else if (num_warning != 0)
-               return (RET_WARNING);
-
-       return (RET_OKAY);
+       return (status_code);
 } /* int do_check_con_none */
 
-int do_check_con_average (int values_num, double *values, char **values_names)
+static int do_check_con_average (size_t values_num,
+               double *values, char **values_names)
 {
        int i;
        double total;
        int total_num;
        double average;
+       const char *status_str = "UNKNOWN";
+       int status_code = RET_UNKNOWN;
 
        total = 0.0;
        total_num = 0;
@@ -376,30 +368,45 @@ int do_check_con_average (int values_num, double *values, char **values_names)
        }
 
        if (total_num == 0)
-               average = NAN;
-       else
-               average = total / total_num;
-       printf ("%lf average |", average);
-       for (i = 0; i < values_num; i++)
-               printf (" %s=%lf;;;;", values_names[i], values[i]);
-
-       if (total_num == 0)
+       {
+               printf ("WARNING: No defined values found\n");
                return (RET_WARNING);
+       }
 
-       if (isnan (average)
-                       || match_range (&range_critical_g, average))
-               return (RET_CRITICAL);
+       average = total / total_num;
+
+       if (match_range (&range_critical_g, average) != 0)
+       {
+               status_str = "CRITICAL";
+               status_code = RET_CRITICAL;
+       }
        else if (match_range (&range_warning_g, average) != 0)
-               return (RET_WARNING);
+       {
+               status_str = "WARNING";
+               status_code = RET_WARNING;
+       }
+       else
+       {
+               status_str = "OKAY";
+               status_code = RET_OKAY;
+       }
 
-       return (RET_OKAY);
+       printf ("%s: %g average |", status_str, average);
+       for (i = 0; i < values_num; i++)
+               printf (" %s=%g;;;;", values_names[i], values[i]);
+       printf ("\n");
+
+       return (status_code);
 } /* int do_check_con_average */
 
-int do_check_con_sum (int values_num, double *values, char **values_names)
+static int do_check_con_sum (size_t values_num,
+               double *values, char **values_names)
 {
        int i;
        double total;
        int total_num;
+       const char *status_str = "UNKNOWN";
+       int status_code = RET_UNKNOWN;
 
        total = 0.0;
        total_num = 0;
@@ -420,47 +427,150 @@ int do_check_con_sum (int values_num, double *values, char **values_names)
 
        if (match_range (&range_critical_g, total) != 0)
        {
-               printf ("CRITICAL: Sum = %lf\n", total);
-               return (RET_CRITICAL);
+               status_str = "CRITICAL";
+               status_code = RET_CRITICAL;
        }
        else if (match_range (&range_warning_g, total) != 0)
        {
-               printf ("WARNING: Sum = %lf\n", total);
-               return (RET_WARNING);
+               status_str = "WARNING";
+               status_code = RET_WARNING;
        }
        else
        {
-               printf ("OKAY: Sum = %lf\n", total);
-               return (RET_OKAY);
+               status_str = "OKAY";
+               status_code = RET_OKAY;
        }
 
-       return (RET_UNKNOWN);
+       printf ("%s: %g sum |", status_str, total);
+       for (i = 0; i < values_num; i++)
+               printf (" %s=%g;;;;", values_names[i], values[i]);
+       printf ("\n");
+
+       return (status_code);
 } /* int do_check_con_sum */
 
-int do_check (void)
+static int do_check_con_percentage (size_t values_num,
+               double *values, char **values_names)
 {
-       double  *values;
+       int i;
+       double sum = 0.0;
+       double percentage;
+
+       const char *status_str  = "UNKNOWN";
+       int         status_code = RET_UNKNOWN;
+
+       if ((values_num < 1) || (isnan (values[0])))
+       {
+               printf ("WARNING: The first value is not defined\n");
+               return (RET_WARNING);
+       }
+
+       for (i = 0; i < values_num; i++)
+               if (!isnan (values[i]))
+                       sum += values[i];
+
+       if (sum == 0.0)
+       {
+               printf ("WARNING: Values sum up to zero\n");
+               return (RET_WARNING);
+       }
+
+       percentage = 100.0 * values[0] / sum;
+
+       if (match_range (&range_critical_g, percentage) != 0)
+       {
+               status_str  = "CRITICAL";
+               status_code = RET_CRITICAL;
+       }
+       else if (match_range (&range_warning_g, percentage) != 0)
+       {
+               status_str  = "WARNING";
+               status_code = RET_WARNING;
+       }
+       else
+       {
+               status_str  = "OKAY";
+               status_code = RET_OKAY;
+       }
+
+       printf ("%s: %lf percent |", status_str, percentage);
+       for (i = 0; i < values_num; i++)
+               printf (" %s=%lf;;;;", values_names[i], values[i]);
+       return (status_code);
+} /* int do_check_con_percentage */
+
+static int do_check (void)
+{
+       lcc_connection_t *connection;
+       gauge_t *values;
        char   **values_names;
-       int      values_num;
+       size_t   values_num;
+       char address[1024];
+       char ident_str[1024];
+       lcc_identifier_t ident;
+       size_t i;
+       int status;
+
+       snprintf (address, sizeof (address), "unix:%s", socket_file_g);
+       address[sizeof (address) - 1] = 0;
+
+       snprintf (ident_str, sizeof (ident_str), "%s/%s",
+                       hostname_g, value_string_g);
+       ident_str[sizeof (ident_str) - 1] = 0;
+
+       connection = NULL;
+       status = lcc_connect (address, &connection);
+       if (status != 0)
+       {
+               printf ("ERROR: Connecting to daemon at %s failed.\n",
+                               socket_file_g);
+               return (RET_CRITICAL);
+       }
+
+       memset (&ident, 0, sizeof (ident));
+       status = lcc_string_to_identifier (connection, &ident, ident_str);
+       if (status != 0)
+       {
+               printf ("ERROR: Creating an identifier failed: %s.\n",
+                               lcc_strerror (connection));
+               LCC_DESTROY (connection);
+               return (RET_CRITICAL);
+       }
 
-       if (get_values (&values_num, &values, &values_names) != 0)
+       status = lcc_getval (connection, &ident,
+                       &values_num, &values, &values_names);
+       if (status != 0)
        {
-               fputs ("ERROR: Cannot get values from daemon\n", stdout);
+               printf ("ERROR: Retrieving values from the daemon failed: %s.\n",
+                               lcc_strerror (connection));
+               LCC_DESTROY (connection);
                return (RET_CRITICAL);
        }
 
+       LCC_DESTROY (connection);
+
+       status = filter_ds (&values_num, &values, &values_names);
+       if (status != RET_OKAY)
+               return (status);
+
+       status = RET_UNKNOWN;
        if (consolitation_g == CON_NONE)
-               return (do_check_con_none (values_num, values, values_names));
+               status =  do_check_con_none (values_num, values, values_names);
        else if (consolitation_g == CON_AVERAGE)
-               return (do_check_con_average (values_num, values, values_names));
+               status =  do_check_con_average (values_num, values, values_names);
        else if (consolitation_g == CON_SUM)
-               return (do_check_con_sum (values_num, values, values_names));
+               status = do_check_con_sum (values_num, values, values_names);
+       else if (consolitation_g == CON_PERCENTAGE)
+               status = do_check_con_percentage (values_num, values, values_names);
 
        free (values);
-       free (values_names); /* FIXME? */
+       if (values_names != NULL)
+               for (i = 0; i < values_num; i++)
+                       free (values_names[i]);
+       free (values_names);
 
-       return (RET_UNKNOWN);
-}
+       return (status);
+} /* int do_check */
 
 int main (int argc, char **argv)
 {
@@ -504,8 +614,14 @@ int main (int argc, char **argv)
                                        consolitation_g = CON_AVERAGE;
                                else if (strcasecmp (optarg, "sum") == 0)
                                        consolitation_g = CON_SUM;
+                               else if (strcasecmp (optarg, "percentage") == 0)
+                                       consolitation_g = CON_PERCENTAGE;
                                else
+                               {
+                                       fprintf (stderr, "Unknown consolidation function `%s'.\n",
+                                                       optarg);
                                        usage (argv[0]);
+                               }
                                break;
                        case 'd':
                        {
@@ -520,10 +636,10 @@ int main (int argc, char **argv)
                                        return (RET_UNKNOWN);
                                }
                                match_ds_g = tmp;
-                               match_ds_g[match_ds_num_g] = strdup (optarg);
+                               match_ds_g[match_ds_num_g] = cn_strdup (optarg);
                                if (match_ds_g[match_ds_num_g] == NULL)
                                {
-                                       fprintf (stderr, "strdup failed: %s\n",
+                                       fprintf (stderr, "cn_strdup failed: %s\n",
                                                        strerror (errno));
                                        return (RET_UNKNOWN);
                                }
@@ -537,7 +653,10 @@ int main (int argc, char **argv)
 
        if ((socket_file_g == NULL) || (value_string_g == NULL)
                        || (hostname_g == NULL))
+       {
+               fprintf (stderr, "Missing required arguments.\n");
                usage (argv[0]);
+       }
 
        return (do_check ());
 } /* int main */
index 49800255f266053fda3e990867c40a6fec80a7ba..c6347eac9361746c8f60b6b33713983d466da241 100644 (file)
@@ -63,6 +63,12 @@ The warning and critical ranges are applied to the average of all values.
 
 The warning and critical ranges are applied to the sum of all values.
 
+=item B<percentage>
+
+The warning and critical ranges are applied to the ratio (in percent) of the
+first value and the sum of all values. A warning is returned if the first
+value is not defined or if all values sum up to zero.
+
 =back
 
 =item B<-c> I<range>
@@ -96,7 +102,7 @@ As usual for Nagios plugins, this program writes a short, one line status
 message to STDOUT and signals success or failure with it's return value. It
 exits with a return value of B<0> for I<success>, B<1> for I<warning> and B<2>
 for I<critical>. If the values are not available or some other error occurred,
-it returns B<3> for I<unknown>. 
+it returns B<3> for I<unknown>.
 
 =head1 SEE ALSO
 
index 9f14257bb545fa714792985b1af22f1a1e177852..0c27edcf03d0226190d07ac6509c0b9cc9faa948 100644 (file)
@@ -9,7 +9,7 @@ FQDNLookup   true
 #BaseDir     "@prefix@/var/lib/@PACKAGE_NAME@"
 #PIDFile     "@prefix@/var/run/@PACKAGE_NAME@.pid"
 #PluginDir   "@prefix@/lib/@PACKAGE_NAME@"
-#TypesDB     "@prefix@/lib/@PACKAGE_NAME@/types.db"
+#TypesDB     "@prefix@/share/@PACKAGE_NAME@/types.db"
 #Interval     10
 #ReadThreads  5
 
@@ -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
@@ -63,11 +64,13 @@ FQDNLookup   true
 @BUILD_PLUGIN_NTPD_TRUE@LoadPlugin ntpd
 @BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut
 @BUILD_PLUGIN_ONEWIRE_TRUE@LoadPlugin onewire
+@BUILD_PLUGIN_OPENVPN_TRUE@LoadPlugin openvpn
 @BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl
 @BUILD_PLUGIN_PING_TRUE@LoadPlugin ping
 @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
@@ -80,7 +83,7 @@ FQDNLookup   true
 @BUILD_PLUGIN_THERMAL_TRUE@LoadPlugin thermal
 @BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock
 @BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
-@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
+#LoadPlugin uuid
 @BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
 @BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
 @BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless
@@ -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,6 +168,7 @@ FQDNLookup   true
 #              Name "*.conf"
 #              MTime "-5m"
 #              Size "+10k"
+#              Recursive true
 #      </Directory>
 #</Plugin>
 
@@ -318,6 +341,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
@@ -423,3 +453,26 @@ FQDNLookup   true
 #      Verbose false
 #</Plugin>
 
+# * * * * * * * * * * * * *
+# * FILTER CONFIGURATION  *
+# * * * * * * * * * * * * *
+
+# The following configures collectd's filtering mechanism. Before changing
+# anything in this section, please read the `FILTER CONFIGURATION' section in
+# the collectd.conf(5) manual page.
+
+# Load required matches:
+#@BUILD_PLUGIN_MATCH_REGEX_TRUE@LoadPlugin match_regex
+#@BUILD_PLUGIN_MATCH_VALUE_TRUE@LoadPlugin match_value
+
+# Load required targets:
+#@BUILD_PLUGIN_TARGET_NOTIFICATION_TRUE@LoadPlugin target_notification
+#@BUILD_PLUGIN_TARGET_REPLACE_TRUE@LoadPlugin target_replace
+#@BUILD_PLUGIN_TARGET_SET_TRUE@LoadPlugin target_set
+# The following block demonstrates the default behavior if no filtering is
+# configured at all: All values will be sent to all available write plugins.
+
+#<Chain "Main">
+#  Target "write"
+#</Chain>
index 7ca850674e5ad11fc01e0b71180a633685947ef9..df28cc3b887c11904d5c130d6d00111270b0be4a 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>
@@ -799,6 +1049,23 @@ Password needed to log into the database.
 Select this database. Defaults to I<no database> which is a perfectly reasonable
 option for what this plugin does.
 
+=item B<Port> I<Port>
+
+TCP-port to connect to. The port must be specified in its numeric form, but it
+must be passed as a string nonetheless. For example:
+
+  Port "3306"
+
+If B<Host> is set to B<localhost> (the default), this setting has no effect.
+See the documentation for the C<mysql_real_connect> function for details.
+
+=item B<Socket> I<Socket>
+
+Specifies the path to the UNIX domain socket of the MySQL server. This option
+only has any effect, if B<Host> is set to B<localhost> (the default).
+Otherwise, use the B<Port> option above. See the documentation for the
+C<mysql_real_connect> function for details.
+
 =back
 
 =head2 Plugin C<netlink>
@@ -1096,11 +1363,96 @@ controller, but as soon as you throw in a couple more senors and maybe a hub
 or two, reading all values will take more than ten seconds (the default
 interval). We will probably add some separate thread for reading the sensors
 and some cache or something like that, but it's not done yet. We will try to
-maintain backwards compatibility in the future, but we can't probmise. So in
-short: If it works for you: Great! But kaap in mind that the config I<might>
+maintain backwards compatibility in the future, but we can't promise. So in
+short: If it works for you: Great! But keep 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<openvpn>
+
+The OpenVPN plugin reads a status file maintained by OpenVPN and gathers
+traffic statistics about connected clients.
+
+To set up OpenVPN to write to the status file periodically, use the
+B<--status> option of OpenVPN. Since OpenVPN can write two different formats,
+you need to set the required format, too. This is done by setting
+B<--status-version> to B<2>.
+
+So, in a nutshell you need:
+
+  openvpn $OTHER_OPTIONS \
+    --status "/var/run/openvpn-status" 10 \
+    --status-version 2
+
+Available options:
+
+=over 4
+
+=item B<StatusFile> I<File>
+
+Specifies the location of the status file.
+
+=back
+
+=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 +1852,65 @@ collected for these selected processes are size of the resident segment size
 (RSS), user- and system-time used, number of processes and number of threads,
 and minor and major pagefaults.
 
+=item B<ProcessMatch> I<name> I<regex>
+
+Similar to the B<Process> option this allows to select more detailed
+statistics of processes matching the specified I<regex> (see L<regex(7)> for
+details). The statistics of all matching processes are summed up and
+dispatched to the daemon using the specified I<name> as an identifier. This
+allows to "group" several processes together. I<name> must not contain
+slashes.
+
+=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>
@@ -2077,6 +2488,481 @@ only one such notification is generated until the value appears again.
 
 =back
 
+=head1 FILTER CONFIGURATION
+
+TODO: Update this entire section once development is done.
+
+Starting with collectd 4.6 there is a powerful filtering infrastructure
+implemented in the daemon. The concept has mostly been copied from I<iptables>,
+the packet filter infrastructure for Linux. We'll use a similar terminology, so
+that users that are familiar with iptables feel right at home.
+
+=head2 Terminology
+
+The most important terms are:
+
+=over 4
+
+=item B<Match>
+
+A I<match> is a criteria to select specific values. Examples are, of course, the
+name of the value or it's current value.
+
+=item B<Target>
+
+A I<target> is some action that is to be performed with data. Such actions
+could, for example, be to change part of the value's identifier or to ignore
+the value completely. Built-in functions are B<write> and B<stop>, see below.
+
+Some targets, for example the built-in B<stop> target, signal that processing
+of a value should be stopped. In that case processing of the current chain will
+be aborted.
+
+=item B<Rule>
+
+The combination of any number of matches and at least one target is called a
+I<rule>. The target actions will be performed for all values for which B<all>
+matches apply. If the rule does not have any matches associated with it, the
+target action will be performed for all values.
+
+If any target returns the stop condition, the processing will stop right away.
+This means that any targets following the current one will not be called after
+the stop condition has been returned.
+
+=item B<Chain>
+
+A I<chain> is a list of rules and possibly default targets. The rules are tried
+in order and if one matches, the associated target will be called. If a value
+is handled by a rule, it depends on the target whether or not any subsequent
+rules are considered or if traversal of the chain is aborted. After all rules
+have been checked and no target returned the stop condition, the default
+targets will be executed.
+
+=back
+
+=head2 General structure
+
+The following shows the resulting structure:
+
+ +---------+
+ ! Chain   !
+ +---------+
+      !
+      V
+ +---------+  +---------+  +---------+  +---------+
+ ! Rule    !->! Match   !->! Match   !->! Target  !
+ +---------+  +---------+  +---------+  +---------+
+      !
+      V
+ +---------+  +---------+  +---------+
+ ! Rule    !->! Target  !->! Target  !
+ +---------+  +---------+  +---------+
+      !
+      V
+      :
+      :
+      !
+      V
+ +---------+  +---------+  +---------+
+ ! Rule    !->! Match   !->! Target  !
+ +---------+  +---------+  +---------+
+      !
+      V
+ +---------+
+ ! Default !
+ ! Target  !
+ +---------+
+
+=head2 Synopsis
+
+The configuration reflects this structure directly:
+
+ <Chain "main">
+   <Rule "ignore_mysql_show">
+     <Match "regex">
+       Plugin "^mysql$"
+       Type "^mysql_command$"
+       TypeInstance "^show_"
+     </Match>
+     <Target "stop">
+     </Target>
+   </Rule>
+   <Target "write">
+     Plugin "rrdtool"
+   </Target>
+ </Chain>
+
+The above configuration example will ignore all values where the plugin field
+is "mysql", the type is "mysql_command" and the type instance begins with
+"show_". All other values will be sent to the "rrdtool" write plugin via the
+default target of the chain.
+
+=head2 List of configuration options
+
+=over 4
+
+=item B<Chain> I<Name>
+
+Adds a new chain with a certain name. This name can be used to refer to a
+specific chain, for example to jump to it.
+
+Within the B<Chain> block, there can be B<Rule> blocks and B<Target> blocks.
+
+=item B<Rule> [I<Name>]
+
+Adds a new rule to the current chain. The name of the rule is optional and
+currently has no meaning for the daemon.
+
+Within the B<Rule> block, there may be any number of B<Match> blocks and there
+must be at least one B<Target> block.
+
+=item B<Match> I<Name>
+
+Adds a match to a B<Rule> block. The name specifies what kind of match should
+be performed. Available matches depend on the plugins that have been loaded.
+
+The arguments inside the B<Match> block are passed to the plugin implementing
+the match, so which arguments are valid here depends on the plugin being used.
+If you do not need any to pass any arguments to a match, you can use the
+shorter syntax:
+
+ Match "foobar"
+
+Which is equivalent to:
+
+ <Match "foobar">
+ </Match>
+
+=item B<Target> I<Name>
+
+Add a target to a rule or a default target to a chain. The name specifies what
+kind of target is to be added. Which targets are available depends on the
+plugins being loaded.
+
+The arguments inside the B<Target> block are passed to the plugin implementing
+the target, so which arguments are valid here depends on the plugin being used.
+If you do not need any to pass any arguments to a target, you can use the
+shorter syntax:
+
+ Target "stop"
+
+This is the same as writing:
+
+ <Target "stop">
+ </Target>
+
+=back
+
+=head2 Built-in targets 
+
+The following targets are built into the core daemon and therefore need no
+plugins to be loaded:
+
+=over 4
+
+=item B<return>
+
+Signals the "return" condition. This causes the current chain to stop
+processing the value and returns control to the calling chain. The calling
+chain will continue processing targets and rules just after the B<jump> target
+(see below). This is very similar to the B<RETURN> target of iptables, see
+L<iptables(8)>.
+
+This target does not have any options.
+
+Example:
+
+ Target "return"
+
+=item B<stop>
+
+Signals the "stop" condition, causing processing of the value to be aborted
+immediately. This is similar to the B<DROP> target of iptables, see
+L<iptables(8)>.
+
+This target does not have any options.
+
+Example:
+
+ Target "stop"
+
+=item B<write>
+
+Sends the value to write plugins.
+
+Available options:
+
+=over 4
+
+=item B<Plugin> I<Name>
+
+Name of the write plugin to which the data should be sent. This option may be
+given multiple times to send the data to more than one write plugin.
+
+=back
+
+If no plugin is explicitly specified, the values will be sent to all available
+write plugins.
+
+Example:
+
+ <Target "write">
+   Plugin "rrdtool"
+ </Target>
+
+=item B<jump>
+
+Starts processing the rules of another chain. If the end of that chain is
+reached, or a stop condition is encountered, processing will continue right
+after the B<jump> target, i.E<nbsp>e. with the next target or the next rule.
+This is similar to the B<-j> command line option of iptables, see
+L<iptables(8)>.
+
+Available options:
+
+=over 4
+
+=item B<Chain> I<Name>
+
+Jumps to the chain I<Name>. This argument is required and may appear only once.
+
+=back
+
+Example:
+
+ <Target "jump">
+   Chain "foobar"
+ </Target>
+
+=back
+
+=head2 Available matches
+
+=over 4
+
+=item B<regex>
+
+Matches a value using regular expressions.
+
+Available options:
+
+=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>
+
+Match values where the given regular expressions match the various fields of
+the identifier of a value. If multiple regular expressions are given, B<all>
+regexen must match for a value to match.
+
+=back
+
+Example:
+
+ <Match "regex">
+   Host "customer[0-9]+"
+   Plugin "^foobar$"
+ </Match>
+
+=item B<value>
+
+Matches the actual value of data sources against given minimumE<nbsp>/ maximum
+values. If a data-set consists of more than one data-source, all data-sources
+must match the specified ranges for a positive match.
+
+Available options:
+
+=over 4
+
+=item B<Min> I<Value>
+
+Sets the smallest value which still results in a match. If unset, behaves like
+negative infinity.
+
+=item B<Max> I<Value>
+
+Sets the largest value which still results in a match. If unset, behaves like
+positive infinity.
+
+=item B<Invert> B<true>|B<false>
+
+Inverts the selection. If the B<Min> and B<Max> settings result in a match,
+no-match is returned and vice versa.
+
+=back
+
+Either B<Min> or B<Max>, but not both, may be unset.
+
+Example:
+
+ # Match all values smaller than or equal to 100.
+ <Match "value">
+   Max 100
+ </Match>
+
+=back
+
+=head2 Available targets
+
+=over 4
+
+=item B<notification>
+
+Creates and dispatches a notification.
+
+Available options:
+
+=over 4
+
+=item B<Message> I<String>
+
+This required option sets the message of the notification. The following
+placeholders will be replaced by an appropriate value:
+
+=over 4
+
+=item B<%{host}>
+
+=item B<%{plugin}>
+
+=item B<%{plugin_instance}>
+
+=item B<%{type}>
+
+=item B<%{type_instance}>
+
+These placeholders are replaced by the identifier field of the same name.
+
+=item B<%{ds:>I<name>B<}>
+
+These placeholders are replaced by a (hopefully) human readable representation
+of the current rate of this data source. If you changed the instance name
+(using the B<set> or B<replace> targets, see below), it may not be possible to
+convert counter values to rates.
+
+=back
+
+Please note that these placeholders are B<case sensitive>!
+
+=item B<Severity> B<"FATAL">|B<"WARNING">|B<"OKAY">
+
+Sets the severity of the message. If omitted, the severity B<"WARNING"> is
+used.
+
+=back
+
+Example:
+
+  <Target "notification">
+    Message "Oops, the %{type_instance} temperature is currently %{ds:value}!"
+    Severity "WARNING"
+  </Target>
+
+=item B<replace>
+
+Replaces parts of the identifier using regular expressions.
+
+Available options:
+
+=over 4
+
+=item B<Host> I<Regex> I<Replacement>
+
+=item B<Plugin> I<Regex> I<Replacement>
+
+=item B<PluginInstance> I<Regex> I<Replacement>
+
+=item B<TypeInstance> I<Regex> I<Replacement>
+
+Match the appropriate field with the given regular expression I<Regex>. If the
+regular expression matches, that part that matches is replaced with
+I<Replacement>. If multiple places of the input buffer match a given regular
+expression, only the first occurrence will be replaced.
+
+You can specify each option multiple times to use multiple regular expressions
+one after another.
+
+=back
+
+Example:
+
+ <Target "replace">
+   # Replace "example.net" with "example.com"
+   Host "\\<example.net\\>" "example.com"
+   # Strip "www." from hostnames
+   Host "\\<www\\." ""
+ </Target>
+
+=item B<set>
+
+Sets part of the identifier of a value to a given string.
+
+Available options:
+
+=over 4
+
+=item B<Host> I<String>
+
+=item B<Plugin> I<String>
+
+=item B<PluginInstance> I<String>
+
+=item B<TypeInstance> I<String>
+
+Set the appropriate field to the given string. The strings for plugin instance
+and type instance may be empty, the strings for host and plugin may not be
+empty. It's currently not possible to set the type of a value this way.
+
+=back
+
+Example:
+
+ <Target "set">
+   PluginInstance "coretemp"
+   TypeInstance "core3"
+ </Target>
+
+=back
+
+=head2 Backwards compatibility
+
+If you use collectd with an old configuration, i.E<nbsp>e. one without a
+B<Chain> block, it will behave as it used to. This is equivalent to the
+following configuration:
+
+ <Chain "main">
+   Target "write"
+ </Chain>
+
+If you specify a B<Chain> block anywhere, the B<write> target will not be added
+anywhere and you will have to make sure that it is called where appropriate. We
+suggest to add the above snippet as default target to your main chain.
+
+TODO: Notifications will be implemented using chains, too. Describe that here!
+
+=head2 Examples
+
+Ignore all values, where the hostname does not contain a dot, i.E<nbsp>e. can't
+be an FQDN.
+
+ <Chain "main">
+   <Rule "no_fqdn">
+     <Match "regex">
+       Host "^[^\.]*$"
+     </Match>
+     Target "stop"
+   </Rule>
+   Target "write"
+ </Chain>
+
 =head1 SEE ALSO
 
 L<collectd(1)>,
@@ -2085,9 +2971,12 @@ L<collectd-perl(5)>,
 L<collectd-unixsock(5)>,
 L<types.db(5)>,
 L<hddtemp(8)>,
+L<iptables(8)>,
 L<kstat(3KSTAT)>,
 L<mbmon(1)>,
+L<pcre(3)>,
 L<psql(1)>,
+L<regex(7)>,
 L<rrdtool(1)>,
 L<sensors(1)>
 
index 119d284fdfd2d72d94e98220953151d33ea256ff..ae89c44f9aee71ceaf5ceb8df2e117ef418d2d1f 100644 (file)
@@ -77,15 +77,21 @@ int ssnprintf (char *dest, size_t n, const char *format, ...)
 char *sstrdup (const char *s)
 {
        char *r;
+       size_t sz;
 
        if (s == NULL)
                return (NULL);
 
-       if((r = strdup (s)) == NULL)
+       /* Do not use `strdup' here, because it's not specified in POSIX. It's
+        * ``only'' an XSI extension. */
+       sz = strlen (s) + 1;
+       r = (char *) malloc (sizeof (char) * sz);
+       if (r == NULL)
        {
-               ERROR ("Not enough memory.");
+               ERROR ("sstrdup: Out of memory.");
                exit (3);
        }
+       memcpy (r, s, sizeof (char) * sz);
 
        return (r);
 } /* char *sstrdup */
index bb57ca26a23e77444b8bafb157e385b8468c84e5..f9ade29b7f8001404a2fadd4f156964f56fc2b28 100644 (file)
@@ -30,6 +30,7 @@
 #include "configfile.h"
 #include "types_list.h"
 #include "utils_threshold.h"
+#include "filter_chain.h"
 
 #if HAVE_WORDEXP_H
 # include <wordexp.h>
@@ -341,6 +342,8 @@ static int dispatch_block (oconfig_item_t *ci)
                return (dispatch_block_plugin (ci));
        else if (strcasecmp (ci->key, "Threshold") == 0)
                return (ut_config (ci));
+       else if (strcasecmp (ci->key, "Chain") == 0)
+               return (fc_configure (ci));
 
        return (0);
 }
@@ -892,7 +895,7 @@ int cf_read (char *filename)
 
        /* Read the default types.db if no `TypesDB' option was given. */
        if (cf_default_typesdb)
-               read_types_list (PLUGINDIR"/types.db");
+               read_types_list (PKGDATADIR"/types.db");
 
        return (0);
 } /* int cf_read */
diff --git a/src/dbi.c b/src/dbi.c
new file mode 100644 (file)
index 0000000..e1dd1bc
--- /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 (connection, NULL) != 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 (db->connection, NULL) != 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 (db->connection, NULL) != 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 f0713795f52100f909cb61a6fb52f313a19737d5..e2b002074fd9462fc0bc047e1bdc3b11b2428a1d 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_chain.c b/src/filter_chain.c
new file mode 100644 (file)
index 0000000..01cd224
--- /dev/null
@@ -0,0 +1,1058 @@
+/**
+ * collectd - src/filter_chain.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>
+ **/
+
+/*
+ * First tell the compiler to stick to the C99 and POSIX standards as close as
+ * possible.
+ */
+#ifndef __STRICT_ANSI__ /* {{{ */
+# define __STRICT_ANSI__
+#endif
+
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+
+#ifdef _POSIX_C_SOURCE
+# undef _POSIX_C_SOURCE
+#endif
+#define _POSIX_C_SOURCE 200112L
+
+#if 0
+/* Single UNIX needed for strdup. */
+#ifdef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
+#endif
+#define _XOPEN_SOURCE 500
+#endif
+
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+#ifndef _THREAD_SAFE
+# define _THREAD_SAFE
+#endif
+
+#ifdef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* }}} */
+
+#include "collectd.h"
+#include "configfile.h"
+#include "plugin.h"
+#include "common.h"
+#include "filter_chain.h"
+
+/*
+ * Data types
+ */
+/* List of matches, used in fc_rule_t and for the global `match_list_head'
+ * variable. */
+struct fc_match_s;
+typedef struct fc_match_s fc_match_t; /* {{{ */
+struct fc_match_s
+{
+  char name[DATA_MAX_NAME_LEN];
+  match_proc_t proc;
+  void *user_data;
+  fc_match_t *next;
+}; /* }}} */
+
+/* List of targets, used in fc_rule_t and for the global `target_list_head'
+ * variable. */
+struct fc_target_s;
+typedef struct fc_target_s fc_target_t; /* {{{ */
+struct fc_target_s
+{
+  char name[DATA_MAX_NAME_LEN];
+  void *user_data;
+  target_proc_t proc;
+  fc_target_t *next;
+}; /* }}} */
+
+/* List of rules, used in fc_chain_t */
+struct fc_rule_s;
+typedef struct fc_rule_s fc_rule_t; /* {{{ */
+struct fc_rule_s
+{
+  char name[DATA_MAX_NAME_LEN];
+  fc_match_t  *matches;
+  fc_target_t *targets;
+  fc_rule_t *next;
+}; /* }}} */
+
+/* List of chains, used for `chain_list_head' */
+struct fc_chain_s;
+typedef struct fc_chain_s fc_chain_t; /* {{{ */
+struct fc_chain_s
+{
+  char name[DATA_MAX_NAME_LEN];
+  fc_rule_t   *rules;
+  fc_target_t *targets;
+  fc_chain_t  *next;
+}; /* }}} */
+
+/*
+ * Global variables
+ */
+static fc_match_t  *match_list_head;
+static fc_target_t *target_list_head;
+static fc_chain_t  *chain_list_head;
+
+/*
+ * Private functions
+ */
+static void fc_free_matches (fc_match_t *m) /* {{{ */
+{
+  if (m == NULL)
+    return;
+
+  if (m->proc.destroy != NULL)
+    (*m->proc.destroy) (&m->user_data);
+  else if (m->user_data != NULL)
+  {
+    ERROR ("Filter sybsystem: fc_free_matches: There is user data, but no "
+        "destroy functions has been specified. "
+        "Memory will probably be lost!");
+  }
+
+  if (m->next != NULL)
+    fc_free_matches (m->next);
+
+  free (m);
+} /* }}} void fc_free_matches */
+
+static void fc_free_targets (fc_target_t *t) /* {{{ */
+{
+  if (t == NULL)
+    return;
+
+  if (t->proc.destroy != NULL)
+    (*t->proc.destroy) (&t->user_data);
+  else if (t->user_data != NULL)
+  {
+    ERROR ("Filter sybsystem: fc_free_targets: There is user data, but no "
+        "destroy functions has been specified. "
+        "Memory will probably be lost!");
+  }
+
+  if (t->next != NULL)
+    fc_free_targets (t->next);
+
+  free (t);
+} /* }}} void fc_free_targets */
+
+static void fc_free_rules (fc_rule_t *r) /* {{{ */
+{
+  if (r == NULL)
+    return;
+
+  fc_free_matches (r->matches);
+  fc_free_targets (r->targets);
+
+  if (r->next != NULL)
+    fc_free_rules (r->next);
+
+  free (r);
+} /* }}} void fc_free_rules */
+
+static void fc_free_chains (fc_chain_t *c) /* {{{ */
+{
+  if (c == NULL)
+    return;
+
+  fc_free_rules (c->rules);
+  fc_free_targets (c->targets);
+
+  if (c->next != NULL)
+    fc_free_chains (c->next);
+
+  free (c);
+} /* }}} void fc_free_chains */
+
+static char *fc_strdup (const char *orig) /* {{{ */
+{
+  size_t sz;
+  char *dest;
+
+  if (orig == NULL)
+    return (NULL);
+
+  sz = strlen (orig) + 1;
+  dest = (char *) malloc (sz);
+  if (dest == NULL)
+    return (NULL);
+
+  memcpy (dest, orig, sz);
+
+  return (dest);
+} /* }}} char *fc_strdup */
+
+/*
+ * Configuration.
+ *
+ * The configuration looks somewhat like this:
+ *
+ *  <Chain "main">
+ *    <Rule>
+ *      <Match "regex">
+ *        Plugin "^mysql$"
+ *        Type "^mysql_command$"
+ *        TypeInstance "^show_"
+ *      </Match>
+ *      <Target "drop">
+ *      </Target>
+ *    </Rule>
+ *
+ *    <Target "write">
+ *      Plugin "rrdtool"
+ *    </Target>
+ *  </Chain>
+ */
+static int fc_config_add_match (fc_match_t **matches_head, /* {{{ */
+    oconfig_item_t *ci)
+{
+  fc_match_t *m;
+  fc_match_t *ptr;
+  int status;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("Filter subsystem: `Match' blocks require "
+        "exactly one string argument.");
+    return (-1);
+  }
+
+  ptr = match_list_head;
+  while (ptr != NULL)
+  {
+    if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
+      break;
+    ptr = ptr->next;
+  }
+
+  if (ptr == NULL)
+  {
+    WARNING ("Filter subsystem: Cannot find a \"%s\" match. "
+        "Did you load the appropriate plugin?",
+        ci->values[0].value.string);
+    return (-1);
+  }
+
+  m = (fc_match_t *) malloc (sizeof (*m));
+  if (m == NULL)
+  {
+    ERROR ("fc_config_add_match: malloc failed.");
+    return (-1);
+  }
+  memset (m, 0, sizeof (*m));
+
+  sstrncpy (m->name, ptr->name, sizeof (m->name));
+  memcpy (&m->proc, &ptr->proc, sizeof (m->proc));
+  assert (m->proc.create != NULL);
+  m->user_data = NULL;
+  m->next = NULL;
+
+  status = (*m->proc.create) (ci, &m->user_data);
+  if (status != 0)
+  {
+    WARNING ("Filter subsystem: Failed to create a %s match.",
+        m->name);
+    fc_free_matches (m);
+    return (-1);
+  }
+  
+  if (*matches_head != NULL)
+  {
+    ptr = *matches_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = m;
+  }
+  else
+  {
+    *matches_head = m;
+  }
+
+  return (0);
+} /* }}} int fc_config_add_match */
+
+static int fc_config_add_target (fc_target_t **targets_head, /* {{{ */
+    oconfig_item_t *ci)
+{
+  fc_target_t *t;
+  fc_target_t *ptr;
+  int status;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("Filter subsystem: `Target' blocks require "
+        "exactly one string argument.");
+    return (-1);
+  }
+
+  ptr = target_list_head;
+  while (ptr != NULL)
+  {
+    if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
+      break;
+    ptr = ptr->next;
+  }
+
+  if (ptr == NULL)
+  {
+    WARNING ("Filter subsystem: Cannot find a \"%s\" target. "
+        "Did you load the appropriate plugin?",
+        ci->values[0].value.string);
+    return (-1);
+  }
+
+  t = (fc_target_t *) malloc (sizeof (*t));
+  if (t == NULL)
+  {
+    ERROR ("fc_config_add_match: malloc failed.");
+    return (-1);
+  }
+  memset (t, 0, sizeof (*t));
+
+  sstrncpy (t->name, ptr->name, sizeof (t->name));
+  memcpy (&t->proc, &ptr->proc, sizeof (t->proc));
+  t->user_data = NULL;
+  t->next = NULL;
+
+  if (t->proc.create != NULL)
+  {
+    status = (*t->proc.create) (ci, &t->user_data);
+    if (status != 0)
+    {
+      WARNING ("Filter subsystem: Failed to create a %s match.",
+          t->name);
+      fc_free_targets (t);
+      return (-1);
+    }
+  }
+  else
+  {
+    t->user_data = NULL;
+  }
+  
+  if (*targets_head != NULL)
+  {
+    ptr = *targets_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = t;
+  }
+  else
+  {
+    *targets_head = t;
+  }
+
+  return (0);
+} /* }}} int fc_config_add_target */
+
+static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */
+    oconfig_item_t *ci)
+{
+  fc_rule_t *rule;
+  char rule_name[2*DATA_MAX_NAME_LEN] = "Unnamed rule";
+  int status = 0;
+  int i;
+
+  if (ci->values_num > 1)
+  {
+    WARNING ("Filter subsystem: `Rule' blocks have at most one argument.");
+    return (-1);
+  }
+  else if ((ci->values_num == 1)
+      && (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("Filter subsystem: `Rule' blocks expect one string argument "
+        "or no argument at all.");
+    return (-1);
+  }
+
+  rule = (fc_rule_t *) malloc (sizeof (*rule));
+  if (rule == NULL)
+  {
+    ERROR ("fc_config_add_rule: malloc failed.");
+    return (-1);
+  }
+  memset (rule, 0, sizeof (*rule));
+  rule->next = NULL;
+
+  if (ci->values_num == 1)
+  {
+    sstrncpy (rule->name, ci->values[0].value.string, sizeof (rule->name));
+    ssnprintf (rule_name, sizeof (rule_name), "Rule \"%s\"",
+        ci->values[0].value.string);
+  }
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *option = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Match", option->key) == 0)
+      status = fc_config_add_match (&rule->matches, option);
+    else if (strcasecmp ("Target", option->key) == 0)
+      status = fc_config_add_target (&rule->targets, option);
+    else
+    {
+      WARNING ("Filter subsystem: %s: Option `%s' not allowed "
+          "inside a <Rule> block.", rule_name, option->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  } /* for (ci->children) */
+
+  /* Additional sanity checking. */
+  while (status == 0)
+  {
+    if (rule->targets == NULL)
+    {
+      WARNING ("Filter subsystem: %s: No target has been specified.",
+          rule_name);
+      status = -1;
+      break;
+    }
+
+    break;
+  } /* while (status == 0) */
+
+  if (status != 0)
+  {
+    fc_free_rules (rule);
+    return (-1);
+  }
+
+  if (chain->rules != NULL)
+  {
+    fc_rule_t *ptr;
+
+    ptr = chain->rules;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = rule;
+  }
+  else
+  {
+    chain->rules = rule;
+  }
+
+  return (0);
+} /* }}} int fc_config_add_rule */
+
+static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
+{
+  fc_chain_t *chain;
+  int status = 0;
+  int i;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("Filter subsystem: <Chain> blocks require exactly one "
+        "string argument.");
+    return (-1);
+  }
+
+  chain = (fc_chain_t *) malloc (sizeof (*chain));
+  if (chain == NULL)
+  {
+    ERROR ("fc_config_add_chain: malloc failed.");
+    return (-1);
+  }
+  memset (chain, 0, sizeof (*chain));
+  sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name));
+  chain->rules = NULL;
+  chain->targets = NULL;
+  chain->next = NULL;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *option = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Rule", option->key) == 0)
+      status = fc_config_add_rule (chain, option);
+    else if (strcasecmp ("Target", option->key) == 0)
+      status = fc_config_add_target (&chain->targets, option);
+    else
+    {
+      WARNING ("Filter subsystem: Chain %s: Option `%s' not allowed "
+          "inside a <Chain> block.", chain->name, option->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  } /* for (ci->children) */
+
+  /* Additional sanity checking. */
+  while (status == 0)
+  {
+    if (chain->targets == NULL)
+    {
+      WARNING ("Filter subsystem: Chain %s: No default target has been "
+          "specified. Please make sure that there is a <Target> block within "
+          "the <Chain> block!", chain->name);
+      status = -1;
+      break;
+    }
+
+    break;
+  } /* while (status == 0) */
+
+  if (status != 0)
+  {
+    fc_free_chains (chain);
+    return (-1);
+  }
+
+  if (chain_list_head != NULL)
+  {
+    fc_chain_t *ptr;
+
+    ptr = chain_list_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = chain;
+  }
+  else
+  {
+    chain_list_head = chain;
+  }
+
+  return (0);
+} /* }}} int fc_config_add_chain */
+
+int fc_process_chain (const data_set_t *ds, value_list_t *vl, /* {{{ */
+    fc_chain_t *chain)
+{
+  fc_rule_t *rule;
+  fc_target_t *target;
+  int status;
+
+  if (chain == NULL)
+    return (-1);
+
+  DEBUG ("fc_process_chain (chain = %s);", chain->name);
+
+  status = FC_TARGET_CONTINUE;
+  for (rule = chain->rules; rule != NULL; rule = rule->next)
+  {
+    fc_match_t *match;
+
+    if (rule->name[0] != 0)
+    {
+      DEBUG ("fc_process_chain (%s): Testing the `%s' rule.",
+          chain->name, rule->name);
+    }
+
+    /* N. B.: rule->matches may be NULL. */
+    for (match = rule->matches; match != NULL; match = match->next)
+    {
+      status = (*match->proc.match) (ds, vl, /* meta = */ NULL,
+          &match->user_data);
+      if (status < 0)
+      {
+        WARNING ("fc_process_chain (%s): A match failed.", chain->name);
+        break;
+      }
+      else if (status != FC_MATCH_MATCHES)
+        break;
+    }
+
+    /* for-loop has been aborted: Either error or no match. */
+    if (match != NULL)
+    {
+      status = FC_TARGET_CONTINUE;
+      continue;
+    }
+
+    if (rule->name[0] != 0)
+    {
+      DEBUG ("fc_process_chain (%s): Rule `%s' matches.",
+          chain->name, rule->name);
+    }
+
+    for (target = rule->targets; target != NULL; target = target->next)
+    {
+      /* If we get here, all matches have matched the value. Execute the
+       * target. */
+      status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
+          &target->user_data);
+      if (status < 0)
+      {
+        WARNING ("fc_process_chain (%s): A target failed.", chain->name);
+        continue;
+      }
+      else if (status == FC_TARGET_CONTINUE)
+        continue;
+      else if (status == FC_TARGET_STOP)
+        break;
+      else if (status == FC_TARGET_RETURN)
+        break;
+      else
+      {
+        WARNING ("fc_process_chain (%s): Unknown return value "
+            "from target `%s': %i",
+            chain->name, target->name, status);
+      }
+    }
+
+    if ((status == FC_TARGET_STOP)
+        || (status == FC_TARGET_RETURN))
+    {
+      if (rule->name[0] != 0)
+      {
+        DEBUG ("fc_process_chain (%s): Rule `%s' signaled "
+            "the %s condition.",
+            chain->name, rule->name,
+            (status == FC_TARGET_STOP) ? "stop" : "return");
+      }
+      break;
+    }
+    else
+    {
+      status = FC_TARGET_CONTINUE;
+    }
+  } /* for (rule) */
+
+  if (status == FC_TARGET_STOP)
+    return (FC_TARGET_STOP);
+  else if (status == FC_TARGET_RETURN)
+    return (FC_TARGET_CONTINUE);
+
+  /* for-loop has been aborted: A target returned `FC_TARGET_STOP' */
+  if (rule != NULL)
+    return (FC_TARGET_CONTINUE);
+
+  DEBUG ("fc_process_chain (%s): Executing the default targets.",
+      chain->name);
+
+  status = FC_TARGET_CONTINUE;
+  for (target = chain->targets; target != NULL; target = target->next)
+  {
+    /* If we get here, all matches have matched the value. Execute the
+     * target. */
+    status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
+        &target->user_data);
+    if (status < 0)
+    {
+      WARNING ("fc_process_chain (%s): The default target failed.",
+          chain->name);
+    }
+    else if (status == FC_TARGET_CONTINUE)
+      continue;
+    else if (status == FC_TARGET_STOP)
+      break;
+    else if (status == FC_TARGET_RETURN)
+      break;
+    else
+    {
+      WARNING ("fc_process_chain (%s): Unknown return value "
+          "from target `%s': %i",
+          chain->name, target->name, status);
+    }
+  }
+
+  if ((status == FC_TARGET_STOP)
+      || (status == FC_TARGET_RETURN))
+  {
+    assert (target != NULL);
+    DEBUG ("fc_process_chain (%s): Default target `%s' signaled "
+        "the %s condition.",
+        chain->name, target->name,
+        (status == FC_TARGET_STOP) ? "stop" : "return");
+    if (status == FC_TARGET_STOP)
+      return (FC_TARGET_STOP);
+    else
+      return (FC_TARGET_CONTINUE);
+  }
+
+  DEBUG ("fc_process_chain (%s): Signaling `continue' at end of chain.",
+      chain->name);
+
+  return (FC_TARGET_CONTINUE);
+} /* }}} int fc_process_chain */
+
+/*
+ * Built-in target "jump"
+ *
+ * Prefix `bit' like `_b_uilt-_i_n _t_arget'
+ */
+static int fc_bit_jump_create (const oconfig_item_t *ci, /* {{{ */
+    void **user_data)
+{
+  oconfig_item_t *ci_chain;
+
+  if (ci->children_num != 1)
+  {
+    ERROR ("Filter subsystem: The built-in target `jump' needs exactly "
+        "one `Chain' argument!");
+    return (-1);
+  }
+
+  ci_chain = ci->children;
+  if (strcasecmp ("Chain", ci_chain->key) != 0)
+  {
+    ERROR ("Filter subsystem: The built-in target `jump' does not "
+        "support the configuration option `%s'.",
+        ci_chain->key);
+    return (-1);
+  }
+
+  if ((ci_chain->values_num != 1)
+      || (ci_chain->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    ERROR ("Filter subsystem: Built-in target `jump': The `Chain' option "
+        "needs exactly one string argument.");
+    return (-1);
+  }
+
+  *user_data = fc_strdup (ci_chain->values[0].value.string);
+  if (*user_data == NULL)
+  {
+    ERROR ("fc_bit_jump_create: fc_strdup failed.");
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int fc_bit_jump_create */
+
+static int fc_bit_jump_destroy (void **user_data) /* {{{ */
+{
+  if (user_data != NULL)
+  {
+    free (*user_data);
+    *user_data = NULL;
+  }
+
+  return (0);
+} /* }}} int fc_bit_jump_destroy */
+
+static int fc_bit_jump_invoke (const data_set_t *ds, /* {{{ */
+    value_list_t *vl, notification_meta_t **meta, void **user_data)
+{
+  char *chain_name;
+  fc_chain_t *chain;
+  int status;
+
+  chain_name = *user_data;
+
+  for (chain = chain_list_head; chain != NULL; chain = chain->next)
+    if (strcasecmp (chain_name, chain->name) == 0)
+      break;
+
+  if (chain == NULL)
+  {
+    ERROR ("Filter subsystem: Built-in target `jump': There is no chain "
+        "named `%s'.", chain_name);
+    return (-1);
+  }
+
+  status = fc_process_chain (ds, vl, chain);
+  if (status < 0)
+    return (status);
+  else if (status == FC_TARGET_STOP)
+    return (FC_TARGET_STOP);
+  else
+    return (FC_TARGET_CONTINUE);
+} /* }}} int fc_bit_jump_invoke */
+
+static int fc_bit_stop_invoke (const data_set_t *ds, /* {{{ */
+    value_list_t *vl, notification_meta_t **meta, void **user_data)
+{
+  return (FC_TARGET_STOP);
+} /* }}} int fc_bit_stop_invoke */
+
+static int fc_bit_return_invoke (const data_set_t *ds, /* {{{ */
+    value_list_t *vl, notification_meta_t **meta, void **user_data)
+{
+  return (FC_TARGET_RETURN);
+} /* }}} int fc_bit_return_invoke */
+
+static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */
+    void **user_data)
+{
+  int i;
+
+  char **plugin_list;
+  size_t plugin_list_len;
+
+  plugin_list = NULL;
+  plugin_list_len = 0;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+    char **temp;
+    int j;
+
+    if (strcasecmp ("Plugin", child->key) != 0)
+    {
+      ERROR ("Filter subsystem: The built-in target `write' does not "
+          "support the configuration option `%s'.",
+          child->key);
+      continue;
+    }
+
+    for (j = 0; j < child->values_num; j++)
+    {
+      if (child->values[j].type != OCONFIG_TYPE_STRING)
+      {
+        ERROR ("Filter subsystem: Built-in target `write': "
+            "The `Plugin' option accepts only string arguments.");
+        continue;
+      }
+
+      temp = (char **) realloc (plugin_list, (plugin_list_len + 2)
+          * (sizeof (*plugin_list)));
+      if (temp == NULL)
+      {
+        ERROR ("fc_bit_write_create: realloc failed.");
+        continue;
+      }
+      plugin_list = temp;
+
+      plugin_list[plugin_list_len] = fc_strdup (child->values[j].value.string);
+      if (plugin_list[plugin_list_len] == NULL)
+      {
+        ERROR ("fc_bit_write_create: fc_strdup failed.");
+        continue;
+      }
+      plugin_list_len++;
+      plugin_list[plugin_list_len] = NULL;
+    } /* for (j = 0; j < child->values_num; j++) */
+  } /* for (i = 0; i < ci->children_num; i++) */
+
+  *user_data = plugin_list;
+
+  return (0);
+} /* }}} int fc_bit_write_create */
+
+static int fc_bit_write_destroy (void **user_data) /* {{{ */
+{
+  char **plugin_list;
+  size_t i;
+
+  if ((user_data == NULL) || (*user_data == NULL))
+    return (0);
+
+  plugin_list = *user_data;
+
+  for (i = 0; plugin_list[i] != NULL; i++)
+    free (plugin_list[i]);
+  free (plugin_list);
+
+  return (0);
+} /* }}} int fc_bit_write_destroy */
+
+static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */
+    value_list_t *vl, notification_meta_t **meta, void **user_data)
+{
+  char **plugin_list;
+  int status;
+
+  plugin_list = NULL;
+  if (user_data != NULL)
+    plugin_list = *user_data;
+
+  if ((plugin_list == NULL) || (plugin_list[0] == NULL))
+  {
+    status = plugin_write (/* plugin = */ NULL, ds, vl);
+    if (status != 0)
+    {
+      INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
+          "all write plugins failed with status %i.", status);
+    }
+  }
+  else
+  {
+    size_t i;
+
+    for (i = 0; plugin_list[i] != NULL; i++)
+    {
+      status = plugin_write (plugin_list[i], ds, vl);
+      if (status != 0)
+      {
+        INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
+            "the `%s' plugin failed with status %i.", plugin_list[i], status);
+      }
+    } /* for (i = 0; plugin_list[i] != NULL; i++) */
+  }
+
+  return (FC_TARGET_CONTINUE);
+} /* }}} int fc_bit_write_invoke */
+
+static int fc_init_once (void) /* {{{ */
+{
+  static int done = 0;
+  target_proc_t tproc;
+
+  if (done != 0)
+    return (0);
+
+  memset (&tproc, 0, sizeof (tproc));
+  tproc.create  = fc_bit_jump_create;
+  tproc.destroy = fc_bit_jump_destroy;
+  tproc.invoke  = fc_bit_jump_invoke;
+  fc_register_target ("jump", tproc);
+
+  memset (&tproc, 0, sizeof (tproc));
+  tproc.create  = NULL;
+  tproc.destroy = NULL;
+  tproc.invoke  = fc_bit_stop_invoke;
+  fc_register_target ("stop", tproc);
+
+  memset (&tproc, 0, sizeof (tproc));
+  tproc.create  = NULL;
+  tproc.destroy = NULL;
+  tproc.invoke  = fc_bit_return_invoke;
+  fc_register_target ("return", tproc);
+
+  memset (&tproc, 0, sizeof (tproc));
+  tproc.create  = fc_bit_write_create;
+  tproc.destroy = fc_bit_write_destroy;
+  tproc.invoke  = fc_bit_write_invoke;
+  fc_register_target ("write", tproc);
+
+  done++;
+  return (0);
+} /* }}} int fc_init_once */
+
+/*
+ * Public functions
+ */
+/* Add a match to list of available matches. */
+int fc_register_match (const char *name, match_proc_t proc) /* {{{ */
+{
+  fc_match_t *m;
+
+  DEBUG ("fc_register_match (%s);", name);
+
+  m = (fc_match_t *) malloc (sizeof (*m));
+  if (m == NULL)
+    return (-ENOMEM);
+  memset (m, 0, sizeof (*m));
+
+  sstrncpy (m->name, name, sizeof (m->name));
+  memcpy (&m->proc, &proc, sizeof (m->proc));
+  m->next = NULL;
+
+  if (match_list_head == NULL)
+  {
+    match_list_head = m;
+  }
+  else
+  {
+    fc_match_t *ptr;
+
+    ptr = match_list_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = m;
+  }
+
+  return (0);
+} /* }}} int fc_register_match */
+
+/* Add a target to list of available targets. */
+int fc_register_target (const char *name, target_proc_t proc) /* {{{ */
+{
+  fc_target_t *t;
+
+  DEBUG ("fc_register_target (%s);", name);
+
+  t = (fc_target_t *) malloc (sizeof (*t));
+  if (t == NULL)
+    return (-ENOMEM);
+  memset (t, 0, sizeof (*t));
+
+  sstrncpy (t->name, name, sizeof (t->name));
+  memcpy (&t->proc, &proc, sizeof (t->proc));
+  t->next = NULL;
+
+  if (target_list_head == NULL)
+  {
+    target_list_head = t;
+  }
+  else
+  {
+    fc_target_t *ptr;
+
+    ptr = target_list_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = t;
+  }
+
+  return (0);
+} /* }}} int fc_register_target */
+
+/* Iterate over all rules in the chain and execute all targets for which all
+ * matches match. */
+int fc_process (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  fc_chain_t *chain;
+
+  for (chain = chain_list_head; chain != NULL; chain = chain->next)
+    if (strcasecmp ("Main", chain->name) == 0)
+      break;
+
+  if (chain != NULL)
+    return (fc_process_chain (ds, vl, chain));
+
+  return (fc_bit_write_invoke (ds, vl,
+        /* meta = */ NULL, /* user_data = */ NULL));
+} /* }}} int fc_process */
+
+int fc_configure (const oconfig_item_t *ci) /* {{{ */
+{
+  fc_init_once ();
+
+  if (ci == NULL)
+    return (-EINVAL);
+
+  if (strcasecmp ("Chain", ci->key) == 0)
+    return (fc_config_add_chain (ci));
+
+  WARNING ("Filter subsystem: Unknown top level config option `%s'.",
+      ci->key);
+
+  return (-1);
+} /* }}} int fc_configure */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/filter_chain.h b/src/filter_chain.h
new file mode 100644 (file)
index 0000000..2fd78d9
--- /dev/null
@@ -0,0 +1,93 @@
+/**
+ * collectd - src/filter_chain.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 FILTER_CHAIN_H
+#define FILTER_CHAIN_H 1
+
+#include "collectd.h"
+#include "plugin.h"
+
+#define FC_MATCH_NO_MATCH  0
+#define FC_MATCH_MATCHES   1
+
+#define FC_TARGET_CONTINUE 0
+#define FC_TARGET_STOP     1
+#define FC_TARGET_RETURN   2
+
+/*
+ * Match functions
+ */
+struct match_proc_s
+{
+  int (*create) (const oconfig_item_t *ci, void **user_data);
+  int (*destroy) (void **user_data);
+  int (*match) (const data_set_t *ds, const value_list_t *vl,
+      notification_meta_t **meta, void **user_data);
+};
+typedef struct match_proc_s match_proc_t;
+
+int fc_register_match (const char *name, match_proc_t proc);
+
+/*
+ * Target functions
+ */
+struct target_proc_s
+{
+  int (*create) (const oconfig_item_t *ci, void **user_data);
+  int (*destroy) (void **user_data);
+  int (*invoke) (const data_set_t *ds, value_list_t *vl,
+      notification_meta_t **meta, void **user_data);
+};
+typedef struct target_proc_s target_proc_t;
+
+int fc_register_target (const char *name, target_proc_t proc);
+
+/*
+ * TODO: Chain management
+ */
+#if 0
+int fc_chain_add (const char *chain_name,
+    const char *target_name, int target_argc, char **target_argv);
+int fc_chain_delete (const char *chain_name);
+#endif
+
+/*
+ * TODO: Rule management
+ */
+#if 0
+int fc_rule_add (const char *chain_name, int position,
+    int match_num, const char **match_name, int *match_argc, char ***match_argv,
+    const char *target_name, int target_argc, char **target_argv);
+int fc_rule_delete (const char *chain_name, int position);
+#endif
+
+/*
+ * Processing function
+ */
+int fc_process (const data_set_t *ds, value_list_t *vl);
+
+/* 
+ * Shortcut for global configuration
+ */
+int fc_configure (const oconfig_item_t *ci);
+
+#endif /* FILTER_CHAIN_H */
+/* vim: set sw=2 sts=2 et : */
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 : */
index 806a336dfde09c814c9ab9f204e5770758251893..7512c4ee3c21c7e07fc591f3c4bdf19f057746ac 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;
diff --git a/src/libcollectdclient/Makefile.am b/src/libcollectdclient/Makefile.am
new file mode 100644 (file)
index 0000000..ca50560
--- /dev/null
@@ -0,0 +1,11 @@
+AUTOMAKE_OPTIONS = foreign no-dependencies
+
+if COMPILER_IS_GCC
+AM_CFLAGS = -Wall -Werror
+endif
+
+pkginclude_HEADERS = client.h
+lib_LTLIBRARIES = libcollectdclient.la
+
+libcollectdclient_la_SOURCES = client.c
+libcollectdclient_la_LDFLAGS = -version-info 0:0:0
diff --git a/src/libcollectdclient/client.c b/src/libcollectdclient/client.c
new file mode 100644 (file)
index 0000000..b2f2daf
--- /dev/null
@@ -0,0 +1,1011 @@
+/**
+ * libcollectdclient - src/libcollectdclient/client.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>
+ **/
+
+/* Set to C99 and POSIX code */
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+#ifndef _POSIX_SOURCE
+# define _POSIX_SOURCE
+#endif
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200112L
+#endif
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+/* Disable non-standard extensions */
+#ifdef _BSD_SOURCE
+# undef _BSD_SOURCE
+#endif
+#ifdef _SVID_SOURCE
+# undef _SVID_SOURCE
+#endif
+#ifdef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+
+#if !defined(__GNUC__) || !__GNUC__
+# define __attribute__(x) /**/
+#endif
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <math.h>
+#include <netdb.h>
+
+#include "client.h"
+
+/* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11
+ * to no longer define it. We'll use the old, RFC 2553 value here. */
+#ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+#endif
+
+/* Secure/static macros. They work like `strcpy' and `strcat', but assure null
+ * termination. They work for static buffers only, because they use `sizeof'.
+ * The `SSTRCATF' combines the functionality of `snprintf' and `strcat' which
+ * is very useful to add formatted stuff to the end of a buffer. */
+#define SSTRCPY(d,s) do { \
+    strncpy ((d), (s), sizeof (d)); \
+    (d)[sizeof (d) - 1] = 0; \
+  } while (0)
+
+#define SSTRCAT(d,s) do { \
+    strncat ((d), (s), sizeof (d)); \
+    (d)[sizeof (d) - 1] = 0; \
+  } while (0)
+
+#define SSTRCATF(d, ...) do { \
+    char _b[sizeof (d)]; \
+    snprintf (_b, sizeof (_b), __VA_ARGS__); \
+    _b[sizeof (_b) - 1] = 0; \
+    SSTRCAT ((d), _b); \
+  } while (0)
+    
+
+#define LCC_SET_ERRSTR(c, ...) do { \
+  snprintf ((c)->errbuf, sizeof ((c)->errbuf), __VA_ARGS__); \
+  (c)->errbuf[sizeof ((c)->errbuf) - 1] = 0; \
+} while (0)
+
+#if COLLECT_DEBUG
+# define LCC_DEBUG(...) printf (__VA_ARGS__)
+#else
+# define LCC_DEBUG(...) /**/
+#endif
+
+/*
+ * Types
+ */
+struct lcc_connection_s
+{
+  FILE *fh;
+  char errbuf[1024];
+};
+
+struct lcc_response_s
+{
+  int status;
+  char message[1024];
+  char **lines;
+  size_t lines_num;
+};
+typedef struct lcc_response_s lcc_response_t;
+
+/*
+ * Private functions
+ */
+static int lcc_set_errno (lcc_connection_t *c, int err) /* {{{ */
+{
+  if (c == NULL)
+    return (-1);
+
+  strerror_r (err, c->errbuf, sizeof (c->errbuf));
+  c->errbuf[sizeof (c->errbuf) - 1] = 0;
+
+  return (0);
+} /* }}} int lcc_set_errno */
+
+/* lcc_strdup: Since `strdup' is an XSI extension, we provide our own version
+ * here. */
+__attribute__((malloc, nonnull (1)))
+static char *lcc_strdup (const char *str) /* {{{ */
+{
+  size_t strsize;
+  char *ret;
+
+  strsize = strlen (str) + 1;
+  ret = (char *) malloc (strsize);
+  if (ret != NULL)
+    memcpy (ret, str, strsize);
+  return (ret);
+} /* }}} char *lcc_strdup */
+
+__attribute__((nonnull (1, 2)))
+static char *lcc_strescape (char *dest, const char *src, size_t dest_size) /* {{{ */
+{
+  size_t dest_pos;
+  size_t src_pos;
+
+  dest_pos = 0;
+  src_pos = 0;
+
+  assert (dest_size >= 3);
+
+  dest[dest_pos] = '"';
+  dest_pos++;
+
+  while (42)
+  {
+    if ((dest_pos == (dest_size - 2))
+        || (src[src_pos] == 0))
+      break;
+
+    if ((src[src_pos] == '"') || (src[src_pos] == '\\'))
+    {
+      /* Check if there is enough space for both characters.. */
+      if (dest_pos == (dest_size - 3))
+        break;
+
+      dest[dest_pos] = '\\';
+      dest_pos++;
+    }
+
+    dest[dest_pos] = src[src_pos];
+    dest_pos++;
+    src_pos++;
+  }
+
+  assert (dest_pos <= (dest_size - 2));
+
+  dest[dest_pos] = '"';
+  dest_pos++;
+
+  dest[dest_pos] = 0;
+  dest_pos++;
+  src_pos++;
+
+  return (dest);
+} /* }}} char *lcc_strescape */
+
+/* lcc_chomp: Removes all control-characters at the end of a string. */
+static void lcc_chomp (char *str) /* {{{ */
+{
+  size_t str_len;
+
+  str_len = strlen (str);
+  while (str_len > 0)
+  {
+    if (str[str_len - 1] >= 32)
+      break;
+    str[str_len - 1] = 0;
+    str_len--;
+  }
+} /* }}} void lcc_chomp */
+
+static void lcc_response_free (lcc_response_t *res) /* {{{ */
+{
+  size_t i;
+
+  if (res == NULL)
+    return;
+
+  for (i = 0; i < res->lines_num; i++)
+    free (res->lines[i]);
+  free (res->lines);
+  res->lines = NULL;
+} /* }}} void lcc_response_free */
+
+static int lcc_send (lcc_connection_t *c, const char *command) /* {{{ */
+{
+  int status;
+
+  LCC_DEBUG ("send:    --> %s\n", command);
+
+  status = fprintf (c->fh, "%s\r\n", command);
+  if (status < 0)
+  {
+    lcc_set_errno (c, errno);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int lcc_send */
+
+static int lcc_receive (lcc_connection_t *c, /* {{{ */
+    lcc_response_t *ret_res)
+{
+  lcc_response_t res;
+  char *ptr;
+  char buffer[4096];
+  size_t i;
+
+  memset (&res, 0, sizeof (res));
+
+  /* Read the first line, containing the status and a message */
+  ptr = fgets (buffer, sizeof (buffer), c->fh);
+  if (ptr == NULL)
+  {
+    lcc_set_errno (c, errno);
+    return (-1);
+  }
+  lcc_chomp (buffer);
+  LCC_DEBUG ("receive: <-- %s\n", buffer);
+
+  /* Convert the leading status to an integer and make `ptr' to point to the
+   * beginning of the message. */
+  ptr = NULL;
+  errno = 0;
+  res.status = strtol (buffer, &ptr, 0);
+  if ((errno != 0) || (ptr == &buffer[0]))
+  {
+    lcc_set_errno (c, errno);
+    return (-1);
+  }
+
+  /* Skip white spaces after the status number */
+  while ((*ptr == ' ') || (*ptr == '\t'))
+    ptr++;
+
+  /* Now copy the message. */
+  strncpy (res.message, ptr, sizeof (res.message));
+  res.message[sizeof (res.message) - 1] = 0;
+
+  /* Error or no lines follow: We're done. */
+  if (res.status <= 0)
+  {
+    memcpy (ret_res, &res, sizeof (res));
+    return (0);
+  }
+
+  /* Allocate space for the char-pointers */
+  res.lines_num = (size_t) res.status;
+  res.status = 0;
+  res.lines = (char **) malloc (res.lines_num * sizeof (char *));
+  if (res.lines == NULL)
+  {
+    lcc_set_errno (c, ENOMEM);
+    return (-1);
+  }
+
+  /* Now receive all the lines */
+  for (i = 0; i < res.lines_num; i++)
+  {
+    ptr = fgets (buffer, sizeof (buffer), c->fh);
+    if (ptr == NULL)
+    {
+      lcc_set_errno (c, errno);
+      break;
+    }
+    lcc_chomp (buffer);
+    LCC_DEBUG ("receive: <-- %s\n", buffer);
+
+    res.lines[i] = lcc_strdup (buffer);
+    if (res.lines[i] == NULL)
+    {
+      lcc_set_errno (c, ENOMEM);
+      break;
+    }
+  }
+
+  /* Check if the for-loop exited with an error. */
+  if (i < res.lines_num)
+  {
+    while (i > 0)
+    {
+      i--;
+      free (res.lines[i]);
+    }
+    free (res.lines);
+    return (-1);
+  }
+
+  memcpy (ret_res, &res, sizeof (res));
+  return (0);
+} /* }}} int lcc_receive */
+
+static int lcc_sendreceive (lcc_connection_t *c, /* {{{ */
+    const char *command, lcc_response_t *ret_res)
+{
+  lcc_response_t res;
+  int status;
+
+  status = lcc_send (c, command);
+  if (status != 0)
+    return (status);
+
+  memset (&res, 0, sizeof (res));
+  status = lcc_receive (c, &res);
+  if (status == 0)
+    memcpy (ret_res, &res, sizeof (*ret_res));
+
+  return (status);
+} /* }}} int lcc_sendreceive */
+
+static int lcc_open_unixsocket (lcc_connection_t *c, const char *path) /* {{{ */
+{
+  struct sockaddr_un sa;
+  int fd;
+  int status;
+
+  assert (c != NULL);
+  assert (c->fh == NULL);
+  assert (path != NULL);
+
+  /* Don't use PF_UNIX here, because it's broken on Mac OS X (10.4, possibly
+   * others). */
+  fd = socket (AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
+  if (fd < 0)
+  {
+    lcc_set_errno (c, errno);
+    return (-1);
+  }
+
+  memset (&sa, 0, sizeof (sa));
+  sa.sun_family = AF_UNIX;
+  strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
+
+  status = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
+  if (status != 0)
+  {
+    lcc_set_errno (c, errno);
+    close (fd);
+    return (-1);
+  }
+
+  c->fh = fdopen (fd, "r+");
+  if (c->fh == NULL)
+  {
+    lcc_set_errno (c, errno);
+    close (fd);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int lcc_open_unixsocket */
+
+static int lcc_open_netsocket (lcc_connection_t *c, /* {{{ */
+    const char *addr_orig)
+{
+  struct addrinfo ai_hints;
+  struct addrinfo *ai_res;
+  struct addrinfo *ai_ptr;
+  char addr_copy[NI_MAXHOST];
+  char *addr;
+  char *port;
+  int fd;
+  int status;
+
+  assert (c != NULL);
+  assert (c->fh == NULL);
+  assert (addr_orig != NULL);
+
+  strncpy(addr_copy, addr_orig, sizeof(addr_copy));
+  addr_copy[sizeof(addr_copy) - 1] = '\0';
+  addr = addr_copy;
+
+  memset (&ai_hints, 0, sizeof (ai_hints));
+  ai_hints.ai_flags = 0;
+#ifdef AI_ADDRCONFIG
+  ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+  ai_hints.ai_family = AF_UNSPEC;
+  ai_hints.ai_socktype = SOCK_STREAM;
+
+  port = NULL;
+  if (*addr == '[') /* IPv6+port format */
+  {
+    /* `addr' is something like "[2001:780:104:2:211:24ff:feab:26f8]:12345" */
+    addr++;
+
+    port = strchr (addr, ']');
+    if (port == NULL)
+    {
+      LCC_SET_ERRSTR (c, "malformed address: %s", addr_orig);
+      return (-1);
+    }
+    *port = 0;
+    port++;
+
+    if (*port == ':')
+      port++;
+    else if (*port == 0)
+      port = NULL;
+    else
+    {
+      LCC_SET_ERRSTR (c, "garbage after address: %s", port);
+      return (-1);
+    }
+  } /* if (*addr = ']') */
+  else if (strchr (addr, '.') != NULL) /* Hostname or IPv4 */
+  {
+    port = strrchr (addr, ':');
+    if (port != NULL)
+    {
+      *port = 0;
+      port++;
+    }
+  }
+
+  ai_res = NULL;
+  status = getaddrinfo (addr,
+                        port == NULL ? LCC_DEFAULT_PORT : port,
+                        &ai_hints, &ai_res);
+  if (status != 0)
+  {
+    LCC_SET_ERRSTR (c, "getaddrinfo: %s", gai_strerror (status));
+    return (-1);
+  }
+
+  for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (fd < 0)
+    {
+      status = errno;
+      fd = -1;
+      continue;
+    }
+
+    status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    if (status != 0)
+    {
+      status = errno;
+      close (fd);
+      fd = -1;
+      continue;
+    }
+
+    c->fh = fdopen (fd, "r+");
+    if (c->fh == NULL)
+    {
+      status = errno;
+      close (fd);
+      fd = -1;
+      continue;
+    }
+
+    assert (status == 0);
+    break;
+  } /* for (ai_ptr) */
+
+  if (status != 0)
+  {
+    lcc_set_errno (c, status);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int lcc_open_netsocket */
+
+static int lcc_open_socket (lcc_connection_t *c, const char *addr) /* {{{ */
+{
+  int status = 0;
+
+  if (addr == NULL)
+    return (-1);
+
+  assert (c != NULL);
+  assert (c->fh == NULL);
+  assert (addr != NULL);
+
+  if (strncmp ("unix:", addr, strlen ("unix:")) == 0)
+    status = lcc_open_unixsocket (c, addr + strlen ("unix:"));
+  else if (addr[0] == '/')
+    status = lcc_open_unixsocket (c, addr);
+  else
+    status = lcc_open_netsocket (c, addr);
+
+  return (status);
+} /* }}} int lcc_open_socket */
+
+/*
+ * Public functions
+ */
+int lcc_connect (const char *address, lcc_connection_t **ret_con) /* {{{ */
+{
+  lcc_connection_t *c;
+
+  if (address == NULL)
+    return (-1);
+
+  if (ret_con == NULL)
+    return (-1);
+
+  c = (lcc_connection_t *) malloc (sizeof (*c));
+  if (c == NULL)
+    return (-1);
+  memset (c, 0, sizeof (*c));
+
+  *ret_con = c;
+  return (lcc_open_socket (c, address));
+} /* }}} int lcc_connect */
+
+int lcc_disconnect (lcc_connection_t *c) /* {{{ */
+{
+  if (c == NULL)
+    return (-1);
+
+  if (c->fh != NULL)
+  {
+    fclose (c->fh);
+    c->fh = NULL;
+  }
+
+  free (c);
+  return (0);
+} /* }}} int lcc_disconnect */
+
+int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */
+    size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names)
+{
+  char ident_str[6 * LCC_NAME_LEN];
+  char ident_esc[12 * LCC_NAME_LEN];
+  char command[14 * LCC_NAME_LEN];
+
+  lcc_response_t res;
+  size_t   values_num;
+  gauge_t *values = NULL;
+  char   **values_names = NULL;
+
+  size_t i;
+  int status;
+
+  if (c == NULL)
+    return (-1);
+
+  if (ident == NULL)
+  {
+    lcc_set_errno (c, EINVAL);
+    return (-1);
+  }
+
+  /* Build a commend with an escaped version of the identifier string. */
+  status = lcc_identifier_to_string (c, ident_str, sizeof (ident_str), ident);
+  if (status != 0)
+    return (status);
+
+  snprintf (command, sizeof (command), "GETVAL %s",
+      lcc_strescape (ident_esc, ident_str, sizeof (ident_esc)));
+  command[sizeof (command) - 1] = 0;
+
+  /* Send talk to the daemon.. */
+  status = lcc_sendreceive (c, command, &res);
+  if (status != 0)
+    return (status);
+
+  if (res.status != 0)
+  {
+    LCC_SET_ERRSTR (c, "Server error: %s", res.message);
+    lcc_response_free (&res);
+    return (-1);
+  }
+
+  values_num = res.lines_num;
+
+#define BAIL_OUT(e) do { \
+  lcc_set_errno (c, (e)); \
+  free (values); \
+  if (values_names != NULL) { \
+    for (i = 0; i < values_num; i++) { \
+      free (values_names[i]); \
+    } \
+  } \
+  free (values_names); \
+  lcc_response_free (&res); \
+  return (-1); \
+} while (0)
+
+  /* If neither the values nor the names are requested, return here.. */
+  if ((ret_values == NULL) && (ret_values_names == NULL))
+  {
+    if (ret_values_num != NULL)
+      *ret_values_num = values_num;
+    lcc_response_free (&res);
+    return (0);
+  }
+
+  /* Allocate space for the values */
+  if (ret_values != NULL)
+  {
+    values = (gauge_t *) malloc (values_num * sizeof (*values));
+    if (values == NULL)
+      BAIL_OUT (ENOMEM);
+  }
+
+  if (ret_values_names != NULL)
+  {
+    values_names = (char **) calloc (values_num, sizeof (*values_names));
+    if (values_names == NULL)
+      BAIL_OUT (ENOMEM);
+  }
+
+  for (i = 0; i < res.lines_num; i++)
+  {
+    char *key;
+    char *value;
+    char *endptr;
+
+    key = res.lines[i];
+    value = strchr (key, '=');
+    if (value == NULL)
+      BAIL_OUT (EPROTO);
+
+    *value = 0;
+    value++;
+
+    if (values != NULL)
+    {
+      endptr = NULL;
+      errno = 0;
+      values[i] = strtod (value, &endptr);
+
+      if ((endptr == value) || (errno != 0))
+        BAIL_OUT (errno);
+    }
+
+    if (values_names != NULL)
+    {
+      values_names[i] = lcc_strdup (key);
+      if (values_names[i] == NULL)
+        BAIL_OUT (ENOMEM);
+    }
+  } /* for (i = 0; i < res.lines_num; i++) */
+
+  if (ret_values_num != NULL)
+    *ret_values_num = values_num;
+  if (ret_values != NULL)
+    *ret_values = values;
+  if (ret_values_names != NULL)
+    *ret_values_names = values_names;
+
+  return (0);
+} /* }}} int lcc_getval */
+
+int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */
+{
+  char ident_str[6 * LCC_NAME_LEN];
+  char ident_esc[12 * LCC_NAME_LEN];
+  char command[1024] = "";
+  lcc_response_t res;
+  int status;
+  size_t i;
+
+  if ((c == NULL) || (vl == NULL) || (vl->values_len < 1)
+      || (vl->values == NULL) || (vl->values_types == NULL))
+  {
+    lcc_set_errno (c, EINVAL);
+    return (-1);
+  }
+
+  status = lcc_identifier_to_string (c, ident_str, sizeof (ident_str),
+      &vl->identifier);
+  if (status != 0)
+    return (status);
+
+  SSTRCATF (command, "PUTVAL %s",
+      lcc_strescape (ident_esc, ident_str, sizeof (ident_esc)));
+
+  if (vl->interval > 0)
+    SSTRCATF (command, " interval=%i", vl->interval);
+
+  if (vl->time > 0)
+    SSTRCATF (command, "%u", (unsigned int) vl->time);
+  else
+    SSTRCAT (command, "N");
+
+  for (i = 0; i < vl->values_len; i++)
+  {
+    if (vl->values_types[i] == LCC_TYPE_COUNTER)
+      SSTRCATF (command, ":%"PRIu64, vl->values[i].counter);
+    else if (vl->values_types[i] == LCC_TYPE_GAUGE)
+    {
+      if (isnan (vl->values[i].gauge))
+        SSTRCPY (command, ":U");
+      else
+        SSTRCATF (command, ":%g", vl->values[i].gauge);
+    }
+  } /* for (i = 0; i < vl->values_len; i++) */
+
+  status = lcc_sendreceive (c, command, &res);
+  if (status != 0)
+    return (status);
+
+  if (res.status != 0)
+  {
+    LCC_SET_ERRSTR (c, "Server error: %s", res.message);
+    lcc_response_free (&res);
+    return (-1);
+  }
+
+  lcc_response_free (&res);
+  return (0);
+} /* }}} int lcc_putval */
+
+int lcc_flush (lcc_connection_t *c, const char *plugin, /* {{{ */
+    lcc_identifier_t *ident, int timeout)
+{
+  char command[1024];
+  lcc_response_t res;
+  int status;
+
+  if (c == NULL)
+  {
+    lcc_set_errno (c, EINVAL);
+    return (-1);
+  }
+
+  SSTRCPY (command, "FLUSH");
+
+  if (timeout > 0)
+    SSTRCATF (command, " timeout=%i", timeout);
+
+  if (plugin != NULL)
+  {
+    char buffer[2 * LCC_NAME_LEN];
+    SSTRCATF (command, " plugin=%s",
+        lcc_strescape (buffer, plugin, sizeof (buffer)));
+  }
+
+  if (ident != NULL)
+  {
+    char ident_str[6 * LCC_NAME_LEN];
+    char ident_esc[12 * LCC_NAME_LEN];
+
+    status = lcc_identifier_to_string (c, ident_str, sizeof (ident_str), ident);
+    if (status != 0)
+      return (status);
+
+    SSTRCATF (command, " identifier=%s",
+        lcc_strescape (ident_esc, ident_str, sizeof (ident_esc)));
+  }
+
+  status = lcc_sendreceive (c, command, &res);
+  if (status != 0)
+    return (status);
+
+  if (res.status != 0)
+  {
+    LCC_SET_ERRSTR (c, "Server error: %s", res.message);
+    lcc_response_free (&res);
+    return (-1);
+  }
+
+  lcc_response_free (&res);
+  return (0);
+} /* }}} int lcc_flush */
+
+/* TODO: Implement lcc_putnotif */
+
+int lcc_listval (lcc_connection_t *c, /* {{{ */
+    lcc_identifier_t **ret_ident, size_t *ret_ident_num)
+{
+  lcc_response_t res;
+  size_t i;
+  int status;
+
+  lcc_identifier_t *ident;
+  size_t ident_num;
+
+  if (c == NULL)
+    return (-1);
+
+  if ((ret_ident == NULL) || (ret_ident_num == NULL))
+  {
+    lcc_set_errno (c, EINVAL);
+    return (-1);
+  }
+
+  status = lcc_sendreceive (c, "LISTVAL", &res);
+  if (status != 0)
+    return (status);
+
+  if (res.status != 0)
+  {
+    LCC_SET_ERRSTR (c, "Server error: %s", res.message);
+    lcc_response_free (&res);
+    return (-1);
+  }
+
+  ident_num = res.lines_num;
+  ident = (lcc_identifier_t *) malloc (ident_num * sizeof (*ident));
+  if (ident == NULL)
+  {
+    lcc_response_free (&res);
+    lcc_set_errno (c, ENOMEM);
+    return (-1);
+  }
+
+  for (i = 0; i < res.lines_num; i++)
+  {
+    char *time_str;
+    char *ident_str;
+
+    /* First field is the time. */
+    time_str = res.lines[i];
+
+    /* Set `ident_str' to the beginning of the second field. */
+    ident_str = time_str;
+    while ((*ident_str != ' ') && (*ident_str != '\t') && (*ident_str != 0))
+      ident_str++;
+    while ((*ident_str == ' ') || (*ident_str == '\t'))
+    {
+      *ident_str = 0;
+      ident_str++;
+    }
+
+    if (*ident_str == 0)
+    {
+      lcc_set_errno (c, EPROTO);
+      status = -1;
+      break;
+    }
+
+    status = lcc_string_to_identifier (c, ident + i, ident_str);
+    if (status != 0)
+      break;
+  }
+
+  lcc_response_free (&res);
+
+  if (status != 0)
+  {
+    free (ident);
+    return (-1);
+  }
+
+  *ret_ident = ident;
+  *ret_ident_num = ident_num;
+
+  return (0);
+} /* }}} int lcc_listval */
+
+const char *lcc_strerror (lcc_connection_t *c) /* {{{ */
+{
+  if (c == NULL)
+    return ("Invalid object");
+  return (c->errbuf);
+} /* }}} const char *lcc_strerror */
+
+int lcc_identifier_to_string (lcc_connection_t *c, /* {{{ */
+    char *string, size_t string_size, const lcc_identifier_t *ident)
+{
+  if ((string == NULL) || (string_size < 6) || (ident == NULL))
+  {
+    lcc_set_errno (c, EINVAL);
+    return (-1);
+  }
+
+  if (ident->plugin_instance[0] == 0)
+  {
+    if (ident->type_instance[0] == 0)
+      snprintf (string, string_size, "%s/%s/%s",
+          ident->host,
+          ident->plugin,
+          ident->type);
+    else
+      snprintf (string, string_size, "%s/%s/%s-%s",
+          ident->host,
+          ident->plugin,
+          ident->type,
+          ident->type_instance);
+  }
+  else
+  {
+    if (ident->type_instance[0] == 0)
+      snprintf (string, string_size, "%s/%s-%s/%s",
+          ident->host,
+          ident->plugin,
+          ident->plugin_instance,
+          ident->type);
+    else
+      snprintf (string, string_size, "%s/%s-%s/%s-%s",
+          ident->host,
+          ident->plugin,
+          ident->plugin_instance,
+          ident->type,
+          ident->type_instance);
+  }
+
+  string[string_size - 1] = 0;
+  return (0);
+} /* }}} int lcc_identifier_to_string */
+
+int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */
+    lcc_identifier_t *ident, const char *string)
+{
+  char *string_copy;
+  char *host;
+  char *plugin;
+  char *plugin_instance;
+  char *type;
+  char *type_instance;
+
+  string_copy = lcc_strdup (string);
+  if (string_copy == NULL)
+  {
+    lcc_set_errno (c, ENOMEM);
+    return (-1);
+  }
+
+  host = string_copy;
+  plugin = strchr (host, '/');
+  if (plugin == NULL)
+  {
+    LCC_SET_ERRSTR (c, "Malformed identifier string: %s", string);
+    free (string_copy);
+    return (-1);
+  }
+  *plugin = 0;
+  plugin++;
+
+  type = strchr (plugin, '/');
+  if (type == NULL)
+  {
+    LCC_SET_ERRSTR (c, "Malformed identifier string: %s", string);
+    free (string_copy);
+    return (-1);
+  }
+  *type = 0;
+  type++;
+
+  plugin_instance = strchr (plugin, '-');
+  if (plugin_instance != NULL)
+  {
+    *plugin_instance = 0;
+    plugin_instance++;
+  }
+
+  type_instance = strchr (type, '-');
+  if (type_instance != NULL)
+  {
+    *type_instance = 0;
+    type_instance++;
+  }
+
+  memset (ident, 0, sizeof (*ident));
+
+  SSTRCPY (ident->host, host);
+  SSTRCPY (ident->plugin, plugin);
+  if (plugin_instance != NULL)
+    SSTRCPY (ident->plugin_instance, plugin_instance);
+  SSTRCPY (ident->type, type);
+  if (type_instance != NULL)
+    SSTRCPY (ident->type_instance, type_instance);
+
+  free (string_copy);
+  return (0);
+} /* }}} int lcc_string_to_identifier */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/libcollectdclient/client.h b/src/libcollectdclient/client.h
new file mode 100644 (file)
index 0000000..a0ab94c
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * libcollectdclient - src/libcollectdclient/client.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 LIBCOLLECTD_COLLECTDCLIENT_H
+#define LIBCOLLECTD_COLLECTDCLIENT_H 1
+
+/*
+ * Includes (for data types)
+ */
+#include <stdint.h>
+#include <inttypes.h>
+#include <time.h>
+
+/*
+ * Defines
+ */
+#define LCC_VERSION 0
+#define LCC_NAME_LEN 64
+#define LCC_DEFAULT_PORT "25826"
+
+/*
+ * Types
+ */
+#define LCC_TYPE_COUNTER 0
+#define LCC_TYPE_GAUGE   1
+
+typedef uint64_t counter_t;
+typedef double gauge_t;
+
+union value_u
+{
+  counter_t counter;
+  gauge_t   gauge;
+};
+typedef union value_u value_t;
+
+struct lcc_identifier_s
+{
+  char host[LCC_NAME_LEN];
+  char plugin[LCC_NAME_LEN];
+  char plugin_instance[LCC_NAME_LEN];
+  char type[LCC_NAME_LEN];
+  char type_instance[LCC_NAME_LEN];
+};
+typedef struct lcc_identifier_s lcc_identifier_t;
+#define LCC_IDENTIFIER_INIT { "localhost", "", "", "", "" }
+
+struct lcc_value_list_s
+{
+  value_t *values;
+  int     *values_types;
+  size_t   values_len;
+  time_t   time;
+  int      interval;
+  lcc_identifier_t identifier;
+};
+typedef struct lcc_value_list_s lcc_value_list_t;
+#define LCC_VALUE_LIST_INIT { NULL, 0, 0, 0, LCC_IDENTIFIER_INIT }
+
+struct lcc_connection_s;
+typedef struct lcc_connection_s lcc_connection_t;
+
+/*
+ * Functions
+ */
+int lcc_connect (const char *address, lcc_connection_t **ret_con);
+int lcc_disconnect (lcc_connection_t *c);
+#define LCC_DESTROY(c) do { lcc_disconnect (c); (c) = NULL; } while (0)
+
+int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident,
+    size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names);
+
+int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl);
+
+int lcc_flush (lcc_connection_t *c, const char *plugin,
+    lcc_identifier_t *ident, int timeout);
+
+int lcc_listval (lcc_connection_t *c,
+    lcc_identifier_t **ret_ident, size_t *ret_ident_num);
+
+/* TODO: putnotif */
+
+const char *lcc_strerror (lcc_connection_t *c);
+
+int lcc_identifier_to_string (lcc_connection_t *c,
+    char *string, size_t string_size, const lcc_identifier_t *ident);
+int lcc_string_to_identifier (lcc_connection_t *c,
+    lcc_identifier_t *ident, const char *string);
+
+/* vim: set sw=2 sts=2 et : */
+#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */
index ffa98878e5784db99991455ea9be27b4b67542d5..a8ed93cce01e2f7280415a6744a90f31d9ca9ad1 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 103267b7007e22edff845732ab0d292ed2688ce3..9253e11fa32bd5d569e88930f5e1f3a4ba3dba5d 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 f8e88b2818c13be8d219d132c29db5f9c11166ab..f7a6640fffe09b398a2de9a54d21cc9f250b3e33 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;
@@ -281,12 +289,308 @@ 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 d9f7423c69d4ffef49b34da69d59f9d03251d8ea..3fc25b6fb01608117f44ebc88e772f1b1aef43fc 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/match_regex.c b/src/match_regex.c
new file mode 100644 (file)
index 0000000..9233d80
--- /dev/null
@@ -0,0 +1,298 @@
+/**
+ * collectd - src/match_regex.c
+ * Copyright (C) 2008  Sebastian Harl
+ * Copyright (C) 2008  Florian 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:
+ *   Sebastian Harl <sh at tokkee.org>
+ *   Florian Forster <octo at verplant.org>
+ **/
+
+/*
+ * This module allows to filter and rewrite value lists based on
+ * Perl-compatible regular expressions.
+ */
+
+#include "collectd.h"
+#include "filter_chain.h"
+
+#include <sys/types.h>
+#include <regex.h>
+
+#define log_err(...) ERROR ("`regex' match: " __VA_ARGS__)
+#define log_warn(...) WARNING ("`regex' match: " __VA_ARGS__)
+
+/*
+ * private data types
+ */
+
+struct mr_regex_s;
+typedef struct mr_regex_s mr_regex_t;
+struct mr_regex_s
+{
+       regex_t re;
+       char *re_str;
+
+       mr_regex_t *next;
+};
+
+struct mr_match_s;
+typedef struct mr_match_s mr_match_t;
+struct mr_match_s
+{
+       mr_regex_t *host;
+       mr_regex_t *plugin;
+       mr_regex_t *plugin_instance;
+       mr_regex_t *type;
+       mr_regex_t *type_instance;
+};
+
+/*
+ * internal helper functions
+ */
+static void mr_free_regex (mr_regex_t *r) /* {{{ */
+{
+       if (r == NULL)
+               return;
+
+       regfree (&r->re);
+       memset (&r->re, 0, sizeof (r->re));
+       free (r->re_str);
+
+       if (r->next != NULL)
+               mr_free_regex (r->next);
+} /* }}} void mr_free_regex */
+
+static void mr_free_match (mr_match_t *m) /* {{{ */
+{
+       if (m == NULL)
+               return;
+
+       mr_free_regex (m->host);
+       mr_free_regex (m->plugin);
+       mr_free_regex (m->plugin_instance);
+       mr_free_regex (m->type);
+       mr_free_regex (m->type_instance);
+
+       free (m);
+} /* }}} void mr_free_match */
+
+static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
+               const char *string)
+{
+       mr_regex_t *re;
+
+       if (re_head == NULL)
+               return (FC_MATCH_MATCHES);
+
+       for (re = re_head; re != NULL; re = re->next)
+       {
+               int status;
+
+               status = regexec (&re->re, string,
+                               /* nmatch = */ 0, /* pmatch = */ NULL,
+                               /* eflags = */ 0);
+               if (status == 0)
+               {
+                       DEBUG ("regex match: Regular expression `%s' matches `%s'.",
+                                       re->re_str, string);
+               }
+               else
+               {
+                       DEBUG ("regex match: Regular expression `%s' does not match `%s'.",
+                                       re->re_str, string);
+                       return (FC_MATCH_NO_MATCH);
+               }
+
+       }
+
+       return (FC_MATCH_MATCHES);
+} /* }}} int mr_match_regexen */
+
+static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
+               oconfig_item_t *ci)
+{
+       mr_regex_t *re;
+       int status;
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               log_warn ("`%s' needs exactly one string argument.", ci->key);
+               return (-1);
+       }
+
+       re = (mr_regex_t *) malloc (sizeof (*re));
+       if (re == NULL)
+       {
+               log_err ("mr_config_add_regex: malloc failed.");
+               return (-1);
+       }
+       memset (re, 0, sizeof (*re));
+       re->next = NULL;
+
+       re->re_str = strdup (ci->values[0].value.string);
+       if (re->re_str == NULL)
+       {
+               free (re);
+               log_err ("mr_config_add_regex: strdup failed.");
+               return (-1);
+       }
+
+       status = regcomp (&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
+       if (status != 0)
+       {
+               char errmsg[1024];
+               regerror (status, &re->re, errmsg, sizeof (errmsg));
+               errmsg[sizeof (errmsg) - 1] = 0;
+               log_err ("Compiling regex `%s' for `%s' failed: %s.", 
+                               re->re_str, ci->key, errmsg);
+               free (re->re_str);
+               free (re);
+               return (-1);
+       }
+
+       if (*re_head == NULL)
+       {
+               *re_head = re;
+       }
+       else
+       {
+               mr_regex_t *ptr;
+
+               ptr = *re_head;
+               while (ptr->next != NULL)
+                       ptr = ptr->next;
+
+               ptr->next = re;
+       }
+
+       return (0);
+} /* }}} int mr_config_add_regex */
+
+static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+       mr_match_t *m;
+       int status;
+       int i;
+
+       m = (mr_match_t *) malloc (sizeof (*m));
+       if (m == NULL)
+       {
+               log_err ("mr_create: malloc failed.");
+               return (-ENOMEM);
+       }
+       memset (m, 0, sizeof (*m));
+
+       status = 0;
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if ((strcasecmp ("Host", child->key) == 0)
+                               || (strcasecmp ("Hostname", child->key) == 0))
+                       status = mr_config_add_regex (&m->host, child);
+               else if (strcasecmp ("Plugin", child->key) == 0)
+                       status = mr_config_add_regex (&m->plugin, child);
+               else if (strcasecmp ("PluginInstance", child->key) == 0)
+                       status = mr_config_add_regex (&m->plugin_instance, child);
+               else if (strcasecmp ("Type", child->key) == 0)
+                       status = mr_config_add_regex (&m->type, child);
+               else if (strcasecmp ("TypeInstance", child->key) == 0)
+                       status = mr_config_add_regex (&m->type_instance, child);
+               else
+               {
+                       log_err ("The `%s' configuration option is not understood and "
+                                       "will be ignored.", child->key);
+                       status = 0;
+               }
+
+               if (status != 0)
+                       break;
+       }
+
+       /* Additional sanity-checking */
+       while (status == 0)
+       {
+               if ((m->host == NULL)
+                               && (m->plugin == NULL)
+                               && (m->plugin_instance == NULL)
+                               && (m->type == NULL)
+                               && (m->type_instance == NULL))
+               {
+                       log_err ("No (valid) regular expressions have been configured. "
+                                       "This match will be ignored.");
+                       status = -1;
+               }
+
+               break;
+       }
+
+       if (status != 0)
+       {
+               mr_free_match (m);
+               return (status);
+       }
+
+       *user_data = m;
+       return (0);
+} /* }}} int mr_create */
+
+static int mr_destroy (void **user_data) /* {{{ */
+{
+       if ((user_data != NULL) && (*user_data != NULL))
+               mr_free_match (*user_data);
+       return (0);
+} /* }}} int mr_destroy */
+
+static int mr_match (const data_set_t *ds, const value_list_t *vl, /* {{{ */
+               notification_meta_t **meta, void **user_data)
+{
+       mr_match_t *m;
+
+       if ((user_data == NULL) || (*user_data == NULL))
+               return (-1);
+
+       m = *user_data;
+
+       if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH)
+               return (FC_MATCH_NO_MATCH);
+       if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
+               return (FC_MATCH_NO_MATCH);
+       if (mr_match_regexen (m->plugin_instance,
+                               vl->plugin_instance) == FC_MATCH_NO_MATCH)
+               return (FC_MATCH_NO_MATCH);
+       if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
+               return (FC_MATCH_NO_MATCH);
+       if (mr_match_regexen (m->type_instance,
+                               vl->type_instance) == FC_MATCH_NO_MATCH)
+               return (FC_MATCH_NO_MATCH);
+
+       return (FC_MATCH_MATCHES);
+} /* }}} int mr_match */
+
+void module_register (void)
+{
+       match_proc_t mproc;
+
+       memset (&mproc, 0, sizeof (mproc));
+       mproc.create  = mr_create;
+       mproc.destroy = mr_destroy;
+       mproc.match   = mr_match;
+       fc_register_match ("regex", mproc);
+} /* module_register */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */
+
diff --git a/src/match_value.c b/src/match_value.c
new file mode 100644 (file)
index 0000000..b850b77
--- /dev/null
@@ -0,0 +1,219 @@
+/**
+ * collectd - src/match_value.c
+ * Copyright (C) 2008  Florian 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 Forster <octo at verplant.org>
+ **/
+
+/*
+ * This module allows to filter and rewrite value lists based on
+ * Perl-compatible regular expressions.
+ */
+
+#include "collectd.h"
+#include "utils_cache.h"
+#include "filter_chain.h"
+
+/*
+ * private data types
+ */
+struct mv_match_s;
+typedef struct mv_match_s mv_match_t;
+struct mv_match_s
+{
+  gauge_t min;
+  gauge_t max;
+  int invert;
+};
+
+/*
+ * internal helper functions
+ */
+static void mv_free_match (mv_match_t *m) /* {{{ */
+{
+  if (m == NULL)
+    return;
+
+  free (m);
+} /* }}} void mv_free_match */
+
+static int mv_config_add_gauge (gauge_t *ret_value, /* {{{ */
+    oconfig_item_t *ci)
+{
+
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+  {
+    ERROR ("`value' match: `%s' needs exactly one numeric argument.",
+        ci->key);
+    return (-1);
+  }
+
+  *ret_value = ci->values[0].value.number;
+
+  return (0);
+} /* }}} int mv_config_add_gauge */
+
+static int mv_config_add_boolean (int *ret_value, /* {{{ */
+    oconfig_item_t *ci)
+{
+
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+  {
+    ERROR ("`value' match: `%s' needs exactly one boolean argument.",
+        ci->key);
+    return (-1);
+  }
+
+  if (ci->values[0].value.boolean)
+    *ret_value = 1;
+  else
+    *ret_value = 0;
+
+  return (0);
+} /* }}} int mv_config_add_boolean */
+
+static int mv_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+  mv_match_t *m;
+  int status;
+  int i;
+
+  m = (mv_match_t *) malloc (sizeof (*m));
+  if (m == NULL)
+  {
+    ERROR ("mv_create: malloc failed.");
+    return (-ENOMEM);
+  }
+  memset (m, 0, sizeof (*m));
+
+  m->min = NAN;
+  m->max = NAN;
+  m->invert = 0;
+
+  status = 0;
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Min", child->key) == 0)
+      status = mv_config_add_gauge (&m->min, child);
+    else if (strcasecmp ("Max", child->key) == 0)
+      status = mv_config_add_gauge (&m->max, child);
+    else if (strcasecmp ("Invert", child->key) == 0)
+      status = mv_config_add_boolean (&m->invert, child);
+    else
+    {
+      ERROR ("`value' match: The `%s' configuration option is not "
+          "understood and will be ignored.", child->key);
+      status = 0;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  /* Additional sanity-checking */
+  while (status == 0)
+  {
+    if (isnan (m->min) && isnan (m->max))
+    {
+      ERROR ("`value' match: Neither minimum nor maximum are defined. "
+          "This match will be ignored.");
+      status = -1;
+    }
+
+    break;
+  }
+
+  if (status != 0)
+  {
+    mv_free_match (m);
+    return (status);
+  }
+
+  *user_data = m;
+  return (0);
+} /* }}} int mv_create */
+
+static int mv_destroy (void **user_data) /* {{{ */
+{
+  if ((user_data != NULL) && (*user_data != NULL))
+    mv_free_match (*user_data);
+  return (0);
+} /* }}} int mv_destroy */
+
+static int mv_match (const data_set_t *ds, const value_list_t *vl, /* {{{ */
+    notification_meta_t **meta, void **user_data)
+{
+  mv_match_t *m;
+  gauge_t *values;
+  int status;
+  int i;
+
+  if ((user_data == NULL) || (*user_data == NULL))
+    return (-1);
+
+  m = *user_data;
+
+  values = uc_get_rate (ds, vl);
+  if (values == NULL)
+  {
+    ERROR ("`value' match: Retrieving the current rate from the cache "
+        "failed.");
+    return (-1);
+  }
+
+  status = FC_MATCH_MATCHES;
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    DEBUG ("`value' match: current = %g; min = %g; max = %g; invert = %s;",
+        values[i], m->min, m->max,
+        m->invert ? "true" : "false");
+
+    if ((!isnan (m->min) && (values[i] < m->min))
+        || (!isnan (m->max) && (values[i] > m->max)))
+    {
+      status = FC_MATCH_NO_MATCH;
+      break;
+    }
+  }
+
+  if (m->invert)
+  {
+    if (status == FC_MATCH_MATCHES)
+      status = FC_MATCH_NO_MATCH;
+    else
+      status = FC_MATCH_MATCHES;
+  }
+
+  free (values);
+  return (status);
+} /* }}} int mv_match */
+
+void module_register (void)
+{
+  match_proc_t mproc;
+
+  memset (&mproc, 0, sizeof (mproc));
+  mproc.create  = mv_create;
+  mproc.destroy = mv_destroy;
+  mproc.match   = mv_match;
+  fc_register_match ("value", mproc);
+} /* module_register */
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
+
diff --git a/src/meta_data.c b/src/meta_data.c
new file mode 100644 (file)
index 0000000..1412f7e
--- /dev/null
@@ -0,0 +1,536 @@
+/**
+ * collectd - src/meta_data.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>
+ **/
+
+/*
+ * First tell the compiler to stick to the C99 and POSIX standards as close as
+ * possible.
+ */
+#ifndef __STRICT_ANSI__ /* {{{ */
+# define __STRICT_ANSI__
+#endif
+
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+
+#ifdef _POSIX_C_SOURCE
+# undef _POSIX_C_SOURCE
+#endif
+#define _POSIX_C_SOURCE 200112L
+
+#if 0
+/* Single UNIX needed for strdup. */
+#ifdef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
+#endif
+#define _XOPEN_SOURCE 500
+#endif
+
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+#ifndef _THREAD_SAFE
+# define _THREAD_SAFE
+#endif
+
+#ifdef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* }}} */
+
+#include "collectd.h"
+#include "plugin.h"
+#include "meta_data.h"
+
+#include <pthread.h>
+
+/*
+ * Defines
+ */
+#define MD_TYPE_STRING       1
+#define MD_TYPE_SIGNED_INT   2
+#define MD_TYPE_UNSIGNED_INT 3
+#define MD_TYPE_DOUBLE       4
+
+/*
+ * Data types
+ */
+union meta_value_u
+{
+  char    *mv_string;
+  int64_t  mv_signed_int;
+  uint64_t mv_unsigned_int;
+  double   mv_double;
+};
+typedef union meta_value_u meta_value_t;
+
+struct meta_entry_s;
+typedef struct meta_entry_s meta_entry_t;
+struct meta_entry_s
+{
+  char         *key;
+  meta_value_t  value;
+  int           type;
+  meta_entry_t *next;
+};
+
+struct meta_data_s
+{
+  meta_entry_t   *head;
+  pthread_mutex_t lock;
+};
+
+/*
+ * Private functions
+ */
+static char *md_strdup (const char *orig) /* {{{ */
+{
+  size_t sz;
+  char *dest;
+
+  if (orig == NULL)
+    return (NULL);
+
+  sz = strlen (orig) + 1;
+  dest = (char *) malloc (sz);
+  if (dest == NULL)
+    return (NULL);
+
+  memcpy (dest, orig, sz);
+
+  return (dest);
+} /* }}} char *md_strdup */
+
+static meta_entry_t *md_entry_alloc (const char *key) /* {{{ */
+{
+  meta_entry_t *e;
+
+  e = (meta_entry_t *) malloc (sizeof (*e));
+  if (e == NULL)
+  {
+    ERROR ("md_entry_alloc: malloc failed.");
+    return (NULL);
+  }
+  memset (e, 0, sizeof (*e));
+
+  e->key = md_strdup (key);
+  if (e->key == NULL)
+  {
+    free (e);
+    ERROR ("md_entry_alloc: md_strdup failed.");
+    return (NULL);
+  }
+
+  e->type = 0;
+  e->next = NULL;
+
+  return (e);
+} /* }}} meta_entry_t *md_entry_alloc */
+
+static void md_entry_free (meta_entry_t *e) /* {{{ */
+{
+  if (e == NULL)
+    return;
+
+  free (e->key);
+
+  if (e->type == MD_TYPE_STRING)
+    free (e->value.mv_string);
+
+  if (e->next != NULL)
+    md_entry_free (e->next);
+
+  free (e);
+} /* }}} void md_entry_free */
+
+static int md_entry_insert (meta_data_t *md, meta_entry_t *e) /* {{{ */
+{
+  meta_entry_t *this;
+  meta_entry_t *prev;
+
+  if ((md == NULL) || (e == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  prev = NULL;
+  this = md->head;
+  while (this != NULL)
+  {
+    if (strcasecmp (e->key, this->key) == 0)
+      break;
+
+    prev = this;
+    this = this->next;
+  }
+
+  if (this == NULL)
+  {
+    /* This key does not exist yet. */
+    if (md->head == NULL)
+      md->head = e;
+    else
+    {
+      assert (prev != NULL);
+      prev->next = e;
+    }
+
+    e->next = NULL;
+  }
+  else /* (this != NULL) */
+  {
+    if (prev == NULL)
+      md->head = e;
+    else
+      prev->next = e;
+
+    e->next = this->next;
+  }
+
+  pthread_mutex_unlock (&md->lock);
+
+  if (this != NULL)
+  {
+    this->next = NULL;
+    md_entry_free (this);
+  }
+
+  return (0);
+} /* }}} int md_entry_insert */
+
+/* XXX: The lock on md must be held while calling this function! */
+static meta_entry_t *md_entry_lookup (meta_data_t *md, /* {{{ */
+    const char *key)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (NULL);
+
+  for (e = md->head; e != NULL; e = e->next)
+    if (strcasecmp (key, e->key) == 0)
+      break;
+
+  return (e);
+} /* }}} meta_entry_t *md_entry_lookup */
+
+/*
+ * Public functions
+ */
+meta_data_t *meta_data_create (void) /* {{{ */
+{
+  meta_data_t *md;
+
+  md = (meta_data_t *) malloc (sizeof (*md));
+  if (md == NULL)
+  {
+    ERROR ("meta_data_create: malloc failed.");
+    return (NULL);
+  }
+  memset (md, 0, sizeof (*md));
+
+  md->head = NULL;
+  pthread_mutex_init (&md->lock, /* attr = */ NULL);
+
+  return (md);
+} /* }}} meta_data_t *meta_data_create */
+
+void meta_data_destroy (meta_data_t *md) /* {{{ */
+{
+  if (md == NULL)
+    return;
+
+  md_entry_free (md->head);
+  free (md);
+} /* }}} void meta_data_destroy */
+
+int meta_data_exists (meta_data_t *md, const char *key) /* {{{ */
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  for (e = md->head; e != NULL; e = e->next)
+  {
+    if (strcasecmp (key, e->key) == 0)
+    {
+      pthread_mutex_unlock (&md->lock);
+      return (1);
+    }
+  }
+
+  pthread_mutex_unlock (&md->lock);
+  return (0);
+} /* }}} int meta_data_exists */
+
+int meta_data_delete (meta_data_t *md, const char *key) /* {{{ */
+{
+  meta_entry_t *this;
+  meta_entry_t *prev;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  prev = NULL;
+  this = md->head;
+  while (this != NULL)
+  {
+    if (strcasecmp (key, this->key) == 0)
+      break;
+
+    prev = this;
+    this = this->next;
+  }
+
+  if (this == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (prev == NULL)
+    md->head = this->next;
+  else
+    prev->next = this->next;
+
+  pthread_mutex_unlock (&md->lock);
+
+  this->next = NULL;
+  md_entry_free (this);
+
+  return (0);
+} /* }}} int meta_data_delete */
+
+int meta_data_add_string (meta_data_t *md, /* {{{ */
+    const char *key, const char *value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  e = md_entry_alloc (key);
+  if (e == NULL)
+    return (-ENOMEM);
+
+  e->value.mv_string = md_strdup (value);
+  if (e->value.mv_string == NULL)
+  {
+    ERROR ("meta_data_add_string: md_strdup failed.");
+    md_entry_free (e);
+    return (-ENOMEM);
+  }
+  e->type = MD_TYPE_STRING;
+
+  return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_string */
+
+int meta_data_add_signed_int (meta_data_t *md, /* {{{ */
+    const char *key, int64_t value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  e = md_entry_alloc (key);
+  if (e == NULL)
+    return (-ENOMEM);
+
+  e->value.mv_signed_int = value;
+  e->type = MD_TYPE_SIGNED_INT;
+
+  return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_signed_int */
+
+int meta_data_add_unsigned_int (meta_data_t *md, /* {{{ */
+    const char *key, uint64_t value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  e = md_entry_alloc (key);
+  if (e == NULL)
+    return (-ENOMEM);
+
+  e->value.mv_unsigned_int = value;
+  e->type = MD_TYPE_UNSIGNED_INT;
+
+  return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_unsigned_int */
+
+int meta_data_add_double (meta_data_t *md, /* {{{ */
+    const char *key, double value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  e = md_entry_alloc (key);
+  if (e == NULL)
+    return (-ENOMEM);
+
+  e->value.mv_double = value;
+  e->type = MD_TYPE_DOUBLE;
+
+  return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_double */
+
+int meta_data_get_string (meta_data_t *md, /* {{{ */
+    const char *key, char **value)
+{
+  meta_entry_t *e;
+  char *temp;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (e->type != MD_TYPE_SIGNED_INT)
+  {
+    ERROR ("meta_data_get_signed_int: Type mismatch for key `%s'", e->key);
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  temp = md_strdup (e->value.mv_string);
+  if (temp == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    ERROR ("meta_data_get_string: md_strdup failed.");
+    return (-ENOMEM);
+  }
+  pthread_mutex_unlock (&md->lock);
+
+  *value = temp;
+
+  return (0);
+} /* }}} int meta_data_get_string */
+
+int meta_data_get_signed_int (meta_data_t *md, /* {{{ */
+    const char *key, int64_t *value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (e->type != MD_TYPE_SIGNED_INT)
+  {
+    ERROR ("meta_data_get_signed_int: Type mismatch for key `%s'", e->key);
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  *value = e->value.mv_signed_int;
+
+  pthread_mutex_unlock (&md->lock);
+  return (0);
+} /* }}} int meta_data_get_signed_int */
+
+int meta_data_get_unsigned_int (meta_data_t *md, /* {{{ */
+    const char *key, uint64_t *value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (e->type != MD_TYPE_UNSIGNED_INT)
+  {
+    ERROR ("meta_data_get_unsigned_int: Type mismatch for key `%s'", e->key);
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  *value = e->value.mv_unsigned_int;
+
+  pthread_mutex_unlock (&md->lock);
+  return (0);
+} /* }}} int meta_data_get_unsigned_int */
+
+int meta_data_get_double (meta_data_t *md, /* {{{ */
+    const char *key, double *value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (e->type != MD_TYPE_DOUBLE)
+  {
+    ERROR ("meta_data_get_double: Type mismatch for key `%s'", e->key);
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  *value = e->value.mv_double;
+
+  pthread_mutex_unlock (&md->lock);
+  return (0);
+} /* }}} int meta_data_get_double */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/meta_data.h b/src/meta_data.h
new file mode 100644 (file)
index 0000000..30e7285
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * collectd - src/meta_data.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 META_DATA_H
+#define META_DATA_H
+
+#include "collectd.h"
+
+struct meta_data_s;
+typedef struct meta_data_s meta_data_t;
+
+meta_data_t *meta_data_create (void);
+void meta_data_destroy (meta_data_t *md);
+
+int meta_data_exists (meta_data_t *md, const char *key);
+int meta_data_delete (meta_data_t *md, const char *key);
+
+int meta_data_add_string (meta_data_t *md,
+    const char *key,
+    const char *value);
+int meta_data_add_signed_int (meta_data_t *md,
+    const char *key,
+    int64_t value);
+int meta_data_add_unsigned_int (meta_data_t *md,
+    const char *key,
+    uint64_t value);
+int meta_data_add_double (meta_data_t *md,
+    const char *key,
+    double value);
+
+int meta_data_get_string (meta_data_t *md,
+    const char *key,
+    char **value);
+int meta_data_get_signed_int (meta_data_t *md,
+    const char *key,
+    int64_t *value);
+int meta_data_get_unsigned_int (meta_data_t *md,
+    const char *key,
+    uint64_t *value);
+int meta_data_get_double (meta_data_t *md,
+    const char *key,
+    double *value);
+
+#endif /* META_DATA_H */
+/* vim: set sw=2 sts=2 et : */
index 4e72b5bf8989669cafb724ce011cc8b0a28cc2ee..57ad23978de761297e235d3d87c28824a16b5b13 100644 (file)
@@ -38,14 +38,18 @@ static const char *config_keys[] =
        "User",
        "Password",
        "Database",
+       "Port",
+       "Socket",
        NULL
 };
-static int config_keys_num = 4;
+static int config_keys_num = 6;
 
 static char *host = "localhost";
 static char *user;
 static char *pass;
 static char *db = NULL;
+static char *socket = NULL;
+static int   port = 0;
 
 static MYSQL *getconnection (void)
 {
@@ -88,7 +92,7 @@ static MYSQL *getconnection (void)
                return (NULL);
        }
 
-       if (mysql_real_connect (con, host, user, pass, db, 0, NULL, 0) == NULL)
+       if (mysql_real_connect (con, host, user, pass, db, port, socket, 0) == NULL)
        {
                ERROR ("mysql_real_connect failed: %s", mysql_error (con));
                state = 0;
@@ -113,9 +117,36 @@ static int config (const char *key, const char *value)
                return ((pass = strdup (value)) == NULL ? 1 : 0);
        else if (strcasecmp (key, "database") == 0)
                return ((db = strdup (value)) == NULL ? 1 : 0);
+       else if (strcasecmp (key, "socket") == 0)
+               return ((socket = strdup (value)) == NULL ? 1 : 0);
+       else if (strcasecmp (key, "port") == 0)
+       {
+           char *endptr = NULL;
+           int temp;
+
+           errno = 0;
+           temp = strtol (value, &endptr, 0);
+           if ((errno != 0) || (value == endptr))
+           {
+               ERROR ("mysql plugin: Invalid \"Port\" argument: %s",
+                       value);
+               port = 0;
+               return (1);
+           }
+           else if ((temp < 0) || (temp >= 65535))
+           {
+               ERROR ("mysql plugin: Port number out of range: %i",
+                       temp);
+               port = 0;
+               return (1);
+           }
+
+           port = temp;
+           return (0);
+       }
        else
                return (-1);
-}
+} /* int config */
 
 static void counter_submit (const char *type, const char *type_instance,
                counter_t value)
diff --git a/src/openvpn.c b/src/openvpn.c
new file mode 100644 (file)
index 0000000..16d4502
--- /dev/null
@@ -0,0 +1,136 @@
+/**
+ * collectd - src/openvpn.c
+ * Copyright (C) 2008  Doug MacEachern
+ * 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:
+ *   Doug MacEachern <dougm at hyperic.com>
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+static char *status_file = "/etc/openvpn/openvpn-status.log";
+
+static const char *config_keys[] =
+{
+       "StatusFile"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+/* copy-n-pasted from common.c - changed delim to "," */
+static int openvpn_strsplit (char *string, char **fields, size_t size)
+{
+        size_t i;
+        char *ptr;
+        char *saveptr;
+
+        i = 0;
+        ptr = string;
+        saveptr = NULL;
+        while ((fields[i] = strtok_r (ptr, ",", &saveptr)) != NULL)
+        {
+                ptr = NULL;
+                i++;
+
+                if (i >= size)
+                        break;
+        }
+
+        return (i);
+} /* int openvpn_strsplit */
+
+static void openvpn_submit (char *name, counter_t rx, counter_t tx)
+{
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = rx;
+       values[1].counter = tx;
+
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+       vl.time = time (NULL);
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "openvpn", sizeof (vl.plugin));
+       sstrncpy (vl.type_instance, name, sizeof (vl.type_instance));
+       sstrncpy (vl.type, "if_octets", sizeof (vl.type));
+
+       plugin_dispatch_values (&vl);
+} /* void openvpn_submit */
+
+static int openvpn_read (void)
+{
+       char *name;
+       counter_t rx, tx;
+       FILE *fh;
+       char buffer[1024];
+       char *fields[8];
+       const int max_fields = sizeof(fields)/sizeof(fields[0]);
+       int   fields_num;
+       static const char *prefix = "CLIENT_LIST,";
+
+       fh = fopen (status_file, "r");
+       if (fh == NULL)
+               return (-1);
+
+        /* status file is generated by openvpn/multi.c:multi_print_status()
+        * this plugin requires server.conf: "status-version 2"
+         * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
+         */
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               if (strncmp(buffer, prefix, strlen(prefix)) != 0)
+               {
+                       continue;
+               }
+
+               fields_num = openvpn_strsplit (buffer, fields, max_fields);
+               if (fields_num != max_fields)
+               {
+                       continue;
+               }
+               name =      fields[1];  /* "Common Name" */
+               rx = atoll (fields[4]); /* "Bytes Received */
+               tx = atoll (fields[5]); /* "Bytes Sent" */
+               openvpn_submit (name, rx, tx);
+       }
+       fclose (fh);
+
+       return (0);
+} /* int openvpn_read */
+
+static int openvpn_config (const char *key, const char *value)
+{
+       if (strcasecmp ("StatusFile", key) == 0)
+       {
+               status_file = strdup(value);
+       }
+       else
+       {
+               return (-1);
+       }
+        return (0);
+} /* int openvpn_config */
+
+void module_register (void)
+{
+       plugin_register_config ("openvpn", openvpn_config,
+                               config_keys, config_keys_num);
+       plugin_register_read ("openvpn", openvpn_read);
+} /* void module_register */
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 343c83aefa99e27f0de126460ca0dc0d8d04ff61..4a76c0aa6f636d9075057c83e39b8d6b6b5c060d 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 a887327760269e22e6b16d6bfb738ea4259b9f27..4ad7366cd0c5d9ff8244fb64866211f6d9ef27bd 100644 (file)
@@ -36,6 +36,7 @@
 #include "utils_llist.h"
 #include "utils_cache.h"
 #include "utils_threshold.h"
+#include "filter_chain.h"
 
 /*
  * Private structures
@@ -55,6 +56,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 +444,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 +550,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));
@@ -665,6 +678,73 @@ void plugin_read_all (void)
        pthread_mutex_unlock (&read_lock);
 } /* void plugin_read_all */
 
+int plugin_write (const char *plugin, /* {{{ */
+               const data_set_t *ds, const value_list_t *vl)
+{
+  int (*callback) (const data_set_t *ds, const value_list_t *vl);
+  llentry_t *le;
+  int status;
+
+  if (vl == NULL)
+    return (EINVAL);
+
+  if (list_write == NULL)
+    return (ENOENT);
+
+  if (ds == NULL)
+  {
+    ds = plugin_get_ds (vl->type);
+    if (ds == NULL)
+    {
+      ERROR ("plugin_write: Unable to lookup type `%s'.", vl->type);
+      return (ENOENT);
+    }
+  }
+
+  if (plugin == NULL)
+  {
+    int success = 0;
+    int failure = 0;
+
+    le = llist_head (list_write);
+    while (le != NULL)
+    {
+      callback = le->value;
+      status = (*callback) (ds, vl);
+      if (status != 0)
+        failure++;
+      else
+        success++;
+
+      le = le->next;
+    }
+
+    if ((success == 0) && (failure != 0))
+      status = -1;
+    else
+      status = 0;
+  }
+  else /* plugin != NULL */
+  {
+    le = llist_head (list_write);
+    while (le != NULL)
+    {
+      if (strcasecmp (plugin, le->key) == 0)
+        break;
+
+      le = le->next;
+    }
+
+    if (le == NULL)
+      return (ENOENT);
+
+    callback = le->value;
+    status = (*callback) (ds, vl);
+  }
+
+  return (status);
+} /* }}} int plugin_write */
+
 int plugin_flush (const char *plugin, int timeout, const char *identifier)
 {
   int (*callback) (int timeout, const char *identifier);
@@ -718,11 +798,9 @@ 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;
 
        if ((vl == NULL) || (*vl->type == '\0')) {
                ERROR ("plugin_dispatch_values: Invalid value list.");
@@ -787,16 +865,8 @@ int plugin_dispatch_values (value_list_t *vl)
 
        /* Update the value cache */
        uc_update (ds, vl);
-       ut_check_threshold (ds, vl);
 
-       le = llist_head (list_write);
-       while (le != NULL)
-       {
-               callback = (int (*) (const data_set_t *, const value_list_t *)) le->value;
-               (*callback) (ds, vl);
-
-               le = le->next;
-       }
+       fc_process (ds, vl);
 
        return (0);
 } /* int plugin_dispatch_values */
index dc3bbb0809fb3da3cabcd73dca44060799ab470e..a6f89a0ec37697ef11c61ffdc03312494aa06e22 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
  */
@@ -178,6 +183,36 @@ void plugin_init_all (void);
 void plugin_read_all (void);
 void plugin_shutdown_all (void);
 
+/*
+ * NAME
+ *  plugin_write
+ *
+ * DESCRIPTION
+ *  Calls the write function of the given plugin with the provided data set and
+ *  value list. It differs from `plugin_dispatch_value' in that it does not
+ *  update the cache, does no do threshold checking, call the chain subsystem
+ *  and so on. It looks up the requested plugin and invokes the function, end
+ *  of story.
+ *
+ * ARGUMENTS
+ *  plugin     Name of the plugin. If NULL, the value is sent to all registered
+ *             write functions.
+ *  ds         Pointer to the data_set_t structure. If NULL, the data set is
+ *             looked up according to the `type' member in the `vl' argument.
+ *  vl         The actual value to be processes. Must not be NULL.
+ *
+ * RETURN VALUE
+ *  Returns zero upon success or non-zero if an error occurred. If `plugin' is
+ *  NULL and more than one plugin is called, an error is only returned if *all*
+ *  plugins fail.
+ *
+ * NOTES
+ *  This is the function used by the `write' built-in target. May be used by
+ *  other target plugins.
+ */
+int plugin_write (const char *plugin,
+    const data_set_t *ds, const value_list_t *vl);
+
 int plugin_flush (const char *plugin, int timeout, const char *identifier);
 
 /*
@@ -196,6 +231,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 +248,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 faad16cd9b02b404c5326279bd8fa1aa85e64624..a5bacf49b2a4fc8df8aeaabbbdb2c8b4e3e63e4b 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;
 
index 8cf2562e8886b04b2dd7aa27ee681cc5dbb6c5b2..f4ffef8f67ed49a2ddb459e95cce8ad154aa49f9 100644 (file)
@@ -22,6 +22,7 @@
  *   Lyonel Vincent <lyonel at ezix.org>
  *   Florian octo Forster <octo at verplant.org>
  *   Oleg King <king2 at kaluga.ru>
+ *   Sebastian Harl <sh at tokkee.org>
  **/
 
 #include "collectd.h"
@@ -458,12 +459,18 @@ static int ps_config (const char *key, const char *value)
                int fields_num;
 
                new_val = strdup (value);
-               if (new_val == NULL)
+               if (new_val == NULL) {
+                       ERROR ("processes plugin: strdup failed when processing "
+                                       "`ProcessMatch %s'.", value);
                        return (1);
+               }
+
                fields_num = strsplit (new_val, fields,
                                STATIC_ARRAY_SIZE (fields));
                if (fields_num != 2)
                {
+                       ERROR ("processes plugin: `ProcessMatch' needs exactly "
+                                       "two string arguments.");
                        sfree (new_val);
                        return (1);
                }
@@ -472,6 +479,8 @@ static int ps_config (const char *key, const char *value)
        }
        else
        {
+               ERROR ("processes plugin: The `%s' configuration option is not "
+                               "understood and will be ignored.", key);
                return (-1);
        }
 
@@ -746,6 +755,96 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        /* success */
        return (0);
 } /* int ps_read_process (...) */
+
+static char *ps_get_cmdline (pid_t pid, char *name, char *buf, size_t buf_len)
+{
+       char  *buf_ptr;
+       size_t len;
+
+       char file[PATH_MAX];
+       int  fd;
+
+       size_t n;
+
+       if ((pid < 1) || (NULL == buf) || (buf_len < 2))
+               return NULL;
+
+       ssnprintf (file, sizeof (file), "/proc/%u/cmdline", pid);
+
+       fd = open (file, O_RDONLY);
+       if (fd < 0) {
+               char errbuf[4096];
+               WARNING ("processes plugin: Failed to open `%s': %s.", file,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return NULL;
+       }
+
+       buf_ptr = buf;
+       len     = buf_len;
+
+       n = 0;
+
+       while (42) {
+               size_t status;
+
+               status = read (fd, (void *)buf_ptr, len);
+
+               if (status < 0) {
+                       char errbuf[4096];
+
+                       if ((EAGAIN == errno) || (EINTR == errno))
+                               continue;
+
+                       WARNING ("processes plugin: Failed to read from `%s': %s.", file,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       close (fd);
+                       return NULL;
+               }
+
+               n += status;
+
+               if (status == 0)
+                       break;
+
+               buf_ptr += status;
+               len     -= status;
+
+               if (len <= 0)
+                       break;
+       }
+
+       close (fd);
+
+       if (0 == n) {
+               /* cmdline not available; e.g. kernel thread, zombie */
+               if (NULL == name)
+                       return NULL;
+
+               ssnprintf (buf, buf_len, "[%s]", name);
+               return buf;
+       }
+
+       assert (n <= buf_len);
+
+       if (n == buf_len)
+               --n;
+       buf[n] = '\0';
+
+       --n;
+       /* remove trailing whitespace */
+       while ((n > 0) && (isspace (buf[n]) || ('\0' == buf[n]))) {
+               buf[n] = '\0';
+               --n;
+       }
+
+       /* arguments are separated by '\0' in /proc/<pid>/cmdline */
+       while (n > 0) {
+               if ('\0' == buf[n])
+                       buf[n] = ' ';
+               --n;
+       }
+       return buf;
+} /* char *ps_get_cmdline (...) */
 #endif /* KERNEL_LINUX */
 
 #if HAVE_THREAD_INFO
@@ -1061,6 +1160,8 @@ static int ps_read (void)
        DIR           *proc;
        int            pid;
 
+       char cmdline[ARG_MAX];
+
        int        status;
        procstat_t ps;
        procstat_entry_t pse;
@@ -1121,8 +1222,9 @@ static int ps_read (void)
                        case 'W': paging++;   break;
                }
 
-               /* FIXME: cmdline should be here instead of NULL */
-               ps_list_add (ps.name, NULL, &pse);
+               ps_list_add (ps.name,
+                               ps_get_cmdline (pid, ps.name, cmdline, sizeof (cmdline)),
+                               &pse);
        }
 
        closedir (proc);
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 e1c7902b1f5e346250f8ea82ae52512421fca173..3265561673fb2263c76965c2bea5f4f7ab0d78d2 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
@@ -1200,15 +860,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)
        {
@@ -1221,7 +881,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)
        {
@@ -1240,8 +900,8 @@ 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");
@@ -1249,15 +909,15 @@ static int rrd_config (const char *key, const char *value)
                                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);
@@ -1273,7 +933,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)
        {
@@ -1327,16 +987,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.");
@@ -1372,7 +1034,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 4d6e76947a37ef983c7180debff49dfc1c25f6c0..3ba93fe8438078c92bf3481401dfd11a8441f103 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
  */
diff --git a/src/target_notification.c b/src/target_notification.c
new file mode 100644 (file)
index 0000000..f77d338
--- /dev/null
@@ -0,0 +1,323 @@
+/**
+ * collectd - src/target_notification.c
+ * Copyright (C) 2008  Florian 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 Forster <octo at verplant.org>
+ **/
+
+/*
+ * First tell the compiler to stick to the C99 and POSIX standards as close as
+ * possible.
+ */
+#ifndef __STRICT_ANSI__ /* {{{ */
+# define __STRICT_ANSI__
+#endif
+
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+
+#ifdef _POSIX_C_SOURCE
+# undef _POSIX_C_SOURCE
+#endif
+#define _POSIX_C_SOURCE 200112L
+
+#if 0
+/* Single UNIX needed for strdup. */
+#ifdef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
+#endif
+#define _XOPEN_SOURCE 500
+#endif
+
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+#ifndef _THREAD_SAFE
+# define _THREAD_SAFE
+#endif
+
+#ifdef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* }}} */
+
+#include "collectd.h"
+#include "common.h"
+#include "filter_chain.h"
+#include "utils_cache.h"
+#include "utils_subst.h"
+
+struct tn_data_s
+{
+  int severity;
+  char *message;
+};
+typedef struct tn_data_s tn_data_t;
+
+static int tn_config_add_severity (tn_data_t *data, /* {{{ */
+    const oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    ERROR ("Target `notification': The `%s' option requires exactly one string "
+        "argument.", ci->key);
+    return (-1);
+  }
+
+  if ((strcasecmp ("FAILURE", ci->values[0].value.string) == 0)
+      || (strcasecmp ("CRITICAL", ci->values[0].value.string) == 0))
+    data->severity = NOTIF_FAILURE;
+  else if ((strcasecmp ("WARNING", ci->values[0].value.string) == 0)
+      || (strcasecmp ("WARN", ci->values[0].value.string) == 0))
+    data->severity = NOTIF_WARNING;
+  else if (strcasecmp ("OKAY", ci->values[0].value.string) == 0)
+    data->severity = NOTIF_OKAY;
+  else
+  {
+    WARNING ("Target `notification': Unknown severity `%s'. "
+        "Will use `FAILURE' instead.",
+        ci->values[0].value.string);
+    data->severity = NOTIF_FAILURE;
+  }
+
+  return (0);
+} /* }}} int tn_config_add_severity */
+
+static int tn_config_add_string (char **dest, /* {{{ */
+    const oconfig_item_t *ci)
+{
+  char *temp;
+
+  if (dest == NULL)
+    return (-EINVAL);
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    ERROR ("Target `notification': The `%s' option requires exactly one string "
+        "argument.", ci->key);
+    return (-1);
+  }
+
+  if (ci->values[0].value.string[0] == 0)
+  {
+    ERROR ("Target `notification': The `%s' option does not accept empty strings.",
+        ci->key);
+    return (-1);
+  }
+
+  temp = sstrdup (ci->values[0].value.string);
+  if (temp == NULL)
+  {
+    ERROR ("tn_config_add_string: sstrdup failed.");
+    return (-1);
+  }
+
+  free (*dest);
+  *dest = temp;
+
+  return (0);
+} /* }}} int tn_config_add_string */
+
+static int tn_destroy (void **user_data) /* {{{ */
+{
+  tn_data_t *data;
+
+  if (user_data == NULL)
+    return (-EINVAL);
+
+  data = *user_data;
+  if (data == NULL)
+    return (0);
+
+  sfree (data->message);
+  sfree (data);
+
+  return (0);
+} /* }}} int tn_destroy */
+
+static int tn_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+  tn_data_t *data;
+  int status;
+  int i;
+
+  data = (tn_data_t *) malloc (sizeof (*data));
+  if (data == NULL)
+  {
+    ERROR ("tn_create: malloc failed.");
+    return (-ENOMEM);
+  }
+  memset (data, 0, sizeof (*data));
+
+  data->message = NULL;
+  data->severity = 0;
+
+  status = 0;
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Message", child->key) == 0)
+      status = tn_config_add_string (&data->message, child);
+    else if (strcasecmp ("Severity", child->key) == 0)
+      status = tn_config_add_severity (data, child);
+    else
+    {
+      ERROR ("Target `notification': The `%s' configuration option is not understood "
+          "and will be ignored.", child->key);
+      status = 0;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  /* Additional sanity-checking */
+  while (status == 0)
+  {
+    if ((data->severity != NOTIF_FAILURE)
+        && (data->severity != NOTIF_WARNING)
+        && (data->severity != NOTIF_OKAY))
+    {
+      DEBUG ("Target `notification': Setting "
+          "the default severity `WARNING'.");
+      data->severity = NOTIF_WARNING;
+    }
+
+    if (data->message == NULL)
+    {
+      ERROR ("Target `notification': No `Message' option has been specified. "
+          "Without it, the `Notification' target is useless.");
+      status = -1;
+    }
+
+    break;
+  }
+
+  if (status != 0)
+  {
+    tn_destroy ((void *) data);
+    return (status);
+  }
+
+  *user_data = data;
+  return (0);
+} /* }}} int tn_create */
+
+static int tn_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
+    notification_meta_t **meta, void **user_data)
+{
+  tn_data_t *data;
+  notification_t n;
+  char temp[NOTIF_MAX_MSG_LEN];
+
+  gauge_t *rates;
+  int rates_failed;
+
+  int i;
+
+  if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
+    return (-EINVAL);
+
+  data = *user_data;
+  if (data == NULL)
+  {
+    ERROR ("Target `notification': Invoke: `data' is NULL.");
+    return (-EINVAL);
+  }
+
+  /* Initialize the structure. */
+  memset (&n, 0, sizeof (n));
+  n.severity = data->severity;
+  n.time = time (NULL);
+  sstrncpy (n.message, data->message, sizeof (n.message));
+  sstrncpy (n.host, vl->host, sizeof (n.host));
+  sstrncpy (n.plugin, vl->plugin, sizeof (n.plugin));
+  sstrncpy (n.plugin_instance, vl->plugin_instance,
+      sizeof (n.plugin_instance));
+  sstrncpy (n.type, vl->type, sizeof (n.type));
+  sstrncpy (n.type_instance, vl->type_instance,
+      sizeof (n.type_instance));
+  n.meta = NULL;
+
+#define REPLACE_FIELD(t,v) \
+  if (subst_string (temp, sizeof (temp), n.message, t, v) != NULL) \
+    sstrncpy (n.message, temp, sizeof (n.message));
+  REPLACE_FIELD ("%{host}", n.host);
+  REPLACE_FIELD ("%{plugin}", n.plugin);
+  REPLACE_FIELD ("%{plugin_instance}", n.plugin_instance);
+  REPLACE_FIELD ("%{type}", n.type);
+  REPLACE_FIELD ("%{type_instance}", n.type_instance);
+
+  rates_failed = 0;
+  rates = NULL;
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    char template[DATA_MAX_NAME_LEN];
+    char value_str[DATA_MAX_NAME_LEN];
+
+    ssnprintf (template, sizeof (template), "%%{ds:%s}", ds->ds[i].name);
+
+    if (ds->ds[i].type != DS_TYPE_GAUGE)
+    {
+      if ((rates == NULL) && (rates_failed == 0))
+      {
+        rates = uc_get_rate (ds, vl);
+        if (rates == NULL)
+          rates_failed = 1;
+      }
+    }
+
+    /* If this is a gauge value, use the current value. */
+    if (ds->ds[i].type == DS_TYPE_GAUGE)
+      ssnprintf (value_str, sizeof (value_str),
+          "%g", (double) vl->values[i].gauge);
+    /* If it's a counter, try to use the current rate. This may fail, if the
+     * value has been renamed. */
+    else if (rates != NULL)
+      ssnprintf (value_str, sizeof (value_str),
+          "%g", (double) rates[i]);
+    /* Since we don't know any better, use the string `unknown'. */
+    else
+      sstrncpy (value_str, "unknown", sizeof (value_str));
+
+    REPLACE_FIELD (template, value_str);
+  }
+  sfree (rates);
+
+  plugin_dispatch_notification (&n);
+
+  return (FC_TARGET_CONTINUE);
+} /* }}} int tn_invoke */
+
+void module_register (void)
+{
+       target_proc_t tproc;
+
+       memset (&tproc, 0, sizeof (tproc));
+       tproc.create  = tn_create;
+       tproc.destroy = tn_destroy;
+       tproc.invoke  = tn_invoke;
+       fc_register_target ("notification", tproc);
+} /* module_register */
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
+
diff --git a/src/target_replace.c b/src/target_replace.c
new file mode 100644 (file)
index 0000000..6e72f31
--- /dev/null
@@ -0,0 +1,394 @@
+/**
+ * collectd - src/target_replace.c
+ * Copyright (C) 2008  Florian 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 Forster <octo at verplant.org>
+ **/
+
+/*
+ * First tell the compiler to stick to the C99 and POSIX standards as close as
+ * possible.
+ */
+#ifndef __STRICT_ANSI__ /* {{{ */
+# define __STRICT_ANSI__
+#endif
+
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+
+#ifdef _POSIX_C_SOURCE
+# undef _POSIX_C_SOURCE
+#endif
+#define _POSIX_C_SOURCE 200112L
+
+#if 0
+/* Single UNIX needed for strdup. */
+#ifdef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
+#endif
+#define _XOPEN_SOURCE 500
+#endif
+
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+#ifndef _THREAD_SAFE
+# define _THREAD_SAFE
+#endif
+
+#ifdef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* }}} */
+
+#include "collectd.h"
+#include "common.h"
+#include "filter_chain.h"
+#include "utils_subst.h"
+
+#include <regex.h>
+
+struct tr_action_s;
+typedef struct tr_action_s tr_action_t;
+struct tr_action_s
+{
+  regex_t re;
+  char *replacement;
+  int may_be_empty;
+
+  tr_action_t *next;
+};
+
+struct tr_data_s
+{
+  tr_action_t *host;
+  tr_action_t *plugin;
+  tr_action_t *plugin_instance;
+  /* tr_action_t *type; */
+  tr_action_t *type_instance;
+};
+typedef struct tr_data_s tr_data_t;
+
+static char *tr_strdup (const char *orig) /* {{{ */
+{
+  size_t sz;
+  char *dest;
+
+  if (orig == NULL)
+    return (NULL);
+
+  sz = strlen (orig) + 1;
+  dest = (char *) malloc (sz);
+  if (dest == NULL)
+    return (NULL);
+
+  memcpy (dest, orig, sz);
+
+  return (dest);
+} /* }}} char *tr_strdup */
+
+static void tr_action_destroy (tr_action_t *act) /* {{{ */
+{
+  if (act == NULL)
+    return;
+
+  regfree (&act->re);
+  sfree (act->replacement);
+
+  if (act->next != NULL)
+    tr_action_destroy (act->next);
+
+  sfree (act);
+} /* }}} void tr_action_destroy */
+
+static int tr_config_add_action (tr_action_t **dest, /* {{{ */
+    const oconfig_item_t *ci, int may_be_empty)
+{
+  tr_action_t *act;
+  int status;
+
+  if (dest == NULL)
+    return (-EINVAL);
+
+  if ((ci->values_num != 2)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING)
+      || (ci->values[1].type != OCONFIG_TYPE_STRING))
+  {
+    ERROR ("Target `replace': The `%s' option requires exactly two string "
+        "arguments.", ci->key);
+    return (-1);
+  }
+
+  act = (tr_action_t *) malloc (sizeof (*act));
+  if (act == NULL)
+  {
+    ERROR ("tr_config_add_action: malloc failed.");
+    return (-ENOMEM);
+  }
+  memset (act, 0, sizeof (*act));
+
+  act->replacement = NULL;
+  act->may_be_empty = may_be_empty;
+
+  status = regcomp (&act->re, ci->values[0].value.string, REG_EXTENDED);
+  if (status != 0)
+  {
+    char errbuf[1024] = "";
+
+    /* regerror assures null termination. */
+    regerror (status, &act->re, errbuf, sizeof (errbuf));
+    ERROR ("Target `replace': Compiling the regular expression `%s' "
+        "failed: %s.",
+        ci->values[0].value.string, errbuf);
+    sfree (act);
+    return (-EINVAL);
+  }
+
+  act->replacement = tr_strdup (ci->values[1].value.string);
+  if (act->replacement == NULL)
+  {
+    ERROR ("tr_config_add_action: tr_strdup failed.");
+    regfree (&act->re);
+    sfree (act);
+    return (-ENOMEM);
+  }
+
+  /* Insert action at end of list. */
+  if (*dest == NULL)
+    *dest = act;
+  else
+  {
+    tr_action_t *prev;
+
+    prev = *dest;
+    while (prev->next != NULL)
+      prev = prev->next;
+
+    prev->next = act;
+  }
+
+  return (0);
+} /* }}} int tr_config_add_action */
+
+static int tr_action_invoke (tr_action_t *act_head, /* {{{ */
+    char *buffer_in, size_t buffer_in_size, int may_be_empty)
+{
+  tr_action_t *act;
+  int status;
+  char buffer[DATA_MAX_NAME_LEN];
+  regmatch_t matches[8];
+
+  if (act_head == NULL)
+    return (-EINVAL);
+
+  sstrncpy (buffer, buffer_in, sizeof (buffer));
+  memset (matches, 0, sizeof (matches));
+
+  DEBUG ("target_replace plugin: tr_action_invoke: <- buffer = %s;", buffer);
+
+  for (act = act_head; act != NULL; act = act->next)
+  {
+    char temp[DATA_MAX_NAME_LEN];
+    char *subst_status;
+
+    status = regexec (&act->re, buffer,
+        STATIC_ARRAY_SIZE (matches), matches,
+        /* flags = */ 0);
+    if (status == REG_NOMATCH)
+      continue;
+    else if (status != 0)
+    {
+      char errbuf[1024] = "";
+
+      regerror (status, &act->re, errbuf, sizeof (errbuf));
+      ERROR ("Target `replace': Executing a regular expression failed: %s.",
+          errbuf);
+      continue;
+    }
+
+    subst_status = subst (temp, sizeof (temp), buffer,
+        matches[0].rm_so, matches[0].rm_eo, act->replacement);
+    if (subst_status == NULL)
+    {
+      ERROR ("Target `replace': subst (buffer = %s, start = %zu, end = %zu, "
+          "replacement = %s) failed.",
+          buffer, (size_t) matches[0].rm_so, (size_t) matches[0].rm_eo,
+          act->replacement);
+      continue;
+    }
+    sstrncpy (buffer, temp, sizeof (buffer));
+
+    DEBUG ("target_replace plugin: tr_action_invoke: -- buffer = %s;", buffer);
+  } /* for (act = act_head; act != NULL; act = act->next) */
+
+  if ((may_be_empty == 0) && (buffer[0] == 0))
+  {
+    WARNING ("Target `replace': Replacement resulted in an empty string, "
+        "which is not allowed for this buffer (`host' or `plugin').");
+    return (0);
+  }
+
+  DEBUG ("target_replace plugin: tr_action_invoke: -> buffer = %s;", buffer);
+  sstrncpy (buffer_in, buffer, buffer_in_size);
+
+  return (0);
+} /* }}} int tr_action_invoke */
+
+static int tr_destroy (void **user_data) /* {{{ */
+{
+  tr_data_t *data;
+
+  if (user_data == NULL)
+    return (-EINVAL);
+
+  data = *user_data;
+  if (data == NULL)
+    return (0);
+
+  tr_action_destroy (data->host);
+  tr_action_destroy (data->plugin);
+  tr_action_destroy (data->plugin_instance);
+  /* tr_action_destroy (data->type); */
+  tr_action_destroy (data->type_instance);
+  sfree (data);
+
+  return (0);
+} /* }}} int tr_destroy */
+
+static int tr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+  tr_data_t *data;
+  int status;
+  int i;
+
+  data = (tr_data_t *) malloc (sizeof (*data));
+  if (data == NULL)
+  {
+    ERROR ("tr_create: malloc failed.");
+    return (-ENOMEM);
+  }
+  memset (data, 0, sizeof (*data));
+
+  data->host = NULL;
+  data->plugin = NULL;
+  data->plugin_instance = NULL;
+  /* data->type = NULL; */
+  data->type_instance = NULL;
+
+  status = 0;
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if ((strcasecmp ("Host", child->key) == 0)
+        || (strcasecmp ("Hostname", child->key) == 0))
+      status = tr_config_add_action (&data->host, child,
+          /* may be empty = */ 0);
+    else if (strcasecmp ("Plugin", child->key) == 0)
+      status = tr_config_add_action (&data->plugin, child,
+          /* may be empty = */ 0);
+    else if (strcasecmp ("PluginInstance", child->key) == 0)
+      status = tr_config_add_action (&data->plugin_instance, child,
+          /* may be empty = */ 1);
+#if 0
+    else if (strcasecmp ("Type", child->key) == 0)
+      status = tr_config_add_action (&data->type, child,
+          /* may be empty = */ 0);
+#endif
+    else if (strcasecmp ("TypeInstance", child->key) == 0)
+      status = tr_config_add_action (&data->type_instance, child,
+          /* may be empty = */ 1);
+    else
+    {
+      ERROR ("Target `replace': The `%s' configuration option is not understood "
+          "and will be ignored.", child->key);
+      status = 0;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  /* Additional sanity-checking */
+  while (status == 0)
+  {
+    if ((data->host == NULL)
+        && (data->plugin == NULL)
+        && (data->plugin_instance == NULL)
+        /* && (data->type == NULL) */
+        && (data->type_instance == NULL))
+    {
+      ERROR ("Target `replace': You need to set at lease one of `Host', "
+          "`Plugin', `PluginInstance', `Type', or `TypeInstance'.");
+      status = -1;
+    }
+
+    break;
+  }
+
+  if (status != 0)
+  {
+    tr_destroy ((void *) &data);
+    return (status);
+  }
+
+  *user_data = data;
+  return (0);
+} /* }}} int tr_create */
+
+static int tr_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
+    notification_meta_t **meta, void **user_data)
+{
+  tr_data_t *data;
+
+  if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
+    return (-EINVAL);
+
+  data = *user_data;
+  if (data == NULL)
+  {
+    ERROR ("Target `replace': Invoke: `data' is NULL.");
+    return (-EINVAL);
+  }
+
+#define HANDLE_FIELD(f,e) \
+  if (data->f != NULL) \
+    tr_action_invoke (data->f, vl->f, sizeof (vl->f), e)
+  HANDLE_FIELD (host, 0);
+  HANDLE_FIELD (plugin, 0);
+  HANDLE_FIELD (plugin_instance, 1);
+  /* HANDLE_FIELD (type); */
+  HANDLE_FIELD (type_instance, 1);
+
+  return (FC_TARGET_CONTINUE);
+} /* }}} int tr_invoke */
+
+void module_register (void)
+{
+       target_proc_t tproc;
+
+       memset (&tproc, 0, sizeof (tproc));
+       tproc.create  = tr_create;
+       tproc.destroy = tr_destroy;
+       tproc.invoke  = tr_invoke;
+       fc_register_target ("replace", tproc);
+} /* module_register */
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
+
diff --git a/src/target_set.c b/src/target_set.c
new file mode 100644 (file)
index 0000000..70b0fdf
--- /dev/null
@@ -0,0 +1,267 @@
+/**
+ * collectd - src/target_set.c
+ * Copyright (C) 2008  Florian 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 Forster <octo at verplant.org>
+ **/
+
+/*
+ * First tell the compiler to stick to the C99 and POSIX standards as close as
+ * possible.
+ */
+#ifndef __STRICT_ANSI__ /* {{{ */
+# define __STRICT_ANSI__
+#endif
+
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+
+#ifdef _POSIX_C_SOURCE
+# undef _POSIX_C_SOURCE
+#endif
+#define _POSIX_C_SOURCE 200112L
+
+#if 0
+/* Single UNIX needed for strdup. */
+#ifdef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
+#endif
+#define _XOPEN_SOURCE 500
+#endif
+
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+#ifndef _THREAD_SAFE
+# define _THREAD_SAFE
+#endif
+
+#ifdef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* }}} */
+
+#include "collectd.h"
+#include "common.h"
+#include "filter_chain.h"
+
+struct ts_data_s
+{
+  char *host;
+  char *plugin;
+  char *plugin_instance;
+  /* char *type; */
+  char *type_instance;
+};
+typedef struct ts_data_s ts_data_t;
+
+static char *ts_strdup (const char *orig) /* {{{ */
+{
+  size_t sz;
+  char *dest;
+
+  if (orig == NULL)
+    return (NULL);
+
+  sz = strlen (orig) + 1;
+  dest = (char *) malloc (sz);
+  if (dest == NULL)
+    return (NULL);
+
+  memcpy (dest, orig, sz);
+
+  return (dest);
+} /* }}} char *ts_strdup */
+
+static int ts_config_add_string (char **dest, /* {{{ */
+    const oconfig_item_t *ci, int may_be_empty)
+{
+  char *temp;
+
+  if (dest == NULL)
+    return (-EINVAL);
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    ERROR ("Target `set': The `%s' option requires exactly one string "
+        "argument.", ci->key);
+    return (-1);
+  }
+
+  if ((!may_be_empty) && (ci->values[0].value.string[0] == 0))
+  {
+    ERROR ("Target `set': The `%s' option does not accept empty strings.",
+        ci->key);
+    return (-1);
+  }
+
+  temp = ts_strdup (ci->values[0].value.string);
+  if (temp == NULL)
+  {
+    ERROR ("ts_config_add_string: ts_strdup failed.");
+    return (-1);
+  }
+
+  free (*dest);
+  *dest = temp;
+
+  return (0);
+} /* }}} int ts_config_add_string */
+
+static int ts_destroy (void **user_data) /* {{{ */
+{
+  ts_data_t *data;
+
+  if (user_data == NULL)
+    return (-EINVAL);
+
+  data = *user_data;
+  if (data == NULL)
+    return (0);
+
+  free (data->host);
+  free (data->plugin);
+  free (data->plugin_instance);
+  /* free (data->type); */
+  free (data->type_instance);
+  free (data);
+
+  return (0);
+} /* }}} int ts_destroy */
+
+static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+  ts_data_t *data;
+  int status;
+  int i;
+
+  data = (ts_data_t *) malloc (sizeof (*data));
+  if (data == NULL)
+  {
+    ERROR ("ts_create: malloc failed.");
+    return (-ENOMEM);
+  }
+  memset (data, 0, sizeof (*data));
+
+  data->host = NULL;
+  data->plugin = NULL;
+  data->plugin_instance = NULL;
+  /* data->type = NULL; */
+  data->type_instance = NULL;
+
+  status = 0;
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if ((strcasecmp ("Host", child->key) == 0)
+        || (strcasecmp ("Hostname", child->key) == 0))
+      status = ts_config_add_string (&data->host, child,
+          /* may be empty = */ 0);
+    else if (strcasecmp ("Plugin", child->key) == 0)
+      status = ts_config_add_string (&data->plugin, child,
+          /* may be empty = */ 0);
+    else if (strcasecmp ("PluginInstance", child->key) == 0)
+      status = ts_config_add_string (&data->plugin_instance, child,
+          /* may be empty = */ 1);
+#if 0
+    else if (strcasecmp ("Type", child->key) == 0)
+      status = ts_config_add_string (&data->type, child,
+          /* may be empty = */ 0);
+#endif
+    else if (strcasecmp ("TypeInstance", child->key) == 0)
+      status = ts_config_add_string (&data->type_instance, child,
+          /* may be empty = */ 1);
+    else
+    {
+      ERROR ("Target `set': The `%s' configuration option is not understood "
+          "and will be ignored.", child->key);
+      status = 0;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  /* Additional sanity-checking */
+  while (status == 0)
+  {
+    if ((data->host == NULL)
+        && (data->plugin == NULL)
+        && (data->plugin_instance == NULL)
+        /* && (data->type == NULL) */
+        && (data->type_instance == NULL))
+    {
+      ERROR ("Target `set': You need to set at lease one of `Host', "
+          "`Plugin', `PluginInstance', `Type', or `TypeInstance'.");
+      status = -1;
+    }
+
+    break;
+  }
+
+  if (status != 0)
+  {
+    ts_destroy ((void *) &data);
+    return (status);
+  }
+
+  *user_data = data;
+  return (0);
+} /* }}} int ts_create */
+
+static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
+    notification_meta_t **meta, void **user_data)
+{
+  ts_data_t *data;
+
+  if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
+    return (-EINVAL);
+
+  data = *user_data;
+  if (data == NULL)
+  {
+    ERROR ("Target `set': Invoke: `data' is NULL.");
+    return (-EINVAL);
+  }
+
+#define SET_FIELD(f) if (data->f != NULL) { sstrncpy (vl->f, data->f, sizeof (vl->f)); }
+  SET_FIELD (host);
+  SET_FIELD (plugin);
+  SET_FIELD (plugin_instance);
+  /* SET_FIELD (type); */
+  SET_FIELD (type_instance);
+
+  return (FC_TARGET_CONTINUE);
+} /* }}} int ts_invoke */
+
+void module_register (void)
+{
+       target_proc_t tproc;
+
+       memset (&tproc, 0, sizeof (tproc));
+       tproc.create  = ts_create;
+       tproc.destroy = ts_destroy;
+       tproc.invoke  = ts_invoke;
+       fc_register_target ("set", tproc);
+} /* module_register */
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
+
index a3b51dc812e16ac35715cb01e370ba7bc51878a7..67d8f0d3979d6f6ea5711c50fa661f2c6b7ddf43 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 3b4f1facbb53f6f8dfd1b304450aa3a146f82d57..4a1bfa4059d058d7726a1dfd59e10ab002ae8c3c 100644 (file)
@@ -29,7 +29,8 @@ happen. See L<rrdcreate(1)> for more details.
 =head1 FILES
 
 The location of the types.db file is defined by the B<TypesDB> configuration
-option (see L<collectd.conf(5)>).
+option (see L<collectd.conf(5)>). It defaults to collectd's shared data
+directory, i.E<nbsp>e. F<I<prefix>/share/collectd/>.
 
 =head1 CUSTOM TYPES
 
index dccafd1534255307701d030a82b706cbbacf797d..6b54acecaa30f26307e1e51504a388e4b09f831e 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 86134cdf9a19b1e542662352161a8e51c35bad9f..ed9ca9a345d2592421548a85908ecde141100212 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 73146e785b6b4bd6cd854871fc1f333549c994da..0c72d678a46f6d69a9c3b652a800fd36a1e25da8 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 8d5475b92fc3b75ccd20c28e84b873ebb00fe79a..7e900b509f7010cf4bc90d093140a287d044dd60 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 9ba52cc8d80f8a98b54dfaf25755179e9594849a..8460b13388488be5c1401a3ff23a57a3d6e4f4bb 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 e93d823e2ae8a52cb30cb15ff4be67c57308a184..b51a81c677a10d8777ca2d22b2ff7503093e0704 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 b3f08acdafb2b95ae53d8786ad503c7d0b4e30df..efc790319cebea5e3253cad748de71d4e8923aaa 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..640007c
--- /dev/null
@@ -0,0 +1,143 @@
+/**
+ * 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 */
+
+char *subst_string (char *buf, size_t buflen, const char *string,
+               const char *needle, const char *replacement)
+{
+       char *temp;
+       size_t needle_len;
+       size_t i;
+
+       if ((buf == NULL) || (string == NULL)
+                       || (needle == NULL) || (replacement == NULL))
+               return (NULL);
+
+       temp = (char *) malloc (buflen);
+       if (temp == NULL)
+       {
+               ERROR ("subst_string: malloc failed.");
+               return (NULL);
+       }
+
+       needle_len = strlen (needle);
+       strncpy (buf, string, buflen);
+
+       /* Limit the loop to prevent endless loops. */
+       for (i = 0; i < buflen; i++)
+       {
+               char *begin_ptr;
+               size_t begin;
+
+               /* Find `needle' in `buf'. */
+               begin_ptr = strstr (buf, needle);
+               if (begin_ptr == NULL)
+                       break;
+
+               /* Calculate the start offset. */
+               begin = begin_ptr - buf;
+
+               /* Substitute the region using `subst'. The result is stored in
+                * `temp'. */
+               begin_ptr = subst (temp, buflen, buf,
+                               begin, begin + needle_len,
+                               replacement);
+               if (begin_ptr == NULL)
+               {
+                       WARNING ("subst_string: subst failed.");
+                       break;
+               }
+
+               /* Copy the new string in `temp' to `buf' for the next round. */
+               strncpy (buf, temp, buflen);
+       }
+
+       if (i >= buflen)
+       {
+               WARNING ("subst_string: Loop exited after %zu iterations: "
+                               "string = %s; needle = %s; replacement = %s;",
+                               i, string, needle, replacement);
+       }
+
+       sfree (temp);
+       return (buf);
+} /* char *subst_string */
+
+/* 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..4387b85
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * 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);
+
+/*
+ * subst_string:
+ *
+ * Works like `subst', but instead of specifying start and end offsets you
+ * specify `needle', the string that is to be replaced. If `needle' is found
+ * in `string' (using strstr(3)), the offset is calculated and `subst' is
+ * called with the determined parameters.
+ *
+ * If the substring is not found, no error will be indicated and
+ * `subst_string' works mostly like `strncpy'.
+ *
+ * If the substring appears multiple times, all appearances will be replaced.
+ * If the substring has been found `buflen' times, an endless loop is assumed
+ * and the loop is broken. A warning is printed and the function returns
+ * success.
+ */
+char *subst_string (char *buf, size_t buflen, const char *string,
+               const char *needle, const char *replacement);
+
+#endif /* UTILS_SUBST_H */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+