Merge pull request #321 from mwongatemma/db_meta_data
authorPierre-Yves Ritschard <pyr@spootnik.org>
Mon, 28 Jul 2014 21:08:36 +0000 (23:08 +0200)
committerPierre-Yves Ritschard <pyr@spootnik.org>
Mon, 28 Jul 2014 21:08:36 +0000 (23:08 +0200)
Annotate meta data for db plugins

175 files changed:
.gitignore
AUTHORS
ChangeLog
README
bindings/Makefile.am
bindings/java/org/collectd/java/GenericJMXConfConnection.java
bindings/perl/uninstall_mod.pl [new file with mode: 0644]
clean.sh
configure.ac [new file with mode: 0644]
configure.in [deleted file]
contrib/collectd_network.py
contrib/collectd_unixsock.py
contrib/collection.cgi
contrib/collection3/etc/collection.conf
contrib/collection3/lib/Collectd/Config.pm
contrib/collection3/lib/Collectd/Graph/Config.pm
contrib/collection3/lib/Collectd/Graph/TypeLoader.pm
contrib/examples/myplugin.c
contrib/php-collection/graph.php
contrib/postgresql/collectd_insert.sql
contrib/redhat/apache.conf [deleted file]
contrib/redhat/collectd.conf [deleted file]
contrib/redhat/collectd.spec
contrib/redhat/collection3.conf [new file with mode: 0644]
contrib/redhat/email.conf [deleted file]
contrib/redhat/init.d-collectd
contrib/redhat/mysql.conf [deleted file]
contrib/redhat/nginx.conf [deleted file]
contrib/redhat/php-collection.conf [new file with mode: 0644]
contrib/redhat/sensors.conf [deleted file]
contrib/redhat/snmp.conf [deleted file]
contrib/upstart.collectd.conf [new file with mode: 0644]
src/Makefile.am
src/aggregation.c
src/amqp.c
src/apache.c
src/apcups.c
src/aquaero.c [new file with mode: 0644]
src/ascent.c
src/battery.c
src/bind.c
src/cgroups.c [new file with mode: 0644]
src/collectd-email.pod
src/collectd-exec.pod
src/collectd-java.pod
src/collectd-nagios.pod
src/collectd-perl.pod
src/collectd-python.pod
src/collectd-snmp.pod
src/collectd-tg.pod [new file with mode: 0644]
src/collectd-threshold.pod
src/collectd-unixsock.pod
src/collectd.c
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.h
src/collectd.pod
src/collectdctl.pod
src/collectdmon.pod
src/common.c
src/common.h
src/configfile.c
src/configfile.h
src/conntrack.c
src/cpu.c
src/cpython.h
src/csv.c
src/curl.c
src/curl_json.c
src/curl_xml.c
src/dbi.c
src/df.c
src/disk.c
src/dns.c
src/ethstat.c
src/exec.c
src/filter_chain.c
src/interface.c
src/java.c
src/libcollectdclient/Makefile.am
src/libcollectdclient/collectd/network.h
src/libcollectdclient/network.c
src/libcollectdclient/network_buffer.c
src/libvirt.c
src/load.c
src/log_logstash.c [new file with mode: 0644]
src/logfile.c
src/lvm.c [new file with mode: 0644]
src/memcached.c
src/memory.c
src/mic.c [new file with mode: 0644]
src/modbus.c
src/mysql.c
src/netapp.c
src/netlink.c
src/network.c
src/nfs.c
src/nginx.c
src/notify_email.c
src/onewire.c
src/pf.c
src/pinba.c
src/plugin.c
src/plugin.h
src/postgresql.c
src/redis.c
src/riemann.proto [new file with mode: 0644]
src/rrdcached.c
src/rrdtool.c
src/sigrok.c [new file with mode: 0644]
src/snmp.c
src/statsd.c [new file with mode: 0644]
src/swap.c
src/syslog.c
src/tail.c
src/tail_csv.c [new file with mode: 0644]
src/target_replace.c
src/target_set.c
src/tcpconns.c
src/teamspeak2.c
src/tests/common_test.c [new file with mode: 0644]
src/tests/macros.h [new file with mode: 0644]
src/tests/mock/plugin.c [new file with mode: 0644]
src/tests/mock/utils_cache.c [new file with mode: 0644]
src/tests/mock/utils_time.c [new file with mode: 0644]
src/tests/test_common.c [new file with mode: 0644]
src/tests/test_utils_avltree.c [new file with mode: 0644]
src/tests/test_utils_heap.c [new file with mode: 0644]
src/tests/test_utils_mount.c [new file with mode: 0644]
src/tests/test_utils_vl_lookup.c [new file with mode: 0644]
src/thermal.c
src/threshold.c
src/types.db
src/types.db.pod
src/unixsock.c
src/uptime.c
src/utils_cache.c
src/utils_cmd_flush.c
src/utils_cmd_getthreshold.c
src/utils_cmd_putnotif.c
src/utils_complain.c
src/utils_complain.h
src/utils_crc32.c [new file with mode: 0644]
src/utils_crc32.h [new file with mode: 0644]
src/utils_dns.c
src/utils_fbhash.c
src/utils_format_graphite.c
src/utils_format_graphite.h
src/utils_latency.c [new file with mode: 0644]
src/utils_latency.h [new file with mode: 0644]
src/utils_mount.c
src/utils_mount.h
src/utils_parse_option.c
src/utils_random.c [new file with mode: 0644]
src/utils_random.h [new file with mode: 0644]
src/utils_rrdcreate.c
src/utils_rrdcreate.h
src/utils_tail.c
src/utils_tail_match.c
src/utils_tail_match.h
src/utils_threshold.c [new file with mode: 0644]
src/utils_threshold.h [new file with mode: 0644]
src/utils_vl_lookup.c
src/utils_vl_lookup.h
src/utils_vl_lookup_test.c [deleted file]
src/uuid.c
src/varnish.c
src/write_graphite.c
src/write_http.c
src/write_kafka.c [new file with mode: 0644]
src/write_mongodb.c
src/write_riemann.c [new file with mode: 0644]
src/write_riemann_threshold.c [new file with mode: 0644]
src/zfs_arc.c
version-gen.sh

index 75cb307..e1120d3 100644 (file)
@@ -33,11 +33,14 @@ src/stamp-h1
 .libs/
 src/collectd
 src/collectd-nagios
+src/collectd-tg
 src/collectdctl
 src/collectdmon
 src/*.1
 src/*.5
-src/libcollectdclient/lcc_features.h
+src/.pod2man.tmp.*
+src/libcollectdclient/collectd/lcc_features.h
+src/utils_vl_lookup_test
 
 # patch stuff
 *.rej
@@ -49,6 +52,9 @@ src/liboconfig/parser.c
 src/liboconfig/parser.h
 src/liboconfig/scanner.c
 
+# protobuf stuff:
+src/*.pb-c.[ch]
+
 # make dist stuff:
 /collectd-*.tar.gz
 /collectd-*.tar.bz2
@@ -58,6 +64,7 @@ bindings/.perl-directory-stamp
 bindings/perl/Collectd/pm_to_blib
 bindings/perl/blib/
 bindings/perl/pm_to_blib
+bindings/buildperl
 
 # java stuff
 bindings/java/java-build-stamp
diff --git a/AUTHORS b/AUTHORS
index 78dbad1..38d20b9 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -21,6 +21,9 @@ Akkarit Sangpetch <asangpet at andrew.cmu.edu>
 Alessandro Iurlano <alessandro.iurlano at gmail.com>
  - Initial filecount plugin.
 
+Alex Deymo <deymo at chromium.org>
+ - aquaero plugin.
+
 Alvaro Barcellos <alvaro.barcellos at gmail.com>
  - Don't-fork patch.
 
@@ -28,6 +31,9 @@ Amit Gupta <amit.gupta221 at gmail.com>
  - Multiple servers in the apache plugin.
  - curl_xml plugin.
 
+Andreas Henriksson <andreas at fatal.se>
+ - libmnl support in the netlink plugin.
+
 Anthony Dewhurst <dewhurst at gmail.com>
  - zfs_arc plugin.
 
@@ -41,12 +47,18 @@ Aurélien Reynaud <collectd at wattapower.net>
  - LPAR plugin.
  - Various fixes for AIX, HP-UX and Solaris.
 
+Bert Vermeulen <bert at biot.com>
+ - sigrok plugin
+
 Bruno Prémont <bonbons at linux-vserver.org>
  - BIND plugin.
  - Many bugreports and -fixes in various plugins,
    especially a nasty bug in the network plugin.
  - Wireshark dissector.
 
+Chad Malfait <malfaitc at yahoo.com>
+ - LVM plugin.
+
 Chris Lundquist <clundquist at bluebox.net>
  - Improvements to the write_mongodb plugin.
 
@@ -73,6 +85,9 @@ Doug MacEachern <dougm at hyperic.com>
 Edward “Koko” Konetzko <konetzed at quixoticagony.com>
  - fscache plugin.
 
+Evan Felix <evan.felix at pnnl.gov>
+ - mic plugin.
+
 Fabian Linzberger <e at lefant.net>
  - Percentage aggregation for `collectd-nagios'.
 
@@ -88,9 +103,15 @@ Franck Lombardi
 Jason Pepas <cell at ices.utexas.edu>
  - nfs plugin.
 
+J. Javier Maestro <jjmaestro at ieee.org>
+ - Write-Graphite plugin: UDP support and LogSendErrors flag.
+
 Jérôme Renard <jerome.renard at gmail.com>
  - varnish plugin.
 
+Kris Nielander <nielander at fox-it.com>
+ - tail_csv plugin.
+
 Luboš Staněk <kolektor at atlas.cz>
  - sensors plugin improvements.
  - Time and effort to find a nasty bug in the ntpd-plugin.
@@ -113,6 +134,11 @@ Manuel Sanmartin
    + swap
  - Various AIX-related fixes and hacks.
 
+Marc Fournier <marc.fournier at camptocamp.com>
+ - Various fixes to the varnish plugin.
+ - RPM specfile update.
+ - libmnl support in the netlink plugin.
+
 Marco Chiappero <marco at absence.it>
  - uptime plugin.
  - ip6tables support in the iptables plugin.
@@ -123,6 +149,7 @@ Michael Hanselmann <public at hansmi.ch>
 
 Michael Stapelberg <michael+git at stapelberg.de>
  - OpenBSD port of the tcpconns plugin.
+ - cgroups plugin.
 
 Michał Mirosław <mirq-linux at rere.qmqm.pl>
  - thermal plugin.
@@ -165,6 +192,14 @@ Peter Holik <peter at holik.at>
 Phoenix Kayo <kayo.k11.4 at gmail.com>
  - pinba plugin.
 
+Pierre-Yves Ritschard <pyr at spootnik.org>
+ - Write-Riemann plugin.
+ - Write-Graphite plugin: Notification support.
+ - Write-Kafka plugin.
+ - Log-Logstash plugin.
+ - Normalization in the CPU plugin.
+ - Relative values in the Load plugin.
+
 Piotr Hosowicz <the55 at wp.pl>
  - SMF manifest for collectd.
 
@@ -212,6 +247,9 @@ Tommie Gannert <d00-tga at d.kth.se>
 Vincent Stehlé <vincent.stehle at free.fr>
  - hddtemp plugin.
 
+Xin Li <delphij at freebsd.org>
+ - FreeBSD port of the ZFS-ARC plugin.
+
 collectd is available at:
   <http://collectd.org/>
 
index d7dcbdf..8cd1579 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,172 @@
+2013-08-18, Version 5.4.0
+       * collectd: The "LoadPlugin" config option no longer attempts to load
+         plugins twice. If more than one "LoadPlugin" statement or block is
+         encountered, only the first will have any effect.
+       * collectd: The "AutoLoadPlugin" option allows to automatically load
+         plugins for which a configuration is found.
+       * collectd: The "WriteQueueLimitHigh" and "WriteQueueLimitLow" options
+         allow collectd to drop values when under stress, to avoid running out
+         of memory. Thanks to Yves Mettier for his patch.
+       * amqp plugin: The "GraphiteSeparateInstances" and
+         "GraphiteAlwaysAppendDS" options have been added. Thanks to Laurent
+         for the patch.
+       * aquaero plugin: This new plugin reads various metrics, e.g. fan
+         speeds and temperatures, from Aquaero 5, a fan and water cooling
+         control panel. Thanks to Alex Deymo for his patch.
+       * curl plugin: The "MeasureResponseCode" option has been added. Thanks
+         to Jan Matějka for his patch.
+       * curl_json plugin: Support for UNIX domain sockets and array wildcards
+         has been added. Thanks to Jim Radford for his patch.
+       * curl_xml plugin: Support for long URLs has been improved.
+       * cgroups plugin: This new plugin collects CPU accounting information
+         for processes in a cgroup. Thanks to Michael Stapelberg for his patch.
+       * df plugin: The "ValuesAbsolute" and "ValuesPercentage" options have
+         been added. Thanks to Vedran Bartonicek for the patch.
+       * exec plugin: Do UID / GID lookups before forking. This should prevent
+         a race condition in the NSS library. Thanks to Ceri Storey for the
+         patch.
+       * lvm plugin: This new plugin collects size information from Linux'
+         Logical Volume Manager (LVM). Thanks to Chad Malfait for his work.
+       * memcached plugin: Support for increment and decrement counts has been
+         added. Thanks to Blake Matheny for the patch.
+       * mic plugin: This new plugin collects CPU and memory usage, power
+         consumption and temperatures of Intel's Many-Integrated-Core (MIC)
+         architecture, such as Xeon Phi cards. Thanks to Evan Felix for his
+         work.
+       * netlink plugin: This plugin has been converted to use the supported
+         "libmnl" library. Thanks to Andreas Henriksson for his patch.
+       * nginx plugin: Collection of accepted and handled connections has been
+         added. Thanks to Patrick Shan for his patch.
+       * sigrok plugin: This new plugin collects metrics from sigrok, a signal
+         processing framework reading various hardware devices, from light
+         meters to spectrum analyzers. Thanks to Bert Vermeulen for his patch.
+       * statsd plugin: This new plugin listens to a UDP socket and reads
+         metrics in the StatsD format.
+       * varnish plugin: Many additional metrics have been added. Thanks to
+         Nick Stenning for his patch.
+       * write_graphite plugin: Support for "UDP" has been added. Thanks to
+         Javier Maestro for his patch.
+       * write_riemann plugin: The "TTLFactor" option has been added.
+       * zfs_arc plugin: Support for FreeBSD has been added. Thanks to Xin Li
+         for his patch.
+
+2013-07-13, Version 5.3.1
+       * Documentation: Various fixes.
+       * Configuration: Fix error handling: Errors in included files were
+         ignored, causing configuration mistakes to go unnoticed.
+       * dns plugin: Don't abort when PCAP returns an error.
+       * modbus plugin: The reconnection strategy was improved, fixing a
+         segfault in the libmodbud library. Thanks to Stefan Nickl and
+         Fabien Wernli for their patches.
+       * mysql plugin: The notification about a newly running MySQL slave
+         thread has been fixed. Thanks to Joaquín Cuenca Abela for the patch.
+       * snmp plugin: A build issue has been fixed (C99 mixed declaration).
+         The end-of-tree check has been improved by Pierre-Yves Ritschard.
+       * threshold plugin: Handling of the "Interesting" configuration option
+         has been fixed. Thanks to Björn for the patch.
+       * write_riemann plugin: A memory leak has been fixed. Thanks to Dave
+         Cottlehuber for reporting it.
+
+2013-04-09, Version 5.3.0
+       * collectd: The "Include" statements can now be limited to include
+         only matching files in a directory. Thanks to Sebastian Harl for his
+         patch.
+       * collectd: Dispatches / writes are now handled by a thread pool. This
+         improves reliability and throughput for instances configured to act
+         as a "server". Thanks to Sebastian Harl and Dan Fandrich for
+         reviewing this change and fixing bugs.
+       * aggregation plugin: Selection of value lists is now possible using
+         regular expressions. Parts of the identifier of the resulting metric
+         can now be set via the configuration file.
+       * apcups plugin: The "ReportSeconds" option has been implemented.
+       * curl* plugins: Support for POST requests and custom request headers
+         has been added. Thanks to Dan Fandrich for his patch.
+       * curl_xml plugin: Support for XML namespaces has been added. Thanks
+         to Dan Fandrich for his patch.
+       * dbi plugin: Support for numeric options has been added. The
+         "Host" option has been added. Thanks to Daniel Hilst for his patch.
+       * disk plugin: Support for systems with >256 has been fixed. Thanks to
+         Greg Mason for his patch.
+       * libvirt plugin: Support for memory allocation has been added. Thanks
+         to Johan Wirén for his patch.
+       * netapp plugin: Support for "SnapVault", "VFiler" and deduplication /
+         compression and quota metrics. Thanks to Sebastian Harl for his
+         patches and teamix GmbH for sponsoring this work.
+       * postgresql plugin: The reconnection logic has been improved. Thanks
+         to Sebastian Harl for his patches.
+       * rrdtool, rrdcached plugins: The "CreateFilesAsync" option has been
+         implemented. When enabled, new RRD files will be created
+         asynchronously, which improved throughput of "server" instances.
+         Many thanks to Yves Mettier for all his input and code.
+       * tail_csv plugin: This new plugins allows to read metrics from CSV
+         files, such as Snort's statistics file. Thanks to Kris Nielander for
+         his patch.
+       * write_mongodb plugin: Authentication options have been added.
+       * write_riemann plugin: This new plugin allows sending metrics to
+         Riemann, a stream processing and alerting tool. Big thanks to
+         Pierre-Yves Ritschard for his work.
+
+2013-04-08, Version 5.2.2
+       * Build system: A bad interaction between the Java detection code and
+         libltdl has been fixed. Thanks to Dave Cottlehuber for his patch.
+         Installation of the Perl bindings has been improved / fixed. Thanks
+         to Sebastian Harl for his patch.
+       * collectd: Fixed read callback scheduling at startup.
+       * apache, ascent, bind, curl, curl_json, curl_xml, nginx and
+         write_http plugins: Portability fixes, protection from infinite
+         redirect loops, improved error handling and incorrect dereferences
+         have been fixed. Most of these are related to the cURL library.
+         Thanks to Dan Fandrich for his patches.
+       * logfile plugin: Flush the output file handle. This works around
+         caching when logging to STDOUT and redirecting into a file. Thanks
+         to Nathan Huff for the patch.
+       * mysql plugin: Fix a memory leak in the error handling. Thanks to
+         Tomas Doran for his patch.
+       * netapp plugin: Fix the interval with which values are dispatched.
+       * network plugin: Build issues under FreeBSD and initialization have
+         been fixed. Thanks to Ed Schouten for his patch.
+       * nfs plugin: A compilation problem has been fixed.
+       * notify_email plugin: Add a character set to the mail header. Thanks
+         to Manuel Cissé for his patch.
+       * pf plugin: Build issues have been fixed.
+       * postgresql plugin: Build issues have been fixed.
+       * rrdcached plugin: Connect to the daemon from the read callback.
+       * snmp plugin: Matching of SNMP subtrees has been improved. Thanks to
+         "jkrabbe" for the patch.
+       * thermal plugin: The initialization of dispatched value lists has
+         been fixed. Thanks to Markus Knetschke for his patch.
+       * unixsock plugin: Parsing of options with an underscore, e.g.
+         "plugin_instance" has been fixed. Thanks to Tommie Gannert for his
+         patch.
+
+2013-01-27, Version 5.2.1
+       * Build system: "make distcheck" has been fixed. Build fixes Solaris
+         and systems without gcrypt. Thanks to Yves Mettier for his patches.
+       * collectd: The complaint mechanism was fixed. It reported messages
+         more frequently than intended.
+       * collectd-tg: A manual page has been added.
+       * dns plugin: Build issues on FreeBSD have been fixed. Thanks to
+         Ed Schouten for his patch.
+       * ethstat plugin: Fix the "Map" config option. An incorrectly used
+         character pointer may lead to a segmentation fault.
+       * network plugin: Build issues on FreeBSD have been fixed. Thanks to
+         Ed Schouten for his patch.
+       * postgresql plugin: A memory leak in the writing code has been fixed.
+         A use-after-free issue that happened when more than one database was
+         configured was fixed. Thanks to Sebastian Harl for fixing these
+         problems.
+       * redis plugin: A build failure has been fixed. Thanks to Pierre-Yves
+         Ritschard for his patch.
+       * varnish plugin: Fix a problem with instances without name.
+       * write_graphite plugin: A regression which rendered the
+         "SeparateInstances" and "AlwaysAppendDS" options unusable has been
+         fixed. A failed assertion when using types with many data sources
+         has been fixed. Improve reporting of connection errors to not spam
+         log files too much. Thanks to Pierre-Yves Ritschard for reporting
+         the logging problem.
+       * zfs_arc plugin: Fix the type used for mutex misses. Thanks to Yves
+         Mettier for reporting this bug.
+
 2012-11-17, Version 5.2.0
        * collectd: The performance of the LISTVAL command has been improved.
          Thanks to Yves Mettier for the patch.
        * tcpconns plugin: Use a netlink socket rather than reading from /proc
          for improved performance. Thanks to Michael Stapelberg for the patch.
 
+2013-04-08, Version 5.1.3
+       * Build system: A bad interaction between the Java detection code and
+         libltdl has been fixed. Thanks to Dave Cottlehuber for his patch.
+       * collectd: Fixed read callback scheduling at startup.
+       * apache, ascent, bind, curl, curl_json, curl_xml, nginx and
+         write_http plugins: Portability fixes, protection from infinite
+         redirect loops, improved error handling and incorrect dereferences
+         have been fixed. Most of these are related to the cURL library.
+         Thanks to Dan Fandrich for his patches.
+       * logfile plugin: Flush the output file handle. This works around
+         caching when logging to STDOUT and redirecting into a file. Thanks
+         to Nathan Huff for the patch.
+       * mysql plugin: Fix a memory leak in the error handling. Thanks to
+         Tomas Doran for his patch.
+       * netapp plugin: Fix the interval with which values are dispatched.
+       * network plugin: Build issues under FreeBSD and initialization have
+         been fixed. Thanks to Ed Schouten for his patch.
+       * nfs plugin: A compilation problem has been fixed.
+       * notify_email plugin: Add a character set to the mail header. Thanks
+         to Manuel Cissé for his patch.
+       * rrdcached plugin: Connect to the daemon from the read callback.
+       * snmp plugin: Matching of SNMP subtrees has been improved. Thanks to
+         "jkrabbe" for the patch.
+       * thermal plugin: The initialization of dispatched value lists has
+         been fixed. Thanks to Markus Knetschke for his patch.
+       * unixsock plugin: Parsing of options with an underscore, e.g.
+         "plugin_instance" has been fixed. Thanks to Tommie Gannert for his
+         patch.
+
+2013-01-25, Version 5.1.2
+       * Build system: "make distcheck" has been fixed. Thanks to Yves
+         Mettier for his patches.
+       * collectd: The complaint mechanism was fixed. It reported messages
+         more frequently than intended.
+       * dns plugin: Build issues on FreeBSD have been fixed. Thanks to
+         Ed Schouten for his patch.
+       * ethstat plugin: Fix the "Map" config option. An incorrectly used
+         character pointer may lead to a segmentation fault.
+       * network plugin: Build issues on FreeBSD have been fixed. Thanks to
+         Ed Schouten for his patch.
+       * varnish plugin: Fix a problem with instances without name.
+       * write_graphite: Improve reporting of connection errors to not spam
+         log files too much. Thanks to Pierre-Yves Ritschard for reporting
+         this problem.
+       * zfs_arc plugin: Fix the type used for mutex misses. Thanks to Yves
+         Mettier for reporting this bug.
+
 2012-11-11, Version 5.1.1
        * collectd: Create new directories with mode 0777 and let umask remove
          unwanted permission bits.
        * v5upgrade target: Target for converting v4 data sets to the v5
          schema.
 
+2013-04-07, Version 4.10.9
+       * Build system: A bad interaction between the Java detection code and
+         libltdl has been fixed. Thanks to Dave Cottlehuber for his patch.
+       * apache, ascent, bind, curl, curl_json, curl_xml, nginx and
+         write_http plugins: Portability fixes, protection from infinite
+         redirect loops, improved error handling and incorrect dereferences
+         have been fixed. Most of these are related to the cURL library.
+         Thanks to Dan Fandrich for his patches.
+       * logfile plugin: Flush the output file handle. This works around
+         caching when logging to STDOUT and redirecting into a file. Thanks
+         to Nathan Huff for the patch.
+       * network plugin: Build issues under FreeBSD and initialization have
+         been fixed. Thanks to Ed Schouten for his patch.
+       * mysql plugin: Fix a memory leak in the error handling. Thanks to
+         Tomas Doran for his patch.
+       * thermal plugin: The initialization of dispatched value lists has
+         been fixed. Thanks to Markus Knetschke for his patch.
+       * unixsock plugin: Parsing of options with an underscore, e.g.
+         "plugin_instance" has been fixed. Thanks to Tommie Gannert for his
+         patch.
+
 2012-11-11, Version 4.10.8
        * collectd: Create new directories with mode 0777 and let umask remove
          unwanted permission bits.
 
 2008-07-15, Version 4.4.2
        * build system: Use pkg-config to detect the upsclient library.
-       * collectd: Try even harder to determine the endianess of the
+       * collectd: Try even harder to determine the endianness of the
          architecture collectd is being built on.
        * disk plugin: Fix for Linux 2.4: A wrong field was used as the name
          of disks.
 2008-08-30, Version 4.3.4
        * Build system: Improved detection of and linking with the statgrab
          library.
-       * collectd: Portability fixes, especially to determine endianess more
+       * collectd: Portability fixes, especially to determine endianness more
          reliable.
        * Various plugins: Fix format strings.
        * disk plugin: A fix for giving disks under Linux 2.4 the right names
diff --git a/README b/README
index 67cac04..66408a1 100644 (file)
--- a/README
+++ b/README
@@ -26,6 +26,9 @@ Features
       Sensors in Macs running Mac OS X / Darwin: Temperature, fanspeed and
       voltage sensors.
 
+    - aquaero
+      Various sensors in the Aquaero 5 watercooling board made by Aquacomputer.
+
     - ascent
       Statistics about Ascent, a free server for the game `World of Warcraft'.
 
@@ -37,6 +40,9 @@ Features
       Name server and resolver statistics from the `statistics-channel'
       interface of BIND 9.5, 9,6 and later.
 
+    - cgroups
+      CPU accounting information for process groups under Linux.
+
     - conntrack
       Number of nf_conntrack entries.
 
@@ -133,7 +139,11 @@ Features
       technique built into IBM's POWER processors.
 
     - libvirt
-      CPU, disk and network I/O statistics from virtual machines.
+      CPU, memory, disk and network I/O statistics from virtual machines.
+
+    - lvm
+      Size of “Logical Volumes” (LV) and “Volume Groups” (VG) of Linux'
+      “Logical Volume Manager” (LVM).
 
     - madwifi
       Queries very detailed usage statistics from wireless LAN adapters and
@@ -158,6 +168,10 @@ Features
       Memory utilization: Memory occupied by running processes, page cache,
       buffer cache and free.
 
+    - mic
+      Collects CPU usage, memory usage, temperatures and power consumption from
+      Intel Many Integrated Core (MIC) CPUs.
+
     - modbus
       Reads values from Modbus/TCP enabled devices. Supports reading values
       from multiple "slaves" so gateway devices can be used.
@@ -222,6 +236,9 @@ Features
       write your own plugins in Perl and return arbitrary values using this
       API. See collectd-perl(5).
 
+    - pf
+      Query statistics from BSD's packet filter "pf".
+
     - pinba
       Receive and dispatch timing values from Pinba, a profiling extension for
       PHP.
@@ -266,6 +283,11 @@ Features
     - serial
       RX and TX of serial interfaces. Linux only; needs root privileges.
 
+    - sigrok
+      Uses libsigrok as a backend, allowing any sigrok-supported device
+      to have its measurements fed to collectd. This includes multimeters,
+      sound level meters, thermometers, and much more.
+
     - snmp
       Read values from SNMP (Simple Network Management Protocol) enabled
       network devices such as switches, routers, thermometers, rack monitoring
@@ -281,6 +303,10 @@ Features
       Follows (tails) logfiles, parses them by lines and submits matched
       values.
 
+    - tail_csv
+      Follows (tails) files in CSV format, parses each line and submits
+      extracted values.
+
     - tape
       Bytes and operations read and written on tape devices. Solaris only.
 
@@ -369,16 +395,27 @@ Features
       done.
 
     - write_graphite
-      Sends data to Carbon, the storage layer of Graphite.
+      Sends data to Carbon, the storage layer of Graphite using TCP or UDP. It
+      can be configured to avoid logging send errors (especially useful when
+      using UDP).
 
     - write_http
       Sends the values collected by collectd to a web-server using HTTP POST
       requests. The transmitted data is either in a form understood by the
       Exec plugin or formatted in JSON.
 
+    - write_kafka
+      Sends data to Apache Kafka, a distributed queue.
+
+    - write_mongodb
+      Sends data to MongoDB, a NoSQL database.
+
     - write_redis
       Sends the values to a Redis key-value database server.
 
+    - write_riemann
+      Sends data to Riemann, a stream processing and monitoring system.
+
   * Logging is, as everything in collectd, provided by plugins. The following
     plugins keep up informed about what's going on:
 
@@ -458,6 +495,10 @@ Features
 
   * Miscellaneous plugins:
 
+    - aggregation
+      Selects multiple value lists based on patterns or regular expressions
+      and creates new aggregated values lists from those.
+
     - threshold
       Checks values against configured thresholds and creates notifications if
       values are out of bounds. See collectd-threshold(5) for details.
@@ -527,10 +568,18 @@ Prerequisites
 
   * A POSIX-threads (pthread) implementation.
     Since gathering some statistics is slow (network connections, slow devices,
-    etc) the collectd is parallelized. The POSIX threads interface is being
+    etc) collectd is parallelized. The POSIX threads interface is being
     used and should be found in various implementations for hopefully all
     platforms.
 
+  * aerotools-ng (optional)
+    Used by the `aquaero' plugin. Currently, the `libaquaero5' library, which
+    is used by the `aerotools-ng' toolkit, is not compiled as a shared object
+    nor does it feature an installation routine. Therefore, you need to point
+    collectd's configure script at the source directory of the `aerotools-ng'
+    project.
+    <https://github.com/lynix/aerotools-ng>
+
   * CoreFoundation.framework and IOKit.framework (optional)
     For compiling on Darwin in general and the `apple_sensors' plugin in
     particular.
@@ -588,6 +637,10 @@ Prerequisites
     Used by the `memcachec' plugin to connect to a memcache daemon.
     <http://tangent.org/552/libmemcached.html>
 
+  * libmnl (optional)
+    Used by the `netlink' plugin.
+    <http://www.netfilter.org/projects/libmnl/>
+
   * libmodbus (optional)
     Used by the “modbus” plugin to communicate with Modbus/TCP devices. The
     “modbus” plugin works with version 2.0.3 of the library – due to frequent
@@ -602,10 +655,6 @@ Prerequisites
     Required for the “netapp” plugin.
     This library is part of the “Manage ONTAP SDK” published by NetApp.
 
-  * libnetlink (optional)
-    Used, obviously, for the `netlink' plugin.
-    <http://www.linuxfoundation.org/en/Net:Iproute2>
-
   * libnetsnmp (optional)
     For the `snmp' plugin.
     <http://www.net-snmp.org/>
@@ -653,6 +702,10 @@ Prerequisites
     Used by the AMQP plugin for AMQP connections, for example to RabbitMQ.
     <http://hg.rabbitmq.com/rabbitmq-c/>
 
+  * librdkafka (optional; also called “rdkafka”)
+    Used by the Kafka plugin for producing.
+    <https://github.com/edenhill/librdkafka>
+
   * librouteros (optional)
     Used by the `routeros' plugin to connect to a device running `RouterOS'.
     <http://verplant.org/librouteros/>
@@ -671,6 +724,11 @@ Prerequisites
     To read from `lm_sensors', see the `sensors' plugin.
     <http://www.lm-sensors.org/>
 
+  * libsigrok (optional)
+    Used by the sigrok plugin. In addition, libsigrok depends on glib,
+    libzip, and optionally (depending on which drivers are enabled) on
+    libusb, libftdi and libudev.
+
   * libstatgrab (optional)
     Used by various plugins to collect statistics on systems other than Linux
     and/or Solaris.
index 3876cc2..07373e8 100644 (file)
@@ -5,6 +5,7 @@ SUBDIRS += java
 endif
 
 EXTRA_DIST = perl/Makefile.PL \
+            perl/uninstall_mod.pl \
             perl/lib/Collectd.pm \
             perl/lib/Collectd/Unixsock.pm \
             perl/lib/Collectd/Plugins/Monitorus.pm \
@@ -33,12 +34,8 @@ install-exec-local:
 # 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
+       @PERL@ -I$(DESTDIR)$(prefix) $(srcdir)/perl/uninstall_mod.pl Collectd
+       find $(DESTDIR)$(prefix) -name "perllocal.pod" -exec rm {} \;
 
 clean-local:
        rm -rf buildperl
@@ -48,7 +45,13 @@ perl: buildperl/Makefile
 
 buildperl/Makefile: .perl-directory-stamp buildperl/Makefile.PL \
        $(top_builddir)/config.status
-       cd buildperl && @PERL@ Makefile.PL INSTALL_BASE=$(prefix) @PERL_BINDINGS_OPTIONS@
+       @# beautify the output a bit
+       @echo 'cd buildperl && @PERL@ Makefile.PL @PERL_BINDINGS_OPTIONS@'
+       @cd buildperl && ( if ! @PERL@ Makefile.PL @PERL_BINDINGS_OPTIONS@; then \
+                       echo ""; \
+                       echo 'Check whether you have set $$PERL_MM_OPT in your environment and try using ./configure --with-perl-bindings=""'; \
+                       echo ""; \
+               fi )
 
 buildperl/Makefile.PL: .perl-directory-stamp $(top_builddir)/config.status
 
index 7fad08f..99b140a 100644 (file)
@@ -118,6 +118,7 @@ private void connect () /* {{{ */
 
     environment = new HashMap ();
     environment.put (JMXConnector.CREDENTIALS, credentials);
+    environment.put(JMXConnectorFactory.PROTOCOL_PROVIDER_CLASS_LOADER, this.getClass().getClassLoader());
   }
 
   try
diff --git a/bindings/perl/uninstall_mod.pl b/bindings/perl/uninstall_mod.pl
new file mode 100644 (file)
index 0000000..f8083af
--- /dev/null
@@ -0,0 +1,8 @@
+#! /usr/bin/perl
+
+use strict;
+use ExtUtils::Installed;
+
+my $mod = $ARGV[0] || die "Usage : $0 Module\n";
+my $inst = ExtUtils::Installed->new();
+unlink $inst->files($mod), $inst->packlist($mod)->packlist_file();
index 098669d..46e8c29 100755 (executable)
--- a/clean.sh
+++ b/clean.sh
@@ -21,6 +21,7 @@ true \
 && rm -f Makefile \
 && rm -f Makefile.in \
 && rm -f missing \
+&& rm -f INSTALL \
 && rm -f -r src/.deps \
 && rm -f -r src/.libs \
 && rm -f src/*.o \
@@ -28,6 +29,11 @@ true \
 && rm -f src/*.lo \
 && rm -f src/collectd \
 && rm -f src/collectd.1 \
+&& rm -f src/collectd.conf \
+&& rm -f src/collectdctl \
+&& rm -f src/collectd-tg \
+&& rm -f src/collectd-nagios \
+&& rm -f src/collectdmon \
 && rm -f src/config.h \
 && rm -f src/config.h.in \
 && rm -f src/config.h.in~ \
@@ -35,6 +41,18 @@ true \
 && rm -f src/Makefile.in \
 && rm -f src/stamp-h1 \
 && rm -f src/stamp-h1.in \
+&& rm -f src/*.pb-c.c \
+&& rm -f src/*.pb-c.h \
+&& rm -f src/Makefile.in \
+&& rm -f src/liboconfig/*.o \
+&& rm -f src/liboconfig/*.la \
+&& rm -f src/liboconfig/*.lo \
+&& rm -f -r src/liboconfig/.libs \
+&& rm -f -r src/liboconfig/Makefile \
+&& rm -f -r src/liboconfig/Makefile.in \
+&& rm -f -r src/liboconfig/parser.c \
+&& rm -f -r src/liboconfig/parser.h \
+&& rm -f -r src/liboconfig/scanner.c \
 && rm -f -r src/libping/.libs \
 && rm -f src/libping/*.o \
 && rm -f src/libping/*.la \
@@ -47,4 +65,18 @@ true \
 && rm -f -r src/libcollectdclient/.libs \
 && rm -f src/libcollectdclient/*.o \
 && rm -f src/libcollectdclient/*.la \
-&& rm -f src/libcollectdclient/*.lo
+&& rm -f src/libcollectdclient/*.lo \
+&& rm -f src/libcollectdclient/Makefile \
+&& rm -f src/libcollectdclient/Makefile.in \
+&& rm -f src/libcollectdclient/collectd/lcc_features.h \
+&& rm -f src/libcollectdclient/libcollectdclient.pc \
+&& rm -f bindings/Makefile \
+&& rm -f bindings/Makefile.in \
+&& rm -f -r bindings/java/.libs \
+&& rm -f bindings/java/Makefile \
+&& rm -f bindings/java/Makefile.in \
+&& rm -f bindings/java/java-build-stamp \
+&& rm -f bindings/java/org/collectd/api/*.class \
+&& rm -f bindings/java/org/collectd/java/*.class \
+&& rm -f bindings/.perl-directory-stamp \
+&& rm -f -r bindings/buildperl
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..1907b85
--- /dev/null
@@ -0,0 +1,5702 @@
+dnl Process this file with autoconf to produce a configure script.
+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])
+
+m4_ifdef([LT_PACKAGE_VERSION],
+       # libtool >= 2.2
+       [
+        LT_CONFIG_LTDL_DIR([libltdl])
+        LT_INIT([dlopen])
+        LTDL_INIT([convenience])
+        AC_DEFINE(LIBTOOL_VERSION, 2, [Define to used libtool version.])
+       ]
+,
+       # libtool <= 1.5
+       [
+        AC_LIBLTDL_CONVENIENCE
+        AC_SUBST(LTDLINCL)
+        AC_SUBST(LIBLTDL)
+        AC_LIBTOOL_DLOPEN
+        AC_CONFIG_SUBDIRS(libltdl)
+        AC_DEFINE(LIBTOOL_VERSION, 1, [Define to used libtool version.])
+       ]
+)
+
+AM_INIT_AUTOMAKE([tar-pax dist-bzip2 foreign])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AC_LANG(C)
+
+AC_PREFIX_DEFAULT("/opt/collectd")
+
+AC_SYS_LARGEFILE
+
+#
+# Checks for programs.
+#
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AM_PROG_CC_C_O
+AM_CONDITIONAL(COMPILER_IS_GCC, test "x$GCC" = "xyes")
+
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+AC_PROG_LEX
+AC_PROG_YACC
+PKG_PROG_PKG_CONFIG
+
+AC_CHECK_PROG([have_protoc_c], [protoc-c], [yes], [no])
+AC_CHECK_HEADERS([google/protobuf-c/protobuf-c.h],
+                 [have_protobuf_c_h="yes"],
+                 [have_protobuf_c_h="no"])
+if test "x$have_protoc_c" = "xyes" && test "x$have_protobuf_c_h" != "xyes"
+then
+       have_protoc_c="no (unable to find <google/protobuf-c/protobuf-c.h>)"
+fi
+AM_CONDITIONAL(HAVE_PROTOC_C, test "x$have_protoc_c" = "xyes")
+
+AC_MSG_CHECKING([for kernel type ($host_os)])
+case $host_os in
+       *linux*)
+       AC_DEFINE([KERNEL_LINUX], 1, [True if program is to be compiled for a Linux kernel])
+       ac_system="Linux"
+       ;;
+       *solaris*)
+       AC_DEFINE([KERNEL_SOLARIS], 1, [True if program is to be compiled for a Solaris kernel])
+       ac_system="Solaris"
+       ;;
+       *darwin*)
+       ac_system="Darwin"
+       ;;
+       *openbsd*)
+       ac_system="OpenBSD"
+       ;;
+       *aix*)
+       AC_DEFINE([KERNEL_AIX], 1, [True if program is to be compiled for a AIX kernel])
+       ac_system="AIX"
+       ;;
+       *freebsd*)
+       AC_DEFINE([KERNEL_FREEBSD], 1, [True if program is to be compiled for a FreeBSD kernel])
+       ac_system="FreeBSD"
+       ;;
+       *)
+       ac_system="unknown"
+esac
+AC_MSG_RESULT([$ac_system])
+
+if test "x$ac_system" = "xLinux"
+then
+       AC_ARG_VAR([KERNEL_DIR], [path to Linux kernel sources])
+       if test -z "$KERNEL_DIR"
+       then
+               KERNEL_DIR="/lib/modules/`uname -r`/source"
+       fi
+
+       KERNEL_CFLAGS="-I$KERNEL_DIR/include"
+       AC_SUBST(KERNEL_CFLAGS)
+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
+       AC_DEFINE(_THREAD_SAFE_ERRNO, 1, [Define to use the thread-safe version of errno under AIX.])
+fi
+
+# Where to install .pc files.
+pkgconfigdir="${libdir}/pkgconfig"
+AC_SUBST(pkgconfigdir)
+
+# Check for standards compliance mode
+AC_ARG_ENABLE(standards,
+             AS_HELP_STRING([--enable-standards], [Enable standards compliance mode]),
+             [enable_standards="$enableval"],
+             [enable_standards="no"])
+if test "x$enable_standards" = "xyes"
+then
+       AC_DEFINE(_ISOC99_SOURCE,        1, [Define to enforce ISO C99 compliance.])
+       AC_DEFINE(_POSIX_C_SOURCE, 200809L, [Define to enforce POSIX.1-2008 compliance.])
+       AC_DEFINE(_XOPEN_SOURCE,       700, [Define to enforce X/Open 7 (XSI) compliance.])
+       AC_DEFINE(_REENTRANT,            1, [Define to enable reentrancy interfaces.])
+       if test "x$GCC" = "xyes"
+       then
+               CFLAGS="$CFLAGS -std=c99"
+       fi
+fi
+AM_CONDITIONAL(BUILD_FEATURE_STANDARDS, test "x$enable_standards" = "xyes")
+
+#
+# Checks for header files.
+#
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_HEADER_DIRENT
+AC_HEADER_STDBOOL
+
+AC_CHECK_HEADERS(stdio.h errno.h math.h stdarg.h syslog.h fcntl.h signal.h assert.h sys/types.h sys/socket.h sys/select.h poll.h netdb.h arpa/inet.h sys/resource.h sys/param.h kstat.h regex.h sys/ioctl.h endian.h sys/isa_defs.h fnmatch.h libgen.h)
+
+# For ping library
+AC_CHECK_HEADERS(netinet/in_systm.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+])
+AC_CHECK_HEADERS(netinet/in.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+])
+AC_CHECK_HEADERS(netinet/ip.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+])
+AC_CHECK_HEADERS(netinet/ip_icmp.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP_H
+# include <netinet/ip.h>
+#endif
+])
+AC_CHECK_HEADERS(netinet/ip_var.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP_H
+# include <netinet/ip.h>
+#endif
+])
+AC_CHECK_HEADERS(netinet/ip6.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+])
+AC_CHECK_HEADERS(netinet/icmp6.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP6_H
+# include <netinet/ip6.h>
+#endif
+])
+AC_CHECK_HEADERS(netinet/tcp.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP_H
+# include <netinet/ip.h>
+#endif
+])
+AC_CHECK_HEADERS(netinet/udp.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP_H
+# include <netinet/ip.h>
+#endif
+])
+
+# For cpu modules
+AC_CHECK_HEADERS(sys/dkstat.h)
+if test "x$ac_system" = "xDarwin"
+then
+       AC_CHECK_HEADERS(mach/mach_init.h mach/host_priv.h mach/mach_error.h mach/mach_host.h mach/mach_port.h mach/mach_types.h mach/message.h mach/processor_set.h mach/processor.h mach/processor_info.h mach/task.h mach/thread_act.h mach/vm_region.h mach/vm_map.h mach/vm_prot.h mach/vm_statistics.h mach/kern_return.h)
+       AC_CHECK_HEADERS(CoreFoundation/CoreFoundation.h IOKit/IOKitLib.h IOKit/IOTypes.h IOKit/ps/IOPSKeys.h IOKit/IOBSD.h IOKit/storage/IOBlockStorageDriver.h)
+       # For the battery plugin
+       AC_CHECK_HEADERS(IOKit/ps/IOPowerSources.h, [], [],
+[
+#if HAVE_IOKIT_IOKITLIB_H
+#  include <IOKit/IOKitLib.h>
+#endif
+#if HAVE_IOKIT_IOTYPES_H
+#  include <IOKit/IOTypes.h>
+#endif
+])
+
+fi
+
+AC_CHECK_HEADERS(sys/sysctl.h, [], [],
+[
+#if HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+])
+
+AC_MSG_CHECKING([for sysctl kern.cp_times])
+if test -x /sbin/sysctl
+then
+       /sbin/sysctl kern.cp_times 2>/dev/null
+       if test $? -eq 0
+       then
+               AC_MSG_RESULT([yes])
+               AC_DEFINE(HAVE_SYSCTL_KERN_CP_TIMES, 1,
+               [Define if sysctl supports kern.cp_times])
+       else
+               AC_MSG_RESULT([no])
+       fi
+else
+       AC_MSG_RESULT([no])
+fi
+
+# For hddtemp module
+AC_CHECK_HEADERS(linux/major.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 swap module
+have_linux_wireless_h="no"
+if test "x$ac_system" = "xLinux"
+then
+  AC_CHECK_HEADERS(linux/wireless.h,
+                  [have_linux_wireless_h="yes"],
+                  [have_linux_wireless_h="no"],
+[
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+])
+fi
+
+# For the swap module
+have_sys_swap_h="yes"
+AC_CHECK_HEADERS(sys/swap.h vm/anon.h, [], [have_sys_swap_h="no"],
+[
+#undef _FILE_OFFSET_BITS
+#undef _LARGEFILE64_SOURCE
+#if HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+])
+
+if test "x$have_sys_swap_h$ac_system" = "xnoSolaris"
+then
+       hint_64=""
+       if test "x$GCC" = "xyes"
+       then
+               hint_64="CFLAGS='-m64'"
+       else
+               hint_64="CFLAGS='-xarch=v9'"
+       fi
+       AC_MSG_NOTICE([Solaris detected and sys/swap.h not usable. Try building a 64-bit binary ($hint_64 ./configure).])
+fi
+
+# For load module
+# For the processes plugin
+# For users module
+AC_CHECK_HEADERS(sys/loadavg.h linux/config.h utmp.h utmpx.h)
+
+# For interface plugin
+AC_CHECK_HEADERS(ifaddrs.h)
+AC_CHECK_HEADERS(net/if.h, [], [],
+[
+#if HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#  include <sys/socket.h>
+#endif
+])
+AC_CHECK_HEADERS(linux/if.h, [], [],
+[
+#if HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#  include <sys/socket.h>
+#endif
+])
+AC_CHECK_HEADERS(linux/netdevice.h, [], [],
+[
+#if HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#  include <sys/socket.h>
+#endif
+#if HAVE_LINUX_IF_H
+# include <linux/if.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"
+have_ip_vs_h="no"
+ip_vs_h_needs_kernel_cflags="no"
+if test "x$ac_system" = "xLinux"
+then
+       AC_CHECK_HEADERS(linux/ip_vs.h, [have_linux_ip_vs_h="yes"])
+       AC_CHECK_HEADERS(net/ip_vs.h, [have_net_ip_vs_h="yes"])
+       AC_CHECK_HEADERS(ip_vs.h, [have_ip_vs_h="yes"])
+
+       if test "x$have_linux_ip_vs_h$have_net_ip_vs_h$have_ip_vs_h" = "xnonono" && test -d "$KERNEL_DIR"
+       then
+               SAVE_CFLAGS="$CFLAGS"
+               CFLAGS="$CFLAGS $KERNEL_CFLAGS"
+
+               AC_MSG_NOTICE([Did not find ip_vs.h. Trying again using headers from $KERNEL_DIR.])
+
+               AC_CHECK_HEADERS(linux/ip_vs.h, [have_linux_ip_vs_h="yes"])
+               AC_CHECK_HEADERS(net/ip_vs.h, [have_net_ip_vs_h="yes"])
+               AC_CHECK_HEADERS(ip_vs.h, [have_ip_vs_h="yes"])
+
+               if test "x$have_linux_ip_vs_h" = "xyes" || test "x$have_net_ip_vs_h" = "xyes" || test "x$have_ip_vs_h" = "xyes"
+               then
+                       ip_vs_h_needs_kernel_cflags="yes"
+               fi
+
+               CFLAGS="$SAVE_CFLAGS"
+       fi
+fi
+AM_CONDITIONAL(IP_VS_H_NEEDS_KERNEL_CFLAGS, test "x$ip_vs_h_needs_kernel_cflags" = "xyes")
+
+# For quota module
+AC_CHECK_HEADERS(sys/ucred.h, [], [],
+[
+#if HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+])
+
+# For mount interface
+AC_CHECK_HEADERS(sys/mount.h, [], [],
+[
+#if HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+])
+
+# For the email plugin
+AC_CHECK_HEADERS(linux/un.h, [], [],
+[
+#if HAVE_SYS_SOCKET_H
+#      include <sys/socket.h>
+#endif
+])
+
+AC_CHECK_HEADERS(pwd.h grp.h sys/un.h ctype.h limits.h xfs/xqm.h fs_info.h fshelp.h paths.h mntent.h mnttab.h sys/fstyp.h sys/fs_types.h sys/mntent.h sys/mnttab.h sys/statfs.h sys/statvfs.h sys/vfs.h sys/vfstab.h kvm.h wordexp.h)
+
+# For the dns plugin
+AC_CHECK_HEADERS(arpa/nameser.h)
+AC_CHECK_HEADERS(arpa/nameser_compat.h, [], [],
+[
+#if HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#endif
+])
+
+AC_CHECK_HEADERS(net/if_arp.h, [], [],
+[#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+])
+AC_CHECK_HEADERS(net/ppp_defs.h)
+AC_CHECK_HEADERS(net/if_ppp.h, [], [],
+[#if HAVE_NET_PPP_DEFS_H
+# include <net/ppp_defs.h>
+#endif
+])
+AC_CHECK_HEADERS(netinet/if_ether.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+])
+
+AC_CHECK_HEADERS(netinet/ip_compat.h)
+
+have_net_pfvar_h="no"
+AC_CHECK_HEADERS(net/pfvar.h,
+               [have_net_pfvar_h="yes"],
+               [have_net_pfvar_h="no"],
+[
+#if HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+])
+
+# For the multimeter plugin
+have_termios_h="no"
+AC_CHECK_HEADERS(termios.h, [have_termios_h="yes"])
+
+#
+# Checks for typedefs, structures, and compiler characteristics.
+#
+AC_C_CONST
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_TYPE_UID_T
+AC_HEADER_TIME
+
+#
+# Checks for library functions.
+#
+AC_PROG_GCC_TRADITIONAL
+AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf setenv if_indextoname)
+
+AC_FUNC_STRERROR_R
+
+SAVE_CFLAGS="$CFLAGS"
+# Emulate behavior of src/Makefile.am
+if test "x$GCC" = "xyes"
+then
+       CFLAGS="$CFLAGS -Wall -Werror"
+fi
+
+AC_CACHE_CHECK([for strtok_r],
+  [c_cv_have_strtok_r_default],
+  AC_LINK_IFELSE(
+    [AC_LANG_PROGRAM(
+[[[
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+]]],
+[[[
+      char buffer[] = "foo,bar,baz";
+      char *token;
+      char *dummy;
+      char *saveptr;
+
+      dummy = buffer;
+      saveptr = NULL;
+      while ((token = strtok_r (dummy, ",", &saveptr)) != NULL)
+      {
+        dummy = NULL;
+        printf ("token = %s;\n", token);
+      }
+]]]
+    )],
+    [c_cv_have_strtok_r_default="yes"],
+    [c_cv_have_strtok_r_default="no"]
+  )
+)
+
+if test "x$c_cv_have_strtok_r_default" = "xno"
+then
+  CFLAGS="$CFLAGS -D_REENTRANT=1"
+
+  AC_CACHE_CHECK([if strtok_r needs _REENTRANT],
+    [c_cv_have_strtok_r_reentrant],
+    AC_LINK_IFELSE(
+      [AC_LANG_PROGRAM(
+[[[
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+]]],
+[[[
+        char buffer[] = "foo,bar,baz";
+        char *token;
+        char *dummy;
+        char *saveptr;
+
+        dummy = buffer;
+        saveptr = NULL;
+        while ((token = strtok_r (dummy, ",", &saveptr)) != NULL)
+        {
+          dummy = NULL;
+          printf ("token = %s;\n", token);
+        }
+]]]
+      )],
+      [c_cv_have_strtok_r_reentrant="yes"],
+      [AC_MSG_FAILURE([strtok_r isn't available. Please file a bugreport!])]
+    )
+  )
+fi
+
+CFLAGS="$SAVE_CFLAGS"
+if test "x$c_cv_have_strtok_r_reentrant" = "xyes"
+then
+       CFLAGS="$CFLAGS -D_REENTRANT=1"
+fi
+
+AC_CHECK_FUNCS(getpwnam_r getgrnam_r setgroups regcomp regerror regexec regfree)
+
+socket_needs_socket="no"
+AC_CHECK_FUNCS(socket, [], AC_CHECK_LIB(socket, socket, [socket_needs_socket="yes"], AC_MSG_ERROR(cannot find socket)))
+AM_CONDITIONAL(BUILD_WITH_LIBSOCKET, test "x$socket_needs_socket" = "xyes")
+
+clock_gettime_needs_rt="no"
+clock_gettime_needs_posix4="no"
+have_clock_gettime="no"
+AC_CHECK_FUNCS(clock_gettime, [have_clock_gettime="yes"])
+if test "x$have_clock_gettime" = "xno"
+then
+       AC_CHECK_LIB(rt, clock_gettime, [clock_gettime_needs_rt="yes"
+                                        have_clock_gettime="yes"])
+fi
+if test "x$have_clock_gettime" = "xno"
+then
+       AC_CHECK_LIB(posix4, clock_gettime, [clock_gettime_needs_posix4="yes"
+                                            have_clock_gettime="yes"])
+fi
+if test "x$have_clock_gettime" = "xyes"
+then
+       AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [Define if the clock_gettime(2) function is available.])
+else
+       AC_MSG_WARN(cannot find clock_gettime)
+fi
+
+nanosleep_needs_rt="no"
+nanosleep_needs_posix4="no"
+AC_CHECK_FUNCS(nanosleep,
+    [],
+    AC_CHECK_LIB(rt, nanosleep,
+        [nanosleep_needs_rt="yes"],
+        AC_CHECK_LIB(posix4, nanosleep,
+            [nanosleep_needs_posix4="yes"],
+            AC_MSG_ERROR(cannot find nanosleep))))
+
+AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$clock_gettime_needs_rt" = "xyes" || test "x$nanosleep_needs_rt" = "xyes")
+AM_CONDITIONAL(BUILD_WITH_LIBPOSIX4, test "x$clock_gettime_needs_posix4" = "xyes" || test "x$nanosleep_needs_posix4" = "xyes")
+
+AC_CHECK_FUNCS(sysctl, [have_sysctl="yes"], [have_sysctl="no"])
+AC_CHECK_FUNCS(sysctlbyname, [have_sysctlbyname="yes"], [have_sysctlbyname="no"])
+AC_CHECK_FUNCS(host_statistics, [have_host_statistics="yes"], [have_host_statistics="no"])
+AC_CHECK_FUNCS(processor_info, [have_processor_info="yes"], [have_processor_info="no"])
+AC_CHECK_FUNCS(thread_info, [have_thread_info="yes"], [have_thread_info="no"])
+AC_CHECK_FUNCS(statfs, [have_statfs="yes"], [have_statfs="no"])
+AC_CHECK_FUNCS(statvfs, [have_statvfs="yes"], [have_statvfs="no"])
+AC_CHECK_FUNCS(getifaddrs, [have_getifaddrs="yes"], [have_getifaddrs="no"])
+AC_CHECK_FUNCS(getloadavg, [have_getloadavg="yes"], [have_getloadavg="no"])
+AC_CHECK_FUNCS(syslog, [have_syslog="yes"], [have_syslog="no"])
+AC_CHECK_FUNCS(getutent, [have_getutent="yes"], [have_getutent="no"])
+AC_CHECK_FUNCS(getutxent, [have_getutxent="yes"], [have_getutxent="no"])
+
+# Check for strptime {{{
+if test "x$GCC" = "xyes"
+then
+       SAVE_CFLAGS="$CFLAGS"
+       CFLAGS="$CFLAGS -Wall -Wextra -Werror"
+fi
+
+AC_CHECK_FUNCS(strptime, [have_strptime="yes"], [have_strptime="no"])
+if test "x$have_strptime" = "xyes"
+then
+       AC_CACHE_CHECK([whether strptime is exported by default],
+                      [c_cv_have_strptime_default],
+                      AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <time.h>
+]]],
+[[[
+ struct tm stm;
+ (void) strptime ("2010-12-30%13:42:42", "%Y-%m-%dT%T", &stm);
+]]]
+                      )],
+                      [c_cv_have_strptime_default="yes"],
+                      [c_cv_have_strptime_default="no"]))
+fi
+if test "x$have_strptime" = "xyes" && test "x$c_cv_have_strptime_default" = "xno"
+then
+       AC_CACHE_CHECK([whether strptime needs standards mode],
+                      [c_cv_have_strptime_standards],
+                      AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[[
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE 1
+#endif
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200112L
+#endif
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500
+#endif
+#include <time.h>
+]]],
+[[[
+ struct tm stm;
+ (void) strptime ("2010-12-30%13:42:42", "%Y-%m-%dT%T", &stm);
+]]]
+                      )],
+                      [c_cv_have_strptime_standards="yes"],
+                      [c_cv_have_strptime_standards="no"]))
+
+       if test "x$c_cv_have_strptime_standards" = "xyes"
+       then
+               AC_DEFINE([STRPTIME_NEEDS_STANDARDS], 1, [Set to true if strptime is only exported in X/Open mode (GNU libc).])
+       else
+               have_strptime="no"
+       fi
+fi
+
+if test "x$GCC" = "xyes"
+then
+       CFLAGS="$SAVE_CFLAGS"
+fi
+# }}} Check for strptime
+
+AC_CHECK_FUNCS(swapctl, [have_swapctl="yes"], [have_swapctl="no"])
+if test "x$have_swapctl" = "xyes"; then
+        AC_CACHE_CHECK([whether swapctl takes two arguments],
+                [c_cv_have_swapctl_two_args],
+                AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[[
+#if HAVE_SYS_SWAP_H && !defined(_LP64) && _FILE_OFFSET_BITS == 64
+#  undef _FILE_OFFSET_BITS
+#  undef _LARGEFILE64_SOURCE
+#endif
+#include <sys/stat.h>
+#include <sys/swap.h>
+]]],
+[[[
+int num = swapctl(0, NULL);
+]]]
+                        )],
+                        [c_cv_have_swapctl_two_args="yes"],
+                        [c_cv_have_swapctl_two_args="no"]
+                )
+        )
+        AC_CACHE_CHECK([whether swapctl takes three arguments],
+                [c_cv_have_swapctl_three_args],
+                AC_COMPILE_IFELSE(
+                        [AC_LANG_PROGRAM(
+[[[
+#if HAVE_SYS_SWAP_H && !defined(_LP64) && _FILE_OFFSET_BITS == 64
+#  undef _FILE_OFFSET_BITS
+#  undef _LARGEFILE64_SOURCE
+#endif
+#include <sys/stat.h>
+#include <sys/swap.h>
+]]],
+[[[
+int num = swapctl(0, NULL, 0);
+]]]
+                        )],
+                        [c_cv_have_swapctl_three_args="yes"],
+                        [c_cv_have_swapctl_three_args="no"]
+                )
+        )
+fi
+# Check for different versions of `swapctl' here..
+if test "x$have_swapctl" = "xyes"; then
+        if test "x$c_cv_have_swapctl_two_args" = "xyes"; then
+                AC_DEFINE(HAVE_SWAPCTL_TWO_ARGS, 1,
+                          [Define if the function swapctl exists and takes two arguments.])
+        fi
+        if test "x$c_cv_have_swapctl_three_args" = "xyes"; then
+                AC_DEFINE(HAVE_SWAPCTL_THREE_ARGS, 1,
+                          [Define if the function swapctl exists and takes three arguments.])
+        fi
+fi
+
+# Check for NAN
+AC_ARG_WITH(nan-emulation, [AS_HELP_STRING([--with-nan-emulation], [use emulated NAN. For crosscompiling only.])],
+[
+ if test "x$withval" = "xno"; then
+        nan_type="none"
+ else if test "x$withval" = "xyes"; then
+        nan_type="zero"
+ else
+        nan_type="$withval"
+ fi; fi
+],
+[nan_type="none"])
+if test "x$nan_type" = "xnone"; then
+  AC_CACHE_CHECK([whether NAN is defined by default],
+    [c_cv_have_nan_default],
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <stdlib.h>
+#include <math.h>
+static double foo = NAN;
+]]],
+[[[
+       if (isnan (foo))
+        return 0;
+       else
+       return 1;
+]]]
+      )],
+      [c_cv_have_nan_default="yes"],
+      [c_cv_have_nan_default="no"]
+    )
+  )
+  if test "x$c_cv_have_nan_default" = "xyes"
+  then
+    nan_type="default"
+  fi
+fi
+if test "x$nan_type" = "xnone"; then
+  AC_CACHE_CHECK([whether NAN is defined by __USE_ISOC99],
+    [c_cv_have_nan_isoc],
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <stdlib.h>
+#define __USE_ISOC99 1
+#include <math.h>
+static double foo = NAN;
+]]],
+[[[
+       if (isnan (foo))
+        return 0;
+       else
+       return 1;
+]]]
+      )],
+      [c_cv_have_nan_isoc="yes"],
+      [c_cv_have_nan_isoc="no"]
+    )
+  )
+  if test "x$c_cv_have_nan_isoc" = "xyes"
+  then
+    nan_type="isoc99"
+  fi
+fi
+if test "x$nan_type" = "xnone"; then
+  SAVE_LDFLAGS=$LDFLAGS
+  LDFLAGS="$LDFLAGS -lm"
+  AC_CACHE_CHECK([whether NAN can be defined by 0/0],
+    [c_cv_have_nan_zero],
+    AC_RUN_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <stdlib.h>
+#include <math.h>
+#ifdef NAN
+# undef NAN
+#endif
+#define NAN (0.0 / 0.0)
+#ifndef isnan
+# define isnan(f) ((f) != (f))
+#endif
+static double foo = NAN;
+]]],
+[[[
+       if (isnan (foo))
+        return 0;
+       else
+       return 1;
+]]]
+      )],
+      [c_cv_have_nan_zero="yes"],
+      [c_cv_have_nan_zero="no"]
+    )
+  )
+  LDFLAGS=$SAVE_LDFLAGS
+  if test "x$c_cv_have_nan_zero" = "xyes"
+  then
+    nan_type="zero"
+  fi
+fi
+
+if test "x$nan_type" = "xdefault"; then
+  AC_DEFINE(NAN_STATIC_DEFAULT, 1,
+    [Define if NAN is defined by default and can initialize static variables.])
+else if test "x$nan_type" = "xisoc99"; then
+  AC_DEFINE(NAN_STATIC_ISOC, 1,
+    [Define if NAN is defined by __USE_ISOC99 and can initialize static variables.])
+else if test "x$nan_type" = "xzero"; then
+  AC_DEFINE(NAN_ZERO_ZERO, 1,
+    [Define if NAN can be defined as (0.0 / 0.0)])
+else
+  AC_MSG_ERROR([Didn't find out how to statically initialize variables to NAN. Sorry.])
+fi; fi; fi
+
+AC_ARG_WITH(fp-layout, [AS_HELP_STRING([--with-fp-layout], [set the memory layout of doubles. For crosscompiling only.])],
+[
+ if test "x$withval" = "xnothing"; then
+       fp_layout_type="nothing"
+ else if test "x$withval" = "xendianflip"; then
+       fp_layout_type="endianflip"
+ else if test "x$withval" = "xintswap"; then
+       fp_layout_type="intswap"
+ else
+       AC_MSG_ERROR([Invalid argument for --with-fp-layout. Valid arguments are: nothing, endianflip, intswap]);
+fi; fi; fi
+],
+[fp_layout_type="unknown"])
+
+if test "x$fp_layout_type" = "xunknown"; then
+  AC_CACHE_CHECK([if doubles are stored in x86 representation],
+    [c_cv_fp_layout_need_nothing],
+    AC_RUN_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif
+]]],
+[[[
+       uint64_t i0;
+       uint64_t i1;
+       uint8_t c[8];
+       double d;
+
+       d = 8.642135e130;
+       memcpy ((void *) &i0, (void *) &d, 8);
+
+       i1 = i0;
+       memcpy ((void *) c, (void *) &i1, 8);
+
+       if ((c[0] == 0x2f) && (c[1] == 0x25)
+                       && (c[2] == 0xc0) && (c[3] == 0xc7)
+                       && (c[4] == 0x43) && (c[5] == 0x2b)
+                       && (c[6] == 0x1f) && (c[7] == 0x5b))
+               return (0);
+       else
+               return (1);
+]]]
+      )],
+      [c_cv_fp_layout_need_nothing="yes"],
+      [c_cv_fp_layout_need_nothing="no"]
+    )
+  )
+  if test "x$c_cv_fp_layout_need_nothing" = "xyes"; then
+    fp_layout_type="nothing"
+  fi
+fi
+if test "x$fp_layout_type" = "xunknown"; then
+  AC_CACHE_CHECK([if endianflip converts to x86 representation],
+    [c_cv_fp_layout_need_endianflip],
+    AC_RUN_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif
+#define endianflip(A) ((((uint64_t)(A) & 0xff00000000000000LL) >> 56) | \
+                       (((uint64_t)(A) & 0x00ff000000000000LL) >> 40) | \
+                       (((uint64_t)(A) & 0x0000ff0000000000LL) >> 24) | \
+                       (((uint64_t)(A) & 0x000000ff00000000LL) >> 8)  | \
+                       (((uint64_t)(A) & 0x00000000ff000000LL) << 8)  | \
+                       (((uint64_t)(A) & 0x0000000000ff0000LL) << 24) | \
+                       (((uint64_t)(A) & 0x000000000000ff00LL) << 40) | \
+                       (((uint64_t)(A) & 0x00000000000000ffLL) << 56))
+]]],
+[[[
+       uint64_t i0;
+       uint64_t i1;
+       uint8_t c[8];
+       double d;
+
+       d = 8.642135e130;
+       memcpy ((void *) &i0, (void *) &d, 8);
+
+       i1 = endianflip (i0);
+       memcpy ((void *) c, (void *) &i1, 8);
+
+       if ((c[0] == 0x2f) && (c[1] == 0x25)
+                       && (c[2] == 0xc0) && (c[3] == 0xc7)
+                       && (c[4] == 0x43) && (c[5] == 0x2b)
+                       && (c[6] == 0x1f) && (c[7] == 0x5b))
+               return (0);
+       else
+               return (1);
+]]]
+      )],
+      [c_cv_fp_layout_need_endianflip="yes"],
+      [c_cv_fp_layout_need_endianflip="no"]
+    )
+  )
+  if test "x$c_cv_fp_layout_need_endianflip" = "xyes"; then
+    fp_layout_type="endianflip"
+  fi
+fi
+if test "x$fp_layout_type" = "xunknown"; then
+  AC_CACHE_CHECK([if intswap converts to x86 representation],
+    [c_cv_fp_layout_need_intswap],
+    AC_RUN_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif
+#define intswap(A)    ((((uint64_t)(A) & 0xffffffff00000000LL) >> 32) | \
+                       (((uint64_t)(A) & 0x00000000ffffffffLL) << 32))
+]]],
+[[[
+       uint64_t i0;
+       uint64_t i1;
+       uint8_t c[8];
+       double d;
+
+       d = 8.642135e130;
+       memcpy ((void *) &i0, (void *) &d, 8);
+
+       i1 = intswap (i0);
+       memcpy ((void *) c, (void *) &i1, 8);
+
+       if ((c[0] == 0x2f) && (c[1] == 0x25)
+                       && (c[2] == 0xc0) && (c[3] == 0xc7)
+                       && (c[4] == 0x43) && (c[5] == 0x2b)
+                       && (c[6] == 0x1f) && (c[7] == 0x5b))
+               return (0);
+       else
+               return (1);
+]]]
+      )],
+      [c_cv_fp_layout_need_intswap="yes"],
+      [c_cv_fp_layout_need_intswap="no"]
+    )
+  )
+  if test "x$c_cv_fp_layout_need_intswap" = "xyes"; then
+    fp_layout_type="intswap"
+  fi
+fi
+
+if test "x$fp_layout_type" = "xnothing"; then
+  AC_DEFINE(FP_LAYOUT_NEED_NOTHING, 1,
+  [Define if doubles are stored in x86 representation.])
+else if test "x$fp_layout_type" = "xendianflip"; then
+  AC_DEFINE(FP_LAYOUT_NEED_ENDIANFLIP, 1,
+  [Define if endianflip is needed to convert to x86 representation.])
+else if test "x$fp_layout_type" = "xintswap"; then
+  AC_DEFINE(FP_LAYOUT_NEED_INTSWAP, 1,
+  [Define if intswap is needed to convert to x86 representation.])
+else
+  AC_MSG_ERROR([Didn't find out how doubles are stored in memory. Sorry.])
+fi; fi; fi
+
+# --with-useragent {{{
+AC_ARG_WITH(useragent, [AS_HELP_STRING([--with-useragent@<:@=AGENT@:>@], [User agent to use on http requests])],
+[
+    if test "x$withval" != "xno" && test "x$withval" != "xyes"
+    then
+        AC_DEFINE_UNQUOTED(COLLECTD_USERAGENT, ["$withval"], [User agent for http requests])
+    fi
+])
+
+# }}}
+
+have_getfsstat="no"
+AC_CHECK_FUNCS(getfsstat, [have_getfsstat="yes"])
+have_getvfsstat="no"
+AC_CHECK_FUNCS(getvfsstat, [have_getvfsstat="yes"])
+have_listmntent="no"
+AC_CHECK_FUNCS(listmntent, [have_listmntent="yes"])
+
+have_getmntent="no"
+AC_CHECK_FUNCS(getmntent, [have_getmntent="c"])
+if test "x$have_getmntent" = "xno"; then
+       AC_CHECK_LIB(sun, getmntent, [have_getmntent="sun"])
+fi
+if test "x$have_getmntent" = "xno"; then
+       AC_CHECK_LIB(seq, getmntent, [have_getmntent="seq"])
+fi
+if test "x$have_getmntent" = "xno"; then
+       AC_CHECK_LIB(gen, getmntent, [have_getmntent="gen"])
+fi
+
+if test "x$have_getmntent" = "xc"; then
+       AC_CACHE_CHECK([whether getmntent takes one argument],
+               [c_cv_have_one_getmntent],
+               AC_COMPILE_IFELSE(
+                       [AC_LANG_PROGRAM(
+[[[
+#include "$srcdir/src/utils_mount.h"
+]]],
+[[[
+FILE *fh;
+struct mntent *me;
+fh = setmntent ("/etc/mtab", "r");
+me = getmntent (fh);
+]]]
+                       )],
+                       [c_cv_have_one_getmntent="yes"],
+                       [c_cv_have_one_getmntent="no"]
+               )
+       )
+       AC_CACHE_CHECK([whether getmntent takes two arguments],
+               [c_cv_have_two_getmntent],
+               AC_COMPILE_IFELSE(
+                       [AC_LANG_PROGRAM(
+[[[
+#include "$srcdir/src/utils_mount.h"
+]]],
+[[[
+                                FILE *fh;
+                                struct mnttab mt;
+                                int status;
+                                fh = fopen ("/etc/mnttab", "r");
+                                status = getmntent (fh, &mt);
+]]]
+                       )],
+                       [c_cv_have_two_getmntent="yes"],
+                       [c_cv_have_two_getmntent="no"]
+               )
+       )
+fi
+
+# Check for different versions of `getmntent' here..
+
+if test "x$have_getmntent" = "xc"; then
+       if test "x$c_cv_have_one_getmntent" = "xyes"; then
+               AC_DEFINE(HAVE_ONE_GETMNTENT, 1,
+                         [Define if the function getmntent exists and takes one argument.])
+       fi
+       if test "x$c_cv_have_two_getmntent" = "xyes"; then
+               AC_DEFINE(HAVE_TWO_GETMNTENT, 1,
+                         [Define if the function getmntent exists and takes two arguments.])
+       fi
+fi
+if test "x$have_getmntent" = "xsun"; then
+       AC_DEFINE(HAVE_SUN_GETMNTENT, 1,
+                 [Define if the function getmntent exists. It's the version from libsun.])
+fi
+if test "x$have_getmntent" = "xseq"; then
+       AC_DEFINE(HAVE_SEQ_GETMNTENT, 1,
+                 [Define if the function getmntent exists. It's the version from libseq.])
+fi
+if test "x$have_getmntent" = "xgen"; then
+       AC_DEFINE(HAVE_GEN_GETMNTENT, 1,
+                 [Define if the function getmntent exists. It's the version from libgen.])
+fi
+
+# Check for htonll
+AC_MSG_CHECKING([if have htonll defined])
+
+    have_htonll="no"
+    AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <sys/types.h>
+#include <netinet/in.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+]]],
+[[[
+          return htonll(0);
+]]]
+    )],
+    [
+      have_htonll="yes"
+      AC_DEFINE(HAVE_HTONLL, 1, [Define if the function htonll exists.])
+    ])
+
+AC_MSG_RESULT([$have_htonll])
+
+# Check for structures
+AC_CHECK_MEMBERS([struct if_data.ifi_ibytes, struct if_data.ifi_opackets, struct if_data.ifi_ierrors],
+       [AC_DEFINE(HAVE_STRUCT_IF_DATA, 1, [Define if struct if_data exists and is usable.])],
+       [],
+       [
+       #include <sys/types.h>
+       #include <sys/socket.h>
+       #include <net/if.h>
+       ])
+AC_CHECK_MEMBERS([struct net_device_stats.rx_bytes, struct net_device_stats.tx_packets, struct net_device_stats.rx_errors],
+       [AC_DEFINE(HAVE_STRUCT_NET_DEVICE_STATS, 1, [Define if struct net_device_stats exists and is usable.])],
+       [],
+       [
+       #include <sys/types.h>
+       #include <sys/socket.h>
+       #include <linux/if.h>
+       #include <linux/netdevice.h>
+       ])
+
+AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [],
+       [],
+       [
+       #include <netinet/in.h>
+       #include <net/if.h>
+       ])
+
+AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct kinfo_proc.ki_rusage],
+       [
+               AC_DEFINE(HAVE_STRUCT_KINFO_PROC_FREEBSD, 1,
+                       [Define if struct kinfo_proc exists in the FreeBSD variant.])
+               have_struct_kinfo_proc_freebsd="yes"
+       ],
+       [
+               have_struct_kinfo_proc_freebsd="no"
+       ],
+       [
+#include <kvm.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+       ])
+
+AC_CHECK_MEMBERS([struct kinfo_proc.kp_proc, struct kinfo_proc.kp_eproc],
+       [
+               AC_DEFINE(HAVE_STRUCT_KINFO_PROC_OPENBSD, 1,
+                       [Define if struct kinfo_proc exists in the OpenBSD variant.])
+               have_struct_kinfo_proc_openbsd="yes"
+       ],
+       [
+               have_struct_kinfo_proc_openbsd="no"
+       ],
+       [
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <kvm.h>
+       ])
+
+AC_CHECK_MEMBERS([struct udphdr.uh_dport, struct udphdr.uh_sport], [], [],
+[#define _BSD_SOURCE
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP_H
+# include <netinet/ip.h>
+#endif
+#if HAVE_NETINET_UDP_H
+# include <netinet/udp.h>
+#endif
+])
+AC_CHECK_MEMBERS([struct udphdr.dest, struct udphdr.source], [], [],
+[#define _BSD_SOURCE
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP_H
+# include <netinet/ip.h>
+#endif
+#if HAVE_NETINET_UDP_H
+# include <netinet/udp.h>
+#endif
+])
+
+AC_CHECK_MEMBERS([kstat_io_t.nwritten, kstat_io_t.writes, kstat_io_t.nwrites, kstat_io_t.wtime],
+       [],
+       [],
+       [
+#if HAVE_KSTAT_H
+# include <kstat.h>
+#endif
+       ])
+
+#
+# Checks for libraries begin here
+#
+
+with_libresolv="yes"
+AC_CHECK_LIB(resolv, res_search,
+[
+       AC_DEFINE(HAVE_LIBRESOLV, 1, [Define to 1 if you have the 'resolv' library (-lresolv).])
+],
+[with_libresolv="no"])
+AM_CONDITIONAL(BUILD_WITH_LIBRESOLV, test "x$with_libresolv" = "xyes")
+
+dnl Check for HAL (hardware abstraction library)
+with_libhal="yes"
+AC_CHECK_LIB(hal,libhal_device_property_exists,
+            [AC_DEFINE(HAVE_LIBHAL, 1, [Define to 1 if you have 'hal' library])],
+            [with_libhal="no"])
+if test "x$with_libhal" = "xyes"; then
+       if test "x$PKG_CONFIG" != "x"; then
+               BUILD_WITH_LIBHAL_CFLAGS="`pkg-config --cflags hal`"
+               BUILD_WITH_LIBHAL_LIBS="`pkg-config --libs hal`"
+               AC_SUBST(BUILD_WITH_LIBHAL_CFLAGS)
+               AC_SUBST(BUILD_WITH_LIBHAL_LIBS)
+       fi
+fi
+
+m4_divert_once([HELP_WITH], [
+collectd additional packages:])
+
+AM_CONDITIONAL([BUILD_FREEBSD],[test "x$x$ac_system" = "xFreeBSD"])
+
+AM_CONDITIONAL([BUILD_AIX],[test "x$x$ac_system" = "xAIX"])
+
+if test "x$ac_system" = "xAIX"
+then
+       with_perfstat="yes"
+       with_procinfo="yes"
+else
+       with_perfstat="no (AIX only)"
+       with_procinfo="no (AIX only)"
+fi
+
+if test "x$with_perfstat" = "xyes"
+then
+       AC_CHECK_LIB(perfstat, perfstat_reset, [with_perfstat="yes"], [with_perfstat="no (perfstat not found)"], [])
+#      AC_CHECK_HEADERS(sys/protosw.h libperfstat.h,, [with_perfstat="no (perfstat not found)"])
+fi
+if test "x$with_perfstat" = "xyes"
+then
+        AC_DEFINE(HAVE_PERFSTAT, 1, [Define to 1 if you have the 'perfstat' library (-lperfstat)])
+        # struct members pertaining to donation have been added to libperfstat somewhere between AIX5.3ML5 and AIX5.3ML9
+        AC_CHECK_MEMBER([perfstat_partition_type_t.b.donate_enabled], [], [], [[#include <libperfstat.h]])
+        if test "x$av_cv_member_perfstat_partition_type_t_b_donate_enabled" = "xyes"
+        then
+               AC_DEFINE(PERFSTAT_SUPPORTS_DONATION, 1, [Define to 1 if your version of the 'perfstat' library supports donation])
+        fi
+fi
+AM_CONDITIONAL(BUILD_WITH_PERFSTAT, test "x$with_perfstat" = "xyes")
+
+# Processes plugin under AIX.
+if test "x$with_procinfo" = "xyes"
+then
+       AC_CHECK_HEADERS(procinfo.h,, [with_procinfo="no (procinfo.h not found)"])
+fi
+if test "x$with_procinfo" = "xyes"
+then
+        AC_DEFINE(HAVE_PROCINFO_H, 1, [Define to 1 if you have the procinfo.h])
+fi
+
+if test "x$ac_system" = "xSolaris"
+then
+       with_kstat="yes"
+       with_devinfo="yes"
+else
+       with_kstat="no (Solaris only)"
+       with_devinfo="no (Solaris only)"
+fi
+
+if test "x$with_kstat" = "xyes"
+then
+       AC_CHECK_LIB(kstat, kstat_open, [with_kstat="yes"], [with_kstat="no (libkstat not found)"], [])
+fi
+if test "x$with_kstat" = "xyes"
+then
+       AC_CHECK_LIB(devinfo, di_init, [with_devinfo="yes"], [with_devinfo="no (not found)"], [])
+       AC_CHECK_HEADERS(kstat.h,, [with_kstat="no (kstat.h not found)"])
+fi
+if test "x$with_kstat" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBKSTAT, 1,
+                 [Define to 1 if you have the 'kstat' library (-lkstat)])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKSTAT, test "x$with_kstat" = "xyes")
+AM_CONDITIONAL(BUILD_WITH_LIBDEVINFO, test "x$with_devinfo" = "xyes")
+
+with_libiokit="no"
+if test "x$ac_system" = "xDarwin"
+then
+       with_libiokit="yes"
+else
+       with_libiokit="no"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBIOKIT, test "x$with_libiokit" = "xyes")
+
+with_libkvm="no"
+AC_CHECK_LIB(kvm, kvm_getprocs, [with_kvm_getprocs="yes"], [with_kvm_getprocs="no"])
+if test "x$with_kvm_getprocs" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBKVM_GETPROCS, 1,
+                 [Define to 1 if you have the 'kvm' library with the 'kvm_getprocs' symbol (-lkvm)])
+       with_libkvm="yes"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETPROCS, test "x$with_kvm_getprocs" = "xyes")
+
+AC_CHECK_LIB(kvm, kvm_getswapinfo, [with_kvm_getswapinfo="yes"], [with_kvm_getswapinfo="no"])
+if test "x$with_kvm_getswapinfo" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBKVM_GETSWAPINFO, 1,
+                 [Define to 1 if you have the 'kvm' library with the 'kvm_getswapinfo' symbol (-lkvm)])
+       with_libkvm="yes"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETSWAPINFO, test "x$with_kvm_getswapinfo" = "xyes")
+
+AC_CHECK_LIB(kvm, kvm_nlist, [with_kvm_nlist="yes"], [with_kvm_nlist="no"])
+if test "x$with_kvm_nlist" = "xyes"
+then
+       AC_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"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_NLIST, test "x$with_kvm_nlist" = "xyes")
+
+AC_CHECK_LIB(kvm, kvm_openfiles, [with_kvm_openfiles="yes"], [with_kvm_openfiles="no"])
+if test "x$with_kvm_openfiles" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBKVM_NLIST, 1,
+                 [Define to 1 if you have the 'kvm' library with the 'kvm_openfiles' symbol (-lkvm)])
+       with_libkvm="yes"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_OPENFILES, test "x$with_kvm_openfiles" = "xyes")
+
+# --with-libaquaero5 {{{
+AC_ARG_WITH(libaquaero5, [AS_HELP_STRING([--with-libaquaero5@<:@=PREFIX@:>@], [Path to aquatools-ng source code.])],
+[
+ if test "x$withval" = "xyes"
+ then
+        with_libaquaero5="yes"
+ else if test "x$withval" = "xno"
+ then
+        with_libaquaero5="no"
+ else
+        with_libaquaero5="yes"
+        LIBAQUAERO5_CFLAGS="$LIBAQUAERO5_CFLAGS -I$withval/src"
+        LIBAQUAERO5_LDFLAGS="$LIBAQUAERO5_LDFLAGS -L$withval/obj"
+ fi; fi
+],
+[with_libaquaero5="yes"])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $LIBAQUAERO5_CFLAGS"
+LDFLAGS="$LDFLAGS $LIBAQUAERO5_LDFLAGS"
+
+if test "x$with_libaquaero5" = "xyes"
+then
+       if test "x$LIBAQUAERO5_CFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([libaquaero5 CPPFLAGS: $LIBAQUAERO5_CFLAGS])
+       fi
+       AC_CHECK_HEADERS(libaquaero5.h,
+       [with_libaquaero5="yes"],
+       [with_libaquaero5="no (libaquaero5.h not found)"])
+fi
+if test "x$with_libaquaero5" = "xyes"
+then
+       if test "x$LIBAQUAERO5_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([libaquaero5 LDFLAGS: $LIBAQUAERO5_LDFLAGS])
+       fi
+       AC_CHECK_LIB(aquaero5, libaquaero5_poll,
+       [with_libaquaero5="yes"],
+       [with_libaquaero5="no (symbol 'libaquaero5_poll' not found)"])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libaquaero5" = "xyes"
+then
+       BUILD_WITH_LIBAQUAERO5_CFLAGS="$LIBAQUAERO5_CFLAGS"
+       BUILD_WITH_LIBAQUAERO5_LDFLAGS="$LIBAQUAERO5_LDFLAGS"
+       AC_SUBST(BUILD_WITH_LIBAQUAERO5_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBAQUAERO5_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBAQUAERO5, test "x$with_libaquaero5" = "xyes")
+# }}}
+
+# --with-libcredis {{{
+AC_ARG_WITH(libcredis, [AS_HELP_STRING([--with-libcredis@<:@=PREFIX@:>@], [Path to libcredis.])],
+[
+ if test "x$withval" = "xyes"
+ then
+        with_libcredis="yes"
+ else if test "x$withval" = "xno"
+ then
+        with_libcredis="no"
+ else
+        with_libcredis="yes"
+        LIBCREDIS_CPPFLAGS="$LIBCREDIS_CPPFLAGS -I$withval/include"
+        LIBCREDIS_LDFLAGS="$LIBCREDIS_LDFLAGS -L$withval/lib"
+ fi; fi
+],
+[with_libcredis="yes"])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $LIBCREDIS_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBCREDIS_LDFLAGS"
+
+if test "x$with_libcredis" = "xyes"
+then
+       if test "x$LIBCREDIS_CPPFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([libcredis CPPFLAGS: $LIBCREDIS_CPPFLAGS])
+       fi
+       AC_CHECK_HEADERS(credis.h,
+       [with_libcredis="yes"],
+       [with_libcredis="no (credis.h not found)"])
+fi
+if test "x$with_libcredis" = "xyes"
+then
+       if test "x$LIBCREDIS_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([libcredis LDFLAGS: $LIBCREDIS_LDFLAGS])
+       fi
+       AC_CHECK_LIB(credis, credis_info,
+       [with_libcredis="yes"],
+       [with_libcredis="no (symbol 'credis_info' not found)"])
+
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libcredis" = "xyes"
+then
+       BUILD_WITH_LIBCREDIS_CPPFLAGS="$LIBCREDIS_CPPFLAGS"
+       BUILD_WITH_LIBCREDIS_LDFLAGS="$LIBCREDIS_LDFLAGS"
+       AC_SUBST(BUILD_WITH_LIBCREDIS_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBCREDIS_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBCREDIS, test "x$with_libcredis" = "xyes")
+# }}}
+
+# --with-libcurl {{{
+with_curl_config="curl-config"
+with_curl_cflags=""
+with_curl_libs=""
+AC_ARG_WITH(libcurl, [AS_HELP_STRING([--with-libcurl@<:@=PREFIX@:>@], [Path to libcurl.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libcurl="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libcurl="yes"
+       else
+               if test -f "$withval" && test -x "$withval"
+               then
+                       with_curl_config="$withval"
+                       with_libcurl="yes"
+               else if test -x "$withval/bin/curl-config"
+               then
+                       with_curl_config="$withval/bin/curl-config"
+                       with_libcurl="yes"
+               fi; fi
+               with_libcurl="yes"
+       fi; fi
+],
+[
+       with_libcurl="yes"
+])
+if test "x$with_libcurl" = "xyes"
+then
+       with_curl_cflags=`$with_curl_config --cflags 2>/dev/null`
+       curl_config_status=$?
+
+       if test $curl_config_status -ne 0
+       then
+               with_libcurl="no ($with_curl_config failed)"
+       else
+               SAVE_CPPFLAGS="$CPPFLAGS"
+               CPPFLAGS="$CPPFLAGS $with_curl_cflags"
+
+               AC_CHECK_HEADERS(curl/curl.h, [], [with_libcurl="no (curl/curl.h not found)"], [])
+
+               CPPFLAGS="$SAVE_CPPFLAGS"
+       fi
+fi
+if test "x$with_libcurl" = "xyes"
+then
+       with_curl_libs=`$with_curl_config --libs 2>/dev/null`
+       curl_config_status=$?
+
+       if test $curl_config_status -ne 0
+       then
+               with_libcurl="no ($with_curl_config failed)"
+       else
+               AC_CHECK_LIB(curl, curl_easy_init,
+                [with_libcurl="yes"],
+                [with_libcurl="no (symbol 'curl_easy_init' not found)"],
+                [$with_curl_libs])
+       fi
+fi
+if test "x$with_libcurl" = "xyes"
+then
+       BUILD_WITH_LIBCURL_CFLAGS="$with_curl_cflags"
+       BUILD_WITH_LIBCURL_LIBS="$with_curl_libs"
+       AC_SUBST(BUILD_WITH_LIBCURL_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBCURL_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBCURL, test "x$with_libcurl" = "xyes")
+# }}}
+
+# --with-libdbi {{{
+with_libdbi_cppflags=""
+with_libdbi_ldflags=""
+AC_ARG_WITH(libdbi, [AS_HELP_STRING([--with-libdbi@<:@=PREFIX@:>@], [Path to libdbi.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               with_libdbi_cppflags="-I$withval/include"
+               with_libdbi_ldflags="-L$withval/lib"
+               with_libdbi="yes"
+       else
+               with_libdbi="$withval"
+       fi
+],
+[
+       with_libdbi="yes"
+])
+if test "x$with_libdbi" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libdbi_cppflags"
+
+       AC_CHECK_HEADERS(dbi/dbi.h, [with_libdbi="yes"], [with_libdbi="no (dbi/dbi.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libdbi" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libdbi_cppflags"
+       LDFLAGS="$LDFLAGS $with_libdbi_ldflags"
+
+       AC_CHECK_LIB(dbi, dbi_initialize, [with_libdbi="yes"], [with_libdbi="no (Symbol 'dbi_initialize' not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libdbi" = "xyes"
+then
+       BUILD_WITH_LIBDBI_CPPFLAGS="$with_libdbi_cppflags"
+       BUILD_WITH_LIBDBI_LDFLAGS="$with_libdbi_ldflags"
+       BUILD_WITH_LIBDBI_LIBS="-ldbi"
+       AC_SUBST(BUILD_WITH_LIBDBI_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBDBI_LDFLAGS)
+       AC_SUBST(BUILD_WITH_LIBDBI_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBDBI, test "x$with_libdbi" = "xyes")
+# }}}
+
+# --with-libesmtp {{{
+AC_ARG_WITH(libesmtp, [AS_HELP_STRING([--with-libesmtp@<:@=PREFIX@:>@], [Path to libesmtp.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include -D_THREAD_SAFE"
+               with_libesmtp="yes"
+       else
+               with_libesmtp="$withval"
+       fi
+],
+[
+       with_libesmtp="yes"
+])
+if test "x$with_libesmtp" = "xyes"
+then
+       AC_CHECK_LIB(esmtp, smtp_create_session,
+       [
+               AC_DEFINE(HAVE_LIBESMTP, 1, [Define to 1 if you have the esmtp library (-lesmtp).])
+       ], [with_libesmtp="no (libesmtp not found)"])
+fi
+if test "x$with_libesmtp" = "xyes"
+then
+       AC_CHECK_HEADERS(libesmtp.h,
+       [
+               AC_DEFINE(HAVE_LIBESMTP_H, 1, [Define to 1 if you have the <libesmtp.h> header file.])
+       ], [with_libesmtp="no (libesmtp.h not found)"])
+fi
+if test "x$with_libesmtp" = "xyes"
+then
+       collect_libesmtp=1
+else
+       collect_libesmtp=0
+fi
+AC_DEFINE_UNQUOTED(COLLECT_LIBESMTP, [$collect_libesmtp],
+       [Wether or not to use the esmtp library])
+AM_CONDITIONAL(BUILD_WITH_LIBESMTP, test "x$with_libesmtp" = "xyes")
+# }}}
+
+# --with-libganglia {{{
+AC_ARG_WITH(libganglia, [AS_HELP_STRING([--with-libganglia@<:@=PREFIX@:>@], [Path to libganglia.])],
+[
+ if test -f "$withval" && test -x "$withval"
+ then
+        with_libganglia_config="$withval"
+        with_libganglia="yes"
+ else if test -f "$withval/bin/ganglia-config" && test -x "$withval/bin/ganglia-config"
+ then
+        with_libganglia_config="$withval/bin/ganglia-config"
+        with_libganglia="yes"
+ else if test -d "$withval"
+ then
+        GANGLIA_CPPFLAGS="-I$withval/include"
+        GANGLIA_LDFLAGS="-L$withval/lib"
+        with_libganglia="yes"
+ else
+        with_libganglia_config="ganglia-config"
+        with_libganglia="$withval"
+ fi; fi; fi
+],
+[
+ with_libganglia_config="ganglia-config"
+ with_libganglia="yes"
+])
+
+if test "x$with_libganglia" = "xyes" && test "x$with_libganglia_config" != "x"
+then
+       if test "x$GANGLIA_CPPFLAGS" = "x"
+       then
+               GANGLIA_CPPFLAGS=`"$with_libganglia_config" --cflags 2>/dev/null`
+       fi
+
+       if test "x$GANGLIA_LDFLAGS" = "x"
+       then
+               GANGLIA_LDFLAGS=`"$with_libganglia_config" --ldflags 2>/dev/null`
+       fi
+
+       if test "x$GANGLIA_LIBS" = "x"
+       then
+               GANGLIA_LIBS=`"$with_libganglia_config" --libs 2>/dev/null`
+       fi
+fi
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $GANGLIA_CPPFLAGS"
+LDFLAGS="$LDFLAGS $GANGLIA_LDFLAGS"
+
+if test "x$with_libganglia" = "xyes"
+then
+       AC_CHECK_HEADERS(gm_protocol.h,
+       [
+               AC_DEFINE(HAVE_GM_PROTOCOL_H, 1,
+                         [Define to 1 if you have the <gm_protocol.h> header file.])
+       ], [with_libganglia="no (gm_protocol.h not found)"])
+fi
+
+if test "x$with_libganglia" = "xyes"
+then
+       AC_CHECK_LIB(ganglia, xdr_Ganglia_value_msg,
+       [
+               AC_DEFINE(HAVE_LIBGANGLIA, 1,
+                         [Define to 1 if you have the ganglia library (-lganglia).])
+       ], [with_libganglia="no (symbol xdr_Ganglia_value_msg not found)"])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+AC_SUBST(GANGLIA_CPPFLAGS)
+AC_SUBST(GANGLIA_LDFLAGS)
+AC_SUBST(GANGLIA_LIBS)
+AM_CONDITIONAL(BUILD_WITH_LIBGANGLIA, test "x$with_libganglia" = "xyes")
+# }}}
+
+# --with-libgcrypt {{{
+GCRYPT_CPPFLAGS="$GCRYPT_CPPFLAGS"
+GCRYPT_LDFLAGS="$GCRYPT_LDFLAGS"
+GCRYPT_LIBS="$GCRYPT_LIBS"
+AC_ARG_WITH(libgcrypt, [AS_HELP_STRING([--with-libgcrypt@<:@=PREFIX@:>@], [Path to libgcrypt.])],
+[
+ if test -f "$withval" && test -x "$withval"
+ then
+        with_libgcrypt_config="$withval"
+        with_libgcrypt="yes"
+ else if test -f "$withval/bin/gcrypt-config" && test -x "$withval/bin/gcrypt-config"
+ then
+        with_libgcrypt_config="$withval/bin/gcrypt-config"
+        with_libgcrypt="yes"
+ else if test -d "$withval"
+ then
+        GCRYPT_CPPFLAGS="$GCRYPT_CPPFLAGS -I$withval/include"
+        GCRYPT_LDFLAGS="$GCRYPT_LDFLAGS -L$withval/lib"
+        with_libgcrypt="yes"
+ else
+        with_libgcrypt_config="gcrypt-config"
+        with_libgcrypt="$withval"
+ fi; fi; fi
+],
+[
+ with_libgcrypt_config="libgcrypt-config"
+ with_libgcrypt="yes"
+])
+
+if test "x$with_libgcrypt" = "xyes" && test "x$with_libgcrypt_config" != "x"
+then
+       if test "x$GCRYPT_CPPFLAGS" = "x"
+       then
+               GCRYPT_CPPFLAGS=`"$with_libgcrypt_config" --cflags 2>/dev/null`
+       fi
+
+       if test "x$GCRYPT_LDFLAGS" = "x"
+       then
+               gcrypt_exec_prefix=`"$with_libgcrypt_config" --exec-prefix 2>/dev/null`
+               GCRYPT_LDFLAGS="-L$gcrypt_exec_prefix/lib"
+       fi
+
+       if test "x$GCRYPT_LIBS" = "x"
+       then
+               GCRYPT_LIBS=`"$with_libgcrypt_config" --libs 2>/dev/null`
+       fi
+fi
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $GCRYPT_CPPFLAGS"
+LDFLAGS="$LDFLAGS $GCRYPT_LDFLAGS"
+
+if test "x$with_libgcrypt" = "xyes"
+then
+       if test "x$GCRYPT_CPPFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([gcrypt CPPFLAGS: $GCRYPT_CPPFLAGS])
+       fi
+       AC_CHECK_HEADERS(gcrypt.h,
+               [with_libgcrypt="yes"],
+               [with_libgcrypt="no (gcrypt.h not found)"])
+fi
+
+if test "x$with_libgcrypt" = "xyes"
+then
+       if test "x$GCRYPT_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([gcrypt LDFLAGS: $GCRYPT_LDFLAGS])
+       fi
+       AC_CHECK_LIB(gcrypt, gcry_md_hash_buffer,
+               [with_libgcrypt="yes"],
+               [with_libgcrypt="no (symbol gcry_md_hash_buffer not found)"])
+
+       if test "$with_libgcrypt" != "no"; then
+               AM_PATH_LIBGCRYPT(1:1.2.0,,with_libgcrypt="no (version 1.2.0+ required)")
+               GCRYPT_CPPFLAGS="$LIBGCRYPT_CPPFLAGS $LIBGCRYPT_CFLAGS"
+               GCRYPT_LIBS="$LIBGCRYPT_LIBS"
+       fi
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libgcrypt" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBGCRYPT, 1, [Define to 1 if you have the gcrypt library (-lgcrypt).])
+fi
+
+AC_SUBST(GCRYPT_CPPFLAGS)
+AC_SUBST(GCRYPT_LDFLAGS)
+AC_SUBST(GCRYPT_LIBS)
+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" = "xshipped"
+       then
+               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="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="pkgconfig"
+       else
+               with_libiptc="no (Linux only)"
+       fi
+])
+
+if test "x$with_libiptc" = "xpkgconfig" && test "x$PKG_CONFIG" = "x"
+then
+       with_libiptc="no (Don't have pkg-config)"
+fi
+
+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/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" = "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
+       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, [],
+       [
+               with_libiptc="no (Linux iptables headers not found)"
+       ],
+       [
+#include "$srcdir/src/owniptc/ipt_kernel_headers.h"
+       ])
+fi
+AM_CONDITIONAL(BUILD_WITH_OWN_LIBIPTC, test "x$with_libiptc" = "xown")
+if test "x$with_libiptc" = "xown"
+then
+       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="$with_libiptc_cflags"
+       BUILD_WITH_LIBIPTC_LDFLAGS="$with_libiptc_libs"
+       AC_SUBST(BUILD_WITH_LIBIPTC_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBIPTC_LDFLAGS)
+fi
+# }}}
+
+# --with-java {{{
+with_java_home="$JAVA_HOME"
+with_java_vmtype="client"
+with_java_cflags=""
+with_java_libs=""
+JAVAC="$JAVAC"
+JAR="$JAR"
+AC_ARG_WITH(java, [AS_HELP_STRING([--with-java@<:@=PREFIX@:>@], [Path to Java home.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_java="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_java="yes"
+       else
+               with_java_home="$withval"
+               with_java="yes"
+       fi; fi
+],
+[with_java="yes"])
+if test "x$with_java" = "xyes"
+then
+       if test -d "$with_java_home"
+       then
+               AC_MSG_CHECKING([for jni.h])
+               TMPVAR=`find "$with_java_home" -name jni.h -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1`
+               if test "x$TMPVAR" != "x"
+               then
+                       AC_MSG_RESULT([found in $TMPVAR])
+                       JAVA_CPPFLAGS="$JAVA_CPPFLAGS -I$TMPVAR"
+               else
+                       AC_MSG_RESULT([not found])
+               fi
+
+               AC_MSG_CHECKING([for jni_md.h])
+               TMPVAR=`find "$with_java_home" -name jni_md.h -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1`
+               if test "x$TMPVAR" != "x"
+               then
+                       AC_MSG_RESULT([found in $TMPVAR])
+                       JAVA_CPPFLAGS="$JAVA_CPPFLAGS -I$TMPVAR"
+               else
+                       AC_MSG_RESULT([not found])
+               fi
+
+               AC_MSG_CHECKING([for libjvm.so])
+               TMPVAR=`find "$with_java_home" -name libjvm.so -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1`
+               if test "x$TMPVAR" != "x"
+               then
+                       AC_MSG_RESULT([found in $TMPVAR])
+                       JAVA_LDFLAGS="$JAVA_LDFLAGS -L$TMPVAR -Wl,-rpath -Wl,$TMPVAR"
+               else
+                       AC_MSG_RESULT([not found])
+               fi
+
+               if test "x$JAVAC" = "x"
+               then
+                       AC_MSG_CHECKING([for javac])
+                       TMPVAR=`find "$with_java_home" -name javac -type f 2>/dev/null | head -n 1`
+                       if test "x$TMPVAR" != "x"
+                       then
+                               JAVAC="$TMPVAR"
+                               AC_MSG_RESULT([$JAVAC])
+                       else
+                               AC_MSG_RESULT([not found])
+                       fi
+               fi
+               if test "x$JAR" = "x"
+               then
+                       AC_MSG_CHECKING([for jar])
+                       TMPVAR=`find "$with_java_home" -name jar -type f 2>/dev/null | head -n 1`
+                       if test "x$TMPVAR" != "x"
+                       then
+                               JAR="$TMPVAR"
+                               AC_MSG_RESULT([$JAR])
+                       else
+                               AC_MSG_RESULT([not found])
+                       fi
+               fi
+       else if test "x$with_java_home" != "x"
+       then
+               AC_MSG_WARN([JAVA_HOME: No such directory: $with_java_home])
+       fi; fi
+fi
+
+if test "x$JAVA_CPPFLAGS" != "x"
+then
+       AC_MSG_NOTICE([Building with JAVA_CPPFLAGS set to: $JAVA_CPPFLAGS])
+fi
+if test "x$JAVA_CFLAGS" != "x"
+then
+       AC_MSG_NOTICE([Building with JAVA_CFLAGS set to: $JAVA_CFLAGS])
+fi
+if test "x$JAVA_LDFLAGS" != "x"
+then
+       AC_MSG_NOTICE([Building with JAVA_LDFLAGS set to: $JAVA_LDFLAGS])
+fi
+if test "x$JAVAC" = "x"
+then
+       with_javac_path="$PATH"
+       if test "x$with_java_home" != "x"
+       then
+               with_javac_path="$with_java_home:with_javac_path"
+               if test -d "$with_java_home/bin"
+               then
+                       with_javac_path="$with_java_home/bin:with_javac_path"
+               fi
+       fi
+
+       AC_PATH_PROG(JAVAC, javac, [], "$with_javac_path")
+fi
+if test "x$JAVAC" = "x"
+then
+       with_java="no (javac not found)"
+fi
+if test "x$JAR" = "x"
+then
+       with_jar_path="$PATH"
+       if test "x$with_java_home" != "x"
+       then
+               with_jar_path="$with_java_home:$with_jar_path"
+               if test -d "$with_java_home/bin"
+               then
+                       with_jar_path="$with_java_home/bin:$with_jar_path"
+               fi
+       fi
+
+       AC_PATH_PROG(JAR, jar, [], "$with_jar_path")
+fi
+if test "x$JAR" = "x"
+then
+       with_java="no (jar not found)"
+fi
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_CFLAGS="$CFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $JAVA_CPPFLAGS"
+CFLAGS="$CFLAGS $JAVA_CFLAGS"
+LDFLAGS="$LDFLAGS $JAVA_LDFLAGS"
+
+if test "x$with_java" = "xyes"
+then
+       AC_CHECK_HEADERS(jni.h, [], [with_java="no (jni.h not found)"])
+fi
+if test "x$with_java" = "xyes"
+then
+       AC_CHECK_LIB(jvm, JNI_CreateJavaVM,
+       [with_java="yes"],
+       [with_java="no (libjvm not found)"],
+       [$JAVA_LIBS])
+fi
+if test "x$with_java" = "xyes"
+then
+       JAVA_LIBS="$JAVA_LIBS -ljvm"
+       AC_MSG_NOTICE([Building with JAVA_LIBS set to: $JAVA_LIBS])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+CFLAGS="$SAVE_CFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+AC_SUBST(JAVA_CPPFLAGS)
+AC_SUBST(JAVA_CFLAGS)
+AC_SUBST(JAVA_LDFLAGS)
+AC_SUBST(JAVA_LIBS)
+AM_CONDITIONAL(BUILD_WITH_JAVA, test "x$with_java" = "xyes")
+# }}}
+
+# --with-liblvm2app {{{
+with_liblvm2app_cppflags=""
+with_liblvm2app_ldflags=""
+AC_ARG_WITH(liblvm2app, [AS_HELP_STRING([--with-liblvm2app@<:@=PREFIX@:>@], [Path to liblvm2app.])],
+[
+        if test "x$withval" != "xno" && test "x$withval" != "xyes"
+        then
+                with_liblvm2app_cppflags="-I$withval/include"
+                with_liblvm2app_ldflags="-L$withval/lib"
+                with_liblvm2app="yes"
+        else
+                with_liblvm2app="$withval"
+        fi
+],
+[
+        with_liblvm2app="yes"
+])
+if test "x$with_liblvm2app" = "xyes"
+then
+        SAVE_CPPFLAGS="$CPPFLAGS"
+        CPPFLAGS="$CPPFLAGS $with_liblvm2app_cppflags"
+
+        AC_CHECK_HEADERS(lvm2app.h, [with_liblvm2app="yes"], [with_liblvm2app="no (lvm2app.h not found)"])
+
+        CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+if test "x$with_liblvm2app" = "xyes"
+then
+        SAVE_CPPFLAGS="$CPPFLAGS"
+        SAVE_LDFLAGS="$LDFLAGS"
+        CPPFLAGS="$CPPFLAGS $with_liblvm2app_cppflags"
+        LDFLAGS="$LDFLAGS $with_liblvm2app_ldflags"
+
+        AC_CHECK_LIB(lvm2app, lvm_init, [with_liblvm2app="yes"], [with_liblvm2app="no (Symbol 'lvm_init' not found)"])
+
+        CPPFLAGS="$SAVE_CPPFLAGS"
+        LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_liblvm2app" = "xyes"
+then
+        BUILD_WITH_LIBLVM2APP_CPPFLAGS="$with_liblvm2app_cppflags"
+        BUILD_WITH_LIBLVM2APP_LDFLAGS="$with_liblvm2app_ldflags"
+        BUILD_WITH_LIBLVM2APP_LIBS="-llvm2app"
+        AC_SUBST(BUILD_WITH_LIBLVM2APP_CPPFLAGS)
+        AC_SUBST(BUILD_WITH_LIBLVM2APP_LDFLAGS)
+        AC_SUBST(BUILD_WITH_LIBLVM2APP_LIBS)
+        AC_DEFINE(HAVE_LIBLVM2APP, 1, [Define if liblvm2app is present and usable.])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBLVM2APP, test "x$with_liblvm2app" = "xyes")
+# }}}
+
+# --with-libmemcached {{{
+with_libmemcached_cppflags=""
+with_libmemcached_ldflags=""
+AC_ARG_WITH(libmemcached, [AS_HELP_STRING([--with-libmemcached@<:@=PREFIX@:>@], [Path to libmemcached.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               with_libmemcached_cppflags="-I$withval/include"
+               with_libmemcached_ldflags="-L$withval/lib"
+               with_libmemcached="yes"
+       else
+               with_libmemcached="$withval"
+       fi
+],
+[
+       with_libmemcached="yes"
+])
+if test "x$with_libmemcached" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libmemcached_cppflags"
+
+       AC_CHECK_HEADERS(libmemcached/memcached.h, [with_libmemcached="yes"], [with_libmemcached="no (libmemcached/memcached.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libmemcached" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libmemcached_cppflags"
+       LDFLAGS="$LDFLAGS $with_libmemcached_ldflags"
+
+       AC_CHECK_LIB(memcached, memcached_create, [with_libmemcached="yes"], [with_libmemcached="no (Symbol 'memcached_create' not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libmemcached" = "xyes"
+then
+       BUILD_WITH_LIBMEMCACHED_CPPFLAGS="$with_libmemcached_cppflags"
+       BUILD_WITH_LIBMEMCACHED_LDFLAGS="$with_libmemcached_ldflags"
+       BUILD_WITH_LIBMEMCACHED_LIBS="-lmemcached"
+       AC_SUBST(BUILD_WITH_LIBMEMCACHED_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBMEMCACHED_LDFLAGS)
+       AC_SUBST(BUILD_WITH_LIBMEMCACHED_LIBS)
+       AC_DEFINE(HAVE_LIBMEMCACHED, 1, [Define if libmemcached is present and usable.])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBMEMCACHED, test "x$with_libmemcached" = "xyes")
+# }}}
+
+# --with-libmodbus {{{
+with_libmodbus_config=""
+with_libmodbus_cflags=""
+with_libmodbus_libs=""
+AC_ARG_WITH(libmodbus, [AS_HELP_STRING([--with-libmodbus@<:@=PREFIX@:>@], [Path to the modbus library.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libmodbus="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libmodbus="use_pkgconfig"
+       else if test -d "$with_libmodbus/lib"
+       then
+               AC_MSG_NOTICE([Not checking for libmodbus: Manually configured])
+               with_libmodbus_cflags="-I$withval/include"
+               with_libmodbus_libs="-L$withval/lib -lmodbus"
+               with_libmodbus="yes"
+       fi; fi; fi
+],
+[with_libmodbus="use_pkgconfig"])
+
+# configure using pkg-config
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+       if test "x$PKG_CONFIG" = "x"
+       then
+               with_libmodbus="no (Don't have pkg-config)"
+       fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+       AC_MSG_NOTICE([Checking for libmodbus using $PKG_CONFIG])
+       $PKG_CONFIG --exists 'libmodbus' 2>/dev/null
+       if test $? -ne 0
+       then
+               with_libmodbus="no (pkg-config doesn't know libmodbus)"
+       fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+       with_libmodbus_cflags="`$PKG_CONFIG --cflags 'libmodbus'`"
+       if test $? -ne 0
+       then
+               with_libmodbus="no ($PKG_CONFIG failed)"
+       fi
+       with_libmodbus_libs="`$PKG_CONFIG --libs 'libmodbus'`"
+       if test $? -ne 0
+       then
+               with_libmodbus="no ($PKG_CONFIG failed)"
+       fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+       with_libmodbus="yes"
+fi
+
+# with_libmodbus_cflags and with_libmodbus_libs are set up now, let's do
+# the actual checks.
+if test "x$with_libmodbus" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
+
+       AC_CHECK_HEADERS(modbus/modbus.h, [], [with_libmodbus="no (modbus/modbus.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libmodbus" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
+       LDFLAGS="$LDFLAGS $with_libmodbus_libs"
+
+       AC_CHECK_LIB(modbus, modbus_connect,
+                    [with_libmodbus="yes"],
+                    [with_libmodbus="no (symbol modbus_connect not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libmodbus" = "xyes"
+then
+       BUILD_WITH_LIBMODBUS_CFLAGS="$with_libmodbus_cflags"
+       BUILD_WITH_LIBMODBUS_LIBS="$with_libmodbus_libs"
+       AC_SUBST(BUILD_WITH_LIBMODBUS_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBMODBUS_LIBS)
+fi
+# }}}
+
+# --with-libmongoc {{{
+AC_ARG_WITH(libmongoc, [AS_HELP_STRING([--with-libmongoc@<:@=PREFIX@:>@], [Path to libmongoc.])],
+[
+ if test "x$withval" = "xyes"
+ then
+        with_libmongoc="yes"
+ else if test "x$withval" = "xno"
+ then
+        with_libmongoc="no"
+ else
+        with_libmongoc="yes"
+        LIBMONGOC_CPPFLAGS="$LIBMONGOC_CPPFLAGS -I$withval/include"
+        LIBMONGOC_LDFLAGS="$LIBMONGOC_LDFLAGS -L$withval/lib"
+ fi; fi
+],
+[with_libmongoc="yes"])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $LIBMONGOC_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBMONGOC_LDFLAGS"
+
+if test "x$with_libmongoc" = "xyes"
+then
+       if test "x$LIBMONGOC_CPPFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([libmongoc CPPFLAGS: $LIBMONGOC_CPPFLAGS])
+       fi
+       AC_CHECK_HEADERS(mongo.h,
+       [with_libmongoc="yes"],
+       [with_libmongoc="no ('mongo.h' not found)"],
+[#if HAVE_STDINT_H
+# define MONGO_HAVE_STDINT 1
+#else
+# define MONGO_USE_LONG_LONG_INT 1
+#endif
+])
+fi
+if test "x$with_libmongoc" = "xyes"
+then
+       if test "x$LIBMONGOC_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([libmongoc LDFLAGS: $LIBMONGOC_LDFLAGS])
+       fi
+       AC_CHECK_LIB(mongoc, mongo_run_command,
+       [with_libmongoc="yes"],
+       [with_libmongoc="no (symbol 'mongo_run_command' not found)"])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libmongoc" = "xyes"
+then
+       BUILD_WITH_LIBMONGOC_CPPFLAGS="$LIBMONGOC_CPPFLAGS"
+       BUILD_WITH_LIBMONGOC_LDFLAGS="$LIBMONGOC_LDFLAGS"
+       AC_SUBST(BUILD_WITH_LIBMONGOC_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBMONGOC_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBMONGOC, test "x$with_libmongoc" = "xyes")
+# }}}
+
+# --with-libmysql {{{
+with_mysql_config="mysql_config"
+with_mysql_cflags=""
+with_mysql_libs=""
+AC_ARG_WITH(libmysql, [AS_HELP_STRING([--with-libmysql@<:@=PREFIX@:>@], [Path to libmysql.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libmysql="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libmysql="yes"
+       else
+               if test -f "$withval" && test -x "$withval";
+               then
+                       with_mysql_config="$withval"
+               else if test -x "$withval/bin/mysql_config"
+               then
+                       with_mysql_config="$withval/bin/mysql_config"
+               fi; fi
+               with_libmysql="yes"
+       fi; fi
+],
+[
+       with_libmysql="yes"
+])
+if test "x$with_libmysql" = "xyes"
+then
+       with_mysql_cflags=`$with_mysql_config --cflags 2>/dev/null`
+       mysql_config_status=$?
+
+       if test $mysql_config_status -ne 0
+       then
+               with_libmysql="no ($with_mysql_config failed)"
+       else
+               SAVE_CPPFLAGS="$CPPFLAGS"
+               CPPFLAGS="$CPPFLAGS $with_mysql_cflags"
+
+               have_mysql_h="no"
+               have_mysql_mysql_h="no"
+               AC_CHECK_HEADERS(mysql.h, [have_mysql_h="yes"])
+
+               if test "x$have_mysql_h" = "xno"
+               then
+                       AC_CHECK_HEADERS(mysql/mysql.h, [have_mysql_mysql_h="yes"])
+               fi
+
+               if test "x$have_mysql_h$have_mysql_mysql_h" = "xnono"
+               then
+                       with_libmysql="no (mysql.h not found)"
+               fi
+
+               CPPFLAGS="$SAVE_CPPFLAGS"
+       fi
+fi
+if test "x$with_libmysql" = "xyes"
+then
+       with_mysql_libs=`$with_mysql_config --libs_r 2>/dev/null`
+       mysql_config_status=$?
+
+       if test $mysql_config_status -ne 0
+       then
+               with_libmysql="no ($with_mysql_config failed)"
+       else
+               AC_CHECK_LIB(mysqlclient, mysql_init,
+                [with_libmysql="yes"],
+                [with_libmysql="no (symbol 'mysql_init' not found)"],
+                [$with_mysql_libs])
+
+               AC_CHECK_LIB(mysqlclient, mysql_get_server_version,
+                [with_libmysql="yes"],
+                [with_libmysql="no (symbol 'mysql_get_server_version' not found)"],
+                [$with_mysql_libs])
+       fi
+fi
+if test "x$with_libmysql" = "xyes"
+then
+       BUILD_WITH_LIBMYSQL_CFLAGS="$with_mysql_cflags"
+       BUILD_WITH_LIBMYSQL_LIBS="$with_mysql_libs"
+       AC_SUBST(BUILD_WITH_LIBMYSQL_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBMYSQL_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBMYSQL, test "x$with_libmysql" = "xyes")
+# }}}
+
+# --with-libmnl {{{
+with_libmnl_cflags=""
+with_libmnl_libs=""
+AC_ARG_WITH(libmnl, [AS_HELP_STRING([--with-libmnl@<:@=PREFIX@:>@], [Path to libmnl.])],
+[
+ echo "libmnl: withval = $withval"
+ if test "x$withval" = "xyes"
+ then
+        with_libmnl="yes"
+ else if test "x$withval" = "xno"
+ then
+        with_libmnl="no"
+ else
+        if test -d "$withval/include"
+        then
+                with_libmnl_cflags="-I$withval/include"
+                with_libmnl_libs="-L$withval/lib -lmnl"
+                with_libmnl="yes"
+        else
+                AC_MSG_ERROR("no such directory: $withval/include")
+        fi
+ fi; fi
+],
+[
+ if test "x$ac_system" = "xLinux"
+ then
+        with_libmnl="yes"
+ else
+        with_libmnl="no (Linux only library)"
+ fi
+])
+if test "x$PKG_CONFIG" = "x"
+then
+       with_libmnl="no (Don't have pkg-config)"
+fi
+if test "x$with_libmnl" = "xyes"
+then
+       if $PKG_CONFIG --exists libmnl 2>/dev/null; then
+         with_libmnl_cflags="$with_libmnl_ldflags `$PKG_CONFIG --cflags libmnl`"
+         with_libmnl_libs="$with_libmnl_libs `$PKG_CONFIG --libs libmnl`"
+       fi
+
+       AC_CHECK_HEADERS(libmnl.h libmnl/libmnl.h,
+       [
+        with_libmnl="yes"
+        break
+       ], [],
+[#include <stdio.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>])
+       AC_CHECK_HEADERS(linux/gen_stats.h linux/pkt_sched.h, [], [],
+[#include <stdio.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <sys/socket.h>])
+
+        AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[
+#include <stdio.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+]],
+[[
+int retval = TCA_STATS2;
+return (retval);
+]]
+       )],
+       [AC_DEFINE([HAVE_TCA_STATS2], [1], [True if the enum-member TCA_STATS2 exists])])
+
+       AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[
+#include <stdio.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+]],
+[[
+int retval = TCA_STATS;
+return (retval);
+]]
+       )],
+       [AC_DEFINE([HAVE_TCA_STATS], 1, [True if the enum-member TCA_STATS exists])])
+fi
+if test "x$with_libmnl" = "xyes"
+then
+       AC_CHECK_MEMBERS([struct rtnl_link_stats64.tx_window_errors],
+       [AC_DEFINE(HAVE_RTNL_LINK_STATS64, 1, [Define if struct rtnl_link_stats64 exists and is usable.])],
+       [],
+       [
+       #include <linux/if_link.h>
+       ])
+fi
+if test "x$with_libmnl" = "xyes"
+then
+       AC_CHECK_LIB(mnl, mnl_nlmsg_get_payload,
+                    [with_libmnl="yes"],
+                    [with_libmnl="no (symbol 'mnl_nlmsg_get_payload' not found)"],
+                    [$with_libmnl_libs])
+fi
+if test "x$with_libmnl" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBMNL, 1, [Define if libmnl is present and usable.])
+       BUILD_WITH_LIBMNL_CFLAGS="$with_libmnl_cflags"
+       BUILD_WITH_LIBMNL_LIBS="$with_libmnl_libs"
+       AC_SUBST(BUILD_WITH_LIBMNL_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBMNL_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBMNL, test "x$with_libmnl" = "xyes")
+# }}}
+
+# --with-libnetapp {{{
+AC_ARG_VAR([LIBNETAPP_CPPFLAGS], [C preprocessor flags required to build with libnetapp])
+AC_ARG_VAR([LIBNETAPP_LDFLAGS],  [Linker flags required to build with libnetapp])
+AC_ARG_VAR([LIBNETAPP_LIBS],     [Other libraries required to link against libnetapp])
+LIBNETAPP_CPPFLAGS="$LIBNETAPP_CPPFLAGS"
+LIBNETAPP_LDFLAGS="$LIBNETAPP_LDFLAGS"
+LIBNETAPP_LIBS="$LIBNETAPP_LIBS"
+AC_ARG_WITH(libnetapp, [AS_HELP_STRING([--with-libnetapp@<:@=PREFIX@:>@], [Path to libnetapp.])],
+[
+ if test -d "$withval"
+ then
+        LIBNETAPP_CPPFLAGS="$LIBNETAPP_CPPFLAGS -I$withval/include"
+        LIBNETAPP_LDFLAGS="$LIBNETAPP_LDFLAGS -L$withval/lib"
+        with_libnetapp="yes"
+ else
+        with_libnetapp="$withval"
+ fi
+],
+[
+ with_libnetapp="yes"
+])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $LIBNETAPP_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBNETAPP_LDFLAGS"
+
+if test "x$with_libnetapp" = "xyes"
+then
+       if test "x$LIBNETAPP_CPPFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([netapp CPPFLAGS: $LIBNETAPP_CPPFLAGS])
+       fi
+       AC_CHECK_HEADERS(netapp_api.h,
+               [with_libnetapp="yes"],
+               [with_libnetapp="no (netapp_api.h not found)"])
+fi
+
+if test "x$with_libnetapp" = "xyes"
+then
+       if test "x$LIBNETAPP_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([netapp LDFLAGS: $LIBNETAPP_LDFLAGS])
+       fi
+
+       if test "x$LIBNETAPP_LIBS" = "x"
+       then
+               LIBNETAPP_LIBS="-lpthread -lxml -ladt -lssl -lm -lcrypto -lz"
+       fi
+       AC_MSG_NOTICE([netapp LIBS: $LIBNETAPP_LIBS])
+
+       AC_CHECK_LIB(netapp, na_server_invoke_elem,
+               [with_libnetapp="yes"],
+               [with_libnetapp="no (symbol na_server_invoke_elem not found)"],
+               [$LIBNETAPP_LIBS])
+       LIBNETAPP_LIBS="-lnetapp $LIBNETAPP_LIBS"
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libnetapp" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBNETAPP, 1, [Define to 1 if you have the netapp library (-lnetapp).])
+fi
+
+AC_SUBST(LIBNETAPP_CPPFLAGS)
+AC_SUBST(LIBNETAPP_LDFLAGS)
+AC_SUBST(LIBNETAPP_LIBS)
+AM_CONDITIONAL(BUILD_WITH_LIBNETAPP, test "x$with_libnetapp" = "xyes")
+# }}}
+
+# --with-libnetsnmp {{{
+with_snmp_config="net-snmp-config"
+with_snmp_cflags=""
+with_snmp_libs=""
+AC_ARG_WITH(libnetsnmp, [AS_HELP_STRING([--with-libnetsnmp@<:@=PREFIX@:>@], [Path to the Net-SNMPD library.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libnetsnmp="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libnetsnmp="yes"
+       else
+               if test -x "$withval"
+               then
+                       with_snmp_config="$withval"
+                       with_libnetsnmp="yes"
+               else
+                       with_snmp_config="$withval/bin/net-snmp-config"
+                       with_libnetsnmp="yes"
+               fi
+       fi; fi
+],
+[with_libnetsnmp="yes"])
+if test "x$with_libnetsnmp" = "xyes"
+then
+       with_snmp_cflags=`$with_snmp_config --cflags 2>/dev/null`
+       snmp_config_status=$?
+
+       if test $snmp_config_status -ne 0
+       then
+               with_libnetsnmp="no ($with_snmp_config failed)"
+       else
+               SAVE_CPPFLAGS="$CPPFLAGS"
+               CPPFLAGS="$CPPFLAGS $with_snmp_cflags"
+
+               AC_CHECK_HEADERS(net-snmp/net-snmp-config.h, [], [with_libnetsnmp="no (net-snmp/net-snmp-config.h not found)"])
+
+               CPPFLAGS="$SAVE_CPPFLAGS"
+       fi
+fi
+if test "x$with_libnetsnmp" = "xyes"
+then
+       with_snmp_libs=`$with_snmp_config --libs 2>/dev/null`
+       snmp_config_status=$?
+
+       if test $snmp_config_status -ne 0
+       then
+               with_libnetsnmp="no ($with_snmp_config failed)"
+       else
+               AC_CHECK_LIB(netsnmp, init_snmp,
+               [with_libnetsnmp="yes"],
+               [with_libnetsnmp="no (libnetsnmp not found)"],
+               [$with_snmp_libs])
+       fi
+fi
+if test "x$with_libnetsnmp" = "xyes"
+then
+       BUILD_WITH_LIBSNMP_CFLAGS="$with_snmp_cflags"
+       BUILD_WITH_LIBSNMP_LIBS="$with_snmp_libs"
+       AC_SUBST(BUILD_WITH_LIBSNMP_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBSNMP_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBNETSNMP, test "x$with_libnetsnmp" = "xyes")
+# }}}
+
+# --with-liboconfig {{{
+with_own_liboconfig="no"
+liboconfig_LDFLAGS="$LDFLAGS"
+liboconfig_CPPFLAGS="$CPPFLAGS"
+AC_ARG_WITH(liboconfig, [AS_HELP_STRING([--with-liboconfig@<:@=PREFIX@:>@], [Path to liboconfig.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               if test -d "$withval/lib"
+               then
+                       liboconfig_LDFLAGS="$LDFLAGS -L$withval/lib"
+               fi
+               if test -d "$withval/include"
+               then
+                       liboconfig_CPPFLAGS="$CPPFLAGS -I$withval/include"
+               fi
+       fi
+       if test "x$withval" = "xno"
+       then
+               AC_MSG_ERROR("liboconfig is required")
+       fi
+],
+[
+       with_liboconfig="yes"
+])
+
+save_LDFLAGS="$LDFLAGS"
+save_CPPFLAGS="$CPPFLAGS"
+LDFLAGS="$liboconfig_LDFLAGS"
+CPPFLAGS="$liboconfig_CPPFLAGS"
+AC_CHECK_LIB(oconfig, oconfig_parse_fh,
+[
+       with_liboconfig="yes"
+       with_own_liboconfig="no"
+],
+[
+       with_liboconfig="yes"
+       with_own_liboconfig="yes"
+       LDFLAGS="$save_LDFLAGS"
+       CPPFLAGS="$save_CPPFLAGS"
+])
+
+AM_CONDITIONAL(BUILD_WITH_OWN_LIBOCONFIG, test "x$with_own_liboconfig" = "xyes")
+if test "x$with_own_liboconfig" = "xyes"
+then
+       with_liboconfig="yes (shipped version)"
+fi
+# }}}
+
+# --with-liboping {{{
+AC_ARG_WITH(liboping, [AS_HELP_STRING([--with-liboping@<:@=PREFIX@:>@], [Path to liboping.])],
+[
+ if test "x$withval" = "xyes"
+ then
+        with_liboping="yes"
+ else if test "x$withval" = "xno"
+ then
+        with_liboping="no"
+ else
+        with_liboping="yes"
+        LIBOPING_CPPFLAGS="$LIBOPING_CPPFLAGS -I$withval/include"
+        LIBOPING_LDFLAGS="$LIBOPING_LDFLAGS -L$withval/lib"
+ fi; fi
+],
+[with_liboping="yes"])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $LIBOPING_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBOPING_LDFLAGS"
+
+if test "x$with_liboping" = "xyes"
+then
+       if test "x$LIBOPING_CPPFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([liboping CPPFLAGS: $LIBOPING_CPPFLAGS])
+       fi
+       AC_CHECK_HEADERS(oping.h,
+       [with_liboping="yes"],
+       [with_liboping="no (oping.h not found)"])
+fi
+if test "x$with_liboping" = "xyes"
+then
+       if test "x$LIBOPING_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([liboping LDFLAGS: $LIBOPING_LDFLAGS])
+       fi
+       AC_CHECK_LIB(oping, ping_construct,
+       [with_liboping="yes"],
+       [with_liboping="no (symbol 'ping_construct' not found)"])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_liboping" = "xyes"
+then
+       BUILD_WITH_LIBOPING_CPPFLAGS="$LIBOPING_CPPFLAGS"
+       BUILD_WITH_LIBOPING_LDFLAGS="$LIBOPING_LDFLAGS"
+       AC_SUBST(BUILD_WITH_LIBOPING_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBOPING_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBOPING, test "x$with_liboping" = "xyes")
+# }}}
+
+# --with-oracle {{{
+with_oracle_cppflags=""
+with_oracle_libs=""
+AC_ARG_WITH(oracle, [AS_HELP_STRING([--with-oracle@<:@=ORACLE_HOME@:>@], [Path to Oracle.])],
+[
+       if test "x$withval" = "xyes"
+       then
+               if test "x$ORACLE_HOME" = "x"
+               then
+                       AC_MSG_WARN([Use of the Oracle library has been forced, but the environment variable ORACLE_HOME is not set.])
+               fi
+               with_oracle="yes"
+       else if test "x$withval" = "xno"
+       then
+               with_oracle="no"
+       else
+               with_oracle="yes"
+               ORACLE_HOME="$withval"
+       fi; fi
+],
+[
+       if test "x$ORACLE_HOME" = "x"
+       then
+               with_oracle="no (ORACLE_HOME is not set)"
+       else
+               with_oracle="yes"
+       fi
+])
+if test "x$ORACLE_HOME" != "x"
+then
+       with_oracle_cppflags="-I$ORACLE_HOME/rdbms/public"
+
+       if test -e "$ORACLE_HOME/lib/ldflags"
+       then
+               with_oracle_libs=`cat "$ORACLE_HOME/lib/ldflags"`
+       fi
+       #with_oracle_libs="-L$ORACLE_HOME/lib $with_oracle_libs -lclntsh"
+       with_oracle_libs="-L$ORACLE_HOME/lib -lclntsh"
+fi
+if test "x$with_oracle" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_oracle_cppflags"
+
+       AC_CHECK_HEADERS(oci.h, [with_oracle="yes"], [with_oracle="no (oci.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_oracle" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_oracle_cppflags"
+       LDFLAGS="$LDFLAGS $with_oracle_libs"
+
+       AC_CHECK_FUNC(OCIEnvCreate, [with_oracle="yes"], [with_oracle="no (Symbol 'OCIEnvCreate' not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_oracle" = "xyes"
+then
+       BUILD_WITH_ORACLE_CFLAGS="$with_oracle_cppflags"
+       BUILD_WITH_ORACLE_LIBS="$with_oracle_libs"
+       AC_SUBST(BUILD_WITH_ORACLE_CFLAGS)
+       AC_SUBST(BUILD_WITH_ORACLE_LIBS)
+fi
+# }}}
+
+# --with-libowcapi {{{
+with_libowcapi_cppflags=""
+with_libowcapi_libs="-lowcapi"
+AC_ARG_WITH(libowcapi, [AS_HELP_STRING([--with-libowcapi@<:@=PREFIX@:>@], [Path to libowcapi.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               with_libowcapi_cppflags="-I$withval/include"
+               with_libowcapi_libs="-L$withval/lib -lowcapi"
+               with_libowcapi="yes"
+       else
+               with_libowcapi="$withval"
+       fi
+],
+[
+       with_libowcapi="yes"
+])
+if test "x$with_libowcapi" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$with_libowcapi_cppflags"
+
+       AC_CHECK_HEADERS(owcapi.h, [with_libowcapi="yes"], [with_libowcapi="no (owcapi.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libowcapi" = "xyes"
+then
+       SAVE_LDFLAGS="$LDFLAGS"
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       LDFLAGS="$with_libowcapi_libs"
+       CPPFLAGS="$with_libowcapi_cppflags"
+
+       AC_CHECK_LIB(owcapi, OW_get, [with_libowcapi="yes"], [with_libowcapi="no (libowcapi not found)"])
+
+       LDFLAGS="$SAVE_LDFLAGS"
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libowcapi" = "xyes"
+then
+       BUILD_WITH_LIBOWCAPI_CPPFLAGS="$with_libowcapi_cppflags"
+       BUILD_WITH_LIBOWCAPI_LIBS="$with_libowcapi_libs"
+       AC_SUBST(BUILD_WITH_LIBOWCAPI_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBOWCAPI_LIBS)
+fi
+# }}}
+
+# --with-libpcap {{{
+AC_ARG_WITH(libpcap, [AS_HELP_STRING([--with-libpcap@<:@=PREFIX@:>@], [Path to libpcap.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_libpcap="yes"
+       else
+               with_libpcap="$withval"
+       fi
+],
+[
+       with_libpcap="yes"
+])
+if test "x$with_libpcap" = "xyes"
+then
+       AC_CHECK_LIB(pcap, pcap_open_live,
+       [
+               AC_DEFINE(HAVE_LIBPCAP, 1, [Define to 1 if you have the pcap library (-lpcap).])
+       ], [with_libpcap="no (libpcap not found)"])
+fi
+if test "x$with_libpcap" = "xyes"
+then
+       AC_CHECK_HEADERS(pcap.h,,
+                        [with_libpcap="no (pcap.h not found)"])
+fi
+if test "x$with_libpcap" = "xyes"
+then
+       AC_CHECK_HEADERS(pcap-bpf.h,,
+                        [with_libpcap="no (pcap-bpf.h not found)"])
+fi
+if test "x$with_libpcap" = "xyes"
+then
+       AC_CACHE_CHECK([whether libpcap has PCAP_ERROR_IFACE_NOT_UP],
+                      [c_cv_libpcap_have_pcap_error_iface_not_up],
+                      AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <pcap.h>
+]]],
+[[[
+  int val = PCAP_ERROR_IFACE_NOT_UP;
+]]]
+                      )],
+                      [c_cv_libpcap_have_pcap_error_iface_not_up="yes"],
+                      [c_cv_libpcap_have_pcap_error_iface_not_up="no"]))
+fi
+if test "x$c_cv_libpcap_have_pcap_error_iface_not_up" != "xyes"
+then
+               with_libpcap="no (pcap.h misses PCAP_ERROR_IFACE_NOT_UP)"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes")
+# }}}
+
+# --with-libperl {{{
+perl_interpreter="perl"
+AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
+[
+       if test -f "$withval" && test -x "$withval"
+       then
+               perl_interpreter="$withval"
+               with_libperl="yes"
+       else if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               perl_interpreter="$withval/bin/perl"
+               with_libperl="yes"
+       else
+               with_libperl="$withval"
+       fi; fi
+],
+[
+       with_libperl="yes"
+])
+
+AC_MSG_CHECKING([for perl])
+perl_interpreter=`which "$perl_interpreter" 2> /dev/null`
+if test -x "$perl_interpreter"
+then
+       AC_MSG_RESULT([yes ($perl_interpreter)])
+else
+       perl_interpreter=""
+       AC_MSG_RESULT([no])
+fi
+
+AC_SUBST(PERL, "$perl_interpreter")
+
+if test "x$with_libperl" = "xyes" \
+       && test -n "$perl_interpreter"
+then
+  SAVE_CFLAGS="$CFLAGS"
+  SAVE_LDFLAGS="$LDFLAGS"
+dnl ARCHFLAGS="" -> disable multi -arch on OSX (see Config_heavy.pl:fetch_string)
+  PERL_CFLAGS=`ARCHFLAGS="" $perl_interpreter -MExtUtils::Embed -e ccopts`
+  PERL_LDFLAGS=`ARCHFLAGS="" $perl_interpreter -MExtUtils::Embed -e ldopts`
+  CFLAGS="$CFLAGS $PERL_CFLAGS"
+  LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
+
+  AC_CACHE_CHECK([for libperl],
+    [c_cv_have_libperl],
+    AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[[
+#define PERL_NO_GET_CONTEXT
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+]]],
+[[[
+       dTHX;
+       load_module (PERL_LOADMOD_NOIMPORT,
+                        newSVpv ("Collectd::Plugin::FooBar", 24),
+                        Nullsv);
+]]]
+      )],
+      [c_cv_have_libperl="yes"],
+      [c_cv_have_libperl="no"]
+    )
+  )
+
+  if test "x$c_cv_have_libperl" = "xyes"
+  then
+         AC_DEFINE(HAVE_LIBPERL, 1, [Define if libperl is present and usable.])
+         AC_SUBST(PERL_CFLAGS)
+         AC_SUBST(PERL_LDFLAGS)
+  else
+         with_libperl="no"
+  fi
+
+  CFLAGS="$SAVE_CFLAGS"
+  LDFLAGS="$SAVE_LDFLAGS"
+else if test -z "$perl_interpreter"; then
+  with_libperl="no (no perl interpreter found)"
+  c_cv_have_libperl="no"
+fi; fi
+AM_CONDITIONAL(BUILD_WITH_LIBPERL, test "x$with_libperl" = "xyes")
+
+if test "x$with_libperl" = "xyes"
+then
+       SAVE_CFLAGS="$CFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CFLAGS="$CFLAGS $PERL_CFLAGS"
+       LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
+
+       AC_CACHE_CHECK([if perl supports ithreads],
+               [c_cv_have_perl_ithreads],
+               AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+
+#if !defined(USE_ITHREADS)
+# error "Perl does not support ithreads!"
+#endif /* !defined(USE_ITHREADS) */
+]]],
+[[[ ]]]
+                       )],
+                       [c_cv_have_perl_ithreads="yes"],
+                       [c_cv_have_perl_ithreads="no"]
+               )
+       )
+
+       if test "x$c_cv_have_perl_ithreads" = "xyes"
+       then
+               AC_DEFINE(HAVE_PERL_ITHREADS, 1, [Define if Perl supports ithreads.])
+       fi
+
+       CFLAGS="$SAVE_CFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+
+if test "x$with_libperl" = "xyes"
+then
+       SAVE_CFLAGS="$CFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       # trigger an error if Perl_load_module*() uses __attribute__nonnull__(3)
+       # (see issues #41 and #42)
+       CFLAGS="$CFLAGS $PERL_CFLAGS -Wall -Werror"
+       LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
+
+       AC_CACHE_CHECK([for broken Perl_load_module()],
+               [c_cv_have_broken_perl_load_module],
+               AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[[
+#define PERL_NO_GET_CONTEXT
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+]]],
+[[[
+                        dTHX;
+                        load_module (PERL_LOADMOD_NOIMPORT,
+                            newSVpv ("Collectd::Plugin::FooBar", 24),
+                            Nullsv);
+]]]
+                       )],
+                       [c_cv_have_broken_perl_load_module="no"],
+                       [c_cv_have_broken_perl_load_module="yes"]
+               )
+       )
+
+       CFLAGS="$SAVE_CFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+AM_CONDITIONAL(HAVE_BROKEN_PERL_LOAD_MODULE,
+               test "x$c_cv_have_broken_perl_load_module" = "xyes")
+
+if test "x$with_libperl" = "xyes"
+then
+       SAVE_CFLAGS="$CFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CFLAGS="$CFLAGS $PERL_CFLAGS"
+       LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
+
+       AC_CHECK_MEMBER(
+               [struct mgvtbl.svt_local],
+               [have_struct_mgvtbl_svt_local="yes"],
+               [have_struct_mgvtbl_svt_local="no"],
+               [
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+               ])
+
+       if test "x$have_struct_mgvtbl_svt_local" = "xyes"
+       then
+               AC_DEFINE(HAVE_PERL_STRUCT_MGVTBL_SVT_LOCAL, 1,
+                                 [Define if Perl's struct mgvtbl has member svt_local.])
+       fi
+
+       CFLAGS="$SAVE_CFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+# }}}
+
+# --with-libpq {{{
+with_pg_config="pg_config"
+with_libpq_includedir=""
+with_libpq_libdir=""
+with_libpq_cppflags=""
+with_libpq_ldflags=""
+AC_ARG_WITH(libpq, [AS_HELP_STRING([--with-libpq@<:@=PREFIX@:>@],
+       [Path to libpq.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libpq="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libpq="yes"
+       else
+               if test -f "$withval" && test -x "$withval";
+               then
+                       with_pg_config="$withval"
+               else if test -x "$withval/bin/pg_config"
+               then
+                       with_pg_config="$withval/bin/pg_config"
+               fi; fi
+               with_libpq="yes"
+       fi; fi
+],
+[
+       with_libpq="yes"
+])
+if test "x$with_libpq" = "xyes"
+then
+       with_libpq_includedir=`$with_pg_config --includedir 2> /dev/null`
+       pg_config_status=$?
+
+       if test $pg_config_status -eq 0
+       then
+               if test -n "$with_libpq_includedir"; then
+                       for dir in $with_libpq_includedir; do
+                               with_libpq_cppflags="$with_libpq_cppflags -I$dir"
+                       done
+               fi
+       else
+               AC_MSG_WARN([$with_pg_config returned with status $pg_config_status])
+       fi
+
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libpq_cppflags"
+
+       AC_CHECK_HEADERS(libpq-fe.h, [],
+               [with_libpq="no (libpq-fe.h not found)"], [])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libpq" = "xyes"
+then
+       with_libpq_libdir=`$with_pg_config --libdir 2> /dev/null`
+       pg_config_status=$?
+
+       if test $pg_config_status -eq 0
+       then
+               if test -n "$with_libpq_libdir"; then
+                       for dir in $with_libpq_libdir; do
+                               with_libpq_ldflags="$with_libpq_ldflags -L$dir"
+                       done
+               fi
+       else
+               AC_MSG_WARN([$with_pg_config returned with status $pg_config_status])
+       fi
+
+       SAVE_LDFLAGS="$LDFLAGS"
+       LDFLAGS="$LDFLAGS $with_libpq_ldflags"
+
+       AC_CHECK_LIB(pq, PQconnectdb,
+               [with_libpq="yes"],
+               [with_libpq="no (symbol 'PQconnectdb' not found)"])
+
+       AC_CHECK_LIB(pq, PQserverVersion,
+               [with_libpq="yes"],
+               [with_libpq="no (symbol 'PQserverVersion' not found)"])
+
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libpq" = "xyes"
+then
+       BUILD_WITH_LIBPQ_CPPFLAGS="$with_libpq_cppflags"
+       BUILD_WITH_LIBPQ_LDFLAGS="$with_libpq_ldflags"
+       AC_SUBST(BUILD_WITH_LIBPQ_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBPQ_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBPQ, test "x$with_libpq" = "xyes")
+# }}}
+
+# --with-libpthread {{{
+AC_ARG_WITH(libpthread, [AS_HELP_STRING([--with-libpthread=@<:@=PREFIX@:>@], [Path to libpthread.])],
+[      if test "x$withval" != "xno" \
+               && test "x$withval" != "xyes"
+       then
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_libpthread="yes"
+       else
+               if test "x$withval" = "xno"
+               then
+                       with_libpthread="no (disabled)"
+               fi
+       fi
+], [with_libpthread="yes"])
+if test "x$with_libpthread" = "xyes"
+then
+       AC_CHECK_LIB(pthread, pthread_create, [with_libpthread="yes"], [with_libpthread="no (libpthread not found)"], [])
+fi
+
+if test "x$with_libpthread" = "xyes"
+then
+       AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"])
+fi
+if test "x$with_libpthread" = "xyes"
+then
+       collect_pthread=1
+else
+       collect_pthread=0
+fi
+AC_DEFINE_UNQUOTED(HAVE_LIBPTHREAD, [$collect_pthread],
+       [Wether or not to use pthread (POSIX threads) library])
+AM_CONDITIONAL(BUILD_WITH_LIBPTHREAD, test "x$with_libpthread" = "xyes")
+# }}}
+
+# --with-python {{{
+with_python_prog=""
+with_python_path="$PATH"
+AC_ARG_WITH(python, [AS_HELP_STRING([--with-python@<:@=PREFIX@:>@], [Path to the python interpreter.])],
+[
+ if test "x$withval" = "xyes" || test "x$withval" = "xno"
+ then
+        with_python="$withval"
+ else if test -x "$withval"
+ then
+        with_python_prog="$withval"
+        with_python_path="`dirname \"$withval\"`$PATH_SEPARATOR$with_python_path"
+        with_python="yes"
+ else if test -d "$withval"
+ then
+        with_python_path="$withval$PATH_SEPARATOR$with_python_path"
+        with_python="yes"
+ else
+        AC_MSG_WARN([Argument not recognized: $withval])
+ fi; fi; fi
+], [with_python="yes"])
+
+SAVE_PATH="$PATH"
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+SAVE_LIBS="$LIBS"
+
+PATH="$with_python_path"
+
+if test "x$with_python" = "xyes" && test "x$with_python_prog" = "x"
+then
+       AC_MSG_CHECKING([for python])
+       with_python_prog="`which python 2>/dev/null`"
+       if test "x$with_python_prog" = "x"
+       then
+               AC_MSG_RESULT([not found])
+               with_python="no (interpreter not found)"
+       else
+               AC_MSG_RESULT([$with_python_prog])
+       fi
+fi
+
+if test "x$with_python" = "xyes"
+then
+       AC_MSG_CHECKING([for Python CPPFLAGS])
+       python_include_path=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_python_inc())" | "$with_python_prog" 2>&1`
+       python_config_status=$?
+
+       if test "$python_config_status" -ne 0 || test "x$python_include_path" = "x"
+       then
+               AC_MSG_RESULT([failed with status $python_config_status (output: $python_include_path)])
+               with_python="no"
+       else
+               AC_MSG_RESULT([$python_include_path])
+       fi
+fi
+
+if test "x$with_python" = "xyes"
+then
+       CPPFLAGS="-I$python_include_path $CPPFLAGS"
+       AC_CHECK_HEADERS(Python.h,
+                        [with_python="yes"],
+                        [with_python="no ('Python.h' not found)"])
+fi
+
+if test "x$with_python" = "xyes"
+then
+       AC_MSG_CHECKING([for Python LDFLAGS])
+       python_library_path=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_config_vars(\"LIBDIR\").__getitem__(0))" | "$with_python_prog" 2>&1`
+       python_config_status=$?
+
+       if test "$python_config_status" -ne 0 || test "x$python_library_path" = "x"
+       then
+               AC_MSG_RESULT([failed with status $python_config_status (output: $python_library_path)])
+               with_python="no"
+       else
+               AC_MSG_RESULT([$python_library_path])
+       fi
+fi
+
+if test "x$with_python" = "xyes"
+then
+       AC_MSG_CHECKING([for Python LIBS])
+       python_library_flags=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_config_vars(\"BLDLIBRARY\").__getitem__(0))" | "$with_python_prog" 2>&1`
+       python_config_status=$?
+
+       if test "$python_config_status" -ne 0 || test "x$python_library_flags" = "x"
+       then
+               AC_MSG_RESULT([failed with status $python_config_status (output: $python_library_flags)])
+               with_python="no"
+       else
+               AC_MSG_RESULT([$python_library_flags])
+       fi
+fi
+
+if test "x$with_python" = "xyes"
+then
+       LDFLAGS="-L$python_library_path $LDFLAGS"
+       LIBS="$python_library_flags $LIBS"
+
+       AC_CHECK_FUNC(PyObject_CallFunction,
+                     [with_python="yes"],
+                     [with_python="no (Symbol 'PyObject_CallFunction' not found)"])
+fi
+
+PATH="$SAVE_PATH"
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+LIBS="$SAVE_LIBS"
+
+if test "x$with_python" = "xyes"
+then
+       BUILD_WITH_PYTHON_CPPFLAGS="-I$python_include_path"
+       BUILD_WITH_PYTHON_LDFLAGS="-L$python_library_path"
+       BUILD_WITH_PYTHON_LIBS="$python_library_flags"
+       AC_SUBST(BUILD_WITH_PYTHON_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_PYTHON_LDFLAGS)
+       AC_SUBST(BUILD_WITH_PYTHON_LIBS)
+fi
+# }}} --with-python
+
+# --with-librabbitmq {{{
+with_librabbitmq_cppflags=""
+with_librabbitmq_ldflags=""
+AC_ARG_WITH(librabbitmq, [AS_HELP_STRING([--with-librabbitmq@<:@=PREFIX@:>@], [Path to librabbitmq.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               with_librabbitmq_cppflags="-I$withval/include"
+               with_librabbitmq_ldflags="-L$withval/lib"
+               with_librabbitmq="yes"
+       else
+               with_librabbitmq="$withval"
+       fi
+],
+[
+       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
+       AC_CHECK_HEADERS(amqp.h, [with_librabbitmq="yes"], [with_librabbitmq="no (amqp.h not found)"])
+fi
+if test "x$with_librabbitmq" = "xyes"
+then
+       # 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)"])
+fi
+if test "x$with_librabbitmq" = "xyes"
+then
+       BUILD_WITH_LIBRABBITMQ_CPPFLAGS="$with_librabbitmq_cppflags"
+       BUILD_WITH_LIBRABBITMQ_LDFLAGS="$with_librabbitmq_ldflags"
+       BUILD_WITH_LIBRABBITMQ_LIBS="-lrabbitmq"
+       AC_SUBST(BUILD_WITH_LIBRABBITMQ_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBRABBITMQ_LDFLAGS)
+       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")
+# }}}
+
+# --with-librdkafka {{{
+AC_ARG_WITH(librdkafka, [AS_HELP_STRING([--with-librdkafka@<:@=PREFIX@:>@], [Path to librdkafka.])],
+[
+  if test "x$withval" = "xno" && test "x$withval" != "xyes"
+  then
+    with_librdkafka_cppflags="-I$withval/include"
+    with_librdkafka_ldflags="-L$withval/lib"
+    with_librdkafka="yes"
+  else
+    with_librdkafka="$withval"
+  fi
+],
+[
+  with_librdkafka="yes"
+])
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+if test "x$with_librdkafka" = "xyes"
+then
+       AC_CHECK_HEADERS(librdkafka/rdkafka.h, [with_librdkafka="yes"], [with_librdkafka="no (librdkafka/rdkafka.h not found)"])
+fi
+
+if test "x$with_librdkafka" = "xyes"
+then
+       AC_CHECK_LIB(rdkafka, rd_kafka_new, [with_librdkafka="yes"], [with_librdkafka="no (Symbol 'rd_kafka_new' not found)"])
+fi
+if test "x$with_librdkafka" = "xyes"
+then
+       BUILD_WITH_LIBRDKAFKA_CPPFLAGS="$with_librdkafka_cppflags"
+       BUILD_WITH_LIBRDKAFKA_LDFLAGS="$with_librdkafka_ldflags"
+       BUILD_WITH_LIBRDKAFKA_LIBS="-lrdkafka"
+       AC_SUBST(BUILD_WITH_LIBRDKAFKA_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBRDKAFKA_LDFLAGS)
+       AC_SUBST(BUILD_WITH_LIBRDKAFKA_LIBS)
+       AC_DEFINE(HAVE_LIBRDKAFKA, 1, [Define if librdkafka is present and usable.])
+fi
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+AM_CONDITIONAL(BUILD_WITH_LIBRDKAFKA, test "x$with_librdkafka" = "xyes")
+
+# }}}
+
+# --with-librouteros {{{
+AC_ARG_WITH(librouteros, [AS_HELP_STRING([--with-librouteros@<:@=PREFIX@:>@], [Path to librouteros.])],
+[
+ if test "x$withval" = "xyes"
+ then
+        with_librouteros="yes"
+ else if test "x$withval" = "xno"
+ then
+        with_librouteros="no"
+ else
+        with_librouteros="yes"
+        LIBROUTEROS_CPPFLAGS="$LIBROUTEROS_CPPFLAGS -I$withval/include"
+        LIBROUTEROS_LDFLAGS="$LIBROUTEROS_LDFLAGS -L$withval/lib"
+ fi; fi
+],
+[with_librouteros="yes"])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $LIBROUTEROS_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBROUTEROS_LDFLAGS"
+
+if test "x$with_librouteros" = "xyes"
+then
+       if test "x$LIBROUTEROS_CPPFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([librouteros CPPFLAGS: $LIBROUTEROS_CPPFLAGS])
+       fi
+       AC_CHECK_HEADERS(routeros_api.h,
+       [with_librouteros="yes"],
+       [with_librouteros="no (routeros_api.h not found)"])
+fi
+if test "x$with_librouteros" = "xyes"
+then
+       if test "x$LIBROUTEROS_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([librouteros LDFLAGS: $LIBROUTEROS_LDFLAGS])
+       fi
+       AC_CHECK_LIB(routeros, ros_interface,
+       [with_librouteros="yes"],
+       [with_librouteros="no (symbol 'ros_interface' not found)"])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_librouteros" = "xyes"
+then
+       BUILD_WITH_LIBROUTEROS_CPPFLAGS="$LIBROUTEROS_CPPFLAGS"
+       BUILD_WITH_LIBROUTEROS_LDFLAGS="$LIBROUTEROS_LDFLAGS"
+       AC_SUBST(BUILD_WITH_LIBROUTEROS_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBROUTEROS_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBROUTEROS, test "x$with_librouteros" = "xyes")
+# }}}
+
+# --with-librrd {{{
+# AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given])
+librrd_cflags=""
+librrd_ldflags=""
+librrd_threadsafe="yes"
+librrd_rrdc_update="no"
+AC_ARG_WITH(librrd, [AS_HELP_STRING([--with-librrd@<:@=PREFIX@:>@], [Path to rrdtool.])],
+[      if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               librrd_cflags="-I$withval/include"
+               librrd_ldflags="-L$withval/lib"
+               with_librrd="yes"
+       else
+               with_librrd="$withval"
+       fi
+], [with_librrd="yes"])
+if test "x$with_librrd" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CPPFLAGS="$CPPFLAGS $librrd_cflags"
+       LDFLAGS="$LDFLAGS $librrd_ldflags"
+
+       AC_CHECK_HEADERS(rrd.h,, [with_librrd="no (rrd.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_librrd" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CPPFLAGS="$CPPFLAGS $librrd_cflags"
+       LDFLAGS="$LDFLAGS $librrd_ldflags"
+
+       AC_CHECK_LIB(rrd_th, rrd_update_r,
+       [with_librrd="yes"
+        librrd_ldflags="$librrd_ldflags -lrrd_th -lm"
+       ],
+       [librrd_threadsafe="no"
+        AC_CHECK_LIB(rrd, rrd_update,
+        [with_librrd="yes"
+         librrd_ldflags="$librrd_ldflags -lrrd -lm"
+        ],
+        [with_librrd="no (symbol 'rrd_update' not found)"],
+        [-lm])
+       ],
+       [-lm])
+
+       if test "x$librrd_threadsafe" = "xyes"
+       then
+               AC_CHECK_LIB(rrd_th, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"])
+       else
+               AC_CHECK_LIB(rrd, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"])
+       fi
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_librrd" = "xyes"
+then
+       BUILD_WITH_LIBRRD_CFLAGS="$librrd_cflags"
+       BUILD_WITH_LIBRRD_LDFLAGS="$librrd_ldflags"
+       AC_SUBST(BUILD_WITH_LIBRRD_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBRRD_LDFLAGS)
+fi
+if test "x$librrd_threadsafe" = "xyes"
+then
+       AC_DEFINE(HAVE_THREADSAFE_LIBRRD, 1, [Define to 1 if you have the threadsafe rrd library (-lrrd_th).])
+fi
+# }}}
+
+# --with-libsensors {{{
+with_sensors_cflags=""
+with_sensors_ldflags=""
+AC_ARG_WITH(libsensors, [AS_HELP_STRING([--with-libsensors@<:@=PREFIX@:>@], [Path to lm_sensors.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libsensors="no"
+       else
+               with_libsensors="yes"
+               if test "x$withval" != "xyes"
+               then
+                       with_sensors_cflags="-I$withval/include"
+                       with_sensors_ldflags="-L$withval/lib"
+                       with_libsensors="yes"
+               fi
+       fi
+],
+[
+       if test "x$ac_system" = "xLinux"
+       then
+               with_libsensors="yes"
+       else
+               with_libsensors="no (Linux only library)"
+       fi
+])
+if test "x$with_libsensors" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
+
+#      AC_CHECK_HEADERS(sensors/sensors.h,
+#      [
+#              AC_DEFINE(HAVE_SENSORS_SENSORS_H, 1, [Define to 1 if you have the <sensors/sensors.h> header file.])
+#      ],
+#      [with_libsensors="no (sensors/sensors.h not found)"])
+       AC_CHECK_HEADERS(sensors/sensors.h, [], [with_libsensors="no (sensors/sensors.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libsensors" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
+       LDFLAGS="$LDFLAGS $with_sensors_ldflags"
+
+       AC_CHECK_LIB(sensors, sensors_init,
+       [
+               AC_DEFINE(HAVE_LIBSENSORS, 1, [Define to 1 if you have the sensors library (-lsensors).])
+       ],
+       [with_libsensors="no (libsensors not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libsensors" = "xyes"
+then
+       BUILD_WITH_LIBSENSORS_CFLAGS="$with_sensors_cflags"
+       BUILD_WITH_LIBSENSORS_LDFLAGS="$with_sensors_ldflags"
+       AC_SUBST(BUILD_WITH_LIBSENSORS_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBSENSORS_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_libsensors" = "xyes")
+# }}}
+
+# --with-libsigrok {{{
+with_libsigrok_cflags=""
+with_libsigrok_ldflags=""
+AC_ARG_WITH(libsigrok, [AS_HELP_STRING([--with-libsigrok@<:@=PREFIX@:>@], [Path to libsigrok.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libsigrok="no"
+       else
+               with_libsigrok="yes"
+               if test "x$withval" != "xyes"
+               then
+                       with_libsigrok_cflags="-I$withval/include"
+                       with_libsigrok_ldflags="-L$withval/lib"
+               fi
+       fi
+],[])
+
+# libsigrok has a glib dependency
+if test "x$with_libsigrok" = "xyes"
+then
+       if test -z "m4_ifdef([AM_PATH_GLIB_2_0], [yes], [])"
+       then
+               with_libsigrok="no (glib not available)"
+       else
+               AM_PATH_GLIB_2_0([2.28.0],
+                       [with_libsigrok_cflags="$with_libsigrok_cflags $GLIB_CFLAGS"; with_libsigrok_ldflags="$with_libsigrok_ldflags $GLIB_LIBS"])
+       fi
+fi
+
+# libsigrok headers
+if test "x$with_libsigrok" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libsigrok_cflags"
+
+       AC_CHECK_HEADERS(libsigrok/libsigrok.h, [], [with_libsigrok="no (libsigrok/libsigrok.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+# libsigrok library
+if test "x$with_libsigrok" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libsigrok_cflags"
+       LDFLAGS="$LDFLAGS $with_libsigrok_ldflags"
+
+       AC_CHECK_LIB(sigrok, sr_init,
+       [
+               AC_DEFINE(HAVE_LIBSIGROK, 1, [Define to 1 if you have the sigrok library (-lsigrok).])
+       ],
+       [with_libsigrok="no (libsigrok not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libsigrok" = "xyes"
+then
+       BUILD_WITH_LIBSIGROK_CFLAGS="$with_libsigrok_cflags"
+       BUILD_WITH_LIBSIGROK_LDFLAGS="$with_libsigrok_ldflags"
+       AC_SUBST(BUILD_WITH_LIBSIGROK_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBSIGROK_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBSIGROK, test "x$with_libsigrok" = "xyes")
+# }}}
+
+# --with-libstatgrab {{{
+with_libstatgrab_cflags=""
+with_libstatgrab_ldflags=""
+AC_ARG_WITH(libstatgrab, [AS_HELP_STRING([--with-libstatgrab@<:@=PREFIX@:>@], [Path to libstatgrab.])],
+[
+ if test "x$withval" != "xno" \
+   && test "x$withval" != "xyes"
+ then
+   with_libstatgrab_cflags="-I$withval/include"
+   with_libstatgrab_ldflags="-L$withval/lib -lstatgrab"
+   with_libstatgrab="yes"
+   with_libstatgrab_pkg_config="no"
+ else
+   with_libstatgrab="$withval"
+   with_libstatgrab_pkg_config="yes"
+ fi
+ ],
+[
+ with_libstatgrab="yes"
+ with_libstatgrab_pkg_config="yes"
+])
+
+if test "x$with_libstatgrab" = "xyes" \
+  && test "x$with_libstatgrab_pkg_config" = "xyes"
+then
+  if test "x$PKG_CONFIG" != "x"
+  then
+    AC_MSG_CHECKING([pkg-config for libstatgrab])
+    temp_result="found"
+    $PKG_CONFIG --exists libstatgrab 2>/dev/null
+    if test "$?" != "0"
+    then
+      with_libstatgrab_pkg_config="no"
+      with_libstatgrab="no (pkg-config doesn't know libstatgrab)"
+      temp_result="not found"
+    fi
+    AC_MSG_RESULT([$temp_result])
+  else
+    AC_MSG_NOTICE([pkg-config not available, trying to guess flags for the statgrab library.])
+    with_libstatgrab_pkg_config="no"
+    with_libstatgrab_ldflags="$with_libstatgrab_ldflags -lstatgrab"
+  fi
+fi
+
+if test "x$with_libstatgrab" = "xyes" \
+  && test "x$with_libstatgrab_pkg_config" = "xyes" \
+  && test "x$with_libstatgrab_cflags" = "x"
+then
+  AC_MSG_CHECKING([for libstatgrab CFLAGS])
+  temp_result="`$PKG_CONFIG --cflags libstatgrab`"
+  if test "$?" = "0"
+  then
+    with_libstatgrab_cflags="$temp_result"
+  else
+    with_libstatgrab="no ($PKG_CONFIG --cflags libstatgrab failed)"
+    temp_result="$PKG_CONFIG --cflags libstatgrab failed"
+  fi
+  AC_MSG_RESULT([$temp_result])
+fi
+
+if test "x$with_libstatgrab" = "xyes" \
+  && test "x$with_libstatgrab_pkg_config" = "xyes" \
+  && test "x$with_libstatgrab_ldflags" = "x"
+then
+  AC_MSG_CHECKING([for libstatgrab LDFLAGS])
+  temp_result="`$PKG_CONFIG --libs libstatgrab`"
+  if test "$?" = "0"
+  then
+    with_libstatgrab_ldflags="$temp_result"
+  else
+    with_libstatgrab="no ($PKG_CONFIG --libs libstatgrab failed)"
+    temp_result="$PKG_CONFIG --libs libstatgrab failed"
+  fi
+  AC_MSG_RESULT([$temp_result])
+fi
+
+if test "x$with_libstatgrab" = "xyes"
+then
+  SAVE_CPPFLAGS="$CPPFLAGS"
+  CPPFLAGS="$CPPFLAGS $with_libstatgrab_cflags"
+
+  AC_CHECK_HEADERS(statgrab.h,
+                  [with_libstatgrab="yes"],
+                  [with_libstatgrab="no (statgrab.h not found)"])
+
+  CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+if test "x$with_libstatgrab" = "xyes"
+then
+  SAVE_CFLAGS="$CFLAGS"
+  SAVE_LDFLAGS="$LDFLAGS"
+
+  CFLAGS="$CFLAGS $with_libstatgrab_cflags"
+  LDFLAGS="$LDFLAGS $with_libstatgrab_ldflags"
+
+  AC_CHECK_LIB(statgrab, sg_init,
+              [with_libstatgrab="yes"],
+              [with_libstatgrab="no (symbol sg_init not found)"])
+
+  CFLAGS="$SAVE_CFLAGS"
+  LDFLAGS="$SAVE_LDFLAGS"
+fi
+
+AM_CONDITIONAL(BUILD_WITH_LIBSTATGRAB, test "x$with_libstatgrab" = "xyes")
+if test "x$with_libstatgrab" = "xyes"
+then
+  AC_DEFINE(HAVE_LIBSTATGRAB, 1, [Define to 1 if you have the 'statgrab' library (-lstatgrab)])
+  BUILD_WITH_LIBSTATGRAB_CFLAGS="$with_libstatgrab_cflags"
+  BUILD_WITH_LIBSTATGRAB_LDFLAGS="$with_libstatgrab_ldflags"
+  AC_SUBST(BUILD_WITH_LIBSTATGRAB_CFLAGS)
+  AC_SUBST(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
+fi
+# }}}
+
+# --with-libtokyotyrant {{{
+with_libtokyotyrant_cppflags=""
+with_libtokyotyrant_ldflags=""
+with_libtokyotyrant_libs=""
+AC_ARG_WITH(libtokyotyrant, [AS_HELP_STRING([--with-libtokyotyrant@<:@=PREFIX@:>@], [Path to libtokyotyrant.])],
+[
+  if test "x$withval" = "xno"
+  then
+    with_libtokyotyrant="no"
+  else if test "x$withval" = "xyes"
+  then
+    with_libtokyotyrant="yes"
+  else
+    with_libtokyotyrant_cppflags="-I$withval/include"
+    with_libtokyotyrant_ldflags="-L$withval/include"
+    with_libtokyotyrant_libs="-ltokyotyrant"
+    with_libtokyotyrant="yes"
+  fi; fi
+],
+[
+  with_libtokyotyrant="yes"
+])
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+  if $PKG_CONFIG --exists tokyotyrant
+  then
+    with_libtokyotyrant_cppflags="$with_libtokyotyrant_cppflags `$PKG_CONFIG --cflags tokyotyrant`"
+    with_libtokyotyrant_ldflags="$with_libtokyotyrant_ldflags `pkg-config --libs-only-L tokyotyrant`"
+    with_libtokyotyrant_libs="$with_libtokyotyrant_libs `pkg-config --libs-only-l tokyotyrant`"
+  fi
+fi
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $with_libtokyotyrant_cppflags"
+LDFLAGS="$LDFLAGS $with_libtokyotyrant_ldflags"
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+  AC_CHECK_HEADERS(tcrdb.h,
+  [
+          AC_DEFINE(HAVE_TCRDB_H, 1,
+                    [Define to 1 if you have the <tcrdb.h> header file.])
+  ], [with_libtokyotyrant="no (tcrdb.h not found)"])
+fi
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+  AC_CHECK_LIB(tokyotyrant, tcrdbrnum,
+  [
+          AC_DEFINE(HAVE_LIBTOKYOTYRANT, 1,
+                    [Define to 1 if you have the tokyotyrant library (-ltokyotyrant).])
+  ],
+  [with_libtokyotyrant="no (symbol tcrdbrnum not found)"],
+  [$with_libtokyotyrant_libs])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+  BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS="$with_libtokyotyrant_cppflags"
+  BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS="$with_libtokyotyrant_ldflags"
+  BUILD_WITH_LIBTOKYOTYRANT_LIBS="$with_libtokyotyrant_libs"
+  AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS)
+  AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS)
+  AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBTOKYOTYRANT, test "x$with_libtokyotyrant" = "xyes")
+# }}}
+
+# --with-libudev {{{
+with_libudev_cflags=""
+with_libudev_ldflags=""
+AC_ARG_WITH(libudev, [AS_HELP_STRING([--with-libudev@<:@=PREFIX@:>@], [Path to libudev.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libudev="no"
+       else
+               with_libudev="yes"
+               if test "x$withval" != "xyes"
+               then
+                       with_libudev_cflags="-I$withval/include"
+                       with_libudev_ldflags="-L$withval/lib"
+                       with_libudev="yes"
+               fi
+       fi
+],
+[
+       if test "x$ac_system" = "xLinux"
+       then
+               with_libudev="yes"
+       else
+               with_libudev="no (Linux only library)"
+       fi
+])
+if test "x$with_libudev" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libudev_cflags"
+
+       AC_CHECK_HEADERS(libudev.h, [], [with_libudev="no (libudev.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libudev" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libudev_cflags"
+       LDFLAGS="$LDFLAGS $with_libudev_ldflags"
+
+       AC_CHECK_LIB(udev, udev_new,
+       [
+               AC_DEFINE(HAVE_LIBUDEV, 1, [Define to 1 if you have the udev library (-ludev).])
+       ],
+       [with_libudev="no (libudev not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libudev" = "xyes"
+then
+       BUILD_WITH_LIBUDEV_CFLAGS="$with_libudev_cflags"
+       BUILD_WITH_LIBUDEV_LDFLAGS="$with_libudev_ldflags"
+       AC_SUBST(BUILD_WITH_LIBUDEV_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBUDEV_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBUDEV, test "x$with_libudev" = "xyes")
+# }}}
+
+# --with-libupsclient {{{
+with_libupsclient_config=""
+with_libupsclient_cflags=""
+with_libupsclient_libs=""
+AC_ARG_WITH(libupsclient, [AS_HELP_STRING([--with-libupsclient@<:@=PREFIX@:>@], [Path to the upsclient library.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libupsclient="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libupsclient="use_pkgconfig"
+       else
+               if test -x "$withval"
+               then
+                       with_libupsclient_config="$withval"
+                       with_libupsclient="use_libupsclient_config"
+               else if test -x "$withval/bin/libupsclient-config"
+               then
+                       with_libupsclient_config="$withval/bin/libupsclient-config"
+                       with_libupsclient="use_libupsclient_config"
+               else
+                       AC_MSG_NOTICE([Not checking for libupsclient: Manually configured])
+                       with_libupsclient_cflags="-I$withval/include"
+                       with_libupsclient_libs="-L$withval/lib -lupsclient"
+                       with_libupsclient="yes"
+               fi; fi
+       fi; fi
+],
+[with_libupsclient="use_pkgconfig"])
+
+# configure using libupsclient-config
+if test "x$with_libupsclient" = "xuse_libupsclient_config"
+then
+       AC_MSG_NOTICE([Checking for libupsclient using $with_libupsclient_config])
+       with_libupsclient_cflags="`$with_libupsclient_config --cflags`"
+       if test $? -ne 0
+       then
+               with_libupsclient="no ($with_libupsclient_config failed)"
+       fi
+       with_libupsclient_libs="`$with_libupsclient_config --libs`"
+       if test $? -ne 0
+       then
+               with_libupsclient="no ($with_libupsclient_config failed)"
+       fi
+fi
+if test "x$with_libupsclient" = "xuse_libupsclient_config"
+then
+       with_libupsclient="yes"
+fi
+
+# configure using pkg-config
+if test "x$with_libupsclient" = "xuse_pkgconfig"
+then
+       if test "x$PKG_CONFIG" = "x"
+       then
+               with_libupsclient="no (Don't have pkg-config)"
+       fi
+fi
+if test "x$with_libupsclient" = "xuse_pkgconfig"
+then
+       AC_MSG_NOTICE([Checking for libupsclient using $PKG_CONFIG])
+       $PKG_CONFIG --exists 'libupsclient' 2>/dev/null
+       if test $? -ne 0
+       then
+               with_libupsclient="no (pkg-config doesn't know libupsclient)"
+       fi
+fi
+if test "x$with_libupsclient" = "xuse_pkgconfig"
+then
+       with_libupsclient_cflags="`$PKG_CONFIG --cflags 'libupsclient'`"
+       if test $? -ne 0
+       then
+               with_libupsclient="no ($PKG_CONFIG failed)"
+       fi
+       with_libupsclient_libs="`$PKG_CONFIG --libs 'libupsclient'`"
+       if test $? -ne 0
+       then
+               with_libupsclient="no ($PKG_CONFIG failed)"
+       fi
+fi
+if test "x$with_libupsclient" = "xuse_pkgconfig"
+then
+       with_libupsclient="yes"
+fi
+
+# with_libupsclient_cflags and with_libupsclient_libs are set up now, let's do
+# the actual checks.
+if test "x$with_libupsclient" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
+
+       AC_CHECK_HEADERS(upsclient.h, [], [with_libupsclient="no (upsclient.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libupsclient" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
+       LDFLAGS="$LDFLAGS $with_libupsclient_libs"
+
+       AC_CHECK_LIB(upsclient, upscli_connect,
+                    [with_libupsclient="yes"],
+                    [with_libupsclient="no (symbol upscli_connect not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libupsclient" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
+
+       AC_CHECK_TYPES([UPSCONN_t, UPSCONN], [], [],
+[#include <stdlib.h>
+#include <stdio.h>
+#include <upsclient.h>])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libupsclient" = "xyes"
+then
+       BUILD_WITH_LIBUPSCLIENT_CFLAGS="$with_libupsclient_cflags"
+       BUILD_WITH_LIBUPSCLIENT_LIBS="$with_libupsclient_libs"
+       AC_SUBST(BUILD_WITH_LIBUPSCLIENT_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBUPSCLIENT_LIBS)
+fi
+# }}}
+
+# --with-libxmms {{{
+with_xmms_config="xmms-config"
+with_xmms_cflags=""
+with_xmms_libs=""
+AC_ARG_WITH(libxmms, [AS_HELP_STRING([--with-libxmms@<:@=PREFIX@:>@], [Path to libxmms.])],
+[
+       if test "x$withval" != "xno" \
+               && test "x$withval" != "xyes"
+       then
+               if test -f "$withval" && test -x "$withval";
+               then
+                       with_xmms_config="$withval"
+               else if test -x "$withval/bin/xmms-config"
+               then
+                       with_xmms_config="$withval/bin/xmms-config"
+               fi; fi
+               with_libxmms="yes"
+       else if test "x$withval" = "xno"
+       then
+               with_libxmms="no"
+       else
+               with_libxmms="yes"
+       fi; fi
+],
+[
+       with_libxmms="yes"
+])
+if test "x$with_libxmms" = "xyes"
+then
+       with_xmms_cflags=`$with_xmms_config --cflags 2>/dev/null`
+       xmms_config_status=$?
+
+       if test $xmms_config_status -ne 0
+       then
+               with_libxmms="no"
+       fi
+fi
+if test "x$with_libxmms" = "xyes"
+then
+       with_xmms_libs=`$with_xmms_config --libs 2>/dev/null`
+       xmms_config_status=$?
+
+       if test $xmms_config_status -ne 0
+       then
+               with_libxmms="no"
+       fi
+fi
+if test "x$with_libxmms" = "xyes"
+then
+       AC_CHECK_LIB(xmms, xmms_remote_get_info,
+       [
+               BUILD_WITH_LIBXMMS_CFLAGS="$with_xmms_cflags"
+               BUILD_WITH_LIBXMMS_LIBS="$with_xmms_libs"
+               AC_SUBST(BUILD_WITH_LIBXMMS_CFLAGS)
+               AC_SUBST(BUILD_WITH_LIBXMMS_LIBS)
+       ],
+       [
+               with_libxmms="no"
+       ],
+       [$with_xmms_libs])
+fi
+with_libxmms_numeric=0
+if test "x$with_libxmms" = "xyes"
+then
+       with_libxmms_numeric=1
+fi
+AC_DEFINE_UNQUOTED(HAVE_LIBXMMS, [$with_libxmms_numeric], [Define to 1 if you have the 'xmms' library (-lxmms).])
+AM_CONDITIONAL(BUILD_WITH_LIBXMMS, test "x$with_libxmms" = "xyes")
+# }}}
+
+# --with-libyajl {{{
+with_libyajl_cppflags=""
+with_libyajl_ldflags=""
+AC_ARG_WITH(libyajl, [AS_HELP_STRING([--with-libyajl@<:@=PREFIX@:>@], [Path to libyajl.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               with_libyajl_cppflags="-I$withval/include"
+               with_libyajl_ldflags="-L$withval/lib"
+               with_libyajl="yes"
+       else
+               with_libyajl="$withval"
+       fi
+],
+[
+       with_libyajl="yes"
+])
+if test "x$with_libyajl" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libyajl_cppflags"
+
+       AC_CHECK_HEADERS(yajl/yajl_parse.h, [with_libyajl="yes"], [with_libyajl="no (yajl/yajl_parse.h not found)"])
+       AC_CHECK_HEADERS(yajl/yajl_version.h)
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libyajl" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libyajl_cppflags"
+       LDFLAGS="$LDFLAGS $with_libyajl_ldflags"
+
+       AC_CHECK_LIB(yajl, yajl_alloc, [with_libyajl="yes"], [with_libyajl="no (Symbol 'yajl_alloc' not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libyajl" = "xyes"
+then
+       BUILD_WITH_LIBYAJL_CPPFLAGS="$with_libyajl_cppflags"
+       BUILD_WITH_LIBYAJL_LDFLAGS="$with_libyajl_ldflags"
+       BUILD_WITH_LIBYAJL_LIBS="-lyajl"
+       AC_SUBST(BUILD_WITH_LIBYAJL_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBYAJL_LDFLAGS)
+       AC_SUBST(BUILD_WITH_LIBYAJL_LIBS)
+       AC_DEFINE(HAVE_LIBYAJL, 1, [Define if libyajl is present and usable.])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBYAJL, test "x$with_libyajl" = "xyes")
+# }}}
+
+# --with-mic {{{
+with_mic_cflags="-I/opt/intel/mic/sysmgmt/sdk/include"
+with_mic_ldpath="-L/opt/intel/mic/sysmgmt/sdk/lib/Linux"
+with_mic_libs=""
+AC_ARG_WITH(mic,[AS_HELP_STRING([--with-mic@<:@=PREFIX@:>@], [Path to Intel MIC Access API.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_mic="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_mic="yes"
+       else if test -d "$with_mic/lib"
+       then
+               AC_MSG_NOTICE([Not checking for Intel Mic: Manually configured])
+               with_mic_cflags="-I$withval/include"
+               with_mic_ldpath="-L$withval/lib/Linux"
+               with_mic_libs="-lMicAccessSDK -lscif -lpthread"
+               with_mic="yes"
+       fi; fi; fi
+],
+[with_mic="yes"])
+if test "x$with_mic" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_mic_cflags"
+       AC_CHECK_HEADERS(MicAccessApi.h,[],[with_mic="no (MicAccessApi not found)"])
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_mic" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CPPFLAGS="$CPPFLAGS $with_mic_cflags"
+       LDFLAGS="$LDFLAGS $with_mic_ldpath"
+
+       AC_CHECK_LIB(MicAccessSDK, MicInitAPI,
+                       [with_mic_ldpath="$with_mic_ldpath"
+                       with_mic_libs="-lMicAccessSDK -lscif -lpthread"],
+                       [with_mic="no (symbol MicInitAPI not found)"],[-lscif -lpthread])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+
+if test "x$with_mic" = "xyes"
+then
+       BUILD_WITH_MIC_CPPFLAGS="$with_mic_cflags"
+       BUILD_WITH_MIC_LIBPATH="$with_mic_ldpath"
+       BUILD_WITH_MIC_LDADD="$with_mic_libs"
+       AC_SUBST(BUILD_WITH_MIC_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_MIC_LIBPATH)
+       AC_SUBST(BUILD_WITH_MIC_LDADD)
+fi
+#}}}
+
+# --with-libvarnish {{{
+with_libvarnish_cppflags=""
+with_libvarnish_cflags=""
+with_libvarnish_libs=""
+AC_ARG_WITH(libvarnish, [AS_HELP_STRING([--with-libvarnish@<:@=PREFIX@:>@], [Path to libvarnish.])],
+[
+       if test "x$withval" = "xno"
+       then
+               with_libvarnish="no"
+       else if test "x$withval" = "xyes"
+       then
+               with_libvarnish="use_pkgconfig"
+       else if test -d "$with_libvarnish/lib"
+       then
+               AC_MSG_NOTICE([Not checking for libvarnish: Manually configured])
+               with_libvarnish_cflags="-I$withval/include"
+               with_libvarnish_libs="-L$withval/lib -lvarnishapi"
+               with_libvarnish="yes"
+       fi; fi; fi
+],
+[with_libvarnish="use_pkgconfig"])
+
+# configure using pkg-config
+if test "x$with_libvarnish" = "xuse_pkgconfig"
+then
+       if test "x$PKG_CONFIG" = "x"
+       then
+               with_libvarnish="no (Don't have pkg-config)"
+       fi
+fi
+if test "x$with_libvarnish" = "xuse_pkgconfig"
+then
+       AC_MSG_NOTICE([Checking for varnishapi using $PKG_CONFIG])
+       $PKG_CONFIG --exists 'varnishapi' 2>/dev/null
+       if test $? -ne 0
+       then
+               with_libvarnish="no (pkg-config doesn't know varnishapi)"
+       fi
+fi
+if test "x$with_libvarnish" = "xuse_pkgconfig"
+then
+       with_libvarnish_cflags="`$PKG_CONFIG --cflags 'varnishapi'`"
+       if test $? -ne 0
+       then
+               with_libvarnish="no ($PKG_CONFIG failed)"
+       fi
+       with_libvarnish_libs="`$PKG_CONFIG --libs 'varnishapi'`"
+       if test $? -ne 0
+       then
+               with_libvarnish="no ($PKG_CONFIG failed)"
+       fi
+fi
+if test "x$with_libvarnish" = "xuse_pkgconfig"
+then
+       with_libvarnish="yes"
+fi
+
+# with_libvarnish_cflags and with_libvarnish_libs are set up now, let's do
+# the actual checks.
+if test "x$with_libvarnish" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libvarnish_cflags"
+       AC_CHECK_HEADERS(varnish/varnishapi.h, [], [with_libvarnish="no (varnish/varnishapi.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libvarnish" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       #SAVE_LDFLAGS="$LDFLAGS"
+
+       CPPFLAGS="$CPPFLAGS $with_libvarnish_cflags"
+       #LDFLAGS="$LDFLAGS $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"
+fi
+if test "x$with_libvarnish" = "xyes"
+then
+       BUILD_WITH_LIBVARNISH_CFLAGS="$with_libvarnish_cflags"
+       BUILD_WITH_LIBVARNISH_LIBS="$with_libvarnish_libs"
+       AC_SUBST(BUILD_WITH_LIBVARNISH_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBVARNISH_LIBS)
+fi
+# }}}
+
+# pkg-config --exists 'libxml-2.0'; pkg-config --exists libvirt {{{
+with_libxml2="no (pkg-config isn't available)"
+with_libxml2_cflags=""
+with_libxml2_ldflags=""
+with_libvirt="no (pkg-config isn't available)"
+with_libvirt_cflags=""
+with_libvirt_ldflags=""
+if test "x$PKG_CONFIG" != "x"
+then
+       pkg-config --exists 'libxml-2.0' 2>/dev/null
+       if test "$?" = "0"
+       then
+               with_libxml2="yes"
+       else
+               with_libxml2="no (pkg-config doesn't know libxml-2.0)"
+       fi
+
+       pkg-config --exists libvirt 2>/dev/null
+       if test "$?" = "0"
+       then
+               with_libvirt="yes"
+       else
+               with_libvirt="no (pkg-config doesn't know libvirt)"
+       fi
+fi
+if test "x$with_libxml2" = "xyes"
+then
+       with_libxml2_cflags="`pkg-config --cflags libxml-2.0`"
+       if test $? -ne 0
+       then
+               with_libxml2="no"
+       fi
+       with_libxml2_ldflags="`pkg-config --libs libxml-2.0`"
+       if test $? -ne 0
+       then
+               with_libxml2="no"
+       fi
+fi
+if test "x$with_libxml2" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libxml2_cflags"
+
+       AC_CHECK_HEADERS(libxml/parser.h, [],
+                     [with_libxml2="no (libxml/parser.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libxml2" = "xyes"
+then
+       SAVE_CFLAGS="$CFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CFLAGS="$CFLAGS $with_libxml2_cflags"
+       LDFLAGS="$LDFLAGS $with_libxml2_ldflags"
+
+       AC_CHECK_LIB(xml2, xmlXPathEval,
+                    [with_libxml2="yes"],
+                    [with_libxml2="no (symbol xmlXPathEval not found)"])
+
+       CFLAGS="$SAVE_CFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+dnl Add the right compiler flags and libraries.
+if test "x$with_libxml2" = "xyes"; then
+       BUILD_WITH_LIBXML2_CFLAGS="$with_libxml2_cflags"
+       BUILD_WITH_LIBXML2_LIBS="$with_libxml2_ldflags"
+       AC_SUBST(BUILD_WITH_LIBXML2_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBXML2_LIBS)
+fi
+if test "x$with_libvirt" = "xyes"
+then
+       with_libvirt_cflags="`pkg-config --cflags libvirt`"
+       if test $? -ne 0
+       then
+               with_libvirt="no"
+       fi
+       with_libvirt_ldflags="`pkg-config --libs libvirt`"
+       if test $? -ne 0
+       then
+               with_libvirt="no"
+       fi
+fi
+if test "x$with_libvirt" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libvirt_cflags"
+
+       AC_CHECK_HEADERS(libvirt/libvirt.h, [],
+                     [with_libvirt="no (libvirt/libvirt.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libvirt" = "xyes"
+then
+       SAVE_CFLAGS="$CFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+
+       CFLAGS="$CFLAGS $with_libvirt_cflags"
+       LDFLAGS="$LDFLAGS $with_libvirt_ldflags"
+
+       AC_CHECK_LIB(virt, virDomainBlockStats,
+                    [with_libvirt="yes"],
+                    [with_libvirt="no (symbol virDomainBlockStats not found)"])
+
+       CFLAGS="$SAVE_CFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+dnl Add the right compiler flags and libraries.
+if test "x$with_libvirt" = "xyes"; then
+       BUILD_WITH_LIBVIRT_CFLAGS="$with_libvirt_cflags"
+       BUILD_WITH_LIBVIRT_LIBS="$with_libvirt_ldflags"
+       AC_SUBST(BUILD_WITH_LIBVIRT_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBVIRT_LIBS)
+fi
+# }}}
+
+# $PKG_CONFIG --exists OpenIPMIpthread {{{
+with_libopenipmipthread="yes"
+with_libopenipmipthread_cflags=""
+with_libopenipmipthread_libs=""
+
+AC_MSG_CHECKING([for pkg-config])
+temp_result="no"
+if test "x$PKG_CONFIG" = "x"
+then
+       with_libopenipmipthread="no"
+       temp_result="no"
+else
+       temp_result="$PKG_CONFIG"
+fi
+AC_MSG_RESULT([$temp_result])
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       AC_MSG_CHECKING([for libOpenIPMIpthread])
+       $PKG_CONFIG --exists OpenIPMIpthread 2>/dev/null
+       if test "$?" != "0"
+       then
+               with_libopenipmipthread="no (pkg-config doesn't know OpenIPMIpthread)"
+       fi
+       AC_MSG_RESULT([$with_libopenipmipthread])
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       AC_MSG_CHECKING([for libOpenIPMIpthread CFLAGS])
+       temp_result="`$PKG_CONFIG --cflags OpenIPMIpthread`"
+       if test "$?" = "0"
+       then
+               with_libopenipmipthread_cflags="$temp_result"
+       else
+               with_libopenipmipthread="no ($PKG_CONFIG --cflags OpenIPMIpthread failed)"
+               temp_result="$PKG_CONFIG --cflags OpenIPMIpthread failed"
+       fi
+       AC_MSG_RESULT([$temp_result])
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       AC_MSG_CHECKING([for libOpenIPMIpthread LDFLAGS])
+       temp_result="`$PKG_CONFIG --libs OpenIPMIpthread`"
+       if test "$?" = "0"
+       then
+               with_libopenipmipthread_ldflags="$temp_result"
+       else
+               with_libopenipmipthread="no ($PKG_CONFIG --libs OpenIPMIpthread failed)"
+               temp_result="$PKG_CONFIG --libs OpenIPMIpthread failed"
+       fi
+       AC_MSG_RESULT([$temp_result])
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libopenipmipthread_cflags"
+
+       AC_CHECK_HEADERS(OpenIPMI/ipmi_smi.h,
+                        [with_libopenipmipthread="yes"],
+                        [with_libopenipmipthread="no (OpenIPMI/ipmi_smi.h not found)"],
+[#include <OpenIPMI/ipmiif.h>
+#include <OpenIPMI/ipmi_err.h>
+#include <OpenIPMI/ipmi_posix.h>
+#include <OpenIPMI/ipmi_conn.h>
+])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       BUILD_WITH_OPENIPMI_CFLAGS="$with_libopenipmipthread_cflags"
+       BUILD_WITH_OPENIPMI_LIBS="$with_libopenipmipthread_ldflags"
+       AC_SUBST(BUILD_WITH_OPENIPMI_CFLAGS)
+       AC_SUBST(BUILD_WITH_OPENIPMI_LIBS)
+fi
+# }}}
+
+PKG_CHECK_MODULES([LIBNOTIFY], [libnotify],
+               [with_libnotify="yes"],
+               [if test "x$LIBNOTIFY_PKG_ERRORS" = "x"; then
+                        with_libnotify="no"
+                else
+                        with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"
+                fi])
+
+# Check for enabled/disabled features
+#
+
+# AC_COLLECTD(name, enable/disable, info-text, feature/module)
+# ------------------------------------------------------------
+dnl
+m4_define([my_toupper], [m4_translit([$1], m4_defn([m4_cr_letters]), m4_defn([m4_cr_LETTERS]))])
+dnl
+AC_DEFUN(
+       [AC_COLLECTD],
+       [
+       m4_if([$1], [], [AC_FATAL([AC_COLLECTD([$1], [$2], [$3], [$4]): 1st argument must not be empty])])dnl
+       m4_if(
+               [$2],
+               [enable],
+               [dnl
+               m4_define([EnDis],[disabled])dnl
+               m4_define([YesNo],[no])dnl
+               ],dnl
+               [m4_if(
+                       [$2],
+                       [disable],
+                       [dnl
+                       m4_define([EnDis],[enabled])dnl
+                       m4_define([YesNo],[yes])dnl
+                       ],
+                       [dnl
+                       AC_FATAL([AC_COLLECTD([$1], [$2], [$3], [$4]): 2nd argument must be either enable or disable])dnl
+                       ]dnl
+               )]dnl
+       )dnl
+       m4_if([$3], [feature], [],
+               [m4_if(
+                       [$3], [module], [],
+                       [dnl
+                       AC_FATAL([AC_COLLECTD([$1], [$2], [$3], [$4]): 3rd argument must be either feature or disable])dnl
+                       ]dnl
+               )]dnl
+       )dnl
+       AC_ARG_ENABLE(
+               [$1],
+               AS_HELP_STRING([--$2-$1], [$2 $4 (EnDis by def)]),
+               [],
+               enable_$1='[YesNo]'dnl
+       )# AC_ARG_ENABLE
+if test "x$enable_$1" = "xno"
+then
+       collectd_$1=0
+else
+       if test "x$enable_$1" = "xyes"
+       then
+               collectd_$1=1
+       else
+               AC_MSG_NOTICE([please specify either --enable-$1 or --disable-$1; enabling $1.])
+               collectd_$1=1
+               enable_$1='yes'
+       fi
+fi
+       AC_DEFINE_UNQUOTED([COLLECT_]my_toupper([$1]), [$collectd_$1], [wether or not to enable $3 $4])
+       AM_CONDITIONAL([BUILD_]my_toupper([$3])[_]my_toupper([$1]), [test "x$enable_$1" = "xyes"])dnl
+       ]dnl
+)# AC_COLLECTD(name, enable/disable, info-text, feature/module)
+
+# AC_PLUGIN(name, default, info)
+# ------------------------------------------------------------
+dnl
+AC_DEFUN(
+  [AC_PLUGIN],
+  [
+    enable_plugin="no"
+    force="no"
+    AC_ARG_ENABLE([$1], AS_HELP_STRING([--enable-$1],[$3]),
+    [
+     if test "x$enableval" = "xyes"
+     then
+            enable_plugin="yes"
+     else if test "x$enableval" = "xforce"
+     then
+            enable_plugin="yes"
+            force="yes"
+     else
+            enable_plugin="no (disabled on command line)"
+     fi; fi
+    ],
+    [
+        if test "x$enable_all_plugins" = "xauto"
+        then
+            if test "x$2" = "xyes"
+            then
+                    enable_plugin="yes"
+            else
+                    enable_plugin="no"
+            fi
+        else
+            enable_plugin="$enable_all_plugins"
+        fi
+    ])
+    if test "x$enable_plugin" = "xyes"
+    then
+           if test "x$2" = "xyes" || test "x$force" = "xyes"
+           then
+                   AC_DEFINE([HAVE_PLUGIN_]my_toupper([$1]), 1, [Define to 1 if the $1 plugin is enabled.])
+                   if test "x$2" != "xyes"
+                   then
+                           dependency_warning="yes"
+                   fi
+           else # User passed "yes" but dependency checking yielded "no" => Dependency problem.
+                   dependency_error="yes"
+                   enable_plugin="no (dependency error)"
+           fi
+    fi
+    AM_CONDITIONAL([BUILD_PLUGIN_]my_toupper([$1]), test "x$enable_plugin" = "xyes")
+    enable_$1="$enable_plugin"
+  ]
+)# AC_PLUGIN(name, default, info)
+
+m4_divert_once([HELP_ENABLE], [
+collectd features:])
+# FIXME: Remove these calls to `AC_COLLECTD' and then remove that macro.
+AC_COLLECTD([debug],     [enable],  [feature], [debugging])
+AC_COLLECTD([daemon],    [disable], [feature], [daemon mode])
+AC_COLLECTD([getifaddrs],[enable],  [feature], [getifaddrs under Linux])
+
+dependency_warning="no"
+dependency_error="no"
+
+plugin_ascent="no"
+plugin_battery="no"
+plugin_bind="no"
+plugin_cgroups="no"
+plugin_conntrack="no"
+plugin_contextswitch="no"
+plugin_cpu="no"
+plugin_cpufreq="no"
+plugin_curl_json="no"
+plugin_curl_xml="no"
+plugin_df="no"
+plugin_disk="no"
+plugin_entropy="no"
+plugin_ethstat="no"
+plugin_fscache="no"
+plugin_interface="no"
+plugin_ipmi="no"
+plugin_ipvs="no"
+plugin_irq="no"
+plugin_libvirt="no"
+plugin_load="no"
+plugin_log_logstash="no"
+plugin_memory="no"
+plugin_multimeter="no"
+plugin_nfs="no"
+plugin_numa="no"
+plugin_perl="no"
+plugin_processes="no"
+plugin_protocols="no"
+plugin_serial="no"
+plugin_swap="no"
+plugin_tape="no"
+plugin_tcpconns="no"
+plugin_ted="no"
+plugin_thermal="no"
+plugin_users="no"
+plugin_uptime="no"
+plugin_vmem="no"
+plugin_vserver="no"
+plugin_wireless="no"
+plugin_zfs_arc="no"
+
+# Linux
+if test "x$ac_system" = "xLinux"
+then
+       plugin_battery="yes"
+       plugin_conntrack="yes"
+       plugin_contextswitch="yes"
+       plugin_cgroups="yes"
+       plugin_cpu="yes"
+       plugin_cpufreq="yes"
+       plugin_disk="yes"
+       plugin_entropy="yes"
+       plugin_fscache="yes"
+       plugin_interface="yes"
+       plugin_irq="yes"
+       plugin_load="yes"
+       plugin_lvm="yes"
+       plugin_memory="yes"
+       plugin_nfs="yes"
+       plugin_numa="yes"
+       plugin_processes="yes"
+       plugin_protocols="yes"
+       plugin_serial="yes"
+       plugin_swap="yes"
+       plugin_tcpconns="yes"
+       plugin_thermal="yes"
+       plugin_uptime="yes"
+       plugin_vmem="yes"
+       plugin_vserver="yes"
+       plugin_wireless="yes"
+
+       if test "x$have_linux_ip_vs_h" = "xyes" || test "x$have_net_ip_vs_h" = "xyes" || test "x$have_ip_vs_h" = "xyes"
+       then
+               plugin_ipvs="yes"
+       fi
+fi
+
+if test "x$ac_system" = "xOpenBSD"
+then
+       plugin_tcpconns="yes"
+fi
+
+# Mac OS X devices
+if test "x$with_libiokit" = "xyes"
+then
+       plugin_battery="yes"
+       plugin_disk="yes"
+fi
+
+# AIX
+
+if test "x$ac_system" = "xAIX"
+then
+        plugin_tcpconns="yes"
+fi
+
+# FreeBSD
+
+if test "x$ac_system" = "xFreeBSD"
+then
+        plugin_zfs_arc="yes"
+fi
+
+
+if test "x$with_perfstat" = "xyes"
+then
+       plugin_cpu="yes"
+       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"
+then
+       plugin_processes="yes"
+fi
+
+# Solaris
+if test "x$with_kstat" = "xyes"
+then
+       plugin_nfs="yes"
+       plugin_uptime="yes"
+       plugin_zfs_arc="yes"
+fi
+
+if test "x$with_devinfo$with_kstat" = "xyesyes"
+then
+       plugin_cpu="yes"
+       plugin_disk="yes"
+       plugin_interface="yes"
+       plugin_memory="yes"
+       plugin_tape="yes"
+fi
+
+# libstatgrab
+if test "x$with_libstatgrab" = "xyes"
+then
+       plugin_cpu="yes"
+       plugin_disk="yes"
+       plugin_interface="yes"
+       plugin_load="yes"
+       plugin_memory="yes"
+       plugin_swap="yes"
+       plugin_users="yes"
+fi
+
+if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
+then
+       plugin_ascent="yes"
+       if test "x$have_strptime" = "xyes"
+       then
+               plugin_bind="yes"
+       fi
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+       plugin_ipmi="yes"
+fi
+
+if test "x$with_libcurl" = "xyes" && test "x$with_libyajl" = "xyes"
+then
+       plugin_curl_json="yes"
+fi
+
+if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
+then
+       plugin_curl_xml="yes"
+fi
+
+if test "x$have_processor_info" = "xyes"
+then
+       plugin_cpu="yes"
+fi
+if test "x$have_sysctl" = "xyes"
+then
+       plugin_cpu="yes"
+       plugin_memory="yes"
+       plugin_uptime="yes"
+       if test "x$ac_system" = "xDarwin"
+       then
+               plugin_swap="yes"
+       fi
+fi
+if test "x$have_sysctlbyname" = "xyes"
+then
+       plugin_contextswitch="yes"
+       plugin_cpu="yes"
+       plugin_memory="yes"
+       plugin_tcpconns="yes"
+fi
+
+# Df plugin: Check if we know how to determine mount points first.
+#if test "x$have_listmntent" = "xyes"; then
+#      plugin_df="yes"
+#fi
+if test "x$have_getvfsstat" = "xyes" || test "x$have_getfsstat" = "xyes"
+then
+       plugin_df="yes"
+fi
+if test "x$c_cv_have_two_getmntent" = "xyes" || test "x$have_getmntent" = "xgen" || test "x$have_getmntent" = "xsun"
+then
+       plugin_df="yes"
+fi
+#if test "x$have_getmntent" = "xseq"
+#then
+#      plugin_df="yes"
+#fi
+if test "x$c_cv_have_one_getmntent" = "xyes"
+then
+       plugin_df="yes"
+fi
+
+# Df plugin: Check if we have either `statfs' or `statvfs' second.
+if test "x$plugin_df" = "xyes"
+then
+       plugin_df="no"
+       if test "x$have_statfs" = "xyes"
+       then
+               plugin_df="yes"
+       fi
+       if test "x$have_statvfs" = "xyes"
+       then
+               plugin_df="yes"
+       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"
+fi
+
+if test "x$with_libxml2" = "xyes" && test "x$with_libvirt" = "xyes"
+then
+       plugin_libvirt="yes"
+fi
+
+if test "x$have_getloadavg" = "xyes"
+then
+       plugin_load="yes"
+fi
+
+if test "x$with_libyajl" = "xyes"
+then
+       plugin_log_logstash="yes"
+fi
+
+if test "x$c_cv_have_libperl$c_cv_have_perl_ithreads" = "xyesyes"
+then
+       plugin_perl="yes"
+fi
+
+# Mac OS X memory interface
+if test "x$have_host_statistics" = "xyes"
+then
+       plugin_memory="yes"
+fi
+
+if test "x$have_termios_h" = "xyes"
+then
+       plugin_multimeter="yes"
+       plugin_ted="yes"
+fi
+
+if test "x$have_thread_info" = "xyes"
+then
+       plugin_processes="yes"
+fi
+
+if test "x$with_kvm_getprocs" = "xyes" && test "x$have_struct_kinfo_proc_freebsd" = "xyes"
+then
+       plugin_processes="yes"
+fi
+
+if test "x$with_kvm_getswapinfo" = "xyes"
+then
+       plugin_swap="yes"
+fi
+
+if test "x$have_swapctl" = "xyes" && test "x$c_cv_have_swapctl_two_args" = "xyes"
+then
+       plugin_swap="yes"
+fi
+
+if test "x$with_kvm_openfiles$with_kvm_nlist" = "xyesyes"
+then
+       plugin_tcpconns="yes"
+fi
+
+if test "x$have_getutent" = "xyes"
+then
+       plugin_users="yes"
+fi
+if test "x$have_getutxent" = "xyes"
+then
+       plugin_users="yes"
+fi
+
+m4_divert_once([HELP_ENABLE], [
+collectd plugins:])
+
+AC_ARG_ENABLE([all-plugins],
+               AS_HELP_STRING([--enable-all-plugins],[enable all plugins (auto by def)]),
+               [
+                if test "x$enableval" = "xyes"
+                then
+                        enable_all_plugins="yes"
+                else if test "x$enableval" = "xauto"
+                then
+                        enable_all_plugins="auto"
+                else
+                        enable_all_plugins="no"
+                fi; fi
+               ],
+               [enable_all_plugins="auto"])
+
+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])
+AC_PLUGIN([apple_sensors], [$with_libiokit],   [Apple's hardware sensors])
+AC_PLUGIN([aquaero],     [$with_libaquaero5],  [Aquaero's hardware sensors])
+AC_PLUGIN([ascent],      [$plugin_ascent],     [AscentEmu player statistics])
+AC_PLUGIN([battery],     [$plugin_battery],    [Battery statistics])
+AC_PLUGIN([bind],        [$plugin_bind],       [ISC Bind nameserver statistics])
+AC_PLUGIN([conntrack],   [$plugin_conntrack],  [nf_conntrack statistics])
+AC_PLUGIN([contextswitch], [$plugin_contextswitch], [context switch statistics])
+AC_PLUGIN([cpufreq],     [$plugin_cpufreq],    [CPU frequency statistics])
+AC_PLUGIN([cpu],         [$plugin_cpu],        [CPU usage statistics])
+AC_PLUGIN([csv],         [yes],                [CSV output plugin])
+AC_PLUGIN([curl],        [$with_libcurl],      [CURL generic web statistics])
+AC_PLUGIN([curl_json],   [$plugin_curl_json],    [CouchDB statistics])
+AC_PLUGIN([curl_xml],   [$plugin_curl_xml],    [CURL generic xml statistics])
+AC_PLUGIN([cgroups],     [$plugin_cgroups],    [CGroups CPU usage accounting])
+AC_PLUGIN([dbi],         [$with_libdbi],       [General database statistics])
+AC_PLUGIN([df],          [$plugin_df],         [Filesystem usage statistics])
+AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
+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])
+AC_PLUGIN([gmond],       [$with_libganglia],   [Ganglia plugin])
+AC_PLUGIN([hddtemp],     [yes],                [Query hddtempd])
+AC_PLUGIN([interface],   [$plugin_interface],  [Interface traffic statistics])
+AC_PLUGIN([ipmi],        [$plugin_ipmi],       [IPMI sensor statistics])
+AC_PLUGIN([iptables],    [$with_libiptc],      [IPTables rule counters])
+AC_PLUGIN([ipvs],        [$plugin_ipvs],       [IPVS connection statistics])
+AC_PLUGIN([irq],         [$plugin_irq],        [IRQ statistics])
+AC_PLUGIN([java],        [$with_java],         [Embed the Java Virtual Machine])
+AC_PLUGIN([libvirt],     [$plugin_libvirt],    [Virtual machine statistics])
+AC_PLUGIN([load],        [$plugin_load],       [System load])
+AC_PLUGIN([logfile],     [yes],                [File logging plugin])
+AC_PLUGIN([log_logstash], [$plugin_log_logstash], [Logstash json_event compatible logging])
+AC_PLUGIN([lpar],        [$with_perfstat],     [AIX logical partitions statistics])
+AC_PLUGIN([lvm],         [$with_liblvm2app],   [LVM statistics])
+AC_PLUGIN([madwifi],     [$have_linux_wireless_h], [Madwifi wireless statistics])
+AC_PLUGIN([match_empty_counter], [yes],        [The empty counter match])
+AC_PLUGIN([match_hashed], [yes],               [The hashed match])
+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])
+AC_PLUGIN([mic],         [$with_mic],          [Intel Many Integrated Core stats])
+AC_PLUGIN([modbus],      [$with_libmodbus],    [Modbus plugin])
+AC_PLUGIN([multimeter],  [$plugin_multimeter], [Read multimeter values])
+AC_PLUGIN([mysql],       [$with_libmysql],     [MySQL statistics])
+AC_PLUGIN([netapp],      [$with_libnetapp],    [NetApp plugin])
+AC_PLUGIN([netlink],     [$with_libmnl],       [Enhanced Linux network statistics])
+AC_PLUGIN([network],     [yes],                [Network communication plugin])
+AC_PLUGIN([nfs],         [$plugin_nfs],        [NFS statistics])
+AC_PLUGIN([nginx],       [$with_libcurl],      [nginx statistics])
+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])
+AC_PLUGIN([openvpn],     [yes],                [OpenVPN client statistics])
+AC_PLUGIN([oracle],      [$with_oracle],       [Oracle plugin])
+AC_PLUGIN([perl],        [$plugin_perl],       [Embed a Perl interpreter])
+AC_PLUGIN([pf],          [$have_net_pfvar_h],  [BSD packet filter (PF) statistics])
+# FIXME: Check for libevent, too.
+AC_PLUGIN([pinba],       [$have_protoc_c],     [Pinba statistics])
+AC_PLUGIN([ping],        [$with_liboping],     [Network latency statistics])
+AC_PLUGIN([postgresql],  [$with_libpq],        [PostgreSQL database statistics])
+AC_PLUGIN([powerdns],    [yes],                [PowerDNS statistics])
+AC_PLUGIN([processes],   [$plugin_processes],  [Process statistics])
+AC_PLUGIN([protocols],   [$plugin_protocols],  [Protocol (IP, TCP, ...) statistics])
+AC_PLUGIN([python],      [$with_python],       [Embed a Python interpreter])
+AC_PLUGIN([redis],       [$with_libcredis],    [Redis plugin])
+AC_PLUGIN([routeros],    [$with_librouteros],  [RouterOS plugin])
+AC_PLUGIN([rrdcached],   [$librrd_rrdc_update], [RRDTool output plugin])
+AC_PLUGIN([rrdtool],     [$with_librrd],       [RRDTool output plugin])
+AC_PLUGIN([sensors],     [$with_libsensors],   [lm_sensors statistics])
+AC_PLUGIN([serial],      [$plugin_serial],     [serial port traffic])
+AC_PLUGIN([sigrok],      [$with_libsigrok],    [sigrok acquisition sources])
+AC_PLUGIN([snmp],        [$with_libnetsnmp],   [SNMP querying plugin])
+AC_PLUGIN([statsd],      [yes],                [StatsD plugin])
+AC_PLUGIN([swap],        [$plugin_swap],       [Swap usage statistics])
+AC_PLUGIN([syslog],      [$have_syslog],       [Syslog logging plugin])
+AC_PLUGIN([table],       [yes],                [Parsing of tabular data])
+AC_PLUGIN([tail],        [yes],                [Parsing of logfiles])
+AC_PLUGIN([tail_csv],    [yes],                [Parsing of CSV files])
+AC_PLUGIN([tape],        [$plugin_tape],       [Tape drive statistics])
+AC_PLUGIN([target_notification], [yes],        [The notification target])
+AC_PLUGIN([target_replace], [yes],             [The replace target])
+AC_PLUGIN([target_scale],[yes],                [The scale target])
+AC_PLUGIN([target_set],  [yes],                [The set target])
+AC_PLUGIN([target_v5upgrade], [yes],           [The v5upgrade target])
+AC_PLUGIN([tcpconns],    [$plugin_tcpconns],   [TCP connection statistics])
+AC_PLUGIN([teamspeak2],  [yes],                [TeamSpeak2 server statistics])
+AC_PLUGIN([ted],         [$plugin_ted],        [Read The Energy Detective values])
+AC_PLUGIN([thermal],     [$plugin_thermal],    [Linux ACPI thermal zone statistics])
+AC_PLUGIN([threshold],   [yes],                [Threshold checking plugin])
+AC_PLUGIN([tokyotyrant], [$with_libtokyotyrant],  [TokyoTyrant database statistics])
+AC_PLUGIN([unixsock],    [yes],                [Unixsock communication plugin])
+AC_PLUGIN([uptime],      [$plugin_uptime],     [Uptime statistics])
+AC_PLUGIN([users],       [$plugin_users],      [User statistics])
+AC_PLUGIN([uuid],        [yes],                [UUID as hostname plugin])
+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_kafka],  [$with_librdkafka],  [Kafka output plugin])
+AC_PLUGIN([write_mongodb], [$with_libmongoc],  [MongoDB output plugin])
+AC_PLUGIN([write_redis], [$with_libcredis],    [Redis output plugin])
+AC_PLUGIN([write_riemann], [$have_protoc_c],   [Riemann output plugin])
+AC_PLUGIN([xmms],        [$with_libxmms],      [XMMS statistics])
+AC_PLUGIN([zfs_arc],     [$plugin_zfs_arc],    [ZFS ARC statistics])
+
+dnl Default configuration file
+# Load either syslog or logfile
+LOAD_PLUGIN_SYSLOG=""
+LOAD_PLUGIN_LOGFILE=""
+
+AC_MSG_CHECKING([which default log plugin to load])
+default_log_plugin="none"
+if test "x$enable_syslog" = "xyes"
+then
+       default_log_plugin="syslog"
+else
+       LOAD_PLUGIN_SYSLOG="##"
+fi
+
+if test "x$enable_logfile" = "xyes"
+then
+       if test "x$default_log_plugin" = "xnone"
+       then
+               default_log_plugin="logfile"
+       else
+               LOAD_PLUGIN_LOGFILE="#"
+       fi
+else
+       LOAD_PLUGIN_LOGFILE="##"
+fi
+AC_MSG_RESULT([$default_log_plugin])
+
+AC_SUBST(LOAD_PLUGIN_SYSLOG)
+AC_SUBST(LOAD_PLUGIN_LOGFILE)
+
+DEFAULT_LOG_LEVEL="info"
+if test "x$enable_debug" = "xyes"
+then
+       DEFAULT_LOG_LEVEL="debug"
+fi
+AC_SUBST(DEFAULT_LOG_LEVEL)
+
+# Load only one of rrdtool, network, csv in the default config.
+LOAD_PLUGIN_RRDTOOL=""
+LOAD_PLUGIN_NETWORK=""
+LOAD_PLUGIN_CSV=""
+
+AC_MSG_CHECKING([which default write plugin to load])
+default_write_plugin="none"
+if test "x$enable_rrdtool" = "xyes"
+then
+       default_write_plugin="rrdtool"
+else
+       LOAD_PLUGIN_RRDTOOL="##"
+fi
+
+if test "x$enable_network" = "xyes"
+then
+       if test "x$default_write_plugin" = "xnone"
+       then
+               default_write_plugin="network"
+       else
+               LOAD_PLUGIN_NETWORK="#"
+       fi
+else
+       LOAD_PLUGIN_NETWORK="##"
+fi
+
+if test "x$enable_csv" = "xyes"
+then
+       if test "x$default_write_plugin" = "xnone"
+       then
+               default_write_plugin="csv"
+       else
+               LOAD_PLUGIN_CSV="#"
+       fi
+else
+       LOAD_PLUGIN_CSV="##"
+fi
+AC_MSG_RESULT([$default_write_plugin])
+
+AC_SUBST(LOAD_PLUGIN_RRDTOOL)
+AC_SUBST(LOAD_PLUGIN_NETWORK)
+AC_SUBST(LOAD_PLUGIN_CSV)
+
+dnl ip_vs.h
+if test "x$ac_system" = "xLinux" \
+       && test "x$have_linux_ip_vs_h$have_net_ip_vs_h$have_ip_vs_h" = "xnonono"
+then
+       enable_ipvs="$enable_ipvs (ip_vs.h not found)"
+fi
+
+if test "x$ip_vs_h_needs_kernel_cflags" = "xyes"
+then
+       enable_ipvs="$enable_ipvs (needs $KERNEL_CFLAGS)"
+fi
+
+dnl Perl bindings
+PERL_BINDINGS_OPTIONS="PREFIX=${prefix}"
+AC_ARG_WITH(perl-bindings, [AS_HELP_STRING([--with-perl-bindings@<:@=OPTIONS@:>@], [Options passed to "perl Makefile.PL".])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               PERL_BINDINGS_OPTIONS="$withval"
+               with_perl_bindings="yes"
+       else
+               with_perl_bindings="$withval"
+       fi
+],
+[
+       if test -n "$perl_interpreter"
+       then
+               with_perl_bindings="yes"
+       else
+               with_perl_bindings="no (no perl interpreter found)"
+       fi
+])
+if test "x$with_perl_bindings" = "xyes"
+then
+       PERL_BINDINGS="perl"
+else
+       PERL_BINDINGS=""
+fi
+AC_SUBST(PERL_BINDINGS)
+AC_SUBST(PERL_BINDINGS_OPTIONS)
+
+dnl libcollectdclient
+LCC_VERSION_MAJOR=`echo $PACKAGE_VERSION | cut -d'.' -f1`
+LCC_VERSION_MINOR=`echo $PACKAGE_VERSION | cut -d'.' -f2`
+LCC_VERSION_PATCH=`echo $PACKAGE_VERSION | cut -d'.' -f3`
+
+LCC_VERSION_EXTRA=`echo $PACKAGE_VERSION | cut -d'.' -f4-`
+
+LCC_VERSION_STRING="$LCC_VERSION_MAJOR.$LCC_VERSION_MINOR.$LCC_VERSION_PATCH"
+
+AC_SUBST(LCC_VERSION_MAJOR)
+AC_SUBST(LCC_VERSION_MINOR)
+AC_SUBST(LCC_VERSION_PATCH)
+AC_SUBST(LCC_VERSION_EXTRA)
+AC_SUBST(LCC_VERSION_STRING)
+
+AC_CONFIG_FILES(src/libcollectdclient/collectd/lcc_features.h)
+
+AC_CONFIG_FILES([Makefile src/Makefile src/collectd.conf src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile bindings/java/Makefile])
+AC_OUTPUT
+
+if test "x$with_librrd" = "xyes" \
+       && test "x$librrd_threadsafe" != "xyes"
+then
+       with_librrd="yes (warning: librrd is not thread-safe)"
+fi
+
+if test "x$with_libperl" = "xyes"
+then
+       with_libperl="yes (version `$perl_interpreter -MConfig -e 'print $Config{version};'`)"
+else
+       enable_perl="no (needs libperl)"
+fi
+
+if test "x$enable_perl" = "xno" && test "x$c_cv_have_perl_ithreads" = "xno"
+then
+       enable_perl="no (libperl doesn't support ithreads)"
+fi
+
+if test "x$with_perl_bindings" = "xyes" \
+       && test "x$PERL_BINDINGS_OPTIONS" != "x"
+then
+       with_perl_bindings="yes ($PERL_BINDINGS_OPTIONS)"
+fi
+
+cat <<EOF;
+
+Configuration:
+  Libraries:
+    intel mic . . . . . . $with_mic
+    libaquaero5 . . . . . $with_libaquaero5
+    libcurl . . . . . . . $with_libcurl
+    libdbi  . . . . . . . $with_libdbi
+    libcredis . . . . . . $with_libcredis
+    libesmtp  . . . . . . $with_libesmtp
+    libganglia  . . . . . $with_libganglia
+    libgcrypt . . . . . . $with_libgcrypt
+    libiokit  . . . . . . $with_libiokit
+    libiptc . . . . . . . $with_libiptc
+    libjvm  . . . . . . . $with_java
+    libkstat  . . . . . . $with_kstat
+    libkvm  . . . . . . . $with_libkvm
+    libmemcached  . . . . $with_libmemcached
+    libmnl  . . . . . . . $with_libmnl
+    libmodbus . . . . . . $with_libmodbus
+    libmysql  . . . . . . $with_libmysql
+    libnetapp . . . . . . $with_libnetapp
+    libnetsnmp  . . . . . $with_libnetsnmp
+    libnotify . . . . . . $with_libnotify
+    liboconfig  . . . . . $with_liboconfig
+    libopenipmi . . . . . $with_libopenipmipthread
+    liboping  . . . . . . $with_liboping
+    libpcap . . . . . . . $with_libpcap
+    libperfstat . . . . . $with_perfstat
+    libperl . . . . . . . $with_libperl
+    libpq . . . . . . . . $with_libpq
+    libpthread  . . . . . $with_libpthread
+    librabbitmq . . . . . $with_librabbitmq
+    librdkafka  . . . . . $with_librdkafka
+    librouteros . . . . . $with_librouteros
+    librrd  . . . . . . . $with_librrd
+    libsensors  . . . . . $with_libsensors
+    libsigrok   . . . . . $with_libsigrok
+    libstatgrab . . . . . $with_libstatgrab
+    libtokyotyrant  . . . $with_libtokyotyrant
+    libudev . . . . . . . $with_libudev
+    libupsclient  . . . . $with_libupsclient
+    libvarnish  . . . . . $with_libvarnish
+    libvirt . . . . . . . $with_libvirt
+    libxml2 . . . . . . . $with_libxml2
+    libxmms . . . . . . . $with_libxmms
+    libyajl . . . . . . . $with_libyajl
+    libevent  . . . . . . $with_libevent
+    protobuf-c  . . . . . $have_protoc_c
+    oracle  . . . . . . . $with_oracle
+    python  . . . . . . . $with_python
+
+  Features:
+    daemon mode . . . . . $enable_daemon
+    debug . . . . . . . . $enable_debug
+
+  Bindings:
+    perl  . . . . . . . . $with_perl_bindings
+
+  Modules:
+    aggregation . . . . . $enable_aggregation
+    amqp    . . . . . . . $enable_amqp
+    apache  . . . . . . . $enable_apache
+    apcups  . . . . . . . $enable_apcups
+    aquaero . . . . . . . $enable_aquaero
+    apple_sensors . . . . $enable_apple_sensors
+    ascent  . . . . . . . $enable_ascent
+    battery . . . . . . . $enable_battery
+    bind  . . . . . . . . $enable_bind
+    conntrack . . . . . . $enable_conntrack
+    contextswitch . . . . $enable_contextswitch
+    cgroups . . . . . . . $enable_cgroups
+    cpu . . . . . . . . . $enable_cpu
+    cpufreq . . . . . . . $enable_cpufreq
+    csv . . . . . . . . . $enable_csv
+    curl  . . . . . . . . $enable_curl
+    curl_json . . . . . . $enable_curl_json
+    curl_xml  . . . . . . $enable_curl_xml
+    dbi . . . . . . . . . $enable_dbi
+    df  . . . . . . . . . $enable_df
+    disk  . . . . . . . . $enable_disk
+    dns . . . . . . . . . $enable_dns
+    email . . . . . . . . $enable_email
+    entropy . . . . . . . $enable_entropy
+    ethstat . . . . . . . $enable_ethstat
+    exec  . . . . . . . . $enable_exec
+    filecount . . . . . . $enable_filecount
+    fscache . . . . . . . $enable_fscache
+    gmond . . . . . . . . $enable_gmond
+    hddtemp . . . . . . . $enable_hddtemp
+    interface . . . . . . $enable_interface
+    ipmi  . . . . . . . . $enable_ipmi
+    iptables  . . . . . . $enable_iptables
+    ipvs  . . . . . . . . $enable_ipvs
+    irq . . . . . . . . . $enable_irq
+    java  . . . . . . . . $enable_java
+    libvirt . . . . . . . $enable_libvirt
+    load  . . . . . . . . $enable_load
+    logfile . . . . . . . $enable_logfile
+    lpar  . . . . . . . . $enable_lpar
+    log_logstash  . . . . $enable_log_logstash
+    lvm . . . . . . . . . $enable_lvm
+    madwifi . . . . . . . $enable_madwifi
+    match_empty_counter . $enable_match_empty_counter
+    match_hashed  . . . . $enable_match_hashed
+    match_regex . . . . . $enable_match_regex
+    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
+    mic . . . . . . . . . $enable_mic
+    modbus  . . . . . . . $enable_modbus
+    multimeter  . . . . . $enable_multimeter
+    mysql . . . . . . . . $enable_mysql
+    netapp  . . . . . . . $enable_netapp
+    netlink . . . . . . . $enable_netlink
+    network . . . . . . . $enable_network
+    nfs . . . . . . . . . $enable_nfs
+    nginx . . . . . . . . $enable_nginx
+    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
+    openvpn . . . . . . . $enable_openvpn
+    oracle  . . . . . . . $enable_oracle
+    perl  . . . . . . . . $enable_perl
+    pf  . . . . . . . . . $enable_pf
+    pinba . . . . . . . . $enable_pinba
+    ping  . . . . . . . . $enable_ping
+    postgresql  . . . . . $enable_postgresql
+    powerdns  . . . . . . $enable_powerdns
+    processes . . . . . . $enable_processes
+    protocols . . . . . . $enable_protocols
+    python  . . . . . . . $enable_python
+    redis . . . . . . . . $enable_redis
+    routeros  . . . . . . $enable_routeros
+    rrdcached . . . . . . $enable_rrdcached
+    rrdtool . . . . . . . $enable_rrdtool
+    sensors . . . . . . . $enable_sensors
+    serial  . . . . . . . $enable_serial
+    sigrok  . . . . . . . $enable_sigrok
+    snmp  . . . . . . . . $enable_snmp
+    statsd  . . . . . . . $enable_statsd
+    swap  . . . . . . . . $enable_swap
+    syslog  . . . . . . . $enable_syslog
+    table . . . . . . . . $enable_table
+    tail  . . . . . . . . $enable_tail
+    tail_csv  . . . . . . $enable_tail_csv
+    tape  . . . . . . . . $enable_tape
+    target_notification . $enable_target_notification
+    target_replace  . . . $enable_target_replace
+    target_scale  . . . . $enable_target_scale
+    target_set  . . . . . $enable_target_set
+    target_v5upgrade  . . $enable_target_v5upgrade
+    tcpconns  . . . . . . $enable_tcpconns
+    teamspeak2  . . . . . $enable_teamspeak2
+    ted . . . . . . . . . $enable_ted
+    thermal . . . . . . . $enable_thermal
+    threshold . . . . . . $enable_threshold
+    tokyotyrant . . . . . $enable_tokyotyrant
+    unixsock  . . . . . . $enable_unixsock
+    uptime  . . . . . . . $enable_uptime
+    users . . . . . . . . $enable_users
+    uuid  . . . . . . . . $enable_uuid
+    varnish . . . . . . . $enable_varnish
+    vmem  . . . . . . . . $enable_vmem
+    vserver . . . . . . . $enable_vserver
+    wireless  . . . . . . $enable_wireless
+    write_graphite  . . . $enable_write_graphite
+    write_http  . . . . . $enable_write_http
+    write_kafka . . . . . $enable_write_kafka
+    write_mongodb . . . . $enable_write_mongodb
+    write_redis . . . . . $enable_write_redis
+    write_riemann . . . . $enable_write_riemann
+    xmms  . . . . . . . . $enable_xmms
+    zfs_arc . . . . . . . $enable_zfs_arc
+
+EOF
+
+if test "x$dependency_error" = "xyes"; then
+       AC_MSG_ERROR("Some plugins are missing dependencies - see the summary above for details")
+fi
+
+if test "x$dependency_warning" = "xyes"; then
+       AC_MSG_WARN("Some plugins seem to have missing dependencies but have been enabled forcibly - see the summary above for details")
+fi
+
+# vim: set fdm=marker :
diff --git a/configure.in b/configure.in
deleted file mode 100644 (file)
index 98395ed..0000000
+++ /dev/null
@@ -1,5315 +0,0 @@
-dnl Process this file with autoconf to produce a configure script.
-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])
-
-m4_ifdef([LT_PACKAGE_VERSION],
-       # libtool >= 2.2
-       [
-        LT_CONFIG_LTDL_DIR([libltdl])
-        LT_INIT([dlopen])
-        LTDL_INIT([convenience])
-        AC_DEFINE(LIBTOOL_VERSION, 2, [Define to used libtool version.])
-       ]
-,
-       # libtool <= 1.5
-       [
-        AC_LIBLTDL_CONVENIENCE
-        AC_SUBST(LTDLINCL)
-        AC_SUBST(LIBLTDL)
-        AC_LIBTOOL_DLOPEN
-        AC_CONFIG_SUBDIRS(libltdl)
-        AC_DEFINE(LIBTOOL_VERSION, 1, [Define to used libtool version.])
-       ]
-)
-
-AM_INIT_AUTOMAKE(dist-bzip2)
-AC_LANG(C)
-
-AC_PREFIX_DEFAULT("/opt/collectd")
-
-AC_SYS_LARGEFILE
-
-#
-# Checks for programs.
-#
-AC_PROG_CC
-AC_PROG_CPP
-AC_PROG_INSTALL
-AC_PROG_LN_S
-AC_PROG_MAKE_SET
-AM_PROG_CC_C_O
-AM_CONDITIONAL(COMPILER_IS_GCC, test "x$GCC" = "xyes")
-
-AC_DISABLE_STATIC
-AC_PROG_LIBTOOL
-AC_PROG_LEX
-AC_PROG_YACC
-PKG_PROG_PKG_CONFIG
-
-AC_CHECK_PROG([have_protoc_c], [protoc-c], [yes], [no])
-AM_CONDITIONAL(HAVE_PROTOC_C, test "x$have_protoc_c" = "xyes")
-
-AC_MSG_CHECKING([for kernel type ($host_os)])
-case $host_os in
-       *linux*)
-       AC_DEFINE([KERNEL_LINUX], 1, [True if program is to be compiled for a Linux kernel])
-       ac_system="Linux"
-       ;;
-       *solaris*)
-       AC_DEFINE([KERNEL_SOLARIS], 1, [True if program is to be compiled for a Solaris kernel])
-       ac_system="Solaris"
-       ;;
-       *darwin*)
-       ac_system="Darwin"
-       ;;
-       *openbsd*)
-       ac_system="OpenBSD"
-       ;;
-       *aix*)
-       AC_DEFINE([KERNEL_AIX], 1, [True if program is to be compiled for a AIX kernel])
-       ac_system="AIX"
-       ;;
-       *)
-       ac_system="unknown"
-esac
-AC_MSG_RESULT([$ac_system])
-
-if test "x$ac_system" = "xLinux"
-then
-       AC_ARG_VAR([KERNEL_DIR], [path to Linux kernel sources])
-       if test -z "$KERNEL_DIR"
-       then
-               KERNEL_DIR="/lib/modules/`uname -r`/source"
-       fi
-
-       KERNEL_CFLAGS="-I$KERNEL_DIR/include"
-       AC_SUBST(KERNEL_CFLAGS)
-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
-       AC_DEFINE(_THREAD_SAFE_ERRNO, 1, [Define to use the thread-safe version of errno under AIX.])
-fi
-
-# Where to install .pc files.
-pkgconfigdir="${libdir}/pkgconfig"
-AC_SUBST(pkgconfigdir)
-
-# Check for standards compliance mode
-AC_ARG_ENABLE(standards,
-             AS_HELP_STRING([--enable-standards], [Enable standards compliance mode]),
-             [enable_standards="$enableval"],
-             [enable_standards="no"])
-if test "x$enable_standards" = "xyes"
-then
-       AC_DEFINE(_ISOC99_SOURCE,        1, [Define to enforce ISO C99 compliance.])
-       AC_DEFINE(_POSIX_C_SOURCE, 200809L, [Define to enforce POSIX.1-2008 compliance.])
-       AC_DEFINE(_XOPEN_SOURCE,       700, [Define to enforce X/Open 7 (XSI) compliance.])
-       AC_DEFINE(_REENTRANT,            1, [Define to enable reentrancy interfaces.])
-       if test "x$GCC" = "xyes"
-       then
-               CFLAGS="$CFLAGS -std=c99"
-       fi
-fi
-AM_CONDITIONAL(BUILD_FEATURE_STANDARDS, test "x$enable_standards" = "xyes")
-
-#
-# Checks for header files.
-#
-AC_HEADER_STDC
-AC_HEADER_SYS_WAIT
-AC_HEADER_DIRENT
-AC_HEADER_STDBOOL
-
-AC_CHECK_HEADERS(stdio.h errno.h math.h stdarg.h syslog.h fcntl.h signal.h assert.h sys/types.h sys/socket.h sys/select.h poll.h netdb.h arpa/inet.h sys/resource.h sys/param.h kstat.h regex.h sys/ioctl.h endian.h sys/isa_defs.h)
-
-# For ping library
-AC_CHECK_HEADERS(netinet/in_systm.h, [], [],
-[#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-])
-AC_CHECK_HEADERS(netinet/in.h, [], [],
-[#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-])
-AC_CHECK_HEADERS(netinet/ip.h, [], [],
-[#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-])
-AC_CHECK_HEADERS(netinet/ip_icmp.h, [], [],
-[#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-#if HAVE_NETINET_IP_H
-# include <netinet/ip.h>
-#endif
-])
-AC_CHECK_HEADERS(netinet/ip_var.h, [], [],
-[#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-#if HAVE_NETINET_IP_H
-# include <netinet/ip.h>
-#endif
-])
-AC_CHECK_HEADERS(netinet/ip6.h, [], [],
-[#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-])
-AC_CHECK_HEADERS(netinet/icmp6.h, [], [],
-[#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-#if HAVE_NETINET_IP6_H
-# include <netinet/ip6.h>
-#endif
-])
-AC_CHECK_HEADERS(netinet/tcp.h, [], [],
-[#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-#if HAVE_NETINET_IP_H
-# include <netinet/ip.h>
-#endif
-])
-AC_CHECK_HEADERS(netinet/udp.h, [], [],
-[#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-#if HAVE_NETINET_IP_H
-# include <netinet/ip.h>
-#endif
-])
-
-# For cpu modules
-AC_CHECK_HEADERS(sys/dkstat.h)
-if test "x$ac_system" = "xDarwin"
-then
-       AC_CHECK_HEADERS(mach/mach_init.h mach/host_priv.h mach/mach_error.h mach/mach_host.h mach/mach_port.h mach/mach_types.h mach/message.h mach/processor_set.h mach/processor.h mach/processor_info.h mach/task.h mach/thread_act.h mach/vm_region.h mach/vm_map.h mach/vm_prot.h mach/vm_statistics.h mach/kern_return.h)
-       AC_CHECK_HEADERS(CoreFoundation/CoreFoundation.h IOKit/IOKitLib.h IOKit/IOTypes.h IOKit/ps/IOPSKeys.h IOKit/IOBSD.h IOKit/storage/IOBlockStorageDriver.h)
-fi
-AC_CHECK_HEADERS(sys/sysctl.h, [], [],
-[
-#if HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
-#if HAVE_SYS_PARAM_H
-# include <sys/param.h>
-#endif
-])
-
-AC_MSG_CHECKING([for sysctl kern.cp_times])
-if test -x /sbin/sysctl
-then
-       /sbin/sysctl kern.cp_times 2>/dev/null
-       if test $? -eq 0
-       then
-               AC_MSG_RESULT([yes])
-               AC_DEFINE(HAVE_SYSCTL_KERN_CP_TIMES, 1,
-               [Define if sysctl supports kern.cp_times])
-       else
-               AC_MSG_RESULT([no])
-       fi
-else
-       AC_MSG_RESULT([no])
-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, [], [],
-[
-#if HAVE_IOKIT_IOKITLIB_H
-#  include <IOKit/IOKitLib.h>
-#endif
-#if HAVE_IOKIT_IOTYPES_H
-#  include <IOKit/IOTypes.h>
-#endif
-])
-
-# For the swap module
-have_linux_wireless_h="no"
-if test "x$ac_system" = "xLinux"
-then
-  AC_CHECK_HEADERS(linux/wireless.h,
-                  [have_linux_wireless_h="yes"],
-                  [have_linux_wireless_h="no"],
-[
-#include <dirent.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-])
-fi
-
-# For the swap module
-have_sys_swap_h="yes"
-AC_CHECK_HEADERS(sys/swap.h vm/anon.h, [], [have_sys_swap_h="no"],
-[
-#undef _FILE_OFFSET_BITS
-#undef _LARGEFILE64_SOURCE
-#if HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
-#if HAVE_SYS_PARAM_H
-# include <sys/param.h>
-#endif
-])
-
-if test "x$have_sys_swap_h$ac_system" = "xnoSolaris"
-then
-       hint_64=""
-       if test "x$GCC" = "xyes"
-       then
-               hint_64="CFLAGS='-m64'"
-       else
-               hint_64="CFLAGS='-xarch=v9'"
-       fi
-       AC_MSG_NOTICE([Solaris detected and sys/swap.h not usable. Try building a 64-bit binary ($hint_64 ./configure).])
-fi
-
-# For load module
-# For the processes plugin
-# For users module
-AC_CHECK_HEADERS(sys/loadavg.h linux/config.h utmp.h utmpx.h)
-
-# For interface plugin
-AC_CHECK_HEADERS(ifaddrs.h)
-AC_CHECK_HEADERS(net/if.h, [], [],
-[
-#if HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
-#if HAVE_SYS_SOCKET_H
-#  include <sys/socket.h>
-#endif
-])
-AC_CHECK_HEADERS(linux/if.h, [], [],
-[
-#if HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
-#if HAVE_SYS_SOCKET_H
-#  include <sys/socket.h>
-#endif
-])
-AC_CHECK_HEADERS(linux/netdevice.h, [], [],
-[
-#if HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
-#if HAVE_SYS_SOCKET_H
-#  include <sys/socket.h>
-#endif
-#if HAVE_LINUX_IF_H
-# include <linux/if.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"
-have_ip_vs_h="no"
-ip_vs_h_needs_kernel_cflags="no"
-if test "x$ac_system" = "xLinux"
-then
-       AC_CHECK_HEADERS(linux/ip_vs.h, [have_linux_ip_vs_h="yes"])
-       AC_CHECK_HEADERS(net/ip_vs.h, [have_net_ip_vs_h="yes"])
-       AC_CHECK_HEADERS(ip_vs.h, [have_ip_vs_h="yes"])
-
-       if test "x$have_linux_ip_vs_h$have_net_ip_vs_h$have_ip_vs_h" = "xnonono" && test -d "$KERNEL_DIR"
-       then
-               SAVE_CFLAGS="$CFLAGS"
-               CFLAGS="$CFLAGS $KERNEL_CFLAGS"
-
-               AC_MSG_NOTICE([Did not find ip_vs.h. Trying again using headers from $KERNEL_DIR.])
-
-               AC_CHECK_HEADERS(linux/ip_vs.h, [have_linux_ip_vs_h="yes"])
-               AC_CHECK_HEADERS(net/ip_vs.h, [have_net_ip_vs_h="yes"])
-               AC_CHECK_HEADERS(ip_vs.h, [have_ip_vs_h="yes"])
-
-               if test "x$have_linux_ip_vs_h" = "xyes" || test "x$have_net_ip_vs_h" = "xyes" || test "x$have_ip_vs_h" = "xyes"
-               then
-                       ip_vs_h_needs_kernel_cflags="yes"
-               fi
-
-               CFLAGS="$SAVE_CFLAGS"
-       fi
-fi
-AM_CONDITIONAL(IP_VS_H_NEEDS_KERNEL_CFLAGS, test "x$ip_vs_h_needs_kernel_cflags" = "xyes")
-
-# For quota module
-AC_CHECK_HEADERS(sys/ucred.h, [], [],
-[
-#if HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
-#if HAVE_SYS_PARAM_H
-# include <sys/param.h>
-#endif
-])
-
-# For mount interface
-AC_CHECK_HEADERS(sys/mount.h, [], [],
-[
-#if HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
-#if HAVE_SYS_PARAM_H
-# include <sys/param.h>
-#endif
-])
-
-# For the email plugin
-AC_CHECK_HEADERS(linux/un.h, [], [],
-[
-#if HAVE_SYS_SOCKET_H
-#      include <sys/socket.h>
-#endif
-])
-
-AC_CHECK_HEADERS(pwd.h grp.h sys/un.h ctype.h limits.h xfs/xqm.h fs_info.h fshelp.h paths.h mntent.h mnttab.h sys/fstyp.h sys/fs_types.h sys/mntent.h sys/mnttab.h sys/statfs.h sys/statvfs.h sys/vfs.h sys/vfstab.h kvm.h wordexp.h)
-
-# For the dns plugin
-AC_CHECK_HEADERS(arpa/nameser.h)
-AC_CHECK_HEADERS(arpa/nameser_compat.h, [], [],
-[
-#if HAVE_ARPA_NAMESER_H
-# include <arpa/nameser.h>
-#endif
-])
-
-AC_CHECK_HEADERS(net/if_arp.h, [], [],
-[#if HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-#endif
-])
-AC_CHECK_HEADERS(net/ppp_defs.h)
-AC_CHECK_HEADERS(net/if_ppp.h, [], [],
-[#if HAVE_NET_PPP_DEFS_H
-# include <net/ppp_defs.h>
-#endif
-])
-AC_CHECK_HEADERS(netinet/if_ether.h, [], [],
-[#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-#endif
-#if HAVE_NET_IF_H
-# include <net/if.h>
-#endif
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-])
-
-AC_CHECK_HEADERS(netinet/ip_compat.h)
-
-have_net_pfvar_h="no"
-AC_CHECK_HEADERS(net/pfvar.h, [have_net_pfvar_h="yes"])
-
-# For the multimeter plugin
-have_termios_h="no"
-AC_CHECK_HEADERS(termios.h, [have_termios_h="yes"])
-
-#
-# Checks for typedefs, structures, and compiler characteristics.
-#
-AC_C_CONST
-AC_TYPE_PID_T
-AC_TYPE_SIZE_T
-AC_TYPE_UID_T
-AC_HEADER_TIME
-
-#
-# Checks for library functions.
-#
-AC_PROG_GCC_TRADITIONAL
-AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf setenv if_indextoname)
-
-AC_FUNC_STRERROR_R
-
-SAVE_CFLAGS="$CFLAGS"
-# Emulate behavior of src/Makefile.am
-if test "x$GCC" = "xyes"
-then
-       CFLAGS="$CFLAGS -Wall -Werror"
-fi
-
-AC_CACHE_CHECK([for strtok_r],
-  [c_cv_have_strtok_r_default],
-  AC_LINK_IFELSE(
-    AC_LANG_PROGRAM(
-    [[[[
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-    ]]]],
-    [[[[
-      char buffer[] = "foo,bar,baz";
-      char *token;
-      char *dummy;
-      char *saveptr;
-
-      dummy = buffer;
-      saveptr = NULL;
-      while ((token = strtok_r (dummy, ",", &saveptr)) != NULL)
-      {
-       dummy = NULL;
-        printf ("token = %s;\n", token);
-      }
-    ]]]]),
-    [c_cv_have_strtok_r_default="yes"],
-    [c_cv_have_strtok_r_default="no"]
-  )
-)
-
-if test "x$c_cv_have_strtok_r_default" = "xno"
-then
-  CFLAGS="$CFLAGS -D_REENTRANT=1"
-
-  AC_CACHE_CHECK([if strtok_r needs _REENTRANT],
-    [c_cv_have_strtok_r_reentrant],
-    AC_LINK_IFELSE(
-      AC_LANG_PROGRAM(
-      [[[[
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-      ]]]],
-      [[[[
-        char buffer[] = "foo,bar,baz";
-        char *token;
-        char *dummy;
-        char *saveptr;
-
-        dummy = buffer;
-        saveptr = NULL;
-        while ((token = strtok_r (dummy, ",", &saveptr)) != NULL)
-        {
-         dummy = NULL;
-          printf ("token = %s;\n", token);
-        }
-      ]]]]),
-      [c_cv_have_strtok_r_reentrant="yes"],
-      [AC_MSG_FAILURE([strtok_r isn't available. Please file a bugreport!])]
-    )
-  )
-fi
-
-CFLAGS="$SAVE_CFLAGS"
-if test "x$c_cv_have_strtok_r_reentrant" = "xyes"
-then
-       CFLAGS="$CFLAGS -D_REENTRANT=1"
-fi
-
-AC_CHECK_FUNCS(getpwnam_r getgrnam_r setgroups regcomp regerror regexec regfree)
-
-socket_needs_socket="no"
-AC_CHECK_FUNCS(socket, [], AC_CHECK_LIB(socket, socket, [socket_needs_socket="yes"], AC_MSG_ERROR(cannot find socket)))
-AM_CONDITIONAL(BUILD_WITH_LIBSOCKET, test "x$socket_needs_socket" = "xyes")
-
-clock_gettime_needs_rt="no"
-clock_gettime_needs_posix4="no"
-have_clock_gettime="no"
-AC_CHECK_FUNCS(clock_gettime, [have_clock_gettime="yes"])
-if test "x$have_clock_gettime" = "xno"
-then
-       AC_CHECK_LIB(rt, clock_gettime, [clock_gettime_needs_rt="yes"
-                                        have_clock_gettime="yes"])
-fi
-if test "x$have_clock_gettime" = "xno"
-then
-       AC_CHECK_LIB(posix4, clock_gettime, [clock_gettime_needs_posix4="yes"
-                                            have_clock_gettime="yes"])
-fi
-if test "x$have_clock_gettime" = "xyes"
-then
-       AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [Define if the clock_gettime(2) function is available.])
-else
-       AC_MSG_WARN(cannot find clock_gettime)
-fi
-
-nanosleep_needs_rt="no"
-nanosleep_needs_posix4="no"
-AC_CHECK_FUNCS(nanosleep,
-    [],
-    AC_CHECK_LIB(rt, nanosleep,
-        [nanosleep_needs_rt="yes"],
-        AC_CHECK_LIB(posix4, nanosleep,
-            [nanosleep_needs_posix4="yes"],
-            AC_MSG_ERROR(cannot find nanosleep))))
-
-AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$clock_gettime_needs_rt" = "xyes" || test "x$nanosleep_needs_rt" = "xyes")
-AM_CONDITIONAL(BUILD_WITH_LIBPOSIX4, test "x$clock_gettime_needs_posix4" = "xyes" || test "x$nanosleep_needs_posix4" = "xyes")
-
-AC_CHECK_FUNCS(sysctl, [have_sysctl="yes"], [have_sysctl="no"])
-AC_CHECK_FUNCS(sysctlbyname, [have_sysctlbyname="yes"], [have_sysctlbyname="no"])
-AC_CHECK_FUNCS(host_statistics, [have_host_statistics="yes"], [have_host_statistics="no"])
-AC_CHECK_FUNCS(processor_info, [have_processor_info="yes"], [have_processor_info="no"])
-AC_CHECK_FUNCS(thread_info, [have_thread_info="yes"], [have_thread_info="no"])
-AC_CHECK_FUNCS(statfs, [have_statfs="yes"], [have_statfs="no"])
-AC_CHECK_FUNCS(statvfs, [have_statvfs="yes"], [have_statvfs="no"])
-AC_CHECK_FUNCS(getifaddrs, [have_getifaddrs="yes"], [have_getifaddrs="no"])
-AC_CHECK_FUNCS(getloadavg, [have_getloadavg="yes"], [have_getloadavg="no"])
-AC_CHECK_FUNCS(syslog, [have_syslog="yes"], [have_syslog="no"])
-AC_CHECK_FUNCS(getutent, [have_getutent="yes"], [have_getutent="no"])
-AC_CHECK_FUNCS(getutxent, [have_getutxent="yes"], [have_getutxent="no"])
-
-# Check for strptime {{{
-if test "x$GCC" = "xyes"
-then
-       SAVE_CFLAGS="$CFLAGS"
-       CFLAGS="$CFLAGS -Wall -Wextra -Werror"
-fi
-
-AC_CHECK_FUNCS(strptime, [have_strptime="yes"], [have_strptime="no"])
-if test "x$have_strptime" = "xyes"
-then
-       AC_CACHE_CHECK([whether strptime is exported by default],
-                      [c_cv_have_strptime_default],
-                      AC_COMPILE_IFELSE(
-AC_LANG_PROGRAM(
-[[
-AC_INCLUDES_DEFAULT
-#include <time.h>
-]],
-[[
- struct tm stm;
- (void) strptime ("2010-12-30%13:42:42", "%Y-%m-%dT%T", &stm);
-]]),
-                      [c_cv_have_strptime_default="yes"],
-                      [c_cv_have_strptime_default="no"]))
-fi
-if test "x$have_strptime" = "xyes" && test "x$c_cv_have_strptime_default" = "xno"
-then
-       AC_CACHE_CHECK([whether strptime needs standards mode],
-                      [c_cv_have_strptime_standards],
-                      AC_COMPILE_IFELSE(
-AC_LANG_PROGRAM(
-[[
-#ifndef _ISOC99_SOURCE
-# define _ISOC99_SOURCE 1
-#endif
-#ifndef _POSIX_C_SOURCE
-# define _POSIX_C_SOURCE 200112L
-#endif
-#ifndef _XOPEN_SOURCE
-# define _XOPEN_SOURCE 500
-#endif
-AC_INCLUDES_DEFAULT
-#include <time.h>
-]],
-[[
- struct tm stm;
- (void) strptime ("2010-12-30%13:42:42", "%Y-%m-%dT%T", &stm);
-]]),
-                      [c_cv_have_strptime_standards="yes"],
-                      [c_cv_have_strptime_standards="no"]))
-
-       if test "x$c_cv_have_strptime_standards" = "xyes"
-       then
-               AC_DEFINE([STRPTIME_NEEDS_STANDARDS], 1, [Set to true if strptime is only exported in X/Open mode (GNU libc).])
-       else
-               have_strptime="no"
-       fi
-fi
-
-if test "x$GCC" = "xyes"
-then
-       CFLAGS="$SAVE_CFLAGS"
-fi
-
-# }}} Check for strptime
-
-AC_CHECK_FUNCS(swapctl, [have_swapctl="yes"], [have_swapctl="no"])
-if test "x$have_swapctl" = "xyes"; then
-        AC_CACHE_CHECK([whether swapctl takes two arguments],
-                [c_cv_have_swapctl_two_args],
-                AC_COMPILE_IFELSE(
-                        AC_LANG_PROGRAM([[AC_INCLUDES_DEFAULT
-#if HAVE_SYS_SWAP_H && !defined(_LP64) && _FILE_OFFSET_BITS == 64
-#  undef _FILE_OFFSET_BITS
-#  undef _LARGEFILE64_SOURCE
-#endif
-#include <sys/stat.h>
-#include <sys/swap.h>]],
-                                [[
-                                int num = swapctl(0, NULL);
-                                ]]
-                        ),
-                        [c_cv_have_swapctl_two_args="yes"],
-                        [c_cv_have_swapctl_two_args="no"]
-                )
-        )
-        AC_CACHE_CHECK([whether swapctl takes three arguments],
-                [c_cv_have_swapctl_three_args],
-                AC_COMPILE_IFELSE(
-                        AC_LANG_PROGRAM([[AC_INCLUDES_DEFAULT
-#if HAVE_SYS_SWAP_H && !defined(_LP64) && _FILE_OFFSET_BITS == 64
-#  undef _FILE_OFFSET_BITS
-#  undef _LARGEFILE64_SOURCE
-#endif
-#include <sys/stat.h>
-#include <sys/swap.h>]],
-                                [[
-                                int num = swapctl(0, NULL,0);
-                                ]]
-                        ),
-                        [c_cv_have_swapctl_three_args="yes"],
-                        [c_cv_have_swapctl_three_args="no"]
-                )
-        )
-fi
-# Check for different versions of `swapctl' here..
-if test "x$have_swapctl" = "xyes"; then
-        if test "x$c_cv_have_swapctl_two_args" = "xyes"; then
-                AC_DEFINE(HAVE_SWAPCTL_TWO_ARGS, 1,
-                          [Define if the function swapctl exists and takes two arguments.])
-        fi
-        if test "x$c_cv_have_swapctl_three_args" = "xyes"; then
-                AC_DEFINE(HAVE_SWAPCTL_THREE_ARGS, 1,
-                          [Define if the function swapctl exists and takes three arguments.])
-        fi
-fi
-
-# Check for NAN
-AC_ARG_WITH(nan-emulation, [AS_HELP_STRING([--with-nan-emulation], [use emulated NAN. For crosscompiling only.])],
-[
- if test "x$withval" = "xno"; then
-        nan_type="none"
- else if test "x$withval" = "xyes"; then
-        nan_type="zero"
- else
-        nan_type="$withval"
- fi; fi
-],
-[nan_type="none"])
-if test "x$nan_type" = "xnone"; then
-  AC_CACHE_CHECK([whether NAN is defined by default],
-    [c_cv_have_nan_default],
-    AC_COMPILE_IFELSE(
-      AC_LANG_PROGRAM(
-      [[
-#include <stdlib.h>
-#include <math.h>
-static double foo = NAN;
-      ]],
-      [[
-       if (isnan (foo))
-        return 0;
-       else
-       return 1;
-      ]]),
-      [c_cv_have_nan_default="yes"],
-      [c_cv_have_nan_default="no"]
-    )
-  )
-  if test "x$c_cv_have_nan_default" = "xyes"
-  then
-    nan_type="default"
-  fi
-fi
-if test "x$nan_type" = "xnone"; then
-  AC_CACHE_CHECK([whether NAN is defined by __USE_ISOC99],
-    [c_cv_have_nan_isoc],
-    AC_COMPILE_IFELSE(
-      AC_LANG_PROGRAM(
-      [[
-#include <stdlib.h>
-#define __USE_ISOC99 1
-#include <math.h>
-static double foo = NAN;
-      ]],
-      [[
-       if (isnan (foo))
-        return 0;
-       else
-       return 1;
-      ]]),
-      [c_cv_have_nan_isoc="yes"],
-      [c_cv_have_nan_isoc="no"]
-    )
-  )
-  if test "x$c_cv_have_nan_isoc" = "xyes"
-  then
-    nan_type="isoc99"
-  fi
-fi
-if test "x$nan_type" = "xnone"; then
-  SAVE_LDFLAGS=$LDFLAGS
-  LDFLAGS="$LDFLAGS -lm"
-  AC_CACHE_CHECK([whether NAN can be defined by 0/0],
-    [c_cv_have_nan_zero],
-    AC_RUN_IFELSE(
-      AC_LANG_PROGRAM(
-      [[
-#include <stdlib.h>
-#include <math.h>
-#ifdef NAN
-# undef NAN
-#endif
-#define NAN (0.0 / 0.0)
-#ifndef isnan
-# define isnan(f) ((f) != (f))
-#endif
-static double foo = NAN;
-      ]],
-      [[
-       if (isnan (foo))
-        return 0;
-       else
-       return 1;
-      ]]),
-      [c_cv_have_nan_zero="yes"],
-      [c_cv_have_nan_zero="no"]
-    )
-  )
-  LDFLAGS=$SAVE_LDFLAGS
-  if test "x$c_cv_have_nan_zero" = "xyes"
-  then
-    nan_type="zero"
-  fi
-fi
-
-if test "x$nan_type" = "xdefault"; then
-  AC_DEFINE(NAN_STATIC_DEFAULT, 1,
-    [Define if NAN is defined by default and can initialize static variables.])
-else if test "x$nan_type" = "xisoc99"; then
-  AC_DEFINE(NAN_STATIC_ISOC, 1,
-    [Define if NAN is defined by __USE_ISOC99 and can initialize static variables.])
-else if test "x$nan_type" = "xzero"; then
-  AC_DEFINE(NAN_ZERO_ZERO, 1,
-    [Define if NAN can be defined as (0.0 / 0.0)])
-else
-  AC_MSG_ERROR([Didn't find out how to statically initialize variables to NAN. Sorry.])
-fi; fi; fi
-
-AC_ARG_WITH(fp-layout, [AS_HELP_STRING([--with-fp-layout], [set the memory layout of doubles. For crosscompiling only.])],
-[
- if test "x$withval" = "xnothing"; then
-       fp_layout_type="nothing"
- else if test "x$withval" = "xendianflip"; then
-       fp_layout_type="endianflip"
- else if test "x$withval" = "xintswap"; then
-       fp_layout_type="intswap"
- else
-       AC_MSG_ERROR([Invalid argument for --with-fp-layout. Valid arguments are: nothing, endianflip, intswap]);
-fi; fi; fi
-],
-[fp_layout_type="unknown"])
-
-if test "x$fp_layout_type" = "xunknown"; then
-  AC_CACHE_CHECK([if doubles are stored in x86 representation],
-    [c_cv_fp_layout_need_nothing],
-    AC_RUN_IFELSE(
-      AC_LANG_PROGRAM(
-      [[[[
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-#if HAVE_STDBOOL_H
-# include <stdbool.h>
-#endif
-      ]]]],
-      [[[[
-       uint64_t i0;
-       uint64_t i1;
-       uint8_t c[8];
-       double d;
-
-       d = 8.642135e130; 
-       memcpy ((void *) &i0, (void *) &d, 8);
-
-       i1 = i0;
-       memcpy ((void *) c, (void *) &i1, 8);
-
-       if ((c[0] == 0x2f) && (c[1] == 0x25)
-                       && (c[2] == 0xc0) && (c[3] == 0xc7)
-                       && (c[4] == 0x43) && (c[5] == 0x2b)
-                       && (c[6] == 0x1f) && (c[7] == 0x5b))
-               return (0);
-       else
-               return (1);
-      ]]]]),
-      [c_cv_fp_layout_need_nothing="yes"],
-      [c_cv_fp_layout_need_nothing="no"]
-    )
-  )
-  if test "x$c_cv_fp_layout_need_nothing" = "xyes"; then
-    fp_layout_type="nothing"
-  fi
-fi
-if test "x$fp_layout_type" = "xunknown"; then
-  AC_CACHE_CHECK([if endianflip converts to x86 representation],
-    [c_cv_fp_layout_need_endianflip],
-    AC_RUN_IFELSE(
-      AC_LANG_PROGRAM(
-      [[[[
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-#if HAVE_STDBOOL_H
-# include <stdbool.h>
-#endif
-#define endianflip(A) ((((uint64_t)(A) & 0xff00000000000000LL) >> 56) | \
-                       (((uint64_t)(A) & 0x00ff000000000000LL) >> 40) | \
-                       (((uint64_t)(A) & 0x0000ff0000000000LL) >> 24) | \
-                       (((uint64_t)(A) & 0x000000ff00000000LL) >> 8)  | \
-                       (((uint64_t)(A) & 0x00000000ff000000LL) << 8)  | \
-                       (((uint64_t)(A) & 0x0000000000ff0000LL) << 24) | \
-                       (((uint64_t)(A) & 0x000000000000ff00LL) << 40) | \
-                       (((uint64_t)(A) & 0x00000000000000ffLL) << 56))
-      ]]]],
-      [[[[
-       uint64_t i0;
-       uint64_t i1;
-       uint8_t c[8];
-       double d;
-
-       d = 8.642135e130; 
-       memcpy ((void *) &i0, (void *) &d, 8);
-
-       i1 = endianflip (i0);
-       memcpy ((void *) c, (void *) &i1, 8);
-
-       if ((c[0] == 0x2f) && (c[1] == 0x25)
-                       && (c[2] == 0xc0) && (c[3] == 0xc7)
-                       && (c[4] == 0x43) && (c[5] == 0x2b)
-                       && (c[6] == 0x1f) && (c[7] == 0x5b))
-               return (0);
-       else
-               return (1);
-      ]]]]),
-      [c_cv_fp_layout_need_endianflip="yes"],
-      [c_cv_fp_layout_need_endianflip="no"]
-    )
-  )
-  if test "x$c_cv_fp_layout_need_endianflip" = "xyes"; then
-    fp_layout_type="endianflip"
-  fi
-fi
-if test "x$fp_layout_type" = "xunknown"; then
-  AC_CACHE_CHECK([if intswap converts to x86 representation],
-    [c_cv_fp_layout_need_intswap],
-    AC_RUN_IFELSE(
-      AC_LANG_PROGRAM(
-      [[[[
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-#if HAVE_STDBOOL_H
-# include <stdbool.h>
-#endif
-#define intswap(A)    ((((uint64_t)(A) & 0xffffffff00000000LL) >> 32) | \
-                       (((uint64_t)(A) & 0x00000000ffffffffLL) << 32))
-      ]]]],
-      [[[[
-       uint64_t i0;
-       uint64_t i1;
-       uint8_t c[8];
-       double d;
-
-       d = 8.642135e130; 
-       memcpy ((void *) &i0, (void *) &d, 8);
-
-       i1 = intswap (i0);
-       memcpy ((void *) c, (void *) &i1, 8);
-
-       if ((c[0] == 0x2f) && (c[1] == 0x25)
-                       && (c[2] == 0xc0) && (c[3] == 0xc7)
-                       && (c[4] == 0x43) && (c[5] == 0x2b)
-                       && (c[6] == 0x1f) && (c[7] == 0x5b))
-               return (0);
-       else
-               return (1);
-      ]]]]),
-      [c_cv_fp_layout_need_intswap="yes"],
-      [c_cv_fp_layout_need_intswap="no"]
-    )
-  )
-  if test "x$c_cv_fp_layout_need_intswap" = "xyes"; then
-    fp_layout_type="intswap"
-  fi
-fi
-
-if test "x$fp_layout_type" = "xnothing"; then
-  AC_DEFINE(FP_LAYOUT_NEED_NOTHING, 1,
-  [Define if doubles are stored in x86 representation.])
-else if test "x$fp_layout_type" = "xendianflip"; then
-  AC_DEFINE(FP_LAYOUT_NEED_ENDIANFLIP, 1,
-  [Define if endianflip is needed to convert to x86 representation.])
-else if test "x$fp_layout_type" = "xintswap"; then
-  AC_DEFINE(FP_LAYOUT_NEED_INTSWAP, 1,
-  [Define if intswap is needed to convert to x86 representation.])
-else
-  AC_MSG_ERROR([Didn't find out how doubles are stored in memory. Sorry.])
-fi; fi; fi
-
-have_getfsstat="no"
-AC_CHECK_FUNCS(getfsstat, [have_getfsstat="yes"])
-have_getvfsstat="no"
-AC_CHECK_FUNCS(getvfsstat, [have_getvfsstat="yes"])
-have_listmntent="no"
-AC_CHECK_FUNCS(listmntent, [have_listmntent="yes"])
-
-have_getmntent="no"
-AC_CHECK_FUNCS(getmntent, [have_getmntent="c"])
-if test "x$have_getmntent" = "xno"; then
-       AC_CHECK_LIB(sun, getmntent, [have_getmntent="sun"])
-fi
-if test "x$have_getmntent" = "xno"; then
-       AC_CHECK_LIB(seq, getmntent, [have_getmntent="seq"])
-fi
-if test "x$have_getmntent" = "xno"; then
-       AC_CHECK_LIB(gen, getmntent, [have_getmntent="gen"])
-fi
-
-if test "x$have_getmntent" = "xc"; then
-       AC_CACHE_CHECK([whether getmntent takes one argument],
-               [c_cv_have_one_getmntent],
-               AC_COMPILE_IFELSE(
-                       AC_LANG_PROGRAM([[AC_INCLUDES_DEFAULT
-#include "$srcdir/src/utils_mount.h"]],
-                               [[
-                                FILE *fh;
-                                struct mntent *me;
-                                fh = setmntent ("/etc/mtab", "r");
-                                me = getmntent (fh);
-                               ]]
-                       ),
-                       [c_cv_have_one_getmntent="yes"],
-                       [c_cv_have_one_getmntent="no"]
-               )
-       )
-       AC_CACHE_CHECK([whether getmntent takes two arguments],
-               [c_cv_have_two_getmntent],
-               AC_COMPILE_IFELSE(
-                       AC_LANG_PROGRAM([[AC_INCLUDES_DEFAULT
-#include "$srcdir/src/utils_mount.h"]],
-                               [[
-                                FILE *fh;
-                                struct mnttab mt;
-                                int status;
-                                fh = fopen ("/etc/mnttab", "r");
-                                status = getmntent (fh, &mt);
-                               ]]
-                       ),
-                       [c_cv_have_two_getmntent="yes"],
-                       [c_cv_have_two_getmntent="no"]
-               )
-       )
-fi
-
-# Check for different versions of `getmntent' here..
-
-if test "x$have_getmntent" = "xc"; then
-       if test "x$c_cv_have_one_getmntent" = "xyes"; then
-               AC_DEFINE(HAVE_ONE_GETMNTENT, 1,
-                         [Define if the function getmntent exists and takes one argument.])
-       fi
-       if test "x$c_cv_have_two_getmntent" = "xyes"; then
-               AC_DEFINE(HAVE_TWO_GETMNTENT, 1,
-                         [Define if the function getmntent exists and takes two arguments.])
-       fi
-fi
-if test "x$have_getmntent" = "xsun"; then
-       AC_DEFINE(HAVE_SUN_GETMNTENT, 1,
-                 [Define if the function getmntent exists. It's the version from libsun.])
-fi
-if test "x$have_getmntent" = "xseq"; then
-       AC_DEFINE(HAVE_SEQ_GETMNTENT, 1,
-                 [Define if the function getmntent exists. It's the version from libseq.])
-fi
-if test "x$have_getmntent" = "xgen"; then
-       AC_DEFINE(HAVE_GEN_GETMNTENT, 1,
-                 [Define if the function getmntent exists. It's the version from libgen.])
-fi
-
-# Check for htonll
-AC_MSG_CHECKING([if have htonll defined])
-
-    have_htonll="no"
-    AC_LINK_IFELSE([
-       AC_LANG_PROGRAM([
-#include <sys/types.h>
-#include <netinet/in.h>
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-       ], [
-          return htonll(0);
-       ])
-    ], [
-      have_htonll="yes"
-      AC_DEFINE(HAVE_HTONLL, 1, [Define if the function htonll exists.])
-    ])
-AC_MSG_RESULT([$have_htonll])
-
-# Check for structures
-AC_CHECK_MEMBERS([struct if_data.ifi_ibytes, struct if_data.ifi_opackets, struct if_data.ifi_ierrors],
-       [AC_DEFINE(HAVE_STRUCT_IF_DATA, 1, [Define if struct if_data exists and is usable.])],
-       [],
-       [
-       #include <sys/types.h>
-       #include <sys/socket.h>
-       #include <net/if.h>
-       ])
-AC_CHECK_MEMBERS([struct net_device_stats.rx_bytes, struct net_device_stats.tx_packets, struct net_device_stats.rx_errors],
-       [AC_DEFINE(HAVE_STRUCT_NET_DEVICE_STATS, 1, [Define if struct net_device_stats exists and is usable.])],
-       [],
-       [
-       #include <sys/types.h>
-       #include <sys/socket.h>
-       #include <linux/if.h>
-       #include <linux/netdevice.h>
-       ])
-
-AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [],
-       [],
-       [
-       #include <netinet/in.h>
-       #include <net/if.h>
-       ])
-
-AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct kinfo_proc.ki_rusage],
-       [
-               AC_DEFINE(HAVE_STRUCT_KINFO_PROC_FREEBSD, 1,
-                       [Define if struct kinfo_proc exists in the FreeBSD variant.])
-               have_struct_kinfo_proc_freebsd="yes"
-       ],
-       [
-               have_struct_kinfo_proc_freebsd="no"
-       ],
-       [
-AC_INCLUDES_DEFAULT
-#include <kvm.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/user.h>
-       ])
-
-AC_CHECK_MEMBERS([struct kinfo_proc.kp_proc, struct kinfo_proc.kp_eproc],
-       [
-               AC_DEFINE(HAVE_STRUCT_KINFO_PROC_OPENBSD, 1,
-                       [Define if struct kinfo_proc exists in the OpenBSD variant.])
-               have_struct_kinfo_proc_openbsd="yes"
-       ],
-       [
-               have_struct_kinfo_proc_openbsd="no"
-       ],
-       [
-AC_INCLUDES_DEFAULT
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <kvm.h>
-       ])
-
-AC_CHECK_MEMBERS([struct udphdr.uh_dport, struct udphdr.uh_sport], [], [],
-[#define _BSD_SOURCE
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-#if HAVE_NETINET_IP_H
-# include <netinet/ip.h>
-#endif
-#if HAVE_NETINET_UDP_H
-# include <netinet/udp.h>
-#endif
-])
-AC_CHECK_MEMBERS([struct udphdr.dest, struct udphdr.source], [], [],
-[#define _BSD_SOURCE
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-#if HAVE_NETINET_IP_H
-# include <netinet/ip.h>
-#endif
-#if HAVE_NETINET_UDP_H
-# include <netinet/udp.h>
-#endif
-])
-
-AC_CHECK_MEMBERS([kstat_io_t.nwritten, kstat_io_t.writes, kstat_io_t.nwrites, kstat_io_t.wtime],
-       [],
-       [],
-       [
-#if HAVE_KSTAT_H
-# include <kstat.h>
-#endif
-       ])
-
-#
-# Checks for libraries begin here
-#
-
-with_libresolv="yes"
-AC_CHECK_LIB(resolv, res_search,
-[
-       AC_DEFINE(HAVE_LIBRESOLV, 1, [Define to 1 if you have the 'resolv' library (-lresolv).])
-],
-[with_libresolv="no"])
-AM_CONDITIONAL(BUILD_WITH_LIBRESOLV, test "x$with_libresolv" = "xyes")
-
-dnl Check for HAL (hardware abstraction library)
-with_libhal="yes"
-AC_CHECK_LIB(hal,libhal_device_property_exists,
-            [AC_DEFINE(HAVE_LIBHAL, 1, [Define to 1 if you have 'hal' library])],
-            [with_libhal="no"])
-if test "x$with_libhal" = "xyes"; then
-       if test "x$PKG_CONFIG" != "x"; then
-               BUILD_WITH_LIBHAL_CFLAGS="`pkg-config --cflags hal`"
-               BUILD_WITH_LIBHAL_LIBS="`pkg-config --libs hal`"
-               AC_SUBST(BUILD_WITH_LIBHAL_CFLAGS)
-               AC_SUBST(BUILD_WITH_LIBHAL_LIBS)
-       fi
-fi
-
-m4_divert_once([HELP_WITH], [
-collectd additional packages:])
-
-AM_CONDITIONAL([BUILD_AIX],[test "x$x$ac_system" = "xAIX"]) 
-
-if test "x$ac_system" = "xAIX"
-then
-       with_perfstat="yes"
-       with_procinfo="yes"
-else
-       with_perfstat="no (AIX only)"
-       with_procinfo="no (AIX only)"
-fi
-
-if test "x$with_perfstat" = "xyes"
-then
-       AC_CHECK_LIB(perfstat, perfstat_reset, [with_perfstat="yes"], [with_perfstat="no (perfstat not found)"], [])
-#      AC_CHECK_HEADERS(sys/protosw.h libperfstat.h,, [with_perfstat="no (perfstat not found)"])
-fi
-if test "x$with_perfstat" = "xyes"
-then
-        AC_DEFINE(HAVE_PERFSTAT, 1, [Define to 1 if you have the 'perfstat' library (-lperfstat)])
-        # struct members pertaining to donation have been added to libperfstat somewhere between AIX5.3ML5 and AIX5.3ML9
-        AC_CHECK_MEMBER([perfstat_partition_type_t.b.donate_enabled], [], [], [[#include <libperfstat.h]])
-        if test "x$av_cv_member_perfstat_partition_type_t_b_donate_enabled" = "xyes"
-        then
-               AC_DEFINE(PERFSTAT_SUPPORTS_DONATION, 1, [Define to 1 if your version of the 'perfstat' library supports donation])
-        fi
-fi
-AM_CONDITIONAL(BUILD_WITH_PERFSTAT, test "x$with_perfstat" = "xyes")
-
-# Processes plugin under AIX.
-if test "x$with_procinfo" = "xyes"
-then
-       AC_CHECK_HEADERS(procinfo.h,, [with_procinfo="no (procinfo.h not found)"])
-fi
-if test "x$with_procinfo" = "xyes"
-then
-        AC_DEFINE(HAVE_PROCINFO_H, 1, [Define to 1 if you have the procinfo.h])
-fi
-
-if test "x$ac_system" = "xSolaris"
-then
-       with_kstat="yes"
-       with_devinfo="yes"
-else
-       with_kstat="no (Solaris only)"
-       with_devinfo="no (Solaris only)"
-fi
-
-if test "x$with_kstat" = "xyes"
-then
-       AC_CHECK_LIB(kstat, kstat_open, [with_kstat="yes"], [with_kstat="no (libkstat not found)"], [])
-fi
-if test "x$with_kstat" = "xyes"
-then
-       AC_CHECK_LIB(devinfo, di_init, [with_devinfo="yes"], [with_devinfo="no (not found)"], [])
-       AC_CHECK_HEADERS(kstat.h,, [with_kstat="no (kstat.h not found)"])
-fi
-if test "x$with_kstat" = "xyes"
-then
-       AC_DEFINE(HAVE_LIBKSTAT, 1,
-                 [Define to 1 if you have the 'kstat' library (-lkstat)])
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBKSTAT, test "x$with_kstat" = "xyes")
-AM_CONDITIONAL(BUILD_WITH_LIBDEVINFO, test "x$with_devinfo" = "xyes")
-
-with_libiokit="no"
-AC_CHECK_LIB(IOKit, IOServiceGetMatchingServices,
-[
-       with_libiokit="yes"
-], 
-[
-       with_libiokit="no"
-])
-AM_CONDITIONAL(BUILD_WITH_LIBIOKIT, test "x$with_libiokit" = "xyes")
-
-with_libkvm="no"
-AC_CHECK_LIB(kvm, kvm_getprocs, [with_kvm_getprocs="yes"], [with_kvm_getprocs="no"])
-if test "x$with_kvm_getprocs" = "xyes"
-then
-       AC_DEFINE(HAVE_LIBKVM_GETPROCS, 1,
-                 [Define to 1 if you have the 'kvm' library with the 'kvm_getprocs' symbol (-lkvm)])
-       with_libkvm="yes"
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETPROCS, test "x$with_kvm_getprocs" = "xyes")
-
-AC_CHECK_LIB(kvm, kvm_getswapinfo, [with_kvm_getswapinfo="yes"], [with_kvm_getswapinfo="no"])
-if test "x$with_kvm_getswapinfo" = "xyes"
-then
-       AC_DEFINE(HAVE_LIBKVM_GETSWAPINFO, 1,
-                 [Define to 1 if you have the 'kvm' library with the 'kvm_getswapinfo' symbol (-lkvm)])
-       with_libkvm="yes"
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETSWAPINFO, test "x$with_kvm_getswapinfo" = "xyes")
-
-AC_CHECK_LIB(kvm, kvm_nlist, [with_kvm_nlist="yes"], [with_kvm_nlist="no"])
-if test "x$with_kvm_nlist" = "xyes"
-then
-       AC_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"
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBKVM_NLIST, test "x$with_kvm_nlist" = "xyes")
-
-AC_CHECK_LIB(kvm, kvm_openfiles, [with_kvm_openfiles="yes"], [with_kvm_openfiles="no"])
-if test "x$with_kvm_openfiles" = "xyes"
-then
-       AC_DEFINE(HAVE_LIBKVM_NLIST, 1,
-                 [Define to 1 if you have the 'kvm' library with the 'kvm_openfiles' symbol (-lkvm)])
-       with_libkvm="yes"
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBKVM_OPENFILES, test "x$with_kvm_openfiles" = "xyes")
-
-# --with-libcredis {{{
-AC_ARG_WITH(libcredis, [AS_HELP_STRING([--with-libcredis@<:@=PREFIX@:>@], [Path to libcredis.])],
-[
- if test "x$withval" = "xyes"
- then
-        with_libcredis="yes"
- else if test "x$withval" = "xno"
- then
-        with_libcredis="no"
- else
-        with_libcredis="yes"
-        LIBCREDIS_CPPFLAGS="$LIBCREDIS_CPPFLAGS -I$withval/include"
-        LIBCREDIS_LDFLAGS="$LIBCREDIS_LDFLAGS -L$withval/lib"
- fi; fi
-],
-[with_libcredis="yes"])
-
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-
-CPPFLAGS="$CPPFLAGS $LIBCREDIS_CPPFLAGS"
-LDFLAGS="$LDFLAGS $LIBCREDIS_LDFLAGS"
-
-if test "x$with_libcredis" = "xyes"
-then
-       if test "x$LIBCREDIS_CPPFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([libcredis CPPFLAGS: $LIBCREDIS_CPPFLAGS])
-       fi
-       AC_CHECK_HEADERS(credis.h,
-       [with_libcredis="yes"],
-       [with_libcredis="no (credis.h not found)"])
-fi
-if test "x$with_libcredis" = "xyes"
-then
-       if test "x$LIBCREDIS_LDFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([libcredis LDFLAGS: $LIBCREDIS_LDFLAGS])
-       fi
-       AC_CHECK_LIB(credis, credis_info,
-       [with_libcredis="yes"],
-       [with_libcredis="no (symbol 'credis_info' not found)"])
-
-fi
-
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
-
-if test "x$with_libcredis" = "xyes"
-then
-       BUILD_WITH_LIBCREDIS_CPPFLAGS="$LIBCREDIS_CPPFLAGS"
-       BUILD_WITH_LIBCREDIS_LDFLAGS="$LIBCREDIS_LDFLAGS"
-       AC_SUBST(BUILD_WITH_LIBCREDIS_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBCREDIS_LDFLAGS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBCREDIS, test "x$with_libcredis" = "xyes")
-# }}}
-
-# --with-libcurl {{{
-with_curl_config="curl-config"
-with_curl_cflags=""
-with_curl_libs=""
-AC_ARG_WITH(libcurl, [AS_HELP_STRING([--with-libcurl@<:@=PREFIX@:>@], [Path to libcurl.])],
-[
-       if test "x$withval" = "xno"
-       then
-               with_libcurl="no"
-       else if test "x$withval" = "xyes"
-       then
-               with_libcurl="yes"
-       else
-               if test -f "$withval" && test -x "$withval"
-               then
-                       with_curl_config="$withval"
-                       with_libcurl="yes"
-               else if test -x "$withval/bin/curl-config"
-               then
-                       with_curl_config="$withval/bin/curl-config"
-                       with_libcurl="yes"
-               fi; fi
-               with_libcurl="yes"
-       fi; fi
-],
-[
-       with_libcurl="yes"
-])
-if test "x$with_libcurl" = "xyes"
-then
-       with_curl_cflags=`$with_curl_config --cflags 2>/dev/null`
-       curl_config_status=$?
-
-       if test $curl_config_status -ne 0
-       then
-               with_libcurl="no ($with_curl_config failed)"
-       else
-               SAVE_CPPFLAGS="$CPPFLAGS"
-               CPPFLAGS="$CPPFLAGS $with_curl_cflags"
-
-               AC_CHECK_HEADERS(curl/curl.h, [], [with_libcurl="no (curl/curl.h not found)"], [])
-
-               CPPFLAGS="$SAVE_CPPFLAGS"
-       fi
-fi
-if test "x$with_libcurl" = "xyes"
-then
-       with_curl_libs=`$with_curl_config --libs 2>/dev/null`
-       curl_config_status=$?
-
-       if test $curl_config_status -ne 0
-       then
-               with_libcurl="no ($with_curl_config failed)"
-       else
-               AC_CHECK_LIB(curl, curl_easy_init,
-                [with_libcurl="yes"],
-                [with_libcurl="no (symbol 'curl_easy_init' not found)"],
-                [$with_curl_libs])
-       fi
-fi
-if test "x$with_libcurl" = "xyes"
-then
-       BUILD_WITH_LIBCURL_CFLAGS="$with_curl_cflags"
-       BUILD_WITH_LIBCURL_LIBS="$with_curl_libs"
-       AC_SUBST(BUILD_WITH_LIBCURL_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBCURL_LIBS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBCURL, test "x$with_libcurl" = "xyes")
-# }}}
-
-# --with-libdbi {{{
-with_libdbi_cppflags=""
-with_libdbi_ldflags=""
-AC_ARG_WITH(libdbi, [AS_HELP_STRING([--with-libdbi@<:@=PREFIX@:>@], [Path to libdbi.])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               with_libdbi_cppflags="-I$withval/include"
-               with_libdbi_ldflags="-L$withval/lib"
-               with_libdbi="yes"
-       else
-               with_libdbi="$withval"
-       fi
-],
-[
-       with_libdbi="yes"
-])
-if test "x$with_libdbi" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libdbi_cppflags"
-
-       AC_CHECK_HEADERS(dbi/dbi.h, [with_libdbi="yes"], [with_libdbi="no (dbi/dbi.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libdbi" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libdbi_cppflags"
-       LDFLAGS="$LDFLAGS $with_libdbi_ldflags"
-
-       AC_CHECK_LIB(dbi, dbi_initialize, [with_libdbi="yes"], [with_libdbi="no (Symbol 'dbi_initialize' not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_libdbi" = "xyes"
-then
-       BUILD_WITH_LIBDBI_CPPFLAGS="$with_libdbi_cppflags"
-       BUILD_WITH_LIBDBI_LDFLAGS="$with_libdbi_ldflags"
-       BUILD_WITH_LIBDBI_LIBS="-ldbi"
-       AC_SUBST(BUILD_WITH_LIBDBI_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBDBI_LDFLAGS)
-       AC_SUBST(BUILD_WITH_LIBDBI_LIBS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBDBI, test "x$with_libdbi" = "xyes")
-# }}}
-
-# --with-libesmtp {{{
-AC_ARG_WITH(libesmtp, [AS_HELP_STRING([--with-libesmtp@<:@=PREFIX@:>@], [Path to libesmtp.])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include -D_THREAD_SAFE"
-               with_libesmtp="yes"
-       else
-               with_libesmtp="$withval"
-       fi
-],
-[
-       with_libesmtp="yes"
-])
-if test "x$with_libesmtp" = "xyes"
-then
-       AC_CHECK_LIB(esmtp, smtp_create_session,
-       [
-               AC_DEFINE(HAVE_LIBESMTP, 1, [Define to 1 if you have the esmtp library (-lesmtp).])
-       ], [with_libesmtp="no (libesmtp not found)"])
-fi
-if test "x$with_libesmtp" = "xyes"
-then
-       AC_CHECK_HEADERS(libesmtp.h,
-       [
-               AC_DEFINE(HAVE_LIBESMTP_H, 1, [Define to 1 if you have the <libesmtp.h> header file.])
-       ], [with_libesmtp="no (libesmtp.h not found)"])
-fi
-if test "x$with_libesmtp" = "xyes"
-then
-       collect_libesmtp=1
-else
-       collect_libesmtp=0
-fi
-AC_DEFINE_UNQUOTED(COLLECT_LIBESMTP, [$collect_libesmtp],
-       [Wether or not to use the esmtp library])
-AM_CONDITIONAL(BUILD_WITH_LIBESMTP, test "x$with_libesmtp" = "xyes")
-# }}}
-
-# --with-libganglia {{{
-AC_ARG_WITH(libganglia, [AS_HELP_STRING([--with-libganglia@<:@=PREFIX@:>@], [Path to libganglia.])],
-[
- if test -f "$withval" && test -x "$withval"
- then
-        with_libganglia_config="$withval"
-        with_libganglia="yes"
- else if test -f "$withval/bin/ganglia-config" && test -x "$withval/bin/ganglia-config"
- then
-        with_libganglia_config="$withval/bin/ganglia-config"
-        with_libganglia="yes"
- else if test -d "$withval"
- then
-        GANGLIA_CPPFLAGS="-I$withval/include"
-        GANGLIA_LDFLAGS="-L$withval/lib"
-        with_libganglia="yes"
- else
-        with_libganglia_config="ganglia-config"
-        with_libganglia="$withval"
- fi; fi; fi
-],
-[
- with_libganglia_config="ganglia-config"
- with_libganglia="yes"
-])
-
-if test "x$with_libganglia" = "xyes" && test "x$with_libganglia_config" != "x"
-then
-       if test "x$GANGLIA_CPPFLAGS" = "x"
-       then
-               GANGLIA_CPPFLAGS=`"$with_libganglia_config" --cflags 2>/dev/null`
-       fi
-
-       if test "x$GANGLIA_LDFLAGS" = "x"
-       then
-               GANGLIA_LDFLAGS=`"$with_libganglia_config" --ldflags 2>/dev/null`
-       fi
-
-       if test "x$GANGLIA_LIBS" = "x"
-       then
-               GANGLIA_LIBS=`"$with_libganglia_config" --libs 2>/dev/null`
-       fi
-fi
-
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-CPPFLAGS="$CPPFLAGS $GANGLIA_CPPFLAGS"
-LDFLAGS="$LDFLAGS $GANGLIA_LDFLAGS"
-
-if test "x$with_libganglia" = "xyes"
-then
-       AC_CHECK_HEADERS(gm_protocol.h,
-       [
-               AC_DEFINE(HAVE_GM_PROTOCOL_H, 1,
-                         [Define to 1 if you have the <gm_protocol.h> header file.])
-       ], [with_libganglia="no (gm_protocol.h not found)"])
-fi
-
-if test "x$with_libganglia" = "xyes"
-then
-       AC_CHECK_LIB(ganglia, xdr_Ganglia_value_msg,
-       [
-               AC_DEFINE(HAVE_LIBGANGLIA, 1,
-                         [Define to 1 if you have the ganglia library (-lganglia).])
-       ], [with_libganglia="no (symbol xdr_Ganglia_value_msg not found)"])
-fi
-
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
-
-AC_SUBST(GANGLIA_CPPFLAGS)
-AC_SUBST(GANGLIA_LDFLAGS)
-AC_SUBST(GANGLIA_LIBS)
-AM_CONDITIONAL(BUILD_WITH_LIBGANGLIA, test "x$with_libganglia" = "xyes")
-# }}}
-
-# --with-libgcrypt {{{
-GCRYPT_CPPFLAGS="$GCRYPT_CPPFLAGS"
-GCRYPT_LDFLAGS="$GCRYPT_LDFLAGS"
-GCRYPT_LIBS="$GCRYPT_LIBS"
-AC_ARG_WITH(libgcrypt, [AS_HELP_STRING([--with-libgcrypt@<:@=PREFIX@:>@], [Path to libgcrypt.])],
-[
- if test -f "$withval" && test -x "$withval"
- then
-        with_libgcrypt_config="$withval"
-        with_libgcrypt="yes"
- else if test -f "$withval/bin/gcrypt-config" && test -x "$withval/bin/gcrypt-config"
- then
-        with_libgcrypt_config="$withval/bin/gcrypt-config"
-        with_libgcrypt="yes"
- else if test -d "$withval"
- then
-        GCRYPT_CPPFLAGS="$GCRYPT_CPPFLAGS -I$withval/include"
-        GCRYPT_LDFLAGS="$GCRYPT_LDFLAGS -L$withval/lib"
-        with_libgcrypt="yes"
- else
-        with_libgcrypt_config="gcrypt-config"
-        with_libgcrypt="$withval"
- fi; fi; fi
-],
-[
- with_libgcrypt_config="libgcrypt-config"
- with_libgcrypt="yes"
-])
-
-if test "x$with_libgcrypt" = "xyes" && test "x$with_libgcrypt_config" != "x"
-then
-       if test "x$GCRYPT_CPPFLAGS" = "x"
-       then
-               GCRYPT_CPPFLAGS=`"$with_libgcrypt_config" --cflags 2>/dev/null`
-       fi
-
-       if test "x$GCRYPT_LDFLAGS" = "x"
-       then
-               gcrypt_exec_prefix=`"$with_libgcrypt_config" --exec-prefix 2>/dev/null`
-               GCRYPT_LDFLAGS="-L$gcrypt_exec_prefix/lib"
-       fi
-
-       if test "x$GCRYPT_LIBS" = "x"
-       then
-               GCRYPT_LIBS=`"$with_libgcrypt_config" --libs 2>/dev/null`
-       fi
-fi
-
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-CPPFLAGS="$CPPFLAGS $GCRYPT_CPPFLAGS"
-LDFLAGS="$LDFLAGS $GCRYPT_LDFLAGS"
-
-if test "x$with_libgcrypt" = "xyes"
-then
-       if test "x$GCRYPT_CPPFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([gcrypt CPPFLAGS: $GCRYPT_CPPFLAGS])
-       fi
-       AC_CHECK_HEADERS(gcrypt.h,
-               [with_libgcrypt="yes"],
-               [with_libgcrypt="no (gcrypt.h not found)"])
-fi
-
-if test "x$with_libgcrypt" = "xyes"
-then
-       if test "x$GCRYPT_LDFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([gcrypt LDFLAGS: $GCRYPT_LDFLAGS])
-       fi
-       AC_CHECK_LIB(gcrypt, gcry_md_hash_buffer,
-               [with_libgcrypt="yes"],
-               [with_libgcrypt="no (symbol gcry_md_hash_buffer not found)"])
-
-       if test "$with_libgcrypt" != "no"; then
-               AM_PATH_LIBGCRYPT(1:1.2.0,,with_libgcrypt="no (version 1.2.0+ required)")
-       fi
-fi
-
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
-
-if test "x$with_libgcrypt" = "xyes"
-then
-       AC_DEFINE(HAVE_LIBGCRYPT, 1, [Define to 1 if you have the gcrypt library (-lgcrypt).])
-fi
-
-AC_SUBST(GCRYPT_CPPFLAGS)
-AC_SUBST(GCRYPT_LDFLAGS)
-AC_SUBST(GCRYPT_LIBS)
-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" = "xshipped"
-       then
-               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="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="pkgconfig"
-       else
-               with_libiptc="no (Linux only)"
-       fi
-])
-
-if test "x$with_libiptc" = "xpkgconfig" && test "x$PKG_CONFIG" = "x"
-then
-       with_libiptc="no (Don't have pkg-config)"
-fi
-
-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/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" = "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
-       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, [],
-       [
-               with_libiptc="no (Linux iptables headers not found)"
-       ],
-       [
-#include "$srcdir/src/owniptc/ipt_kernel_headers.h"
-       ])
-fi
-AM_CONDITIONAL(BUILD_WITH_OWN_LIBIPTC, test "x$with_libiptc" = "xown")
-if test "x$with_libiptc" = "xown"
-then
-       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="$with_libiptc_cflags"
-       BUILD_WITH_LIBIPTC_LDFLAGS="$with_libiptc_libs"
-       AC_SUBST(BUILD_WITH_LIBIPTC_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBIPTC_LDFLAGS)
-fi
-# }}}
-
-# --with-java {{{
-with_java_home="$JAVA_HOME"
-with_java_vmtype="client"
-with_java_cflags=""
-with_java_libs=""
-JAVAC="$JAVAC"
-JAR="$JAR"
-AC_ARG_WITH(java, [AS_HELP_STRING([--with-java@<:@=PREFIX@:>@], [Path to Java home.])],
-[
-       if test "x$withval" = "xno"
-       then
-               with_java="no"
-       else if test "x$withval" = "xyes"
-       then
-               with_java="yes"
-       else
-               with_java_home="$withval"
-               with_java="yes"
-       fi; fi
-],
-[with_java="yes"])
-if test "x$with_java" = "xyes"
-then
-       if test -d "$with_java_home"
-       then
-               AC_MSG_CHECKING([for jni.h])
-               TMPDIR=`find "$with_java_home" -name jni.h -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1`
-               if test "x$TMPDIR" != "x"
-               then
-                       AC_MSG_RESULT([found in $TMPDIR])
-                       JAVA_CPPFLAGS="$JAVA_CPPFLAGS -I$TMPDIR"
-               else
-                       AC_MSG_RESULT([not found])
-               fi
-
-               AC_MSG_CHECKING([for jni_md.h])
-               TMPDIR=`find "$with_java_home" -name jni_md.h -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1`
-               if test "x$TMPDIR" != "x"
-               then
-                       AC_MSG_RESULT([found in $TMPDIR])
-                       JAVA_CPPFLAGS="$JAVA_CPPFLAGS -I$TMPDIR"
-               else
-                       AC_MSG_RESULT([not found])
-               fi
-
-               AC_MSG_CHECKING([for libjvm.so])
-               TMPDIR=`find "$with_java_home" -name libjvm.so -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1`
-               if test "x$TMPDIR" != "x"
-               then
-                       AC_MSG_RESULT([found in $TMPDIR])
-                       JAVA_LDFLAGS="$JAVA_LDFLAGS -L$TMPDIR -Wl,-rpath -Wl,$TMPDIR"
-               else
-                       AC_MSG_RESULT([not found])
-               fi
-
-               if test "x$JAVAC" = "x"
-               then
-                       AC_MSG_CHECKING([for javac])
-                       TMPDIR=`find "$with_java_home" -name javac -type f 2>/dev/null | head -n 1`
-                       if test "x$TMPDIR" != "x"
-                       then
-                               JAVAC="$TMPDIR"
-                               AC_MSG_RESULT([$JAVAC])
-                       else
-                               AC_MSG_RESULT([not found])
-                       fi
-               fi
-               if test "x$JAR" = "x"
-               then
-                       AC_MSG_CHECKING([for jar])
-                       TMPDIR=`find "$with_java_home" -name jar -type f 2>/dev/null | head -n 1`
-                       if test "x$TMPDIR" != "x"
-                       then
-                               JAR="$TMPDIR"
-                               AC_MSG_RESULT([$JAR])
-                       else
-                               AC_MSG_RESULT([not found])
-                       fi
-               fi
-       else if test "x$with_java_home" != "x"
-       then
-               AC_MSG_WARN([JAVA_HOME: No such directory: $with_java_home])
-       fi; fi
-fi
-
-if test "x$JAVA_CPPFLAGS" != "x"
-then
-       AC_MSG_NOTICE([Building with JAVA_CPPFLAGS set to: $JAVA_CPPFLAGS])
-fi
-if test "x$JAVA_CFLAGS" != "x"
-then
-       AC_MSG_NOTICE([Building with JAVA_CFLAGS set to: $JAVA_CFLAGS])
-fi
-if test "x$JAVA_LDFLAGS" != "x"
-then
-       AC_MSG_NOTICE([Building with JAVA_LDFLAGS set to: $JAVA_LDFLAGS])
-fi
-if test "x$JAVAC" = "x"
-then
-       with_javac_path="$PATH"
-       if test "x$with_java_home" != "x"
-       then
-               with_javac_path="$with_java_home:with_javac_path"
-               if test -d "$with_java_home/bin"
-               then
-                       with_javac_path="$with_java_home/bin:with_javac_path"
-               fi
-       fi
-
-       AC_PATH_PROG(JAVAC, javac, [], "$with_javac_path")
-fi
-if test "x$JAVAC" = "x"
-then
-       with_java="no (javac not found)"
-fi
-if test "x$JAR" = "x"
-then
-       with_jar_path="$PATH"
-       if test "x$with_java_home" != "x"
-       then
-               with_jar_path="$with_java_home:$with_jar_path"
-               if test -d "$with_java_home/bin"
-               then
-                       with_jar_path="$with_java_home/bin:$with_jar_path"
-               fi
-       fi
-
-       AC_PATH_PROG(JAR, jar, [], "$with_jar_path")
-fi
-if test "x$JAR" = "x"
-then
-       with_java="no (jar not found)"
-fi
-
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_CFLAGS="$CFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-CPPFLAGS="$CPPFLAGS $JAVA_CPPFLAGS"
-CFLAGS="$CFLAGS $JAVA_CFLAGS"
-LDFLAGS="$LDFLAGS $JAVA_LDFLAGS"
-
-if test "x$with_java" = "xyes"
-then
-       AC_CHECK_HEADERS(jni.h, [], [with_java="no (jni.h not found)"])
-fi
-if test "x$with_java" = "xyes"
-then
-       AC_CHECK_LIB(jvm, JNI_CreateJavaVM,
-       [with_java="yes"],
-       [with_java="no (libjvm not found)"],
-       [$JAVA_LIBS])
-fi
-if test "x$with_java" = "xyes"
-then
-       JAVA_LIBS="$JAVA_LIBS -ljvm"
-       AC_MSG_NOTICE([Building with JAVA_LIBS set to: $JAVA_LIBS])
-fi
-
-CPPFLAGS="$SAVE_CPPFLAGS"
-CFLAGS="$SAVE_CFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
-
-AC_SUBST(JAVA_CPPFLAGS)
-AC_SUBST(JAVA_CFLAGS)
-AC_SUBST(JAVA_LDFLAGS)
-AC_SUBST(JAVA_LIBS)
-AM_CONDITIONAL(BUILD_WITH_JAVA, test "x$with_java" = "xyes")
-# }}}
-
-# --with-libmemcached {{{
-with_libmemcached_cppflags=""
-with_libmemcached_ldflags=""
-AC_ARG_WITH(libmemcached, [AS_HELP_STRING([--with-libmemcached@<:@=PREFIX@:>@], [Path to libmemcached.])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               with_libmemcached_cppflags="-I$withval/include"
-               with_libmemcached_ldflags="-L$withval/lib"
-               with_libmemcached="yes"
-       else
-               with_libmemcached="$withval"
-       fi
-],
-[
-       with_libmemcached="yes"
-])
-if test "x$with_libmemcached" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libmemcached_cppflags"
-
-       AC_CHECK_HEADERS(libmemcached/memcached.h, [with_libmemcached="yes"], [with_libmemcached="no (libmemcached/memcached.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libmemcached" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libmemcached_cppflags"
-       LDFLAGS="$LDFLAGS $with_libmemcached_ldflags"
-
-       AC_CHECK_LIB(memcached, memcached_create, [with_libmemcached="yes"], [with_libmemcached="no (Symbol 'memcached_create' not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_libmemcached" = "xyes"
-then
-       BUILD_WITH_LIBMEMCACHED_CPPFLAGS="$with_libmemcached_cppflags"
-       BUILD_WITH_LIBMEMCACHED_LDFLAGS="$with_libmemcached_ldflags"
-       BUILD_WITH_LIBMEMCACHED_LIBS="-lmemcached"
-       AC_SUBST(BUILD_WITH_LIBMEMCACHED_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBMEMCACHED_LDFLAGS)
-       AC_SUBST(BUILD_WITH_LIBMEMCACHED_LIBS)
-       AC_DEFINE(HAVE_LIBMEMCACHED, 1, [Define if libmemcached is present and usable.])
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBMEMCACHED, test "x$with_libmemcached" = "xyes")
-# }}}
-
-# --with-libmodbus {{{
-with_libmodbus_config=""
-with_libmodbus_cflags=""
-with_libmodbus_libs=""
-AC_ARG_WITH(libmodbus, [AS_HELP_STRING([--with-libmodbus@<:@=PREFIX@:>@], [Path to the modbus library.])],
-[
-       if test "x$withval" = "xno"
-       then
-               with_libmodbus="no"
-       else if test "x$withval" = "xyes"
-       then
-               with_libmodbus="use_pkgconfig"
-       else if test -d "$with_libmodbus/lib"
-       then
-               AC_MSG_NOTICE([Not checking for libmodbus: Manually configured])
-               with_libmodbus_cflags="-I$withval/include"
-               with_libmodbus_libs="-L$withval/lib -lmodbus"
-               with_libmodbus="yes"
-       fi; fi; fi
-],
-[with_libmodbus="use_pkgconfig"])
-
-# configure using pkg-config
-if test "x$with_libmodbus" = "xuse_pkgconfig"
-then
-       if test "x$PKG_CONFIG" = "x"
-       then
-               with_libmodbus="no (Don't have pkg-config)"
-       fi
-fi
-if test "x$with_libmodbus" = "xuse_pkgconfig"
-then
-       AC_MSG_NOTICE([Checking for libmodbus using $PKG_CONFIG])
-       $PKG_CONFIG --exists 'libmodbus' 2>/dev/null
-       if test $? -ne 0
-       then
-               with_libmodbus="no (pkg-config doesn't know libmodbus)"
-       fi
-fi
-if test "x$with_libmodbus" = "xuse_pkgconfig"
-then
-       with_libmodbus_cflags="`$PKG_CONFIG --cflags 'libmodbus'`"
-       if test $? -ne 0
-       then
-               with_libmodbus="no ($PKG_CONFIG failed)"
-       fi
-       with_libmodbus_libs="`$PKG_CONFIG --libs 'libmodbus'`"
-       if test $? -ne 0
-       then
-               with_libmodbus="no ($PKG_CONFIG failed)"
-       fi
-fi
-if test "x$with_libmodbus" = "xuse_pkgconfig"
-then
-       with_libmodbus="yes"
-fi
-
-# with_libmodbus_cflags and with_libmodbus_libs are set up now, let's do
-# the actual checks.
-if test "x$with_libmodbus" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
-
-       AC_CHECK_HEADERS(modbus/modbus.h, [], [with_libmodbus="no (modbus/modbus.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libmodbus" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-
-       CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
-       LDFLAGS="$LDFLAGS $with_libmodbus_libs"
-
-       AC_CHECK_LIB(modbus, modbus_connect,
-                    [with_libmodbus="yes"],
-                    [with_libmodbus="no (symbol modbus_connect not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_libmodbus" = "xyes"
-then
-       BUILD_WITH_LIBMODBUS_CFLAGS="$with_libmodbus_cflags"
-       BUILD_WITH_LIBMODBUS_LIBS="$with_libmodbus_libs"
-       AC_SUBST(BUILD_WITH_LIBMODBUS_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBMODBUS_LIBS)
-fi
-# }}}
-
-# --with-libmongoc {{{
-AC_ARG_WITH(libmongoc, [AS_HELP_STRING([--with-libmongoc@<:@=PREFIX@:>@], [Path to libmongoc.])],
-[
- if test "x$withval" = "xyes"
- then
-        with_libmongoc="yes"
- else if test "x$withval" = "xno"
- then
-        with_libmongoc="no"
- else
-        with_libmongoc="yes"
-        LIBMONGOC_CPPFLAGS="$LIBMONGOC_CPPFLAGS -I$withval/include"
-        LIBMONGOC_LDFLAGS="$LIBMONGOC_LDFLAGS -L$withval/lib"
- fi; fi
-],
-[with_libmongoc="yes"])
-
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-
-CPPFLAGS="$CPPFLAGS $LIBMONGOC_CPPFLAGS"
-LDFLAGS="$LDFLAGS $LIBMONGOC_LDFLAGS"
-
-if test "x$with_libmongoc" = "xyes"
-then
-       if test "x$LIBMONGOC_CPPFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([libmongoc CPPFLAGS: $LIBMONGOC_CPPFLAGS])
-       fi
-       AC_CHECK_HEADERS(mongo.h,
-       [with_libmongoc="yes"],
-       [with_libmongoc="no ('mongo.h' not found)"],
-[#if HAVE_STDINT_H
-# define MONGO_HAVE_STDINT 1
-#else
-# define MONGO_USE_LONG_LONG_INT 1
-#endif
-])
-fi
-if test "x$with_libmongoc" = "xyes"
-then
-       if test "x$LIBMONGOC_LDFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([libmongoc LDFLAGS: $LIBMONGOC_LDFLAGS])
-       fi
-       AC_CHECK_LIB(mongoc, mongo_run_command,
-       [with_libmongoc="yes"],
-       [with_libmongoc="no (symbol 'mongo_run_command' not found)"])
-fi
-
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
-
-if test "x$with_libmongoc" = "xyes"
-then
-       BUILD_WITH_LIBMONGOC_CPPFLAGS="$LIBMONGOC_CPPFLAGS"
-       BUILD_WITH_LIBMONGOC_LDFLAGS="$LIBMONGOC_LDFLAGS"
-       AC_SUBST(BUILD_WITH_LIBMONGOC_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBMONGOC_LDFLAGS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBMONGOC, test "x$with_libmongoc" = "xyes")
-# }}}
-
-# --with-libmysql {{{
-with_mysql_config="mysql_config"
-with_mysql_cflags=""
-with_mysql_libs=""
-AC_ARG_WITH(libmysql, [AS_HELP_STRING([--with-libmysql@<:@=PREFIX@:>@], [Path to libmysql.])],
-[
-       if test "x$withval" = "xno"
-       then
-               with_libmysql="no"
-       else if test "x$withval" = "xyes"
-       then
-               with_libmysql="yes"
-       else
-               if test -f "$withval" && test -x "$withval";
-               then
-                       with_mysql_config="$withval"
-               else if test -x "$withval/bin/mysql_config"
-               then
-                       with_mysql_config="$withval/bin/mysql_config"
-               fi; fi
-               with_libmysql="yes"
-       fi; fi
-],
-[
-       with_libmysql="yes"
-])
-if test "x$with_libmysql" = "xyes"
-then
-       with_mysql_cflags=`$with_mysql_config --cflags 2>/dev/null`
-       mysql_config_status=$?
-
-       if test $mysql_config_status -ne 0
-       then
-               with_libmysql="no ($with_mysql_config failed)"
-       else
-               SAVE_CPPFLAGS="$CPPFLAGS"
-               CPPFLAGS="$CPPFLAGS $with_mysql_cflags"
-
-               have_mysql_h="no"
-               have_mysql_mysql_h="no"
-               AC_CHECK_HEADERS(mysql.h, [have_mysql_h="yes"])
-
-               if test "x$have_mysql_h" = "xno"
-               then
-                       AC_CHECK_HEADERS(mysql/mysql.h, [have_mysql_mysql_h="yes"])
-               fi
-
-               if test "x$have_mysql_h$have_mysql_mysql_h" = "xnono"
-               then
-                       with_libmysql="no (mysql.h not found)"
-               fi
-
-               CPPFLAGS="$SAVE_CPPFLAGS"
-       fi
-fi
-if test "x$with_libmysql" = "xyes"
-then
-       with_mysql_libs=`$with_mysql_config --libs_r 2>/dev/null`
-       mysql_config_status=$?
-
-       if test $mysql_config_status -ne 0
-       then
-               with_libmysql="no ($with_mysql_config failed)"
-       else
-               AC_CHECK_LIB(mysqlclient, mysql_init,
-                [with_libmysql="yes"],
-                [with_libmysql="no (symbol 'mysql_init' not found)"],
-                [$with_mysql_libs])
-
-               AC_CHECK_LIB(mysqlclient, mysql_get_server_version,
-                [with_libmysql="yes"],
-                [with_libmysql="no (symbol 'mysql_get_server_version' not found)"],
-                [$with_mysql_libs])
-       fi
-fi
-if test "x$with_libmysql" = "xyes"
-then
-       BUILD_WITH_LIBMYSQL_CFLAGS="$with_mysql_cflags"
-       BUILD_WITH_LIBMYSQL_LIBS="$with_mysql_libs"
-       AC_SUBST(BUILD_WITH_LIBMYSQL_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBMYSQL_LIBS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBMYSQL, test "x$with_libmysql" = "xyes")
-# }}}
-
-# --with-libnetlink {{{
-with_libnetlink_cflags=""
-with_libnetlink_libs="-lnetlink"
-AC_ARG_WITH(libnetlink, [AS_HELP_STRING([--with-libnetlink@<:@=PREFIX@:>@], [Path to libnetlink.])],
-[
- echo "libnetlink: withval = $withval"
- if test "x$withval" = "xyes"
- then
-        with_libnetlink="yes"
- else if test "x$withval" = "xno"
- then
-        with_libnetlink="no"
- else
-        if test -d "$withval/include"
-        then
-                with_libnetlink_cflags="-I$withval/include"
-                with_libnetlink_libs="-L$withval/lib -lnetlink"
-                with_libnetlink="yes"
-        else
-                AC_MSG_ERROR("no such directory: $withval/include")
-        fi
- fi; fi
-],
-[
- if test "x$ac_system" = "xLinux"
- then
-        with_libnetlink="yes"
- else
-        with_libnetlink="no (Linux only library)"
- fi
-])
-if test "x$with_libnetlink" = "xyes"
-then
-       SAVE_CFLAGS="$CFLAGS"
-       CFLAGS="$CFLAGS $with_libnetlink_cflags"
-
-       with_libnetlink="no (libnetlink.h not found)"
-
-       AC_CHECK_HEADERS(libnetlink.h iproute/libnetlink.h linux/libnetlink.h,
-       [
-        with_libnetlink="yes"
-        break
-       ], [],
-[#include <stdio.h>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>])
-       AC_CHECK_HEADERS(linux/gen_stats.h linux/pkt_sched.h, [], [],
-[#include <stdio.h>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>])
-
-        AC_COMPILE_IFELSE([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>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
-int main (void)
-{
-       int retval = TCA_STATS;
-       return (retval);
-}],
-       [AC_DEFINE([HAVE_TCA_STATS], 1, [True if the enum-member TCA_STATS exists])]
-       []);
-
-       CFLAGS="$SAVE_CFLAGS"
-fi
-if test "x$with_libnetlink" = "xyes"
-then
-       AC_CHECK_LIB(netlink, rtnl_open,
-                    [with_libnetlink="yes"],
-                    [with_libnetlink="no (symbol 'rtnl_open' not found)"],
-                    [$with_libnetlink_libs])
-fi
-if test "x$with_libnetlink" = "xyes"
-then
-       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)
-       AC_SUBST(BUILD_WITH_LIBNETLINK_LIBS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBNETLINK, test "x$with_libnetlink" = "xyes")
-# }}}
-
-# --with-libnetapp {{{
-AC_ARG_VAR([LIBNETAPP_CPPFLAGS], [C preprocessor flags required to build with libnetapp])
-AC_ARG_VAR([LIBNETAPP_LDFLAGS],  [Linker flags required to build with libnetapp])
-AC_ARG_VAR([LIBNETAPP_LIBS],     [Other libraries required to link against libnetapp])
-LIBNETAPP_CPPFLAGS="$LIBNETAPP_CPPFLAGS"
-LIBNETAPP_LDFLAGS="$LIBNETAPP_LDFLAGS"
-LIBNETAPP_LIBS="$LIBNETAPP_LIBS"
-AC_ARG_WITH(libnetapp, [AS_HELP_STRING([--with-libnetapp@<:@=PREFIX@:>@], [Path to libnetapp.])],
-[
- if test -d "$withval"
- then
-        LIBNETAPP_CPPFLAGS="$LIBNETAPP_CPPFLAGS -I$withval/include"
-        LIBNETAPP_LDFLAGS="$LIBNETAPP_LDFLAGS -L$withval/lib"
-        with_libnetapp="yes"
- else
-        with_libnetapp="$withval"
- fi
-],
-[
- with_libnetapp="yes"
-])
-
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-CPPFLAGS="$CPPFLAGS $LIBNETAPP_CPPFLAGS"
-LDFLAGS="$LDFLAGS $LIBNETAPP_LDFLAGS"
-
-if test "x$with_libnetapp" = "xyes"
-then
-       if test "x$LIBNETAPP_CPPFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([netapp CPPFLAGS: $LIBNETAPP_CPPFLAGS])
-       fi
-       AC_CHECK_HEADERS(netapp_api.h,
-               [with_libnetapp="yes"],
-               [with_libnetapp="no (netapp_api.h not found)"])
-fi
-
-if test "x$with_libnetapp" = "xyes"
-then
-       if test "x$LIBNETAPP_LDFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([netapp LDFLAGS: $LIBNETAPP_LDFLAGS])
-       fi
-
-       if test "x$LIBNETAPP_LIBS" = "x"
-       then
-               LIBNETAPP_LIBS="-lpthread -lxml -ladt -lssl -lm -lcrypto -lz"
-       fi
-       AC_MSG_NOTICE([netapp LIBS: $LIBNETAPP_LIBS])
-
-       AC_CHECK_LIB(netapp, na_server_invoke_elem,
-               [with_libnetapp="yes"],
-               [with_libnetapp="no (symbol na_server_invoke_elem not found)"],
-               [$LIBNETAPP_LIBS])
-       LIBNETAPP_LIBS="-lnetapp $LIBNETAPP_LIBS"
-fi
-
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
-
-if test "x$with_libnetapp" = "xyes"
-then
-       AC_DEFINE(HAVE_LIBNETAPP, 1, [Define to 1 if you have the netapp library (-lnetapp).])
-fi
-
-AC_SUBST(LIBNETAPP_CPPFLAGS)
-AC_SUBST(LIBNETAPP_LDFLAGS)
-AC_SUBST(LIBNETAPP_LIBS)
-AM_CONDITIONAL(BUILD_WITH_LIBNETAPP, test "x$with_libnetapp" = "xyes")
-# }}}
-
-# --with-libnetsnmp {{{
-with_snmp_config="net-snmp-config"
-with_snmp_cflags=""
-with_snmp_libs=""
-AC_ARG_WITH(libnetsnmp, [AS_HELP_STRING([--with-libnetsnmp@<:@=PREFIX@:>@], [Path to the Net-SNMPD library.])],
-[
-       if test "x$withval" = "xno"
-       then
-               with_libnetsnmp="no"
-       else if test "x$withval" = "xyes"
-       then
-               with_libnetsnmp="yes"
-       else
-               if test -x "$withval"
-               then
-                       with_snmp_config="$withval"
-                       with_libnetsnmp="yes"
-               else
-                       with_snmp_config="$withval/bin/net-snmp-config"
-                       with_libnetsnmp="yes"
-               fi
-       fi; fi
-],
-[with_libnetsnmp="yes"])
-if test "x$with_libnetsnmp" = "xyes"
-then
-       with_snmp_cflags=`$with_snmp_config --cflags 2>/dev/null`
-       snmp_config_status=$?
-
-       if test $snmp_config_status -ne 0
-       then
-               with_libnetsnmp="no ($with_snmp_config failed)"
-       else
-               SAVE_CPPFLAGS="$CPPFLAGS"
-               CPPFLAGS="$CPPFLAGS $with_snmp_cflags"
-               
-               AC_CHECK_HEADERS(net-snmp/net-snmp-config.h, [], [with_libnetsnmp="no (net-snmp/net-snmp-config.h not found)"])
-
-               CPPFLAGS="$SAVE_CPPFLAGS"
-       fi
-fi
-if test "x$with_libnetsnmp" = "xyes"
-then
-       with_snmp_libs=`$with_snmp_config --libs 2>/dev/null`
-       snmp_config_status=$?
-
-       if test $snmp_config_status -ne 0
-       then
-               with_libnetsnmp="no ($with_snmp_config failed)"
-       else
-               AC_CHECK_LIB(netsnmp, init_snmp,
-               [with_libnetsnmp="yes"],
-               [with_libnetsnmp="no (libnetsnmp not found)"],
-               [$with_snmp_libs])
-       fi
-fi
-if test "x$with_libnetsnmp" = "xyes"
-then
-       BUILD_WITH_LIBSNMP_CFLAGS="$with_snmp_cflags"
-       BUILD_WITH_LIBSNMP_LIBS="$with_snmp_libs"
-       AC_SUBST(BUILD_WITH_LIBSNMP_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBSNMP_LIBS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBNETSNMP, test "x$with_libnetsnmp" = "xyes")
-# }}}
-
-# --with-liboconfig {{{
-with_own_liboconfig="no"
-liboconfig_LDFLAGS="$LDFLAGS"
-liboconfig_CPPFLAGS="$CPPFLAGS"
-AC_ARG_WITH(liboconfig, [AS_HELP_STRING([--with-liboconfig@<:@=PREFIX@:>@], [Path to liboconfig.])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               if test -d "$withval/lib"
-               then
-                       liboconfig_LDFLAGS="$LDFLAGS -L$withval/lib"
-               fi
-               if test -d "$withval/include"
-               then
-                       liboconfig_CPPFLAGS="$CPPFLAGS -I$withval/include"
-               fi
-       fi
-       if test "x$withval" = "xno"
-       then
-               AC_MSG_ERROR("liboconfig is required")
-       fi
-],
-[
-       with_liboconfig="yes"
-])
-
-save_LDFLAGS="$LDFLAGS"
-save_CPPFLAGS="$CPPFLAGS"
-LDFLAGS="$liboconfig_LDFLAGS"
-CPPFLAGS="$liboconfig_CPPFLAGS"
-AC_CHECK_LIB(oconfig, oconfig_parse_fh,
-[
-       with_liboconfig="yes"
-       with_own_liboconfig="no"
-],
-[
-       with_liboconfig="yes"
-       with_own_liboconfig="yes"
-       LDFLAGS="$save_LDFLAGS"
-       CPPFLAGS="$save_CPPFLAGS"
-])
-
-AM_CONDITIONAL(BUILD_WITH_OWN_LIBOCONFIG, test "x$with_own_liboconfig" = "xyes")
-if test "x$with_own_liboconfig" = "xyes"
-then
-       with_liboconfig="yes (shipped version)"
-fi
-# }}}
-
-# --with-liboping {{{
-AC_ARG_WITH(liboping, [AS_HELP_STRING([--with-liboping@<:@=PREFIX@:>@], [Path to liboping.])],
-[
- if test "x$withval" = "xyes"
- then
-        with_liboping="yes"
- else if test "x$withval" = "xno"
- then
-        with_liboping="no"
- else
-        with_liboping="yes"
-        LIBOPING_CPPFLAGS="$LIBOPING_CPPFLAGS -I$withval/include"
-        LIBOPING_LDFLAGS="$LIBOPING_LDFLAGS -L$withval/lib"
- fi; fi
-],
-[with_liboping="yes"])
-
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-
-CPPFLAGS="$CPPFLAGS $LIBOPING_CPPFLAGS"
-LDFLAGS="$LDFLAGS $LIBOPING_LDFLAGS"
-
-if test "x$with_liboping" = "xyes"
-then
-       if test "x$LIBOPING_CPPFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([liboping CPPFLAGS: $LIBOPING_CPPFLAGS])
-       fi
-       AC_CHECK_HEADERS(oping.h,
-       [with_liboping="yes"],
-       [with_liboping="no (oping.h not found)"])
-fi
-if test "x$with_liboping" = "xyes"
-then
-       if test "x$LIBOPING_LDFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([liboping LDFLAGS: $LIBOPING_LDFLAGS])
-       fi
-       AC_CHECK_LIB(oping, ping_construct,
-       [with_liboping="yes"],
-       [with_liboping="no (symbol 'ping_construct' not found)"])
-fi
-
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
-
-if test "x$with_liboping" = "xyes"
-then
-       BUILD_WITH_LIBOPING_CPPFLAGS="$LIBOPING_CPPFLAGS"
-       BUILD_WITH_LIBOPING_LDFLAGS="$LIBOPING_LDFLAGS"
-       AC_SUBST(BUILD_WITH_LIBOPING_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBOPING_LDFLAGS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBOPING, test "x$with_liboping" = "xyes")
-# }}}
-
-# --with-oracle {{{
-with_oracle_cppflags=""
-with_oracle_libs=""
-AC_ARG_WITH(oracle, [AS_HELP_STRING([--with-oracle@<:@=ORACLE_HOME@:>@], [Path to Oracle.])],
-[
-       if test "x$withval" = "xyes"
-       then
-               if test "x$ORACLE_HOME" = "x"
-               then
-                       AC_MSG_WARN([Use of the Oracle library has been forced, but the environment variable ORACLE_HOME is not set.])
-               fi
-               with_oracle="yes"
-       else if test "x$withval" = "xno"
-       then
-               with_oracle="no"
-       else
-               with_oracle="yes"
-               ORACLE_HOME="$withval"
-       fi; fi
-],
-[
-       if test "x$ORACLE_HOME" = "x"
-       then
-               with_oracle="no (ORACLE_HOME is not set)"
-       else
-               with_oracle="yes"
-       fi
-])
-if test "x$ORACLE_HOME" != "x"
-then
-       with_oracle_cppflags="-I$ORACLE_HOME/rdbms/public"
-
-       if test -e "$ORACLE_HOME/lib/ldflags"
-       then
-               with_oracle_libs=`cat "$ORACLE_HOME/lib/ldflags"`
-       fi
-       #with_oracle_libs="-L$ORACLE_HOME/lib $with_oracle_libs -lclntsh"
-       with_oracle_libs="-L$ORACLE_HOME/lib -lclntsh"
-fi
-if test "x$with_oracle" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_oracle_cppflags"
-
-       AC_CHECK_HEADERS(oci.h, [with_oracle="yes"], [with_oracle="no (oci.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_oracle" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_oracle_cppflags"
-       LDFLAGS="$LDFLAGS $with_oracle_libs"
-
-       AC_CHECK_FUNC(OCIEnvCreate, [with_oracle="yes"], [with_oracle="no (Symbol 'OCIEnvCreate' not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_oracle" = "xyes"
-then
-       BUILD_WITH_ORACLE_CFLAGS="$with_oracle_cppflags"
-       BUILD_WITH_ORACLE_LIBS="$with_oracle_libs"
-       AC_SUBST(BUILD_WITH_ORACLE_CFLAGS)
-       AC_SUBST(BUILD_WITH_ORACLE_LIBS)
-fi
-# }}}
-
-# --with-libowcapi {{{
-with_libowcapi_cppflags=""
-with_libowcapi_libs="-lowcapi"
-AC_ARG_WITH(libowcapi, [AS_HELP_STRING([--with-libowcapi@<:@=PREFIX@:>@], [Path to libowcapi.])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               with_libowcapi_cppflags="-I$withval/include"
-               with_libowcapi_libs="-L$withval/lib -lowcapi"
-               with_libowcapi="yes"
-       else
-               with_libowcapi="$withval"
-       fi
-],
-[
-       with_libowcapi="yes"
-])
-if test "x$with_libowcapi" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$with_libowcapi_cppflags"
-       
-       AC_CHECK_HEADERS(owcapi.h, [with_libowcapi="yes"], [with_libowcapi="no (owcapi.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libowcapi" = "xyes"
-then
-       SAVE_LDFLAGS="$LDFLAGS"
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       LDFLAGS="$with_libowcapi_libs"
-       CPPFLAGS="$with_libowcapi_cppflags"
-       
-       AC_CHECK_LIB(owcapi, OW_get, [with_libowcapi="yes"], [with_libowcapi="no (libowcapi not found)"])
-
-       LDFLAGS="$SAVE_LDFLAGS"
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libowcapi" = "xyes"
-then
-       BUILD_WITH_LIBOWCAPI_CPPFLAGS="$with_libowcapi_cppflags"
-       BUILD_WITH_LIBOWCAPI_LIBS="$with_libowcapi_libs"
-       AC_SUBST(BUILD_WITH_LIBOWCAPI_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBOWCAPI_LIBS)
-fi
-# }}}
-
-# --with-libpcap {{{
-AC_ARG_WITH(libpcap, [AS_HELP_STRING([--with-libpcap@<:@=PREFIX@:>@], [Path to libpcap.])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include"
-               with_libpcap="yes"
-       else
-               with_libpcap="$withval"
-       fi
-],
-[
-       with_libpcap="yes"
-])
-if test "x$with_libpcap" = "xyes"
-then
-       AC_CHECK_LIB(pcap, pcap_open_live,
-       [
-               AC_DEFINE(HAVE_LIBPCAP, 1, [Define to 1 if you have the pcap library (-lpcap).])
-       ], [with_libpcap="no (libpcap not found)"])
-fi
-if test "x$with_libpcap" = "xyes"
-then
-       AC_CHECK_HEADERS(pcap.h,,
-                        [with_libpcap="no (pcap.h not found)"])
-fi
-if test "x$with_libpcap" = "xyes"
-then
-       AC_CHECK_HEADERS(pcap-bpf.h,,
-                        [with_libpcap="no (pcap-bpf.h not found)"])
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes")
-# }}}
-
-# --with-libperl {{{
-perl_interpreter="perl"
-AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
-[
-       if test -f "$withval" && test -x "$withval"
-       then
-               perl_interpreter="$withval"
-               with_libperl="yes"
-       else if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include"
-               perl_interpreter="$withval/bin/perl"
-               with_libperl="yes"
-       else
-               with_libperl="$withval"
-       fi; fi
-],
-[
-       with_libperl="yes"
-])
-
-AC_MSG_CHECKING([for perl])
-perl_interpreter=`which "$perl_interpreter" 2> /dev/null`
-if test -x "$perl_interpreter"
-then
-       AC_MSG_RESULT([yes ($perl_interpreter)])
-else
-       perl_interpreter=""
-       AC_MSG_RESULT([no])
-fi
-
-AC_SUBST(PERL, "$perl_interpreter")
-
-if test "x$with_libperl" = "xyes" \
-       && test -n "$perl_interpreter"
-then
-  SAVE_CFLAGS="$CFLAGS"
-  SAVE_LDFLAGS="$LDFLAGS"
-dnl ARCHFLAGS="" -> disable multi -arch on OSX (see Config_heavy.pl:fetch_string)
-  PERL_CFLAGS=`ARCHFLAGS="" $perl_interpreter -MExtUtils::Embed -e ccopts`
-  PERL_LDFLAGS=`ARCHFLAGS="" $perl_interpreter -MExtUtils::Embed -e ldopts`
-  CFLAGS="$CFLAGS $PERL_CFLAGS"
-  LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
-
-  AC_CACHE_CHECK([for libperl],
-    [c_cv_have_libperl],
-    AC_LINK_IFELSE(
-      AC_LANG_PROGRAM(
-      [[
-#define PERL_NO_GET_CONTEXT
-#include <EXTERN.h>
-#include <perl.h>
-#include <XSUB.h>
-      ]],
-      [[
-       dTHX;
-       load_module (PERL_LOADMOD_NOIMPORT,
-                        newSVpv ("Collectd::Plugin::FooBar", 24),
-                        Nullsv);
-      ]]),
-      [c_cv_have_libperl="yes"],
-      [c_cv_have_libperl="no"]
-    )
-  )
-
-  if test "x$c_cv_have_libperl" = "xyes"
-  then
-         AC_DEFINE(HAVE_LIBPERL, 1, [Define if libperl is present and usable.])
-         AC_SUBST(PERL_CFLAGS)
-         AC_SUBST(PERL_LDFLAGS)
-  else
-         with_libperl="no"
-  fi
-
-  CFLAGS="$SAVE_CFLAGS"
-  LDFLAGS="$SAVE_LDFLAGS"
-else if test -z "$perl_interpreter"; then
-  with_libperl="no (no perl interpreter found)"
-  c_cv_have_libperl="no"
-fi; fi
-AM_CONDITIONAL(BUILD_WITH_LIBPERL, test "x$with_libperl" = "xyes")
-
-if test "x$with_libperl" = "xyes"
-then
-       SAVE_CFLAGS="$CFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-       CFLAGS="$CFLAGS $PERL_CFLAGS"
-       LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
-
-       AC_CACHE_CHECK([if perl supports ithreads],
-               [c_cv_have_perl_ithreads],
-               AC_LINK_IFELSE(
-                       AC_LANG_PROGRAM(
-                       [[
-#include <EXTERN.h>
-#include <perl.h>
-#include <XSUB.h>
-
-#if !defined(USE_ITHREADS)
-# error "Perl does not support ithreads!"
-#endif /* !defined(USE_ITHREADS) */
-                       ]],
-                       [[ ]]),
-                       [c_cv_have_perl_ithreads="yes"],
-                       [c_cv_have_perl_ithreads="no"]
-               )
-       )
-
-       if test "x$c_cv_have_perl_ithreads" = "xyes"
-       then
-               AC_DEFINE(HAVE_PERL_ITHREADS, 1, [Define if Perl supports ithreads.])
-       fi
-
-       CFLAGS="$SAVE_CFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-
-if test "x$with_libperl" = "xyes"
-then
-       SAVE_CFLAGS="$CFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-       # trigger an error if Perl_load_module*() uses __attribute__nonnull__(3)
-       # (see issues #41 and #42)
-       CFLAGS="$CFLAGS $PERL_CFLAGS -Wall -Werror"
-       LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
-
-       AC_CACHE_CHECK([for broken Perl_load_module()],
-               [c_cv_have_broken_perl_load_module],
-               AC_LINK_IFELSE(
-                       AC_LANG_PROGRAM(
-                       [[
-#define PERL_NO_GET_CONTEXT
-#include <EXTERN.h>
-#include <perl.h>
-#include <XSUB.h>
-                       ]],
-                       [[
-                        dTHX;
-                        load_module (PERL_LOADMOD_NOIMPORT,
-                            newSVpv ("Collectd::Plugin::FooBar", 24),
-                            Nullsv);
-                       ]]),
-                       [c_cv_have_broken_perl_load_module="no"],
-                       [c_cv_have_broken_perl_load_module="yes"]
-               )
-       )
-
-       CFLAGS="$SAVE_CFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-AM_CONDITIONAL(HAVE_BROKEN_PERL_LOAD_MODULE,
-               test "x$c_cv_have_broken_perl_load_module" = "xyes")
-
-if test "x$with_libperl" = "xyes"
-then
-       SAVE_CFLAGS="$CFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-       CFLAGS="$CFLAGS $PERL_CFLAGS"
-       LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
-
-       AC_CHECK_MEMBER(
-               [struct mgvtbl.svt_local],
-               [have_struct_mgvtbl_svt_local="yes"],
-               [have_struct_mgvtbl_svt_local="no"],
-               [
-#include <EXTERN.h>
-#include <perl.h>
-#include <XSUB.h>
-               ])
-
-       if test "x$have_struct_mgvtbl_svt_local" = "xyes"
-       then
-               AC_DEFINE(HAVE_PERL_STRUCT_MGVTBL_SVT_LOCAL, 1,
-                                 [Define if Perl's struct mgvtbl has member svt_local.])
-       fi
-
-       CFLAGS="$SAVE_CFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-# }}}
-
-# --with-libpq {{{
-with_pg_config="pg_config"
-with_libpq_includedir=""
-with_libpq_libdir=""
-with_libpq_cppflags=""
-with_libpq_ldflags=""
-AC_ARG_WITH(libpq, [AS_HELP_STRING([--with-libpq@<:@=PREFIX@:>@],
-       [Path to libpq.])],
-[
-       if test "x$withval" = "xno"
-       then
-               with_libpq="no"
-       else if test "x$withval" = "xyes"
-       then
-               with_libpq="yes"
-       else
-               if test -f "$withval" && test -x "$withval";
-               then
-                       with_pg_config="$withval"
-               else if test -x "$withval/bin/pg_config"
-               then
-                       with_pg_config="$withval/bin/pg_config"
-               fi; fi
-               with_libpq="yes"
-       fi; fi
-],
-[
-       with_libpq="yes"
-])
-if test "x$with_libpq" = "xyes"
-then
-       with_libpq_includedir=`$with_pg_config --includedir 2> /dev/null`
-       pg_config_status=$?
-
-       if test $pg_config_status -eq 0
-       then
-               if test -n "$with_libpq_includedir"; then
-                       for dir in $with_libpq_includedir; do
-                               with_libpq_cppflags="$with_libpq_cppflags -I$dir"
-                       done
-               fi
-       else
-               AC_MSG_WARN([$with_pg_config returned with status $pg_config_status])
-       fi
-
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libpq_cppflags"
-
-       AC_CHECK_HEADERS(libpq-fe.h, [],
-               [with_libpq="no (libpq-fe.h not found)"], [])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libpq" = "xyes"
-then
-       with_libpq_libdir=`$with_pg_config --libdir 2> /dev/null`
-       pg_config_status=$?
-
-       if test $pg_config_status -eq 0
-       then
-               if test -n "$with_libpq_libdir"; then
-                       for dir in $with_libpq_libdir; do
-                               with_libpq_ldflags="$with_libpq_ldflags -L$dir"
-                       done
-               fi
-       else
-               AC_MSG_WARN([$with_pg_config returned with status $pg_config_status])
-       fi
-
-       SAVE_LDFLAGS="$LDFLAGS"
-       LDFLAGS="$LDFLAGS $with_libpq_ldflags"
-
-       AC_CHECK_LIB(pq, PQconnectdb,
-               [with_libpq="yes"],
-               [with_libpq="no (symbol 'PQconnectdb' not found)"])
-
-       AC_CHECK_LIB(pq, PQserverVersion,
-               [with_libpq="yes"],
-               [with_libpq="no (symbol 'PQserverVersion' not found)"])
-
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_libpq" = "xyes"
-then
-       BUILD_WITH_LIBPQ_CPPFLAGS="$with_libpq_cppflags"
-       BUILD_WITH_LIBPQ_LDFLAGS="$with_libpq_ldflags"
-       AC_SUBST(BUILD_WITH_LIBPQ_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBPQ_LDFLAGS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBPQ, test "x$with_libpq" = "xyes")
-# }}}
-
-# --with-libpthread {{{
-AC_ARG_WITH(libpthread, [AS_HELP_STRING([--with-libpthread=@<:@=PREFIX@:>@], [Path to libpthread.])],
-[      if test "x$withval" != "xno" \
-               && test "x$withval" != "xyes"
-       then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include"
-               with_libpthread="yes"
-       else
-               if test "x$withval" = "xno"
-               then
-                       with_libpthread="no (disabled)"
-               fi
-       fi
-], [with_libpthread="yes"])
-if test "x$with_libpthread" = "xyes"
-then
-       AC_CHECK_LIB(pthread, pthread_create, [with_libpthread="yes"], [with_libpthread="no (libpthread not found)"], [])
-fi
-
-if test "x$with_libpthread" = "xyes"
-then
-       AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"])
-fi
-if test "x$with_libpthread" = "xyes"
-then
-       collect_pthread=1
-else
-       collect_pthread=0
-fi
-AC_DEFINE_UNQUOTED(HAVE_LIBPTHREAD, [$collect_pthread],
-       [Wether or not to use pthread (POSIX threads) library])
-AM_CONDITIONAL(BUILD_WITH_LIBPTHREAD, test "x$with_libpthread" = "xyes")
-# }}}
-
-# --with-python {{{
-with_python_prog=""
-with_python_path="$PATH"
-AC_ARG_WITH(python, [AS_HELP_STRING([--with-python@<:@=PREFIX@:>@], [Path to the python interpreter.])],
-[
- if test "x$withval" = "xyes" || test "x$withval" = "xno"
- then
-        with_python="$withval"
- else if test -x "$withval"
- then
-        with_python_prog="$withval"
-        with_python_path="`dirname \"$withval\"`$PATH_SEPARATOR$with_python_path"
-        with_python="yes"
- else if test -d "$withval"
- then
-        with_python_path="$withval$PATH_SEPARATOR$with_python_path"
-        with_python="yes"
- else
-        AC_MSG_WARN([Argument not recognized: $withval])
- fi; fi; fi
-], [with_python="yes"])
-
-SAVE_PATH="$PATH"
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-SAVE_LIBS="$LIBS"
-
-PATH="$with_python_path"
-
-if test "x$with_python" = "xyes" && test "x$with_python_prog" = "x"
-then
-       AC_MSG_CHECKING([for python])
-       with_python_prog="`which python 2>/dev/null`"
-       if test "x$with_python_prog" = "x"
-       then
-               AC_MSG_RESULT([not found])
-               with_python="no (interpreter not found)"
-       else
-               AC_MSG_RESULT([$with_python_prog])
-       fi
-fi
-
-if test "x$with_python" = "xyes"
-then
-       AC_MSG_CHECKING([for Python CPPFLAGS])
-       python_include_path=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_python_inc())" | "$with_python_prog" 2>&1`
-       python_config_status=$?
-
-       if test "$python_config_status" -ne 0 || test "x$python_include_path" = "x"
-       then
-               AC_MSG_RESULT([failed with status $python_config_status (output: $python_include_path)])
-               with_python="no"
-       else
-               AC_MSG_RESULT([$python_include_path])
-       fi
-fi
-
-if test "x$with_python" = "xyes"
-then
-       CPPFLAGS="-I$python_include_path $CPPFLAGS"
-       AC_CHECK_HEADERS(Python.h,
-                        [with_python="yes"],
-                        [with_python="no ('Python.h' not found)"])
-fi
-
-if test "x$with_python" = "xyes"
-then
-       AC_MSG_CHECKING([for Python LDFLAGS])
-       python_library_path=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_config_vars(\"LIBDIR\").__getitem__(0))" | "$with_python_prog" 2>&1`
-       python_config_status=$?
-
-       if test "$python_config_status" -ne 0 || test "x$python_library_path" = "x"
-       then
-               AC_MSG_RESULT([failed with status $python_config_status (output: $python_library_path)])
-               with_python="no"
-       else
-               AC_MSG_RESULT([$python_library_path])
-       fi
-fi
-
-if test "x$with_python" = "xyes"
-then
-       AC_MSG_CHECKING([for Python LIBS])
-       python_library_flags=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_config_vars(\"BLDLIBRARY\").__getitem__(0))" | "$with_python_prog" 2>&1`
-       python_config_status=$?
-
-       if test "$python_config_status" -ne 0 || test "x$python_library_flags" = "x"
-       then
-               AC_MSG_RESULT([failed with status $python_config_status (output: $python_library_flags)])
-               with_python="no"
-       else
-               AC_MSG_RESULT([$python_library_flags])
-       fi
-fi
-
-if test "x$with_python" = "xyes"
-then
-       LDFLAGS="-L$python_library_path $LDFLAGS"
-       LIBS="$python_library_flags $LIBS"
-
-       AC_CHECK_FUNC(PyObject_CallFunction,
-                     [with_python="yes"],
-                     [with_python="no (Symbol 'PyObject_CallFunction' not found)"])
-fi
-
-PATH="$SAVE_PATH"
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
-LIBS="$SAVE_LIBS"
-
-if test "x$with_python" = "xyes"
-then
-       BUILD_WITH_PYTHON_CPPFLAGS="-I$python_include_path"
-       BUILD_WITH_PYTHON_LDFLAGS="-L$python_library_path"
-       BUILD_WITH_PYTHON_LIBS="$python_library_flags"
-       AC_SUBST(BUILD_WITH_PYTHON_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_PYTHON_LDFLAGS)
-       AC_SUBST(BUILD_WITH_PYTHON_LIBS)
-fi
-# }}} --with-python
-
-# --with-librabbitmq {{{
-with_librabbitmq_cppflags=""
-with_librabbitmq_ldflags=""
-AC_ARG_WITH(librabbitmq, [AS_HELP_STRING([--with-librabbitmq@<:@=PREFIX@:>@], [Path to librabbitmq.])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               with_librabbitmq_cppflags="-I$withval/include"
-               with_librabbitmq_ldflags="-L$withval/lib"
-               with_librabbitmq="yes"
-       else
-               with_librabbitmq="$withval"
-       fi
-],
-[
-       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
-       AC_CHECK_HEADERS(amqp.h, [with_librabbitmq="yes"], [with_librabbitmq="no (amqp.h not found)"])
-fi
-if test "x$with_librabbitmq" = "xyes"
-then
-       # 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)"])
-fi
-if test "x$with_librabbitmq" = "xyes"
-then
-       BUILD_WITH_LIBRABBITMQ_CPPFLAGS="$with_librabbitmq_cppflags"
-       BUILD_WITH_LIBRABBITMQ_LDFLAGS="$with_librabbitmq_ldflags"
-       BUILD_WITH_LIBRABBITMQ_LIBS="-lrabbitmq"
-       AC_SUBST(BUILD_WITH_LIBRABBITMQ_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBRABBITMQ_LDFLAGS)
-       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")
-# }}}
-
-# --with-librouteros {{{
-AC_ARG_WITH(librouteros, [AS_HELP_STRING([--with-librouteros@<:@=PREFIX@:>@], [Path to librouteros.])],
-[
- if test "x$withval" = "xyes"
- then
-        with_librouteros="yes"
- else if test "x$withval" = "xno"
- then
-        with_librouteros="no"
- else
-        with_librouteros="yes"
-        LIBROUTEROS_CPPFLAGS="$LIBROUTEROS_CPPFLAGS -I$withval/include"
-        LIBROUTEROS_LDFLAGS="$LIBROUTEROS_LDFLAGS -L$withval/lib"
- fi; fi
-],
-[with_librouteros="yes"])
-
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-
-CPPFLAGS="$CPPFLAGS $LIBROUTEROS_CPPFLAGS"
-LDFLAGS="$LDFLAGS $LIBROUTEROS_LDFLAGS"
-
-if test "x$with_librouteros" = "xyes"
-then
-       if test "x$LIBROUTEROS_CPPFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([librouteros CPPFLAGS: $LIBROUTEROS_CPPFLAGS])
-       fi
-       AC_CHECK_HEADERS(routeros_api.h,
-       [with_librouteros="yes"],
-       [with_librouteros="no (routeros_api.h not found)"])
-fi
-if test "x$with_librouteros" = "xyes"
-then
-       if test "x$LIBROUTEROS_LDFLAGS" != "x"
-       then
-               AC_MSG_NOTICE([librouteros LDFLAGS: $LIBROUTEROS_LDFLAGS])
-       fi
-       AC_CHECK_LIB(routeros, ros_interface,
-       [with_librouteros="yes"],
-       [with_librouteros="no (symbol 'ros_interface' not found)"])
-fi
-
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
-
-if test "x$with_librouteros" = "xyes"
-then
-       BUILD_WITH_LIBROUTEROS_CPPFLAGS="$LIBROUTEROS_CPPFLAGS"
-       BUILD_WITH_LIBROUTEROS_LDFLAGS="$LIBROUTEROS_LDFLAGS"
-       AC_SUBST(BUILD_WITH_LIBROUTEROS_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBROUTEROS_LDFLAGS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBROUTEROS, test "x$with_librouteros" = "xyes")
-# }}}
-
-# --with-librrd {{{
-# AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given])
-librrd_cflags=""
-librrd_ldflags=""
-librrd_threadsafe="yes"
-librrd_rrdc_update="no"
-AC_ARG_WITH(librrd, [AS_HELP_STRING([--with-librrd@<:@=PREFIX@:>@], [Path to rrdtool.])],
-[      if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               librrd_cflags="-I$withval/include"
-               librrd_ldflags="-L$withval/lib"
-               with_librrd="yes"
-       else
-               with_librrd="$withval"
-       fi
-], [with_librrd="yes"])
-if test "x$with_librrd" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-
-       CPPFLAGS="$CPPFLAGS $librrd_cflags"
-       LDFLAGS="$LDFLAGS $librrd_ldflags"
-
-       AC_CHECK_HEADERS(rrd.h,, [with_librrd="no (rrd.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_librrd" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-
-       CPPFLAGS="$CPPFLAGS $librrd_cflags"
-       LDFLAGS="$LDFLAGS $librrd_ldflags"
-
-       AC_CHECK_LIB(rrd_th, rrd_update_r,
-       [with_librrd="yes"
-        librrd_ldflags="$librrd_ldflags -lrrd_th -lm"
-       ],
-       [librrd_threadsafe="no"
-        AC_CHECK_LIB(rrd, rrd_update,
-        [with_librrd="yes"
-         librrd_ldflags="$librrd_ldflags -lrrd -lm"
-        ],
-        [with_librrd="no (symbol 'rrd_update' not found)"],
-        [-lm])
-       ],
-       [-lm])
-
-       if test "x$librrd_threadsafe" = "xyes"
-       then
-               AC_CHECK_LIB(rrd_th, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"])
-       else
-               AC_CHECK_LIB(rrd, rrdc_update, [librrd_rrdc_update="yes"], [librrd_rrdc_update="no"])
-       fi
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_librrd" = "xyes"
-then
-       BUILD_WITH_LIBRRD_CFLAGS="$librrd_cflags"
-       BUILD_WITH_LIBRRD_LDFLAGS="$librrd_ldflags"
-       AC_SUBST(BUILD_WITH_LIBRRD_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBRRD_LDFLAGS)
-fi
-if test "x$librrd_threadsafe" = "xyes"
-then
-       AC_DEFINE(HAVE_THREADSAFE_LIBRRD, 1, [Define to 1 if you have the threadsafe rrd library (-lrrd_th).])
-fi
-# }}}
-
-# --with-libsensors {{{
-with_sensors_cflags=""
-with_sensors_ldflags=""
-AC_ARG_WITH(libsensors, [AS_HELP_STRING([--with-libsensors@<:@=PREFIX@:>@], [Path to lm_sensors.])],
-[
-       if test "x$withval" = "xno"
-       then
-               with_libsensors="no"
-       else
-               with_libsensors="yes"
-               if test "x$withval" != "xyes"
-               then
-                       with_sensors_cflags="-I$withval/include"
-                       with_sensors_ldflags="-L$withval/lib"
-                       with_libsensors="yes"
-               fi
-       fi
-],
-[
-       if test "x$ac_system" = "xLinux"
-       then
-               with_libsensors="yes"
-       else
-               with_libsensors="no (Linux only library)"
-       fi
-])
-if test "x$with_libsensors" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
-
-#      AC_CHECK_HEADERS(sensors/sensors.h,
-#      [
-#              AC_DEFINE(HAVE_SENSORS_SENSORS_H, 1, [Define to 1 if you have the <sensors/sensors.h> header file.])
-#      ],
-#      [with_libsensors="no (sensors/sensors.h not found)"])
-       AC_CHECK_HEADERS(sensors/sensors.h, [], [with_libsensors="no (sensors/sensors.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libsensors" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
-       LDFLAGS="$LDFLAGS $with_sensors_ldflags"
-
-       AC_CHECK_LIB(sensors, sensors_init,
-       [
-               AC_DEFINE(HAVE_LIBSENSORS, 1, [Define to 1 if you have the sensors library (-lsensors).])
-       ],
-       [with_libsensors="no (libsensors not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_libsensors" = "xyes"
-then
-       BUILD_WITH_LIBSENSORS_CFLAGS="$with_sensors_cflags"
-       BUILD_WITH_LIBSENSORS_LDFLAGS="$with_sensors_ldflags"
-       AC_SUBST(BUILD_WITH_LIBSENSORS_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBSENSORS_LDFLAGS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_libsensors" = "xyes")
-# }}}
-
-# --with-libstatgrab {{{
-with_libstatgrab_cflags=""
-with_libstatgrab_ldflags=""
-AC_ARG_WITH(libstatgrab, [AS_HELP_STRING([--with-libstatgrab@<:@=PREFIX@:>@], [Path to libstatgrab.])],
-[
- if test "x$withval" != "xno" \
-   && test "x$withval" != "xyes"
- then
-   with_libstatgrab_cflags="-I$withval/include"
-   with_libstatgrab_ldflags="-L$withval/lib -lstatgrab"
-   with_libstatgrab="yes"
-   with_libstatgrab_pkg_config="no"
- else
-   with_libstatgrab="$withval"
-   with_libstatgrab_pkg_config="yes"
- fi
- ],
-[
- with_libstatgrab="yes"
- with_libstatgrab_pkg_config="yes"
-])
-
-if test "x$with_libstatgrab" = "xyes" \
-  && test "x$with_libstatgrab_pkg_config" = "xyes"
-then
-  if test "x$PKG_CONFIG" != "x"
-  then
-    AC_MSG_CHECKING([pkg-config for libstatgrab])
-    temp_result="found"
-    $PKG_CONFIG --exists libstatgrab 2>/dev/null
-    if test "$?" != "0"
-    then
-      with_libstatgrab_pkg_config="no"
-      with_libstatgrab="no (pkg-config doesn't know libstatgrab)"
-      temp_result="not found"
-    fi
-    AC_MSG_RESULT([$temp_result])
-  else
-    AC_MSG_NOTICE([pkg-config not available, trying to guess flags for the statgrab library.])
-    with_libstatgrab_pkg_config="no"
-    with_libstatgrab_ldflags="$with_libstatgrab_ldflags -lstatgrab"
-  fi
-fi
-
-if test "x$with_libstatgrab" = "xyes" \
-  && test "x$with_libstatgrab_pkg_config" = "xyes" \
-  && test "x$with_libstatgrab_cflags" = "x"
-then
-  AC_MSG_CHECKING([for libstatgrab CFLAGS])
-  temp_result="`$PKG_CONFIG --cflags libstatgrab`"
-  if test "$?" = "0"
-  then
-    with_libstatgrab_cflags="$temp_result"
-  else
-    with_libstatgrab="no ($PKG_CONFIG --cflags libstatgrab failed)"
-    temp_result="$PKG_CONFIG --cflags libstatgrab failed"
-  fi
-  AC_MSG_RESULT([$temp_result])
-fi
-
-if test "x$with_libstatgrab" = "xyes" \
-  && test "x$with_libstatgrab_pkg_config" = "xyes" \
-  && test "x$with_libstatgrab_ldflags" = "x"
-then
-  AC_MSG_CHECKING([for libstatgrab LDFLAGS])
-  temp_result="`$PKG_CONFIG --libs libstatgrab`"
-  if test "$?" = "0"
-  then
-    with_libstatgrab_ldflags="$temp_result"
-  else
-    with_libstatgrab="no ($PKG_CONFIG --libs libstatgrab failed)"
-    temp_result="$PKG_CONFIG --libs libstatgrab failed"
-  fi
-  AC_MSG_RESULT([$temp_result])
-fi
-
-if test "x$with_libstatgrab" = "xyes"
-then
-  SAVE_CPPFLAGS="$CPPFLAGS"
-  CPPFLAGS="$CPPFLAGS $with_libstatgrab_cflags"
-
-  AC_CHECK_HEADERS(statgrab.h,
-                  [with_libstatgrab="yes"],
-                  [with_libstatgrab="no (statgrab.h not found)"])
-
-  CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-
-if test "x$with_libstatgrab" = "xyes"
-then
-  SAVE_CFLAGS="$CFLAGS"
-  SAVE_LDFLAGS="$LDFLAGS"
-
-  CFLAGS="$CFLAGS $with_libstatgrab_cflags"
-  LDFLAGS="$LDFLAGS $with_libstatgrab_ldflags"
-
-  AC_CHECK_LIB(statgrab, sg_init,
-              [with_libstatgrab="yes"],
-              [with_libstatgrab="no (symbol sg_init not found)"])
-
-  CFLAGS="$SAVE_CFLAGS"
-  LDFLAGS="$SAVE_LDFLAGS"
-fi
-
-AM_CONDITIONAL(BUILD_WITH_LIBSTATGRAB, test "x$with_libstatgrab" = "xyes")
-if test "x$with_libstatgrab" = "xyes"
-then
-  AC_DEFINE(HAVE_LIBSTATGRAB, 1, [Define to 1 if you have the 'statgrab' library (-lstatgrab)])
-  BUILD_WITH_LIBSTATGRAB_CFLAGS="$with_libstatgrab_cflags"
-  BUILD_WITH_LIBSTATGRAB_LDFLAGS="$with_libstatgrab_ldflags"
-  AC_SUBST(BUILD_WITH_LIBSTATGRAB_CFLAGS)
-  AC_SUBST(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
-fi
-# }}}
-
-# --with-libtokyotyrant {{{
-with_libtokyotyrant_cppflags=""
-with_libtokyotyrant_ldflags=""
-with_libtokyotyrant_libs=""
-AC_ARG_WITH(libtokyotyrant, [AS_HELP_STRING([--with-libtokyotyrant@<:@=PREFIX@:>@], [Path to libtokyotyrant.])],
-[
-  if test "x$withval" = "xno"
-  then
-    with_libtokyotyrant="no"
-  else if test "x$withval" = "xyes"
-  then
-    with_libtokyotyrant="yes"
-  else
-    with_libtokyotyrant_cppflags="-I$withval/include"
-    with_libtokyotyrant_ldflags="-L$withval/include"
-    with_libtokyotyrant_libs="-ltokyotyrant"
-    with_libtokyotyrant="yes"
-  fi; fi
-],
-[
-  with_libtokyotyrant="yes"
-])
-
-if test "x$with_libtokyotyrant" = "xyes"
-then
-  if $PKG_CONFIG --exists tokyotyrant
-  then
-    with_libtokyotyrant_cppflags="$with_libtokyotyrant_cppflags `$PKG_CONFIG --cflags tokyotyrant`"
-    with_libtokyotyrant_ldflags="$with_libtokyotyrant_ldflags `pkg-config --libs-only-L tokyotyrant`"
-    with_libtokyotyrant_libs="$with_libtokyotyrant_libs `pkg-config --libs-only-l tokyotyrant`"
-  fi
-fi
-
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-CPPFLAGS="$CPPFLAGS $with_libtokyotyrant_cppflags"
-LDFLAGS="$LDFLAGS $with_libtokyotyrant_ldflags"
-
-if test "x$with_libtokyotyrant" = "xyes"
-then
-  AC_CHECK_HEADERS(tcrdb.h,
-  [
-          AC_DEFINE(HAVE_TCRDB_H, 1,
-                    [Define to 1 if you have the <tcrdb.h> header file.])
-  ], [with_libtokyotyrant="no (tcrdb.h not found)"])
-fi
-
-if test "x$with_libtokyotyrant" = "xyes"
-then
-  AC_CHECK_LIB(tokyotyrant, tcrdbrnum,
-  [
-          AC_DEFINE(HAVE_LIBTOKYOTYRANT, 1,
-                    [Define to 1 if you have the tokyotyrant library (-ltokyotyrant).])
-  ],
-  [with_libtokyotyrant="no (symbol tcrdbrnum not found)"],
-  [$with_libtokyotyrant_libs])
-fi
-
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
-
-if test "x$with_libtokyotyrant" = "xyes"
-then 
-  BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS="$with_libtokyotyrant_cppflags"
-  BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS="$with_libtokyotyrant_ldflags"
-  BUILD_WITH_LIBTOKYOTYRANT_LIBS="$with_libtokyotyrant_libs"
-  AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS)
-  AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS)
-  AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_LIBS)
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBTOKYOTYRANT, test "x$with_libtokyotyrant" = "xyes")
-# }}}
-
-# --with-libupsclient {{{
-with_libupsclient_config=""
-with_libupsclient_cflags=""
-with_libupsclient_libs=""
-AC_ARG_WITH(libupsclient, [AS_HELP_STRING([--with-libupsclient@<:@=PREFIX@:>@], [Path to the upsclient library.])],
-[
-       if test "x$withval" = "xno"
-       then
-               with_libupsclient="no"
-       else if test "x$withval" = "xyes"
-       then
-               with_libupsclient="use_pkgconfig"
-       else
-               if test -x "$withval"
-               then
-                       with_libupsclient_config="$withval"
-                       with_libupsclient="use_libupsclient_config"
-               else if test -x "$withval/bin/libupsclient-config"
-               then
-                       with_libupsclient_config="$withval/bin/libupsclient-config"
-                       with_libupsclient="use_libupsclient_config"
-               else
-                       AC_MSG_NOTICE([Not checking for libupsclient: Manually configured])
-                       with_libupsclient_cflags="-I$withval/include"
-                       with_libupsclient_libs="-L$withval/lib -lupsclient"
-                       with_libupsclient="yes"
-               fi; fi
-       fi; fi
-],
-[with_libupsclient="use_pkgconfig"])
-
-# configure using libupsclient-config
-if test "x$with_libupsclient" = "xuse_libupsclient_config"
-then
-       AC_MSG_NOTICE([Checking for libupsclient using $with_libupsclient_config])
-       with_libupsclient_cflags="`$with_libupsclient_config --cflags`"
-       if test $? -ne 0
-       then
-               with_libupsclient="no ($with_libupsclient_config failed)"
-       fi
-       with_libupsclient_libs="`$with_libupsclient_config --libs`"
-       if test $? -ne 0
-       then
-               with_libupsclient="no ($with_libupsclient_config failed)"
-       fi
-fi
-if test "x$with_libupsclient" = "xuse_libupsclient_config"
-then
-       with_libupsclient="yes"
-fi
-
-# configure using pkg-config
-if test "x$with_libupsclient" = "xuse_pkgconfig"
-then
-       if test "x$PKG_CONFIG" = "x"
-       then
-               with_libupsclient="no (Don't have pkg-config)"
-       fi
-fi
-if test "x$with_libupsclient" = "xuse_pkgconfig"
-then
-       AC_MSG_NOTICE([Checking for libupsclient using $PKG_CONFIG])
-       $PKG_CONFIG --exists 'libupsclient' 2>/dev/null
-       if test $? -ne 0
-       then
-               with_libupsclient="no (pkg-config doesn't know libupsclient)"
-       fi
-fi
-if test "x$with_libupsclient" = "xuse_pkgconfig"
-then
-       with_libupsclient_cflags="`$PKG_CONFIG --cflags 'libupsclient'`"
-       if test $? -ne 0
-       then
-               with_libupsclient="no ($PKG_CONFIG failed)"
-       fi
-       with_libupsclient_libs="`$PKG_CONFIG --libs 'libupsclient'`"
-       if test $? -ne 0
-       then
-               with_libupsclient="no ($PKG_CONFIG failed)"
-       fi
-fi
-if test "x$with_libupsclient" = "xuse_pkgconfig"
-then
-       with_libupsclient="yes"
-fi
-
-# with_libupsclient_cflags and with_libupsclient_libs are set up now, let's do
-# the actual checks.
-if test "x$with_libupsclient" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
-
-       AC_CHECK_HEADERS(upsclient.h, [], [with_libupsclient="no (upsclient.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libupsclient" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-
-       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
-       LDFLAGS="$LDFLAGS $with_libupsclient_libs"
-
-       AC_CHECK_LIB(upsclient, upscli_connect,
-                    [with_libupsclient="yes"],
-                    [with_libupsclient="no (symbol upscli_connect not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_libupsclient" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libupsclient_cflags"
-
-       AC_CHECK_TYPES([UPSCONN_t, UPSCONN], [], [],
-[#include <stdlib.h>
-#include <stdio.h>
-#include <upsclient.h>])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libupsclient" = "xyes"
-then
-       BUILD_WITH_LIBUPSCLIENT_CFLAGS="$with_libupsclient_cflags"
-       BUILD_WITH_LIBUPSCLIENT_LIBS="$with_libupsclient_libs"
-       AC_SUBST(BUILD_WITH_LIBUPSCLIENT_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBUPSCLIENT_LIBS)
-fi
-# }}}
-
-# --with-libxmms {{{
-with_xmms_config="xmms-config"
-with_xmms_cflags=""
-with_xmms_libs=""
-AC_ARG_WITH(libxmms, [AS_HELP_STRING([--with-libxmms@<:@=PREFIX@:>@], [Path to libxmms.])],
-[
-       if test "x$withval" != "xno" \
-               && test "x$withval" != "xyes"
-       then
-               if test -f "$withval" && test -x "$withval";
-               then
-                       with_xmms_config="$withval"
-               else if test -x "$withval/bin/xmms-config"
-               then
-                       with_xmms_config="$withval/bin/xmms-config"
-               fi; fi
-               with_libxmms="yes"
-       else if test "x$withval" = "xno"
-       then
-               with_libxmms="no"
-       else
-               with_libxmms="yes"
-       fi; fi
-],
-[
-       with_libxmms="yes"
-])
-if test "x$with_libxmms" = "xyes"
-then
-       with_xmms_cflags=`$with_xmms_config --cflags 2>/dev/null`
-       xmms_config_status=$?
-
-       if test $xmms_config_status -ne 0
-       then
-               with_libxmms="no"
-       fi
-fi
-if test "x$with_libxmms" = "xyes"
-then
-       with_xmms_libs=`$with_xmms_config --libs 2>/dev/null`
-       xmms_config_status=$?
-
-       if test $xmms_config_status -ne 0
-       then
-               with_libxmms="no"
-       fi
-fi
-if test "x$with_libxmms" = "xyes"
-then
-       AC_CHECK_LIB(xmms, xmms_remote_get_info,
-       [
-               BUILD_WITH_LIBXMMS_CFLAGS="$with_xmms_cflags"
-               BUILD_WITH_LIBXMMS_LIBS="$with_xmms_libs"
-               AC_SUBST(BUILD_WITH_LIBXMMS_CFLAGS)
-               AC_SUBST(BUILD_WITH_LIBXMMS_LIBS)
-       ],
-       [
-               with_libxmms="no"
-       ],
-       [$with_xmms_libs])
-fi
-with_libxmms_numeric=0
-if test "x$with_libxmms" = "xyes"
-then
-       with_libxmms_numeric=1
-fi
-AC_DEFINE_UNQUOTED(HAVE_LIBXMMS, [$with_libxmms_numeric], [Define to 1 if you have the 'xmms' library (-lxmms).])
-AM_CONDITIONAL(BUILD_WITH_LIBXMMS, test "x$with_libxmms" = "xyes")
-# }}}
-
-# --with-libyajl {{{
-with_libyajl_cppflags=""
-with_libyajl_ldflags=""
-AC_ARG_WITH(libyajl, [AS_HELP_STRING([--with-libyajl@<:@=PREFIX@:>@], [Path to libyajl.])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               with_libyajl_cppflags="-I$withval/include"
-               with_libyajl_ldflags="-L$withval/lib"
-               with_libyajl="yes"
-       else
-               with_libyajl="$withval"
-       fi
-],
-[
-       with_libyajl="yes"
-])
-if test "x$with_libyajl" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libyajl_cppflags"
-
-       AC_CHECK_HEADERS(yajl/yajl_parse.h, [with_libyajl="yes"], [with_libyajl="no (yajl/yajl_parse.h not found)"])
-       AC_CHECK_HEADERS(yajl/yajl_version.h)
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libyajl" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libyajl_cppflags"
-       LDFLAGS="$LDFLAGS $with_libyajl_ldflags"
-
-       AC_CHECK_LIB(yajl, yajl_alloc, [with_libyajl="yes"], [with_libyajl="no (Symbol 'yajl_alloc' not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-if test "x$with_libyajl" = "xyes"
-then
-       BUILD_WITH_LIBYAJL_CPPFLAGS="$with_libyajl_cppflags"
-       BUILD_WITH_LIBYAJL_LDFLAGS="$with_libyajl_ldflags"
-       BUILD_WITH_LIBYAJL_LIBS="-lyajl"
-       AC_SUBST(BUILD_WITH_LIBYAJL_CPPFLAGS)
-       AC_SUBST(BUILD_WITH_LIBYAJL_LDFLAGS)
-       AC_SUBST(BUILD_WITH_LIBYAJL_LIBS)
-       AC_DEFINE(HAVE_LIBYAJL, 1, [Define if libyajl is present and usable.])
-fi
-AM_CONDITIONAL(BUILD_WITH_LIBYAJL, test "x$with_libyajl" = "xyes")
-# }}}
-
-# --with-libvarnish {{{
-with_libvarnish_cppflags=""
-with_libvarnish_cflags=""
-with_libvarnish_libs=""
-AC_ARG_WITH(libvarnish, [AS_HELP_STRING([--with-libvarnish@<:@=PREFIX@:>@], [Path to libvarnish.])],
-[
-       if test "x$withval" = "xno"
-       then
-               with_libvarnish="no"
-       else if test "x$withval" = "xyes"
-       then
-               with_libvarnish="use_pkgconfig"
-       else if test -d "$with_libvarnish/lib"
-       then
-               AC_MSG_NOTICE([Not checking for libvarnish: Manually configured])
-               with_libvarnish_cflags="-I$withval/include"
-               with_libvarnish_libs="-L$withval/lib -lvarnishapi"
-               with_libvarnish="yes"
-       fi; fi; fi
-],
-[with_libvarnish="use_pkgconfig"])
-
-# configure using pkg-config
-if test "x$with_libvarnish" = "xuse_pkgconfig"
-then
-       if test "x$PKG_CONFIG" = "x"
-       then
-               with_libvarnish="no (Don't have pkg-config)"
-       fi
-fi
-if test "x$with_libvarnish" = "xuse_pkgconfig"
-then
-       AC_MSG_NOTICE([Checking for varnishapi using $PKG_CONFIG])
-       $PKG_CONFIG --exists 'varnishapi' 2>/dev/null
-       if test $? -ne 0
-       then
-               with_libvarnish="no (pkg-config doesn't know varnishapi)"
-       fi
-fi
-if test "x$with_libvarnish" = "xuse_pkgconfig"
-then
-       with_libvarnish_cflags="`$PKG_CONFIG --cflags 'varnishapi'`"
-       if test $? -ne 0
-       then
-               with_libvarnish="no ($PKG_CONFIG failed)"
-       fi
-       with_libvarnish_libs="`$PKG_CONFIG --libs 'varnishapi'`"
-       if test $? -ne 0
-       then
-               with_libvarnish="no ($PKG_CONFIG failed)"
-       fi
-fi
-if test "x$with_libvarnish" = "xuse_pkgconfig"
-then
-       with_libvarnish="yes"
-fi
-
-# with_libvarnish_cflags and with_libvarnish_libs are set up now, let's do
-# the actual checks.
-if test "x$with_libvarnish" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libvarnish_cflags"
-       AC_CHECK_HEADERS(varnish/varnishapi.h, [], [with_libvarnish="no (varnish/varnishapi.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libvarnish" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       #SAVE_LDFLAGS="$LDFLAGS"
-
-       CPPFLAGS="$CPPFLAGS $with_libvarnish_cflags"
-       #LDFLAGS="$LDFLAGS $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"
-fi
-if test "x$with_libvarnish" = "xyes"
-then
-       BUILD_WITH_LIBVARNISH_CFLAGS="$with_libvarnish_cflags"
-       BUILD_WITH_LIBVARNISH_LIBS="$with_libvarnish_libs"
-       AC_SUBST(BUILD_WITH_LIBVARNISH_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBVARNISH_LIBS)
-fi
-# }}}
-
-# pkg-config --exists 'libxml-2.0'; pkg-config --exists libvirt {{{
-with_libxml2="no (pkg-config isn't available)"
-with_libxml2_cflags=""
-with_libxml2_ldflags=""
-with_libvirt="no (pkg-config isn't available)"
-with_libvirt_cflags=""
-with_libvirt_ldflags=""
-if test "x$PKG_CONFIG" != "x"
-then
-       pkg-config --exists 'libxml-2.0' 2>/dev/null
-       if test "$?" = "0"
-       then
-               with_libxml2="yes"
-       else
-               with_libxml2="no (pkg-config doesn't know libxml-2.0)"
-       fi
-
-       pkg-config --exists libvirt 2>/dev/null
-       if test "$?" = "0"
-       then
-               with_libvirt="yes"
-       else
-               with_libvirt="no (pkg-config doesn't know libvirt)"
-       fi
-fi
-if test "x$with_libxml2" = "xyes"
-then
-       with_libxml2_cflags="`pkg-config --cflags libxml-2.0`"
-       if test $? -ne 0
-       then
-               with_libxml2="no"
-       fi
-       with_libxml2_ldflags="`pkg-config --libs libxml-2.0`"
-       if test $? -ne 0
-       then
-               with_libxml2="no"
-       fi
-fi
-if test "x$with_libxml2" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libxml2_cflags"
-
-       AC_CHECK_HEADERS(libxml/parser.h, [],
-                     [with_libxml2="no (libxml/parser.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libxml2" = "xyes"
-then
-       SAVE_CFLAGS="$CFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-
-       CFLAGS="$CFLAGS $with_libxml2_cflags"
-       LDFLAGS="$LDFLAGS $with_libxml2_ldflags"
-
-       AC_CHECK_LIB(xml2, xmlXPathEval,
-                    [with_libxml2="yes"],
-                    [with_libxml2="no (symbol xmlXPathEval not found)"])
-
-       CFLAGS="$SAVE_CFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-dnl Add the right compiler flags and libraries.
-if test "x$with_libxml2" = "xyes"; then
-       BUILD_WITH_LIBXML2_CFLAGS="$with_libxml2_cflags"
-       BUILD_WITH_LIBXML2_LIBS="$with_libxml2_ldflags"
-       AC_SUBST(BUILD_WITH_LIBXML2_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBXML2_LIBS)
-fi
-if test "x$with_libvirt" = "xyes"
-then
-       with_libvirt_cflags="`pkg-config --cflags libvirt`"
-       if test $? -ne 0
-       then
-               with_libvirt="no"
-       fi
-       with_libvirt_ldflags="`pkg-config --libs libvirt`"
-       if test $? -ne 0
-       then
-               with_libvirt="no"
-       fi
-fi
-if test "x$with_libvirt" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libvirt_cflags"
-
-       AC_CHECK_HEADERS(libvirt/libvirt.h, [],
-                     [with_libvirt="no (libvirt/libvirt.h not found)"])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-if test "x$with_libvirt" = "xyes"
-then
-       SAVE_CFLAGS="$CFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
-
-       CFLAGS="$CFLAGS $with_libvirt_cflags"
-       LDFLAGS="$LDFLAGS $with_libvirt_ldflags"
-
-       AC_CHECK_LIB(virt, virDomainBlockStats,
-                    [with_libvirt="yes"],
-                    [with_libvirt="no (symbol virDomainBlockStats not found)"])
-
-       CFLAGS="$SAVE_CFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
-fi
-dnl Add the right compiler flags and libraries.
-if test "x$with_libvirt" = "xyes"; then
-       BUILD_WITH_LIBVIRT_CFLAGS="$with_libvirt_cflags"
-       BUILD_WITH_LIBVIRT_LIBS="$with_libvirt_ldflags"
-       AC_SUBST(BUILD_WITH_LIBVIRT_CFLAGS)
-       AC_SUBST(BUILD_WITH_LIBVIRT_LIBS)
-fi
-# }}}
-
-# $PKG_CONFIG --exists OpenIPMIpthread {{{
-with_libopenipmipthread="yes"
-with_libopenipmipthread_cflags=""
-with_libopenipmipthread_libs=""
-
-AC_MSG_CHECKING([for pkg-config])
-temp_result="no"
-if test "x$PKG_CONFIG" = "x"
-then
-       with_libopenipmipthread="no"
-       temp_result="no"
-else
-       temp_result="$PKG_CONFIG"
-fi
-AC_MSG_RESULT([$temp_result])
-
-if test "x$with_libopenipmipthread" = "xyes"
-then
-       AC_MSG_CHECKING([for libOpenIPMIpthread])
-       $PKG_CONFIG --exists OpenIPMIpthread 2>/dev/null
-       if test "$?" != "0"
-       then
-               with_libopenipmipthread="no (pkg-config doesn't know OpenIPMIpthread)"
-       fi
-       AC_MSG_RESULT([$with_libopenipmipthread])
-fi
-
-if test "x$with_libopenipmipthread" = "xyes"
-then
-       AC_MSG_CHECKING([for libOpenIPMIpthread CFLAGS])
-       temp_result="`$PKG_CONFIG --cflags OpenIPMIpthread`"
-       if test "$?" = "0"
-       then
-               with_libopenipmipthread_cflags="$temp_result"
-       else
-               with_libopenipmipthread="no ($PKG_CONFIG --cflags OpenIPMIpthread failed)"
-               temp_result="$PKG_CONFIG --cflags OpenIPMIpthread failed"
-       fi
-       AC_MSG_RESULT([$temp_result])
-fi
-
-if test "x$with_libopenipmipthread" = "xyes"
-then
-       AC_MSG_CHECKING([for libOpenIPMIpthread LDFLAGS])
-       temp_result="`$PKG_CONFIG --libs OpenIPMIpthread`"
-       if test "$?" = "0"
-       then
-               with_libopenipmipthread_ldflags="$temp_result"
-       else
-               with_libopenipmipthread="no ($PKG_CONFIG --libs OpenIPMIpthread failed)"
-               temp_result="$PKG_CONFIG --libs OpenIPMIpthread failed"
-       fi
-       AC_MSG_RESULT([$temp_result])
-fi
-
-if test "x$with_libopenipmipthread" = "xyes"
-then
-       SAVE_CPPFLAGS="$CPPFLAGS"
-       CPPFLAGS="$CPPFLAGS $with_libopenipmipthread_cflags"
-
-       AC_CHECK_HEADERS(OpenIPMI/ipmi_smi.h,
-                        [with_libopenipmipthread="yes"],
-                        [with_libopenipmipthread="no (OpenIPMI/ipmi_smi.h not found)"],
-[#include <OpenIPMI/ipmiif.h>
-#include <OpenIPMI/ipmi_err.h>
-#include <OpenIPMI/ipmi_posix.h>
-#include <OpenIPMI/ipmi_conn.h>
-])
-
-       CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-
-if test "x$with_libopenipmipthread" = "xyes"
-then
-       BUILD_WITH_OPENIPMI_CFLAGS="$with_libopenipmipthread_cflags"
-       BUILD_WITH_OPENIPMI_LIBS="$with_libopenipmipthread_ldflags"
-       AC_SUBST(BUILD_WITH_OPENIPMI_CFLAGS)
-       AC_SUBST(BUILD_WITH_OPENIPMI_LIBS)
-fi
-# }}}
-
-PKG_CHECK_MODULES([LIBNOTIFY], [libnotify],
-               [with_libnotify="yes"],
-               [if test "x$LIBNOTIFY_PKG_ERRORS" = "x"; then
-                        with_libnotify="no"
-                else
-                        with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"
-                fi])
-
-# Check for enabled/disabled features
-#
-
-# AC_COLLECTD(name, enable/disable, info-text, feature/module)
-# ------------------------------------------------------------
-dnl
-m4_define([my_toupper], [m4_translit([$1], m4_defn([m4_cr_letters]), m4_defn([m4_cr_LETTERS]))])
-dnl
-AC_DEFUN(
-       [AC_COLLECTD],
-       [
-       m4_if([$1], [], [AC_FATAL([AC_COLLECTD([$1], [$2], [$3], [$4]): 1st argument must not be empty])])dnl
-       m4_if(
-               [$2],
-               [enable],
-               [dnl
-               m4_define([EnDis],[disabled])dnl
-               m4_define([YesNo],[no])dnl
-               ],dnl
-               [m4_if(
-                       [$2],
-                       [disable],
-                       [dnl
-                       m4_define([EnDis],[enabled])dnl
-                       m4_define([YesNo],[yes])dnl
-                       ],
-                       [dnl
-                       AC_FATAL([AC_COLLECTD([$1], [$2], [$3], [$4]): 2nd argument must be either enable or disable])dnl
-                       ]dnl
-               )]dnl
-       )dnl
-       m4_if([$3], [feature], [],
-               [m4_if(
-                       [$3], [module], [],
-                       [dnl
-                       AC_FATAL([AC_COLLECTD([$1], [$2], [$3], [$4]): 3rd argument must be either feature or disable])dnl
-                       ]dnl
-               )]dnl
-       )dnl
-       AC_ARG_ENABLE(
-               [$1],
-               AS_HELP_STRING([--$2-$1], [$2 $4 (EnDis by def)]),
-               [],
-               enable_$1='[YesNo]'dnl
-       )# AC_ARG_ENABLE
-if test "x$enable_$1" = "xno"
-then
-       collectd_$1=0
-else
-       if test "x$enable_$1" = "xyes"
-       then
-               collectd_$1=1
-       else
-               AC_MSG_NOTICE([please specify either --enable-$1 or --disable-$1; enabling $1.])
-               collectd_$1=1
-               enable_$1='yes'
-       fi
-fi
-       AC_DEFINE_UNQUOTED([COLLECT_]my_toupper([$1]), [$collectd_$1], [wether or not to enable $3 $4])
-       AM_CONDITIONAL([BUILD_]my_toupper([$3])[_]my_toupper([$1]), [test "x$enable_$1" = "xyes"])dnl
-       ]dnl
-)# AC_COLLECTD(name, enable/disable, info-text, feature/module)
-
-# AC_PLUGIN(name, default, info)
-# ------------------------------------------------------------
-dnl
-AC_DEFUN(
-  [AC_PLUGIN],
-  [
-    enable_plugin="no"
-    force="no"
-    AC_ARG_ENABLE([$1], AC_HELP_STRING([--enable-$1], [$3]),
-    [
-     if test "x$enableval" = "xyes"
-     then
-            enable_plugin="yes"
-     else if test "x$enableval" = "xforce"
-     then
-            enable_plugin="yes"
-            force="yes"
-     else
-            enable_plugin="no (disabled on command line)"
-     fi; fi
-    ],
-    [
-        if test "x$enable_all_plugins" = "xauto"
-        then
-            if test "x$2" = "xyes"
-            then
-                    enable_plugin="yes"
-            else
-                    enable_plugin="no"
-            fi
-        else
-            enable_plugin="$enable_all_plugins"
-        fi
-    ])
-    if test "x$enable_plugin" = "xyes"
-    then
-           if test "x$2" = "xyes" || test "x$force" = "xyes"
-           then
-                   AC_DEFINE([HAVE_PLUGIN_]my_toupper([$1]), 1, [Define to 1 if the $1 plugin is enabled.])
-                   if test "x$2" != "xyes"
-                   then
-                           dependency_warning="yes"
-                   fi
-           else # User passed "yes" but dependency checking yielded "no" => Dependency problem.
-                   dependency_error="yes"
-                   enable_plugin="no (dependency error)"
-           fi
-    fi
-    AM_CONDITIONAL([BUILD_PLUGIN_]my_toupper([$1]), test "x$enable_plugin" = "xyes")
-    enable_$1="$enable_plugin"
-  ]
-)# AC_PLUGIN(name, default, info)
-
-m4_divert_once([HELP_ENABLE], [
-collectd features:])
-# FIXME: Remove these calls to `AC_COLLECTD' and then remove that macro.
-AC_COLLECTD([debug],     [enable],  [feature], [debugging])
-AC_COLLECTD([daemon],    [disable], [feature], [daemon mode])
-AC_COLLECTD([getifaddrs],[enable],  [feature], [getifaddrs under Linux])
-
-dependency_warning="no"
-dependency_error="no"
-
-plugin_ascent="no"
-plugin_battery="no"
-plugin_bind="no"
-plugin_conntrack="no"
-plugin_contextswitch="no"
-plugin_cpu="no"
-plugin_cpufreq="no"
-plugin_curl_json="no"
-plugin_curl_xml="no"
-plugin_df="no"
-plugin_disk="no"
-plugin_entropy="no"
-plugin_ethstat="no"
-plugin_fscache="no"
-plugin_interface="no"
-plugin_ipmi="no"
-plugin_ipvs="no"
-plugin_irq="no"
-plugin_libvirt="no"
-plugin_load="no"
-plugin_memory="no"
-plugin_multimeter="no"
-plugin_nfs="no"
-plugin_numa="no"
-plugin_perl="no"
-plugin_processes="no"
-plugin_protocols="no"
-plugin_serial="no"
-plugin_swap="no"
-plugin_tape="no"
-plugin_tcpconns="no"
-plugin_ted="no"
-plugin_thermal="no"
-plugin_users="no"
-plugin_uptime="no"
-plugin_vmem="no"
-plugin_vserver="no"
-plugin_wireless="no"
-plugin_zfs_arc="no"
-
-# Linux
-if test "x$ac_system" = "xLinux"
-then
-       plugin_battery="yes"
-       plugin_conntrack="yes"
-       plugin_contextswitch="yes"
-       plugin_cpu="yes"
-       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_numa="yes"
-       plugin_processes="yes"
-       plugin_protocols="yes"
-       plugin_serial="yes"
-       plugin_swap="yes"
-       plugin_tcpconns="yes"
-       plugin_thermal="yes"
-       plugin_uptime="yes"
-       plugin_vmem="yes"
-       plugin_vserver="yes"
-       plugin_wireless="yes"
-
-       if test "x$have_linux_ip_vs_h" = "xyes" || test "x$have_net_ip_vs_h" = "xyes" || test "x$have_ip_vs_h" = "xyes"
-       then
-               plugin_ipvs="yes"
-       fi
-fi
-
-if test "x$ac_system" = "xOpenBSD"
-then
-       plugin_tcpconns="yes"
-fi
-
-# Mac OS X devices
-if test "x$with_libiokit" = "xyes"
-then
-       plugin_battery="yes"
-       plugin_disk="yes"
-fi
-
-# AIX
-
-if test "x$ac_system" = "xAIX"
-then
-        plugin_tcpconns="yes"
-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"
-then
-       plugin_processes="yes"
-fi
-
-# Solaris
-if test "x$with_kstat" = "xyes"
-then
-       plugin_nfs="yes"
-       plugin_uptime="yes"
-       plugin_zfs_arc="yes"
-fi
-
-if test "x$with_devinfo$with_kstat" = "xyesyes"
-then
-       plugin_cpu="yes"
-       plugin_disk="yes"
-       plugin_interface="yes"
-       plugin_memory="yes"
-       plugin_tape="yes"
-fi
-
-# libstatgrab
-if test "x$with_libstatgrab" = "xyes"
-then
-       plugin_cpu="yes"
-       plugin_disk="yes"
-       plugin_interface="yes"
-       plugin_load="yes"
-       plugin_memory="yes"
-       plugin_swap="yes"
-       plugin_users="yes"
-fi
-
-if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
-then
-       plugin_ascent="yes"
-       if test "x$have_strptime" = "xyes"
-       then
-               plugin_bind="yes"
-       fi
-fi
-
-if test "x$with_libopenipmipthread" = "xyes"
-then
-       plugin_ipmi="yes"
-fi
-
-if test "x$with_libcurl" = "xyes" && test "x$with_libyajl" = "xyes"
-then
-       plugin_curl_json="yes"
-fi
-
-if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
-then
-       plugin_curl_xml="yes"
-fi
-
-if test "x$have_processor_info" = "xyes"
-then
-       plugin_cpu="yes"
-fi
-if test "x$have_sysctl" = "xyes"
-then
-       plugin_cpu="yes"
-       plugin_memory="yes"
-       plugin_uptime="yes"
-       if test "x$ac_system" = "xDarwin"
-       then
-               plugin_swap="yes"
-       fi
-fi
-if test "x$have_sysctlbyname" = "xyes"
-then
-       plugin_contextswitch="yes"
-       plugin_cpu="yes"
-       plugin_memory="yes"
-       plugin_tcpconns="yes"
-fi
-
-# Df plugin: Check if we know how to determine mount points first.
-#if test "x$have_listmntent" = "xyes"; then
-#      plugin_df="yes"
-#fi
-if test "x$have_getvfsstat" = "xyes" || test "x$have_getfsstat" = "xyes"
-then
-       plugin_df="yes"
-fi
-if test "x$c_cv_have_two_getmntent" = "xyes" || test "x$have_getmntent" = "xgen" || test "x$have_getmntent" = "xsun"
-then
-       plugin_df="yes"
-fi
-#if test "x$have_getmntent" = "xseq"
-#then
-#      plugin_df="yes"
-#fi
-if test "x$c_cv_have_one_getmntent" = "xyes"
-then
-       plugin_df="yes"
-fi
-
-# Df plugin: Check if we have either `statfs' or `statvfs' second.
-if test "x$plugin_df" = "xyes"
-then
-       plugin_df="no"
-       if test "x$have_statfs" = "xyes"
-       then
-               plugin_df="yes"
-       fi
-       if test "x$have_statvfs" = "xyes"
-       then
-               plugin_df="yes"
-       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"
-fi
-
-if test "x$with_libxml2" = "xyes" && test "x$with_libvirt" = "xyes"
-then
-       plugin_libvirt="yes"
-fi
-
-if test "x$have_getloadavg" = "xyes"
-then
-       plugin_load="yes"
-fi
-
-if test "x$c_cv_have_libperl$c_cv_have_perl_ithreads" = "xyesyes"
-then
-       plugin_perl="yes"
-fi
-
-# Mac OS X memory interface
-if test "x$have_host_statistics" = "xyes"
-then
-       plugin_memory="yes"
-fi
-
-if test "x$have_termios_h" = "xyes"
-then
-       plugin_multimeter="yes"
-       plugin_ted="yes"
-fi
-
-if test "x$have_thread_info" = "xyes"
-then
-       plugin_processes="yes"
-fi
-
-if test "x$with_kvm_getprocs" = "xyes" && test "x$have_struct_kinfo_proc_freebsd" = "xyes"
-then
-       plugin_processes="yes"
-fi
-
-if test "x$with_kvm_getswapinfo" = "xyes"
-then
-       plugin_swap="yes"
-fi
-
-if test "x$have_swapctl" = "xyes" && test "x$c_cv_have_swapctl_two_args" = "xyes"
-then
-       plugin_swap="yes"
-fi
-
-if test "x$with_kvm_openfiles$with_kvm_nlist" = "xyesyes"
-then
-       plugin_tcpconns="yes"
-fi
-
-if test "x$have_getutent" = "xyes"
-then
-       plugin_users="yes"
-fi
-if test "x$have_getutxent" = "xyes"
-then
-       plugin_users="yes"
-fi
-
-m4_divert_once([HELP_ENABLE], [
-collectd plugins:])
-
-AC_ARG_ENABLE([all-plugins],
-               AC_HELP_STRING([--enable-all-plugins],
-                               [enable all plugins (auto by def)]),
-               [
-                if test "x$enableval" = "xyes"
-                then
-                        enable_all_plugins="yes"
-                else if test "x$enableval" = "xauto"
-                then
-                        enable_all_plugins="auto"
-                else
-                        enable_all_plugins="no"
-                fi; fi
-               ],
-               [enable_all_plugins="auto"])
-
-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])
-AC_PLUGIN([apple_sensors], [$with_libiokit],   [Apple's hardware sensors])
-AC_PLUGIN([ascent],      [$plugin_ascent],     [AscentEmu player statistics])
-AC_PLUGIN([battery],     [$plugin_battery],    [Battery statistics])
-AC_PLUGIN([bind],        [$plugin_bind],       [ISC Bind nameserver statistics])
-AC_PLUGIN([conntrack],   [$plugin_conntrack],  [nf_conntrack statistics])
-AC_PLUGIN([contextswitch], [$plugin_contextswitch], [context switch statistics])
-AC_PLUGIN([cpufreq],     [$plugin_cpufreq],    [CPU frequency statistics])
-AC_PLUGIN([cpu],         [$plugin_cpu],        [CPU usage statistics])
-AC_PLUGIN([csv],         [yes],                [CSV output plugin])
-AC_PLUGIN([curl],        [$with_libcurl],      [CURL generic web statistics])
-AC_PLUGIN([curl_json],   [$plugin_curl_json],    [CouchDB statistics])
-AC_PLUGIN([curl_xml],   [$plugin_curl_xml],    [CURL generic xml statistics])
-AC_PLUGIN([dbi],         [$with_libdbi],       [General database statistics])
-AC_PLUGIN([df],          [$plugin_df],         [Filesystem usage statistics])
-AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
-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])
-AC_PLUGIN([gmond],       [$with_libganglia],   [Ganglia plugin])
-AC_PLUGIN([hddtemp],     [yes],                [Query hddtempd])
-AC_PLUGIN([interface],   [$plugin_interface],  [Interface traffic statistics])
-AC_PLUGIN([ipmi],        [$plugin_ipmi],       [IPMI sensor statistics])
-AC_PLUGIN([iptables],    [$with_libiptc],      [IPTables rule counters])
-AC_PLUGIN([ipvs],        [$plugin_ipvs],       [IPVS connection statistics])
-AC_PLUGIN([irq],         [$plugin_irq],        [IRQ statistics])
-AC_PLUGIN([java],        [$with_java],         [Embed the Java Virtual Machine])
-AC_PLUGIN([libvirt],     [$plugin_libvirt],    [Virtual machine statistics])
-AC_PLUGIN([load],        [$plugin_load],       [System load])
-AC_PLUGIN([logfile],     [yes],                [File logging plugin])
-AC_PLUGIN([lpar],        [$with_perfstat],     [AIX logical partitions statistics])
-AC_PLUGIN([madwifi],     [$have_linux_wireless_h], [Madwifi wireless statistics])
-AC_PLUGIN([match_empty_counter], [yes],        [The empty counter match])
-AC_PLUGIN([match_hashed], [yes],               [The hashed match])
-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])
-AC_PLUGIN([modbus],      [$with_libmodbus],    [Modbus plugin])
-AC_PLUGIN([multimeter],  [$plugin_multimeter], [Read multimeter values])
-AC_PLUGIN([mysql],       [$with_libmysql],     [MySQL statistics])
-AC_PLUGIN([netapp],      [$with_libnetapp],    [NetApp plugin])
-AC_PLUGIN([netlink],     [$with_libnetlink],   [Enhanced Linux network statistics])
-AC_PLUGIN([network],     [yes],                [Network communication plugin])
-AC_PLUGIN([nfs],         [$plugin_nfs],        [NFS statistics])
-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])
-AC_PLUGIN([openvpn],     [yes],                [OpenVPN client statistics])
-AC_PLUGIN([oracle],      [$with_oracle],       [Oracle plugin])
-AC_PLUGIN([perl],        [$plugin_perl],       [Embed a Perl interpreter])
-AC_PLUGIN([pf],          [$have_net_pfvar_h],  [BSD packet filter (PF) statistics])
-# FIXME: Check for libevent, too.
-AC_PLUGIN([pinba],       [$have_protoc_c],     [Pinba statistics])
-AC_PLUGIN([ping],        [$with_liboping],     [Network latency statistics])
-AC_PLUGIN([postgresql],  [$with_libpq],        [PostgreSQL database statistics])
-AC_PLUGIN([powerdns],    [yes],                [PowerDNS statistics])
-AC_PLUGIN([processes],   [$plugin_processes],  [Process statistics])
-AC_PLUGIN([protocols],   [$plugin_protocols],  [Protocol (IP, TCP, ...) statistics])
-AC_PLUGIN([python],      [$with_python],       [Embed a Python interpreter])
-AC_PLUGIN([redis],       [$with_libcredis],    [Redis plugin])
-AC_PLUGIN([routeros],    [$with_librouteros],  [RouterOS plugin])
-AC_PLUGIN([rrdcached],   [$librrd_rrdc_update], [RRDTool output plugin])
-AC_PLUGIN([rrdtool],     [$with_librrd],       [RRDTool output plugin])
-AC_PLUGIN([sensors],     [$with_libsensors],   [lm_sensors statistics])
-AC_PLUGIN([serial],      [$plugin_serial],     [serial port traffic])
-AC_PLUGIN([snmp],        [$with_libnetsnmp],   [SNMP querying plugin])
-AC_PLUGIN([swap],        [$plugin_swap],       [Swap usage statistics])
-AC_PLUGIN([syslog],      [$have_syslog],       [Syslog logging plugin])
-AC_PLUGIN([table],       [yes],                [Parsing of tabular data])
-AC_PLUGIN([tail],        [yes],                [Parsing of logfiles])
-AC_PLUGIN([tape],        [$plugin_tape],       [Tape drive statistics])
-AC_PLUGIN([target_notification], [yes],        [The notification target])
-AC_PLUGIN([target_replace], [yes],             [The replace target])
-AC_PLUGIN([target_scale],[yes],                [The scale target])
-AC_PLUGIN([target_set],  [yes],                [The set target])
-AC_PLUGIN([target_v5upgrade], [yes],           [The v5upgrade target])
-AC_PLUGIN([tcpconns],    [$plugin_tcpconns],   [TCP connection statistics])
-AC_PLUGIN([teamspeak2],  [yes],                [TeamSpeak2 server statistics])
-AC_PLUGIN([ted],         [$plugin_ted],        [Read The Energy Detective values])
-AC_PLUGIN([thermal],     [$plugin_thermal],    [Linux ACPI thermal zone statistics])
-AC_PLUGIN([threshold],   [yes],                [Threshold checking plugin])
-AC_PLUGIN([tokyotyrant], [$with_libtokyotyrant],  [TokyoTyrant database statistics])
-AC_PLUGIN([unixsock],    [yes],                [Unixsock communication plugin])
-AC_PLUGIN([uptime],      [$plugin_uptime],     [Uptime statistics])
-AC_PLUGIN([users],       [$plugin_users],      [User statistics])
-AC_PLUGIN([uuid],        [yes],                [UUID as hostname plugin])
-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])
-AC_PLUGIN([xmms],        [$with_libxmms],      [XMMS statistics])
-AC_PLUGIN([zfs_arc],     [$plugin_zfs_arc],    [ZFS ARC statistics])
-
-dnl Default configuration file
-# Load either syslog or logfile
-LOAD_PLUGIN_SYSLOG=""
-LOAD_PLUGIN_LOGFILE=""
-
-AC_MSG_CHECKING([which default log plugin to load])
-default_log_plugin="none"
-if test "x$enable_syslog" = "xyes"
-then
-       default_log_plugin="syslog"
-else
-       LOAD_PLUGIN_SYSLOG="##"
-fi
-
-if test "x$enable_logfile" = "xyes"
-then
-       if test "x$default_log_plugin" = "xnone"
-       then
-               default_log_plugin="logfile"
-       else
-               LOAD_PLUGIN_LOGFILE="#"
-       fi
-else
-       LOAD_PLUGIN_LOGFILE="##"
-fi
-AC_MSG_RESULT([$default_log_plugin])
-
-AC_SUBST(LOAD_PLUGIN_SYSLOG)
-AC_SUBST(LOAD_PLUGIN_LOGFILE)
-
-DEFAULT_LOG_LEVEL="info"
-if test "x$enable_debug" = "xyes"
-then
-       DEFAULT_LOG_LEVEL="debug"
-fi
-AC_SUBST(DEFAULT_LOG_LEVEL)
-
-# Load only one of rrdtool, network, csv in the default config.
-LOAD_PLUGIN_RRDTOOL=""
-LOAD_PLUGIN_NETWORK=""
-LOAD_PLUGIN_CSV=""
-
-AC_MSG_CHECKING([which default write plugin to load])
-default_write_plugin="none"
-if test "x$enable_rrdtool" = "xyes"
-then
-       default_write_plugin="rrdtool"
-else
-       LOAD_PLUGIN_RRDTOOL="##"
-fi
-
-if test "x$enable_network" = "xyes"
-then
-       if test "x$default_write_plugin" = "xnone"
-       then
-               default_write_plugin="network"
-       else
-               LOAD_PLUGIN_NETWORK="#"
-       fi
-else
-       LOAD_PLUGIN_NETWORK="##"
-fi
-
-if test "x$enable_csv" = "xyes"
-then
-       if test "x$default_write_plugin" = "xnone"
-       then
-               default_write_plugin="csv"
-       else
-               LOAD_PLUGIN_CSV="#"
-       fi
-else
-       LOAD_PLUGIN_CSV="##"
-fi
-AC_MSG_RESULT([$default_write_plugin])
-
-AC_SUBST(LOAD_PLUGIN_RRDTOOL)
-AC_SUBST(LOAD_PLUGIN_NETWORK)
-AC_SUBST(LOAD_PLUGIN_CSV)
-
-dnl ip_vs.h
-if test "x$ac_system" = "xLinux" \
-       && test "x$have_linux_ip_vs_h$have_net_ip_vs_h$have_ip_vs_h" = "xnonono"
-then
-       enable_ipvs="$enable_ipvs (ip_vs.h not found)"
-fi
-
-if test "x$ip_vs_h_needs_kernel_cflags" = "xyes"
-then
-       enable_ipvs="$enable_ipvs (needs $KERNEL_CFLAGS)"
-fi
-
-dnl Perl bindings
-AC_ARG_WITH(perl-bindings, [AS_HELP_STRING([--with-perl-bindings@<:@=OPTIONS@:>@], [Options passed to "perl Makefile.PL".])],
-[
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
-       then
-               PERL_BINDINGS_OPTIONS="$withval"
-               with_perl_bindings="yes"
-       else
-               PERL_BINDINGS_OPTIONS=""
-               with_perl_bindings="$withval"
-       fi
-],
-[
-       PERL_BINDINGS_OPTIONS=""
-       if test -n "$perl_interpreter"
-       then
-               with_perl_bindings="yes"
-       else
-               with_perl_bindings="no (no perl interpreter found)"
-       fi
-])
-if test "x$with_perl_bindings" = "xyes"
-then
-       PERL_BINDINGS="perl"
-else
-       PERL_BINDINGS=""
-fi
-AC_SUBST(PERL_BINDINGS)
-AC_SUBST(PERL_BINDINGS_OPTIONS)
-
-dnl libcollectdclient
-LCC_VERSION_MAJOR=`echo $PACKAGE_VERSION | cut -d'.' -f1`
-LCC_VERSION_MINOR=`echo $PACKAGE_VERSION | cut -d'.' -f2`
-LCC_VERSION_PATCH=`echo $PACKAGE_VERSION | cut -d'.' -f3`
-
-LCC_VERSION_EXTRA=`echo $PACKAGE_VERSION | cut -d'.' -f4-`
-
-LCC_VERSION_STRING="$LCC_VERSION_MAJOR.$LCC_VERSION_MINOR.$LCC_VERSION_PATCH"
-
-AC_SUBST(LCC_VERSION_MAJOR)
-AC_SUBST(LCC_VERSION_MINOR)
-AC_SUBST(LCC_VERSION_PATCH)
-AC_SUBST(LCC_VERSION_EXTRA)
-AC_SUBST(LCC_VERSION_STRING)
-
-AC_CONFIG_FILES(src/libcollectdclient/collectd/lcc_features.h)
-
-AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile bindings/java/Makefile)
-
-if test "x$with_librrd" = "xyes" \
-       && test "x$librrd_threadsafe" != "xyes"
-then
-       with_librrd="yes (warning: librrd is not thread-safe)"
-fi
-
-if test "x$with_libperl" = "xyes"
-then
-       with_libperl="yes (version `$perl_interpreter -MConfig -e 'print $Config{version};'`)"
-else
-       enable_perl="no (needs libperl)"
-fi
-
-if test "x$enable_perl" = "xno" && test "x$c_cv_have_perl_ithreads" = "xno"
-then
-       enable_perl="no (libperl doesn't support ithreads)"
-fi
-
-if test "x$with_perl_bindings" = "xyes" \
-       && test "x$PERL_BINDINGS_OPTIONS" != "x"
-then
-       with_perl_bindings="yes ($PERL_BINDINGS_OPTIONS)"
-fi
-
-cat <<EOF;
-
-Configuration:
-  Libraries:
-    libcurl . . . . . . . $with_libcurl
-    libdbi  . . . . . . . $with_libdbi
-    libcredis . . . . . . $with_libcredis
-    libesmtp  . . . . . . $with_libesmtp
-    libganglia  . . . . . $with_libganglia
-    libgcrypt . . . . . . $with_libgcrypt
-    libiokit  . . . . . . $with_libiokit
-    libiptc . . . . . . . $with_libiptc
-    libjvm  . . . . . . . $with_java
-    libkstat  . . . . . . $with_kstat
-    libkvm  . . . . . . . $with_libkvm
-    libmemcached  . . . . $with_libmemcached
-    libmodbus . . . . . . $with_libmodbus
-    libmysql  . . . . . . $with_libmysql
-    libnetapp . . . . . . $with_libnetapp
-    libnetlink  . . . . . $with_libnetlink
-    libnetsnmp  . . . . . $with_libnetsnmp
-    libnotify . . . . . . $with_libnotify
-    liboconfig  . . . . . $with_liboconfig
-    libopenipmi . . . . . $with_libopenipmipthread
-    liboping  . . . . . . $with_liboping
-    libpcap . . . . . . . $with_libpcap
-    libperfstat . . . . . $with_perfstat
-    libperl . . . . . . . $with_libperl
-    libpq . . . . . . . . $with_libpq
-    libpthread  . . . . . $with_libpthread
-    librabbitmq . . . . . $with_librabbitmq
-    librouteros . . . . . $with_librouteros
-    librrd  . . . . . . . $with_librrd
-    libsensors  . . . . . $with_libsensors
-    libstatgrab . . . . . $with_libstatgrab
-    libtokyotyrant  . . . $with_libtokyotyrant
-    libupsclient  . . . . $with_libupsclient
-    libvarnish  . . . . . $with_libvarnish
-    libvirt . . . . . . . $with_libvirt
-    libxml2 . . . . . . . $with_libxml2
-    libxmms . . . . . . . $with_libxmms
-    libyajl . . . . . . . $with_libyajl
-    libevent  . . . . . . $with_libevent
-    protobuf-c  . . . . . $have_protoc_c
-    oracle  . . . . . . . $with_oracle
-    python  . . . . . . . $with_python
-
-  Features:
-    daemon mode . . . . . $enable_daemon
-    debug . . . . . . . . $enable_debug
-
-  Bindings:
-    perl  . . . . . . . . $with_perl_bindings
-
-  Modules:
-    aggregation . . . . . $enable_aggregation
-    amqp    . . . . . . . $enable_amqp
-    apache  . . . . . . . $enable_apache
-    apcups  . . . . . . . $enable_apcups
-    apple_sensors . . . . $enable_apple_sensors
-    ascent  . . . . . . . $enable_ascent
-    battery . . . . . . . $enable_battery
-    bind  . . . . . . . . $enable_bind
-    conntrack . . . . . . $enable_conntrack
-    contextswitch . . . . $enable_contextswitch
-    cpu . . . . . . . . . $enable_cpu
-    cpufreq . . . . . . . $enable_cpufreq
-    csv . . . . . . . . . $enable_csv
-    curl  . . . . . . . . $enable_curl
-    curl_json . . . . . . $enable_curl_json
-    curl_xml  . . . . . . $enable_curl_xml
-    dbi . . . . . . . . . $enable_dbi
-    df  . . . . . . . . . $enable_df
-    disk  . . . . . . . . $enable_disk
-    dns . . . . . . . . . $enable_dns
-    email . . . . . . . . $enable_email
-    entropy . . . . . . . $enable_entropy
-    ethstat . . . . . . . $enable_ethstat
-    exec  . . . . . . . . $enable_exec
-    filecount . . . . . . $enable_filecount
-    fscache . . . . . . . $enable_fscache
-    gmond . . . . . . . . $enable_gmond
-    hddtemp . . . . . . . $enable_hddtemp
-    interface . . . . . . $enable_interface
-    ipmi  . . . . . . . . $enable_ipmi
-    iptables  . . . . . . $enable_iptables
-    ipvs  . . . . . . . . $enable_ipvs
-    irq . . . . . . . . . $enable_irq
-    java  . . . . . . . . $enable_java
-    libvirt . . . . . . . $enable_libvirt
-    load  . . . . . . . . $enable_load
-    logfile . . . . . . . $enable_logfile
-    lpar... . . . . . . . $enable_lpar
-    madwifi . . . . . . . $enable_madwifi
-    match_empty_counter . $enable_match_empty_counter
-    match_hashed  . . . . $enable_match_hashed
-    match_regex . . . . . $enable_match_regex
-    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
-    modbus  . . . . . . . $enable_modbus
-    multimeter  . . . . . $enable_multimeter
-    mysql . . . . . . . . $enable_mysql
-    netapp  . . . . . . . $enable_netapp
-    netlink . . . . . . . $enable_netlink
-    network . . . . . . . $enable_network
-    nfs . . . . . . . . . $enable_nfs
-    nginx . . . . . . . . $enable_nginx
-    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
-    openvpn . . . . . . . $enable_openvpn
-    oracle  . . . . . . . $enable_oracle
-    perl  . . . . . . . . $enable_perl
-    pf  . . . . . . . . . $enable_pf
-    pinba . . . . . . . . $enable_pinba
-    ping  . . . . . . . . $enable_ping
-    postgresql  . . . . . $enable_postgresql
-    powerdns  . . . . . . $enable_powerdns
-    processes . . . . . . $enable_processes
-    protocols . . . . . . $enable_protocols
-    python  . . . . . . . $enable_python
-    redis . . . . . . . . $enable_redis
-    routeros  . . . . . . $enable_routeros
-    rrdcached . . . . . . $enable_rrdcached
-    rrdtool . . . . . . . $enable_rrdtool
-    sensors . . . . . . . $enable_sensors
-    serial  . . . . . . . $enable_serial
-    snmp  . . . . . . . . $enable_snmp
-    swap  . . . . . . . . $enable_swap
-    syslog  . . . . . . . $enable_syslog
-    table . . . . . . . . $enable_table
-    tail  . . . . . . . . $enable_tail
-    tape  . . . . . . . . $enable_tape
-    target_notification . $enable_target_notification
-    target_replace  . . . $enable_target_replace
-    target_scale  . . . . $enable_target_scale
-    target_set  . . . . . $enable_target_set
-    target_v5upgrade  . . $enable_target_v5upgrade
-    tcpconns  . . . . . . $enable_tcpconns
-    teamspeak2  . . . . . $enable_teamspeak2
-    ted . . . . . . . . . $enable_ted
-    thermal . . . . . . . $enable_thermal
-    threshold . . . . . . $enable_threshold
-    tokyotyrant . . . . . $enable_tokyotyrant
-    unixsock  . . . . . . $enable_unixsock
-    uptime  . . . . . . . $enable_uptime
-    users . . . . . . . . $enable_users
-    uuid  . . . . . . . . $enable_uuid
-    varnish . . . . . . . $enable_varnish
-    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
-    xmms  . . . . . . . . $enable_xmms
-    zfs_arc . . . . . . . $enable_zfs_arc
-
-EOF
-
-if test "x$dependency_error" = "xyes"; then
-       AC_MSG_ERROR("Some plugins are missing dependencies - see the summary above for details")
-fi
-
-if test "x$dependency_warning" = "xyes"; then
-       AC_MSG_WARN("Some plugins seem to have missing dependencies but have been enabled forcibly - see the summary above for details")
-fi
-
-# vim: set fdm=marker :
index 9af0fb0..809f19d 100644 (file)
@@ -17,10 +17,15 @@ Collectd network protocol implementation.
 """
 
 import socket,struct,sys
-try:
-  from io import StringIO
-except ImportError:
-  from cStringIO import StringIO
+import platform
+if platform.python_version() < '2.8.0':
+    # Python 2.7 and below io.StringIO does not like unicode
+    from StringIO import StringIO
+else:
+    try:
+      from io import StringIO
+    except ImportError:
+      from cStringIO import StringIO
 
 from datetime import datetime
 from copy import deepcopy
@@ -76,7 +81,7 @@ def decode_network_values(ptype, plen, buf):
     assert double.size == number.size
 
     result = []
-    for dstype in buf[header.size+short.size:off]:
+    for dstype in [ord(x) for x in buf[header.size+short.size:off]]:
         if dstype == DS_TYPE_COUNTER:
             result.append((dstype, number.unpack_from(buf, off)[0]))
             off += valskip
index 1b8e6b1..5cd4ab8 100644 (file)
@@ -158,7 +158,7 @@ class Collectd():
         self._sock.send(c + "\n")
         status_message = self._readline()
         if self.noisy:
-            print "[recive] %s" % status_message
+            print "[receive] %s" % status_message
         if not status_message:
             return None
         code, message = status_message.split(' ', 1)
index af64fb1..5ff259f 100755 (executable)
@@ -1,4 +1,25 @@
 #!/usr/bin/perl
+# Copyright (c) 2006-2010 Florian Forster <octo at collectd.org>
+# Copyright (c) 2006-2008 Sebastian Harl <sh at tokkee.org>
+# Copyright (c) 2008      Mirko Buffoni <briareos at eswat.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 
 use strict;
 use warnings;
@@ -507,6 +528,7 @@ sub _custom_sort_arrayref
 {
   my $array_ref = shift;
   my $array_sort = shift;
+  my $unknown_first = shift || 0;
 
   my %elements = map { $_ => 1 } (@$array_ref);
   splice (@$array_ref, 0);
@@ -517,7 +539,12 @@ sub _custom_sort_arrayref
     push (@$array_ref, $_);
     delete ($elements{$_});
   }
-  push (@$array_ref, sort (keys %elements));
+  if ($unknown_first) {
+    unshift (@$array_ref, sort (keys %elements));
+  }
+  else {
+    push (@$array_ref, sort (keys %elements));
+  }
 } # _custom_sort_arrayref
 
 sub action_show_host
@@ -957,9 +984,9 @@ sub load_graph_definitions
 
   $GraphDefs =
   {
-    apache_bytes => ['DEF:min_raw={file}:count:MIN',
-    'DEF:avg_raw={file}:count:AVERAGE',
-    'DEF:max_raw={file}:count:MAX',
+    apache_bytes => ['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,*',
@@ -976,9 +1003,9 @@ sub load_graph_definitions
     'GPRINT:avg:LAST:%5.1lf%s Last',
     'GPRINT:avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
     ],
-   apache_connections => ['DEF:min={file}:count:MIN',
-    'DEF:avg={file}:count:AVERAGE',
-    'DEF:max={file}:count:MAX',
+   apache_connections => ['DEF:min={file}:value:MIN',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:max={file}:value:MAX',
     "AREA:max#$HalfBlue",
     "AREA:min#$Canvas",
     "LINE1:avg#$FullBlue:Connections",
@@ -987,9 +1014,9 @@ sub load_graph_definitions
     'GPRINT:max:MAX:%6.2lf Max,',
     'GPRINT:avg:LAST:%6.2lf Last'
     ],
-    apache_idle_workers => ['DEF:min={file}:count:MIN',
-    'DEF:avg={file}:count:AVERAGE',
-    'DEF:max={file}:count:MAX',
+    apache_idle_workers => ['DEF:min={file}:value:MIN',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:max={file}:value:MAX',
     "AREA:max#$HalfBlue",
     "AREA:min#$Canvas",
     "LINE1:avg#$FullBlue:Idle Workers",
@@ -998,9 +1025,9 @@ sub load_graph_definitions
     'GPRINT:max:MAX:%6.2lf Max,',
     'GPRINT:avg:LAST:%6.2lf Last'
     ],
-    apache_requests => ['DEF:min={file}:count:MIN',
-    'DEF:avg={file}:count:AVERAGE',
-    'DEF:max={file}:count:MAX',
+    apache_requests => ['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",
@@ -1009,9 +1036,9 @@ sub load_graph_definitions
     'GPRINT:max:MAX:%6.2lf Max,',
     'GPRINT:avg:LAST:%6.2lf Last'
     ],
-    apache_scoreboard => ['DEF:min={file}:count:MIN',
-    'DEF:avg={file}:count:AVERAGE',
-    'DEF:max={file}:count:MAX',
+    apache_scoreboard => ['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",
@@ -2676,6 +2703,7 @@ sub load_graph_definitions
   $GraphDefs->{'virt_cpu_total'} = $GraphDefs->{'virt_cpu_total'};
 
   $MetaGraphDefs->{'cpu'} = \&meta_graph_cpu;
+  $MetaGraphDefs->{'df_complex'} = \&meta_graph_df;
   $MetaGraphDefs->{'dns_qtype'} = \&meta_graph_dns;
   $MetaGraphDefs->{'dns_rcode'} = \&meta_graph_dns;
   $MetaGraphDefs->{'if_rx_errors'} = \&meta_graph_if_rx_errors;
@@ -2858,6 +2886,73 @@ sub meta_graph_cpu
   return (meta_graph_generic_stack ($opts, $sources));
 } # meta_graph_cpu
 
+sub meta_graph_df
+{
+  confess ("Wrong number of arguments") if (@_ != 5);
+
+  my $host = shift;
+  my $plugin = shift;
+  my $plugin_instance = shift;
+  my $type = shift;
+  my $type_instances = shift;
+
+  my $opts = {};
+  my $sources = [];
+
+  my $prefix = "$host/$plugin"
+  . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
+
+  $opts->{'title'} = "Disk usage $prefix";
+
+  $opts->{'number_format'} = '%5.1lf%s';
+  $opts->{'rrd_opts'} = ['-l', 0, '-b', '1024', '-v', 'Bytes'];
+
+  my @files = ();
+
+  $opts->{'colors'} =
+  {
+    'used'              => 'ff0000',
+    'snap_normal_used'  => 'c10640',
+    'snap_reserve_used' => '820c81',
+    'snap_reserved'     => 'f15aef',
+    'reserved'          => 'ffb000',
+    'free'              => '00ff00',
+    'sis_saved'         => '00e0e0',
+    'dedup_saved'       => '00c1c1',
+    'compression_saved' => '00a2a2'
+  };
+
+  # LVM uses LV names as type-instance; they should sort first
+  _custom_sort_arrayref ($type_instances,
+    [qw(compression_saved dedup_saved sis_saved free reserved snap_reserved
+      snap_reserve_used snap_normal_used used)], 1);
+
+  for (@$type_instances)
+  {
+    my $inst = $_;
+    my $file = '';
+
+    for (@DataDirs)
+    {
+      if (-e "$_/$prefix-$inst.rrd")
+      {
+       $file = "$_/$prefix-$inst.rrd";
+       last;
+      }
+    }
+    confess ("No file found for $prefix") if ($file eq '');
+
+    push (@$sources,
+      {
+       name => $inst,
+       file => $file
+      }
+    );
+  } # for (@$type_instances)
+
+  return (meta_graph_generic_stack ($opts, $sources));
+} # meta_graph_df
+
 sub meta_graph_dns
 {
   confess ("Wrong number of arguments") if (@_ != 5);
index 3bb3d8b..5fe4313 100644 (file)
@@ -2,16 +2,16 @@
 GraphWidth 400
 #UnixSockAddr "/var/run/collectd-unixsock"
 <Type apache_bytes>
-  DataSources count
-  DSName "count Bytes/s"
+  DataSources value
+  DSName "value Bytes/s"
   RRDTitle "Apache Traffic"
   RRDVerticalLabel "Bytes/s"
   RRDFormat "%5.1lf%s"
   Color count 0000ff
 </Type>
 <Type apache_requests>
-  DataSources count
-  DSName "count Requests/s"
+  DataSources value
+  DSName "value Requests/s"
   RRDTitle "Apache Traffic"
   RRDVerticalLabel "Requests/s"
   RRDFormat "%5.2lf"
@@ -19,7 +19,7 @@ GraphWidth 400
 </Type>
 <Type apache_scoreboard>
   Module GenericStacked
-  DataSources count
+  DataSources value
   RRDTitle "Apache scoreboard on {hostname}"
   RRDVerticalLabel "Slots"
   RRDFormat "%6.2lf"
@@ -245,15 +245,15 @@ GraphWidth 400
   RRDFormat "%6.1lf"
 </Type>
 <Type conntrack>
-  DataSources conntrack
-  DSName conntrack Conntrack count
+  DataSources value
+  DSName value Conntrack count
   RRDTitle "nf_conntrack connections on {hostname}"
   RRDVerticalLabel "Count"
   RRDFormat "%4.0lf"
 </Type>
 <Type entropy>
-  DataSources entropy
-  DSName entropy Entropy bits
+  DataSources value
+  DSName value Entropy bits
   RRDTitle "Available entropy on {hostname}"
   RRDVerticalLabel "Bits"
   RRDFormat "%4.0lf"
@@ -267,8 +267,8 @@ GraphWidth 400
   Color value 00b000
 </Type>
 <Type frequency>
-  DataSources frequency
-  DSName frequency Frequency
+  DataSources value
+  DSName value Frequency
   RRDTitle "Frequency ({type_instance})"
   RRDVerticalLabel "Hertz"
   RRDFormat "%4.1lfHz"
@@ -542,16 +542,16 @@ GraphWidth 400
   Scale 8
 </Type>
 <Type percent>
-  DataSources percent
-  DSName percent Percent
+  DataSources value
+  DSName value Percent
   RRDTitle "Percent ({type_instance})"
   RRDVerticalLabel "Percent"
   RRDFormat "%4.1lf%%"
   Color percent 0000ff
 </Type>
 <Type ping>
-  DataSources ping
-  DSName "ping Latency"
+  DataSources value
+  DSName "value Latency"
   RRDTitle "Network latency ({type_instance})"
   RRDVerticalLabel "Milliseconds"
   RRDFormat "%5.2lfms"
@@ -700,8 +700,8 @@ GraphWidth 400
   Scale 0.001
 </Type>
 <Type users>
-  DataSources users
-  DSName users Users
+  DataSources value
+  DSName value Users
   RRDTitle "Users ({type_instance}) on {hostname}"
   RRDVerticalLabel "Users"
   RRDFormat "%.1lf"
index d20be35..a376002 100644 (file)
@@ -44,7 +44,7 @@ return (1);
 =item B<gc_read_config> (I<$file>)
 
 Reads the configuration from the file located at I<$file>. Returns B<true> when
-successfull and B<false> otherwise.
+successful and B<false> otherwise.
 
 =cut
 
index 42582a7..36f8706 100644 (file)
@@ -43,7 +43,7 @@ return (1);
 =item B<gc_read_config> (I<$file>)
 
 Reads the configuration from the file located at I<$file>. Returns B<true> when
-successfull and B<false> otherwise.
+successful and B<false> otherwise.
 
 =cut
 
index 5a0b522..3d6f61e 100644 (file)
@@ -59,7 +59,7 @@ sub _create_object
   my $module = shift;
   my $obj;
 
-  # Surpress warnings and error messages caused by the eval.
+  # Suppress warnings and error messages caused by the eval.
   local $SIG{__WARN__} = sub { return (1); print STDERR "WARNING: " . join (', ', @_) . "\n"; };
   local $SIG{__DIE__}  = sub { return (1); print STDERR "FATAL: "   . join (', ', @_) . "\n"; };
 
index f68cc1a..9539062 100644 (file)
@@ -100,14 +100,16 @@ static int my_read (void)
        vl.time       = time (NULL);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "myplugin", sizeof (vl.plugin));
+
+       /* it is strongly recommended to use a type defined in the types.db file
+        * instead of a custom type */
+       sstrncpy (vl.type, "myplugin", sizeof (vl.plugin));
        /* optionally set vl.plugin_instance and vl.type_instance to reasonable
         * values (default: "") */
 
        /* dispatch the values to collectd which passes them on to all registered
-        * write functions - the first argument is used to lookup the data set
-        * definition (it is strongly recommended to use a type defined in the
-        * types.db file) */
-       plugin_dispatch_values ("myplugin", &vl);
+        * write functions */
+       plugin_dispatch_values (&vl);
 
        /* A return value != 0 indicates an error and the plugin will be skipped
         * for an increasing amount of time. */
@@ -117,7 +119,8 @@ static int my_read (void)
 /*
  * This function is called after values have been dispatched to collectd.
  */
-static int my_write (const data_set_t *ds, const value_list_t *vl)
+static int my_write (const data_set_t *ds, const value_list_t *vl,
+               user_data_t *ud)
 {
        char name[1024] = "";
        int i = 0;
@@ -151,7 +154,7 @@ static int my_write (const data_set_t *ds, const value_list_t *vl)
 /*
  * This function is called when plugin_log () has been used.
  */
-static void my_log (int severity, const char *msg)
+static void my_log (int severity, const char *msg, user_data_t *ud)
 {
        printf ("LOG: %i - %s\n", severity, msg);
        return;
@@ -160,7 +163,7 @@ static void my_log (int severity, const char *msg)
 /*
  * This function is called when plugin_dispatch_notification () has been used.
  */
-static int my_notify (const notification_t *notif)
+static int my_notify (const notification_t *notif, user_data_t *ud)
 {
        char time_str[32] = "";
        struct tm *tm = NULL;
@@ -210,12 +213,13 @@ static int my_shutdown (void)
  */
 void module_register (void)
 {
-       plugin_register_log ("myplugin", my_log);
-       plugin_register_notification ("myplugin", my_notify);
+       plugin_register_log ("myplugin", my_log, /* user data */ NULL);
+       plugin_register_notification ("myplugin", my_notify,
+                       /* user data */ NULL);
        plugin_register_data_set (&ds);
        plugin_register_read ("myplugin", my_read);
        plugin_register_init ("myplugin", my_init);
-       plugin_register_write ("myplugin", my_write);
+       plugin_register_write ("myplugin", my_write, /* user data */ NULL);
        plugin_register_shutdown ("myplugin", my_shutdown);
     return;
 } /* void module_register (void) */
index b9fefa6..fdfcbaa 100644 (file)
@@ -86,7 +86,7 @@ function error($code, $code_msg, $title, $msg) {
        imagestring($png, 4, ceil(($w-strlen($title)*imagefontwidth(4)) / 2), 10, $title, $c_txt);
        imagestring($png, 5, 60, 35, sprintf('%s [%d]', $code_msg, $code), $c_etxt);
        if (function_exists('imagettfbbox') && is_file($config['error_font'])) {
-               // Detailled error message
+               // Detailed error message
                $fmt_msg = makeTextBlock($msg, $errorfont, 10, $w-86);
                $fmtbox  = imagettfbbox(12, 0, $errorfont, $fmt_msg);
                imagettftext($png, 10, 0, 55, 35+3+imagefontwidth(5)-$fmtbox[7]+$fmtbox[1], $c_txt, $errorfont, $fmt_msg);
index fb44bb4..bee182c 100644 (file)
@@ -33,8 +33,8 @@
 -- and 'values' to store the value-list identifier and the actual values
 -- respectively.
 --
--- The 'values' table is partitioned to improve performance and maintainance.
--- Please note that additional maintainance scripts are required in order to
+-- The 'values' table is partitioned to improve performance and maintenance.
+-- Please note that additional maintenance scripts are required in order to
 -- keep the setup running -- see the comments below for details.
 --
 -- The function 'collectd_insert' may be used to actually insert values
@@ -104,7 +104,7 @@ CREATE OR REPLACE VIEW collectd
                     WHEN type_inst IS NOT NULL THEN '-'
                     ELSE ''
                 END
-                || coalesce(plugin_inst, '') AS identifier,
+                || coalesce(type_inst, '') AS identifier,
             tstamp, name, value
         FROM identifiers
             JOIN values
diff --git a/contrib/redhat/apache.conf b/contrib/redhat/apache.conf
deleted file mode 100644 (file)
index e9c767a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-LoadPlugin apache
-#<Plugin apache>
-#      URL "http://localhost/status?auto"
-#      User "www-user"
-#      Password "secret"
-#      CACert "/etc/ssl/ca.crt"
-#</Plugin>
-
diff --git a/contrib/redhat/collectd.conf b/contrib/redhat/collectd.conf
deleted file mode 100644 (file)
index f8352ff..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-#
-# Config file for collectd(1).
-# Please read collectd.conf(5) for a list of options.
-# http://collectd.org/
-#
-
-#Hostname    "localhost"
-FQDNLookup   true
-BaseDir     "/var/lib/collectd"
-PIDFile     "/var/run/collectd.pid"
-PluginDir   "/usr/lib/collectd"
-TypesDB     "/usr/share/collectd/types.db"
-Interval     10
-ReadThreads  5
-
-LoadPlugin apcups
-#LoadPlugin apple_sensors
-LoadPlugin battery
-LoadPlugin conntrack
-LoadPlugin cpu
-LoadPlugin cpufreq
-LoadPlugin csv
-LoadPlugin df
-LoadPlugin disk
-LoadPlugin dns
-LoadPlugin entropy
-LoadPlugin exec
-LoadPlugin hddtemp
-LoadPlugin interface
-#LoadPlugin iptables
-#LoadPlugin ipvs
-LoadPlugin irq
-#LoadPlugin libvirt
-LoadPlugin load
-LoadPlugin logfile
-LoadPlugin mbmon
-LoadPlugin memcached
-LoadPlugin memory
-LoadPlugin multimeter
-#LoadPlugin netlink
-LoadPlugin network
-LoadPlugin nfs
-LoadPlugin ntpd
-#LoadPlugin nut
-LoadPlugin perl
-LoadPlugin ping
-LoadPlugin processes
-LoadPlugin rrdtool
-LoadPlugin serial
-LoadPlugin swap
-LoadPlugin syslog
-#LoadPlugin tape
-LoadPlugin tcpconns
-LoadPlugin unixsock
-LoadPlugin users
-LoadPlugin uuid
-LoadPlugin vserver
-LoadPlugin wireless
-#LoadPlugin xmms
-
-
-#<Plugin apcups>
-#      Host "localhost"
-#      Port "3551"
-#</Plugin>
-
-#<Plugin csv>
-#      DataDir "/usr/var/lib/collectd/csv"
-#      StoreRates false
-#</Plugin>
-
-#<Plugin df>
-#      Device "/dev/hda1"
-#      Device "192.168.0.2:/mnt/nfs"
-#      MountPoint "/home"
-#      FSType "ext3"
-#      IgnoreSelected false
-#</Plugin>
-
-#<Plugin dns>
-#      Interface "eth0"
-#      IgnoreSource "192.168.0.1"
-#</Plugin>
-
-#<Plugin exec>
-#      Exec "user:group" "/path/to/exec"
-#      NotificationExec "/path/to/exec"
-#</Plugin>
-
-#<Plugin hddtemp>
-#      Host "127.0.0.1"
-#      Port "7634"
-#      TranslateDevicename false
-#</Plugin>
-
-#<Plugin interface>
-#      Interface "eth0"
-#      IgnoreSelected false
-#</Plugin>
-
-#<Plugin iptables>
-#      Chain table chain
-#</Plugin>
-
-#<Plugin irq>
-#      Irq 7
-#      Irq 8
-#      Irq 9
-#      IgnoreSelected true
-#</Plugin>
-
-#<Plugin libvirt>
-#      Connection "xen:///"
-#      RefreshInterval 60
-#      Domain "name"
-#      BlockDevice "name:device"
-#      InterfaceDevice "name:device"
-#      IgnoreSelected false
-#      HostnameFormat name
-#</Plugin>
-
-#<Plugin logfile>
-#      LogLevel info
-#      File STDOUT
-#      Timestamp true
-#</Plugin>
-
-#<Plugin mbmon>
-#      Host "127.0.0.1"
-#      Port "411"
-#</Plugin>
-
-#<Plugin memcached>
-#      Host "127.0.0.1"
-#      Port "11211"
-#</Plugin>
-
-#<Plugin netlink>
-#      Interface "All"
-#      VerboseInterface "All"
-#      QDisc "eth0" "pfifo_fast-1:0"
-#      Class "ppp0" "htb-1:10"
-#      Filter "ppp0" "u32-1:0"
-#      IgnoreSelected false
-#</Plugin>
-
-#<Plugin network>
-#      Server "ff18::efc0:4a42" "25826"
-#      Server "239.192.74.66" "25826"
-#      Listen "ff18::efc0:4a42" "25826"
-#      Listen "239.192.74.66" "25826"
-#      TimeToLive "128"
-#      Forward false
-#      CacheFlush 1800
-#</Plugin>
-
-#<Plugin ntpd>
-#      Host "localhost"
-#      Port 123
-#      ReverseLookups false
-#</Plugin>
-
-#<Plugin nut>
-#      UPS "upsname@hostname:port"
-#</Plugin>
-
-#<Plugin perl>
-#      IncludeDir "/my/include/path"
-#      BaseName "Collectd::Plugin"
-#      EnableDebugger ""
-#      LoadPlugin foo
-#</Plugin>
-
-#<Plugin ping>
-#      Host "host.foo.bar"
-#      TTL 255
-#</Plugin>
-
-#<Plugin processes>
-#      Process "name"
-#</Plugin>
-
-#<Plugin rrdtool>
-#      DataDir "/usr/var/lib/collectd/rrd"
-#      CacheTimeout 120
-#      CacheFlush   900
-#</Plugin>
-
-#<Plugin syslog>
-#      LogLevel info
-#</Plugin>
-
-#<Plugin tcpconns>
-#      ListeningPorts false
-#      LocalPort "25"
-#      RemotePort "25"
-#</Plugin>
-
-#<Plugin unixsock>
-#      SocketFile "/usr/var/run/collectd-unixsock"
-#      SocketGroup "collectd"
-#      SocketPerms "0660"
-#</Plugin>
-
-#<Plugin uuid>
-#      UUIDFile "/etc/uuid"
-#</Plugin>
-
-Include "/etc/collectd.d"
-
index 6eefac9..6af91e8 100644 (file)
-
-%define with_java %(test -z "$JAVA_HOME" ; echo $?)
-
-Summary:       Statistics collection daemon for filling RRD files.
+#
+# q: What is this ?
+# a: A specfile for building RPM packages of current collectd releases, for
+#    RHEL/CentOS versions 5 and 6. By default all the plugins which are
+#    buildable based on the libraries available in the distribution + the
+#    EPEL repository, will be built. Plugins depending on external libs will
+#    be packaged in separate RPMs.
+#
+# q: And how can I do that ?
+# a: By following these instructions, using mock:
+#
+# - install and configure mock (https://fedoraproject.org/wiki/Projects/Mock)
+#
+# - enable the EPEL repository (http://dl.fedoraproject.org/pub/epel/) in the
+#   configuration files for your target systems (/etc/mock/*.cfg).
+#
+# - copy this file in your ~/rpmbuild/SPECS/ directory
+#
+# - fetch the desired collectd release file from https://collectd.org/files/
+#   and save it in your ~/rpmbuild/SOURCES/ directory
+#
+# - build the SRPM first:
+#   mock -r centos-6-x86_64 --buildsrpm --spec ~/rpmbuild/SPECS/collectd.spec \
+#     --sources ~/rpmbuild/SOURCES/
+#
+# - then build the RPMs:
+#   mock -r centos-6-x86_64 --no-clean --rebuild \
+#     /var/lib/mock/centos-6-x86_64/result/collectd-X.Y.Z-NN.src.rpm
+#
+# - you can also optionally enable/disable plugins which are disabled/enabled
+#   by default:
+#   mock -r centos-6-x86_64 --no-clean --without=java --with=oracle --rebuild \
+#     /var/lib/mock/centos-6-x86_64/result/collectd-X.Y.Z-NN.src.rpm
+#
+
+%global _hardened_build 1
+
+# plugins only buildable on RHEL6
+# (NB: %{elN} macro is not available on RHEL < 6)
+%{?el6:%global _has_libyajl 1}
+%{?el6:%global _has_recent_libpcap 1}
+%{?el6:%global _has_recent_sockios_h 1}
+%{?el6:%global _has_recent_libganglia 1}
+%{?el6:%global _has_working_libiptc 1}
+%{?el6:%global _has_ip_vs_h 1}
+%{?el6:%global _has_lvm2app_h 1}
+%{?el6:%global _has_perl_extutils_embed 1}
+
+# plugins enabled by default
+%define with_aggregation 0%{!?_without_aggregation:1}
+%define with_amqp 0%{!?_without_amqp:1}
+%define with_apache 0%{!?_without_apache:1}
+%define with_apcups 0%{!?_without_apcups:1}
+%define with_ascent 0%{!?_without_ascent:1}
+%define with_battery 0%{!?_without_battery:1}
+%define with_bind 0%{!?_without_bind:1}
+%define with_cgroups 0%{!?_without_cgroups:1}
+%define with_conntrack 0%{!?_without_conntrack:1}
+%define with_contextswitch 0%{!?_without_contextswitch:1}
+%define with_cpu 0%{!?_without_cpu:1}
+%define with_cpufreq 0%{!?_without_cpufreq:1}
+%define with_csv 0%{!?_without_csv:1}
+%define with_curl 0%{!?_without_curl:1}
+%define with_curl_json 0%{!?_without_curl_json:0%{?_has_libyajl}}
+%define with_curl_xml 0%{!?_without_curl_xml:1}
+%define with_dbi 0%{!?_without_dbi:1}
+%define with_df 0%{!?_without_df:1}
+%define with_disk 0%{!?_without_disk:1}
+%define with_dns 0%{!?_without_dns:0%{?_has_recent_libpcap}}
+%define with_email 0%{!?_without_email:1}
+%define with_entropy 0%{!?_without_entropy:1}
+%define with_ethstat 0%{!?_without_ethstat:0%{?_has_recent_sockios_h}}
+%define with_exec 0%{!?_without_exec:1}
+%define with_filecount 0%{!?_without_filecount:1}
+%define with_fscache 0%{!?_without_fscache:1}
+%define with_gmond 0%{!?_without_gmond:0%{?_has_recent_libganglia}}
+%define with_hddtemp 0%{!?_without_hddtemp:1}
+%define with_interface 0%{!?_without_interface:1}
+%define with_ipmi 0%{!?_without_ipmi:1}
+%define with_iptables 0%{!?_without_iptables:0%{?_has_working_libiptc}}
+%define with_ipvs 0%{!?_without_ipvs:0%{?_has_ip_vs_h}}
+%define with_irq 0%{!?_without_irq:1}
+%define with_java 0%{!?_without_java:1}
+%define with_libvirt 0%{!?_without_libvirt:1}
+%define with_load 0%{!?_without_load:1}
+%define with_logfile 0%{!?_without_logfile:1}
+%define with_lvm 0%{!?_without_lvm:0%{?_has_lvm2app_h}}
+%define with_madwifi 0%{!?_without_madwifi:1}
+%define with_mbmon 0%{!?_without_mbmon:1}
+%define with_md 0%{!?_without_md:1}
+%define with_memcachec 0%{!?_without_memcachec:1}
+%define with_memcached 0%{!?_without_memcached:1}
+%define with_memory 0%{!?_without_memory:1}
+%define with_multimeter 0%{!?_without_multimeter:1}
+%define with_mysql 0%{!?_without_mysql:1}
+%define with_netlink 0%{!?_without_netlink:1}
+%define with_network 0%{!?_without_network:1}
+%define with_nfs 0%{!?_without_nfs:1}
+%define with_nginx 0%{!?_without_nginx:1}
+%define with_notify_desktop 0%{!?_without_notify_desktop:1}
+%define with_notify_email 0%{!?_without_notify_email:1}
+%define with_ntpd 0%{!?_without_ntpd:1}
+%define with_numa 0%{!?_without_numa:1}
+%define with_nut 0%{!?_without_nut:1}
+%define with_olsrd 0%{!?_without_olsrd:1}
+%define with_openvpn 0%{!?_without_openvpn:1}
+%define with_perl 0%{!?_without_perl:0%{?_has_perl_extutils_embed}}
+%define with_pinba 0%{!?_without_pinba:1}
+%define with_ping 0%{!?_without_ping:1}
+%define with_postgresql 0%{!?_without_postgresql:1}
+%define with_powerdns 0%{!?_without_powerdns:1}
+%define with_processes 0%{!?_without_processes:1}
+%define with_protocols 0%{!?_without_protocols:1}
+%define with_python 0%{!?_without_python:1}
+%define with_rrdtool 0%{!?_without_rrdtool:1}
+%define with_sensors 0%{!?_without_sensors:1}
+%define with_serial 0%{!?_without_serial:1}
+%define with_snmp 0%{!?_without_snmp:1}
+%define with_statsd 0%{!?_without_statsd:1}
+%define with_swap 0%{!?_without_swap:1}
+%define with_syslog 0%{!?_without_syslog:1}
+%define with_table 0%{!?_without_table:1}
+%define with_tail 0%{!?_without_tail:1}
+%define with_tail_csv 0%{!?_without_tail_csv:1}
+%define with_tcpconns 0%{!?_without_tcpconns:1}
+%define with_teamspeak2 0%{!?_without_teamspeak2:1}
+%define with_ted 0%{!?_without_ted:1}
+%define with_thermal 0%{!?_without_thermal:1}
+%define with_threshold 0%{!?_without_threshold:1}
+%define with_unixsock 0%{!?_without_unixsock:1}
+%define with_uptime 0%{!?_without_uptime:1}
+%define with_users 0%{!?_without_users:1}
+%define with_uuid 0%{!?_without_uuid:1}
+%define with_varnish 0%{!?_without_varnish:1}
+%define with_vmem 0%{!?_without_vmem:1}
+%define with_vserver 0%{!?_without_vserver:1}
+%define with_wireless 0%{!?_without_wireless:1}
+%define with_write_graphite 0%{!?_without_write_graphite:1}
+%define with_write_http 0%{!?_without_write_http:1}
+%define with_write_riemann 0%{!?_without_write_riemann:1}
+
+# Plugins not built by default because of dependencies on libraries not
+# available in RHEL or EPEL:
+
+# plugin apple_sensors disabled, requires a Mac
+%define with_apple_sensors 0%{!?_without_apple_sensors:0}
+# plugin aquaero disabled, requires a libaquaero5
+%define with_aquaero 0%{!?_without_aquaero:0}
+# plugin lpar disabled, requires AIX
+%define with_lpar 0%{!?_without_lpar:0}
+# plugin mic disabled, requires Mic
+%define with_mic 0%{!?_without_mic:0}
+# plugin modbus disabled, requires libmodbus
+%define with_modbus 0%{!?_without_modbus:0}
+# plugin netapp disabled, requires libnetapp
+%define with_netapp 0%{!?_without_netapp:0}
+# plugin onewire disabled, requires libowfs
+%define with_onewire 0%{!?_without_onewire:0}
+# plugin oracle disabled, requires Oracle
+%define with_oracle 0%{!?_without_oracle:0}
+# plugin oracle disabled, requires BSD
+%define with_pf 0%{!?_without_pf:0}
+# plugin redis disabled, requires credis
+%define with_redis 0%{!?_without_redis:0}
+# plugin routeros disabled, requires librouteros
+%define with_routeros 0%{!?_without_routeros:0}
+# plugin rrdcached disabled, requires rrdtool >= 1.4
+%define with_rrdcached 0%{!?_without_rrdcached:0}
+# plugin sigrok disabled, requires libsigrok
+%define with_sigrok 0%{!?_without_sigrok:0}
+# plugin tape disabled, requires libkstat
+%define with_tape 0%{!?_without_tape:0}
+# plugin tokyotyrant disabled, requires tcrdb.h
+%define with_tokyotyrant 0%{!?_without_tokyotyrant:0}
+# plugin write_mongodb disabled, requires libmongoc
+%define with_write_mongodb 0%{!?_without_write_mongodb:0}
+# plugin write_redis disabled, requires credis
+%define with_write_redis 0%{!?_without_write_redis:0}
+# plugin xmms disabled, requires xmms
+%define with_xmms 0%{!?_without_xmms:0}
+# plugin zfs_arc disabled, requires FreeBSD/Solaris
+%define with_zfs_arc 0%{!?_without_zfs_arc:0}
+
+Summary:       Statistics collection daemon for filling RRD files
 Name:          collectd
-Version:       5.0.1
+Version:       5.4.0
 Release:       1%{?dist}
-Source:                http://collectd.org/files/%{name}-%{version}.tar.gz
-License:       GPL
+URL:           http://collectd.org
+Source:                http://collectd.org/files/%{name}-%{version}.tar.bz2
+License:       GPLv2
 Group:         System Environment/Daemons
 BuildRoot:     %{_tmppath}/%{name}-%{version}-root
-BuildPrereq:   lm_sensors-devel, rrdtool-devel, libpcap-devel, net-snmp-devel, libstatgrab-devel, libxml2-devel, libiptcdata-devel
-# libcurl deps
-BuildPrereq:   curl-devel,libidn-devel,openssl-devel
-Requires:      rrdtool, perl-Regexp-Common, libstatgrab
-Packager:      RightScale <support@rightscale.com>
+BuildRequires: libgcrypt-devel, kernel-headers
 Vendor:                collectd development team <collectd@verplant.org>
 
+Requires(post):                chkconfig
+Requires(preun):       chkconfig, initscripts
+Requires(postun):      initscripts
+
 %description
 collectd is a small daemon which collects system information periodically and
 provides mechanisms to monitor and store the values in a variety of ways. It
-is written in C for performance. Since the daemon doesn't need to startup
+is written in C for performance. Since the daemon doesn't need to start up
 every time it wants to update the values it's very fast and easy on the
 system. Also, the statistics are very fine grained since the files are updated
-every 10 seconds.
+every 10 seconds by default.
 
+%if %{with_amqp}
+%package amqp
+Summary:       AMQP plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: librabbitmq-devel
+%description amqp
+The AMQP plugin transmits or receives values collected by collectd via the
+Advanced Message Queuing Protocol (AMQP).
+%endif
 
+%if %{with_apache}
 %package apache
-Summary:       apache-plugin for collectd.
+Summary:       Apache plugin for collectd
 Group:         System Environment/Daemons
-Requires:      collectd = %{version}, curl
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: curl-devel
 %description apache
 This plugin collects data provided by Apache's `mod_status'.
+%endif
+
+%if %{with_aquaero}
+%package aquaero
+Summary:       aquaero plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+%description aquaero
+Various sensors in the Aquaero 5 watercooling board made by Aquacomputer.
+%endif
+
+%if %{with_ascent}
+%package ascent
+Summary:       Ascent plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libxml2-devel, curl-devel
+%description ascent
+The Ascent plugin reads and parses the statistics page of Ascent, a free and
+open-source server software for the game World of Warcraft by Blizzard
+Entertainment.
+%endif
+
+%if %{with_bind}
+%package bind
+Summary:       Bind plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libxml2-devel, curl-devel
+%description bind
+The BIND plugin retrieves this information that's encoded in XML and provided
+via HTTP and submits the values to collectd.
+%endif
+
+%if %{with_curl}
+%package curl
+Summary:       Curl plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: curl-devel
+%description curl
+The cURL plugin uses libcurl to read files and then parses them according to
+the configuration.
+%endif
+
+%if %{with_curl_json}
+%package curl_json
+Summary:       Curl_json plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: curl-devel, yajl-devel
+%description curl_json
+The cURL-JSON plugin queries JavaScript Object Notation (JSON) data using the
+cURL library and parses it according to the user's configuration.
+%endif
+
+%if %{with_curl_xml}
+%package curl_xml
+Summary:       Curl_xml plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: curl-devel, libxml2-devel
+%description curl_xml
+The cURL-XML plugin reads files using libcurl and parses it as Extensible
+Markup Language (XML).
+%endif
 
+%if %{with_dbi}
+%package dbi
+Summary:       DBI plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libdbi-devel
+%description dbi
+The DBI plugin uses libdbi, a database abstraction library, to execute SQL
+statements on a database and read back the result.
+%endif
+
+%if %{with_dns}
+%package dns
+Summary:       DNS plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}, libpcap >= 1.0
+BuildRequires: libpcap-devel >= 1.0
+%description dns
+The DNS plugin has a similar functionality to dnstop: It uses libpcap to get a
+copy of all traffic from/to port UDP/53 (that's the DNS port), interprets the
+packets and collects statistics of your DNS traffic.
+%endif
+
+%if %{with_email}
 %package email
-Summary:       email-plugin for collectd.
+Summary:       Email plugin for collectd
 Group:         System Environment/Daemons
-Requires:      collectd = %{version}, spamassassin
+Requires:      %{name}%{?_isa} = %{version}-%{release}, spamassassin
 %description email
 This plugin collects data provided by spamassassin.
+%endif
+
+%if %{with_gmond}
+%package gmond
+Summary:       Gmond plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: ganglia-devel
+%description gmond
+The gmond plugin subscribes to a Multicast group to receive data from gmond,
+the client daemon of the Ganglia project.
+%endif
+
+%if %{with_hddtemp}
+%package hddtemp
+Summary:       Hddtemp plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}, hddtemp
+%description hddtemp
+The HDDTemp plugin collects the temperature of hard disks. The temperatures are
+provided via SMART and queried by the external hddtemp daemon.
+%endif
+
+%if %{with_ipmi}
+%package ipmi
+Summary:       IPMI plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: OpenIPMI-devel
+%description ipmi
+The IPMI plugin uses the OpenIPMI library to read hardware sensors from servers
+using the Intelligent Platform Management Interface (IPMI).
+%endif
+
+%if %{with_iptables}
+%package iptables
+Summary:       IPtables plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: iptables-devel
+%description iptables
+The IPtables plugin can gather statistics from your ip_tables based packet
+filter (aka. firewall) for both the IPv4 and the IPv6 protocol. It can collect
+the byte- and packet-counters of selected rules and submit them to collectd.
+%endif
+
+%if %{with_java}
+%package java
+Summary:       Java plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: java-devel, jpackage-utils
+Requires:      java, jpackage-utils
+%description java
+This plugin for collectd allows plugins to be written in Java and executed
+in an embedded JVM.
+%endif
+
+%if %{with_libvirt}
+%package libvirt
+Summary:       Libvirt plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libvirt-devel
+%description libvirt
+This plugin collects information from virtualized guests.
+%endif
+
+%if %{with_lvm}
+%package lvm
+Summary:       LVM plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: lvm2-devel
+%description lvm
+This plugin collects size of “Logical Volumes” (LV) and “Volume Groups” (VG)
+of Linux' “Logical Volume Manager” (LVM).
+%endif
 
+%if %{with_memcachec}
+%package memcachec
+Summary:       Memcachec plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libmemcached-devel
+%description memcachec
+The Memcachec plugin uses libmemcached to read statistics from a Memcached
+instance. Note that another plugin, named `memcached', exists and does a
+similar job, without requiring the installation of libmemcached.
+%endif
+
+%if %{with_mic}
+%package mic
+Summary:       mic plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+%description mic
+The mic plugin collects CPU usage, memory usage, temperatures and power
+consumption from Intel Many Integrated Core (MIC) CPUs.
+%endif
+
+%if %{with_mysql}
 %package mysql
-Summary:       mysql-module for collectd.
+Summary:       MySQL plugin for collectd
 Group:         System Environment/Daemons
-Requires:      collectd = %{version}, mysql
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: mysql-devel
 %description mysql
-MySQL querying plugin. This plugins provides data of issued commands, called
+MySQL querying plugin. This plugin provides data of issued commands, called
 handlers and database traffic.
+%endif
+
+%if %{with_netlink}
+%package netlink
+Summary:       netlink plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libmnl-devel
+%description netlink
+This plugin collects very detailed Linux network interface and routing
+statistics.
+%endif
 
+%if %{with_nginx}
 %package nginx
-Summary:       nginx-plugin for collectd.
+Summary:       Nginx plugin for collectd
 Group:         System Environment/Daemons
-Requires:      collectd = %{version}, curl
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: curl-devel
 %description nginx
 This plugin gets data provided by nginx.
+%endif
+
+%if %{with_notify_desktop}
+%package notify_desktop
+Summary:       Notify_desktop plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libnotify-devel, gtk2-devel
+%description notify_desktop
+The Notify Desktop plugin uses libnotify to display notifications to the user
+via the desktop notification specification, i. e. on an X display.
+%endif
+
+%if %{with_notify_email}
+%package notify_email
+Summary:       Notify_email plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libesmtp-devel
+%description notify_email
+The Notify Email plugin uses libESMTP to send notifications to a configured
+email address.
+%endif
+
+%if %{with_nut}
+%package nut
+Summary:       Nut plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: nut-devel
+%description nut
+This plugin for collectd provides Network UPS Tools support.
+%endif
+
+%if %{with_perl}
+%package perl
+Summary:       Perl plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+Requires:      perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
+BuildRequires: perl-ExtUtils-Embed
+%description perl
+The Perl plugin embeds a Perl interpreter into collectd and exposes the
+application programming interface (API) to Perl-scripts.
+%endif
+
+%if %{with_pinba}
+%package pinba
+Summary:       Pinba plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: protobuf-c-devel
+%description pinba
+The Pinba plugin receives and dispatches timing values from Pinba, a profiling
+extension for PHP.
+%endif
+
+%if %{with_ping}
+%package ping
+Summary:       Ping plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: liboping-devel
+%description ping
+The Ping plugin measures network latency using ICMP “echo requests”, usually
+known as “ping”.
+%endif
+
+%if %{with_postgresql}
+%package postgresql
+Summary:       PostgreSQL plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: postgresql-devel
+%description postgresql
+The PostgreSQL plugin connects to and executes SQL statements on a PostgreSQL
+database.
+%endif
+
+%if %{with_python}
+%package python
+Summary:       Python plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+%if 0%{?rhel} >= 6
+BuildRequires: python-devel
+%else
+BuildRequires: python26-devel
+%endif
+%description python
+The Python plugin embeds a Python interpreter into collectd and exposes the
+application programming interface (API) to Python-scripts.
+%endif
+
+%if %{with_redis}
+%package redis
+Summary:       Redis plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: credis-devel
+%description redis
+The Redis plugin connects to one or more instances of Redis, a key-value store,
+and collects usage information using the credis library.
+%endif
+
+%if %{with_rrdcached}
+%package rrdcached
+Summary:        RRDCached plugin for collectd
+Group:          System Environment/Daemons
+Requires:       %{name}%{?_isa} = %{version}-%{release}, rrdtool >= 1.4
+BuildRequires:  rrdtool-devel
+%description rrdcached
+The RRDCacheD plugin connects to the “RRD caching daemon”, rrdcached and
+submits updates for RRD files to that daemon.
+%endif
+
+%if %{with_rrdtool}
+%package rrdtool
+Summary:       RRDtool plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: rrdtool-devel
+%description rrdtool
+The RRDtool plugin writes values to RRD-files using librrd.
+%endif
 
+%if %{with_sensors}
 %package sensors
-Summary:       libsensors-module for collectd.
+Summary:       Sensors plugin for collectd
 Group:         System Environment/Daemons
-Requires:      collectd = %{version}, lm_sensors
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: lm_sensors-devel
 %description sensors
 This plugin for collectd provides querying of sensors supported by lm_sensors.
+%endif
+
+%if %{with_sigrok}
+%package sigrok
+Summary:       sigrok plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+%description sigrok
+Uses libsigrok as a backend, allowing any sigrok-supported device to have its
+measurements fed to collectd. This includes multimeters, sound level meters,
+thermometers, and much more.
+%endif
 
+%if %{with_snmp}
 %package snmp
-Summary:       snmp-module for collectd.
+Summary:       SNMP plugin for collectd
 Group:         System Environment/Daemons
-Requires:      collectd = %{version}, net-snmp
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: net-snmp-devel
 %description snmp
 This plugin for collectd allows querying of network equipment using SNMP.
+%endif
 
-%if %with_java
-%package java
-Summary:       java-module for collectd.
+%if %{with_varnish}
+%package varnish
+Summary:       Varnish plugin for collectd
 Group:         System Environment/Daemons
-Requires:      collectd = %{version}, jdk >= 1.6
-BuildPrereq:   jdk >= 1.6
-%description java
-This plugin for collectd allows plugins to be written in Java and executed
-in an embedded JVM.
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: varnish-libs-devel
+%description varnish
+The Varnish plugin collects information about Varnish, an HTTP accelerator.
+%endif
+
+%if %{with_write_http}
+%package write_http
+Summary:       Write-HTTP plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: curl-devel
+%description write_http
+The Write-HTTP plugin sends the values collected by collectd to a web-server
+using HTTP POST requests.
 %endif
 
+%if %{with_write_redis}
+%package write_redis
+Summary:       Write-Redis plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: credis-devel
+%description write_redis
+The Write Redis plugin stores values in Redis, a “data structures server”.
+%endif
+
+%if %{with_write_riemann}
+%package write_riemann
+Summary:       riemann plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: protobuf-c-devel
+%description write_riemann
+The riemann plugin submits values to Riemann, an event stream processor.
+%endif
+
+%package collection3
+Summary:       Web-based viewer for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+Requires: httpd
+%description collection3
+collection3 is a graphing front-end for the RRD files created by and filled
+with collectd. It is written in Perl and should be run as an CGI-script.
+Graphs are generated on-the-fly, so no cron job or similar is necessary.
+
+%package php-collection
+Summary:       collect php webfrontent
+Group:         System Environment/Daemons
+Requires:      collectd = %{version}-%{release}
+Requires:      httpd
+Requires:      php
+Requires:      php-rrdtool
+%description php-collection
+PHP graphing frontend for RRD files created by and filled with collectd.
+
+%package contrib
+Summary:       Contrib files for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+%description contrib
+All the files found under contrib/ in the source tree are bundled in this
+package.
+
+%package -n libcollectdclient
+Summary:       Collectd client library
+Group:         System Environment/Daemons
+%description -n libcollectdclient
+Collectd client library
+
+%package -n libcollectdclient-devel
+Summary:       Development files for libcollectdclient
+Group:         System Environment/Daemons
+Requires:      pkgconfig
+Requires:      libcollectdclient%{?_isa} = %{version}-%{release}
+%description -n libcollectdclient-devel
+Development files for libcollectdclient
+
+
 %prep
-rm -rf $RPM_BUILD_ROOT
-%setup
+%setup -q
 
 %build
-./configure CFLAGS=-"DLT_LAZY_OR_NOW='RTLD_LAZY|RTLD_GLOBAL'" --prefix=%{_prefix} --sbindir=%{_sbindir} --mandir=%{_mandir} --libdir=%{_libdir} --sysconfdir=%{_sysconfdir} \
-    %{!?with_java:"--with-java=$JAVA_HOME --enable-java"} \
-    --disable-battery
-make
+%if %{with_aggregation}
+%define _with_aggregation --enable-aggregation
+%else
+%define _with_aggregation --disable-aggregation
+%endif
 
-%install
-make install DESTDIR=$RPM_BUILD_ROOT
-mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
-mkdir -p $RPM_BUILD_ROOT/var/www/cgi-bin
-cp contrib/redhat/init.d-collectd $RPM_BUILD_ROOT/etc/rc.d/init.d/collectd
-cp contrib/collection.cgi $RPM_BUILD_ROOT/var/www/cgi-bin
-mkdir -p $RPM_BUILD_ROOT/etc/collectd.d
-mkdir -p $RPM_BUILD_ROOT/var/lib/collectd
-### Clean up docs
-find contrib/ -type f -exec %{__chmod} a-x {} \;
+%if %{with_amqp}
+%define _with_amqp --enable-amqp
+%else
+%define _with_amqp --disable-amqp
+%endif
 
-###Modify Config for Redhat Based Distros
-sed -i 's:#BaseDir     "/usr/var/lib/collectd":BaseDir     "/var/lib/collectd":' $RPM_BUILD_ROOT/etc/collectd.conf
-sed -i 's:#PIDFile     "/usr/var/run/collectd.pid":PIDFile     "/var/run/collectd.pid":' $RPM_BUILD_ROOT/etc/collectd.conf
-sed -i 's:#PluginDir   "/usr/lib/collectd":PluginDir   "%{_libdir}/collectd":' $RPM_BUILD_ROOT/etc/collectd.conf
-sed -i 's:#TypesDB     "/usr/share/collectd/types.db":TypesDB     "/usr/share/collectd/types.db":' $RPM_BUILD_ROOT/etc/collectd.conf
-sed -i 's:#Interval     10:Interval     30:' $RPM_BUILD_ROOT/etc/collectd.conf
-sed -i 's:#ReadThreads  5:ReadThreads  5:' $RPM_BUILD_ROOT/etc/collectd.conf
-###Include broken out config directory
-echo -e '\nInclude "/etc/collectd.d"' >> $RPM_BUILD_ROOT/etc/collectd.conf
-
-##Move config contribs
-cp contrib/redhat/apache.conf $RPM_BUILD_ROOT/etc/collectd.d/apache.conf
-cp contrib/redhat/email.conf $RPM_BUILD_ROOT/etc/collectd.d/email.conf
-cp contrib/redhat/sensors.conf $RPM_BUILD_ROOT/etc/collectd.d/sensors.conf
-cp contrib/redhat/mysql.conf $RPM_BUILD_ROOT/etc/collectd.d/mysql.conf
-cp contrib/redhat/nginx.conf $RPM_BUILD_ROOT/etc/collectd.d/nginx.conf
-cp contrib/redhat/snmp.conf $RPM_BUILD_ROOT/etc/collectd.d/snmp.conf
+%if %{with_apache}
+%define _with_apache --enable-apache
+%else
+%define _with_apache --disable-apache
+%endif
 
-%clean
-rm -rf $RPM_BUILD_ROOT
+%if %{with_apcups}
+%define _with_apcups --enable-apcups
+%else
+%define _with_apcups --disable-apcups
+%endif
 
-%post
-/sbin/chkconfig --add collectd
-/sbin/chkconfig collectd on
+%if %{with_apple_sensors}
+%define _with_apple_sensors --enable-apple_sensors
+%else
+%define _with_apple_sensors --disable-apple_sensors
+%endif
 
-%preun
-if [ "$1" = 0 ]; then
-   /sbin/chkconfig collectd off
-   /etc/init.d/collectd stop
-   /sbin/chkconfig --del collectd
-fi
-exit 0
+%if %{with_aquaero}
+%define _with_aquaero --enable-aquaero
+%else
+%define _with_aquaero --disable-aquaero
+%endif
 
-%postun
-if [ "$1" -ge 1 ]; then
-    /etc/init.d/collectd restart
-fi
-exit 0
+%if %{with_ascent}
+%define _with_ascent --enable-ascent
+%else
+%define _with_ascent --disable-ascent
+%endif
 
-%files
-%defattr(-,root,root)
-%doc AUTHORS COPYING ChangeLog INSTALL NEWS README contrib/
-%config %attr(0644,root,root) /etc/collectd.conf
-%attr(0755,root,root) /etc/rc.d/init.d/collectd
-%attr(0755,root,root) /var/www/cgi-bin/collection.cgi
-%attr(0755,root,root) %{_sbindir}/collectd
-%attr(0755,root,root) %{_bindir}/collectd-nagios
-%attr(0755,root,root) %{_bindir}/collectdctl
-%attr(0755,root,root) %{_sbindir}/collectdmon
-%attr(0644,root,root) %{_mandir}/man1/*
-%attr(0644,root,root) %{_mandir}/man5/*
-%dir /etc/collectd.d
-
-# client
-%attr(0644,root,root) /usr/include/collectd/client.h
-%attr(0644,root,root) /usr/include/collectd/lcc_features.h
-
-%attr(0644,root,root) %{_libdir}/libcollectdclient.*
-%attr(0644,root,root) %{_libdir}/pkgconfig/libcollectdclient.pc
-
-# macro to grab binaries for a plugin, given a name
-%define plugin_macro() \
-%attr(0644,root,root) %{_libdir}/%{name}/%1.a \
-%attr(0644,root,root) %{_libdir}/%{name}/%1.so* \
-%attr(0644,root,root) %{_libdir}/%{name}/%1.la
-
-%plugin_macro apcups
-%plugin_macro ascent
-%plugin_macro bind
-%plugin_macro conntrack
-%plugin_macro contextswitch
-%plugin_macro cpufreq
-%plugin_macro cpu
-%plugin_macro csv
-%plugin_macro curl
-%plugin_macro curl_xml
-%plugin_macro df
-%plugin_macro disk
-%plugin_macro dns
-%plugin_macro entropy
-%plugin_macro email
-%plugin_macro exec
-%plugin_macro filecount
-%plugin_macro fscache
-%plugin_macro hddtemp
-%plugin_macro interface
-%plugin_macro iptables
-%plugin_macro irq
-%plugin_macro load
-%plugin_macro logfile
-%plugin_macro madwifi
-
-%plugin_macro match_empty_counter
-%plugin_macro match_hashed
-%plugin_macro match_regex
-%plugin_macro match_timediff
-%plugin_macro match_value
-
-%plugin_macro mbmon
-%plugin_macro memcachec
-%plugin_macro memcached
-%plugin_macro memory
-%plugin_macro multimeter
-%plugin_macro network
-%plugin_macro nfs
-%plugin_macro ntpd
-%plugin_macro openvpn
-%plugin_macro olsrd
-%plugin_macro perl
-%plugin_macro powerdns
-%plugin_macro processes
-%plugin_macro protocols
-%plugin_macro python
-%plugin_macro rrdtool
-%plugin_macro serial
-%plugin_macro sensors
-%plugin_macro swap
-%plugin_macro syslog
-%plugin_macro table
-%plugin_macro tail
-
-%plugin_macro target_notification
-%plugin_macro target_replace
-%plugin_macro target_scale
-%plugin_macro target_set
-%plugin_macro target_v5upgrade
-
-%plugin_macro tcpconns
-%plugin_macro teamspeak2
-%plugin_macro ted
-%plugin_macro thermal
-%plugin_macro threshold
-
-%plugin_macro unixsock
-%plugin_macro uptime
-%plugin_macro users
-%plugin_macro uuid
-%plugin_macro vmem
-%plugin_macro vserver
-%plugin_macro wireless
-%plugin_macro write_http
-
-%attr(0644,root,root) %{_datadir}/%{name}/types.db
-
-%exclude %{_libdir}/perl5/5.8.8/%{_arch}-linux-thread-multi/perllocal.pod
-%attr(0644,root,root) %{_libdir}/perl5/site_perl/5.8.8/%{_arch}-linux-thread-multi/auto/Collectd/.packlist
-%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd.pm
-%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd/Unixsock.pm
-%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd/Plugins/OpenVZ.pm
-%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd/Plugins/Monitorus.pm
-%attr(0644,root,root) /usr/share/man/man3/Collectd::Unixsock.3pm.gz
-
-%exclude /usr/share/collectd/postgresql_default.conf
-
-%dir /var/lib/collectd
-
-%if %with_java
-%files java
-/usr/share/collectd/java/collectd-api.jar
-/usr/share/collectd/java/generic-jmx.jar
-%plugin_macro java
+%if %{with_battery}
+%define _with_battery --enable-battery
+%else
+%define _with_battery --disable-battery
 %endif
 
-%files apache
-%config %attr(0644,root,root) /etc/collectd.d/apache.conf
-%plugin_macro apache
+%if %{with_bind}
+%define _with_bind --enable-bind
+%else
+%define _with_bind --disable-bind
+%endif
 
-%files email
-%attr(0644,root,root) %{_libdir}/%{name}/email.so*
-%attr(0644,root,root) %{_libdir}/%{name}/email.la
-%config %attr(0644,root,root) /etc/collectd.d/email.conf
+%if %{with_cgroups}
+%define _with_cgroups --enable-cgroups
+%else
+%define _with_cgroups --disable-cgroups
+%endif
 
-%files mysql
-%config %attr(0644,root,root) /etc/collectd.d/mysql.conf
-%plugin_macro mysql
+%if %{with_conntrack}
+%define _with_conntrack --enable-conntrack
+%else
+%define _with_conntrack --disable-conntrack
+%endif
 
-%files nginx
-%config %attr(0644,root,root) /etc/collectd.d/nginx.conf
-%plugin_macro nginx
+%if %{with_contextswitch}
+%define _with_contextswitch --enable-contextswitch
+%else
+%define _with_contextswitch --disable-contextswitch
+%endif
 
-%files sensors
-%attr(0644,root,root) %{_libdir}/%{name}/sensors.so*
-%attr(0644,root,root) %{_libdir}/%{name}/sensors.la
-%config %attr(0644,root,root) /etc/collectd.d/sensors.conf
+%if %{with_cpu}
+%define _with_cpu --enable-cpu
+%else
+%define _with_cpu --disable-cpu
+%endif
 
-%files snmp
-%attr(0644,root,root) /etc/collectd.d/snmp.conf
-%plugin_macro snmp
+%if %{with_cpufreq}
+%define _with_cpufreq --enable-cpufreq
+%else
+%define _with_cpufreq --disable-cpufreq
+%endif
+
+%if %{with_csv}
+%define _with_csv --enable-csv
+%else
+%define _with_csv --disable-csv
+%endif
+
+%if %{with_curl}
+%define _with_curl --enable-curl
+%else
+%define _with_curl --disable-curl
+%endif
+
+%if %{with_curl_json}
+%define _with_curl_json --enable-curl_json
+%else
+%define _with_curl_json --disable-curl_json
+%endif
+
+%if %{with_curl_xml}
+%define _with_curl_xml --enable-curl_xml
+%else
+%define _with_curl_xml --disable-curl_xml
+%endif
+
+%if %{with_dbi}
+%define _with_dbi --enable-dbi
+%else
+%define _with_dbi --disable-dbi --without-libdbi
+%endif
+
+%if %{with_df}
+%define _with_df --enable-df
+%else
+%define _with_df --disable-df
+%endif
+
+%if %{with_disk}
+%define _with_disk --enable-disk
+%else
+%define _with_disk --disable-disk
+%endif
+
+%if %{with_dns}
+%define _with_dns --enable-dns
+%else
+%define _with_dns --disable-dns
+%endif
+
+%if %{with_email}
+%define _with_email --enable-email
+%else
+%define _with_email --disable-email
+%endif
+
+%if %{with_entropy}
+%define _with_entropy --enable-entropy
+%else
+%define _with_entropy --disable-entropy
+%endif
+
+%if %{with_ethstat}
+%define _with_ethstat --enable-ethstat
+%else
+%define _with_ethstat --disable-ethstat
+%endif
+
+%if %{with_exec}
+%define _with_exec --enable-exec
+%else
+%define _with_exec --disable-exec
+%endif
+
+%if %{with_filecount}
+%define _with_filecount --enable-filecount
+%else
+%define _with_filecount --disable-filecount
+%endif
+
+%if %{with_fscache}
+%define _with_fscache --enable-fscache
+%else
+%define _with_fscache --disable-fscache
+%endif
+
+%if %{with_gmond}
+%define _with_gmond --enable-gmond
+%else
+%define _with_gmond --disable-gmond
+%endif
+
+%if %{with_hddtemp}
+%define _with_hddtemp --enable-hddtemp
+%else
+%define _with_hddtemp --disable-hddtemp
+%endif
+
+%if %{with_interface}
+%define _with_interface --enable-interface
+%else
+%define _with_interface --disable-interface
+%endif
+
+%if %{with_ipmi}
+%define _with_ipmi --enable-ipmi
+%else
+%define _with_ipmi --disable-ipmi
+%endif
+
+%if %{with_iptables}
+%define _with_iptables --enable-iptables
+%else
+%define _with_iptables --disable-iptables
+%endif
+
+%if %{with_ipvs}
+%define _with_ipvs --enable-ipvs
+%else
+%define _with_ipvs --disable-ipvs
+%endif
+
+%if %{with_irq}
+%define _with_irq --enable-irq
+%else
+%define _with_irq --disable-irq
+%endif
+
+%if %{with_java}
+%define _with_java --enable-java --with-java=%{java_home}/
+%else
+%define _with_java --disable-java
+%endif
+
+%if %{with_libvirt}
+%define _with_libvirt --enable-libvirt
+%else
+%define _with_libvirt --disable-libvirt
+%endif
+
+%if %{with_load}
+%define _with_load --enable-load
+%else
+%define _with_load --disable-load
+%endif
+
+%if %{with_logfile}
+%define _with_logfile --enable-logfile
+%else
+%define _with_logfile --disable-logfile
+%endif
+
+%if %{with_lpar}
+%define _with_lpar --enable-lpar
+%else
+%define _with_lpar --disable-lpar
+%endif
+
+%if %{with_lvm}
+%define _with_lvm --enable-lvm
+%else
+%define _with_lvm --disable-lvm
+%endif
+
+%if %{with_madwifi}
+%define _with_madwifi --enable-madwifi
+%else
+%define _with_madwifi --disable-madwifi
+%endif
+
+%if %{with_mbmon}
+%define _with_mbmon --enable-mbmon
+%else
+%define _with_mbmon --disable-mbmon
+%endif
+
+%if %{with_md}
+%define _with_md --enable-md
+%else
+%define _with_md --disable-md
+%endif
+
+%if %{with_memcachec}
+%define _with_memcachec --enable-memcachec
+%else
+%define _with_memcachec --disable-memcachec
+%endif
+
+%if %{with_memcached}
+%define _with_memcached --enable-memcached
+%else
+%define _with_memcached --disable-memcached
+%endif
+
+%if %{with_memory}
+%define _with_memory --enable-memory
+%else
+%define _with_memory --disable-memory
+%endif
+
+%if %{with_mic}
+%define _with_mic --enable-mic
+%else
+%define _with_mic --disable-mic
+%endif
+
+%if %{with_modbus}
+%define _with_modbus --enable-modbus
+%else
+%define _with_modbus --disable-modbus
+%endif
+
+%if %{with_multimeter}
+%define _with_multimeter --enable-multimeter
+%else
+%define _with_multimeter --disable-multimeter
+%endif
+
+%if %{with_mysql}
+%define _with_mysql --enable-mysql
+%else
+%define _with_mysql --disable-mysql
+%endif
+
+%if %{with_netapp}
+%define _with_netapp --enable-netapp
+%else
+%define _with_netapp --disable-netapp
+%endif
+
+%if %{with_netlink}
+%define _with_netlink --enable-netlink
+%else
+%define _with_netlink --disable-netlink
+%endif
+
+%if %{with_network}
+%define _with_network --enable-network
+%else
+%define _with_network --disable-network
+%endif
+
+%if %{with_nfs}
+%define _with_nfs --enable-nfs
+%else
+%define _with_nfs --disable-nfs
+%endif
+
+%if %{with_nginx}
+%define _with_nginx --enable-nginx
+%else
+%define _with_nginx --disable-nginx
+%endif
+
+%if %{with_notify_desktop}
+%define _with_notify_desktop --enable-notify_desktop
+%else
+%define _with_notify_desktop --disable-notify_desktop
+%endif
+
+%if %{with_notify_email}
+%define _with_notify_email --enable-notify_email
+%else
+%define _with_notify_email --disable-notify_email --without-libesmpt
+%endif
+
+%if %{with_ntpd}
+%define _with_ntpd --enable-ntpd
+%else
+%define _with_ntpd --disable-ntpd
+%endif
+
+%if %{with_numa}
+%define _with_numa --enable-numa
+%else
+%define _with_numa --disable-numa
+%endif
+
+%if %{with_nut}
+%define _with_nut --enable-nut
+%else
+%define _with_nut --disable-nut
+%endif
+
+%if %{with_olsrd}
+%define _with_olsrd --enable-olsrd
+%else
+%define _with_olsrd --disable-olsrd
+%endif
+
+%if %{with_onewire}
+%define _with_onewire --enable-onewire
+%else
+%define _with_onewire --disable-onewire
+%endif
+
+%if %{with_openvpn}
+%define _with_openvpn --enable-openvpn
+%else
+%define _with_openvpn --disable-openvpn
+%endif
+
+%if %{with_oracle}
+%define _with_oracle --enable-oracle
+%else
+%define _with_oracle --disable-oracle
+%endif
+
+%if %{with_perl}
+%define _with_perl --enable-perl --with-perl-bindings="INSTALLDIRS=vendor"
+%else
+%define _with_perl --disable-perl --without-libperl
+%endif
+
+%if %{with_pf}
+%define _with_pf --enable-pf
+%else
+%define _with_pf --disable-pf
+%endif
+
+%if %{with_pinba}
+%define _with_pinba --enable-pinba
+%else
+%define _with_pinba --disable-pinba
+%endif
+
+%if %{with_ping}
+%define _with_ping --enable-ping
+%else
+%define _with_ping --disable-ping
+%endif
+
+%if %{with_postgresql}
+%define _with_postgresql --enable-postgresql
+%else
+%define _with_postgresql --disable-postgresql
+%endif
+
+%if %{with_powerdns}
+%define _with_powerdns --enable-powerdns
+%else
+%define _with_powerdns --disable-powerdns
+%endif
+
+%if %{with_processes}
+%define _with_processes --enable-processes
+%else
+%define _with_processes --disable-processes
+%endif
+
+%if %{with_protocols}
+%define _with_protocols --enable-protocols
+%else
+%define _with_protocols --disable-protocols
+%endif
+
+%if %{with_python}
+%if 0%{?rhel} >= 6
+%define _with_python --enable-python
+%else
+%define _with_python --enable-python --with-python=%{_bindir}/python2.6
+%endif
+%else
+%define _with_python --disable-python
+%endif
+
+%if %{with_redis}
+%define _with_redis --enable-redis
+%else
+%define _with_redis --disable-redis
+%endif
+
+%if %{with_routeros}
+%define _with_routeros --enable-routeros
+%else
+%define _with_routeros --disable-routeros
+%endif
+
+%if %{with_rrdcached}
+%define _with_rrdcached --enable-rrdcached
+%else
+%define _with_rrdcached --disable-rrdcached
+%endif
+
+%if %{with_rrdtool}
+%define _with_rrdtool --enable-rrdtool
+%else
+%define _with_rrdtool --disable-rrdtool
+%endif
+
+%if %{with_sensors}
+%define _with_sensors --enable-sensors
+%else
+%define _with_sensors --disable-sensors
+%endif
+
+%if %{with_serial}
+%define _with_serial --enable-serial
+%else
+%define _with_serial --disable-serial
+%endif
+
+%if %{with_sigrok}
+%define _with_sigrok --enable-sigrok
+%else
+%define _with_sigrok --disable-sigrok
+%endif
+
+%if %{with_snmp}
+%define _with_snmp --enable-snmp
+%else
+%define _with_snmp --disable-snmp
+%endif
+
+%if %{with_statsd}
+%define _with_statsd --enable-statsd
+%else
+%define _with_statsd --disable-statsd
+%endif
+
+%if %{with_swap}
+%define _with_swap --enable-swap
+%else
+%define _with_swap --disable-swap
+%endif
+
+%if %{with_syslog}
+%define _with_syslog --enable-syslog
+%else
+%define _with_syslog --disable-syslog
+%endif
+
+%if %{with_table}
+%define _with_table --enable-table
+%else
+%define _with_table --disable-table
+%endif
+
+%if %{with_tail}
+%define _with_tail --enable-tail
+%else
+%define _with_tail --disable-tail
+%endif
+
+%if %{with_tail_csv}
+%define _with_tail_csv --enable-tail_csv
+%else
+%define _with_tail_csv --disable-tail_csv
+%endif
+
+%if %{with_tape}
+%define _with_tape --enable-tape
+%else
+%define _with_tape --disable-tape
+%endif
+
+%if %{with_tcpconns}
+%define _with_tcpconns --enable-tcpconns
+%else
+%define _with_tcpconns --disable-tcpconns
+%endif
+
+%if %{with_teamspeak2}
+%define _with_teamspeak2 --enable-teamspeak2
+%else
+%define _with_teamspeak2 --disable-teamspeak2
+%endif
+
+%if %{with_ted}
+%define _with_ted --enable-ted
+%else
+%define _with_ted --disable-ted
+%endif
+
+%if %{with_thermal}
+%define _with_thermal --enable-thermal
+%else
+%define _with_thermal --disable-thermal
+%endif
+
+%if %{with_threshold}
+%define _with_threshold --enable-threshold
+%else
+%define _with_threshold --disable-threshold
+%endif
+
+%if %{with_tokyotyrant}
+%define _with_tokyotyrant --enable-tokyotyrant
+%else
+%define _with_tokyotyrant --disable-tokyotyrant
+%endif
+
+%if %{with_unixsock}
+%define _with_unixsock --enable-unixsock
+%else
+%define _with_unixsock --disable-unixsock
+%endif
+
+%if %{with_uptime}
+%define _with_uptime --enable-uptime
+%else
+%define _with_uptime --disable-uptime
+%endif
+
+%if %{with_users}
+%define _with_users --enable-users
+%else
+%define _with_users --disable-users
+%endif
+
+%if %{with_uuid}
+%define _with_uuid --enable-uuid
+%else
+%define _with_uuid --disable-uuid
+%endif
+
+%if %{with_varnish}
+%define _with_varnish --enable-varnish
+%else
+%define _with_varnish --disable-varnish
+%endif
+
+%if %{with_vmem}
+%define _with_vmem --enable-vmem
+%else
+%define _with_vmem --disable-vmem
+%endif
+
+%if %{with_vserver}
+%define _with_vserver --enable-vserver
+%else
+%define _with_vserver --disable-vserver
+%endif
+
+%if %{with_wireless}
+%define _with_wireless --enable-wireless
+%else
+%define _with_wireless --disable-wireless
+%endif
+
+%if %{with_write_graphite}
+%define _with_write_graphite --enable-write_graphite
+%else
+%define _with_write_graphite --disable-write_graphite
+%endif
+
+%if %{with_write_http}
+%define _with_write_http --enable-write_http
+%else
+%define _with_write_http --disable-write_http
+%endif
+
+%if %{with_write_mongodb}
+%define _with_write_mongodb --enable-write_mongodb
+%else
+%define _with_write_mongodb --disable-write_mongodb --without-libmongoc
+%endif
+
+%if %{with_write_redis}
+%define _with_write_redis --enable-write_redis
+%else
+%define _with_write_redis --disable-write_redis --without-libcredis
+%endif
+
+%if %{with_write_riemann}
+%define _with_write_riemann --enable-write_riemann
+%else
+%define _with_write_riemann --disable-write_riemann
+%endif
+
+%if %{with_xmms}
+%define _with_xmms --enable-xmms
+%else
+%define _with_xmms --disable-xmms
+%endif
+
+%if %{with_zfs_arc}
+%define _with_zfs_arc --enable-zfs_arc
+%else
+%define _with_zfs_arc --disable-zfs_arc
+%endif
+
+%configure CFLAGS="%{optflags} -DLT_LAZY_OR_NOW=\"RTLD_LAZY|RTLD_GLOBAL\"" \
+       --disable-static \
+       --without-included-ltdl \
+       --enable-all-plugins=yes \
+       --enable-match_empty_counter \
+       --enable-match_hashed \
+       --enable-match_regex \
+       --enable-match_timediff \
+       --enable-match_value \
+       --enable-target_notification \
+       --enable-target_replace \
+       --enable-target_scale \
+       --enable-target_set \
+       --enable-target_v5upgrade \
+       %{?_with_aggregation} \
+       %{?_with_amqp} \
+       %{?_with_apache} \
+       %{?_with_apcups} \
+       %{?_with_apple_sensors} \
+       %{?_with_aquaero} \
+       %{?_with_ascent} \
+       %{?_with_battery} \
+       %{?_with_bind} \
+       %{?_with_cgroups} \
+       %{?_with_conntrack} \
+       %{?_with_contextswitch} \
+       %{?_with_cpu} \
+       %{?_with_cpufreq} \
+       %{?_with_csv} \
+       %{?_with_curl} \
+       %{?_with_curl_json} \
+       %{?_with_curl_xml} \
+       %{?_with_dbi} \
+       %{?_with_df} \
+       %{?_with_disk} \
+       %{?_with_dns} \
+       %{?_with_email} \
+       %{?_with_entropy} \
+       %{?_with_ethstat} \
+       %{?_with_exec} \
+       %{?_with_filecount} \
+       %{?_with_fscache} \
+       %{?_with_gmond} \
+       %{?_with_hddtemp} \
+       %{?_with_interface} \
+       %{?_with_ipmi} \
+       %{?_with_iptables} \
+       %{?_with_ipvs} \
+       %{?_with_java} \
+       %{?_with_libvirt} \
+       %{?_with_lpar} \
+       %{?_with_lvm} \
+       %{?_with_memcachec} \
+       %{?_with_mic} \
+       %{?_with_modbus} \
+       %{?_with_multimeter} \
+       %{?_with_mysql} \
+       %{?_with_netapp} \
+       %{?_with_netlink} \
+       %{?_with_nginx} \
+       %{?_with_notify_desktop} \
+       %{?_with_notify_email} \
+       %{?_with_nut} \
+       %{?_with_onewire} \
+       %{?_with_oracle} \
+       %{?_with_perl} \
+       %{?_with_pf} \
+       %{?_with_pinba} \
+       %{?_with_ping} \
+       %{?_with_postgresql} \
+       %{?_with_python} \
+       %{?_with_redis} \
+       %{?_with_routeros} \
+       %{?_with_rrdcached} \
+       %{?_with_rrdtool} \
+       %{?_with_sensors} \
+       %{?_with_sigrok} \
+       %{?_with_snmp} \
+       %{?_with_tape} \
+       %{?_with_tokyotyrant} \
+       %{?_with_varnish} \
+       %{?_with_write_http} \
+       %{?_with_write_mongodb} \
+       %{?_with_write_redis} \
+       %{?_with_xmms} \
+       %{?_with_zfs_arc} \
+       %{?_with_irq} \
+       %{?_with_load} \
+       %{?_with_logfile} \
+       %{?_with_madwifi} \
+       %{?_with_mbmon} \
+       %{?_with_md} \
+       %{?_with_memcached} \
+       %{?_with_memory} \
+       %{?_with_network} \
+       %{?_with_nfs} \
+       %{?_with_ntpd} \
+       %{?_with_numa} \
+       %{?_with_olsrd} \
+       %{?_with_openvpn} \
+       %{?_with_powerdns} \
+       %{?_with_processes} \
+       %{?_with_protocols} \
+       %{?_with_serial} \
+       %{?_with_statsd} \
+       %{?_with_swap} \
+       %{?_with_syslog} \
+       %{?_with_table} \
+       %{?_with_tail} \
+       %{?_with_tail_csv} \
+       %{?_with_tcpconns} \
+       %{?_with_teamspeak2} \
+       %{?_with_ted} \
+       %{?_with_thermal} \
+       %{?_with_threshold} \
+       %{?_with_unixsock} \
+       %{?_with_uptime} \
+       %{?_with_users} \
+       %{?_with_uuid} \
+       %{?_with_vmem} \
+       %{?_with_vserver} \
+       %{?_with_wireless}\
+       %{?_with_write_graphite} \
+       %{?_with_write_http} \
+       %{?_with_write_riemann}
+
+
+%{__make} %{?_smp_mflags}
+
+
+%install
+rm -rf %{buildroot}
+%{__make} install DESTDIR=%{buildroot}
+%{__install} -Dp -m 0755 contrib/redhat/init.d-collectd %{buildroot}%{_initrddir}/collectd
+%{__install} -Dp -m0644 src/collectd.conf %{buildroot}%{_sysconfdir}/collectd.conf
+%{__install} -d %{buildroot}%{_sharedstatedir}/collectd/
+%{__install} -d %{buildroot}%{_sysconfdir}/collectd.d/
+
+%{__mkdir} -p %{buildroot}%{_localstatedir}/www
+%{__mkdir} -p %{buildroot}/%{_sysconfdir}/httpd/conf.d
+
+%{__cp} -a contrib/collection3 %{buildroot}%{_localstatedir}/www
+%{__cp} -a contrib/redhat/collection3.conf %{buildroot}/%{_sysconfdir}/httpd/conf.d/
+
+%{__cp} -a contrib/php-collection %{buildroot}%{_localstatedir}/www
+%{__cp} -a contrib/redhat/php-collection.conf %{buildroot}/%{_sysconfdir}/httpd/conf.d/
+
+### Clean up docs
+find contrib/ -type f -exec %{__chmod} a-x {} \;
+# *.la files shouldn't be distributed.
+rm -f %{buildroot}/%{_libdir}/{collectd/,}*.la
+
+# Move the Perl examples to a separate directory.
+mkdir perl-examples
+find contrib -name '*.p[lm]' -exec mv {} perl-examples/ \;
+
+# Remove Perl hidden .packlist files.
+find %{buildroot} -type f -name .packlist -delete
+# Remove Perl temporary file perllocal.pod
+find %{buildroot} -type f -name perllocal.pod -delete
+
+%if ! %{with_java}
+rm -f %{buildroot}%{_mandir}/man5/collectd-java.5*
+%endif
+
+%if ! %{with_perl}
+rm -f %{buildroot}%{_mandir}/man5/collectd-perl.5*
+rm -f %{buildroot}%{_mandir}/man3/Collectd::Unixsock.3pm*
+rm -fr perl-examples/
+rm -fr %{buildroot}/usr/lib/perl5/
+%endif
+
+%if ! %{with_python}
+rm -f %{buildroot}%{_mandir}/man5/collectd-python.5*
+%endif
+
+%if ! %{with_snmp}
+rm -f %{buildroot}%{_mandir}/man5/collectd-snmp.5*
+%endif
+
+
+%clean
+rm -rf %{buildroot}
+
+%post
+/sbin/chkconfig --add collectd
+
+%preun
+if [ $1 -eq 0 ]; then
+       /sbin/service collectd stop &>/dev/null
+       /sbin/chkconfig --del collectd
+fi
+
+%postun
+if [ $1 -ge 1 ]; then
+       /sbin/service collectd condrestart &>/dev/null || :
+fi
+
+%post -n libcollectdclient -p /sbin/ldconfig
+%postun -n libcollectdclient -p /sbin/ldconfig
+
+
+%files
+%doc AUTHORS COPYING ChangeLog README
+%config(noreplace) %{_sysconfdir}/collectd.conf
+%{_initrddir}/collectd
+%{_sbindir}/collectd
+%{_bindir}/collectd-nagios
+%{_bindir}/collectd-tg
+%{_bindir}/collectdctl
+%{_sbindir}/collectdmon
+%{_datadir}/collectd/
+%{_sharedstatedir}/collectd
+%{_mandir}/man1/collectd-nagios.1*
+%{_mandir}/man1/collectd.1*
+%{_mandir}/man1/collectdctl.1*
+%{_mandir}/man1/collectdmon.1*
+%{_mandir}/man1/collectd-tg.1*
+%{_mandir}/man5/collectd-email.5*
+%{_mandir}/man5/collectd-exec.5*
+%{_mandir}/man5/collectd-threshold.5*
+%{_mandir}/man5/collectd-unixsock.5*
+%{_mandir}/man5/collectd.conf.5*
+%{_mandir}/man5/types.db.5*
+
+# all plugins bundled with the main collectd package
+%{_libdir}/%{name}/match_empty_counter.so
+%{_libdir}/%{name}/match_hashed.so
+%{_libdir}/%{name}/match_regex.so
+%{_libdir}/%{name}/match_timediff.so
+%{_libdir}/%{name}/match_value.so
+%{_libdir}/%{name}/target_notification.so
+%{_libdir}/%{name}/target_replace.so
+%{_libdir}/%{name}/target_scale.so
+%{_libdir}/%{name}/target_set.so
+%{_libdir}/%{name}/target_v5upgrade.so
+
+%if %{with_aggregation}
+%{_libdir}/%{name}/aggregation.so
+%endif
+%if %{with_apcups}
+%{_libdir}/%{name}/apcups.so
+%endif
+%if %{with_battery}
+%{_libdir}/%{name}/battery.so
+%endif
+%if %{with_cgroups}
+%{_libdir}/%{name}/cgroups.so
+%endif
+%if %{with_conntrack}
+%{_libdir}/%{name}/conntrack.so
+%endif
+%if %{with_contextswitch}
+%{_libdir}/%{name}/contextswitch.so
+%endif
+%if %{with_cpu}
+%{_libdir}/%{name}/cpu.so
+%endif
+%if %{with_cpufreq}
+%{_libdir}/%{name}/cpufreq.so
+%endif
+%if %{with_csv}
+%{_libdir}/%{name}/csv.so
+%endif
+%if %{with_df}
+%{_libdir}/%{name}/df.so
+%endif
+%if %{with_disk}
+%{_libdir}/%{name}/disk.so
+%endif
+%if %{with_ethstat}
+%{_libdir}/%{name}/ethstat.so
+%endif
+%if %{with_entropy}
+%{_libdir}/%{name}/entropy.so
+%endif
+%if %{with_exec}
+%{_libdir}/%{name}/exec.so
+%endif
+%if %{with_filecount}
+%{_libdir}/%{name}/filecount.so
+%endif
+%if %{with_fscache}
+%{_libdir}/%{name}/fscache.so
+%endif
+%if %{with_interface}
+%{_libdir}/%{name}/interface.so
+%endif
+%if %{with_ipvs}
+%{_libdir}/%{name}/ipvs.so
+%endif
+%if %{with_irq}
+%{_libdir}/%{name}/irq.so
+%endif
+%if %{with_load}
+%{_libdir}/%{name}/load.so
+%endif
+%if %{with_logfile}
+%{_libdir}/%{name}/logfile.so
+%endif
+%if %{with_madwifi}
+%{_libdir}/%{name}/madwifi.so
+%endif
+%if %{with_mbmon}
+%{_libdir}/%{name}/mbmon.so
+%endif
+%if %{with_md}
+%{_libdir}/%{name}/md.so
+%endif
+%if %{with_memcached}
+%{_libdir}/%{name}/memcached.so
+%endif
+%if %{with_memory}
+%{_libdir}/%{name}/memory.so
+%endif
+%if %{with_multimeter}
+%{_libdir}/%{name}/multimeter.so
+%endif
+%if %{with_network}
+%{_libdir}/%{name}/network.so
+%endif
+%if %{with_nfs}
+%{_libdir}/%{name}/nfs.so
+%endif
+%if %{with_ntpd}
+%{_libdir}/%{name}/ntpd.so
+%endif
+%if %{with_numa}
+%{_libdir}/%{name}/numa.so
+%endif
+%if %{with_openvpn}
+%{_libdir}/%{name}/openvpn.so
+%endif
+%if %{with_olsrd}
+%{_libdir}/%{name}/olsrd.so
+%endif
+%if %{with_powerdns}
+%{_libdir}/%{name}/powerdns.so
+%endif
+%if %{with_processes}
+%{_libdir}/%{name}/processes.so
+%endif
+%if %{with_protocols}
+%{_libdir}/%{name}/protocols.so
+%endif
+%if %{with_serial}
+%{_libdir}/%{name}/serial.so
+%endif
+%if %{with_statsd}
+%{_libdir}/%{name}/statsd.so
+%endif
+%if %{with_swap}
+%{_libdir}/%{name}/swap.so
+%endif
+%if %{with_syslog}
+%{_libdir}/%{name}/syslog.so
+%endif
+%if %{with_table}
+%{_libdir}/%{name}/table.so
+%endif
+%if %{with_tail}
+%{_libdir}/%{name}/tail.so
+%endif
+%if %{with_tail_csv}
+%{_libdir}/%{name}/tail_csv.so
+%endif
+%if %{with_tcpconns}
+%{_libdir}/%{name}/tcpconns.so
+%endif
+%if %{with_teamspeak2}
+%{_libdir}/%{name}/teamspeak2.so
+%endif
+%if %{with_ted}
+%{_libdir}/%{name}/ted.so
+%endif
+%if %{with_thermal}
+%{_libdir}/%{name}/thermal.so
+%endif
+%if %{with_load}
+%{_libdir}/%{name}/threshold.so
+%endif
+%if %{with_unixsock}
+%{_libdir}/%{name}/unixsock.so
+%endif
+%if %{with_uptime}
+%{_libdir}/%{name}/uptime.so
+%endif
+%if %{with_users}
+%{_libdir}/%{name}/users.so
+%endif
+%if %{with_uuid}
+%{_libdir}/%{name}/uuid.so
+%endif
+%if %{with_vmem}
+%{_libdir}/%{name}/vmem.so
+%endif
+%if %{with_vserver}
+%{_libdir}/%{name}/vserver.so
+%endif
+%if %{with_wireless}
+%{_libdir}/%{name}/wireless.so
+%endif
+%if %{with_write_graphite}
+%{_libdir}/%{name}/write_graphite.so
+%endif
+
+
+%files -n libcollectdclient-devel
+%{_includedir}/collectd/client.h
+%{_includedir}/collectd/network.h
+%{_includedir}/collectd/network_buffer.h
+%{_includedir}/collectd/lcc_features.h
+%{_libdir}/pkgconfig/libcollectdclient.pc
+
+%files -n libcollectdclient
+%{_libdir}/libcollectdclient.so
+%{_libdir}/libcollectdclient.so.*
+
+%if %{with_amqp}
+%files amqp
+%{_libdir}/%{name}/amqp.so
+%endif
+
+%if %{with_apache}
+%files apache
+%{_libdir}/%{name}/apache.so
+%endif
+
+%if %{with_aquaero}
+%files aquaero
+%{_libdir}/%{name}/aquaero.so
+%endif
+
+%if %{with_ascent}
+%files ascent
+%{_libdir}/%{name}/ascent.so
+%endif
+
+%if %{with_bind}
+%files bind
+%{_libdir}/%{name}/bind.so
+%endif
+
+%if %{with_curl}
+%files curl
+%{_libdir}/%{name}/curl.so
+%endif
+
+%if %{with_curl_json}
+%files curl_json
+%{_libdir}/%{name}/curl_json.so
+%endif
+
+%if %{with_curl_xml}
+%files curl_xml
+%{_libdir}/%{name}/curl_xml.so
+%endif
+
+%if %{with_dns}
+%files dns
+%{_libdir}/%{name}/dns.so
+%endif
+
+%if %{with_dbi}
+%files dbi
+%{_libdir}/%{name}/dbi.so
+%endif
+
+%if %{with_email}
+%files email
+%{_libdir}/%{name}/email.so
+%endif
+
+%if %{with_gmond}
+%files gmond
+%{_libdir}/%{name}/gmond.so
+%endif
+
+%if %{with_hddtemp}
+%files hddtemp
+%{_libdir}/%{name}/hddtemp.so
+%endif
+
+%if %{with_ipmi}
+%files ipmi
+%{_libdir}/%{name}/ipmi.so
+%endif
+
+%if %{with_iptables}
+%files iptables
+%{_libdir}/%{name}/iptables.so
+%endif
+
+%if %{with_java}
+%files java
+%{_prefix}/share/collectd/java/collectd-api.jar
+%{_prefix}/share/collectd/java/generic-jmx.jar
+%{_libdir}/%{name}/java.so
+%{_mandir}/man5/collectd-java.5*
+%endif
+
+%if %{with_libvirt}
+%files libvirt
+%{_libdir}/%{name}/libvirt.so
+%endif
+
+%if %{with_lvm}
+%files lvm
+%{_libdir}/%{name}/lvm.so
+%endif
+
+%if %{with_memcachec}
+%files memcachec
+%{_libdir}/%{name}/memcachec.so
+%endif
+
+%if %{with_mic}
+%files mic
+%{_libdir}/%{name}/mic.so
+%endif
+
+%if %{with_mysql}
+%files mysql
+%{_libdir}/%{name}/mysql.so
+%endif
+
+%if %{with_netlink}
+%files netlink
+%{_libdir}/%{name}/netlink.so
+%endif
+
+%if %{with_nginx}
+%files nginx
+%{_libdir}/%{name}/nginx.so
+%endif
+
+%if %{with_notify_desktop}
+%files notify_desktop
+%{_libdir}/%{name}/notify_desktop.so
+%endif
+
+%if %{with_notify_email}
+%files notify_email
+%{_libdir}/%{name}/notify_email.so
+%endif
+
+%if %{with_nut}
+%files nut
+%{_libdir}/%{name}/nut.so
+%endif
+
+%if %{with_perl}
+%files perl
+%doc perl-examples/*
+%{perl_vendorlib}/Collectd.pm
+%{perl_vendorlib}/Collectd/
+%{_mandir}/man3/Collectd::Unixsock.3pm*
+%{_mandir}/man5/collectd-perl.5*
+%{_libdir}/%{name}/perl.so
+%endif
+
+%if %{with_pinba}
+%files pinba
+%{_libdir}/%{name}/pinba.so
+%endif
+
+%if %{with_ping}
+%files ping
+%{_libdir}/%{name}/ping.so
+%endif
+
+%if %{with_postgresql}
+%files postgresql
+%{_prefix}/share/collectd/postgresql_default.conf
+%{_libdir}/%{name}/postgresql.so
+%endif
+
+%if %{with_python}
+%files python
+%{_mandir}/man5/collectd-python*
+%{_libdir}/%{name}/python.so
+%endif
+
+%if %{with_redis}
+%files redis
+%{_libdir}/%{name}/redis.so
+%endif
+
+%if %{with_rrdcached}
+%files rrdcached
+%{_libdir}/%{name}/rrdcached.so
+%endif
+
+%if %{with_rrdtool}
+%files rrdtool
+%{_libdir}/%{name}/rrdtool.so
+%endif
+
+%if %{with_sensors}
+%files sensors
+%{_libdir}/%{name}/sensors.so
+%endif
+
+%if %{with_sigrok}
+%files sigrok
+%{_libdir}/%{name}/sigrok.so
+%endif
+
+%if %{with_snmp}
+%files snmp
+%{_mandir}/man5/collectd-snmp.5*
+%{_libdir}/%{name}/snmp.so
+%endif
+
+%if %{with_varnish}
+%files varnish
+%{_libdir}/%{name}/varnish.so
+%endif
+
+%if %{with_write_http}
+%files write_http
+%{_libdir}/%{name}/write_http.so
+%endif
+
+%if %{with_write_redis}
+%files write_redis
+%{_libdir}/%{name}/write_redis.so
+%endif
+
+%if %{with_write_riemann}
+%files write_riemann
+%{_libdir}/%{name}/write_riemann.so
+%endif
+
+%files collection3
+%{_localstatedir}/www/collection3
+%{_sysconfdir}/httpd/conf.d/collection3.conf
+
+%files php-collection
+%{_localstatedir}/www/php-collection
+%{_sysconfdir}/httpd/conf.d/php-collection.conf
+
+%files contrib
+%doc contrib/
+
+%changelog
+* Mon Aug 19 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.4.0-1
+- New upstream version
+- Build netlink plugin by default
+- Enable cgroups, lvm and statsd plugins
+- Enable (but don't build by default) mic, aquaero and sigrok plugins
+
+* Tue Aug 06 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.3.1-1
+- New upstream version
+- Added RHEL5 support:
+  * conditionally disable plugins not building on this platform
+  * add/specify some build dependencies and options
+  * replace some RPM macros not available on this platform
+- Removed duplicate --enable-aggregation
+- Added some comments & usage examples
+- Replaced a couple of "Buildrequires" by "BuildRequires"
+
+* Wed Apr 10 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.3.0-1
+- New upstream version
+- Enabled write_riemann plugin
+- Enabled tail_csv plugin
+- Installed collectd-tc manpage
+
+* Thu Jan 11 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.2.0-3
+- remove dependency on libstatgrab, which isn't required on linux
+
+* Thu Jan 03 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.2.0-2
+- collection3 and php-collection viewers are now in separate packages
+
+* Fri Dec 21 2012 Marc Fournier <marc.fournier@camptocamp.com> 5.2.0-1
+- New upstream version
+- Enabled aggregation plugin
+- Installed collectd-tc
+- Added network.h and network_buffer.h to libcollectdclient-devel
+- Moved libxml2-devel and libcurl-devel BRs to relevant plugins sections
+- Moved libcollectdclient.so from libcollectdclient-devel to libcollectdclient
+- Added rrdcached and redis plugin descriptions
+- Mentioned new pf plugin in disabled plugins list
+
+* Sun Nov 18 2012 Ruben Kerkhof <ruben@tilaa.nl> 5.1.0-3
+- Follow Fedora Packaging Guidelines in java subpackage
+
+* Sat Nov 17 2012 Ruben Kerkhof <ruben@tilaa.nl> 5.1.0-2
+- Move perl stuff to perl_vendorlib
+- Replace hardcoded paths with macros
+- Remove unnecessary Requires
+- Removed .a and .la files
+- Some other small cleanups
+
+* Fri Nov 16 2012 Marc Fournier <marc.fournier@camptocamp.com> 5.1.0-1
+- New upstream version
+- Changes to support 5.1.0
+- Enabled all buildable plugins based on libraries available on EL6 + EPEL
+- All plugins requiring external libraries are now shipped in separate
+  packages.
+- No longer treat Java plugin as an exception, correctly set $JAVA_HOME during
+  the build process + ensure build deps are installed.
+- Dropped per-plugin configuration files, as they tend to diverge from upstream
+  defaults.
+- Moved perl stuff to /usr/share/perl5/
+- Don't alter Interval and ReadThreads by default, let the user change this
+  himself.
+- Initscript improvements:
+  * checks configuration before (re)starting, based on debian's initscript
+  * use /etc/sysconfig instdead of /etc/default
+  * include optional $ARGS in arguments passed to collectd.
+- Drop collection.cgi from main package, as it's been obsoleted by collection3
+- Moved contrib/ to its own package, to avoid cluttering the main package with
+  non-essential stuff.
+- Replaced BuildPrereq by BuildRequires
 
-%changelog
 * Tue Jan 03 2011 Monetate <jason.stelzer@monetate.com> 5.0.1
 - New upstream version
 - Changes to support 5.0.1
@@ -315,7 +2102,7 @@ exit 0
 * Wed Jan 11 2007 Iain Lea <iain@bricbrac.de> 3.11.0-0
 - fixed spec file to build correctly on fedora core
 - added improved init.d script to work with chkconfig
-- added %post and %postun to call chkconfig automatically
+- added %%post and %%postun to call chkconfig automatically
 
 * Sun Jul 09 2006 Florian octo Forster <octo@verplant.org> 3.10.0-1
 - New upstream version
diff --git a/contrib/redhat/collection3.conf b/contrib/redhat/collection3.conf
new file mode 100644 (file)
index 0000000..91596b4
--- /dev/null
@@ -0,0 +1,5 @@
+Alias /collection3/ /var/www/collection3/
+<Directory /var/www/collection3/>
+    Options +Indexes
+    AllowOverride all
+</Directory>
diff --git a/contrib/redhat/email.conf b/contrib/redhat/email.conf
deleted file mode 100644 (file)
index 6f2caba..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-LoadPlugin email
-#<Plugin email>
-#      SocketFile "/usr/var/run/collectd-email"
-#      SocketGroup "collectd"
-#      SocketPerms "0770"
-#      MaxConns 5
-#</Plugin>
-
index a60acb3..abdb168 100644 (file)
@@ -18,17 +18,39 @@ prog="collectdmon"
 service="collectd"
 CONFIG=/etc/collectd.conf
 COLLECTD=/usr/sbin/collectd
-COLLECTDMONPID=/var/run/collectdmon.pid
+COLLECTDMONPIDDIR="/var/run"
+COLLECTDMONPID="$COLLECTDMONPIDDIR/collectdmon.pid"
 
-if [ -r /etc/default/$prog ]; then
-       . /etc/default/$prog
+if [ -r /etc/sysconfig/$service ]; then
+       . /etc/sysconfig/$service
 fi
 
+if [[ ! -d $COLLECTDMONPIDDIR ]]; then
+       mkdir -p $COLLECTDMONPIDDIR
+       [ -n "${RUNAS}" ] && chown "${RUNAS}:" "$COLLECTDMONPIDDIR"
+fi
+
+check_config() {
+        if test ! -r "$CONFIG"; then
+                return 2
+        fi
+        if ! $COLLECTD -t -C "$CONFIG"; then
+                return 1
+        fi
+        return 0
+}
+
+
 start () {
        echo -n $"Starting collectd: "
-       if [ -r "$CONFIG" ]
-       then
-               daemon $prog -P $COLLECTDMONPID -c $COLLECTD -- -C "$CONFIG"
+       check_config
+       rc="$?"
+       if test "$rc" -ne 0; then
+               RETVAL=6
+               echo $"not starting due to configuration error"
+               failure $"not starting $service due to configuration error"
+       else
+               daemon --user "${RUNAS:-root}" $prog -P $COLLECTDMONPID -c $COLLECTD -- -C "$CONFIG" $ARGS
                RETVAL=$?
                echo
                [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$service
@@ -36,7 +58,7 @@ start () {
 }
 stop () {
        echo -n $"Stopping collectd: "
-       killproc $prog
+       killproc -p $COLLECTDMONPID $prog
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$service
@@ -50,14 +72,22 @@ case "$1" in
        stop
        ;;
   status)
-       status $prog
+       status -p $COLLECTDMONPID $prog
        ;;
   restart|reload)
-       stop
-       start
+       check_config
+       rc="$?"
+       if test "$rc" -ne 0; then
+               RETVAL=6
+               echo $"not restarting due to configuration error"
+               failure $"not restarting $service due to configuration error"
+       else
+               stop
+               start
+       fi
        ;;
   condrestart)
-       [ -f /var/lock/subsys/$prog ] && restart || :
+       [ -f /var/lock/subsys/$service ] && restart || :
        ;;
   *)
        echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
diff --git a/contrib/redhat/mysql.conf b/contrib/redhat/mysql.conf
deleted file mode 100644 (file)
index ad87557..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-LoadPlugin mysql
-
-#<Plugin mysql>
-#      Host "database.serv.er"
-#      User "db_user"
-#      Password "secret"
-#      Database "db_name"
-#</Plugin>
-
diff --git a/contrib/redhat/nginx.conf b/contrib/redhat/nginx.conf
deleted file mode 100644 (file)
index 56ce35d..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-LoadPlugin nginx
-
-#<Plugin nginx>
-#      URL "http://localhost/status?auto"
-#      User "www-user"
-#      Password "secret"
-#      CACert "/etc/ssl/ca.crt"
-#</Plugin>
diff --git a/contrib/redhat/php-collection.conf b/contrib/redhat/php-collection.conf
new file mode 100644 (file)
index 0000000..88bb609
--- /dev/null
@@ -0,0 +1,6 @@
+Alias /php-collection/ /var/www/php-collection/
+<Directory /var/www/php-collection/>
+    DirectoryIndex index.php
+    Options -Indexes
+    AddType application/x-httpd-php .php
+</Directory>
diff --git a/contrib/redhat/sensors.conf b/contrib/redhat/sensors.conf
deleted file mode 100644 (file)
index 82455f8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-LoadPlugin sensors
-
-#<Plugin sensors>
-#      Sensor "it8712-isa-0290/temperature-temp1"
-#      Sensor "it8712-isa-0290/fanspeed-fan3"
-#      Sensor "it8712-isa-0290/voltage-in8"
-#      IgnoreSelected false
-#</Plugin>
-
diff --git a/contrib/redhat/snmp.conf b/contrib/redhat/snmp.conf
deleted file mode 100644 (file)
index e13833c..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-LoadPlugin snmp
-
-#<Plugin snmp>
-#   <Data "powerplus_voltge_input">
-#       Type "voltage"
-#       Table false
-#       Instance "input_line1"
-#       Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1"
-#   </Data>
-#   <Data "hr_users">
-#       Type "users"
-#       Table false
-#       Instance ""
-#       Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0"
-#   </Data>
-#   <Data "std_traffic">
-#       Type "if_octets"
-#       Table true
-#       Instance "IF-MIB::ifDescr"
-#       Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
-#   </Data>
-#   
-#   <Host "some.switch.mydomain.org">
-#       Address "192.168.0.2"
-#       Version 1
-#       Community "community_string"
-#       Collect "std_traffic"
-#       Inverval 120
-#   </Host>
-#   <Host "some.server.mydomain.org">
-#       Address "192.168.0.42"
-#       Version 2
-#       Community "another_string"
-#       Collect "std_traffic" "hr_users"
-#   </Host>
-#   <Host "some.ups.mydomain.org">
-#       Address "192.168.0.3"
-#       Version 1
-#       Community "more_communities"
-#       Collect "powerplus_voltge_input"
-#       Interval 300
-#   </Host>
-#</Plugin>
-
diff --git a/contrib/upstart.collectd.conf b/contrib/upstart.collectd.conf
new file mode 100644 (file)
index 0000000..1c7fd9c
--- /dev/null
@@ -0,0 +1,47 @@
+description "start/stop/control collectd"
+# http://collectd.org/
+# Upstart is the replacement init system used in Debian, Ubuntu,
+# and in Fedora. Refer to http://upstart.ubuntu.com/cookbook/
+#
+# Normally this file will live as `/etc/init/collectd.conf`
+
+usage "initctl <start|stop> collectd"
+author "Dave Cottlehuber <dch@jsonified.com>"
+version "1.1"
+
+# There are a number of alternative start sequences however
+# most of those do not work on all Ubuntu flavours and releases.
+start on started networking and filesystem
+stop on runlevel [!2345]
+
+# collectd itself will run with reduced privileges, but not
+# all plugins will. Test and edit as required.
+# An alternative configuration is as a user script in ~/.init/ however
+# these cannot be started at boot time by the system without
+# arcane trickery. Also a root user will not see these tasks/jobs
+# by default. set*id is a reasonable and secure compromise.
+#setuid nobody
+#setgid nobody
+
+# Other parameters such as the path to the configuration file
+# will have been compiled into the binary. These are trivially
+# added as environment variables below, and then into both
+# `pre-start` command check before collectd runs, and subsequent
+# `exec` command parameters below. Remember that upstart runs all
+# shell commands via `sh -e`.
+env DAEMON=/usr/sbin/collectd
+
+# Tell upstart to watch for forking when tracking the pid for us.
+expect fork
+
+# prevent thrashing - 10 restarts in 5 seconds
+respawn
+respawn limit 10 5
+
+# Make a log available in /var/log/upstart/collectd.log
+console log
+
+# The daemon will not start if the configuration is invalid.
+pre-start exec $DAEMON -t
+# Let's Fork!
+exec $DAEMON
index 26c2355..84affcb 100644 (file)
@@ -33,12 +33,15 @@ collectd_SOURCES = collectd.c collectd.h \
                   utils_ignorelist.c utils_ignorelist.h \
                   utils_llist.c utils_llist.h \
                   utils_parse_option.c utils_parse_option.h \
+                  utils_random.c utils_random.h \
                   utils_tail_match.c utils_tail_match.h \
                   utils_match.c utils_match.h \
                   utils_subst.c utils_subst.h \
                   utils_tail.c utils_tail.h \
                   utils_time.c utils_time.h \
-                  types_list.c types_list.h
+                  types_list.c types_list.h \
+                  utils_threshold.c utils_threshold.h
+
 
 collectd_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL)
 collectd_CFLAGS = $(AM_CFLAGS)
@@ -90,6 +93,7 @@ collectdmon_SOURCES = collectdmon.c
 collectdmon_CPPFLAGS = $(AM_CPPFLAGS)
 
 collectd_nagios_SOURCES = collectd-nagios.c
+collectd_nagios_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/libcollectdclient/collectd
 collectd_nagios_LDADD =
 if BUILD_WITH_LIBSOCKET
 collectd_nagios_LDADD += -lsocket
@@ -103,6 +107,7 @@ collectd_nagios_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
 
 collectdctl_SOURCES = collectdctl.c
+collectdctl_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/libcollectdclient/collectd
 collectdctl_LDADD =
 if BUILD_WITH_LIBSOCKET
 collectdctl_LDADD += -lsocket
@@ -115,10 +120,14 @@ collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
 collectd_tg_SOURCES = collectd-tg.c \
                      utils_heap.c utils_heap.h
+collectd_tg_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/libcollectdclient/collectd
 collectd_tg_LDADD =
 if BUILD_WITH_LIBSOCKET
 collectd_tg_LDADD += -lsocket
 endif
+if BUILD_WITH_LIBRT
+collectd_tg_LDADD += -lrt
+endif
 if BUILD_AIX
 collectd_tg_LDADD += -lm
 endif
@@ -126,10 +135,10 @@ collectd_tg_LDADD += libcollectdclient/libcollectdclient.la
 collectd_tg_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
 
-pkglib_LTLIBRARIES = 
+pkglib_LTLIBRARIES =
 
-BUILT_SOURCES = 
-CLEANFILES = 
+BUILT_SOURCES =
+CLEANFILES =
 
 if BUILD_PLUGIN_AGGREGATION
 pkglib_LTLIBRARIES += aggregation.la
@@ -184,11 +193,21 @@ if BUILD_PLUGIN_APPLE_SENSORS
 pkglib_LTLIBRARIES += apple_sensors.la
 apple_sensors_la_SOURCES = apple_sensors.c
 apple_sensors_la_LDFLAGS = -module -avoid-version
-apple_sensors_la_LIBADD = -lIOKit
+apple_sensors_la_LDFLAGS += -framework IOKit
 collectd_LDADD += "-dlopen" apple_sensors.la
 collectd_DEPENDENCIES += apple_sensors.la
 endif
 
+if BUILD_PLUGIN_AQUAERO
+pkglib_LTLIBRARIES += aquaero.la
+aquaero_la_SOURCES = aquaero.c
+aquaero_la_LDFLAGS = -module -avoid-version
+aquaero_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBAQUAERO5_CFLAGS)
+aquaero_la_LIBADD = $(BUILD_WITH_LIBAQUAERO5_LDFLAGS) -laquaero5
+collectd_LDADD += "-dlopen" aquaero.la
+collectd_DEPENDENCIES += aquaero.la
+endif
+
 if BUILD_PLUGIN_ASCENT
 pkglib_LTLIBRARIES += ascent.la
 ascent_la_SOURCES = ascent.c
@@ -206,7 +225,7 @@ battery_la_SOURCES = battery.c
 battery_la_LDFLAGS = -module -avoid-version
 battery_la_LIBADD =
 if BUILD_WITH_LIBIOKIT
-battery_la_LIBADD += -lIOKit
+battery_la_LDFLAGS += -framework IOKit
 endif
 collectd_LDADD += "-dlopen" battery.la
 collectd_DEPENDENCIES += battery.la
@@ -223,6 +242,14 @@ collectd_LDADD += "-dlopen" bind.la
 collectd_DEPENDENCIES += bind.la
 endif
 
+if BUILD_PLUGIN_CGROUPS
+pkglib_LTLIBRARIES += cgroups.la
+cgroups_la_SOURCES = cgroups.c utils_mount.c utils_mount.h
+cgroups_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" cgroups.la
+collectd_DEPENDENCIES += cgroups.la
+endif
+
 if BUILD_PLUGIN_CONNTRACK
 pkglib_LTLIBRARIES += conntrack.la
 conntrack_la_SOURCES = conntrack.c
@@ -248,7 +275,7 @@ pkglib_LTLIBRARIES += cpu.la
 cpu_la_SOURCES = cpu.c
 cpu_la_CFLAGS = $(AM_CFLAGS)
 cpu_la_LDFLAGS = -module -avoid-version
-cpu_la_LIBADD = 
+cpu_la_LIBADD =
 if BUILD_WITH_LIBKSTAT
 cpu_la_LIBADD += -lkstat
 endif
@@ -346,7 +373,7 @@ pkglib_LTLIBRARIES += disk.la
 disk_la_SOURCES = disk.c
 disk_la_CFLAGS = $(AM_CFLAGS)
 disk_la_LDFLAGS = -module -avoid-version
-disk_la_LIBADD = 
+disk_la_LIBADD =
 if BUILD_WITH_LIBKSTAT
 disk_la_LIBADD += -lkstat
 endif
@@ -354,12 +381,15 @@ if BUILD_WITH_LIBDEVINFO
 disk_la_LIBADD += -ldevinfo
 endif
 if BUILD_WITH_LIBIOKIT
-disk_la_LIBADD += -lIOKit
+disk_la_LDFLAGS += -framework IOKit
 endif
 if BUILD_WITH_LIBSTATGRAB
-disk_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)  
+disk_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
 disk_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
 endif
+if BUILD_WITH_LIBUDEV
+disk_la_LIBADD += -ludev
+endif
 if BUILD_WITH_PERFSTAT
 disk_la_LIBADD += -lperfstat
 endif
@@ -552,6 +582,17 @@ collectd_LDADD += "-dlopen" logfile.la
 collectd_DEPENDENCIES += logfile.la
 endif
 
+if BUILD_PLUGIN_LOG_LOGSTASH
+pkglib_LTLIBRARIES += log_logstash.la
+log_logstash_la_SOURCES = log_logstash.c
+log_logstash_la_CFLAGS = $(AM_CFLAGS)
+log_logstash_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBYAJL_LDFLAGS)
+log_logstash_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
+log_logstash_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
+collectd_LDADD += "-dlopen" log_logstash.la
+collectd_DEPENDENCIES += log_logstash.la
+endif
+
 if BUILD_PLUGIN_LPAR
 pkglib_LTLIBRARIES += lpar.la
 lpar_la_SOURCES = lpar.c
@@ -561,6 +602,15 @@ collectd_DEPENDENCIES += lpar.la
 lpar_la_LIBADD = -lperfstat
 endif
 
+if BUILD_PLUGIN_LVM
+pkglib_LTLIBRARIES += lvm.la
+lvm_la_SOURCES = lvm.c
+lvm_la_LDFLAGS = -module -avoid-version
+lvm_la_LIBADD = $(BUILD_WITH_LIBLVM2APP_LIBS)
+collectd_LDADD += "-dlopen" lvm.la
+collectd_DEPENDENCIES += lvm.la
+endif
+
 if BUILD_PLUGIN_MADWIFI
 pkglib_LTLIBRARIES += madwifi.la
 madwifi_la_SOURCES = madwifi.c madwifi.h
@@ -720,8 +770,8 @@ if BUILD_PLUGIN_NETLINK
 pkglib_LTLIBRARIES += netlink.la
 netlink_la_SOURCES = netlink.c
 netlink_la_LDFLAGS = -module -avoid-version
-netlink_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBNETLINK_CFLAGS)
-netlink_la_LIBADD = $(BUILD_WITH_LIBNETLINK_LIBS)
+netlink_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
+netlink_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS)
 collectd_LDADD += "-dlopen" netlink.la
 collectd_DEPENDENCIES += netlink.la
 endif
@@ -828,7 +878,7 @@ if BUILD_PLUGIN_OLSRD
 pkglib_LTLIBRARIES += olsrd.la
 olsrd_la_SOURCES = olsrd.c
 olsrd_la_LDFLAGS = -module -avoid-version
-olsrd_la_LIBADD = 
+olsrd_la_LIBADD =
 if BUILD_WITH_LIBSOCKET
 olsrd_la_LIBADD += -lsocket
 endif
@@ -897,10 +947,9 @@ collectd_DEPENDENCIES += pf.la
 endif
 
 if BUILD_PLUGIN_PINBA
-BUILT_SOURCES += pinba.pb-c.c pinba.pb-c.h
-CLEANFILES += pinba.pb-c.c pinba.pb-c.h
 pkglib_LTLIBRARIES += pinba.la
 pinba_la_SOURCES = pinba.c
+nodist_pinba_la_SOURCES = pinba.pb-c.c pinba.pb-c.h
 pinba_la_LDFLAGS = -module -avoid-version
 pinba_la_LIBADD = -lprotobuf-c
 collectd_LDADD += "-dlopen" pinba.la
@@ -1029,6 +1078,16 @@ collectd_LDADD += "-dlopen" serial.la
 collectd_DEPENDENCIES += serial.la
 endif
 
+if BUILD_PLUGIN_SIGROK
+pkglib_LTLIBRARIES += sigrok.la
+sigrok_la_SOURCES = sigrok.c
+sigrok_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBSIGROK_CFLAGS)
+sigrok_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBSIGROK_LDFLAGS)
+sigrok_la_LIBADD = -lsigrok
+collectd_LDADD += "-dlopen" sigrok.la
+collectd_DEPENDENCIES += sigrok.la
+endif
+
 if BUILD_PLUGIN_SNMP
 pkglib_LTLIBRARIES += snmp.la
 snmp_la_SOURCES = snmp.c
@@ -1046,6 +1105,16 @@ collectd_LDADD += "-dlopen" snmp.la
 collectd_DEPENDENCIES += snmp.la
 endif
 
+if BUILD_PLUGIN_STATSD
+pkglib_LTLIBRARIES += statsd.la
+statsd_la_SOURCES = statsd.c \
+                    utils_latency.h utils_latency.c
+statsd_la_LDFLAGS = -module -avoid-version
+statsd_la_LIBADD = -lpthread
+collectd_LDADD += "-dlopen" statsd.la
+collectd_DEPENDENCIES += statsd.la
+endif
+
 if BUILD_PLUGIN_SWAP
 pkglib_LTLIBRARIES += swap.la
 swap_la_SOURCES = swap.c
@@ -1097,6 +1166,14 @@ collectd_LDADD += "-dlopen" tail.la
 collectd_DEPENDENCIES += tail.la
 endif
 
+if BUILD_PLUGIN_TAIL_CSV
+pkglib_LTLIBRARIES += tail_csv.la
+tail_csv_la_SOURCES = tail_csv.c
+tail_csv_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" tail_csv.la
+collectd_DEPENDENCIES += tail_csv.la
+endif
+
 if BUILD_PLUGIN_TAPE
 pkglib_LTLIBRARIES += tape.la
 tape_la_SOURCES = tape.c
@@ -1208,6 +1285,7 @@ pkglib_LTLIBRARIES += unixsock.la
 unixsock_la_SOURCES = unixsock.c \
                      utils_cmd_flush.h utils_cmd_flush.c \
                      utils_cmd_getval.h utils_cmd_getval.c \
+                     utils_cmd_getthreshold.h utils_cmd_getthreshold.c \
                      utils_cmd_listval.h utils_cmd_listval.c \
                      utils_cmd_putval.h utils_cmd_putval.c \
                      utils_cmd_putnotif.h utils_cmd_putnotif.c
@@ -1257,6 +1335,16 @@ collectd_LDADD += "-dlopen" uuid.la
 collectd_DEPENDENCIES += uuid.la
 endif
 
+if BUILD_PLUGIN_MIC
+pkglib_LTLIBRARIES += mic.la
+mic_la_SOURCES = mic.c
+mic_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_MIC_LIBPATH)
+mic_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_MIC_CPPFLAGS)
+mic_la_LIBADD = $(BUILD_WITH_MIC_LDADD)
+collectd_LDADD += "-dlopen" mic.la
+collectd_DEPENDENCIES += mic.la
+endif
+
 if BUILD_PLUGIN_VARNISH
 pkglib_LTLIBRARIES += varnish.la
 varnish_la_SOURCES = varnish.c
@@ -1316,6 +1404,19 @@ endif
 collectd_DEPENDENCIES += write_http.la
 endif
 
+if BUILD_PLUGIN_WRITE_KAFKA
+pkglib_LTLIBRARIES += write_kafka.la
+write_kafka_la_SOURCES = write_kafka.c \
+                        utils_format_graphite.c utils_format_graphite.h \
+                        utils_format_json.c utils_format_json.h \
+                        utils_cmd_putval.c utils_cmd_putval.h \
+                        utils_crc32.c utils_crc32.h
+write_kafka_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBRDKAFKA_LDFLAGS)
+write_kafka_la_LIBADD = $(BUILD_WITH_LIBRDKAFKA_LIBS)
+collectd_LDADD += "-dlopen" write_kafka.la
+collectd_DEPENDENCIES += write_kafka.la
+endif
+
 if BUILD_PLUGIN_WRITE_MONGODB
 pkglib_LTLIBRARIES += write_mongodb.la
 write_mongodb_la_SOURCES = write_mongodb.c
@@ -1336,6 +1437,16 @@ collectd_LDADD += "-dlopen" write_redis.la
 collectd_DEPENDENCIES += write_redis.la
 endif
 
+if BUILD_PLUGIN_WRITE_RIEMANN
+pkglib_LTLIBRARIES += write_riemann.la
+write_riemann_la_SOURCES = write_riemann.c write_riemann_threshold.c
+nodist_write_riemann_la_SOURCES = riemann.pb-c.c riemann.pb-c.h
+write_riemann_la_LDFLAGS = -module -avoid-version
+write_riemann_la_LIBADD = -lprotobuf-c
+collectd_LDADD += "-dlopen" write_riemann.la
+collectd_DEPENDENCIES += write_riemann.la
+endif
+
 if BUILD_PLUGIN_XMMS
 pkglib_LTLIBRARIES += xmms.la
 xmms_la_SOURCES = xmms.c
@@ -1351,7 +1462,11 @@ pkglib_LTLIBRARIES += zfs_arc.la
 zfs_arc_la_SOURCES = zfs_arc.c
 zfs_arc_la_CFLAGS = $(AM_CFLAGS)
 zfs_arc_la_LDFLAGS = -module -avoid-version
+if BUILD_FREEBSD
+zfs_arc_la_LIBADD = -lm
+else
 zfs_arc_la_LIBADD = -lkstat
+endif
 collectd_LDADD += "-dlopen" zfs_arc.la
 collectd_DEPENDENCIES += zfs_arc.la
 endif
@@ -1369,13 +1484,14 @@ dist_man_MANS = collectd.1 \
                collectd-perl.5 \
                collectd-python.5 \
                collectd-snmp.5 \
+               collectd-tg.1 \
                collectd-threshold.5 \
                collectd-unixsock.5 \
                types.db.5
 
 #collectd_1_SOURCES = collectd.pod
 
-EXTRA_DIST = types.db pinba.proto
+EXTRA_DIST = types.db
 
 EXTRA_DIST +=   collectd.conf.pod \
                collectd-email.pod \
@@ -1388,6 +1504,7 @@ EXTRA_DIST +=   collectd.conf.pod \
                collectd-python.pod \
                collectd.pod \
                collectd-snmp.pod \
+               collectd-tg.pod \
                collectd-threshold.pod \
                collectd-unixsock.pod \
                postgresql_default.conf \
@@ -1409,8 +1526,26 @@ EXTRA_DIST +=   collectd.conf.pod \
                echo "$@ has some POD errors!"; false; \
        fi
 
+# Protocol buffer for the "pinba" plugin.
+EXTRA_DIST += pinba.proto
+if HAVE_PROTOC_C
+CLEANFILES += pinba.pb-c.c pinba.pb-c.h
+BUILT_SOURCES += pinba.pb-c.c pinba.pb-c.h
+
 pinba.pb-c.c pinba.pb-c.h: pinba.proto
-       protoc-c --c_out . pinba.proto
+       protoc-c -I$(srcdir) --c_out . $(srcdir)/pinba.proto
+endif
+
+# Protocol buffer for the "write_riemann" plugin.
+EXTRA_DIST += riemann.proto
+if HAVE_PROTOC_C
+CLEANFILES += riemann.pb-c.c riemann.pb-c.h
+
+BUILT_SOURCES += riemann.pb-c.c riemann.pb-c.h
+
+riemann.pb-c.c riemann.pb-c.h: riemann.proto
+       protoc-c -I$(srcdir) --c_out . $(srcdir)/riemann.proto
+endif
 
 install-exec-hook:
        $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
@@ -1430,15 +1565,48 @@ uninstall-hook:
        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 \
+check_PROGRAMS = test_common test_utils_avltree test_utils_heap test_utils_mount test_utils_vl_lookup
+
+test_common_SOURCES = tests/test_common.c \
+                      common.h common.c \
+                      tests/mock/plugin.c \
+                      tests/mock/utils_cache.c \
+                      tests/mock/utils_time.c
+test_common_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL)
+test_common_LDFLAGS = -export-dynamic
+test_common_LDADD =
+
+test_utils_avltree_SOURCES = tests/test_utils_avltree.c \
+                             utils_avltree.c utils_avltree.h
+test_utils_avltree_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL)
+test_utils_avltree_LDFLAGS = -export-dynamic
+test_utils_avltree_LDADD =
+
+test_utils_heap_SOURCES = tests/test_utils_heap.c \
+                          utils_heap.c utils_heap.h
+test_utils_heap_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL)
+test_utils_heap_LDFLAGS = -export-dynamic
+test_utils_heap_LDADD =
+
+test_utils_mount_SOURCES = tests/test_utils_mount.c \
+                           utils_mount.c utils_mount.h \
+                           common.c common.h \
+                           tests/mock/plugin.c \
+                           tests/mock/utils_cache.c \
+                           tests/mock/utils_time.c
+test_utils_mount_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL)
+test_utils_mount_LDFLAGS = -export-dynamic
+test_utils_mount_LDADD =
+
+test_utils_vl_lookup_SOURCES = tests/test_utils_vl_lookup.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
+                               common.c common.h \
+                               tests/mock/plugin.c \
+                               tests/mock/utils_cache.c \
+                               tests/mock/utils_time.c
+test_utils_vl_lookup_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL)
+test_utils_vl_lookup_LDFLAGS = -export-dynamic
+test_utils_vl_lookup_LDADD =
+
+TESTS = test_common test_utils_avltree test_utils_heap test_utils_mount test_utils_vl_lookup
index db33c17..0c0f19d 100644 (file)
  **/
 
 #include "collectd.h"
+
+#include <pthread.h>
+
 #include "plugin.h"
 #include "common.h"
 #include "configfile.h"
 #include "meta_data.h"
 #include "utils_cache.h" /* for uc_get_rate() */
+#include "utils_subst.h"
 #include "utils_vl_lookup.h"
 
-#include <pthread.h>
+#define AGG_MATCHES_ALL(str) (strcmp ("/.*/", str) == 0)
+#define AGG_FUNC_PLACEHOLDER "%{aggregation}"
 
 struct aggregation_s /* {{{ */
 {
   identifier_t ident;
+  unsigned int group_by;
+
+  unsigned int regex_fields;
+
+  char *set_host;
+  char *set_plugin;
+  char *set_plugin_instance;
+  char *set_type_instance;
 
   _Bool calc_num;
   _Bool calc_sum;
@@ -78,6 +91,23 @@ 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 _Bool agg_is_regex (char const *str) /* {{{ */
+{
+  size_t len;
+
+  if (str == NULL)
+    return (0);
+
+  len = strlen (str);
+  if (len < 3)
+    return (0);
+
+  if ((str[0] == '/') && (str[len - 1] == '/'))
+    return (1);
+  else
+    return (0);
+} /* }}} _Bool agg_is_regex */
+
 static void agg_destroy (aggregation_t *agg) /* {{{ */
 {
   sfree (agg);
@@ -116,6 +146,92 @@ static void agg_instance_destroy (agg_instance_t *inst) /* {{{ */
   inst->max = NAN;
 } /* }}} void agg_instance_destroy */
 
+static int agg_instance_create_name (agg_instance_t *inst, /* {{{ */
+    value_list_t const *vl, aggregation_t const *agg)
+{
+#define COPY_FIELD(buffer, buffer_size, field, group_mask, all_value) do { \
+  if (agg->set_ ## field != NULL) \
+    sstrncpy (buffer, agg->set_ ## field, buffer_size); \
+  else if ((agg->regex_fields & group_mask) \
+      && (agg->group_by & group_mask)) \
+    sstrncpy (buffer, vl->field, buffer_size); \
+  else if ((agg->regex_fields & group_mask) \
+      && (AGG_MATCHES_ALL (agg->ident.field))) \
+    sstrncpy (buffer, all_value, buffer_size); \
+  else \
+    sstrncpy (buffer, agg->ident.field, buffer_size); \
+} while (0)
+
+  /* Host */
+  COPY_FIELD (inst->ident.host, sizeof (inst->ident.host),
+      host, LU_GROUP_BY_HOST, "global");
+
+  /* Plugin */
+  if (agg->set_plugin != NULL)
+    sstrncpy (inst->ident.plugin, agg->set_plugin,
+        sizeof (inst->ident.plugin));
+  else
+    sstrncpy (inst->ident.plugin, "aggregation", sizeof (inst->ident.plugin));
+
+  /* Plugin instance */
+  if (agg->set_plugin_instance != NULL)
+    sstrncpy (inst->ident.plugin_instance, agg->set_plugin_instance,
+        sizeof (inst->ident.plugin_instance));
+  else
+  {
+    char tmp_plugin[DATA_MAX_NAME_LEN];
+    char tmp_plugin_instance[DATA_MAX_NAME_LEN] = "";
+
+    if ((agg->regex_fields & LU_GROUP_BY_PLUGIN)
+        && (agg->group_by & LU_GROUP_BY_PLUGIN))
+      sstrncpy (tmp_plugin, vl->plugin, sizeof (tmp_plugin));
+    else if ((agg->regex_fields & LU_GROUP_BY_PLUGIN)
+        && (AGG_MATCHES_ALL (agg->ident.plugin)))
+      sstrncpy (tmp_plugin, "", sizeof (tmp_plugin));
+    else
+      sstrncpy (tmp_plugin, agg->ident.plugin, sizeof (tmp_plugin));
+
+    if ((agg->regex_fields & LU_GROUP_BY_PLUGIN_INSTANCE)
+        && (agg->group_by & LU_GROUP_BY_PLUGIN_INSTANCE))
+      sstrncpy (tmp_plugin_instance, vl->plugin_instance,
+          sizeof (tmp_plugin_instance));
+    else if ((agg->regex_fields & LU_GROUP_BY_PLUGIN_INSTANCE)
+        && (AGG_MATCHES_ALL (agg->ident.plugin_instance)))
+      sstrncpy (tmp_plugin_instance, "", sizeof (tmp_plugin_instance));
+    else
+      sstrncpy (tmp_plugin_instance, agg->ident.plugin_instance,
+          sizeof (tmp_plugin_instance));
+
+    if ((strcmp ("", tmp_plugin) == 0)
+        && (strcmp ("", tmp_plugin_instance) == 0))
+      sstrncpy (inst->ident.plugin_instance, AGG_FUNC_PLACEHOLDER,
+          sizeof (inst->ident.plugin_instance));
+    else if (strcmp ("", tmp_plugin) != 0)
+      ssnprintf (inst->ident.plugin_instance,
+          sizeof (inst->ident.plugin_instance),
+          "%s-%s", tmp_plugin, AGG_FUNC_PLACEHOLDER);
+    else if (strcmp ("", tmp_plugin_instance) != 0)
+      ssnprintf (inst->ident.plugin_instance,
+          sizeof (inst->ident.plugin_instance),
+          "%s-%s", tmp_plugin_instance, AGG_FUNC_PLACEHOLDER);
+    else
+      ssnprintf (inst->ident.plugin_instance,
+          sizeof (inst->ident.plugin_instance),
+          "%s-%s-%s", tmp_plugin, tmp_plugin_instance, AGG_FUNC_PLACEHOLDER);
+  }
+
+  /* Type */
+  sstrncpy (inst->ident.type, agg->ident.type, sizeof (inst->ident.type));
+
+  /* Type instance */
+  COPY_FIELD (inst->ident.type_instance, sizeof (inst->ident.type_instance),
+      type_instance, LU_GROUP_BY_TYPE_INSTANCE, "");
+
+#undef COPY_FIELD
+
+  return (0);
+} /* }}} int agg_instance_create_name */
+
 /* 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)
@@ -135,19 +251,7 @@ static agg_instance_t *agg_instance_create (data_set_t const *ds, /* {{{ */
 
   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
+  agg_instance_create_name (inst, vl, agg);
 
   inst->min = NAN;
   inst->max = NAN;
@@ -240,8 +344,8 @@ static int agg_instance_read_func (agg_instance_t *inst, /* {{{ */
   int status;
 
   if (pi_prefix[0] != 0)
-    ssnprintf (vl->plugin_instance, sizeof (vl->plugin_instance), "%s-%s",
-        pi_prefix, func);
+    subst_string (vl->plugin_instance, sizeof (vl->plugin_instance),
+        pi_prefix, AGG_FUNC_PLACEHOLDER, func);
   else
     sstrncpy (vl->plugin_instance, func, sizeof (vl->plugin_instance));
 
@@ -263,7 +367,7 @@ static int agg_instance_read_func (agg_instance_t *inst, /* {{{ */
   vl->values = &v;
   vl->values_len = 1;
 
-  plugin_dispatch_values_secure (vl);
+  plugin_dispatch_values (vl);
 
   vl->values = NULL;
   vl->values_len = 0;
@@ -274,7 +378,6 @@ static int agg_instance_read_func (agg_instance_t *inst, /* {{{ */
 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
@@ -291,39 +394,16 @@ static int agg_instance_read (agg_instance_t *inst, cdtime_t t) /* {{{ */
   }
   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.host, inst->ident.host, sizeof (vl.host));
+  sstrncpy (vl.plugin, inst->ident.plugin, sizeof (vl.plugin));
   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));
+  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); \
+        inst->state_ ## func, &vl, inst->ident.plugin_instance, t); \
   } \
 } while (0)
 
@@ -424,14 +504,13 @@ static int agg_config_handle_group_by (oconfig_item_t const *ci, /* {{{ */
     value = ci->values[i].value.string;
 
     if (strcasecmp ("Host", value) == 0)
-      sstrncpy (agg->ident.host, LU_ANY, sizeof (agg->ident.host));
+      agg->group_by |= LU_GROUP_BY_HOST;
     else if (strcasecmp ("Plugin", value) == 0)
-      sstrncpy (agg->ident.plugin, LU_ANY, sizeof (agg->ident.plugin));
+      agg->group_by |= LU_GROUP_BY_PLUGIN;
     else if (strcasecmp ("PluginInstance", value) == 0)
-      sstrncpy (agg->ident.plugin_instance, LU_ANY,
-          sizeof (agg->ident.plugin_instance));
+      agg->group_by |= LU_GROUP_BY_PLUGIN_INSTANCE;
     else if (strcasecmp ("TypeInstance", value) == 0)
-      sstrncpy (agg->ident.type_instance, LU_ANY, sizeof (agg->ident.type_instance));
+      agg->group_by |= LU_GROUP_BY_TYPE_INSTANCE;
     else if (strcasecmp ("Type", value) == 0)
       ERROR ("aggregation plugin: Grouping by type is not supported.");
     else
@@ -457,12 +536,12 @@ static int agg_config_aggregation (oconfig_item_t *ci) /* {{{ */
   }
   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,
+  sstrncpy (agg->ident.host, "/.*/", sizeof (agg->ident.host));
+  sstrncpy (agg->ident.plugin, "/.*/", sizeof (agg->ident.plugin));
+  sstrncpy (agg->ident.plugin_instance, "/.*/",
       sizeof (agg->ident.plugin_instance));
-  sstrncpy (agg->ident.type, LU_ALL, sizeof (agg->ident.type));
-  sstrncpy (agg->ident.type_instance, LU_ALL,
+  sstrncpy (agg->ident.type, "/.*/", sizeof (agg->ident.type));
+  sstrncpy (agg->ident.type_instance, "/.*/",
       sizeof (agg->ident.type_instance));
 
   for (i = 0; i < ci->children_num; i++)
@@ -484,6 +563,14 @@ static int agg_config_aggregation (oconfig_item_t *ci) /* {{{ */
     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 ("SetHost", child->key) == 0)
+      cf_util_get_string (child, &agg->set_host);
+    else if (strcasecmp ("SetPlugin", child->key) == 0)
+      cf_util_get_string (child, &agg->set_plugin);
+    else if (strcasecmp ("SetPluginInstance", child->key) == 0)
+      cf_util_get_string (child, &agg->set_plugin_instance);
+    else if (strcasecmp ("SetTypeInstance", child->key) == 0)
+      cf_util_get_string (child, &agg->set_type_instance);
     else if (strcasecmp ("GroupBy", child->key) == 0)
       agg_config_handle_group_by (child, agg);
     else if (strcasecmp ("CalculateNum", child->key) == 0)
@@ -503,9 +590,18 @@ static int agg_config_aggregation (oconfig_item_t *ci) /* {{{ */
           "<Aggregation /> blocks and will be ignored.", child->key);
   }
 
+  if (agg_is_regex (agg->ident.host))
+    agg->regex_fields |= LU_GROUP_BY_HOST;
+  if (agg_is_regex (agg->ident.plugin))
+    agg->regex_fields |= LU_GROUP_BY_PLUGIN;
+  if (agg_is_regex (agg->ident.plugin_instance))
+    agg->regex_fields |= LU_GROUP_BY_PLUGIN_INSTANCE;
+  if (agg_is_regex (agg->ident.type_instance))
+    agg->regex_fields |= LU_GROUP_BY_TYPE_INSTANCE;
+
   /* Sanity checking */
   is_valid = 1;
-  if (LU_IS_ALL (agg->ident.type)) /* {{{ */
+  if (strcmp ("/.*/", agg->ident.type) == 0) /* {{{ */
   {
     ERROR ("aggregation plugin: It appears you did not specify the required "
         "\"Type\" option in this aggregation. "
@@ -518,20 +614,31 @@ static int agg_config_aggregation (oconfig_item_t *ci) /* {{{ */
   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 "
+        "character. Especially, it may not be a regex. 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))
+  /* Check that there is at least one regex field without a grouping. {{{ */
+  if ((agg->regex_fields & ~agg->group_by) == 0)
   {
     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. "
+        "or using a regular expression 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;
+  } /* }}} */
+
+  /* Check that all grouping fields are regular expressions. {{{ */
+  if (agg->group_by & ~agg->regex_fields)
+  {
+    ERROR ("aggregation plugin: Only wildcard fields (fields for which a "
+        "regular expression is configured or which are left blank) can be "
+        "specified in the \"GroupBy\" option. "
         "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
         "Type \"%s\", TypeInstance \"%s\")",
         agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
@@ -557,7 +664,7 @@ static int agg_config_aggregation (oconfig_item_t *ci) /* {{{ */
     return (-1);
   } /* }}} */
 
-  status = lookup_add (lookup, &agg->ident, agg);
+  status = lookup_add (lookup, &agg->ident, agg->group_by, agg);
   if (status != 0)
   {
     ERROR ("aggregation plugin: lookup_add failed with status %i.", status);
index 9c8c6e5..41390e9 100644 (file)
@@ -74,10 +74,13 @@ struct camqp_config_s
     char    *prefix;
     char    *postfix;
     char    escape_char;
+    unsigned int graphite_flags;
 
     /* subscribe only */
     char   *exchange_type;
     char   *queue;
+    _Bool   queue_durable;
+    _Bool   queue_auto_delete;
 
     amqp_connection_state_t connection;
     pthread_mutex_t lock;
@@ -313,9 +316,9 @@ static int camqp_setup_queue (camqp_config_t *conf) /* {{{ */
             ? amqp_cstring_bytes (conf->queue)
             : AMQP_EMPTY_BYTES,
             /* passive     = */ 0,
-            /* durable     = */ 0,
+            /* durable     = */ conf->queue_durable,
             /* exclusive   = */ 0,
-            /* auto_delete = */ 1,
+            /* auto_delete = */ conf->queue_auto_delete,
             /* arguments   = */ AMQP_EMPTY_TABLE);
     if (qd_ret == NULL)
     {
@@ -740,7 +743,7 @@ static int camqp_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
 {
     camqp_config_t *conf = user_data->data;
     char routing_key[6 * DATA_MAX_NAME_LEN];
-    char buffer[4096];
+    char buffer[8192];
     int status;
 
     if ((ds == NULL) || (vl == NULL) || (conf == NULL))
@@ -794,7 +797,7 @@ static int camqp_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
     {
         status = format_graphite (buffer, sizeof (buffer), ds, vl,
                     conf->prefix, conf->postfix, conf->escape_char,
-                    conf->store_rates);
+                    conf->graphite_flags);
         if (status != 0)
         {
             ERROR ("amqp plugin: format_graphite failed with status %i.",
@@ -876,6 +879,7 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
     /* publish only */
     conf->delivery_mode = CAMQP_DM_VOLATILE;
     conf->store_rates = 0;
+    conf->graphite_flags = 0;
     /* publish & graphite only */
     conf->prefix = NULL;
     conf->postfix = NULL;
@@ -883,6 +887,8 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
     /* subscribe only */
     conf->exchange_type = NULL;
     conf->queue = NULL;
+    conf->queue_durable = 0;
+    conf->queue_auto_delete = 1;
     /* general */
     conf->connection = NULL;
     pthread_mutex_init (&conf->lock, /* attr = */ NULL);
@@ -922,6 +928,10 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
             status = cf_util_get_string (child, &conf->exchange_type);
         else if ((strcasecmp ("Queue", child->key) == 0) && !publish)
             status = cf_util_get_string (child, &conf->queue);
+        else if ((strcasecmp ("QueueDurable", child->key) == 0) && !publish)
+            status = cf_util_get_boolean (child, &conf->queue_durable);
+        else if ((strcasecmp ("QueueAutoDelete", child->key) == 0) && !publish)
+            status = cf_util_get_boolean (child, &conf->queue_auto_delete);
         else if (strcasecmp ("RoutingKey", child->key) == 0)
             status = cf_util_get_string (child, &conf->routing_key);
         else if ((strcasecmp ("Persistent", child->key) == 0) && publish)
@@ -934,9 +944,19 @@ static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
                 conf->delivery_mode = CAMQP_DM_VOLATILE;
         }
         else if ((strcasecmp ("StoreRates", child->key) == 0) && publish)
+        {
             status = cf_util_get_boolean (child, &conf->store_rates);
+            (void) cf_util_get_flag (child, &conf->graphite_flags,
+                    GRAPHITE_STORE_RATES);
+        }
         else if ((strcasecmp ("Format", child->key) == 0) && publish)
             status = camqp_config_set_format (child, conf);
+        else if ((strcasecmp ("GraphiteSeparateInstances", child->key) == 0) && publish)
+            status = cf_util_get_flag (child, &conf->graphite_flags,
+                    GRAPHITE_SEPARATE_INSTANCES);
+        else if ((strcasecmp ("GraphiteAlwaysAppendDS", child->key) == 0) && publish)
+            status = cf_util_get_flag (child, &conf->graphite_flags,
+                    GRAPHITE_ALWAYS_APPEND_DS);
         else if ((strcasecmp ("GraphitePrefix", child->key) == 0) && publish)
             status = cf_util_get_string (child, &conf->prefix);
         else if ((strcasecmp ("GraphitePostfix", child->key) == 0) && publish)
index c31dd87..c5f099f 100644 (file)
@@ -373,7 +373,7 @@ static int init_host (apache_t *st) /* {{{ */
                return (-1);
        }
 
-       curl_easy_setopt (st->curl, CURLOPT_NOSIGNAL, 1);
+       curl_easy_setopt (st->curl, CURLOPT_NOSIGNAL, 1L);
        curl_easy_setopt (st->curl, CURLOPT_WRITEFUNCTION, apache_curl_callback);
        curl_easy_setopt (st->curl, CURLOPT_WRITEDATA, st);
 
@@ -402,7 +402,7 @@ static int init_host (apache_t *st) /* {{{ */
                curl_easy_setopt (st->curl, CURLOPT_WRITEHEADER, st);
        }
 
-       curl_easy_setopt (st->curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+       curl_easy_setopt (st->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
        curl_easy_setopt (st->curl, CURLOPT_ERRORBUFFER, st->apache_curl_error);
 
        if (st->user != NULL)
@@ -425,24 +425,25 @@ static int init_host (apache_t *st) /* {{{ */
        }
 
        curl_easy_setopt (st->curl, CURLOPT_URL, st->url);
-       curl_easy_setopt (st->curl, CURLOPT_FOLLOWLOCATION, 1);
+       curl_easy_setopt (st->curl, CURLOPT_FOLLOWLOCATION, 1L);
+       curl_easy_setopt (st->curl, CURLOPT_MAXREDIRS, 50L);
 
        if (st->verify_peer != 0)
        {
-               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYPEER, 1);
+               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYPEER, 1L);
        }
        else
        {
-               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYPEER, 0);
+               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYPEER, 0L);
        }
 
        if (st->verify_host != 0)
        {
-               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYHOST, 2);
+               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYHOST, 2L);
        }
        else
        {
-               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYHOST, 0);
+               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYHOST, 0L);
        }
 
        if (st->cacert != NULL)
@@ -611,7 +612,7 @@ static int apache_read_host (user_data_t *user_data) /* {{{ */
        assert (st->curl != NULL);
 
        st->apache_buffer_fill = 0;
-       if (curl_easy_perform (st->curl) != 0)
+       if (curl_easy_perform (st->curl) != CURLE_OK)
        {
                ERROR ("apache: curl_easy_perform failed: %s",
                                st->apache_curl_error);
index 4fbbde6..ee15b14 100644 (file)
@@ -70,6 +70,8 @@ struct apc_detail_s
 /* Default values for contacting daemon */
 static char *conf_host = NULL;
 static int   conf_port = NISPORT;
+/* Defaults to false for backwards compatibility. */
+static _Bool conf_report_seconds = 0;
 
 static int global_sockfd = -1;
 
@@ -81,9 +83,9 @@ static const char *config_keys[] =
 {
        "Host",
        "Port",
-       NULL
+       "ReportSeconds"
 };
-static int config_keys_num = 2;
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
 static int net_shutdown (int *fd)
 {
@@ -360,7 +362,13 @@ static int apc_query_server (char *host, int port,
                        else if (strcmp ("LINEFREQ", key) == 0)
                                apcups_detail->linefreq = value;
                        else if (strcmp ("TIMELEFT", key) == 0)
+                       {
+                               /* Convert minutes to seconds if requested by
+                                * the user. */
+                               if (conf_report_seconds)
+                                       value *= 60.0;
                                apcups_detail->timeleft = value;
+                       }
 
                        tokptr = strtok_r (NULL, ":", &toksaveptr);
                } /* while (tokptr != NULL) */
@@ -403,6 +411,13 @@ static int apcups_config (const char *key, const char *value)
                }
                conf_port = port_tmp;
        }
+       else if (strcasecmp (key, "ReportSeconds") == 0)
+       {
+               if (IS_TRUE (value))
+                       conf_report_seconds = 1;
+               else
+                       conf_report_seconds = 0;
+       }
        else
        {
                return (-1);
@@ -450,7 +465,7 @@ static int apcups_read (void)
        apcups_detail.battv    =   -1.0;
        apcups_detail.loadpct  =   -1.0;
        apcups_detail.bcharge  =   -1.0;
-       apcups_detail.timeleft =   -1.0;
+       apcups_detail.timeleft =    NAN;
        apcups_detail.itemp    = -300.0;
        apcups_detail.linefreq =   -1.0;
 
diff --git a/src/aquaero.c b/src/aquaero.c
new file mode 100644 (file)
index 0000000..5db988e
--- /dev/null
@@ -0,0 +1,193 @@
+/**
+ * collectd - src/aquaero.c
+ * Copyright (C) 2013  Alex Deymo
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Alex Deymo
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <libaquaero5.h>
+
+/*
+ * Private variables
+ */
+/* Default values for contacting daemon */
+static char *conf_device = NULL;
+
+static int aquaero_config (oconfig_item_t *ci)
+{
+       int i;
+
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if (strcasecmp ("Device", child->key))
+                       cf_util_get_string (child, &conf_device);
+               else
+               {
+                       ERROR ("aquaero plugin: Unknown config option \"%s\".",
+                                       child->key);
+               }
+       }
+
+       return (0);
+}
+
+static int aquaero_shutdown (void)
+{
+       libaquaero5_exit();
+       return (0);
+} /* int aquaero_shutdown */
+
+static void aquaero_submit (const char *type, const char *type_instance,
+               double value)
+{
+       const char *instance = conf_device?conf_device:"default";
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       /* Don't report undefined values. */
+       if (value == AQ5_FLOAT_UNDEF)
+               return;
+
+       values[0].gauge = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "aquaero", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, instance, sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* int aquaero_submit */
+
+/* aquaero_submit_array submits every value of a given array of values */
+static void aquaero_submit_array (const char *type,
+               const char *type_instance_prefix, double *value_array, int len)
+{
+       char type_instance[DATA_MAX_NAME_LEN];
+       int i;
+
+       for (i = 0; i < len; i++)
+       {
+               if (value_array[i] == AQ5_FLOAT_UNDEF)
+                       continue;
+
+               snprintf (type_instance, sizeof (type_instance), "%s%d",
+                               type_instance_prefix, i + 1);
+               aquaero_submit (type, type_instance, value_array[i]);
+       }
+}
+
+static int aquaero_read (void)
+{
+       aq5_data_t aq_data;
+       aq5_settings_t aq_sett;
+       char *err_msg = NULL;
+       char type_instance[DATA_MAX_NAME_LEN];
+       int i;
+
+       if (libaquaero5_poll(conf_device, &aq_data, &err_msg) < 0)
+       {
+               char errbuf[1024];
+               ERROR ("aquaero plugin: Failed to poll device \"%s\": %s (%s)",
+                               conf_device ? conf_device : "default", err_msg,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       if (libaquaero5_getsettings(conf_device, &aq_sett, &err_msg) < 0)
+       {
+               char errbuf[1024];
+               ERROR ("aquaero plugin: Failed to get settings "
+                               "for device \"%s\": %s (%s)",
+                               conf_device ? conf_device : "default", err_msg,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       /* CPU Temperature sensor */
+       aquaero_submit("temperature", "cpu", aq_data.cpu_temp[0]);
+
+       /* Temperature sensors */
+       aquaero_submit_array("temperature", "sensor", aq_data.temp,
+                       AQ5_NUM_TEMP);
+
+       /* Virtual temperature sensors */
+       aquaero_submit_array("temperature", "virtual", aq_data.vtemp,
+                       AQ5_NUM_VIRT_SENSORS);
+
+       /* Software temperature sensors */
+       aquaero_submit_array("temperature", "software", aq_data.stemp,
+                       AQ5_NUM_SOFT_SENSORS);
+
+       /* Other temperature sensors */
+       aquaero_submit_array("temperature", "other", aq_data.otemp,
+                       AQ5_NUM_OTHER_SENSORS);
+
+       /* Fans */
+       for (i = 0; i < AQ5_NUM_FAN; i++)
+       {
+               if ((aq_sett.fan_data_source[i] == NONE)
+                               || (aq_data.fan_vrm_temp[i] != AQ5_FLOAT_UNDEF))
+                       continue;
+
+               snprintf (type_instance, sizeof (type_instance),
+                               "fan%d", i + 1);
+
+               aquaero_submit ("fanspeed", type_instance,
+                               aq_data.fan_rpm[i]);
+               aquaero_submit ("percent", type_instance,
+                               aq_data.fan_duty[i]);
+               aquaero_submit ("voltage", type_instance,
+                               aq_data.fan_voltage[i]);
+               aquaero_submit ("current", type_instance,
+                               aq_data.fan_current[i]);
+
+               /* Report the voltage reglator module (VRM) temperature with a
+                * different type instance. */
+               snprintf (type_instance, sizeof (type_instance),
+                               "fan%d-vrm", i + 1);
+               aquaero_submit ("temperature", type_instance,
+                               aq_data.fan_vrm_temp[i]);
+       }
+
+       /* Flow sensors */
+       aquaero_submit_array("flow", "sensor", aq_data.flow, AQ5_NUM_FLOW);
+
+       /* Liquid level */
+       aquaero_submit_array("percent", "waterlevel",
+                       aq_data.level, AQ5_NUM_LEVEL);
+
+       return (0);
+}
+
+void module_register (void)
+{
+       plugin_register_complex_config ("aquaero", aquaero_config);
+       plugin_register_read ("aquaero", aquaero_read);
+       plugin_register_shutdown ("aquaero", aquaero_shutdown);
+} /* void module_register */
+
+/* vim: set sw=8 sts=8 noet : */
index 993e480..6809bac 100644 (file)
@@ -539,9 +539,9 @@ static int ascent_init (void) /* {{{ */
     return (-1);
   }
 
-  curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
+  curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ascent_curl_callback);
-  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, ascent_curl_error);
 
   if (user != NULL)
@@ -561,17 +561,18 @@ static int ascent_init (void) /* {{{ */
   }
 
   curl_easy_setopt (curl, CURLOPT_URL, url);
-  curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
+  curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L);
+  curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 50L);
 
   if ((verify_peer == NULL) || IS_TRUE (verify_peer))
-    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1);
+    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1L);
   else
-    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0);
+    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
 
   if ((verify_host == NULL) || IS_TRUE (verify_host))
-    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2);
+    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2L);
   else
-    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0);
+    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
 
   if (cacert != NULL)
     curl_easy_setopt (curl, CURLOPT_CAINFO, cacert);
@@ -596,7 +597,7 @@ static int ascent_read (void) /* {{{ */
   }
 
   ascent_buffer_fill = 0;
-  if (curl_easy_perform (curl) != 0)
+  if (curl_easy_perform (curl) != CURLE_OK)
   {
     ERROR ("ascent plugin: curl_easy_perform failed: %s",
         ascent_curl_error);
index 4178d8b..691ba5d 100644 (file)
@@ -71,7 +71,7 @@ static const char *battery_acpi_dir = "/proc/acpi/battery";
 static int battery_init (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
-       /* No init neccessary */
+       /* No init necessary */
 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
 
 #elif KERNEL_LINUX
index 5b7d7a0..2f990a2 100644 (file)
@@ -1393,12 +1393,13 @@ static int bind_init (void) /* {{{ */
     return (-1);
   }
 
-  curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
+  curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, bind_curl_callback);
-  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, bind_curl_error);
   curl_easy_setopt (curl, CURLOPT_URL, (url != NULL) ? url : BIND_DEFAULT_URL);
-  curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
+  curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L);
+  curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 50L);
 
   return (0);
 } /* }}} int bind_init */
@@ -1414,7 +1415,7 @@ static int bind_read (void) /* {{{ */
   }
 
   bind_buffer_fill = 0;
-  if (curl_easy_perform (curl) != 0)
+  if (curl_easy_perform (curl) != CURLE_OK)
   {
     ERROR ("bind plugin: curl_easy_perform failed: %s",
         bind_curl_error);
diff --git a/src/cgroups.c b/src/cgroups.c
new file mode 100644 (file)
index 0000000..6d41972
--- /dev/null
@@ -0,0 +1,256 @@
+/**
+ * collectd - src/cgroups.c
+ * Copyright (C) 2011  Michael Stapelberg
+ * Copyright (C) 2013  Florian Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the license is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Michael Stapelberg <michael at stapelberg.de>
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_mount.h"
+#include "utils_ignorelist.h"
+
+static char const *config_keys[] =
+{
+       "CGroup",
+       "IgnoreSelected"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *il_cgroup = NULL;
+
+__attribute__ ((nonnull(1)))
+__attribute__ ((nonnull(2)))
+static void cgroups_submit_one (char const *plugin_instance,
+               char const *type_instance, value_t value)
+{
+       value_list_t vl = VALUE_LIST_INIT;
+
+       vl.values = &value;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "cgroups", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, plugin_instance,
+                       sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "cpu", sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance,
+                       sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* void cgroups_submit_one */
+
+/*
+ * This callback reads the user/system CPU time for each cgroup.
+ */
+static int read_cpuacct_procs (const char *dirname, char const *cgroup_name,
+    void *user_data)
+{
+       char abs_path[PATH_MAX];
+       struct stat statbuf;
+       char buf[1024];
+       int status;
+
+       FILE *fh;
+
+       if (ignorelist_match (il_cgroup, cgroup_name))
+               return (0);
+
+       ssnprintf (abs_path, sizeof (abs_path), "%s/%s", dirname, cgroup_name);
+
+       status = lstat (abs_path, &statbuf);
+       if (status != 0)
+       {
+               ERROR ("cgroups plugin: stat (\"%s\") failed.",
+                               abs_path);
+               return (-1);
+       }
+
+       /* We are only interested in directories, so skip everything else. */
+       if (!S_ISDIR (statbuf.st_mode))
+               return (0);
+
+       ssnprintf (abs_path, sizeof (abs_path), "%s/%s/cpuacct.stat",
+                       dirname, cgroup_name);
+       fh = fopen (abs_path, "r");
+       if (fh == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("cgroups plugin: fopen (\"%s\") failed: %s",
+                               abs_path,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       while (fgets (buf, sizeof (buf), fh) != NULL)
+       {
+               char *fields[8];
+               int numfields = 0;
+               char *key;
+               size_t key_len;
+               value_t value;
+
+               /* Expected format:
+                *
+                *   user: 12345
+                *   system: 23456
+                *
+                * Or:
+                *
+                *   user 12345
+                *   system 23456
+                */
+               strstripnewline (buf);
+               numfields = strsplit (buf, fields, STATIC_ARRAY_SIZE (fields));
+               if (numfields != 2)
+                       continue;
+
+               key = fields[0];
+               key_len = strlen (key);
+               if (key_len < 2)
+                       continue;
+
+               /* Strip colon off the first column, if found */
+               if (key[key_len - 1] == ':')
+                       key[key_len - 1] = 0;
+
+               status = parse_value (fields[1], &value, DS_TYPE_DERIVE);
+               if (status != 0)
+                       continue;
+
+               cgroups_submit_one (cgroup_name, key, value);
+       }
+
+       fclose (fh);
+       return (0);
+} /* int read_cpuacct_procs */
+
+/*
+ * Gets called for every file/folder in /sys/fs/cgroup/cpu,cpuacct (or
+ * wherever cpuacct is mounted on the system). Calls walk_directory with the
+ * read_cpuacct_procs callback on every folder it finds, such as "system".
+ */
+static int read_cpuacct_root (const char *dirname, const char *filename,
+               void *user_data)
+{
+       char abs_path[PATH_MAX];
+       struct stat statbuf;
+       int status;
+
+       ssnprintf (abs_path, sizeof (abs_path), "%s/%s", dirname, filename);
+
+       status = lstat (abs_path, &statbuf);
+       if (status != 0)
+       {
+               ERROR ("cgroups plugin: stat (%s) failed.", abs_path);
+               return (-1);
+       }
+
+       if (S_ISDIR (statbuf.st_mode))
+       {
+               status = walk_directory (abs_path, read_cpuacct_procs,
+                               /* user_data = */ NULL,
+                               /* include_hidden = */ 0);
+               return (status);
+       }
+
+       return (0);
+}
+
+static int cgroups_init (void)
+{
+       if (il_cgroup == NULL)
+               il_cgroup = ignorelist_create (1);
+
+       return (0);
+}
+
+static int cgroups_config (const char *key, const char *value)
+{
+       cgroups_init ();
+
+       if (strcasecmp (key, "CGroup") == 0)
+       {
+               if (ignorelist_add (il_cgroup, value))
+                       return (1);
+               return (0);
+       }
+       else if (strcasecmp (key, "IgnoreSelected") == 0)
+       {
+               if (IS_TRUE (value))
+                       ignorelist_set_invert (il_cgroup, 0);
+               else
+                       ignorelist_set_invert (il_cgroup, 1);
+               return (0);
+       }
+
+       return (-1);
+}
+
+static int cgroups_read (void)
+{
+       cu_mount_t *mnt_list;
+       cu_mount_t *mnt_ptr;
+       _Bool cgroup_found = 0;
+
+       mnt_list = NULL;
+       if (cu_mount_getlist (&mnt_list) == NULL)
+       {
+               ERROR ("cgroups plugin: cu_mount_getlist failed.");
+               return (-1);
+       }
+
+       for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next)
+       {
+               /* Find the cgroup mountpoint which contains the cpuacct
+                * controller. */
+               if ((strcmp(mnt_ptr->type, "cgroup") != 0)
+                               || !cu_mount_checkoption(mnt_ptr->options,
+                                       "cpuacct", /* full = */ 1))
+                       continue;
+
+               walk_directory (mnt_ptr->dir, read_cpuacct_root,
+                               /* user_data = */ NULL,
+                               /* include_hidden = */ 0);
+               cgroup_found = 1;
+               /* It doesn't make sense to check other cpuacct mount-points
+                * (if any), they contain the same data. */
+               break;
+       }
+
+       cu_mount_freelist (mnt_list);
+
+       if (!cgroup_found)
+       {
+               WARNING ("cgroups plugin: Unable to find cgroup "
+                               "mount-point with the \"cpuacct\" option.");
+               return (-1);
+       }
+
+       return (0);
+} /* int cgroup_read */
+
+void module_register (void)
+{
+       plugin_register_config ("cgroups", cgroups_config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("cgroups", cgroups_init);
+       plugin_register_read ("cgroups", cgroups_read);
+} /* void module_register */
index e19d13e..6f58542 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-email - Documentation of collectd's C<email plugin>
index 10f6829..2c59630 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-exec - Documentation of collectd's C<exec plugin>
@@ -116,12 +118,8 @@ case you're confused.
 
 Since examples usually let one understand a lot better, here are some:
 
-  leeloo/cpu-0/cpu-idle N:2299366
-  alice/interface/if_octets-eth0 interval=10 1180647081:421465:479194
-
-Since this action was the only one supported with older versions of the C<exec
-plugin> all lines were treated as if they were prefixed with B<PUTVAL>. This is
-still the case to maintain backwards compatibility but deprecated.
+  PUTVAL leeloo/cpu-0/cpu-idle N:2299366
+  PUTVAL alice/interface/if_octets-eth0 interval=10 1180647081:421465:479194
 
 =item B<PUTNOTIF> [I<OptionList>] B<message=>I<Message>
 
index 9e2f81a..00f209a 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-java - Documentation of collectd's "java plugin"
index d7c749c..aab753c 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-nagios - Nagios plugin for querying collectd
index ee05e00..669e3f4 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-perl - Documentation of collectd's C<perl plugin>
index 39503ad..426a711 100644 (file)
@@ -8,6 +8,8 @@
 # The above copyright notice and this permission notice shall be included in
 # all copies or substantial portions of the Software.
 
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-python - Documentation of collectd's C<python plugin>
index 3c6e799..b3085d3 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-snmp - Documentation of collectd's C<snmp plugin>
@@ -41,6 +43,17 @@ collectd-snmp - Documentation of collectd's C<snmp plugin>
       Community "another_string"
       Collect "std_traffic" "hr_users"
     </Host>
+    <Host "secure.router.mydomain.org">
+      Address "192.168.0.7"
+      Version 3
+      SecurityLevel "authPriv"
+      Username "cosmo"
+      AuthProtocol "SHA"
+      AuthPassphrase "setec_astronomy"
+      PrivacyProtocol "AES"
+      PrivacyPassphrase "too_many_secrets"
+      Collect "std_traffic"
+    </Host>
     <Host "some.ups.mydomain.org">
       Address "192.168.0.3"
       Version 1
@@ -173,7 +186,7 @@ traffic.
 =item B<Scale> I<Value>
 
 The gauge-values returned by the SNMP-agent are multiplied by I<Value>.  This
-is useful when values are transfered as a fixed point real number. For example,
+is useful when values are transferred as a fixed point real number. For example,
 thermometers may transfer B<243> but actually mean B<24.3>, so you can specify
 a scale value of B<0.1> to correct this. The default value is, of course,
 B<1.0>.
@@ -189,6 +202,18 @@ degrees Celsius. The default value is, of course, B<0.0>.
 
 This value is not applied to counter-values.
 
+=item B<Ignore> I<Value> [, I<Value> ...]
+
+The ignore values allows to ignore Instances based on their name and the patterns
+specified by the various values you've entered. The match is a glob-type shell
+matching.
+
+=item B<InvertMatch> I<true|false(default)>
+
+The invertmatch value should be use in combination of the Ignore option.
+It changes the behaviour of the Ignore option, from a blacklist behaviour
+when InvertMatch is set to false, to a whitelist when specified to true.
+
 =back
 
 =head2 The Host block
@@ -205,14 +230,41 @@ stored by collectd.
 
 Set the address to connect to.
 
-=item B<Version> B<1>|B<2>
+=item B<Version> B<1>|B<2>|B<3>
 
 Set the SNMP version to use. When giving B<2> version C<2c> is actually used.
-Version 3 is not supported by this plugin.
 
 =item B<Community> I<Community>
 
-Pass I<Community> to the host.
+Pass I<Community> to the host. (Ignored for SNMPv3).
+
+=item B<Username> I<Username>
+
+Sets the I<Username> to use for SNMPv3 security.
+
+=item B<SecurityLevel> I<authPriv>|I<authNoPriv>|I<noAuthNoPriv>
+
+Selects the security level for SNMPv3 security.
+
+=item B<Context> I<Context>
+
+Sets the I<Context> for SNMPv3 security.
+
+=item B<AuthProtocol> I<MD5>|I<SHA>
+
+Selects the authentication protocol for SNMPv3 security.
+
+=item B<AuthPassphrase> I<Passphrase>
+
+Sets the authentication passphrase for SNMPv3 security. 
+
+=item B<PrivacyProtocol> I<AES>|I<DES>
+
+Selects the privacy (encryption) protocol for SNMPv3 security.
+
+=item B<PrivacyPassphrase> I<Passphrase>
+
+Sets the privacy (encryption) passphrase for SNMPv3 security. 
 
 =item B<Collect> I<Data> [I<Data> ...]
 
@@ -239,8 +291,9 @@ L<snmpgetnext(1)>,
 L<variables(5)>,
 L<unix(7)>
 
-=head1 AUTHOR
+=head1 AUTHORS
 
 Florian Forster E<lt>octo@verplant.orgE<gt>
+Michael Pilat E<lt>mike@mikepilat.comE<gt>
 
 =cut
diff --git a/src/collectd-tg.pod b/src/collectd-tg.pod
new file mode 100644 (file)
index 0000000..97112e7
--- /dev/null
@@ -0,0 +1,67 @@
+=encoding UTF-8
+
+=head1 NAME
+
+collectd-tg - Traffic generator for collectd.
+
+=head1 SYNOPSIS
+
+collectd-tg B<-n> I<num_vl> B<-H> I<num_hosts> B<-p> I<num_plugins> B<-i> I<interval> B<-d> I<dest> B<-D> I<dport>
+
+=head1 DESCRIPTION
+
+B<collectd-tg> generates bogus I<collectd> network traffic. While host, plugin
+and values are generated randomly, the generated traffic tries to mimic "real"
+traffic as closely as possible.
+
+=head1 ARGUMENTS AND OPTIONS
+
+The following options are understood by I<collectd-tg>. The order of the
+arguments generally doesn't matter, as long as no argument is passed more than
+once.
+
+=over 4
+
+=item B<-n> I<num_vl>
+
+Sets the number of unique I<value lists> (VL) to generate. Defaults to 10000.
+
+=item B<-H> I<num_hosts>
+
+Sets the number of unique hosts to simulate. Defaults to 1000.
+
+=item B<-p> I<num_plugins>
+
+Sets the number of unique plugins to simulate. Defaults to 20.
+
+=item B<-i> I<interval>
+
+Sets the interval in which each I<value list> is dispatched. Defaults to 10.0
+seconds.
+
+=item B<-d> I<dest>
+
+Sets the destination to which to send the generated network traffic. Defaults
+to the IPv6 multicast address, C<ff18::efc0:4a42>.
+
+=item B<-D> I<dport>
+
+Sets the destination port or service to which to send the generated network
+traffic. Defaults to I<collectd's> default port, C<25826>.
+
+=item B<-h>
+
+Print usage summary.
+
+=back
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>
+
+=head1 AUTHOR
+
+Florian Forster E<lt>octoE<nbsp>atE<nbsp>collectd.orgE<gt>
+
+=cut
index 02e41b8..35f8a9f 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-threshold - Documentation of collectd's I<Threshold plugin>
@@ -184,10 +186,12 @@ state will be keep.
 
 =item B<Interesting> B<true>|B<false>
 
-If set to B<true> (the default), the threshold must be treated as interesting
-and, when a number of B<Timeout> values will lost, then a missing notification
-will be dispatched. On the other hand, if set to B<false>, the missing
-notification will never dispatched for this threshold.
+If set to B<true> (the default), a notification with severity C<FAILURE> will
+be created when a matching value list is no longer updated and purged from the
+internal cache. When this happens depends on the I<interval> of the value list
+and the global B<Timeout> setting. See the B<Interval> and B<Timeout> settings
+in L<collectd.conf(5)> for details. If set to B<false>, this event will be
+ignored.
 
 =back
 
index 83802a1..40626d3 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-unixsock - Documentation of collectd's C<unixsock plugin>
index d96d676..6c37c61 100644 (file)
@@ -41,6 +41,7 @@
  */
 char hostname_g[DATA_MAX_NAME_LEN];
 cdtime_t interval_g;
+int  pidfile_from_cli = 0;
 int  timeout_g;
 #if HAVE_LIBKSTAT
 kstat_ctl_t *kc;
@@ -118,7 +119,7 @@ static int init_hostname (void)
                ERROR ("Looking up \"%s\" failed. You have set the "
                                "\"FQDNLookup\" option, but I cannot resolve "
                                "my hostname to a fully qualified domain "
-                               "name. Please fix you network "
+                               "name. Please fix the network "
                                "configuration.", hostname_g);
                return (-1);
        }
@@ -439,6 +440,7 @@ int main (int argc, char **argv)
 #if COLLECT_DAEMON
                        case 'P':
                                global_option_set ("PIDFile", optarg);
+                               pidfile_from_cli = 1;
                                break;
                        case 'f':
                                daemonize = 0;
index 87af1c9..a959452 100644 (file)
 #PIDFile     "@localstatedir@/run/@PACKAGE_NAME@.pid"
 #PluginDir   "@libdir@/@PACKAGE_NAME@"
 #TypesDB     "@prefix@/share/@PACKAGE_NAME@/types.db"
+
+#----------------------------------------------------------------------------#
+# When enabled, plugins are loaded automatically with the default options    #
+# when an appropriate <Plugin ...> block is encountered.                     #
+# Disabled by default.                                                       #
+#----------------------------------------------------------------------------#
+#AutoLoadPlugin false
+
+#----------------------------------------------------------------------------#
+# Interval at which to query values. This may be overwritten on a per-plugin #
+# base by using the 'Interval' option of the LoadPlugin block:               #
+#   <LoadPlugin foo>                                                         #
+#       Interval 60                                                          #
+#   </LoadPlugin>                                                            #
+#----------------------------------------------------------------------------#
 #Interval     10
+
 #Timeout      2
 #ReadThreads  5
+#WriteThreads 5
+
+# Limit the size of the write queue. Default is no limit. Setting up a limit is
+# recommended for servers handling a high volume of traffic.
+#WriteQueueLimitHigh 1000000
+#WriteQueueLimitLow   800000
 
 ##############################################################################
 # Logging                                                                    #
@@ -30,6 +52,7 @@
 
 @LOAD_PLUGIN_SYSLOG@LoadPlugin syslog
 @LOAD_PLUGIN_LOGFILE@LoadPlugin logfile
+@LOAD_PLUGIN_LOG_LOGSTASH@LoadPlugin log_logstash
 
 #<Plugin logfile>
 #      LogLevel @DEFAULT_LOG_LEVEL@
 #      PrintSeverity false
 #</Plugin>
 
+#<Plugin log_logstash>
+#      LogLevel @DEFAULT_LOG_LEVEL@
+#      File "@localstatedir@/log/@PACKAGE_NAME@.json.log"
+#</Plugin>
+
 #<Plugin syslog>
 #      LogLevel @DEFAULT_LOG_LEVEL@
 #</Plugin>
 #@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
 #@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
 #@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors
+#@BUILD_PLUGIN_AQUAERO_TRUE@LoadPlugin aquaero
 #@BUILD_PLUGIN_ASCENT_TRUE@LoadPlugin ascent
 #@BUILD_PLUGIN_BATTERY_TRUE@LoadPlugin battery
 #@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind
 #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack
 #@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch
+#@BUILD_PLUGIN_CGROUPS_TRUE@LoadPlugin cgroups
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 #@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq
 @LOAD_PLUGIN_CSV@LoadPlugin csv
 #@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt
 @BUILD_PLUGIN_LOAD_TRUE@@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load
 #@BUILD_PLUGIN_LPAR_TRUE@LoadPlugin lpar
+#@BUILD_PLUGIN_LVM_TRUE@LoadPlugin lvm
 #@BUILD_PLUGIN_MADWIFI_TRUE@LoadPlugin madwifi
 #@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon
 #@BUILD_PLUGIN_MD_TRUE@LoadPlugin md
 @LOAD_PLUGIN_RRDTOOL@LoadPlugin rrdtool
 #@BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors
 #@BUILD_PLUGIN_SERIAL_TRUE@LoadPlugin serial
+#@BUILD_PLUGIN_SIGROK_TRUE@LoadPlugin sigrok
 #@BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp
+#@BUILD_PLUGIN_STATSD_TRUE@LoadPlugin statsd
 #@BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap
 #@BUILD_PLUGIN_TABLE_TRUE@LoadPlugin table
 #@BUILD_PLUGIN_TAIL_TRUE@LoadPlugin tail
+#@BUILD_PLUGIN_TAIL_CSV_TRUE@LoadPlugin tail_csv
 #@BUILD_PLUGIN_TAPE_TRUE@LoadPlugin tape
 #@BUILD_PLUGIN_TCPCONNS_TRUE@LoadPlugin tcpconns
 #@BUILD_PLUGIN_TEAMSPEAK2_TRUE@LoadPlugin teamspeak2
 #@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
 #@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
 #@BUILD_PLUGIN_VARNISH_TRUE@LoadPlugin varnish
+#@BUILD_PLUGIN_MIC_TRUE@LoadPlugin mic
 #@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
 #@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
 #@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless
 #@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_MONGODB_TRUE@LoadPlugin write_mongodb
+#@BUILD_PLUGIN_WRITE_REDIS_TRUE@LoadPlugin write_redis
+#@BUILD_PLUGIN_WRITE_RIEMANN_TRUE@LoadPlugin write_riemann
 #@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">
+#<Plugin aggregation>
 #  <Aggregation>
 #    #Host "unspecified"
 #    Plugin "cpu"
 #  </Aggregation>
 #</Plugin>
 
-#<Plugin "amqp">
+#<Plugin amqp>
 #  <Publish "name">
 #    Host "localhost"
 #    Port "5672"
 #<Plugin apcups>
 #      Host "localhost"
 #      Port "3551"
+#      ReportSeconds true
+#</Plugin>
+
+#<Plugin aquaero>
+#      Device ""
 #</Plugin>
 
 #<Plugin ascent>
 #      CACert "/etc/ssl/ca.crt"
 #</Plugin>
 
-#<Plugin "bind">
+#<Plugin bind>
 #  URL "http://localhost:8053/"
 #  ParseTime       false
 #  OpCodes         true
 #  </View>
 #</Plugin>
 
+#<Plugin cgroup>
+#  CGroup "libvirt"
+#  IgnoreSelected false
+#</Plugin>
+
+#<Plugin cpu>
+#  ReportActive false
+#  ReportByCpu true
+#  ValuesPercentage false
+#</Plugin>
+#
 #<Plugin csv>
 #      DataDir "@localstatedir@/lib/@PACKAGE_NAME@/csv"
 #      StoreRates false
 #  </URL>
 #</Plugin>
 
-#<Plugin "curl_xml">
+#<Plugin curl_xml>
 #  <URL "http://localhost/stats.xml">
 #    Host "my_host"
 #    Instance "some_instance"
 #              #SelectDB "custdb0"
 #              Query "num_of_customers"
 #              #Query "..."
+#              #Host "..."
 #      </Database>
 #</Plugin>
 
 #      ReportByDevice false
 #      ReportReserved false
 #      ReportInodes false
+#      ValuesAbsolute true
+#      ValuesPercentage false
 #</Plugin>
 
 #<Plugin disk>
 #      Disk "/^[hs]d[a-f][0-9]?$/"
 #      IgnoreSelected false
+#      UseBSDName false
+#      UdevNameAttr "DEVNAME"
 #</Plugin>
 
 #<Plugin dns>
 #      </Directory>
 #</Plugin>
 
-#<Plugin "gmond">
+#<Plugin gmond>
 #  MCReceiveFrom "239.2.11.71" "8649"
 #  <Metric "swap_total">
 #    Type "swap"
 #      IgnoreSelected true
 #</Plugin>
 
-#<Plugin "java">
+#<Plugin java>
 #      JVMArg "-verbose:jni"
 #      JVMArg "-Djava.class.path=@prefix@/share/collectd/java/collectd-api.jar"
 #
 #      InterfaceFormat name
 #</Plugin>
 
+#<Plugin load>
+#        ReportRelative true
+#</Plugin>
+
 #<Plugin lpar>
 #      CpuPoolStats   false
 #      ReportBySerial false
 #      </Instance>
 #</Plugin>
 
+#<Plugin memory>
+#      ValuesAbsolute true
+#      ValuesPercentage false
+#</Plugin>
+
 #<Plugin modbus>
 #      <Data "data_name">
 #              RegisterBase 1234
 #                      ValuesFrom "count"
 #              </Result>
 #      </Query>
+#      <Writer sqlstore>
+#              # See contrib/postgresql/collectd_insert.sql for details
+#              Statement "SELECT collectd_insert($1, $2, $3, $4, $5, $6, $7, $8, $9);"
+#              StoreRates true
+#      </Writer>
 #      <Database foo>
 #              Host "hostname"
 #              Port "5432"
 #              Query backend # predefined
 #              Query rt36_tickets
 #      </Database>
+#      <Database qux>
+#              Service "collectd_store"
+#              Writer sqlstore
+#              # see collectd.conf(5) for details
+#              CommitInterval 30
+#      </Database>
 #</Plugin>
 
 #<Plugin powerdns>
 #      DaemonAddress "unix:/tmp/rrdcached.sock"
 #      DataDir "@localstatedir@/lib/@PACKAGE_NAME@/rrd"
 #      CreateFiles true
+#      CreateFilesAsync false
 #      CollectStatistics true
 #</Plugin>
 
 #<Plugin rrdtool>
 #      DataDir "@localstatedir@/lib/@PACKAGE_NAME@/rrd"
+#      CreateFilesAsync false
 #      CacheTimeout 120
 #      CacheFlush   900
+#      WritesPerSecond 50
 #</Plugin>
 
 #<Plugin sensors>
 #      IgnoreSelected false
 #</Plugin>
 
+#<Plugin sigrok>
+#  LogLevel 3
+#  <Device "AC Voltage">
+#    Driver "fluke-dmm"
+#    MinimumInterval 10
+#    Conn "/dev/ttyUSB2"
+#  </Device>
+#  <Device "Sound Level">
+#    Driver "cem-dt-885x"
+#    Conn "/dev/ttyUSB1"
+#  </Device>
+#</Plugin>
+
 #<Plugin snmp>
 #   <Data "powerplus_voltge_input">
 #       Type "voltage"
 #   </Host>
 #</Plugin>
 
-#<Plugin "swap">
+#<Plugin statsd>
+#  Host "::"
+#  Port "8125"
+#  DeleteCounters false
+#  DeleteTimers   false
+#  DeleteGauges   false
+#  DeleteSets     false
+#  TimerPercentile 90.0
+#</Plugin>
+
+#<Plugin swap>
 #      ReportByDevice false
 #      ReportBytes true
+#      ValuesAbsolute true
+#      ValuesPercentage false
 #</Plugin>
 
-#<Plugin "table">
+#<Plugin table>
 #      <Table "/proc/slabinfo">
 #              Instance "slabinfo"
 #              Separator " "
 #      </Table>
 #</Plugin>
 
-#<Plugin "tail">
+#<Plugin tail>
 #  <File "/var/log/exim4/mainlog">
 #    Instance "exim"
+#    Interval 60
 #    <Match>
 #      Regex "S=([1-9][0-9]*)"
 #      DSType "CounterAdd"
 #  </File>
 #</Plugin>
 
+#<Plugin tail_csv>
+#   <Metric "dropped">
+#       Type "percent"
+#       Instance "dropped"
+#       ValueFrom 1
+#   </Metric>
+#   <Metric "mbps">
+#       Type "bytes"
+#       Instance "wire-realtime"
+#       ValueFrom 2
+#   </Metric>
+#   <Metric "alerts">
+#       Type "alerts_per_second"
+#       ValueFrom 3
+#   </Metric>
+#   <Metric "kpps">
+#       Type "kpackets_wire_per_sec.realtime"
+#       ValueFrom 4
+#   </Metric>
+#   <File "/var/log/snort/snort.stats">
+#       Instance "snort-eth0"
+#       Interval 600
+#       Collect "dropped" "mbps" "alerts" "kpps"
+#       TimeFrom 0
+#   </File>
+#</Plugin>
+
 #<Plugin tcpconns>
 #      ListeningPorts false
 #      LocalPort "25"
 #      UUIDFile "/etc/uuid"
 #</Plugin>
 
+#<Plugin mic>
+#   ShowCPU true
+#   ShowCPUCores true
+#   ShowMemory true
+#   ShowTemperatures true
+##  Temperature Sensors can be ignored/shown by repeated #Temperature lines, and
+##  then inverted with a IgnoreSelectedTemperature.
+##  Known Temperature sensors: die, devmem, fin, fout, vccp, vddg, vddq
+#   Temperature vddg
+#   IgnoreSelectedTemperature true
+#   ShowPower true
+##  Power Sensors can be ignored/shown by repeated #Power lines, and
+##  then inverted with a IgnoreSelectedTemperature.
+##  Known Temperature sensors: total0, total1, inst, imax, pci3, c2x3, c2x4, vccp, vddg, vddq
+#   Power total1
+#   IgnoreSelectedPower true
+#</Plugin>
+
 #<Plugin varnish>
 #   This tag support an argument if you want to
 #   monitor the local instance just use </Instance>
 #   <Instance>
 #      CollectCache true
 #      CollectBackend true
+#      CollectBan false           # Varnish 3 only
 #      CollectConnections true
+#      CollectDirectorDNS false   # Varnish 3 only
 #      CollectSHM true
 #      CollectESI false
 #      CollectFetch false
 #      CollectHCB false
-#      CollectSMA false
+#      CollectObjects false
+#      CollectPurge false         # Varnish 2 only
+#      CollectSession false
+#      CollectSMA false           # Varnish 2 only
 #      CollectSMS false
-#      CollectSM false
+#      CollectSM false            # Varnish 2 only
+#      CollectStruct false
 #      CollectTotals false
+#      CollectUptime false
+#      CollectVCL false
 #      CollectWorkers false
 #   </Instance>
 #</Plugin>
 #</Plugin>
 
 #<Plugin write_graphite>
-#  <Carbon>
+#  <Node "example">
 #    Host "localhost"
 #    Port "2003"
+#    Protocol "tcp"
+#    LogSendErrors true
 #    Prefix "collectd"
 #    Postfix "collectd"
-#    StoreRates false
+#    StoreRates true
 #    AlwaysAppendDS false
 #    EscapeCharacter "_"
-#  </Carbon>
+#  </Node>
 #</Plugin>
 
 #<Plugin write_http>
 #              VerifyPeer true
 #              VerifyHost true
 #              CACert "/etc/ssl/ca.crt"
+#              CAPath "/etc/ssl/certs/"
+#              ClientKey "/etc/ssl/client.pem"
+#              ClientCert "/etc/ssl/client.crt"
+#              ClientKeyPass "secret"
+#              SSLVersion "TLSv1"
 #              Format "Command"
 #              StoreRates false
 #      </URL>
 #</Plugin>
 
+#<Plugin write_mongodb>
+#      <Node "example">
+#              Host "localhost"
+#              Port "27017"
+#              Timeout 1000
+#              StoreRates false
+#              Database "auth_db"
+#              User "auth_user"
+#              Password "auth_passwd"
+#      </Node>
+#</Plugin>
+
 #<Plugin write_redis>
 #      <Node "example">
 #              Host "localhost"
 #      </Node>
 #</Plugin>
 
-#<Plugin write_mongodb>
+#<Plugin write_riemann>
 #      <Node "example">
 #              Host "localhost"
-#              Port "27017"
-#              Timeout 1000
-#              StoreRates false
+#              Port 5555
+#              Protocol UDP
+#              StoreRates true
+#              AlwaysAppendDS false
+#              TTLFactor 2.0
 #      </Node>
+#      Tag "foobar"
+#       Attribute "foo" "bar"
 #</Plugin>
 
 ##############################################################################
 ##############################################################################
 
 #@BUILD_PLUGIN_THRESHOLD_TRUE@LoadPlugin "threshold"
-#<Plugin "threshold">
+#<Plugin threshold>
 #  <Type "foo">
 #    WarningMin    0.00
 #    WarningMax 1000.00
index 8a2fc16..cd98685 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd.conf - Configuration for the system statistics collection daemon B<collectd>
@@ -10,8 +12,12 @@ collectd.conf - Configuration for the system statistics collection daemon B<coll
 
   LoadPlugin cpu
   LoadPlugin load
-  LoadPlugin ping
 
+  <LoadPlugin df>
+    Interval 3600
+  </LoadPlugin>
+
+  LoadPlugin ping
   <Plugin ping>
     Host "example.org"
     Host "provider.net"
@@ -64,16 +70,33 @@ directory for the daemon.
 
 =item B<LoadPlugin> I<Plugin>
 
-Loads the plugin I<Plugin>. There must be at least one such line or B<collectd>
-will be mostly useless.
+Loads the plugin I<Plugin>. This is required to load plugins, unless the
+B<AutoLoadPlugin> option is enabled (see below). Without any loaded plugins,
+I<collectd> will be mostly useless.
 
-Starting with collectd 4.9, this may also be a block in which further options
-affecting the behavior of B<LoadPlugin> may be specified. The following
-options are allowed inside a B<LoadPlugin> block:
+Only the first B<LoadPlugin> statement or block for a given plugin name has any
+effect. This is useful when you want to split up the configuration into smaller
+files and want each file to be "self contained", i.e. it contains a B<Plugin>
+block I<and> then appropriate B<LoadPlugin> statement. The downside is that if
+you have multiple conflicting B<LoadPlugin> blocks, e.g. when they specify
+different intervals, only one of them (the first one encountered) will take
+effect and all others will be silently ignored.
 
-  <LoadPlugin perl>
-    Globals true
-  </LoadPlugin>
+B<LoadPlugin> may either be a simple configuration I<statement> or a I<block>
+with additional options, affecting the behavior of B<LoadPlugin>. A simple
+statement looks like this:
+
+ LoadPlugin "cpu"
+
+Options inside a B<LoadPlugin> block can override default settings and
+influence the way plugins are loaded, e.g.:
+
+ <LoadPlugin perl>
+   Globals true
+   Interval 60
+ </LoadPlugin>
+
+The following options are valid inside B<LoadPlugin> blocks:
 
 =over 4
 
@@ -104,7 +127,20 @@ interval, that setting will take precedence.
 
 =back
 
-=item B<Include> I<Path>
+=item B<AutoLoadPlugin> B<false>|B<true>
+
+When set to B<false> (the default), each plugin needs to be loaded explicitly,
+using the B<LoadPlugin> statement documented above. If a
+B<E<lt>PluginE<nbsp>...E<gt>> block is encountered and no configuration
+handling callback for this plugin has been registered, a warning is logged and
+the block is ignored.
+
+When set to B<true>, explicit B<LoadPlugin> statements are not required. Each
+B<E<lt>PluginE<nbsp>...E<gt>> block acts as if it was immediately preceded by a
+B<LoadPlugin> statement. B<LoadPlugin> statements are still required for
+plugins that don't provide any configuration, e.g. the I<Load plugin>.
+
+=item B<Include> I<Path> [I<pattern>]
 
 If I<Path> points to a file, includes that file. If I<Path> points to a
 directory, recursively includes all files within that directory and its
@@ -114,6 +150,29 @@ use statements like the following:
 
   Include "/etc/collectd.d/*.conf"
 
+Starting with version 5.3, this may also be a block in which further options
+affecting the behavior of B<Include> may be specified. The following option is
+currently allowed:
+
+  <Include "/etc/collectd.d">
+    Filter "*.conf"
+  </Include>
+
+=over 4
+
+=item B<Filter> I<pattern>
+
+If the C<fnmatch> function is available on your system, a shell-like wildcard
+I<pattern> may be specified to filter which files to include. This may be used
+in combination with recursively including a directory to easily be able to
+arbitrarily mix configuration files and other documents (e.g. README files).
+The given example is similar to the first example above but includes all files
+matching C<*.conf> in any subdirectory of C</etc/collectd.d>:
+
+  Include "/etc/collectd.d" "*.conf"
+
+=back
+
 If more than one files are included by a single B<Include> option, the files
 will be included in lexicographical order (as defined by the C<strcmp>
 function). Thus, you can e.E<nbsp>g. use numbered prefixes to specify the
@@ -167,8 +226,44 @@ see L<collectd-threshold(5)> for details.
 
 Number of threads to start for reading plugins. The default value is B<5>, but
 you may want to increase this if you have more than five plugins that take a
-long time to read. Mostly those are plugin that do network-IO. Setting this to
-a value higher than the number of plugins you've loaded is totally useless.
+long time to read. Mostly those are plugins that do network-IO. Setting this to
+a value higher than the number of registered read callbacks is not recommended.
+
+=item B<WriteThreads> I<Num>
+
+Number of threads to start for dispatching value lists to write plugins. The
+default value is B<5>, but you may want to increase this if you have more than
+five plugins that may take relatively long to write to.
+
+=item B<WriteQueueLimitHigh> I<HighNum>
+
+=item B<WriteQueueLimitLow> I<LowNum>
+
+Metrics are read by the I<read threads> and then put into a queue to be handled
+by the I<write threads>. If one of the I<write plugins> is slow (e.g. network
+timeouts, I/O saturation of the disk) this queue will grow. In order to avoid
+running into memory issues in such a case, you can limit the size of this
+queue.
+
+By default, there is no limit and memory may grow indefinitely. This is most
+likely not an issue for clients, i.e. instances that only handle the local
+metrics. For servers it is recommended to set this to a non-zero value, though.
+
+You can set the limits using B<WriteQueueLimitHigh> and B<WriteQueueLimitLow>.
+Each of them takes a numerical argument which is the number of metrics in the
+queue. If there are I<HighNum> metrics in the queue, any new metrics I<will> be
+dropped. If there are less than I<LowNum> metrics in the queue, all new metrics
+I<will> be enqueued. If the number of metrics currently in the queue is between
+I<LowNum> and I<HighNum>, the metric is dropped with a probability that is
+proportional to the number of metrics in the queue (i.e. it increases linearly
+until it reaches 100%.)
+
+If B<WriteQueueLimitHigh> is set to non-zero and B<WriteQueueLimitLow> is
+unset, the latter will default to half of B<WriteQueueLimitHigh>.
+
+If you do not want to randomly drop values when the queue size is between
+I<LowNum> and I<HighNum>, set If B<WriteQueueLimitHigh> and
+B<WriteQueueLimitLow> to same value.
 
 =item B<Hostname> I<Name>
 
@@ -246,10 +341,10 @@ The full example configuration looks like this:
    <Aggregation>
      Plugin "cpu"
      Type "cpu"
-     
+
      GroupBy "Host"
      GroupBy "TypeInstance"
-     
+
      CalculateSum true
      CalculateAverage true
    </Aggregation>
@@ -292,11 +387,72 @@ aggregations. The following options are valid inside B<Aggregation> blocks:
 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.
 
+If the string starts with and ends with a slash (C</>), the string is
+interpreted as a I<regular expression>. The regex flavor used are POSIX
+extended regular expressions as described in L<regex(7)>. Example usage:
+
+ Host "/^db[0-9]\\.example\\.com$/"
+
 =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<SetHost> I<Host>
+
+=item B<SetPlugin> I<Plugin>
+
+=item B<SetPluginInstance> I<PluginInstance>
+
+=item B<SetTypeInstance> I<TypeInstance>
+
+Sets the appropriate part of the identifier to the provided string.
+
+The I<PluginInstance> should include the placeholder C<%{aggregation}> which
+will be replaced with the aggregation function, e.g. "average". Not including
+the placeholder will result in duplication warnings and/or messed up values if
+more than one aggregation function are enabled.
+
+The following example calculates the average usage of all "even" CPUs:
+
+ <Plugin "aggregation">
+   <Aggregation>
+     Plugin "cpu"
+     PluginInstance "/[0,2,4,6,8]$/"
+     Type "cpu"
+
+     SetPlugin "cpu"
+     SetPluginInstance "even-%{aggregation}"
+
+     GroupBy "Host"
+     GroupBy "TypeInstance"
+
+     CalculateAverage true
+   </Aggregation>
+ </Plugin>
+
+This will create the files:
+
+=over 4
+
+=item
+
+foo.example.com/cpu-even-average/cpu-idle
+
+=item
+
+foo.example.com/cpu-even-average/cpu-system
+
+=item
+
+foo.example.com/cpu-even-average/cpu-user
+
+=item
+
+...
+
+=back
+
 =item B<CalculateNum> B<true>|B<false>
 
 =item B<CalculateSum> B<true>|B<false>
@@ -339,7 +495,7 @@ possibly filtering or messages.
  #   GraphitePrefix "collectd."
  #   GraphiteEscapeChar "_"
    </Publish>
-   
+
    # Receive values from an AMQP broker
    <Subscribe "some_name">
      Host "localhost"
@@ -350,6 +506,8 @@ possibly filtering or messages.
      Exchange "amq.fanout"
  #   ExchangeType "fanout"
  #   Queue "queue_name"
+ #   QueueDurable false
+ #   QueueAutoDelete true
  #   RoutingKey "collectd.#"
    </Subscribe>
  </Plugin>
@@ -402,9 +560,23 @@ be bound to this exchange.
 
 =item B<Queue> I<Queue> (Subscribe only)
 
-Configures the I<queue> name to subscribe to. If no queue name was configures
+Configures the I<queue> name to subscribe to. If no queue name was configured
 explicitly, a unique queue name will be created by the broker.
 
+=item B<QueueDurable> B<true>|B<false> (Subscribe only)
+
+Defines if the I<queue> subscribed to is durable (saved to persistent storage)
+or transient (will disappear if the AMQP broker is restarted). Defaults to
+"false".
+
+This option should be used in conjunction with the I<Persistent> option on the
+publish side.
+
+=item B<QueueAutoDelete> B<true>|B<false> (Subscribe only)
+
+Defines if the I<queue> subscribed to will be deleted once the last consumer
+unsubscribes. Defaults to "true".
+
 =item B<RoutingKey> I<Key>
 
 In I<Publish> blocks, this configures the routing key to set on all outgoing
@@ -427,7 +599,7 @@ mode will be used, i.e. delivery is guaranteed. If set to B<false> (the
 default), the I<transient> delivery mode will be used, i.e. messages may be
 lost due to high load, overflowing queues or similar issues.
 
-=item B<Format> B<Command>|B<JSON> (Publish only)
+=item B<Format> B<Command>|B<JSON>|B<Graphite> (Publish only)
 
 Selects the format in which messages are sent to the broker. If set to
 B<Command> (the default), values are sent as C<PUTVAL> commands which are
@@ -566,6 +738,35 @@ B<apcupsd> can handle it.
 
 TCP-Port to connect to. Defaults to B<3551>.
 
+=item B<ReportSeconds> B<true|false>
+
+If set to B<true>, the time reported in the C<timeleft> metric will be
+converted to seconds. This is the recommended setting. If set to B<false>, the
+default for backwards compatibility, the time will be reported in minutes.
+
+=back
+
+=head2 Plugin C<aquaero>
+
+This plugin collects the value of the available sensors in an
+I<AquaeroE<nbsp>5> board. AquaeroE<nbsp>5 is a water-cooling controller board,
+manufactured by Aqua Computer GmbH L<http://www.aquacomputer.de/>, with a USB2
+connection for monitoring and configuration. The board can handle multiple
+temperature sensors, fans, water pumps and water level sensors and adjust the
+output settings such as fan voltage or power used by the water pump based on
+the available inputs using a configurable controller included in the board.
+This plugin collects all the available inputs as well as some of the output
+values chosen by this controller. The plugin is based on the I<libaquaero5>
+library provided by I<aquatools-ng>.
+
+=over 4
+
+=item B<Device> I<DevicePath>
+
+Device path of the AquaeroE<nbsp>5's USB HID (human interface device), usually
+in the form C</dev/usb/hiddevX>. If this option is no set the plugin will try
+to auto-detect the Aquaero 5 USB device based on vendor-ID and product-ID.
+
 =back
 
 =head2 Plugin C<ascent>
@@ -637,17 +838,17 @@ Synopsis:
    ParseTime       false
    OpCodes         true
    QTypes          true
+
    ServerStats     true
    ZoneMaintStats  true
    ResolverStats   false
    MemoryStats     true
+
    <View "_default">
      QTypes        true
      ResolverStats true
      CacheRRSets   true
+
      Zone "127.in-addr.arpa/IN"
    </View>
  </Plugin>
@@ -765,6 +966,54 @@ By default no detailed zone information is collected.
 
 =back
 
+=head2 Plugin C<cgroups>
+
+This plugin collects the CPU user/system time for each I<cgroup> by reading the
+F<cpuacct.stat> files in the first cpuacct-mountpoint (typically
+F</sys/fs/cgroup/cpu.cpuacct> on machines using systemd).
+
+=over 4
+
+=item B<CGroup> I<Directory>
+
+Select I<cgroup> based on the name. Whether only matching I<cgroups> are
+collected or if they are ignored is controlled by the B<IgnoreSelected> option;
+see below.
+
+=item B<IgnoreSelected> B<true>|B<false>
+
+Invert the selection: If set to true, all cgroups I<except> the ones that
+match any one of the criteria are collected. By default only selected
+cgroups are collected if a selection is made. If no selection is configured
+at all, B<all> cgroups are selected.
+
+=back
+
+=head2 Plugin C<cpu>
+
+The I<CPU plugin> collects CPU usage metrics.
+
+The following configuration options are available:
+
+=over 4
+
+=item B<ReportActive> B<false>|B<true>
+
+Reports non-idle CPU usage as the "active" value. Defaults to false.
+
+=item B<ReportByCpu> B<false>|B<true>
+
+When true reports usage for all cores. When false, reports cpu usage
+aggregated over all cores. Implies ValuesPercentage when false.
+Defaults to true.
+
+=item B<ValuesPercentage> B<false>|B<true>
+
+When true report percentage usage instead of tick values. Defaults to false.
+
+=back
+
+
 =head2 Plugin C<cpufreq>
 
 This plugin doesn't have any options. It reads
@@ -838,6 +1087,10 @@ Username to use if authorization is required to read the page.
 
 Password to use if authorization is required to read the page.
 
+=item B<Digest> B<true>|B<false>
+
+Enable HTTP digest authentication.
+
 =item B<VerifyPeer> B<true>|B<false>
 
 Enable or disable peer SSL certificate verification. See
@@ -857,6 +1110,19 @@ File that holds one or more SSL certificates. If you want to use HTTPS you will
 possibly need this option. What CA certificates come bundled with C<libcurl>
 and are checked by default depends on the distribution you use.
 
+=item B<Header> I<Header>
+
+A HTTP header to add to the request. Multiple headers are added if this option
+is specified more than once.
+
+=item B<Post> I<Body>
+
+Specifies that the HTTP operation should be a POST instead of a GET. The
+complete data to be posted is given as the argument.  This option will usually
+need to be accompanied by a B<Header> option to set an appropriate
+C<Content-Type> for the post body (e.g. to
+C<application/x-www-form-urlencoded>).
+
 =item B<MeasureResponseTime> B<true>|B<false>
 
 Measure response time for the request. If this setting is enabled, B<Match>
@@ -874,13 +1140,15 @@ is set to B<true>, B<Match> blocks are optional.
 
 =head2 Plugin C<curl_json>
 
-The B<curl_json plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and
-B<libyajl> (L<http://www.lloydforge.org/projects/yajl/>) to retrieve JSON data
-via cURL. This can be used to collect values from CouchDB documents (which are
-stored JSON notation), for example.
+The B<curl_json plugin> collects values from JSON data to be parsed by
+B<libyajl> (L<http://www.lloydforge.org/projects/yajl/>) retrieved via
+either B<libcurl> (L<http://curl.haxx.se/>) or read directly from a
+unix socket. The former can be used, for example, to collect values
+from CouchDB documents (which are stored JSON notation), and the
+latter to collect values from a uWSGI stats socket.
 
-The following example will collect several values from the built-in `_stats'
-runtime statistics module of CouchDB
+The following example will collect several values from the built-in
+C<_stats> runtime statistics module of I<CouchDB>
 (L<http://wiki.apache.org/couchdb/Runtime_Statistics>).
 
   <Plugin curl_json>
@@ -900,11 +1168,30 @@ runtime statistics module of CouchDB
     </URL>
   </Plugin>
 
-In the B<Plugin> block, there may be one or more B<URL> blocks, each defining
-a URL to be fetched via HTTP (using libcurl) and one or more B<Key> blocks.
-The B<Key> string argument must be in a path format, which is used to collect a
-value from a JSON map object. If a path element of B<Key> is the
-I<*>E<nbsp>wildcard, the values for all keys will be collectd.
+This example will collect data directly from a I<uWSGI> "Stats Server" socket.
+
+  <Plugin curl_json>
+    <Sock "/var/run/uwsgi.stats.sock">
+      Instance "uwsgi"
+      <Key "workers/*/requests">
+        Type "http_requests"
+      </Key>
+
+      <Key "workers/*/apps/*/requests">
+        Type "http_requests"
+      </Key>
+    </Sock>
+  </Plugin>
+
+In the B<Plugin> block, there may be one or more B<URL> blocks, each
+defining a URL to be fetched via HTTP (using libcurl) or B<Sock>
+blocks defining a unix socket to read JSON from directly.  Each of
+these blocks may have one or more B<Key> blocks.
+
+The B<Key> string argument must be in a path format. Each component is
+used to match the key from a JSON map or the index of an JSON
+array. If a path component of a B<Key> is a I<*>E<nbsp>wildcard, the
+values for all map keys or array indices will be collectd.
 
 The following options are valid within B<URL> blocks:
 
@@ -916,30 +1203,22 @@ Sets the plugin instance to I<Instance>.
 
 =item B<User> I<Name>
 
-Username to use if authorization is required to read the page.
-
 =item B<Password> I<Password>
 
-Password to use if authorization is required to read the page.
+=item B<Digest> B<true>|B<false>
 
 =item B<VerifyPeer> B<true>|B<false>
 
-Enable or disable peer SSL certificate verification. See
-L<http://curl.haxx.se/docs/sslcerts.html> for details. Enabled by default.
-
 =item B<VerifyHost> B<true>|B<false>
 
-Enable or disable peer host name verification. If enabled, the plugin checks if
-the C<Common Name> or a C<Subject Alternate Name> field of the SSL certificate
-matches the host name provided by the B<URL> option. If this identity check
-fails, the connection is aborted. Obviously, only works when connecting to a
-SSL enabled server. Enabled by default.
-
 =item B<CACert> I<file>
 
-File that holds one or more SSL certificates. If you want to use HTTPS you will
-possibly need this option. What CA certificates come bundled with C<libcurl>
-and are checked by default depends on the distribution you use.
+=item B<Header> I<Header>
+
+=item B<Post> I<Body>
+
+These options behave exactly equivalent to the appropriate options of the
+I<cURL> plugin. Please see there for a detailed description.
 
 =back
 
@@ -1008,14 +1287,36 @@ host name setting.
 Use I<Instance> as the plugin instance when submitting values. Defaults to an
 empty string (no plugin instance).
 
+=item B<Namespace> I<Prefix> I<URL>
+
+If an XPath expression references namespaces, they must be specified
+with this option. I<Prefix> is the "namespace prefix" used in the XML document.
+I<URL> is the "namespace name", an URI reference uniquely identifying the
+namespace. The option can be repeated to register multiple namespaces.
+
+Examples:
+
+  Namespace "s" "http://schemas.xmlsoap.org/soap/envelope/"
+  Namespace "m" "http://www.w3.org/1998/Math/MathML"
+
 =item B<User> I<User>
+
 =item B<Password> I<Password>
+
+=item B<Digest> B<true>|B<false>
+
 =item B<VerifyPeer> B<true>|B<false>
+
 =item B<VerifyHost> B<true>|B<false>
+
 =item B<CACert> I<CA Cert File>
 
+=item B<Header> I<Header>
+
+=item B<Post> I<Body>
+
 These options behave exactly equivalent to the appropriate options of the
-I<cURL> and I<cURL-JSON> plugins. Please see there for a detailed description.
+I<cURL plugin>. Please see there for a detailed description.
 
 =item E<lt>B<XPath> I<XPath-expression>E<gt>
 
@@ -1289,11 +1590,23 @@ documentation for each driver, somewhere at
 L<http://libdbi-drivers.sourceforge.net/>. However, the options "host",
 "username", "password", and "dbname" seem to be deE<nbsp>facto standards.
 
+DBDs can register two types of options: String options and numeric options. The
+plugin will use the C<dbi_conn_set_option> function when the configuration
+provides a string and the C<dbi_conn_require_option_numeric> function when the
+configuration provides a number. So these two lines will actually result in
+different calls being used:
+
+  DriverOption "Port" 1234      # numeric
+  DriverOption "Port" "1234"    # string
+
 Unfortunately, drivers are not too keen to report errors when an unknown option
 is passed to them, so invalid settings here may go unnoticed. This is not the
 plugin's fault, it will report errors if it gets them from the libraryE<nbsp>/
 the driver. If a driver complains about an option, the plugin will dump a
-complete list of all options understood by that driver to the log.
+complete list of all options understood by that driver to the log. There is no
+way to programatically find out if an option expects a string or a numeric
+argument, so you will have to refer to the appropriate DBD's documentation to
+find this out. Sorry.
 
 =item B<SelectDB> I<Database>
 
@@ -1308,6 +1621,11 @@ query needs to be defined I<before> this statement, i.E<nbsp>e. all query
 blocks you want to refer to must be placed above the database block you want to
 refer to them from.
 
+=item B<Host> I<Hostname>
+
+Sets the B<host> field of I<value lists> to I<Hostname> when dispatching
+values. Defaults to the global hostname setting.
+
 =back
 
 =head2 Plugin C<df>
@@ -1348,6 +1666,20 @@ Enable this option if inodes are a scarce resource for you, usually because
 many small files are stored on the disk. This is a usual scenario for mail
 transfer agents and web caches.
 
+=item B<ValuesAbsolute> B<true>|B<false>
+
+Enables or disables reporting of free and used disk space in 1K-blocks.
+Defaults to B<true>.
+
+=item B<ValuesPercentage> B<false>|B<true>
+
+Enables or disables reporting of free and used disk space in percentage.
+Defaults to B<false>.
+
+This is useful for deploying I<collectd> on the cloud, where machines with
+different disk size may exist. Then it is more practical to configure
+thresholds based on relative disk size.
+
 =back
 
 =head2 Plugin C<disk>
@@ -1382,6 +1714,20 @@ collected. If at least one B<Disk> option is given and no B<IgnoreSelected> or
 set to B<false>, B<only> matching disks will be collected. If B<IgnoreSelected>
 is set to B<true>, all disks are collected B<except> the ones matched.
 
+=item B<UseBSDName> B<true>|B<false>
+
+Whether to use the device's "BSD Name", on MacE<nbsp>OSE<nbsp>X, instead of the
+default major/minor numbers. Requires collectd to be built with Apple's
+IOKitLib support.
+
+=item B<UdevNameAttr> I<Attribute>
+
+Attempt to override disk instance name with the value of a specified udev
+attribute when built with B<libudev>.  If the attribute is not defined for the
+given device, the default name is used. Example:
+
+  UdevNameAttr "DM_NAME"
+
 =back
 
 =head2 Plugin C<dns>
@@ -1932,6 +2278,25 @@ interface path might change between reboots of a guest or across migrations.
 
 =back
 
++=head2 Plugin C<load>
+
+The I<Load plugin> collects the system load. These numbers give a rough overview
+over the utilization of a machine. The system load is defined as the number of
+runnable tasks in the run-queue and is provided by many operating systems as a
+one, five or fifteen minute average.
+
+The following configuration options are available:
+
+=over 4
+
+=item B<ReportRelative> B<false>|B<true>
+
+When enabled, system load divided by number of available CPU cores is reported
+for intervals 1 min, 5 min and 15 min. Defaults to false.
+
+=back
+
+
 =head2 Plugin C<logfile>
 
 =over 4
@@ -1966,6 +2331,34 @@ B<Note>: There is no need to notify the daemon after moving or removing the
 log file (e.E<nbsp>g. when rotating the logs). The plugin reopens the file
 for each line it writes.
 
+=head2 Plugin C<log_logstash>
+
+The I<log logstash plugin> behaves like the logfile plugin but formats
+messages as JSON events for logstash to parse and input.
+
+=over 4
+
+=item B<LogLevel> B<debug|info|notice|warning|err>
+
+Sets the log-level. If, for example, set to B<notice>, then all events with
+severity B<notice>, B<warning>, or B<err> will be written to the logfile.
+
+Please note that B<debug> is only available if collectd has been compiled with
+debugging support.
+
+=item B<File> I<File>
+
+Sets the file to write log messages to. The special strings B<stdout> and
+B<stderr> can be used to write to the standard output and standard error
+channels, respectively. This, of course, only makes much sense when I<collectd>
+is running in foreground- or non-daemon-mode.
+
+=back
+
+B<Note>: There is no need to notify the daemon after moving or removing the
+log file (e.E<nbsp>g. when rotating the logs). The plugin reopens the file
+for each line it writes.
+
 =head2 Plugin C<lpar>
 
 The I<LPAR plugin> reads CPU statistics of I<Logical Partitions>, a
@@ -2096,7 +2489,7 @@ interpreted. For a description of match blocks, please see L<"Plugin tail">.
 
 =head2 Plugin C<memcached>
 
-The C<memcached plugin> connects to a memcached server and queries statistics
+The B<memcached plugin> connects to a memcached server and queries statistics
 about cache utilization, memory and bandwidth used.
 L<http://www.danga.com/memcached/>
 
@@ -2128,57 +2521,238 @@ setting is given, the B<Host> and B<Port> settings are ignored.
 
 =back
 
-=head2 Plugin C<modbus>
-
-The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP and reads
-register values. It supports reading single registers (unsigned 16E<nbsp>bit
-values), large integer values (unsigned 32E<nbsp>bit values) and floating point
-values (two registers interpreted as IEEE floats in big endian notation).
+=head2 Plugin C<mic>
 
-Synopsis:
+The B<mic plugin> gathers CPU statistics, memory usage and temperatures from
+Intel's Many Integrated Core (MIC) systems.
 
- <Data "voltage-input-1">
-   RegisterBase 0
-   RegisterType float
-   Type voltage
-   Instance "input-1"
- </Data>
- <Data "voltage-input-2">
-   RegisterBase 2
-   RegisterType float
-   Type voltage
-   Instance "input-2"
- </Data>
- <Host "modbus.example.com">
-   Address "192.168.0.42"
-   Port    "502"
-   Interval 60
-   
-   <Slave 1>
-     Instance "power-supply"
-     Collect  "voltage-input-1"
-     Collect  "voltage-input-2"
-   </Slave>
- </Host>
+B<Synopsis:>
 
-=over 4
+ <Plugin mic>
+   ShowCPU true
+   ShowCPUCores true
+   ShowMemory true
 
-=item E<lt>B<Data> I<Name>E<gt> blocks
+   ShowTemperatures true
+   Temperature vddg
+   Temperature vddq
+   IgnoreSelectedTemperature true
 
-Data blocks define a mapping between register numbers and the "types" used by
-I<collectd>.
+   ShowPower true
+   Power total0
+   Power total1
+   IgnoreSelectedPower true
+ </Plugin>
 
-Within E<lt>DataE<nbsp>/E<gt> blocks, the following options are allowed:
+The following options are valid inside the B<PluginE<nbsp>mic> block:
 
 =over 4
 
-=item B<RegisterBase> I<Number>
+=item B<ShowCPU> B<true>|B<false>
 
-Configures the base register to read from the device. If the option
-B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
-register will be read (the register number is increased by one).
+If enabled (the default) a sum of the CPU usage across all cores is reported.
+
+=item B<ShowCPUCores> B<true>|B<false>
+
+If enabled (the default) per-core CPU usage is reported.
+
+=item B<ShowMemory> B<true>|B<false>
+
+If enabled (the default) the physical memory usage of the MIC system is
+reported.
+
+=item B<ShowTemperatures> B<true>|B<false>
+
+If enabled (the default) various temperatures of the MIC system are reported.
+
+=item B<Temperature> I<Name>
+
+This option controls which temperatures are being reported. Whether matching
+temperatures are being ignored or I<only> matching temperatures are reported
+depends on the B<IgnoreSelectedTemperature> setting below. By default I<all>
+temperatures are reported.
+
+=item B<IgnoreSelectedTemperature> B<false>|B<true>
+
+Controls the behavior of the B<Temperature> setting above. If set to B<false>
+(the default) only temperatures matching a B<Temperature> option are reported
+or, if no B<Temperature> option is specified, all temperatures are reported. If
+set to B<true>, matching temperatures are I<ignored> and all other temperatures
+are reported.
+
+Known temperature names are:
+
+=over 4
+
+=item die
+
+Die of the CPU
+
+=item devmem
+
+Device Memory
+
+=item fin
+
+Fan In
+
+=item fout
+
+Fan Out
+
+=item vccp
+
+Voltage ccp
+
+=item vddg
+
+Voltage ddg
+
+=item vddq
+
+Voltage ddq
+
+=back
+
+=item B<ShowPower> B<true>|B<false>
+
+If enabled (the default) various temperatures of the MIC system are reported.
+
+=item B<Power> I<Name>
+
+This option controls which power readings are being reported. Whether matching
+power readings are being ignored or I<only> matching power readings are reported
+depends on the B<IgnoreSelectedPower> setting below. By default I<all>
+power readings are reported.
+
+=item B<IgnoreSelectedPower> B<false>|B<true>
+
+Controls the behavior of the B<Power> setting above. If set to B<false>
+(the default) only power readings matching a B<Power> option are reported
+or, if no B<Power> option is specified, all power readings are reported. If
+set to B<true>, matching power readings are I<ignored> and all other power readings
+are reported.
+
+Known power names are:
+
+=over 4
+
+=item total0
+
+Total power utilization averaged over Time Window 0 (uWatts).
+
+=item total1
+
+Total power utilization averaged over Time Window 0 (uWatts).
+
+=item inst
+
+Instantaneous power (uWatts).
+
+=item imax
+
+Max instantaneous power (uWatts).
+
+=item pcie
+
+PCI-E connector power (uWatts).
+
+=item c2x3
+
+2x3 connector power (uWatts).
+
+=item c2x4
+
+2x4 connector power (uWatts).
+
+=item vccp
+
+Core rail (uVolts).
+
+=item vddg
+
+Uncore rail (uVolts).
+
+=item vddq
+
+Memory subsystem rail (uVolts).
+
+=back
+
+=back
+
+=head2 Plugin C<memory>
+
+The I<memory plugin> provides the following configuration options:
+
+=over 4
+
+=item B<ValuesAbsolute> B<true>|B<false>
+
+Enables or disables reporting of physical memory usage in absolute numbers,
+i.e. bytes. Defaults to B<true>.
+
+=item B<ValuesPercentage> B<false>|B<true>
+
+Enables or disables reporting of physical memory usage in percentages, e.g.
+percent of physical memory used. Defaults to B<false>.
+
+This is useful for deploying I<collectd> in a heterogeneous environment in
+which the sizes of physical memory vary.
+
+=back
+
+=head2 Plugin C<modbus>
+
+The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP and reads
+register values. It supports reading single registers (unsigned 16E<nbsp>bit
+values), large integer values (unsigned 32E<nbsp>bit values) and floating point
+values (two registers interpreted as IEEE floats in big endian notation).
+
+B<Synopsis:>
+
+ <Data "voltage-input-1">
+   RegisterBase 0
+   RegisterType float
+   Type voltage
+   Instance "input-1"
+ </Data>
+
+ <Data "voltage-input-2">
+   RegisterBase 2
+   RegisterType float
+   Type voltage
+   Instance "input-2"
+ </Data>
+
+ <Host "modbus.example.com">
+   Address "192.168.0.42"
+   Port    "502"
+   Interval 60
+
+   <Slave 1>
+     Instance "power-supply"
+     Collect  "voltage-input-1"
+     Collect  "voltage-input-2"
+   </Slave>
+ </Host>
+
+=over 4
+
+=item E<lt>B<Data> I<Name>E<gt> blocks
+
+Data blocks define a mapping between register numbers and the "types" used by
+I<collectd>.
+
+Within E<lt>DataE<nbsp>/E<gt> blocks, the following options are allowed:
+
+=over 4
+
+=item B<RegisterBase> I<Number>
+
+Configures the base register to read from the device. If the option
+B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
+register will be read (the register number is increased by one).
 
 =item B<RegisterType> B<Int16>|B<Int32>|B<Uint16>|B<Uint32>|B<Float>
 
@@ -2388,7 +2962,7 @@ Required capabilities are documented below.
     User          "username"
     Password      "aef4Aebe"
     Interval      30
-    
+
     <WAFL>
       Interval 30
       GetNameCache   true
@@ -2396,12 +2970,12 @@ Required capabilities are documented below.
       GetBufferCache true
       GetInodeCache  true
     </WAFL>
-    
+
     <Disks>
       Interval 30
       GetBusy true
     </Disks>
-    
+
     <VolumePerf>
       Interval 30
       GetIO      "volume0"
@@ -2411,7 +2985,7 @@ Required capabilities are documented below.
       GetLatency "volume0"
       IgnoreSelectedLatency false
     </VolumePerf>
-    
+
     <VolumeUsage>
       Interval 30
       GetCapacity "vol0"
@@ -2421,7 +2995,15 @@ Required capabilities are documented below.
       GetSnapshot "vol3"
       IgnoreSelectedSnapshot false
     </VolumeUsage>
-    
+
+    <Quota>
+      Interval 60
+    </Quota>
+
+    <Snapvault>
+      Interval 30
+    </Snapvault>
+
     <System>
       Interval 30
       GetCPULoad     true
@@ -2429,6 +3011,13 @@ Required capabilities are documented below.
       GetDiskOps     true
       GetDiskIO      true
     </System>
+
+    <VFiler vfilerA>
+      Interval 60
+
+      SnapVault true
+      # ...
+    </VFiler>
    </Host>
  </Plugin>
 
@@ -2439,7 +3028,27 @@ The netapp plugin accepts the following configuration options:
 =item B<Host> I<Name>
 
 A host block defines one NetApp filer. It will appear in collectd with the name
-you specify here which does not have to be its real name nor its hostname.
+you specify here which does not have to be its real name nor its hostname (see
+the B<Address> option below).
+
+=item B<VFiler> I<Name>
+
+A B<VFiler> block may only be used inside a host block. It accepts all the
+same options as the B<Host> block (except for cascaded B<VFiler> blocks) and
+will execute all NetApp API commands in the context of the specified
+VFiler(R). It will appear in collectd with the name you specify here which
+does not have to be its real name. The VFiler name may be specified using the
+B<VFilerName> option. If this is not specified, it will default to the name
+you specify here.
+
+The VFiler block inherits all connection related settings from the surrounding
+B<Host> block (which appear before the B<VFiler> block) but they may be
+overwritten inside the B<VFiler> block.
+
+This feature is useful, for example, when using a VFiler as SnapVault target
+(supported since OnTap 8.1). In that case, the SnapVault statistics are not
+available in the host filer (vfiler0) but only in the respective VFiler
+context.
 
 =item B<Protocol> B<httpd>|B<http>
 
@@ -2483,6 +3092,19 @@ Mandatory
 
 Type: string
 
+=item B<VFilerName> I<Name>
+
+The name of the VFiler in which context to execute API commands. If not
+specified, the name provided to the B<VFiler> block will be used instead.
+
+Optional
+
+Type: string
+
+Default: name of the B<VFiler> block
+
+B<Note:> This option may only be used inside B<VFiler> blocks.
+
 =item B<Interval> I<Interval>
 
 B<TODO>
@@ -2813,6 +3435,38 @@ capacities will be selected anyway.
 
 =back
 
+=head3 The Quota block
+
+This will collect (tree) quota statistics (used disk space and number of used
+files). This mechanism is useful to get usage information for single qtrees.
+In case the quotas are not used for any other purpose, an entry similar to the
+following in C</etc/quotas> would be sufficient:
+
+  /vol/volA/some_qtree tree - - - - -
+
+After adding the entry, issue C<quota on -w volA> on the NetApp filer.
+
+=over 4
+
+=item B<Interval> I<Seconds>
+
+Collect SnapVault(R) statistics every I<Seconds> seconds.
+
+=back
+
+=head3 The SnapVault block
+
+This will collect statistics about the time and traffic of SnapVault(R)
+transfers.
+
+=over 4
+
+=item B<Interval> I<Seconds>
+
+Collect SnapVault(R) statistics every I<Seconds> seconds.
+
+=back
+
 =head2 Plugin C<netlink>
 
 The C<netlink> plugin uses a netlink socket to query the Linux kernel about
@@ -2910,7 +3564,7 @@ signature):
    # 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">
@@ -4470,6 +5124,16 @@ 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<CreateFilesAsync> B<false>|B<true>
+
+When enabled, new RRD files are enabled asynchronously, using a separate thread
+that runs in the background. This prevents writes to block, which is a problem
+especially when many hundreds of files need to be created at once. However,
+since the purpose of creating the files asynchronously is I<not> to block until
+the file is available, values before the file is available will be discarded.
+When disabled (the default) files are created synchronously, blocking for a
+short while, while the file is being written.
+
 =item B<StepSize> I<Seconds>
 
 B<Force> the stepsize of newly created RRD-files. Ideally (and per default)
@@ -4527,8 +5191,18 @@ can safely ignore these settings.
 
 =item B<DataDir> I<Directory>
 
-Set the directory to store RRD-files under. Per default RRD-files are generated
-beneath the daemon's working directory, i.E<nbsp>e. the B<BaseDir>.
+Set the directory to store RRD files under. By default RRD files are generated
+beneath the daemon's working directory, i.e. the B<BaseDir>.
+
+=item B<CreateFilesAsync> B<false>|B<true>
+
+When enabled, new RRD files are enabled asynchronously, using a separate thread
+that runs in the background. This prevents writes to block, which is a problem
+especially when many hundreds of files need to be created at once. However,
+since the purpose of creating the files asynchronously is I<not> to block until
+the file is available, values before the file is available will be discarded.
+When disabled (the default) files are created synchronously, blocking for a
+short while, while the file is being written.
 
 =item B<StepSize> I<Seconds>
 
@@ -4549,7 +5223,7 @@ a very good reason to do so.
 
 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
+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.
 
@@ -4665,12 +5339,129 @@ and all other sensors are collected.
 
 =back
 
+=head2 Plugin C<sigrok>
+
+The I<sigrok plugin> uses I<libsigrok> to retrieve measurements from any device
+supported by the L<sigrok|http://sigrok.org/> project.
+
+B<Synopsis>
+
+ <Plugin sigrok>
+   LogLevel 3
+   <Device "AC Voltage">
+      Driver "fluke-dmm"
+      MinimumInterval 10
+      Conn "/dev/ttyUSB2"
+   </Device>
+   <Device "Sound Level">
+      Driver "cem-dt-885x"
+      Conn "/dev/ttyUSB1"
+   </Device>
+ </Plugin>
+
+=over 4
+
+=item B<LogLevel> B<0-5>
+
+The I<sigrok> logging level to pass on to the I<collectd> log, as a number
+between B<0> and B<5> (inclusive). These levels correspond to C<None>,
+C<Errors>, C<Warnings>, C<Informational>, C<Debug >and C<Spew>, respectively.
+The default is B<2> (C<Warnings>). The I<sigrok> log messages, regardless of
+their level, are always submitted to I<collectd> at its INFO log level.
+
+=item E<lt>B<Device> I<Name>E<gt>
+
+A sigrok-supported device, uniquely identified by this section's options. The
+I<Name> is passed to I<collectd> as the I<plugin instance>.
+
+=item B<Driver> I<DriverName>
+
+The sigrok driver to use for this device.
+
+=item B<Conn> I<ConnectionSpec>
+
+If the device cannot be auto-discovered, or more than one might be discovered
+by the driver, I<ConnectionSpec> specifies the connection string to the device.
+It can be of the form of a device path (e.g.E<nbsp>C</dev/ttyUSB2>), or, in
+case of a non-serial USB-connected device, the USB I<VendorID>B<.>I<ProductID>
+separated by a period (e.g.E<nbsp>C<0403.6001>). A USB device can also be
+specified as I<Bus>B<.>I<Address> (e.g.E<nbsp>C<1.41>).
+
+=item B<SerialComm> I<SerialSpec>
+
+For serial devices with non-standard port settings, this option can be used
+to specify them in a form understood by I<sigrok>, e.g.E<nbsp>C<9600/8n1>.
+This should not be necessary; drivers know how to communicate with devices they
+support.
+
+=item B<MinimumInterval> I<Seconds>
+
+Specifies the minimum time between measurement dispatches to I<collectd>, in
+seconds. Since some I<sigrok> supported devices can acquire measurements many
+times per second, it may be necessary to throttle these. For example, the
+I<RRD plugin> cannot process writes more than once per second.
+
+The default B<MinimumInterval> is B<0>, meaning measurements received from the
+device are always dispatched to I<collectd>. When throttled, unused
+measurements are discarded.
+
+=back
+
 =head2 Plugin C<snmp>
 
 Since the configuration of the C<snmp plugin> is a little more complicated than
 other plugins, its documentation has been moved to an own manpage,
 L<collectd-snmp(5)>. Please see there for details.
 
+=head2 Plugin C<statsd>
+
+The I<statsd plugin> listens to a UDP socket, reads "events" in the statsd
+protocol and dispatches rates or other aggregates of these numbers
+periodically.
+
+The plugin implements the I<Counter>, I<Timer>, I<Gauge> and I<Set> types which
+are dispatched as the I<collectd> types C<derive>, C<latency>, C<gauge> and
+C<objects> respectively.
+
+The following configuration options are valid:
+
+=over 4
+
+=item B<Host> I<Host>
+
+Bind to the hostname / address I<Host>. By default, the plugin will bind to the
+"any" address, i.e. accept packets sent to any of the hosts addresses.
+
+=item B<Port> I<Port>
+
+UDP port to listen to. This can be either a service name or a port number.
+Defaults to C<8125>.
+
+=item B<DeleteCounters> B<false>|B<true>
+
+=item B<DeleteTimers> B<false>|B<true>
+
+=item B<DeleteGauges> B<false>|B<true>
+
+=item B<DeleteSets> B<false>|B<true>
+
+These options control what happens if metrics are not updated in an interval.
+If set to B<False>, the default, metrics are dispatched unchanged, i.e. the
+rate of counters and size of sets will be zero, timers report C<NaN> and gauges
+are unchanged. If set to B<True>, the such metrics are not dispatched and
+removed from the internal cache.
+
+=item B<TimerPercentile> I<Percent>
+
+Calculate and dispatch the configured percentile, i.e. compute the latency, so
+that I<Percent> of all reported timers are smaller than or equal to the
+computed latency. This is useful for cutting off the long tail latency, as it's
+often done in I<Service Level Agreements> (SLAs).
+
+If not specified, no percentile is calculated / dispatched.
+
+=back
+
 =head2 Plugin C<swap>
 
 The I<Swap plugin> collects information about used and available swap space. On
@@ -4693,6 +5484,19 @@ This option is only available if the I<Swap plugin> can read C</proc/swaps>
 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.
 
+=item B<ValuesAbsolute> B<true>|B<false>
+
+Enables or disables reporting of absolute swap metrics, i.e. number of I<bytes>
+available and used. Defaults to B<true>.
+
+=item B<ValuesPercentage> B<false>|B<true>
+
+Enables or disables reporting of relative swap metrics, i.e. I<percent>
+available and free. Defaults to B<false>.
+
+This is useful for deploying I<collectd> in a heterogeneous environment, where
+swap sizes differ and you want to specify generic thresholds or similar.
+
 =back
 
 =head2 Plugin C<syslog>
@@ -4828,6 +5632,7 @@ user using (extended) regular expressions, as described in L<regex(7)>.
   <Plugin "tail">
     <File "/var/log/exim4/mainlog">
       Instance "exim"
+      Interval 60
       <Match>
         Regex "S=([1-9][0-9]*)"
         DSType "CounterAdd"
@@ -4854,6 +5659,9 @@ This plugin instance is for all B<Match> blocks that B<follow> it, until the
 next B<Instance> option. This way you can extract several plugin instances from
 one logfile, handy when parsing syslog and the like.
 
+The B<Interval> option allows you to define the length of time between reads. If
+this is not set, the default Interval will be used.
+
 Each B<Match> block has the following options to describe how the match should
 be performed:
 
@@ -4945,6 +5753,97 @@ This optional setting sets the type instance to use.
 
 =back
 
+=head2 Plugin C<tail_csv>
+
+The I<tail_csv plugin> reads files in the CSV format, e.g. the statistics file
+written by I<Snort>.
+
+B<Synopsis:>
+
+ <Plugin "tail_csv">
+   <Metric "snort-dropped">
+       Type "percent"
+       Instance "dropped"
+       Index 1
+   </Metric>
+   <File "/var/log/snort/snort.stats">
+       Instance "snort-eth0"
+       Interval 600
+       Collect "snort-dropped"
+   </File>
+ </Plugin>
+
+The configuration consists of one or more B<Metric> blocks that define an index
+into the line of the CSV file and how this value is mapped to I<collectd's>
+internal representation. These are followed by one or more B<Instance> blocks
+which configure which file to read, in which interval and which metrics to
+extract.
+
+=over 4
+
+=item E<lt>B<Metric> I<Name>E<gt>
+
+The B<Metric> block configures a new metric to be extracted from the statistics
+file and how it is mapped on I<collectd's> data model. The string I<Name> is
+only used inside the B<Instance> blocks to refer to this block, so you can use
+one B<Metric> block for multiple CSV files.
+
+=over 4
+
+=item B<Type> I<Type>
+
+Configures which I<Type> to use when dispatching this metric. Types are defined
+in the L<types.db(5)> file, see the appropriate manual page for more
+information on specifying types. Only types with a single I<data source> are
+supported by the I<tail_csv plugin>. The information whether the value is an
+absolute value (i.e. a C<GAUGE>) or a rate (i.e. a C<DERIVE>) is taken from the
+I<Type's> definition.
+
+=item B<Instance> I<TypeInstance>
+
+If set, I<TypeInstance> is used to populate the type instance field of the
+created value lists. Otherwise, no type instance is used.
+
+=item B<ValueFrom> I<Index>
+
+Configure to read the value from the field with the zero-based index I<Index>.
+If the value is parsed as signed integer, unsigned integer or double depends on
+the B<Type> setting, see above.
+
+=back
+
+=item E<lt>B<File> I<Path>E<gt>
+
+Each B<File> block represents one CSV file to read. There must be at least one
+I<File> block but there can be multiple if you have multiple CSV files.
+
+=over 4
+
+=item B<Instance> I<PluginInstance>
+
+Sets the I<plugin instance> used when dispatching the values.
+
+=item B<Collect> I<Metric>
+
+Specifies which I<Metric> to collect. This option must be specified at least
+once, and you can use this option multiple times to specify more than one
+metric to be extracted from this statistic file.
+
+=item B<Interval> I<Seconds>
+
+Configures the interval in which to read values from this instance / file.
+Defaults to the plugin's default interval.
+
+=item B<TimeFrom> I<Index>
+
+Rather than using the local time when dispatching a value, read the timestamp
+from the field with the zero-based index I<Index>. The value is interpreted as
+seconds since epoch. The value is parsed as a double and may be factional.
+
+=back
+
+=back
+
 =head2 Plugin C<teamspeak2>
 
 The C<teamspeak2 plugin> connects to the query port of a teamspeak2 server and
@@ -5173,7 +6072,33 @@ Take the UUID from the given file (default I</etc/uuid>).
 
 =head2 Plugin C<varnish>
 
-The Varnish plugin collects information about Varnish, an HTTP accelerator.
+The I<varnish plugin> collects information about Varnish, an HTTP accelerator.
+
+Synopsis:
+
+ <Plugin "varnish">
+   <Instance "example">
+     CollectCache       true
+     CollectConnections true
+     CollectBackend     true
+     CollectSHM         true
+     CollectESI         false
+     CollectFetch       false
+     CollectHCB         false
+     CollectSMA         false
+     CollectSMS         false
+     CollectSM          false
+     CollectTotals      false
+     CollectWorkers     false
+   </Instance>
+ </Plugin>
+
+The configuration consists of one or more E<lt>B<Instance>E<nbsp>I<Name>E<gt>
+blocks. I<Name> is the parameter passed to "varnishd -n". If left empty, it
+will collectd statistics from the default "varnishd" instance (this should work
+fine in most cases).
+
+Inside each E<lt>B<Instance>E<gt> blocks, the following options are recognized:
 
 =over 4
 
@@ -5195,6 +6120,17 @@ and closed connections. True by default.
 Statistics about the shared memory log, a memory region to store
 log messages which is flushed to disk when full. True by default.
 
+=item B<CollectBan> B<true>|B<false>
+
+Statistics about ban operations, such as number of bans added, retired, and
+number of objects tested against ban operations. Only available with Varnish
+3.x. False by default.
+
+=item B<CollectDirectorDNS> B<true>|B<false>
+
+DNS director lookup cache statistics. Only available with Varnish 3.x. False by
+default.
+
 =item B<CollectESI> B<true>|B<false>
 
 Edge Side Includes (ESI) parse statistics. False by default.
@@ -5208,10 +6144,27 @@ Statistics about fetches (HTTP requests sent to the backend). False by default.
 Inserts and look-ups in the crit bit tree based hash. Look-ups are
 divided into locked and unlocked look-ups. False by default.
 
+=item B<CollectObjects> B<true>|B<false>
+
+Statistics on cached objects: number of objects expired, nuked (prematurely
+expired), saved, moved, etc. False by default.
+
+=item B<CollectPurge> B<true>|B<false>
+
+Statistics about purge operations, such as number of purges added, retired, and
+number of objects tested against purge operations. Only available with Varnish
+2.x. False by default.
+
+=item B<CollectSession> B<true>|B<false>
+
+Client session statistics. Number of past and current sessions, session herd and
+linger counters, etc. False by default.
+
 =item B<CollectSMA> B<true>|B<false>
 
-malloc or umem (umem_alloc(3MALLOC) based) storage statistics.
-The umem storage component is Solaris specific. False by default.
+malloc or umem (umem_alloc(3MALLOC) based) storage statistics. The umem storage
+component is Solaris specific. Only available with Varnish 2.x. False by
+default.
 
 =item B<CollectSMS> B<true>|B<false>
 
@@ -5220,13 +6173,28 @@ component is used internally only. False by default.
 
 =item B<CollectSM> B<true>|B<false>
 
-file (memory mapped file) storage statistics. False by default.
+file (memory mapped file) storage statistics. Only available with Varnish 2.x.
+False by default.
+
+=item B<CollectStruct> B<true>|B<false>
+
+Current varnish internal state statistics. Number of current sessions, objects
+in cache store, open connections to backends (with Varnish 2.x), etc. False by
+default.
 
 =item B<CollectTotals> B<true>|B<false>
 
 Collects overview counters, such as the number of sessions created,
 the number of requests and bytes transferred. False by default.
 
+=item B<CollectUptime> B<true>|B<false>
+
+Varnish uptime. False by default.
+
+=item B<CollectVCL> B<true>|B<false>
+
+Number of total (available + discarded) VCL (config files). False by default.
+
 =item B<CollectWorkers> B<true>|B<false>
 
 Collect statistics about worker threads. False by default.
@@ -5271,20 +6239,25 @@ iptables to feed data for the guest IPs into the iptables plugin.
 
 The C<write_graphite> plugin writes data to I<Graphite>, an open-source metrics
 storage and graphing project. The plugin connects to I<Carbon>, the data layer
-of I<Graphite>, and sends data via the "line based" protocol (per default using
-portE<nbsp>2003). The data will be sent in blocks of at most 1428 bytes to
-minimize the number of network packets.
+of I<Graphite>, via I<TCP> or I<UDP> and sends data via the "line based"
+protocol (per default using portE<nbsp>2003). The data will be sent in blocks
+of at most 1428 bytes to minimize the number of network packets.
 
 Synopsis:
 
  <Plugin write_graphite>
-   <Carbon>
+   <Node "example">
      Host "localhost"
      Port "2003"
+     Protocol "tcp"
+     LogSendErrors true
      Prefix "collectd"
-   </Carbon>
+   </Node>
  </Plugin>
 
+The configuration consists of one or more E<lt>B<Node>E<nbsp>I<Name>E<gt>
+blocks. Inside the B<Node> blocks, the following options are recognized:
+
 =over 4
 
 =item B<Host> I<Address>
@@ -5295,6 +6268,17 @@ Hostname or address to connect to. Defaults to C<localhost>.
 
 Service name or port number to connect to. Defaults to C<2003>.
 
+=item B<Protocol> I<String>
+
+Protocol to use when connecting to I<Graphite>. Defaults to C<tcp>.
+
+=item B<LogSendErrors> B<false>|B<true>
+
+If set to B<true> (the default), logs errors when sending data to I<Graphite>.
+If set to B<false>, it will not log the errors. This is especially useful when
+using Protocol UDP since many times we want to use the "fire-and-forget"
+approach and logging errors fills syslog with unneeded messages.
+
 =item B<Prefix> I<String>
 
 When set, I<String> is added in front of the host name. Dots and whitespace are
@@ -5374,6 +6358,16 @@ 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.
 
+=item B<Database> I<Database>
+
+=item B<User> I<User>
+
+=item B<Password> I<Password>
+
+Sets the information used when authenticating to a I<MongoDB> database. The
+fields are optional (in which case no authentication is attempted), but if you
+want to use authentication all three fields must be set.
+
 =back
 
 =head2 Plugin C<write_http>
@@ -5424,6 +6418,33 @@ File that holds one or more SSL certificates. If you want to use HTTPS you will
 possibly need this option. What CA certificates come bundled with C<libcurl>
 and are checked by default depends on the distribution you use.
 
+=item B<CAPath> I<Directory>
+
+Directory holding one or more CA certificate files. You can use this if for
+some reason all the needed CA certificates aren't in the same file and can't be
+pointed to using the B<CACert> option. Requires C<libcurl> to be built against
+OpenSSL.
+
+=item B<ClientKey> I<File>
+
+File that holds the private key in PEM format to be used for certificate-based
+authentication.
+
+=item B<ClientCert> I<File>
+
+File that holds the SSL certificate to be used for certificate-based
+authentication.
+
+=item B<ClientKeyPass> I<Password>
+
+Password required to load the private key in B<ClientKey>.
+
+=item B<SSLVersion> B<SSLv2>|B<SSLv3>|B<TLSv1>|B<TLSv1_0>|B<TLSv1_1>|B<TLSv1_2>
+
+Define which SSL protocol version must be used. By default C<libcurl> will
+attempt to figure out the remote SSL protocol version. See
+L<curl_easy_setopt(3)> for more details.
+
 =item B<Format> B<Command>|B<JSON>
 
 Format of the output to generate. If set to B<Command>, will create output that
@@ -5440,6 +6461,208 @@ number.
 
 =back
 
+=head2 Plugin C<write_kafka>
+
+The I<write_kafka plugin> will send values to a I<Kafka> topic, a distributed
+queue.
+Synopsis:
+
+ <Plugin "write_kafka">
+   Property "metadata.broker.list" "broker1:9092,broker2:9092"
+   <Topic "collectd">
+     Format JSON
+   </Topic>
+ </Plugin>
+
+The following options are understood by the I<write_kafka plugin>:
+
+=over 4
+
+=item E<lt>B<Topic> I<Name>E<gt>
+
+The plugin's configuration consists of one or more B<Topic> blocks. Each block
+is given a unique I<Name> and specifies one kafka producer.
+Inside the B<Topic> block, the following per-topic options are
+understood:
+
+=over 4
+
+=item B<Property> I<String> I<String>
+
+Configure the named property for the current topic. Properties are
+forwarded to the kafka producer library B<librdkafka>.
+
+=item B<Key> I<String>
+
+Use the specified string as a partioning key for the topic. Kafka breaks
+topic into partitions and guarantees that for a given topology, the same
+consumer will be used for a specific key. The special (case insensitive)
+string B<Random> can be used to specify that an arbitrary partition should
+be used.
+
+=item B<Format> B<Command>|B<JSON>|B<Graphite>
+
+Selects the format in which messages are sent to the broker. If set to
+B<Command> (the default), values are sent as C<PUTVAL> commands which are
+identical to the syntax used by the I<Exec> and I<UnixSock plugins>.
+
+If set to B<JSON>, the values are encoded in the I<JavaScript Object Notation>,
+an easy and straight forward exchange format.
+
+If set to B<Graphite>, values are encoded in the I<Graphite> format, which is
+"<metric> <value> <timestamp>\n".
+
+=item B<StoreRates> B<true>|B<false>
+
+Determines whether or not C<COUNTER>, C<DERIVE> and C<ABSOLUTE> data sources
+are converted to a I<rate> (i.e. a C<GAUGE> value). If set to B<false> (the
+default), no conversion is performed. Otherwise the conversion is performed
+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> (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> (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> (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>).
+
+=item B<GraphiteSeparateInstances> 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<StoreRates> B<true>|B<false>
+
+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.
+
+This will be reflected in the C<ds_type> tag: If B<StoreRates> is enabled,
+converted values will have "rate" appended to the data source type, e.g.
+C<ds_type:derive:rate>.
+
+=back
+
+=item B<Property> I<String> I<String>
+
+Configure the kafka producer through properties, you almost always will
+want to set B<metadata.broker.list> to your Kafka broker list.
+
+=back
+
+=head2 Plugin C<write_riemann>
+
+The I<write_riemann plugin> will send values to I<Riemann>, a powerful stream
+aggregation and monitoring system. The plugin sends I<Protobuf> encoded data to
+I<Riemann> using UDP packets.
+
+Synopsis:
+
+ <Plugin "write_riemann">
+   <Node "example">
+     Host "localhost"
+     Port "5555"
+     Protocol UDP
+     StoreRates true
+     AlwaysAppendDS false
+     TTLFactor 2.0
+   </Node>
+   Tag "foobar"
+   Attribute "foo" "bar"
+ </Plugin>
+
+The following options are understood by the I<write_riemann plugin>:
+
+=over 4
+
+=item E<lt>B<Node> I<Name>E<gt>
+
+The plugin's configuration consists of one or more B<Node> blocks. Each block
+is given a unique I<Name> and specifies one connection to an instance of
+I<Riemann>. Indise the B<Node> block, the following per-connection options are
+understood:
+
+=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<5555>.
+
+=item B<Protocol> B<UDP>|B<TCP>
+
+Specify the protocol to use when communicating with I<Riemann>. Defaults to
+B<UDP>.
+
+=item B<StoreRates> B<true>|B<false>
+
+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.
+
+This will be reflected in the C<ds_type> tag: If B<StoreRates> is enabled,
+converted values will have "rate" appended to the data source type, e.g.
+C<ds_type:derive:rate>.
+
+=item B<AlwaysAppendDS> B<false>|B<true>
+
+If set the B<true>, append the name of the I<Data Source> (DS) to the
+"service", i.e. the field that, together with the "host" field, uniquely
+identifies a metric in I<Riemann>. If set to B<false> (the default), this is
+only done when there is more than one DS.
+
+=item B<TTLFactor> I<Factor>
+
+I<Riemann> events have a I<Time to Live> (TTL) which specifies how long each
+event is considered active. I<collectd> populates this field based on the
+metrics interval setting. This setting controls the factor with which the
+interval is multiplied to set the TTL. The default value is B<2.0>. Unless you
+know exactly what you're doing, you should only increase this setting from its
+default value.
+
+=item B<Notifications> B<false>|B<true>
+
+If set to B<true>, create riemann events for notifications. This is B<true>
+by default. When processing thresholds from write_riemann, it might prove
+useful to avoid getting notification events.
+
+=item B<CheckThresholds> B<false>|B<true>
+
+If set to B<true>, attach state to events based on thresholds defined
+in the B<Threshold> plugin. Defaults to B<false>.
+
+=back
+
+=item B<Tag> I<String>
+
+Add the given string as an additional tag to the metric being sent to
+I<Riemann>.
+
+=item B<Attribute> I<String> I<String>
+
+Consider the two given strings to be the key and value of an additional
+attribute for each metric being sent out to I<Riemann>.
+
+=back
+
 =head1 THRESHOLD CONFIGURATION
 
 Starting with version C<4.3.0> collectd has support for B<monitoring>. By that
@@ -5472,7 +6695,7 @@ When a value comes within range again or is received after it was missing, an
 Here is a configuration example to get you started. Read below for more
 information.
 
- <Threshold>
+ <Plugin threshold>
    <Type "foo">
      WarningMin    0.00
      WarningMax 1000.00
@@ -5503,7 +6726,7 @@ information.
        </Type>
      </Plugin>
    </Host>
- </Threshold>
+ </Plugin>
 
 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
@@ -6092,7 +7315,7 @@ Example:
    Max 100
    Satisfy "All"
  </Match>
+
  # Match if the value of any data source is outside the range of 0 - 100.
  <Match "value">
    Min   0
@@ -6274,7 +7497,7 @@ Example:
  <Target "replace">
    # Replace "example.net" with "example.com"
    Host "\\<example.net\\>" "example.com"
+
    # Strip "www." from hostnames
    Host "\\<www\\." ""
  </Target>
index c0994d1..558dc7a 100644 (file)
@@ -262,7 +262,9 @@ typedef int _Bool;
 # define COLLECTD_DEFAULT_INTERVAL 10.0
 #endif
 
-#define STATIC_ARRAY_LEN(array) (sizeof (array) / sizeof ((array)[0]))
+ #ifndef COLLECTD_USERAGENT
+ # define COLLECTD_USERAGENT PACKAGE_NAME"/"PACKAGE_VERSION
+ #endif
 
 /* Remove GNU specific __attribute__ settings when using another compiler */
 #if !__GNUC__
@@ -294,6 +296,7 @@ typedef uint64_t cdtime_t;
 
 extern char     hostname_g[];
 extern cdtime_t interval_g;
+extern int      pidfile_from_cli;
 extern int      timeout_g;
 
 #endif /* COLLECTD_H */
index e36dcdf..2f7b9f7 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd - System statistics collection daemon
index 21c0b50..52b8ef2 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectdctl - Control interface for collectd
index 73ba6b8..3151e11 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectdmon - Monitoring daemon for collectd
index b679bf7..1608d52 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/common.c
- * Copyright (C) 2005-2010  Florian octo Forster
+ * Copyright (C) 2005-2014  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
@@ -86,6 +86,47 @@ int ssnprintf (char *dest, size_t n, const char *format, ...)
        return (ret);
 } /* int ssnprintf */
 
+char *ssnprintf_alloc (char const *format, ...) /* {{{ */
+{
+       char static_buffer[1024] = "";
+       char *alloc_buffer;
+       size_t alloc_buffer_size;
+       int status;
+       va_list ap;
+
+       /* Try printing into the static buffer. In many cases it will be
+        * sufficiently large and we can simply return a strdup() of this
+        * buffer. */
+       va_start (ap, format);
+       status = vsnprintf (static_buffer, sizeof (static_buffer), format, ap);
+       va_end (ap);
+       if (status < 0)
+               return (NULL);
+
+       /* "status" does not include the null byte. */
+       alloc_buffer_size = (size_t) (status + 1);
+       if (alloc_buffer_size <= sizeof (static_buffer))
+               return (strdup (static_buffer));
+
+       /* Allocate a buffer large enough to hold the string. */
+       alloc_buffer = malloc (alloc_buffer_size);
+       if (alloc_buffer == NULL)
+               return (NULL);
+       memset (alloc_buffer, 0, alloc_buffer_size);
+
+       /* Print again into this new buffer. */
+       va_start (ap, format);
+       status = vsnprintf (alloc_buffer, alloc_buffer_size, format, ap);
+       va_end (ap);
+       if (status < 0)
+       {
+               sfree (alloc_buffer);
+               return (NULL);
+       }
+
+       return (alloc_buffer);
+} /* }}} char *ssnprintf_alloc */
+
 char *sstrdup (const char *s)
 {
        char *r;
@@ -336,8 +377,10 @@ int strunescape (char *buf, size_t buf_len)
                if (buf[i] != '\\')
                        continue;
 
-               if ((i >= buf_len) || (buf[i + 1] == '\0')) {
+               if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) {
                        ERROR ("string unescape: backslash found at end of string.");
+                       /* Ensure null-byte at the end of the buffer. */
+                       buf[i] = 0;
                        return (-1);
                }
 
@@ -356,39 +399,60 @@ int strunescape (char *buf, size_t buf_len)
                                break;
                }
 
+               /* Move everything after the position one position to the left.
+                * Add a null-byte as last character in the buffer. */
                memmove (buf + i + 1, buf + i + 2, buf_len - i - 2);
+               buf[buf_len - 1] = 0;
        }
        return (0);
 } /* int strunescape */
 
-int escape_slashes (char *buf, int buf_len)
+size_t strstripnewline (char *buffer)
 {
-       int i;
+       size_t buffer_len = strlen (buffer);
 
-       if (strcmp (buf, "/") == 0)
+       while (buffer_len > 0)
        {
-               if (buf_len < 5)
-                       return (-1);
-
-               strncpy (buf, "root", buf_len);
-               return (0);
+               if ((buffer[buffer_len - 1] != '\n')
+                               && (buffer[buffer_len - 1] != '\r'))
+                       break;
+               buffer[buffer_len] = 0;
+               buffer_len--;
        }
 
-       if (buf_len <= 1)
+       return (buffer_len);
+} /* size_t strstripnewline */
+
+int escape_slashes (char *buffer, size_t buffer_size)
+{
+       int i;
+       size_t buffer_len;
+
+       buffer_len = strlen (buffer);
+
+       if (buffer_len <= 1)
+       {
+               if (strcmp ("/", buffer) == 0)
+               {
+                       if (buffer_size < 5)
+                               return (-1);
+                       sstrncpy (buffer, "root", buffer_size);
+               }
                return (0);
+       }
 
        /* Move one to the left */
-       if (buf[0] == '/')
-               memmove (buf, buf + 1, buf_len - 1);
+       if (buffer[0] == '/')
+       {
+               memmove (buffer, buffer + 1, buffer_len);
+               buffer_len--;
+       }
 
-       for (i = 0; i < buf_len - 1; i++)
+       for (i = 0; i < buffer_len - 1; i++)
        {
-               if (buf[i] == '\0')
-                       break;
-               else if (buf[i] == '/')
-                       buf[i] = '_';
+               if (buffer[i] == '/')
+                       buffer[i] = '_';
        }
-       buf[i] = '\0';
 
        return (0);
 } /* int escape_slashes */
@@ -591,7 +655,7 @@ int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
        char ident[128];
 
        *ksp_ptr = NULL;
-       
+
        if (kc == NULL)
                return (-1);
 
@@ -664,7 +728,7 @@ long long get_kstat_value (kstat_t *ksp, char *name)
                retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
        else
                WARNING ("get_kstat_value: Not a numeric value: %s", name);
-                
+
        return (retval);
 }
 #endif /* HAVE_LIBKSTAT */
@@ -771,36 +835,43 @@ int format_name (char *ret, int ret_len,
                const char *plugin, const char *plugin_instance,
                const char *type, const char *type_instance)
 {
-       int  status;
+  char *buffer;
+  size_t buffer_size;
+
+  buffer = ret;
+  buffer_size = (size_t) ret_len;
+
+#define APPEND(str) do {                                               \
+  size_t l = strlen (str);                                             \
+  if (l >= buffer_size)                                                \
+    return (ENOBUFS);                                                  \
+  memcpy (buffer, (str), l);                                           \
+  buffer += l; buffer_size -= l;                                       \
+} while (0)
 
-       assert (plugin != NULL);
-       assert (type != NULL);
+  assert (plugin != NULL);
+  assert (type != NULL);
 
-       if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0))
-       {
-               if ((type_instance == NULL) || (strlen (type_instance) == 0))
-                       status = ssnprintf (ret, ret_len, "%s/%s/%s",
-                                       hostname, plugin, type);
-               else
-                       status = ssnprintf (ret, ret_len, "%s/%s/%s-%s",
-                                       hostname, plugin, type,
-                                       type_instance);
-       }
-       else
-       {
-               if ((type_instance == NULL) || (strlen (type_instance) == 0))
-                       status = ssnprintf (ret, ret_len, "%s/%s-%s/%s",
-                                       hostname, plugin, plugin_instance,
-                                       type);
-               else
-                       status = ssnprintf (ret, ret_len, "%s/%s-%s/%s-%s",
-                                       hostname, plugin, plugin_instance,
-                                       type, type_instance);
-       }
+  APPEND (hostname);
+  APPEND ("/");
+  APPEND (plugin);
+  if ((plugin_instance != NULL) && (plugin_instance[0] != 0))
+  {
+    APPEND ("-");
+    APPEND (plugin_instance);
+  }
+  APPEND ("/");
+  APPEND (type);
+  if ((type_instance != NULL) && (type_instance[0] != 0))
+  {
+    APPEND ("-");
+    APPEND (type_instance);
+  }
+  assert (buffer_size > 0);
+  buffer[0] = 0;
 
-       if ((status < 1) || (status >= ret_len))
-               return (-1);
-       return (0);
+#undef APPEND
+  return (0);
 } /* int format_name */
 
 int format_values (char *ret, size_t ret_len, /* {{{ */
@@ -1170,7 +1241,7 @@ int walk_directory (const char *dir, dirwalk_callback_f callback,
        while ((ent = readdir (dh)) != NULL)
        {
                int status;
-               
+
                if (include_hidden)
                {
                        if ((strcmp (".", ent->d_name) == 0)
@@ -1197,18 +1268,25 @@ int walk_directory (const char *dir, dirwalk_callback_f callback,
        return (0);
 }
 
-int read_file_contents (const char *filename, char *buf, int bufsize)
+ssize_t read_file_contents (const char *filename, char *buf, size_t bufsize)
 {
        FILE *fh;
-       int n;
+       ssize_t ret;
 
-       if ((fh = fopen (filename, "r")) == NULL)
-               return -1;
+       fh = fopen (filename, "r");
+       if (fh == NULL)
+               return (-1);
 
-       n = fread(buf, 1, bufsize, fh);
-       fclose(fh);
+       ret = (ssize_t) fread (buf, 1, bufsize, fh);
+       if ((ret == 0) && (ferror (fh) != 0))
+       {
+               ERROR ("read_file_contents: Reading file \"%s\" failed.",
+                               filename);
+               ret = -1;
+       }
 
-       return n;
+       fclose(fh);
+       return (ret);
 }
 
 counter_t counter_diff (counter_t old_value, counter_t new_value)
@@ -1326,6 +1404,69 @@ int rate_to_value (value_t *ret_value, gauge_t rate, /* {{{ */
        return (0);
 } /* }}} value_t rate_to_value */
 
+int value_to_rate (value_t *ret_rate, derive_t value, /* {{{ */
+               value_to_rate_state_t *state,
+               int ds_type, cdtime_t t)
+{
+       double interval;
+
+       /* Another invalid state: The time is not increasing. */
+       if (t <= state->last_time)
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       interval = CDTIME_T_TO_DOUBLE(t - state->last_time);
+
+       /* Previous value is invalid. */
+       if (state->last_time == 0) /* {{{ */
+       {
+               if (ds_type == DS_TYPE_DERIVE)
+               {
+                       state->last_value.derive = value;
+               }
+               else if (ds_type == DS_TYPE_COUNTER)
+               {
+                       state->last_value.counter = (counter_t) value;
+               }
+               else if (ds_type == DS_TYPE_ABSOLUTE)
+               {
+                       state->last_value.absolute = (absolute_t) value;
+               }
+               else
+               {
+                       assert (23 == 42);
+               }
+
+               state->last_time = t;
+               return (EAGAIN);
+       } /* }}} */
+
+       if (ds_type == DS_TYPE_DERIVE)
+       {
+               ret_rate->gauge = (value - state->last_value.derive) / interval;
+               state->last_value.derive = value;
+       }
+       else if (ds_type == DS_TYPE_COUNTER)
+       {
+               ret_rate->gauge = (((counter_t)value) - state->last_value.counter) / interval;
+               state->last_value.counter = (counter_t) value;
+       }
+       else if (ds_type == DS_TYPE_ABSOLUTE)
+       {
+               ret_rate->gauge = (((absolute_t)value) - state->last_value.absolute) / interval;
+               state->last_value.absolute = (absolute_t) value;
+       }
+       else
+       {
+               assert (23 == 42);
+       }
+
+        state->last_time = t;
+       return (0);
+} /* }}} value_t rate_to_value */
+
 int service_name_to_port_number (const char *service_name)
 {
        struct addrinfo *ai_list;
@@ -1396,3 +1537,35 @@ int strtoderive (const char *string, derive_t *ret_value) /* {{{ */
        *ret_value = tmp;
        return (0);
 } /* }}} int strtoderive */
+
+int strarray_add (char ***ret_array, size_t *ret_array_len, char const *str) /* {{{ */
+{
+       char **array;
+       size_t array_len = *ret_array_len;
+
+       if (str == NULL)
+               return (EINVAL);
+
+       array = realloc (*ret_array,
+            (array_len + 1) * sizeof (*array));
+       if (array == NULL)
+               return (ENOMEM);
+       *ret_array = array;
+
+       array[array_len] = strdup (str);
+       if (array[array_len] == NULL)
+               return (ENOMEM);
+
+       array_len++;
+        *ret_array_len = array_len;
+       return (0);
+} /* }}} int strarray_add */
+
+void strarray_free (char **array, size_t array_len) /* {{{ */
+{
+       size_t i;
+
+       for (i = 0; i < array_len; i++)
+               sfree (array[i]);
+       sfree (array);
+} /* }}} void strarray_free */
index 8a7d986..29590ff 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/common.h
- * Copyright (C) 2005-2010  Florian octo Forster
+ * Copyright (C) 2005-2014  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
@@ -55,8 +55,21 @@ struct rate_to_value_state_s
 };
 typedef struct rate_to_value_state_s rate_to_value_state_t;
 
+struct value_to_rate_state_s
+{
+  value_t last_value;
+  cdtime_t last_time;
+};
+typedef struct value_to_rate_state_s value_to_rate_state_t;
+
 char *sstrncpy (char *dest, const char *src, size_t n);
+
+__attribute__ ((format(printf,3,4)))
 int ssnprintf (char *dest, size_t n, const char *format, ...);
+
+__attribute__ ((format(printf,1,2)))
+char *ssnprintf_alloc (char const *format, ...);
+
 char *sstrdup(const char *s);
 void *smalloc(size_t size);
 char *sstrerror (int errnum, char *buf, size_t buflen);
@@ -152,19 +165,21 @@ int strjoin (char *dst, size_t dst_len, char **fields, size_t fields_num, const
  *   escape_slashes
  *
  * DESCRIPTION
- *   Removes slashes from the string `buf' and substitutes them with something
- *   appropriate. This function should be used whenever a path is to be used as
- *   (part of) an instance.
+ *   Removes slashes ("/") from "buffer". If buffer contains a single slash,
+ *   the result will be "root". Leading slashes are removed. All other slashes
+ *   are replaced with underscores ("_").
+ *   This function is used by plugin_dispatch_values() to escape all parts of
+ *   the identifier.
  *
  * PARAMETERS
- *   `buf'         String to be escaped.
- *   `buf_len'     Length of the buffer. No more then this many bytes will be
- *   written to `buf', including the trailing null-byte.
+ *   `buffer'         String to be escaped.
+ *   `buffer_size'    Size of the buffer. No more then this many bytes will be
+ *                    written to `buffer', including the trailing null-byte.
  *
  * RETURN VALUE
  *   Returns zero upon success and a value smaller than zero upon failure.
  */
-int escape_slashes (char *buf, int buf_len);
+int escape_slashes (char *buffer, size_t buffer_size);
 
 /*
  * NAME
@@ -208,6 +223,13 @@ int strsubstitute (char *str, char c_from, char c_to);
  */
 int strunescape (char *buf, size_t buf_len);
 
+/**
+ * Removed trailing newline characters (CR and LF) from buffer, which must be
+ * null terminated. Returns the length of the resulting string.
+ */
+__attribute__((nonnull (1)))
+size_t strstripnewline (char *buffer);
+
 /*
  * NAME
  *   timeval_cmp
@@ -296,7 +318,7 @@ typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename,
 int walk_directory (const char *dir, dirwalk_callback_f callback,
                void *user_data, int hidden);
 /* Returns the number of bytes read or negative on error. */
-int read_file_contents (const char *filename, char *buf, int bufsize);
+ssize_t read_file_contents (char const *filename, char *buf, size_t bufsize);
 
 counter_t counter_diff (counter_t old_value, counter_t new_value);
 
@@ -309,6 +331,9 @@ counter_t counter_diff (counter_t old_value, counter_t new_value);
 int rate_to_value (value_t *ret_value, gauge_t rate,
                rate_to_value_state_t *state, int ds_type, cdtime_t t);
 
+int value_to_rate (value_t *ret_rate, derive_t value,
+               value_to_rate_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);
@@ -317,4 +342,7 @@ int service_name_to_port_number (const char *service_name);
  * failure. If failure is returned, ret_value is not touched. */
 int strtoderive (const char *string, derive_t *ret_value);
 
+int strarray_add (char ***ret_array, size_t *ret_array_len, char const *str);
+void strarray_free (char **array, size_t array_len);
+
 #endif /* COMMON_H */
index 5920c53..c5d3e36 100644 (file)
 # include <wordexp.h>
 #endif /* HAVE_WORDEXP_H */
 
+#if HAVE_FNMATCH_H
+# include <fnmatch.h>
+#endif /* HAVE_FNMATCH_H */
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif /* HAVE_LIBGEN_H */
+
 #define ESCAPE_NULL(str) ((str) == NULL ? "(null)" : (str))
 
 /*
@@ -90,7 +98,7 @@ static cf_value_map_t cf_value_map[] =
        {"PluginDir",  dispatch_value_plugindir},
        {"LoadPlugin", dispatch_loadplugin}
 };
-static int cf_value_map_num = STATIC_ARRAY_LEN (cf_value_map);
+static int cf_value_map_num = STATIC_ARRAY_SIZE (cf_value_map);
 
 static cf_global_option_t cf_global_options[] =
 {
@@ -100,11 +108,15 @@ static cf_global_option_t cf_global_options[] =
        {"FQDNLookup",  NULL, "true"},
        {"Interval",    NULL, NULL},
        {"ReadThreads", NULL, "5"},
+       {"WriteThreads", NULL, "5"},
+       {"WriteQueueLimitHigh", NULL, NULL},
+       {"WriteQueueLimitLow", NULL, NULL},
        {"Timeout",     NULL, "2"},
+       {"AutoLoadPlugin", NULL, "false"},
        {"PreCacheChain",  NULL, "PreCache"},
        {"PostCacheChain", NULL, "PostCache"}
 };
-static int cf_global_options_num = STATIC_ARRAY_LEN (cf_global_options);
+static int cf_global_options_num = STATIC_ARRAY_SIZE (cf_global_options);
 
 static int cf_default_typesdb = 1;
 
@@ -268,21 +280,6 @@ static int dispatch_loadplugin (const oconfig_item_t *ci)
        memset (&ctx, 0, sizeof (ctx));
        ctx.interval = cf_get_default_interval ();
 
-       /*
-        * XXX: Magic at work:
-        *
-        * Some of the language bindings, for example the Python and Perl
-        * plugins, need to be able to export symbols to the scripts they run.
-        * For this to happen, the "Globals" flag needs to be set.
-        * Unfortunately, this technical detail is hard to explain to the
-        * average user and she shouldn't have to worry about this, ideally.
-        * So in order to save everyone's sanity use a different default for a
-        * handful of special plugins. --octo
-        */
-       if ((strcasecmp ("Perl", name) == 0)
-                       || (strcasecmp ("Python", name) == 0))
-               flags |= PLUGIN_FLAGS_GLOBAL;
-
        for (i = 0; i < ci->children_num; ++i) {
                if (strcasecmp("Globals", ci->children[i].key) == 0)
                        cf_util_get_flag (ci->children + i, &flags, PLUGIN_FLAGS_GLOBAL);
@@ -385,6 +382,19 @@ static int dispatch_block_plugin (oconfig_item_t *ci)
 
        name = ci->values[0].value.string;
 
+       if (IS_TRUE (global_option_get ("AutoLoadPlugin")))
+       {
+               int status;
+
+               status = plugin_load (name, /* flags = */ 0);
+               if (status != 0)
+               {
+                       ERROR ("Automatically loading plugin \"%s\" failed "
+                                       "with status %i.", name, status);
+                       return (status);
+               }
+       }
+
        /* Check for a complex callback first */
        for (cb = complex_callback_head; cb != NULL; cb = cb->next)
        {
@@ -535,7 +545,8 @@ static int cf_ci_append_children (oconfig_item_t *dst, oconfig_item_t *src)
 } /* int cf_ci_append_children */
 
 #define CF_MAX_DEPTH 8
-static oconfig_item_t *cf_read_generic (const char *path, int depth);
+static oconfig_item_t *cf_read_generic (const char *path,
+               const char *pattern, int depth);
 
 static int cf_include_all (oconfig_item_t *root, int depth)
 {
@@ -546,9 +557,9 @@ static int cf_include_all (oconfig_item_t *root, int depth)
                oconfig_item_t *new;
                oconfig_item_t *old;
 
-               /* Ignore all blocks, including `Include' blocks. */
-               if (root->children[i].children_num != 0)
-                       continue;
+               char *pattern = NULL;
+
+               int j;
 
                if (strcasecmp (root->children[i].key, "Include") != 0)
                        continue;
@@ -562,9 +573,22 @@ static int cf_include_all (oconfig_item_t *root, int depth)
                        continue;
                }
 
-               new = cf_read_generic (old->values[0].value.string, depth + 1);
+               for (j = 0; j < old->children_num; ++j)
+               {
+                       oconfig_item_t *child = old->children + j;
+
+                       if (strcasecmp (child->key, "Filter") == 0)
+                               cf_util_get_string (child, &pattern);
+                       else
+                               ERROR ("configfile: Option `%s' not allowed in <Include> block.",
+                                               child->key);
+               }
+
+               new = cf_read_generic (old->values[0].value.string, pattern, depth + 1);
+               sfree (pattern);
+
                if (new == NULL)
-                       continue;
+                       return (-1);
 
                /* Now replace the i'th child in `root' with `new'. */
                cf_ci_replace_child (root, new, i);
@@ -579,12 +603,35 @@ static int cf_include_all (oconfig_item_t *root, int depth)
        return (0);
 } /* int cf_include_all */
 
-static oconfig_item_t *cf_read_file (const char *file, int depth)
+static oconfig_item_t *cf_read_file (const char *file,
+               const char *pattern, int depth)
 {
        oconfig_item_t *root;
+       int status;
 
        assert (depth < CF_MAX_DEPTH);
 
+       if (pattern != NULL) {
+#if HAVE_FNMATCH_H && HAVE_LIBGEN_H
+               char *tmp = sstrdup (file);
+               char *filename = basename (tmp);
+
+               if ((filename != NULL) && (fnmatch (pattern, filename, 0) != 0)) {
+                       DEBUG ("configfile: Not including `%s' because it "
+                                       "does not match pattern `%s'.",
+                                       filename, pattern);
+                       free (tmp);
+                       return (NULL);
+               }
+
+               free (tmp);
+#else
+               ERROR ("configfile: Cannot apply pattern filter '%s' "
+                               "to file '%s': functions basename() and / or "
+                               "fnmatch() not available.", pattern, file);
+#endif /* HAVE_FNMATCH_H && HAVE_LIBGEN_H */
+       }
+
        root = oconfig_parse_file (file);
        if (root == NULL)
        {
@@ -592,7 +639,12 @@ static oconfig_item_t *cf_read_file (const char *file, int depth)
                return (NULL);
        }
 
-       cf_include_all (root, depth);
+       status = cf_include_all (root, depth);
+       if (status != 0)
+       {
+               oconfig_free (root);
+               return (NULL);
+       }
 
        return (root);
 } /* oconfig_item_t *cf_read_file */
@@ -602,7 +654,8 @@ static int cf_compare_string (const void *p1, const void *p2)
        return strcmp (*(const char **) p1, *(const char **) p2);
 }
 
-static oconfig_item_t *cf_read_dir (const char *dir, int depth)
+static oconfig_item_t *cf_read_dir (const char *dir,
+               const char *pattern, int depth)
 {
        oconfig_item_t *root = NULL;
        DIR *dh;
@@ -677,7 +730,7 @@ static oconfig_item_t *cf_read_dir (const char *dir, int depth)
                oconfig_item_t *temp;
                char *name = filenames[i];
 
-               temp = cf_read_generic (name, depth);
+               temp = cf_read_generic (name, pattern, depth);
                if (temp == NULL)
                {
                        /* An error should already have been reported. */
@@ -708,7 +761,8 @@ static oconfig_item_t *cf_read_dir (const char *dir, int depth)
  * simpler function is used which does not do any such expansion.
  */
 #if HAVE_WORDEXP_H
-static oconfig_item_t *cf_read_generic (const char *path, int depth)
+static oconfig_item_t *cf_read_generic (const char *path,
+               const char *pattern, int depth)
 {
        oconfig_item_t *root = NULL;
        int status;
@@ -761,9 +815,9 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth)
                }
 
                if (S_ISREG (statbuf.st_mode))
-                       temp = cf_read_file (path_ptr, depth);
+                       temp = cf_read_file (path_ptr, pattern, depth);
                else if (S_ISDIR (statbuf.st_mode))
-                       temp = cf_read_dir (path_ptr, depth);
+                       temp = cf_read_dir (path_ptr, pattern, depth);
                else
                {
                        WARNING ("configfile: %s is neither a file nor a "
@@ -783,18 +837,13 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth)
 
        wordfree (&we);
 
-       if (root->children == NULL)
-       {
-               oconfig_free (root);
-               return (NULL);
-       }
-
        return (root);
 } /* oconfig_item_t *cf_read_generic */
 /* #endif HAVE_WORDEXP_H */
 
 #else /* if !HAVE_WORDEXP_H */
-static oconfig_item_t *cf_read_generic (const char *path, int depth)
+static oconfig_item_t *cf_read_generic (const char *path,
+               const char *pattern, int depth)
 {
        struct stat statbuf;
        int status;
@@ -817,9 +866,9 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth)
        }
 
        if (S_ISREG (statbuf.st_mode))
-               return (cf_read_file (path, depth));
+               return (cf_read_file (path, pattern, depth));
        else if (S_ISDIR (statbuf.st_mode))
-               return (cf_read_dir (path, depth));
+               return (cf_read_dir (path, pattern, depth));
 
        ERROR ("configfile: %s is neither a file nor a directory.", path);
        return (NULL);
@@ -842,6 +891,13 @@ int global_option_set (const char *option, const char *value)
        if (i >= cf_global_options_num)
                return (-1);
 
+       if (strcasecmp (option, "PIDFile") == 0 && pidfile_from_cli == 1)
+       {
+               DEBUG ("Configfile: Ignoring `PIDFILE' option because "
+                       "command-line option `-P' take precedence.");
+               return (0);
+       }
+
        sfree (cf_global_options[i].value);
 
        if (value != NULL)
@@ -868,6 +924,23 @@ const char *global_option_get (const char *option)
                        : cf_global_options[i].def);
 } /* char *global_option_get */
 
+long global_option_get_long (const char *option, long default_value)
+{
+               const char *str;
+               long value;
+
+               str = global_option_get (option);
+               if (NULL == str)
+                       return (default_value);
+
+               errno = 0;
+               value = strtol (str, /* endptr = */ NULL, /* base = */ 0);
+               if (errno != 0)
+                       return (default_value);
+
+               return (value);
+} /* char *global_option_get_long */
+
 cdtime_t cf_get_default_interval (void)
 {
   char const *str = global_option_get ("Interval");
@@ -993,12 +1066,18 @@ int cf_read (char *filename)
        oconfig_item_t *conf;
        int i;
 
-       conf = cf_read_generic (filename, 0 /* depth */);
+       conf = cf_read_generic (filename, /* pattern = */ NULL, /* depth = */ 0);
        if (conf == NULL)
        {
                ERROR ("Unable to read config file %s.", filename);
                return (-1);
        }
+       else if (conf->children_num == 0)
+       {
+               ERROR ("Configuration file %s is empty.", filename);
+               oconfig_free (conf);
+               return (-1);
+       }
 
        for (i = 0; i < conf->children_num; i++)
        {
index 5a719a4..c91fcd5 100644 (file)
@@ -86,6 +86,8 @@ int cf_read (char *filename);
 
 int global_option_set (const char *option, const char *value);
 const char *global_option_get (const char *option);
+long global_option_get_long (const char *option, long default_value);
+long global_option_get_long_in_range (const char *option, long default_value, long min, long max);
 
 cdtime_t cf_get_default_interval (void);
 
index 33236c4..e7bccad 100644 (file)
@@ -8,7 +8,7 @@
  *
  * 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
+ * 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
 #endif
 
 #define CONNTRACK_FILE "/proc/sys/net/netfilter/nf_conntrack_count"
+#define CONNTRACK_MAX_FILE "/proc/sys/net/netfilter/nf_conntrack_max"
 
-static void conntrack_submit (value_t conntrack)
+static void conntrack_submit (const char *type, const char *type_instance,
+                             value_t conntrack)
 {
        value_list_t vl = VALUE_LIST_INIT;
 
@@ -39,14 +41,17 @@ static void conntrack_submit (value_t conntrack)
        vl.values_len = 1;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "conntrack", sizeof (vl.plugin));
-       sstrncpy (vl.type, "conntrack", sizeof (vl.type));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       if (type_instance != NULL)
+               sstrncpy (vl.type_instance, type_instance,
+                         sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
 } /* static void conntrack_submit */
 
 static int conntrack_read (void)
 {
-       value_t conntrack;
+       value_t conntrack, conntrack_max, conntrack_pct;
        FILE *fh;
        char buffer[64];
        size_t buffer_len;
@@ -74,7 +79,35 @@ static int conntrack_read (void)
        if (parse_value (buffer, &conntrack, DS_TYPE_GAUGE) != 0)
                return (-1);
 
-       conntrack_submit (conntrack);
+       conntrack_submit ("conntrack", NULL, conntrack);
+
+       fh = fopen (CONNTRACK_MAX_FILE, "r");
+       if (fh == NULL)
+               return (-1);
+
+       memset (buffer, 0, sizeof (buffer));
+       if (fgets (buffer, sizeof (buffer), fh) == NULL)
+       {
+               fclose (fh);
+               return (-1);
+       }
+       fclose (fh);
+
+       /* 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_max, DS_TYPE_GAUGE) != 0)
+               return (-1);
+
+       conntrack_submit ("conntrack", "max", conntrack_max);
+       conntrack_pct.gauge = (conntrack.gauge / conntrack_max.gauge) * 100;
+       conntrack_submit ("percent", "used", conntrack_pct);
+
 
        return (0);
 } /* static int conntrack_read */
index 5448003..79dc150 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -1,9 +1,9 @@
 /**
  * collectd - src/cpu.c
  * Copyright (C) 2005-2010  Florian octo Forster
- * Copyright (C) 2008       Oleg King
- * Copyright (C) 2009       Simon Kuhnle
- * Copyright (C) 2009       Manuel Sanmartin
+ * Copyright (C) 2008      Oleg King
+ * Copyright (C) 2009      Simon Kuhnle
+ * Copyright (C) 2009      Manuel Sanmartin
  *
  * 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
@@ -11,7 +11,7 @@
  *
  * 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
+ * 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
 # define CAN_USE_SYSCTL 0
 #endif
 
+#define CPU_SUBMIT_USER 0
+#define CPU_SUBMIT_SYSTEM 1
+#define CPU_SUBMIT_WAIT 2
+#define CPU_SUBMIT_NICE 3
+#define CPU_SUBMIT_SWAP 4
+#define CPU_SUBMIT_INTERRUPT 5
+#define CPU_SUBMIT_SOFTIRQ 6
+#define CPU_SUBMIT_STEAL 7
+#define CPU_SUBMIT_IDLE 8
+#define CPU_SUBMIT_ACTIVE 9
+#define CPU_SUBMIT_MAX 10
+
 #if HAVE_STATGRAB_H
 # include <statgrab.h>
 #endif
 # error "No applicable input method."
 #endif
 
+static const char *cpu_state_names[] = {
+       "user",
+       "system",
+       "wait",
+       "nice",
+       "swap",
+       "interrupt",
+       "softirq",
+       "steal",
+       "idle",
+       "active"
+};
+
 #ifdef PROCESSOR_CPU_LOAD_INFO
 static mach_port_t port_host;
 static processor_port_array_t cpu_list;
@@ -107,8 +132,8 @@ static mach_msg_type_number_t cpu_list_len;
 
 #if PROCESSOR_TEMPERATURE
 static int cpu_temp_retry_counter = 0;
-static int cpu_temp_retry_step    = 1;
-static int cpu_temp_retry_max     = 1;
+static int cpu_temp_retry_step   = 1;
+static int cpu_temp_retry_max    = 1;
 #endif /* PROCESSOR_TEMPERATURE */
 /* #endif PROCESSOR_CPU_LOAD_INFO */
 
@@ -145,6 +170,87 @@ static int numcpu;
 static int pnumcpu;
 #endif /* HAVE_PERFSTAT */
 
+static value_to_rate_state_t *percents = NULL;
+static gauge_t agg_percents[CPU_SUBMIT_MAX] = {
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+
+};
+static int percents_cells = 0;
+static int cpu_count = 0;
+
+
+static _Bool report_by_cpu = 1;
+static _Bool report_percent = 0;
+static _Bool report_active = 0;
+
+static const char *config_keys[] =
+{
+       "ReportByCpu",
+       "ReportActive",
+       "ValuesPercentage"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+
+static int cpu_config (const char *key, const char *value)
+{
+       if (strcasecmp (key, "ReportByCpu") == 0) {
+               report_by_cpu = IS_TRUE (value) ? 1 : 0;
+               if (!report_by_cpu)
+                       report_percent = 1;
+       }
+       if (strcasecmp (key, "ValuesPercentage") == 0) {
+               report_percent = IS_TRUE (value) ? 1 : 0;
+               if (!report_percent)
+                       report_by_cpu = 1;
+       }
+       if (strcasecmp (key, "ReportActive") == 0)
+               report_active = IS_TRUE (value) ? 1 : 0;
+       return (-1);
+}
+
+static int cpu_states_grow (void)
+{
+  void *tmp;
+  int size;
+  int i;
+
+  size = cpu_count * CPU_SUBMIT_MAX; /* always alloc for all states */
+
+  if (size <= 0)
+         return 0;
+
+  if (percents_cells >= size)
+         return 0;
+
+  if (percents == NULL) {
+         percents = malloc(size * sizeof(*percents));
+         if (percents == NULL)
+                 return -1;
+         for (i = 0; i < size; i++)
+                 memset(&percents[i], 0, sizeof(*percents));
+         percents_cells = size;
+         return 0;
+  }
+
+  tmp = realloc(percents, size * sizeof(*percents));
+
+  if (tmp == NULL) {
+         ERROR ("cpu plugin: could not reserve enough space to hold states");
+         percents = NULL;
+         return -1;
+  }
+
+  percents = tmp;
+
+  for (i = percents_cells ; i < size; i++)
+         memset(&percents[i], 0, sizeof(*percents));
+
+  percents_cells = size;
+  return 0;
+} /* cpu_states_grow */
+
+
 static int init (void)
 {
 #if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE
@@ -241,39 +347,156 @@ static int init (void)
        return (0);
 } /* int init */
 
-static void submit (int cpu_num, const char *type_instance, derive_t value)
+static void submit_value (int cpu_num, int cpu_state, const char *type, value_t value)
 {
        value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].derive = value;
+       memcpy(&values[0], &value, sizeof(value));
 
        vl.values = values;
        vl.values_len = 1;
+
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "cpu", sizeof (vl.plugin));
-       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
-                       "%i", cpu_num);
-       sstrncpy (vl.type, "cpu", sizeof (vl.type));
-       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       sstrncpy (vl.type_instance, cpu_state_names[cpu_state],
+                 sizeof (vl.type_instance));
 
+       if (cpu_num >= 0) {
+               ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+                          "%i", cpu_num);
+       }
        plugin_dispatch_values (&vl);
 }
 
+static void submit_percent(int cpu_num, int cpu_state, gauge_t percent)
+{
+       value_t value;
+
+       value.gauge = percent;
+       submit_value (cpu_num, cpu_state, "percent", value);
+}
+
+static void submit_derive(int cpu_num, int cpu_state, derive_t derive)
+{
+       value_t value;
+
+       value.derive = derive;
+       submit_value (cpu_num, cpu_state, "cpu", value);
+}
+
+static void submit_flush (void)
+{
+       int i = 0;
+
+       if (report_by_cpu) {
+               cpu_count = 0;
+               return;
+       }
+
+       for (i = 0; i < CPU_SUBMIT_MAX; i++) {
+               if (agg_percents[i] == -1)
+                       continue;
+
+               submit_percent(-1, i, agg_percents[i] / cpu_count);
+               agg_percents[i] = -1;
+       }
+       cpu_count = 0;
+}
+
+static void submit (int cpu_num, derive_t *derives)
+{
+
+       int i = 0;
+
+       if (!report_percent && report_by_cpu) {
+               derive_t cpu_active = 0;
+               for (i = 0; i < CPU_SUBMIT_ACTIVE; i++)
+               {
+                       if (derives[i] == -1)
+                               continue;
+
+                       if (i != CPU_SUBMIT_IDLE)
+                               cpu_active += derives[i];
+
+                       submit_derive(cpu_num, i, derives[i]);
+               }
+               if (report_active)
+                       submit_derive(cpu_num, CPU_SUBMIT_ACTIVE, cpu_active);
+       }
+       else /* we are reporting percents */
+       {
+               cdtime_t cdt;
+               gauge_t percent;
+               gauge_t cpu_total = 0;
+               gauge_t cpu_active = 0;
+               gauge_t local_rates[CPU_SUBMIT_MAX];
+
+               cpu_count++;
+               if (cpu_states_grow())
+                       return;
+
+               memset(local_rates, 0, sizeof(local_rates));
+
+               cdt = cdtime();
+               for (i = 0; i < CPU_SUBMIT_ACTIVE; i++) {
+                       value_t rate;
+                       int index;
+
+                       if (derives[i] == -1)
+                               continue;
+
+                       index = (cpu_num * CPU_SUBMIT_MAX) + i;
+                       if (value_to_rate(&rate, derives[i], &percents[index],
+                                         DS_TYPE_DERIVE, cdt) != 0) {
+                               local_rates[i] = -1;
+                               continue;
+                       }
+
+                       local_rates[i] = rate.gauge;
+                       cpu_total += rate.gauge;
+                       if (i != CPU_SUBMIT_IDLE)
+                               cpu_active += rate.gauge;
+               }
+               if (cpu_total == 0.0)
+                       return;
+
+               if (report_active)
+                       local_rates[CPU_SUBMIT_ACTIVE] = cpu_active;
+
+               for (i = 0; i < CPU_SUBMIT_MAX; i++) {
+                       if (local_rates[i] == -1)
+                               continue;
+
+                       percent = (local_rates[i] / cpu_total) * 100;
+                       if (report_by_cpu)
+                               submit_percent (cpu_num, i, percent);
+                       else {
+                               if (agg_percents[i] == -1)
+                                       agg_percents[i] = percent;
+                               else
+                                       agg_percents[i] += percent;
+                       }
+
+               }
+       }
+}
+
 static int cpu_read (void)
 {
 #if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE
        int cpu;
 
        kern_return_t status;
-       
+
 #if PROCESSOR_CPU_LOAD_INFO
        processor_cpu_load_info_data_t cpu_info;
-       mach_msg_type_number_t         cpu_info_len;
+       mach_msg_type_number_t         cpu_info_len;
 #endif
 #if PROCESSOR_TEMPERATURE
-       processor_info_data_t          cpu_temp;
-       mach_msg_type_number_t         cpu_temp_len;
+       processor_info_data_t          cpu_temp;
+       mach_msg_type_number_t         cpu_temp_len;
 #endif
 
        host_t cpu_host;
@@ -281,6 +504,10 @@ static int cpu_read (void)
        for (cpu = 0; cpu < cpu_list_len; cpu++)
        {
 #if PROCESSOR_CPU_LOAD_INFO
+               derive_t derives[CPU_SUBMIT_MAX] = {
+                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+               };
+               memset(derives, -1, sizeof(derives));
                cpu_host = 0;
                cpu_info_len = PROCESSOR_BASIC_INFO_COUNT;
 
@@ -298,10 +525,12 @@ static int cpu_read (void)
                        continue;
                }
 
-               submit (cpu, "user", (derive_t) cpu_info.cpu_ticks[CPU_STATE_USER]);
-               submit (cpu, "nice", (derive_t) cpu_info.cpu_ticks[CPU_STATE_NICE]);
-               submit (cpu, "system", (derive_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM]);
-               submit (cpu, "idle", (derive_t) cpu_info.cpu_ticks[CPU_STATE_IDLE]);
+               derives[CPU_SUBMIT_USER] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_USER];
+               derives[CPU_SUBMIT_NICE] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_NICE];
+               derives[CPU_SUBMIT_SYSTEM] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM];
+               derives[CPU_SUBMIT_IDLE] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_IDLE];
+               submit (cpu, derives);
+
 #endif /* PROCESSOR_CPU_LOAD_INFO */
 #if PROCESSOR_TEMPERATURE
                /*
@@ -338,22 +567,19 @@ static int cpu_read (void)
                if (cpu_temp_len != 1)
                {
                        DEBUG ("processor_info (PROCESSOR_TEMPERATURE) returned %i elements..?",
-                                       (int) cpu_temp_len);
+                                       (int) cpu_temp_len);
                        continue;
                }
 
                cpu_temp_retry_counter = 0;
                cpu_temp_retry_step    = 1;
-
-               DEBUG ("cpu_temp = %i", (int) cpu_temp);
 #endif /* PROCESSOR_TEMPERATURE */
        }
+       submit_flush ();
 /* #endif PROCESSOR_CPU_LOAD_INFO */
 
 #elif defined(KERNEL_LINUX)
        int cpu;
-       derive_t user, nice, syst, idle;
-       derive_t wait, intr, sitr; /* sitr == soft interrupt */
        FILE *fh;
        char buf[1024];
 
@@ -370,6 +596,10 @@ static int cpu_read (void)
 
        while (fgets (buf, 1024, fh) != NULL)
        {
+               derive_t derives[CPU_SUBMIT_MAX] = {
+                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+               };
+
                if (strncmp (buf, "cpu", 3))
                        continue;
                if ((buf[3] < '0') || (buf[3] > '9'))
@@ -380,37 +610,29 @@ static int cpu_read (void)
                        continue;
 
                cpu = atoi (fields[0] + 3);
-               user = atoll (fields[1]);
-               nice = atoll (fields[2]);
-               syst = atoll (fields[3]);
-               idle = atoll (fields[4]);
-
-               submit (cpu, "user", user);
-               submit (cpu, "nice", nice);
-               submit (cpu, "system", syst);
-               submit (cpu, "idle", idle);
+               derives[CPU_SUBMIT_USER] = atoll(fields[1]);
+               derives[CPU_SUBMIT_NICE] = atoll(fields[2]);
+               derives[CPU_SUBMIT_SYSTEM] = atoll(fields[3]);
+               derives[CPU_SUBMIT_IDLE] = atoll(fields[4]);
 
                if (numfields >= 8)
                {
-                       wait = atoll (fields[5]);
-                       intr = atoll (fields[6]);
-                       sitr = atoll (fields[7]);
-
-                       submit (cpu, "wait", wait);
-                       submit (cpu, "interrupt", intr);
-                       submit (cpu, "softirq", sitr);
+                       derives[CPU_SUBMIT_WAIT] = atoll(fields[5]);
+                       derives[CPU_SUBMIT_INTERRUPT] = atoll(fields[6]);
+                       derives[CPU_SUBMIT_SOFTIRQ] = atoll(fields[6]);
 
                        if (numfields >= 9)
-                               submit (cpu, "steal", atoll (fields[8]));
+                               derives[CPU_SUBMIT_STEAL] = atoll(fields[8]);
                }
+               submit(cpu, derives);
        }
+       submit_flush();
 
        fclose (fh);
 /* #endif defined(KERNEL_LINUX) */
 
 #elif defined(HAVE_LIBKSTAT)
        int cpu;
-       derive_t user, syst, idle, wait;
        static cpu_stat_t cs;
 
        if (kc == NULL)
@@ -418,19 +640,21 @@ static int cpu_read (void)
 
        for (cpu = 0; cpu < numcpu; cpu++)
        {
+               derive_t derives[CPU_SUBMIT_MAX] = {
+                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+               };
+
                if (kstat_read (kc, ksp[cpu], &cs) == -1)
                        continue; /* error message? */
 
-               idle = (derive_t) cs.cpu_sysinfo.cpu[CPU_IDLE];
-               user = (derive_t) cs.cpu_sysinfo.cpu[CPU_USER];
-               syst = (derive_t) cs.cpu_sysinfo.cpu[CPU_KERNEL];
-               wait = (derive_t) cs.cpu_sysinfo.cpu[CPU_WAIT];
-
-               submit (ksp[cpu]->ks_instance, "user", user);
-               submit (ksp[cpu]->ks_instance, "system", syst);
-               submit (ksp[cpu]->ks_instance, "idle", idle);
-               submit (ksp[cpu]->ks_instance, "wait", wait);
+               memset(derives, -1, sizeof(derives));
+               derives[CPU_SUBMIT_IDLE] = cs.cpu_sysinfo.cpu[CPU_IDLE];
+               derives[CPU_SUBMIT_USER] = cs.cpu_sysinfo.cpu[CPU_USER];
+               derives[CPU_SUBMIT_SYSTEM] = cs.cpu_sysinfo.cpu[CPU_KERNEL];
+               derives[CPU_SUBMIT_WAIT] = cs.cpu_sysinfo.cpu[CPU_WAIT];
+               submit (ksp[cpu]->ks_instance, derives);
        }
+       submit_flush ();
 /* #endif defined(HAVE_LIBKSTAT) */
 
 #elif CAN_USE_SYSCTL
@@ -489,12 +713,18 @@ static int cpu_read (void)
        }
 
        for (i = 0; i < numcpu; i++) {
-               submit (i, "user",      cpuinfo[i][CP_USER]);
-               submit (i, "nice",      cpuinfo[i][CP_NICE]);
-               submit (i, "system",    cpuinfo[i][CP_SYS]);
-               submit (i, "idle",      cpuinfo[i][CP_IDLE]);
-               submit (i, "interrupt", cpuinfo[i][CP_INTR]);
+               derive_t derives[CPU_SUBMIT_MAX] = {
+                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+               };
+
+               derives[CPU_SUBMIT_USER] = cpuinfo[i][CP_USER];
+               derives[CPU_SUBMIT_NICE] = cpuinfo[i][CP_NICE];
+               derives[CPU_SUBMIT_SYSTEM] = cpuinfo[i][CP_SYS];
+               derives[CPU_SUBMIT_IDLE] = cpuinfo[i][CP_IDLE];
+               derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[i][CP_INTR];
+               submit(i, derives);
        }
+       submit_flush();
 /* #endif CAN_USE_SYSCTL */
 #elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES)
        long cpuinfo[maxcpu][CPUSTATES];
@@ -513,16 +743,26 @@ static int cpu_read (void)
        }
 
        for (i = 0; i < numcpu; i++) {
-               submit (i, "user", cpuinfo[i][CP_USER]);
-               submit (i, "nice", cpuinfo[i][CP_NICE]);
-               submit (i, "system", cpuinfo[i][CP_SYS]);
-               submit (i, "idle", cpuinfo[i][CP_IDLE]);
-               submit (i, "interrupt", cpuinfo[i][CP_INTR]);
+               derive_t derives[CPU_SUBMIT_MAX] = {
+                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+               };
+
+               derives[CPU_SUBMIT_USER] = cpuinfo[i][CP_USER];
+               derives[CPU_SUBMIT_NICE] = cpuinfo[i][CP_NICE];
+               derives[CPU_SUBMIT_SYSTEM] = cpuinfo[i][CP_SYS];
+               derives[CPU_SUBMIT_IDLE] = cpuinfo[i][CP_IDLE];
+               derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[i][CP_INTR];
+               submit(i, derives);
        }
+       submit_flush();
+
 /* #endif HAVE_SYSCTL_KERN_CP_TIMES */
 #elif defined(HAVE_SYSCTLBYNAME)
        long cpuinfo[CPUSTATES];
        size_t cpuinfo_size;
+       derive_t derives[CPU_SUBMIT_MAX] = {
+               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+       };
 
        cpuinfo_size = sizeof (cpuinfo);
 
@@ -534,15 +774,21 @@ static int cpu_read (void)
                return (-1);
        }
 
-       submit (0, "user", cpuinfo[CP_USER]);
-       submit (0, "nice", cpuinfo[CP_NICE]);
-       submit (0, "system", cpuinfo[CP_SYS]);
-       submit (0, "idle", cpuinfo[CP_IDLE]);
-       submit (0, "interrupt", cpuinfo[CP_INTR]);
+       derives[CPU_SUBMIT_USER] = cpuinfo[CP_USER];
+       derives[CPU_SUBMIT_SYSTEM] = cpuinfo[CP_SYS];
+       derives[CPU_SUBMIT_NICE] = cpuinfo[CP_NICE];
+       derives[CPU_SUBMIT_IDLE] = cpuinfo[CP_IDLE];
+       derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[CP_INTR];
+       submit(0, derives);
+       submit_flush();
+
 /* #endif HAVE_SYSCTLBYNAME */
 
 #elif defined(HAVE_LIBSTATGRAB)
        sg_cpu_stats *cs;
+       derive_t derives[CPU_SUBMIT_MAX] = {
+               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+       };
        cs = sg_get_cpu_stats ();
 
        if (cs == NULL)
@@ -551,12 +797,14 @@ static int cpu_read (void)
                return (-1);
        }
 
-       submit (0, "idle",   (derive_t) cs->idle);
-       submit (0, "nice",   (derive_t) cs->nice);
-       submit (0, "swap",   (derive_t) cs->swap);
-       submit (0, "system", (derive_t) cs->kernel);
-       submit (0, "user",   (derive_t) cs->user);
-       submit (0, "wait",   (derive_t) cs->iowait);
+       derives[CPU_SUBMIT_IDLE] = (derive_t) cs->idle;
+       derives[CPU_SUBMIT_NICE] = (derive_t) cs->nice;
+       derives[CPU_SUBMIT_SWAP] = (derive_t) cs->swap;
+       derives[CPU_SUBMIT_SYSTEM] = (derive_t) cs->kernel;
+       derives[CPU_SUBMIT_USER] = (derive_t) cs->user;
+       derives[CPU_SUBMIT_WAIT] = (derive_t) cs->iowait;
+       submit(0, derives);
+       submit_flush();
 /* #endif HAVE_LIBSTATGRAB */
 
 #elif defined(HAVE_PERFSTAT)
@@ -571,10 +819,10 @@ static int cpu_read (void)
                        sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
-       
-       if (pnumcpu != numcpu || perfcpu == NULL) 
+
+       if (pnumcpu != numcpu || perfcpu == NULL)
        {
-               if (perfcpu != NULL) 
+               if (perfcpu != NULL)
                        free(perfcpu);
                perfcpu = malloc(numcpu * sizeof(perfstat_cpu_t));
        }
@@ -589,13 +837,18 @@ static int cpu_read (void)
                return (-1);
        }
 
-       for (i = 0; i < cpus; i++) 
+       for (i = 0; i < cpus; i++)
        {
-               submit (i, "idle",   (derive_t) perfcpu[i].idle);
-               submit (i, "system", (derive_t) perfcpu[i].sys);
-               submit (i, "user",   (derive_t) perfcpu[i].user);
-               submit (i, "wait",   (derive_t) perfcpu[i].wait);
+               derive_t derives[CPU_SUBMIT_MAX] = {
+                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+               };
+               derives[CPU_SUBMIT_IDLE] = perfcpu[i].idle;
+               derives[CPU_SUBMIT_SYSTEM] = perfcpu[i].sys;
+               derives[CPU_SUBMIT_USER] = perfcpu[i].user;
+               derives[CPU_SUBMIT_WAIT] = perfcpu[i].wait;
+               submit(i, derives);
        }
+       submit_flush();
 #endif /* HAVE_PERFSTAT */
 
        return (0);
@@ -604,5 +857,6 @@ static int cpu_read (void)
 void module_register (void)
 {
        plugin_register_init ("cpu", init);
+       plugin_register_config ("cpu", cpu_config, config_keys, config_keys_num);
        plugin_register_read ("cpu", cpu_read);
 } /* void module_register */
index 4b8aa72..81e580d 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <longintrepr.h>
 
-/* These two macros are basicly Py_BEGIN_ALLOW_THREADS and Py_BEGIN_ALLOW_THREADS
+/* These two macros are basically Py_BEGIN_ALLOW_THREADS and Py_BEGIN_ALLOW_THREADS
  * from the other direction. If a Python thread calls a C function
  * Py_BEGIN_ALLOW_THREADS is used to allow other python threads to run because
  * we don't intend to call any Python functions.
@@ -36,7 +36,7 @@
  * These two macros are used whenever a C thread intends to call some Python
  * function, usually because some registered callback was triggered.
  * Just like Py_BEGIN_ALLOW_THREADS it opens a block so these macros have to be
- * used in pairs. They aquire the GIL, create a new Python thread state and swap
+ * used in pairs. They acquire the GIL, create a new Python thread state and swap
  * the current thread state with the new one. This means this thread is now allowed
  * to execute Python code. */
 
index 02d62c1..958e34b 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -121,65 +121,62 @@ static int value_list_to_string (char *buffer, int buffer_len,
        return (0);
 } /* int value_list_to_string */
 
-static int value_list_to_filename (char *buffer, int buffer_len,
-               const data_set_t *ds, const value_list_t *vl)
+static int value_list_to_filename (char *buffer, size_t buffer_size,
+               value_list_t const *vl)
 {
-       int offset = 0;
        int status;
 
-       assert (0 == strcmp (ds->type, vl->type));
+       char *ptr = buffer;
+       size_t ptr_size = buffer_size;
+       time_t now;
+       struct tm struct_tm;
 
        if (datadir != NULL)
        {
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", datadir);
-               if ((status < 1) || (status >= buffer_len - offset))
-                       return (-1);
-               offset += status;
+               size_t len = strlen (datadir) + 1;
+
+               if (len >= ptr_size)
+                       return (ENOBUFS);
+
+               memcpy (ptr, datadir, len);
+               ptr[len-1] = '/';
+               ptr_size -= len;
+               ptr += len;
        }
 
-       status = ssnprintf (buffer + offset, buffer_len - offset,
-                       "%s/", vl->host);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       status = FORMAT_VL (ptr, ptr_size, vl);
+       if (status != 0)
+               return (status);
 
-       if (strlen (vl->plugin_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s/", vl->plugin, vl->plugin_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", vl->plugin);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       /* Skip all the time formatting stuff when printing to STDOUT or
+        * STDERR. */
+       if (use_stdio)
+               return (0);
 
-       if (strlen (vl->type_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s", vl->type, vl->type_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s", vl->type);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       ptr_size -= strlen (ptr);
+       ptr +=  strlen (ptr);
 
-       if (!use_stdio)
+       /* "-2013-07-12" => 11 bytes */
+       if (ptr_size < 12)
        {
-               time_t now;
-               struct tm stm;
+               ERROR ("csv plugin: Buffer too small.");
+               return (ENOMEM);
+       }
 
-               /* TODO: Find a way to minimize the calls to `localtime_r',
-                * since they are pretty expensive.. */
-               now = time (NULL);
-               if (localtime_r (&now, &stm) == NULL)
-               {
-                       ERROR ("csv plugin: localtime_r failed");
-                       return (1);
-               }
+       /* TODO: Find a way to minimize the calls to `localtime_r',
+        * since they are pretty expensive.. */
+       now = time (NULL);
+       if (localtime_r (&now, &struct_tm) == NULL)
+       {
+               ERROR ("csv plugin: localtime_r failed");
+               return (-1);
+       }
 
-               strftime (buffer + offset, buffer_len - offset,
-                               "-%Y-%m-%d", &stm);
+       status = strftime (ptr, ptr_size, "-%Y-%m-%d", &struct_tm);
+       if (status == 0) /* yep, it returns zero on error. */
+       {
+               ERROR ("csv plugin: strftime failed");
+               return (-1);
        }
 
        return (0);
@@ -218,7 +215,10 @@ static int csv_config (const char *key, const char *value)
        if (strcasecmp ("DataDir", key) == 0)
        {
                if (datadir != NULL)
+               {
                        free (datadir);
+                       datadir = NULL;
+               }
                if (strcasecmp ("stdout", value) == 0)
                {
                        use_stdio = 1;
@@ -275,7 +275,8 @@ static int csv_write (const data_set_t *ds, const value_list_t *vl,
                return -1;
        }
 
-       if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+       status = value_list_to_filename (filename, sizeof (filename), vl);
+       if (status != 0)
                return (-1);
 
        DEBUG ("csv plugin: csv_write: filename = %s;", filename);
index 2160b98..e189df6 100644 (file)
@@ -26,6 +26,7 @@
 #include "plugin.h"
 #include "configfile.h"
 #include "utils_match.h"
+#include "utils_time.h"
 
 #include <curl/curl.h>
 
@@ -57,10 +58,14 @@ struct web_page_s /* {{{ */
   char *user;
   char *pass;
   char *credentials;
-  int   verify_peer;
-  int   verify_host;
+  _Bool digest;
+  _Bool verify_peer;
+  _Bool verify_host;
   char *cacert;
-  int   response_time;
+  struct curl_slist *headers;
+  char *post_body;
+  _Bool response_time;
+  _Bool response_code;
 
   CURL *curl;
   char curl_errbuf[CURL_ERROR_SIZE];
@@ -87,7 +92,7 @@ static size_t cc_curl_callback (void *buf, /* {{{ */
 {
   web_page_t *wp;
   size_t len;
-  
+
   len = size * nmemb;
   if (len <= 0)
     return (len);
@@ -148,6 +153,8 @@ static void cc_web_page_free (web_page_t *wp) /* {{{ */
   sfree (wp->pass);
   sfree (wp->credentials);
   sfree (wp->cacert);
+  sfree (wp->post_body);
+  curl_slist_free_all (wp->headers);
 
   sfree (wp->buffer);
 
@@ -156,7 +163,7 @@ static void cc_web_page_free (web_page_t *wp) /* {{{ */
   sfree (wp);
 } /* }}} void cc_web_page_free */
 
-static int cc_config_add_string (const char *name, char **dest, /* {{{ */
+static int cc_config_append_string (const char *name, struct curl_slist **dest, /* {{{ */
     oconfig_item_t *ci)
 {
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
@@ -165,27 +172,12 @@ static int cc_config_add_string (const char *name, char **dest, /* {{{ */
     return (-1);
   }
 
-  sfree (*dest);
-  *dest = strdup (ci->values[0].value.string);
+  *dest = curl_slist_append(*dest, ci->values[0].value.string);
   if (*dest == NULL)
     return (-1);
 
   return (0);
-} /* }}} int cc_config_add_string */
-
-static int cc_config_set_boolean (const char *name, int *dest, /* {{{ */
-    oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
-  {
-    WARNING ("curl plugin: `%s' needs exactly one boolean argument.", name);
-    return (-1);
-  }
-
-  *dest = ci->values[0].value.boolean ? 1 : 0;
-
-  return (0);
-} /* }}} int cc_config_set_boolean */
+} /* }}} int cc_config_append_string */
 
 static int cc_config_add_match_dstype (int *dstype_ret, /* {{{ */
     oconfig_item_t *ci)
@@ -291,15 +283,15 @@ static int cc_config_add_match (web_page_t *page, /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("Regex", child->key) == 0)
-      status = cc_config_add_string ("Regex", &match->regex, child);
+      status = cf_util_get_string (child, &match->regex);
     else if (strcasecmp ("ExcludeRegex", child->key) == 0)
-      status = cc_config_add_string ("ExcludeRegex", &match->exclude_regex, child);
+      status = cf_util_get_string (child, &match->exclude_regex);
     else if (strcasecmp ("DSType", child->key) == 0)
       status = cc_config_add_match_dstype (&match->dstype, child);
     else if (strcasecmp ("Type", child->key) == 0)
-      status = cc_config_add_string ("Type", &match->type, child);
+      status = cf_util_get_string (child, &match->type);
     else if (strcasecmp ("Instance", child->key) == 0)
-      status = cc_config_add_string ("Instance", &match->instance, child);
+      status = cf_util_get_string (child, &match->instance);
     else
     {
       WARNING ("curl plugin: Option `%s' not allowed here.", child->key);
@@ -370,14 +362,14 @@ static int cc_page_init_curl (web_page_t *wp) /* {{{ */
     return (-1);
   }
 
-  curl_easy_setopt (wp->curl, CURLOPT_NOSIGNAL, 1);
+  curl_easy_setopt (wp->curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (wp->curl, CURLOPT_WRITEFUNCTION, cc_curl_callback);
   curl_easy_setopt (wp->curl, CURLOPT_WRITEDATA, wp);
-  curl_easy_setopt (wp->curl, CURLOPT_USERAGENT,
-      PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (wp->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (wp->curl, CURLOPT_ERRORBUFFER, wp->curl_errbuf);
   curl_easy_setopt (wp->curl, CURLOPT_URL, wp->url);
-  curl_easy_setopt (wp->curl, CURLOPT_FOLLOWLOCATION, 1);
+  curl_easy_setopt (wp->curl, CURLOPT_FOLLOWLOCATION, 1L);
+  curl_easy_setopt (wp->curl, CURLOPT_MAXREDIRS, 50L);
 
   if (wp->user != NULL)
   {
@@ -397,13 +389,24 @@ static int cc_page_init_curl (web_page_t *wp) /* {{{ */
     ssnprintf (wp->credentials, credentials_size, "%s:%s",
         wp->user, (wp->pass == NULL) ? "" : wp->pass);
     curl_easy_setopt (wp->curl, CURLOPT_USERPWD, wp->credentials);
+    
+    if (wp->digest)
+    {
+      curl_easy_setopt (wp->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
+      curl_easy_setopt (wp->curl, CURLOPT_USERNAME, wp->user);
+      curl_easy_setopt (wp->curl, CURLOPT_PASSWORD, wp->pass);
+    }
   }
 
-  curl_easy_setopt (wp->curl, CURLOPT_SSL_VERIFYPEER, wp->verify_peer);
+  curl_easy_setopt (wp->curl, CURLOPT_SSL_VERIFYPEER, (long) wp->verify_peer);
   curl_easy_setopt (wp->curl, CURLOPT_SSL_VERIFYHOST,
-      wp->verify_host ? 2 : 0);
+      wp->verify_host ? 2L : 0L);
   if (wp->cacert != NULL)
     curl_easy_setopt (wp->curl, CURLOPT_CAINFO, wp->cacert);
+  if (wp->headers != NULL)
+    curl_easy_setopt (wp->curl, CURLOPT_HTTPHEADER, wp->headers);
+  if (wp->post_body != NULL)
+    curl_easy_setopt (wp->curl, CURLOPT_POSTFIELDS, wp->post_body);
 
   return (0);
 } /* }}} int cc_page_init_curl */
@@ -430,9 +433,11 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
   page->url = NULL;
   page->user = NULL;
   page->pass = NULL;
+  page->digest = 0;
   page->verify_peer = 1;
   page->verify_host = 1;
   page->response_time = 0;
+  page->response_code = 0;
 
   page->instance = strdup (ci->values[0].value.string);
   if (page->instance == NULL)
@@ -449,22 +454,30 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("URL", child->key) == 0)
-      status = cc_config_add_string ("URL", &page->url, child);
+      status = cf_util_get_string (child, &page->url);
     else if (strcasecmp ("User", child->key) == 0)
-      status = cc_config_add_string ("User", &page->user, child);
+      status = cf_util_get_string (child, &page->user);
     else if (strcasecmp ("Password", child->key) == 0)
-      status = cc_config_add_string ("Password", &page->pass, child);
+      status = cf_util_get_string (child, &page->pass);
+    else if (strcasecmp ("Digest", child->key) == 0)
+      status = cf_util_get_boolean (child, &page->digest);
     else if (strcasecmp ("VerifyPeer", child->key) == 0)
-      status = cc_config_set_boolean ("VerifyPeer", &page->verify_peer, child);
+      status = cf_util_get_boolean (child, &page->verify_peer);
     else if (strcasecmp ("VerifyHost", child->key) == 0)
-      status = cc_config_set_boolean ("VerifyHost", &page->verify_host, child);
+      status = cf_util_get_boolean (child, &page->verify_host);
     else if (strcasecmp ("MeasureResponseTime", child->key) == 0)
-      status = cc_config_set_boolean (child->key, &page->response_time, child);
+      status = cf_util_get_boolean (child, &page->response_time);
+    else if (strcasecmp ("MeasureResponseCode", child->key) == 0)
+      status = cf_util_get_boolean (child, &page->response_code);
     else if (strcasecmp ("CACert", child->key) == 0)
-      status = cc_config_add_string ("CACert", &page->cacert, child);
+      status = cf_util_get_string (child, &page->cacert);
     else if (strcasecmp ("Match", child->key) == 0)
       /* Be liberal with failing matches => don't set `status'. */
       cc_config_add_match (page, child);
+    else if (strcasecmp ("Header", child->key) == 0)
+      status = cc_config_append_string ("Header", &page->headers, child);
+    else if (strcasecmp ("Post", child->key) == 0)
+      status = cf_util_get_string (child, &page->post_body);
     else
     {
       WARNING ("curl plugin: Option `%s' not allowed here.", child->key);
@@ -484,11 +497,12 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
       status = -1;
     }
 
-    if (page->matches == NULL && !page->response_time)
+    if (page->matches == NULL && !page->response_time && !page->response_code)
     {
       assert (page->instance != NULL);
       WARNING ("curl plugin: No (valid) `Match' block "
-          "or MeasureResponseTime within `Page' block `%s'.", page->instance);
+          "or MeasureResponseTime or MeasureResponseCode within "
+          "`Page' block `%s'.", page->instance);
       status = -1;
     }
 
@@ -582,17 +596,36 @@ static void cc_submit (const web_page_t *wp, const web_match_t *wm, /* {{{ */
   sstrncpy (vl.plugin, "curl", sizeof (vl.plugin));
   sstrncpy (vl.plugin_instance, wp->instance, sizeof (vl.plugin_instance));
   sstrncpy (vl.type, wm->type, sizeof (vl.type));
-  sstrncpy (vl.type_instance, wm->instance, sizeof (vl.type_instance));
+  if (wm->instance != NULL)
+    sstrncpy (vl.type_instance, wm->instance, sizeof (vl.type_instance));
 
   plugin_dispatch_values (&vl);
 } /* }}} void cc_submit */
 
-static void cc_submit_response_time (const web_page_t *wp, double seconds) /* {{{ */
+static void cc_submit_response_code (const web_page_t *wp, long code) /* {{{ */
 {
   value_t values[1];
   value_list_t vl = VALUE_LIST_INIT;
 
-  values[0].gauge = seconds;
+  values[0].gauge = code;
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "curl", sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, wp->instance, sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, "response_code", sizeof (vl.type));
+
+  plugin_dispatch_values (&vl);
+} /* }}} void cc_submit_response_code */
+
+static void cc_submit_response_time (const web_page_t *wp, /* {{{ */
+    cdtime_t response_time)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = CDTIME_T_TO_DOUBLE (response_time);
 
   vl.values = values;
   vl.values_len = 1;
@@ -608,14 +641,14 @@ static int cc_read_page (web_page_t *wp) /* {{{ */
 {
   web_match_t *wm;
   int status;
-  struct timeval start, end;
+  cdtime_t start = 0;
 
   if (wp->response_time)
-    gettimeofday (&start, NULL);
+    start = cdtime ();
 
   wp->buffer_fill = 0;
   status = curl_easy_perform (wp->curl);
-  if (status != 0)
+  if (status != CURLE_OK)
   {
     ERROR ("curl plugin: curl_easy_perform failed with staus %i: %s",
         status, wp->curl_errbuf);
@@ -623,12 +656,18 @@ static int cc_read_page (web_page_t *wp) /* {{{ */
   }
 
   if (wp->response_time)
+    cc_submit_response_time (wp, cdtime() - start);
+
+  if(wp->response_code)
   {
-    double secs = 0;
-    gettimeofday (&end, NULL);
-    secs += end.tv_sec - start.tv_sec;
-    secs += (end.tv_usec - start.tv_usec) / 1000000.0;
-    cc_submit_response_time (wp, secs);
+    long response_code = 0;
+    status = curl_easy_getinfo(wp->curl, CURLINFO_RESPONSE_CODE, &response_code);
+    if(status != CURLE_OK) {
+      ERROR ("curl plugin: Fetching response code failed with staus %i: %s",
+        status, wp->curl_errbuf);
+    } else {
+      cc_submit_response_code(wp, response_code);
+    }
   }
 
   for (wm = wp->matches; wm != NULL; wm = wm->next)
index cc8b4ad..029c802 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/curl_json.c
  * Copyright (C) 2009       Doug MacEachern
- * Copyright (C) 2006-2011  Florian octo Forster
+ * Copyright (C) 2006-2013  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
 #include "utils_avltree.h"
 #include "utils_complain.h"
 
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
 #include <curl/curl.h>
+
 #include <yajl/yajl_parse.h>
 #if HAVE_YAJL_YAJL_VERSION_H
 # include <yajl/yajl_version.h>
@@ -48,10 +53,10 @@ struct cj_key_s;
 typedef struct cj_key_s cj_key_t;
 struct cj_key_s /* {{{ */
 {
+  unsigned long magic;
   char *path;
   char *type;
   char *instance;
-  unsigned long magic;
 };
 /* }}} */
 
@@ -60,13 +65,18 @@ struct cj_s /* {{{ */
   char *instance;
   char *host;
 
+  char *sock;
+
   char *url;
   char *user;
   char *pass;
   char *credentials;
+  _Bool digest;
   _Bool verify_peer;
   _Bool verify_host;
   char *cacert;
+  struct curl_slist *headers;
+  char *post_body;
 
   CURL *curl;
   char curl_errbuf[CURL_ERROR_SIZE];
@@ -80,6 +90,8 @@ struct cj_s /* {{{ */
       c_avl_tree_t *tree;
       cj_key_t *key;
     };
+    _Bool in_array;
+    int index;
     char name[DATA_MAX_NAME_LEN];
   } state[YAJL_MAX_DEPTH];
 };
@@ -92,7 +104,6 @@ typedef unsigned int yajl_len_t;
 #endif
 
 static int cj_read (user_data_t *ud);
-static int cj_curl_perform (cj_t *db, CURL *curl);
 static void cj_submit (cj_t *db, cj_key_t *key, value_t *value);
 
 static size_t cj_curl_callback (void *buf, /* {{{ */
@@ -111,16 +122,9 @@ static size_t cj_curl_callback (void *buf, /* {{{ */
   if (db == NULL)
     return (0);
 
-  status = yajl_parse(db->yajl, (unsigned char *) buf, len);
+  status = yajl_parse(db->yajl, (unsigned char *)buf, len);
   if (status == yajl_status_ok)
-  {
-#if HAVE_YAJL_V2
-    status = yajl_complete_parse(db->yajl);
-#else
-    status = yajl_parse_complete(db->yajl);
-#endif
     return (len);
-  }
 #if !HAVE_YAJL_V2
   else if (status == yajl_status_insufficient_data)
     return (len);
@@ -172,12 +176,44 @@ static int cj_get_type (cj_key_t *key)
   return ds->ds[0].type;
 }
 
+static int cj_cb_map_key (void *ctx, const unsigned char *val,
+    yajl_len_t len);
+
+static void cj_cb_inc_array_index (void *ctx, _Bool update_key)
+{
+  cj_t *db = (cj_t *)ctx;
+
+  if (!db->state[db->depth].in_array)
+    return;
+
+  db->state[db->depth].index++;
+
+  if (update_key)
+  {
+    char name[DATA_MAX_NAME_LEN];
+
+    ssnprintf (name, sizeof (name), "%d", db->state[db->depth].index - 1);
+
+    cj_cb_map_key (ctx, (unsigned char *)name, (yajl_len_t) strlen (name));
+  }
+}
+
 /* yajl callbacks */
 #define CJ_CB_ABORT    0
 #define CJ_CB_CONTINUE 1
 
-/* "number" may not be null terminated, so copy it into a buffer before
- * parsing. */
+static int cj_cb_boolean (void * ctx, int boolVal)
+{
+  cj_cb_inc_array_index (ctx, /* update_key = */ 0);
+  return (CJ_CB_CONTINUE);
+}
+
+static int cj_cb_null (void * ctx)
+{
+  cj_cb_inc_array_index (ctx, /* update_key = */ 0);
+  return (CJ_CB_CONTINUE);
+}
+
 static int cj_cb_number (void *ctx,
     const char *number, yajl_len_t number_len)
 {
@@ -189,12 +225,20 @@ static int cj_cb_number (void *ctx,
   int type;
   int status;
 
-  if ((key == NULL) || !CJ_IS_KEY (key))
-    return (CJ_CB_CONTINUE);
-
+  /* Create a null-terminated version of the string. */
   memcpy (buffer, number, number_len);
   buffer[sizeof (buffer) - 1] = 0;
 
+  if ((key == NULL) || !CJ_IS_KEY (key)) {
+    if (key != NULL)
+      NOTICE ("curl_json plugin: Found \"%s\", but the configuration expects"
+              " a map.", buffer);
+    cj_cb_inc_array_index (ctx, /* update_key = */ 0);
+    return (CJ_CB_CONTINUE);
+  } else {
+    cj_cb_inc_array_index (ctx, /* update_key = */ 1);
+  }
+
   type = cj_get_type (key);
   status = parse_value (buffer, &vt, type);
   if (status != 0)
@@ -207,8 +251,11 @@ static int cj_cb_number (void *ctx,
   return (CJ_CB_CONTINUE);
 } /* int cj_cb_number */
 
-static int cj_cb_map_key (void *ctx, const unsigned char *val,
-    yajl_len_t len)
+/* Queries the key-tree of the parent context for "in_name" and, if found,
+ * updates the "key" field of the current context. Otherwise, "key" is set to
+ * NULL. */
+static int cj_cb_map_key (void *ctx,
+    unsigned char const *in_name, yajl_len_t in_name_len)
 {
   cj_t *db = (cj_t *)ctx;
   c_avl_tree_t *tree;
@@ -217,12 +264,16 @@ static int cj_cb_map_key (void *ctx, const unsigned char *val,
 
   if (tree != NULL)
   {
-    cj_key_t *value;
+    cj_key_t *value = NULL;
     char *name;
+    size_t name_len;
 
+    /* Create a null-terminated version of the name. */
     name = db->state[db->depth].name;
-    len = COUCH_MIN(len, sizeof (db->state[db->depth].name)-1);
-    sstrncpy (name, (char *)val, len+1);
+    name_len = COUCH_MIN ((size_t) in_name_len,
+        sizeof (db->state[db->depth].name) - 1);
+    memcpy (name, in_name, name_len);
+    name[name_len] = 0;
 
     if (c_avl_get (tree, name, (void *) &value) == 0)
       db->state[db->depth].key = value;
@@ -238,24 +289,6 @@ static int cj_cb_map_key (void *ctx, const unsigned char *val,
 static int cj_cb_string (void *ctx, const unsigned char *val,
     yajl_len_t len)
 {
-  cj_t *db = (cj_t *)ctx;
-  char str[len + 1];
-
-  /* Create a null-terminated version of the string. */
-  memcpy (str, val, len);
-  str[len] = 0;
-
-  /* No configuration for this string -> simply return. */
-  if (db->state[db->depth].key == NULL)
-    return (CJ_CB_CONTINUE);
-
-  if (!CJ_IS_KEY (db->state[db->depth].key))
-  {
-    NOTICE ("curl_json plugin: Found string \"%s\", but the configuration "
-        "expects a map here.", str);
-    return (CJ_CB_CONTINUE);
-  }
-
   /* Handle the string as if it was a number. */
   return (cj_cb_number (ctx, (const char *) val, len));
 } /* int cj_cb_string */
@@ -265,7 +298,8 @@ static int cj_cb_start (void *ctx)
   cj_t *db = (cj_t *)ctx;
   if (++db->depth >= YAJL_MAX_DEPTH)
   {
-    ERROR ("curl_json plugin: %s depth exceeds max, aborting.", db->url);
+    ERROR ("curl_json plugin: %s depth exceeds max, aborting.",
+           db->url ? db->url : db->sock);
     return (CJ_CB_ABORT);
   }
   return (CJ_CB_CONTINUE);
@@ -281,6 +315,7 @@ static int cj_cb_end (void *ctx)
 
 static int cj_cb_start_map (void *ctx)
 {
+  cj_cb_inc_array_index (ctx, /* update_key = */ 1);
   return cj_cb_start (ctx);
 }
 
@@ -291,17 +326,25 @@ static int cj_cb_end_map (void *ctx)
 
 static int cj_cb_start_array (void * ctx)
 {
+  cj_t *db = (cj_t *)ctx;
+  cj_cb_inc_array_index (ctx, /* update_key = */ 1);
+  if (db->depth+1 < YAJL_MAX_DEPTH) {
+    db->state[db->depth+1].in_array = 1;
+    db->state[db->depth+1].index = 0;
+  }
   return cj_cb_start (ctx);
 }
 
 static int cj_cb_end_array (void * ctx)
 {
+  cj_t *db = (cj_t *)ctx;
+  db->state[db->depth].in_array = 0;
   return cj_cb_end (ctx);
 }
 
 static yajl_callbacks ycallbacks = {
-  NULL, /* null */
-  NULL, /* boolean */
+  cj_cb_null, /* null */
+  cj_cb_boolean, /* boolean */
   NULL, /* integer */
   NULL, /* double */
   cj_cb_number,
@@ -369,11 +412,15 @@ static void cj_free (void *arg) /* {{{ */
   sfree (db->instance);
   sfree (db->host);
 
+  sfree (db->sock);
+
   sfree (db->url);
   sfree (db->user);
   sfree (db->pass);
   sfree (db->credentials);
   sfree (db->cacert);
+  sfree (db->post_body);
+  curl_slist_free_all (db->headers);
 
   sfree (db);
 } /* }}} void cj_free */
@@ -385,6 +432,22 @@ static c_avl_tree_t *cj_avl_create(void)
   return c_avl_create ((int (*) (const void *, const void *)) strcmp);
 }
 
+static int cj_config_append_string (const char *name, struct curl_slist **dest, /* {{{ */
+    oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("curl_json plugin: `%s' needs exactly one string argument.", name);
+    return (-1);
+  }
+
+  *dest = curl_slist_append(*dest, ci->values[0].value.string);
+  if (*dest == NULL)
+    return (-1);
+
+  return (0);
+} /* }}} int cj_config_append_string */
+
 static int cj_config_add_key (cj_t *db, /* {{{ */
                                    oconfig_item_t *ci)
 {
@@ -487,6 +550,7 @@ static int cj_config_add_key (cj_t *db, /* {{{ */
         len = ptr-name;
         if (len == 0)
           break;
+        len = COUCH_MIN(len, sizeof (ent)-1);
         sstrncpy (ent, name, len+1);
 
         if (c_avl_get (tree, ent, (void *) &value) != 0)
@@ -521,11 +585,10 @@ static int cj_init_curl (cj_t *db) /* {{{ */
     return (-1);
   }
 
-  curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1);
+  curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cj_curl_callback);
   curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
-  curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
-                    PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (db->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
   curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
 
@@ -547,13 +610,24 @@ static int cj_init_curl (cj_t *db) /* {{{ */
     ssnprintf (db->credentials, credentials_size, "%s:%s",
                db->user, (db->pass == NULL) ? "" : db->pass);
     curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
+    
+    if (db->digest)
+    {
+      curl_easy_setopt (db->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
+      curl_easy_setopt (db->curl, CURLOPT_USERNAME, db->user);
+      curl_easy_setopt (db->curl, CURLOPT_PASSWORD, db->pass);
+    }
   }
 
-  curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, (int) db->verify_peer);
+  curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, (long) db->verify_peer);
   curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST,
-                    (int) (db->verify_host ? 2 : 0));
+                    db->verify_host ? 2L : 0L);
   if (db->cacert != NULL)
     curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
+  if (db->headers != NULL)
+    curl_easy_setopt (db->curl, CURLOPT_HTTPHEADER, db->headers);
+  if (db->post_body != NULL)
+    curl_easy_setopt (db->curl, CURLOPT_POSTFIELDS, db->post_body);
 
   return (0);
 } /* }}} int cj_init_curl */
@@ -581,20 +655,20 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
   memset (db, 0, sizeof (*db));
 
   if (strcasecmp ("URL", ci->key) == 0)
-  {
     status = cf_util_get_string (ci, &db->url);
-    if (status != 0)
-    {
-      sfree (db);
-      return (status);
-    }
-  }
+  else if (strcasecmp ("Sock", ci->key) == 0)
+    status = cf_util_get_string (ci, &db->sock);
   else
   {
     ERROR ("curl_json plugin: cj_config: "
            "Invalid key: %s", ci->key);
     return (-1);
   }
+  if (status != 0)
+  {
+    sfree (db);
+    return (status);
+  }
 
   /* Fill the `cj_t' structure.. */
   for (i = 0; i < ci->children_num; i++)
@@ -605,16 +679,22 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string (child, &db->instance);
     else if (strcasecmp ("Host", child->key) == 0)
       status = cf_util_get_string (child, &db->host);
-    else if (strcasecmp ("User", child->key) == 0)
+    else if (db->url && strcasecmp ("User", child->key) == 0)
       status = cf_util_get_string (child, &db->user);
-    else if (strcasecmp ("Password", child->key) == 0)
+    else if (db->url && strcasecmp ("Password", child->key) == 0)
       status = cf_util_get_string (child, &db->pass);
-    else if (strcasecmp ("VerifyPeer", child->key) == 0)
+    else if (strcasecmp ("Digest", child->key) == 0)
+      status = cf_util_get_boolean (child, &db->digest);
+    else if (db->url && strcasecmp ("VerifyPeer", child->key) == 0)
       status = cf_util_get_boolean (child, &db->verify_peer);
-    else if (strcasecmp ("VerifyHost", child->key) == 0)
+    else if (db->url && strcasecmp ("VerifyHost", child->key) == 0)
       status = cf_util_get_boolean (child, &db->verify_host);
-    else if (strcasecmp ("CACert", child->key) == 0)
+    else if (db->url && strcasecmp ("CACert", child->key) == 0)
       status = cf_util_get_string (child, &db->cacert);
+    else if (db->url && strcasecmp ("Header", child->key) == 0)
+      status = cj_config_append_string ("Header", &db->headers, child);
+    else if (db->url && strcasecmp ("Post", child->key) == 0)
+      status = cf_util_get_string (child, &db->post_body);
     else if (strcasecmp ("Key", child->key) == 0)
       status = cj_config_add_key (db, child);
     else
@@ -631,11 +711,11 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
   {
     if (db->tree == NULL)
     {
-      WARNING ("curl_json plugin: No (valid) `Key' block "
-               "within `URL' block `%s'.", db->url);
+      WARNING ("curl_json plugin: No (valid) `Key' block within `%s' \"`%s'\".",
+               db->url ? "URL" : "Sock", db->url ? db->url : db->sock);
       status = -1;
     }
-    if (status == 0)
+    if (status == 0 && db->url)
       status = cj_init_curl (db);
   }
 
@@ -656,7 +736,7 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
     ud.free_func = cj_free;
 
     ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
-               db->instance, db->url);
+               db->instance, db->url ? db->url : db->sock);
 
     plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
                                   /* interval = */ NULL, &ud);
@@ -685,7 +765,8 @@ static int cj_config (oconfig_item_t *ci) /* {{{ */
   {
     oconfig_item_t *child = ci->children + i;
 
-    if (strcasecmp ("URL", child->key) == 0)
+    if (strcasecmp ("Sock", child->key) == 0
+        || strcasecmp ("URL", child->key) == 0)
     {
       status = cj_config_add_url (child);
       if (status == 0)
@@ -728,11 +809,10 @@ static void cj_submit (cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
 
   if (key->instance == NULL)
   {
-    if ((db->depth == 0) || (strcmp ("", db->state[db->depth-1].name) == 0))
-      sstrncpy (vl.type_instance, db->state[db->depth].name, sizeof (vl.type_instance));
-    else
-      ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
-          db->state[db->depth-1].name, db->state[db->depth].name);
+    int i, len = 0;
+    for (i = 0; i < db->depth; i++)
+      len += ssnprintf(vl.type_instance+len, sizeof(vl.type_instance)-len,
+                       i ? "-%s" : "%s", db->state[i+1].name);
   }
   else
     sstrncpy (vl.type_instance, key->instance, sizeof (vl.type_instance));
@@ -745,11 +825,75 @@ static void cj_submit (cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
   plugin_dispatch_values (&vl);
 } /* }}} int cj_submit */
 
-static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */
+static int cj_sock_perform (cj_t *db) /* {{{ */
+{
+  char errbuf[1024];
+  struct sockaddr_un sa_unix = {};
+  sa_unix.sun_family = AF_UNIX;
+  sstrncpy (sa_unix.sun_path, db->sock, sizeof (sa_unix.sun_path));
+
+  int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd < 0)
+    return (-1);
+  if (connect (fd, (struct sockaddr *)&sa_unix, sizeof(sa_unix)) < 0)
+  {
+    ERROR ("curl_json plugin: connect(%s) failed: %s",
+           (db->sock != NULL) ? db->sock : "<null>",
+           sstrerror(errno, errbuf, sizeof (errbuf)));
+    close (fd);
+    return (-1);
+  }
+
+  ssize_t red;
+  do {
+    unsigned char buffer[4096];
+    red = read (fd, buffer, sizeof(buffer));
+    if (red < 0) {
+        ERROR ("curl_json plugin: read(%s) failed: %s",
+               (db->sock != NULL) ? db->sock : "<null>",
+               sstrerror(errno, errbuf, sizeof (errbuf)));
+        close (fd);
+        return (-1);
+    }
+    if (!cj_curl_callback (buffer, red, 1, db))
+        break;
+  } while (red > 0);
+  close (fd);
+  return (0);
+} /* }}} int cj_sock_perform */
+
+
+static int cj_curl_perform(cj_t *db) /* {{{ */
 {
   int status;
   long rc;
   char *url;
+  url = db->url;
+
+  status = curl_easy_perform (db->curl);
+  if (status != CURLE_OK)
+  {
+    ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
+           status, db->curl_errbuf, url);
+    return (-1);
+  }
+
+  curl_easy_getinfo(db->curl, CURLINFO_EFFECTIVE_URL, &url);
+  curl_easy_getinfo(db->curl, CURLINFO_RESPONSE_CODE, &rc);
+
+  /* The response code is zero if a non-HTTP transport was used. */
+  if ((rc != 0) && (rc != 200))
+  {
+    ERROR ("curl_json plugin: curl_easy_perform failed with "
+        "response code %ld (%s)", rc, url);
+    return (-1);
+  }
+  return (0);
+} /* }}} int cj_curl_perform */
+
+static int cj_perform (cj_t *db) /* {{{ */
+{
+  int status;
   yajl_handle yprev = db->yajl;
 
   db->yajl = yajl_alloc (&ycallbacks,
@@ -766,26 +910,12 @@ static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */
     return (-1);
   }
 
-  url = NULL;
-  curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
-
-  status = curl_easy_perform (curl);
-  if (status != 0)
-  {
-    ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
-           status, db->curl_errbuf, (url != NULL) ? url : "<null>");
-    yajl_free (db->yajl);
-    db->yajl = yprev;
-    return (-1);
-  }
-
-  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
-
-  /* The response code is zero if a non-HTTP transport was used. */
-  if ((rc != 0) && (rc != 200))
+  if (db->url)
+    status = cj_curl_perform (db);
+  else
+    status = cj_sock_perform (db);
+  if (status < 0)
   {
-    ERROR ("curl_json plugin: curl_easy_perform failed with "
-        "response code %ld (%s)", rc, url);
     yajl_free (db->yajl);
     db->yajl = yprev;
     return (-1);
@@ -813,7 +943,7 @@ static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */
   yajl_free (db->yajl);
   db->yajl = yprev;
   return (0);
-} /* }}} int cj_curl_perform */
+} /* }}} int cj_perform */
 
 static int cj_read (user_data_t *ud) /* {{{ */
 {
@@ -832,7 +962,7 @@ static int cj_read (user_data_t *ud) /* {{{ */
   db->state[db->depth].tree = db->tree;
   db->key = NULL;
 
-  return cj_curl_perform (db, db->curl);
+  return cj_perform (db);
 } /* }}} int cj_read */
 
 void module_register (void)
index 46e0d49..6d36d29 100644 (file)
@@ -28,6 +28,7 @@
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 #include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
 
 #include <curl/curl.h>
 
@@ -58,6 +59,14 @@ struct cx_xpath_s /* {{{ */
 typedef struct cx_xpath_s cx_xpath_t;
 /* }}} */
 
+struct cx_namespace_s /* {{{ */
+{
+  char *prefix;
+  char *url;
+};
+typedef struct cx_namespace_s cx_namespace_t;
+/* }}} */
+
 struct cx_s /* {{{ */
 {
   char *instance;
@@ -67,9 +76,15 @@ struct cx_s /* {{{ */
   char *user;
   char *pass;
   char *credentials;
+  _Bool digest;
   _Bool verify_peer;
   _Bool verify_host;
   char *cacert;
+  char *post_body;
+  struct curl_slist *headers;
+
+  cx_namespace_t *namespaces;
+  size_t namespaces_num;
 
   CURL *curl;
   char curl_errbuf[CURL_ERROR_SIZE];
@@ -160,6 +175,7 @@ static void cx_list_free (llist_t *list) /* {{{ */
 static void cx_free (void *arg) /* {{{ */
 {
   cx_t *db;
+  size_t i;
 
   DEBUG ("curl_xml plugin: cx_free (arg = %p);", arg);
 
@@ -184,10 +200,35 @@ static void cx_free (void *arg) /* {{{ */
   sfree (db->pass);
   sfree (db->credentials);
   sfree (db->cacert);
+  sfree (db->post_body);
+  curl_slist_free_all (db->headers);
+
+  for (i = 0; i < db->namespaces_num; i++)
+  {
+    sfree (db->namespaces[i].prefix);
+    sfree (db->namespaces[i].url);
+  }
+  sfree (db->namespaces);
 
   sfree (db);
 } /* }}} void cx_free */
 
+static int cx_config_append_string (const char *name, struct curl_slist **dest, /* {{{ */
+    oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("curl_xml plugin: `%s' needs exactly one string argument.", name);
+    return (-1);
+  }
+
+  *dest = curl_slist_append(*dest, ci->values[0].value.string);
+  if (*dest == NULL)
+    return (-1);
+
+  return (0);
+} /* }}} int cx_config_append_string */
+
 static int cx_check_type (const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */
 {
   if (!ds)
@@ -225,7 +266,8 @@ static xmlXPathObjectPtr cx_evaluate_xpath (xmlXPathContextPtr xpath_ctx, /* {{{
 
 static int cx_if_not_text_node (xmlNodePtr node) /* {{{ */
 {
-  if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE)
+  if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE ||
+      node->type == XML_ELEMENT_NODE)
     return (0);
 
   WARNING ("curl_xml plugin: "
@@ -342,7 +384,7 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
   memset (vl->type_instance, 0, sizeof (vl->type_instance));
 
   /* If the base xpath returns more than one block, the result is assumed to be
-   * a table. The `Instnce' option is not optional in this case. Check for the
+   * a table. The `Instance' option is not optional in this case. Check for the
    * condition and inform the user. */
   if (is_table && (vl->type_instance == NULL))
   {
@@ -365,7 +407,7 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
     instance_node = instance_node_obj->nodesetval;
     tmp_size = (instance_node) ? instance_node->nodeNr : 0;
 
-    if ( (tmp_size == 0) && (is_table) )
+    if (tmp_size <= 0)
     {
       WARNING ("curl_xml plugin: "
           "relative xpath expression for 'InstanceFrom' \"%s\" doesn't match "
@@ -520,6 +562,7 @@ static int cx_parse_stats_xml(xmlChar* xml, cx_t *db) /* {{{ */
   int status;
   xmlDocPtr doc;
   xmlXPathContextPtr xpath_ctx;
+  size_t i;
 
   /* Load the XML */
   doc = xmlParseDoc(xml);
@@ -537,6 +580,22 @@ static int cx_parse_stats_xml(xmlChar* xml, cx_t *db) /* {{{ */
     return (-1);
   }
 
+  for (i = 0; i < db->namespaces_num; i++)
+  {
+    cx_namespace_t const *ns = db->namespaces + i;
+    status = xmlXPathRegisterNs (xpath_ctx,
+        BAD_CAST ns->prefix, BAD_CAST ns->url);
+    if (status != 0)
+    {
+      ERROR ("curl_xml plugin: "
+          "unable to register NS with prefix=\"%s\" and href=\"%s\"\n",
+          ns->prefix, ns->url);
+      xmlXPathFreeContext(xpath_ctx);
+      xmlFreeDoc (doc);
+      return (status);
+    }
+  }
+
   status = cx_handle_parsed_xml (doc, xpath_ctx, db);
   /* Cleanup */
   xmlXPathFreeContext(xpath_ctx);
@@ -550,9 +609,16 @@ static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */
   long rc;
   char *ptr;
   char *url;
+  url = db->url;
 
   db->buffer_fill = 0; 
   status = curl_easy_perform (curl);
+  if (status != CURLE_OK)
+  {
+    ERROR ("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
+           status, db->curl_errbuf, url);
+    return (-1);
+  }
 
   curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
   curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
@@ -565,13 +631,6 @@ static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */
     return (-1);
   }
 
-  if (status != 0)
-  {
-    ERROR ("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
-           status, db->curl_errbuf, url);
-    return (-1);
-  }
-
   ptr = db->buffer;
 
   status = cx_parse_stats_xml(BAD_CAST ptr, db);
@@ -727,6 +786,46 @@ static int cx_config_add_xpath (cx_t *db, /* {{{ */
   return (status);
 } /* }}} int cx_config_add_xpath */
 
+static int cx_config_add_namespace (cx_t *db, /* {{{ */
+    oconfig_item_t *ci)
+{
+  cx_namespace_t *ns;
+
+  if ((ci->values_num != 2)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING)
+      || (ci->values[1].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("curl_xml plugin: The `Namespace' option "
+             "needs exactly two string arguments.");
+    return (EINVAL);
+  }
+
+  ns = realloc (db->namespaces, sizeof (*db->namespaces)
+      * (db->namespaces_num + 1));
+  if (ns == NULL)
+  {
+    ERROR ("curl_xml plugin: realloc failed.");
+    return (ENOMEM);
+  }
+  db->namespaces = ns;
+  ns = db->namespaces + db->namespaces_num;
+  memset (ns, 0, sizeof (*ns));
+
+  ns->prefix = strdup (ci->values[0].value.string);
+  ns->url = strdup (ci->values[1].value.string);
+
+  if ((ns->prefix == NULL) || (ns->url == NULL))
+  {
+    sfree (ns->prefix);
+    sfree (ns->url);
+    ERROR ("curl_xml plugin: strdup failed.");
+    return (ENOMEM);
+  }
+
+  db->namespaces_num++;
+  return (0);
+} /* }}} int cx_config_add_namespace */
+
 /* Initialize db->curl */
 static int cx_init_curl (cx_t *db) /* {{{ */
 {
@@ -737,11 +836,10 @@ static int cx_init_curl (cx_t *db) /* {{{ */
     return (-1);
   }
 
-  curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1);
+  curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cx_curl_callback);
   curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
-  curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
-                    PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (db->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
   curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
 
@@ -763,6 +861,13 @@ static int cx_init_curl (cx_t *db) /* {{{ */
     ssnprintf (db->credentials, credentials_size, "%s:%s",
                db->user, (db->pass == NULL) ? "" : db->pass);
     curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
+    
+    if (db->digest)
+    {
+      curl_easy_setopt (db->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
+      curl_easy_setopt (db->curl, CURLOPT_USERNAME, db->user);
+      curl_easy_setopt (db->curl, CURLOPT_PASSWORD, db->pass);
+    }
   }
 
   curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer ? 1L : 0L);
@@ -770,6 +875,10 @@ static int cx_init_curl (cx_t *db) /* {{{ */
                     db->verify_host ? 2L : 0L);
   if (db->cacert != NULL)
     curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
+  if (db->headers != NULL)
+    curl_easy_setopt (db->curl, CURLOPT_HTTPHEADER, db->headers);
+  if (db->post_body != NULL)
+    curl_easy_setopt (db->curl, CURLOPT_POSTFIELDS, db->post_body);
 
   return (0);
 } /* }}} int cx_init_curl */
@@ -825,6 +934,8 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string (child, &db->user);
     else if (strcasecmp ("Password", child->key) == 0)
       status = cf_util_get_string (child, &db->pass);
+    else if (strcasecmp ("Digest", child->key) == 0)
+      status = cf_util_get_boolean (child, &db->digest);
     else if (strcasecmp ("VerifyPeer", child->key) == 0)
       status = cf_util_get_boolean (child, &db->verify_peer);
     else if (strcasecmp ("VerifyHost", child->key) == 0)
@@ -833,6 +944,12 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string (child, &db->cacert);
     else if (strcasecmp ("xpath", child->key) == 0)
       status = cx_config_add_xpath (db, child);
+    else if (strcasecmp ("Header", child->key) == 0)
+      status = cx_config_append_string ("Header", &db->headers, child);
+    else if (strcasecmp ("Post", child->key) == 0)
+      status = cf_util_get_string (child, &db->post_body);
+    else if (strcasecmp ("Namespace", child->key) == 0)
+      status = cx_config_add_namespace (db, child);
     else
     {
       WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
@@ -859,7 +976,7 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
   if (status == 0)
   {
     user_data_t ud;
-    char cb_name[DATA_MAX_NAME_LEN];
+    char *cb_name;
 
     if (db->instance == NULL)
       db->instance = strdup("default");
@@ -871,11 +988,10 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
     ud.data = (void *) db;
     ud.free_func = cx_free;
 
-    ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s",
-               db->instance, db->url);
-
-    plugin_register_complex_read (/* group = */ NULL, cb_name, cx_read,
+    cb_name = ssnprintf_alloc ("curl_xml-%s-%s", db->instance, db->url);
+    plugin_register_complex_read (/* group = */ "curl_xml", cb_name, cx_read,
                                   /* interval = */ NULL, &ud);
+    sfree (cb_name);
   }
   else
   {
index caa41ef..779c723 100644 (file)
--- a/src/dbi.c
+++ b/src/dbi.c
@@ -1,6 +1,6 @@
 /**
  * collectd - src/dbi.c
- * Copyright (C) 2008,2009  Florian octo Forster
+ * Copyright (C) 2008-2013  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -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"
 struct cdbi_driver_option_s /* {{{ */
 {
   char *key;
-  char *value;
+  union
+  {
+    char *string;
+    int numeric;
+  } value;
+  _Bool is_numeric;
 };
 typedef struct cdbi_driver_option_s cdbi_driver_option_t; /* }}} */
 
@@ -43,6 +48,7 @@ struct cdbi_database_s /* {{{ */
   char *select_db;
 
   char *driver;
+  char *host;
   cdbi_driver_option_t *driver_options;
   size_t driver_options_num;
 
@@ -62,6 +68,8 @@ static size_t            queries_num   = 0;
 static cdbi_database_t **databases     = NULL;
 static size_t            databases_num = 0;
 
+static int cdbi_read_database (user_data_t *ud);
+
 /*
  * Functions
  */
@@ -159,7 +167,8 @@ static void cdbi_database_free (cdbi_database_t *db) /* {{{ */
   for (i = 0; i < db->driver_options_num; i++)
   {
     sfree (db->driver_options[i].key);
-    sfree (db->driver_options[i].value);
+    if (!db->driver_options[i].is_numeric)
+      sfree (db->driver_options[i].value.string);
   }
   sfree (db->driver_options);
 
@@ -193,33 +202,6 @@ static void cdbi_database_free (cdbi_database_t *db) /* {{{ */
  * </Plugin>
  */
 
-static int cdbi_config_set_string (char **ret_string, /* {{{ */
-    oconfig_item_t *ci)
-{
-  char *string;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("dbi plugin: The `%s' config option "
-        "needs exactly one string argument.", ci->key);
-    return (-1);
-  }
-
-  string = strdup (ci->values[0].value.string);
-  if (string == NULL)
-  {
-    ERROR ("dbi plugin: strdup failed.");
-    return (-1);
-  }
-
-  if (*ret_string != NULL)
-    free (*ret_string);
-  *ret_string = string;
-
-  return (0);
-} /* }}} int cdbi_config_set_string */
-
 static int cdbi_config_add_database_driver_option (cdbi_database_t *db, /* {{{ */
     oconfig_item_t *ci)
 {
@@ -227,10 +209,11 @@ static int cdbi_config_add_database_driver_option (cdbi_database_t *db, /* {{{ *
 
   if ((ci->values_num != 2)
       || (ci->values[0].type != OCONFIG_TYPE_STRING)
-      || (ci->values[1].type != OCONFIG_TYPE_STRING))
+      || ((ci->values[1].type != OCONFIG_TYPE_STRING)
+        && (ci->values[1].type != OCONFIG_TYPE_NUMBER)))
   {
     WARNING ("dbi plugin: The `DriverOption' config option "
-        "needs exactly two string arguments.");
+        "needs exactly two arguments.");
     return (-1);
   }
 
@@ -244,6 +227,7 @@ static int cdbi_config_add_database_driver_option (cdbi_database_t *db, /* {{{ *
 
   db->driver_options = option;
   option = db->driver_options + db->driver_options_num;
+  memset (option, 0, sizeof (*option));
 
   option->key = strdup (ci->values[0].value.string);
   if (option->key == NULL)
@@ -252,12 +236,21 @@ static int cdbi_config_add_database_driver_option (cdbi_database_t *db, /* {{{ *
     return (-1);
   }
 
-  option->value = strdup (ci->values[1].value.string);
-  if (option->value == NULL)
+  if (ci->values[1].type == OCONFIG_TYPE_STRING)
   {
-    ERROR ("dbi plugin: strdup failed.");
-    sfree (option->key);
-    return (-1);
+    option->value.string = strdup (ci->values[1].value.string);
+    if (option->value.string == NULL)
+    {
+      ERROR ("dbi plugin: strdup failed.");
+      sfree (option->key);
+      return (-1);
+    }
+  }
+  else
+  {
+    assert (ci->values[1].type == OCONFIG_TYPE_NUMBER);
+    option->value.numeric = (int) (ci->values[1].value.number + .5);
+    option->is_numeric = 1;
   }
 
   db->driver_options_num++;
@@ -286,7 +279,7 @@ static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
   }
   memset (db, 0, sizeof (*db));
 
-  status = cdbi_config_set_string (&db->name, ci);
+  status = cf_util_get_string (ci, &db->name);
   if (status != 0)
   {
     sfree (db);
@@ -299,14 +292,16 @@ static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("Driver", child->key) == 0)
-      status = cdbi_config_set_string (&db->driver, child);
+      status = cf_util_get_string (child, &db->driver);
     else if (strcasecmp ("DriverOption", child->key) == 0)
       status = cdbi_config_add_database_driver_option (db, child);
     else if (strcasecmp ("SelectDB", child->key) == 0)
-      status = cdbi_config_set_string (&db->select_db, child);
+      status = cf_util_get_string (child, &db->select_db);
     else if (strcasecmp ("Query", child->key) == 0)
       status = udb_query_pick_from_list (child, queries, queries_num,
           &db->queries, &db->queries_num);
+    else if (strcasecmp ("Host", child->key) == 0)
+      status = cf_util_get_string (child, &db->host);
     else
     {
       WARNING ("dbi plugin: Option `%s' not allowed here.", child->key);
@@ -376,9 +371,24 @@ static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
     }
     else
     {
+      user_data_t ud;
+      char *name = NULL;
+
       databases = temp;
       databases[databases_num] = db;
       databases_num++;
+
+      memset (&ud, 0, sizeof (ud));
+      ud.data = (void *) db;
+      ud.free_func = NULL;
+      name = ssnprintf_alloc("dbi:%s", db->name);
+
+      plugin_register_complex_read (/* group = */ NULL,
+          /* name = */ name ? name : db->name,
+          /* callback = */ cdbi_read_database,
+          /* interval = */ NULL,
+          /* user_data = */ &ud);
+      free (name);
     }
   }
 
@@ -405,7 +415,7 @@ static int cdbi_config (oconfig_item_t *ci) /* {{{ */
       cdbi_config_add_database (child);
     else
     {
-      WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
+      WARNING ("dbi plugin: Ignoring unknown config option `%s'.", child->key);
     }
   } /* for (ci->children) */
 
@@ -564,7 +574,7 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
     sstrncpy (column_names[i], column_name, DATA_MAX_NAME_LEN);
   } /* }}} for (i = 0; i < column_num; i++) */
 
-  udb_query_prepare_result (q, prep_area, hostname_g,
+  udb_query_prepare_result (q, prep_area, (db->host ? db->host : hostname_g),
       /* plugin = */ "dbi", db->name,
       column_names, column_num, /* interval = */ 0);
 
@@ -688,24 +698,38 @@ static int cdbi_connect_database (cdbi_database_t *db) /* {{{ */
    * trouble finding out how to configure the plugin correctly.. */
   for (i = 0; i < db->driver_options_num; i++)
   {
-    DEBUG ("dbi plugin: cdbi_connect_database (%s): "
-        "key = %s; value = %s;",
-        db->name,
-        db->driver_options[i].key,
-        db->driver_options[i].value);
+    if (db->driver_options[i].is_numeric)
+    {
+      status = dbi_conn_set_option_numeric (connection,
+          db->driver_options[i].key, db->driver_options[i].value.numeric);
+      if (status != 0)
+      {
+        char errbuf[1024];
+        ERROR ("dbi plugin: cdbi_connect_database (%s): "
+            "dbi_conn_set_option_numeric (\"%s\", %i) failed: %s.",
+            db->name,
+            db->driver_options[i].key, db->driver_options[i].value.numeric,
+            cdbi_strerror (connection, errbuf, sizeof (errbuf)));
+      }
+    }
+    else
+    {
+      status = dbi_conn_set_option (connection,
+          db->driver_options[i].key, db->driver_options[i].value.string);
+      if (status != 0)
+      {
+        char errbuf[1024];
+        ERROR ("dbi plugin: cdbi_connect_database (%s): "
+            "dbi_conn_set_option (\"%s\", \"%s\") failed: %s.",
+            db->name,
+            db->driver_options[i].key, db->driver_options[i].value.string,
+            cdbi_strerror (connection, errbuf, sizeof (errbuf)));
+      }
+    }
 
-    status = dbi_conn_set_option (connection,
-        db->driver_options[i].key, db->driver_options[i].value);
     if (status != 0)
     {
-      char errbuf[1024];
-      const char *opt;
-
-      ERROR ("dbi plugin: cdbi_connect_database (%s): "
-          "dbi_conn_set_option (%s, %s) failed: %s.",
-          db->name,
-          db->driver_options[i].key, db->driver_options[i].value,
-          cdbi_strerror (connection, errbuf, sizeof (errbuf)));
+      char const *opt;
 
       INFO ("dbi plugin: This is a list of all options understood "
           "by the `%s' driver:", db->driver);
@@ -751,8 +775,9 @@ static int cdbi_connect_database (cdbi_database_t *db) /* {{{ */
   return (0);
 } /* }}} int cdbi_connect_database */
 
-static int cdbi_read_database (cdbi_database_t *db) /* {{{ */
+static int cdbi_read_database (user_data_t *ud) /* {{{ */
 {
+  cdbi_database_t *db = (cdbi_database_t *) ud->data;
   size_t i;
   int success;
   int status;
@@ -791,29 +816,6 @@ static int cdbi_read_database (cdbi_database_t *db) /* {{{ */
   return (0);
 } /* }}} int cdbi_read_database */
 
-static int cdbi_read (void) /* {{{ */
-{
-  size_t i;
-  int success = 0;
-  int status;
-
-  for (i = 0; i < databases_num; i++)
-  {
-    status = cdbi_read_database (databases[i]);
-    if (status == 0)
-      success++;
-  }
-
-  if (success == 0)
-  {
-    ERROR ("dbi plugin: No database could be read. Will return an error so "
-        "the plugin will be delayed.");
-    return (-1);
-  }
-
-  return (0);
-} /* }}} int cdbi_read */
-
 static int cdbi_shutdown (void) /* {{{ */
 {
   size_t i;
@@ -841,7 +843,6 @@ void module_register (void) /* {{{ */
 {
   plugin_register_complex_config ("dbi", cdbi_config);
   plugin_register_init ("dbi", cdbi_init);
-  plugin_register_read ("dbi", cdbi_read);
   plugin_register_shutdown ("dbi", cdbi_shutdown);
 } /* }}} void module_register */
 
index ded374b..3d5a402 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -54,7 +54,9 @@ static const char *config_keys[] =
        "IgnoreSelected",
        "ReportByDevice",
        "ReportReserved",
-       "ReportInodes"
+       "ReportInodes",
+       "ValuesAbsolute",
+       "ValuesPercentage"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -64,6 +66,8 @@ static ignorelist_t *il_fstype = NULL;
 
 static _Bool by_device = 0;
 static _Bool report_inodes = 0;
+static _Bool values_absolute = 1;
+static _Bool values_percentage = 0;
 
 static int df_init (void)
 {
@@ -131,7 +135,24 @@ static int df_config (const char *key, const char *value)
 
                return (0);
        }
+       else if (strcasecmp (key, "ValuesAbsolute") == 0)
+       {
+               if (IS_TRUE (value))
+                       values_absolute = 1;
+               else
+                       values_absolute = 0;
+
+               return (0);
+       }
+       else if (strcasecmp (key, "ValuesPercentage") == 0)
+       {
+               if (IS_TRUE (value))
+                       values_percentage = 1;
+               else
+                       values_percentage = 0;
 
+               return (0);
+       }
 
        return (-1);
 }
@@ -210,7 +231,7 @@ static int df_read (void)
                if (!statbuf.f_blocks)
                        continue;
 
-               if (by_device) 
+               if (by_device)
                {
                        /* eg, /dev/hda1  -- strip off the "/dev/" */
                        if (strncmp (mnt_ptr->spec_device, "/dev/", strlen ("/dev/")) == 0)
@@ -218,13 +239,13 @@ static int df_read (void)
                        else
                                sstrncpy (disk_name, mnt_ptr->spec_device, sizeof (disk_name));
 
-                       if (strlen(disk_name) < 1) 
+                       if (strlen(disk_name) < 1)
                        {
-                               DEBUG("df: no device name name for mountpoint %s, skipping", mnt_ptr->dir);
+                               DEBUG("df: no device name for mountpoint %s, skipping", mnt_ptr->dir);
                                continue;
                        }
-               } 
-               else 
+               }
+               else
                {
                        if (strcmp (mnt_ptr->dir, "/") == 0)
                        {
@@ -274,12 +295,29 @@ static int df_read (void)
                blk_reserved = (uint64_t) (statbuf.f_bfree - statbuf.f_bavail);
                blk_used     = (uint64_t) (statbuf.f_blocks - statbuf.f_bfree);
 
-               df_submit_one (disk_name, "df_complex", "free",
+               if (values_absolute)
+               {
+                       df_submit_one (disk_name, "df_complex", "free",
                                (gauge_t) (blk_free * blocksize));
-               df_submit_one (disk_name, "df_complex", "reserved",
+                       df_submit_one (disk_name, "df_complex", "reserved",
                                (gauge_t) (blk_reserved * blocksize));
-               df_submit_one (disk_name, "df_complex", "used",
+                       df_submit_one (disk_name, "df_complex", "used",
                                (gauge_t) (blk_used * blocksize));
+               }
+
+               if (values_percentage)
+               {
+                       if (statbuf.f_blocks > 0)
+                               {
+                               df_submit_one (disk_name, "percent_bytes", "free",
+                                       (gauge_t) ((float_t)(blk_free) / statbuf.f_blocks * 100));
+                               df_submit_one (disk_name, "percent_bytes", "reserved",
+                                       (gauge_t) ((float_t)(blk_reserved) / statbuf.f_blocks * 100));
+                               df_submit_one (disk_name, "percent_bytes", "used",
+                                       (gauge_t) ((float_t)(blk_used) / statbuf.f_blocks * 100));
+                               }
+                       else return (-1);
+               }
 
                /* inode handling */
                if (report_inodes)
@@ -297,13 +335,29 @@ static int df_read (void)
                        inode_free = (uint64_t) statbuf.f_favail;
                        inode_reserved = (uint64_t) (statbuf.f_ffree - statbuf.f_favail);
                        inode_used = (uint64_t) (statbuf.f_files - statbuf.f_ffree);
-                       
-                       df_submit_one (disk_name, "df_inodes", "free",
-                                       (gauge_t) inode_free);
-                       df_submit_one (disk_name, "df_inodes", "reserved",
-                                       (gauge_t) inode_reserved);
-                       df_submit_one (disk_name, "df_inodes", "used",
-                                       (gauge_t) inode_used);
+
+                       if (values_percentage)
+                       {
+                               if (statbuf.f_files > 0)
+                               {
+                                       df_submit_one (disk_name, "percent_inodes", "free",
+                                               (gauge_t) ((float_t)(inode_free) / statbuf.f_files * 100));
+                                       df_submit_one (disk_name, "percent_inodes", "reserved",
+                                               (gauge_t) ((float_t)(inode_reserved) / statbuf.f_files * 100));
+                                       df_submit_one (disk_name, "percent_inodes", "used",
+                                               (gauge_t) ((float_t)(inode_used) / statbuf.f_files * 100));
+                               }
+                               else return (-1);
+                       }
+                       if (values_absolute)
+                       {
+                               df_submit_one (disk_name, "df_inodes", "free",
+                                               (gauge_t) inode_free);
+                               df_submit_one (disk_name, "df_inodes", "reserved",
+                                               (gauge_t) inode_reserved);
+                               df_submit_one (disk_name, "df_inodes", "used",
+                                               (gauge_t) inode_used);
+                       }
                }
        }
 
index 3728d55..cdf2816 100644 (file)
@@ -109,7 +109,7 @@ static diskstats_t *disklist;
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
-#define MAX_NUMDISK 256
+#define MAX_NUMDISK 1024
 extern kstat_ctl_t *kc;
 static kstat_t *ksp[MAX_NUMDISK];
 static int numdisk = 0;
@@ -128,11 +128,19 @@ static int pnumdisk;
 # error "No applicable input method."
 #endif
 
+#if HAVE_LIBUDEV
+#include <libudev.h>
+
+static char *conf_udev_name_attr = NULL;
+static struct udev *handle_udev;
+#endif
+
 static const char *config_keys[] =
 {
        "Disk",
        "UseBSDName",
-       "IgnoreSelected"
+       "IgnoreSelected",
+       "UdevNameAttr"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -165,6 +173,21 @@ static int disk_config (const char *key, const char *value)
         "on Mach / Mac OS X and will be ignored.");
 #endif
   }
+  else if (strcasecmp ("UdevNameAttr", key) == 0)
+  {
+#if HAVE_LIBUDEV
+    if (conf_udev_name_attr != NULL)
+    {
+      free (conf_udev_name_attr);
+      conf_udev_name_attr = NULL;
+    }
+    if ((conf_udev_name_attr = strdup (value)) == NULL)
+      return (1);
+#else
+    WARNING ("disk plugin: The \"UdevNameAttr\" option is only supported "
+        "if collectd is built with libudev support");
+#endif
+  }
   else
   {
     return (-1);
@@ -259,6 +282,34 @@ static counter_t disk_calc_time_incr (counter_t delta_time, counter_t delta_ops)
 }
 #endif
 
+#if HAVE_LIBUDEV
+/**
+ * Attempt to provide an rename disk instance from an assigned udev attribute.
+ *
+ * On success, it returns a strduped char* to the desired attribute value.
+ * Otherwise it returns NULL.
+ */
+
+static char *disk_udev_attr_name (struct udev *udev, char *disk_name, const char *attr)
+{
+       struct udev_device *dev;
+       const char *prop;
+       char *output = NULL;
+
+       dev = udev_device_new_from_subsystem_sysname (udev, "block", disk_name);
+       if (dev != NULL)
+       {
+               prop = udev_device_get_property_value (dev, attr);
+               if (prop) {
+                       output = strdup (prop);
+                       DEBUG ("disk plugin: renaming %s => %s", disk_name, output);
+               }
+               udev_device_unref (dev);
+       }
+       return output;
+}
+#endif
+
 #if HAVE_IOKIT_IOKITLIB_H
 static signed long long dict_get_value (CFDictionaryRef dict, const char *key)
 {
@@ -505,9 +556,15 @@ static int disk_read (void)
                fieldshift = 1;
        }
 
+#if HAVE_LIBUDEV
+       handle_udev = udev_new();
+#endif
+
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
                char *disk_name;
+               char *output_name;
+               char *alt_name;
 
                numfields = strsplit (buffer, fields, 32);
 
@@ -659,25 +716,43 @@ static int disk_read (void)
                        continue;
                }
 
+               output_name = disk_name;
+
+#if HAVE_LIBUDEV
+               alt_name = disk_udev_attr_name (handle_udev, disk_name,
+                               conf_udev_name_attr);
+#else
+               alt_name = NULL;
+#endif
+               if (alt_name != NULL)
+                       output_name = alt_name;
+
                if ((ds->read_bytes != 0) || (ds->write_bytes != 0))
-                       disk_submit (disk_name, "disk_octets",
+                       disk_submit (output_name, "disk_octets",
                                        ds->read_bytes, ds->write_bytes);
 
                if ((ds->read_ops != 0) || (ds->write_ops != 0))
-                       disk_submit (disk_name, "disk_ops",
+                       disk_submit (output_name, "disk_ops",
                                        read_ops, write_ops);
 
                if ((ds->avg_read_time != 0) || (ds->avg_write_time != 0))
-                       disk_submit (disk_name, "disk_time",
+                       disk_submit (output_name, "disk_time",
                                        ds->avg_read_time, ds->avg_write_time);
 
                if (is_disk)
                {
-                       disk_submit (disk_name, "disk_merged",
+                       disk_submit (output_name, "disk_merged",
                                        read_merged, write_merged);
                } /* if (is_disk) */
+
+               /* release udev-based alternate name, if allocated */
+               free(alt_name);
        } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
 
+#if HAVE_LIBUDEV
+       udev_unref(handle_udev);
+#endif
+
        fclose (fh);
 /* #endif defined(KERNEL_LINUX) */
 
index fe3b672..00fcff1 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -208,7 +208,7 @@ static void dns_child_callback (const rfc1035_header_t *dns)
        pthread_mutex_unlock (&opcode_mutex);
 }
 
-static void *dns_child_loop (__attribute__((unused)) void *dummy)
+static int dns_run_pcap_loop (void)
 {
        pcap_t *pcap_obj;
        char    pcap_error[PCAP_ERRBUF_SIZE];
@@ -236,19 +236,24 @@ static void *dns_child_loop (__attribute__((unused)) void *dummy)
                                "failed: %s",
                                (pcap_device != NULL) ? pcap_device : "any",
                                pcap_error);
-               return (NULL);
+               return (PCAP_ERROR);
        }
 
        memset (&fp, 0, sizeof (fp));
-       if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
+       status = pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0);
+       if (status < 0)
        {
-               ERROR ("dns plugin: pcap_compile failed");
-               return (NULL);
+               ERROR ("dns plugin: pcap_compile failed: %s",
+                               pcap_statustostr (status));
+               return (status);
        }
-       if (pcap_setfilter (pcap_obj, &fp) < 0)
+
+       status = pcap_setfilter (pcap_obj, &fp);
+       if (status < 0)
        {
-               ERROR ("dns plugin: pcap_setfilter failed");
-               return (NULL);
+               ERROR ("dns plugin: pcap_setfilter failed: %s",
+                               pcap_statustostr (status));
+               return (status);
        }
 
        DEBUG ("dns plugin: PCAP object created.");
@@ -259,19 +264,65 @@ static void *dns_child_loop (__attribute__((unused)) void *dummy)
        status = pcap_loop (pcap_obj,
                        -1 /* loop forever */,
                        handle_pcap /* callback */,
-                       NULL /* Whatever this means.. */);
-       if (status < 0)
-               ERROR ("dns plugin: Listener thread is exiting "
-                               "abnormally: %s", pcap_geterr (pcap_obj));
-
-       DEBUG ("dns plugin: Child is exiting.");
+                       NULL /* user data */);
+       INFO ("dns plugin: pcap_loop exited with status %i.", status);
+       /* We need to handle "PCAP_ERROR" specially because libpcap currently
+        * doesn't return PCAP_ERROR_IFACE_NOT_UP for compatibility reasons. */
+       if (status == PCAP_ERROR)
+               status = PCAP_ERROR_IFACE_NOT_UP;
 
        pcap_close (pcap_obj);
-       listen_thread_init = 0;
-       pthread_exit (NULL);
+       return (status);
+} /* int dns_run_pcap_loop */
+
+static int dns_sleep_one_interval (void) /* {{{ */
+{
+       cdtime_t interval;
+       struct timespec ts = { 0, 0 };
+       int status = 0;
+
+       interval = plugin_get_interval ();
+       CDTIME_T_TO_TIMESPEC (interval, &ts);
+
+       while (42)
+       {
+               struct timespec rem = { 0, 0 };
+
+               status = nanosleep (&ts, &rem);
+               if (status == 0)
+                       break;
+               else if ((errno == EINTR) || (errno == EAGAIN))
+               {
+                       ts = rem;
+                       continue;
+               }
+               else
+                       break;
+       }
+
+       return (status);
+} /* }}} int dns_sleep_one_interval */
+
+static void *dns_child_loop (__attribute__((unused)) void *dummy) /* {{{ */
+{
+       int status;
+
+       while (42)
+       {
+               status = dns_run_pcap_loop ();
+               if (status != PCAP_ERROR_IFACE_NOT_UP)
+                       break;
 
+               dns_sleep_one_interval ();
+       }
+
+       if (status != PCAP_ERROR_BREAK)
+               ERROR ("dns plugin: PCAP returned error %s.",
+                               pcap_statustostr (status));
+
+       listen_thread_init = 0;
        return (NULL);
-} /* static void dns_child_loop (void) */
+} /* }}} void *dns_child_loop */
 
 static int dns_init (void)
 {
index 08381a8..dec14f3 100644 (file)
@@ -73,7 +73,7 @@ static int ethstat_add_interface (const oconfig_item_t *ci) /* {{{ */
     return (status);
 
   interfaces_num++;
-  INFO("ethstat plugin: Registred interface %s",
+  INFO("ethstat plugin: Registered interface %s",
       interfaces[interfaces_num - 1]);
 
   return (0);
@@ -83,6 +83,7 @@ static int ethstat_add_map (const oconfig_item_t *ci) /* {{{ */
 {
   value_map_t *map;
   int status;
+  char *key;
 
   if ((ci->values_num < 2)
       || (ci->values_num > 3)
@@ -96,9 +97,17 @@ static int ethstat_add_map (const oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
+  key = strdup (ci->values[0].value.string);
+  if (key == NULL)
+  {
+    ERROR ("ethstat plugin: strdup(3) failed.");
+    return (ENOMEM);
+  }
+
   map = malloc (sizeof (*map));
   if (map == NULL)
   {
+    sfree (key);
     ERROR ("ethstat plugin: malloc(3) failed.");
     return (ENOMEM);
   }
@@ -115,23 +124,24 @@ static int ethstat_add_map (const oconfig_item_t *ci) /* {{{ */
     if (value_map == NULL)
     {
       sfree (map);
+      sfree (key);
       ERROR ("ethstat plugin: c_avl_create() failed.");
       return (-1);
     }
   }
 
   status = c_avl_insert (value_map,
-      /* key = */ ci->values[0].value.string,
+      /* key = */ key,
       /* value = */ map);
   if (status != 0)
   {
-    sfree (map);
     if (status > 0)
-      ERROR ("ethstat plugin: Multiple mappings for \"%s\".",
-          ci->values[0].value.string);
+      ERROR ("ethstat plugin: Multiple mappings for \"%s\".", key);
     else
-      ERROR ("ethstat plugin: c_avl_insert(\"%s\") failed.",
-          ci->values[0].value.string);
+      ERROR ("ethstat plugin: c_avl_insert(\"%s\") failed.", key);
+
+    sfree (map);
+    sfree (key);
     return (-1);
   }
 
@@ -328,10 +338,31 @@ static int ethstat_read(void)
   return 0;
 }
 
+static int ethstat_shutdown (void)
+{
+  void *key = NULL;
+  void *value = NULL;
+
+  if (value_map == NULL)
+    return (0);
+
+  while (c_avl_pick (value_map, &key, &value) == 0)
+  {
+    sfree (key);
+    sfree (value);
+  }
+
+  c_avl_destroy (value_map);
+  value_map = NULL;
+
+  return (0);
+}
+
 void module_register (void)
 {
   plugin_register_complex_config ("ethstat", ethstat_config);
   plugin_register_read ("ethstat", ethstat_read);
+  plugin_register_shutdown ("ethstat", ethstat_shutdown);
 }
 
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 14d288f..cfd82a3 100644 (file)
@@ -287,71 +287,11 @@ static void set_environment (void) /* {{{ */
 } /* }}} void set_environment */
 
 __attribute__((noreturn))
-static void exec_child (program_list_t *pl) /* {{{ */
+static void exec_child (program_list_t *pl, int uid, int gid, int egid) /* {{{ */
 {
   int status;
-  int uid;
-  int gid;
-  int egid;
-
-  struct passwd *sp_ptr;
-  struct passwd sp;
-  char nambuf[2048];
   char errbuf[1024];
 
-  sp_ptr = NULL;
-  status = getpwnam_r (pl->user, &sp, nambuf, sizeof (nambuf), &sp_ptr);
-  if (status != 0)
-  {
-    ERROR ("exec plugin: Failed to get user information for user ``%s'': %s",
-        pl->user, sstrerror (errno, errbuf, sizeof (errbuf)));
-    exit (-1);
-  }
-  if (sp_ptr == NULL)
-  {
-    ERROR ("exec plugin: No such user: `%s'", pl->user);
-    exit (-1);
-  }
-
-  uid = sp.pw_uid;
-  gid = sp.pw_gid;
-  if (uid == 0)
-  {
-    ERROR ("exec plugin: Cowardly refusing to exec program as root.");
-    exit (-1);
-  }
-
-  /* The group configured in the configfile is set as effective group, because
-   * this way the forked process can (re-)gain the user's primary group. */
-  egid = -1;
-  if (NULL != pl->group)
-  {
-    if ('\0' != *pl->group) {
-      struct group *gr_ptr = NULL;
-      struct group gr;
-
-      status = getgrnam_r (pl->group, &gr, nambuf, sizeof (nambuf), &gr_ptr);
-      if (0 != status)
-      {
-        ERROR ("exec plugin: Failed to get group information "
-            "for group ``%s'': %s", pl->group,
-            sstrerror (errno, errbuf, sizeof (errbuf)));
-        exit (-1);
-      }
-      if (NULL == gr_ptr)
-      {
-        ERROR ("exec plugin: No such group: `%s'", pl->group);
-        exit (-1);
-      }
-
-      egid = gr.gr_gid;
-    }
-    else
-    {
-      egid = gid;
-    }
-  } /* if (pl->group == NULL) */
-
 #if HAVE_SETGROUPS
   if (getuid () == 0)
   {
@@ -429,6 +369,14 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
   int status;
   int pid;
 
+  int uid;
+  int gid;
+  int egid;
+
+  struct passwd *sp_ptr;
+  struct passwd sp;
+  char nambuf[2048];
+
   if (pl->pid != 0)
     return (-1);
 
@@ -456,6 +404,59 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
     return (-1);
   }
 
+  sp_ptr = NULL;
+  status = getpwnam_r (pl->user, &sp, nambuf, sizeof (nambuf), &sp_ptr);
+  if (status != 0)
+  {
+    ERROR ("exec plugin: Failed to get user information for user ``%s'': %s",
+        pl->user, sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+  if (sp_ptr == NULL)
+  {
+    ERROR ("exec plugin: No such user: `%s'", pl->user);
+    return (-1);
+  }
+
+  uid = sp.pw_uid;
+  gid = sp.pw_gid;
+  if (uid == 0)
+  {
+    ERROR ("exec plugin: Cowardly refusing to exec program as root.");
+    return (-1);
+  }
+
+  /* The group configured in the configfile is set as effective group, because
+   * this way the forked process can (re-)gain the user's primary group. */
+  egid = -1;
+  if (NULL != pl->group)
+  {
+    if ('\0' != *pl->group) {
+      struct group *gr_ptr = NULL;
+      struct group gr;
+
+      status = getgrnam_r (pl->group, &gr, nambuf, sizeof (nambuf), &gr_ptr);
+      if (0 != status)
+      {
+        ERROR ("exec plugin: Failed to get group information "
+            "for group ``%s'': %s", pl->group,
+            sstrerror (errno, errbuf, sizeof (errbuf)));
+        return (-1);
+      }
+      if (NULL == gr_ptr)
+      {
+        ERROR ("exec plugin: No such group: `%s'", pl->group);
+        return (-1);
+      }
+
+      egid = gr.gr_gid;
+    }
+    else
+    {
+      egid = gid;
+    }
+  } /* if (pl->group == NULL) */
+
   pid = fork ();
   if (pid < 0)
   {
@@ -493,7 +494,7 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
       close (fd_pipe_out[1]);
     }
 
-    /* Now connect the `out' pipe to STDOUT */
+    /* Now connect the `err' pipe to STDERR */
     if (fd_pipe_err[1] != STDERR_FILENO)
     {
       dup2 (fd_pipe_err[1], STDERR_FILENO);
@@ -505,7 +506,7 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
     /* Unblock all signals */
     reset_signal_mask ();
 
-    exec_child (pl);
+    exec_child (pl, uid, gid, egid);
     /* does not return */
   }
 
index ed2df61..f5606e9 100644 (file)
@@ -92,7 +92,7 @@ static void fc_free_matches (fc_match_t *m) /* {{{ */
     (*m->proc.destroy) (&m->user_data);
   else if (m->user_data != NULL)
   {
-    ERROR ("Filter sybsystem: fc_free_matches: There is user data, but no "
+    ERROR ("Filter subsystem: fc_free_matches: There is user data, but no "
         "destroy functions has been specified. "
         "Memory will probably be lost!");
   }
@@ -112,7 +112,7 @@ static void fc_free_targets (fc_target_t *t) /* {{{ */
     (*t->proc.destroy) (&t->user_data);
   else if (t->user_data != NULL)
   {
-    ERROR ("Filter sybsystem: fc_free_targets: There is user data, but no "
+    ERROR ("Filter subsystem: fc_free_targets: There is user data, but no "
         "destroy functions has been specified. "
         "Memory will probably be lost!");
   }
@@ -435,9 +435,10 @@ static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */
 
 static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
 {
-  fc_chain_t *chain;
+  fc_chain_t *chain = NULL;
   int status = 0;
   int i;
+  int new_chain = 1;
 
   if ((ci->values_num != 1)
       || (ci->values[0].type != OCONFIG_TYPE_STRING))
@@ -447,17 +448,26 @@ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
-  chain = (fc_chain_t *) malloc (sizeof (*chain));
+  if (chain_list_head != NULL)
+  {
+    if ((chain = fc_chain_get_by_name (ci->values[0].value.string)) != NULL)
+      new_chain = 0;
+  }
+
   if (chain == NULL)
   {
-    ERROR ("fc_config_add_chain: malloc failed.");
-    return (-1);
+    chain = (fc_chain_t *) malloc (sizeof (*chain));
+    if (chain == NULL)
+    {
+      ERROR ("fc_config_add_chain: malloc failed.");
+      return (-1);
+    }
+    memset (chain, 0, sizeof (*chain));
+    sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name));
+    chain->rules = NULL;
+    chain->targets = NULL;
+    chain->next = NULL;
   }
-  memset (chain, 0, sizeof (*chain));
-  sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name));
-  chain->rules = NULL;
-  chain->targets = NULL;
-  chain->next = NULL;
 
   for (i = 0; i < ci->children_num; i++)
   {
@@ -487,6 +497,9 @@ static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
 
   if (chain_list_head != NULL)
   {
+    if (!new_chain)
+      return (0);
+
     fc_chain_t *ptr;
 
     ptr = chain_list_head;
index ea82039..c618af9 100644 (file)
@@ -213,18 +213,19 @@ static int interface_read (void)
 
        for (if_ptr = if_list; if_ptr != NULL; if_ptr = if_ptr->ifa_next)
        {
-               if ((if_data = (struct IFA_DATA *) if_ptr->ifa_data) == NULL)
-                       continue;
+               if (if_ptr->ifa_addr != NULL && if_ptr->ifa_addr->sa_family == AF_LINK) {
+                       if_data = (struct IFA_DATA *) if_ptr->ifa_data;
 
-               if_submit (if_ptr->ifa_name, "if_octets",
+                       if_submit (if_ptr->ifa_name, "if_octets",
                                if_data->IFA_RX_BYTES,
                                if_data->IFA_TX_BYTES);
-               if_submit (if_ptr->ifa_name, "if_packets",
+                       if_submit (if_ptr->ifa_name, "if_packets",
                                if_data->IFA_RX_PACKT,
                                if_data->IFA_TX_PACKT);
-               if_submit (if_ptr->ifa_name, "if_errors",
+                       if_submit (if_ptr->ifa_name, "if_errors",
                                if_data->IFA_RX_ERROR,
                                if_data->IFA_TX_ERROR);
+               }
        }
 
        freeifaddrs (if_list);
index b69ca94..e4b10be 100644 (file)
@@ -906,7 +906,7 @@ static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */
 #undef SET_STRING
 
   /* Set the `time' member. Java stores time in milliseconds. */
-  status = ctoj_long (jvm_env, ((jlong) n->time) * ((jlong) 1000),
+  status = ctoj_long (jvm_env, (jlong) CDTIME_T_TO_MS (n->time),
       c_notification, o_notification, "setTime");
   if (status != 0)
   {
@@ -1306,7 +1306,7 @@ static int jtoc_notification (JNIEnv *jvm_env, notification_t *n, /* {{{ */
     return (-1);
   }
   /* Java measures time in milliseconds. */
-  n->time = (time_t) (tmp_long / ((jlong) 1000));
+  n->time = MS_TO_CDTIME_T(tmp_long);
 
   status = jtoc_int (jvm_env, &tmp_int,
       class_ptr, object_ptr, "getSeverity");
@@ -2436,7 +2436,7 @@ static void cjni_callback_info_destroy (void *arg) /* {{{ */
 
   cbi = (cjni_callback_info_t *) arg;
 
-  /* This condition can occurr when shutting down. */
+  /* This condition can occur when shutting down. */
   if (jvm == NULL)
   {
     sfree (cbi);
index 9fbf0d1..1d4dff5 100644 (file)
@@ -11,7 +11,7 @@ nodist_pkgconfig_DATA = libcollectdclient.pc
 BUILT_SOURCES = collectd/lcc_features.h
 
 libcollectdclient_la_SOURCES = client.c network.c network_buffer.c
-libcollectdclient_la_CPPFLAGS = $(AM_CPPFLAGS)
+libcollectdclient_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/libcollectdclient/collectd -I$(top_srcdir)/src
 libcollectdclient_la_LDFLAGS = -version-info 1:0:0
 libcollectdclient_la_LIBADD = 
 if BUILD_WITH_LIBGCRYPT
index 3962666..049f7f0 100644 (file)
@@ -65,6 +65,7 @@ int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv);
 
 /* Configure servers */
 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl);
+int lcc_server_set_interface (lcc_server_t *srv, char const *interface);
 int lcc_server_set_security_level (lcc_server_t *srv,
     lcc_security_level_t level,
     const char *username, const char *password);
index 6b6450c..ddb3b5b 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/libcollectdclient/network.c
- * Copyright (C) 2005-2012  Florian octo Forster
+ * Copyright (C) 2005-2013  Florian Forster
+ * Copyright (C) 2010       Max Henkel
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -21,7 +22,8 @@
  * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
- *   Florian octo Forster <octo at collectd.org>
+ *   Florian Forster <octo at collectd.org>
+ *   Max Henkel <henkel at gmx.at>
  **/
 
 #include "collectd.h"
 # include <netinet/in.h>
 #endif
 
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+
 #include "collectd/network.h"
 #include "collectd/network_buffer.h"
 
@@ -379,6 +385,81 @@ int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
   return (0);
 } /* }}} int lcc_server_set_ttl */
 
+int lcc_server_set_interface (lcc_server_t *srv, char const *interface) /* {{{ */
+{
+  int if_index;
+  int status;
+
+  if ((srv == NULL) || (interface == NULL))
+    return (EINVAL);
+
+  if_index = if_nametoindex (interface);
+  if (if_index == 0)
+    return (ENOENT);
+
+  /* IPv4 multicast */
+  if (srv->sa->sa_family == AF_INET)
+  {
+    struct sockaddr_in *addr = (struct sockaddr_in *) srv->sa;
+
+    if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+    {
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+      /* If possible, use the "ip_mreqn" structure which has
+       * an "interface index" member. Using the interface
+       * index is preferred here, because of its similarity
+       * to the way IPv6 handles this. Unfortunately, it
+       * appears not to be portable. */
+      struct ip_mreqn mreq;
+
+      memset (&mreq, 0, sizeof (mreq));
+      mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+      mreq.imr_address.s_addr = ntohl (INADDR_ANY);
+      mreq.imr_ifindex = if_index;
+#else
+      struct ip_mreq mreq;
+
+      memset (&mreq, 0, sizeof (mreq));
+      mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+      mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
+#endif
+
+      status = setsockopt (srv->fd, IPPROTO_IP, IP_MULTICAST_IF,
+          &mreq, sizeof (mreq));
+      if (status != 0)
+        return (status);
+
+      return (0);
+    }
+  }
+
+  /* IPv6 multicast */
+  if (srv->sa->sa_family == AF_INET6)
+  {
+    struct sockaddr_in6 *addr = (struct sockaddr_in6 *) srv->sa;
+
+    if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+    {
+      status = setsockopt (srv->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+          &if_index, sizeof (if_index));
+      if (status != 0)
+        return (status);
+
+      return (0);
+    }
+  }
+
+  /* else: Not a multicast interface. */
+#if defined(SO_BINDTODEVICE)
+  status = setsockopt (srv->fd, SOL_SOCKET, SO_BINDTODEVICE,
+      interface, strlen (interface) + 1);
+  if (status != 0)
+    return (-1);
+#endif
+
+  return (0);
+} /* }}} int lcc_server_set_interface */
+
 int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
     lcc_security_level_t level,
     const char *username, const char *password)
index 0b34e1f..7b06620 100644 (file)
@@ -106,9 +106,11 @@ struct lcc_network_buffer_s
   char *username;
   char *password;
 
+#if HAVE_LIBGCRYPT
   gcry_cipher_hd_t encr_cypher;
   size_t encr_header_len;
   char encr_iv[16];
+#endif
 };
 
 #define SSTRNCPY(dst,src,sz) do { \
@@ -128,6 +130,7 @@ static _Bool have_gcrypt (void) /* {{{ */
     return (result);
   need_init = 0;
 
+#if HAVE_LIBGCRYPT
   gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
 
   if (!gcry_check_version (GCRYPT_VERSION))
@@ -138,8 +141,12 @@ static _Bool have_gcrypt (void) /* {{{ */
 
   result = 1;
   return (1);
+#else
+  return(0);
+#endif
 } /* }}} _Bool have_gcrypt */
 
+#ifndef HAVE_HTONLL
 static uint64_t htonll (uint64_t val) /* {{{ */
 {
   static int config = 0;
@@ -169,6 +176,7 @@ static uint64_t htonll (uint64_t val) /* {{{ */
 
   return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
 } /* }}} uint64_t htonll */
+#endif
 
 static double htond (double val) /* {{{ */
 {
@@ -494,6 +502,7 @@ static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
   return (0);
 } /* }}} int nb_add_value_list */
 
+#if HAVE_LIBGCRYPT
 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
 {
   char *buffer;
@@ -620,6 +629,7 @@ static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
 
   return (0);
 } /* }}} int nb_add_encryption */
+#endif
 
 /*
  * Public functions
@@ -720,6 +730,7 @@ int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
   nb->ptr = nb->buffer;
   nb->free = nb->size;
 
+#if HAVE_LIBGCRYPT
   if (nb->seclevel == SIGN)
   {
     size_t username_len;
@@ -765,6 +776,7 @@ int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
     ADD_GENERIC (nb, hash, sizeof (hash));
     assert ((nb->encr_header_len + nb->free) == nb->size);
   }
+#endif
 
   return (0);
 } /* }}} int lcc_network_buffer_initialize */
@@ -774,10 +786,12 @@ int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
   if (nb == NULL)
     return (EINVAL);
 
+#if HAVE_LIBGCRYPT
   if (nb->seclevel == SIGN)
     nb_add_signature (nb);
   else if (nb->seclevel == ENCRYPT)
     nb_add_encryption (nb);
+#endif
 
   return (0);
 } /* }}} int lcc_network_buffer_finalize */
index c53a81d..cfabaaa 100644 (file)
@@ -178,6 +178,25 @@ init_value_list (value_list_t *vl, virDomainPtr dom)
 } /* void init_value_list */
 
 static void
+memory_submit (gauge_t memory, virDomainPtr dom)
+{
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].gauge = memory;
+
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, "memory", sizeof (vl.type));
+    sstrncpy (vl.type_instance, "total", sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
 cpu_submit (unsigned long long cpu_time,
             virDomainPtr dom, const char *type)
 {
@@ -407,7 +426,7 @@ lv_read (void)
                  interface_devices[i].path);
 #endif
 
-    /* Get CPU usage, VCPU usage for each domain. */
+    /* Get CPU usage, memory, VCPU usage for each domain. */
     for (i = 0; i < nr_domains; ++i) {
         virDomainInfo info;
         virVcpuInfoPtr vinfo = NULL;
@@ -423,6 +442,7 @@ lv_read (void)
         }
 
         cpu_submit (info.cpuTime, domains[i], "virt_cpu_total");
+        memory_submit ((gauge_t) info.memory * 1024, domains[i]);
 
         vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0]));
         if (vinfo == NULL) {
index 0188da7..9b5dd3d 100644 (file)
@@ -2,6 +2,7 @@
  * collectd - src/load.c
  * Copyright (C) 2005-2008  Florian octo Forster
  * Copyright (C) 2009       Manuel Sanmartin
+ * Copyright (C) 2013       Vedran Bartonicek
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -19,6 +20,7 @@
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
  *   Manuel Sanmartin
+ *   Vedran Bartonicek <vbartoni at gmail.com>
  **/
 
 #define _BSD_SOURCE
@@ -27,6 +29,8 @@
 #include "common.h"
 #include "plugin.h"
 
+#include <unistd.h>
+
 #ifdef HAVE_SYS_LOADAVG_H
 #include <sys/loadavg.h>
 #endif
 # include <libperfstat.h>
 #endif /* HAVE_PERFSTAT */
 
+static _Bool report_relative_load = 0;
+
+static const char *config_keys[] =
+{
+       "ReportRelative"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static int load_config (const char *key, const char *value)
+{
+       if (strcasecmp (key, "ReportRelative") == 0)
+#ifdef _SC_NPROCESSORS_ONLN
+               report_relative_load = IS_TRUE (value) ? 1 : 0;
+#else
+                WARNING ("load plugin: The \"ReportRelative\" configuration "
+                         "is not available, because I can't determine the "
+                         "number of CPUS on this system. Sorry.");
+#endif
+       return (-1);
+
+}
 static void load_submit (gauge_t snum, gauge_t mnum, gauge_t lnum)
 {
        value_t values[3];
        value_list_t vl = VALUE_LIST_INIT;
+        int cores = 0;
+        char errbuf[1024];
+
+#ifdef  _SC_NPROCESSORS_ONLN
+        if (report_relative_load) {
+                if ((cores = sysconf(_SC_NPROCESSORS_ONLN)) < 1) {
+                       WARNING ("load: sysconf failed : %s",
+                                sstrerror (errno, errbuf, sizeof (errbuf)));
+               }
+       }
+#endif
+       if (cores > 0) {
+               snum /= cores;
+               mnum /= cores;
+               lnum /= cores;
+       }
 
        values[0].gauge = snum;
        values[1].gauge = mnum;
@@ -60,10 +101,16 @@ static void load_submit (gauge_t snum, gauge_t mnum, gauge_t lnum)
 
        vl.values = values;
        vl.values_len = STATIC_ARRAY_SIZE (values);
+
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "load", sizeof (vl.plugin));
        sstrncpy (vl.type, "load", sizeof (vl.type));
 
+       if (cores > 0) {
+               sstrncpy(vl.type_instance, "relative",
+                        sizeof (vl.type_instance));
+       }
+
        plugin_dispatch_values (&vl);
 }
 
@@ -73,17 +120,17 @@ static int load_read (void)
        double load[3];
 
        if (getloadavg (load, 3) == 3)
-               load_submit (load[LOADAVG_1MIN], load[LOADAVG_5MIN], load[LOADAVG_15MIN]);
-       else
-       {
-               char errbuf[1024];
-               WARNING ("load: getloadavg failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                load_submit (load[LOADAVG_1MIN], load[LOADAVG_5MIN], load[LOADAVG_15MIN]);
+        else
+        {
+                char errbuf[1024];
+                WARNING ("load: getloadavg failed: %s",
+                         sstrerror (errno, errbuf, sizeof (errbuf)));
        }
 /* #endif HAVE_GETLOADAVG */
 
 #elif defined(KERNEL_LINUX)
-       gauge_t snum, mnum, lnum;
+        gauge_t snum, mnum, lnum;
        FILE *loadavg;
        char buffer[16];
 
@@ -123,11 +170,11 @@ static int load_read (void)
        mnum = atof (fields[1]);
        lnum = atof (fields[2]);
 
-       load_submit (snum, mnum, lnum);
+        load_submit(snum, mnum, lnum);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBSTATGRAB
-       gauge_t snum, mnum, lnum;
+        gauge_t snum, mnum, lnum;
        sg_load_stats *ls;
 
        if ((ls = sg_get_load_stats ()) == NULL)
@@ -136,12 +183,11 @@ static int load_read (void)
        snum = ls->min1;
        mnum = ls->min5;
        lnum = ls->min15;
-
-       load_submit (snum, mnum, lnum);
+        load_submit(snum, mnum, lnum);
 /* #endif HAVE_LIBSTATGRAB */
 
 #elif HAVE_PERFSTAT
-       gauge_t snum, mnum, lnum;
+        gauge_t snum, mnum, lnum;
        perfstat_cpu_total_t cputotal;
 
        if (perfstat_cpu_total(NULL,  &cputotal, sizeof(perfstat_cpu_total_t), 1) < 0)
@@ -155,8 +201,7 @@ static int load_read (void)
        snum = (float)cputotal.loadavg[0]/(float)(1<<SBITS);
        mnum = (float)cputotal.loadavg[1]/(float)(1<<SBITS);
        lnum = (float)cputotal.loadavg[2]/(float)(1<<SBITS);
-
-       load_submit (snum, mnum, lnum);
+        load_submit(snum, mnum, lnum);
 /* #endif HAVE_PERFSTAT */
 
 #else
@@ -168,5 +213,6 @@ static int load_read (void)
 
 void module_register (void)
 {
+       plugin_register_config ("load", load_config, config_keys, config_keys_num);
        plugin_register_read ("load", load_read);
 } /* void module_register */
diff --git a/src/log_logstash.c b/src/log_logstash.c
new file mode 100644 (file)
index 0000000..6cb5b0a
--- /dev/null
@@ -0,0 +1,389 @@
+/**
+ * collectd - src/log_logstash.c
+ * Copyright (C) 2013 Pierre-Yves Ritschard
+ *
+ * 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:
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ * Acknowledgements:
+ *   This file is largely inspired by logfile.c
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <sys/types.h>
+#include <pthread.h>
+#include <yajl/yajl_common.h>
+#include <yajl/yajl_gen.h>
+#if HAVE_YAJL_YAJL_VERSION_H
+# include <yajl/yajl_version.h>
+#endif
+#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
+# define HAVE_YAJL_V2 1
+#endif
+
+#define DEFAULT_LOGFILE LOCALSTATEDIR"/log/"PACKAGE_NAME".json.log"
+
+#if COLLECT_DEBUG
+static int log_level = LOG_DEBUG;
+#else
+static int log_level = LOG_INFO;
+#endif /* COLLECT_DEBUG */
+
+static pthread_mutex_t file_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static char *log_file = NULL;
+
+static const char *config_keys[] =
+{
+       "LogLevel",
+       "File"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static int log_logstash_config (const char *key, const char *value)
+{
+
+       if (0 == strcasecmp (key, "LogLevel")) {
+               log_level = parse_log_severity(value);
+        if (log_level < 0) {
+            log_level = LOG_INFO;
+            ERROR("log_logstash: invalid loglevel [%s] defaulting to 'info'",
+                  value);
+            return 1;
+        }
+       }
+       else if (0 == strcasecmp (key, "File")) {
+               sfree (log_file);
+               log_file = strdup (value);
+       }
+       else {
+               return -1;
+       }
+       return 0;
+} /* int log_logstash_config (const char *, const char *) */
+
+static void log_logstash_print (yajl_gen g, int severity,
+               cdtime_t timestamp_time)
+{
+       FILE *fh;
+       _Bool do_close = 0;
+       struct tm timestamp_tm;
+       char timestamp_str[64];
+       const unsigned char *buf;
+       time_t tt;
+#if HAVE_YAJL_V2
+       size_t len;
+#else
+       unsigned int len;
+#endif
+
+       if (yajl_gen_string(g, (u_char *)"@level", strlen("@level")) !=
+           yajl_gen_status_ok)
+               goto err;
+
+       switch (severity) {
+       case LOG_ERR:
+               if (yajl_gen_string(g, (u_char *)"error", strlen("error")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               break;
+       case LOG_WARNING:
+               if (yajl_gen_string(g, (u_char *)"warning",
+                                   strlen("warning")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               break;
+       case LOG_NOTICE:
+               if (yajl_gen_string(g, (u_char *)"notice", strlen("notice")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               break;
+       case LOG_INFO:
+               if (yajl_gen_string(g, (u_char *)"info", strlen("info")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               break;
+       case LOG_DEBUG:
+               if (yajl_gen_string(g, (u_char *)"debug", strlen("debug")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               break;
+       default:
+               if (yajl_gen_string(g, (u_char *)"unknown",
+                                   strlen("unknown")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               break;
+       }
+
+       if (yajl_gen_string(g, (u_char *)"@timestamp", strlen("@timestamp")) !=
+           yajl_gen_status_ok)
+               goto err;
+
+       tt = CDTIME_T_TO_TIME_T (timestamp_time);
+       gmtime_r (&tt, &timestamp_tm);
+
+       /*
+        * format time as a UTC ISO 8601 compliant string
+        */
+       strftime (timestamp_str, sizeof (timestamp_str),
+                 "%Y-%m-%d %H:%M:%SZ", &timestamp_tm);
+       timestamp_str[sizeof (timestamp_str) - 1] = '\0';
+
+       if (yajl_gen_string(g, (u_char *)timestamp_str,
+                           strlen(timestamp_str)) !=
+           yajl_gen_status_ok)
+               goto err;
+
+       if (yajl_gen_map_close(g) != yajl_gen_status_ok)
+               goto err;
+
+       if (yajl_gen_get_buf(g, &buf, &len) != yajl_gen_status_ok)
+               goto err;
+       pthread_mutex_lock (&file_lock);
+
+       if (log_file == NULL)
+       {
+               fh = fopen (DEFAULT_LOGFILE, "a");
+               do_close = 1;
+       } else if (strcasecmp(log_file, "stdout") == 0) {
+        fh = stdout;
+        do_close = 0;
+       } else if (strcasecmp(log_file, "stderr") == 0) {
+        fh = stderr;
+        do_close = 0;
+       } else {
+               fh = fopen (log_file, "a");
+               do_close = 1;
+       }
+
+       if (fh == NULL)
+       {
+                       char errbuf[1024];
+                       fprintf (stderr, "log_logstash plugin: fopen (%s) failed: %s\n",
+                                       (log_file == NULL) ? DEFAULT_LOGFILE : log_file,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
+       else
+       {
+               fprintf(fh, "%s\n", buf);
+               if (do_close) {
+                       fclose (fh);
+               } else {
+                       fflush(fh);
+               }
+       }
+       pthread_mutex_unlock (&file_lock);
+       yajl_gen_free(g);
+       return;
+
+ err:
+       yajl_gen_free(g);
+       fprintf(stderr, "Could not correctly generate JSON message\n");
+       return;
+} /* void log_logstash_print */
+
+static void log_logstash_log (int severity, const char *msg,
+               user_data_t __attribute__((unused)) *user_data)
+{
+       yajl_gen g;
+#if !defined(HAVE_YAJL_V2)
+       yajl_gen_config conf;
+
+       conf.beautify = 0;
+#endif
+
+       if (severity > log_level)
+               return;
+
+#if HAVE_YAJL_V2
+       g = yajl_gen_alloc(NULL);
+#else
+       g = yajl_gen_alloc(&conf, NULL);
+#endif
+
+       if (g == NULL) {
+               fprintf(stderr, "Could not allocate JSON generator.\n");
+               return;
+       }
+
+       if (yajl_gen_map_open(g) != yajl_gen_status_ok)
+               goto err;
+       if (yajl_gen_string(g, (u_char *)"@message", strlen("@message")) !=
+           yajl_gen_status_ok)
+               goto err;
+       if (yajl_gen_string(g, (u_char *)msg, strlen(msg)) !=
+           yajl_gen_status_ok)
+               goto err;
+
+       log_logstash_print (g, severity, cdtime ());
+       return;
+ err:
+       yajl_gen_free(g);
+       fprintf(stderr, "Could not generate JSON message preamble\n");
+       return;
+
+} /* void log_logstash_log (int, const char *) */
+
+static int log_logstash_notification (const notification_t *n,
+               user_data_t __attribute__((unused)) *user_data)
+{
+       yajl_gen g;
+#if HAVE_YAJL_V2
+       g = yajl_gen_alloc(NULL);
+#else
+       yajl_gen_config conf;
+
+       conf.beautify = 0;
+       g = yajl_gen_alloc(&conf, NULL);
+#endif
+
+       if (g == NULL) {
+               fprintf(stderr, "Could not allocate JSON generator.\n");
+               return (0);
+       }
+
+       if (yajl_gen_map_open(g) != yajl_gen_status_ok)
+               goto err;
+       if (yajl_gen_string(g, (u_char *)"@message", strlen("@message")) !=
+           yajl_gen_status_ok)
+               goto err;
+       if (strlen(n->message) > 0) {
+               if (yajl_gen_string(g, (u_char *)n->message,
+                                   strlen(n->message)) !=
+                   yajl_gen_status_ok)
+                       goto err;
+       } else {
+               if (yajl_gen_string(g, (u_char *)"notification without a message",
+                                   strlen("notification without a message")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+       }
+
+
+       if (yajl_gen_string(g, (u_char *)"@fields", strlen("@fields")) !=
+           yajl_gen_status_ok)
+               goto err;
+       if (yajl_gen_map_open(g) !=
+           yajl_gen_status_ok)
+               goto err;
+
+       if (strlen(n->host) > 0) {
+               if (yajl_gen_string(g, (u_char *)"host", strlen("host")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               if (yajl_gen_string(g, (u_char *)n->host, strlen(n->host)) !=
+                   yajl_gen_status_ok)
+                       goto err;
+
+       }
+       if (strlen(n->plugin) > 0) {
+               if (yajl_gen_string(g, (u_char *)"plugin", strlen("plugin")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               if (yajl_gen_string(g, (u_char *)n->plugin, strlen(n->plugin)) !=
+                   yajl_gen_status_ok)
+                       goto err;
+       }
+       if (strlen(n->plugin_instance) > 0) {
+               if (yajl_gen_string(g, (u_char *)"plugin_instance",
+                                   strlen("plugin_instance")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               if (yajl_gen_string(g, (u_char *)n->plugin_instance,
+                                   strlen(n->plugin_instance)) !=
+                   yajl_gen_status_ok)
+                       goto err;
+       }
+       if (strlen(n->type) > 0) {
+               if (yajl_gen_string(g, (u_char *)"type", strlen("type")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               if (yajl_gen_string(g, (u_char *)n->type, strlen(n->type)) !=
+                   yajl_gen_status_ok)
+                       goto err;
+       }
+       if (strlen(n->type_instance) > 0) {
+               if (yajl_gen_string(g, (u_char *)"type_instance",
+                                   strlen("type_instance")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               if (yajl_gen_string(g, (u_char *)n->type_instance,
+                                   strlen(n->type_instance)) !=
+                   yajl_gen_status_ok)
+                       goto err;
+       }
+
+       if (yajl_gen_string(g, (u_char *)"severity",
+                           strlen("severity")) !=
+           yajl_gen_status_ok)
+               goto err;
+
+       switch (n->severity) {
+       case NOTIF_FAILURE:
+               if (yajl_gen_string(g, (u_char *)"failure",
+                                   strlen("failure")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               break;
+       case NOTIF_WARNING:
+               if (yajl_gen_string(g, (u_char *)"warning",
+                                   strlen("warning")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               break;
+       case NOTIF_OKAY:
+               if (yajl_gen_string(g, (u_char *)"ok",
+                                   strlen("ok")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               break;
+       default:
+               if (yajl_gen_string(g, (u_char *)"unknown",
+                                   strlen("unknown")) !=
+                   yajl_gen_status_ok)
+                       goto err;
+               break;
+       }
+       if (yajl_gen_map_close(g) != yajl_gen_status_ok)
+               goto err;
+
+       log_logstash_print (g, LOG_INFO, (n->time != 0) ? n->time : cdtime ());
+       return (0);
+
+ err:
+       yajl_gen_free(g);
+       fprintf(stderr, "Could not correctly generate JSON notification\n");
+       return (0);
+} /* int log_logstash_notification */
+
+void module_register (void)
+{
+       plugin_register_config ("log_logstash",
+                               log_logstash_config,
+                               config_keys,
+                               config_keys_num);
+       plugin_register_log ("log_logstash",
+                            log_logstash_log,
+                            /* user_data = */ NULL);
+       plugin_register_notification ("log_logstash",
+                                     log_logstash_notification,
+                                     /* user_data = */ NULL);
+} /* void module_register (void) */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
index ded7732..63448cb 100644 (file)
@@ -54,7 +54,11 @@ static int logfile_config (const char *key, const char *value)
 {
        if (0 == strcasecmp (key, "LogLevel")) {
                log_level = parse_log_severity(value);
-               if (log_level == -1) return 1; /* to keep previous behaviour */
+               if (log_level < 0) {
+                       log_level = LOG_INFO;
+                       ERROR ("logfile: invalid loglevel [%s] defaulting to 'info'", value);
+                       return (1);
+               }
        }
        else if (0 == strcasecmp (key, "File")) {
                sfree (log_file);
@@ -81,7 +85,7 @@ static void logfile_print (const char *msg, int severity,
                cdtime_t timestamp_time)
 {
        FILE *fh;
-       int do_close = 0;
+       _Bool do_close = 0;
        struct tm timestamp_tm;
        char timestamp_str[64];
        char level_str[16] = "";
@@ -151,8 +155,11 @@ static void logfile_print (const char *msg, int severity,
                else
                        fprintf (fh, "%s%s\n", level_str, msg);
 
-               if (do_close != 0)
+               if (do_close) {
                        fclose (fh);
+               } else {
+                       fflush(fh);
+               }
        }
 
        pthread_mutex_unlock (&file_lock);
diff --git a/src/lvm.c b/src/lvm.c
new file mode 100644 (file)
index 0000000..6ef3a7b
--- /dev/null
+++ b/src/lvm.c
@@ -0,0 +1,103 @@
+/**
+ * collectd - src/lvm.c
+ * Copyright (C) 2013       Chad Malfait
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Chad Malfait <malfaitc at yahoo.com>
+ **/
+
+#include <lvm2app.h>
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+static void lvm_submit (char const *plugin_instance, char const *type_instance,
+        uint64_t ivalue)
+{
+    value_t v;
+    value_list_t vl = VALUE_LIST_INIT;
+
+    v.gauge = (gauge_t) ivalue;
+
+    vl.values = &v;
+    vl.values_len = 1;
+
+    sstrncpy(vl.host, hostname_g, sizeof (vl.host));
+    sstrncpy(vl.plugin, "lvm", sizeof (vl.plugin));
+    sstrncpy(vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+    sstrncpy(vl.type, "df_complex", sizeof (vl.type));
+    sstrncpy(vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+}
+
+static int vg_read(vg_t vg, char const *vg_name)
+{
+    struct dm_list *lvs;
+    struct lvm_lv_list *lvl;
+
+    lvm_submit (vg_name, "free", lvm_vg_get_free_size(vg));
+
+    lvs = lvm_vg_list_lvs(vg);
+    dm_list_iterate_items(lvl, lvs) {
+         lvm_submit(vg_name, lvm_lv_get_name(lvl->lv), lvm_lv_get_size(lvl->lv));
+    }
+
+    return (0);
+}
+
+static int lvm_read(void)
+{
+    lvm_t lvm;
+    struct dm_list *vg_names;
+    struct lvm_str_list *name_list;
+
+    lvm = lvm_init(NULL);
+    if (!lvm) {
+        ERROR("lvm plugin: lvm_init failed.");
+        return (-1);
+    }
+
+    vg_names = lvm_list_vg_names(lvm);
+    if (!vg_names) {
+        ERROR("lvm plugin lvm_list_vg_name failed %s", lvm_errmsg(lvm));
+        lvm_quit(lvm);
+        return (-1);
+    }
+
+    dm_list_iterate_items(name_list, vg_names) {
+        vg_t vg;
+
+        vg = lvm_vg_open(lvm, name_list->str, "r", 0);
+        if (!vg) {
+            ERROR ("lvm plugin: lvm_vg_open (%s) failed: %s",
+                    name_list->str, lvm_errmsg(lvm));
+            continue;
+        }
+
+        vg_read(vg, name_list->str);
+        lvm_vg_close(vg);
+    }
+
+    lvm_quit(lvm);
+    return (0);
+} /*lvm_read */
+
+void module_register(void)
+{
+    plugin_register_read("lvm", lvm_read);
+} /* void module_register */
index a09f45e..e2ccfee 100644 (file)
@@ -84,6 +84,15 @@ static int memcached_connect_unix (memcached_t *st)
     return (-1);
   }
 
+  /* connect to the memcached daemon */
+  int status = connect (fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
+  if (status != 0)
+  {
+      shutdown (fd, SHUT_RDWR);
+      close (fd);
+      fd = -1;
+  }
+
   return (fd);
 } /* int memcached_connect_unix */
 
@@ -340,6 +349,10 @@ static int memcached_read (user_data_t *user_data)
   gauge_t bytes_total = NAN;
   gauge_t hits = NAN;
   gauge_t gets = NAN;
+  gauge_t incr_hits = NAN;
+  derive_t incr = 0;
+  gauge_t decr_hits = NAN;
+  derive_t decr = 0;
   derive_t rusage_user = 0;
   derive_t rusage_syst = 0;
   derive_t octets_rx = 0;
@@ -374,7 +387,7 @@ static int memcached_read (user_data_t *user_data)
 
     /*
      * For an explanation on these fields please refer to
-     * <http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt>
+     * <https://github.com/memcached/memcached/blob/master/doc/protocol.txt>
      */
 
     /*
@@ -424,6 +437,10 @@ static int memcached_read (user_data_t *user_data)
     {
       submit_gauge ("memcached_connections", "current", atof (fields[2]), st);
     }
+    else if (FIELD_IS ("listen_disabled_num"))
+    {
+      submit_derive ("memcached_connections", "listen_disabled", atof (fields[2]), st);
+    }
 
     /*
      * Commands
@@ -437,6 +454,36 @@ static int memcached_read (user_data_t *user_data)
     }
 
     /*
+     * Increment/Decrement
+     */
+    else if (FIELD_IS("incr_misses"))
+    {
+      derive_t incr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "incr_misses", incr_count, st);
+      incr += incr_count;
+    }
+    else if (FIELD_IS ("incr_hits"))
+    {
+      derive_t incr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "incr_hits", incr_count, st);
+      incr_hits = atof (fields[2]);
+      incr += incr_count;
+    }
+    else if (FIELD_IS ("decr_misses"))
+    {
+      derive_t decr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "decr_misses", decr_count, st);
+      decr += decr_count;
+    }
+    else if (FIELD_IS ("decr_hits"))
+    {
+      derive_t decr_count = atoll (fields[2]);
+      submit_derive ("memcached_ops", "decr_hits", decr_count, st);
+      decr_hits = atof (fields[2]);
+      decr += decr_count;
+    }
+
+    /*
      * Operations on the cache, i. e. cache hits, cache misses and evictions of items
      */
     else if (FIELD_IS ("get_hits"))
@@ -485,6 +532,20 @@ static int memcached_read (user_data_t *user_data)
     submit_gauge ("percent", "hitratio", rate, st);
   }
 
+  if (!isnan (incr_hits) && incr != 0)
+  {
+    gauge_t incr_rate = 100.0 * incr_hits / incr;
+    submit_gauge ("percent", "incr_hitratio", incr_rate, st);
+    submit_derive ("memcached_ops", "incr", incr, st);
+  }
+
+  if (!isnan (decr_hits) && decr != 0)
+  {
+    gauge_t decr_rate = 100.0 * decr_hits / decr;
+    submit_gauge ("percent", "decr_hitratio", decr_rate, st);
+    submit_derive ("memcached_ops", "decr", decr, st);
+  }
+
   return 0;
 } /* int memcached_read */
 
index 6a50161..b33c1f8 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/memory.c
- * Copyright (C) 2005-2008  Florian octo Forster
+ * Copyright (C) 2005-2014  Florian octo Forster
  * Copyright (C) 2009       Simon Kuhnle
  * Copyright (C) 2009       Manuel Sanmartin
  *
@@ -84,12 +84,33 @@ static int pagesize;
 /* endif HAVE_LIBSTATGRAB */
 #elif HAVE_PERFSTAT
 static int pagesize;
-static perfstat_memory_total_t pmemory;
 /* endif HAVE_PERFSTAT */
 #else
 # error "No applicable input method."
 #endif
 
+static _Bool values_absolute = 1;
+static _Bool values_percentage = 0;
+
+static int memory_config (oconfig_item_t *ci) /* {{{ */
+{
+       int i;
+
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+               if (strcasecmp ("ValuesAbsolute", child->key) == 0)
+                       cf_util_get_boolean (child, &values_absolute);
+               else if (strcasecmp ("ValuesPercentage", child->key) == 0)
+                       cf_util_get_boolean (child, &values_percentage);
+               else
+                       ERROR ("memory plugin: Invalid configuration option: "
+                                       "\"%s\".", child->key);
+       }
+
+       return (0);
+} /* }}} int memory_config */
+
 static int memory_init (void)
 {
 #if HAVE_HOST_STATISTICS
@@ -134,24 +155,14 @@ static int memory_init (void)
        return (0);
 } /* int memory_init */
 
-static void memory_submit (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, "memory", sizeof (vl.plugin));
-       sstrncpy (vl.type, "memory", sizeof (vl.type));
-       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
-
-       plugin_dispatch_values (&vl);
-}
+#define MEMORY_SUBMIT(...) do { \
+       if (values_absolute) \
+               plugin_dispatch_multivalue (vl, 0, __VA_ARGS__, NULL); \
+       if (values_percentage) \
+               plugin_dispatch_multivalue (vl, 1, __VA_ARGS__, NULL); \
+} while (0)
 
-static int memory_read (void)
+static int memory_read_internal (value_list_t *vl)
 {
 #if HAVE_HOST_STATISTICS
        kern_return_t status;
@@ -200,10 +211,10 @@ static int memory_read (void)
        inactive = (gauge_t) (((uint64_t) vm_data.inactive_count) * ((uint64_t) pagesize));
        free     = (gauge_t) (((uint64_t) vm_data.free_count)     * ((uint64_t) pagesize));
 
-       memory_submit ("wired",    wired);
-       memory_submit ("active",   active);
-       memory_submit ("inactive", inactive);
-       memory_submit ("free",     free);
+       MEMORY_SUBMIT ("wired",    wired,
+                      "active",   active,
+                      "inactive", inactive,
+                      "free",     free);
 /* #endif HAVE_HOST_STATISTICS */
 
 #elif HAVE_SYSCTLBYNAME
@@ -253,11 +264,11 @@ static int memory_read (void)
                if (!isnan (sysctl_vals[i]))
                        sysctl_vals[i] *= sysctl_vals[0];
 
-       memory_submit ("free",     sysctl_vals[2]);
-       memory_submit ("wired",    sysctl_vals[3]);
-       memory_submit ("active",   sysctl_vals[4]);
-       memory_submit ("inactive", sysctl_vals[5]);
-       memory_submit ("cache",    sysctl_vals[6]);
+       MEMORY_SUBMIT ("free",     (gauge_t) sysctl_vals[2],
+                      "wired",    (gauge_t) sysctl_vals[3],
+                      "active",   (gauge_t) sysctl_vals[4],
+                      "inactive", (gauge_t) sysctl_vals[5],
+                      "cache",    (gauge_t) sysctl_vals[6]);
 /* #endif HAVE_SYSCTLBYNAME */
 
 #elif KERNEL_LINUX
@@ -267,10 +278,11 @@ static int memory_read (void)
        char *fields[8];
        int numfields;
 
-       long long mem_used = 0;
-       long long mem_buffered = 0;
-       long long mem_cached = 0;
-       long long mem_free = 0;
+       gauge_t mem_total = 0;
+       gauge_t mem_used = 0;
+       gauge_t mem_buffered = 0;
+       gauge_t mem_cached = 0;
+       gauge_t mem_free = 0;
 
        if ((fh = fopen ("/proc/meminfo", "r")) == NULL)
        {
@@ -280,12 +292,12 @@ static int memory_read (void)
                return (-1);
        }
 
-       while (fgets (buffer, 1024, fh) != NULL)
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
-               long long *val = NULL;
+               gauge_t *val = NULL;
 
                if (strncasecmp (buffer, "MemTotal:", 9) == 0)
-                       val = &mem_used;
+                       val = &mem_total;
                else if (strncasecmp (buffer, "MemFree:", 8) == 0)
                        val = &mem_free;
                else if (strncasecmp (buffer, "Buffers:", 8) == 0)
@@ -295,12 +307,11 @@ static int memory_read (void)
                else
                        continue;
 
-               numfields = strsplit (buffer, fields, 8);
-
+               numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
                if (numfields < 2)
                        continue;
 
-               *val = atoll (fields[1]) * 1024LL;
+               *val = 1024.0 * atof (fields[1]);
        }
 
        if (fclose (fh))
@@ -310,19 +321,19 @@ static int memory_read (void)
                                sstrerror (errno, errbuf, sizeof (errbuf)));
        }
 
-       if (mem_used >= (mem_free + mem_buffered + mem_cached))
-       {
-               mem_used -= mem_free + mem_buffered + mem_cached;
-               memory_submit ("used",     mem_used);
-               memory_submit ("buffered", mem_buffered);
-               memory_submit ("cached",   mem_cached);
-               memory_submit ("free",     mem_free);
-       }
+       if (mem_total < (mem_free + mem_buffered + mem_cached))
+               return (-1);
+
+       mem_used = mem_total - (mem_free + mem_buffered + mem_cached);
+       MEMORY_SUBMIT ("used",     mem_used,
+                      "buffered", mem_buffered,
+                      "cached",   mem_cached,
+                      "free",     mem_free);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
-        /* Most of the additions here were taken as-is from the k9toolkit from
-         * Brendan Gregg and are subject to change I guess */
+       /* Most of the additions here were taken as-is from the k9toolkit from
+        * Brendan Gregg and are subject to change I guess */
        long long mem_used;
        long long mem_free;
        long long mem_lock;
@@ -372,7 +383,7 @@ static int memory_read (void)
        }
 
        /* mem_kern is accounted for in mem_lock */
-       if ( pp_kernel < mem_lock )
+       if (pp_kernel < mem_lock)
        {
                mem_kern = pp_kernel;
                mem_lock -= pp_kernel;
@@ -389,16 +400,19 @@ static int memory_read (void)
        mem_kern *= pagesize; /* it's 2011 RAM is cheap */
        mem_unus *= pagesize;
 
-       memory_submit ("used",   mem_used);
-       memory_submit ("free",   mem_free);
-       memory_submit ("locked", mem_lock);
-       memory_submit ("kernel", mem_kern);
-       memory_submit ("unusable", mem_unus);
+       MEMORY_SUBMIT ("used",     (gauge_t) mem_used,
+                      "free",     (gauge_t) mem_free,
+                      "locked",   (gauge_t) mem_lock,
+                      "kernel",   (gauge_t) mem_kern,
+                      "unusable", (gauge_t) mem_unus);
 /* #endif HAVE_LIBKSTAT */
 
 #elif HAVE_SYSCTL
        int mib[] = {CTL_VM, VM_METER};
        struct vmtotal vmtotal;
+       gauge_t mem_active;
+       gauge_t mem_inactive;
+       gauge_t mem_free;
        size_t size;
 
        memset (&vmtotal, 0, sizeof (vmtotal));
@@ -412,42 +426,76 @@ static int memory_read (void)
        }
 
        assert (pagesize > 0);
-       memory_submit ("active",   vmtotal.t_arm * pagesize);
-       memory_submit ("inactive", (vmtotal.t_rm - vmtotal.t_arm) * pagesize);
-       memory_submit ("free",     vmtotal.t_free * pagesize);
+       mem_active   = (gauge_t) (vmtotal.t_arm * pagesize);
+       mem_inactive = (gauge_t) ((vmtotal.t_rm - vmtotal.t_arm) * pagesize);
+       mem_free     = (gauge_t) (vmtotal.t_free * pagesize);
+
+       MEMORY_SUBMIT ("active",   mem_active,
+                      "inactive", mem_inactive,
+                      "free",     mem_free);
 /* #endif HAVE_SYSCTL */
 
 #elif HAVE_LIBSTATGRAB
        sg_mem_stats *ios;
 
-       if ((ios = sg_get_mem_stats ()) != NULL)
-       {
-               memory_submit ("used",   ios->used);
-               memory_submit ("cached", ios->cache);
-               memory_submit ("free",   ios->free);
-       }
+       ios = sg_get_mem_stats ();
+       if (ios == NULL)
+               return (-1);
+
+       MEMORY_SUBMIT ("used",   (gauge_t) ios->used,
+                      "cached", (gauge_t) ios->cache,
+                      "free",   (gauge_t) ios->free);
 /* #endif HAVE_LIBSTATGRAB */
 
 #elif HAVE_PERFSTAT
-       if (perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1) < 0)
+       perfstat_memory_total_t pmemory;
+
+       memset (&pmemory, 0, sizeof (pmemory));
+       if (perfstat_memory_total(NULL, &pmemory, sizeof(pmemory), 1) < 0)
        {
                char errbuf[1024];
                WARNING ("memory plugin: perfstat_memory_total failed: %s",
                        sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
-       memory_submit ("used",   pmemory.real_inuse * pagesize);
-       memory_submit ("free",   pmemory.real_free * pagesize);
-       memory_submit ("cached", pmemory.numperm * pagesize);
-       memory_submit ("system", pmemory.real_system * pagesize);
-       memory_submit ("user",   pmemory.real_process * pagesize);
+
+       /* Unfortunately, the AIX documentation is not very clear on how these
+        * numbers relate to one another. The only thing is states explcitly
+        * is:
+        *   real_total = real_process + real_free + numperm + real_system
+        *
+        * Another segmentation, which would be closer to the numbers reported
+        * by the "svmon" utility, would be:
+        *   real_total = real_free + real_inuse
+        *   real_inuse = "active" + real_pinned + numperm
+        */
+       MEMORY_SUBMIT ("free",   (gauge_t) (pmemory.real_free    * pagesize),
+                      "cached", (gauge_t) (pmemory.numperm      * pagesize),
+                      "system", (gauge_t) (pmemory.real_system  * pagesize),
+                      "user",   (gauge_t) (pmemory.real_process * pagesize));
 #endif /* HAVE_PERFSTAT */
 
        return (0);
-}
+} /* }}} int memory_read_internal */
+
+static int memory_read (void) /* {{{ */
+{
+       value_t v[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       vl.values = v;
+       vl.values_len = STATIC_ARRAY_SIZE (v);
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "memory", sizeof (vl.plugin));
+       sstrncpy (vl.type, "memory", sizeof (vl.type));
+       vl.time = cdtime ();
+
+       return (memory_read_internal (&vl));
+} /* }}} int memory_read */
 
 void module_register (void)
 {
+       plugin_register_complex_config ("memory", memory_config);
        plugin_register_init ("memory", memory_init);
        plugin_register_read ("memory", memory_read);
 } /* void module_register */
diff --git a/src/mic.c b/src/mic.c
new file mode 100644 (file)
index 0000000..570da51
--- /dev/null
+++ b/src/mic.c
@@ -0,0 +1,417 @@
+/**
+ * collectd - src/mic.c
+ * Copyright (C) 2013 Battelle Memorial Institute
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Evan Felix <evan.felix at pnnl.gov>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "utils_ignorelist.h"
+
+#include <MicAccessTypes.h>
+#include <MicAccessErrorTypes.h>
+#include <MicAccessApi.h>
+#include <MicThermalAPI.h>
+#include <MicPowerManagerAPI.h>
+
+#define MAX_MICS 32
+#define MAX_CORES 256
+
+static MicDeviceOnSystem mics[MAX_MICS];
+static U32 num_mics = 0;
+static HANDLE mic_handle = NULL;
+
+static int const therm_ids[] = {
+       eMicThermalDie, eMicThermalDevMem, eMicThermalFin, eMicThermalFout,
+       eMicThermalVccp, eMicThermalVddg, eMicThermalVddq };
+static char const * const therm_names[] = {
+       "die", "devmem", "fin", "fout",
+       "vccp", "vddg", "vddq" };
+
+static const char *config_keys[] =
+{
+       "ShowCPU",
+       "ShowCPUCores",
+       "ShowMemory",
+       "ShowTemperatures",
+       "Temperature",
+       "IgnoreSelectedTemperature",
+       "ShowPower",
+       "Power",
+       "IgnoreSelectedPower"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static _Bool show_cpu = 1;
+static _Bool show_cpu_cores = 1;
+static _Bool show_memory = 1;
+static _Bool show_temps = 1;
+static ignorelist_t *temp_ignore = NULL;
+static _Bool show_power = 1;
+static ignorelist_t *power_ignore = NULL;
+
+static int mic_init (void)
+{
+       U32 ret;
+       U32 mic_count;
+
+       if (mic_handle)
+               return (0);
+
+       mic_count = (U32) STATIC_ARRAY_SIZE(mics);
+       ret = MicInitAPI(&mic_handle,  eTARGET_SCIF_DRIVER, mics, &mic_count);
+       if (ret != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem initializing MicAccessAPI: %s",
+                               MicGetErrorString(ret));
+       }
+       DEBUG("mic plugin: found: %"PRIu32" MIC(s)",mic_count);
+
+       if (mic_count<0 || mic_count>=MAX_MICS) {
+               ERROR("mic plugin: No Intel MICs in system");
+               return (1);
+       }
+       else {
+               num_mics = mic_count;
+               return (0);
+       }
+}
+
+static int mic_config (const char *key, const char *value) {
+       if (temp_ignore == NULL)
+               temp_ignore = ignorelist_create(1);
+       if (power_ignore == NULL)
+               power_ignore = ignorelist_create(1);
+       if (temp_ignore == NULL || power_ignore == NULL)
+               return (1);
+
+       if (strcasecmp("ShowCPU",key) == 0)
+       {
+               show_cpu = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowCPUCores",key) == 0)
+       {
+               show_cpu_cores = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowTemperatures",key) == 0)
+       {
+               show_temps = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowMemory",key) == 0)
+       {
+               show_memory = IS_TRUE(value);
+       }
+       else if (strcasecmp("ShowPower",key) == 0)
+       {
+               show_power = IS_TRUE(value);
+       }
+       else if (strcasecmp("Temperature",key) == 0)
+       {
+               ignorelist_add(temp_ignore,value);
+       }
+       else if (strcasecmp("IgnoreSelectedTemperature",key) == 0)
+       {
+               int invert = 1;
+               if (IS_TRUE(value))
+                       invert = 0;
+               ignorelist_set_invert(temp_ignore,invert);
+       }
+       else if (strcasecmp("Power",key) == 0)
+       {
+               ignorelist_add(power_ignore,value);
+       }
+       else if (strcasecmp("IgnoreSelectedPower",key) == 0)
+       {
+               int invert = 1;
+               if (IS_TRUE(value))
+                       invert = 0;
+               ignorelist_set_invert(power_ignore,invert);
+       }
+       else
+       {
+               return (-1);
+       }
+       return (0);
+}
+
+static void mic_submit_memory_use(int micnumber, const char *type_instance, U32 val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       /* MicAccessAPI reports KB's of memory, adjust for this */
+       DEBUG("mic plugin: Memory Value Report; %u %lf",val,((gauge_t)val)*1024.0);
+       values[0].gauge = ((gauge_t)val)*1024.0;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), "%i", micnumber);
+       strncpy (vl.type, "memory", sizeof (vl.type));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/* Gather memory Utilization */
+static int mic_read_memory(int mic)
+{
+       U32 ret;
+       U32 mem_total,mem_free,mem_bufs;
+
+       ret = MicGetMemoryUtilization(mic_handle,&mem_total,&mem_free,&mem_bufs);
+       if (ret != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem getting Memory Utilization: %s",
+                               MicGetErrorString(ret));
+               return (1);
+       }
+       mic_submit_memory_use(mic,"free",mem_free);
+       mic_submit_memory_use(mic,"used",mem_total-mem_free-mem_bufs);
+       mic_submit_memory_use(mic,"buffered",mem_bufs);
+       DEBUG("mic plugin: Memory Read: %u %u %u",mem_total,mem_free,mem_bufs);
+       return (0);
+}
+
+static void mic_submit_temp(int micnumber, const char *type, gauge_t val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = val;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+                       "%i", micnumber);
+       strncpy (vl.type, "temperature", sizeof (vl.type));
+       strncpy (vl.type_instance, type, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/* Gather Temperature Information */
+static int mic_read_temps(int mic)
+{
+       size_t num_therms = STATIC_ARRAY_SIZE(therm_ids);
+       size_t j;
+
+       for (j = 0; j < num_therms; j++) {
+               U32 status;
+               U32 temp_buffer;
+               U32 buffer_size = (U32)sizeof(temp_buffer);
+               char const *name = therm_names[j];
+
+               if (ignorelist_match(temp_ignore, name) != 0)
+                       continue;
+
+               status = MicGetTemperature(mic_handle, therm_ids[j],
+                               &temp_buffer, &buffer_size);
+               if (status != MIC_ACCESS_API_SUCCESS) {
+                       ERROR("mic plugin: Error reading temperature \"%s\": "
+                                       "%s", name, MicGetErrorString(status));
+                       return (1);
+               }
+               mic_submit_temp(mic, name, temp_buffer);
+       }
+       return (0);
+}
+
+static void mic_submit_cpu(int micnumber, const char *type_instance,
+               int core, derive_t val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].derive = val;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       if (core < 0) /* global aggregation */
+               ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+                               "%i", micnumber);
+       else /* per-core statistics */
+               ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+                               "%i-cpu-%i", micnumber, core);
+       strncpy (vl.type, "cpu", sizeof (vl.type));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/*Gather CPU Utilization Information */
+static int mic_read_cpu(int mic)
+{
+       MicCoreUtil core_util;
+       MicCoreJiff core_jiffs[MAX_CORES];
+       U32 core_jiffs_size;
+       U32 status;
+
+       core_jiffs_size = MAX_CORES * sizeof(MicCoreJiff);
+       status = MicGetCoreUtilization(mic_handle, &core_util,
+                       core_jiffs, &core_jiffs_size);
+       if (status != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem getting CPU utilization: %s",
+                               MicGetErrorString(status));
+               return(-1);
+       }
+
+       if (show_cpu) {
+               mic_submit_cpu(mic, "user", -1, core_util.sum.user);
+               mic_submit_cpu(mic, "sys", -1, core_util.sum.sys);
+               mic_submit_cpu(mic, "nice", -1, core_util.sum.nice);
+               mic_submit_cpu(mic, "idle", -1, core_util.sum.idle);
+       }
+
+       if (show_cpu_cores) {
+               int j;
+               for (j = 0; j < core_util.core; j++) {
+                       mic_submit_cpu(mic, "user", j, core_jiffs[j].user);
+                       mic_submit_cpu(mic, "sys", j, core_jiffs[j].sys);
+                       mic_submit_cpu(mic, "nice", j, core_jiffs[j].nice);
+                       mic_submit_cpu(mic, "idle", j, core_jiffs[j].idle);
+               }
+       }
+       return (0);
+}
+
+static void mic_submit_power(int micnumber, const char *type, const char *type_instance, gauge_t val)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = val;
+
+       vl.values=values;
+       vl.values_len=1;
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "mic", sizeof (vl.plugin));
+       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), "%i", micnumber);
+       strncpy (vl.type, type, sizeof (vl.type));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+/* Gather Power Information */
+static int mic_read_power(int mic)
+{
+       U32 ret;
+       MicPwrUsage power_use;
+
+       ret = MicGetPowerUsage(mic_handle,&power_use);
+       if (ret != MIC_ACCESS_API_SUCCESS) {
+               ERROR("mic plugin: Problem getting Power Usage: %s",
+                       MicGetErrorString(ret));
+               return (1);
+       }
+
+       /* power is in uWatts, current in mA, voltage in uVolts..   convert to
+        * base unit */
+       #define SUB_POWER(name) do { if (ignorelist_match(power_ignore,#name)==0) \
+               mic_submit_power(mic,"power",#name,(gauge_t)power_use.name.prr*0.000001); \
+       } while(0)
+       #define SUB_VOLTS(name) do { if (ignorelist_match(power_ignore,#name)==0) {\
+               mic_submit_power(mic,"power",#name,(gauge_t)(power_use.name.pwr*0.000001)); \
+               mic_submit_power(mic,"current",#name,(gauge_t)(power_use.name.cur*0.001)); \
+               mic_submit_power(mic,"voltage",#name,(gauge_t)(power_use.name.volt*0.000001)); \
+       }} while(0)
+
+       SUB_POWER(total0);
+       SUB_POWER(total1);
+       SUB_POWER(inst);
+       SUB_POWER(imax);
+       SUB_POWER(pcie);
+       SUB_POWER(c2x3);
+       SUB_POWER(c2x4);
+       SUB_VOLTS(vccp);
+       SUB_VOLTS(vddg);
+       SUB_VOLTS(vddq);
+
+       return (0);
+}
+
+static int mic_read (void)
+{
+       int i;
+       U32 ret;
+       int error;
+
+       error=0;
+       for (i=0;i<num_mics;i++) {
+               ret = MicInitAdapter(&mic_handle,&mics[i]);
+               if (ret != MIC_ACCESS_API_SUCCESS) {
+                       ERROR("mic plugin: Problem initializing MicAdapter: %s",
+                                       MicGetErrorString(ret));
+                       error=1;
+               }
+
+               if (error == 0 && show_memory)
+                       error = mic_read_memory(i);
+
+               if (error == 0 && show_temps)
+                       error = mic_read_temps(i);
+
+               if (error == 0 && (show_cpu || show_cpu_cores))
+                       error = mic_read_cpu(i);
+
+               if (error == 0 && (show_power))
+                       error = mic_read_power(i);
+
+               ret = MicCloseAdapter(mic_handle);
+               if (ret != MIC_ACCESS_API_SUCCESS) {
+                       ERROR("mic plugin: Problem closing MicAdapter: %s",
+                                       MicGetErrorString(ret));
+                       error=2;
+                       break;
+               }
+       }
+       if (num_mics==0)
+               error=3;
+       return error;
+}
+
+
+static int mic_shutdown (void)
+{
+       if (mic_handle)
+               MicCloseAPI(&mic_handle);
+       mic_handle = NULL;
+
+       return (0);
+}
+
+void module_register (void)
+{
+       plugin_register_init ("mic", mic_init);
+       plugin_register_shutdown ("mic", mic_shutdown);
+       plugin_register_read ("mic", mic_read);
+       plugin_register_config ("mic",mic_config, config_keys, config_keys_num);
+} /* void module_register */
+
+/*
+ * vim: set shiftwidth=8 softtabstop=8 noet textwidth=78 :
+ */
index 8a9fe93..887c63c 100644 (file)
@@ -115,7 +115,6 @@ struct mb_host_s /* {{{ */
   modbus_t *connection;
 #endif
   _Bool is_connected;
-  _Bool have_reconnected;
 }; /* }}} */
 typedef struct mb_host_s mb_host_t;
 
@@ -260,6 +259,7 @@ static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */
   union
   {
     uint8_t b[4];
+    uint16_t s[2];
     float f;
   } conv;
 
@@ -288,13 +288,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   if (host == NULL)
     return (EINVAL);
 
-  if (host->is_connected)
-    return (0);
-
-  /* Only reconnect once per interval. */
-  if (host->have_reconnected)
-    return (-1);
-
   modbus_set_debug (&host->connection, 1);
 
   /* We'll do the error handling ourselves. */
@@ -319,7 +312,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   }
 
   host->is_connected = 1;
-  host->have_reconnected = 1;
   return (0);
 } /* }}} int mb_init_connection */
 /* #endif LEGACY_LIBMODBUS */
@@ -336,10 +328,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   if (host->connection != NULL)
     return (0);
 
-  /* Only reconnect once per interval. */
-  if (host->have_reconnected)
-    return (-1);
-
   if ((host->port < 1) || (host->port > 65535))
     host->port = MODBUS_TCP_DEFAULT_PORT;
 
@@ -349,7 +337,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
   host->connection = modbus_new_tcp (host->node, host->port);
   if (host->connection == NULL)
   {
-    host->have_reconnected = 1;
     ERROR ("Modbus plugin: Creating new Modbus/TCP object failed.");
     return (-1);
   }
@@ -369,7 +356,6 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */
     return (status);
   }
 
-  host->have_reconnected = 1;
   return (0);
 } /* }}} int mb_init_connection */
 #endif /* !LEGACY_LIBMODBUS */
@@ -392,7 +378,6 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   int values_num;
   const data_set_t *ds;
   int status;
-  int i;
 
   if ((host == NULL) || (slave == NULL) || (data == NULL))
     return (EINVAL);
@@ -429,6 +414,44 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   else
     values_num = 1;
 
+  status = 0;
+  if (host->connection == NULL)
+  {
+    status = EBADF;
+  }
+  else
+  {
+    struct sockaddr sockaddr;
+    socklen_t saddrlen = sizeof (sockaddr);
+
+    status = getpeername (modbus_get_socket (host->connection),
+        &sockaddr, &saddrlen);
+    if (status != 0)
+      status = errno;
+  }
+
+  if ((status == EBADF) || (status == ENOTSOCK) || (status == ENOTCONN))
+  {
+    status = mb_init_connection (host);
+    if (status != 0)
+    {
+      ERROR ("Modbus plugin: mb_init_connection (%s/%s) failed. ",
+          host->host, host->node);
+      host->is_connected = 0;
+      host->connection = NULL;
+      return (-1);
+    }
+  }
+  else if (status != 0)
+  {
+#if LEGACY_LIBMODBUS
+    modbus_close (&host->connection);
+#else
+    modbus_close (host->connection);
+    modbus_free (host->connection);
+#endif
+  }
 #if LEGACY_LIBMODBUS
   /* Version 2.0.3: Pass the connection struct as a pointer and pass the slave
    * id to each call of "read_holding_registers". */
@@ -445,51 +468,22 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 #endif
 
-  for (i = 0; i < 2; i++)
-  {
-    status = modbus_read_registers (host->connection,
+  status = modbus_read_registers (host->connection,
         /* start_addr = */ data->register_base,
         /* num_registers = */ values_num, /* buffer = */ values);
-    if (status > 0)
-      break;
-
-    if (host->is_connected)
-    {
+  if (status != values_num)
+  {
+    ERROR ("Modbus plugin: modbus_read_registers (%s/%s) failed. status = %i, values_num = %i "
+        "Giving up.", host->host, host->node, status, values_num);
 #if LEGACY_LIBMODBUS
-      modbus_close (&host->connection);
-      host->is_connected = 0;
+    modbus_close (&host->connection);
 #else
-      modbus_close (host->connection);
-      modbus_free (host->connection);
-      host->connection = NULL;
+    modbus_close (host->connection);
+    modbus_free (host->connection);
 #endif
-    }
-
-    /* If we already tried reconnecting this round, give up. */
-    if (host->have_reconnected)
-    {
-      ERROR ("Modbus plugin: modbus_read_registers (%s) failed. "
-          "Reconnecting has already been tried. Giving up.", host->host);
-      return (-1);
-    }
-
-    /* Maybe the device closed the connection during the waiting interval.
-     * Try re-establishing the connection. */
-    status = mb_init_connection (host);
-    if (status != 0)
-    {
-      ERROR ("Modbus plugin: modbus_read_registers (%s) failed. "
-          "While trying to reconnect, connecting to \"%s\" failed. "
-          "Giving up.",
-          host->host, host->node);
-      return (-1);
-    }
-
-    DEBUG ("Modbus plugin: Re-established connection to %s", host->host);
-
-    /* try again */
-    continue;
-  } /* for (i = 0, 1) */
+    host->connection = NULL;
+    return (-1);
+  }
 
   DEBUG ("Modbus plugin: mb_read_data: Success! "
       "modbus_read_registers returned with status %i.", status);
@@ -602,9 +596,6 @@ static int mb_read (user_data_t *user_data) /* {{{ */
 
   host = user_data->data;
 
-  /* Clear the reconnect flag. */
-  host->have_reconnected = 0;
-
   success = 0;
   for (i = 0; i < host->slaves_num; i++)
   {
index 32b352b..6337ab4 100644 (file)
@@ -58,7 +58,7 @@ struct mysql_database_s /* {{{ */
        _Bool slave_sql_running;
 
        MYSQL *con;
-       int    state;
+       _Bool  is_connected;
 };
 typedef struct mysql_database_s mysql_database_t; /* }}} */
 
@@ -237,30 +237,28 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */
 
 static MYSQL *getconnection (mysql_database_t *db)
 {
-       if (db->state != 0)
+       if (db->is_connected)
        {
-               int err;
-               if ((err = mysql_ping (db->con)) != 0)
-               {
-                       /* Assured by "mysql_config_database" */
-                       assert (db->instance != NULL);
-                       WARNING ("mysql_ping failed for instance \"%s\": %s",
-                                       db->instance,
-                                       mysql_error (db->con));
-                       db->state = 0;
-               }
-               else
-               {
-                       db->state = 1;
+               int status;
+
+               status = mysql_ping (db->con);
+               if (status == 0)
                        return (db->con);
-               }
+
+               WARNING ("mysql plugin: Lost connection to instance \"%s\": %s",
+                               db->instance, mysql_error (db->con));
        }
+       db->is_connected = 0;
 
-       if ((db->con = mysql_init (db->con)) == NULL)
+       if (db->con == NULL)
        {
-               ERROR ("mysql_init failed: %s", mysql_error (db->con));
-               db->state = 0;
-               return (NULL);
+               db->con = mysql_init (NULL);
+               if (db->con == NULL)
+               {
+                       ERROR ("mysql plugin: mysql_init failed: %s",
+                                       mysql_error (db->con));
+                       return (NULL);
+               }
        }
 
        if (mysql_real_connect (db->con, db->host, db->user, db->pass,
@@ -271,26 +269,25 @@ static MYSQL *getconnection (mysql_database_t *db)
                                (db->database != NULL) ? db->database : "<none>",
                                (db->host != NULL) ? db->host : "localhost",
                                mysql_error (db->con));
-               db->state = 0;
                return (NULL);
        }
-       else
-       {
-               INFO ("mysql plugin: Successfully connected to database %s "
-                               "at server %s (server version: %s, protocol version: %d)",
-                               (db->database != NULL) ? db->database : "<none>",
-                               mysql_get_host_info (db->con),
-                               mysql_get_server_info (db->con),
-                               mysql_get_proto_info (db->con));
-               db->state = 1;
-               return (db->con);
-       }
+
+       INFO ("mysql plugin: Successfully connected to database %s "
+                       "at server %s (server version: %s, protocol version: %d)",
+                       (db->database != NULL) ? db->database : "<none>",
+                       mysql_get_host_info (db->con),
+                       mysql_get_server_info (db->con),
+                       mysql_get_proto_info (db->con));
+
+       db->is_connected = 1;
+       return (db->con);
 } /* static MYSQL *getconnection (mysql_database_t *db) */
 
 static void set_host (mysql_database_t *db, char *buf, size_t buflen)
 {
        if ((db->host == NULL)
                        || (strcmp ("", db->host) == 0)
+                       || (strcmp ("127.0.0.1", db->host) == 0)
                        || (strcmp ("localhost", db->host) == 0))
                sstrncpy (buf, hostname_g, buflen);
        else
@@ -403,6 +400,7 @@ static int mysql_read_master_stats (mysql_database_t *db, MYSQL *con)
        {
                ERROR ("mysql plugin: Failed to get master statistics: "
                                "`%s' did not return any rows.", query);
+               mysql_free_result (res);
                return (-1);
        }
 
@@ -411,6 +409,7 @@ static int mysql_read_master_stats (mysql_database_t *db, MYSQL *con)
        {
                ERROR ("mysql plugin: Failed to get master statistics: "
                                "`%s' returned less than two columns.", query);
+               mysql_free_result (res);
                return (-1);
        }
 
@@ -454,6 +453,7 @@ static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con)
        {
                ERROR ("mysql plugin: Failed to get slave statistics: "
                                "`%s' did not return any rows.", query);
+               mysql_free_result (res);
                return (-1);
        }
 
@@ -462,6 +462,7 @@ static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con)
        {
                ERROR ("mysql plugin: Failed to get slave statistics: "
                                "`%s' returned less than 33 columns.", query);
+               mysql_free_result (res);
                return (-1);
        }
 
@@ -534,7 +535,7 @@ static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con)
                        ssnprintf (n.message, sizeof (n.message),
                                        "slave SQL thread started");
                        plugin_dispatch_notification (&n);
-                       db->slave_sql_running = 0;
+                       db->slave_sql_running = 1;
                }
        }
 
index f7bc04d..f446d59 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/netapp.c
  * Copyright (C) 2009,2010  Sven Trenkel
+ * Copyright (C) 2012-2013  teamix GmbH
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -21,7 +22,8 @@
  * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
- *   Sven Trenkel <collectd at semidefinite.de>  
+ *   Sven Trenkel <collectd at semidefinite.de>
+ *   Sebastian 'tokkee' Harl <sh@teamix.net>
  **/
 
 #include "collectd.h"
@@ -181,16 +183,18 @@ typedef struct {
  *
  * \brief Configuration struct for volume usage data (free / used).
  */
-#define CFG_VOLUME_USAGE_DF             0x0002
-#define CFG_VOLUME_USAGE_SNAP           0x0004
-#define CFG_VOLUME_USAGE_ALL            0x0006
-#define HAVE_VOLUME_USAGE_NORM_FREE     0x0010
-#define HAVE_VOLUME_USAGE_NORM_USED     0x0020
-#define HAVE_VOLUME_USAGE_SNAP_RSVD     0x0040
-#define HAVE_VOLUME_USAGE_SNAP_USED     0x0080
-#define HAVE_VOLUME_USAGE_SIS_SAVED     0x0100
-#define HAVE_VOLUME_USAGE_ALL           0x01f0
-#define IS_VOLUME_USAGE_OFFLINE         0x0200
+#define CFG_VOLUME_USAGE_DF              0x0002
+#define CFG_VOLUME_USAGE_SNAP            0x0004
+#define CFG_VOLUME_USAGE_ALL             0x0006
+#define HAVE_VOLUME_USAGE_NORM_FREE      0x0010
+#define HAVE_VOLUME_USAGE_NORM_USED      0x0020
+#define HAVE_VOLUME_USAGE_SNAP_RSVD      0x0040
+#define HAVE_VOLUME_USAGE_SNAP_USED      0x0080
+#define HAVE_VOLUME_USAGE_SIS_SAVED      0x0100
+#define HAVE_VOLUME_USAGE_COMPRESS_SAVED 0x0200
+#define HAVE_VOLUME_USAGE_DEDUP_SAVED    0x0400
+#define HAVE_VOLUME_USAGE_ALL            0x07f0
+#define IS_VOLUME_USAGE_OFFLINE          0x0800
 struct data_volume_usage_s;
 typedef struct data_volume_usage_s data_volume_usage_t;
 struct data_volume_usage_s {
@@ -204,6 +208,8 @@ struct data_volume_usage_s {
        uint64_t snap_reserved;
        uint64_t snap_used;
        uint64_t sis_saved;
+       uint64_t compress_saved;
+       uint64_t dedup_saved;
 
        data_volume_usage_t *next;
 };
@@ -219,6 +225,26 @@ typedef struct {
 } cfg_volume_usage_t;
 /* }}} cfg_volume_usage_t */
 
+/*! Data types for quota statistics {{{
+ *
+ * \brief Persistent data for quota statistics
+ */
+typedef struct {
+       cna_interval_t interval;
+       na_elem_t *query;
+} cfg_quota_t;
+/* }}} cfg_quota_t */
+
+/*! Data types for SnapVault statistics {{{
+ *
+ * \brief Persistent data for SnapVault(R) statistics
+ */
+typedef struct {
+       cna_interval_t interval;
+       na_elem_t *query;
+} cfg_snapvault_t;
+/* }}} cfg_snapvault_t */
+
 /*! Data types for system statistics {{{
  *
  * \brief Persistent data for system performance counters
@@ -242,6 +268,7 @@ struct host_config_s {
        int port;
        char *username;
        char *password;
+       char *vfiler;
        cdtime_t interval;
 
        na_server_t *srv;
@@ -249,6 +276,8 @@ struct host_config_s {
        cfg_disk_t *cfg_disk;
        cfg_volume_perf_t *cfg_volume_perf;
        cfg_volume_usage_t *cfg_volume_usage;
+       cfg_quota_t *cfg_quota;
+       cfg_snapvault_t *cfg_snapvault;
        cfg_system_t *cfg_system;
 
        struct host_config_s *next;
@@ -354,6 +383,28 @@ static void free_cfg_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */
        sfree (cvu);
 } /* }}} void free_cfg_volume_usage */
 
+static void free_cfg_quota (cfg_quota_t *q) /* {{{ */
+{
+       if (q == NULL)
+               return;
+
+       if (q->query != NULL)
+               na_elem_free (q->query);
+
+       sfree (q);
+} /* }}} void free_cfg_quota */
+
+static void free_cfg_snapvault (cfg_snapvault_t *sv) /* {{{ */
+{
+       if (sv == NULL)
+               return;
+
+       if (sv->query != NULL)
+               na_elem_free (sv->query);
+
+       sfree (sv);
+} /* }}} void free_cfg_snapvault */
+
 static void free_cfg_system (cfg_system_t *cs) /* {{{ */
 {
        if (cs == NULL)
@@ -378,11 +429,14 @@ static void free_host_config (host_config_t *hc) /* {{{ */
        sfree (hc->host);
        sfree (hc->username);
        sfree (hc->password);
+       sfree (hc->vfiler);
 
        free_cfg_disk (hc->cfg_disk);
        free_cfg_wafl (hc->cfg_wafl);
        free_cfg_volume_perf (hc->cfg_volume_perf);
        free_cfg_volume_usage (hc->cfg_volume_usage);
+       free_cfg_quota (hc->cfg_quota);
+       free_cfg_snapvault (hc->cfg_snapvault);
        free_cfg_system (hc->cfg_system);
 
        if (hc->srv != NULL)
@@ -675,7 +729,7 @@ static int submit_cache_ratio (const char *host, /* {{{ */
 
 /* Submits all the caches used by WAFL. Uses "submit_cache_ratio". */
 static int submit_wafl_data (const char *hostname, const char *instance, /* {{{ */
-               cfg_wafl_t *old_data, const cfg_wafl_t *new_data, int interval)
+               cfg_wafl_t *old_data, const cfg_wafl_t *new_data, cdtime_t interval)
 {
        /* Submit requested counters */
        if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_NAME_CACHE | HAVE_WAFL_NAME_CACHE)
@@ -836,7 +890,7 @@ static cdtime_t cna_child_get_cdtime (na_elem_t *data) /* {{{ */
  */
 /* Data corresponding to <WAFL /> */
 static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /* {{{ */
-               na_elem_t *data, int interval)
+               na_elem_t *data, cdtime_t interval)
 {
        cfg_wafl_t perf_data;
        const char *plugin_inst;
@@ -989,7 +1043,8 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */
                return (-1);
        }
 
-       status = cna_handle_wafl_data (host->name, host->cfg_wafl, data, host->interval);
+       status = cna_handle_wafl_data (host->name, host->cfg_wafl, data,
+                       host->cfg_wafl->interval.interval);
 
        if (status == 0)
                host->cfg_wafl->interval.last_read = now;
@@ -1184,7 +1239,8 @@ static int cna_query_disk (host_config_t *host) /* {{{ */
                return (-1);
        }
 
-       status = cna_handle_disk_data (host->name, host->cfg_disk, data, host->interval);
+       status = cna_handle_disk_data (host->name, host->cfg_disk, data,
+                       host->cfg_disk->interval.interval);
 
        if (status == 0)
                host->cfg_disk->interval.last_read = now;
@@ -1355,7 +1411,8 @@ static int cna_query_volume_perf (host_config_t *host) /* {{{ */
                return (-1);
        }
 
-       status = cna_handle_volume_perf_data (host->name, host->cfg_volume_perf, data, host->interval);
+       status = cna_handle_volume_perf_data (host->name, host->cfg_volume_perf, data,
+                       host->cfg_volume_perf->interval.interval);
 
        if (status == 0)
                host->cfg_volume_perf->interval.last_read = now;
@@ -1377,6 +1434,8 @@ static int cna_submit_volume_usage_data (const char *hostname, /* {{{ */
                uint64_t norm_used = v->norm_used;
                uint64_t norm_free = v->norm_free;
                uint64_t sis_saved = v->sis_saved;
+               uint64_t compress_saved = v->compress_saved;
+               uint64_t dedup_saved = v->dedup_saved;
                uint64_t snap_reserve_used = 0;
                uint64_t snap_reserve_free = v->snap_reserved;
                uint64_t snap_norm_used = v->snap_used;
@@ -1421,6 +1480,16 @@ static int cna_submit_volume_usage_data (const char *hostname, /* {{{ */
                                        "df_complex", "sis_saved",
                                        (double) sis_saved, /* timestamp = */ 0, interval);
 
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_COMPRESS_SAVED))
+                       submit_double (hostname, /* plugin instance = */ plugin_instance,
+                                       "df_complex", "compression_saved",
+                                       (double) compress_saved, /* timestamp = */ 0, interval);
+
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_DEDUP_SAVED))
+                       submit_double (hostname, /* plugin instance = */ plugin_instance,
+                                       "df_complex", "dedup_saved",
+                                       (double) dedup_saved, /* timestamp = */ 0, interval);
+
                if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_USED))
                        submit_double (hostname, /* plugin instance = */ plugin_instance,
                                        "df_complex", "used",
@@ -1529,6 +1598,101 @@ static void cna_handle_volume_snap_usage(const host_config_t *host, /* {{{ */
        v->flags |= HAVE_VOLUME_USAGE_SNAP_USED;
 } /* }}} void cna_handle_volume_snap_usage */
 
+static void cna_handle_volume_sis_data (const host_config_t *host, /* {{{ */
+               data_volume_usage_t *v, na_elem_t *sis)
+{
+       const char *sis_state;
+       uint64_t sis_saved_reported;
+
+       if (na_elem_child(sis, "sis-info"))
+               sis = na_elem_child(sis, "sis-info");
+
+       sis_state = na_child_get_string(sis, "state");
+       if (sis_state == NULL)
+               return;
+
+       /* If SIS is not enabled, there's nothing left to do for this volume. */
+       if (strcmp ("enabled", sis_state) != 0)
+               return;
+
+       sis_saved_reported = na_child_get_uint64(sis, "size-saved", UINT64_MAX);
+       if (sis_saved_reported == UINT64_MAX)
+               return;
+
+       /* size-saved is actually a 32 bit number, so ... time for some guesswork. */
+       if ((sis_saved_reported >> 32) != 0) {
+               /* In case they ever fix this bug. */
+               v->sis_saved = sis_saved_reported;
+               v->flags |= HAVE_VOLUME_USAGE_SIS_SAVED;
+       } else { /* really hacky work-around code. {{{ */
+               uint64_t sis_saved_percent;
+               uint64_t sis_saved_guess;
+               uint64_t overflow_guess;
+               uint64_t guess1, guess2, guess3;
+
+               /* Check if we have v->norm_used. Without it, we cannot calculate
+                * sis_saved_guess. */
+               if ((v->flags & HAVE_VOLUME_USAGE_NORM_USED) == 0)
+                       return;
+
+               sis_saved_percent = na_child_get_uint64(sis, "percentage-saved", UINT64_MAX);
+               if (sis_saved_percent > 100)
+                       return;
+
+               /* The "size-saved" value is a 32bit unsigned integer. This is a bug and
+                * will hopefully be fixed in later versions. To work around the bug, try
+                * to figure out how often the 32bit integer wrapped around by using the
+                * "percentage-saved" value. Because the percentage is in the range
+                * [0-100], this should work as long as the saved space does not exceed
+                * 400 GBytes. */
+               /* percentage-saved = size-saved / (size-saved + size-used) */
+               if (sis_saved_percent < 100)
+                       sis_saved_guess = v->norm_used * sis_saved_percent / (100 - sis_saved_percent);
+               else
+                       sis_saved_guess = v->norm_used;
+
+               overflow_guess = sis_saved_guess >> 32;
+               guess1 = overflow_guess ? ((overflow_guess - 1) << 32) + sis_saved_reported : sis_saved_reported;
+               guess2 = (overflow_guess << 32) + sis_saved_reported;
+               guess3 = ((overflow_guess + 1) << 32) + sis_saved_reported;
+
+               if (sis_saved_guess < guess2) {
+                       if ((sis_saved_guess - guess1) < (guess2 - sis_saved_guess))
+                               v->sis_saved = guess1;
+                       else
+                               v->sis_saved = guess2;
+               } else {
+                       if ((sis_saved_guess - guess2) < (guess3 - sis_saved_guess))
+                               v->sis_saved = guess2;
+                       else
+                               v->sis_saved = guess3;
+               }
+               v->flags |= HAVE_VOLUME_USAGE_SIS_SAVED;
+       } /* }}} end of 32-bit workaround */
+} /* }}} void cna_handle_volume_sis_data */
+
+/* ONTAP >= 8.1 uses SIS for managing dedup and compression */
+static void cna_handle_volume_sis_saved (const host_config_t *host, /* {{{ */
+               data_volume_usage_t *v, na_elem_t *sis)
+{
+       uint64_t saved;
+
+       if (na_elem_child(sis, "sis-info"))
+               sis = na_elem_child(sis, "sis-info");
+
+       saved = na_child_get_uint64(sis, "compress-saved", UINT64_MAX);
+       if (saved != UINT64_MAX) {
+               v->compress_saved = saved;
+               v->flags |= HAVE_VOLUME_USAGE_COMPRESS_SAVED;
+       }
+
+       saved = na_child_get_uint64(sis, "dedup-saved", UINT64_MAX);
+       if (saved != UINT64_MAX) {
+               v->dedup_saved = saved;
+               v->flags |= HAVE_VOLUME_USAGE_DEDUP_SAVED;
+       }
+} /* }}} void cna_handle_volume_sis_saved */
+
 static int cna_handle_volume_usage_data (const host_config_t *host, /* {{{ */
                cfg_volume_usage_t *cfg_volume, na_elem_t *data)
 {
@@ -1556,8 +1720,6 @@ static int cna_handle_volume_usage_data (const host_config_t *host, /* {{{ */
                uint64_t value;
 
                na_elem_t *sis;
-               const char *sis_state;
-               uint64_t sis_saved_reported;
 
                volume_name = na_child_get_string (elem_volume, "name");
                if (volume_name == NULL)
@@ -1599,77 +1761,14 @@ static int cna_handle_volume_usage_data (const host_config_t *host, /* {{{ */
                }
 
                sis = na_elem_child(elem_volume, "sis");
-               if (sis == NULL)
-                       continue;
-
-               if (na_elem_child(sis, "sis-info"))
-                       sis = na_elem_child(sis, "sis-info");
-               
-               sis_state = na_child_get_string(sis, "state");
-               if (sis_state == NULL)
-                       continue;
-
-               /* If SIS is not enabled, there's nothing left to do for this volume. */
-               if (strcmp ("enabled", sis_state) != 0)
-                       continue;
-
-               sis_saved_reported = na_child_get_uint64(sis, "size-saved", UINT64_MAX);
-               if (sis_saved_reported == UINT64_MAX)
-                       continue;
-
-               /* size-saved is actually a 32 bit number, so ... time for some guesswork. */
-               if ((sis_saved_reported >> 32) != 0) {
-                       /* In case they ever fix this bug. */
-                       v->sis_saved = sis_saved_reported;
-                       v->flags |= HAVE_VOLUME_USAGE_SIS_SAVED;
-               } else { /* really hacky work-around code. {{{ */
-                       uint64_t sis_saved_percent;
-                       uint64_t sis_saved_guess;
-                       uint64_t overflow_guess;
-                       uint64_t guess1, guess2, guess3;
-
-                       /* Check if we have v->norm_used. Without it, we cannot calculate
-                        * sis_saved_guess. */
-                       if ((v->flags & HAVE_VOLUME_USAGE_NORM_USED) == 0)
-                               continue;
-
-                       sis_saved_percent = na_child_get_uint64(sis, "percentage-saved", UINT64_MAX);
-                       if (sis_saved_percent > 100)
-                               continue;
-
-                       /* The "size-saved" value is a 32bit unsigned integer. This is a bug and
-                        * will hopefully be fixed in later versions. To work around the bug, try
-                        * to figure out how often the 32bit integer wrapped around by using the
-                        * "percentage-saved" value. Because the percentage is in the range
-                        * [0-100], this should work as long as the saved space does not exceed
-                        * 400 GBytes. */
-                       /* percentage-saved = size-saved / (size-saved + size-used) */
-                       if (sis_saved_percent < 100)
-                               sis_saved_guess = v->norm_used * sis_saved_percent / (100 - sis_saved_percent);
-                       else
-                               sis_saved_guess = v->norm_used;
-
-                       overflow_guess = sis_saved_guess >> 32;
-                       guess1 = overflow_guess ? ((overflow_guess - 1) << 32) + sis_saved_reported : sis_saved_reported;
-                       guess2 = (overflow_guess << 32) + sis_saved_reported;
-                       guess3 = ((overflow_guess + 1) << 32) + sis_saved_reported;
-
-                       if (sis_saved_guess < guess2) {
-                               if ((sis_saved_guess - guess1) < (guess2 - sis_saved_guess))
-                                       v->sis_saved = guess1;
-                               else
-                                       v->sis_saved = guess2;
-                       } else {
-                               if ((sis_saved_guess - guess2) < (guess3 - sis_saved_guess))
-                                       v->sis_saved = guess2;
-                               else
-                                       v->sis_saved = guess3;
-                       }
-                       v->flags |= HAVE_VOLUME_USAGE_SIS_SAVED;
-               } /* }}} end of 32-bit workaround */
+               if (sis != NULL) {
+                       cna_handle_volume_sis_data (host, v, sis);
+                       cna_handle_volume_sis_saved (host, v, sis);
+               }
        } /* for (elem_volume) */
 
-       return (cna_submit_volume_usage_data (host->name, cfg_volume, host->interval));
+       return (cna_submit_volume_usage_data (host->name, cfg_volume,
+                               host->cfg_volume_usage->interval.interval));
 } /* }}} int cna_handle_volume_usage_data */
 
 static int cna_setup_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */
@@ -1731,6 +1830,292 @@ static int cna_query_volume_usage (host_config_t *host) /* {{{ */
        return (status);
 } /* }}} int cna_query_volume_usage */
 
+/* Data corresponding to <Quota /> */
+static int cna_handle_quota_data (const host_config_t *host, /* {{{ */
+               cfg_quota_t *cfg_quota, na_elem_t *data)
+{
+       na_elem_t *elem_quota;
+       na_elem_t *elem_quotas;
+       na_elem_iter_t iter_quota;
+
+       elem_quotas = na_elem_child (data, "quotas");
+       if (elem_quotas == NULL)
+       {
+               ERROR ("netapp plugin: cna_handle_quota_data: "
+                               "na_elem_child (\"quotas\") failed "
+                               "for host %s.", host->name);
+               return (-1);
+       }
+
+       iter_quota = na_child_iterator (elem_quotas);
+       for (elem_quota = na_iterator_next (&iter_quota);
+                       elem_quota != NULL;
+                       elem_quota = na_iterator_next (&iter_quota))
+       {
+               const char *quota_type, *volume_name, *tree_name;
+               uint64_t value;
+
+               char plugin_instance[DATA_MAX_NAME_LEN];
+
+               quota_type = na_child_get_string (elem_quota, "quota-type");
+               if (quota_type == NULL)
+                       continue;
+
+               /* possible TODO: support other types as well */
+               if (strcmp (quota_type, "tree") != 0)
+                       continue;
+
+               tree_name = na_child_get_string (elem_quota, "tree");
+               if ((tree_name == NULL) || (*tree_name == '\0'))
+                       continue;
+
+               volume_name = na_child_get_string (elem_quota, "volume");
+               if (volume_name == NULL)
+                       continue;
+
+               ssnprintf (plugin_instance, sizeof (plugin_instance),
+                               "quota-%s-%s", volume_name, tree_name);
+
+               value = na_child_get_uint64 (elem_quota, "disk-used", UINT64_MAX);
+               if (value != UINT64_MAX) {
+                       value *= 1024; /* disk-used reports kilobytes */
+                       submit_double (host->name, plugin_instance,
+                                       /* type = */ "df_complex", /* type instance = */ NULL,
+                                       (double)value, /* timestamp = */ 0,
+                                       host->cfg_quota->interval.interval);
+               }
+
+               value = na_child_get_uint64 (elem_quota, "files-used", UINT64_MAX);
+               if (value != UINT64_MAX) {
+                       submit_double (host->name, plugin_instance,
+                                       /* type = */ "files", /* type instance = */ NULL,
+                                       (double)value, /* timestamp = */ 0,
+                                       host->cfg_quota->interval.interval);
+               }
+       } /* for (elem_quota) */
+
+       return (0);
+} /* }}} int cna_handle_volume_usage_data */
+
+static int cna_setup_quota (cfg_quota_t *cq) /* {{{ */
+{
+       if (cq == NULL)
+               return (EINVAL);
+
+       if (cq->query != NULL)
+               return (0);
+
+       cq->query = na_elem_new ("quota-report");
+       if (cq->query == NULL)
+       {
+               ERROR ("netapp plugin: na_elem_new failed.");
+               return (-1);
+       }
+
+       return (0);
+} /* }}} int cna_setup_quota */
+
+static int cna_query_quota (host_config_t *host) /* {{{ */
+{
+       na_elem_t *data;
+       int status;
+       cdtime_t now;
+
+       if (host == NULL)
+               return (EINVAL);
+
+       /* If the user did not configure quota statistics, return without
+        * doing anything. */
+       if (host->cfg_quota == NULL)
+               return (0);
+
+       now = cdtime ();
+       if ((host->cfg_quota->interval.interval + host->cfg_quota->interval.last_read) > now)
+               return (0);
+
+       status = cna_setup_quota (host->cfg_quota);
+       if (status != 0)
+               return (status);
+       assert (host->cfg_quota->query != NULL);
+
+       data = na_server_invoke_elem (host->srv, host->cfg_quota->query);
+       if (na_results_status (data) != NA_OK)
+       {
+               ERROR ("netapp plugin: cna_query_quota: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
+               na_elem_free (data);
+               return (-1);
+       }
+
+       status = cna_handle_quota_data (host, host->cfg_quota, data);
+
+       if (status == 0)
+               host->cfg_quota->interval.last_read = now;
+
+       na_elem_free (data);
+       return (status);
+} /* }}} int cna_query_quota */
+
+/* Data corresponding to <SnapVault /> */
+static int cna_handle_snapvault_data (const char *hostname, /* {{{ */
+               cfg_snapvault_t *cfg_snapvault, na_elem_t *data, cdtime_t interval)
+{
+       na_elem_t *status;
+       na_elem_iter_t status_iter;
+
+       status = na_elem_child (data, "status-list");
+       if (! status) {
+               ERROR ("netapp plugin: SnapVault status record missing status-list");
+               return (0);
+       }
+
+       status_iter = na_child_iterator (status);
+       for (status = na_iterator_next (&status_iter);
+                       status != NULL;
+                       status = na_iterator_next (&status_iter))
+       {
+               const char *dest_sys, *dest_path, *src_sys, *src_path;
+               char plugin_instance[DATA_MAX_NAME_LEN];
+               uint64_t value;
+
+               dest_sys  = na_child_get_string (status, "destination-system");
+               dest_path = na_child_get_string (status, "destination-path");
+               src_sys   = na_child_get_string (status, "source-system");
+               src_path  = na_child_get_string (status, "source-path");
+
+               if ((! dest_sys) || (! dest_path) || (! src_sys) || (! src_path))
+                       continue;
+
+               value = na_child_get_uint64 (status, "lag-time", UINT64_MAX);
+               if (value == UINT64_MAX) /* no successful baseline transfer yet */
+                       continue;
+
+               /* possible TODO: make plugin instance configurable */
+               ssnprintf (plugin_instance, sizeof (plugin_instance),
+                               "snapvault-%s", dest_path);
+               submit_double (hostname, plugin_instance, /* type = */ "delay", NULL,
+                               (double)value, /* timestamp = */ 0, interval);
+
+               value = na_child_get_uint64 (status, "last-transfer-duration", UINT64_MAX);
+               if (value != UINT64_MAX)
+                       submit_double (hostname, plugin_instance, /* type = */ "duration", "last_transfer",
+                                       (double)value, /* timestamp = */ 0, interval);
+
+               value = na_child_get_uint64 (status, "transfer-progress", UINT64_MAX);
+               if (value == UINT64_MAX)
+                       value = na_child_get_uint64 (status, "last-transfer-size", UINT64_MAX);
+               if (value != UINT64_MAX) {
+                       value *= 1024; /* this is kilobytes */
+                       submit_derive (hostname, plugin_instance, /* type = */ "if_rx_octets", "transferred",
+                                       value, /* timestamp = */ 0, interval);
+               }
+       } /* for (status) */
+
+       return (0);
+} /* }}} int cna_handle_snapvault_data */
+
+static int cna_handle_snapvault_iter (host_config_t *host, /* {{{ */
+               na_elem_t *data)
+{
+       const char *tag;
+
+       uint32_t records_count;
+       uint32_t i;
+
+       records_count = na_child_get_uint32 (data, "records", UINT32_MAX);
+       if (records_count == UINT32_MAX)
+               return 0;
+
+       tag = na_child_get_string (data, "tag");
+       if (! tag)
+               return 0;
+
+       DEBUG ("netapp plugin: Iterating %u SV records (tag = %s)", records_count, tag);
+
+       for (i = 0; i < records_count; ++i) {
+               na_elem_t *elem;
+
+               elem = na_server_invoke (host->srv,
+                               "snapvault-secondary-relationship-status-list-iter-next",
+                               "maximum", "1", "tag", tag, NULL);
+
+               if (na_results_status (elem) != NA_OK)
+               {
+                       ERROR ("netapp plugin: cna_handle_snapvault_iter: "
+                                       "na_server_invoke failed for host %s: %s",
+                                       host->name, na_results_reason (data));
+                       na_elem_free (elem);
+                       return (-1);
+               }
+
+               cna_handle_snapvault_data (host->name, host->cfg_snapvault, elem,
+                               host->cfg_snapvault->interval.interval);
+               na_elem_free (elem);
+       }
+
+       na_elem_free (na_server_invoke (host->srv,
+                       "snapvault-secondary-relationship-status-list-iter-end",
+                       "tag", tag, NULL));
+       return (0);
+} /* }}} int cna_handle_snapvault_iter */
+
+static int cna_setup_snapvault (cfg_snapvault_t *sv) /* {{{ */
+{
+       if (sv == NULL)
+               return (EINVAL);
+
+       if (sv->query != NULL)
+               return (0);
+
+       sv->query = na_elem_new ("snapvault-secondary-relationship-status-list-iter-start");
+       if (sv->query == NULL)
+       {
+               ERROR ("netapp plugin: na_elem_new failed.");
+               return (-1);
+       }
+
+       return (0);
+} /* }}} int cna_setup_snapvault */
+
+static int cna_query_snapvault (host_config_t *host) /* {{{ */
+{
+       na_elem_t *data;
+       int status;
+       cdtime_t now;
+
+       if (host == NULL)
+               return EINVAL;
+
+       if (host->cfg_snapvault == NULL)
+               return 0;
+
+       now = cdtime ();
+       if ((host->cfg_snapvault->interval.interval + host->cfg_snapvault->interval.last_read) > now)
+               return (0);
+
+       status = cna_setup_snapvault (host->cfg_snapvault);
+       if (status != 0)
+               return (status);
+       assert (host->cfg_snapvault->query != NULL);
+
+       data = na_server_invoke_elem (host->srv, host->cfg_snapvault->query);
+       if (na_results_status (data) != NA_OK)
+       {
+               ERROR ("netapp plugin: cna_query_snapvault: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
+               na_elem_free (data);
+               return (-1);
+       }
+
+       status = cna_handle_snapvault_iter (host, data);
+
+       if (status == 0)
+               host->cfg_snapvault->interval.last_read = now;
+
+       na_elem_free (data);
+       return (status);
+} /* }}} int cna_query_snapvault */
+
 /* Data corresponding to <System /> */
 static int cna_handle_system_data (const char *hostname, /* {{{ */
                cfg_system_t *cfg_system, na_elem_t *data, int interval)
@@ -1881,7 +2266,8 @@ static int cna_query_system (host_config_t *host) /* {{{ */
                return (-1);
        }
 
-       status = cna_handle_system_data (host->name, host->cfg_system, data, host->interval);
+       status = cna_handle_system_data (host->name, host->cfg_system, data,
+                       host->cfg_system->interval.interval);
 
        if (status == 0)
                host->cfg_system->interval.last_read = now;
@@ -2136,6 +2522,40 @@ static void cna_config_volume_usage_default (cfg_volume_usage_t *cvu, /* {{{ */
                ignorelist_set_invert (il, /* invert = */ 1);
 } /* }}} void cna_config_volume_usage_default */
 
+/* Corresponds to a <Quota /> block */
+static int cna_config_quota (host_config_t *host, oconfig_item_t *ci) /* {{{ */
+{
+       cfg_quota_t *cfg_quota;
+       int i;
+
+       if ((host == NULL) || (ci == NULL))
+               return (EINVAL);
+
+       if (host->cfg_quota == NULL)
+       {
+               cfg_quota = malloc (sizeof (*cfg_quota));
+               if (cfg_quota == NULL)
+                       return (ENOMEM);
+               memset (cfg_quota, 0, sizeof (*cfg_quota));
+               cfg_quota->query = NULL;
+
+               host->cfg_quota = cfg_quota;
+       }
+       cfg_quota = host->cfg_quota;
+
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *item = ci->children + i;
+
+               if (strcasecmp (item->key, "Interval") == 0)
+                       cna_config_get_interval (item, &cfg_quota->interval);
+               else
+                       WARNING ("netapp plugin: The option %s is not allowed within "
+                                       "`Quota' blocks.", item->key);
+       }
+
+       return (0);
+} /* }}} int cna_config_quota */
+
 /* Corresponds to a <Disks /> block */
 static int cna_config_disk(host_config_t *host, oconfig_item_t *ci) { /* {{{ */
        cfg_disk_t *cfg_disk;
@@ -2311,6 +2731,42 @@ static int cna_config_volume_usage(host_config_t *host, /* {{{ */
        return (0);
 } /* }}} int cna_config_volume_usage */
 
+/* Corresponds to a <SnapVault /> block */
+static int cna_config_snapvault (host_config_t *host, /* {{{ */
+               const oconfig_item_t *ci)
+{
+       cfg_snapvault_t *cfg_snapvault;
+       int i;
+
+       if ((host == NULL) || (ci == NULL))
+               return EINVAL;
+
+       if (host->cfg_snapvault == NULL)
+       {
+               cfg_snapvault = malloc (sizeof (*cfg_snapvault));
+               if (cfg_snapvault == NULL)
+                       return ENOMEM;
+               memset (cfg_snapvault, 0, sizeof (*cfg_snapvault));
+               cfg_snapvault->query = NULL;
+
+               host->cfg_snapvault = cfg_snapvault;
+       }
+
+       cfg_snapvault = host->cfg_snapvault;
+
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *item = ci->children + i;
+
+               if (strcasecmp (item->key, "Interval") == 0)
+                       cna_config_get_interval (item, &cfg_snapvault->interval);
+               else
+                       WARNING ("netapp plugin: The option %s is not allowed within "
+                                       "`SnapVault' blocks.", item->key);
+       }
+
+       return 0;
+} /* }}} int cna_config_snapvault */
+
 /* Corresponds to a <System /> block */
 static int cna_config_system (host_config_t *host, /* {{{ */
                oconfig_item_t *ci)
@@ -2367,38 +2823,131 @@ static int cna_config_system (host_config_t *host, /* {{{ */
 } /* }}} int cna_config_system */
 
 /* Corresponds to a <Host /> block. */
-static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
+static host_config_t *cna_alloc_host (void) /* {{{ */
 {
-       oconfig_item_t *item;
        host_config_t *host;
-       int status;
-       int i;
-       
-       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-               WARNING("netapp plugin: \"Host\" needs exactly one string argument. Ignoring host block.");
-               return 0;
-       }
 
        host = malloc(sizeof(*host));
+       if (! host)
+               return (NULL);
        memset (host, 0, sizeof (*host));
+
        host->name = NULL;
        host->protocol = NA_SERVER_TRANSPORT_HTTPS;
        host->host = NULL;
        host->username = NULL;
        host->password = NULL;
+       host->vfiler = NULL;
        host->srv = NULL;
        host->cfg_wafl = NULL;
        host->cfg_disk = NULL;
        host->cfg_volume_perf = NULL;
        host->cfg_volume_usage = NULL;
+       host->cfg_quota = NULL;
+       host->cfg_snapvault = NULL;
        host->cfg_system = NULL;
 
-       status = cf_util_get_string (ci, &host->name);
-       if (status != 0)
-       {
-               sfree (host);
+       return (host);
+} /* }}} host_config_t *cna_alloc_host */
+
+static host_config_t *cna_shallow_clone_host (host_config_t *host) /* {{{ */
+{
+       host_config_t *clone;
+
+       if (host == NULL)
                return (NULL);
+
+       clone = cna_alloc_host ();
+       if (clone == NULL)
+               return (NULL);
+
+       if (host->name != NULL) {
+               clone->name = strdup (host->name);
+               if (clone->name == NULL) {
+                       free_host_config (clone);
+                       return NULL;
+               }
+       }
+
+       clone->protocol = host->protocol;
+
+       if (host->host != NULL) {
+               clone->host = strdup (host->host);
+               if (clone->host == NULL) {
+                       free_host_config (clone);
+                       return NULL;
+               }
+       }
+
+       clone->port = host->port;
+
+       if (host->username != NULL) {
+               clone->username = strdup (host->username);
+               if (clone->username == NULL) {
+                       free_host_config (clone);
+                       return NULL;
+               }
        }
+       if (host->password != NULL) {
+               clone->password = strdup (host->password);
+               if (clone->password == NULL) {
+                       free_host_config (clone);
+                       return NULL;
+               }
+       }
+
+       clone->interval = host->interval;
+
+       return (clone);
+} /* }}} host_config_t *cna_shallow_clone_host */
+
+static int cna_read (user_data_t *ud);
+
+static int cna_register_host (host_config_t *host) /* {{{ */
+{
+       char cb_name[256];
+       struct timespec interval;
+       user_data_t ud;
+
+       if (host->vfiler)
+               ssnprintf (cb_name, sizeof (cb_name), "netapp-%s-%s",
+                               host->name, host->vfiler);
+       else
+               ssnprintf (cb_name, sizeof (cb_name), "netapp-%s", host->name);
+
+       CDTIME_T_TO_TIMESPEC (host->interval, &interval);
+
+       memset (&ud, 0, sizeof (ud));
+       ud.data = host;
+       ud.free_func = (void (*) (void *)) free_host_config;
+
+       plugin_register_complex_read (/* group = */ NULL, cb_name,
+                       /* callback  = */ cna_read,
+                       /* interval  = */ (host->interval > 0) ? &interval : NULL,
+                       /* user data = */ &ud);
+
+       return (0);
+} /* }}} int cna_register_host */
+
+static int cna_config_host (host_config_t *host, /* {{{ */
+               const oconfig_item_t *ci)
+{
+       oconfig_item_t *item;
+       _Bool is_vfiler = 0;
+       int status;
+       int i;
+
+       if (! strcasecmp (ci->key, "VFiler"))
+               is_vfiler = 1;
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+               WARNING ("netapp plugin: \"%s\" needs exactly one string argument. Ignoring host block.", ci->key);
+               return (1);
+       }
+
+       status = cf_util_get_string (ci, &host->name);
+       if (status != 0)
+               return (1);
 
        for (i = 0; i < ci->children_num; ++i) {
                item = ci->children + i;
@@ -2416,7 +2965,7 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
                } else if (!strcasecmp(item->key, "Protocol")) {
                        if ((item->values_num != 1) || (item->values[0].type != OCONFIG_TYPE_STRING) || (strcasecmp(item->values[0].value.string, "http") && strcasecmp(item->values[0].value.string, "https"))) {
                                WARNING("netapp plugin: \"Protocol\" needs to be either \"http\" or \"https\". Ignoring host block \"%s\".", ci->values[0].value.string);
-                               return 0;
+                               return (1);
                        }
                        if (!strcasecmp(item->values[0].value.string, "http")) host->protocol = NA_SERVER_TRANSPORT_HTTP;
                        else host->protocol = NA_SERVER_TRANSPORT_HTTPS;
@@ -2434,11 +2983,32 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
                        cna_config_volume_performance(host, item);
                } else if (!strcasecmp(item->key, "VolumeUsage")) {
                        cna_config_volume_usage(host, item);
+               } else if (!strcasecmp(item->key, "Quota")) {
+                       cna_config_quota(host, item);
+               } else if (!strcasecmp(item->key, "SnapVault")) {
+                       cna_config_snapvault(host, item);
                } else if (!strcasecmp(item->key, "System")) {
                        cna_config_system(host, item);
+               } else if ((!strcasecmp(item->key, "VFiler")) && (! is_vfiler)) {
+                       host_config_t *vfiler;
+
+                       vfiler = cna_shallow_clone_host (host);
+                       if (! vfiler) {
+                               ERROR ("netapp plugin: Failed to allocate host object for vfiler.");
+                               continue;
+                       }
+
+                       if (cna_config_host (vfiler, item)) {
+                               free_host_config (vfiler);
+                               continue;
+                       }
+
+                       cna_register_host (vfiler);
+               } else if ((!strcasecmp(item->key, "VFilerName")) && is_vfiler) {
+                       status = cf_util_get_string (item, &host->vfiler);
                } else {
-                       WARNING("netapp plugin: Ignoring unknown config option \"%s\" in host block \"%s\".",
-                                       item->key, ci->values[0].value.string);
+                       WARNING ("netapp plugin: Ignoring unknown config option \"%s\" in %s block \"%s\".",
+                                       item->key, is_vfiler ? "vfiler" : "host", ci->values[0].value.string);
                }
 
                if (status != 0)
@@ -2448,6 +3018,9 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
        if (host->host == NULL)
                host->host = strdup (host->name);
 
+       if (is_vfiler && (! host->vfiler))
+               host->vfiler = strdup (host->name);
+
        if (host->host == NULL)
                status = -1;
 
@@ -2461,12 +3034,9 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
        }
 
        if (status != 0)
-       {
-               free_host_config (host);
-               return (NULL);
-       }
+               return status;
 
-       return host;
+       return (0);
 } /* }}} host_config_t *cna_config_host */
 
 /*
@@ -2476,15 +3046,19 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
  */
 static int cna_init_host (host_config_t *host) /* {{{ */
 {
+       /* Request version 1.1 of the ONTAP API */
+       int major_version = 1, minor_version = 1;
+
        if (host == NULL)
                return (EINVAL);
 
        if (host->srv != NULL)
                return (0);
 
-       /* Request version 1.1 of the ONTAP API */
-       host->srv = na_server_open(host->host,
-                       /* major version = */ 1, /* minor version = */ 1); 
+       if (host->vfiler != NULL) /* Request version 1.7 of the ONTAP API */
+               minor_version = 7;
+
+       host->srv = na_server_open (host->host, major_version, minor_version);
        if (host->srv == NULL) {
                ERROR ("netapp plugin: na_server_open (%s) failed.", host->host);
                return (-1);
@@ -2497,6 +3071,18 @@ 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 */);
 
+       if (host->vfiler != NULL) {
+               if (! na_server_set_vfiler (host->srv, host->vfiler)) {
+                       ERROR ("netapp plugin: Failed to connect to VFiler '%s' on host '%s'.",
+                                       host->vfiler, host->host);
+                       return (-1);
+               }
+               else {
+                       INFO ("netapp plugin: Connected to VFiler '%s' on host '%s'.",
+                                       host->vfiler, host->host);
+               }
+       }
+
        return (0);
 } /* }}} int cna_init_host */
 
@@ -2533,6 +3119,14 @@ static int cna_read_internal (host_config_t *host) { /* {{{ */
        if (status != 0)
                return (status);
 
+       status = cna_query_quota (host);
+       if (status != 0)
+               return (status);
+
+       status = cna_query_snapvault (host);
+       if (status != 0)
+               return (status);
+
        status = cna_query_system (host);
        if (status != 0)
                return (status);
@@ -2574,27 +3168,19 @@ static int cna_config (oconfig_item_t *ci) { /* {{{ */
                if (strcasecmp(item->key, "Host") == 0)
                {
                        host_config_t *host;
-                       char cb_name[256];
-                       struct timespec interval;
-                       user_data_t ud;
 
-                       host = cna_config_host (item);
-                       if (host == NULL)
+                       host = cna_alloc_host ();
+                       if (host == NULL) {
+                               ERROR ("netapp plugin: Failed to allocate host object.");
                                continue;
+                       }
 
-                       ssnprintf (cb_name, sizeof (cb_name), "netapp-%s", host->name);
-
-                       CDTIME_T_TO_TIMESPEC (host->interval, &interval);
-
-                       memset (&ud, 0, sizeof (ud));
-                       ud.data = host;
-                       ud.free_func = (void (*) (void *)) free_host_config;
+                       if (cna_config_host (host, item) != 0) {
+                               free_host_config (host);
+                               continue;
+                       }
 
-                       plugin_register_complex_read (/* group = */ NULL, cb_name,
-                                       /* callback  = */ cna_read, 
-                                       /* interval  = */ (host->interval > 0) ? &interval : NULL,
-                                       /* user data = */ &ud);
-                       continue;
+                       cna_register_host (host);
                }
                else /* if (item->key != "Host") */
                {
index 39536d9..de2de9b 100644 (file)
@@ -1,6 +1,9 @@
 /**
  * collectd - src/netlink.c
  * Copyright (C) 2007-2010  Florian octo Forster
+ * Copyright (C) 2008-2012  Sebastian Harl
+ * Copyright (C) 2013       Andreas Henriksson
+ * Copyright (C) 2013       Marc Fournier
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -17,6 +20,9 @@
  *
  * Authors:
  *   Florian octo Forster <octo at collectd.org>
+ *   Sebastian Harl <sh at tokkee.org>
+ *   Andreas Henriksson <andreas at fatal.se>
+ *   Marc Fournier <marc.fournier at camptocamp.com>
  **/
 
 #include "collectd.h"
 # include <linux/pkt_sched.h>
 #endif
 
-#if HAVE_LIBNETLINK_H
-# include <libnetlink.h>
-#elif HAVE_IPROUTE_LIBNETLINK_H
-# include <iproute/libnetlink.h>
-#elif HAVE_LINUX_LIBNETLINK_H
-# include <linux/libnetlink.h>
+#include <libmnl/libmnl.h>
+
+struct ir_link_stats_storage_s {
+
+  uint64_t rx_packets;
+  uint64_t tx_packets;
+  uint64_t rx_bytes;
+  uint64_t tx_bytes;
+  uint64_t rx_errors;
+  uint64_t tx_errors;
+
+  uint64_t rx_dropped;
+  uint64_t tx_dropped;
+  uint64_t multicast;
+  uint64_t collisions;
+
+  uint64_t rx_length_errors;
+  uint64_t rx_over_errors;
+  uint64_t rx_crc_errors;
+  uint64_t rx_frame_errors;
+  uint64_t rx_fifo_errors;
+  uint64_t rx_missed_errors;
+
+  uint64_t tx_aborted_errors;
+  uint64_t tx_carrier_errors;
+  uint64_t tx_fifo_errors;
+  uint64_t tx_heartbeat_errors;
+  uint64_t tx_window_errors;
+};
+
+union ir_link_stats_u {
+  struct rtnl_link_stats *stats32;
+#ifdef HAVE_RTNL_LINK_STATS64
+  struct rtnl_link_stats64 *stats64;
 #endif
+};
 
 typedef struct ir_ignorelist_s
 {
@@ -54,19 +89,19 @@ typedef struct ir_ignorelist_s
 static int ir_ignorelist_invert = 1;
 static ir_ignorelist_t *ir_ignorelist_head = NULL;
 
-static struct rtnl_handle rth;
+static struct mnl_socket *nl;
 
 static char **iflist = NULL;
 static size_t iflist_len = 0;
 
 static const char *config_keys[] =
 {
-       "Interface",
-       "VerboseInterface",
-       "QDisc",
-       "Class",
-       "Filter",
-       "IgnoreSelected"
+        "Interface",
+        "VerboseInterface",
+        "QDisc",
+        "Class",
+        "Filter",
+        "IgnoreSelected"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -117,7 +152,7 @@ static int add_ignorelist (const char *dev, const char *type,
   return (0);
 } /* int add_ignorelist */
 
-/* 
+/*
  * Checks wether a data set should be ignored. Returns `true' is the value
  * should be ignored, `false' otherwise.
  */
@@ -135,24 +170,24 @@ static int check_ignorelist (const char *dev,
   {
     /* i->device == NULL  =>  match all devices */
     if ((i->device != NULL)
-       && (strcasecmp (i->device, dev) != 0))
+        && (strcasecmp (i->device, dev) != 0))
       continue;
 
     if (strcasecmp (i->type, type) != 0)
       continue;
 
     if ((i->inst != NULL) && (type_instance != NULL)
-       && (strcasecmp (i->inst, type_instance) != 0))
+        && (strcasecmp (i->inst, type_instance) != 0))
       continue;
 
     DEBUG ("netlink plugin: check_ignorelist: "
-       "(dev = %s; type = %s; inst = %s) matched "
-       "(dev = %s; type = %s; inst = %s)",
-       dev, type,
-       type_instance == NULL ? "(nil)" : type_instance,
-       i->device == NULL ? "(nil)" : i->device,
-       i->type,
-       i->inst == NULL ? "(nil)" : i->inst);
+        "(dev = %s; type = %s; inst = %s) matched "
+        "(dev = %s; type = %s; inst = %s)",
+        dev, type,
+        type_instance == NULL ? "(nil)" : type_instance,
+        i->device == NULL ? "(nil)" : i->device,
+        i->type,
+        i->inst == NULL ? "(nil)" : i->inst);
 
     return (ir_ignorelist_invert ? 0 : 1);
   } /* for i */
@@ -204,46 +239,8 @@ static void submit_two (const char *dev, const char *type,
   plugin_dispatch_values (&vl);
 } /* void submit_two */
 
-static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
-    struct nlmsghdr *nmh, void __attribute__((unused)) *args)
+static int update_iflist (struct ifinfomsg *msg, const char *dev)
 {
-  struct ifinfomsg *msg;
-  int msg_len;
-  struct rtattr *attrs[IFLA_MAX + 1];
-  struct rtnl_link_stats *stats;
-
-  const char *dev;
-
-  if (nmh->nlmsg_type != RTM_NEWLINK)
-  {
-    ERROR ("netlink plugin: link_filter: Don't know how to handle type %i.",
-       nmh->nlmsg_type);
-    return (-1);
-  }
-
-  msg = NLMSG_DATA (nmh);
-
-  msg_len = nmh->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifinfomsg));
-  if (msg_len < 0)
-  {
-    ERROR ("netlink plugin: link_filter: msg_len = %i < 0;", msg_len);
-    return (-1);
-  }
-
-  memset (attrs, '\0', sizeof (attrs));
-  if (parse_rtattr (attrs, IFLA_MAX, IFLA_RTA (msg), msg_len) != 0)
-  {
-    ERROR ("netlink plugin: link_filter: parse_rtattr failed.");
-    return (-1);
-  }
-
-  if (attrs[IFLA_IFNAME] == NULL)
-  {
-    ERROR ("netlink plugin: link_filter: attrs[IFLA_IFNAME] == NULL");
-    return (-1);
-  }
-  dev = RTA_DATA (attrs[IFLA_IFNAME]);
-
   /* Update the `iflist'. It's used to know which interfaces exist and query
    * them later for qdiscs and classes. */
   if ((msg->ifi_index >= 0) && ((size_t) msg->ifi_index >= iflist_len))
@@ -253,12 +250,12 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
     temp = (char **) realloc (iflist, (msg->ifi_index + 1) * sizeof (char *));
     if (temp == NULL)
     {
-      ERROR ("netlink plugin: link_filter: realloc failed.");
+      ERROR ("netlink plugin: update_iflist: realloc failed.");
       return (-1);
     }
 
     memset (temp + iflist_len, '\0',
-       (msg->ifi_index + 1 - iflist_len) * sizeof (char *));
+        (msg->ifi_index + 1 - iflist_len) * sizeof (char *));
     iflist = temp;
     iflist_len = msg->ifi_index + 1;
   }
@@ -269,12 +266,12 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
     iflist[msg->ifi_index] = strdup (dev);
   }
 
-  if (attrs[IFLA_STATS] == NULL)
-  {
-    DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev);
-    return (0);
-  }
-  stats = RTA_DATA (attrs[IFLA_STATS]);
+  return (0);
+} /* int update_iflist */
+
+static void check_ignorelist_and_submit (const char *dev,
+    struct ir_link_stats_storage_s *stats)
+{
 
   if (check_ignorelist (dev, "interface", NULL) == 0)
   {
@@ -311,158 +308,323 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
     DEBUG ("netlink plugin: Ignoring %s/if_detail.", dev);
   }
 
-  return (0);
-} /* int link_filter */
+} /* void check_ignorelist_and_submit */
+
+#define COPY_RTNL_LINK_VALUE(dst_stats, src_stats, value_name) \
+  (dst_stats)->value_name = (src_stats)->value_name
+
+#define COPY_RTNL_LINK_STATS(dst_stats, src_stats) \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_packets); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_packets); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_bytes); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_bytes); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_dropped); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_dropped); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, multicast); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, collisions); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_length_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_over_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_crc_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_frame_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_fifo_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, rx_missed_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_aborted_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_carrier_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_fifo_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_heartbeat_errors); \
+  COPY_RTNL_LINK_VALUE (dst_stats, src_stats, tx_window_errors)
+
+#ifdef HAVE_RTNL_LINK_STATS64
+static void check_ignorelist_and_submit64 (const char *dev,
+    struct rtnl_link_stats64 *stats)
+{
+  struct ir_link_stats_storage_s s;
+
+  COPY_RTNL_LINK_STATS (&s, stats);
+
+  check_ignorelist_and_submit (dev, &s);
+}
+#endif
+
+static void check_ignorelist_and_submit32 (const char *dev,
+    struct rtnl_link_stats *stats)
+{
+  struct ir_link_stats_storage_s s;
+
+  COPY_RTNL_LINK_STATS(&s, stats);
+
+  check_ignorelist_and_submit (dev, &s);
+}
+
+static int link_filter_cb (const struct nlmsghdr *nlh,
+    void *args __attribute__((unused)))
+{
+  struct ifinfomsg *ifm = mnl_nlmsg_get_payload (nlh);
+  struct nlattr *attr;
+  const char *dev = NULL;
+  union ir_link_stats_u stats;
+
+  if (nlh->nlmsg_type != RTM_NEWLINK)
+  {
+    ERROR ("netlink plugin: link_filter_cb: Don't know how to handle type %i.",
+        nlh->nlmsg_type);
+    return MNL_CB_ERROR;
+  }
+
+  /* Scan attribute list for device name. */
+  mnl_attr_for_each (attr, nlh, sizeof (*ifm))
+  {
+    if (mnl_attr_get_type (attr) != IFLA_IFNAME)
+      continue;
+
+    if (mnl_attr_validate (attr, MNL_TYPE_STRING) < 0)
+    {
+      ERROR ("netlink plugin: link_filter_cb: IFLA_IFNAME mnl_attr_validate failed.");
+      return MNL_CB_ERROR;
+    }
+
+    dev = mnl_attr_get_str (attr);
+    if (update_iflist (ifm, dev) < 0)
+      return MNL_CB_ERROR;
+    break;
+  }
+
+  if (dev == NULL)
+  {
+    ERROR ("netlink plugin: link_filter_cb: dev == NULL");
+    return MNL_CB_ERROR;
+  }
+#ifdef HAVE_RTNL_LINK_STATS64
+  mnl_attr_for_each (attr, nlh, sizeof (*ifm))
+  {
+    if (mnl_attr_get_type (attr) != IFLA_STATS64)
+      continue;
+
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*stats.stats64)) < 0)
+    {
+      ERROR ("netlink plugin: link_filter_cb: IFLA_STATS64 mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    stats.stats64 = mnl_attr_get_payload (attr);
+
+    check_ignorelist_and_submit64 (dev, stats.stats64);
+
+    return MNL_CB_OK;
+  }
+#endif
+  mnl_attr_for_each (attr, nlh, sizeof (*ifm))
+  {
+    if (mnl_attr_get_type (attr) != IFLA_STATS)
+      continue;
+
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*stats.stats32)) < 0)
+    {
+      ERROR ("netlink plugin: link_filter_cb: IFLA_STATS mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    stats.stats32 = mnl_attr_get_payload (attr);
+
+    check_ignorelist_and_submit32 (dev, stats.stats32);
+
+    return MNL_CB_OK;
+  }
+
+  DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev);
+  return MNL_CB_OK;
+
+} /* int link_filter_cb */
+
+#if HAVE_TCA_STATS2
+static int qos_attr_cb (const struct nlattr *attr, void *data)
+{
+  struct gnet_stats_basic **bs = (struct gnet_stats_basic **)data;
+
+  /* skip unsupported attribute in user-space */
+  if (mnl_attr_type_valid (attr, TCA_STATS_MAX) < 0)
+    return MNL_CB_OK;
+
+  if (mnl_attr_get_type (attr) == TCA_STATS_BASIC)
+  {
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (**bs)) < 0)
+    {
+      ERROR ("netlink plugin: qos_attr_cb: TCA_STATS_BASIC mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    *bs = mnl_attr_get_payload (attr);
+    return MNL_CB_STOP;
+  }
+
+  return MNL_CB_OK;
+} /* qos_attr_cb */
+#endif
 
-static int qos_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
-    struct nlmsghdr *nmh, void *args)
+static int qos_filter_cb (const struct nlmsghdr *nlh, void *args)
 {
-  struct tcmsg *msg;
-  int msg_len;
-  struct rtattr *attrs[TCA_MAX + 1];
+  struct tcmsg *tm = mnl_nlmsg_get_payload (nlh);
+  struct nlattr *attr;
 
   int wanted_ifindex = *((int *) args);
 
   const char *dev;
+  const char *kind = NULL;
 
   /* char *type_instance; */
   char *tc_type;
   char tc_inst[DATA_MAX_NAME_LEN];
 
-  if (nmh->nlmsg_type == RTM_NEWQDISC)
+  _Bool stats_submitted = 0;
+
+  if (nlh->nlmsg_type == RTM_NEWQDISC)
     tc_type = "qdisc";
-  else if (nmh->nlmsg_type == RTM_NEWTCLASS)
+  else if (nlh->nlmsg_type == RTM_NEWTCLASS)
     tc_type = "class";
-  else if (nmh->nlmsg_type == RTM_NEWTFILTER)
+  else if (nlh->nlmsg_type == RTM_NEWTFILTER)
     tc_type = "filter";
   else
   {
-    ERROR ("netlink plugin: qos_filter: Don't know how to handle type %i.",
-       nmh->nlmsg_type);
-    return (-1);
-  }
-
-  msg = NLMSG_DATA (nmh);
-
-  msg_len = nmh->nlmsg_len - sizeof (struct tcmsg);
-  if (msg_len < 0)
-  {
-    ERROR ("netlink plugin: qos_filter: msg_len = %i < 0;", msg_len);
-    return (-1);
+    ERROR ("netlink plugin: qos_filter_cb: Don't know how to handle type %i.",
+        nlh->nlmsg_type);
+    return MNL_CB_ERROR;
   }
 
-  if (msg->tcm_ifindex != wanted_ifindex)
+  if (tm->tcm_ifindex != wanted_ifindex)
   {
-    DEBUG ("netlink plugin: qos_filter: Got %s for interface #%i, "
-       "but expected #%i.",
-       tc_type, msg->tcm_ifindex, wanted_ifindex);
-    return (0);
+    DEBUG ("netlink plugin: qos_filter_cb: Got %s for interface #%i, "
+        "but expected #%i.",
+        tc_type, tm->tcm_ifindex, wanted_ifindex);
+    return MNL_CB_OK;
   }
 
-  if ((msg->tcm_ifindex >= 0)
-      && ((size_t) msg->tcm_ifindex >= iflist_len))
+  if ((tm->tcm_ifindex >= 0)
+      && ((size_t) tm->tcm_ifindex >= iflist_len))
   {
-    ERROR ("netlink plugin: qos_filter: msg->tcm_ifindex = %i "
-       ">= iflist_len = %zu",
-       msg->tcm_ifindex, iflist_len);
-    return (-1);
+    ERROR ("netlink plugin: qos_filter_cb: tm->tcm_ifindex = %i "
+        ">= iflist_len = %zu",
+        tm->tcm_ifindex, iflist_len);
+    return MNL_CB_ERROR;
   }
 
-  dev = iflist[msg->tcm_ifindex];
+  dev = iflist[tm->tcm_ifindex];
   if (dev == NULL)
   {
-    ERROR ("netlink plugin: qos_filter: iflist[%i] == NULL",
-       msg->tcm_ifindex);
-    return (-1);
+    ERROR ("netlink plugin: qos_filter_cb: iflist[%i] == NULL",
+        tm->tcm_ifindex);
+    return MNL_CB_ERROR;
   }
 
-  memset (attrs, '\0', sizeof (attrs));
-  if (parse_rtattr (attrs, TCA_MAX, TCA_RTA (msg), msg_len) != 0)
+  mnl_attr_for_each (attr, nlh, sizeof (*tm))
   {
-    ERROR ("netlink plugin: qos_filter: parse_rtattr failed.");
-    return (-1);
+    if (mnl_attr_get_type (attr) != TCA_KIND)
+      continue;
+
+    if (mnl_attr_validate (attr, MNL_TYPE_STRING) < 0)
+    {
+      ERROR ("netlink plugin: qos_filter_cb: TCA_KIND mnl_attr_validate failed.");
+      return MNL_CB_ERROR;
+    }
+
+    kind = mnl_attr_get_str (attr);
+    break;
   }
 
-  if (attrs[TCA_KIND] == NULL)
+  if (kind == NULL)
   {
-    ERROR ("netlink plugin: qos_filter: attrs[TCA_KIND] == NULL");
+    ERROR ("netlink plugin: qos_filter_cb: kind == NULL");
     return (-1);
   }
 
-  { /* The the ID */
+  { /* The ID */
     uint32_t numberic_id;
 
-    numberic_id = msg->tcm_handle;
+    numberic_id = tm->tcm_handle;
     if (strcmp (tc_type, "filter") == 0)
-      numberic_id = msg->tcm_parent;
+      numberic_id = tm->tcm_parent;
 
     ssnprintf (tc_inst, sizeof (tc_inst), "%s-%x:%x",
-       (const char *) RTA_DATA (attrs[TCA_KIND]),
-       numberic_id >> 16,
-       numberic_id & 0x0000FFFF);
+        kind,
+        numberic_id >> 16,
+        numberic_id & 0x0000FFFF);
   }
 
-  DEBUG ("netlink plugin: qos_filter: got %s for %s (%i).",
-      tc_type, dev, msg->tcm_ifindex);
-  
+  DEBUG ("netlink plugin: qos_filter_cb: got %s for %s (%i).",
+      tc_type, dev, tm->tcm_ifindex);
+
   if (check_ignorelist (dev, tc_type, tc_inst))
-    return (0);
+    return MNL_CB_OK;
 
 #if HAVE_TCA_STATS2
-  if (attrs[TCA_STATS2])
+  mnl_attr_for_each (attr, nlh, sizeof (*tm))
   {
-    struct rtattr *attrs_stats[TCA_STATS_MAX + 1];
+    struct gnet_stats_basic *bs = NULL;
 
-    memset (attrs_stats, '\0', sizeof (attrs_stats));
-    parse_rtattr_nested (attrs_stats, TCA_STATS_MAX, attrs[TCA_STATS2]);
+    if (mnl_attr_get_type (attr) != TCA_STATS2)
+      continue;
 
-    if (attrs_stats[TCA_STATS_BASIC])
+    if (mnl_attr_validate (attr, MNL_TYPE_NESTED) < 0)
+    {
+      ERROR ("netlink plugin: qos_filter_cb: TCA_STATS2 mnl_attr_validate failed.");
+      return MNL_CB_ERROR;
+    }
+
+    mnl_attr_parse_nested (attr, qos_attr_cb, &bs);
+
+    if (bs != NULL)
     {
-      struct gnet_stats_basic bs;
       char type_instance[DATA_MAX_NAME_LEN];
 
-      ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
-         tc_type, tc_inst);
+      stats_submitted = 1;
 
-      memset (&bs, '\0', sizeof (bs));
-      memcpy (&bs, RTA_DATA (attrs_stats[TCA_STATS_BASIC]),
-         MIN (RTA_PAYLOAD (attrs_stats[TCA_STATS_BASIC]), sizeof(bs)));
+      ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
+          tc_type, tc_inst);
 
-      submit_one (dev, "ipt_bytes", type_instance, bs.bytes);
-      submit_one (dev, "ipt_packets", type_instance, bs.packets);
+      submit_one (dev, "ipt_bytes", type_instance, bs->bytes);
+      submit_one (dev, "ipt_packets", type_instance, bs->packets);
     }
+
+    break;
   }
 #endif /* TCA_STATS2 */
-#if HAVE_TCA_STATS && HAVE_TCA_STATS2
-  else
-#endif
+
 #if HAVE_TCA_STATS
-  if (attrs[TCA_STATS] != NULL)
+  mnl_attr_for_each (attr, nlh, sizeof (*tm))
   {
-    struct tc_stats ts;
-    char type_instance[DATA_MAX_NAME_LEN];
+    struct tc_stats *ts = NULL;
+
+    if (mnl_attr_get_type (attr) != TCA_STATS)
+      continue;
+
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*ts)) < 0)
+    {
+      ERROR ("netlink plugin: qos_filter_cb: TCA_STATS mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    ts = mnl_attr_get_payload (attr);
 
-    ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
-       tc_type, tc_inst);
+    if (!stats_submitted && ts != NULL)
+    {
+      char type_instance[DATA_MAX_NAME_LEN];
 
-    memset(&ts, '\0', sizeof (ts));
-    memcpy(&ts, RTA_DATA (attrs[TCA_STATS]),
-       MIN (RTA_PAYLOAD (attrs[TCA_STATS]), sizeof (ts)));
+      ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
+          tc_type, tc_inst);
 
-    submit_one (dev, "ipt_bytes", type_instance, ts.bytes);
-    submit_one (dev, "ipt_packets", type_instance, ts.packets);
+      submit_one (dev, "ipt_bytes", type_instance, ts->bytes);
+      submit_one (dev, "ipt_packets", type_instance, ts->packets);
+    }
+
+    break;
   }
+
 #endif /* TCA_STATS */
-#if HAVE_TCA_STATS || HAVE_TCA_STATS2
-  else
+
+#if !(HAVE_TCA_STATS && HAVE_TCA_STATS2)
+  DEBUG ("netlink plugin: qos_filter_cb: Have neither TCA_STATS2 nor "
+      "TCA_STATS.");
 #endif
-  {
-    DEBUG ("netlink plugin: qos_filter: Have neither TCA_STATS2 nor "
-       "TCA_STATS.");
-  }
 
-  return (0);
-} /* int qos_filter */
+  return MNL_CB_OK;
+} /* int qos_filter_cb */
 
 static int ir_config (const char *key, const char *value)
 {
@@ -488,14 +650,14 @@ static int ir_config (const char *key, const char *value)
     if (fields_num != 1)
     {
       ERROR ("netlink plugin: Invalid number of fields for option "
-         "`%s'. Got %i, expected 1.", key, fields_num);
+          "`%s'. Got %i, expected 1.", key, fields_num);
       status = -1;
     }
     else
     {
       add_ignorelist (fields[0], "interface", NULL);
       if (strcasecmp (key, "VerboseInterface") == 0)
-       add_ignorelist (fields[0], "if_detail", NULL);
+        add_ignorelist (fields[0], "if_detail", NULL);
       status = 0;
     }
   }
@@ -506,13 +668,13 @@ static int ir_config (const char *key, const char *value)
     if ((fields_num < 1) || (fields_num > 2))
     {
       ERROR ("netlink plugin: Invalid number of fields for option "
-         "`%s'. Got %i, expected 1 or 2.", key, fields_num);
+          "`%s'. Got %i, expected 1 or 2.", key, fields_num);
       return (-1);
     }
     else
     {
       add_ignorelist (fields[0], key,
-         (fields_num == 2) ? fields[1] : NULL);
+          (fields_num == 2) ? fields[1] : NULL);
       status = 0;
     }
   }
@@ -521,15 +683,15 @@ static int ir_config (const char *key, const char *value)
     if (fields_num != 1)
     {
       ERROR ("netlink plugin: Invalid number of fields for option "
-         "`IgnoreSelected'. Got %i, expected 1.", fields_num);
+          "`IgnoreSelected'. Got %i, expected 1.", fields_num);
       status = -1;
     }
     else
     {
       if (IS_TRUE (fields[0]))
-       ir_ignorelist_invert = 0;
+        ir_ignorelist_invert = 0;
       else
-       ir_ignorelist_invert = 1;
+        ir_ignorelist_invert = 1;
       status = 0;
     }
   }
@@ -541,11 +703,16 @@ static int ir_config (const char *key, const char *value)
 
 static int ir_init (void)
 {
-  memset (&rth, '\0', sizeof (rth));
+  nl = mnl_socket_open (NETLINK_ROUTE);
+  if (nl == NULL)
+  {
+    ERROR ("netlink plugin: ir_init: mnl_socket_open failed.");
+    return (-1);
+  }
 
-  if (rtnl_open (&rth, 0) != 0)
+  if (mnl_socket_bind (nl, 0, MNL_SOCKET_AUTOPID) < 0)
   {
-    ERROR ("netlink plugin: ir_init: rtnl_open failed.");
+    ERROR ("netlink plugin: ir_init: mnl_socket_bind failed.");
     return (-1);
   }
 
@@ -554,35 +721,51 @@ static int ir_init (void)
 
 static int ir_read (void)
 {
-  struct tcmsg tm;
-  int ifindex;
+  char buf[MNL_SOCKET_BUFFER_SIZE];
+  struct nlmsghdr *nlh;
+  struct rtgenmsg *rt;
+  int ret;
+  unsigned int seq, portid;
+
+  size_t ifindex;
 
   static const int type_id[] = { RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER };
   static const char *type_name[] = { "qdisc", "class", "filter" };
 
-  if (rtnl_wilddump_request (&rth, AF_UNSPEC, RTM_GETLINK) < 0)
+  portid = mnl_socket_get_portid (nl);
+
+  nlh = mnl_nlmsg_put_header (buf);
+  nlh->nlmsg_type = RTM_GETLINK;
+  nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+  nlh->nlmsg_seq = seq = time (NULL);
+  rt = mnl_nlmsg_put_extra_header (nlh, sizeof (*rt));
+  rt->rtgen_family = AF_PACKET;
+
+  if (mnl_socket_sendto (nl, nlh, nlh->nlmsg_len) < 0)
   {
     ERROR ("netlink plugin: ir_read: rtnl_wilddump_request failed.");
     return (-1);
   }
 
-#ifdef RTNL_DUMP_FILTER_FIVE_ARGS
-  if (rtnl_dump_filter (&rth, link_filter, /* arg1 = */ NULL,
-       NULL, NULL) != 0)
-#elif defined(RTNL_DUMP_FILTER_THREE_ARGS)
-  if (rtnl_dump_filter (&rth, link_filter, /* arg = */ NULL) != 0)
-#else
-#error "Failed to determine the number of arguments to 'rtnl_dump_filter'!"
-#endif
+  ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
+  while (ret > 0)
+  {
+    ret = mnl_cb_run (buf, ret, seq, portid, link_filter_cb, NULL);
+    if (ret <= MNL_CB_STOP)
+      break;
+    ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
+  }
+  if (ret < 0)
   {
-    ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
+    ERROR ("netlink plugin: ir_read: mnl_socket_recvfrom failed.");
     return (-1);
   }
 
-  /* `link_filter' will update `iflist' which is used here to iterate over all
-   * interfaces. */
-  for (ifindex = 0; (size_t) ifindex < iflist_len; ifindex++)
+  /* `link_filter_cb' will update `iflist' which is used here to iterate
+   * over all interfaces. */
+  for (ifindex = 1; ifindex < iflist_len; ifindex++)
   {
+    struct tcmsg *tm;
     size_t type_index;
 
     if (iflist[ifindex] == NULL)
@@ -592,36 +775,42 @@ static int ir_read (void)
     {
       if (check_ignorelist (iflist[ifindex], type_name[type_index], NULL))
       {
-       DEBUG ("netlink plugin: ir_read: check_ignorelist (%s, %s, (nil)) "
-           "== TRUE", iflist[ifindex], type_name[type_index]);
-       continue;
+        DEBUG ("netlink plugin: ir_read: check_ignorelist (%s, %s, (nil)) "
+            "== TRUE", iflist[ifindex], type_name[type_index]);
+        continue;
       }
 
-      DEBUG ("netlink plugin: ir_read: querying %s from %s (%i).",
-         type_name[type_index], iflist[ifindex], ifindex);
+      DEBUG ("netlink plugin: ir_read: querying %s from %s (%lu).",
+          type_name[type_index], iflist[ifindex], ifindex);
 
-      memset (&tm, '\0', sizeof (tm));
-      tm.tcm_family = AF_UNSPEC;
-      tm.tcm_ifindex = ifindex;
+      nlh = mnl_nlmsg_put_header (buf);
+      nlh->nlmsg_type = type_id[type_index];
+      nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+      nlh->nlmsg_seq = seq = time (NULL);
+      tm = mnl_nlmsg_put_extra_header (nlh, sizeof (*tm));
+      tm->tcm_family = AF_PACKET;
+      tm->tcm_ifindex = ifindex;
 
-      if (rtnl_dump_request (&rth, type_id[type_index], &tm, sizeof (tm)) < 0)
+      if (mnl_socket_sendto (nl, nlh, nlh->nlmsg_len) < 0)
       {
-       ERROR ("netlink plugin: ir_read: rtnl_dump_request failed.");
-       continue;
+        ERROR ("netlink plugin: ir_read: mnl_socket_sendto failed.");
+        continue;
       }
 
-#ifdef RTNL_DUMP_FILTER_FIVE_ARGS
-      if (rtnl_dump_filter (&rth, qos_filter, (void *) &ifindex,
-           NULL, NULL) != 0)
-#elif defined(RTNL_DUMP_FILTER_THREE_ARGS)
-      if (rtnl_dump_filter (&rth, qos_filter, /* arg = */ &ifindex) != 0)
-#else
-#error "Failed to determine the number of arguments to 'rtnl_dump_filter'!"
-#endif
+      ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
+      while (ret > 0)
       {
-       ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
-       continue;
+        ret = mnl_cb_run (buf, ret, seq, portid, qos_filter_cb, &ifindex);
+        if (ret <= MNL_CB_STOP)
+          break;
+        ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
       }
+      if (ret < 0)
+      {
+        ERROR ("netlink plugin: ir_read:mnl_socket_recvfrom failed.");
+        continue;
+      }
+
     } /* for (type_index) */
   } /* for (if_index) */
 
@@ -630,12 +819,12 @@ static int ir_read (void)
 
 static int ir_shutdown (void)
 {
-  if ((rth.fd != 0) || (rth.seq != 0) || (rth.dump != 0))
+  if (nl)
   {
-    rtnl_close(&rth);
-    memset (&rth, '\0', sizeof (rth));
+    mnl_socket_close (nl);
+    nl = NULL;
   }
-  
+
   return (0);
 } /* int ir_shutdown */
 
index 72e3484..98cb8eb 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/network.c
- * Copyright (C) 2005-2010  Florian octo Forster
+ * Copyright (C) 2005-2013  Florian octo Forster
  * Copyright (C) 2009       Aman Gupta
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -18,7 +18,7 @@
  * Inc., 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>
  *   Aman Gupta <aman at tmm1.net>
  **/
 
@@ -460,7 +460,7 @@ static int network_dispatch_values (value_list_t *vl, /* {{{ */
     }
   }
 
-  plugin_dispatch_values_secure (vl);
+  plugin_dispatch_values (vl);
   stats_values_dispatched++;
 
   meta_data_destroy (vl->meta);
@@ -493,6 +493,20 @@ static int network_dispatch_notification (notification_t *n) /* {{{ */
 } /* }}} int network_dispatch_notification */
 
 #if HAVE_LIBGCRYPT
+static void network_init_gcrypt (void) /* {{{ */
+{
+  /* 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))
+    return;
+
+  gcry_check_version (NULL); /* before calling any other functions */
+  gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+  gcry_control (GCRYCTL_INIT_SECMEM, 32768);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED);
+} /* }}} void network_init_gcrypt */
+
 static gcry_cipher_hd_t network_get_aes256_cypher (sockent_t *se, /* {{{ */
     const void *iv, size_t iv_size, const char *username)
 {
@@ -2029,6 +2043,8 @@ static int sockent_open (sockent_t *se) /* {{{ */
        {
                if (se->data.client.security_level > SECURITY_LEVEL_NONE)
                {
+                       network_init_gcrypt ();
+
                        if ((se->data.client.username == NULL)
                                        || (se->data.client.password == NULL))
                        {
@@ -2047,6 +2063,8 @@ static int sockent_open (sockent_t *se) /* {{{ */
        {
                if (se->data.server.security_level > SECURITY_LEVEL_NONE)
                {
+                       network_init_gcrypt ();
+
                        if (se->data.server.auth_file == NULL)
                        {
                                ERROR ("network plugin: Server socket with "
@@ -3318,13 +3336,13 @@ static int network_stats_read (void) /* {{{ */
        vl.values[0].derive = (derive_t) copy_octets_rx;
        vl.values[1].derive = (derive_t) copy_octets_tx;
        sstrncpy (vl.type, "if_octets", sizeof (vl.type));
-       plugin_dispatch_values_secure (&vl);
+       plugin_dispatch_values (&vl);
 
        /* Packets received / send */
        vl.values[0].derive = (derive_t) copy_packets_rx;
        vl.values[1].derive = (derive_t) copy_packets_tx;
        sstrncpy (vl.type, "if_packets", sizeof (vl.type));
-       plugin_dispatch_values_secure (&vl);
+       plugin_dispatch_values (&vl);
 
        /* Values (not) dispatched and (not) send */
        sstrncpy (vl.type, "total_values", sizeof (vl.type));
@@ -3333,28 +3351,28 @@ static int network_stats_read (void) /* {{{ */
        vl.values[0].derive = (derive_t) copy_values_dispatched;
        sstrncpy (vl.type_instance, "dispatch-accepted",
                        sizeof (vl.type_instance));
-       plugin_dispatch_values_secure (&vl);
+       plugin_dispatch_values (&vl);
 
        vl.values[0].derive = (derive_t) copy_values_not_dispatched;
        sstrncpy (vl.type_instance, "dispatch-rejected",
                        sizeof (vl.type_instance));
-       plugin_dispatch_values_secure (&vl);
+       plugin_dispatch_values (&vl);
 
        vl.values[0].derive = (derive_t) copy_values_sent;
        sstrncpy (vl.type_instance, "send-accepted",
                        sizeof (vl.type_instance));
-       plugin_dispatch_values_secure (&vl);
+       plugin_dispatch_values (&vl);
 
        vl.values[0].derive = (derive_t) copy_values_not_sent;
        sstrncpy (vl.type_instance, "send-rejected",
                        sizeof (vl.type_instance));
-       plugin_dispatch_values_secure (&vl);
+       plugin_dispatch_values (&vl);
 
        /* Receive queue length */
        vl.values[0].gauge = (gauge_t) copy_receive_list_length;
        sstrncpy (vl.type, "queue_length", sizeof (vl.type));
        vl.type_instance[0] = 0;
-       plugin_dispatch_values_secure (&vl);
+       plugin_dispatch_values (&vl);
 
        return (0);
 } /* }}} int network_stats_read */
@@ -3370,17 +3388,7 @@ static int network_init (void)
        have_init = 1;
 
 #if HAVE_LIBGCRYPT
-    /* 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);
-    }
+       network_init_gcrypt ();
 #endif
 
        if (network_config_stats != 0)
index 461e806..f5b579e 100644 (file)
--- a/src/nfs.c
+++ b/src/nfs.c
@@ -1,6 +1,7 @@
 /**
  * collectd - src/nfs.c
  * Copyright (C) 2005,2006  Jason Pepas
+ * Copyright (C) 2012,2013  Florian Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -17,7 +18,7 @@
  *
  * Authors:
  *   Jason Pepas <cell at ices.utexas.edu>
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Cosmin Ioiart <cioiart at gmail.com>
  **/
 
@@ -246,7 +247,7 @@ static void nfs_procedures_submit (const char *plugin_instance,
                vl.values = values + i;
                sstrncpy (vl.type_instance, type_instances[i],
                                sizeof (vl.type_instance));
-               plugin_dispatch_values_secure (&vl);
+               plugin_dispatch_values (&vl);
        }
 } /* void nfs_procedures_submit */
 
@@ -318,7 +319,7 @@ static void nfs_read_linux (FILE *fh, 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)
+               char const **proc_names, size_t proc_names_num)
 {
        char plugin_instance[DATA_MAX_NAME_LEN];
        value_t values[proc_names_num];
@@ -332,8 +333,14 @@ static int nfs_read_kstat (kstat_t *ksp, int nfs_version, char *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]);
+       {
+               /* The name passed to kstat_data_lookup() doesn't have the
+                * "const" modifier, so we need to copy the name here. */
+               char name[32];
+               sstrncpy (name, proc_names[i], sizeof (name));
+
+               values[i].counter = (derive_t) get_kstat_value (ksp, name);
+       }
 
        nfs_procedures_submit (plugin_instance, proc_names, values,
                        proc_names_num);
index 3e162ba..c356353 100644 (file)
@@ -120,9 +120,9 @@ static int init (void)
     return (-1);
   }
 
-  curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
+  curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, nginx_curl_callback);
-  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
   curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, nginx_curl_error);
 
   if (user != NULL)
@@ -143,24 +143,25 @@ static int init (void)
     curl_easy_setopt (curl, CURLOPT_URL, url);
   }
 
-  curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
+  curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L);
+  curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 50L);
 
   if ((verify_peer == NULL) || IS_TRUE (verify_peer))
   {
-    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1);
+    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1L);
   }
   else
   {
-    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0);
+    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
   }
 
   if ((verify_host == NULL) || IS_TRUE (verify_host))
   {
-    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2);
+    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2L);
   }
   else
   {
-    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0);
+    curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
   }
 
   if (cacert != NULL)
@@ -180,6 +181,8 @@ static void submit (char *type, char *inst, long long value)
     values[0].gauge = value;
   else if (strcmp (type, "nginx_requests") == 0)
     values[0].derive = value;
+  else if (strcmp (type, "connections") == 0)
+    values[0].derive = value;
   else
     return;
 
@@ -214,7 +217,7 @@ static int nginx_read (void)
     return (-1);
 
   nginx_buffer_len = 0;
-  if (curl_easy_perform (curl) != 0)
+  if (curl_easy_perform (curl) != CURLE_OK)
   {
     WARNING ("nginx plugin: curl_easy_perform failed: %s", nginx_curl_error);
     return (-1);
@@ -253,6 +256,8 @@ static int nginx_read (void)
          && (atoll (fields[1]) != 0)
          && (atoll (fields[2]) != 0))
       {
+       submit ("connections", "accepted", atoll (fields[0]));
+       submit ("connections", "handled", atoll (fields[1]));
        submit ("nginx_requests", NULL, atoll (fields[2]));
       }
     }
index cd216ca..24c66ec 100644 (file)
@@ -258,7 +258,7 @@ static int notify_email_notification (const notification_t *n,
   /* Let's make RFC822 message text with \r\n EOLs */
   ssnprintf (buf, buf_len,
       "MIME-Version: 1.0\r\n"
-      "Content-Type: text/plain;\r\n"
+      "Content-Type: text/plain; charset=\"US-ASCII\"\r\n"
       "Content-Transfer-Encoding: 8bit\r\n"
       "Subject: %s\r\n"
       "\r\n"
index 09a6bf0..1b7aee6 100644 (file)
@@ -44,7 +44,7 @@ typedef struct ow_family_features_s ow_family_features_t;
 /* see http://owfs.sourceforge.net/ow_table.html for a list of families */
 static ow_family_features_t ow_family_features[] =
 {
-  {
+  { /* DS18S20 Precision Thermometer and DS1920 ibutton */
     /* family = */ "10.",
     {
       {
@@ -54,6 +54,50 @@ static ow_family_features_t ow_family_features[] =
       }
     },
     /* features_num = */ 1
+  },
+  { /* DS1822 Econo Thermometer */
+    /* family = */ "22.",
+    {
+      {
+        /* filename = */ "temperature",
+        /* type = */ "temperature",
+        /* type_instance = */ ""
+      }
+    },
+    /* features_num = */ 1
+  },
+  { /* DS18B20 Programmable Resolution Thermometer */
+    /* family = */ "28.",
+    {
+      {
+        /* filename = */ "temperature",
+        /* type = */ "temperature",
+        /* type_instance = */ ""
+      }
+    },
+    /* features_num = */ 1
+  },
+  { /* DS2436 Volts/Temp */
+    /* family = */ "1B.",
+    {
+      {
+        /* filename = */ "temperature",
+        /* type = */ "temperature",
+        /* type_instance = */ ""
+      }
+    },
+    /* features_num = */ 1
+  },
+  { /* DS2438 Volts/Temp */
+    /* family = */ "26.",
+    {
+      {
+        /* filename = */ "temperature",
+        /* type = */ "temperature",
+        /* type_instance = */ ""
+      }
+    },
+    /* features_num = */ 1
   }
 };
 static int ow_family_features_num = STATIC_ARRAY_SIZE (ow_family_features);
index da539a2..44f0c7b 100644 (file)
--- a/src/pf.c
+++ b/src/pf.c
 #include "plugin.h"
 #include "common.h"
 
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <net/if.h>
+#if HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+
 #include <net/pfvar.h>
-#include <paths.h>
-#include <err.h>
-#include <pwd.h>
+
+#ifndef FCNT_NAMES
+# if FCNT_MAX != 3
+#  error "Unexpected value for FCNT_MAX"
+# endif
+# define FCNT_NAMES {"search", "insert", "removals", NULL};
+#endif
+
+#ifndef SCNT_NAMES
+# if SCNT_MAX != 3
+#  error "Unexpected value for SCNT_MAX"
+# endif
+# define SCNT_NAMES {"search", "insert", "removals", NULL};
+#endif
 
 static char const *pf_reasons[PFRES_MAX+1] = PFRES_NAMES;
 static char const *pf_lcounters[LCNT_MAX+1] = LCNT_NAMES;
 static char const *pf_fcounters[FCNT_MAX+1] = FCNT_NAMES;
-static char const *pf_scounters[FCNT_MAX+1] = FCNT_NAMES;
+static char const *pf_scounters[SCNT_MAX+1] = SCNT_NAMES;
 
 static char const *pf_device = "/dev/pf";
 
@@ -86,7 +104,7 @@ static int pf_read (void)
        close (fd);
        fd = -1;
 
-       if (!status.running)
+       if (!state.running)
        {
                WARNING ("pf plugin: PF is not running.");
                return (-1);
index 6879733..e7bf2aa 100644 (file)
 
 #include "pinba.pb-c.h"
 
+/* AIX doesn't have MSG_DONTWAIT */
+#ifndef MSG_DONTWAIT
+#  define MSG_DONTWAIT MSG_NONBLOCK
+#endif
+
 /*
  * Defines
  */
index bbede05..5f1ca55 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/plugin.c
- * Copyright (C) 2005-2011  Florian octo Forster
+ * Copyright (C) 2005-2014  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  **/
 
 #include "collectd.h"
-#include "utils_complain.h"
-
-#include <ltdl.h>
-
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
-
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
+#include "filter_chain.h"
 #include "utils_avltree.h"
+#include "utils_cache.h"
+#include "utils_complain.h"
 #include "utils_llist.h"
 #include "utils_heap.h"
-#include "utils_cache.h"
-#include "filter_chain.h"
+#include "utils_time.h"
+#include "utils_random.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+#include <ltdl.h>
 
 /*
  * Private structures
@@ -61,17 +62,28 @@ struct read_func_s
 #define rf_ctx rf_super.cf_ctx
        callback_func_t rf_super;
        char rf_group[DATA_MAX_NAME_LEN];
-       char rf_name[DATA_MAX_NAME_LEN];
+       char *rf_name;
        int rf_type;
-       struct timespec rf_interval;
-       struct timespec rf_effective_interval;
-       struct timespec rf_next_read;
+       cdtime_t rf_interval;
+       cdtime_t rf_effective_interval;
+       cdtime_t rf_next_read;
 };
 typedef struct read_func_s read_func_t;
 
+struct write_queue_s;
+typedef struct write_queue_s write_queue_t;
+struct write_queue_s
+{
+       value_list_t *vl;
+       plugin_ctx_t ctx;
+       write_queue_t *next;
+};
+
 /*
  * Private variables
  */
+static c_avl_tree_t *plugins_loaded = NULL;
+
 static llist_t *list_init;
 static llist_t *list_write;
 static llist_t *list_flush;
@@ -95,12 +107,26 @@ static pthread_cond_t  read_cond = PTHREAD_COND_INITIALIZER;
 static pthread_t      *read_threads = NULL;
 static int             read_threads_num = 0;
 
+static write_queue_t  *write_queue_head;
+static write_queue_t  *write_queue_tail;
+static long            write_queue_length = 0;
+static _Bool           write_loop = 1;
+static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  write_cond = PTHREAD_COND_INITIALIZER;
+static pthread_t      *write_threads = NULL;
+static size_t          write_threads_num = 0;
+
 static pthread_key_t   plugin_ctx_key;
 static _Bool           plugin_ctx_key_initialized = 0;
 
+static long            write_limit_high = 0;
+static long            write_limit_low = 0;
+
 /*
  * Static functions
  */
+static int plugin_dispatch_values_internal (value_list_t *vl);
+
 static const char *plugin_get_dir (void)
 {
        if (plugindir == NULL)
@@ -341,13 +367,6 @@ static int plugin_load_file (char *file, uint32_t flags)
        return (0);
 }
 
-static _Bool timeout_reached(struct timespec timeout)
-{
-       struct timeval now;
-       gettimeofday(&now, NULL);
-       return (now.tv_sec >= timeout.tv_sec && now.tv_usec >= (timeout.tv_nsec / 1000));
-}
-
 static void *plugin_read_thread (void __attribute__((unused)) *args)
 {
        while (read_loop != 0)
@@ -373,18 +392,15 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                }
                pthread_mutex_unlock (&read_lock);
 
-               if ((rf->rf_interval.tv_sec == 0) && (rf->rf_interval.tv_nsec == 0))
+               if (rf->rf_interval == 0)
                {
                        /* this should not happen, because the interval is set
                         * for each plugin when loading it
                         * XXX: issue a warning? */
-                       now = cdtime ();
-
-                       CDTIME_T_TO_TIMESPEC (plugin_get_interval (), &rf->rf_interval);
-
+                       rf->rf_interval = plugin_get_interval ();
                        rf->rf_effective_interval = rf->rf_interval;
 
-                       CDTIME_T_TO_TIMESPEC (now, &rf->rf_next_read);
+                       rf->rf_next_read = cdtime ();
                }
 
                /* sleep until this entry is due,
@@ -396,11 +412,15 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                 * pthread_cond_timedwait returns. */
                rc = 0;
                while ((read_loop != 0)
-                               && !timeout_reached(rf->rf_next_read)
+                               && (cdtime () < rf->rf_next_read)
                                && rc == 0)
                {
+                       struct timespec ts = { 0 };
+
+                       CDTIME_T_TO_TIMESPEC (rf->rf_next_read, &ts);
+
                        rc = pthread_cond_timedwait (&read_cond, &read_lock,
-                               &rf->rf_next_read);
+                               &ts);
                }
 
                /* Must hold `read_lock' when accessing `rf->rf_type'. */
@@ -424,6 +444,7 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                {
                        DEBUG ("plugin_read_thread: Destroying the `%s' "
                                        "callback.", rf->rf_name);
+                       sfree (rf->rf_name);
                        destroy_callback ((callback_func_t *) rf);
                        rf = NULL;
                        continue;
@@ -456,20 +477,14 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                 * intervals in which it will be called. */
                if (status != 0)
                {
-                       rf->rf_effective_interval.tv_sec *= 2;
-                       rf->rf_effective_interval.tv_nsec *= 2;
-                       NORMALIZE_TIMESPEC (rf->rf_effective_interval);
-
-                       if (rf->rf_effective_interval.tv_sec >= 86400)
-                       {
-                               rf->rf_effective_interval.tv_sec = 86400;
-                               rf->rf_effective_interval.tv_nsec = 0;
-                       }
+                       rf->rf_effective_interval *= 2;
+                       if (rf->rf_effective_interval > TIME_T_TO_CDTIME_T (86400))
+                               rf->rf_effective_interval = TIME_T_TO_CDTIME_T (86400);
 
                        NOTICE ("read-function of plugin `%s' failed. "
-                                       "Will suspend it for %i seconds.",
+                                       "Will suspend it for %.3f seconds.",
                                        rf->rf_name,
-                                       (int) rf->rf_effective_interval.tv_sec);
+                                       CDTIME_T_TO_DOUBLE (rf->rf_effective_interval));
                }
                else
                {
@@ -481,32 +496,26 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                now = cdtime ();
 
                DEBUG ("plugin_read_thread: Effective interval of the "
-                               "%s plugin is %i.%09i.",
+                               "%s plugin is %.3f seconds.",
                                rf->rf_name,
-                               (int) rf->rf_effective_interval.tv_sec,
-                               (int) rf->rf_effective_interval.tv_nsec);
+                               CDTIME_T_TO_DOUBLE (rf->rf_effective_interval));
 
                /* Calculate the next (absolute) time at which this function
                 * should be called. */
-               rf->rf_next_read.tv_sec = rf->rf_next_read.tv_sec
-                       + rf->rf_effective_interval.tv_sec;
-               rf->rf_next_read.tv_nsec = rf->rf_next_read.tv_nsec
-                       + rf->rf_effective_interval.tv_nsec;
-               NORMALIZE_TIMESPEC (rf->rf_next_read);
+               rf->rf_next_read += rf->rf_effective_interval;
 
                /* Check, if `rf_next_read' is in the past. */
-               if (TIMESPEC_TO_CDTIME_T (&rf->rf_next_read) < now)
+               if (rf->rf_next_read < now)
                {
                        /* `rf_next_read' is in the past. Insert `now'
                         * so this value doesn't trail off into the
                         * past too much. */
-                       CDTIME_T_TO_TIMESPEC (now, &rf->rf_next_read);
+                       rf->rf_next_read = now;
                }
 
-               DEBUG ("plugin_read_thread: Next read of the %s plugin at %i.%09i.",
+               DEBUG ("plugin_read_thread: Next read of the %s plugin at %.3f.",
                                rf->rf_name,
-                               (int) rf->rf_next_read.tv_sec,
-                               (int) rf->rf_next_read.tv_nsec);
+                               CDTIME_T_TO_DOUBLE (rf->rf_next_read));
 
                /* Re-insert this read function into the heap again. */
                c_heap_insert (read_heap, rf);
@@ -573,6 +582,250 @@ static void stop_read_threads (void)
        read_threads_num = 0;
 } /* void stop_read_threads */
 
+static void plugin_value_list_free (value_list_t *vl) /* {{{ */
+{
+       if (vl == NULL)
+               return;
+
+       meta_data_destroy (vl->meta);
+       sfree (vl->values);
+       sfree (vl);
+} /* }}} void plugin_value_list_free */
+
+static value_list_t *plugin_value_list_clone (value_list_t const *vl_orig) /* {{{ */
+{
+       value_list_t *vl;
+
+       if (vl_orig == NULL)
+               return (NULL);
+
+       vl = malloc (sizeof (*vl));
+       if (vl == NULL)
+               return (NULL);
+       memcpy (vl, vl_orig, sizeof (*vl));
+
+       vl->values = calloc (vl_orig->values_len, sizeof (*vl->values));
+       if (vl->values == NULL)
+       {
+               plugin_value_list_free (vl);
+               return (NULL);
+       }
+       memcpy (vl->values, vl_orig->values,
+                       vl_orig->values_len * sizeof (*vl->values));
+
+       vl->meta = meta_data_clone (vl->meta);
+       if ((vl_orig->meta != NULL) && (vl->meta == NULL))
+       {
+               plugin_value_list_free (vl);
+               return (NULL);
+       }
+
+       if (vl->time == 0)
+               vl->time = cdtime ();
+
+       /* Fill in the interval from the thread context, if it is zero. */
+       if (vl->interval == 0)
+       {
+               plugin_ctx_t ctx = plugin_get_ctx ();
+
+               if (ctx.interval != 0)
+                       vl->interval = ctx.interval;
+               else
+               {
+                       char name[6 * DATA_MAX_NAME_LEN];
+                       FORMAT_VL (name, sizeof (name), vl);
+                       ERROR ("plugin_value_list_clone: Unable to determine "
+                                       "interval from context for "
+                                       "value list \"%s\". "
+                                       "This indicates a broken plugin. "
+                                       "Please report this problem to the "
+                                       "collectd mailing list or at "
+                                       "<http://collectd.org/bugs/>.", name);
+                       vl->interval = cf_get_default_interval ();
+               }
+       }
+
+       return (vl);
+} /* }}} value_list_t *plugin_value_list_clone */
+
+static int plugin_write_enqueue (value_list_t const *vl) /* {{{ */
+{
+       write_queue_t *q;
+
+       q = malloc (sizeof (*q));
+       if (q == NULL)
+               return (ENOMEM);
+       q->next = NULL;
+
+       q->vl = plugin_value_list_clone (vl);
+       if (q->vl == NULL)
+       {
+               sfree (q);
+               return (ENOMEM);
+       }
+
+       /* Store context of caller (read plugin); otherwise, it would not be
+        * available to the write plugins when actually dispatching the
+        * value-list later on. */
+       q->ctx = plugin_get_ctx ();
+
+       pthread_mutex_lock (&write_lock);
+
+       if (write_queue_tail == NULL)
+       {
+               write_queue_head = q;
+               write_queue_tail = q;
+               write_queue_length = 1;
+       }
+       else
+       {
+               write_queue_tail->next = q;
+               write_queue_tail = q;
+               write_queue_length += 1;
+       }
+
+       pthread_cond_signal (&write_cond);
+       pthread_mutex_unlock (&write_lock);
+
+       return (0);
+} /* }}} int plugin_write_enqueue */
+
+static value_list_t *plugin_write_dequeue (void) /* {{{ */
+{
+       write_queue_t *q;
+       value_list_t *vl;
+
+       pthread_mutex_lock (&write_lock);
+
+       while (write_loop && (write_queue_head == NULL))
+               pthread_cond_wait (&write_cond, &write_lock);
+
+       if (write_queue_head == NULL)
+       {
+               pthread_mutex_unlock (&write_lock);
+               return (NULL);
+       }
+
+       q = write_queue_head;
+       write_queue_head = q->next;
+       write_queue_length -= 1;
+       if (write_queue_head == NULL) {
+               write_queue_tail = NULL;
+               assert(0 == write_queue_length);
+               }
+
+       pthread_mutex_unlock (&write_lock);
+
+       (void) plugin_set_ctx (q->ctx);
+
+       vl = q->vl;
+       sfree (q);
+       return (vl);
+} /* }}} value_list_t *plugin_write_dequeue */
+
+static void *plugin_write_thread (void __attribute__((unused)) *args) /* {{{ */
+{
+       while (write_loop)
+       {
+               value_list_t *vl = plugin_write_dequeue ();
+               if (vl == NULL)
+                       continue;
+
+               plugin_dispatch_values_internal (vl);
+
+               plugin_value_list_free (vl);
+       }
+
+       pthread_exit (NULL);
+       return ((void *) 0);
+} /* }}} void *plugin_write_thread */
+
+static void start_write_threads (size_t num) /* {{{ */
+{
+       size_t i;
+
+       if (write_threads != NULL)
+               return;
+
+       write_threads = (pthread_t *) calloc (num, sizeof (pthread_t));
+       if (write_threads == NULL)
+       {
+               ERROR ("plugin: start_write_threads: calloc failed.");
+               return;
+       }
+
+       write_threads_num = 0;
+       for (i = 0; i < num; i++)
+       {
+               int status;
+
+               status = pthread_create (write_threads + write_threads_num,
+                               /* attr = */ NULL,
+                               plugin_write_thread,
+                               /* arg = */ NULL);
+               if (status != 0)
+               {
+                       char errbuf[1024];
+                       ERROR ("plugin: start_write_threads: pthread_create failed "
+                                       "with status %i (%s).", status,
+                                       sstrerror (status, errbuf, sizeof (errbuf)));
+                       return;
+               }
+
+               write_threads_num++;
+       } /* for (i) */
+} /* }}} void start_write_threads */
+
+static void stop_write_threads (void) /* {{{ */
+{
+       write_queue_t *q;
+       int i;
+
+       if (write_threads == NULL)
+               return;
+
+       INFO ("collectd: Stopping %zu write threads.", write_threads_num);
+
+       pthread_mutex_lock (&write_lock);
+       write_loop = 0;
+       DEBUG ("plugin: stop_write_threads: Signalling `write_cond'");
+       pthread_cond_broadcast (&write_cond);
+       pthread_mutex_unlock (&write_lock);
+
+       for (i = 0; i < write_threads_num; i++)
+       {
+               if (pthread_join (write_threads[i], NULL) != 0)
+               {
+                       ERROR ("plugin: stop_write_threads: pthread_join failed.");
+               }
+               write_threads[i] = (pthread_t) 0;
+       }
+       sfree (write_threads);
+       write_threads_num = 0;
+
+       pthread_mutex_lock (&write_lock);
+       i = 0;
+       for (q = write_queue_head; q != NULL; )
+       {
+               write_queue_t *q1 = q;
+               plugin_value_list_free (q->vl);
+               q = q->next;
+               sfree (q1);
+               i++;
+       }
+       write_queue_head = NULL;
+       write_queue_tail = NULL;
+       write_queue_length = 0;
+       pthread_mutex_unlock (&write_lock);
+
+       if (i > 0)
+       {
+               WARNING ("plugin: %i value list%s left after shutting down "
+                               "the write threads.",
+                               i, (i == 1) ? " was" : "s were");
+       }
+} /* }}} void stop_write_threads */
+
 /*
  * Public functions
  */
@@ -591,8 +844,52 @@ void plugin_set_dir (const char *dir)
        }
 }
 
+static _Bool plugin_is_loaded (char const *name)
+{
+       int status;
+
+       if (plugins_loaded == NULL)
+               plugins_loaded = c_avl_create ((void *) strcasecmp);
+       assert (plugins_loaded != NULL);
+
+       status = c_avl_get (plugins_loaded, name, /* ret_value = */ NULL);
+       return (status == 0);
+}
+
+static int plugin_mark_loaded (char const *name)
+{
+       char *name_copy;
+       int status;
+
+       name_copy = strdup (name);
+       if (name_copy == NULL)
+               return (ENOMEM);
+
+       status = c_avl_insert (plugins_loaded,
+                       /* key = */ name_copy, /* value = */ NULL);
+       return (status);
+}
+
+static void plugin_free_loaded ()
+{
+       void *key;
+       void *value;
+
+       if (plugins_loaded == NULL)
+               return;
+
+       while (c_avl_pick (plugins_loaded, &key, &value) == 0)
+       {
+               sfree (key);
+               assert (value == NULL);
+       }
+
+       c_avl_destroy (plugins_loaded);
+       plugins_loaded = NULL;
+}
+
 #define BUFSIZE 512
-int plugin_load (const char *type, uint32_t flags)
+int plugin_load (char const *plugin_name, uint32_t flags)
 {
        DIR  *dh;
        const char *dir;
@@ -604,17 +901,38 @@ int plugin_load (const char *type, uint32_t flags)
        struct dirent *de;
        int status;
 
-       DEBUG ("type = %s", type);
+       if (plugin_name == NULL)
+               return (EINVAL);
+
+       /* Check if plugin is already loaded and don't do anything in this
+        * case. */
+       if (plugin_is_loaded (plugin_name))
+               return (0);
 
        dir = plugin_get_dir ();
        ret = 1;
 
+       /*
+        * XXX: Magic at work:
+        *
+        * Some of the language bindings, for example the Python and Perl
+        * plugins, need to be able to export symbols to the scripts they run.
+        * For this to happen, the "Globals" flag needs to be set.
+        * Unfortunately, this technical detail is hard to explain to the
+        * average user and she shouldn't have to worry about this, ideally.
+        * So in order to save everyone's sanity use a different default for a
+        * handful of special plugins. --octo
+        */
+       if ((strcasecmp ("perl", plugin_name) == 0)
+                       || (strcasecmp ("python", plugin_name) == 0))
+               flags |= PLUGIN_FLAGS_GLOBAL;
+
        /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
         * type when matching the filename */
-       status = ssnprintf (typename, sizeof (typename), "%s.so", type);
+       status = ssnprintf (typename, sizeof (typename), "%s.so", plugin_name);
        if ((status < 0) || ((size_t) status >= sizeof (typename)))
        {
-               WARNING ("snprintf: truncated: `%s.so'", type);
+               WARNING ("plugin_load: Filename too long: \"%s.so\"", plugin_name);
                return (-1);
        }
        typename_len = strlen (typename);
@@ -622,7 +940,7 @@ int plugin_load (const char *type, uint32_t flags)
        if ((dh = opendir (dir)) == NULL)
        {
                char errbuf[1024];
-               ERROR ("opendir (%s): %s", dir,
+               ERROR ("plugin_load: opendir (%s) failed: %s", dir,
                                sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
@@ -636,40 +954,47 @@ int plugin_load (const char *type, uint32_t flags)
                                "%s/%s", dir, de->d_name);
                if ((status < 0) || ((size_t) status >= sizeof (filename)))
                {
-                       WARNING ("snprintf: truncated: `%s/%s'", dir, de->d_name);
+                       WARNING ("plugin_load: Filename too long: \"%s/%s\"",
+                                       dir, de->d_name);
                        continue;
                }
 
                if (lstat (filename, &statbuf) == -1)
                {
                        char errbuf[1024];
-                       WARNING ("stat %s: %s", filename,
+                       WARNING ("plugin_load: stat (\"%s\") failed: %s",
+                                       filename,
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
                        continue;
                }
                else if (!S_ISREG (statbuf.st_mode))
                {
                        /* don't follow symlinks */
-                       WARNING ("stat %s: not a regular file", filename);
+                       WARNING ("plugin_load: %s is not a regular file.",
+                                       filename);
                        continue;
                }
 
-               if (plugin_load_file (filename, flags) == 0)
+               status = plugin_load_file (filename, flags);
+               if (status == 0)
                {
                        /* success */
+                       plugin_mark_loaded (plugin_name);
                        ret = 0;
                        break;
                }
                else
                {
-                       fprintf (stderr, "Unable to load plugin %s.\n", type);
+                       ERROR ("plugin_load: Load plugin \"%s\" failed with "
+                                       "status %i.", plugin_name, status);
                }
        }
 
        closedir (dh);
 
-       if (filename[0] == '\0')
-               fprintf (stderr, "Could not find plugin %s.\n", type);
+       if (filename[0] == 0)
+               ERROR ("plugin_load: Could not find plugin \"%s\" in %s",
+                               plugin_name, dir);
 
        return (ret);
 }
@@ -706,13 +1031,9 @@ static int plugin_compare_read_func (const void *arg0, const void *arg1)
        rf0 = arg0;
        rf1 = arg1;
 
-       if (rf0->rf_next_read.tv_sec < rf1->rf_next_read.tv_sec)
+       if (rf0->rf_next_read < rf1->rf_next_read)
                return (-1);
-       else if (rf0->rf_next_read.tv_sec > rf1->rf_next_read.tv_sec)
-               return (1);
-       else if (rf0->rf_next_read.tv_nsec < rf1->rf_next_read.tv_nsec)
-               return (-1);
-       else if (rf0->rf_next_read.tv_nsec > rf1->rf_next_read.tv_nsec)
+       else if (rf0->rf_next_read > rf1->rf_next_read)
                return (1);
        else
                return (0);
@@ -726,6 +1047,9 @@ static int plugin_insert_read (read_func_t *rf)
        int status;
        llentry_t *le;
 
+       rf->rf_next_read = cdtime ();
+       rf->rf_effective_interval = rf->rf_interval;
+
        pthread_mutex_lock (&read_lock);
 
        if (read_list == NULL)
@@ -787,43 +1111,12 @@ static int plugin_insert_read (read_func_t *rf)
        return (0);
 } /* int plugin_insert_read */
 
-static int read_cb_wrapper (user_data_t *ud)
-{
-       int (*callback) (void);
-
-       if (ud == NULL)
-               return -1;
-
-       callback = ud->data;
-       return callback();
-} /* int read_cb_wrapper */
-
 int plugin_register_read (const char *name,
                int (*callback) (void))
 {
        read_func_t *rf;
-       plugin_ctx_t ctx = plugin_get_ctx ();
        int status;
 
-       if (ctx.interval != 0) {
-               /* If ctx.interval is not zero (== use the plugin or global
-                * interval), we need to use the "complex" read callback,
-                * because only that allows to specify a different interval.
-                * Wrap the callback using read_cb_wrapper(). */
-               struct timespec interval;
-               user_data_t user_data;
-
-               user_data.data = callback;
-               user_data.free_func = NULL;
-
-               CDTIME_T_TO_TIMESPEC (ctx.interval, &interval);
-               return plugin_register_complex_read (/* group = */ NULL,
-                               name, read_cb_wrapper, &interval, &user_data);
-       }
-
-       DEBUG ("plugin_register_read: default_interval = %.3f",
-                       CDTIME_T_TO_DOUBLE(plugin_get_interval ()));
-
        rf = malloc (sizeof (*rf));
        if (rf == NULL)
        {
@@ -835,13 +1128,11 @@ int plugin_register_read (const char *name,
        rf->rf_callback = (void *) callback;
        rf->rf_udata.data = NULL;
        rf->rf_udata.free_func = NULL;
-       rf->rf_ctx = ctx;
+       rf->rf_ctx = plugin_get_ctx ();
        rf->rf_group[0] = '\0';
-       sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
+       rf->rf_name = strdup (name);
        rf->rf_type = RF_SIMPLE;
-       rf->rf_interval.tv_sec = 0;
-       rf->rf_interval.tv_nsec = 0;
-       rf->rf_effective_interval = rf->rf_interval;
+       rf->rf_interval = plugin_get_interval ();
 
        status = plugin_insert_read (rf);
        if (status != 0)
@@ -856,7 +1147,6 @@ int plugin_register_complex_read (const char *group, const char *name,
                user_data_t *user_data)
 {
        read_func_t *rf;
-       plugin_ctx_t ctx = plugin_get_ctx ();
        int status;
 
        rf = malloc (sizeof (*rf));
@@ -872,21 +1162,12 @@ int plugin_register_complex_read (const char *group, const char *name,
                sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
        else
                rf->rf_group[0] = '\0';
-       sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
+       rf->rf_name = strdup (name);
        rf->rf_type = RF_COMPLEX;
        if (interval != NULL)
-       {
-               rf->rf_interval = *interval;
-       }
-       else if (ctx.interval != 0)
-       {
-               CDTIME_T_TO_TIMESPEC (ctx.interval, &rf->rf_interval);
-       }
-       rf->rf_effective_interval = rf->rf_interval;
-
-       DEBUG ("plugin_register_read: interval = %i.%09i",
-                       (int) rf->rf_interval.tv_sec,
-                       (int) rf->rf_interval.tv_nsec);
+               rf->rf_interval = TIMESPEC_TO_CDTIME_T (interval);
+       else
+               rf->rf_interval = plugin_get_interval ();
 
        /* Set user data */
        if (user_data == NULL)
@@ -899,7 +1180,7 @@ int plugin_register_complex_read (const char *group, const char *name,
                rf->rf_udata = *user_data;
        }
 
-       rf->rf_ctx = ctx;
+       rf->rf_ctx = plugin_get_ctx ();
 
        status = plugin_insert_read (rf);
        if (status != 0)
@@ -936,6 +1217,27 @@ int plugin_register_shutdown (const char *name,
                                (void *) callback, /* user_data = */ NULL));
 } /* int plugin_register_shutdown */
 
+static void plugin_free_data_sets (void)
+{
+       void *key;
+       void *value;
+
+       if (data_sets == NULL)
+               return;
+
+       while (c_avl_pick (data_sets, &key, &value) == 0)
+       {
+               data_set_t *ds = value;
+               /* key is a pointer to ds->type */
+
+               sfree (ds->ds);
+               sfree (ds);
+       }
+
+       c_avl_destroy (data_sets);
+       data_sets = NULL;
+} /* void plugin_free_data_sets */
+
 int plugin_register_data_set (const data_set_t *ds)
 {
        data_set_t *ds_copy;
@@ -1153,7 +1455,8 @@ int plugin_unregister_notification (const char *name)
 
 void plugin_init_all (void)
 {
-       const char *chain_name;
+       char const *chain_name;
+       long write_threads_num;
        llentry_t *le;
        int status;
 
@@ -1166,6 +1469,37 @@ void plugin_init_all (void)
        chain_name = global_option_get ("PostCacheChain");
        post_cache_chain = fc_chain_get_by_name (chain_name);
 
+       write_limit_high = global_option_get_long ("WriteQueueLimitHigh",
+                       /* default = */ 0);
+       if (write_limit_high < 0)
+       {
+               ERROR ("WriteQueueLimitHigh must be positive or zero.");
+               write_limit_high = 0;
+       }
+
+       write_limit_low = global_option_get_long ("WriteQueueLimitLow",
+                       /* default = */ write_limit_high / 2);
+       if (write_limit_low < 0)
+       {
+               ERROR ("WriteQueueLimitLow must be positive or zero.");
+               write_limit_low = write_limit_high / 2;
+       }
+       else if (write_limit_low > write_limit_high)
+       {
+               ERROR ("WriteQueueLimitLow must not be larger than "
+                               "WriteQueueLimitHigh.");
+               write_limit_low = write_limit_high;
+       }
+
+       write_threads_num = global_option_get_long ("WriteThreads",
+                       /* default = */ 5);
+       if (write_threads_num < 1)
+       {
+               ERROR ("WriteThreads must be positive.");
+               write_threads_num = 5;
+       }
+
+       start_write_threads ((size_t) write_threads_num);
 
        if ((list_init == NULL) && (read_heap == NULL))
                return;
@@ -1435,6 +1769,8 @@ void plugin_shutdown_all (void)
                plugin_set_ctx (old_ctx);
        }
 
+       stop_write_threads ();
+
        /* Write plugins which use the `user_data' pointer usually need the
         * same data available to the flush callback. If this is the case, set
         * the free_function to NULL when registering the flush callback and to
@@ -1447,6 +1783,9 @@ void plugin_shutdown_all (void)
        destroy_all_callbacks (&list_notification);
        destroy_all_callbacks (&list_shutdown);
        destroy_all_callbacks (&list_log);
+
+       plugin_free_loaded ();
+       plugin_free_data_sets ();
 } /* void plugin_shutdown_all */
 
 int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
@@ -1490,7 +1829,7 @@ int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
   return (0);
 } /* int }}} plugin_dispatch_missing */
 
-int plugin_dispatch_values (value_list_t *vl)
+static int plugin_dispatch_values_internal (value_list_t *vl)
 {
        int status;
        static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC;
@@ -1541,29 +1880,10 @@ int plugin_dispatch_values (value_list_t *vl)
                return (-1);
        }
 
-       if (vl->time == 0)
-               vl->time = cdtime ();
-
-       if (vl->interval <= 0)
-       {
-               plugin_ctx_t ctx = plugin_get_ctx ();
-
-               if (ctx.interval != 0)
-                       vl->interval = ctx.interval;
-               else
-               {
-                       char name[6 * DATA_MAX_NAME_LEN];
-                       FORMAT_VL (name, sizeof (name), vl);
-                       ERROR ("plugin_dispatch_values: Unable to determine "
-                                       "interval from context for "
-                                       "value list \"%s\". "
-                                       "This indicates a broken plugin. "
-                                       "Please report this problem to the "
-                                       "collectd mailing list or at "
-                                       "<http://collectd.org/bugs/>.", name);
-                       vl->interval = cf_get_default_interval ();
-               }
-       }
+       /* Assured by plugin_value_list_clone(). The time is determined at
+        * _enqueue_ time. */
+       assert (vl->time != 0);
+       assert (vl->interval != 0);
 
        DEBUG ("plugin_dispatch_values: time = %.3f; interval = %.3f; "
                        "host = %s; "
@@ -1684,53 +2004,149 @@ int plugin_dispatch_values (value_list_t *vl)
        }
 
        return (0);
-} /* int plugin_dispatch_values */
+} /* int plugin_dispatch_values_internal */
 
-int plugin_dispatch_values_secure (const value_list_t *vl)
+static double get_drop_probability (void) /* {{{ */
 {
-  value_list_t vl_copy;
-  int status;
+       long pos;
+       long size;
+       long wql;
 
-  if (vl == NULL)
-    return EINVAL;
+       pthread_mutex_lock (&write_lock);
+       wql = write_queue_length;
+       pthread_mutex_unlock (&write_lock);
 
-  memcpy (&vl_copy, vl, sizeof (vl_copy));
+       if (wql < write_limit_low)
+               return (0.0);
+       if (wql >= write_limit_high)
+               return (1.0);
 
-  /* Write callbacks must not change the values and meta pointers, so we can
-   * savely skip copying those and make this more efficient. */
-  if ((pre_cache_chain == NULL) && (post_cache_chain == NULL))
-    return (plugin_dispatch_values (&vl_copy));
+       pos = 1 + wql - write_limit_low;
+       size = 1 + write_limit_high - write_limit_low;
 
-  /* Set pointers to NULL, just to be on the save side. */
-  vl_copy.values = NULL;
-  vl_copy.meta = NULL;
+       return (((double) pos) / ((double) size));
+} /* }}} double get_drop_probability */
 
-  vl_copy.values = malloc (sizeof (*vl_copy.values) * vl->values_len);
-  if (vl_copy.values == NULL)
-  {
-    ERROR ("plugin_dispatch_values_secure: malloc failed.");
-    return (ENOMEM);
-  }
-  memcpy (vl_copy.values, vl->values, sizeof (*vl_copy.values) * vl->values_len);
+static _Bool check_drop_value (void) /* {{{ */
+{
+       static cdtime_t last_message_time = 0;
+       static pthread_mutex_t last_message_lock = PTHREAD_MUTEX_INITIALIZER;
 
-  if (vl->meta != NULL)
-  {
-    vl_copy.meta = meta_data_clone (vl->meta);
-    if (vl_copy.meta == NULL)
-    {
-      ERROR ("plugin_dispatch_values_secure: meta_data_clone failed.");
-      free (vl_copy.values);
-      return (ENOMEM);
-    }
-  } /* if (vl->meta) */
+       double p;
+       double q;
+       int status;
 
-  status = plugin_dispatch_values (&vl_copy);
+       if (write_limit_high == 0)
+               return (0);
 
-  meta_data_destroy (vl_copy.meta);
-  free (vl_copy.values);
+       p = get_drop_probability ();
+       if (p == 0.0)
+               return (0);
 
-  return (status);
-} /* int plugin_dispatch_values_secure */
+       status = pthread_mutex_trylock (&last_message_lock);
+       if (status == 0)
+       {
+               cdtime_t now;
+
+               now = cdtime ();
+               if ((now - last_message_time) > TIME_T_TO_CDTIME_T (1))
+               {
+                       last_message_time = now;
+                       ERROR ("plugin_dispatch_values: Low water mark "
+                                       "reached. Dropping %.0f%% of metrics.",
+                                       100.0 * p);
+               }
+               pthread_mutex_unlock (&last_message_lock);
+       }
+
+       if (p == 1.0)
+               return (1);
+
+       q = cdrand_d ();
+       if (q > p)
+               return (1);
+       else
+               return (0);
+} /* }}} _Bool check_drop_value */
+
+int plugin_dispatch_values (value_list_t const *vl)
+{
+       int status;
+
+       if (check_drop_value ())
+               return (0);
+
+       status = plugin_write_enqueue (vl);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("plugin_dispatch_values: plugin_write_enqueue failed "
+                               "with status %i (%s).", status,
+                               sstrerror (status, errbuf, sizeof (errbuf)));
+               return (status);
+       }
+
+       return (0);
+}
+
+__attribute__((sentinel))
+int plugin_dispatch_multivalue (value_list_t const *template, /* {{{ */
+               _Bool store_percentage, ...)
+{
+       value_list_t *vl;
+       int failed = 0;
+       gauge_t sum = 0.0;
+       va_list ap;
+
+       assert (template->values_len == 1);
+
+       va_start (ap, store_percentage);
+       while (42)
+       {
+               char const *name;
+               gauge_t value;
+
+               name = va_arg (ap, char const *);
+               if (name == NULL)
+                       break;
+
+               value = va_arg (ap, gauge_t);
+               if (!isnan (value))
+                       sum += value;
+       }
+       va_end (ap);
+
+       vl = plugin_value_list_clone (template);
+       /* plugin_value_list_clone makes sure vl->time is set to non-zero. */
+       if (store_percentage)
+               sstrncpy (vl->type, "percent", sizeof (vl->type));
+
+       va_start (ap, store_percentage);
+       while (42)
+       {
+               char const *name;
+               int status;
+
+               /* Set the type instance. */
+               name = va_arg (ap, char const *);
+               if (name == NULL)
+                       break;
+               sstrncpy (vl->type_instance, name, sizeof (vl->type_instance));
+
+               /* Set the value. */
+               vl->values[0].gauge = va_arg (ap, gauge_t);
+               if (store_percentage)
+                       vl->values[0].gauge *= 100.0 / sum;
+
+               status = plugin_write_enqueue (vl);
+               if (status != 0)
+                       failed++;
+       }
+       va_end (ap);
+
+       plugin_value_list_free (vl);
+       return (failed);
+} /* }}} int plugin_dispatch_multivalue */
 
 int plugin_dispatch_notification (const notification_t *notif)
 {
@@ -1854,6 +2270,12 @@ const data_set_t *plugin_get_ds (const char *name)
 {
        data_set_t *ds;
 
+       if (data_sets == NULL)
+       {
+               ERROR ("plugin_get_ds: No data sets are defined yet.");
+               return (NULL);
+       }
+
        if (c_avl_get (data_sets, name, (void *) &ds) != 0)
        {
                DEBUG ("No such dataset registered: %s", name);
index 0f35de5..924c842 100644 (file)
@@ -2,7 +2,7 @@
 #define PLUGIN_H
 /**
  * collectd - src/plugin.h
- * Copyright (C) 2005-2011  Florian octo Forster
+ * Copyright (C) 2005-2014  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
@@ -223,7 +223,8 @@ void plugin_set_dir (const char *dir);
  *  and a value below zero if an error occurs.
  *
  * NOTES
- *  No attempt is made to re-load an already loaded module.
+ *  Re-loading an already loaded module is detected and zero is returned in
+ *  this case.
  */
 int plugin_load (const char *name, uint32_t flags);
 
@@ -326,8 +327,36 @@ int plugin_unregister_notification (const char *name);
  *  `vl'        Value list of the values that have been read by a `read'
  *              function.
  */
-int plugin_dispatch_values (value_list_t *vl);
-int plugin_dispatch_values_secure (const value_list_t *vl);
+int plugin_dispatch_values (value_list_t const *vl);
+
+/*
+ * NAME
+ *  plugin_dispatch_multivalue
+ *
+ * SYNOPSIS
+ *  plugin_dispatch_multivalue (vl, 1,
+ *                              "free", 42.0,
+ *                              "used", 58.0,
+ *                              NULL);
+ *
+ * DESCRIPTION
+ *  Takes a list of type instances and values and dispatches that in a batch,
+ *  making sure that all values have the same time stamp. If "store_percentage"
+ *  is set to true, the "type" is set to "percent" and a percentage is
+ *  calculated and dispatched, rather than the absolute values. Values that are
+ *  NaN are dispatched as NaN and will not influence the total.
+ *
+ *  The variadic arguments is a list of type_instance / gauge pairs, that are
+ *  interpreted as type "char const *" and "gauge_t". The last argument must be
+ *  a NULL pointer to signal end-of-list.
+ *
+ * RETURNS
+ *  The number of values it failed to dispatch (zero on success).
+ */
+__attribute__((sentinel))
+int plugin_dispatch_multivalue (value_list_t const *vl,
+               _Bool store_percentage, ...);
+
 int plugin_dispatch_missing (const value_list_t *vl);
 
 int plugin_dispatch_notification (const notification_t *notif);
index 15d4666..16e539a 100644 (file)
@@ -170,14 +170,14 @@ static char *def_queries[] = {
 };
 static int def_queries_num = STATIC_ARRAY_SIZE (def_queries);
 
-static c_psql_database_t *databases     = NULL;
-static size_t             databases_num = 0;
+static c_psql_database_t **databases     = NULL;
+static size_t              databases_num = 0;
 
-static udb_query_t      **queries       = NULL;
-static size_t             queries_num   = 0;
+static udb_query_t       **queries       = NULL;
+static size_t              queries_num   = 0;
 
-static c_psql_writer_t   *writers       = NULL;
-static size_t             writers_num   = 0;
+static c_psql_writer_t    *writers       = NULL;
+static size_t              writers_num   = 0;
 
 static int c_psql_begin (c_psql_database_t *db)
 {
@@ -220,17 +220,25 @@ static int c_psql_commit (c_psql_database_t *db)
 
 static c_psql_database_t *c_psql_database_new (const char *name)
 {
-       c_psql_database_t *db;
+       c_psql_database_t **tmp;
+       c_psql_database_t  *db;
 
-       db = (c_psql_database_t *)realloc (databases,
-                       (databases_num + 1) * sizeof (*db));
+       db = (c_psql_database_t *)malloc (sizeof(*db));
        if (NULL == db) {
                log_err ("Out of memory.");
                return NULL;
        }
 
-       databases = db;
-       db = databases + databases_num;
+       tmp = (c_psql_database_t **)realloc (databases,
+                       (databases_num + 1) * sizeof (*databases));
+       if (NULL == tmp) {
+               log_err ("Out of memory.");
+               sfree (db);
+               return NULL;
+       }
+
+       databases = tmp;
+       databases[databases_num] = db;
        ++databases_num;
 
        db->conn = NULL;
@@ -324,7 +332,9 @@ static void c_psql_database_delete (void *data)
        sfree (db->service);
 
        /* don't care about freeing or reordering the 'databases' array
-        * this is done in 'shutdown' */
+        * this is done in 'shutdown'; also, don't free the database instance
+        * object just to make sure that in case anybody accesses it before
+        * shutdown won't segfault */
        return;
 } /* c_psql_database_delete */
 
@@ -371,9 +381,6 @@ static int c_psql_check_connection (c_psql_database_t *db)
                c_psql_connect (db);
        }
 
-       /* "ping" */
-       PQclear (PQexec (db->conn, "SELECT 42;"));
-
        if (CONNECTION_OK != PQstatus (db->conn)) {
                PQreset (db->conn);
 
@@ -514,6 +521,12 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
        if (PGRES_TUPLES_OK != PQresultStatus (res)) {
                pthread_mutex_lock (&db->db_lock);
 
+               if ((CONNECTION_OK != PQstatus (db->conn))
+                               && (0 == c_psql_check_connection (db))) {
+                       PQclear (res);
+                       return c_psql_exec_query (db, q, prep_area);
+               }
+
                log_err ("Failed to execute SQL query: %s",
                                PQerrorMessage (db->conn));
                log_info ("SQL query was: %s",
@@ -558,6 +571,7 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
        }
 
        if (C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host)
+                       || (0 == strcmp (db->host, "127.0.0.1"))
                        || (0 == strcmp (db->host, "localhost")))
                host = hostname_g;
        else
@@ -900,10 +914,10 @@ static int c_psql_write (const data_set_t *ds, const value_list_t *vl,
 
                if ((PGRES_COMMAND_OK != PQresultStatus (res))
                                && (PGRES_TUPLES_OK != PQresultStatus (res))) {
+                       PQclear (res);
+
                        if ((CONNECTION_OK != PQstatus (db->conn))
                                        && (0 == c_psql_check_connection (db))) {
-                               PQclear (res);
-
                                /* try again */
                                res = PQexecParams (db->conn, writer->statement,
                                                STATIC_ARRAY_SIZE (params), NULL,
@@ -912,6 +926,7 @@ static int c_psql_write (const data_set_t *ds, const value_list_t *vl,
 
                                if ((PGRES_COMMAND_OK == PQresultStatus (res))
                                                || (PGRES_TUPLES_OK == PQresultStatus (res))) {
+                                       PQclear (res);
                                        success = 1;
                                        continue;
                                }
@@ -932,6 +947,8 @@ static int c_psql_write (const data_set_t *ds, const value_list_t *vl,
                        pthread_mutex_unlock (&db->db_lock);
                        return -1;
                }
+
+               PQclear (res);
                success = 1;
        }
 
@@ -953,17 +970,17 @@ static int c_psql_flush (cdtime_t timeout,
                __attribute__((unused)) const char *ident,
                user_data_t *ud)
 {
-       c_psql_database_t *dbs = databases;
+       c_psql_database_t **dbs = databases;
        size_t dbs_num = databases_num;
        size_t i;
 
        if ((ud != NULL) && (ud->data != NULL)) {
-               dbs = ud->data;
+               dbs = (void *)&ud->data;
                dbs_num = 1;
        }
 
        for (i = 0; i < dbs_num; ++i) {
-               c_psql_database_t *db = dbs + i;
+               c_psql_database_t *db = dbs[i];
 
                /* don't commit if the timeout is larger than the regular commit
                 * interval as in that case all requested data has already been
@@ -983,7 +1000,7 @@ static int c_psql_shutdown (void)
        plugin_unregister_read_group ("postgresql");
 
        for (i = 0; i < databases_num; ++i) {
-               c_psql_database_t *db = databases + i;
+               c_psql_database_t *db = databases[i];
 
                if (db->writers_num > 0) {
                        char cb_name[DATA_MAX_NAME_LEN];
@@ -998,6 +1015,8 @@ static int c_psql_shutdown (void)
                        plugin_unregister_flush (cb_name);
                        plugin_unregister_write (cb_name);
                }
+
+               sfree (db);
        }
 
        udb_query_free (queries, queries_num);
index 439cf4b..92be18f 100644 (file)
 #endif
 
 #define REDIS_DEF_HOST   "localhost"
+#define REDIS_DEF_PASSWD ""
 #define REDIS_DEF_PORT    6379
 #define REDIS_DEF_TIMEOUT 2000
 #define MAX_REDIS_NODE_NAME 64
+#define MAX_REDIS_PASSWD_LENGTH 512
 
 /* Redis plugin configuration example:
  *
@@ -54,7 +56,7 @@ struct redis_node_s
 {
   char name[MAX_REDIS_NODE_NAME];
   char host[HOST_NAME_MAX];
-  char passwd[HOST_NAME_MAX];
+  char passwd[MAX_REDIS_PASSWD_LENGTH];
   int port;
   int timeout;
 
@@ -229,8 +231,8 @@ static void redis_submit_d (char *plugin_instance,
 
 static int redis_init (void) /* {{{ */
 {
-  redis_node_t rn = { "default", REDIS_DEF_HOST, REDIS_DEF_PORT,
-    REDIS_DEF_TIMEOUT, /* next = */ NULL };
+  redis_node_t rn = { "default", REDIS_DEF_HOST, REDIS_DEF_PASSWD,
+    REDIS_DEF_PORT, REDIS_DEF_TIMEOUT, /* next = */ NULL };
 
   if (nodes_head == NULL)
     redis_node_add (&rn);
diff --git a/src/riemann.proto b/src/riemann.proto
new file mode 100644 (file)
index 0000000..3e946a3
--- /dev/null
@@ -0,0 +1,45 @@
+option java_package = "com.aphyr.riemann";
+option java_outer_classname = "Proto";
+
+message State {
+  optional int64 time = 1;
+  optional string state = 2;
+  optional string service = 3;
+  optional string host = 4;
+  optional string description = 5;
+  optional bool once = 6;
+  repeated string tags = 7;
+  optional float ttl = 8;
+}
+
+message Event {
+  optional int64 time = 1;
+  optional string state = 2;
+  optional string service = 3;
+  optional string host = 4;
+  optional string description = 5;
+  repeated string tags = 7;
+  optional float ttl = 8;
+  repeated Attribute attributes = 9;
+
+  optional sint64 metric_sint64 = 13;
+  optional double metric_d = 14;
+  optional float metric_f = 15;
+}
+
+message Query {
+  optional string string = 1;
+}
+
+message Msg {
+  optional bool ok = 2;
+  optional string error = 3;
+  repeated State states = 4;
+  optional Query query = 5;
+  repeated Event events = 6;
+}
+
+message Attribute {
+  required string key = 1;
+  optional string value = 2;
+}
index 45553b7..e77be2d 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/rrdcached.c
- * Copyright (C) 2008-2012  Florian octo Forster
+ * Copyright (C) 2008-2013  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -46,7 +46,9 @@ static rrdcreate_config_t rrdcreate_config =
        /* timespans_num = */ 0,
 
        /* consolidation_functions = */ NULL,
-       /* consolidation_functions_num = */ 0
+       /* consolidation_functions_num = */ 0,
+
+       /* async = */ 0
 };
 
 /*
@@ -112,52 +114,41 @@ static int value_list_to_string (char *buffer, int buffer_len,
   return (0);
 } /* int value_list_to_string */
 
-static int value_list_to_filename (char *buffer, int buffer_len,
-    const data_set_t *ds, const value_list_t *vl)
+static int value_list_to_filename (char *buffer, size_t buffer_size,
+    value_list_t const *vl)
 {
-  int offset = 0;
+  char const suffix[] = ".rrd";
   int status;
-
-  assert (0 == strcmp (ds->type, vl->type));
+  size_t len;
 
   if (datadir != NULL)
   {
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s/", datadir);
-    if ((status < 1) || (status >= buffer_len - offset))
-      return (-1);
-    offset += status;
-  }
+    size_t datadir_len = strlen (datadir) + 1;
 
-  status = ssnprintf (buffer + offset, buffer_len - offset,
-      "%s/", vl->host);
-  if ((status < 1) || (status >= buffer_len - offset))
-    return (-1);
-  offset += status;
+    if (datadir_len >= buffer_size)
+      return (ENOMEM);
 
-  if (strlen (vl->plugin_instance) > 0)
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s-%s/", vl->plugin, vl->plugin_instance);
-  else
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s/", vl->plugin);
-  if ((status < 1) || (status >= buffer_len - offset))
-    return (-1);
-  offset += status;
+    sstrncpy (buffer, datadir, buffer_size);
+    buffer[datadir_len - 1] = '/';
+    buffer[datadir_len] = 0;
 
-  if (strlen (vl->type_instance) > 0)
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s-%s", vl->type, vl->type_instance);
-  else
-    status = ssnprintf (buffer + offset, buffer_len - offset,
-        "%s", vl->type);
-  if ((status < 1) || (status >= buffer_len - offset))
-    return (-1);
-  offset += status;
+    buffer += datadir_len;
+    buffer_size -= datadir_len;
+  }
 
-  strncpy (buffer + offset, ".rrd", buffer_len - offset);
-  buffer[buffer_len - 1] = 0;
+  status = FORMAT_VL (buffer, buffer_size, vl);
+  if (status != 0)
+    return (status);
 
+  len = strlen (buffer);
+  assert (len < buffer_size);
+  buffer += len;
+  buffer_size -= len;
+
+  if (buffer_size <= sizeof (suffix))
+    return (ENOMEM);
+
+  memcpy (buffer, suffix, sizeof (suffix));
   return (0);
 } /* int value_list_to_filename */
 
@@ -250,6 +241,8 @@ static int rc_config (oconfig_item_t *ci)
       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 ("CreateFilesAsync", key) == 0)
+      status = cf_util_get_boolean (child, &rrdcreate_config.async);
     else if (strcasecmp ("CollectStatistics", key) == 0)
       status = cf_util_get_boolean (child, &config_collect_stats);
     else if (strcasecmp ("StepSize", key) == 0)
@@ -316,6 +309,14 @@ static int rc_read (void)
     sstrncpy (vl.host, daemon_address, sizeof (vl.host));
   sstrncpy (vl.plugin, "rrdcached", sizeof (vl.plugin));
 
+  status = rrdc_connect (daemon_address);
+  if (status != 0)
+  {
+    ERROR ("rrdcached plugin: rrdc_connect (%s) failed with status %i.",
+        daemon_address, status);
+    return (-1);
+  }
+
   head = NULL;
   status = rrdc_stats_get (&head);
   if (status != 0)
@@ -422,7 +423,7 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl,
     return (-1);
   }
 
-  if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+  if (value_list_to_filename (filename, sizeof (filename), vl) != 0)
   {
     ERROR ("rrdcached plugin: value_list_to_filename failed.");
     return (-1);
@@ -459,6 +460,8 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl,
             filename);
         return (-1);
       }
+      else if (rrdcreate_config.async)
+        return (0);
     }
   }
 
index b1d13ee..c795e26 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/rrdtool.c
- * Copyright (C) 2006-2008  Florian octo Forster
+ * Copyright (C) 2006-2013  Florian octo Forster
  * Copyright (C) 2008-2008  Sebastian Harl
  * Copyright (C) 2009       Mariusz Gronczewski
  *
@@ -18,7 +18,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>
  *   Sebastian Harl <sh at tokkee.org>
  *   Mariusz Gronczewski <xani666 at gmail.com>
  **/
@@ -27,6 +27,7 @@
 #include "plugin.h"
 #include "common.h"
 #include "utils_avltree.h"
+#include "utils_random.h"
 #include "utils_rrdcreate.h"
 
 #include <rrd.h>
@@ -75,6 +76,7 @@ static const char *config_keys[] =
 {
        "CacheTimeout",
        "CacheFlush",
+       "CreateFilesAsync",
        "DataDir",
        "StepSize",
        "HeartBeat",
@@ -102,7 +104,9 @@ static rrdcreate_config_t rrdcreate_config =
        /* timespans_num = */ 0,
 
        /* consolidation_functions = */ NULL,
-       /* consolidation_functions_num = */ 0
+       /* consolidation_functions_num = */ 0,
+
+       /* async = */ 0
 };
 
 /* XXX: If you need to lock both, cache_lock and queue_lock, at the same time,
@@ -194,7 +198,7 @@ static int srrd_update (char *filename, char *template,
 } /* int srrd_update */
 #endif /* !HAVE_THREADSAFE_LIBRRD */
 
-static int value_list_to_string (char *buffer, int buffer_len,
+static int value_list_to_string_multiple (char *buffer, int buffer_len,
                const data_set_t *ds, const value_list_t *vl)
 {
        int offset;
@@ -238,49 +242,82 @@ static int value_list_to_string (char *buffer, int buffer_len,
        } /* for ds->ds_num */
 
        return (0);
+} /* int value_list_to_string_multiple */
+
+static int value_list_to_string (char *buffer, int buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       int status;
+       time_t tt;
+
+       if (ds->ds_num != 1)
+               return (value_list_to_string_multiple (buffer, buffer_len,
+                                       ds, vl));
+
+       tt = CDTIME_T_TO_TIME_T (vl->time);
+       switch (ds->ds[0].type)
+       {
+               case DS_TYPE_DERIVE:
+                       status = ssnprintf (buffer, buffer_len, "%u:%"PRIi64,
+                               (unsigned) tt, vl->values[0].derive);
+                       break;
+               case DS_TYPE_GAUGE:
+                       status = ssnprintf (buffer, buffer_len, "%u:%lf",
+                               (unsigned) tt, vl->values[0].gauge);
+                       break;
+               case DS_TYPE_COUNTER:
+                       status = ssnprintf (buffer, buffer_len, "%u:%llu",
+                               (unsigned) tt, vl->values[0].counter);
+                       break;
+               case DS_TYPE_ABSOLUTE:
+                       status = ssnprintf (buffer, buffer_len, "%u:%"PRIu64,
+                               (unsigned) tt, vl->values[0].absolute);
+                       break;
+               default:
+                       return (EINVAL);
+       }
+
+       if ((status < 1) || (status >= buffer_len))
+               return (ENOMEM);
+
+       return (0);
 } /* int value_list_to_string */
 
-static int value_list_to_filename (char *buffer, int buffer_len,
-               const data_set_t __attribute__((unused)) *ds, const value_list_t *vl)
+static int value_list_to_filename (char *buffer, size_t buffer_size,
+               value_list_t const *vl)
 {
-       int offset = 0;
+       char const suffix[] = ".rrd";
        int status;
+       size_t len;
 
        if (datadir != NULL)
        {
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", datadir);
-               if ((status < 1) || (status >= buffer_len - offset))
-                       return (-1);
-               offset += status;
+               size_t datadir_len = strlen (datadir) + 1;
+
+               if (datadir_len >= buffer_size)
+                       return (ENOMEM);
+
+               sstrncpy (buffer, datadir, buffer_size);
+               buffer[datadir_len - 1] = '/';
+               buffer[datadir_len] = 0;
+
+               buffer += datadir_len;
+               buffer_size -= datadir_len;
        }
 
-       status = ssnprintf (buffer + offset, buffer_len - offset,
-                       "%s/", vl->host);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       status = FORMAT_VL (buffer, buffer_size, vl);
+       if (status != 0)
+               return (status);
 
-       if (strlen (vl->plugin_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s/", vl->plugin, vl->plugin_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s/", vl->plugin);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       len = strlen (buffer);
+       assert (len < buffer_size);
+       buffer += len;
+       buffer_size -= len;
 
-       if (strlen (vl->type_instance) > 0)
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s-%s.rrd", vl->type, vl->type_instance);
-       else
-               status = ssnprintf (buffer + offset, buffer_len - offset,
-                               "%s.rrd", vl->type);
-       if ((status < 1) || (status >= buffer_len - offset))
-               return (-1);
-       offset += status;
+       if (buffer_size <= sizeof (suffix))
+               return (ENOMEM);
 
+       memcpy (buffer, suffix, sizeof (suffix));
        return (0);
 } /* int value_list_to_filename */
 
@@ -652,11 +689,8 @@ static int rrd_cache_flush_identifier (cdtime_t timeout,
 
 static int64_t rrd_get_random_variation (void)
 {
-  double dbl_timeout;
-  cdtime_t ctm_timeout;
-  double rand_fact;
-  _Bool negative;
-  int64_t ret;
+  long min;
+  long max;
 
   if (random_timeout <= 0)
     return (0);
@@ -669,20 +703,10 @@ static int64_t rrd_get_random_variation (void)
          random_timeout = cache_timeout;
   }
 
-  /* This seems a bit complicated, but "random_timeout" is likely larger than
-   * RAND_MAX, so we can't simply use modulo here. */
-  dbl_timeout = CDTIME_T_TO_DOUBLE (random_timeout);
-  rand_fact = ((double) random ())
-    / ((double) RAND_MAX);
-  negative = (_Bool) (random () % 2);
-
-  ctm_timeout = DOUBLE_TO_CDTIME_T (dbl_timeout * rand_fact);
+  max = (long) (random_timeout / 2);
+  min = max - ((long) random_timeout);
 
-  ret = (int64_t) ctm_timeout;
-  if (negative)
-    ret *= -1;
-
-  return (ret);
+  return ((int64_t) cdrand_range (min, max));
 } /* int64_t rrd_get_random_variation */
 
 static int rrd_cache_insert (const char *filename,
@@ -896,7 +920,7 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl,
                return -1;
        }
 
-       if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+       if (value_list_to_filename (filename, sizeof (filename), vl) != 0)
                return (-1);
 
        if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
@@ -910,6 +934,8 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl,
                                        ds, vl, &rrdcreate_config);
                        if (status != 0)
                                return (-1);
+                       else if (rrdcreate_config.async)
+                               return (0);
                }
                else
                {
@@ -1008,6 +1034,13 @@ static int rrd_config (const char *key, const char *value)
                if (temp > 0)
                        rrdcreate_config.heartbeat = temp;
        }
+       else if (strcasecmp ("CreateFilesAsync", key) == 0)
+       {
+               if (IS_TRUE (value))
+                       rrdcreate_config.async = 1;
+               else
+                       rrdcreate_config.async = 0;
+       }
        else if (strcasecmp ("RRARows", key) == 0)
        {
                int tmp = atoi (value);
diff --git a/src/sigrok.c b/src/sigrok.c
new file mode 100644 (file)
index 0000000..ba3e406
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * collectd - src/sigrok.c
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <pthread.h>
+
+#include <glib.h>
+#include <libsigrok/libsigrok.h>
+
+/* Minimum interval between dispatches coming from this plugin. The RRD
+ * plugin, at least, complains when written to with sub-second intervals.*/
+#define DEFAULT_MIN_DISPATCH_INTERVAL TIME_T_TO_CDTIME_T(0)
+
+static pthread_t sr_thread;
+static int sr_thread_running = FALSE;
+GSList *config_devices;
+static int num_devices;
+static int loglevel = SR_LOG_WARN;
+static struct sr_context *sr_ctx;
+
+struct config_device {
+       char *name;
+       char *driver;
+       char *conn;
+       char *serialcomm;
+       struct sr_dev_inst *sdi;
+       cdtime_t min_dispatch_interval;
+       cdtime_t last_dispatch;
+};
+
+
+static int sigrok_log_callback(void*cb_data __attribute__((unused)),
+               int msg_loglevel, const char *format, va_list args)
+{
+       char s[512];
+
+       if (msg_loglevel <= loglevel) {
+               vsnprintf(s, 512, format, args);
+               plugin_log(LOG_INFO, "sigrok plugin: %s", s);
+       }
+
+       return 0;
+}
+
+static int sigrok_config_device(oconfig_item_t *ci)
+{
+       struct config_device *cfdev;
+       int i;
+
+       if (!(cfdev = malloc(sizeof(struct config_device)))) {
+               ERROR("sigrok plugin: malloc() failed.");
+               return -1;
+       }
+       memset(cfdev, 0, sizeof(*cfdev));
+       if (cf_util_get_string(ci, &cfdev->name)) {
+               free(cfdev);
+               WARNING("sigrok plugin: Invalid device name.");
+               return -1;
+       }
+       cfdev->min_dispatch_interval = DEFAULT_MIN_DISPATCH_INTERVAL;
+
+       for (i = 0; i < ci->children_num; i++) {
+               oconfig_item_t *item = ci->children + i;
+               if (!strcasecmp(item->key, "driver"))
+                       cf_util_get_string(item, &cfdev->driver);
+               else if (!strcasecmp(item->key, "conn"))
+                       cf_util_get_string(item, &cfdev->conn);
+               else if (!strcasecmp(item->key, "serialcomm"))
+                       cf_util_get_string(item, &cfdev->serialcomm);
+               else if (!strcasecmp(item->key, "minimuminterval"))
+                       cf_util_get_cdtime(item, &cfdev->min_dispatch_interval);
+               else
+                       WARNING("sigrok plugin: Invalid keyword \"%s\".",
+                                       item->key);
+       }
+
+       config_devices = g_slist_append(config_devices, cfdev);
+
+       return 0;
+}
+
+static int sigrok_config(oconfig_item_t *ci)
+{
+       int i;
+
+       for (i = 0; i < ci->children_num; i++) {
+               oconfig_item_t *item = ci->children + i;
+               if (strcasecmp("LogLevel", item->key) == 0) {
+                       int status;
+                       int tmp = -1;
+
+                       status = cf_util_get_int (item, &tmp);
+                       if (status != 0)
+                               continue;
+                       else if ((tmp < 0) || (tmp > 5)) {
+                               ERROR ("sigrok plugin: The \"LogLevel\" "
+                                               "configuration option expects "
+                                               "an integer between 0 and 5 "
+                                               "(inclusive); you provided %i.",
+                                               tmp);
+                               continue;
+                       }
+                       loglevel = tmp;
+               } else if (!strcasecmp(item->key, "Device"))
+                       sigrok_config_device(item);
+               else
+                       WARNING("sigrok plugin: Invalid keyword \"%s\".",
+                                       item->key);
+       }
+
+       return 0;
+}
+
+static char *sigrok_value_type(const struct sr_datafeed_analog *analog)
+{
+       char *s;
+
+       if (analog->mq == SR_MQ_VOLTAGE)
+               s = "voltage";
+       else if (analog->mq == SR_MQ_CURRENT)
+               s = "current";
+       else if (analog->mq == SR_MQ_FREQUENCY)
+               s = "frequency";
+       else if (analog->mq == SR_MQ_POWER)
+               s = "power";
+       else if (analog->mq == SR_MQ_TEMPERATURE)
+               s = "temperature";
+       else if (analog->mq == SR_MQ_RELATIVE_HUMIDITY)
+               s = "humidity";
+       else if (analog->mq == SR_MQ_SOUND_PRESSURE_LEVEL)
+               s = "spl";
+       else
+               s = "gauge";
+
+       return s;
+}
+
+static void sigrok_feed_callback(const struct sr_dev_inst *sdi,
+               const struct sr_datafeed_packet *packet, void *cb_data)
+{
+       const struct sr_datafeed_analog *analog;
+       struct config_device *cfdev;
+       GSList *l;
+       value_t value;
+       value_list_t vl = VALUE_LIST_INIT;
+
+       /* Find this device's configuration. */
+       cfdev = NULL;
+       for (l = config_devices; l; l = l->next) {
+               cfdev = l->data;
+               if (cfdev->sdi == sdi) {
+                       /* Found it. */
+                       break;
+               }
+               cfdev = NULL;
+       }
+
+       if (!cfdev) {
+               ERROR("sigrok plugin: Received data from driver \"%s\" but "
+                               "can't find a configuration / device matching "
+                               "it.", sdi->driver->name);
+               return;
+       }
+
+       if (packet->type == SR_DF_END) {
+               /* TODO: try to restart acquisition after a delay? */
+               WARNING("sigrok plugin: acquisition for \"%s\" ended.",
+                               cfdev->name);
+               return;
+       }
+
+       if (packet->type != SR_DF_ANALOG)
+               return;
+
+       if ((cfdev->min_dispatch_interval != 0)
+                       && ((cdtime() - cfdev->last_dispatch)
+                               < cfdev->min_dispatch_interval))
+               return;
+
+       /* Ignore all but the first sample on the first probe. */
+       analog = packet->payload;
+       value.gauge = analog->data[0];
+       vl.values = &value;
+       vl.values_len = 1;
+       sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+       sstrncpy(vl.plugin, "sigrok", sizeof(vl.plugin));
+       ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance),
+                       "%s", cfdev->name);
+       sstrncpy(vl.type, sigrok_value_type(analog), sizeof(vl.type));
+
+       plugin_dispatch_values(&vl);
+       cfdev->last_dispatch = cdtime();
+}
+
+static void sigrok_free_drvopts(struct sr_config *src)
+{
+       g_variant_unref(src->data);
+       g_free(src);
+}
+
+static int sigrok_init_driver(struct config_device *cfdev,
+               struct sr_dev_driver *drv)
+{
+       struct sr_config *src;
+       GSList *devlist, *drvopts;
+       char hwident[512];
+
+       if (sr_driver_init(sr_ctx, drv) != SR_OK)
+               /* Error was logged by libsigrok. */
+               return -1;
+
+       drvopts = NULL;
+       if (cfdev->conn) {
+               if (!(src = malloc(sizeof(struct sr_config))))
+                       return -1;
+               src->key = SR_CONF_CONN;
+               src->data = g_variant_new_string(cfdev->conn);
+               drvopts = g_slist_append(drvopts, src);
+       }
+       if (cfdev->serialcomm) {
+               if (!(src = malloc(sizeof(struct sr_config))))
+                       return -1;
+               src->key = SR_CONF_SERIALCOMM;
+               src->data = g_variant_new_string(cfdev->serialcomm);
+               drvopts = g_slist_append(drvopts, src);
+       }
+       devlist = sr_driver_scan(drv, drvopts);
+       g_slist_free_full(drvopts, (GDestroyNotify)sigrok_free_drvopts);
+       if (!devlist) {
+               /* Not an error, but the user should know about it. */
+               WARNING("sigrok plugin: No device found for \"%s\".",
+                               cfdev->name);
+               return 0;
+       }
+
+       if (g_slist_length(devlist) > 1) {
+               INFO("sigrok plugin: %d sigrok devices for device entry "
+                               "\"%s\": must be 1.",
+                               g_slist_length(devlist), cfdev->name);
+               return -1;
+       }
+       cfdev->sdi = devlist->data;
+       g_slist_free(devlist);
+       ssnprintf(hwident, sizeof(hwident), "%s %s %s",
+                       cfdev->sdi->vendor ? cfdev->sdi->vendor : "",
+                       cfdev->sdi->model ? cfdev->sdi->model : "",
+                       cfdev->sdi->version ? cfdev->sdi->version : "");
+       INFO("sigrok plugin: Device \"%s\" is a %s", cfdev->name, hwident);
+
+       if (sr_dev_open(cfdev->sdi) != SR_OK)
+               return -1;
+
+       if (sr_session_dev_add(cfdev->sdi) != SR_OK)
+               return -1;
+
+       return 1;
+}
+
+static void *sigrok_read_thread(void *arg __attribute__((unused)))
+{
+       struct sr_dev_driver *drv, **drvlist;
+       GSList *l;
+       struct config_device *cfdev;
+       int ret, i;
+
+       sr_log_callback_set(sigrok_log_callback, NULL);
+       sr_log_loglevel_set(loglevel);
+
+       if ((ret = sr_init(&sr_ctx)) != SR_OK) {
+               ERROR("sigrok plugin: Failed to initialize libsigrok: %s.",
+                               sr_strerror(ret));
+               return NULL;
+       }
+
+       if (!sr_session_new())
+               return NULL;
+
+       num_devices = 0;
+       drvlist = sr_driver_list();
+       for (l = config_devices; l; l = l->next) {
+               cfdev = l->data;
+               drv = NULL;
+               for (i = 0; drvlist[i]; i++) {
+                       if (!strcmp(drvlist[i]->name, cfdev->driver)) {
+                               drv = drvlist[i];
+                               break;
+                       }
+               }
+               if (!drv) {
+                       ERROR("sigrok plugin: Unknown driver \"%s\".",
+                                       cfdev->driver);
+                       return NULL;
+               }
+
+               if ((ret = sigrok_init_driver(cfdev, drv)) < 0)
+                       /* Error was already logged. */
+                       return NULL;
+
+               num_devices += ret;
+       }
+
+       if (num_devices > 0) {
+               /* Do this only when we're sure there's hardware to talk to. */
+               if (sr_session_datafeed_callback_add(sigrok_feed_callback, NULL)
+                               != SR_OK)
+                       return NULL;
+
+               /* Start acquisition on all devices. */
+               if (sr_session_start() != SR_OK)
+                       return NULL;
+
+               /* Main loop, runs forever. */
+               sr_session_run();
+
+               sr_session_stop();
+               sr_session_dev_remove_all();
+       }
+
+       sr_session_destroy();
+
+       sr_exit(sr_ctx);
+
+       pthread_exit(NULL);
+       sr_thread_running = FALSE;
+
+       return NULL;
+}
+
+static int sigrok_init(void)
+{
+       int status;
+
+       if (sr_thread_running) {
+               ERROR("sigrok plugin: Thread already running.");
+               return -1;
+       }
+
+       if ((status = plugin_thread_create(&sr_thread, NULL, sigrok_read_thread,
+                       NULL)) != 0) {
+               ERROR("sigrok plugin: Failed to create thread: %s.",
+                               strerror(status));
+               return -1;
+       }
+       sr_thread_running = TRUE;
+
+       return 0;
+}
+
+static int sigrok_shutdown(void)
+{
+       struct config_device *cfdev;
+       GSList *l;
+
+       if (sr_thread_running) {
+               pthread_cancel(sr_thread);
+               pthread_join(sr_thread, NULL);
+       }
+
+       for (l = config_devices; l; l = l->next) {
+               cfdev = l->data;
+               free(cfdev->name);
+               free(cfdev->driver);
+               free(cfdev->conn);
+               free(cfdev->serialcomm);
+               free(cfdev);
+       }
+       g_slist_free(config_devices);
+
+       return 0;
+}
+
+void module_register(void)
+{
+       plugin_register_complex_config("sigrok", sigrok_config);
+       plugin_register_init("sigrok", sigrok_init);
+       plugin_register_shutdown("sigrok", sigrok_shutdown);
+}
index c4d043b..2bb7c2a 100644 (file)
@@ -29,6 +29,8 @@
 #include <net-snmp/net-snmp-config.h>
 #include <net-snmp/net-snmp-includes.h>
 
+#include <fnmatch.h>
+
 /*
  * Private data structes
  */
@@ -50,7 +52,7 @@ struct data_definition_s
 {
   char *name; /* used to reference this from the `Collect' option */
   char *type; /* used to find the data_set */
-  int is_table;
+  _Bool is_table;
   instance_t instance;
   char *instance_prefix;
   oid_t *values;
@@ -58,6 +60,9 @@ struct data_definition_s
   double scale;
   double shift;
   struct data_definition_s *next;
+  char **ignores;
+  size_t ignores_len;
+  int invert_match;
 };
 typedef struct data_definition_s data_definition_t;
 
@@ -65,8 +70,22 @@ struct host_definition_s
 {
   char *name;
   char *address;
-  char *community;
   int version;
+
+  /* snmpv1/2 options */
+  char *community;
+
+  /* snmpv3 security options */
+  char *username;
+  oid *auth_protocol;
+  size_t auth_protocol_len;
+  char *auth_passphrase;
+  oid *priv_protocol;
+  size_t priv_protocol_len;
+  char *priv_passphrase;
+  int security_level;
+  char *context;
+
   void *sess_handle;
   c_complain_t complaint;
   cdtime_t interval;
@@ -108,7 +127,7 @@ static int csnmp_read_host (user_data_t *ud);
  */
 static void csnmp_oid_init (oid_t *dst, oid const *src, size_t n)
 {
-  assert (n <= STATIC_ARRAY_LEN (dst->oid));
+  assert (n <= STATIC_ARRAY_SIZE (dst->oid));
   memcpy (dst->oid, src, sizeof (*src) * n);
   dst->oid_len = n;
 }
@@ -183,6 +202,10 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */
   sfree (hd->name);
   sfree (hd->address);
   sfree (hd->community);
+  sfree (hd->username);
+  sfree (hd->auth_passphrase);
+  sfree (hd->priv_passphrase);
+  sfree (hd->context);
   sfree (hd->data_list);
 
   sfree (hd);
@@ -197,16 +220,15 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */
  *  csnmp_config
  *  +-> call_snmp_init_once
  *  +-> csnmp_config_add_data
- *  !   +-> csnmp_config_add_data_type
- *  !   +-> csnmp_config_add_data_table
  *  !   +-> csnmp_config_add_data_instance
  *  !   +-> csnmp_config_add_data_instance_prefix
  *  !   +-> csnmp_config_add_data_values
  *  +-> csnmp_config_add_host
- *      +-> csnmp_config_add_host_address
- *      +-> csnmp_config_add_host_community
  *      +-> csnmp_config_add_host_version
  *      +-> csnmp_config_add_host_collect
+ *      +-> csnmp_config_add_host_auth_protocol
+ *      +-> csnmp_config_add_host_priv_protocol
+ *      +-> csnmp_config_add_host_security_level
  */
 static void call_snmp_init_once (void)
 {
@@ -217,60 +239,31 @@ static void call_snmp_init_once (void)
   have_init = 1;
 } /* void call_snmp_init_once */
 
-static int csnmp_config_add_data_type (data_definition_t *dd, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("snmp plugin: `Type' needs exactly one string argument.");
-    return (-1);
-  }
-
-  sfree (dd->type);
-  dd->type = strdup (ci->values[0].value.string);
-  if (dd->type == NULL)
-    return (-1);
-
-  return (0);
-} /* int csnmp_config_add_data_type */
-
-static int csnmp_config_add_data_table (data_definition_t *dd, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
-  {
-    WARNING ("snmp plugin: `Table' needs exactly one boolean argument.");
-    return (-1);
-  }
-
-  dd->is_table = ci->values[0].value.boolean ? 1 : 0;
-
-  return (0);
-} /* int csnmp_config_add_data_table */
-
 static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t *ci)
 {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("snmp plugin: `Instance' needs exactly one string argument.");
-    return (-1);
-  }
+  char buffer[DATA_MAX_NAME_LEN];
+  int status;
+
+  status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
+  if (status != 0)
+    return status;
 
   if (dd->is_table)
   {
     /* Instance is an OID */
     dd->instance.oid.oid_len = MAX_OID_LEN;
 
-    if (!read_objid (ci->values[0].value.string,
+    if (!read_objid (buffer,
           dd->instance.oid.oid, &dd->instance.oid.oid_len))
     {
-      ERROR ("snmp plugin: read_objid (%s) failed.",
-          ci->values[0].value.string);
+      ERROR ("snmp plugin: read_objid (%s) failed.", buffer);
       return (-1);
     }
   }
   else
   {
     /* Instance is a simple string */
-    sstrncpy (dd->instance.string, ci->values[0].value.string,
+    sstrncpy (dd->instance.string, buffer,
         sizeof (dd->instance.string));
   }
 
@@ -280,11 +273,7 @@ static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t
 static int csnmp_config_add_data_instance_prefix (data_definition_t *dd,
     oconfig_item_t *ci)
 {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("snmp plugin: `InstancePrefix' needs exactly one string argument.");
-    return (-1);
-  }
+  int status;
 
   if (!dd->is_table)
   {
@@ -293,12 +282,8 @@ static int csnmp_config_add_data_instance_prefix (data_definition_t *dd,
     return (-1);
   }
 
-  sfree (dd->instance_prefix);
-  dd->instance_prefix = strdup (ci->values[0].value.string);
-  if (dd->instance_prefix == NULL)
-    return (-1);
-
-  return (0);
+  status = cf_util_get_string(ci, &dd->instance_prefix);
+  return status;
 } /* int csnmp_config_add_data_instance_prefix */
 
 static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *ci)
@@ -344,33 +329,49 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *
   return (0);
 } /* int csnmp_config_add_data_instance */
 
-static int csnmp_config_add_data_shift (data_definition_t *dd, oconfig_item_t *ci)
+static int csnmp_config_add_data_blacklist(data_definition_t *dd, oconfig_item_t *ci)
 {
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+  int i;
+
+  if (ci->values_num < 1)
+    return (0);
+
+  for (i = 0; i < ci->values_num; i++)
   {
-    WARNING ("snmp plugin: The `Shift' config option needs exactly one number argument.");
-    return (-1);
+    if (ci->values[i].type != OCONFIG_TYPE_STRING)
+    {
+      WARNING ("snmp plugin: `Ignore' needs only string argument.");
+      return (-1);
+    }
   }
 
-  dd->shift = ci->values[0].value.number;
+  dd->ignores_len = 0;
+  dd->ignores = NULL;
 
-  return (0);
-} /* int csnmp_config_add_data_shift */
+  for (i = 0; i < ci->values_num; ++i)
+  {
+    if (strarray_add(&(dd->ignores), &(dd->ignores_len), ci->values[i].value.string) != 0)
+    {
+      ERROR("snmp plugin: Can't allocate memory");
+      strarray_free(dd->ignores, dd->ignores_len);
+      return (ENOMEM); 
+    }
+  }
+  return 0;
+} /* int csnmp_config_add_data_blacklist */
 
-static int csnmp_config_add_data_scale (data_definition_t *dd, oconfig_item_t *ci)
+static int csnmp_config_add_data_blacklist_match_inverted(data_definition_t *dd, oconfig_item_t *ci)
 {
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
   {
-    WARNING ("snmp plugin: The `Scale' config option needs exactly one number argument.");
+    WARNING ("snmp plugin: `InvertMatch' needs exactly one boolean argument.");
     return (-1);
   }
 
-  dd->scale = ci->values[0].value.number;
+  dd->invert_match = ci->values[0].value.boolean ? 1 : 0;
 
   return (0);
-} /* int csnmp_config_add_data_scale */
+} /* int csnmp_config_add_data_blacklist_match_inverted */
 
 static int csnmp_config_add_data (oconfig_item_t *ci)
 {
@@ -378,24 +379,18 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
   int status = 0;
   int i;
 
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("snmp plugin: The `Data' config option needs exactly one string argument.");
-    return (-1);
-  }
-
   dd = (data_definition_t *) malloc (sizeof (data_definition_t));
   if (dd == NULL)
     return (-1);
   memset (dd, '\0', sizeof (data_definition_t));
 
-  dd->name = strdup (ci->values[0].value.string);
-  if (dd->name == NULL)
+  status = cf_util_get_string(ci, &dd->name);
+  if (status != 0)
   {
     free (dd);
     return (-1);
   }
+
   dd->scale = 1.0;
   dd->shift = 0.0;
 
@@ -405,9 +400,9 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
     status = 0;
 
     if (strcasecmp ("Type", option->key) == 0)
-      status = csnmp_config_add_data_type (dd, option);
+      status = cf_util_get_string(option, &dd->type);
     else if (strcasecmp ("Table", option->key) == 0)
-      status = csnmp_config_add_data_table (dd, option);
+      status = cf_util_get_boolean(option, &dd->is_table);
     else if (strcasecmp ("Instance", option->key) == 0)
       status = csnmp_config_add_data_instance (dd, option);
     else if (strcasecmp ("InstancePrefix", option->key) == 0)
@@ -415,9 +410,13 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
     else if (strcasecmp ("Values", option->key) == 0)
       status = csnmp_config_add_data_values (dd, option);
     else if (strcasecmp ("Shift", option->key) == 0)
-      status = csnmp_config_add_data_shift (dd, option);
+      status = cf_util_get_double(option, &dd->shift);
     else if (strcasecmp ("Scale", option->key) == 0)
-      status = csnmp_config_add_data_scale (dd, option);
+      status = cf_util_get_double(option, &dd->scale);
+    else if (strcasecmp ("Ignore", option->key) == 0)
+      status = csnmp_config_add_data_blacklist(dd, option);
+    else if (strcasecmp ("InvertMatch", option->key) == 0)
+      status = csnmp_config_add_data_blacklist_match_inverted(dd, option);
     else
     {
       WARNING ("snmp plugin: Option `%s' not allowed here.", option->key);
@@ -451,6 +450,7 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
     sfree (dd->name);
     sfree (dd->instance_prefix);
     sfree (dd->values);
+    sfree (dd->ignores);
     sfree (dd);
     return (-1);
   }
@@ -472,50 +472,6 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
   return (0);
 } /* int csnmp_config_add_data */
 
-static int csnmp_config_add_host_address (host_definition_t *hd, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("snmp plugin: The `Address' config option needs exactly one string argument.");
-    return (-1);
-  }
-
-  if (hd->address == NULL)
-    free (hd->address);
-
-  hd->address = strdup (ci->values[0].value.string);
-  if (hd->address == NULL)
-    return (-1);
-
-  DEBUG ("snmp plugin: host = %s; host->address = %s;",
-      hd->name, hd->address);
-
-  return (0);
-} /* int csnmp_config_add_host_address */
-
-static int csnmp_config_add_host_community (host_definition_t *hd, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("snmp plugin: The `Community' config option needs exactly one string argument.");
-    return (-1);
-  }
-
-  if (hd->community == NULL)
-    free (hd->community);
-
-  hd->community = strdup (ci->values[0].value.string);
-  if (hd->community == NULL)
-    return (-1);
-
-  DEBUG ("snmp plugin: host = %s; host->community = %s;",
-      hd->name, hd->community);
-
-  return (0);
-} /* int csnmp_config_add_host_community */
-
 static int csnmp_config_add_host_version (host_definition_t *hd, oconfig_item_t *ci)
 {
   int version;
@@ -528,9 +484,9 @@ static int csnmp_config_add_host_version (host_definition_t *hd, oconfig_item_t
   }
 
   version = (int) ci->values[0].value.number;
-  if ((version != 1) && (version != 2))
+  if ((version < 1) || (version > 3))
   {
-    WARNING ("snmp plugin: `Version' must either be `1' or `2'.");
+    WARNING ("snmp plugin: `Version' must either be `1', `2', or `3'.");
     return (-1);
   }
 
@@ -590,6 +546,92 @@ static int csnmp_config_add_host_collect (host_definition_t *host,
   return (0);
 } /* int csnmp_config_add_host_collect */
 
+static int csnmp_config_add_host_auth_protocol (host_definition_t *hd, oconfig_item_t *ci)
+{
+  char buffer[4];
+  int status;
+
+  status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
+  if (status != 0)
+    return status;
+
+  if (strcasecmp("MD5", buffer) == 0) {
+    hd->auth_protocol = usmHMACMD5AuthProtocol;
+    hd->auth_protocol_len = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid);
+  }
+  else if (strcasecmp("SHA", buffer) == 0) {
+    hd->auth_protocol = usmHMACSHA1AuthProtocol;
+    hd->auth_protocol_len = sizeof(usmHMACSHA1AuthProtocol)/sizeof(oid);
+  }
+  else
+  {
+    WARNING ("snmp plugin: The `AuthProtocol' config option must be `MD5' or `SHA'.");
+    return (-1);
+  }
+
+  DEBUG ("snmp plugin: host = %s; host->auth_protocol = %s;",
+      hd->name, hd->auth_protocol == usmHMACMD5AuthProtocol ? "MD5" : "SHA");
+
+  return (0);
+} /* int csnmp_config_add_host_auth_protocol */
+
+static int csnmp_config_add_host_priv_protocol (host_definition_t *hd, oconfig_item_t *ci)
+{
+  char buffer[4];
+  int status;
+
+  status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
+  if (status != 0)
+    return status;
+
+  if (strcasecmp("AES", buffer) == 0)
+  {
+    hd->priv_protocol = usmAESPrivProtocol;
+    hd->priv_protocol_len = sizeof(usmAESPrivProtocol)/sizeof(oid);
+  }
+  else if (strcasecmp("DES", buffer) == 0) {
+    hd->priv_protocol = usmDESPrivProtocol;
+    hd->priv_protocol_len = sizeof(usmDESPrivProtocol)/sizeof(oid);
+  }
+  else
+  {
+    WARNING ("snmp plugin: The `PrivProtocol' config option must be `AES' or `DES'.");
+    return (-1);
+  }
+
+  DEBUG ("snmp plugin: host = %s; host->priv_protocol = %s;",
+      hd->name, hd->priv_protocol == usmAESPrivProtocol ? "AES" : "DES");
+
+  return (0);
+} /* int csnmp_config_add_host_priv_protocol */
+
+static int csnmp_config_add_host_security_level (host_definition_t *hd, oconfig_item_t *ci)
+{
+  char buffer[16];
+  int status;
+
+  status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
+  if (status != 0)
+    return status;
+
+  if (strcasecmp("noAuthNoPriv", buffer) == 0)
+    hd->security_level = SNMP_SEC_LEVEL_NOAUTH;
+  else if (strcasecmp("authNoPriv", buffer) == 0)
+    hd->security_level = SNMP_SEC_LEVEL_AUTHNOPRIV;
+  else if (strcasecmp("authPriv", buffer) == 0)
+    hd->security_level = SNMP_SEC_LEVEL_AUTHPRIV;
+  else
+  {
+    WARNING ("snmp plugin: The `SecurityLevel' config option must be `noAuthNoPriv', `authNoPriv', or `authPriv'.");
+    return (-1);
+  }
+
+  DEBUG ("snmp plugin: host = %s; host->security_level = %d;",
+      hd->name, hd->security_level);
+
+  return (0);
+} /* int csnmp_config_add_host_security_level */
+
 static int csnmp_config_add_host (oconfig_item_t *ci)
 {
   host_definition_t *hd;
@@ -601,12 +643,6 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
   user_data_t cb_data;
   struct timespec cb_interval;
 
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("snmp plugin: `Host' needs exactly one string argument.");
-    return (-1);
-  }
-
   hd = (host_definition_t *) malloc (sizeof (host_definition_t));
   if (hd == NULL)
     return (-1);
@@ -614,12 +650,9 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
   hd->version = 2;
   C_COMPLAIN_INIT (&hd->complaint);
 
-  hd->name = strdup (ci->values[0].value.string);
-  if (hd->name == NULL)
-  {
-    free (hd);
-    return (-1);
-  }
+  status = cf_util_get_string(ci, &hd->name);
+  if (status != 0)
+    return status;
 
   hd->sess_handle = NULL;
   hd->interval = 0;
@@ -630,15 +663,29 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
     status = 0;
 
     if (strcasecmp ("Address", option->key) == 0)
-      status = csnmp_config_add_host_address (hd, option);
+      status = cf_util_get_string(option, &hd->address);
     else if (strcasecmp ("Community", option->key) == 0)
-      status = csnmp_config_add_host_community (hd, option);
+      status = cf_util_get_string(option, &hd->community);
     else if (strcasecmp ("Version", option->key) == 0)
       status = csnmp_config_add_host_version (hd, option);
     else if (strcasecmp ("Collect", option->key) == 0)
       csnmp_config_add_host_collect (hd, option);
     else if (strcasecmp ("Interval", option->key) == 0)
       cf_util_get_cdtime (option, &hd->interval);
+    else if (strcasecmp ("Username", option->key) == 0)
+      status = cf_util_get_string(option, &hd->username);
+    else if (strcasecmp ("AuthProtocol", option->key) == 0)
+      status = csnmp_config_add_host_auth_protocol (hd, option);
+    else if (strcasecmp ("PrivacyProtocol", option->key) == 0)
+      status = csnmp_config_add_host_priv_protocol (hd, option);
+    else if (strcasecmp ("AuthPassphrase", option->key) == 0)
+      status = cf_util_get_string(option, &hd->auth_passphrase);
+    else if (strcasecmp ("PrivacyPassphrase", option->key) == 0)
+      status = cf_util_get_string(option, &hd->priv_passphrase);
+    else if (strcasecmp ("SecurityLevel", option->key) == 0)
+      status = csnmp_config_add_host_security_level (hd, option);
+    else if (strcasecmp ("Context", option->key) == 0)
+      status = cf_util_get_string(option, &hd->context);
     else
     {
       WARNING ("snmp plugin: csnmp_config_add_host: Option `%s' not allowed here.", option->key);
@@ -657,12 +704,57 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
       status = -1;
       break;
     }
-    if (hd->community == NULL)
+    if (hd->community == NULL && hd->version < 3)
     {
       WARNING ("snmp plugin: `Community' not given for host `%s'", hd->name);
       status = -1;
       break;
     }
+    if (hd->version == 3)
+    {
+      if (hd->username == NULL)
+      {
+        WARNING ("snmp plugin: `Username' not given for host `%s'", hd->name);
+        status = -1;
+        break;
+      }
+      if (hd->security_level == 0)
+      {
+        WARNING ("snmp plugin: `SecurityLevel' not given for host `%s'", hd->name);
+        status = -1;
+        break;
+      }
+      if (hd->security_level == SNMP_SEC_LEVEL_AUTHNOPRIV || hd->security_level == SNMP_SEC_LEVEL_AUTHPRIV)
+      {
+       if (hd->auth_protocol == NULL)
+       {
+         WARNING ("snmp plugin: `AuthProtocol' not given for host `%s'", hd->name);
+         status = -1;
+         break;
+       }
+       if (hd->auth_passphrase == NULL)
+       {
+         WARNING ("snmp plugin: `AuthPassphrase' not given for host `%s'", hd->name);
+         status = -1;
+         break;
+       }
+      }
+      if (hd->security_level == SNMP_SEC_LEVEL_AUTHPRIV)
+      {
+       if (hd->priv_protocol == NULL)
+       {
+         WARNING ("snmp plugin: `PrivacyProtocol' not given for host `%s'", hd->name);
+         status = -1;
+         break;
+       }
+       if (hd->priv_passphrase == NULL)
+       {
+         WARNING ("snmp plugin: `PrivacyPassphrase' not given for host `%s'", hd->name);
+         status = -1;
+         break;
+       }
+      }
+    }
 
     break;
   } /* while (status == 0) */
@@ -724,15 +816,75 @@ static int csnmp_config (oconfig_item_t *ci)
 static void csnmp_host_open_session (host_definition_t *host)
 {
   struct snmp_session sess;
+  int error;
 
   if (host->sess_handle != NULL)
     csnmp_host_close_session (host);
 
   snmp_sess_init (&sess);
   sess.peername = host->address;
-  sess.community = (u_char *) host->community;
-  sess.community_len = strlen (host->community);
-  sess.version = (host->version == 1) ? SNMP_VERSION_1 : SNMP_VERSION_2c;
+  switch (host->version)
+  {
+    case 1:
+      sess.version = SNMP_VERSION_1;
+      break;
+    case 3:
+      sess.version = SNMP_VERSION_3;
+      break;
+    default:
+      sess.version = SNMP_VERSION_2c;
+      break;
+  }
+
+  if (host->version == 3)
+  {
+    sess.securityName = host->username;
+    sess.securityNameLen = strlen (host->username);
+    sess.securityLevel = host->security_level;
+
+    if (sess.securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV || sess.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV)
+    {
+      sess.securityAuthProto = host->auth_protocol;
+      sess.securityAuthProtoLen = host->auth_protocol_len;
+      sess.securityAuthKeyLen = USM_AUTH_KU_LEN;
+      error = generate_Ku (sess.securityAuthProto,
+           sess.securityAuthProtoLen,
+           (u_char *) host->auth_passphrase,
+           strlen(host->auth_passphrase),
+           sess.securityAuthKey,
+           &sess.securityAuthKeyLen);
+      if (error != SNMPERR_SUCCESS) {
+       ERROR ("snmp plugin: host %s: Error generating Ku from auth_passphrase. (Error %d)", host->name, error);
+      }
+    }
+
+    if (sess.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV)
+    {
+      sess.securityPrivProto = host->priv_protocol;
+      sess.securityPrivProtoLen = host->priv_protocol_len;
+      sess.securityPrivKeyLen = USM_PRIV_KU_LEN;
+      error = generate_Ku (sess.securityAuthProto,
+           sess.securityAuthProtoLen,
+           (u_char *) host->priv_passphrase,
+           strlen(host->priv_passphrase),
+           sess.securityPrivKey,
+           &sess.securityPrivKeyLen);
+      if (error != SNMPERR_SUCCESS) {
+       ERROR ("snmp plugin: host %s: Error generating Ku from priv_passphrase. (Error %d)", host->name, error);
+      }
+    }
+
+    if (host->context != NULL)
+    {
+      sess.contextName = host->context;
+      sess.contextNameLen = strlen (host->context);
+    }
+  }
+  else /* SNMPv1/2 "authenticates" with community string */
+  {
+    sess.community = (u_char *) host->community;
+    sess.community_len = strlen (host->community);
+  }
 
   /* snmp_sess_open will copy the `struct snmp_session *'. */
   host->sess_handle = snmp_sess_open (&sess);
@@ -896,71 +1048,6 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
   return (ret);
 } /* value_t csnmp_value_list_to_value */
 
-/* Returns true if all OIDs have left their subtree */
-static int csnmp_check_res_left_subtree (const host_definition_t *host,
-    const data_definition_t *data,
-    struct snmp_pdu *res)
-{
-  struct variable_list *vb;
-  int num_checked;
-  int num_left_subtree;
-  int i;
-
-  vb = res->variables;
-  if (vb == NULL)
-    return (-1);
-
-  num_checked = 0;
-  num_left_subtree = 0;
-
-  /* check all the variables and count how many have left their subtree */
-  for (vb = res->variables, i = 0;
-      (vb != NULL) && (i < data->values_len);
-      vb = vb->next_variable, i++)
-  {
-    num_checked++;
-
-    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++;
-  }
-
-  /* check if enough variables have been returned */
-  if (i < data->values_len)
-  {
-    ERROR ("snmp plugin: host %s: Expected %i variables, but got only %i",
-        host->name, data->values_len, i);
-    return (-1);
-  }
-
-  if (data->instance.oid.oid_len > 0)
-  {
-    if (vb == NULL)
-    {
-      ERROR ("snmp plugin: host %s: Expected one more variable for "
-          "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)
-      num_left_subtree++;
-  }
-
-  DEBUG ("snmp plugin: csnmp_check_res_left_subtree: %i of %i variables have "
-      "left their subtree",
-      num_left_subtree, num_checked);
-  if (num_left_subtree >= num_checked)
-    return (1);
-  return (0);
-} /* int csnmp_check_res_left_subtree */
-
 static int csnmp_strvbcopy_hexstring (char *dst, /* {{{ */
     const struct variable_list *vb, size_t dst_size)
 {
@@ -1036,6 +1123,8 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
   struct variable_list *vb;
   oid_t vb_name;
   int status;
+  uint32_t i;
+  uint32_t is_matched;
 
   /* Set vb on the last variable */
   for (vb = res->variables;
@@ -1069,7 +1158,29 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     char *ptr;
 
     csnmp_strvbcopy (il->instance, vb, sizeof (il->instance));
-
+    is_matched = 0;
+    for (i = 0; i < dd->ignores_len; i++)
+    {
+      status = fnmatch(dd->ignores[i], il->instance, 0);
+      if (status == 0)
+      {
+        if (dd->invert_match == 0)
+        {
+          sfree(il);
+          return 0;
+        }
+       else
+       {
+          is_matched = 1;
+         break;
+       }
+      }
+    }
+    if (dd->invert_match != 0 && is_matched == 0)
+    {
+      sfree(il);
+      return 0;
+    }
     for (ptr = il->instance; *ptr != '\0'; ptr++)
     {
       if ((*ptr > 0) && (*ptr < 32))
@@ -1264,11 +1375,18 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   struct variable_list *vb;
 
   const data_set_t *ds;
-  oid_t *oid_list;
-  uint32_t oid_list_len;
+
+  uint32_t oid_list_len = (uint32_t) (data->values_len + 1);
+  /* Holds the last OID returned by the device. We use this in the GETNEXT
+   * request to proceed. */
+  oid_t oid_list[oid_list_len];
+  /* Set to false when an OID has left its subtree so we don't re-request it
+   * again. */
+  _Bool oid_list_todo[oid_list_len];
 
   int status;
   int i;
+  uint32_t j;
 
   /* `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
@@ -1302,19 +1420,15 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   }
 
   /* We need a copy of all the OIDs, because GETNEXT will destroy them. */
-  oid_list_len = data->values_len + 1;
-  oid_list = (oid_t *) malloc (sizeof (oid_t) * (oid_list_len));
-  if (oid_list == NULL)
-  {
-    ERROR ("snmp plugin: csnmp_read_table: malloc failed.");
-    return (-1);
-  }
   memcpy (oid_list, data->values, data->values_len * sizeof (oid_t));
   if (data->instance.oid.oid_len > 0)
     memcpy (oid_list + data->values_len, &data->instance.oid, sizeof (oid_t));
-  else
+  else /* no InstanceFrom option specified. */
     oid_list_len--;
 
+  for (j = 0; j < oid_list_len; j++)
+    oid_list_todo[j] = 1;
+
   /* 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. */
@@ -1323,7 +1437,6 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   if ((value_list_head == NULL) || (value_list_tail == NULL))
   {
     ERROR ("snmp plugin: csnmp_read_table: calloc failed.");
-    sfree (oid_list);
     sfree (value_list_head);
     sfree (value_list_tail);
     return (-1);
@@ -1335,6 +1448,8 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   status = 0;
   while (status == 0)
   {
+    int oid_list_todo_num;
+
     req = snmp_pdu_create (SNMP_MSG_GETNEXT);
     if (req == NULL)
     {
@@ -1343,12 +1458,26 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       break;
     }
 
-    for (i = 0; (uint32_t) i < oid_list_len; i++)
-      snmp_add_null_var (req, oid_list[i].oid, oid_list[i].oid_len);
+    oid_list_todo_num = 0;
+    for (j = 0; j < oid_list_len; j++)
+    {
+      /* Do not rerequest already finished OIDs */
+      if (!oid_list_todo[j])
+        continue;
+      oid_list_todo_num++;
+      snmp_add_null_var (req, oid_list[j].oid, oid_list[j].oid_len);
+    }
+
+    if (oid_list_todo_num == 0)
+    {
+      /* The request is still empty - so we are finished */
+      DEBUG ("snmp plugin: all variables have left their subtree");
+      status = 0;
+      break;
+    }
 
     res = NULL;
     status = snmp_sess_synch_response (host->sess_handle, req, &res);
-
     if ((status != STAT_SUCCESS) || (res == NULL))
     {
       char *errstr = NULL;
@@ -1369,6 +1498,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       status = -1;
       break;
     }
+
     status = 0;
     assert (res != NULL);
     c_release (LOG_INFO, &host->complaint,
@@ -1382,103 +1512,97 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       break;
     }
 
-    /* Check if all values (and possibly the instance) have left their
-     * subtree */
-    if (csnmp_check_res_left_subtree (host, data, res) != 0)
+    for (vb = res->variables, i = 0; (vb != NULL); vb = vb->next_variable, i++)
     {
-      status = 0;
-      break;
-    }
+      /* Calculate value index from todo list */
+      while (!oid_list_todo[i] && (i < oid_list_len))
+        i++;
 
-    /* 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_head, &instance_list_tail,
-           res, host, data) != 0)
+      /* An instance is configured and the res variable we process is the
+       * instance value (last index) */
+      if ((data->instance.oid.oid_len > 0) && (i == data->values_len))
       {
-        ERROR ("snmp plugin: csnmp_instance_list_add failed.");
-        status = -1;
-        break;
-      }
-
-      /* 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 */;
-      assert (vb != NULL);
-
-      /* 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);
-      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++)
-    {
-      csnmp_table_values_t *vt;
-      oid_t vb_name;
-      oid_t suffix;
-
-      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 failed. "
-            "It probably left its subtree.",
-            host->name, data->name, i);
-        continue;
+        if ((vb->type == SNMP_ENDOFMIBVIEW)
+            || (snmp_oid_ncompare (data->instance.oid.oid,
+                data->instance.oid.oid_len,
+                vb->name, vb->name_length,
+                data->instance.oid.oid_len) != 0))
+        {
+          DEBUG ("snmp plugin: host = %s; data = %s; Instance left its subtree.",
+              host->name, data->name);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        /* Allocate a new `csnmp_list_instances_t', insert the instance name and
+         * add it to the list */
+        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;
+        }
       }
-
-      /* 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))
+      else /* The variable we are processing is a normal value */
       {
-        DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
-            "Suffix is not increasing.",
-            host->name, data->name, i);
-        continue;
-      }
+        csnmp_table_values_t *vt;
+        oid_t vb_name;
+        oid_t suffix;
+        int ret;
+
+        csnmp_oid_init (&vb_name, vb->name, vb->name_length);
+
+        /* Calculate the current suffix. This is later used to check that the
+         * suffix is increasing. This also checks if we left the subtree */
+        ret = csnmp_oid_suffix (&suffix, &vb_name, data->values + i);
+        if (ret != 0)
+        {
+          DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
+              "Value probably left its subtree.",
+              host->name, data->name, i);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        /* 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; "
+              "Suffix is not increasing.",
+              host->name, data->name, i);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        vt = malloc (sizeof (*vt));
+        if (vt == NULL)
+        {
+          ERROR ("snmp plugin: malloc failed.");
+          status = -1;
+          break;
+        }
+        memset (vt, 0, sizeof (*vt));
 
-      vt = malloc (sizeof (*vt));
-      if (vt == NULL)
-      {
-        ERROR ("snmp plugin: malloc failed.");
-        status = -1;
-        break;
+        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_list_tail[i] == NULL)
+          value_list_head[i] = vt;
+        else
+          value_list_tail[i]->next = vt;
+        value_list_tail[i] = vt;
       }
-      memset (vt, 0, sizeof (*vt));
-
-      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_list_tail[i] == NULL)
-        value_list_head[i] = vt;
-      else
-        value_list_tail[i]->next = vt;
-      value_list_tail[i] = vt;
-
-      /* Copy OID to oid_list[i + 1] */
+      /* Copy OID to oid_list[i] */
       memcpy (oid_list[i].oid, vb->name, sizeof (oid) * vb->name_length);
       oid_list[i].oid_len = vb->name_length;
-    } /* for (i = data->values_len) */
+
+    } /* for (vb = res->variables ...) */
 
     if (res != NULL)
       snmp_free_pdu (res);
@@ -1512,7 +1636,6 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
 
   sfree (value_list_head);
   sfree (value_list_tail);
-  sfree (oid_list);
 
   return (0);
 } /* int csnmp_read_table */
@@ -1671,10 +1794,10 @@ static int csnmp_read_host (user_data_t *ud)
   if ((time_end - time_start) > host->interval)
   {
     WARNING ("snmp plugin: Host `%s' should be queried every %.3f "
-       "seconds, but reading all values takes %.3f seconds.",
-       host->name,
-       CDTIME_T_TO_DOUBLE (host->interval),
-       CDTIME_T_TO_DOUBLE (time_end - time_start));
+        "seconds, but reading all values takes %.3f seconds.",
+        host->name,
+        CDTIME_T_TO_DOUBLE (host->interval),
+        CDTIME_T_TO_DOUBLE (time_end - time_start));
   }
 
   if (success == 0)
@@ -1708,6 +1831,7 @@ static int csnmp_shutdown (void)
     sfree (data_this->name);
     sfree (data_this->type);
     sfree (data_this->values);
+    sfree (data_this->ignores);
     sfree (data_this);
 
     data_this = data_next;
diff --git a/src/statsd.c b/src/statsd.c
new file mode 100644 (file)
index 0000000..52dd8f8
--- /dev/null
@@ -0,0 +1,913 @@
+/**
+ * collectd - src/statsd.c
+ *
+ * Copyright (C) 2013       Florian octo Forster
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+#include "utils_avltree.h"
+#include "utils_complain.h"
+#include "utils_latency.h"
+
+#include <pthread.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <poll.h>
+
+/* AIX doesn't have MSG_DONTWAIT */
+#ifndef MSG_DONTWAIT
+#  define MSG_DONTWAIT MSG_NONBLOCK
+#endif
+
+#ifndef STATSD_DEFAULT_NODE
+# define STATSD_DEFAULT_NODE NULL
+#endif
+
+#ifndef STATSD_DEFAULT_SERVICE
+# define STATSD_DEFAULT_SERVICE "8125"
+#endif
+
+enum metric_type_e
+{
+  STATSD_COUNTER,
+  STATSD_TIMER,
+  STATSD_GAUGE,
+  STATSD_SET
+};
+typedef enum metric_type_e metric_type_t;
+
+struct statsd_metric_s
+{
+  metric_type_t type;
+  double value;
+  latency_counter_t *latency;
+  c_avl_tree_t *set;
+  unsigned long updates_num;
+};
+typedef struct statsd_metric_s statsd_metric_t;
+
+static c_avl_tree_t   *metrics_tree = NULL;
+static pthread_mutex_t metrics_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_t network_thread;
+static _Bool     network_thread_running = 0;
+static _Bool     network_thread_shutdown = 0;
+
+static char *conf_node = NULL;
+static char *conf_service = NULL;
+
+static _Bool conf_delete_counters = 0;
+static _Bool conf_delete_timers   = 0;
+static _Bool conf_delete_gauges   = 0;
+static _Bool conf_delete_sets     = 0;
+
+static double *conf_timer_percentile = NULL;
+static size_t  conf_timer_percentile_num = 0;
+
+static _Bool conf_timer_lower     = 0;
+static _Bool conf_timer_upper     = 0;
+static _Bool conf_timer_sum       = 0;
+static _Bool conf_timer_count     = 0;
+
+/* Must hold metrics_lock when calling this function. */
+static statsd_metric_t *statsd_metric_lookup_unsafe (char const *name, /* {{{ */
+    metric_type_t type)
+{
+  char key[DATA_MAX_NAME_LEN + 2];
+  char *key_copy;
+  statsd_metric_t *metric;
+  int status;
+
+  switch (type)
+  {
+    case STATSD_COUNTER: key[0] = 'c'; break;
+    case STATSD_TIMER:   key[0] = 't'; break;
+    case STATSD_GAUGE:   key[0] = 'g'; break;
+    case STATSD_SET:     key[0] = 's'; break;
+    default: return (NULL);
+  }
+
+  key[1] = ':';
+  sstrncpy (&key[2], name, sizeof (key) - 2);
+
+  status = c_avl_get (metrics_tree, key, (void *) &metric);
+  if (status == 0)
+    return (metric);
+
+  key_copy = strdup (key);
+  if (key_copy == NULL)
+  {
+    ERROR ("statsd plugin: strdup failed.");
+    return (NULL);
+  }
+
+  metric = malloc (sizeof (*metric));
+  if (metric == NULL)
+  {
+    ERROR ("statsd plugin: malloc failed.");
+    sfree (key_copy);
+    return (NULL);
+  }
+  memset (metric, 0, sizeof (*metric));
+
+  metric->type = type;
+  metric->latency = NULL;
+  metric->set = NULL;
+
+  status = c_avl_insert (metrics_tree, key_copy, metric);
+  if (status != 0)
+  {
+    ERROR ("statsd plugin: c_avl_insert failed.");
+    sfree (key_copy);
+    sfree (metric);
+    return (NULL);
+  }
+
+  return (metric);
+} /* }}} statsd_metric_lookup_unsafe */
+
+static int statsd_metric_set (char const *name, double value, /* {{{ */
+    metric_type_t type)
+{
+  statsd_metric_t *metric;
+
+  pthread_mutex_lock (&metrics_lock);
+
+  metric = statsd_metric_lookup_unsafe (name, type);
+  if (metric == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (-1);
+  }
+
+  metric->value = value;
+  metric->updates_num++;
+
+  pthread_mutex_unlock (&metrics_lock);
+
+  return (0);
+} /* }}} int statsd_metric_set */
+
+static int statsd_metric_add (char const *name, double delta, /* {{{ */
+    metric_type_t type)
+{
+  statsd_metric_t *metric;
+
+  pthread_mutex_lock (&metrics_lock);
+
+  metric = statsd_metric_lookup_unsafe (name, type);
+  if (metric == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (-1);
+  }
+
+  metric->value += delta;
+  metric->updates_num++;
+
+  pthread_mutex_unlock (&metrics_lock);
+
+  return (0);
+} /* }}} int statsd_metric_add */
+
+static int statsd_parse_value (char const *str, value_t *ret_value) /* {{{ */
+{
+  char *endptr = NULL;
+
+  ret_value->gauge = (gauge_t) strtod (str, &endptr);
+  if ((str == endptr) || ((endptr != NULL) && (*endptr != 0)))
+    return (-1);
+
+  return (0);
+} /* }}} int statsd_parse_value */
+
+static int statsd_handle_counter (char const *name, /* {{{ */
+    char const *value_str,
+    char const *extra)
+{
+  value_t value;
+  value_t scale;
+  int status;
+
+  if ((extra != NULL) && (extra[0] != '@'))
+    return (-1);
+
+  scale.gauge = 1.0;
+  if (extra != NULL)
+  {
+    status = statsd_parse_value (extra + 1, &scale);
+    if (status != 0)
+      return (status);
+
+    if (!isfinite (scale.gauge) || (scale.gauge <= 0.0) || (scale.gauge > 1.0))
+      return (-1);
+  }
+
+  value.gauge = 1.0;
+  status = statsd_parse_value (value_str, &value);
+  if (status != 0)
+    return (status);
+
+  return (statsd_metric_add (name, (double) (value.gauge / scale.gauge),
+        STATSD_COUNTER));
+} /* }}} int statsd_handle_counter */
+
+static int statsd_handle_gauge (char const *name, /* {{{ */
+    char const *value_str)
+{
+  value_t value;
+  int status;
+
+  value.gauge = 0;
+  status = statsd_parse_value (value_str, &value);
+  if (status != 0)
+    return (status);
+
+  if ((value_str[0] == '+') || (value_str[0] == '-'))
+    return (statsd_metric_add (name, (double) value.gauge, STATSD_GAUGE));
+  else
+    return (statsd_metric_set (name, (double) value.gauge, STATSD_GAUGE));
+} /* }}} int statsd_handle_gauge */
+
+static int statsd_handle_timer (char const *name, /* {{{ */
+    char const *value_str)
+{
+  statsd_metric_t *metric;
+  value_t value_ms;
+  cdtime_t value;
+  int status;
+
+  value_ms.derive = 0;
+  status = statsd_parse_value (value_str, &value_ms);
+  if (status != 0)
+    return (status);
+
+  value = MS_TO_CDTIME_T (value_ms.gauge);
+
+  pthread_mutex_lock (&metrics_lock);
+
+  metric = statsd_metric_lookup_unsafe (name, STATSD_TIMER);
+  if (metric == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (-1);
+  }
+
+  if (metric->latency == NULL)
+    metric->latency = latency_counter_create ();
+  if (metric->latency == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (-1);
+  }
+
+  latency_counter_add (metric->latency, value);
+  metric->updates_num++;
+
+  pthread_mutex_unlock (&metrics_lock);
+  return (0);
+} /* }}} int statsd_handle_timer */
+
+static int statsd_handle_set (char const *name, /* {{{ */
+    char const *set_key_orig)
+{
+  statsd_metric_t *metric = NULL;
+  char *set_key;
+  int status;
+
+  pthread_mutex_lock (&metrics_lock);
+
+  metric = statsd_metric_lookup_unsafe (name, STATSD_SET);
+  if (metric == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (-1);
+  }
+
+  /* Make sure metric->set exists. */
+  if (metric->set == NULL)
+    metric->set = c_avl_create ((void *) strcmp);
+
+  if (metric->set == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    ERROR ("statsd plugin: c_avl_create failed.");
+    return (-1);
+  }
+
+  set_key = strdup (set_key_orig);
+  if (set_key == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    ERROR ("statsd plugin: strdup failed.");
+    return (-1);
+  }
+
+  status = c_avl_insert (metric->set, set_key, /* value = */ NULL);
+  if (status < 0)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    if (status < 0)
+      ERROR ("statsd plugin: c_avl_insert (\"%s\") failed with status %i.",
+          set_key, status);
+    sfree (set_key);
+    return (-1);
+  }
+  else if (status > 0) /* key already exists */
+  {
+    sfree (set_key);
+  }
+
+  metric->updates_num++;
+
+  pthread_mutex_unlock (&metrics_lock);
+  return (0);
+} /* }}} int statsd_handle_set */
+
+static int statsd_parse_line (char *buffer) /* {{{ */
+{
+  char *name = buffer;
+  char *value;
+  char *type;
+  char *extra;
+
+  type = strchr (name, '|');
+  if (type == NULL)
+    return (-1);
+  *type = 0;
+  type++;
+
+  value = strrchr (name, ':');
+  if (value == NULL)
+    return (-1);
+  *value = 0;
+  value++;
+
+  extra = strchr (type, '|');
+  if (extra != NULL)
+  {
+    *extra = 0;
+    extra++;
+  }
+
+  if (strcmp ("c", type) == 0)
+    return (statsd_handle_counter (name, value, extra));
+
+  /* extra is only valid for counters */
+  if (extra != NULL)
+    return (-1);
+
+  if (strcmp ("g", type) == 0)
+    return (statsd_handle_gauge (name, value));
+  else if (strcmp ("ms", type) == 0)
+    return (statsd_handle_timer (name, value));
+  else if (strcmp ("s", type) == 0)
+    return (statsd_handle_set (name, value));
+  else
+    return (-1);
+} /* }}} void statsd_parse_line */
+
+static void statsd_parse_buffer (char *buffer) /* {{{ */
+{
+  while (buffer != NULL)
+  {
+    char orig[64];
+    char *next;
+    int status;
+
+    next = strchr (buffer, '\n');
+    if (next != NULL)
+    {
+      *next = 0;
+      next++;
+    }
+
+    if (*buffer == 0)
+    {
+      buffer = next;
+      continue;
+    }
+
+    sstrncpy (orig, buffer, sizeof (orig));
+
+    status = statsd_parse_line (buffer);
+    if (status != 0)
+      ERROR ("statsd plugin: Unable to parse line: \"%s\"", orig);
+
+    buffer = next;
+  }
+} /* }}} void statsd_parse_buffer */
+
+static void statsd_network_read (int fd) /* {{{ */
+{
+  char buffer[4096];
+  size_t buffer_size;
+  ssize_t status;
+
+  status = recv (fd, buffer, sizeof (buffer), /* flags = */ MSG_DONTWAIT);
+  if (status < 0)
+  {
+    char errbuf[1024];
+
+    if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+      return;
+
+    ERROR ("statsd plugin: recv(2) failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return;
+  }
+
+  buffer_size = (size_t) status;
+  if (buffer_size >= sizeof (buffer))
+    buffer_size = sizeof (buffer) - 1;
+  buffer[buffer_size] = 0;
+
+  statsd_parse_buffer (buffer);
+} /* }}} void statsd_network_read */
+
+static int statsd_network_init (struct pollfd **ret_fds, /* {{{ */
+    size_t *ret_fds_num)
+{
+  struct pollfd *fds = NULL;
+  size_t fds_num = 0;
+
+  struct addrinfo ai_hints;
+  struct addrinfo *ai_list = NULL;
+  struct addrinfo *ai_ptr;
+  int status;
+
+  char const *node = (conf_node != NULL) ? conf_node : STATSD_DEFAULT_NODE;
+  char const *service = (conf_service != NULL)
+    ? conf_service : STATSD_DEFAULT_SERVICE;
+
+  memset (&ai_hints, 0, sizeof (ai_hints));
+  ai_hints.ai_flags = AI_PASSIVE;
+#ifdef AI_ADDRCONFIG
+  ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+  ai_hints.ai_family = AF_UNSPEC;
+  ai_hints.ai_socktype = SOCK_DGRAM;
+
+  status = getaddrinfo (node, service, &ai_hints, &ai_list);
+  if (status != 0)
+  {
+    ERROR ("statsd plugin: getaddrinfo (\"%s\", \"%s\") failed: %s",
+        node, service, gai_strerror (status));
+    return (status);
+  }
+
+  for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    int fd;
+    struct pollfd *tmp;
+
+    char dbg_node[NI_MAXHOST];
+    char dbg_service[NI_MAXSERV];
+
+    fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (fd < 0)
+    {
+      char errbuf[1024];
+      ERROR ("statsd plugin: socket(2) failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      continue;
+    }
+
+    getnameinfo (ai_ptr->ai_addr, ai_ptr->ai_addrlen,
+        dbg_node, sizeof (dbg_node), dbg_service, sizeof (dbg_service),
+        NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV);
+    DEBUG ("statsd plugin: Trying to bind to [%s]:%s ...", dbg_node, dbg_service);
+
+    status = bind (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    if (status != 0)
+    {
+      char errbuf[1024];
+      ERROR ("statsd plugin: bind(2) failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      close (fd);
+      continue;
+    }
+
+    tmp = realloc (fds, sizeof (*fds) * (fds_num + 1));
+    if (tmp == NULL)
+    {
+      ERROR ("statsd plugin: realloc failed.");
+      continue;
+    }
+    fds = tmp;
+    tmp = fds + fds_num;
+    fds_num++;
+
+    memset (tmp, 0, sizeof (*tmp));
+    tmp->fd = fd;
+    tmp->events = POLLIN | POLLPRI;
+  }
+
+  freeaddrinfo (ai_list);
+
+  if (fds_num == 0)
+  {
+    ERROR ("statsd plugin: Unable to create listening socket for [%s]:%s.",
+        (node != NULL) ? node : "::", service);
+    return (ENOENT);
+  }
+
+  *ret_fds = fds;
+  *ret_fds_num = fds_num;
+  return (0);
+} /* }}} int statsd_network_init */
+
+static void *statsd_network_thread (void *args) /* {{{ */
+{
+  struct pollfd *fds = NULL;
+  size_t fds_num = 0;
+  int status;
+  size_t i;
+
+  status = statsd_network_init (&fds, &fds_num);
+  if (status != 0)
+  {
+    ERROR ("statsd plugin: Unable to open listening sockets.");
+    pthread_exit ((void *) 0);
+  }
+
+  while (!network_thread_shutdown)
+  {
+    status = poll (fds, (nfds_t) fds_num, /* timeout = */ -1);
+    if (status < 0)
+    {
+      char errbuf[1024];
+
+      if ((errno == EINTR) || (errno == EAGAIN))
+        continue;
+
+      ERROR ("statsd plugin: poll(2) failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      break;
+    }
+
+    for (i = 0; i < fds_num; i++)
+    {
+      if ((fds[i].revents & (POLLIN | POLLPRI)) == 0)
+        continue;
+
+      statsd_network_read (fds[i].fd);
+      fds[i].revents = 0;
+    }
+  } /* while (!network_thread_shutdown) */
+
+  /* Clean up */
+  for (i = 0; i < fds_num; i++)
+    close (fds[i].fd);
+  sfree (fds);
+
+  return ((void *) 0);
+} /* }}} void *statsd_network_thread */
+
+static int statsd_config_timer_percentile (oconfig_item_t *ci) /* {{{ */
+{
+  double percent = NAN;
+  double *tmp;
+  int status;
+
+  status = cf_util_get_double (ci, &percent);
+  if (status != 0)
+    return (status);
+
+  if ((percent <= 0.0) || (percent >= 100))
+  {
+    ERROR ("statsd plugin: The value for \"%s\" must be between 0 and 100, "
+        "exclusively.", ci->key);
+    return (ERANGE);
+  }
+
+  tmp = realloc (conf_timer_percentile,
+      sizeof (*conf_timer_percentile) * (conf_timer_percentile_num + 1));
+  if (tmp == NULL)
+  {
+    ERROR ("statsd plugin: realloc failed.");
+    return (ENOMEM);
+  }
+  conf_timer_percentile = tmp;
+  conf_timer_percentile[conf_timer_percentile_num] = percent;
+  conf_timer_percentile_num++;
+
+  return (0);
+} /* }}} int statsd_config_timer_percentile */
+
+static int statsd_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Host", child->key) == 0)
+      cf_util_get_string (child, &conf_node);
+    else if (strcasecmp ("Port", child->key) == 0)
+      cf_util_get_service (child, &conf_service);
+    else if (strcasecmp ("DeleteCounters", child->key) == 0)
+      cf_util_get_boolean (child, &conf_delete_counters);
+    else if (strcasecmp ("DeleteTimers", child->key) == 0)
+      cf_util_get_boolean (child, &conf_delete_timers);
+    else if (strcasecmp ("DeleteGauges", child->key) == 0)
+      cf_util_get_boolean (child, &conf_delete_gauges);
+    else if (strcasecmp ("DeleteSets", child->key) == 0)
+      cf_util_get_boolean (child, &conf_delete_sets);
+    else if (strcasecmp ("TimerLower", child->key) == 0)
+      cf_util_get_boolean (child, &conf_timer_lower);
+    else if (strcasecmp ("TimerUpper", child->key) == 0)
+      cf_util_get_boolean (child, &conf_timer_upper);
+    else if (strcasecmp ("TimerSum", child->key) == 0)
+      cf_util_get_boolean (child, &conf_timer_sum);
+    else if (strcasecmp ("TimerCount", child->key) == 0)
+      cf_util_get_boolean (child, &conf_timer_count);
+    else if (strcasecmp ("TimerPercentile", child->key) == 0)
+      statsd_config_timer_percentile (child);
+    else
+      ERROR ("statsd plugin: The \"%s\" config option is not valid.",
+          child->key);
+  }
+
+  return (0);
+} /* }}} int statsd_config */
+
+static int statsd_init (void) /* {{{ */
+{
+  pthread_mutex_lock (&metrics_lock);
+  if (metrics_tree == NULL)
+    metrics_tree = c_avl_create ((void *) strcmp);
+
+  if (!network_thread_running)
+  {
+    int status;
+
+    status = pthread_create (&network_thread,
+        /* attr = */ NULL,
+        statsd_network_thread,
+        /* args = */ NULL);
+    if (status != 0)
+    {
+      char errbuf[1024];
+      pthread_mutex_unlock (&metrics_lock);
+      ERROR ("statsd plugin: pthread_create failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      return (status);
+    }
+  }
+  network_thread_running = 1;
+
+  pthread_mutex_unlock (&metrics_lock);
+
+  return (0);
+} /* }}} int statsd_init */
+
+/* Must hold metrics_lock when calling this function. */
+static int statsd_metric_clear_set_unsafe (statsd_metric_t *metric) /* {{{ */
+{
+  void *key;
+  void *value;
+
+  if ((metric == NULL) || (metric->type != STATSD_SET))
+    return (EINVAL);
+
+  if (metric->set == NULL)
+    return (0);
+
+  while (c_avl_pick (metric->set, &key, &value) == 0)
+  {
+    sfree (key);
+    sfree (value);
+  }
+
+  return (0);
+} /* }}} int statsd_metric_clear_set_unsafe */
+
+/* Must hold metrics_lock when calling this function. */
+static int statsd_metric_submit_unsafe (char const *name, /* {{{ */
+    statsd_metric_t const *metric)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "statsd", sizeof (vl.plugin));
+
+  if (metric->type == STATSD_GAUGE)
+    sstrncpy (vl.type, "gauge", sizeof (vl.type));
+  else if (metric->type == STATSD_TIMER)
+    sstrncpy (vl.type, "latency", sizeof (vl.type));
+  else if (metric->type == STATSD_SET)
+    sstrncpy (vl.type, "objects", sizeof (vl.type));
+  else /* if (metric->type == STATSD_COUNTER) */
+    sstrncpy (vl.type, "derive", sizeof (vl.type));
+
+  sstrncpy (vl.type_instance, name, sizeof (vl.type_instance));
+
+  if (metric->type == STATSD_GAUGE)
+    values[0].gauge = (gauge_t) metric->value;
+  else if (metric->type == STATSD_TIMER)
+  {
+    size_t i;
+
+    if (metric->updates_num == 0)
+      return (0);
+
+    vl.time = cdtime ();
+
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+        "%s-average", name);
+    values[0].gauge = CDTIME_T_TO_DOUBLE (
+        latency_counter_get_average (metric->latency));
+    plugin_dispatch_values (&vl);
+
+    if (conf_timer_lower) {
+      ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+          "%s-lower", name);
+      values[0].gauge = CDTIME_T_TO_DOUBLE (
+          latency_counter_get_min (metric->latency));
+      plugin_dispatch_values (&vl);
+    }
+
+    if (conf_timer_upper) {
+      ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+          "%s-upper", name);
+      values[0].gauge = CDTIME_T_TO_DOUBLE (
+          latency_counter_get_max (metric->latency));
+      plugin_dispatch_values (&vl);
+    }
+
+    if (conf_timer_sum) {
+      ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+          "%s-sum", name);
+      values[0].gauge = CDTIME_T_TO_DOUBLE (
+          latency_counter_get_sum (metric->latency));
+      plugin_dispatch_values (&vl);
+    }
+
+    for (i = 0; i < conf_timer_percentile_num; i++)
+    {
+      ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+          "%s-percentile-%.0f", name, conf_timer_percentile[i]);
+      values[0].gauge = CDTIME_T_TO_DOUBLE (
+          latency_counter_get_percentile (
+            metric->latency, conf_timer_percentile[i]));
+      plugin_dispatch_values (&vl);
+    }
+
+    /* Keep this at the end, since vl.type is set to "gauge" here. The
+     * vl.type's above are implicitly set to "latency". */
+    if (conf_timer_count) {
+      sstrncpy (vl.type, "gauge", sizeof (vl.type));
+      ssnprintf (vl.type_instance, sizeof (vl.type_instance),
+          "%s-count", name);
+      values[0].gauge = latency_counter_get_num (metric->latency);
+      plugin_dispatch_values (&vl);
+    }
+
+    latency_counter_reset (metric->latency);
+    return (0);
+  }
+  else if (metric->type == STATSD_SET)
+  {
+    if (metric->set == NULL)
+      values[0].gauge = 0.0;
+    else
+      values[0].gauge = (gauge_t) c_avl_size (metric->set);
+  }
+  else
+    values[0].derive = (derive_t) metric->value;
+
+  return (plugin_dispatch_values (&vl));
+} /* }}} int statsd_metric_submit_unsafe */
+
+static int statsd_read (void) /* {{{ */
+{
+  c_avl_iterator_t *iter;
+  char *name;
+  statsd_metric_t *metric;
+
+  char **to_be_deleted = NULL;
+  size_t to_be_deleted_num = 0;
+  size_t i;
+
+  pthread_mutex_lock (&metrics_lock);
+
+  if (metrics_tree == NULL)
+  {
+    pthread_mutex_unlock (&metrics_lock);
+    return (0);
+  }
+
+  iter = c_avl_get_iterator (metrics_tree);
+  while (c_avl_iterator_next (iter, (void *) &name, (void *) &metric) == 0)
+  {
+    if ((metric->updates_num == 0)
+        && ((conf_delete_counters && (metric->type == STATSD_COUNTER))
+          || (conf_delete_timers && (metric->type == STATSD_TIMER))
+          || (conf_delete_gauges && (metric->type == STATSD_GAUGE))
+          || (conf_delete_sets && (metric->type == STATSD_SET))))
+    {
+      DEBUG ("statsd plugin: Deleting metric \"%s\".", name);
+      strarray_add (&to_be_deleted, &to_be_deleted_num, name);
+      continue;
+    }
+
+    /* Names have a prefix, e.g. "c:", which determines the (statsd) type.
+     * Remove this here. */
+    statsd_metric_submit_unsafe (name + 2, metric);
+
+    /* Reset the metric. */
+    metric->updates_num = 0;
+    if (metric->type == STATSD_SET)
+      statsd_metric_clear_set_unsafe (metric);
+  }
+  c_avl_iterator_destroy (iter);
+
+  for (i = 0; i < to_be_deleted_num; i++)
+  {
+    int status;
+
+    status = c_avl_remove (metrics_tree, to_be_deleted[i],
+        (void *) &name, (void *) &metric);
+    if (status != 0)
+    {
+      ERROR ("stats plugin: c_avl_remove (\"%s\") failed with status %i.",
+          to_be_deleted[i], status);
+      continue;
+    }
+
+    sfree (name);
+    sfree (metric);
+  }
+
+  pthread_mutex_unlock (&metrics_lock);
+
+  strarray_free (to_be_deleted, to_be_deleted_num);
+
+  return (0);
+} /* }}} int statsd_read */
+
+static int statsd_shutdown (void) /* {{{ */
+{
+  void *key;
+  void *value;
+
+  pthread_mutex_lock (&metrics_lock);
+
+  if (network_thread_running)
+  {
+    network_thread_shutdown = 1;
+    pthread_kill (network_thread, SIGTERM);
+    pthread_join (network_thread, /* retval = */ NULL);
+  }
+  network_thread_running = 0;
+
+  while (c_avl_pick (metrics_tree, &key, &value) == 0)
+  {
+    sfree (key);
+    sfree (value);
+  }
+  c_avl_destroy (metrics_tree);
+  metrics_tree = NULL;
+
+  sfree (conf_node);
+  sfree (conf_service);
+
+  pthread_mutex_unlock (&metrics_lock);
+
+  return (0);
+} /* }}} int statsd_shutdown */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("statsd", statsd_config);
+  plugin_register_init ("statsd", statsd_init);
+  plugin_register_read ("statsd", statsd_read);
+  plugin_register_shutdown ("statsd", statsd_shutdown);
+}
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
index 46d3534..508f9d5 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/swap.c
- * Copyright (C) 2005-2012  Florian octo Forster
+ * Copyright (C) 2005-2014  Florian octo Forster
  * Copyright (C) 2009       Stefan Völkel
  * Copyright (C) 2009       Manuel Sanmartin
  * Copyright (C) 2010       Aurélien Reynaud
@@ -97,48 +97,45 @@ int kvm_pagesize;
 
 #elif HAVE_PERFSTAT
 static int pagesize;
-static perfstat_memory_total_t pmemory;
 /*# endif HAVE_PERFSTAT */
 
 #else
 # error "No applicable input method."
 #endif /* HAVE_LIBSTATGRAB */
 
-static const char *config_keys[] =
-{
-       "ReportBytes",
-       "ReportByDevice"
-};
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+static _Bool values_absolute = 1;
+static _Bool values_percentage = 0;
 
-static int swap_config (const char *key, const char *value) /* {{{ */
+static int swap_config (oconfig_item_t *ci) /* {{{ */
 {
-       if (strcasecmp ("ReportBytes", key) == 0)
+       int i;
+
+       for (i = 0; i < ci->children_num; i++)
        {
+               oconfig_item_t *child = ci->children + i;
+               if (strcasecmp ("ReportBytes", child->key) == 0)
 #if KERNEL_LINUX
-               report_bytes = IS_TRUE (value) ? 1 : 0;
+                       cf_util_get_boolean (child, &report_bytes);
 #else
-               WARNING ("swap plugin: The \"ReportBytes\" option is only "
-                               "valid under Linux. "
-                               "The option is going to be ignored.");
+                       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)
-       {
+               else if (strcasecmp ("ReportByDevice", child->key) == 0)
 #if SWAP_HAVE_REPORT_BY_DEVICE
-               if (IS_TRUE (value))
-                       report_by_device = 1;
-               else
-                       report_by_device = 0;
+                       cf_util_get_boolean (child, &report_by_device);
 #else
-               WARNING ("swap plugin: The \"ReportByDevice\" option is not "
-                               "supported on this platform. "
-                               "The option is going to be ignored.");
+                       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
-       {
-               return (-1);
+               else if (strcasecmp ("ValuesAbsolute", child->key) == 0)
+                       cf_util_get_boolean (child, &values_absolute);
+               else if (strcasecmp ("ValuesPercentage", child->key) == 0)
+                       cf_util_get_boolean (child, &values_percentage);
+               else
+                       WARNING ("swap plugin: Unknown config option: \"%s\"",
+                                       child->key);
        }
 
        return (0);
@@ -191,44 +188,50 @@ static int swap_init (void) /* {{{ */
        return (0);
 } /* }}} int swap_init */
 
-static void swap_submit (const char *plugin_instance, /* {{{ */
-               const char *type, const char *type_instance,
-               value_t value)
+static void swap_submit_usage (char const *plugin_instance, /* {{{ */
+               gauge_t used, gauge_t free,
+               char const *other_name, gauge_t other_value)
 {
+       value_t v[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       assert (type != NULL);
-
-       vl.values = &value;
-       vl.values_len = 1;
+       vl.values = v;
+       vl.values_len = STATIC_ARRAY_SIZE (v);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "swap", sizeof (vl.plugin));
        if (plugin_instance != NULL)
-               sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-       if (type_instance != NULL)
-               sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
-
-       plugin_dispatch_values (&vl);
-} /* }}} void swap_submit_inst */
+               sstrncpy (vl.plugin_instance, plugin_instance,
+                               sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "swap", sizeof (vl.type));
+
+       if (values_absolute)
+               plugin_dispatch_multivalue (&vl, 0,
+                               "used", used, "free", free,
+                               other_name, other_value, NULL);
+       if (values_percentage)
+               plugin_dispatch_multivalue (&vl, 1,
+                               "used", used, "free", free,
+                               other_name, other_value, NULL);
+} /* }}} void swap_submit_usage */
 
-static void swap_submit_gauge (const char *plugin_instance, /* {{{ */
-               const char *type_instance, gauge_t value)
+#if KERNEL_LINUX || HAVE_PERFSTAT
+__attribute__((nonnull(1)))
+static void swap_submit_derive (char const *type_instance, /* {{{ */
+               derive_t value)
 {
-       value_t v;
+       value_list_t vl = VALUE_LIST_INIT;
+       value_t v[1];
 
-       v.gauge = value;
-       swap_submit (plugin_instance, "swap", type_instance, v);
-} /* }}} void swap_submit_gauge */
+       v[0].derive = value;
 
-#if KERNEL_LINUX || HAVE_PERFSTAT
-static void swap_submit_derive (const char *plugin_instance, /* {{{ */
-               const char *type_instance, derive_t value)
-{
-       value_t v;
+       vl.values = v;
+       vl.values_len = STATIC_ARRAY_SIZE (v);
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "swap", sizeof (vl.plugin));
+       sstrncpy (vl.type, "swap_io", sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
-       v.derive = value;
-       swap_submit (plugin_instance, "swap_io", type_instance, v);
+       plugin_dispatch_values (&vl);
 } /* }}} void swap_submit_derive */
 #endif
 
@@ -254,9 +257,8 @@ static int swap_read_separate (void) /* {{{ */
                char *endptr;
 
                char path[PATH_MAX];
-               gauge_t size;
+               gauge_t total;
                gauge_t used;
-               gauge_t free;
 
                numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
                if (numfields != 5)
@@ -267,7 +269,7 @@ static int swap_read_separate (void) /* {{{ */
 
                errno = 0;
                endptr = NULL;
-               size = strtod (fields[2], &endptr);
+               total = strtod (fields[2], &endptr);
                if ((endptr == fields[2]) || (errno != 0))
                        continue;
 
@@ -277,13 +279,10 @@ static int swap_read_separate (void) /* {{{ */
                if ((endptr == fields[3]) || (errno != 0))
                        continue;
 
-               if (size < used)
+               if (total < used)
                        continue;
 
-               free = size - used;
-
-               swap_submit_gauge (path, "used", used);
-               swap_submit_gauge (path, "free", free);
+               swap_submit_usage (path, used, total - used, NULL, NAN);
        }
 
        fclose (fh);
@@ -349,10 +348,7 @@ static int swap_read_combined (void) /* {{{ */
 
        swap_used = swap_total - (swap_free + swap_cached);
 
-       swap_submit_gauge (NULL, "used",   1024.0 * swap_used);
-       swap_submit_gauge (NULL, "free",   1024.0 * swap_free);
-       swap_submit_gauge (NULL, "cached", 1024.0 * swap_cached);
-
+       swap_submit_usage (NULL, swap_used, swap_free, "cached", swap_cached);
        return (0);
 } /* }}} int swap_read_combined */
 
@@ -430,8 +426,8 @@ static int swap_read_io (void) /* {{{ */
                swap_out = swap_out * pagesize;
        }
 
-       swap_submit_derive (NULL, "in",  swap_in);
-       swap_submit_derive (NULL, "out", swap_out);
+       swap_submit_derive ("in",  swap_in);
+       swap_submit_derive ("out", swap_out);
 
        return (0);
 } /* }}} int swap_read_io */
@@ -462,9 +458,9 @@ static int swap_read (void) /* {{{ */
 /* kstat-based read function */
 static int swap_read_kstat (void) /* {{{ */
 {
-       derive_t swap_alloc;
-       derive_t swap_resv;
-       derive_t swap_avail;
+       gauge_t swap_alloc;
+       gauge_t swap_resv;
+       gauge_t swap_avail;
 
        struct anoninfo ai;
 
@@ -497,15 +493,11 @@ static int swap_read_kstat (void) /* {{{ */
         * swap_alloc = pagesize * ( ai.ani_max - ai.ani_free );
         * can suffer from a 32bit overflow.
         */
-       swap_alloc  = (derive_t) ((ai.ani_max - ai.ani_free) * pagesize);
-       swap_resv   = (derive_t) ((ai.ani_resv + ai.ani_free - ai.ani_max)
-                       * pagesize);
-       swap_avail  = (derive_t) ((ai.ani_max - ai.ani_resv) * pagesize);
-
-       swap_submit_gauge (NULL, "used", swap_alloc);
-       swap_submit_gauge (NULL, "free", swap_avail);
-       swap_submit_gauge (NULL, "reserved", swap_resv);
+       swap_alloc = (gauge_t) ((ai.ani_max - ai.ani_free) * pagesize);
+       swap_resv  = (gauge_t) ((ai.ani_resv + ai.ani_free - ai.ani_max) * pagesize);
+       swap_avail = (gauge_t) ((ai.ani_max - ai.ani_resv) * pagesize);
 
+       swap_submit_usage (NULL, swap_alloc, swap_avail, "reserved", swap_resv);
        return (0);
 } /* }}} int swap_read_kstat */
 /* #endif 0 && HAVE_LIBKSTAT */
@@ -520,8 +512,8 @@ static int swap_read (void) /* {{{ */
         int status;
         int i;
 
-        derive_t avail = 0;
-        derive_t total = 0;
+        gauge_t avail = 0;
+        gauge_t total = 0;
 
         swap_num = swapctl (SC_GETNSWP, NULL);
         if (swap_num < 0)
@@ -584,14 +576,14 @@ static int swap_read (void) /* {{{ */
         for (i = 0; i < swap_num; i++)
         {
                char path[PATH_MAX];
-               derive_t this_total;
-               derive_t this_avail;
+               gauge_t this_total;
+               gauge_t this_avail;
 
                 if ((s->swt_ent[i].ste_flags & ST_INDEL) != 0)
                         continue;
 
-               this_total = ((derive_t) s->swt_ent[i].ste_pages) * pagesize;
-               this_avail = ((derive_t) s->swt_ent[i].ste_free)  * pagesize;
+               this_total = (gauge_t) (s->swt_ent[i].ste_pages * pagesize);
+               this_avail = (gauge_t) (s->swt_ent[i].ste_free  * pagesize);
 
                /* Shortcut for the "combined" setting (default) */
                if (!report_by_device)
@@ -604,27 +596,23 @@ static int swap_read (void) /* {{{ */
                sstrncpy (path, s->swt_ent[i].ste_path, sizeof (path));
                escape_slashes (path, sizeof (path));
 
-               swap_submit_gauge (path, "used", (gauge_t) (this_total - this_avail));
-               swap_submit_gauge (path, "free", (gauge_t) this_avail);
+               swap_submit_usage (path, this_total - this_avail, this_avail,
+                               NULL, NAN);
         } /* for (swap_num) */
 
         if (total < avail)
         {
-                ERROR ("swap plugin: Total swap space (%"PRIi64") "
-                                "is less than free swap space (%"PRIi64").",
+                ERROR ("swap plugin: Total swap space (%g) is less than free swap space (%g).",
                                 total, avail);
                sfree (s_paths);
                 sfree (s);
                 return (-1);
         }
 
-       /* If the "separate" option was specified (report_by_device == 2), all
+       /* If the "separate" option was specified (report_by_device == 1), all
         * values have already been dispatched from within the loop. */
        if (!report_by_device)
-       {
-               swap_submit_gauge (NULL, "used", (gauge_t) (total - avail));
-               swap_submit_gauge (NULL, "free", (gauge_t) avail);
-       }
+               swap_submit_usage (NULL, total - avail, avail, NULL, NAN);
 
        sfree (s_paths);
         sfree (s);
@@ -640,8 +628,8 @@ static int swap_read (void) /* {{{ */
        int status;
        int i;
 
-       derive_t used  = 0;
-       derive_t total = 0;
+       gauge_t used  = 0;
+       gauge_t total = 0;
 
        swap_num = swapctl (SWAP_NSWAP, NULL, 0);
        if (swap_num < 0)
@@ -670,35 +658,32 @@ static int swap_read (void) /* {{{ */
        }
 
 #if defined(DEV_BSIZE) && (DEV_BSIZE > 0)
-# define C_SWAP_BLOCK_SIZE ((derive_t) DEV_BSIZE)
+# define C_SWAP_BLOCK_SIZE ((gauge_t) DEV_BSIZE)
 #else
-# define C_SWAP_BLOCK_SIZE ((derive_t) 512)
+# define C_SWAP_BLOCK_SIZE 512.0
 #endif
 
+       /* TODO: Report per-device stats. The path name is available from
+        * swap_entries[i].se_path */
        for (i = 0; i < swap_num; i++)
        {
                if ((swap_entries[i].se_flags & SWF_ENABLE) == 0)
                        continue;
 
-               used  += ((derive_t) swap_entries[i].se_inuse)
-                       * C_SWAP_BLOCK_SIZE;
-               total += ((derive_t) swap_entries[i].se_nblks)
-                       * C_SWAP_BLOCK_SIZE;
+               used  += ((gauge_t) swap_entries[i].se_inuse) * C_SWAP_BLOCK_SIZE;
+               total += ((gauge_t) swap_entries[i].se_nblks) * C_SWAP_BLOCK_SIZE;
        }
 
        if (total < used)
        {
-               ERROR ("swap plugin: Total swap space (%"PRIu64") "
-                               "is less than used swap space (%"PRIu64").",
+               ERROR ("swap plugin: Total swap space (%g) is less than used swap space (%g).",
                                total, used);
                return (-1);
        }
 
-       swap_submit_gauge (NULL, "used", (gauge_t) used);
-       swap_submit_gauge (NULL, "free", (gauge_t) (total - used));
+       swap_submit_usage (NULL, used, total - used, NULL, NAN);
 
        sfree (swap_entries);
-
        return (0);
 } /* }}} int swap_read */
 /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS */
@@ -721,8 +706,9 @@ static int swap_read (void) /* {{{ */
                return (-1);
 
        /* The returned values are bytes. */
-       swap_submit_gauge (NULL, "used", (gauge_t) sw_usage.xsu_used);
-       swap_submit_gauge (NULL, "free", (gauge_t) sw_usage.xsu_avail);
+       swap_submit_usage (NULL,
+                       (gauge_t) sw_usage.xsu_used, (gauge_t) sw_usage.xsu_avail,
+                       NULL, NAN);
 
        return (0);
 } /* }}} int swap_read */
@@ -734,9 +720,8 @@ static int swap_read (void) /* {{{ */
        struct kvm_swap data_s;
        int             status;
 
-       derive_t used;
-       derive_t free;
-       derive_t total;
+       gauge_t used;
+       gauge_t total;
 
        if (kvm_obj == NULL)
                return (-1);
@@ -746,16 +731,13 @@ static int swap_read (void) /* {{{ */
        if (status == -1)
                return (-1);
 
-       total = (derive_t) data_s.ksw_total;
-       used  = (derive_t) data_s.ksw_used;
+       total = (gauge_t) data_s.ksw_total;
+       used  = (gauge_t) data_s.ksw_used;
 
-       total *= (derive_t) kvm_pagesize;
-       used  *= (derive_t) kvm_pagesize;
+       total *= (gauge_t) kvm_pagesize;
+       used  *= (gauge_t) kvm_pagesize;
 
-       free = total - used;
-
-       swap_submit_gauge (NULL, "used", (gauge_t) used);
-       swap_submit_gauge (NULL, "free", (gauge_t) free);
+       swap_submit_usage (NULL, used, total - used, NULL, NAN);
 
        return (0);
 } /* }}} int swap_read */
@@ -767,12 +749,11 @@ static int swap_read (void) /* {{{ */
        sg_swap_stats *swap;
 
        swap = sg_get_swap_stats ();
-
        if (swap == NULL)
                return (-1);
 
-       swap_submit_gauge (NULL, "used", (gauge_t) swap->used);
-       swap_submit_gauge (NULL, "free", (gauge_t) swap->free);
+       swap_submit_usage (NULL, (gauge_t) swap->used, (gauge_t) swap->free,
+                       NULL, NAN);
 
        return (0);
 } /* }}} int swap_read */
@@ -781,19 +762,30 @@ static int swap_read (void) /* {{{ */
 #elif HAVE_PERFSTAT
 static int swap_read (void) /* {{{ */
 {
-        if(perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1) < 0)
+       perfstat_memory_total_t pmemory;
+       int status;
+
+       gauge_t total;
+       gauge_t free;
+       gauge_t reserved;
+
+       memset (&pmemory, 0, sizeof (pmemory));
+        status = perfstat_memory_total (NULL, &pmemory, sizeof(perfstat_memory_total_t), 1);
+       if (status < 0)
        {
                 char errbuf[1024];
-                WARNING ("memory plugin: perfstat_memory_total failed: %s",
+                WARNING ("swap plugin: perfstat_memory_total failed: %s",
                         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);
+       total    = (gauge_t) (pmemory.pgsp_total * pagesize);
+       free     = (gauge_t) (pmemory.pgsp_free * pagesize);
+       reserved = (gauge_t) (pmemory.pgsp_rsvd * pagesize);
+
+       swap_submit_usage (NULL, total - free, free, "reserved", reserved);
+       swap_submit_derive ("in",  (derive_t) pmemory.pgspins * pagesize);
+       swap_submit_derive ("out", (derive_t) pmemory.pgspouts * pagesize);
 
        return (0);
 } /* }}} int swap_read */
@@ -801,8 +793,7 @@ static int swap_read (void) /* {{{ */
 
 void module_register (void)
 {
-       plugin_register_config ("swap", swap_config,
-                       config_keys, config_keys_num);
+       plugin_register_complex_config ("swap", swap_config);
        plugin_register_init ("swap", swap_init);
        plugin_register_read ("swap", swap_read);
 } /* void module_register */
index 4f5d0c4..834ba79 100644 (file)
@@ -48,7 +48,11 @@ static int sl_config (const char *key, const char *value)
        {
                log_level = parse_log_severity (value);
                if (log_level < 0)
+               {
+                       log_level = LOG_INFO;
+                       ERROR ("syslog: invalid loglevel [%s] defaulting to 'info'", value);
                        return (1);
+               }
        }
        else if (strcasecmp (key, "NotifyLevel") == 0)
        {
index 25cbcd4..20f2a9b 100644 (file)
@@ -28,6 +28,7 @@
  *  <Plugin tail>
  *    <File "/var/log/exim4/mainlog">
  *     Instance "exim"
+ *      Interval 60
  *     <Match>
  *       Regex "S=([1-9][0-9]*)"
  *       ExcludeRegex "U=root.*S="
@@ -46,27 +47,13 @@ struct ctail_config_match_s
   int flags;
   char *type;
   char *type_instance;
+  cdtime_t interval;
 };
 typedef struct ctail_config_match_s ctail_config_match_t;
 
 cu_tail_match_t **tail_match_list = NULL;
 size_t tail_match_list_num = 0;
-
-static int ctail_config_add_string (const char *name, char **dest, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("tail plugin: `%s' needs exactly one string argument.", name);
-    return (-1);
-  }
-
-  sfree (*dest);
-  *dest = strdup (ci->values[0].value.string);
-  if (*dest == NULL)
-    return (-1);
-
-  return (0);
-} /* int ctail_config_add_string */
+cdtime_t tail_match_list_intervals[255];
 
 static int ctail_config_add_match_dstype (ctail_config_match_t *cm,
     oconfig_item_t *ci)
@@ -139,7 +126,7 @@ static int ctail_config_add_match_dstype (ctail_config_match_t *cm,
 } /* int ctail_config_add_match_dstype */
 
 static int ctail_config_add_match (cu_tail_match_t *tm,
-    const char *plugin_instance, oconfig_item_t *ci)
+    const char *plugin_instance, oconfig_item_t *ci, cdtime_t interval)
 {
   ctail_config_match_t cm;
   int status;
@@ -158,16 +145,15 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
     oconfig_item_t *option = ci->children + i;
 
     if (strcasecmp ("Regex", option->key) == 0)
-      status = ctail_config_add_string ("Regex", &cm.regex, option);
+      status = cf_util_get_string (option, &cm.regex);
     else if (strcasecmp ("ExcludeRegex", option->key) == 0)
-      status = ctail_config_add_string ("ExcludeRegex", &cm.excluderegex,
-                                       option);
+      status = cf_util_get_string (option, &cm.excluderegex);
     else if (strcasecmp ("DSType", option->key) == 0)
       status = ctail_config_add_match_dstype (&cm, option);
     else if (strcasecmp ("Type", option->key) == 0)
-      status = ctail_config_add_string ("Type", &cm.type, option);
+      status = cf_util_get_string (option, &cm.type);
     else if (strcasecmp ("Instance", option->key) == 0)
-      status = ctail_config_add_string ("Instance", &cm.type_instance, option);
+      status = cf_util_get_string (option, &cm.type_instance);
     else
     {
       WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
@@ -207,7 +193,7 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
   if (status == 0)
   {
     status = tail_match_add_match_simple (tm, cm.regex, cm.excluderegex,
-       cm.flags, "tail", plugin_instance, cm.type, cm.type_instance);
+       cm.flags, "tail", plugin_instance, cm.type, cm.type_instance, interval);
 
     if (status != 0)
     {
@@ -226,6 +212,7 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
 static int ctail_config_add_file (oconfig_item_t *ci)
 {
   cu_tail_match_t *tm;
+  cdtime_t interval = 0;
   char *plugin_instance = NULL;
   int num_matches = 0;
   int status;
@@ -250,19 +237,20 @@ static int ctail_config_add_file (oconfig_item_t *ci)
   {
     oconfig_item_t *option = ci->children + i;
 
-    if (strcasecmp ("Match", option->key) == 0)
+    if (strcasecmp ("Instance", option->key) == 0)
+      status = cf_util_get_string (option, &plugin_instance);
+    else if (strcasecmp ("Interval", option->key) == 0)
+      cf_util_get_cdtime (option, &interval);
+    else if (strcasecmp ("Match", option->key) == 0)
     {
-      status = ctail_config_add_match (tm, plugin_instance, option);
+      status = ctail_config_add_match (tm, plugin_instance, option, interval);
       if (status == 0)
        num_matches++;
       /* Be mild with failed matches.. */
       status = 0;
     }
-    else if (strcasecmp ("Instance", option->key) == 0)
-      status = ctail_config_add_string ("Instance", &plugin_instance, option);
     else
     {
-      WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
       status = -1;
     }
 
@@ -292,6 +280,7 @@ static int ctail_config_add_file (oconfig_item_t *ci)
 
     tail_match_list = temp;
     tail_match_list[tail_match_list_num] = tm;
+    tail_match_list_intervals[tail_match_list_num] = interval;
     tail_match_list_num++;
   }
 
@@ -317,41 +306,43 @@ static int ctail_config (oconfig_item_t *ci)
   return (0);
 } /* int ctail_config */
 
-static int ctail_init (void)
+static int ctail_read (user_data_t *ud)
 {
-  if (tail_match_list_num == 0)
+  int status;
+
+  status = tail_match_read ((cu_tail_match_t *)ud->data);
+  if (status != 0)
   {
-    WARNING ("tail plugin: File list is empty. Returning an error.");
+    ERROR ("tail plugin: tail_match_read failed.");
     return (-1);
   }
 
   return (0);
-} /* int ctail_init */
+} /* int ctail_read */
 
-static int ctail_read (void)
+static int ctail_init (void)
 {
-  int success = 0;
+  struct timespec cb_interval;
+  char str[255];
+  user_data_t ud;
   size_t i;
 
-  for (i = 0; i < tail_match_list_num; i++)
+  if (tail_match_list_num == 0)
   {
-    int status;
+    WARNING ("tail plugin: File list is empty. Returning an error.");
+    return (-1);
+  }
 
-    status = tail_match_read (tail_match_list[i]);
-    if (status != 0)
-    {
-      ERROR ("tail plugin: tail_match_read[%zu] failed.", i);
-    }
-    else
-    {
-      success++;
-    }
+  for (i = 0; i < tail_match_list_num; i++)
+  {
+    ud.data = (void *)tail_match_list[i];
+    ssnprintf(str, sizeof(str), "tail-%zu", i);
+    CDTIME_T_TO_TIMESPEC (tail_match_list_intervals[i], &cb_interval);
+    plugin_register_complex_read (NULL, str, ctail_read, &cb_interval, &ud);
   }
 
-  if (success == 0)
-    return (-1);
   return (0);
-} /* int ctail_read */
+} /* int ctail_init */
 
 static int ctail_shutdown (void)
 {
@@ -372,7 +363,6 @@ void module_register (void)
 {
   plugin_register_complex_config ("tail", ctail_config);
   plugin_register_init ("tail", ctail_init);
-  plugin_register_read ("tail", ctail_read);
   plugin_register_shutdown ("tail", ctail_shutdown);
 } /* void module_register */
 
diff --git a/src/tail_csv.c b/src/tail_csv.c
new file mode 100644 (file)
index 0000000..a70b665
--- /dev/null
@@ -0,0 +1,565 @@
+/**
+ * collectd - src/tail_csv.c
+ * Copyright (C) 2013 Kris Nielander
+ * Copyright (C) 2013 Florian Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Kris Nielander <nielander at fox-it.com>
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+#include "common.h" /* auxiliary functions */
+#include "utils_tail.h"
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct metric_definition_s {
+    char *name;
+    char *type;
+    char *instance;
+    int data_source_type;
+    int value_from;
+    struct metric_definition_s *next;
+};
+typedef struct metric_definition_s metric_definition_t;
+
+struct instance_definition_s {
+    char *instance;
+    char *path;
+    cu_tail_t *tail;
+    metric_definition_t **metric_list;
+    size_t metric_list_len;
+    cdtime_t interval;
+    int time_from;
+    struct instance_definition_s *next;
+};
+typedef struct instance_definition_s instance_definition_t;
+
+/* Private */
+static metric_definition_t *metric_head = NULL;
+
+static int tcsv_submit (instance_definition_t *id,
+        metric_definition_t *md,
+        value_t v, cdtime_t t)
+{
+    /* Registration variables */
+    value_list_t vl = VALUE_LIST_INIT;
+
+    /* Register */
+    vl.values_len = 1;
+    vl.values = &v;
+
+    sstrncpy(vl.host, hostname_g, sizeof (vl.host));
+    sstrncpy(vl.plugin, "tail_csv", sizeof(vl.plugin));
+    if (id->instance != NULL)
+        sstrncpy(vl.plugin_instance, id->instance, sizeof(vl.plugin_instance));
+    sstrncpy(vl.type, md->type, sizeof(vl.type));
+    if (md->instance != NULL)
+        sstrncpy(vl.type_instance, md->instance, sizeof(vl.type_instance));
+
+    vl.time = t;
+    vl.interval = id->interval;
+
+    return (plugin_dispatch_values(&vl));
+}
+
+static cdtime_t parse_time (char const *tbuf)
+{
+    double t;
+    char *endptr = 0;
+
+    errno = 0;
+    t = strtod (tbuf, &endptr);
+    if ((errno != 0) || (endptr == NULL) || (endptr[0] != 0))
+        return (cdtime ());
+
+    return (DOUBLE_TO_CDTIME_T (t));
+}
+
+static int tcsv_read_metric (instance_definition_t *id,
+        metric_definition_t *md,
+        char **fields, size_t fields_num)
+{
+    value_t v;
+    cdtime_t t;
+    int status;
+
+    if (md->data_source_type == -1)
+        return (EINVAL);
+
+    if ((md->value_from >= fields_num) || (id->time_from >= fields_num))
+        return (EINVAL);
+
+    t = 0;
+    if (id->time_from >= 0)
+        t = parse_time (fields[id->time_from]);
+
+    status = parse_value (fields[md->value_from], &v, md->data_source_type);
+    if (status != 0)
+        return (status);
+
+    return (tcsv_submit (id, md, v, t));
+}
+
+static _Bool tcsv_check_index (int index, size_t fields_num, char const *name)
+{
+    if (index < 0)
+        return 1;
+    else if (((size_t) index) < fields_num)
+        return 1;
+
+    ERROR ("tail_csv plugin: Metric \"%s\": Request for index %i when "
+            "only %zu fields are available.",
+            name, index, fields_num);
+    return (0);
+}
+
+static int tcsv_read_buffer (instance_definition_t *id,
+        char *buffer, size_t buffer_size)
+{
+    char **metrics;
+    size_t metrics_num;
+
+    char *ptr;
+    size_t i;
+
+    /* Remove newlines at the end of line. */
+    while (buffer_size > 0) {
+        if ((buffer[buffer_size - 1] == '\n')
+                || (buffer[buffer_size - 1] == '\r')) {
+            buffer[buffer_size - 1] = 0;
+            buffer_size--;
+        } else {
+            break;
+        }
+    }
+
+    /* Ignore empty lines. */
+    if ((buffer_size == 0) || (buffer[0] == '#'))
+        return (0);
+
+    /* Count the number of fields. */
+    metrics_num = 1;
+    for (i = 0; i < buffer_size; i++) {
+        if (buffer[i] == ',')
+            metrics_num++;
+    }
+
+    if (metrics_num == 1) {
+        ERROR("tail_csv plugin: last line of `%s' does not contain "
+                "enough values.", id->path);
+        return (-1);
+    }
+
+    /* Create a list of all values */
+    metrics = calloc (metrics_num, sizeof (*metrics));
+    if (metrics == NULL) {
+        ERROR ("tail_csv plugin: calloc failed.");
+        return (ENOMEM);
+    }
+
+    ptr = buffer;
+    metrics[0] = ptr;
+    i = 1;
+    for (ptr = buffer; *ptr != 0; ptr++) {
+        if (*ptr != ',')
+            continue;
+
+        *ptr = 0;
+        metrics[i] = ptr + 1;
+        i++;
+    }
+    assert (i == metrics_num);
+
+    /* Register values */
+    for (i = 0; i < id->metric_list_len; ++i){
+        metric_definition_t *md = id->metric_list[i];
+
+        if (!tcsv_check_index (md->value_from, metrics_num, md->name)
+                || !tcsv_check_index (id->time_from, metrics_num, md->name))
+            continue;
+
+        tcsv_read_metric (id, md, metrics, metrics_num);
+    }
+
+    /* Free up resources */
+    sfree (metrics);
+    return (0);
+}
+
+static int tcsv_read (user_data_t *ud) {
+    instance_definition_t *id;
+    id = ud->data;
+
+    if (id->tail == NULL)
+    {
+        id->tail = cu_tail_create (id->path);
+        if (id->tail == NULL)
+        {
+            ERROR ("tail_csv plugin: cu_tail_create (\"%s\") failed.",
+                    id->path);
+            return (-1);
+        }
+    }
+
+    while (42)
+    {
+        char buffer[1024];
+        size_t buffer_len;
+        int status;
+
+        status = cu_tail_readline (id->tail, buffer, (int) sizeof (buffer));
+        if (status != 0)
+        {
+            ERROR ("tail_csv plugin: File \"%s\": cu_tail_readline failed "
+                    "with status %i.", id->path, status);
+            return (-1);
+        }
+
+        buffer_len = strlen (buffer);
+        if (buffer_len == 0)
+            break;
+
+        tcsv_read_buffer (id, buffer, buffer_len);
+    }
+
+    return (0);
+}
+
+static void tcsv_metric_definition_destroy(void *arg){
+    metric_definition_t *md;
+    metric_definition_t *next;
+
+    md = arg;
+    if (md == NULL)
+        return;
+
+    next = md->next;
+    md->next = NULL;
+
+    sfree(md->name);
+    sfree(md->type);
+    sfree(md->instance);
+    sfree(md);
+
+    tcsv_metric_definition_destroy (next);
+}
+
+static int tcsv_config_get_index(oconfig_item_t *ci, int *ret_index) {
+    int index;
+
+    if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)){
+        WARNING("tail_csv plugin: The \"%s\" config option needs exactly one "
+                "integer argument.", ci->key);
+        return (-1);
+    }
+
+    index = (int) ci->values[0].value.number;
+    if (index < 0) {
+        WARNING("tail_csv plugin: The \"%s\" config option must be positive "
+                "(or zero).", ci->key);
+        return (-1);
+    }
+
+    *ret_index = index;
+    return (0);
+}
+
+/* Parse metric  */
+static int tcsv_config_add_metric(oconfig_item_t *ci){
+    metric_definition_t *md;
+    int status = 0;
+    int i;
+
+    md = (metric_definition_t *)malloc(sizeof(*md));
+    if (md == NULL)
+        return (-1);
+    memset(md, 0, sizeof(*md));
+    md->name = NULL;
+    md->type = NULL;
+    md->instance = NULL;
+    md->data_source_type = -1;
+    md->value_from = -1;
+    md->next = NULL;
+
+    status = cf_util_get_string (ci, &md->name);
+    if (status != 0) {
+        sfree (md);
+        return (-1);
+    }
+
+    for (i = 0; i < ci->children_num; ++i){
+        oconfig_item_t *option = ci->children + i;
+        status = 0;
+
+        if (strcasecmp("Type", option->key) == 0)
+            status = cf_util_get_string(option, &md->type);
+        else if (strcasecmp("Instance", option->key) == 0)
+            status = cf_util_get_string(option, &md->instance);
+        else if (strcasecmp("ValueFrom", option->key) == 0)
+            status = tcsv_config_get_index (option, &md->value_from);
+        else {
+            WARNING("tail_csv plugin: Option `%s' not allowed here.", option->key);
+            status = -1;
+        }
+
+        if (status != 0)
+            break;
+    }
+
+    if (status != 0){
+        tcsv_metric_definition_destroy(md);
+        return (-1);
+    }
+
+    /* Verify all necessary options have been set. */
+    if (md->type == NULL) {
+        WARNING("tail_csv plugin: Option `Type' must be set.");
+        status = -1;
+    } else if (md->value_from < 0) {
+        WARNING("tail_csv plugin: Option `ValueFrom' must be set.");
+        status = -1;
+    }
+    if (status != 0) {
+        tcsv_metric_definition_destroy(md);
+        return (status);
+    }
+
+    if (metric_head == NULL)
+        metric_head = md;
+    else {
+        metric_definition_t *last;
+        last = metric_head;
+        while (last->next != NULL)
+            last = last->next;
+        last->next = md;
+    }
+
+    return (0);
+}
+
+static void tcsv_instance_definition_destroy(void *arg){
+    instance_definition_t *id;
+
+    id = arg;
+    if (id == NULL)
+        return;
+
+    if (id->tail != NULL)
+        cu_tail_destroy (id->tail);
+    id->tail = NULL;
+
+    sfree(id->instance);
+    sfree(id->path);
+    sfree(id->metric_list);
+    sfree(id);
+}
+
+static int tcsv_config_add_instance_collect(instance_definition_t *id, oconfig_item_t *ci){
+    metric_definition_t *metric;
+    int i;
+
+    if (ci->values_num < 1){
+        WARNING("tail_csv plugin: The `Collect' config option needs at least one argument.");
+        return (-1);
+    }
+
+    /* Verify string arguments */
+    for (i = 0; i < ci->values_num; ++i)
+        if (ci->values[i].type != OCONFIG_TYPE_STRING){
+            WARNING("tail_csv plugin: All arguments to `Collect' must be strings.");
+            return (-1);
+        }
+
+    id->metric_list = (metric_definition_t **)malloc(sizeof(metric_definition_t *) * ci->values_num);
+    if (id->metric_list == NULL)
+        return (-1);
+
+    for (i = 0; i < ci->values_num; ++i){
+        for (metric = metric_head; metric != NULL; metric = metric->next)
+            if (strcasecmp(ci->values[i].value.string, metric->name) == 0)
+                break;
+
+        if (metric == NULL){
+            WARNING("tail_csv plugin: `Collect' argument not found `%s'.", ci->values[i].value.string);
+            return (-1);
+        }
+
+        id->metric_list[i] = metric;
+        id->metric_list_len++;
+    }
+
+    return (0);
+}
+
+/* <File /> block */
+static int tcsv_config_add_file(oconfig_item_t *ci)
+{
+    instance_definition_t* id;
+    int status = 0;
+    int i;
+
+    /* Registration variables */
+    char cb_name[DATA_MAX_NAME_LEN];
+    user_data_t cb_data;
+    struct timespec cb_interval;
+
+    id = malloc(sizeof(*id));
+    if (id == NULL)
+        return (-1);
+    memset(id, 0, sizeof(*id));
+    id->instance = NULL;
+    id->path = NULL;
+    id->metric_list = NULL;
+    id->time_from = -1;
+    id->next = NULL;
+
+    status = cf_util_get_string (ci, &id->path);
+    if (status != 0) {
+        sfree (id);
+        return (status);
+    }
+
+    /* Use default interval. */
+    id->interval = plugin_get_interval();
+
+    for (i = 0; i < ci->children_num; ++i){
+        oconfig_item_t *option = ci->children + i;
+        status = 0;
+
+        if (strcasecmp("Instance", option->key) == 0)
+            status = cf_util_get_string(option, &id->instance);
+        else if (strcasecmp("Collect", option->key) == 0)
+            status = tcsv_config_add_instance_collect(id, option);
+        else if (strcasecmp("Interval", option->key) == 0)
+            cf_util_get_cdtime(option, &id->interval);
+        else if (strcasecmp("TimeFrom", option->key) == 0)
+            status = tcsv_config_get_index (option, &id->time_from);
+        else {
+            WARNING("tail_csv plugin: Option `%s' not allowed here.", option->key);
+            status = -1;
+        }
+
+        if (status != 0)
+            break;
+    }
+
+    if (status != 0){
+        tcsv_instance_definition_destroy(id);
+        return (-1);
+    }
+
+    /* Verify all necessary options have been set. */
+    if (id->path == NULL){
+        WARNING("tail_csv plugin: Option `Path' must be set.");
+        status = -1;
+    } else if (id->metric_list == NULL){
+        WARNING("tail_csv plugin: Option `Collect' must be set.");
+        status = -1;
+   }
+
+    if (status != 0){
+        tcsv_instance_definition_destroy(id);
+        return (-1);
+    }
+
+    ssnprintf (cb_name, sizeof (cb_name), "tail_csv/%s", id->path);
+    memset(&cb_data, 0, sizeof(cb_data));
+    cb_data.data = id;
+    cb_data.free_func = tcsv_instance_definition_destroy;
+    CDTIME_T_TO_TIMESPEC(id->interval, &cb_interval);
+    status = plugin_register_complex_read(NULL, cb_name, tcsv_read, &cb_interval, &cb_data);
+
+    if (status != 0){
+        ERROR("tail_csv plugin: Registering complex read function failed.");
+        tcsv_instance_definition_destroy(id);
+        return (-1);
+    }
+
+    return (0);
+}
+
+/* Parse blocks */
+static int tcsv_config(oconfig_item_t *ci){
+    int i;
+    for (i = 0; i < ci->children_num; ++i){
+        oconfig_item_t *child = ci->children + i;
+        if (strcasecmp("Metric", child->key) == 0)
+            tcsv_config_add_metric(child);
+        else if (strcasecmp("File", child->key) == 0)
+            tcsv_config_add_file(child);
+        else
+            WARNING("tail_csv plugin: Ignore unknown config option `%s'.", child->key);
+    }
+
+    return (0);
+} /* int tcsv_config */
+
+static int tcsv_init(void) { /* {{{ */
+    static _Bool have_init = 0;
+    metric_definition_t *md;
+
+    if (have_init)
+        return (0);
+
+    for (md = metric_head; md != NULL; md = md->next) {
+        data_set_t const *ds;
+
+        /* Retrieve the data source type from the types db. */
+        ds = plugin_get_ds(md->type);
+        if (ds == NULL)
+        {
+            ERROR ("tail_csv plugin: Failed to look up type \"%s\" for "
+                    "metric \"%s\". It may not be defined in the types.db "
+                    "file. Please read the types.db(5) manual page for more "
+                    "details.",
+                    md->type, md->name);
+            continue;
+        }
+        else if (ds->ds_num != 1)
+        {
+            ERROR ("tail_csv plugin: The type \"%s\" has %i data sources. "
+                    "Only types with a single data soure are supported.",
+                    ds->type, ds->ds_num);
+            continue;
+        }
+
+        md->data_source_type = ds->ds->type;
+    }
+
+    return (0);
+} /* }}} int tcsv_init */
+
+static int tcsv_shutdown (void) {
+    tcsv_metric_definition_destroy (metric_head);
+    metric_head = NULL;
+
+    return (0);
+}
+
+void module_register(void){
+    plugin_register_complex_config("tail_csv", tcsv_config);
+    plugin_register_init("tail_csv", tcsv_init);
+    plugin_register_shutdown("tail_csv", tcsv_shutdown);
+}
+
+/* vim: set sw=4 sts=4 et : */
index 9a9affb..1d7af5c 100644 (file)
@@ -296,8 +296,8 @@ static int tr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
         /* && (data->type == NULL) */
         && (data->type_instance == NULL))
     {
-      ERROR ("Target `replace': You need to set at lease one of `Host', "
-          "`Plugin', `PluginInstance', `Type', or `TypeInstance'.");
+      ERROR ("Target `replace': You need to set at least one of `Host', "
+          "`Plugin', `PluginInstance' or `TypeInstance'.");
       status = -1;
     }
 
index 8a014c3..2fb9cee 100644 (file)
@@ -171,8 +171,8 @@ static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
         /* && (data->type == NULL) */
         && (data->type_instance == NULL))
     {
-      ERROR ("Target `set': You need to set at lease one of `Host', "
-          "`Plugin', `PluginInstance', `Type', or `TypeInstance'.");
+      ERROR ("Target `set': You need to set at least one of `Host', "
+          "`Plugin', `PluginInstance' or `TypeInstance'.");
       status = -1;
     }
 
index 765b892..890e07f 100644 (file)
@@ -215,13 +215,13 @@ static const char *tcp_state[] =
   "CLOSED",
   "LISTEN",
   "SYN_SENT",
-  "SYN_RCVD",
+  "SYN_RECV",
   "ESTABLISHED",
   "CLOSE_WAIT",
-  "FIN_WAIT_1",
+  "FIN_WAIT1",
   "CLOSING",
   "LAST_ACK",
-  "FIN_WAIT_2",
+  "FIN_WAIT2",
   "TIME_WAIT"
 };
 
index 2552ad3..138b4df 100644 (file)
@@ -439,7 +439,7 @@ static int tss2_vserver_gapl (FILE *read_fh, FILE *write_fh,
                status = tss2_receive_line (read_fh, buffer, sizeof (buffer));
                if (status != 0)
                {
-                       /* Set to NULL just to make sure noone uses these FHs anymore. */
+                       /* Set to NULL just to make sure no one uses these FHs anymore. */
                        read_fh = NULL;
                        write_fh = NULL;
                        ERROR ("teamspeak2 plugin: tss2_receive_line failed.");
@@ -564,7 +564,7 @@ static int tss2_read_vserver (vserver_list_t *vserver)
                status = tss2_receive_line (read_fh, buffer, sizeof (buffer));
                if (status != 0)
                {
-                       /* Set to NULL just to make sure noone uses these FHs anymore. */
+                       /* Set to NULL just to make sure no one uses these FHs anymore. */
                        read_fh = NULL;
                        write_fh = NULL;
                        ERROR ("teamspeak2 plugin: tss2_receive_line failed.");
diff --git a/src/tests/common_test.c b/src/tests/common_test.c
new file mode 100644 (file)
index 0000000..eabba11
--- /dev/null
@@ -0,0 +1,247 @@
+/**
+ * collectd - src/common_test.c
+ *
+ * Copyright (C) 2013       Florian octo 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 octo Forster <octo at collectd.org>
+ */
+
+#include "tests/macros.h"
+#include "common.h"
+
+DEF_TEST(sstrncpy)
+{
+  char buffer[16] = "";
+  char *ptr = &buffer[4];
+  char *ret;
+
+  buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff;
+  buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff;
+
+  ret = sstrncpy (ptr, "foobar", 8);
+  OK(ret == ptr);
+  STREQ ("foobar", ptr);
+  OK(buffer[3] == buffer[12]);
+
+  ret = sstrncpy (ptr, "abc", 8);
+  OK(ret == ptr);
+  STREQ ("abc", ptr);
+  OK(buffer[3] == buffer[12]);
+
+  ret = sstrncpy (ptr, "collectd", 8);
+  OK(ret == ptr);
+  OK(ptr[7] == 0);
+  STREQ ("collect", ptr);
+  OK(buffer[3] == buffer[12]);
+
+  return (0);
+}
+
+DEF_TEST(ssnprintf)
+{
+  char buffer[16] = "";
+  char *ptr = &buffer[4];
+  int status;
+
+  buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff;
+  buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff;
+
+  status = ssnprintf (ptr, 8, "%i", 1337);
+  OK(status == 4);
+  STREQ ("1337", ptr);
+
+  status = ssnprintf (ptr, 8, "%s", "collectd");
+  OK(status == 8);
+  OK(ptr[7] == 0);
+  STREQ ("collect", ptr);
+  OK(buffer[3] == buffer[12]);
+
+  return (0);
+}
+
+DEF_TEST(sstrdup)
+{
+  char *ptr;
+
+  ptr = sstrdup ("collectd");
+  OK(ptr != NULL);
+  STREQ ("collectd", ptr);
+
+  sfree(ptr);
+  OK(ptr == NULL);
+
+  ptr = sstrdup (NULL);
+  OK(ptr == NULL);
+
+  return (0);
+}
+
+DEF_TEST(strsplit)
+{
+  char buffer[32];
+  char *fields[8];
+  int status;
+
+  strncpy (buffer, "foo bar", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 2);
+  STREQ ("foo", fields[0]);
+  STREQ ("bar", fields[1]);
+
+  strncpy (buffer, "foo \t bar", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 2);
+  STREQ ("foo", fields[0]);
+  STREQ ("bar", fields[1]);
+
+  strncpy (buffer, "one two\tthree\rfour\nfive", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 5);
+  STREQ ("one", fields[0]);
+  STREQ ("two", fields[1]);
+  STREQ ("three", fields[2]);
+  STREQ ("four", fields[3]);
+  STREQ ("five", fields[4]);
+
+  strncpy (buffer, "\twith trailing\n", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 2);
+  STREQ ("with", fields[0]);
+  STREQ ("trailing", fields[1]);
+
+  strncpy (buffer, "1 2 3 4 5 6 7 8 9 10 11 12 13", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 8);
+  STREQ ("7", fields[6]);
+  STREQ ("8", fields[7]);
+
+  strncpy (buffer, "single", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 1);
+  STREQ ("single", fields[0]);
+
+  strncpy (buffer, "", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 0);
+
+  return (0);
+}
+
+DEF_TEST(strjoin)
+{
+  char buffer[16];
+  char *fields[4];
+  int status;
+
+  fields[0] = "foo";
+  fields[1] = "bar";
+  fields[2] = "baz";
+  fields[3] = "qux";
+
+  status = strjoin (buffer, sizeof (buffer), fields, 2, "!");
+  OK(status == 7);
+  STREQ ("foo!bar", buffer);
+
+  status = strjoin (buffer, sizeof (buffer), fields, 1, "!");
+  OK(status == 3);
+  STREQ ("foo", buffer);
+
+  status = strjoin (buffer, sizeof (buffer), fields, 0, "!");
+  OK(status < 0);
+
+  status = strjoin (buffer, sizeof (buffer), fields, 2, "rcht");
+  OK(status == 10);
+  STREQ ("foorchtbar", buffer);
+
+  status = strjoin (buffer, sizeof (buffer), fields, 4, "");
+  OK(status == 12);
+  STREQ ("foobarbazqux", buffer);
+
+  status = strjoin (buffer, sizeof (buffer), fields, 4, "!");
+  OK(status == 15);
+  STREQ ("foo!bar!baz!qux", buffer);
+
+  fields[0] = "0123";
+  fields[1] = "4567";
+  fields[2] = "8901";
+  fields[3] = "2345";
+  status = strjoin (buffer, sizeof (buffer), fields, 4, "-");
+  OK(status < 0);
+
+  return (0);
+}
+
+DEF_TEST(strunescape)
+{
+  char buffer[16];
+  int status;
+
+  strncpy (buffer, "foo\\tbar", sizeof (buffer));
+  status = strunescape (buffer, sizeof (buffer));
+  OK(status == 0);
+  STREQ ("foo\tbar", buffer);
+
+  strncpy (buffer, "\\tfoo\\r\\n", sizeof (buffer));
+  status = strunescape (buffer, sizeof (buffer));
+  OK(status == 0);
+  STREQ ("\tfoo\r\n", buffer);
+
+  strncpy (buffer, "With \\\"quotes\\\"", sizeof (buffer));
+  status = strunescape (buffer, sizeof (buffer));
+  OK(status == 0);
+  STREQ ("With \"quotes\"", buffer);
+
+  /* Backslash before null byte */
+  strncpy (buffer, "\\tbackslash end\\", sizeof (buffer));
+  status = strunescape (buffer, sizeof (buffer));
+  OK(status != 0);
+  STREQ ("\tbackslash end", buffer);
+  return (0);
+
+  /* Backslash at buffer end */
+  strncpy (buffer, "\\t3\\56", sizeof (buffer));
+  status = strunescape (buffer, 4);
+  OK(status != 0);
+  OK(buffer[0] == '\t');
+  OK(buffer[1] == '3');
+  OK(buffer[2] == 0);
+  OK(buffer[3] == 0);
+  OK(buffer[4] == '5');
+  OK(buffer[5] == '6');
+  OK(buffer[6] == '7');
+
+  return (0);
+}
+
+int main (void)
+{
+  RUN_TEST(sstrncpy);
+  RUN_TEST(ssnprintf);
+  RUN_TEST(sstrdup);
+  RUN_TEST(strsplit);
+  RUN_TEST(strjoin);
+  RUN_TEST(strunescape);
+
+  END_TEST;
+}
+
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/tests/macros.h b/src/tests/macros.h
new file mode 100644 (file)
index 0000000..e454b69
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * collectd - src/tests/macros.h
+ *
+ * Copyright (C) 2013       Florian octo 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 octo Forster <octo at collectd.org>
+ */
+
+static int fail_count__ = 0;
+static int check_count__ = 0;
+
+#define DEF_TEST(func) static int test_##func ()
+
+#define RUN_TEST(func) do { \
+  int status; \
+  printf ("Testing %s ...\n", #func); \
+  status = test_ ## func (); \
+  printf ("%s.\n", (status == 0) ? "Success" : "FAILURE"); \
+  if (status != 0) { fail_count__++; } \
+} while (0)
+
+#define END_TEST exit ((fail_count__ == 0) ? 0 : 1);
+
+#define OK1(cond, text) do { \
+  _Bool result = (cond); \
+  printf ("%s %i - %s\n", result ? "ok" : "not ok", ++check_count__, text); \
+} while (0)
+#define OK(cond) OK1(cond, #cond)
+
+#define STREQ(expect, actual) do { \
+  if (strcmp (expect, actual) != 0) { \
+    printf ("not ok %i - %s incorrect: expected \"%s\", got \"%s\"\n", \
+        ++check_count__, #actual, expect, actual); \
+    return (-1); \
+  } \
+  printf ("ok %i - %s evaluates to \"%s\"\n", ++check_count__, #actual, expect); \
+} while (0)
+
+#define CHECK_NOT_NULL(expr) do { \
+  void *ptr_; \
+  ptr_ = (expr); \
+  OK1(ptr_ != NULL, #expr); \
+} while (0)
+
+#define CHECK_ZERO(expr) do { \
+  long status_; \
+  status_ = (long) (expr); \
+  OK1(status_ == 0L, #expr); \
+} while (0)
diff --git a/src/tests/mock/plugin.c b/src/tests/mock/plugin.c
new file mode 100644 (file)
index 0000000..cb3c65a
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * collectd - src/mock/plugin.c
+ *
+ * Copyright (C) 2013       Florian octo 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 octo Forster <octo at collectd.org>
+ */
+
+#include "plugin.h"
+
+void plugin_log (int level, char const *format, ...)
+{
+  char buffer[1024];
+  va_list ap;
+
+  va_start (ap, format);
+  vsnprintf (buffer, sizeof (buffer), format, ap);
+  va_end (ap);
+
+  printf ("plugin_log (%i, \"%s\");\n", level, buffer);
+}
+
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/tests/mock/utils_cache.c b/src/tests/mock/utils_cache.c
new file mode 100644 (file)
index 0000000..8c8b55b
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+ * collectd - src/mock/utils_cache.c
+ *
+ * Copyright (C) 2013       Florian octo 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 octo Forster <octo at collectd.org>
+ */
+
+#include "utils_cache.h"
+
+gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl)
+{
+  return (NULL);
+}
diff --git a/src/tests/mock/utils_time.c b/src/tests/mock/utils_time.c
new file mode 100644 (file)
index 0000000..38b59ca
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * collectd - src/mock/utils_time.c
+ *
+ * Copyright (C) 2013       Florian octo 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 octo Forster <octo at collectd.org>
+ */
+
+#include "utils_time.h"
+
+cdtime_t cdtime (void)
+{
+  return (0);
+}
+
diff --git a/src/tests/test_common.c b/src/tests/test_common.c
new file mode 100644 (file)
index 0000000..eabba11
--- /dev/null
@@ -0,0 +1,247 @@
+/**
+ * collectd - src/common_test.c
+ *
+ * Copyright (C) 2013       Florian octo 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 octo Forster <octo at collectd.org>
+ */
+
+#include "tests/macros.h"
+#include "common.h"
+
+DEF_TEST(sstrncpy)
+{
+  char buffer[16] = "";
+  char *ptr = &buffer[4];
+  char *ret;
+
+  buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff;
+  buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff;
+
+  ret = sstrncpy (ptr, "foobar", 8);
+  OK(ret == ptr);
+  STREQ ("foobar", ptr);
+  OK(buffer[3] == buffer[12]);
+
+  ret = sstrncpy (ptr, "abc", 8);
+  OK(ret == ptr);
+  STREQ ("abc", ptr);
+  OK(buffer[3] == buffer[12]);
+
+  ret = sstrncpy (ptr, "collectd", 8);
+  OK(ret == ptr);
+  OK(ptr[7] == 0);
+  STREQ ("collect", ptr);
+  OK(buffer[3] == buffer[12]);
+
+  return (0);
+}
+
+DEF_TEST(ssnprintf)
+{
+  char buffer[16] = "";
+  char *ptr = &buffer[4];
+  int status;
+
+  buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff;
+  buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff;
+
+  status = ssnprintf (ptr, 8, "%i", 1337);
+  OK(status == 4);
+  STREQ ("1337", ptr);
+
+  status = ssnprintf (ptr, 8, "%s", "collectd");
+  OK(status == 8);
+  OK(ptr[7] == 0);
+  STREQ ("collect", ptr);
+  OK(buffer[3] == buffer[12]);
+
+  return (0);
+}
+
+DEF_TEST(sstrdup)
+{
+  char *ptr;
+
+  ptr = sstrdup ("collectd");
+  OK(ptr != NULL);
+  STREQ ("collectd", ptr);
+
+  sfree(ptr);
+  OK(ptr == NULL);
+
+  ptr = sstrdup (NULL);
+  OK(ptr == NULL);
+
+  return (0);
+}
+
+DEF_TEST(strsplit)
+{
+  char buffer[32];
+  char *fields[8];
+  int status;
+
+  strncpy (buffer, "foo bar", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 2);
+  STREQ ("foo", fields[0]);
+  STREQ ("bar", fields[1]);
+
+  strncpy (buffer, "foo \t bar", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 2);
+  STREQ ("foo", fields[0]);
+  STREQ ("bar", fields[1]);
+
+  strncpy (buffer, "one two\tthree\rfour\nfive", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 5);
+  STREQ ("one", fields[0]);
+  STREQ ("two", fields[1]);
+  STREQ ("three", fields[2]);
+  STREQ ("four", fields[3]);
+  STREQ ("five", fields[4]);
+
+  strncpy (buffer, "\twith trailing\n", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 2);
+  STREQ ("with", fields[0]);
+  STREQ ("trailing", fields[1]);
+
+  strncpy (buffer, "1 2 3 4 5 6 7 8 9 10 11 12 13", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 8);
+  STREQ ("7", fields[6]);
+  STREQ ("8", fields[7]);
+
+  strncpy (buffer, "single", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 1);
+  STREQ ("single", fields[0]);
+
+  strncpy (buffer, "", sizeof (buffer));
+  status = strsplit (buffer, fields, 8);
+  OK(status == 0);
+
+  return (0);
+}
+
+DEF_TEST(strjoin)
+{
+  char buffer[16];
+  char *fields[4];
+  int status;
+
+  fields[0] = "foo";
+  fields[1] = "bar";
+  fields[2] = "baz";
+  fields[3] = "qux";
+
+  status = strjoin (buffer, sizeof (buffer), fields, 2, "!");
+  OK(status == 7);
+  STREQ ("foo!bar", buffer);
+
+  status = strjoin (buffer, sizeof (buffer), fields, 1, "!");
+  OK(status == 3);
+  STREQ ("foo", buffer);
+
+  status = strjoin (buffer, sizeof (buffer), fields, 0, "!");
+  OK(status < 0);
+
+  status = strjoin (buffer, sizeof (buffer), fields, 2, "rcht");
+  OK(status == 10);
+  STREQ ("foorchtbar", buffer);
+
+  status = strjoin (buffer, sizeof (buffer), fields, 4, "");
+  OK(status == 12);
+  STREQ ("foobarbazqux", buffer);
+
+  status = strjoin (buffer, sizeof (buffer), fields, 4, "!");
+  OK(status == 15);
+  STREQ ("foo!bar!baz!qux", buffer);
+
+  fields[0] = "0123";
+  fields[1] = "4567";
+  fields[2] = "8901";
+  fields[3] = "2345";
+  status = strjoin (buffer, sizeof (buffer), fields, 4, "-");
+  OK(status < 0);
+
+  return (0);
+}
+
+DEF_TEST(strunescape)
+{
+  char buffer[16];
+  int status;
+
+  strncpy (buffer, "foo\\tbar", sizeof (buffer));
+  status = strunescape (buffer, sizeof (buffer));
+  OK(status == 0);
+  STREQ ("foo\tbar", buffer);
+
+  strncpy (buffer, "\\tfoo\\r\\n", sizeof (buffer));
+  status = strunescape (buffer, sizeof (buffer));
+  OK(status == 0);
+  STREQ ("\tfoo\r\n", buffer);
+
+  strncpy (buffer, "With \\\"quotes\\\"", sizeof (buffer));
+  status = strunescape (buffer, sizeof (buffer));
+  OK(status == 0);
+  STREQ ("With \"quotes\"", buffer);
+
+  /* Backslash before null byte */
+  strncpy (buffer, "\\tbackslash end\\", sizeof (buffer));
+  status = strunescape (buffer, sizeof (buffer));
+  OK(status != 0);
+  STREQ ("\tbackslash end", buffer);
+  return (0);
+
+  /* Backslash at buffer end */
+  strncpy (buffer, "\\t3\\56", sizeof (buffer));
+  status = strunescape (buffer, 4);
+  OK(status != 0);
+  OK(buffer[0] == '\t');
+  OK(buffer[1] == '3');
+  OK(buffer[2] == 0);
+  OK(buffer[3] == 0);
+  OK(buffer[4] == '5');
+  OK(buffer[5] == '6');
+  OK(buffer[6] == '7');
+
+  return (0);
+}
+
+int main (void)
+{
+  RUN_TEST(sstrncpy);
+  RUN_TEST(ssnprintf);
+  RUN_TEST(sstrdup);
+  RUN_TEST(strsplit);
+  RUN_TEST(strjoin);
+  RUN_TEST(strunescape);
+
+  END_TEST;
+}
+
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/tests/test_utils_avltree.c b/src/tests/test_utils_avltree.c
new file mode 100644 (file)
index 0000000..0e0b988
--- /dev/null
@@ -0,0 +1,83 @@
+/**
+ * collectd - src/utils_avltree_test.c
+ *
+ * Copyright (C) 2013       Florian octo 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 octo Forster <octo at collectd.org>
+ */
+
+#include "tests/macros.h"
+#include "collectd.h"
+#include "utils_avltree.h"
+
+static int compare_total_count = 0;
+#define RESET_COUNTS() do { compare_total_count = 0; } while (0)
+
+static int compare_callback (void const *v0, void const *v1)
+{
+  assert (v0 != NULL);
+  assert (v1 != NULL);
+
+  compare_total_count++;
+  return (strcmp (v0, v1));
+}
+
+DEF_TEST(success)
+{
+  c_avl_tree_t *t;
+  char key_orig[] = "foo";
+  char value_orig[] = "bar";
+  char *key_ret = NULL;
+  char *value_ret = NULL;
+
+  RESET_COUNTS ();
+  t = c_avl_create (compare_callback);
+  OK (t != NULL);
+
+  OK (c_avl_insert (t, key_orig, value_orig) == 0);
+  OK (c_avl_size (t) == 1);
+
+  /* Key already exists. */
+  OK (c_avl_insert (t, "foo", "qux") > 0);
+
+  OK (c_avl_get (t, "foo", (void *) &value_ret) == 0);
+  OK (value_ret == &value_orig[0]);
+
+  key_ret = value_ret = NULL;
+  OK (c_avl_remove (t, "foo", (void *) &key_ret, (void *) &value_ret) == 0);
+  OK (key_ret == &key_orig[0]);
+  OK (value_ret == &value_orig[0]);
+  OK (c_avl_size (t) == 0);
+
+  c_avl_destroy (t);
+
+  return (0);
+}
+
+int main (void)
+{
+  RUN_TEST(success);
+
+  END_TEST;
+}
+
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/tests/test_utils_heap.c b/src/tests/test_utils_heap.c
new file mode 100644 (file)
index 0000000..125eb8b
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * collectd - src/tests/utils_heap_test.c
+ *
+ * Copyright (C) 2013       Florian octo 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 octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+#include "tests/macros.h"
+#include "utils_heap.h"
+
+static int compare (void const *v0, void const *v1)
+{
+  int const *i0 = v0;
+  int const *i1 = v1;
+
+  if ((*i0) < (*i1))
+    return -1;
+  else if ((*i0) > (*i1))
+    return 1;
+  else
+    return 0;
+}
+
+DEF_TEST(simple)
+{
+  int values[] = { 9, 5, 6, 1, 3, 4, 0, 8, 2, 7 };
+  int i;
+  c_heap_t *h;
+
+  CHECK_NOT_NULL(h = c_heap_create (compare));
+  for (i = 0; i < 10; i++)
+    CHECK_ZERO(c_heap_insert (h, &values[i]));
+
+  for (i = 0; i < 5; i++)
+  {
+    int *ret = NULL;
+    CHECK_NOT_NULL(ret = c_heap_get_root(h));
+    OK(*ret == i);
+  }
+
+  CHECK_ZERO(c_heap_insert (h, &values[6] /* = 0 */));
+  CHECK_ZERO(c_heap_insert (h, &values[3] /* = 1 */));
+  CHECK_ZERO(c_heap_insert (h, &values[8] /* = 2 */));
+  CHECK_ZERO(c_heap_insert (h, &values[4] /* = 3 */));
+  CHECK_ZERO(c_heap_insert (h, &values[5] /* = 4 */));
+
+  for (i = 0; i < 10; i++)
+  {
+    int *ret = NULL;
+    CHECK_NOT_NULL(ret = c_heap_get_root(h));
+    OK(*ret == i);
+  }
+
+  c_heap_destroy(h);
+  return (0);
+}
+
+int main (void)
+{
+  RUN_TEST(simple);
+
+  END_TEST;
+}
+
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/tests/test_utils_mount.c b/src/tests/test_utils_mount.c
new file mode 100644 (file)
index 0000000..698a491
--- /dev/null
@@ -0,0 +1,102 @@
+/**
+ * collectd - src/utils_mount_test.c
+ *
+ * Copyright (C) 2013       Florian octo 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 octo Forster <octo at collectd.org>
+ */
+
+#include "tests/macros.h"
+#include "collectd.h"
+#include "utils_mount.h"
+
+DEF_TEST(cu_mount_checkoption)
+{
+  char line_opts[] = "foo=one,bar=two,qux=three";
+  char *foo = strstr (line_opts, "foo");
+  char *bar = strstr (line_opts, "bar");
+  char *qux = strstr (line_opts, "qux");
+
+  char line_bool[] = "one,two,three";
+  char *one = strstr (line_bool, "one");
+  char *two = strstr (line_bool, "two");
+  char *three = strstr (line_bool, "three");
+
+  /* Normal operation */
+  OK (foo == cu_mount_checkoption (line_opts, "foo", 0));
+  OK (bar == cu_mount_checkoption (line_opts, "bar", 0));
+  OK (qux == cu_mount_checkoption (line_opts, "qux", 0));
+  OK (NULL == cu_mount_checkoption (line_opts, "unknown", 0));
+
+  OK (one == cu_mount_checkoption (line_bool, "one", 0));
+  OK (two == cu_mount_checkoption (line_bool, "two", 0));
+  OK (three == cu_mount_checkoption (line_bool, "three", 0));
+  OK (NULL == cu_mount_checkoption (line_bool, "four", 0));
+
+  /* Shorter and longer parts */
+  OK (foo == cu_mount_checkoption (line_opts, "fo", 0));
+  OK (bar == cu_mount_checkoption (line_opts, "bar=", 0));
+  OK (qux == cu_mount_checkoption (line_opts, "qux=thr", 0));
+
+  OK (one == cu_mount_checkoption (line_bool, "o", 0));
+  OK (two == cu_mount_checkoption (line_bool, "tw", 0));
+  OK (three == cu_mount_checkoption (line_bool, "thr", 0));
+
+  /* "full" flag */
+  OK (one == cu_mount_checkoption (line_bool, "one", 1));
+  OK (two == cu_mount_checkoption (line_bool, "two", 1));
+  OK (three == cu_mount_checkoption (line_bool, "three", 1));
+  OK (NULL == cu_mount_checkoption (line_bool, "four", 1));
+
+  OK (NULL == cu_mount_checkoption (line_bool, "o", 1));
+  OK (NULL == cu_mount_checkoption (line_bool, "tw", 1));
+  OK (NULL == cu_mount_checkoption (line_bool, "thr", 1));
+
+  return (0);
+}
+DEF_TEST(cu_mount_getoptionvalue)
+{
+  char line_opts[] = "foo=one,bar=two,qux=three";
+  char line_bool[] = "one,two,three";
+
+  STREQ ("one", cu_mount_getoptionvalue (line_opts, "foo="));
+  STREQ ("two", cu_mount_getoptionvalue (line_opts, "bar="));
+  STREQ ("three", cu_mount_getoptionvalue (line_opts, "qux="));
+  OK (NULL == cu_mount_getoptionvalue (line_opts, "unknown="));
+
+  STREQ ("", cu_mount_getoptionvalue (line_bool, "one"));
+  STREQ ("", cu_mount_getoptionvalue (line_bool, "two"));
+  STREQ ("", cu_mount_getoptionvalue (line_bool, "three"));
+  OK (NULL == cu_mount_getoptionvalue (line_bool, "four"));
+
+  return (0);
+}
+
+int main (void)
+{
+  RUN_TEST(cu_mount_checkoption);
+  RUN_TEST(cu_mount_getoptionvalue);
+
+  END_TEST;
+}
+
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/tests/test_utils_vl_lookup.c b/src/tests/test_utils_vl_lookup.c
new file mode 100644 (file)
index 0000000..f958601
--- /dev/null
@@ -0,0 +1,249 @@
+/**
+ * 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 "tests/macros.h"
+#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;
+
+  OK1(expect_new_obj == have_new_obj,
+      (expect_new_obj ? "New obj is created." : "Updating existing 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;
+
+  OK(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,
+    unsigned int group_by)
+{
+  identifier_t ident;
+  void *user_class;
+
+  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));
+
+  OK(lookup_add (obj, &ident, group_by, user_class) == 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);
+  OK(obj != NULL);
+  return (obj);
+}
+
+DEF_TEST(group_by_specific_host)
+{
+  lookup_t *obj = checked_lookup_create ();
+
+  checked_lookup_add (obj, "/.*/", "test", "", "test", "/.*/", LU_GROUP_BY_HOST);
+  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);
+  return (0);
+}
+
+DEF_TEST(group_by_any_host)
+{
+  lookup_t *obj = checked_lookup_create ();
+
+  checked_lookup_add (obj, "/.*/", "/.*/", "/.*/", "test", "/.*/", LU_GROUP_BY_HOST);
+  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);
+  return (0);
+}
+
+DEF_TEST(multiple_lookups)
+{
+  lookup_t *obj = checked_lookup_create ();
+  int status;
+
+  checked_lookup_add (obj, "/.*/", "plugin0", "", "test", "/.*/", LU_GROUP_BY_HOST);
+  checked_lookup_add (obj, "/.*/", "/.*/", "", "test", "ti0", LU_GROUP_BY_HOST);
+
+  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);
+  return (0);
+}
+
+DEF_TEST(regex)
+{
+  lookup_t *obj = checked_lookup_create ();
+
+  checked_lookup_add (obj, "/^db[0-9]\\./", "cpu", "/.*/", "cpu", "/.*/",
+      LU_GROUP_BY_TYPE_INSTANCE);
+  checked_lookup_search (obj, "db0.example.com", "cpu", "0", "cpu", "user",
+      /* expect new = */ 1);
+  checked_lookup_search (obj, "db0.example.com", "cpu", "0", "cpu", "idle",
+      /* expect new = */ 1);
+  checked_lookup_search (obj, "db0.example.com", "cpu", "1", "cpu", "user",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "db0.example.com", "cpu", "1", "cpu", "idle",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "app0.example.com", "cpu", "0", "cpu", "user",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "app0.example.com", "cpu", "0", "cpu", "idle",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "db1.example.com", "cpu", "0", "cpu", "user",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "db1.example.com", "cpu", "0", "cpu", "idle",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "db1.example.com", "cpu", "0", "cpu", "system",
+      /* expect new = */ 1);
+
+  lookup_destroy (obj);
+  return (0);
+}
+
+int main (int argc, char **argv) /* {{{ */
+{
+  RUN_TEST(group_by_specific_host);
+  RUN_TEST(group_by_any_host);
+  RUN_TEST(multiple_lookups);
+  RUN_TEST(regex);
+
+  END_TEST;
+} /* }}} int main */
index 0ad0d90..27c92bc 100644 (file)
@@ -55,6 +55,9 @@ static void thermal_submit (const char *plugin_instance, enum dev_type dt,
        v.gauge = value;
        vl.values = &v;
 
+       vl.values_len = 1;
+
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "thermal", sizeof(vl.plugin));
        if (plugin_instance != NULL)
                sstrncpy (vl.plugin_instance, plugin_instance,
@@ -77,12 +80,12 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
        if (device_list && ignorelist_match (device_list, name))
                return -1;
 
-       len = snprintf (filename, sizeof (filename),
+       len = ssnprintf (filename, sizeof (filename),
                        "%s/%s/temp", dirname_sysfs, name);
        if ((len < 0) || ((size_t) len >= sizeof (filename)))
                return -1;
 
-       len = read_file_contents (filename, data, sizeof(data));
+       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
        if (len > 1 && data[--len] == '\n') {
                char *endptr = NULL;
                double temp;
@@ -97,12 +100,12 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
                }
        }
 
-       len = snprintf (filename, sizeof (filename),
+       len = ssnprintf (filename, sizeof (filename),
                        "%s/%s/cur_state", dirname_sysfs, name);
        if ((len < 0) || ((size_t) len >= sizeof (filename)))
                return -1;
 
-       len = read_file_contents (filename, data, sizeof(data));
+       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
        if (len > 1 && data[--len] == '\n') {
                char *endptr = NULL;
                double state;
@@ -136,12 +139,12 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir,
         * temperature:             55 C
         */
        
-       len = snprintf (filename, sizeof (filename),
+       len = ssnprintf (filename, sizeof (filename),
                        "%s/%s/temperature", dirname_procfs, name);
        if ((len < 0) || ((size_t) len >= sizeof (filename)))
                return -1;
 
-       len = read_file_contents (filename, data, sizeof(data));
+       len = (ssize_t) read_file_contents (filename, data, sizeof(data));
        if ((len > 0) && ((size_t) len > sizeof(str_temp))
                        && (data[--len] == '\n')
                        && (! strncmp(data, str_temp, sizeof(str_temp)-1))) {
index d4cfd6e..8815a00 100644 (file)
 #include "plugin.h"
 #include "utils_avltree.h"
 #include "utils_cache.h"
+#include "utils_threshold.h"
 
 #include <assert.h>
 #include <pthread.h>
 
 /*
- * Private data structures
- * {{{ */
-#define UT_FLAG_INVERT  0x01
-#define UT_FLAG_PERSIST 0x02
-#define UT_FLAG_PERCENTAGE 0x04
-#define UT_FLAG_INTERESTING 0x08
-#define UT_FLAG_PERSIST_OK 0x10
-typedef struct threshold_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];
-  char data_source[DATA_MAX_NAME_LEN];
-  gauge_t warning_min;
-  gauge_t warning_max;
-  gauge_t failure_min;
-  gauge_t failure_max;
-  gauge_t hysteresis;
-  unsigned int flags;
-  int hits;
-  struct threshold_s *next;
-} threshold_t;
-/* }}} */
-
-/*
- * Private (static) variables
- * {{{ */
-static c_avl_tree_t   *threshold_tree = NULL;
-static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
-/* }}} */
-
-/*
  * Threshold management
  * ====================
  * The following functions add, delete, search, etc. configured thresholds to
  * the underlying AVL trees.
  */
-/*
- * threshold_t *threshold_get
- *
- * Retrieve one specific threshold configuration. For looking up a threshold
- * matching a value_list_t, see "threshold_search" below. Returns NULL if the
- * specified threshold doesn't exist.
- */
-static threshold_t *threshold_get (const char *hostname,
-    const char *plugin, const char *plugin_instance,
-    const char *type, const char *type_instance)
-{ /* {{{ */
-  char name[6 * DATA_MAX_NAME_LEN];
-  threshold_t *th = NULL;
-
-  format_name (name, sizeof (name),
-      (hostname == NULL) ? "" : hostname,
-      (plugin == NULL) ? "" : plugin, plugin_instance,
-      (type == NULL) ? "" : type, type_instance);
-  name[sizeof (name) - 1] = '\0';
-
-  if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
-    return (th);
-  else
-    return (NULL);
-} /* }}} threshold_t *threshold_get */
 
 /*
  * int ut_threshold_add
@@ -171,58 +113,6 @@ static int ut_threshold_add (const threshold_t *th)
   return (status);
 } /* }}} int ut_threshold_add */
 
-/* 
- * threshold_t *threshold_search
- *
- * Searches for a threshold configuration using all the possible variations of
- * "Host", "Plugin" and "Type" blocks. Returns NULL if no threshold could be
- * found.
- * XXX: This is likely the least efficient function in collectd.
- */
-static threshold_t *threshold_search (const value_list_t *vl)
-{ /* {{{ */
-  threshold_t *th;
-
-  if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
-         vl->type, NULL)) != NULL)
-    return (th);
-  else if ((th = threshold_get (vl->host, vl->plugin, NULL,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get (vl->host, vl->plugin, NULL,
-         vl->type, NULL)) != NULL)
-    return (th);
-  else if ((th = threshold_get (vl->host, "", NULL,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get (vl->host, "", NULL,
-         vl->type, NULL)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
-         vl->type, NULL)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", vl->plugin, NULL,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", vl->plugin, NULL,
-         vl->type, NULL)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", "", NULL,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", "", NULL,
-         vl->type, NULL)) != NULL)
-    return (th);
-
-  return (NULL);
-} /* }}} threshold_t *threshold_search */
-
 /*
  * Configuration
  * =============
@@ -629,7 +519,9 @@ static int ut_report_state (const data_set_t *ds,
           ": Value is no longer missing.");
     else
       status = ssnprintf (buf, bufsize,
-          ": All data sources are within range again.");
+          ": All data sources are within range again. "
+          "Current value of \"%s\" is %f.",
+          ds->ds[ds_index].name, values[ds_index]);
     buf += status;
     bufsize -= status;
   }
@@ -747,23 +639,40 @@ static int ut_check_one_data_source (const data_set_t *ds,
 
   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
    * and probably, do not work as you expect. Enjoy! :D */
-  if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
-  {
-    switch(prev_state)
+  if (th->hysteresis > 0)
+  {
+    prev_state = uc_get_state(ds,vl);
+    /* The purpose of hysteresis is elliminating flapping state when the value
+     * oscilates around the thresholds. In other words, what is important is
+     * the previous state; if the new value would trigger a transition, make
+     * sure that we artificially widen the range which is considered to apply
+     * for the previous state, and only trigger the notification if the value
+     * is outside of this expanded range.
+     *
+     * There is no hysteresis for the OKAY state.
+     * */
+    gauge_t hysteresis_for_warning = 0, hysteresis_for_failure = 0;
+    switch (prev_state)
     {
       case STATE_ERROR:
-       if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
-            (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
-         return (STATE_OKAY);
-       else
-         is_failure++;
+        hysteresis_for_failure = th->hysteresis;
+        break;
       case STATE_WARNING:
-       if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
-            (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
-         return (STATE_OKAY);
-       else
-         is_warning++;
-     }
+        hysteresis_for_warning = th->hysteresis;
+        break;
+      case STATE_OKAY:
+        /* do nothing -- the hysteresis only applies to the non-normal states */
+        break;
+    }
+
+    if ((!isnan (th->failure_min) && (th->failure_min + hysteresis_for_failure > values[ds_index]))
+       || (!isnan (th->failure_max) && (th->failure_max - hysteresis_for_failure < values[ds_index])))
+      is_failure++;
+
+    if ((!isnan (th->warning_min) && (th->warning_min + hysteresis_for_warning > values[ds_index]))
+       || (!isnan (th->warning_max) && (th->warning_max - hysteresis_for_warning < values[ds_index])))
+      is_warning++;
+
   }
   else { /* no hysteresis */
     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
@@ -773,7 +682,7 @@ static int ut_check_one_data_source (const data_set_t *ds,
     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
        || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
       is_warning++;
- }
 }
 
   if (is_failure != 0)
     return (STATE_ERROR);
@@ -862,7 +771,7 @@ static int ut_check_one_threshold (const data_set_t *ds,
  *
  * Gets a list of matching thresholds and searches for the worst status by one
  * of the thresholds. Then reports that status using the ut_report_state
- * function above. 
+ * function above.
  * Returns zero on success and if no threshold has been configured. Returns
  * less than zero on failure.
  */
@@ -942,22 +851,25 @@ static int ut_missing (const value_list_t *vl,
   cdtime_t missing_time;
   char identifier[6 * DATA_MAX_NAME_LEN];
   notification_t n;
+  cdtime_t now;
 
-  /* dispatch notifications for "interesting" values only */
   if (threshold_tree == NULL)
     return (0);
 
   th = threshold_search (vl);
-  if (th == NULL)
+  /* dispatch notifications for "interesting" values only */
+  if ((th == NULL) || ((th->flags & UT_FLAG_INTERESTING) == 0))
     return (0);
 
-  missing_time = cdtime () - vl->time;
+  now = cdtime ();
+  missing_time = now - vl->time;
   FORMAT_VL (identifier, sizeof (identifier), vl);
 
   NOTIFICATION_INIT_VL (&n, vl);
   ssnprintf (n.message, sizeof (n.message),
       "%s has not been updated for %.3f seconds.",
       identifier, CDTIME_T_TO_DOUBLE (missing_time));
+  n.time = now;
 
   plugin_dispatch_notification (&n);
 
@@ -990,7 +902,7 @@ int ut_config (oconfig_item_t *ci)
   th.hits = 0;
   th.hysteresis = 0;
   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
-    
+
   for (i = 0; i < ci->children_num; i++)
   {
     oconfig_item_t *option = ci->children + i;
index 25f4d7c..97cc4cc 100644 (file)
@@ -6,13 +6,14 @@ apache_requests               value:DERIVE:0:U
 apache_scoreboard      value:GAUGE:0:65535
 ath_nodes              value:GAUGE:0:65535
 ath_stat               value:DERIVE:0:U
+backends               value:GAUGE:0:65535
 bitrate                        value:GAUGE:0:4294967295
 bytes                  value:GAUGE:0:U
 cache_eviction         value:DERIVE:0:U
 cache_operation                value:DERIVE:0:U
 cache_ratio            value:GAUGE:0:100
 cache_result           value:DERIVE:0:U
-cache_size             value:GAUGE:0:4294967295
+cache_size             value:GAUGE:0:U
 charge                 value:GAUGE:0:U
 compression_ratio      value:GAUGE:0:2
 compression            uncompressed:DERIVE:0:U, compressed:DERIVE:0:U
@@ -52,6 +53,7 @@ dns_response          value:DERIVE:0:U
 dns_transfer           value:DERIVE:0:U
 dns_update             value:DERIVE:0:U
 dns_zops               value:DERIVE:0:U
+duration               seconds:GAUGE:0:U
 email_check            value:GAUGE:0:U
 email_count            value:GAUGE:0:U
 email_size             value:GAUGE:0:U
@@ -59,6 +61,7 @@ entropy                       value:GAUGE:0:4294967295
 fanspeed               value:GAUGE:0:U
 file_size              value:GAUGE:0:U
 files                  value:GAUGE:0:U
+flow                   value:GAUGE:0:U
 fork_rate              value:DERIVE:0:U
 frequency_offset       value:GAUGE:-1000000:1000000
 frequency              value:GAUGE:0:U
@@ -76,16 +79,18 @@ if_multicast                value:DERIVE:0:U
 if_octets              rx:DERIVE:0:U, tx:DERIVE:0:U
 if_packets             rx:DERIVE:0:U, tx:DERIVE:0:U
 if_rx_errors           value:DERIVE:0:U
+if_rx_octets           value:DERIVE:0:U
 if_tx_errors           value:DERIVE:0:U
+if_tx_octets           value:DERIVE:0:U
 invocations            value:DERIVE:0:U
 io_octets              rx:DERIVE:0:U, tx:DERIVE:0:U
 io_packets             rx:DERIVE:0:U, tx:DERIVE:0:U
 ipt_bytes              value:DERIVE:0:U
 ipt_packets            value:DERIVE:0:U
 irq                    value:DERIVE:0:U
-latency                        value:GAUGE:0:65535
+latency                        value:GAUGE:0:U
 links                  value:GAUGE:0:U
-load                   shortterm:GAUGE:0:100, midterm:GAUGE:0:100, longterm:GAUGE:0:100
+load                   shortterm:GAUGE:0:5000, midterm:GAUGE:0:5000, longterm:GAUGE:0:5000
 md_disks               value:GAUGE:0:U
 memcached_command      value:DERIVE:0:U
 memcached_connections  value:GAUGE:0:U
@@ -107,8 +112,11 @@ node_octets                rx:DERIVE:0:U, tx:DERIVE:0:U
 node_rssi              value:GAUGE:0:255
 node_stat              value:DERIVE:0:U
 node_tx_rate           value:GAUGE:0:127
+objects                        value:GAUGE:0:U
 operations             value:DERIVE:0:U
 percent                        value:GAUGE:0:100.1
+percent_bytes          value:GAUGE:0:100.1
+percent_inodes         value:GAUGE:0:100.1
 pf_counters            value:DERIVE:0:U
 pf_limits              value:DERIVE:0:U
 pf_source              value:DERIVE:0:U
@@ -142,6 +150,7 @@ queue_length                value:GAUGE:0:U
 records                        value:GAUGE:0:U
 requests               value:GAUGE:0:U
 response_time          value:GAUGE:0:U
+response_code          value:GAUGE:0:U
 route_etx              value:GAUGE:0:U
 route_metric           value:GAUGE:0:U
 routes                 value:GAUGE:0:U
@@ -152,16 +161,18 @@ signal_quality            value:GAUGE:0:U
 snr                    value:GAUGE:0:U
 spam_check             value:GAUGE:0:U
 spam_score             value:GAUGE:U:U
+spl                    value:GAUGE:U:U
 swap_io                        value:DERIVE:0:U
 swap                   value:GAUGE:0:1099511627776
 tcp_connections                value:GAUGE:0:4294967295
-temperature            value:GAUGE:-273.15:U
+temperature            value:GAUGE:U:U
 threads                        value:GAUGE:0:U
 time_dispersion                value:GAUGE:-1000000:1000000
-timeleft               value:GAUGE:0:3600
+timeleft               value:GAUGE:0:U
 time_offset            value:GAUGE:-1000000:1000000
 total_bytes            value:DERIVE:0:U
 total_connections      value:DERIVE:0:U
+total_objects          value:DERIVE:0:U
 total_operations       value:DERIVE:0:U
 total_requests         value:DERIVE:0:U
 total_sessions         value:DERIVE:0:U
@@ -170,6 +181,7 @@ total_time_in_ms    value:DERIVE:0:U
 total_values           value:DERIVE:0:U
 uptime                 value:GAUGE:0:4294967295
 users                  value:GAUGE:0:65535
+vcl                    value:GAUGE:0:65535
 vcpu                   value:GAUGE:0:U
 virt_cpu_total         value:DERIVE:0:U
 virt_vcpu              value:DERIVE:0:U
@@ -183,6 +195,7 @@ voltage                     value:GAUGE:U:U
 vs_memory              value:GAUGE:0:9223372036854775807
 vs_processes           value:GAUGE:0:65535
 vs_threads             value:GAUGE:0:65535
+
 #
 # Legacy types
 # (required for the v5 upgrade target)
index 67f9881..111742b 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 types.db - Data-set specifications for the system statistics collection daemon
index 2c1665f..7e7a1b1 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "utils_cmd_flush.h"
 #include "utils_cmd_getval.h"
+#include "utils_cmd_getthreshold.h"
 #include "utils_cmd_listval.h"
 #include "utils_cmd_putval.h"
 #include "utils_cmd_putnotif.h"
@@ -281,6 +282,10 @@ static void *us_handle_client (void *arg)
                {
                        handle_getval (fhout, buffer);
                }
+               else if (strcasecmp (fields[0], "getthreshold") == 0)
+               {
+                       handle_getthreshold (fhout, buffer);
+               }
                else if (strcasecmp (fields[0], "putval") == 0)
                {
                        handle_putval (fhout, buffer);
index 064c3ce..345128d 100644 (file)
@@ -241,7 +241,7 @@ static int uptime_read (void)
        gauge_t uptime;
        time_t elapsed;
 
-       /* calculate the ammount of time elapsed since boot, AKA uptime */
+       /* calculate the amount of time elapsed since boot, AKA uptime */
        elapsed = time (NULL) - boottime;
 
        uptime = (gauge_t) elapsed;
index fa6e660..05386db 100644 (file)
@@ -68,7 +68,9 @@ static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static int cache_compare (const cache_entry_t *a, const cache_entry_t *b)
 {
+#if COLLECT_DEBUG
   assert ((a != NULL) && (b != NULL));
+#endif
   return (strcmp (a->name, b->name));
 } /* int cache_compare */
 
index 3584f3b..d71074a 100644 (file)
@@ -168,7 +168,7 @@ int handle_flush (FILE *fh, char *buffer)
        }
        else
        {
-               plugin_flush (NULL, timeout, NULL);
+               plugin_flush (NULL, DOUBLE_TO_CDTIME_T (timeout), NULL);
                print_to_socket (fh, "0 Done\n");
        }
 
index e8c29fa..b8249c6 100644 (file)
@@ -23,6 +23,7 @@
 #include "common.h"
 #include "plugin.h"
 
+#include "utils_avltree.h"
 #include "utils_threshold.h"
 #include "utils_parse_option.h" /* for `parse_string' */
 #include "utils_cmd_getthreshold.h"
index 5a9faff..d3cf383 100644 (file)
@@ -49,13 +49,18 @@ static int set_option_severity (notification_t *n, const char *value)
 
 static int set_option_time (notification_t *n, const char *value)
 {
-  time_t tmp;
-  
-  tmp = (time_t) atoi (value);
-  if (tmp <= 0)
+  char *endptr = NULL;
+  double tmp;
+
+  errno = 0;
+  tmp = strtod (value, &endptr);
+  if ((errno != 0)         /* Overflow */
+      || (endptr == value) /* Invalid string */
+      || (endptr == NULL)  /* This should not happen */
+      || (*endptr != 0))   /* Trailing chars */
     return (-1);
 
-  n->time = tmp;
+  n->time = DOUBLE_TO_CDTIME_T (tmp);
 
   return (0);
 } /* int set_option_time */
index 328a6ab..c3752bc 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/utils_complain.c
- * Copyright (C) 2006-2007  Florian octo Forster
+ * Copyright (C) 2006-2013  Florian octo Forster
  * Copyright (C) 2008  Sebastian tokkee Harl
  *
  * This program is free software; you can redistribute it and/or modify it
 static int vcomplain (int level, c_complain_t *c,
                const char *format, va_list ap)
 {
-       time_t now;
+       cdtime_t now;
        char   message[512];
 
-       now = time (NULL);
+       now = cdtime ();
 
        if (c->last + c->interval > now)
                return 0;
@@ -44,8 +44,8 @@ static int vcomplain (int level, c_complain_t *c,
        else
                c->interval *= 2;
 
-       if (c->interval > 86400)
-               c->interval = 86400;
+       if (c->interval > TIME_T_TO_CDTIME_T (86400))
+               c->interval = TIME_T_TO_CDTIME_T (86400);
 
        vsnprintf (message, sizeof (message), format, ap);
        message[sizeof (message) - 1] = '\0';
@@ -58,12 +58,9 @@ void c_complain (int level, c_complain_t *c, const char *format, ...)
 {
        va_list ap;
 
-       /* reset the old interval */
-       if (c->interval < 0)
-               c->interval *= -1;
-
        va_start (ap, format);
-       vcomplain (level, c, format, ap);
+       if (vcomplain (level, c, format, ap))
+               c->complained_once = 1;
        va_end (ap);
 } /* c_complain */
 
@@ -71,12 +68,12 @@ void c_complain_once (int level, c_complain_t *c, const char *format, ...)
 {
        va_list ap;
 
-       if (c->interval < 0)
+       if (c->complained_once)
                return;
 
        va_start (ap, format);
        if (vcomplain (level, c, format, ap))
-               c->interval *= -1;
+               c->complained_once = 1;
        va_end (ap);
 } /* c_complain_once */
 
@@ -89,6 +86,7 @@ void c_do_release (int level, c_complain_t *c, const char *format, ...)
                return;
 
        c->interval = 0;
+       c->complained_once = 0;
 
        va_start (ap, format);
        vsnprintf (message, sizeof (message), format, ap);
index 09c4375..028dda6 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/utils_complain.h
- * Copyright (C) 2006-2007  Florian octo Forster
+ * Copyright (C) 2006-2013  Florian octo Forster
  * Copyright (C) 2008  Sebastian tokkee Harl
  *
  * This program is free software; you can redistribute it and/or modify it
 #ifndef UTILS_COMPLAIN_H
 #define UTILS_COMPLAIN_H 1
 
-#include <time.h>
+#include "utils_time.h"
 
 typedef struct
 {
        /* time of the last report */
-       time_t last;
+       cdtime_t last;
 
-       /* how long to wait until reporting again
-        *   0 indicates that the complaint is no longer valid
-        * < 0 indicates that the complaint has been reported once
-        *     => c_complain_once will not report again
-        *     => c_complain uses the absolute value to reset the old value */
-       int interval;
+       /* How long to wait until reporting again.
+        * 0 indicates that the complaint is no longer valid. */
+       cdtime_t interval;
+
+       _Bool complained_once;
 } c_complain_t;
 
-#define C_COMPLAIN_INIT_STATIC { 0, 0 }
-#define C_COMPLAIN_INIT(c) do { (c)->last = 0; (c)->interval = 0; } while (0)
+#define C_COMPLAIN_INIT_STATIC { 0, 0, 0 }
+#define C_COMPLAIN_INIT(c) do { \
+       (c)->last = 0; \
+       (c)->interval = 0; \
+       (c)->complained_once = 0; \
+} while (0)
 
 /*
  * NAME
diff --git a/src/utils_crc32.c b/src/utils_crc32.c
new file mode 100644 (file)
index 0000000..4c6d694
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ *
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ */
+
+#include <sys/types.h>
+
+u_int32_t               crc32_buffer(const u_char *, size_t);
+static unsigned int     crc32_tab[] = {
+       0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+       0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+       0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+       0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+       0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+       0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+       0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+       0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+       0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+       0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+       0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+       0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+       0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+       0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+       0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+       0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+       0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+       0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+       0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+       0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+       0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+       0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+       0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+       0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+       0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+       0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+       0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+       0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+       0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+       0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+       0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+       0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+       0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+       0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+       0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+       0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+       0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+       0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+       0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+       0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+       0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+       0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+       0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+       0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+       0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+       0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+       0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+       0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+       0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+       0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+       0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+       0x2d02ef8dL
+};
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+u_int32_t
+crc32_buffer(const u_char *s, size_t len)
+{
+    size_t      i;
+    u_int32_t   ret;
+
+    ret = 0;
+    for (i = 0;  i < len;  i++)
+        ret = crc32_tab[(ret ^ s[i]) & 0xff] ^ (ret >> 8);
+    return ret;
+}
diff --git a/src/utils_crc32.h b/src/utils_crc32.h
new file mode 100644 (file)
index 0000000..b16409d
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * collectd - src/utils_crc32.h
+ *
+ * Copyright (C) 2014       Pierre-Yves Ritschard
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ */
+
+#ifndef UTILS_CRC32_H
+#define UTILS_CRC32_H 1
+
+u_int32_t               crc32_buffer(const u_char *, size_t);
+
+#endif
index 80a2ee5..655c61e 100644 (file)
 #include "plugin.h"
 #include "common.h"
 
-#if HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#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_SYS_SOCKET_H
 # include <sys/socket.h>
 #endif
 
-#if HAVE_ARPA_NAMESER_H
-# include <arpa/nameser.h>
-#endif
-#if HAVE_ARPA_NAMESER_COMPAT_H
-# include <arpa/nameser_compat.h>
-#endif
-
 #if HAVE_NET_IF_ARP_H
 # include <net/if_arp.h>
 #endif
 #if HAVE_NET_IF_H
 # include <net/if.h>
 #endif
-#if HAVE_NETINET_IF_ETHER_H
-# include <netinet/if_ether.h>
-#endif
 #if HAVE_NET_PPP_DEFS_H
 # include <net/ppp_defs.h>
 #endif
 # include <net/if_ppp.h>
 #endif
 
-#if HAVE_NETDB_H
-# include <netdb.h>
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#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_NETINET_IF_ETHER_H
+# include <netinet/if_ether.h>
 #endif
-
 #if HAVE_NETINET_IP_H
 # include <netinet/ip.h>
 #endif
 #ifdef HAVE_NETINET_IP_VAR_H
 # include <netinet/ip_var.h>
 #endif
-#if HAVE_NETINET_IP6_H
-# include <netinet/ip6.h>
-#endif
 #if HAVE_NETINET_UDP_H
 # include <netinet/udp.h>
 #endif
 
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#if HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#endif
+#if HAVE_ARPA_NAMESER_COMPAT_H
+# include <arpa/nameser_compat.h>
+#endif
+
+#if HAVE_NETDB_H
+# include <netdb.h>
+#endif
+
 #if HAVE_PCAP_H
 # include <pcap.h>
 #endif
index 97f21a1..38b713e 100644 (file)
@@ -253,7 +253,7 @@ char *fbh_get (fbhash_t *h, const char *key) /* {{{ */
 
   pthread_mutex_lock (&h->lock);
 
-  /* TODO: Checking this everytime may be a bit much..? */
+  /* TODO: Checking this every time may be a bit much..? */
   fbh_check_file (h);
 
   status = c_avl_get (h->tree, key, (void *) &value);
index b9b906f..b7d4494 100644 (file)
 #include "plugin.h"
 #include "common.h"
 
+#include "utils_format_graphite.h"
 #include "utils_cache.h"
-#include "utils_format_json.h"
 #include "utils_parse_option.h"
 
+#define GRAPHITE_FORBIDDEN " \t\"\\:!/()\n\r"
+
 /* Utils functions to format data sets in graphite format.
  * Largely taken from write_graphite.c as it remains the same formatting */
 
@@ -108,11 +110,12 @@ static void gr_copy_escape_part (char *dst, const char *src, size_t dst_len,
 }
 
 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)
+        value_list_t const *vl,
+        char const *ds_name,
+        char const *prefix,
+        char const *postfix,
+        char const escape_char,
+        unsigned int flags)
 {
     char n_host[DATA_MAX_NAME_LEN];
     char n_plugin[DATA_MAX_NAME_LEN];
@@ -143,7 +146,7 @@ static int gr_format_name (char *ret, int ret_len,
     if (n_plugin_instance[0] != '\0')
         ssnprintf (tmp_plugin, sizeof (tmp_plugin), "%s%c%s",
             n_plugin,
-            '-',
+            (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
             n_plugin_instance);
     else
         sstrncpy (tmp_plugin, n_plugin, sizeof (tmp_plugin));
@@ -151,11 +154,13 @@ static int gr_format_name (char *ret, int ret_len,
     if (n_type_instance[0] != '\0')
         ssnprintf (tmp_type, sizeof (tmp_type), "%s%c%s",
             n_type,
-            '-',
+            (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
             n_type_instance);
     else
         sstrncpy (tmp_type, n_type, sizeof (tmp_type));
 
+    /* Assert always_append_ds -> ds_name */
+    assert (!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
     if (ds_name != NULL)
         ssnprintf (ret, ret_len, "%s%s%s.%s.%s.%s",
             prefix, n_host, postfix, tmp_plugin, tmp_type, ds_name);
@@ -166,32 +171,46 @@ static int gr_format_name (char *ret, int ret_len,
     return (0);
 }
 
+static void escape_graphite_string (char *buffer, char escape_char)
+{
+       char *head;
+
+       assert (strchr(GRAPHITE_FORBIDDEN, escape_char) == NULL);
+
+       for (head = buffer + strcspn(buffer, GRAPHITE_FORBIDDEN);
+            *head != '\0';
+            head += strcspn(head, GRAPHITE_FORBIDDEN))
+               *head = escape_char;
+}
+
 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)
+    data_set_t const *ds, value_list_t const *vl,
+    char const *prefix, char const *postfix, char const escape_char,
+    unsigned int flags)
 {
     int status = 0;
     int i;
     int buffer_pos = 0;
 
     gauge_t *rates = NULL;
-    if (store_rates)
+    if (flags & GRAPHITE_STORE_RATES)
       rates = uc_get_rate (ds, vl);
 
     for (i = 0; i < ds->ds_num; i++)
     {
-        const char *ds_name = NULL;
+        char const *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;
+        if ((flags & GRAPHITE_ALWAYS_APPEND_DS)
+            || (ds->ds_num > 1))
+          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);
+                    prefix, postfix, escape_char, flags);
         if (status != 0)
         {
             ERROR ("format_graphite: error with gr_format_name");
@@ -199,7 +218,7 @@ int format_graphite (char *buffer, size_t buffer_size,
             return (status);
         }
 
-        escape_string (key, sizeof (key));
+        escape_graphite_string (key, escape_char);
         /* Convert the values to an ASCII representation and put that into
          * `values'. */
         status = gr_format_values (values, sizeof (values), i, ds, vl, rates);
index a7a05bb..398defb 100644 (file)
 #include "collectd.h"
 #include "plugin.h"
 
+#define GRAPHITE_STORE_RATES        0x01
+#define GRAPHITE_SEPARATE_INSTANCES 0x02
+#define GRAPHITE_ALWAYS_APPEND_DS   0x04
+
 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);
+    unsigned int flags);
 
 #endif /* UTILS_FORMAT_GRAPHITE_H */
diff --git a/src/utils_latency.c b/src/utils_latency.c
new file mode 100644 (file)
index 0000000..94da211
--- /dev/null
@@ -0,0 +1,187 @@
+/**
+ * collectd - src/utils_latency.c
+ * Copyright (C) 2013  Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <ff at octo.it>
+ **/
+
+#include "collectd.h"
+#include "utils_latency.h"
+#include "common.h"
+
+#ifndef LATENCY_HISTOGRAM_SIZE
+# define LATENCY_HISTOGRAM_SIZE 1000
+#endif
+
+struct latency_counter_s
+{
+  cdtime_t start_time;
+
+  cdtime_t sum;
+  size_t num;
+
+  cdtime_t min;
+  cdtime_t max;
+
+  int histogram[LATENCY_HISTOGRAM_SIZE];
+};
+
+latency_counter_t *latency_counter_create () /* {{{ */
+{
+  latency_counter_t *lc;
+
+  lc = malloc (sizeof (*lc));
+  if (lc == NULL)
+    return (NULL);
+
+  latency_counter_reset (lc);
+  return (lc);
+} /* }}} latency_counter_t *latency_counter_create */
+
+void latency_counter_destroy (latency_counter_t *lc) /* {{{ */
+{
+  sfree (lc);
+} /* }}} void latency_counter_destroy */
+
+void latency_counter_add (latency_counter_t *lc, cdtime_t latency) /* {{{ */
+{
+  size_t latency_ms;
+
+  if ((lc == NULL) || (latency == 0))
+    return;
+
+  lc->sum += latency;
+  lc->num++;
+
+  if ((lc->min == 0) && (lc->max == 0))
+    lc->min = lc->max = latency;
+  if (lc->min > latency)
+    lc->min = latency;
+  if (lc->max < latency)
+    lc->max = latency;
+
+  /* A latency of _exactly_ 1.0 ms should be stored in the buffer 0, so
+   * subtract one from the cdtime_t value so that exactly 1.0 ms get sorted
+   * accordingly. */
+  latency_ms = (size_t) CDTIME_T_TO_MS (latency - 1);
+  if (latency_ms < STATIC_ARRAY_SIZE (lc->histogram))
+    lc->histogram[latency_ms]++;
+} /* }}} void latency_counter_add */
+
+void latency_counter_reset (latency_counter_t *lc) /* {{{ */
+{
+  if (lc == NULL)
+    return;
+
+  memset (lc, 0, sizeof (*lc));
+  lc->start_time = cdtime ();
+} /* }}} void latency_counter_reset */
+
+cdtime_t latency_counter_get_min (latency_counter_t *lc) /* {{{ */
+{
+  if (lc == NULL)
+    return (0);
+  return (lc->min);
+} /* }}} cdtime_t latency_counter_get_min */
+
+cdtime_t latency_counter_get_max (latency_counter_t *lc) /* {{{ */
+{
+  if (lc == NULL)
+    return (0);
+  return (lc->max);
+} /* }}} cdtime_t latency_counter_get_max */
+
+cdtime_t latency_counter_get_sum (latency_counter_t *lc) /* {{{ */
+{
+  if (lc == NULL)
+    return (0);
+  return (lc->sum);
+} /* }}} cdtime_t latency_counter_get_sum */
+
+size_t latency_counter_get_num (latency_counter_t *lc) /* {{{ */
+{
+  if (lc == NULL)
+    return (0);
+  return (lc->num);
+} /* }}} size_t latency_counter_get_num */
+
+cdtime_t latency_counter_get_average (latency_counter_t *lc) /* {{{ */
+{
+  double average;
+
+  if (lc == NULL)
+    return (0);
+
+  average = CDTIME_T_TO_DOUBLE (lc->sum) / ((double) lc->num);
+  return (DOUBLE_TO_CDTIME_T (average));
+} /* }}} cdtime_t latency_counter_get_average */
+
+cdtime_t latency_counter_get_percentile (latency_counter_t *lc,
+    double percent)
+{
+  double percent_upper;
+  double percent_lower;
+  double ms_upper;
+  double ms_lower;
+  double ms_interpolated;
+  int sum;
+  size_t i;
+
+  if ((lc == NULL) || !((percent > 0.0) && (percent < 100.0)))
+    return (0);
+
+  /* Find index i so that at least "percent" events are within i+1 ms. */
+  percent_upper = 0.0;
+  percent_lower = 0.0;
+  sum = 0;
+  for (i = 0; i < LATENCY_HISTOGRAM_SIZE; i++)
+  {
+    percent_lower = percent_upper;
+    sum += lc->histogram[i];
+    if (sum == 0)
+      percent_upper = 0.0;
+    else
+      percent_upper = 100.0 * ((double) sum) / ((double) lc->num);
+
+    if (percent_upper >= percent)
+      break;
+  }
+
+  if (i >= LATENCY_HISTOGRAM_SIZE)
+    return (0);
+
+  assert (percent_upper >= percent);
+  assert (percent_lower < percent);
+
+  ms_upper = (double) (i + 1);
+  ms_lower = (double) i;
+  if (i == 0)
+    return (MS_TO_CDTIME_T (ms_upper));
+
+  ms_interpolated = (((percent_upper - percent) * ms_lower)
+      + ((percent - percent_lower) * ms_upper))
+    / (percent_upper - percent_lower);
+
+  return (MS_TO_CDTIME_T (ms_interpolated));
+} /* }}} cdtime_t latency_counter_get_percentile */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/utils_latency.h b/src/utils_latency.h
new file mode 100644 (file)
index 0000000..3787c77
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * collectd - src/utils_latency.h
+ * Copyright (C) 2013  Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <ff at octo.it>
+ **/
+
+#include "collectd.h"
+#include "utils_time.h"
+
+struct latency_counter_s;
+typedef struct latency_counter_s latency_counter_t;
+
+latency_counter_t *latency_counter_create ();
+void latency_counter_destroy (latency_counter_t *lc);
+
+void latency_counter_add (latency_counter_t *lc, cdtime_t latency);
+void latency_counter_reset (latency_counter_t *lc);
+
+cdtime_t latency_counter_get_min (latency_counter_t *lc);
+cdtime_t latency_counter_get_max (latency_counter_t *lc);
+cdtime_t latency_counter_get_sum (latency_counter_t *lc);
+size_t   latency_counter_get_num (latency_counter_t *lc);
+cdtime_t latency_counter_get_average (latency_counter_t *lc);
+cdtime_t latency_counter_get_percentile (latency_counter_t *lc,
+    double percent);
+
+/* vim: set sw=2 sts=2 et : */
index cae3706..3cede01 100644 (file)
@@ -752,9 +752,6 @@ cu_mount_getoptionvalue(char *line, char *keyword)
                r += strlen(keyword);
                p = strchr(r, ',');
                if(p == NULL) {
-                       if(strlen(r) == 0) {
-                               return NULL;
-                       }
                        return sstrdup(r);
                } else {
                        char *m;
index 1f2403c..bc0077f 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef COLLECTD_UTILS_MOUNT_H
 #define COLLECTD_UTILS_MOUNT_H 1
 
+#include <stdio.h>
 #if HAVE_FS_INFO_H
 # include <fs_info.h>
 #endif
@@ -118,7 +119,7 @@ char *cu_mount_checkoption(char *line, char *keyword, int full);
   DESCRIPTION
        The cu_mount_checkoption() function is a replacement of
        char *hasmntopt(const struct mntent *mnt, const char *opt).
-       In fact hasmntopt() just looks for the first occurence of the
+       In fact hasmntopt() just looks for the first occurrence of the
        characters at opt in mnt->mnt_opts. cu_mount_checkoption()
        checks for the *option* keyword in line, starting at the
        first character of line or after a ','.
index 2168cd1..820f14f 100644 (file)
@@ -127,7 +127,7 @@ int parse_option (char **ret_buffer, char **ret_key, char **ret_value)
 
   /* Look for the equal sign */
   buffer = key;
-  while (isalnum ((int) *buffer))
+  while (isalnum ((int) *buffer) || *buffer == '_')
     buffer++;
   if ((*buffer != '=') || (buffer == key))
     return (1);
diff --git a/src/utils_random.c b/src/utils_random.c
new file mode 100644 (file)
index 0000000..b873845
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * collectd - src/utils_random.c
+ * Copyright (C) 2013       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "utils_time.h"
+
+#include <pthread.h>
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static _Bool have_seed = 0;
+static unsigned short seed[3];
+
+static void cdrand_seed (void)
+{
+  cdtime_t t;
+
+  if (have_seed)
+    return;
+
+  t = cdtime();
+
+  seed[0] = (unsigned short) t;
+  seed[1] = (unsigned short) (t >> 16);
+  seed[2] = (unsigned short) (t >> 32);
+
+  have_seed = 1;
+}
+
+double cdrand_d (void)
+{
+  double r;
+
+  pthread_mutex_lock (&lock);
+  cdrand_seed ();
+  r = erand48 (seed);
+  pthread_mutex_unlock (&lock);
+
+  return (r);
+}
+
+long cdrand_range (long min, long max)
+{
+  long range;
+  long r;
+
+  range = 1 + max - min;
+
+  r = (long) (0.5 + (cdrand_d () * range));
+  r += min;
+
+  return (r);
+}
diff --git a/src/utils_random.h b/src/utils_random.h
new file mode 100644 (file)
index 0000000..b05f4c8
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * collectd - src/utils_random.h
+ * Copyright (C) 2013       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+/**
+ * Returns a random double value in the range [0..1), i.e. excluding 1.
+ *
+ * This function is thread- and reentrant-safe.
+ */
+double cdrand_d (void);
+
+/**
+ * Returns a random long between min and max, inclusively.
+ *
+ * If min is larger than max, the result may be rounded incorrectly and may be
+ * outside the intended range. This function is thread- and reentrant-safe.
+ */
+long cdrand_range (long min, long max);
index 091b5fa..a34e0da 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/utils_rrdcreate.c
- * Copyright (C) 2006-2008  Florian octo Forster
+ * Copyright (C) 2006-2013  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -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"
 #include <pthread.h>
 #include <rrd.h>
 
+struct srrd_create_args_s
+{
+  char *filename;
+  unsigned long pdp_step;
+  time_t last_up;
+  int argc;
+  char **argv;
+};
+typedef struct srrd_create_args_s srrd_create_args_t;
+
+struct async_create_file_s;
+typedef struct async_create_file_s async_create_file_t;
+struct async_create_file_s
+{
+  char *filename;
+  async_create_file_t *next;
+};
+
 /*
  * Private variables
  */
@@ -51,6 +69,9 @@ static int rra_types_num = STATIC_ARRAY_SIZE (rra_types);
 static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
+static async_create_file_t *async_creation_list = NULL;
+static pthread_mutex_t async_creation_lock = PTHREAD_MUTEX_INITIALIZER;
+
 /*
  * Private functions
  */
@@ -65,6 +86,71 @@ static void rra_free (int rra_num, char **rra_def) /* {{{ */
   sfree (rra_def);
 } /* }}} void rra_free */
 
+static void srrd_create_args_destroy (srrd_create_args_t *args)
+{
+  if (args == NULL)
+    return;
+
+  sfree (args->filename);
+  if (args->argv != NULL)
+  {
+    int i;
+    for (i = 0; i < args->argc; i++)
+      sfree (args->argv[i]);
+    sfree (args->argv);
+  }
+} /* void srrd_create_args_destroy */
+
+static srrd_create_args_t *srrd_create_args_create (const char *filename,
+    unsigned long pdp_step, time_t last_up,
+    int argc, const char **argv)
+{
+  srrd_create_args_t *args;
+
+  args = malloc (sizeof (*args));
+  if (args == NULL)
+  {
+    ERROR ("srrd_create_args_create: malloc failed.");
+    return (NULL);
+  }
+  memset (args, 0, sizeof (*args));
+  args->filename = NULL;
+  args->pdp_step = pdp_step;
+  args->last_up = last_up;
+  args->argv = NULL;
+
+  args->filename = strdup (filename);
+  if (args->filename == NULL)
+  {
+    ERROR ("srrd_create_args_create: strdup failed.");
+    srrd_create_args_destroy (args);
+    return (NULL);
+  }
+
+  args->argv = calloc ((size_t) (argc + 1), sizeof (*args->argv));
+  if (args->argv == NULL)
+  {
+    ERROR ("srrd_create_args_create: calloc failed.");
+    srrd_create_args_destroy (args);
+    return (NULL);
+  }
+
+  for (args->argc = 0; args->argc < argc; args->argc++)
+  {
+    args->argv[args->argc] = strdup (argv[args->argc]);
+    if (args->argv[args->argc] == NULL)
+    {
+      ERROR ("srrd_create_args_create: strdup failed.");
+      srrd_create_args_destroy (args);
+      return (NULL);
+    }
+  }
+  assert (args->argc == argc);
+  args->argv[args->argc] = NULL;
+
+  return (args);
+} /* srrd_create_args_t *srrd_create_args_create */
+
 /* * * * * * * * * *
  * WARNING:  Magic *
  * * * * * * * * * */
@@ -359,6 +445,197 @@ static int srrd_create (const char *filename, /* {{{ */
 } /* }}} int srrd_create */
 #endif /* !HAVE_THREADSAFE_LIBRRD */
 
+static int lock_file (char const *filename) /* {{{ */
+{
+  async_create_file_t *ptr;
+  struct stat sb;
+  int status;
+
+  pthread_mutex_lock (&async_creation_lock);
+
+  for (ptr = async_creation_list; ptr != NULL; ptr = ptr->next)
+    if (strcmp (filename, ptr->filename) == 0)
+      break;
+
+  if (ptr != NULL)
+  {
+    pthread_mutex_unlock (&async_creation_lock);
+    return (EEXIST);
+  }
+
+  status = stat (filename, &sb);
+  if ((status == 0) || (errno != ENOENT))
+  {
+    pthread_mutex_unlock (&async_creation_lock);
+    return (EEXIST);
+  }
+
+  ptr = malloc (sizeof (*ptr));
+  if (ptr == NULL)
+  {
+    pthread_mutex_unlock (&async_creation_lock);
+    return (ENOMEM);
+  }
+
+  ptr->filename = strdup (filename);
+  if (ptr->filename == NULL)
+  {
+    pthread_mutex_unlock (&async_creation_lock);
+    sfree (ptr);
+    return (ENOMEM);
+  }
+
+  ptr->next = async_creation_list;
+  async_creation_list = ptr;
+
+  pthread_mutex_unlock (&async_creation_lock);
+
+  return (0);
+} /* }}} int lock_file */
+
+static int unlock_file (char const *filename) /* {{{ */
+{
+  async_create_file_t *this;
+  async_create_file_t *prev;
+
+
+  pthread_mutex_lock (&async_creation_lock);
+
+  prev = NULL;
+  for (this = async_creation_list; this != NULL; this = this->next)
+  {
+    if (strcmp (filename, this->filename) == 0)
+      break;
+    prev = this;
+  }
+
+  if (this == NULL)
+  {
+    pthread_mutex_unlock (&async_creation_lock);
+    return (ENOENT);
+  }
+
+  if (prev == NULL)
+  {
+    assert (this == async_creation_list);
+    async_creation_list = this->next;
+  }
+  else
+  {
+    assert (this == prev->next);
+    prev->next = this->next;
+  }
+  this->next = NULL;
+
+  pthread_mutex_unlock (&async_creation_lock);
+
+  sfree (this->filename);
+  sfree (this);
+
+  return (0);
+} /* }}} int unlock_file */
+
+static void *srrd_create_thread (void *targs) /* {{{ */
+{
+  srrd_create_args_t *args = targs;
+  char tmpfile[PATH_MAX];
+  int status;
+
+  status = lock_file (args->filename);
+  if (status != 0)
+  {
+    if (status == EEXIST)
+      NOTICE ("srrd_create_thread: File \"%s\" is already being created.",
+          args->filename);
+    else
+      ERROR ("srrd_create_thread: Unable to lock file \"%s\".",
+          args->filename);
+    srrd_create_args_destroy (args);
+    return (0);
+  }
+
+  ssnprintf (tmpfile, sizeof (tmpfile), "%s.async", args->filename);
+
+  status = srrd_create (tmpfile, args->pdp_step, args->last_up,
+      args->argc, (void *) args->argv);
+  if (status != 0)
+  {
+    WARNING ("srrd_create_thread: srrd_create (%s) returned status %i.",
+        args->filename, status);
+    unlink (tmpfile);
+    unlock_file (args->filename);
+    srrd_create_args_destroy (args);
+    return (0);
+  }
+
+  status = rename (tmpfile, args->filename);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("srrd_create_thread: rename (\"%s\", \"%s\") failed: %s",
+        tmpfile, args->filename,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    unlink (tmpfile);
+    unlock_file (args->filename);
+    srrd_create_args_destroy (args);
+    return (0);
+  }
+
+  DEBUG ("srrd_create_thread: Successfully created RRD file \"%s\".",
+      args->filename);
+
+  unlock_file (args->filename);
+  srrd_create_args_destroy (args);
+
+  return (0);
+} /* }}} void *srrd_create_thread */
+
+static int srrd_create_async (const char *filename, /* {{{ */
+    unsigned long pdp_step, time_t last_up,
+    int argc, const char **argv)
+{
+  srrd_create_args_t *args;
+  pthread_t thread;
+  pthread_attr_t attr;
+  int status;
+
+  DEBUG ("srrd_create_async: Creating \"%s\" in the background.", filename);
+
+  args = srrd_create_args_create (filename, pdp_step, last_up, argc, argv);
+  if (args == NULL)
+    return (-1);
+
+  status = pthread_attr_init (&attr);
+  if (status != 0)
+  {
+    srrd_create_args_destroy (args);
+    return (-1);
+  }
+
+  status = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+  if (status != 0)
+  {
+    pthread_attr_destroy (&attr);
+    srrd_create_args_destroy (args);
+    return (-1);
+  }
+
+  status = pthread_create (&thread, &attr, srrd_create_thread, args);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("srrd_create_async: pthread_create failed: %s",
+        sstrerror (status, errbuf, sizeof (errbuf)));
+    pthread_attr_destroy (&attr);
+    srrd_create_args_destroy (args);
+    return (status);
+  }
+
+  pthread_attr_destroy (&attr);
+  /* args is freed in srrd_create_thread(). */
+  return (0);
+} /* }}} int srrd_create_async */
+
 /*
  * Public functions
  */
@@ -415,24 +692,36 @@ int cu_rrd_create_file (const char *filename, /* {{{ */
   else
     stepsize = (unsigned long) CDTIME_T_TO_TIME_T (vl->interval);
 
-  status = srrd_create (filename, stepsize, last_up,
-      argc, (const char **) argv);
-
-  free (argv);
-  ds_free (ds_num, ds_def);
-  rra_free (rra_num, rra_def);
-
-  if (status != 0)
+  if (cfg->async)
   {
-    WARNING ("cu_rrd_create_file: srrd_create (%s) returned status %i.",
-        filename, status);
+    status = srrd_create_async (filename, stepsize, last_up,
+        argc, (const char **) argv);
+    if (status != 0)
+      WARNING ("cu_rrd_create_file: srrd_create_async (%s) "
+          "returned status %i.",
+          filename, status);
   }
-  else
+  else /* synchronous */
   {
-    DEBUG ("cu_rrd_create_file: Successfully created RRD file \"%s\".",
-        filename);
+    status = srrd_create (filename, stepsize, last_up,
+        argc, (const char **) argv);
+
+    if (status != 0)
+    {
+      WARNING ("cu_rrd_create_file: srrd_create (%s) returned status %i.",
+          filename, status);
+    }
+    else
+    {
+      DEBUG ("cu_rrd_create_file: Successfully created RRD file \"%s\".",
+          filename);
+    }
   }
 
+  free (argv);
+  ds_free (ds_num, ds_def);
+  rra_free (rra_num, rra_def);
+
   return (status);
 } /* }}} int cu_rrd_create_file */
 
index 103ca57..fdfd6ec 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/utils_rrdcreate.h
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008-2013  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -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>
  **/
 
 #ifndef UTILS_RRDCREATE_H
@@ -38,6 +38,8 @@ struct rrdcreate_config_s
 
   char **consolidation_functions;
   size_t consolidation_functions_num;
+
+  _Bool async;
 };
 typedef struct rrdcreate_config_s rrdcreate_config_t;
 
index 5b7551d..0b31262 100644 (file)
@@ -239,6 +239,7 @@ int cu_tail_read (cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
                        if (buf[len - 1] != '\n')
                                break;
                        buf[len - 1] = '\0';
+                       len--;
                }
 
                status = callback (data, buf, buflen);
index 8ae2208..13b518b 100644 (file)
@@ -37,6 +37,7 @@ struct cu_tail_match_simple_s
   char plugin_instance[DATA_MAX_NAME_LEN];
   char type[DATA_MAX_NAME_LEN];
   char type_instance[DATA_MAX_NAME_LEN];
+  cdtime_t interval;
 };
 typedef struct cu_tail_match_simple_s cu_tail_match_simple_t;
 
@@ -54,6 +55,7 @@ struct cu_tail_match_s
   int flags;
   cu_tail_t *tail;
 
+  cdtime_t interval;
   cu_tail_match_match_t *matches;
   size_t matches_num;
 };
@@ -88,6 +90,7 @@ static int simple_submit_match (cu_match_t *match, void *user_data)
   sstrncpy (vl.type_instance, data->type_instance,
       sizeof (vl.type_instance));
 
+  vl.interval = data->interval;
   plugin_dispatch_values (&vl);
 
   if (match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
@@ -180,6 +183,7 @@ int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
   obj->matches = temp;
   obj->matches_num++;
 
+  DEBUG ("tail_match_add_match interval %lf", CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval));
   temp = obj->matches + (obj->matches_num - 1);
 
   temp->match = match;
@@ -193,7 +197,7 @@ int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
 int tail_match_add_match_simple (cu_tail_match_t *obj,
     const char *regex, const char *excluderegex, int ds_type,
     const char *plugin, const char *plugin_instance,
-    const char *type, const char *type_instance)
+    const char *type, const char *type_instance, const cdtime_t interval)
 {
   cu_match_t *match;
   cu_tail_match_simple_t *user_data;
@@ -221,6 +225,8 @@ int tail_match_add_match_simple (cu_tail_match_t *obj,
     sstrncpy (user_data->type_instance, type_instance,
        sizeof (user_data->type_instance));
 
+  user_data->interval = interval;
+
   status = tail_match_add_match (obj, match, simple_submit_match,
       user_data, free);
 
index 7659745..abd98b6 100644 (file)
@@ -105,7 +105,7 @@ int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
 int tail_match_add_match_simple (cu_tail_match_t *obj,
     const char *regex, const char *excluderegex, int ds_type,
     const char *plugin, const char *plugin_instance,
-    const char *type, const char *type_instance);
+    const char *type, const char *type_instance, const cdtime_t interval);
 
 /*
  * NAME
diff --git a/src/utils_threshold.c b/src/utils_threshold.c
new file mode 100644 (file)
index 0000000..31685dd
--- /dev/null
@@ -0,0 +1,138 @@
+/**
+ * collectd - src/utils_threshold.c
+ * Copyright (C) 2014 Pierre-Yves Ritschard
+ *
+ * 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:
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_avltree.h"
+#include "utils_threshold.h"
+
+#include <pthread.h>
+
+/*
+ * Exported symbols
+ * {{{ */
+c_avl_tree_t   *threshold_tree = NULL;
+pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
+/* }}} */
+
+/*
+ * threshold_t *threshold_get
+ *
+ * Retrieve one specific threshold configuration. For looking up a threshold
+ * matching a value_list_t, see "threshold_search" below. Returns NULL if the
+ * specified threshold doesn't exist.
+ */
+threshold_t *threshold_get (const char *hostname,
+    const char *plugin, const char *plugin_instance,
+    const char *type, const char *type_instance)
+{ /* {{{ */
+  char name[6 * DATA_MAX_NAME_LEN];
+  threshold_t *th = NULL;
+
+  format_name (name, sizeof (name),
+      (hostname == NULL) ? "" : hostname,
+      (plugin == NULL) ? "" : plugin, plugin_instance,
+      (type == NULL) ? "" : type, type_instance);
+  name[sizeof (name) - 1] = '\0';
+
+  if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
+    return (th);
+  else
+    return (NULL);
+} /* }}} threshold_t *threshold_get */
+
+/*
+ * threshold_t *threshold_search
+ *
+ * Searches for a threshold configuration using all the possible variations of
+ * "Host", "Plugin" and "Type" blocks. Returns NULL if no threshold could be
+ * found.
+ * XXX: This is likely the least efficient function in collectd.
+ */
+threshold_t *threshold_search (const value_list_t *vl)
+{ /* {{{ */
+  threshold_t *th;
+
+  if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
+         vl->type, NULL)) != NULL)
+    return (th);
+  else if ((th = threshold_get (vl->host, vl->plugin, NULL,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get (vl->host, vl->plugin, NULL,
+         vl->type, NULL)) != NULL)
+    return (th);
+  else if ((th = threshold_get (vl->host, "", NULL,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get (vl->host, "", NULL,
+         vl->type, NULL)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
+         vl->type, NULL)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", vl->plugin, NULL,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", vl->plugin, NULL,
+         vl->type, NULL)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", "", NULL,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", "", NULL,
+         vl->type, NULL)) != NULL)
+    return (th);
+
+  return (NULL);
+} /* }}} threshold_t *threshold_search */
+
+int ut_search_threshold (const value_list_t *vl, /* {{{ */
+    threshold_t *ret_threshold)
+{
+  threshold_t *t;
+
+  if (vl == NULL)
+    return (EINVAL);
+
+       /* Is this lock really necessary? */
+       pthread_mutex_lock (&threshold_lock);
+  t = threshold_search (vl);
+  if (t == NULL) {
+               pthread_mutex_unlock (&threshold_lock);
+    return (ENOENT);
+       }
+
+  memcpy (ret_threshold, t, sizeof (*ret_threshold));
+       pthread_mutex_unlock (&threshold_lock);
+
+  ret_threshold->next = NULL;
+
+  return (0);
+} /* }}} int ut_search_threshold */
+
+
diff --git a/src/utils_threshold.h b/src/utils_threshold.h
new file mode 100644 (file)
index 0000000..948f807
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * collectd - src/utils_threshold.h
+ * Copyright (C) 2014 Pierre-Yves Ritschard
+ *
+ * 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:
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ **/
+
+#ifndef UTILS_THRESHOLD_H
+#define UTILS_THRESHOLD_H 1
+
+#define UT_FLAG_INVERT  0x01
+#define UT_FLAG_PERSIST 0x02
+#define UT_FLAG_PERCENTAGE 0x04
+#define UT_FLAG_INTERESTING 0x08
+#define UT_FLAG_PERSIST_OK 0x10
+typedef struct threshold_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];
+  char data_source[DATA_MAX_NAME_LEN];
+  gauge_t warning_min;
+  gauge_t warning_max;
+  gauge_t failure_min;
+  gauge_t failure_max;
+  gauge_t hysteresis;
+  unsigned int flags;
+  int hits;
+  struct threshold_s *next;
+} threshold_t;
+
+extern c_avl_tree_t   *threshold_tree;
+extern pthread_mutex_t threshold_lock;
+
+threshold_t *threshold_get (const char *hostname,
+    const char *plugin, const char *plugin_instance,
+    const char *type, const char *type_instance);
+
+threshold_t *threshold_search (const value_list_t *vl);
+
+int ut_search_threshold (const value_list_t *vl, 
+  threshold_t *ret_threshold);
+
+#endif /* UTILS_THRESHOLD_H */
+
+/* vim: set sw=2 sts=2 ts=8 : */
index 2dada24..722c452 100644 (file)
@@ -25,6 +25,9 @@
  **/
 
 #include "collectd.h"
+
+#include <regex.h>
+
 #include "common.h"
 #include "utils_vl_lookup.h"
 #include "utils_avltree.h"
 /*
  * Types
  */
+struct part_match_s
+{
+  char str[DATA_MAX_NAME_LEN];
+  regex_t regex;
+  _Bool is_regex;
+};
+typedef struct part_match_s part_match_t;
+
+struct identifier_match_s
+{
+  part_match_t host;
+  part_match_t plugin;
+  part_match_t plugin_instance;
+  part_match_t type;
+  part_match_t type_instance;
+
+  unsigned int group_by;
+};
+typedef struct identifier_match_s identifier_match_t;
+
 struct lookup_s
 {
   c_avl_tree_t *by_type_tree;
@@ -64,7 +87,7 @@ struct user_obj_s
 struct user_class_s
 {
   void *user_class;
-  identifier_t ident;
+  identifier_match_t match;
   user_obj_t *user_obj_list; /* list of user_obj */
 };
 typedef struct user_class_s user_class_t;
@@ -87,6 +110,87 @@ typedef struct by_type_entry_s by_type_entry_t;
 /*
  * Private functions
  */
+static _Bool lu_part_matches (part_match_t const *match, /* {{{ */
+    char const *str)
+{
+  if (match->is_regex)
+  {
+    /* Short cut popular catch-all regex. */
+    if (strcmp (".*", match->str) == 0)
+      return (1);
+
+    int status = regexec (&match->regex, str,
+        /* nmatch = */ 0, /* pmatch = */ NULL,
+        /* flags = */ 0);
+    if (status == 0)
+      return (1);
+    else
+      return (0);
+  }
+  else if (strcmp (match->str, str) == 0)
+    return (1);
+  else
+    return (0);
+} /* }}} _Bool lu_part_matches */
+
+static int lu_copy_ident_to_match_part (part_match_t *match_part, /* {{{ */
+    char const *ident_part)
+{
+  size_t len = strlen (ident_part);
+  int status;
+
+  if ((len < 3) || (ident_part[0] != '/') || (ident_part[len - 1] != '/'))
+  {
+    sstrncpy (match_part->str, ident_part, sizeof (match_part->str));
+    match_part->is_regex = 0;
+    return (0);
+  }
+
+  /* Copy string without the leading slash. */
+  sstrncpy (match_part->str, ident_part + 1, sizeof (match_part->str));
+  assert (sizeof (match_part->str) > len);
+  /* strip trailing slash */
+  match_part->str[len - 2] = 0;
+  
+  status = regcomp (&match_part->regex, match_part->str,
+      /* flags = */ REG_EXTENDED);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    regerror (status, &match_part->regex, errbuf, sizeof (errbuf));
+    ERROR ("utils_vl_lookup: Compiling regular expression \"%s\" failed: %s",
+        match_part->str, errbuf);
+    return (EINVAL);
+  }
+  match_part->is_regex = 1;
+  
+  return (0);
+} /* }}} int lu_copy_ident_to_match_part */
+
+static int lu_copy_ident_to_match (identifier_match_t *match, /* {{{ */
+    identifier_t const *ident, unsigned int group_by)
+{
+  memset (match, 0, sizeof (*match));
+
+  match->group_by = group_by;
+
+#define COPY_FIELD(field) do { \
+  int status = lu_copy_ident_to_match_part (&match->field, ident->field); \
+  if (status != 0) \
+    return (status); \
+} while (0)
+
+  COPY_FIELD (host);
+  COPY_FIELD (plugin);
+  COPY_FIELD (plugin_instance);
+  COPY_FIELD (type);
+  COPY_FIELD (type_instance);
+
+#undef COPY_FIELD
+
+  return (0);
+} /* }}} int lu_copy_ident_to_match */
+
 static void *lu_create_user_obj (lookup_t *obj, /* {{{ */
     data_set_t const *ds, value_list_t const *vl,
     user_class_t *user_class)
@@ -110,21 +214,21 @@ static void *lu_create_user_obj (lookup_t *obj, /* {{{ */
     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));
+#define COPY_FIELD(field, group_mask) do { \
+  if (user_class->match.field.is_regex \
+      && ((user_class->match.group_by & group_mask) == 0)) \
+    sstrncpy (user_obj->ident.field, "/.*/", sizeof (user_obj->ident.field)); \
+  else \
+    sstrncpy (user_obj->ident.field, vl->field, sizeof (user_obj->ident.field)); \
+} while (0)
+
+  COPY_FIELD (host, LU_GROUP_BY_HOST);
+  COPY_FIELD (plugin, LU_GROUP_BY_PLUGIN);
+  COPY_FIELD (plugin_instance, LU_GROUP_BY_PLUGIN_INSTANCE);
+  COPY_FIELD (type, 0);
+  COPY_FIELD (type_instance, LU_GROUP_BY_TYPE_INSTANCE);
+
+#undef COPY_FIELD
 
   if (user_class->user_obj_list == NULL)
   {
@@ -150,14 +254,21 @@ static user_obj_t *lu_find_user_obj (user_class_t *user_class, /* {{{ */
       ptr != NULL;
       ptr = ptr->next)
   {
-    if (!LU_IS_ALL (ptr->ident.host)
-        && (strcmp (ptr->ident.host, vl->host) != 0))
+    if (user_class->match.host.is_regex
+        && (user_class->match.group_by & LU_GROUP_BY_HOST)
+        && (strcmp (vl->host, ptr->ident.host) != 0))
+      continue;
+    if (user_class->match.plugin.is_regex
+        && (user_class->match.group_by & LU_GROUP_BY_PLUGIN)
+        && (strcmp (vl->plugin, ptr->ident.plugin) != 0))
       continue;
-    if (!LU_IS_ALL (ptr->ident.plugin_instance)
-        && (strcmp (ptr->ident.plugin_instance, vl->plugin_instance) != 0))
+    if (user_class->match.plugin_instance.is_regex
+        && (user_class->match.group_by & LU_GROUP_BY_PLUGIN_INSTANCE)
+        && (strcmp (vl->plugin_instance, ptr->ident.plugin_instance) != 0))
       continue;
-    if (!LU_IS_ALL (ptr->ident.type_instance)
-        && (strcmp (ptr->ident.type_instance, vl->type_instance) != 0))
+    if (user_class->match.type_instance.is_regex
+        && (user_class->match.group_by & LU_GROUP_BY_TYPE_INSTANCE)
+        && (strcmp (vl->type_instance, ptr->ident.type_instance) != 0))
       continue;
 
     return (ptr);
@@ -173,21 +284,14 @@ static int lu_handle_user_class (lookup_t *obj, /* {{{ */
   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));
+  assert (strcmp (vl->type, user_class->match.type.str) == 0);
+  assert (user_class->match.plugin.is_regex
+      || (strcmp (vl->plugin, user_class->match.plugin.str)) == 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))
+  if (!lu_part_matches (&user_class->match.type_instance, vl->type_instance)
+      || !lu_part_matches (&user_class->match.plugin_instance, vl->plugin_instance)
+      || !lu_part_matches (&user_class->match.plugin, vl->plugin)
+      || !lu_part_matches (&user_class->match.host, vl->host))
     return (1);
 
   user_obj = lu_find_user_obj (user_class, vl);
@@ -292,14 +396,15 @@ static by_type_entry_t *lu_search_by_type (lookup_t *obj, /* {{{ */
 } /* }}} 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 *user_class_list)
 {
   user_class_list_t *ptr = NULL;
+  identifier_match_t const *match = &user_class_list->entry.match;
 
   /* 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 (match->plugin.is_regex)
   {
     if (by_type->wildcard_plugin_list == NULL)
     {
@@ -314,11 +419,11 @@ static int lu_add_by_plugin (by_type_entry_t *by_type, /* {{{ */
     int status;
 
     status = c_avl_get (by_type->by_plugin_tree,
-        ident->plugin, (void *) &ptr);
+        match->plugin.str, (void *) &ptr);
 
     if (status != 0) /* plugin not yet in tree */
     {
-      char *plugin_copy = strdup (ident->plugin);
+      char *plugin_copy = strdup (match->plugin.str);
 
       if (plugin_copy == NULL)
       {
@@ -478,7 +583,7 @@ void lookup_destroy (lookup_t *obj) /* {{{ */
 } /* }}} void lookup_destroy */
 
 int lookup_add (lookup_t *obj, /* {{{ */
-    identifier_t const *ident, void *user_class)
+    identifier_t const *ident, unsigned int group_by, void *user_class)
 {
   by_type_entry_t *by_type = NULL;
   user_class_list_t *user_class_obj;
@@ -495,11 +600,11 @@ int lookup_add (lookup_t *obj, /* {{{ */
   }
   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));
+  lu_copy_ident_to_match (&user_class_obj->entry.match, ident, group_by);
   user_class_obj->entry.user_obj_list = NULL;
   user_class_obj->next = NULL;
 
-  return (lu_add_by_plugin (by_type, ident, user_class_obj));
+  return (lu_add_by_plugin (by_type, user_class_obj));
 } /* }}} int lookup_add */
 
 /* returns the number of successful calls to the callback function */
index c006fc7..31787f5 100644 (file)
@@ -63,12 +63,11 @@ struct identifier_s
 };
 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))
+#define LU_GROUP_BY_HOST            0x01
+#define LU_GROUP_BY_PLUGIN          0x02
+#define LU_GROUP_BY_PLUGIN_INSTANCE 0x04
+/* #define LU_GROUP_BY_TYPE            0x00 */
+#define LU_GROUP_BY_TYPE_INSTANCE   0x10
 
 /*
  * Functions
@@ -81,7 +80,7 @@ lookup_t *lookup_create (lookup_class_callback_t,
 void lookup_destroy (lookup_t *obj);
 
 int lookup_add (lookup_t *obj,
-    identifier_t const *ident, void *user_class);
+    identifier_t const *ident, unsigned int group_by, void *user_class);
 
 /* TODO(octo): Pass lookup_obj_callback_t to lookup_search()? */
 int lookup_search (lookup_t *obj,
diff --git a/src/utils_vl_lookup_test.c b/src/utils_vl_lookup_test.c
deleted file mode 100644 (file)
index 6265b32..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-/**
- * 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 cf23f5b..2df6063 100644 (file)
 #define UUID_PRINTABLE_COMPACT_LENGTH  (UUID_RAW_LENGTH * 2)
 #define UUID_PRINTABLE_NORMAL_LENGTH  (UUID_PRINTABLE_COMPACT_LENGTH + 4)
 
-#define HANDLE_PREFIX "Handle"
-#define SYSINFO_PREFIX "System Information"
-#define ALT_SYSINFO_PREFIX "\tSystem Information"
-#define UUID_PREFIX "\tUUID:"
-#define ALT_UUID_PREFIX "\t\tUUID:"
+static char *uuidfile = NULL;
+
+static const char *config_keys[] = {
+    "UUIDFile"
+};
 
 static int
 looks_like_a_uuid (const char *uuid)
@@ -65,42 +65,28 @@ static char *
 uuid_parse_dmidecode(FILE *file)
 {
     char line[1024];
-    int inSysInfo = 0;
 
-    for (;;) {
-        if (!fgets(line, sizeof(line)/sizeof(char), file)) {
-            return NULL;
-        }
-        if (strncmp(line, HANDLE_PREFIX,
-                    (sizeof(HANDLE_PREFIX)/sizeof(char))-1) == 0) {
-            /*printf("Got handle %s\n", line);*/
-            inSysInfo = 0;
-        } else if (strncmp(line, SYSINFO_PREFIX,
-                           (sizeof(SYSINFO_PREFIX)/sizeof(char))-1) == 0) {
-            /*printf("Got system info %s\n", line);*/
-            inSysInfo = 1;
-        } else if (strncmp(line, ALT_SYSINFO_PREFIX,
-                           (sizeof(ALT_SYSINFO_PREFIX)/sizeof(char))-1) == 0) {
-            /*printf("Got alt system info %s\n", line);*/
-            inSysInfo = 1;
-        }
-        
-        if (inSysInfo) {
-            if (strncmp(line, UUID_PREFIX,
-                        (sizeof(UUID_PREFIX)/sizeof(char))-1) == 0) {
-                char *uuid = line + (sizeof(UUID_PREFIX)/sizeof(char));
-                /*printf("Got uuid [%s]\n", uuid);*/
-                if (looks_like_a_uuid (uuid))
-                    return strdup (uuid);
-            }
-            if (strncmp(line, ALT_UUID_PREFIX,
-                        (sizeof(ALT_UUID_PREFIX)/sizeof(char))-1) == 0) {
-                char *uuid = line + (sizeof(ALT_UUID_PREFIX)/sizeof(char));
-                /*printf("Got alt uuid [%s]\n", uuid);*/
-                if (looks_like_a_uuid (uuid))
-                    return strdup (uuid);
-            }
-        }
+    while (fgets (line, sizeof (line), file) != NULL)
+    {
+        char *fields[4];
+        int fields_num;
+
+        strstripnewline (line);
+
+        /* Look for a line reading:
+         *   UUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
+         */
+        fields_num = strsplit (line, fields, STATIC_ARRAY_SIZE (fields));
+        if (fields_num != 2)
+            continue;
+
+        if (strcmp("UUID:", fields[0]) != 0)
+            continue;
+
+        if (!looks_like_a_uuid (fields[1]))
+            continue;
+
+        return strdup (fields[1]);
     }
     return NULL;
 }
@@ -187,23 +173,22 @@ static char *
 uuid_get_from_file(const char *path)
 {
     FILE *file;
-    char uuid[UUID_PRINTABLE_NORMAL_LENGTH+1];
+    char uuid[UUID_PRINTABLE_NORMAL_LENGTH + 1] = "";
 
-    if (!(file = fopen(path, "r"))) {
+    file = fopen (path, "r");
+    if (file == NULL)
         return NULL;
-    }
 
     if (!fgets(uuid, sizeof(uuid), file)) {
         fclose(file);
         return NULL;
     }
     fclose(file);
+    strstripnewline (uuid);
 
     return strdup (uuid);
 }
 
-static char *uuidfile = NULL;
-
 static char *
 uuid_get_local(void)
 {
@@ -231,23 +216,19 @@ uuid_get_local(void)
     return NULL;
 }
 
-static const char *config_keys[] = {
-    "UUIDFile",
-    NULL
-};
-#define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
-
 static int
 uuid_config (const char *key, const char *value)
 {
     if (strcasecmp (key, "UUIDFile") == 0) {
-        if (uuidfile) {
-            ERROR ("UUIDFile given twice in configuration file");
-            return 1;
-        }
-        uuidfile = strdup (value);
-        return 0;
+        char *tmp = strdup (value);
+        if (tmp == NULL)
+            return -1;
+        sfree (uuidfile);
+        uuidfile = tmp;
+    } else {
+        return 1;
     }
+
     return 0;
 }
 
@@ -268,9 +249,9 @@ uuid_init (void)
 
 void module_register (void)
 {
-       plugin_register_config ("uuid", uuid_config,
-                            config_keys, NR_CONFIG_KEYS);
-       plugin_register_init ("uuid", uuid_init);
+    plugin_register_config ("uuid", uuid_config,
+            config_keys, STATIC_ARRAY_SIZE (config_keys));
+    plugin_register_init ("uuid", uuid_init);
 }
 
 /*
index de60e39..3a8dddd 100644 (file)
@@ -1,8 +1,8 @@
 /**
  * collectd - src/varnish.c
- * Copyright (C) 2010 Jérôme Renard
- * Copyright (C) 2010 Marc Fournier
- * Copyright (C) 2010 Florian Forster
+ * Copyright (C) 2010      Jérôme Renard
+ * Copyright (C) 2010      Marc Fournier
+ * Copyright (C) 2010-2012 Florian Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Authors:
  *   Jérôme Renard <jerome.renard at gmail.com>
  *   Marc Fournier <marc.fournier at camptocamp.com>
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
-/**
- * Current list of what is monitored and what is not monitored (yet)
- * {{{
- * Field name           Description                           Monitored
- * ----------           -----------                           ---------
- * uptime               Child uptime                              N
- * client_conn          Client connections accepted               Y
- * client_drop          Connection dropped, no sess               Y
- * client_req           Client requests received                  Y
- * cache_hit            Cache hits                                Y
- * cache_hitpass        Cache hits for pass                       Y
- * cache_miss           Cache misses                              Y
- * backend_conn         Backend conn. success                     Y
- * backend_unhealthy    Backend conn. not attempted               Y
- * backend_busy         Backend conn. too many                    Y
- * backend_fail         Backend conn. failures                    Y
- * backend_reuse        Backend conn. reuses                      Y
- * backend_toolate      Backend conn. was closed                  Y
- * backend_recycle      Backend conn. recycles                    Y
- * backend_unused       Backend conn. unused                      Y
- * fetch_head           Fetch head                                Y
- * fetch_length         Fetch with Length                         Y
- * fetch_chunked        Fetch chunked                             Y
- * fetch_eof            Fetch EOF                                 Y
- * fetch_bad            Fetch had bad headers                     Y
- * fetch_close          Fetch wanted close                        Y
- * fetch_oldhttp        Fetch pre HTTP/1.1 closed                 Y
- * fetch_zero           Fetch zero len                            Y
- * fetch_failed         Fetch failed                              Y
- * n_sess_mem           N struct sess_mem                         N
- * n_sess               N struct sess                             N
- * n_object             N struct object                           N
- * n_vampireobject      N unresurrected objects                   N
- * n_objectcore         N struct objectcore                       N
- * n_objecthead         N struct objecthead                       N
- * n_smf                N struct smf                              N
- * n_smf_frag           N small free smf                          N
- * n_smf_large          N large free smf                          N
- * n_vbe_conn           N struct vbe_conn                         N
- * n_wrk                N worker threads                          Y
- * n_wrk_create         N worker threads created                  Y
- * n_wrk_failed         N worker threads not created              Y
- * n_wrk_max            N worker threads limited                  Y
- * n_wrk_queue          N queued work requests                    Y
- * n_wrk_overflow       N overflowed work requests                Y
- * n_wrk_drop           N dropped work requests                   Y
- * n_backend            N backends                                N
- * n_expired            N expired objects                         N
- * n_lru_nuked          N LRU nuked objects                       N
- * n_lru_saved          N LRU saved objects                       N
- * n_lru_moved          N LRU moved objects                       N
- * n_deathrow           N objects on deathrow                     N
- * losthdr              HTTP header overflows                     N
- * n_objsendfile        Objects sent with sendfile                N
- * n_objwrite           Objects sent with write                   N
- * n_objoverflow        Objects overflowing workspace             N
- * s_sess               Total Sessions                            Y
- * s_req                Total Requests                            Y
- * s_pipe               Total pipe                                Y
- * s_pass               Total pass                                Y
- * s_fetch              Total fetch                               Y
- * s_hdrbytes           Total header bytes                        Y
- * s_bodybytes          Total body bytes                          Y
- * sess_closed          Session Closed                            N
- * sess_pipeline        Session Pipeline                          N
- * sess_readahead       Session Read Ahead                        N
- * sess_linger          Session Linger                            N
- * sess_herd            Session herd                              N
- * shm_records          SHM records                               Y
- * shm_writes           SHM writes                                Y
- * shm_flushes          SHM flushes due to overflow               Y
- * shm_cont             SHM MTX contention                        Y
- * shm_cycles           SHM cycles through buffer                 Y
- * sm_nreq              allocator requests                        Y
- * sm_nobj              outstanding allocations                   Y
- * sm_balloc            bytes allocated                           Y
- * sm_bfree             bytes free                                Y
- * sma_nreq             SMA allocator requests                    Y
- * sma_nobj             SMA outstanding allocations               Y
- * sma_nbytes           SMA outstanding bytes                     Y
- * sma_balloc           SMA bytes allocated                       Y
- * sma_bfree            SMA bytes free                            Y
- * sms_nreq             SMS allocator requests                    Y
- * sms_nobj             SMS outstanding allocations               Y
- * sms_nbytes           SMS outstanding bytes                     Y
- * sms_balloc           SMS bytes allocated                       Y
- * sms_bfree            SMS bytes freed                           Y
- * backend_req          Backend requests made                     N
- * n_vcl                N vcl total                               N
- * n_vcl_avail          N vcl available                           N
- * n_vcl_discard        N vcl discarded                           N
- * n_purge              N total active purges                     N
- * n_purge_add          N new purges added                        N
- * n_purge_retire       N old purges deleted                      N
- * n_purge_obj_test     N objects tested                          N
- * n_purge_re_test      N regexps tested against                  N
- * n_purge_dups         N duplicate purges removed                N
- * hcb_nolock           HCB Lookups without lock                  Y
- * hcb_lock             HCB Lookups with lock                     Y
- * hcb_insert           HCB Inserts                               Y
- * esi_parse            Objects ESI parsed (unlock)               Y
- * esi_errors           ESI parse errors (unlock)                 Y
- * }}}
- */
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
@@ -151,15 +47,30 @@ struct user_config_s {
        _Bool collect_connections;
        _Bool collect_esi;
        _Bool collect_backend;
+#ifdef HAVE_VARNISH_V3
+       _Bool collect_dirdns;
+#endif
        _Bool collect_fetch;
        _Bool collect_hcb;
+       _Bool collect_objects;
+#if HAVE_VARNISH_V2
+       _Bool collect_purge;
+#else
+       _Bool collect_ban;
+#endif
+       _Bool collect_session;
        _Bool collect_shm;
        _Bool collect_sms;
 #if HAVE_VARNISH_V2
        _Bool collect_sm;
        _Bool collect_sma;
 #endif
+       _Bool collect_struct;
        _Bool collect_totals;
+#ifdef HAVE_VARNISH_V3
+       _Bool collect_uptime;
+#endif
+       _Bool collect_vcl;
        _Bool collect_workers;
 };
 typedef struct user_config_s user_config_t; /* }}} */
@@ -238,13 +149,30 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance, "connections", "connections", "received", stats->client_req);
        }
 
+#ifdef HAVE_VARNISH_V3
+       if (conf->collect_dirdns)
+       {
+               /* DNS director lookups */
+               varnish_submit_derive (conf->instance, "dirdns", "cache_operation", "lookups",    stats->dir_dns_lookups);
+               /* DNS director failed lookups */
+               varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "failed",     stats->dir_dns_failed);
+               /* DNS director cached lookups hit */
+               varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "hits",       stats->dir_dns_hit);
+               /* DNS director full dnscache */
+               varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "cache_full", stats->dir_dns_cache_full);
+       }
+#endif
+
        if (conf->collect_esi)
        {
                /* ESI parse errors (unlock)   */
-               varnish_submit_derive (conf->instance, "esi", "total_operations", "error",  stats->esi_errors);
+               varnish_submit_derive (conf->instance, "esi", "total_operations", "error",   stats->esi_errors);
 #if HAVE_VARNISH_V2
                /* Objects ESI parsed (unlock) */
-               varnish_submit_derive (conf->instance, "esi", "total_operations", "parsed", stats->esi_parse);
+               varnish_submit_derive (conf->instance, "esi", "total_operations", "parsed",  stats->esi_parse);
+#else
+               /* ESI parse warnings (unlock) */
+               varnish_submit_derive (conf->instance, "esi", "total_operations", "warning", stats->esi_warnings);
 #endif
        }
 
@@ -267,7 +195,14 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
 #if HAVE_VARNISH_V2
                /* Backend conn. unused        */
                varnish_submit_derive (conf->instance, "backend", "connections", "unused"       , stats->backend_unused);
+#else
+               /* Backend conn. retry         */
+               varnish_submit_derive (conf->instance, "backend", "connections", "retries"      , stats->backend_retry);
 #endif
+               /* Backend requests mades      */
+               varnish_submit_derive (conf->instance, "backend", "http_requests", "requests"   , stats->backend_req);
+               /* N backends                  */
+               varnish_submit_gauge  (conf->instance, "backend", "backends", "n_backends"      , stats->n_backend);
        }
 
        if (conf->collect_fetch)
@@ -290,6 +225,14 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance, "fetch", "http_requests", "zero"       , stats->fetch_zero);
                /* Fetch failed              */
                varnish_submit_derive (conf->instance, "fetch", "http_requests", "failed"     , stats->fetch_failed);
+#if HAVE_VARNISH_V3
+               /* Fetch no body (1xx)       */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_1xx", stats->fetch_1xx);
+               /* Fetch no body (204)       */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_204", stats->fetch_204);
+               /* Fetch no body (304)       */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_304", stats->fetch_304);
+#endif
        }
 
        if (conf->collect_hcb)
@@ -302,6 +245,80 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance, "hcb", "cache_operation", "insert",        stats->hcb_insert);
        }
 
+       if (conf->collect_objects)
+       {
+               /* N expired objects             */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "expired",            stats->n_expired);
+               /* N LRU nuked objects           */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_nuked",          stats->n_lru_nuked);
+#if HAVE_VARNISH_V2
+               /* N LRU saved objects           */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_saved",          stats->n_lru_saved);
+#endif
+               /* N LRU moved objects           */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_moved",          stats->n_lru_moved);
+#if HAVE_VARNISH_V2
+               /* N objects on deathrow         */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "deathrow",           stats->n_deathrow);
+#endif
+               /* HTTP header overflows         */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "header_overflow",    stats->losthdr);
+               /* Objects sent with sendfile    */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "sent_sendfile",      stats->n_objsendfile);
+               /* Objects sent with write       */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "sent_write",         stats->n_objwrite);
+               /* Objects overflowing workspace */
+               varnish_submit_derive (conf->instance, "objects", "total_objects", "workspace_overflow", stats->n_objoverflow);
+       }
+
+#if HAVE_VARNISH_V2
+       if (conf->collect_purge)
+       {
+               /* N total active purges      */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "total",            stats->n_purge);
+               /* N new purges added         */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "added",            stats->n_purge_add);
+               /* N old purges deleted       */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "deleted",          stats->n_purge_retire);
+               /* N objects tested           */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "objects_tested",   stats->n_purge_obj_test);
+               /* N regexps tested against   */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "regexps_tested",   stats->n_purge_re_test);
+               /* N duplicate purges removed */
+               varnish_submit_derive (conf->instance, "purge", "total_operations", "duplicate",        stats->n_purge_dups);
+       }
+#else
+       if (conf->collect_ban)
+       {
+               /* N total active bans      */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "total",          stats->n_ban);
+               /* N new bans added         */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "added",          stats->n_ban_add);
+               /* N old bans deleted       */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "deleted",        stats->n_ban_retire);
+               /* N objects tested         */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "objects_tested", stats->n_ban_obj_test);
+               /* N regexps tested against */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "regexps_tested", stats->n_ban_re_test);
+               /* N duplicate bans removed */
+               varnish_submit_derive (conf->instance, "ban", "total_operations", "duplicate",      stats->n_ban_dups);
+       }
+#endif
+
+       if (conf->collect_session)
+       {
+               /* Session Closed     */
+               varnish_submit_derive (conf->instance, "session", "total_operations", "closed",    stats->sess_closed);
+               /* Session Pipeline   */
+               varnish_submit_derive (conf->instance, "session", "total_operations", "pipeline",  stats->sess_pipeline);
+               /* Session Read Ahead */
+               varnish_submit_derive (conf->instance, "session", "total_operations", "readahead", stats->sess_readahead);
+               /* Session Linger     */
+               varnish_submit_derive (conf->instance, "session", "total_operations", "linger",    stats->sess_linger);
+               /* Session herd       */
+               varnish_submit_derive (conf->instance, "session", "total_operations", "herd",      stats->sess_herd);
+       }
+
        if (conf->collect_shm)
        {
                /* SHM records                 */
@@ -358,6 +375,34 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance,  "sms", "total_bytes", "free",        stats->sms_bfree);
        }
 
+       if (conf->collect_struct)
+       {
+               /* N struct sess_mem       */
+               varnish_submit_gauge (conf->instance, "struct", "current_sessions", "sess_mem",  stats->n_sess_mem);
+               /* N struct sess           */
+               varnish_submit_gauge (conf->instance, "struct", "current_sessions", "sess",      stats->n_sess);
+               /* N struct object         */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "object",             stats->n_object);
+#ifdef HAVE_VARNISH_V3
+               /* N unresurrected objects */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "vampireobject",      stats->n_vampireobject);
+               /* N struct objectcore     */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "objectcore",         stats->n_objectcore);
+#endif
+               /* N struct objecthead     */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "objecthead",         stats->n_objecthead);
+#ifdef HAVE_VARNISH_V2
+               /* N struct smf            */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "smf",                stats->n_smf);
+               /* N small free smf         */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "smf_frag",           stats->n_smf_frag);
+               /* N large free smf         */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "smf_large",          stats->n_smf_large);
+               /* N struct vbe_conn        */
+               varnish_submit_gauge (conf->instance, "struct", "objects", "vbe_conn",           stats->n_vbe_conn);
+#endif
+       }
+
        if (conf->collect_totals)
        {
                /* Total Sessions */
@@ -376,6 +421,24 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance, "totals", "total_bytes", "body-bytes",   stats->s_bodybytes);
        }
 
+#ifdef HAVE_VARNISH_V3
+       if (conf->collect_uptime)
+       {
+               /* Client uptime */
+               varnish_submit_gauge (conf->instance, "uptime", "uptime", "client_uptime", stats->uptime);
+       }
+#endif
+
+       if (conf->collect_vcl)
+       {
+               /* N vcl total     */
+               varnish_submit_gauge (conf->instance, "vcl", "vcl", "total_vcl",     stats->n_vcl);
+               /* N vcl available */
+               varnish_submit_gauge (conf->instance, "vcl", "vcl", "avail_vcl",     stats->n_vcl_avail);
+               /* N vcl discarded */
+               varnish_submit_gauge (conf->instance, "vcl", "vcl", "discarded_vcl", stats->n_vcl_discard);
+       }
+
        if (conf->collect_workers)
        {
                /* worker threads */
@@ -393,6 +456,11 @@ static void varnish_monitor (const user_config_t *conf, /* {{{ */
                varnish_submit_derive (conf->instance, "workers", "total_requests", "queued",     stats->n_wrk_queue);
                /* overflowed work requests */
                varnish_submit_derive (conf->instance, "workers", "total_requests", "overflowed", stats->n_wrk_overflow);
+#else
+               /* queued work requests */
+               varnish_submit_derive (conf->instance, "workers", "total_requests", "queued",       stats->n_wrk_queued);
+               /* work request queue length */
+               varnish_submit_derive (conf->instance, "workers", "total_requests", "queue_length", stats->n_wrk_lqueue);
 #endif
        }
 } /* }}} void varnish_monitor */
@@ -412,11 +480,21 @@ static int varnish_read (user_data_t *ud) /* {{{ */
 
        vd = VSM_New();
        VSC_Setup(vd);
-       if (VSM_n_Arg(vd, conf->instance) == -1)
+
+       if (conf->instance != NULL)
        {
-               ERROR ("Varnish plugin : unable to load statistics from instance");
-               return (-1);
+               int status;
+
+               status = VSM_n_Arg (vd, conf->instance);
+               if (status < 0)
+               {
+                       ERROR ("varnish plugin: VSM_n_Arg (\"%s\") failed "
+                                       "with status %i.",
+                                       conf->instance, status);
+                       return (-1);
+               }
        }
+
        if (VSC_Open (vd, /* diag = */ 1))
        {
                ERROR ("varnish plugin: Unable to load statistics.");
@@ -476,16 +554,32 @@ static int varnish_config_apply_default (user_config_t *conf) /* {{{ */
        conf->collect_backend     = 1;
        conf->collect_cache       = 1;
        conf->collect_connections = 1;
+#ifdef HAVE_VARNISH_V3
+       conf->collect_dirdns      = 0;
+#endif
        conf->collect_esi         = 0;
        conf->collect_fetch       = 0;
        conf->collect_hcb         = 0;
+       conf->collect_objects     = 0;
+#if HAVE_VARNISH_V2
+       conf->collect_purge       = 0;
+#else
+       conf->collect_ban         = 0;
+#endif
+       conf->collect_session     = 0;
        conf->collect_shm         = 1;
 #if HAVE_VARNISH_V2
        conf->collect_sm          = 0;
        conf->collect_sma         = 0;
 #endif
        conf->collect_sms         = 0;
+       conf->collect_struct      = 0;
        conf->collect_totals      = 0;
+#ifdef HAVE_VARNISH_V3
+       conf->collect_uptime      = 0;
+#endif
+       conf->collect_vcl         = 0;
+       conf->collect_workers     = 0;
 
        return (0);
 } /* }}} int varnish_config_apply_default */
@@ -570,12 +664,27 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                        cf_util_get_boolean (child, &conf->collect_connections);
                else if (strcasecmp ("CollectESI", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_esi);
+#ifdef HAVE_VARNISH_V3
+               else if (strcasecmp ("CollectDirectorDNS", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_dirdns);
+#endif
                else if (strcasecmp ("CollectBackend", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_backend);
                else if (strcasecmp ("CollectFetch", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_fetch);
                else if (strcasecmp ("CollectHCB", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_hcb);
+               else if (strcasecmp ("CollectObjects", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_objects);
+#if HAVE_VARNISH_V2
+               else if (strcasecmp ("CollectPurge", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_purge);
+#else
+               else if (strcasecmp ("CollectBan", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_ban);
+#endif
+               else if (strcasecmp ("CollectSession", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_session);
                else if (strcasecmp ("CollectSHM", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_shm);
                else if (strcasecmp ("CollectSMS", child->key) == 0)
@@ -586,14 +695,24 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                else if (strcasecmp ("CollectSM", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_sm);
 #endif
+               else if (strcasecmp ("CollectStruct", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_struct);
                else if (strcasecmp ("CollectTotals", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_totals);
+#ifdef HAVE_VARNISH_V3
+               else if (strcasecmp ("CollectUptime", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_uptime);
+#endif
+               else if (strcasecmp ("CollectVCL", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_vcl);
                else if (strcasecmp ("CollectWorkers", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_workers);
                else
                {
                        WARNING ("Varnish plugin: Ignoring unknown "
-                                       "configuration option: \"%s\"",
+                                       "configuration option: \"%s\". Did "
+                                       "you forget to add an <Instance /> "
+                                       "block around the configuration?",
                                        child->key);
                }
        }
@@ -602,15 +721,30 @@ static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
                        && !conf->collect_connections
                        && !conf->collect_esi
                        && !conf->collect_backend
+#ifdef HAVE_VARNISH_V3
+                       && !conf->collect_dirdns
+#endif
                        && !conf->collect_fetch
                        && !conf->collect_hcb
+                       && !conf->collect_objects
+#if HAVE_VARNISH_V2
+                       && !conf->collect_purge
+#else
+                       && !conf->collect_ban
+#endif
+                       && !conf->collect_session
                        && !conf->collect_shm
                        && !conf->collect_sms
 #if HAVE_VARNISH_V2
                        && !conf->collect_sma
                        && !conf->collect_sm
 #endif
+                       && !conf->collect_struct
                        && !conf->collect_totals
+#ifdef HAVE_VARNISH_V3
+                       && !conf->collect_uptime
+#endif
+                       && !conf->collect_vcl
                        && !conf->collect_workers)
        {
                WARNING ("Varnish plugin: No metric has been configured for "
index 2ae30ef..c17b7f3 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2011       Scott Sanders
  * Copyright (C) 2009       Paul Sadauskas
  * Copyright (C) 2009       Doug MacEachern
- * Copyright (C) 2007-2012  Florian octo Forster
+ * Copyright (C) 2007-2013  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -35,6 +35,8 @@
   *   <Carbon>
   *     Host "localhost"
   *     Port "2003"
+  *     Protocol "udp"
+  *     LogSendErrors true
   *     Prefix "collectd"
   *   </Carbon>
   * </Plugin>
@@ -46,6 +48,7 @@
 #include "configfile.h"
 
 #include "utils_cache.h"
+#include "utils_complain.h"
 #include "utils_parse_option.h"
 #include "utils_format_graphite.h"
 
 # define WG_DEFAULT_SERVICE "2003"
 #endif
 
+#ifndef WG_DEFAULT_PROTOCOL
+# define WG_DEFAULT_PROTOCOL "tcp"
+#endif
+
+#ifndef WG_DEFAULT_LOG_SEND_ERRORS
+# define WG_DEFAULT_LOG_SEND_ERRORS 1
+#endif
+
 #ifndef WG_DEFAULT_ESCAPE
 # define WG_DEFAULT_ESCAPE '_'
 #endif
@@ -79,15 +90,17 @@ struct wg_callback
 {
     int      sock_fd;
 
+    char    *name;
+
     char    *node;
     char    *service;
+    char    *protocol;
+    _Bool   log_send_errors;
     char    *prefix;
     char    *postfix;
     char     escape_char;
 
-    _Bool    store_rates;
-    _Bool    separate_instances;
-    _Bool    always_append_ds;
+    unsigned int format_flags;
 
     char     send_buf[WG_SEND_BUF_SIZE];
     size_t   send_buf_free;
@@ -95,6 +108,7 @@ struct wg_callback
     cdtime_t send_buf_init_time;
 
     pthread_mutex_t send_lock;
+    c_complain_t init_complaint;
 };
 
 
@@ -116,10 +130,15 @@ static int wg_send_buffer (struct wg_callback *cb)
     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)));
+        const char *protocol = cb->protocol ? cb->protocol : WG_DEFAULT_PROTOCOL;
 
+        if (cb->log_send_errors)
+        {
+            char errbuf[1024];
+            ERROR ("write_graphite plugin: send to %s:%s (%s) failed with status %zi (%s)",
+                    cb->node, cb->service, protocol,
+                    status, sstrerror (errno, errbuf, sizeof (errbuf)));
+        }
 
         close (cb->sock_fd);
         cb->sock_fd = -1;
@@ -171,6 +190,7 @@ static int wg_callback_init (struct wg_callback *cb)
 
     const char *node = cb->node ? cb->node : WG_DEFAULT_NODE;
     const char *service = cb->service ? cb->service : WG_DEFAULT_SERVICE;
+    const char *protocol = cb->protocol ? cb->protocol : WG_DEFAULT_PROTOCOL;
 
     if (cb->sock_fd > 0)
         return (0);
@@ -180,15 +200,19 @@ static int wg_callback_init (struct wg_callback *cb)
     ai_hints.ai_flags |= AI_ADDRCONFIG;
 #endif
     ai_hints.ai_family = AF_UNSPEC;
-    ai_hints.ai_socktype = SOCK_STREAM;
+
+    if (0 == strcasecmp ("tcp", protocol))
+        ai_hints.ai_socktype = SOCK_STREAM;
+    else
+        ai_hints.ai_socktype = SOCK_DGRAM;
 
     ai_list = NULL;
 
     status = getaddrinfo (node, service, &ai_hints, &ai_list);
     if (status != 0)
     {
-        ERROR ("write_graphite plugin: getaddrinfo (%s, %s) failed: %s",
-                node, service, gai_strerror (status));
+        ERROR ("write_graphite plugin: getaddrinfo (%s, %s, %s) failed: %s",
+                node, service, protocol, gai_strerror (status));
         return (-1);
     }
 
@@ -216,12 +240,18 @@ static int wg_callback_init (struct wg_callback *cb)
     if (cb->sock_fd < 0)
     {
         char errbuf[1024];
-        ERROR ("write_graphite plugin: Connecting to %s:%s failed. "
-                "The last error was: %s", node, service,
+        c_complain (LOG_ERR, &cb->init_complaint,
+                "write_graphite plugin: Connecting to %s:%s via %s failed. "
+                "The last error was: %s", node, service, protocol,
                 sstrerror (errno, errbuf, sizeof (errbuf)));
-        close (cb->sock_fd);
         return (-1);
     }
+    else
+    {
+        c_release (LOG_INFO, &cb->init_complaint,
+                "write_graphite plugin: Successfully connected to %s:%s via %s.",
+                node, service, protocol);
+    }
 
     wg_reset_buffer (cb);
 
@@ -241,10 +271,15 @@ static void wg_callback_free (void *data)
 
     wg_flush_nolock (/* timeout = */ 0, cb);
 
-    close(cb->sock_fd);
-    cb->sock_fd = -1;
+    if (cb->sock_fd >= 0)
+    {
+        close (cb->sock_fd);
+        cb->sock_fd = -1;
+    }
 
+    sfree(cb->name);
     sfree(cb->node);
+    sfree(cb->protocol);
     sfree(cb->service);
     sfree(cb->prefix);
     sfree(cb->postfix);
@@ -273,7 +308,7 @@ static int wg_flush (cdtime_t timeout,
         status = wg_callback_init (cb);
         if (status != 0)
         {
-            ERROR ("write_graphite plugin: wg_callback_init failed.");
+            /* An error message has already been printed. */
             pthread_mutex_unlock (&cb->send_lock);
             return (-1);
         }
@@ -299,7 +334,7 @@ static int wg_send_message (char const *message, struct wg_callback *cb)
         status = wg_callback_init (cb);
         if (status != 0)
         {
-            ERROR ("write_graphite plugin: wg_callback_init failed.");
+            /* An error message has already been printed. */
             pthread_mutex_unlock (&cb->send_lock);
             return (-1);
         }
@@ -325,9 +360,10 @@ static int wg_send_message (char const *message, struct wg_callback *cb)
     cb->send_buf_fill += message_len;
     cb->send_buf_free -= message_len;
 
-    DEBUG ("write_graphite plugin: [%s]:%s buf %zu/%zu (%.1f %%) \"%s\"",
+    DEBUG ("write_graphite plugin: [%s]:%s (%s) buf %zu/%zu (%.1f %%) \"%s\"",
             cb->node,
             cb->service,
+            cb->protocol,
             cb->send_buf_fill, sizeof (cb->send_buf),
             100.0 * ((double) cb->send_buf_fill) / ((double) sizeof (cb->send_buf)),
             message);
@@ -340,7 +376,7 @@ static int wg_send_message (char const *message, struct wg_callback *cb)
 static int wg_write_messages (const data_set_t *ds, const value_list_t *vl,
         struct wg_callback *cb)
 {
-    char buffer[4096];
+    char buffer[WG_SEND_BUF_SIZE];
     int status;
 
     if (0 != strcmp (ds->type, vl->type))
@@ -352,17 +388,14 @@ static int wg_write_messages (const data_set_t *ds, const value_list_t *vl,
 
     memset (buffer, 0, sizeof (buffer));
     status = format_graphite (buffer, sizeof (buffer), ds, vl,
-            cb->prefix, cb->postfix, cb->escape_char, cb->store_rates);
+            cb->prefix, cb->postfix, cb->escape_char, cb->format_flags);
     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);
+    /* Send the message to graphite */
+    status = wg_send_message (buffer, cb);
+    if (status != 0) /* error message has been printed already. */
         return (status);
-    }
 
     return (0);
 } /* int wg_write_messages */
@@ -414,12 +447,13 @@ static int config_set_char (char *dest,
     return (0);
 }
 
-static int wg_config_carbon (oconfig_item_t *ci)
+static int wg_config_node (oconfig_item_t *ci)
 {
     struct wg_callback *cb;
     user_data_t user_data;
     char callback_name[DATA_MAX_NAME_LEN];
     int i;
+    int status = 0;
 
     cb = malloc (sizeof (*cb));
     if (cb == NULL)
@@ -429,14 +463,29 @@ static int wg_config_carbon (oconfig_item_t *ci)
     }
     memset (cb, 0, sizeof (*cb));
     cb->sock_fd = -1;
+    cb->name = NULL;
     cb->node = NULL;
     cb->service = NULL;
+    cb->protocol = NULL;
+    cb->log_send_errors = WG_DEFAULT_LOG_SEND_ERRORS;
     cb->prefix = NULL;
     cb->postfix = NULL;
     cb->escape_char = WG_DEFAULT_ESCAPE;
-    cb->store_rates = 1;
+    cb->format_flags = GRAPHITE_STORE_RATES;
+
+    /* FIXME: Legacy configuration syntax. */
+    if (strcasecmp ("Carbon", ci->key) != 0)
+    {
+        status = cf_util_get_string (ci, &cb->name);
+        if (status != 0)
+        {
+            wg_callback_free (cb);
+            return (status);
+        }
+    }
 
     pthread_mutex_init (&cb->send_lock, /* attr = */ NULL);
+    C_COMPLAIN_INIT (&cb->init_complaint);
 
     for (i = 0; i < ci->children_num; i++)
     {
@@ -446,28 +495,61 @@ static int wg_config_carbon (oconfig_item_t *ci)
             cf_util_get_string (child, &cb->node);
         else if (strcasecmp ("Port", child->key) == 0)
             cf_util_get_service (child, &cb->service);
+        else if (strcasecmp ("Protocol", child->key) == 0)
+        {
+            cf_util_get_string (child, &cb->protocol);
+
+            if (strcasecmp ("UDP", cb->protocol) != 0 &&
+                strcasecmp ("TCP", cb->protocol) != 0)
+            {
+                ERROR ("write_graphite plugin: Unknown protocol (%s)",
+                        cb->protocol);
+                status = -1;
+            }
+        }
+        else if (strcasecmp ("LogSendErrors", child->key) == 0)
+            cf_util_get_boolean (child, &cb->log_send_errors);
         else if (strcasecmp ("Prefix", child->key) == 0)
             cf_util_get_string (child, &cb->prefix);
         else if (strcasecmp ("Postfix", child->key) == 0)
             cf_util_get_string (child, &cb->postfix);
         else if (strcasecmp ("StoreRates", child->key) == 0)
-            cf_util_get_boolean (child, &cb->store_rates);
+            cf_util_get_flag (child, &cb->format_flags,
+                    GRAPHITE_STORE_RATES);
         else if (strcasecmp ("SeparateInstances", child->key) == 0)
-            cf_util_get_boolean (child, &cb->separate_instances);
+            cf_util_get_flag (child, &cb->format_flags,
+                    GRAPHITE_SEPARATE_INSTANCES);
         else if (strcasecmp ("AlwaysAppendDS", child->key) == 0)
-            cf_util_get_boolean (child, &cb->always_append_ds);
+            cf_util_get_flag (child, &cb->format_flags,
+                    GRAPHITE_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);
+            status = -1;
         }
+
+        if (status != 0)
+            break;
+    }
+
+    if (status != 0)
+    {
+        wg_callback_free (cb);
+        return (status);
     }
 
-    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);
+    /* FIXME: Legacy configuration syntax. */
+    if (cb->name == NULL)
+        ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s/%s/%s",
+                cb->node != NULL ? cb->node : WG_DEFAULT_NODE,
+                cb->service != NULL ? cb->service : WG_DEFAULT_SERVICE,
+                cb->protocol != NULL ? cb->protocol : WG_DEFAULT_PROTOCOL);
+    else
+        ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s",
+                cb->name);
 
     memset (&user_data, 0, sizeof (user_data));
     user_data.data = cb;
@@ -488,8 +570,11 @@ static int wg_config (oconfig_item_t *ci)
     {
         oconfig_item_t *child = ci->children + i;
 
-        if (strcasecmp ("Carbon", child->key) == 0)
-            wg_config_carbon (child);
+        if (strcasecmp ("Node", child->key) == 0)
+            wg_config_node (child);
+        /* FIXME: Remove this legacy mode in version 6. */
+        else if (strcasecmp ("Carbon", child->key) == 0)
+            wg_config_node (child);
         else
         {
             ERROR ("write_graphite plugin: Invalid configuration "
index 3035e43..6dc7b36 100644 (file)
@@ -46,10 +46,15 @@ struct wh_callback_s
         char *user;
         char *pass;
         char *credentials;
-        int   verify_peer;
-        int   verify_host;
+        _Bool verify_peer;
+        _Bool verify_host;
         char *cacert;
-        int   store_rates;
+        char *capath;
+        char *clientkey;
+        char *clientcert;
+        char *clientkeypass;
+        long sslversion;
+        _Bool store_rates;
 
 #define WH_FORMAT_COMMAND 0
 #define WH_FORMAT_JSON    1
@@ -88,7 +93,7 @@ static int wh_send_buffer (wh_callback_t *cb) /* {{{ */
 
         curl_easy_setopt (cb->curl, CURLOPT_POSTFIELDS, cb->send_buffer);
         status = curl_easy_perform (cb->curl);
-        if (status != 0)
+        if (status != CURLE_OK)
         {
                 ERROR ("write_http plugin: curl_easy_perform failed with "
                                 "status %i: %s",
@@ -111,8 +116,8 @@ static int wh_callback_init (wh_callback_t *cb) /* {{{ */
                 return (-1);
         }
 
-        curl_easy_setopt (cb->curl, CURLOPT_NOSIGNAL, 1);
-        curl_easy_setopt (cb->curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+        curl_easy_setopt (cb->curl, CURLOPT_NOSIGNAL, 1L);
+        curl_easy_setopt (cb->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
 
         headers = NULL;
         headers = curl_slist_append (headers, "Accept:  */*");
@@ -147,11 +152,23 @@ static int wh_callback_init (wh_callback_t *cb) /* {{{ */
                 curl_easy_setopt (cb->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
         }
 
-        curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYPEER, cb->verify_peer);
+        curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYPEER, (long) cb->verify_peer);
         curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYHOST,
-                        cb->verify_host ? 2 : 0);
+                        cb->verify_host ? 2L : 0L);
+        curl_easy_setopt (cb->curl, CURLOPT_SSLVERSION, cb->sslversion);
         if (cb->cacert != NULL)
                 curl_easy_setopt (cb->curl, CURLOPT_CAINFO, cb->cacert);
+        if (cb->capath != NULL)
+                curl_easy_setopt (cb->curl, CURLOPT_CAPATH, cb->capath);
+
+        if (cb->clientkey != NULL && cb->clientcert != NULL)
+        {
+            curl_easy_setopt (cb->curl, CURLOPT_SSLKEY, cb->clientkey);
+            curl_easy_setopt (cb->curl, CURLOPT_SSLCERT, cb->clientcert);
+
+            if (cb->clientkeypass != NULL)
+                curl_easy_setopt (cb->curl, CURLOPT_SSLKEYPASSWD, cb->clientkeypass);
+        }
 
         wh_reset_buffer (cb);
 
@@ -269,6 +286,10 @@ static void wh_callback_free (void *data) /* {{{ */
         sfree (cb->pass);
         sfree (cb->credentials);
         sfree (cb->cacert);
+        sfree (cb->capath);
+        sfree (cb->clientkey);
+        sfree (cb->clientcert);
+        sfree (cb->clientkeypass);
 
         sfree (cb);
 } /* }}} void wh_callback_free */
@@ -433,47 +454,6 @@ static int wh_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
         return (status);
 } /* }}} int wh_write */
 
-static int 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 ("write_http 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 ("write_http plugin: strdup failed.");
-                return (-1);
-        }
-
-        if (*ret_string != NULL)
-                free (*ret_string);
-        *ret_string = string;
-
-        return (0);
-} /* }}} int config_set_string */
-
-static int config_set_boolean (int *dest, oconfig_item_t *ci) /* {{{ */
-{
-        if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
-        {
-                WARNING ("write_http plugin: The `%s' config option "
-                                "needs exactly one boolean argument.", ci->key);
-                return (-1);
-        }
-
-        *dest = ci->values[0].value.boolean ? 1 : 0;
-
-        return (0);
-} /* }}} int config_set_boolean */
-
 static int config_set_format (wh_callback_t *cb, /* {{{ */
                 oconfig_item_t *ci)
 {
@@ -500,7 +480,7 @@ static int config_set_format (wh_callback_t *cb, /* {{{ */
         }
 
         return (0);
-} /* }}} int config_set_string */
+} /* }}} int config_set_format */
 
 static int wh_config_url (oconfig_item_t *ci) /* {{{ */
 {
@@ -515,19 +495,14 @@ static int wh_config_url (oconfig_item_t *ci) /* {{{ */
                 return (-1);
         }
         memset (cb, 0, sizeof (*cb));
-        cb->location = NULL;
-        cb->user = NULL;
-        cb->pass = NULL;
-        cb->credentials = NULL;
         cb->verify_peer = 1;
         cb->verify_host = 1;
-        cb->cacert = NULL;
         cb->format = WH_FORMAT_COMMAND;
-        cb->curl = NULL;
+        cb->sslversion = CURL_SSLVERSION_DEFAULT;
 
         pthread_mutex_init (&cb->send_lock, /* attr = */ NULL);
 
-        config_set_string (&cb->location, ci);
+        cf_util_get_string (ci, &cb->location);
         if (cb->location == NULL)
                 return (-1);
 
@@ -536,19 +511,55 @@ static int wh_config_url (oconfig_item_t *ci) /* {{{ */
                 oconfig_item_t *child = ci->children + i;
 
                 if (strcasecmp ("User", child->key) == 0)
-                        config_set_string (&cb->user, child);
+                        cf_util_get_string (child, &cb->user);
                 else if (strcasecmp ("Password", child->key) == 0)
-                        config_set_string (&cb->pass, child);
+                        cf_util_get_string (child, &cb->pass);
                 else if (strcasecmp ("VerifyPeer", child->key) == 0)
-                        config_set_boolean (&cb->verify_peer, child);
+                        cf_util_get_boolean (child, &cb->verify_peer);
                 else if (strcasecmp ("VerifyHost", child->key) == 0)
-                        config_set_boolean (&cb->verify_host, child);
+                        cf_util_get_boolean (child, &cb->verify_host);
                 else if (strcasecmp ("CACert", child->key) == 0)
-                        config_set_string (&cb->cacert, child);
+                        cf_util_get_string (child, &cb->cacert);
+                else if (strcasecmp ("CAPath", child->key) == 0)
+                        cf_util_get_string (child, &cb->capath);
+                else if (strcasecmp ("ClientKey", child->key) == 0)
+                        cf_util_get_string (child, &cb->clientkey);
+                else if (strcasecmp ("ClientCert", child->key) == 0)
+                        cf_util_get_string (child, &cb->clientcert);
+                else if (strcasecmp ("ClientKeyPass", child->key) == 0)
+                        cf_util_get_string (child, &cb->clientkeypass);
+                else if (strcasecmp ("SSLVersion", child->key) == 0)
+                {
+                        char *value = NULL;
+
+                        cf_util_get_string (child, &value);
+
+                        if (value == NULL || strcasecmp ("default", value) == 0)
+                                cb->sslversion = CURL_SSLVERSION_DEFAULT;
+                        else if (strcasecmp ("SSLv2", value) == 0)
+                                cb->sslversion = CURL_SSLVERSION_SSLv2;
+                        else if (strcasecmp ("SSLv3", value) == 0)
+                                cb->sslversion = CURL_SSLVERSION_SSLv3;
+                        else if (strcasecmp ("TLSv1", value) == 0)
+                                cb->sslversion = CURL_SSLVERSION_TLSv1;
+#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 34)
+                        else if (strcasecmp ("TLSv1_0", value) == 0)
+                                cb->sslversion = CURL_SSLVERSION_TLSv1_0;
+                        else if (strcasecmp ("TLSv1_1", value) == 0)
+                                cb->sslversion = CURL_SSLVERSION_TLSv1_1;
+                        else if (strcasecmp ("TLSv1_2", value) == 0)
+                                cb->sslversion = CURL_SSLVERSION_TLSv1_2;
+#endif
+                        else
+                                ERROR ("write_http plugin: Invalid SSLVersion "
+                                                "option: %s.", value);
+
+                        sfree(value);
+                }
                 else if (strcasecmp ("Format", child->key) == 0)
                         config_set_format (cb, child);
                 else if (strcasecmp ("StoreRates", child->key) == 0)
-                        config_set_boolean (&cb->store_rates, child);
+                        cf_util_get_boolean (child, &cb->store_rates);
                 else
                 {
                         ERROR ("write_http plugin: Invalid configuration "
diff --git a/src/write_kafka.c b/src/write_kafka.c
new file mode 100644 (file)
index 0000000..97db426
--- /dev/null
@@ -0,0 +1,418 @@
+/**
+ * collectd - src/write_kafka.c
+ *
+ * Copyright (C) 2014       Pierre-Yves Ritschard
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ */
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+#include "utils_cache.h"
+#include "utils_cmd_putval.h"
+#include "utils_format_graphite.h"
+#include "utils_format_json.h"
+#include "utils_crc32.h"
+
+#include <sys/types.h>
+#include <librdkafka/rdkafka.h>
+#include <pthread.h>
+#include <zlib.h>
+
+struct kafka_topic_context {
+#define KAFKA_FORMAT_COMMAND     1
+#define KAFKA_FORMAT_GRAPHITE    2
+#define KAFKA_FORMAT_JSON        3
+    u_int8_t                     format;
+    unsigned int                 graphite_flags;
+    _Bool                        store_rates;
+    rd_kafka_topic_conf_t       *conf;
+    rd_kafka_topic_t            *topic;
+    rd_kafka_t                  *kafka;
+    int                          has_key;
+    u_int32_t                    key;
+    char                        *prefix;
+    char                        *postfix;
+    char                         escape_char;
+    char                        *topic_name;
+};
+
+static int kafka_write(const data_set_t *, const value_list_t *, user_data_t *);
+static int32_t kafka_partition(const rd_kafka_topic_t *, const void *, size_t,
+                               int32_t, void *, void *);
+static void kafka_log(const rd_kafka_t *, int, const char *, const char *);
+
+static void kafka_log(const rd_kafka_t *rkt, int level,
+                      const char *fac, const char *msg)
+{
+    plugin_log(level, "%s", msg);
+}
+
+static int32_t kafka_partition(const rd_kafka_topic_t *rkt,
+                               const void *keydata, size_t keylen,
+                               int32_t partition_cnt, void *p, void *m)
+{
+    u_int32_t key = *((u_int32_t *)keydata );
+
+    return key % partition_cnt;
+}
+
+static int kafka_write(const data_set_t *ds, /* {{{ */
+             const value_list_t *vl,
+             user_data_t *ud)
+{
+       int                      status = 0;
+    u_int32_t    key;
+    char         buffer[8192];
+    size_t bfree = sizeof(buffer);
+    size_t bfill = 0;
+    size_t blen = 0;
+       struct kafka_topic_context      *ctx = ud->data;
+
+    if ((ds == NULL) || (vl == NULL) || (ctx == NULL))
+        return EINVAL;
+
+    bzero(buffer, sizeof(buffer));
+
+    switch (ctx->format) {
+    case KAFKA_FORMAT_COMMAND:
+        status = create_putval(buffer, sizeof(buffer), ds, vl);
+        if (status != 0) {
+            ERROR("write_kafka plugin: create_putval failed with status %i.",
+                  status);
+            return status;
+        }
+        blen = strlen(buffer);
+        break;
+    case KAFKA_FORMAT_JSON:
+
+        format_json_initialize(buffer, &bfill, &bfree);
+        format_json_value_list(buffer, &bfill, &bfree, ds, vl,
+                               ctx->store_rates);
+        format_json_finalize(buffer, &bfill, &bfree);
+        blen = strlen(buffer);
+        break;
+    case KAFKA_FORMAT_GRAPHITE:
+        status = format_graphite(buffer, sizeof(buffer), ds, vl,
+                                 ctx->prefix, ctx->postfix, ctx->escape_char,
+                                 ctx->graphite_flags);
+        if (status != 0) {
+            ERROR("write_kafka plugin: format_graphite failed with status %i.",
+                  status);
+            return status;
+        }
+        blen = strlen(buffer);
+        break;
+    default:
+        ERROR("write_kafka plugin: invalid format %i.", ctx->format);
+        return -1;
+    }
+
+    /*
+     * We partition our stream by metric name
+     */
+    if (ctx->has_key)
+        key = ctx->key;
+    else
+        key = rand();
+
+    rd_kafka_produce(ctx->topic, RD_KAFKA_PARTITION_UA,
+                     RD_KAFKA_MSG_F_COPY, buffer, blen,
+                     &key, sizeof(key), NULL);
+
+       return status;
+} /* }}} int kafka_write */
+
+static void kafka_topic_context_free(void *p) /* {{{ */
+{
+       struct kafka_topic_context *ctx = p;
+
+       if (ctx == NULL)
+               return;
+
+    if (ctx->topic_name != NULL)
+        sfree(ctx->topic_name);
+    if (ctx->topic != NULL)
+        rd_kafka_topic_destroy(ctx->topic);
+    if (ctx->conf != NULL)
+        rd_kafka_topic_conf_destroy(ctx->conf);
+
+    sfree(ctx);
+} /* }}} void kafka_topic_context_free */
+
+static void kafka_config_topic(rd_kafka_conf_t *conf, oconfig_item_t *ci) /* {{{ */
+{
+    int                          status;
+    int                          i;
+    struct kafka_topic_context  *tctx;
+    char                        *key;
+    char                        *val;
+    char                         callback_name[DATA_MAX_NAME_LEN];
+    char                         errbuf[1024];
+    user_data_t                  ud;
+       oconfig_item_t              *child;
+    rd_kafka_conf_res_t          ret;
+
+       if ((tctx = calloc(1, sizeof (*tctx))) == NULL) {
+               ERROR ("write_kafka plugin: calloc failed.");
+        return;
+       }
+
+    tctx->escape_char = '.';
+    tctx->store_rates = 1;
+
+    rd_kafka_conf_set_log_cb(conf, kafka_log);
+    if ((tctx->kafka = rd_kafka_new(RD_KAFKA_PRODUCER, conf,
+                                    errbuf, sizeof(errbuf))) == NULL) {
+        sfree(tctx);
+        ERROR("write_kafka plugin: cannot create kafka handle.");
+        return;
+    }
+    conf = NULL;
+
+    if ((tctx->conf = rd_kafka_topic_conf_new()) == NULL) {
+        rd_kafka_destroy(tctx->kafka);
+        sfree(tctx);
+        ERROR ("write_kafka plugin: cannot create topic configuration.");
+        return;
+    }
+
+    if (ci->values_num != 1) {
+        WARNING("kafka topic name needed.");
+        goto errout;
+    }
+
+    if (ci->values[0].type != OCONFIG_TYPE_STRING) {
+        WARNING("kafka topic needs a string argument.");
+        goto errout;
+    }
+
+    if ((tctx->topic_name = strdup(ci->values[0].value.string)) == NULL) {
+        ERROR("write_kafka plugin: cannot copy topic name.");
+        goto errout;
+    }
+
+       for (i = 0; i < ci->children_num; i++) {
+               /*
+                * The code here could be simplified but makes room
+                * for easy adding of new options later on.
+                */
+               child = &ci->children[i];
+               status = 0;
+
+               if (strcasecmp ("Property", child->key) == 0) {
+                       if (child->values_num != 2) {
+                               WARNING("kafka properties need both a key and a value.");
+                goto errout;
+                       }
+                       if (child->values[0].type != OCONFIG_TYPE_STRING ||
+                           child->values[1].type != OCONFIG_TYPE_STRING) {
+                               WARNING("kafka properties needs string arguments.");
+                goto errout;
+                       }
+            key = child->values[0].value.string;
+            val = child->values[0].value.string;
+            ret = rd_kafka_topic_conf_set(tctx->conf,key, val,
+                                          errbuf, sizeof(errbuf));
+            if (ret != RD_KAFKA_CONF_OK) {
+                               WARNING("cannot set kafka topic property %s to %s: %s.",
+                        key, val, errbuf);
+                goto errout;
+                       }
+
+        } else if (strcasecmp ("Key", child->key) == 0)  {
+            char *tmp_buf = NULL;
+            status = cf_util_get_string(child, &tmp_buf);
+            if (status != 0) {
+                WARNING("write_kafka plugin: invalid key supplied");
+                break;
+            }
+
+            if (strcasecmp(tmp_buf, "Random") != 0) {
+                tctx->has_key = 1;
+                tctx->key = crc32_buffer((u_char *)tmp_buf, strlen(tmp_buf));
+            }
+            sfree(tmp_buf);
+
+        } else if (strcasecmp ("Format", child->key) == 0) {
+            status = cf_util_get_string(child, &key);
+            if (status != 0)
+                goto errout;
+
+            assert(key != NULL);
+
+            if (strcasecmp(key, "Command") == 0) {
+
+                tctx->format = KAFKA_FORMAT_COMMAND;
+
+            } else if (strcasecmp(key, "Graphite") == 0) {
+                tctx->format = KAFKA_FORMAT_GRAPHITE;
+
+            } else if (strcasecmp(key, "Json") == 0) {
+                tctx->format = KAFKA_FORMAT_JSON;
+
+            } else {
+                WARNING ("write_kafka plugin: Invalid format string: %s",
+                         key);
+            }
+            sfree(key);
+
+        } else if (strcasecmp ("StoreRates", child->key) == 0) {
+            status = cf_util_get_boolean (child, &tctx->store_rates);
+            (void) cf_util_get_flag (child, &tctx->graphite_flags,
+                                     GRAPHITE_STORE_RATES);
+
+        } else if (strcasecmp ("GraphiteSeparateInstances", child->key) == 0) {
+            status = cf_util_get_flag (child, &tctx->graphite_flags,
+                                       GRAPHITE_SEPARATE_INSTANCES);
+
+        } else if (strcasecmp ("GraphiteAlwaysAppendDS", child->key) == 0) {
+            status = cf_util_get_flag (child, &tctx->graphite_flags,
+                                       GRAPHITE_ALWAYS_APPEND_DS);
+
+        } else if (strcasecmp ("GraphitePrefix", child->key) == 0) {
+            status = cf_util_get_string (child, &tctx->prefix);
+        } else if (strcasecmp ("GraphitePostfix", child->key) == 0) {
+            status = cf_util_get_string (child, &tctx->postfix);
+        } else if (strcasecmp ("GraphiteEscapeChar", child->key) == 0) {
+            char *tmp_buff = NULL;
+            status = cf_util_get_string (child, &tmp_buff);
+            if (strlen (tmp_buff) > 1)
+                WARNING ("write_kafka plugin: The option \"GraphiteEscapeChar\" handles "
+                        "only one character. Others will be ignored.");
+            tctx->escape_char = tmp_buff[0];
+            sfree (tmp_buff);
+        } else {
+            WARNING ("write_kafka plugin: Invalid directive: %s.", child->key);
+        }
+
+        if (status != 0)
+            break;
+    }
+
+    rd_kafka_topic_conf_set_partitioner_cb(tctx->conf, kafka_partition);
+    rd_kafka_topic_conf_set_opaque(tctx->conf, tctx);
+
+    if ((tctx->topic = rd_kafka_topic_new(tctx->kafka, tctx->topic_name,
+                                       tctx->conf)) == NULL) {
+        ERROR("write_kafka plugin: cannot create topic.");
+        goto errout;
+    }
+    tctx->conf = NULL;
+
+    ssnprintf(callback_name, sizeof(callback_name),
+              "write_kafka/%s", tctx->topic_name);
+
+    ud.data = tctx;
+    ud.free_func = kafka_topic_context_free;
+
+       status = plugin_register_write (callback_name, kafka_write, &ud);
+       if (status != 0) {
+               WARNING ("write_kafka plugin: plugin_register_write (\"%s\") "
+                               "failed with status %i.",
+                               callback_name, status);
+        goto errout;
+    }
+    return;
+ errout:
+    if (conf != NULL)
+        rd_kafka_conf_destroy(conf);
+    if (tctx->kafka != NULL)
+        rd_kafka_destroy(tctx->kafka);
+    if (tctx->topic != NULL)
+        rd_kafka_topic_destroy(tctx->topic);
+    if (tctx->topic_name != NULL)
+        free(tctx->topic_name);
+    if (tctx->conf != NULL)
+        rd_kafka_topic_conf_destroy(tctx->conf);
+    sfree(tctx);
+} /* }}} int kafka_config_topic */
+
+static int kafka_config(oconfig_item_t *ci) /* {{{ */
+{
+       int                          i;
+       oconfig_item_t              *child;
+    rd_kafka_conf_t             *conf;
+    rd_kafka_conf_t             *cloned;
+    rd_kafka_conf_res_t          ret;
+    char                         errbuf[1024];
+
+    if ((conf = rd_kafka_conf_new()) == NULL) {
+        WARNING("cannot allocate kafka configuration.");
+        return -1;
+    }
+
+       for (i = 0; i < ci->children_num; i++)  {
+               child = &ci->children[i];
+
+               if (strcasecmp("Topic", child->key) == 0) {
+            if ((cloned = rd_kafka_conf_dup(conf)) == NULL) {
+                WARNING("write_kafka plugin: cannot allocate memory for kafka config");
+                goto errout;
+            }
+                       kafka_config_topic (cloned, child);
+               } else if (strcasecmp(child->key, "Property") == 0) {
+                       char *key = NULL;
+                       char *val = NULL;
+
+                       if (child->values_num != 2) {
+                               WARNING("kafka properties need both a key and a value.");
+                goto errout;
+                       }
+                       if (child->values[0].type != OCONFIG_TYPE_STRING ||
+                           child->values[1].type != OCONFIG_TYPE_STRING) {
+                               WARNING("kafka properties needs string arguments.");
+                goto errout;
+                       }
+                       if ((key = strdup(child->values[0].value.string)) == NULL) {
+                               WARNING("cannot allocate memory for attribute key.");
+                goto errout;
+                       }
+                       if ((val = strdup(child->values[1].value.string)) == NULL) {
+                               WARNING("cannot allocate memory for attribute value.");
+                goto errout;
+                       }
+            ret = rd_kafka_conf_set(conf, key, val, errbuf, sizeof(errbuf));
+            if (ret != RD_KAFKA_CONF_OK) {
+                WARNING("cannot set kafka property %s to %s: %s",
+                        key, val, errbuf);
+                goto errout;
+            }
+                       sfree(key);
+                       sfree(val);
+               } else {
+                       WARNING ("write_kafka plugin: Ignoring unknown "
+                                "configuration option \"%s\" at top level.",
+                                child->key);
+               }
+       }
+    if (conf != NULL)
+        rd_kafka_conf_destroy(conf);
+       return (0);
+ errout:
+    if (conf != NULL)
+        rd_kafka_conf_destroy(conf);
+    return -1;
+} /* }}} int kafka_config */
+
+void module_register(void)
+{
+       plugin_register_complex_config ("write_kafka", kafka_config);
+}
+
+/* vim: set sw=8 sts=8 ts=8 noet : */
index c7b7682..30e261c 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/write_mongodb.c
- * Copyright (C) 2010-2012  Florian Forster
+ * Copyright (C) 2010-2013  Florian Forster
  * Copyright (C) 2010       Akkarit Sangpetch
  * Copyright (C) 2012       Chris Lundquist
  *
@@ -23,7 +23,7 @@
  * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
- *   Florian Forster <ff at octo.it>
+ *   Florian Forster <octo at collectd.org>
  *   Akkarit Sangpetch <asangpet at andrew.cmu.edu>
  *   Chris Lundquist <clundquist at bluebox.net>
  **/
@@ -51,6 +51,11 @@ struct wm_node_s
   int port;
   int timeout;
 
+  /* Authentication information */
+  char *db;
+  char *user;
+  char *passwd;
+
   _Bool store_rates;
 
   mongo conn[1];
@@ -183,6 +188,23 @@ static int wm_write (const data_set_t *ds, /* {{{ */
       return (-1);
     }
 
+    if ((node->db != NULL) && (node->user != NULL) && (node->passwd != NULL))
+    {
+      status = mongo_cmd_authenticate (node->conn,
+          node->db, node->user, node->passwd);
+      if (status != MONGO_OK)
+      {
+        ERROR ("write_mongodb plugin: Authenticating to [%s]%i for database "
+            "\"%s\" as user \"%s\" failed.",
+          (node->host != NULL) ? node->host : "localhost",
+          (node->port != 0) ? node->port : MONGO_DEFAULT_PORT,
+          node->db, node->user);
+        mongo_destroy (node->conn);
+        pthread_mutex_unlock (&node->lock);
+        return (-1);
+      }
+    }
+
     if (node->timeout > 0) {
       status = mongo_set_op_timeout (node->conn, node->timeout);
       if (status != MONGO_OK) {
@@ -203,13 +225,14 @@ static int wm_write (const data_set_t *ds, /* {{{ */
     status = mongo_insert (node->conn, collection_name, bson_record);
   #endif
 
-  if(status != MONGO_OK)
+  if (status != MONGO_OK)
   {
     ERROR ( "write_mongodb plugin: error inserting record: %d", node->conn->err);
     if (node->conn->err != MONGO_BSON_INVALID)
       ERROR ("write_mongodb plugin: %s", node->conn->errstr);
-    else if (bson_record->err)
-      ERROR ("write_mongodb plugin: %s", bson_record->errstr);
+    else
+      ERROR ("write_mongodb plugin: Invalid BSON structure, error = %#x",
+          (unsigned int) bson_record->err);
 
     /* Disconnect except on data errors. */
     if ((node->conn->err != MONGO_BSON_INVALID)
@@ -281,6 +304,12 @@ static int wm_config_node (oconfig_item_t *ci) /* {{{ */
       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 if (strcasecmp ("Database", child->key) == 0)
+      status = cf_util_get_string (child, &node->db);
+    else if (strcasecmp ("User", child->key) == 0)
+      status = cf_util_get_string (child, &node->user);
+    else if (strcasecmp ("Password", child->key) == 0)
+      status = cf_util_get_string (child, &node->passwd);
     else
       WARNING ("write_mongodb plugin: Ignoring unknown config option \"%s\".",
           child->key);
@@ -289,6 +318,20 @@ static int wm_config_node (oconfig_item_t *ci) /* {{{ */
       break;
   } /* for (i = 0; i < ci->children_num; i++) */
 
+  if ((node->db != NULL) || (node->user != NULL) || (node->passwd != NULL))
+  {
+    if ((node->db == NULL) || (node->user == NULL) || (node->passwd == NULL))
+    {
+      WARNING ("write_mongodb plugin: Authentication requires the "
+          "\"Database\", \"User\" and \"Password\" options to be specified, "
+          "but at last one of them is missing. Authentication will NOT be "
+          "used.");
+      sfree (node->db);
+      sfree (node->user);
+      sfree (node->passwd);
+    }
+  }
+
   if (status == 0)
   {
     char cb_name[DATA_MAX_NAME_LEN];
diff --git a/src/write_riemann.c b/src/write_riemann.c
new file mode 100644 (file)
index 0000000..780bccb
--- /dev/null
@@ -0,0 +1,934 @@
+/**
+ * collectd - src/write_riemann.c
+ *
+ * Copyright (C) 2012,2013  Pierre-Yves Ritschard
+ * Copyright (C) 2013       Florian octo Forster
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ *   Florian octo Forster <octo at collectd.org>
+ */
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+#include "utils_cache.h"
+#include "riemann.pb-c.h"
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <inttypes.h>
+#include <pthread.h>
+
+#define RIEMANN_HOST           "localhost"
+#define RIEMANN_PORT           "5555"
+#define RIEMANN_TTL_FACTOR      2.0
+
+int write_riemann_threshold_check(const data_set_t *, const value_list_t *, int *);
+
+struct riemann_host {
+       char                    *name;
+#define F_CONNECT               0x01
+       uint8_t                  flags;
+       pthread_mutex_t          lock;
+    _Bool            notifications;
+    _Bool            check_thresholds;
+       _Bool                    store_rates;
+       _Bool                    always_append_ds;
+       char                    *node;
+       char                    *service;
+       _Bool                    use_tcp;
+       int                      s;
+       double                   ttl_factor;
+
+       int                      reference_count;
+};
+
+static char    **riemann_tags;
+static size_t    riemann_tags_num;
+static char    **riemann_attrs;
+static size_t     riemann_attrs_num;
+
+static void riemann_event_protobuf_free (Event *event) /* {{{ */
+{
+       size_t i;
+
+       if (event == NULL)
+               return;
+
+       sfree (event->state);
+       sfree (event->service);
+       sfree (event->host);
+       sfree (event->description);
+
+       strarray_free (event->tags, event->n_tags);
+       event->tags = NULL;
+       event->n_tags = 0;
+
+       for (i = 0; i < event->n_attributes; i++)
+       {
+               sfree (event->attributes[i]->key);
+               sfree (event->attributes[i]->value);
+               sfree (event->attributes[i]);
+       }
+       sfree (event->attributes);
+       event->n_attributes = 0;
+
+       sfree (event);
+} /* }}} void riemann_event_protobuf_free */
+
+static void riemann_msg_protobuf_free (Msg *msg) /* {{{ */
+{
+       size_t i;
+
+       if (msg == NULL)
+               return;
+
+       for (i = 0; i < msg->n_events; i++)
+       {
+               riemann_event_protobuf_free (msg->events[i]);
+               msg->events[i] = NULL;
+       }
+
+       sfree (msg->events);
+       msg->n_events = 0;
+
+       sfree (msg);
+} /* }}} void riemann_msg_protobuf_free */
+
+/* host->lock must be held when calling this function. */
+static int riemann_connect(struct riemann_host *host) /* {{{ */
+{
+       int                      e;
+       struct addrinfo         *ai, *res, hints;
+       char const              *node;
+       char const              *service;
+
+       if (host->flags & F_CONNECT)
+               return 0;
+
+       memset(&hints, 0, sizeof(hints));
+       memset(&service, 0, sizeof(service));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = host->use_tcp ? SOCK_STREAM : SOCK_DGRAM;
+#ifdef AI_ADDRCONFIG
+       hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+
+       node = (host->node != NULL) ? host->node : RIEMANN_HOST;
+       service = (host->service != NULL) ? host->service : RIEMANN_PORT;
+
+       if ((e = getaddrinfo(node, service, &hints, &res)) != 0) {
+               ERROR ("write_riemann plugin: Unable to resolve host \"%s\": %s",
+                       node, gai_strerror(e));
+               return -1;
+       }
+
+       host->s = -1;
+       for (ai = res; ai != NULL; ai = ai->ai_next) {
+               if ((host->s = socket(ai->ai_family,
+                                     ai->ai_socktype,
+                                     ai->ai_protocol)) == -1) {
+                       continue;
+               }
+
+               if (connect(host->s, ai->ai_addr, ai->ai_addrlen) != 0) {
+                       close(host->s);
+                       host->s = -1;
+                       continue;
+               }
+
+               host->flags |= F_CONNECT;
+               DEBUG("write_riemann plugin: got a successful connection for: %s:%s",
+                               node, service);
+               break;
+       }
+
+       freeaddrinfo(res);
+
+       if (host->s < 0) {
+               WARNING("write_riemann plugin: Unable to connect to Riemann at %s:%s",
+                               node, service);
+               return -1;
+       }
+       return 0;
+} /* }}} int riemann_connect */
+
+/* host->lock must be held when calling this function. */
+static int riemann_disconnect (struct riemann_host *host) /* {{{ */
+{
+       if ((host->flags & F_CONNECT) == 0)
+               return (0);
+
+       close (host->s);
+       host->s = -1;
+       host->flags &= ~F_CONNECT;
+
+       return (0);
+} /* }}} int riemann_disconnect */
+
+static int riemann_send_msg (struct riemann_host *host, const Msg *msg) /* {{{ */
+{
+       int status = 0;
+       u_char *buffer = NULL;
+       size_t  buffer_len;
+
+       status = riemann_connect (host);
+       if (status != 0)
+               return status;
+
+       buffer_len = msg__get_packed_size(msg);
+
+       if (host->use_tcp)
+               buffer_len += 4;
+
+       buffer = malloc (buffer_len);
+       if (buffer == NULL) {
+               ERROR ("write_riemann plugin: malloc failed.");
+               return ENOMEM;
+       }
+       memset (buffer, 0, buffer_len);
+
+       if (host->use_tcp)
+       {
+               uint32_t length = htonl ((uint32_t) (buffer_len - 4));
+               memcpy (buffer, &length, 4);
+               msg__pack(msg, buffer + 4);
+       }
+       else
+       {
+               msg__pack(msg, buffer);
+       }
+
+       status = (int) swrite (host->s, buffer, buffer_len);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("write_riemann plugin: Sending to Riemann at %s:%s failed: %s",
+                               (host->node != NULL) ? host->node : RIEMANN_HOST,
+                               (host->service != NULL) ? host->service : RIEMANN_PORT,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               sfree (buffer);
+               return -1;
+       }
+
+       sfree (buffer);
+       return 0;
+} /* }}} int riemann_send_msg */
+
+static int riemann_recv_ack(struct riemann_host *host) /* {{{ */
+{
+       int status = 0;
+       Msg *msg = NULL;
+       uint32_t header;
+
+       status = (int) sread (host->s, &header, 4);
+
+       if (status != 0)
+               return -1;
+
+       size_t size = ntohl(header);
+
+       // Buffer on the stack since acknowledges are typically small.
+       u_char buffer[size];
+       memset (buffer, 0, size);
+
+       status = (int) sread (host->s, buffer, size);
+
+       if (status != 0)
+               return status;
+
+       msg = msg__unpack (NULL, size, buffer);
+
+       if (msg == NULL)
+               return -1;
+
+       if (!msg->ok)
+       {
+               ERROR ("write_riemann plugin: Sending to Riemann at %s:%s acknowledgement message reported error: %s",
+                               (host->node != NULL) ? host->node : RIEMANN_HOST,
+                               (host->service != NULL) ? host->service : RIEMANN_PORT,
+                               msg->error);
+
+               msg__free_unpacked(msg, NULL);
+               return -1;
+       }
+
+       msg__free_unpacked (msg, NULL);
+       return 0;
+} /* }}} int riemann_recv_ack */
+
+/**
+ * Function to send messages (Msg) to riemann.
+ *
+ * Acquires the host lock, disconnects on errors.
+ */
+static int riemann_send(struct riemann_host *host, Msg const *msg) /* {{{ */
+{
+       int status = 0;
+       pthread_mutex_lock (&host->lock);
+
+       status = riemann_send_msg(host, msg);
+       if (status != 0) {
+               riemann_disconnect (host);
+               pthread_mutex_unlock (&host->lock);
+               return status;
+       }
+
+       /*
+        * For TCP we need to receive message acknowledgemenent.
+        */
+       if (host->use_tcp)
+       {
+               status = riemann_recv_ack(host);
+
+               if (status != 0)
+               {
+                       riemann_disconnect (host);
+                       pthread_mutex_unlock (&host->lock);
+                       return status;
+               }
+       }
+
+       pthread_mutex_unlock (&host->lock);
+       return 0;
+} /* }}} int riemann_send */
+
+static int riemann_event_add_tag (Event *event, char const *tag) /* {{{ */
+{
+       return (strarray_add (&event->tags, &event->n_tags, tag));
+} /* }}} int riemann_event_add_tag */
+
+static int riemann_event_add_attribute (Event *event, /* {{{ */
+               char const *key, char const *value)
+{
+       Attribute **new_attributes;
+       Attribute *a;
+
+       new_attributes = realloc (event->attributes,
+                       sizeof (*event->attributes) * (event->n_attributes + 1));
+       if (new_attributes == NULL)
+       {
+               ERROR ("write_riemann plugin: realloc failed.");
+               return (ENOMEM);
+       }
+       event->attributes = new_attributes;
+
+       a = malloc (sizeof (*a));
+       if (a == NULL)
+       {
+               ERROR ("write_riemann plugin: malloc failed.");
+               return (ENOMEM);
+       }
+       attribute__init (a);
+
+       a->key = strdup (key);
+       if (value != NULL)
+               a->value = strdup (value);
+
+       event->attributes[event->n_attributes] = a;
+       event->n_attributes++;
+
+       return (0);
+} /* }}} int riemann_event_add_attribute */
+
+static Msg *riemann_notification_to_protobuf (struct riemann_host *host, /* {{{ */
+               notification_t const *n)
+{
+       Msg *msg;
+       Event *event;
+       char service_buffer[6 * DATA_MAX_NAME_LEN];
+       char const *severity;
+       notification_meta_t *meta;
+       int i;
+
+       msg = malloc (sizeof (*msg));
+       if (msg == NULL)
+       {
+               ERROR ("write_riemann plugin: malloc failed.");
+               return (NULL);
+       }
+       memset (msg, 0, sizeof (*msg));
+       msg__init (msg);
+
+       msg->events = malloc (sizeof (*msg->events));
+       if (msg->events == NULL)
+       {
+               ERROR ("write_riemann plugin: malloc failed.");
+               sfree (msg);
+               return (NULL);
+       }
+
+       event = malloc (sizeof (*event));
+       if (event == NULL)
+       {
+               ERROR ("write_riemann plugin: malloc failed.");
+               sfree (msg->events);
+               sfree (msg);
+               return (NULL);
+       }
+       memset (event, 0, sizeof (*event));
+       event__init (event);
+
+       msg->events[0] = event;
+       msg->n_events = 1;
+
+       event->host = strdup (n->host);
+       event->time = CDTIME_T_TO_TIME_T (n->time);
+       event->has_time = 1;
+
+       switch (n->severity)
+       {
+               case NOTIF_OKAY:        severity = "ok"; break;
+               case NOTIF_WARNING:     severity = "warning"; break;
+               case NOTIF_FAILURE:     severity = "critical"; break;
+               default:                severity = "unknown";
+       }
+       event->state = strdup (severity);
+
+       riemann_event_add_tag (event, "notification");
+       if (n->host[0] != 0)
+               riemann_event_add_attribute (event, "host", n->host);
+       if (n->plugin[0] != 0)
+               riemann_event_add_attribute (event, "plugin", n->plugin);
+       if (n->plugin_instance[0] != 0)
+               riemann_event_add_attribute (event, "plugin_instance",
+                               n->plugin_instance);
+
+       if (n->type[0] != 0)
+               riemann_event_add_attribute (event, "type", n->type);
+       if (n->type_instance[0] != 0)
+               riemann_event_add_attribute (event, "type_instance",
+                               n->type_instance);
+
+       for (i = 0; i < riemann_attrs_num; i += 2)
+               riemann_event_add_attribute(event,
+                                           riemann_attrs[i],
+                                           riemann_attrs[i +1]);
+
+       for (i = 0; i < riemann_tags_num; i++)
+               riemann_event_add_tag (event, riemann_tags[i]);
+
+       format_name (service_buffer, sizeof (service_buffer),
+                       /* host = */ "", n->plugin, n->plugin_instance,
+                       n->type, n->type_instance);
+       event->service = strdup (&service_buffer[1]);
+
+       if (n->message[0] != 0)
+               riemann_event_add_attribute (event, "description", n->message);
+
+       /* Pull in values from threshold and add extra attributes */
+       for (meta = n->meta; meta != NULL; meta = meta->next)
+       {
+               if (strcasecmp ("CurrentValue", meta->name) == 0 && meta->type == NM_TYPE_DOUBLE)
+               {
+                       event->metric_d = meta->nm_value.nm_double;
+                       event->has_metric_d = 1;
+                       continue;
+               }
+
+               if (meta->type == NM_TYPE_STRING) {
+                       riemann_event_add_attribute (event, meta->name, meta->nm_value.nm_string);
+                       continue;
+               }
+       }
+
+       DEBUG ("write_riemann plugin: Successfully created protobuf for notification: "
+                       "host = \"%s\", service = \"%s\", state = \"%s\"",
+                       event->host, event->service, event->state);
+       return (msg);
+} /* }}} Msg *riemann_notification_to_protobuf */
+
+static Event *riemann_value_to_protobuf (struct riemann_host const *host, /* {{{ */
+               data_set_t const *ds,
+               value_list_t const *vl, size_t index,
+                                        gauge_t const *rates,
+                                        int status)
+{
+       Event *event;
+       char name_buffer[5 * DATA_MAX_NAME_LEN];
+       char service_buffer[6 * DATA_MAX_NAME_LEN];
+       double ttl;
+       int i;
+
+       event = malloc (sizeof (*event));
+       if (event == NULL)
+       {
+               ERROR ("write_riemann plugin: malloc failed.");
+               return (NULL);
+       }
+       memset (event, 0, sizeof (*event));
+       event__init (event);
+
+       event->host = strdup (vl->host);
+       event->time = CDTIME_T_TO_TIME_T (vl->time);
+       event->has_time = 1;
+
+    if (host->check_thresholds) {
+        switch (status) {
+        case STATE_OKAY:
+            event->state = strdup("ok");
+            break;
+        case STATE_ERROR:
+            event->state = strdup("critical");
+            break;
+        case STATE_WARNING:
+            event->state = strdup("warning");
+            break;
+        case STATE_MISSING:
+            event->state = strdup("unknown");
+            break;
+        }
+    }
+
+       ttl = CDTIME_T_TO_DOUBLE (vl->interval) * host->ttl_factor;
+       event->ttl = (float) ttl;
+       event->has_ttl = 1;
+
+       riemann_event_add_attribute (event, "plugin", vl->plugin);
+       if (vl->plugin_instance[0] != 0)
+               riemann_event_add_attribute (event, "plugin_instance",
+                               vl->plugin_instance);
+
+       riemann_event_add_attribute (event, "type", vl->type);
+       if (vl->type_instance[0] != 0)
+               riemann_event_add_attribute (event, "type_instance",
+                               vl->type_instance);
+
+       if ((ds->ds[index].type != DS_TYPE_GAUGE) && (rates != NULL))
+       {
+               char ds_type[DATA_MAX_NAME_LEN];
+
+               ssnprintf (ds_type, sizeof (ds_type), "%s:rate",
+                               DS_TYPE_TO_STRING(ds->ds[index].type));
+               riemann_event_add_attribute (event, "ds_type", ds_type);
+       }
+       else
+       {
+               riemann_event_add_attribute (event, "ds_type",
+                               DS_TYPE_TO_STRING(ds->ds[index].type));
+       }
+       riemann_event_add_attribute (event, "ds_name", ds->ds[index].name);
+       {
+               char ds_index[DATA_MAX_NAME_LEN];
+
+               ssnprintf (ds_index, sizeof (ds_index), "%zu", index);
+               riemann_event_add_attribute (event, "ds_index", ds_index);
+       }
+
+       for (i = 0; i < riemann_attrs_num; i += 2)
+               riemann_event_add_attribute(event,
+                                           riemann_attrs[i],
+                                           riemann_attrs[i +1]);
+
+       for (i = 0; i < riemann_tags_num; i++)
+               riemann_event_add_tag (event, riemann_tags[i]);
+
+       if (ds->ds[index].type == DS_TYPE_GAUGE)
+       {
+               event->has_metric_d = 1;
+               event->metric_d = (double) vl->values[index].gauge;
+       }
+       else if (rates != NULL)
+       {
+               event->has_metric_d = 1;
+               event->metric_d = (double) rates[index];
+       }
+       else
+       {
+               event->has_metric_sint64 = 1;
+               if (ds->ds[index].type == DS_TYPE_DERIVE)
+                       event->metric_sint64 = (int64_t) vl->values[index].derive;
+               else if (ds->ds[index].type == DS_TYPE_ABSOLUTE)
+                       event->metric_sint64 = (int64_t) vl->values[index].absolute;
+               else
+                       event->metric_sint64 = (int64_t) vl->values[index].counter;
+       }
+
+       format_name (name_buffer, sizeof (name_buffer),
+                       /* host = */ "", vl->plugin, vl->plugin_instance,
+                       vl->type, vl->type_instance);
+       if (host->always_append_ds || (ds->ds_num > 1))
+               ssnprintf (service_buffer, sizeof (service_buffer),
+                               "%s/%s", &name_buffer[1], ds->ds[index].name);
+       else
+               sstrncpy (service_buffer, &name_buffer[1],
+                               sizeof (service_buffer));
+
+       event->service = strdup (service_buffer);
+
+       DEBUG ("write_riemann plugin: Successfully created protobuf for metric: "
+                       "host = \"%s\", service = \"%s\"",
+                       event->host, event->service);
+       return (event);
+} /* }}} Event *riemann_value_to_protobuf */
+
+static Msg *riemann_value_list_to_protobuf (struct riemann_host const *host, /* {{{ */
+                                           data_set_t const *ds,
+                                           value_list_t const *vl,
+                                           int *statuses)
+{
+       Msg *msg;
+       size_t i;
+       gauge_t *rates = NULL;
+
+       /* Initialize the Msg structure. */
+       msg = malloc (sizeof (*msg));
+       if (msg == NULL)
+       {
+               ERROR ("write_riemann plugin: malloc failed.");
+               return (NULL);
+       }
+       memset (msg, 0, sizeof (*msg));
+       msg__init (msg);
+
+       /* Set up events. First, the list of pointers. */
+       msg->n_events = (size_t) vl->values_len;
+       msg->events = calloc (msg->n_events, sizeof (*msg->events));
+       if (msg->events == NULL)
+       {
+               ERROR ("write_riemann plugin: calloc failed.");
+               riemann_msg_protobuf_free (msg);
+               return (NULL);
+       }
+
+       if (host->store_rates)
+       {
+               rates = uc_get_rate (ds, vl);
+               if (rates == NULL)
+               {
+                       ERROR ("write_riemann plugin: uc_get_rate failed.");
+                       riemann_msg_protobuf_free (msg);
+                       return (NULL);
+               }
+       }
+
+       for (i = 0; i < msg->n_events; i++)
+       {
+               msg->events[i] = riemann_value_to_protobuf (host, ds, vl,
+                                                           (int) i, rates, statuses[i]);
+               if (msg->events[i] == NULL)
+               {
+                       riemann_msg_protobuf_free (msg);
+                       sfree (rates);
+                       return (NULL);
+               }
+       }
+
+       sfree (rates);
+       return (msg);
+} /* }}} Msg *riemann_value_list_to_protobuf */
+
+static int riemann_notification(const notification_t *n, user_data_t *ud) /* {{{ */
+{
+       int                      status;
+       struct riemann_host     *host = ud->data;
+       Msg                     *msg;
+
+    if (!host->notifications)
+        return 0;
+
+       msg = riemann_notification_to_protobuf (host, n);
+       if (msg == NULL)
+               return (-1);
+
+       status = riemann_send (host, msg);
+       if (status != 0)
+               ERROR ("write_riemann plugin: riemann_send failed with status %i",
+                               status);
+
+       riemann_msg_protobuf_free (msg);
+       return (status);
+} /* }}} int riemann_notification */
+
+static int riemann_write(const data_set_t *ds, /* {{{ */
+             const value_list_t *vl,
+             user_data_t *ud)
+{
+       int                      status;
+       int                      statuses[vl->values_len];
+       struct riemann_host     *host = ud->data;
+       Msg                     *msg;
+
+    if (host->check_thresholds)
+        write_riemann_threshold_check(ds, vl, statuses);
+       msg = riemann_value_list_to_protobuf (host, ds, vl, statuses);
+       if (msg == NULL)
+               return (-1);
+
+       status = riemann_send (host, msg);
+       if (status != 0)
+               ERROR ("write_riemann plugin: riemann_send failed with status %i",
+                               status);
+
+       riemann_msg_protobuf_free (msg);
+       return status;
+} /* }}} int riemann_write */
+
+static void riemann_free(void *p) /* {{{ */
+{
+       struct riemann_host     *host = p;
+
+       if (host == NULL)
+               return;
+
+       pthread_mutex_lock (&host->lock);
+
+       host->reference_count--;
+       if (host->reference_count > 0)
+       {
+               pthread_mutex_unlock (&host->lock);
+               return;
+       }
+
+       riemann_disconnect (host);
+
+       sfree(host->service);
+       pthread_mutex_destroy (&host->lock);
+       sfree(host);
+} /* }}} void riemann_free */
+
+static int riemann_config_node(oconfig_item_t *ci) /* {{{ */
+{
+       struct riemann_host     *host = NULL;
+       int                      status = 0;
+       int                      i;
+       oconfig_item_t          *child;
+       char                     callback_name[DATA_MAX_NAME_LEN];
+       user_data_t              ud;
+
+       if ((host = calloc(1, sizeof (*host))) == NULL) {
+               ERROR ("write_riemann plugin: calloc failed.");
+               return ENOMEM;
+       }
+       pthread_mutex_init (&host->lock, NULL);
+       host->reference_count = 1;
+       host->node = NULL;
+       host->service = NULL;
+    host->notifications = 1;
+    host->check_thresholds = 0;
+       host->store_rates = 1;
+       host->always_append_ds = 0;
+       host->use_tcp = 0;
+       host->ttl_factor = RIEMANN_TTL_FACTOR;
+
+       status = cf_util_get_string (ci, &host->name);
+       if (status != 0) {
+               WARNING("write_riemann plugin: Required host name is missing.");
+               riemann_free (host);
+               return -1;
+       }
+
+       for (i = 0; i < ci->children_num; i++) {
+               /*
+                * The code here could be simplified but makes room
+                * for easy adding of new options later on.
+                */
+               child = &ci->children[i];
+               status = 0;
+
+               if (strcasecmp ("Host", child->key) == 0) {
+                       status = cf_util_get_string (child, &host->node);
+                       if (status != 0)
+                               break;
+        } else if (strcasecmp ("Notifications", child->key) == 0) {
+            status = cf_util_get_boolean(child, &host->notifications);
+            if (status != 0)
+                break;
+        } else if (strcasecmp ("CheckThresholds", child->key) == 0) {
+            status = cf_util_get_boolean(child, &host->check_thresholds);
+            if (status != 0)
+                break;
+               } else if (strcasecmp ("Port", child->key) == 0) {
+                       status = cf_util_get_service (child, &host->service);
+                       if (status != 0) {
+                               ERROR ("write_riemann plugin: Invalid argument "
+                                               "configured for the \"Port\" "
+                                               "option.");
+                               break;
+                       }
+               } else if (strcasecmp ("Protocol", child->key) == 0) {
+                       char tmp[16];
+                       status = cf_util_get_string_buffer (child,
+                                       tmp, sizeof (tmp));
+                       if (status != 0)
+                       {
+                               ERROR ("write_riemann plugin: cf_util_get_"
+                                               "string_buffer failed with "
+                                               "status %i.", status);
+                               break;
+                       }
+
+                       if (strcasecmp ("UDP", tmp) == 0)
+                               host->use_tcp = 0;
+                       else if (strcasecmp ("TCP", tmp) == 0)
+                               host->use_tcp = 1;
+                       else
+                               WARNING ("write_riemann plugin: The value "
+                                               "\"%s\" is not valid for the "
+                                               "\"Protocol\" option. Use "
+                                               "either \"UDP\" or \"TCP\".",
+                                               tmp);
+               } else if (strcasecmp ("StoreRates", child->key) == 0) {
+                       status = cf_util_get_boolean (child, &host->store_rates);
+                       if (status != 0)
+                               break;
+               } else if (strcasecmp ("AlwaysAppendDS", child->key) == 0) {
+                       status = cf_util_get_boolean (child,
+                                       &host->always_append_ds);
+                       if (status != 0)
+                               break;
+               } else if (strcasecmp ("TTLFactor", child->key) == 0) {
+                       double tmp = NAN;
+                       status = cf_util_get_double (child, &tmp);
+                       if (status != 0)
+                               break;
+                       if (tmp >= 2.0) {
+                               host->ttl_factor = tmp;
+                       } else if (tmp >= 1.0) {
+                               NOTICE ("write_riemann plugin: The configured "
+                                               "TTLFactor is very small "
+                                               "(%.1f). A value of 2.0 or "
+                                               "greater is recommended.",
+                                               tmp);
+                               host->ttl_factor = tmp;
+                       } else if (tmp > 0.0) {
+                               WARNING ("write_riemann plugin: The configured "
+                                               "TTLFactor is too small to be "
+                                               "useful (%.1f). I'll use it "
+                                               "since the user knows best, "
+                                               "but under protest.",
+                                               tmp);
+                               host->ttl_factor = tmp;
+                       } else { /* zero, negative and NAN */
+                               ERROR ("write_riemann plugin: The configured "
+                                               "TTLFactor is invalid (%.1f).",
+                                               tmp);
+                       }
+               } else {
+                       WARNING("write_riemann plugin: ignoring unknown config "
+                               "option: \"%s\"", child->key);
+               }
+       }
+       if (status != 0) {
+               riemann_free (host);
+               return status;
+       }
+
+       ssnprintf (callback_name, sizeof (callback_name), "write_riemann/%s",
+                       host->name);
+       ud.data = host;
+       ud.free_func = riemann_free;
+
+       pthread_mutex_lock (&host->lock);
+
+       status = plugin_register_write (callback_name, riemann_write, &ud);
+       if (status != 0)
+               WARNING ("write_riemann plugin: plugin_register_write (\"%s\") "
+                               "failed with status %i.",
+                               callback_name, status);
+       else /* success */
+               host->reference_count++;
+
+       status = plugin_register_notification (callback_name,
+                       riemann_notification, &ud);
+       if (status != 0)
+               WARNING ("write_riemann plugin: plugin_register_notification (\"%s\") "
+                               "failed with status %i.",
+                               callback_name, status);
+       else /* success */
+               host->reference_count++;
+
+       if (host->reference_count <= 1)
+       {
+               /* Both callbacks failed => free memory.
+                * We need to unlock here, because riemann_free() will lock.
+                * This is not a race condition, because we're the only one
+                * holding a reference. */
+               pthread_mutex_unlock (&host->lock);
+               riemann_free (host);
+               return (-1);
+       }
+
+       host->reference_count--;
+       pthread_mutex_unlock (&host->lock);
+
+       return status;
+} /* }}} int riemann_config_node */
+
+static int riemann_config(oconfig_item_t *ci) /* {{{ */
+{
+       int              i;
+       oconfig_item_t  *child;
+       int              status;
+
+       for (i = 0; i < ci->children_num; i++)  {
+               child = &ci->children[i];
+
+               if (strcasecmp("Node", child->key) == 0) {
+                       riemann_config_node (child);
+               } else if (strcasecmp(child->key, "attribute") == 0) {
+                       char *key = NULL;
+                       char *val = NULL;
+
+                       if (child->values_num != 2) {
+                               WARNING("riemann attributes need both a key and a value.");
+                               return (-1);
+                       }
+                       if (child->values[0].type != OCONFIG_TYPE_STRING ||
+                           child->values[1].type != OCONFIG_TYPE_STRING) {
+                               WARNING("riemann attribute needs string arguments.");
+                               return (-1);
+                       }
+                       if ((key = strdup(child->values[0].value.string)) == NULL) {
+                               WARNING("cannot allocate memory for attribute key.");
+                               return (-1);
+                       }
+                       if ((val = strdup(child->values[1].value.string)) == NULL) {
+                               WARNING("cannot allocate memory for attribute value.");
+                               return (-1);
+                       }
+                       strarray_add(&riemann_attrs, &riemann_attrs_num, key);
+                       strarray_add(&riemann_attrs, &riemann_attrs_num, val);
+                       DEBUG("write_riemann: got attr: %s => %s", key, val);
+                       sfree(key);
+                       sfree(val);
+               } else if (strcasecmp(child->key, "tag") == 0) {
+                       char *tmp = NULL;
+                       status = cf_util_get_string(child, &tmp);
+                       if (status != 0)
+                               continue;
+
+                       strarray_add (&riemann_tags, &riemann_tags_num, tmp);
+                       DEBUG("write_riemann plugin: Got tag: %s", tmp);
+                       sfree (tmp);
+               } else {
+                       WARNING ("write_riemann plugin: Ignoring unknown "
+                                "configuration option \"%s\" at top level.",
+                                child->key);
+               }
+       }
+    return 0;
+} /* }}} int riemann_config */
+
+void module_register(void)
+{
+       plugin_register_complex_config ("write_riemann", riemann_config);
+}
+
+/* vim: set sw=8 sts=8 ts=8 noet : */
diff --git a/src/write_riemann_threshold.c b/src/write_riemann_threshold.c
new file mode 100644 (file)
index 0000000..6d5af03
--- /dev/null
@@ -0,0 +1,242 @@
+/**
+ * collectd - src/threshold.c
+ * Copyright (C) 2007-2010  Florian Forster
+ * Copyright (C) 2008-2009  Sebastian Harl
+ * Copyright (C) 2009       Andrés J. Díaz
+ * Copyright (C) 2014       Pierre-Yves Ritschard
+ *
+ * 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:
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ *   Florian octo Forster <octo at collectd.org>
+ *   Sebastian Harl <sh at tokkee.org>
+ *   Andrés J. Díaz <ajdiaz at connectical.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_avltree.h"
+#include "utils_cache.h"
+#include "utils_threshold.h"
+
+#include <assert.h>
+#include <ltdl.h>
+#include <pthread.h>
+
+/*
+ * Threshold management
+ * ====================
+ * The following functions add, delete, etc. configured thresholds to
+ * the underlying AVL trees.
+ */
+
+/*
+ * int ut_check_one_data_source
+ *
+ * Checks one data source against the given threshold configuration. If the
+ * `DataSource' option is set in the threshold, and the name does NOT match,
+ * `okay' is returned. If the threshold does match, its failure and warning
+ * min and max values are checked and `failure' or `warning' is returned if
+ * appropriate.
+ * Does not fail.
+ */
+static int ut_check_one_data_source (const data_set_t *ds,
+    const value_list_t __attribute__((unused)) *vl,
+    const threshold_t *th,
+    const gauge_t *values,
+    int ds_index)
+{ /* {{{ */
+  const char *ds_name;
+  int is_warning = 0;
+  int is_failure = 0;
+  int prev_state = STATE_OKAY;
+
+  /* check if this threshold applies to this data source */
+  if (ds != NULL)
+  {
+    ds_name = ds->ds[ds_index].name;
+    if ((th->data_source[0] != 0)
+       && (strcmp (ds_name, th->data_source) != 0))
+      return (STATE_OKAY);
+  }
+
+  if ((th->flags & UT_FLAG_INVERT) != 0)
+  {
+    is_warning--;
+    is_failure--;
+  }
+
+  /* XXX: This is an experimental code, not optimized, not fast, not reliable,
+   * and probably, do not work as you expect. Enjoy! :D */
+  if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
+  {
+    switch(prev_state)
+    {
+      case STATE_ERROR:
+       if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
+            (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
+         return (STATE_OKAY);
+       else
+         is_failure++;
+      case STATE_WARNING:
+       if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
+            (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
+         return (STATE_OKAY);
+       else
+         is_warning++;
+     }
+  }
+  else { /* no hysteresis */
+    if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
+       || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
+      is_failure++;
+
+    if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
+       || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
+      is_warning++;
+ }
+
+  if (is_failure != 0)
+    return (STATE_ERROR);
+
+  if (is_warning != 0)
+    return (STATE_WARNING);
+
+  return (STATE_OKAY);
+} /* }}} int ut_check_one_data_source */
+
+/*
+ * int ut_check_one_threshold
+ *
+ * Checks all data sources of a value list against the given threshold, using
+ * the ut_check_one_data_source function above. Returns the worst status,
+ * which is `okay' if nothing has failed.
+ * Returns less than zero if the data set doesn't have any data sources.
+ */
+static int ut_check_one_threshold (const data_set_t *ds,
+    const value_list_t *vl,
+    const threshold_t *th,
+    const gauge_t *values,
+    int *statuses)
+{ /* {{{ */
+  int ret = -1;
+  int i;
+  int status;
+  gauge_t values_copy[ds->ds_num];
+
+  memcpy (values_copy, values, sizeof (values_copy));
+
+  if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
+  {
+    int num = 0;
+    gauge_t sum=0.0;
+
+    if (ds->ds_num == 1)
+    {
+      WARNING ("ut_check_one_threshold: The %s type has only one data "
+          "source, but you have configured to check this as a percentage. "
+          "That doesn't make much sense, because the percentage will always "
+          "be 100%%!", ds->type);
+    }
+
+    /* Prepare `sum' and `num'. */
+    for (i = 0; i < ds->ds_num; i++)
+      if (!isnan (values[i]))
+      {
+        num++;
+       sum += values[i];
+      }
+
+    if ((num == 0) /* All data sources are undefined. */
+        || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
+    {
+      for (i = 0; i < ds->ds_num; i++)
+        values_copy[i] = NAN;
+    }
+    else /* We can actually calculate the percentage. */
+    {
+      for (i = 0; i < ds->ds_num; i++)
+        values_copy[i] = 100.0 * values[i] / sum;
+    }
+  } /* if (UT_FLAG_PERCENTAGE) */
+
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    status = ut_check_one_data_source (ds, vl, th, values_copy, i);
+    if (status != -1) {
+           ret = 0;
+           if (statuses[i] < status)
+                   statuses[i] = status;
+    }
+  } /* for (ds->ds_num) */
+
+  return (ret);
+} /* }}} int ut_check_one_threshold */
+
+/*
+ * int ut_check_threshold
+ *
+ * Gets a list of matching thresholds and searches for the worst status by one
+ * of the thresholds. Then reports that status using the ut_report_state
+ * function above.
+ * Returns zero on success and if no threshold has been configured. Returns
+ * less than zero on failure.
+ */
+int write_riemann_threshold_check (const data_set_t *ds, const value_list_t *vl,
+                                  int *statuses)
+{ /* {{{ */
+  threshold_t *th;
+  gauge_t *values;
+  int status;
+
+  memset(statuses, 0, vl->values_len * sizeof(*statuses));
+  if (threshold_tree == NULL)
+         return 0;
+
+  /* Is this lock really necessary? So far, thresholds are only inserted at
+   * startup. -octo */
+  pthread_mutex_lock (&threshold_lock);
+  th = threshold_search (vl);
+  pthread_mutex_unlock (&threshold_lock);
+  if (th == NULL)
+         return (0);
+
+  DEBUG ("ut_check_threshold: Found matching threshold(s)");
+
+  values = uc_get_rate (ds, vl);
+  if (values == NULL)
+         return (0);
+
+  while (th != NULL)
+  {
+    status = ut_check_one_threshold (ds, vl, th, values, statuses);
+    if (status < 0)
+    {
+      ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
+      sfree (values);
+      return (-1);
+    }
+
+    th = th->next;
+  } /* while (th) */
+
+  sfree (values);
+
+  return (0);
+} /* }}} int ut_check_threshold */
+
+
+/* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */
index 046b7b0..96ffc54 100644 (file)
@@ -2,6 +2,7 @@
  * collectd - src/zfs_arc.c
  * Copyright (C) 2009  Anthony Dewhurst
  * Copyright (C) 2012  Aurelien Rougemont
+ * Copyright (C) 2013  Xin Li
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -19,6 +20,7 @@
  * Authors:
  *   Anthony Dewhurst <dewhurst at gmail>
  *   Aurelien Rougemont <beorn at gandi.net>
+ *   Xin Li <delphij at FreeBSD.org>
  **/
 
 #include "collectd.h"
  * Global variables
  */
 
+#if !defined(__FreeBSD__)
 extern kstat_ctl_t *kc;
 
+static long long get_zfs_value(kstat_t *ksp, char *name)
+{
+
+       return (get_kstat_value(ksp, name));
+}
+#else
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+const char zfs_arcstat[] = "kstat.zfs.misc.arcstats.";
+
+#if !defined(kstat_t)
+typedef void kstat_t;
+#endif
+
+static long long get_zfs_value(kstat_t *dummy __attribute__((unused)),
+               char const *name)
+{
+       char buffer[256];
+       long long value;
+       size_t valuelen = sizeof(value);
+       int rv;
+
+       ssnprintf (buffer, sizeof (buffer), "%s%s", zfs_arcstat, name);
+       rv = sysctlbyname (buffer, (void *) &value, &valuelen,
+                       /* new value = */ NULL, /* new length = */ (size_t) 0);
+       if (rv == 0)
+               return (value);
+
+       return (-1);
+}
+#endif
+
 static void za_submit (const char* type, const char* type_instance, value_t* values, int values_len)
 {
        value_list_t vl = VALUE_LIST_INIT;
@@ -60,10 +96,10 @@ static int za_read_derive (kstat_t *ksp, const char *kstat_value,
   long long tmp;
   value_t v;
 
-  tmp = get_kstat_value (ksp, (char *)kstat_value);
+  tmp = get_zfs_value (ksp, (char *)kstat_value);
   if (tmp == -1LL)
   {
-    ERROR ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
+    WARNING ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
     return (-1);
   }
 
@@ -78,10 +114,10 @@ static int za_read_gauge (kstat_t *ksp, const char *kstat_value,
   long long tmp;
   value_t v;
 
-  tmp = get_kstat_value (ksp, (char *)kstat_value);
+  tmp = get_zfs_value (ksp, (char *)kstat_value);
   if (tmp == -1LL)
   {
-    ERROR ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
+    WARNING ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
     return (-1);
   }
 
@@ -111,27 +147,31 @@ static int za_read (void)
        value_t  l2_io[2];
        kstat_t  *ksp   = NULL;
 
+#if !defined(__FreeBSD__)
        get_kstat (&ksp, "zfs", 0, "arcstats");
        if (ksp == NULL)
        {
                ERROR ("zfs_arc plugin: Cannot find zfs:0:arcstats kstat.");
                return (-1);
        }
+#endif
 
        /* Sizes */
        za_read_gauge (ksp, "size",    "cache_size", "arc");
        za_read_gauge (ksp, "l2_size", "cache_size", "L2");
 
-        /* Operations */
-       za_read_derive (ksp, "allocated","cache_operation", "allocated");
+       /* Operations */
        za_read_derive (ksp, "deleted",  "cache_operation", "deleted");
+#if __FreeBSD__
+       za_read_derive (ksp, "allocated","cache_operation", "allocated");
        za_read_derive (ksp, "stolen",   "cache_operation", "stolen");
+#endif
 
-        /* Issue indicators */
-        za_read_derive (ksp, "mutex_miss", "mutex_operation", "miss");
+       /* Issue indicators */
+       za_read_derive (ksp, "mutex_miss", "mutex_operations", "miss");
        za_read_derive (ksp, "hash_collisions", "hash_collisions", "");
        
-        /* Evictions */
+       /* Evictions */
        za_read_derive (ksp, "evict_l2_cached",     "cache_eviction", "cached");
        za_read_derive (ksp, "evict_l2_eligible",   "cache_eviction", "eligible");
        za_read_derive (ksp, "evict_l2_ineligible", "cache_eviction", "ineligible");
@@ -147,17 +187,17 @@ static int za_read (void)
        za_read_derive (ksp, "prefetch_metadata_misses", "cache_result", "prefetch_metadata-miss");
 
        /* Ratios */
-       arc_hits   = (gauge_t) get_kstat_value(ksp, "hits");
-       arc_misses = (gauge_t) get_kstat_value(ksp, "misses");
-       l2_hits    = (gauge_t) get_kstat_value(ksp, "l2_hits");
-       l2_misses  = (gauge_t) get_kstat_value(ksp, "l2_misses");
+       arc_hits   = (gauge_t) get_zfs_value(ksp, "hits");
+       arc_misses = (gauge_t) get_zfs_value(ksp, "misses");
+       l2_hits    = (gauge_t) get_zfs_value(ksp, "l2_hits");
+       l2_misses  = (gauge_t) get_zfs_value(ksp, "l2_misses");
 
        za_submit_ratio ("arc", arc_hits, arc_misses);
        za_submit_ratio ("L2", l2_hits, l2_misses);
 
        /* I/O */
-       l2_io[0].derive = get_kstat_value(ksp, "l2_read_bytes");
-       l2_io[1].derive = get_kstat_value(ksp, "l2_write_bytes");
+       l2_io[0].derive = get_zfs_value(ksp, "l2_read_bytes");
+       l2_io[1].derive = get_zfs_value(ksp, "l2_write_bytes");
 
        za_submit ("io_octets", "L2", l2_io, /* num values = */ 2);
 
@@ -166,12 +206,14 @@ static int za_read (void)
 
 static int za_init (void) /* {{{ */
 {
+#if !defined(__FreeBSD__)
        /* kstats chain already opened by update_kstat (using *kc), verify everything went fine. */
        if (kc == NULL)
        {
                ERROR ("zfs_arc plugin: kstat chain control structure not available.");
                return (-1);
        }
+#endif
 
        return (0);
 } /* }}} int za_init */
index 7245857..e89e711 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-DEFAULT_VERSION="5.2.0.git"
+DEFAULT_VERSION="5.4.0.git"
 
 VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"