Code

Merge branch 'collectd-5.3'
authorFlorian Forster <octo@collectd.org>
Sun, 18 Aug 2013 10:14:39 +0000 (12:14 +0200)
committerFlorian Forster <octo@collectd.org>
Sun, 18 Aug 2013 10:14:39 +0000 (12:14 +0200)
Conflicts:
configure.in

41 files changed:
.gitignore
AUTHORS
README
clean.sh
configure.in
contrib/collectd_network.py
contrib/collectd_unixsock.py
contrib/redhat/init.d-collectd
src/Makefile.am
src/amqp.c
src/aquaero.c [new file with mode: 0644]
src/cgroups.c [new file with mode: 0644]
src/collectd.conf.in
src/collectd.conf.pod
src/common.c
src/common.h
src/configfile.c
src/configfile.h
src/curl.c
src/curl_json.c
src/curl_xml.c
src/df.c
src/lvm.c [new file with mode: 0644]
src/memcached.c
src/mic.c [new file with mode: 0644]
src/netlink.c
src/nginx.c
src/plugin.c
src/plugin.h
src/rrdtool.c
src/sigrok.c [new file with mode: 0644]
src/statsd.c [new file with mode: 0644]
src/tail.c
src/thermal.c
src/types.db
src/utils_latency.c [new file with mode: 0644]
src/utils_latency.h [new file with mode: 0644]
src/varnish.c
src/write_graphite.c
src/write_riemann.c
src/zfs_arc.c

index 6e87aaf27590797fe199103b5ebb94cd592aea7a..7c7c848fab9bba94e3b0505a8d1efc80026d6eae 100644 (file)
@@ -33,11 +33,12 @@ 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/libcollectdclient/collectd/lcc_features.h
 
 # patch stuff
 *.rej
diff --git a/AUTHORS b/AUTHORS
index 45645d1a26e52700130ed68c6dc7042307f27895..31d132fbfa3c12cf27948ab5e1c9019ddab49adc 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,12 +47,18 @@ 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,
    especially a nasty bug in the network plugin.
  - Wireshark dissector.
 
+Chad Malfait <malfaitc at yahoo.com>
+ - LVM plugin.
+
 Chris Lundquist <clundquist at bluebox.net>
  - Improvements to the write_mongodb plugin.
 
@@ -73,6 +85,9 @@ Doug MacEachern <dougm at hyperic.com>
 Edward “Koko” Konetzko <konetzed at quixoticagony.com>
  - fscache plugin.
 
+Evan Felix <evan.felix at pnnl.gov>
+ - mic plugin.
+
 Fabian Linzberger <e at lefant.net>
  - Percentage aggregation for `collectd-nagios'.
 
@@ -88,6 +103,9 @@ Franck Lombardi
 Jason Pepas <cell at ices.utexas.edu>
  - nfs plugin.
 
+J. Javier Maestro <jjmaestro at ieee.org>
+ - Write-Graphite plugin: UDP support and LogSendErrors flag.
+
 Jérôme Renard <jerome.renard at gmail.com>
  - varnish plugin.
 
@@ -116,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.
@@ -126,6 +149,7 @@ Michael Hanselmann <public at hansmi.ch>
 
 Michael Stapelberg <michael+git at stapelberg.de>
  - OpenBSD port of the tcpconns plugin.
+ - cgroups plugin.
 
 Michał Mirosław <mirq-linux at rere.qmqm.pl>
  - thermal plugin.
@@ -219,6 +243,9 @@ Tommie Gannert <d00-tga at d.kth.se>
 Vincent Stehlé <vincent.stehle at free.fr>
  - hddtemp plugin.
 
+Xin Li <delphij at freebsd.org>
+ - FreeBSD port of the ZFS-ARC plugin.
+
 collectd is available at:
   <http://collectd.org/>
 
diff --git a/README b/README
index c3c454720484f71c3915bf6f0917ce2e27dd93ae..c0d703633d7dce78bace8c356748578ba6e22a4a 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'.
 
@@ -37,6 +40,9 @@ Features
       Name server and resolver statistics from the `statistics-channel'
       interface of BIND 9.5, 9,6 and later.
 
+    - cgroups
+      CPU accounting information for process groups under Linux.
+
     - conntrack
       Number of nf_conntrack entries.
 
@@ -135,6 +141,10 @@ Features
     - libvirt
       CPU, memory, disk and network I/O statistics from virtual machines.
 
+    - lvm
+      Size of “Logical Volumes” (LV) and “Volume Groups” (VG) of Linux'
+      “Logical Volume Manager” (LVM).
+
     - madwifi
       Queries very detailed usage statistics from wireless LAN adapters and
       interfaces that use the Atheros chipset and the MadWifi driver.
@@ -158,6 +168,10 @@ Features
       Memory utilization: Memory occupied by running processes, page cache,
       buffer cache and free.
 
+    - mic
+      Collects CPU usage, memory usage, temperatures and power consumption from
+      Intel Many Integrated Core (MIC) CPUs.
+
     - modbus
       Reads values from Modbus/TCP enabled devices. Supports reading values
       from multiple "slaves" so gateway devices can be used.
@@ -269,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
@@ -376,7 +395,9 @@ Features
       done.
 
     - write_graphite
-      Sends data to Carbon, the storage layer of Graphite.
+      Sends data to Carbon, the storage layer of Graphite using TCP or UDP. It
+      can be configured to avoid logging send errors (especially useful when
+      using UDP).
 
     - write_http
       Sends the values collected by collectd to a web-server using HTTP POST
@@ -548,6 +569,14 @@ Prerequisites
     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.
@@ -605,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
@@ -619,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/>
@@ -688,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 9defb05d5a1f93297e19e4cff350a3b31b0f70ab..46e8c29e54897fe14dd7c25894f7e98de6478ded 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 d207c2889ba34c4b34d957f49f742f72793e9a0d..c23df178ca1ac0c9dd464894ca1fb407bd32fb78 100644 (file)
@@ -78,6 +78,10 @@ case $host_os in
        AC_DEFINE([KERNEL_AIX], 1, [True if program is to be compiled for a AIX kernel])
        ac_system="AIX"
        ;;
+       *freebsd*)
+       AC_DEFINE([KERNEL_FREEBSD], 1, [True if program is to be compiled for a FreeBSD kernel])
+       ac_system="FreeBSD"
+       ;;
        *)
        ac_system="unknown"
 esac
@@ -1362,6 +1366,8 @@ fi
 m4_divert_once([HELP_WITH], [
 collectd additional packages:])
 
+AM_CONDITIONAL([BUILD_FREEBSD],[test "x$x$ac_system" = "xFreeBSD"])
+
 AM_CONDITIONAL([BUILD_AIX],[test "x$x$ac_system" = "xAIX"]) 
 
 if test "x$ac_system" = "xAIX"
@@ -1474,6 +1480,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.])],
 [
@@ -2152,6 +2215,58 @@ AC_SUBST(JAVA_LIBS)
 AM_CONDITIONAL(BUILD_WITH_JAVA, test "x$with_java" = "xyes")
 # }}}
 
+# --with-liblvm2app {{{
+with_liblvm2app_cppflags=""
+with_liblvm2app_ldflags=""
+AC_ARG_WITH(liblvm2app, [AS_HELP_STRING([--with-liblvm2app@<:@=PREFIX@:>@], [Path to liblvm2app.])],
+[
+        if test "x$withval" != "xno" && test "x$withval" != "xyes"
+        then
+                with_liblvm2app_cppflags="-I$withval/include"
+                with_liblvm2app_ldflags="-L$withval/lib"
+                with_liblvm2app="yes"
+        else
+                with_liblvm2app="$withval"
+        fi
+],
+[
+        with_liblvm2app="yes"
+])
+if test "x$with_liblvm2app" = "xyes"
+then
+        SAVE_CPPFLAGS="$CPPFLAGS"
+        CPPFLAGS="$CPPFLAGS $with_liblvm2app_cppflags"
+
+        AC_CHECK_HEADERS(lvm2app.h, [with_liblvm2app="yes"], [with_liblvm2app="no (lvm2app.h not found)"])
+
+        CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+if test "x$with_liblvm2app" = "xyes"
+then
+        SAVE_CPPFLAGS="$CPPFLAGS"
+        SAVE_LDFLAGS="$LDFLAGS"
+        CPPFLAGS="$CPPFLAGS $with_liblvm2app_cppflags"
+        LDFLAGS="$LDFLAGS $with_liblvm2app_ldflags"
+
+        AC_CHECK_LIB(lvm2app, lvm_init, [with_liblvm2app="yes"], [with_liblvm2app="no (Symbol 'lvm_init' not found)"])
+
+        CPPFLAGS="$SAVE_CPPFLAGS"
+        LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_liblvm2app" = "xyes"
+then
+        BUILD_WITH_LIBLVM2APP_CPPFLAGS="$with_liblvm2app_cppflags"
+        BUILD_WITH_LIBLVM2APP_LDFLAGS="$with_liblvm2app_ldflags"
+        BUILD_WITH_LIBLVM2APP_LIBS="-llvm2app"
+        AC_SUBST(BUILD_WITH_LIBLVM2APP_CPPFLAGS)
+        AC_SUBST(BUILD_WITH_LIBLVM2APP_LDFLAGS)
+        AC_SUBST(BUILD_WITH_LIBLVM2APP_LIBS)
+        AC_DEFINE(HAVE_LIBLVM2APP, 1, [Define if liblvm2app is present and usable.])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBLVM2APP, test "x$with_liblvm2app" = "xyes")
+# }}}
+
 # --with-libmemcached {{{
 with_libmemcached_cppflags=""
 with_libmemcached_ldflags=""
@@ -2443,24 +2558,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
@@ -2469,21 +2584,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>
@@ -2499,126 +2618,53 @@ 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_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_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_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 {{{
@@ -3709,6 +3755,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=""
@@ -4152,6 +4266,63 @@ fi
 AM_CONDITIONAL(BUILD_WITH_LIBYAJL, test "x$with_libyajl" = "xyes")
 # }}}
 
+# --with-mic {{{
+with_mic_cflags="-I/opt/intel/mic/sysmgmt/sdk/include"
+with_mic_ldpath="-L/opt/intel/mic/sysmgmt/sdk/lib/Linux"
+with_mic_libs=""
+AC_ARG_WITH(mic,[AS_HELP_STRING([--with-mic@<:@=PREFIX@:>@], [Path to Intel MIC Access API.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_mic="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_mic="yes"
+       else if test -d "$with_mic/lib"
+       then
+               AC_MSG_NOTICE([Not checking for Intel Mic: Manually configured])
+               with_mic_cflags="-I$withval/include"
+               with_mic_ldpath="-L$withval/lib/Linux"
+               with_mic_libs="-lMicAccessSDK -lscif -lpthread"
+               with_mic="yes"
+       fi; fi; fi
+],
+[with_mic="yes"])
+if test "x$with_mic" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_mic_cflags"
+       AC_CHECK_HEADERS(MicAccessApi.h,[],[with_mic="no (MicAccessApi not found)"])
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_mic" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CPPFLAGS="$CPPFLAGS $with_mic_cflags"
+       LDFLAGS="$LDFLAGS $with_mic_ldpath"
+
+       AC_CHECK_LIB(MicAccessSDK, MicInitAPI,
+                       [with_mic_ldpath="$with_mic_ldpath"
+                       with_mic_libs="-lMicAccessSDK -lscif -lpthread"],
+                       [with_mic="no (symbol MicInitAPI not found)"],[-lscif -lpthread])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+
+if test "x$with_mic" = "xyes"
+then
+       BUILD_WITH_MIC_CPPFLAGS="$with_mic_cflags"
+       BUILD_WITH_MIC_LIBPATH="$with_mic_ldpath"
+       BUILD_WITH_MIC_LDADD="$with_mic_libs"
+       AC_SUBST(BUILD_WITH_MIC_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_MIC_LIBPATH)
+       AC_SUBST(BUILD_WITH_MIC_LDADD)
+fi
+#}}}
+
 # --with-libvarnish {{{
 with_libvarnish_cppflags=""
 with_libvarnish_cflags=""
@@ -4578,6 +4749,7 @@ dependency_error="no"
 plugin_ascent="no"
 plugin_battery="no"
 plugin_bind="no"
+plugin_cgroups="no"
 plugin_conntrack="no"
 plugin_contextswitch="no"
 plugin_cpu="no"
@@ -4621,6 +4793,7 @@ then
        plugin_battery="yes"
        plugin_conntrack="yes"
        plugin_contextswitch="yes"
+       plugin_cgroups="yes"
        plugin_cpu="yes"
        plugin_cpufreq="yes"
        plugin_disk="yes"
@@ -4629,6 +4802,7 @@ then
        plugin_interface="yes"
        plugin_irq="yes"
        plugin_load="yes"
+       plugin_lvm="yes"
        plugin_memory="yes"
        plugin_nfs="yes"
        plugin_numa="yes"
@@ -4668,6 +4842,14 @@ then
         plugin_tcpconns="yes"
 fi
 
+# FreeBSD
+
+if test "x$ac_system" = "xFreeBSD"
+then
+        plugin_zfs_arc="yes"
+fi
+
+
 if test "x$with_perfstat" = "xyes"
 then
        plugin_cpu="yes"
@@ -4892,6 +5074,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])
@@ -4903,6 +5086,7 @@ AC_PLUGIN([csv],         [yes],                [CSV output plugin])
 AC_PLUGIN([curl],        [$with_libcurl],      [CURL generic web statistics])
 AC_PLUGIN([curl_json],   [$plugin_curl_json],    [CouchDB statistics])
 AC_PLUGIN([curl_xml],   [$plugin_curl_xml],    [CURL generic xml statistics])
+AC_PLUGIN([cgroups],     [$plugin_cgroups],    [CGroups CPU usage accounting])
 AC_PLUGIN([dbi],         [$with_libdbi],       [General database statistics])
 AC_PLUGIN([df],          [$plugin_df],         [Filesystem usage statistics])
 AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
@@ -4925,6 +5109,7 @@ AC_PLUGIN([libvirt],     [$plugin_libvirt],    [Virtual machine statistics])
 AC_PLUGIN([load],        [$plugin_load],       [System load])
 AC_PLUGIN([logfile],     [yes],                [File logging plugin])
 AC_PLUGIN([lpar],        [$with_perfstat],     [AIX logical partitions statistics])
+AC_PLUGIN([lvm],         [$with_liblvm2app],   [LVM statistics])
 AC_PLUGIN([madwifi],     [$have_linux_wireless_h], [Madwifi wireless statistics])
 AC_PLUGIN([match_empty_counter], [yes],        [The empty counter match])
 AC_PLUGIN([match_hashed], [yes],               [The hashed match])
@@ -4936,11 +5121,12 @@ AC_PLUGIN([md],          [$have_linux_raid_md_u_h], [md (Linux software RAID) de
 AC_PLUGIN([memcachec],   [$with_libmemcached], [memcachec statistics])
 AC_PLUGIN([memcached],   [yes],                [memcached statistics])
 AC_PLUGIN([memory],      [$plugin_memory],     [Memory usage])
+AC_PLUGIN([mic],         [$with_mic],          [Intel Many Integrated Core stats])
 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])
@@ -4969,7 +5155,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])
@@ -5171,6 +5359,8 @@ cat <<EOF;
 
 Configuration:
   Libraries:
+    intel mic . . . . . . $with_mic
+    libaquaero5 . . . . . $with_libaquaero5
     libcurl . . . . . . . $with_libcurl
     libdbi  . . . . . . . $with_libdbi
     libcredis . . . . . . $with_libcredis
@@ -5183,10 +5373,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
@@ -5201,6 +5391,7 @@ Configuration:
     librouteros . . . . . $with_librouteros
     librrd  . . . . . . . $with_librrd
     libsensors  . . . . . $with_libsensors
+    libsigrok   . . . . . $with_libsigrok
     libstatgrab . . . . . $with_libstatgrab
     libtokyotyrant  . . . $with_libtokyotyrant
     libupsclient  . . . . $with_libupsclient
@@ -5226,12 +5417,14 @@ Configuration:
     amqp    . . . . . . . $enable_amqp
     apache  . . . . . . . $enable_apache
     apcups  . . . . . . . $enable_apcups
+    aquaero . . . . . . . $enable_aquaero
     apple_sensors . . . . $enable_apple_sensors
     ascent  . . . . . . . $enable_ascent
     battery . . . . . . . $enable_battery
     bind  . . . . . . . . $enable_bind
     conntrack . . . . . . $enable_conntrack
     contextswitch . . . . $enable_contextswitch
+    cgroups . . . . . . . $enable_cgroups
     cpu . . . . . . . . . $enable_cpu
     cpufreq . . . . . . . $enable_cpufreq
     csv . . . . . . . . . $enable_csv
@@ -5260,6 +5453,7 @@ Configuration:
     load  . . . . . . . . $enable_load
     logfile . . . . . . . $enable_logfile
     lpar  . . . . . . . . $enable_lpar
+    lvm . . . . . . . . . $enable_lvm
     madwifi . . . . . . . $enable_madwifi
     match_empty_counter . $enable_match_empty_counter
     match_hashed  . . . . $enable_match_hashed
@@ -5303,7 +5497,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 9af0fb008a72790e48562e7e6440c3bec8c6dd2a..809f19dee65d2ec89a030a762bacb5e98041d8aa 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 1b8e6b174ed8d8ad1a7abc1237842e03d5908075..5cd4ab8ee6de5d7ba3ebadd83418765dea9d39e5 100644 (file)
@@ -158,7 +158,7 @@ class Collectd():
         self._sock.send(c + "\n")
         status_message = self._readline()
         if self.noisy:
-            print "[recive] %s" % status_message
+            print "[receive] %s" % status_message
         if not status_message:
             return None
         code, message = status_message.split(' ', 1)
index ec55a52a55d75e68613e06b35e989601d0d25bd5..abdb168f15d28cce78db59ca29589d2ef0a40e5d 100644 (file)
@@ -18,12 +18,18 @@ prog="collectdmon"
 service="collectd"
 CONFIG=/etc/collectd.conf
 COLLECTD=/usr/sbin/collectd
-COLLECTDMONPID=/var/run/collectdmon.pid
+COLLECTDMONPIDDIR="/var/run"
+COLLECTDMONPID="$COLLECTDMONPIDDIR/collectdmon.pid"
 
 if [ -r /etc/sysconfig/$service ]; then
        . /etc/sysconfig/$service
 fi
 
+if [[ ! -d $COLLECTDMONPIDDIR ]]; then
+       mkdir -p $COLLECTDMONPIDDIR
+       [ -n "${RUNAS}" ] && chown "${RUNAS}:" "$COLLECTDMONPIDDIR"
+fi
+
 check_config() {
         if test ! -r "$CONFIG"; then
                 return 2
@@ -44,7 +50,7 @@ start () {
                echo $"not starting due to configuration error"
                failure $"not starting $service due to configuration error"
        else
-               daemon $prog -P $COLLECTDMONPID -c $COLLECTD -- -C "$CONFIG" $ARGS
+               daemon --user "${RUNAS:-root}" $prog -P $COLLECTDMONPID -c $COLLECTD -- -C "$CONFIG" $ARGS
                RETVAL=$?
                echo
                [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$service
@@ -52,7 +58,7 @@ start () {
 }
 stop () {
        echo -n $"Stopping collectd: "
-       killproc $prog
+       killproc -p $COLLECTDMONPID $prog
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$service
@@ -66,7 +72,7 @@ case "$1" in
        stop
        ;;
   status)
-       status $prog
+       status -p $COLLECTDMONPID $prog
        ;;
   restart|reload)
        check_config
@@ -81,7 +87,7 @@ case "$1" in
        fi
        ;;
   condrestart)
-       [ -f /var/lock/subsys/$prog ] && restart || :
+       [ -f /var/lock/subsys/$service ] && restart || :
        ;;
   *)
        echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
index 2bebec55c6d88ff320f94689c4eb795ead97a7cf..59454dc09fc12109219593098780f6e5fe29a016 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
@@ -230,6 +240,14 @@ collectd_LDADD += "-dlopen" bind.la
 collectd_DEPENDENCIES += bind.la
 endif
 
+if BUILD_PLUGIN_CGROUPS
+pkglib_LTLIBRARIES += cgroups.la
+cgroups_la_SOURCES = cgroups.c utils_mount.c utils_mount.h
+cgroups_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" cgroups.la
+collectd_DEPENDENCIES += cgroups.la
+endif
+
 if BUILD_PLUGIN_CONNTRACK
 pkglib_LTLIBRARIES += conntrack.la
 conntrack_la_SOURCES = conntrack.c
@@ -568,6 +586,15 @@ collectd_DEPENDENCIES += lpar.la
 lpar_la_LIBADD = -lperfstat
 endif
 
+if BUILD_PLUGIN_LVM
+pkglib_LTLIBRARIES += lvm.la
+lvm_la_SOURCES = lvm.c
+lvm_la_LDFLAGS = -module -avoid-version
+lvm_la_LIBADD = $(BUILD_WITH_LIBLVM2APP_LIBS)
+collectd_LDADD += "-dlopen" lvm.la
+collectd_DEPENDENCIES += lvm.la
+endif
+
 if BUILD_PLUGIN_MADWIFI
 pkglib_LTLIBRARIES += madwifi.la
 madwifi_la_SOURCES = madwifi.c madwifi.h
@@ -727,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
@@ -1035,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
@@ -1052,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
@@ -1271,6 +1318,16 @@ collectd_LDADD += "-dlopen" uuid.la
 collectd_DEPENDENCIES += uuid.la
 endif
 
+if BUILD_PLUGIN_MIC
+pkglib_LTLIBRARIES += mic.la
+mic_la_SOURCES = mic.c
+mic_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_MIC_LIBPATH)
+mic_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_MIC_CPPFLAGS)
+mic_la_LIBADD = $(BUILD_WITH_MIC_LDADD)
+collectd_LDADD += "-dlopen" mic.la
+collectd_DEPENDENCIES += mic.la
+endif
+
 if BUILD_PLUGIN_VARNISH
 pkglib_LTLIBRARIES += varnish.la
 varnish_la_SOURCES = varnish.c
@@ -1375,7 +1432,11 @@ pkglib_LTLIBRARIES += zfs_arc.la
 zfs_arc_la_SOURCES = zfs_arc.c
 zfs_arc_la_CFLAGS = $(AM_CFLAGS)
 zfs_arc_la_LDFLAGS = -module -avoid-version
+if BUILD_FREEBSD
+zfs_arc_la_LIBADD = -lm
+else
 zfs_arc_la_LIBADD = -lkstat
+endif
 collectd_LDADD += "-dlopen" zfs_arc.la
 collectd_DEPENDENCIES += zfs_arc.la
 endif
index 767a8776bbfb07ce2a26f932cf7fb5da4d22d047..edd4f749396bbb14c501792b62db70d188ade0de 100644 (file)
@@ -877,6 +877,7 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
     /* publish only */
     conf->delivery_mode = CAMQP_DM_VOLATILE;
     conf->store_rates = 0;
+    conf->graphite_flags = 0;
     /* publish & graphite only */
     conf->prefix = NULL;
     conf->postfix = NULL;
@@ -942,6 +943,12 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
         }
         else if ((strcasecmp ("Format", child->key) == 0) && publish)
             status = camqp_config_set_format (child, conf);
+        else if ((strcasecmp ("GraphiteSeparateInstances", child->key) == 0) && publish)
+            status = cf_util_get_flag (child, &conf->graphite_flags,
+                    GRAPHITE_SEPARATE_INSTANCES);
+        else if ((strcasecmp ("GraphiteAlwaysAppendDS", child->key) == 0) && publish)
+            status = cf_util_get_flag (child, &conf->graphite_flags,
+                    GRAPHITE_ALWAYS_APPEND_DS);
         else if ((strcasecmp ("GraphitePrefix", child->key) == 0) && publish)
             status = cf_util_get_string (child, &conf->prefix);
         else if ((strcasecmp ("GraphitePostfix", child->key) == 0) && publish)
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 : */
diff --git a/src/cgroups.c b/src/cgroups.c
new file mode 100644 (file)
index 0000000..ffb1740
--- /dev/null
@@ -0,0 +1,251 @@
+/**
+ * collectd - src/cgroups.c
+ * Copyright (C) 2011  Michael Stapelberg
+ * Copyright (C) 2013  Florian Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the license is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Michael Stapelberg <michael at stapelberg.de>
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_mount.h"
+#include "utils_ignorelist.h"
+
+static char const *config_keys[] =
+{
+       "CGroup",
+       "IgnoreSelected"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *il_cgroup = NULL;
+
+__attribute__ ((nonnull(1)))
+__attribute__ ((nonnull(2)))
+static void cgroups_submit_one (char const *plugin_instance,
+               char const *type_instance, value_t value)
+{
+       value_list_t vl = VALUE_LIST_INIT;
+
+       vl.values = &value;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "cgroups", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, plugin_instance,
+                       sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "cpu", sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance,
+                       sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* void cgroups_submit_one */
+
+/*
+ * This callback reads the user/system CPU time for each cgroup.
+ */
+static int read_cpuacct_procs (const char *dirname, char const *cgroup_name,
+    void *user_data)
+{
+       char abs_path[PATH_MAX];
+       struct stat statbuf;
+       char buf[1024];
+       int status;
+
+       FILE *fh;
+
+       if (ignorelist_match (il_cgroup, cgroup_name))
+               return (0);
+
+       ssnprintf (abs_path, sizeof (abs_path), "%s/%s", dirname, cgroup_name);
+
+       status = lstat (abs_path, &statbuf);
+       if (status != 0)
+       {
+               ERROR ("cgroups plugin: stat (\"%s\") failed.",
+                               abs_path);
+               return (-1);
+       }
+
+       /* We are only interested in directories, so skip everything else. */
+       if (!S_ISDIR (statbuf.st_mode))
+               return (0);
+
+       ssnprintf (abs_path, sizeof (abs_path), "%s/%s/cpuacct.stat",
+                       dirname, cgroup_name);
+       fh = fopen (abs_path, "r");
+       if (fh == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("cgroups pluign: fopen (\"%s\") failed: %s",
+                               abs_path,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       while (fgets (buf, sizeof (buf), fh) != NULL)
+       {
+               char *fields[8];
+               int numfields = 0;
+               char *key;
+               size_t key_len;
+               value_t value;
+
+               /* Expected format:
+                *
+                *   user: 12345
+                *   system: 23456
+                */
+               strstripnewline (buf);
+               numfields = strsplit (buf, fields, STATIC_ARRAY_SIZE (fields));
+               if (numfields != 2)
+                       continue;
+
+               key = fields[0];
+               key_len = strlen (key);
+               if (key_len < 2)
+                       continue;
+
+               /* Strip colon off the first column */
+               if (key[key_len - 1] != ':')
+                       continue;
+               key[key_len - 1] = 0;
+
+               status = parse_value (fields[1], &value, DS_TYPE_DERIVE);
+               if (status != 0)
+                       continue;
+
+               cgroups_submit_one (cgroup_name, key, value);
+       }
+
+       fclose (fh);
+       return (0);
+} /* int read_cpuacct_procs */
+
+/*
+ * Gets called for every file/folder in /sys/fs/cgroup/cpu,cpuacct (or
+ * whereever cpuacct is mounted on the system). Calls walk_directory with the
+ * read_cpuacct_procs callback on every folder it finds, such as "system".
+ */
+static int read_cpuacct_root (const char *dirname, const char *filename,
+               void *user_data)
+{
+       char abs_path[PATH_MAX];
+       struct stat statbuf;
+       int status;
+
+       ssnprintf (abs_path, sizeof (abs_path), "%s/%s", dirname, filename);
+
+       status = lstat (abs_path, &statbuf);
+       if (status != 0)
+       {
+               ERROR ("cgroups plugin: stat (%s) failed.", abs_path);
+               return (-1);
+       }
+
+       if (S_ISDIR (statbuf.st_mode))
+       {
+               status = walk_directory (abs_path, read_cpuacct_procs,
+                               /* user_data = */ NULL,
+                               /* include_hidden = */ 0);
+               return (status);
+       }
+
+       return (0);
+}
+
+static int cgroups_init (void)
+{
+       if (il_cgroup == NULL)
+               il_cgroup = ignorelist_create (1);
+
+       return (0);
+}
+
+static int cgroups_config (const char *key, const char *value)
+{
+       cgroups_init ();
+
+       if (strcasecmp (key, "CGroup") == 0)
+       {
+               if (ignorelist_add (il_cgroup, value))
+                       return (1);
+               return (0);
+       }
+       else if (strcasecmp (key, "IgnoreSelected") == 0)
+       {
+               if (IS_TRUE (value))
+                       ignorelist_set_invert (il_cgroup, 0);
+               else
+                       ignorelist_set_invert (il_cgroup, 1);
+               return (0);
+       }
+
+       return (-1);
+}
+
+static int cgroups_read (void)
+{
+       cu_mount_t *mnt_list;
+       cu_mount_t *mnt_ptr;
+       _Bool cgroup_found = 0;
+
+       mnt_list = NULL;
+       if (cu_mount_getlist (&mnt_list) == NULL)
+       {
+               ERROR ("cgroups plugin: cu_mount_getlist failed.");
+               return (-1);
+       }
+
+       for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next)
+       {
+               /* Find the cgroup mountpoint which contains the cpuacct
+                * controller. */
+               if (strcmp(mnt_ptr->type, "cgroup") != 0 ||
+                       !cu_mount_getoptionvalue(mnt_ptr->options, "cpuacct"))
+                       continue;
+
+               walk_directory (mnt_ptr->dir, read_cpuacct_root,
+                               /* user_data = */ NULL,
+                               /* include_hidden = */ 0);
+               cgroup_found = 1;
+               /* It doesn't make sense to check other cpuacct mount-points
+                * (if any), they contain the same data. */
+               break;
+       }
+
+       cu_mount_freelist (mnt_list);
+
+       if (!cgroup_found)
+       {
+               WARNING ("cgroups plugin: Unable to find cgroup "
+                               "mount-point with the \"cpuacct\" option.");
+               return (-1);
+       }
+
+       return (0);
+} /* int cgroup_read */
+
+void module_register (void)
+{
+       plugin_register_config ("cgroups", cgroups_config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("cgroups", cgroups_init);
+       plugin_register_read ("cgroups", cgroups_read);
+} /* void module_register */
index 5af15684092acb486b57d90023f7203da1a9abaa..d4d5a205b89c7b3c784033c5083ac3f75a336f98 100644 (file)
 #PluginDir   "@libdir@/@PACKAGE_NAME@"
 #TypesDB     "@prefix@/share/@PACKAGE_NAME@/types.db"
 
+#----------------------------------------------------------------------------#
+# When enabled, plugins are loaded automatically with the default options    #
+# when an appropriate <Plugin ...> block is encountered.                     #
+# Disabled by default.                                                       #
+#----------------------------------------------------------------------------#
+#AutoLoadPlugin false
+
 #----------------------------------------------------------------------------#
 # Interval at which to query values. This may be overwritten on a per-plugin #
 # base by using the 'Interval' option of the LoadPlugin block:               #
 #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                                                                    #
 #----------------------------------------------------------------------------#
 #@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
 #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack
 #@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch
+#@BUILD_PLUGIN_CGROUPS_TRUE@LoadPlugin cgroups
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 #@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq
 @LOAD_PLUGIN_CSV@LoadPlugin csv
 #@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt
 @BUILD_PLUGIN_LOAD_TRUE@@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load
 #@BUILD_PLUGIN_LPAR_TRUE@LoadPlugin lpar
+#@BUILD_PLUGIN_LVM_TRUE@LoadPlugin lvm
 #@BUILD_PLUGIN_MADWIFI_TRUE@LoadPlugin madwifi
 #@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon
 #@BUILD_PLUGIN_MD_TRUE@LoadPlugin md
 @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
 #@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
 #@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
 #@BUILD_PLUGIN_VARNISH_TRUE@LoadPlugin varnish
+#@BUILD_PLUGIN_MIC_TRUE@LoadPlugin mic
 #@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
 #@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
 #@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless
 #      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
 #      UUIDFile "/etc/uuid"
 #</Plugin>
 
+#<Plugin mic>
+#   ShowCPU true
+#   ShowCPUCores true
+#   ShowMemory true
+#   ShowTemperatures true
+##  Temperature Sensors can be ignored/shown by repeated #Temperature lines, and
+##  then inverted with a IgnoreSelectedTemperature.
+##  Known Temperature sensors: die, devmem, fin, fout, vccp, vddg, vddq
+#   Temperature vddg
+#   IgnoreSelectedTemperature true
+#   ShowPower true
+##  Power Sensors can be ignored/shown by repeated #Power lines, and
+##  then inverted with a IgnoreSelectedTemperature.
+##  Known Temperature sensors: total0, total1, inst, imax, pci3, c2x3, c2x4, vccp, vddg, vddq
+#   Power total1
+#   IgnoreSelectedPower true
+#</Plugin>
+
 #<Plugin varnish>
 #   This tag support an argument if you want to
 #   monitor the local instance just use </Instance>
 #   <Instance>
 #      CollectCache true
 #      CollectBackend true
+#      CollectBan false           # Varnish 3 only
 #      CollectConnections true
+#      CollectDirectorDNS false   # Varnish 3 only
 #      CollectSHM true
 #      CollectESI false
 #      CollectFetch false
 #      CollectHCB false
-#      CollectSMA false
+#      CollectObjects false
+#      CollectPurge false         # Varnish 2 only
+#      CollectSession false
+#      CollectSMA false           # Varnish 2 only
 #      CollectSMS false
-#      CollectSM false
+#      CollectSM false            # Varnish 2 only
+#      CollectStruct false
 #      CollectTotals false
+#      CollectUptime false
+#      CollectVCL false
 #      CollectWorkers false
 #   </Instance>
 #</Plugin>
 #  <Node "example">
 #    Host "localhost"
 #    Port "2003"
+#    Protocol "udp"
+#    LogSendErrors true
 #    Prefix "collectd"
 #    Postfix "collectd"
 #    StoreRates true
index 0a2a5f7790de1ca30e5b93aa714d97af5073b3d0..691a7494fecf3b521cf3afb46dee4c7e85a9f4a3 100644 (file)
@@ -70,17 +70,33 @@ directory for the daemon.
 
 =item B<LoadPlugin> I<Plugin>
 
-Loads the plugin I<Plugin>. There must be at least one such line or B<collectd>
-will be mostly useless.
+Loads the plugin I<Plugin>. This is required to load plugins, unless the
+B<AutoLoadPlugin> option is enabled (see below). Without any loaded plugins,
+I<collectd> will be mostly useless.
 
-Starting with collectd 4.9, this may also be a block in which further options
-affecting the behavior of B<LoadPlugin> may be specified. The following
-options are allowed inside a B<LoadPlugin> block:
+Only the first B<LoadPlugin> statement or block for a given plugin name has any
+effect. This is useful when you want to split up the configuration into smaller
+files and want each file to be "self contained", i.e. it contains a B<Plugin>
+block I<and> then appropriate B<LoadPlugin> statement. The downside is that if
+you have multiple conflicting B<LoadPlugin> blocks, e.g. when they specify
+different intervals, only one of them (the first one encountered) will take
+effect and all others will be silently ignored.
 
-  <LoadPlugin perl>
-    Globals true
-    Interval 10
-  </LoadPlugin>
+B<LoadPlugin> may either be a simple configuration I<statement> or a I<block>
+with additional options, affecting the behavior of B<LoadPlugin>. A simple
+statement looks like this:
+
+ LoadPlugin "cpu"
+
+Options inside a B<LoadPlugin> block can override default settings and
+influence the way plugins are loaded, e.g.:
+
+ <LoadPlugin perl>
+   Globals true
+   Interval 60
+ </LoadPlugin>
+
+The following options are valid inside B<LoadPlugin> blocks:
 
 =over 4
 
@@ -111,7 +127,20 @@ interval, that setting will take precedence.
 
 =back
 
-=item B<Include> I<Path>
+=item B<AutoLoadPlugin> B<false>|B<true>
+
+When set to B<false> (the default), each plugin needs to be loaded explicitly,
+using the B<LoadPlugin> statement documented above. If a
+B<E<lt>PluginE<nbsp>...E<gt>> block is encountered and no configuration
+handling callback for this plugin has been registered, a warning is logged and
+the block is ignored.
+
+When set to B<true>, explicit B<LoadPlugin> statements are not required. Each
+B<E<lt>PluginE<nbsp>...E<gt>> block acts as if it was immediately preceded by a
+B<LoadPlugin> statement. B<LoadPlugin> statements are still required for
+plugins that don't provide any configuration, e.g. the I<Load plugin>.
+
+=item B<Include> I<Path> [I<pattern>]
 
 If I<Path> points to a file, includes that file. If I<Path> points to a
 directory, recursively includes all files within that directory and its
@@ -206,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
@@ -671,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
@@ -868,6 +950,29 @@ By default no detailed zone information is collected.
 
 =back
 
+=head2 Plugin C<cgroups>
+
+This plugin collects the CPU user/system time for each I<cgroup> by reading the
+F<cpuacct.stat> files in the first cpuacct-mountpoint (typically
+F</sys/fs/cgroup/cpu.cpuacct> on machines using systemd).
+
+=over 4
+
+=item B<CGroup> I<Directory>
+
+Select I<cgroup> based on the name. Whether only matching I<cgroups> are
+collected or if they are ignored is controlled by the B<IgnoreSelected> option;
+see below.
+
+=item B<IgnoreSelected> B<true>|B<false>
+
+Invert the selection: If set to true, all cgroups I<except> the ones that
+match any one of the criteria are collected. By default only selected
+cgroups are collected if a selection is made. If no selection is configured
+at all, B<all> cgroups are selected.
+
+=back
+
 =head2 Plugin C<cpufreq>
 
 This plugin doesn't have any options. It reads
@@ -990,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>
@@ -1016,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:
 
@@ -1475,6 +1601,20 @@ 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<ValuesAbsolute> B<true>|B<false>
+
+Enables or disables reporting of free, used and used disk space in 1K-blocks. 
+Defaults to true.
+
+=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
 
 =head2 Plugin C<disk>
@@ -2223,7 +2363,7 @@ interpreted. For a description of match blocks, please see L<"Plugin tail">.
 
 =head2 Plugin C<memcached>
 
-The C<memcached plugin> connects to a memcached server and queries statistics
+The B<memcached plugin> connects to a memcached server and queries statistics
 about cache utilization, memory and bandwidth used.
 L<http://www.danga.com/memcached/>
 
@@ -2255,6 +2395,166 @@ setting is given, the B<Host> and B<Port> settings are ignored.
 
 =back
 
+=head2 Plugin C<mic>
+
+The B<mic plugin> gathers CPU statistics, memory usage and temperatures from
+Intel's Many Integrated Core (MIC) systems.
+
+B<Synopsis:>
+
+ <Plugin mic>
+   ShowCPU true
+   ShowCPUCores true
+   ShowMemory true
+   
+   ShowTemperatures true
+   Temperature vddg
+   Temperature vddq
+   IgnoreSelectedTemperature true
+
+   ShowPower true
+   Power total0
+   Power total1
+   IgnoreSelectedPower true   
+ </Plugin>
+
+The following options are valid inside the B<PluginE<nbsp>mic> block:
+
+=over 4
+
+=item B<ShowCPU> B<true>|B<false>
+
+If enabled (the default) a sum of the CPU usage accross all cores is reported.
+
+=item B<ShowCPUCores> B<true>|B<false>
+
+If enabled (the default) per-core CPU usage is reported.
+
+=item B<ShowMemory> B<true>|B<false>
+
+If enabled (the default) the physical memory usage of the MIC system is
+reported.
+
+=item B<ShowTemperatures> B<true>|B<false>
+
+If enabled (the default) various temperatures of the MIC system are reported.
+
+=item B<Temperature> I<Name>
+
+This option controls which temperatures are being reported. Whether matching
+temperatures are being ignored or I<only> matching temperatures are reported
+depends on the B<IgnoreSelectedTemperature> setting below. By default I<all>
+temperatures are reported.
+
+=item B<IgnoreSelectedTemperature> B<false>|B<true>
+
+Controls the behavior of the B<Temperature> setting above. If set to B<false>
+(the default) only temperatures matching a B<Temperature> option are reported
+or, if no B<Temperature> option is specified, all temperatures are reported. If
+set to B<true>, matching temperatures are I<ignored> and all other temperatures
+are reported.
+
+Known temperature names are:
+
+=over 4
+
+=item die
+
+Die of the CPU
+
+=item devmem
+
+Device Memory
+
+=item fin
+
+Fan In
+
+=item fout
+
+Fan Out 
+
+=item vccp
+
+Voltage ccp
+
+=item vddg
+
+Voltage ddg
+
+=item vddq
+
+Voltage ddq
+
+=back
+
+=item B<ShowPower> B<true>|B<false>
+
+If enabled (the default) various temperatures of the MIC system are reported.
+
+=item B<Power> I<Name>
+
+This option controls which power readings are being reported. Whether matching
+power readings are being ignored or I<only> matching power readings are reported
+depends on the B<IgnoreSelectedPower> setting below. By default I<all>
+power readings are reported.
+
+=item B<IgnoreSelectedPower> B<false>|B<true>
+
+Controls the behavior of the B<Power> setting above. If set to B<false>
+(the default) only power readings matching a B<Power> option are reported
+or, if no B<Power> option is specified, all power readings are reported. If
+set to B<true>, matching power readings are I<ignored> and all other power readings
+are reported.
+
+Known power names are:
+
+=over 4
+
+=item total0
+
+Total power utilization averaged over Time Window 0 (uWatts). 
+
+=item total1
+
+Total power utilization averaged over Time Window 0 (uWatts). 
+
+=item inst
+
+Instantaneous power (uWatts).
+
+=item imax
+
+Max instantaneous power (uWatts). 
+
+=item pcie
+
+PCI-E connector power (uWatts). 
+
+=item c2x3
+
+2x3 connector power (uWatts). 
+
+=item c2x4
+
+2x4 connector power (uWatts). 
+
+=item vccp
+
+Core rail (uVolts). 
+
+=item vddg
+
+Uncore rail (uVolts). 
+
+=item vddq
+
+Memory subsystem rail (uVolts). 
+
+=back
+
+=back
+
 =head2 Plugin C<modbus>
 
 The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP and reads
@@ -2262,7 +2562,7 @@ register values. It supports reading single registers (unsigned 16E<nbsp>bit
 values), large integer values (unsigned 32E<nbsp>bit values) and floating point
 values (two registers interpreted as IEEE floats in big endian notation).
 
-Synopsis:
+B<Synopsis:>
 
  <Data "voltage-input-1">
    RegisterBase 0
@@ -4892,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
@@ -5539,6 +5956,17 @@ and closed connections. True by default.
 Statistics about the shared memory log, a memory region to store
 log messages which is flushed to disk when full. True by default.
 
+=item B<CollectBan> B<true>|B<false>
+
+Statistics about ban operations, such as number of bans added, retired, and
+number of objects tested against ban operations. Only available with Varnish
+3.x. False by default.
+
+=item B<CollectDirectorDNS> B<true>|B<false>
+
+DNS director lookup cache statistics. Only available with Varnish 3.x. False by
+default.
+
 =item B<CollectESI> B<true>|B<false>
 
 Edge Side Includes (ESI) parse statistics. False by default.
@@ -5552,10 +5980,27 @@ Statistics about fetches (HTTP requests sent to the backend). False by default.
 Inserts and look-ups in the crit bit tree based hash. Look-ups are
 divided into locked and unlocked look-ups. False by default.
 
+=item B<CollectObjects> B<true>|B<false>
+
+Statistics on cached objects: number of objects expired, nuked (prematurely
+expired), saved, moved, etc. False by default.
+
+=item B<CollectPurge> B<true>|B<false>
+
+Statistics about purge operations, such as number of purges added, retired, and
+number of objects tested against purge operations. Only available with Varnish
+2.x. False by default.
+
+=item B<CollectSession> B<true>|B<false>
+
+Client session statistics. Number of past and current sessions, session herd and
+linger counters, etc. False by default.
+
 =item B<CollectSMA> B<true>|B<false>
 
-malloc or umem (umem_alloc(3MALLOC) based) storage statistics.
-The umem storage component is Solaris specific. False by default.
+malloc or umem (umem_alloc(3MALLOC) based) storage statistics. The umem storage
+component is Solaris specific. Only available with Varnish 2.x. False by
+default.
 
 =item B<CollectSMS> B<true>|B<false>
 
@@ -5564,13 +6009,28 @@ component is used internally only. False by default.
 
 =item B<CollectSM> B<true>|B<false>
 
-file (memory mapped file) storage statistics. False by default.
+file (memory mapped file) storage statistics. Only available with Varnish 2.x.
+False by default.
+
+=item B<CollectStruct> B<true>|B<false>
+
+Current varnish internal state statistics. Number of current sessions, objects
+in cache store, open connections to backends (with Varnish 2.x), etc. False by
+default.
 
 =item B<CollectTotals> B<true>|B<false>
 
 Collects overview counters, such as the number of sessions created,
 the number of requests and bytes transferred. False by default.
 
+=item B<CollectUptime> B<true>|B<false>
+
+Varnish uptime. False by default.
+
+=item B<CollectVCL> B<true>|B<false>
+
+Number of total (available + discarded) VCL (config files). False by default.
+
 =item B<CollectWorkers> B<true>|B<false>
 
 Collect statistics about worker threads. False by default.
@@ -5615,9 +6075,9 @@ iptables to feed data for the guest IPs into the iptables plugin.
 
 The C<write_graphite> plugin writes data to I<Graphite>, an open-source metrics
 storage and graphing project. The plugin connects to I<Carbon>, the data layer
-of I<Graphite>, and sends data via the "line based" protocol (per default using
-portE<nbsp>2003). The data will be sent in blocks of at most 1428 bytes to
-minimize the number of network packets.
+of I<Graphite>, via I<TCP> or I<UDP> and sends data via the "line based"
+protocol (per default using portE<nbsp>2003). The data will be sent in blocks
+of at most 1428 bytes to minimize the number of network packets.
 
 Synopsis:
 
@@ -5625,6 +6085,8 @@ Synopsis:
    <Node "example">
      Host "localhost"
      Port "2003"
+     Protocol "udp"
+     LogSendErrors true
      Prefix "collectd"
    </Node>
  </Plugin>
@@ -5642,6 +6104,17 @@ Hostname or address to connect to. Defaults to C<localhost>.
 
 Service name or port number to connect to. Defaults to C<2003>.
 
+=item B<Protocol> I<String>
+
+Protocol to use when connecting to I<Graphite>. Defaults to C<tcp>.
+
+=item B<LogSendErrors> B<false>|B<true>
+
+If set to B<true> (the default), logs errors when sending data to I<Graphite>.
+If set to B<false>, it will not log the errors. This is especially useful when
+using Protocol UDP since many times we want to use the "fire-and-forget"
+approach and logging errors fills syslog with unneeded messages.
+
 =item B<Prefix> I<String>
 
 When set, I<String> is added in front of the host name. Dots and whitespace are
@@ -5812,7 +6285,7 @@ Synopsis:
      Protocol UDP
      StoreRates true
      AlwaysAppendDS false
-     Delay 10
+     TTLFactor 2.0
    </Node>
    Tag "foobar"
  </Plugin>
@@ -5859,6 +6332,15 @@ If set the B<true>, append the name of the I<Data Source> (DS) to the
 identifies a metric in I<Riemann>. If set to B<false> (the default), this is
 only done when there is more than one DS.
 
+=item B<TTLFactor> I<Factor>
+
+I<Riemann> events have a I<Time to Live> (TTL) which specifies how long each
+event is considered active. I<collectd> populates this field based on the
+metrics interval setting. This setting controls the factor with which the
+interval is multiplied to set the TTL. The default value is B<2.0>. Unless you
+know exactly what you're doing, you should only increase this setting from its
+default value.
+
 =back
 
 =item B<Tag> I<String>
index c41c4fe629dd14685e543964d9a7c05b7d8f399b..161b4d67fc063d693b1f319e016687cea4ae639d 100644 (file)
@@ -86,6 +86,47 @@ int ssnprintf (char *dest, size_t n, const char *format, ...)
        return (ret);
 } /* int ssnprintf */
 
+char *ssnprintf_alloc (char const *format, ...) /* {{{ */
+{
+       char static_buffer[1024] = "";
+       char *alloc_buffer;
+       size_t alloc_buffer_size;
+       int status;
+       va_list ap;
+
+       /* Try printing into the static buffer. In many cases it will be
+        * sufficiently large and we can simply return a strdup() of this
+        * buffer. */
+       va_start (ap, format);
+       status = vsnprintf (static_buffer, sizeof (static_buffer), format, ap);
+       va_end (ap);
+       if (status < 0)
+               return (NULL);
+
+       /* "status" does not include the null byte. */
+       alloc_buffer_size = (size_t) (status + 1);
+       if (alloc_buffer_size <= sizeof (static_buffer))
+               return (strdup (static_buffer));
+
+       /* Allocate a buffer large enough to hold the string. */
+       alloc_buffer = malloc (alloc_buffer_size);
+       if (alloc_buffer == NULL)
+               return (NULL);
+       memset (alloc_buffer, 0, alloc_buffer_size);
+
+       /* Print again into this new buffer. */
+       va_start (ap, format);
+       status = vsnprintf (alloc_buffer, alloc_buffer_size, format, ap);
+       va_end (ap);
+       if (status < 0)
+       {
+               sfree (alloc_buffer);
+               return (NULL);
+       }
+
+       return (alloc_buffer);
+} /* }}} char *ssnprintf_alloc */
+
 char *sstrdup (const char *s)
 {
        char *r;
@@ -1220,18 +1261,25 @@ int walk_directory (const char *dir, dirwalk_callback_f callback,
        return (0);
 }
 
-int read_file_contents (const char *filename, char *buf, int bufsize)
+ssize_t read_file_contents (const char *filename, char *buf, size_t bufsize)
 {
        FILE *fh;
-       int n;
+       ssize_t ret;
 
-       if ((fh = fopen (filename, "r")) == NULL)
-               return -1;
+       fh = fopen (filename, "r");
+       if (fh == NULL)
+               return (-1);
 
-       n = fread(buf, 1, bufsize, fh);
-       fclose(fh);
+       ret = (ssize_t) fread (buf, 1, bufsize, fh);
+       if ((ret == 0) && (ferror (fh) != 0))
+       {
+               ERROR ("read_file_contents: Reading file \"%s\" failed.",
+                               filename);
+               ret = -1;
+       }
 
-       return n;
+       fclose(fh);
+       return (ret);
 }
 
 counter_t counter_diff (counter_t old_value, counter_t new_value)
index ae8e311f9983e8ff63441c0ab2936d287220ae67..317be8d1579d063cc23338e229abcdede533da84 100644 (file)
@@ -56,7 +56,13 @@ struct rate_to_value_state_s
 typedef struct rate_to_value_state_s rate_to_value_state_t;
 
 char *sstrncpy (char *dest, const char *src, size_t n);
+
+__attribute__ ((format(printf,3,4)))
 int ssnprintf (char *dest, size_t n, const char *format, ...);
+
+__attribute__ ((format(printf,1,2)))
+char *ssnprintf_alloc (char const *format, ...);
+
 char *sstrdup(const char *s);
 void *smalloc(size_t size);
 char *sstrerror (int errnum, char *buf, size_t buflen);
@@ -303,7 +309,7 @@ typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename,
 int walk_directory (const char *dir, dirwalk_callback_f callback,
                void *user_data, int hidden);
 /* Returns the number of bytes read or negative on error. */
-int read_file_contents (const char *filename, char *buf, int bufsize);
+ssize_t read_file_contents (char const *filename, char *buf, size_t bufsize);
 
 counter_t counter_diff (counter_t old_value, counter_t new_value);
 
index 154c041cee2d8b6d01ff4021c723810993c91dac..0e54f267f9fdd08c15d4d4d11a427f409d708723 100644 (file)
@@ -109,7 +109,10 @@ 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"},
        {"PostCacheChain", NULL, "PostCache"}
 };
@@ -277,21 +280,6 @@ static int dispatch_loadplugin (const oconfig_item_t *ci)
        memset (&ctx, 0, sizeof (ctx));
        ctx.interval = cf_get_default_interval ();
 
-       /*
-        * XXX: Magic at work:
-        *
-        * Some of the language bindings, for example the Python and Perl
-        * plugins, need to be able to export symbols to the scripts they run.
-        * For this to happen, the "Globals" flag needs to be set.
-        * Unfortunately, this technical detail is hard to explain to the
-        * average user and she shouldn't have to worry about this, ideally.
-        * So in order to save everyone's sanity use a different default for a
-        * handful of special plugins. --octo
-        */
-       if ((strcasecmp ("Perl", name) == 0)
-                       || (strcasecmp ("Python", name) == 0))
-               flags |= PLUGIN_FLAGS_GLOBAL;
-
        for (i = 0; i < ci->children_num; ++i) {
                if (strcasecmp("Globals", ci->children[i].key) == 0)
                        cf_util_get_flag (ci->children + i, &flags, PLUGIN_FLAGS_GLOBAL);
@@ -394,6 +382,19 @@ static int dispatch_block_plugin (oconfig_item_t *ci)
 
        name = ci->values[0].value.string;
 
+       if (IS_TRUE (global_option_get ("AutoLoadPlugin")))
+       {
+               int status;
+
+               status = plugin_load (name, /* flags = */ 0);
+               if (status != 0)
+               {
+                       ERROR ("Automatically loading plugin \"%s\" failed "
+                                       "with status %i.", name, status);
+                       return (status);
+               }
+       }
+
        /* Check for a complex callback first */
        for (cb = complex_callback_head; cb != NULL; cb = cb->next)
        {
@@ -916,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");
index 5a719a421a66a25df0073d6bdc4f24e8cbe9ebac..c91fcd5f4abb0bf87bf2339ae4524ac8e3b1de99 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 c6e2ae9c1f521350db159dc2922dafcb6fb54f41..280e61c8649ae70a0f2bc99658aeed5659e71e5a 100644 (file)
@@ -26,6 +26,7 @@
 #include "plugin.h"
 #include "configfile.h"
 #include "utils_match.h"
+#include "utils_time.h"
 
 #include <curl/curl.h>
 
@@ -57,12 +58,13 @@ struct web_page_s /* {{{ */
   char *user;
   char *pass;
   char *credentials;
-  int   verify_peer;
-  int   verify_host;
+  _Bool verify_peer;
+  _Bool verify_host;
   char *cacert;
   struct curl_slist *headers;
   char *post_body;
-  int   response_time;
+  _Bool response_time;
+  _Bool response_code;
 
   CURL *curl;
   char curl_errbuf[CURL_ERROR_SIZE];
@@ -89,7 +91,7 @@ static size_t cc_curl_callback (void *buf, /* {{{ */
 {
   web_page_t *wp;
   size_t len;
-  
+
   len = size * nmemb;
   if (len <= 0)
     return (len);
@@ -160,23 +162,6 @@ static void cc_web_page_free (web_page_t *wp) /* {{{ */
   sfree (wp);
 } /* }}} void cc_web_page_free */
 
-static int cc_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 ("curl 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 cc_config_add_string */
-
 static int cc_config_append_string (const char *name, struct curl_slist **dest, /* {{{ */
     oconfig_item_t *ci)
 {
@@ -193,21 +178,6 @@ static int cc_config_append_string (const char *name, struct curl_slist **dest,
   return (0);
 } /* }}} int cc_config_append_string */
 
-
-static int cc_config_set_boolean (const char *name, int *dest, /* {{{ */
-    oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
-  {
-    WARNING ("curl plugin: `%s' needs exactly one boolean argument.", name);
-    return (-1);
-  }
-
-  *dest = ci->values[0].value.boolean ? 1 : 0;
-
-  return (0);
-} /* }}} int cc_config_set_boolean */
-
 static int cc_config_add_match_dstype (int *dstype_ret, /* {{{ */
     oconfig_item_t *ci)
 {
@@ -312,15 +282,15 @@ static int cc_config_add_match (web_page_t *page, /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("Regex", child->key) == 0)
-      status = cc_config_add_string ("Regex", &match->regex, child);
+      status = cf_util_get_string (child, &match->regex);
     else if (strcasecmp ("ExcludeRegex", child->key) == 0)
-      status = cc_config_add_string ("ExcludeRegex", &match->exclude_regex, child);
+      status = cf_util_get_string (child, &match->exclude_regex);
     else if (strcasecmp ("DSType", child->key) == 0)
       status = cc_config_add_match_dstype (&match->dstype, child);
     else if (strcasecmp ("Type", child->key) == 0)
-      status = cc_config_add_string ("Type", &match->type, child);
+      status = cf_util_get_string (child, &match->type);
     else if (strcasecmp ("Instance", child->key) == 0)
-      status = cc_config_add_string ("Instance", &match->instance, child);
+      status = cf_util_get_string (child, &match->instance);
     else
     {
       WARNING ("curl plugin: Option `%s' not allowed here.", child->key);
@@ -459,6 +429,7 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
   page->verify_peer = 1;
   page->verify_host = 1;
   page->response_time = 0;
+  page->response_code = 0;
 
   page->instance = strdup (ci->values[0].value.string);
   if (page->instance == NULL)
@@ -475,26 +446,28 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("URL", child->key) == 0)
-      status = cc_config_add_string ("URL", &page->url, child);
+      status = cf_util_get_string (child, &page->url);
     else if (strcasecmp ("User", child->key) == 0)
-      status = cc_config_add_string ("User", &page->user, child);
+      status = cf_util_get_string (child, &page->user);
     else if (strcasecmp ("Password", child->key) == 0)
-      status = cc_config_add_string ("Password", &page->pass, child);
+      status = cf_util_get_string (child, &page->pass);
     else if (strcasecmp ("VerifyPeer", child->key) == 0)
-      status = cc_config_set_boolean ("VerifyPeer", &page->verify_peer, child);
+      status = cf_util_get_boolean (child, &page->verify_peer);
     else if (strcasecmp ("VerifyHost", child->key) == 0)
-      status = cc_config_set_boolean ("VerifyHost", &page->verify_host, child);
+      status = cf_util_get_boolean (child, &page->verify_host);
     else if (strcasecmp ("MeasureResponseTime", child->key) == 0)
-      status = cc_config_set_boolean (child->key, &page->response_time, child);
+      status = cf_util_get_boolean (child, &page->response_time);
+    else if (strcasecmp ("MeasureResponseCode", child->key) == 0)
+      status = cf_util_get_boolean (child, &page->response_code);
     else if (strcasecmp ("CACert", child->key) == 0)
-      status = cc_config_add_string ("CACert", &page->cacert, child);
+      status = cf_util_get_string (child, &page->cacert);
     else if (strcasecmp ("Match", child->key) == 0)
       /* Be liberal with failing matches => don't set `status'. */
       cc_config_add_match (page, child);
     else if (strcasecmp ("Header", child->key) == 0)
       status = cc_config_append_string ("Header", &page->headers, child);
     else if (strcasecmp ("Post", child->key) == 0)
-      status = cc_config_add_string ("Post", &page->post_body, child);
+      status = cf_util_get_string (child, &page->post_body);
     else
     {
       WARNING ("curl plugin: Option `%s' not allowed here.", child->key);
@@ -514,11 +487,12 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
       status = -1;
     }
 
-    if (page->matches == NULL && !page->response_time)
+    if (page->matches == NULL && !page->response_time && !page->response_code)
     {
       assert (page->instance != NULL);
       WARNING ("curl plugin: No (valid) `Match' block "
-          "or MeasureResponseTime within `Page' block `%s'.", page->instance);
+          "or MeasureResponseTime or MeasureResponseCode within "
+          "`Page' block `%s'.", page->instance);
       status = -1;
     }
 
@@ -617,12 +591,30 @@ static void cc_submit (const web_page_t *wp, const web_match_t *wm, /* {{{ */
   plugin_dispatch_values (&vl);
 } /* }}} void cc_submit */
 
-static void cc_submit_response_time (const web_page_t *wp, double seconds) /* {{{ */
+static void cc_submit_response_code (const web_page_t *wp, long code) /* {{{ */
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = code;
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "curl", sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, wp->instance, sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, "response_code", sizeof (vl.type));
+
+  plugin_dispatch_values (&vl);
+} /* }}} void cc_submit_response_code */
+
+static void cc_submit_response_time (const web_page_t *wp, /* {{{ */
+    cdtime_t response_time)
 {
   value_t values[1];
   value_list_t vl = VALUE_LIST_INIT;
 
-  values[0].gauge = seconds;
+  values[0].gauge = CDTIME_T_TO_DOUBLE (response_time);
 
   vl.values = values;
   vl.values_len = 1;
@@ -638,10 +630,10 @@ static int cc_read_page (web_page_t *wp) /* {{{ */
 {
   web_match_t *wm;
   int status;
-  struct timeval start, end;
+  cdtime_t start = 0;
 
   if (wp->response_time)
-    gettimeofday (&start, NULL);
+    start = cdtime ();
 
   wp->buffer_fill = 0;
   status = curl_easy_perform (wp->curl);
@@ -653,12 +645,18 @@ static int cc_read_page (web_page_t *wp) /* {{{ */
   }
 
   if (wp->response_time)
+    cc_submit_response_time (wp, cdtime() - start);
+
+  if(wp->response_code)
   {
-    double secs = 0;
-    gettimeofday (&end, NULL);
-    secs += end.tv_sec - start.tv_sec;
-    secs += (end.tv_usec - start.tv_usec) / 1000000.0;
-    cc_submit_response_time (wp, secs);
+    long response_code = 0;
+    status = curl_easy_getinfo(wp->curl, CURLINFO_RESPONSE_CODE, &response_code);
+    if(status != CURLE_OK) {
+      ERROR ("curl plugin: Fetching response code failed with staus %i: %s",
+        status, wp->curl_errbuf);
+    } else {
+      cc_submit_response_code(wp, response_code);
+    }
   }
 
   for (wm = wp->matches; wm != NULL; wm = wm->next)
index deee460bb3327e6291d6536190ac68e7a7d87cb0..511863e39f12e98b3914f83ebc0ba446f5db4f20 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);
@@ -598,20 +647,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 +671,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 +701,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 +726,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 +755,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 +816,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 +934,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 +953,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 0b4130473f4e3359dc9cbb66059831ac09386a34..5adaf067a157e44220655adb79855c6fee0d3b26 100644 (file)
@@ -966,7 +966,7 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
   if (status == 0)
   {
     user_data_t ud;
-    char cb_name[DATA_MAX_NAME_LEN];
+    char *cb_name;
 
     if (db->instance == NULL)
       db->instance = strdup("default");
@@ -978,11 +978,10 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
     ud.data = (void *) db;
     ud.free_func = cx_free;
 
-    ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s",
-               db->instance, db->url);
-
-    plugin_register_complex_read (/* group = */ NULL, cb_name, cx_read,
+    cb_name = ssnprintf_alloc ("curl_xml-%s-%s", db->instance, db->url);
+    plugin_register_complex_read (/* group = */ "curl_xml", cb_name, cx_read,
                                   /* interval = */ NULL, &ud);
+    sfree (cb_name);
   }
   else
   {
index ded374b942e5d08d804dd027292eef2ef9f50fcb..3d5a402c7949b8741b7568b78d987c1fe033dc6a 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -54,7 +54,9 @@ static const char *config_keys[] =
        "IgnoreSelected",
        "ReportByDevice",
        "ReportReserved",
-       "ReportInodes"
+       "ReportInodes",
+       "ValuesAbsolute",
+       "ValuesPercentage"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -64,6 +66,8 @@ static ignorelist_t *il_fstype = NULL;
 
 static _Bool by_device = 0;
 static _Bool report_inodes = 0;
+static _Bool values_absolute = 1;
+static _Bool values_percentage = 0;
 
 static int df_init (void)
 {
@@ -131,7 +135,24 @@ 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;
+
+               return (0);
+       }
+       else if (strcasecmp (key, "ValuesPercentage") == 0)
+       {
+               if (IS_TRUE (value))
+                       values_percentage = 1;
+               else
+                       values_percentage = 0;
 
+               return (0);
+       }
 
        return (-1);
 }
@@ -210,7 +231,7 @@ static int df_read (void)
                if (!statbuf.f_blocks)
                        continue;
 
-               if (by_device) 
+               if (by_device)
                {
                        /* eg, /dev/hda1  -- strip off the "/dev/" */
                        if (strncmp (mnt_ptr->spec_device, "/dev/", strlen ("/dev/")) == 0)
@@ -218,13 +239,13 @@ static int df_read (void)
                        else
                                sstrncpy (disk_name, mnt_ptr->spec_device, sizeof (disk_name));
 
-                       if (strlen(disk_name) < 1) 
+                       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;
                        }
-               } 
-               else 
+               }
+               else
                {
                        if (strcmp (mnt_ptr->dir, "/") == 0)
                        {
@@ -274,12 +295,29 @@ 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);
 
-               df_submit_one (disk_name, "df_complex", "free",
+               if (values_absolute)
+               {
+                       df_submit_one (disk_name, "df_complex", "free",
                                (gauge_t) (blk_free * blocksize));
-               df_submit_one (disk_name, "df_complex", "reserved",
+                       df_submit_one (disk_name, "df_complex", "reserved",
                                (gauge_t) (blk_reserved * blocksize));
-               df_submit_one (disk_name, "df_complex", "used",
+                       df_submit_one (disk_name, "df_complex", "used",
                                (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)
@@ -297,13 +335,29 @@ static int df_read (void)
                        inode_free = (uint64_t) statbuf.f_favail;
                        inode_reserved = (uint64_t) (statbuf.f_ffree - statbuf.f_favail);
                        inode_used = (uint64_t) (statbuf.f_files - statbuf.f_ffree);
-                       
-                       df_submit_one (disk_name, "df_inodes", "free",
-                                       (gauge_t) inode_free);
-                       df_submit_one (disk_name, "df_inodes", "reserved",
-                                       (gauge_t) inode_reserved);
-                       df_submit_one (disk_name, "df_inodes", "used",
-                                       (gauge_t) inode_used);
+
+                       if (values_percentage)
+                       {
+                               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);
+                       }
+                       if (values_absolute)
+                       {
+                               df_submit_one (disk_name, "df_inodes", "free",
+                                               (gauge_t) inode_free);
+                               df_submit_one (disk_name, "df_inodes", "reserved",
+                                               (gauge_t) inode_reserved);
+                               df_submit_one (disk_name, "df_inodes", "used",
+                                               (gauge_t) inode_used);
+                       }
                }
        }
 
diff --git a/src/lvm.c b/src/lvm.c
new file mode 100644 (file)
index 0000000..6ef3a7b
--- /dev/null
+++ b/src/lvm.c
@@ -0,0 +1,103 @@
+/**
+ * collectd - src/lvm.c
+ * Copyright (C) 2013       Chad Malfait
+ *
+ * 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:
+ *   Chad Malfait <malfaitc at yahoo.com>
+ **/
+
+#include <lvm2app.h>
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+static void lvm_submit (char const *plugin_instance, char const *type_instance,
+        uint64_t ivalue)
+{
+    value_t v;
+    value_list_t vl = VALUE_LIST_INIT;
+
+    v.gauge = (gauge_t) ivalue;
+
+    vl.values = &v;
+    vl.values_len = 1;
+
+    sstrncpy(vl.host, hostname_g, sizeof (vl.host));
+    sstrncpy(vl.plugin, "lvm", sizeof (vl.plugin));
+    sstrncpy(vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+    sstrncpy(vl.type, "df_complex", sizeof (vl.type));
+    sstrncpy(vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+}
+
+static int vg_read(vg_t vg, char const *vg_name)
+{
+    struct dm_list *lvs;
+    struct lvm_lv_list *lvl;
+
+    lvm_submit (vg_name, "free", lvm_vg_get_free_size(vg));
+
+    lvs = lvm_vg_list_lvs(vg);
+    dm_list_iterate_items(lvl, lvs) {
+         lvm_submit(vg_name, lvm_lv_get_name(lvl->lv), lvm_lv_get_size(lvl->lv));
+    }
+
+    return (0);
+}
+
+static int lvm_read(void)
+{
+    lvm_t lvm;
+    struct dm_list *vg_names;
+    struct lvm_str_list *name_list;
+
+    lvm = lvm_init(NULL);
+    if (!lvm) {
+        ERROR("lvm plugin: lvm_init failed.");
+        return (-1);
+    }
+
+    vg_names = lvm_list_vg_names(lvm);
+    if (!vg_names) {
+        ERROR("lvm plugin lvm_list_vg_name failed %s", lvm_errmsg(lvm));
+        lvm_quit(lvm);
+        return (-1);
+    }
+
+    dm_list_iterate_items(name_list, vg_names) {
+        vg_t vg;
+
+        vg = lvm_vg_open(lvm, name_list->str, "r", 0);
+        if (!vg) {
+            ERROR ("lvm plugin: lvm_vg_open (%s) failed: %s",
+                    name_list->str, lvm_errmsg(lvm));
+            continue;
+        }
+
+        vg_read(vg, name_list->str);
+        lvm_vg_close(vg);
+    }
+
+    lvm_quit(lvm);
+    return (0);
+} /*lvm_read */
+
+void module_register(void)
+{
+    plugin_register_read("lvm", lvm_read);
+} /* void module_register */
index 2a5e0f0f0845ed6a7361982c541efbab3b996603..d739203655d5fb4b840b6f36128d81efcdfcd8f4 100644 (file)
@@ -349,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;
@@ -445,6 +449,36 @@ static int memcached_read (user_data_t *user_data)
         gets = atof (fields[2]);
     }
 
+    /*
+     * 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
      */
@@ -494,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 */
 
diff --git a/src/mic.c b/src/mic.c
new file mode 100644 (file)
index 0000000..570da51
--- /dev/null
+++ b/src/mic.c
@@ -0,0 +1,417 @@
+/**
+ * collectd - src/mic.c
+ * Copyright (C) 2013 Battelle Memorial Institute
+ *
+ * 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:
+ *   Evan Felix <evan.felix at pnnl.gov>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "utils_ignorelist.h"
+
+#include <MicAccessTypes.h>
+#include <MicAccessErrorTypes.h>
+#include <MicAccessApi.h>
+#include <MicThermalAPI.h>
+#include <MicPowerManagerAPI.h>
+
+#define MAX_MICS 32
+#define MAX_CORES 256
+
+static MicDeviceOnSystem mics[MAX_MICS];
+static U32 num_mics = 0;
+static HANDLE mic_handle = NULL;
+
+static int const therm_ids[] = {
+       eMicThermalDie, eMicThermalDevMem, eMicThermalFin, eMicThermalFout,
+       eMicThermalVccp, eMicThermalVddg, eMicThermalVddq };
+static char const * const therm_names[] = {
+       "die", "devmem", "fin", "fout",
+       "vccp", "vddg", "vddq" };
+
+static const char *config_keys[] =
+{
+       "ShowCPU",
+       "ShowCPUCores",
+       "ShowMemory",
+       "ShowTemperatures",
+       "Temperature",
+       "IgnoreSelectedTemperature",
+       "ShowPower",
+       "Power",
+       "IgnoreSelectedPower"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static _Bool show_cpu = 1;
+static _Bool show_cpu_cores = 1;
+static _Bool show_memory = 1;
+static _Bool show_temps = 1;
+static ignorelist_t *temp_ignore = NULL;
+static _Bool show_power = 1;
+static ignorelist_t *power_ignore = NULL;
+
+static int mic_init (void)
+{
+       U32 ret;
+       U32 mic_count;
+
+       if (mic_handle)
+               return (0);
+
+       mic_count = (U32) STATIC_ARRAY_SIZE(mics);
+       ret = MicInitAPI(&mic_handle,  eTARGET_SCIF_DRIVER, mics, &mic_count);
+       if (ret != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem initializing MicAccessAPI: %s",
+                               MicGetErrorString(ret));
+       }
+       DEBUG("mic plugin: found: %"PRIu32" MIC(s)",mic_count);
+
+       if (mic_count<0 || mic_count>=MAX_MICS) {
+               ERROR("mic plugin: No Intel MICs in system");
+               return (1);
+       }
+       else {
+               num_mics = mic_count;
+               return (0);
+       }
+}
+
+static int mic_config (const char *key, const char *value) {
+       if (temp_ignore == NULL)
+               temp_ignore = ignorelist_create(1);
+       if (power_ignore == NULL)
+               power_ignore = ignorelist_create(1);
+       if (temp_ignore == NULL || power_ignore == NULL)
+               return (1);
+
+       if (strcasecmp("ShowCPU",key) == 0)
+       {
+               show_cpu = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowCPUCores",key) == 0)
+       {
+               show_cpu_cores = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowTemperatures",key) == 0)
+       {
+               show_temps = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowMemory",key) == 0)
+       {
+               show_memory = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowPower",key) == 0)
+       {
+               show_power = IS_TRUE(value);
+       }
+       else if (strcasecmp("Temperature",key) == 0)
+       {
+               ignorelist_add(temp_ignore,value);
+       }
+       else if (strcasecmp("IgnoreSelectedTemperature",key) == 0)
+       {
+               int invert = 1;
+               if (IS_TRUE(value))
+                       invert = 0;
+               ignorelist_set_invert(temp_ignore,invert);
+       }
+       else if (strcasecmp("Power",key) == 0)
+       {
+               ignorelist_add(power_ignore,value);
+       }
+       else if (strcasecmp("IgnoreSelectedPower",key) == 0)
+       {
+               int invert = 1;
+               if (IS_TRUE(value))
+                       invert = 0;
+               ignorelist_set_invert(power_ignore,invert);
+       }
+       else
+       {
+               return (-1);
+       }
+       return (0);
+}
+
+static void mic_submit_memory_use(int micnumber, const char *type_instance, U32 val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       /* MicAccessAPI reports KB's of memory, adjust for this */
+       DEBUG("mic plugin: Memory Value Report; %u %lf",val,((gauge_t)val)*1024.0);
+       values[0].gauge = ((gauge_t)val)*1024.0;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), "%i", micnumber);
+       strncpy (vl.type, "memory", sizeof (vl.type));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/* Gather memory Utilization */
+static int mic_read_memory(int mic)
+{
+       U32 ret;
+       U32 mem_total,mem_free,mem_bufs;
+
+       ret = MicGetMemoryUtilization(mic_handle,&mem_total,&mem_free,&mem_bufs);
+       if (ret != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem getting Memory Utilization: %s",
+                               MicGetErrorString(ret));
+               return (1);
+       }
+       mic_submit_memory_use(mic,"free",mem_free);
+       mic_submit_memory_use(mic,"used",mem_total-mem_free-mem_bufs);
+       mic_submit_memory_use(mic,"buffered",mem_bufs);
+       DEBUG("mic plugin: Memory Read: %u %u %u",mem_total,mem_free,mem_bufs);
+       return (0);
+}
+
+static void mic_submit_temp(int micnumber, const char *type, gauge_t val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = val;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+                       "%i", micnumber);
+       strncpy (vl.type, "temperature", sizeof (vl.type));
+       strncpy (vl.type_instance, type, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/* Gather Temperature Information */
+static int mic_read_temps(int mic)
+{
+       size_t num_therms = STATIC_ARRAY_SIZE(therm_ids);
+       size_t j;
+
+       for (j = 0; j < num_therms; j++) {
+               U32 status;
+               U32 temp_buffer;
+               U32 buffer_size = (U32)sizeof(temp_buffer);
+               char const *name = therm_names[j];
+
+               if (ignorelist_match(temp_ignore, name) != 0)
+                       continue;
+
+               status = MicGetTemperature(mic_handle, therm_ids[j],
+                               &temp_buffer, &buffer_size);
+               if (status != MIC_ACCESS_API_SUCCESS) {
+                       ERROR("mic plugin: Error reading temperature \"%s\": "
+                                       "%s", name, MicGetErrorString(status));
+                       return (1);
+               }
+               mic_submit_temp(mic, name, temp_buffer);
+       }
+       return (0);
+}
+
+static void mic_submit_cpu(int micnumber, const char *type_instance,
+               int core, derive_t val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].derive = val;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       if (core < 0) /* global aggregation */
+               ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+                               "%i", micnumber);
+       else /* per-core statistics */
+               ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+                               "%i-cpu-%i", micnumber, core);
+       strncpy (vl.type, "cpu", sizeof (vl.type));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/*Gather CPU Utilization Information */
+static int mic_read_cpu(int mic)
+{
+       MicCoreUtil core_util;
+       MicCoreJiff core_jiffs[MAX_CORES];
+       U32 core_jiffs_size;
+       U32 status;
+
+       core_jiffs_size = MAX_CORES * sizeof(MicCoreJiff);
+       status = MicGetCoreUtilization(mic_handle, &core_util,
+                       core_jiffs, &core_jiffs_size);
+       if (status != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem getting CPU utilization: %s",
+                               MicGetErrorString(status));
+               return(-1);
+       }
+
+       if (show_cpu) {
+               mic_submit_cpu(mic, "user", -1, core_util.sum.user);
+               mic_submit_cpu(mic, "sys", -1, core_util.sum.sys);
+               mic_submit_cpu(mic, "nice", -1, core_util.sum.nice);
+               mic_submit_cpu(mic, "idle", -1, core_util.sum.idle);
+       }
+
+       if (show_cpu_cores) {
+               int j;
+               for (j = 0; j < core_util.core; j++) {
+                       mic_submit_cpu(mic, "user", j, core_jiffs[j].user);
+                       mic_submit_cpu(mic, "sys", j, core_jiffs[j].sys);
+                       mic_submit_cpu(mic, "nice", j, core_jiffs[j].nice);
+                       mic_submit_cpu(mic, "idle", j, core_jiffs[j].idle);
+               }
+       }
+       return (0);
+}
+
+static void mic_submit_power(int micnumber, const char *type, const char *type_instance, gauge_t val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = val;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), "%i", micnumber);
+       strncpy (vl.type, type, sizeof (vl.type));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/* Gather Power Information */
+static int mic_read_power(int mic)
+{
+       U32 ret;
+       MicPwrUsage power_use;
+
+       ret = MicGetPowerUsage(mic_handle,&power_use);
+       if (ret != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem getting Power Usage: %s",
+                       MicGetErrorString(ret));
+               return (1);
+       }
+
+       /* power is in uWatts, current in mA, voltage in uVolts..   convert to
+        * base unit */
+       #define SUB_POWER(name) do { if (ignorelist_match(power_ignore,#name)==0) \
+               mic_submit_power(mic,"power",#name,(gauge_t)power_use.name.prr*0.000001); \
+       } while(0)
+       #define SUB_VOLTS(name) do { if (ignorelist_match(power_ignore,#name)==0) {\
+               mic_submit_power(mic,"power",#name,(gauge_t)(power_use.name.pwr*0.000001)); \
+               mic_submit_power(mic,"current",#name,(gauge_t)(power_use.name.cur*0.001)); \
+               mic_submit_power(mic,"voltage",#name,(gauge_t)(power_use.name.volt*0.000001)); \
+       }} while(0)
+
+       SUB_POWER(total0);
+       SUB_POWER(total1);
+       SUB_POWER(inst);
+       SUB_POWER(imax);
+       SUB_POWER(pcie);
+       SUB_POWER(c2x3);
+       SUB_POWER(c2x4);
+       SUB_VOLTS(vccp);
+       SUB_VOLTS(vddg);
+       SUB_VOLTS(vddq);
+
+       return (0);
+}
+
+static int mic_read (void)
+{
+       int i;
+       U32 ret;
+       int error;
+
+       error=0;
+       for (i=0;i<num_mics;i++) {
+               ret = MicInitAdapter(&mic_handle,&mics[i]);
+               if (ret != MIC_ACCESS_API_SUCCESS) {
+                       ERROR("mic plugin: Problem initializing MicAdapter: %s",
+                                       MicGetErrorString(ret));
+                       error=1;
+               }
+
+               if (error == 0 && show_memory)
+                       error = mic_read_memory(i);
+
+               if (error == 0 && show_temps)
+                       error = mic_read_temps(i);
+
+               if (error == 0 && (show_cpu || show_cpu_cores))
+                       error = mic_read_cpu(i);
+
+               if (error == 0 && (show_power))
+                       error = mic_read_power(i);
+
+               ret = MicCloseAdapter(mic_handle);
+               if (ret != MIC_ACCESS_API_SUCCESS) {
+                       ERROR("mic plugin: Problem closing MicAdapter: %s",
+                                       MicGetErrorString(ret));
+                       error=2;
+                       break;
+               }
+       }
+       if (num_mics==0)
+               error=3;
+       return error;
+}
+
+
+static int mic_shutdown (void)
+{
+       if (mic_handle)
+               MicCloseAPI(&mic_handle);
+       mic_handle = NULL;
+
+       return (0);
+}
+
+void module_register (void)
+{
+       plugin_register_init ("mic", mic_init);
+       plugin_register_shutdown ("mic", mic_shutdown);
+       plugin_register_read ("mic", mic_read);
+       plugin_register_config ("mic",mic_config, config_keys, config_keys_num);
+} /* void module_register */
+
+/*
+ * vim: set shiftwidth=8 softtabstop=8 noet textwidth=78 :
+ */
index 39536d92ec76f712581d42df8dcea18b7103c093..422dc8c6fdadaf1b2feb7449cc5f3a82eda1954c 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>
-#endif
+#include <libmnl/libmnl.h>
 
 typedef struct ir_ignorelist_s
 {
@@ -54,19 +54,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 +117,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 +135,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 +204,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 +215,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 +231,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 rtnl_link_stats *stats)
+{
 
   if (check_ignorelist (dev, "interface", NULL) == 0)
   {
@@ -311,158 +273,261 @@ 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 */
+
+static int link_filter_cb (const struct nlmsghdr *nlh,
+    void *args __attribute__((unused)))
+{
+  struct ifinfomsg *ifm = mnl_nlmsg_get_payload (nlh);
+  struct nlattr *attr;
+  struct rtnl_link_stats *stats = NULL;
+  const char *dev = NULL;
+
+  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;
+  }
+
+  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)) < 0)
+    {
+      ERROR ("netlink plugin: link_filter_cb: IFLA_STATS mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    stats = mnl_attr_get_payload (attr);
+
+    check_ignorelist_and_submit (dev, stats);
+    break;
+  }
+
+  if (stats == NULL)
+  {
+    DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev);
+    return MNL_CB_OK;
+  }
 
-static int qos_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
-    struct nlmsghdr *nmh, void *args)
+  return MNL_CB_OK;
+} /* int link_filter_cb */
+
+#if HAVE_TCA_STATS2
+static int qos_attr_cb (const struct nlattr *attr, void *data)
 {
-  struct tcmsg *msg;
-  int msg_len;
-  struct rtattr *attrs[TCA_MAX + 1];
+  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_cb (const struct nlmsghdr *nlh, void *args)
+{
+  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;
+
+    if (mnl_attr_get_type (attr) != TCA_STATS2)
+      continue;
 
-    memset (attrs_stats, '\0', sizeof (attrs_stats));
-    parse_rtattr_nested (attrs_stats, TCA_STATS_MAX, attrs[TCA_STATS2]);
+    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;
+    }
 
-    if (attrs_stats[TCA_STATS_BASIC])
+    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);
+
+    if (!stats_submitted && ts != NULL)
+    {
+      char type_instance[DATA_MAX_NAME_LEN];
 
-    ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
-       tc_type, tc_inst);
+      ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
+          tc_type, tc_inst);
 
-    memset(&ts, '\0', sizeof (ts));
-    memcpy(&ts, RTA_DATA (attrs[TCA_STATS]),
-       MIN (RTA_PAYLOAD (attrs[TCA_STATS]), sizeof (ts)));
+      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 +553,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 +571,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 +586,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 +606,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 +624,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)
   {
-    ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
+    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: 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 +678,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 +722,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 7568a2c5ee2ded6439b6eb24927d64bb1b503e4a..de52262f4ec817b1b54ca876652b02e06a845252 100644 (file)
@@ -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 d3767d1283b9678bec65e8665d5feb7b8c64bef7..6c7aa057cf2d272b126eadf6308a76188d3f3b1a 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>
@@ -61,7 +62,7 @@ struct read_func_s
 #define rf_ctx rf_super.cf_ctx
        callback_func_t rf_super;
        char rf_group[DATA_MAX_NAME_LEN];
-       char rf_name[DATA_MAX_NAME_LEN];
+       char *rf_name;
        int rf_type;
        cdtime_t rf_interval;
        cdtime_t rf_effective_interval;
@@ -81,6 +82,8 @@ struct write_queue_s
 /*
  * Private variables
  */
+static c_avl_tree_t *plugins_loaded = NULL;
+
 static llist_t *list_init;
 static llist_t *list_write;
 static llist_t *list_flush;
@@ -106,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;
@@ -115,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
  */
@@ -437,6 +444,7 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                {
                        DEBUG ("plugin_read_thread: Destroying the `%s' "
                                        "callback.", rf->rf_name);
+                       sfree (rf->rf_name);
                        destroy_callback ((callback_func_t *) rf);
                        rf = NULL;
                        continue;
@@ -667,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);
@@ -698,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);
 
@@ -802,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)
@@ -830,8 +844,52 @@ void plugin_set_dir (const char *dir)
        }
 }
 
+static _Bool plugin_is_loaded (char const *name)
+{
+       int status;
+
+       if (plugins_loaded == NULL)
+               plugins_loaded = c_avl_create ((void *) strcasecmp);
+       assert (plugins_loaded != NULL);
+
+       status = c_avl_get (plugins_loaded, name, /* ret_value = */ NULL);
+       return (status == 0);
+}
+
+static int plugin_mark_loaded (char const *name)
+{
+       char *name_copy;
+       int status;
+
+       name_copy = strdup (name);
+       if (name_copy == NULL)
+               return (ENOMEM);
+
+       status = c_avl_insert (plugins_loaded,
+                       /* key = */ name_copy, /* value = */ NULL);
+       return (status);
+}
+
+static void plugin_free_loaded ()
+{
+       void *key;
+       void *value;
+
+       if (plugins_loaded == NULL)
+               return;
+
+       while (c_avl_pick (plugins_loaded, &key, &value) == 0)
+       {
+               sfree (key);
+               assert (value == NULL);
+       }
+
+       c_avl_destroy (plugins_loaded);
+       plugins_loaded = NULL;
+}
+
 #define BUFSIZE 512
-int plugin_load (const char *type, uint32_t flags)
+int plugin_load (char const *plugin_name, uint32_t flags)
 {
        DIR  *dh;
        const char *dir;
@@ -843,15 +901,38 @@ int plugin_load (const char *type, uint32_t flags)
        struct dirent *de;
        int status;
 
+       if (plugin_name == NULL)
+               return (EINVAL);
+
+       /* Check if plugin is already loaded and don't do anything in this
+        * case. */
+       if (plugin_is_loaded (plugin_name))
+               return (0);
+
        dir = plugin_get_dir ();
        ret = 1;
 
+       /*
+        * XXX: Magic at work:
+        *
+        * Some of the language bindings, for example the Python and Perl
+        * plugins, need to be able to export symbols to the scripts they run.
+        * For this to happen, the "Globals" flag needs to be set.
+        * Unfortunately, this technical detail is hard to explain to the
+        * average user and she shouldn't have to worry about this, ideally.
+        * So in order to save everyone's sanity use a different default for a
+        * handful of special plugins. --octo
+        */
+       if ((strcasecmp ("perl", plugin_name) == 0)
+                       || (strcasecmp ("python", plugin_name) == 0))
+               flags |= PLUGIN_FLAGS_GLOBAL;
+
        /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
         * type when matching the filename */
-       status = ssnprintf (typename, sizeof (typename), "%s.so", type);
+       status = ssnprintf (typename, sizeof (typename), "%s.so", plugin_name);
        if ((status < 0) || ((size_t) status >= sizeof (typename)))
        {
-               WARNING ("plugin_load: Filename too long: \"%s.so\"", type);
+               WARNING ("plugin_load: Filename too long: \"%s.so\"", plugin_name);
                return (-1);
        }
        typename_len = strlen (typename);
@@ -898,13 +979,14 @@ int plugin_load (const char *type, uint32_t flags)
                if (status == 0)
                {
                        /* success */
+                       plugin_mark_loaded (plugin_name);
                        ret = 0;
                        break;
                }
                else
                {
                        ERROR ("plugin_load: Load plugin \"%s\" failed with "
-                                       "status %i.", type, status);
+                                       "status %i.", plugin_name, status);
                }
        }
 
@@ -912,7 +994,7 @@ int plugin_load (const char *type, uint32_t flags)
 
        if (filename[0] == 0)
                ERROR ("plugin_load: Could not find plugin \"%s\" in %s",
-                               type, dir);
+                               plugin_name, dir);
 
        return (ret);
 }
@@ -1048,7 +1130,7 @@ int plugin_register_read (const char *name,
        rf->rf_udata.free_func = NULL;
        rf->rf_ctx = plugin_get_ctx ();
        rf->rf_group[0] = '\0';
-       sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
+       rf->rf_name = strdup (name);
        rf->rf_type = RF_SIMPLE;
        rf->rf_interval = plugin_get_interval ();
 
@@ -1080,7 +1162,7 @@ int plugin_register_complex_read (const char *group, const char *name,
                sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
        else
                rf->rf_group[0] = '\0';
-       sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
+       rf->rf_name = strdup (name);
        rf->rf_type = RF_COMPLEX;
        if (interval != NULL)
                rf->rf_interval = TIMESPEC_TO_CDTIME_T (interval);
@@ -1373,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;
 
@@ -1386,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;
 
@@ -1679,6 +1784,7 @@ void plugin_shutdown_all (void)
        destroy_all_callbacks (&list_shutdown);
        destroy_all_callbacks (&list_log);
 
+       plugin_free_loaded ();
        plugin_free_data_sets ();
 } /* void plugin_shutdown_all */
 
@@ -1900,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 635ff308f2cd4f83278554f4f79759a5b2efaeb4..8f0c6d86105fbfe56547d6b5d78ac9fd7a7f4b26 100644 (file)
@@ -223,7 +223,8 @@ void plugin_set_dir (const char *dir);
  *  and a value below zero if an error occurs.
  *
  * NOTES
- *  No attempt is made to re-load an already loaded module.
+ *  Re-loading an already loaded module is detected and zero is returned in
+ *  this case.
  */
 int plugin_load (const char *name, uint32_t flags);
 
index 71da54749fe1d1a6377bbe450551e04658e433bb..c795e2656502685ca353937ddda2b9defcd3a518 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;
@@ -241,6 +241,45 @@ static int value_list_to_string (char *buffer, int buffer_len,
                offset += status;
        } /* 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 */
 
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);
+}
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 25cbcd4c1b5188df787e843c60aecb895aad5688..bcb157255fcd1a30a8048d7977271cb1dc46e027 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 603f85bbf6a96595ee04891127f37006d90730aa..27c92bc730c65b44c54c25bd9dc27a75c279980c 100644 (file)
@@ -80,12 +80,12 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
        if (device_list && ignorelist_match (device_list, name))
                return -1;
 
-       len = snprintf (filename, sizeof (filename),
+       len = ssnprintf (filename, sizeof (filename),
                        "%s/%s/temp", dirname_sysfs, name);
        if ((len < 0) || ((size_t) len >= sizeof (filename)))
                return -1;
 
-       len = read_file_contents (filename, data, sizeof(data));
+       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
        if (len > 1 && data[--len] == '\n') {
                char *endptr = NULL;
                double temp;
@@ -100,12 +100,12 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
                }
        }
 
-       len = snprintf (filename, sizeof (filename),
+       len = ssnprintf (filename, sizeof (filename),
                        "%s/%s/cur_state", dirname_sysfs, name);
        if ((len < 0) || ((size_t) len >= sizeof (filename)))
                return -1;
 
-       len = read_file_contents (filename, data, sizeof(data));
+       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
        if (len > 1 && data[--len] == '\n') {
                char *endptr = NULL;
                double state;
@@ -139,12 +139,12 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir,
         * temperature:             55 C
         */
        
-       len = snprintf (filename, sizeof (filename),
+       len = ssnprintf (filename, sizeof (filename),
                        "%s/%s/temperature", dirname_procfs, name);
        if ((len < 0) || ((size_t) len >= sizeof (filename)))
                return -1;
 
-       len = read_file_contents (filename, data, sizeof(data));
+       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
        if ((len > 0) && ((size_t) len > sizeof(str_temp))
                        && (data[--len] == '\n')
                        && (! strncmp(data, str_temp, sizeof(str_temp)-1))) {
index fb443d9e99023e59bc367e2579c6adafe7ce7bb3..3a5e207a7e99d6cea56e15d0c85f98e7312135f6 100644 (file)
@@ -6,6 +6,7 @@ apache_requests         value:DERIVE:0:U
 apache_scoreboard      value:GAUGE:0:65535
 ath_nodes              value:GAUGE:0:65535
 ath_stat               value:DERIVE:0:U
+backends               value:GAUGE:0:65535
 bitrate                        value:GAUGE:0:4294967295
 bytes                  value:GAUGE:0:U
 cache_eviction         value:DERIVE:0:U
@@ -86,7 +87,7 @@ 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:5000, midterm:GAUGE:0:5000, longterm:GAUGE:0:5000
 md_disks               value:GAUGE:0:U
@@ -110,8 +111,11 @@ node_octets                rx:DERIVE:0:U, tx:DERIVE:0:U
 node_rssi              value:GAUGE:0:255
 node_stat              value:DERIVE:0:U
 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
@@ -145,6 +149,7 @@ queue_length                value:GAUGE:0:U
 records                        value:GAUGE:0:U
 requests               value:GAUGE:0:U
 response_time          value:GAUGE:0:U
+response_code          value:GAUGE:0:U
 route_etx              value:GAUGE:0:U
 route_metric           value:GAUGE:0:U
 routes                 value:GAUGE:0:U
@@ -155,16 +160,18 @@ 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
 time_offset            value:GAUGE:-1000000:1000000
 total_bytes            value:DERIVE:0:U
 total_connections      value:DERIVE:0:U
+total_objects          value:DERIVE:0:U
 total_operations       value:DERIVE:0:U
 total_requests         value:DERIVE:0:U
 total_sessions         value:DERIVE:0:U
@@ -173,6 +180,7 @@ total_time_in_ms    value:DERIVE:0:U
 total_values           value:DERIVE:0:U
 uptime                 value:GAUGE:0:4294967295
 users                  value:GAUGE:0:65535
+vcl                    value:GAUGE:0:65535
 vcpu                   value:GAUGE:0:U
 virt_cpu_total         value:DERIVE:0:U
 virt_vcpu              value:DERIVE:0:U
@@ -186,6 +194,7 @@ voltage                     value:GAUGE:U:U
 vs_memory              value:GAUGE:0:9223372036854775807
 vs_processes           value:GAUGE:0:65535
 vs_threads             value:GAUGE:0:65535
+
 #
 # Legacy types
 # (required for the v5 upgrade target)
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 5de3389d862eb41828b5fb73a55f1d5fe2197104..3a8dddd78ac05bbf2633c362dda2f90a350e0d8a 100644 (file)
  *   Florian octo Forster <octo at collectd.org>
  **/
 
-/**
- * Current list of what is monitored and what is not monitored (yet)
- * {{{
- * Field name           Description                           Monitored
- * ----------           -----------                           ---------
- * uptime               Child uptime                              N
- * client_conn          Client connections accepted               Y
- * client_drop          Connection dropped, no sess               Y
- * client_req           Client requests received                  Y
- * cache_hit            Cache hits                                Y
- * cache_hitpass        Cache hits for pass                       Y
- * cache_miss           Cache misses                              Y
- * backend_conn         Backend conn. success                     Y
- * backend_unhealthy    Backend conn. not attempted               Y
- * backend_busy         Backend conn. too many                    Y
- * backend_fail         Backend conn. failures                    Y
- * backend_reuse        Backend conn. reuses                      Y
- * backend_toolate      Backend conn. was closed                  Y
- * backend_recycle      Backend conn. recycles                    Y
- * backend_unused       Backend conn. unused                      Y
- * fetch_head           Fetch head                                Y
- * fetch_length         Fetch with Length                         Y
- * fetch_chunked        Fetch chunked                             Y
- * fetch_eof            Fetch EOF                                 Y
- * fetch_bad            Fetch had bad headers                     Y
- * fetch_close          Fetch wanted close                        Y
- * fetch_oldhttp        Fetch pre HTTP/1.1 closed                 Y
- * fetch_zero           Fetch zero len                            Y
- * fetch_failed         Fetch failed                              Y
- * n_sess_mem           N struct sess_mem                         N
- * n_sess               N struct sess                             N
- * n_object             N struct object                           N
- * n_vampireobject      N unresurrected objects                   N
- * n_objectcore         N struct objectcore                       N
- * n_objecthead         N struct objecthead                       N
- * n_smf                N struct smf                              N
- * n_smf_frag           N small free smf                          N
- * n_smf_large          N large free smf                          N
- * n_vbe_conn           N struct vbe_conn                         N
- * n_wrk                N worker threads                          Y
- * n_wrk_create         N worker threads created                  Y
- * n_wrk_failed         N worker threads not created              Y
- * n_wrk_max            N worker threads limited                  Y
- * n_wrk_queue          N queued work requests                    Y
- * n_wrk_overflow       N overflowed work requests                Y
- * n_wrk_drop           N dropped work requests                   Y
- * n_backend            N backends                                N
- * n_expired            N expired objects                         N
- * n_lru_nuked          N LRU nuked objects                       N
- * n_lru_saved          N LRU saved objects                       N
- * n_lru_moved          N LRU moved objects                       N
- * n_deathrow           N objects on deathrow                     N
- * losthdr              HTTP header overflows                     N
- * n_objsendfile        Objects sent with sendfile                N
- * n_objwrite           Objects sent with write                   N
- * n_objoverflow        Objects overflowing workspace             N
- * s_sess               Total Sessions                            Y
- * s_req                Total Requests                            Y
- * s_pipe               Total pipe                                Y
- * s_pass               Total pass                                Y
- * s_fetch              Total fetch                               Y
- * s_hdrbytes           Total header bytes                        Y
- * s_bodybytes          Total body bytes                          Y
- * sess_closed          Session Closed                            N
- * sess_pipeline        Session Pipeline                          N
- * sess_readahead       Session Read Ahead                        N
- * sess_linger          Session Linger                            N
- * sess_herd            Session herd                              N
- * shm_records          SHM records                               Y
- * shm_writes           SHM writes                                Y
- * shm_flushes          SHM flushes due to overflow               Y
- * shm_cont             SHM MTX contention                        Y
- * shm_cycles           SHM cycles through buffer                 Y
- * sm_nreq              allocator requests                        Y
- * sm_nobj              outstanding allocations                   Y
- * sm_balloc            bytes allocated                           Y
- * sm_bfree             bytes free                                Y
- * sma_nreq             SMA allocator requests                    Y
- * sma_nobj             SMA outstanding allocations               Y
- * sma_nbytes           SMA outstanding bytes                     Y
- * sma_balloc           SMA bytes allocated                       Y
- * sma_bfree            SMA bytes free                            Y
- * sms_nreq             SMS allocator requests                    Y
- * sms_nobj             SMS outstanding allocations               Y
- * sms_nbytes           SMS outstanding bytes                     Y
- * sms_balloc           SMS bytes allocated                       Y
- * sms_bfree            SMS bytes freed                           Y
- * backend_req          Backend requests made                     N
- * n_vcl                N vcl total                               N
- * n_vcl_avail          N vcl available                           N
- * n_vcl_discard        N vcl discarded                           N
- * n_purge              N total active purges                     N
- * n_purge_add          N new purges added                        N
- * n_purge_retire       N old purges deleted                      N
- * n_purge_obj_test     N objects tested                          N
- * n_purge_re_test      N regexps tested against                  N
- * n_purge_dups         N duplicate purges removed                N
- * hcb_nolock           HCB Lookups without lock                  Y
- * hcb_lock             HCB Lookups with lock                     Y
- * hcb_insert           HCB Inserts                               Y
- * esi_parse            Objects ESI parsed (unlock)               Y
- * esi_errors           ESI parse errors (unlock)                 Y
- * }}}
- */
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
@@ -151,15 +47,30 @@ struct user_config_s {
        _Bool collect_connections;
        _Bool collect_esi;
        _Bool collect_backend;
+#ifdef HAVE_VARNISH_V3
+       _Bool collect_dirdns;
+#endif
        _Bool collect_fetch;
        _Bool collect_hcb;
+       _Bool collect_objects;
+#if HAVE_VARNISH_V2
+       _Bool collect_purge;
+#else
+       _Bool collect_ban;
+#endif
+       _Bool collect_session;
        _Bool collect_shm;
        _Bool collect_sms;
 #if HAVE_VARNISH_V2
        _Bool collect_sm;
        _Bool collect_sma;
 #endif
+       _Bool collect_struct;
        _Bool collect_totals;
+#ifdef HAVE_VARNISH_V3
+       _Bool collect_uptime;
+#endif
+       _Bool collect_vcl;
        _Bool collect_workers;
 };
 typedef struct user_config_s user_config_t; /* }}} */
@@ -238,13 +149,30 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance, "connections", "connections", "received", stats->client_req);
        }
 
+#ifdef HAVE_VARNISH_V3
+       if (conf->collect_dirdns)
+       {
+               /* DNS director lookups */
+               varnish_submit_derive (conf->instance, "dirdns", "cache_operation", "lookups",    stats->dir_dns_lookups);
+               /* DNS director failed lookups */
+               varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "failed",     stats->dir_dns_failed);
+               /* DNS director cached lookups hit */
+               varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "hits",       stats->dir_dns_hit);
+               /* DNS director full dnscache */
+               varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "cache_full", stats->dir_dns_cache_full);
+       }
+#endif
+
        if (conf->collect_esi)
        {
                /* ESI parse errors (unlock)   */
-               varnish_submit_derive (conf->instance, "esi", "total_operations", "error",  stats->esi_errors);
+               varnish_submit_derive (conf->instance, "esi", "total_operations", "error",   stats->esi_errors);
 #if HAVE_VARNISH_V2
                /* Objects ESI parsed (unlock) */
-               varnish_submit_derive (conf->instance, "esi", "total_operations", "parsed", stats->esi_parse);
+               varnish_submit_derive (conf->instance, "esi", "total_operations", "parsed",  stats->esi_parse);
+#else
+               /* ESI parse warnings (unlock) */
+               varnish_submit_derive (conf->instance, "esi", "total_operations", "warning", stats->esi_warnings);
 #endif
        }
 
@@ -267,7 +195,14 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
 #if HAVE_VARNISH_V2
                /* Backend conn. unused        */
                varnish_submit_derive (conf->instance, "backend", "connections", "unused"       , stats->backend_unused);
+#else
+               /* Backend conn. retry         */
+               varnish_submit_derive (conf->instance, "backend", "connections", "retries"      , stats->backend_retry);
 #endif
+               /* Backend requests mades      */
+               varnish_submit_derive (conf->instance, "backend", "http_requests", "requests"   , stats->backend_req);
+               /* N backends                  */
+               varnish_submit_gauge  (conf->instance, "backend", "backends", "n_backends"      , stats->n_backend);
        }
 
        if (conf->collect_fetch)
@@ -290,6 +225,14 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance, "fetch", "http_requests", "zero"       , stats->fetch_zero);
                /* Fetch failed              */
                varnish_submit_derive (conf->instance, "fetch", "http_requests", "failed"     , stats->fetch_failed);
+#if HAVE_VARNISH_V3
+               /* Fetch no body (1xx)       */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_1xx", stats->fetch_1xx);
+               /* Fetch no body (204)       */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_204", stats->fetch_204);
+               /* Fetch no body (304)       */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_304", stats->fetch_304);
+#endif
        }
 
        if (conf->collect_hcb)
@@ -302,6 +245,80 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance, "hcb", "cache_operation", "insert",        stats->hcb_insert);
        }
 
+       if (conf->collect_objects)
+       {
+               /* N expired objects             */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "expired",            stats->n_expired);
+               /* N LRU nuked objects           */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_nuked",          stats->n_lru_nuked);
+#if HAVE_VARNISH_V2
+               /* N LRU saved objects           */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_saved",          stats->n_lru_saved);
+#endif
+               /* N LRU moved objects           */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_moved",          stats->n_lru_moved);
+#if HAVE_VARNISH_V2
+               /* N objects on deathrow         */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "deathrow",           stats->n_deathrow);
+#endif
+               /* HTTP header overflows         */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "header_overflow",    stats->losthdr);
+               /* Objects sent with sendfile    */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "sent_sendfile",      stats->n_objsendfile);
+               /* Objects sent with write       */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "sent_write",         stats->n_objwrite);
+               /* Objects overflowing workspace */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "workspace_overflow", stats->n_objoverflow);
+       }
+
+#if HAVE_VARNISH_V2
+       if (conf->collect_purge)
+       {
+               /* N total active purges      */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "total",            stats->n_purge);
+               /* N new purges added         */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "added",            stats->n_purge_add);
+               /* N old purges deleted       */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "deleted",          stats->n_purge_retire);
+               /* N objects tested           */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "objects_tested",   stats->n_purge_obj_test);
+               /* N regexps tested against   */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "regexps_tested",   stats->n_purge_re_test);
+               /* N duplicate purges removed */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "duplicate",        stats->n_purge_dups);
+       }
+#else
+       if (conf->collect_ban)
+       {
+               /* N total active bans      */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "total",          stats->n_ban);
+               /* N new bans added         */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "added",          stats->n_ban_add);
+               /* N old bans deleted       */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "deleted",        stats->n_ban_retire);
+               /* N objects tested         */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "objects_tested", stats->n_ban_obj_test);
+               /* N regexps tested against */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "regexps_tested", stats->n_ban_re_test);
+               /* N duplicate bans removed */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "duplicate",      stats->n_ban_dups);
+       }
+#endif
+
+       if (conf->collect_session)
+       {
+               /* Session Closed     */
+               varnish_submit_derive (conf->instance, "session", "total_operations", "closed",    stats->sess_closed);
+               /* Session Pipeline   */
+               varnish_submit_derive (conf->instance, "session", "total_operations", "pipeline",  stats->sess_pipeline);
+               /* Session Read Ahead */
+               varnish_submit_derive (conf->instance, "session", "total_operations", "readahead", stats->sess_readahead);
+               /* Session Linger     */
+               varnish_submit_derive (conf->instance, "session", "total_operations", "linger",    stats->sess_linger);
+               /* Session herd       */
+               varnish_submit_derive (conf->instance, "session", "total_operations", "herd",      stats->sess_herd);
+       }
+
        if (conf->collect_shm)
        {
                /* SHM records                 */
@@ -358,6 +375,34 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance,  "sms", "total_bytes", "free",        stats->sms_bfree);
        }
 
+       if (conf->collect_struct)
+       {
+               /* N struct sess_mem       */
+               varnish_submit_gauge (conf->instance, "struct", "current_sessions", "sess_mem",  stats->n_sess_mem);
+               /* N struct sess           */
+               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
+               /* N struct smf            */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "smf",                stats->n_smf);
+               /* N small free smf         */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "smf_frag",           stats->n_smf_frag);
+               /* N large free smf         */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "smf_large",          stats->n_smf_large);
+               /* N struct vbe_conn        */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "vbe_conn",           stats->n_vbe_conn);
+#endif
+       }
+
        if (conf->collect_totals)
        {
                /* Total Sessions */
@@ -376,6 +421,24 @@ 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)
+       {
+               /* N vcl total     */
+               varnish_submit_gauge (conf->instance, "vcl", "vcl", "total_vcl",     stats->n_vcl);
+               /* N vcl available */
+               varnish_submit_gauge (conf->instance, "vcl", "vcl", "avail_vcl",     stats->n_vcl_avail);
+               /* N vcl discarded */
+               varnish_submit_gauge (conf->instance, "vcl", "vcl", "discarded_vcl", stats->n_vcl_discard);
+       }
+
        if (conf->collect_workers)
        {
                /* worker threads */
@@ -393,6 +456,11 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance, "workers", "total_requests", "queued",     stats->n_wrk_queue);
                /* overflowed work requests */
                varnish_submit_derive (conf->instance, "workers", "total_requests", "overflowed", stats->n_wrk_overflow);
+#else
+               /* queued work requests */
+               varnish_submit_derive (conf->instance, "workers", "total_requests", "queued",       stats->n_wrk_queued);
+               /* work request queue length */
+               varnish_submit_derive (conf->instance, "workers", "total_requests", "queue_length", stats->n_wrk_lqueue);
 #endif
        }
 } /* }}} void varnish_monitor */
@@ -486,16 +554,32 @@ static int varnish_config_apply_default (user_config_t *conf) /* {{{ */
        conf->collect_backend     = 1;
        conf->collect_cache       = 1;
        conf->collect_connections = 1;
+#ifdef HAVE_VARNISH_V3
+       conf->collect_dirdns      = 0;
+#endif
        conf->collect_esi         = 0;
        conf->collect_fetch       = 0;
        conf->collect_hcb         = 0;
+       conf->collect_objects     = 0;
+#if HAVE_VARNISH_V2
+       conf->collect_purge       = 0;
+#else
+       conf->collect_ban         = 0;
+#endif
+       conf->collect_session     = 0;
        conf->collect_shm         = 1;
 #if HAVE_VARNISH_V2
        conf->collect_sm          = 0;
        conf->collect_sma         = 0;
 #endif
        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;
 
        return (0);
 } /* }}} int varnish_config_apply_default */
@@ -580,12 +664,27 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                        cf_util_get_boolean (child, &conf->collect_connections);
                else if (strcasecmp ("CollectESI", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_esi);
+#ifdef HAVE_VARNISH_V3
+               else if (strcasecmp ("CollectDirectorDNS", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_dirdns);
+#endif
                else if (strcasecmp ("CollectBackend", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_backend);
                else if (strcasecmp ("CollectFetch", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_fetch);
                else if (strcasecmp ("CollectHCB", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_hcb);
+               else if (strcasecmp ("CollectObjects", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_objects);
+#if HAVE_VARNISH_V2
+               else if (strcasecmp ("CollectPurge", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_purge);
+#else
+               else if (strcasecmp ("CollectBan", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_ban);
+#endif
+               else if (strcasecmp ("CollectSession", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_session);
                else if (strcasecmp ("CollectSHM", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_shm);
                else if (strcasecmp ("CollectSMS", child->key) == 0)
@@ -596,8 +695,16 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                else if (strcasecmp ("CollectSM", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_sm);
 #endif
+               else if (strcasecmp ("CollectStruct", child->key) == 0)
+                       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)
                        cf_util_get_boolean (child, &conf->collect_workers);
                else
@@ -614,15 +721,30 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                        && !conf->collect_connections
                        && !conf->collect_esi
                        && !conf->collect_backend
+#ifdef HAVE_VARNISH_V3
+                       && !conf->collect_dirdns
+#endif
                        && !conf->collect_fetch
                        && !conf->collect_hcb
+                       && !conf->collect_objects
+#if HAVE_VARNISH_V2
+                       && !conf->collect_purge
+#else
+                       && !conf->collect_ban
+#endif
+                       && !conf->collect_session
                        && !conf->collect_shm
                        && !conf->collect_sms
 #if HAVE_VARNISH_V2
                        && !conf->collect_sma
                        && !conf->collect_sm
 #endif
+                       && !conf->collect_struct
                        && !conf->collect_totals
+#ifdef HAVE_VARNISH_V3
+                       && !conf->collect_uptime
+#endif
+                       && !conf->collect_vcl
                        && !conf->collect_workers)
        {
                WARNING ("Varnish plugin: No metric has been configured for "
index 99c6f4d80a91a669ee0ac86f0bfe0620de6bb13f..6124d33edf5a07ae26f922b1cc7a151a959b6c28 100644 (file)
@@ -35,6 +35,8 @@
   *   <Carbon>
   *     Host "localhost"
   *     Port "2003"
+  *     Protocol "udp"
+  *     LogSendErrors true
   *     Prefix "collectd"
   *   </Carbon>
   * </Plugin>
 # define WG_DEFAULT_SERVICE "2003"
 #endif
 
+#ifndef WG_DEFAULT_PROTOCOL
+# define WG_DEFAULT_PROTOCOL "udp"
+#endif
+
+#ifndef WG_DEFAULT_LOG_SEND_ERRORS
+# define WG_DEFAULT_LOG_SEND_ERRORS 1
+#endif
+
 #ifndef WG_DEFAULT_ESCAPE
 # define WG_DEFAULT_ESCAPE '_'
 #endif
@@ -84,6 +94,8 @@ struct wg_callback
 
     char    *node;
     char    *service;
+    char    *protocol;
+    _Bool   log_send_errors;
     char    *prefix;
     char    *postfix;
     char     escape_char;
@@ -116,10 +128,11 @@ static int wg_send_buffer (struct wg_callback *cb)
     ssize_t status = 0;
 
     status = swrite (cb->sock_fd, cb->send_buf, strlen (cb->send_buf));
-    if (status < 0)
+    if (cb->log_send_errors && status < 0)
     {
         char errbuf[1024];
-        ERROR ("write_graphite plugin: send failed with status %zi (%s)",
+        ERROR ("write_graphite plugin: send to %s:%s (%s) failed with status %zi (%s)",
+                cb->node, cb->service, cb->protocol,
                 status, sstrerror (errno, errbuf, sizeof (errbuf)));
 
 
@@ -173,6 +186,7 @@ static int wg_callback_init (struct wg_callback *cb)
 
     const char *node = cb->node ? cb->node : WG_DEFAULT_NODE;
     const char *service = cb->service ? cb->service : WG_DEFAULT_SERVICE;
+    const char *protocol = cb->protocol ? cb->protocol : WG_DEFAULT_PROTOCOL;
 
     if (cb->sock_fd > 0)
         return (0);
@@ -182,15 +196,19 @@ static int wg_callback_init (struct wg_callback *cb)
     ai_hints.ai_flags |= AI_ADDRCONFIG;
 #endif
     ai_hints.ai_family = AF_UNSPEC;
-    ai_hints.ai_socktype = SOCK_STREAM;
+
+    if (0 == strcasecmp ("tcp", protocol))
+        ai_hints.ai_socktype = SOCK_STREAM;
+    else
+        ai_hints.ai_socktype = SOCK_DGRAM;
 
     ai_list = NULL;
 
     status = getaddrinfo (node, service, &ai_hints, &ai_list);
     if (status != 0)
     {
-        ERROR ("write_graphite plugin: getaddrinfo (%s, %s) failed: %s",
-                node, service, gai_strerror (status));
+        ERROR ("write_graphite plugin: getaddrinfo (%s, %s, %s) failed: %s",
+                node, service, protocol, gai_strerror (status));
         return (-1);
     }
 
@@ -219,16 +237,16 @@ static int wg_callback_init (struct wg_callback *cb)
     {
         char errbuf[1024];
         c_complain (LOG_ERR, &cb->init_complaint,
-                "write_graphite plugin: Connecting to %s:%s failed. "
-                "The last error was: %s", node, service,
+                "write_graphite plugin: Connecting to %s:%s via %s failed. "
+                "The last error was: %s", node, service, protocol,
                 sstrerror (errno, errbuf, sizeof (errbuf)));
         return (-1);
     }
     else
     {
         c_release (LOG_INFO, &cb->init_complaint,
-                "write_graphite plugin: Successfully connected to %s:%s.",
-                node, service);
+                "write_graphite plugin: Successfully connected to %s:%s via %s.",
+                node, service, protocol);
     }
 
     wg_reset_buffer (cb);
@@ -257,6 +275,7 @@ static void wg_callback_free (void *data)
 
     sfree(cb->name);
     sfree(cb->node);
+    sfree(cb->protocol);
     sfree(cb->service);
     sfree(cb->prefix);
     sfree(cb->postfix);
@@ -337,9 +356,10 @@ static int wg_send_message (char const *message, struct wg_callback *cb)
     cb->send_buf_fill += message_len;
     cb->send_buf_free -= message_len;
 
-    DEBUG ("write_graphite plugin: [%s]:%s buf %zu/%zu (%.1f %%) \"%s\"",
+    DEBUG ("write_graphite plugin: [%s]:%s (%s) buf %zu/%zu (%.1f %%) \"%s\"",
             cb->node,
             cb->service,
+            cb->protocol,
             cb->send_buf_fill, sizeof (cb->send_buf),
             100.0 * ((double) cb->send_buf_fill) / ((double) sizeof (cb->send_buf)),
             message);
@@ -429,6 +449,7 @@ static int wg_config_node (oconfig_item_t *ci)
     user_data_t user_data;
     char callback_name[DATA_MAX_NAME_LEN];
     int i;
+    int status = 0;
 
     cb = malloc (sizeof (*cb));
     if (cb == NULL)
@@ -441,6 +462,8 @@ static int wg_config_node (oconfig_item_t *ci)
     cb->name = NULL;
     cb->node = NULL;
     cb->service = NULL;
+    cb->protocol = NULL;
+    cb->log_send_errors = WG_DEFAULT_LOG_SEND_ERRORS;
     cb->prefix = NULL;
     cb->postfix = NULL;
     cb->escape_char = WG_DEFAULT_ESCAPE;
@@ -449,7 +472,7 @@ static int wg_config_node (oconfig_item_t *ci)
     /* FIXME: Legacy configuration syntax. */
     if (strcasecmp ("Carbon", ci->key) != 0)
     {
-        int status = cf_util_get_string (ci, &cb->name);
+        status = cf_util_get_string (ci, &cb->name);
         if (status != 0)
         {
             wg_callback_free (cb);
@@ -468,6 +491,20 @@ static int wg_config_node (oconfig_item_t *ci)
             cf_util_get_string (child, &cb->node);
         else if (strcasecmp ("Port", child->key) == 0)
             cf_util_get_service (child, &cb->service);
+        else if (strcasecmp ("Protocol", child->key) == 0)
+        {
+            cf_util_get_string (child, &cb->protocol);
+
+            if (strcasecmp ("UDP", cb->protocol) != 0 &&
+                strcasecmp ("TCP", cb->protocol) != 0)
+            {
+                ERROR ("write_graphite plugin: Unknown protocol (%s)",
+                        cb->protocol);
+                status = -1;
+            }
+        }
+        else if (strcasecmp ("LogSendErrors", child->key) == 0)
+            cf_util_get_boolean (child, &cb->log_send_errors);
         else if (strcasecmp ("Prefix", child->key) == 0)
             cf_util_get_string (child, &cb->prefix);
         else if (strcasecmp ("Postfix", child->key) == 0)
@@ -487,14 +524,25 @@ static int wg_config_node (oconfig_item_t *ci)
         {
             ERROR ("write_graphite plugin: Invalid configuration "
                         "option: %s.", child->key);
+            status = -1;
         }
+
+        if (status != 0)
+            break;
+    }
+
+    if (status != 0)
+    {
+        wg_callback_free (cb);
+        return (status);
     }
 
     /* FIXME: Legacy configuration syntax. */
     if (cb->name == NULL)
-        ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s/%s",
+        ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s/%s/%s",
                 cb->node != NULL ? cb->node : WG_DEFAULT_NODE,
-                cb->service != NULL ? cb->service : WG_DEFAULT_SERVICE);
+                cb->service != NULL ? cb->service : WG_DEFAULT_SERVICE,
+                cb->protocol != NULL ? cb->protocol : WG_DEFAULT_PROTOCOL);
     else
         ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s",
                 cb->name);
index 15bb23787c59f7b4f01d543c621a75fee2be42f4..3345d0444a94653a4e3be42a9d9051bfd264e790 100644 (file)
@@ -37,6 +37,7 @@
 
 #define RIEMANN_HOST           "localhost"
 #define RIEMANN_PORT           "5555"
+#define RIEMANN_TTL_FACTOR      2.0
 
 struct riemann_host {
        char                    *name;
@@ -49,6 +50,7 @@ struct riemann_host {
        char                    *service;
        _Bool                    use_tcp;
        int                      s;
+       double                   ttl_factor;
 
        int                      reference_count;
 };
@@ -376,6 +378,7 @@ static Event *riemann_value_to_protobuf (struct riemann_host const *host, /* {{{
        Event *event;
        char name_buffer[5 * DATA_MAX_NAME_LEN];
        char service_buffer[6 * DATA_MAX_NAME_LEN];
+       double ttl;
        int i;
 
        event = malloc (sizeof (*event));
@@ -390,7 +393,9 @@ static Event *riemann_value_to_protobuf (struct riemann_host const *host, /* {{{
        event->host = strdup (vl->host);
        event->time = CDTIME_T_TO_TIME_T (vl->time);
        event->has_time = 1;
-       event->ttl = CDTIME_T_TO_TIME_T (2 * vl->interval);
+
+       ttl = CDTIME_T_TO_DOUBLE (vl->interval) * host->ttl_factor;
+       event->ttl = (float) ttl;
        event->has_ttl = 1;
 
        riemann_event_add_attribute (event, "plugin", vl->plugin);
@@ -608,6 +613,7 @@ riemann_config_node(oconfig_item_t *ci)
        host->store_rates = 1;
        host->always_append_ds = 0;
        host->use_tcp = 0;
+       host->ttl_factor = RIEMANN_TTL_FACTOR;
 
        status = cf_util_get_string (ci, &host->name);
        if (status != 0) {
@@ -667,6 +673,33 @@ riemann_config_node(oconfig_item_t *ci)
                                        &host->always_append_ds);
                        if (status != 0)
                                break;
+               } else if (strcasecmp ("TTLFactor", child->key) == 0) {
+                       double tmp = NAN;
+                       status = cf_util_get_double (child, &tmp);
+                       if (status != 0)
+                               break;
+                       if (tmp >= 2.0) {
+                               host->ttl_factor = tmp;
+                       } else if (tmp >= 1.0) {
+                               NOTICE ("write_riemann plugin: The configured "
+                                               "TTLFactor is very small "
+                                               "(%.1f). A value of 2.0 or "
+                                               "greater is recommended.",
+                                               tmp);
+                               host->ttl_factor = tmp;
+                       } else if (tmp > 0.0) {
+                               WARNING ("write_riemann plugin: The configured "
+                                               "TTLFactor is too small to be "
+                                               "useful (%.1f). I'll use it "
+                                               "since the user knows best, "
+                                               "but under protest.",
+                                               tmp);
+                               host->ttl_factor = tmp;
+                       } else { /* zero, negative and NAN */
+                               ERROR ("write_riemann plugin: The configured "
+                                               "TTLFactor is invalid (%.1f).",
+                                               tmp);
+                       }
                } else {
                        WARNING("write_riemann plugin: ignoring unknown config "
                                "option: \"%s\"", child->key);
index aa900193b834e32b5d71c282f11663fe824a4e47..2edba6d1b68d4abc803b06145d6b2f5376503eca 100644 (file)
@@ -2,6 +2,7 @@
  * collectd - src/zfs_arc.c
  * Copyright (C) 2009  Anthony Dewhurst
  * Copyright (C) 2012  Aurelien Rougemont
+ * Copyright (C) 2013  Xin Li
  *
  * 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
@@ -19,6 +20,7 @@
  * Authors:
  *   Anthony Dewhurst <dewhurst at gmail>
  *   Aurelien Rougemont <beorn at gandi.net>
+ *   Xin Li <delphij at FreeBSD.org>
  **/
 
 #include "collectd.h"
  * Global variables
  */
 
+#if !defined(__FreeBSD__)
 extern kstat_ctl_t *kc;
 
+static long long get_zfs_value(kstat_t *ksp, char *name)
+{
+
+       return (get_kstat_value(ksp, name));
+}
+#else
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+const char zfs_arcstat[] = "kstat.zfs.misc.arcstats.";
+
+#if !defined(kstat_t)
+typedef void kstat_t;
+#endif
+
+static long long get_zfs_value(kstat_t *dummy __attribute__((unused)),
+               char const *name)
+{
+       char buffer[256];
+       long long value;
+       size_t valuelen = sizeof(value);
+       int rv;
+
+       ssnprintf (buffer, sizeof (buffer), "%s%s", zfs_arcstat, name);
+       rv = sysctlbyname (buffer, (void *) &value, &valuelen,
+                       /* new value = */ NULL, /* new length = */ (size_t) 0);
+       if (rv == 0)
+               return (value);
+
+       return (-1);
+}
+#endif
+
 static void za_submit (const char* type, const char* type_instance, value_t* values, int values_len)
 {
        value_list_t vl = VALUE_LIST_INIT;
@@ -60,7 +96,7 @@ static int za_read_derive (kstat_t *ksp, const char *kstat_value,
   long long tmp;
   value_t v;
 
-  tmp = get_kstat_value (ksp, (char *)kstat_value);
+  tmp = get_zfs_value (ksp, (char *)kstat_value);
   if (tmp == -1LL)
   {
     ERROR ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
@@ -78,7 +114,7 @@ static int za_read_gauge (kstat_t *ksp, const char *kstat_value,
   long long tmp;
   value_t v;
 
-  tmp = get_kstat_value (ksp, (char *)kstat_value);
+  tmp = get_zfs_value (ksp, (char *)kstat_value);
   if (tmp == -1LL)
   {
     ERROR ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
@@ -111,27 +147,29 @@ static int za_read (void)
        value_t  l2_io[2];
        kstat_t  *ksp   = NULL;
 
+#if !defined(__FreeBSD__)
        get_kstat (&ksp, "zfs", 0, "arcstats");
        if (ksp == NULL)
        {
                ERROR ("zfs_arc plugin: Cannot find zfs:0:arcstats kstat.");
                return (-1);
        }
+#endif
 
        /* Sizes */
        za_read_gauge (ksp, "size",    "cache_size", "arc");
        za_read_gauge (ksp, "l2_size", "cache_size", "L2");
 
-        /* Operations */
+       /* Operations */
        za_read_derive (ksp, "allocated","cache_operation", "allocated");
        za_read_derive (ksp, "deleted",  "cache_operation", "deleted");
        za_read_derive (ksp, "stolen",   "cache_operation", "stolen");
 
-        /* Issue indicators */
-        za_read_derive (ksp, "mutex_miss", "mutex_operations", "miss");
+       /* Issue indicators */
+       za_read_derive (ksp, "mutex_miss", "mutex_operations", "miss");
        za_read_derive (ksp, "hash_collisions", "hash_collisions", "");
        
-        /* Evictions */
+       /* Evictions */
        za_read_derive (ksp, "evict_l2_cached",     "cache_eviction", "cached");
        za_read_derive (ksp, "evict_l2_eligible",   "cache_eviction", "eligible");
        za_read_derive (ksp, "evict_l2_ineligible", "cache_eviction", "ineligible");
@@ -147,17 +185,17 @@ static int za_read (void)
        za_read_derive (ksp, "prefetch_metadata_misses", "cache_result", "prefetch_metadata-miss");
 
        /* Ratios */
-       arc_hits   = (gauge_t) get_kstat_value(ksp, "hits");
-       arc_misses = (gauge_t) get_kstat_value(ksp, "misses");
-       l2_hits    = (gauge_t) get_kstat_value(ksp, "l2_hits");
-       l2_misses  = (gauge_t) get_kstat_value(ksp, "l2_misses");
+       arc_hits   = (gauge_t) get_zfs_value(ksp, "hits");
+       arc_misses = (gauge_t) get_zfs_value(ksp, "misses");
+       l2_hits    = (gauge_t) get_zfs_value(ksp, "l2_hits");
+       l2_misses  = (gauge_t) get_zfs_value(ksp, "l2_misses");
 
        za_submit_ratio ("arc", arc_hits, arc_misses);
        za_submit_ratio ("L2", l2_hits, l2_misses);
 
        /* I/O */
-       l2_io[0].derive = get_kstat_value(ksp, "l2_read_bytes");
-       l2_io[1].derive = get_kstat_value(ksp, "l2_write_bytes");
+       l2_io[0].derive = get_zfs_value(ksp, "l2_read_bytes");
+       l2_io[1].derive = get_zfs_value(ksp, "l2_write_bytes");
 
        za_submit ("io_octets", "L2", l2_io, /* num values = */ 2);
 
@@ -166,12 +204,14 @@ static int za_read (void)
 
 static int za_init (void) /* {{{ */
 {
+#if !defined(__FreeBSD__)
        /* kstats chain already opened by update_kstat (using *kc), verify everything went fine. */
        if (kc == NULL)
        {
                ERROR ("zfs_arc plugin: kstat chain control structure not available.");
                return (-1);
        }
+#endif
 
        return (0);
 } /* }}} int za_init */