Merge pull request #362 from mikepilat/add-snmpv3
authorPierre-Yves Ritschard <pyr@spootnik.org>
Fri, 20 Dec 2013 08:15:21 +0000 (00:15 -0800)
committerPierre-Yves Ritschard <pyr@spootnik.org>
Fri, 20 Dec 2013 08:15:21 +0000 (00:15 -0800)
Add SNMPv3 authentication and encryption support

70 files changed:
.gitignore
AUTHORS
ChangeLog
README
bindings/java/org/collectd/java/GenericJMXConfConnection.java
clean.sh
configure.in
contrib/collectd_network.py
contrib/collection3/etc/collection.conf
contrib/postgresql/collectd_insert.sql
contrib/redhat/collectd.spec
src/Makefile.am
src/apache.c
src/aquaero.c [new file with mode: 0644]
src/ascent.c
src/bind.c
src/collectd-email.pod
src/collectd-exec.pod
src/collectd-java.pod
src/collectd-nagios.pod
src/collectd-perl.pod
src/collectd-python.pod
src/collectd-snmp.pod
src/collectd-tg.pod
src/collectd-threshold.pod
src/collectd-unixsock.pod
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.h
src/collectd.pod
src/collectdctl.pod
src/collectdmon.pod
src/common.c
src/configfile.c
src/configfile.h
src/csv.c
src/curl.c
src/curl_json.c
src/curl_xml.c
src/dbi.c
src/df.c
src/dns.c
src/exec.c
src/filter_chain.c
src/libcollectdclient/collectd/network.h
src/libcollectdclient/network.c
src/memcached.c
src/modbus.c
src/mysql.c
src/netlink.c
src/nginx.c
src/plugin.c
src/postgresql.c
src/rrdcached.c
src/rrdtool.c
src/sigrok.c [new file with mode: 0644]
src/snmp.c
src/statsd.c [new file with mode: 0644]
src/tail.c
src/threshold.c
src/types.db
src/types.db.pod
src/utils_latency.c [new file with mode: 0644]
src/utils_latency.h [new file with mode: 0644]
src/utils_mount.h
src/varnish.c
src/write_graphite.c
src/write_http.c
src/write_riemann.c
version-gen.sh

index 6e87aaf..e1120d3 100644 (file)
@@ -33,11 +33,14 @@ src/stamp-h1
 .libs/
 src/collectd
 src/collectd-nagios
+src/collectd-tg
 src/collectdctl
 src/collectdmon
 src/*.1
 src/*.5
-src/libcollectdclient/lcc_features.h
+src/.pod2man.tmp.*
+src/libcollectdclient/collectd/lcc_features.h
+src/utils_vl_lookup_test
 
 # patch stuff
 *.rej
@@ -61,6 +64,7 @@ bindings/.perl-directory-stamp
 bindings/perl/Collectd/pm_to_blib
 bindings/perl/blib/
 bindings/perl/pm_to_blib
+bindings/buildperl
 
 # java stuff
 bindings/java/java-build-stamp
diff --git a/AUTHORS b/AUTHORS
index adb7481..31d132f 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -21,6 +21,9 @@ Akkarit Sangpetch <asangpet at andrew.cmu.edu>
 Alessandro Iurlano <alessandro.iurlano at gmail.com>
  - Initial filecount plugin.
 
+Alex Deymo <deymo at chromium.org>
+ - aquaero plugin.
+
 Alvaro Barcellos <alvaro.barcellos at gmail.com>
  - Don't-fork patch.
 
@@ -28,6 +31,9 @@ Amit Gupta <amit.gupta221 at gmail.com>
  - Multiple servers in the apache plugin.
  - curl_xml plugin.
 
+Andreas Henriksson <andreas at fatal.se>
+ - libmnl support in the netlink plugin.
+
 Anthony Dewhurst <dewhurst at gmail.com>
  - zfs_arc plugin.
 
@@ -41,6 +47,9 @@ Aurélien Reynaud <collectd at wattapower.net>
  - LPAR plugin.
  - Various fixes for AIX, HP-UX and Solaris.
 
+Bert Vermeulen <bert at biot.com>
+ - sigrok plugin
+
 Bruno Prémont <bonbons at linux-vserver.org>
  - BIND plugin.
  - Many bugreports and -fixes in various plugins,
@@ -125,6 +134,11 @@ Manuel Sanmartin
    + swap
  - Various AIX-related fixes and hacks.
 
+Marc Fournier <marc.fournier at camptocamp.com>
+ - Various fixes to the varnish plugin.
+ - RPM specfile update.
+ - libmnl support in the netlink plugin.
+
 Marco Chiappero <marco at absence.it>
  - uptime plugin.
  - ip6tables support in the iptables plugin.
index 27f58e9..3c8128a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,72 @@
+2013-08-18, Version 5.4.0
+       * collectd: The "LoadPlugin" config option no longer attempts to load
+         plugins twice. If more than one "LoadPlugin" statement or block is
+         encountered, only the first will have any effect.
+       * collectd: The "AutoLoadPlugin" option allows to automatically load
+         plugins for which a configuration is found.
+       * collectd: The "WriteQueueLimitHigh" and "WriteQueueLimitLow" options
+         allow collectd to drop values when under stress, to avoid running out
+         of memory. Thanks to Yves Mettier for his patch.
+       * amqp plugin: The "GraphiteSeparateInstances" and
+         "GraphiteAlwaysAppendDS" options have been added. Thanks to Laurent
+         for the patch.
+       * aquaero plugin: This new plugin reads various metrics, e.g. fan
+         speeds and temperatures, from Aquaero 5, a fan and water cooling
+         control panel. Thanks to Alex Deymo for his patch.
+       * curl plugin: The "MeasureResponseCode" option has been added. Thanks
+         to Jan Matějka for his patch.
+       * curl_json plugin: Support for UNIX domain sockets and array wildcards
+         has been added. Thanks to Jim Radford for his patch.
+       * curl_xml plugin: Support for long URLs has been improved.
+       * cgroups plugin: This new plugin collects CPU accounting information
+         for processes in a cgroup. Thanks to Michael Stapelberg for his patch.
+       * df plugin: The "ValuesAbsolute" and "ValuesPercentage" options have
+         been added. Thanks to Vedran Bartonicek for the patch.
+       * exec plugin: Do UID / GID lookups before forking. This should prevent
+         a race condition in the NSS library. Thanks to Ceri Storey for the
+         patch.
+       * lvm plugin: This new plugin collects size information from Linux'
+         Logical Volume Manager (LVM). Thanks to Chad Malfait for his work.
+       * memcached plugin: Support for increment and decrement counts has been
+         added. Thanks to Blake Matheny for the patch.
+       * mic plugin: This new plugin collects CPU and memory usage, power
+         consumption and temperatures of Intel's Many-Integrated-Core (MIC)
+         architecture, such as Xeon Phi cards. Thanks to Evan Felix for his
+         work.
+       * netlink plugin: This plugin has been converted to use the supported
+         "libmnl" library. Thanks to Andreas Henriksson for his patch.
+       * nginx plugin: Collection of accepted and handled connections has been
+         added. Thanks to Patrick Shan for his patch.
+       * sigrok plugin: This new plugin collects metrics from sigrok, a signal
+         processing framework reading various hardware devices, from light
+         meters to spectrum analyzers. Thanks to Bert Vermeulen for his patch.
+       * statsd plugin: This new plugin listens to a UDP socket and reads
+         metrics in the StatsD format.
+       * varnish plugin: Many additional metrics have been added. Thanks to
+         Nick Stenning for his patch.
+       * write_graphite plugin: Support for "UDP" has been added. Thanks to
+         Javier Maestro for his patch.
+       * write_riemann plugin: The "TTLFactor" option has been added.
+       * zfs_arc plugin: Support for FreeBSD has been added. Thanks to Xin Li
+         for his patch.
+
+2013-07-13, Version 5.3.1
+       * Documentation: Various fixes.
+       * Configuration: Fix error handling: Errors in included files were
+         ignored, causing configuration mistakes to go unnoticed.
+       * dns plugin: Don't abort when PCAP returns an error.
+       * modbus plugin: The reconnection strategy was improved, fixing a
+         segfault in the libmodbud library. Thanks to Stefan Nickl and
+         Fabien Wernli for their patches.
+       * mysql plugin: The notification about a newly running MySQL slave
+         thread has been fixed. Thanks to Joaquín Cuenca Abela for the patch.
+       * snmp plugin: A build issue has been fixed (C99 mixed declaration).
+         The end-of-tree check has been improved by Pierre-Yves Ritschard.
+       * threshold plugin: Handling of the "Interesting" configuration option
+         has been fixed. Thanks to Björn for the patch.
+       * write_riemann plugin: A memory leak has been fixed. Thanks to Dave
+         Cottlehuber for reporting it.
+
 2013-04-09, Version 5.3.0
        * collectd: The "Include" statements can now be limited to include
          only matching files in a directory. Thanks to Sebastian Harl for his
diff --git a/README b/README
index 3f65963..fa88f38 100644 (file)
--- a/README
+++ b/README
@@ -26,6 +26,9 @@ Features
       Sensors in Macs running Mac OS X / Darwin: Temperature, fanspeed and
       voltage sensors.
 
+    - aquaero
+      Various sensors in the Aquaero 5 watercooling board made by Aquacomputer.
+
     - ascent
       Statistics about Ascent, a free server for the game `World of Warcraft'.
 
@@ -280,6 +283,11 @@ Features
     - serial
       RX and TX of serial interfaces. Linux only; needs root privileges.
 
+    - sigrok
+      Uses libsigrok as a backend, allowing any sigrok-supported device
+      to have its measurements fed to collectd. This includes multimeters,
+      sound level meters, thermometers, and much more.
+
     - snmp
       Read values from SNMP (Simple Network Management Protocol) enabled
       network devices such as switches, routers, thermometers, rack monitoring
@@ -557,10 +565,18 @@ Prerequisites
 
   * A POSIX-threads (pthread) implementation.
     Since gathering some statistics is slow (network connections, slow devices,
-    etc) the collectd is parallelized. The POSIX threads interface is being
+    etc) collectd is parallelized. The POSIX threads interface is being
     used and should be found in various implementations for hopefully all
     platforms.
 
+  * aerotools-ng (optional)
+    Used by the `aquaero' plugin. Currently, the `libaquaero5' library, which
+    is used by the `aerotools-ng' toolkit, is not compiled as a shared object
+    nor does it feature an installation routine. Therefore, you need to point
+    collectd's configure script at the source directory of the `aerotools-ng'
+    project.
+    <https://github.com/lynix/aerotools-ng>
+
   * CoreFoundation.framework and IOKit.framework (optional)
     For compiling on Darwin in general and the `apple_sensors' plugin in
     particular.
@@ -618,6 +634,10 @@ Prerequisites
     Used by the `memcachec' plugin to connect to a memcache daemon.
     <http://tangent.org/552/libmemcached.html>
 
+  * libmnl (optional)
+    Used by the `netlink' plugin.
+    <http://www.netfilter.org/projects/libmnl/>
+
   * libmodbus (optional)
     Used by the “modbus” plugin to communicate with Modbus/TCP devices. The
     “modbus” plugin works with version 2.0.3 of the library – due to frequent
@@ -632,10 +652,6 @@ Prerequisites
     Required for the “netapp” plugin.
     This library is part of the “Manage ONTAP SDK” published by NetApp.
 
-  * libnetlink (optional)
-    Used, obviously, for the `netlink' plugin.
-    <http://www.linuxfoundation.org/en/Net:Iproute2>
-
   * libnetsnmp (optional)
     For the `snmp' plugin.
     <http://www.net-snmp.org/>
@@ -701,6 +717,11 @@ Prerequisites
     To read from `lm_sensors', see the `sensors' plugin.
     <http://www.lm-sensors.org/>
 
+  * libsigrok (optional)
+    Used by the sigrok plugin. In addition, libsigrok depends on glib,
+    libzip, and optionally (depending on which drivers are enabled) on
+    libusb, libftdi and libudev.
+
   * libstatgrab (optional)
     Used by various plugins to collect statistics on systems other than Linux
     and/or Solaris.
index 7fad08f..99b140a 100644 (file)
@@ -118,6 +118,7 @@ private void connect () /* {{{ */
 
     environment = new HashMap ();
     environment.put (JMXConnector.CREDENTIALS, credentials);
+    environment.put(JMXConnectorFactory.PROTOCOL_PROVIDER_CLASS_LOADER, this.getClass().getClassLoader());
   }
 
   try
index 9defb05..46e8c29 100755 (executable)
--- a/clean.sh
+++ b/clean.sh
@@ -21,6 +21,7 @@ true \
 && rm -f Makefile \
 && rm -f Makefile.in \
 && rm -f missing \
+&& rm -f INSTALL \
 && rm -f -r src/.deps \
 && rm -f -r src/.libs \
 && rm -f src/*.o \
@@ -28,6 +29,11 @@ true \
 && rm -f src/*.lo \
 && rm -f src/collectd \
 && rm -f src/collectd.1 \
+&& rm -f src/collectd.conf \
+&& rm -f src/collectdctl \
+&& rm -f src/collectd-tg \
+&& rm -f src/collectd-nagios \
+&& rm -f src/collectdmon \
 && rm -f src/config.h \
 && rm -f src/config.h.in \
 && rm -f src/config.h.in~ \
@@ -35,6 +41,18 @@ true \
 && rm -f src/Makefile.in \
 && rm -f src/stamp-h1 \
 && rm -f src/stamp-h1.in \
+&& rm -f src/*.pb-c.c \
+&& rm -f src/*.pb-c.h \
+&& rm -f src/Makefile.in \
+&& rm -f src/liboconfig/*.o \
+&& rm -f src/liboconfig/*.la \
+&& rm -f src/liboconfig/*.lo \
+&& rm -f -r src/liboconfig/.libs \
+&& rm -f -r src/liboconfig/Makefile \
+&& rm -f -r src/liboconfig/Makefile.in \
+&& rm -f -r src/liboconfig/parser.c \
+&& rm -f -r src/liboconfig/parser.h \
+&& rm -f -r src/liboconfig/scanner.c \
 && rm -f -r src/libping/.libs \
 && rm -f src/libping/*.o \
 && rm -f src/libping/*.la \
@@ -48,5 +66,17 @@ true \
 && rm -f src/libcollectdclient/*.o \
 && rm -f src/libcollectdclient/*.la \
 && rm -f src/libcollectdclient/*.lo \
+&& rm -f src/libcollectdclient/Makefile \
+&& rm -f src/libcollectdclient/Makefile.in \
+&& rm -f src/libcollectdclient/collectd/lcc_features.h \
+&& rm -f src/libcollectdclient/libcollectdclient.pc \
+&& rm -f bindings/Makefile \
+&& rm -f bindings/Makefile.in \
+&& rm -f -r bindings/java/.libs \
+&& rm -f bindings/java/Makefile \
+&& rm -f bindings/java/Makefile.in \
+&& rm -f bindings/java/java-build-stamp \
+&& rm -f bindings/java/org/collectd/api/*.class \
+&& rm -f bindings/java/org/collectd/java/*.class \
 && rm -f bindings/.perl-directory-stamp \
 && rm -f -r bindings/buildperl
index 27c6cd3..c4d9558 100644 (file)
@@ -1121,6 +1121,17 @@ else
   AC_MSG_ERROR([Didn't find out how doubles are stored in memory. Sorry.])
 fi; fi; fi
 
+# --with-useragent {{{
+AC_ARG_WITH(useragent, [AS_HELP_STRING([--with-useragent@<:@=AGENT@:>@], [User agent to use on http requests])],
+[
+    if test "x$withval" != "xno" && test "x$withval" != "xyes"
+    then
+        AC_DEFINE_UNQUOTED(COLLECTD_USERAGENT, ["$withval"], [User agent for http requests])
+    fi
+])
+
+# }}}
+
 have_getfsstat="no"
 AC_CHECK_FUNCS(getfsstat, [have_getfsstat="yes"])
 have_getvfsstat="no"
@@ -1480,6 +1491,63 @@ then
 fi
 AM_CONDITIONAL(BUILD_WITH_LIBKVM_OPENFILES, test "x$with_kvm_openfiles" = "xyes")
 
+# --with-libaquaero5 {{{
+AC_ARG_WITH(libaquaero5, [AS_HELP_STRING([--with-libaquaero5@<:@=PREFIX@:>@], [Path to aquatools-ng source code.])],
+[
+ if test "x$withval" = "xyes"
+ then
+        with_libaquaero5="yes"
+ else if test "x$withval" = "xno"
+ then
+        with_libaquaero5="no"
+ else
+        with_libaquaero5="yes"
+        LIBAQUAERO5_CFLAGS="$LIBAQUAERO5_CFLAGS -I$withval/src"
+        LIBAQUAERO5_LDFLAGS="$LIBAQUAERO5_LDFLAGS -L$withval/obj"
+ fi; fi
+],
+[with_libaquaero5="yes"])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $LIBAQUAERO5_CFLAGS"
+LDFLAGS="$LDFLAGS $LIBAQUAERO5_LDFLAGS"
+
+if test "x$with_libaquaero5" = "xyes"
+then
+       if test "x$LIBAQUAERO5_CFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([libaquaero5 CPPFLAGS: $LIBAQUAERO5_CFLAGS])
+       fi
+       AC_CHECK_HEADERS(libaquaero5.h,
+       [with_libaquaero5="yes"],
+       [with_libaquaero5="no (libaquaero5.h not found)"])
+fi
+if test "x$with_libaquaero5" = "xyes"
+then
+       if test "x$LIBAQUAERO5_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([libaquaero5 LDFLAGS: $LIBAQUAERO5_LDFLAGS])
+       fi
+       AC_CHECK_LIB(aquaero5, libaquaero5_poll,
+       [with_libaquaero5="yes"],
+       [with_libaquaero5="no (symbol 'libaquaero5_poll' not found)"])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libaquaero5" = "xyes"
+then
+       BUILD_WITH_LIBAQUAERO5_CFLAGS="$LIBAQUAERO5_CFLAGS"
+       BUILD_WITH_LIBAQUAERO5_LDFLAGS="$LIBAQUAERO5_LDFLAGS"
+       AC_SUBST(BUILD_WITH_LIBAQUAERO5_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBAQUAERO5_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBAQUAERO5, test "x$with_libaquaero5" = "xyes")
+# }}}
+
 # --with-libcredis {{{
 AC_ARG_WITH(libcredis, [AS_HELP_STRING([--with-libcredis@<:@=PREFIX@:>@], [Path to libcredis.])],
 [
@@ -2501,24 +2569,24 @@ fi
 AM_CONDITIONAL(BUILD_WITH_LIBMYSQL, test "x$with_libmysql" = "xyes")
 # }}}
 
-# --with-libnetlink {{{
-with_libnetlink_cflags=""
-with_libnetlink_libs="-lnetlink"
-AC_ARG_WITH(libnetlink, [AS_HELP_STRING([--with-libnetlink@<:@=PREFIX@:>@], [Path to libnetlink.])],
+# --with-libmnl {{{
+with_libmnl_cflags=""
+with_libmnl_libs=""
+AC_ARG_WITH(libmnl, [AS_HELP_STRING([--with-libmnl@<:@=PREFIX@:>@], [Path to libmnl.])],
 [
- echo "libnetlink: withval = $withval"
+ echo "libmnl: withval = $withval"
  if test "x$withval" = "xyes"
  then
-        with_libnetlink="yes"
+        with_libmnl="yes"
  else if test "x$withval" = "xno"
  then
-        with_libnetlink="no"
+        with_libmnl="no"
  else
         if test -d "$withval/include"
         then
-                with_libnetlink_cflags="-I$withval/include"
-                with_libnetlink_libs="-L$withval/lib -lnetlink"
-                with_libnetlink="yes"
+                with_libmnl_cflags="-I$withval/include"
+                with_libmnl_libs="-L$withval/lib -lmnl"
+                with_libmnl="yes"
         else
                 AC_MSG_ERROR("no such directory: $withval/include")
         fi
@@ -2527,21 +2595,25 @@ AC_ARG_WITH(libnetlink, [AS_HELP_STRING([--with-libnetlink@<:@=PREFIX@:>@], [Pat
 [
  if test "x$ac_system" = "xLinux"
  then
-        with_libnetlink="yes"
+        with_libmnl="yes"
  else
-        with_libnetlink="no (Linux only library)"
+        with_libmnl="no (Linux only library)"
  fi
 ])
-if test "x$with_libnetlink" = "xyes"
+if test "x$PKG_CONFIG" = "x"
 then
-       SAVE_CFLAGS="$CFLAGS"
-       CFLAGS="$CFLAGS $with_libnetlink_cflags"
-
-       with_libnetlink="no (libnetlink.h not found)"
+       with_libmnl="no (Don't have pkg-config)"
+fi
+if test "x$with_libmnl" = "xyes"
+then
+       if $PKG_CONFIG --exists libmnl 2>/dev/null; then
+         with_libmnl_cflags="$with_libmnl_ldflags `$PKG_CONFIG --cflags libmnl`"
+         with_libmnl_libs="$with_libmnl_libs `$PKG_CONFIG --libs libmnl`"
+       fi
 
-       AC_CHECK_HEADERS(libnetlink.h iproute/libnetlink.h linux/libnetlink.h,
+       AC_CHECK_HEADERS(libmnl.h libmnl/libmnl.h,
        [
-        with_libnetlink="yes"
+        with_libmnl="yes"
         break
        ], [],
 [#include <stdio.h>
@@ -2557,126 +2629,62 @@ then
 #include <sys/socket.h>])
 
         AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
-[[[
+[[
 #include <stdio.h>
 #include <sys/types.h>
 #include <asm/types.h>
 #include <sys/socket.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
-]]],
-[[[
+]],
+[[
 int retval = TCA_STATS2;
 return (retval);
-]]]
+]]
        )],
        [AC_DEFINE([HAVE_TCA_STATS2], [1], [True if the enum-member TCA_STATS2 exists])])
 
        AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
-[[[
+[[
 #include <stdio.h>
 #include <sys/types.h>
 #include <asm/types.h>
 #include <sys/socket.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
-]]],
-[[[
+]],
+[[
 int retval = TCA_STATS;
 return (retval);
-]]]
+]]
        )],
        [AC_DEFINE([HAVE_TCA_STATS], 1, [True if the enum-member TCA_STATS exists])])
-
-       CFLAGS="$SAVE_CFLAGS"
 fi
-if test "x$with_libnetlink" = "xyes"
+if test "x$with_libmnl" = "xyes"
 then
-       AC_CHECK_LIB(netlink, rtnl_open,
-                    [with_libnetlink="yes"],
-                    [with_libnetlink="no (symbol 'rtnl_open' not found)"],
-                    [$with_libnetlink_libs])
+       AC_CHECK_MEMBERS([struct rtnl_link_stats64.tx_window_errors],
+       [AC_DEFINE(HAVE_RTNL_LINK_STATS64, 1, [Define if struct rtnl_link_stats64 exists and is usable.])],
+       [],
+       [
+       #include <linux/if_link.h>
+       ])
 fi
-if test "x$with_libnetlink" = "xyes"
+if test "x$with_libmnl" = "xyes"
 then
-       SAVE_CFLAGS="$CFLAGS"
-       CFLAGS="$CFLAGS $with_libnetlink_cflags"
-
-       AC_CACHE_CHECK(
-               [if function 'rtnl_dump_filter' expects five arguments],
-               [c_cv_rtnl_dump_filter_five_args],
-               AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
-[[[
-#include <stdio.h>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>
-#if HAVE_LIBNETLINK_H
-# include <libnetlink.h>
-#elif HAVE_IPROUTE_LIBNETLINK_H
-# include <iproute/libnetlink.h>
-#elif HAVE_LINUX_LIBNETLINK_H
-# include <linux/libnetlink.h>
-#endif
-]]],
-[[[
-if (rtnl_dump_filter(NULL, NULL, NULL, NULL, NULL))
-       return 1;
-return 0;
-]]]
-       )],
-       [c_cv_rtnl_dump_filter_five_args="yes"],
-       [c_cv_rtnl_dump_filter_five_args="no"]
-       )
-)
-
-       AC_CACHE_CHECK(
-               [if function 'rtnl_dump_filter' expects three arguments],
-               [c_cv_rtnl_dump_filter_three_args],
-               AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
-[[[
-#include <stdio.h>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>
-#if HAVE_LIBNETLINK_H
-# include <libnetlink.h>
-#elif HAVE_IPROUTE_LIBNETLINK_H
-# include <iproute/libnetlink.h>
-#elif HAVE_LINUX_LIBNETLINK_H
-# include <linux/libnetlink.h>
-#endif
-]]],
-[[[
-if (rtnl_dump_filter(NULL, NULL, NULL))
-       return 1;
-return 0;
-]]]
-       )],
-       [c_cv_rtnl_dump_filter_three_args="yes"],
-       [c_cv_rtnl_dump_filter_three_args="no"]
-       )
-)
-
-       CFLAGS="$SAVE_CFLAGS"
-
-       if test "x$c_cv_rtnl_dump_filter_five_args" = "xyes"
-       then
-               AC_DEFINE(RTNL_DUMP_FILTER_FIVE_ARGS, 1,
-                               [Define to 1 if function 'rtnl_dump_filter' expects five arguments.])
-       fi
-       if test "x$c_cv_rtnl_dump_filter_three_args" = "xyes"
-       then
-               AC_DEFINE(RTNL_DUMP_FILTER_THREE_ARGS, 1,
-                               [Define to 1 if function 'rtnl_dump_filter' expects three arguments.])
-       fi
-
-       BUILD_WITH_LIBNETLINK_CFLAGS="$with_libnetlink_cflags"
-       BUILD_WITH_LIBNETLINK_LIBS="$with_libnetlink_libs"
-       AC_SUBST(BUILD_WITH_LIBNETLINK_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBNETLINK_LIBS)
+       AC_CHECK_LIB(mnl, mnl_nlmsg_get_payload,
+                    [with_libmnl="yes"],
+                    [with_libmnl="no (symbol 'mnl_nlmsg_get_payload' not found)"],
+                    [$with_libmnl_libs])
+fi
+if test "x$with_libmnl" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBMNL, 1, [Define if libmnl is present and usable.])
+       BUILD_WITH_LIBMNL_CFLAGS="$with_libmnl_cflags"
+       BUILD_WITH_LIBMNL_LIBS="$with_libmnl_libs"
+       AC_SUBST(BUILD_WITH_LIBMNL_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBMNL_LIBS)
 fi
-AM_CONDITIONAL(BUILD_WITH_LIBNETLINK, test "x$with_libnetlink" = "xyes")
+AM_CONDITIONAL(BUILD_WITH_LIBMNL, test "x$with_libmnl" = "xyes")
 # }}}
 
 # --with-libnetapp {{{
@@ -3767,6 +3775,74 @@ fi
 AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_libsensors" = "xyes")
 # }}}
 
+# --with-libsigrok {{{
+with_libsigrok_cflags=""
+with_libsigrok_ldflags=""
+AC_ARG_WITH(libsigrok, [AS_HELP_STRING([--with-libsigrok@<:@=PREFIX@:>@], [Path to libsigrok.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libsigrok="no"
+       else
+               with_libsigrok="yes"
+               if test "x$withval" != "xyes"
+               then
+                       with_libsigrok_cflags="-I$withval/include"
+                       with_libsigrok_ldflags="-L$withval/lib"
+               fi
+       fi
+],[])
+
+# libsigrok has a glib dependency
+if test "x$with_libsigrok" = "xyes"
+then
+       if test -z "m4_ifdef([AM_PATH_GLIB_2_0], [yes], [])"
+       then
+               with_libsigrok="no (glib not available)"
+       else
+               AM_PATH_GLIB_2_0([2.28.0],
+                       [with_libsigrok_cflags="$with_libsigrok_cflags $GLIB_CFLAGS"; with_libsigrok_ldflags="$with_libsigrok_ldflags $GLIB_LIBS"])
+       fi
+fi
+
+# libsigrok headers
+if test "x$with_libsigrok" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libsigrok_cflags"
+
+       AC_CHECK_HEADERS(libsigrok/libsigrok.h, [], [with_libsigrok="no (libsigrok/libsigrok.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+# libsigrok library
+if test "x$with_libsigrok" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libsigrok_cflags"
+       LDFLAGS="$LDFLAGS $with_libsigrok_ldflags"
+
+       AC_CHECK_LIB(sigrok, sr_init,
+       [
+               AC_DEFINE(HAVE_LIBSIGROK, 1, [Define to 1 if you have the sigrok library (-lsigrok).])
+       ],
+       [with_libsigrok="no (libsigrok not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libsigrok" = "xyes"
+then
+       BUILD_WITH_LIBSIGROK_CFLAGS="$with_libsigrok_cflags"
+       BUILD_WITH_LIBSIGROK_LDFLAGS="$with_libsigrok_ldflags"
+       AC_SUBST(BUILD_WITH_LIBSIGROK_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBSIGROK_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBSIGROK, test "x$with_libsigrok" = "xyes")
+# }}}
+
 # --with-libstatgrab {{{
 with_libstatgrab_cflags=""
 with_libstatgrab_ldflags=""
@@ -5018,6 +5094,7 @@ AC_PLUGIN([amqp],        [$with_librabbitmq],  [AMQP output plugin])
 AC_PLUGIN([apache],      [$with_libcurl],      [Apache httpd statistics])
 AC_PLUGIN([apcups],      [yes],                [Statistics of UPSes by APC])
 AC_PLUGIN([apple_sensors], [$with_libiokit],   [Apple's hardware sensors])
+AC_PLUGIN([aquaero],     [$with_libaquaero5],  [Aquaero's hardware sensors])
 AC_PLUGIN([ascent],      [$plugin_ascent],     [AscentEmu player statistics])
 AC_PLUGIN([battery],     [$plugin_battery],    [Battery statistics])
 AC_PLUGIN([bind],        [$plugin_bind],       [ISC Bind nameserver statistics])
@@ -5069,7 +5146,7 @@ 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])
-AC_PLUGIN([netlink],     [$with_libnetlink],   [Enhanced Linux network statistics])
+AC_PLUGIN([netlink],     [$with_libmnl],       [Enhanced Linux network statistics])
 AC_PLUGIN([network],     [yes],                [Network communication plugin])
 AC_PLUGIN([nfs],         [$plugin_nfs],        [NFS statistics])
 AC_PLUGIN([nginx],       [$with_libcurl],      [nginx statistics])
@@ -5098,7 +5175,9 @@ AC_PLUGIN([rrdcached],   [$librrd_rrdc_update], [RRDTool output plugin])
 AC_PLUGIN([rrdtool],     [$with_librrd],       [RRDTool output plugin])
 AC_PLUGIN([sensors],     [$with_libsensors],   [lm_sensors statistics])
 AC_PLUGIN([serial],      [$plugin_serial],     [serial port traffic])
+AC_PLUGIN([sigrok],      [$with_libsigrok],    [sigrok acquisition sources])
 AC_PLUGIN([snmp],        [$with_libnetsnmp],   [SNMP querying plugin])
+AC_PLUGIN([statsd],      [yes],                [StatsD plugin])
 AC_PLUGIN([swap],        [$plugin_swap],       [Swap usage statistics])
 AC_PLUGIN([syslog],      [$have_syslog],       [Syslog logging plugin])
 AC_PLUGIN([table],       [yes],                [Parsing of tabular data])
@@ -5301,6 +5380,7 @@ cat <<EOF;
 Configuration:
   Libraries:
     intel mic . . . . . . $with_mic
+    libaquaero5 . . . . . $with_libaquaero5
     libcurl . . . . . . . $with_libcurl
     libdbi  . . . . . . . $with_libdbi
     libcredis . . . . . . $with_libcredis
@@ -5313,10 +5393,10 @@ Configuration:
     libkstat  . . . . . . $with_kstat
     libkvm  . . . . . . . $with_libkvm
     libmemcached  . . . . $with_libmemcached
+    libmnl  . . . . . . . $with_libmnl
     libmodbus . . . . . . $with_libmodbus
     libmysql  . . . . . . $with_libmysql
     libnetapp . . . . . . $with_libnetapp
-    libnetlink  . . . . . $with_libnetlink
     libnetsnmp  . . . . . $with_libnetsnmp
     libnotify . . . . . . $with_libnotify
     liboconfig  . . . . . $with_liboconfig
@@ -5331,6 +5411,7 @@ Configuration:
     librouteros . . . . . $with_librouteros
     librrd  . . . . . . . $with_librrd
     libsensors  . . . . . $with_libsensors
+    libsigrok   . . . . . $with_libsigrok
     libstatgrab . . . . . $with_libstatgrab
     libtokyotyrant  . . . $with_libtokyotyrant
     libupsclient  . . . . $with_libupsclient
@@ -5356,6 +5437,7 @@ Configuration:
     amqp    . . . . . . . $enable_amqp
     apache  . . . . . . . $enable_apache
     apcups  . . . . . . . $enable_apcups
+    aquaero . . . . . . . $enable_aquaero
     apple_sensors . . . . $enable_apple_sensors
     ascent  . . . . . . . $enable_ascent
     battery . . . . . . . $enable_battery
@@ -5390,7 +5472,7 @@ Configuration:
     libvirt . . . . . . . $enable_libvirt
     load  . . . . . . . . $enable_load
     logfile . . . . . . . $enable_logfile
-    lpar... . . . . . . . $enable_lpar
+    lpar  . . . . . . . . $enable_lpar
     lvm . . . . . . . . . $enable_lvm
     madwifi . . . . . . . $enable_madwifi
     match_empty_counter . $enable_match_empty_counter
@@ -5435,7 +5517,9 @@ Configuration:
     rrdtool . . . . . . . $enable_rrdtool
     sensors . . . . . . . $enable_sensors
     serial  . . . . . . . $enable_serial
+    sigrok  . . . . . . . $enable_sigrok
     snmp  . . . . . . . . $enable_snmp
+    statsd  . . . . . . . $enable_statsd
     swap  . . . . . . . . $enable_swap
     syslog  . . . . . . . $enable_syslog
     table . . . . . . . . $enable_table
index 9af0fb0..809f19d 100644 (file)
@@ -17,10 +17,15 @@ Collectd network protocol implementation.
 """
 
 import socket,struct,sys
-try:
-  from io import StringIO
-except ImportError:
-  from cStringIO import StringIO
+import platform
+if platform.python_version() < '2.8.0':
+    # Python 2.7 and below io.StringIO does not like unicode
+    from StringIO import StringIO
+else:
+    try:
+      from io import StringIO
+    except ImportError:
+      from cStringIO import StringIO
 
 from datetime import datetime
 from copy import deepcopy
@@ -76,7 +81,7 @@ def decode_network_values(ptype, plen, buf):
     assert double.size == number.size
 
     result = []
-    for dstype in buf[header.size+short.size:off]:
+    for dstype in [ord(x) for x in buf[header.size+short.size:off]]:
         if dstype == DS_TYPE_COUNTER:
             result.append((dstype, number.unpack_from(buf, off)[0]))
             off += valskip
index 5d1024f..5fe4313 100644 (file)
@@ -3,7 +3,7 @@ GraphWidth 400
 #UnixSockAddr "/var/run/collectd-unixsock"
 <Type apache_bytes>
   DataSources value
-  DSName "count Bytes/s"
+  DSName "value Bytes/s"
   RRDTitle "Apache Traffic"
   RRDVerticalLabel "Bytes/s"
   RRDFormat "%5.1lf%s"
@@ -11,7 +11,7 @@ GraphWidth 400
 </Type>
 <Type apache_requests>
   DataSources value
-  DSName "count Requests/s"
+  DSName "value Requests/s"
   RRDTitle "Apache Traffic"
   RRDVerticalLabel "Requests/s"
   RRDFormat "%5.2lf"
@@ -246,14 +246,14 @@ GraphWidth 400
 </Type>
 <Type conntrack>
   DataSources value
-  DSName conntrack Conntrack count
+  DSName value Conntrack count
   RRDTitle "nf_conntrack connections on {hostname}"
   RRDVerticalLabel "Count"
   RRDFormat "%4.0lf"
 </Type>
 <Type entropy>
   DataSources value
-  DSName entropy Entropy bits
+  DSName value Entropy bits
   RRDTitle "Available entropy on {hostname}"
   RRDVerticalLabel "Bits"
   RRDFormat "%4.0lf"
@@ -268,7 +268,7 @@ GraphWidth 400
 </Type>
 <Type frequency>
   DataSources value
-  DSName frequency Frequency
+  DSName value Frequency
   RRDTitle "Frequency ({type_instance})"
   RRDVerticalLabel "Hertz"
   RRDFormat "%4.1lfHz"
@@ -543,7 +543,7 @@ GraphWidth 400
 </Type>
 <Type percent>
   DataSources value
-  DSName percent Percent
+  DSName value Percent
   RRDTitle "Percent ({type_instance})"
   RRDVerticalLabel "Percent"
   RRDFormat "%4.1lf%%"
@@ -551,7 +551,7 @@ GraphWidth 400
 </Type>
 <Type ping>
   DataSources value
-  DSName "ping Latency"
+  DSName "value Latency"
   RRDTitle "Network latency ({type_instance})"
   RRDVerticalLabel "Milliseconds"
   RRDFormat "%5.2lfms"
@@ -701,7 +701,7 @@ GraphWidth 400
 </Type>
 <Type users>
   DataSources value
-  DSName users Users
+  DSName value Users
   RRDTitle "Users ({type_instance}) on {hostname}"
   RRDVerticalLabel "Users"
   RRDFormat "%.1lf"
index fb44bb4..00c5519 100644 (file)
@@ -104,7 +104,7 @@ CREATE OR REPLACE VIEW collectd
                     WHEN type_inst IS NOT NULL THEN '-'
                     ELSE ''
                 END
-                || coalesce(plugin_inst, '') AS identifier,
+                || coalesce(type_inst, '') AS identifier,
             tstamp, name, value
         FROM identifiers
             JOIN values
index 86f317f..4f70fd6 100644 (file)
@@ -1,6 +1,51 @@
+#
+# q: What is this ?
+# a: A specfile for building RPM packages of current collectd releases, for
+#    RHEL/CentOS versions 5 and 6. By default all the plugins which are
+#    buildable based on the libraries available in the distribution + the
+#    EPEL repository, will be built. Plugins depending on external libs will
+#    be packaged in separate RPMs.
+#
+# q: And how can I do that ?
+# a: By following these instructions, using mock:
+#
+# - install and configure mock (https://fedoraproject.org/wiki/Projects/Mock)
+#
+# - enable the EPEL repository (http://dl.fedoraproject.org/pub/epel/) in the
+#   configuration files for your target systems (/etc/mock/*.cfg).
+#
+# - copy this file in your ~/rpmbuild/SPECS/ directory
+#
+# - fetch the desired collectd release file from https://collectd.org/files/
+#   and save it in your ~/rpmbuild/SOURCES/ directory
+#
+# - build the SRPM first:
+#   mock -r centos-6-x86_64 --buildsrpm --spec ~/rpmbuild/SPECS/collectd.spec \
+#     --sources ~/rpmbuild/SOURCES/
+#
+# - then build the RPMs:
+#   mock -r centos-6-x86_64 --no-clean --rebuild \
+#     /var/lib/mock/centos-6-x86_64/result/collectd-X.Y.Z-NN.src.rpm
+#
+# - you can also optionally enable/disable plugins which are disabled/enabled
+#   by default:
+#   mock -r centos-6-x86_64 --no-clean --without=java --with=oracle --rebuild \
+#     /var/lib/mock/centos-6-x86_64/result/collectd-X.Y.Z-NN.src.rpm
+#
+
 %global _hardened_build 1
 
-# enabled plugins
+# plugins only buildable on RHEL6
+# (NB: %{elN} macro is not available on RHEL < 6)
+%{?el6:%global _has_libyajl 1}
+%{?el6:%global _has_recent_libpcap 1}
+%{?el6:%global _has_recent_sockios_h 1}
+%{?el6:%global _has_recent_libganglia 1}
+%{?el6:%global _has_working_libiptc 1}
+%{?el6:%global _has_ip_vs_h 1}
+%{?el6:%global _has_perl_extutils_embed 1}
+
+# plugins enabled by default
 %define with_aggregation 0%{!?_without_aggregation:1}
 %define with_amqp 0%{!?_without_amqp:1}
 %define with_apache 0%{!?_without_apache:1}
 %define with_cpufreq 0%{!?_without_cpufreq:1}
 %define with_csv 0%{!?_without_csv:1}
 %define with_curl 0%{!?_without_curl:1}
-%define with_curl_json 0%{!?_without_curl_json:1}
+%define with_curl_json 0%{!?_without_curl_json:0%{?_has_libyajl}}
 %define with_curl_xml 0%{!?_without_curl_xml:1}
 %define with_dbi 0%{!?_without_dbi:1}
 %define with_df 0%{!?_without_df:1}
 %define with_disk 0%{!?_without_disk:1}
-%define with_dns 0%{!?_without_dns:1}
+%define with_dns 0%{!?_without_dns:0%{?_has_recent_libpcap}}
 %define with_email 0%{!?_without_email:1}
 %define with_entropy 0%{!?_without_entropy:1}
-%define with_ethstat 0%{!?_without_ethstat:1}
+%define with_ethstat 0%{!?_without_ethstat:0%{?_has_recent_sockios_h}}
 %define with_exec 0%{!?_without_exec:1}
 %define with_filecount 0%{!?_without_filecount:1}
 %define with_fscache 0%{!?_without_fscache:1}
-%define with_gmond 0%{!?_without_gmond:1}
+%define with_gmond 0%{!?_without_gmond:0%{?_has_recent_libganglia}}
 %define with_hddtemp 0%{!?_without_hddtemp:1}
 %define with_interface 0%{!?_without_interface:1}
 %define with_ipmi 0%{!?_without_ipmi:1}
-%define with_iptables 0%{!?_without_iptables:1}
-%define with_ipvs 0%{!?_without_ipvs:1}
+%define with_iptables 0%{!?_without_iptables:0%{?_has_working_libiptc}}
+%define with_ipvs 0%{!?_without_ipvs:0%{?_has_ip_vs_h}}
 %define with_irq 0%{!?_without_irq:1}
 %define with_java 0%{!?_without_java:1}
 %define with_libvirt 0%{!?_without_libvirt:1}
 %define with_nut 0%{!?_without_nut:1}
 %define with_olsrd 0%{!?_without_olsrd:1}
 %define with_openvpn 0%{!?_without_openvpn:1}
-%define with_perl 0%{!?_without_perl:1}
+%define with_perl 0%{!?_without_perl:0%{?_has_perl_extutils_embed}}
 %define with_pinba 0%{!?_without_pinba:1}
 %define with_ping 0%{!?_without_ping:1}
 %define with_postgresql 0%{!?_without_postgresql:1}
 %define with_write_http 0%{!?_without_write_http:1}
 %define with_write_riemann 0%{!?_without_write_riemann:1}
 
-# disabled plugins
+# Plugins not built by default because of dependencies on libraries not
+# available in RHEL or EPEL:
+
+# plugin apple_sensors disabled, requires a Mac
 %define with_apple_sensors 0%{!?_without_apple_sensors:0}
+# plugin lpar disabled, requires AIX
 %define with_lpar 0%{!?_without_lpar:0}
+# plugin modbus disabled, requires libmodbus
 %define with_modbus 0%{!?_without_modbus:0}
+# plugin netapp disabled, requires libnetapp
 %define with_netapp 0%{!?_without_netapp:0}
+# plugin netlink disabled, requires libnetlink.h
 %define with_netlink 0%{!?_without_netlink:0}
+# plugin onewire disabled, requires libowfs
 %define with_onewire 0%{!?_without_onewire:0}
+# plugin oracle disabled, requires Oracle
 %define with_oracle 0%{!?_without_oracle:0}
+# plugin oracle disabled, requires BSD
 %define with_pf 0%{!?_without_pf:0}
+# plugin redis disabled, requires credis
 %define with_redis 0%{!?_without_redis:0}
+# plugin routeros disabled, requires librouteros
 %define with_routeros 0%{!?_without_routeros:0}
+# plugin rrdcached disabled, requires rrdtool >= 1.4
 %define with_rrdcached 0%{!?_without_rrdcached:0}
+# plugin tape disabled, requires libkstat
 %define with_tape 0%{!?_without_tape:0}
+# plugin tokyotyrant disabled, requires tcrdb.h
 %define with_tokyotyrant 0%{!?_without_tokyotyrant:0}
+# plugin write_mongodb disabled, requires libmongoc
 %define with_write_mongodb 0%{!?_without_write_mongodb:0}
+# plugin write_redis disabled, requires credis
 %define with_write_redis 0%{!?_without_write_redis:0}
+# plugin xmms disabled, requires xmms
 %define with_xmms 0%{!?_without_xmms:0}
+# plugin zfs_arc disabled, requires FreeBSD/Solaris
 %define with_zfs_arc 0%{!?_without_zfs_arc:0}
 
 Summary:       Statistics collection daemon for filling RRD files
 Name:          collectd
-Version:       5.3.0
+Version:       5.3.1
 Release:       1%{?dist}
 URL:           http://collectd.org
 Source:                http://collectd.org/files/%{name}-%{version}.tar.bz2
 License:       GPLv2
 Group:         System Environment/Daemons
 BuildRoot:     %{_tmppath}/%{name}-%{version}-root
-BuildRequires: libgcrypt-devel
+BuildRequires: libgcrypt-devel, kernel-headers
 Vendor:                collectd development team <collectd@verplant.org>
 
 Requires(post):                chkconfig
@@ -192,7 +256,7 @@ the configuration.
 Summary:       Curl_json plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
-Buildrequires: curl-devel, yajl-devel
+BuildRequires: curl-devel, yajl-devel
 %description curl_json
 The cURL-JSON plugin queries JavaScript Object Notation (JSON) data using the
 cURL library and parses it according to the user's configuration.
@@ -214,7 +278,7 @@ Markup Language (XML).
 Summary:       DBI plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
-Buildrequires: libdbi-devel
+BuildRequires: libdbi-devel
 %description dbi
 The DBI plugin uses libdbi, a database abstraction library, to execute SQL
 statements on a database and read back the result.
@@ -224,8 +288,8 @@ statements on a database and read back the result.
 %package dns
 Summary:       DNS plugin for collectd
 Group:         System Environment/Daemons
-Requires:      %{name}%{?_isa} = %{version}-%{release}
-Buildrequires: libpcap-devel
+Requires:      %{name}%{?_isa} = %{version}-%{release}, libpcap >= 1.0
+BuildRequires: libpcap-devel >= 1.0
 %description dns
 The DNS plugin has a similar functionality to dnstop: It uses libpcap to get a
 copy of all traffic from/to port UDP/53 (that's the DNS port), interprets the
@@ -345,7 +409,7 @@ This plugin gets data provided by nginx.
 Summary:       Notify_desktop plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
-BuildRequires: libnotify-devel
+BuildRequires: libnotify-devel, gtk2-devel
 %description notify_desktop
 The Notify Desktop plugin uses libnotify to display notifications to the user
 via the desktop notification specification, i. e. on an X display.
@@ -422,7 +486,11 @@ database.
 Summary:       Python plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
-BuildRequires: python-devel
+%if 0%{?rhel} >= 6
+BuildRequires: python-devel
+%else
+BuildRequires: python26-devel
+%endif
 %description python
 The Python plugin embeds a Python interpreter into collectd and exposes the
 application programming interface (API) to Python-scripts.
@@ -551,11 +619,13 @@ package.
 
 %package -n libcollectdclient
 Summary:       Collectd client library
+Group:         System Environment/Daemons
 %description -n libcollectdclient
 Collectd client library
 
 %package -n libcollectdclient-devel
 Summary:       Development files for libcollectdclient
+Group:         System Environment/Daemons
 Requires:      pkgconfig
 Requires:      libcollectdclient%{?_isa} = %{version}-%{release}
 %description -n libcollectdclient-devel
@@ -981,8 +1051,12 @@ Development files for libcollectdclient
 %endif
 
 %if %{with_python}
+%if 0%{?rhel} >= 6
 %define _with_python --enable-python
 %else
+%define _with_python --enable-python --with-python=%{_bindir}/python2.6
+%endif
+%else
 %define _with_python --disable-python
 %endif
 
@@ -1194,7 +1268,6 @@ Development files for libcollectdclient
        --disable-static \
        --without-included-ltdl \
        --enable-all-plugins=yes \
-       --enable-aggregation \
        --enable-match_empty_counter \
        --enable-match_hashed \
        --enable-match_regex \
@@ -1353,6 +1426,8 @@ rm -f %{buildroot}%{_mandir}/man5/collectd-java.5*
 %if ! %{with_perl}
 rm -f %{buildroot}%{_mandir}/man5/collectd-perl.5*
 rm -f %{buildroot}%{_mandir}/man3/Collectd::Unixsock.3pm*
+rm -fr perl-examples/
+rm -fr %{buildroot}/usr/lib/perl5/
 %endif
 
 %if ! %{with_python}
@@ -1583,21 +1658,6 @@ fi
 %{_libdir}/%{name}/write_graphite.so
 %endif
 
-# All plugins not built by default because of dependencies on libraries not
-# available in RHEL or EPEL:
-# plugin modbus disabled, requires libmodbus
-# plugin netlink disabled, requires libnetlink.h
-# plugin numa disabled, requires libnetapp
-# plugin onewire disabled, requires libowfs
-# plugin oracle disabled, requires Oracle
-# plugin redis disabled, requires credis
-# plugin routeros disabled, requires librouteros
-# plugin rrdcached disabled, requires rrdtool >= 1.4
-# plugin tokyotyrant disabled, requires tcrdb.h
-# plugin write_mongodb disabled, requires libmongoc
-# plugin write_redis disabled, requires credis
-# plugin xmms disabled, requires xmms
-
 
 %files -n libcollectdclient-devel
 %{_includedir}/collectd/client.h
@@ -1682,8 +1742,8 @@ fi
 
 %if %{with_java}
 %files java
-%{_datarootdir}/collectd/java/collectd-api.jar
-%{_datarootdir}/collectd/java/generic-jmx.jar
+%{_prefix}/share/collectd/java/collectd-api.jar
+%{_prefix}/share/collectd/java/generic-jmx.jar
 %{_libdir}/%{name}/java.so
 %{_mandir}/man5/collectd-java.5*
 %endif
@@ -1745,7 +1805,7 @@ fi
 
 %if %{with_postgresql}
 %files postgresql
-%{_datarootdir}/collectd/postgresql_default.conf
+%{_prefix}/share/collectd/postgresql_default.conf
 %{_libdir}/%{name}/postgresql.so
 %endif
 
@@ -1813,6 +1873,16 @@ fi
 %doc contrib/
 
 %changelog
+* Tue Aug 06 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.3.1-1
+- New upstream version
+- Added RHEL5 support:
+  * conditionally disable plugins not building on this platform
+  * add/specify some build dependencies and options
+  * replace some RPM macros not available on this platform
+- Removed duplicate --enable-aggregation
+- Added some comments & usage examples
+- Replaced a couple of "Buildrequires" by "BuildRequires"
+
 * Wed Apr 10 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.3.0-1
 - New upstream version
 - Enabled write_riemann plugin
index c3e596d..59454dc 100644 (file)
@@ -196,6 +196,16 @@ collectd_LDADD += "-dlopen" apple_sensors.la
 collectd_DEPENDENCIES += apple_sensors.la
 endif
 
+if BUILD_PLUGIN_AQUAERO
+pkglib_LTLIBRARIES += aquaero.la
+aquaero_la_SOURCES = aquaero.c
+aquaero_la_LDFLAGS = -module -avoid-version
+aquaero_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBAQUAERO5_CFLAGS)
+aquaero_la_LIBADD = $(BUILD_WITH_LIBAQUAERO5_LDFLAGS) -laquaero5
+collectd_LDADD += "-dlopen" aquaero.la
+collectd_DEPENDENCIES += aquaero.la
+endif
+
 if BUILD_PLUGIN_ASCENT
 pkglib_LTLIBRARIES += ascent.la
 ascent_la_SOURCES = ascent.c
@@ -744,8 +754,8 @@ if BUILD_PLUGIN_NETLINK
 pkglib_LTLIBRARIES += netlink.la
 netlink_la_SOURCES = netlink.c
 netlink_la_LDFLAGS = -module -avoid-version
-netlink_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBNETLINK_CFLAGS)
-netlink_la_LIBADD = $(BUILD_WITH_LIBNETLINK_LIBS)
+netlink_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
+netlink_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS)
 collectd_LDADD += "-dlopen" netlink.la
 collectd_DEPENDENCIES += netlink.la
 endif
@@ -1052,6 +1062,16 @@ collectd_LDADD += "-dlopen" serial.la
 collectd_DEPENDENCIES += serial.la
 endif
 
+if BUILD_PLUGIN_SIGROK
+pkglib_LTLIBRARIES += sigrok.la
+sigrok_la_SOURCES = sigrok.c
+sigrok_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBSIGROK_CFLAGS)
+sigrok_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBSIGROK_LDFLAGS)
+sigrok_la_LIBADD = -lsigrok
+collectd_LDADD += "-dlopen" sigrok.la
+collectd_DEPENDENCIES += sigrok.la
+endif
+
 if BUILD_PLUGIN_SNMP
 pkglib_LTLIBRARIES += snmp.la
 snmp_la_SOURCES = snmp.c
@@ -1069,6 +1089,16 @@ collectd_LDADD += "-dlopen" snmp.la
 collectd_DEPENDENCIES += snmp.la
 endif
 
+if BUILD_PLUGIN_STATSD
+pkglib_LTLIBRARIES += statsd.la
+statsd_la_SOURCES = statsd.c \
+                    utils_latency.h utils_latency.c
+statsd_la_LDFLAGS = -module -avoid-version
+statsd_la_LIBADD = -lpthread
+collectd_LDADD += "-dlopen" statsd.la
+collectd_DEPENDENCIES += statsd.la
+endif
+
 if BUILD_PLUGIN_SWAP
 pkglib_LTLIBRARIES += swap.la
 swap_la_SOURCES = swap.c
index 8458ce1..c5f099f 100644 (file)
@@ -402,7 +402,7 @@ static int init_host (apache_t *st) /* {{{ */
                curl_easy_setopt (st->curl, CURLOPT_WRITEHEADER, st);
        }
 
-       curl_easy_setopt (st->curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+       curl_easy_setopt (st->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
        curl_easy_setopt (st->curl, CURLOPT_ERRORBUFFER, st->apache_curl_error);
 
        if (st->user != NULL)
diff --git a/src/aquaero.c b/src/aquaero.c
new file mode 100644 (file)
index 0000000..84c797b
--- /dev/null
@@ -0,0 +1,193 @@
+/**
+ * collectd - src/aquaero.c
+ * Copyright (C) 2013  Alex Deymo
+ *
+ * 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:
+ *   Alex Deymo
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <libaquaero5.h>
+
+/*
+ * Private variables
+ */
+/* Default values for contacting daemon */
+static char *conf_device = NULL;
+
+static int aquaero_config (oconfig_item_t *ci)
+{
+       int i;
+
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if (strcasecmp ("Device", child->key))
+                       cf_util_get_string (child, &conf_device);
+               else
+               {
+                       ERROR ("aquaero plugin: Unknown config option \"%s\".",
+                                       child->key);
+               }
+       }
+
+       return (0);
+}
+
+static int aquaero_shutdown (void)
+{
+       libaquaero5_exit();
+       return (0);
+} /* int aquaero_shutdown */
+
+static void aquaero_submit (const char *type, const char *type_instance,
+               double value)
+{
+       const char *instance = conf_device?conf_device:"default";
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       /* Don't report undefined values. */
+       if (value == AQ5_FLOAT_UNDEF)
+               return;
+
+       values[0].gauge = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "aquaero", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, instance, sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* int aquaero_submit */
+
+/* aquaero_submit_array submits every value of a given array of values */
+static void aquaero_submit_array (const char *type,
+               const char *type_instance_prefix, double *value_array, int len)
+{
+       char type_instance[DATA_MAX_NAME_LEN];
+       int i;
+
+       for (i = 0; i < len; i++)
+       {
+               if (value_array[i] == AQ5_FLOAT_UNDEF)
+                       continue;
+
+               snprintf (type_instance, sizeof (type_instance), "%s%d",
+                               type_instance_prefix, i + 1);
+               aquaero_submit (type, type_instance, value_array[i]);
+       }
+}
+
+static int aquaero_read (void)
+{
+       aq5_data_t aq_data;
+       aq5_settings_t aq_sett;
+       char *err_msg = NULL;
+       char type_instance[DATA_MAX_NAME_LEN];
+       int i;
+
+       if (libaquaero5_poll(conf_device, &aq_data, &err_msg) < 0)
+       {
+               char errbuf[1024];
+               ERROR ("aquaero plugin: Failed to poll device \"%s\": %s (%s)",
+                               conf_device ? conf_device : "default", err_msg,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       if (libaquaero5_getsettings(conf_device, &aq_sett, &err_msg) < 0)
+       {
+               char errbuf[1024];
+               ERROR ("aquaero plugin: Failed to get settings "
+                               "for device \"%s\": %s (%s)",
+                               conf_device ? conf_device : "default", err_msg,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       /* CPU Temperature sensor */
+       aquaero_submit("temperature", "cpu", aq_data.cpu_temp[0]);
+
+       /* Temperature sensors */
+       aquaero_submit_array("temperature", "sensor", aq_data.temp,
+                       AQ5_NUM_TEMP);
+
+       /* Virtual temperature sensors */
+       aquaero_submit_array("temperature", "virtual", aq_data.vtemp,
+                       AQ5_NUM_VIRT_SENSORS);
+
+       /* Software temperature sensors */
+       aquaero_submit_array("temperature", "software", aq_data.stemp,
+                       AQ5_NUM_SOFT_SENSORS);
+
+       /* Other temperature sensors */
+       aquaero_submit_array("temperature", "other", aq_data.otemp,
+                       AQ5_NUM_OTHER_SENSORS);
+
+       /* Fans */
+       for (i = 0; i < AQ5_NUM_FAN; i++)
+       {
+               if ((aq_sett.fan_data_source[i] == NONE)
+                               || (aq_data.fan_vrm_temp[i] != AQ5_FLOAT_UNDEF))
+                       continue;
+
+               snprintf (type_instance, sizeof (type_instance),
+                               "fan%d", i + 1);
+
+               aquaero_submit ("fanspeed", type_instance,
+                               aq_data.fan_rpm[i]);
+               aquaero_submit ("percentage", type_instance,
+                               aq_data.fan_duty[i]);
+               aquaero_submit ("voltage", type_instance,
+                               aq_data.fan_voltage[i]);
+               aquaero_submit ("current", type_instance,
+                               aq_data.fan_current[i]);
+
+               /* Report the voltage reglator module (VRM) temperature with a
+                * different type instance. */
+               snprintf (type_instance, sizeof (type_instance),
+                               "fan%d-vrm", i + 1);
+               aquaero_submit ("temperature", type_instance,
+                               aq_data.fan_vrm_temp[i]);
+       }
+
+       /* Flow sensors */
+       aquaero_submit_array("flow", "sensor", aq_data.flow, AQ5_NUM_FLOW);
+
+       /* Liquid level */
+       aquaero_submit_array("percentage", "waterlevel",
+                       aq_data.level, AQ5_NUM_LEVEL);
+
+       return (0);
+}
+
+void module_register (void)
+{
+       plugin_register_complex_config ("aquaero", aquaero_config);
+       plugin_register_read ("aquaero", aquaero_read);
+       plugin_register_shutdown ("aquaero", aquaero_shutdown);
+} /* void module_register */
+
+/* vim: set sw=8 sts=8 noet : */
index 94a3938..6809bac 100644 (file)
@@ -541,7 +541,7 @@ static int ascent_init (void) /* {{{ */
 
   curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ascent_curl_callback);
-  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, ascent_curl_error);
 
   if (user != NULL)
index ddde840..2f990a2 100644 (file)
@@ -1395,7 +1395,7 @@ static int bind_init (void) /* {{{ */
 
   curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, bind_curl_callback);
-  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, bind_curl_error);
   curl_easy_setopt (curl, CURLOPT_URL, (url != NULL) ? url : BIND_DEFAULT_URL);
   curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L);
index e19d13e..6f58542 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-email - Documentation of collectd's C<email plugin>
index 10f6829..2c59630 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-exec - Documentation of collectd's C<exec plugin>
@@ -116,12 +118,8 @@ case you're confused.
 
 Since examples usually let one understand a lot better, here are some:
 
-  leeloo/cpu-0/cpu-idle N:2299366
-  alice/interface/if_octets-eth0 interval=10 1180647081:421465:479194
-
-Since this action was the only one supported with older versions of the C<exec
-plugin> all lines were treated as if they were prefixed with B<PUTVAL>. This is
-still the case to maintain backwards compatibility but deprecated.
+  PUTVAL leeloo/cpu-0/cpu-idle N:2299366
+  PUTVAL alice/interface/if_octets-eth0 interval=10 1180647081:421465:479194
 
 =item B<PUTNOTIF> [I<OptionList>] B<message=>I<Message>
 
index 9e2f81a..00f209a 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-java - Documentation of collectd's "java plugin"
index d7c749c..aab753c 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-nagios - Nagios plugin for querying collectd
index ee05e00..669e3f4 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-perl - Documentation of collectd's C<perl plugin>
index 39503ad..426a711 100644 (file)
@@ -8,6 +8,8 @@
 # The above copyright notice and this permission notice shall be included in
 # all copies or substantial portions of the Software.
 
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-python - Documentation of collectd's C<python plugin>
index 48dcba6..2ee0820 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-snmp - Documentation of collectd's C<snmp plugin>
index 5f1b630..97112e7 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-tg - Traffic generator for collectd.
index 02e41b8..35f8a9f 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-threshold - Documentation of collectd's I<Threshold plugin>
@@ -184,10 +186,12 @@ state will be keep.
 
 =item B<Interesting> B<true>|B<false>
 
-If set to B<true> (the default), the threshold must be treated as interesting
-and, when a number of B<Timeout> values will lost, then a missing notification
-will be dispatched. On the other hand, if set to B<false>, the missing
-notification will never dispatched for this threshold.
+If set to B<true> (the default), a notification with severity C<FAILURE> will
+be created when a matching value list is no longer updated and purged from the
+internal cache. When this happens depends on the I<interval> of the value list
+and the global B<Timeout> setting. See the B<Interval> and B<Timeout> settings
+in L<collectd.conf(5)> for details. If set to B<false>, this event will be
+ignored.
 
 =back
 
index 83802a1..40626d3 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-unixsock - Documentation of collectd's C<unixsock plugin>
index f4ac81a..4542a4b 100644 (file)
 #ReadThreads  5
 #WriteThreads 5
 
+# Limit the size of the write queue. Default is no limit. Setting up a limit is
+# recommended for servers handling a high volume of traffic.
+#WriteQueueLimitHigh 1000000
+#WriteQueueLimitLow   800000
+
 ##############################################################################
 # Logging                                                                    #
 #----------------------------------------------------------------------------#
@@ -74,6 +79,7 @@
 #@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
 #@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
 #@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors
+#@BUILD_PLUGIN_AQUAERO_TRUE@LoadPlugin aquaero
 #@BUILD_PLUGIN_ASCENT_TRUE@LoadPlugin ascent
 #@BUILD_PLUGIN_BATTERY_TRUE@LoadPlugin battery
 #@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind
 @LOAD_PLUGIN_RRDTOOL@LoadPlugin rrdtool
 #@BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors
 #@BUILD_PLUGIN_SERIAL_TRUE@LoadPlugin serial
+#@BUILD_PLUGIN_SIGROK_TRUE@LoadPlugin sigrok
 #@BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp
+#@BUILD_PLUGIN_STATSD_TRUE@LoadPlugin statsd
 #@BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap
 #@BUILD_PLUGIN_TABLE_TRUE@LoadPlugin table
 #@BUILD_PLUGIN_TAIL_TRUE@LoadPlugin tail
 #      ReportSeconds true
 #</Plugin>
 
+#<Plugin aquaero>
+#      Device ""
+#</Plugin>
+
 #<Plugin ascent>
 #      URL "http://localhost/ascent/status/"
 #      User "www-user"
 #      IgnoreSelected false
 #</Plugin>
 
+#<Plugin sigrok>
+#  LogLevel 3
+#  <Device "AC Voltage">
+#    Driver "fluke-dmm"
+#    MinimumInterval 10
+#    Conn "/dev/ttyUSB2"
+#  </Device>
+#  <Device "Sound Level">
+#    Driver "cem-dt-885x"
+#    Conn "/dev/ttyUSB1"
+#  </Device>
+#</Plugin>
+
 #<Plugin snmp>
 #   <Data "powerplus_voltge_input">
 #       Type "voltage"
 #   </Host>
 #</Plugin>
 
+#<Plugin statsd>
+#  Host "::"
+#  Port "8125"
+#  DeleteCounters false
+#  DeleteTimers   false
+#  DeleteGauges   false
+#  DeleteSets     false
+#  TimerPercentile 90.0
+#</Plugin>
+
 #<Plugin "swap">
 #      ReportByDevice false
 #      ReportBytes true
 #              AlwaysAppendDS false
 #      </Node>
 #      Tag "foobar"
+#       Attribute "foo" "bar"
 #</Plugin>
 
 ##############################################################################
index 55b9643..fe72534 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd.conf - Configuration for the system statistics collection daemon B<collectd>
@@ -148,15 +150,29 @@ use statements like the following:
 
   Include "/etc/collectd.d/*.conf"
 
+Starting with version 5.3, this may also be a block in which further options
+affecting the behavior of B<Include> may be specified. The following option is
+currently allowed:
+
+  <Include "/etc/collectd.d">
+    Filter "*.conf"
+  </Include>
+
+=over 4
+
+=item B<Filter> I<pattern>
+
 If the C<fnmatch> function is available on your system, a shell-like wildcard
 I<pattern> may be specified to filter which files to include. This may be used
 in combination with recursively including a directory to easily be able to
 arbitrarily mix configuration files and other documents (e.g. README files).
-The following statement is similar to the example above but includes all files
+The given example is similar to the first example above but includes all files
 matching C<*.conf> in any subdirectory of C</etc/collectd.d>:
 
   Include "/etc/collectd.d" "*.conf"
 
+=back
+
 If more than one files are included by a single B<Include> option, the files
 will be included in lexicographical order (as defined by the C<strcmp>
 function). Thus, you can e.E<nbsp>g. use numbered prefixes to specify the
@@ -219,6 +235,36 @@ Number of threads to start for dispatching value lists to write plugins. The
 default value is B<5>, but you may want to increase this if you have more than
 five plugins that may take relatively long to write to.
 
+=item B<WriteQueueLimitHigh> I<HighNum>
+
+=item B<WriteQueueLimitLow> I<LowNum>
+
+Metrics are read by the I<read threads> and then put into a queue to be handled
+by the I<write threads>. If one of the I<write plugins> is slow (e.g. network
+timeouts, I/O saturation of the disk) this queue will grow. In order to avoid
+running into memory issues in such a case, you can limit the size of this
+queue.
+
+By default, there is no limit and memory may grow indefinitely. This is most
+likely not an issue for clients, i.e. instances that only handle the local
+metrics. For servers it is recommended to set this to a non-zero value, though.
+
+You can set the limits using B<WriteQueueLimitHigh> and B<WriteQueueLimitLow>.
+Each of them takes a numerical argument which is the number of metrics in the
+queue. If there are I<HighNum> metrics in the queue, any new metrics I<will> be
+dropped. If there are less than I<LowNum> metrics in the queue, all new metrics
+I<will> be enqueued. If the number of metrics currently in the queue is between
+I<LowNum> and I<HighNum>, the metric is dropped with a probability that is
+proportional to the number of metrics in the queue (i.e. it increases linearly
+until it reaches 100%.)
+
+If B<WriteQueueLimitHigh> is set to non-zero and B<WriteQueueLimitLow> is
+unset, the latter will default to half of B<WriteQueueLimitHigh>.
+
+If you do not want to randomly drop values when the queue size is between
+I<LowNum> and I<HighNum>, set If B<WriteQueueLimitHigh> and
+B<WriteQueueLimitLow> to same value.
+
 =item B<Hostname> I<Name>
 
 Sets the hostname that identifies a host. If you omit this setting, the
@@ -684,6 +730,29 @@ default for backwards compatibility, the time will be reported in minutes.
 
 =back
 
+=head2 Plugin C<aquaero>
+
+This plugin collects the value of the available sensors in an
+I<AquaeroE<nbsp>5> board. AquaeroE<nbsp>5 is a water-cooling controller board,
+manufactured by Aqua Computer GmbH L<http://www.aquacomputer.de/>, with a USB2
+connection for monitoring and configuration. The board can handle multiple
+temperature sensors, fans, water pumps and water level sensors and adjust the
+output settings such as fan voltage or power used by the water pump based on
+the available inputs using a configurable controller included in the board.
+This plugin collects all the available inputs as well as some of the output
+values chosen by this controller. The plugin is based on the I<libaquaero5>
+library provided by I<aquatools-ng>.
+
+=over 4
+
+=item B<Device> I<DevicePath>
+
+Device path of the AquaeroE<nbsp>5's USB HID (human interface device), usually
+in the form C</dev/usb/hiddevX>. If this option is no set the plugin will try
+to auto-detect the Aquaero 5 USB device based on vendor-ID and product-ID.
+
+=back
+
 =head2 Plugin C<ascent>
 
 This plugin collects information about an Ascent server, a free server for the
@@ -1026,13 +1095,15 @@ is set to B<true>, B<Match> blocks are optional.
 
 =head2 Plugin C<curl_json>
 
-The B<curl_json plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and
-B<libyajl> (L<http://www.lloydforge.org/projects/yajl/>) to retrieve JSON data
-via cURL. This can be used to collect values from CouchDB documents (which are
-stored JSON notation), for example.
+The B<curl_json plugin> collects values from JSON data to be parsed by
+B<libyajl> (L<http://www.lloydforge.org/projects/yajl/>) retrieved via
+either B<libcurl> (L<http://curl.haxx.se/>) or read directly from a
+unix socket. The former can be used, for example, to collect values
+from CouchDB documents (which are stored JSON notation), and the
+latter to collect values from a uWSGI stats socket.
 
-The following example will collect several values from the built-in `_stats'
-runtime statistics module of CouchDB
+The following example will collect several values from the built-in
+C<_stats> runtime statistics module of I<CouchDB>
 (L<http://wiki.apache.org/couchdb/Runtime_Statistics>).
 
   <Plugin curl_json>
@@ -1052,11 +1123,30 @@ runtime statistics module of CouchDB
     </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) and one or more B<Key> blocks.
-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.
+This example will collect data directly from a I<uWSGI> "Stats Server" socket.
+
+  <Plugin curl_json>
+    <Sock "/var/run/uwsgi.stats.sock">
+      Instance "uwsgi"
+      <Key "workers/*/requests">
+        Type "http_requests"
+      </Key>
+
+      <Key "workers/*/apps/*/requests">
+        Type "http_requests"
+      </Key>
+    </Sock>
+  </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) or B<Sock>
+blocks defining a unix socket to read JSON from directly.  Each of
+these blocks may have one or more B<Key> blocks.
+
+The B<Key> string argument must be in a path format. Each component is
+used to match the key from a JSON map or the index of an JSON
+array. If a path component of a B<Key> is a I<*>E<nbsp>wildcard, the
+values for all map keys or array indices will be collectd.
 
 The following options are valid within B<URL> blocks:
 
@@ -1511,14 +1601,19 @@ Enable this option if inodes are a scarce resource for you, usually because
 many small files are stored on the disk. This is a usual scenario for mail
 transfer agents and web caches.
 
-=item B<ReportPercentage> B<false>|B<true>
+=item B<ValuesAbsolute> B<true>|B<false>
 
-Enables or disables reporting of disk space and inodes as a percentage.
-Defaults to B<false>.
+Enables or disables reporting of free, used and used disk space in 1K-blocks. 
+Defaults to true.
 
-This is useful for deploying I<collectd> on the cloud, where machines with
-different disk size may exist. Then it is more practical to configure
-thresholds based on relative disk size.
+=item B<ValuesPercentage> B<true>|B<false>
+
+Enables or disables reporting of free, used and used disk space in percentage.
+Defaults to false.
+
+This is useful for deploying collectd on the cloud, where machines with
+different disk size may exist. Then it is more practical to configure thresholds
+based on relative disk size.
 
 =back
 
@@ -5097,12 +5192,129 @@ and all other sensors are collected.
 
 =back
 
+=head2 Plugin C<sigrok>
+
+The I<sigrok plugin> uses I<libsigrok> to retrieve measurements from any device
+supported by the L<sigrok|http://sigrok.org/> project.
+
+B<Synopsis>
+
+ <Plugin sigrok>
+   LogLevel 3
+   <Device "AC Voltage">
+      Driver "fluke-dmm"
+      MinimumInterval 10
+      Conn "/dev/ttyUSB2"
+   </Device>
+   <Device "Sound Level">
+      Driver "cem-dt-885x"
+      Conn "/dev/ttyUSB1"
+   </Device>
+ </Plugin>
+
+=over 4
+
+=item B<LogLevel> B<0-5>
+
+The I<sigrok> logging level to pass on to the I<collectd> log, as a number
+between B<0> and B<5> (inclusive). These levels correspond to C<None>,
+C<Errors>, C<Warnings>, C<Informational>, C<Debug >and C<Spew>, respectively.
+The default is B<2> (C<Warnings>). The I<sigrok> log messages, regardless of
+their level, are always submitted to I<collectd> at its INFO log level.
+
+=item E<lt>B<Device> I<Name>E<gt>
+
+A sigrok-supported device, uniquely identified by this section's options. The
+I<Name> is passed to I<collectd> as the I<plugin instance>.
+
+=item B<Driver> I<DriverName>
+
+The sigrok driver to use for this device.
+
+=item B<Conn> I<ConnectionSpec>
+
+If the device cannot be auto-discovered, or more than one might be discovered
+by the driver, I<ConnectionSpec> specifies the connection string to the device.
+It can be of the form of a device path (e.g.E<nbsp>C</dev/ttyUSB2>), or, in
+case of a non-serial USB-connected device, the USB I<VendorID>B<.>I<ProductID>
+separated by a period (e.g.E<nbsp>C<0403.6001>). A USB device can also be
+specified as I<Bus>B<.>I<Address> (e.g.E<nbsp>C<1.41>).
+
+=item B<SerialComm> I<SerialSpec>
+
+For serial devices with non-standard port settings, this option can be used
+to specify them in a form understood by I<sigrok>, e.g.E<nbsp>C<9600/8n1>.
+This should not be necessary; drivers know how to communicate with devices they
+support.
+
+=item B<MinimumInterval> I<Seconds>
+
+Specifies the minimum time between measurement dispatches to I<collectd>, in
+seconds. Since some I<sigrok> supported devices can acquire measurements many
+times per second, it may be necessary to throttle these. For example, the
+I<RRD plugin> cannot process writes more than once per second.
+
+The default B<MinimumInterval> is B<0>, meaning measurements received from the
+device are always dispatched to I<collectd>. When throttled, unused
+measurements are discarded.
+
+=back
+
 =head2 Plugin C<snmp>
 
 Since the configuration of the C<snmp plugin> is a little more complicated than
 other plugins, its documentation has been moved to an own manpage,
 L<collectd-snmp(5)>. Please see there for details.
 
+=head2 Plugin C<statsd>
+
+The I<statsd plugin> listens to a UDP socket, reads "events" in the statsd
+protocol and dispatches rates or other aggregates of these numbers
+periodically.
+
+The plugin implements the I<Counter>, I<Timer>, I<Gauge> and I<Set> types which
+are dispatched as the I<collectd> types C<derive>, C<latency>, C<gauge> and
+C<objects> respectively.
+
+The following configuration options are valid:
+
+=over 4
+
+=item B<Host> I<Host>
+
+Bind to the hostname / address I<Host>. By default, the plugin will bind to the
+"any" address, i.e. accept packets sent to any of the hosts addresses.
+
+=item B<Port> I<Port>
+
+UDP port to listen to. This can be either a service name or a port number.
+Defaults to C<8125>.
+
+=item B<DeleteCounters> B<false>|B<true>
+
+=item B<DeleteTimers> B<false>|B<true>
+
+=item B<DeleteGauges> B<false>|B<true>
+
+=item B<DeleteSets> B<false>|B<true>
+
+These options control what happens if metrics are not updated in an interval.
+If set to B<False>, the default, metrics are dispatched unchanged, i.e. the
+rate of counters and size of sets will be zero, timers report C<NaN> and gauges
+are unchanged. If set to B<True>, the such metrics are not dispatched and
+removed from the internal cache.
+
+=item B<TimerPercentile> I<Percent>
+
+Calculate and dispatch the configured percentile, i.e. compute the latency, so
+that I<Percent> of all reported timers are smaller than or equal to the
+computed latency. This is useful for cutting off the long tail latency, as it's
+often done in I<Service Level Agreements> (SLAs).
+
+If not specified, no percentile is calculated / dispatched.
+
+=back
+
 =head2 Plugin C<swap>
 
 The I<Swap plugin> collects information about used and available swap space. On
@@ -5696,7 +5908,33 @@ Take the UUID from the given file (default I</etc/uuid>).
 
 =head2 Plugin C<varnish>
 
-The Varnish plugin collects information about Varnish, an HTTP accelerator.
+The I<varnish plugin> collects information about Varnish, an HTTP accelerator.
+
+Synopsis:
+
+ <Plugin "varnish">
+   <Instance "example">
+     CollectCache       true
+     CollectConnections true
+     CollectBackend     true
+     CollectSHM         true
+     CollectESI         false
+     CollectFetch       false
+     CollectHCB         false
+     CollectSMA         false
+     CollectSMS         false
+     CollectSM          false
+     CollectTotals      false
+     CollectWorkers     false
+   </Instance>
+ </Plugin>
+
+The configuration consists of one or more E<lt>B<Instance>E<nbsp>I<Name>E<gt>
+blocks. I<Name> is the parameter passed to "varnishd -n". If left empty, it
+will collectd statistics from the default "varnishd" instance (this should work
+fine in most cases).
+
+Inside each E<lt>B<Instance>E<gt> blocks, the following options are recognized:
 
 =over 4
 
@@ -6050,6 +6288,7 @@ Synopsis:
      TTLFactor 2.0
    </Node>
    Tag "foobar"
+   Attribute "foo" "bar"
  </Plugin>
 
 The following options are understood by the I<write_riemann plugin>:
@@ -6110,6 +6349,11 @@ default value.
 Add the given string as an additional tag to the metric being sent to
 I<Riemann>.
 
+=item B<Attribute> I<String> I<String>
+
+Consider the two given strings to be the key and value of an additional
+attribute for each metric being sent out to I<Riemann>.
+
 =back
 
 =head1 THRESHOLD CONFIGURATION
index 7cb405c..969aeda 100644 (file)
@@ -262,6 +262,10 @@ typedef int _Bool;
 # define COLLECTD_DEFAULT_INTERVAL 10.0
 #endif
 
+ #ifndef COLLECTD_USERAGENT
+ # define COLLECTD_USERAGENT PACKAGE_NAME"/"PACKAGE_VERSION
+ #endif
+
 /* Remove GNU specific __attribute__ settings when using another compiler */
 #if !__GNUC__
 # define __attribute__(x) /**/
index e36dcdf..2f7b9f7 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd - System statistics collection daemon
index 21c0b50..52b8ef2 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectdctl - Control interface for collectd
index 73ba6b8..3151e11 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectdmon - Monitoring daemon for collectd
index d963efa..161b4d6 100644 (file)
@@ -721,7 +721,7 @@ long long get_kstat_value (kstat_t *ksp, char *name)
                retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
        else
                WARNING ("get_kstat_value: Not a numeric value: %s", name);
-                
+
        return (retval);
 }
 #endif /* HAVE_LIBKSTAT */
@@ -828,36 +828,43 @@ int format_name (char *ret, int ret_len,
                const char *plugin, const char *plugin_instance,
                const char *type, const char *type_instance)
 {
-       int  status;
+  char *buffer;
+  size_t buffer_size;
+
+  buffer = ret;
+  buffer_size = (size_t) ret_len;
+
+#define APPEND(str) do {                                               \
+  size_t l = strlen (str);                                             \
+  if (l >= buffer_size)                                                \
+    return (ENOBUFS);                                                  \
+  memcpy (buffer, (str), l);                                           \
+  buffer += l; buffer_size -= l;                                       \
+} while (0)
 
-       assert (plugin != NULL);
-       assert (type != NULL);
+  assert (plugin != NULL);
+  assert (type != NULL);
 
-       if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0))
-       {
-               if ((type_instance == NULL) || (strlen (type_instance) == 0))
-                       status = ssnprintf (ret, ret_len, "%s/%s/%s",
-                                       hostname, plugin, type);
-               else
-                       status = ssnprintf (ret, ret_len, "%s/%s/%s-%s",
-                                       hostname, plugin, type,
-                                       type_instance);
-       }
-       else
-       {
-               if ((type_instance == NULL) || (strlen (type_instance) == 0))
-                       status = ssnprintf (ret, ret_len, "%s/%s-%s/%s",
-                                       hostname, plugin, plugin_instance,
-                                       type);
-               else
-                       status = ssnprintf (ret, ret_len, "%s/%s-%s/%s-%s",
-                                       hostname, plugin, plugin_instance,
-                                       type, type_instance);
-       }
+  APPEND (hostname);
+  APPEND ("/");
+  APPEND (plugin);
+  if ((plugin_instance != NULL) && (plugin_instance[0] != 0))
+  {
+    APPEND ("-");
+    APPEND (plugin_instance);
+  }
+  APPEND ("/");
+  APPEND (type);
+  if ((type_instance != NULL) && (type_instance[0] != 0))
+  {
+    APPEND ("-");
+    APPEND (type_instance);
+  }
+  assert (buffer_size > 0);
+  buffer[0] = 0;
 
-       if ((status < 1) || (status >= ret_len))
-               return (-1);
-       return (0);
+#undef APPEND
+  return (0);
 } /* int format_name */
 
 int format_values (char *ret, size_t ret_len, /* {{{ */
index 876ee23..0e54f26 100644 (file)
@@ -109,6 +109,8 @@ static cf_global_option_t cf_global_options[] =
        {"Interval",    NULL, NULL},
        {"ReadThreads", NULL, "5"},
        {"WriteThreads", NULL, "5"},
+       {"WriteQueueLimitHigh", NULL, NULL},
+       {"WriteQueueLimitLow", NULL, NULL},
        {"Timeout",     NULL, "2"},
        {"AutoLoadPlugin", NULL, "false"},
        {"PreCacheChain",  NULL, "PreCache"},
@@ -586,7 +588,7 @@ static int cf_include_all (oconfig_item_t *root, int depth)
                sfree (pattern);
 
                if (new == NULL)
-                       continue;
+                       return (-1);
 
                /* Now replace the i'th child in `root' with `new'. */
                cf_ci_replace_child (root, new, i);
@@ -605,6 +607,7 @@ static oconfig_item_t *cf_read_file (const char *file,
                const char *pattern, int depth)
 {
        oconfig_item_t *root;
+       int status;
 
        assert (depth < CF_MAX_DEPTH);
 
@@ -636,7 +639,12 @@ static oconfig_item_t *cf_read_file (const char *file,
                return (NULL);
        }
 
-       cf_include_all (root, depth);
+       status = cf_include_all (root, depth);
+       if (status != 0)
+       {
+               oconfig_free (root);
+               return (NULL);
+       }
 
        return (root);
 } /* oconfig_item_t *cf_read_file */
@@ -829,12 +837,6 @@ static oconfig_item_t *cf_read_generic (const char *path,
 
        wordfree (&we);
 
-       if (root->children == NULL)
-       {
-               oconfig_free (root);
-               return (NULL);
-       }
-
        return (root);
 } /* oconfig_item_t *cf_read_generic */
 /* #endif HAVE_WORDEXP_H */
@@ -915,6 +917,23 @@ const char *global_option_get (const char *option)
                        : cf_global_options[i].def);
 } /* char *global_option_get */
 
+long global_option_get_long (const char *option, long default_value)
+{
+               const char *str;
+               long value;
+
+               str = global_option_get (option);
+               if (NULL == str)
+                       return (default_value);
+
+               errno = 0;
+               value = strtol (str, /* endptr = */ NULL, /* base = */ 0);
+               if (errno != 0)
+                       return (default_value);
+
+               return (value);
+} /* char *global_option_get_long */
+
 cdtime_t cf_get_default_interval (void)
 {
   char const *str = global_option_get ("Interval");
@@ -1046,6 +1065,12 @@ int cf_read (char *filename)
                ERROR ("Unable to read config file %s.", filename);
                return (-1);
        }
+       else if (conf->children_num == 0)
+       {
+               ERROR ("Configuration file %s is empty.", filename);
+               oconfig_free (conf);
+               return (-1);
+       }
 
        for (i = 0; i < conf->children_num; i++)
        {
index 5a719a4..c91fcd5 100644 (file)
@@ -86,6 +86,8 @@ int cf_read (char *filename);
 
 int global_option_set (const char *option, const char *value);
 const char *global_option_get (const char *option);
+long global_option_get_long (const char *option, long default_value);
+long global_option_get_long_in_range (const char *option, long default_value, long min, long max);
 
 cdtime_t cf_get_default_interval (void);
 
index 02d62c1..958e34b 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -121,65 +121,62 @@ static int value_list_to_string (char *buffer, int buffer_len,
        return (0);
 } /* int value_list_to_string */
 
-static int value_list_to_filename (char *buffer, int buffer_len,
-               const data_set_t *ds, const value_list_t *vl)
+static int value_list_to_filename (char *buffer, size_t buffer_size,
+               value_list_t const *vl)
 {
-       int offset = 0;
        int status;
 
-       assert (0 == strcmp (ds->type, vl->type));
+       char *ptr = buffer;
+       size_t ptr_size = buffer_size;
+       time_t now;
+       struct tm struct_tm;
 
        if (datadir != NULL)
        {
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", datadir);
-               if ((status < 1) || (status >= buffer_len - offset))
-                       return (-1);
-               offset += status;
+               size_t len = strlen (datadir) + 1;
+
+               if (len >= ptr_size)
+                       return (ENOBUFS);
+
+               memcpy (ptr, datadir, len);
+               ptr[len-1] = '/';
+               ptr_size -= len;
+               ptr += len;
        }
 
-       status = ssnprintf (buffer + offset, buffer_len - offset,
-                       "%s/", vl->host);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       status = FORMAT_VL (ptr, ptr_size, vl);
+       if (status != 0)
+               return (status);
 
-       if (strlen (vl->plugin_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s/", vl->plugin, vl->plugin_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", vl->plugin);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       /* Skip all the time formatting stuff when printing to STDOUT or
+        * STDERR. */
+       if (use_stdio)
+               return (0);
 
-       if (strlen (vl->type_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s", vl->type, vl->type_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s", vl->type);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       ptr_size -= strlen (ptr);
+       ptr +=  strlen (ptr);
 
-       if (!use_stdio)
+       /* "-2013-07-12" => 11 bytes */
+       if (ptr_size < 12)
        {
-               time_t now;
-               struct tm stm;
+               ERROR ("csv plugin: Buffer too small.");
+               return (ENOMEM);
+       }
 
-               /* TODO: Find a way to minimize the calls to `localtime_r',
-                * since they are pretty expensive.. */
-               now = time (NULL);
-               if (localtime_r (&now, &stm) == NULL)
-               {
-                       ERROR ("csv plugin: localtime_r failed");
-                       return (1);
-               }
+       /* TODO: Find a way to minimize the calls to `localtime_r',
+        * since they are pretty expensive.. */
+       now = time (NULL);
+       if (localtime_r (&now, &struct_tm) == NULL)
+       {
+               ERROR ("csv plugin: localtime_r failed");
+               return (-1);
+       }
 
-               strftime (buffer + offset, buffer_len - offset,
-                               "-%Y-%m-%d", &stm);
+       status = strftime (ptr, ptr_size, "-%Y-%m-%d", &struct_tm);
+       if (status == 0) /* yep, it returns zero on error. */
+       {
+               ERROR ("csv plugin: strftime failed");
+               return (-1);
        }
 
        return (0);
@@ -218,7 +215,10 @@ static int csv_config (const char *key, const char *value)
        if (strcasecmp ("DataDir", key) == 0)
        {
                if (datadir != NULL)
+               {
                        free (datadir);
+                       datadir = NULL;
+               }
                if (strcasecmp ("stdout", value) == 0)
                {
                        use_stdio = 1;
@@ -275,7 +275,8 @@ static int csv_write (const data_set_t *ds, const value_list_t *vl,
                return -1;
        }
 
-       if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+       status = value_list_to_filename (filename, sizeof (filename), vl);
+       if (status != 0)
                return (-1);
 
        DEBUG ("csv plugin: csv_write: filename = %s;", filename);
index 280e61c..5990368 100644 (file)
@@ -364,8 +364,7 @@ static int cc_page_init_curl (web_page_t *wp) /* {{{ */
   curl_easy_setopt (wp->curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (wp->curl, CURLOPT_WRITEFUNCTION, cc_curl_callback);
   curl_easy_setopt (wp->curl, CURLOPT_WRITEDATA, wp);
-  curl_easy_setopt (wp->curl, CURLOPT_USERAGENT,
-      PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (wp->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (wp->curl, CURLOPT_ERRORBUFFER, wp->curl_errbuf);
   curl_easy_setopt (wp->curl, CURLOPT_URL, wp->url);
   curl_easy_setopt (wp->curl, CURLOPT_FOLLOWLOCATION, 1L);
index deee460..251fee8 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/curl_json.c
  * Copyright (C) 2009       Doug MacEachern
- * Copyright (C) 2006-2011  Florian octo Forster
+ * Copyright (C) 2006-2013  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
 #include "utils_avltree.h"
 #include "utils_complain.h"
 
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
 #include <curl/curl.h>
+
 #include <yajl/yajl_parse.h>
 #if HAVE_YAJL_YAJL_VERSION_H
 # include <yajl/yajl_version.h>
@@ -60,6 +65,8 @@ struct cj_s /* {{{ */
   char *instance;
   char *host;
 
+  char *sock;
+
   char *url;
   char *user;
   char *pass;
@@ -82,6 +89,8 @@ struct cj_s /* {{{ */
       c_avl_tree_t *tree;
       cj_key_t *key;
     };
+    _Bool in_array;
+    int index;
     char name[DATA_MAX_NAME_LEN];
   } state[YAJL_MAX_DEPTH];
 };
@@ -94,7 +103,6 @@ typedef unsigned int yajl_len_t;
 #endif
 
 static int cj_read (user_data_t *ud);
-static int cj_curl_perform (cj_t *db, CURL *curl);
 static void cj_submit (cj_t *db, cj_key_t *key, value_t *value);
 
 static size_t cj_curl_callback (void *buf, /* {{{ */
@@ -167,12 +175,44 @@ static int cj_get_type (cj_key_t *key)
   return ds->ds[0].type;
 }
 
+static int cj_cb_map_key (void *ctx, const unsigned char *val,
+    yajl_len_t len);
+
+static void cj_cb_inc_array_index (void *ctx, _Bool update_key)
+{
+  cj_t *db = (cj_t *)ctx;
+
+  if (!db->state[db->depth].in_array)
+    return;
+
+  db->state[db->depth].index++;
+
+  if (update_key)
+  {
+    char name[DATA_MAX_NAME_LEN];
+
+    ssnprintf (name, sizeof (name), "%d", db->state[db->depth].index - 1);
+
+    cj_cb_map_key (ctx, (unsigned char *)name, (yajl_len_t) strlen (name));
+  }
+}
+
 /* yajl callbacks */
 #define CJ_CB_ABORT    0
 #define CJ_CB_CONTINUE 1
 
-/* "number" may not be null terminated, so copy it into a buffer before
- * parsing. */
+static int cj_cb_boolean (void * ctx, int boolVal)
+{
+  cj_cb_inc_array_index (ctx, /* update_key = */ 0);
+  return (CJ_CB_CONTINUE);
+}
+
+static int cj_cb_null (void * ctx)
+{
+  cj_cb_inc_array_index (ctx, /* update_key = */ 0);
+  return (CJ_CB_CONTINUE);
+}
+
 static int cj_cb_number (void *ctx,
     const char *number, yajl_len_t number_len)
 {
@@ -184,12 +224,20 @@ static int cj_cb_number (void *ctx,
   int type;
   int status;
 
-  if ((key == NULL) || !CJ_IS_KEY (key))
-    return (CJ_CB_CONTINUE);
-
+  /* Create a null-terminated version of the string. */
   memcpy (buffer, number, number_len);
   buffer[sizeof (buffer) - 1] = 0;
 
+  if ((key == NULL) || !CJ_IS_KEY (key)) {
+    if (key != NULL)
+      NOTICE ("curl_json plugin: Found \"%s\", but the configuration expects"
+              " a map.", buffer);
+    cj_cb_inc_array_index (ctx, /* update_key = */ 0);
+    return (CJ_CB_CONTINUE);
+  } else {
+    cj_cb_inc_array_index (ctx, /* update_key = */ 1);
+  }
+
   type = cj_get_type (key);
   status = parse_value (buffer, &vt, type);
   if (status != 0)
@@ -202,8 +250,11 @@ static int cj_cb_number (void *ctx,
   return (CJ_CB_CONTINUE);
 } /* int cj_cb_number */
 
-static int cj_cb_map_key (void *ctx, const unsigned char *val,
-    yajl_len_t len)
+/* Queries the key-tree of the parent context for "in_name" and, if found,
+ * updates the "key" field of the current context. Otherwise, "key" is set to
+ * NULL. */
+static int cj_cb_map_key (void *ctx,
+    unsigned char const *in_name, yajl_len_t in_name_len)
 {
   cj_t *db = (cj_t *)ctx;
   c_avl_tree_t *tree;
@@ -212,12 +263,16 @@ static int cj_cb_map_key (void *ctx, const unsigned char *val,
 
   if (tree != NULL)
   {
-    cj_key_t *value;
+    cj_key_t *value = NULL;
     char *name;
+    size_t name_len;
 
+    /* Create a null-terminated version of the name. */
     name = db->state[db->depth].name;
-    len = COUCH_MIN(len, sizeof (db->state[db->depth].name)-1);
-    sstrncpy (name, (char *)val, len+1);
+    name_len = COUCH_MIN ((size_t) in_name_len,
+        sizeof (db->state[db->depth].name) - 1);
+    memcpy (name, in_name, name_len);
+    name[name_len] = 0;
 
     if (c_avl_get (tree, name, (void *) &value) == 0)
       db->state[db->depth].key = value;
@@ -233,24 +288,6 @@ static int cj_cb_map_key (void *ctx, const unsigned char *val,
 static int cj_cb_string (void *ctx, const unsigned char *val,
     yajl_len_t len)
 {
-  cj_t *db = (cj_t *)ctx;
-  char str[len + 1];
-
-  /* Create a null-terminated version of the string. */
-  memcpy (str, val, len);
-  str[len] = 0;
-
-  /* No configuration for this string -> simply return. */
-  if (db->state[db->depth].key == NULL)
-    return (CJ_CB_CONTINUE);
-
-  if (!CJ_IS_KEY (db->state[db->depth].key))
-  {
-    NOTICE ("curl_json plugin: Found string \"%s\", but the configuration "
-        "expects a map here.", str);
-    return (CJ_CB_CONTINUE);
-  }
-
   /* Handle the string as if it was a number. */
   return (cj_cb_number (ctx, (const char *) val, len));
 } /* int cj_cb_string */
@@ -260,7 +297,8 @@ static int cj_cb_start (void *ctx)
   cj_t *db = (cj_t *)ctx;
   if (++db->depth >= YAJL_MAX_DEPTH)
   {
-    ERROR ("curl_json plugin: %s depth exceeds max, aborting.", db->url);
+    ERROR ("curl_json plugin: %s depth exceeds max, aborting.",
+           db->url ? db->url : db->sock);
     return (CJ_CB_ABORT);
   }
   return (CJ_CB_CONTINUE);
@@ -276,6 +314,7 @@ static int cj_cb_end (void *ctx)
 
 static int cj_cb_start_map (void *ctx)
 {
+  cj_cb_inc_array_index (ctx, /* update_key = */ 1);
   return cj_cb_start (ctx);
 }
 
@@ -286,17 +325,25 @@ static int cj_cb_end_map (void *ctx)
 
 static int cj_cb_start_array (void * ctx)
 {
+  cj_t *db = (cj_t *)ctx;
+  cj_cb_inc_array_index (ctx, /* update_key = */ 1);
+  if (db->depth+1 < YAJL_MAX_DEPTH) {
+    db->state[db->depth+1].in_array = 1;
+    db->state[db->depth+1].index = 0;
+  }
   return cj_cb_start (ctx);
 }
 
 static int cj_cb_end_array (void * ctx)
 {
+  cj_t *db = (cj_t *)ctx;
+  db->state[db->depth].in_array = 0;
   return cj_cb_end (ctx);
 }
 
 static yajl_callbacks ycallbacks = {
-  NULL, /* null */
-  NULL, /* boolean */
+  cj_cb_null, /* null */
+  cj_cb_boolean, /* boolean */
   NULL, /* integer */
   NULL, /* double */
   cj_cb_number,
@@ -364,6 +411,8 @@ static void cj_free (void *arg) /* {{{ */
   sfree (db->instance);
   sfree (db->host);
 
+  sfree (db->sock);
+
   sfree (db->url);
   sfree (db->user);
   sfree (db->pass);
@@ -537,8 +586,7 @@ static int cj_init_curl (cj_t *db) /* {{{ */
   curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cj_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_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
   curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
 
@@ -598,20 +646,20 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
   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 if (strcasecmp ("Sock", ci->key) == 0)
+    status = cf_util_get_string (ci, &db->sock);
   else
   {
     ERROR ("curl_json plugin: cj_config: "
            "Invalid key: %s", ci->key);
     return (-1);
   }
+  if (status != 0)
+  {
+    sfree (db);
+    return (status);
+  }
 
   /* Fill the `cj_t' structure.. */
   for (i = 0; i < ci->children_num; i++)
@@ -622,19 +670,19 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
       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)
+    else if (db->url && strcasecmp ("User", child->key) == 0)
       status = cf_util_get_string (child, &db->user);
-    else if (strcasecmp ("Password", child->key) == 0)
+    else if (db->url && strcasecmp ("Password", child->key) == 0)
       status = cf_util_get_string (child, &db->pass);
-    else if (strcasecmp ("VerifyPeer", child->key) == 0)
+    else if (db->url && strcasecmp ("VerifyPeer", child->key) == 0)
       status = cf_util_get_boolean (child, &db->verify_peer);
-    else if (strcasecmp ("VerifyHost", child->key) == 0)
+    else if (db->url && strcasecmp ("VerifyHost", child->key) == 0)
       status = cf_util_get_boolean (child, &db->verify_host);
-    else if (strcasecmp ("CACert", child->key) == 0)
+    else if (db->url && strcasecmp ("CACert", child->key) == 0)
       status = cf_util_get_string (child, &db->cacert);
-    else if (strcasecmp ("Header", child->key) == 0)
+    else if (db->url && strcasecmp ("Header", child->key) == 0)
       status = cj_config_append_string ("Header", &db->headers, child);
-    else if (strcasecmp ("Post", child->key) == 0)
+    else if (db->url && strcasecmp ("Post", child->key) == 0)
       status = cf_util_get_string (child, &db->post_body);
     else if (strcasecmp ("Key", child->key) == 0)
       status = cj_config_add_key (db, child);
@@ -652,11 +700,11 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
   {
     if (db->tree == NULL)
     {
-      WARNING ("curl_json plugin: No (valid) `Key' block "
-               "within `URL' block `%s'.", db->url);
+      WARNING ("curl_json plugin: No (valid) `Key' block within `%s' \"`%s'\".",
+               db->url ? "URL" : "Sock", db->url ? db->url : db->sock);
       status = -1;
     }
-    if (status == 0)
+    if (status == 0 && db->url)
       status = cj_init_curl (db);
   }
 
@@ -677,7 +725,7 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
     ud.free_func = cj_free;
 
     ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
-               db->instance, db->url);
+               db->instance, db->url ? db->url : db->sock);
 
     plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
                                   /* interval = */ NULL, &ud);
@@ -706,7 +754,8 @@ static int cj_config (oconfig_item_t *ci) /* {{{ */
   {
     oconfig_item_t *child = ci->children + i;
 
-    if (strcasecmp ("URL", child->key) == 0)
+    if (strcasecmp ("Sock", child->key) == 0
+        || strcasecmp ("URL", child->key) == 0)
     {
       status = cj_config_add_url (child);
       if (status == 0)
@@ -766,47 +815,97 @@ static void cj_submit (cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
   plugin_dispatch_values (&vl);
 } /* }}} int cj_submit */
 
-static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */
+static int cj_sock_perform (cj_t *db) /* {{{ */
 {
-  int status;
-  long rc;
-  char *url;
-  yajl_handle yprev = db->yajl;
+  char errbuf[1024];
+  struct sockaddr_un sa_unix = {};
+  sa_unix.sun_family = AF_UNIX;
+  sstrncpy (sa_unix.sun_path, db->sock, sizeof (sa_unix.sun_path));
 
-  db->yajl = yajl_alloc (&ycallbacks,
-#if HAVE_YAJL_V2
-      /* alloc funcs = */ NULL,
-#else
-      /* alloc funcs = */ NULL, NULL,
-#endif
-      /* context = */ (void *)db);
-  if (db->yajl == NULL)
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd < 0)
+    return (-1);
+  if (connect (fd, (struct sockaddr *)&sa_unix, sizeof(sa_unix)) < 0)
   {
-    ERROR ("curl_json plugin: yajl_alloc failed.");
-    db->yajl = yprev;
+    ERROR ("curl_json plugin: connect(%s) failed: %s",
+           (db->sock != NULL) ? db->sock : "<null>",
+           sstrerror(errno, errbuf, sizeof (errbuf)));
+    close (fd);
     return (-1);
   }
 
+  ssize_t red;
+  do {
+    unsigned char buffer[4096];
+    red = read (fd, buffer, sizeof(buffer));
+    if (red < 0) {
+        ERROR ("curl_json plugin: read(%s) failed: %s",
+               (db->sock != NULL) ? db->sock : "<null>",
+               sstrerror(errno, errbuf, sizeof (errbuf)));
+        close (fd);
+        return (-1);
+    }
+    if (!cj_curl_callback (buffer, red, 1, db))
+        break;
+  } while (red > 0);
+  close (fd);
+  return (0);
+} /* }}} int cj_sock_perform */
+
+
+static int cj_curl_perform(cj_t *db) /* {{{ */
+{
+  int status;
+  long rc;
+  char *url;
   url = NULL;
-  curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
+  curl_easy_getinfo(db->curl, CURLINFO_EFFECTIVE_URL, &url);
 
-  status = curl_easy_perform (curl);
+  status = curl_easy_perform (db->curl);
   if (status != CURLE_OK)
   {
     ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
            status, db->curl_errbuf, (url != NULL) ? url : "<null>");
-    yajl_free (db->yajl);
-    db->yajl = yprev;
     return (-1);
   }
 
-  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
+  curl_easy_getinfo(db->curl, CURLINFO_RESPONSE_CODE, &rc);
 
   /* The response code is zero if a non-HTTP transport was used. */
   if ((rc != 0) && (rc != 200))
   {
     ERROR ("curl_json plugin: curl_easy_perform failed with "
         "response code %ld (%s)", rc, url);
+    return (-1);
+  }
+  return (0);
+} /* }}} int cj_curl_perform */
+
+static int cj_perform (cj_t *db) /* {{{ */
+{
+  int status;
+  yajl_handle yprev = db->yajl;
+
+  db->yajl = yajl_alloc (&ycallbacks,
+#if HAVE_YAJL_V2
+      /* alloc funcs = */ NULL,
+#else
+      /* alloc funcs = */ NULL, NULL,
+#endif
+      /* context = */ (void *)db);
+  if (db->yajl == NULL)
+  {
+    ERROR ("curl_json plugin: yajl_alloc failed.");
+    db->yajl = yprev;
+    return (-1);
+  }
+
+  if (db->url)
+    status = cj_curl_perform (db);
+  else
+    status = cj_sock_perform (db);
+  if (status < 0)
+  {
     yajl_free (db->yajl);
     db->yajl = yprev;
     return (-1);
@@ -834,7 +933,7 @@ static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */
   yajl_free (db->yajl);
   db->yajl = yprev;
   return (0);
-} /* }}} int cj_curl_perform */
+} /* }}} int cj_perform */
 
 static int cj_read (user_data_t *ud) /* {{{ */
 {
@@ -853,7 +952,7 @@ static int cj_read (user_data_t *ud) /* {{{ */
   db->state[db->depth].tree = db->tree;
   db->key = NULL;
 
-  return cj_curl_perform (db, db->curl);
+  return cj_perform (db);
 } /* }}} int cj_read */
 
 void module_register (void)
index 5adaf06..3554d7e 100644 (file)
@@ -837,8 +837,7 @@ static int cx_init_curl (cx_t *db) /* {{{ */
   curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1L);
   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_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
   curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
 
index 80488d8..779c723 100644 (file)
--- a/src/dbi.c
+++ b/src/dbi.c
@@ -68,6 +68,8 @@ static size_t            queries_num   = 0;
 static cdbi_database_t **databases     = NULL;
 static size_t            databases_num = 0;
 
+static int cdbi_read_database (user_data_t *ud);
+
 /*
  * Functions
  */
@@ -369,9 +371,24 @@ static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
     }
     else
     {
+      user_data_t ud;
+      char *name = NULL;
+
       databases = temp;
       databases[databases_num] = db;
       databases_num++;
+
+      memset (&ud, 0, sizeof (ud));
+      ud.data = (void *) db;
+      ud.free_func = NULL;
+      name = ssnprintf_alloc("dbi:%s", db->name);
+
+      plugin_register_complex_read (/* group = */ NULL,
+          /* name = */ name ? name : db->name,
+          /* callback = */ cdbi_read_database,
+          /* interval = */ NULL,
+          /* user_data = */ &ud);
+      free (name);
     }
   }
 
@@ -398,7 +415,7 @@ static int cdbi_config (oconfig_item_t *ci) /* {{{ */
       cdbi_config_add_database (child);
     else
     {
-      WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
+      WARNING ("dbi plugin: Ignoring unknown config option `%s'.", child->key);
     }
   } /* for (ci->children) */
 
@@ -758,8 +775,9 @@ static int cdbi_connect_database (cdbi_database_t *db) /* {{{ */
   return (0);
 } /* }}} int cdbi_connect_database */
 
-static int cdbi_read_database (cdbi_database_t *db) /* {{{ */
+static int cdbi_read_database (user_data_t *ud) /* {{{ */
 {
+  cdbi_database_t *db = (cdbi_database_t *) ud->data;
   size_t i;
   int success;
   int status;
@@ -798,29 +816,6 @@ static int cdbi_read_database (cdbi_database_t *db) /* {{{ */
   return (0);
 } /* }}} int cdbi_read_database */
 
-static int cdbi_read (void) /* {{{ */
-{
-  size_t i;
-  int success = 0;
-  int status;
-
-  for (i = 0; i < databases_num; i++)
-  {
-    status = cdbi_read_database (databases[i]);
-    if (status == 0)
-      success++;
-  }
-
-  if (success == 0)
-  {
-    ERROR ("dbi plugin: No database could be read. Will return an error so "
-        "the plugin will be delayed.");
-    return (-1);
-  }
-
-  return (0);
-} /* }}} int cdbi_read */
-
 static int cdbi_shutdown (void) /* {{{ */
 {
   size_t i;
@@ -848,7 +843,6 @@ void module_register (void) /* {{{ */
 {
   plugin_register_complex_config ("dbi", cdbi_config);
   plugin_register_init ("dbi", cdbi_init);
-  plugin_register_read ("dbi", cdbi_read);
   plugin_register_shutdown ("dbi", cdbi_shutdown);
 } /* }}} void module_register */
 
index 5ff3f59..3d5a402 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -55,7 +55,8 @@ static const char *config_keys[] =
        "ReportByDevice",
        "ReportReserved",
        "ReportInodes",
-       "ReportPercentage"
+       "ValuesAbsolute",
+       "ValuesPercentage"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -65,7 +66,8 @@ static ignorelist_t *il_fstype = NULL;
 
 static _Bool by_device = 0;
 static _Bool report_inodes = 0;
-static _Bool report_percentage = 0;
+static _Bool values_absolute = 1;
+static _Bool values_percentage = 0;
 
 static int df_init (void)
 {
@@ -133,13 +135,21 @@ static int df_config (const char *key, const char *value)
 
                return (0);
        }
+       else if (strcasecmp (key, "ValuesAbsolute") == 0)
+       {
+               if (IS_TRUE (value))
+                       values_absolute = 1;
+               else
+                       values_absolute = 0;
 
-       else if (strcasecmp (key, "ReportPercentage") == 0)
+               return (0);
+       }
+       else if (strcasecmp (key, "ValuesPercentage") == 0)
        {
                if (IS_TRUE (value))
-                       report_percentage = 1;
+                       values_percentage = 1;
                else
-                       report_percentage = 0;
+                       values_percentage = 0;
 
                return (0);
        }
@@ -231,7 +241,7 @@ static int df_read (void)
 
                        if (strlen(disk_name) < 1)
                        {
-                               DEBUG("df: no device name name for mountpoint %s, skipping", mnt_ptr->dir);
+                               DEBUG("df: no device name for mountpoint %s, skipping", mnt_ptr->dir);
                                continue;
                        }
                }
@@ -285,22 +295,7 @@ static int df_read (void)
                blk_reserved = (uint64_t) (statbuf.f_bfree - statbuf.f_bavail);
                blk_used     = (uint64_t) (statbuf.f_blocks - statbuf.f_bfree);
 
-               if (report_percentage && (statbuf.f_blocks > 0))
-               {
-                       uint64_t blk_total = (uint64_t) statbuf.f_blocks;
-                       char plugin_instance[DATA_MAX_NAME_LEN];
-
-                       ssnprintf (plugin_instance, sizeof (plugin_instance),
-                                       "%s-bytes", disk_name);
-
-                       df_submit_one (plugin_instance, "percent", "free",
-                                       100.0 * ((gauge_t) blk_free) / ((gauge_t) blk_total));
-                       df_submit_one (plugin_instance, "percent", "reserved",
-                                       100.0 * ((gauge_t) blk_reserved) / ((gauge_t) blk_total));
-                       df_submit_one (plugin_instance, "percent", "used",
-                                       100.0 * ((gauge_t) blk_used) / ((gauge_t) blk_total));
-               }
-               else if (!report_percentage)
+               if (values_absolute)
                {
                        df_submit_one (disk_name, "df_complex", "free",
                                (gauge_t) (blk_free * blocksize));
@@ -310,6 +305,20 @@ static int df_read (void)
                                (gauge_t) (blk_used * blocksize));
                }
 
+               if (values_percentage)
+               {
+                       if (statbuf.f_blocks > 0)
+                               {
+                               df_submit_one (disk_name, "percent_bytes", "free",
+                                       (gauge_t) ((float_t)(blk_free) / statbuf.f_blocks * 100));
+                               df_submit_one (disk_name, "percent_bytes", "reserved",
+                                       (gauge_t) ((float_t)(blk_reserved) / statbuf.f_blocks * 100));
+                               df_submit_one (disk_name, "percent_bytes", "used",
+                                       (gauge_t) ((float_t)(blk_used) / statbuf.f_blocks * 100));
+                               }
+                       else return (-1);
+               }
+
                /* inode handling */
                if (report_inodes)
                {
@@ -327,22 +336,20 @@ static int df_read (void)
                        inode_reserved = (uint64_t) (statbuf.f_ffree - statbuf.f_favail);
                        inode_used = (uint64_t) (statbuf.f_files - statbuf.f_ffree);
 
-                       if (report_percentage && (statbuf.f_files > 0))
+                       if (values_percentage)
                        {
-                               uint64_t inode_total = (uint64_t) statbuf.f_files;
-                               char plugin_instance[DATA_MAX_NAME_LEN];
-
-                               ssnprintf (plugin_instance, sizeof (plugin_instance),
-                                               "%s-inodes", disk_name);
-
-                               df_submit_one (plugin_instance, "percent", "free",
-                                               100.0 * ((gauge_t) inode_free) / ((gauge_t) inode_total));
-                               df_submit_one (plugin_instance, "percent", "reserved",
-                                               100.0 * ((gauge_t) inode_reserved) / ((gauge_t) inode_total));
-                               df_submit_one (plugin_instance, "percent", "used",
-                                               100.0 * ((gauge_t) inode_used) / ((gauge_t) inode_total));
+                               if (statbuf.f_files > 0)
+                               {
+                                       df_submit_one (disk_name, "percent_inodes", "free",
+                                               (gauge_t) ((float_t)(inode_free) / statbuf.f_files * 100));
+                                       df_submit_one (disk_name, "percent_inodes", "reserved",
+                                               (gauge_t) ((float_t)(inode_reserved) / statbuf.f_files * 100));
+                                       df_submit_one (disk_name, "percent_inodes", "used",
+                                               (gauge_t) ((float_t)(inode_used) / statbuf.f_files * 100));
+                               }
+                               else return (-1);
                        }
-                       else if (!report_percentage)
+                       if (values_absolute)
                        {
                                df_submit_one (disk_name, "df_inodes", "free",
                                                (gauge_t) inode_free);
index fe3b672..00fcff1 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -208,7 +208,7 @@ static void dns_child_callback (const rfc1035_header_t *dns)
        pthread_mutex_unlock (&opcode_mutex);
 }
 
-static void *dns_child_loop (__attribute__((unused)) void *dummy)
+static int dns_run_pcap_loop (void)
 {
        pcap_t *pcap_obj;
        char    pcap_error[PCAP_ERRBUF_SIZE];
@@ -236,19 +236,24 @@ static void *dns_child_loop (__attribute__((unused)) void *dummy)
                                "failed: %s",
                                (pcap_device != NULL) ? pcap_device : "any",
                                pcap_error);
-               return (NULL);
+               return (PCAP_ERROR);
        }
 
        memset (&fp, 0, sizeof (fp));
-       if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
+       status = pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0);
+       if (status < 0)
        {
-               ERROR ("dns plugin: pcap_compile failed");
-               return (NULL);
+               ERROR ("dns plugin: pcap_compile failed: %s",
+                               pcap_statustostr (status));
+               return (status);
        }
-       if (pcap_setfilter (pcap_obj, &fp) < 0)
+
+       status = pcap_setfilter (pcap_obj, &fp);
+       if (status < 0)
        {
-               ERROR ("dns plugin: pcap_setfilter failed");
-               return (NULL);
+               ERROR ("dns plugin: pcap_setfilter failed: %s",
+                               pcap_statustostr (status));
+               return (status);
        }
 
        DEBUG ("dns plugin: PCAP object created.");
@@ -259,19 +264,65 @@ static void *dns_child_loop (__attribute__((unused)) void *dummy)
        status = pcap_loop (pcap_obj,
                        -1 /* loop forever */,
                        handle_pcap /* callback */,
-                       NULL /* Whatever this means.. */);
-       if (status < 0)
-               ERROR ("dns plugin: Listener thread is exiting "
-                               "abnormally: %s", pcap_geterr (pcap_obj));
-
-       DEBUG ("dns plugin: Child is exiting.");
+                       NULL /* user data */);
+       INFO ("dns plugin: pcap_loop exited with status %i.", status);
+       /* We need to handle "PCAP_ERROR" specially because libpcap currently
+        * doesn't return PCAP_ERROR_IFACE_NOT_UP for compatibility reasons. */
+       if (status == PCAP_ERROR)
+               status = PCAP_ERROR_IFACE_NOT_UP;
 
        pcap_close (pcap_obj);
-       listen_thread_init = 0;
-       pthread_exit (NULL);
+       return (status);
+} /* int dns_run_pcap_loop */
+
+static int dns_sleep_one_interval (void) /* {{{ */
+{
+       cdtime_t interval;
+       struct timespec ts = { 0, 0 };
+       int status = 0;
+
+       interval = plugin_get_interval ();
+       CDTIME_T_TO_TIMESPEC (interval, &ts);
+
+       while (42)
+       {
+               struct timespec rem = { 0, 0 };
+
+               status = nanosleep (&ts, &rem);
+               if (status == 0)
+                       break;
+               else if ((errno == EINTR) || (errno == EAGAIN))
+               {
+                       ts = rem;
+                       continue;
+               }
+               else
+                       break;
+       }
+
+       return (status);
+} /* }}} int dns_sleep_one_interval */
+
+static void *dns_child_loop (__attribute__((unused)) void *dummy) /* {{{ */
+{
+       int status;
+
+       while (42)
+       {
+               status = dns_run_pcap_loop ();
+               if (status != PCAP_ERROR_IFACE_NOT_UP)
+                       break;
 
+               dns_sleep_one_interval ();
+       }
+
+       if (status != PCAP_ERROR_BREAK)
+               ERROR ("dns plugin: PCAP returned error %s.",
+                               pcap_statustostr (status));
+
+       listen_thread_init = 0;
        return (NULL);
-} /* static void dns_child_loop (void) */
+} /* }}} void *dns_child_loop */
 
 static int dns_init (void)
 {
index fbd9c26..cfd82a3 100644 (file)
@@ -287,71 +287,11 @@ static void set_environment (void) /* {{{ */
 } /* }}} void set_environment */
 
 __attribute__((noreturn))
-static void exec_child (program_list_t *pl) /* {{{ */
+static void exec_child (program_list_t *pl, int uid, int gid, int egid) /* {{{ */
 {
   int status;
-  int uid;
-  int gid;
-  int egid;
-
-  struct passwd *sp_ptr;
-  struct passwd sp;
-  char nambuf[2048];
   char errbuf[1024];
 
-  sp_ptr = NULL;
-  status = getpwnam_r (pl->user, &sp, nambuf, sizeof (nambuf), &sp_ptr);
-  if (status != 0)
-  {
-    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)
-  {
-    ERROR ("exec plugin: No such user: `%s'", pl->user);
-    exit (-1);
-  }
-
-  uid = sp.pw_uid;
-  gid = sp.pw_gid;
-  if (uid == 0)
-  {
-    ERROR ("exec plugin: Cowardly refusing to exec program as root.");
-    exit (-1);
-  }
-
-  /* The group configured in the configfile is set as effective group, because
-   * this way the forked process can (re-)gain the user's primary group. */
-  egid = -1;
-  if (NULL != pl->group)
-  {
-    if ('\0' != *pl->group) {
-      struct group *gr_ptr = NULL;
-      struct group gr;
-
-      status = getgrnam_r (pl->group, &gr, nambuf, sizeof (nambuf), &gr_ptr);
-      if (0 != status)
-      {
-        ERROR ("exec plugin: Failed to get group information "
-            "for group ``%s'': %s", pl->group,
-            sstrerror (errno, errbuf, sizeof (errbuf)));
-        exit (-1);
-      }
-      if (NULL == gr_ptr)
-      {
-        ERROR ("exec plugin: No such group: `%s'", pl->group);
-        exit (-1);
-      }
-
-      egid = gr.gr_gid;
-    }
-    else
-    {
-      egid = gid;
-    }
-  } /* if (pl->group == NULL) */
-
 #if HAVE_SETGROUPS
   if (getuid () == 0)
   {
@@ -429,6 +369,14 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
   int status;
   int pid;
 
+  int uid;
+  int gid;
+  int egid;
+
+  struct passwd *sp_ptr;
+  struct passwd sp;
+  char nambuf[2048];
+
   if (pl->pid != 0)
     return (-1);
 
@@ -456,6 +404,59 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
     return (-1);
   }
 
+  sp_ptr = NULL;
+  status = getpwnam_r (pl->user, &sp, nambuf, sizeof (nambuf), &sp_ptr);
+  if (status != 0)
+  {
+    ERROR ("exec plugin: Failed to get user information for user ``%s'': %s",
+        pl->user, sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+  if (sp_ptr == NULL)
+  {
+    ERROR ("exec plugin: No such user: `%s'", pl->user);
+    return (-1);
+  }
+
+  uid = sp.pw_uid;
+  gid = sp.pw_gid;
+  if (uid == 0)
+  {
+    ERROR ("exec plugin: Cowardly refusing to exec program as root.");
+    return (-1);
+  }
+
+  /* The group configured in the configfile is set as effective group, because
+   * this way the forked process can (re-)gain the user's primary group. */
+  egid = -1;
+  if (NULL != pl->group)
+  {
+    if ('\0' != *pl->group) {
+      struct group *gr_ptr = NULL;
+      struct group gr;
+
+      status = getgrnam_r (pl->group, &gr, nambuf, sizeof (nambuf), &gr_ptr);
+      if (0 != status)
+      {
+        ERROR ("exec plugin: Failed to get group information "
+            "for group ``%s'': %s", pl->group,
+            sstrerror (errno, errbuf, sizeof (errbuf)));
+        return (-1);
+      }
+      if (NULL == gr_ptr)
+      {
+        ERROR ("exec plugin: No such group: `%s'", pl->group);
+        return (-1);
+      }
+
+      egid = gr.gr_gid;
+    }
+    else
+    {
+      egid = gid;
+    }
+  } /* if (pl->group == NULL) */
+
   pid = fork ();
   if (pid < 0)
   {
@@ -505,7 +506,7 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
     /* Unblock all signals */
     reset_signal_mask ();
 
-    exec_child (pl);
+    exec_child (pl, uid, gid, egid);
     /* does not return */
   }
 
index 7d8369b..f5606e9 100644 (file)
@@ -435,9 +435,10 @@ static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */
 
 static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
 {
-  fc_chain_t *chain;
+  fc_chain_t *chain = NULL;
   int status = 0;
   int i;
+  int new_chain = 1;
 
   if ((ci->values_num != 1)
       || (ci->values[0].type != OCONFIG_TYPE_STRING))
@@ -447,17 +448,26 @@ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
-  chain = (fc_chain_t *) malloc (sizeof (*chain));
+  if (chain_list_head != NULL)
+  {
+    if ((chain = fc_chain_get_by_name (ci->values[0].value.string)) != NULL)
+      new_chain = 0;
+  }
+
   if (chain == NULL)
   {
-    ERROR ("fc_config_add_chain: malloc failed.");
-    return (-1);
+    chain = (fc_chain_t *) malloc (sizeof (*chain));
+    if (chain == NULL)
+    {
+      ERROR ("fc_config_add_chain: malloc failed.");
+      return (-1);
+    }
+    memset (chain, 0, sizeof (*chain));
+    sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name));
+    chain->rules = NULL;
+    chain->targets = NULL;
+    chain->next = NULL;
   }
-  memset (chain, 0, sizeof (*chain));
-  sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name));
-  chain->rules = NULL;
-  chain->targets = NULL;
-  chain->next = NULL;
 
   for (i = 0; i < ci->children_num; i++)
   {
@@ -487,6 +497,9 @@ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
 
   if (chain_list_head != NULL)
   {
+    if (!new_chain)
+      return (0);
+
     fc_chain_t *ptr;
 
     ptr = chain_list_head;
index 3962666..049f7f0 100644 (file)
@@ -65,6 +65,7 @@ int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv);
 
 /* Configure servers */
 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl);
+int lcc_server_set_interface (lcc_server_t *srv, char const *interface);
 int lcc_server_set_security_level (lcc_server_t *srv,
     lcc_security_level_t level,
     const char *username, const char *password);
index 6b6450c..ddb3b5b 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/libcollectdclient/network.c
- * Copyright (C) 2005-2012  Florian octo Forster
+ * Copyright (C) 2005-2013  Florian Forster
+ * Copyright (C) 2010       Max Henkel
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -21,7 +22,8 @@
  * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
- *   Florian octo Forster <octo at collectd.org>
+ *   Florian Forster <octo at collectd.org>
+ *   Max Henkel <henkel at gmx.at>
  **/
 
 #include "collectd.h"
 # include <netinet/in.h>
 #endif
 
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+
 #include "collectd/network.h"
 #include "collectd/network_buffer.h"
 
@@ -379,6 +385,81 @@ int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
   return (0);
 } /* }}} int lcc_server_set_ttl */
 
+int lcc_server_set_interface (lcc_server_t *srv, char const *interface) /* {{{ */
+{
+  int if_index;
+  int status;
+
+  if ((srv == NULL) || (interface == NULL))
+    return (EINVAL);
+
+  if_index = if_nametoindex (interface);
+  if (if_index == 0)
+    return (ENOENT);
+
+  /* IPv4 multicast */
+  if (srv->sa->sa_family == AF_INET)
+  {
+    struct sockaddr_in *addr = (struct sockaddr_in *) srv->sa;
+
+    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 = if_index;
+#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
+
+      status = setsockopt (srv->fd, IPPROTO_IP, IP_MULTICAST_IF,
+          &mreq, sizeof (mreq));
+      if (status != 0)
+        return (status);
+
+      return (0);
+    }
+  }
+
+  /* IPv6 multicast */
+  if (srv->sa->sa_family == AF_INET6)
+  {
+    struct sockaddr_in6 *addr = (struct sockaddr_in6 *) srv->sa;
+
+    if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+    {
+      status = setsockopt (srv->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+          &if_index, sizeof (if_index));
+      if (status != 0)
+        return (status);
+
+      return (0);
+    }
+  }
+
+  /* else: Not a multicast interface. */
+#if defined(SO_BINDTODEVICE)
+  status = setsockopt (srv->fd, SOL_SOCKET, SO_BINDTODEVICE,
+      interface, strlen (interface) + 1);
+  if (status != 0)
+    return (-1);
+#endif
+
+  return (0);
+} /* }}} int lcc_server_set_interface */
+
 int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
     lcc_security_level_t level,
     const char *username, const char *password)
index a09f45e..d739203 100644 (file)
@@ -84,6 +84,15 @@ static int memcached_connect_unix (memcached_t *st)
     return (-1);
   }
 
+  /* connect to the memcached daemon */
+  int status = connect (fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
+  if (status != 0)
+  {
+      shutdown (fd, SHUT_RDWR);
+      close (fd);
+      fd = -1;
+  }
+
   return (fd);
 } /* int memcached_connect_unix */
 
@@ -340,6 +349,10 @@ static int memcached_read (user_data_t *user_data)
   gauge_t bytes_total = NAN;
   gauge_t hits = NAN;
   gauge_t gets = NAN;
+  gauge_t incr_hits = NAN;
+  derive_t incr = 0;
+  gauge_t decr_hits = NAN;
+  derive_t decr = 0;
   derive_t rusage_user = 0;
   derive_t rusage_syst = 0;
   derive_t octets_rx = 0;
@@ -437,6 +450,36 @@ static int memcached_read (user_data_t *user_data)
     }
 
     /*
+     * Increment/Decrement
+     */
+    else if (FIELD_IS("incr_misses"))
+    {
+      derive_t incr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "incr_misses", incr_count, st);
+      incr += incr_count;
+    }
+    else if (FIELD_IS ("incr_hits"))
+    {
+      derive_t incr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "incr_hits", incr_count, st);
+      incr_hits = atof (fields[2]);
+      incr += incr_count;
+    }
+    else if (FIELD_IS ("decr_misses"))
+    {
+      derive_t decr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "decr_misses", decr_count, st);
+      decr += decr_count;
+    }
+    else if (FIELD_IS ("decr_hits"))
+    {
+      derive_t decr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "decr_hits", decr_count, st);
+      decr_hits = atof (fields[2]);
+      decr += decr_count;
+    }
+
+    /*
      * Operations on the cache, i. e. cache hits, cache misses and evictions of items
      */
     else if (FIELD_IS ("get_hits"))
@@ -485,6 +528,20 @@ static int memcached_read (user_data_t *user_data)
     submit_gauge ("percent", "hitratio", rate, st);
   }
 
+  if (!isnan (incr_hits) && incr != 0)
+  {
+    gauge_t incr_rate = 100.0 * incr_hits / incr;
+    submit_gauge ("percent", "incr_hitratio", incr_rate, st);
+    submit_derive ("memcached_ops", "incr", incr, st);
+  }
+
+  if (!isnan (decr_hits) && decr != 0)
+  {
+    gauge_t decr_rate = 100.0 * decr_hits / decr;
+    submit_gauge ("percent", "decr_hitratio", decr_rate, st);
+    submit_derive ("memcached_ops", "decr", decr, st);
+  }
+
   return 0;
 } /* int memcached_read */
 
index 8a9fe93..887c63c 100644 (file)
@@ -115,7 +115,6 @@ struct mb_host_s /* {{{ */
   modbus_t *connection;
 #endif
   _Bool is_connected;
-  _Bool have_reconnected;
 }; /* }}} */
 typedef struct mb_host_s mb_host_t;
 
@@ -260,6 +259,7 @@ static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */
   union
   {
     uint8_t b[4];
+    uint16_t s[2];
     float f;
   } conv;
 
@@ -288,13 +288,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   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);
 
   /* We'll do the error handling ourselves. */
@@ -319,7 +312,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   }
 
   host->is_connected = 1;
-  host->have_reconnected = 1;
   return (0);
 } /* }}} int mb_init_connection */
 /* #endif LEGACY_LIBMODBUS */
@@ -336,10 +328,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   if (host->connection != NULL)
     return (0);
 
-  /* Only reconnect once per interval. */
-  if (host->have_reconnected)
-    return (-1);
-
   if ((host->port < 1) || (host->port > 65535))
     host->port = MODBUS_TCP_DEFAULT_PORT;
 
@@ -349,7 +337,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   host->connection = modbus_new_tcp (host->node, host->port);
   if (host->connection == NULL)
   {
-    host->have_reconnected = 1;
     ERROR ("Modbus plugin: Creating new Modbus/TCP object failed.");
     return (-1);
   }
@@ -369,7 +356,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
     return (status);
   }
 
-  host->have_reconnected = 1;
   return (0);
 } /* }}} int mb_init_connection */
 #endif /* !LEGACY_LIBMODBUS */
@@ -392,7 +378,6 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   int values_num;
   const data_set_t *ds;
   int status;
-  int i;
 
   if ((host == NULL) || (slave == NULL) || (data == NULL))
     return (EINVAL);
@@ -429,6 +414,44 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   else
     values_num = 1;
 
+  status = 0;
+  if (host->connection == NULL)
+  {
+    status = EBADF;
+  }
+  else
+  {
+    struct sockaddr sockaddr;
+    socklen_t saddrlen = sizeof (sockaddr);
+
+    status = getpeername (modbus_get_socket (host->connection),
+        &sockaddr, &saddrlen);
+    if (status != 0)
+      status = errno;
+  }
+
+  if ((status == EBADF) || (status == ENOTSOCK) || (status == ENOTCONN))
+  {
+    status = mb_init_connection (host);
+    if (status != 0)
+    {
+      ERROR ("Modbus plugin: mb_init_connection (%s/%s) failed. ",
+          host->host, host->node);
+      host->is_connected = 0;
+      host->connection = NULL;
+      return (-1);
+    }
+  }
+  else if (status != 0)
+  {
+#if LEGACY_LIBMODBUS
+    modbus_close (&host->connection);
+#else
+    modbus_close (host->connection);
+    modbus_free (host->connection);
+#endif
+  }
 #if LEGACY_LIBMODBUS
   /* Version 2.0.3: Pass the connection struct as a pointer and pass the slave
    * id to each call of "read_holding_registers". */
@@ -445,51 +468,22 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 #endif
 
-  for (i = 0; i < 2; i++)
-  {
-    status = modbus_read_registers (host->connection,
+  status = modbus_read_registers (host->connection,
         /* start_addr = */ data->register_base,
         /* num_registers = */ values_num, /* buffer = */ values);
-    if (status > 0)
-      break;
-
-    if (host->is_connected)
-    {
+  if (status != values_num)
+  {
+    ERROR ("Modbus plugin: modbus_read_registers (%s/%s) failed. status = %i, values_num = %i "
+        "Giving up.", host->host, host->node, status, values_num);
 #if LEGACY_LIBMODBUS
-      modbus_close (&host->connection);
-      host->is_connected = 0;
+    modbus_close (&host->connection);
 #else
-      modbus_close (host->connection);
-      modbus_free (host->connection);
-      host->connection = NULL;
+    modbus_close (host->connection);
+    modbus_free (host->connection);
 #endif
-    }
-
-    /* If we already tried reconnecting this round, give up. */
-    if (host->have_reconnected)
-    {
-      ERROR ("Modbus plugin: modbus_read_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: modbus_read_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) */
+    host->connection = NULL;
+    return (-1);
+  }
 
   DEBUG ("Modbus plugin: mb_read_data: Success! "
       "modbus_read_registers returned with status %i.", status);
@@ -602,9 +596,6 @@ static int mb_read (user_data_t *user_data) /* {{{ */
 
   host = user_data->data;
 
-  /* Clear the reconnect flag. */
-  host->have_reconnected = 0;
-
   success = 0;
   for (i = 0; i < host->slaves_num; i++)
   {
index d2d0b4b..6337ab4 100644 (file)
@@ -287,6 +287,7 @@ static void set_host (mysql_database_t *db, char *buf, size_t buflen)
 {
        if ((db->host == NULL)
                        || (strcmp ("", db->host) == 0)
+                       || (strcmp ("127.0.0.1", db->host) == 0)
                        || (strcmp ("localhost", db->host) == 0))
                sstrncpy (buf, hostname_g, buflen);
        else
@@ -534,7 +535,7 @@ static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con)
                        ssnprintf (n.message, sizeof (n.message),
                                        "slave SQL thread started");
                        plugin_dispatch_notification (&n);
-                       db->slave_sql_running = 0;
+                       db->slave_sql_running = 1;
                }
        }
 
index 39536d9..5e670d4 100644 (file)
@@ -1,6 +1,9 @@
 /**
  * collectd - src/netlink.c
  * Copyright (C) 2007-2010  Florian octo Forster
+ * Copyright (C) 2008-2012  Sebastian Harl
+ * Copyright (C) 2013       Andreas Henriksson
+ * Copyright (C) 2013       Marc Fournier
  *
  * 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
@@ -17,6 +20,9 @@
  *
  * Authors:
  *   Florian octo Forster <octo at collectd.org>
+ *   Sebastian Harl <sh at tokkee.org>
+ *   Andreas Henriksson <andreas at fatal.se>
+ *   Marc Fournier <marc.fournier at camptocamp.com>
  **/
 
 #include "collectd.h"
 # include <linux/pkt_sched.h>
 #endif
 
-#if HAVE_LIBNETLINK_H
-# include <libnetlink.h>
-#elif HAVE_IPROUTE_LIBNETLINK_H
-# include <iproute/libnetlink.h>
-#elif HAVE_LINUX_LIBNETLINK_H
-# include <linux/libnetlink.h>
+#include <libmnl/libmnl.h>
+
+struct ir_link_stats_storage_s {
+
+  uint64_t rx_packets;
+  uint64_t tx_packets;
+  uint64_t rx_bytes;
+  uint64_t tx_bytes;
+  uint64_t rx_errors;
+  uint64_t tx_errors;
+
+  uint64_t rx_dropped;
+  uint64_t tx_dropped;
+  uint64_t multicast;
+  uint64_t collisions;
+
+  uint64_t rx_length_errors;
+  uint64_t rx_over_errors;
+  uint64_t rx_crc_errors;
+  uint64_t rx_frame_errors;
+  uint64_t rx_fifo_errors;
+  uint64_t rx_missed_errors;
+
+  uint64_t tx_aborted_errors;
+  uint64_t tx_carrier_errors;
+  uint64_t tx_fifo_errors;
+  uint64_t tx_heartbeat_errors;
+  uint64_t tx_window_errors;
+};
+
+union ir_link_stats_u {
+  struct rtnl_link_stats *stats32;
+#ifdef HAVE_RTNL_LINK_STATS64
+  struct rtnl_link_stats64 *stats64;
 #endif
+};
 
 typedef struct ir_ignorelist_s
 {
@@ -54,19 +89,19 @@ typedef struct ir_ignorelist_s
 static int ir_ignorelist_invert = 1;
 static ir_ignorelist_t *ir_ignorelist_head = NULL;
 
-static struct rtnl_handle rth;
+static struct mnl_socket *nl;
 
 static char **iflist = NULL;
 static size_t iflist_len = 0;
 
 static const char *config_keys[] =
 {
-       "Interface",
-       "VerboseInterface",
-       "QDisc",
-       "Class",
-       "Filter",
-       "IgnoreSelected"
+        "Interface",
+        "VerboseInterface",
+        "QDisc",
+        "Class",
+        "Filter",
+        "IgnoreSelected"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -117,7 +152,7 @@ static int add_ignorelist (const char *dev, const char *type,
   return (0);
 } /* int add_ignorelist */
 
-/* 
+/*
  * Checks wether a data set should be ignored. Returns `true' is the value
  * should be ignored, `false' otherwise.
  */
@@ -135,24 +170,24 @@ static int check_ignorelist (const char *dev,
   {
     /* i->device == NULL  =>  match all devices */
     if ((i->device != NULL)
-       && (strcasecmp (i->device, dev) != 0))
+        && (strcasecmp (i->device, dev) != 0))
       continue;
 
     if (strcasecmp (i->type, type) != 0)
       continue;
 
     if ((i->inst != NULL) && (type_instance != NULL)
-       && (strcasecmp (i->inst, type_instance) != 0))
+        && (strcasecmp (i->inst, type_instance) != 0))
       continue;
 
     DEBUG ("netlink plugin: check_ignorelist: "
-       "(dev = %s; type = %s; inst = %s) matched "
-       "(dev = %s; type = %s; inst = %s)",
-       dev, type,
-       type_instance == NULL ? "(nil)" : type_instance,
-       i->device == NULL ? "(nil)" : i->device,
-       i->type,
-       i->inst == NULL ? "(nil)" : i->inst);
+        "(dev = %s; type = %s; inst = %s) matched "
+        "(dev = %s; type = %s; inst = %s)",
+        dev, type,
+        type_instance == NULL ? "(nil)" : type_instance,
+        i->device == NULL ? "(nil)" : i->device,
+        i->type,
+        i->inst == NULL ? "(nil)" : i->inst);
 
     return (ir_ignorelist_invert ? 0 : 1);
   } /* for i */
@@ -204,46 +239,8 @@ static void submit_two (const char *dev, const char *type,
   plugin_dispatch_values (&vl);
 } /* void submit_two */
 
-static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
-    struct nlmsghdr *nmh, void __attribute__((unused)) *args)
+static int update_iflist (struct ifinfomsg *msg, const char *dev)
 {
-  struct ifinfomsg *msg;
-  int msg_len;
-  struct rtattr *attrs[IFLA_MAX + 1];
-  struct rtnl_link_stats *stats;
-
-  const char *dev;
-
-  if (nmh->nlmsg_type != RTM_NEWLINK)
-  {
-    ERROR ("netlink plugin: link_filter: Don't know how to handle type %i.",
-       nmh->nlmsg_type);
-    return (-1);
-  }
-
-  msg = NLMSG_DATA (nmh);
-
-  msg_len = nmh->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifinfomsg));
-  if (msg_len < 0)
-  {
-    ERROR ("netlink plugin: link_filter: msg_len = %i < 0;", msg_len);
-    return (-1);
-  }
-
-  memset (attrs, '\0', sizeof (attrs));
-  if (parse_rtattr (attrs, IFLA_MAX, IFLA_RTA (msg), msg_len) != 0)
-  {
-    ERROR ("netlink plugin: link_filter: parse_rtattr failed.");
-    return (-1);
-  }
-
-  if (attrs[IFLA_IFNAME] == NULL)
-  {
-    ERROR ("netlink plugin: link_filter: attrs[IFLA_IFNAME] == NULL");
-    return (-1);
-  }
-  dev = RTA_DATA (attrs[IFLA_IFNAME]);
-
   /* Update the `iflist'. It's used to know which interfaces exist and query
    * them later for qdiscs and classes. */
   if ((msg->ifi_index >= 0) && ((size_t) msg->ifi_index >= iflist_len))
@@ -253,12 +250,12 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
     temp = (char **) realloc (iflist, (msg->ifi_index + 1) * sizeof (char *));
     if (temp == NULL)
     {
-      ERROR ("netlink plugin: link_filter: realloc failed.");
+      ERROR ("netlink plugin: update_iflist: realloc failed.");
       return (-1);
     }
 
     memset (temp + iflist_len, '\0',
-       (msg->ifi_index + 1 - iflist_len) * sizeof (char *));
+        (msg->ifi_index + 1 - iflist_len) * sizeof (char *));
     iflist = temp;
     iflist_len = msg->ifi_index + 1;
   }
@@ -269,12 +266,12 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
     iflist[msg->ifi_index] = strdup (dev);
   }
 
-  if (attrs[IFLA_STATS] == NULL)
-  {
-    DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev);
-    return (0);
-  }
-  stats = RTA_DATA (attrs[IFLA_STATS]);
+  return (0);
+} /* int update_iflist */
+
+static void check_ignorelist_and_submit (const char *dev,
+    struct ir_link_stats_storage_s *stats)
+{
 
   if (check_ignorelist (dev, "interface", NULL) == 0)
   {
@@ -311,158 +308,323 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
     DEBUG ("netlink plugin: Ignoring %s/if_detail.", dev);
   }
 
-  return (0);
-} /* int link_filter */
+} /* void check_ignorelist_and_submit */
+
+#define COPY_RTNL_LINK_VALUE (dst_stats, src_stats, value_name) \
+  (dst_stats)->value_name = (src_stats)->value_name
+
+#define COPY_RTNL_LINK_STATS (dst_stats, src_stats) \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_packets); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_packets); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_bytes); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_bytes); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_dropped); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_dropped); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, multicast); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, collisions); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_length_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_over_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_crc_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_frame_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_fifo_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_missed_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_aborted_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_carrier_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_fifo_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_heartbeat_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_window_errors)
+
+#ifdef HAVE_RTNL_LINK_STATS64
+static void check_ignorelist_and_submit64 (const char *dev,
+    struct rtnl_link_stats64 *stats)
+{
+  struct ir_link_stats_storage_s s;
+
+  COPY_RTNL_LINK_STATS (&s, stats);
+
+  check_ignorelist_and_submit (dev, &s);
+}
+#endif
+
+static void check_ignorelist_and_submit32 (const char *dev,
+    struct rtnl_link_stats *stats)
+{
+  struct ir_link_stats_storage_s s;
+
+  COPY_RTNL_LINK_STATS(&s, stats);
+
+  check_ignorelist_and_submit (dev, &s);
+}
+
+static int link_filter_cb (const struct nlmsghdr *nlh,
+    void *args __attribute__((unused)))
+{
+  struct ifinfomsg *ifm = mnl_nlmsg_get_payload (nlh);
+  struct nlattr *attr;
+  const char *dev = NULL;
+  union ir_link_stats_u stats;
+
+  if (nlh->nlmsg_type != RTM_NEWLINK)
+  {
+    ERROR ("netlink plugin: link_filter_cb: Don't know how to handle type %i.",
+        nlh->nlmsg_type);
+    return MNL_CB_ERROR;
+  }
+
+  /* Scan attribute list for device name. */
+  mnl_attr_for_each (attr, nlh, sizeof (*ifm))
+  {
+    if (mnl_attr_get_type (attr) != IFLA_IFNAME)
+      continue;
+
+    if (mnl_attr_validate (attr, MNL_TYPE_STRING) < 0)
+    {
+      ERROR ("netlink plugin: link_filter_cb: IFLA_IFNAME mnl_attr_validate failed.");
+      return MNL_CB_ERROR;
+    }
+
+    dev = mnl_attr_get_str (attr);
+    if (update_iflist (ifm, dev) < 0)
+      return MNL_CB_ERROR;
+    break;
+  }
+
+  if (dev == NULL)
+  {
+    ERROR ("netlink plugin: link_filter_cb: dev == NULL");
+    return MNL_CB_ERROR;
+  }
+#ifdef HAVE_RTNL_LINK_STATS64
+  mnl_attr_for_each (attr, nlh, sizeof (*ifm))
+  {
+    if (mnl_attr_get_type (attr) != IFLA_STATS64)
+      continue;
+
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*stats.stats64)) < 0)
+    {
+      ERROR ("netlink plugin: link_filter_cb: IFLA_STATS64 mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    stats.stats64 = mnl_attr_get_payload (attr);
+
+    check_ignorelist_and_submit64 (dev, stats.stats64);
+
+    return MNL_CB_OK;
+  }
+#endif
+  mnl_attr_for_each (attr, nlh, sizeof (*ifm))
+  {
+    if (mnl_attr_get_type (attr) != IFLA_STATS)
+      continue;
+
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*stats.stats32)) < 0)
+    {
+      ERROR ("netlink plugin: link_filter_cb: IFLA_STATS mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    stats.stats32 = mnl_attr_get_payload (attr);
+
+    check_ignorelist_and_submit32 (dev, stats.stats32);
+
+    return MNL_CB_OK;
+  }
+
+  DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev);
+  return MNL_CB_OK;
+
+} /* int link_filter_cb */
+
+#if HAVE_TCA_STATS2
+static int qos_attr_cb (const struct nlattr *attr, void *data)
+{
+  struct gnet_stats_basic **bs = (struct gnet_stats_basic **)data;
+
+  /* skip unsupported attribute in user-space */
+  if (mnl_attr_type_valid (attr, TCA_STATS_MAX) < 0)
+    return MNL_CB_OK;
+
+  if (mnl_attr_get_type (attr) == TCA_STATS_BASIC)
+  {
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (**bs)) < 0)
+    {
+      ERROR ("netlink plugin: qos_attr_cb: TCA_STATS_BASIC mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    *bs = mnl_attr_get_payload (attr);
+    return MNL_CB_STOP;
+  }
+
+  return MNL_CB_OK;
+} /* qos_attr_cb */
+#endif
 
-static int qos_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
-    struct nlmsghdr *nmh, void *args)
+static int qos_filter_cb (const struct nlmsghdr *nlh, void *args)
 {
-  struct tcmsg *msg;
-  int msg_len;
-  struct rtattr *attrs[TCA_MAX + 1];
+  struct tcmsg *tm = mnl_nlmsg_get_payload (nlh);
+  struct nlattr *attr;
 
   int wanted_ifindex = *((int *) args);
 
   const char *dev;
+  const char *kind = NULL;
 
   /* char *type_instance; */
   char *tc_type;
   char tc_inst[DATA_MAX_NAME_LEN];
 
-  if (nmh->nlmsg_type == RTM_NEWQDISC)
+  _Bool stats_submitted = 0;
+
+  if (nlh->nlmsg_type == RTM_NEWQDISC)
     tc_type = "qdisc";
-  else if (nmh->nlmsg_type == RTM_NEWTCLASS)
+  else if (nlh->nlmsg_type == RTM_NEWTCLASS)
     tc_type = "class";
-  else if (nmh->nlmsg_type == RTM_NEWTFILTER)
+  else if (nlh->nlmsg_type == RTM_NEWTFILTER)
     tc_type = "filter";
   else
   {
-    ERROR ("netlink plugin: qos_filter: Don't know how to handle type %i.",
-       nmh->nlmsg_type);
-    return (-1);
-  }
-
-  msg = NLMSG_DATA (nmh);
-
-  msg_len = nmh->nlmsg_len - sizeof (struct tcmsg);
-  if (msg_len < 0)
-  {
-    ERROR ("netlink plugin: qos_filter: msg_len = %i < 0;", msg_len);
-    return (-1);
+    ERROR ("netlink plugin: qos_filter_cb: Don't know how to handle type %i.",
+        nlh->nlmsg_type);
+    return MNL_CB_ERROR;
   }
 
-  if (msg->tcm_ifindex != wanted_ifindex)
+  if (tm->tcm_ifindex != wanted_ifindex)
   {
-    DEBUG ("netlink plugin: qos_filter: Got %s for interface #%i, "
-       "but expected #%i.",
-       tc_type, msg->tcm_ifindex, wanted_ifindex);
-    return (0);
+    DEBUG ("netlink plugin: qos_filter_cb: Got %s for interface #%i, "
+        "but expected #%i.",
+        tc_type, tm->tcm_ifindex, wanted_ifindex);
+    return MNL_CB_OK;
   }
 
-  if ((msg->tcm_ifindex >= 0)
-      && ((size_t) msg->tcm_ifindex >= iflist_len))
+  if ((tm->tcm_ifindex >= 0)
+      && ((size_t) tm->tcm_ifindex >= iflist_len))
   {
-    ERROR ("netlink plugin: qos_filter: msg->tcm_ifindex = %i "
-       ">= iflist_len = %zu",
-       msg->tcm_ifindex, iflist_len);
-    return (-1);
+    ERROR ("netlink plugin: qos_filter_cb: tm->tcm_ifindex = %i "
+        ">= iflist_len = %zu",
+        tm->tcm_ifindex, iflist_len);
+    return MNL_CB_ERROR;
   }
 
-  dev = iflist[msg->tcm_ifindex];
+  dev = iflist[tm->tcm_ifindex];
   if (dev == NULL)
   {
-    ERROR ("netlink plugin: qos_filter: iflist[%i] == NULL",
-       msg->tcm_ifindex);
-    return (-1);
+    ERROR ("netlink plugin: qos_filter_cb: iflist[%i] == NULL",
+        tm->tcm_ifindex);
+    return MNL_CB_ERROR;
   }
 
-  memset (attrs, '\0', sizeof (attrs));
-  if (parse_rtattr (attrs, TCA_MAX, TCA_RTA (msg), msg_len) != 0)
+  mnl_attr_for_each (attr, nlh, sizeof (*tm))
   {
-    ERROR ("netlink plugin: qos_filter: parse_rtattr failed.");
-    return (-1);
+    if (mnl_attr_get_type (attr) != TCA_KIND)
+      continue;
+
+    if (mnl_attr_validate (attr, MNL_TYPE_STRING) < 0)
+    {
+      ERROR ("netlink plugin: qos_filter_cb: TCA_KIND mnl_attr_validate failed.");
+      return MNL_CB_ERROR;
+    }
+
+    kind = mnl_attr_get_str (attr);
+    break;
   }
 
-  if (attrs[TCA_KIND] == NULL)
+  if (kind == NULL)
   {
-    ERROR ("netlink plugin: qos_filter: attrs[TCA_KIND] == NULL");
+    ERROR ("netlink plugin: qos_filter_cb: kind == NULL");
     return (-1);
   }
 
-  { /* The the ID */
+  { /* The ID */
     uint32_t numberic_id;
 
-    numberic_id = msg->tcm_handle;
+    numberic_id = tm->tcm_handle;
     if (strcmp (tc_type, "filter") == 0)
-      numberic_id = msg->tcm_parent;
+      numberic_id = tm->tcm_parent;
 
     ssnprintf (tc_inst, sizeof (tc_inst), "%s-%x:%x",
-       (const char *) RTA_DATA (attrs[TCA_KIND]),
-       numberic_id >> 16,
-       numberic_id & 0x0000FFFF);
+        kind,
+        numberic_id >> 16,
+        numberic_id & 0x0000FFFF);
   }
 
-  DEBUG ("netlink plugin: qos_filter: got %s for %s (%i).",
-      tc_type, dev, msg->tcm_ifindex);
-  
+  DEBUG ("netlink plugin: qos_filter_cb: got %s for %s (%i).",
+      tc_type, dev, tm->tcm_ifindex);
+
   if (check_ignorelist (dev, tc_type, tc_inst))
-    return (0);
+    return MNL_CB_OK;
 
 #if HAVE_TCA_STATS2
-  if (attrs[TCA_STATS2])
+  mnl_attr_for_each (attr, nlh, sizeof (*tm))
   {
-    struct rtattr *attrs_stats[TCA_STATS_MAX + 1];
+    struct gnet_stats_basic *bs = NULL;
 
-    memset (attrs_stats, '\0', sizeof (attrs_stats));
-    parse_rtattr_nested (attrs_stats, TCA_STATS_MAX, attrs[TCA_STATS2]);
+    if (mnl_attr_get_type (attr) != TCA_STATS2)
+      continue;
 
-    if (attrs_stats[TCA_STATS_BASIC])
+    if (mnl_attr_validate (attr, MNL_TYPE_NESTED) < 0)
+    {
+      ERROR ("netlink plugin: qos_filter_cb: TCA_STATS2 mnl_attr_validate failed.");
+      return MNL_CB_ERROR;
+    }
+
+    mnl_attr_parse_nested (attr, qos_attr_cb, &bs);
+
+    if (bs != NULL)
     {
-      struct gnet_stats_basic bs;
       char type_instance[DATA_MAX_NAME_LEN];
 
-      ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
-         tc_type, tc_inst);
+      stats_submitted = 1;
 
-      memset (&bs, '\0', sizeof (bs));
-      memcpy (&bs, RTA_DATA (attrs_stats[TCA_STATS_BASIC]),
-         MIN (RTA_PAYLOAD (attrs_stats[TCA_STATS_BASIC]), sizeof(bs)));
+      ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
+          tc_type, tc_inst);
 
-      submit_one (dev, "ipt_bytes", type_instance, bs.bytes);
-      submit_one (dev, "ipt_packets", type_instance, bs.packets);
+      submit_one (dev, "ipt_bytes", type_instance, bs->bytes);
+      submit_one (dev, "ipt_packets", type_instance, bs->packets);
     }
+
+    break;
   }
 #endif /* TCA_STATS2 */
-#if HAVE_TCA_STATS && HAVE_TCA_STATS2
-  else
-#endif
+
 #if HAVE_TCA_STATS
-  if (attrs[TCA_STATS] != NULL)
+  mnl_attr_for_each (attr, nlh, sizeof (*tm))
   {
-    struct tc_stats ts;
-    char type_instance[DATA_MAX_NAME_LEN];
+    struct tc_stats *ts = NULL;
+
+    if (mnl_attr_get_type (attr) != TCA_STATS)
+      continue;
+
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*ts)) < 0)
+    {
+      ERROR ("netlink plugin: qos_filter_cb: TCA_STATS mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    ts = mnl_attr_get_payload (attr);
 
-    ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
-       tc_type, tc_inst);
+    if (!stats_submitted && ts != NULL)
+    {
+      char type_instance[DATA_MAX_NAME_LEN];
 
-    memset(&ts, '\0', sizeof (ts));
-    memcpy(&ts, RTA_DATA (attrs[TCA_STATS]),
-       MIN (RTA_PAYLOAD (attrs[TCA_STATS]), sizeof (ts)));
+      ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
+          tc_type, tc_inst);
 
-    submit_one (dev, "ipt_bytes", type_instance, ts.bytes);
-    submit_one (dev, "ipt_packets", type_instance, ts.packets);
+      submit_one (dev, "ipt_bytes", type_instance, ts->bytes);
+      submit_one (dev, "ipt_packets", type_instance, ts->packets);
+    }
+
+    break;
   }
+
 #endif /* TCA_STATS */
-#if HAVE_TCA_STATS || HAVE_TCA_STATS2
-  else
+
+#if !(HAVE_TCA_STATS && HAVE_TCA_STATS2)
+  DEBUG ("netlink plugin: qos_filter_cb: Have neither TCA_STATS2 nor "
+      "TCA_STATS.");
 #endif
-  {
-    DEBUG ("netlink plugin: qos_filter: Have neither TCA_STATS2 nor "
-       "TCA_STATS.");
-  }
 
-  return (0);
-} /* int qos_filter */
+  return MNL_CB_OK;
+} /* int qos_filter_cb */
 
 static int ir_config (const char *key, const char *value)
 {
@@ -488,14 +650,14 @@ static int ir_config (const char *key, const char *value)
     if (fields_num != 1)
     {
       ERROR ("netlink plugin: Invalid number of fields for option "
-         "`%s'. Got %i, expected 1.", key, fields_num);
+          "`%s'. Got %i, expected 1.", key, fields_num);
       status = -1;
     }
     else
     {
       add_ignorelist (fields[0], "interface", NULL);
       if (strcasecmp (key, "VerboseInterface") == 0)
-       add_ignorelist (fields[0], "if_detail", NULL);
+        add_ignorelist (fields[0], "if_detail", NULL);
       status = 0;
     }
   }
@@ -506,13 +668,13 @@ static int ir_config (const char *key, const char *value)
     if ((fields_num < 1) || (fields_num > 2))
     {
       ERROR ("netlink plugin: Invalid number of fields for option "
-         "`%s'. Got %i, expected 1 or 2.", key, fields_num);
+          "`%s'. Got %i, expected 1 or 2.", key, fields_num);
       return (-1);
     }
     else
     {
       add_ignorelist (fields[0], key,
-         (fields_num == 2) ? fields[1] : NULL);
+          (fields_num == 2) ? fields[1] : NULL);
       status = 0;
     }
   }
@@ -521,15 +683,15 @@ static int ir_config (const char *key, const char *value)
     if (fields_num != 1)
     {
       ERROR ("netlink plugin: Invalid number of fields for option "
-         "`IgnoreSelected'. Got %i, expected 1.", fields_num);
+          "`IgnoreSelected'. Got %i, expected 1.", fields_num);
       status = -1;
     }
     else
     {
       if (IS_TRUE (fields[0]))
-       ir_ignorelist_invert = 0;
+        ir_ignorelist_invert = 0;
       else
-       ir_ignorelist_invert = 1;
+        ir_ignorelist_invert = 1;
       status = 0;
     }
   }
@@ -541,11 +703,16 @@ static int ir_config (const char *key, const char *value)
 
 static int ir_init (void)
 {
-  memset (&rth, '\0', sizeof (rth));
+  nl = mnl_socket_open (NETLINK_ROUTE);
+  if (nl == NULL)
+  {
+    ERROR ("netlink plugin: ir_init: mnl_socket_open failed.");
+    return (-1);
+  }
 
-  if (rtnl_open (&rth, 0) != 0)
+  if (mnl_socket_bind (nl, 0, MNL_SOCKET_AUTOPID) < 0)
   {
-    ERROR ("netlink plugin: ir_init: rtnl_open failed.");
+    ERROR ("netlink plugin: ir_init: mnl_socket_bind failed.");
     return (-1);
   }
 
@@ -554,35 +721,51 @@ static int ir_init (void)
 
 static int ir_read (void)
 {
-  struct tcmsg tm;
-  int ifindex;
+  char buf[MNL_SOCKET_BUFFER_SIZE];
+  struct nlmsghdr *nlh;
+  struct rtgenmsg *rt;
+  int ret;
+  unsigned int seq, portid;
+
+  size_t ifindex;
 
   static const int type_id[] = { RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER };
   static const char *type_name[] = { "qdisc", "class", "filter" };
 
-  if (rtnl_wilddump_request (&rth, AF_UNSPEC, RTM_GETLINK) < 0)
+  portid = mnl_socket_get_portid (nl);
+
+  nlh = mnl_nlmsg_put_header (buf);
+  nlh->nlmsg_type = RTM_GETLINK;
+  nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+  nlh->nlmsg_seq = seq = time (NULL);
+  rt = mnl_nlmsg_put_extra_header (nlh, sizeof (*rt));
+  rt->rtgen_family = AF_PACKET;
+
+  if (mnl_socket_sendto (nl, nlh, nlh->nlmsg_len) < 0)
   {
     ERROR ("netlink plugin: ir_read: rtnl_wilddump_request failed.");
     return (-1);
   }
 
-#ifdef RTNL_DUMP_FILTER_FIVE_ARGS
-  if (rtnl_dump_filter (&rth, link_filter, /* arg1 = */ NULL,
-       NULL, NULL) != 0)
-#elif defined(RTNL_DUMP_FILTER_THREE_ARGS)
-  if (rtnl_dump_filter (&rth, link_filter, /* arg = */ NULL) != 0)
-#else
-#error "Failed to determine the number of arguments to 'rtnl_dump_filter'!"
-#endif
+  ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
+  while (ret > 0)
+  {
+    ret = mnl_cb_run (buf, ret, seq, portid, link_filter_cb, NULL);
+    if (ret <= MNL_CB_STOP)
+      break;
+    ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
+  }
+  if (ret < 0)
   {
-    ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
+    ERROR ("netlink plugin: ir_read: mnl_socket_recvfrom failed.");
     return (-1);
   }
 
-  /* `link_filter' will update `iflist' which is used here to iterate over all
-   * interfaces. */
-  for (ifindex = 0; (size_t) ifindex < iflist_len; ifindex++)
+  /* `link_filter_cb' will update `iflist' which is used here to iterate
+   * over all interfaces. */
+  for (ifindex = 1; ifindex < iflist_len; ifindex++)
   {
+    struct tcmsg *tm;
     size_t type_index;
 
     if (iflist[ifindex] == NULL)
@@ -592,36 +775,42 @@ static int ir_read (void)
     {
       if (check_ignorelist (iflist[ifindex], type_name[type_index], NULL))
       {
-       DEBUG ("netlink plugin: ir_read: check_ignorelist (%s, %s, (nil)) "
-           "== TRUE", iflist[ifindex], type_name[type_index]);
-       continue;
+        DEBUG ("netlink plugin: ir_read: check_ignorelist (%s, %s, (nil)) "
+            "== TRUE", iflist[ifindex], type_name[type_index]);
+        continue;
       }
 
-      DEBUG ("netlink plugin: ir_read: querying %s from %s (%i).",
-         type_name[type_index], iflist[ifindex], ifindex);
+      DEBUG ("netlink plugin: ir_read: querying %s from %s (%lu).",
+          type_name[type_index], iflist[ifindex], ifindex);
 
-      memset (&tm, '\0', sizeof (tm));
-      tm.tcm_family = AF_UNSPEC;
-      tm.tcm_ifindex = ifindex;
+      nlh = mnl_nlmsg_put_header (buf);
+      nlh->nlmsg_type = type_id[type_index];
+      nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+      nlh->nlmsg_seq = seq = time (NULL);
+      tm = mnl_nlmsg_put_extra_header (nlh, sizeof (*tm));
+      tm->tcm_family = AF_PACKET;
+      tm->tcm_ifindex = ifindex;
 
-      if (rtnl_dump_request (&rth, type_id[type_index], &tm, sizeof (tm)) < 0)
+      if (mnl_socket_sendto (nl, nlh, nlh->nlmsg_len) < 0)
       {
-       ERROR ("netlink plugin: ir_read: rtnl_dump_request failed.");
-       continue;
+        ERROR ("netlink plugin: ir_read: mnl_socket_sendto failed.");
+        continue;
       }
 
-#ifdef RTNL_DUMP_FILTER_FIVE_ARGS
-      if (rtnl_dump_filter (&rth, qos_filter, (void *) &ifindex,
-           NULL, NULL) != 0)
-#elif defined(RTNL_DUMP_FILTER_THREE_ARGS)
-      if (rtnl_dump_filter (&rth, qos_filter, /* arg = */ &ifindex) != 0)
-#else
-#error "Failed to determine the number of arguments to 'rtnl_dump_filter'!"
-#endif
+      ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
+      while (ret > 0)
       {
-       ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
-       continue;
+        ret = mnl_cb_run (buf, ret, seq, portid, qos_filter_cb, &ifindex);
+        if (ret <= MNL_CB_STOP)
+          break;
+        ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
       }
+      if (ret < 0)
+      {
+        ERROR ("netlink plugin: ir_read:mnl_socket_recvfrom failed.");
+        continue;
+      }
+
     } /* for (type_index) */
   } /* for (if_index) */
 
@@ -630,12 +819,12 @@ static int ir_read (void)
 
 static int ir_shutdown (void)
 {
-  if ((rth.fd != 0) || (rth.seq != 0) || (rth.dump != 0))
+  if (nl)
   {
-    rtnl_close(&rth);
-    memset (&rth, '\0', sizeof (rth));
+    mnl_socket_close (nl);
+    nl = NULL;
   }
-  
+
   return (0);
 } /* int ir_shutdown */
 
index 7568a2c..c356353 100644 (file)
@@ -122,7 +122,7 @@ static int init (void)
 
   curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, nginx_curl_callback);
-  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, nginx_curl_error);
 
   if (user != NULL)
@@ -181,6 +181,8 @@ static void submit (char *type, char *inst, long long value)
     values[0].gauge = value;
   else if (strcmp (type, "nginx_requests") == 0)
     values[0].derive = value;
+  else if (strcmp (type, "connections") == 0)
+    values[0].derive = value;
   else
     return;
 
@@ -254,6 +256,8 @@ static int nginx_read (void)
          && (atoll (fields[1]) != 0)
          && (atoll (fields[2]) != 0))
       {
+       submit ("connections", "accepted", atoll (fields[0]));
+       submit ("connections", "handled", atoll (fields[1]));
        submit ("nginx_requests", NULL, atoll (fields[2]));
       }
     }
index 894b0e5..6c7aa05 100644 (file)
@@ -31,6 +31,7 @@
 #include "utils_llist.h"
 #include "utils_heap.h"
 #include "utils_time.h"
+#include "utils_random.h"
 
 #if HAVE_PTHREAD_H
 # include <pthread.h>
@@ -108,6 +109,7 @@ static int             read_threads_num = 0;
 
 static write_queue_t  *write_queue_head;
 static write_queue_t  *write_queue_tail;
+static long            write_queue_length = 0;
 static _Bool           write_loop = 1;
 static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t  write_cond = PTHREAD_COND_INITIALIZER;
@@ -117,6 +119,9 @@ static size_t          write_threads_num = 0;
 static pthread_key_t   plugin_ctx_key;
 static _Bool           plugin_ctx_key_initialized = 0;
 
+static long            write_limit_high = 0;
+static long            write_limit_low = 0;
+
 /*
  * Static functions
  */
@@ -670,11 +675,13 @@ static int plugin_write_enqueue (value_list_t const *vl) /* {{{ */
        {
                write_queue_head = q;
                write_queue_tail = q;
+               write_queue_length = 1;
        }
        else
        {
                write_queue_tail->next = q;
                write_queue_tail = q;
+               write_queue_length += 1;
        }
 
        pthread_cond_signal (&write_cond);
@@ -701,8 +708,11 @@ static value_list_t *plugin_write_dequeue (void) /* {{{ */
 
        q = write_queue_head;
        write_queue_head = q->next;
-       if (write_queue_head == NULL)
+       write_queue_length -= 1;
+       if (write_queue_head == NULL) {
                write_queue_tail = NULL;
+               assert(0 == write_queue_length);
+               }
 
        pthread_mutex_unlock (&write_lock);
 
@@ -805,6 +815,7 @@ static void stop_write_threads (void) /* {{{ */
        }
        write_queue_head = NULL;
        write_queue_tail = NULL;
+       write_queue_length = 0;
        pthread_mutex_unlock (&write_lock);
 
        if (i > 0)
@@ -1206,6 +1217,27 @@ int plugin_register_shutdown (const char *name,
                                (void *) callback, /* user_data = */ NULL));
 } /* int plugin_register_shutdown */
 
+static void plugin_free_data_sets (void)
+{
+       void *key;
+       void *value;
+
+       if (data_sets == NULL)
+               return;
+
+       while (c_avl_pick (data_sets, &key, &value) == 0)
+       {
+               data_set_t *ds = value;
+               /* key is a pointer to ds->type */
+
+               sfree (ds->ds);
+               sfree (ds);
+       }
+
+       c_avl_destroy (data_sets);
+       data_sets = NULL;
+} /* void plugin_free_data_sets */
+
 int plugin_register_data_set (const data_set_t *ds)
 {
        data_set_t *ds_copy;
@@ -1423,7 +1455,8 @@ int plugin_unregister_notification (const char *name)
 
 void plugin_init_all (void)
 {
-       const char *chain_name;
+       char const *chain_name;
+       long write_threads_num;
        llentry_t *le;
        int status;
 
@@ -1436,16 +1469,38 @@ void plugin_init_all (void)
        chain_name = global_option_get ("PostCacheChain");
        post_cache_chain = fc_chain_get_by_name (chain_name);
 
+       write_limit_high = global_option_get_long ("WriteQueueLimitHigh",
+                       /* default = */ 0);
+       if (write_limit_high < 0)
        {
-               char const *tmp = global_option_get ("WriteThreads");
-               int num = atoi (tmp);
+               ERROR ("WriteQueueLimitHigh must be positive or zero.");
+               write_limit_high = 0;
+       }
 
-               if (num < 1)
-                       num = 5;
+       write_limit_low = global_option_get_long ("WriteQueueLimitLow",
+                       /* default = */ write_limit_high / 2);
+       if (write_limit_low < 0)
+       {
+               ERROR ("WriteQueueLimitLow must be positive or zero.");
+               write_limit_low = write_limit_high / 2;
+       }
+       else if (write_limit_low > write_limit_high)
+       {
+               ERROR ("WriteQueueLimitLow must not be larger than "
+                               "WriteQueueLimitHigh.");
+               write_limit_low = write_limit_high;
+       }
 
-               start_write_threads ((size_t) num);
+       write_threads_num = global_option_get_long ("WriteThreads",
+                       /* default = */ 5);
+       if (write_threads_num < 1)
+       {
+               ERROR ("WriteThreads must be positive.");
+               write_threads_num = 5;
        }
 
+       start_write_threads ((size_t) write_threads_num);
+
        if ((list_init == NULL) && (read_heap == NULL))
                return;
 
@@ -1730,6 +1785,7 @@ void plugin_shutdown_all (void)
        destroy_all_callbacks (&list_log);
 
        plugin_free_loaded ();
+       plugin_free_data_sets ();
 } /* void plugin_shutdown_all */
 
 int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
@@ -1950,10 +2006,76 @@ static int plugin_dispatch_values_internal (value_list_t *vl)
        return (0);
 } /* int plugin_dispatch_values_internal */
 
+static double get_drop_probability (void) /* {{{ */
+{
+       long pos;
+       long size;
+       long wql;
+
+       pthread_mutex_lock (&write_lock);
+       wql = write_queue_length;
+       pthread_mutex_unlock (&write_lock);
+
+       if (wql < write_limit_low)
+               return (0.0);
+       if (wql >= write_limit_high)
+               return (1.0);
+
+       pos = 1 + wql - write_limit_low;
+       size = 1 + write_limit_high - write_limit_low;
+
+       return (((double) pos) / ((double) size));
+} /* }}} double get_drop_probability */
+
+static _Bool check_drop_value (void) /* {{{ */
+{
+       static cdtime_t last_message_time = 0;
+       static pthread_mutex_t last_message_lock = PTHREAD_MUTEX_INITIALIZER;
+
+       double p;
+       double q;
+       int status;
+
+       if (write_limit_high == 0)
+               return (0);
+
+       p = get_drop_probability ();
+       if (p == 0.0)
+               return (0);
+
+       status = pthread_mutex_trylock (&last_message_lock);
+       if (status == 0)
+       {
+               cdtime_t now;
+
+               now = cdtime ();
+               if ((now - last_message_time) > TIME_T_TO_CDTIME_T (1))
+               {
+                       last_message_time = now;
+                       ERROR ("plugin_dispatch_values: Low water mark "
+                                       "reached. Dropping %.0f%% of metrics.",
+                                       100.0 * p);
+               }
+               pthread_mutex_unlock (&last_message_lock);
+       }
+
+       if (p == 1.0)
+               return (1);
+
+       q = cdrand_d ();
+       if (q > p)
+               return (1);
+       else
+               return (0);
+} /* }}} _Bool check_drop_value */
+
 int plugin_dispatch_values (value_list_t const *vl)
 {
        int status;
 
+       if (check_drop_value ())
+               return (0);
+
        status = plugin_write_enqueue (vl);
        if (status != 0)
        {
index 1eaf6bd..16e539a 100644 (file)
@@ -571,6 +571,7 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
        }
 
        if (C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host)
+                       || (0 == strcmp (db->host, "127.0.0.1"))
                        || (0 == strcmp (db->host, "localhost")))
                host = hostname_g;
        else
index 7e0b1e7..e77be2d 100644 (file)
@@ -114,52 +114,41 @@ static int value_list_to_string (char *buffer, int buffer_len,
   return (0);
 } /* int value_list_to_string */
 
-static int value_list_to_filename (char *buffer, int buffer_len,
-    const data_set_t *ds, const value_list_t *vl)
+static int value_list_to_filename (char *buffer, size_t buffer_size,
+    value_list_t const *vl)
 {
-  int offset = 0;
+  char const suffix[] = ".rrd";
   int status;
-
-  assert (0 == strcmp (ds->type, vl->type));
+  size_t len;
 
   if (datadir != NULL)
   {
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s/", datadir);
-    if ((status < 1) || (status >= buffer_len - offset))
-      return (-1);
-    offset += status;
-  }
+    size_t datadir_len = strlen (datadir) + 1;
 
-  status = ssnprintf (buffer + offset, buffer_len - offset,
-      "%s/", vl->host);
-  if ((status < 1) || (status >= buffer_len - offset))
-    return (-1);
-  offset += status;
+    if (datadir_len >= buffer_size)
+      return (ENOMEM);
 
-  if (strlen (vl->plugin_instance) > 0)
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s-%s/", vl->plugin, vl->plugin_instance);
-  else
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s/", vl->plugin);
-  if ((status < 1) || (status >= buffer_len - offset))
-    return (-1);
-  offset += status;
+    sstrncpy (buffer, datadir, buffer_size);
+    buffer[datadir_len - 1] = '/';
+    buffer[datadir_len] = 0;
 
-  if (strlen (vl->type_instance) > 0)
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s-%s", vl->type, vl->type_instance);
-  else
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s", vl->type);
-  if ((status < 1) || (status >= buffer_len - offset))
-    return (-1);
-  offset += status;
+    buffer += datadir_len;
+    buffer_size -= datadir_len;
+  }
+
+  status = FORMAT_VL (buffer, buffer_size, vl);
+  if (status != 0)
+    return (status);
 
-  strncpy (buffer + offset, ".rrd", buffer_len - offset);
-  buffer[buffer_len - 1] = 0;
+  len = strlen (buffer);
+  assert (len < buffer_size);
+  buffer += len;
+  buffer_size -= len;
+
+  if (buffer_size <= sizeof (suffix))
+    return (ENOMEM);
 
+  memcpy (buffer, suffix, sizeof (suffix));
   return (0);
 } /* int value_list_to_filename */
 
@@ -434,7 +423,7 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl,
     return (-1);
   }
 
-  if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+  if (value_list_to_filename (filename, sizeof (filename), vl) != 0)
   {
     ERROR ("rrdcached plugin: value_list_to_filename failed.");
     return (-1);
index 2c80762..c795e26 100644 (file)
@@ -198,7 +198,7 @@ static int srrd_update (char *filename, char *template,
 } /* int srrd_update */
 #endif /* !HAVE_THREADSAFE_LIBRRD */
 
-static int value_list_to_string (char *buffer, int buffer_len,
+static int value_list_to_string_multiple (char *buffer, int buffer_len,
                const data_set_t *ds, const value_list_t *vl)
 {
        int offset;
@@ -242,49 +242,82 @@ static int value_list_to_string (char *buffer, int buffer_len,
        } /* for ds->ds_num */
 
        return (0);
+} /* int value_list_to_string_multiple */
+
+static int value_list_to_string (char *buffer, int buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       int status;
+       time_t tt;
+
+       if (ds->ds_num != 1)
+               return (value_list_to_string_multiple (buffer, buffer_len,
+                                       ds, vl));
+
+       tt = CDTIME_T_TO_TIME_T (vl->time);
+       switch (ds->ds[0].type)
+       {
+               case DS_TYPE_DERIVE:
+                       status = ssnprintf (buffer, buffer_len, "%u:%"PRIi64,
+                               (unsigned) tt, vl->values[0].derive);
+                       break;
+               case DS_TYPE_GAUGE:
+                       status = ssnprintf (buffer, buffer_len, "%u:%lf",
+                               (unsigned) tt, vl->values[0].gauge);
+                       break;
+               case DS_TYPE_COUNTER:
+                       status = ssnprintf (buffer, buffer_len, "%u:%llu",
+                               (unsigned) tt, vl->values[0].counter);
+                       break;
+               case DS_TYPE_ABSOLUTE:
+                       status = ssnprintf (buffer, buffer_len, "%u:%"PRIu64,
+                               (unsigned) tt, vl->values[0].absolute);
+                       break;
+               default:
+                       return (EINVAL);
+       }
+
+       if ((status < 1) || (status >= buffer_len))
+               return (ENOMEM);
+
+       return (0);
 } /* int value_list_to_string */
 
-static int value_list_to_filename (char *buffer, int buffer_len,
-               const data_set_t __attribute__((unused)) *ds, const value_list_t *vl)
+static int value_list_to_filename (char *buffer, size_t buffer_size,
+               value_list_t const *vl)
 {
-       int offset = 0;
+       char const suffix[] = ".rrd";
        int status;
+       size_t len;
 
        if (datadir != NULL)
        {
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", datadir);
-               if ((status < 1) || (status >= buffer_len - offset))
-                       return (-1);
-               offset += status;
+               size_t datadir_len = strlen (datadir) + 1;
+
+               if (datadir_len >= buffer_size)
+                       return (ENOMEM);
+
+               sstrncpy (buffer, datadir, buffer_size);
+               buffer[datadir_len - 1] = '/';
+               buffer[datadir_len] = 0;
+
+               buffer += datadir_len;
+               buffer_size -= datadir_len;
        }
 
-       status = ssnprintf (buffer + offset, buffer_len - offset,
-                       "%s/", vl->host);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       status = FORMAT_VL (buffer, buffer_size, vl);
+       if (status != 0)
+               return (status);
 
-       if (strlen (vl->plugin_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s/", vl->plugin, vl->plugin_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", vl->plugin);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       len = strlen (buffer);
+       assert (len < buffer_size);
+       buffer += len;
+       buffer_size -= len;
 
-       if (strlen (vl->type_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s.rrd", vl->type, vl->type_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s.rrd", vl->type);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       if (buffer_size <= sizeof (suffix))
+               return (ENOMEM);
 
+       memcpy (buffer, suffix, sizeof (suffix));
        return (0);
 } /* int value_list_to_filename */
 
@@ -887,7 +920,7 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl,
                return -1;
        }
 
-       if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+       if (value_list_to_filename (filename, sizeof (filename), vl) != 0)
                return (-1);
 
        if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
diff --git a/src/sigrok.c b/src/sigrok.c
new file mode 100644 (file)
index 0000000..ba3e406
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * collectd - src/sigrok.c
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <pthread.h>
+
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+
+/* Minimum interval between dispatches coming from this plugin. The RRD
+ * plugin, at least, complains when written to with sub-second intervals.*/
+#define DEFAULT_MIN_DISPATCH_INTERVAL TIME_T_TO_CDTIME_T(0)
+
+static pthread_t sr_thread;
+static int sr_thread_running = FALSE;
+GSList *config_devices;
+static int num_devices;
+static int loglevel = SR_LOG_WARN;
+static struct sr_context *sr_ctx;
+
+struct config_device {
+       char *name;
+       char *driver;
+       char *conn;
+       char *serialcomm;
+       struct sr_dev_inst *sdi;
+       cdtime_t min_dispatch_interval;
+       cdtime_t last_dispatch;
+};
+
+
+static int sigrok_log_callback(void*cb_data __attribute__((unused)),
+               int msg_loglevel, const char *format, va_list args)
+{
+       char s[512];
+
+       if (msg_loglevel <= loglevel) {
+               vsnprintf(s, 512, format, args);
+               plugin_log(LOG_INFO, "sigrok plugin: %s", s);
+       }
+
+       return 0;
+}
+
+static int sigrok_config_device(oconfig_item_t *ci)
+{
+       struct config_device *cfdev;
+       int i;
+
+       if (!(cfdev = malloc(sizeof(struct config_device)))) {
+               ERROR("sigrok plugin: malloc() failed.");
+               return -1;
+       }
+       memset(cfdev, 0, sizeof(*cfdev));
+       if (cf_util_get_string(ci, &cfdev->name)) {
+               free(cfdev);
+               WARNING("sigrok plugin: Invalid device name.");
+               return -1;
+       }
+       cfdev->min_dispatch_interval = DEFAULT_MIN_DISPATCH_INTERVAL;
+
+       for (i = 0; i < ci->children_num; i++) {
+               oconfig_item_t *item = ci->children + i;
+               if (!strcasecmp(item->key, "driver"))
+                       cf_util_get_string(item, &cfdev->driver);
+               else if (!strcasecmp(item->key, "conn"))
+                       cf_util_get_string(item, &cfdev->conn);
+               else if (!strcasecmp(item->key, "serialcomm"))
+                       cf_util_get_string(item, &cfdev->serialcomm);
+               else if (!strcasecmp(item->key, "minimuminterval"))
+                       cf_util_get_cdtime(item, &cfdev->min_dispatch_interval);
+               else
+                       WARNING("sigrok plugin: Invalid keyword \"%s\".",
+                                       item->key);
+       }
+
+       config_devices = g_slist_append(config_devices, cfdev);
+
+       return 0;
+}
+
+static int sigrok_config(oconfig_item_t *ci)
+{
+       int i;
+
+       for (i = 0; i < ci->children_num; i++) {
+               oconfig_item_t *item = ci->children + i;
+               if (strcasecmp("LogLevel", item->key) == 0) {
+                       int status;
+                       int tmp = -1;
+
+                       status = cf_util_get_int (item, &tmp);
+                       if (status != 0)
+                               continue;
+                       else if ((tmp < 0) || (tmp > 5)) {
+                               ERROR ("sigrok plugin: The \"LogLevel\" "
+                                               "configuration option expects "
+                                               "an integer between 0 and 5 "
+                                               "(inclusive); you provided %i.",
+                                               tmp);
+                               continue;
+                       }
+                       loglevel = tmp;
+               } else if (!strcasecmp(item->key, "Device"))
+                       sigrok_config_device(item);
+               else
+                       WARNING("sigrok plugin: Invalid keyword \"%s\".",
+                                       item->key);
+       }
+
+       return 0;
+}
+
+static char *sigrok_value_type(const struct sr_datafeed_analog *analog)
+{
+       char *s;
+
+       if (analog->mq == SR_MQ_VOLTAGE)
+               s = "voltage";
+       else if (analog->mq == SR_MQ_CURRENT)
+               s = "current";
+       else if (analog->mq == SR_MQ_FREQUENCY)
+               s = "frequency";
+       else if (analog->mq == SR_MQ_POWER)
+               s = "power";
+       else if (analog->mq == SR_MQ_TEMPERATURE)
+               s = "temperature";
+       else if (analog->mq == SR_MQ_RELATIVE_HUMIDITY)
+               s = "humidity";
+       else if (analog->mq == SR_MQ_SOUND_PRESSURE_LEVEL)
+               s = "spl";
+       else
+               s = "gauge";
+
+       return s;
+}
+
+static void sigrok_feed_callback(const struct sr_dev_inst *sdi,
+               const struct sr_datafeed_packet *packet, void *cb_data)
+{
+       const struct sr_datafeed_analog *analog;
+       struct config_device *cfdev;
+       GSList *l;
+       value_t value;
+       value_list_t vl = VALUE_LIST_INIT;
+
+       /* Find this device's configuration. */
+       cfdev = NULL;
+       for (l = config_devices; l; l = l->next) {
+               cfdev = l->data;
+               if (cfdev->sdi == sdi) {
+                       /* Found it. */
+                       break;
+               }
+               cfdev = NULL;
+       }
+
+       if (!cfdev) {
+               ERROR("sigrok plugin: Received data from driver \"%s\" but "
+                               "can't find a configuration / device matching "
+                               "it.", sdi->driver->name);
+               return;
+       }
+
+       if (packet->type == SR_DF_END) {
+               /* TODO: try to restart acquisition after a delay? */
+               WARNING("sigrok plugin: acquisition for \"%s\" ended.",
+                               cfdev->name);
+               return;
+       }
+
+       if (packet->type != SR_DF_ANALOG)
+               return;
+
+       if ((cfdev->min_dispatch_interval != 0)
+                       && ((cdtime() - cfdev->last_dispatch)
+                               < cfdev->min_dispatch_interval))
+               return;
+
+       /* Ignore all but the first sample on the first probe. */
+       analog = packet->payload;
+       value.gauge = analog->data[0];
+       vl.values = &value;
+       vl.values_len = 1;
+       sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+       sstrncpy(vl.plugin, "sigrok", sizeof(vl.plugin));
+       ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance),
+                       "%s", cfdev->name);
+       sstrncpy(vl.type, sigrok_value_type(analog), sizeof(vl.type));
+
+       plugin_dispatch_values(&vl);
+       cfdev->last_dispatch = cdtime();
+}
+
+static void sigrok_free_drvopts(struct sr_config *src)
+{
+       g_variant_unref(src->data);
+       g_free(src);
+}
+
+static int sigrok_init_driver(struct config_device *cfdev,
+               struct sr_dev_driver *drv)
+{
+       struct sr_config *src;
+       GSList *devlist, *drvopts;
+       char hwident[512];
+
+       if (sr_driver_init(sr_ctx, drv) != SR_OK)
+               /* Error was logged by libsigrok. */
+               return -1;
+
+       drvopts = NULL;
+       if (cfdev->conn) {
+               if (!(src = malloc(sizeof(struct sr_config))))
+                       return -1;
+               src->key = SR_CONF_CONN;
+               src->data = g_variant_new_string(cfdev->conn);
+               drvopts = g_slist_append(drvopts, src);
+       }
+       if (cfdev->serialcomm) {
+               if (!(src = malloc(sizeof(struct sr_config))))
+                       return -1;
+               src->key = SR_CONF_SERIALCOMM;
+               src->data = g_variant_new_string(cfdev->serialcomm);
+               drvopts = g_slist_append(drvopts, src);
+       }
+       devlist = sr_driver_scan(drv, drvopts);
+       g_slist_free_full(drvopts, (GDestroyNotify)sigrok_free_drvopts);
+       if (!devlist) {
+               /* Not an error, but the user should know about it. */
+               WARNING("sigrok plugin: No device found for \"%s\".",
+                               cfdev->name);
+               return 0;
+       }
+
+       if (g_slist_length(devlist) > 1) {
+               INFO("sigrok plugin: %d sigrok devices for device entry "
+                               "\"%s\": must be 1.",
+                               g_slist_length(devlist), cfdev->name);
+               return -1;
+       }
+       cfdev->sdi = devlist->data;
+       g_slist_free(devlist);
+       ssnprintf(hwident, sizeof(hwident), "%s %s %s",
+                       cfdev->sdi->vendor ? cfdev->sdi->vendor : "",
+                       cfdev->sdi->model ? cfdev->sdi->model : "",
+                       cfdev->sdi->version ? cfdev->sdi->version : "");
+       INFO("sigrok plugin: Device \"%s\" is a %s", cfdev->name, hwident);
+
+       if (sr_dev_open(cfdev->sdi) != SR_OK)
+               return -1;
+
+       if (sr_session_dev_add(cfdev->sdi) != SR_OK)
+               return -1;
+
+       return 1;
+}
+
+static void *sigrok_read_thread(void *arg __attribute__((unused)))
+{
+       struct sr_dev_driver *drv, **drvlist;
+       GSList *l;
+       struct config_device *cfdev;
+       int ret, i;
+
+       sr_log_callback_set(sigrok_log_callback, NULL);
+       sr_log_loglevel_set(loglevel);
+
+       if ((ret = sr_init(&sr_ctx)) != SR_OK) {
+               ERROR("sigrok plugin: Failed to initialize libsigrok: %s.",
+                               sr_strerror(ret));
+               return NULL;
+       }
+
+       if (!sr_session_new())
+               return NULL;
+
+       num_devices = 0;
+       drvlist = sr_driver_list();
+       for (l = config_devices; l; l = l->next) {
+               cfdev = l->data;
+               drv = NULL;
+               for (i = 0; drvlist[i]; i++) {
+                       if (!strcmp(drvlist[i]->name, cfdev->driver)) {
+                               drv = drvlist[i];
+                               break;
+                       }
+               }
+               if (!drv) {
+                       ERROR("sigrok plugin: Unknown driver \"%s\".",
+                                       cfdev->driver);
+                       return NULL;
+               }
+
+               if ((ret = sigrok_init_driver(cfdev, drv)) < 0)
+                       /* Error was already logged. */
+                       return NULL;
+
+               num_devices += ret;
+       }
+
+       if (num_devices > 0) {
+               /* Do this only when we're sure there's hardware to talk to. */
+               if (sr_session_datafeed_callback_add(sigrok_feed_callback, NULL)
+                               != SR_OK)
+                       return NULL;
+
+               /* Start acquisition on all devices. */
+               if (sr_session_start() != SR_OK)
+                       return NULL;
+
+               /* Main loop, runs forever. */
+               sr_session_run();
+
+               sr_session_stop();
+               sr_session_dev_remove_all();
+       }
+
+       sr_session_destroy();
+
+       sr_exit(sr_ctx);
+
+       pthread_exit(NULL);
+       sr_thread_running = FALSE;
+
+       return NULL;
+}
+
+static int sigrok_init(void)
+{
+       int status;
+
+       if (sr_thread_running) {
+               ERROR("sigrok plugin: Thread already running.");
+               return -1;
+       }
+
+       if ((status = plugin_thread_create(&sr_thread, NULL, sigrok_read_thread,
+                       NULL)) != 0) {
+               ERROR("sigrok plugin: Failed to create thread: %s.",
+                               strerror(status));
+               return -1;
+       }
+       sr_thread_running = TRUE;
+
+       return 0;
+}
+
+static int sigrok_shutdown(void)
+{
+       struct config_device *cfdev;
+       GSList *l;
+
+       if (sr_thread_running) {
+               pthread_cancel(sr_thread);
+               pthread_join(sr_thread, NULL);
+       }
+
+       for (l = config_devices; l; l = l->next) {
+               cfdev = l->data;
+               free(cfdev->name);
+               free(cfdev->driver);
+               free(cfdev->conn);
+               free(cfdev->serialcomm);
+               free(cfdev);
+       }
+       g_slist_free(config_devices);
+
+       return 0;
+}
+
+void module_register(void)
+{
+       plugin_register_complex_config("sigrok", sigrok_config);
+       plugin_register_init("sigrok", sigrok_init);
+       plugin_register_shutdown("sigrok", sigrok_shutdown);
+}
index 491d45a..806e38c 100644 (file)
@@ -1471,13 +1471,14 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
         csnmp_table_values_t *vt;
         oid_t vb_name;
         oid_t suffix;
+        int ret;
 
         csnmp_oid_init (&vb_name, vb->name, vb->name_length);
 
         /* Calculate the current suffix. This is later used to check that the
          * suffix is increasing. This also checks if we left the subtree */
-        status = csnmp_oid_suffix (&suffix, &vb_name, data->values + i);
-        if (status != 0)
+        ret = csnmp_oid_suffix (&suffix, &vb_name, data->values + i);
+        if (ret != 0)
         {
           DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
               "Value probably left its subtree.",
diff --git a/src/statsd.c b/src/statsd.c
new file mode 100644 (file)
index 0000000..72a7779
--- /dev/null
@@ -0,0 +1,908 @@
+/**
+ * collectd - src/statsd.c
+ *
+ * Copyright (C) 2013       Florian octo Forster
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+#include "utils_avltree.h"
+#include "utils_complain.h"
+#include "utils_latency.h"
+
+#include <pthread.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <poll.h>
+
+#ifndef STATSD_DEFAULT_NODE
+# define STATSD_DEFAULT_NODE NULL
+#endif
+
+#ifndef STATSD_DEFAULT_SERVICE
+# define STATSD_DEFAULT_SERVICE "8125"
+#endif
+
+enum metric_type_e
+{
+  STATSD_COUNTER,
+  STATSD_TIMER,
+  STATSD_GAUGE,
+  STATSD_SET
+};
+typedef enum metric_type_e metric_type_t;
+
+struct statsd_metric_s
+{
+  metric_type_t type;
+  double value;
+  latency_counter_t *latency;
+  c_avl_tree_t *set;
+  unsigned long updates_num;
+};
+typedef struct statsd_metric_s statsd_metric_t;
+
+static c_avl_tree_t   *metrics_tree = NULL;
+static pthread_mutex_t metrics_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_t network_thread;
+static _Bool     network_thread_running = 0;
+static _Bool     network_thread_shutdown = 0;
+
+static char *conf_node = NULL;
+static char *conf_service = NULL;
+
+static _Bool conf_delete_counters = 0;
+static _Bool conf_delete_timers   = 0;
+static _Bool conf_delete_gauges   = 0;
+static _Bool conf_delete_sets     = 0;
+
+static double *conf_timer_percentile = NULL;
+static size_t  conf_timer_percentile_num = 0;
+
+static _Bool conf_timer_lower     = 0;
+static _Bool conf_timer_upper     = 0;
+static _Bool conf_timer_sum       = 0;
+static _Bool conf_timer_count     = 0;
+
+/* Must hold metrics_lock when calling this function. */
+static statsd_metric_t *statsd_metric_lookup_unsafe (char const *name, /* {{{ */
+    metric_type_t type)
+{
+  char key[DATA_MAX_NAME_LEN + 2];
+  char *key_copy;
+  statsd_metric_t *metric;
+  int status;
+
+  switch (type)
+  {
+    case STATSD_COUNTER: key[0] = 'c'; break;
+    case STATSD_TIMER:   key[0] = 't'; break;
+    case STATSD_GAUGE:   key[0] = 'g'; break;
+    case STATSD_SET:     key[0] = 's'; break;
+    default: return (NULL);
+  }
+
+  key[1] = ':';
+  sstrncpy (&key[2], name, sizeof (key) - 2);
+
+  status = c_avl_get (metrics_tree, key, (void *) &metric);
+  if (status == 0)
+    return (metric);
+
+  key_copy = strdup (key);
+  if (key_copy == NULL)
+  {
+    ERROR ("statsd plugin: strdup failed.");
+    return (NULL);
+  }
+
+  metric = malloc (sizeof (*metric));
+  if (metric == NULL)
+  {
+    ERROR ("statsd plugin: malloc failed.");
+    sfree (key_copy);
+    return (NULL);
+  }
+  memset (metric, 0, sizeof (*metric));
+
+  metric->type = type;
+  metric->latency = NULL;
+  metric->set = NULL;
+
+  status = c_avl_insert (metrics_tree, key_copy, metric);
+  if (status != 0)
+  {
+    ERROR ("statsd plugin: c_avl_insert failed.");
+    sfree (key_copy);
+    sfree (metric);
+    return (NULL);
+  }
+
+  return (metric);
+} /* }}} statsd_metric_lookup_unsafe */
+
+static int statsd_metric_set (char const *name, double value, /* {{{ */
+    metric_type_t type)
+{
+  statsd_metric_t *metric;
+
+  pthread_mutex_lock (&metrics_lock);
+
+  metric = statsd_metric_lookup_unsafe (name, type);
+  if (metric == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (-1);
+  }
+
+  metric->value = value;
+  metric->updates_num++;
+
+  pthread_mutex_unlock (&metrics_lock);
+
+  return (0);
+} /* }}} int statsd_metric_set */
+
+static int statsd_metric_add (char const *name, double delta, /* {{{ */
+    metric_type_t type)
+{
+  statsd_metric_t *metric;
+
+  pthread_mutex_lock (&metrics_lock);
+
+  metric = statsd_metric_lookup_unsafe (name, type);
+  if (metric == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (-1);
+  }
+
+  metric->value += delta;
+  metric->updates_num++;
+
+  pthread_mutex_unlock (&metrics_lock);
+
+  return (0);
+} /* }}} int statsd_metric_add */
+
+static int statsd_parse_value (char const *str, value_t *ret_value) /* {{{ */
+{
+  char *endptr = NULL;
+
+  ret_value->gauge = (gauge_t) strtod (str, &endptr);
+  if ((str == endptr) || ((endptr != NULL) && (*endptr != 0)))
+    return (-1);
+
+  return (0);
+} /* }}} int statsd_parse_value */
+
+static int statsd_handle_counter (char const *name, /* {{{ */
+    char const *value_str,
+    char const *extra)
+{
+  value_t value;
+  value_t scale;
+  int status;
+
+  if ((extra != NULL) && (extra[0] != '@'))
+    return (-1);
+
+  scale.gauge = 1.0;
+  if (extra != NULL)
+  {
+    status = statsd_parse_value (extra + 1, &scale);
+    if (status != 0)
+      return (status);
+
+    if (!isfinite (scale.gauge) || (scale.gauge <= 0.0) || (scale.gauge > 1.0))
+      return (-1);
+  }
+
+  value.gauge = 1.0;
+  status = statsd_parse_value (value_str, &value);
+  if (status != 0)
+    return (status);
+
+  return (statsd_metric_add (name, (double) (value.gauge / scale.gauge),
+        STATSD_COUNTER));
+} /* }}} int statsd_handle_counter */
+
+static int statsd_handle_gauge (char const *name, /* {{{ */
+    char const *value_str)
+{
+  value_t value;
+  int status;
+
+  value.gauge = 0;
+  status = statsd_parse_value (value_str, &value);
+  if (status != 0)
+    return (status);
+
+  if ((value_str[0] == '+') || (value_str[0] == '-'))
+    return (statsd_metric_add (name, (double) value.gauge, STATSD_GAUGE));
+  else
+    return (statsd_metric_set (name, (double) value.gauge, STATSD_GAUGE));
+} /* }}} int statsd_handle_gauge */
+
+static int statsd_handle_timer (char const *name, /* {{{ */
+    char const *value_str)
+{
+  statsd_metric_t *metric;
+  value_t value_ms;
+  cdtime_t value;
+  int status;
+
+  value_ms.derive = 0;
+  status = statsd_parse_value (value_str, &value_ms);
+  if (status != 0)
+    return (status);
+
+  value = MS_TO_CDTIME_T (value_ms.gauge);
+
+  pthread_mutex_lock (&metrics_lock);
+
+  metric = statsd_metric_lookup_unsafe (name, STATSD_TIMER);
+  if (metric == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (-1);
+  }
+
+  if (metric->latency == NULL)
+    metric->latency = latency_counter_create ();
+  if (metric->latency == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (-1);
+  }
+
+  latency_counter_add (metric->latency, value);
+  metric->updates_num++;
+
+  pthread_mutex_unlock (&metrics_lock);
+  return (0);
+} /* }}} int statsd_handle_timer */
+
+static int statsd_handle_set (char const *name, /* {{{ */
+    char const *set_key_orig)
+{
+  statsd_metric_t *metric = NULL;
+  char *set_key;
+  int status;
+
+  pthread_mutex_lock (&metrics_lock);
+
+  metric = statsd_metric_lookup_unsafe (name, STATSD_SET);
+  if (metric == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (-1);
+  }
+
+  /* Make sure metric->set exists. */
+  if (metric->set == NULL)
+    metric->set = c_avl_create ((void *) strcmp);
+
+  if (metric->set == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    ERROR ("statsd plugin: c_avl_create failed.");
+    return (-1);
+  }
+
+  set_key = strdup (set_key_orig);
+  if (set_key == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    ERROR ("statsd plugin: strdup failed.");
+    return (-1);
+  }
+
+  status = c_avl_insert (metric->set, set_key, /* value = */ NULL);
+  if (status < 0)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    if (status < 0)
+      ERROR ("statsd plugin: c_avl_insert (\"%s\") failed with status %i.",
+          set_key, status);
+    sfree (set_key);
+    return (-1);
+  }
+  else if (status > 0) /* key already exists */
+  {
+    sfree (set_key);
+  }
+
+  metric->updates_num++;
+
+  pthread_mutex_unlock (&metrics_lock);
+  return (0);
+} /* }}} int statsd_handle_set */
+
+static int statsd_parse_line (char *buffer) /* {{{ */
+{
+  char *name = buffer;
+  char *value;
+  char *type;
+  char *extra;
+
+  type = strchr (name, '|');
+  if (type == NULL)
+    return (-1);
+  *type = 0;
+  type++;
+
+  value = strrchr (name, ':');
+  if (value == NULL)
+    return (-1);
+  *value = 0;
+  value++;
+
+  extra = strchr (type, '|');
+  if (extra != NULL)
+  {
+    *extra = 0;
+    extra++;
+  }
+
+  if (strcmp ("c", type) == 0)
+    return (statsd_handle_counter (name, value, extra));
+
+  /* extra is only valid for counters */
+  if (extra != NULL)
+    return (-1);
+
+  if (strcmp ("g", type) == 0)
+    return (statsd_handle_gauge (name, value));
+  else if (strcmp ("ms", type) == 0)
+    return (statsd_handle_timer (name, value));
+  else if (strcmp ("s", type) == 0)
+    return (statsd_handle_set (name, value));
+  else
+    return (-1);
+} /* }}} void statsd_parse_line */
+
+static void statsd_parse_buffer (char *buffer) /* {{{ */
+{
+  while (buffer != NULL)
+  {
+    char orig[64];
+    char *next;
+    int status;
+
+    next = strchr (buffer, '\n');
+    if (next != NULL)
+    {
+      *next = 0;
+      next++;
+    }
+
+    if (*buffer == 0)
+    {
+      buffer = next;
+      continue;
+    }
+
+    sstrncpy (orig, buffer, sizeof (orig));
+
+    status = statsd_parse_line (buffer);
+    if (status != 0)
+      ERROR ("statsd plugin: Unable to parse line: \"%s\"", orig);
+
+    buffer = next;
+  }
+} /* }}} void statsd_parse_buffer */
+
+static void statsd_network_read (int fd) /* {{{ */
+{
+  char buffer[4096];
+  size_t buffer_size;
+  ssize_t status;
+
+  status = recv (fd, buffer, sizeof (buffer), /* flags = */ MSG_DONTWAIT);
+  if (status < 0)
+  {
+    char errbuf[1024];
+
+    if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+      return;
+
+    ERROR ("statsd plugin: recv(2) failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return;
+  }
+
+  buffer_size = (size_t) status;
+  if (buffer_size >= sizeof (buffer))
+    buffer_size = sizeof (buffer) - 1;
+  buffer[buffer_size] = 0;
+
+  statsd_parse_buffer (buffer);
+} /* }}} void statsd_network_read */
+
+static int statsd_network_init (struct pollfd **ret_fds, /* {{{ */
+    size_t *ret_fds_num)
+{
+  struct pollfd *fds = NULL;
+  size_t fds_num = 0;
+
+  struct addrinfo ai_hints;
+  struct addrinfo *ai_list = NULL;
+  struct addrinfo *ai_ptr;
+  int status;
+
+  char const *node = (conf_node != NULL) ? conf_node : STATSD_DEFAULT_NODE;
+  char const *service = (conf_service != NULL)
+    ? conf_service : STATSD_DEFAULT_SERVICE;
+
+  memset (&ai_hints, 0, sizeof (ai_hints));
+  ai_hints.ai_flags = AI_PASSIVE;
+#ifdef AI_ADDRCONFIG
+  ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+  ai_hints.ai_family = AF_UNSPEC;
+  ai_hints.ai_socktype = SOCK_DGRAM;
+
+  status = getaddrinfo (node, service, &ai_hints, &ai_list);
+  if (status != 0)
+  {
+    ERROR ("statsd plugin: getaddrinfo (\"%s\", \"%s\") failed: %s",
+        node, service, gai_strerror (status));
+    return (status);
+  }
+
+  for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    int fd;
+    struct pollfd *tmp;
+
+    char dbg_node[NI_MAXHOST];
+    char dbg_service[NI_MAXSERV];
+
+    fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (fd < 0)
+    {
+      char errbuf[1024];
+      ERROR ("statsd plugin: socket(2) failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      continue;
+    }
+
+    getnameinfo (ai_ptr->ai_addr, ai_ptr->ai_addrlen,
+        dbg_node, sizeof (dbg_node), dbg_service, sizeof (dbg_service),
+        NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV);
+    DEBUG ("statsd plugin: Trying to bind to [%s]:%s ...", dbg_node, dbg_service);
+
+    status = bind (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    if (status != 0)
+    {
+      char errbuf[1024];
+      ERROR ("statsd plugin: bind(2) failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      close (fd);
+      continue;
+    }
+
+    tmp = realloc (fds, sizeof (*fds) * (fds_num + 1));
+    if (tmp == NULL)
+    {
+      ERROR ("statsd plugin: realloc failed.");
+      continue;
+    }
+    fds = tmp;
+    tmp = fds + fds_num;
+    fds_num++;
+
+    memset (tmp, 0, sizeof (*tmp));
+    tmp->fd = fd;
+    tmp->events = POLLIN | POLLPRI;
+  }
+
+  freeaddrinfo (ai_list);
+
+  if (fds_num == 0)
+  {
+    ERROR ("statsd plugin: Unable to create listening socket for [%s]:%s.",
+        (node != NULL) ? node : "::", service);
+    return (ENOENT);
+  }
+
+  *ret_fds = fds;
+  *ret_fds_num = fds_num;
+  return (0);
+} /* }}} int statsd_network_init */
+
+static void *statsd_network_thread (void *args) /* {{{ */
+{
+  struct pollfd *fds = NULL;
+  size_t fds_num = 0;
+  int status;
+  size_t i;
+
+  status = statsd_network_init (&fds, &fds_num);
+  if (status != 0)
+  {
+    ERROR ("statsd plugin: Unable to open listening sockets.");
+    pthread_exit ((void *) 0);
+  }
+
+  while (!network_thread_shutdown)
+  {
+    status = poll (fds, (nfds_t) fds_num, /* timeout = */ -1);
+    if (status < 0)
+    {
+      char errbuf[1024];
+
+      if ((errno == EINTR) || (errno == EAGAIN))
+        continue;
+
+      ERROR ("statsd plugin: poll(2) failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      break;
+    }
+
+    for (i = 0; i < fds_num; i++)
+    {
+      if ((fds[i].revents & (POLLIN | POLLPRI)) == 0)
+        continue;
+
+      statsd_network_read (fds[i].fd);
+      fds[i].revents = 0;
+    }
+  } /* while (!network_thread_shutdown) */
+
+  /* Clean up */
+  for (i = 0; i < fds_num; i++)
+    close (fds[i].fd);
+  sfree (fds);
+
+  return ((void *) 0);
+} /* }}} void *statsd_network_thread */
+
+static int statsd_config_timer_percentile (oconfig_item_t *ci) /* {{{ */
+{
+  double percent = NAN;
+  double *tmp;
+  int status;
+
+  status = cf_util_get_double (ci, &percent);
+  if (status != 0)
+    return (status);
+
+  if ((percent <= 0.0) || (percent >= 100))
+  {
+    ERROR ("statsd plugin: The value for \"%s\" must be between 0 and 100, "
+        "exclusively.", ci->key);
+    return (ERANGE);
+  }
+
+  tmp = realloc (conf_timer_percentile,
+      sizeof (*conf_timer_percentile) * (conf_timer_percentile_num + 1));
+  if (tmp == NULL)
+  {
+    ERROR ("statsd plugin: realloc failed.");
+    return (ENOMEM);
+  }
+  conf_timer_percentile = tmp;
+  conf_timer_percentile[conf_timer_percentile_num] = percent;
+  conf_timer_percentile_num++;
+
+  return (0);
+} /* }}} int statsd_config_timer_percentile */
+
+static int statsd_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Host", child->key) == 0)
+      cf_util_get_string (child, &conf_node);
+    else if (strcasecmp ("Port", child->key) == 0)
+      cf_util_get_service (child, &conf_service);
+    else if (strcasecmp ("DeleteCounters", child->key) == 0)
+      cf_util_get_boolean (child, &conf_delete_counters);
+    else if (strcasecmp ("DeleteTimers", child->key) == 0)
+      cf_util_get_boolean (child, &conf_delete_timers);
+    else if (strcasecmp ("DeleteGauges", child->key) == 0)
+      cf_util_get_boolean (child, &conf_delete_gauges);
+    else if (strcasecmp ("DeleteSets", child->key) == 0)
+      cf_util_get_boolean (child, &conf_delete_sets);
+    else if (strcasecmp ("TimerLower", child->key) == 0)
+      cf_util_get_boolean (child, &conf_timer_lower);
+    else if (strcasecmp ("TimerUpper", child->key) == 0)
+      cf_util_get_boolean (child, &conf_timer_upper);
+    else if (strcasecmp ("TimerSum", child->key) == 0)
+      cf_util_get_boolean (child, &conf_timer_sum);
+    else if (strcasecmp ("TimerCount", child->key) == 0)
+      cf_util_get_boolean (child, &conf_timer_count);
+    else if (strcasecmp ("TimerPercentile", child->key) == 0)
+      statsd_config_timer_percentile (child);
+    else
+      ERROR ("statsd plugin: The \"%s\" config option is not valid.",
+          child->key);
+  }
+
+  return (0);
+} /* }}} int statsd_config */
+
+static int statsd_init (void) /* {{{ */
+{
+  pthread_mutex_lock (&metrics_lock);
+  if (metrics_tree == NULL)
+    metrics_tree = c_avl_create ((void *) strcmp);
+
+  if (!network_thread_running)
+  {
+    int status;
+
+    status = pthread_create (&network_thread,
+        /* attr = */ NULL,
+        statsd_network_thread,
+        /* args = */ NULL);
+    if (status != 0)
+    {
+      char errbuf[1024];
+      pthread_mutex_unlock (&metrics_lock);
+      ERROR ("statsd plugin: pthread_create failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      return (status);
+    }
+  }
+  network_thread_running = 1;
+
+  pthread_mutex_unlock (&metrics_lock);
+
+  return (0);
+} /* }}} int statsd_init */
+
+/* Must hold metrics_lock when calling this function. */
+static int statsd_metric_clear_set_unsafe (statsd_metric_t *metric) /* {{{ */
+{
+  void *key;
+  void *value;
+
+  if ((metric == NULL) || (metric->type != STATSD_SET))
+    return (EINVAL);
+
+  if (metric->set == NULL)
+    return (0);
+
+  while (c_avl_pick (metric->set, &key, &value) == 0)
+  {
+    sfree (key);
+    sfree (value);
+  }
+
+  return (0);
+} /* }}} int statsd_metric_clear_set_unsafe */
+
+/* Must hold metrics_lock when calling this function. */
+static int statsd_metric_submit_unsafe (char const *name, /* {{{ */
+    statsd_metric_t const *metric)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "statsd", sizeof (vl.plugin));
+
+  if (metric->type == STATSD_GAUGE)
+    sstrncpy (vl.type, "gauge", sizeof (vl.type));
+  else if (metric->type == STATSD_TIMER)
+    sstrncpy (vl.type, "latency", sizeof (vl.type));
+  else if (metric->type == STATSD_SET)
+    sstrncpy (vl.type, "objects", sizeof (vl.type));
+  else /* if (metric->type == STATSD_COUNTER) */
+    sstrncpy (vl.type, "derive", sizeof (vl.type));
+
+  sstrncpy (vl.type_instance, name, sizeof (vl.type_instance));
+
+  if (metric->type == STATSD_GAUGE)
+    values[0].gauge = (gauge_t) metric->value;
+  else if (metric->type == STATSD_TIMER)
+  {
+    size_t i;
+
+    if (metric->updates_num == 0)
+      return (0);
+
+    vl.time = cdtime ();
+
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+        "%s-average", name);
+    values[0].gauge = CDTIME_T_TO_DOUBLE (
+        latency_counter_get_average (metric->latency));
+    plugin_dispatch_values (&vl);
+
+    if (conf_timer_lower) {
+      ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+          "%s-lower", name);
+      values[0].gauge = CDTIME_T_TO_DOUBLE (
+          latency_counter_get_min (metric->latency));
+      plugin_dispatch_values (&vl);
+    }
+
+    if (conf_timer_upper) {
+      ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+          "%s-upper", name);
+      values[0].gauge = CDTIME_T_TO_DOUBLE (
+          latency_counter_get_max (metric->latency));
+      plugin_dispatch_values (&vl);
+    }
+
+    if (conf_timer_sum) {
+      ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+          "%s-sum", name);
+      values[0].gauge = CDTIME_T_TO_DOUBLE (
+          latency_counter_get_sum (metric->latency));
+      plugin_dispatch_values (&vl);
+    }
+
+    for (i = 0; i < conf_timer_percentile_num; i++)
+    {
+      ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+          "%s-percentile-%.0f", name, conf_timer_percentile[i]);
+      values[0].gauge = CDTIME_T_TO_DOUBLE (
+          latency_counter_get_percentile (
+            metric->latency, conf_timer_percentile[i]));
+      plugin_dispatch_values (&vl);
+    }
+
+    /* Keep this at the end, since vl.type is set to "gauge" here. The
+     * vl.type's above are implicitly set to "latency". */
+    if (conf_timer_count) {
+      sstrncpy (vl.type, "gauge", sizeof (vl.type));
+      ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+          "%s-count", name);
+      values[0].gauge = latency_counter_get_num (metric->latency);
+      plugin_dispatch_values (&vl);
+    }
+
+    latency_counter_reset (metric->latency);
+    return (0);
+  }
+  else if (metric->type == STATSD_SET)
+  {
+    if (metric->set == NULL)
+      values[0].gauge = 0.0;
+    else
+      values[0].gauge = (gauge_t) c_avl_size (metric->set);
+  }
+  else
+    values[0].derive = (derive_t) metric->value;
+
+  return (plugin_dispatch_values (&vl));
+} /* }}} int statsd_metric_submit_unsafe */
+
+static int statsd_read (void) /* {{{ */
+{
+  c_avl_iterator_t *iter;
+  char *name;
+  statsd_metric_t *metric;
+
+  char **to_be_deleted = NULL;
+  size_t to_be_deleted_num = 0;
+  size_t i;
+
+  pthread_mutex_lock (&metrics_lock);
+
+  if (metrics_tree == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (0);
+  }
+
+  iter = c_avl_get_iterator (metrics_tree);
+  while (c_avl_iterator_next (iter, (void *) &name, (void *) &metric) == 0)
+  {
+    if ((metric->updates_num == 0)
+        && ((conf_delete_counters && (metric->type == STATSD_COUNTER))
+          || (conf_delete_timers && (metric->type == STATSD_TIMER))
+          || (conf_delete_gauges && (metric->type == STATSD_GAUGE))
+          || (conf_delete_sets && (metric->type == STATSD_SET))))
+    {
+      DEBUG ("statsd plugin: Deleting metric \"%s\".", name);
+      strarray_add (&to_be_deleted, &to_be_deleted_num, name);
+      continue;
+    }
+
+    /* Names have a prefix, e.g. "c:", which determines the (statsd) type.
+     * Remove this here. */
+    statsd_metric_submit_unsafe (name + 2, metric);
+
+    /* Reset the metric. */
+    metric->updates_num = 0;
+    if (metric->type == STATSD_SET)
+      statsd_metric_clear_set_unsafe (metric);
+  }
+  c_avl_iterator_destroy (iter);
+
+  for (i = 0; i < to_be_deleted_num; i++)
+  {
+    int status;
+
+    status = c_avl_remove (metrics_tree, to_be_deleted[i],
+        (void *) &name, (void *) &metric);
+    if (status != 0)
+    {
+      ERROR ("stats plugin: c_avl_remove (\"%s\") failed with status %i.",
+          to_be_deleted[i], status);
+      continue;
+    }
+
+    sfree (name);
+    sfree (metric);
+  }
+
+  pthread_mutex_unlock (&metrics_lock);
+
+  strarray_free (to_be_deleted, to_be_deleted_num);
+
+  return (0);
+} /* }}} int statsd_read */
+
+static int statsd_shutdown (void) /* {{{ */
+{
+  void *key;
+  void *value;
+
+  pthread_mutex_lock (&metrics_lock);
+
+  if (network_thread_running)
+  {
+    network_thread_shutdown = 1;
+    pthread_kill (network_thread, SIGTERM);
+    pthread_join (network_thread, /* retval = */ NULL);
+  }
+  network_thread_running = 0;
+
+  while (c_avl_pick (metrics_tree, &key, &value) == 0)
+  {
+    sfree (key);
+    sfree (value);
+  }
+  c_avl_destroy (metrics_tree);
+  metrics_tree = NULL;
+
+  sfree (conf_node);
+  sfree (conf_service);
+
+  pthread_mutex_unlock (&metrics_lock);
+
+  return (0);
+} /* }}} int statsd_shutdown */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("statsd", statsd_config);
+  plugin_register_init ("statsd", statsd_init);
+  plugin_register_read ("statsd", statsd_read);
+  plugin_register_shutdown ("statsd", statsd_shutdown);
+}
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
index 25cbcd4..bcb1572 100644 (file)
@@ -52,22 +52,6 @@ typedef struct ctail_config_match_s ctail_config_match_t;
 cu_tail_match_t **tail_match_list = NULL;
 size_t tail_match_list_num = 0;
 
-static int ctail_config_add_string (const char *name, char **dest, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("tail plugin: `%s' needs exactly one string argument.", name);
-    return (-1);
-  }
-
-  sfree (*dest);
-  *dest = strdup (ci->values[0].value.string);
-  if (*dest == NULL)
-    return (-1);
-
-  return (0);
-} /* int ctail_config_add_string */
-
 static int ctail_config_add_match_dstype (ctail_config_match_t *cm,
     oconfig_item_t *ci)
 {
@@ -158,16 +142,15 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
     oconfig_item_t *option = ci->children + i;
 
     if (strcasecmp ("Regex", option->key) == 0)
-      status = ctail_config_add_string ("Regex", &cm.regex, option);
+      status = cf_util_get_string (option, &cm.regex);
     else if (strcasecmp ("ExcludeRegex", option->key) == 0)
-      status = ctail_config_add_string ("ExcludeRegex", &cm.excluderegex,
-                                       option);
+      status = cf_util_get_string (option, &cm.excluderegex);
     else if (strcasecmp ("DSType", option->key) == 0)
       status = ctail_config_add_match_dstype (&cm, option);
     else if (strcasecmp ("Type", option->key) == 0)
-      status = ctail_config_add_string ("Type", &cm.type, option);
+      status = cf_util_get_string (option, &cm.type);
     else if (strcasecmp ("Instance", option->key) == 0)
-      status = ctail_config_add_string ("Instance", &cm.type_instance, option);
+      status = cf_util_get_string (option, &cm.type_instance);
     else
     {
       WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
@@ -259,7 +242,7 @@ static int ctail_config_add_file (oconfig_item_t *ci)
       status = 0;
     }
     else if (strcasecmp ("Instance", option->key) == 0)
-      status = ctail_config_add_string ("Instance", &plugin_instance, option);
+      status = cf_util_get_string (option, &plugin_instance);
     else
     {
       WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
index d4cfd6e..7df4d61 100644 (file)
@@ -943,12 +943,12 @@ static int ut_missing (const value_list_t *vl,
   char identifier[6 * DATA_MAX_NAME_LEN];
   notification_t n;
 
-  /* dispatch notifications for "interesting" values only */
   if (threshold_tree == NULL)
     return (0);
 
   th = threshold_search (vl);
-  if (th == NULL)
+  /* dispatch notifications for "interesting" values only */
+  if ((th == NULL) || ((th->flags & UT_FLAG_INTERESTING) == 0))
     return (0);
 
   missing_time = cdtime () - vl->time;
index 950f6b0..3a5e207 100644 (file)
@@ -87,9 +87,9 @@ io_packets            rx:DERIVE:0:U, tx:DERIVE:0:U
 ipt_bytes              value:DERIVE:0:U
 ipt_packets            value:DERIVE:0:U
 irq                    value:DERIVE:0:U
-latency                        value:GAUGE:0:65535
+latency                        value:GAUGE:0:U
 links                  value:GAUGE:0:U
-load                   shortterm:GAUGE:0:100, midterm:GAUGE:0:100, longterm:GAUGE:0:100
+load                   shortterm:GAUGE:0:5000, midterm:GAUGE:0:5000, longterm:GAUGE:0:5000
 md_disks               value:GAUGE:0:U
 memcached_command      value:DERIVE:0:U
 memcached_connections  value:GAUGE:0:U
@@ -114,6 +114,8 @@ node_tx_rate                value:GAUGE:0:127
 objects                        value:GAUGE:0:U
 operations             value:DERIVE:0:U
 percent                        value:GAUGE:0:100.1
+percent_bytes          value:GAUGE:0:100.1
+percent_inodes         value:GAUGE:0:100.1
 pf_counters            value:DERIVE:0:U
 pf_limits              value:DERIVE:0:U
 pf_source              value:DERIVE:0:U
@@ -158,10 +160,11 @@ signal_quality            value:GAUGE:0:U
 snr                    value:GAUGE:0:U
 spam_check             value:GAUGE:0:U
 spam_score             value:GAUGE:U:U
+spl                    value:GAUGE:U:U
 swap_io                        value:DERIVE:0:U
 swap                   value:GAUGE:0:1099511627776
 tcp_connections                value:GAUGE:0:4294967295
-temperature            value:GAUGE:-273.15:U
+temperature            value:GAUGE:U:U
 threads                        value:GAUGE:0:U
 time_dispersion                value:GAUGE:-1000000:1000000
 timeleft               value:GAUGE:0:U
index 67f9881..111742b 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 types.db - Data-set specifications for the system statistics collection daemon
diff --git a/src/utils_latency.c b/src/utils_latency.c
new file mode 100644 (file)
index 0000000..94da211
--- /dev/null
@@ -0,0 +1,187 @@
+/**
+ * collectd - src/utils_latency.c
+ * Copyright (C) 2013  Florian Forster
+ *
+ * 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.
+ *
+ * Authors:
+ *   Florian Forster <ff at octo.it>
+ **/
+
+#include "collectd.h"
+#include "utils_latency.h"
+#include "common.h"
+
+#ifndef LATENCY_HISTOGRAM_SIZE
+# define LATENCY_HISTOGRAM_SIZE 1000
+#endif
+
+struct latency_counter_s
+{
+  cdtime_t start_time;
+
+  cdtime_t sum;
+  size_t num;
+
+  cdtime_t min;
+  cdtime_t max;
+
+  int histogram[LATENCY_HISTOGRAM_SIZE];
+};
+
+latency_counter_t *latency_counter_create () /* {{{ */
+{
+  latency_counter_t *lc;
+
+  lc = malloc (sizeof (*lc));
+  if (lc == NULL)
+    return (NULL);
+
+  latency_counter_reset (lc);
+  return (lc);
+} /* }}} latency_counter_t *latency_counter_create */
+
+void latency_counter_destroy (latency_counter_t *lc) /* {{{ */
+{
+  sfree (lc);
+} /* }}} void latency_counter_destroy */
+
+void latency_counter_add (latency_counter_t *lc, cdtime_t latency) /* {{{ */
+{
+  size_t latency_ms;
+
+  if ((lc == NULL) || (latency == 0))
+    return;
+
+  lc->sum += latency;
+  lc->num++;
+
+  if ((lc->min == 0) && (lc->max == 0))
+    lc->min = lc->max = latency;
+  if (lc->min > latency)
+    lc->min = latency;
+  if (lc->max < latency)
+    lc->max = latency;
+
+  /* A latency of _exactly_ 1.0 ms should be stored in the buffer 0, so
+   * subtract one from the cdtime_t value so that exactly 1.0 ms get sorted
+   * accordingly. */
+  latency_ms = (size_t) CDTIME_T_TO_MS (latency - 1);
+  if (latency_ms < STATIC_ARRAY_SIZE (lc->histogram))
+    lc->histogram[latency_ms]++;
+} /* }}} void latency_counter_add */
+
+void latency_counter_reset (latency_counter_t *lc) /* {{{ */
+{
+  if (lc == NULL)
+    return;
+
+  memset (lc, 0, sizeof (*lc));
+  lc->start_time = cdtime ();
+} /* }}} void latency_counter_reset */
+
+cdtime_t latency_counter_get_min (latency_counter_t *lc) /* {{{ */
+{
+  if (lc == NULL)
+    return (0);
+  return (lc->min);
+} /* }}} cdtime_t latency_counter_get_min */
+
+cdtime_t latency_counter_get_max (latency_counter_t *lc) /* {{{ */
+{
+  if (lc == NULL)
+    return (0);
+  return (lc->max);
+} /* }}} cdtime_t latency_counter_get_max */
+
+cdtime_t latency_counter_get_sum (latency_counter_t *lc) /* {{{ */
+{
+  if (lc == NULL)
+    return (0);
+  return (lc->sum);
+} /* }}} cdtime_t latency_counter_get_sum */
+
+size_t latency_counter_get_num (latency_counter_t *lc) /* {{{ */
+{
+  if (lc == NULL)
+    return (0);
+  return (lc->num);
+} /* }}} size_t latency_counter_get_num */
+
+cdtime_t latency_counter_get_average (latency_counter_t *lc) /* {{{ */
+{
+  double average;
+
+  if (lc == NULL)
+    return (0);
+
+  average = CDTIME_T_TO_DOUBLE (lc->sum) / ((double) lc->num);
+  return (DOUBLE_TO_CDTIME_T (average));
+} /* }}} cdtime_t latency_counter_get_average */
+
+cdtime_t latency_counter_get_percentile (latency_counter_t *lc,
+    double percent)
+{
+  double percent_upper;
+  double percent_lower;
+  double ms_upper;
+  double ms_lower;
+  double ms_interpolated;
+  int sum;
+  size_t i;
+
+  if ((lc == NULL) || !((percent > 0.0) && (percent < 100.0)))
+    return (0);
+
+  /* Find index i so that at least "percent" events are within i+1 ms. */
+  percent_upper = 0.0;
+  percent_lower = 0.0;
+  sum = 0;
+  for (i = 0; i < LATENCY_HISTOGRAM_SIZE; i++)
+  {
+    percent_lower = percent_upper;
+    sum += lc->histogram[i];
+    if (sum == 0)
+      percent_upper = 0.0;
+    else
+      percent_upper = 100.0 * ((double) sum) / ((double) lc->num);
+
+    if (percent_upper >= percent)
+      break;
+  }
+
+  if (i >= LATENCY_HISTOGRAM_SIZE)
+    return (0);
+
+  assert (percent_upper >= percent);
+  assert (percent_lower < percent);
+
+  ms_upper = (double) (i + 1);
+  ms_lower = (double) i;
+  if (i == 0)
+    return (MS_TO_CDTIME_T (ms_upper));
+
+  ms_interpolated = (((percent_upper - percent) * ms_lower)
+      + ((percent - percent_lower) * ms_upper))
+    / (percent_upper - percent_lower);
+
+  return (MS_TO_CDTIME_T (ms_interpolated));
+} /* }}} cdtime_t latency_counter_get_percentile */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/utils_latency.h b/src/utils_latency.h
new file mode 100644 (file)
index 0000000..3787c77
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * collectd - src/utils_latency.h
+ * Copyright (C) 2013  Florian Forster
+ *
+ * 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.
+ *
+ * Authors:
+ *   Florian Forster <ff at octo.it>
+ **/
+
+#include "collectd.h"
+#include "utils_time.h"
+
+struct latency_counter_s;
+typedef struct latency_counter_s latency_counter_t;
+
+latency_counter_t *latency_counter_create ();
+void latency_counter_destroy (latency_counter_t *lc);
+
+void latency_counter_add (latency_counter_t *lc, cdtime_t latency);
+void latency_counter_reset (latency_counter_t *lc);
+
+cdtime_t latency_counter_get_min (latency_counter_t *lc);
+cdtime_t latency_counter_get_max (latency_counter_t *lc);
+cdtime_t latency_counter_get_sum (latency_counter_t *lc);
+size_t   latency_counter_get_num (latency_counter_t *lc);
+cdtime_t latency_counter_get_average (latency_counter_t *lc);
+cdtime_t latency_counter_get_percentile (latency_counter_t *lc,
+    double percent);
+
+/* vim: set sw=2 sts=2 et : */
index 1f2403c..83f789b 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef COLLECTD_UTILS_MOUNT_H
 #define COLLECTD_UTILS_MOUNT_H 1
 
+#include <stdio.h>
 #if HAVE_FS_INFO_H
 # include <fs_info.h>
 #endif
index f7bca41..3a8dddd 100644 (file)
@@ -67,7 +67,9 @@ struct user_config_s {
 #endif
        _Bool collect_struct;
        _Bool collect_totals;
+#ifdef HAVE_VARNISH_V3
        _Bool collect_uptime;
+#endif
        _Bool collect_vcl;
        _Bool collect_workers;
 };
@@ -381,10 +383,12 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_gauge (conf->instance, "struct", "current_sessions", "sess",      stats->n_sess);
                /* N struct object         */
                varnish_submit_gauge (conf->instance, "struct", "objects", "object",             stats->n_object);
+#ifdef HAVE_VARNISH_V3
                /* N unresurrected objects */
                varnish_submit_gauge (conf->instance, "struct", "objects", "vampireobject",      stats->n_vampireobject);
                /* N struct objectcore     */
                varnish_submit_gauge (conf->instance, "struct", "objects", "objectcore",         stats->n_objectcore);
+#endif
                /* N struct objecthead     */
                varnish_submit_gauge (conf->instance, "struct", "objects", "objecthead",         stats->n_objecthead);
 #ifdef HAVE_VARNISH_V2
@@ -417,11 +421,13 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance, "totals", "total_bytes", "body-bytes",   stats->s_bodybytes);
        }
 
+#ifdef HAVE_VARNISH_V3
        if (conf->collect_uptime)
        {
                /* Client uptime */
                varnish_submit_gauge (conf->instance, "uptime", "uptime", "client_uptime", stats->uptime);
        }
+#endif
 
        if (conf->collect_vcl)
        {
@@ -569,7 +575,9 @@ static int varnish_config_apply_default (user_config_t *conf) /* {{{ */
        conf->collect_sms         = 0;
        conf->collect_struct      = 0;
        conf->collect_totals      = 0;
+#ifdef HAVE_VARNISH_V3
        conf->collect_uptime      = 0;
+#endif
        conf->collect_vcl         = 0;
        conf->collect_workers     = 0;
 
@@ -691,8 +699,10 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                        cf_util_get_boolean (child, &conf->collect_struct);
                else if (strcasecmp ("CollectTotals", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_totals);
+#ifdef HAVE_VARNISH_V3
                else if (strcasecmp ("CollectUptime", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_uptime);
+#endif
                else if (strcasecmp ("CollectVCL", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_vcl);
                else if (strcasecmp ("CollectWorkers", child->key) == 0)
@@ -700,7 +710,9 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                else
                {
                        WARNING ("Varnish plugin: Ignoring unknown "
-                                       "configuration option: \"%s\"",
+                                       "configuration option: \"%s\". Did "
+                                       "you forget to add an <Instance /> "
+                                       "block around the configuration?",
                                        child->key);
                }
        }
@@ -729,7 +741,9 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
 #endif
                        && !conf->collect_struct
                        && !conf->collect_totals
+#ifdef HAVE_VARNISH_V3
                        && !conf->collect_uptime
+#endif
                        && !conf->collect_vcl
                        && !conf->collect_workers)
        {
index c1a11df..6124d33 100644 (file)
@@ -240,7 +240,6 @@ static int wg_callback_init (struct wg_callback *cb)
                 "write_graphite plugin: Connecting to %s:%s via %s failed. "
                 "The last error was: %s", node, service, protocol,
                 sstrerror (errno, errbuf, sizeof (errbuf)));
-        close (cb->sock_fd);
         return (-1);
     }
     else
@@ -268,8 +267,11 @@ static void wg_callback_free (void *data)
 
     wg_flush_nolock (/* timeout = */ 0, cb);
 
-    close(cb->sock_fd);
-    cb->sock_fd = -1;
+    if (cb->sock_fd >= 0)
+    {
+        close (cb->sock_fd);
+        cb->sock_fd = -1;
+    }
 
     sfree(cb->name);
     sfree(cb->node);
@@ -387,12 +389,9 @@ static int wg_write_messages (const data_set_t *ds, const value_list_t *vl,
         return (status);
 
     /* Send the message to graphite */
-    wg_send_message (buffer, cb);
-    if (status != 0)
-    {
-        /* An error message has already been printed. */
+    status = wg_send_message (buffer, cb);
+    if (status != 0) /* error message has been printed already. */
         return (status);
-    }
 
     return (0);
 } /* int wg_write_messages */
index 7f5943a..25133c3 100644 (file)
@@ -112,7 +112,7 @@ static int wh_callback_init (wh_callback_t *cb) /* {{{ */
         }
 
         curl_easy_setopt (cb->curl, CURLOPT_NOSIGNAL, 1L);
-        curl_easy_setopt (cb->curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+        curl_easy_setopt (cb->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
 
         headers = NULL;
         headers = curl_slist_append (headers, "Accept:  */*");
index 3e2ddd7..3a7738f 100644 (file)
@@ -57,9 +57,13 @@ struct riemann_host {
 
 static char    **riemann_tags;
 static size_t    riemann_tags_num;
+static char    **riemann_attrs;
+static size_t     riemann_attrs_num;
 
 static void riemann_event_protobuf_free (Event *event) /* {{{ */
 {
+       size_t i;
+
        if (event == NULL)
                return;
 
@@ -72,6 +76,15 @@ static void riemann_event_protobuf_free (Event *event) /* {{{ */
        event->tags = NULL;
        event->n_tags = 0;
 
+       for (i = 0; i < event->n_attributes; i++)
+       {
+               sfree (event->attributes[i]->key);
+               sfree (event->attributes[i]->value);
+               sfree (event->attributes[i]);
+       }
+       sfree (event->attributes);
+       event->n_attributes = 0;
+
        sfree (event);
 } /* }}} void riemann_event_protobuf_free */
 
@@ -167,32 +180,30 @@ riemann_disconnect (struct riemann_host *host)
        return (0);
 }
 
-static int
-riemann_send(struct riemann_host *host, Msg const *msg)
+static inline int
+riemann_send_msg(struct riemann_host *host, const Msg *msg)
 {
-       u_char *buffer;
+       int status = 0;
+       u_char *buffer = NULL;
        size_t  buffer_len;
-       int status;
-
-       pthread_mutex_lock (&host->lock);
 
        status = riemann_connect (host);
+
        if (status != 0)
-       {
-               pthread_mutex_unlock (&host->lock);
                return status;
-       }
 
        buffer_len = msg__get_packed_size(msg);
+
        if (host->use_tcp)
                buffer_len += 4;
 
        buffer = malloc (buffer_len);
+
        if (buffer == NULL) {
-               pthread_mutex_unlock (&host->lock);
                ERROR ("write_riemann plugin: malloc failed.");
                return ENOMEM;
        }
+
        memset (buffer, 0, buffer_len);
 
        if (host->use_tcp)
@@ -207,26 +218,105 @@ riemann_send(struct riemann_host *host, Msg const *msg)
        }
 
        status = (int) swrite (host->s, buffer, buffer_len);
+
        if (status != 0)
        {
                char errbuf[1024];
 
-               riemann_disconnect (host);
-               pthread_mutex_unlock (&host->lock);
-
                ERROR ("write_riemann plugin: Sending to Riemann at %s:%s failed: %s",
                                (host->node != NULL) ? host->node : RIEMANN_HOST,
                                (host->service != NULL) ? host->service : RIEMANN_PORT,
                                sstrerror (errno, errbuf, sizeof (errbuf)));
+
                sfree (buffer);
                return -1;
        }
 
-       pthread_mutex_unlock (&host->lock);
        sfree (buffer);
        return 0;
 }
 
+static inline int
+riemann_recv_ack(struct riemann_host *host)
+{
+       int status = 0;
+       Msg *msg = NULL;
+       uint32_t header;
+
+       status = (int) sread (host->s, &header, 4);
+
+       if (status != 0)
+               return -1;
+
+       size_t size = ntohl(header);
+
+       // Buffer on the stack since acknowledges are typically small.
+       u_char buffer[size];
+       memset (buffer, 0, size);
+
+       status = (int) sread (host->s, buffer, size);
+
+       if (status != 0)
+               return status;
+
+       msg = msg__unpack (NULL, size, buffer);
+
+       if (msg == NULL)
+               return -1;
+
+       if (!msg->ok)
+       {
+               ERROR ("write_riemann plugin: Sending to Riemann at %s:%s acknowledgement message reported error: %s",
+                               (host->node != NULL) ? host->node : RIEMANN_HOST,
+                               (host->service != NULL) ? host->service : RIEMANN_PORT,
+                               msg->error);
+
+               msg__free_unpacked(msg, NULL);
+               return -1;
+       }
+
+       msg__free_unpacked (msg, NULL);
+       return 0;
+}
+
+/**
+ * Function to send messages (Msg) to riemann.
+ *
+ * Acquires the host lock, disconnects on errors.
+ */
+static int
+riemann_send(struct riemann_host *host, Msg const *msg)
+{
+       int status = 0;
+       pthread_mutex_lock (&host->lock);
+
+       status = riemann_send_msg(host, msg);
+
+       if (status != 0) {
+               riemann_disconnect (host);
+               pthread_mutex_unlock (&host->lock);
+               return status;
+       }
+
+       /*
+        * For TCP we need to receive message acknowledgemenent.
+        */
+       if (host->use_tcp)
+       {
+               status = riemann_recv_ack(host);
+
+               if (status != 0)
+               {
+                       riemann_disconnect (host);
+                       pthread_mutex_unlock (&host->lock);
+                       return status;
+               }
+       }
+
+       pthread_mutex_unlock (&host->lock);
+       return 0;
+}
+
 static int riemann_event_add_tag (Event *event, char const *tag) /* {{{ */
 {
        return (strarray_add (&event->tags, &event->n_tags, tag));
@@ -334,6 +424,11 @@ static Msg *riemann_notification_to_protobuf (struct riemann_host *host, /* {{{
                riemann_event_add_attribute (event, "type_instance",
                                n->type_instance);
 
+       for (i = 0; i < riemann_attrs_num; i += 2)
+               riemann_event_add_attribute(event,
+                                           riemann_attrs[i],
+                                           riemann_attrs[i +1]);
+
        for (i = 0; i < riemann_tags_num; i++)
                riemann_event_add_tag (event, riemann_tags[i]);
 
@@ -342,15 +437,20 @@ static Msg *riemann_notification_to_protobuf (struct riemann_host *host, /* {{{
                        n->type, n->type_instance);
        event->service = strdup (&service_buffer[1]);
 
-       /* Pull in values from threshold */
+       /* Pull in values from threshold and add extra attributes */
        for (meta = n->meta; meta != NULL; meta = meta->next)
        {
-               if (strcasecmp ("CurrentValue", meta->name) != 0)
+               if (strcasecmp ("CurrentValue", meta->name) == 0 && meta->type == NM_TYPE_DOUBLE)
+               {
+                       event->metric_d = meta->nm_value.nm_double;
+                       event->has_metric_d = 1;
                        continue;
+               }
 
-               event->metric_d = meta->nm_value.nm_double;
-               event->has_metric_d = 1;
-               break;
+               if (meta->type == NM_TYPE_STRING) {
+                       riemann_event_add_attribute (event, meta->name, meta->nm_value.nm_string);
+                       continue;
+               }
        }
 
        DEBUG ("write_riemann plugin: Successfully created protobuf for notification: "
@@ -418,6 +518,11 @@ static Event *riemann_value_to_protobuf (struct riemann_host const *host, /* {{{
                riemann_event_add_attribute (event, "ds_index", ds_index);
        }
 
+       for (i = 0; i < riemann_attrs_num; i += 2)
+               riemann_event_add_attribute(event,
+                                           riemann_attrs[i],
+                                           riemann_attrs[i +1]);
+
        for (i = 0; i < riemann_tags_num; i++)
                riemann_event_add_tag (event, riemann_tags[i]);
 
@@ -752,6 +857,32 @@ riemann_config(oconfig_item_t *ci)
 
                if (strcasecmp("Node", child->key) == 0) {
                        riemann_config_node (child);
+               } else if (strcasecmp(child->key, "attribute") == 0) {
+                       char *key = NULL;
+                       char *val = NULL;
+
+                       if (child->values_num != 2) {
+                               WARNING("riemann attributes need both a key and a value.");
+                               return (-1);
+                       }
+                       if (child->values[0].type != OCONFIG_TYPE_STRING ||
+                           child->values[1].type != OCONFIG_TYPE_STRING) {
+                               WARNING("riemann attribute needs string arguments.");
+                               return (-1);
+                       }
+                       if ((key = strdup(child->values[0].value.string)) == NULL) {
+                               WARNING("cannot allocate memory for attribute key.");
+                               return (-1);
+                       }
+                       if ((val = strdup(child->values[1].value.string)) == NULL) {
+                               WARNING("cannot allocate memory for attribute value.");
+                               return (-1);
+                       }
+                       strarray_add(&riemann_attrs, &riemann_attrs_num, key);
+                       strarray_add(&riemann_attrs, &riemann_attrs_num, val);
+                       DEBUG("write_riemann: got attr: %s => %s", key, val);
+                       sfree(key);
+                       sfree(val);
                } else if (strcasecmp(child->key, "tag") == 0) {
                        char *tmp = NULL;
                        status = cf_util_get_string(child, &tmp);
index 4be9a49..e89e711 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-DEFAULT_VERSION="5.3.0.git"
+DEFAULT_VERSION="5.4.0.git"
 
 VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"