Merge remote branch 'trenkel/st/python'
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Thu, 22 Apr 2010 08:45:58 +0000 (10:45 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Thu, 22 Apr 2010 08:45:58 +0000 (10:45 +0200)
85 files changed:
.mailmap
AUTHORS
ChangeLog
README
TODO
bindings/java/Makefile.am
bindings/perl/lib/Collectd.pm
configure.in
contrib/collection.cgi
contrib/exec-munin.px
contrib/exec-nagios.px
contrib/redhat/collectd.spec
contrib/snmp-probe-host.px
debian/README.Debian [deleted file]
debian/changelog [deleted file]
debian/collectd.conf [deleted file]
debian/collectd.init.d [deleted file]
debian/collectd.postrm [deleted file]
debian/compat [deleted file]
debian/control [deleted file]
debian/copyright [deleted file]
debian/examples/myplugin.c [deleted file]
debian/rules [deleted file]
src/Makefile.am
src/apache.c
src/collectd-perl.pod
src/collectd-python.pod
src/collectd.c
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.h
src/common.c
src/common.h
src/configfile.c
src/configfile.h
src/contextswitch.c
src/csv.c
src/curl.c
src/curl_json.c
src/curl_xml.c [new file with mode: 0644]
src/dbi.c
src/exec.c
src/filter_chain.c
src/java.c
src/logfile.c
src/match_regex.c
src/memcachec.c
src/memcached.c
src/modbus.c [new file with mode: 0644]
src/mysql.c
src/netapp.c
src/network.c
src/onewire.c
src/openvpn.c
src/oracle.c
src/owniptc/libip4tc.c
src/owniptc/libip6tc.c
src/owniptc/libiptc.c
src/ping.c
src/plugin.c
src/plugin.h
src/postgresql.c
src/processes.c
src/python.c
src/routeros.c
src/rrdtool.c
src/snmp.c
src/swap.c
src/tail.c
src/target_scale.c
src/types.db
src/types.db.pod
src/utils_cmd_listval.c
src/utils_db_query.c
src/utils_db_query.h
src/utils_format_json.c
src/utils_format_json.h
src/utils_llist.c
src/utils_llist.h
src/utils_match.c
src/utils_match.h
src/utils_tail_match.c
src/utils_tail_match.h
src/write_http.c
version-gen.sh

index 9bb4399..a39a1d2 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -5,4 +5,5 @@ Luboš Staněk <kolektor@atlas.cz>
 Luboš Staněk <lubek@users.sourceforge.net>
 Niki W. Waibel <niki>
 Sebastian Harl <tokkee>
+Rodolphe Quiedeville <rquiedeville@bearstech.com>
 
diff --git a/AUTHORS b/AUTHORS
index 4b133fa..67c75f0 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -23,6 +23,7 @@ Alvaro Barcellos <alvaro.barcellos at gmail.com>
 
 Amit Gupta <amit.gupta221 at gmail.com>
  - Multiple servers in the apache plugin.
+ - curl_xml plugin.
 
 Anthony Dewhurst <dewhurst at gmail.com>
  - zfs_arc plugin.
index 6275837..191892d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2010-01-14, Version 4.9.1
+       * Documentation: Some manpage fixes.
+       * Default config: Added sample configuration for missing plugins.
+       * apache plugin: Fix a segmentation fault in the config handling of
+         VerifyPeer / VerifyHost. Thanks to "plazmus" for his or her patch.
+       * processes plugin: Fix handling of derive data sources.
+       * rrdtool plugin: Fix a bug with random write timeouts. Due to an
+         incorrect initialization some files may be suspended basically
+         indefinitely. After flushing the files they were written regularly
+         again.
+       * routeros plugin: Use the node name for the "host" field.
+       * Monitorus.pm: Put the plugin into the "Collectd::Plugins" namespace.
+       * Perl bindings: Fix a warning that was printed when building
+         debugging output.
+
 2009-12-21, Version 4.9.0
        * contextswitch plugin: The new ContextSwitch plugin gathers the
          number of context switches done by the CPU. Thanks to Patrik
        * scale target: This target to scale (multiply) values by an arbitrary
          value has been added.
 
+2010-04-07, Version 4.8.4
+       * Build system, various plugins: Fixes for AIX compatibility have been
+         added. Thanks to Manuel Sanmartin for his patches.
+       * Build system: Checking for "nanosleep" on old Solaris machines has
+         been fixed. Thanks to Vincent McIntyre and Sebastian Harl for
+         figuring out a way to make this work.
+       * collectd: Serialization of NANs in JSON format has been fixed.
+         Thanks to Chris Buben for pointing out the resulting syntax error.
+       * collectd: Checks whether a "sleep" returned early have been added;
+         the cases are now handled correctly. Thanks to Michael Stapelberg
+         for the patch.
+       * collectd: Continue reading files in a directory when parsing one
+         file fails.
+       * apache plugin: Collection of the number of active connections has
+         been fixed for Apache 2.*.
+       * exec plugin: Error messages have been improved. The "running" flag
+         is now cleared correctly when forking a child fails.
+       * iptables plugin: Fix a violation of aliasing rules. This resolves a
+         warning / error with new GCC versions. Thanks to Jan Engelhardt for
+         the work-around.
+       * java plugin: The Java API files are now packaged into a .jar file.
+         Thanks to Amit Gupta for his patch.
+       * network plugin: A memory leak when receiving encrypted network
+         packets has been fixed.
+       * oracle plugin: Fix checking for lost connections and reconnect in
+         this case. Thanks to Sven Trenkel for pointing out the problem.
+       * unixsock plugin: A memory leak in the "LISTVAL" command has been
+         fixed. Thanks to Peter Warasin for pointing it out.
+       * write_http plugin: Use the "any" authentication schema. This used to
+         be "digest". Thanks to Paul Sadauskas for the patch.
+
+2010-01-14, Version 4.8.3
+       * Documentation: Some manpage fixes.
+       * rrdtool plugin: Fix a bug with random write timeouts. Due to an
+         incorrect initialization some files may be suspended basically
+         indefinitely. After flushing the files they were written regularly
+         again.
+
 2009-12-18, Version 4.8.2
        * Build system, java plugin: Don't use “find -L” to search for Java
          headers, because it's a GNU extension.
diff --git a/README b/README
index 96fbdf3..7d1a64a 100644 (file)
--- a/README
+++ b/README
@@ -57,6 +57,10 @@ Features
       Retrieves JSON data via cURL and parses it according to user
       configuration.
 
+    - curl_xml
+      Retrieves XML data via cURL and parses it according to user
+      configuration.
+
     - dbi
       Executes SQL statements on various databases and interprets the returned
       data.
@@ -143,6 +147,10 @@ Features
       Memory utilization: Memory occupied by running processes, page cache,
       buffer cache and free.
 
+    - modbus
+      Reads values from Modbus/TCP enabled devices. Supports reading values
+      from multiple "slaves" so gateway devices can be used.
+
     - multimeter
       Information provided by serial multimeters, such as the `Metex
       M-4650CR'.
@@ -293,7 +301,7 @@ Features
     - zfs_arc
       Statistics for ZFS' “Adaptive Replacement Cache” (ARC).
 
-  * Output can be written or send to various destinations by the following
+  * Output can be written or sent to various destinations by the following
     plugins:
 
     - csv
@@ -426,7 +434,7 @@ Features
     network plugins, makes sure your resources are used efficiently. Also,
     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.
+    one plugin 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.
@@ -537,11 +545,15 @@ Prerequisites
     Used by the `memcachec' plugin to connect to a memcache daemon.
     <http://tangent.org/552/libmemcached.html>
 
+  * libmodbus (optional)
+    Used by the `modbus' plugin to communicate with Modbus/TCP devices.
+    <https://launchpad.net/libmodbus>
+
   * libmysqlclient (optional)
     Unsurprisingly used by the `mysql' plugin.
     <http://dev.mysql.com/>
 
-  * libnatapp (optional)
+  * libnetapp (optional)
     Required for the “netapp” plugin.
     This library is part of the “Manage ONTAP SDK” published by NetApp.
 
diff --git a/TODO b/TODO
index 3c8f49e..009eb7f 100644 (file)
--- a/TODO
+++ b/TODO
@@ -5,9 +5,6 @@
 src/battery.c: commend not working code.
 
 Wishlist:
-* Update the RPM specfile to
-  - build `collectd-apache'
-  - be free of syntax errors.
 * Port nfs module to solaris
 * Port tape module to Linux
 * Port the apple_sensors plugin to Linux/PPC.
index d3315ac..fa253f6 100644 (file)
@@ -27,19 +27,22 @@ EXTRA_DIST = org/collectd/api/CollectdConfigInterface.java \
 java-build-stamp: org/collectd/api/*.java
        $(JAVAC) -d "." "$(srcdir)/org/collectd/api"/*.java
        $(JAVAC) -d "." "$(srcdir)/org/collectd/java"/*.java
+       mkdir -p .libs
+       $(JAR) cf .libs/collectd-api.jar "org/collectd/api"/*.class
+       $(JAR) cf .libs/generic-jmx.jar "org/collectd/java"/*.class
        touch "$@"
 
 all-local: java-build-stamp
 
 install-exec-local: java-build-stamp
-       mkdir -p "$(DESTDIR)$(pkgdatadir)/java/org/collectd/api"
-       $(INSTALL) -m 644 "org/collectd/api"/*.class \
-               "$(DESTDIR)$(pkgdatadir)/java/org/collectd/api/"
-       mkdir -p "$(DESTDIR)$(pkgdatadir)/java/org/collectd/java"
-       $(INSTALL) -m 644 "org/collectd/java"/*.class \
-               "$(DESTDIR)$(pkgdatadir)/java/org/collectd/java/"
+       mkdir -p "$(DESTDIR)$(pkgdatadir)/java"
+       $(INSTALL) -m 644 .libs/collectd-api.jar \
+               "$(DESTDIR)$(pkgdatadir)/java"
+       $(INSTALL) -m 644 .libs/generic-jmx.jar \
+               "$(DESTDIR)$(pkgdatadir)/java"
 
 clean-local:
        rm -f "org/collectd/api"/*.class
        rm -f "org/collectd/java"/*.class
+       rm -f .libs
        rm -f "java-build-stamp"
index ef2f3e5..f1b5d85 100644 (file)
@@ -116,6 +116,7 @@ my @fc_plugins : shared = ();
 my %cf_callbacks : shared = ();
 
 my %types = (
+       TYPE_CONFIG,   "config",
        TYPE_INIT,     "init",
        TYPE_READ,     "read",
        TYPE_WRITE,    "write",
index bb7b535..3224526 100644 (file)
@@ -477,7 +477,7 @@ AC_HEADER_TIME
 # Checks for library functions.
 #
 AC_PROG_GCC_TRADITIONAL
-AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf)
+AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf if_indextoname)
 
 AC_FUNC_STRERROR_R
 
@@ -550,8 +550,16 @@ AC_CHECK_FUNCS(socket, [], AC_CHECK_LIB(socket, socket, [socket_needs_socket="ye
 AM_CONDITIONAL(BUILD_WITH_LIBSOCKET, test "x$socket_needs_socket" = "xyes")
 
 nanosleep_needs_rt="no"
-AC_CHECK_FUNCS(nanosleep, [], AC_CHECK_LIB(rt, nanosleep, [nanosleep_needs_rt="yes"], AC_MSG_ERROR(cannot find nanosleep)))
+nanosleep_needs_posix4="no"
+AC_CHECK_FUNCS(nanosleep,
+    [],
+    AC_CHECK_LIB(rt, nanosleep,
+        [nanosleep_needs_rt="yes"],
+        AC_CHECK_LIB(posix4, nanosleep,
+            [nanosleep_needs_posix4="yes"],
+            AC_MSG_ERROR(cannot find nanosleep))))
 AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes")
+AM_CONDITIONAL(BUILD_WITH_LIBPOSIX4, test "x$nanosleep_needs_posix4" = "xyes")
 
 AC_CHECK_FUNCS(sysctl, [have_sysctl="yes"], [have_sysctl="no"])
 AC_CHECK_FUNCS(sysctlbyname, [have_sysctlbyname="yes"], [have_sysctlbyname="no"])
@@ -931,6 +939,27 @@ if test "x$have_getmntent" = "xgen"; then
                  [Define if the function getmntent exists. It's the version from libgen.])
 fi
 
+# Check for htonll
+AC_MSG_CHECKING([if have htonll defined])
+
+    have_htonll="no"
+    AC_RUN_IFELSE([
+       AC_LANG_PROGRAM([
+#include <sys/types.h>
+#include <netinet/in.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h> 
+#endif
+       ], [
+          return htonll(0);
+       ])
+    ], [
+      have_htonll="yes"
+      AC_DEFINE(HAVE_HTONLL, 1, [Define if the function htonll exists.])
+    ])
+AC_MSG_RESULT([$have_htonll])
+
 # Check for structures
 AC_CHECK_MEMBERS([struct if_data.ifi_ibytes, struct if_data.ifi_opackets, struct if_data.ifi_ierrors],
        [AC_DEFINE(HAVE_STRUCT_IF_DATA, 1, [Define if struct if_data exists and is usable.])],
@@ -950,6 +979,13 @@ AC_CHECK_MEMBERS([struct net_device_stats.rx_bytes, struct net_device_stats.tx_p
        #include <linux/netdevice.h>
        ])
 
+AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [],
+       [],
+       [
+       #include <netinet/in.h>
+       #include <net/if.h>
+       ])
+
 AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct kinfo_proc.ki_rusage],
        [
                AC_DEFINE(HAVE_STRUCT_KINFO_PROC_FREEBSD, 1,
@@ -1611,6 +1647,7 @@ with_java_vmtype="client"
 with_java_cflags=""
 with_java_libs=""
 JAVAC="$JAVAC"
+JAR="$JAR"
 AC_ARG_WITH(java, [AS_HELP_STRING([--with-java@<:@=PREFIX@:>@], [Path to Java home.])],
 [
        if test "x$withval" = "xno"
@@ -1671,6 +1708,18 @@ then
                                AC_MSG_RESULT([not found])
                        fi
                fi
+               if test "x$JAR" = "x"
+               then
+                       AC_MSG_CHECKING([for jar])
+                       TMPDIR=`find "$with_java_home" -name jar -type f | head -n 1`
+                       if test "x$TMPDIR" != "x"
+                       then
+                               JAR="$TMPDIR"
+                               AC_MSG_RESULT([$JAR])
+                       else
+                               AC_MSG_RESULT([not found])
+                       fi
+               fi
        else if test "x$with_java_home" != "x"
        then
                AC_MSG_WARN([JAVA_HOME: No such directory: $with_java_home])
@@ -1707,6 +1756,24 @@ if test "x$JAVAC" = "x"
 then
        with_java="no (javac not found)"
 fi
+if test "x$JAR" = "x"
+then
+       with_jar_path="$PATH"
+       if test "x$with_java_home" != "x"
+       then
+               with_jar_path="$with_java_home:$with_jar_path"
+               if test -d "$with_java_home/bin"
+               then
+                       with_jar_path="$with_java_home/bin:$with_jar_path"
+               fi
+       fi
+
+       AC_PATH_PROG(JAR, jar, [], "$with_jar_path")
+fi
+if test "x$JAR" = "x"
+then
+       with_java="no (jar not found)"
+fi
 
 SAVE_CPPFLAGS="$CPPFLAGS"
 SAVE_CFLAGS="$CFLAGS"
@@ -1794,6 +1861,98 @@ fi
 AM_CONDITIONAL(BUILD_WITH_LIBMEMCACHED, test "x$with_libmemcached" = "xyes")
 # }}}
 
+# --with-libmodbus {{{
+with_libmodbus_config=""
+with_libmodbus_cflags=""
+with_libmodbus_libs=""
+AC_ARG_WITH(libmodbus, [AS_HELP_STRING([--with-libmodbus@<:@=PREFIX@:>@], [Path to the modbus library.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libmodbus="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libmodbus="use_pkgconfig"
+       else if test -d "$with_libmodbus/lib"
+       then
+               AC_MSG_NOTICE([Not checking for libmodbus: Manually configured])
+               with_libmodbus_cflags="-I$withval/include"
+               with_libmodbus_libs="-L$withval/lib -lmodbus"
+               with_libmodbus="yes"
+       fi; fi; fi
+],
+[with_libmodbus="use_pkgconfig"])
+
+# configure using pkg-config
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+       if test "x$PKG_CONFIG" = "x"
+       then
+               with_libmodbus="no (Don't have pkg-config)"
+       fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+       AC_MSG_NOTICE([Checking for modbus using $PKG_CONFIG])
+       $PKG_CONFIG --exists 'modbus' 2>/dev/null
+       if test $? -ne 0
+       then
+               with_libmodbus="no (pkg-config doesn't know library)"
+       fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+       with_libmodbus_cflags="`$PKG_CONFIG --cflags 'modbus'`"
+       if test $? -ne 0
+       then
+               with_libmodbus="no ($PKG_CONFIG failed)"
+       fi
+       with_libmodbus_libs="`$PKG_CONFIG --libs 'modbus'`"
+       if test $? -ne 0
+       then
+               with_libmodbus="no ($PKG_CONFIG failed)"
+       fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+       with_libmodbus="yes"
+fi
+
+# with_libmodbus_cflags and with_libmodbus_libs are set up now, let's do
+# the actual checks.
+if test "x$with_libmodbus" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
+
+       AC_CHECK_HEADERS(modbus/modbus.h, [], [with_libmodbus="no (modbus/modbus.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libmodbus" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
+       LDFLAGS="$LDFLAGS $with_libmodbus_libs"
+
+       AC_CHECK_LIB(modbus, modbus_init_tcp,
+                    [with_libmodbus="yes"],
+                    [with_libmodbus="no (symbol modbus_init_tcp not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libmodbus" = "xyes"
+then
+       BUILD_WITH_LIBMODBUS_CFLAGS="$with_libmodbus_cflags"
+       BUILD_WITH_LIBMODBUS_LIBS="$with_libmodbus_libs"
+       AC_SUBST(BUILD_WITH_LIBMODBUS_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBMODBUS_LIBS)
+fi
+# }}}
+
 # --with-libmysql {{{
 with_mysql_config="mysql_config"
 with_mysql_cflags=""
@@ -3737,7 +3896,7 @@ AC_DEFUN(
             enable_plugin="yes"
             force="yes"
      else
-            enable_plugin="no"
+            enable_plugin="no (disabled on command line)"
      fi; fi
     ],
     [
@@ -3790,6 +3949,7 @@ plugin_contextswitch="no"
 plugin_cpu="no"
 plugin_cpufreq="no"
 plugin_curl_json="no"
+plugin_curl_xml="no"
 plugin_df="no"
 plugin_disk="no"
 plugin_entropy="no"
@@ -3929,6 +4089,11 @@ then
        plugin_curl_json="yes"
 fi
 
+if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
+then
+       plugin_curl_xml="yes"
+fi
+
 if test "x$have_processor_info" = "xyes"
 then
        plugin_cpu="yes"
@@ -4082,6 +4247,7 @@ AC_PLUGIN([cpu],         [$plugin_cpu],        [CPU usage statistics])
 AC_PLUGIN([csv],         [yes],                [CSV output plugin])
 AC_PLUGIN([curl],        [$with_libcurl],      [CURL generic web statistics])
 AC_PLUGIN([curl_json],   [$plugin_curl_json],    [CouchDB statistics])
+AC_PLUGIN([curl_xml],   [$plugin_curl_xml],    [CURL generic xml statistics])
 AC_PLUGIN([dbi],         [$with_libdbi],       [General database statistics])
 AC_PLUGIN([df],          [$plugin_df],         [Filesystem usage statistics])
 AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
@@ -4112,6 +4278,7 @@ AC_PLUGIN([mbmon],       [yes],                [Query mbmond])
 AC_PLUGIN([memcachec],   [$with_libmemcached], [memcachec statistics])
 AC_PLUGIN([memcached],   [yes],                [memcached statistics])
 AC_PLUGIN([memory],      [$plugin_memory],     [Memory usage])
+AC_PLUGIN([modbus],      [$with_libmodbus],    [Modbus plugin])
 AC_PLUGIN([multimeter],  [$plugin_multimeter], [Read multimeter values])
 AC_PLUGIN([mysql],       [$with_libmysql],     [MySQL statistics])
 AC_PLUGIN([netapp],      [$with_libnetapp],    [NetApp plugin])
@@ -4345,6 +4512,7 @@ Configuration:
     libkstat  . . . . . . $with_kstat
     libkvm  . . . . . . . $with_libkvm
     libmemcached  . . . . $with_libmemcached
+    libmodbus . . . . . . $with_libmodbus
     libmysql  . . . . . . $with_libmysql
     libnetapp . . . . . . $with_libnetapp
     libnetlink  . . . . . $with_libnetlink
@@ -4392,6 +4560,7 @@ Configuration:
     csv . . . . . . . . . $enable_csv
     curl  . . . . . . . . $enable_curl
     curl_json . . . . . . $enable_curl_json
+    curl_xml  . . . . . . $enable_curl_xml
     dbi . . . . . . . . . $enable_dbi
     df  . . . . . . . . . $enable_df
     disk  . . . . . . . . $enable_disk
@@ -4422,6 +4591,7 @@ Configuration:
     memcachec . . . . . . $enable_memcachec
     memcached . . . . . . $enable_memcached
     memory  . . . . . . . $enable_memory
+    modbus  . . . . . . . $enable_modbus
     multimeter  . . . . . $enable_multimeter
     mysql . . . . . . . . $enable_mysql
     netapp  . . . . . . . $enable_netapp
index cbdb76e..af64fb1 100755 (executable)
@@ -13,6 +13,7 @@ use Data::Dumper ();
 
 our $Config = "/etc/collection.conf";
 our @DataDirs = ();
+our @DontShowTypes = ();
 our $LibDir;
 
 our $ValidTimespan =
@@ -78,6 +79,10 @@ sub read_config
                        $value =~ s#/*$##;
                        $LibDir = $value;
                }
+               elsif ($key eq 'dontshowtype')
+               {
+                 push (@DontShowTypes, $value);
+               }
                else
                {
                        print STDERR "Unknown key: $key\n";
@@ -239,6 +244,7 @@ sub _find_types
       my $name = "$_";
       $name =~ s/\.rrd$//i;
       my ($type, $instance) = split (m/-/, $name, 2);
+      if (grep { $_ eq $type } @DontShowTypes) { next; }
       $types{$type} = [] if (!$types{$type});
       push (@{$types{$type}}, $instance) if (defined ($instance));
     }
@@ -970,6 +976,28 @@ sub load_graph_definitions
     'GPRINT:avg:LAST:%5.1lf%s Last',
     'GPRINT:avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
     ],
+   apache_connections => ['DEF:min={file}:count:MIN',
+    'DEF:avg={file}:count:AVERAGE',
+    'DEF:max={file}:count:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Connections",
+    'GPRINT:min:MIN:%6.2lf Min,',
+    'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+    'GPRINT:max:MAX:%6.2lf Max,',
+    'GPRINT:avg:LAST:%6.2lf Last'
+    ],
+    apache_idle_workers => ['DEF:min={file}:count:MIN',
+    'DEF:avg={file}:count:AVERAGE',
+    'DEF:max={file}:count:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Idle Workers",
+    'GPRINT:min:MIN:%6.2lf Min,',
+    'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+    'GPRINT:max:MAX:%6.2lf Max,',
+    'GPRINT:avg:LAST:%6.2lf Last'
+    ],
     apache_requests => ['DEF:min={file}:count:MIN',
     'DEF:avg={file}:count:AVERAGE',
     'DEF:max={file}:count:MAX',
index 9163730..907ea9b 100755 (executable)
@@ -1,5 +1,26 @@
 #!/usr/bin/perl
 
+#
+# collectd - contrib/exec-munin.px
+# Copyright (C) 2007,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 octo Forster <octo at verplant.org>
+#
+
 use strict;
 use warnings;
 
index a9f4663..938721f 100755 (executable)
@@ -88,6 +88,8 @@ with the C<exec-plugin>).
 
 =back
 
+=back
+
 =cut
 
 sub handle_config_addtype
@@ -293,7 +295,7 @@ sub execute_script
       
       if ($perfdata)
       {
-       push (@serviceperfdata, split (' ', $perfdata));
+        push (@serviceperfdata, split (' ', $perfdata));
       }
 
       $state = 1;
@@ -308,8 +310,8 @@ sub execute_script
 
       if ($perfdata)
       {
-       push (@serviceperfdata, split (' ', $perfdata));
-       $state = 2;
+        push (@serviceperfdata, split (' ', $perfdata));
+        $state = 2;
       }
     }
     else # ($state == 2)
@@ -347,7 +349,7 @@ sub execute_script
     for (@serviceperfdata)
     {
       handle_performance_data ($host, 'nagios', $pinst, $script->{'type'},
-       $time, $_);
+        $time, $_);
     }
   }
 } # execute_script
index 012fc48..4617f6c 100644 (file)
@@ -1,12 +1,17 @@
+
+%define with_java %(test -z "$JAVA_HOME" ; echo $?)
+
 Summary:       Statistics collection daemon for filling RRD files.
 Name:          collectd
-Version:       4.3.1
-Release:       0.centos5
+Version:       4.9.0
+Release:       1%{?dist}
 Source:                http://collectd.org/files/%{name}-%{version}.tar.gz
 License:       GPL
 Group:         System Environment/Daemons
 BuildRoot:     %{_tmppath}/%{name}-%{version}-root
-BuildPrereq:   lm_sensors-devel, mysql-devel, rrdtool-devel, curl-devel, libpcap-devel, net-snmp-devel, libstatgrab-devel, mysql-devel, libxml2-devel, libiptcdata-devel
+BuildPrereq:   lm_sensors-devel, rrdtool-devel, libpcap-devel, net-snmp-devel, libstatgrab-devel, libxml2-devel, libiptcdata-devel
+# libcurl deps
+BuildPrereq:   curl-devel,libidn-devel,openssl-devel
 Requires:      rrdtool, perl-Regexp-Common, libstatgrab
 Packager:      RightScale <support@rightscale.com>
 Vendor:                collectd development team <collectd@verplant.org>
@@ -19,6 +24,7 @@ every time it wants to update the values it's very fast and easy on the
 system. Also, the statistics are very fine grained since the files are updated
 every 10 seconds.
 
+
 %package apache
 Summary:       apache-plugin for collectd.
 Group:         System Environment/Daemons
@@ -62,12 +68,25 @@ Requires:   collectd = %{version}, net-snmp
 %description snmp
 This plugin for collectd allows querying of network equipment using SNMP.
 
+%if %with_java
+%package java
+Summary:       java-module for collectd.
+Group:         System Environment/Daemons
+Requires:      collectd = %{version}, jdk >= 1.6
+BuildPrereq:   jdk >= 1.6
+%description java
+This plugin for collectd allows plugins to be written in Java and executed
+in an embedded JVM.
+%endif
+
 %prep
 rm -rf $RPM_BUILD_ROOT
 %setup
 
 %build
-./configure CFLAGS=-"DLT_LAZY_OR_NOW='RTLD_LAZY|RTLD_GLOBAL'" --prefix=%{_prefix} --sbindir=%{_sbindir} --mandir=%{_mandir} --libdir=%{_libdir} --sysconfdir=%{_sysconfdir} --enable-apache --enable-email --enable-mysql --enable-dns
+./configure CFLAGS=-"DLT_LAZY_OR_NOW='RTLD_LAZY|RTLD_GLOBAL'" --prefix=%{_prefix} --sbindir=%{_sbindir} --mandir=%{_mandir} --libdir=%{_libdir} --sysconfdir=%{_sysconfdir} \
+    %{!?with_java:"--with-java=$JAVA_HOME --enable-java"} \
+    --disable-battery
 make
 
 %install
@@ -80,14 +99,17 @@ mkdir -p $RPM_BUILD_ROOT/etc/collectd.d
 mkdir -p $RPM_BUILD_ROOT/var/lib/collectd
 ### Clean up docs
 find contrib/ -type f -exec %{__chmod} a-x {} \;
+
 ###Modify Config for Redhat Based Distros
-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:#PluginDir   "/usr/lib/collectd":PluginDir   "%{_libdir}/collectd":' $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:#Interval     10:Interval     30:' $RPM_BUILD_ROOT/etc/collectd.conf
 sed -i 's:#ReadThreads  5:ReadThreads  5:' $RPM_BUILD_ROOT/etc/collectd.conf
+###Include broken out config directory
+echo -e '\nInclude "/etc/collectd.d"' >> $RPM_BUILD_ROOT/etc/collectd.conf
+
 ##Move config contribs
 cp contrib/redhat/apache.conf $RPM_BUILD_ROOT/etc/collectd.d/apache.conf
 cp contrib/redhat/email.conf $RPM_BUILD_ROOT/etc/collectd.d/email.conf
@@ -120,7 +142,7 @@ exit 0
 %files
 %defattr(-,root,root)
 %doc AUTHORS COPYING ChangeLog INSTALL NEWS README contrib/
-%attr(0644,root,root) %config(noreplace) /etc/collectd.conf
+%config %attr(0644,root,root) /etc/collectd.conf
 %attr(0755,root,root) /etc/rc.d/init.d/collectd
 %attr(0755,root,root) /var/www/cgi-bin/collection.cgi
 %attr(0755,root,root) %{_sbindir}/collectd
@@ -130,167 +152,142 @@ exit 0
 %attr(0644,root,root) %{_mandir}/man5/*
 %dir /etc/collectd.d
 
-%attr(0644,root,root) %{_libdir}/%{name}/apcups.so*
-%attr(0644,root,root) %{_libdir}/%{name}/apcups.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/apple_sensors.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/apple_sensors.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/battery.so*
-%attr(0644,root,root) %{_libdir}/%{name}/battery.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/conntrack.so*
-%attr(0644,root,root) %{_libdir}/%{name}/conntrack.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/cpufreq.so*
-%attr(0644,root,root) %{_libdir}/%{name}/cpufreq.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/cpu.so*
-%attr(0644,root,root) %{_libdir}/%{name}/cpu.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/csv.so*
-%attr(0644,root,root) %{_libdir}/%{name}/csv.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/df.so*
-%attr(0644,root,root) %{_libdir}/%{name}/df.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/disk.so*
-%attr(0644,root,root) %{_libdir}/%{name}/disk.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/dns.so*
-%attr(0644,root,root) %{_libdir}/%{name}/dns.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/entropy.so*
-%attr(0644,root,root) %{_libdir}/%{name}/entropy.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/exec.so*
-%attr(0644,root,root) %{_libdir}/%{name}/exec.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/hddtemp.so*
-%attr(0644,root,root) %{_libdir}/%{name}/hddtemp.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/interface.so*
-%attr(0644,root,root) %{_libdir}/%{name}/interface.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/iptables.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/iptables.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/irq.so*
-%attr(0644,root,root) %{_libdir}/%{name}/irq.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/load.so*
-%attr(0644,root,root) %{_libdir}/%{name}/load.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/logfile.so*
-%attr(0644,root,root) %{_libdir}/%{name}/logfile.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/mbmon.so
-%attr(0644,root,root) %{_libdir}/%{name}/mbmon.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/memcached.so*
-%attr(0644,root,root) %{_libdir}/%{name}/memcached.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/memory.so*
-%attr(0644,root,root) %{_libdir}/%{name}/memory.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/multimeter.so*
-%attr(0644,root,root) %{_libdir}/%{name}/multimeter.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/network.so*
-%attr(0644,root,root) %{_libdir}/%{name}/network.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/nfs.so*
-%attr(0644,root,root) %{_libdir}/%{name}/nfs.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/ntpd.so*
-%attr(0644,root,root) %{_libdir}/%{name}/ntpd.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/nut.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/nut.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/perl.so*
-%attr(0644,root,root) %{_libdir}/%{name}/perl.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/ping.so*
-%attr(0644,root,root) %{_libdir}/%{name}/ping.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/processes.so*
-%attr(0644,root,root) %{_libdir}/%{name}/processes.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/rrdtool.so*
-%attr(0644,root,root) %{_libdir}/%{name}/rrdtool.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/serial.so*
-%attr(0644,root,root) %{_libdir}/%{name}/serial.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/swap.so*
-%attr(0644,root,root) %{_libdir}/%{name}/swap.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/syslog.so*
-%attr(0644,root,root) %{_libdir}/%{name}/syslog.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/tape.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/tape.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/tcpconns.so*
-%attr(0644,root,root) %{_libdir}/%{name}/tcpconns.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/unixsock.so*
-%attr(0644,root,root) %{_libdir}/%{name}/unixsock.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/users.so*
-%attr(0644,root,root) %{_libdir}/%{name}/users.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/uuid.so*
-%attr(0644,root,root) %{_libdir}/%{name}/uuid.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/vserver.so*
-%attr(0644,root,root) %{_libdir}/%{name}/vserver.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/wireless.so*
-%attr(0644,root,root) %{_libdir}/%{name}/wireless.la
+# client
+%attr(0644,root,root) /usr/include/collectd/client.h
+%attr(0644,root,root) /usr/include/collectd/lcc_features.h
+
+%attr(0644,root,root) %{_libdir}/libcollectdclient.*
+%attr(0644,root,root) %{_libdir}/pkgconfig/libcollectdclient.pc
+
+# macro to grab binaries for a plugin, given a name
+%define plugin_macro() \
+%attr(0644,root,root) %{_libdir}/%{name}/%1.a \
+%attr(0644,root,root) %{_libdir}/%{name}/%1.so* \
+%attr(0644,root,root) %{_libdir}/%{name}/%1.la
+
+%plugin_macro apcups
+%plugin_macro ascent
+%plugin_macro bind
+%plugin_macro conntrack
+%plugin_macro contextswitch
+%plugin_macro cpufreq
+%plugin_macro cpu
+%plugin_macro csv
+%plugin_macro curl
+%plugin_macro df
+%plugin_macro disk
+%plugin_macro dns
+%plugin_macro entropy
+%plugin_macro email
+%plugin_macro exec
+%plugin_macro filecount
+%plugin_macro fscache
+%plugin_macro hddtemp
+%plugin_macro interface
+%plugin_macro iptables
+%plugin_macro irq
+%plugin_macro load
+%plugin_macro logfile
+%plugin_macro madwifi
+
+%plugin_macro match_empty_counter
+%plugin_macro match_hashed
+%plugin_macro match_regex
+%plugin_macro match_timediff
+%plugin_macro match_value
+
+%plugin_macro mbmon
+%plugin_macro memcached
+%plugin_macro memory
+%plugin_macro multimeter
+%plugin_macro network
+%plugin_macro nfs
+%plugin_macro ntpd
+%plugin_macro openvpn
+%plugin_macro olsrd
+%plugin_macro perl
+%plugin_macro powerdns
+%plugin_macro processes
+%plugin_macro protocols
+%plugin_macro python
+%plugin_macro rrdtool
+%plugin_macro serial
+%plugin_macro sensors
+%plugin_macro swap
+%plugin_macro syslog
+%plugin_macro table
+%plugin_macro tail
+
+%plugin_macro target_notification
+%plugin_macro target_replace
+%plugin_macro target_scale
+%plugin_macro target_set
+
+%plugin_macro tcpconns
+%plugin_macro teamspeak2
+%plugin_macro ted
+%plugin_macro thermal
+%plugin_macro unixsock
+%plugin_macro uptime
+%plugin_macro users
+%plugin_macro uuid
+%plugin_macro vmem
+%plugin_macro vserver
+%plugin_macro wireless
+%plugin_macro write_http
 
 %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
-%attr(0644,root,root) %{_libdir}/perl5/site_perl/5.8.8/Collectd/Unixsock.pm
 %attr(0644,root,root) %{_libdir}/perl5/site_perl/5.8.8/%{_arch}-linux-thread-multi/auto/Collectd/.packlist
+%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd.pm
+%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd/Unixsock.pm
+%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd/Plugins/OpenVZ.pm
 %attr(0644,root,root) /usr/share/man/man3/Collectd::Unixsock.3pm.gz
 
+%exclude /usr/share/collectd/postgresql_default.conf
+
 %dir /var/lib/collectd
 
+%if %with_java
+%files java
+%attr(0644,root,root) /usr/share/%{name}/java/org/collectd/api/*.class
+%attr(0644,root,root) /usr/share/%{name}/java/org/collectd/java/*.class
+%plugin_macro java
+%endif
+
 %files apache
-%attr(0644,root,root) %{_libdir}/%{name}/apache.so*
-%attr(0644,root,root) %{_libdir}/%{name}/apache.la
-%attr(0644,root,root) /etc/collectd.d/apache.conf
+%config %attr(0644,root,root) /etc/collectd.d/apache.conf
+%plugin_macro apache
 
 %files email
 %attr(0644,root,root) %{_libdir}/%{name}/email.so*
 %attr(0644,root,root) %{_libdir}/%{name}/email.la
-%attr(0644,root,root) /etc/collectd.d/email.conf
+%config %attr(0644,root,root) /etc/collectd.d/email.conf
 
 %files mysql
-%attr(0644,root,root) %{_libdir}/%{name}/mysql.so*
-%attr(0644,root,root) %{_libdir}/%{name}/mysql.la
-%attr(0644,root,root) /etc/collectd.d/mysql.conf
+%config %attr(0644,root,root) /etc/collectd.d/mysql.conf
+%plugin_macro mysql
 
 %files nginx
-%attr(0644,root,root) %{_libdir}/%{name}/nginx.so*
-%attr(0644,root,root) %{_libdir}/%{name}/nginx.la
-%attr(0644,root,root) /etc/collectd.d/nginx.conf
+%config %attr(0644,root,root) /etc/collectd.d/nginx.conf
+%plugin_macro nginx
 
 %files sensors
 %attr(0644,root,root) %{_libdir}/%{name}/sensors.so*
 %attr(0644,root,root) %{_libdir}/%{name}/sensors.la
-%attr(0644,root,root) /etc/collectd.d/sensors.conf
+%config %attr(0644,root,root) /etc/collectd.d/sensors.conf
 
 %files snmp
-%attr(0644,root,root) %{_libdir}/%{name}/snmp.so*
-%attr(0644,root,root) %{_libdir}/%{name}/snmp.la
 %attr(0644,root,root) /etc/collectd.d/snmp.conf
+%plugin_macro snmp
 
 %changelog
+* Tue Jan 04 2010 Rackspace <stu.hood@rackspace.com> 4.9.0
+- New upstream version
+- Changes to support 4.9.0
+- Added support for Java/GenericJMX plugin
+
 * Mon Mar 17 2008 RightScale <support@rightscale.com> 4.3.1
 - New upstream version
 - Changes to support 4.3.1
index 1d8f975..d1a7a88 100755 (executable)
@@ -1,7 +1,8 @@
 #!/usr/bin/perl
 #
 # collectd - snmp-probe-host.px
-# Copyright (C) 2008  Florian octo Forster
+# Copyright (C) 2008,2009  Florian octo Forster
+# Copyright (C) 2009       noris network AG
 #
 # 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
@@ -27,6 +28,12 @@ use Config::General ('ParseConfig');
 use Getopt::Long ('GetOptions');
 use Socket6;
 
+our %ExcludeOptions =
+(
+  'IF-MIB64' => qr/^\.?1\.3\.6\.1\.2\.1\.31/,
+  'IF-MIB32' => qr/^\.?1\.3\.6\.1\.2\.1\.2/
+);
+
 sub get_config
 {
   my %conf;
@@ -50,6 +57,7 @@ sub probe_one
 {
   my $sess = shift;
   my $conf = shift;
+  my $excludes = @_ ? shift : [];
   my @oids;
   my $cmd = 'GET';
   my $vl;
@@ -90,6 +98,14 @@ sub probe_one
       $oid_orig = $tmp;
     }
 
+    for (@$excludes)
+    {
+      if ($oid_orig =~ $_)
+      {
+        return;
+      }
+    }
+
     $vb = SNMP::Varbind->new ([$oid_orig]);
 
     if ($cmd eq 'GET')
@@ -148,6 +164,7 @@ sub probe_all
   my $host = shift;
   my $community = shift;
   my $data = shift;
+  my $excludes = @_ ? shift : [];
   my $version = 2;
   my @valid_data = ();
   my $begin;
@@ -203,7 +220,7 @@ sub probe_all
     for (keys %$data)
     {
       my $name = $_;
-      if (probe_one ($sess, $data->{$name}))
+      if (probe_one ($sess, $data->{$name}, $excludes))
       {
         push (@valid_data, $name);
       }
@@ -258,11 +275,24 @@ Options are:
   -C | --config        Path to config file holding the SNMP data blocks.
   -c | --community     SNMP community to use. Default: `public'.
   -h | --help          Print this information and exit.
+  -x | --exclude       Exclude a specific MIB. Call with "help" for more
+                       information.
 
 USAGE
   exit (1);
 }
 
+sub exit_usage_exclude
+{
+  print "Available exclude MIBs:\n\n";
+  for (sort (keys %ExcludeOptions))
+  {
+    print "  $_\n";
+  }
+  print "\n";
+  exit (1);
+}
+
 =head1 NAME
 
 snmp-probe-host.px - Find out what information an SNMP device provides.
@@ -294,6 +324,7 @@ my $file = '/etc/collectd/collectd.conf';
 my $community = 'public';
 my $conf;
 my $working_data;
+my @excludes = ();
 
 =head1 OPTIONS
 
@@ -318,6 +349,19 @@ Defaults to F</etc/collectd/collectd.conf>.
 
 SNMP community to use. Should be pretty straight forward.
 
+=item B<--exclude> I<MIB>
+
+This option can be used to exclude specific data from being enabled in the
+generated config. Currently the following MIBs are understood:
+
+=over 4
+
+=item B<IF-MIB>
+
+Exclude interface information, such as I<ifOctets> and I<ifPackets>.
+
+=back
+
 =back
 
 =cut
@@ -325,6 +369,7 @@ SNMP community to use. Should be pretty straight forward.
 GetOptions ('H|host|hostname=s' => \$host,
   'C|conf|config=s' => \$file,
   'c|community=s' => \$community,
+  'x|exclude=s' => \@excludes,
   'h|help' => \&exit_usage) or die;
 
 if (!$host)
@@ -333,6 +378,28 @@ if (!$host)
   exit (1);
 }
 
+if (@excludes)
+{
+  my $tmp = join (',', @excludes);
+  my @tmp = split (/\s*,\s*/, $tmp);
+
+  @excludes = ();
+  for (@tmp)
+  {
+    my $mib = uc ($_);
+    if ($mib eq 'HELP')
+    {
+      exit_usage_exclude ();
+    }
+    elsif (!exists ($ExcludeOptions{$mib}))
+    {
+      print STDERR "No such MIB: $mib\n";
+      exit_usage_exclude ();
+    }
+    push (@excludes, $ExcludeOptions{$mib});
+  }
+}
+
 $conf = get_config ($file) or die ("Cannot read config");
 
 if (!defined ($conf->{'plugin'})
@@ -343,7 +410,7 @@ if (!defined ($conf->{'plugin'})
   exit (1);
 }
 
-probe_all ($host, $community, $conf->{'plugin'}{'snmp'}{'data'});
+probe_all ($host, $community, $conf->{'plugin'}{'snmp'}{'data'}, \@excludes);
 
 exit (0);
 
diff --git a/debian/README.Debian b/debian/README.Debian
deleted file mode 100644 (file)
index b0b701b..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-collectd on Debian
-==================
-
-General notes:
---------------
-
-- This package is split up into several packages to prevent you from having to
-  install dependencies (or recommended packages) that you don't actually need.
-  Any plugin that has dependencies other than libc gets its own package.
-
-Configuring collectd:
----------------------
-
-- collectd uses a similar configuration layout as openvpn does. That is to
-  say that one daemon process is started for each configuration file found in
-  /etc/collectd/.
-
-- See collectd.conf(5) for details about configuring collectd.
-
-Building your own plugins:
---------------------------
-
-- If you want to contribute plugins to the official distribution you should
-  read http://collectd.org/dev-info.shtml.
-
-- If you want to build plugins for your personal use only simply install the
-  collectd-dev package and use /usr/share/doc/collectd-dev/examples/myplugin.c
-  as a starting point (Note: This is already a working example, though it does
-  not collect any useful data).
-  
-  The resulting file can be compiled as follows:
-
-    gcc -shared -o myplugin.so myplugin.c
-
-  Copy myplugin.so to /usr/lib/collectd and add the following line to your
-  collectd config file:
-
-    LoadPlugin myplugin
-
-  Restart collectd and you're done.
-
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644 (file)
index 14e450b..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-collectd (3.11.5-0octo1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Florian Forster <octo@leeloo.home.verplant.org>  Tue, 29 May 2007 22:50:59 +0200
-
-collectd (3.11.4-0octo1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Florian Forster <octo@leeloo.home.verplant.org>  Tue, 10 Apr 2007 17:18:11 +0200
-
-collectd (3.11.3-0octo1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Florian octo Forster <octo@alice.lan.home.verplant.org>  Fri, 30 Mar 2007 22:08:40 +0200
-
-collectd (3.11.0-0octo1) unstable; urgency=low
-
-  * New upstream release.
-  * The package collectd-mbmon has been added.
-
- -- Florian octo Forster <octo@alice.lan.home.verplant.org>  Fri, 22 Dec 2006 15:10:37 +0100
-
-collectd (3.10.4-0octo1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Florian Forster <octo@leeloo.home.verplant.org>  Thu, 21 Dec 2006 20:36:37 +0100
-
-collectd (3.10.3-0octo1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Florian Forster <octo@leeloo.home.verplant.org>  Sat,  4 Nov 2006 13:46:49 +0100
-
-collectd (3.10.1-4) unstable; urgency=low
-
-  * Changed collectd-dbg's section and priority to "devel" and "extra"
-    respectively.
-  * Set init start sequence code to 95 to be sure to start after any daemons
-    that data is collected from.
-
- -- Sebastian Harl <sh@tokkee.org>  Thu,  5 Oct 2006 10:25:07 +0000
-
-collectd (3.10.1-3) unstable; urgency=low
-
-  * Added --oknodo to start-stop-daemon in the init script (Closes: #379703).
-
- -- Sebastian Harl <sh@tokkee.org>  Tue, 25 Jul 2006 18:34:55 +0200
-
-collectd (3.10.1-2) unstable; urgency=low
-
-  * Added collectd-dbg package.
-
- -- Sebastian Harl <sh@tokkee.org>  Sun, 23 Jul 2006 23:39:42 +0200
-
-collectd (3.10.1-1) unstable; urgency=low
-
-  * New upstream release.
-  * Dynamically link against external liboping.
-    - New binary package collectd-ping.
-    - Added collectd-ping to suggested packages.
-  * Moved config file from /usr/share/doc/collectd/examples/ to
-    /etc/collectd/.
-
- -- Sebastian Harl <sh@tokkee.org>  Sat, 22 Jul 2006 21:43:37 +0200
-
-collectd (3.10.0-1) unstable; urgency=low
-
-  * New upstream release.
-    New plugins:
-    - APC UPS's charge, load, input/output/battery voltage, etc.: apcups
-    - NTP daemon's local clock drift, offset to peers, etc.: ntpd
-  * Upstream no longer provides a debian/ directory. Thus no repackaging is
-    required any longer.
-  * Not using getifaddrs() is now the default in upstream. getifaddrs.dpatch
-    no longer needed.
-  * Added collectd-hddtemp as a suggestion to the collectd package.
-
- -- Sebastian Harl <sh@tokkee.org>  Sun,  9 Jul 2006 21:52:13 +0200
-
-collectd (3.9.4+debian-1) unstable; urgency=low
-
-  * Initial release (Closes: #373008).
-  * Removed upstream's debian/ directory from .orig.tar.gz.
-  * getifaddrs.dpatch: Patching src/traffic.c to read data from /proc instead
-    of using getifaddrs(). getifaddrs() does not seem to work correctly on 
-    AMD64.
-
- -- Sebastian Harl <sh@tokkee.org>  Fri,  7 Jul 2006 15:49:42 +0200
-
diff --git a/debian/collectd.conf b/debian/collectd.conf
deleted file mode 100644 (file)
index 91eef47..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-# Config file for collectd(1).
-#
-# Some plugins need additional configuration and are disabled by default.
-# Please read collectd.conf(5) for details.
-
-Mode Local
-
-# If in "Client" mode you have to specify which server to send datagrams to.
-#Mode Client
-#Server 123.123.123.123 12345
-
-#Mode Server
-
-#Mode Log
-
-#DataDir    /var/lib/collectd
-#PIDFILE    /var/run/collectd.pid
-#PluginDir  /usr/lib/collectd
-
-#LoadPlugin apache
-#LoadPlugin apcups
-#LoadPlugin apple_sensors
-LoadPlugin battery
-LoadPlugin cpu
-#LoadPlugin cpufreq
-LoadPlugin df
-LoadPlugin disk
-#LoadPlugin hddtemp
-LoadPlugin load
-#LoadPlugin mbmon
-LoadPlugin memory
-#LoadPlugin mysql
-#LoadPlugin nfs
-#LoadPlugin ntpd
-#LoadPlugin ping
-LoadPlugin processes
-#LoadPlugin sensors
-#LoadPlugin serial
-LoadPlugin swap
-#LoadPlugin tape
-LoadPlugin traffic
-LoadPlugin users
-#LoadPlugin vserver
-#LoadPlugin wireless
-
-#<Plugin apache>
-#   URL http://localhost/status?auto
-#   User www-user
-#   Password secret
-#   CACert /etc/ssl/ca.crt
-#</Plugin>
-
-#<Plugin apcups>
-#   Host localhost
-#   Port 3551
-#</Plugin>
-
-#<Plugin hddtemp>
-#   Host 127.0.0.1
-#   Port 7634
-#</Plugin>
-
-#<Plugin mbmon>
-#      Host 127.0.0.1
-#      Port 411
-#</Plugin>
-
-#<Plugin ntpd>
-#   Host localhost
-#   Port 123
-#</Plugin>
-
-#<Plugin mysql>
-#   Host localhost
-#   User db_user
-#   Password secret
-#   Database db_name
-#</Plugin>
-
-#<Plugin ping>
-#   Host host.foo.bar
-#   Host host.baz.qux
-#</Plugin>
-
-#<Plugin traffic>
-#   Interface eth0
-#   IgnoreSelected false
-#</Plugin>
-
diff --git a/debian/collectd.init.d b/debian/collectd.init.d
deleted file mode 100755 (executable)
index 7c8ac29..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-#!/bin/bash
-#
-# collectd     Initscript for collectd
-#              http://collectd.org/
-# Authors:     Florian Forster <octo@verplant.org>
-#              Sebastian Harl <sh@tokkee.org>
-#
-
-set -e
-
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-DESC="Statistics collection daemon"
-NAME=collectd
-DAEMON=/usr/sbin/$NAME
-SCRIPTNAME=/etc/init.d/$NAME
-ARGS=""
-
-CONFIGDIR=/etc/collectd
-# for backward compatibility
-FALLBACKCONF=/etc/collectd.conf
-
-# Gracefully exit if the package has been removed.
-test -x $DAEMON || exit 0
-
-if [ -r /etc/default/$NAME ]
-then
-       . /etc/default/$NAME
-fi
-
-#
-#      Function that starts the daemon/service.
-#
-d_start() {
-       i=0
-       
-       if [ ! -d "$CONFIGDIR" ]
-       then
-               if [ -e "$FALLBACKCONF" ]
-               then
-                       $DAEMON -C "$FALLBACKCONF" 2> /dev/null
-               else
-                       echo ""
-                       echo "This package is not configured yet. Please refer"
-                       echo "to /usr/share/doc/collectd/README.Debian for"
-                       echo "details."
-                       echo ""
-                       exit 0
-               fi
-       else
-               for FILE in `ls $CONFIGDIR/*.conf 2>/dev/null`
-               do
-                       NAME=`basename "$FILE" .conf`
-
-                       if [ $i == 0 ]
-                       then
-                               echo -n " ("
-                       else
-                               echo -n ", "
-                       fi
-                       
-                       $DAEMON -C "$FILE" 2> /dev/null
-                       if [ $? == 0 ]
-                       then
-                               echo -n "$NAME"
-                       else
-                               echo -n "$NAME failed"
-                       fi
-
-                       i=$(($i+1))
-               done
-
-               if [ $i == 0 ]
-               then
-                       echo -n "[no config found]"
-                       exit 1
-               else
-                       echo -n ")"
-               fi
-       fi
-}
-
-#
-#      Function that stops the daemon/service.
-#
-d_stop() {
-       start-stop-daemon --stop --quiet --oknodo --exec $DAEMON
-}
-
-case "$1" in
-  start)
-       echo -n "Starting $DESC: $NAME"
-       d_start
-       echo "."
-       ;;
-  stop)
-       echo -n "Stopping $DESC: $NAME"
-       d_stop
-       echo "."
-       ;;
-  restart|force-reload)
-       echo -n "Restarting $DESC: $NAME"
-       d_stop
-       sleep 1
-       d_start
-       echo "."
-       ;;
-  *)
-       echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
-       exit 1
-       ;;
-esac
-
-exit 0
-
-# vim: syntax=sh noexpandtab sw=8 ts=8 :
diff --git a/debian/collectd.postrm b/debian/collectd.postrm
deleted file mode 100755 (executable)
index 84b8386..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/bash
-# postrm script for collectd
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-#        * <postrm> `remove'
-#        * <postrm> `purge'
-#        * <old-postrm> `upgrade' <new-version>
-#        * <new-postrm> `failed-upgrade' <old-version>
-#        * <new-postrm> `abort-install'
-#        * <new-postrm> `abort-install' <old-version>
-#        * <new-postrm> `abort-upgrade' <old-version>
-#        * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
-    purge)
-        rm -rf /var/lib/collectd
-        rm -rf /etc/collectd
-        ;;
-    
-    remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
-        ;;
-
-    *)
-        echo "postrm called with unknown argument \`$1'" >&2
-        exit 1
-        ;;
-
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
-
diff --git a/debian/compat b/debian/compat
deleted file mode 100644 (file)
index 7ed6ff8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-5
diff --git a/debian/control b/debian/control
deleted file mode 100644 (file)
index 56e8463..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-Source: collectd
-Section: utils
-Priority: optional
-Maintainer: Sebastian Harl <sh@tokkee.org>
-Build-Depends: debhelper (>= 5), autotools-dev, libcurl3-dev,
- libmysqlclient15-dev | libmysqlclient14-dev, librrd2-dev | librrd0-dev,
- libsensors-dev, liboping-dev (> 0.3.3)
-Standards-Version: 3.7.2
-
-Package: collectd
-Architecture: any
-Depends: ${shlibs:Depends}
-Suggests: collectd-apache, collectd-mysql, collectd-sensors, collectd-dev,
- collectd-hddtemp, collectd-mbmon, collectd-ping, librrds-perl
-Description: statistics collection daemon
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the main program file and the following plugins:
-   * APC UPS's charge, load, input/output/battery voltage, etc.: apcups
-   * Apple computer's sensors information: apple_sensors (server mode only)
-   * battery status: battery
-   * CPU utilization: cpu
-   * CPU frequency: cpufreq
-   * disk space usage: df
-   * disk and partition throughput: disk
-   * system load averages: load
-   * memory usage: memory
-   * NFS utilization: nfs
-   * NTP daemon's local clock drift, offset to peers, etc.: ntpd
-   * number of processes: processes
-   * serial port traffic: serial
-   * swap usage: swap
-   * tape drive usage: tape (server mode only)
-   * network traffic: traffic
-   * number of users logged into the system: users
-   * system ressources used by Linux-VServers: vserver
-   * wireless network stats: wireless
-
-Package: collectd-dbg
-Section: devel
-Architecture: any
-Priority: extra
-Depends: collectd (= ${Source-Version}), collectd-apache (= ${Source-Version}),
- collectd-hddtemp (= ${Source-Version}), collectd-mysql (= ${Source-Version}),
- collectd-ping (= ${Source-Version}), collectd-sensors (= ${Source-Version})
-Description: statistics collection daemon (debugging symbols)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the debugging symbols.
-
-Package: collectd-apache
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Description: statistics collection daemon (Apache plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the Apache plugin which collects Apache statistics
- provided by Apache's mod_status.
-
-Package: collectd-hddtemp
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Recommends: hddtemp
-Description: statistics collection daemon (hddtemp plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the hddtemp plugin which collects harddisk temperatures.
-
-Package: collectd-mbmon
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Recommends: mbmon (>= 2.01)
-Description: statistics collection daemon (mbmon plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the mbmon plugin which collects motherboard status
- information: temperatures, voltages and cooling fan speeds.
-
-Package: collectd-mysql
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Description: statistics collection daemon (MySQL plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the MySQL plugin which collects MySQL statistics
- provided by MySQL's "show status" command.
-
-Package: collectd-ping
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Description: statistics collection daemon (ping plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the ping plugin which collects network latency
- statistics.
-
-Package: collectd-sensors
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Description: statistics collection daemon (sensors plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the sensors plugin which collects lm_sensors
- information (e.g. CPU temperature, fan speeds).
-
-Package: collectd-dev
-Architecture: all
-Depends: collectd (= ${Source-Version})
-Description: statistics collection daemon (development files)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the development files needed to create your own
- plugins.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644 (file)
index c75c8ee..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-This package was debianized by Sebastian Harl <sh@tokkee.org> on
-Wed, 10 May 2006 09:20:39 +0200.
-
-It was downloaded from <http://collectd.org/files/>.
-
-Upstream Authors:
-       Core Developer:
-       Florian Forster <octo@verplant.org>
-
-       For individual credits, see AUTHORS.
-
-Copyright Holders (in alphabetical order):
-       Andre M. Hedrick <andre@suse.com>
-       Anthony Gialluca <tonyabg@charter.net>
-       David Bacher <drbacher@gmail.com>
-       Florian Forster <octo@verplant.org>
-       Free Software Foundation, Inc.
-       Jason Pepas <cell@ices.utexas.edu>
-       Kern Sibbald
-       Lyonel Vincent <lyonel@ezix.org>
-       Niki W. Waibel <niki.waibel@newlogic.com>
-       Peter Holik <peter@holik.at>
-       Roy Keene, Keene Enterprises <libconfig@rkeene.org>
-       Scott Garrett <sgarrett@technomancer.com>
-       Sebastian Harl <sh@tokkee.org>
-       Vincent Stehlé <vincent.stehle@free.fr>
-
-License:
-
-       Files src/apache.c
-             src/apple_sensors.c
-             src/mysql.c
-             src/ntpd.c
-             src/battery.c
-             src/wireless.c
-       Copyright (C) 2006 Florian Forster <octo@verplant.org>
-
-       File src/apcups.c
-       Copyright (C) 2006 Anthony Gialluca <tonyabg@charter.net>
-       Copyright (C) 2000-2004 Kern Sibbald
-       Copyright (C) 1996-99 Andre M. Hedrick <andre@suse.com>
-
-       File src/cpufreq.c
-       Copyright (C) 2005, 2006 Peter Holik <peter@holik.at>
-
-       File src/hddtemp.c
-       Copyright (C) 2005, 2006 Vincent Stehlé <vincent.stehle@free.fr>
-
-       File src/mbmon.c
-       Copyright (C) 2006 Flavio Stanchina <flavio@stanchina.net>
-
-       File src/nfs.c
-       Copyright (C) 2005, 2006 Jason Pepas <cell@ices.utexas.edu>
-
-       File src/processes.c
-       Copyright (C) 2005 Lyonel Vincent <lyonel@ezix.org>
-       Copyright (C) 2006 Florian Forster <octo@verplant.org> (Mach code)
-
-       File src/serial.c
-       Copyright (C) 2005, 2006 David Bacher <drbacher@gmail.com>
-
-       File src/tape.c
-       Copyright (C) 2005, 2006 Scott Garrett <sgarrett@technomancer.com>
-
-       File src/users.c
-       Copyright (C) 2005, 2006 Sebastian Harl <sh@tokkee.org>
-
-       Files src/utils_debug.c
-             src/utils_debug.h
-             src/utils_mount.c
-             src/utils_mount.h
-       Copyright (C) 2005, 2006 Niki W. Waibel <niki.waibel@gmx.net>
-
-       File src/vserver.c
-       Copyright (C) 2006 Sebastian Harl <sh@tokkee.org>
-
-       All other files (except libltdl/* and src/libconfig/*):
-       Copyright (C) 2005, 2006 Florian Forster <octo@verplant.org>
-
-       This package is free software; you can redistribute it and/or modify
-       it under the terms of the GNU General Public License as published by
-       the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
-
-       This package 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 package; if not, write to the Free Software
-       Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 
-       USA.
-
-On Debian systems, the complete text of the GNU General Public License 
-can be found in `/usr/share/common-licenses/GPL'.
-
-       File libltdl/ltdl.c
-       Copyright (C) 1998-2000, 2004, 2005 Free Software Foundation, Inc.
-       Originally by Thomas Tanner <tanner@ffii.org>
-
-       File libltdl/ltdl.h
-       Copyright (C) 1998-2000 Free Software Foundation, Inc.
-       Originally by Thomas Tanner <tanner@ffii.org>
-       
-       This library is free software; you can redistribute it and/or
-       modify it under the terms of the GNU Lesser General Public
-       License as published by the Free Software Foundation; either
-       version 2 of the License, or (at your option) any later version.
-
-       This library 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
-       Lesser General Public License for more details.
-
-       You should have received a copy of the GNU Lesser General Public
-       License along with this library; if not, write to the Free Software
-       Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 
-       USA.
-
-On Debian systems, the complete text of the GNU Lesser General Public License 
-can be found in `/usr/share/common-licenses/LGPL'.
-
-       Files src/libconfig/*
-       Copyright (C) Roy Keene <libconfig@rkeene.org>
-
-       Permission is hereby granted, free of charge, to any person
-       obtaining a copy of this software and associated documentation 
-       files (the "Software"), to deal in the Software without restriction, 
-       including without limitation the rights to use, copy, modify, merge, 
-       publish, distribute, sublicense, and/or sell copies of the Software, 
-       and to permit persons to whom the Software is furnished to do so, 
-       subject to the following conditions:
-
-       The above copyright notice and this permission notice shall be 
-       included in all copies or substantial portions of the Software.
-
-       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
-       EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
-       MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
-       IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
-       CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
-       TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
-       SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-The Debian packaging is (C) 2006, Sebastian Harl <sh@tokkee.org> and
-is licensed under the GPL, see above.
-
diff --git a/debian/examples/myplugin.c b/debian/examples/myplugin.c
deleted file mode 100644 (file)
index 1bd8e42..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * /usr/share/doc/collectd/examples/sample_plugin.c
- *
- * A sample plugin for collectd.
- *
- * Written by Sebastian Harl <sh@tokkee.org>
- *
- * This is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free 
- * Software Foundation; either version 2 of the License, or (at your 
- * option) any later version.
- */
-
-#include <collectd/common.h>    /* rrd_update_file */
-#include <collectd/plugin.h>    /* plugin_* */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-/* Optional config file support */
-/* #include <collectd/configfile.h> */
-
-/* Optional debugging support 
- * (only available if collectd was compiled with debugging support) */
-/* #include <collectd/utils_debug.h> */
-
-#define MODULE_NAME "myplugin"
-
-/* Name of the rrd file under DataDir (/var/lib/collectd by default)
- *
- * The name may contain slashes to create subdirectories. */
-static char *my_rrd = "myplugin.rrd";
-
-/* DS definitions for the rrd file
- *
- * See the rrdcreate(1) manpage for details. The heartbeat is configurable in
- * collectd. It defaults to 25. */
-static char *ds_def[] =
-{
-    "DS:my_ds:GAUGE:25:0:U",
-    NULL
-};
-
-/* DS count */
-static int ds_num = 1;
-
-/* Time at which the read function is called */
-extern time_t curtime;
-
-/* Initialize the plugin
- *
- * This function is called to set up a plugin before using it. */
-static void my_init(void)
-{
-    /* we have nothing to do here :-) */
-    return;
-}
-
-/* Get the data
- *
- * This function implements the magic used to get the desired values that
- * should be stored in the rrd file. It uses plugin_submit to transfer the
- * data to whatever place is configured in the config file. If there are more
- * than one instances you should pass a uniq identifier as seconds argument to
- * the plugin_submit function. */
-#define BUFSIZE 256
-static void my_read(void)
-{
-    long int data = 0;
-    char buf[BUFSIZE] = "";
-
-    /* magic ;-) */
-    data = random();
-
-    if (snprintf(buf, BUFSIZE, "%u:%li", 
-                (unsigned int)curtime, data) >= BUFSIZE)
-        return;
-
-    plugin_submit(MODULE_NAME, NULL, buf);
-    return;
-}
-#undef BUFSIZE
-
-/* Save the data
- *
- * This function saves the data to the appropriate location by calling
- * rrd_update_file. It is used to "calculate" the filename and DS definition
- * appropriate for the given instance. */
-static void my_write(host, inst, val)
-    char *host;
-    char *inst;
-    char *val;
-{
-    rrd_update_file(host, my_rrd, val, ds_def, ds_num);
-    return;
-}
-
-/* Register the plugin
- *
- * This function registers the plugin with collectd. It has to be named
- * "module_register". */
-void module_register(void)
-{
-    plugin_register(MODULE_NAME, my_init, my_read, my_write);
-    return;
-}
-
diff --git a/debian/rules b/debian/rules
deleted file mode 100755 (executable)
index 602efd5..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/make -f
-# debian/rules for collectd
-#
-# Written by Sebastian Harl <sh@tokkee.org>.
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-# These are used for cross-compiling and for saving the configure script
-# from having to guess our platform (since we know it already)
-DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
-DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-
-CFLAGS = -Wall -g
-
-ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
-       CFLAGS += -O0
-else
-       CFLAGS += -O2
-endif
-
-config.status: configure
-       dh_testdir
-       CFLAGS="$(CFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) \
-                       --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr \
-                       --mandir=\$${prefix}/share/man \
-                       --localstatedir=/var --sysconfdir=/etc
-
-build: build-stamp
-
-build-stamp: config.status
-       dh_testdir
-       
-       $(MAKE)
-       
-       touch build-stamp
-
-clean:
-       dh_testdir
-       dh_testroot
-       rm -f build-stamp
-       
-       -$(MAKE) distclean
-       
-       dh_clean 
-
-install-indep:
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_installdirs -i
-       
-       include_dir=$(CURDIR)/debian/collectd-dev/usr/include/collectd/ \
-               && mkdir -p $$include_dir \
-               && cp src/*.h $$include_dir
-
-install-arch: build
-       dh_testdir
-       dh_testroot
-       dh_clean -k 
-       dh_installdirs -a
-       
-       $(MAKE) install DESTDIR=$(CURDIR)/debian/collectd
-       
-       rm -f $(CURDIR)/debian/collectd/usr/lib/collectd/*.la
-       
-       rm -f $(CURDIR)/debian/collectd/etc/collectd.conf
-       install -D -m 0644 $(CURDIR)/debian/collectd.conf \
-               $(CURDIR)/debian/collectd/etc/collectd/collectd.conf
-       
-       for PLUGIN in apache hddtemp mbmon mysql ping sensors; do \
-               plugin_dir=$(CURDIR)/debian/collectd-$$PLUGIN/usr/lib/collectd/; \
-               mkdir -p $$plugin_dir; \
-               mv $(CURDIR)/debian/collectd/usr/lib/collectd/$$PLUGIN.so \
-                       $$plugin_dir; \
-       done    
-
-binary-indep: install-indep
-       dh_testdir
-       dh_testroot
-       dh_installchangelogs -i ChangeLog
-       dh_installdocs -A -i debian/README.Debian AUTHORS README TODO
-       dh_installexamples -i debian/examples/myplugin.c
-       dh_compress -i
-       dh_fixperms -i
-       dh_installdeb -i
-       dh_gencontrol -i
-       dh_md5sums -i
-       dh_builddeb -i
-
-binary-arch: build install-arch
-       dh_testdir
-       dh_testroot
-       dh_installchangelogs -a ChangeLog
-       dh_installdocs -A -a debian/README.Debian AUTHORS README TODO
-       dh_installexamples -a contrib/collectd2html.pl contrib/collection.cgi
-       dh_installinit -a -- defaults 95
-       dh_installman -a src/collectd.1 src/collectd.conf.5
-       dh_link -a
-       dh_strip -a --dbg-package=collectd-dbg
-       dh_compress -a
-       dh_fixperms -a
-       dh_installdeb -a
-       dh_shlibdeps -a
-       dh_gencontrol -a
-       dh_md5sums -a
-       dh_builddeb -a
-
-binary: binary-arch binary-indep
-.PHONY: build clean binary-indep binary-arch binary install-indep install-arch
-
index d928311..1bf550e 100644 (file)
@@ -53,6 +53,9 @@ collectd_DEPENDENCIES =
 if BUILD_WITH_LIBRT
 collectd_LDADD += -lrt
 endif
+if BUILD_WITH_LIBPOSIX4
+collectd_LDADD += -lposix4
+endif
 if BUILD_WITH_LIBSOCKET
 collectd_LDADD += -lsocket
 endif
@@ -257,6 +260,17 @@ collectd_LDADD += "-dlopen" curl_json.la
 collectd_DEPENDENCIES += curl_json.la
 endif
 
+if BUILD_PLUGIN_CURL_XML
+pkglib_LTLIBRARIES += curl_xml.la
+curl_xml_la_SOURCES = curl_xml.c
+curl_xml_la_LDFLAGS = -module -avoid-version
+curl_xml_la_CFLAGS = $(AM_CFLAGS) \
+               $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
+curl_xml_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
+collectd_LDADD += "-dlopen" curl_xml.la
+collectd_DEPENDENCIES += curl_xml.la
+endif
+
 if BUILD_PLUGIN_DBI
 pkglib_LTLIBRARIES += dbi.la
 dbi_la_SOURCES = dbi.c \
@@ -587,6 +601,16 @@ memory_la_LIBADD += -lperfstat
 endif
 endif
 
+if BUILD_PLUGIN_MODBUS
+pkglib_LTLIBRARIES += modbus.la
+modbus_la_SOURCES = modbus.c
+modbus_la_LDFLAGS = -module -avoid-version
+modbus_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMODBUS_CFLAGS)
+modbus_la_LIBADD = $(BUILD_WITH_LIBMODBUS_LIBS)
+collectd_LDADD += "-dlopen" modbus.la
+collectd_DEPENDENCIES += modbus.la
+endif
+
 if BUILD_PLUGIN_MULTIMETER
 pkglib_LTLIBRARIES += multimeter.la
 multimeter_la_SOURCES = multimeter.c
index d6712dc..3d6d957 100644 (file)
@@ -313,7 +313,8 @@ static int config_add (oconfig_item_t *ci)
                                (st->host != NULL) ? st->host : hostname_g,
                                (st->name != NULL) ? st->name : "default"),
 
-               status = plugin_register_complex_read (callback_name,
+               status = plugin_register_complex_read (/* group = */ NULL,
+                               /* name      = */ callback_name,
                                /* callback  = */ apache_read_host,
                                /* interval  = */ NULL,
                                /* user_data = */ &ud);
@@ -685,8 +686,12 @@ static int apache_read_host (user_data_t *user_data) /* {{{ */
                {
                        if (strcmp (fields[0], "Scoreboard:") == 0)
                                submit_scoreboard (fields[1], st);
-                       else if (strcmp (fields[0], "BusyServers:") == 0)
+                       else if ((strcmp (fields[0], "BusyServers:") == 0) /* Apache 1.* */
+                                       || (strcmp (fields[0], "BusyWorkers:") == 0) /* Apache 2.* */)
                                submit_gauge ("apache_connections", NULL, atol (fields[1]), st);
+                       else if ((strcmp (fields[0], "IdleServers:") == 0) /* Apache 1.x */
+                                       || (strcmp (fields[0], "IdleWorkers:") == 0) /* Apache 2.x */)
+                               submit_gauge ("apache_idle_workers", NULL, atol (fields[1]), st);
                }
        }
 
index b8d72f0..5637053 100644 (file)
@@ -8,7 +8,7 @@ collectd-perl - Documentation of collectd's C<perl plugin>
   # ...
   <Plugin perl>
     IncludeDir "/path/to/perl/plugins"
-    BaseName "Collectd::Plugin"
+    BaseName "Collectd::Plugins"
     EnableDebugger ""
     LoadPlugin "FooBar"
 
@@ -38,7 +38,9 @@ causes the Perl-interpreter to be initialized.
 =item B<BaseName> I<Name>
 
 Prepends I<Name>B<::> to all plugin names loaded after this option. This is
-provided for convenience to keep plugin names short.
+provided for convenience to keep plugin names short. All Perl-based plugins
+provided with the I<collectd> distributions reside in the C<Collectd::Plugins>
+namespace.
 
 =item E<lt>B<Plugin> I<Name>E<gt> block
 
@@ -163,7 +165,7 @@ and collectd:
 
 =item Config-Item
 
-A config-item is one structure which keeps the informations provided in the
+A config-item is one structure which keeps the information provided in the
 configuration file. The array of children keeps one entry for each
 configuration option. Each such entry is another config-item structure, which
 may nest further if nested blocks are used.
@@ -713,7 +715,7 @@ types used by the read, write and match functions.
 
 =item
 
-Please feel free to send in new plugins to collectd's mailinglist at
+Please feel free to send in new plugins to collectd's mailing list at
 E<lt>collectdE<nbsp>atE<nbsp>verplant.orgE<gt> for review and, possibly,
 inclusion in the main distribution. In the latter case, we will take care of
 keeping the plugin up to date and adapting it to new versions of collectd.
index dd0f1f6..36d2be3 100644 (file)
@@ -38,8 +38,8 @@ At least python I<version 2.3> is required.
 Loads the Python plugin I<Plugin>. Unlike most other LoadPlugin lines, this one
 should be a block containing the line "Globals true". This will cause collectd
 to export the name of all objects in the python interpreter for all plugins to
-see. If you don't do this or your platform does not support it, the embeded
-interpreter will start anywa but you won't be able to load certain python
+see. If you don't do this or your platform does not support it, the embedded
+interpreter will start anyway but you won't be able to load certain python
 modules, e.g. "time".
 
 =item B<Encoding> I<Name>
@@ -264,7 +264,7 @@ to choose the way it is stored in the meta data.
 
 =head2 Config
 
-The Config class is an object which keeps the informations provided in the
+The Config class is an object which keeps the information provided in the
 configuration file. The sequence of children keeps one entry for each
 configuration option. Each such entry is another Config instance, which
 may nest further if nested blocks are used.
@@ -693,7 +693,7 @@ dispatched by the python plugin after upgrades.
 =item
 
 This plugin is not compatible with python3. Trying to compile it with python3
-will fail because of the ways string, unicode and bytearray bahavior was
+will fail because of the ways string, unicode and bytearray behavior was
 changed.
 
 =item
index bc69a3b..abab10f 100644 (file)
@@ -259,9 +259,10 @@ static void exit_usage (int status)
 #endif
                        "    -h              Display help (this message)\n"
                        "\nBuiltin defaults:\n"
-                       "  Config-File       "CONFIGFILE"\n"
-                       "  PID-File          "PIDFILE"\n"
-                       "  Data-Directory    "PKGLOCALSTATEDIR"\n"
+                       "  Config file       "CONFIGFILE"\n"
+                       "  PID file          "PIDFILE"\n"
+                       "  Plugin directory  "PLUGINDIR"\n"
+                       "  Data directory    "PKGLOCALSTATEDIR"\n"
                        "\n"PACKAGE" "VERSION", http://collectd.org/\n"
                        "by Florian octo Forster <octo@verplant.org>\n"
                        "for contributions see `AUTHORS'\n");
index 049b07d..0d95ddf 100644 (file)
@@ -63,6 +63,7 @@ FQDNLookup   true
 @LOAD_PLUGIN_CSV@LoadPlugin csv
 #@BUILD_PLUGIN_CURL_TRUE@LoadPlugin curl
 #@BUILD_PLUGIN_CURL_JSON_TRUE@LoadPlugin curl_json
+#@BUILD_PLUGIN_CURL_XML_TRUE@LoadPlugin curl_xml
 #@BUILD_PLUGIN_DBI_TRUE@LoadPlugin dbi
 #@BUILD_PLUGIN_DF_TRUE@LoadPlugin df
 #@BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk
@@ -231,6 +232,25 @@ FQDNLookup   true
 #  </URL>
 #</Plugin>
 
+#<Plugin "curl_xml">
+#  <URL "http://localhost/stats.xml">
+#    Host "my_host"
+#    Instance "some_instance"
+#    User "collectd"
+#    Password "thaiNg0I"
+#    VerifyPeer true
+#    VerifyHost true
+#    CACert "/path/to/ca.crt"
+#
+#    <XPath "table[@id=\"magic_level\"]/tr">
+#      Type "magic_level"
+#      #InstancePrefix "prefix-"
+#      InstanceFrom "td[1]"
+#      ValuesFrom "td[2]/span[@class=\"level\"]"
+#    </XPath>
+#  </URL>
+#</Plugin>
+
 #<Plugin dbi>
 #      <Query "num_of_customers">
 #              Statement "SELECT 'customers' AS c_key, COUNT(*) AS c_value FROM customers_tbl"
@@ -349,7 +369,7 @@ FQDNLookup   true
 
 #<Plugin "java">
 #      JVMArg "-verbose:jni"
-#      JVMArg "-Djava.class.path=@prefix@/share/collectd/java"
+#      JVMArg "-Djava.class.path=@prefix@/share/collectd/java/collectd-api.jar"
 #
 #      LoadPlugin "org.collectd.java.Foobar"
 #      <Plugin "org.collectd.java.Foobar">
@@ -562,7 +582,7 @@ FQDNLookup   true
 
 #<Plugin perl>
 #      IncludeDir "/my/include/path"
-#      BaseName "Collectd::Plugin"
+#      BaseName "Collectd::Plugins"
 #      EnableDebugger ""
 #      LoadPlugin Monitorus
 #      LoadPlugin OpenVZ
@@ -665,6 +685,10 @@ FQDNLookup   true
 #              Password "dozaiTh4"
 #              CollectInterface true
 #              CollectRegistrationTable true
+#              CollectCPULoad true
+#              CollectMemory true
+#              CollectDF true
+#              CollectDisk true
 #      </Router>
 #</Plugin>
 
@@ -760,6 +784,7 @@ FQDNLookup   true
 #    </Match>
 #    <Match>
 #      Regex "\\<R=local_user\\>"
+#      ExcludeRegex "\\<R=local_user\\>.*mail_spool defer"
 #      DSType "CounterInc"
 #      Type "counter"
 #      Instance "local_user"
index 9af1ff5..3c8b26c 100644 (file)
@@ -112,7 +112,7 @@ a value higher than the number of plugins you've loaded is totally useless.
 =item B<Hostname> I<Name>
 
 Sets the hostname that identifies a host. If you omit this setting, the
-hostname will be determinded using the L<gethostname(2)> system call.
+hostname will be determined using the L<gethostname(2)> system call.
 
 =item B<FQDNLookup> B<true|false>
 
@@ -443,7 +443,7 @@ The curl plugin uses the B<libcurl> (L<http://curl.haxx.se/>) to read web pages
 and the match infrastructure (the same code used by the tail plugin) to use
 regular expressions with the received data.
 
-The following example will read the current value of AMD stock from google's
+The following example will read the current value of AMD stock from Google's
 finance page and dispatch the value to collectd.
 
   <Plugin curl>
@@ -562,9 +562,9 @@ The following example will collect the status values from each database:
 
 In the B<Plugin> block, there may be one or more B<URL> blocks, each defining
 a URL to be fetched via HTTP (using libcurl) and one or more B<Key> blocks.
-The B<Key> string argument must be in a path format, of which is used to collect
-a value from a JSON map object.  If a B<Key> path element is that of a I<*> wildcard,
-the values for all keys will be collectd.
+The B<Key> string argument must be in a path format, which is used to collect a
+value from a JSON map object. If a path element of B<Key> is the
+I<*>E<nbsp>wildcard, the values for all keys will be collectd.
 
 The following options are valid within B<URL> blocks:
 
@@ -619,6 +619,110 @@ Type-instance to use. Defaults to the current map key or current string array el
 
 =back
 
+=head2 Plugin C<curl_xml>
+
+The B<curl_xml plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and B<libxml2>
+(L<http://xmlsoft.org/>) to retrieve XML data via cURL.
+
+ <Plugin "curl_xml">
+   <URL "http://localhost/stats.xml">
+     Host "my_host"
+     Instance "some_instance"
+     User "collectd"
+     Password "thaiNg0I"
+     VerifyPeer true
+     VerifyHost true
+     CACert "/path/to/ca.crt"
+
+     <XPath "table[@id=\"magic_level\"]/tr">
+       Type "magic_level"
+       #InstancePrefix "prefix-"
+       InstanceFrom "td[1]"
+       ValuesFrom "td[2]/span[@class=\"level\"]"
+     </XPath>
+   </URL>
+ </Plugin>
+
+In the B<Plugin> block, there may be one or more B<URL> blocks, each defining a
+URL to be fetched via HTTP (using libcurl). Within each B<URL> block there are
+options which specify the connection parameters, for example authentication
+information, and one or more B<XPath> blocks.
+
+Each B<XPath> block specifies how to get one type of information. The
+string argument must be a valid XPath expression which returns a list
+of "base elements". One value is dispatched for each "base element". The
+I<type instance> and values are looked up using further I<XPath> expressions
+that should be relative to the base element.
+
+Within the B<URL> block the following options are accepted:
+
+=over 4
+
+=item B<Host> I<Name>
+
+Use I<Name> as the host name when submitting values. Defaults to the global
+host name setting.
+
+=item B<Instance> I<Instance>
+
+Use I<Instance> as the plugin instance when submitting values. Defaults to an
+empty string (no plugin instance).
+
+=item B<User> I<User>
+=item B<Password> I<Password>
+=item B<VerifyPeer> B<true>|B<false>
+=item B<VerifyHost> B<true>|B<false>
+=item B<CACert> I<CA Cert File>
+
+These options behave exactly equivalent to the appropriate options of the
+I<cURL> and I<cURL-JSON> plugins. Please see there for a detailed description.
+
+=item E<lt>B<XPath> I<XPath-expression>E<gt>
+
+Within each B<URL> block, there must be one or more B<XPath> blocks. Each
+B<XPath> block specifies how to get one type of information. The string
+argument must be a valid XPath expression which returns a list of "base
+elements". One value is dispatched for each "base element".
+
+Within the B<XPath> block the following options are accepted:
+
+=over 4
+
+=item B<Type> I<Type>
+
+Specifies the I<Type> used for submitting patches. This determines the number
+of values that are required / expected and whether the strings are parsed as
+signed or unsigned integer or as double values. See L<types.db(5)> for details.
+This option is required.
+
+=item B<InstancePrefix> I<InstancePrefix>
+
+Prefix the I<type instance> with I<InstancePrefix>. The values are simply
+concatenated together without any separator.
+This option is optional.
+
+=item B<InstanceFrom> I<InstanceFrom>
+
+Specifies a XPath expression to use for determining the I<type instance>. The
+XPath expression must return exactly one element. The element's value is then
+used as I<type instance>, possibly prefixed with I<InstancePrefix> (see above).
+
+This value is required. As a special exception, if the "base XPath expression"
+(the argument to the B<XPath> block) returns exactly one argument, then this
+option may be omitted.
+
+=item B<ValuesFrom> I<ValuesFrom> [I<ValuesFrom> ...]
+
+Specifies one or more XPath expression to use for reading the values. The
+number of XPath expressions must match the number of data sources in the
+I<type> specified with B<Type> (see above). Each XPath expression must return
+exactly one element. The element's value is then parsed as a number and used as
+value for the appropriate value in the value list dispatched to the daemon.
+
+=back
+
+=back
+
 =head2 Plugin C<dbi>
 
 This plugin uses the B<dbi> library (L<http://libdbi.sourceforge.net/>) to
@@ -669,7 +773,7 @@ The following is a complete list of options:
 Query blocks define I<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. 
+not used in collectd.
 
 In each B<Query> block, there is one or more B<Result> blocks. B<Result> blocks
 define which column holds which value or instance information. You can use
@@ -727,7 +831,7 @@ options to provide multiple queries with the same name but with a slightly
 different syntax. The plugin will use only those queries, where the specified
 minimum and maximum versions fit the version of the database in use.
 
-The database version is determined by C<dbi_conn_get_engine_version>, see the 
+The database version is determined by C<dbi_conn_get_engine_version>, see the
 L<libdbi documentation|http://libdbi.sourceforge.net/docs/programmers-guide/reference-conn.html#DBI-CONN-GET-ENGINE-VERSION>
 for details. Basically, each part of the version is assumed to be in the range
 from B<00> to B<99> and all dots are removed. So version "4.1.2" becomes
@@ -952,7 +1056,7 @@ is set to B<true>, all disks are collected B<except> the ones matched.
 
 =item B<Interface> I<Interface>
 
-The dns plugin uses B<libpcap> to capture dns traffic and analyses it. This
+The dns plugin uses B<libpcap> to capture dns traffic and analyzes it. This
 option sets the interface that should be used. If this option is not set, or
 set to "any", the plugin will try to get packets from B<all> interfaces. This
 may not work on certain platforms, such as MacE<nbsp>OSE<nbsp>X.
@@ -1479,6 +1583,11 @@ running in foreground- or non-daemon-mode.
 
 Prefix all lines printed by the current time. Defaults to B<true>.
 
+=item B<PrintSeverity> B<true>|B<false>
+
+When enabled, all lines are prefixed by the severity of the log message, for
+example "warning". Defaults to B<false>.
+
 =back
 
 B<Note>: There is no need to notify the daemon after moving or removing the
@@ -1579,6 +1688,132 @@ TCP-Port to connect to. Defaults to B<11211>.
 
 =back
 
+=head2 Plugin C<modbus>
+
+The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP and reads
+register values. It supports reading single registers (unsigned 16E<nbsp>bit
+values), large integer values (unsigned 32E<nbsp>bit values) and floating point
+values (two registers interpreted as IEEE floats in big endian notation).
+
+Synopsis:
+
+ <Data "voltage-input-1">
+   RegisterBase 0
+   RegisterType float
+   Type voltage
+   Instance "input-1"
+ </Data>
+ <Data "voltage-input-2">
+   RegisterBase 2
+   RegisterType float
+   Type voltage
+   Instance "input-2"
+ </Data>
+ <Host "modbus.example.com">
+   Address "192.168.0.42"
+   Port    "502"
+   Interval 60
+   
+   <Slave 1>
+     Instance "power-supply"
+     Collect  "voltage-input-1"
+     Collect  "voltage-input-2"
+   </Slave>
+ </Host>
+
+=over 4
+
+=item E<lt>B<Data> I<Name>E<gt> blocks
+
+Data blocks define a mapping between register numbers and the "types" used by
+I<collectd>.
+
+Within E<lt>DataE<nbsp>/E<gt> blocks, the following options are allowed:
+
+=over 4
+
+=item B<RegisterBase> I<Number>
+
+Configures the base register to read from the device. If the option
+B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
+register will be read (the register number is increased by one).
+
+=item B<RegisterType> B<Uint16>|B<Uint32>|B<Float>
+
+Specifies what kind of data is returned by the device. If the type is B<Uint32>
+or B<Float>, two 16E<nbsp>bit registers will be read and the data is combined
+into one value. Defaults to B<Uint16>.
+
+=item B<Type> I<Type>
+
+Specifies the "type" (data set) to use when dispatching the value to
+I<collectd>. Currently, only data sets with exactly one data source are
+supported.
+
+=item B<Instance> I<Instance>
+
+Sets the type instance to use when dispatching the value to I<collectd>. If
+unset, an empty string (no type instance) is used.
+
+=back
+
+=item E<lt>B<Host> I<Name>E<gt> blocks
+
+Host blocks are used to specify to which hosts to connect and what data to read
+from their "slaves". The string argument I<Name> is used as hostname when
+dispatching the values to I<collectd>.
+
+Within E<lt>HostE<nbsp>/E<gt> blocks, the following options are allowed:
+
+=over 4
+
+=item B<Address> I<Hostname>
+
+Specifies the node name (the actual network address) used to connect to the
+host. This may be an IP address or a hostname. Please note that the used
+I<libmodbus> library only supports IPv4 at the moment.
+
+=item B<Port> I<Service>
+
+Specifies the port used to connect to the host. The port can either be given as
+a number or as a service name. Please note that the I<Service> argument must be
+a string, even if ports are given in their numerical form. Defaults to "502".
+
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+host. By default the global B<Interval> setting will be used.
+
+=item E<lt>B<Slave> I<ID>E<gt>
+
+Over each TCP connection, multiple Modbus devices may be reached. The slave ID
+is used to specify which device should be addressed. For each device you want
+to query, one B<Slave> block must be given.
+
+Within E<lt>SlaveE<nbsp>/E<gt> blocks, the following options are allowed:
+
+=over 4
+
+=item B<Instance> I<Instance>
+
+Specify the plugin instance to use when dispatching the values to I<collectd>.
+By default "slave_I<ID>" is used.
+
+=item B<Collect> I<DataName>
+
+Specifies which data to retrieve from the device. I<DataName> must be the same
+string as the I<Name> argument passed to a B<Data> block. You can specify this
+option multiple times to collect more than one value from a slave. At least one
+B<Collect> option is mandatory.
+
+=back
+
+=back
+
+=back
+
 =head2 Plugin C<mysql>
 
 The C<mysql plugin> requires B<mysqlclient> to be installed. It connects to
@@ -1679,7 +1914,7 @@ or SQL threads are not running.
 
 =head2 Plugin C<netapp>
 
-The netapp plugin can collect various performance and capacity informations
+The netapp plugin can collect various performance and capacity information
 from a NetApp filer using the NetApp API.
 
 Please note that NetApp has a wide line of products and a lot of different
@@ -2203,7 +2438,7 @@ Here are some examples to help you understand the above text more easily:
 
 =item B<IgnoreSelected>
 
-The behaviour is the same as with all other similar plugins: If nothing is
+The behavior is the same as with all other similar plugins: If nothing is
 selected at all, everything is collected. If some things are selected using the
 options described above, only these statistics are collected. If you set
 B<IgnoreSelected> to B<true>, this behavior is inverted, i.E<nbsp>e. the
@@ -2215,7 +2450,7 @@ specified statistics will not be collected.
 
 The Network plugin sends data to a remote instance of collectd, receives data
 from a remote instance, or both at the same time. Data which has been received
-from the network is usually not transmitted again, but this can be actived, see
+from the network is usually not transmitted again, but this can be activated, see
 the B<Forward> option below.
 
 The default IPv6 multicast group is C<ff18::efc0:4a42>. The default IPv4
@@ -2277,6 +2512,15 @@ B<None> require this setting.
 This feature is only available if the I<network> plugin was linked with
 I<libgcrypt>.
 
+=item B<Interface> I<Interface name>
+
+Set the outgoing interface for IP packets. This applies at least
+to IPv6 packets and if possible to IPv4. If this option is not applicable,
+undefined or a non-existent interface name is specified, the default
+behaviour is to let the kernel choose the appropriate interface. Be warned
+that the manual selection of an interface for unicast traffic is only
+necessary in rare cases.
+
 =back
 
 =item B<E<lt>Listen> I<Host> [I<Port>]B<E<gt>>
@@ -2325,6 +2569,14 @@ Each time a packet is received, the modification time of the file is checked
 using L<stat(2)>. If the file has been changed, the contents is re-read. While
 the file is being read, it is locked using L<fcntl(2)>.
 
+=item B<Interface> I<Interface name>
+
+Set the incoming interface for IP packets explicitly. This applies at least
+to IPv6 packets and if possible to IPv4. If this option is not applicable,
+undefined or a non-existent interface name is specified, the default
+behaviour is, to let the kernel choose the appropriate interface. Thus incoming
+traffic gets only accepted, if it arrives on the given interface.
+
 =back
 
 =item B<TimeToLive> I<1-255>
@@ -2509,7 +2761,7 @@ UDP-Port to connect to. Defaults to B<123>.
 
 =item B<ReverseLookups> B<true>|B<false>
 
-Sets wether or not to perform reverse lookups on peers. Since the name or
+Sets whether or not to perform reverse lookups on peers. Since the name or
 IP-address may be used in a filename it is recommended to disable reverse
 lookups. The default is to do reverse lookups to preserve backwards
 compatibility, though.
@@ -2667,12 +2919,31 @@ Available options:
 
 Specifies the location of the status file.
 
-=item B<Compression> B<true>|B<false>
+=item B<ImprovedNamingSchema> B<true>|B<false>
+
+When enabled, the filename of the status file will be used as plugin instance
+and the client's "common name" will be used as type instance. This is required
+when reading multiple status files. Enabling this option is recommended, but to
+maintain backwards compatibility this option is disabled by default.
+
+=item B<CollectCompression> B<true>|B<false>
 
 Sets whether or not statistics about the compression used by OpenVPN should be
 collected. This information is only available in I<single> mode. Enabled by
 default.
 
+=item B<CollectIndividualUsers> B<true>|B<false>
+
+Sets whether or not traffic information is collected for each connected client
+individually. If set to false, currently no traffic data is collected at all
+because aggregating this data in a save manner is tricky. Defaults to B<true>.
+
+=item B<CollectUserCount> B<true>|B<false>
+
+When enabled, the number of currently connected clients or users is collected.
+This is expecially interesting when B<CollectIndividualUsers> is disabled, but
+can be configured independently from that option. Defaults to B<false>.
+
 =back
 
 =head2 Plugin C<oracle>
@@ -2860,6 +3131,7 @@ L<http://www.postgresql.org/docs/manuals/>.
     </Database>
 
     <Database bar>
+      Interval 300
       Service "service_name"
       Query backend # predefined
       Query rt36_tickets
@@ -2923,7 +3195,8 @@ The username used to connect to the database.
 
 =item I<interval>
 
-The interval collectd is using (as specified by the B<Interval> option).
+The interval with which this database is queried (as specified by the database
+specific or global B<Interval> options).
 
 =back
 
@@ -3061,6 +3334,11 @@ for details.
 
 =over 4
 
+=item B<Interval> I<seconds>
+
+Specify the interval with which the database should be queried. The default is
+to use the global B<Interval> setting.
+
 =item B<Host> I<hostname>
 
 Specify the hostname or IP of the PostgreSQL server to connect to. If the
@@ -3341,6 +3619,8 @@ multiple routers:
       User "collectd"
       Password "secr3t"
       CollectInterface true
+      CollectCPULoad true
+      CollectMemory true
     </Router>
     <Router>
       Host "router1.example.com"
@@ -3348,6 +3628,8 @@ multiple routers:
       Password "5ecret"
       CollectInterface true
       CollectRegistrationTable true
+      CollectDF true
+      CollectDisk true
     </Router>
   </Plugin>
 
@@ -3385,6 +3667,29 @@ present on the device. Defaults to B<false>.
 When set to B<true>, information about wireless LAN connections will be
 collected. Defaults to B<false>.
 
+=item B<CollectCPULoad> B<true>|B<false>
+
+When set to B<true>, information about the CPU usage will be collected. The
+number is a dimensionless value where zero indicates no CPU usage at all.
+Defaults to B<false>.
+
+=item B<CollectMemory> B<true>|B<false>
+
+When enabled, the amount of used and free memory will be collected. How used
+memory is calculated is unknown, for example whether or not caches are counted
+as used space.
+Defaults to B<false>.
+
+=item B<CollectDF> B<true>|B<false>
+
+When enabled, the amount of used and free disk space will be collected.
+Defaults to B<false>.
+
+=item B<CollectDisk> B<true>|B<false>
+
+When enabled, the number of sectors written and bad blocks will be collected.
+Defaults to B<false>.
+
 =back
 
 =head2 Plugin C<rrdcached>
@@ -3717,6 +4022,7 @@ user using (extended) regular expressions, as described in L<regex(7)>.
       </Match>
       <Match>
         Regex "\\<R=local_user\\>"
+        ExcludeRegex "\\<R=local_user\\>.*mail_spool defer"
         DSType "CounterInc"
         Type "counter"
         Instance "local_user"
@@ -3751,6 +4057,13 @@ want to match literal parentheses you need to do the following:
 
   Regex "SPAM \\(Score: (-?[0-9]+\\.[0-9]+)\\)"
 
+=item B<ExcludeRegex> I<regex>
+
+Sets an optional regular expression to use for excluding lines from the match.
+An example which excludes all connections from localhost from the match:
+
+  ExcludeRegex "127\\.0\\.0\\.1"
+
 =item B<DSType> I<Type>
 
 Sets how the values are cumulated. I<Type> is one of:
@@ -3936,7 +4249,7 @@ selection is configured at all, B<all> devices are selected.
 
 =head2 Plugin C<tokyotyrant>
 
-The C<tokyotyrant plugin> connects to a TokyoTyrant server and collects a 
+The C<tokyotyrant plugin> connects to a TokyoTyrant server and collects a
 couple metrics: number of records, and database size on disk.
 
 =over 4
@@ -4106,6 +4419,12 @@ create output in the I<JavaScript Object Notation> (JSON).
 
 Defaults to B<Command>.
 
+=item B<StoreRates> B<true|false>
+
+If set to B<true>, convert counter values to rates. If set to B<false> (the
+default) counter values are stored as is, i.E<nbsp>e. as an increasing integer
+number.
+
 =back
 
 =head1 THRESHOLD CONFIGURATION
@@ -4504,7 +4823,7 @@ This is the same as writing:
 
 =back
 
-=head2 Built-in targets 
+=head2 Built-in targets
 
 The following targets are built into the core daemon and therefore need no
 plugins to be loaded:
@@ -4613,6 +4932,12 @@ 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.
 
+=item B<Invert> B<false>|B<true>
+
+When set to B<true>, the result of the match is inverted, i.e. all value lists
+where all regular expressions apply are not matched, all other value lists are
+matched. Defaults to B<false>.
+
 =back
 
 Example:
index 3d8998f..957654b 100644 (file)
@@ -135,6 +135,12 @@ typedef bool _Bool;
 # ifndef isnan
 #  define isnan(f) ((f) != (f))
 # endif /* !defined(isnan) */
+# ifndef isfinite
+#  define isfinite(f) (((f) - (f)) == 0.0)
+# endif
+# ifndef isinf
+#  define isinf(f) (!isfinite(f) && !isnan(f))
+# endif
 #endif /* NAN_ZERO_ZERO */
 
 /* Try really, really hard to determine endianess. Under NexentaStor 1.0.2 this
index 3695a9b..2598036 100644 (file)
@@ -668,6 +668,7 @@ long long get_kstat_value (kstat_t *ksp, char *name)
 }
 #endif /* HAVE_LIBKSTAT */
 
+#ifndef HAVE_HTONLL
 unsigned long long ntohll (unsigned long long n)
 {
 #if BYTE_ORDER == BIG_ENDIAN
@@ -685,6 +686,7 @@ unsigned long long htonll (unsigned long long n)
        return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
 #endif
 } /* unsigned long long htonll */
+#endif /* HAVE_HTONLL */
 
 #if FP_LAYOUT_NEED_NOTHING
 /* Well, we need nothing.. */
@@ -1142,3 +1144,21 @@ int service_name_to_port_number (const char *service_name)
                return (service_number);
        return (-1);
 } /* int service_name_to_port_number */
+
+int strtoderive (const char *string, derive_t *ret_value) /* {{{ */
+{
+       derive_t tmp;
+       char *endptr;
+
+       if ((string == NULL) || (ret_value == NULL))
+               return (EINVAL);
+
+       errno = 0;
+       endptr = NULL;
+       tmp = (derive_t) strtoll (string, &endptr, /* base = */ 0);
+       if ((endptr == string) || (errno != 0))
+               return (-1);
+
+       *ret_value = tmp;
+       return (0);
+} /* }}} int strtoderive */
index 2d5c794..c0bea36 100644 (file)
@@ -236,8 +236,10 @@ int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name);
 long long get_kstat_value (kstat_t *ksp, char *name);
 #endif
 
+#ifndef HAVE_HTONLL
 unsigned long long ntohll (unsigned long long n);
 unsigned long long htonll (unsigned long long n);
+#endif
 
 #if FP_LAYOUT_NEED_NOTHING
 # define ntohd(d) (d)
@@ -289,4 +291,6 @@ counter_t counter_diff (counter_t old_value, counter_t new_value);
  * (in the range [1-65535]). Returns less than zero on error. */
 int service_name_to_port_number (const char *service_name);
 
+int strtoderive (const char *string, derive_t *ret_value);
+
 #endif /* COMMON_H */
index b2997d6..fe2bce3 100644 (file)
@@ -575,14 +575,14 @@ static oconfig_item_t *cf_read_dir (const char *dir, int depth)
                ERROR ("configfile: malloc failed.");
                return (NULL);
        }
-       memset (root, '\0', sizeof (oconfig_item_t));
+       memset (root, 0, sizeof (oconfig_item_t));
 
        while ((de = readdir (dh)) != NULL)
        {
                char   name[1024];
                char **tmp;
 
-               if ((de->d_name[0] == '.') || (de->d_name[0] == '\0'))
+               if ((de->d_name[0] == '.') || (de->d_name[0] == 0))
                        continue;
 
                status = ssnprintf (name, sizeof (name), "%s/%s",
@@ -624,13 +624,11 @@ static oconfig_item_t *cf_read_dir (const char *dir, int depth)
                char *name = filenames[i];
 
                temp = cf_read_generic (name, depth);
-               if (temp == NULL) {
-                       int j;
-                       for (j = i; j < filenames_num; ++j)
-                               free (filenames[j]);
-                       free (filenames);
-                       oconfig_free (root);
-                       return (NULL);
+               if (temp == NULL)
+               {
+                       /* An error should already have been reported. */
+                       sfree (name);
+                       continue;
                }
 
                cf_ci_append_children (root, temp);
@@ -959,6 +957,45 @@ int cf_util_get_string (const oconfig_item_t *ci, char **ret_string) /* {{{ */
        return (0);
 } /* }}} int cf_util_get_string */
 
+/* Assures the config option is a string and copies it to the provided buffer.
+ * Assures null-termination. */
+int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer, /* {{{ */
+               size_t buffer_size)
+{
+       if ((ci == NULL) || (buffer == NULL) || (buffer_size < 1))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               ERROR ("cf_util_get_string_buffer: The %s option requires "
+                               "exactly one string argument.", ci->key);
+               return (-1);
+       }
+
+       strncpy (buffer, ci->values[0].value.string, buffer_size);
+       buffer[buffer_size - 1] = 0;
+
+       return (0);
+} /* }}} int cf_util_get_string_buffer */
+
+/* Assures the config option is a number and returns it as an int. */
+int cf_util_get_int (const oconfig_item_t *ci, int *ret_value) /* {{{ */
+{
+       if ((ci == NULL) || (ret_value == NULL))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+       {
+               ERROR ("cf_util_get_int: The %s option requires "
+                               "exactly one numeric argument.", ci->key);
+               return (-1);
+       }
+
+       *ret_value = (int) ci->values[0].value.number;
+
+       return (0);
+} /* }}} int cf_util_get_int */
+
 int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
 {
        if ((ci == NULL) || (ret_bool == NULL))
@@ -967,7 +1004,7 @@ int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
        if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
        {
                ERROR ("cf_util_get_boolean: The %s option requires "
-                               "exactly one string argument.", ci->key);
+                               "exactly one boolean argument.", ci->key);
                return (-1);
        }
 
index a73def2..432e09f 100644 (file)
@@ -91,6 +91,14 @@ const char *global_option_get (const char *option);
  * success. */
 int cf_util_get_string (const oconfig_item_t *ci, char **ret_string);
 
+/* Assures the config option is a string and copies it to the provided buffer.
+ * Assures null-termination. */
+int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer,
+               size_t buffer_size);
+
+/* Assures the config option is a number and returns it as an int. */
+int cf_util_get_int (const oconfig_item_t *ci, int *ret_value);
+
 /* Assures the config option is a boolean and assignes it to `ret_bool'.
  * Otherwise, `ret_bool' is not changed and non-zero is returned. */
 int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool);
index 7787203..06055ca 100644 (file)
@@ -27,7 +27,7 @@
 # error "No applicable input method."
 #endif
 
-static void cs_submit (unsigned long context_switches)
+static void cs_submit (derive_t context_switches)
 {
        value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
@@ -49,7 +49,7 @@ static int cs_read (void)
        char buffer[64];
        int numfields;
        char *fields[3];
-       unsigned long result = 0;
+       derive_t result = 0;
        int status = -2;
 
        fh = fopen ("/proc/stat", "r");
@@ -72,7 +72,7 @@ static int cs_read (void)
 
                errno = 0;
                endptr = NULL;
-               result = strtoul(fields[1], &endptr, 10);
+               result = (derive_t) strtoll (fields[1], &endptr, /* base = */ 10);
                if ((endptr == fields[1]) || (errno != 0)) {
                        ERROR ("contextswitch plugin: Cannot parse ctxt value: %s",
                                        fields[1]);
index 0b34687..96c1e3e 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -299,7 +299,7 @@ static int csv_write (const data_set_t *ds, const value_list_t *vl,
 
                fprintf (use_stdio == 1 ? stdout : stderr,
                         "PUTVAL %s interval=%i %s\n",
-                        filename, interval_g, values);
+                        filename, vl->interval, values);
                return (0);
        }
 
index bc11c28..a533e14 100644 (file)
@@ -37,6 +37,7 @@ typedef struct web_match_s web_match_t;
 struct web_match_s /* {{{ */
 {
   char *regex;
+  char *exclude_regex;
   int dstype;
   char *type;
   char *instance;
@@ -291,6 +292,8 @@ static int cc_config_add_match (web_page_t *page, /* {{{ */
 
     if (strcasecmp ("Regex", child->key) == 0)
       status = cc_config_add_string ("Regex", &match->regex, child);
+    else if (strcasecmp ("ExcludeRegex", child->key) == 0)
+      status = cc_config_add_string ("ExcludeRegex", &match->exclude_regex, child);
     else if (strcasecmp ("DSType", child->key) == 0)
       status = cc_config_add_match_dstype (&match->dstype, child);
     else if (strcasecmp ("Type", child->key) == 0)
@@ -333,7 +336,8 @@ static int cc_config_add_match (web_page_t *page, /* {{{ */
   if (status != 0)
     return (status);
 
-  match->match = match_create_simple (match->regex, match->dstype);
+  match->match = match_create_simple (match->regex, match->exclude_regex,
+      match->dstype);
   if (match->match == NULL)
   {
     ERROR ("curl plugin: tail_match_add_match_simple failed.");
index 53e8abd..03ef6a3 100644 (file)
@@ -673,7 +673,7 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
     ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
                db->instance, db->url);
 
-    plugin_register_complex_read (cb_name, cj_read,
+    plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
                                   /* interval = */ NULL, &ud);
   }
   else
diff --git a/src/curl_xml.c b/src/curl_xml.c
new file mode 100644 (file)
index 0000000..c10955c
--- /dev/null
@@ -0,0 +1,930 @@
+/**
+ * collectd - src/curl_xml.c
+ * Copyright (C) 2009,2010       Amit Gupta
+ *
+ * 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:
+ *   Amit Gupta <amit.gupta221 at gmail.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_llist.h"
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+#include <curl/curl.h>
+
+#define CX_DEFAULT_HOST "localhost"
+
+/*
+ * Private data structures
+ */
+struct cx_values_s /* {{{ */
+{
+  char path[DATA_MAX_NAME_LEN];
+  size_t path_len;
+};
+typedef struct cx_values_s cx_values_t;
+/* }}} */
+
+struct cx_xpath_s /* {{{ */
+{
+  char *path;
+  char *type;
+  cx_values_t *values;
+  int values_len;
+  char *instance_prefix;
+  char *instance;
+  int is_table;
+  unsigned long magic;
+};
+typedef struct cx_xpath_s cx_xpath_t;
+/* }}} */
+
+struct cx_s /* {{{ */
+{
+  char *instance;
+  char *host;
+
+  char *url;
+  char *user;
+  char *pass;
+  char *credentials;
+  _Bool verify_peer;
+  _Bool verify_host;
+  char *cacert;
+
+  CURL *curl;
+  char curl_errbuf[CURL_ERROR_SIZE];
+  char *buffer;
+  size_t buffer_size;
+  size_t buffer_fill;
+
+  llist_t *list; /* list of xpath blocks */
+};
+typedef struct cx_s cx_t; /* }}} */
+
+/*
+ * Private functions
+ */
+static size_t cx_curl_callback (void *buf, /* {{{ */
+    size_t size, size_t nmemb, void *user_data)
+{
+  size_t len = size * nmemb;
+  cx_t *db;
+
+  db = user_data;
+  if (db == NULL)
+  {
+    ERROR ("curl_xml plugin: cx_curl_callback: "
+           "user_data pointer is NULL.");
+    return (0);
+  }
+
+   if (len <= 0)
+    return (len);
+
+  if ((db->buffer_fill + len) >= db->buffer_size)
+  {
+    char *temp;
+
+    temp = (char *) realloc (db->buffer,
+                    db->buffer_fill + len + 1);
+    if (temp == NULL)
+    {
+      ERROR ("curl_xml plugin: realloc failed.");
+      return (0);
+    }
+    db->buffer = temp;
+    db->buffer_size = db->buffer_fill + len + 1;
+  }
+
+  memcpy (db->buffer + db->buffer_fill, (char *) buf, len);
+  db->buffer_fill += len;
+  db->buffer[db->buffer_fill] = 0;
+
+  return (len);
+} /* }}} size_t cx_curl_callback */
+
+static void cx_xpath_free (cx_xpath_t *xpath) /* {{{ */
+{
+  if (xpath == NULL)
+    return;
+
+  sfree (xpath->path);
+  sfree (xpath->type);
+  sfree (xpath->instance_prefix);
+  sfree (xpath->instance);
+  sfree (xpath->values);
+  sfree (xpath);
+} /* }}} void cx_xpath_free */
+
+static void cx_list_free (llist_t *list) /* {{{ */
+{
+  llentry_t *le;
+
+  le = llist_head (list);
+  while (le != NULL)
+  {
+    llentry_t *le_next;
+
+    le_next = le->next;
+
+    sfree (le->key);
+    cx_xpath_free (le->value);
+
+    le = le_next;
+  }
+
+  llist_destroy (list);
+  list = NULL;
+} /* }}} void cx_list_free */
+
+static void cx_free (void *arg) /* {{{ */
+{
+  cx_t *db;
+
+  DEBUG ("curl_xml plugin: cx_free (arg = %p);", arg);
+
+  db = (cx_t *) arg;
+
+  if (db == NULL)
+    return;
+
+  if (db->curl != NULL)
+    curl_easy_cleanup (db->curl);
+  db->curl = NULL;
+
+  if (db->list != NULL)
+    cx_list_free (db->list);
+
+  sfree (db->buffer);
+  sfree (db->instance);
+  sfree (db->host);
+
+  sfree (db->url);
+  sfree (db->user);
+  sfree (db->pass);
+  sfree (db->credentials);
+  sfree (db->cacert);
+
+  sfree (db);
+} /* }}} void cx_free */
+
+static int cx_check_type (const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */
+{
+  if (!ds)
+  {
+    WARNING ("curl_xml plugin: DataSet `%s' not defined.", xpath->type);
+    return (-1);
+  }
+
+  if (ds->ds_num != xpath->values_len)
+  {
+    WARNING ("curl_xml plugin: DataSet `%s' requires %i values, but config talks about %i",
+        xpath->type, ds->ds_num, xpath->values_len);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} cx_check_type */
+
+static xmlXPathObjectPtr cx_evaluate_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ 
+           xmlChar *expr)
+{
+  xmlXPathObjectPtr xpath_obj;
+
+  /* XXX: When to free this? */
+  xpath_obj = xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
+  if (xpath_obj == NULL)
+  {
+     WARNING ("curl_xml plugin: "
+               "Error unable to evaluate xpath expression \"%s\". Skipping...", expr);
+     return NULL;
+  }
+
+  return xpath_obj;
+} /* }}} cx_evaluate_xpath */
+
+static int cx_if_not_text_node (xmlNodePtr node) /* {{{ */
+{
+  if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE)
+    return (0);
+
+  WARNING ("curl_xml plugin: "
+           "Node \"%s\" doesn't seem to be a text node. Skipping...", node->name);
+  return -1;
+} /* }}} cx_if_not_text_node */
+
+static int cx_handle_single_value_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+    cx_xpath_t *xpath,
+    const data_set_t *ds, value_list_t *vl, int index)
+{
+  xmlXPathObjectPtr values_node_obj;
+  xmlNodeSetPtr values_node;
+  int tmp_size;
+  char *node_value;
+
+  values_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->values[index].path);
+  if (values_node_obj == NULL)
+    return (-1); /* Error already logged. */
+
+  values_node = values_node_obj->nodesetval;
+  tmp_size = (values_node) ? values_node->nodeNr : 0;
+
+  if (tmp_size == 0)
+  {
+    WARNING ("curl_xml plugin: "
+        "relative xpath expression \"%s\" doesn't match any of the nodes. "
+        "Skipping...", xpath->values[index].path);
+    xmlXPathFreeObject (values_node_obj);
+    return (-1);
+  }
+
+  if (tmp_size > 1)
+  {
+    WARNING ("curl_xml plugin: "
+        "relative xpath expression \"%s\" is expected to return "
+        "only one node. Skipping...", xpath->values[index].path);
+    xmlXPathFreeObject (values_node_obj);
+    return (-1);
+  }
+
+  /* ignoring the element if other than textnode/attribute*/
+  if (cx_if_not_text_node(values_node->nodeTab[0]))
+  {
+    WARNING ("curl_xml plugin: "
+        "relative xpath expression \"%s\" is expected to return "
+        "only text/attribute node which is not the case. Skipping...", 
+        xpath->values[index].path);
+    xmlXPathFreeObject (values_node_obj);
+    return (-1);
+  }
+
+  node_value = (char *) xmlNodeGetContent(values_node->nodeTab[0]);
+  switch (ds->ds[index].type)
+  {
+    case DS_TYPE_COUNTER:
+      vl->values[index].counter = (counter_t) strtoull (node_value,
+          /* endptr = */ NULL, /* base = */ 0);
+      break;
+    case DS_TYPE_DERIVE:
+      vl->values[index].derive = (derive_t) strtoll (node_value,
+          /* endptr = */ NULL, /* base = */ 0);
+      break;
+    case DS_TYPE_ABSOLUTE:
+      vl->values[index].absolute = (absolute_t) strtoull (node_value,
+          /* endptr = */ NULL, /* base = */ 0);
+      break;
+    case DS_TYPE_GAUGE: 
+      vl->values[index].gauge = (gauge_t) strtod (node_value,
+          /* endptr = */ NULL);
+  }
+
+  /* free up object */
+  xmlXPathFreeObject (values_node_obj);
+
+  /* We have reached here which means that
+   * we have got something to work */
+  return (0);
+} /* }}} int cx_handle_single_value_xpath */
+
+static int cx_handle_all_value_xpaths (xmlXPathContextPtr xpath_ctx, /* {{{ */
+    cx_xpath_t *xpath,
+    const data_set_t *ds, value_list_t *vl)
+{
+  value_t values[xpath->values_len];
+  int status;
+  int i;
+
+  assert (xpath->values_len > 0);
+  assert (xpath->values_len == vl->values_len);
+  assert (xpath->values_len == ds->ds_num);
+  vl->values = values;
+
+  for (i = 0; i < xpath->values_len; i++)
+  {
+    status = cx_handle_single_value_xpath (xpath_ctx, xpath, ds, vl, i);
+    if (status != 0)
+      return (-1); /* An error has been printed. */
+  } /* for (i = 0; i < xpath->values_len; i++) */
+
+  plugin_dispatch_values (vl);
+  vl->values = NULL;
+
+  return (0);
+} /* }}} int cx_handle_all_value_xpaths */
+
+static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+    cx_xpath_t *xpath, value_list_t *vl,
+    _Bool is_table)
+{
+  xmlXPathObjectPtr instance_node_obj = NULL;
+  xmlNodeSetPtr instance_node = NULL;
+
+  memset (vl->type_instance, 0, sizeof (vl->type_instance));
+
+  /* If the base xpath returns more than one block, the result is assumed to be
+   * a table. The `Instnce' option is not optional in this case. Check for the
+   * condition and inform the user. */
+  if (is_table && (vl->type_instance == NULL))
+  {
+    WARNING ("curl_xml plugin: "
+        "Base-XPath %s is a table (more than one result was returned), "
+        "but no instance-XPath has been defined.",
+        xpath->path);
+    return (-1);
+  }
+
+  /* instance has to be an xpath expression */
+  if (xpath->instance != NULL)
+  {
+    int tmp_size;
+
+    instance_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->instance);
+    if (instance_node_obj == NULL)
+      return (-1); /* error is logged already */
+
+    instance_node = instance_node_obj->nodesetval;
+    tmp_size = (instance_node) ? instance_node->nodeNr : 0;
+
+    if ( (tmp_size == 0) && (is_table) )
+    {
+      WARNING ("curl_xml plugin: "
+          "relative xpath expression for 'InstanceFrom' \"%s\" doesn't match "
+          "any of the nodes. Skipping the node.", xpath->instance);
+      xmlXPathFreeObject (instance_node_obj);
+      return (-1);
+    }
+
+    if (tmp_size > 1)
+    {
+      WARNING ("curl_xml plugin: "
+          "relative xpath expression for 'InstanceFrom' \"%s\" is expected "
+          "to return only one text node. Skipping the node.", xpath->instance);
+      xmlXPathFreeObject (instance_node_obj);
+      return (-1);
+    }
+
+    /* ignoring the element if other than textnode/attribute */
+    if (cx_if_not_text_node(instance_node->nodeTab[0]))
+    {
+      WARNING ("curl_xml plugin: "
+          "relative xpath expression \"%s\" is expected to return only text node "
+          "which is not the case. Skipping the node.", xpath->instance);
+      xmlXPathFreeObject (instance_node_obj);
+      return (-1);
+    }
+  } /* if (xpath->instance != NULL) */
+
+  if (xpath->instance_prefix != NULL)
+  {
+    if (instance_node != NULL)
+      ssnprintf (vl->type_instance, sizeof (vl->type_instance),"%s%s",
+          xpath->instance_prefix, (char *) xmlNodeGetContent(instance_node->nodeTab[0]));
+    else
+      sstrncpy (vl->type_instance, xpath->instance_prefix,
+          sizeof (vl->type_instance));
+  }
+  else
+  {
+    /* If instance_prefix and instance_node are NULL, then
+     * don't set the type_instance */
+    if (instance_node != NULL)
+      sstrncpy (vl->type_instance, (char *) xmlNodeGetContent(instance_node->nodeTab[0]),
+          sizeof (vl->type_instance));
+  }
+
+  /* Free `instance_node_obj' this late, because `instance_node' points to
+   * somewhere inside this structure. */
+  xmlXPathFreeObject (instance_node_obj);
+
+  return (0);
+} /* }}} int cx_handle_instance_xpath */
+
+static int  cx_handle_base_xpath (char *plugin_instance, /* {{{ */
+    xmlXPathContextPtr xpath_ctx, const data_set_t *ds, 
+    char *base_xpath, cx_xpath_t *xpath)
+{
+  int total_nodes;
+  int i;
+
+  xmlXPathObjectPtr base_node_obj = NULL;
+  xmlNodeSetPtr base_nodes = NULL;
+
+  value_list_t vl = VALUE_LIST_INIT;
+
+  base_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST base_xpath); 
+  if (base_node_obj == NULL)
+    return -1; /* error is logged already */
+
+  base_nodes = base_node_obj->nodesetval;
+  total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
+
+  if (total_nodes == 0)
+  {
+     ERROR ("curl_xml plugin: "
+              "xpath expression \"%s\" doesn't match any of the nodes. "
+              "Skipping the xpath block...", base_xpath);
+     xmlXPathFreeObject (base_node_obj);
+     return -1;
+  }
+
+  /* If base_xpath returned multiple results, then */
+  /* Instance in the xpath block is required */ 
+  if (total_nodes > 1 && xpath->instance == NULL)
+  {
+    ERROR ("curl_xml plugin: "
+             "InstanceFrom is must in xpath block since the base xpath expression \"%s\" "
+             "returned multiple results. Skipping the xpath block...", base_xpath);
+    return -1;
+  }
+
+  /* set the values for the value_list */
+  vl.values_len = ds->ds_num;
+  sstrncpy (vl.type, xpath->type, sizeof (vl.type));
+  sstrncpy (vl.plugin, "curl_xml", sizeof (vl.plugin));
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  if (plugin_instance != NULL)
+    sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); 
+
+  for (i = 0; i < total_nodes; i++)
+  {
+    int status;
+
+    xpath_ctx->node = base_nodes->nodeTab[i];
+
+    status = cx_handle_instance_xpath (xpath_ctx, xpath, &vl,
+        /* is_table = */ (total_nodes > 1));
+    if (status != 0)
+      continue; /* An error has already been reported. */
+
+    status = cx_handle_all_value_xpaths (xpath_ctx, xpath, ds, &vl);
+    if (status != 0)
+      continue; /* An error has been logged. */
+  } /* for (i = 0; i < total_nodes; i++) */
+
+  /* free up the allocated memory */
+  xmlXPathFreeObject (base_node_obj); 
+
+  return (0); 
+} /* }}} cx_handle_base_xpath */
+
+static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */ 
+                       xmlXPathContextPtr xpath_ctx, cx_t *db)
+{
+  llentry_t *le;
+  const data_set_t *ds;
+  cx_xpath_t *xpath;
+  int status=-1;
+  
+
+  le = llist_head (db->list);
+  while (le != NULL)
+  {
+    /* get the ds */
+    xpath = (cx_xpath_t *) le->value;
+    ds = plugin_get_ds (xpath->type);
+
+    if ( (cx_check_type(ds, xpath) == 0) &&
+         (cx_handle_base_xpath(db->instance, xpath_ctx, ds, le->key, xpath) == 0) )
+      status = 0; /* we got atleast one success */
+
+    le = le->next;
+  } /* while (le != NULL) */
+
+  return status;
+} /* }}} cx_handle_parsed_xml */
+
+static int cx_parse_stats_xml(xmlChar* xml, cx_t *db) /* {{{ */
+{
+  int status;
+  xmlDocPtr doc;
+  xmlXPathContextPtr xpath_ctx;
+
+  /* Load the XML */
+  doc = xmlParseDoc(xml);
+  if (doc == NULL)
+  {
+    ERROR ("curl_xml plugin: Failed to parse the xml document  - %s", xml);
+    return (-1);
+  }
+
+  xpath_ctx = xmlXPathNewContext(doc);
+  if(xpath_ctx == NULL)
+  {
+    ERROR ("curl_xml plugin: Failed to create the xml context");
+    xmlFreeDoc(doc);
+    return (-1);
+  }
+
+  status = cx_handle_parsed_xml (doc, xpath_ctx, db);
+  /* Cleanup */
+  xmlXPathFreeContext(xpath_ctx);
+  xmlFreeDoc(doc);
+  return status;
+} /* }}} cx_parse_stats_xml */
+
+static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */
+{
+  int status;
+  long rc;
+  char *ptr;
+  char *url;
+
+  db->buffer_fill = 0; 
+  status = curl_easy_perform (curl);
+
+  curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
+  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
+
+  if (rc != 200)
+  {
+    ERROR ("curl_xml plugin: curl_easy_perform failed with response code %ld (%s)",
+           rc, url);
+    return (-1);
+  }
+
+  if (status != 0)
+  {
+    ERROR ("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
+           status, db->curl_errbuf, url);
+    return (-1);
+  }
+
+  ptr = db->buffer;
+
+  status = cx_parse_stats_xml(BAD_CAST ptr, db);
+  db->buffer_fill = 0;
+
+  return status;
+} /* }}} int cx_curl_perform */
+
+static int cx_read (user_data_t *ud) /* {{{ */
+{
+  cx_t *db;
+
+  if ((ud == NULL) || (ud->data == NULL))
+  {
+    ERROR ("curl_xml plugin: cx_read: Invalid user data.");
+    return (-1);
+  }
+
+  db = (cx_t *) ud->data;
+
+  return cx_curl_perform (db, db->curl);
+} /* }}} int cx_read */
+
+/* Configuration handling functions {{{ */
+
+static int cx_config_add_values (const char *name, cx_xpath_t *xpath, /* {{{ */
+                                      oconfig_item_t *ci)
+{
+  int i;
+
+  if (ci->values_num < 1)
+  {
+    WARNING ("curl_xml plugin: `ValuesFrom' needs at least one argument.");
+    return (-1);
+  }
+
+  for (i = 0; i < ci->values_num; i++)
+    if (ci->values[i].type != OCONFIG_TYPE_STRING)
+    {
+      WARNING ("curl_xml plugin: `ValuesFrom' needs only string argument.");
+      return (-1);
+    }
+
+  sfree (xpath->values);
+
+  xpath->values_len = 0;
+  xpath->values = (cx_values_t *) malloc (sizeof (cx_values_t) * ci->values_num);
+  if (xpath->values == NULL)
+    return (-1);
+  xpath->values_len = ci->values_num;
+
+  /* populate cx_values_t structure */
+  for (i = 0; i < ci->values_num; i++)
+  {
+    xpath->values[i].path_len = sizeof (ci->values[i].value.string);
+    sstrncpy (xpath->values[i].path, ci->values[i].value.string, sizeof (xpath->values[i].path));
+  }
+
+  return (0); 
+} /* }}} cx_config_add_values */
+
+static int cx_config_add_xpath (cx_t *db, /* {{{ */
+                                   oconfig_item_t *ci)
+{
+  cx_xpath_t *xpath;
+  int status;
+  int i;
+
+  xpath = (cx_xpath_t *) malloc (sizeof (*xpath));
+  if (xpath == NULL)
+  {
+    ERROR ("curl_xml plugin: malloc failed.");
+    return (-1);
+  }
+  memset (xpath, 0, sizeof (*xpath));
+
+  status = cf_util_get_string (ci, &xpath->path);
+  if (status != 0)
+  {
+    sfree (xpath);
+    return (status);
+  }
+
+  /* error out if xpath->path is an empty string */
+  if (*xpath->path == 0)
+  {
+    ERROR ("curl_xml plugin: invalid xpath. "
+           "xpath value can't be an empty string");
+    return (-1);
+  }
+
+  status = 0;
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Type", child->key) == 0)
+      status = cf_util_get_string (child, &xpath->type);
+    else if (strcasecmp ("InstancePrefix", child->key) == 0)
+      status = cf_util_get_string (child, &xpath->instance_prefix);
+    else if (strcasecmp ("InstanceFrom", child->key) == 0)
+      status = cf_util_get_string (child, &xpath->instance);
+    else if (strcasecmp ("ValuesFrom", child->key) == 0)
+      status = cx_config_add_values ("ValuesFrom", xpath, child);
+    else
+    {
+      WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  } /* for (i = 0; i < ci->children_num; i++) */
+
+  if (status == 0 && xpath->type == NULL)
+  {
+    WARNING ("curl_xml plugin: `Type' missing in `xpath' block.");
+    status = -1;
+  }
+
+  if (status == 0)
+  {
+    char *name;
+    llentry_t *le;
+
+    if (db->list == NULL)
+    {
+      db->list = llist_create();
+      if (db->list == NULL)
+      {
+        ERROR ("curl_xml plugin: list creation failed.");
+        return (-1);
+      }
+    }
+
+    name = strdup(xpath->path);
+    if (name == NULL)
+    {
+        ERROR ("curl_xml plugin: strdup failed.");
+        return (-1);
+    }
+
+    le = llentry_create (name, xpath);
+    if (le == NULL)
+    {
+      ERROR ("curl_xml plugin: llentry_create failed.");
+      return (-1);
+    }
+
+    llist_append (db->list, le);
+  }
+
+  return (status);
+} /* }}} int cx_config_add_xpath */
+
+/* Initialize db->curl */
+static int cx_init_curl (cx_t *db) /* {{{ */
+{
+  db->curl = curl_easy_init ();
+  if (db->curl == NULL)
+  {
+    ERROR ("curl_xml plugin: curl_easy_init failed.");
+    return (-1);
+  }
+
+  curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cx_curl_callback);
+  curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
+  curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
+                    PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
+  curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
+
+  if (db->user != NULL)
+  {
+    size_t credentials_size;
+
+    credentials_size = strlen (db->user) + 2;
+    if (db->pass != NULL)
+      credentials_size += strlen (db->pass);
+
+    db->credentials = (char *) malloc (credentials_size);
+    if (db->credentials == NULL)
+    {
+      ERROR ("curl_xml plugin: malloc failed.");
+      return (-1);
+    }
+
+    ssnprintf (db->credentials, credentials_size, "%s:%s",
+               db->user, (db->pass == NULL) ? "" : db->pass);
+    curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
+  }
+
+  curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer ? 1L : 0L);
+  curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST,
+                    db->verify_host ? 2L : 0L);
+  if (db->cacert != NULL)
+    curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
+
+  return (0);
+} /* }}} int cx_init_curl */
+
+static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
+{
+  cx_t *db;
+  int status = 0;
+  int i;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("curl_xml plugin: The `URL' block "
+             "needs exactly one string argument.");
+    return (-1);
+  }
+
+  db = (cx_t *) malloc (sizeof (*db));
+  if (db == NULL)
+  {
+    ERROR ("curl_xml plugin: malloc failed.");
+    return (-1);
+  }
+  memset (db, 0, sizeof (*db));
+
+  if (strcasecmp ("URL", ci->key) == 0)
+  {
+    status = cf_util_get_string (ci, &db->url);
+    if (status != 0)
+    {
+      sfree (db);
+      return (status);
+    }
+  }
+  else
+  {
+    ERROR ("curl_xml plugin: cx_config: "
+           "Invalid key: %s", ci->key);
+    return (-1);
+  }
+
+  /* Fill the `cx_t' structure.. */
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Instance", child->key) == 0)
+      status = cf_util_get_string (child, &db->instance);
+    else if (strcasecmp ("Host", child->key) == 0)
+      status = cf_util_get_string (child, &db->host);
+    else if (strcasecmp ("User", child->key) == 0)
+      status = cf_util_get_string (child, &db->user);
+    else if (strcasecmp ("Password", child->key) == 0)
+      status = cf_util_get_string (child, &db->pass);
+    else if (strcasecmp ("VerifyPeer", child->key) == 0)
+      status = cf_util_get_boolean (child, &db->verify_peer);
+    else if (strcasecmp ("VerifyHost", child->key) == 0)
+      status = cf_util_get_boolean (child, &db->verify_host);
+    else if (strcasecmp ("CACert", child->key) == 0)
+      status = cf_util_get_string (child, &db->cacert);
+    else if (strcasecmp ("xpath", child->key) == 0)
+      status = cx_config_add_xpath (db, child);
+    else
+    {
+      WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  if (status == 0)
+  {
+    if (db->list == NULL)
+    {
+      WARNING ("curl_xml plugin: No (valid) `Key' block "
+               "within `URL' block `%s'.", db->url);
+      status = -1;
+    }
+    if (status == 0)
+      status = cx_init_curl (db);
+  }
+
+  /* If all went well, register this database for reading */
+  if (status == 0)
+  {
+    user_data_t ud;
+    char cb_name[DATA_MAX_NAME_LEN];
+
+    if (db->instance == NULL)
+      db->instance = strdup("default");
+
+    DEBUG ("curl_xml plugin: Registering new read callback: %s",
+           db->instance);
+
+    memset (&ud, 0, sizeof (ud));
+    ud.data = (void *) db;
+    ud.free_func = cx_free;
+
+    ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s",
+               db->instance, db->url);
+
+    plugin_register_complex_read (/* group = */ NULL, cb_name, cx_read,
+                                  /* interval = */ NULL, &ud);
+  }
+  else
+  {
+    cx_free (db);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int cx_config_add_url */
+
+/* }}} End of configuration handling functions */
+
+static int cx_config (oconfig_item_t *ci) /* {{{ */
+{
+  int success;
+  int errors;
+  int status;
+  int i;
+
+  success = 0;
+  errors = 0;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("URL", child->key) == 0)
+    {
+      status = cx_config_add_url (child);
+      if (status == 0)
+        success++;
+      else
+        errors++;
+    }
+    else
+    {
+      WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+      errors++;
+    }
+  }
+
+  if ((success == 0) && (errors > 0))
+  {
+    ERROR ("curl_xml plugin: All statements failed.");
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int cx_config */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("curl_xml", cx_config);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
index ce4cd02..77f393f 100644 (file)
--- a/src/dbi.c
+++ b/src/dbi.c
@@ -46,6 +46,7 @@ struct cdbi_database_s /* {{{ */
   cdbi_driver_option_t *driver_options;
   size_t driver_options_num;
 
+  udb_query_preparation_area_t **q_prep_areas;
   udb_query_t **queries;
   size_t        queries_num;
 
@@ -162,6 +163,11 @@ static void cdbi_database_free (cdbi_database_t *db) /* {{{ */
   }
   sfree (db->driver_options);
 
+  if (db->q_prep_areas)
+    for (i = 0; i < db->queries_num; ++i)
+      udb_query_delete_preparation_area (db->q_prep_areas[i]);
+  free (db->q_prep_areas);
+
   sfree (db);
 } /* }}} void cdbi_database_free */
 
@@ -328,6 +334,34 @@ static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
     break;
   } /* while (status == 0) */
 
+  while ((status == 0) && (db->queries_num > 0))
+  {
+    db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+        db->queries_num, sizeof (*db->q_prep_areas));
+
+    if (db->q_prep_areas == NULL)
+    {
+      WARNING ("dbi plugin: malloc failed");
+      status = -1;
+      break;
+    }
+
+    for (i = 0; i < db->queries_num; ++i)
+    {
+      db->q_prep_areas[i]
+        = udb_query_allocate_preparation_area (db->queries[i]);
+
+      if (db->q_prep_areas[i] == NULL)
+      {
+        WARNING ("dbi plugin: udb_query_allocate_preparation_area failed");
+        status = -1;
+        break;
+      }
+    }
+
+    break;
+  }
+
   /* If all went well, add this database to the global list of databases. */
   if (status == 0)
   {
@@ -422,7 +456,7 @@ static int cdbi_init (void) /* {{{ */
 } /* }}} int cdbi_init */
 
 static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
-    udb_query_t *q)
+    udb_query_t *q, udb_query_preparation_area_t *prep_area)
 {
   const char *statement;
   dbi_result res;
@@ -530,8 +564,9 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
     sstrncpy (column_names[i], column_name, DATA_MAX_NAME_LEN);
   } /* }}} for (i = 0; i < column_num; i++) */
 
-  udb_query_prepare_result (q, hostname_g, /* plugin = */ "dbi", db->name,
-      column_names, column_num);
+  udb_query_prepare_result (q, prep_area, hostname_g,
+      /* plugin = */ "dbi", db->name,
+      column_names, column_num, /* interval = */ -1);
 
   /* 0 = error; 1 = success; */
   status = dbi_result_first_row (res); /* {{{ */
@@ -543,7 +578,7 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
         "return any rows?",
         db->name, udb_query_get_name (q),
         cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
-    udb_query_finish_result (q);
+    udb_query_finish_result (q, prep_area);
     BAIL_OUT (-1);
   } /* }}} */
 
@@ -572,7 +607,7 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
      * to dispatch the row to the daemon. */
     if (status == 0) /* {{{ */
     {
-      status = udb_query_handle_result (q, column_values);
+      status = udb_query_handle_result (q, prep_area, column_values);
       if (status != 0)
       {
         ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): "
@@ -598,7 +633,7 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
   } /* }}} while (42) */
 
   /* Tell the db query interface that we're done with this query. */
-  udb_query_finish_result (q);
+  udb_query_finish_result (q, prep_area);
 
   /* Clean up and return `status = 0' (success) */
   BAIL_OUT (0);
@@ -741,7 +776,8 @@ static int cdbi_read_database (cdbi_database_t *db) /* {{{ */
         && (udb_query_check_version (db->queries[i], db_version) == 0))
       continue;
 
-    status = cdbi_read_database_query (db, db->queries[i]);
+    status = cdbi_read_database_query (db,
+        db->queries[i], db->q_prep_areas[i]);
     if (status == 0)
       success++;
   }
index acc6cf6..681b94d 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/exec.c
- * Copyright (C) 2007-2009  Florian octo Forster
+ * Copyright (C) 2007-2010  Florian octo Forster
  * Copyright (C) 2007-2009  Sebastian Harl
  * Copyright (C) 2008       Peter Holik
  *
@@ -276,6 +276,7 @@ static void set_environment (void) /* {{{ */
   setenv ("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
 } /* }}} void set_environment */
 
+__attribute__((noreturn))
 static void exec_child (program_list_t *pl) /* {{{ */
 {
   int status;
@@ -292,8 +293,8 @@ static void exec_child (program_list_t *pl) /* {{{ */
   status = getpwnam_r (pl->user, &sp, nambuf, sizeof (nambuf), &sp_ptr);
   if (status != 0)
   {
-    ERROR ("exec plugin: getpwnam_r failed: %s",
-       sstrerror (errno, errbuf, sizeof (errbuf)));
+    ERROR ("exec plugin: Failed to get user information for user ``%s'': %s",
+       pl->user, sstrerror (errno, errbuf, sizeof (errbuf)));
     exit (-1);
   }
   if (sp_ptr == NULL)
@@ -322,7 +323,8 @@ static void exec_child (program_list_t *pl) /* {{{ */
       status = getgrnam_r (pl->group, &gr, nambuf, sizeof (nambuf), &gr_ptr);
       if (0 != status)
       {
-       ERROR ("exec plugin: getgrnam_r failed: %s",
+       ERROR ("exec plugin: Failed to get group information "
+           "for group ``%s'': %s", pl->group,
            sstrerror (errno, errbuf, sizeof (errbuf)));
        exit (-1);
       }
@@ -388,8 +390,8 @@ static void exec_child (program_list_t *pl) /* {{{ */
 
   status = execvp (pl->exec, pl->argv);
 
-  ERROR ("exec plugin: exec failed: %s",
-      sstrerror (errno, errbuf, sizeof (errbuf)));
+  ERROR ("exec plugin: Failed to execute ``%s'': %s",
+      pl->exec, sstrerror (errno, errbuf, sizeof (errbuf)));
   exit (-1);
 } /* void exec_child }}} */
 
@@ -549,7 +551,13 @@ static void *exec_read_one (void *arg) /* {{{ */
 
   status = fork_child (pl, NULL, &fd, &fd_err);
   if (status < 0)
+  {
+    /* Reset the "running" flag */
+    pthread_mutex_lock (&pl_lock);
+    pl->flags &= ~PL_RUNNING;
+    pthread_mutex_unlock (&pl_lock);
     pthread_exit ((void *) 1);
+  }
   pl->pid = status;
 
   assert (pl->pid != 0);
@@ -805,7 +813,7 @@ static int exec_read (void) /* {{{ */
   return (0);
 } /* int exec_read }}} */
 
-static int exec_notification (const notification_t *n,
+static int exec_notification (const notification_t *n, /* {{{ */
     user_data_t __attribute__((unused)) *user_data)
 {
   program_list_t *pl;
index 7c23a35..4b164dc 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/filter_chain.h
- * Copyright (C) 2008,2009  Florian octo Forster
+ * Copyright (C) 2008-2010  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
@@ -693,7 +693,14 @@ static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */
   if ((plugin_list == NULL) || (plugin_list[0] == NULL))
   {
     status = plugin_write (/* plugin = */ NULL, ds, vl);
-    if (status != 0)
+    if (status == ENOENT)
+    {
+      INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
+          "all write plugins failed with status %i (ENOENT). "
+          "Most likely this means you didn't load any write plugins.",
+          status);
+    }
+    else if (status != 0)
     {
       INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
           "all write plugins failed with status %i.", status);
index 87b189f..528ec9c 100644 (file)
@@ -1419,7 +1419,7 @@ static jint JNICALL cjni_api_register_read (JNIEnv *jvm_env, /* {{{ */
   ud.data = (void *) cbi;
   ud.free_func = cjni_callback_info_destroy;
 
-  plugin_register_complex_read (cbi->name, cjni_read,
+  plugin_register_complex_read (/* group = */ NULL, cbi->name, cjni_read,
       /* interval = */ NULL, &ud);
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_read);
index 7b96ac5..6d0f6e0 100644 (file)
@@ -39,12 +39,14 @@ static pthread_mutex_t file_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static char *log_file = NULL;
 static int print_timestamp = 1;
+static int print_severity = 0;
 
 static const char *config_keys[] =
 {
        "LogLevel",
        "File",
-       "Timestamp"
+       "Timestamp",
+       "PrintSeverity"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -78,6 +80,11 @@ static int logfile_config (const char *key, const char *value)
                        print_timestamp = 0;
                else
                        print_timestamp = 1;
+       } else if (0 == strcasecmp(key, "PrintSeverity")) {
+               if (IS_FALSE (value))
+                       print_severity = 0;
+               else
+                       print_severity = 1;
        }
        else {
                return -1;
@@ -85,12 +92,37 @@ static int logfile_config (const char *key, const char *value)
        return 0;
 } /* int logfile_config (const char *, const char *) */
 
-static void logfile_print (const char *msg, time_t timestamp_time)
+static void logfile_print (const char *msg, int severity, time_t timestamp_time)
 {
        FILE *fh;
        int do_close = 0;
        struct tm timestamp_tm;
        char timestamp_str[64];
+       char level_str[16] = "";
+
+       if (print_severity)
+       {
+               switch (severity)
+               {
+               case LOG_ERR:
+                       snprintf(level_str, sizeof (level_str), "[error] ");
+                       break;  
+               case LOG_WARNING:
+                       snprintf(level_str, sizeof (level_str), "[warning] ");
+                       break;
+               case LOG_NOTICE:
+                       snprintf(level_str, sizeof (level_str), "[notice] ");
+                       break;  
+               case LOG_INFO:
+                       snprintf(level_str, sizeof (level_str), "[info] ");
+                       break;  
+               case LOG_DEBUG:
+                       snprintf(level_str, sizeof (level_str), "[debug] ");
+                       break;  
+               default:
+                       break;
+               }
+       }
 
        if (print_timestamp)
        {
@@ -128,9 +160,9 @@ static void logfile_print (const char *msg, time_t timestamp_time)
        else
        {
                if (print_timestamp)
-                       fprintf (fh, "[%s] %s\n", timestamp_str, msg);
+                       fprintf (fh, "[%s] %s%s\n", timestamp_str, level_str, msg);
                else
-                       fprintf (fh, "%s\n", msg);
+                       fprintf (fh, "%s%s\n", level_str, msg);
 
                if (do_close != 0)
                        fclose (fh);
@@ -147,7 +179,7 @@ static void logfile_log (int severity, const char *msg,
        if (severity > log_level)
                return;
 
-       logfile_print (msg, time (NULL));
+       logfile_print (msg, severity, time (NULL));
 } /* void logfile_log (int, const char *) */
 
 static int logfile_notification (const notification_t *n,
@@ -185,7 +217,7 @@ static int logfile_notification (const notification_t *n,
 
        buf[sizeof (buf) - 1] = '\0';
 
-       logfile_print (buf,
+       logfile_print (buf, LOG_INFO,
                        (n->time > 0) ? n->time : time (NULL));
 
        return (0);
index 1fca889..1defc18 100644 (file)
@@ -58,6 +58,7 @@ struct mr_match_s
        mr_regex_t *plugin_instance;
        mr_regex_t *type;
        mr_regex_t *type_instance;
+       _Bool invert;
 };
 
 /*
@@ -195,6 +196,8 @@ static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
                return (-ENOMEM);
        }
        memset (m, 0, sizeof (*m));
+       
+       m->invert = 0;
 
        status = 0;
        for (i = 0; i < ci->children_num; i++)
@@ -212,6 +215,8 @@ static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
                        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 if (strcasecmp ("Invert", child->key) == 0)
+                       status = cf_util_get_boolean(child, &m->invert);
                else
                {
                        log_err ("The `%s' configuration option is not understood and "
@@ -263,26 +268,34 @@ static int mr_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
                void **user_data)
 {
        mr_match_t *m;
+       int match_value = FC_MATCH_MATCHES;
+       int nomatch_value = FC_MATCH_NO_MATCH;
 
        if ((user_data == NULL) || (*user_data == NULL))
                return (-1);
 
        m = *user_data;
 
+       if (m->invert)
+       {
+               match_value = FC_MATCH_NO_MATCH;
+               nomatch_value = FC_MATCH_MATCHES;
+       }
+
        if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH)
-               return (FC_MATCH_NO_MATCH);
+               return (nomatch_value);
        if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
-               return (FC_MATCH_NO_MATCH);
+               return (nomatch_value);
        if (mr_match_regexen (m->plugin_instance,
                                vl->plugin_instance) == FC_MATCH_NO_MATCH)
-               return (FC_MATCH_NO_MATCH);
+               return (nomatch_value);
        if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
-               return (FC_MATCH_NO_MATCH);
+               return (nomatch_value);
        if (mr_match_regexen (m->type_instance,
                                vl->type_instance) == FC_MATCH_NO_MATCH)
-               return (FC_MATCH_NO_MATCH);
+               return (nomatch_value);
 
-       return (FC_MATCH_MATCHES);
+       return (match_value);
 } /* }}} int mr_match */
 
 void module_register (void)
index 451a853..d066501 100644 (file)
@@ -37,6 +37,7 @@ typedef struct web_match_s web_match_t;
 struct web_match_s /* {{{ */
 {
   char *regex;
+  char *exclude_regex;
   int dstype;
   char *type;
   char *instance;
@@ -220,6 +221,8 @@ static int cmc_config_add_match (web_page_t *page, /* {{{ */
 
     if (strcasecmp ("Regex", child->key) == 0)
       status = cmc_config_add_string ("Regex", &match->regex, child);
+    else if (strcasecmp ("ExcludeRegex", child->key) == 0)
+      status = cmc_config_add_string ("ExcludeRegex", &match->exclude_regex, child);
     else if (strcasecmp ("DSType", child->key) == 0)
       status = cmc_config_add_match_dstype (&match->dstype, child);
     else if (strcasecmp ("Type", child->key) == 0)
@@ -262,7 +265,8 @@ static int cmc_config_add_match (web_page_t *page, /* {{{ */
   if (status != 0)
     return (status);
 
-  match->match = match_create_simple (match->regex, match->dstype);
+  match->match = match_create_simple (match->regex, match->exclude_regex,
+      match->dstype);
   if (match->match == NULL)
   {
     ERROR ("memcachec plugin: tail_match_add_match_simple failed.");
index b332157..348591f 100644 (file)
 # include <netinet/in.h>
 # include <netinet/tcp.h>
 
+/* Hack to work around the missing define in AIX */
+#ifndef MSG_DONTWAIT
+# define MSG_DONTWAIT MSG_NONBLOCK
+#endif
+
 #define MEMCACHED_DEF_HOST "127.0.0.1"
 #define MEMCACHED_DEF_PORT "11211"
 
diff --git a/src/modbus.c b/src/modbus.c
new file mode 100644 (file)
index 0000000..adab0d0
--- /dev/null
@@ -0,0 +1,862 @@
+/**
+ * collectd - src/modbus.c
+ * Copyright (C) 2010  noris network AG
+ *
+ * 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 noris.net>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <netdb.h>
+
+#include <modbus/modbus.h>
+
+#ifndef MODBUS_TCP_DEFAULT_PORT
+# ifdef MODBUS_TCP_PORT
+#  define MODBUS_TCP_DEFAULT_PORT MODBUS_TCP_PORT
+# else
+#  define MODBUS_TCP_DEFAULT_PORT 502
+# endif
+#endif
+
+/*
+ * <Data "data_name">
+ *   RegisterBase 1234
+ *   RegisterType float
+ *   Type gauge
+ *   Instance "..."
+ * </Data>
+ *
+ * <Host "name">
+ *   Address "addr"
+ *   Port "1234"
+ *   Interval 60
+ *
+ *   <Slave 1>
+ *     Instance "foobar" # optional
+ *     Collect "data_name"
+ *   </Slave>
+ * </Host>
+ */
+
+/*
+ * Data structures
+ */
+enum mb_register_type_e /* {{{ */
+{
+  REG_TYPE_UINT16,
+  REG_TYPE_UINT32,
+  REG_TYPE_FLOAT
+}; /* }}} */
+typedef enum mb_register_type_e mb_register_type_t;
+
+struct mb_data_s;
+typedef struct mb_data_s mb_data_t;
+struct mb_data_s /* {{{ */
+{
+  char *name;
+  int register_base;
+  mb_register_type_t register_type;
+  char type[DATA_MAX_NAME_LEN];
+  char instance[DATA_MAX_NAME_LEN];
+
+  mb_data_t *next;
+}; /* }}} */
+
+struct mb_slave_s /* {{{ */
+{
+  int id;
+  char instance[DATA_MAX_NAME_LEN];
+  mb_data_t *collect;
+}; /* }}} */
+typedef struct mb_slave_s mb_slave_t;
+
+struct mb_host_s /* {{{ */
+{
+  char host[DATA_MAX_NAME_LEN];
+  char node[NI_MAXHOST];
+  /* char service[NI_MAXSERV]; */
+  int port;
+  int interval;
+
+  mb_slave_t *slaves;
+  size_t slaves_num;
+
+  modbus_param_t connection;
+  _Bool is_connected;
+  _Bool have_reconnected;
+}; /* }}} */
+typedef struct mb_host_s mb_host_t;
+
+struct mb_data_group_s;
+typedef struct mb_data_group_s mb_data_group_t;
+struct mb_data_group_s /* {{{ */
+{
+  mb_data_t *registers;
+  size_t registers_num;
+
+  mb_data_group_t *next;
+}; /* }}} */
+
+/*
+ * Global variables
+ */
+static mb_data_t *data_definitions = NULL;
+
+/*
+ * Functions
+ */
+static mb_data_t *data_get_by_name (mb_data_t *src, /* {{{ */
+    const char *name)
+{
+  mb_data_t *ptr;
+
+  if (name == NULL)
+    return (NULL);
+
+  for (ptr = src; ptr != NULL; ptr = ptr->next)
+    if (strcasecmp (ptr->name, name) == 0)
+      return (ptr);
+
+  return (NULL);
+} /* }}} mb_data_t *data_get_by_name */
+
+static int data_append (mb_data_t **dst, mb_data_t *src) /* {{{ */
+{
+  mb_data_t *ptr;
+
+  if ((dst == NULL) || (src == NULL))
+    return (EINVAL);
+
+  ptr = *dst;
+
+  if (ptr == NULL)
+  {
+    *dst = src;
+    return (0);
+  }
+
+  while (ptr->next != NULL)
+    ptr = ptr->next;
+
+  ptr->next = src;
+
+  return (0);
+} /* }}} int data_append */
+
+/* Copy a single mb_data_t and append it to another list. */
+static int data_copy (mb_data_t **dst, const mb_data_t *src) /* {{{ */
+{
+  mb_data_t *tmp;
+  int status;
+
+  if ((dst == NULL) || (src == NULL))
+    return (EINVAL);
+
+  tmp = malloc (sizeof (*tmp));
+  if (tmp == NULL)
+    return (ENOMEM);
+  memcpy (tmp, src, sizeof (*tmp));
+  tmp->name = NULL;
+  tmp->next = NULL;
+
+  tmp->name = strdup (src->name);
+  if (tmp->name == NULL)
+  {
+    sfree (tmp);
+    return (ENOMEM);
+  }
+
+  status = data_append (dst, tmp);
+  if (status != 0)
+  {
+    sfree (tmp->name);
+    sfree (tmp);
+    return (status);
+  }
+
+  return (0);
+} /* }}} int data_copy */
+
+/* Lookup a single mb_data_t instance, copy it and append the copy to another
+ * list. */
+static int data_copy_by_name (mb_data_t **dst, mb_data_t *src, /* {{{ */
+    const char *name)
+{
+  mb_data_t *ptr;
+
+  if ((dst == NULL) || (src == NULL) || (name == NULL))
+    return (EINVAL);
+
+  ptr = data_get_by_name (src, name);
+  if (ptr == NULL)
+    return (ENOENT);
+
+  return (data_copy (dst, ptr));
+} /* }}} int data_copy_by_name */
+
+/* Read functions */
+
+static int mb_submit (mb_host_t *host, mb_slave_t *slave, /* {{{ */
+    mb_data_t *data, value_t value)
+{
+  value_list_t vl = VALUE_LIST_INIT;
+
+  if ((host == NULL) || (slave == NULL) || (data == NULL))
+    return (EINVAL);
+
+  if (host->interval <= 0)
+    host->interval = interval_g;
+
+  if (slave->instance[0] == 0)
+    ssnprintf (slave->instance, sizeof (slave->instance), "slave_%i",
+        slave->id);
+
+  vl.values = &value;
+  vl.values_len = 1;
+  vl.interval = host->interval;
+  sstrncpy (vl.host, host->host, sizeof (vl.host));
+  sstrncpy (vl.plugin, "modbus", sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, slave->instance, sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, data->type, sizeof (vl.type));
+  sstrncpy (vl.type_instance, data->instance, sizeof (vl.type_instance));
+
+  return (plugin_dispatch_values (&vl));
+} /* }}} int mb_submit */
+
+static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */
+{
+  union
+  {
+    uint8_t b[4];
+    float f;
+  } conv;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+  /* little endian */
+  conv.b[0] = lo & 0x00ff;
+  conv.b[1] = (lo >> 8) & 0x00ff;
+  conv.b[2] = hi & 0x00ff;
+  conv.b[3] = (hi >> 8) & 0x00ff;
+#else
+  conv.b[3] = lo & 0x00ff;
+  conv.b[2] = (lo >> 8) & 0x00ff;
+  conv.b[1] = hi & 0x00ff;
+  conv.b[0] = (hi >> 8) & 0x00ff;
+#endif
+
+  return (conv.f);
+} /* }}} float mb_register_to_float */
+
+static int mb_init_connection (mb_host_t *host) /* {{{ */
+{
+  int status;
+
+  if (host == NULL)
+    return (EINVAL);
+
+  if (host->is_connected)
+    return (0);
+
+  /* Only reconnect once per interval. */
+  if (host->have_reconnected)
+    return (-1);
+
+  modbus_set_debug (&host->connection, 1);
+
+#if 0
+  /* We'll do the error handling ourselves. */
+  modbus_set_error_handling (&host->connection, NOP_ON_ERROR);
+#endif
+
+  if ((host->port < 1) || (host->port > 65535))
+    host->port = MODBUS_TCP_DEFAULT_PORT;
+
+  DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
+      host->node, host->port);
+
+  modbus_init_tcp (&host->connection,
+      /* host = */ host->node);
+#if 0
+      /* port = */ host->port);
+#endif
+
+  status = modbus_connect (&host->connection);
+  if (status != 0)
+  {
+    ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.",
+        host->node, host->port, status);
+    return (status);
+  }
+
+  host->is_connected = 1;
+  host->have_reconnected = 1;
+  return (0);
+} /* }}} int mb_init_connection */
+
+#define CAST_TO_VALUE_T(ds,vt,raw) do { \
+  if ((ds)->ds[0].type == DS_TYPE_COUNTER) \
+    (vt).counter = (counter_t) (raw); \
+  else if ((ds)->ds[0].type == DS_TYPE_GAUGE) \
+    (vt).gauge = (gauge_t) (raw); \
+  else if ((ds)->ds[0].type == DS_TYPE_DERIVE) \
+    (vt).derive = (derive_t) (raw); \
+  else /* if (ds->ds[0].type == DS_TYPE_ABSOLUTE) */ \
+    (vt).absolute = (absolute_t) (raw); \
+} while (0)
+
+static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
+    mb_data_t *data)
+{
+  int values[2];
+  int values_num;
+  const data_set_t *ds;
+  int status;
+  int i;
+
+  if ((host == NULL) || (slave == NULL) || (data == NULL))
+    return (EINVAL);
+
+  ds = plugin_get_ds (data->type);
+  if (ds == NULL)
+  {
+    ERROR ("Modbus plugin: Type \"%s\" is not defined.", data->type);
+    return (-1);
+  }
+
+  if (ds->ds_num != 1)
+  {
+    ERROR ("Modbus plugin: The type \"%s\" has %i data sources. "
+        "I can only handle data sets with only one data source.",
+        data->type, ds->ds_num);
+    return (-1);
+  }
+
+  if ((ds->ds[0].type != DS_TYPE_GAUGE)
+      && (data->register_type != REG_TYPE_UINT32))
+  {
+    NOTICE ("Modbus plugin: The data source of type \"%s\" is %s, not gauge. "
+        "This will most likely result in problems, because the register type "
+        "is not UINT32.", data->type, DS_TYPE_TO_STRING (ds->ds[0].type));
+  }
+
+  memset (values, 0, sizeof (values));
+  if ((data->register_type == REG_TYPE_UINT32)
+      || (data->register_type == REG_TYPE_FLOAT))
+    values_num = 2;
+  else
+    values_num = 1;
+
+  for (i = 0; i < 2; i++)
+  {
+    status = read_holding_registers (&host->connection,
+        /* slave = */ slave->id, /* start_addr = */ data->register_base,
+        /* num_registers = */ values_num, /* buffer = */ values);
+    if (status > 0)
+      break;
+
+    if (host->is_connected)
+      modbus_close (&host->connection);
+    host->is_connected = 0;
+
+    /* If we already tried reconnecting this round, give up. */
+    if (host->have_reconnected)
+    {
+      ERROR ("Modbus plugin: read_holding_registers (%s) failed. "
+          "Reconnecting has already been tried. Giving up.", host->host);
+      return (-1);
+    }
+
+    /* Maybe the device closed the connection during the waiting interval.
+     * Try re-establishing the connection. */
+    status = mb_init_connection (host);
+    if (status != 0)
+    {
+      ERROR ("Modbus plugin: read_holding_registers (%s) failed. "
+          "While trying to reconnect, connecting to \"%s\" failed. "
+          "Giving up.",
+          host->host, host->node);
+      return (-1);
+    }
+
+    DEBUG ("Modbus plugin: Re-established connection to %s", host->host);
+
+    /* try again */
+    continue;
+  } /* for (i = 0, 1) */
+
+  DEBUG ("Modbus plugin: mb_read_data: Success! "
+      "read_holding_registers returned with status %i.", status);
+
+  if (data->register_type == REG_TYPE_FLOAT)
+  {
+    float float_value;
+    value_t vt;
+
+    float_value = mb_register_to_float (values[0], values[1]);
+    DEBUG ("Modbus plugin: mb_read_data: "
+        "Returned float value is %g", (double) float_value);
+
+    CAST_TO_VALUE_T (ds, vt, float_value);
+    mb_submit (host, slave, data, vt);
+  }
+  else if (data->register_type == REG_TYPE_UINT32)
+  {
+    uint32_t v32;
+    value_t vt;
+
+    v32 = (values[0] << 16) | values[1];
+    DEBUG ("Modbus plugin: mb_read_data: "
+        "Returned uint32 value is %"PRIu32, v32);
+
+    CAST_TO_VALUE_T (ds, vt, v32);
+    mb_submit (host, slave, data, vt);
+  }
+  else /* if (data->register_type == REG_TYPE_UINT16) */
+  {
+    value_t vt;
+
+    DEBUG ("Modbus plugin: mb_read_data: "
+        "Returned uint16 value is %"PRIu16, values[0]);
+
+    CAST_TO_VALUE_T (ds, vt, values[0]);
+    mb_submit (host, slave, data, vt);
+  }
+
+  return (0);
+} /* }}} int mb_read_data */
+
+static int mb_read_slave (mb_host_t *host, mb_slave_t *slave) /* {{{ */
+{
+  mb_data_t *data;
+  int success;
+  int status;
+
+  if ((host == NULL) || (slave == NULL))
+    return (EINVAL);
+
+  success = 0;
+  for (data = slave->collect; data != NULL; data = data->next)
+  {
+    status = mb_read_data (host, slave, data);
+    if (status == 0)
+      success++;
+  }
+
+  if (success == 0)
+    return (-1);
+  else
+    return (0);
+} /* }}} int mb_read_slave */
+
+static int mb_read (user_data_t *user_data) /* {{{ */
+{
+  mb_host_t *host;
+  size_t i;
+  int success;
+  int status;
+
+  if ((user_data == NULL) || (user_data->data == NULL))
+    return (EINVAL);
+
+  host = user_data->data;
+
+  /* Clear the reconnect flag. */
+  host->have_reconnected = 0;
+
+  success = 0;
+  for (i = 0; i < host->slaves_num; i++)
+  {
+    status = mb_read_slave (host, host->slaves + i);
+    if (status == 0)
+      success++;
+  }
+
+  if (success == 0)
+    return (-1);
+  else
+    return (0);
+} /* }}} int mb_read */
+
+/* Free functions */
+
+static void data_free_one (mb_data_t *data) /* {{{ */
+{
+  if (data == NULL)
+    return;
+
+  sfree (data->name);
+  sfree (data);
+} /* }}} void data_free_one */
+
+static void data_free_all (mb_data_t *data) /* {{{ */
+{
+  mb_data_t *next;
+
+  if (data == NULL)
+    return;
+
+  next = data->next;
+  data_free_one (data);
+
+  data_free_all (next);
+} /* }}} void data_free_all */
+
+static void slaves_free_all (mb_slave_t *slaves, size_t slaves_num) /* {{{ */
+{
+  size_t i;
+
+  if (slaves == NULL)
+    return;
+
+  for (i = 0; i < slaves_num; i++)
+    data_free_all (slaves[i].collect);
+  sfree (slaves);
+} /* }}} void slaves_free_all */
+
+static void host_free (void *void_host) /* {{{ */
+{
+  mb_host_t *host = void_host;
+
+  if (host == NULL)
+    return;
+
+  slaves_free_all (host->slaves, host->slaves_num);
+  sfree (host);
+} /* }}} void host_free */
+
+/* Config functions */
+
+static int mb_config_add_data (oconfig_item_t *ci) /* {{{ */
+{
+  mb_data_t data;
+  int status;
+  int i;
+
+  memset (&data, 0, sizeof (data));
+  data.name = NULL;
+  data.register_type = REG_TYPE_UINT16;
+  data.next = NULL;
+
+  status = cf_util_get_string (ci, &data.name);
+  if (status != 0)
+    return (status);
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Type", child->key) == 0)
+      status = cf_util_get_string_buffer (child,
+          data.type, sizeof (data.type));
+    else if (strcasecmp ("Instance", child->key) == 0)
+      status = cf_util_get_string_buffer (child,
+          data.instance, sizeof (data.instance));
+    else if (strcasecmp ("RegisterBase", child->key) == 0)
+      status = cf_util_get_int (child, &data.register_base);
+    else if (strcasecmp ("RegisterType", child->key) == 0)
+    {
+      char tmp[16];
+      status = cf_util_get_string_buffer (child, tmp, sizeof (tmp));
+      if (status != 0)
+        /* do nothing */;
+      else if (strcasecmp ("Uint16", tmp) == 0)
+        data.register_type = REG_TYPE_UINT16;
+      else if (strcasecmp ("Uint32", tmp) == 0)
+        data.register_type = REG_TYPE_UINT32;
+      else if (strcasecmp ("Float", tmp) == 0)
+        data.register_type = REG_TYPE_FLOAT;
+      else
+      {
+        ERROR ("Modbus plugin: The register type \"%s\" is unknown.", tmp);
+        status = -1;
+      }
+    }
+    else
+    {
+      ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  } /* for (i = 0; i < ci->children_num; i++) */
+
+  assert (data.name != NULL);
+  if (data.type[0] == 0)
+  {
+    ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.",
+        data.name);
+    status = -1;
+  }
+
+  if (status == 0)
+    data_copy (&data_definitions, &data);
+
+  sfree (data.name);
+
+  return (status);
+} /* }}} int mb_config_add_data */
+
+static int mb_config_set_host_address (mb_host_t *host, /* {{{ */
+    const char *address)
+{
+  struct addrinfo *ai_list;
+  struct addrinfo *ai_ptr;
+  struct addrinfo  ai_hints;
+  int status;
+
+  if ((host == NULL) || (address == NULL))
+    return (EINVAL);
+
+  memset (&ai_hints, 0, sizeof (ai_hints));
+#if AI_ADDRCONFIG
+  ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+  /* XXX: libmodbus can only handle IPv4 addresses. */
+  ai_hints.ai_family = AF_INET;
+  ai_hints.ai_addr = NULL;
+  ai_hints.ai_canonname = NULL;
+  ai_hints.ai_next = NULL;
+
+  ai_list = NULL;
+  status = getaddrinfo (address, /* service = */ NULL,
+      &ai_hints, &ai_list);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("Modbus plugin: getaddrinfo failed: %s",
+        (status == EAI_SYSTEM)
+        ? sstrerror (errno, errbuf, sizeof (errbuf))
+        : gai_strerror (status));
+    return (status);
+  }
+
+  for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    status = getnameinfo (ai_ptr->ai_addr, ai_ptr->ai_addrlen,
+        host->node, sizeof (host->node),
+        /* service = */ NULL, /* length = */ 0,
+        /* flags = */ NI_NUMERICHOST);
+    if (status == 0)
+      break;
+  } /* for (ai_ptr) */
+
+  freeaddrinfo (ai_list);
+
+  if (status != 0)
+    ERROR ("Modbus plugin: Unable to translate node name: \"%s\"", address);
+  else /* if (status == 0) */
+  {
+    DEBUG ("Modbus plugin: mb_config_set_host_address: %s -> %s",
+        address, host->node);
+  }
+
+  return (status);
+} /* }}} int mb_config_set_host_address */
+
+static int mb_config_add_slave (mb_host_t *host, oconfig_item_t *ci) /* {{{ */
+{
+  mb_slave_t *slave;
+  int status;
+  int i;
+
+  if ((host == NULL) || (ci == NULL))
+    return (EINVAL);
+
+  slave = realloc (host->slaves, sizeof (*slave) * (host->slaves_num + 1));
+  if (slave == NULL)
+    return (ENOMEM);
+  host->slaves = slave;
+  slave = host->slaves + host->slaves_num;
+  memset (slave, 0, sizeof (*slave));
+  slave->collect = NULL;
+
+  status = cf_util_get_int (ci, &slave->id);
+  if (status != 0)
+    return (status);
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Instance", child->key) == 0)
+      status = cf_util_get_string_buffer (child,
+          slave->instance, sizeof (slave->instance));
+    else if (strcasecmp ("Collect", child->key) == 0)
+    {
+      char buffer[1024];
+      status = cf_util_get_string_buffer (child, buffer, sizeof (buffer));
+      if (status == 0)
+        data_copy_by_name (&slave->collect, data_definitions, buffer);
+      status = 0; /* continue after failure. */
+    }
+    else
+    {
+      ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  if ((status == 0) && (slave->collect == NULL))
+    status = EINVAL;
+
+  if (slave->id < 0)
+    status = EINVAL;
+
+  if (status == 0)
+    host->slaves_num++;
+  else /* if (status != 0) */
+    data_free_all (slave->collect);
+
+  return (status);
+} /* }}} int mb_config_add_slave */
+
+static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */
+{
+  mb_host_t *host;
+  int status;
+  int i;
+
+  host = malloc (sizeof (*host));
+  if (host == NULL)
+    return (ENOMEM);
+  memset (host, 0, sizeof (*host));
+  host->slaves = NULL;
+
+  status = cf_util_get_string_buffer (ci, host->host, sizeof (host->host));
+  if (status != 0)
+    return (status);
+  if (host->host[0] == 0)
+    return (EINVAL);
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Address", child->key) == 0)
+    {
+      char buffer[NI_MAXHOST];
+      status = cf_util_get_string_buffer (child, buffer, sizeof (buffer));
+      if (status == 0)
+        status = mb_config_set_host_address (host, buffer);
+    }
+    else if (strcasecmp ("Port", child->key) == 0)
+    {
+      host->port = cf_util_get_port_number (child);
+      if (host->port <= 0)
+        status = -1;
+    }
+    else if (strcasecmp ("Interval", child->key) == 0)
+      status = cf_util_get_int (child, &host->interval);
+    else if (strcasecmp ("Slave", child->key) == 0)
+      /* Don't set status: Gracefully continue if a slave fails. */
+      mb_config_add_slave (host, child);
+    else
+    {
+      ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  } /* for (i = 0; i < ci->children_num; i++) */
+
+  assert (host->host[0] != 0);
+  if (host->host[0] == 0)
+  {
+    ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.",
+        host->host);
+    status = -1;
+  }
+
+  if (status == 0)
+  {
+    user_data_t ud;
+    char name[1024];
+    struct timespec interval;
+
+    ud.data = host;
+    ud.free_func = host_free;
+
+    ssnprintf (name, sizeof (name), "modbus-%s", host->host);
+
+    interval.tv_nsec = 0;
+    if (host->interval > 0)
+      interval.tv_sec = host->interval;
+    else
+      interval.tv_sec = 0;
+
+    plugin_register_complex_read (/* group = */ NULL, name,
+        mb_read, (interval.tv_sec > 0) ? &interval : NULL, &ud);
+  }
+  else
+  {
+    host_free (host);
+  }
+
+  return (status);
+} /* }}} int mb_config_add_host */
+
+static int mb_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  if (ci == NULL)
+    return (EINVAL);
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Data", child->key) == 0)
+      mb_config_add_data (child);
+    else if (strcasecmp ("Host", child->key) == 0)
+      mb_config_add_host (child);
+    else
+      ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+  }
+
+  return (0);
+} /* }}} int mb_config */
+
+/* ========= */
+
+static int mb_shutdown (void) /* {{{ */
+{
+  data_free_all (data_definitions);
+  data_definitions = NULL;
+
+  return (0);
+} /* }}} int mb_shutdown */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("modbus", mb_config);
+  plugin_register_shutdown ("modbus", mb_shutdown);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
index 1048511..c7b796b 100644 (file)
@@ -342,7 +342,8 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */
                else
                        sstrncpy (cb_name, "mysql", sizeof (cb_name));
 
-               plugin_register_complex_read (cb_name, mysql_read,
+               plugin_register_complex_read (/* group = */ NULL, cb_name,
+                                             mysql_read,
                                              /* interval = */ NULL, &ud);
        }
        else
@@ -397,7 +398,7 @@ static MYSQL *getconnection (mysql_database_t *db)
        }
        else
        {
-               INFO ("mysql plugin: Sucessfully connected to database %s "
+               INFO ("mysql plugin: Successfully connected to database %s "
                                "at server %s (server version: %s, protocol version: %d)",
                                (db->database != NULL) ? db->database : "<none>",
                                mysql_get_host_info (db->con),
index abd5612..317b0fe 100644 (file)
@@ -1490,7 +1490,7 @@ static void cna_handle_volume_snap_usage(const host_config_t *host, /* {{{ */
        {
                ERROR ("netapp plugin: cna_handle_volume_snap_usage: "
                                "na_elem_child (\"snapshots\") failed "
-                               "for host %s.", hostname);
+                               "for host %s.", host->name);
                na_elem_free(data);
                return;
        }
@@ -1525,7 +1525,7 @@ static int cna_handle_volume_usage_data (const host_config_t *host, /* {{{ */
        {
                ERROR ("netapp plugin: cna_handle_volume_usage_data: "
                                "na_elem_child (\"volumes\") failed "
-                               "for host %s.", hostname);
+                               "for host %s.", host->name);
                return (-1);
        }
 
@@ -2559,7 +2559,7 @@ static int cna_config (oconfig_item_t *ci) { /* {{{ */
                        ud.data = host;
                        ud.free_func = (void (*) (void *)) free_host_config;
 
-                       plugin_register_complex_read (cb_name,
+                       plugin_register_complex_read (/* group = */ NULL, cb_name,
                                        /* callback  = */ cna_read, 
                                        /* interval  = */ (host->interval > 0) ? &interval : NULL,
                                        /* user data = */ &ud);
index 8615753..457637b 100644 (file)
@@ -52,6 +52,9 @@
 #if HAVE_POLL_H
 # include <poll.h>
 #endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
 
 #if HAVE_LIBGCRYPT
 # include <gcrypt.h>
@@ -118,6 +121,7 @@ typedef struct sockent
 
        char *node;
        char *service;
+       int interface;
 
        union
        {
@@ -346,7 +350,8 @@ static _Bool check_send_okay (const value_list_t *vl) /* {{{ */
   return (!received);
 } /* }}} _Bool check_send_okay */
 
-static int network_dispatch_values (value_list_t *vl) /* {{{ */
+static int network_dispatch_values (value_list_t *vl, /* {{{ */
+    const char *username)
 {
   int status;
 
@@ -387,6 +392,18 @@ static int network_dispatch_values (value_list_t *vl) /* {{{ */
     return (status);
   }
 
+  if (username != NULL)
+  {
+    status = meta_data_add_string (vl->meta, "network:username", username);
+    if (status != 0)
+    {
+      ERROR ("network plugin: meta_data_add_string failed.");
+      meta_data_destroy (vl->meta);
+      vl->meta = NULL;
+      return (status);
+    }
+  }
+
   plugin_dispatch_values (vl);
   stats_values_dispatched++;
 
@@ -740,11 +757,11 @@ static int parse_part_values (void **ret_buffer, size_t *ret_buffer_len,
                    break;
 
                  default:
-                   sfree (pkg_types);
-                   sfree (pkg_values);
                    NOTICE ("network plugin: parse_part_values: "
                        "Don't know how to handle data source type %"PRIu8,
                        pkg_types[i]);
+                   sfree (pkg_types);
+                   sfree (pkg_values);
                    return (-1);
                } /* switch (pkg_types[i]) */
        }
@@ -888,7 +905,8 @@ static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
 #define PP_SIGNED    0x01
 #define PP_ENCRYPTED 0x02
 static int parse_packet (sockent_t *se,
-               void *buffer, size_t buffer_size, int flags);
+               void *buffer, size_t buffer_size, int flags,
+               const char *username);
 
 #define BUFFER_READ(p,s) do { \
   memcpy ((p), buffer + buffer_offset, (s)); \
@@ -983,6 +1001,8 @@ static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */
   {
     ERROR ("network plugin: gcry_md_setkey failed: %s", gcry_strerror (err));
     gcry_md_close (hd);
+    sfree (secret);
+    sfree (pss.username);
     return (-1);
   }
 
@@ -1004,9 +1024,6 @@ static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */
   gcry_md_close (hd);
   hd = NULL;
 
-  sfree (secret);
-  sfree (pss.username);
-
   if (memcmp (pss.hash, hash, sizeof (pss.hash)) != 0)
   {
     WARNING ("network plugin: Verifying HMAC-SHA-256 signature failed: "
@@ -1015,9 +1032,12 @@ static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */
   else
   {
     parse_packet (se, buffer + buffer_offset, buffer_len - buffer_offset,
-        flags | PP_SIGNED);
+        flags | PP_SIGNED, pss.username);
   }
 
+  sfree (secret);
+  sfree (pss.username);
+
   *ret_buffer = buffer + buffer_len;
   *ret_buffer_len = 0;
 
@@ -1061,7 +1081,8 @@ static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */
     warning_has_been_printed = 1;
   }
 
-  parse_packet (se, buffer + part_len, buffer_size - part_len, flags);
+  parse_packet (se, buffer + part_len, buffer_size - part_len, flags,
+      /* username = */ NULL);
 
   *ret_buffer = buffer + buffer_size;
   *ret_buffer_size = 0;
@@ -1140,7 +1161,10 @@ static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */
   cypher = network_get_aes256_cypher (se, pea.iv, sizeof (pea.iv),
       pea.username);
   if (cypher == NULL)
+  {
+    sfree (pea.username);
     return (-1);
+  }
 
   payload_len = part_size - (PART_ENCRYPTION_AES256_SIZE + username_len);
   assert (payload_len > 0);
@@ -1152,6 +1176,7 @@ static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */
       /* in = */ NULL, /* in len = */ 0);
   if (err != 0)
   {
+    sfree (pea.username);
     ERROR ("network plugin: gcry_cipher_decrypt returned: %s",
         gcry_strerror (err));
     return (-1);
@@ -1170,17 +1195,22 @@ static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */
       buffer + buffer_offset, payload_len);
   if (memcmp (hash, pea.hash, sizeof (hash)) != 0)
   {
+    sfree (pea.username);
     ERROR ("network plugin: Decryption failed: Checksum mismatch.");
     return (-1);
   }
 
   parse_packet (se, buffer + buffer_offset, payload_len,
-      flags | PP_ENCRYPTED);
+      flags | PP_ENCRYPTED, pea.username);
+
+  /* XXX: Free pea.username?!? */
 
   /* Update return values */
   *ret_buffer =     buffer     + part_size;
   *ret_buffer_len = buffer_len - part_size;
 
+  sfree (pea.username);
+
   return (0);
 } /* }}} int parse_part_encr_aes256 */
 /* #endif HAVE_LIBGCRYPT */
@@ -1235,7 +1265,8 @@ static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */
 #undef BUFFER_READ
 
 static int parse_packet (sockent_t *se, /* {{{ */
-               void *buffer, size_t buffer_size, int flags)
+               void *buffer, size_t buffer_size, int flags,
+               const char *username)
 {
        int status;
 
@@ -1335,7 +1366,7 @@ static int parse_packet (sockent_t *se, /* {{{ */
                        if (status != 0)
                                break;
 
-                       network_dispatch_values (&vl);
+                       network_dispatch_values (&vl, username);
 
                        sfree (vl.values);
                }
@@ -1549,7 +1580,7 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
 
                if (setsockopt (se->data.client.fd, IPPROTO_IP, optname,
                                        &network_config_ttl,
-                                       sizeof (network_config_ttl)) == -1)
+                                       sizeof (network_config_ttl)) != 0)
                {
                        char errbuf[1024];
                        ERROR ("setsockopt: %s",
@@ -1570,7 +1601,7 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
 
                if (setsockopt (se->data.client.fd, IPPROTO_IPV6, optname,
                                        &network_config_ttl,
-                                       sizeof (network_config_ttl)) == -1)
+                                       sizeof (network_config_ttl)) != 0)
                {
                        char errbuf[1024];
                        ERROR ("setsockopt: %s",
@@ -1583,7 +1614,110 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
        return (0);
 } /* int network_set_ttl */
 
-static int network_bind_socket (int fd, const struct addrinfo *ai)
+static int network_set_interface (const sockent_t *se, const struct addrinfo *ai) /* {{{ */
+{
+       DEBUG ("network plugin: network_set_interface: interface index = %i;",
+                       se->interface);
+
+        assert (se->type == SOCKENT_TYPE_CLIENT);
+
+       if (ai->ai_family == AF_INET)
+       {
+               struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
+
+               if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+               {
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+                       /* If possible, use the "ip_mreqn" structure which has
+                        * an "interface index" member. Using the interface
+                        * index is preferred here, because of its similarity
+                        * to the way IPv6 handles this. Unfortunately, it
+                        * appears not to be portable. */
+                       struct ip_mreqn mreq;
+
+                       memset (&mreq, 0, sizeof (mreq));
+                       mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+                       mreq.imr_address.s_addr = ntohl (INADDR_ANY);
+                       mreq.imr_ifindex = se->interface;
+#else
+                       struct ip_mreq mreq;
+
+                       memset (&mreq, 0, sizeof (mreq));
+                       mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+                       mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
+#endif
+
+                       if (setsockopt (se->data.client.fd, IPPROTO_IP, IP_MULTICAST_IF,
+                                               &mreq, sizeof (mreq)) != 0)
+                       {
+                               char errbuf[1024];
+                               ERROR ("setsockopt: %s",
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                               return (-1);
+                       }
+
+                       return (0);
+               }
+       }
+       else if (ai->ai_family == AF_INET6)
+       {
+               struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
+
+               if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+               {
+                       if (setsockopt (se->data.client.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+                                               &se->interface,
+                                               sizeof (se->interface)) != 0)
+                       {
+                               char errbuf[1024];
+                               ERROR ("setsockopt: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
+                               return (-1);
+                       }
+
+                       return (0);
+               }
+       }
+
+       /* else: Not a multicast interface. */
+#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE)
+       if (se->interface != 0)
+       {
+               char interface_name[IFNAMSIZ];
+
+               if (if_indextoname (se->interface, interface_name) == NULL)
+                       return (-1);
+
+               DEBUG ("network plugin: Binding socket to interface %s", interface_name);
+
+               if (setsockopt (se->data.client.fd, SOL_SOCKET, SO_BINDTODEVICE,
+                                       interface_name,
+                                       sizeof(interface_name)) == -1 )
+               {
+                       char errbuf[1024];
+                       ERROR ("setsockopt: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       return (-1);
+               }
+       }
+/* #endif HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
+
+#else
+       WARNING ("network plugin: Cannot set the interface on a unicast "
+                       "socket because "
+# if !defined(SO_BINDTODEVICE)
+                       "the the \"SO_BINDTODEVICE\" socket option "
+# else
+                       "the \"if_indextoname\" function "
+# endif
+                       "is not available on your system.");
+#endif
+
+       return (0);
+} /* }}} network_set_interface */
+
+static int network_bind_socket (int fd, const struct addrinfo *ai, const int interface_idx)
 {
        int loop = 0;
        int yes  = 1;
@@ -1612,12 +1746,24 @@ static int network_bind_socket (int fd, const struct addrinfo *ai)
                struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
                if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
                {
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+                       struct ip_mreqn mreq;
+#else
                        struct ip_mreq mreq;
+#endif
 
                        DEBUG ("fd = %i; IPv4 multicast address found", fd);
 
                        mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
-                       mreq.imr_interface.s_addr = htonl (INADDR_ANY);
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+                       /* Set the interface using the interface index if
+                        * possible (available). Unfortunately, the struct
+                        * ip_mreqn is not portable. */
+                       mreq.imr_address.s_addr = ntohl (INADDR_ANY);
+                       mreq.imr_ifindex = interface_idx;
+#else
+                       mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
+#endif
 
                        if (setsockopt (fd, IPPROTO_IP, IP_MULTICAST_LOOP,
                                                &loop, sizeof (loop)) == -1)
@@ -1638,6 +1784,8 @@ static int network_bind_socket (int fd, const struct addrinfo *ai)
                                                        sizeof (errbuf)));
                                return (-1);
                        }
+
+                       return (0);
                }
        }
        else if (ai->ai_family == AF_INET6)
@@ -1663,7 +1811,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai)
                         * single interface; programs running on
                         * multihomed hosts may need to join the same
                         * group on more than one interface.*/
-                       mreq.ipv6mr_interface = 0;
+                       mreq.ipv6mr_interface = interface_idx;
 
                        if (setsockopt (fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
                                                &loop, sizeof (loop)) == -1)
@@ -1684,9 +1832,36 @@ static int network_bind_socket (int fd, const struct addrinfo *ai)
                                                        sizeof (errbuf)));
                                return (-1);
                        }
+
+                       return (0);
                }
        }
 
+#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE)
+       /* if a specific interface was set, bind the socket to it. But to avoid
+        * possible problems with multicast routing, only do that for non-multicast
+        * addresses */
+       if (interface_idx != 0)
+       {
+               char interface_name[IFNAMSIZ];
+
+               if (if_indextoname (interface_idx, interface_name) == NULL)
+                       return (-1);
+
+               DEBUG ("fd = %i; Binding socket to interface %s", fd, interface_name);
+
+               if (setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE,
+                                       interface_name,
+                                       sizeof(interface_name)) == -1 )
+               {
+                       char errbuf[1024];
+                       ERROR ("setsockopt: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       return (-1);
+               }
+       }
+#endif /* HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
+
        return (0);
 } /* int network_bind_socket */
 
@@ -1702,6 +1877,7 @@ static int sockent_init (sockent_t *se, int type) /* {{{ */
        se->type = SOCKENT_TYPE_CLIENT;
        se->node = NULL;
        se->service = NULL;
+       se->interface = 0;
        se->next = NULL;
 
        if (type == SOCKENT_TYPE_SERVER)
@@ -1850,7 +2026,7 @@ static int sockent_open (sockent_t *se) /* {{{ */
                                continue;
                        }
 
-                       status = network_bind_socket (*tmp, ai_ptr);
+                       status = network_bind_socket (*tmp, ai_ptr, se->interface);
                        if (status != 0)
                        {
                                close (*tmp);
@@ -1890,6 +2066,7 @@ static int sockent_open (sockent_t *se) /* {{{ */
                        se->data.client.addrlen = ai_ptr->ai_addrlen;
 
                        network_set_ttl (se, ai_ptr);
+                       network_set_interface (se, ai_ptr);
 
                        /* We don't open more than one write-socket per
                         * node/service pair.. */
@@ -2023,7 +2200,8 @@ static void *dispatch_thread (void __attribute__((unused)) *arg) /* {{{ */
       continue;
     }
 
-    parse_packet (se, ent->data, ent->data_len, /* flags = */ 0);
+    parse_packet (se, ent->data, ent->data_len, /* flags = */ 0,
+       /* username = */ NULL);
     sfree (ent->data);
     sfree (ent);
   } /* while (42) */
@@ -2601,6 +2779,25 @@ static int network_config_set_ttl (const oconfig_item_t *ci) /* {{{ */
   return (0);
 } /* }}} int network_config_set_ttl */
 
+static int network_config_set_interface (const oconfig_item_t *ci, /* {{{ */
+    int *interface)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("network plugin: The `Interface' config option needs exactly "
+        "one string argument.");
+    return (-1);
+  }
+
+  if (interface == NULL)
+    return (-1);
+
+  *interface = if_nametoindex (ci->values[0].value.string);
+
+  return (0);
+} /* }}} int network_config_set_interface */
+
 static int network_config_set_buffer_size (const oconfig_item_t *ci) /* {{{ */
 {
   int tmp;
@@ -2712,6 +2909,10 @@ static int network_config_add_listen (const oconfig_item_t *ci) /* {{{ */
           &se->data.server.security_level);
     else
 #endif /* HAVE_LIBGCRYPT */
+    if (strcasecmp ("Interface", child->key) == 0)
+      network_config_set_interface (child,
+          &se->interface);
+    else
     {
       WARNING ("network plugin: Option `%s' is not allowed here.",
           child->key);
@@ -2790,6 +2991,10 @@ static int network_config_add_server (const oconfig_item_t *ci) /* {{{ */
           &se->data.client.security_level);
     else
 #endif /* HAVE_LIBGCRYPT */
+    if (strcasecmp ("Interface", child->key) == 0)
+      network_config_set_interface (child,
+          &se->interface);
+    else
     {
       WARNING ("network plugin: Option `%s' is not allowed here.",
           child->key);
index cae0d63..462458c 100644 (file)
@@ -310,7 +310,7 @@ static int cow_init (void)
   if (ow_interval > 0)
     cb_interval.tv_sec = (time_t) ow_interval;
 
-  plugin_register_complex_read ("onewire", cow_read,
+  plugin_register_complex_read (/* group = */ NULL, "onewire", cow_read,
       &cb_interval, /* user data = */ NULL);
   plugin_register_shutdown ("onewire", cow_shutdown);
 
index 1ac5083..2aca414 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (C) 2008  Doug MacEachern
  * Copyright (C) 2009  Florian octo Forster
  * Copyright (C) 2009  Marco Chiappero
+ * Copyright (C) 2009  Fabian Schuh
  *
  * 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
@@ -21,6 +22,7 @@
  *   Doug MacEachern <dougm at hyperic.com>
  *   Florian octo Forster <octo at verplant.org>
  *   Marco Chiappero <marco at absence.it>
+ *   Fabian Schuh <mail at xeroc.org>
  **/
 
 #include "collectd.h"
@@ -50,14 +52,19 @@ typedef struct vpn_status_s vpn_status_t;
 static vpn_status_t **vpn_list = NULL;
 static int vpn_num = 0;
 
-static int store_compression = 1;
-static int new_naming_schema = 0;
+static _Bool new_naming_schema = 0;
+static _Bool collect_compression = 1;
+static _Bool collect_user_count  = 0;
+static _Bool collect_individual_users  = 1;
 
 static const char *config_keys[] =
 {
        "StatusFile",
-       "Compression",
-       "ImprovedNamingSchema"
+       "Compression", /* old, deprecated name */
+       "ImprovedNamingSchema",
+       "CollectCompression",
+       "CollectUserCount",
+       "CollectIndividualUsers"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -85,6 +92,27 @@ static int openvpn_strsplit (char *string, char **fields, size_t size)
        return (i);
 } /* int openvpn_strsplit */
 
+/* dispatches number of users */
+static void numusers_submit (char *pinst, char *tinst, gauge_t value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = value;
+
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "openvpn", sizeof (vl.plugin));
+       sstrncpy (vl.type, "users", sizeof (vl.type));
+       if (pinst != NULL)
+               sstrncpy (vl.plugin_instance, pinst, sizeof (vl.plugin_instance));
+       if (tinst != NULL)
+               sstrncpy (vl.type_instance, tinst, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* void numusers_submit */
+
 /* dispatches stats about traffic (TCP or UDP) generated by the tunnel per single endpoint */
 static void iostats_submit (char *pinst, char *tinst, counter_t rx, counter_t tx)
 {
@@ -221,7 +249,7 @@ static int single_read (char *name, FILE *fh)
 
        iostats_submit (name, "overhead", overhead_rx, overhead_tx);
 
-       if (store_compression)
+       if (collect_compression)
        {
                compression_submit (name, "data_in", post_decompress, pre_decompress);
                compression_submit (name, "data_out", pre_compress, post_compress);
@@ -238,6 +266,7 @@ static int multi1_read (char *name, FILE *fh)
        char buffer[1024];
        char *fields[10];
        int  fields_num, read = 0, found_header = 0;
+       long long sum_users = 0;
 
        /* read the file until the "ROUTING TABLE" line is found (no more info after) */
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
@@ -261,24 +290,38 @@ static int multi1_read (char *name, FILE *fh)
                if (fields_num < 4)
                        continue;
 
-               if (new_naming_schema)
+               if (collect_user_count)
+                       /* If so, sum all users, ignore the individuals*/
                {
-                       iostats_submit (fields[0],          /* "Common Name" */
-                                       NULL,               /* unused when in multimode */
-                                       atoll (fields[2]),  /* "Bytes Received" */
-                                       atoll (fields[3])); /* "Bytes Sent" */
+                       sum_users += 1;
                }
-               else
+               if (collect_individual_users)
                {
-                       iostats_submit (name,               /* vpn instance */
-                                       fields[0],          /* "Common Name" */
-                                       atoll (fields[2]),  /* "Bytes Received" */
-                                       atoll (fields[3])); /* "Bytes Sent" */
+                       if (new_naming_schema)
+                       {
+                               iostats_submit (name,               /* vpn instance */
+                                               fields[0],          /* "Common Name" */
+                                               atoll (fields[2]),  /* "Bytes Received" */
+                                               atoll (fields[3])); /* "Bytes Sent" */
+                       }
+                       else
+                       {
+                               iostats_submit (fields[0],          /* "Common Name" */
+                                               NULL,               /* unused when in multimode */
+                                               atoll (fields[2]),  /* "Bytes Received" */
+                                               atoll (fields[3])); /* "Bytes Sent" */
+                       }
                }
 
                read = 1;
        }
 
+       if (collect_user_count)
+       {
+               numusers_submit(name, name, sum_users);
+               read = 1;
+       }
+
        return (read);
 } /* int multi1_read */
 
@@ -289,6 +332,7 @@ static int multi2_read (char *name, FILE *fh)
        char *fields[10];
        const int max_fields = STATIC_ARRAY_SIZE (fields);
        int  fields_num, read = 0;
+       long long sum_users    = 0;
 
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
@@ -306,26 +350,40 @@ static int multi2_read (char *name, FILE *fh)
                if (strcmp (fields[0], "CLIENT_LIST") != 0)
                        continue;
 
-               if (new_naming_schema)
+               if (collect_user_count)
+                       /* If so, sum all users, ignore the individuals*/
                {
-                       /* plugin inst = file name, type inst = fields[1] */
-                       iostats_submit (name,               /* vpn instance */
-                                       fields[1],          /* "Common Name" */
-                                       atoll (fields[4]),  /* "Bytes Received" */
-                                       atoll (fields[5])); /* "Bytes Sent" */
+                       sum_users += 1;
                }
-               else
+               if (collect_individual_users)
                {
-                       /* plugin inst = fields[1], type inst = "" */
-                       iostats_submit (fields[1],          /* "Common Name" */
-                                       NULL,               /* unused when in multimode */
-                                       atoll (fields[4]),  /* "Bytes Received" */
-                                       atoll (fields[5])); /* "Bytes Sent" */
+                       if (new_naming_schema)
+                       {
+                               /* plugin inst = file name, type inst = fields[1] */
+                               iostats_submit (name,               /* vpn instance */
+                                               fields[1],          /* "Common Name" */
+                                               atoll (fields[4]),  /* "Bytes Received" */
+                                               atoll (fields[5])); /* "Bytes Sent" */
+                       }
+                       else
+                       {
+                               /* plugin inst = fields[1], type inst = "" */
+                               iostats_submit (fields[1],          /* "Common Name" */
+                                               NULL,               /* unused when in multimode */
+                                               atoll (fields[4]),  /* "Bytes Received" */
+                                               atoll (fields[5])); /* "Bytes Sent" */
+                       }
                }
 
                read = 1;
        }
 
+       if (collect_user_count)
+       {
+               numusers_submit(name, name, sum_users);
+               read = 1;
+       }
+
        return (read);
 } /* int multi2_read */
 
@@ -336,6 +394,7 @@ static int multi3_read (char *name, FILE *fh)
        char *fields[15];
        const int max_fields = STATIC_ARRAY_SIZE (fields);
        int  fields_num, read = 0;
+       long long sum_users    = 0;
 
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
@@ -356,25 +415,40 @@ static int multi3_read (char *name, FILE *fh)
                        if (strcmp (fields[0], "CLIENT_LIST") != 0)
                                continue;
 
-                       if (new_naming_schema)
+                       if (collect_user_count)
+                               /* If so, sum all users, ignore the individuals*/
                        {
-                               iostats_submit (name,               /* vpn instance */
-                                               fields[1],          /* "Common Name" */
-                                               atoll (fields[4]),  /* "Bytes Received" */
-                                               atoll (fields[5])); /* "Bytes Sent" */
+                               sum_users += 1;
                        }
-                       else
+
+                       if (collect_individual_users)
                        {
-                               iostats_submit (fields[1],          /* "Common Name" */
-                                               NULL,               /* unused when in multimode */
-                                               atoll (fields[4]),  /* "Bytes Received" */
-                                               atoll (fields[5])); /* "Bytes Sent" */
+                               if (new_naming_schema)
+                               {
+                                       iostats_submit (name,               /* vpn instance */
+                                                       fields[1],          /* "Common Name" */
+                                                       atoll (fields[4]),  /* "Bytes Received" */
+                                                       atoll (fields[5])); /* "Bytes Sent" */
+                               }
+                               else
+                               {
+                                       iostats_submit (fields[1],          /* "Common Name" */
+                                                       NULL,               /* unused when in multimode */
+                                                       atoll (fields[4]),  /* "Bytes Received" */
+                                                       atoll (fields[5])); /* "Bytes Sent" */
+                               }
                        }
 
                        read = 1;
                }
        }
 
+       if (collect_user_count)
+       {
+               numusers_submit(name, name, sum_users);
+               read = 1;
+       }
+
        return (read);
 } /* int multi3_read */
 
@@ -394,7 +468,7 @@ static int openvpn_read (void)
                {
                        char errbuf[1024];
                        WARNING ("openvpn plugin: fopen(%s) failed: %s", vpn_list[i]->file,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
 
                        continue;
                }
@@ -506,7 +580,7 @@ static int openvpn_config (const char *key, const char *value)
                if (status_version == 0)
                {
                        WARNING ("openvpn plugin: unable to detect status version, \
-                               discarding status file \"%s\".", value);
+                                       discarding status file \"%s\".", value);
                        return (1);
                }
 
@@ -515,7 +589,7 @@ static int openvpn_config (const char *key, const char *value)
                {
                        char errbuf[1024];
                        WARNING ("openvpn plugin: sstrdup failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                        return (1);
                }
 
@@ -556,7 +630,7 @@ static int openvpn_config (const char *key, const char *value)
                {
                        char errbuf[1024];
                        ERROR ("openvpn plugin: malloc failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
 
                        sfree (temp->file);
                        sfree (temp);
@@ -569,16 +643,14 @@ static int openvpn_config (const char *key, const char *value)
                DEBUG ("openvpn plugin: status file \"%s\" added", temp->file);
 
        } /* if (strcasecmp ("StatusFile", key) == 0) */
-       else if (strcasecmp ("Compression", key) == 0)
+       else if ((strcasecmp ("CollectCompression", key) == 0)
+               || (strcasecmp ("Compression", key) == 0)) /* old, deprecated name */
        {
-               if (IS_TRUE (value))
-                       store_compression = 1;
+               if (IS_FALSE (value))
+                       collect_compression = 0;
                else
-               {
-                       store_compression = 0;
-                       DEBUG ("openvpn plugin: no 'compression statistcs' collected");
-               }
-       } /* if (strcasecmp ("Compression", key) == 0) */
+                       collect_compression = 1;
+       } /* if (strcasecmp ("CollectCompression", key) == 0) */
        else if (strcasecmp ("ImprovedNamingSchema", key) == 0)
        {
                if (IS_TRUE (value))
@@ -591,6 +663,20 @@ static int openvpn_config (const char *key, const char *value)
                        new_naming_schema = 0;
                }
        } /* if (strcasecmp ("ImprovedNamingSchema", key) == 0) */
+       else if (strcasecmp("CollectUserCount", key) == 0)
+       {
+               if (IS_TRUE(value))
+                       collect_user_count = 1;
+               else
+                       collect_user_count = 0;
+       } /* if (strcasecmp("CollectUserCount", key) == 0) */
+       else if (strcasecmp("CollectIndividualUsers", key) == 0)
+       {
+               if (IS_FALSE (value))
+                       collect_individual_users = 0;
+               else
+                       collect_individual_users = 1;
+       } /* if (strcasecmp("CollectIndividualUsers", key) == 0) */
        else
        {
                return (-1);
@@ -615,12 +701,29 @@ static int openvpn_shutdown (void)
        return (0);
 } /* int openvpn_shutdown */
 
+static int openvpn_init (void)
+{
+       if (!collect_individual_users
+                       && !collect_compression
+                       && !collect_user_count)
+       {
+               WARNING ("OpenVPN plugin: Neither `CollectIndividualUsers', "
+                               "`CollectCompression', nor `CollectUserCount' is true. There's no "
+                               "data left to collect.");
+               return (-1);
+       }
+
+       plugin_register_read ("openvpn", openvpn_read);
+       plugin_register_shutdown ("openvpn", openvpn_shutdown);
+
+       return (0);
+} /* int openvpn_init */
+
 void module_register (void)
 {
        plugin_register_config ("openvpn", openvpn_config,
                        config_keys, config_keys_num);
-       plugin_register_read ("openvpn", openvpn_read);
-       plugin_register_shutdown ("openvpn", openvpn_shutdown);
+       plugin_register_init ("openvpn", openvpn_init);
 } /* void module_register */
 
 /* vim: set sw=2 ts=2 : */
index 49cf657..3fe2125 100644 (file)
@@ -62,6 +62,7 @@ struct o_database_s
   char *username;
   char *password;
 
+  udb_query_preparation_area_t **q_prep_areas;
   udb_query_t **queries;
   size_t        queries_num;
 
@@ -121,6 +122,8 @@ static void o_report_error (const char *where, /* {{{ */
 
 static void o_database_free (o_database_t *db) /* {{{ */
 {
+  size_t i;
+
   if (db == NULL)
     return;
 
@@ -130,6 +133,11 @@ static void o_database_free (o_database_t *db) /* {{{ */
   sfree (db->password);
   sfree (db->queries);
 
+  if (db->q_prep_areas != NULL)
+    for (i = 0; i < db->queries_num; ++i)
+      udb_query_delete_preparation_area (db->q_prep_areas[i]);
+  free (db->q_prep_areas);
+
   sfree (db);
 } /* }}} void o_database_free */
 
@@ -256,6 +264,34 @@ static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
     break;
   } /* while (status == 0) */
 
+  while ((status == 0) && (db->queries_num > 0))
+  {
+    db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+        db->queries_num, sizeof (*db->q_prep_areas));
+
+    if (db->q_prep_areas == NULL)
+    {
+      WARNING ("oracle plugin: malloc failed");
+      status = -1;
+      break;
+    }
+
+    for (i = 0; i < db->queries_num; ++i)
+    {
+      db->q_prep_areas[i]
+        = udb_query_allocate_preparation_area (db->queries[i]);
+
+      if (db->q_prep_areas[i] == NULL)
+      {
+        WARNING ("oracle plugin: udb_query_allocate_preparation_area failed");
+        status = -1;
+        break;
+      }
+    }
+
+    break;
+  }
+
   /* If all went well, add this query to the list of queries within the
    * database structure. */
   if (status == 0)
@@ -349,7 +385,7 @@ static int o_init (void) /* {{{ */
 } /* }}} int o_init */
 
 static int o_read_database_query (o_database_t *db, /* {{{ */
-    udb_query_t *q)
+    udb_query_t *q, udb_query_preparation_area_t *prep_area)
 {
   char **column_names;
   char **column_values;
@@ -548,8 +584,9 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
   } /* for (j = 1; j <= param_counter; j++) */
   /* }}} End of the ``define'' stuff. */
 
-  status = udb_query_prepare_result (q, hostname_g, /* plugin = */ "oracle",
-      db->name, column_names, column_num);
+  status = udb_query_prepare_result (q, prep_area, hostname_g,
+      /* plugin = */ "oracle", db->name, column_names, column_num,
+      /* interval = */ -1);
   if (status != 0)
   {
     ERROR ("oracle plugin: o_read_database_query (%s, %s): "
@@ -576,7 +613,7 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
       break;
     }
 
-    status = udb_query_handle_result (q, column_values);
+    status = udb_query_handle_result (q, prep_area, column_values);
     if (status != 0)
     {
       WARNING ("oracle plugin: o_read_database_query (%s, %s): "
@@ -612,16 +649,22 @@ static int o_read_database (o_database_t *db) /* {{{ */
       o_report_error ("o_read_database", "OCIAttrGet", oci_error);
       return (-1);
     }
-    assert (server_handle != NULL);
 
-    connection_status = 0;
-    status = OCIAttrGet ((void *) server_handle, OCI_HTYPE_SERVER,
-        (void *) &connection_status, /* size pointer = */ NULL,
-        OCI_ATTR_SERVER_STATUS, oci_error);
-    if (status != OCI_SUCCESS)
+    if (server_handle == NULL)
     {
-      o_report_error ("o_read_database", "OCIAttrGet", oci_error);
-      return (-1);
+      connection_status = OCI_SERVER_NOT_CONNECTED;
+    }
+    else /* if (server_handle != NULL) */
+    {
+      connection_status = 0;
+      status = OCIAttrGet ((void *) server_handle, OCI_HTYPE_SERVER,
+          (void *) &connection_status, /* size pointer = */ NULL,
+          OCI_ATTR_SERVER_STATUS, oci_error);
+      if (status != OCI_SUCCESS)
+      {
+        o_report_error ("o_read_database", "OCIAttrGet", oci_error);
+        return (-1);
+      }
     }
 
     if (connection_status != OCI_SERVER_NORMAL)
@@ -655,7 +698,7 @@ static int o_read_database (o_database_t *db) /* {{{ */
       db->connect_id, db->oci_service_context);
 
   for (i = 0; i < db->queries_num; i++)
-    o_read_database_query (db, db->queries[i]);
+    o_read_database_query (db, db->queries[i], db->q_prep_areas[i]);
 
   return (0);
 } /* }}} int o_read_database */
index 66abb44..bf7327c 100644 (file)
@@ -173,7 +173,8 @@ dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
        t = GET_TARGET(e);
        printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
        if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) {
-               int pos = *(int *)t->data;
+               const unsigned char *data = t->data;
+               int pos = *(const int *)data;
                if (pos < 0)
                        printf("verdict=%s\n",
                               pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
index 276b7af..672dae1 100644 (file)
@@ -204,7 +204,8 @@ dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
        t = ip6t_get_target(e);
        printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
        if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
-               int pos = *(int *)t->data;
+               const unsigned char *data = t->data;
+               int pos = *(const int *)data;
                if (pos < 0)
                        printf("verdict=%s\n",
                               pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
index 5e5fde0..8f0b0f0 100644 (file)
@@ -744,14 +744,16 @@ static void iptcc_delete_rule(struct rule_head *r)
  * to be called from specific places within the parser */
 static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num)
 {
+       const unsigned char *data;
+
        if (h->chain_iterator_cur) {
                /* policy rule is last rule */
                struct rule_head *pr = (struct rule_head *)
                        h->chain_iterator_cur->rules.prev;
 
                /* save verdict */
-               h->chain_iterator_cur->verdict = 
-                       *(int *)GET_TARGET(pr->entry)->data;
+               data = GET_TARGET(pr->entry)->data;
+               h->chain_iterator_cur->verdict = *(const int *)data;
 
                /* save counter and counter_map information */
                h->chain_iterator_cur->counter_map.maptype = 
@@ -1563,6 +1565,7 @@ const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
 {
        STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
        struct rule_head *r = container_of(e, struct rule_head, entry[0]);
+       const unsigned char *data;
 
        iptc_fn = TC_GET_TARGET;
 
@@ -1576,7 +1579,8 @@ const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
                        return r->jump->name;
                        break;
                case IPTCC_R_STANDARD:
-                       spos = *(int *)GET_TARGET(e)->data;
+                       data = GET_TARGET(e)->data;
+                       spos = *(const int *)data;
                        DEBUGP("r=%p, spos=%d'\n", r, spos);
                        return standard_target_map(spos);
                        break;
index 5366b98..b536f42 100644 (file)
@@ -184,10 +184,11 @@ static void *ping_thread (void *arg) /* {{{ */
   count = 0;
   for (hl = hostlist_head; hl != NULL; hl = hl->next)
   {
-    int status;
-    status = ping_host_add (pingobj, hl->host);
-    if (status != 0)
-      WARNING ("ping plugin: ping_host_add (%s) failed.", hl->host);
+    int tmp_status;
+    tmp_status = ping_host_add (pingobj, hl->host);
+    if (tmp_status != 0)
+      WARNING ("ping plugin: ping_host_add (%s) failed: %s",
+          hl->host, ping_get_error (pingobj));
     else
       count++;
   }
@@ -217,7 +218,9 @@ static void *ping_thread (void *arg) /* {{{ */
 
     if (gettimeofday (&tv_begin, NULL) < 0)
     {
-      ERROR ("ping plugin: gettimeofday failed");
+      char errbuf[1024];
+      ERROR ("ping plugin: gettimeofday failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
       ping_thread_error = 1;
       break;
     }
@@ -322,7 +325,9 @@ static void *ping_thread (void *arg) /* {{{ */
 
     if (gettimeofday (&tv_end, NULL) < 0)
     {
-      ERROR ("ping plugin: gettimeofday failed");
+      char errbuf[1024];
+      ERROR ("ping plugin: gettimeofday failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
       ping_thread_error = 1;
       break;
     }
index 182892a..3682fa5 100644 (file)
@@ -59,6 +59,7 @@ struct read_func_s
 #define rf_callback rf_super.cf_callback
 #define rf_udata rf_super.cf_udata
        callback_func_t rf_super;
+       char rf_group[DATA_MAX_NAME_LEN];
        char rf_name[DATA_MAX_NAME_LEN];
        int rf_type;
        struct timespec rf_interval;
@@ -323,6 +324,13 @@ static int plugin_load_file (char *file, uint32_t flags)
        return (0);
 }
 
+static _Bool timeout_reached(struct timespec timeout)
+{
+       struct timeval now;
+       gettimeofday(&now, NULL);
+       return (now.tv_sec >= timeout.tv_sec && now.tv_usec >= (timeout.tv_nsec / 1000));
+}
+
 static void *plugin_read_thread (void __attribute__((unused)) *args)
 {
        while (read_loop != 0)
@@ -331,6 +339,7 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                struct timeval now;
                int status;
                int rf_type;
+               int rc;
 
                /* Get the read function that needs to be read next. */
                rf = c_heap_get_root (read_heap);
@@ -366,8 +375,16 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                /* sleep until this entry is due,
                 * using pthread_cond_timedwait */
                pthread_mutex_lock (&read_lock);
-               pthread_cond_timedwait (&read_cond, &read_lock,
+               /* In pthread_cond_timedwait, spurious wakeups are possible
+                * (and really happen, at least on NetBSD with > 1 CPU), thus
+                * we need to re-evaluate the condition every time
+                * pthread_cond_timedwait returns. */
+               rc = 0;
+               while (!timeout_reached(rf->rf_next_read) && rc == 0) {
+                       rc = pthread_cond_timedwait (&read_cond, &read_lock,
                                &rf->rf_next_read);
+               }
+
                /* Must hold `real_lock' when accessing `rf->rf_type'. */
                rf_type = rf->rf_type;
                pthread_mutex_unlock (&read_lock);
@@ -756,6 +773,7 @@ int plugin_register_read (const char *name,
        rf->rf_callback = (void *) callback;
        rf->rf_udata.data = NULL;
        rf->rf_udata.free_func = NULL;
+       rf->rf_group[0] = '\0';
        sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
        rf->rf_type = RF_SIMPLE;
        rf->rf_interval.tv_sec = 0;
@@ -765,7 +783,7 @@ int plugin_register_read (const char *name,
        return (plugin_insert_read (rf));
 } /* int plugin_register_read */
 
-int plugin_register_complex_read (const char *name,
+int plugin_register_complex_read (const char *group, const char *name,
                plugin_read_cb callback,
                const struct timespec *interval,
                user_data_t *user_data)
@@ -781,6 +799,10 @@ int plugin_register_complex_read (const char *name,
 
        memset (rf, 0, sizeof (read_func_t));
        rf->rf_callback = (void *) callback;
+       if (group != NULL)
+               sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
+       else
+               rf->rf_group[0] = '\0';
        sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
        rf->rf_type = RF_COMPLEX;
        if (interval != NULL)
@@ -932,6 +954,67 @@ int plugin_unregister_read (const char *name) /* {{{ */
        return (0);
 } /* }}} int plugin_unregister_read */
 
+static int compare_read_func_group (llentry_t *e, void *ud) /* {{{ */
+{
+       read_func_t *rf    = e->value;
+       char        *group = ud;
+
+       return strcmp (rf->rf_group, (const char *)group);
+} /* }}} int compare_read_func_group */
+
+int plugin_unregister_read_group (const char *group) /* {{{ */
+{
+       llentry_t *le;
+       read_func_t *rf;
+
+       int found = 0;
+
+       if (group == NULL)
+               return (-ENOENT);
+
+       pthread_mutex_lock (&read_lock);
+
+       if (read_list == NULL)
+       {
+               pthread_mutex_unlock (&read_lock);
+               return (-ENOENT);
+       }
+
+       while (42)
+       {
+               le = llist_search_custom (read_list,
+                               compare_read_func_group, (void *)group);
+
+               if (le == NULL)
+                       break;
+
+               ++found;
+
+               llist_remove (read_list, le);
+
+               rf = le->value;
+               assert (rf != NULL);
+               rf->rf_type = RF_REMOVE;
+
+               llentry_destroy (le);
+
+               DEBUG ("plugin_unregister_read_group: "
+                               "Marked `%s' (group `%s') for removal.",
+                               rf->rf_name, group);
+       }
+
+       pthread_mutex_unlock (&read_lock);
+
+       if (found == 0)
+       {
+               WARNING ("plugin_unregister_read_group: No such "
+                               "group of read function: %s", group);
+               return (-ENOENT);
+       }
+
+       return (0);
+} /* }}} int plugin_unregister_read_group */
+
 int plugin_unregister_write (const char *name)
 {
        return (plugin_unregister (list_write, name));
@@ -1464,14 +1547,6 @@ void plugin_log (int level, const char *format, ...)
        va_list ap;
        llentry_t *le;
 
-       if (list_log == NULL)
-       {
-               va_start (ap, format);
-               vfprintf (stderr, format, ap);
-               va_end (ap);
-               return;
-       }
-
 #if !COLLECT_DEBUG
        if (level >= LOG_DEBUG)
                return;
@@ -1482,6 +1557,12 @@ void plugin_log (int level, const char *format, ...)
        msg[sizeof (msg) - 1] = '\0';
        va_end (ap);
 
+       if (list_log == NULL)
+       {
+               fprintf (stderr, "%s\n", msg);
+               return;
+       }
+
        le = llist_head (list_log);
        while (le != NULL)
        {
index 868f44a..8b9449e 100644 (file)
@@ -264,7 +264,7 @@ int plugin_register_init (const char *name,
                plugin_init_cb callback);
 int plugin_register_read (const char *name,
                int (*callback) (void));
-int plugin_register_complex_read (const char *name,
+int plugin_register_complex_read (const char *group, const char *name,
                plugin_read_cb callback,
                const struct timespec *interval,
                user_data_t *user_data);
@@ -284,6 +284,7 @@ int plugin_unregister_config (const char *name);
 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_read_group (const char *group);
 int plugin_unregister_write (const char *name);
 int plugin_unregister_flush (const char *name);
 int plugin_unregister_shutdown (const char *name);
index d6dd0ba..dd53cb4 100644 (file)
@@ -2,19 +2,30 @@
  * collectd - src/postgresql.c
  * Copyright (C) 2008, 2009  Sebastian Harl
  * Copyright (C) 2009        Florian Forster
+ * All rights reserved.
  *
- * 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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * 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.
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
  *
- * 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
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
  *
  * Authors:
  *   Sebastian Harl <sh at tokkee.org>
@@ -102,9 +113,12 @@ typedef struct {
        int max_params_num;
 
        /* user configuration */
+       udb_query_preparation_area_t **q_prep_areas;
        udb_query_t    **queries;
        size_t           queries_num;
 
+       int interval;
+
        char *host;
        char *port;
        char *database;
@@ -132,22 +146,16 @@ static int def_queries_num = STATIC_ARRAY_SIZE (def_queries);
 static udb_query_t      **queries       = NULL;
 static size_t             queries_num   = 0;
 
-static c_psql_database_t *databases     = NULL;
-static int                databases_num = 0;
-
 static c_psql_database_t *c_psql_database_new (const char *name)
 {
        c_psql_database_t *db;
 
-       ++databases_num;
-       if (NULL == (databases = (c_psql_database_t *)realloc (databases,
-                               databases_num * sizeof (*databases)))) {
+       db = (c_psql_database_t *)malloc (sizeof (*db));
+       if (NULL == db) {
                log_err ("Out of memory.");
-               exit (5);
+               return NULL;
        }
 
-       db = databases + (databases_num - 1);
-
        db->conn = NULL;
 
        C_COMPLAIN_INIT (&db->conn_complaint);
@@ -157,9 +165,12 @@ static c_psql_database_t *c_psql_database_new (const char *name)
 
        db->max_params_num = 0;
 
+       db->q_prep_areas   = NULL;
        db->queries        = NULL;
        db->queries_num    = 0;
 
+       db->interval   = 0;
+
        db->database   = sstrdup (name);
        db->host       = NULL;
        db->port       = NULL;
@@ -174,11 +185,20 @@ static c_psql_database_t *c_psql_database_new (const char *name)
        return db;
 } /* c_psql_database_new */
 
-static void c_psql_database_delete (c_psql_database_t *db)
+static void c_psql_database_delete (void *data)
 {
+       size_t i;
+
+       c_psql_database_t *db = data;
+
        PQfinish (db->conn);
        db->conn = NULL;
 
+       if (db->q_prep_areas)
+               for (i = 0; i < db->queries_num; ++i)
+                       udb_query_delete_preparation_area (db->q_prep_areas[i]);
+       free (db->q_prep_areas);
+
        sfree (db->queries);
        db->queries_num = 0;
 
@@ -196,8 +216,49 @@ static void c_psql_database_delete (c_psql_database_t *db)
        return;
 } /* c_psql_database_delete */
 
+static int c_psql_connect (c_psql_database_t *db)
+{
+       char  conninfo[4096];
+       char *buf     = conninfo;
+       int   buf_len = sizeof (conninfo);
+       int   status;
+
+       if (! db)
+               return -1;
+
+       status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
+       if (0 < status) {
+               buf     += status;
+               buf_len -= status;
+       }
+
+       C_PSQL_PAR_APPEND (buf, buf_len, "host",       db->host);
+       C_PSQL_PAR_APPEND (buf, buf_len, "port",       db->port);
+       C_PSQL_PAR_APPEND (buf, buf_len, "user",       db->user);
+       C_PSQL_PAR_APPEND (buf, buf_len, "password",   db->password);
+       C_PSQL_PAR_APPEND (buf, buf_len, "sslmode",    db->sslmode);
+       C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
+       C_PSQL_PAR_APPEND (buf, buf_len, "service",    db->service);
+
+       db->conn = PQconnectdb (conninfo);
+       db->proto_version = PQprotocolVersion (db->conn);
+       return 0;
+} /* c_psql_connect */
+
 static int c_psql_check_connection (c_psql_database_t *db)
 {
+       _Bool init = 0;
+
+       if (! db->conn) {
+               init = 1;
+
+               /* trigger c_release() */
+               if (0 == db->conn_complaint.interval)
+                       db->conn_complaint.interval = 1;
+
+               c_psql_connect (db);
+       }
+
        /* "ping" */
        PQclear (PQexec (db->conn, "SELECT 42;"));
 
@@ -216,15 +277,30 @@ static int c_psql_check_connection (c_psql_database_t *db)
                }
 
                db->proto_version = PQprotocolVersion (db->conn);
-               if (3 > db->proto_version)
-                       log_warn ("Protocol version %d does not support parameters.",
-                                       db->proto_version);
        }
 
        db->server_version = PQserverVersion (db->conn);
 
-       c_release (LOG_INFO, &db->conn_complaint,
-                       "Successfully reconnected to database %s", PQdb (db->conn));
+       if (c_would_release (&db->conn_complaint)) {
+               char *server_host;
+               int   server_version;
+
+               server_host    = PQhost (db->conn);
+               server_version = PQserverVersion (db->conn);
+
+               c_do_release (LOG_INFO, &db->conn_complaint,
+                               "Successfully %sconnected to database %s (user %s) "
+                               "at server %s%s%s (server version: %d.%d.%d, "
+                               "protocol version: %d, pid: %d)", init ? "" : "re",
+                               PQdb (db->conn), PQuser (db->conn),
+                               C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
+                               C_PSQL_SERVER_VERSION3 (server_version),
+                               db->proto_version, PQbackendPID (db->conn));
+
+               if (3 > db->proto_version)
+                       log_warn ("Protocol version %d does not support parameters.",
+                                       db->proto_version);
+       }
        return 0;
 } /* c_psql_check_connection */
 
@@ -259,7 +335,8 @@ static PGresult *c_psql_exec_query_params (c_psql_database_t *db,
                                params[i] = db->user;
                                break;
                        case C_PSQL_PARAM_INTERVAL:
-                               ssnprintf (interval, sizeof (interval), "%i", interval_g);
+                               ssnprintf (interval, sizeof (interval), "%i",
+                                               db->interval > 0 ? db->interval : interval_g);
                                params[i] = interval;
                                break;
                        default:
@@ -273,7 +350,8 @@ static PGresult *c_psql_exec_query_params (c_psql_database_t *db,
                        NULL, NULL, /* return text data */ 0);
 } /* c_psql_exec_query_params */
 
-static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q)
+static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
+               udb_query_preparation_area_t *prep_area)
 {
        PGresult *res;
 
@@ -345,7 +423,7 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q)
                 * `BAIL_OUT'. */
                column_names[col] = PQfname (res, col);
                if (NULL == column_names[col]) {
-                       log_err ("Failed to resolv name of column %i.", col);
+                       log_err ("Failed to resolve name of column %i.", col);
                        BAIL_OUT (-1);
                }
        }
@@ -356,8 +434,8 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q)
        else
                host = db->host;
 
-       status = udb_query_prepare_result (q, host, "postgresql",
-                       db->database, column_names, (size_t) column_num);
+       status = udb_query_prepare_result (q, prep_area, host, "postgresql",
+                       db->database, column_names, (size_t) column_num, db->interval);
        if (0 != status) {
                log_err ("udb_query_prepare_result failed with status %i.",
                                status);
@@ -380,46 +458,52 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q)
                if (col < column_num)
                        continue;
 
-               status = udb_query_handle_result (q, column_values);
+               status = udb_query_handle_result (q, prep_area, column_values);
                if (status != 0) {
                        log_err ("udb_query_handle_result failed with status %i.",
                                        status);
                }
        } /* for (row = 0; row < rows_num; ++row) */
 
+       udb_query_finish_result (q, prep_area);
+
        BAIL_OUT (0);
 #undef BAIL_OUT
 } /* c_psql_exec_query */
 
-static int c_psql_read (void)
+static int c_psql_read (user_data_t *ud)
 {
+       c_psql_database_t *db;
+
        int success = 0;
        int i;
 
-       for (i = 0; i < databases_num; ++i) {
-               c_psql_database_t *db = databases + i;
+       if ((ud == NULL) || (ud->data == NULL)) {
+               log_err ("c_psql_read: Invalid user data.");
+               return -1;
+       }
 
-               int j;
+       db = ud->data;
 
-               assert (NULL != db->database);
+       assert (NULL != db->database);
 
-               if (0 != c_psql_check_connection (db))
-                       continue;
+       if (0 != c_psql_check_connection (db))
+               return -1;
 
-               for (j = 0; j < db->queries_num; ++j)
-               {
-                       udb_query_t *q;
+       for (i = 0; i < db->queries_num; ++i)
+       {
+               udb_query_preparation_area_t *prep_area;
+               udb_query_t *q;
 
-                       q = db->queries[j];
+               prep_area = db->q_prep_areas[i];
+               q = db->queries[i];
 
-                       if ((0 != db->server_version)
+               if ((0 != db->server_version)
                                && (udb_query_check_version (q, db->server_version) <= 0))
-                               continue;
-
-                       c_psql_exec_query (db, q);
-               }
+                       continue;
 
-               ++success;
+               if (0 == c_psql_exec_query (db, q, prep_area))
+                       success = 1;
        }
 
        if (! success)
@@ -429,19 +513,7 @@ static int c_psql_read (void)
 
 static int c_psql_shutdown (void)
 {
-       int i;
-
-       if ((NULL == databases) || (0 == databases_num))
-               return 0;
-
-       plugin_unregister_read ("postgresql");
-       plugin_unregister_shutdown ("postgresql");
-
-       for (i = 0; i < databases_num; ++i)
-               c_psql_database_delete (databases + i);
-
-       sfree (databases);
-       databases_num = 0;
+       plugin_unregister_read_group ("postgresql");
 
        udb_query_free (queries, queries_num);
        queries = NULL;
@@ -450,70 +522,6 @@ static int c_psql_shutdown (void)
        return 0;
 } /* c_psql_shutdown */
 
-static int c_psql_init (void)
-{
-       int i;
-
-       if ((NULL == databases) || (0 == databases_num))
-               return 0;
-
-       for (i = 0; i < databases_num; ++i) {
-               c_psql_database_t *db = databases + i;
-
-               char  conninfo[4096];
-               char *buf     = conninfo;
-               int   buf_len = sizeof (conninfo);
-               int   status;
-
-               char *server_host;
-               int   server_version;
-
-               /* this will happen during reinitialization */
-               if (NULL != db->conn) {
-                       c_psql_check_connection (db);
-                       continue;
-               }
-
-               status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
-               if (0 < status) {
-                       buf     += status;
-                       buf_len -= status;
-               }
-
-               C_PSQL_PAR_APPEND (buf, buf_len, "host",       db->host);
-               C_PSQL_PAR_APPEND (buf, buf_len, "port",       db->port);
-               C_PSQL_PAR_APPEND (buf, buf_len, "user",       db->user);
-               C_PSQL_PAR_APPEND (buf, buf_len, "password",   db->password);
-               C_PSQL_PAR_APPEND (buf, buf_len, "sslmode",    db->sslmode);
-               C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
-               C_PSQL_PAR_APPEND (buf, buf_len, "service",    db->service);
-
-               db->conn = PQconnectdb (conninfo);
-               if (0 != c_psql_check_connection (db))
-                       continue;
-
-               db->proto_version = PQprotocolVersion (db->conn);
-
-               server_host    = PQhost (db->conn);
-               server_version = PQserverVersion (db->conn);
-               log_info ("Sucessfully connected to database %s (user %s) "
-                               "at server %s%s%s (server version: %d.%d.%d, "
-                               "protocol version: %d, pid: %d)",
-                               PQdb (db->conn), PQuser (db->conn),
-                               C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
-                               C_PSQL_SERVER_VERSION3 (server_version),
-                               db->proto_version, PQbackendPID (db->conn));
-
-               if (3 > db->proto_version)
-                       log_warn ("Protocol version %d does not support parameters.",
-                                       db->proto_version);
-       }
-
-       plugin_register_read ("postgresql", c_psql_read);
-       plugin_register_shutdown ("postgresql", c_psql_shutdown);
-       return 0;
-} /* c_psql_init */
-
 static int config_set_s (char *name, char **var, const oconfig_item_t *ci)
 {
        if ((0 != ci->children_num) || (1 != ci->values_num)
@@ -527,6 +535,28 @@ static int config_set_s (char *name, char **var, const oconfig_item_t *ci)
        return 0;
 } /* config_set_s */
 
+static int config_set_i (char *name, int *var,
+               const oconfig_item_t *ci, int min)
+{
+       int value;
+
+       if ((0 != ci->children_num) || (1 != ci->values_num)
+                       || (OCONFIG_TYPE_NUMBER != ci->values[0].type)) {
+               log_err ("%s expects a single number argument.", name);
+               return 1;
+       }
+
+       value = (int)ci->values[0].value.number;
+
+       if (value < min) {
+               log_err ("%s expects a number greater or equal to %i.", name, min);
+               return 1;
+       }
+
+       *var = value;
+       return 0;
+} /* config_set_s */
+
 static int config_query_param_add (udb_query_t *q, oconfig_item_t *ci)
 {
        c_psql_user_data_t *data;
@@ -587,6 +617,10 @@ static int c_psql_config_database (oconfig_item_t *ci)
 {
        c_psql_database_t *db;
 
+       char cb_name[DATA_MAX_NAME_LEN];
+       struct timespec cb_interval;
+       user_data_t ud;
+
        int i;
 
        if ((1 != ci->values_num)
@@ -595,7 +629,11 @@ static int c_psql_config_database (oconfig_item_t *ci)
                return 1;
        }
 
+       memset (&ud, 0, sizeof (ud));
+
        db = c_psql_database_new (ci->values[0].value.string);
+       if (db == NULL)
+               return -1;
 
        for (i = 0; i < ci->children_num; ++i) {
                oconfig_item_t *c = ci->children + i;
@@ -617,25 +655,58 @@ static int c_psql_config_database (oconfig_item_t *ci)
                else if (0 == strcasecmp (c->key, "Query"))
                        udb_query_pick_from_list (c, queries, queries_num,
                                        &db->queries, &db->queries_num);
+               else if (0 == strcasecmp (c->key, "Interval"))
+                       config_set_i ("Interval", &db->interval, c, /* min = */ 1);
                else
                        log_warn ("Ignoring unknown config key \"%s\".", c->key);
        }
 
        /* If no `Query' options were given, add the default queries.. */
-       if (db->queries_num == 0)
-       {
+       if (db->queries_num == 0) {
                for (i = 0; i < def_queries_num; i++)
                        udb_query_pick_from_list_by_name (def_queries[i],
                                        queries, queries_num,
                                        &db->queries, &db->queries_num);
        }
 
+       if (db->queries_num > 0) {
+               db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+                               db->queries_num, sizeof (*db->q_prep_areas));
+
+               if (db->q_prep_areas == NULL) {
+                       log_err ("Out of memory.");
+                       c_psql_database_delete (db);
+                       return -1;
+               }
+       }
+
        for (i = 0; (size_t)i < db->queries_num; ++i) {
                c_psql_user_data_t *data;
                data = udb_query_get_user_data (db->queries[i]);
                if ((data != NULL) && (data->params_num > db->max_params_num))
                        db->max_params_num = data->params_num;
+
+               db->q_prep_areas[i]
+                       = udb_query_allocate_preparation_area (db->queries[i]);
+
+               if (db->q_prep_areas[i] == NULL) {
+                       log_err ("Out of memory.");
+                       c_psql_database_delete (db);
+                       return -1;
+               }
        }
+
+       ud.data = db;
+       ud.free_func = c_psql_database_delete;
+
+       ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->database);
+
+       memset (&cb_interval, 0, sizeof (cb_interval));
+       if (db->interval > 0)
+               cb_interval.tv_sec = (time_t)db->interval;
+
+       plugin_register_complex_read ("postgresql", cb_name, c_psql_read,
+                       /* interval = */ &cb_interval, &ud);
        return 0;
 } /* c_psql_config_database */
 
@@ -679,7 +750,7 @@ static int c_psql_config (oconfig_item_t *ci)
 void module_register (void)
 {
        plugin_register_complex_config ("postgresql", c_psql_config);
-       plugin_register_init ("postgresql", c_psql_init);
+       plugin_register_shutdown ("postgresql", c_psql_shutdown);
 } /* module_register */
 
 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
index 8c7700a..c642e58 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright (C) 2009       Sebastian Harl
  * Copyright (C) 2009       Andrés J. Díaz
  * Copyright (C) 2009       Manuel Sanmartin
+ * Copyright (C) 2010       Clément Stenac
  *
  * 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
@@ -28,6 +29,7 @@
  *   Sebastian Harl <sh at tokkee.org>
  *   Andrés J. Díaz <ajdiaz at connectical.com>
  *   Manuel Sanmartin
+ *   Clément Stenac <clement.stenac at diwi.org>
  **/
 
 #include "collectd.h"
 #  define ARG_MAX 4096
 #endif
 
-#define BUFSIZE 256
-
 static const char *config_keys[] =
 {
        "Process",
@@ -137,6 +137,8 @@ typedef struct procstat_entry_s
        unsigned long num_lwp;
        unsigned long vmem_size;
        unsigned long vmem_rss;
+       unsigned long vmem_data;
+       unsigned long vmem_code;
        unsigned long stack_size;
 
        unsigned long vmem_minflt;
@@ -150,10 +152,10 @@ typedef struct procstat_entry_s
        unsigned long cpu_system_counter;
 
        /* io data */
-       long io_rchar;
-       long io_wchar;
-       long io_syscr;
-       long io_syscw;
+       derive_t io_rchar;
+       derive_t io_wchar;
+       derive_t io_syscr;
+       derive_t io_syscw;
 
        struct procstat_entry_s *next;
 } procstat_entry_t;
@@ -170,6 +172,8 @@ typedef struct procstat
        unsigned long num_lwp;
        unsigned long vmem_size;
        unsigned long vmem_rss;
+       unsigned long vmem_data;
+       unsigned long vmem_code;
        unsigned long stack_size;
 
        unsigned long vmem_minflt_counter;
@@ -179,10 +183,10 @@ typedef struct procstat
        unsigned long cpu_system_counter;
 
        /* io data */
-       long io_rchar;
-       long io_wchar;
-       long io_syscr;
-       long io_syscw;
+       derive_t io_rchar;
+       derive_t io_wchar;
+       derive_t io_syscr;
+       derive_t io_syscw;
 
        struct procstat   *next;
        struct procstat_entry_s *instances;
@@ -262,7 +266,7 @@ static void ps_list_register (const char *name, const char *regexp)
                ERROR ("processes plugin: ps_list_register: "
                                "Regular expression \"%s\" found in config "
                                "file, but support for regular expressions "
-                               "has been dispabled at compile time.",
+                               "has been disabled at compile time.",
                                regexp);
                sfree (new);
                return;
@@ -364,6 +368,8 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                pse->num_lwp    = entry->num_lwp;
                pse->vmem_size  = entry->vmem_size;
                pse->vmem_rss   = entry->vmem_rss;
+               pse->vmem_data  = entry->vmem_data;
+               pse->vmem_code  = entry->vmem_code;
                pse->stack_size = entry->stack_size;
                pse->io_rchar   = entry->io_rchar;
                pse->io_wchar   = entry->io_wchar;
@@ -374,6 +380,8 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                ps->num_lwp    += pse->num_lwp;
                ps->vmem_size  += pse->vmem_size;
                ps->vmem_rss   += pse->vmem_rss;
+               ps->vmem_data  += pse->vmem_data;
+               ps->vmem_code  += pse->vmem_code;
                ps->stack_size += pse->stack_size;
 
                ps->io_rchar   += ((pse->io_rchar == -1)?0:pse->io_rchar);
@@ -470,6 +478,8 @@ static void ps_list_reset (void)
                ps->num_lwp     = 0;
                ps->vmem_size   = 0;
                ps->vmem_rss    = 0;
+               ps->vmem_data   = 0;
+               ps->vmem_code   = 0;
                ps->stack_size  = 0;
                ps->io_rchar = -1;
                ps->io_wchar = -1;
@@ -638,6 +648,16 @@ static void ps_submit_proc_list (procstat_t *ps)
        vl.values_len = 1;
        plugin_dispatch_values (&vl);
 
+       sstrncpy (vl.type, "ps_data", sizeof (vl.type));
+       vl.values[0].gauge = ps->vmem_data;
+       vl.values_len = 1;
+       plugin_dispatch_values (&vl);
+
+       sstrncpy (vl.type, "ps_code", sizeof (vl.type));
+       vl.values[0].gauge = ps->vmem_code;
+       vl.values_len = 1;
+       plugin_dispatch_values (&vl);
+
        sstrncpy (vl.type, "ps_stacksize", sizeof (vl.type));
        vl.values[0].gauge = ps->stack_size;
        vl.values_len = 1;
@@ -664,8 +684,8 @@ static void ps_submit_proc_list (procstat_t *ps)
        if ( (ps->io_rchar != -1) && (ps->io_wchar != -1) )
        {
                sstrncpy (vl.type, "ps_disk_octets", sizeof (vl.type));
-               vl.values[0].counter = ps->io_rchar;
-               vl.values[1].counter = ps->io_wchar;
+               vl.values[0].derive = ps->io_rchar;
+               vl.values[1].derive = ps->io_wchar;
                vl.values_len = 2;
                plugin_dispatch_values (&vl);
        }
@@ -673,18 +693,22 @@ static void ps_submit_proc_list (procstat_t *ps)
        if ( (ps->io_syscr != -1) && (ps->io_syscw != -1) )
        {
                sstrncpy (vl.type, "ps_disk_ops", sizeof (vl.type));
-               vl.values[0].counter = ps->io_syscr;
-               vl.values[1].counter = ps->io_syscw;
+               vl.values[0].derive = ps->io_syscr;
+               vl.values[1].derive = ps->io_syscw;
                vl.values_len = 2;
                plugin_dispatch_values (&vl);
        }
 
-       DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
+       DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; "
+                        "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
+                       "vmem_code = %lu; "
                        "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
                        "cpu_user_counter = %lu; cpu_system_counter = %lu; "
-                       "io_rchar = %ld; io_wchar = %ld; "
-                       "io_syscr = %ld; io_syscw = %ld;",
-                       ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
+                       "io_rchar = %"PRIi64"; io_wchar = %"PRIi64"; "
+                       "io_syscr = %"PRIi64"; io_syscw = %"PRIi64";",
+                       ps->name, ps->num_proc, ps->num_lwp,
+                       ps->vmem_size, ps->vmem_rss,
+                       ps->vmem_data, ps->vmem_code,
                        ps->vmem_minflt_counter, ps->vmem_majflt_counter,
                        ps->cpu_user_counter, ps->cpu_system_counter,
                        ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
@@ -719,6 +743,69 @@ static int ps_read_tasks (int pid)
        return ((count >= 1) ? count : 1);
 } /* int *ps_read_tasks */
 
+/* Read advanced virtual memory data from /proc/pid/status */
+static procstat_t *ps_read_vmem (int pid, procstat_t *ps)
+{
+       FILE *fh;
+       char buffer[1024];
+       char filename[64];
+       unsigned long long lib = 0;
+       unsigned long long exe = 0;
+       unsigned long long data = 0;
+       char *fields[8];
+       int numfields;
+
+       ssnprintf (filename, sizeof (filename), "/proc/%i/status", pid);
+       if ((fh = fopen (filename, "r")) == NULL)
+               return (NULL);
+
+       while (fgets (buffer, sizeof(buffer), fh) != NULL)
+       {
+               long long tmp;
+               char *endptr;
+
+               if (strncmp (buffer, "Vm", 2) != 0)
+                       continue;
+
+               numfields = strsplit (buffer, fields,
+                                      STATIC_ARRAY_SIZE (fields));
+
+               if (numfields < 2)
+                       continue;
+
+               errno = 0;
+               endptr = NULL;
+               tmp = strtoll (fields[1], &endptr, /* base = */ 10);
+               if ((errno == 0) && (endptr != fields[1]))
+               {
+                       if (strncmp (buffer, "VmData", 6) == 0) 
+                       {
+                               data = tmp;
+                       }
+                       else if (strncmp (buffer, "VmLib", 5) == 0)
+                       {
+                               lib = tmp;
+                       }
+                       else if  (strncmp(buffer, "VmExe", 5) == 0)
+                       {
+                               exe = tmp;
+                       }
+               }
+       } /* while (fgets) */
+
+       if (fclose (fh))
+       {
+               char errbuf[1024];
+               WARNING ("processes: fclose: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
+
+       ps->vmem_data = data * 1024;
+       ps->vmem_code = (exe + lib) * 1024;
+
+       return (ps);
+} /* procstat_t *ps_read_vmem */
+
 static procstat_t *ps_read_io (int pid, procstat_t *ps)
 {
        FILE *fh;
@@ -732,9 +819,11 @@ static procstat_t *ps_read_io (int pid, procstat_t *ps)
        if ((fh = fopen (filename, "r")) == NULL)
                return (NULL);
 
-       while (fgets (buffer, 1024, fh) != NULL)
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
-               long *val = NULL;
+               derive_t *val = NULL;
+               long long tmp;
+               char *endptr;
 
                if (strncasecmp (buffer, "rchar:", 6) == 0)
                        val = &(ps->io_rchar);
@@ -747,13 +836,20 @@ static procstat_t *ps_read_io (int pid, procstat_t *ps)
                else
                        continue;
 
-               numfields = strsplit (buffer, fields, 8);
+               numfields = strsplit (buffer, fields,
+                               STATIC_ARRAY_SIZE (fields));
 
                if (numfields < 2)
                        continue;
 
-               *val = atol (fields[1]);
-       }
+               errno = 0;
+               endptr = NULL;
+               tmp = strtoll (fields[1], &endptr, /* base = */ 10);
+               if ((errno != 0) || (endptr == fields[1]))
+                       *val = -1;
+               else
+                       *val = (derive_t) tmp;
+       } /* while (fgets) */
 
        if (fclose (fh))
        {
@@ -793,7 +889,7 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
                return (-1);
        buffer[i] = 0;
 
-       fields_len = strsplit (buffer, fields, 64);
+       fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
        if (fields_len < 24)
        {
                DEBUG ("processes plugin: ps_read_process (pid = %i):"
@@ -861,6 +957,14 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
        vmem_rss = vmem_rss * pagesize_g;
 
+       if ( (ps_read_vmem(pid, ps)) == NULL)
+       {
+               /* No VMem data */
+               ps->vmem_data = -1;
+               ps->vmem_code = -1;
+               DEBUG("ps_read_process: did not get vmem data for pid %i",pid);
+       }
+
        ps->cpu_user_counter = (unsigned long) cpu_user_counter;
        ps->cpu_system_counter = (unsigned long) cpu_system_counter;
        ps->vmem_size = (unsigned long) vmem_size;
@@ -1001,7 +1105,7 @@ static unsigned long read_fork_rate ()
 
                errno = 0;
                endptr = NULL;
-               result = strtoul(fields[1], &endptr, 10);
+               result = strtoul(fields[1], &endptr, /* base = */ 10);
                if ((endptr == fields[1]) || (errno != 0)) {
                        ERROR ("processes plugin: Cannot parse fork rate: %s",
                                        fields[1]);
@@ -1200,7 +1304,11 @@ static int ps_read (void)
                                }
 
                                pse.num_proc++;
+                               pse.vmem_size = task_basic_info.virtual_size;
                                pse.vmem_rss = task_basic_info.resident_size;
+                               /* Does not seem to be easily exposed */
+                               pse.vmem_data = 0;
+                               pse.vmem_code = 0;
 
                                pse.vmem_minflt_counter = task_events_info.cow_faults;
                                pse.vmem_majflt_counter = task_events_info.faults;
@@ -1394,6 +1502,8 @@ static int ps_read (void)
                pse.num_lwp    = ps.num_lwp;
                pse.vmem_size  = ps.vmem_size;
                pse.vmem_rss   = ps.vmem_rss;
+               pse.vmem_data  = ps.vmem_data;
+               pse.vmem_code  = ps.vmem_code;
                pse.stack_size = ps.stack_size;
 
                pse.vmem_minflt = 0;
@@ -1524,6 +1634,8 @@ static int ps_read (void)
 
                pse.vmem_size = procs[i].ki_size;
                pse.vmem_rss = procs[i].ki_rssize * getpagesize();
+               pse.vmem_data = procs[i].ki_dsize * getpagesize();
+               pse.vmem_code = procs[i].ki_tsize * getpagesize();
                pse.stack_size = procs[i].ki_ssize * getpagesize();
                pse.vmem_minflt = 0;
                pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
@@ -1680,6 +1792,9 @@ static int ps_read (void)
 
                        pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
                        pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
+                       /* Not supported */
+                       pse.vmem_data = 0;
+                       pse.vmem_code = 0;
                        pse.stack_size =  0;
 
                        ps_list_add (cmdline, cargs, &pse);
index d193d2d..4d44977 100644 (file)
@@ -623,7 +623,8 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwd
        user_data->data = c;
        ts.tv_sec = interval;
        ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
-       plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
+       plugin_register_complex_read(/* group = */ NULL, buf,
+                       cpy_read_callback, &ts, user_data);
        return cpy_string_to_unicode_or_bytes(buf);
 }
 
index 4fe33fa..a020692 100644 (file)
@@ -36,6 +36,10 @@ struct cr_data_s
 
   _Bool collect_interface;
   _Bool collect_regtable;
+  _Bool collect_cpu_load;
+  _Bool collect_memory;
+  _Bool collect_df;
+  _Bool collect_disk;
 };
 typedef struct cr_data_s cr_data_t;
 
@@ -110,6 +114,26 @@ static void cr_submit_gauge (cr_data_t *rd, const char *type, /* {{{ */
        plugin_dispatch_values (&vl);
 } /* }}} void cr_submit_gauge */
 
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0)
+static void cr_submit_counter (cr_data_t *rd, const char *type, /* {{{ */
+    const char *type_instance, counter_t value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = value;
+
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+       sstrncpy (vl.host, rd->node, sizeof (vl.host)); /* FIXME */
+       sstrncpy (vl.plugin, "routeros", sizeof (vl.plugin));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* }}} void cr_submit_gauge */
+#endif
+
 static void submit_regtable (cr_data_t *rd, /* {{{ */
     const ros_registration_table_t *r)
 {
@@ -158,6 +182,44 @@ static int handle_regtable (__attribute__((unused)) ros_connection_t *c, /* {{{
   return (0);
 } /* }}} int handle_regtable */
 
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0) /* FIXME */
+static int handle_system_resource (__attribute__((unused)) ros_connection_t *c, /* {{{ */
+        const ros_system_resource_t *r,
+       __attribute__((unused)) void *user_data)
+{
+  cr_data_t *rd;
+
+  if ((r == NULL) || (user_data == NULL))
+    return (EINVAL);
+  rd = user_data;
+
+  if (rd->collect_cpu_load)
+    cr_submit_gauge (rd, "gauge", "cpu_load", (gauge_t) r->cpu_load);
+
+  if (rd->collect_memory)
+  {
+    cr_submit_gauge (rd, "memory", "used",
+       (gauge_t) (r->total_memory - r->free_memory));
+    cr_submit_gauge (rd, "memory", "free", (gauge_t) r->free_memory);
+  }
+
+  if (rd->collect_df)
+  {
+    cr_submit_gauge (rd, "df_complex", "used",
+       (gauge_t) (r->total_memory - r->free_memory));
+    cr_submit_gauge (rd, "df_complex", "free", (gauge_t) r->free_memory);
+  }
+
+  if (rd->collect_disk)
+  {
+    cr_submit_counter (rd, "counter", "secors_written", (counter_t) r->write_sect_total);
+    cr_submit_gauge (rd, "gauge", "bad_blocks", (gauge_t) r->bad_blocks);
+  }
+
+  return (0);
+} /* }}} int handle_system_resource */
+#endif
+
 static int cr_read (user_data_t *user_data) /* {{{ */
 {
   int status;
@@ -214,6 +276,26 @@ static int cr_read (user_data_t *user_data) /* {{{ */
     }
   }
 
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0) /* FIXME */
+  if (rd->collect_cpu_load
+      || rd->collect_memory
+      || rd->collect_df
+      || rd->collect_disk)
+  {
+    status = ros_system_resource (rd->connection, handle_system_resource,
+       /* user data = */ rd);
+    if (status != 0)
+    {
+      char errbuf[128];
+      ERROR ("routeros plugin: ros_system_resource failed: %s",
+         sstrerror (status, errbuf, sizeof (errbuf)));
+      ros_disconnect (rd->connection);
+      rd->connection = NULL;
+      return (-1);
+    }
+  }
+#endif
+
   return (0);
 } /* }}} int cr_read */
 
@@ -250,8 +332,6 @@ static int cr_config_router (oconfig_item_t *ci) /* {{{ */
   router_data->service = NULL;
   router_data->username = NULL;
   router_data->password = NULL;
-  router_data->collect_interface = false;
-  router_data->collect_regtable = false;
 
   status = 0;
   for (i = 0; i < ci->children_num; i++)
@@ -270,6 +350,16 @@ static int cr_config_router (oconfig_item_t *ci) /* {{{ */
       cf_util_get_boolean (child, &router_data->collect_interface);
     else if (strcasecmp ("CollectRegistrationTable", child->key) == 0)
       cf_util_get_boolean (child, &router_data->collect_regtable);
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0) /* FIXME */
+    else if (strcasecmp ("CollectCPULoad", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_cpu_load);
+    else if (strcasecmp ("CollectMemory", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_memory);
+    else if (strcasecmp ("CollectDF", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_df);
+    else if (strcasecmp ("CollectDisk", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_disk);
+#endif
     else
     {
       WARNING ("routeros plugin: Unknown config option `%s'.", child->key);
@@ -318,8 +408,8 @@ static int cr_config_router (oconfig_item_t *ci) /* {{{ */
   user_data.data = router_data;
   user_data.free_func = (void *) cr_free_data;
   if (status == 0)
-    status = plugin_register_complex_read (read_name, cr_read,
-       /* interval = */ NULL, &user_data);
+    status = plugin_register_complex_read (/* group = */ NULL, read_name,
+       cr_read, /* interval = */ NULL, &user_data);
 
   if (status != 0)
     cr_free_data (router_data);
index ed1ced1..01fca30 100644 (file)
@@ -432,11 +432,6 @@ static void *rrd_queue_thread (void __attribute__((unused)) *data)
                sfree (queue_entry);
        } /* while (42) */
 
-       pthread_mutex_lock (&cache_lock);
-       c_avl_destroy (cache);
-       cache = NULL;
-       pthread_mutex_unlock (&cache_lock);
-
        pthread_exit ((void *) 0);
        return ((void *) 0);
 } /* void *rrd_queue_thread */
@@ -786,6 +781,59 @@ static int rrd_cache_insert (const char *filename,
        return (0);
 } /* int rrd_cache_insert */
 
+static int rrd_cache_destroy (void) /* {{{ */
+{
+  void *key = NULL;
+  void *value = NULL;
+
+  int non_empty = 0;
+
+  pthread_mutex_lock (&cache_lock);
+
+  if (cache == NULL)
+  {
+    pthread_mutex_unlock (&cache_lock);
+    return (0);
+  }
+
+  while (c_avl_pick (cache, &key, &value) == 0)
+  {
+    rrd_cache_t *rc;
+    int i;
+
+    sfree (key);
+    key = NULL;
+
+    rc = value;
+    value = NULL;
+
+    if (rc->values_num > 0)
+      non_empty++;
+
+    for (i = 0; i < rc->values_num; i++)
+      sfree (rc->values[i]);
+    sfree (rc->values);
+    sfree (rc);
+  }
+
+  c_avl_destroy (cache);
+  cache = NULL;
+
+  if (non_empty > 0)
+  {
+    INFO ("rrdtool plugin: %i cache %s had values when destroying the cache.",
+        non_empty, (non_empty == 1) ? "entry" : "entries");
+  }
+  else
+  {
+    DEBUG ("rrdtool plugin: No values have been lost "
+        "when destroying the cache.");
+  }
+
+  pthread_mutex_unlock (&cache_lock);
+  return (0);
+} /* }}} int rrd_cache_destroy */
+
 static int rrd_compare_numeric (const void *a_ptr, const void *b_ptr)
 {
        int a = *((int *) a_ptr);
@@ -807,6 +855,9 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl,
        char         values[512];
        int          status;
 
+       if (do_shutdown)
+               return (0);
+
        if (0 != strcmp (ds->type, vl->type)) {
                ERROR ("rrdtool plugin: DS type does not match value list type");
                return -1;
@@ -1063,7 +1114,7 @@ static int rrd_shutdown (void)
                DEBUG ("rrdtool plugin: queue_thread exited.");
        }
 
-       /* TODO: Maybe it'd be a good idea to free the cache here.. */
+       rrd_cache_destroy ();
 
        return (0);
 } /* int rrd_shutdown */
index 00df377..fdb7c65 100644 (file)
@@ -655,8 +655,9 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
   if (hd->interval != 0)
     cb_interval.tv_sec = (time_t) hd->interval;
 
-  status = plugin_register_complex_read (cb_name, csnmp_read_host,
-      /* interval = */ &cb_interval, /* user_data = */ &cb_data);
+  status = plugin_register_complex_read (/* group = */ NULL, cb_name,
+      csnmp_read_host, /* interval = */ &cb_interval,
+      /* user_data = */ &cb_data);
   if (status != 0)
   {
     ERROR ("snmp plugin: Registering complex read function failed.");
@@ -1127,7 +1128,7 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
       char temp[DATA_MAX_NAME_LEN];
 
       if (instance_list_ptr == NULL)
-       ssnprintf (temp, sizeof (temp), "%u", (uint32_t) subid);
+       ssnprintf (temp, sizeof (temp), "%"PRIu32, (uint32_t) subid);
       else
        sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
 
index 7f41c9e..e467818 100644 (file)
@@ -96,7 +96,7 @@ int kvm_pagesize;
 #elif HAVE_PERFSTAT
 static int pagesize;
 static perfstat_memory_total_t pmemory;
-/*# endif HAVE_PERFSTAT */ 
+/*# endif HAVE_PERFSTAT */
 
 #else
 # error "No applicable input method."
@@ -193,6 +193,8 @@ static int swap_read (void)
        char *fields[8];
        int numfields;
 
+       _Bool old_kernel=0;
+
        derive_t swap_used   = 0;
        derive_t swap_cached = 0;
        derive_t swap_free   = 0;
@@ -243,30 +245,44 @@ static int swap_read (void)
 
        if ((fh = fopen ("/proc/vmstat", "r")) == NULL)
        {
-               char errbuf[1024];
-               WARNING ("swap: fopen: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (-1);
+               // /proc/vmstat does not exist in kernels <2.6
+               if ((fh = fopen ("/proc/stat", "r")) == NULL )
+               {
+                       char errbuf[1024];
+                       WARNING ("swap: fopen: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       return (-1);
+               }
+               else
+                       old_kernel = 1;
        }
 
        while (fgets (buffer, 1024, fh) != NULL)
        {
-               derive_t *val = NULL;
-
-               if (strncasecmp (buffer, "pswpin", 6) == 0)
-                       val = &swap_in;
-               else if (strncasecmp (buffer, "pswpout", 7) == 0)
-                       val = &swap_out;
-               else
-                       continue;
-
-               numfields = strsplit (buffer, fields, 8);
-
-               if (numfields < 2)
-                       continue;
-
-               *val = (derive_t) atoll (fields[1]);
-       }
+               numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+
+               if (!old_kernel)
+               {
+                       if (numfields != 2)
+                               continue;
+
+                       if (strcasecmp ("pswpin", fields[0]) != 0)
+                               strtoderive (fields[1], &swap_in);
+                       else if (strcasecmp ("pswpout", fields[0]) == 0)
+                               strtoderive (fields[1], &swap_out);
+               }
+               else /* if (old_kernel) */
+               {
+                       if (numfields != 3)
+                               continue;
+
+                       if (strcasecmp ("page", fields[0]) == 0)
+                       {
+                               strtoderive (fields[1], &swap_in);
+                               strtoderive (fields[2], &swap_out);
+                       }
+               }
+       } /* while (fgets) */
 
        if (fclose (fh))
        {
@@ -280,7 +296,6 @@ static int swap_read (void)
        swap_submit ("cached", swap_cached, DS_TYPE_GAUGE);
        swap_submit ("in", swap_in, DS_TYPE_DERIVE);
        swap_submit ("out", swap_out, DS_TYPE_DERIVE);
-
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
@@ -310,7 +325,7 @@ static int swap_read (void)
         * However, Solaris does not allow to allocated/reserved more than the
         * available swap (physical memory + disk swap), so the pedant may
         * prefer: allocated + unallocated = reserved, available
-        * 
+        *
         * We map the above to: used + resv = n/a, free
         *
         * Does your brain hurt yet?  - Christophe Kalt
index 8becc05..25cbcd4 100644 (file)
@@ -30,6 +30,7 @@
  *     Instance "exim"
  *     <Match>
  *       Regex "S=([1-9][0-9]*)"
+ *       ExcludeRegex "U=root.*S="
  *       DSType "CounterAdd"
  *       Type "ipt_bytes"
  *       Instance "total"
@@ -41,6 +42,7 @@
 struct ctail_config_match_s
 {
   char *regex;
+  char *excluderegex;
   int flags;
   char *type;
   char *type_instance;
@@ -157,6 +159,9 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
 
     if (strcasecmp ("Regex", option->key) == 0)
       status = ctail_config_add_string ("Regex", &cm.regex, option);
+    else if (strcasecmp ("ExcludeRegex", option->key) == 0)
+      status = ctail_config_add_string ("ExcludeRegex", &cm.excluderegex,
+                                       option);
     else if (strcasecmp ("DSType", option->key) == 0)
       status = ctail_config_add_match_dstype (&cm, option);
     else if (strcasecmp ("Type", option->key) == 0)
@@ -201,8 +206,8 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
 
   if (status == 0)
   {
-    status = tail_match_add_match_simple (tm, cm.regex, cm.flags,
-       "tail", plugin_instance, cm.type, cm.type_instance);
+    status = tail_match_add_match_simple (tm, cm.regex, cm.excluderegex,
+       cm.flags, "tail", plugin_instance, cm.type, cm.type_instance);
 
     if (status != 0)
     {
@@ -211,6 +216,7 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
   }
 
   sfree (cm.regex);
+  sfree (cm.excluderegex);
   sfree (cm.type);
   sfree (cm.type_instance);
 
index 6b261c7..29fecdf 100644 (file)
@@ -302,11 +302,15 @@ static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
 
 static int ts_destroy (void **user_data) /* {{{ */
 {
+       ts_data_t **data;
+
        if (user_data == NULL)
                return (-EINVAL);
 
-       free (*user_data);
-       *user_data = NULL;
+       data = (ts_data_t **) user_data;
+
+       free (*data);
+       *data = NULL;
 
        return (0);
 } /* }}} int ts_destroy */
index a5872eb..fc53fbf 100644 (file)
@@ -1,6 +1,7 @@
 absolute               count:ABSOLUTE:0:U
 apache_bytes           count:COUNTER:0:134217728
 apache_connections     count:GAUGE:0:65535
+apache_idle_workers    count:GAUGE:0:65535
 apache_requests                count:COUNTER:0:134217728
 apache_scoreboard      count:GAUGE:0:65535
 arc_counts             demand_data:COUNTER:0:U, demand_metadata:COUNTER:0:U, prefetch_data:COUNTER:0:U, prefetch_metadata:COUNTER:0:U
@@ -127,6 +128,8 @@ ps_pagefaults               minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036
 ps_disk_octets         read:DERIVE:0:U, write:DERIVE:0:U
 ps_disk_ops            read:DERIVE:0:U, write:DERIVE:0:U
 ps_rss                 value:GAUGE:0:9223372036854775807
+ps_code                        value:GAUGE:0:9223372036854775807
+ps_data                        value:GAUGE:0:9223372036854775807
 ps_stacksize           value:GAUGE:0:9223372036854775807
 ps_state               value:GAUGE:0:65535
 ps_vm                  value:GAUGE:0:9223372036854775807
index a46eb41..67f9881 100644 (file)
@@ -21,7 +21,8 @@ The format of the data-source specification has been inspired by RRDtool's
 data-source specification. Each data-source is defined by a quadruple made up
 of the data-source name, type, minimal and maximal values, delimited by colons
 (":"): I<ds-name>:I<ds-type>:I<min>:I<max>. I<ds-type> may be either
-B<COUNTER> or B<GAUGE>. I<min> and I<max> define the range of valid values for
+B<ABSOLUTE>, B<COUNTER>, B<DERIVE>, or B<GAUGE>. I<min> and I<max> define the
+range of valid values for
 data stored for this data-source. If B<U> is specified for either the min or
 max value, it will be set to unknown, meaning that no range checks will
 happen. See L<rrdcreate(1)> for more details.
index 072593f..4ca9646 100644 (file)
 #include "utils_cache.h"
 #include "utils_parse_option.h"
 
+#define free_everything_and_return(status) do { \
+    size_t j; \
+    for (j = 0; j < number; j++) { \
+      sfree(names[j]); \
+      names[j] = NULL; \
+    } \
+    sfree(names); \
+    sfree(times); \
+    return (status); \
+  } while (0)
+
 #define print_to_socket(fh, ...) \
   if (fprintf (fh, __VA_ARGS__) < 0) { \
     char errbuf[1024]; \
     WARNING ("handle_listval: failed to write to socket #%i: %s", \
        fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
-    return -1; \
+    free_everything_and_return (-1); \
   }
 
 int handle_listval (FILE *fh, char *buffer)
@@ -52,20 +63,20 @@ int handle_listval (FILE *fh, char *buffer)
   if (status != 0)
   {
     print_to_socket (fh, "-1 Cannot parse command.\n");
-    return (-1);
+    free_everything_and_return (-1);
   }
   assert (command != NULL);
 
   if (strcasecmp ("LISTVAL", command) != 0)
   {
     print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
-    return (-1);
+    free_everything_and_return (-1);
   }
 
   if (*buffer != 0)
   {
     print_to_socket (fh, "-1 Garbage after end of command: %s\n", buffer);
-    return (-1);
+    free_everything_and_return (-1);
   }
 
   status = uc_get_names (&names, &times, &number);
@@ -73,21 +84,15 @@ int handle_listval (FILE *fh, char *buffer)
   {
     DEBUG ("command listval: uc_get_names failed with status %i", status);
     print_to_socket (fh, "-1 uc_get_names failed.\n");
-    return (-1);
+    free_everything_and_return (-1);
   }
 
   print_to_socket (fh, "%i Value%s found\n",
       (int) number, (number == 1) ? "" : "s");
   for (i = 0; i < number; i++)
-  {
     print_to_socket (fh, "%u %s\n", (unsigned int) times[i], names[i]);
-    sfree(names[i]);
-  }
 
-  sfree(names);
-  sfree(times);
-   
-  return (0);
+  free_everything_and_return (0);
 } /* int handle_listval */
 
 /* vim: set sw=2 sts=2 ts=8 : */
index 5f892a4..7d594d8 100644 (file)
@@ -39,13 +39,6 @@ struct udb_result_s
   char   **values;
   size_t   values_num;
 
-  /* Preparation area */
-  const   data_set_t *ds;
-  size_t *instances_pos;
-  size_t *values_pos;
-  char  **instances_buffer;
-  char  **values_buffer;
-
   /* Legacy data */
   int legacy_mode;
   size_t legacy_position;
@@ -69,13 +62,31 @@ struct udb_query_s /* {{{ */
   unsigned int min_version;
   unsigned int max_version;
 
-  /* Preparation area */
+  udb_result_t *results;
+}; /* }}} */
+
+struct udb_result_preparation_area_s /* {{{ */
+{
+  const   data_set_t *ds;
+  size_t *instances_pos;
+  size_t *values_pos;
+  char  **instances_buffer;
+  char  **values_buffer;
+
+  struct udb_result_preparation_area_s *next;
+}; /* }}} */
+typedef struct udb_result_preparation_area_s udb_result_preparation_area_t;
+
+struct udb_query_preparation_area_s /* {{{ */
+{
   size_t column_num;
   char *host;
   char *plugin;
   char *db_name;
 
-  udb_result_t *results;
+  int interval;
+
+  udb_result_preparation_area_t *result_prep_areas;
 }; /* }}} */
 
 /*
@@ -182,43 +193,49 @@ static int udb_config_set_uint (unsigned int *ret_value, /* {{{ */
 /*
  * Legacy result private functions
  */
-static void udb_legacy_result_finish_result (udb_result_t *r) /* {{{ */
+static void udb_legacy_result_finish_result (const udb_result_t const *r, /* {{{ */
+    udb_result_preparation_area_t *prep_area)
 {
-  if (r == NULL)
+  if ((r == NULL) || (prep_area))
     return;
 
   assert (r->legacy_mode == 1);
 
-  r->ds = NULL;
+  prep_area->ds = NULL;
 } /* }}} void udb_legacy_result_finish_result */
 
 static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
-    udb_query_t *q, char **column_values)
+    udb_query_preparation_area_t *q_area,
+    udb_result_preparation_area_t *r_area,
+    const udb_query_t const *q, char **column_values)
 {
   value_list_t vl = VALUE_LIST_INIT;
   value_t value;
   char *value_str;
 
   assert (r->legacy_mode == 1);
-  assert (r->ds != NULL);
-  assert (r->ds->ds_num == 1);
+  assert (r_area->ds != NULL);
+  assert (r_area->ds->ds_num == 1);
 
   vl.values = &value;
   vl.values_len = 1;
 
   value_str = column_values[r->legacy_position];
-  if (0 != parse_value (value_str, &vl.values[0], r->ds->ds[0].type))
+  if (0 != parse_value (value_str, &vl.values[0], r_area->ds->ds[0].type))
   {
     ERROR ("db query utils: udb_legacy_result_handle_result: "
         "Parsing `%s' as %s failed.", value_str,
-        DS_TYPE_TO_STRING (r->ds->ds[0].type));
+        DS_TYPE_TO_STRING (r_area->ds->ds[0].type));
     errno = EINVAL;
     return (-1);
   }
 
-  sstrncpy (vl.host, q->host, sizeof (vl.host));
-  sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance));
+  if (q_area->interval > 0)
+    vl.interval = q_area->interval;
+
+  sstrncpy (vl.host, q_area->host, sizeof (vl.host));
+  sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
   sstrncpy (vl.type, r->type, sizeof (vl.type));
 
   if (r->instance_prefix != NULL)
@@ -230,7 +247,8 @@ static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
   return (0);
 } /* }}} int udb_legacy_result_handle_result */
 
-static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
+static int udb_legacy_result_prepare_result (const udb_result_t const *r, /* {{{ */
+    udb_result_preparation_area_t *prep_area,
     char **column_names, size_t column_num)
 {
   if (r == NULL)
@@ -239,7 +257,7 @@ static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
   assert (r->legacy_mode == 1);
 
   /* Make sure previous preparations are cleaned up. */
-  udb_legacy_result_finish_result (r);
+  udb_legacy_result_finish_result (r, prep_area);
 
   if (r->legacy_position >= column_num)
   {
@@ -250,8 +268,8 @@ static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
   }
 
   /* Read `ds' and check number of values {{{ */
-  r->ds = plugin_get_ds (r->type);
-  if (r->ds == NULL)
+  prep_area->ds = plugin_get_ds (r->type);
+  if (prep_area->ds == NULL)
   {
     ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
         "known by the daemon. See types.db(5) for details.",
@@ -259,13 +277,13 @@ static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
     return (-1);
   }
 
-  if (r->ds->ds_num != 1)
+  if (prep_area->ds->ds_num != 1)
   {
     ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
         "requires exactly %i values, but the legacy configuration "
         "requires exactly one!",
         r->type,
-        r->ds->ds_num);
+        prep_area->ds->ds_num);
     return (-1);
   }
   /* }}} */
@@ -342,40 +360,45 @@ static int udb_legacy_result_create (const char *query_name, /* {{{ */
 /*
  * Result private functions
  */
-static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
+static int udb_result_submit (udb_result_t *r, /* {{{ */
+    udb_result_preparation_area_t *r_area,
+    const udb_query_t const *q, udb_query_preparation_area_t *q_area)
 {
   value_list_t vl = VALUE_LIST_INIT;
   size_t i;
 
   assert (r != NULL);
   assert (r->legacy_mode == 0);
-  assert (r->ds != NULL);
-  assert (((size_t) r->ds->ds_num) == r->values_num);
+  assert (r_area->ds != NULL);
+  assert (((size_t) r_area->ds->ds_num) == r->values_num);
 
-  vl.values = (value_t *) calloc (r->ds->ds_num, sizeof (value_t));
+  vl.values = (value_t *) calloc (r_area->ds->ds_num, sizeof (value_t));
   if (vl.values == NULL)
   {
     ERROR ("db query utils: malloc failed.");
     return (-1);
   }
-  vl.values_len = r->ds->ds_num;
+  vl.values_len = r_area->ds->ds_num;
 
   for (i = 0; i < r->values_num; i++)
   {
-    char *value_str = r->values_buffer[i];
+    char *value_str = r_area->values_buffer[i];
 
-    if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i].type))
+    if (0 != parse_value (value_str, &vl.values[i], r_area->ds->ds[i].type))
     {
       ERROR ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
-          value_str, DS_TYPE_TO_STRING (r->ds->ds[i].type));
+          value_str, DS_TYPE_TO_STRING (r_area->ds->ds[i].type));
       errno = EINVAL;
       return (-1);
     }
   }
 
-  sstrncpy (vl.host, q->host, sizeof (vl.host));
-  sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance));
+  if (q_area->interval > 0)
+    vl.interval = q_area->interval;
+
+  sstrncpy (vl.host, q_area->host, sizeof (vl.host));
+  sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
   sstrncpy (vl.type, r->type, sizeof (vl.type));
 
   /* Set vl.type_instance {{{ */
@@ -392,13 +415,14 @@ static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
     if (r->instance_prefix == NULL)
     {
       strjoin (vl.type_instance, sizeof (vl.type_instance),
-          r->instances_buffer, r->instances_num, "-");
+          r_area->instances_buffer, r->instances_num, "-");
     }
     else
     {
       char tmp[DATA_MAX_NAME_LEN];
 
-      strjoin (tmp, sizeof (tmp), r->instances_buffer, r->instances_num, "-");
+      strjoin (tmp, sizeof (tmp), r_area->instances_buffer,
+          r->instances_num, "-");
       tmp[sizeof (tmp) - 1] = 0;
 
       snprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
@@ -414,74 +438,82 @@ static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
   return (0);
 } /* }}} void udb_result_submit */
 
-static void udb_result_finish_result (udb_result_t *r) /* {{{ */
+static void udb_result_finish_result (const udb_result_t const *r, /* {{{ */
+    udb_result_preparation_area_t *prep_area)
 {
-  if (r == NULL)
+  if ((r == NULL) || (prep_area == NULL))
     return;
 
   if (r->legacy_mode == 1)
   {
-    udb_legacy_result_finish_result (r);
+    udb_legacy_result_finish_result (r, prep_area);
     return;
   }
 
   assert (r->legacy_mode == 0);
 
-  r->ds = NULL;
-  sfree (r->instances_pos);
-  sfree (r->values_pos);
-  sfree (r->instances_buffer);
-  sfree (r->values_buffer);
+  prep_area->ds = NULL;
+  sfree (prep_area->instances_pos);
+  sfree (prep_area->values_pos);
+  sfree (prep_area->instances_buffer);
+  sfree (prep_area->values_buffer);
 } /* }}} void udb_result_finish_result */
 
 static int udb_result_handle_result (udb_result_t *r, /* {{{ */
-    udb_query_t *q, char **column_values)
+    udb_query_preparation_area_t *q_area,
+    udb_result_preparation_area_t *r_area,
+    const udb_query_t const *q, char **column_values)
 {
   size_t i;
 
+  assert (r && q_area && r_area);
+
   if (r->legacy_mode == 1)
-    return (udb_legacy_result_handle_result (r, q, column_values));
+    return (udb_legacy_result_handle_result (r, q_area, r_area,
+          q, column_values));
 
   assert (r->legacy_mode == 0);
 
   for (i = 0; i < r->instances_num; i++)
-    r->instances_buffer[i] = column_values[r->instances_pos[i]];
+    r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]];
 
   for (i = 0; i < r->values_num; i++)
-    r->values_buffer[i] = column_values[r->values_pos[i]];
+    r_area->values_buffer[i] = column_values[r_area->values_pos[i]];
 
-  return udb_result_submit (r, q);
+  return udb_result_submit (r, r_area, q, q_area);
 } /* }}} int udb_result_handle_result */
 
-static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
+static int udb_result_prepare_result (const udb_result_t const *r, /* {{{ */
+    udb_result_preparation_area_t *prep_area,
     char **column_names, size_t column_num)
 {
   size_t i;
 
-  if (r == NULL)
+  if ((r == NULL) || (prep_area == NULL))
     return (-EINVAL);
 
   if (r->legacy_mode == 1)
-    return (udb_legacy_result_prepare_result (r, column_names, column_num));
+    return (udb_legacy_result_prepare_result (r, prep_area,
+          column_names, column_num));
 
   assert (r->legacy_mode == 0);
 
 #define BAIL_OUT(status) \
-  r->ds = NULL; \
-  sfree (r->instances_pos); \
-  sfree (r->values_pos); \
-  sfree (r->instances_buffer); \
-  sfree (r->values_buffer); \
+  prep_area->ds = NULL; \
+  sfree (prep_area->instances_pos); \
+  sfree (prep_area->values_pos); \
+  sfree (prep_area->instances_buffer); \
+  sfree (prep_area->values_buffer); \
   return (status)
 
   /* Make sure previous preparations are cleaned up. */
-  udb_result_finish_result (r);
-  r->instances_pos = NULL;
-  r->values_pos = NULL;
+  udb_result_finish_result (r, prep_area);
+  prep_area->instances_pos = NULL;
+  prep_area->values_pos = NULL;
 
   /* Read `ds' and check number of values {{{ */
-  r->ds = plugin_get_ds (r->type);
-  if (r->ds == NULL)
+  prep_area->ds = plugin_get_ds (r->type);
+  if (prep_area->ds == NULL)
   {
     ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
         "known by the daemon. See types.db(5) for details.",
@@ -489,12 +521,12 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
     BAIL_OUT (-1);
   }
 
-  if (((size_t) r->ds->ds_num) != r->values_num)
+  if (((size_t) prep_area->ds->ds_num) != r->values_num)
   {
     ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
         "requires exactly %i value%s, but the configuration specifies %zu.",
         r->type,
-        r->ds->ds_num, (r->ds->ds_num == 1) ? "" : "s",
+        prep_area->ds->ds_num, (prep_area->ds->ds_num == 1) ? "" : "s",
         r->values_num);
     BAIL_OUT (-1);
   }
@@ -504,30 +536,34 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
    * r->values_buffer {{{ */
   if (r->instances_num > 0)
   {
-    r->instances_pos = (size_t *) calloc (r->instances_num, sizeof (size_t));
-    if (r->instances_pos == NULL)
+    prep_area->instances_pos
+      = (size_t *) calloc (r->instances_num, sizeof (size_t));
+    if (prep_area->instances_pos == NULL)
     {
       ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
       BAIL_OUT (-ENOMEM);
     }
 
-    r->instances_buffer = (char **) calloc (r->instances_num, sizeof (char *));
-    if (r->instances_buffer == NULL)
+    prep_area->instances_buffer
+      = (char **) calloc (r->instances_num, sizeof (char *));
+    if (prep_area->instances_buffer == NULL)
     {
       ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
       BAIL_OUT (-ENOMEM);
     }
   } /* if (r->instances_num > 0) */
 
-  r->values_pos = (size_t *) calloc (r->values_num, sizeof (size_t));
-  if (r->values_pos == NULL)
+  prep_area->values_pos
+    = (size_t *) calloc (r->values_num, sizeof (size_t));
+  if (prep_area->values_pos == NULL)
   {
     ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
     BAIL_OUT (-ENOMEM);
   }
 
-  r->values_buffer = (char **) calloc (r->values_num, sizeof (char *));
-  if (r->values_buffer == NULL)
+  prep_area->values_buffer
+    = (char **) calloc (r->values_num, sizeof (char *));
+  if (prep_area->values_buffer == NULL)
   {
     ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
     BAIL_OUT (-ENOMEM);
@@ -543,7 +579,7 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
     {
       if (strcasecmp (r->instances[i], column_names[j]) == 0)
       {
-        r->instances_pos[i] = j;
+        prep_area->instances_pos[i] = j;
         break;
       }
     }
@@ -566,7 +602,7 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
     {
       if (strcasecmp (r->values[i], column_names[j]) == 0)
       {
-        r->values_pos[i] = j;
+        prep_area->values_pos[i] = j;
         break;
       }
     }
@@ -1029,33 +1065,45 @@ int udb_query_check_version (udb_query_t *q, unsigned int version) /* {{{ */
   return (1);
 } /* }}} int udb_query_check_version */
 
-void udb_query_finish_result (udb_query_t *q) /* {{{ */
+void udb_query_finish_result (const udb_query_t const *q, /* {{{ */
+    udb_query_preparation_area_t *prep_area)
 {
+  udb_result_preparation_area_t *r_area;
   udb_result_t *r;
 
-  if (q == NULL)
+  if ((q == NULL) || (prep_area == NULL))
     return;
 
-  q->column_num = 0;
-  sfree (q->host);
-  sfree (q->plugin);
-  sfree (q->db_name);
+  prep_area->column_num = 0;
+  sfree (prep_area->host);
+  sfree (prep_area->plugin);
+  sfree (prep_area->db_name);
 
-  for (r = q->results; r != NULL; r = r->next)
-    udb_result_finish_result (r);
+  prep_area->interval = -1;
+
+  for (r = q->results, r_area = prep_area->result_prep_areas;
+      r != NULL; r = r->next, r_area = r_area->next)
+  {
+    /* this may happen during error conditions of the caller */
+    if (r_area == NULL)
+      break;
+    udb_result_finish_result (r, r_area);
+  }
 } /* }}} void udb_query_finish_result */
 
-int udb_query_handle_result (udb_query_t *q, char **column_values) /* {{{ */
+int udb_query_handle_result (const udb_query_t const *q, /* {{{ */
+    udb_query_preparation_area_t *prep_area, char **column_values)
 {
+  udb_result_preparation_area_t *r_area;
   udb_result_t *r;
   int success;
   int status;
 
-  if (q == NULL)
+  if ((q == NULL) || (prep_area == NULL))
     return (-EINVAL);
 
-  if ((q->column_num < 1) || (q->host == NULL) || (q->plugin == NULL)
-      || (q->db_name == NULL))
+  if ((prep_area->column_num < 1) || (prep_area->host == NULL)
+      || (prep_area->plugin == NULL) || (prep_area->db_name == NULL))
   {
     ERROR ("db query utils: Query `%s': Query is not prepared; "
         "can't handle result.", q->name);
@@ -1067,19 +1115,21 @@ int udb_query_handle_result (udb_query_t *q, char **column_values) /* {{{ */
   {
     size_t i;
 
-    for (i = 0; i < q->column_num; i++)
+    for (i = 0; i < prep_area->column_num; i++)
     {
       DEBUG ("db query utils: udb_query_handle_result (%s, %s): "
           "column[%zu] = %s;",
-          q->db_name, q->name, i, column_values[i]);
+          prep_area->db_name, q->name, i, column_values[i]);
     }
   } while (0);
 #endif /* }}} */
 
   success = 0;
-  for (r = q->results; r != NULL; r = r->next)
+  for (r = q->results, r_area = prep_area->result_prep_areas;
+      r != NULL; r = r->next, r_area = r_area->next)
   {
-    status = udb_result_handle_result (r, q, column_values);
+    status = udb_result_handle_result (r, prep_area, r_area,
+        q, column_values);
     if (status == 0)
       success++;
   }
@@ -1087,34 +1137,39 @@ int udb_query_handle_result (udb_query_t *q, char **column_values) /* {{{ */
   if (success == 0)
   {
     ERROR ("db query utils: udb_query_handle_result (%s, %s): "
-        "All results failed.", q->db_name, q->name);
+        "All results failed.", prep_area->db_name, q->name);
     return (-1);
   }
 
   return (0);
 } /* }}} int udb_query_handle_result */
 
-int udb_query_prepare_result (udb_query_t *q, /* {{{ */
+int udb_query_prepare_result (const udb_query_t const *q, /* {{{ */
+    udb_query_preparation_area_t *prep_area,
     const char *host, const char *plugin, const char *db_name,
-    char **column_names, size_t column_num)
+    char **column_names, size_t column_num, int interval)
 {
+  udb_result_preparation_area_t *r_area;
   udb_result_t *r;
   int status;
 
-  if (q == NULL)
+  if ((q == NULL) || (prep_area == NULL))
     return (-EINVAL);
 
-  udb_query_finish_result (q);
+  udb_query_finish_result (q, prep_area);
+
+  prep_area->column_num = column_num;
+  prep_area->host = strdup (host);
+  prep_area->plugin = strdup (plugin);
+  prep_area->db_name = strdup (db_name);
 
-  q->column_num = column_num;
-  q->host = strdup (host);
-  q->plugin = strdup (plugin);
-  q->db_name = strdup (db_name);
+  prep_area->interval = interval;
 
-  if ((q->host == NULL) || (q->plugin == NULL) || (q->db_name == NULL))
+  if ((prep_area->host == NULL) || (prep_area->plugin == NULL)
+      || (prep_area->db_name == NULL))
   {
     ERROR ("db query utils: Query `%s': Prepare failed: Out of memory.", q->name);
-    udb_query_finish_result (q);
+    udb_query_finish_result (q, prep_area);
     return (-ENOMEM);
   }
 
@@ -1132,12 +1187,21 @@ int udb_query_prepare_result (udb_query_t *q, /* {{{ */
   } while (0);
 #endif
 
-  for (r = q->results; r != NULL; r = r->next)
+  for (r = q->results, r_area = prep_area->result_prep_areas;
+      r != NULL; r = r->next, r_area = r_area->next)
   {
-    status = udb_result_prepare_result (r, column_names, column_num);
+    if (! r_area)
+    {
+      ERROR ("db query utils: Query `%s': Invalid number of result "
+          "preparation areas.", q->name);
+      udb_query_finish_result (q, prep_area);
+      return (-EINVAL);
+    }
+
+    status = udb_result_prepare_result (r, r_area, column_names, column_num);
     if (status != 0)
     {
-      udb_query_finish_result (q);
+      udb_query_finish_result (q, prep_area);
       return (status);
     }
   }
@@ -1145,4 +1209,72 @@ int udb_query_prepare_result (udb_query_t *q, /* {{{ */
   return (0);
 } /* }}} int udb_query_prepare_result */
 
+udb_query_preparation_area_t *
+udb_query_allocate_preparation_area (udb_query_t *q) /* {{{ */
+{
+  udb_query_preparation_area_t   *q_area;
+  udb_result_preparation_area_t **next_r_area;
+  udb_result_t *r;
+
+  q_area = (udb_query_preparation_area_t *)malloc (sizeof (*q_area));
+  if (q_area == NULL)
+    return NULL;
+
+  memset (q_area, 0, sizeof (*q_area));
+
+  next_r_area = &q_area->result_prep_areas;
+  for (r = q->results; r != NULL; r = r->next)
+  {
+    udb_result_preparation_area_t *r_area;
+
+    r_area = (udb_result_preparation_area_t *)malloc (sizeof (*r_area));
+    if (r_area == NULL)
+    {
+      for (r_area = q_area->result_prep_areas;
+          r_area != NULL; r_area = r_area->next)
+      {
+        free (r_area);
+      }
+      free (q_area);
+      return NULL;
+    }
+
+    memset (r_area, 0, sizeof (*r_area));
+
+    *next_r_area = r_area;
+    next_r_area  = &r_area->next;
+  }
+
+  return (q_area);
+} /* }}} udb_query_preparation_area_t *udb_query_allocate_preparation_area */
+
+void
+udb_query_delete_preparation_area (udb_query_preparation_area_t *q_area) /* {{{ */
+{
+  udb_result_preparation_area_t *r_area;
+
+  if (q_area == NULL)
+    return;
+
+  r_area = q_area->result_prep_areas;
+  while (r_area != NULL)
+  {
+    udb_result_preparation_area_t *area = r_area;
+
+    r_area = r_area->next;
+
+    sfree (area->instances_pos);
+    sfree (area->values_pos);
+    sfree (area->instances_buffer);
+    sfree (area->values_buffer);
+    free (area);
+  }
+
+  sfree (q_area->host);
+  sfree (q_area->plugin);
+  sfree (q_area->db_name);
+
+  free (q_area);
+} /* }}} void udb_query_delete_preparation_area */
+
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 6703e92..fa2b288 100644 (file)
@@ -30,6 +30,9 @@
 struct udb_query_s;
 typedef struct udb_query_s udb_query_t;
 
+struct udb_query_preparation_area_s;
+typedef struct udb_query_preparation_area_s udb_query_preparation_area_t;
+
 typedef int (*udb_query_create_callback_t) (udb_query_t *q,
     oconfig_item_t *ci);
 
@@ -62,11 +65,19 @@ void *udb_query_get_user_data (udb_query_t *q);
  */
 int udb_query_check_version (udb_query_t *q, unsigned int version);
 
-int udb_query_prepare_result (udb_query_t *q,
+int udb_query_prepare_result (const udb_query_t const *q,
+    udb_query_preparation_area_t *prep_area,
     const char *host, const char *plugin, const char *db_name,
-    char **column_names, size_t column_num);
-int udb_query_handle_result (udb_query_t *q, char **column_values);
-void udb_query_finish_result (udb_query_t *q);
+    char **column_names, size_t column_num, int interval);
+int udb_query_handle_result (const udb_query_t const *q,
+    udb_query_preparation_area_t *prep_area, char **column_values);
+void udb_query_finish_result (const udb_query_t const *q,
+    udb_query_preparation_area_t *prep_area);
+
+udb_query_preparation_area_t *
+udb_query_allocate_preparation_area (udb_query_t *q);
+void
+udb_query_delete_preparation_area (udb_query_preparation_area_t *q_area);
 
 #endif /* UTILS_DB_QUERY_H */
 /* vim: set sw=2 sts=2 et : */
index a919316..ac88c0f 100644 (file)
@@ -23,6 +23,7 @@
 #include "plugin.h"
 #include "common.h"
 
+#include "utils_cache.h"
 #include "utils_format_json.h"
 
 static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
@@ -72,10 +73,11 @@ static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
 } /* }}} int buffer_add_string */
 
 static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
-                const data_set_t *ds, const value_list_t *vl)
+                const data_set_t *ds, const value_list_t *vl, int store_rates)
 {
   size_t offset = 0;
   int i;
+  gauge_t *rates = NULL;
 
   memset (buffer, 0, buffer_size);
 
@@ -84,11 +86,17 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
   status = ssnprintf (buffer + offset, buffer_size - offset, \
       __VA_ARGS__); \
   if (status < 1) \
+  { \
+    sfree(rates); \
     return (-1); \
+  } \
   else if (((size_t) status) >= (buffer_size - offset)) \
+  { \
+    sfree(rates); \
     return (-ENOMEM); \
+  } \
   else \
-  offset += ((size_t) status); \
+    offset += ((size_t) status); \
 } while (0)
 
   BUFFER_ADD ("[");
@@ -98,7 +106,28 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
       BUFFER_ADD (",");
 
     if (ds->ds[i].type == DS_TYPE_GAUGE)
-      BUFFER_ADD ("%g", vl->values[i].gauge);
+    {
+      if(isfinite (vl->values[i].gauge))
+        BUFFER_ADD ("%g", vl->values[i].gauge);
+      else
+        BUFFER_ADD ("null");
+    }
+    else if (store_rates)
+    {
+      if (rates == NULL)
+        rates = uc_get_rate (ds, vl);
+      if (rates == NULL)
+      {
+        WARNING ("utils_format_json: uc_get_rate failed.");
+        sfree(rates);
+        return (-1);
+      }
+
+      if(isfinite (rates[i]))
+        BUFFER_ADD ("%g", rates[i]);
+      else
+        BUFFER_ADD ("null");
+    }
     else if (ds->ds[i].type == DS_TYPE_COUNTER)
       BUFFER_ADD ("%llu", vl->values[i].counter);
     else if (ds->ds[i].type == DS_TYPE_DERIVE)
@@ -109,6 +138,7 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
     {
       ERROR ("format_json: Unknown data source type: %i",
           ds->ds[i].type);
+      sfree (rates);
       return (-1);
     }
   } /* for ds->ds_num */
@@ -117,13 +147,87 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
 #undef BUFFER_ADD
 
   DEBUG ("format_json: values_to_json: buffer = %s;", buffer);
-
+  sfree(rates);
   return (0);
 } /* }}} int values_to_json */
 
-static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
+static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */
+                const data_set_t *ds, const value_list_t *vl)
+{
+  size_t offset = 0;
+  int i;
+
+  memset (buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) do { \
+  int status; \
+  status = ssnprintf (buffer + offset, buffer_size - offset, \
+      __VA_ARGS__); \
+  if (status < 1) \
+    return (-1); \
+  else if (((size_t) status) >= (buffer_size - offset)) \
+    return (-ENOMEM); \
+  else \
+    offset += ((size_t) status); \
+} while (0)
+
+  BUFFER_ADD ("[");
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    if (i > 0)
+      BUFFER_ADD (",");
+
+    BUFFER_ADD ("\"%s\"", DS_TYPE_TO_STRING (ds->ds[i].type));
+  } /* for ds->ds_num */
+  BUFFER_ADD ("]");
+
+#undef BUFFER_ADD
+
+  DEBUG ("format_json: dstypes_to_json: buffer = %s;", buffer);
+
+  return (0);
+} /* }}} int dstypes_to_json */
+
+static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */
                 const data_set_t *ds, const value_list_t *vl)
 {
+  size_t offset = 0;
+  int i;
+
+  memset (buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) do { \
+  int status; \
+  status = ssnprintf (buffer + offset, buffer_size - offset, \
+      __VA_ARGS__); \
+  if (status < 1) \
+    return (-1); \
+  else if (((size_t) status) >= (buffer_size - offset)) \
+    return (-ENOMEM); \
+  else \
+    offset += ((size_t) status); \
+} while (0)
+
+  BUFFER_ADD ("[");
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    if (i > 0)
+      BUFFER_ADD (",");
+
+    BUFFER_ADD ("\"%s\"", ds->ds[i].name);
+  } /* for ds->ds_num */
+  BUFFER_ADD ("]");
+
+#undef BUFFER_ADD
+
+  DEBUG ("format_json: dsnames_to_json: buffer = %s;", buffer);
+
+  return (0);
+} /* }}} int dsnames_to_json */
+
+static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
+                const data_set_t *ds, const value_list_t *vl, int store_rates)
+{
   char temp[512];
   size_t offset = 0;
   int status;
@@ -145,11 +249,21 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
    * a square bracket in `format_json_finalize'. */
   BUFFER_ADD (",{");
 
-  status = values_to_json (temp, sizeof (temp), ds, vl);
+  status = values_to_json (temp, sizeof (temp), ds, vl, store_rates);
   if (status != 0)
     return (status);
   BUFFER_ADD ("\"values\":%s", temp);
 
+  status = dstypes_to_json (temp, sizeof (temp), ds, vl);
+  if (status != 0)
+    return (status);
+  BUFFER_ADD (",\"dstypes\":%s", temp);
+
+  status = dsnames_to_json (temp, sizeof (temp), ds, vl);
+  if (status != 0)
+    return (status);
+  BUFFER_ADD (",\"dsnames\":%s", temp);
+
   BUFFER_ADD (",\"time\":%lu", (unsigned long) vl->time);
   BUFFER_ADD (",\"interval\":%i", vl->interval);
 
@@ -179,12 +293,12 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
 static int format_json_value_list_nocheck (char *buffer, /* {{{ */
     size_t *ret_buffer_fill, size_t *ret_buffer_free,
     const data_set_t *ds, const value_list_t *vl,
-    size_t temp_size)
+    int store_rates, size_t temp_size)
 {
   char temp[temp_size];
   int status;
 
-  status = value_list_to_json (temp, sizeof (temp), ds, vl);
+  status = value_list_to_json (temp, sizeof (temp), ds, vl, store_rates);
   if (status != 0)
     return (status);
   temp_size = strlen (temp);
@@ -250,7 +364,7 @@ int format_json_finalize (char *buffer, /* {{{ */
 
 int format_json_value_list (char *buffer, /* {{{ */
     size_t *ret_buffer_fill, size_t *ret_buffer_free,
-    const data_set_t *ds, const value_list_t *vl)
+    const data_set_t *ds, const value_list_t *vl, int store_rates)
 {
   if ((buffer == NULL)
       || (ret_buffer_fill == NULL) || (ret_buffer_free == NULL)
@@ -262,7 +376,7 @@ int format_json_value_list (char *buffer, /* {{{ */
 
   return (format_json_value_list_nocheck (buffer,
         ret_buffer_fill, ret_buffer_free, ds, vl,
-        (*ret_buffer_free) - 2));
+        store_rates, (*ret_buffer_free) - 2));
 } /* }}} int format_json_value_list */
 
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 880a79d..c902e27 100644 (file)
@@ -29,7 +29,7 @@ int format_json_initialize (char *buffer,
     size_t *ret_buffer_fill, size_t *ret_buffer_free);
 int format_json_value_list (char *buffer,
     size_t *ret_buffer_fill, size_t *ret_buffer_free,
-    const data_set_t *ds, const value_list_t *vl);
+    const data_set_t *ds, const value_list_t *vl, int store_rates);
 int format_json_finalize (char *buffer,
     size_t *ret_buffer_fill, size_t *ret_buffer_free);
 
index bf5f7e4..6a0c6f0 100644 (file)
@@ -139,17 +139,36 @@ int llist_size (llist_t *l)
        return (l ? l->size : 0);
 }
 
+static int llist_strcmp (llentry_t *e, void *ud)
+{
+       if ((e == NULL) || (ud == NULL))
+               return (-1);
+       return (strcmp (e->key, (const char *)ud));
+}
+
 llentry_t *llist_search (llist_t *l, const char *key)
 {
+       return (llist_search_custom (l, llist_strcmp, (void *)key));
+}
+
+llentry_t *llist_search_custom (llist_t *l,
+               int (*compare) (llentry_t *, void *), void *user_data)
+{
        llentry_t *e;
 
        if (l == NULL)
                return (NULL);
 
-       for (e = l->head; e != NULL; e = e->next)
-               if (strcmp (key, e->key) == 0)
+       e = l->head;
+       while (e != NULL) {
+               llentry_t *next = e->next;
+
+               if (compare (e, user_data) == 0)
                        break;
 
+               e = next;
+       }
+
        return (e);
 }
 
index c3753d8..19d8d94 100644 (file)
@@ -54,6 +54,8 @@ void llist_remove (llist_t *l, llentry_t *e);
 int llist_size (llist_t *l);
 
 llentry_t *llist_search (llist_t *l, const char *key);
+llentry_t *llist_search_custom (llist_t *l,
+               int (*compare) (llentry_t *, void *), void *user_data);
 
 llentry_t *llist_head (llist_t *l);
 llentry_t *llist_tail (llist_t *l);
index bdbad3f..0f87bc0 100644 (file)
 #include <regex.h>
 
 #define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
+#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
 
 struct cu_match_s
 {
   regex_t regex;
+  regex_t excluderegex;
   int flags;
 
   int (*callback) (const char *str, char * const *matches, size_t matches_num,
@@ -210,7 +212,7 @@ static int default_callback (const char __attribute__((unused)) *str,
 /*
  * Public functions
  */
-cu_match_t *match_create_callback (const char *regex,
+cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
                int (*callback) (const char *str,
                  char * const *matches, size_t matches_num, void *user_data),
                void *user_data)
@@ -218,7 +220,8 @@ cu_match_t *match_create_callback (const char *regex,
   cu_match_t *obj;
   int status;
 
-  DEBUG ("utils_match: match_create_callback: regex = %s", regex);
+  DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
+        regex, excluderegex);
 
   obj = (cu_match_t *) malloc (sizeof (cu_match_t));
   if (obj == NULL)
@@ -233,13 +236,26 @@ cu_match_t *match_create_callback (const char *regex,
     return (NULL);
   }
 
+  if (excluderegex && strcmp(excluderegex, "") != 0) {
+    status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED);
+    if (status != 0)
+    {
+       ERROR ("Compiling the excluding regular expression \"%s\" failed.",
+              excluderegex);
+       sfree (obj);
+       return (NULL);
+    }
+    obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
+  }
+
   obj->callback = callback;
   obj->user_data = user_data;
 
   return (obj);
 } /* cu_match_t *match_create_callback */
 
-cu_match_t *match_create_simple (const char *regex, int match_ds_type)
+cu_match_t *match_create_simple (const char *regex,
+                                const char *excluderegex, int match_ds_type)
 {
   cu_match_value_t *user_data;
   cu_match_t *obj;
@@ -250,7 +266,8 @@ cu_match_t *match_create_simple (const char *regex, int match_ds_type)
   memset (user_data, '\0', sizeof (cu_match_value_t));
   user_data->ds_type = match_ds_type;
 
-  obj = match_create_callback (regex, default_callback, user_data);
+  obj = match_create_callback (regex, excluderegex,
+                              default_callback, user_data);
   if (obj == NULL)
   {
     sfree (user_data);
@@ -286,6 +303,17 @@ int match_apply (cu_match_t *obj, const char *str)
   if ((obj == NULL) || (str == NULL))
     return (-1);
 
+  if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
+    status = regexec (&obj->excluderegex, str,
+                     STATIC_ARRAY_SIZE (re_match), re_match,
+                     /* eflags = */ 0);
+    /* Regex did match, so exclude this line */
+    if (status == 0) {
+      DEBUG("ExludeRegex matched, don't count that line\n");
+      return (0);
+    }
+  }
+
   status = regexec (&obj->regex, str,
       STATIC_ARRAY_SIZE (re_match), re_match,
       /* eflags = */ 0);
index 5a0c033..9e47d5c 100644 (file)
@@ -82,8 +82,10 @@ typedef struct cu_match_value_s cu_match_value_t;
  *  then only the submatch (the part in the parenthesis) will be passed to the
  *  callback. If there is no submatch, then the entire string is passed to the
  *  callback.
+ *  The optional `excluderegex' allows to exclude the line from the match, if
+ *  the excluderegex matches.
  */
-cu_match_t *match_create_callback (const char *regex,
+cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
                int (*callback) (const char *str,
                  char * const *matches, size_t matches_num, void *user_data),
                void *user_data);
@@ -112,7 +114,8 @@ cu_match_t *match_create_callback (const char *regex,
  *    The function will not search for anything in the string and increase
  *    value.counter by one.
  */
-cu_match_t *match_create_simple (const char *regex, int ds_type);
+cu_match_t *match_create_simple (const char *regex,
+                                const char *excluderegex, int ds_type);
 
 /*
  * NAME
index 22c7917..8ae2208 100644 (file)
@@ -191,7 +191,7 @@ int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
 } /* int tail_match_add_match */
 
 int tail_match_add_match_simple (cu_tail_match_t *obj,
-    const char *regex, int ds_type,
+    const char *regex, const char *excluderegex, int ds_type,
     const char *plugin, const char *plugin_instance,
     const char *type, const char *type_instance)
 {
@@ -199,7 +199,7 @@ int tail_match_add_match_simple (cu_tail_match_t *obj,
   cu_tail_match_simple_t *user_data;
   int status;
 
-  match = match_create_simple (regex, ds_type);
+  match = match_create_simple (regex, excluderegex, ds_type);
   if (match == NULL)
     return (-1);
 
index d08c728..7659745 100644 (file)
@@ -97,12 +97,13 @@ int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
  *  The values gathered are dispatched by the tail_match module in this case. The
  *  passed `plugin', `plugin_instance', `type', and `type_instance' are
  *  directly used when submitting these values.
+ *  With excluderegex it is possible to exlude lines from the match.
  *
  * RETURN VALUE
  *   Zero upon success, non-zero otherwise.
  */
 int tail_match_add_match_simple (cu_tail_match_t *obj,
-    const char *regex, int ds_type,
+    const char *regex, const char *excluderegex, int ds_type,
     const char *plugin, const char *plugin_instance,
     const char *type, const char *type_instance);
 
index f14636b..ab8757e 100644 (file)
@@ -49,6 +49,7 @@ struct wh_callback_s
         int   verify_peer;
         int   verify_host;
         char *cacert;
+        int   store_rates;
 
 #define WH_FORMAT_COMMAND 0
 #define WH_FORMAT_JSON    1
@@ -142,7 +143,7 @@ static int wh_callback_init (wh_callback_t *cb) /* {{{ */
                 ssnprintf (cb->credentials, credentials_size, "%s:%s",
                                 cb->user, (cb->pass == NULL) ? "" : cb->pass);
                 curl_easy_setopt (cb->curl, CURLOPT_USERPWD, cb->credentials);
-                curl_easy_setopt (cb->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
+                curl_easy_setopt (cb->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
         }
 
         curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYPEER, cb->verify_peer);
@@ -271,11 +272,13 @@ static void wh_callback_free (void *data) /* {{{ */
 
 static int wh_value_list_to_string (char *buffer, /* {{{ */
                 size_t buffer_size,
-                const data_set_t *ds, const value_list_t *vl)
+                const data_set_t *ds, const value_list_t *vl,
+                wh_callback_t *cb)
 {
         size_t offset = 0;
         int status;
         int i;
+        gauge_t *rates = NULL;
 
         assert (0 == strcmp (ds->type, vl->type));
 
@@ -285,9 +288,15 @@ static int wh_value_list_to_string (char *buffer, /* {{{ */
         status = ssnprintf (buffer + offset, buffer_size - offset, \
                         __VA_ARGS__); \
         if (status < 1) \
+        { \
+                sfree (rates); \
                 return (-1); \
+        } \
         else if (((size_t) status) >= (buffer_size - offset)) \
+        { \
+                sfree (rates); \
                 return (-1); \
+        } \
         else \
                 offset += ((size_t) status); \
 } while (0)
@@ -295,26 +304,40 @@ static int wh_value_list_to_string (char *buffer, /* {{{ */
         BUFFER_ADD ("%lu", (unsigned long) vl->time);
 
         for (i = 0; i < ds->ds_num; i++)
-{
-        if (ds->ds[i].type == DS_TYPE_GAUGE)
-                BUFFER_ADD (":%f", vl->values[i].gauge);
-        else if (ds->ds[i].type == DS_TYPE_COUNTER)
-                BUFFER_ADD (":%llu", vl->values[i].counter);
-        else if (ds->ds[i].type == DS_TYPE_DERIVE)
-                BUFFER_ADD (":%"PRIi64, vl->values[i].derive);
-        else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
-                BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
-        else
         {
-                ERROR ("write_http plugin: Unknown data source type: %i",
-                                ds->ds[i].type);
-                return (-1);
-        }
-} /* for ds->ds_num */
+                if (ds->ds[i].type == DS_TYPE_GAUGE)
+                        BUFFER_ADD (":%f", vl->values[i].gauge);
+                else if (cb->store_rates)
+                {
+                        if (rates == NULL)
+                                rates = uc_get_rate (ds, vl);
+                        if (rates == NULL)
+                        {
+                                WARNING ("write_http plugin: "
+                                                "uc_get_rate failed.");
+                                return (-1);
+                        }
+                        BUFFER_ADD (":%g", rates[i]);
+                }
+                else if (ds->ds[i].type == DS_TYPE_COUNTER)
+                        BUFFER_ADD (":%llu", vl->values[i].counter);
+                else if (ds->ds[i].type == DS_TYPE_DERIVE)
+                        BUFFER_ADD (":%"PRIi64, vl->values[i].derive);
+                else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+                        BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
+                else
+                {
+                        ERROR ("write_http plugin: Unknown data source type: %i",
+                                        ds->ds[i].type);
+                        sfree (rates);
+                        return (-1);
+                }
+        } /* for ds->ds_num */
 
 #undef BUFFER_ADD
 
-return (0);
+        sfree (rates);
+        return (0);
 } /* }}} int wh_value_list_to_string */
 
 static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{{ */
@@ -343,7 +366,7 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{
 
         /* Convert the values to an ASCII representation and put that into
          * `values'. */
-        status = wh_value_list_to_string (values, sizeof (values), ds, vl);
+        status = wh_value_list_to_string (values, sizeof (values), ds, vl, cb);
         if (status != 0) {
                 ERROR ("write_http plugin: error with "
                                 "wh_value_list_to_string");
@@ -423,7 +446,7 @@ static int wh_write_json (const data_set_t *ds, const value_list_t *vl, /* {{{ *
         status = format_json_value_list (cb->send_buffer,
                         &cb->send_buffer_fill,
                         &cb->send_buffer_free,
-                        ds, vl);
+                        ds, vl, cb->store_rates);
         if (status == (-ENOMEM))
         {
                 status = wh_flush_nolock (/* timeout = */ -1, cb);
@@ -437,7 +460,7 @@ static int wh_write_json (const data_set_t *ds, const value_list_t *vl, /* {{{ *
                 status = format_json_value_list (cb->send_buffer,
                                 &cb->send_buffer_fill,
                                 &cb->send_buffer_free,
-                                ds, vl);
+                                ds, vl, cb->store_rates);
         }
         if (status != 0)
         {
@@ -589,6 +612,8 @@ static int wh_config_url (oconfig_item_t *ci) /* {{{ */
                         config_set_string (&cb->cacert, child);
                 else if (strcasecmp ("Format", child->key) == 0)
                         config_set_format (cb, child);
+                else if (strcasecmp ("StoreRates", child->key) == 0)
+                        config_set_boolean (&cb->store_rates, child);
                 else
                 {
                         ERROR ("write_http plugin: Invalid configuration "
index bd87bbb..a84eddb 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-DEFAULT_VERSION="4.8.2.git"
+DEFAULT_VERSION="4.8.4.git"
 
 VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"