Merge branch 'sh/plugin_interval'
authorFlorian Forster <octo@collectd.org>
Fri, 16 Nov 2012 06:53:27 +0000 (07:53 +0100)
committerFlorian Forster <octo@collectd.org>
Fri, 16 Nov 2012 06:55:52 +0000 (07:55 +0100)
Conflicts:
src/amqp.c
src/exec.c
src/memcached.c
src/unixsock.c

94 files changed:
AUTHORS
ChangeLog
Makefile.am
README
bindings/Makefile.am
configure.in
contrib/README
contrib/collectd.service [new file with mode: 0644]
contrib/collectd_network.py
contrib/collection3/bin/index.cgi
contrib/collection3/lib/Collectd/Graph/Type/Df.pm
contrib/exec-ksm.sh [new file with mode: 0755]
contrib/exec-nagios.conf
contrib/exec-nagios.px
contrib/migrate-4-5.px
contrib/php-collection/definitions.php
src/Makefile.am
src/aggregation.c [new file with mode: 0644]
src/amqp.c
src/apcups.c
src/bind.c
src/collectd-python.pod
src/collectd.c
src/collectd.conf.in
src/collectd.conf.pod
src/collectdctl.c
src/common.c
src/common.h
src/configfile.c
src/configfile.h
src/conntrack.c
src/contextswitch.c
src/curl_xml.c
src/df.c
src/disk.c
src/email.c
src/ethstat.c [new file with mode: 0644]
src/exec.c
src/interface.c
src/libcollectdclient/client.c
src/liboconfig/parser.y
src/logfile.c
src/md.c [new file with mode: 0644]
src/memcachec.c
src/memcached.c
src/meta_data.c
src/mysql.c
src/netapp.c
src/netlink.c
src/network.c
src/nfs.c
src/ntpd.c
src/numa.c [new file with mode: 0644]
src/oracle.c
src/perl.c
src/pinba.c
src/ping.c
src/plugin.c
src/plugin.h
src/postgresql.c
src/processes.c
src/python.c
src/pyvalues.c
src/redis.c
src/routeros.c
src/rrdcached.c
src/rrdtool.c
src/sensors.c
src/snmp.c
src/swap.c
src/syslog.c
src/target_scale.c
src/tcpconns.c
src/types.db
src/unixsock.c
src/uptime.c
src/utils_avltree.c
src/utils_cache.c
src/utils_db_query.c
src/utils_db_query.h
src/utils_dns.c
src/utils_fbhash.c
src/utils_format_graphite.c [new file with mode: 0644]
src/utils_format_graphite.h [new file with mode: 0644]
src/utils_format_json.c
src/utils_rrdcreate.c
src/utils_vl_lookup.c [new file with mode: 0644]
src/utils_vl_lookup.h [new file with mode: 0644]
src/utils_vl_lookup_test.c [new file with mode: 0644]
src/varnish.c
src/write_graphite.c [new file with mode: 0644]
src/write_mongodb.c
src/zfs_arc.c
version-gen.sh

diff --git a/AUTHORS b/AUTHORS
index 14e96be..78dbad1 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -54,6 +54,9 @@ Christophe Kalt <collectd at klb.taranis.org>
  - The version 3 `log' mode.
  - Many Solaris related hints and fixes.
 
+Cyril Feraudet <cyril at feraudet.com>
+ - ethstat plugin.
+
 Dan Berrange <berrange at redhat.com>
  - uuid plugin.
 
@@ -115,6 +118,9 @@ Marco Chiappero <marco at absence.it>
  - ip6tables support in the iptables plugin.
  - openvpn plugin (support for more status file formats)
 
+Michael Hanselmann <public at hansmi.ch>
+ - md plugin.
+
 Michael Stapelberg <michael+git at stapelberg.de>
  - OpenBSD port of the tcpconns plugin.
 
@@ -175,6 +181,9 @@ Rodolphe Quiédeville <rquiedeville at bearstech.com>
 Scott Garrett <sgarrett at technomancer.com>
  - tape plugin.
 
+Scott Sanders <scott at jssjr.com>
+ - Write-Graphite plugin.
+
 Sebastien Pahl <sebastien.pahl at dotcloud.com>
  - AMQP plugin.
 
@@ -191,6 +200,9 @@ Sven Trenkel <collectd at semidefinite.de>
  - netapp plugin.
  - python plugin.
 
+Thomas Meson <zllak at hycik.org>
+ - Graphite support for the AMQP plugin.
+
 Tomasz Pala <gotar at pld-linux.org>
  - conntrack plugin.
 
index 2e6b6e9..ef2023a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,217 @@
+2012-11-11, Version 5.1.1
+       * collectd: Create new directories with mode 0777 and let umask remove
+         unwanted permission bits.
+       * collectd: Build issues have been fixed.
+       * collectd: An incorrect assertion has been fixed in some common code
+         for Solaris. This should resolve pseudo-random assertion failures
+         under Solaris. Thanks to Jeff Blane for his help debugging this.
+       * collectd: A couple of memory leaks through PThread thread attributes
+         have been fixed. Thanks to Gerrie Roos for fixing these.
+       * collectdctl: Fix PUTVAL for data sets with multiple data sources.
+         Thanks to Cyril Feraudet for reporting this problem.
+       * contrib/migrate-4-5.px: Handle to "df" to "df_complex" conversion
+         correctly.
+       * apcups plugin: Improve the reconnect behavior.
+       * curl_xml plugin: The "Host" setting was silently ignored. Thanks to
+         Fabien Wernli for fixing this.
+       * df plugin: Ignore "rootfs" devices under Linux to avoid having them
+         reported twice. Thanks to Brune Prémont for fixing this.
+       * disk plugin: Fix incorrect computation of read and write latency (the
+         "disk_time" type). Previously, the numbers reported where too small
+         by a factor of "interval", e.g. when the interval is set to 10
+         seconds, the values were too low by a factor of 10. Thanks to Manuel
+         Sanmartin for reporting this problem.
+       * dns plugin: A build issue under Solaris has been fixed. A erroneous
+         define that could lead to the reporting of bad data has been fixed by
+         Daniel Sutto.
+       * ethstat plugin: An off-by-one error and potential use of
+         uninitialized memory has been fixed. Thanks to Mark Voelker for
+         reporting these problems.
+       * memcachec plugin: A bug in the configuration handling has been fixed.
+         Thanks to Pascal Hofmann for fixing this issue.
+       * mysql plugin: Fix a bug when registering multiple databases. Thanks
+         to Sebastian Harl for fixing this.
+       * netapp plugin: Correctly close the connection on communication
+         errors.
+       * netlink plugin: The function used to query statistics has been
+         changed to be more in line with iproute2's behavior. Thanks to
+         "KIvosak" for the patch.
+       * network plugin: Initialization of libgcrypt has been fixed. Thanks to
+         Chris Lundquist for his patch.
+       * oracle plugin: Error messages have been improved.
+       * ping plugin: Don't enter the exponential back-off mode when
+         ping_send() fails. This should make recovery after a network failure
+         much faster.
+       * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert
+         and Sven Trenkel for fixing this.
+       * redis plugin: Fix a compilation problem on FreeBSD. Thanks to
+         "biancalana" for the fix.
+       * rrdtool plugin: Fix an out-of-bounds array access when printing a
+         warning message. Thanks to Will Hawkins for fixing this bug.
+       * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been
+         added. Support for more complex / unusual MIBs / subtrees has been
+         added. Thanks to Mark Juric to test the changes and point out these
+         problems.
+       * varnish plugin: Support for multiple instances of Varnish 3 has been
+         fixed. Thanks to Jonathan Huot for the patch.
+       * write_mongodb plugin: Add compatibility with libmongo 0.6.0 and
+         later. Thanks to Chris Lundquist for this patch.
+
+2012-04-01, Version 5.1.0
+       * Build system, iptables plugin: The shipped version of libiptc has
+         been removed.
+       * collectd-nagios: A list of value lists can now be queried using
+         "-n LIST". Thanks to Sebastian Harl for his patches.
+       * bind plugin: The "ParseTime" option has been added. It allows to use
+         the system time rather than the time reported by BIND.
+       * curl, memcachec, tail plugins: The "ExcludeRegexp" option has been
+         added. Thanks to Peter Warasin for his initial patch.
+       * ethstat plugin: The new "ethstat" plugin reads performance statistics
+         directly from ethernet cards. Thanks to Cyril Feraudet for his patch.
+       * GenericJMX plugin: Support for querying MBean "Operations" (in
+         addition to "Attributes") has been added. Thanks to Pierre-Yves
+         Ritschard for his patch.
+       * irq plugin: The selection / ignore code now uses the default
+         ignorelist infrastructure, providing the standard feature set, e.g.
+         regex matching.
+       * md plugin: The new "md" plugin reports the number of disks in various
+         states in Linux software RAID devices. Thanks to Michael Hanselmann
+         for his patch.
+       * modbus plugin: Support for signed integer register types has been
+         added.
+       * nfs plugin: Support for Solaris has been added. Thanks to Cosmin
+         Ioiart for his patch.
+       * numa plugin: The new "numa" plugin reports statistics of the
+         Non-Uniform Memory Access (NUMA) subsystem of Linux.
+       * processes plugin: Various fixes for the FreeBSD implementation.
+         Thanks to Phil Kulin for his patch.
+       * rrdcached plugin: Passing flushes to the caching daemon has been
+         added.
+       * sensors plugin: The initialization code has been improved. Thanks to
+         Henrique de Moraes Holschuh for his patch.
+       * swap plugin: The "ReportByDevice" option has been added.
+       * syslog plugin: Support for writing notifications has been added.
+         Thanks to Fabien Wernli for his patch.
+       * tcpconns plugin: Support for AIX has been added. Thanks to Manuel
+         Luis Sanmartín Rozada for his patch.
+       * threshold plugin: The "PersistOK" option has been added. Thanks to
+         Aaron Brady for his patch.
+       * varnish plugin: Support for Varnish 3.0 has been added. Thanks to
+         Jérôme Renard for his patches.
+       * write_mongodb plugin: The new "write_mongodb" plugin writes value
+         lists to MongoDB, a shema-less database. Thanks to Akkarit Sangpetch
+         and Chris Lundquist for their work.
+       * write_graphite plugin: The new "write_graphite" plugin writes value
+         lists to Carbon, the storage layer of the Graphite time-series
+         database. Thanks to Scott Sanders and Pierre-Yves Ritschard for their
+         work.
+       * zfs_arc plugin: Several new statistics have been added. Thanks to
+         Aurelien Rougemont for his patches.
+       * scale target: Support for scaling specific data sources only has been
+         added. Thanks to Gerrie Roos for his patch.
+
+2012-11-11, Version 5.0.5
+       * collectd: Create new directories with mode 0777 and let umask remove
+         unwanted permission bits.
+       * collectd: Build issues have been fixed.
+       * collectd: An incorrect assertion has been fixed in some common code
+         for Solaris. This should resolve pseudo-random assertion failures
+         under Solaris. Thanks to Jeff Blane for his help debugging this.
+       * collectd: A couple of memory leaks through PThread thread attributes
+         have been fixed. Thanks to Gerrie Roos for fixing these.
+       * collectdctl: Fix PUTVAL for data sets with multiple data sources.
+         Thanks to Cyril Feraudet for reporting this problem.
+       * contrib/migrate-4-5.px: Handle to "df" to "df_complex" conversion
+         correctly.
+       * apcups plugin: Improve the reconnect behavior.
+       * curl_xml plugin: The "Host" setting was silently ignored. Thanks to
+         Fabien Wernli for fixing this.
+       * df plugin: Ignore "rootfs" devices under Linux to avoid having them
+         reported twice. Thanks to Brune Prémont for fixing this.
+       * disk plugin: Fix incorrect computation of read and write latency (the
+         "disk_time" type). Previously, the numbers reported where too small
+         by a factor of "interval", e.g. when the interval is set to 10
+         seconds, the values were too low by a factor of 10. Thanks to Manuel
+         Sanmartin for reporting this problem.
+       * dns plugin: A build issue under Solaris has been fixed. A erroneous
+         define that could lead to the reporting of bad data has been fixed by
+         Daniel Sutto.
+       * memcachec plugin: A bug in the configuration handling has been fixed.
+         Thanks to Pascal Hofmann for fixing this issue.
+       * mysql plugin: Fix a bug when registering multiple databases. Thanks
+         to Sebastian Harl for fixing this.
+       * netapp plugin: Correctly close the connection on communication
+         errors.
+       * netlink plugin: The function used to query statistics has been
+         changed to be more in line with iproute2's behavior. Thanks to
+         "KIvosak" for the patch.
+       * network plugin: Initialization of libgcrypt has been fixed. Thanks to
+         Chris Lundquist for his patch.
+       * oracle plugin: Error messages have been improved.
+       * ping plugin: Don't enter the exponential back-off mode when
+         ping_send() fails. This should make recovery after a network failure
+         much faster.
+       * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert
+         and Sven Trenkel for fixing this.
+       * redis plugin: Fix a compilation problem on FreeBSD. Thanks to
+         "biancalana" for the fix.
+       * rrdtool plugin: Fix an out-of-bounds array access when printing a
+         warning message. Thanks to Will Hawkins for fixing this bug.
+       * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been
+         added. Support for more complex / unusual MIBs / subtrees has been
+         added. Thanks to Mark Juric to test the changes and point out these
+         problems.
+
+2012-04-01, Version 5.0.4
+       * Build system: Fix the use of a libltdl macro. Thanks to Clemens Lang
+         for fixing this. Adresses some issues with building the iptables
+         plugin under Gentoo.
+       * libcollectdclient: A memory leak in the lcc_getval() function has
+         been fixed. Thanks to Jason Schmidlapp for finding and fixing this
+         issue.
+       * bind plugin: The use of 'QType" types has been fixed.
+       * df plugin: Fixed compiler issue under Mac OS X 10.7.
+       * conntrack plugin: Support zero as legitimate value. Thanks to Louis
+         Opter for his patch.
+       * memcached plugin: Increased the size of a static buffer, which was
+         truncating status messages form memcached. Thanks to Timon for the
+         patch.
+       * network plugin: Forwarding of notifications has been disabled. This
+         was a contition not checked for before, which may retult in an
+         endless loop.
+       * processes plugin: Support for process names with spaces has been
+         added to the Linux implementation. Thanks to Darrell Bishop for his
+         patch.
+       * perl plugin: A race condition in several callbacks, including log and
+         write callbacks, has been fixed. Thanks to "Rrpv" for reporting this
+         bug.
+       * snmp plugin: A bug when casting unsigned integers to gauge values has
+         been fixed: Unsigned integers would be cast to a signed integer and
+         then to a gauge, possibly resulting in a negative value.
+       * tcpconns plugin: Compilation with newer versions of the FreeBSD
+         runtime has been fixed.
+
+2012-02-19, Version 5.0.3
+       * Build system: Fix problems when building the ipvs and iptables
+         plugins. Thanks to Sebastian Harl for his patch. A bashism in the
+         version-gen.sh script has been fixed. Thanks to Jo-Philipp Wich for
+         his patch.
+       * csv and rrdtool plugins: Print a more helpful error message when the
+         DataDir is a symlink pointing to a non-existing location. Thanks to
+         Jonathan Nieder for his patch.
+       * exec plugin: Fix a problem when using select(2) to read from file
+         handles. Thanks to Gerrie Roos for his patch.
+       * network plugin: An incorrect error message in the handling of the
+         "Interface" configuration option has been fixed. Thanks to Gerrie
+         Roos for his patch.
+       * oracle plugin: A potential endless loop in the error handling has
+         been fixed.
+       * python plugin: A crash bug in the configuration handling has been
+         fixed. Thanks to Sven Trenkel for his patch.
+       * interfaces plugin: The change which was supposed to ignore "bogus"
+         interfaces has been reverted, since it ignored legit interfaces, such
+         as bonding pseudo-devices as well.
+
 2012-01-21, Version 5.0.2
        * curl_xml plugin: Fix handling of file:// and other URLs (which don't
          follow HTTP status codes). Thanks to Fabien Wernli for his patch!
        * v5upgrade target: Target for converting v4 data sets to the v5
          schema.
 
+2012-11-11, Version 4.10.8
+       * collectd: Create new directories with mode 0777 and let umask remove
+         unwanted permission bits.
+       * collectd: Build issues have been fixed.
+       * collectd: An incorrect assertion has been fixed in some common code
+         for Solaris. This should resolve pseudo-random assertion failures
+         under Solaris. Thanks to Jeff Blane for his help debugging this.
+       * collectd: A couple of memory leaks through PThread thread attributes
+         have been fixed. Thanks to Gerrie Roos for fixing these.
+       * apcups plugin: Improve the reconnect behavior.
+       * df plugin: Ignore "rootfs" devices under Linux to avoid having them
+         reported twice. Thanks to Brune Prémont for fixing this.
+       * disk plugin: Fix incorrect computation of read and write latency (the
+         "disk_time" type). Previously, the numbers reported where too small
+         by a factor of "interval", e.g. when the interval is set to 10
+         seconds, the values were too low by a factor of 10. Thanks to Manuel
+         Sanmartin for reporting this problem.
+       * dns plugin: A build issue under Solaris has been fixed. A erroneous
+         define that could lead to the reporting of bad data has been fixed by
+         Daniel Sutto.
+       * memcachec plugin: A bug in the configuration handling has been fixed.
+         Thanks to Pascal Hofmann for fixing this issue.
+       * netapp plugin: Correctly close the connection on communication
+         errors.
+       * netlink plugin: The function used to query statistics has been
+         changed to be more in line with iproute2's behavior. Thanks to
+         "KIvosak" for the patch.
+       * network plugin: Initialization of libgcrypt has been fixed. Thanks to
+         Chris Lundquist for his patch.
+       * oracle plugin: Error messages have been improved.
+       * ping plugin: Don't enter the exponential back-off mode when
+         ping_send() fails. This should make recovery after a network failure
+         much faster.
+       * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert
+         and Sven Trenkel for fixing this.
+       * rrdtool plugin: Fix an out-of-bounds array access when printing a
+         warning message. Thanks to Will Hawkins for fixing this bug.
+       * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been
+         added. Support for more complex / unusual MIBs / subtrees has been
+         added. Thanks to Mark Juric to test the changes and point out these
+         problems.
+
+2012-04-01, Version 4.10.7
+       * Build system: Fix the use of a libltdl macro. Thanks to Clemens Lang
+         for fixing this. Adresses some issues with building the iptables
+         plugin under Gentoo.
+       * libcollectdclient: A memory leak in the lcc_getval() function has
+         been fixed. Thanks to Jason Schmidlapp for finding and fixing this
+         issue.
+       * bind plugin: The use of 'QType" types has been fixed.
+       * df plugin: Fixed compiler issue under Mac OS X 10.7.
+       * conntrack plugin: Support zero as legitimate value. Thanks to Louis
+         Opter for his patch.
+       * memcached plugin: Increased the size of a static buffer, which was
+         truncating status messages form memcached. Thanks to Timon for the
+         patch.
+       * network plugin: Forwarding of notifications has been disabled. This
+         was a contition not checked for before, which may retult in an
+         endless loop.
+       * processes plugin: Support for process names with spaces has been
+         added to the Linux implementation. Thanks to Darrell Bishop for his
+         patch.
+       * perl plugin: A race condition in several callbacks, including log and
+         write callbacks, has been fixed. Thanks to "Rrpv" for reporting this
+         bug.
+       * snmp plugin: A bug when casting unsigned integers to gauge values has
+         been fixed: Unsigned integers would be cast to a signed integer and
+         then to a gauge, possibly resulting in a negative value.
+       * tcpconns plugin: Compilation with newer versions of the FreeBSD
+         runtime has been fixed.
+
+2012-02-19, Version 4.10.6
+       * Build system: Fix problems when building the ipvs and iptables
+         plugins. Thanks to Sebastian Harl for his patch. A bashism in the
+         version-gen.sh script has been fixed. Thanks to Jo-Philipp Wich for
+         his patch.
+       * csv and rrdtool plugins: Print a more helpful error message when the
+         DataDir is a symlink pointing to a non-existing location. Thanks to
+         Jonathan Nieder for his patch.
+       * exec plugin: Fix a problem when using select(2) to read from file
+         handles. Thanks to Gerrie Roos for his patch.
+       * network plugin: An incorrect error message in the handling of the
+         "Interface" configuration option has been fixed. Thanks to Gerrie
+         Roos for his patch.
+       * oracle plugin: A potential endless loop in the error handling has
+         been fixed.
+       * python plugin: A crash bug in the configuration handling has been
+         fixed. Thanks to Sven Trenkel for his patch.
+       * interfaces plugin: The change which was supposed to ignore "bogus"
+         interfaces has been reverted, since it ignored legit interfaces, such
+         as bonding pseudo-devices as well.
+
 2012-01-21, Version 4.10.5
        * curl_xml plugin: Fix handling of file:// and other URLs (which don't
          follow HTTP status codes). Thanks to Fabien Wernli for his patch!
index 9e3feac..5267123 100644 (file)
@@ -1,6 +1,6 @@
 ACLOCAL_AMFLAGS = -I libltdl/m4
 
-SUBDIRS = libltdl src bindings
+SUBDIRS = libltdl src bindings .
 
 INCLUDES = $(LTDLINCL)
 
@@ -10,3 +10,8 @@ install-exec-hook:
        $(mkinstalldirs) $(DESTDIR)$(localstatedir)/run
        $(mkinstalldirs) $(DESTDIR)$(localstatedir)/lib/$(PACKAGE_NAME)
        $(mkinstalldirs) $(DESTDIR)$(localstatedir)/log
+
+maintainer-clean-local:
+       -rm -f -r libltdl
+       -rm -f INSTALL
+       -rm -f aclocal.m4
diff --git a/README b/README
index ac79ce5..67cac04 100644 (file)
--- a/README
+++ b/README
@@ -83,6 +83,9 @@ Features
     - entropy
       Amount of entropy available to the system.
 
+    - ethstat
+      Network interface card statistics.
+
     - exec
       Values gathered by a custom program or script.
       See collectd-exec(5).
@@ -140,6 +143,10 @@ Features
       Motherboard sensors: temperature, fanspeed and voltage information,
       using mbmon(1).
 
+    - md
+      Linux software-RAID device information (number of active, failed, spare
+      and missing disks).
+
     - memcachec
       Query and parse data from a memcache daemon (memcached).
 
@@ -192,6 +199,9 @@ Features
       Network UPS tools: UPS current, voltage, power, charge, utilisation,
       temperature, etc. See upsd(8).
 
+    - numa
+      Information about Non-Uniform Memory Access (NUMA).
+
     - olsrd
       Queries routing information from the “Optimized Link State Routing”
       daemon.
@@ -358,6 +368,9 @@ Features
       needed. Please read collectd-unixsock(5) for a description on how that's
       done.
 
+    - write_graphite
+      Sends data to Carbon, the storage layer of Graphite.
+
     - write_http
       Sends the values collected by collectd to a web-server using HTTP POST
       requests. The transmitted data is either in a form understood by the
index f39e9bb..3876cc2 100644 (file)
@@ -10,28 +10,55 @@ EXTRA_DIST = perl/Makefile.PL \
             perl/lib/Collectd/Plugins/Monitorus.pm \
             perl/lib/Collectd/Plugins/OpenVZ.pm
 
+CLEANFILES = \
+       buildperl/Collectd.pm \
+       buildperl/Collectd/Plugins/OpenVZ.pm \
+       buildperl/Collectd/Unixsock.pm \
+       buildperl/Makefile.PL \
+       .perl-directory-stamp
+
+DISTCLEANFILES = \
+       buildperl/Collectd.pm \
+       buildperl/Collectd/Plugins/OpenVZ.pm \
+       buildperl/Collectd/Unixsock.pm \
+       buildperl/Makefile.PL \
+       .perl-directory-stamp
+
 all-local: @PERL_BINDINGS@
 
+
 install-exec-local:
-       [ ! -f perl/Makefile ] || ( cd perl && $(MAKE) install )
+       [ ! -f buildperl/Makefile ] || ( cd buildperl && $(MAKE) install )
+
+# Perl 'make uninstall' does not work as well as wanted.
+# So we do the work here.
+uninstall-local:
+       rm -f $(DESTDIR)$(mandir)/man3/Collectd::Unixsock.3pm
+       rm -f $(DESTDIR)$(datarootdir)/perl5/Collectd.pm
+       rm -f $(DESTDIR)$(datarootdir)/perl5/Collectd/Plugins/OpenVZ.pm
+       rm -f $(DESTDIR)$(datarootdir)/perl5/Collectd/Unixsock.pm
+       rm -f $(DESTDIR)$(prefix)/lib64/perl5/perllocal.pod
+       rm -f $(DESTDIR)$(prefix)/lib64/perl5/auto/Collectd/.packlist
 
 clean-local:
-       [ ! -f perl/Makefile ] || ( cd perl && $(MAKE) realclean )
+       rm -rf buildperl
 
-perl: perl/Makefile
-       cd perl && $(MAKE)
+perl: buildperl/Makefile
+       cd buildperl && $(MAKE)
 
-perl/Makefile: .perl-directory-stamp perl/Makefile.PL \
+buildperl/Makefile: .perl-directory-stamp buildperl/Makefile.PL \
        $(top_builddir)/config.status
-       cd perl && @PERL@ Makefile.PL PREFIX=$(prefix) @PERL_BINDINGS_OPTIONS@
+       cd buildperl && @PERL@ Makefile.PL INSTALL_BASE=$(prefix) @PERL_BINDINGS_OPTIONS@
+
+buildperl/Makefile.PL: .perl-directory-stamp $(top_builddir)/config.status
 
 .perl-directory-stamp:
-       if test ! -d perl; then \
-         mkdir -p perl/Collectd/Plugins; \
-         cp $(srcdir)/perl/Collectd.pm perl/; \
-         cp $(srcdir)/perl/Makefile.PL perl/; \
-         cp $(srcdir)/perl/Collectd/Unixsock.pm perl/Collectd/; \
-         cp $(srcdir)/perl/Collectd/Plugins/OpenVZ.pm perl/Collectd/Plugins/; \
+       if test ! -d buildperl; then \
+         mkdir -p buildperl/Collectd/Plugins; \
+         cp $(srcdir)/perl/lib/Collectd.pm buildperl/; \
+         cp $(srcdir)/perl/Makefile.PL buildperl/; \
+         cp $(srcdir)/perl/lib/Collectd/Unixsock.pm buildperl/Collectd/; \
+         cp $(srcdir)/perl/lib/Collectd/Plugins/OpenVZ.pm buildperl/Collectd/Plugins/; \
        fi
        touch $@
 
index b3ab7c9..4a15976 100644 (file)
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-AC_INIT(collectd, m4_esyscmd(./version-gen.sh))
+AC_INIT(collectd, [m4_esyscmd(./version-gen.sh)])
 AC_CONFIG_SRCDIR(src/collectd.c)
 AC_CONFIG_HEADERS(src/config.h)
 AC_CONFIG_AUX_DIR([libltdl/config])
@@ -91,6 +91,7 @@ fi
 if test "x$ac_system" = "xSolaris"
 then
        AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Define to enforce POSIX thread semantics under Solaris.])
+       AC_DEFINE(_REENTRANT,               1, [Define to enable reentrancy interfaces.])
 fi
 if test "x$ac_system" = "xAIX"
 then
@@ -299,6 +300,21 @@ fi
 # For hddtemp module
 AC_CHECK_HEADERS(linux/major.h libgen.h)
 
+# For md module (Linux only)
+if test "x$ac_system" = "xLinux"
+then
+       AC_CHECK_HEADERS(linux/raid/md_u.h,
+                        [have_linux_raid_md_u_h="yes"],
+                        [have_linux_raid_md_u_h="no"],
+[
+#include <sys/ioctl.h>
+#include <linux/major.h>
+#include <linux/types.h>
+])
+else
+       have_linux_raid_md_u_h="no"
+fi
+
 # For the battery plugin
 AC_CHECK_HEADERS(IOKit/ps/IOPowerSources.h, [], [],
 [
@@ -388,6 +404,33 @@ AC_CHECK_HEADERS(linux/netdevice.h, [], [],
 #endif
 ])
 
+# For ethstat module
+AC_CHECK_HEADERS(linux/sockios.h,
+    [have_linux_sockios_h="yes"],
+    [have_linux_sockios_h="no"],
+    [
+#if HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+    ])
+AC_CHECK_HEADERS(linux/ethtool.h,
+    [have_linux_ethtool_h="yes"],
+    [have_linux_ethtool_h="no"],
+    [
+#if HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+#if HAVE_LINUX_SOCKIOS_H
+# include <linux/sockios.h>
+#endif
+    ])
+
 # For ipvs module
 have_linux_ip_vs_h="no"
 have_net_ip_vs_h="no"
@@ -490,6 +533,8 @@ AC_CHECK_HEADERS(netinet/if_ether.h, [], [],
 #endif
 ])
 
+AC_CHECK_HEADERS(netinet/ip_compat.h)
+
 # For the multimeter plugin
 have_termios_h="no"
 AC_CHECK_HEADERS(termios.h, [have_termios_h="yes"])
@@ -1183,6 +1228,7 @@ AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct
                have_struct_kinfo_proc_freebsd="no"
        ],
        [
+AC_INCLUDES_DEFAULT
 #include <kvm.h>
 #include <sys/param.h>
 #include <sys/sysctl.h>
@@ -1199,6 +1245,7 @@ AC_CHECK_MEMBERS([struct kinfo_proc.kp_proc, struct kinfo_proc.kp_eproc],
                have_struct_kinfo_proc_openbsd="no"
        ],
        [
+AC_INCLUDES_DEFAULT
 #include <sys/param.h>
 #include <sys/sysctl.h>
 #include <kvm.h>
@@ -1381,6 +1428,7 @@ AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETSWAPINFO, test "x$with_kvm_getswapinfo" = "x
 AC_CHECK_LIB(kvm, kvm_nlist, [with_kvm_nlist="yes"], [with_kvm_nlist="no"])
 if test "x$with_kvm_nlist" = "xyes"
 then
+       AC_CHECK_HEADERS(bsd/nlist.h nlist.h)
        AC_DEFINE(HAVE_LIBKVM_NLIST, 1,
                  [Define to 1 if you have the 'kvm' library with the 'kvm_nlist' symbol (-lkvm)])
        with_libkvm="yes"
@@ -1786,68 +1834,121 @@ AM_CONDITIONAL(BUILD_WITH_LIBGCRYPT, test "x$with_libgcrypt" = "xyes")
 # --with-libiptc {{{
 AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])],
 [
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       if test "x$withval" = "xshipped"
        then
-               LIBIPTC_CPPFLAGS="$LIBIPTC_CPPFLAGS -I$withval/include"
-               LIBIPTC_LDFLAGS="$LIBIPTC_LDFLAGS -L$withval/lib"
-               with_libiptc="yes"
+               with_libiptc="own"
+       else if test "x$withval" = "xyes"
+       then
+               with_libiptc="pkgconfig"
+       else if test "x$withval" = "xno"
+       then
+               with_libiptc="no"
        else
-               with_libiptc="$withval"
-       fi
+               with_libiptc="yes"
+               with_libiptc_cflags="-I$withval/include"
+               with_libiptc_libs="-L$withval/lib"
+       fi; fi; fi
 ],
 [
        if test "x$ac_system" = "xLinux"
        then
-               with_libiptc="yes"
+               with_libiptc="pkgconfig"
        else
                with_libiptc="no (Linux only)"
        fi
 ])
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-CPPFLAGS="$CPPFLAGS $LIBIPTC_CPPFLAGS"
-LDFLAGS="$LDFLAGS $LIBIPTC_LDFLAGS"
-# check whether the header file for libiptc is available.
-if test "x$with_libiptc" = "xyes"
+
+if test "x$with_libiptc" = "xpkgconfig" && test "x$PKG_CONFIG" = "x"
 then
-       AC_CHECK_HEADERS(libiptc/libiptc.h,
-                        [with_libiptc="yes"],
-                        [with_libiptc="no (libiptc/libiptc.h not found)"])
+       with_libiptc="no (Don't have pkg-config)"
 fi
-if test "x$with_libiptc" = "xyes"
+
+if test "x$with_libiptc" = "xpkgconfig"
+then
+       $PKG_CONFIG --exists 'libiptc' 2>/dev/null
+       if test $? -ne 0
+       then
+               with_libiptc="no (pkg-config doesn't know libiptc)"
+       fi
+fi
+if test "x$with_libiptc" = "xpkgconfig"
+then
+       with_libiptc_cflags="`$PKG_CONFIG --cflags 'libiptc'`"
+       if test $? -ne 0
+       then
+               with_libiptc="no ($PKG_CONFIG failed)"
+       fi
+       with_libiptc_libs="`$PKG_CONFIG --libs 'libiptc'`"
+       if test $? -ne 0
+       then
+               with_libiptc="no ($PKG_CONFIG failed)"
+       fi
+fi
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS $with_libiptc_cflags"
+
+# check whether the header file for libiptc is available.
+if test "x$with_libiptc" = "xpkgconfig"
 then
-       AC_CHECK_HEADERS(libiptc/libip6tc.h,
-                        [with_libiptc="yes"],
-                        [with_libiptc="no (libiptc/libip6tc.h not found)"])
+       AC_CHECK_HEADERS(libiptc/libiptc.h libiptc/libip6tc.h, ,
+                       [with_libiptc="no (header file missing)"])
 fi
 # If the header file is available, check for the required type declaractions.
 # They may be missing in old versions of libiptc. In that case, they will be
 # declared in the iptables plugin.
-if test "x$with_libiptc" = "xyes"
+if test "x$with_libiptc" = "xpkgconfig"
+then
+       AC_CHECK_TYPES([iptc_handle_t, ip6tc_handle_t], [], [])
+fi
+# Check for the iptc_init symbol in the library.
+# This could be in iptc or ip4tc
+if test "x$with_libiptc" = "xpkgconfig"
 then
-       AC_CHECK_TYPES([iptc_handle_t, ip6tc_handle_t], [], [],
+       SAVE_LIBS="$LIBS"
+       AC_SEARCH_LIBS(iptc_init, [iptc ip4tc],
+                       [with_libiptc="pkgconfig"],
+                       [with_libiptc="no"],
+                       [$with_libiptc_libs])
+       LIBS="$SAVE_LIBS"
+fi
+if test "x$with_libiptc" = "xpkgconfig"
+then
+       with_libiptc="yes"
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+
+if test "x$with_libiptc" = "xown"
+then
+       with_libiptc_cflags=""
+       with_libiptc_libs=""
+fi
+if test "x$with_libiptc" = "xown"
+then
+       AC_CHECK_HEADERS(linux/netfilter_ipv4/ip_tables.h linux/netfilter_ipv6/ip6_tables.h linux/netfilter/x_tables.h, [],
        [
-#include <libiptc/libiptc.h>
-#include <libiptc/libip6tc.h>
+               with_libiptc="no (Linux iptables headers not found)"
+       ],
+       [
+#include "$srcdir/src/owniptc/ipt_kernel_headers.h"
        ])
 fi
-# Check for the iptc_init symbol in the library.
-if test "x$with_libiptc" = "xyes"
+AM_CONDITIONAL(BUILD_WITH_OWN_LIBIPTC, test "x$with_libiptc" = "xown")
+if test "x$with_libiptc" = "xown"
 then
-       AC_CHECK_LIB(iptc, iptc_init,
-                    [with_libiptc="yes"],
-                    [with_libiptc="no (symbol 'iptc_init' not found)"])
+       AC_DEFINE(OWN_LIBIPTC, 1, [Define to 1 if we use the shipped iptc library.])
+       with_libiptc="yes"
 fi
+
 AM_CONDITIONAL(BUILD_WITH_LIBIPTC, test "x$with_libiptc" = "xyes")
 if test "x$with_libiptc" = "xyes"
 then
-       BUILD_WITH_LIBIPTC_CPPFLAGS="$LIBIPTC_CPPFLAGS"
-       BUILD_WITH_LIBIPTC_LDFLAGS="$LIBIPTC_LDFLAGS"
+       BUILD_WITH_LIBIPTC_CPPFLAGS="$with_libiptc_cflags"
+       BUILD_WITH_LIBIPTC_LDFLAGS="$with_libiptc_libs"
        AC_SUBST(BUILD_WITH_LIBIPTC_CPPFLAGS)
        AC_SUBST(BUILD_WITH_LIBIPTC_LDFLAGS)
 fi
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
 # }}}
 
 # --with-java {{{
@@ -2282,7 +2383,7 @@ then
 fi
 if test "x$with_libmysql" = "xyes"
 then
-       with_mysql_libs=`$with_mysql_config --libs 2>/dev/null`
+       with_mysql_libs=`$with_mysql_config --libs_r 2>/dev/null`
        mysql_config_status=$?
 
        if test $mysql_config_status -ne 0
@@ -2365,21 +2466,20 @@ then
 #include <asm/types.h>
 #include <sys/socket.h>])
 
-       AC_COMPILE_IFELSE(
-[#include <stdio.h>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
-int main (void)
-{
-       int retval = TCA_STATS2;
-       return (retval);
-}],
-       [AC_DEFINE([HAVE_TCA_STATS2], 1, [True if the enum-member TCA_STATS2 exists])]
-       []);
+        AC_COMPILE_IFELSE([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(
 [#include <stdio.h>
@@ -2408,6 +2508,76 @@ then
 fi
 if test "x$with_libnetlink" = "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(
+                               [
+AC_INCLUDES_DEFAULT
+#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(
+                               [
+AC_INCLUDES_DEFAULT
+#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)
@@ -2815,7 +2985,7 @@ AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes")
 perl_interpreter="perl"
 AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
 [
-       if test -x "$withval"
+       if test -f "$withval" && test -x "$withval"
        then
                perl_interpreter="$withval"
                with_libperl="yes"
@@ -3258,26 +3428,39 @@ AC_ARG_WITH(librabbitmq, [AS_HELP_STRING([--with-librabbitmq@<:@=PREFIX@:>@], [P
 [
        with_librabbitmq="yes"
 ])
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $with_librabbitmq_cppflags"
+LDFLAGS="$LDFLAGS $with_librabbitmq_ldflags"
 if test "x$with_librabbitmq" = "xyes"
 then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_librabbitmq_cppflags"
-
        AC_CHECK_HEADERS(amqp.h, [with_librabbitmq="yes"], [with_librabbitmq="no (amqp.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
 fi
 if test "x$with_librabbitmq" = "xyes"
 then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_librabbitmq_cppflags"
-       LDFLAGS="$LDFLAGS $with_librabbitmq_ldflags"
-
+       # librabbitmq up to version 0.9.1 provides "library_errno", later
+       # versions use "library_error". The library does not provide a version
+       # macro :( Use "AC_CHECK_MEMBERS" (plural) for automatic defines.
+       AC_CHECK_MEMBERS([amqp_rpc_reply_t.library_errno],,,
+                        [
+#if HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#if HAVE_STDIO_H
+# include <stdio.h>
+#endif
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#include <amqp.h>
+                         ])
+fi
+if test "x$with_librabbitmq" = "xyes"
+then
        AC_CHECK_LIB(rabbitmq, amqp_basic_publish, [with_librabbitmq="yes"], [with_librabbitmq="no (Symbol 'amqp_basic_publish' not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
 fi
 if test "x$with_librabbitmq" = "xyes"
 then
@@ -3289,6 +3472,8 @@ then
        AC_SUBST(BUILD_WITH_LIBRABBITMQ_LIBS)
        AC_DEFINE(HAVE_LIBRABBITMQ, 1, [Define if librabbitmq is present and usable.])
 fi
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
 AM_CONDITIONAL(BUILD_WITH_LIBRABBITMQ, test "x$with_librabbitmq" = "xyes")
 # }}}
 
@@ -3948,7 +4133,7 @@ AC_ARG_WITH(libvarnish, [AS_HELP_STRING([--with-libvarnish@<:@=PREFIX@:>@], [Pat
        then
                AC_MSG_NOTICE([Not checking for libvarnish: Manually configured])
                with_libvarnish_cflags="-I$withval/include"
-               with_libvarnish_libs="-L$withval/lib -lvarnish -lvarnishcompat -lvarnishapi"
+               with_libvarnish_libs="-L$withval/lib -lvarnishapi"
                with_libvarnish="yes"
        fi; fi; fi
 ],
@@ -4007,10 +4192,9 @@ then
        CPPFLAGS="$CPPFLAGS $with_libvarnish_cflags"
        #LDFLAGS="$LDFLAGS $with_libvarnish_libs"
 
-       AC_CHECK_LIB(varnishapi, VSL_OpenStats,
-                    [with_libvarnish="yes"],
-                    [with_libvarnish="no (symbol VSL_OpenStats not found)"],
-                    [$with_libvarnish_libs])
+    AC_CHECK_HEADERS(varnish/vsc.h,
+        [AC_DEFINE([HAVE_VARNISH_V3], [1], [Varnish 3 API support])],
+        [AC_DEFINE([HAVE_VARNISH_V2], [1], [Varnish 2 API support])])
 
        CPPFLAGS="$SAVE_CPPFLAGS"
        #LDFLAGS="$SAVE_LDFLAGS"
@@ -4368,6 +4552,8 @@ plugin_curl_xml="no"
 plugin_df="no"
 plugin_disk="no"
 plugin_entropy="no"
+plugin_ethstat="no"
+plugin_fscache="no"
 plugin_interface="no"
 plugin_ipmi="no"
 plugin_ipvs="no"
@@ -4377,7 +4563,7 @@ plugin_load="no"
 plugin_memory="no"
 plugin_multimeter="no"
 plugin_nfs="no"
-plugin_fscache="no"
+plugin_numa="no"
 plugin_perl="no"
 plugin_processes="no"
 plugin_protocols="no"
@@ -4404,12 +4590,13 @@ then
        plugin_cpufreq="yes"
        plugin_disk="yes"
        plugin_entropy="yes"
+       plugin_fscache="yes"
        plugin_interface="yes"
        plugin_irq="yes"
        plugin_load="yes"
        plugin_memory="yes"
        plugin_nfs="yes"
-       plugin_fscache="yes"
+       plugin_numa="yes"
        plugin_processes="yes"
        plugin_protocols="yes"
        plugin_serial="yes"
@@ -4449,11 +4636,13 @@ fi
 if test "x$with_perfstat" = "xyes"
 then
        plugin_cpu="yes"
+       plugin_contextswitch="yes"
        plugin_disk="yes"
        plugin_memory="yes"
        plugin_swap="yes"
        plugin_interface="yes"
        plugin_load="yes"
+       plugin_uptime="yes"
 fi
 
 if test "x$with_procinfo" = "xyes"
@@ -4464,6 +4653,7 @@ fi
 # Solaris
 if test "x$with_kstat" = "xyes"
 then
+       plugin_nfs="yes"
        plugin_uptime="yes"
        plugin_zfs_arc="yes"
 fi
@@ -4570,6 +4760,11 @@ then
        fi
 fi
 
+if test "x$have_linux_sockios_h$have_linux_ethtool_h" = "xyesyes"
+then
+       plugin_ethstat="yes"
+fi
+
 if test "x$have_getifaddrs" = "xyes"
 then
        plugin_interface="yes"
@@ -4657,6 +4852,7 @@ AC_ARG_ENABLE([all-plugins],
 
 m4_divert_once([HELP_ENABLE], [])
 
+AC_PLUGIN([aggregation], [yes],                [Aggregation plugin])
 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])
@@ -4678,6 +4874,7 @@ AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
 AC_PLUGIN([dns],         [$with_libpcap],      [DNS traffic analysis])
 AC_PLUGIN([email],       [yes],                [EMail statistics])
 AC_PLUGIN([entropy],     [$plugin_entropy],    [Entropy statistics])
+AC_PLUGIN([ethstat],     [$plugin_ethstat],    [Stats from NIC driver])
 AC_PLUGIN([exec],        [yes],                [Execution of external programs])
 AC_PLUGIN([filecount],   [yes],                [Count files in directories])
 AC_PLUGIN([fscache],     [$plugin_fscache],    [fscache statistics])
@@ -4700,6 +4897,7 @@ AC_PLUGIN([match_regex], [yes],                [The regex match])
 AC_PLUGIN([match_timediff], [yes],             [The timediff match])
 AC_PLUGIN([match_value], [yes],                [The value match])
 AC_PLUGIN([mbmon],       [yes],                [Query mbmond])
+AC_PLUGIN([md],          [$have_linux_raid_md_u_h], [md (Linux software RAID) devices])
 AC_PLUGIN([memcachec],   [$with_libmemcached], [memcachec statistics])
 AC_PLUGIN([memcached],   [yes],                [memcached statistics])
 AC_PLUGIN([memory],      [$plugin_memory],     [Memory usage])
@@ -4714,6 +4912,7 @@ AC_PLUGIN([nginx],       [$with_libcurl],      [nginx statistics])
 AC_PLUGIN([notify_desktop], [$with_libnotify], [Desktop notifications])
 AC_PLUGIN([notify_email], [$with_libesmtp],    [Email notifier])
 AC_PLUGIN([ntpd],        [yes],                [NTPd statistics])
+AC_PLUGIN([numa],        [$plugin_numa],       [NUMA virtual memory statistics])
 AC_PLUGIN([nut],         [$with_libupsclient], [Network UPS tools statistics])
 AC_PLUGIN([olsrd],       [yes],                [olsrd statistics])
 AC_PLUGIN([onewire],     [$with_libowcapi],    [OneWire sensor statistics])
@@ -4759,6 +4958,7 @@ AC_PLUGIN([varnish],     [$with_libvarnish],   [Varnish cache statistics])
 AC_PLUGIN([vmem],        [$plugin_vmem],       [Virtual memory statistics])
 AC_PLUGIN([vserver],     [$plugin_vserver],    [Linux VServer statistics])
 AC_PLUGIN([wireless],    [$plugin_wireless],   [Wireless statistics])
+AC_PLUGIN([write_graphite], [yes],             [Graphite / Carbon output plugin])
 AC_PLUGIN([write_http],  [$with_libcurl],      [HTTP output plugin])
 AC_PLUGIN([write_redis], [$with_libcredis],    [Redis output plugin])
 AC_PLUGIN([write_mongodb], [$with_libmongoc],  [MongoDB output plugin])
@@ -4985,6 +5185,7 @@ Configuration:
     perl  . . . . . . . . $with_perl_bindings
 
   Modules:
+    aggregation . . . . . $enable_aggregation
     amqp    . . . . . . . $enable_amqp
     apache  . . . . . . . $enable_apache
     apcups  . . . . . . . $enable_apcups
@@ -5006,6 +5207,7 @@ Configuration:
     dns . . . . . . . . . $enable_dns
     email . . . . . . . . $enable_email
     entropy . . . . . . . $enable_entropy
+    ethstat . . . . . . . $enable_ethstat
     exec  . . . . . . . . $enable_exec
     filecount . . . . . . $enable_filecount
     fscache . . . . . . . $enable_fscache
@@ -5028,6 +5230,7 @@ Configuration:
     match_timediff  . . . $enable_match_timediff
     match_value . . . . . $enable_match_value
     mbmon . . . . . . . . $enable_mbmon
+    md  . . . . . . . . . $enable_md
     memcachec . . . . . . $enable_memcachec
     memcached . . . . . . $enable_memcached
     memory  . . . . . . . $enable_memory
@@ -5042,6 +5245,7 @@ Configuration:
     notify_desktop  . . . $enable_notify_desktop
     notify_email  . . . . $enable_notify_email
     ntpd  . . . . . . . . $enable_ntpd
+    numa  . . . . . . . . $enable_numa
     nut . . . . . . . . . $enable_nut
     olsrd . . . . . . . . $enable_olsrd
     onewire . . . . . . . $enable_onewire
@@ -5086,6 +5290,7 @@ Configuration:
     vmem  . . . . . . . . $enable_vmem
     vserver . . . . . . . $enable_vserver
     wireless  . . . . . . $enable_wireless
+    write_graphite  . . . $enable_write_graphite
     write_http  . . . . . $enable_write_http
     write_redis . . . . . $enable_write_redis
     write_mongodb . . . . $enable_write_mongodb
index bc1fe9f..1ebf1f1 100644 (file)
@@ -101,3 +101,8 @@ solaris-smf
 -----------
   Manifest file for the Solaris SMF system and detailed information on how to
 register collectd as a service with this system.
+
+collectd.service
+----------------
+  Service file for systemd. Please ship this file as
+  /lib/systemd/system/collectd.service in any linux package of collectd.
diff --git a/contrib/collectd.service b/contrib/collectd.service
new file mode 100644 (file)
index 0000000..ee4d596
--- /dev/null
@@ -0,0 +1,15 @@
+[Unit]
+Description=statistics collection daemon
+Documentation=man:collectd(1)
+After=local-fs.target network.target
+Requires=local-fs.target network.target
+
+[Service]
+ExecStart=/usr/sbin/collectd -C /etc/collectd/collectd.conf -f
+Restart=always
+RestartSec=10
+StandardOutput=syslog
+StandardError=syslog
+
+[Install]
+WantedBy=multi-user.target
index 445b183..9af0fb0 100644 (file)
@@ -4,19 +4,23 @@
 #
 # Copyright © 2009 Adrian Perez <aperez@igalia.com>
 #
-# Distributed under terms of the GPLv2 license.
+# Distributed under terms of the GPLv2 license or newer.
+# 
+# Frank Marien (frank@apsu.be) 6 Sep 2012
+# - quick fixes for 5.1 binary protocol
+# - updated to python 3
+# - fixed for larger packet sizes (possible on lo interface)
+# - fixed comment typo (decode_network_string decodes a string)
 
 """
 Collectd network protocol implementation.
 """
 
-import socket
-import struct
-
+import socket,struct,sys
 try:
-    from cStringIO import StringIO
+  from io import StringIO
 except ImportError:
-    from StringIO import StringIO
+  from cStringIO import StringIO
 
 from datetime import datetime
 from copy import deepcopy
@@ -31,17 +35,19 @@ DEFAULT_IPv4_GROUP = "239.192.74.66"
 DEFAULT_IPv6_GROUP = "ff18::efc0:4a42"
 """Default IPv6 multicast group"""
 
-
+HR_TIME_DIV = (2.0**30)
 
 # Message kinds
 TYPE_HOST            = 0x0000
 TYPE_TIME            = 0x0001
+TYPE_TIME_HR         = 0x0008
 TYPE_PLUGIN          = 0x0002
 TYPE_PLUGIN_INSTANCE = 0x0003
 TYPE_TYPE            = 0x0004
 TYPE_TYPE_INSTANCE   = 0x0005
 TYPE_VALUES          = 0x0006
 TYPE_INTERVAL        = 0x0007
+TYPE_INTERVAL_HR     = 0x0009
 
 # For notifications
 TYPE_MESSAGE         = 0x0100
@@ -50,14 +56,14 @@ TYPE_SEVERITY        = 0x0101
 # DS kinds
 DS_TYPE_COUNTER      = 0
 DS_TYPE_GAUGE        = 1
-
+DS_TYPE_DERIVE       = 2
+DS_TYPE_ABSOLUTE     = 3
 
 header = struct.Struct("!2H")
 number = struct.Struct("!Q")
 short  = struct.Struct("!H")
 double = struct.Struct("<d")
 
-
 def decode_network_values(ptype, plen, buf):
     """Decodes a list of DS values in collectd network format
     """
@@ -70,13 +76,19 @@ def decode_network_values(ptype, plen, buf):
     assert double.size == number.size
 
     result = []
-    for dstype in map(ord, buf[header.size+short.size:off]):
+    for dstype in buf[header.size+short.size:off]:
         if dstype == DS_TYPE_COUNTER:
             result.append((dstype, number.unpack_from(buf, off)[0]))
             off += valskip
         elif dstype == DS_TYPE_GAUGE:
             result.append((dstype, double.unpack_from(buf, off)[0]))
             off += valskip
+        elif dstype == DS_TYPE_DERIVE:
+            result.append((dstype, number.unpack_from(buf, off)[0]))
+            off += valskip
+        elif dstype == DS_TYPE_ABSOLUTE:
+            result.append((dstype, number.unpack_from(buf, off)[0]))
+            off += valskip
         else:
             raise ValueError("DS type %i unsupported" % dstype)
 
@@ -84,13 +96,13 @@ def decode_network_values(ptype, plen, buf):
 
 
 def decode_network_number(ptype, plen, buf):
-    """Decodes a number (64-bit unsigned) in collectd network format.
+    """Decodes a number (64-bit unsigned) from collectd network format.
     """
     return number.unpack_from(buf, header.size)[0]
 
 
 def decode_network_string(msgtype, plen, buf):
-    """Decodes a floating point number (64-bit) in collectd network format.
+    """Decodes a string from collectd network format.
     """
     return buf[header.size:plen-1]
 
@@ -99,7 +111,9 @@ def decode_network_string(msgtype, plen, buf):
 _decoders = {
     TYPE_VALUES         : decode_network_values,
     TYPE_TIME           : decode_network_number,
+    TYPE_TIME_HR        : decode_network_number,
     TYPE_INTERVAL       : decode_network_number,
+    TYPE_INTERVAL_HR    : decode_network_number,
     TYPE_HOST           : decode_network_string,
     TYPE_PLUGIN         : decode_network_string,
     TYPE_PLUGIN_INSTANCE: decode_network_string,
@@ -115,6 +129,7 @@ def decode_network_packet(buf):
     """
     off = 0
     blen = len(buf)
+
     while off < blen:
         ptype, plen = header.unpack_from(buf, off)
 
@@ -128,9 +143,6 @@ def decode_network_packet(buf):
         off += plen
 
 
-
-
-
 class Data(object):
     time = 0
     host = None
@@ -140,7 +152,7 @@ class Data(object):
     typeinstance = None
 
     def __init__(self, **kw):
-        [setattr(self, k, v) for k, v in kw.iteritems()]
+        [setattr(self, k, v) for k, v in kw.items()]
 
     @property
     def datetime(self):
@@ -150,19 +162,19 @@ class Data(object):
     def source(self):
         buf = StringIO()
         if self.host:
-            buf.write(self.host)
+            buf.write(str(self.host))
         if self.plugin:
             buf.write("/")
-            buf.write(self.plugin)
+            buf.write(str(self.plugin))
         if self.plugininstance:
             buf.write("/")
-            buf.write(self.plugininstance)
+            buf.write(str(self.plugininstance))
         if self.type:
             buf.write("/")
-            buf.write(self.type)
+            buf.write(str(self.type))
         if self.typeinstance:
             buf.write("/")
-            buf.write(self.typeinstance)
+            buf.write(str(self.typeinstance))
         return buf.getvalue()
 
     def __str__(self):
@@ -215,8 +227,12 @@ def interpret_opcodes(iterable):
     for kind, data in iterable:
         if kind == TYPE_TIME:
             vl.time = nt.time = data
+        elif kind == TYPE_TIME_HR:
+            vl.time = nt.time = data / HR_TIME_DIV
         elif kind == TYPE_INTERVAL:
             vl.interval = data
+        elif kind == TYPE_INTERVAL_HR:
+            vl.interval = data / HR_TIME_DIV
         elif kind == TYPE_HOST:
             vl.host = nt.host = data
         elif kind == TYPE_PLUGIN:
@@ -248,7 +264,7 @@ class Reader(object):
     host = None
     port = DEFAULT_PORT
 
-    BUFFER_SIZE = 1024
+    BUFFER_SIZE = 16384
 
 
     def __init__(self, host=None, port=DEFAULT_PORT, multicast=False):
@@ -311,8 +327,6 @@ class Reader(object):
         """
         if iterable is None:
             iterable = self.decode()
-        if isinstance(iterable, basestring):
+        if isinstance(iterable, str):
             iterable = self.decode(iterable)
         return interpret_opcodes(iterable)
-
-
index 027961f..4723af9 100755 (executable)
@@ -240,6 +240,28 @@ HTML
   $html_started = 0;
 }
 
+sub contains_invalid_chars
+{
+  my $str = shift;
+
+  for (split (m//, $str))
+  {
+    my $n = ord ($_);
+
+    # Whitespace is allowed.
+    if (($n >= 9) && ($n <= 13))
+    {
+      next;
+    }
+    elsif ($n < 32)
+    {
+      return (1);
+    }
+  }
+
+  return;
+}
+
 sub show_selector
 {
   my $timespan_selection = get_timespan_selection ();
@@ -254,6 +276,7 @@ sub show_selector
 HTML
   for (sort (keys %$host_selection))
   {
+    next if contains_invalid_chars ($_);
     my $host = encode_entities ($_);
     my $selected = $host_selection->{$_}
     ? ' selected="selected"'
@@ -266,6 +289,7 @@ HTML
 HTML
   for (sort (keys %$plugin_selection))
   {
+    next if contains_invalid_chars ($_);
     my $plugin = encode_entities ($_);
     my $selected = $plugin_selection->{$_}
     ? ' selected="selected"'
@@ -278,6 +302,7 @@ HTML
 HTML
   for (sort { $TimeSpans->{$a} <=> $TimeSpans->{$b} } (keys (%$TimeSpans)))
   {
+    next if contains_invalid_chars ($_);
     my $name = encode_entities ($_);
     my $value = $TimeSpans->{$_};
     my $selected = ($value == $timespan_selection)
@@ -304,6 +329,7 @@ sub action_list_hosts
   for (sort @hosts)
   {
     my $url = encode_entities (script_name () . "?action=show_selection;hostname=$_");
+    next if contains_invalid_chars ($_);
     my $name = encode_entities ($_);
     print qq#      <li><a href="$url">$name</a></li>\n#;
   }
index 0fbd0d3..4a70c41 100644 (file)
@@ -34,7 +34,7 @@ sub new
   my $obj = Collectd::Graph::Type->new (@_);
   $obj->{'data_sources'} = [qw(free used)];
   $obj->{'rrd_opts'} = ['-v', 'Bytes'];
-  $obj->{'rrd_title'} = 'Disk space ({type_instance})';
+  $obj->{'rrd_title'} = 'Disk space ({instance})';
   $obj->{'rrd_format'} = '%5.1lf%sB';
   $obj->{'colors'} = [qw(00b000 ff0000)];
 
@@ -58,7 +58,7 @@ sub getRRDArgs
   my $faded_green = get_faded_color ('00ff00');
   my $faded_red = get_faded_color ('ff0000');
 
-  return (['-t', 'Diskspace (' . $ident->{'type_instance'} . ')', '-v', 'Bytes', '-l', '0',
+  return (['-t', $obj->getTitle ($ident), '-v', 'Bytes', '-l', '0',
     "DEF:free_min=${filename}:free:MIN",
     "DEF:free_avg=${filename}:free:AVERAGE",
     "DEF:free_max=${filename}:free:MAX",
diff --git a/contrib/exec-ksm.sh b/contrib/exec-ksm.sh
new file mode 100755 (executable)
index 0000000..a21aa1d
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# collectd - contrib/exec-ksm.sh
+# Copyright (C) 2011  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 <octo at collectd.org>
+
+HOSTNAME="${COLLECTD_HOSTNAME:-$(hostname -f)}"
+INTERVAL="${COLLECTD_INTERVAL:-60}"
+
+function read_file() {
+  local type="$1"
+  local type_instance="$2"
+  local file_name="/sys/kernel/mm/ksm/$3"
+  local ident="$HOSTNAME/exec-ksm/vmpage_number-${type_instance}"
+
+  echo "PUTVAL \"$ident\" interval=$INTERVAL N:$(< $file_name)"
+}
+
+if [[ 0 -eq $(< /sys/kernel/mm/ksm/run) ]]; then
+  echo "$0: KSM not active." >&2
+  exit 1
+fi
+
+while sleep "$INTERVAL"
+do
+  read_file vmpage_number    shared   pages_shared
+  read_file vmpage_number    saved    pages_sharing
+  read_file vmpage_number    unshared pages_unshared
+  read_file vmpage_number    volatile pages_volatile
+  read_file total_operations scan     full_scans
+done
+
+exit 0
index 26dd621..71644f7 100644 (file)
@@ -1,5 +1,7 @@
 # Run `perldoc exec-nagios.px' for details on this config file.
 
+NRPEConfig /etc/nrpe.cfg
+
 Interval 300
 
 <Script /usr/lib/nagios/check_tcp>
index 4b112f9..c7f18c5 100755 (executable)
@@ -23,6 +23,7 @@ use Regexp::Common ('number');
 
 our $ConfigFile = '/etc/exec-nagios.conf';
 our $TypeMap = {};
+our $NRPEMap = {};
 our $Scripts = [];
 our $Interval = defined ($ENV{'COLLECTD_INTERVAL'}) ? (0 + $ENV{'COLLECTD_INTERVAL'}) : 300;
 our $Hostname = defined ($ENV{'COLLECTD_HOSTNAME'}) ? $ENV{'COLLECTD_HOSTNAME'} : '';
@@ -41,6 +42,7 @@ config syntax, so it's very similar to the F<collectd.conf> syntax, too.
 
 Here's a short sample config:
 
+  NRPEConfig "/etc/nrpe.cfg"
   Interval 300
   <Script /usr/lib/nagios/check_tcp>
     Arguments -H alice -p 22
@@ -55,6 +57,18 @@ The options have the following semantic (i.E<nbsp>e. meaning):
 
 =over 4
 
+=item B<NRPEConfig> I<File>
+
+Read the NRPE config and add the command definitions to an alias table. After
+reading the file you can use the NRPE command name rather than the script's
+filename within B<Script> blocks (see below). If both, the NRPE config and the
+B<Script> block, define arguments they will be merged by concatenating the
+arguments together in the order "NRPE-args Script-args".
+
+Please note that this option is rather dumb. It does not support "command
+argument processing" (i.e. replacing C<$ARG1$> and friends), inclusion of other
+NRPE config files, include directories etc.
+
 =item B<Interval> I<Seconds>
 
 Sets the interval in which the plugins are executed. This doesn't need to match
@@ -65,8 +79,9 @@ seconds.
 =item E<lt>B<Script> I<File>E<gt>
 
 Adds a script to the list of scripts to be executed once per I<Interval>
-seconds. You can use the following optional arguments to specify the operation
-further:
+seconds. If the B<NRPEConfig> is given above the B<Script> block, you may use
+the NRPE command name rather than the script's filename. You can use the
+following optional arguments to specify the operation further:
 
 =over 4
 
@@ -93,6 +108,48 @@ with the C<exec-plugin>).
 
 =cut
 
+sub parse_nrpe_conf
+{
+  my $file = shift;
+  my $fh;
+  my $status;
+
+  $status = open ($fh, '<', $file);
+  if (!$status)
+  {
+    print STDERR "Reading NRPE config from \"$file\" failed: $!\n";
+    return;
+  }
+
+  while (<$fh>)
+  {
+    my $line = $_;
+    chomp ($line);
+
+    if ($line =~ m/^\s*command\[([^\]]+)\]\s*=\s*(.+)$/)
+    {
+      my $alias = $1;
+      my $script;
+      my $arguments;
+
+      ($script, $arguments) = split (' ', $2, 2);
+
+      if ($NRPEMap->{$alias})
+      {
+        print STDERR "Warning: NRPE command \"$alias\" redefined.\n";
+      }
+
+      $NRPEMap->{$alias} = { script => $script };
+      if ($arguments)
+      {
+        $NRPEMap->{$alias}{'arguments'} = $arguments;
+      }
+    }
+  } # while (<$fh>)
+
+  close ($fh);
+} # parse_nrpe_conf
+
 sub handle_config_addtype
 {
   my $list = shift;
@@ -107,6 +164,30 @@ sub handle_config_addtype
   }
 } # handle_config_addtype
 
+# Update the script record. This function adds the name of the script /
+# executable to the hash and merges the configured and NRPE arguments if
+# required.
+sub update_script_opts
+{
+  my $opts = shift;
+  my $script = shift;
+  my $nrpe_args = shift;
+
+  $opts->{'script'} = $script;
+
+  if ($nrpe_args)
+  {
+    if ($opts->{'arguments'})
+    {
+      $opts->{'arguments'} = $nrpe_args . ' ' . $opts->{'arguments'};
+    }
+    else
+    {
+      $opts->{'arguments'} = $nrpe_args;
+    }
+  }
+} # update_script_opts
+
 sub handle_config_script
 {
   my $scripts = shift;
@@ -116,6 +197,20 @@ sub handle_config_script
     my $script = $_;
     my $opts = $scripts->{$script};
 
+    my $nrpe_args = '';
+
+    # Check if the script exists in the NRPE map. If so, replace the alias name
+    # with the actual script name.
+    if ($NRPEMap->{$script})
+    {
+      if ($NRPEMap->{$script}{'arguments'})
+      {
+        $nrpe_args = $NRPEMap->{$script}{'arguments'};
+      }
+      $script = $NRPEMap->{$script}{'script'};
+    }
+
+    # Check if the script exists and is executable.
     if (!-e $script)
     {
       print STDERR "Script `$script' doesn't exist.\n";
@@ -126,20 +221,21 @@ sub handle_config_script
     }
     else
     {
+      # Add the script to the global @$Script array.
       if (ref ($opts) eq 'ARRAY')
+      {
+        for (@$opts)
         {
-          for (@$opts)
-            {
-              my $opt = $_;
-              $opt->{'script'} = $script;
-              push (@$Scripts, $opt);
-            }
-        }
-          else
-        {
-          $opts->{'script'} = $script;
-          push (@$Scripts, $opts);
+          my $opt = $_;
+          update_script_opts ($opt, $script, $nrpe_args);
+          push (@$Scripts, $opt);
         }
+      }
+      else
+      {
+        update_script_opts ($opts, $script, $nrpe_args);
+        push (@$Scripts, $opts);
+      }
     }
   } # for (keys %$scripts)
 } # handle_config_script
@@ -148,6 +244,26 @@ sub handle_config
 {
   my $config = shift;
 
+  if (defined ($config->{'nrpeconfig'}))
+  {
+    if (ref ($config->{'nrpeconfig'}) eq 'ARRAY')
+    {
+      for (@{$config->{'nrpeconfig'}})
+      {
+        parse_nrpe_conf ($_);
+      }
+    }
+    elsif (ref ($config->{'nrpeconfig'}) eq '')
+    {
+      parse_nrpe_conf ($config->{'nrpeconfig'});
+    }
+    else
+    {
+      print STDERR "Cannot handle ref type '"
+      . ref ($config->{'nrpeconfig'}) . "' for option 'NRPEConfig'.\n";
+    }
+  }
+
   if (defined ($config->{'addtype'}))
   {
     if (ref ($config->{'addtype'}) eq 'ARRAY')
index d3ff796..c2a9555 100755 (executable)
@@ -33,6 +33,7 @@ use File::Basename ('dirname');
 
 our $InDir = '/var/lib/collectd';
 our $RRDtool = 'rrdtool';
+our $RRDFilter = 'rrd_filter.px';
 
 our %TypesCounterToDerive = # {{{
 (
@@ -184,7 +185,15 @@ sub handle_file # {{{
   {
     my $dir = join ('/', @path);
     print "mkdir -p \"$dir/$plugin-$type_inst\"\n";
-    print "mv \"$path\" \"$dir/$plugin-$type_inst/$type.rrd\"\n";
+    if (($plugin eq 'df') and ($type eq 'df'))
+    {
+      print "$RRDFilter --infile=\"$path\" --outfile=\"$dir/$plugin-$type_inst/df_complex-free.rrd\" --map free:value\n";
+      print "$RRDFilter --infile=\"$path\" --outfile=\"$dir/$plugin-$type_inst/df_complex-used.rrd\" --map used:value\n";
+    }
+    else
+    {
+      print "mv \"$path\" \"$dir/$plugin-$type_inst/$type.rrd\"\n";
+    }
   }
 } # }}} sub handle_file
 
@@ -223,10 +232,12 @@ migrate-4-5.px [OPTIONS]
 
 Valid options are:
 
-  --indir <dir>      Source directory
-                     Default: $InDir
-  --rrdtool <path>   Path to the RRDtool binary
-                     Default: $RRDtool
+  --indir <dir>         Source directory
+                        Default: $InDir
+  --rrdtool <path>      Path to the RRDtool binary
+                        Default: $RRDtool
+  --rrdfilter <path>    Path to the rrd_filter.px script
+                        Default: $RRDFilter
 
 EOF
   exit (1);
@@ -234,8 +245,11 @@ EOF
 
 GetOptions ("indir|i=s" => \$InDir,
         "rrdtool=s" => \$RRDtool,
+        "rrdfilter=s" => \$RRDFilter,
         "help|h" => \&exit_usage) or exit_usage ();
 
+print "#!/bin/bash\n\n";
+
 scan_dir ($InDir);
 
 # vim: set sw=2 sts=2 et fdm=marker :
index c84aabe..7297d85 100644 (file)
@@ -48,9 +48,9 @@ function load_graph_definitions($logarithmic = false, $tinylegend = false) {
        $GraphDefs = array();
        $GraphDefs['apache_bytes'] = array(
                '-v', 'Bits/s',
-               'DEF:min_raw={file}:count:MIN',
-               'DEF:avg_raw={file}:count:AVERAGE',
-               'DEF:max_raw={file}:count:MAX',
+               'DEF:min_raw={file}:value:MIN',
+               'DEF:avg_raw={file}:value:AVERAGE',
+               'DEF:max_raw={file}:value:MAX',
                'CDEF:min=min_raw,8,*',
                'CDEF:avg=avg_raw,8,*',
                'CDEF:max=max_raw,8,*',
@@ -68,9 +68,9 @@ function load_graph_definitions($logarithmic = false, $tinylegend = false) {
                'GPRINT:avg_sum:LAST:(ca. %5.1lf%sB Total)\l');
        $GraphDefs['apache_requests'] = array(
                '-v', 'Requests/s',
-               'DEF:min={file}:count:MIN',
-               'DEF:avg={file}:count:AVERAGE',
-               'DEF:max={file}:count:MAX',
+               'DEF:min={file}:value:MIN',
+               'DEF:avg={file}:value:AVERAGE',
+               'DEF:max={file}:value:MAX',
                "AREA:max#$HalfBlue",
                "AREA:min#$Canvas",
                "LINE1:avg#$FullBlue:Requests/s",
@@ -79,9 +79,9 @@ function load_graph_definitions($logarithmic = false, $tinylegend = false) {
                'GPRINT:max:MAX:%6.2lf Max,',
                'GPRINT:avg:LAST:%6.2lf Last');
        $GraphDefs['apache_scoreboard'] = array(
-               'DEF:min={file}:count:MIN',
-               'DEF:avg={file}:count:AVERAGE',
-               'DEF:max={file}:count:MAX',
+               'DEF:min={file}:value:MIN',
+               'DEF:avg={file}:value:AVERAGE',
+               'DEF:max={file}:value:MAX',
                "AREA:max#$HalfBlue",
                "AREA:min#$Canvas",
                "LINE1:avg#$FullBlue:Processes",
@@ -2024,7 +2024,7 @@ function meta_graph_apache_scoreboard($host, $plugin, $plugin_instance, $type, $
                if ($file == '')
                        continue;
 
-               $sources[] = array('name'=>$inst, 'file'=>$file, 'ds'=>'count');
+               $sources[] = array('name'=>$inst, 'file'=>$file, 'ds'=>'value');
        }
 
        return collectd_draw_meta_stack($opts, $sources);
index affc171..58ab17e 100644 (file)
@@ -43,7 +43,7 @@ collectd_SOURCES = collectd.c collectd.h \
 collectd_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL)
 collectd_CFLAGS = $(AM_CFLAGS)
 collectd_LDFLAGS = -export-dynamic
-collectd_LDADD =
+collectd_LDADD = -lm
 collectd_DEPENDENCIES =
 
 # Link to these libraries..
@@ -70,7 +70,6 @@ collectd_LDADD += -ldevinfo
 endif
 if BUILD_AIX
 collectd_LDFLAGS += -Wl,-bexpall,-brtllib
-collectd_LDADD += -lm
 endif
 
 # The daemon needs to call sg_init, so we need to link it against libstatgrab,
@@ -82,7 +81,7 @@ endif
 
 if BUILD_WITH_OWN_LIBOCONFIG
 collectd_LDADD += $(LIBLTDL) liboconfig/liboconfig.la
-collectd_DEPENDENCIES += $(LIBLTDL) liboconfig/liboconfig.la
+collectd_DEPENDENCIES += liboconfig/liboconfig.la
 else
 collectd_LDADD += -loconfig
 endif
@@ -120,10 +119,21 @@ pkglib_LTLIBRARIES =
 BUILT_SOURCES = 
 CLEANFILES = 
 
+if BUILD_PLUGIN_AGGREGATION
+pkglib_LTLIBRARIES += aggregation.la
+aggregation_la_SOURCES = aggregation.c \
+                         utils_vl_lookup.c utils_vl_lookup.h
+aggregation_la_LDFLAGS = -module -avoid-version
+aggregation_la_LIBADD =
+collectd_LDADD += "-dlopen" aggregation.la
+collectd_DEPENDENCIES += aggregation.la
+endif
+
 if BUILD_PLUGIN_AMQP
 pkglib_LTLIBRARIES += amqp.la
 amqp_la_SOURCES = amqp.c \
                  utils_cmd_putval.c utils_cmd_putval.h \
+                 utils_format_graphite.c utils_format_graphite.h \
                  utils_format_json.c utils_format_json.h
 amqp_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBRABBITMQ_LDFLAGS)
 amqp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRABBITMQ_CPPFLAGS)
@@ -213,6 +223,10 @@ if BUILD_PLUGIN_CONTEXTSWITCH
 pkglib_LTLIBRARIES += contextswitch.la
 contextswitch_la_SOURCES = contextswitch.c
 contextswitch_la_LDFLAGS = -module -avoid-version
+contextswitch_la_LIBADD =
+if BUILD_WITH_PERFSTAT
+contextswitch_la_LIBADD += -lperfstat
+endif
 collectd_LDADD += "-dlopen" contextswitch.la
 collectd_DEPENDENCIES += contextswitch.la
 endif
@@ -378,6 +392,14 @@ collectd_LDADD += "-dlopen" exec.la
 collectd_DEPENDENCIES += exec.la
 endif
 
+if BUILD_PLUGIN_ETHSTAT
+pkglib_LTLIBRARIES += ethstat.la
+ethstat_la_SOURCES = ethstat.c
+ethstat_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" ethstat.la
+collectd_DEPENDENCIES += ethstat.la
+endif
+
 if BUILD_PLUGIN_FILECOUNT
 pkglib_LTLIBRARIES += filecount.la
 filecount_la_SOURCES = filecount.c
@@ -587,6 +609,14 @@ collectd_LDADD += "-dlopen" mbmon.la
 collectd_DEPENDENCIES += mbmon.la
 endif
 
+if BUILD_PLUGIN_MD
+pkglib_LTLIBRARIES += md.la
+md_la_SOURCES = md.c
+md_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" md.la
+collectd_DEPENDENCIES += md.la
+endif
+
 if BUILD_PLUGIN_MEMCACHEC
 pkglib_LTLIBRARIES += memcachec.la
 memcachec_la_SOURCES = memcachec.c
@@ -764,6 +794,14 @@ collectd_LDADD += "-dlopen" ntpd.la
 collectd_DEPENDENCIES += ntpd.la
 endif
 
+if BUILD_PLUGIN_NUMA
+pkglib_LTLIBRARIES += numa.la
+numa_la_SOURCES = numa.c
+numa_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" numa.la
+collectd_DEPENDENCIES += numa.la
+endif
+
 if BUILD_PLUGIN_NUT
 pkglib_LTLIBRARIES += nut.la
 nut_la_SOURCES = nut.c
@@ -1168,6 +1206,9 @@ uptime_la_LIBADD =
 if BUILD_WITH_LIBKSTAT
 uptime_la_LIBADD += -lkstat
 endif
+if BUILD_WITH_PERFSTAT
+uptime_la_LIBADD += -lperfstat
+endif
 collectd_LDADD += "-dlopen" uptime.la
 collectd_DEPENDENCIES += uptime.la
 endif
@@ -1230,6 +1271,16 @@ collectd_LDADD += "-dlopen" wireless.la
 collectd_DEPENDENCIES += wireless.la
 endif
 
+if BUILD_PLUGIN_WRITE_GRAPHITE
+pkglib_LTLIBRARIES += write_graphite.la
+write_graphite_la_SOURCES = write_graphite.c \
+                        utils_format_graphite.c utils_format_graphite.h \
+                        utils_format_json.c utils_format_json.h
+write_graphite_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" write_graphite.la
+collectd_DEPENDENCIES += write_graphite.la
+endif
+
 if BUILD_PLUGIN_WRITE_HTTP
 pkglib_LTLIBRARIES += write_http.la
 write_http_la_SOURCES = write_http.c \
@@ -1285,6 +1336,8 @@ collectd_LDADD += "-dlopen" zfs_arc.la
 collectd_DEPENDENCIES += zfs_arc.la
 endif
 
+BUILT_SOURCES += $(dist_man_MANS)
+
 dist_man_MANS = collectd.1 \
                collectd.conf.5 \
                collectd-email.5 \
@@ -1337,7 +1390,7 @@ EXTRA_DIST +=   collectd.conf.pod \
        fi
 
 pinba.pb-c.c pinba.pb-c.h: pinba.proto
-       protoc-c --c_out $(builddir) pinba.proto
+       protoc-c --c_out . pinba.proto
 
 install-exec-hook:
        $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
@@ -1351,3 +1404,21 @@ install-exec-hook:
        $(INSTALL) -m 0644 $(srcdir)/types.db $(DESTDIR)$(pkgdatadir)/types.db;
        $(INSTALL) -m 0644 $(srcdir)/postgresql_default.conf \
                $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
+
+uninstall-hook:
+       rm -f $(DESTDIR)$(pkgdatadir)/types.db;
+       rm -f $(DESTDIR)$(sysconfdir)/collectd.conf
+       rm -f $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
+
+if BUILD_FEATURE_DEBUG
+bin_PROGRAMS += utils_vl_lookup_test
+utils_vl_lookup_test_SOURCES = utils_vl_lookup_test.c \
+                               utils_vl_lookup.h utils_vl_lookup.c \
+                               utils_avltree.c utils_avltree.h \
+                               common.h
+
+utils_vl_lookup_test_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL) -DBUILD_TEST=1
+utils_vl_lookup_test_CFLAGS = $(AM_CFLAGS)
+utils_vl_lookup_test_LDFLAGS = -export-dynamic
+utils_vl_lookup_test_LDADD =
+endif
diff --git a/src/aggregation.c b/src/aggregation.c
new file mode 100644 (file)
index 0000000..db33c17
--- /dev/null
@@ -0,0 +1,684 @@
+/**
+ * collectd - src/aggregation.c
+ * Copyright (C) 2012       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 <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+#include "meta_data.h"
+#include "utils_cache.h" /* for uc_get_rate() */
+#include "utils_vl_lookup.h"
+
+#include <pthread.h>
+
+struct aggregation_s /* {{{ */
+{
+  identifier_t ident;
+
+  _Bool calc_num;
+  _Bool calc_sum;
+  _Bool calc_average;
+  _Bool calc_min;
+  _Bool calc_max;
+  _Bool calc_stddev;
+}; /* }}} */
+typedef struct aggregation_s aggregation_t;
+
+struct agg_instance_s;
+typedef struct agg_instance_s agg_instance_t;
+struct agg_instance_s /* {{{ */
+{
+  pthread_mutex_t lock;
+  identifier_t ident;
+
+  int ds_type;
+
+  derive_t num;
+  gauge_t sum;
+  gauge_t squares_sum;
+
+  gauge_t min;
+  gauge_t max;
+
+  rate_to_value_state_t *state_num;
+  rate_to_value_state_t *state_sum;
+  rate_to_value_state_t *state_average;
+  rate_to_value_state_t *state_min;
+  rate_to_value_state_t *state_max;
+  rate_to_value_state_t *state_stddev;
+
+  agg_instance_t *next;
+}; /* }}} */
+
+static lookup_t *lookup = NULL;
+
+static pthread_mutex_t agg_instance_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static agg_instance_t *agg_instance_list_head = NULL;
+
+static void agg_destroy (aggregation_t *agg) /* {{{ */
+{
+  sfree (agg);
+} /* }}} void agg_destroy */
+
+/* Frees all dynamically allocated memory within the instance. */
+static void agg_instance_destroy (agg_instance_t *inst) /* {{{ */
+{
+  if (inst == NULL)
+    return;
+
+  /* Remove this instance from the global list of instances. */
+  pthread_mutex_lock (&agg_instance_list_lock);
+  if (agg_instance_list_head == inst)
+    agg_instance_list_head = inst->next;
+  else if (agg_instance_list_head != NULL)
+  {
+    agg_instance_t *prev = agg_instance_list_head;
+    while ((prev != NULL) && (prev->next != inst))
+      prev = prev->next;
+    if (prev != NULL)
+      prev->next = inst->next;
+  }
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  sfree (inst->state_num);
+  sfree (inst->state_sum);
+  sfree (inst->state_average);
+  sfree (inst->state_min);
+  sfree (inst->state_max);
+  sfree (inst->state_stddev);
+
+  memset (inst, 0, sizeof (*inst));
+  inst->ds_type = -1;
+  inst->min = NAN;
+  inst->max = NAN;
+} /* }}} void agg_instance_destroy */
+
+/* Create a new aggregation instance. */
+static agg_instance_t *agg_instance_create (data_set_t const *ds, /* {{{ */
+    value_list_t const *vl, aggregation_t *agg)
+{
+  agg_instance_t *inst;
+
+  DEBUG ("aggregation plugin: Creating new instance.");
+
+  inst = malloc (sizeof (*inst));
+  if (inst == NULL)
+  {
+    ERROR ("aggregation plugin: malloc() failed.");
+    return (NULL);
+  }
+  memset (inst, 0, sizeof (*inst));
+  pthread_mutex_init (&inst->lock, /* attr = */ NULL);
+
+  inst->ds_type = ds->ds[0].type;
+
+#define COPY_FIELD(fld) do { \
+  sstrncpy (inst->ident.fld, \
+      LU_IS_ANY (agg->ident.fld) ? vl->fld : agg->ident.fld, \
+      sizeof (inst->ident.fld)); \
+} while (0)
+
+  COPY_FIELD (host);
+  COPY_FIELD (plugin);
+  COPY_FIELD (plugin_instance);
+  COPY_FIELD (type);
+  COPY_FIELD (type_instance);
+
+#undef COPY_FIELD
+
+  inst->min = NAN;
+  inst->max = NAN;
+
+#define INIT_STATE(field) do { \
+  inst->state_ ## field = NULL; \
+  if (agg->calc_ ## field) { \
+    inst->state_ ## field = malloc (sizeof (*inst->state_ ## field)); \
+    if (inst->state_ ## field == NULL) { \
+      agg_instance_destroy (inst); \
+      ERROR ("aggregation plugin: malloc() failed."); \
+      return (NULL); \
+    } \
+    memset (inst->state_ ## field, 0, sizeof (*inst->state_ ## field)); \
+  } \
+} while (0)
+
+  INIT_STATE (num);
+  INIT_STATE (sum);
+  INIT_STATE (average);
+  INIT_STATE (min);
+  INIT_STATE (max);
+  INIT_STATE (stddev);
+
+#undef INIT_STATE
+
+  pthread_mutex_lock (&agg_instance_list_lock);
+  inst->next = agg_instance_list_head;
+  agg_instance_list_head = inst;
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  return (inst);
+} /* }}} agg_instance_t *agg_instance_create */
+
+/* Update the num, sum, min, max, ... fields of the aggregation instance, if
+ * the rate of the value list is available. Value lists with more than one data
+ * source are not supported and will return an error. Returns zero on success
+ * and non-zero otherwise. */
+static int agg_instance_update (agg_instance_t *inst, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl)
+{
+  gauge_t *rate;
+
+  if (ds->ds_num != 1)
+  {
+    ERROR ("aggregation plugin: The \"%s\" type (data set) has more than one "
+        "data source. This is currently not supported by this plugin. "
+        "Sorry.", ds->type);
+    return (EINVAL);
+  }
+
+  rate = uc_get_rate (ds, vl);
+  if (rate == NULL)
+  {
+    char ident[6 * DATA_MAX_NAME_LEN];
+    FORMAT_VL (ident, sizeof (ident), vl);
+    ERROR ("aggregation plugin: Unable to read the current rate of \"%s\".",
+        ident);
+    return (ENOENT);
+  }
+
+  if (isnan (rate[0]))
+  {
+    sfree (rate);
+    return (0);
+  }
+
+  pthread_mutex_lock (&inst->lock);
+
+  inst->num++;
+  inst->sum += rate[0];
+  inst->squares_sum += (rate[0] * rate[0]);
+
+  if (isnan (inst->min) || (inst->min > rate[0]))
+    inst->min = rate[0];
+  if (isnan (inst->max) || (inst->max < rate[0]))
+    inst->max = rate[0];
+
+  pthread_mutex_unlock (&inst->lock);
+
+  sfree (rate);
+  return (0);
+} /* }}} int agg_instance_update */
+
+static int agg_instance_read_func (agg_instance_t *inst, /* {{{ */
+  char const *func, gauge_t rate, rate_to_value_state_t *state,
+  value_list_t *vl, char const *pi_prefix, cdtime_t t)
+{
+  value_t v;
+  int status;
+
+  if (pi_prefix[0] != 0)
+    ssnprintf (vl->plugin_instance, sizeof (vl->plugin_instance), "%s-%s",
+        pi_prefix, func);
+  else
+    sstrncpy (vl->plugin_instance, func, sizeof (vl->plugin_instance));
+
+  memset (&v, 0, sizeof (v));
+  status = rate_to_value (&v, rate, state, inst->ds_type, t);
+  if (status != 0)
+  {
+    /* If this is the first iteration and rate_to_value() was asked to return a
+     * COUNTER or a DERIVE, it will return EAGAIN. Catch this and handle
+     * gracefully. */
+    if (status == EAGAIN)
+      return (0);
+
+    WARNING ("aggregation plugin: rate_to_value failed with status %i.",
+        status);
+    return (-1);
+  }
+
+  vl->values = &v;
+  vl->values_len = 1;
+
+  plugin_dispatch_values_secure (vl);
+
+  vl->values = NULL;
+  vl->values_len = 0;
+
+  return (0);
+} /* }}} int agg_instance_read_func */
+
+static int agg_instance_read (agg_instance_t *inst, cdtime_t t) /* {{{ */
+{
+  value_list_t vl = VALUE_LIST_INIT;
+  char pi_prefix[DATA_MAX_NAME_LEN];
+
+  /* Pre-set all the fields in the value list that will not change per
+   * aggregation type (sum, average, ...). The struct will be re-used and must
+   * therefore be dispatched using the "secure" function. */
+
+  vl.time = t;
+  vl.interval = 0;
+
+  vl.meta = meta_data_create ();
+  if (vl.meta == NULL)
+  {
+    ERROR ("aggregation plugin: meta_data_create failed.");
+    return (-1);
+  }
+  meta_data_add_boolean (vl.meta, "aggregation:created", 1);
+
+  if (LU_IS_ALL (inst->ident.host))
+    sstrncpy (vl.host, "global", sizeof (vl.host));
+  else
+    sstrncpy (vl.host, inst->ident.host, sizeof (vl.host));
+
+  sstrncpy (vl.plugin, "aggregation", sizeof (vl.plugin));
+
+  if (LU_IS_ALL (inst->ident.plugin))
+  {
+    if (LU_IS_ALL (inst->ident.plugin_instance))
+      sstrncpy (pi_prefix, "", sizeof (pi_prefix));
+    else
+      sstrncpy (pi_prefix, inst->ident.plugin_instance, sizeof (pi_prefix));
+  }
+  else
+  {
+    if (LU_IS_ALL (inst->ident.plugin_instance))
+      sstrncpy (pi_prefix, inst->ident.plugin, sizeof (pi_prefix));
+    else
+      ssnprintf (pi_prefix, sizeof (pi_prefix),
+          "%s-%s", inst->ident.plugin, inst->ident.plugin_instance);
+  }
+
+  sstrncpy (vl.type, inst->ident.type, sizeof (vl.type));
+
+  if (!LU_IS_ALL (inst->ident.type_instance))
+    sstrncpy (vl.type_instance, inst->ident.type_instance,
+        sizeof (vl.type_instance));
+
+#define READ_FUNC(func, rate) do { \
+  if (inst->state_ ## func != NULL) { \
+    agg_instance_read_func (inst, #func, rate, \
+        inst->state_ ## func, &vl, pi_prefix, t); \
+  } \
+} while (0)
+
+  pthread_mutex_lock (&inst->lock);
+
+  READ_FUNC (num, (gauge_t) inst->num);
+
+  /* All other aggregations are only defined when there have been any values
+   * at all. */
+  if (inst->num > 0)
+  {
+    READ_FUNC (sum, inst->sum);
+    READ_FUNC (average, (inst->sum / ((gauge_t) inst->num)));
+    READ_FUNC (min, inst->min);
+    READ_FUNC (max, inst->max);
+    READ_FUNC (stddev, sqrt((((gauge_t) inst->num) * inst->squares_sum)
+          - (inst->sum * inst->sum)) / ((gauge_t) inst->num));
+  }
+
+  /* Reset internal state. */
+  inst->num = 0;
+  inst->sum = 0.0;
+  inst->squares_sum = 0.0;
+  inst->min = NAN;
+  inst->max = NAN;
+
+  pthread_mutex_unlock (&inst->lock);
+
+  meta_data_destroy (vl.meta);
+  vl.meta = NULL;
+
+  return (0);
+} /* }}} int agg_instance_read */
+
+/* lookup_class_callback_t for utils_vl_lookup */
+static void *agg_lookup_class_callback ( /* {{{ */
+    __attribute__((unused)) data_set_t const *ds,
+    value_list_t const *vl, void *user_class)
+{
+  return (agg_instance_create (ds, vl, (aggregation_t *) user_class));
+} /* }}} void *agg_class_callback */
+
+/* lookup_obj_callback_t for utils_vl_lookup */
+static int agg_lookup_obj_callback (data_set_t const *ds, /* {{{ */
+    value_list_t const *vl,
+    __attribute__((unused)) void *user_class,
+    void *user_obj)
+{
+  return (agg_instance_update ((agg_instance_t *) user_obj, ds, vl));
+} /* }}} int agg_lookup_obj_callback */
+
+/* lookup_free_class_callback_t for utils_vl_lookup */
+static void agg_lookup_free_class_callback (void *user_class) /* {{{ */
+{
+  agg_destroy ((aggregation_t *) user_class);
+} /* }}} void agg_lookup_free_class_callback */
+
+/* lookup_free_obj_callback_t for utils_vl_lookup */
+static void agg_lookup_free_obj_callback (void *user_obj) /* {{{ */
+{
+  agg_instance_destroy ((agg_instance_t *) user_obj);
+} /* }}} void agg_lookup_free_obj_callback */
+
+/*
+ * <Plugin "aggregation">
+ *   <Aggregation>
+ *     Plugin "cpu"
+ *     Type "cpu"
+ *
+ *     GroupBy Host
+ *     GroupBy TypeInstance
+ *
+ *     CalculateNum true
+ *     CalculateSum true
+ *     CalculateAverage true
+ *     CalculateMinimum true
+ *     CalculateMaximum true
+ *     CalculateStddev true
+ *   </Aggregation>
+ * </Plugin>
+ */
+static int agg_config_handle_group_by (oconfig_item_t const *ci, /* {{{ */
+    aggregation_t *agg)
+{
+  int i;
+
+  for (i = 0; i < ci->values_num; i++)
+  {
+    char const *value;
+
+    if (ci->values[i].type != OCONFIG_TYPE_STRING)
+    {
+      ERROR ("aggregation plugin: Argument %i of the \"GroupBy\" option "
+          "is not a string.", i + 1);
+      continue;
+    }
+
+    value = ci->values[i].value.string;
+
+    if (strcasecmp ("Host", value) == 0)
+      sstrncpy (agg->ident.host, LU_ANY, sizeof (agg->ident.host));
+    else if (strcasecmp ("Plugin", value) == 0)
+      sstrncpy (agg->ident.plugin, LU_ANY, sizeof (agg->ident.plugin));
+    else if (strcasecmp ("PluginInstance", value) == 0)
+      sstrncpy (agg->ident.plugin_instance, LU_ANY,
+          sizeof (agg->ident.plugin_instance));
+    else if (strcasecmp ("TypeInstance", value) == 0)
+      sstrncpy (agg->ident.type_instance, LU_ANY, sizeof (agg->ident.type_instance));
+    else if (strcasecmp ("Type", value) == 0)
+      ERROR ("aggregation plugin: Grouping by type is not supported.");
+    else
+      WARNING ("aggregation plugin: The \"%s\" argument to the \"GroupBy\" "
+          "option is invalid and will be ignored.", value);
+  } /* for (ci->values) */
+
+  return (0);
+} /* }}} int agg_config_handle_group_by */
+
+static int agg_config_aggregation (oconfig_item_t *ci) /* {{{ */
+{
+  aggregation_t *agg;
+  _Bool is_valid;
+  int status;
+  int i;
+
+  agg = malloc (sizeof (*agg));
+  if (agg == NULL)
+  {
+    ERROR ("aggregation plugin: malloc failed.");
+    return (-1);
+  }
+  memset (agg, 0, sizeof (*agg));
+
+  sstrncpy (agg->ident.host, LU_ALL, sizeof (agg->ident.host));
+  sstrncpy (agg->ident.plugin, LU_ALL, sizeof (agg->ident.plugin));
+  sstrncpy (agg->ident.plugin_instance, LU_ALL,
+      sizeof (agg->ident.plugin_instance));
+  sstrncpy (agg->ident.type, LU_ALL, sizeof (agg->ident.type));
+  sstrncpy (agg->ident.type_instance, LU_ALL,
+      sizeof (agg->ident.type_instance));
+
+  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_buffer (child, agg->ident.host,
+          sizeof (agg->ident.host));
+    else if (strcasecmp ("Plugin", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.plugin,
+          sizeof (agg->ident.plugin));
+    else if (strcasecmp ("PluginInstance", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.plugin_instance,
+          sizeof (agg->ident.plugin_instance));
+    else if (strcasecmp ("Type", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.type,
+          sizeof (agg->ident.type));
+    else if (strcasecmp ("TypeInstance", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.type_instance,
+          sizeof (agg->ident.type_instance));
+    else if (strcasecmp ("GroupBy", child->key) == 0)
+      agg_config_handle_group_by (child, agg);
+    else if (strcasecmp ("CalculateNum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_num);
+    else if (strcasecmp ("CalculateSum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_sum);
+    else if (strcasecmp ("CalculateAverage", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_average);
+    else if (strcasecmp ("CalculateMinimum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_min);
+    else if (strcasecmp ("CalculateMaximum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_max);
+    else if (strcasecmp ("CalculateStddev", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_stddev);
+    else
+      WARNING ("aggregation plugin: The \"%s\" key is not allowed inside "
+          "<Aggregation /> blocks and will be ignored.", child->key);
+  }
+
+  /* Sanity checking */
+  is_valid = 1;
+  if (LU_IS_ALL (agg->ident.type)) /* {{{ */
+  {
+    ERROR ("aggregation plugin: It appears you did not specify the required "
+        "\"Type\" option in this aggregation. "
+        "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+        "Type \"%s\", TypeInstance \"%s\")",
+        agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+        agg->ident.type, agg->ident.type_instance);
+    is_valid = 0;
+  }
+  else if (strchr (agg->ident.type, '/') != NULL)
+  {
+    ERROR ("aggregation plugin: The \"Type\" may not contain the '/' "
+        "character. Especially, it may not be a wildcard. The current "
+        "value is \"%s\".", agg->ident.type);
+    is_valid = 0;
+  } /* }}} */
+
+  if (!LU_IS_ALL (agg->ident.host) /* {{{ */
+      && !LU_IS_ALL (agg->ident.plugin)
+      && !LU_IS_ALL (agg->ident.plugin_instance)
+      && !LU_IS_ALL (agg->ident.type_instance))
+  {
+    ERROR ("aggregation plugin: An aggregation must contain at least one "
+        "wildcard. This is achieved by leaving at least one of the \"Host\", "
+        "\"Plugin\", \"PluginInstance\" and \"TypeInstance\" options blank "
+        "and not grouping by that field. "
+        "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+        "Type \"%s\", TypeInstance \"%s\")",
+        agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+        agg->ident.type, agg->ident.type_instance);
+    is_valid = 0;
+  } /* }}} */
+
+  if (!agg->calc_num && !agg->calc_sum && !agg->calc_average /* {{{ */
+      && !agg->calc_min && !agg->calc_max && !agg->calc_stddev)
+  {
+    ERROR ("aggregation plugin: No aggregation function has been specified. "
+        "Without this, I don't know what I should be calculating. "
+        "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+        "Type \"%s\", TypeInstance \"%s\")",
+        agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+        agg->ident.type, agg->ident.type_instance);
+    is_valid = 0;
+  } /* }}} */
+
+  if (!is_valid) /* {{{ */
+  {
+    sfree (agg);
+    return (-1);
+  } /* }}} */
+
+  status = lookup_add (lookup, &agg->ident, agg);
+  if (status != 0)
+  {
+    ERROR ("aggregation plugin: lookup_add failed with status %i.", status);
+    sfree (agg);
+    return (-1);
+  }
+
+  DEBUG ("aggregation plugin: Successfully added aggregation: "
+      "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+      "Type \"%s\", TypeInstance \"%s\")",
+      agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+      agg->ident.type, agg->ident.type_instance);
+  return (0);
+} /* }}} int agg_config_aggregation */
+
+static int agg_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  pthread_mutex_lock (&agg_instance_list_lock);
+
+  if (lookup == NULL)
+  {
+    lookup = lookup_create (agg_lookup_class_callback,
+        agg_lookup_obj_callback,
+        agg_lookup_free_class_callback,
+        agg_lookup_free_obj_callback);
+    if (lookup == NULL)
+    {
+      pthread_mutex_unlock (&agg_instance_list_lock);
+      ERROR ("aggregation plugin: lookup_create failed.");
+      return (-1);
+    }
+  }
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Aggregation", child->key) == 0)
+      agg_config_aggregation (child);
+    else
+      WARNING ("aggregation plugin: The \"%s\" key is not allowed inside "
+          "<Plugin aggregation /> blocks and will be ignored.", child->key);
+  }
+
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  return (0);
+} /* }}} int agg_config */
+
+static int agg_read (void) /* {{{ */
+{
+  agg_instance_t *this;
+  cdtime_t t;
+  int success;
+
+  t = cdtime ();
+  success = 0;
+
+  pthread_mutex_lock (&agg_instance_list_lock);
+
+  /* agg_instance_list_head only holds data, after the "write" callback has
+   * been called with a matching value list at least once. So on startup,
+   * there's a race between the aggregations read() and write() callback. If
+   * the read() callback is called first, agg_instance_list_head is NULL and
+   * "success" may be zero. This is expected and should not result in an error.
+   * Therefore we need to handle this case separately. */
+  if (agg_instance_list_head == NULL)
+  {
+    pthread_mutex_unlock (&agg_instance_list_lock);
+    return (0);
+  }
+
+  for (this = agg_instance_list_head; this != NULL; this = this->next)
+  {
+    int status;
+
+    status = agg_instance_read (this, t);
+    if (status != 0)
+      WARNING ("aggregation plugin: Reading an aggregation instance "
+          "failed with status %i.", status);
+    else
+      success++;
+  }
+
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  return ((success > 0) ? 0 : -1);
+} /* }}} int agg_read */
+
+static int agg_write (data_set_t const *ds, value_list_t const *vl, /* {{{ */
+    __attribute__((unused)) user_data_t *user_data)
+{
+  _Bool created_by_aggregation = 0;
+  int status;
+
+  /* Ignore values that were created by the aggregation plugin to avoid weird
+   * effects. */
+  (void) meta_data_get_boolean (vl->meta, "aggregation:created",
+      &created_by_aggregation);
+  if (created_by_aggregation)
+    return (0);
+
+  if (lookup == NULL)
+    status = ENOENT;
+  else
+  {
+    status = lookup_search (lookup, ds, vl);
+    if (status > 0)
+      status = 0;
+  }
+
+  return (status);
+} /* }}} int agg_write */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("aggregation", agg_config);
+  plugin_register_read ("aggregation", agg_read);
+  plugin_register_write ("aggregation", agg_write, /* user_data = */ NULL);
+}
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
index db65791..9c8c6e5 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/amqp.c
- * Copyright (C) 2009  Sebastien Pahl
- * Copyright (C) 2010  Florian Forster
+ * Copyright (C) 2009       Sebastien Pahl
+ * Copyright (C) 2010-2012  Florian Forster
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -31,6 +31,7 @@
 #include "plugin.h"
 #include "utils_cmd_putval.h"
 #include "utils_format_json.h"
+#include "utils_format_graphite.h"
 
 #include <pthread.h>
 
@@ -42,8 +43,9 @@
 #define CAMQP_DM_VOLATILE   1
 #define CAMQP_DM_PERSISTENT 2
 
-#define CAMQP_FORMAT_COMMAND 1
-#define CAMQP_FORMAT_JSON    2
+#define CAMQP_FORMAT_COMMAND    1
+#define CAMQP_FORMAT_JSON       2
+#define CAMQP_FORMAT_GRAPHITE   3
 
 #define CAMQP_CHANNEL 1
 
@@ -68,6 +70,10 @@ struct camqp_config_s
     uint8_t delivery_mode;
     _Bool   store_rates;
     int     format;
+    /* publish & graphite format only */
+    char    *prefix;
+    char    *postfix;
+    char    escape_char;
 
     /* subscribe only */
     char   *exchange_type;
@@ -129,6 +135,9 @@ static void camqp_config_free (void *ptr) /* {{{ */
     sfree (conf->exchange_type);
     sfree (conf->queue);
     sfree (conf->routing_key);
+    sfree (conf->prefix);
+    sfree (conf->postfix);
+
 
     sfree (conf);
 } /* }}} void camqp_config_free */
@@ -178,8 +187,13 @@ static char *camqp_strerror (camqp_config_t *conf, /* {{{ */
             break;
 
         case AMQP_RESPONSE_LIBRARY_EXCEPTION:
+#if HAVE_AMQP_RPC_REPLY_T_LIBRARY_ERRNO
             if (r.library_errno)
                 return (sstrerror (r.library_errno, buffer, buffer_size));
+#else
+            if (r.library_error)
+                return (sstrerror (r.library_error, buffer, buffer_size));
+#endif
             else
                 sstrncpy (buffer, "End of stream", sizeof (buffer));
             break;
@@ -216,6 +230,7 @@ static char *camqp_strerror (camqp_config_t *conf, /* {{{ */
     return (buffer);
 } /* }}} char *camqp_strerror */
 
+#if HAVE_AMQP_RPC_REPLY_T_LIBRARY_ERRNO
 static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */
 {
     amqp_exchange_declare_ok_t *ed_ret;
@@ -246,6 +261,46 @@ static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */
 
     return (0);
 } /* }}} int camqp_create_exchange */
+#else
+static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */
+{
+    amqp_exchange_declare_ok_t *ed_ret;
+    amqp_table_t argument_table;
+    struct amqp_table_entry_t_ argument_table_entries[1];
+
+    if (conf->exchange_type == NULL)
+        return (0);
+
+    /* Valid arguments: "auto_delete", "internal" */
+    argument_table.num_entries = STATIC_ARRAY_SIZE (argument_table_entries);
+    argument_table.entries = argument_table_entries;
+    argument_table_entries[0].key = amqp_cstring_bytes ("auto_delete");
+    argument_table_entries[0].value.kind = AMQP_FIELD_KIND_BOOLEAN;
+    argument_table_entries[0].value.value.boolean = 1;
+
+    ed_ret = amqp_exchange_declare (conf->connection,
+            /* channel     = */ CAMQP_CHANNEL,
+            /* exchange    = */ amqp_cstring_bytes (conf->exchange),
+            /* type        = */ amqp_cstring_bytes (conf->exchange_type),
+            /* passive     = */ 0,
+            /* durable     = */ 0,
+            /* arguments   = */ argument_table);
+    if ((ed_ret == NULL) && camqp_is_error (conf))
+    {
+        char errbuf[1024];
+        ERROR ("amqp plugin: amqp_exchange_declare failed: %s",
+                camqp_strerror (conf, errbuf, sizeof (errbuf)));
+        camqp_close_connection (conf);
+        return (-1);
+    }
+
+    INFO ("amqp plugin: Successfully created exchange \"%s\" "
+            "with type \"%s\".",
+            conf->exchange, conf->exchange_type);
+
+    return (0);
+} /* }}} int camqp_create_exchange */
+#endif
 
 static int camqp_setup_queue (camqp_config_t *conf) /* {{{ */
 {
@@ -316,7 +371,9 @@ static int camqp_setup_queue (camqp_config_t *conf) /* {{{ */
             /* consumer_tag = */ AMQP_EMPTY_BYTES,
             /* no_local     = */ 0,
             /* no_ack       = */ 1,
-            /* exclusive    = */ 0);
+            /* exclusive    = */ 0,
+            /* arguments    = */ AMQP_EMPTY_TABLE
+        );
     if ((cm_ret == NULL) && camqp_is_error (conf))
     {
         char errbuf[1024];
@@ -552,21 +609,25 @@ static void *camqp_subscribe_thread (void *user_data) /* {{{ */
         status = camqp_connect (conf);
         if (status != 0)
         {
+            struct timespec ts_interval;
             ERROR ("amqp plugin: camqp_connect failed. "
                     "Will sleep for %.3f seconds.",
                     CDTIME_T_TO_DOUBLE (interval));
-            sleep (interval);
+            CDTIME_T_TO_TIMESPEC (interval, &ts_interval);
+            nanosleep (&ts_interval, /* remaining = */ NULL);
             continue;
         }
 
         status = amqp_simple_wait_frame (conf->connection, &frame);
         if (status < 0)
         {
+            struct timespec ts_interval;
             ERROR ("amqp plugin: amqp_simple_wait_frame failed. "
                     "Will sleep for %.3f seconds.",
                     CDTIME_T_TO_DOUBLE (interval));
             camqp_close_connection (conf);
-            sleep (interval);
+            CDTIME_T_TO_TIMESPEC (interval, &ts_interval);
+            nanosleep (&ts_interval, /* remaining = */ NULL);
             continue;
         }
 
@@ -591,6 +652,7 @@ static void *camqp_subscribe_thread (void *user_data) /* {{{ */
 
     camqp_config_free (conf);
     pthread_exit (NULL);
+    return (NULL);
 } /* }}} void *camqp_subscribe_thread */
 
 static int camqp_subscribe_init (camqp_config_t *conf) /* {{{ */
@@ -648,6 +710,8 @@ static int camqp_write_locked (camqp_config_t *conf, /* {{{ */
         props.content_type = amqp_cstring_bytes("text/collectd");
     else if (conf->format == CAMQP_FORMAT_JSON)
         props.content_type = amqp_cstring_bytes("application/json");
+    else if (conf->format == CAMQP_FORMAT_GRAPHITE)
+        props.content_type = amqp_cstring_bytes("text/graphite");
     else
         assert (23 == 42);
     props.delivery_mode = conf->delivery_mode;
@@ -726,6 +790,18 @@ static int camqp_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
         format_json_value_list (buffer, &bfill, &bfree, ds, vl, conf->store_rates);
         format_json_finalize (buffer, &bfill, &bfree);
     }
+    else if (conf->format == CAMQP_FORMAT_GRAPHITE)
+    {
+        status = format_graphite (buffer, sizeof (buffer), ds, vl,
+                    conf->prefix, conf->postfix, conf->escape_char,
+                    conf->store_rates);
+        if (status != 0)
+        {
+            ERROR ("amqp plugin: format_graphite failed with status %i.",
+                    status);
+            return (status);
+        }
+    }
     else
     {
         ERROR ("amqp plugin: Invalid format (%i).", conf->format);
@@ -758,6 +834,8 @@ static int camqp_config_set_format (oconfig_item_t *ci, /* {{{ */
         conf->format = CAMQP_FORMAT_COMMAND;
     else if (strcasecmp ("JSON", string) == 0)
         conf->format = CAMQP_FORMAT_JSON;
+    else if (strcasecmp ("Graphite", string) == 0)
+        conf->format = CAMQP_FORMAT_GRAPHITE;
     else
     {
         WARNING ("amqp plugin: Invalid format string: %s",
@@ -798,6 +876,10 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
     /* publish only */
     conf->delivery_mode = CAMQP_DM_VOLATILE;
     conf->store_rates = 0;
+    /* publish & graphite only */
+    conf->prefix = NULL;
+    conf->postfix = NULL;
+    conf->escape_char = '_';
     /* subscribe only */
     conf->exchange_type = NULL;
     conf->queue = NULL;
@@ -855,6 +937,20 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
             status = cf_util_get_boolean (child, &conf->store_rates);
         else if ((strcasecmp ("Format", child->key) == 0) && publish)
             status = camqp_config_set_format (child, conf);
+        else if ((strcasecmp ("GraphitePrefix", child->key) == 0) && publish)
+            status = cf_util_get_string (child, &conf->prefix);
+        else if ((strcasecmp ("GraphitePostfix", child->key) == 0) && publish)
+            status = cf_util_get_string (child, &conf->postfix);
+        else if ((strcasecmp ("GraphiteEscapeChar", child->key) == 0) && publish)
+        {
+            char *tmp_buff = NULL;
+            status = cf_util_get_string (child, &tmp_buff);
+            if (strlen (tmp_buff) > 1)
+                WARNING ("amqp plugin: The option \"GraphiteEscapeChar\" handles "
+                        "only one character. Others will be ignored.");
+            conf->escape_char = tmp_buff[0];
+            sfree (tmp_buff);
+        }
         else
             WARNING ("amqp plugin: Ignoring unknown "
                     "configuration option \"%s\".", child->key);
index a0629d5..4fbbde6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * collectd - src/apcups.c
- * Copyright (C) 2006-2007  Florian octo Forster
+ * Copyright (C) 2006-2012  Florian octo Forster
  * Copyright (C) 2006       Anthony Gialluca <tonyabg at charter.net>
  * Copyright (C) 2000-2004  Kern Sibbald
  * Copyright (C) 1996-1999  Andre M. Hedrick <andre at suse.com>
@@ -73,6 +73,10 @@ static int   conf_port = NISPORT;
 
 static int global_sockfd = -1;
 
+static int count_retries = 0;
+static int count_iterations = 0;
+static _Bool close_socket = 0;
+
 static const char *config_keys[] =
 {
        "Host",
@@ -81,26 +85,31 @@ static const char *config_keys[] =
 };
 static int config_keys_num = 2;
 
-/* Close the network connection */
-static int apcups_shutdown (void)
+static int net_shutdown (int *fd)
 {
        uint16_t packet_size = 0;
 
-       if (global_sockfd < 0)
-               return (0);
+       if ((fd == NULL) || (*fd < 0))
+               return (EINVAL);
 
-       DEBUG ("Gracefully shutting down socket %i.", global_sockfd);
+       swrite (*fd, (void *) &packet_size, sizeof (packet_size));
+       close (*fd);
+       *fd = -1;
 
-       /* send EOF sentinel */
-       swrite (global_sockfd, (void *) &packet_size, sizeof (packet_size));
+       return (0);
+} /* int net_shutdown */
 
-       close (global_sockfd);
-       global_sockfd = -1;
+/* Close the network connection */
+static int apcups_shutdown (void)
+{
+       if (global_sockfd < 0)
+               return (0);
 
+       net_shutdown (&global_sockfd);
        return (0);
 } /* int apcups_shutdown */
 
-/*     
+/*
  * Open a TCP connection to the UPS network server
  * Returns -1 on error
  * Returns socket file descriptor otherwise
@@ -170,7 +179,7 @@ static int net_open (char *host, int port)
        return (sd);
 } /* int net_open (char *host, char *service, int port) */
 
-/* 
+/*
  * Receive a message from the other end. Each message consists of
  * two packets. The first is a header that contains the size
  * of the data that follows in the second packet.
@@ -186,6 +195,7 @@ static int net_recv (int *sockfd, char *buf, int buflen)
        /* get data size -- in short */
        if (sread (*sockfd, (void *) &packet_size, sizeof (packet_size)) != 0)
        {
+               close (*sockfd);
                *sockfd = -1;
                return (-1);
        }
@@ -193,7 +203,11 @@ static int net_recv (int *sockfd, char *buf, int buflen)
        packet_size = ntohs (packet_size);
        if (packet_size > buflen)
        {
-               DEBUG ("record length too large");
+               ERROR ("apcups plugin: Received %"PRIu16" bytes of payload "
+                               "but have only %i bytes of buffer available.",
+                               packet_size, buflen);
+               close (*sockfd);
+               *sockfd = -1;
                return (-2);
        }
 
@@ -203,6 +217,7 @@ static int net_recv (int *sockfd, char *buf, int buflen)
        /* now read the actual data */
        if (sread (*sockfd, (void *) buf, packet_size) != 0)
        {
+               close (*sockfd);
                *sockfd = -1;
                return (-1);
        }
@@ -229,6 +244,7 @@ static int net_send (int *sockfd, char *buff, int len)
 
        if (swrite (*sockfd, (void *) &packet_size, sizeof (packet_size)) != 0)
        {
+               close (*sockfd);
                *sockfd = -1;
                return (-1);
        }
@@ -236,6 +252,7 @@ static int net_send (int *sockfd, char *buff, int len)
        /* send data packet */
        if (swrite (*sockfd, (void *) buff, len) != 0)
        {
+               close (*sockfd);
                *sockfd = -1;
                return (-2);
        }
@@ -253,6 +270,8 @@ static int apc_query_server (char *host, int port,
        char   *toksaveptr;
        char   *key;
        double  value;
+       _Bool retry = 1;
+       int status;
 
 #if APCMAIN
 # define PRINT_VALUE(name, val) printf("  Found property: name = %s; value = %f;\n", name, val)
@@ -260,21 +279,51 @@ static int apc_query_server (char *host, int port,
 # define PRINT_VALUE(name, val) /**/
 #endif
 
-       if (global_sockfd < 0)
+       while (retry)
        {
-               global_sockfd = net_open (host, port);
                if (global_sockfd < 0)
                {
-                       ERROR ("apcups plugin: Connecting to the "
-                                       "apcupsd failed.");
+                       global_sockfd = net_open (host, port);
+                       if (global_sockfd < 0)
+                       {
+                               ERROR ("apcups plugin: Connecting to the "
+                                               "apcupsd failed.");
+                               return (-1);
+                       }
+               }
+
+
+               status = net_send (&global_sockfd, "status", strlen ("status"));
+               if (status != 0)
+               {
+                       /* net_send is closing the socket on error. */
+                       assert (global_sockfd < 0);
+                       if (retry)
+                       {
+                               retry = 0;
+                               count_retries++;
+                               continue;
+                       }
+
+                       ERROR ("apcups plugin: Writing to the socket failed.");
                        return (-1);
                }
-       }
 
-       if (net_send (&global_sockfd, "status", 6) < 0)
+               break;
+       } /* while (retry) */
+
+        /* When collectd's collection interval is larger than apcupsd's
+         * timeout, we would have to retry / re-connect each iteration. Try to
+         * detect this situation and shut down the socket gracefully in that
+         * case. Otherwise, keep the socket open to avoid overhead. */
+       count_iterations++;
+       if ((count_iterations == 10) && (count_retries > 2))
        {
-               ERROR ("apcups plugin: Writing to the socket failed.");
-               return (-1);
+               NOTICE ("apcups plugin: There have been %i retries in the "
+                               "first %i iterations. Will close the socket "
+                               "in future iterations.",
+                               count_retries, count_iterations);
+               close_socket = 1;
        }
 
        while ((n = net_recv (&global_sockfd, recvline, sizeof (recvline) - 1)) > 0)
@@ -298,7 +347,7 @@ static int apc_query_server (char *host, int port,
 
                        if (strcmp ("LINEV", key) == 0)
                                apcups_detail->linev = value;
-                       else if (strcmp ("BATTV", key) == 0) 
+                       else if (strcmp ("BATTV", key) == 0)
                                apcups_detail->battv = value;
                        else if (strcmp ("ITEMP", key) == 0)
                                apcups_detail->itemp = value;
@@ -316,10 +365,16 @@ static int apc_query_server (char *host, int port,
                        tokptr = strtok_r (NULL, ":", &toksaveptr);
                } /* while (tokptr != NULL) */
        }
-       
+       status = errno; /* save errno, net_shutdown() may re-set it. */
+
+       if (close_socket)
+               net_shutdown (&global_sockfd);
+
        if (n < 0)
        {
-               WARNING ("apcups plugin: Error reading from socket");
+               char errbuf[1024];
+               ERROR ("apcups plugin: Reading from socket failed: %s",
+                               sstrerror (status, errbuf, sizeof (errbuf)));
                return (-1);
        }
 
@@ -398,12 +453,12 @@ static int apcups_read (void)
        apcups_detail.timeleft =   -1.0;
        apcups_detail.itemp    = -300.0;
        apcups_detail.linefreq =   -1.0;
-  
+
        status = apc_query_server (conf_host == NULL
                        ? APCUPS_DEFAULT_HOST
                        : conf_host,
                        conf_port, &apcups_detail);
+
        /*
         * if we did not connect then do not bother submitting
         * zeros. We want rrd files to have NAN.
index b640a59..5b7d7a0 100644 (file)
@@ -98,6 +98,10 @@ struct list_info_ptr_s
 };
 typedef struct list_info_ptr_s list_info_ptr_t;
 
+/* FIXME: Enabled by default for backwards compatibility. */
+/* TODO: Remove time parsing code. */
+static _Bool config_parse_time = 1;
+
 static char *url                   = NULL;
 static int global_opcodes          = 1;
 static int global_qtypes           = 1;
@@ -249,7 +253,8 @@ static void submit (time_t ts, const char *plugin_instance, /* {{{ */
 
   vl.values = values;
   vl.values_len = 1;
-  vl.time = TIME_T_TO_CDTIME_T (ts);
+  if (config_parse_time)
+    vl.time = TIME_T_TO_CDTIME_T (ts);
   sstrncpy(vl.host, hostname_g, sizeof(vl.host));
   sstrncpy(vl.plugin, "bind", sizeof(vl.plugin));
   if (plugin_instance) {
@@ -794,7 +799,7 @@ static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */
     list_info_ptr_t list_info =
     {
       plugin_instance,
-      /* type = */ "dns_qtype_gauge"
+      /* type = */ "dns_qtype"
     };
 
     ssnprintf (plugin_instance, sizeof (plugin_instance), "%s-qtypes",
@@ -825,13 +830,14 @@ static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */
         doc, path_ctx, current_time, DS_TYPE_COUNTER);
   } /* }}} */
 
+  /* Record types in the cache */
   if (view->cacherrsets != 0) /* {{{ */
   {
     char plugin_instance[DATA_MAX_NAME_LEN];
     list_info_ptr_t list_info =
     {
       plugin_instance,
-      /* type = */ "dns_qtype_gauge"
+      /* type = */ "dns_qtype_cached"
     };
 
     ssnprintf (plugin_instance, sizeof (plugin_instance), "%s-cache_rr_sets",
@@ -1363,6 +1369,8 @@ static int bind_config (oconfig_item_t *ci) /* {{{ */
       bind_config_set_bool ("MemoryStats", &global_memory_stats, child);
     else if (strcasecmp ("View", child->key) == 0)
       bind_config_add_view (child);
+    else if (strcasecmp ("ParseTime", child->key) == 0)
+      cf_util_get_boolean (child, &config_parse_time);
     else
     {
       WARNING ("bind plugin: Unknown configuration option "
index 5fd1f4f..39503ad 100644 (file)
@@ -37,7 +37,7 @@ for collectd in Python. This is a lot more efficient than executing a
 Python-script every time you want to read a value with the C<exec plugin> (see
 L<collectd-exec(5)>) and provides a lot more functionality, too.
 
-At least python I<version 2.3> is required.
+The minimum required Python version is I<2.3>.
 
 =head1 CONFIGURATION
 
@@ -47,9 +47,9 @@ At least python I<version 2.3> is required.
 
 Loads the Python plugin I<Plugin>. Unlike most other LoadPlugin lines, this one
 should be a block containing the line "Globals true". This will cause collectd
-to export the name of all objects in the python interpreter for all plugins to
+to export the name of all objects in the Python interpreter for all plugins to
 see. If you don't do this or your platform does not support it, the embedded
-interpreter will start anyway but you won't be able to load certain python
+interpreter will start anyway but you won't be able to load certain Python
 modules, e.g. "time".
 
 =item B<Encoding> I<Name>
@@ -68,25 +68,25 @@ use multiple B<ModulePath> lines to add more than one directory.
 
 =item B<LogTraces> I<bool>
 
-If a python script throws an exception it will be logged by collectd with the
+If a Python script throws an exception it will be logged by collectd with the
 name of the exception and the message. If you set this option to true it will
 also log the full stacktrace just like the default output of an interactive
-python interpreter. This should probably be set to false most of the time but
+Python interpreter. This should probably be set to false most of the time but
 is very useful for development and debugging of new modules.
 
 =item B<Interactive> I<bool>
 
-This option will cause the module to launch an interactive python interpreter
+This option will cause the module to launch an interactive Python interpreter
 that reads from and writes to the terminal. Note that collectd will terminate
 right after starting up if you try to run it as a daemon while this option is
-enabled to make sure to start collectd with the B<-f> option.
+enabled so make sure to start collectd with the B<-f> option.
 
 The B<collectd> module is I<not> imported into the interpreter's globals. You
 have to do it manually. Be sure to read the help text of the module, it can be
 used as a reference guide during coding.
 
 This interactive session will behave slightly differently from a daemonized
-collectd script as well as from a normal python interpreter:
+collectd script as well as from a normal Python interpreter:
 
 =over 4
 
@@ -107,22 +107,22 @@ To quit collectd send I<EOF> (press I<Ctrl+D> at the beginning of a new line).
 
 =item
 
-B<3.> collectd handles I<SIGCHLD>. This means that python won't be able to
+B<3.> collectd handles I<SIGCHLD>. This means that Python won't be able to
 determine the return code of spawned processes with system(), popen() and
-subprocess. This will result in python not using external programs like less
+subprocess. This will result in Python not using external programs like less
 to display help texts. You can override this behavior with the B<PAGER>
 environment variable, e.g. I<export PAGER=less> before starting collectd.
-Depending on your version of python this might or might not result in an
+Depending on your version of Python this might or might not result in an
 B<OSError> exception which can be ignored.
 
-If you really need to spawn new processes from python you can register an init
+If you really need to spawn new processes from Python you can register an init
 callback and reset the action for SIGCHLD to the default behavior. Please note
 that this I<will> break the exec plugin. Do not even load the exec plugin if
 you intend to do this!
 
 There is an example script located in B<contrib/python/getsigchld.py>  to do
 this. If you import this from I<collectd.conf> SIGCHLD will be handled
-normally and spawning processes from python will work as intended.
+normally and spawning processes from Python will work as intended.
 
 =back
 
@@ -139,22 +139,22 @@ The I<name> identifies the callback.
 
 =head1 STRINGS
 
-There are a lot of places where strings are send from collectd to python and
-from python to collectd. How exactly this works depends on wheather byte or
-unicode strings or python2 or python3 are used.
+There are a lot of places where strings are sent from collectd to Python and
+from Python to collectd. How exactly this works depends on whether byte or
+unicode strings or Python2 or Python3 are used.
 
 Python2 has I<str>, which is just bytes, and I<unicode>. Python3 has I<str>,
 which is a unicode object, and I<bytes>.
 
-When passing strings from python to collectd all of these object are supported
+When passing strings from Python to collectd all of these object are supported
 in all places, however I<str> should be used if possible. These strings must
 not contain a NUL byte. Ignoring this will result in a I<TypeError> exception.
 If a byte string was used it will be used as is by collectd. If a unicode
 object was used it will be encoded using the default encoding (see above). If
-this is not possible python will raise a I<UnicodeEncodeError> exception.
+this is not possible Python will raise a I<UnicodeEncodeError> exception.
 
-Wenn passing strings from collectd to python the behavior depends on the
-python version used. Python2 will always receive a I<str> object. Python3 will
+When passing strings from collectd to Python the behavior depends on the
+Python version used. Python2 will always receive a I<str> object. Python3 will
 usually receive a I<str> object as well, however the original string will be
 decoded to unicode using the default encoding. If this fails because the
 string is not a valid sequence for this encoding a I<bytes> object will be
@@ -174,7 +174,7 @@ example. The following types of B<callback functions> are known to collectd
 
 =item configuration functions
 
-This type of functions is called during configuration if an appropriate
+These are called during configuration if an appropriate
 B<Module> block has been encountered. It is called once for each B<Module>
 block which matches the name of the callback as provided with the
 B<register_config> method - see below.
@@ -184,14 +184,14 @@ threading functions here!
 
 =item init functions
 
-This type of functions is called once after loading the module and before any
+These are called once after loading the module and before any
 calls to the read and write functions. It should be used to initialize the
 internal state of the plugin (e.E<nbsp>g. open sockets, ...). This is the
 earliest point where you may use threads.
 
 =item read functions
 
-This type of function is used to collect the actual data. It is called once
+These are used to collect the actual data. It is called once
 per interval (see the B<Interval> configuration option of collectd). Usually
 it will call B<plugin_dispatch_values> to dispatch the values to collectd
 which will pass them on to all registered B<write functions>. If this function
@@ -200,23 +200,23 @@ amount of time until it returns normally again.
 
 =item write functions
 
-This type of function is used to write the dispatched values. It is called
+These are used to write the dispatched values. It is called
 once for every value that was dispatched by any plugin.
 
 =item flush functions
 
-This type of function is used to flush internal caches of plugins. It is
+These are used to flush internal caches of plugins. It is
 usually triggered by the user only. Any plugin which caches data before
 writing it to disk should provide this kind of callback function.
 
 =item log functions
 
-This type of function is used to pass messages of plugins or the daemon itself
+These are used to pass messages of plugins or the daemon itself
 to the user.
 
 =item notification function
 
-This type of function is used to act upon notifications. In general, a
+These are used to act upon notifications. In general, a
 notification is a status message that may be associated with a data instance.
 Usually, a notification is generated by the daemon if a configured threshold
 has been exceeded (see the section "THRESHOLD CONFIGURATION" in
@@ -225,12 +225,12 @@ notifications as well.
 
 =item shutdown functions
 
-This type of function is called once before the daemon shuts down. It should
+These are called once before the daemon shuts down. It should
 be used to clean up the plugin (e.g. close sockets, ...).
 
 =back
 
-Any function (except log functions) may set throw an exception in case of any
+Any function (except log functions) may throw an exception in case of
 errors. The exception will be passed on to the user using collectd's logging
 mechanism. If a log callback throws an exception it will be printed to standard
 error instead.
@@ -306,7 +306,7 @@ config file. It will always be a string.
 This is a tuple (which might be empty) of all value, i.e. words following the
 keyword in any given line in the config file.
 
-Every item in this tuple will be either a string or a float or a boolean,
+Every item in this tuple will be either a string, a float or a boolean,
 depending on the contents of the configuration file.
 
 =item children
@@ -367,7 +367,7 @@ Type instance string. May be empty.
 
 =head2 Values
 
-A Value is an object which features a sequence of values. It is based on then
+A Value is an object which features a sequence of values. It is based on the
 I<PluginData> type and uses its members to identify the values.
 
  class Values(PluginData)
@@ -495,7 +495,7 @@ Data descriptors defined here:
 
 =item message
 
-Some kind of description what's going on and why this Notification was
+Some kind of description of what's going on and why this Notification was
 generated.
 
 =item severity
@@ -534,9 +534,12 @@ your callback, not even None.
 I<name> is an optional identifier for this callback. The default name is
 B<python>.I<module>. I<module> is taken from the B<__module__> attribute of
 your callback function. Every callback needs a unique identifier, so if you
-want to register the same callback multiple time in the same module you need to
-specify a name here. Otherwise it's save to ignore this parameter I<identifier>
-is the full identifier assigned to this callback.
+want to register the same callback multiple times in the same module you need to
+specify a name here. Otherwise it's safe to ignore this parameter.
+
+=item
+
+I<identifier> is the full identifier assigned to this callback.
 
 =back
 
@@ -549,7 +552,7 @@ L<"WRITING YOUR OWN PLUGINS"> above) and are passed the following arguments:
 
 The only argument passed is a I<Config> object. See above for the layout of this
 data type.
-Note that you can not receive the whole config files this way, only B<Module>
+Note that you cannot receive the whole config files this way, only B<Module>
 blocks inside the Python configuration block. Additionally you will only
 receive blocks where your callback identifier matches B<python.>I<blockname>.
 
@@ -570,7 +573,7 @@ The callback will be called without arguments.
 
 =item register_write
 
-The callback function will be called with one arguments passed, which will be a
+The callback function will be called with one argument passed, which will be a
 I<Values> object. For the layout of I<Values> see above.
 If this callback function throws an exception the next call will be delayed by
 an increasing interval.
@@ -657,7 +660,7 @@ types used by the read, write and match functions.
 
 =item
 
-Please feel free to send in new plugins to collectd's mailinglist at
+Please feel free to send in new plugins to collectd's mailing list at
 E<lt>collectdE<nbsp>atE<nbsp>verplant.orgE<gt> for review and, possibly,
 inclusion in the main distribution. In the latter case, we will take care of
 keeping the plugin up to date and adapting it to new versions of collectd.
@@ -673,7 +676,7 @@ L<http://collectd.org/dev-info.shtml>.
 
 =item
 
-collectd is heavily multi-threaded. Each collectd thread accessing the python
+collectd is heavily multi-threaded. Each collectd thread accessing the Python
 plugin will be mapped to a Python interpreter thread. Any such thread will be
 created and destroyed transparently and on-the-fly.
 
@@ -703,7 +706,7 @@ dispatched by the python plugin after upgrades.
 
 =item
 
-Not all aspects of the collectd API are accessible from python. This includes
+Not all aspects of the collectd API are accessible from Python. This includes
 but is not limited to filters and data sets.
 
 =back
index cdb0ee8..d96d676 100644 (file)
@@ -79,6 +79,7 @@ static void sig_usr1_handler (int __attribute__((unused)) signal)
        pthread_attr_init (&attr);
        pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
        pthread_create (&thread, &attr, do_flush, NULL);
+       pthread_attr_destroy (&attr);
 }
 
 static int init_hostname (void)
@@ -164,10 +165,11 @@ static int init_global_variables (void)
 
 static int change_basedir (const char *orig_dir)
 {
-       char *dir = strdup (orig_dir);
-       int dirlen;
+       char *dir;
+       size_t dirlen;
        int status;
 
+       dir = strdup (orig_dir);
        if (dir == NULL)
        {
                char errbuf[1024];
@@ -184,39 +186,41 @@ static int change_basedir (const char *orig_dir)
                return (-1);
 
        status = chdir (dir);
-       free (dir);
+       if (status == 0)
+       {
+               free (dir);
+               return (0);
+       }
+       else if (errno != ENOENT)
+       {
+               char errbuf[1024];
+               ERROR ("change_basedir: chdir (%s): %s", dir,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               free (dir);
+               return (-1);
+       }
 
+       status = mkdir (dir, S_IRWXU | S_IRWXG | S_IRWXO);
        if (status != 0)
        {
-               if (errno == ENOENT)
-               {
-                       if (mkdir (orig_dir, 0755) == -1)
-                       {
-                               char errbuf[1024];
-                               ERROR ("change_basedir: mkdir (%s): %s", orig_dir,
-                                               sstrerror (errno, errbuf,
-                                                       sizeof (errbuf)));
-                               return (-1);
-                       }
-                       else if (chdir (orig_dir) == -1)
-                       {
-                               char errbuf[1024];
-                               ERROR ("chdir (%s): %s", orig_dir,
-                                               sstrerror (errno, errbuf,
-                                                       sizeof (errbuf)));
-                               return (-1);
-                       }
-               }
-               else
-               {
-                       char errbuf[1024];
-                       ERROR ("chdir (%s): %s", orig_dir,
-                                       sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-                       return (-1);
-               }
+               char errbuf[1024];
+               ERROR ("change_basedir: mkdir (%s): %s", dir,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               free (dir);
+               return (-1);
+       }
+
+       status = chdir (dir);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("change_basedir: chdir (%s): %s", dir,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               free (dir);
+               return (-1);
        }
 
+       free (dir);
        return (0);
 } /* static int change_basedir (char *dir) */
 
index c731810..9f5dedc 100644 (file)
@@ -52,6 +52,7 @@
 # to missing dependencies or because they have been deactivated explicitly.  #
 ##############################################################################
 
+#@BUILD_PLUGIN_AGGREGATION_TRUE@LoadPlugin aggregation
 #@BUILD_PLUGIN_AMQP_TRUE@LoadPlugin amqp
 #@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
 #@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
@@ -73,6 +74,7 @@
 #@BUILD_PLUGIN_DNS_TRUE@LoadPlugin dns
 #@BUILD_PLUGIN_EMAIL_TRUE@LoadPlugin email
 #@BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy
+#@BUILD_PLUGIN_ETHSTAT_TRUE@LoadPlugin ethstat
 #@BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec
 #@BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount
 #@BUILD_PLUGIN_FSCACHE_TRUE@LoadPlugin fscache
@@ -89,6 +91,7 @@
 #@BUILD_PLUGIN_LPAR_TRUE@LoadPlugin lpar
 #@BUILD_PLUGIN_MADWIFI_TRUE@LoadPlugin madwifi
 #@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon
+#@BUILD_PLUGIN_MD_TRUE@LoadPlugin md
 #@BUILD_PLUGIN_MEMCACHEC_TRUE@LoadPlugin memcachec
 #@BUILD_PLUGIN_MEMCACHED_TRUE@LoadPlugin memcached
 @BUILD_PLUGIN_MEMORY_TRUE@@BUILD_PLUGIN_MEMORY_TRUE@LoadPlugin memory
 #@BUILD_PLUGIN_NOTIFY_DESKTOP_TRUE@LoadPlugin notify_desktop
 #@BUILD_PLUGIN_NOTIFY_EMAIL_TRUE@LoadPlugin notify_email
 #@BUILD_PLUGIN_NTPD_TRUE@LoadPlugin ntpd
+#@BUILD_PLUGIN_NUMA_TRUE@LoadPlugin numa
 #@BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut
 #@BUILD_PLUGIN_OLSRD_TRUE@LoadPlugin olsrd
 #@BUILD_PLUGIN_ONEWIRE_TRUE@LoadPlugin onewire
 #@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
 #@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
 #@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless
+#@BUILD_PLUGIN_WRITE_GRAPHITE_TRUE@LoadPlugin write_graphite
 #@BUILD_PLUGIN_WRITE_HTTP_TRUE@LoadPlugin write_http
 #@BUILD_PLUGIN_WRITE_REDIS_TRUE@LoadPlugin write_redis
-#@BUILD_PLUGIN_WRITE_MONGO_TRUE@LoadPlugin write_mongo
+#@BUILD_PLUGIN_WRITE_MONGODB_TRUE@LoadPlugin write_mongodb
 #@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms
 #@BUILD_PLUGIN_ZFS_ARC_TRUE@LoadPlugin zfs_arc
 
 # ription of those options is available in the collectd.conf(5) manual page. #
 ##############################################################################
 
+#<Plugin "aggregation">
+#  <Aggregation>
+#    #Host "unspecified"
+#    Plugin "cpu"
+#    #PluginInstance "unspecified"
+#    Type "cpu"
+#    #TypeInstance "unspecified"
+#
+#    GroupBy "Host"
+#    GroupBy "TypeInstance"
+#
+#    CalculateNum false
+#    CalculateSum false
+#    CalculateAverage true
+#    CalculateMinimum false
+#    CalculateMaximum false
+#    CalculateStddev false
+#  </Aggregation>
+#</Plugin>
+
 #<Plugin "amqp">
 #  <Publish "name">
 #    Host "localhost"
 
 #<Plugin "bind">
 #  URL "http://localhost:8053/"
+#  ParseTime       false
 #  OpCodes         true
 #  QTypes          true
 #
 #      MaxConns 5
 #</Plugin>
 
+#<Plugin ethstat>
+#      Interface "eth0"
+#      Map "rx_csum_offload_errors" "if_rx_errors" "checksum_offload"
+#      Map "multicast" "if_multicast"
+#      MappedOnly false
+#</Plugin>
+
 #<Plugin exec>
 #      Exec "user:group" "/path/to/exec"
 #      NotificationExec "user:group" "/path/to/exec"
 #      Port "411"
 #</Plugin>
 
+#<Plugin md>
+#      Device "/dev/md0"
+#      IgnoreSelected false
+#</Plugin>
+
 #<Plugin memcachec>
 #      <Page "plugin_instance">
 #              Server "localhost"
 #</Plugin>
 
 #<Plugin memcached>
-#      Host "127.0.0.1"
-#      Port "11211"
+#      <Instance "local">
+#              Host "127.0.0.1"
+#              Port "11211"
+#      </Instance>
 #</Plugin>
 
 #<Plugin modbus>
 #      Host "localhost"
 #      Port 123
 #      ReverseLookups false
+#      IncludeUnitID true
 #</Plugin>
 
 #<Plugin nut>
 #</Plugin>
 
 #<Plugin sensors>
+#      SensorConfigFile "/etc/sensors.conf"
 #      Sensor "it8712-isa-0290/temperature-temp1"
 #      Sensor "it8712-isa-0290/fanspeed-fan3"
 #      Sensor "it8712-isa-0290/voltage-in8"
 
 #<Plugin "swap">
 #      ReportByDevice false
+#      ReportBytes true
 #</Plugin>
 
 #<Plugin "table">
 #      Verbose false
 #</Plugin>
 
+#<Plugin write_graphite>
+#  <Carbon>
+#    Host "localhost"
+#    Port "2003"
+#    Prefix "collectd"
+#    Postfix "collectd"
+#    StoreRates false
+#    AlwaysAppendDS false
+#    EscapeCharacter "_"
+#  </Carbon>
+#</Plugin>
+
 #<Plugin write_http>
 #      <URL "http://example.com/collectd-post">
 #              User "collectd"
 #      </Node>
 #</Plugin>
 
-#<Plugin write_mongo>
+#<Plugin write_mongodb>
 #      <Node "example">
 #              Host "localhost"
 #              Port "27017"
 #              Timeout 1000
+#              StoreRates false
 #      </Node>
 #</Plugin>
 
index b9a1e02..15b6f68 100644 (file)
@@ -25,22 +25,32 @@ controls which plugins to load. These plugins ultimately define collectd's
 behavior.
 
 The syntax of this config file is similar to the config file of the famous
-B<Apache Webserver>. Each line contains either a key-value-pair or a
-section-start or -end. Empty lines and everything after the hash-symbol `#' is
-ignored. Values are either string, enclosed in double-quotes,
-(floating-point-)numbers or a boolean expression, i.E<nbsp>e. either B<true> or
-B<false>. String containing of only alphanumeric characters and underscores do
-not need to be quoted. Lines may be wrapped by using `\' as the last character
-before the newline. This allows long lines to be split into multiple lines.
-Quoted strings may be wrapped as well. However, those are treated special in
-that whitespace at the beginning of the following lines will be ignored, which
-allows for nicely indenting the wrapped lines.
-
-The configuration is read and processed in order, i.E<nbsp>e. from top to
-bottom. So the plugins are loaded in the order listed in this config file. It
-is a good idea to load any logging plugins first in order to catch messages
-from plugins during configuration. Also, the C<LoadPlugin> option B<must> occur
-B<before> the C<E<lt>Plugin ...E<gt>> block.
+I<Apache> webserver. Each line contains either an option (a key and a list of
+one or more values) or a section-start or -end. Empty lines and everything
+after a non-quoted hash-symbol (C<#>) is ignored. I<Keys> are unquoted
+strings, consisting only of alphanumeric characters and the underscore (C<_>)
+character. Keys are handled case insensitive by I<collectd> itself and all
+plugins included with it. I<Values> can either be an I<unquoted string>, a
+I<quoted string> (enclosed in double-quotes) a I<number> or a I<boolean>
+expression. I<Unquoted strings> consist of only alphanumeric characters and
+underscores (C<_>) and do not need to be quoted. I<Quoted strings> are
+enclosed in double quotes (C<">). You can use the backslash character (C<\>)
+to include double quotes as part of the string. I<Numbers> can be specified in
+decimal and floating point format (using a dot C<.> as decimal separator),
+hexadecimal when using the C<0x> prefix and octal with a leading zero (C<0>).
+I<Boolean> values are either B<true> or B<false>.
+
+Lines may be wrapped by using C<\> as the last character before the newline.
+This allows long lines to be split into multiple lines. Quoted strings may be
+wrapped as well. However, those are treated special in that whitespace at the
+beginning of the following lines will be ignored, which allows for nicely
+indenting the wrapped lines.
+
+The configuration is read and processed in order, i.e. from top to bottom. So
+the plugins are loaded in the order listed in this config file. It is a good
+idea to load any logging plugins first in order to catch messages from plugins
+during configuration. Also, the C<LoadPlugin> option B<must> occur B<before>
+the appropriate C<E<lt>Plugin ...E<gt>> block.
 
 =head1 GLOBAL OPTIONS
 
@@ -189,12 +199,122 @@ C<Plugin>-Section. Which options exist depends on the plugin used. Some plugins
 require external configuration, too. The C<apache plugin>, for example,
 required C<mod_status> to be configured in the webserver you're going to
 collect data from. These plugins are listed below as well, even if they don't
-require any configuration within collectd's configfile.
+require any configuration within collectd's configuration file.
 
 A list of all plugins and a short summary for each plugin can be found in the
 F<README> file shipped with the sourcecode and hopefully binary packets as
 well.
 
+=head2 Plugin C<aggregation>
+
+The I<Aggregation plugin> makes it possible to aggregate several values into
+one using aggregation functions such as I<sum>, I<average>, I<min> and I<max>.
+This can be put to a wide variety of uses, e.g. average and total CPU
+statistics for your entire fleet.
+
+The grouping is powerful but, as with many powerful tools, may be a bit
+difficult to wrap your head around. The grouping will therefore be
+demonstrated using an example: The average and sum of the CPU usage across
+all CPUs of each host is to be calculated.
+
+To select all the affected values for our example, set C<Plugin cpu> and
+C<Type cpu>. The other values are left unspecified, meaning "all values". The
+I<Host>, I<Plugin>, I<PluginInstance>, I<Type> and I<TypeInstance> options
+work as if they were specified in the C<WHERE> clause of an C<SELECT> SQL
+statement.
+
+  Plugin "cpu"
+  Type "cpu"
+
+Although the I<Host>, I<PluginInstance> (CPU number, i.e. 0, 1, 2, ...)  and
+I<TypeInstance> (idle, user, system, ...) fields are left unspecified in the
+example, the intention is to have a new value for each host / type instance
+pair. This is achieved by "grouping" the values using the C<GroupBy> option.
+It can be specified multiple times to group by more than one field.
+
+  GroupBy "Host"
+  GroupBy "TypeInstance"
+
+We do neither specify nor group by I<plugin instance> (the CPU number), so all
+metrics that differ in the CPU number only will be aggregated. Each
+aggregation needs I<at least one> such field, otherwise no aggregation would
+take place.
+
+The full example configuration looks like this:
+
+ <Plugin "aggregation">
+   <Aggregation>
+     Plugin "cpu"
+     Type "cpu"
+     
+     GroupBy "Host"
+     GroupBy "TypeInstance"
+     
+     CalculateSum true
+     CalculateAverage true
+   </Aggregation>
+ </Plugin>
+
+There are a couple of limitations you should be aware of:
+
+=over 4
+
+=item
+
+The I<Type> cannot be left unspecified, because it is not reasonable to add
+apples to oranges. Also, the internal lookup structure won't work if you try
+to group by type.
+
+=item
+
+There must be at least one unspecified, ungrouped field. Otherwise nothing
+will be aggregated.
+
+=back
+
+As you can see in the example above, each aggregation has its own
+B<Aggregation> block. You can have multiple aggregation blocks and aggregation
+blocks may match the same values, i.e. one value list can update multiple
+aggregations. The following options are valid inside B<Aggregation> blocks:
+
+=over 4
+
+=item B<Host> I<Host>
+
+=item B<Plugin> I<Plugin>
+
+=item B<PluginInstance> I<PluginInstance>
+
+=item B<Type> I<Type>
+
+=item B<TypeInstance> I<TypeInstance>
+
+Selects the value lists to be added to this aggregation. B<Type> must be a
+valid data set name, see L<types.db(5)> for details.
+
+=item B<GroupBy> B<Host>|B<Plugin>|B<PluginInstance>|B<TypeInstance>
+
+Group valued by the specified field. The B<GroupBy> option may be repeated to
+group by multiple fields.
+
+=item B<CalculateNum> B<true>|B<false>
+
+=item B<CalculateSum> B<true>|B<false>
+
+=item B<CalculateAverage> B<true>|B<false>
+
+=item B<CalculateMinimum> B<true>|B<false>
+
+=item B<CalculateMaximum> B<true>|B<false>
+
+=item B<CalculateStddev> B<true>|B<false>
+
+Boolean options for enabling calculation of the number of value lists, their
+sum, average, minimum, maximum andE<nbsp>/ or standard deviation. All options
+are disabled by default.
+
+=back
+
 =head2 Plugin C<amqp>
 
 The I<AMQMP plugin> can be used to communicate with other instances of
@@ -216,6 +336,8 @@ possibly filtering or messages.
  #   Persistent false
  #   Format "command"
  #   StoreRates false
+ #   GraphitePrefix "collectd."
+ #   GraphiteEscapeChar "_"
    </Publish>
    
    # Receive values from an AMQP broker
@@ -316,6 +438,10 @@ If set to B<JSON>, the values are encoded in the I<JavaScript Object Notation>,
 an easy and straight forward exchange format. The C<Content-Type> header field
 will be set to C<application/json>.
 
+If set to B<Graphite>, values are encoded in the I<Graphite> format, which is
+"<metric> <value> <timestamp>\n". The C<Content-Type> header field will be set to
+C<text/graphite>.
+
 A subscribing client I<should> use the C<Content-Type> header field to
 determine how to decode the values. Currently, the I<AMQP plugin> itself can
 only decode the B<Command> format.
@@ -330,6 +456,25 @@ using the internal value cache.
 Please note that currently this option is only used if the B<Format> option has
 been set to B<JSON>.
 
+=item B<GraphitePrefix> (Publish and B<Format>=I<Graphite> only)
+
+A prefix can be added in the metric name when outputting in the I<Graphite> format.
+It's added before the I<Host> name.
+Metric name will be "<prefix><host><postfix><plugin><type><name>"
+
+=item B<GraphitePostfix> (Publish and B<Format>=I<Graphite> only)
+
+A postfix can be added in the metric name when outputting in the I<Graphite> format.
+It's added after the I<Host> name.
+Metric name will be "<prefix><host><postfix><plugin><type><name>"
+
+=item B<GraphiteEscapeChar> (Publish and B<Format>=I<Graphite> only)
+
+Specify a character to replace dots (.) in the host part of the metric name.
+In I<Graphite> metric name, dots are used as separators between different
+metric parts (host, plugin, type).
+Default is "_" (I<Underscore>).
+
 =back
 
 =head2 Plugin C<apache>
@@ -489,6 +634,7 @@ Synopsis:
 
  <Plugin "bind">
    URL "http://localhost:8053/"
+   ParseTime       false
    OpCodes         true
    QTypes          true
  
@@ -515,35 +661,44 @@ The bind plugin accepts the following configuration options:
 URL from which to retrieve the XML data. If not specified,
 C<http://localhost:8053/> will be used.
 
-=item B<OpCodes> I<true>|I<false>
+=item B<ParseTime> B<true>|B<false>
+
+When set to B<true>, the time provided by BIND will be parsed and used to
+dispatch the values. When set to B<false>, the local time source is queried.
+
+This setting is set to B<true> by default for backwards compatibility; setting
+this to B<false> is I<recommended> to avoid problems with timezones and
+localization.
+
+=item B<OpCodes> B<true>|B<false>
 
 When enabled, statistics about the I<"OpCodes">, for example the number of
 C<QUERY> packets, are collected.
 
 Default: Enabled.
 
-=item B<QTypes> I<true>|I<false>
+=item B<QTypes> B<true>|B<false>
 
 When enabled, the number of I<incoming> queries by query types (for example
 C<A>, C<MX>, C<AAAA>) is collected.
 
 Default: Enabled.
 
-=item B<ServerStats> I<true>|I<false>
+=item B<ServerStats> B<true>|B<false>
 
 Collect global server statistics, such as requests received over IPv4 and IPv6,
 successful queries, and failed updates.
 
 Default: Enabled.
 
-=item B<ZoneMaintStats> I<true>|I<false>
+=item B<ZoneMaintStats> B<true>|B<false>
 
 Collect zone maintenance statistics, mostly information about notifications
 (zone updates) and zone transfers.
 
 Default: Enabled.
 
-=item B<ResolverStats> I<true>|I<false>
+=item B<ResolverStats> B<true>|B<false>
 
 Collect resolver statistics, i.E<nbsp>e. statistics about outgoing requests
 (e.E<nbsp>g. queries over IPv4, lame servers). Since the global resolver
@@ -572,21 +727,21 @@ configured, no detailed view statistics will be collected.
 
 =over 4
 
-=item B<QTypes> I<true>|I<false>
+=item B<QTypes> B<true>|B<false>
 
 If enabled, the number of I<outgoing> queries by query type (e.E<nbsp>g. C<A>,
 C<MX>) is collected.
 
 Default: Enabled.
 
-=item B<ResolverStats> I<true>|I<false>
+=item B<ResolverStats> B<true>|B<false>
 
 Collect resolver statistics, i.E<nbsp>e. statistics about outgoing requests
 (e.E<nbsp>g. queries over IPv4, lame servers).
 
 Default: Enabled.
 
-=item B<CacheRRSets> I<true>|I<false>
+=item B<CacheRRSets> B<true>|B<false>
 
 If enabled, the number of entries (I<"RR sets">) in the view's cache by query
 type is collected. Negative entries (queries which resulted in an error, for
@@ -1268,6 +1423,41 @@ at most B<16384> to prevent typos and dumb mistakes.
 
 =back
 
+=head2 Plugin C<ethstat>
+
+The I<ethstat plugin> collects information about network interface cards (NICs)
+by talking directly with the underlying kernel driver using L<ioctl(2)>.
+
+B<Synopsis:>
+
+ <Plugin "ethstat">
+   Interface "eth0"
+   Map "rx_csum_offload_errors" "if_rx_errors" "checksum_offload"
+   Map "multicast" "if_multicast"
+ </Plugin>
+
+B<Options:>
+
+=over 4
+
+=item B<Interface> I<Name>
+
+Collect statistical information about interface I<Name>.
+
+=item B<Map> I<Name> I<Type> [I<TypeInstance>]
+
+By default, the plugin will submit values as type C<derive> and I<type
+instance> set to I<Name>, the name of the metric as reported by the driver. If
+an appropriate B<Map> option exists, the given I<Type> and, optionally,
+I<TypeInstance> will be used.
+
+=item B<MappedOnly> B<true>|B<false>
+
+When set to B<true>, only metrics that can be mapped to to a I<type> will be
+collected, all other metrics will be ignored. Defaults to B<false>.
+
+=back
+
 =head2 Plugin C<exec>
 
 Please make sure to read L<collectd-exec(5)> before using this plugin. It
@@ -1818,6 +2008,31 @@ TCP-Port to connect to. Defaults to B<411>.
 
 =back
 
+=head2 Plugin C<md>
+
+The C<md plugin> collects information from Linux Software-RAID devices (md).
+
+All reported values are of the type C<md_disks>. Reported type instances are
+I<active>, I<failed> (present but not operational), I<spare> (hot stand-by) and
+I<missing> (physically absent) disks.
+
+=over 4
+
+=item B<Device> I<Device>
+
+Select md devices based on device name. The I<device name> is the basename of
+the device, i.e. the name of the block device without the leading C</dev/>.
+See B<IgnoreSelected> for more details.
+
+=item B<IgnoreSelected> B<true>|B<false>
+
+Invert device selection: If set to B<true>, all md devices B<except> those
+listed using B<Device> are collected. If B<false> (the default), only those
+listed are collected. If no configuration is given, the B<md> plugin will
+collect data from all md devices.
+
+=back
+
 =head2 Plugin C<memcachec>
 
 The C<memcachec plugin> connects to a memcached server, queries one or more
@@ -1875,6 +2090,17 @@ The C<memcached plugin> connects to a memcached server and queries statistics
 about cache utilization, memory and bandwidth used.
 L<http://www.danga.com/memcached/>
 
+ <Plugin "memcached">
+   <Instance "name">
+     Host "memcache.example.com"
+     Port 11211
+   </Instance>
+ </Plugin>
+
+The plugin configuration consists of one or more B<Instance> blocks which
+specify one I<memcached> connection each. Within the B<Instance> blocks, the
+following options are allowed:
+
 =over 4
 
 =item B<Host> I<Hostname>
@@ -1885,6 +2111,11 @@ Hostname to connect to. Defaults to B<127.0.0.1>.
 
 TCP-Port to connect to. Defaults to B<11211>.
 
+=item B<Socket> I<Path>
+
+Connect to I<memcached> using the UNIX domain socket at I<Path>. If this
+setting is given, the B<Host> and B<Port> settings are ignored.
+
 =back
 
 =head2 Plugin C<modbus>
@@ -2660,10 +2891,18 @@ The default IPv6 multicast group is C<ff18::efc0:4a42>. The default IPv4
 multicast group is C<239.192.74.66>. The default I<UDP> port is B<25826>.
 
 Both, B<Server> and B<Listen> can be used as single option or as block. When
-used as block, given options are valid for this socket only. For example:
+used as block, given options are valid for this socket only. The following
+example will export the metrics twice: Once to an "internal" server (without
+encryption and signing) and one to an external server (with cryptographic
+signature):
 
  <Plugin "network">
+   # Export to an internal server
+   # (demonstrates usage without additional options)
    Server "collectd.internal.tld"
+   
+   # Export to an external server
+   # (demonstrates usage with signature options)
    <Server "collectd.external.tld">
      SecurityLevel "sign"
      Username "myhostname"
@@ -2792,7 +3031,18 @@ operating systems.
 =item B<MaxPacketSize> I<1024-65535>
 
 Set the maximum size for datagrams received over the network. Packets larger
-than this will be truncated. Defaults to 1452E<nbsp>bytes.
+than this will be truncated. Defaults to 1452E<nbsp>bytes, which is the maximum
+payload size that can be transmitted in one Ethernet frame using IPv6E<nbsp>/
+UDP.
+
+On the server side, this limit should be set to the largest value used on
+I<any> client. Likewise, the value on the client must not be larger than the
+value on the server, or data will be lost.
+
+B<Compatibility:> Versions prior to I<versionE<nbsp>4.8> used a fixed sized
+buffer of 1024E<nbsp>bytes. Versions I<4.8>, I<4.9> and I<4.10> used a default
+value of 1024E<nbsp>bytes to avoid problems when sending data to an older
+server.
 
 =item B<Forward> I<true|false>
 
@@ -2960,6 +3210,16 @@ IP-address may be used in a filename it is recommended to disable reverse
 lookups. The default is to do reverse lookups to preserve backwards
 compatibility, though.
 
+=item B<IncludeUnitID> B<true>|B<false>
+
+When a peer is a refclock, include the unit ID in the I<type instance>.
+Defaults to B<false> for backward compatibility.
+
+If two refclock peers use the same driver and this is B<false>, the plugin will
+try to write simultaneous measurements from both to the same type instance.
+This will result in error messages in the log and only one set of measurements
+making it through.
+
 =back
 
 =head2 Plugin C<nut>
@@ -3185,6 +3445,11 @@ values submitted to the daemon. Other than that, that name is not used.
 Defines the "database alias" or "service name" to connect to. Usually, these
 names are defined in the file named C<$ORACLE_HOME/network/admin/tnsnames.ora>.
 
+=item B<Host> I<Host>
+
+Hostname to use when dispatching values for this database. Defaults to using
+the global hostname of the I<collectd> instance.
+
 =item B<Username> I<Username>
 
 Username used for authentication.
@@ -3455,6 +3720,11 @@ used, the parameter expands to "localhost".
 
 The name of the database of the current connection.
 
+=item I<instance>
+
+The name of the database plugin instance. See the B<Instance> option of the
+database specification below for details.
+
 =item I<username>
 
 The username used to connect to the database.
@@ -3613,6 +3883,13 @@ Specify the password to be used when connecting to the server.
 Specify whether to use an SSL connection when contacting the server. The
 following modes are supported:
 
+=item B<Instance> I<name>
+
+Specify the plugin instance name that should be used instead of the database
+name (which is the default, if this option has not been specified). This
+allows to query multiple databases of the same name on the same host (e.g.
+when running multiple database server versions in parallel).
+
 =over 4
 
 =item I<disable>
@@ -3973,6 +4250,10 @@ The B<Port> option is the TCP port on which the Redis instance accepts
 connections. Either a service name of a port number may be given. Please note
 that numerical port numbers must be given as a string, too.
 
+=item B<Password> I<Password>
+
+Use I<Password> to authenticate when connecting to I<Redis>.
+
 =item B<Timeout> I<Timeout in miliseconds>
 
 The B<Timeout> option set the socket timeout for node response. Since the Redis
@@ -4030,6 +4311,50 @@ Enables or disables the creation of RRD files. If the daemon is not running
 locally, or B<DataDir> is set to a relative path, this will not work as
 expected. Default is B<true>.
 
+=item B<StepSize> I<Seconds>
+
+B<Force> the stepsize of newly created RRD-files. Ideally (and per default)
+this setting is unset and the stepsize is set to the interval in which the data
+is collected. Do not use this option unless you absolutely have to for some
+reason. Setting this option may cause problems with the C<snmp plugin>, the
+C<exec plugin> or when the daemon is set up to receive data from other hosts.
+
+=item B<HeartBeat> I<Seconds>
+
+B<Force> the heartbeat of newly created RRD-files. This setting should be unset
+in which case the heartbeat is set to twice the B<StepSize> which should equal
+the interval in which data is collected. Do not set this option unless you have
+a very good reason to do so.
+
+=item B<RRARows> I<NumRows>
+
+The C<rrdtool plugin> calculates the number of PDPs per CDP based on the
+B<StepSize>, this setting and a timespan. This plugin creates RRD-files with
+three times five RRAs, i. e. five RRAs with the CFs B<MIN>, B<AVERAGE>, and
+B<MAX>. The five RRAs are optimized for graphs covering one hour, one day, one
+week, one month, and one year.
+
+So for each timespan, it calculates how many PDPs need to be consolidated into
+one CDP by calculating:
+  number of PDPs = timespan / (stepsize * rrarows)
+
+Bottom line is, set this no smaller than the width of you graphs in pixels. The
+default is 1200.
+
+=item B<RRATimespan> I<Seconds>
+
+Adds an RRA-timespan, given in seconds. Use this option multiple times to have
+more then one RRA. If this option is never used, the built-in default of (3600,
+86400, 604800, 2678400, 31622400) is used.
+
+For more information on how RRA-sizes are calculated see B<RRARows> above.
+
+=item B<XFF> I<Factor>
+
+Set the "XFiles Factor". The default is 0.1. If unsure, don't set this option.
+I<Factor> must be in the range C<[0.0-1.0)>, i.e. between zero (inclusive) and
+one (exclusive).
+
 =back
 
 =head2 Plugin C<rrdtool>
@@ -4087,6 +4412,8 @@ For more information on how RRA-sizes are calculated see B<RRARows> above.
 =item B<XFF> I<Factor>
 
 Set the "XFiles Factor". The default is 0.1. If unsure, don't set this option.
+I<Factor> must be in the range C<[0.0-1.0)>, i.e. between zero (inclusive) and
+one (exclusive).
 
 =item B<CacheFlush> I<Seconds>
 
@@ -4155,6 +4482,11 @@ L<http://secure.netroedge.com/~lm78/>.
 
 =over 4
 
+=item B<SensorConfigFile> I<File>
+
+Read the I<lm_sensors> configuration from I<File>. When unset (recommended),
+the library's default will be used.
+
 =item B<Sensor> I<chip-bus-address/type-feature>
 
 Selects the name of the sensor which you want to collect or ignore, depending
@@ -4197,6 +4529,11 @@ and available space of each device will be reported separately.
 This option is only available if the I<Swap plugin> can read C</proc/swaps>
 (under Linux) or use the L<swapctl(2)> mechanism (under I<Solaris>).
 
+=item B<ReportBytes> B<false>|B<true>
+
+When enabled, the I<swap I/O> is reported in bytes. When disabled, the default,
+I<swap I/O> is reported in pages. This option is available under Linux only.
+
 =back
 
 =head2 Plugin C<syslog>
@@ -4212,6 +4549,15 @@ syslog-daemon.
 Please note that B<debug> is only available if collectd has been compiled with
 debugging support.
 
+=item B<NotifyLevel> B<OKAY>|B<WARNING>|B<FAILURE>
+
+Controls which notifications should be sent to syslog. The default behaviour is
+not to send any. Less severe notifications always imply logging more severe
+notifications: Setting this to B<OKAY> means all notifications will be sent to
+syslog, setting this to B<WARNING> will send B<WARNING> and B<FAILURE>
+notifications but will dismiss B<OKAY> notifications. Setting this option to
+B<FAILURE> will only send failures to syslog.
+
 =back
 
 =head2 Plugin C<table>
@@ -4762,6 +5108,115 @@ traffic (e.E<nbsp>g. due to headers and retransmission). If you want to
 collect on-wire traffic you could, for example, use the logging facilities of
 iptables to feed data for the guest IPs into the iptables plugin.
 
+=head2 Plugin C<write_graphite>
+
+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.
+
+Synopsis:
+
+ <Plugin write_graphite>
+   <Carbon>
+     Host "localhost"
+     Port "2003"
+     Prefix "collectd"
+   </Carbon>
+ </Plugin>
+
+=over 4
+
+=item B<Host> I<Address>
+
+Hostname or address to connect to. Defaults to C<localhost>.
+
+=item B<Port> I<Service>
+
+Service name or port number to connect to. Defaults to C<2003>.
+
+=item B<Prefix> I<String>
+
+When set, I<String> is added in front of the host name. Dots and whitespace are
+I<not> escaped in this string (see B<EscapeCharacter> below).
+
+=item B<Postfix> I<String>
+
+When set, I<String> is appended to the host name. Dots and whitespace are
+I<not> escaped in this string (see B<EscapeCharacter> below).
+
+=item B<EscapeCharacter> I<Char>
+
+I<Carbon> uses the dot (C<.>) as escape character and doesn't allow whitespace
+in the identifier. The B<EscapeCharacter> option determines which character
+dots, whitespace and control characters are replaced with. Defaults to
+underscore (C<_>).
+
+=item B<StoreRates> B<false>|B<true>
+
+If set to B<true> (the default), convert counter values to rates. If set to
+B<false> counter values are stored as is, i.E<nbsp>e. as an increasing integer
+number.
+
+=item B<SeparateInstances> B<false>|B<true>
+
+If set to B<true>, the plugin instance and type instance will be in their own
+path component, for example C<host.cpu.0.cpu.idle>. If set to B<false> (the
+default), the plugin and plugin instance (and likewise the type and type
+instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
+
+=item B<AlwaysAppendDS> B<false>|B<true>
+
+If set the B<true>, append the name of the I<Data Source> (DS) to the "metric"
+identifier. If set to B<false> (the default), this is only done when there is
+more than one DS.
+
+=back
+
+=head2 Plugin C<write_mongodb>
+
+The I<write_mongodb plugin> will send values to I<MongoDB>, a schema-less
+NoSQL database.
+
+B<Synopsis:>
+
+ <Plugin "write_mongodb">
+   <Node "default">
+     Host "localhost"
+     Port "27017"
+     Timeout 1000
+     StoreRates true
+   </Node>
+ </Plugin>
+
+The plugin can send values to multiple instances of I<MongoDB> by specifying
+one B<Node> block for each instance. Within the B<Node> blocks, the following
+options are available:
+
+=over 4
+
+=item B<Host> I<Address>
+
+Hostname or address to connect to. Defaults to C<localhost>.
+
+=item B<Port> I<Service>
+
+Service name or port number to connect to. Defaults to C<27017>.
+
+=item B<Timeout> I<Timeout>
+
+Set the timeout for each operation on I<MongoDB> to I<Timeout> milliseconds.
+Setting this option to zero means no timeout, which is the default.
+
+=item B<StoreRates> B<false>|B<true>
+
+If set to B<true> (the default), convert counter values to rates. If set to
+B<false> counter values are stored as is, i.e. as an increasing integer
+number.
+
+=back
+
 =head2 Plugin C<write_http>
 
 This output plugin submits values to an http server by POST them using the
@@ -4826,6 +5281,170 @@ number.
 
 =back
 
+=head1 THRESHOLD CONFIGURATION
+
+Starting with version C<4.3.0> collectd has support for B<monitoring>. By that
+we mean that the values are not only stored or sent somewhere, but that they
+are judged and, if a problem is recognized, acted upon. The only action
+collectd takes itself is to generate and dispatch a "notification". Plugins can
+register to receive notifications and perform appropriate further actions.
+
+Since systems and what you expect them to do differ a lot, you can configure
+B<thresholds> for your values freely. This gives you a lot of flexibility but
+also a lot of responsibility.
+
+Every time a value is out of range a notification is dispatched. This means
+that the idle percentage of your CPU needs to be less then the configured
+threshold only once for a notification to be generated. There's no such thing
+as a moving average or similar - at least not now.
+
+Also, all values that match a threshold are considered to be relevant or
+"interesting". As a consequence collectd will issue a notification if they are
+not received for B<Timeout> iterations. The B<Timeout> configuration option is
+explained in section L<"GLOBAL OPTIONS">. If, for example, B<Timeout> is set to
+"2" (the default) and some hosts sends it's CPU statistics to the server every
+60 seconds, a notification will be dispatched after about 120 seconds. It may
+take a little longer because the timeout is checked only once each B<Interval>
+on the server.
+
+When a value comes within range again or is received after it was missing, an
+"OKAY-notification" is dispatched.
+
+Here is a configuration example to get you started. Read below for more
+information.
+
+ <Threshold>
+   <Type "foo">
+     WarningMin    0.00
+     WarningMax 1000.00
+     FailureMin    0.00
+     FailureMax 1200.00
+     Invert false
+     Instance "bar"
+   </Type>
+
+   <Plugin "interface">
+     Instance "eth0"
+     <Type "if_octets">
+       FailureMax 10000000
+       DataSource "rx"
+     </Type>
+   </Plugin>
+
+   <Host "hostname">
+     <Type "cpu">
+       Instance "idle"
+       FailureMin 10
+     </Type>
+
+     <Plugin "memory">
+       <Type "memory">
+         Instance "cached"
+         WarningMin 100000000
+       </Type>
+     </Plugin>
+   </Host>
+ </Threshold>
+
+There are basically two types of configuration statements: The C<Host>,
+C<Plugin>, and C<Type> blocks select the value for which a threshold should be
+configured. The C<Plugin> and C<Type> blocks may be specified further using the
+C<Instance> option. You can combine the block by nesting the blocks, though
+they must be nested in the above order, i.E<nbsp>e. C<Host> may contain either
+C<Plugin> and C<Type> blocks, C<Plugin> may only contain C<Type> blocks and
+C<Type> may not contain other blocks. If multiple blocks apply to the same
+value the most specific block is used.
+
+The other statements specify the threshold to configure. They B<must> be
+included in a C<Type> block. Currently the following statements are recognized:
+
+=over 4
+
+=item B<FailureMax> I<Value>
+
+=item B<WarningMax> I<Value>
+
+Sets the upper bound of acceptable values. If unset defaults to positive
+infinity. If a value is greater than B<FailureMax> a B<FAILURE> notification
+will be created. If the value is greater than B<WarningMax> but less than (or
+equal to) B<FailureMax> a B<WARNING> notification will be created.
+
+=item B<FailureMin> I<Value>
+
+=item B<WarningMin> I<Value>
+
+Sets the lower bound of acceptable values. If unset defaults to negative
+infinity. If a value is less than B<FailureMin> a B<FAILURE> notification will
+be created. If the value is less than B<WarningMin> but greater than (or equal
+to) B<FailureMin> a B<WARNING> notification will be created.
+
+=item B<DataSource> I<DSName>
+
+Some data sets have more than one "data source". Interesting examples are the
+C<if_octets> data set, which has received (C<rx>) and sent (C<tx>) bytes and
+the C<disk_ops> data set, which holds C<read> and C<write> operations. The
+system load data set, C<load>, even has three data sources: C<shortterm>,
+C<midterm>, and C<longterm>.
+
+Normally, all data sources are checked against a configured threshold. If this
+is undesirable, or if you want to specify different limits for each data
+source, you can use the B<DataSource> option to have a threshold apply only to
+one data source.
+
+=item B<Invert> B<true>|B<false>
+
+If set to B<true> the range of acceptable values is inverted, i.E<nbsp>e.
+values between B<FailureMin> and B<FailureMax> (B<WarningMin> and
+B<WarningMax>) are not okay. Defaults to B<false>.
+
+=item B<Persist> B<true>|B<false>
+
+Sets how often notifications are generated. If set to B<true> one notification
+will be generated for each value that is out of the acceptable range. If set to
+B<false> (the default) then a notification is only generated if a value is out
+of range but the previous value was okay.
+
+This applies to missing values, too: If set to B<true> a notification about a
+missing value is generated once every B<Interval> seconds. If set to B<false>
+only one such notification is generated until the value appears again.
+
+=item B<Percentage> B<true>|B<false>
+
+If set to B<true>, the minimum and maximum values given are interpreted as
+percentage value, relative to the other data sources. This is helpful for
+example for the "df" type, where you may want to issue a warning when less than
+5E<nbsp>% of the total space is available. Defaults to B<false>.
+
+=item B<Hits> I<Number>
+
+Delay creating the notification until the threshold has been passed I<Number>
+times. When a notification has been generated, or when a subsequent value is
+inside the threshold, the counter is reset. If, for example, a value is
+collected once every 10E<nbsp>seconds and B<Hits> is set to 3, a notification
+will be dispatched at most once every 30E<nbsp>seconds.
+
+This is useful when short bursts are not a problem. If, for example, 100% CPU
+usage for up to a minute is normal (and data is collected every
+10E<nbsp>seconds), you could set B<Hits> to B<6> to account for this.
+
+=item B<Hysteresis> I<Number>
+
+When set to non-zero, a hysteresis value is applied when checking minimum and
+maximum bounds. This is useful for values that increase slowly and fluctuate a
+bit while doing so. When these values come close to the threshold, they may
+"flap", i.e. switch between failure / warning case and okay case repeatedly.
+
+If, for example, the threshold is configures as
+
+  WarningMax 100.0
+  Hysteresis 1.0
+
+then a I<Warning> notification is created when the value exceeds I<101> and the
+corresponding I<Okay> notification is only created once the value falls below
+I<99>, thus avoiding the "flapping".
+
+=back
+
 =head1 FILTER CONFIGURATION
 
 Starting with collectd 4.6 there is a powerful filtering infrastructure
@@ -5451,7 +6070,7 @@ convert counter values to rates.
 
 Please note that these placeholders are B<case sensitive>!
 
-=item B<Severity> B<"FATAL">|B<"WARNING">|B<"OKAY">
+=item B<Severity> B<"FAILURE">|B<"WARNING">|B<"OKAY">
 
 Sets the severity of the message. If omitted, the severity B<"WARNING"> is
 used.
index 3bd8f04..61b7fc5 100644 (file)
@@ -485,7 +485,7 @@ static int putval (lcc_connection_t *c, int argc, char **argv)
       while (value != 0) {
         char *dot, *endptr;
 
-        tmp = strchr (argv[i], (int)':');
+        tmp = strchr (value, (int)':');
 
         if (tmp != NULL) {
           *tmp = '\0';
index 0069a8b..b679bf7 100644 (file)
@@ -548,7 +548,7 @@ int check_create_dir (const char *file_orig)
                        {
                                if (errno == ENOENT)
                                {
-                                       if (mkdir (dir, 0755) == 0)
+                                       if (mkdir (dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
                                                break;
 
                                        /* this might happen, if a different thread created
@@ -636,24 +636,23 @@ long long get_kstat_value (kstat_t *ksp, char *name)
        kstat_named_t *kn;
        long long retval = -1LL;
 
-#ifdef assert
-       assert (ksp != NULL);
-       assert (ksp->ks_type == KSTAT_TYPE_NAMED);
-#else
        if (ksp == NULL)
        {
-               ERROR ("ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
+               ERROR ("get_kstat_value (\"%s\"): ksp is NULL.", name);
                return (-1LL);
        }
        else if (ksp->ks_type != KSTAT_TYPE_NAMED)
        {
-               ERROR ("ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
+               ERROR ("get_kstat_value (\"%s\"): ksp->ks_type (%#x) "
+                               "is not KSTAT_TYPE_NAMED (%#x).",
+                               name,
+                               (unsigned int) ksp->ks_type,
+                               (unsigned int) KSTAT_TYPE_NAMED);
                return (-1LL);
        }
-#endif
 
        if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
-               return (retval);
+               return (-1LL);
 
        if (kn->data_type == KSTAT_DATA_INT32)
                retval = (long long) kn->value.i32;
@@ -953,9 +952,25 @@ int parse_identifier_vl (const char *str, value_list_t *vl) /* {{{ */
        return (0);
 } /* }}} int parse_identifier_vl */
 
-int parse_value (const char *value, value_t *ret_value, int ds_type)
+int parse_value (const char *value_orig, value_t *ret_value, int ds_type)
 {
+  char *value;
   char *endptr = NULL;
+  size_t value_len;
+
+  if (value_orig == NULL)
+    return (EINVAL);
+
+  value = strdup (value_orig);
+  if (value == NULL)
+    return (ENOMEM);
+  value_len = strlen (value);
+
+  while ((value_len > 0) && isspace ((int) value[value_len - 1]))
+  {
+    value[value_len - 1] = 0;
+    value_len--;
+  }
 
   switch (ds_type)
   {
@@ -976,11 +991,13 @@ int parse_value (const char *value, value_t *ret_value, int ds_type)
       break;
 
     default:
+      sfree (value);
       ERROR ("parse_value: Invalid data source type: %i.", ds_type);
       return -1;
   }
 
   if (value == endptr) {
+    sfree (value);
     ERROR ("parse_value: Failed to parse string as %s: %s.",
         DS_TYPE_TO_STRING (ds_type), value);
     return -1;
@@ -988,8 +1005,9 @@ int parse_value (const char *value, value_t *ret_value, int ds_type)
   else if ((NULL != endptr) && ('\0' != *endptr))
     INFO ("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
         "Input string was \"%s\".",
-        endptr, DS_TYPE_TO_STRING (ds_type), value);
+        endptr, DS_TYPE_TO_STRING (ds_type), value_orig);
 
+  sfree (value);
   return 0;
 } /* int parse_value */
 
@@ -1211,7 +1229,102 @@ counter_t counter_diff (counter_t old_value, counter_t new_value)
        }
 
        return (diff);
-} /* counter_t counter_to_gauge */
+} /* counter_t counter_diff */
+
+int rate_to_value (value_t *ret_value, gauge_t rate, /* {{{ */
+               rate_to_value_state_t *state,
+               int ds_type, cdtime_t t)
+{
+       gauge_t delta_gauge;
+       cdtime_t delta_t;
+
+       if (ds_type == DS_TYPE_GAUGE)
+       {
+               state->last_value.gauge = rate;
+               state->last_time = t;
+
+               *ret_value = state->last_value;
+               return (0);
+       }
+
+       /* Counter and absolute can't handle negative rates. Reset "last time"
+        * to zero, so that the next valid rate will re-initialize the
+        * structure. */
+       if ((rate < 0.0)
+                       && ((ds_type == DS_TYPE_COUNTER)
+                               || (ds_type == DS_TYPE_ABSOLUTE)))
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       /* Another invalid state: The time is not increasing. */
+       if (t <= state->last_time)
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       delta_t = t - state->last_time;
+       delta_gauge = (rate * CDTIME_T_TO_DOUBLE (delta_t)) + state->residual;
+
+       /* Previous value is invalid. */
+       if (state->last_time == 0) /* {{{ */
+       {
+               if (ds_type == DS_TYPE_DERIVE)
+               {
+                       state->last_value.derive = (derive_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.derive);
+               }
+               else if (ds_type == DS_TYPE_COUNTER)
+               {
+                       state->last_value.counter = (counter_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.counter);
+               }
+               else if (ds_type == DS_TYPE_ABSOLUTE)
+               {
+                       state->last_value.absolute = (absolute_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.absolute);
+               }
+               else
+               {
+                       assert (23 == 42);
+               }
+
+               state->last_time = t;
+               return (EAGAIN);
+       } /* }}} */
+
+       if (ds_type == DS_TYPE_DERIVE)
+       {
+               derive_t delta_derive = (derive_t) delta_gauge;
+
+               state->last_value.derive += delta_derive;
+               state->residual = delta_gauge - ((gauge_t) delta_derive);
+       }
+       else if (ds_type == DS_TYPE_COUNTER)
+       {
+               counter_t delta_counter = (counter_t) delta_gauge;
+
+               state->last_value.counter += delta_counter;
+               state->residual = delta_gauge - ((gauge_t) delta_counter);
+       }
+       else if (ds_type == DS_TYPE_ABSOLUTE)
+       {
+               absolute_t delta_absolute = (absolute_t) delta_gauge;
+
+               state->last_value.absolute = delta_absolute;
+               state->residual = delta_gauge - ((gauge_t) delta_absolute);
+       }
+       else
+       {
+               assert (23 == 42);
+       }
+
+        state->last_time = t;
+       *ret_value = state->last_value;
+       return (0);
+} /* }}} value_t rate_to_value */
 
 int service_name_to_port_number (const char *service_name)
 {
index e6b899d..8a7d986 100644 (file)
                || (strcasecmp ("no", (s)) == 0) \
                || (strcasecmp ("off", (s)) == 0))
 
+struct rate_to_value_state_s
+{
+  value_t last_value;
+  cdtime_t last_time;
+  gauge_t residual;
+};
+typedef struct rate_to_value_state_s rate_to_value_state_t;
+
 char *sstrncpy (char *dest, const char *src, size_t n);
 int ssnprintf (char *dest, size_t n, const char *format, ...);
 char *sstrdup(const char *s);
@@ -287,10 +295,20 @@ typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename,
                void *user_data);
 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);
 
 counter_t counter_diff (counter_t old_value, counter_t new_value);
 
+/* Convert a rate back to a value_t. When converting to a derive_t, counter_t
+ * or absoltue_t, take fractional residuals into account. This is important
+ * when scaling counters, for example.
+ * Returns zero on success. Returns EAGAIN when called for the first time; in
+ * this case the value_t is invalid and the next call should succeed. Other
+ * return values indicate an error. */
+int rate_to_value (value_t *ret_value, gauge_t rate,
+               rate_to_value_state_t *state, int ds_type, cdtime_t t);
+
 /* Converts a service name (a string) to a port number
  * (in the range [1-65535]). Returns less than zero on error. */
 int service_name_to_port_number (const char *service_name);
index e22daf3..5920c53 100644 (file)
@@ -1177,6 +1177,54 @@ int cf_util_get_port_number (const oconfig_item_t *ci) /* {{{ */
        return (tmp);
 } /* }}} int cf_util_get_port_number */
 
+int cf_util_get_service (const oconfig_item_t *ci, char **ret_string) /* {{{ */
+{
+       int port;
+       char *service;
+       int status;
+
+       if (ci->values_num != 1)
+       {
+               ERROR ("cf_util_get_service: The %s option requires exactly "
+                               "one argument.", ci->key);
+               return (-1);
+       }
+
+       if (ci->values[0].type == OCONFIG_TYPE_STRING)
+               return (cf_util_get_string (ci, ret_string));
+       if (ci->values[0].type != OCONFIG_TYPE_NUMBER)
+       {
+               ERROR ("cf_util_get_service: The %s option requires "
+                               "exactly one string or numeric argument.",
+                               ci->key);
+       }
+
+       port = 0;
+       status = cf_util_get_int (ci, &port);
+       if (status != 0)
+               return (status);
+       else if ((port < 1) || (port > 65535))
+       {
+               ERROR ("cf_util_get_service: The port number given "
+                               "for the %s option is out of "
+                               "range (%i).", ci->key, port);
+               return (-1);
+       }
+
+       service = malloc (6);
+       if (service == NULL)
+       {
+               ERROR ("cf_util_get_service: Out of memory.");
+               return (-1);
+       }
+       ssnprintf (service, 6, "%i", port);
+
+       sfree (*ret_string);
+       *ret_string = service;
+
+       return (0);
+} /* }}} int cf_util_get_service */
+
 int cf_util_get_cdtime (const oconfig_item_t *ci, cdtime_t *ret_value) /* {{{ */
 {
        if ((ci == NULL) || (ret_value == NULL))
index b44a738..5a719a4 100644 (file)
@@ -121,6 +121,12 @@ int cf_util_get_flag (const oconfig_item_t *ci,
  * failure. */
 int cf_util_get_port_number (const oconfig_item_t *ci);
 
+/* Assures that the config option is either a service name (a string) or a port
+ * number (an integer in the appropriate range) and returns a newly allocated
+ * string. If ret_string points to a non-NULL pointer, it is freed before
+ * assigning a new value. */
+int cf_util_get_service (const oconfig_item_t *ci, char **ret_string);
+
 int cf_util_get_cdtime (const oconfig_item_t *ci, cdtime_t *ret_value);
 
 #endif /* defined(CONFIGFILE_H) */
index e70ff5f..33236c4 100644 (file)
 
 #define CONNTRACK_FILE "/proc/sys/net/netfilter/nf_conntrack_count"
 
-static void conntrack_submit (double conntrack)
+static void conntrack_submit (value_t conntrack)
 {
-       value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].gauge = conntrack;
-
-       vl.values = values;
+       vl.values = &conntrack;
        vl.values_len = 1;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "conntrack", sizeof (vl.plugin));
@@ -49,14 +46,16 @@ static void conntrack_submit (double conntrack)
 
 static int conntrack_read (void)
 {
-       double conntrack;
+       value_t conntrack;
        FILE *fh;
        char buffer[64];
+       size_t buffer_len;
 
        fh = fopen (CONNTRACK_FILE, "r");
        if (fh == NULL)
                return (-1);
 
+       memset (buffer, 0, sizeof (buffer));
        if (fgets (buffer, sizeof (buffer), fh) == NULL)
        {
                fclose (fh);
@@ -64,10 +63,18 @@ static int conntrack_read (void)
        }
        fclose (fh);
 
-       conntrack = atof (buffer);
+       /* strip trailing newline. */
+       buffer_len = strlen (buffer);
+       while ((buffer_len > 0) && isspace ((int) buffer[buffer_len - 1]))
+       {
+               buffer[buffer_len - 1] = 0;
+               buffer_len--;
+       }
+
+       if (parse_value (buffer, &conntrack, DS_TYPE_GAUGE) != 0)
+               return (-1);
 
-       if (conntrack > 0.0)
-               conntrack_submit (conntrack);
+       conntrack_submit (conntrack);
 
        return (0);
 } /* static int conntrack_read */
index c207318..344f76e 100644 (file)
 /* no global variables */
 /* #endif KERNEL_LINUX */
 
+#elif HAVE_PERFSTAT
+# include <sys/protosw.h>
+# include <libperfstat.h>
+/* #endif HAVE_PERFSTAT */
+
 #else
 # error "No applicable input method."
 #endif
@@ -121,7 +126,24 @@ static int cs_read (void)
 
        if (status == -2)
                ERROR ("contextswitch plugin: Unable to find context switch value.");
-#endif /* KERNEL_LINUX */
+/* #endif  KERNEL_LINUX */
+
+#elif HAVE_PERFSTAT
+       int status = 0;
+       perfstat_cpu_total_t perfcputotal;
+
+       status = perfstat_cpu_total(NULL, &perfcputotal, sizeof(perfstat_cpu_total_t), 1);
+       if (status < 0)
+       {
+               char errbuf[1024];
+               ERROR ("contextswitch plugin: perfstat_cpu_total: %s",
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       cs_submit(perfcputotal.pswitch);
+       status = 0;
+#endif /* defined(HAVE_PERFSTAT) */
 
        return status;
 }
index 052ea1e..46e0d49 100644 (file)
@@ -419,7 +419,8 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
   return (0);
 } /* }}} int cx_handle_instance_xpath */
 
-static int  cx_handle_base_xpath (char *plugin_instance, /* {{{ */
+static int  cx_handle_base_xpath (char const *plugin_instance, /* {{{ */
+    char const *host,
     xmlXPathContextPtr xpath_ctx, const data_set_t *ds, 
     char *base_xpath, cx_xpath_t *xpath)
 {
@@ -461,7 +462,7 @@ static int  cx_handle_base_xpath (char *plugin_instance, /* {{{ */
   vl.values_len = ds->ds_num;
   sstrncpy (vl.type, xpath->type, sizeof (vl.type));
   sstrncpy (vl.plugin, "curl_xml", sizeof (vl.plugin));
-  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.host, (host != NULL) ? host : hostname_g, sizeof (vl.host));
   if (plugin_instance != NULL)
     sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); 
 
@@ -504,7 +505,8 @@ static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
     ds = plugin_get_ds (xpath->type);
 
     if ( (cx_check_type(ds, xpath) == 0) &&
-         (cx_handle_base_xpath(db->instance, xpath_ctx, ds, le->key, xpath) == 0) )
+         (cx_handle_base_xpath(db->instance, db->host,
+                               xpath_ctx, ds, le->key, xpath) == 0) )
       status = 0; /* we got atleast one success */
 
     le = le->next;
index 371a7fc..ded374b 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -228,6 +228,8 @@ static int df_read (void)
                {
                        if (strcmp (mnt_ptr->dir, "/") == 0)
                        {
+                               if (strcmp (mnt_ptr->type, "rootfs") == 0)
+                                       continue;
                                sstrncpy (disk_name, "root", sizeof (disk_name));
                        }
                        else
@@ -252,9 +254,11 @@ static int df_read (void)
                 * report negative free space for user. Notice. blk_reserved
                 * will start to diminish after this. */
 #if HAVE_STATVFS
-               /* Cast is needed to avoid compiler warnings.
+               /* Cast and temporary variable are needed to avoid
+                * compiler warnings.
                 * ((struct statvfs).f_bavail is unsigned (POSIX)) */
-               if (((int64_t) statbuf.f_bavail) < 0)
+               int64_t signed_bavail = (int64_t) statbuf.f_bavail;
+               if (signed_bavail < 0)
                        statbuf.f_bavail = 0;
 #elif HAVE_STATFS
                if (statbuf.f_bavail < 0)
index fde0dcd..3728d55 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/disk.c
- * Copyright (C) 2005-2010  Florian octo Forster
+ * Copyright (C) 2005-2012  Florian octo Forster
  * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -17,7 +17,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Manuel Sanmartin
  **/
 
@@ -75,6 +75,9 @@
 
 #if HAVE_IOKIT_IOKITLIB_H
 static mach_port_t io_master_port = MACH_PORT_NULL;
+/* This defaults to false for backwards compatibility. Please fix in the next
+ * major version. */
+static _Bool use_bsd_name = 0;
 /* #endif HAVE_IOKIT_IOKITLIB_H */
 
 #elif KERNEL_LINUX
@@ -128,6 +131,7 @@ static int pnumdisk;
 static const char *config_keys[] =
 {
        "Disk",
+       "UseBSDName",
        "IgnoreSelected"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
@@ -152,6 +156,15 @@ static int disk_config (const char *key, const char *value)
       invert = 0;
     ignorelist_set_invert (ignorelist, invert);
   }
+  else if (strcasecmp ("UseBSDName", key) == 0)
+  {
+#if HAVE_IOKIT_IOKITLIB_H
+    use_bsd_name = IS_TRUE (value) ? 1 : 0;
+#else
+    WARNING ("disk plugin: The \"UseBSDName\" option is only supported "
+        "on Mach / Mac OS X and will be ignored.");
+#endif
+  }
   else
   {
     return (-1);
@@ -235,6 +248,17 @@ static void disk_submit (const char *plugin_instance,
        plugin_dispatch_values (&vl);
 } /* void disk_submit */
 
+#if KERNEL_LINUX
+static counter_t disk_calc_time_incr (counter_t delta_time, counter_t delta_ops)
+{
+       double interval = CDTIME_T_TO_DOUBLE (plugin_get_interval ());
+       double avg_time = ((double) delta_time) / ((double) delta_ops);
+       double avg_time_incr = interval * avg_time;
+
+       return ((counter_t) (avg_time_incr + .5));
+}
+#endif
+
 #if HAVE_IOKIT_IOKITLIB_H
 static signed long long dict_get_value (CFDictionaryRef dict, const char *key)
 {
@@ -281,7 +305,8 @@ static int disk_read (void)
        CFDictionaryRef         props_dict;
        CFDictionaryRef         stats_dict;
        CFDictionaryRef         child_dict;
-       kern_return_t           status;
+       CFStringRef             tmp_cf_string_ref;
+       kern_return_t           status;
 
        signed long long read_ops;
        signed long long read_byt;
@@ -292,7 +317,8 @@ static int disk_read (void)
 
        int  disk_major;
        int  disk_minor;
-       char disk_name[64];
+       char disk_name[DATA_MAX_NAME_LEN];
+       char disk_name_bsd[DATA_MAX_NAME_LEN];
 
        /* Get the list of all disk objects. */
        if (IOServiceGetMatchingServices (io_master_port,
@@ -340,12 +366,41 @@ static int disk_read (void)
                        continue;
                }
 
+               /* tmp_cf_string_ref doesn't need to be released. */
+               tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (props_dict,
+                               CFSTR(kIOBSDNameKey));
+               if (!tmp_cf_string_ref)
+               {
+                       DEBUG ("disk plugin: CFDictionaryGetValue("
+                                       "kIOBSDNameKey) failed.");
+                       CFRelease (props_dict);
+                       IOObjectRelease (disk_child);
+                       IOObjectRelease (disk);
+                       continue;
+               }
+               assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ());
+
+               memset (disk_name_bsd, 0, sizeof (disk_name_bsd));
+               CFStringGetCString (tmp_cf_string_ref,
+                               disk_name_bsd, sizeof (disk_name_bsd),
+                               kCFStringEncodingUTF8);
+               if (disk_name_bsd[0] == 0)
+               {
+                       ERROR ("disk plugin: CFStringGetCString() failed.");
+                       CFRelease (props_dict);
+                       IOObjectRelease (disk_child);
+                       IOObjectRelease (disk);
+                       continue;
+               }
+               DEBUG ("disk plugin: disk_name_bsd = \"%s\"", disk_name_bsd);
+
                stats_dict = (CFDictionaryRef) CFDictionaryGetValue (props_dict,
                                CFSTR (kIOBlockStorageDriverStatisticsKey));
 
                if (stats_dict == NULL)
                {
-                       DEBUG ("CFDictionaryGetValue (%s) failed.",
+                       DEBUG ("disk plugin: CFDictionaryGetValue ("
+                                       "%s) failed.",
                                        kIOBlockStorageDriverStatisticsKey);
                        CFRelease (props_dict);
                        IOObjectRelease (disk_child);
@@ -359,7 +414,8 @@ static int disk_read (void)
                                        kNilOptions)
                                != kIOReturnSuccess)
                {
-                       DEBUG ("IORegistryEntryCreateCFProperties (disk_child) failed.");
+                       DEBUG ("disk plugin: IORegistryEntryCreateCFProperties ("
+                                       "disk_child) failed.");
                        IOObjectRelease (disk_child);
                        CFRelease (props_dict);
                        IOObjectRelease (disk);
@@ -389,17 +445,12 @@ static int disk_read (void)
                write_tme = dict_get_value (stats_dict,
                                kIOBlockStorageDriverStatisticsTotalWriteTimeKey);
 
-               if (ssnprintf (disk_name, sizeof (disk_name),
-                               "%i-%i", disk_major, disk_minor) >= sizeof (disk_name))
-               {
-                       DEBUG ("snprintf (major, minor) failed.");
-                       CFRelease (child_dict);
-                       IOObjectRelease (disk_child);
-                       CFRelease (props_dict);
-                       IOObjectRelease (disk);
-                       continue;
-               }
-               DEBUG ("disk_name = %s", disk_name);
+               if (use_bsd_name)
+                       sstrncpy (disk_name, disk_name_bsd, sizeof (disk_name));
+               else
+                       ssnprintf (disk_name, sizeof (disk_name), "%i-%i",
+                                       disk_major, disk_minor);
+               DEBUG ("disk plugin: disk_name = \"%s\"", disk_name);
 
                if ((read_byt != -1LL) || (write_byt != -1LL))
                        disk_submit (disk_name, "disk_octets", read_byt, write_byt);
@@ -579,13 +630,11 @@ static int disk_read (void)
                                diff_write_time = write_time - ds->write_time;
 
                        if (diff_read_ops != 0)
-                               ds->avg_read_time += (diff_read_time
-                                               + (diff_read_ops / 2))
-                                       / diff_read_ops;
+                               ds->avg_read_time += disk_calc_time_incr (
+                                               diff_read_time, diff_read_ops);
                        if (diff_write_ops != 0)
-                               ds->avg_write_time += (diff_write_time
-                                               + (diff_write_ops / 2))
-                                       / diff_write_ops;
+                               ds->avg_write_time += disk_calc_time_incr (
+                                               diff_write_time, diff_write_ops);
 
                        ds->read_ops = read_ops;
                        ds->read_time = read_time;
index 787f391..8f633cd 100644 (file)
@@ -376,6 +376,7 @@ static void *collect (void *arg)
        } /* while (1) */
 
        pthread_exit ((void *)0);
+       return ((void *) 0);
 } /* static void *collect (void *) */
 
 static void *open_connection (void __attribute__((unused)) *arg)
@@ -548,7 +549,9 @@ static void *open_connection (void __attribute__((unused)) *arg)
 
                pthread_cond_signal (&conn_available);
        }
-       pthread_exit ((void *)0);
+
+       pthread_exit ((void *) 0);
+       return ((void *) 0);
 } /* static void *open_connection (void *) */
 
 static int email_init (void)
diff --git a/src/ethstat.c b/src/ethstat.c
new file mode 100644 (file)
index 0000000..08381a8
--- /dev/null
@@ -0,0 +1,337 @@
+/**
+ * collectd - src/ethstat.c
+ * Copyright (C) 2011       Cyril Feraudet
+ * Copyright (C) 2012       Florian "octo" Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Cyril Feraudet <cyril at feraudet.com>
+ *   Florian "octo" Forster <octo@collectd.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_avltree.h"
+#include "utils_complain.h"
+
+#if HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+#if HAVE_LINUX_SOCKIOS_H
+# include <linux/sockios.h>
+#endif
+#if HAVE_LINUX_ETHTOOL_H
+# include <linux/ethtool.h>
+#endif
+
+struct value_map_s
+{
+  char type[DATA_MAX_NAME_LEN];
+  char type_instance[DATA_MAX_NAME_LEN];
+};
+typedef struct value_map_s value_map_t;
+
+static char **interfaces = NULL;
+static size_t interfaces_num = 0;
+
+static c_avl_tree_t *value_map = NULL;
+
+static _Bool collect_mapped_only = 0;
+
+static int ethstat_add_interface (const oconfig_item_t *ci) /* {{{ */
+{
+  char **tmp;
+  int status;
+
+  tmp = realloc (interfaces,
+      sizeof (*interfaces) * (interfaces_num + 1));
+  if (tmp == NULL)
+    return (-1);
+  interfaces = tmp;
+  interfaces[interfaces_num] = NULL;
+
+  status = cf_util_get_string (ci, interfaces + interfaces_num);
+  if (status != 0)
+    return (status);
+
+  interfaces_num++;
+  INFO("ethstat plugin: Registred interface %s",
+      interfaces[interfaces_num - 1]);
+
+  return (0);
+} /* }}} int ethstat_add_interface */
+
+static int ethstat_add_map (const oconfig_item_t *ci) /* {{{ */
+{
+  value_map_t *map;
+  int status;
+
+  if ((ci->values_num < 2)
+      || (ci->values_num > 3)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING)
+      || (ci->values[1].type != OCONFIG_TYPE_STRING)
+      || ((ci->values_num == 3)
+        && (ci->values[2].type != OCONFIG_TYPE_STRING)))
+  {
+    ERROR ("ethstat plugin: The %s option requires "
+        "two or three string arguments.", ci->key);
+    return (-1);
+  }
+
+  map = malloc (sizeof (*map));
+  if (map == NULL)
+  {
+    ERROR ("ethstat plugin: malloc(3) failed.");
+    return (ENOMEM);
+  }
+  memset (map, 0, sizeof (*map));
+
+  sstrncpy (map->type, ci->values[1].value.string, sizeof (map->type));
+  if (ci->values_num == 3)
+    sstrncpy (map->type_instance, ci->values[2].value.string,
+        sizeof (map->type_instance));
+
+  if (value_map == NULL)
+  {
+    value_map = c_avl_create ((void *) strcmp);
+    if (value_map == NULL)
+    {
+      sfree (map);
+      ERROR ("ethstat plugin: c_avl_create() failed.");
+      return (-1);
+    }
+  }
+
+  status = c_avl_insert (value_map,
+      /* key = */ ci->values[0].value.string,
+      /* value = */ map);
+  if (status != 0)
+  {
+    sfree (map);
+    if (status > 0)
+      ERROR ("ethstat plugin: Multiple mappings for \"%s\".",
+          ci->values[0].value.string);
+    else
+      ERROR ("ethstat plugin: c_avl_insert(\"%s\") failed.",
+          ci->values[0].value.string);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int ethstat_add_map */
+
+static int ethstat_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Interface", child->key) == 0)
+      ethstat_add_interface (child);
+    else if (strcasecmp ("Map", child->key) == 0)
+      ethstat_add_map (child);
+    else if (strcasecmp ("MappedOnly", child->key) == 0)
+      (void) cf_util_get_boolean (child, &collect_mapped_only);
+    else
+      WARNING ("ethstat plugin: The config option \"%s\" is unknown.",
+          child->key);
+  }
+
+  return (0);
+} /* }}} */
+
+static void ethstat_submit_value (const char *device,
+    const char *type_instance, derive_t value)
+{
+  static c_complain_t complain_no_map = C_COMPLAIN_INIT_STATIC;
+
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+  value_map_t *map = NULL;
+
+  if (value_map != NULL)
+    c_avl_get (value_map, type_instance, (void *) &map);
+
+  /* If the "MappedOnly" option is specified, ignore unmapped values. */
+  if (collect_mapped_only && (map == NULL))
+  {
+    if (value_map == NULL)
+      c_complain (LOG_WARNING, &complain_no_map,
+          "ethstat plugin: The \"MappedOnly\" option has been set to true, "
+          "but no mapping has been configured. All values will be ignored!");
+    return;
+  }
+
+  values[0].derive = value;
+  vl.values = values;
+  vl.values_len = 1;
+
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "ethstat", sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, device, sizeof (vl.plugin_instance));
+  if (map != NULL)
+  {
+    sstrncpy (vl.type, map->type, sizeof (vl.type));
+    sstrncpy (vl.type_instance, map->type_instance,
+        sizeof (vl.type_instance));
+  }
+  else
+  {
+    sstrncpy (vl.type, "derive", sizeof (vl.type));
+    sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+  }
+
+  plugin_dispatch_values (&vl);
+}
+
+static int ethstat_read_interface (char *device)
+{
+  int fd;
+  struct ifreq req;
+  struct ethtool_drvinfo drvinfo;
+  struct ethtool_gstrings *strings;
+  struct ethtool_stats *stats;
+  size_t n_stats;
+  size_t strings_size;
+  size_t stats_size;
+  size_t i;
+  int status;
+
+  memset (&req, 0, sizeof (req));
+  sstrncpy(req.ifr_name, device, sizeof (req.ifr_name));
+
+  fd = socket(AF_INET, SOCK_DGRAM, /* protocol = */ 0);
+  if (fd < 0)
+  {
+    char errbuf[1024];
+    ERROR("ethstat plugin: Failed to open control socket: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return 1;
+  }
+
+  memset (&drvinfo, 0, sizeof (drvinfo));
+  drvinfo.cmd = ETHTOOL_GDRVINFO;
+  req.ifr_data = (void *) &drvinfo;
+  status = ioctl (fd, SIOCETHTOOL, &req);
+  if (status < 0)
+  {
+    char errbuf[1024];
+    close (fd);
+    ERROR ("ethstat plugin: Failed to get driver information "
+        "from %s: %s", device,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  n_stats = (size_t) drvinfo.n_stats;
+  if (n_stats < 1)
+  {
+    close (fd);
+    ERROR("ethstat plugin: No stats available for %s", device);
+    return (-1);
+  }
+
+  strings_size = sizeof (struct ethtool_gstrings)
+    + (n_stats * ETH_GSTRING_LEN);
+  stats_size = sizeof (struct ethtool_stats)
+    + (n_stats * sizeof (uint64_t));
+
+  strings = malloc (strings_size);
+  stats = malloc (stats_size);
+  if ((strings == NULL) || (stats == NULL))
+  {
+    close (fd);
+    sfree (strings);
+    sfree (stats);
+    ERROR("ethstat plugin: malloc(3) failed.");
+    return (-1);
+  }
+
+  strings->cmd = ETHTOOL_GSTRINGS;
+  strings->string_set = ETH_SS_STATS;
+  strings->len = n_stats;
+  req.ifr_data = (void *) strings;
+  status = ioctl (fd, SIOCETHTOOL, &req);
+  if (status < 0)
+  {
+    char errbuf[1024];
+    close (fd);
+    free (strings);
+    free (stats);
+    ERROR ("ethstat plugin: Cannot get strings from %s: %s",
+        device,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  stats->cmd = ETHTOOL_GSTATS;
+  stats->n_stats = n_stats;
+  req.ifr_data = (void *) stats;
+  status = ioctl (fd, SIOCETHTOOL, &req);
+  if (status < 0)
+  {
+    char errbuf[1024];
+    close (fd);
+    free(strings);
+    free(stats);
+    ERROR("ethstat plugin: Reading statistics from %s failed: %s",
+        device,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  for (i = 0; i < n_stats; i++)
+  {
+    const char *stat_name;
+
+    stat_name = (void *) &strings->data[i * ETH_GSTRING_LEN];
+    DEBUG("ethstat plugin: device = \"%s\": %s = %"PRIu64,
+        device, stat_name, (uint64_t) stats->data[i]);
+    ethstat_submit_value (device,
+        stat_name, (derive_t) stats->data[i]);
+  }
+
+  close (fd);
+  sfree (strings);
+  sfree (stats);
+
+  return (0);
+} /* }}} ethstat_read_interface */
+
+static int ethstat_read(void)
+{
+  size_t i;
+
+  for (i = 0; i < interfaces_num; i++)
+    ethstat_read_interface (interfaces[i]);
+
+  return 0;
+}
+
+void module_register (void)
+{
+  plugin_register_complex_config ("ethstat", ethstat_config);
+  plugin_register_read ("ethstat", ethstat_read);
+}
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
index da6e367..14d288f 100644 (file)
@@ -93,7 +93,7 @@ static void sigchld_handler (int __attribute__((unused)) signal) /* {{{ */
     program_list_t *pl;
     for (pl = pl_head; pl != NULL; pl = pl->next)
       if (pl->pid == pid)
-       break;
+        break;
     if (pl != NULL)
       pl->status = status;
   } /* while (waitpid) */
@@ -108,20 +108,20 @@ static int exec_config_exec (oconfig_item_t *ci) /* {{{ */
   if (ci->children_num != 0)
   {
     WARNING ("exec plugin: The config option `%s' may not be a block.",
-       ci->key);
+        ci->key);
     return (-1);
   }
   if (ci->values_num < 2)
   {
     WARNING ("exec plugin: The config option `%s' needs at least two "
-       "arguments.", ci->key);
+        "arguments.", ci->key);
     return (-1);
   }
   if ((ci->values[0].type != OCONFIG_TYPE_STRING)
       || (ci->values[1].type != OCONFIG_TYPE_STRING))
   {
     WARNING ("exec plugin: The first two arguments to the `%s' option must "
-       "be string arguments.", ci->key);
+        "be string arguments.", ci->key);
     return (-1);
   }
 
@@ -201,15 +201,15 @@ static int exec_config_exec (oconfig_item_t *ci) /* {{{ */
     {
       if (ci->values[i + 1].type == OCONFIG_TYPE_NUMBER)
       {
-       ssnprintf (buffer, sizeof (buffer), "%lf",
-           ci->values[i + 1].value.number);
+        ssnprintf (buffer, sizeof (buffer), "%lf",
+            ci->values[i + 1].value.number);
       }
       else
       {
-       if (ci->values[i + 1].value.boolean)
-         sstrncpy (buffer, "true", sizeof (buffer));
-       else
-         sstrncpy (buffer, "false", sizeof (buffer));
+        if (ci->values[i + 1].value.boolean)
+          sstrncpy (buffer, "true", sizeof (buffer));
+        else
+          sstrncpy (buffer, "false", sizeof (buffer));
       }
 
       pl->argv[i] = strdup (buffer);
@@ -254,7 +254,7 @@ static int exec_config (oconfig_item_t *ci) /* {{{ */
   {
     oconfig_item_t *child = ci->children + i;
     if ((strcasecmp ("Exec", child->key) == 0)
-       || (strcasecmp ("NotificationExec", child->key) == 0))
+        || (strcasecmp ("NotificationExec", child->key) == 0))
       exec_config_exec (child);
     else
     {
@@ -304,7 +304,7 @@ static void exec_child (program_list_t *pl) /* {{{ */
   if (status != 0)
   {
     ERROR ("exec plugin: Failed to get user information for user ``%s'': %s",
-       pl->user, sstrerror (errno, errbuf, sizeof (errbuf)));
+        pl->user, sstrerror (errno, errbuf, sizeof (errbuf)));
     exit (-1);
   }
   if (sp_ptr == NULL)
@@ -333,15 +333,15 @@ static void exec_child (program_list_t *pl) /* {{{ */
       status = getgrnam_r (pl->group, &gr, nambuf, sizeof (nambuf), &gr_ptr);
       if (0 != status)
       {
-       ERROR ("exec plugin: Failed to get group information "
-           "for group ``%s'': %s", pl->group,
-           sstrerror (errno, errbuf, sizeof (errbuf)));
-       exit (-1);
+        ERROR ("exec plugin: Failed to get group information "
+            "for group ``%s'': %s", pl->group,
+            sstrerror (errno, errbuf, sizeof (errbuf)));
+        exit (-1);
       }
       if (NULL == gr_ptr)
       {
-       ERROR ("exec plugin: No such group: `%s'", pl->group);
-       exit (-1);
+        ERROR ("exec plugin: No such group: `%s'", pl->group);
+        exit (-1);
       }
 
       egid = gr.gr_gid;
@@ -375,7 +375,7 @@ static void exec_child (program_list_t *pl) /* {{{ */
   if (status != 0)
   {
     ERROR ("exec plugin: setgid (%i) failed: %s",
-       gid, sstrerror (errno, errbuf, sizeof (errbuf)));
+        gid, sstrerror (errno, errbuf, sizeof (errbuf)));
     exit (-1);
   }
 
@@ -385,7 +385,7 @@ static void exec_child (program_list_t *pl) /* {{{ */
     if (status != 0)
     {
       ERROR ("exec plugin: setegid (%i) failed: %s",
-         egid, sstrerror (errno, errbuf, sizeof (errbuf)));
+          egid, sstrerror (errno, errbuf, sizeof (errbuf)));
       exit (-1);
     }
   }
@@ -394,7 +394,7 @@ static void exec_child (program_list_t *pl) /* {{{ */
   if (status != 0)
   {
     ERROR ("exec plugin: setuid (%i) failed: %s",
-       uid, sstrerror (errno, errbuf, sizeof (errbuf)));
+        uid, sstrerror (errno, errbuf, sizeof (errbuf)));
     exit (-1);
   }
 
@@ -436,7 +436,7 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
   if (status != 0)
   {
     ERROR ("exec plugin: pipe failed: %s",
-       sstrerror (errno, errbuf, sizeof (errbuf)));
+        sstrerror (errno, errbuf, sizeof (errbuf)));
     return (-1);
   }
 
@@ -444,7 +444,7 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
   if (status != 0)
   {
     ERROR ("exec plugin: pipe failed: %s",
-       sstrerror (errno, errbuf, sizeof (errbuf)));
+        sstrerror (errno, errbuf, sizeof (errbuf)));
     return (-1);
   }
 
@@ -452,7 +452,7 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
   if (status != 0)
   {
     ERROR ("exec plugin: pipe failed: %s",
-       sstrerror (errno, errbuf, sizeof (errbuf)));
+        sstrerror (errno, errbuf, sizeof (errbuf)));
     return (-1);
   }
 
@@ -460,7 +460,7 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
   if (pid < 0)
   {
     ERROR ("exec plugin: fork failed: %s",
-       sstrerror (errno, errbuf, sizeof (errbuf)));
+        sstrerror (errno, errbuf, sizeof (errbuf)));
     return (-1);
   }
   else if (pid == 0)
@@ -473,9 +473,9 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
     for (fd = 0; fd < fd_num; fd++)
     {
       if ((fd == fd_pipe_in[0])
-         || (fd == fd_pipe_out[1])
-         || (fd == fd_pipe_err[1]))
-       continue;
+          || (fd == fd_pipe_out[1])
+          || (fd == fd_pipe_err[1]))
+        continue;
       close (fd);
     }
 
@@ -579,10 +579,18 @@ static void *exec_read_one (void *arg) /* {{{ */
   /* We use a copy of fdset, as select modifies it */
   copy = fdset;
 
-  while (select(highest_fd + 1, &copy, NULL, NULL, NULL ) > 0)
+  while (1)
   {
     int len;
 
+    status = select (highest_fd + 1, &copy, NULL, NULL, NULL);
+    if (status < 0)
+    {
+      if (errno == EINTR)
+        continue;
+      break;
+    }
+
     if (FD_ISSET(fd, &copy))
     {
       char *pnl;
@@ -628,19 +636,24 @@ static void *exec_read_one (void *arg) /* {{{ */
 
       if (len < 0)
       {
-        if (errno == EAGAIN || errno == EINTR)  continue;
+        if (errno == EAGAIN || errno == EINTR)
+          continue;
         break;
       }
       else if (len == 0)
       {
-       /* We've reached EOF */
-       NOTICE ("exec plugin: Program `%s' has closed STDERR.",
-           pl->exec);
-       close (fd_err);
-       FD_CLR (fd_err, &fdset);
-       highest_fd = fd;
-       fd_err = -1;
-       continue;
+        /* We've reached EOF */
+        NOTICE ("exec plugin: Program `%s' has closed STDERR.", pl->exec);
+
+        /* Remove file descriptor form select() set. */
+        FD_CLR (fd_err, &fdset);
+        copy = fdset;
+        highest_fd = fd;
+
+        /* Clean up file descriptor */
+        close (fd_err);
+        fd_err = -1;
+        continue;
       }
 
       pbuffer_err[len] = '\0';
@@ -714,7 +727,7 @@ static void *exec_notification_one (void *arg) /* {{{ */
   {
     char errbuf[1024];
     ERROR ("exec plugin: fdopen (%i) failed: %s", fd,
-       sstrerror (errno, errbuf, sizeof (errbuf)));
+        sstrerror (errno, errbuf, sizeof (errbuf)));
     kill (pl->pid, SIGTERM);
     pl->pid = 0;
     close (fd);
@@ -757,7 +770,7 @@ static void *exec_notification_one (void *arg) /* {{{ */
       fprintf (fh, "%s: %e\n", meta->name, meta->nm_value.nm_double);
     else if (meta->type == NM_TYPE_BOOLEAN)
       fprintf (fh, "%s: %s\n", meta->name,
-         meta->nm_value.nm_boolean ? "true" : "false");
+          meta->nm_value.nm_boolean ? "true" : "false");
   }
 
   fprintf (fh, "\n%s\n", n->message);
@@ -815,6 +828,7 @@ static int exec_read (void) /* {{{ */
     pthread_attr_init (&attr);
     pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
     plugin_thread_create (&t, &attr, exec_read_one, (void *) pl);
+    pthread_attr_destroy (&attr);
   } /* for (pl) */
 
   return (0);
@@ -840,7 +854,7 @@ static int exec_notification (const notification_t *n, /* {{{ */
       continue;
 
     pln = (program_list_and_notification_t *) malloc (sizeof
-       (program_list_and_notification_t));
+        (program_list_and_notification_t));
     if (pln == NULL)
     {
       ERROR ("exec plugin: malloc failed.");
@@ -858,6 +872,7 @@ static int exec_notification (const notification_t *n, /* {{{ */
     pthread_attr_init (&attr);
     pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
     plugin_thread_create (&t, &attr, exec_notification_one, (void *) pln);
+    pthread_attr_destroy (&attr);
   } /* for (pl) */
 
   return (0);
index 9501161..ea82039 100644 (file)
@@ -141,17 +141,11 @@ static int interface_init (void)
        {
                if (strncmp (ksp_chain->ks_class, "net", 3))
                        continue;
-               /* Ignore kstat entry if not the regular statistic set. This
-                * avoids problems with "bogus" interfaces, such as
-                * "wrsmd<num>" */
-               if (strncmp (ksp_chain->ks_name, ksp_chain->ks_module,
-                                       strlen (ksp_chain->ks_module)) != 0)
-                       continue;
                if (ksp_chain->ks_type != KSTAT_TYPE_NAMED)
                        continue;
                if (kstat_read (kc, ksp_chain, NULL) == -1)
                        continue;
-               if ((val = get_kstat_value (ksp_chain, "ifspeed")) == -1LL)
+               if ((val = get_kstat_value (ksp_chain, "obytes")) == -1LL)
                        continue;
                ksp[numif++] = ksp_chain;
        }
index d13decd..2adf6d4 100644 (file)
@@ -780,6 +780,8 @@ int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */
   if (ret_values_names != NULL)
     *ret_values_names = values_names;
 
+  lcc_response_free (&res);
+
   return (0);
 } /* }}} int lcc_getval */
 
index 5b7aa94..19f58b2 100644 (file)
@@ -148,6 +148,19 @@ block:
         $$.children = $2.statement;
         $$.children_num = $2.statement_num;
        }
+       | block_begin block_end
+       {
+        if (strcmp ($1.key, $2) != 0)
+        {
+               printf ("block_begin = %s; block_end = %s;\n", $1.key, $2);
+               yyerror ("Block not closed..\n");
+               exit (1);
+        }
+        free ($2); $2 = NULL;
+        $$ = $1;
+        $$.children = NULL;
+        $$.children_num = 0;
+       }
        ;
 
 statement:
@@ -191,6 +204,13 @@ entire_file:
         ci_root->children = $1.statement;
         ci_root->children_num = $1.statement_num;
        }
+       | /* epsilon */
+       {
+        ci_root = malloc (sizeof (oconfig_item_t));
+        memset (ci_root, '\0', sizeof (oconfig_item_t));
+        ci_root->children = NULL;
+        ci_root->children_num = 0;
+       }
        ;
 
 %%
index 60fb5d9..ded7732 100644 (file)
@@ -53,23 +53,8 @@ static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 static int logfile_config (const char *key, const char *value)
 {
        if (0 == strcasecmp (key, "LogLevel")) {
-               if ((0 == strcasecmp (value, "emerg"))
-                               || (0 == strcasecmp (value, "alert"))
-                               || (0 == strcasecmp (value, "crit"))
-                               || (0 == strcasecmp (value, "err")))
-                       log_level = LOG_ERR;
-               else if (0 == strcasecmp (value, "warning"))
-                       log_level = LOG_WARNING;
-               else if (0 == strcasecmp (value, "notice"))
-                       log_level = LOG_NOTICE;
-               else if (0 == strcasecmp (value, "info"))
-                       log_level = LOG_INFO;
-#if COLLECT_DEBUG
-               else if (0 == strcasecmp (value, "debug"))
-                       log_level = LOG_DEBUG;
-#endif /* COLLECT_DEBUG */
-               else
-                       return 1;
+               log_level = parse_log_severity(value);
+               if (log_level == -1) return 1; /* to keep previous behaviour */
        }
        else if (0 == strcasecmp (key, "File")) {
                sfree (log_file);
diff --git a/src/md.c b/src/md.c
new file mode 100644 (file)
index 0000000..3725f9a
--- /dev/null
+++ b/src/md.c
@@ -0,0 +1,218 @@
+/**
+ * collectd - src/md.c
+ * Copyright (C) 2010,2011  Michael Hanselmann
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Michael Hanselmann
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_ignorelist.h"
+
+#include <sys/ioctl.h>
+
+#include <linux/major.h>
+#include <linux/raid/md_u.h>
+
+#define PROC_DISKSTATS "/proc/diskstats"
+#define DEV_DIR "/dev"
+
+static const char *config_keys[] =
+{
+  "Device",
+  "IgnoreSelected"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *ignorelist = NULL;
+
+static int md_config (const char *key, const char *value)
+{
+  if (ignorelist == NULL)
+    ignorelist = ignorelist_create (/* invert = */ 1);
+  if (ignorelist == NULL)
+    return (1);
+
+  if (strcasecmp (key, "Device") == 0)
+  {
+    ignorelist_add (ignorelist, value);
+  }
+  else if (strcasecmp (key, "IgnoreSelected") == 0)
+  {
+    ignorelist_set_invert (ignorelist, IS_TRUE (value) ? 0 : 1);
+  }
+  else
+  {
+    return (-1);
+  }
+
+  return (0);
+}
+
+static void md_submit (const int minor, const char *type_instance,
+    gauge_t value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = value;
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "md", sizeof (vl.plugin));
+  ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+      "%i", minor);
+  sstrncpy (vl.type, "md_disks", sizeof (vl.type));
+  sstrncpy (vl.type_instance, type_instance,
+      sizeof (vl.type_instance));
+
+  plugin_dispatch_values (&vl);
+} /* void md_submit */
+
+static void md_process (const int minor, const char *path)
+{
+  char errbuf[1024];
+  int fd;
+  struct stat st;
+  mdu_array_info_t array;
+  gauge_t disks_missing;
+
+  fd = open (path, O_RDONLY);
+  if (fd < 0)
+  {
+    WARNING ("md: open(%s): %s", path,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return;
+  }
+
+  if (fstat (fd, &st) < 0)
+  {
+    WARNING ("md: Unable to fstat file descriptor for %s: %s", path,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    close (fd);
+    return;
+  }
+
+  if (! S_ISBLK (st.st_mode))
+  {
+    WARNING ("md: %s is no block device", path);
+    close (fd);
+    return;
+  }
+
+  if (st.st_rdev != makedev (MD_MAJOR, minor))
+  {
+    WARNING ("md: Major/minor of %s are %i:%i, should be %i:%i",
+        path, (int)major(st.st_rdev), (int)minor(st.st_rdev),
+        (int)MD_MAJOR, minor);
+    close (fd);
+    return;
+  }
+
+  /* Retrieve md information */
+  if (ioctl (fd, GET_ARRAY_INFO, &array) < 0) {
+    WARNING ("md: Unable to retrieve array info from %s: %s", path,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    close (fd);
+    return;
+  }
+
+  close (fd);
+
+  /*
+   * The mdu_array_info_t structure contains numbers of disks in the array.
+   * However, disks are accounted for more than once:
+   *
+   * active:  Number of active (in sync) disks.
+   * spare:   Number of stand-by disks.
+   * working: Number of working disks. (active + sync)
+   * failed:  Number of failed disks.
+   * nr:      Number of physically present disks. (working + failed)
+   * raid:    Number of disks in the RAID. This may be larger than "nr" if
+   *          disks are missing and smaller than "nr" when spare disks are
+   *          around.
+   */
+  md_submit (minor, "active",  (gauge_t) array.active_disks);
+  md_submit (minor, "failed",  (gauge_t) array.failed_disks);
+  md_submit (minor, "spare",   (gauge_t) array.spare_disks);
+
+  disks_missing = 0.0;
+  if (array.raid_disks > array.nr_disks)
+    disks_missing = (gauge_t) (array.raid_disks - array.nr_disks);
+  md_submit (minor, "missing", disks_missing);
+} /* void md_process */
+
+static int md_read (void)
+{
+  FILE *fh;
+  char buffer[1024];
+
+  fh = fopen (PROC_DISKSTATS, "r");
+  if (fh == NULL) {
+    char errbuf[1024];
+    WARNING ("md: Unable to open %s: %s",
+        PROC_DISKSTATS ,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  /* Iterate md devices */
+  while (fgets (buffer, sizeof (buffer), fh) != NULL)
+  {
+    char path[PATH_MAX];
+    char *fields[4];
+    char *name;
+    int major, minor;
+
+    /* Extract interesting fields */
+    if (strsplit (buffer, fields, STATIC_ARRAY_SIZE(fields)) < 3)
+      continue;
+
+    major = atoi (fields[0]);
+
+    if (major != MD_MAJOR)
+      continue;
+
+    minor = atoi (fields[1]);
+    name = fields[2];
+
+    if (ignorelist_match (ignorelist, name))
+      continue;
+
+    /* FIXME: Don't hardcode path. Walk /dev collecting major,
+     * minor and name, then use lookup table to find device.
+     * Alternatively create a temporary device file with correct
+     * major/minor, but that again can be tricky if the filesystem
+     * with the device file is mounted using the "nodev" option.
+     */
+    ssnprintf (path, sizeof (path), "%s/%s", DEV_DIR, name);
+
+    md_process (minor, path);
+  }
+
+  fclose (fh);
+
+  return (0);
+} /* int md_read */
+
+void module_register (void)
+{
+  plugin_register_config ("md", md_config, config_keys, config_keys_num);
+  plugin_register_read ("md", md_read);
+} /* void module_register */
index 8f51e22..c57a831 100644 (file)
@@ -328,7 +328,7 @@ static int cmc_config_add_page (oconfig_item_t *ci) /* {{{ */
 
     if (strcasecmp ("Server", child->key) == 0)
       status = cmc_config_add_string ("Server", &page->server, child);
-    if (strcasecmp ("Key", child->key) == 0)
+    else if (strcasecmp ("Key", child->key) == 0)
       status = cmc_config_add_string ("Key", &page->key, child);
     else if (strcasecmp ("Match", child->key) == 0)
       /* Be liberal with failing matches => don't set `status'. */
index eda1b03..a09f45e 100644 (file)
@@ -1,9 +1,10 @@
 /**
  * collectd - src/memcached.c, based on src/hddtemp.c
  * Copyright (C) 2007       Antony Dovgal
- * Copyright (C) 2007-2010  Florian Forster
+ * Copyright (C) 2007-2012  Florian Forster
  * Copyright (C) 2009       Doug MacEachern
  * Copyright (C) 2009       Franck Lombardi
+ * Copyright (C) 2012       Nicolas Szalay
  *
  * 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
@@ -24,6 +25,7 @@
  *   Florian octo Forster <octo at collectd.org>
  *   Doug MacEachern <dougm at hyperic.com>
  *   Franck Lombardi
+ *   Nicolas Szalay
  **/
 
 #include "collectd.h"
 #include "plugin.h"
 #include "configfile.h"
 
-# include <poll.h>
-# include <netdb.h>
-# include <sys/socket.h>
-# include <sys/un.h>
-# include <netinet/in.h>
-# include <netinet/tcp.h>
-
-/* Hack to work around the missing define in AIX */
-#ifndef MSG_DONTWAIT
-# define MSG_DONTWAIT MSG_NONBLOCK
-#endif
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
 
 #define MEMCACHED_DEF_HOST "127.0.0.1"
 #define MEMCACHED_DEF_PORT "11211"
 
-#define MEMCACHED_RETRY_COUNT 100
-
-static const char *config_keys[] =
+struct memcached_s
 {
-       "Socket",
-       "Host",
-       "Port"
+  char *name;
+  char *socket;
+  char *host;
+  char *port;
 };
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+typedef struct memcached_s memcached_t;
 
-static char *memcached_socket = NULL;
-static char *memcached_host = NULL;
-static char memcached_port[16];
+static _Bool memcached_have_instances = 0;
 
-static int memcached_query_daemon (char *buffer, int buffer_size) /* {{{ */
+static void memcached_free (memcached_t *st)
 {
-       int fd;
-       ssize_t status;
-       int buffer_fill;
-       int i = 0;
-
-       if (memcached_socket != NULL) {
-               struct sockaddr_un serv_addr;
-
-               memset (&serv_addr, 0, sizeof (serv_addr));
-               serv_addr.sun_family = AF_UNIX;
-               sstrncpy (serv_addr.sun_path, memcached_socket,
-                               sizeof (serv_addr.sun_path));
-
-               /* create our socket descriptor */
-               fd = socket (AF_UNIX, SOCK_STREAM, 0);
-               if (fd < 0) {
-                       char errbuf[1024];
-                       ERROR ("memcached: unix socket: %s", sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-                       return -1;
-               }
-
-               /* connect to the memcached daemon */
-               status = (ssize_t) connect (fd, (struct sockaddr *) &serv_addr,
-                               sizeof (serv_addr));
-               if (status != 0) {
-                       shutdown (fd, SHUT_RDWR);
-                       close (fd);
-                       fd = -1;
-               }
-       }
-       else { /* if (memcached_socket == NULL) */
-               const char *host;
-               const char *port;
-
-               struct addrinfo  ai_hints;
-               struct addrinfo *ai_list, *ai_ptr;
-               int              ai_return = 0;
-
-               memset (&ai_hints, '\0', sizeof (ai_hints));
-               ai_hints.ai_flags    = 0;
+  if (st == NULL)
+    return;
+
+  sfree (st->name);
+  sfree (st->socket);
+  sfree (st->host);
+  sfree (st->port);
+}
+
+static int memcached_connect_unix (memcached_t *st)
+{
+  struct sockaddr_un serv_addr;
+  int fd;
+
+  memset (&serv_addr, 0, sizeof (serv_addr));
+  serv_addr.sun_family = AF_UNIX;
+  sstrncpy (serv_addr.sun_path, st->socket,
+      sizeof (serv_addr.sun_path));
+
+  /* create our socket descriptor */
+  fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd < 0)
+  {
+    char errbuf[1024];
+    ERROR ("memcached plugin: memcached_connect_unix: socket(2) failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  return (fd);
+} /* int memcached_connect_unix */
+
+static int memcached_connect_inet (memcached_t *st)
+{
+  char *host;
+  char *port;
+
+  struct addrinfo  ai_hints;
+  struct addrinfo *ai_list, *ai_ptr;
+  int status;
+  int fd = -1;
+
+  memset (&ai_hints, 0, sizeof (ai_hints));
+  ai_hints.ai_flags    = 0;
 #ifdef AI_ADDRCONFIG
-               /*      ai_hints.ai_flags   |= AI_ADDRCONFIG; */
+  ai_hints.ai_flags   |= AI_ADDRCONFIG;
 #endif
-               ai_hints.ai_family   = AF_INET;
-               ai_hints.ai_socktype = SOCK_STREAM;
-               ai_hints.ai_protocol = 0;
-
-               host = memcached_host;
-               if (host == NULL) {
-                       host = MEMCACHED_DEF_HOST;
-               }
-
-               port = memcached_port;
-               if (strlen (port) == 0) {
-                       port = MEMCACHED_DEF_PORT;
-               }
-
-               if ((ai_return = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0) {
-                       char errbuf[1024];
-                       ERROR ("memcached: getaddrinfo (%s, %s): %s",
-                                       host, port,
-                                       (ai_return == EAI_SYSTEM)
-                                       ? sstrerror (errno, errbuf, sizeof (errbuf))
-                                       : gai_strerror (ai_return));
-                       return -1;
-               }
-
-               fd = -1;
-               for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
-                       /* create our socket descriptor */
-                       fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
-                       if (fd < 0) {
-                               char errbuf[1024];
-                               ERROR ("memcached: socket: %s", sstrerror (errno, errbuf, sizeof (errbuf)));
-                               continue;
-                       }
-
-                       /* connect to the memcached daemon */
-                       status = (ssize_t) connect (fd, (struct sockaddr *) ai_ptr->ai_addr, ai_ptr->ai_addrlen);
-                       if (status != 0) {
-                               shutdown (fd, SHUT_RDWR);
-                               close (fd);
-                               fd = -1;
-                               continue;
-                       }
-
-                       /* A socket could be opened and connecting succeeded. We're
-                        * done. */
-                       break;
-               }
-
-               freeaddrinfo (ai_list);
-       }
-
-       if (fd < 0) {
-               ERROR ("memcached: Could not connect to daemon.");
-               return -1;
-       }
-
-       if (send(fd, "stats\r\n", sizeof("stats\r\n") - 1, MSG_DONTWAIT) != (sizeof("stats\r\n") - 1)) {
-               ERROR ("memcached: Could not send command to the memcached daemon.");
-               return -1;
-       }
-
-       {
-               struct pollfd p;
-               int status;
-
-               memset (&p, 0, sizeof (p));
-               p.fd = fd;
-               p.events = POLLIN | POLLERR | POLLHUP;
-               p.revents = 0;
-
-               status = poll (&p, /* nfds = */ 1,
-                               /* timeout = */ CDTIME_T_TO_MS (plugin_get_interval ()));
-               if (status <= 0)
-               {
-                       if (status == 0)
-                       {
-                               ERROR ("memcached: poll(2) timed out after %.3f seconds.",
-                                               CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
-                       }
-                       else
-                       {
-                               char errbuf[1024];
-                               ERROR ("memcached: poll(2) failed: %s",
-                                               sstrerror (errno, errbuf, sizeof (errbuf)));
-                       }
-                       shutdown (fd, SHUT_RDWR);
-                       close (fd);
-                       return (-1);
-               }
-       }
-
-       /* receive data from the memcached daemon */
-       memset (buffer, '\0', buffer_size);
-
-       buffer_fill = 0;
-       while ((status = recv (fd, buffer + buffer_fill, buffer_size - buffer_fill, MSG_DONTWAIT)) != 0) {
-               if (i > MEMCACHED_RETRY_COUNT) {
-                       ERROR("recv() timed out");
-                       break;
-               }
-               i++;
-
-               if (status == -1) {
-                       char errbuf[1024];
-
-                       if (errno == EAGAIN) {
-                               continue;
-                       }
-
-                       ERROR ("memcached: Error reading from socket: %s",
-                                       sstrerror (errno, errbuf, sizeof (errbuf)));
-                       shutdown(fd, SHUT_RDWR);
-                       close (fd);
-                       return -1;
-               }
-               buffer_fill += status;
-
-               if (buffer_fill > 3 && buffer[buffer_fill-5] == 'E' && buffer[buffer_fill-4] == 'N' && buffer[buffer_fill-3] == 'D') {
-                       /* we got all the data */
-                       break;
-               }
-       }
-
-       if (buffer_fill >= buffer_size) {
-               buffer[buffer_size - 1] = '\0';
-               WARNING ("memcached: Message from memcached has been truncated.");
-       } else if (buffer_fill == 0) {
-               WARNING ("memcached: Peer has unexpectedly shut down the socket. "
-                               "Buffer: `%s'", buffer);
-               shutdown(fd, SHUT_RDWR);
-               close(fd);
-               return -1;
-       }
-
-       shutdown(fd, SHUT_RDWR);
-       close(fd);
-       return 0;
+  ai_hints.ai_family   = AF_UNSPEC;
+  ai_hints.ai_socktype = SOCK_STREAM;
+  ai_hints.ai_protocol = 0;
+
+  host = (st->host != NULL) ? st->host : MEMCACHED_DEF_HOST;
+  port = (st->port != NULL) ? st->port : MEMCACHED_DEF_PORT;
+
+  ai_list = NULL;
+  status = getaddrinfo (host, port, &ai_hints, &ai_list);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("memcached plugin: memcached_connect_inet: "
+        "getaddrinfo(%s,%s) failed: %s",
+        host, port,
+        (status == EAI_SYSTEM)
+        ? sstrerror (errno, errbuf, sizeof (errbuf))
+        : gai_strerror (status));
+    return (-1);
+  }
+
+  for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    /* create our socket descriptor */
+    fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (fd < 0)
+    {
+      char errbuf[1024];
+      WARNING ("memcached plugin: memcached_connect_inet: "
+          "socket(2) failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      continue;
+    }
+
+    /* connect to the memcached daemon */
+    status = (int) connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    if (status != 0)
+    {
+      shutdown (fd, SHUT_RDWR);
+      close (fd);
+      fd = -1;
+      continue;
+    }
+
+    /* A socket could be opened and connecting succeeded. We're done. */
+    break;
+  }
+
+  freeaddrinfo (ai_list);
+  return (fd);
+} /* int memcached_connect_inet */
+
+static int memcached_connect (memcached_t *st)
+{
+  if (st->socket != NULL)
+    return (memcached_connect_unix (st));
+  else
+    return (memcached_connect_inet (st));
 }
-/* }}} */
 
-static int memcached_config (const char *key, const char *value) /* {{{ */
+static int memcached_query_daemon (char *buffer, size_t buffer_size, memcached_t *st)
 {
-       if (strcasecmp (key, "Socket") == 0) {
-               if (memcached_socket != NULL) {
-                       free (memcached_socket);
-               }
-               memcached_socket = strdup (value);
-       } else if (strcasecmp (key, "Host") == 0) {
-               if (memcached_host != NULL) {
-                       free (memcached_host);
-               }
-               memcached_host = strdup (value);
-       } else if (strcasecmp (key, "Port") == 0) {
-               int port = (int) (atof (value));
-               if ((port > 0) && (port <= 65535)) {
-                       ssnprintf (memcached_port, sizeof (memcached_port), "%i", port);
-               } else {
-                       sstrncpy (memcached_port, value, sizeof (memcached_port));
-               }
-       } else {
-               return -1;
-       }
-
-       return 0;
+  int fd = -1;
+  int status;
+  size_t buffer_fill;
+
+  fd = memcached_connect (st);
+  if (fd < 0) {
+    ERROR ("memcached plugin: Instance \"%s\" could not connect to daemon.",
+        st->name);
+    return -1;
+  }
+
+  status = (int) swrite (fd, "stats\r\n", strlen ("stats\r\n"));
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("memcached plugin: write(2) failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    shutdown(fd, SHUT_RDWR);
+    close (fd);
+    return (-1);
+  }
+
+  /* receive data from the memcached daemon */
+  memset (buffer, 0, buffer_size);
+
+  buffer_fill = 0;
+  while ((status = (int) recv (fd, buffer + buffer_fill,
+          buffer_size - buffer_fill, /* flags = */ 0)) != 0)
+  {
+    char const end_token[5] = {'E', 'N', 'D', '\r', '\n'};
+    if (status < 0)
+    {
+      char errbuf[1024];
+
+      if ((errno == EAGAIN) || (errno == EINTR))
+          continue;
+
+      ERROR ("memcached: Error reading from socket: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      shutdown(fd, SHUT_RDWR);
+      close (fd);
+      return (-1);
+    }
+
+    buffer_fill += (size_t) status;
+    if (buffer_fill > buffer_size)
+    {
+      buffer_fill = buffer_size;
+      WARNING ("memcached plugin: Message was truncated.");
+      break;
+    }
+
+    /* If buffer ends in end_token, we have all the data. */
+    if (memcmp (buffer + buffer_fill - sizeof (end_token),
+          end_token, sizeof (end_token)) == 0)
+      break;
+  } /* while (recv) */
+
+  status = 0;
+  if (buffer_fill == 0)
+  {
+    WARNING ("memcached plugin: No data returned by memcached.");
+    status = -1;
+  }
+
+  shutdown(fd, SHUT_RDWR);
+  close(fd);
+  return (status);
+} /* int memcached_query_daemon */
+
+static void memcached_init_vl (value_list_t *vl, memcached_t const *st)
+{
+  sstrncpy (vl->plugin, "memcached", sizeof (vl->plugin));
+  if (strcmp (st->name, "__legacy__") == 0) /* legacy mode */
+  {
+    sstrncpy (vl->host, hostname_g, sizeof (vl->host));
+  }
+  else
+  {
+    if (st->socket != NULL)
+      sstrncpy (vl->host, hostname_g, sizeof (vl->host));
+    else
+      sstrncpy (vl->host,
+          (st->host != NULL) ? st->host : MEMCACHED_DEF_HOST,
+          sizeof (vl->host));
+    sstrncpy (vl->plugin_instance, st->name, sizeof (vl->plugin_instance));
+  }
 }
-/* }}} */
 
 static void submit_derive (const char *type, const char *type_inst,
-               derive_t value) /* {{{ */
+    derive_t value, memcached_t *st)
 {
-       value_t values[1];
-       value_list_t vl = VALUE_LIST_INIT;
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+  memcached_init_vl (&vl, st);
 
-       values[0].derive = value;
+  values[0].derive = value;
 
-       vl.values = values;
-       vl.values_len = 1;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-       if (type_inst != NULL)
-               sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.type, type, sizeof (vl.type));
+  if (type_inst != NULL)
+    sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 
-       plugin_dispatch_values (&vl);
-} /* void memcached_submit_cmd */
-/* }}} */
+  plugin_dispatch_values (&vl);
+}
 
 static void submit_derive2 (const char *type, const char *type_inst,
-               derive_t value0, derive_t value1) /* {{{ */
+    derive_t value0, derive_t value1, memcached_t *st)
 {
-       value_t values[2];
-       value_list_t vl = VALUE_LIST_INIT;
+  value_t values[2];
+  value_list_t vl = VALUE_LIST_INIT;
+  memcached_init_vl (&vl, st);
 
-       values[0].derive = value0;
-       values[1].derive = value1;
+  values[0].derive = value0;
+  values[1].derive = value1;
 
-       vl.values = values;
-       vl.values_len = 2;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-       if (type_inst != NULL)
-               sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+  vl.values = values;
+  vl.values_len = 2;
+  sstrncpy (vl.type, type, sizeof (vl.type));
+  if (type_inst != NULL)
+    sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 
-       plugin_dispatch_values (&vl);
-} /* void memcached_submit_cmd */
-/* }}} */
+  plugin_dispatch_values (&vl);
+}
 
 static void submit_gauge (const char *type, const char *type_inst,
-               gauge_t value) /* {{{ */
+    gauge_t value, memcached_t *st)
 {
-       value_t values[1];
-       value_list_t vl = VALUE_LIST_INIT;
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+  memcached_init_vl (&vl, st);
 
-       values[0].gauge = value;
+  values[0].gauge = value;
 
-       vl.values = values;
-       vl.values_len = 1;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-       if (type_inst != NULL)
-               sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.type, type, sizeof (vl.type));
+  if (type_inst != NULL)
+    sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 
-       plugin_dispatch_values (&vl);
+  plugin_dispatch_values (&vl);
 }
-/* }}} */
 
 static void submit_gauge2 (const char *type, const char *type_inst,
-               gauge_t value0, gauge_t value1) /* {{{ */
+    gauge_t value0, gauge_t value1, memcached_t *st)
 {
-       value_t values[2];
-       value_list_t vl = VALUE_LIST_INIT;
+  value_t values[2];
+  value_list_t vl = VALUE_LIST_INIT;
+  memcached_init_vl (&vl, st);
 
-       values[0].gauge = value0;
-       values[1].gauge = value1;
+  values[0].gauge = value0;
+  values[1].gauge = value1;
 
-       vl.values = values;
-       vl.values_len = 2;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-       if (type_inst != NULL)
-               sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+  vl.values = values;
+  vl.values_len = 2;
+  sstrncpy (vl.type, type, sizeof (vl.type));
+  if (type_inst != NULL)
+    sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 
-       plugin_dispatch_values (&vl);
+  plugin_dispatch_values (&vl);
 }
-/* }}} */
 
-static int memcached_read (void) /* {{{ */
+static int memcached_read (user_data_t *user_data)
 {
-       char buf[1024];
-       char *fields[3];
-       char *ptr;
-       char *line;
-       char *saveptr;
-       int fields_num;
-
-       gauge_t bytes_used = NAN;
-       gauge_t bytes_total = NAN;
-       gauge_t hits = NAN;
-       gauge_t gets = NAN;
-       derive_t rusage_user = 0;
-       derive_t rusage_syst = 0;
-       derive_t octets_rx = 0;
-       derive_t octets_tx = 0;
-
-       /* get data from daemon */
-       if (memcached_query_daemon (buf, sizeof (buf)) < 0) {
-               return -1;
-       }
+  char buf[4096];
+  char *fields[3];
+  char *ptr;
+  char *line;
+  char *saveptr;
+  int fields_num;
+
+  gauge_t bytes_used = NAN;
+  gauge_t bytes_total = NAN;
+  gauge_t hits = NAN;
+  gauge_t gets = NAN;
+  derive_t rusage_user = 0;
+  derive_t rusage_syst = 0;
+  derive_t octets_rx = 0;
+  derive_t octets_tx = 0;
+
+  memcached_t *st;
+  st = user_data->data;
+
+  /* get data from daemon */
+  if (memcached_query_daemon (buf, sizeof (buf), st) < 0) {
+    return -1;
+  }
 
 #define FIELD_IS(cnst) \
-       (((sizeof(cnst) - 1) == name_len) && (strcmp (cnst, fields[1]) == 0))
-
-       ptr = buf;
-       saveptr = NULL;
-       while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL)
-       {
-               int name_len;
-
-               ptr = NULL;
-
-               fields_num = strsplit(line, fields, 3);
-               if (fields_num != 3)
-                       continue;
-
-               name_len = strlen(fields[1]);
-               if (name_len == 0)
-                       continue;
-
-               /*
-                * For an explanation on these fields please refer to
-                * <http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt>
-                */
-
-               /*
-                * CPU time consumed by the memcached process
-                */
-               if (FIELD_IS ("rusage_user"))
-               {
-                       rusage_user = atoll (fields[2]);
-               }
-               else if (FIELD_IS ("rusage_system"))
-               {
-                       rusage_syst = atoll(fields[2]);
-               }
-
-               /*
-                * Number of threads of this instance
-                */
-               else if (FIELD_IS ("threads"))
-               {
-                       submit_gauge2 ("ps_count", NULL, NAN, atof (fields[2]));
-               }
-
-               /*
-                * Number of items stored
-                */
-               else if (FIELD_IS ("curr_items"))
-               {
-                       submit_gauge ("memcached_items", "current", atof (fields[2]));
-               }
-
-               /*
-                * Number of bytes used and available (total - used)
-                */
-               else if (FIELD_IS ("bytes"))
-               {
-                       bytes_used = atof (fields[2]);
-               }
-               else if (FIELD_IS ("limit_maxbytes"))
-               {
-                       bytes_total = atof(fields[2]);
-               }
-
-               /*
-                * Connections
-                */
-               else if (FIELD_IS ("curr_connections"))
-               {
-                       submit_gauge ("memcached_connections", "current", atof (fields[2]));
-               }
-
-               /*
-                * Commands
-                */
-               else if ((name_len > 4) && (strncmp (fields[1], "cmd_", 4) == 0))
-               {
-                       const char *name = fields[1] + 4;
-                       submit_derive ("memcached_command", name, atoll (fields[2]));
-                       if (strcmp (name, "get") == 0)
-                               gets = atof (fields[2]);
-               }
-
-               /*
-                * Operations on the cache, i. e. cache hits, cache misses and evictions of items
-                */
-               else if (FIELD_IS ("get_hits"))
-               {
-                       submit_derive ("memcached_ops", "hits", atoll (fields[2]));
-                       hits = atof (fields[2]);
-               }
-               else if (FIELD_IS ("get_misses"))
-               {
-                       submit_derive ("memcached_ops", "misses", atoll (fields[2]));
-               }
-               else if (FIELD_IS ("evictions"))
-               {
-                       submit_derive ("memcached_ops", "evictions", atoll (fields[2]));
-               }
-
-               /*
-                * Network traffic
-                */
-               else if (FIELD_IS ("bytes_read"))
-               {
-                       octets_rx = atoll (fields[2]);
-               }
-               else if (FIELD_IS ("bytes_written"))
-               {
-                       octets_tx = atoll (fields[2]);
-               }
-       } /* while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL) */
-
-       if (!isnan (bytes_used) && !isnan (bytes_total) && (bytes_used <= bytes_total))
-               submit_gauge2 ("df", "cache", bytes_used, bytes_total - bytes_used);
-
-       if ((rusage_user != 0) || (rusage_syst != 0))
-               submit_derive2 ("ps_cputime", NULL, rusage_user, rusage_syst);
-
-       if ((octets_rx != 0) || (octets_tx != 0))
-               submit_derive2 ("memcached_octets", NULL, octets_rx, octets_tx);
-
-       if (!isnan (gets) && !isnan (hits))
-       {
-               gauge_t rate = NAN;
-
-               if (gets != 0.0)
-                       rate = 100.0 * hits / gets;
-
-               submit_gauge ("percent", "hitratio", rate);
-       }
-
-       return 0;
+  (((sizeof(cnst) - 1) == name_len) && (strcmp (cnst, fields[1]) == 0))
+
+  ptr = buf;
+  saveptr = NULL;
+  while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL)
+  {
+    int name_len;
+
+    ptr = NULL;
+
+    fields_num = strsplit(line, fields, 3);
+    if (fields_num != 3)
+      continue;
+
+    name_len = strlen(fields[1]);
+    if (name_len == 0)
+      continue;
+
+    /*
+     * For an explanation on these fields please refer to
+     * <http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt>
+     */
+
+    /*
+     * CPU time consumed by the memcached process
+     */
+    if (FIELD_IS ("rusage_user"))
+    {
+      rusage_user = atoll (fields[2]);
+    }
+    else if (FIELD_IS ("rusage_system"))
+    {
+      rusage_syst = atoll(fields[2]);
+    }
+
+    /*
+     * Number of threads of this instance
+     */
+    else if (FIELD_IS ("threads"))
+    {
+      submit_gauge2 ("ps_count", NULL, NAN, atof (fields[2]), st);
+    }
+
+    /*
+     * Number of items stored
+     */
+    else if (FIELD_IS ("curr_items"))
+    {
+      submit_gauge ("memcached_items", "current", atof (fields[2]), st);
+    }
+
+    /*
+     * Number of bytes used and available (total - used)
+     */
+    else if (FIELD_IS ("bytes"))
+    {
+      bytes_used = atof (fields[2]);
+    }
+    else if (FIELD_IS ("limit_maxbytes"))
+    {
+      bytes_total = atof(fields[2]);
+    }
+
+    /*
+     * Connections
+     */
+    else if (FIELD_IS ("curr_connections"))
+    {
+      submit_gauge ("memcached_connections", "current", atof (fields[2]), st);
+    }
+
+    /*
+     * Commands
+     */
+    else if ((name_len > 4) && (strncmp (fields[1], "cmd_", 4) == 0))
+    {
+      const char *name = fields[1] + 4;
+      submit_derive ("memcached_command", name, atoll (fields[2]), st);
+      if (strcmp (name, "get") == 0)
+        gets = atof (fields[2]);
+    }
+
+    /*
+     * Operations on the cache, i. e. cache hits, cache misses and evictions of items
+     */
+    else if (FIELD_IS ("get_hits"))
+    {
+      submit_derive ("memcached_ops", "hits", atoll (fields[2]), st);
+      hits = atof (fields[2]);
+    }
+    else if (FIELD_IS ("get_misses"))
+    {
+      submit_derive ("memcached_ops", "misses", atoll (fields[2]), st);
+    }
+    else if (FIELD_IS ("evictions"))
+    {
+      submit_derive ("memcached_ops", "evictions", atoll (fields[2]), st);
+    }
+
+    /*
+     * Network traffic
+     */
+    else if (FIELD_IS ("bytes_read"))
+    {
+      octets_rx = atoll (fields[2]);
+    }
+    else if (FIELD_IS ("bytes_written"))
+    {
+      octets_tx = atoll (fields[2]);
+    }
+  } /* while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL) */
+
+  if (!isnan (bytes_used) && !isnan (bytes_total) && (bytes_used <= bytes_total))
+    submit_gauge2 ("df", "cache", bytes_used, bytes_total - bytes_used, st);
+
+  if ((rusage_user != 0) || (rusage_syst != 0))
+    submit_derive2 ("ps_cputime", NULL, rusage_user, rusage_syst, st);
+
+  if ((octets_rx != 0) || (octets_tx != 0))
+    submit_derive2 ("memcached_octets", NULL, octets_rx, octets_tx, st);
+
+  if (!isnan (gets) && !isnan (hits))
+  {
+    gauge_t rate = NAN;
+
+    if (gets != 0.0)
+      rate = 100.0 * hits / gets;
+
+    submit_gauge ("percent", "hitratio", rate, st);
+  }
+
+  return 0;
+} /* int memcached_read */
+
+static int memcached_add_read_callback (memcached_t *st)
+{
+  user_data_t ud;
+  char callback_name[3*DATA_MAX_NAME_LEN];
+  int status;
+
+  memset (&ud, 0, sizeof (ud));
+  ud.data = st;
+  ud.free_func = (void *) memcached_free;
+
+  assert (st->name != NULL);
+  ssnprintf (callback_name, sizeof (callback_name), "memcached/%s", st->name);
+
+  status = plugin_register_complex_read (/* group = */ "memcached",
+      /* name      = */ callback_name,
+      /* callback  = */ memcached_read,
+      /* interval  = */ NULL,
+      /* user_data = */ &ud);
+  return (status);
+} /* int memcached_add_read_callback */
+
+/* Configuration handling functiions
+ * <Plugin memcached>
+ *   <Instance "instance_name">
+ *     Host foo.zomg.com
+ *     Port "1234"
+ *   </Instance>
+ * </Plugin>
+ */
+static int config_add_instance(oconfig_item_t *ci)
+{
+  memcached_t *st;
+  int i;
+  int status = 0;
+
+  /* Disable automatic generation of default instance in the init callback. */
+  memcached_have_instances = 1;
+
+  st = malloc (sizeof (*st));
+  if (st == NULL)
+  {
+    ERROR ("memcached plugin: malloc failed.");
+    return (-1);
+  }
+
+  memset (st, 0, sizeof (*st));
+  st->name = NULL;
+  st->socket = NULL;
+  st->host = NULL;
+  st->port = NULL;
+
+  if (strcasecmp (ci->key, "Plugin") == 0) /* default instance */
+    st->name = sstrdup ("__legacy__");
+  else /* <Instance /> block */
+    status = cf_util_get_string (ci, &st->name);
+  if (status != 0)
+  {
+    sfree (st);
+    return (status);
+  }
+  assert (st->name != NULL);
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Socket", child->key) == 0)
+      status = cf_util_get_string (child, &st->socket);
+    else if (strcasecmp ("Host", child->key) == 0)
+      status = cf_util_get_string (child, &st->host);
+    else if (strcasecmp ("Port", child->key) == 0)
+      status = cf_util_get_service (child, &st->port);
+    else
+    {
+      WARNING ("memcached plugin: Option `%s' not allowed here.",
+          child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  if (status == 0)
+    status = memcached_add_read_callback (st);
+
+  if (status != 0)
+  {
+    memcached_free(st);
+    return (-1);
+  }
+
+  return (0);
 }
-/* }}} */
 
-void module_register (void) /* {{{ */
+static int memcached_config (oconfig_item_t *ci)
 {
-       plugin_register_config ("memcached", memcached_config, config_keys, config_keys_num);
-       plugin_register_read ("memcached", memcached_read);
+  int status = 0;
+  _Bool have_instance_block = 0;
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Instance", child->key) == 0)
+    {
+      config_add_instance (child);
+      have_instance_block = 1;
+    }
+    else if (!have_instance_block)
+    {
+      /* Non-instance option: Assume legacy configuration (without <Instance />
+       * blocks) and call config_add_instance() with the <Plugin /> block. */
+      return (config_add_instance (ci));
+    }
+    else
+      WARNING ("memcached plugin: The configuration option "
+          "\"%s\" is not allowed here. Did you "
+          "forget to add an <Instance /> block "
+          "around the configuration?",
+          child->key);
+  } /* for (ci->children) */
+
+  return (status);
 }
-/* }}} */
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: sw=4 ts=4 fdm=marker noexpandtab
- * vim<600: sw=4 ts=4 noexpandtab
- */
 
+static int memcached_init (void)
+{
+  memcached_t *st;
+  int status;
+
+  if (memcached_have_instances)
+    return (0);
+
+  /* No instances were configured, lets start a default instance. */
+  st = malloc (sizeof (*st));
+  if (st == NULL)
+    return (ENOMEM);
+  memset (st, 0, sizeof (*st));
+  st->name = sstrdup ("__legacy__");
+  st->socket = NULL;
+  st->host = NULL;
+  st->port = NULL;
+
+  status = memcached_add_read_callback (st);
+  if (status == 0)
+    memcached_have_instances = 1;
+  else
+    memcached_free (st);
+
+  return (status);
+} /* int memcached_init */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("memcached", memcached_config);
+  plugin_register_init ("memcached", memcached_init);
+}
index b502b37..ea98ba9 100644 (file)
@@ -250,6 +250,7 @@ void meta_data_destroy (meta_data_t *md) /* {{{ */
   if (md == NULL)
     return;
 
+  pthread_mutex_destroy(&md->lock);
   md_entry_free (md->head);
   pthread_mutex_destroy (&md->lock);
   free (md);
index 6b63678..32b352b 100644 (file)
@@ -192,9 +192,9 @@ static int mysql_config_database (oconfig_item_t *ci) /* {{{ */
                ud.data = (void *) db;
                ud.free_func = mysql_database_free;
 
-               if (db->database != NULL)
+               if (db->instance != NULL)
                        ssnprintf (cb_name, sizeof (cb_name), "mysql-%s",
-                                       db->database);
+                                       db->instance);
                else
                        sstrncpy (cb_name, "mysql", sizeof (cb_name));
 
index d9bd1ae..f7bc04d 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/netapp.c
- * Copyright (C) 2009  Sven Trenkel
+ * Copyright (C) 2009,2010  Sven Trenkel
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -2497,7 +2497,7 @@ static int cna_init_host (host_config_t *host) /* {{{ */
        na_server_adminuser(host->srv, host->username, host->password);
        na_server_set_timeout(host->srv, 5 /* seconds */);
 
-       return 0;
+       return (0);
 } /* }}} int cna_init_host */
 
 static int cna_init (void) /* {{{ */
@@ -2514,6 +2514,32 @@ static int cna_init (void) /* {{{ */
        return (0);
 } /* }}} cna_init */
 
+static int cna_read_internal (host_config_t *host) { /* {{{ */
+       int status;
+
+       status = cna_query_wafl (host);
+       if (status != 0)
+               return (status);
+
+       status = cna_query_disk (host);
+       if (status != 0)
+               return (status);
+
+       status = cna_query_volume_perf (host);
+       if (status != 0)
+               return (status);
+
+       status = cna_query_volume_usage (host);
+       if (status != 0)
+               return (status);
+
+       status = cna_query_system (host);
+       if (status != 0)
+               return (status);
+
+       return 0;
+} /* }}} int cna_read_internal */
+
 static int cna_read (user_data_t *ud) { /* {{{ */
        host_config_t *host;
        int status;
@@ -2526,12 +2552,14 @@ static int cna_read (user_data_t *ud) { /* {{{ */
        status = cna_init_host (host);
        if (status != 0)
                return (status);
-       
-       cna_query_wafl (host);
-       cna_query_disk (host);
-       cna_query_volume_perf (host);
-       cna_query_volume_usage (host);
-       cna_query_system (host);
+
+       status = cna_read_internal (host);
+       if (status != 0)
+       {
+               if (host->srv != NULL)
+                       na_server_close (host->srv);
+               host->srv = NULL;
+       }
 
        return 0;
 } /* }}} int cna_read */
index ef851d3..39536d9 100644 (file)
@@ -223,7 +223,7 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
 
   msg = NLMSG_DATA (nmh);
 
-  msg_len = nmh->nlmsg_len - sizeof (struct ifinfomsg);
+  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);
@@ -554,24 +554,26 @@ static int ir_init (void)
 
 static int ir_read (void)
 {
-  struct ifinfomsg im;
   struct tcmsg tm;
   int ifindex;
 
   static const int type_id[] = { RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER };
   static const char *type_name[] = { "qdisc", "class", "filter" };
 
-  memset (&im, '\0', sizeof (im));
-  im.ifi_type = AF_UNSPEC;
-
-  if (rtnl_dump_request (&rth, RTM_GETLINK, &im, sizeof (im)) < 0)
+  if (rtnl_wilddump_request (&rth, AF_UNSPEC, RTM_GETLINK) < 0)
   {
-    ERROR ("netlink plugin: ir_read: rtnl_dump_request failed.");
+    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
   {
     ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
     return (-1);
@@ -608,8 +610,14 @@ static int ir_read (void)
        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
       {
        ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
        continue;
index 369da94..e8b45a3 100644 (file)
 #endif
 
 #if HAVE_LIBGCRYPT
+# include <pthread.h>
+# if defined __APPLE__
+/* default xcode compiler throws warnings even when deprecated functionality
+ * is not used. -Werror breaks the build because of erroneous warnings.
+ * http://stackoverflow.com/questions/10556299/compiler-warnings-with-libgcrypt-v1-5-0/12830209#12830209
+ */
+#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+# endif
 # include <gcrypt.h>
+# if defined __APPLE__
+/* Re enable deprecation warnings */
+#  pragma GCC diagnostic warning "-Wdeprecated-declarations"
+# endif
 GCRY_THREAD_OPTION_PTHREAD_IMPL;
 #endif
 
@@ -259,6 +271,7 @@ typedef struct receive_list_entry_s receive_list_entry_t;
  * Private variables
  */
 static int network_config_ttl = 0;
+/* Ethernet - (IPv6 + UDP) = 1500 - (40 + 8) = 1452 */
 static size_t network_config_packet_size = 1452;
 static int network_config_forward = 0;
 static int network_config_stats = 0;
@@ -351,6 +364,43 @@ static _Bool check_send_okay (const value_list_t *vl) /* {{{ */
   return (!received);
 } /* }}} _Bool check_send_okay */
 
+static _Bool check_notify_received (const notification_t *n) /* {{{ */
+{
+  notification_meta_t *ptr;
+
+  for (ptr = n->meta; ptr != NULL; ptr = ptr->next)
+    if ((strcmp ("network:received", ptr->name) == 0)
+        && (ptr->type == NM_TYPE_BOOLEAN))
+      return ((_Bool) ptr->nm_value.nm_boolean);
+
+  return (0);
+} /* }}} _Bool check_notify_received */
+
+static _Bool check_send_notify_okay (const notification_t *n) /* {{{ */
+{
+  static c_complain_t complain_forwarding = C_COMPLAIN_INIT_STATIC;
+  _Bool received = 0;
+
+  if (n->meta == NULL)
+    return (1);
+
+  received = check_notify_received (n);
+
+  if (network_config_forward && received)
+  {
+    c_complain_once (LOG_ERR, &complain_forwarding,
+        "network plugin: A notification has been received via the network "
+        "forwarding if enabled. Forwarding of notifications is currently "
+        "not supported, because there is not loop-deteciton available. "
+        "Please contact the collectd mailing list if you need this "
+        "feature.");
+  }
+
+  /* By default, only *send* value lists that were not *received* by the
+   * network plugin. */
+  return (!received);
+} /* }}} _Bool check_send_notify_okay */
+
 static int network_dispatch_values (value_list_t *vl, /* {{{ */
     const char *username)
 {
@@ -414,6 +464,29 @@ static int network_dispatch_values (value_list_t *vl, /* {{{ */
   return (0);
 } /* }}} int network_dispatch_values */
 
+static int network_dispatch_notification (notification_t *n) /* {{{ */
+{
+  int status;
+
+  assert (n->meta == NULL);
+
+  status = plugin_notification_meta_add_boolean (n, "network:received", 1);
+  if (status != 0)
+  {
+    ERROR ("network plugin: plugin_notification_meta_add_boolean failed.");
+    plugin_notification_meta_free (n->meta);
+    n->meta = NULL;
+    return (status);
+  }
+
+  status = plugin_dispatch_notification (n);
+
+  plugin_notification_meta_free (n->meta);
+  n->meta = NULL;
+
+  return (status);
+} /* }}} int network_dispatch_notification */
+
 #if HAVE_LIBGCRYPT
 static gcry_cipher_hd_t network_get_aes256_cypher (sockent_t *se, /* {{{ */
     const void *iv, size_t iv_size, const char *username)
@@ -600,7 +673,7 @@ static int write_part_number (char **ret_buffer, int *ret_buffer_len,
 
        part_header_t pkg_head;
        uint64_t pkg_value;
-       
+
        int offset;
 
        packet_len = sizeof (pkg_head) + sizeof (pkg_value);
@@ -704,7 +777,7 @@ static int parse_part_values (void **ret_buffer, size_t *ret_buffer_len,
 
        exp_size = 3 * sizeof (uint16_t)
                + pkg_numval * (sizeof (uint8_t) + sizeof (value_t));
-       if ((buffer_len < 0) || (buffer_len < exp_size))
+       if (buffer_len < exp_size)
        {
                WARNING ("network plugin: parse_part_values: "
                                "Packet too short: "
@@ -789,7 +862,7 @@ static int parse_part_number (void **ret_buffer, size_t *ret_buffer_len,
 
        uint16_t pkg_length;
 
-       if ((buffer_len < 0) || ((size_t) buffer_len < exp_size))
+       if (buffer_len < exp_size)
        {
                WARNING ("network plugin: parse_part_number: "
                                "Packet too short: "
@@ -828,7 +901,7 @@ static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
 
        uint16_t pkg_length;
 
-       if ((buffer_len < 0) || (buffer_len < header_size))
+       if (buffer_len < header_size)
        {
                WARNING ("network plugin: parse_part_string: "
                                "Packet too short: "
@@ -1483,7 +1556,7 @@ static int parse_packet (sockent_t *se, /* {{{ */
                        }
                        else
                        {
-                               plugin_dispatch_notification (&n);
+                               network_dispatch_notification (&n);
                        }
                }
                else if (pkg_type == TYPE_SEVERITY)
@@ -1604,7 +1677,7 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
                                        sizeof (network_config_ttl)) != 0)
                {
                        char errbuf[1024];
-                       ERROR ("setsockopt: %s",
+                       ERROR ("network plugin: setsockopt (ipv4-ttl): %s",
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
                        return (-1);
                }
@@ -1625,7 +1698,7 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
                                        sizeof (network_config_ttl)) != 0)
                {
                        char errbuf[1024];
-                       ERROR ("setsockopt: %s",
+                       ERROR ("network plugin: setsockopt(ipv6-ttl): %s",
                                        sstrerror (errno, errbuf,
                                                sizeof (errbuf)));
                        return (-1);
@@ -1672,7 +1745,7 @@ static int network_set_interface (const sockent_t *se, const struct addrinfo *ai
                                                &mreq, sizeof (mreq)) != 0)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (ipv4-multicast-if): %s",
                                                sstrerror (errno, errbuf, sizeof (errbuf)));
                                return (-1);
                        }
@@ -1691,7 +1764,7 @@ static int network_set_interface (const sockent_t *se, const struct addrinfo *ai
                                                sizeof (se->interface)) != 0)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (ipv6-multicast-if): %s",
                                                sstrerror (errno, errbuf,
                                                        sizeof (errbuf)));
                                return (-1);
@@ -1702,9 +1775,9 @@ static int network_set_interface (const sockent_t *se, const struct addrinfo *ai
        }
 
        /* else: Not a multicast interface. */
-#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE)
        if (se->interface != 0)
        {
+#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE)
                char interface_name[IFNAMSIZ];
 
                if (if_indextoname (se->interface, interface_name) == NULL)
@@ -1717,37 +1790,42 @@ static int network_set_interface (const sockent_t *se, const struct addrinfo *ai
                                        sizeof(interface_name)) == -1 )
                {
                        char errbuf[1024];
-                       ERROR ("setsockopt: %s",
+                       ERROR ("network plugin: setsockopt (bind-if): %s",
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
                        return (-1);
                }
-       }
 /* #endif HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
 
 #else
-       WARNING ("network plugin: Cannot set the interface on a unicast "
+               WARNING ("network plugin: Cannot set the interface on a unicast "
                        "socket because "
 # if !defined(SO_BINDTODEVICE)
-                       "the the \"SO_BINDTODEVICE\" socket option "
+                       "the \"SO_BINDTODEVICE\" socket option "
 # else
                        "the \"if_indextoname\" function "
 # endif
                        "is not available on your system.");
 #endif
 
+       }
+
        return (0);
 } /* }}} network_set_interface */
 
 static int network_bind_socket (int fd, const struct addrinfo *ai, const int interface_idx)
 {
+#if KERNEL_SOLARIS
+       char loop   = 0;
+#else
        int loop = 0;
+#endif
        int yes  = 1;
 
        /* allow multiple sockets to use the same PORT number */
        if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
                                &yes, sizeof(yes)) == -1) {
                 char errbuf[1024];
-                ERROR ("setsockopt: %s", 
+                ERROR ("network plugin: setsockopt (reuseaddr): %s",
                                 sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
@@ -1790,7 +1868,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
                                                &loop, sizeof (loop)) == -1)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (multicast-loop): %s",
                                                sstrerror (errno, errbuf,
                                                        sizeof (errbuf)));
                                return (-1);
@@ -1800,7 +1878,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
                                                &mreq, sizeof (mreq)) == -1)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (add-membership): %s",
                                                sstrerror (errno, errbuf,
                                                        sizeof (errbuf)));
                                return (-1);
@@ -1838,7 +1916,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
                                                &loop, sizeof (loop)) == -1)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (ipv6-multicast-loop): %s",
                                                sstrerror (errno, errbuf,
                                                        sizeof (errbuf)));
                                return (-1);
@@ -1848,7 +1926,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
                                                &mreq, sizeof (mreq)) == -1)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (ipv6-add-membership): %s",
                                                sstrerror (errno, errbuf,
                                                        sizeof (errbuf)));
                                return (-1);
@@ -1876,7 +1954,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
                                        sizeof(interface_name)) == -1 )
                {
                        char errbuf[1024];
-                       ERROR ("setsockopt: %s",
+                       ERROR ("network plugin: setsockopt (bind-if): %s",
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
                        return (-1);
                }
@@ -2651,7 +2729,7 @@ static int add_to_buffer (char *buffer, int buffer_size, /* {{{ */
                        return (-1);
                sstrncpy (vl_def->type_instance, vl->type_instance, sizeof (vl_def->type_instance));
        }
-       
+
        if (write_part_values (&buffer, &buffer_size, ds, vl) != 0)
                return (-1);
 
@@ -3085,14 +3163,17 @@ static int network_config (oconfig_item_t *ci) /* {{{ */
 } /* }}} int network_config */
 
 static int network_notification (const notification_t *n,
-               user_data_t __attribute__((unused)) *user_data)
+    user_data_t __attribute__((unused)) *user_data)
 {
   char  buffer[network_config_packet_size];
   char *buffer_ptr = buffer;
   int   buffer_free = sizeof (buffer);
   int   status;
 
-  memset (buffer, '\0', sizeof (buffer));
+  if (!check_send_notify_okay (n))
+    return (0);
+
+  memset (buffer, 0, sizeof (buffer));
 
   status = write_part_number (&buffer_ptr, &buffer_free, TYPE_TIME_HR,
       (uint64_t) n->time);
@@ -3107,7 +3188,7 @@ static int network_notification (const notification_t *n,
   if (strlen (n->host) > 0)
   {
     status = write_part_string (&buffer_ptr, &buffer_free, TYPE_HOST,
-       n->host, strlen (n->host));
+        n->host, strlen (n->host));
     if (status != 0)
       return (-1);
   }
@@ -3115,7 +3196,7 @@ static int network_notification (const notification_t *n,
   if (strlen (n->plugin) > 0)
   {
     status = write_part_string (&buffer_ptr, &buffer_free, TYPE_PLUGIN,
-       n->plugin, strlen (n->plugin));
+        n->plugin, strlen (n->plugin));
     if (status != 0)
       return (-1);
   }
@@ -3123,8 +3204,8 @@ static int network_notification (const notification_t *n,
   if (strlen (n->plugin_instance) > 0)
   {
     status = write_part_string (&buffer_ptr, &buffer_free,
-       TYPE_PLUGIN_INSTANCE,
-       n->plugin_instance, strlen (n->plugin_instance));
+        TYPE_PLUGIN_INSTANCE,
+        n->plugin_instance, strlen (n->plugin_instance));
     if (status != 0)
       return (-1);
   }
@@ -3132,7 +3213,7 @@ static int network_notification (const notification_t *n,
   if (strlen (n->type) > 0)
   {
     status = write_part_string (&buffer_ptr, &buffer_free, TYPE_TYPE,
-       n->type, strlen (n->type));
+        n->type, strlen (n->type));
     if (status != 0)
       return (-1);
   }
@@ -3140,7 +3221,7 @@ static int network_notification (const notification_t *n,
   if (strlen (n->type_instance) > 0)
   {
     status = write_part_string (&buffer_ptr, &buffer_free, TYPE_TYPE_INSTANCE,
-       n->type_instance, strlen (n->type_instance));
+        n->type_instance, strlen (n->type_instance));
     if (status != 0)
       return (-1);
   }
@@ -3284,9 +3365,17 @@ static int network_init (void)
        have_init = 1;
 
 #if HAVE_LIBGCRYPT
-       gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
-       gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
-       gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+    /* http://lists.gnupg.org/pipermail/gcrypt-devel/2003-August/000458.html
+     * Because you can't know in a library whether another library has
+     * already initialized the library
+     */
+    if (!gcry_control (GCRYCTL_ANY_INITIALIZATION_P))
+    {
+        gcry_check_version(NULL); /* before calling any other functions */
+        gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+        gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
+        gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+    }
 #endif
 
        if (network_config_stats != 0)
@@ -3360,7 +3449,7 @@ static int network_init (void)
        return (0);
 } /* int network_init */
 
-/* 
+/*
  * The flush option of the network plugin cannot flush individual identifiers.
  * All the values are added to a buffer and sent when the buffer is full, the
  * requested value may or may not be in there, it's not worth finding out. We
index f3c636e..461e806 100644 (file)
--- a/src/nfs.c
+++ b/src/nfs.c
  * Authors:
  *   Jason Pepas <cell at ices.utexas.edu>
  *   Florian octo Forster <octo at verplant.org>
+ *   Cosmin Ioiart <cioiart at gmail.com>
  **/
 
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 
-#if !KERNEL_LINUX
-# error "No applicable input method."
+#if HAVE_KSTAT_H
+#include <kstat.h>
 #endif
 
 /*
@@ -92,10 +93,9 @@ static const char *nfs2_procedures_names[] =
        "mkdir",
        "rmdir",
        "readdir",
-       "fsstat",
-       NULL
+       "fsstat"
 };
-static int nfs2_procedures_names_num = 18;
+static size_t nfs2_procedures_names_num = STATIC_ARRAY_SIZE (nfs2_procedures_names);
 
 static const char *nfs3_procedures_names[] =
 {
@@ -120,12 +120,57 @@ static const char *nfs3_procedures_names[] =
        "fsstat",
        "fsinfo",
        "pathconf",
+       "commit"
+};
+static size_t nfs3_procedures_names_num = STATIC_ARRAY_SIZE (nfs3_procedures_names);
+
+#if HAVE_LIBKSTAT
+static const char *nfs4_procedures_names[] =
+{
+       "null",
+       "compound",
+       "reserved",
+       "access",
+       "close",
        "commit",
-       NULL
+       "create",
+       "delegpurge",
+       "delegreturn",
+       "getattr",
+       "getfh",
+       "link",
+       "lock",
+       "lockt",
+       "locku",
+       "lookup",
+       "lookupp",
+       "nverify",
+       "open",
+       "openattr",
+       "open_confirm",
+       "open_downgrade",
+       "putfh",
+       "putpubfh",
+       "putrootfh",
+       "read",
+       "readdir",
+       "readlink",
+       "remove",
+       "rename",
+       "renew",
+       "restorefh",
+       "savefh",
+       "secinfo",
+       "setattr",
+       "setclientid",
+       "setclientid_confirm",
+       "verify",
+       "write"
 };
-static int nfs3_procedures_names_num = 22;
+static size_t nfs4_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_procedures_names);
+#endif
 
-#if HAVE_LIBKSTAT && 0
+#if HAVE_LIBKSTAT
 extern kstat_ctl_t *kc;
 static kstat_t *nfs2_ksp_client;
 static kstat_t *nfs2_ksp_server;
@@ -137,11 +182,17 @@ static kstat_t *nfs4_ksp_server;
 
 /* Possibly TODO: NFSv4 statistics */
 
-#if 0
+#if KERNEL_LINUX
+static int nfs_init (void)
+{
+       return (0);
+}
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKSTAT
 static int nfs_init (void)
 {
-#if HAVE_LIBKSTAT && 0
-       kstat_t *ksp_chain;
+       kstat_t *ksp_chain = NULL;
 
        nfs2_ksp_client = NULL;
        nfs2_ksp_server = NULL;
@@ -149,9 +200,9 @@ static int nfs_init (void)
        nfs3_ksp_server = NULL;
        nfs4_ksp_client = NULL;
        nfs4_ksp_server = NULL;
-       
+
        if (kc == NULL)
-               return;
+               return (-1);
 
        for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
                        ksp_chain = ksp_chain->ks_next)
@@ -171,192 +222,168 @@ static int nfs_init (void)
                else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
                        nfs4_ksp_client = ksp_chain;
        }
-#endif
 
        return (0);
 } /* int nfs_init */
 #endif
 
-#define BUFSIZE 1024
 static void nfs_procedures_submit (const char *plugin_instance,
-               unsigned long long *val, const char **names, int len)
+               const char **type_instances,
+               value_t *values, size_t values_num)
 {
-       value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
-       int i;
+       size_t i;
 
-       vl.values = values;
        vl.values_len = 1;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "nfs", sizeof (vl.plugin));
        sstrncpy (vl.plugin_instance, plugin_instance,
-                       sizeof (vl.plugin_instance));
+                       sizeof (vl.plugin_instance));
        sstrncpy (vl.type, "nfs_procedure", sizeof (vl.type));
 
-       for (i = 0; i < len; i++)
+       for (i = 0; i < values_num; i++)
        {
-               values[0].derive = val[i];
-               sstrncpy (vl.type_instance, names[i],
+               vl.values = values + i;
+               sstrncpy (vl.type_instance, type_instances[i],
                                sizeof (vl.type_instance));
-               DEBUG ("%s-%s/nfs_procedure-%s = %llu",
-                               vl.plugin, vl.plugin_instance,
-                               vl.type_instance, val[i]);
-               plugin_dispatch_values (&vl);
+               plugin_dispatch_values_secure (&vl);
        }
 } /* void nfs_procedures_submit */
 
-static void nfs_read_stats_file (FILE *fh, char *inst)
+#if KERNEL_LINUX
+static int nfs_submit_fields (int nfs_version, const char *instance,
+               char **fields, size_t fields_num,
+               const char **proc_names, size_t proc_names_num)
 {
-       char buffer[BUFSIZE];
-
        char plugin_instance[DATA_MAX_NAME_LEN];
+       value_t values[fields_num];
+       size_t i;
+
+       if (fields_num != proc_names_num)
+       {
+               WARNING ("nfs plugin: Wrong number of fields for "
+                               "NFSv%i %s statistics. Expected %zu, got %zu.",
+                               nfs_version, instance,
+                               proc_names_num, fields_num);
+               return (EINVAL);
+       }
+
+       ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
+                       nfs_version, instance);
+
+       for (i = 0; i < proc_names_num; i++)
+               (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
+
+       nfs_procedures_submit (plugin_instance, proc_names, values,
+                       proc_names_num);
+
+       return (0);
+}
+
+static void nfs_read_linux (FILE *fh, char *inst)
+{
+       char buffer[1024];
 
        char *fields[48];
-       int numfields = 0;
+       int fields_num = 0;
 
        if (fh == NULL)
                return;
 
-       while (fgets (buffer, BUFSIZE, fh) != NULL)
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
-               numfields = strsplit (buffer, fields, 48);
+               fields_num = strsplit (buffer,
+                               fields, STATIC_ARRAY_SIZE (fields));
 
-               if (((numfields - 2) != nfs2_procedures_names_num)
-                               && ((numfields - 2)
-                                       != nfs3_procedures_names_num))
+               if (fields_num < 3)
                        continue;
 
                if (strcmp (fields[0], "proc2") == 0)
                {
-                       int i;
-                       unsigned long long *values;
-
-                       if ((numfields - 2) != nfs2_procedures_names_num)
-                       {
-                               WARNING ("nfs plugin: Wrong "
-                                               "number of fields (= %i) "
-                                               "for NFSv2 statistics.",
-                                               numfields - 2);
-                               continue;
-                       }
-
-                       ssnprintf (plugin_instance, sizeof (plugin_instance),
-                                       "v2%s", inst);
-
-                       values = (unsigned long long *) malloc (nfs2_procedures_names_num * sizeof (unsigned long long));
-                       if (values == NULL)
-                       {
-                               char errbuf[1024];
-                               ERROR ("nfs plugin: malloc "
-                                               "failed: %s",
-                                               sstrerror (errno, errbuf, sizeof (errbuf)));
-                               continue;
-                       }
-
-                       for (i = 0; i < nfs2_procedures_names_num; i++)
-                               values[i] = atoll (fields[i + 2]);
-
-                       nfs_procedures_submit (plugin_instance, values,
+                       nfs_submit_fields (/* version = */ 2, inst,
+                                       fields + 2, (size_t) (fields_num - 2),
                                        nfs2_procedures_names,
                                        nfs2_procedures_names_num);
-
-                       free (values);
                }
                else if (strncmp (fields[0], "proc3", 5) == 0)
                {
-                       int i;
-                       unsigned long long *values;
-
-                       if ((numfields - 2) != nfs3_procedures_names_num)
-                       {
-                               WARNING ("nfs plugin: Wrong "
-                                               "number of fields (= %i) "
-                                               "for NFSv3 statistics.",
-                                               numfields - 2);
-                               continue;
-                       }
-
-                       ssnprintf (plugin_instance, sizeof (plugin_instance),
-                                       "v3%s", inst);
-
-                       values = (unsigned long long *) malloc (nfs3_procedures_names_num * sizeof (unsigned long long));
-                       if (values == NULL)
-                       {
-                               char errbuf[1024];
-                               ERROR ("nfs plugin: malloc "
-                                               "failed: %s",
-                                               sstrerror (errno, errbuf, sizeof (errbuf)));
-                               continue;
-                       }
-
-                       for (i = 0; i < nfs3_procedures_names_num; i++)
-                               values[i] = atoll (fields[i + 2]);
-
-                       nfs_procedures_submit (plugin_instance, values,
+                       nfs_submit_fields (/* version = */ 3, inst,
+                                       fields + 2, (size_t) (fields_num - 2),
                                        nfs3_procedures_names,
                                        nfs3_procedures_names_num);
-
-                       free (values);
                }
-       } /* while (fgets (buffer, BUFSIZE, fh) != NULL) */
-} /* void nfs_read_stats_file */
-#undef BUFSIZE
+       } /* while (fgets) */
+} /* void nfs_read_linux */
+#endif /* KERNEL_LINUX */
 
-#if HAVE_LIBKSTAT && 0
-static void nfs2_read_kstat (kstat_t *ksp, char *inst)
+#if HAVE_LIBKSTAT
+static int nfs_read_kstat (kstat_t *ksp, int nfs_version, char *inst,
+               const char **proc_names, size_t proc_names_num)
 {
-       unsigned long long values[18];
-
-       values[0] = get_kstat_value (ksp, "null");
-       values[1] = get_kstat_value (ksp, "getattr");
-       values[2] = get_kstat_value (ksp, "setattr");
-       values[3] = get_kstat_value (ksp, "root");
-       values[4] = get_kstat_value (ksp, "lookup");
-       values[5] = get_kstat_value (ksp, "readlink");
-       values[6] = get_kstat_value (ksp, "read");
-       values[7] = get_kstat_value (ksp, "wrcache");
-       values[8] = get_kstat_value (ksp, "write");
-       values[9] = get_kstat_value (ksp, "create");
-       values[10] = get_kstat_value (ksp, "remove");
-       values[11] = get_kstat_value (ksp, "rename");
-       values[12] = get_kstat_value (ksp, "link");
-       values[13] = get_kstat_value (ksp, "symlink");
-       values[14] = get_kstat_value (ksp, "mkdir");
-       values[15] = get_kstat_value (ksp, "rmdir");
-       values[16] = get_kstat_value (ksp, "readdir");
-       values[17] = get_kstat_value (ksp, "statfs");
-
-       nfs2_procedures_submit (values, inst);
+       char plugin_instance[DATA_MAX_NAME_LEN];
+       value_t values[proc_names_num];
+       size_t i;
+
+       if (ksp == NULL)
+               return (EINVAL);
+
+       ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
+                       nfs_version, inst);
+
+       kstat_read(kc, ksp, NULL);
+       for (i = 0; i < proc_names_num; i++)
+               values[i].counter = (derive_t) get_kstat_value (ksp,
+                               (char *)proc_names[i]);
+
+       nfs_procedures_submit (plugin_instance, proc_names, values,
+                       proc_names_num);
+       return (0);
 }
 #endif
 
+#if KERNEL_LINUX
 static int nfs_read (void)
 {
        FILE *fh;
 
        if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
        {
-               nfs_read_stats_file (fh, "client");
+               nfs_read_linux (fh, "client");
                fclose (fh);
        }
 
        if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
        {
-               nfs_read_stats_file (fh, "server");
+               nfs_read_linux (fh, "server");
                fclose (fh);
        }
 
-#if HAVE_LIBKSTAT && 0
-       if (nfs2_ksp_client != NULL)
-               nfs2_read_kstat (nfs2_ksp_client, "client");
-       if (nfs2_ksp_server != NULL)
-               nfs2_read_kstat (nfs2_ksp_server, "server");
-#endif /* defined(HAVE_LIBKSTAT) */
+       return (0);
+}
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKSTAT
+static int nfs_read (void)
+{
+       nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
+                       nfs2_procedures_names, nfs2_procedures_names_num);
+       nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
+                       nfs2_procedures_names, nfs2_procedures_names_num);
+       nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
+                       nfs3_procedures_names, nfs3_procedures_names_num);
+       nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
+                       nfs3_procedures_names, nfs3_procedures_names_num);
+       nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
+                       nfs4_procedures_names, nfs4_procedures_names_num);
+       nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
+                       nfs4_procedures_names, nfs4_procedures_names_num);
 
        return (0);
 }
+#endif /* HAVE_LIBKSTAT */
 
 void module_register (void)
 {
+       plugin_register_init ("nfs", nfs_init);
        plugin_register_read ("nfs", nfs_read);
 } /* void module_register */
index 8bbf74d..bbc455f 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/ntpd.c
- * Copyright (C) 2006-2007  Florian octo Forster
+ * Copyright (C) 2006-2012  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
@@ -16,7 +16,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #define _BSD_SOURCE /* For NI_MAXHOST */
@@ -52,11 +52,17 @@ static const char *config_keys[] =
 {
        "Host",
        "Port",
-       "ReverseLookups"
+       "ReverseLookups",
+       "IncludeUnitID"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-static int do_reverse_lookups = 1;
+static _Bool do_reverse_lookups = 1;
+
+/* This option only exists for backward compatibility. If it is false and two
+ * ntpd peers use the same refclock driver, the plugin will try to write
+ * simultaneous measurements from both to the same type instance. */
+static _Bool include_unit_id = 0;
 
 # define NTPD_DEFAULT_HOST "localhost"
 # define NTPD_DEFAULT_PORT "123"
@@ -283,6 +289,13 @@ static int ntpd_config (const char *key, const char *value)
                else
                        do_reverse_lookups = 0;
        }
+       else if (strcasecmp (key, "IncludeUnitID") == 0)
+       {
+               if (IS_TRUE (value))
+                       include_unit_id = 1;
+               else
+                       include_unit_id = 0;
+       }
        else
        {
                return (-1);
@@ -309,6 +322,18 @@ static void ntpd_submit (char *type, char *type_inst, double value)
        plugin_dispatch_values (&vl);
 }
 
+/* Each time a peer is polled, ntpd shifts the reach register to the left and
+ * sets the LSB based on whether the peer was reachable. If the LSB is zero,
+ * the values are out of date. */
+static void ntpd_submit_reach (char *type, char *type_inst, uint8_t reach,
+               double value)
+{
+       if (!(reach & 1))
+               value = NAN;
+
+       ntpd_submit (type, type_inst, value);
+}
+
 static int ntpd_connect (void)
 {
        char *host;
@@ -763,6 +788,108 @@ static double ntpd_read_fp (int32_t val_int)
        return (val_double);
 }
 
+static uint32_t ntpd_get_refclock_id (struct info_peer_summary const *peer_info)
+{
+       uint32_t addr = ntohl (peer_info->srcadr);
+       uint32_t refclock_id = (addr >> 8) & 0x00FF;
+
+       return (refclock_id);
+}
+
+static int ntpd_get_name_from_address (char *buffer, size_t buffer_size,
+               struct info_peer_summary const *peer_info, _Bool do_reverse_lookup)
+{
+       struct sockaddr_storage sa;
+       socklen_t sa_len;
+       int flags = 0;
+       int status;
+
+       memset (&sa, 0, sizeof (sa));
+
+       if (peer_info->v6_flag)
+       {
+               struct sockaddr_in6 sa6;
+
+               assert (sizeof (sa) >= sizeof (sa6));
+
+               memset (&sa6, 0, sizeof (sa6));
+               sa6.sin6_family = AF_INET6;
+               sa6.sin6_port = htons (123);
+               memcpy (&sa6.sin6_addr, &peer_info->srcadr6,
+                               sizeof (struct in6_addr));
+               sa_len = sizeof (sa6);
+
+               memcpy (&sa, &sa6, sizeof (sa6));
+       }
+       else
+       {
+               struct sockaddr_in sa4;
+
+               assert (sizeof (sa) >= sizeof (sa4));
+
+               memset (&sa4, 0, sizeof (sa4));
+               sa4.sin_family = AF_INET;
+               sa4.sin_port = htons (123);
+               memcpy (&sa4.sin_addr, &peer_info->srcadr,
+                               sizeof (struct in_addr));
+               sa_len = sizeof (sa4);
+
+               memcpy (&sa, &sa4, sizeof (sa4));
+       }
+
+       if (!do_reverse_lookup)
+               flags |= NI_NUMERICHOST;
+
+       status = getnameinfo ((struct sockaddr const *) &sa, sa_len,
+                       buffer, buffer_size,
+                       NULL, 0, /* No port name */
+                       flags);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("ntpd plugin: getnameinfo failed: %s",
+                               (status == EAI_SYSTEM)
+                               ? sstrerror (errno, errbuf, sizeof (errbuf))
+                               : gai_strerror (status));
+               return (-1);
+       }
+
+       return (0);
+} /* ntpd_get_name_from_address */
+
+static int ntpd_get_name_refclock (char *buffer, size_t buffer_size,
+               struct info_peer_summary const *peer_info)
+{
+       uint32_t refclock_id = ntpd_get_refclock_id (peer_info);
+       uint32_t unit_id = ntohl (peer_info->srcadr) & 0x00FF;
+
+       if (refclock_id >= refclock_names_num)
+               return (ntpd_get_name_from_address (buffer, buffer_size,
+                                       peer_info,
+                                       /* do_reverse_lookup = */ 0));
+
+       if (include_unit_id)
+               ssnprintf (buffer, buffer_size, "%s-%"PRIu32,
+                               refclock_names[refclock_id], unit_id);
+       else
+               sstrncpy (buffer, refclock_names[refclock_id], buffer_size);
+
+       return (0);
+} /* int ntpd_get_name_refclock */
+
+static int ntpd_get_name (char *buffer, size_t buffer_size,
+               struct info_peer_summary const *peer_info)
+{
+       uint32_t addr = ntohl (peer_info->srcadr);
+
+       if (!peer_info->v6_flag && ((addr & REFCLOCK_MASK) == REFCLOCK_ADDR))
+               return (ntpd_get_name_refclock (buffer, buffer_size,
+                                       peer_info));
+       else
+               return (ntpd_get_name_from_address (buffer, buffer_size,
+                                       peer_info, do_reverse_lookups));
+} /* int ntpd_addr_to_name */
+
 static int ntpd_read (void)
 {
        struct info_kernel *ik;
@@ -837,99 +964,26 @@ static int ntpd_read (void)
                double offset;
 
                char peername[NI_MAXHOST];
-               int refclock_id;
-               
-               ptr = ps + i;
-               refclock_id = 0;
+               uint32_t refclock_id;
 
-               /* Convert the `long floating point' offset value to double */
-               M_LFPTOD (ntohl (ptr->offset_int), ntohl (ptr->offset_frc), offset);
+               ptr = ps + i;
 
-               /* Special IP addresses for hardware clocks and stuff.. */
-               if (!ptr->v6_flag
-                               && ((ntohl (ptr->srcadr) & REFCLOCK_MASK)
-                                       == REFCLOCK_ADDR))
+               status = ntpd_get_name (peername, sizeof (peername), ptr);
+               if (status != 0)
                {
-                       struct in_addr  addr_obj;
-                       char *addr_str;
-
-                       refclock_id = (ntohl (ptr->srcadr) >> 8) & 0x000000FF;
-
-                       if (refclock_id < refclock_names_num)
-                       {
-                               sstrncpy (peername, refclock_names[refclock_id],
-                                               sizeof (peername));
-                       }
-                       else
-                       {
-                               memset ((void *) &addr_obj, '\0', sizeof (addr_obj));
-                               addr_obj.s_addr = ptr->srcadr;
-                               addr_str = inet_ntoa (addr_obj);
-
-                               sstrncpy (peername, addr_str, sizeof (peername));
-                       }
+                       ERROR ("ntpd plugin: Determining name of peer failed.");
+                       continue;
                }
-               else /* Normal network host. */
-               {
-                       struct sockaddr_storage sa;
-                       socklen_t sa_len;
-                       int flags = 0;
-
-                       memset (&sa, '\0', sizeof (sa));
-
-                       if (ptr->v6_flag)
-                       {
-                               struct sockaddr_in6 sa6;
-
-                               assert (sizeof (sa) >= sizeof (sa6));
-
-                               memset (&sa6, 0, sizeof (sa6));
-                               sa6.sin6_family = AF_INET6;
-                               sa6.sin6_port = htons (123);
-                               memcpy (&sa6.sin6_addr, &ptr->srcadr6,
-                                               sizeof (struct in6_addr));
-                               sa_len = sizeof (sa6);
-
-                               memcpy (&sa, &sa6, sizeof (sa6));
-                       }
-                       else
-                       {
-                               struct sockaddr_in sa4;
-
-                               assert (sizeof (sa) >= sizeof (sa4));
 
-                               memset (&sa4, 0, sizeof (sa4));
-                               sa4.sin_family = AF_INET;
-                               sa4.sin_port = htons (123);
-                               memcpy (&sa4.sin_addr, &ptr->srcadr,
-                                               sizeof (struct in_addr));
-                               sa_len = sizeof (sa4);
+               refclock_id = ntpd_get_refclock_id (ptr);
 
-                               memcpy (&sa, &sa4, sizeof (sa4));
-                       }
-
-                       if (do_reverse_lookups == 0)
-                               flags |= NI_NUMERICHOST;
-
-                       status = getnameinfo ((const struct sockaddr *) &sa,
-                                       sa_len,
-                                       peername, sizeof (peername),
-                                       NULL, 0, /* No port name */
-                                       flags);
-                       if (status != 0)
-                       {
-                               char errbuf[1024];
-                               ERROR ("ntpd plugin: getnameinfo failed: %s",
-                                               (status == EAI_SYSTEM)
-                                               ? sstrerror (errno, errbuf, sizeof (errbuf))
-                                               : gai_strerror (status));
-                               continue;
-                       }
-               }
+               /* Convert the `long floating point' offset value to double */
+               M_LFPTOD (ntohl (ptr->offset_int), ntohl (ptr->offset_frc), offset);
 
                DEBUG ("peer %i:\n"
                                "  peername   = %s\n"
                                "  srcadr     = 0x%08x\n"
+                               "  reach      = 0%03o\n"
                                "  delay      = %f\n"
                                "  offset_int = %i\n"
                                "  offset_frc = %i\n"
@@ -938,6 +992,7 @@ static int ntpd_read (void)
                                i,
                                peername,
                                ntohl (ptr->srcadr),
+                               ptr->reach,
                                ntpd_read_fp (ptr->delay),
                                ntohl (ptr->offset_int),
                                ntohl (ptr->offset_frc),
@@ -945,10 +1000,13 @@ static int ntpd_read (void)
                                ntpd_read_fp (ptr->dispersion));
 
                if (refclock_id != 1) /* not the system clock (offset will always be zero.. */
-                       ntpd_submit ("time_offset", peername, offset);
-               ntpd_submit ("time_dispersion", peername, ntpd_read_fp (ptr->dispersion));
+                       ntpd_submit_reach ("time_offset", peername, ptr->reach,
+                                       offset);
+               ntpd_submit_reach ("time_dispersion", peername, ptr->reach,
+                               ntpd_read_fp (ptr->dispersion));
                if (refclock_id == 0) /* not a reference clock */
-                       ntpd_submit ("delay", peername, ntpd_read_fp (ptr->delay));
+                       ntpd_submit_reach ("delay", peername, ptr->reach,
+                                       ntpd_read_fp (ptr->delay));
        }
 
        free (ps);
diff --git a/src/numa.c b/src/numa.c
new file mode 100644 (file)
index 0000000..4b4ef20
--- /dev/null
@@ -0,0 +1,168 @@
+/**
+ * collectd - src/numa.c
+ * Copyright (C) 2012  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 <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if !KERNEL_LINUX
+# error "No applicable input method."
+#endif
+
+#ifndef NUMA_ROOT_DIR
+# define NUMA_ROOT_DIR "/sys/devices/system/node"
+#endif
+
+static int max_node = -1;
+
+static void numa_dispatch_value (int node, /* {{{ */
+    const char *type_instance, value_t v)
+{
+  value_list_t vl = VALUE_LIST_INIT;
+
+  vl.values = &v;
+  vl.values_len = 1;
+
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "numa", sizeof (vl.plugin));
+  ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), "node%i", node);
+  sstrncpy (vl.type, "vmpage_action", sizeof (vl.type));
+  sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+  plugin_dispatch_values (&vl);
+} /* }}} void numa_dispatch_value */
+
+static int numa_read_node (int node) /* {{{ */
+{
+  char path[PATH_MAX];
+  FILE *fh;
+  char buffer[128];
+  int status;
+  int success;
+
+  ssnprintf (path, sizeof (path), NUMA_ROOT_DIR "/node%i/numastat", node);
+
+  fh = fopen (path, "r");
+  if (fh == NULL)
+  {
+    char errbuf[1024];
+    ERROR ("numa plugin: Reading node %i failed: open(%s): %s",
+        node, path, sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  success = 0;
+  while (fgets (buffer, sizeof (buffer), fh) != NULL)
+  {
+    char *fields[4];
+    value_t v;
+
+    status = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+    if (status != 2)
+    {
+      WARNING ("numa plugin: Ignoring line with unexpected "
+          "number of fields (node %i).", node);
+      continue;
+    }
+
+    v.derive = 0;
+    status = parse_value (fields[1], &v, DS_TYPE_DERIVE);
+    if (status != 0)
+      continue;
+
+    numa_dispatch_value (node, fields[0], v);
+    success++;
+  }
+
+  fclose (fh);
+  return (success ? 0 : -1);
+} /* }}} int numa_read_node */
+
+static int numa_read (void) /* {{{ */
+{
+  int i;
+  int status;
+  int success;
+
+  if (max_node < 0)
+  {
+    WARNING ("numa plugin: No NUMA nodes were detected.");
+    return (-1);
+  }
+
+  success = 0;
+  for (i = 0; i <= max_node; i++)
+  {
+    status = numa_read_node (i);
+    if (status == 0)
+      success++;
+  }
+
+  return (success ? 0 : -1);
+} /* }}} int numa_read */
+
+static int numa_init (void) /* {{{ */
+{
+  /* Determine the number of nodes on this machine. */
+  while (42)
+  {
+    char path[PATH_MAX];
+    struct stat statbuf;
+    int status;
+
+    ssnprintf (path, sizeof (path), NUMA_ROOT_DIR "/node%i", max_node + 1);
+    memset (&statbuf, 0, sizeof (statbuf));
+
+    status = stat (path, &statbuf);
+    if (status == 0)
+    {
+      max_node++;
+      continue;
+    }
+    else if (errno == ENOENT)
+    {
+      break;
+    }
+    else /* ((status != 0) && (errno != ENOENT)) */
+    {
+      char errbuf[1024];
+      ERROR ("numa plugin: stat(%s) failed: %s", path,
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      return (-1);
+    }
+  }
+
+  DEBUG ("numa plugin: Found %i nodes.", max_node + 1);
+  return (0);
+} /* }}} int numa_init */
+
+void module_register (void)
+{
+  plugin_register_init ("numa", numa_init);
+  plugin_register_read ("numa", numa_read);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et : */
index 4415b48..ab0812b 100644 (file)
@@ -59,6 +59,7 @@
 struct o_database_s
 {
   char *name;
+  char *host;
   char *connect_id;
   char *username;
   char *password;
@@ -86,6 +87,7 @@ OCIError *oci_error = NULL;
  * Functions
  */
 static void o_report_error (const char *where, /* {{{ */
+    const char *db_name, const char *query_name,
     const char *what, OCIError *eh)
 {
   char buffer[2048];
@@ -93,6 +95,11 @@ static void o_report_error (const char *where, /* {{{ */
   int status;
   unsigned int record_number;
 
+  if (db_name == NULL)
+    db_name = "(none)";
+  if (query_name == NULL)
+    query_name = "(none)";
+
   /* An operation may cause / return multiple errors. Loop until we have
    * handled all errors available (with a fail-save limit of 16). */
   for (record_number = 1; record_number <= 16; record_number++)
@@ -122,12 +129,14 @@ static void o_report_error (const char *where, /* {{{ */
         buffer[buffer_length] = 0;
       }
 
-      ERROR ("oracle plugin: %s: %s failed: %s", where, what, buffer);
+      ERROR ("oracle plugin: %s (db = %s, query = %s): %s failed: %s",
+          where, db_name, query_name, what, buffer);
     }
     else
     {
-      ERROR ("oracle plugin: %s: %s failed. Additionally, OCIErrorGet failed with status %i.",
-          where, what, status);
+      ERROR ("oracle plugin: %s (db = %s, query = %s): %s failed. "
+          "Additionally, OCIErrorGet failed with status %i.",
+          where, db_name, query_name, what, status);
       return;
     }
   }
@@ -175,33 +184,6 @@ static void o_database_free (o_database_t *db) /* {{{ */
  * </Plugin>
  */
 
-static int o_config_set_string (char **ret_string, /* {{{ */
-    oconfig_item_t *ci)
-{
-  char *string;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("oracle plugin: The `%s' config option "
-        "needs exactly one string argument.", ci->key);
-    return (-1);
-  }
-
-  string = strdup (ci->values[0].value.string);
-  if (string == NULL)
-  {
-    ERROR ("oracle plugin: strdup failed.");
-    return (-1);
-  }
-
-  if (*ret_string != NULL)
-    free (*ret_string);
-  *ret_string = string;
-
-  return (0);
-} /* }}} int o_config_set_string */
-
 static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
 {
   o_database_t *db;
@@ -223,8 +205,13 @@ static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
   memset (db, 0, sizeof (*db));
+  db->name = NULL;
+  db->host = NULL;
+  db->connect_id = NULL;
+  db->username = NULL;
+  db->password = NULL;
 
-  status = o_config_set_string (&db->name, ci);
+  status = cf_util_get_string (ci, &db->name);
   if (status != 0)
   {
     sfree (db);
@@ -237,11 +224,13 @@ static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("ConnectID", child->key) == 0)
-      status = o_config_set_string (&db->connect_id, child);
+      status = cf_util_get_string (child, &db->connect_id);
+    else if (strcasecmp ("Host", child->key) == 0)
+      status = cf_util_get_string (child, &db->host);
     else if (strcasecmp ("Username", child->key) == 0)
-      status = o_config_set_string (&db->username, child);
+      status = cf_util_get_string (child, &db->username);
     else if (strcasecmp ("Password", child->key) == 0)
-      status = o_config_set_string (&db->password, child);
+      status = cf_util_get_string (child, &db->password);
     else if (strcasecmp ("Query", child->key) == 0)
       status = udb_query_pick_from_list (child, queries, queries_num,
           &db->queries, &db->queries_num);
@@ -427,7 +416,8 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
         OCI_HTYPE_STMT, /* user_data_size = */ 0, /* user_data = */ NULL);
     if (status != OCI_SUCCESS)
     {
-      o_report_error ("o_read_database_query", "OCIHandleAlloc", oci_error);
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIHandleAlloc", oci_error);
       oci_statement = NULL;
       return (-1);
     }
@@ -438,7 +428,8 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
         /* mode     = */ OCI_DEFAULT);
     if (status != OCI_SUCCESS)
     {
-      o_report_error ("o_read_database_query", "OCIStmtPrepare", oci_error);
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIStmtPrepare", oci_error);
       OCIHandleFree (oci_statement, OCI_HTYPE_STMT);
       oci_statement = NULL;
       return (-1);
@@ -462,10 +453,8 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
       /* mode = */ OCI_DEFAULT);
   if (status != OCI_SUCCESS)
   {
-    DEBUG ("oracle plugin: o_read_database_query: status = %i (%#x)", status, status);
-    o_report_error ("o_read_database_query", "OCIStmtExecute", oci_error);
-    ERROR ("oracle plugin: o_read_database_query: "
-        "Failing statement was: %s", udb_query_get_statement (q));
+    o_report_error ("o_read_database_query", db->name, udb_query_get_name (q),
+        "OCIStmtExecute", oci_error);
     return (-1);
   } /* }}} */
 
@@ -478,7 +467,8 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
         OCI_ATTR_PARAM_COUNT, oci_error);
     if (status != OCI_SUCCESS)
     {
-      o_report_error ("o_read_database_query", "OCIAttrGet", oci_error);
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIAttrGet", oci_error);
       return (-1);
     } /* }}} */
 
@@ -556,8 +546,10 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
     if (status != OCI_SUCCESS)
     {
       /* This is probably alright */
-      DEBUG ("oracle plugin: o_read_database_query: status = %#x (= %i);", status, status);
-      o_report_error ("o_read_database_query", "OCIParamGet", oci_error);
+      DEBUG ("oracle plugin: o_read_database_query: status = %#x (= %i);",
+          status, status);
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIParamGet", oci_error);
       status = OCI_SUCCESS;
       break;
     }
@@ -569,8 +561,8 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
     if (status != OCI_SUCCESS)
     {
       OCIDescriptorFree (oci_param, OCI_DTYPE_PARAM);
-      o_report_error ("o_read_database_query", "OCIAttrGet (OCI_ATTR_NAME)",
-          oci_error);
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIAttrGet (OCI_ATTR_NAME)", oci_error);
       continue;
     }
 
@@ -595,13 +587,15 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
         NULL, NULL, NULL, OCI_DEFAULT);
     if (status != OCI_SUCCESS)
     {
-      o_report_error ("o_read_database_query", "OCIDefineByPos", oci_error);
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIDefineByPos", oci_error);
       continue;
     }
   } /* for (j = 1; j <= param_counter; j++) */
   /* }}} End of the ``define'' stuff. */
 
-  status = udb_query_prepare_result (q, prep_area, hostname_g,
+  status = udb_query_prepare_result (q, prep_area,
+      (db->host != NULL) ? db->host : hostname_g,
       /* plugin = */ "oracle", db->name, column_names, column_num,
       /* interval = */ 0);
   if (status != 0)
@@ -626,7 +620,8 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
     }
     else if ((status != OCI_SUCCESS) && (status != OCI_SUCCESS_WITH_INFO))
     {
-      o_report_error ("o_read_database_query", "OCIStmtFetch2", oci_error);
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIStmtFetch2", oci_error);
       break;
     }
 
@@ -663,7 +658,8 @@ static int o_read_database (o_database_t *db) /* {{{ */
         OCI_ATTR_SERVER, oci_error);
     if (status != OCI_SUCCESS)
     {
-      o_report_error ("o_read_database", "OCIAttrGet", oci_error);
+      o_report_error ("o_read_database", db->name, NULL, "OCIAttrGet",
+          oci_error);
       return (-1);
     }
 
@@ -679,7 +675,8 @@ static int o_read_database (o_database_t *db) /* {{{ */
           OCI_ATTR_SERVER_STATUS, oci_error);
       if (status != OCI_SUCCESS)
       {
-        o_report_error ("o_read_database", "OCIAttrGet", oci_error);
+        o_report_error ("o_read_database", db->name, NULL, "OCIAttrGet",
+            oci_error);
         return (-1);
       }
     }
@@ -702,7 +699,11 @@ static int o_read_database (o_database_t *db) /* {{{ */
         (OraText *) db->connect_id, (ub4) strlen (db->connect_id));
     if ((status != OCI_SUCCESS) && (status != OCI_SUCCESS_WITH_INFO))
     {
-      o_report_error ("o_read_database", "OCILogon", oci_error);
+      char errfunc[256];
+
+      ssnprintf (errfunc, sizeof (errfunc), "OCILogon(\"%s\")", db->connect_id);
+
+      o_report_error ("o_read_database", db->name, NULL, errfunc, oci_error);
       DEBUG ("oracle plugin: OCILogon (%s): db->oci_service_context = %p;",
           db->connect_id, db->oci_service_context);
       db->oci_service_context = NULL;
index 57ea712..78e508a 100644 (file)
@@ -1946,6 +1946,11 @@ static int perl_read (void)
                aTHX = t->interp;
        }
 
+       /* Assert that we're not running as the base thread. Otherwise, we might
+        * run into concurrency issues with c_ithread_create(). See
+        * https://github.com/collectd/collectd/issues/9 for details. */
+       assert (aTHX != perl_threads->head->interp);
+
        log_debug ("perl_read: c_ithread: interp = %p (active threads: %i)",
                        aTHX, perl_threads->number_of_threads);
        return pplugin_call_all (aTHX_ PLUGIN_READ);
@@ -1954,6 +1959,7 @@ static int perl_read (void)
 static int perl_write (const data_set_t *ds, const value_list_t *vl,
                user_data_t __attribute__((unused)) *user_data)
 {
+       int status;
        dTHX;
 
        if (NULL == perl_threads)
@@ -1969,9 +1975,20 @@ static int perl_write (const data_set_t *ds, const value_list_t *vl,
                aTHX = t->interp;
        }
 
+       /* Lock the base thread if this is not called from one of the read threads
+        * to avoid race conditions with c_ithread_create(). See
+        * https://github.com/collectd/collectd/issues/9 for details. */
+       if (aTHX == perl_threads->head->interp)
+               pthread_mutex_lock (&perl_threads->mutex);
+
        log_debug ("perl_write: c_ithread: interp = %p (active threads: %i)",
                        aTHX, perl_threads->number_of_threads);
-       return pplugin_call_all (aTHX_ PLUGIN_WRITE, ds, vl);
+       status = pplugin_call_all (aTHX_ PLUGIN_WRITE, ds, vl);
+
+       if (aTHX == perl_threads->head->interp)
+               pthread_mutex_unlock (&perl_threads->mutex);
+
+       return status;
 } /* static int perl_write (const data_set_t *, const value_list_t *) */
 
 static void perl_log (int level, const char *msg,
@@ -1992,7 +2009,17 @@ static void perl_log (int level, const char *msg,
                aTHX = t->interp;
        }
 
+       /* Lock the base thread if this is not called from one of the read threads
+        * to avoid race conditions with c_ithread_create(). See
+        * https://github.com/collectd/collectd/issues/9 for details. */
+       if (aTHX == perl_threads->head->interp)
+               pthread_mutex_lock (&perl_threads->mutex);
+
        pplugin_call_all (aTHX_ PLUGIN_LOG, level, msg);
+
+       if (aTHX == perl_threads->head->interp)
+               pthread_mutex_unlock (&perl_threads->mutex);
+
        return;
 } /* static void perl_log (int, const char *) */
 
index 48a2431..6879733 100644 (file)
@@ -617,7 +617,7 @@ static int plugin_config (oconfig_item_t *ci) /* {{{ */
     if (strcasecmp ("Address", child->key) == 0)
       cf_util_get_string (child, &conf_node);
     else if (strcasecmp ("Port", child->key) == 0)
-      cf_util_get_string (child, &conf_service);
+      cf_util_get_service (child, &conf_service);
     else if (strcasecmp ("View", child->key) == 0)
       pinba_config_view (child);
     else
index 417a7ac..8bbb807 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/ping.c
- * Copyright (C) 2005-2009  Florian octo Forster
+ * Copyright (C) 2005-2012  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
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
+#include "utils_complain.h"
 
 #include <pthread.h>
 #include <netinet/in.h>
@@ -142,6 +143,97 @@ static void time_calc (struct timespec *ts_dest, /* {{{ */
   time_normalize (ts_dest);
 } /* }}} void time_calc */
 
+static int ping_dispatch_all (pingobj_t *pingobj) /* {{{ */
+{
+  pingobj_iter_t *iter;
+  hostlist_t *hl;
+  int status;
+
+  for (iter = ping_iterator_get (pingobj);
+      iter != NULL;
+      iter = ping_iterator_next (iter))
+  { /* {{{ */
+    char userhost[NI_MAXHOST];
+    double latency;
+    size_t param_size;
+
+    param_size = sizeof (userhost);
+    status = ping_iterator_get_info (iter,
+#ifdef PING_INFO_USERNAME
+        PING_INFO_USERNAME,
+#else
+        PING_INFO_HOSTNAME,
+#endif
+        userhost, &param_size);
+    if (status != 0)
+    {
+      WARNING ("ping plugin: ping_iterator_get_info failed: %s",
+          ping_get_error (pingobj));
+      continue;
+    }
+
+    for (hl = hostlist_head; hl != NULL; hl = hl->next)
+      if (strcmp (userhost, hl->host) == 0)
+        break;
+
+    if (hl == NULL)
+    {
+      WARNING ("ping plugin: Cannot find host %s.", userhost);
+      continue;
+    }
+
+    param_size = sizeof (latency);
+    status = ping_iterator_get_info (iter, PING_INFO_LATENCY,
+        (void *) &latency, &param_size);
+    if (status != 0)
+    {
+      WARNING ("ping plugin: ping_iterator_get_info failed: %s",
+          ping_get_error (pingobj));
+      continue;
+    }
+
+    hl->pkg_sent++;
+    if (latency >= 0.0)
+    {
+      hl->pkg_recv++;
+      hl->latency_total += latency;
+      hl->latency_squared += (latency * latency);
+
+      /* reset missed packages counter */
+      hl->pkg_missed = 0;
+    } else
+      hl->pkg_missed++;
+
+    /* if the host did not answer our last N packages, trigger a resolv. */
+    if (ping_max_missed >= 0 && hl->pkg_missed >= ping_max_missed)
+    { /* {{{ */
+      /* we reset the missed package counter here, since we only want to
+       * trigger a resolv every N packages and not every package _AFTER_ N
+       * missed packages */
+      hl->pkg_missed = 0;
+
+      WARNING ("ping plugin: host %s has not answered %d PING requests,"
+          " triggering resolve", hl->host, ping_max_missed);
+
+      /* we trigger the resolv simply be removeing and adding the host to our
+       * ping object */
+      status = ping_host_remove (pingobj, hl->host);
+      if (status != 0)
+      {
+        WARNING ("ping plugin: ping_host_remove (%s) failed.", hl->host);
+      }
+      else
+      {
+        status = ping_host_add (pingobj, hl->host);
+        if (status != 0)
+          ERROR ("ping plugin: ping_host_add (%s) failed.", hl->host);
+      }
+    } /* }}} ping_max_missed */
+  } /* }}} for (iter) */
+
+  return (0);
+} /* }}} int ping_dispatch_all */
+
 static void *ping_thread (void *arg) /* {{{ */
 {
   static pingobj_t *pingobj = NULL;
@@ -154,6 +246,8 @@ static void *ping_thread (void *arg) /* {{{ */
   hostlist_t *hl;
   int count;
 
+  c_complain_t complaint = C_COMPLAIN_INIT_STATIC;
+
   pthread_mutex_lock (&ping_lock);
 
   pingobj = ping_construct ();
@@ -213,8 +307,8 @@ static void *ping_thread (void *arg) /* {{{ */
 
   while (ping_thread_loop > 0)
   {
-    pingobj_iter_t *iter;
     int status;
+    _Bool send_successful = 0;
 
     if (gettimeofday (&tv_begin, NULL) < 0)
     {
@@ -230,10 +324,13 @@ static void *ping_thread (void *arg) /* {{{ */
     status = ping_send (pingobj);
     if (status < 0)
     {
-      ERROR ("ping plugin: ping_send failed: %s", ping_get_error (pingobj));
-      pthread_mutex_lock (&ping_lock);
-      ping_thread_error = 1;
-      break;
+      c_complain (LOG_ERR, &complaint, "ping plugin: ping_send failed: %s",
+          ping_get_error (pingobj));
+    }
+    else
+    {
+      c_release (LOG_NOTICE, &complaint, "ping plugin: ping_send succeeded.");
+      send_successful = 1;
     }
 
     pthread_mutex_lock (&ping_lock);
@@ -241,87 +338,8 @@ static void *ping_thread (void *arg) /* {{{ */
     if (ping_thread_loop <= 0)
       break;
 
-    for (iter = ping_iterator_get (pingobj);
-        iter != NULL;
-        iter = ping_iterator_next (iter))
-    { /* {{{ */
-      char userhost[NI_MAXHOST];
-      double latency;
-      size_t param_size;
-
-      param_size = sizeof (userhost);
-      status = ping_iterator_get_info (iter,
-#ifdef PING_INFO_USERNAME
-          PING_INFO_USERNAME,
-#else
-          PING_INFO_HOSTNAME,
-#endif
-          userhost, &param_size);
-      if (status != 0)
-      {
-        WARNING ("ping plugin: ping_iterator_get_info failed: %s",
-            ping_get_error (pingobj));
-        continue;
-      }
-
-      for (hl = hostlist_head; hl != NULL; hl = hl->next)
-        if (strcmp (userhost, hl->host) == 0)
-          break;
-
-      if (hl == NULL)
-      {
-        WARNING ("ping plugin: Cannot find host %s.", userhost);
-        continue;
-      }
-
-      param_size = sizeof (latency);
-      status = ping_iterator_get_info (iter, PING_INFO_LATENCY,
-          (void *) &latency, &param_size);
-      if (status != 0)
-      {
-        WARNING ("ping plugin: ping_iterator_get_info failed: %s",
-            ping_get_error (pingobj));
-        continue;
-      }
-
-      hl->pkg_sent++;
-      if (latency >= 0.0)
-      {
-        hl->pkg_recv++;
-        hl->latency_total += latency;
-        hl->latency_squared += (latency * latency);
-
-        /* reset missed packages counter */
-        hl->pkg_missed = 0;
-      } else
-        hl->pkg_missed++;
-
-      /* if the host did not answer our last N packages, trigger a resolv. */
-      if (ping_max_missed >= 0 && hl->pkg_missed >= ping_max_missed)
-      { /* {{{ */
-        /* we reset the missed package counter here, since we only want to
-         * trigger a resolv every N packages and not every package _AFTER_ N
-         * missed packages */
-        hl->pkg_missed = 0;
-
-        WARNING ("ping plugin: host %s has not answered %d PING requests,"
-          " triggering resolve", hl->host, ping_max_missed);
-
-        /* we trigger the resolv simply be removeing and adding the host to our
-         * ping object */
-        status = ping_host_remove (pingobj, hl->host);
-        if (status != 0)
-        {
-          WARNING ("ping plugin: ping_host_remove (%s) failed.", hl->host);
-        }
-        else
-        {
-          status = ping_host_add (pingobj, hl->host);
-          if (status != 0)
-            WARNING ("ping plugin: ping_host_add (%s) failed.", hl->host);
-        }
-      } /* }}} ping_max_missed */
-    } /* }}} for (iter) */
+    if (send_successful)
+      (void) ping_dispatch_all (pingobj);
 
     if (gettimeofday (&tv_end, NULL) < 0)
     {
index f042b12..c69046c 100644 (file)
@@ -1812,6 +1812,44 @@ void plugin_log (int level, const char *format, ...)
        }
 } /* void plugin_log */
 
+int parse_log_severity (const char *severity)
+{
+       int log_level = -1;
+
+       if ((0 == strcasecmp (severity, "emerg"))
+                       || (0 == strcasecmp (severity, "alert"))
+                       || (0 == strcasecmp (severity, "crit"))
+                       || (0 == strcasecmp (severity, "err")))
+               log_level = LOG_ERR;
+       else if (0 == strcasecmp (severity, "warning"))
+               log_level = LOG_WARNING;
+       else if (0 == strcasecmp (severity, "notice"))
+               log_level = LOG_NOTICE;
+       else if (0 == strcasecmp (severity, "info"))
+               log_level = LOG_INFO;
+#if COLLECT_DEBUG
+       else if (0 == strcasecmp (severity, "debug"))
+               log_level = LOG_DEBUG;
+#endif /* COLLECT_DEBUG */
+
+       return (log_level);
+} /* int parse_log_severity */
+
+int parse_notif_severity (const char *severity)
+{
+       int notif_severity = -1;
+
+       if (strcasecmp (severity, "FAILURE") == 0)
+               notif_severity = NOTIF_FAILURE;
+       else if (strcmp (severity, "OKAY") == 0)
+               notif_severity = NOTIF_OKAY;
+       else if ((strcmp (severity, "WARNING") == 0)
+                       || (strcmp (severity, "WARN") == 0))
+               notif_severity = NOTIF_WARNING;
+
+       return (notif_severity);
+} /* int parse_notif_severity */
+
 const data_set_t *plugin_get_ds (const char *name)
 {
        data_set_t *ds;
index 3f2d98c..0f35de5 100644 (file)
@@ -335,6 +335,10 @@ int plugin_dispatch_notification (const notification_t *notif);
 void plugin_log (int level, const char *format, ...)
        __attribute__ ((format(printf,2,3)));
 
+/* These functions return the parsed severity or less than zero on failure. */
+int parse_log_severity (const char *severity);
+int parse_notif_severity (const char *severity);
+
 #define ERROR(...)   plugin_log (LOG_ERR,     __VA_ARGS__)
 #define WARNING(...) plugin_log (LOG_WARNING, __VA_ARGS__)
 #define NOTICE(...)  plugin_log (LOG_NOTICE,  __VA_ARGS__)
index 953bcd8..a72109a 100644 (file)
@@ -95,6 +95,7 @@ typedef enum {
        C_PSQL_PARAM_DB,
        C_PSQL_PARAM_USER,
        C_PSQL_PARAM_INTERVAL,
+       C_PSQL_PARAM_INSTANCE,
 } c_psql_param_t;
 
 /* Parameter configuration. Stored as `user data' in the query objects. */
@@ -125,6 +126,8 @@ typedef struct {
        char *user;
        char *password;
 
+       char *instance;
+
        char *sslmode;
 
        char *krbsrvname;
@@ -177,6 +180,8 @@ static c_psql_database_t *c_psql_database_new (const char *name)
        db->user       = NULL;
        db->password   = NULL;
 
+       db->instance   = sstrdup (name);
+
        db->sslmode    = NULL;
 
        db->krbsrvname = NULL;
@@ -208,6 +213,8 @@ static void c_psql_database_delete (void *data)
        sfree (db->user);
        sfree (db->password);
 
+       sfree (db->instance);
+
        sfree (db->sslmode);
 
        sfree (db->krbsrvname);
@@ -271,8 +278,9 @@ static int c_psql_check_connection (c_psql_database_t *db)
 
                if (CONNECTION_OK != PQstatus (db->conn)) {
                        c_complain (LOG_ERR, &db->conn_complaint,
-                                       "Failed to connect to database %s: %s",
-                                       db->database, PQerrorMessage (db->conn));
+                                       "Failed to connect to database %s (%s): %s",
+                                       db->database, db->instance,
+                                       PQerrorMessage (db->conn));
                        return -1;
                }
 
@@ -341,6 +349,9 @@ static PGresult *c_psql_exec_query_params (c_psql_database_t *db,
                                                : plugin_get_interval ());
                                params[i] = interval;
                                break;
+                       case C_PSQL_PARAM_INSTANCE:
+                               params[i] = db->instance;
+                               break;
                        default:
                                assert (0);
                }
@@ -378,9 +389,10 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
        else if ((NULL == data) || (0 == data->params_num))
                res = c_psql_exec_query_noparams (db, q);
        else {
-               log_err ("Connection to database \"%s\" does not support parameters "
-                               "(protocol version %d) - cannot execute query \"%s\".",
-                               db->database, db->proto_version,
+               log_err ("Connection to database \"%s\" (%s) does not support "
+                               "parameters (protocol version %d) - "
+                               "cannot execute query \"%s\".",
+                               db->database, db->instance, db->proto_version,
                                udb_query_get_name (q));
                return -1;
        }
@@ -437,7 +449,7 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
                host = db->host;
 
        status = udb_query_prepare_result (q, prep_area, host, "postgresql",
-                       db->database, column_names, (size_t) column_num, db->interval);
+                       db->instance, column_names, (size_t) column_num, db->interval);
        if (0 != status) {
                log_err ("udb_query_prepare_result failed with status %i.",
                                status);
@@ -488,6 +500,7 @@ static int c_psql_read (user_data_t *ud)
        db = ud->data;
 
        assert (NULL != db->database);
+       assert (NULL != db->instance);
 
        if (0 != c_psql_check_connection (db))
                return -1;
@@ -524,19 +537,6 @@ static int c_psql_shutdown (void)
        return 0;
 } /* c_psql_shutdown */
 
-static int config_set_s (char *name, char **var, const oconfig_item_t *ci)
-{
-       if ((0 != ci->children_num) || (1 != ci->values_num)
-                       || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
-               log_err ("%s expects a single string argument.", name);
-               return 1;
-       }
-
-       sfree (*var);
-       *var = sstrdup (ci->values[0].value.string);
-       return 0;
-} /* config_set_s */
-
 static int config_query_param_add (udb_query_t *q, oconfig_item_t *ci)
 {
        c_psql_user_data_t *data;
@@ -572,6 +572,8 @@ static int config_query_param_add (udb_query_t *q, oconfig_item_t *ci)
                data->params[data->params_num] = C_PSQL_PARAM_USER;
        else if (0 == strcasecmp (param_str, "interval"))
                data->params[data->params_num] = C_PSQL_PARAM_INTERVAL;
+       else if (0 == strcasecmp (param_str, "instance"))
+               data->params[data->params_num] = C_PSQL_PARAM_INSTANCE;
        else {
                log_err ("Invalid parameter \"%s\".", param_str);
                return 1;
@@ -619,19 +621,21 @@ static int c_psql_config_database (oconfig_item_t *ci)
                oconfig_item_t *c = ci->children + i;
 
                if (0 == strcasecmp (c->key, "Host"))
-                       config_set_s ("Host", &db->host, c);
+                       cf_util_get_string (c, &db->host);
                else if (0 == strcasecmp (c->key, "Port"))
-                       config_set_s ("Port", &db->port, c);
+                       cf_util_get_service (c, &db->port);
                else if (0 == strcasecmp (c->key, "User"))
-                       config_set_s ("User", &db->user, c);
+                       cf_util_get_string (c, &db->user);
                else if (0 == strcasecmp (c->key, "Password"))
-                       config_set_s ("Password", &db->password, c);
+                       cf_util_get_string (c, &db->password);
+               else if (0 == strcasecmp (c->key, "Instance"))
+                       cf_util_get_string (c, &db->instance);
                else if (0 == strcasecmp (c->key, "SSLMode"))
-                       config_set_s ("SSLMode", &db->sslmode, c);
+                       cf_util_get_string (c, &db->sslmode);
                else if (0 == strcasecmp (c->key, "KRBSrvName"))
-                       config_set_s ("KRBSrvName", &db->krbsrvname, c);
+                       cf_util_get_string (c, &db->krbsrvname);
                else if (0 == strcasecmp (c->key, "Service"))
-                       config_set_s ("Service", &db->service, c);
+                       cf_util_get_string (c, &db->service);
                else if (0 == strcasecmp (c->key, "Query"))
                        udb_query_pick_from_list (c, queries, queries_num,
                                        &db->queries, &db->queries_num);
@@ -679,7 +683,7 @@ static int c_psql_config_database (oconfig_item_t *ci)
        ud.data = db;
        ud.free_func = c_psql_database_delete;
 
-       ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->database);
+       ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->instance);
 
        CDTIME_T_TO_TIMESPEC (db->interval, &cb_interval);
 
index 8f4eb88..c77859d 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 2009       Andrés J. Díaz
  * Copyright (C) 2009       Manuel Sanmartin
  * Copyright (C) 2010       Clément Stenac
+ * Copyright (C) 2012       Cosmin Ioiart
  *
  * 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
@@ -30,6 +31,7 @@
  *   Andrés J. Díaz <ajdiaz at connectical.com>
  *   Manuel Sanmartin
  *   Clément Stenac <clement.stenac at diwi.org>
+ *   Cosmin Ioiart <cioiart at gmail.com>
  **/
 
 #include "collectd.h"
 #define MAXARGLN 1024
 /* #endif HAVE_PROCINFO_H */
 
+#elif KERNEL_SOLARIS
+# include <procfs.h>
+# include <dirent.h>
+/* #endif KERNEL_SOLARIS */
+
 #else
 # error "No applicable input method."
 #endif
 # include <regex.h>
 #endif
 
+#if HAVE_KSTAT_H
+# include <kstat.h>
+#endif
+
 #ifndef ARG_MAX
 #  define ARG_MAX 4096
 #endif
@@ -200,7 +211,7 @@ static long pagesize_g;
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
-/* no global variables */
+static int pagesize;
 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
 
 #elif HAVE_PROCINFO_H
@@ -216,8 +227,8 @@ int getargs (struct procentry64 *processBuffer, int bufferLen, char *argsBuffer,
 #endif /* HAVE_PROCINFO_H */
 
 /* put name of process from config to list_head_g tree
  list_head_g is a list of 'procstat_t' structs with
  processes names we want to watch */
* list_head_g is a list of 'procstat_t' structs with
* processes names we want to watch */
 static void ps_list_register (const char *name, const char *regexp)
 {
        procstat_t *new;
@@ -609,7 +620,7 @@ static int ps_init (void)
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
-/* no initialization */
+       pagesize = getpagesize();
 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
 
 #elif HAVE_PROCINFO_H
@@ -712,7 +723,7 @@ static void ps_submit_proc_list (procstat_t *ps)
        }
 
        DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; "
-                        "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
+                       "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
                        "vmem_code = %lu; "
                        "vmem_minflt_counter = %"PRIi64"; vmem_majflt_counter = %"PRIi64"; "
                        "cpu_user_counter = %"PRIi64"; cpu_system_counter = %"PRIi64"; "
@@ -726,6 +737,26 @@ static void ps_submit_proc_list (procstat_t *ps)
                        ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
 } /* void ps_submit_proc_list */
 
+#if KERNEL_LINUX || KERNEL_SOLARIS
+static void ps_submit_fork_rate (derive_t value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].derive = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       sstrncpy(vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy(vl.plugin, "processes", sizeof (vl.plugin));
+       sstrncpy(vl.plugin_instance, "", sizeof (vl.plugin_instance));
+       sstrncpy(vl.type, "fork_rate", sizeof (vl.type));
+       sstrncpy(vl.type_instance, "", sizeof (vl.type_instance));
+
+       plugin_dispatch_values(&vl);
+}
+#endif /* KERNEL_LINUX || KERNEL_SOLARIS*/
+
 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
 #if KERNEL_LINUX
 static int ps_read_tasks (int pid)
@@ -780,7 +811,7 @@ static procstat_t *ps_read_vmem (int pid, procstat_t *ps)
                        continue;
 
                numfields = strsplit (buffer, fields,
-                                      STATIC_ARRAY_SIZE (fields));
+                               STATIC_ARRAY_SIZE (fields));
 
                if (numfields < 2)
                        continue;
@@ -790,7 +821,7 @@ static procstat_t *ps_read_vmem (int pid, procstat_t *ps)
                tmp = strtoll (fields[1], &endptr, /* base = */ 10);
                if ((errno == 0) && (endptr != fields[1]))
                {
-                       if (strncmp (buffer, "VmData", 6) == 0) 
+                       if (strncmp (buffer, "VmData", 6) == 0)
                        {
                                data = tmp;
                        }
@@ -881,9 +912,12 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        char *fields[64];
        char  fields_len;
 
-       int   i;
+       int   buffer_len;
 
-       int   name_len;
+       char *buffer_ptr;
+       size_t name_start_pos;
+       size_t name_end_pos;
+       size_t name_len;
 
        derive_t cpu_user_counter;
        derive_t cpu_system_counter;
@@ -895,33 +929,56 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
 
        ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
 
-       i = read_file_contents (filename, buffer, sizeof(buffer) - 1);
-       if (i <= 0)
+       buffer_len = read_file_contents (filename,
+                       buffer, sizeof(buffer) - 1);
+       if (buffer_len <= 0)
                return (-1);
-       buffer[i] = 0;
-
-       fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
-       if (fields_len < 24)
+       buffer[buffer_len] = 0;
+
+       /* The name of the process is enclosed in parens. Since the name can
+        * contain parens itself, spaces, numbers and pretty much everything
+        * else, use these to determine the process name. We don't use
+        * strchr(3) and strrchr(3) to avoid pointer arithmetic which would
+        * otherwise be required to determine name_len. */
+       name_start_pos = 0;
+       while ((buffer[name_start_pos] != '(')
+                       && (name_start_pos < buffer_len))
+               name_start_pos++;
+
+       name_end_pos = buffer_len;
+       while ((buffer[name_end_pos] != ')')
+                       && (name_end_pos > 0))
+               name_end_pos--;
+
+       /* Either '(' or ')' is not found or they are in the wrong order.
+        * Anyway, something weird that shouldn't happen ever. */
+       if (name_start_pos >= name_end_pos)
        {
-               DEBUG ("processes plugin: ps_read_process (pid = %i):"
-                               " `%s' has only %i fields..",
-                               (int) pid, filename, fields_len);
+               ERROR ("processes plugin: name_start_pos = %zu >= name_end_pos = %zu",
+                               name_start_pos, name_end_pos);
                return (-1);
        }
 
-       /* copy the name, strip brackets in the process */
-       name_len = strlen (fields[1]) - 2;
-       if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
+       name_len = (name_end_pos - name_start_pos) - 1;
+       if (name_len >= sizeof (ps->name))
+               name_len = sizeof (ps->name) - 1;
+
+       sstrncpy (ps->name, &buffer[name_start_pos + 1], name_len + 1);
+
+       if ((buffer_len - name_end_pos) < 2)
+               return (-1);
+       buffer_ptr = &buffer[name_end_pos + 2];
+
+       fields_len = strsplit (buffer_ptr, fields, STATIC_ARRAY_SIZE (fields));
+       if (fields_len < 22)
        {
-               DEBUG ("No brackets found in process name: `%s'", fields[1]);
+               DEBUG ("processes plugin: ps_read_process (pid = %i):"
+                               " `%s' has only %i fields..",
+                               (int) pid, filename, fields_len);
                return (-1);
        }
-       fields[1] = fields[1] + 1;
-       fields[1][name_len] = '\0';
-       strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
 
-
-       *state = fields[2][0];
+       *state = fields[0][0];
 
        if (*state == 'Z')
        {
@@ -946,16 +1003,16 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
                return (0);
        }
 
-       cpu_user_counter   = atoll (fields[13]);
-       cpu_system_counter = atoll (fields[14]);
-       vmem_size          = atoll (fields[22]);
-       vmem_rss           = atoll (fields[23]);
-       ps->vmem_minflt_counter = atoll (fields[9]);
-       ps->vmem_majflt_counter = atoll (fields[11]);
+       cpu_user_counter   = atoll (fields[11]);
+       cpu_system_counter = atoll (fields[12]);
+       vmem_size          = atoll (fields[20]);
+       vmem_rss           = atoll (fields[21]);
+       ps->vmem_minflt_counter = atol (fields[7]);
+       ps->vmem_majflt_counter = atol (fields[9]);
 
        {
-               unsigned long long stack_start = atoll (fields[27]);
-               unsigned long long stack_ptr   = atoll (fields[28]);
+               unsigned long long stack_start = atoll (fields[25]);
+               unsigned long long stack_ptr   = atoll (fields[26]);
 
                stack_size = (stack_start > stack_ptr)
                        ? stack_start - stack_ptr
@@ -1091,70 +1148,226 @@ static char *ps_get_cmdline (pid_t pid, char *name, char *buf, size_t buf_len)
        return buf;
 } /* char *ps_get_cmdline (...) */
 
-static unsigned long read_fork_rate ()
+static int read_fork_rate ()
 {
        FILE *proc_stat;
-       char buf[1024];
-       unsigned long result = 0;
-       int numfields;
-       char *fields[3];
+       char buffer[1024];
+       value_t value;
+       _Bool value_valid = 0;
 
-       proc_stat = fopen("/proc/stat", "r");
-       if (proc_stat == NULL) {
+       proc_stat = fopen ("/proc/stat", "r");
+       if (proc_stat == NULL)
+       {
                char errbuf[1024];
                ERROR ("processes plugin: fopen (/proc/stat) failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
-               return ULONG_MAX;
+               return (-1);
        }
 
-       while (fgets (buf, sizeof(buf), proc_stat) != NULL)
+       while (fgets (buffer, sizeof (buffer), proc_stat) != NULL)
        {
-               char *endptr;
+               int status;
+               char *fields[3];
+               int fields_num;
 
-               numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE (fields));
-               if (numfields != 2)
+               fields_num = strsplit (buffer, fields,
+                               STATIC_ARRAY_SIZE (fields));
+               if (fields_num != 2)
                        continue;
 
                if (strcmp ("processes", fields[0]) != 0)
                        continue;
 
-               errno = 0;
-               endptr = NULL;
-               result = strtoul(fields[1], &endptr, /* base = */ 10);
-               if ((endptr == fields[1]) || (errno != 0)) {
-                       ERROR ("processes plugin: Cannot parse fork rate: %s",
-                                       fields[1]);
-                       result = ULONG_MAX;
-                       break;
-               }
+               status = parse_value (fields[1], &value, DS_TYPE_DERIVE);
+               if (status == 0)
+                       value_valid = 1;
 
                break;
        }
-
        fclose(proc_stat);
 
-       return result;
+       if (!value_valid)
+               return (-1);
+
+       ps_submit_fork_rate (value.derive);
+       return (0);
 }
+#endif /*KERNEL_LINUX */
 
-static void ps_submit_fork_rate (unsigned long value)
+#if KERNEL_SOLARIS
+static const char *ps_get_cmdline (pid_t pid, /* {{{ */
+               char *buffer, size_t buffer_size)
 {
-       value_t values[1];
-       value_list_t vl = VALUE_LIST_INIT;
+       char path[PATH_MAX];
+       psinfo_t info;
+       int status;
 
-       values[0].derive = (derive_t) value;
+       snprintf(path, sizeof (path), "/proc/%i/psinfo", pid);
 
-       vl.values = values;
-       vl.values_len = 1;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
-       sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
-       sstrncpy (vl.type, "fork_rate", sizeof (vl.type));
-       sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
+       status = read_file_contents (path, (void *) &info, sizeof (info));
+       if (status != ((int) buffer_size))
+       {
+               ERROR ("processes plugin: Unexpected return value "
+                               "while reading \"%s\": "
+                               "Returned %i but expected %zu.",
+                               path, status, buffer_size);
+               return (NULL);
+       }
 
-       plugin_dispatch_values (&vl);
+       info.pr_psargs[sizeof (info.pr_psargs) - 1] = 0;
+       sstrncpy (buffer, info.pr_psargs, buffer_size);
+
+       return (buffer);
+} /* }}} int ps_get_cmdline */
+
+/*
+ * Reads process information on the Solaris OS. The information comes mainly from
+ * /proc/PID/status, /proc/PID/psinfo and /proc/PID/usage
+ * The values for input and ouput chars are calculated "by hand"
+ * Added a few "solaris" specific process states as well
+ */
+static int ps_read_process(int pid, procstat_t *ps, char *state)
+{
+       char filename[64];
+       char f_psinfo[64], f_usage[64];
+       char *buffer;
+
+       pstatus_t *myStatus;
+       psinfo_t *myInfo;
+       prusage_t *myUsage;
+
+       snprintf(filename, sizeof (filename), "/proc/%i/status", pid);
+       snprintf(f_psinfo, sizeof (f_psinfo), "/proc/%i/psinfo", pid);
+       snprintf(f_usage, sizeof (f_usage), "/proc/%i/usage", pid);
+
+
+       buffer = malloc(sizeof (pstatus_t));
+       memset(buffer, 0, sizeof (pstatus_t));
+       read_file_contents(filename, buffer, sizeof (pstatus_t));
+       myStatus = (pstatus_t *) buffer;
+
+       buffer = malloc(sizeof (psinfo_t));
+       memset(buffer, 0, sizeof(psinfo_t));
+       read_file_contents(f_psinfo, buffer, sizeof (psinfo_t));
+       myInfo = (psinfo_t *) buffer;
+
+       buffer = malloc(sizeof (prusage_t));
+       memset(buffer, 0, sizeof(prusage_t));
+       read_file_contents(f_usage, buffer, sizeof (prusage_t));
+       myUsage = (prusage_t *) buffer;
+
+       sstrncpy(ps->name, myInfo->pr_fname, sizeof (myInfo->pr_fname));
+       ps->num_lwp = myStatus->pr_nlwp;
+       if (myInfo->pr_wstat != 0) {
+               ps->num_proc = 0;
+               ps->num_lwp = 0;
+               *state = (char) 'Z';
+               return (0);
+       } else {
+               ps->num_proc = 1;
+               ps->num_lwp = myInfo->pr_nlwp;
+       }
+
+       /*
+        * Convert system time and user time from nanoseconds to microseconds
+        * for compatibility with the linux module
+        */
+       ps->cpu_system_counter = myStatus -> pr_stime.tv_nsec / 1000;
+       ps->cpu_user_counter = myStatus -> pr_utime.tv_nsec / 1000;
+
+       /*
+        * Convert rssize from KB to bytes to be consistent w/ the linux module
+        */
+       ps->vmem_rss = myInfo->pr_rssize * 1024;
+       ps->vmem_size = myInfo->pr_size * 1024;
+       ps->vmem_minflt_counter = myUsage->pr_minf;
+       ps->vmem_majflt_counter = myUsage->pr_majf;
+
+       /*
+        * TODO: Data and code segment calculations for Solaris
+        */
+
+       ps->vmem_data = -1;
+       ps->vmem_code = -1;
+       ps->stack_size = myStatus->pr_stksize;
+
+       /*
+        * Calculating input/ouput chars
+        * Formula used is total chars / total blocks => chars/block
+        * then convert input/output blocks to chars
+        */
+       ulong_t tot_chars = myUsage->pr_ioch;
+       ulong_t tot_blocks = myUsage->pr_inblk + myUsage->pr_oublk;
+       ulong_t chars_per_block = 1;
+       if (tot_blocks != 0)
+               chars_per_block = tot_chars / tot_blocks;
+       ps->io_rchar = myUsage->pr_inblk * chars_per_block;
+       ps->io_wchar = myUsage->pr_oublk * chars_per_block;
+       ps->io_syscr = myUsage->pr_sysc;
+       ps->io_syscw = myUsage->pr_sysc;
+
+
+       /*
+        * TODO: Find way of setting BLOCKED and PAGING status
+        */
+
+       *state = (char) 'R';
+       if (myStatus->pr_flags & PR_ASLEEP)
+               *state = (char) 'S';
+       else if (myStatus->pr_flags & PR_STOPPED)
+               *state = (char) 'T';
+       else if (myStatus->pr_flags & PR_DETACH)
+               *state = (char) 'E';
+       else if (myStatus->pr_flags & PR_DAEMON)
+               *state = (char) 'A';
+       else if (myStatus->pr_flags & PR_ISSYS)
+               *state = (char) 'Y';
+       else if (myStatus->pr_flags & PR_ORPHAN)
+               *state = (char) 'O';
+
+       sfree(myStatus);
+       sfree(myInfo);
+       sfree(myUsage);
+
+       return (0);
 }
 
-#endif /* KERNEL_LINUX */
+/*
+ * Reads the number of threads created since the last reboot. On Solaris these
+ * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
+ * The result is the sum for all the threads created on each cpu
+ */
+static int read_fork_rate()
+{
+       extern kstat_ctl_t *kc;
+       kstat_t *ksp_chain = NULL;
+       derive_t result = 0;
+
+       if (kc == NULL)
+               return (-1);
+
+       for (ksp_chain = kc->kc_chain;
+                       ksp_chain != NULL;
+                       ksp_chain = ksp_chain->ks_next)
+       {
+               if ((strcmp (ksp_chain->ks_module, "cpu") == 0)
+                               && (strcmp (ksp_chain->ks_name, "sys") == 0)
+                               && (strcmp (ksp_chain->ks_class, "misc") == 0))
+               {
+                       long long tmp;
+
+                       kstat_read (kc, ksp_chain, NULL);
+
+                       tmp = get_kstat_value(ksp_chain, "nthreads");
+                       if (tmp != -1LL)
+                               result += tmp;
+               }
+       }
+
+       ps_submit_fork_rate (result);
+       return (0);
+}
+#endif /* KERNEL_SOLARIS */
 
 #if HAVE_THREAD_INFO
 static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
@@ -1480,8 +1693,6 @@ static int ps_read (void)
        procstat_entry_t pse;
        char       state;
 
-       unsigned long fork_rate;
-
        procstat_t *ps_ptr;
 
        running = sleeping = zombies = stopped = paging = blocked = 0;
@@ -1563,9 +1774,7 @@ static int ps_read (void)
        for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
                ps_submit_proc_list (ps_ptr);
 
-       fork_rate = read_fork_rate();
-       if (fork_rate != ULONG_MAX)
-               ps_submit_fork_rate(fork_rate);
+       read_fork_rate();
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
@@ -1579,11 +1788,9 @@ static int ps_read (void)
 
        kvm_t *kd;
        char errbuf[1024];
-       char cmdline[ARG_MAX];
-       char *cmdline_ptr;
-       struct kinfo_proc *procs;          /* array of processes */
-       char **argv;
-       int count;                         /* returns number of processes */
+       struct kinfo_proc *procs;          /* array of processes */
+       struct kinfo_proc *proc_ptr = NULL;
+       int count;                         /* returns number of processes */
        int i;
 
        procstat_t *ps_ptr;
@@ -1613,64 +1820,80 @@ static int ps_read (void)
        /* Iterate through the processes in kinfo_proc */
        for (i = 0; i < count; i++)
        {
-               /* retrieve the arguments */
-               cmdline[0] = 0;
-               cmdline_ptr = NULL;
-
-               argv = kvm_getargv (kd, (const struct kinfo_proc *) &(procs[i]), 0);
-               if (argv != NULL)
+               /* Create only one process list entry per _process_, i.e.
+                * filter out threads (duplicate PID entries). */
+               if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid))
                {
-                       int status;
-                       int argc;
+                       char cmdline[ARG_MAX] = "";
+                       _Bool have_cmdline = 0;
 
-                       argc = 0;
-                       while (argv[argc] != NULL)
-                               argc++;
-
-                       status = strjoin (cmdline, sizeof (cmdline),
-                                       argv, argc, " ");
-
-                       if (status < 0)
+                       proc_ptr = &(procs[i]);
+                       /* Don't probe system processes and processes without arguments */
+                       if (((procs[i].ki_flag & P_SYSTEM) == 0)
+                                       && (procs[i].ki_args != NULL))
                        {
-                               WARNING ("processes plugin: Command line did "
-                                               "not fit into buffer.");
-                       }
-                       else
-                       {
-                               cmdline_ptr = &cmdline[0];
-                       }
-               }
+                               char **argv;
+                               int argc;
+                               int status;
+
+                               /* retrieve the arguments */
+                               argv = kvm_getargv (kd, proc_ptr, /* nchr = */ 0);
+                               argc = 0;
+                               if ((argv != NULL) && (argv[0] != NULL))
+                               {
+                                       while (argv[argc] != NULL)
+                                               argc++;
+
+                                       status = strjoin (cmdline, sizeof (cmdline), argv, argc, " ");
+                                       if (status < 0)
+                                               WARNING ("processes plugin: Command line did not fit into buffer.");
+                                       else
+                                               have_cmdline = 1;
+                               }
+                       } /* if (process has argument list) */
 
-               pse.id       = procs[i].ki_pid;
-               pse.age      = 0;
+                       pse.id       = procs[i].ki_pid;
+                       pse.age      = 0;
 
-               pse.num_proc = 1;
-               pse.num_lwp  = procs[i].ki_numthreads;
+                       pse.num_proc = 1;
+                       pse.num_lwp  = procs[i].ki_numthreads;
 
-               pse.vmem_size = procs[i].ki_size;
-               pse.vmem_rss = procs[i].ki_rssize * getpagesize();
-               pse.vmem_data = procs[i].ki_dsize * getpagesize();
-               pse.vmem_code = procs[i].ki_tsize * getpagesize();
-               pse.stack_size = procs[i].ki_ssize * getpagesize();
-               pse.vmem_minflt = 0;
-               pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
-               pse.vmem_majflt = 0;
-               pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
+                       pse.vmem_size = procs[i].ki_size;
+                       pse.vmem_rss = procs[i].ki_rssize * pagesize;
+                       pse.vmem_data = procs[i].ki_dsize * pagesize;
+                       pse.vmem_code = procs[i].ki_tsize * pagesize;
+                       pse.stack_size = procs[i].ki_ssize * pagesize;
+                       pse.vmem_minflt = 0;
+                       pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
+                       pse.vmem_majflt = 0;
+                       pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
 
-               pse.cpu_user = 0;
-               pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_sec
-                       * 1000
-                       + procs[i].ki_rusage.ru_utime.tv_usec;
-               pse.cpu_system = 0;
-               pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_sec
-                       * 1000
-                       + procs[i].ki_rusage.ru_stime.tv_usec;
+                       pse.cpu_user = 0;
+                       pse.cpu_system = 0;
+                       pse.cpu_user_counter = 0;
+                       pse.cpu_system_counter = 0;
+                       /*
+                        * The u-area might be swapped out, and we can't get
+                        * at it because we have a crashdump and no swap.
+                        * If it's here fill in these fields, otherwise, just
+                        * leave them 0.
+                        */
+                       if (procs[i].ki_flag & P_INMEM)
+                       {
+                               pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_usec
+                                       + (1000000lu * procs[i].ki_rusage.ru_utime.tv_sec);
+                               pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_usec
+                                       + (1000000lu * procs[i].ki_rusage.ru_stime.tv_sec);
+                       }
 
-               /* no io data */
-               pse.io_rchar = -1;
-               pse.io_wchar = -1;
-               pse.io_syscr = -1;
-               pse.io_syscw = -1;
+                       /* no I/O data */
+                       pse.io_rchar = -1;
+                       pse.io_wchar = -1;
+                       pse.io_syscr = -1;
+                       pse.io_syscw = -1;
+
+                       ps_list_add (procs[i].ki_comm, have_cmdline ? cmdline : NULL, &pse);
+               } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */
 
                switch (procs[i].ki_stat)
                {
@@ -1682,8 +1905,6 @@ static int ps_read (void)
                        case SLOCK:     blocked++;      break;
                        case SZOMB:     zombies++;      break;
                }
-
-               ps_list_add (procs[i].ki_comm, cmdline_ptr, &pse);
        }
 
        kvm_close(kd);
@@ -1740,7 +1961,7 @@ static int ps_read (void)
                                if (procentry[i].pi_pid == 0)
                                        cmdline = "swapper";
                                cargs = cmdline;
-                       }
+                       }
                        else
                        {
                                if (getargs(&procentry[i], sizeof(struct procentry64), arglist, MAXARGLN) >= 0)
@@ -1832,7 +2053,118 @@ static int ps_read (void)
 
        for (ps = list_head_g; ps != NULL; ps = ps->next)
                ps_submit_proc_list (ps);
-#endif /* HAVE_PROCINFO_H */
+/* #endif HAVE_PROCINFO_H */
+
+#elif KERNEL_SOLARIS
+       /*
+        * The Solaris section adds a few more process states and removes some
+        * process states compared to linux. Most notably there is no "PAGING"
+        * and "BLOCKED" state for a process.  The rest is similar to the linux
+        * code.
+        */
+       int running = 0;
+       int sleeping = 0;
+       int zombies = 0;
+       int stopped = 0;
+       int detached = 0;
+       int daemon = 0;
+       int system = 0;
+       int orphan = 0;
+
+       struct dirent *ent;
+       DIR *proc;
+
+       int status;
+       procstat_t *ps_ptr;
+       char state;
+
+       char cmdline[PRARGSZ];
+
+       ps_list_reset ();
+
+       proc = opendir ("/proc");
+       if (proc == NULL)
+               return (-1);
+
+       while ((ent = readdir(proc)) != NULL)
+       {
+               int pid;
+               struct procstat ps;
+               procstat_entry_t pse;
+
+               if (!isdigit ((int) ent->d_name[0]))
+                       continue;
+
+               if ((pid = atoi (ent->d_name)) < 1)
+                       continue;
+
+               status = ps_read_process (pid, &ps, &state);
+               if (status != 0)
+               {
+                       DEBUG("ps_read_process failed: %i", status);
+                       continue;
+               }
+
+               pse.id = pid;
+               pse.age = 0;
+
+               pse.num_proc   = ps.num_proc;
+               pse.num_lwp    = ps.num_lwp;
+               pse.vmem_size  = ps.vmem_size;
+               pse.vmem_rss   = ps.vmem_rss;
+               pse.vmem_data  = ps.vmem_data;
+               pse.vmem_code  = ps.vmem_code;
+               pse.stack_size = ps.stack_size;
+
+               pse.vmem_minflt = 0;
+               pse.vmem_minflt_counter = ps.vmem_minflt_counter;
+               pse.vmem_majflt = 0;
+               pse.vmem_majflt_counter = ps.vmem_majflt_counter;
+
+               pse.cpu_user = 0;
+               pse.cpu_user_counter = ps.cpu_user_counter;
+               pse.cpu_system = 0;
+               pse.cpu_system_counter = ps.cpu_system_counter;
+
+               pse.io_rchar = ps.io_rchar;
+               pse.io_wchar = ps.io_wchar;
+               pse.io_syscr = ps.io_syscr;
+               pse.io_syscw = ps.io_syscw;
+
+               switch (state)
+               {
+                       case 'R': running++;  break;
+                       case 'S': sleeping++; break;
+                       case 'E': detached++; break;
+                       case 'Z': zombies++;  break;
+                       case 'T': stopped++;  break;
+                       case 'A': daemon++;   break;
+                       case 'Y': system++;   break;
+                       case 'O': orphan++;   break;
+               }
+
+
+               ps_list_add (ps.name,
+                               ps_get_cmdline ((pid_t) pid,
+                                       cmdline, sizeof (cmdline)),
+                               &pse);
+       } /* while(readdir) */
+       closedir (proc);
+
+       ps_submit_state ("running",  running);
+       ps_submit_state ("sleeping", sleeping);
+       ps_submit_state ("zombies",  zombies);
+       ps_submit_state ("stopped",  stopped);
+       ps_submit_state ("detached", detached);
+       ps_submit_state ("daemon",   daemon);
+       ps_submit_state ("system",   system);
+       ps_submit_state ("orphan",   orphan);
+
+       for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
+               ps_submit_proc_list (ps_ptr);
+
+       read_fork_rate();
+#endif /* KERNEL_SOLARIS */
 
        return (0);
 } /* int ps_read */
index 350bd14..10ac8f0 100644 (file)
@@ -282,17 +282,13 @@ void cpy_log_exception(const char *context) {
        Py_END_ALLOW_THREADS
        Py_XDECREF(tn);
        Py_XDECREF(m);
-       if (!cpy_format_exception) {
+       if (!cpy_format_exception || !traceback) {
                PyErr_Clear();
-               Py_XDECREF(type);
+               Py_DECREF(type);
                Py_XDECREF(value);
                Py_XDECREF(traceback);
                return;
        }
-       if (!traceback) {
-               PyErr_Clear();
-               return;
-       }
        list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
        if (list)
                l = PyObject_Length(list);
@@ -313,6 +309,9 @@ void cpy_log_exception(const char *context) {
        }
        Py_XDECREF(list);
        PyErr_Clear();
+       Py_DECREF(type);
+       Py_XDECREF(value);
+       Py_XDECREF(traceback);
 }
 
 static int cpy_read_callback(user_data_t *data) {
@@ -335,7 +334,7 @@ static int cpy_read_callback(user_data_t *data) {
 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
        int i;
        cpy_callback_t *c = data->data;
-       PyObject *ret, *list, *temp, *dict = NULL, *val;
+       PyObject *ret, *list, *temp, *dict = NULL;
        Values *v;
 
        CPY_LOCK_THREADS
@@ -375,7 +374,7 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                                CPY_RETURN_FROM_THREADS 0;
                        }
                }
-               dict = PyDict_New();
+               dict = PyDict_New();  /* New reference. */
                if (value_list->meta) {
                        int i, num;
                        char **table;
@@ -394,26 +393,26 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                                if (type == MD_TYPE_STRING) {
                                        if (meta_data_get_string(meta, table[i], &string))
                                                continue;
-                                       temp = cpy_string_to_unicode_or_bytes(string);
+                                       temp = cpy_string_to_unicode_or_bytes(string);  /* New reference. */
                                        free(string);
                                        PyDict_SetItemString(dict, table[i], temp);
                                        Py_XDECREF(temp);
                                } else if (type == MD_TYPE_SIGNED_INT) {
                                        if (meta_data_get_signed_int(meta, table[i], &si))
                                                continue;
-                                       temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0);
+                                       temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0);  /* New reference. */
                                        PyDict_SetItemString(dict, table[i], temp);
                                        Py_XDECREF(temp);
                                } else if (type == MD_TYPE_UNSIGNED_INT) {
                                        if (meta_data_get_unsigned_int(meta, table[i], &ui))
                                                continue;
-                                       temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0);
+                                       temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0);  /* New reference. */
                                        PyDict_SetItemString(dict, table[i], temp);
                                        Py_XDECREF(temp);
                                } else if (type == MD_TYPE_DOUBLE) {
                                        if (meta_data_get_double(meta, table[i], &d))
                                                continue;
-                                       temp = PyFloat_FromDouble(d);
+                                       temp = PyFloat_FromDouble(d);  /* New reference. */
                                        PyDict_SetItemString(dict, table[i], temp);
                                        Py_XDECREF(temp);
                                } else if (type == MD_TYPE_BOOLEAN) {
@@ -428,8 +427,7 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                        }
                        free(table);
                }
-               val = Values_New(); /* New reference. */
-               v = (Values *) val; 
+               v = (Values *) Values_New(); /* New reference. */
                sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
                sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
                sstrncpy(v->data.type_instance, value_list->type_instance, sizeof(v->data.type_instance));
@@ -440,9 +438,9 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                Py_CLEAR(v->values);
                v->values = list;
                Py_CLEAR(v->meta);
-               v->meta = dict;
+               v->meta = dict;  /* Steals a reference. */
                ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
-               Py_XDECREF(val);
+               Py_XDECREF(v);
                if (ret == NULL) {
                        cpy_log_exception("write callback");
                } else {
@@ -484,11 +482,11 @@ static void cpy_log_callback(int severity, const char *message, user_data_t *dat
        PyObject *ret, *text;
 
        CPY_LOCK_THREADS
-       text = cpy_string_to_unicode_or_bytes(message);
+       text = cpy_string_to_unicode_or_bytes(message);  /* New reference. */
        if (c->data == NULL)
-               ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. */
+               ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. Steals a reference from "text". */
        else
-               ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. */
+               ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. Steals a reference from "text". */
 
        if (ret == NULL) {
                /* FIXME */
@@ -525,12 +523,13 @@ static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
        char buf[512];
        cpy_callback_t *c;
-       const char *name = NULL;
+       char *name = NULL;
        PyObject *callback = NULL, *data = NULL, *mod = NULL;
        static char *kwlist[] = {"callback", "data", "name", NULL};
        
        if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
        if (PyCallable_Check(callback) == 0) {
+               PyMem_Free(name);
                PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
                return NULL;
        }
@@ -545,18 +544,21 @@ static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args
        c->next = *list_head;
        *list_head = c;
        Py_XDECREF(mod);
+       PyMem_Free(name);
        return cpy_string_to_unicode_or_bytes(buf);
 }
 
 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
        int timeout = -1;
-       const char *plugin = NULL, *identifier = NULL;
+       char *plugin = NULL, *identifier = NULL;
        static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
        
        if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin, &timeout, NULL, &identifier) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_flush(plugin, timeout, identifier);
        Py_END_ALLOW_THREADS
+       PyMem_Free(plugin);
+       PyMem_Free(identifier);
        Py_RETURN_NONE;
 }
 
@@ -575,16 +577,18 @@ static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObjec
        reg_function_t *register_function = (reg_function_t *) reg;
        cpy_callback_t *c = NULL;
        user_data_t *user_data = NULL;
-       const char *name = NULL;
+       char *name = NULL;
        PyObject *callback = NULL, *data = NULL;
        static char *kwlist[] = {"callback", "data", "name", NULL};
        
        if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
        if (PyCallable_Check(callback) == 0) {
+               PyMem_Free(name);
                PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
                return NULL;
        }
        cpy_build_name(buf, sizeof(buf), callback, name);
+       PyMem_Free(name);
        
        Py_INCREF(callback);
        Py_XINCREF(data);
@@ -605,17 +609,19 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwd
        cpy_callback_t *c = NULL;
        user_data_t *user_data = NULL;
        double interval = 0;
-       const char *name = NULL;
+       char *name = NULL;
        PyObject *callback = NULL, *data = NULL;
        struct timespec ts;
        static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
        
        if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
        if (PyCallable_Check(callback) == 0) {
+               PyMem_Free(name);
                PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
                return NULL;
        }
        cpy_build_name(buf, sizeof(buf), callback, name);
+       PyMem_Free(name);
        
        Py_INCREF(callback);
        Py_XINCREF(data);
@@ -659,48 +665,53 @@ static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject
 }
 
 static PyObject *cpy_error(PyObject *self, PyObject *args) {
-       const char *text;
+       char *text;
        if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_ERR, "%s", text);
        Py_END_ALLOW_THREADS
+       PyMem_Free(text);
        Py_RETURN_NONE;
 }
 
 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
-       const char *text;
+       char *text;
        if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_WARNING, "%s", text);
        Py_END_ALLOW_THREADS
+       PyMem_Free(text);
        Py_RETURN_NONE;
 }
 
 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
-       const char *text;
+       char *text;
        if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_NOTICE, "%s", text);
        Py_END_ALLOW_THREADS
+       PyMem_Free(text);
        Py_RETURN_NONE;
 }
 
 static PyObject *cpy_info(PyObject *self, PyObject *args) {
-       const char *text;
+       char *text;
        if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_INFO, "%s", text);
        Py_END_ALLOW_THREADS
+       PyMem_Free(text);
        Py_RETURN_NONE;
 }
 
 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
 #ifdef COLLECT_DEBUG
-       const char *text;
+       char *text;
        if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_DEBUG, "%s", text);
        Py_END_ALLOW_THREADS
+       PyMem_Free(text);
 #endif
        Py_RETURN_NONE;
 }
index 9d8760a..307af17 100644 (file)
 
 #include "cpython.h"
 
+#define FreeAll() do {\
+       PyMem_Free(type);\
+       PyMem_Free(plugin_instance);\
+       PyMem_Free(type_instance);\
+       PyMem_Free(plugin);\
+       PyMem_Free(host);\
+} while(0)
+
 static PyObject *cpy_common_repr(PyObject *s) {
        PyObject *ret, *tmp;
        static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL, *l_plugin_instance = NULL;
@@ -143,7 +151,7 @@ static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kw
 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
        PluginData *self = (PluginData *) s;
        double time = 0;
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "plugin_instance", "type_instance",
                        "plugin", "host", "time", NULL};
        
@@ -151,18 +159,21 @@ static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
                        NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
                return -1;
        
-       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+       if (type && plugin_get_ds(type) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
                return -1;
        }
 
-       sstrncpy(self->host, host, sizeof(self->host));
-       sstrncpy(self->plugin, plugin, sizeof(self->plugin));
-       sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
-       sstrncpy(self->type, type, sizeof(self->type));
-       sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
-       
+       sstrncpy(self->host, host ? host : "", sizeof(self->host));
+       sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
+       sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->plugin_instance));
+       sstrncpy(self->type, type ? type : "", sizeof(self->type));
+       sstrncpy(self->type_instance, type_instance ? type_instance : "", sizeof(self->type_instance));
        self->time = time;
+
+       FreeAll();
+
        return 0;
 }
 
@@ -353,7 +364,7 @@ static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Values *self = (Values *) s;
        double interval = 0, time = 0;
        PyObject *values = NULL, *meta = NULL, *tmp;
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", "meta", NULL};
        
@@ -362,18 +373,21 @@ static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
                        NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return -1;
        
-       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+       if (type && plugin_get_ds(type) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
                return -1;
        }
 
-       sstrncpy(self->data.host, host, sizeof(self->data.host));
-       sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
-       sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
-       sstrncpy(self->data.type, type, sizeof(self->data.type));
-       sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
+       sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
+       sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
+       sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
+       sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
+       sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
        self->data.time = time;
 
+       FreeAll();
+
        if (values == NULL) {
                values = PyList_New(0);
                PyErr_Clear();
@@ -490,11 +504,7 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        value_list_t value_list = VALUE_LIST_INIT;
        PyObject *values = self->values, *meta = self->meta;
        double time = self->data.time, interval = self->interval;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
        
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", "meta", NULL};
@@ -503,13 +513,20 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
                        NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return NULL;
 
-       if (type[0] == 0) {
+       sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
+       sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
+       sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
+       sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
+       sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
+       FreeAll();
+       if (value_list.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
+               FreeAll();
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(value_list.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
                return NULL;
        }
        if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
@@ -522,7 +539,7 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        }
        size = (int) PySequence_Length(values);
        if (size != ds->ds_num) {
-               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
+               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", value_list.type, ds->ds_num, size);
                return NULL;
        }
        value = malloc(size * sizeof(*value));
@@ -559,7 +576,7 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
                        }
                } else {
                        free(value);
-                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
+                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
                        return NULL;
                }
                if (PyErr_Occurred() != NULL) {
@@ -572,11 +589,6 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        value_list.values_len = size;
        value_list.time = DOUBLE_TO_CDTIME_T(time);
        value_list.interval = DOUBLE_TO_CDTIME_T(interval);
-       sstrncpy(value_list.host, host, sizeof(value_list.host));
-       sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
-       sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
-       sstrncpy(value_list.type, type, sizeof(value_list.type));
-       sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
        if (value_list.host[0] == 0)
                sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
        if (value_list.plugin[0] == 0)
@@ -601,27 +613,28 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        value_list_t value_list = VALUE_LIST_INIT;
        PyObject *values = self->values, *meta = self->meta;
        double time = self->data.time, interval = self->interval;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
-       const char *dest = NULL;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
        
        static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", "meta", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest,
                        NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
                        NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return NULL;
 
-       if (type[0] == 0) {
+       sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
+       sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
+       sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
+       sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
+       sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
+       FreeAll();
+       if (value_list.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(value_list.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
                return NULL;
        }
        if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
@@ -630,7 +643,7 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        }
        size = (int) PySequence_Length(values);
        if (size != ds->ds_num) {
-               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
+               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", value_list.type, ds->ds_num, size);
                return NULL;
        }
        value = malloc(size * sizeof(*value));
@@ -667,7 +680,7 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
                        }
                } else {
                        free(value);
-                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
+                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
                        return NULL;
                }
                if (PyErr_Occurred() != NULL) {
@@ -679,11 +692,6 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        value_list.values_len = size;
        value_list.time = DOUBLE_TO_CDTIME_T(time);
        value_list.interval = DOUBLE_TO_CDTIME_T(interval);
-       sstrncpy(value_list.host, host, sizeof(value_list.host));
-       sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
-       sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
-       sstrncpy(value_list.type, type, sizeof(value_list.type));
-       sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
        value_list.meta = cpy_build_meta(meta);;
        if (value_list.host[0] == 0)
                sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
@@ -827,8 +835,8 @@ static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Notification *self = (Notification *) s;
        int severity = 0;
        double time = 0;
-       const char *message = "";
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       char *message = NULL;
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "severity", NULL};
        
@@ -837,20 +845,24 @@ static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
                        NULL, &plugin, NULL, &host, &time, &severity))
                return -1;
        
-       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+       if (type && plugin_get_ds(type) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
+               PyMem_Free(message);
                return -1;
        }
 
-       sstrncpy(self->data.host, host, sizeof(self->data.host));
-       sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
-       sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
-       sstrncpy(self->data.type, type, sizeof(self->data.type));
-       sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
+       sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
+       sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
+       sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
+       sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
+       sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
+       sstrncpy(self->message, message ? message : "", sizeof(self->message));
        self->data.time = time;
-
-       sstrncpy(self->message, message, sizeof(self->message));
        self->severity = severity;
+
+       FreeAll();
+       PyMem_Free(message);
        return 0;
 }
 
@@ -860,12 +872,8 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj
        notification_t notification;
        double t = self->data.time;
        int severity = self->severity;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
-       const char *message = self->message;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
+       char *message = NULL;
        
        static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "severity", NULL};
@@ -874,25 +882,28 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj
                        NULL, &plugin, NULL, &host, &t, &severity))
                return NULL;
 
-       if (type[0] == 0) {
+       notification.time = DOUBLE_TO_CDTIME_T(t);
+       notification.severity = severity;
+       sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
+       sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
+       sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin, sizeof(notification.plugin));
+       sstrncpy(notification.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(notification.plugin_instance));
+       sstrncpy(notification.type, type ? type : self->data.type, sizeof(notification.type));
+       sstrncpy(notification.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(notification.type_instance));
+       notification.meta = NULL;
+       FreeAll();
+       PyMem_Free(message);
+
+       if (notification.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(notification.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
                return NULL;
        }
 
-       notification.time = DOUBLE_TO_CDTIME_T(t);
-       notification.severity = severity;
-       sstrncpy(notification.message, message, sizeof(notification.message));
-       sstrncpy(notification.host, host, sizeof(notification.host));
-       sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
-       sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
-       sstrncpy(notification.type, type, sizeof(notification.type));
-       sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
-       notification.meta = NULL;
        if (notification.time == 0)
                notification.time = cdtime();
        if (notification.host[0] == 0)
index b694e09..439cf4b 100644 (file)
 #include <pthread.h>
 #include <credis.h>
 
+#ifndef HOST_NAME_MAX
+# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
+#endif
+
 #define REDIS_DEF_HOST   "localhost"
 #define REDIS_DEF_PORT    6379
 #define REDIS_DEF_TIMEOUT 2000
@@ -50,6 +54,7 @@ struct redis_node_s
 {
   char name[MAX_REDIS_NODE_NAME];
   char host[HOST_NAME_MAX];
+  char passwd[HOST_NAME_MAX];
   int port;
   int timeout;
 
@@ -132,6 +137,8 @@ static int redis_config_node (oconfig_item_t *ci) /* {{{ */
     }
     else if (strcasecmp ("Timeout", option->key) == 0)
       status = cf_util_get_int (option, &rn.timeout);
+    else if (strcasecmp ("Password", option->key) == 0)
+      status = cf_util_get_string_buffer (option, rn.passwd, sizeof (rn.passwd));
     else
       WARNING ("redis plugin: Option `%s' not allowed inside a `Node' "
           "block. I'll ignore this option.", option->key);
@@ -251,6 +258,18 @@ static int redis_read (void) /* {{{ */
       continue;
     }
 
+    if (strlen (rn->passwd) > 0)
+    {
+      DEBUG ("redis plugin: authenticanting node `%s' passwd(%s).", rn->name, rn->passwd);
+      status = credis_auth(rh, rn->passwd);
+      if (status != 0)
+      {
+        WARNING ("redis plugin: unable to authenticate on node `%s'.", rn->name);
+        credis_close (rh);
+        continue;
+      }
+    }
+
     memset (&info, 0, sizeof (info));
     status = credis_info (rh, &info);
     if (status != 0)
index d61ffe9..2ade3bb 100644 (file)
@@ -341,7 +341,7 @@ static int cr_config_router (oconfig_item_t *ci) /* {{{ */
     if (strcasecmp ("Host", child->key) == 0)
       status = cf_util_get_string (child, &router_data->node);
     else if (strcasecmp ("Port", child->key) == 0)
-      status = cf_util_get_string (child, &router_data->service);
+      status = cf_util_get_service (child, &router_data->service);
     else if (strcasecmp ("User", child->key) == 0)
       status = cf_util_get_string (child, &router_data->username);
     else if (strcasecmp ("Password", child->key) == 0)
index 11c1c6a..45553b7 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/rrdcached.c
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008-2012  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
@@ -16,7 +16,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
@@ -33,8 +33,8 @@
  */
 static char *datadir = NULL;
 static char *daemon_address = NULL;
-static int config_create_files = 1;
-static int config_collect_stats = 1;
+static _Bool config_create_files = 1;
+static _Bool config_collect_stats = 1;
 static rrdcreate_config_t rrdcreate_config =
 {
        /* stepsize = */ 0,
@@ -161,89 +161,130 @@ static int value_list_to_filename (char *buffer, int buffer_len,
   return (0);
 } /* int value_list_to_filename */
 
-static const char *config_get_string (oconfig_item_t *ci)
+static int rc_config_get_int_positive (oconfig_item_t const *ci, int *ret)
 {
-  if ((ci->children_num != 0) || (ci->values_num != 1)
-      || ((ci->values[0].type != OCONFIG_TYPE_STRING)
-        && (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)))
+  int status;
+  int tmp = 0;
+
+  status = cf_util_get_int (ci, &tmp);
+  if (status != 0)
+    return (status);
+  if (tmp < 0)
+    return (EINVAL);
+
+  *ret = tmp;
+  return (0);
+} /* int rc_config_get_int_positive */
+
+static int rc_config_get_xff (oconfig_item_t const *ci, double *ret)
+{
+  double value;
+
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
   {
-    ERROR ("rrdcached plugin: %s expects a single string argument.",
-        ci->key);
-    return (NULL);
+    ERROR ("rrdcached plugin: The \"%s\" needs exactly one numeric argument "
+        "in the range [0.0, 1.0)", ci->key);
+    return (EINVAL);
   }
 
-  if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN) {
-    if (ci->values[0].value.boolean)
-      return "true";
-    else
-      return "false";
+  value = ci->values[0].value.number;
+  if ((value >= 0.0) && (value < 1.0))
+  {
+    *ret = value;
+    return (0);
   }
-  return (ci->values[0].value.string);
-} /* const char *config_get_string */
+
+  ERROR ("rrdcached plugin: The \"%s\" needs exactly one numeric argument "
+      "in the range [0.0, 1.0)", ci->key);
+  return (EINVAL);
+} /* int rc_config_get_xff */
+
+static int rc_config_add_timespan (int timespan)
+{
+  int *tmp;
+
+  if (timespan <= 0)
+    return (EINVAL);
+
+  tmp = realloc (rrdcreate_config.timespans,
+      sizeof (*rrdcreate_config.timespans)
+      * (rrdcreate_config.timespans_num + 1));
+  if (tmp == NULL)
+    return (ENOMEM);
+  rrdcreate_config.timespans = tmp;
+
+  rrdcreate_config.timespans[rrdcreate_config.timespans_num] = timespan;
+  rrdcreate_config.timespans_num++;
+
+  return (0);
+} /* int rc_config_add_timespan */
 
 static int rc_config (oconfig_item_t *ci)
 {
   int i;
 
-  for (i = 0; i < ci->children_num; ++i) {
-    const char *key = ci->children[i].key;
-    const char *value = config_get_string (ci->children + i);
-
-    if (value == NULL) /* config_get_strings prints error message */
-      continue;
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t const *child = ci->children + i;
+    const char *key = child->key;
+    int status = 0;
 
     if (strcasecmp ("DataDir", key) == 0)
     {
-      if (datadir != NULL)
-        free (datadir);
-      datadir = strdup (value);
-      if (datadir != NULL)
+      status = cf_util_get_string (child, &datadir);
+      if (status == 0)
       {
         int len = strlen (datadir);
+
         while ((len > 0) && (datadir[len - 1] == '/'))
         {
           len--;
-          datadir[len] = '\0';
+          datadir[len] = 0;
         }
+
         if (len <= 0)
-        {
-          free (datadir);
-          datadir = NULL;
-        }
+          sfree (datadir);
       }
     }
     else if (strcasecmp ("DaemonAddress", key) == 0)
-    {
-      sfree (daemon_address);
-      daemon_address = strdup (value);
-      if (daemon_address == NULL)
-      {
-        ERROR ("rrdcached plugin: strdup failed.");
-        continue;
-      }
-    }
+      status = cf_util_get_string (child, &daemon_address);
     else if (strcasecmp ("CreateFiles", key) == 0)
+      status = cf_util_get_boolean (child, &config_create_files);
+    else if (strcasecmp ("CollectStatistics", key) == 0)
+      status = cf_util_get_boolean (child, &config_collect_stats);
+    else if (strcasecmp ("StepSize", key) == 0)
     {
-      if (IS_FALSE (value))
-        config_create_files = 0;
-      else
-        config_create_files = 1;
+      int tmp = -1;
+
+      status = rc_config_get_int_positive (child, &tmp);
+      if (status == 0)
+        rrdcreate_config.stepsize = (unsigned long) tmp;
     }
-    else if (strcasecmp ("CollectStatistics", key) == 0)
+    else if (strcasecmp ("HeartBeat", key) == 0)
+      status = rc_config_get_int_positive (child, &rrdcreate_config.heartbeat);
+    else if (strcasecmp ("RRARows", key) == 0)
+      status = rc_config_get_int_positive (child, &rrdcreate_config.rrarows);
+    else if (strcasecmp ("RRATimespan", key) == 0)
     {
-      if (IS_FALSE (value))
-        config_collect_stats = 0;
-      else
-        config_collect_stats = 1;
+      int tmp = -1;
+      status = rc_config_get_int_positive (child, &tmp);
+      if (status == 0)
+        status = rc_config_add_timespan (tmp);
     }
+    else if (strcasecmp ("XFF", key) == 0)
+      status = rc_config_get_xff (child, &rrdcreate_config.xff);
     else
     {
       WARNING ("rrdcached plugin: Ignoring invalid option %s.", key);
       continue;
     }
+
+    if (status != 0)
+      WARNING ("rrdcached plugin: Handling the \"%s\" option failed.", key);
   }
 
-  if (daemon_address != NULL) {
+  if (daemon_address != NULL)
+  {
     plugin_register_write ("rrdcached", rc_write, /* user_data = */ NULL);
     plugin_register_flush ("rrdcached", rc_flush, /* user_data = */ NULL);
   }
@@ -262,7 +303,7 @@ static int rc_read (void)
   if (daemon_address == NULL)
     return (-1);
 
-  if (config_collect_stats == 0)
+  if (!config_collect_stats)
     return (-1);
 
   vl.values = values;
@@ -354,7 +395,7 @@ static int rc_read (void)
 
 static int rc_init (void)
 {
-  if (config_collect_stats != 0)
+  if (config_collect_stats)
     plugin_register_read ("rrdcached", rc_read);
 
   return (0);
@@ -396,7 +437,7 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl,
   values_array[0] = values;
   values_array[1] = NULL;
 
-  if (config_create_files != 0)
+  if (config_create_files)
   {
     struct stat statbuf;
 
index ceb834d..b1d13ee 100644 (file)
@@ -185,7 +185,7 @@ static int srrd_update (char *filename, char *template,
        if (status != 0)
        {
                WARNING ("rrdtool plugin: rrd_update_r failed: %s: %s",
-                               argv[1], rrd_get_error ());
+                               filename, rrd_get_error ());
        }
 
        sfree (new_argv);
index 8391346..209482e 100644 (file)
  *   - honor sensors.conf's ignored
  *   - config Sensor option
  *   - config IgnoreSelected option
+ *
+ *   Henrique de Moraes Holschuh <hmh at debian.org>
+ *   - use default libsensors config file on API 0x400
+ *   - config SensorConfigFile option
  **/
 
 #include "collectd.h"
@@ -132,7 +136,8 @@ static int known_features_num = STATIC_ARRAY_SIZE (known_features);
 static const char *config_keys[] =
 {
        "Sensor",
-       "IgnoreSelected"
+       "IgnoreSelected",
+       "SensorConfigFile"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -148,6 +153,7 @@ typedef struct featurelist
 # ifndef SENSORS_CONF_PATH
 #  define SENSORS_CONF_PATH "/etc/sensors.conf"
 # endif
+static char *conffile = SENSORS_CONF_PATH;
 /* #endif SENSORS_API_VERSION < 0x400 */
 
 #elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500)
@@ -159,9 +165,7 @@ typedef struct featurelist
        struct featurelist         *next;
 } featurelist_t;
 
-# ifndef SENSORS_CONF_PATH
-#  define SENSORS_CONF_PATH "/etc/sensors3.conf"
-# endif
+static char *conffile = NULL;
 /* #endif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */
 
 #else /* if SENSORS_API_VERSION >= 0x500 */
@@ -169,10 +173,8 @@ typedef struct featurelist
        "as bug."
 #endif
 
-static const char *conffile = SENSORS_CONF_PATH;
 featurelist_t *first_feature = NULL;
 static ignorelist_t *sensor_list;
-static time_t sensors_config_mtime = 0;
 
 #if SENSORS_API_VERSION < 0x400
 /* full chip name logic borrowed from lm_sensors */
@@ -225,7 +227,19 @@ static int sensors_config (const char *key, const char *value)
        if (sensor_list == NULL)
                sensor_list = ignorelist_create (1);
 
-       if (strcasecmp (key, "Sensor") == 0)
+       /* TODO: This setting exists for compatibility with old versions of
+        * lm-sensors. Remove support for those ancient versions in the next
+        * major release. */
+       if (strcasecmp (key, "SensorConfigFile") == 0)
+       {
+               char *tmp = strdup (value);
+               if (tmp != NULL)
+               {
+                       sfree (conffile);
+                       conffile = tmp;
+               }
+       }
+       else if (strcasecmp (key, "Sensor") == 0)
        {
                if (ignorelist_add (sensor_list, value))
                {
@@ -268,47 +282,37 @@ void sensors_free_features (void)
 
 static int sensors_load_conf (void)
 {
-       FILE *fh;
+       static int call_once = 0;
+
+       FILE *fh = NULL;
        featurelist_t *last_feature = NULL;
        
        const sensors_chip_name *chip;
        int chip_num;
 
-       struct stat statbuf;
        int status;
-       
-       status = stat (conffile, &statbuf);
-       if (status != 0)
-       {
-               char errbuf[1024];
-               ERROR ("sensors plugin: stat (%s) failed: %s", conffile,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               sensors_config_mtime = 0;
-       }
 
-       if ((sensors_config_mtime != 0)
-                       && (sensors_config_mtime == statbuf.st_mtime))
-               return (0);
+       if (call_once)
+               return 0;
 
-       if (sensors_config_mtime != 0)
-       {
-               NOTICE ("sensors plugin: Reloading config from %s",
-                               conffile);
-               sensors_free_features ();
-               sensors_config_mtime = 0;
-       }
+       call_once = 1;
 
-       fh = fopen (conffile, "r");
-       if (fh == NULL)
+       if (conffile != NULL)
        {
-               char errbuf[1024];
-               ERROR ("sensors plugin: fopen(%s) failed: %s", conffile,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (-1);
+               fh = fopen (conffile, "r");
+               if (fh == NULL)
+               {
+                       char errbuf[1024];
+                       ERROR ("sensors plugin: fopen(%s) failed: %s", conffile,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       return (-1);
+               }
        }
 
        status = sensors_init (fh);
-       fclose (fh);
+       if (fh)
+               fclose (fh);
+
        if (status != 0)
        {
                ERROR ("sensors plugin: Cannot initialize sensors. "
@@ -316,8 +320,6 @@ static int sensors_load_conf (void)
                return (-1);
        }
 
-       sensors_config_mtime = statbuf.st_mtime;
-
 #if SENSORS_API_VERSION < 0x400
        chip_num = 0;
        while ((chip = sensors_get_detected_chips (&chip_num)) != NULL)
index 35f51b3..c4d043b 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/snmp.c
- * Copyright (C) 2007  Florian octo Forster
+ * Copyright (C) 2007-2012  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
@@ -16,7 +16,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
@@ -79,7 +79,7 @@ typedef struct host_definition_s host_definition_t;
  * gaps in tables. */
 struct csnmp_list_instances_s
 {
-  oid subid;
+  oid_t suffix;
   char instance[DATA_MAX_NAME_LEN];
   struct csnmp_list_instances_s *next;
 };
@@ -87,7 +87,7 @@ typedef struct csnmp_list_instances_s csnmp_list_instances_t;
 
 struct csnmp_table_values_s
 {
-  oid subid;
+  oid_t suffix;
   value_t value;
   struct csnmp_table_values_s *next;
 };
@@ -106,6 +106,54 @@ static int csnmp_read_host (user_data_t *ud);
 /*
  * Private functions
  */
+static void csnmp_oid_init (oid_t *dst, oid const *src, size_t n)
+{
+  assert (n <= STATIC_ARRAY_LEN (dst->oid));
+  memcpy (dst->oid, src, sizeof (*src) * n);
+  dst->oid_len = n;
+}
+
+static int csnmp_oid_compare (oid_t const *left, oid_t const *right)
+{
+  return (snmp_oid_compare (left->oid, left->oid_len,
+        right->oid, right->oid_len));
+}
+
+static int csnmp_oid_suffix (oid_t *dst, oid_t const *src,
+    oid_t const *root)
+{
+  /* Make sure "src" is in "root"s subtree. */
+  if (src->oid_len <= root->oid_len)
+    return (EINVAL);
+  if (snmp_oid_ncompare (root->oid, root->oid_len,
+        src->oid, src->oid_len,
+        /* n = */ root->oid_len) != 0)
+    return (EINVAL);
+
+  memset (dst, 0, sizeof (*dst));
+  dst->oid_len = src->oid_len - root->oid_len;
+  memcpy (dst->oid, &src->oid[root->oid_len],
+      dst->oid_len * sizeof (dst->oid[0]));
+  return (0);
+}
+
+static int csnmp_oid_to_string (char *buffer, size_t buffer_size,
+    oid_t const *o)
+{
+  char oid_str[MAX_OID_LEN][16];
+  char *oid_str_ptr[MAX_OID_LEN];
+  size_t i;
+
+  for (i = 0; i < o->oid_len; i++)
+  {
+    ssnprintf (oid_str[i], sizeof (oid_str[i]), "%lu", (unsigned long) o->oid[i]);
+    oid_str_ptr[i] = oid_str[i];
+  }
+
+  return (strjoin (buffer, buffer_size,
+        oid_str_ptr, o->oid_len, /* separator = */ "."));
+}
+
 static void csnmp_host_close_session (host_definition_t *host) /* {{{ */
 {
   if (host->sess_handle == NULL)
@@ -127,7 +175,7 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */
   if (hd->name != NULL)
   {
     DEBUG ("snmp plugin: Destroying host definition for host `%s'.",
-       hd->name);
+        hd->name);
   }
 
   csnmp_host_close_session (hd);
@@ -212,10 +260,10 @@ static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t
     dd->instance.oid.oid_len = MAX_OID_LEN;
 
     if (!read_objid (ci->values[0].value.string,
-         dd->instance.oid.oid, &dd->instance.oid.oid_len))
+          dd->instance.oid.oid, &dd->instance.oid.oid_len))
     {
       ERROR ("snmp plugin: read_objid (%s) failed.",
-         ci->values[0].value.string);
+          ci->values[0].value.string);
       return (-1);
     }
   }
@@ -223,7 +271,7 @@ static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t
   {
     /* Instance is a simple string */
     sstrncpy (dd->instance.string, ci->values[0].value.string,
-       sizeof (dd->instance.string));
+        sizeof (dd->instance.string));
   }
 
   return (0);
@@ -241,7 +289,7 @@ static int csnmp_config_add_data_instance_prefix (data_definition_t *dd,
   if (!dd->is_table)
   {
     WARNING ("snmp plugin: data %s: InstancePrefix is ignored when `Table' "
-       "is set to `false'.", dd->name);
+        "is set to `false'.", dd->name);
     return (-1);
   }
 
@@ -282,10 +330,10 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *
     dd->values[i].oid_len = MAX_OID_LEN;
 
     if (NULL == snmp_parse_oid (ci->values[i].value.string,
-         dd->values[i].oid, &dd->values[i].oid_len))
+          dd->values[i].oid, &dd->values[i].oid_len))
     {
       ERROR ("snmp plugin: snmp_parse_oid (%s) failed.",
-         ci->values[i].value.string);
+          ci->values[i].value.string);
       free (dd->values);
       dd->values = NULL;
       dd->values_len = 0;
@@ -301,7 +349,7 @@ static int csnmp_config_add_data_shift (data_definition_t *dd, oconfig_item_t *c
   if ((ci->values_num != 1)
       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
   {
-    WARNING ("snmp plugin: The `Scale' config option needs exactly one number argument.");
+    WARNING ("snmp plugin: The `Shift' config option needs exactly one number argument.");
     return (-1);
   }
 
@@ -523,17 +571,17 @@ static int csnmp_config_add_host_collect (host_definition_t *host,
   {
     for (data = data_head; data != NULL; data = data->next)
       if (strcasecmp (ci->values[i].value.string, data->name) == 0)
-       break;
+        break;
 
     if (data == NULL)
     {
       WARNING ("snmp plugin: No such data configured: `%s'",
-         ci->values[i].value.string);
+          ci->values[i].value.string);
       continue;
     }
 
     DEBUG ("snmp plugin: Collect: host = %s, data[%i] = %s;",
-       host->name, host->data_list_len, data->name);
+        host->name, host->data_list_len, data->name);
 
     host->data_list[host->data_list_len] = data;
     host->data_list_len++;
@@ -696,7 +744,7 @@ static void csnmp_host_open_session (host_definition_t *host)
     snmp_error (&sess, NULL, NULL, &errstr);
 
     ERROR ("snmp plugin: host %s: snmp_sess_open failed: %s",
-       host->name, (errstr == NULL) ? "Unknown problem" : errstr);
+        host->name, (errstr == NULL) ? "Unknown problem" : errstr);
     sfree (errstr);
   }
 } /* void csnmp_host_open_session */
@@ -709,7 +757,9 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
   value_t ret;
   uint64_t tmp_unsigned = 0;
   int64_t tmp_signed = 0;
-  int defined = 1;
+  _Bool defined = 1;
+  /* Set to true when the original SNMP type appears to have been signed. */
+  _Bool prefer_signed = 0;
 
   if ((vl->type == ASN_INTEGER)
       || (vl->type == ASN_UINTEGER)
@@ -721,7 +771,12 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
   {
     tmp_unsigned = (uint32_t) *vl->val.integer;
     tmp_signed = (int32_t) *vl->val.integer;
-    DEBUG ("snmp plugin: Parsed int32 value is %"PRIi64".", tmp_signed);
+
+    if ((vl->type == ASN_INTEGER)
+        || (vl->type == ASN_GAUGE))
+      prefer_signed = 1;
+
+    DEBUG ("snmp plugin: Parsed int32 value is %"PRIu64".", tmp_unsigned);
   }
   else if (vl->type == ASN_COUNTER64)
   {
@@ -741,12 +796,12 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
 
     memset (oid_buffer, 0, sizeof (oid_buffer));
     snprint_objid (oid_buffer, sizeof (oid_buffer) - 1,
-       vl->name, vl->name_length);
+        vl->name, vl->name_length);
 
 #ifdef ASN_NULL
     if (vl->type == ASN_NULL)
       INFO ("snmp plugin: OID \"%s\" is undefined (type ASN_NULL)",
-         oid_buffer);
+          oid_buffer);
     else
 #endif
       WARNING ("snmp plugin: I don't know the ASN type #%i "
@@ -769,7 +824,7 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
 
       string_length = sizeof (string) - 1;
       if (vl->val_len < string_length)
-       string_length = vl->val_len;
+        string_length = vl->val_len;
 
       /* The strings we get from the Net-SNMP library may not be null
        * terminated. That is why we're using `memcpy' here and not `strcpy'.
@@ -781,8 +836,8 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
       status = parse_value (string, &ret, type);
       if (status != 0)
       {
-       ERROR ("snmp plugin: csnmp_value_list_to_value: Parsing string as %s failed: %s",
-           DS_TYPE_TO_STRING (type), string);
+        ERROR ("snmp plugin: csnmp_value_list_to_value: Parsing string as %s failed: %s",
+            DS_TYPE_TO_STRING (type), string);
       }
     }
 
@@ -790,20 +845,20 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
     {
       switch (type)
       {
-       case DS_TYPE_COUNTER:
-       case DS_TYPE_DERIVE:
-       case DS_TYPE_ABSOLUTE:
-         memset (&ret, 0, sizeof (ret));
-         break;
-
-       case DS_TYPE_GAUGE:
-         ret.gauge = NAN;
-         break;
-
-       default:
-         ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown "
-             "data source type: %i.", type);
-         ret.gauge = NAN;
+        case DS_TYPE_COUNTER:
+        case DS_TYPE_DERIVE:
+        case DS_TYPE_ABSOLUTE:
+          memset (&ret, 0, sizeof (ret));
+          break;
+
+        case DS_TYPE_GAUGE:
+          ret.gauge = NAN;
+          break;
+
+        default:
+          ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown "
+              "data source type: %i.", type);
+          ret.gauge = NAN;
       }
     }
   } /* if (vl->type == ASN_OCTET_STR) */
@@ -813,18 +868,28 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
   }
   else if (type == DS_TYPE_GAUGE)
   {
-    ret.gauge = NAN;
-    if (defined != 0)
+    if (!defined)
+      ret.gauge = NAN;
+    else if (prefer_signed)
       ret.gauge = (scale * tmp_signed) + shift;
+    else
+      ret.gauge = (scale * tmp_unsigned) + shift;
   }
   else if (type == DS_TYPE_DERIVE)
-    ret.derive = (derive_t) tmp_signed;
+  {
+    if (prefer_signed)
+      ret.derive = (derive_t) tmp_signed;
+    else
+      ret.derive = (derive_t) tmp_unsigned;
+  }
   else if (type == DS_TYPE_ABSOLUTE)
+  {
     ret.absolute = (absolute_t) tmp_unsigned;
+  }
   else
   {
     ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown data source "
-       "type: %i.", type);
+        "type: %i.", type);
     ret.gauge = NAN;
   }
 
@@ -854,10 +919,12 @@ static int csnmp_check_res_left_subtree (const host_definition_t *host,
       vb = vb->next_variable, i++)
   {
     num_checked++;
-    if (snmp_oid_ncompare (data->values[i].oid,
-         data->values[i].oid_len,
-         vb->name, vb->name_length,
-         data->values[i].oid_len) != 0)
+
+    if ((vb->type == SNMP_ENDOFMIBVIEW)
+        || (snmp_oid_ncompare (data->values[i].oid,
+            data->values[i].oid_len,
+            vb->name, vb->name_length,
+            data->values[i].oid_len) != 0))
       num_left_subtree++;
   }
 
@@ -865,7 +932,7 @@ static int csnmp_check_res_left_subtree (const host_definition_t *host,
   if (i < data->values_len)
   {
     ERROR ("snmp plugin: host %s: Expected %i variables, but got only %i",
-       host->name, data->values_len, i);
+        host->name, data->values_len, i);
     return (-1);
   }
 
@@ -874,15 +941,15 @@ static int csnmp_check_res_left_subtree (const host_definition_t *host,
     if (vb == NULL)
     {
       ERROR ("snmp plugin: host %s: Expected one more variable for "
-         "the instance..", host->name);
+          "the instance..", host->name);
       return (-1);
     }
 
     num_checked++;
     if (snmp_oid_ncompare (data->instance.oid.oid,
-         data->instance.oid.oid_len,
-         vb->name, vb->name_length,
-         data->instance.oid.oid_len) != 0)
+          data->instance.oid.oid_len,
+          vb->name, vb->name_length,
+          data->instance.oid.oid_len) != 0)
       num_left_subtree++;
   }
 
@@ -909,7 +976,7 @@ static int csnmp_strvbcopy_hexstring (char *dst, /* {{{ */
     int status;
 
     status = snprintf (buffer_ptr, buffer_free,
-       (i == 0) ? "%02x" : ":%02x", (unsigned int) vb->val.bitstring[i]);
+        (i == 0) ? "%02x" : ":%02x", (unsigned int) vb->val.bitstring[i]);
 
     if (status >= buffer_free)
     {
@@ -967,6 +1034,8 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
 {
   csnmp_list_instances_t *il;
   struct variable_list *vb;
+  oid_t vb_name;
+  int status;
 
   /* Set vb on the last variable */
   for (vb = res->variables;
@@ -976,15 +1045,24 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
   if (vb == NULL)
     return (-1);
 
-  il = (csnmp_list_instances_t *) malloc (sizeof (csnmp_list_instances_t));
+  csnmp_oid_init (&vb_name, vb->name, vb->name_length);
+
+  il = malloc (sizeof (*il));
   if (il == NULL)
   {
     ERROR ("snmp plugin: malloc failed.");
     return (-1);
   }
-  il->subid = vb->name[vb->name_length - 1];
+  memset (il, 0, sizeof (*il));
   il->next = NULL;
 
+  status = csnmp_oid_suffix (&il->suffix, &vb_name, &dd->instance.oid);
+  if (status != 0)
+  {
+    sfree (il);
+    return (status);
+  }
+
   /* Get instance name */
   if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR))
   {
@@ -995,9 +1073,9 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     for (ptr = il->instance; *ptr != '\0'; ptr++)
     {
       if ((*ptr > 0) && (*ptr < 32))
-       *ptr = ' ';
+        *ptr = ' ';
       else if (*ptr == '/')
-       *ptr = '_';
+        *ptr = '_';
     }
     DEBUG ("snmp plugin: il->instance = `%s';", il->instance);
   }
@@ -1006,7 +1084,7 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER,
         /* scale = */ 1.0, /* shift = */ 0.0, hd->name, dd->name);
     ssnprintf (il->instance, sizeof (il->instance),
-       "%llu", val.counter);
+        "%llu", val.counter);
   }
 
   /* TODO: Debugging output */
@@ -1031,8 +1109,8 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
   csnmp_table_values_t **value_table_ptr;
 
   int i;
-  oid subid;
-  int have_more;
+  _Bool have_more;
+  oid_t current_suffix;
 
   ds = plugin_get_ds (data->type);
   if (!ds)
@@ -1044,15 +1122,14 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
 
   instance_list_ptr = instance_list;
 
-  value_table_ptr = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *)
-      * data->values_len);
+  value_table_ptr = malloc (sizeof (*value_table_ptr) * data->values_len);
   if (value_table_ptr == NULL)
     return (-1);
   for (i = 0; i < data->values_len; i++)
     value_table_ptr[i] = value_table[i];
 
   vl.values_len = ds->ds_num;
-  vl.values = (value_t *) malloc (sizeof (value_t) * vl.values_len);
+  vl.values = malloc (sizeof (*vl.values) * vl.values_len);
   if (vl.values == NULL)
   {
     ERROR ("snmp plugin: malloc failed.");
@@ -1065,50 +1142,70 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
 
   vl.interval = host->interval;
 
-  subid = 0;
   have_more = 1;
-
-  while (have_more != 0)
+  memset (&current_suffix, 0, sizeof (current_suffix));
+  while (have_more)
   {
+    _Bool suffix_skipped = 0;
+
+    /* Determine next suffix to handle. */
     if (instance_list != NULL)
     {
-      while ((instance_list_ptr != NULL)
-         && (instance_list_ptr->subid < subid))
-       instance_list_ptr = instance_list_ptr->next;
-
       if (instance_list_ptr == NULL)
       {
-       have_more = 0;
-       continue;
+        have_more = 0;
+        continue;
       }
-      else if (instance_list_ptr->subid > subid)
+
+      memcpy (&current_suffix, &instance_list_ptr->suffix, sizeof (current_suffix));
+    }
+    else /* no instance configured */
+    {
+      csnmp_table_values_t *ptr = value_table_ptr[0];
+      if (ptr == NULL)
       {
-       subid = instance_list_ptr->subid;
-       continue;
+        have_more = 0;
+        continue;
       }
-    } /* if (instance_list != NULL) */
 
+      memcpy (&current_suffix, &ptr->suffix, sizeof (current_suffix));
+    }
+
+    /* Update all the value_table_ptr to point at the entry with the same
+     * trailing partial OID */
     for (i = 0; i < data->values_len; i++)
     {
       while ((value_table_ptr[i] != NULL)
-         && (value_table_ptr[i]->subid < subid))
-       value_table_ptr[i] = value_table_ptr[i]->next;
+          && (csnmp_oid_compare (&value_table_ptr[i]->suffix, &current_suffix) < 0))
+        value_table_ptr[i] = value_table_ptr[i]->next;
 
       if (value_table_ptr[i] == NULL)
       {
-       have_more = 0;
-       break;
+        have_more = 0;
+        break;
       }
-      else if (value_table_ptr[i]->subid > subid)
+      else if (csnmp_oid_compare (&value_table_ptr[i]->suffix, &current_suffix) > 0)
       {
-       subid = value_table_ptr[i]->subid;
-       break;
+        /* This suffix is missing in the subtree. Indicate this with the
+         * "suffix_skipped" flag and try the next instance / suffix. */
+        suffix_skipped = 1;
+        break;
       }
     } /* for (i = 0; i < columns; i++) */
-    /* The subid has been increased - start scanning from the beginning
-     * again.. */
-    if (i < data->values_len)
+
+    if (!have_more)
+      break;
+
+    /* Matching the values failed. Start from the beginning again. */
+    if (suffix_skipped)
+    {
+      if (instance_list != NULL)
+        instance_list_ptr = instance_list_ptr->next;
+      else
+        value_table_ptr[0] = value_table_ptr[0]->next;
+
       continue;
+    }
 
     /* if we reach this line, all value_table_ptr[i] are non-NULL and are set
      * to the same subid. instance_list_ptr is either NULL or points to the
@@ -1117,10 +1214,12 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
     for (i = 1; i < data->values_len; i++)
     {
       assert (value_table_ptr[i] != NULL);
-      assert (value_table_ptr[i-1]->subid == value_table_ptr[i]->subid);
+      assert (csnmp_oid_compare (&value_table_ptr[i-1]->suffix,
+            &value_table_ptr[i]->suffix) == 0);
     }
     assert ((instance_list_ptr == NULL)
-       || (instance_list_ptr->subid == value_table_ptr[0]->subid));
+        || (csnmp_oid_compare (&instance_list_ptr->suffix,
+            &value_table_ptr[0]->suffix) == 0));
 #endif
 
     sstrncpy (vl.type, data->type, sizeof (vl.type));
@@ -1129,15 +1228,15 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
       char temp[DATA_MAX_NAME_LEN];
 
       if (instance_list_ptr == NULL)
-       ssnprintf (temp, sizeof (temp), "%"PRIu32, (uint32_t) subid);
+        csnmp_oid_to_string (temp, sizeof (temp), &current_suffix);
       else
-       sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
+        sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
 
       if (data->instance_prefix == NULL)
-       sstrncpy (vl.type_instance, temp, sizeof (vl.type_instance));
+        sstrncpy (vl.type_instance, temp, sizeof (vl.type_instance));
       else
-       ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
-           data->instance_prefix, temp);
+        ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
+            data->instance_prefix, temp);
     }
 
     for (i = 0; i < data->values_len; i++)
@@ -1146,8 +1245,11 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
     /* If we get here `vl.type_instance' and all `vl.values' have been set */
     plugin_dispatch_values (&vl);
 
-    subid++;
-  } /* while (have_more != 0) */
+    if (instance_list != NULL)
+      instance_list_ptr = instance_list_ptr->next;
+    else
+      value_table_ptr[0] = value_table_ptr[0]->next;
+  } /* while (have_more) */
 
   sfree (vl.values);
   sfree (value_table_ptr);
@@ -1168,13 +1270,13 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   int status;
   int i;
 
-  /* `value_table' and `value_table_ptr' implement a linked list for each
-   * value. `instance_list' and `instance_list_ptr' implement a linked list of
+  /* `value_list_head' and `value_list_tail' implement a linked list for each
+   * value. `instance_list_head' and `instance_list_tail' implement a linked list of
    * instance names. This is used to jump gaps in the table. */
-  csnmp_list_instances_t *instance_list;
-  csnmp_list_instances_t *instance_list_ptr;
-  csnmp_table_values_t **value_table;
-  csnmp_table_values_t **value_table_ptr;
+  csnmp_list_instances_t *instance_list_head;
+  csnmp_list_instances_t *instance_list_tail;
+  csnmp_table_values_t **value_list_head;
+  csnmp_table_values_t **value_list_tail;
 
   DEBUG ("snmp plugin: csnmp_read_table (host = %s, data = %s)",
       host->name, data->name);
@@ -1195,7 +1297,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   if (ds->ds_num != data->values_len)
   {
     ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i",
-       data->type, ds->ds_num, data->values_len);
+        data->type, ds->ds_num, data->values_len);
     return (-1);
   }
 
@@ -1213,20 +1315,22 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   else
     oid_list_len--;
 
-  /* Allocate the `value_table' */
-  value_table = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *)
-      * 2 * data->values_len);
-  if (value_table == NULL)
+  /* We're going to construct n linked lists, one for each "value".
+   * value_list_head will contain pointers to the heads of these linked lists,
+   * value_list_tail will contain pointers to the tail of the lists. */
+  value_list_head = calloc (data->values_len, sizeof (*value_list_head));
+  value_list_tail = calloc (data->values_len, sizeof (*value_list_tail));
+  if ((value_list_head == NULL) || (value_list_tail == NULL))
   {
-    ERROR ("snmp plugin: csnmp_read_table: malloc failed.");
+    ERROR ("snmp plugin: csnmp_read_table: calloc failed.");
     sfree (oid_list);
+    sfree (value_list_head);
+    sfree (value_list_tail);
     return (-1);
   }
-  memset (value_table, '\0', sizeof (csnmp_table_values_t *) * 2 * data->values_len);
-  value_table_ptr = value_table + data->values_len;
-  
-  instance_list = NULL;
-  instance_list_ptr = NULL;
+
+  instance_list_head = NULL;
+  instance_list_tail = NULL;
 
   status = 0;
   while (status == 0)
@@ -1252,11 +1356,11 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
 
       c_complain (LOG_ERR, &host->complaint,
-         "snmp plugin: host %s: snmp_sess_synch_response failed: %s",
-         host->name, (errstr == NULL) ? "Unknown problem" : errstr);
+          "snmp plugin: host %s: snmp_sess_synch_response failed: %s",
+          host->name, (errstr == NULL) ? "Unknown problem" : errstr);
 
       if (res != NULL)
-       snmp_free_pdu (res);
+        snmp_free_pdu (res);
       res = NULL;
 
       sfree (errstr);
@@ -1268,8 +1372,8 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
     status = 0;
     assert (res != NULL);
     c_release (LOG_INFO, &host->complaint,
-       "snmp plugin: host %s: snmp_sess_synch_response successful.",
-       host->name);
+        "snmp plugin: host %s: snmp_sess_synch_response successful.",
+        host->name);
 
     vb = res->variables;
     if (vb == NULL)
@@ -1286,76 +1390,90 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       break;
     }
 
-    /* if an instance-OID is configured.. */
+    /* Copy the OID of the value used as instance to oid_list, if an instance
+     * is configured. */
     if (data->instance.oid.oid_len > 0)
     {
       /* Allocate a new `csnmp_list_instances_t', insert the instance name and
        * add it to the list */
-      if (csnmp_instance_list_add (&instance_list, &instance_list_ptr,
+      if (csnmp_instance_list_add (&instance_list_head, &instance_list_tail,
            res, host, data) != 0)
       {
-       ERROR ("snmp plugin: csnmp_instance_list_add failed.");
-       status = -1;
-       break;
+        ERROR ("snmp plugin: csnmp_instance_list_add failed.");
+        status = -1;
+        break;
       }
 
-      /* Set vb on the last variable */
+      /* The instance OID is added to the list of OIDs to GET from the
+       * snmp agent last, so set vb on the last variable returned and copy
+       * that OID. */
       for (vb = res->variables;
-         (vb != NULL) && (vb->next_variable != NULL);
-         vb = vb->next_variable)
-       /* do nothing */;
+          (vb != NULL) && (vb->next_variable != NULL);
+          vb = vb->next_variable)
+        /* do nothing */;
       assert (vb != NULL);
 
-      /* Copy OID to oid_list[data->values_len] */
+      /* Copy the OID of the instance value to oid_list[data->values_len].
+       * "oid_list" is used for the next GETNEXT request. */
       memcpy (oid_list[data->values_len].oid, vb->name,
-         sizeof (oid) * vb->name_length);
+          sizeof (oid) * vb->name_length);
       oid_list[data->values_len].oid_len = vb->name_length;
     }
 
+    /* Iterate over all the (non-instance) values returned by the agent. The
+     * (i < value_len) check will make sure we're not handling the instance OID
+     * twice. */
     for (vb = res->variables, i = 0;
-       (vb != NULL) && (i < data->values_len);
-       vb = vb->next_variable, i++)
+        (vb != NULL) && (i < data->values_len);
+        vb = vb->next_variable, i++)
     {
       csnmp_table_values_t *vt;
+      oid_t vb_name;
+      oid_t suffix;
 
-      /* Check if we left the subtree */
-      if (snmp_oid_ncompare (data->values[i].oid,
-           data->values[i].oid_len,
-           vb->name, vb->name_length,
-           data->values[i].oid_len) != 0)
+      csnmp_oid_init (&vb_name, vb->name, vb->name_length);
+
+      /* Calculate the current suffix. This is later used to check that the
+       * suffix is increasing. This also checks if we left the subtree */
+      status = csnmp_oid_suffix (&suffix, &vb_name, data->values + i);
+      if (status != 0)
       {
-       DEBUG ("snmp plugin: host = %s; data = %s; Value %i left its subtree.",
-           host->name, data->name, i);
-       continue;
+        DEBUG ("snmp plugin: host = %s; data = %s; Value %i failed. "
+            "It probably left its subtree.",
+            host->name, data->name, i);
+        continue;
       }
 
-      if ((value_table_ptr[i] != NULL)
-         && (vb->name[vb->name_length - 1] <= value_table_ptr[i]->subid))
+      /* Make sure the OIDs returned by the agent are increasing. Otherwise our
+       * table matching algorithm will get confused. */
+      if ((value_list_tail[i] != NULL)
+          && (csnmp_oid_compare (&suffix, &value_list_tail[i]->suffix) <= 0))
       {
-       DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
-           "SUBID is not increasing.",
-           host->name, data->name, i);
-       continue;
+        DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
+            "Suffix is not increasing.",
+            host->name, data->name, i);
+        continue;
       }
 
-      vt = (csnmp_table_values_t *) malloc (sizeof (csnmp_table_values_t));
+      vt = malloc (sizeof (*vt));
       if (vt == NULL)
       {
-       ERROR ("snmp plugin: malloc failed.");
-       status = -1;
-       break;
+        ERROR ("snmp plugin: malloc failed.");
+        status = -1;
+        break;
       }
+      memset (vt, 0, sizeof (*vt));
 
-      vt->subid = vb->name[vb->name_length - 1];
       vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type,
           data->scale, data->shift, host->name, data->name);
+      memcpy (&vt->suffix, &suffix, sizeof (vt->suffix));
       vt->next = NULL;
 
-      if (value_table_ptr[i] == NULL)
-       value_table[i] = vt;
+      if (value_list_tail[i] == NULL)
+        value_list_head[i] = vt;
       else
-       value_table_ptr[i]->next = vt;
-      value_table_ptr[i] = vt;
+        value_list_tail[i]->next = vt;
+      value_list_tail[i] = vt;
 
       /* Copy OID to oid_list[i + 1] */
       memcpy (oid_list[i].oid, vb->name, sizeof (oid) * vb->name_length);
@@ -1372,28 +1490,28 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   res = NULL;
 
   if (status == 0)
-    csnmp_dispatch_table (host, data, instance_list, value_table);
+    csnmp_dispatch_table (host, data, instance_list_head, value_list_head);
 
   /* Free all allocated variables here */
-  while (instance_list != NULL)
+  while (instance_list_head != NULL)
   {
-    instance_list_ptr = instance_list->next;
-    sfree (instance_list);
-    instance_list = instance_list_ptr;
+    csnmp_list_instances_t *next = instance_list_head->next;
+    sfree (instance_list_head);
+    instance_list_head = next;
   }
 
   for (i = 0; i < data->values_len; i++)
   {
-    csnmp_table_values_t *tmp;
-    while (value_table[i] != NULL)
+    while (value_list_head[i] != NULL)
     {
-      tmp = value_table[i]->next;
-      sfree (value_table[i]);
-      value_table[i] = tmp;
+      csnmp_table_values_t *next = value_list_head[i]->next;
+      sfree (value_list_head[i]);
+      value_list_head[i] = next;
     }
   }
 
-  sfree (value_table);
+  sfree (value_list_head);
+  sfree (value_list_tail);
   sfree (oid_list);
 
   return (0);
@@ -1430,7 +1548,7 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
   if (ds->ds_num != data->values_len)
   {
     ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i",
-       data->type, ds->ds_num, data->values_len);
+        data->type, ds->ds_num, data->values_len);
     return (-1);
   }
 
@@ -1473,7 +1591,7 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
 
     snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
     ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s",
-       host->name, (errstr == NULL) ? "Unknown problem" : errstr);
+        host->name, (errstr == NULL) ? "Unknown problem" : errstr);
 
     if (res != NULL)
       snmp_free_pdu (res);
@@ -1491,13 +1609,13 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
 #if COLLECT_DEBUG
     char buffer[1024];
     snprint_variable (buffer, sizeof (buffer),
-       vb->name, vb->name_length, vb);
+        vb->name, vb->name_length, vb);
     DEBUG ("snmp plugin: Got this variable: %s", buffer);
 #endif /* COLLECT_DEBUG */
 
     for (i = 0; i < data->values_len; i++)
       if (snmp_oid_compare (data->values[i].oid, data->values[i].oid_len,
-           vb->name, vb->name_length) == 0)
+            vb->name, vb->name_length) == 0)
         vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type,
             data->scale, data->shift, host->name, data->name);
   } /* for (res->variables) */
index 397969e..46d3534 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/swap.c
- * Copyright (C) 2005-2010  Florian octo Forster
+ * Copyright (C) 2005-2012  Florian octo Forster
  * Copyright (C) 2009       Stefan Völkel
  * Copyright (C) 2009       Manuel Sanmartin
  * Copyright (C) 2010       Aurélien Reynaud
 #define MAX(x,y) ((x) > (y) ? (x) : (y))
 
 #if KERNEL_LINUX
-# define SWAP_HAVE_CONFIG 1
-/* No global variables */
+# define SWAP_HAVE_REPORT_BY_DEVICE 1
+static derive_t pagesize;
+static _Bool report_bytes = 0;
+static _Bool report_by_device = 0;
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
-# define SWAP_HAVE_CONFIG 1
+# define SWAP_HAVE_REPORT_BY_DEVICE 1
 static derive_t pagesize;
+static _Bool report_by_device = 0;
 /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
 
 #elif defined(VM_SWAPUSAGE)
@@ -101,23 +104,37 @@ static perfstat_memory_total_t pmemory;
 # error "No applicable input method."
 #endif /* HAVE_LIBSTATGRAB */
 
-#if SWAP_HAVE_CONFIG
 static const char *config_keys[] =
 {
+       "ReportBytes",
        "ReportByDevice"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-static _Bool report_by_device = 0;
-
 static int swap_config (const char *key, const char *value) /* {{{ */
 {
-       if (strcasecmp ("ReportByDevice", key) == 0)
+       if (strcasecmp ("ReportBytes", key) == 0)
+       {
+#if KERNEL_LINUX
+               report_bytes = IS_TRUE (value) ? 1 : 0;
+#else
+               WARNING ("swap plugin: The \"ReportBytes\" option is only "
+                               "valid under Linux. "
+                               "The option is going to be ignored.");
+#endif
+       }
+       else if (strcasecmp ("ReportByDevice", key) == 0)
        {
+#if SWAP_HAVE_REPORT_BY_DEVICE
                if (IS_TRUE (value))
                        report_by_device = 1;
                else
                        report_by_device = 0;
+#else
+               WARNING ("swap plugin: The \"ReportByDevice\" option is not "
+                               "supported on this platform. "
+                               "The option is going to be ignored.");
+#endif /* SWAP_HAVE_REPORT_BY_DEVICE */
        }
        else
        {
@@ -126,12 +143,11 @@ static int swap_config (const char *key, const char *value) /* {{{ */
 
        return (0);
 } /* }}} int swap_config */
-#endif /* SWAP_HAVE_CONFIG */
 
 static int swap_init (void) /* {{{ */
 {
 #if KERNEL_LINUX
-       /* No init stuff */
+       pagesize = (derive_t) sysconf (_SC_PAGESIZE);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
@@ -205,7 +221,7 @@ static void swap_submit_gauge (const char *plugin_instance, /* {{{ */
        swap_submit (plugin_instance, "swap", type_instance, v);
 } /* }}} void swap_submit_gauge */
 
-#if KERNEL_LINUX
+#if KERNEL_LINUX || HAVE_PERFSTAT
 static void swap_submit_derive (const char *plugin_instance, /* {{{ */
                const char *type_instance, derive_t value)
 {
@@ -214,7 +230,9 @@ static void swap_submit_derive (const char *plugin_instance, /* {{{ */
        v.derive = value;
        swap_submit (plugin_instance, "swap_io", type_instance, v);
 } /* }}} void swap_submit_derive */
+#endif
 
+#if KERNEL_LINUX
 static int swap_read_separate (void) /* {{{ */
 {
        FILE *fh;
@@ -406,6 +424,12 @@ static int swap_read_io (void) /* {{{ */
        if (have_data != 0x03)
                return (ENOENT);
 
+       if (report_bytes)
+       {
+               swap_in = swap_in * pagesize;
+               swap_out = swap_out * pagesize;
+       }
+
        swap_submit_derive (NULL, "in",  swap_in);
        swap_submit_derive (NULL, "out", swap_out);
 
@@ -764,8 +788,12 @@ static int swap_read (void) /* {{{ */
                         sstrerror (errno, errbuf, sizeof (errbuf)));
                 return (-1);
         }
+
        swap_submit_gauge (NULL, "used", (gauge_t) (pmemory.pgsp_total - pmemory.pgsp_free) * pagesize);
        swap_submit_gauge (NULL, "free", (gauge_t) pmemory.pgsp_free * pagesize );
+       swap_submit_gauge (NULL, "reserved", (gauge_t) pmemory.pgsp_rsvd * pagesize);
+       swap_submit_derive (NULL, "in",  (derive_t) pmemory.pgspins * pagesize);
+       swap_submit_derive (NULL, "out", (derive_t) pmemory.pgspouts * pagesize);
 
        return (0);
 } /* }}} int swap_read */
@@ -773,9 +801,8 @@ static int swap_read (void) /* {{{ */
 
 void module_register (void)
 {
-#if SWAP_HAVE_CONFIG
-       plugin_register_config ("swap", swap_config, config_keys, config_keys_num);
-#endif
+       plugin_register_config ("swap", swap_config,
+                       config_keys, config_keys_num);
        plugin_register_init ("swap", swap_init);
        plugin_register_read ("swap", swap_read);
 } /* void module_register */
index ace9dc6..4f5d0c4 100644 (file)
@@ -33,10 +33,12 @@ static int log_level = LOG_DEBUG;
 #else
 static int log_level = LOG_INFO;
 #endif /* COLLECT_DEBUG */
+static int notif_severity = 0;
 
 static const char *config_keys[] =
 {
-       "LogLevel"
+       "LogLevel",
+       "NotifyLevel",
 };
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
@@ -44,26 +46,16 @@ static int sl_config (const char *key, const char *value)
 {
        if (strcasecmp (key, "LogLevel") == 0)
        {
-               if ((strcasecmp (value, "emerg") == 0)
-                               || (strcasecmp (value, "alert") == 0)
-                               || (strcasecmp (value, "crit") == 0)
-                               || (strcasecmp (value, "err") == 0))
-                       log_level = LOG_ERR;
-               else if (strcasecmp (value, "warning") == 0)
-                       log_level = LOG_WARNING;
-               else if (strcasecmp (value, "notice") == 0)
-                       log_level = LOG_NOTICE;
-               else if (strcasecmp (value, "info") == 0)
-                       log_level = LOG_INFO;
-#if COLLECT_DEBUG
-               else if (strcasecmp (value, "debug") == 0)
-                       log_level = LOG_DEBUG;
-#endif
-               else
+               log_level = parse_log_severity (value);
+               if (log_level < 0)
+                       return (1);
+       }
+       else if (strcasecmp (key, "NotifyLevel") == 0)
+       {
+               notif_severity = parse_notif_severity (value);
+               if (notif_severity < 0)
                        return (1);
        }
-       else
-               return (-1);
 
        return (0);
 } /* int sl_config */
@@ -84,11 +76,77 @@ static int sl_shutdown (void)
        return (0);
 }
 
+static int sl_notification (const notification_t *n,
+               user_data_t __attribute__((unused)) *user_data)
+{
+       char  buf[1024] = "";
+       size_t offset = 0;
+       int log_severity;
+       char *severity_string;
+       int status;
+
+       if (n->severity > notif_severity)
+               return (0);
+
+       switch (n->severity)
+       {
+               case NOTIF_FAILURE:
+                       severity_string = "FAILURE";
+                       log_severity = LOG_ERR;
+                       break;
+               case NOTIF_WARNING:
+                       severity_string = "WARNING";
+                       log_severity = LOG_WARNING;
+                       break;
+               case NOTIF_OKAY:
+                       severity_string = "OKAY";
+                       log_severity = LOG_NOTICE;
+                       break;
+               default:
+                       severity_string = "UNKNOWN";
+                       log_severity = LOG_ERR;
+       }
+
+#define BUFFER_ADD(...) do { \
+       status = ssnprintf (&buf[offset], sizeof (buf) - offset, \
+                       __VA_ARGS__); \
+       if (status < 1) \
+               return (-1); \
+       else if (((size_t) status) >= (sizeof (buf) - offset)) \
+               return (-ENOMEM); \
+       else \
+               offset += ((size_t) status); \
+} while (0)
+
+#define BUFFER_ADD_FIELD(field) do { \
+       if (n->field[0]) \
+               BUFFER_ADD (", " #field " = %s", n->field); \
+} while (0)
+
+       BUFFER_ADD ("Notification: severity = %s", severity_string);
+       BUFFER_ADD_FIELD (host);
+       BUFFER_ADD_FIELD (plugin);
+       BUFFER_ADD_FIELD (plugin_instance);
+       BUFFER_ADD_FIELD (type);
+       BUFFER_ADD_FIELD (type_instance);
+       BUFFER_ADD_FIELD (message);
+
+#undef BUFFER_ADD_FIELD
+#undef BUFFER_ADD
+
+       buf[sizeof (buf) - 1] = '\0';
+
+       sl_log (log_severity, buf, NULL);
+
+       return (0);
+} /* int sl_notification */
+
 void module_register (void)
 {
        openlog ("collectd", LOG_CONS | LOG_PID, LOG_DAEMON);
 
        plugin_register_config ("syslog", sl_config, config_keys, config_keys_num);
        plugin_register_log ("syslog", sl_log, /* user_data = */ NULL);
+       plugin_register_notification ("syslog", sl_notification, NULL);
        plugin_register_shutdown ("syslog", sl_shutdown);
 } /* void module_register(void) */
index af224f1..bef03e5 100644 (file)
@@ -29,6 +29,9 @@ struct ts_data_s
 {
        double factor;
        double offset;
+
+       char **data_sources;
+       size_t data_sources_num;
 };
 typedef struct ts_data_s ts_data_t;
 
@@ -300,17 +303,86 @@ static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
        return (0);
 } /* }}} int ts_config_set_double */
 
+static int ts_config_add_data_source(ts_data_t *data, /* {{{ */
+               oconfig_item_t *ci)
+{
+       size_t new_data_sources_num;
+       char **temp;
+       int i;
+
+       /* Check number of arbuments. */
+       if (ci->values_num < 1)
+       {
+               ERROR ("`value' match: `%s' needs at least one argument.",
+                               ci->key);
+               return (-1);
+       }
+
+       /* Check type of arguments */
+       for (i = 0; i < ci->values_num; i++)
+       {
+               if (ci->values[i].type == OCONFIG_TYPE_STRING)
+                       continue;
+
+               ERROR ("`value' match: `%s' accepts only string arguments "
+                               "(argument %i is a %s).",
+                               ci->key, i + 1,
+                               (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
+                               ? "truth value" : "number");
+               return (-1);
+       }
+
+       /* Allocate space for the char pointers */
+       new_data_sources_num = data->data_sources_num + ((size_t) ci->values_num);
+       temp = (char **) realloc (data->data_sources,
+                       new_data_sources_num * sizeof (char *));
+       if (temp == NULL)
+       {
+               ERROR ("`value' match: realloc failed.");
+               return (-1);
+       }
+       data->data_sources = temp;
+
+       /* Copy the strings, allocating memory as needed.  */
+       for (i = 0; i < ci->values_num; i++)
+       {
+               size_t j;
+
+               /* If we get here, there better be memory for us to write to.  */
+               assert (data->data_sources_num < new_data_sources_num);
+
+               j = data->data_sources_num;
+               data->data_sources[j] = sstrdup (ci->values[i].value.string);
+               if (data->data_sources[j] == NULL)
+               {
+                       ERROR ("`value' match: sstrdup failed.");
+                       continue;
+               }
+               data->data_sources_num++;
+       }
+
+       return (0);
+} /* }}} int ts_config_add_data_source */
+
 static int ts_destroy (void **user_data) /* {{{ */
 {
-       ts_data_t **data;
+       ts_data_t *data;
 
        if (user_data == NULL)
                return (-EINVAL);
 
-       data = (ts_data_t **) user_data;
+       data = (ts_data_t *) *user_data;
 
-       free (*data);
-       *data = NULL;
+       if ((data != NULL) && (data->data_sources != NULL))
+       {
+               size_t i;
+               for (i = 0; i < data->data_sources_num; i++)
+                       sfree (data->data_sources[i]);
+               sfree (data->data_sources);
+       }
+
+       sfree (data);
+       *user_data = NULL;
 
        return (0);
 } /* }}} int ts_destroy */
@@ -341,6 +413,8 @@ static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
                                status = ts_config_set_double (&data->factor, child);
                else if (strcasecmp ("Offset", child->key) == 0)
                                status = ts_config_set_double (&data->offset, child);
+               else if (strcasecmp ("DataSource", child->key) == 0)
+                               status = ts_config_add_data_source(data, child);
                else
                {
                        ERROR ("Target `scale': The `%s' configuration option is not understood "
@@ -393,6 +467,18 @@ static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
 
        for (i = 0; i < ds->ds_num; i++)
        {
+               /* If we've got a list of data sources, is it in the list? */
+               if (data->data_sources) {
+                       size_t j;
+                       for (j = 0; j < data->data_sources_num; j++)
+                               if (strcasecmp(ds->ds[i].name, data->data_sources[j]) == 0)
+                                       break;
+
+                       /* No match, ignore */
+                       if (j >= data->data_sources_num)
+                               continue;
+               }
+
                if (ds->ds[i].type == DS_TYPE_COUNTER)
                        ts_invoke_counter (ds, vl, data, i);
                else if (ds->ds[i].type == DS_TYPE_GAUGE)
index 6a7e32d..765b892 100644 (file)
 #endif
 
 #if KERNEL_LINUX
+# include <asm/types.h>
+/* sys/socket.h is necessary to compile when using netlink on older systems. */
+# include <sys/socket.h>
+# include <linux/netlink.h>
+# include <linux/inet_diag.h>
+# include <sys/socket.h>
+# include <arpa/inet.h>
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_SYSCTLBYNAME
 # include <netinet/tcp_var.h>
 # include <netdb.h>
 # include <arpa/inet.h>
-# include <nlist.h>
+# if !defined(HAVE_BSD_NLIST_H) || !HAVE_BSD_NLIST_H
+#  include <nlist.h>
+# else /* HAVE_BSD_NLIST_H */
+#  include <bsd/nlist.h>
+# endif
 # include <kvm.h>
 /* #endif HAVE_LIBKVM_NLIST */
 
 #endif /* KERNEL_AIX */
 
 #if KERNEL_LINUX
+struct nlreq {
+  struct nlmsghdr nlh;
+  struct inet_diag_req r;
+};
+
 static const char *tcp_state[] =
 {
   "", /* 0 */
@@ -259,6 +275,17 @@ static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 static int port_collect_listening = 0;
 static port_entry_t *port_list_head = NULL;
 
+#if KERNEL_LINUX
+static uint32_t sequence_number = 0;
+
+enum
+{
+  SRC_DUNNO,
+  SRC_NETLINK,
+  SRC_PROC
+} linux_source = SRC_DUNNO;
+#endif
+
 static void conn_submit_port_entry (port_entry_t *pe)
 {
   value_t values[1];
@@ -415,6 +442,140 @@ static int conn_handle_ports (uint16_t port_local, uint16_t port_remote, uint8_t
 } /* int conn_handle_ports */
 
 #if KERNEL_LINUX
+/* Returns zero on success, less than zero on socket error and greater than
+ * zero on other errors. */
+static int conn_read_netlink (void)
+{
+  int fd;
+  struct sockaddr_nl nladdr;
+  struct nlreq req;
+  struct msghdr msg;
+  struct iovec iov;
+  struct inet_diag_msg *r;
+  char buf[8192];
+
+  /* If this fails, it's likely a permission problem. We'll fall back to
+   * reading this information from files below. */
+  fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG);
+  if (fd < 0)
+  {
+    ERROR ("tcpconns plugin: conn_read_netlink: socket(AF_NETLINK, SOCK_RAW, "
+       "NETLINK_INET_DIAG) failed: %s",
+       sstrerror (errno, buf, sizeof (buf)));
+    return (-1);
+  }
+
+  memset(&nladdr, 0, sizeof(nladdr));
+  nladdr.nl_family = AF_NETLINK;
+
+  memset(&req, 0, sizeof(req));
+  req.nlh.nlmsg_len = sizeof(req);
+  req.nlh.nlmsg_type = TCPDIAG_GETSOCK;
+  /* NLM_F_ROOT: return the complete table instead of a single entry.
+   * NLM_F_MATCH: return all entries matching criteria (not implemented)
+   * NLM_F_REQUEST: must be set on all request messages */
+  req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+  req.nlh.nlmsg_pid = 0;
+  /* The sequence_number is used to track our messages. Since netlink is not
+   * reliable, we don't want to end up with a corrupt or incomplete old
+   * message in case the system is/was out of memory. */
+  req.nlh.nlmsg_seq = ++sequence_number;
+  req.r.idiag_family = AF_INET;
+  req.r.idiag_states = 0xfff;
+  req.r.idiag_ext = 0;
+
+  memset(&iov, 0, sizeof(iov));
+  iov.iov_base = &req;
+  iov.iov_len = sizeof(req);
+
+  memset(&msg, 0, sizeof(msg));
+  msg.msg_name = (void*)&nladdr;
+  msg.msg_namelen = sizeof(nladdr);
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+
+  if (sendmsg (fd, &msg, 0) < 0)
+  {
+    ERROR ("tcpconns plugin: conn_read_netlink: sendmsg(2) failed: %s",
+       sstrerror (errno, buf, sizeof (buf)));
+    close (fd);
+    return (-1);
+  }
+
+  iov.iov_base = buf;
+  iov.iov_len = sizeof(buf);
+
+  while (1)
+  {
+    int status;
+    struct nlmsghdr *h;
+
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = (void*)&nladdr;
+    msg.msg_namelen = sizeof(nladdr);
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    status = recvmsg(fd, (void *) &msg, /* flags = */ 0);
+    if (status < 0)
+    {
+      if ((errno == EINTR) || (errno == EAGAIN))
+        continue;
+
+      ERROR ("tcpconns plugin: conn_read_netlink: recvmsg(2) failed: %s",
+         sstrerror (errno, buf, sizeof (buf)));
+      close (fd);
+      return (-1);
+    }
+    else if (status == 0)
+    {
+      close (fd);
+      DEBUG ("tcpconns plugin: conn_read_netlink: Unexpected zero-sized "
+         "reply from netlink socket.");
+      return (0);
+    }
+
+    h = (struct nlmsghdr*)buf;
+    while (NLMSG_OK(h, status))
+    {
+      if (h->nlmsg_seq != sequence_number)
+      {
+       h = NLMSG_NEXT(h, status);
+       continue;
+      }
+
+      if (h->nlmsg_type == NLMSG_DONE)
+      {
+       close (fd);
+       return (0);
+      }
+      else if (h->nlmsg_type == NLMSG_ERROR)
+      {
+       struct nlmsgerr *msg_error;
+
+       msg_error = NLMSG_DATA(h);
+       WARNING ("tcpconns plugin: conn_read_netlink: Received error %i.",
+           msg_error->error);
+
+       close (fd);
+       return (1);
+      }
+
+      r = NLMSG_DATA(h);
+
+      /* This code does not (need to) distinguish between IPv4 and IPv6. */
+      conn_handle_ports (ntohs(r->id.idiag_sport),
+         ntohs(r->id.idiag_dport),
+         r->idiag_state);
+
+      h = NLMSG_NEXT(h, status);
+    } /* while (NLMSG_OK) */
+  } /* while (1) */
+
+  /* Not reached because the while() loop above handles the exit condition. */
+  return (0);
+} /* int conn_read_netlink */
+
 static int conn_handle_line (char *buffer)
 {
   char *fields[32];
@@ -549,26 +710,55 @@ static int conn_init (void)
 
 static int conn_read (void)
 {
-  int errors_num = 0;
+  int status;
 
   conn_reset_port_entry ();
 
-  if (conn_read_file ("/proc/net/tcp") != 0)
-    errors_num++;
-  if (conn_read_file ("/proc/net/tcp6") != 0)
-    errors_num++;
-
-  if (errors_num < 2)
+  if (linux_source == SRC_NETLINK)
   {
-    conn_submit_all ();
+    status = conn_read_netlink ();
   }
-  else
+  else if (linux_source == SRC_PROC)
   {
-    ERROR ("tcpconns plugin: Neither /proc/net/tcp nor /proc/net/tcp6 "
-       "coult be read.");
-    return (-1);
+    int errors_num = 0;
+
+    if (conn_read_file ("/proc/net/tcp") != 0)
+      errors_num++;
+    if (conn_read_file ("/proc/net/tcp6") != 0)
+      errors_num++;
+
+    if (errors_num < 2)
+      status = 0;
+    else
+      status = ENOENT;
+  }
+  else /* if (linux_source == SRC_DUNNO) */
+  {
+    /* Try to use netlink for getting this data, it is _much_ faster on systems
+     * with a large amount of connections. */
+    status = conn_read_netlink ();
+    if (status == 0)
+    {
+      INFO ("tcpconns plugin: Reading from netlink succeeded. "
+         "Will use the netlink method from now on.");
+      linux_source = SRC_NETLINK;
+    }
+    else
+    {
+      INFO ("tcpconns plugin: Reading from netlink failed. "
+         "Will read from /proc from now on.");
+      linux_source = SRC_PROC;
+
+      /* return success here to avoid the "plugin failed" message. */
+      return (0);
+    }
   }
 
+  if (status == 0)
+    conn_submit_all ();
+  else
+    return (status);
+
   return (0);
 } /* int conn_read */
 /* #endif KERNEL_LINUX */
index e6345ab..ed27b78 100644 (file)
@@ -8,6 +8,7 @@ ath_nodes               value:GAUGE:0:65535
 ath_stat               value:DERIVE:0:U
 bitrate                        value:GAUGE:0:4294967295
 bytes                  value:GAUGE:0:U
+cache_eviction         value:DERIVE:0:U
 cache_operation                value:DERIVE:0:U
 cache_ratio            value:GAUGE:0:100
 cache_result           value:DERIVE:0:U
@@ -63,6 +64,7 @@ frequency             value:GAUGE:0:U
 frequency_offset       value:GAUGE:-1000000:1000000
 fscache_stat           value:DERIVE:0:U
 gauge                  value:GAUGE:U:U
+hash_collisions                value:DERIVE:0:U
 http_request_methods   value:DERIVE:0:U
 http_requests          value:DERIVE:0:U
 http_response_codes    value:DERIVE:0:U
@@ -84,6 +86,7 @@ irq                   value:DERIVE:0:U
 latency                        value:GAUGE:0:65535
 links                  value:GAUGE:0:U
 load                   shortterm:GAUGE:0:100, midterm:GAUGE:0:100, longterm:GAUGE:0:100
+md_disks               value:GAUGE:0:U
 memcached_command      value:DERIVE:0:U
 memcached_connections  value:GAUGE:0:U
 memcached_items                value:GAUGE:0:U
@@ -91,6 +94,7 @@ memcached_octets      rx:DERIVE:0:U, tx:DERIVE:0:U
 memcached_ops          value:DERIVE:0:U
 memory                 value:GAUGE:0:281474976710656
 multimeter             value:GAUGE:U:U
+mutex_operations       value:DERIVE:0:U
 mysql_commands         value:DERIVE:0:U
 mysql_handler          value:DERIVE:0:U
 mysql_locks            value:DERIVE:0:U
index 337978d..2c1665f 100644 (file)
@@ -205,6 +205,7 @@ static void *us_handle_client (void *arg)
                close (fdin);
                close (fdout);
                pthread_exit ((void *) 1);
+               return ((void *) 1);
        }
 
        fhout = fdopen (fdout, "w");
@@ -216,6 +217,7 @@ static void *us_handle_client (void *arg)
                fclose (fhin); /* this closes fdin as well */
                close (fdout);
                pthread_exit ((void *) 1);
+               return ((void *) 1);
        }
 
        /* change output buffer to line buffered mode */
@@ -227,6 +229,7 @@ static void *us_handle_client (void *arg)
                fclose (fhin);
                fclose (fhout);
                pthread_exit ((void *) 1);
+               return ((void *) 0);
        }
 
        while (42)
@@ -240,6 +243,9 @@ static void *us_handle_client (void *arg)
                errno = 0;
                if (fgets (buffer, sizeof (buffer), fhin) == NULL)
                {
+                       if ((errno == EINTR) || (errno == EAGAIN))
+                               continue;
+
                        if (errno != 0)
                        {
                                char errbuf[1024];
@@ -268,6 +274,7 @@ static void *us_handle_client (void *arg)
                        fclose (fhin);
                        fclose (fhout);
                        pthread_exit ((void *) 1);
+                       return ((void *) 1);
                }
 
                if (strcasecmp (fields[0], "getval") == 0)
@@ -318,6 +325,9 @@ static void *us_server_thread (void __attribute__((unused)) *arg)
        pthread_t th;
        pthread_attr_t th_attr;
 
+       pthread_attr_init (&th_attr);
+       pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED);
+
        if (us_open_socket () != 0)
                pthread_exit ((void *) 1);
 
@@ -336,6 +346,7 @@ static void *us_server_thread (void __attribute__((unused)) *arg)
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
                        close (sock_fd);
                        sock_fd = -1;
+                       pthread_attr_destroy (&th_attr);
                        pthread_exit ((void *) 1);
                }
 
@@ -352,9 +363,6 @@ static void *us_server_thread (void __attribute__((unused)) *arg)
 
                DEBUG ("Spawning child to handle connection on fd #%i", *remote_fd);
 
-               pthread_attr_init (&th_attr);
-               pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED);
-
                status = plugin_thread_create (&th, &th_attr,
                                us_handle_client, (void *) remote_fd);
                if (status != 0)
@@ -370,6 +378,7 @@ static void *us_server_thread (void __attribute__((unused)) *arg)
 
        close (sock_fd);
        sock_fd = -1;
+       pthread_attr_destroy (&th_attr);
 
        status = unlink ((sock_file != NULL) ? sock_file : US_DEFAULT_PATH);
        if (status != 0)
index d2ba963..064c3ce 100644 (file)
 /* Using sysctl interface to retrieve the boot time on *BSD / Darwin / OS X systems */
 /* #endif HAVE_SYS_SYSCTL_H */
 
+#elif HAVE_PERFSTAT
+# include <sys/protosw.h>
+# include <libperfstat.h>
+/* Using perfstat_cpu_total to retrive the boot time in AIX */
+/* #endif HAVE_PERFSTAT */
+
 #else
 # error "No applicable input method."
 #endif
@@ -203,7 +209,29 @@ static int uptime_init (void) /* {{{ */
                                "but `boottime' is zero!");
                return (-1);
        }
-#endif /* HAVE_SYS_SYSCTL_H */
+/* #endif HAVE_SYS_SYSCTL_H */
+
+#elif HAVE_PERFSTAT
+       int status;
+       perfstat_cpu_total_t cputotal;
+       int hertz;
+
+       status = perfstat_cpu_total(NULL, &cputotal,
+               sizeof(perfstat_cpu_total_t), 1);
+       if (status < 0)
+       {
+               char errbuf[1024];
+               ERROR ("uptime plugin: perfstat_cpu_total: %s",
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       hertz = sysconf(_SC_CLK_TCK);
+       if (hertz <= 0)
+               hertz = HZ;
+
+       boottime = time(NULL) - cputotal.lbolt / hertz;
+#endif /* HAVE_PERFSTAT */
 
        return (0);
 } /* }}} int uptime_init */
index 0436d8f..f71b1fd 100644 (file)
@@ -487,6 +487,8 @@ c_avl_tree_t *c_avl_create (int (*compare) (const void *, const void *))
 
 void c_avl_destroy (c_avl_tree_t *t)
 {
+       if (t == NULL)
+               return;
        free_node (t->root);
        free (t);
 }
@@ -510,6 +512,7 @@ int c_avl_insert (c_avl_tree_t *t, void *key, void *value)
        {
                new->parent = NULL;
                t->root = new;
+               t->size = 1;
                return (0);
        }
 
index dd5bcb5..fa6e660 100644 (file)
@@ -572,6 +572,7 @@ int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number)
   char **names = NULL;
   cdtime_t *times = NULL;
   size_t number = 0;
+  size_t size_arrays = 0;
 
   int status = 0;
 
@@ -580,42 +581,47 @@ int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number)
 
   pthread_mutex_lock (&cache_lock);
 
+  size_arrays = (size_t) c_avl_size (cache_tree);
+  if (size_arrays < 1)
+  {
+    /* Handle the "no values" case here, to avoid the error message when
+     * calloc() returns NULL. */
+    pthread_mutex_unlock (&cache_lock);
+    return (0);
+  }
+
+  names = calloc (size_arrays, sizeof (*names));
+  times = calloc (size_arrays, sizeof (*times));
+  if ((names == NULL) || (times == NULL))
+  {
+    ERROR ("uc_get_names: calloc failed.");
+    sfree (names);
+    sfree (times);
+    pthread_mutex_unlock (&cache_lock);
+    return (ENOMEM);
+  }
+
   iter = c_avl_get_iterator (cache_tree);
   while (c_avl_iterator_next (iter, (void *) &key, (void *) &value) == 0)
   {
-    char **temp;
-
     /* remove missing values when list values */
     if (value->state == STATE_MISSING)
       continue;
 
-    if (ret_times != NULL)
-    {
-      cdtime_t *tmp_times;
+    /* c_avl_size does not return a number smaller than the number of elements
+     * returned by c_avl_iterator_next. */
+    assert (number < size_arrays);
 
-      tmp_times = (cdtime_t *) realloc (times, sizeof (cdtime_t) * (number + 1));
-      if (tmp_times == NULL)
-      {
-       status = -1;
-       break;
-      }
-      times = tmp_times;
+    if (ret_times != NULL)
       times[number] = value->last_time;
-    }
 
-    temp = (char **) realloc (names, sizeof (char *) * (number + 1));
-    if (temp == NULL)
-    {
-      status = -1;
-      break;
-    }
-    names = temp;
     names[number] = strdup (key);
     if (names[number] == NULL)
     {
       status = -1;
       break;
     }
+
     number++;
   } /* while (c_avl_iterator_next) */
 
index dcac807..aadf9c5 100644 (file)
@@ -184,7 +184,7 @@ static int udb_config_set_uint (unsigned int *ret_value, /* {{{ */
  */
 static int udb_result_submit (udb_result_t *r, /* {{{ */
     udb_result_preparation_area_t *r_area,
-    const udb_query_t const *q, udb_query_preparation_area_t *q_area)
+    udb_query_t const *q, udb_query_preparation_area_t *q_area)
 {
   value_list_t vl = VALUE_LIST_INIT;
   size_t i;
@@ -219,7 +219,7 @@ static int udb_result_submit (udb_result_t *r, /* {{{ */
 
   sstrncpy (vl.host, q_area->host, sizeof (vl.host));
   sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
+  sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.plugin_instance));
   sstrncpy (vl.type, r->type, sizeof (vl.type));
 
   /* Set vl.type_instance {{{ */
@@ -259,7 +259,7 @@ static int udb_result_submit (udb_result_t *r, /* {{{ */
   return (0);
 } /* }}} void udb_result_submit */
 
-static void udb_result_finish_result (const udb_result_t const *r, /* {{{ */
+static void udb_result_finish_result (udb_result_t const *r, /* {{{ */
     udb_result_preparation_area_t *prep_area)
 {
   if ((r == NULL) || (prep_area == NULL))
@@ -275,7 +275,7 @@ static void udb_result_finish_result (const udb_result_t const *r, /* {{{ */
 static int udb_result_handle_result (udb_result_t *r, /* {{{ */
     udb_query_preparation_area_t *q_area,
     udb_result_preparation_area_t *r_area,
-    const udb_query_t const *q, char **column_values)
+    udb_query_t const *q, char **column_values)
 {
   size_t i;
 
@@ -290,7 +290,7 @@ static int udb_result_handle_result (udb_result_t *r, /* {{{ */
   return udb_result_submit (r, r_area, q, q_area);
 } /* }}} int udb_result_handle_result */
 
-static int udb_result_prepare_result (const udb_result_t const *r, /* {{{ */
+static int udb_result_prepare_result (udb_result_t const *r, /* {{{ */
     udb_result_preparation_area_t *prep_area,
     char **column_names, size_t column_num)
 {
@@ -825,7 +825,7 @@ int udb_query_check_version (udb_query_t *q, unsigned int version) /* {{{ */
   return (1);
 } /* }}} int udb_query_check_version */
 
-void udb_query_finish_result (const udb_query_t const *q, /* {{{ */
+void udb_query_finish_result (udb_query_t const *q, /* {{{ */
     udb_query_preparation_area_t *prep_area)
 {
   udb_result_preparation_area_t *r_area;
@@ -851,7 +851,7 @@ void udb_query_finish_result (const udb_query_t const *q, /* {{{ */
   }
 } /* }}} void udb_query_finish_result */
 
-int udb_query_handle_result (const udb_query_t const *q, /* {{{ */
+int udb_query_handle_result (udb_query_t const *q, /* {{{ */
     udb_query_preparation_area_t *prep_area, char **column_values)
 {
   udb_result_preparation_area_t *r_area;
@@ -904,7 +904,7 @@ int udb_query_handle_result (const udb_query_t const *q, /* {{{ */
   return (0);
 } /* }}} int udb_query_handle_result */
 
-int udb_query_prepare_result (const udb_query_t const *q, /* {{{ */
+int udb_query_prepare_result (udb_query_t const *q, /* {{{ */
     udb_query_preparation_area_t *prep_area,
     const char *host, const char *plugin, const char *db_name,
     char **column_names, size_t column_num, cdtime_t interval)
index 727be03..b6f4cea 100644 (file)
@@ -65,13 +65,13 @@ void *udb_query_get_user_data (udb_query_t *q);
  */
 int udb_query_check_version (udb_query_t *q, unsigned int version);
 
-int udb_query_prepare_result (const udb_query_t const *q,
+int udb_query_prepare_result (udb_query_t const *q,
     udb_query_preparation_area_t *prep_area,
     const char *host, const char *plugin, const char *db_name,
     char **column_names, size_t column_num, cdtime_t interval);
-int udb_query_handle_result (const udb_query_t const *q,
+int udb_query_handle_result (udb_query_t const *q,
     udb_query_preparation_area_t *prep_area, char **column_values);
-void udb_query_finish_result (const udb_query_t const *q,
+void udb_query_finish_result (udb_query_t const *q,
     udb_query_preparation_area_t *prep_area);
 
 udb_query_preparation_area_t *
index cfa4a5c..80a2ee5 100644 (file)
 #if HAVE_NETINET_IN_H
 # include <netinet/in.h>
 #endif
+#if HAVE_NETINET_IP6_H
+# include <netinet/ip6.h>
+#endif
+#if HAVE_NETINET_IP_COMPAT_H
+# include <netinet/ip_compat.h>
+#endif
 #if HAVE_ARPA_INET_H
 # include <arpa/inet.h>
 #endif
 
 #if HAVE_STRUCT_UDPHDR_UH_DPORT && HAVE_STRUCT_UDPHDR_UH_SPORT
 # define UDP_DEST uh_dport
-# define UDP_SRC  uh_dport
+# define UDP_SRC  uh_sport
 #elif HAVE_STRUCT_UDPHDR_DEST && HAVE_STRUCT_UDPHDR_SOURCE
 # define UDP_DEST dest
 # define UDP_SRC  source
@@ -449,7 +455,7 @@ handle_ipv6 (struct ip6_hdr *ipv6, int len)
     unsigned int offset;
     int nexthdr;
 
-    struct in6_addr s_addr;
+    struct in6_addr c_src_addr;
     uint16_t payload_len;
 
     if (0 > len)
@@ -457,10 +463,10 @@ handle_ipv6 (struct ip6_hdr *ipv6, int len)
 
     offset = sizeof (struct ip6_hdr);
     nexthdr = ipv6->ip6_nxt;
-    s_addr = ipv6->ip6_src;
+    c_src_addr = ipv6->ip6_src;
     payload_len = ntohs (ipv6->ip6_plen);
 
-    if (ignore_list_match (&s_addr))
+    if (ignore_list_match (&c_src_addr))
            return (0);
 
     /* Parse extension headers. This only handles the standard headers, as
@@ -469,7 +475,6 @@ handle_ipv6 (struct ip6_hdr *ipv6, int len)
            || (IPPROTO_HOPOPTS == nexthdr) /* Hop-by-Hop options. */
            || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */
            || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */
-           || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */
            || (IPPROTO_AH == nexthdr) /* destination options. */
            || (IPPROTO_ESP == nexthdr)) /* encapsulating security payload. */
     {
@@ -527,15 +532,15 @@ handle_ip(const struct ip *ip, int len)
 {
     char buf[PCAP_SNAPLEN];
     int offset = ip->ip_hl << 2;
-    struct in6_addr s_addr;
-    struct in6_addr d_addr;
+    struct in6_addr c_src_addr;
+    struct in6_addr c_dst_addr;
 
     if (ip->ip_v == 6)
        return (handle_ipv6 ((void *) ip, len));
 
-    in6_addr_from_buffer (&s_addr, &ip->ip_src.s_addr, sizeof (ip->ip_src.s_addr), AF_INET);
-    in6_addr_from_buffer (&d_addr, &ip->ip_dst.s_addr, sizeof (ip->ip_dst.s_addr), AF_INET);
-    if (ignore_list_match (&s_addr))
+    in6_addr_from_buffer (&c_src_addr, &ip->ip_src.s_addr, sizeof (ip->ip_src.s_addr), AF_INET);
+    in6_addr_from_buffer (&c_dst_addr, &ip->ip_dst.s_addr, sizeof (ip->ip_dst.s_addr), AF_INET);
+    if (ignore_list_match (&c_src_addr))
            return (0);
     if (IPPROTO_UDP != ip->ip_p)
        return 0;
index d20b7e3..97f21a1 100644 (file)
@@ -234,6 +234,7 @@ void fbh_destroy (fbhash_t *h) /* {{{ */
   if (h == NULL)
     return;
 
+  pthread_mutex_destroy (&h->lock);
   free (h->filename);
   fbh_free_tree (h->tree);
 } /* }}} void fbh_destroy */
diff --git a/src/utils_format_graphite.c b/src/utils_format_graphite.c
new file mode 100644 (file)
index 0000000..b9b906f
--- /dev/null
@@ -0,0 +1,240 @@
+/**
+ * collectd - src/utils_format_graphite.c
+ * Copyright (C) 2012  Thomas Meson
+ * Copyright (C) 2012  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Thomas Meson <zllak at hycik.org>
+ *   Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+
+#include "utils_cache.h"
+#include "utils_format_json.h"
+#include "utils_parse_option.h"
+
+/* Utils functions to format data sets in graphite format.
+ * Largely taken from write_graphite.c as it remains the same formatting */
+
+static int gr_format_values (char *ret, size_t ret_len,
+        int ds_num, const data_set_t *ds, const value_list_t *vl,
+        gauge_t const *rates)
+{
+    size_t offset = 0;
+    int status;
+
+    assert (0 == strcmp (ds->type, vl->type));
+
+    memset (ret, 0, ret_len);
+
+#define BUFFER_ADD(...) do { \
+    status = ssnprintf (ret + offset, ret_len - offset, \
+            __VA_ARGS__); \
+    if (status < 1) \
+    { \
+        return (-1); \
+    } \
+    else if (((size_t) status) >= (ret_len - offset)) \
+    { \
+        return (-1); \
+    } \
+    else \
+    offset += ((size_t) status); \
+} while (0)
+
+    if (ds->ds[ds_num].type == DS_TYPE_GAUGE)
+        BUFFER_ADD ("%f", vl->values[ds_num].gauge);
+    else if (rates != NULL)
+        BUFFER_ADD ("%f", rates[ds_num]);
+    else if (ds->ds[ds_num].type == DS_TYPE_COUNTER)
+        BUFFER_ADD ("%llu", vl->values[ds_num].counter);
+    else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
+        BUFFER_ADD ("%"PRIi64, vl->values[ds_num].derive);
+    else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
+        BUFFER_ADD ("%"PRIu64, vl->values[ds_num].absolute);
+    else
+    {
+        ERROR ("gr_format_values plugin: Unknown data source type: %i",
+                ds->ds[ds_num].type);
+        return (-1);
+    }
+
+#undef BUFFER_ADD
+
+    return (0);
+}
+
+static void gr_copy_escape_part (char *dst, const char *src, size_t dst_len,
+    char escape_char)
+{
+    size_t i;
+
+    memset (dst, 0, dst_len);
+
+    if (src == NULL)
+        return;
+
+    for (i = 0; i < dst_len; i++)
+    {
+        if (src[i] == 0)
+        {
+            dst[i] = 0;
+            break;
+        }
+
+        if ((src[i] == '.')
+                || isspace ((int) src[i])
+                || iscntrl ((int) src[i]))
+            dst[i] = escape_char;
+        else
+            dst[i] = src[i];
+    }
+}
+
+static int gr_format_name (char *ret, int ret_len,
+        const value_list_t *vl,
+        const char *ds_name,
+        char *prefix,
+        char *postfix,
+        char escape_char)
+{
+    char n_host[DATA_MAX_NAME_LEN];
+    char n_plugin[DATA_MAX_NAME_LEN];
+    char n_plugin_instance[DATA_MAX_NAME_LEN];
+    char n_type[DATA_MAX_NAME_LEN];
+    char n_type_instance[DATA_MAX_NAME_LEN];
+
+    char tmp_plugin[2 * DATA_MAX_NAME_LEN + 1];
+    char tmp_type[2 * DATA_MAX_NAME_LEN + 1];
+
+    if (prefix == NULL)
+        prefix = "";
+
+    if (postfix == NULL)
+        postfix = "";
+
+    gr_copy_escape_part (n_host, vl->host,
+            sizeof (n_host), escape_char);
+    gr_copy_escape_part (n_plugin, vl->plugin,
+            sizeof (n_plugin), escape_char);
+    gr_copy_escape_part (n_plugin_instance, vl->plugin_instance,
+            sizeof (n_plugin_instance), escape_char);
+    gr_copy_escape_part (n_type, vl->type,
+            sizeof (n_type), escape_char);
+    gr_copy_escape_part (n_type_instance, vl->type_instance,
+            sizeof (n_type_instance), escape_char);
+
+    if (n_plugin_instance[0] != '\0')
+        ssnprintf (tmp_plugin, sizeof (tmp_plugin), "%s%c%s",
+            n_plugin,
+            '-',
+            n_plugin_instance);
+    else
+        sstrncpy (tmp_plugin, n_plugin, sizeof (tmp_plugin));
+
+    if (n_type_instance[0] != '\0')
+        ssnprintf (tmp_type, sizeof (tmp_type), "%s%c%s",
+            n_type,
+            '-',
+            n_type_instance);
+    else
+        sstrncpy (tmp_type, n_type, sizeof (tmp_type));
+
+    if (ds_name != NULL)
+        ssnprintf (ret, ret_len, "%s%s%s.%s.%s.%s",
+            prefix, n_host, postfix, tmp_plugin, tmp_type, ds_name);
+    else
+        ssnprintf (ret, ret_len, "%s%s%s.%s.%s",
+            prefix, n_host, postfix, tmp_plugin, tmp_type);
+
+    return (0);
+}
+
+int format_graphite (char *buffer, size_t buffer_size,
+    const data_set_t *ds, const value_list_t *vl, char *prefix,
+    char *postfix, char escape_char,
+    _Bool store_rates)
+{
+    int status = 0;
+    int i;
+    int buffer_pos = 0;
+
+    gauge_t *rates = NULL;
+    if (store_rates)
+      rates = uc_get_rate (ds, vl);
+
+    for (i = 0; i < ds->ds_num; i++)
+    {
+        const char *ds_name = NULL;
+        char        key[10*DATA_MAX_NAME_LEN];
+        char        values[512];
+        size_t      message_len;
+        char        message[1024];
+
+        ds_name = ds->ds[i].name;
+
+        /* Copy the identifier to `key' and escape it. */
+        status = gr_format_name (key, sizeof (key), vl, ds_name,
+                    prefix, postfix, escape_char);
+        if (status != 0)
+        {
+            ERROR ("format_graphite: error with gr_format_name");
+            sfree (rates);
+            return (status);
+        }
+
+        escape_string (key, sizeof (key));
+        /* Convert the values to an ASCII representation and put that into
+         * `values'. */
+        status = gr_format_values (values, sizeof (values), i, ds, vl, rates);
+        if (status != 0)
+        {
+            ERROR ("format_graphite: error with gr_format_values");
+            sfree (rates);
+            return (status);
+        }
+
+        /* Compute the graphite command */
+        message_len = (size_t) ssnprintf (message, sizeof (message),
+                "%s %s %u\r\n",
+                key,
+                values,
+                (unsigned int) CDTIME_T_TO_TIME_T (vl->time));
+        if (message_len >= sizeof (message)) {
+            ERROR ("format_graphite: message buffer too small: "
+                    "Need %zu bytes.", message_len + 1);
+            sfree (rates);
+            return (-ENOMEM);
+        }
+
+        /* Append it in case we got multiple data set */
+        if ((buffer_pos + message_len) >= buffer_size)
+        {
+            ERROR ("format_graphite: target buffer too small");
+            sfree (rates);
+            return (-ENOMEM);
+        }
+        memcpy((void *) (buffer + buffer_pos), message, message_len);
+        buffer_pos += message_len;
+    }
+    sfree (rates);
+    return (status);
+} /* int format_graphite */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/utils_format_graphite.h b/src/utils_format_graphite.h
new file mode 100644 (file)
index 0000000..a7a05bb
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * collectd - src/utils_format_graphite.h
+ * Copyright (C) 2012  Thomas Meson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Thomas Meson <zllak at hycik.org>
+ **/
+
+#ifndef UTILS_FORMAT_GRAPHITE_H
+#define UTILS_FORMAT_GRAPHITE_H 1
+
+#include "collectd.h"
+#include "plugin.h"
+
+int format_graphite (char *buffer,
+    size_t buffer_size, const data_set_t *ds,
+    const value_list_t *vl, const char *prefix,
+    const char *postfix, const char escape_char,
+    _Bool store_rates);
+
+#endif /* UTILS_FORMAT_GRAPHITE_H */
index 2a5526b..bbc3dfd 100644 (file)
@@ -70,7 +70,7 @@ static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
 #undef BUFFER_ADD
 
   return (0);
-} /* }}} int buffer_add_string */
+} /* }}} int escape_string */
 
 static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
                 const data_set_t *ds, const value_list_t *vl, int store_rates)
@@ -152,7 +152,7 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
 } /* }}} int values_to_json */
 
 static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */
-                const data_set_t *ds, const value_list_t *vl)
+                const data_set_t *ds)
 {
   size_t offset = 0;
   int i;
@@ -189,7 +189,7 @@ static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */
 } /* }}} int dstypes_to_json */
 
 static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */
-                const data_set_t *ds, const value_list_t *vl)
+                const data_set_t *ds)
 {
   size_t offset = 0;
   int i;
@@ -225,6 +225,86 @@ static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */
   return (0);
 } /* }}} int dsnames_to_json */
 
+static int meta_data_to_json (char *buffer, size_t buffer_size, /* {{{ */
+    meta_data_t *meta)
+{
+  size_t offset = 0;
+  char **keys = NULL;
+  int keys_num;
+  int status;
+  int i;
+
+  memset (buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) do { \
+  status = ssnprintf (buffer + offset, buffer_size - offset, \
+      __VA_ARGS__); \
+  if (status < 1) \
+    return (-1); \
+  else if (((size_t) status) >= (buffer_size - offset)) \
+    return (-ENOMEM); \
+  else \
+    offset += ((size_t) status); \
+} while (0)
+
+  keys_num = meta_data_toc (meta, &keys);
+  for (i = 0; i < keys_num; ++i)
+  {
+    int type;
+    char *key = keys[i];
+
+    type = meta_data_type (meta, key);
+    if (type == MD_TYPE_STRING)
+    {
+      char *value = NULL;
+      if (meta_data_get_string (meta, key, &value) == 0)
+      {
+        char temp[512] = "";
+        escape_string (temp, sizeof (temp), value);
+        sfree (value);
+        BUFFER_ADD (",\"%s\":%s", key, temp);
+      }
+    }
+    else if (type == MD_TYPE_SIGNED_INT)
+    {
+      int64_t value = 0;
+      if (meta_data_get_signed_int (meta, key, &value) == 0)
+        BUFFER_ADD (",\"%s\":%"PRIi64, key, value);
+    }
+    else if (type == MD_TYPE_UNSIGNED_INT)
+    {
+      uint64_t value = 0;
+      if (meta_data_get_unsigned_int (meta, key, &value) == 0)
+        BUFFER_ADD (",\"%s\":%"PRIu64, key, value);
+    }
+    else if (type == MD_TYPE_DOUBLE)
+    {
+      double value = 0.0;
+      if (meta_data_get_double (meta, key, &value) == 0)
+        BUFFER_ADD (",\"%s\":%f", key, value);
+    }
+    else if (type == MD_TYPE_BOOLEAN)
+    {
+      _Bool value = 0;
+      if (meta_data_get_boolean (meta, key, &value) == 0)
+        BUFFER_ADD (",\"%s\":%s", key, value ? "true" : "false");
+    }
+
+    free (key);
+  } /* for (keys) */
+  free (keys);
+
+  if (offset <= 0)
+    return (ENOENT);
+
+  buffer[0] = '{'; /* replace leading ',' */
+  BUFFER_ADD ("}");
+
+#undef BUFFER_ADD
+
+  return (0);
+} /* int meta_data_to_json */
+
 static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
                 const data_set_t *ds, const value_list_t *vl, int store_rates)
 {
@@ -254,12 +334,12 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
     return (status);
   BUFFER_ADD ("\"values\":%s", temp);
 
-  status = dstypes_to_json (temp, sizeof (temp), ds, vl);
+  status = dstypes_to_json (temp, sizeof (temp), ds);
   if (status != 0)
     return (status);
   BUFFER_ADD (",\"dstypes\":%s", temp);
 
-  status = dsnames_to_json (temp, sizeof (temp), ds, vl);
+  status = dsnames_to_json (temp, sizeof (temp), ds);
   if (status != 0)
     return (status);
   BUFFER_ADD (",\"dsnames\":%s", temp);
@@ -280,6 +360,17 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
   BUFFER_ADD_KEYVAL ("type", vl->type);
   BUFFER_ADD_KEYVAL ("type_instance", vl->type_instance);
 
+  if (vl->meta != NULL)
+  {
+    char meta_buffer[buffer_size];
+    memset (meta_buffer, 0, sizeof (meta_buffer));
+    status = meta_data_to_json (meta_buffer, sizeof (meta_buffer), vl->meta);
+    if (status != 0)
+      return (status);
+
+    BUFFER_ADD (",\"meta\":%s", meta_buffer);
+  } /* if (vl->meta != NULL) */
+
   BUFFER_ADD ("}");
 
 #undef BUFFER_ADD_KEYVAL
index 91ac6ce..091b5fa 100644 (file)
@@ -406,9 +406,9 @@ int cu_rrd_create_file (const char *filename, /* {{{ */
   argv[ds_num + rra_num] = NULL;
 
   last_up = CDTIME_T_TO_TIME_T (vl->time);
-  if (last_up <= 10)
+  if (last_up <= 0)
     last_up = time (NULL);
-  last_up -= 10;
+  last_up -= 1;
 
   if (cfg->stepsize > 0)
     stepsize = cfg->stepsize;
diff --git a/src/utils_vl_lookup.c b/src/utils_vl_lookup.c
new file mode 100644 (file)
index 0000000..2dada24
--- /dev/null
@@ -0,0 +1,541 @@
+/**
+ * collectd - src/utils_vl_lookup.c
+ * Copyright (C) 2012  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 <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_vl_lookup.h"
+#include "utils_avltree.h"
+
+#if BUILD_TEST
+# define sstrncpy strncpy
+# define plugin_log(s, ...) do { \
+  printf ("[severity %i] ", s); \
+  printf (__VA_ARGS__); \
+  printf ("\n"); \
+} while (0)
+#endif
+
+/*
+ * Types
+ */
+struct lookup_s
+{
+  c_avl_tree_t *by_type_tree;
+
+  lookup_class_callback_t cb_user_class;
+  lookup_obj_callback_t cb_user_obj;
+  lookup_free_class_callback_t cb_free_class;
+  lookup_free_obj_callback_t cb_free_obj;
+};
+
+struct user_obj_s;
+typedef struct user_obj_s user_obj_t;
+struct user_obj_s
+{
+  void *user_obj;
+  identifier_t ident;
+
+  user_obj_t *next;
+};
+
+struct user_class_s
+{
+  void *user_class;
+  identifier_t ident;
+  user_obj_t *user_obj_list; /* list of user_obj */
+};
+typedef struct user_class_s user_class_t;
+
+struct user_class_list_s;
+typedef struct user_class_list_s user_class_list_t;
+struct user_class_list_s
+{
+  user_class_t entry;
+  user_class_list_t *next;
+};
+
+struct by_type_entry_s
+{
+  c_avl_tree_t *by_plugin_tree; /* plugin -> user_class_list_t */
+  user_class_list_t *wildcard_plugin_list;
+};
+typedef struct by_type_entry_s by_type_entry_t;
+
+/*
+ * Private functions
+ */
+static void *lu_create_user_obj (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl,
+    user_class_t *user_class)
+{
+  user_obj_t *user_obj;
+
+  user_obj = malloc (sizeof (*user_obj));
+  if (user_obj == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    return (NULL);
+  }
+  memset (user_obj, 0, sizeof (*user_obj));
+  user_obj->next = NULL;
+
+  user_obj->user_obj = obj->cb_user_class (ds, vl, user_class->user_class);
+  if (user_obj->user_obj == NULL)
+  {
+    sfree (user_obj);
+    WARNING("utils_vl_lookup: User-provided constructor failed.");
+    return (NULL);
+  }
+
+  sstrncpy (user_obj->ident.host,
+    LU_IS_ALL (user_class->ident.host) ?  "/all/" : vl->host,
+    sizeof (user_obj->ident.host));
+  sstrncpy (user_obj->ident.plugin,
+    LU_IS_ALL (user_class->ident.plugin) ?  "/all/" : vl->plugin,
+    sizeof (user_obj->ident.plugin));
+  sstrncpy (user_obj->ident.plugin_instance,
+    LU_IS_ALL (user_class->ident.plugin_instance) ?  "/all/" : vl->plugin_instance,
+    sizeof (user_obj->ident.plugin_instance));
+  sstrncpy (user_obj->ident.type,
+    LU_IS_ALL (user_class->ident.type) ?  "/all/" : vl->type,
+    sizeof (user_obj->ident.type));
+  sstrncpy (user_obj->ident.type_instance,
+    LU_IS_ALL (user_class->ident.type_instance) ?  "/all/" : vl->type_instance,
+    sizeof (user_obj->ident.type_instance));
+
+  if (user_class->user_obj_list == NULL)
+  {
+    user_class->user_obj_list = user_obj;
+  }
+  else
+  {
+    user_obj_t *last = user_class->user_obj_list;
+    while (last->next != NULL)
+      last = last->next;
+    last->next = user_obj;
+  }
+
+  return (user_obj);
+} /* }}} void *lu_create_user_obj */
+
+static user_obj_t *lu_find_user_obj (user_class_t *user_class, /* {{{ */
+    value_list_t const *vl)
+{
+  user_obj_t *ptr;
+
+  for (ptr = user_class->user_obj_list;
+      ptr != NULL;
+      ptr = ptr->next)
+  {
+    if (!LU_IS_ALL (ptr->ident.host)
+        && (strcmp (ptr->ident.host, vl->host) != 0))
+      continue;
+    if (!LU_IS_ALL (ptr->ident.plugin_instance)
+        && (strcmp (ptr->ident.plugin_instance, vl->plugin_instance) != 0))
+      continue;
+    if (!LU_IS_ALL (ptr->ident.type_instance)
+        && (strcmp (ptr->ident.type_instance, vl->type_instance) != 0))
+      continue;
+
+    return (ptr);
+  }
+
+  return (NULL);
+} /* }}} user_obj_t *lu_find_user_obj */
+
+static int lu_handle_user_class (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl,
+    user_class_t *user_class)
+{
+  user_obj_t *user_obj;
+  int status;
+
+  assert (strcmp (vl->type, user_class->ident.type) == 0);
+  assert (LU_IS_WILDCARD (user_class->ident.plugin)
+      || (strcmp (vl->plugin, user_class->ident.plugin) == 0));
+
+  /* When we get here, type and plugin already match the user class. Now check
+   * the rest of the fields. */
+  if (!LU_IS_WILDCARD (user_class->ident.type_instance)
+      && (strcmp (vl->type_instance, user_class->ident.type_instance) != 0))
+    return (1);
+  if (!LU_IS_WILDCARD (user_class->ident.plugin_instance)
+      && (strcmp (vl->plugin_instance,
+          user_class->ident.plugin_instance) != 0))
+    return (1);
+  if (!LU_IS_WILDCARD (user_class->ident.host)
+      && (strcmp (vl->host, user_class->ident.host) != 0))
+    return (1);
+
+  user_obj = lu_find_user_obj (user_class, vl);
+  if (user_obj == NULL)
+  {
+    /* call lookup_class_callback_t() and insert into the list of user objects. */
+    user_obj = lu_create_user_obj (obj, ds, vl, user_class);
+    if (user_obj == NULL)
+      return (-1);
+  }
+
+  status = obj->cb_user_obj (ds, vl,
+      user_class->user_class, user_obj->user_obj);
+  if (status != 0)
+  {
+    ERROR ("utils_vl_lookup: The user object callback failed with status %i.",
+        status);
+    /* Returning a negative value means: abort! */
+    if (status < 0)
+      return (status);
+    else
+      return (1);
+  }
+
+  return (0);
+} /* }}} int lu_handle_user_class */
+
+static int lu_handle_user_class_list (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl,
+    user_class_list_t *user_class_list)
+{
+  user_class_list_t *ptr;
+  int retval = 0;
+  
+  for (ptr = user_class_list; ptr != NULL; ptr = ptr->next)
+  {
+    int status;
+
+    status = lu_handle_user_class (obj, ds, vl, &ptr->entry);
+    if (status < 0)
+      return (status);
+    else if (status == 0)
+      retval++;
+  }
+
+  return (retval);
+} /* }}} int lu_handle_user_class_list */
+
+static by_type_entry_t *lu_search_by_type (lookup_t *obj, /* {{{ */
+    char const *type, _Bool allocate_if_missing)
+{
+  by_type_entry_t *by_type;
+  char *type_copy;
+  int status;
+
+  status = c_avl_get (obj->by_type_tree, type, (void *) &by_type);
+  if (status == 0)
+    return (by_type);
+
+  if (!allocate_if_missing)
+    return (NULL);
+
+  type_copy = strdup (type);
+  if (type_copy == NULL)
+  {
+    ERROR ("utils_vl_lookup: strdup failed.");
+    return (NULL);
+  }
+
+  by_type = malloc (sizeof (*by_type));
+  if (by_type == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    sfree (type_copy);
+    return (NULL);
+  }
+  memset (by_type, 0, sizeof (*by_type));
+  by_type->wildcard_plugin_list = NULL;
+  
+  by_type->by_plugin_tree = c_avl_create ((void *) strcmp);
+  if (by_type->by_plugin_tree == NULL)
+  {
+    ERROR ("utils_vl_lookup: c_avl_create failed.");
+    sfree (by_type);
+    sfree (type_copy);
+    return (NULL);
+  }
+
+  status = c_avl_insert (obj->by_type_tree,
+      /* key = */ type_copy, /* value = */ by_type);
+  assert (status <= 0); /* >0 => entry exists => race condition. */
+  if (status != 0)
+  {
+    ERROR ("utils_vl_lookup: c_avl_insert failed.");
+    c_avl_destroy (by_type->by_plugin_tree);
+    sfree (by_type);
+    sfree (type_copy);
+    return (NULL);
+  }
+  
+  return (by_type);
+} /* }}} by_type_entry_t *lu_search_by_type */
+
+static int lu_add_by_plugin (by_type_entry_t *by_type, /* {{{ */
+    identifier_t const *ident, user_class_list_t *user_class_list)
+{
+  user_class_list_t *ptr = NULL;
+
+  /* Lookup user_class_list from the per-plugin structure. If this is the first
+   * user_class to be added, the blocks return immediately. Otherwise they will
+   * set "ptr" to non-NULL. */
+  if (LU_IS_WILDCARD (ident->plugin))
+  {
+    if (by_type->wildcard_plugin_list == NULL)
+    {
+      by_type->wildcard_plugin_list = user_class_list;
+      return (0);
+    }
+
+    ptr = by_type->wildcard_plugin_list;
+  } /* if (plugin is wildcard) */
+  else /* (plugin is not wildcard) */
+  {
+    int status;
+
+    status = c_avl_get (by_type->by_plugin_tree,
+        ident->plugin, (void *) &ptr);
+
+    if (status != 0) /* plugin not yet in tree */
+    {
+      char *plugin_copy = strdup (ident->plugin);
+
+      if (plugin_copy == NULL)
+      {
+        ERROR ("utils_vl_lookup: strdup failed.");
+        sfree (user_class_list);
+        return (ENOMEM);
+      }
+
+      status = c_avl_insert (by_type->by_plugin_tree,
+          plugin_copy, user_class_list);
+      if (status != 0)
+      {
+        ERROR ("utils_vl_lookup: c_avl_insert(\"%s\") failed with status %i.",
+            plugin_copy, status);
+        sfree (plugin_copy);
+        sfree (user_class_list);
+        return (status);
+      }
+      else
+      {
+        return (0);
+      }
+    } /* if (plugin not yet in tree) */
+  } /* if (plugin is not wildcard) */
+
+  assert (ptr != NULL);
+
+  while (ptr->next != NULL)
+    ptr = ptr->next;
+  ptr->next = user_class_list;
+
+  return (0);
+} /* }}} int lu_add_by_plugin */
+
+static void lu_destroy_user_obj (lookup_t *obj, /* {{{ */
+    user_obj_t *user_obj)
+{
+  while (user_obj != NULL)
+  {
+    user_obj_t *next = user_obj->next;
+
+    if (obj->cb_free_obj != NULL)
+      obj->cb_free_obj (user_obj->user_obj);
+    user_obj->user_obj = NULL;
+
+    sfree (user_obj);
+    user_obj = next;
+  }
+} /* }}} void lu_destroy_user_obj */
+
+static void lu_destroy_user_class_list (lookup_t *obj, /* {{{ */
+    user_class_list_t *user_class_list)
+{
+  while (user_class_list != NULL)
+  {
+    user_class_list_t *next = user_class_list->next;
+
+    if (obj->cb_free_class != NULL)
+      obj->cb_free_class (user_class_list->entry.user_class);
+    user_class_list->entry.user_class = NULL;
+
+    lu_destroy_user_obj (obj, user_class_list->entry.user_obj_list);
+    user_class_list->entry.user_obj_list = NULL;
+
+    sfree (user_class_list);
+    user_class_list = next;
+  }
+} /* }}} void lu_destroy_user_class_list */
+
+static void lu_destroy_by_type (lookup_t *obj, /* {{{ */
+    by_type_entry_t *by_type)
+{
+  
+  while (42)
+  {
+    char *plugin = NULL;
+    user_class_list_t *user_class_list = NULL;
+    int status;
+
+    status = c_avl_pick (by_type->by_plugin_tree,
+        (void *) &plugin, (void *) &user_class_list);
+    if (status != 0)
+      break;
+
+    DEBUG ("utils_vl_lookup: lu_destroy_by_type: Destroying plugin \"%s\".",
+        plugin);
+    sfree (plugin);
+    lu_destroy_user_class_list (obj, user_class_list);
+  }
+
+  c_avl_destroy (by_type->by_plugin_tree);
+  by_type->by_plugin_tree = NULL;
+
+  lu_destroy_user_class_list (obj, by_type->wildcard_plugin_list);
+  by_type->wildcard_plugin_list = NULL;
+
+  sfree (by_type);
+} /* }}} int lu_destroy_by_type */
+
+/*
+ * Public functions
+ */
+lookup_t *lookup_create (lookup_class_callback_t cb_user_class, /* {{{ */
+    lookup_obj_callback_t cb_user_obj,
+    lookup_free_class_callback_t cb_free_class,
+    lookup_free_obj_callback_t cb_free_obj)
+{
+  lookup_t *obj = malloc (sizeof (*obj));
+  if (obj == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    return (NULL);
+  }
+  memset (obj, 0, sizeof (*obj));
+
+  obj->by_type_tree = c_avl_create ((void *) strcmp);
+  if (obj->by_type_tree == NULL)
+  {
+    ERROR ("utils_vl_lookup: c_avl_create failed.");
+    sfree (obj);
+    return (NULL);
+  }
+
+  obj->cb_user_class = cb_user_class;
+  obj->cb_user_obj = cb_user_obj;
+  obj->cb_free_class = cb_free_class;
+  obj->cb_free_obj = cb_free_obj;
+
+  return (obj);
+} /* }}} lookup_t *lookup_create */
+
+void lookup_destroy (lookup_t *obj) /* {{{ */
+{
+  int status;
+
+  if (obj == NULL)
+    return;
+
+  while (42)
+  {
+    char *type = NULL;
+    by_type_entry_t *by_type = NULL;
+
+    status = c_avl_pick (obj->by_type_tree, (void *) &type, (void *) &by_type);
+    if (status != 0)
+      break;
+
+    DEBUG ("utils_vl_lookup: lookup_destroy: Destroying type \"%s\".", type);
+    sfree (type);
+    lu_destroy_by_type (obj, by_type);
+  }
+
+  c_avl_destroy (obj->by_type_tree);
+  obj->by_type_tree = NULL;
+
+  sfree (obj);
+} /* }}} void lookup_destroy */
+
+int lookup_add (lookup_t *obj, /* {{{ */
+    identifier_t const *ident, void *user_class)
+{
+  by_type_entry_t *by_type = NULL;
+  user_class_list_t *user_class_obj;
+
+  by_type = lu_search_by_type (obj, ident->type, /* allocate = */ 1);
+  if (by_type == NULL)
+    return (-1);
+
+  user_class_obj = malloc (sizeof (*user_class_obj));
+  if (user_class_obj == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    return (ENOMEM);
+  }
+  memset (user_class_obj, 0, sizeof (*user_class_obj));
+  user_class_obj->entry.user_class = user_class;
+  memmove (&user_class_obj->entry.ident, ident, sizeof (*ident));
+  user_class_obj->entry.user_obj_list = NULL;
+  user_class_obj->next = NULL;
+
+  return (lu_add_by_plugin (by_type, ident, user_class_obj));
+} /* }}} int lookup_add */
+
+/* returns the number of successful calls to the callback function */
+int lookup_search (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl)
+{
+  by_type_entry_t *by_type = NULL;
+  user_class_list_t *user_class_list = NULL;
+  int retval = 0;
+  int status;
+
+  if ((obj == NULL) || (ds == NULL) || (vl == NULL))
+    return (-EINVAL);
+
+  by_type = lu_search_by_type (obj, vl->type, /* allocate = */ 0);
+  if (by_type == NULL)
+    return (0);
+
+  status = c_avl_get (by_type->by_plugin_tree,
+      vl->plugin, (void *) &user_class_list);
+  if (status == 0)
+  {
+    status = lu_handle_user_class_list (obj, ds, vl, user_class_list);
+    if (status < 0)
+      return (status);
+    retval += status;
+  }
+
+  if (by_type->wildcard_plugin_list != NULL)
+  {
+    status = lu_handle_user_class_list (obj, ds, vl,
+        by_type->wildcard_plugin_list);
+    if (status < 0)
+      return (status);
+    retval += status;
+  }
+    
+  return (retval);
+} /* }}} lookup_search */
diff --git a/src/utils_vl_lookup.h b/src/utils_vl_lookup.h
new file mode 100644 (file)
index 0000000..c006fc7
--- /dev/null
@@ -0,0 +1,90 @@
+/**
+ * collectd - src/utils_vl_lookup.h
+ * Copyright (C) 2012  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 <octo at collectd.org>
+ **/
+
+#ifndef UTILS_VL_LOOKUP_H
+#define UTILS_VL_LOOKUP_H 1
+
+#include "plugin.h"
+
+/*
+ * Types
+ */
+struct lookup_s;
+typedef struct lookup_s lookup_t;
+
+/* Given a user_class, constructs a new user_obj. */
+typedef void *(*lookup_class_callback_t) (data_set_t const *ds,
+    value_list_t const *vl, void *user_class);
+
+/* Given a user_class and a ds/vl combination, does stuff with the data.
+ * This is the main working horse of the module. */
+typedef int (*lookup_obj_callback_t) (data_set_t const *ds,
+    value_list_t const *vl,
+    void *user_class, void *user_obj);
+
+/* Used to free user_class pointers. May be NULL in which case nothing is
+ * freed. */
+typedef void (*lookup_free_class_callback_t) (void *user_class);
+
+/* Used to free user_obj pointers. May be NULL in which case nothing is
+ * freed. */
+typedef void (*lookup_free_obj_callback_t) (void *user_obj);
+
+struct identifier_s
+{
+  char host[DATA_MAX_NAME_LEN];
+  char plugin[DATA_MAX_NAME_LEN];
+  char plugin_instance[DATA_MAX_NAME_LEN];
+  char type[DATA_MAX_NAME_LEN];
+  char type_instance[DATA_MAX_NAME_LEN];
+};
+typedef struct identifier_s identifier_t;
+
+#define LU_ANY "/any/"
+#define LU_ALL "/all/"
+
+#define LU_IS_ANY(str) (strcmp (str, LU_ANY) == 0)
+#define LU_IS_ALL(str) (strcmp (str, LU_ALL) == 0)
+#define LU_IS_WILDCARD(str) (LU_IS_ANY(str) || LU_IS_ALL(str))
+
+/*
+ * Functions
+ */
+__attribute__((nonnull(1,2)))
+lookup_t *lookup_create (lookup_class_callback_t,
+    lookup_obj_callback_t,
+    lookup_free_class_callback_t,
+    lookup_free_obj_callback_t);
+void lookup_destroy (lookup_t *obj);
+
+int lookup_add (lookup_t *obj,
+    identifier_t const *ident, void *user_class);
+
+/* TODO(octo): Pass lookup_obj_callback_t to lookup_search()? */
+int lookup_search (lookup_t *obj,
+    data_set_t const *ds, value_list_t const *vl);
+
+#endif /* UTILS_VL_LOOKUP_H */
diff --git a/src/utils_vl_lookup_test.c b/src/utils_vl_lookup_test.c
new file mode 100644 (file)
index 0000000..6265b32
--- /dev/null
@@ -0,0 +1,214 @@
+/**
+ * collectd - src/utils_vl_lookup_test.c
+ * Copyright (C) 2012  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 <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "utils_vl_lookup.h"
+
+static _Bool expect_new_obj = 0;
+static _Bool have_new_obj = 0;
+
+static identifier_t last_class_ident;
+static identifier_t last_obj_ident;
+
+static data_source_t dsrc_test = { "value", DS_TYPE_DERIVE, 0.0, NAN };
+static data_set_t const ds_test = { "test", 1, &dsrc_test };
+
+static data_source_t dsrc_unknown = { "value", DS_TYPE_DERIVE, 0.0, NAN };
+static data_set_t const ds_unknown = { "unknown", 1, &dsrc_unknown };
+
+static int lookup_obj_callback (data_set_t const *ds,
+    value_list_t const *vl,
+    void *user_class, void *user_obj)
+{
+  identifier_t *class = user_class;
+  identifier_t *obj = user_obj;
+
+  assert (expect_new_obj == have_new_obj);
+
+  memcpy (&last_class_ident, class, sizeof (last_class_ident));
+  memcpy (&last_obj_ident, obj, sizeof (last_obj_ident));
+
+  if (strcmp (obj->plugin_instance, "failure") == 0)
+    return (-1);
+
+  return (0);
+}
+
+static void *lookup_class_callback (data_set_t const *ds,
+    value_list_t const *vl, void *user_class)
+{
+  identifier_t *class = user_class;
+  identifier_t *obj;
+
+  assert (expect_new_obj);
+
+  memcpy (&last_class_ident, class, sizeof (last_class_ident));
+  
+  obj = malloc (sizeof (*obj));
+  strncpy (obj->host, vl->host, sizeof (obj->host));
+  strncpy (obj->plugin, vl->plugin, sizeof (obj->plugin));
+  strncpy (obj->plugin_instance, vl->plugin_instance, sizeof (obj->plugin_instance));
+  strncpy (obj->type, vl->type, sizeof (obj->type));
+  strncpy (obj->type_instance, vl->type_instance, sizeof (obj->type_instance));
+
+  have_new_obj = 1;
+
+  return ((void *) obj);
+}
+
+static void checked_lookup_add (lookup_t *obj, /* {{{ */
+    char const *host,
+    char const *plugin, char const *plugin_instance,
+    char const *type, char const *type_instance)
+{
+  identifier_t ident;
+  void *user_class;
+  int status;
+
+  memset (&ident, 0, sizeof (ident));
+  strncpy (ident.host, host, sizeof (ident.host));
+  strncpy (ident.plugin, plugin, sizeof (ident.plugin));
+  strncpy (ident.plugin_instance, plugin_instance, sizeof (ident.plugin_instance));
+  strncpy (ident.type, type, sizeof (ident.type));
+  strncpy (ident.type_instance, type_instance, sizeof (ident.type_instance));
+
+  user_class = malloc (sizeof (ident));
+  memmove (user_class, &ident, sizeof (ident));
+
+  status = lookup_add (obj, &ident, user_class);
+  assert (status == 0);
+} /* }}} void test_add */
+
+static int checked_lookup_search (lookup_t *obj,
+    char const *host,
+    char const *plugin, char const *plugin_instance,
+    char const *type, char const *type_instance,
+    _Bool expect_new)
+{
+  int status;
+  value_list_t vl = VALUE_LIST_STATIC;
+  data_set_t const *ds = &ds_unknown;
+
+  strncpy (vl.host, host, sizeof (vl.host));
+  strncpy (vl.plugin, plugin, sizeof (vl.plugin));
+  strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+  strncpy (vl.type, type, sizeof (vl.type));
+  strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+  if (strcmp (vl.type, "test") == 0)
+    ds = &ds_test;
+
+  expect_new_obj = expect_new;
+  have_new_obj = 0;
+
+  status = lookup_search (obj, ds, &vl);
+  return (status);
+}
+
+static lookup_t *checked_lookup_create (void)
+{
+  lookup_t *obj = lookup_create (
+      lookup_class_callback,
+      lookup_obj_callback,
+      (void *) free,
+      (void *) free);
+  assert (obj != NULL);
+  return (obj);
+}
+
+static void testcase0 (void)
+{
+  lookup_t *obj = checked_lookup_create ();
+
+  checked_lookup_add (obj, "/any/", "test", "", "test", "/all/");
+  checked_lookup_search (obj, "host0", "test", "", "test", "0",
+      /* expect new = */ 1);
+  checked_lookup_search (obj, "host0", "test", "", "test", "1",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host1", "test", "", "test", "0",
+      /* expect new = */ 1);
+  checked_lookup_search (obj, "host1", "test", "", "test", "1",
+      /* expect new = */ 0);
+
+  lookup_destroy (obj);
+}
+
+static void testcase1 (void)
+{
+  lookup_t *obj = checked_lookup_create ();
+
+  checked_lookup_add (obj, "/any/", "/all/", "/all/", "test", "/all/");
+  checked_lookup_search (obj, "host0", "plugin0", "", "test", "0",
+      /* expect new = */ 1);
+  checked_lookup_search (obj, "host0", "plugin0", "", "test", "1",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host0", "plugin1", "", "test", "0",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host0", "plugin1", "", "test", "1",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host1", "plugin0", "", "test", "0",
+      /* expect new = */ 1);
+  checked_lookup_search (obj, "host1", "plugin0", "", "test", "1",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host1", "plugin1", "", "test", "0",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host1", "plugin1", "", "test", "1",
+      /* expect new = */ 0);
+
+  lookup_destroy (obj);
+}
+
+static void testcase2 (void)
+{
+  lookup_t *obj = checked_lookup_create ();
+  int status;
+
+  checked_lookup_add (obj, "/any/", "plugin0", "", "test", "/all/");
+  checked_lookup_add (obj, "/any/", "/all/", "", "test", "ti0");
+
+  status = checked_lookup_search (obj, "host0", "plugin1", "", "test", "",
+      /* expect new = */ 0);
+  assert (status == 0);
+  status = checked_lookup_search (obj, "host0", "plugin0", "", "test", "",
+      /* expect new = */ 1);
+  assert (status == 1);
+  status = checked_lookup_search (obj, "host0", "plugin1", "", "test", "ti0",
+      /* expect new = */ 1);
+  assert (status == 1);
+  status = checked_lookup_search (obj, "host0", "plugin0", "", "test", "ti0",
+      /* expect new = */ 0);
+  assert (status == 2);
+
+  lookup_destroy (obj);
+}
+
+int main (int argc, char **argv) /* {{{ */
+{
+  testcase0 ();
+  testcase1 ();
+  testcase2 ();
+  return (EXIT_SUCCESS);
+} /* }}} int main */
index 37fd4bb..de60e39 100644 (file)
 
 #include <varnish/varnishapi.h>
 
+#if HAVE_VARNISH_V3
+# include <varnish/vsc.h>
+typedef struct VSC_C_main c_varnish_stats_t;
+#endif
+
+#if HAVE_VARNISH_V2
+typedef struct varnish_stats c_varnish_stats_t;
+#endif
+
 /* {{{ user_config_s */
 struct user_config_s {
        char *instance;
@@ -145,9 +154,11 @@ struct user_config_s {
        _Bool collect_fetch;
        _Bool collect_hcb;
        _Bool collect_shm;
-       _Bool collect_sma;
        _Bool collect_sms;
+#if HAVE_VARNISH_V2
        _Bool collect_sm;
+       _Bool collect_sma;
+#endif
        _Bool collect_totals;
        _Bool collect_workers;
 };
@@ -204,182 +215,194 @@ static int varnish_submit_derive (const char *plugin_instance, /* {{{ */
        return (varnish_submit (plugin_instance, category, type, type_instance, value));
 } /* }}} int varnish_submit_derive */
 
-static void varnish_monitor (const user_config_t *conf, struct varnish_stats *VSL_stats) /* {{{ */
+static void varnish_monitor (const user_config_t *conf, /* {{{ */
+               const c_varnish_stats_t *stats)
 {
        if (conf->collect_cache)
        {
                /* Cache hits */
-               varnish_submit_derive (conf->instance, "cache", "cache_result", "hit",     VSL_stats->cache_hit);
+               varnish_submit_derive (conf->instance, "cache", "cache_result", "hit",     stats->cache_hit);
                /* Cache misses */
-               varnish_submit_derive (conf->instance, "cache", "cache_result", "miss",    VSL_stats->cache_miss);
+               varnish_submit_derive (conf->instance, "cache", "cache_result", "miss",    stats->cache_miss);
                /* Cache hits for pass */
-               varnish_submit_derive (conf->instance, "cache", "cache_result", "hitpass", VSL_stats->cache_hitpass);
+               varnish_submit_derive (conf->instance, "cache", "cache_result", "hitpass", stats->cache_hitpass);
        }
 
        if (conf->collect_connections)
        {
                /* Client connections accepted */
-               varnish_submit_derive (conf->instance, "connections", "connections", "accepted", VSL_stats->client_conn);
+               varnish_submit_derive (conf->instance, "connections", "connections", "accepted", stats->client_conn);
                /* Connection dropped, no sess */
-               varnish_submit_derive (conf->instance, "connections", "connections", "dropped" , VSL_stats->client_drop);
+               varnish_submit_derive (conf->instance, "connections", "connections", "dropped" , stats->client_drop);
                /* Client requests received    */
-               varnish_submit_derive (conf->instance, "connections", "connections", "received", VSL_stats->client_req);
+               varnish_submit_derive (conf->instance, "connections", "connections", "received", stats->client_req);
        }
 
        if (conf->collect_esi)
        {
-               /* Objects ESI parsed (unlock) */
-               varnish_submit_derive (conf->instance, "esi", "total_operations", "parsed", VSL_stats->esi_parse);
                /* ESI parse errors (unlock)   */
-               varnish_submit_derive (conf->instance, "esi", "total_operations", "error",  VSL_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);
+#endif
        }
 
        if (conf->collect_backend)
        {
                /* Backend conn. success       */
-               varnish_submit_derive (conf->instance, "backend", "connections", "success"      , VSL_stats->backend_conn);
+               varnish_submit_derive (conf->instance, "backend", "connections", "success"      , stats->backend_conn);
                /* Backend conn. not attempted */
-               varnish_submit_derive (conf->instance, "backend", "connections", "not-attempted", VSL_stats->backend_unhealthy);
+               varnish_submit_derive (conf->instance, "backend", "connections", "not-attempted", stats->backend_unhealthy);
                /* Backend conn. too many      */
-               varnish_submit_derive (conf->instance, "backend", "connections", "too-many"     , VSL_stats->backend_busy);
+               varnish_submit_derive (conf->instance, "backend", "connections", "too-many"     , stats->backend_busy);
                /* Backend conn. failures      */
-               varnish_submit_derive (conf->instance, "backend", "connections", "failures"     , VSL_stats->backend_fail);
+               varnish_submit_derive (conf->instance, "backend", "connections", "failures"     , stats->backend_fail);
                /* Backend conn. reuses        */
-               varnish_submit_derive (conf->instance, "backend", "connections", "reuses"       , VSL_stats->backend_reuse);
+               varnish_submit_derive (conf->instance, "backend", "connections", "reuses"       , stats->backend_reuse);
                /* Backend conn. was closed    */
-               varnish_submit_derive (conf->instance, "backend", "connections", "was-closed"   , VSL_stats->backend_toolate);
+               varnish_submit_derive (conf->instance, "backend", "connections", "was-closed"   , stats->backend_toolate);
                /* Backend conn. recycles      */
-               varnish_submit_derive (conf->instance, "backend", "connections", "recycled"     , VSL_stats->backend_recycle);
+               varnish_submit_derive (conf->instance, "backend", "connections", "recycled"     , stats->backend_recycle);
+#if HAVE_VARNISH_V2
                /* Backend conn. unused        */
-               varnish_submit_derive (conf->instance, "backend", "connections", "unused"       , VSL_stats->backend_unused);
+               varnish_submit_derive (conf->instance, "backend", "connections", "unused"       , stats->backend_unused);
+#endif
        }
 
        if (conf->collect_fetch)
        {
                /* Fetch head                */
-               varnish_submit_derive (conf->instance, "fetch", "http_requests", "head"       , VSL_stats->fetch_head);
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "head"       , stats->fetch_head);
                /* Fetch with length         */
-               varnish_submit_derive (conf->instance, "fetch", "http_requests", "length"     , VSL_stats->fetch_length);
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "length"     , stats->fetch_length);
                /* Fetch chunked             */
-               varnish_submit_derive (conf->instance, "fetch", "http_requests", "chunked"    , VSL_stats->fetch_chunked);
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "chunked"    , stats->fetch_chunked);
                /* Fetch EOF                 */
-               varnish_submit_derive (conf->instance, "fetch", "http_requests", "eof"        , VSL_stats->fetch_eof);
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "eof"        , stats->fetch_eof);
                /* Fetch bad headers         */
-               varnish_submit_derive (conf->instance, "fetch", "http_requests", "bad_headers", VSL_stats->fetch_bad);
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "bad_headers", stats->fetch_bad);
                /* Fetch wanted close        */
-               varnish_submit_derive (conf->instance, "fetch", "http_requests", "close"      , VSL_stats->fetch_close);
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "close"      , stats->fetch_close);
                /* Fetch pre HTTP/1.1 closed */
-               varnish_submit_derive (conf->instance, "fetch", "http_requests", "oldhttp"    , VSL_stats->fetch_oldhttp);
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "oldhttp"    , stats->fetch_oldhttp);
                /* Fetch zero len            */
-               varnish_submit_derive (conf->instance, "fetch", "http_requests", "zero"       , VSL_stats->fetch_zero);
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "zero"       , stats->fetch_zero);
                /* Fetch failed              */
-               varnish_submit_derive (conf->instance, "fetch", "http_requests", "failed"     , VSL_stats->fetch_failed);
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "failed"     , stats->fetch_failed);
        }
 
        if (conf->collect_hcb)
        {
                /* HCB Lookups without lock */
-               varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_nolock", VSL_stats->hcb_nolock);
+               varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_nolock", stats->hcb_nolock);
                /* HCB Lookups with lock    */
-               varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_lock",   VSL_stats->hcb_lock);
+               varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_lock",   stats->hcb_lock);
                /* HCB Inserts              */
-               varnish_submit_derive (conf->instance, "hcb", "cache_operation", "insert",        VSL_stats->hcb_insert);
+               varnish_submit_derive (conf->instance, "hcb", "cache_operation", "insert",        stats->hcb_insert);
        }
 
        if (conf->collect_shm)
        {
                /* SHM records                 */
-               varnish_submit_derive (conf->instance, "shm", "total_operations", "records"   , VSL_stats->shm_records);
+               varnish_submit_derive (conf->instance, "shm", "total_operations", "records"   , stats->shm_records);
                /* SHM writes                  */
-               varnish_submit_derive (conf->instance, "shm", "total_operations", "writes"    , VSL_stats->shm_writes);
+               varnish_submit_derive (conf->instance, "shm", "total_operations", "writes"    , stats->shm_writes);
                /* SHM flushes due to overflow */
-               varnish_submit_derive (conf->instance, "shm", "total_operations", "flushes"   , VSL_stats->shm_flushes);
+               varnish_submit_derive (conf->instance, "shm", "total_operations", "flushes"   , stats->shm_flushes);
                /* SHM MTX contention          */
-               varnish_submit_derive (conf->instance, "shm", "total_operations", "contention", VSL_stats->shm_cont);
+               varnish_submit_derive (conf->instance, "shm", "total_operations", "contention", stats->shm_cont);
                /* SHM cycles through buffer   */
-               varnish_submit_derive (conf->instance, "shm", "total_operations", "cycles"    , VSL_stats->shm_cycles);
+               varnish_submit_derive (conf->instance, "shm", "total_operations", "cycles"    , stats->shm_cycles);
        }
 
+#if HAVE_VARNISH_V2
        if (conf->collect_sm)
        {
                /* allocator requests */
-               varnish_submit_derive (conf->instance, "sm", "total_requests", "nreq",  VSL_stats->sm_nreq);
+               varnish_submit_derive (conf->instance, "sm", "total_requests", "nreq",         stats->sm_nreq);
                /* outstanding allocations */
-               varnish_submit_gauge (conf->instance,  "sm", "requests", "outstanding", VSL_stats->sm_nobj);
+               varnish_submit_gauge (conf->instance,  "sm", "requests", "outstanding",        stats->sm_nobj);
                /* bytes allocated */
-               varnish_submit_derive (conf->instance,  "sm", "total_bytes", "allocated",      VSL_stats->sm_balloc);
+               varnish_submit_derive (conf->instance,  "sm", "total_bytes", "allocated",      stats->sm_balloc);
                /* bytes free */
-               varnish_submit_derive (conf->instance,  "sm", "total_bytes", "free",           VSL_stats->sm_bfree);
+               varnish_submit_derive (conf->instance,  "sm", "total_bytes", "free",           stats->sm_bfree);
        }
 
        if (conf->collect_sma)
        {
                /* SMA allocator requests */
-               varnish_submit_derive (conf->instance, "sma", "total_requests", "nreq",  VSL_stats->sma_nreq);
+               varnish_submit_derive (conf->instance, "sma", "total_requests", "nreq",    stats->sma_nreq);
                /* SMA outstanding allocations */
-               varnish_submit_gauge (conf->instance,  "sma", "requests", "outstanding", VSL_stats->sma_nobj);
+               varnish_submit_gauge (conf->instance,  "sma", "requests", "outstanding",   stats->sma_nobj);
                /* SMA outstanding bytes */
-               varnish_submit_gauge (conf->instance,  "sma", "bytes", "outstanding",    VSL_stats->sma_nbytes);
+               varnish_submit_gauge (conf->instance,  "sma", "bytes", "outstanding",      stats->sma_nbytes);
                /* SMA bytes allocated */
-               varnish_submit_derive (conf->instance,  "sma", "total_bytes", "allocated",      VSL_stats->sma_balloc);
+               varnish_submit_derive (conf->instance,  "sma", "total_bytes", "allocated", stats->sma_balloc);
                /* SMA bytes free */
-               varnish_submit_derive (conf->instance,  "sma", "total_bytes", "free" ,          VSL_stats->sma_bfree);
+               varnish_submit_derive (conf->instance,  "sma", "total_bytes", "free" ,     stats->sma_bfree);
        }
+#endif
 
        if (conf->collect_sms)
        {
                /* SMS allocator requests */
-               varnish_submit_derive (conf->instance, "sms", "total_requests", "allocator", VSL_stats->sms_nreq);
+               varnish_submit_derive (conf->instance, "sms", "total_requests", "allocator", stats->sms_nreq);
                /* SMS outstanding allocations */
-               varnish_submit_gauge (conf->instance,  "sms", "requests", "outstanding",     VSL_stats->sms_nobj);
+               varnish_submit_gauge (conf->instance,  "sms", "requests", "outstanding",     stats->sms_nobj);
                /* SMS outstanding bytes */
-               varnish_submit_gauge (conf->instance,  "sms", "bytes", "outstanding",        VSL_stats->sms_nbytes);
+               varnish_submit_gauge (conf->instance,  "sms", "bytes", "outstanding",        stats->sms_nbytes);
                /* SMS bytes allocated */
-               varnish_submit_derive (conf->instance,  "sms", "total_bytes", "allocated",          VSL_stats->sms_balloc);
+               varnish_submit_derive (conf->instance,  "sms", "total_bytes", "allocated",   stats->sms_balloc);
                /* SMS bytes freed */
-               varnish_submit_derive (conf->instance,  "sms", "total_bytes", "free",               VSL_stats->sms_bfree);
+               varnish_submit_derive (conf->instance,  "sms", "total_bytes", "free",        stats->sms_bfree);
        }
 
        if (conf->collect_totals)
        {
                /* Total Sessions */
-               varnish_submit_derive (conf->instance, "totals", "total_sessions", "sessions",  VSL_stats->s_sess);
+               varnish_submit_derive (conf->instance, "totals", "total_sessions", "sessions",  stats->s_sess);
                /* Total Requests */
-               varnish_submit_derive (conf->instance, "totals", "total_requests", "requests",  VSL_stats->s_req);
+               varnish_submit_derive (conf->instance, "totals", "total_requests", "requests",  stats->s_req);
                /* Total pipe */
-               varnish_submit_derive (conf->instance, "totals", "total_operations", "pipe",    VSL_stats->s_pipe);
+               varnish_submit_derive (conf->instance, "totals", "total_operations", "pipe",    stats->s_pipe);
                /* Total pass */
-               varnish_submit_derive (conf->instance, "totals", "total_operations", "pass",    VSL_stats->s_pass);
+               varnish_submit_derive (conf->instance, "totals", "total_operations", "pass",    stats->s_pass);
                /* Total fetch */
-               varnish_submit_derive (conf->instance, "totals", "total_operations", "fetches", VSL_stats->s_fetch);
+               varnish_submit_derive (conf->instance, "totals", "total_operations", "fetches", stats->s_fetch);
                /* Total header bytes */
-               varnish_submit_derive (conf->instance, "totals", "total_bytes", "header-bytes", VSL_stats->s_hdrbytes);
+               varnish_submit_derive (conf->instance, "totals", "total_bytes", "header-bytes", stats->s_hdrbytes);
                /* Total body byte */
-               varnish_submit_derive (conf->instance, "totals", "total_bytes", "body-bytes",   VSL_stats->s_bodybytes);
+               varnish_submit_derive (conf->instance, "totals", "total_bytes", "body-bytes",   stats->s_bodybytes);
        }
 
        if (conf->collect_workers)
        {
                /* worker threads */
-               varnish_submit_gauge (conf->instance, "workers", "threads", "worker",            VSL_stats->n_wrk);
+               varnish_submit_gauge (conf->instance, "workers", "threads", "worker",             stats->n_wrk);
                /* worker threads created */
-               varnish_submit_derive (conf->instance, "workers", "total_threads", "created",     VSL_stats->n_wrk_create);
+               varnish_submit_derive (conf->instance, "workers", "total_threads", "created",     stats->n_wrk_create);
                /* worker threads not created */
-               varnish_submit_derive (conf->instance, "workers", "total_threads", "failed",      VSL_stats->n_wrk_failed);
+               varnish_submit_derive (conf->instance, "workers", "total_threads", "failed",      stats->n_wrk_failed);
                /* worker threads limited */
-               varnish_submit_derive (conf->instance, "workers", "total_threads", "limited",     VSL_stats->n_wrk_max);
+               varnish_submit_derive (conf->instance, "workers", "total_threads", "limited",     stats->n_wrk_max);
+               /* dropped work requests */
+               varnish_submit_derive (conf->instance, "workers", "total_requests", "dropped",    stats->n_wrk_drop);
+#ifdef HAVE_VARNISH_V2
                /* queued work requests */
-               varnish_submit_derive (conf->instance, "workers", "total_requests", "queued",     VSL_stats->n_wrk_queue);
+               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", VSL_stats->n_wrk_overflow);
-               /* dropped work requests */
-               varnish_submit_derive (conf->instance, "workers", "total_requests", "dropped",    VSL_stats->n_wrk_drop);
+               varnish_submit_derive (conf->instance, "workers", "total_requests", "overflowed", stats->n_wrk_overflow);
+#endif
        }
 } /* }}} void varnish_monitor */
 
+#if HAVE_VARNISH_V3
 static int varnish_read (user_data_t *ud) /* {{{ */
 {
-       struct varnish_stats *VSL_stats;
+       struct VSM_data *vd;
+       const c_varnish_stats_t *stats;
+
        user_config_t *conf;
 
        if ((ud == NULL) || (ud->data == NULL))
@@ -387,18 +410,52 @@ static int varnish_read (user_data_t *ud) /* {{{ */
 
        conf = ud->data;
 
-       VSL_stats = VSL_OpenStats (conf->instance);
-       if (VSL_stats == NULL)
+       vd = VSM_New();
+       VSC_Setup(vd);
+       if (VSM_n_Arg(vd, conf->instance) == -1)
+       {
+               ERROR ("Varnish plugin : unable to load statistics from instance");
+               return (-1);
+       }
+       if (VSC_Open (vd, /* diag = */ 1))
+       {
+               ERROR ("varnish plugin: Unable to load statistics.");
+
+               return (-1);
+       }
+
+       stats = VSC_Main(vd);
+
+       varnish_monitor (conf, stats);
+       VSM_Close (vd);
+
+       return (0);
+} /* }}} */
+#else /* if HAVE_VARNISH_V2 */
+static int varnish_read (user_data_t *ud) /* {{{ */
+{
+       const c_varnish_stats_t *stats;
+
+       user_config_t *conf;
+
+       if ((ud == NULL) || (ud->data == NULL))
+               return (EINVAL);
+
+       conf = ud->data;
+
+       stats = VSL_OpenStats (conf->instance);
+       if (stats == NULL)
        {
                ERROR ("Varnish plugin : unable to load statistics");
 
                return (-1);
        }
 
-       varnish_monitor (conf, VSL_stats);
+       varnish_monitor (conf, stats);
 
-    return (0);
+       return (0);
 } /* }}} */
+#endif
 
 static void varnish_config_free (void *ptr) /* {{{ */
 {
@@ -423,11 +480,13 @@ static int varnish_config_apply_default (user_config_t *conf) /* {{{ */
        conf->collect_fetch       = 0;
        conf->collect_hcb         = 0;
        conf->collect_shm         = 1;
+#if HAVE_VARNISH_V2
        conf->collect_sm          = 0;
        conf->collect_sma         = 0;
+#endif
        conf->collect_sms         = 0;
        conf->collect_totals      = 0;
-       
+
        return (0);
 } /* }}} int varnish_config_apply_default */
 
@@ -519,12 +578,14 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                        cf_util_get_boolean (child, &conf->collect_hcb);
                else if (strcasecmp ("CollectSHM", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_shm);
-               else if (strcasecmp ("CollectSMA", child->key) == 0)
-                       cf_util_get_boolean (child, &conf->collect_sma);
                else if (strcasecmp ("CollectSMS", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_sms);
+#if HAVE_VARNISH_V2
+               else if (strcasecmp ("CollectSMA", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_sma);
                else if (strcasecmp ("CollectSM", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_sm);
+#endif
                else if (strcasecmp ("CollectTotals", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_totals);
                else if (strcasecmp ("CollectWorkers", child->key) == 0)
@@ -544,9 +605,11 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                        && !conf->collect_fetch
                        && !conf->collect_hcb
                        && !conf->collect_shm
-                       && !conf->collect_sma
                        && !conf->collect_sms
+#if HAVE_VARNISH_V2
+                       && !conf->collect_sma
                        && !conf->collect_sm
+#endif
                        && !conf->collect_totals
                        && !conf->collect_workers)
        {
diff --git a/src/write_graphite.c b/src/write_graphite.c
new file mode 100644 (file)
index 0000000..2ae30ef
--- /dev/null
@@ -0,0 +1,508 @@
+/**
+ * collectd - src/write_graphite.c
+ * Copyright (C) 2012       Pierre-Yves Ritschard
+ * Copyright (C) 2011       Scott Sanders
+ * Copyright (C) 2009       Paul Sadauskas
+ * Copyright (C) 2009       Doug MacEachern
+ * Copyright (C) 2007-2012  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ *   Doug MacEachern <dougm at hyperic.com>
+ *   Paul Sadauskas <psadauskas at gmail.com>
+ *   Scott Sanders <scott at jssjr.com>
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ *
+ * Based on the write_http plugin.
+ **/
+
+ /* write_graphite plugin configuation example
+  *
+  * <Plugin write_graphite>
+  *   <Carbon>
+  *     Host "localhost"
+  *     Port "2003"
+  *     Prefix "collectd"
+  *   </Carbon>
+  * </Plugin>
+  */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include "utils_cache.h"
+#include "utils_parse_option.h"
+#include "utils_format_graphite.h"
+
+/* Folks without pthread will need to disable this plugin. */
+#include <pthread.h>
+
+#include <sys/socket.h>
+#include <netdb.h>
+
+#ifndef WG_DEFAULT_NODE
+# define WG_DEFAULT_NODE "localhost"
+#endif
+
+#ifndef WG_DEFAULT_SERVICE
+# define WG_DEFAULT_SERVICE "2003"
+#endif
+
+#ifndef WG_DEFAULT_ESCAPE
+# define WG_DEFAULT_ESCAPE '_'
+#endif
+
+/* Ethernet - (IPv6 + TCP) = 1500 - (40 + 32) = 1428 */
+#ifndef WG_SEND_BUF_SIZE
+# define WG_SEND_BUF_SIZE 1428
+#endif
+
+/*
+ * Private variables
+ */
+struct wg_callback
+{
+    int      sock_fd;
+
+    char    *node;
+    char    *service;
+    char    *prefix;
+    char    *postfix;
+    char     escape_char;
+
+    _Bool    store_rates;
+    _Bool    separate_instances;
+    _Bool    always_append_ds;
+
+    char     send_buf[WG_SEND_BUF_SIZE];
+    size_t   send_buf_free;
+    size_t   send_buf_fill;
+    cdtime_t send_buf_init_time;
+
+    pthread_mutex_t send_lock;
+};
+
+
+/*
+ * Functions
+ */
+static void wg_reset_buffer (struct wg_callback *cb)
+{
+    memset (cb->send_buf, 0, sizeof (cb->send_buf));
+    cb->send_buf_free = sizeof (cb->send_buf);
+    cb->send_buf_fill = 0;
+    cb->send_buf_init_time = cdtime ();
+}
+
+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)
+    {
+        char errbuf[1024];
+        ERROR ("write_graphite plugin: send failed with status %zi (%s)",
+                status, sstrerror (errno, errbuf, sizeof (errbuf)));
+
+
+        close (cb->sock_fd);
+        cb->sock_fd = -1;
+
+        return (-1);
+    }
+
+    return (0);
+}
+
+/* NOTE: You must hold cb->send_lock when calling this function! */
+static int wg_flush_nolock (cdtime_t timeout, struct wg_callback *cb)
+{
+    int status;
+
+    DEBUG ("write_graphite plugin: wg_flush_nolock: timeout = %.3f; "
+            "send_buf_fill = %zu;",
+            (double)timeout,
+            cb->send_buf_fill);
+
+    /* timeout == 0  => flush unconditionally */
+    if (timeout > 0)
+    {
+        cdtime_t now;
+
+        now = cdtime ();
+        if ((cb->send_buf_init_time + timeout) > now)
+            return (0);
+    }
+
+    if (cb->send_buf_fill <= 0)
+    {
+        cb->send_buf_init_time = cdtime ();
+        return (0);
+    }
+
+    status = wg_send_buffer (cb);
+    wg_reset_buffer (cb);
+
+    return (status);
+}
+
+static int wg_callback_init (struct wg_callback *cb)
+{
+    struct addrinfo ai_hints;
+    struct addrinfo *ai_list;
+    struct addrinfo *ai_ptr;
+    int status;
+
+    const char *node = cb->node ? cb->node : WG_DEFAULT_NODE;
+    const char *service = cb->service ? cb->service : WG_DEFAULT_SERVICE;
+
+    if (cb->sock_fd > 0)
+        return (0);
+
+    memset (&ai_hints, 0, sizeof (ai_hints));
+#ifdef AI_ADDRCONFIG
+    ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+    ai_hints.ai_family = AF_UNSPEC;
+    ai_hints.ai_socktype = SOCK_STREAM;
+
+    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));
+        return (-1);
+    }
+
+    assert (ai_list != NULL);
+    for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+    {
+        cb->sock_fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
+                ai_ptr->ai_protocol);
+        if (cb->sock_fd < 0)
+            continue;
+
+        status = connect (cb->sock_fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+        if (status != 0)
+        {
+            close (cb->sock_fd);
+            cb->sock_fd = -1;
+            continue;
+        }
+
+        break;
+    }
+
+    freeaddrinfo (ai_list);
+
+    if (cb->sock_fd < 0)
+    {
+        char errbuf[1024];
+        ERROR ("write_graphite plugin: Connecting to %s:%s failed. "
+                "The last error was: %s", node, service,
+                sstrerror (errno, errbuf, sizeof (errbuf)));
+        close (cb->sock_fd);
+        return (-1);
+    }
+
+    wg_reset_buffer (cb);
+
+    return (0);
+}
+
+static void wg_callback_free (void *data)
+{
+    struct wg_callback *cb;
+
+    if (data == NULL)
+        return;
+
+    cb = data;
+
+    pthread_mutex_lock (&cb->send_lock);
+
+    wg_flush_nolock (/* timeout = */ 0, cb);
+
+    close(cb->sock_fd);
+    cb->sock_fd = -1;
+
+    sfree(cb->node);
+    sfree(cb->service);
+    sfree(cb->prefix);
+    sfree(cb->postfix);
+
+    pthread_mutex_destroy (&cb->send_lock);
+
+    sfree(cb);
+}
+
+static int wg_flush (cdtime_t timeout,
+        const char *identifier __attribute__((unused)),
+        user_data_t *user_data)
+{
+    struct wg_callback *cb;
+    int status;
+
+    if (user_data == NULL)
+        return (-EINVAL);
+
+    cb = user_data->data;
+
+    pthread_mutex_lock (&cb->send_lock);
+
+    if (cb->sock_fd < 0)
+    {
+        status = wg_callback_init (cb);
+        if (status != 0)
+        {
+            ERROR ("write_graphite plugin: wg_callback_init failed.");
+            pthread_mutex_unlock (&cb->send_lock);
+            return (-1);
+        }
+    }
+
+    status = wg_flush_nolock (timeout, cb);
+    pthread_mutex_unlock (&cb->send_lock);
+
+    return (status);
+}
+
+static int wg_send_message (char const *message, struct wg_callback *cb)
+{
+    int status;
+    size_t message_len;
+
+    message_len = strlen (message);
+
+    pthread_mutex_lock (&cb->send_lock);
+
+    if (cb->sock_fd < 0)
+    {
+        status = wg_callback_init (cb);
+        if (status != 0)
+        {
+            ERROR ("write_graphite plugin: wg_callback_init failed.");
+            pthread_mutex_unlock (&cb->send_lock);
+            return (-1);
+        }
+    }
+
+    if (message_len >= cb->send_buf_free)
+    {
+        status = wg_flush_nolock (/* timeout = */ 0, cb);
+        if (status != 0)
+        {
+            pthread_mutex_unlock (&cb->send_lock);
+            return (status);
+        }
+    }
+
+    /* Assert that we have enough space for this message. */
+    assert (message_len < cb->send_buf_free);
+
+    /* `message_len + 1' because `message_len' does not include the
+     * trailing null byte. Neither does `send_buffer_fill'. */
+    memcpy (cb->send_buf + cb->send_buf_fill,
+            message, message_len + 1);
+    cb->send_buf_fill += message_len;
+    cb->send_buf_free -= message_len;
+
+    DEBUG ("write_graphite plugin: [%s]:%s buf %zu/%zu (%.1f %%) \"%s\"",
+            cb->node,
+            cb->service,
+            cb->send_buf_fill, sizeof (cb->send_buf),
+            100.0 * ((double) cb->send_buf_fill) / ((double) sizeof (cb->send_buf)),
+            message);
+
+    pthread_mutex_unlock (&cb->send_lock);
+
+    return (0);
+}
+
+static int wg_write_messages (const data_set_t *ds, const value_list_t *vl,
+        struct wg_callback *cb)
+{
+    char buffer[4096];
+    int status;
+
+    if (0 != strcmp (ds->type, vl->type))
+    {
+        ERROR ("write_graphite plugin: DS type does not match "
+                "value list type");
+        return -1;
+    }
+
+    memset (buffer, 0, sizeof (buffer));
+    status = format_graphite (buffer, sizeof (buffer), ds, vl,
+            cb->prefix, cb->postfix, cb->escape_char, cb->store_rates);
+    if (status != 0) /* error message has been printed already. */
+        return (status);
+
+    wg_send_message (buffer, cb);
+    if (status != 0)
+    {
+        ERROR ("write_graphite plugin: wg_send_message failed "
+                "with status %i.", status);
+        return (status);
+    }
+
+    return (0);
+} /* int wg_write_messages */
+
+static int wg_write (const data_set_t *ds, const value_list_t *vl,
+        user_data_t *user_data)
+{
+    struct wg_callback *cb;
+    int status;
+
+    if (user_data == NULL)
+        return (EINVAL);
+
+    cb = user_data->data;
+
+    status = wg_write_messages (ds, vl, cb);
+
+    return (status);
+}
+
+static int config_set_char (char *dest,
+        oconfig_item_t *ci)
+{
+    char buffer[4];
+    int status;
+
+    memset (buffer, 0, sizeof (buffer));
+
+    status = cf_util_get_string_buffer (ci, buffer, sizeof (buffer));
+    if (status != 0)
+        return (status);
+
+    if (buffer[0] == 0)
+    {
+        ERROR ("write_graphite plugin: Cannot use an empty string for the "
+                "\"EscapeCharacter\" option.");
+        return (-1);
+    }
+
+    if (buffer[1] != 0)
+    {
+        WARNING ("write_graphite plugin: Only the first character of the "
+                "\"EscapeCharacter\" option ('%c') will be used.",
+                (int) buffer[0]);
+    }
+
+    *dest = buffer[0];
+
+    return (0);
+}
+
+static int wg_config_carbon (oconfig_item_t *ci)
+{
+    struct wg_callback *cb;
+    user_data_t user_data;
+    char callback_name[DATA_MAX_NAME_LEN];
+    int i;
+
+    cb = malloc (sizeof (*cb));
+    if (cb == NULL)
+    {
+        ERROR ("write_graphite plugin: malloc failed.");
+        return (-1);
+    }
+    memset (cb, 0, sizeof (*cb));
+    cb->sock_fd = -1;
+    cb->node = NULL;
+    cb->service = NULL;
+    cb->prefix = NULL;
+    cb->postfix = NULL;
+    cb->escape_char = WG_DEFAULT_ESCAPE;
+    cb->store_rates = 1;
+
+    pthread_mutex_init (&cb->send_lock, /* attr = */ NULL);
+
+    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, &cb->node);
+        else if (strcasecmp ("Port", child->key) == 0)
+            cf_util_get_service (child, &cb->service);
+        else if (strcasecmp ("Prefix", child->key) == 0)
+            cf_util_get_string (child, &cb->prefix);
+        else if (strcasecmp ("Postfix", child->key) == 0)
+            cf_util_get_string (child, &cb->postfix);
+        else if (strcasecmp ("StoreRates", child->key) == 0)
+            cf_util_get_boolean (child, &cb->store_rates);
+        else if (strcasecmp ("SeparateInstances", child->key) == 0)
+            cf_util_get_boolean (child, &cb->separate_instances);
+        else if (strcasecmp ("AlwaysAppendDS", child->key) == 0)
+            cf_util_get_boolean (child, &cb->always_append_ds);
+        else if (strcasecmp ("EscapeCharacter", child->key) == 0)
+            config_set_char (&cb->escape_char, child);
+        else
+        {
+            ERROR ("write_graphite plugin: Invalid configuration "
+                        "option: %s.", child->key);
+        }
+    }
+
+    ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s/%s",
+            cb->node != NULL ? cb->node : WG_DEFAULT_NODE,
+            cb->service != NULL ? cb->service : WG_DEFAULT_SERVICE);
+
+    memset (&user_data, 0, sizeof (user_data));
+    user_data.data = cb;
+    user_data.free_func = wg_callback_free;
+    plugin_register_write (callback_name, wg_write, &user_data);
+
+    user_data.free_func = NULL;
+    plugin_register_flush (callback_name, wg_flush, &user_data);
+
+    return (0);
+}
+
+static int wg_config (oconfig_item_t *ci)
+{
+    int i;
+
+    for (i = 0; i < ci->children_num; i++)
+    {
+        oconfig_item_t *child = ci->children + i;
+
+        if (strcasecmp ("Carbon", child->key) == 0)
+            wg_config_carbon (child);
+        else
+        {
+            ERROR ("write_graphite plugin: Invalid configuration "
+                    "option: %s.", child->key);
+        }
+    }
+
+    return (0);
+}
+
+void module_register (void)
+{
+    plugin_register_complex_config ("write_graphite", wg_config);
+}
+
+/* vim: set sw=4 ts=4 sts=4 tw=78 et : */
index 4deb24d..c7b7682 100644 (file)
@@ -1,8 +1,8 @@
 /**
  * collectd - src/write_mongodb.c
- * Copyright (C) 2010  Florian Forster
- * Copyright (C) 2010  Akkarit Sangpetch
- * Copyright (C) 2012  Chris Lundquist
+ * Copyright (C) 2010-2012  Florian Forster
+ * Copyright (C) 2010       Akkarit Sangpetch
+ * Copyright (C) 2012       Chris Lundquist
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -32,6 +32,7 @@
 #include "plugin.h"
 #include "common.h"
 #include "configfile.h"
+#include "utils_cache.h"
 
 #include <pthread.h>
 
@@ -50,7 +51,7 @@ struct wm_node_s
   int port;
   int timeout;
 
-  int connected;
+  _Bool store_rates;
 
   mongo conn[1];
   pthread_mutex_t lock;
@@ -60,78 +61,166 @@ typedef struct wm_node_s wm_node_t;
 /*
  * Functions
  */
-static int wm_write (const data_set_t *ds, /* {{{ */
+static bson *wm_create_bson (const data_set_t *ds, /* {{{ */
     const value_list_t *vl,
-    user_data_t *ud)
+    _Bool store_rates)
 {
-  wm_node_t *node = ud->data;
-  char collection_name[512];
-  int status;
+  bson *ret;
+  gauge_t *rates;
   int i;
-  bson record;
 
-  ssnprintf(collection_name, sizeof (collection_name), "collectd.%s", vl->plugin);
+  ret = bson_create ();
+  if (ret == NULL)
+  {
+    ERROR ("write_mongodb plugin: bson_create failed.");
+    return (NULL);
+  }
 
-  bson_init(&record);
-  bson_append_time_t(&record,"ts",CDTIME_T_TO_TIME_T(vl->time));
-  bson_append_string(&record,"h",vl->host);
-  bson_append_string(&record,"i",vl->plugin_instance);
-  bson_append_string(&record,"t",vl->type);
-  bson_append_string(&record,"ti",vl->type_instance);
+  if (store_rates)
+  {
+    rates = uc_get_rate (ds, vl);
+    if (rates == NULL)
+    {
+      ERROR ("write_mongodb plugin: uc_get_rate() failed.");
+      return (NULL);
+    }
+  }
+  else
+  {
+    rates = NULL;
+  }
 
+  bson_init (ret);
+  bson_append_date (ret, "time", (bson_date_t) CDTIME_T_TO_MS (vl->time));
+  bson_append_string (ret, "host", vl->host);
+  bson_append_string (ret, "plugin", vl->plugin);
+  bson_append_string (ret, "plugin_instance", vl->plugin_instance);
+  bson_append_string (ret, "type", vl->type);
+  bson_append_string (ret, "type_instance", vl->type_instance);
+
+  bson_append_start_array (ret, "values"); /* {{{ */
   for (i = 0; i < ds->ds_num; i++)
   {
-    if (ds->ds[i].type == DS_TYPE_COUNTER)
-      bson_append_long(&record, ds->ds[i].name, vl->values[i].counter);
-    else if (ds->ds[i].type == DS_TYPE_GAUGE)
-      bson_append_double(&record, ds->ds[i].name, vl->values[i].gauge);
+    char key[16];
+
+    ssnprintf (key, sizeof (key), "%i", i);
+
+    if (ds->ds[i].type == DS_TYPE_GAUGE)
+      bson_append_double(ret, key, vl->values[i].gauge);
+    else if (store_rates)
+      bson_append_double(ret, key, (double) rates[i]);
+    else if (ds->ds[i].type == DS_TYPE_COUNTER)
+      bson_append_long(ret, key, vl->values[i].counter);
     else if (ds->ds[i].type == DS_TYPE_DERIVE)
-      bson_append_long(&record, ds->ds[i].name, vl->values[i].derive);
+      bson_append_long(ret, key, vl->values[i].derive);
     else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
-      bson_append_long(&record, ds->ds[i].name, vl->values[i].absolute);
+      bson_append_long(ret, key, vl->values[i].absolute);
     else
       assert (23 == 42);
   }
-  /* We must finish the record, other wise the insert will fail */
-  bson_finish(&record);
+  bson_append_finish_array (ret); /* }}} values */
+
+  bson_append_start_array (ret, "dstypes"); /* {{{ */
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    char key[16];
+
+    ssnprintf (key, sizeof (key), "%i", i);
+
+    if (store_rates)
+      bson_append_string (ret, key, "gauge");
+    else
+      bson_append_string (ret, key, DS_TYPE_TO_STRING (ds->ds[i].type));
+  }
+  bson_append_finish_array (ret); /* }}} dstypes */
+
+  bson_append_start_array (ret, "dsnames"); /* {{{ */
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    char key[16];
+
+    ssnprintf (key, sizeof (key), "%i", i);
+    bson_append_string (ret, key, ds->ds[i].name);
+  }
+  bson_append_finish_array (ret); /* }}} dsnames */
+
+  bson_finish (ret);
+
+  sfree (rates);
+  return (ret);
+} /* }}} bson *wm_create_bson */
+
+static int wm_write (const data_set_t *ds, /* {{{ */
+    const value_list_t *vl,
+    user_data_t *ud)
+{
+  wm_node_t *node = ud->data;
+  char collection_name[512];
+  bson *bson_record;
+  int status;
+
+  ssnprintf (collection_name, sizeof (collection_name), "collectd.%s",
+      vl->plugin);
+
+  bson_record = wm_create_bson (ds, vl, node->store_rates);
+  if (bson_record == NULL)
+    return (ENOMEM);
 
   pthread_mutex_lock (&node->lock);
 
-  if (node->connected == 0)
+  if (!mongo_is_connected (node->conn))
   {
-    status = mongo_connect(node->conn, node->host, node->port);
+    INFO ("write_mongodb plugin: Connecting to [%s]:%i",
+        (node->host != NULL) ? node->host : "localhost",
+        (node->port != 0) ? node->port : MONGO_DEFAULT_PORT);
+    status = mongo_connect (node->conn, node->host, node->port);
     if (status != MONGO_OK) {
-      ERROR ("write_mongodb plugin: Connecting to host \"%s\" (port %i) failed.",
+      ERROR ("write_mongodb plugin: Connecting to [%s]:%i failed.",
           (node->host != NULL) ? node->host : "localhost",
           (node->port != 0) ? node->port : MONGO_DEFAULT_PORT);
-      mongo_destroy(node->conn);
+      mongo_destroy (node->conn);
       pthread_mutex_unlock (&node->lock);
       return (-1);
-    } else {
-      node->connected = 1;
+    }
+
+    if (node->timeout > 0) {
+      status = mongo_set_op_timeout (node->conn, node->timeout);
+      if (status != MONGO_OK) {
+        WARNING ("write_mongodb plugin: mongo_set_op_timeout(%i) failed: %s",
+            node->timeout, node->conn->errstr);
+      }
     }
   }
 
   /* Assert if the connection has been established */
-  assert (node->connected == 1);
-
-  DEBUG ( "write_mongodb plugin: writing record");
-  /* bson_print(&record); */
+  assert (mongo_is_connected (node->conn));
 
-  status = mongo_insert(node->conn,collection_name,&record);
+  #if MONGO_MINOR >= 6
+    /* There was an API change in 0.6.0 as linked below */
+    /* https://github.com/mongodb/mongo-c-driver/blob/master/HISTORY.md */
+    status = mongo_insert (node->conn, collection_name, bson_record, NULL);
+  #else
+    status = mongo_insert (node->conn, collection_name, bson_record);
+  #endif
 
   if(status != MONGO_OK)
   {
     ERROR ( "write_mongodb plugin: error inserting record: %d", node->conn->err);
-    if (node->conn->err == MONGO_BSON_INVALID)
+    if (node->conn->err != MONGO_BSON_INVALID)
       ERROR ("write_mongodb plugin: %s", node->conn->errstr);
-    else if (record.err)
-      ERROR ("write_mongodb plugin: %s", record.errstr);
+    else if (bson_record->err)
+      ERROR ("write_mongodb plugin: %s", bson_record->errstr);
+
+    /* Disconnect except on data errors. */
+    if ((node->conn->err != MONGO_BSON_INVALID)
+        && (node->conn->err != MONGO_BSON_NOT_FINISHED))
+      mongo_destroy (node->conn);
   }
 
   pthread_mutex_unlock (&node->lock);
+
   /* free our resource as not to leak memory */
-  bson_destroy(&record);
+  bson_dispose (bson_record);
 
   return (0);
 } /* }}} int wm_write */
@@ -143,11 +232,8 @@ static void wm_config_free (void *ptr) /* {{{ */
   if (node == NULL)
     return;
 
-  if (node->connected != 0)
-  {
-    mongo_destroy(node->conn);
-    node->connected = 0;
-  }
+  if (mongo_is_connected (node->conn))
+    mongo_destroy (node->conn);
 
   sfree (node->host);
   sfree (node);
@@ -163,10 +249,9 @@ static int wm_config_node (oconfig_item_t *ci) /* {{{ */
   if (node == NULL)
     return (ENOMEM);
   memset (node, 0, sizeof (*node));
+  mongo_init (node->conn);
   node->host = NULL;
-  node->port = 0;
-  node->timeout = 1000;
-  node->connected = 0;
+  node->store_rates = 1;
   pthread_mutex_init (&node->lock, /* attr = */ NULL);
 
   status = cf_util_get_string_buffer (ci, node->name, sizeof (node->name));
@@ -194,6 +279,8 @@ static int wm_config_node (oconfig_item_t *ci) /* {{{ */
     }
     else if (strcasecmp ("Timeout", child->key) == 0)
       status = cf_util_get_int (child, &node->timeout);
+    else if (strcasecmp ("StoreRates", child->key) == 0)
+      status = cf_util_get_boolean (child, &node->store_rates);
     else
       WARNING ("write_mongodb plugin: Ignoring unknown config option \"%s\".",
           child->key);
index 8341be0..046b7b0 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/zfs_arc.c
  * Copyright (C) 2009  Anthony Dewhurst
+ * Copyright (C) 2012  Aurelien Rougemont
  *
  * 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 +18,7 @@
  *
  * Authors:
  *   Anthony Dewhurst <dewhurst at gmail>
+ *   Aurelien Rougemont <beorn at gandi.net>
  **/
 
 #include "collectd.h"
@@ -26,7 +28,7 @@
 /*
  * Global variables
  */
-static kstat_t *ksp;
+
 extern kstat_ctl_t *kc;
 
 static void za_submit (const char* type, const char* type_instance, value_t* values, int values_len)
@@ -52,12 +54,40 @@ static void za_submit_gauge (const char* type, const char* type_instance, gauge_
        za_submit (type, type_instance, &vv, 1);
 }
 
-static void za_submit_derive (const char* type, const char* type_instance, derive_t dv)
+static int za_read_derive (kstat_t *ksp, const char *kstat_value,
+    const char *type, const char *type_instance)
 {
-       value_t vv;
+  long long tmp;
+  value_t v;
+
+  tmp = get_kstat_value (ksp, (char *)kstat_value);
+  if (tmp == -1LL)
+  {
+    ERROR ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
+    return (-1);
+  }
+
+  v.derive = (derive_t) tmp;
+  za_submit (type, type_instance, /* values = */ &v, /* values_num = */ 1);
+  return (0);
+}
 
-       vv.derive = dv;
-       za_submit (type, type_instance, &vv, 1);
+static int za_read_gauge (kstat_t *ksp, const char *kstat_value,
+    const char *type, const char *type_instance)
+{
+  long long tmp;
+  value_t v;
+
+  tmp = get_kstat_value (ksp, (char *)kstat_value);
+  if (tmp == -1LL)
+  {
+    ERROR ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
+    return (-1);
+  }
+
+  v.gauge = (gauge_t) tmp;
+  za_submit (type, type_instance, /* values = */ &v, /* values_num = */ 1);
+  return (0);
 }
 
 static void za_submit_ratio (const char* type_instance, gauge_t hits, gauge_t misses)
@@ -77,17 +107,9 @@ static void za_submit_ratio (const char* type_instance, gauge_t hits, gauge_t mi
 
 static int za_read (void)
 {
-       gauge_t  arc_size, l2_size;
-       derive_t demand_data_hits,
-                demand_metadata_hits,
-                prefetch_data_hits,
-                prefetch_metadata_hits,
-                demand_data_misses,
-                demand_metadata_misses,
-                prefetch_data_misses,
-                prefetch_metadata_misses;
        gauge_t  arc_hits, arc_misses, l2_hits, l2_misses;
        value_t  l2_io[2];
+       kstat_t  *ksp   = NULL;
 
        get_kstat (&ksp, "zfs", 0, "arcstats");
        if (ksp == NULL)
@@ -97,32 +119,32 @@ static int za_read (void)
        }
 
        /* Sizes */
-       arc_size   = get_kstat_value(ksp, "size");
-       l2_size    = get_kstat_value(ksp, "l2_size");
-
-       za_submit_gauge ("cache_size", "arc", arc_size);
-       za_submit_gauge ("cache_size", "L2", l2_size);
+       za_read_gauge (ksp, "size",    "cache_size", "arc");
+       za_read_gauge (ksp, "l2_size", "cache_size", "L2");
+
+        /* 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_operation", "miss");
+       za_read_derive (ksp, "hash_collisions", "hash_collisions", "");
+       
+        /* 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");
 
        /* Hits / misses */
-       demand_data_hits       = get_kstat_value(ksp, "demand_data_hits");
-       demand_metadata_hits   = get_kstat_value(ksp, "demand_metadata_hits");
-       prefetch_data_hits     = get_kstat_value(ksp, "prefetch_data_hits");
-       prefetch_metadata_hits = get_kstat_value(ksp, "prefetch_metadata_hits");
-
-       demand_data_misses       = get_kstat_value(ksp, "demand_data_misses");
-       demand_metadata_misses   = get_kstat_value(ksp, "demand_metadata_misses");
-       prefetch_data_misses     = get_kstat_value(ksp, "prefetch_data_misses");
-       prefetch_metadata_misses = get_kstat_value(ksp, "prefetch_metadata_misses");
-
-       za_submit_derive ("cache_result", "demand_data-hit",       demand_data_hits);
-       za_submit_derive ("cache_result", "demand_metadata-hit",   demand_metadata_hits);
-       za_submit_derive ("cache_result", "prefetch_data-hit",     prefetch_data_hits);
-       za_submit_derive ("cache_result", "prefetch_metadata-hit", prefetch_metadata_hits);
-
-       za_submit_derive ("cache_result", "demand_data-miss",       demand_data_misses);
-       za_submit_derive ("cache_result", "demand_metadata-miss",   demand_metadata_misses);
-       za_submit_derive ("cache_result", "prefetch_data-miss",     prefetch_data_misses);
-       za_submit_derive ("cache_result", "prefetch_metadata-miss", prefetch_metadata_misses);
+       za_read_derive (ksp, "demand_data_hits",         "cache_result", "demand_data-hit");
+       za_read_derive (ksp, "demand_metadata_hits",     "cache_result", "demand_metadata-hit");
+       za_read_derive (ksp, "prefetch_data_hits",       "cache_result", "prefetch_data-hit");
+       za_read_derive (ksp, "prefetch_metadata_hits",   "cache_result", "prefetch_metadata-hit");
+       za_read_derive (ksp, "demand_data_misses",       "cache_result", "demand_data-miss");
+       za_read_derive (ksp, "demand_metadata_misses",   "cache_result", "demand_metadata-miss");
+       za_read_derive (ksp, "prefetch_data_misses",     "cache_result", "prefetch_data-miss");
+       za_read_derive (ksp, "prefetch_metadata_misses", "cache_result", "prefetch_metadata-miss");
 
        /* Ratios */
        arc_hits   = (gauge_t) get_kstat_value(ksp, "hits");
@@ -144,8 +166,6 @@ static int za_read (void)
 
 static int za_init (void) /* {{{ */
 {
-       ksp = NULL;
-
        /* kstats chain already opened by update_kstat (using *kc), verify everything went fine. */
        if (kc == NULL)
        {
index 5bc20a1..762d547 100755 (executable)
@@ -1,6 +1,6 @@
-#!/bin/sh
+#!/usr/bin/env bash
 
-DEFAULT_VERSION="5.0.2.git"
+DEFAULT_VERSION="5.1.1.git"
 
 VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"
 
@@ -10,8 +10,4 @@ fi
 
 VERSION="`echo \"$VERSION\" | sed -e 's/-/./g'`"
 
-if test "x`uname -s`" = "xAIX" || test "x`uname -s`" = "xSunOS" ; then
-       echo "$VERSION\c"
-else 
-       echo -n "$VERSION"
-fi
+echo -n "$VERSION"