Merge branch 'collectd-4.10' into collectd-5.3
authorMarc Fournier <marc.fournier@camptocamp.com>
Mon, 24 Nov 2014 08:18:44 +0000 (09:18 +0100)
committerMarc Fournier <marc.fournier@camptocamp.com>
Mon, 24 Nov 2014 08:18:44 +0000 (09:18 +0100)
224 files changed:
.gitignore
AUTHORS
ChangeLog
README
bindings/Makefile.am
bindings/java/org/collectd/api/CollectdFlushInterface.java
bindings/java/org/collectd/api/ValueList.java
bindings/java/org/collectd/java/GenericJMXConfConnection.java
bindings/java/org/collectd/java/GenericJMXConfMBean.java
bindings/java/org/collectd/java/GenericJMXConfValue.java
bindings/perl/lib/Collectd.pm
bindings/perl/lib/Collectd/Plugins/OpenVZ.pm
bindings/perl/uninstall_mod.pl [new file with mode: 0644]
clean.sh
configure.in
contrib/README
contrib/collectd.service [new file with mode: 0644]
contrib/collectd_network.py
contrib/collection3/bin/graph.cgi
contrib/collection3/bin/index.cgi
contrib/collection3/etc/collection.conf
contrib/collection3/lib/Collectd/Graph/Common.pm
contrib/collection3/lib/Collectd/Graph/TypeLoader.pm
contrib/exec-ksm.sh [new file with mode: 0755]
contrib/exec-munin.px
contrib/exec-nagios.px
contrib/exec-smartctl
contrib/migrate-4-5.px [new file with mode: 0755]
contrib/php-collection/definitions.php
contrib/postgresql/collectd_insert.sql [new file with mode: 0644]
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 [new file with mode: 0644]
src/amqp.c [new file with mode: 0644]
src/apache.c
src/apcups.c
src/bind.c
src/collectd-email.pod
src/collectd-exec.pod
src/collectd-java.pod
src/collectd-nagios.c
src/collectd-nagios.pod
src/collectd-perl.pod
src/collectd-python.pod
src/collectd-snmp.pod
src/collectd-tg.c [new file with mode: 0644]
src/collectd-tg.pod [new file with mode: 0644]
src/collectd-threshold.pod [new file with mode: 0644]
src/collectd-unixsock.pod
src/collectd.c
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.h
src/collectd.pod
src/collectdctl.c [new file with mode: 0644]
src/collectdctl.pod [new file with mode: 0644]
src/collectdmon.pod
src/common.c
src/common.h
src/configfile.c
src/configfile.h
src/contextswitch.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/email.c
src/ethstat.c [new file with mode: 0644]
src/exec.c
src/filter_chain.c
src/fscache.c
src/gmond.c
src/hddtemp.c
src/interface.c
src/ipmi.c
src/iptables.c
src/ipvs.c
src/irq.c
src/java.c
src/libcollectdclient/Makefile.am
src/libcollectdclient/client.c
src/libcollectdclient/client.h [deleted file]
src/libcollectdclient/collectd/client.h [new file with mode: 0644]
src/libcollectdclient/collectd/lcc_features.h.in [new file with mode: 0644]
src/libcollectdclient/collectd/network.h [new file with mode: 0644]
src/libcollectdclient/collectd/network_buffer.h [new file with mode: 0644]
src/libcollectdclient/lcc_features.h.in [deleted file]
src/libcollectdclient/network.c [new file with mode: 0644]
src/libcollectdclient/network_buffer.c [new file with mode: 0644]
src/liboconfig/parser.y
src/libvirt.c
src/logfile.c
src/lpar.c [new file with mode: 0644]
src/madwifi.c
src/match_timediff.c
src/md.c [new file with mode: 0644]
src/memcachec.c
src/memcached.c
src/meta_data.c
src/modbus.c
src/mysql.c
src/netapp.c
src/netlink.c
src/network.c
src/network.h
src/nfs.c
src/nginx.c
src/notify_email.c
src/ntpd.c
src/numa.c [new file with mode: 0644]
src/onewire.c
src/openvpn.c
src/oracle.c
src/owniptc/Makefile.am [deleted file]
src/owniptc/README.collectd [deleted file]
src/owniptc/ipt_kernel_headers.h [deleted file]
src/owniptc/libip4tc.c [deleted file]
src/owniptc/libip6tc.c [deleted file]
src/owniptc/libip6tc.h [deleted file]
src/owniptc/libiptc.c [deleted file]
src/owniptc/libiptc.h [deleted file]
src/owniptc/libxtc.h [deleted file]
src/owniptc/linux_list.h [deleted file]
src/owniptc/xtables.h [deleted file]
src/perl.c
src/pf.c [new file with mode: 0644]
src/pinba.c
src/ping.c
src/plugin.c
src/plugin.h
src/postgresql.c
src/postgresql_default.conf
src/powerdns.c
src/processes.c
src/protocols.c
src/python.c
src/pyvalues.c
src/redis.c [new file with mode: 0644]
src/riemann.proto [new file with mode: 0644]
src/routeros.c
src/rrdcached.c
src/rrdtool.c
src/sensors.c
src/serial.c
src/snmp.c
src/swap.c
src/syslog.c
src/tail_csv.c [new file with mode: 0644]
src/tape.c
src/target_notification.c
src/target_replace.c
src/target_scale.c
src/target_set.c
src/target_v5upgrade.c [new file with mode: 0644]
src/tcpconns.c
src/teamspeak2.c
src/ted.c
src/thermal.c
src/threshold.c [new file with mode: 0644]
src/types.db
src/types.db.pod
src/types_list.c
src/unixsock.c
src/uptime.c
src/utils_avltree.c
src/utils_avltree.h
src/utils_cache.c
src/utils_cache.h
src/utils_cmd_flush.c
src/utils_cmd_listval.c
src/utils_cmd_putnotif.c
src/utils_cmd_putval.c
src/utils_cmd_putval.h
src/utils_complain.c
src/utils_complain.h
src/utils_db_query.c
src/utils_db_query.h
src/utils_dns.c
src/utils_dns.h
src/utils_format_graphite.c [new file with mode: 0644]
src/utils_format_graphite.h [new file with mode: 0644]
src/utils_format_json.c
src/utils_match.c
src/utils_match.h
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_threshold.c [deleted file]
src/utils_threshold.h [deleted file]
src/utils_time.c [new file with mode: 0644]
src/utils_time.h [new file with mode: 0644]
src/utils_vl_lookup.c [new file with mode: 0644]
src/utils_vl_lookup.h [new file with mode: 0644]
src/utils_vl_lookup_test.c [new file with mode: 0644]
src/uuid.c
src/varnish.c [new file with mode: 0644]
src/vmem.c
src/vserver.c
src/write_graphite.c [new file with mode: 0644]
src/write_http.c
src/write_mongodb.c [new file with mode: 0644]
src/write_redis.c [new file with mode: 0644]
src/write_riemann.c [new file with mode: 0644]
src/zfs_arc.c
version-gen.sh

index b85bdec..6e87aaf 100644 (file)
@@ -49,6 +49,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
@@ -66,3 +69,9 @@ bindings/java/org/collectd/java/*.class
 
 # python stuff
 *.pyc
+
+# tag stuff
+src/tags
+
+# backup stuff
+*~
diff --git a/AUTHORS b/AUTHORS
index f090840..45645d1 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -15,6 +15,9 @@ Sebastian "tokkee" Harl <sh at tokkee.org>
 Contributors (sorted alphabetically)
 ====================================
 
+Akkarit Sangpetch <asangpet at andrew.cmu.edu>
+ - write_mongodb plugin.
+
 Alessandro Iurlano <alessandro.iurlano at gmail.com>
  - Initial filecount plugin.
 
@@ -34,16 +37,26 @@ Anthony Gialluca <tonyabg at charter.net>
 Antony Dovgal <tony at daylessday.org>
  - memcached plugin.
 
+Aurélien Reynaud <collectd at wattapower.net>
+ - LPAR plugin.
+ - Various fixes for AIX, HP-UX and Solaris.
+
 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.
 
+Chris Lundquist <clundquist at bluebox.net>
+ - Improvements to the write_mongodb plugin.
+
 Christophe Kalt <collectd at klb.taranis.org>
  - The version 3 `log' mode.
  - Many Solaris related hints and fixes.
 
+Cyril Feraudet <cyril at feraudet.com>
+ - ethstat plugin.
+
 Dan Berrange <berrange at redhat.com>
  - uuid plugin.
 
@@ -75,6 +88,12 @@ Franck Lombardi
 Jason Pepas <cell at ices.utexas.edu>
  - nfs plugin.
 
+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.
@@ -102,6 +121,9 @@ Marco Chiappero <marco at absence.it>
  - ip6tables support in the iptables plugin.
  - openvpn plugin (support for more status file formats)
 
+Michael Hanselmann <public at hansmi.ch>
+ - md plugin.
+
 Michael Stapelberg <michael+git at stapelberg.de>
  - OpenBSD port of the tcpconns plugin.
 
@@ -146,6 +168,10 @@ 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.
+
 Piotr Hosowicz <the55 at wp.pl>
  - SMF manifest for collectd.
 
@@ -162,6 +188,12 @@ Rodolphe Quiédeville <rquiedeville at bearstech.com>
 Scott Garrett <sgarrett at technomancer.com>
  - tape plugin.
 
+Scott Sanders <scott at jssjr.com>
+ - Write-Graphite plugin.
+
+Sebastien Pahl <sebastien.pahl at dotcloud.com>
+ - AMQP plugin.
+
 Simon Kuhnle <simon at blarzwurst.de>
  - OpenBSD code for the cpu and memory plugins.
 
@@ -175,6 +207,9 @@ Sven Trenkel <collectd at semidefinite.de>
  - netapp plugin.
  - python plugin.
 
+Thomas Meson <zllak at hycik.org>
+ - Graphite support for the AMQP plugin.
+
 Tomasz Pala <gotar at pld-linux.org>
  - conntrack plugin.
 
index e8f73b5..acbf7bb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,551 @@
+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.
+       * collectd: The possibility to configure the collection interval on a
+         per-plugin basis has been added. Huge thanks to Sebastian Harl for
+         his work.
+       * collectd-tg: This new binary allows to generate random but real
+         looking collectd network traffic. This can be used to load-test new
+         plugin, for example.
+       * libcollectdclient: Code for constructing and sending network packets
+         in the binary format has been added.
+       * aggregation plugin: This new plugin allows to aggregate multiple
+         value lists into one.
+       * amqp and write_http plugins: Meta data is now included in the JSON
+         output format. Thanks to Mark Wong for the patch.
+       * amqp plugin: Support for "Graphite" output has been added. Thanks to
+         Thomas Meson for the patch.
+       * contextswitch plugin: Support for AIX has been added. Thanks to
+         Manuel Rozada for his patch.
+       * disk plugin: The "UseBSDName" config option has been added to the
+         Mac OS X version.
+       * GenericJMX plugin: Automatically determine the host name if it isn't
+         configured.
+       * libvirt plugin: The "number" interface format has been added. Thanks
+         to "Davide Guerri" for the patch.
+       * memcached plugin: Support for multiple connections has been added.
+         Thanks to Nicolas Szalay for the patch.
+       * ntpd plugin: The "IncludeUnitID" config option has been added. The
+         behavior when a peer is unreachable has been improved. Thanks to
+         Johan Kiviniemi for the patches.
+       * oracle plugin: The "Host" config option has been added.
+       * pf plugin: This new plugin allows to collect statistics from BSD's
+         packet filter "pf". Thanks to Pierre-Yves Ritschard and Stefan Rinkes
+         for their work.
+       * postgresql plugin: The "Instance" config option has been added.
+         Support for writing values to a PostgreSQL database has been added.
+         Thanks to Sebastian Harl for the patches.
+       * processes plugin: Support for Solaris has been added. Thanks to
+         Cosmin Ioiart for the patch.
+       * redis plugin: Support for authenticating via password has been added.
+         Thanks to biancalana for the patch.
+       * rrdcached plugin: The "HeartBeat", "RRARows", "RRATimespan",
+         "StepSize" and "XFF" config options have been added.
+       * swap plugin: The "ReportBytes" config option has been added. The AIX
+         version now also exports "reserved" pages and swap-in / swap-out
+         "traffic". Thanks to Manuel Rozada 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.
+       * collectd: Build issues have been fixed.
+       * collectd: An incorrect assertion has been fixed in some common code
+         for Solaris. This should resolve pseudo-random assertion failures
+         under Solaris. Thanks to Jeff Blane for his help debugging this.
+       * collectd: A couple of memory leaks through PThread thread attributes
+         have been fixed. Thanks to Gerrie Roos for fixing these.
+       * collectdctl: Fix PUTVAL for data sets with multiple data sources.
+         Thanks to Cyril Feraudet for reporting this problem.
+       * contrib/migrate-4-5.px: Handle to "df" to "df_complex" conversion
+         correctly.
+       * apcups plugin: Improve the reconnect behavior.
+       * curl_xml plugin: The "Host" setting was silently ignored. Thanks to
+         Fabien Wernli for fixing this.
+       * df plugin: Ignore "rootfs" devices under Linux to avoid having them
+         reported twice. Thanks to Brune Prémont for fixing this.
+       * disk plugin: Fix incorrect computation of read and write latency (the
+         "disk_time" type). Previously, the numbers reported where too small
+         by a factor of "interval", e.g. when the interval is set to 10
+         seconds, the values were too low by a factor of 10. Thanks to Manuel
+         Sanmartin for reporting this problem.
+       * dns plugin: A build issue under Solaris has been fixed. A erroneous
+         define that could lead to the reporting of bad data has been fixed by
+         Daniel Sutto.
+       * ethstat plugin: An off-by-one error and potential use of
+         uninitialized memory has been fixed. Thanks to Mark Voelker for
+         reporting these problems.
+       * memcachec plugin: A bug in the configuration handling has been fixed.
+         Thanks to Pascal Hofmann for fixing this issue.
+       * mysql plugin: Fix a bug when registering multiple databases. Thanks
+         to Sebastian Harl for fixing this.
+       * netapp plugin: Correctly close the connection on communication
+         errors.
+       * netlink plugin: The function used to query statistics has been
+         changed to be more in line with iproute2's behavior. Thanks to
+         "KIvosak" for the patch.
+       * network plugin: Initialization of libgcrypt has been fixed. Thanks to
+         Chris Lundquist for his patch.
+       * oracle plugin: Error messages have been improved.
+       * ping plugin: Don't enter the exponential back-off mode when
+         ping_send() fails. This should make recovery after a network failure
+         much faster.
+       * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert
+         and Sven Trenkel for fixing this.
+       * redis plugin: Fix a compilation problem on FreeBSD. Thanks to
+         "biancalana" for the fix.
+       * rrdtool plugin: Fix an out-of-bounds array access when printing a
+         warning message. Thanks to Will Hawkins for fixing this bug.
+       * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been
+         added. Support for more complex / unusual MIBs / subtrees has been
+         added. Thanks to Mark Juric to test the changes and point out these
+         problems.
+       * varnish plugin: Support for multiple instances of Varnish 3 has been
+         fixed. Thanks to Jonathan Huot for the patch.
+       * write_mongodb plugin: Add compatibility with libmongo 0.6.0 and
+         later. Thanks to Chris Lundquist for this patch.
+
+2012-04-01, Version 5.1.0
+       * Build system, iptables plugin: The shipped version of libiptc has
+         been removed.
+       * collectd-nagios: A list of value lists can now be queried using
+         "-n LIST". Thanks to Sebastian Harl for his patches.
+       * bind plugin: The "ParseTime" option has been added. It allows to use
+         the system time rather than the time reported by BIND.
+       * curl, memcachec, tail plugins: The "ExcludeRegexp" option has been
+         added. Thanks to Peter Warasin for his initial patch.
+       * ethstat plugin: The new "ethstat" plugin reads performance statistics
+         directly from ethernet cards. Thanks to Cyril Feraudet for his patch.
+       * GenericJMX plugin: Support for querying MBean "Operations" (in
+         addition to "Attributes") has been added. Thanks to Pierre-Yves
+         Ritschard for his patch.
+       * irq plugin: The selection / ignore code now uses the default
+         ignorelist infrastructure, providing the standard feature set, e.g.
+         regex matching.
+       * md plugin: The new "md" plugin reports the number of disks in various
+         states in Linux software RAID devices. Thanks to Michael Hanselmann
+         for his patch.
+       * modbus plugin: Support for signed integer register types has been
+         added.
+       * nfs plugin: Support for Solaris has been added. Thanks to Cosmin
+         Ioiart for his patch.
+       * numa plugin: The new "numa" plugin reports statistics of the
+         Non-Uniform Memory Access (NUMA) subsystem of Linux.
+       * processes plugin: Various fixes for the FreeBSD implementation.
+         Thanks to Phil Kulin for his patch.
+       * rrdcached plugin: Passing flushes to the caching daemon has been
+         added.
+       * sensors plugin: The initialization code has been improved. Thanks to
+         Henrique de Moraes Holschuh for his patch.
+       * swap plugin: The "ReportByDevice" option has been added.
+       * syslog plugin: Support for writing notifications has been added.
+         Thanks to Fabien Wernli for his patch.
+       * tcpconns plugin: Support for AIX has been added. Thanks to Manuel
+         Luis Sanmartín Rozada for his patch.
+       * threshold plugin: The "PersistOK" option has been added. Thanks to
+         Aaron Brady for his patch.
+       * varnish plugin: Support for Varnish 3.0 has been added. Thanks to
+         Jérôme Renard for his patches.
+       * write_mongodb plugin: The new "write_mongodb" plugin writes value
+         lists to MongoDB, a shema-less database. Thanks to Akkarit Sangpetch
+         and Chris Lundquist for their work.
+       * write_graphite plugin: The new "write_graphite" plugin writes value
+         lists to Carbon, the storage layer of the Graphite time-series
+         database. Thanks to Scott Sanders and Pierre-Yves Ritschard for their
+         work.
+       * zfs_arc plugin: Several new statistics have been added. Thanks to
+         Aurelien Rougemont for his patches.
+       * scale target: Support for scaling specific data sources only has been
+         added. Thanks to Gerrie Roos for his patch.
+
+2012-11-11, Version 5.0.5
+       * collectd: Create new directories with mode 0777 and let umask remove
+         unwanted permission bits.
+       * collectd: Build issues have been fixed.
+       * collectd: An incorrect assertion has been fixed in some common code
+         for Solaris. This should resolve pseudo-random assertion failures
+         under Solaris. Thanks to Jeff Blane for his help debugging this.
+       * collectd: A couple of memory leaks through PThread thread attributes
+         have been fixed. Thanks to Gerrie Roos for fixing these.
+       * collectdctl: Fix PUTVAL for data sets with multiple data sources.
+         Thanks to Cyril Feraudet for reporting this problem.
+       * contrib/migrate-4-5.px: Handle to "df" to "df_complex" conversion
+         correctly.
+       * apcups plugin: Improve the reconnect behavior.
+       * curl_xml plugin: The "Host" setting was silently ignored. Thanks to
+         Fabien Wernli for fixing this.
+       * df plugin: Ignore "rootfs" devices under Linux to avoid having them
+         reported twice. Thanks to Brune Prémont for fixing this.
+       * disk plugin: Fix incorrect computation of read and write latency (the
+         "disk_time" type). Previously, the numbers reported where too small
+         by a factor of "interval", e.g. when the interval is set to 10
+         seconds, the values were too low by a factor of 10. Thanks to Manuel
+         Sanmartin for reporting this problem.
+       * dns plugin: A build issue under Solaris has been fixed. A erroneous
+         define that could lead to the reporting of bad data has been fixed by
+         Daniel Sutto.
+       * memcachec plugin: A bug in the configuration handling has been fixed.
+         Thanks to Pascal Hofmann for fixing this issue.
+       * mysql plugin: Fix a bug when registering multiple databases. Thanks
+         to Sebastian Harl for fixing this.
+       * netapp plugin: Correctly close the connection on communication
+         errors.
+       * netlink plugin: The function used to query statistics has been
+         changed to be more in line with iproute2's behavior. Thanks to
+         "KIvosak" for the patch.
+       * network plugin: Initialization of libgcrypt has been fixed. Thanks to
+         Chris Lundquist for his patch.
+       * oracle plugin: Error messages have been improved.
+       * ping plugin: Don't enter the exponential back-off mode when
+         ping_send() fails. This should make recovery after a network failure
+         much faster.
+       * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert
+         and Sven Trenkel for fixing this.
+       * redis plugin: Fix a compilation problem on FreeBSD. Thanks to
+         "biancalana" for the fix.
+       * rrdtool plugin: Fix an out-of-bounds array access when printing a
+         warning message. Thanks to Will Hawkins for fixing this bug.
+       * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been
+         added. Support for more complex / unusual MIBs / subtrees has been
+         added. Thanks to Mark Juric to test the changes and point out these
+         problems.
+
+2012-04-01, Version 5.0.4
+       * Build system: Fix the use of a libltdl macro. Thanks to Clemens Lang
+         for fixing this. Adresses some issues with building the iptables
+         plugin under Gentoo.
+       * libcollectdclient: A memory leak in the lcc_getval() function has
+         been fixed. Thanks to Jason Schmidlapp for finding and fixing this
+         issue.
+       * bind plugin: The use of 'QType" types has been fixed.
+       * df plugin: Fixed compiler issue under Mac OS X 10.7.
+       * conntrack plugin: Support zero as legitimate value. Thanks to Louis
+         Opter for his patch.
+       * memcached plugin: Increased the size of a static buffer, which was
+         truncating status messages form memcached. Thanks to Timon for the
+         patch.
+       * network plugin: Forwarding of notifications has been disabled. This
+         was a contition not checked for before, which may retult in an
+         endless loop.
+       * processes plugin: Support for process names with spaces has been
+         added to the Linux implementation. Thanks to Darrell Bishop for his
+         patch.
+       * perl plugin: A race condition in several callbacks, including log and
+         write callbacks, has been fixed. Thanks to "Rrpv" for reporting this
+         bug.
+       * snmp plugin: A bug when casting unsigned integers to gauge values has
+         been fixed: Unsigned integers would be cast to a signed integer and
+         then to a gauge, possibly resulting in a negative value.
+       * tcpconns plugin: Compilation with newer versions of the FreeBSD
+         runtime has been fixed.
+
+2012-02-19, Version 5.0.3
+       * Build system: Fix problems when building the ipvs and iptables
+         plugins. Thanks to Sebastian Harl for his patch. A bashism in the
+         version-gen.sh script has been fixed. Thanks to Jo-Philipp Wich for
+         his patch.
+       * csv and rrdtool plugins: Print a more helpful error message when the
+         DataDir is a symlink pointing to a non-existing location. Thanks to
+         Jonathan Nieder for his patch.
+       * exec plugin: Fix a problem when using select(2) to read from file
+         handles. Thanks to Gerrie Roos for his patch.
+       * network plugin: An incorrect error message in the handling of the
+         "Interface" configuration option has been fixed. Thanks to Gerrie
+         Roos for his patch.
+       * oracle plugin: A potential endless loop in the error handling has
+         been fixed.
+       * python plugin: A crash bug in the configuration handling has been
+         fixed. Thanks to Sven Trenkel for his patch.
+       * interfaces plugin: The change which was supposed to ignore "bogus"
+         interfaces has been reverted, since it ignored legit interfaces, such
+         as bonding pseudo-devices as well.
+
+2012-01-21, Version 5.0.2
+       * curl_xml plugin: Fix handling of file:// and other URLs (which don't
+         follow HTTP status codes). Thanks to Fabien Wernli for his patch!
+       * df plugin: Fix handling of negative "available" counts. This can
+         occur with some file systems, for example UFS. Thanks to Toni Ylenius
+         for his patch.
+       * interface plugin: "mac" interfaces are now ignored on Solaris. These
+         pseudo-interfaces occur multiple times, causing warnings. Also switch
+         to 64-bit counters on Solaris, improving overflow behavior for
+         high-speed interfaces. Thanks to Eddy Geez and Fabien Wernli for
+         their patches.
+       * memory plugin: Account kernel and unused memory under Solaris. Thanks
+         to Fabien Wernli for his patch.
+       * network plugin: A bug in the interaction between the Network plugin
+         and filter chains has been fixed: When a filter modified a field such
+         as the hostname, subsequent values in the same network packets could
+         have ended up using the modified name rather than the original name.
+         Thanks to Sebastian Harl for identifying the problem.
+       * oracle plugin: A memory leak has been fixed in the parameter handling.
+       * python plugin: A memory leak has been fixed. Thanks to Sven Trenkel
+         for fixing this bug!
+
+2011-10-07, Version 5.0.1
+       * collectd: A mutex leak has been fixed in the meta data code. Thanks
+         to Rafal Lesniak for his patch.
+       * collectd: Compatibility fixes for GCC 4.6 have been applied. Thanks
+         to Peter Green for his patch.
+       * csv plugin: The line buffer size has been increased. Thanks to Colin
+         McCabe for the patch.
+       * curl_json plugin: Don't use the "parent" node to build the type
+         instance, if it is empty. Compatibility with libyajl 2 has been
+         added. Thanks to "spupykin" of the Arch Linux project for the initial
+         code. Formatting of time has been fixed in the JSON module.
+       * exec plugin: Fix the timestamp value passed to notification scripts.
+         Thanks to Alexander Kovalenko for fixing this.
+       * iptables plugin: Fix linking with some versions of libiptc.
+       * irq plugin: Fix support for interrupts under Linux. The old code
+         assumed that interrupts have a numeric value -- this is no longer
+         true for Linux. Thanks to Bostjan Skufca for implementing this.
+       * notify_desktop plugin: Compatibility with libnotify 0.7 has been
+         added. Thanks to Samuli Suominen for his patch.
+       * processes plugin: Fix handling of regular expressions containing
+         spaces. Thanks for Sebastian Harl for fixing this.
+       * rrdtool, rrdcached plugins: Improve precision of the XFF parameter.
+         Previously, values like 0.999 would have been rounded to 1.0. Thanks
+         to Francois-Xavier Bourlet for fixing this.
+       * varnish plugin: Fix data type handling of some metrics. Some values
+         were submitted as gauge even though they were derives.
+       * Various plugin: Set a multi-threading flag in libcurl. Thanks to Mike
+         Flisher for the fix.
+
+2011-03-28, Version 5.0.0
+       * collectd: The "FQDNLookup" option is now enabled by default.
+       * collectd: The internal representation of time has been changed to
+         allow a higher accuracy than one second.
+       * collectdcmd: This new command line utility can send various commands
+         to collectd using the UnixSock plugin. Thanks to Håkon Dugstad
+         Johnsen and Sebastian Harl for their code.
+       * collectd-nagios: The "-m" option has been implemented (treat NaNs as
+         critical).
+       * collectd-tg: Traffic generator creating bogus network traffic
+         compatible to the Network plugin. This utility can be used to
+         stress-test new write plugins and collectd in general.
+       * libcollectdclient: Creating and sending network packets has been
+         added to the collectd client library.
+       * All data sets: The data source name of all data sets with exactly
+         one data source has been changed to "value".
+       * All plugins: All "counter" data sources have been converted to
+         "derive" data sources. All plugins now use "derive" by default, but
+         plugins such as the network plugin can still handle "counter", of
+         course. The minimum value of all derive data sources is zero, the
+         maximum value is unspecified.
+       * amqp plugin: The new AMQP plugin can send data to and receive data
+         from an AMQP broker. Thanks to Sebastien Pahl for his code.
+       * apache plugin: Backwards compatibility code has been removed.
+         Support for the IBM HTTP Server has been added. Thanks to Manuel
+         Luis Sanmartín Rozada for his patch.
+       * contextswitch plugin: Support for sysctlbyname(3) has been added.
+         Thanks to Kimo Rosenbaum for his patch.
+       * df plugin: The default behavior has been changed to be equivalent to
+         the "ReportReserved" behavior of v4.
+       * dns plugin: Improved RFC 1035 name parsing has been imported from
+         "dnstop".
+       * exec plugin: Backwards compatibility code has been removed.
+       * GenericJMX plugin: The "InstancePrefix" option has been added to
+         "Connection" blocks.
+       * hddtemp plugin: The "TranslateDevicename" config option has been
+         removed.
+       * interface plugin: Use the "plugin instance" to store the interface
+         value.
+       * libvirt plugin: The "InterfaceFormat" option has been added. Thanks
+         to Ruben Kerkhof for his patch.
+       * lpar plugin: New plugins for "logical partitions", a virtualization
+         technique of POWER CPUs. Thanks to Aurélien Reynaud for his code and
+         patience.
+       * modbus plugin: Support for libmodbus 2.9.2 has been added and the
+         license has been changed to LGPLv2.1.
+       * mysql plugin: Backwards compatibility code has been removed. The
+         data sets used have been improved.
+       * network plugin: The default buffer size has been increased to
+         1452 bytes.
+       * perl plugin: Backwards compatibility code has been removed.
+       * postgresql plugin: Backwards compatibility code has been removed.
+       * redis plugin: Plugin for collecting statistics from Redis, a key-
+         value store, has been added. Thanks to Andres J. Diaz for his code.
+       * swap plugin: Implement collection of physical and virtual memory
+         statistics under Solaris. The new default is collecting physical
+         memory. Thanks to Aurélien Reynaud for his patches.
+       * threshold plugin: The threshold configuration has been moved into
+         this separate plugin.
+       * unixsock plugin: The "DeleteSocket" option has been added.
+       * varnish plugin: The new Varnish plugin reads statistics from
+         Varnish, a web accelerator. Thanks to Jérôme Renard and Marc
+         Fournier for their contributions.
+       * write_redis: New plugin for writing data to Redis, a key-value
+         store.
+       * zfs_arc plugin: The data sets have been replaced by more elegant
+         alternatives.
+       * 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.
diff --git a/README b/README
index 34caa85..c3c4547 100644 (file)
--- a/README
+++ b/README
@@ -83,6 +83,9 @@ Features
     - entropy
       Amount of entropy available to the system.
 
+    - ethstat
+      Network interface card statistics.
+
     - exec
       Values gathered by a custom program or script.
       See collectd-exec(5).
@@ -125,8 +128,12 @@ Features
     - load
       System load average over the last 1, 5 and 15 minutes.
 
+    - lpar
+      Detailed CPU statistics of the “Logical Partitions” virtualization
+      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.
 
     - madwifi
       Queries very detailed usage statistics from wireless LAN adapters and
@@ -136,6 +143,10 @@ Features
       Motherboard sensors: temperature, fanspeed and voltage information,
       using mbmon(1).
 
+    - md
+      Linux software-RAID device information (number of active, failed, spare
+      and missing disks).
+
     - memcachec
       Query and parse data from a memcache daemon (memcached).
 
@@ -188,6 +199,9 @@ Features
       Network UPS tools: UPS current, voltage, power, charge, utilisation,
       temperature, etc. See upsd(8).
 
+    - numa
+      Information about Non-Uniform Memory Access (NUMA).
+
     - olsrd
       Queries routing information from the “Optimized Link State Routing”
       daemon.
@@ -208,6 +222,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.
@@ -235,6 +252,10 @@ Features
       collectd without the need to start a heavy interpreter every interval.
       See collectd-python(5) for details.
 
+    - redis
+      The redis plugin gathers information from a redis server, including:
+      uptime, used memory, total connections etc.
+
     - routeros
       Query interface and wireless registration statistics from RouterOS.
 
@@ -263,6 +284,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.
 
@@ -288,6 +313,9 @@ Features
     - users
       Users currently logged in.
 
+    - varnish
+      Various statistics from Varnish, an HTTP accelerator.
+
     - vmem
       Virtual memory statistics, e. g. the number of page-ins/-outs or the
       number of pagefaults.
@@ -308,6 +336,10 @@ Features
   * Output can be written or sent to various destinations by the following
     plugins:
 
+    - amqp
+      Sends JSON-encoded data to an Advanced Message Queuing Protocol (AMQP)
+      server, such as RabbitMQ.
+
     - csv
       Write to comma separated values (CSV) files. This needs lots of
       diskspace but is extremely portable and can be analysed with almost
@@ -343,11 +375,23 @@ Features
       needed. Please read collectd-unixsock(5) for a description on how that's
       done.
 
+    - write_graphite
+      Sends data to Carbon, the storage layer of Graphite.
+
     - write_http
       Sends the values collected by collectd to a web-server using HTTP POST
       requests. The transmitted data is either in a form understood by the
       Exec plugin or formatted in JSON.
 
+    - 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:
 
@@ -427,6 +471,14 @@ 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.
+
     - uuid
       Sets the hostname to an unique identifier. This is meant for setups
       where each client may migrate to another physical host, possibly going
@@ -504,6 +556,10 @@ Prerequisites
   * libclntsh (optional)
     Used by the `oracle' plugin.
 
+  * libcredis (optional)
+    Used by the redis plugin. Please note that you require a 0.2.2 version
+    or higher. <http://code.google.com/p/credis/>
+
   * libcurl (optional)
     If you want to use the `apache', `ascent', `curl', `nginx', or `write_http'
     plugin.
@@ -610,6 +666,10 @@ Prerequisites
     are supported.
     <http://www.python.org/>
 
+  * librabbitmq (optional; also called “rabbitmq-c”)
+    Used by the AMQP plugin for AMQP connections, for example to RabbitMQ.
+    <http://hg.rabbitmq.com/rabbitmq-c/>
+
   * librouteros (optional)
     Used by the `routeros' plugin to connect to a device running `RouterOS'.
     <http://verplant.org/librouteros/>
@@ -656,6 +716,10 @@ Prerequisites
     Parse JSON data. This is needed for the `curl_json' plugin.
     <http://github.com/lloyd/yajl>
 
+  * libvarnish (optional)
+     Fetches statistics from a Varnish instance. This is needed for the Varnish plugin
+     <http://varnish-cache.org>
+
 Configuring / Compiling / Installing
 ------------------------------------
 
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 3e492dd..410c61c 100644 (file)
@@ -29,5 +29,5 @@ package org.collectd.api;
  */
 public interface CollectdFlushInterface
 {
-       public int flush (int timeout, String identifier);
+       public int flush (Number timeout, String identifier);
 }
index 1baeff2..b8d6f40 100644 (file)
@@ -87,10 +87,16 @@ public class ValueList extends PluginData {
         _ds = new DataSet (_type, dsrc);
     }
 
+    /**
+     * Returns the interval (in milliseconds) of the value list.
+     */
     public long getInterval() {
         return _interval;
     }
 
+    /**
+     * Sets the interval (in milliseconds) of the value list.
+     */
     public void setInterval(long interval) {
         _interval = interval;
     }
index ffa9ded..81aee03 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * collectd/java - org/collectd/java/GenericJMXConfConnection.java
- * Copyright (C) 2009  Florian octo Forster
+ * Copyright (C) 2009-2012  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -16,7 +16,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  */
 
 package org.collectd.java;
@@ -26,10 +26,10 @@ import java.util.Map;
 import java.util.Iterator;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 
 import javax.management.MBeanServerConnection;
-import javax.management.ObjectName;
-import javax.management.MalformedObjectNameException;
 
 import javax.management.remote.JMXServiceURL;
 import javax.management.remote.JMXConnector;
@@ -45,6 +45,7 @@ class GenericJMXConfConnection
   private String _username = null;
   private String _password = null;
   private String _host = null;
+  private String _instance_prefix = null;
   private String _service_url = null;
   private MBeanServerConnection _jmx_connection = null;
   private List<GenericJMXConfMBean> _mbeans = null;
@@ -76,6 +77,24 @@ class GenericJMXConfConnection
     return (v.getString ());
   } /* }}} String getConfigString */
 
+  private String getHost () /* {{{ */
+  {
+    if (this._host != null)
+    {
+      return (this._host);
+    }
+
+    try
+    {
+      InetAddress localHost = InetAddress.getLocalHost();
+      return (localHost.getHostName ());
+    }
+    catch (UnknownHostException e)
+    {
+      return ("localhost");
+    }
+  } /* }}} String getHost */
+
 private void connect () /* {{{ */
 {
   JMXServiceURL service_url;
@@ -162,6 +181,12 @@ private void connect () /* {{{ */
         if (tmp != null)
           this._service_url = tmp;
       }
+      else if (child.getKey ().equalsIgnoreCase ("InstancePrefix"))
+      {
+        String tmp = getConfigString (child);
+        if (tmp != null)
+          this._instance_prefix = tmp;
+      }
       else if (child.getKey ().equalsIgnoreCase ("Collect"))
       {
         String tmp = getConfigString (child);
@@ -204,14 +229,15 @@ private void connect () /* {{{ */
         + ((this._host != null) ? this._host : "(null)"));
 
     pd = new PluginData ();
-    pd.setHost ((this._host != null) ? this._host : "localhost");
+    pd.setHost (this.getHost ());
     pd.setPlugin ("GenericJMX");
 
     for (int i = 0; i < this._mbeans.size (); i++)
     {
       int status;
 
-      status = this._mbeans.get (i).query (this._jmx_connection, pd);
+      status = this._mbeans.get (i).query (this._jmx_connection, pd,
+          this._instance_prefix);
       if (status != 0)
       {
         this._jmx_connection = null;
index 1587bd5..b1fbfb3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * collectd/java - org/collectd/java/GenericJMXConfMBean.java
- * Copyright (C) 2009  Florian octo Forster
+ * Copyright (C) 2009,2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -67,22 +67,6 @@ class GenericJMXConfMBean
     return (v.getString ());
   } /* }}} String getConfigString */
 
-  private String join (String separator, List<String> list) /* {{{ */
-  {
-    StringBuffer sb;
-
-    sb = new StringBuffer ();
-
-    for (int i = 0; i < list.size (); i++)
-    {
-      if (i > 0)
-        sb.append ("-");
-      sb.append (list.get (i));
-    }
-
-    return (sb.toString ());
-  } /* }}} String join */
-
 /*
  * <MBean "alias name">
  *   ObjectName "object name"
@@ -170,7 +154,8 @@ class GenericJMXConfMBean
     return (this._name);
   } /* }}} */
 
-  public int query (MBeanServerConnection conn, PluginData pd) /* {{{ */
+  public int query (MBeanServerConnection conn, PluginData pd, /* {{{ */
+      String instance_prefix)
   {
     Set<ObjectName> names;
     Iterator<ObjectName> iter;
@@ -197,11 +182,12 @@ class GenericJMXConfMBean
       ObjectName   objName;
       PluginData   pd_tmp;
       List<String> instanceList;
-      String       instance;
+      StringBuffer instance;
 
       objName      = iter.next ();
       pd_tmp       = new PluginData (pd);
       instanceList = new ArrayList<String> ();
+      instance     = new StringBuffer ();
 
       Collectd.logDebug ("GenericJMXConfMBean: objName = "
           + objName.toString ());
@@ -224,14 +210,22 @@ class GenericJMXConfMBean
         }
       }
 
+      if (instance_prefix != null)
+        instance.append (instance_prefix);
+
       if (this._instance_prefix != null)
-        instance = new String (this._instance_prefix
-            + join ("-", instanceList));
-      else
-        instance = join ("-", instanceList);
-      pd_tmp.setPluginInstance (instance);
+        instance.append (this._instance_prefix);
+
+      for (int i = 0; i < instanceList.size (); i++)
+      {
+        if (i > 0)
+          instance.append ("-");
+        instance.append (instanceList.get (i));
+      }
+
+      pd_tmp.setPluginInstance (instance.toString ());
 
-      Collectd.logDebug ("GenericJMXConfMBean: instance = " + instance);
+      Collectd.logDebug ("GenericJMXConfMBean: instance = " + instance.toString ());
 
       for (int i = 0; i < this._values.size (); i++)
         this._values.get (i).query (conn, objName, pd_tmp);
index 0eb0d5f..9fb0fc2 100644 (file)
@@ -312,7 +312,14 @@ class GenericJMXConfValue
 
     try
     {
-      value = conn.getAttribute (objName, key);
+      try
+      {
+        value = conn.getAttribute (objName, key);
+      }
+      catch (javax.management.AttributeNotFoundException e)
+      {
+        value = conn.invoke (objName, key, /* args = */ null, /* types = */ null);
+      }
     }
     catch (Exception e)
     {
index f1b5d85..c1adf44 100644 (file)
@@ -42,6 +42,7 @@ our %EXPORT_TAGS = (
                        plugin_register
                        plugin_unregister
                        plugin_dispatch_values
+                       plugin_get_interval
                        plugin_write
                        plugin_flush
                        plugin_flush_one
@@ -171,6 +172,7 @@ sub plugin_call_all {
        my $type = shift;
 
        my %plugins;
+       my $interval;
 
        our $cb_name = undef;
 
@@ -194,13 +196,15 @@ sub plugin_call_all {
                %plugins = %{$plugins[$type]};
        }
 
+       $interval = plugin_get_interval ();
+
        foreach my $plugin (keys %plugins) {
                my $p = $plugins{$plugin};
 
                my $status = 0;
 
                if ($p->{'wait_left'} > 0) {
-                       $p->{'wait_left'} -= $interval_g;
+                       $p->{'wait_left'} -= $interval;
                }
 
                next if ($p->{'wait_left'} > 0);
@@ -227,11 +231,11 @@ sub plugin_call_all {
 
                if ($status) {
                        $p->{'wait_left'} = 0;
-                       $p->{'wait_time'} = $interval_g;
+                       $p->{'wait_time'} = $interval;
                }
                elsif (TYPE_READ == $type) {
-                       if ($p->{'wait_time'} < $interval_g) {
-                               $p->{'wait_time'} = $interval_g;
+                       if ($p->{'wait_time'} < $interval) {
+                               $p->{'wait_time'} = $interval;
                        }
 
                        $p->{'wait_left'} = $p->{'wait_time'};
@@ -313,7 +317,7 @@ sub plugin_register {
                }
 
                %p = (
-                       wait_time => $interval_g,
+                       wait_time => plugin_get_interval (),
                        wait_left => 0,
                        cb_name   => $data,
                );
@@ -465,35 +469,6 @@ sub plugin_flush {
        }
 }
 
-sub plugin_flush_one {
-       my $timeout = shift;
-       my $name    = shift;
-
-       WARNING ("Collectd::plugin_flush_one is deprecated - "
-               . "use Collectd::plugin_flush instead.");
-
-       if (! (defined ($timeout) && defined ($name))) {
-               ERROR ("Usage: Collectd::plugin_flush_one(timeout, name)");
-               return;
-       }
-
-       plugin_flush (plugins => $name, timeout => $timeout);
-}
-
-sub plugin_flush_all {
-       my $timeout = shift;
-
-       WARNING ("Collectd::plugin_flush_all is deprecated - "
-               . "use Collectd::plugin_flush instead.");
-
-       if (! defined ($timeout)) {
-               ERROR ("Usage: Collectd::plugin_flush_all(timeout)");
-               return;
-       }
-
-       plugin_flush (timeout => $timeout);
-}
-
 sub fc_call {
        my $type    = shift;
        my $name    = shift;
index 2944157..ea3cee9 100644 (file)
@@ -36,7 +36,7 @@ my $last_stat = {};
 
 sub openvz_read
 {
-    my %v = (time => time(), interval => $interval_g);
+    my %v = (time => time(), interval => plugin_get_interval());
     my (@veids, $veid, $name, $key, $val, $i, @lines, @parts, @counters);
 
     @veids = map { s/ //g; $_; } split(/\n/, `$vzlist -Ho veid`);
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..9defb05 100755 (executable)
--- a/clean.sh
+++ b/clean.sh
@@ -47,4 +47,6 @@ 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 bindings/.perl-directory-stamp \
+&& rm -f -r bindings/buildperl
index 5dac543..b00df81 100644 (file)
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-AC_INIT(collectd, m4_esyscmd(./version-gen.sh))
+AC_INIT(collectd, [m4_esyscmd(./version-gen.sh)])
 AC_CONFIG_SRCDIR(src/collectd.c)
 AC_CONFIG_HEADERS(src/config.h)
 AC_CONFIG_AUX_DIR([libltdl/config])
@@ -24,7 +24,7 @@ m4_ifdef([LT_PACKAGE_VERSION],
        ]
 )
 
-AM_INIT_AUTOMAKE(dist-bzip2)
+AM_INIT_AUTOMAKE([tar-pax dist-bzip2])
 AC_LANG(C)
 
 AC_PREFIX_DEFAULT("/opt/collectd")
@@ -49,6 +49,13 @@ 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)])
@@ -91,6 +98,7 @@ fi
 if test "x$ac_system" = "xSolaris"
 then
        AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Define to enforce POSIX thread semantics under Solaris.])
+       AC_DEFINE(_REENTRANT,               1, [Define to enable reentrancy interfaces.])
 fi
 if test "x$ac_system" = "xAIX"
 then
@@ -109,9 +117,13 @@ AC_ARG_ENABLE(standards,
 if test "x$enable_standards" = "xyes"
 then
        AC_DEFINE(_ISOC99_SOURCE,        1, [Define to enforce ISO C99 compliance.])
-       AC_DEFINE(_POSIX_C_SOURCE, 200112L, [Define to enforce POSIX.1-2001 compliance.])
-       AC_DEFINE(_XOPEN_SOURCE,       600, [Define to enforce X/Open 6 (XSI) 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")
 
@@ -123,7 +135,7 @@ 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)
+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, [], [],
@@ -265,7 +277,19 @@ 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
@@ -293,18 +317,22 @@ else
 fi
 
 # For hddtemp module
-AC_CHECK_HEADERS(linux/major.h libgen.h)
+AC_CHECK_HEADERS(linux/major.h)
 
-# For the battery plugin
-AC_CHECK_HEADERS(IOKit/ps/IOPowerSources.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"],
 [
-#if HAVE_IOKIT_IOKITLIB_H
-#  include <IOKit/IOKitLib.h>
-#endif
-#if HAVE_IOKIT_IOTYPES_H
-#  include <IOKit/IOTypes.h>
-#endif
+#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"
@@ -371,6 +399,18 @@ AC_CHECK_HEADERS(linux/if.h, [], [],
 #  include <sys/socket.h>
 #endif
 ])
+AC_CHECK_HEADERS(linux/inet_diag.h, [], [],
+[
+#if HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#  include <sys/socket.h>
+#endif
+#if HAVE_LINUX_INET_DIAG_H
+# include <linux/inet_diag.h>
+#endif
+])
 AC_CHECK_HEADERS(linux/netdevice.h, [], [],
 [
 #if HAVE_SYS_TYPES_H
@@ -384,6 +424,33 @@ AC_CHECK_HEADERS(linux/netdevice.h, [], [],
 #endif
 ])
 
+# For ethstat module
+AC_CHECK_HEADERS(linux/sockios.h,
+    [have_linux_sockios_h="yes"],
+    [have_linux_sockios_h="no"],
+    [
+#if HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+    ])
+AC_CHECK_HEADERS(linux/ethtool.h,
+    [have_linux_ethtool_h="yes"],
+    [have_linux_ethtool_h="no"],
+    [
+#if HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+#if HAVE_LINUX_SOCKIOS_H
+# include <linux/sockios.h>
+#endif
+    ])
+
 # For ipvs module
 have_linux_ip_vs_h="no"
 have_net_ip_vs_h="no"
@@ -486,6 +553,24 @@ AC_CHECK_HEADERS(netinet/if_ether.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"])
@@ -518,12 +603,12 @@ 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;
@@ -536,7 +621,8 @@ AC_CACHE_CHECK([for strtok_r],
         dummy = NULL;
         printf ("token = %s;\n", token);
       }
-    ]]])],
+]]]
+    )],
     [c_cv_have_strtok_r_default="yes"],
     [c_cv_have_strtok_r_default="no"]
   )
@@ -550,12 +636,12 @@ then
     [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;
@@ -568,7 +654,8 @@ then
           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!])]
     )
@@ -587,6 +674,27 @@ 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,
@@ -596,8 +704,9 @@ AC_CHECK_FUNCS(nanosleep,
         AC_CHECK_LIB(posix4, nanosleep,
             [nanosleep_needs_posix4="yes"],
             AC_MSG_ERROR(cannot find nanosleep))))
-AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes")
-AM_CONDITIONAL(BUILD_WITH_LIBPOSIX4, test "x$nanosleep_needs_posix4" = "xyes")
+
+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"])
@@ -607,13 +716,127 @@ 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"])
-AC_CHECK_FUNCS(swapctl, [have_swapctl="yes"], [have_swapctl="no"])
 
-# For load module
-AC_CHECK_FUNCS(getloadavg, [have_getloadavg="yes"], [have_getloadavg="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.])],
@@ -630,19 +853,19 @@ AC_ARG_WITH(nan-emulation, [AS_HELP_STRING([--with-nan-emulation], [use emulated
 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(
-      [[[
+    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"]
     )
@@ -655,20 +878,20 @@ 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(
-      [[[
+    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"]
     )
@@ -683,9 +906,8 @@ if test "x$nan_type" = "xnone"; then
   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(
-      [[[
+    AC_RUN_IFELSE([AC_LANG_PROGRAM(
+[[[
 #include <stdlib.h>
 #include <math.h>
 #ifdef NAN
@@ -696,13 +918,14 @@ if test "x$nan_type" = "xnone"; then
 # 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"]
     )
@@ -744,9 +967,8 @@ fi; fi; fi
 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(
-      [[[
+    AC_RUN_IFELSE([AC_LANG_PROGRAM(
+[[[
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -759,8 +981,8 @@ if test "x$fp_layout_type" = "xunknown"; then
 #if HAVE_STDBOOL_H
 # include <stdbool.h>
 #endif
-      ]]],
-      [[[
+]]],
+[[[
        uint64_t i0;
        uint64_t i1;
        uint8_t c[8];
@@ -779,7 +1001,8 @@ if test "x$fp_layout_type" = "xunknown"; then
                return (0);
        else
                return (1);
-      ]]])],
+]]]
+      )],
       [c_cv_fp_layout_need_nothing="yes"],
       [c_cv_fp_layout_need_nothing="no"]
     )
@@ -791,9 +1014,8 @@ 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(
-      [[[
+    AC_RUN_IFELSE([AC_LANG_PROGRAM(
+[[[
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -814,8 +1036,8 @@ if test "x$fp_layout_type" = "xunknown"; then
                        (((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];
@@ -834,7 +1056,8 @@ if test "x$fp_layout_type" = "xunknown"; then
                return (0);
        else
                return (1);
-      ]]])],
+]]]
+      )],
       [c_cv_fp_layout_need_endianflip="yes"],
       [c_cv_fp_layout_need_endianflip="no"]
     )
@@ -846,9 +1069,8 @@ 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(
-      [[[
+    AC_RUN_IFELSE([AC_LANG_PROGRAM(
+[[[
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -863,8 +1085,8 @@ if test "x$fp_layout_type" = "xunknown"; then
 #endif
 #define intswap(A)    ((((uint64_t)(A) & 0xffffffff00000000LL) >> 32) | \
                        (((uint64_t)(A) & 0x00000000ffffffffLL) << 32))
-      ]]],
-      [[[
+]]],
+[[[
        uint64_t i0;
        uint64_t i1;
        uint8_t c[8];
@@ -883,7 +1105,8 @@ if test "x$fp_layout_type" = "xunknown"; then
                return (0);
        else
                return (1);
-      ]]])],
+]]]
+      )],
       [c_cv_fp_layout_need_intswap="yes"],
       [c_cv_fp_layout_need_intswap="no"]
     )
@@ -929,14 +1152,16 @@ 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);
-                               ]]]
+                       [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"]
@@ -945,15 +1170,17 @@ if test "x$have_getmntent" = "xc"; then
        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"]],
-                               [[[
+                       [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"]
@@ -990,17 +1217,19 @@ fi
 AC_MSG_CHECKING([if have htonll defined])
 
     have_htonll="no"
-    AC_LINK_IFELSE([
-       AC_LANG_PROGRAM([[[
+    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.])
     ])
@@ -1025,6 +1254,13 @@ AC_CHECK_MEMBERS([struct net_device_stats.rx_bytes, struct net_device_stats.tx_p
        #include <linux/if.h>
        #include <linux/netdevice.h>
        ])
+AC_CHECK_MEMBERS([struct inet_diag_req.id, struct inet_diag_req.idiag_states],
+       [AC_DEFINE(HAVE_STRUCT_LINUX_INET_DIAG_REQ, 1, [Define if struct inet_diag_req exists and is usable.])],
+       [],
+       [
+       #include <linux/inet_diag.h>
+       ])
+
 
 AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [],
        [],
@@ -1043,7 +1279,6 @@ AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct
                have_struct_kinfo_proc_freebsd="no"
        ],
        [
-AC_INCLUDES_DEFAULT
 #include <kvm.h>
 #include <sys/param.h>
 #include <sys/sysctl.h>
@@ -1060,7 +1295,6 @@ AC_CHECK_MEMBERS([struct kinfo_proc.kp_proc, struct kinfo_proc.kp_eproc],
                have_struct_kinfo_proc_openbsd="no"
        ],
        [
-AC_INCLUDES_DEFAULT
 #include <sys/param.h>
 #include <sys/sysctl.h>
 #include <kvm.h>
@@ -1121,6 +1355,7 @@ AC_CHECK_MEMBERS([kstat_io_t.nwritten, kstat_io_t.writes, kstat_io_t.nwrites, ks
 #
 # Checks for libraries begin here
 #
+
 with_libresolv="yes"
 AC_CHECK_LIB(resolv, res_search,
 [
@@ -1165,6 +1400,12 @@ 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")
 
@@ -1205,13 +1446,12 @@ 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,
-[
+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"
@@ -1252,6 +1492,64 @@ then
 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=""
@@ -1564,6 +1862,8 @@ then
 
        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
 
@@ -1727,7 +2027,7 @@ 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' '{}' ';' | head -n 1`
+               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])
@@ -1737,7 +2037,7 @@ then
                fi
 
                AC_MSG_CHECKING([for jni_md.h])
-               TMPVAR=`find "$with_java_home" -name jni_md.h -type f -exec 'dirname' '{}' ';' | head -n 1`
+               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])
@@ -1747,7 +2047,7 @@ then
                fi
 
                AC_MSG_CHECKING([for libjvm.so])
-               TMPVAR=`find "$with_java_home" -name libjvm.so -type f -exec 'dirname' '{}' ';' | head -n 1`
+               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])
@@ -1759,7 +2059,7 @@ then
                if test "x$JAVAC" = "x"
                then
                        AC_MSG_CHECKING([for javac])
-                       TMPVAR=`find "$with_java_home" -name javac -type f | head -n 1`
+                       TMPVAR=`find "$with_java_home" -name javac -type f 2>/dev/null | head -n 1`
                        if test "x$TMPVAR" != "x"
                        then
                                JAVAC="$TMPVAR"
@@ -1771,7 +2071,7 @@ then
                if test "x$JAR" = "x"
                then
                        AC_MSG_CHECKING([for jar])
-                       TMPVAR=`find "$with_java_home" -name jar -type f | head -n 1`
+                       TMPVAR=`find "$with_java_home" -name jar -type f 2>/dev/null | head -n 1`
                        if test "x$TMPVAR" != "x"
                        then
                                JAR="$TMPVAR"
@@ -1953,21 +2253,21 @@ then
 fi
 if test "x$with_libmodbus" = "xuse_pkgconfig"
 then
-       AC_MSG_NOTICE([Checking for modbus using $PKG_CONFIG])
-       $PKG_CONFIG --exists 'modbus' 2>/dev/null
+       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 library)"
+               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 'modbus'`"
+       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 'modbus'`"
+       with_libmodbus_libs="`$PKG_CONFIG --libs 'libmodbus'`"
        if test $? -ne 0
        then
                with_libmodbus="no ($PKG_CONFIG failed)"
@@ -1997,9 +2297,9 @@ then
        CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
        LDFLAGS="$LDFLAGS $with_libmodbus_libs"
 
-       AC_CHECK_LIB(modbus, modbus_init_tcp,
+       AC_CHECK_LIB(modbus, modbus_connect,
                     [with_libmodbus="yes"],
-                    [with_libmodbus="no (symbol modbus_init_tcp not found)"])
+                    [with_libmodbus="no (symbol modbus_connect not found)"])
 
        CPPFLAGS="$SAVE_CPPFLAGS"
        LDFLAGS="$SAVE_LDFLAGS"
@@ -2013,6 +2313,69 @@ then
 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=""
@@ -2154,35 +2517,36 @@ then
 #include <sys/socket.h>])
 
         AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
-                           [[[
-                            #include <stdio.h>
-                            #include <sys/types.h>
-                            #include <asm/types.h>
-                            #include <sys/socket.h>
-                            #include <linux/netlink.h>
-                            #include <linux/rtnetlink.h>
-                            ]]], [[[
-                                int retval = TCA_STATS2;
-                                return (retval);
-                                ]]]
-                            )],
-                            [AC_DEFINE([HAVE_TCA_STATS2], [1], [True if the enum-member TCA_STATS2 exists])])
+[[[
+#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 <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])]
-       []);
+]]],
+[[[
+int retval = TCA_STATS;
+return (retval);
+]]]
+       )],
+       [AC_DEFINE([HAVE_TCA_STATS], 1, [True if the enum-member TCA_STATS exists])])
 
        CFLAGS="$SAVE_CFLAGS"
 fi
@@ -2201,8 +2565,10 @@ then
        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
+               AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <stdio.h>
+#include <sys/types.h>
 #include <asm/types.h>
 #include <sys/socket.h>
 #if HAVE_LIBNETLINK_H
@@ -2212,23 +2578,25 @@ AC_INCLUDES_DEFAULT
 #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"]
-               )
+]]]
+       )],
+       [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 <stdio.h>
+#include <sys/types.h>
 #include <asm/types.h>
 #include <sys/socket.h>
 #if HAVE_LIBNETLINK_H
@@ -2238,16 +2606,17 @@ AC_INCLUDES_DEFAULT
 #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"]
-               )
+]]]
+       )],
+       [c_cv_rtnl_dump_filter_three_args="yes"],
+       [c_cv_rtnl_dump_filter_three_args="no"]
        )
+)
 
        CFLAGS="$SAVE_CFLAGS"
 
@@ -2488,7 +2857,7 @@ then
        fi
        AC_CHECK_HEADERS(oping.h,
        [with_liboping="yes"],
-       [with_liboping="no ('oping.h' not found)"])
+       [with_liboping="no (oping.h not found)"])
 fi
 if test "x$with_liboping" = "xyes"
 then
@@ -2565,14 +2934,14 @@ fi
 if test "x$with_oracle" = "xyes"
 then
        SAVE_CPPFLAGS="$CPPFLAGS"
-       SAVE_LDFLAGS="$LDFLAGS"
+       SAVE_LIBS="$LIBS"
        CPPFLAGS="$CPPFLAGS $with_oracle_cppflags"
-       LDFLAGS="$LDFLAGS $with_oracle_libs"
+       LIBS="$LIBS $with_oracle_libs"
 
        AC_CHECK_FUNC(OCIEnvCreate, [with_oracle="yes"], [with_oracle="no (Symbol 'OCIEnvCreate' not found)"])
 
        CPPFLAGS="$SAVE_CPPFLAGS"
-       LDFLAGS="$SAVE_LDFLAGS"
+       LIBS="$SAVE_LIBS"
 fi
 if test "x$with_oracle" = "xyes"
 then
@@ -2659,7 +3028,27 @@ then
 fi
 if test "x$with_libpcap" = "xyes"
 then
-       AC_CHECK_HEADERS(pcap-bpf.h)
+       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")
 # }}}
@@ -2711,20 +3100,20 @@ dnl ARCHFLAGS="" -> disable multi -arch on OSX (see Config_heavy.pl:fetch_string
 
   AC_CACHE_CHECK([for libperl],
     [c_cv_have_libperl],
-    AC_LINK_IFELSE(
-      [AC_LANG_PROGRAM(
-      [[[
+    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"]
     )
@@ -2756,9 +3145,8 @@ then
 
        AC_CACHE_CHECK([if perl supports ithreads],
                [c_cv_have_perl_ithreads],
-               AC_LINK_IFELSE(
-                       [AC_LANG_PROGRAM(
-                       [[[
+               AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[[
 #include <EXTERN.h>
 #include <perl.h>
 #include <XSUB.h>
@@ -2766,8 +3154,9 @@ then
 #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"]
                )
@@ -2793,20 +3182,20 @@ then
 
        AC_CACHE_CHECK([for broken Perl_load_module()],
                [c_cv_have_broken_perl_load_module],
-               AC_LINK_IFELSE(
-                       [AC_LANG_PROGRAM(
-                       [[[
+               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"]
                )
@@ -3094,6 +3483,107 @@ then
 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_amqp_tcp_socket="no"
+if test "x$with_librabbitmq" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       SAVE_LIBS="$LIBS"
+       CPPFLAGS="$CPPFLAGS $with_librabbitmq_cppflags"
+       LDFLAGS="$LDFLAGS $with_librabbitmq_ldflags"
+       LIBS="-lrabbitmq"
+
+       AC_CHECK_HEADERS(amqp_tcp_socket.h amqp_socket.h)
+       AC_CHECK_FUNC(amqp_tcp_socket_new, [with_amqp_tcp_socket="yes"], [with_amqp_tcp_socket="no"])
+       if test "x$with_amqp_tcp_socket" = "xyes"
+       then
+               AC_DEFINE(HAVE_AMQP_TCP_SOCKET, 1,
+                               [Define if librabbitmq provides the new TCP socket interface.])
+       fi
+
+       AC_CHECK_DECLS(amqp_socket_close,
+                               [amqp_socket_close_decl="yes"], [amqp_socket_close_decl="no"],
+                               [[
+#include <amqp.h>
+#ifdef HAVE_AMQP_TCP_SOCKET_H
+# include <amqp_tcp_socket.h>
+#endif
+#ifdef HAVE_AMQP_SOCKET_H
+# include <amqp_socket.h>
+#endif
+                               ]])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+       LIBS="$SAVE_LIBS"
+fi
+# }}}
+
 # --with-librouteros {{{
 AC_ARG_WITH(librouteros, [AS_HELP_STRING([--with-librouteros@<:@=PREFIX@:>@], [Path to librouteros.])],
 [
@@ -3125,7 +3615,7 @@ then
        fi
        AC_CHECK_HEADERS(routeros_api.h,
        [with_librouteros="yes"],
-       [with_librouteros="no ('routeros_api.h' not found)"])
+       [with_librouteros="no (routeros_api.h not found)"])
 fi
 if test "x$with_librouteros" = "xyes"
 then
@@ -3324,7 +3814,7 @@ then
     if test "$?" != "0"
     then
       with_libstatgrab_pkg_config="no"
-      with_libstatgrab="no ($PKG_CONFIG doesn't know libstatgrab)"
+      with_libstatgrab="no (pkg-config doesn't know libstatgrab)"
       temp_result="not found"
     fi
     AC_MSG_RESULT([$temp_result])
@@ -3544,7 +4034,7 @@ then
        $PKG_CONFIG --exists 'libupsclient' 2>/dev/null
        if test $? -ne 0
        then
-               with_libupsclient="no (pkg-config doesn't know library)"
+               with_libupsclient="no (pkg-config doesn't know libupsclient)"
        fi
 fi
 if test "x$with_libupsclient" = "xuse_pkgconfig"
@@ -3734,6 +4224,97 @@ 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=""
@@ -3748,7 +4329,7 @@ then
        then
                with_libxml2="yes"
        else
-               with_libxml2="no (pkg-config doesn't know library)"
+               with_libxml2="no (pkg-config doesn't know libxml-2.0)"
        fi
 
        $PKG_CONFIG --exists libvirt 2>/dev/null
@@ -3756,7 +4337,7 @@ then
        then
                with_libvirt="yes"
        else
-               with_libvirt="no (pkg-config doesn't know library)"
+               with_libvirt="no (pkg-config doesn't know libvirt)"
        fi
 fi
 if test "x$with_libxml2" = "xyes"
@@ -3873,7 +4454,7 @@ then
        $PKG_CONFIG --exists OpenIPMIpthread 2>/dev/null
        if test "$?" != "0"
        then
-               with_libopenipmipthread="no ($PKG_CONFIG doesn't know OpenIPMIpthread)"
+               with_libopenipmipthread="no (pkg-config doesn't know OpenIPMIpthread)"
        fi
        AC_MSG_RESULT([$with_libopenipmipthread])
 fi
@@ -3934,7 +4515,11 @@ fi
 
 PKG_CHECK_MODULES([LIBNOTIFY], [libnotify],
                [with_libnotify="yes"],
-               [with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"])
+               [if test "x$LIBNOTIFY_PKG_ERRORS" = "x"; then
+                        with_libnotify="no"
+                else
+                        with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"
+                fi])
 
 # Check for enabled/disabled features
 #
@@ -4074,6 +4659,8 @@ plugin_curl_xml="no"
 plugin_df="no"
 plugin_disk="no"
 plugin_entropy="no"
+plugin_ethstat="no"
+plugin_fscache="no"
 plugin_interface="no"
 plugin_ipmi="no"
 plugin_ipvs="no"
@@ -4083,7 +4670,7 @@ plugin_load="no"
 plugin_memory="no"
 plugin_multimeter="no"
 plugin_nfs="no"
-plugin_fscache="no"
+plugin_numa="no"
 plugin_perl="no"
 plugin_processes="no"
 plugin_protocols="no"
@@ -4110,12 +4697,13 @@ then
        plugin_cpufreq="yes"
        plugin_disk="yes"
        plugin_entropy="yes"
+       plugin_fscache="yes"
        plugin_interface="yes"
        plugin_irq="yes"
        plugin_load="yes"
        plugin_memory="yes"
        plugin_nfs="yes"
-       plugin_fscache="yes"
+       plugin_numa="yes"
        plugin_processes="yes"
        plugin_protocols="yes"
        plugin_serial="yes"
@@ -4146,14 +4734,22 @@ then
 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"
@@ -4164,6 +4760,7 @@ fi
 # Solaris
 if test "x$with_kstat" = "xyes"
 then
+       plugin_nfs="yes"
        plugin_uptime="yes"
        plugin_zfs_arc="yes"
 fi
@@ -4177,11 +4774,6 @@ then
        plugin_tape="yes"
 fi
 
-if test "x$have_sys_swap_h$with_kstat$ac_system" = "xyesyesSolaris"
-then
-       plugin_swap="yes"
-fi
-
 # libstatgrab
 if test "x$with_libstatgrab" = "xyes"
 then
@@ -4197,7 +4789,10 @@ fi
 if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
 then
        plugin_ascent="yes"
-       plugin_bind="yes"
+       if test "x$have_strptime" = "xyes"
+       then
+               plugin_bind="yes"
+       fi
 fi
 
 if test "x$with_libopenipmipthread" = "xyes"
@@ -4223,11 +4818,15 @@ if test "x$have_sysctl" = "xyes"
 then
        plugin_cpu="yes"
        plugin_memory="yes"
-       plugin_swap="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"
@@ -4268,6 +4867,11 @@ then
        fi
 fi
 
+if test "x$have_linux_sockios_h$have_linux_ethtool_h" = "xyesyes"
+then
+       plugin_ethstat="yes"
+fi
+
 if test "x$have_getifaddrs" = "xyes"
 then
        plugin_interface="yes"
@@ -4315,7 +4919,7 @@ then
        plugin_swap="yes"
 fi
 
-if test "x$have_swapctl" = "xyes"
+if test "x$have_swapctl" = "xyes" && test "x$c_cv_have_swapctl_two_args" = "xyes"
 then
        plugin_swap="yes"
 fi
@@ -4355,6 +4959,8 @@ AC_ARG_ENABLE([all-plugins],
 
 m4_divert_once([HELP_ENABLE], [])
 
+AC_PLUGIN([aggregation], [yes],                [Aggregation plugin])
+AC_PLUGIN([amqp],        [$with_librabbitmq],  [AMQP output plugin])
 AC_PLUGIN([apache],      [$with_libcurl],      [Apache httpd statistics])
 AC_PLUGIN([apcups],      [yes],                [Statistics of UPSes by APC])
 AC_PLUGIN([apple_sensors], [$with_libiokit],   [Apple's hardware sensors])
@@ -4375,6 +4981,7 @@ AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
 AC_PLUGIN([dns],         [$with_libpcap],      [DNS traffic analysis])
 AC_PLUGIN([email],       [yes],                [EMail statistics])
 AC_PLUGIN([entropy],     [$plugin_entropy],    [Entropy statistics])
+AC_PLUGIN([ethstat],     [$plugin_ethstat],    [Stats from NIC driver])
 AC_PLUGIN([exec],        [yes],                [Execution of external programs])
 AC_PLUGIN([filecount],   [yes],                [Count files in directories])
 AC_PLUGIN([fscache],     [$plugin_fscache],    [fscache statistics])
@@ -4389,6 +4996,7 @@ 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])
@@ -4396,6 +5004,7 @@ AC_PLUGIN([match_regex], [yes],                [The regex match])
 AC_PLUGIN([match_timediff], [yes],             [The timediff match])
 AC_PLUGIN([match_value], [yes],                [The value match])
 AC_PLUGIN([mbmon],       [yes],                [Query mbmond])
+AC_PLUGIN([md],          [$have_linux_raid_md_u_h], [md (Linux software RAID) devices])
 AC_PLUGIN([memcachec],   [$with_libmemcached], [memcachec statistics])
 AC_PLUGIN([memcached],   [yes],                [memcached statistics])
 AC_PLUGIN([memory],      [$plugin_memory],     [Memory usage])
@@ -4410,12 +5019,14 @@ 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])
@@ -4424,6 +5035,7 @@ 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])
@@ -4434,24 +5046,32 @@ 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_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])
 
@@ -4548,6 +5168,7 @@ then
 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"
@@ -4555,12 +5176,10 @@ AC_ARG_WITH(perl-bindings, [AS_HELP_STRING([--with-perl-bindings@<:@=OPTIONS@:>@
                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"
@@ -4592,9 +5211,9 @@ AC_SUBST(LCC_VERSION_PATCH)
 AC_SUBST(LCC_VERSION_EXTRA)
 AC_SUBST(LCC_VERSION_STRING)
 
-AC_CONFIG_FILES(src/libcollectdclient/lcc_features.h)
+AC_CONFIG_FILES(src/libcollectdclient/collectd/lcc_features.h)
 
-AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/owniptc/Makefile src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile bindings/java/Makefile)
+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"
@@ -4626,6 +5245,7 @@ Configuration:
   Libraries:
     libcurl . . . . . . . $with_libcurl
     libdbi  . . . . . . . $with_libdbi
+    libcredis . . . . . . $with_libcredis
     libesmtp  . . . . . . $with_libesmtp
     libganglia  . . . . . $with_libganglia
     libgcrypt . . . . . . $with_libgcrypt
@@ -4649,12 +5269,14 @@ Configuration:
     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
@@ -4672,6 +5294,8 @@ Configuration:
     perl  . . . . . . . . $with_perl_bindings
 
   Modules:
+    aggregation . . . . . $enable_aggregation
+    amqp    . . . . . . . $enable_amqp
     apache  . . . . . . . $enable_apache
     apcups  . . . . . . . $enable_apcups
     apple_sensors . . . . $enable_apple_sensors
@@ -4692,6 +5316,7 @@ Configuration:
     dns . . . . . . . . . $enable_dns
     email . . . . . . . . $enable_email
     entropy . . . . . . . $enable_entropy
+    ethstat . . . . . . . $enable_ethstat
     exec  . . . . . . . . $enable_exec
     filecount . . . . . . $enable_filecount
     fscache . . . . . . . $enable_fscache
@@ -4706,6 +5331,7 @@ Configuration:
     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
@@ -4713,6 +5339,7 @@ Configuration:
     match_timediff  . . . $enable_match_timediff
     match_value . . . . . $enable_match_value
     mbmon . . . . . . . . $enable_mbmon
+    md  . . . . . . . . . $enable_md
     memcachec . . . . . . $enable_memcachec
     memcached . . . . . . $enable_memcached
     memory  . . . . . . . $enable_memory
@@ -4727,12 +5354,14 @@ Configuration:
     notify_desktop  . . . $enable_notify_desktop
     notify_email  . . . . $enable_notify_email
     ntpd  . . . . . . . . $enable_ntpd
+    numa  . . . . . . . . $enable_numa
     nut . . . . . . . . . $enable_nut
     olsrd . . . . . . . . $enable_olsrd
     onewire . . . . . . . $enable_onewire
     openvpn . . . . . . . $enable_openvpn
     oracle  . . . . . . . $enable_oracle
     perl  . . . . . . . . $enable_perl
+    pf  . . . . . . . . . $enable_pf
     pinba . . . . . . . . $enable_pinba
     ping  . . . . . . . . $enable_ping
     postgresql  . . . . . $enable_postgresql
@@ -4740,6 +5369,7 @@ Configuration:
     processes . . . . . . $enable_processes
     protocols . . . . . . $enable_protocols
     python  . . . . . . . $enable_python
+    redis . . . . . . . . $enable_redis
     routeros  . . . . . . $enable_routeros
     rrdcached . . . . . . $enable_rrdcached
     rrdtool . . . . . . . $enable_rrdtool
@@ -4750,24 +5380,32 @@ Configuration:
     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_mongodb . . . . $enable_write_mongodb
+    write_redis . . . . . $enable_write_redis
+    write_riemann . . . . $enable_write_riemann
     xmms  . . . . . . . . $enable_xmms
     zfs_arc . . . . . . . $enable_zfs_arc
 
index bc1fe9f..1ebf1f1 100644 (file)
@@ -101,3 +101,8 @@ solaris-smf
 -----------
   Manifest file for the Solaris SMF system and detailed information on how to
 register collectd as a service with this system.
+
+collectd.service
+----------------
+  Service file for systemd. Please ship this file as
+  /lib/systemd/system/collectd.service in any linux package of collectd.
diff --git a/contrib/collectd.service b/contrib/collectd.service
new file mode 100644 (file)
index 0000000..ee4d596
--- /dev/null
@@ -0,0 +1,15 @@
+[Unit]
+Description=statistics collection daemon
+Documentation=man:collectd(1)
+After=local-fs.target network.target
+Requires=local-fs.target network.target
+
+[Service]
+ExecStart=/usr/sbin/collectd -C /etc/collectd/collectd.conf -f
+Restart=always
+RestartSec=10
+StandardOutput=syslog
+StandardError=syslog
+
+[Install]
+WantedBy=multi-user.target
index 445b183..9af0fb0 100644 (file)
@@ -4,19 +4,23 @@
 #
 # Copyright © 2009 Adrian Perez <aperez@igalia.com>
 #
-# Distributed under terms of the GPLv2 license.
+# Distributed under terms of the GPLv2 license or newer.
+# 
+# Frank Marien (frank@apsu.be) 6 Sep 2012
+# - quick fixes for 5.1 binary protocol
+# - updated to python 3
+# - fixed for larger packet sizes (possible on lo interface)
+# - fixed comment typo (decode_network_string decodes a string)
 
 """
 Collectd network protocol implementation.
 """
 
-import socket
-import struct
-
+import socket,struct,sys
 try:
-    from cStringIO import StringIO
+  from io import StringIO
 except ImportError:
-    from StringIO import StringIO
+  from cStringIO import StringIO
 
 from datetime import datetime
 from copy import deepcopy
@@ -31,17 +35,19 @@ DEFAULT_IPv4_GROUP = "239.192.74.66"
 DEFAULT_IPv6_GROUP = "ff18::efc0:4a42"
 """Default IPv6 multicast group"""
 
-
+HR_TIME_DIV = (2.0**30)
 
 # Message kinds
 TYPE_HOST            = 0x0000
 TYPE_TIME            = 0x0001
+TYPE_TIME_HR         = 0x0008
 TYPE_PLUGIN          = 0x0002
 TYPE_PLUGIN_INSTANCE = 0x0003
 TYPE_TYPE            = 0x0004
 TYPE_TYPE_INSTANCE   = 0x0005
 TYPE_VALUES          = 0x0006
 TYPE_INTERVAL        = 0x0007
+TYPE_INTERVAL_HR     = 0x0009
 
 # For notifications
 TYPE_MESSAGE         = 0x0100
@@ -50,14 +56,14 @@ TYPE_SEVERITY        = 0x0101
 # DS kinds
 DS_TYPE_COUNTER      = 0
 DS_TYPE_GAUGE        = 1
-
+DS_TYPE_DERIVE       = 2
+DS_TYPE_ABSOLUTE     = 3
 
 header = struct.Struct("!2H")
 number = struct.Struct("!Q")
 short  = struct.Struct("!H")
 double = struct.Struct("<d")
 
-
 def decode_network_values(ptype, plen, buf):
     """Decodes a list of DS values in collectd network format
     """
@@ -70,13 +76,19 @@ def decode_network_values(ptype, plen, buf):
     assert double.size == number.size
 
     result = []
-    for dstype in map(ord, buf[header.size+short.size:off]):
+    for dstype in buf[header.size+short.size:off]:
         if dstype == DS_TYPE_COUNTER:
             result.append((dstype, number.unpack_from(buf, off)[0]))
             off += valskip
         elif dstype == DS_TYPE_GAUGE:
             result.append((dstype, double.unpack_from(buf, off)[0]))
             off += valskip
+        elif dstype == DS_TYPE_DERIVE:
+            result.append((dstype, number.unpack_from(buf, off)[0]))
+            off += valskip
+        elif dstype == DS_TYPE_ABSOLUTE:
+            result.append((dstype, number.unpack_from(buf, off)[0]))
+            off += valskip
         else:
             raise ValueError("DS type %i unsupported" % dstype)
 
@@ -84,13 +96,13 @@ def decode_network_values(ptype, plen, buf):
 
 
 def decode_network_number(ptype, plen, buf):
-    """Decodes a number (64-bit unsigned) in collectd network format.
+    """Decodes a number (64-bit unsigned) from collectd network format.
     """
     return number.unpack_from(buf, header.size)[0]
 
 
 def decode_network_string(msgtype, plen, buf):
-    """Decodes a floating point number (64-bit) in collectd network format.
+    """Decodes a string from collectd network format.
     """
     return buf[header.size:plen-1]
 
@@ -99,7 +111,9 @@ def decode_network_string(msgtype, plen, buf):
 _decoders = {
     TYPE_VALUES         : decode_network_values,
     TYPE_TIME           : decode_network_number,
+    TYPE_TIME_HR        : decode_network_number,
     TYPE_INTERVAL       : decode_network_number,
+    TYPE_INTERVAL_HR    : decode_network_number,
     TYPE_HOST           : decode_network_string,
     TYPE_PLUGIN         : decode_network_string,
     TYPE_PLUGIN_INSTANCE: decode_network_string,
@@ -115,6 +129,7 @@ def decode_network_packet(buf):
     """
     off = 0
     blen = len(buf)
+
     while off < blen:
         ptype, plen = header.unpack_from(buf, off)
 
@@ -128,9 +143,6 @@ def decode_network_packet(buf):
         off += plen
 
 
-
-
-
 class Data(object):
     time = 0
     host = None
@@ -140,7 +152,7 @@ class Data(object):
     typeinstance = None
 
     def __init__(self, **kw):
-        [setattr(self, k, v) for k, v in kw.iteritems()]
+        [setattr(self, k, v) for k, v in kw.items()]
 
     @property
     def datetime(self):
@@ -150,19 +162,19 @@ class Data(object):
     def source(self):
         buf = StringIO()
         if self.host:
-            buf.write(self.host)
+            buf.write(str(self.host))
         if self.plugin:
             buf.write("/")
-            buf.write(self.plugin)
+            buf.write(str(self.plugin))
         if self.plugininstance:
             buf.write("/")
-            buf.write(self.plugininstance)
+            buf.write(str(self.plugininstance))
         if self.type:
             buf.write("/")
-            buf.write(self.type)
+            buf.write(str(self.type))
         if self.typeinstance:
             buf.write("/")
-            buf.write(self.typeinstance)
+            buf.write(str(self.typeinstance))
         return buf.getvalue()
 
     def __str__(self):
@@ -215,8 +227,12 @@ def interpret_opcodes(iterable):
     for kind, data in iterable:
         if kind == TYPE_TIME:
             vl.time = nt.time = data
+        elif kind == TYPE_TIME_HR:
+            vl.time = nt.time = data / HR_TIME_DIV
         elif kind == TYPE_INTERVAL:
             vl.interval = data
+        elif kind == TYPE_INTERVAL_HR:
+            vl.interval = data / HR_TIME_DIV
         elif kind == TYPE_HOST:
             vl.host = nt.host = data
         elif kind == TYPE_PLUGIN:
@@ -248,7 +264,7 @@ class Reader(object):
     host = None
     port = DEFAULT_PORT
 
-    BUFFER_SIZE = 1024
+    BUFFER_SIZE = 16384
 
 
     def __init__(self, host=None, port=DEFAULT_PORT, multicast=False):
@@ -311,8 +327,6 @@ class Reader(object):
         """
         if iterable is None:
             iterable = self.decode()
-        if isinstance(iterable, basestring):
+        if isinstance(iterable, str):
             iterable = self.decode(iterable)
         return interpret_opcodes(iterable)
-
-
index 8a5bf85..2b3f2fe 100755 (executable)
@@ -1,14 +1,45 @@
 #!/usr/bin/perl
 
+# Copyright (C) 2008-2011  Florian Forster
+# Copyright (C) 2011       noris network AG
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; only version 2 of the License is applicable.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+# Authors:
+#   Florian "octo" Forster <octo at collectd.org>
+
 use strict;
 use warnings;
-use lib ('../lib');
 use utf8;
+use vars (qw($BASE_DIR));
+
+BEGIN
+{
+  if (defined $ENV{'SCRIPT_FILENAME'})
+  {
+    if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
+    {
+      $::BASE_DIR = $1;
+      unshift (@::INC, "$::BASE_DIR/lib");
+    }
+  }
+}
 
-use FindBin ('$RealBin');
 use Carp (qw(confess cluck));
 use CGI (':cgi');
 use RRDs ();
+use File::Temp (':POSIX');
 
 use Collectd::Graph::Config (qw(gc_read_config gc_get_scalar));
 use Collectd::Graph::TypeLoader (qw(tl_load_type));
@@ -17,200 +48,287 @@ use Collectd::Graph::Common (qw(sanitize_type get_selected_files
       epoch_to_rfc1123 flush_files));
 use Collectd::Graph::Type ();
 
-our $Debug = param ('debug');
-our $Begin = param ('begin');
-our $End = param ('end');
-our $GraphWidth = param ('width');
-our $GraphHeight = param ('height');
-our $Index = param ('index') || 0;
-our $OutputFormat = 'PNG';
-our $ContentType = 'image/png';
-
-if (param ('format'))
+sub base_dir
 {
-  my $temp = param ('format') || '';
-  $temp = uc ($temp);
+  if (defined $::BASE_DIR)
+  {
+    return ($::BASE_DIR);
+  }
 
-  if ($temp =~ m/^(PNG|SVG|EPS|PDF)$/)
+  if (!defined ($ENV{'SCRIPT_FILENAME'}))
   {
-    $OutputFormat = $temp;
+    return;
+  }
 
-    if ($OutputFormat eq 'SVG') { $ContentType = 'image/svg+xml'; }
-    elsif ($OutputFormat eq 'EPS') { $ContentType = 'image/eps'; }
-    elsif ($OutputFormat eq 'PDF') { $ContentType = 'application/pdf'; }
+  if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
+  {
+    $::BASE_DIR = $1;
+    return ($::BASE_DIR);
   }
+
+  return;
 }
 
-if ($Debug)
+sub lib_dir
 {
-  print <<HTTP;
-Content-Type: text/plain
+  my $base = base_dir ();
 
-HTTP
+  if ($base)
+  {
+    return "$base/lib";
+  }
+  else
+  {
+    return "../lib";
+  }
 }
 
-gc_read_config ("$RealBin/../etc/collection.conf");
-
-if ($GraphWidth)
+sub sysconf_dir
 {
-  $GraphWidth =~ s/\D//g;
-}
+  my $base = base_dir ();
 
-if (!$GraphWidth)
-{
-  $GraphWidth = gc_get_scalar ('GraphWidth', 400);
+  if ($base)
+  {
+    return "$base/etc";
+  }
+  else
+  {
+    return "../etc";
+  }
 }
 
-if ($GraphHeight)
+sub init
 {
-  $GraphHeight =~ s/\D//g;
+  my $lib_dir = lib_dir ();
+  my $sysconf_dir = sysconf_dir ();
+
+  if (!grep { $lib_dir eq $_ } (@::INC))
+  {
+    unshift (@::INC, $lib_dir);
+  }
+
+  gc_read_config ("$sysconf_dir/collection.conf");
 }
 
-if (!$GraphHeight)
+sub main
 {
-  $GraphHeight = gc_get_scalar ('GraphHeight', 100);
-}
+  my $Begin = param ('begin');
+  my $End = param ('end');
+  my $GraphWidth = param ('width');
+  my $GraphHeight = param ('height');
+  my $Index = param ('index') || 0;
+  my $OutputFormat = 'PNG';
+  my $ContentType = 'image/png';
 
-{ # Sanitize begin and end times
-  $End ||= 0;
-  $Begin ||= 0;
+  init ();
 
-  if ($End =~ m/\D/)
+  if (param ('format'))
   {
-    $End = 0;
+    my $temp = param ('format') || '';
+    $temp = uc ($temp);
+
+    if ($temp =~ m/^(PNG|SVG|EPS|PDF)$/)
+    {
+      $OutputFormat = $temp;
+
+      if ($OutputFormat eq 'SVG') { $ContentType = 'image/svg+xml'; }
+      elsif ($OutputFormat eq 'EPS') { $ContentType = 'image/eps'; }
+      elsif ($OutputFormat eq 'PDF') { $ContentType = 'application/pdf'; }
+    }
   }
 
-  if (!$Begin || !($Begin =~ m/^-?([1-9][0-9]*)$/))
+  if (param ('debug'))
   {
-    $Begin = -86400;
+    print <<HTTP;
+Content-Type: text/plain
+
+HTTP
+    $ContentType = 'text/plain';
   }
 
-  if ($Begin < 0)
+  if ($GraphWidth)
   {
-    if ($End)
-    {
-      $Begin = $End + $Begin;
-    }
-    else
-    {
-      $Begin = time () + $Begin;
-    }
+    $GraphWidth =~ s/\D//g;
   }
 
-  if ($Begin < 0)
+  if (!$GraphWidth)
   {
-    $Begin = time () - 86400;
+    $GraphWidth = gc_get_scalar ('GraphWidth', 400);
   }
 
-  if (($End > 0) && ($Begin > $End))
+  if ($GraphHeight)
   {
-    my $temp = $End;
-    $End = $Begin;
-    $Begin = $temp;
+    $GraphHeight =~ s/\D//g;
   }
-}
 
-my $type = param ('type') or die;
-my $obj;
+  if (!$GraphHeight)
+  {
+    $GraphHeight = gc_get_scalar ('GraphHeight', 100);
+  }
 
-$obj = tl_load_type ($type);
-if (!$obj)
-{
-  confess ("tl_load_type ($type) failed");
-}
+  { # Sanitize begin and end times
+    $End ||= 0;
+    $Begin ||= 0;
 
-$type = ucfirst (lc ($type));
-$type =~ s/_([A-Za-z])/\U$1\E/g;
-$type = sanitize_type ($type);
+    if ($End =~ m/\D/)
+    {
+      $End = 0;
+    }
 
-my $files = get_selected_files ();
-if ($Debug)
-{
-  require Data::Dumper;
-  print STDOUT Data::Dumper->Dump ([$files], ['files']);
-}
-for (@$files)
-{
-  $obj->addFiles ($_);
-}
+    if (!$Begin || !($Begin =~ m/^-?([1-9][0-9]*)$/))
+    {
+      $Begin = -86400;
+    }
 
-my $expires = time ();
-# IF (End is `now')
-#    OR (Begin is before `now' AND End is after `now')
-if (($End == 0) || (($Begin <= $expires) && ($End >= $expires)))
-{
-  # 400 == width in pixels
-  my $timespan;
+    if ($Begin < 0)
+    {
+      if ($End)
+      {
+        $Begin = $End + $Begin;
+      }
+      else
+      {
+        $Begin = time () + $Begin;
+      }
+    }
+
+    if ($Begin < 0)
+    {
+      $Begin = time () - 86400;
+    }
+
+    if (($End > 0) && ($Begin > $End))
+    {
+      my $temp = $End;
+      $End = $Begin;
+      $Begin = $temp;
+    }
+  }
 
-  if ($End == 0)
+  my $type = param ('type') or die;
+  my $obj;
+
+  $obj = tl_load_type ($type);
+  if (!$obj)
   {
-    $timespan = $expires - $Begin;
+    confess ("tl_load_type ($type) failed");
   }
-  else
+
+  $type = ucfirst (lc ($type));
+  $type =~ s/_([A-Za-z])/\U$1\E/g;
+  $type = sanitize_type ($type);
+
+  my $files = get_selected_files ();
+  if (param ('debug'))
   {
-    $timespan = $End - $Begin;
+    require Data::Dumper;
+    print Data::Dumper->Dump ([$files], ['files']);
+  }
+  for (@$files)
+  {
+    $obj->addFiles ($_);
+  }
+
+  my $expires = time ();
+# IF (End is `now')
+#    OR (Begin is before `now' AND End is after `now')
+  if (($End == 0) || (($Begin <= $expires) && ($End >= $expires)))
+  {
+    # 400 == width in pixels
+    my $timespan;
+
+    if ($End == 0)
+    {
+      $timespan = $expires - $Begin;
+    }
+    else
+    {
+      $timespan = $End - $Begin;
+    }
+    $expires += int ($timespan / 400.0);
   }
-  $expires += int ($timespan / 400.0);
-}
 # IF (End is not `now')
 #    AND (End is before `now')
 # ==> Graph will never change again!
-elsif (($End > 0) && ($End < $expires))
-{
-  $expires += (366 * 86400);
-}
-elsif ($Begin > $expires)
-{
-  $expires = $Begin;
-}
+  elsif (($End > 0) && ($End < $expires))
+  {
+    $expires += (366 * 86400);
+  }
+  elsif ($Begin > $expires)
+  {
+    $expires = $Begin;
+  }
 
 # Send FLUSH command to the daemon if necessary and possible.
-flush_files ($files,
+  flush_files ($files,
     begin => $Begin,
     end => $End,
     addr => gc_get_scalar ('UnixSockAddr', undef),
     interval => gc_get_scalar ('Interval', 10));
 
-print STDOUT header (-Content_type => $ContentType,
-  -Last_Modified => epoch_to_rfc1123 ($obj->getLastModified ()),
-  -Expires => epoch_to_rfc1123 ($expires));
-
-if ($Debug)
-{
-  print "\$expires = $expires;\n";
-}
+  print header (-Content_type => $ContentType,
+    -Last_Modified => epoch_to_rfc1123 ($obj->getLastModified ()),
+    -Expires => epoch_to_rfc1123 ($expires));
 
-my $args = $obj->getRRDArgs (0 + $Index);
-
-if ($Debug)
-{
-  require Data::Dumper;
-  print STDOUT Data::Dumper->Dump ([$obj], ['obj']);
-  print STDOUT join (",\n", @$args) . "\n";
-  print STDOUT "Last-Modified: " . epoch_to_rfc1123 ($obj->getLastModified ()) . "\n";
-}
-else
-{
-  my @timesel = ();
+  if (param ('debug'))
+  {
+    print "\$expires = $expires;\n";
+  }
 
-  if ($End) # $Begin is always true
+  my $args = $obj->getRRDArgs (0 + $Index);
+  if (param ('debug'))
   {
-    @timesel = ('-s', $Begin, '-e', $End);
+    require Data::Dumper;
+    print Data::Dumper->Dump ([$obj], ['obj']);
+    print join (",\n", @$args) . "\n";
+    print "Last-Modified: " . epoch_to_rfc1123 ($obj->getLastModified ()) . "\n";
   }
   else
   {
-    @timesel = ('-s', $Begin); # End is implicitely `now'.
-  }
+    my @timesel = ();
+    my $tmpfile = tmpnam ();
+    my $status;
 
-  $| = 1;
-  RRDs::graph ('-', '-a', $OutputFormat, '--width', $GraphWidth, '--height', $GraphHeight, @timesel, @$args);
-  if (my $err = RRDs::error ())
-  {
-    print STDERR "RRDs::graph failed: $err\n";
-    exit (1);
+    if ($End) # $Begin is always true
+    {
+      @timesel = ('-s', $Begin, '-e', $End);
+    }
+    else
+    {
+      @timesel = ('-s', $Begin); # End is implicitely `now'.
+    }
+
+    if (-S "/var/run/rrdcached.sock" && -w "/var/run/rrdcached.sock")
+    {
+      $ENV{"RRDCACHED_ADDRESS"} = "/var/run/rrdcached.sock";
+    }
+    unlink ($tmpfile);
+    RRDs::graph ($tmpfile, '-a', $OutputFormat, '--width', $GraphWidth, '--height', $GraphHeight, @timesel, @$args);
+    if (my $err = RRDs::error ())
+    {
+      print STDERR "RRDs::graph failed: $err\n";
+      exit (1);
+    }
+
+    $status = open (IMG, '<', $tmpfile) or die ("open ($tmpfile): $!");
+    if (!$status)
+    {
+      print STDERR "graph.cgi: Unable to open temporary file \"$tmpfile\" for reading: $!\n";
+    }
+    else
+    {
+      local $/ = undef;
+      while (my $data = <IMG>)
+      {
+        print STDOUT $data;
+      }
+
+      close (IMG);
+      unlink ($tmpfile);
+    }
   }
-}
+} # sub main
 
-exit (0);
+main ();
 
 # vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
index e28e985..4723af9 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/perl
 
-# Copyright (C) 2008  Florian octo Forster <octo at verplant.org>
+# Copyright (C) 2008-2011  Florian Forster
+# Copyright (C) 2011       noris network AG
 #
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
 # 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+# Authors:
+#   Florian "octo" Forster <octo at collectd.org>
 
 use strict;
 use warnings;
-use lib ('../lib');
 use utf8;
+use vars (qw($BASE_DIR));
+
+BEGIN
+{
+  if (defined $ENV{'SCRIPT_FILENAME'})
+  {
+    if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
+    {
+      $::BASE_DIR = $1;
+      unshift (@::INC, "$::BASE_DIR/lib");
+    }
+  }
+}
 
 use Carp (qw(cluck confess));
-use FindBin ('$RealBin');
 use CGI (':cgi');
 use CGI::Carp ('fatalsToBrowser');
 use HTML::Entities ('encode_entities');
@@ -35,8 +50,6 @@ use Collectd::Graph::Common (qw(get_files_from_directory get_all_hosts
       get_plugin_selection flush_files));
 use Collectd::Graph::Type ();
 
-our $Debug = param ('debug') ? 1 : 0;
-
 our $TimeSpans =
 {
   Hour  =>        3600,
@@ -46,23 +59,90 @@ our $TimeSpans =
   Year  => 366 * 86400
 };
 
-my $action = param ('action') || 'list_hosts';
-our %Actions =
+my %Actions =
 (
   list_hosts => \&action_list_hosts,
   show_selection => \&action_show_selection
 );
 
-if (!exists ($Actions{$action}))
+sub base_dir
 {
-  print STDERR "No such action: $action\n";
-  exit 1;
+  if (defined $::BASE_DIR)
+  {
+    return ($::BASE_DIR);
+  }
+
+  if (!defined ($ENV{'SCRIPT_FILENAME'}))
+  {
+    return;
+  }
+
+  if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
+  {
+    $::BASE_DIR = $1;
+    return ($::BASE_DIR);
+  }
+
+  return;
 }
 
-gc_read_config ("$RealBin/../etc/collection.conf");
+sub lib_dir
+{
+  my $base = base_dir ();
 
-$Actions{$action}->();
-exit (0);
+  if ($base)
+  {
+    return "$base/lib";
+  }
+  else
+  {
+    return "../lib";
+  }
+}
+
+sub sysconf_dir
+{
+  my $base = base_dir ();
+
+  if ($base)
+  {
+    return "$base/etc";
+  }
+  else
+  {
+    return "../etc";
+  }
+}
+
+sub init
+{
+  my $lib_dir = lib_dir ();
+  my $sysconf_dir = sysconf_dir ();
+
+  if (!grep { $lib_dir eq $_ } (@::INC))
+  {
+    unshift (@::INC, $lib_dir);
+  }
+
+  gc_read_config ("$sysconf_dir/collection.conf");
+}
+
+sub main
+{
+  my $Debug = param ('debug') ? 1 : 0;
+  my $action = param ('action') || 'list_hosts';
+
+  if (!exists ($Actions{$action}))
+  {
+    print STDERR "No such action: $action\n";
+    return (1);
+  }
+
+  init ();
+
+  $Actions{$action}->();
+  return (1);
+} # sub main
 
 sub can_handle_xhtml
 {
@@ -104,7 +184,7 @@ sub can_handle_xhtml
   }
 } # can_handle_xhtml
 
-{my $html_started;
+my $html_started;
 sub start_html
 {
   return if ($html_started);
@@ -119,9 +199,8 @@ sub start_html
 
   if (can_handle_xhtml ())
   {
+    print header (-Content_Type => 'application/xhtml+xml; charset=UTF-8');
     print <<HTML;
-Content-Type: application/xhtml+xml; charset=UTF-8
-
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
@@ -133,9 +212,8 @@ HTML
   }
   else
   {
+    print header (-Content_Type => 'text/html; charset=UTF-8');
     print <<HTML;
-Content-Type: text/html; charset=UTF-8
-
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
     "http://www.w3.org/TR/html4/strict.dtd">
 <html>
@@ -151,7 +229,7 @@ HTML
   <body onload="nav_init ($begin, $end);">
 HTML
   $html_started = 1;
-}}
+}
 
 sub end_html
 {
@@ -159,6 +237,7 @@ sub end_html
   </body>
 </html>
 HTML
+  $html_started = 0;
 }
 
 sub contains_invalid_chars
@@ -308,7 +387,7 @@ sub action_show_selection
   $all_files = get_selected_files ();
   $timespan = get_timespan_selection ();
 
-  if ($Debug)
+  if (param ('debug'))
   {
     print "<pre>", Data::Dumper->Dump ([$all_files], ['all_files']), "</pre>\n";
   }
@@ -333,7 +412,7 @@ sub action_show_selection
       $types->{$type} = tl_load_type ($file->{'type'});
       if (!$types->{$type})
       {
-        cluck ("tl_load_type (" . $file->{'type'} . ") failed");
+        warn ("tl_load_type (" . $file->{'type'} . ") failed");
         next;
       }
     }
@@ -346,6 +425,12 @@ sub action_show_selection
   for (sort (keys %$types))
   {
     my $type = $_;
+
+    if (!defined ($types->{$type}))
+    {
+      next;
+    }
+
     my $graphs_num = $types->{$type}->getGraphsNum ();
 
     for (my $i = 0; $i < $graphs_num; $i++)
@@ -400,6 +485,8 @@ EOF
   end_html ();
 }
 
+main ();
+
 =head1 SEE ALSO
 
 L<Collectd::Graph::Type>
index 3bb3d8b..3e19bfc 100644 (file)
@@ -2,24 +2,24 @@
 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
+  Color value 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"
-  Color count 00d000
+  Color value 00d000
 </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,12 +267,12 @@ 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"
-  Color frequency a000a0
+  Color value a000a0
 </Type>
 <Type humidity>
   DataSources value
@@ -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
+  Color value 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,12 +700,12 @@ 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"
-  Color users 0000f0
+  Color value 0000f0
 </Type>
 <Type voltage>
   DataSources value
index c6e2508..cc7e141 100644 (file)
@@ -1,5 +1,24 @@
 package Collectd::Graph::Common;
 
+# Copyright (C) 2008-2011  Florian Forster
+# Copyright (C) 2011       noris network AG
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; only version 2 of the License is applicable.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+# Authors:
+#   Florian "octo" Forster <octo at collectd.org>
+
 use strict;
 use warnings;
 
@@ -11,6 +30,8 @@ use CGI (':cgi');
 use Exporter;
 use Collectd::Graph::Config (qw(gc_get_scalar));
 
+our $Cache = {};
+
 $ColorCanvas   = 'FFFFFF';
 $ColorFullBlue = '0000FF';
 $ColorHalfBlue = 'B7B7F7';
@@ -191,22 +212,48 @@ sub ident_to_filename
   return ($ret);
 } # ident_to_filename
 
+sub _part_to_string
+{
+  my $part = shift;
+
+  if (!defined ($part))
+  {
+    return ("(UNDEF)");
+  }
+  if (ref ($part) eq 'ARRAY')
+  {
+    if (1 == @$part)
+    {
+      return ($part->[0]);
+    }
+    else
+    {
+      return ('(' . join (',', @$part) . ')');
+    }
+  }
+  else
+  {
+    return ($part);
+  }
+} # _part_to_string
+
 sub ident_to_string
 {
   my $ident = shift;
 
   my $ret = '';
 
-  $ret .= $ident->{'hostname'} . '/' . $ident->{'plugin'};
+  $ret .= _part_to_string ($ident->{'hostname'})
+  . '/' . _part_to_string ($ident->{'plugin'});
   if (defined ($ident->{'plugin_instance'}))
   {
-    $ret .= '-' . $ident->{'plugin_instance'};
+    $ret .= '-' . _part_to_string ($ident->{'plugin_instance'});
   }
 
-  $ret .= '/' . $ident->{'type'};
+  $ret .= '/' . _part_to_string ($ident->{'type'});
   if (defined ($ident->{'type_instance'}))
   {
-    $ret .= '-' . $ident->{'type_instance'};
+    $ret .= '-' . _part_to_string ($ident->{'type_instance'});
   }
 
   return ($ret);
@@ -258,27 +305,36 @@ sub get_files_from_directory
 
 sub get_all_hosts
 {
-  my $dh;
-  my @ret = ();
-  my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
+  my $ret = [];
 
-  opendir ($dh, "$data_dir") or confess ("opendir ($data_dir): $!");
-  while (my $entry = readdir ($dh))
+  if (defined ($Cache->{'get_all_hosts'}))
   {
-    next if ($entry =~ m/^\./);
-    next if (!-d "$data_dir/$entry");
-    next if (!-r "$data_dir/$entry" or !-x "$data_dir/$entry");
-    push (@ret, sanitize_hostname ($entry));
+    $ret = $Cache->{'get_all_hosts'};
+  }
+  else
+  {
+    my $dh;
+    my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
+
+    opendir ($dh, "$data_dir") or confess ("opendir ($data_dir): $!");
+    while (my $entry = readdir ($dh))
+    {
+      next if ($entry =~ m/^\./);
+      next if (!-d "$data_dir/$entry");
+      push (@$ret, sanitize_hostname ($entry));
+    }
+    closedir ($dh);
+
+    $Cache->{'get_all_hosts'} = $ret;
   }
-  closedir ($dh);
 
   if (wantarray ())
   {
-    return (@ret);
+    return (@$ret);
   }
-  elsif (@ret)
+  elsif (@$ret)
   {
-    return (\@ret);
+    return ($ret);
   }
   else
   {
@@ -292,12 +348,32 @@ sub get_all_plugins
   my $ret = {};
   my $dh;
   my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
+  my $cache_key;
 
-  if (!@hosts)
+  if (@hosts)
   {
+    $cache_key = join (';', @hosts);
+  }
+  else
+  {
+    $cache_key = "/*/";
     @hosts = get_all_hosts ();
   }
 
+  if (defined ($Cache->{'get_all_plugins'}{$cache_key}))
+  {
+    $ret = $Cache->{'get_all_plugins'}{$cache_key};
+
+    if (wantarray ())
+    {
+      return (sort (keys %$ret));
+    }
+    else
+    {
+      return ($ret);
+    }
+  }
+
   for (@hosts)
   {
     my $host = $_;
@@ -331,6 +407,7 @@ sub get_all_plugins
     closedir ($dh);
   } # for (@hosts)
 
+  $Cache->{'get_all_plugins'}{$cache_key} = $ret;
   if (wantarray ())
   {
     return (sort (keys %$ret));
@@ -386,24 +463,44 @@ sub _filter_ident
   return (0);
 } # _filter_ident
 
+sub _get_all_files
+{
+  my $ret;
+
+  if (defined ($Cache->{'_get_all_files'}))
+  {
+    $ret = $Cache->{'_get_all_files'};
+  }
+  else
+  {
+    my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
+
+    $ret = get_files_from_directory ($data_dir, 3);
+    $Cache->{'_get_all_files'} = $ret;
+  }
+
+  return ($ret);
+} # _get_all_files
+
 sub get_files_by_ident
 {
   my $ident = shift;
   my $all_files;
   my @ret = ();
-  my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
 
-  #if ($ident->{'hostname'})
-  #{
-  #$all_files = get_files_for_host ($ident->{'hostname'});
-  #}
-  #else
-  #{
-    $all_files = get_files_from_directory ($data_dir, 3);
-    #}
+  my $cache_key = ident_to_string ($ident);
+  if (defined ($Cache->{'get_files_by_ident'}{$cache_key}))
+  {
+    my $ret = $Cache->{'get_files_by_ident'}{$cache_key};
+
+    return ($ret)
+  }
+
+  $all_files = _get_all_files ();
 
   @ret = grep { _filter_ident ($ident, $_) == 0 } (@$all_files);
 
+  $Cache->{'get_files_by_ident'}{$cache_key} = \@ret;
   return (\@ret);
 } # get_files_by_ident
 
index 9078110..5a0b522 100644 (file)
@@ -92,7 +92,8 @@ sub _load_module_from_config
     $obj = _create_object ($module);
     if (!$obj)
     {
-      cluck ("Creating an $module object failed");
+      #cluck ("Creating an $module object failed");
+      warn ("Creating an $module object failed");
       return;
     }
   }
diff --git a/contrib/exec-ksm.sh b/contrib/exec-ksm.sh
new file mode 100755 (executable)
index 0000000..a21aa1d
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# collectd - contrib/exec-ksm.sh
+# Copyright (C) 2011  Florian Forster
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+# Authors:
+#   Florian Forster <octo at collectd.org>
+
+HOSTNAME="${COLLECTD_HOSTNAME:-$(hostname -f)}"
+INTERVAL="${COLLECTD_INTERVAL:-60}"
+
+function read_file() {
+  local type="$1"
+  local type_instance="$2"
+  local file_name="/sys/kernel/mm/ksm/$3"
+  local ident="$HOSTNAME/exec-ksm/vmpage_number-${type_instance}"
+
+  echo "PUTVAL \"$ident\" interval=$INTERVAL N:$(< $file_name)"
+}
+
+if [[ 0 -eq $(< /sys/kernel/mm/ksm/run) ]]; then
+  echo "$0: KSM not active." >&2
+  exit 1
+fi
+
+while sleep "$INTERVAL"
+do
+  read_file vmpage_number    shared   pages_shared
+  read_file vmpage_number    saved    pages_sharing
+  read_file vmpage_number    unshared pages_unshared
+  read_file vmpage_number    volatile pages_volatile
+  read_file total_operations scan     full_scans
+done
+
+exit 0
index 907ea9b..3e62ce0 100755 (executable)
@@ -46,7 +46,8 @@ use Regexp::Common ('number');
 our $ConfigFile = '/etc/exec-munin.conf';
 our $TypeMap = {};
 our $Scripts = [];
-our $Interval = 300;
+our $Interval = defined ($ENV{'COLLECTD_INTERVAL'}) ? (0 + $ENV{'COLLECTD_INTERVAL'}) : 300;
+our $Hostname = defined ($ENV{'COLLECTD_HOSTNAME'}) ? $ENV{'COLLECTD_HOSTNAME'} : '';
 
 main ();
 exit (0);
@@ -189,7 +190,7 @@ sub execute_script
   my $pinst;
   my $time = time ();
   my $script = shift;
-  my $host = hostname () || 'localhost';
+  my $host = $Hostname || hostname () || 'localhost';
   if (!open ($fh, '-|', $script))
   {
     print STDERR "Cannot execute $script: $!";
@@ -206,8 +207,11 @@ sub execute_script
       my $field = $1;
       my $value = $2;
       my $type = (defined ($TypeMap->{$field})) ? $TypeMap->{$field} : $field;
+      my $ident = "$host/munin-$pinst/$type";
 
-      print "$host/munin-$pinst/$type interval=$Interval $time:$value\n";
+      $ident =~ s/"/\\"/g;
+
+      print qq(PUTVAL "$ident" interval=$Interval $time:$value\n);
     }
   }
 
index 02bd0a3..ec13b0a 100755 (executable)
@@ -25,7 +25,8 @@ our $ConfigFile = '/etc/exec-nagios.conf';
 our $TypeMap = {};
 our $NRPEMap = {};
 our $Scripts = [];
-our $Interval = 300;
+our $Interval = defined ($ENV{'COLLECTD_INTERVAL'}) ? (0 + $ENV{'COLLECTD_INTERVAL'}) : 300;
+our $Hostname = defined ($ENV{'COLLECTD_HOSTNAME'}) ? $ENV{'COLLECTD_HOSTNAME'} : '';
 
 main ();
 exit (0);
@@ -351,6 +352,7 @@ sub handle_performance_data
   my $type = shift;
   my $time = shift;
   my $line = shift;
+  my $ident = "$host/$plugin-$pinst/$type-$tinst";
 
   my $tinst;
   my $value;
@@ -366,7 +368,9 @@ sub handle_performance_data
     return;
   }
 
-  print "PUTVAL $host/$plugin-$pinst/$type-$tinst interval=$Interval ${time}:$value\n";
+  $ident =~ s/"/\\"/g;
+
+  print qq(PUTVAL "$ident" interval=$Interval ${time}:$value\n);
 }
 
 sub execute_script
@@ -376,7 +380,7 @@ sub execute_script
   my $time = time ();
   my $script = shift;
   my @args = ();
-  my $host = hostname () || 'localhost';
+  my $host = $Hostname || hostname () || 'localhost';
 
   my $state = 0;
   my $serviceoutput;
@@ -438,7 +442,7 @@ sub execute_script
 
   close ($fh);
   # Save the exit status of the check in $state
-  $state = $?;
+  $state = $? >> 8;
 
   if ($state == 0)
   {
index d469816..99b6986 100755 (executable)
 # smart   ALL = (root) NOPASSWD: SMARTCTL
 # -- >8 --
 
-HOST="huhu"
-INTERVAL=60
+HOSTNAME="${COLLECTD_HOSTNAME:-`hostname -f`}"
+INTERVAL="${COLLECTD_INTERVAL:-60}"
 
-while true
+while sleep "$INTERVAL"
 do
        TEMP=$((sudo smartctl -d 3ware,0 -A /dev/twe0 | grep Temperature_Celsius | awk '{ print $10; }') 2>/dev/null);
        if [ $? -ne 0 ]
        then
                TEMP="U"
        fi
-       echo "$HOST/exec-smart/temperature-3ware_0 interval=$INTERVAL N:$TEMP"
+       echo "PUTVAL $HOSTNAME/exec-smart/temperature-3ware_0 interval=$INTERVAL N:$TEMP"
 
        TEMP=$((sudo smartctl -d 3ware,1 -A /dev/twe0 | grep Temperature_Celsius | awk '{ print $10; }') 2>/dev/null);
        if [ $? -ne 0 ]
        then
                TEMP="U"
        fi
-       echo "$HOST/exec-smart/temperature-3ware_1 interval=$INTERVAL N:$TEMP"
+       echo "PUTVAL $HOSTNAME/exec-smart/temperature-3ware_1 interval=$INTERVAL N:$TEMP"
 
        TEMP=$((sudo smartctl -d ata -A /dev/sda | grep Temperature_Celsius | awk '{ print $10; }') 2>/dev/null);
        if [ $? -ne 0 ]
        then
                TEMP="U"
        fi
-       echo "$HOST/exec-smart/temperature-sata_0 interval=$INTERVAL N:$TEMP"
-
-       sleep $INTERVAL
+       echo "PUTVAL $HOSTNAME/exec-smart/temperature-sata_0 interval=$INTERVAL N:$TEMP"
 done
diff --git a/contrib/migrate-4-5.px b/contrib/migrate-4-5.px
new file mode 100755 (executable)
index 0000000..c2a9555
--- /dev/null
@@ -0,0 +1,255 @@
+#!/usr/bin/perl
+
+# collectd - contrib/migrate-4-5.px
+# Copyright (C) 2010  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>
+
+use strict;
+use warnings;
+
+use Getopt::Long ('GetOptions');
+use Data::Dumper ();
+use File::Basename ('dirname');
+
+our $InDir = '/var/lib/collectd';
+our $RRDtool = 'rrdtool';
+our $RRDFilter = 'rrd_filter.px';
+
+our %TypesCounterToDerive = # {{{
+(
+  apache_bytes => ["count"],
+  apache_requests => ["count"],
+  arc_counts => ["demand_data", "demand_metadata", "prefetch_data", "prefetch_metadata"],
+  arc_l2_bytes => ["read", "write"],
+  ath_stat => ["value"],
+  compression => ["uncompressed", "compressed"],
+  connections => ["value"],
+  cpu => ["value"],
+  current => ["value"],
+  disk_merged => ["read", "write"],
+  disk_octets => ["read", "write"],
+  disk_ops => ["read", "write"],
+  disk_ops_complex => ["value"],
+  disk_time => ["read", "write"],
+  dns_answer => ["value"],
+  dns_notify => ["value"],
+  dns_octets => ["queries", "responses"],
+  dns_opcode => ["value"],
+  dns_qtype => ["value"],
+  dns_query => ["value"],
+  dns_question => ["value"],
+  dns_rcode => ["value"],
+  dns_reject => ["value"],
+  dns_request => ["value"],
+  dns_resolver => ["value"],
+  dns_response => ["value"],
+  dns_transfer => ["value"],
+  dns_update => ["value"],
+  dns_zops => ["value"],
+  fscache_stat => ["value"],
+  fork_rate => ["value"],
+  http_request_methods => ["count"],
+  http_requests => ["count"],
+  http_response_codes => ["count"],
+  if_collisions => ["value"],
+  if_dropped => ["rx", "tx"],
+  if_errors => ["rx", "tx"],
+  if_multicast => ["value"],
+  if_octets => ["rx", "tx"],
+  if_packets => ["rx", "tx"],
+  if_rx_errors => ["value"],
+  if_tx_errors => ["value"],
+  io_octets => ["rx", "tx"],
+  io_packets => ["rx", "tx"],
+  ipt_bytes => ["value"],
+  ipt_packets => ["value"],
+  irq => ["value"],
+  memcached_command => ["value"],
+  memcached_octets => ["rx", "tx"],
+  memcached_ops => ["value"],
+  mysql_commands => ["value"],
+  mysql_handler => ["value"],
+  mysql_locks => ["value"],
+  mysql_log_position => ["value"],
+  mysql_octets => ["rx", "tx"],
+  nfs_procedure => ["value"],
+  nginx_requests => ["value"],
+  node_octets => ["rx", "tx"],
+  node_stat => ["value"],
+  operations => ["value"],
+  pg_blks => ["value"],
+  pg_n_tup_c => ["value"],
+  pg_scan => ["value"],
+  pg_xact => ["value"],
+  protocol_counter => ["value"],
+  ps_cputime => ["user", "syst"],
+  ps_pagefaults => ["minflt", "majflt"],
+  ps_code => ["value"],
+  ps_data => ["value"],
+  serial_octets => ["rx", "tx"],
+  swap_io => ["value"],
+  virt_cpu_total => ["ns"],
+  virt_vcpu => ["ns"],
+  vmpage_action => ["value"],
+  vmpage_faults => ["minflt", "majflt"],
+  vmpage_io => ["in", "out"],
+); # }}} %TypesCounterToDerive
+
+our %TypesRenameDataSource = # {{{
+(
+  absolute => "count",
+  apache_bytes => "count",
+  apache_connections => "count",
+  apache_idle_workers => "count",
+  apache_requests => "count",
+  apache_scoreboard => "count",
+  conntrack => "entropy",
+  contextswitch => "contextswitches",
+  delay => "seconds",
+  entropy => "entropy",
+  file_size => "bytes",
+  frequency => "frequency",
+  frequency_offset => "ppm",
+  http_request_methods => "count",
+  http_requests => "count",
+  http_response_codes => "count",
+  percent => "percent",
+  ping => "ping",
+  records => "count",
+  time_dispersion => "seconds",
+  timeleft => "timeleft",
+  time_offset => "seconds",
+  users => "users",
+  virt_cpu_total => "ns",
+  virt_vcpu => "ns",
+); # }}} %TypesRenameDataSource
+
+sub handle_file # {{{
+{
+  my @path = @_;
+  my $path = join ('/', @path);
+
+  if (!($path =~ m/\.rrd$/))
+  {
+    return;
+  }
+
+  my $tmp = pop (@path);
+  $tmp =~ s/\.rrd$//;
+  my ($type, $type_inst) = split (m/-/, $tmp, 2);
+  $type_inst ||= '';
+
+  $tmp = pop (@path);
+  my ($plugin, $plugin_inst) = split (m/-/, $tmp, 2);
+  $plugin_inst ||= '';
+
+  if ($TypesRenameDataSource{$type})
+  {
+    my $old_ds = $TypesRenameDataSource{$type};
+    print "$RRDtool tune \"$path\" --data-source-rename ${old_ds}:value\n";
+  }
+
+  if ($TypesCounterToDerive{$type})
+  {
+    my $ds_names = $TypesCounterToDerive{$type};
+    
+    for (@$ds_names)
+    {
+      my $name = $_;
+      print "$RRDtool tune \"$path\" --data-source-type ${name}:DERIVE --minimum ${name}:0 --maximum ${name}:U\n";
+    }
+  }
+
+  if ((($plugin eq 'df') || ($plugin eq 'interface'))
+    && (!$plugin_inst) && ($type_inst))
+  {
+    my $dir = join ('/', @path);
+    print "mkdir -p \"$dir/$plugin-$type_inst\"\n";
+    if (($plugin eq 'df') and ($type eq 'df'))
+    {
+      print "$RRDFilter --infile=\"$path\" --outfile=\"$dir/$plugin-$type_inst/df_complex-free.rrd\" --map free:value\n";
+      print "$RRDFilter --infile=\"$path\" --outfile=\"$dir/$plugin-$type_inst/df_complex-used.rrd\" --map used:value\n";
+    }
+    else
+    {
+      print "mv \"$path\" \"$dir/$plugin-$type_inst/$type.rrd\"\n";
+    }
+  }
+} # }}} sub handle_file
+
+sub scan_dir # {{{
+{
+  my @dir_parts = @_;
+  my $dir_str = join ('/', @dir_parts);
+  my $dh;
+
+  opendir ($dh, $dir_str) || die;
+  while (my $entry = readdir ($dh))
+  {
+    my $entry_path = "$dir_str/$entry";
+
+    if ($entry =~ m/^\./)
+    {
+      next;
+    }
+
+    if (-d $entry_path)
+    {
+      scan_dir (@dir_parts, $entry);
+    }
+    elsif (-f $entry_path)
+    {
+      handle_file (@dir_parts, $entry);
+    }
+  }
+  closedir ($dh);
+} # }}} sub scan_dir
+
+sub exit_usage # {{{
+{
+  print STDERR <<EOF;
+migrate-4-5.px [OPTIONS]
+
+Valid options are:
+
+  --indir <dir>         Source directory
+                        Default: $InDir
+  --rrdtool <path>      Path to the RRDtool binary
+                        Default: $RRDtool
+  --rrdfilter <path>    Path to the rrd_filter.px script
+                        Default: $RRDFilter
+
+EOF
+  exit (1);
+} # }}} sub exit_usage
+
+GetOptions ("indir|i=s" => \$InDir,
+        "rrdtool=s" => \$RRDtool,
+        "rrdfilter=s" => \$RRDFilter,
+        "help|h" => \&exit_usage) or exit_usage ();
+
+print "#!/bin/bash\n\n";
+
+scan_dir ($InDir);
+
+# vim: set sw=2 sts=2 et fdm=marker :
index c84aabe..7297d85 100644 (file)
@@ -48,9 +48,9 @@ function load_graph_definitions($logarithmic = false, $tinylegend = false) {
        $GraphDefs = array();
        $GraphDefs['apache_bytes'] = array(
                '-v', 'Bits/s',
-               'DEF:min_raw={file}:count:MIN',
-               'DEF:avg_raw={file}:count:AVERAGE',
-               'DEF:max_raw={file}:count:MAX',
+               'DEF:min_raw={file}:value:MIN',
+               'DEF:avg_raw={file}:value:AVERAGE',
+               'DEF:max_raw={file}:value:MAX',
                'CDEF:min=min_raw,8,*',
                'CDEF:avg=avg_raw,8,*',
                'CDEF:max=max_raw,8,*',
@@ -68,9 +68,9 @@ function load_graph_definitions($logarithmic = false, $tinylegend = false) {
                'GPRINT:avg_sum:LAST:(ca. %5.1lf%sB Total)\l');
        $GraphDefs['apache_requests'] = array(
                '-v', 'Requests/s',
-               'DEF:min={file}:count:MIN',
-               'DEF:avg={file}:count:AVERAGE',
-               'DEF:max={file}:count:MAX',
+               'DEF:min={file}:value:MIN',
+               'DEF:avg={file}:value:AVERAGE',
+               'DEF:max={file}:value:MAX',
                "AREA:max#$HalfBlue",
                "AREA:min#$Canvas",
                "LINE1:avg#$FullBlue:Requests/s",
@@ -79,9 +79,9 @@ function load_graph_definitions($logarithmic = false, $tinylegend = false) {
                'GPRINT:max:MAX:%6.2lf Max,',
                'GPRINT:avg:LAST:%6.2lf Last');
        $GraphDefs['apache_scoreboard'] = array(
-               'DEF:min={file}:count:MIN',
-               'DEF:avg={file}:count:AVERAGE',
-               'DEF:max={file}:count:MAX',
+               'DEF:min={file}:value:MIN',
+               'DEF:avg={file}:value:AVERAGE',
+               'DEF:max={file}:value:MAX',
                "AREA:max#$HalfBlue",
                "AREA:min#$Canvas",
                "LINE1:avg#$FullBlue:Processes",
@@ -2024,7 +2024,7 @@ function meta_graph_apache_scoreboard($host, $plugin, $plugin_instance, $type, $
                if ($file == '')
                        continue;
 
-               $sources[] = array('name'=>$inst, 'file'=>$file, 'ds'=>'count');
+               $sources[] = array('name'=>$inst, 'file'=>$file, 'ds'=>'value');
        }
 
        return collectd_draw_meta_stack($opts, $sources);
diff --git a/contrib/postgresql/collectd_insert.sql b/contrib/postgresql/collectd_insert.sql
new file mode 100644 (file)
index 0000000..00c5519
--- /dev/null
@@ -0,0 +1,234 @@
+-- collectd - contrib/postgresql/collectd_insert.sql
+-- Copyright (C) 2012 Sebastian 'tokkee' Harl
+-- All rights reserved.
+--
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions
+-- are met:
+--
+-- - Redistributions of source code must retain the above copyright
+--   notice, this list of conditions and the following disclaimer.
+--
+-- - Redistributions in binary form must reproduce the above copyright
+--   notice, this list of conditions and the following disclaimer in the
+--   documentation and/or other materials provided with the distribution.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+-- POSSIBILITY OF SUCH DAMAGE.
+
+-- Description:
+-- ------------
+--
+-- This is a sample database setup that may be used to write data collected by
+-- collectd to a PostgreSQL database. We're using two tables, 'identifiers'
+-- 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
+-- keep the setup running -- see the comments below for details.
+--
+-- The function 'collectd_insert' may be used to actually insert values
+-- submitted by collectd into those tables.
+--
+-- Sample configuration:
+-- ---------------------
+--
+-- <Plugin postgresql>
+--     <Writer sqlstore>
+--         Statement "SELECT collectd_insert($1, $2, $3, $4, $5, $6, $7, $8, $9);"
+--     </Writer>
+--     <Database foo>
+--         # ...
+--         Writer sqlstore
+--     </Database>
+-- </Plugin>
+
+CREATE TABLE identifiers (
+    id integer NOT NULL,
+    host character varying(64) NOT NULL,
+    plugin character varying(64) NOT NULL,
+    plugin_inst character varying(64) DEFAULT NULL::character varying,
+    type character varying(64) NOT NULL,
+    type_inst character varying(64) DEFAULT NULL::character varying
+);
+CREATE SEQUENCE identifiers_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+ALTER SEQUENCE identifiers_id_seq OWNED BY identifiers.id;
+ALTER TABLE ONLY identifiers
+    ALTER COLUMN id SET DEFAULT nextval('identifiers_id_seq'::regclass);
+ALTER TABLE ONLY identifiers
+    ADD CONSTRAINT identifiers_host_plugin_plugin_inst_type_type_inst_key
+        UNIQUE (host, plugin, plugin_inst, type, type_inst);
+ALTER TABLE ONLY identifiers
+    ADD CONSTRAINT identifiers_pkey PRIMARY KEY (id);
+
+-- optionally, create indexes for the identifier fields
+CREATE INDEX identifiers_host ON identifiers USING btree (host);
+CREATE INDEX identifiers_plugin ON identifiers USING btree (plugin);
+CREATE INDEX identifiers_plugin_inst ON identifiers USING btree (plugin_inst);
+CREATE INDEX identifiers_type ON identifiers USING btree (type);
+CREATE INDEX identifiers_type_inst ON identifiers USING btree (type_inst);
+
+CREATE TABLE "values" (
+    id integer NOT NULL,
+    tstamp timestamp with time zone NOT NULL,
+    name character varying(64) NOT NULL,
+    value double precision NOT NULL
+);
+
+CREATE OR REPLACE VIEW collectd
+    AS SELECT host, plugin, plugin_inst, type, type_inst,
+            host
+                || '/' || plugin
+                || CASE
+                    WHEN plugin_inst IS NOT NULL THEN '-'
+                    ELSE ''
+                END
+                || coalesce(plugin_inst, '')
+                || '/' || type
+                || CASE
+                    WHEN type_inst IS NOT NULL THEN '-'
+                    ELSE ''
+                END
+                || coalesce(type_inst, '') AS identifier,
+            tstamp, name, value
+        FROM identifiers
+            JOIN values
+            ON values.id = identifiers.id;
+
+-- partition "values" by day (or week, month, ...)
+
+-- create the child tables for today and the next 'days' days:
+-- this may, for example, be used in a daily cron-job (or similar) to create
+-- the tables for the next couple of days
+CREATE OR REPLACE FUNCTION values_update_childs(
+        integer
+    ) RETURNS SETOF text
+    LANGUAGE plpgsql
+    AS $_$
+DECLARE
+    days alias for $1;
+    cur_day date;
+    next_day date;
+    i integer;
+BEGIN
+    IF days < 1 THEN
+        RAISE EXCEPTION 'Cannot have negative number of days';
+    END IF;
+
+    i := 0;
+    LOOP
+        EXIT WHEN i > days;
+
+        SELECT CAST ('now'::date + i * '1day'::interval AS date) INTO cur_day;
+        SELECT CAST ('now'::date + (i + 1) * '1day'::interval AS date) INTO next_day;
+
+        i := i + 1;
+
+        BEGIN
+            EXECUTE 'CREATE TABLE "values$' || cur_day || '" (
+                CHECK (tstamp >= TIMESTAMP ''' || cur_day || ''' '
+                    || 'AND tstamp < TIMESTAMP ''' || next_day || ''')
+            ) INHERITS (values)';
+        EXCEPTION WHEN duplicate_table THEN
+            CONTINUE;
+        END;
+
+        RETURN NEXT 'values$' || cur_day::text;
+
+        EXECUTE 'ALTER TABLE ONLY "values$' || cur_day || '"
+            ADD CONSTRAINT "values_' || cur_day || '_pkey"
+                PRIMARY KEY (id, tstamp, name, value)';
+        EXECUTE 'ALTER TABLE ONLY "values$' || cur_day || '"
+            ADD CONSTRAINT "values_' || cur_day || '_id_fkey"
+                FOREIGN KEY (id) REFERENCES identifiers(id)';
+    END LOOP;
+    RETURN;
+END;
+$_$;
+
+-- create initial child tables
+SELECT values_update_childs(2);
+
+CREATE OR REPLACE FUNCTION values_insert_trigger()
+    RETURNS trigger
+    LANGUAGE plpgsql
+    AS $_$
+DECLARE
+    child_tbl character varying;
+BEGIN
+    SELECT 'values$' || CAST (NEW.tstamp AS DATE) INTO child_tbl;
+    -- Rather than using 'EXECUTE', some if-cascade checking the date may also
+    -- be used. However, this would require frequent updates of the trigger
+    -- function while this example works automatically.
+    EXECUTE 'INSERT INTO "' || child_tbl || '" VALUES ($1.*)' USING NEW;
+    RETURN NULL;
+END;
+$_$;
+
+CREATE TRIGGER insert_values_trigger
+    BEFORE INSERT ON values
+    FOR EACH ROW EXECUTE PROCEDURE values_insert_trigger();
+
+-- when querying values make sure to enable constraint exclusion
+-- SET constraint_exclusion = on;
+
+CREATE OR REPLACE FUNCTION collectd_insert(
+        timestamp with time zone, character varying,
+        character varying, character varying,
+        character varying, character varying,
+        character varying[], character varying[], double precision[]
+    ) RETURNS void
+    LANGUAGE plpgsql
+    AS $_$
+DECLARE
+    p_time alias for $1;
+    p_host alias for $2;
+    p_plugin alias for $3;
+    p_plugin_instance alias for $4;
+    p_type alias for $5;
+    p_type_instance alias for $6;
+    p_value_names alias for $7;
+    -- don't use the type info; for 'StoreRates true' it's 'gauge' anyway
+    -- p_type_names alias for $8;
+    p_values alias for $9;
+    ds_id integer;
+    i integer;
+BEGIN
+    SELECT id INTO ds_id
+        FROM identifiers
+        WHERE host = p_host
+            AND plugin = p_plugin
+            AND COALESCE(plugin_inst, '') = COALESCE(p_plugin_instance, '')
+            AND type = p_type
+            AND COALESCE(type_inst, '') = COALESCE(p_type_instance, '');
+    IF NOT FOUND THEN
+        INSERT INTO identifiers (host, plugin, plugin_inst, type, type_inst)
+            VALUES (p_host, p_plugin, p_plugin_instance, p_type, p_type_instance)
+            RETURNING id INTO ds_id;
+    END IF;
+    i := 1;
+    LOOP
+        EXIT WHEN i > array_upper(p_value_names, 1);
+        INSERT INTO values (id, tstamp, name, value)
+            VALUES (ds_id, p_time, p_value_names[i], p_values[i]);
+        i := i + 1;
+    END LOOP;
+END;
+$_$;
+
+-- vim: set expandtab :
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 4617f6c..d561dc3 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, 6 and 7. 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_libmodbus 1}
+%{?el6:%global _has_iproute 1}
+
+%{?el7:%global _has_libyajl 1}
+%{?el7:%global _has_recent_libpcap 1}
+%{?el7:%global _has_recent_sockios_h 1}
+%{?el7:%global _has_working_libiptc 1}
+%{?el7:%global _has_ip_vs_h 1}
+%{?el7:%global _has_recent_librrd 1}
+%{?el7:%global _has_varnish4 1}
+%{?el7:%global _has_broken_libmemcached 1}
+%{?el7:%global _has_iproute 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_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_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:0%{!?_has_broken_libmemcached:1}}
+%define with_memcached 0%{!?_without_memcached:1}
+%define with_memory 0%{!?_without_memory:1}
+%define with_multimeter 0%{!?_without_multimeter:1}
+%define with_modbus 0%{!?_without_modbus:0%{?_has_libmodbus}}
+%define with_mysql 0%{!?_without_mysql:1}
+%define with_netlink 0%{!?_without_netlink:0%{?_has_iproute}}
+%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:1}
+%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_rrdcached 0%{!?_without_rrdcached:0%{?_has_recent_librrd}}
+%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_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:0%{!?_has_varnish4: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 lpar disabled, requires AIX
+%define with_lpar 0%{!?_without_lpar: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 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:       4.9.0
+Version:       5.3.1
 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_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_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_modbus}
+%package modbus
+Summary:       modbus plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libmodbus-devel
+%description modbus
+The modbus plugin collects values from Modbus/TCP enabled devices
+%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: iproute-devel
+%description netlink
+The netlink plugin collects detailed 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))
+%if 0%{?rhel} >= 6
+BuildRequires: perl-ExtUtils-Embed
+%else
+BuildRequires: perl
+%endif
+%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_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
+
+%if %{with_amqp}
+%define _with_amqp --enable-amqp
+%else
+%define _with_amqp --disable-amqp
+%endif
+
+%if %{with_apache}
+%define _with_apache --enable-apache
+%else
+%define _with_apache --disable-apache
+%endif
+
+%if %{with_apcups}
+%define _with_apcups --enable-apcups
+%else
+%define _with_apcups --disable-apcups
+%endif
+
+%if %{with_apple_sensors}
+%define _with_apple_sensors --enable-apple_sensors
+%else
+%define _with_apple_sensors --disable-apple_sensors
+%endif
+
+%if %{with_ascent}
+%define _with_ascent --enable-ascent
+%else
+%define _with_ascent --disable-ascent
+%endif
+
+%if %{with_battery}
+%define _with_battery --enable-battery
+%else
+%define _with_battery --disable-battery
+%endif
+
+%if %{with_bind}
+%define _with_bind --enable-bind
+%else
+%define _with_bind --disable-bind
+%endif
+
+%if %{with_conntrack}
+%define _with_conntrack --enable-conntrack
+%else
+%define _with_conntrack --disable-conntrack
+%endif
+
+%if %{with_contextswitch}
+%define _with_contextswitch --enable-contextswitch
+%else
+%define _with_contextswitch --disable-contextswitch
+%endif
+
+%if %{with_cpu}
+%define _with_cpu --enable-cpu
+%else
+%define _with_cpu --disable-cpu
+%endif
+
+%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_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_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_snmp}
+%define _with_snmp --enable-snmp
+%else
+%define _with_snmp --disable-snmp
+%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_ascent} \
+       %{?_with_battery} \
+       %{?_with_bind} \
+       %{?_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_memcachec} \
+       %{?_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_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_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
-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
+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
 
-###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
 
 %clean
-rm -rf $RPM_BUILD_ROOT
+rm -rf %{buildroot}
 
 %post
 /sbin/chkconfig --add collectd
-/sbin/chkconfig collectd on
 
 %preun
-if [ "$1" = 0 ]; then
-   /sbin/chkconfig collectd off
-   /etc/init.d/collectd stop
-   /sbin/chkconfig --del collectd
+if [ $1 -eq 0 ]; then
+       /sbin/service collectd stop &>/dev/null
+       /sbin/chkconfig --del collectd
 fi
-exit 0
 
 %postun
-if [ "$1" -ge 1 ]; then
-    /etc/init.d/collectd restart
+if [ $1 -ge 1 ]; then
+       /sbin/service collectd condrestart &>/dev/null || :
 fi
-exit 0
+
+%post -n libcollectdclient -p /sbin/ldconfig
+%postun -n libcollectdclient -p /sbin/ldconfig
+
 
 %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) %{_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 df
-%plugin_macro disk
-%plugin_macro dns
-%plugin_macro entropy
-%plugin_macro email
-%plugin_macro exec
-%plugin_macro filecount
-%plugin_macro fscache
-%plugin_macro hddtemp
-%plugin_macro interface
-%plugin_macro iptables
-%plugin_macro irq
-%plugin_macro load
-%plugin_macro logfile
-%plugin_macro madwifi
-
-%plugin_macro match_empty_counter
-%plugin_macro match_hashed
-%plugin_macro match_regex
-%plugin_macro match_timediff
-%plugin_macro match_value
-
-%plugin_macro mbmon
-%plugin_macro memcached
-%plugin_macro memory
-%plugin_macro multimeter
-%plugin_macro network
-%plugin_macro nfs
-%plugin_macro ntpd
-%plugin_macro openvpn
-%plugin_macro olsrd
-%plugin_macro perl
-%plugin_macro powerdns
-%plugin_macro processes
-%plugin_macro protocols
-%plugin_macro python
-%plugin_macro rrdtool
-%plugin_macro serial
-%plugin_macro sensors
-%plugin_macro swap
-%plugin_macro syslog
-%plugin_macro table
-%plugin_macro tail
-
-%plugin_macro target_notification
-%plugin_macro target_replace
-%plugin_macro target_scale
-%plugin_macro target_set
-
-%plugin_macro tcpconns
-%plugin_macro teamspeak2
-%plugin_macro ted
-%plugin_macro thermal
-%plugin_macro unixsock
-%plugin_macro uptime
-%plugin_macro users
-%plugin_macro uuid
-%plugin_macro vmem
-%plugin_macro vserver
-%plugin_macro wireless
-%plugin_macro write_http
-
-%attr(0644,root,root) %{_datadir}/%{name}/types.db
-
-%exclude %{_libdir}/perl5/5.8.8/%{_arch}-linux-thread-multi/perllocal.pod
-%attr(0644,root,root) %{_libdir}/perl5/site_perl/5.8.8/%{_arch}-linux-thread-multi/auto/Collectd/.packlist
-%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd.pm
-%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd/Unixsock.pm
-%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd/Plugins/OpenVZ.pm
-%attr(0644,root,root) /usr/share/man/man3/Collectd::Unixsock.3pm.gz
-
-%exclude /usr/share/collectd/postgresql_default.conf
-
-%dir /var/lib/collectd
-
-%if %with_java
-%files java
-%attr(0644,root,root) /usr/share/%{name}/java/org/collectd/api/*.class
-%attr(0644,root,root) /usr/share/%{name}/java/org/collectd/java/*.class
-%plugin_macro java
+%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_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_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
-%config %attr(0644,root,root) /etc/collectd.d/apache.conf
-%plugin_macro apache
+%{_libdir}/%{name}/apache.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
-%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
+%{_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_memcachec}
+%files memcachec
+%{_libdir}/%{name}/memcachec.so
+%endif
+
+%if %{with_modbus}
+%files modbus
+%{_libdir}/%{name}/modbus.so
+%endif
+
+%if %{with_mysql}
 %files mysql
-%config %attr(0644,root,root) /etc/collectd.d/mysql.conf
-%plugin_macro mysql
+%{_libdir}/%{name}/mysql.so
+%endif
+
+%if %{with_netlink}
+%files netlink
+%{_libdir}/%{name}/netlink.so
+%endif
 
+%if %{with_nginx}
 %files nginx
-%config %attr(0644,root,root) /etc/collectd.d/nginx.conf
-%plugin_macro 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
-%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
+%{_libdir}/%{name}/sensors.so
+%endif
 
+%if %{with_snmp}
 %files snmp
-%attr(0644,root,root) /etc/collectd.d/snmp.conf
-%plugin_macro 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
-* Tue Jan 04 2010 Rackspace <stu.hood@rackspace.com> 4.9.0
+* 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"
+- Enabled modbus plugin on RHEL6
+- Enabled netlink plugin on RHEL6 and RHEL7
+- Allow perl plugin to build on RHEL5
+- Add support for RHEL7
+
+* 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
+
+* Fri 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 unneccesary 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 seperate
+  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
+
+* Mon Jan 03 2011 Monetate <jason.stelzer@monetate.com> 5.0.1
+- New upstream version
+- Changes to support 5.0.1
+
+* Mon Jan 04 2010 Rackspace <stu.hood@rackspace.com> 4.9.0
 - New upstream version
 - Changes to support 4.9.0
 - Added support for Java/GenericJMX plugin
@@ -299,20 +2003,20 @@ exit 0
 
 * Wed Jul 25 2007 Kjell Randa <Kjell.Randa@broadpark.no> 4.0.5
 - New major releas
-- Changes to support 4.0.5 
+- Changes to support 4.0.5
 
-* Wed Jan 11 2007 Iain Lea <iain@bricbrac.de> 3.11.0-0
+* Thu 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
 
-* Tue Jun 25 2006 Florian octo Forster <octo@verplant.org> 3.9.4-1
+* Sun Jun 25 2006 Florian octo Forster <octo@verplant.org> 3.9.4-1
 - New upstream version
 
-* Tue Jun 01 2006 Florian octo Forster <octo@verplant.org> 3.9.3-1
+* Thu Jun 01 2006 Florian octo Forster <octo@verplant.org> 3.9.3-1
 - New upstream version
 
 * Tue May 09 2006 Florian octo Forster <octo@verplant.org> 3.9.2-1
@@ -328,10 +2032,10 @@ exit 0
 - New upstream version
 - Added the `apache' package.
 
-* Thu Mar 14 2006 Florian octo Forster <octo@verplant.org> 3.8.2-1
+* Tue Mar 14 2006 Florian octo Forster <octo@verplant.org> 3.8.2-1
 - New upstream version
 
-* Thu Mar 13 2006 Florian octo Forster <octo@verplant.org> 3.8.1-1
+* Mon Mar 13 2006 Florian octo Forster <octo@verplant.org> 3.8.1-1
 - New upstream version
 
 * Thu Mar 09 2006 Florian octo Forster <octo@verplant.org> 3.8.0-1
@@ -368,7 +2072,7 @@ exit 0
 * Sat Nov 05 2005 Florian octo Forster <octo@verplant.org> 3.3.0-1
 - New upstream version
 
-* Tue Oct 26 2005 Florian octo Forster <octo@verplant.org> 3.2.0-1
+* Wed Oct 26 2005 Florian octo Forster <octo@verplant.org> 3.2.0-1
 - New upstream version
 - Added statement to remove the `*.la' files. This fixes a problem when
   `Unpackaged files terminate build' is in effect.
@@ -385,13 +2089,13 @@ exit 0
 * Fri Sep 16 2005 Florian octo Forster <octo@verplant.org> 2.1.0-1
 - New upstream version
 
-* Mon Sep 10 2005 Florian octo Forster <octo@verplant.org> 2.0.0-1
+* Sat Sep 10 2005 Florian octo Forster <octo@verplant.org> 2.0.0-1
 - New upstream version
 
 * Mon Aug 29 2005 Florian octo Forster <octo@verplant.org> 1.8.0-1
 - New upstream version
 
-* Sun Aug 25 2005 Florian octo Forster <octo@verplant.org> 1.7.0-1
+* Thu Aug 25 2005 Florian octo Forster <octo@verplant.org> 1.7.0-1
 - New upstream version
 
 * Sun Aug 21 2005 Florian octo Forster <octo@verplant.org> 1.6.0-1
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..ec55a52 100644 (file)
@@ -20,15 +20,31 @@ CONFIG=/etc/collectd.conf
 COLLECTD=/usr/sbin/collectd
 COLLECTDMONPID=/var/run/collectdmon.pid
 
-if [ -r /etc/default/$prog ]; then
-       . /etc/default/$prog
+if [ -r /etc/sysconfig/$service ]; then
+       . /etc/sysconfig/$service
 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 $prog -P $COLLECTDMONPID -c $COLLECTD -- -C "$CONFIG" $ARGS
                RETVAL=$?
                echo
                [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$service
@@ -53,8 +69,16 @@ case "$1" in
        status $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 || :
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 40b8293..0bec7ab 100644 (file)
@@ -1,7 +1,4 @@
 SUBDIRS = libcollectdclient
-if BUILD_WITH_OWN_LIBIPTC
-SUBDIRS += owniptc
-endif
 if BUILD_WITH_OWN_LIBOCONFIG
 SUBDIRS += liboconfig
 endif
@@ -21,7 +18,7 @@ AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"'
 AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"'
 
 sbin_PROGRAMS = collectd collectdmon
-bin_PROGRAMS = collectd-nagios
+bin_PROGRAMS = collectd-nagios collectdctl collectd-tg
 
 collectd_SOURCES = collectd.c collectd.h \
                   common.c common.h \
@@ -36,11 +33,12 @@ 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_threshold.c utils_threshold.h \
+                  utils_time.c utils_time.h \
                   types_list.c types_list.h
 
 collectd_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL)
@@ -93,6 +91,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
@@ -104,10 +103,63 @@ endif
 collectd_nagios_LDADD += libcollectdclient/libcollectdclient.la
 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
+endif
+if BUILD_AIX
+collectdctl_LDADD += -lm
+endif
+collectdctl_LDADD += libcollectdclient/libcollectdclient.la
+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
+collectd_tg_LDADD += libcollectdclient/libcollectdclient.la
+collectd_tg_DEPENDENCIES = libcollectdclient/libcollectdclient.la
+
+
 pkglib_LTLIBRARIES = 
 
 BUILT_SOURCES = 
-CLEANFILES = 
+CLEANFILES =
+
+if BUILD_PLUGIN_AGGREGATION
+pkglib_LTLIBRARIES += aggregation.la
+aggregation_la_SOURCES = aggregation.c \
+                         utils_vl_lookup.c utils_vl_lookup.h
+aggregation_la_LDFLAGS = -module -avoid-version
+aggregation_la_LIBADD = -lm
+collectd_LDADD += "-dlopen" aggregation.la
+collectd_DEPENDENCIES += aggregation.la
+endif
+
+if BUILD_PLUGIN_AMQP
+pkglib_LTLIBRARIES += amqp.la
+amqp_la_SOURCES = amqp.c \
+                 utils_cmd_putval.c utils_cmd_putval.h \
+                 utils_format_graphite.c utils_format_graphite.h \
+                 utils_format_json.c utils_format_json.h
+amqp_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBRABBITMQ_LDFLAGS)
+amqp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRABBITMQ_CPPFLAGS)
+amqp_la_LIBADD = $(BUILD_WITH_LIBRABBITMQ_LIBS)
+collectd_LDADD += "-dlopen" amqp.la
+collectd_DEPENDENCIES += amqp.la
+endif
 
 if BUILD_PLUGIN_APACHE
 pkglib_LTLIBRARIES += apache.la
@@ -139,7 +191,7 @@ 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
@@ -161,7 +213,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
@@ -190,6 +242,10 @@ if BUILD_PLUGIN_CONTEXTSWITCH
 pkglib_LTLIBRARIES += contextswitch.la
 contextswitch_la_SOURCES = contextswitch.c
 contextswitch_la_LDFLAGS = -module -avoid-version
+contextswitch_la_LIBADD =
+if BUILD_WITH_PERFSTAT
+contextswitch_la_LIBADD += -lperfstat
+endif
 collectd_LDADD += "-dlopen" contextswitch.la
 collectd_DEPENDENCIES += contextswitch.la
 endif
@@ -305,7 +361,7 @@ 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)  
@@ -355,6 +411,14 @@ collectd_LDADD += "-dlopen" exec.la
 collectd_DEPENDENCIES += exec.la
 endif
 
+if BUILD_PLUGIN_ETHSTAT
+pkglib_LTLIBRARIES += ethstat.la
+ethstat_la_SOURCES = ethstat.c
+ethstat_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" ethstat.la
+collectd_DEPENDENCIES += ethstat.la
+endif
+
 if BUILD_PLUGIN_FILECOUNT
 pkglib_LTLIBRARIES += filecount.la
 filecount_la_SOURCES = filecount.c
@@ -413,13 +477,8 @@ if BUILD_PLUGIN_IPTABLES
 pkglib_LTLIBRARIES += iptables.la
 iptables_la_SOURCES = iptables.c
 iptables_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBIPTC_CPPFLAGS)
-iptables_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBIPTC_LDFLAGS)
-if BUILD_WITH_OWN_LIBIPTC
-iptables_la_LIBADD  = owniptc/libiptc.la
-iptables_la_DEPENDENCIES = owniptc/libiptc.la
-else
-iptables_la_LIBADD = -liptc
-endif
+iptables_la_LDFLAGS = -module -avoid-version
+iptables_la_LIBADD = $(BUILD_WITH_LIBIPTC_LDFLAGS)
 collectd_LDADD += "-dlopen" iptables.la
 collectd_DEPENDENCIES += iptables.la
 endif
@@ -500,6 +559,15 @@ collectd_LDADD += "-dlopen" logfile.la
 collectd_DEPENDENCIES += logfile.la
 endif
 
+if BUILD_PLUGIN_LPAR
+pkglib_LTLIBRARIES += lpar.la
+lpar_la_SOURCES = lpar.c
+lpar_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" lpar.la
+collectd_DEPENDENCIES += lpar.la
+lpar_la_LIBADD = -lperfstat
+endif
+
 if BUILD_PLUGIN_MADWIFI
 pkglib_LTLIBRARIES += madwifi.la
 madwifi_la_SOURCES = madwifi.c madwifi.h
@@ -560,6 +628,14 @@ collectd_LDADD += "-dlopen" mbmon.la
 collectd_DEPENDENCIES += mbmon.la
 endif
 
+if BUILD_PLUGIN_MD
+pkglib_LTLIBRARIES += md.la
+md_la_SOURCES = md.c
+md_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" md.la
+collectd_DEPENDENCIES += md.la
+endif
+
 if BUILD_PLUGIN_MEMCACHEC
 pkglib_LTLIBRARIES += memcachec.la
 memcachec_la_SOURCES = memcachec.c
@@ -737,6 +813,14 @@ collectd_LDADD += "-dlopen" ntpd.la
 collectd_DEPENDENCIES += ntpd.la
 endif
 
+if BUILD_PLUGIN_NUMA
+pkglib_LTLIBRARIES += numa.la
+numa_la_SOURCES = numa.c
+numa_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" numa.la
+collectd_DEPENDENCIES += numa.la
+endif
+
 if BUILD_PLUGIN_NUT
 pkglib_LTLIBRARIES += nut.la
 nut_la_SOURCES = nut.c
@@ -811,11 +895,18 @@ collectd_LDADD += "-dlopen" perl.la
 collectd_DEPENDENCIES += perl.la
 endif
 
+if BUILD_PLUGIN_PF
+pkglib_LTLIBRARIES += pf.la
+pf_la_SOURCES = pf.c
+pf_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" pf.la
+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
@@ -886,6 +977,16 @@ collectd_LDADD += "-dlopen" protocols.la
 collectd_DEPENDENCIES += protocols.la
 endif
 
+if BUILD_PLUGIN_REDIS
+pkglib_LTLIBRARIES += redis.la
+redis_la_SOURCES = redis.c
+redis_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBCREDIS_LDFLAGS)
+redis_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCREDIS_CPPFLAGS)
+redis_la_LIBADD = -lcredis
+collectd_LDADD += "-dlopen" redis.la
+collectd_DEPENDENCIES += redis.la
+endif
+
 if BUILD_PLUGIN_ROUTEROS
 pkglib_LTLIBRARIES += routeros.la
 routeros_la_SOURCES = routeros.c
@@ -1002,6 +1103,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
@@ -1043,6 +1152,14 @@ collectd_LDADD += "-dlopen" target_set.la
 collectd_DEPENDENCIES += target_set.la
 endif
 
+if BUILD_PLUGIN_TARGET_V5UPGRADE
+pkglib_LTLIBRARIES += target_v5upgrade.la
+target_v5upgrade_la_SOURCES = target_v5upgrade.c
+target_v5upgrade_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" target_v5upgrade.la
+collectd_DEPENDENCIES += target_v5upgrade.la
+endif
+
 if BUILD_PLUGIN_TCPCONNS
 pkglib_LTLIBRARIES += tcpconns.la
 tcpconns_la_SOURCES = tcpconns.c
@@ -1079,6 +1196,14 @@ collectd_LDADD += "-dlopen" thermal.la
 collectd_DEPENDENCIES += thermal.la
 endif
 
+if BUILD_PLUGIN_THRESHOLD
+pkglib_LTLIBRARIES += threshold.la
+threshold_la_SOURCES = threshold.c
+threshold_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" threshold.la
+collectd_DEPENDENCIES += threshold.la
+endif
+
 if BUILD_PLUGIN_TOKYOTYRANT
 pkglib_LTLIBRARIES += tokyotyrant.la
 tokyotyrant_la_SOURCES = tokyotyrant.c
@@ -1097,7 +1222,6 @@ 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
@@ -1116,6 +1240,9 @@ uptime_la_LIBADD =
 if BUILD_WITH_LIBKSTAT
 uptime_la_LIBADD += -lkstat
 endif
+if BUILD_WITH_PERFSTAT
+uptime_la_LIBADD += -lperfstat
+endif
 collectd_LDADD += "-dlopen" uptime.la
 collectd_DEPENDENCIES += uptime.la
 endif
@@ -1144,6 +1271,16 @@ collectd_LDADD += "-dlopen" uuid.la
 collectd_DEPENDENCIES += uuid.la
 endif
 
+if BUILD_PLUGIN_VARNISH
+pkglib_LTLIBRARIES += varnish.la
+varnish_la_SOURCES = varnish.c
+varnish_la_LDFLAGS = -module -avoid-version
+varnish_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBVARNISH_CFLAGS)
+varnish_la_LIBADD = $(BUILD_WITH_LIBVARNISH_LIBS)
+collectd_LDADD += "-dlopen" varnish.la
+collectd_DEPENDENCIES += varnish.la
+endif
+
 if BUILD_PLUGIN_VMEM
 pkglib_LTLIBRARIES += vmem.la
 vmem_la_SOURCES = vmem.c
@@ -1168,6 +1305,16 @@ collectd_LDADD += "-dlopen" wireless.la
 collectd_DEPENDENCIES += wireless.la
 endif
 
+if BUILD_PLUGIN_WRITE_GRAPHITE
+pkglib_LTLIBRARIES += write_graphite.la
+write_graphite_la_SOURCES = write_graphite.c \
+                        utils_format_graphite.c utils_format_graphite.h \
+                        utils_format_json.c utils_format_json.h
+write_graphite_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" write_graphite.la
+collectd_DEPENDENCIES += write_graphite.la
+endif
+
 if BUILD_PLUGIN_WRITE_HTTP
 pkglib_LTLIBRARIES += write_http.la
 write_http_la_SOURCES = write_http.c \
@@ -1183,6 +1330,36 @@ endif
 collectd_DEPENDENCIES += write_http.la
 endif
 
+if BUILD_PLUGIN_WRITE_MONGODB
+pkglib_LTLIBRARIES += write_mongodb.la
+write_mongodb_la_SOURCES = write_mongodb.c
+write_mongodb_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMONGOC_CPPFLAGS)
+write_mongodb_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBMONGOC_LDFLAGS)
+write_mongodb_la_LIBADD = -lmongoc
+collectd_LDADD += "-dlopen" write_mongodb.la
+collectd_DEPENDENCIES += write_mongodb.la
+endif
+
+if BUILD_PLUGIN_WRITE_REDIS
+pkglib_LTLIBRARIES += write_redis.la
+write_redis_la_SOURCES = write_redis.c
+write_redis_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBCREDIS_LDFLAGS)
+write_redis_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCREDIS_CPPFLAGS)
+write_redis_la_LIBADD = -lcredis
+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
+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
@@ -1209,22 +1386,26 @@ dist_man_MANS = collectd.1 \
                collectd.conf.5 \
                collectd-email.5 \
                collectd-exec.5 \
+               collectdctl.1 \
                collectd-java.5 \
                collectdmon.1 \
                collectd-nagios.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 \
                collectd-exec.pod \
+               collectdctl.pod \
                collectd-java.pod \
                collectdmon.pod \
                collectd-nagios.pod \
@@ -1232,6 +1413,8 @@ 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 \
                types.db.pod
@@ -1252,8 +1435,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 $(builddir) 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)
@@ -1272,3 +1473,16 @@ uninstall-hook:
        rm -f $(DESTDIR)$(pkgdatadir)/types.db;
        rm -f $(DESTDIR)$(sysconfdir)/collectd.conf
        rm -f $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
+
+if BUILD_FEATURE_DEBUG
+bin_PROGRAMS += utils_vl_lookup_test
+utils_vl_lookup_test_SOURCES = utils_vl_lookup_test.c \
+                               utils_vl_lookup.h utils_vl_lookup.c \
+                               utils_avltree.c utils_avltree.h \
+                               common.h
+
+utils_vl_lookup_test_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL) -DBUILD_TEST=1
+utils_vl_lookup_test_CFLAGS = $(AM_CFLAGS)
+utils_vl_lookup_test_LDFLAGS = -export-dynamic
+utils_vl_lookup_test_LDADD =
+endif
diff --git a/src/aggregation.c b/src/aggregation.c
new file mode 100644 (file)
index 0000000..8175c66
--- /dev/null
@@ -0,0 +1,790 @@
+/**
+ * collectd - src/aggregation.c
+ * Copyright (C) 2012       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include <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"
+
+#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;
+  _Bool calc_average;
+  _Bool calc_min;
+  _Bool calc_max;
+  _Bool calc_stddev;
+}; /* }}} */
+typedef struct aggregation_s aggregation_t;
+
+struct agg_instance_s;
+typedef struct agg_instance_s agg_instance_t;
+struct agg_instance_s /* {{{ */
+{
+  pthread_mutex_t lock;
+  identifier_t ident;
+
+  int ds_type;
+
+  derive_t num;
+  gauge_t sum;
+  gauge_t squares_sum;
+
+  gauge_t min;
+  gauge_t max;
+
+  rate_to_value_state_t *state_num;
+  rate_to_value_state_t *state_sum;
+  rate_to_value_state_t *state_average;
+  rate_to_value_state_t *state_min;
+  rate_to_value_state_t *state_max;
+  rate_to_value_state_t *state_stddev;
+
+  agg_instance_t *next;
+}; /* }}} */
+
+static lookup_t *lookup = NULL;
+
+static pthread_mutex_t agg_instance_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static agg_instance_t *agg_instance_list_head = NULL;
+
+static _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);
+} /* }}} void agg_destroy */
+
+/* Frees all dynamically allocated memory within the instance. */
+static void agg_instance_destroy (agg_instance_t *inst) /* {{{ */
+{
+  if (inst == NULL)
+    return;
+
+  /* Remove this instance from the global list of instances. */
+  pthread_mutex_lock (&agg_instance_list_lock);
+  if (agg_instance_list_head == inst)
+    agg_instance_list_head = inst->next;
+  else if (agg_instance_list_head != NULL)
+  {
+    agg_instance_t *prev = agg_instance_list_head;
+    while ((prev != NULL) && (prev->next != inst))
+      prev = prev->next;
+    if (prev != NULL)
+      prev->next = inst->next;
+  }
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  sfree (inst->state_num);
+  sfree (inst->state_sum);
+  sfree (inst->state_average);
+  sfree (inst->state_min);
+  sfree (inst->state_max);
+  sfree (inst->state_stddev);
+
+  memset (inst, 0, sizeof (*inst));
+  inst->ds_type = -1;
+  inst->min = NAN;
+  inst->max = NAN;
+} /* }}} void agg_instance_destroy */
+
+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)
+{
+  agg_instance_t *inst;
+
+  DEBUG ("aggregation plugin: Creating new instance.");
+
+  inst = malloc (sizeof (*inst));
+  if (inst == NULL)
+  {
+    ERROR ("aggregation plugin: malloc() failed.");
+    return (NULL);
+  }
+  memset (inst, 0, sizeof (*inst));
+  pthread_mutex_init (&inst->lock, /* attr = */ NULL);
+
+  inst->ds_type = ds->ds[0].type;
+
+  agg_instance_create_name (inst, vl, agg);
+
+  inst->min = NAN;
+  inst->max = NAN;
+
+#define INIT_STATE(field) do { \
+  inst->state_ ## field = NULL; \
+  if (agg->calc_ ## field) { \
+    inst->state_ ## field = malloc (sizeof (*inst->state_ ## field)); \
+    if (inst->state_ ## field == NULL) { \
+      agg_instance_destroy (inst); \
+      ERROR ("aggregation plugin: malloc() failed."); \
+      return (NULL); \
+    } \
+    memset (inst->state_ ## field, 0, sizeof (*inst->state_ ## field)); \
+  } \
+} while (0)
+
+  INIT_STATE (num);
+  INIT_STATE (sum);
+  INIT_STATE (average);
+  INIT_STATE (min);
+  INIT_STATE (max);
+  INIT_STATE (stddev);
+
+#undef INIT_STATE
+
+  pthread_mutex_lock (&agg_instance_list_lock);
+  inst->next = agg_instance_list_head;
+  agg_instance_list_head = inst;
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  return (inst);
+} /* }}} agg_instance_t *agg_instance_create */
+
+/* Update the num, sum, min, max, ... fields of the aggregation instance, if
+ * the rate of the value list is available. Value lists with more than one data
+ * source are not supported and will return an error. Returns zero on success
+ * and non-zero otherwise. */
+static int agg_instance_update (agg_instance_t *inst, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl)
+{
+  gauge_t *rate;
+
+  if (ds->ds_num != 1)
+  {
+    ERROR ("aggregation plugin: The \"%s\" type (data set) has more than one "
+        "data source. This is currently not supported by this plugin. "
+        "Sorry.", ds->type);
+    return (EINVAL);
+  }
+
+  rate = uc_get_rate (ds, vl);
+  if (rate == NULL)
+  {
+    char ident[6 * DATA_MAX_NAME_LEN];
+    FORMAT_VL (ident, sizeof (ident), vl);
+    ERROR ("aggregation plugin: Unable to read the current rate of \"%s\".",
+        ident);
+    return (ENOENT);
+  }
+
+  if (isnan (rate[0]))
+  {
+    sfree (rate);
+    return (0);
+  }
+
+  pthread_mutex_lock (&inst->lock);
+
+  inst->num++;
+  inst->sum += rate[0];
+  inst->squares_sum += (rate[0] * rate[0]);
+
+  if (isnan (inst->min) || (inst->min > rate[0]))
+    inst->min = rate[0];
+  if (isnan (inst->max) || (inst->max < rate[0]))
+    inst->max = rate[0];
+
+  pthread_mutex_unlock (&inst->lock);
+
+  sfree (rate);
+  return (0);
+} /* }}} int agg_instance_update */
+
+static int agg_instance_read_func (agg_instance_t *inst, /* {{{ */
+  char const *func, gauge_t rate, rate_to_value_state_t *state,
+  value_list_t *vl, char const *pi_prefix, cdtime_t t)
+{
+  value_t v;
+  int status;
+
+  if (pi_prefix[0] != 0)
+    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));
+
+  memset (&v, 0, sizeof (v));
+  status = rate_to_value (&v, rate, state, inst->ds_type, t);
+  if (status != 0)
+  {
+    /* If this is the first iteration and rate_to_value() was asked to return a
+     * COUNTER or a DERIVE, it will return EAGAIN. Catch this and handle
+     * gracefully. */
+    if (status == EAGAIN)
+      return (0);
+
+    WARNING ("aggregation plugin: rate_to_value failed with status %i.",
+        status);
+    return (-1);
+  }
+
+  vl->values = &v;
+  vl->values_len = 1;
+
+  plugin_dispatch_values (vl);
+
+  vl->values = NULL;
+  vl->values_len = 0;
+
+  return (0);
+} /* }}} int agg_instance_read_func */
+
+static int agg_instance_read (agg_instance_t *inst, cdtime_t t) /* {{{ */
+{
+  value_list_t vl = VALUE_LIST_INIT;
+
+  /* Pre-set all the fields in the value list that will not change per
+   * aggregation type (sum, average, ...). The struct will be re-used and must
+   * therefore be dispatched using the "secure" function. */
+
+  vl.time = t;
+  vl.interval = 0;
+
+  vl.meta = meta_data_create ();
+  if (vl.meta == NULL)
+  {
+    ERROR ("aggregation plugin: meta_data_create failed.");
+    return (-1);
+  }
+  meta_data_add_boolean (vl.meta, "aggregation:created", 1);
+
+  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));
+  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, inst->ident.plugin_instance, t); \
+  } \
+} while (0)
+
+  pthread_mutex_lock (&inst->lock);
+
+  READ_FUNC (num, (gauge_t) inst->num);
+
+  /* All other aggregations are only defined when there have been any values
+   * at all. */
+  if (inst->num > 0)
+  {
+    READ_FUNC (sum, inst->sum);
+    READ_FUNC (average, (inst->sum / ((gauge_t) inst->num)));
+    READ_FUNC (min, inst->min);
+    READ_FUNC (max, inst->max);
+    READ_FUNC (stddev, sqrt((((gauge_t) inst->num) * inst->squares_sum)
+          - (inst->sum * inst->sum)) / ((gauge_t) inst->num));
+  }
+
+  /* Reset internal state. */
+  inst->num = 0;
+  inst->sum = 0.0;
+  inst->squares_sum = 0.0;
+  inst->min = NAN;
+  inst->max = NAN;
+
+  pthread_mutex_unlock (&inst->lock);
+
+  meta_data_destroy (vl.meta);
+  vl.meta = NULL;
+
+  return (0);
+} /* }}} int agg_instance_read */
+
+/* lookup_class_callback_t for utils_vl_lookup */
+static void *agg_lookup_class_callback ( /* {{{ */
+    data_set_t const *ds, value_list_t const *vl, void *user_class)
+{
+  return (agg_instance_create (ds, vl, (aggregation_t *) user_class));
+} /* }}} void *agg_class_callback */
+
+/* lookup_obj_callback_t for utils_vl_lookup */
+static int agg_lookup_obj_callback (data_set_t const *ds, /* {{{ */
+    value_list_t const *vl,
+    __attribute__((unused)) void *user_class,
+    void *user_obj)
+{
+  return (agg_instance_update ((agg_instance_t *) user_obj, ds, vl));
+} /* }}} int agg_lookup_obj_callback */
+
+/* lookup_free_class_callback_t for utils_vl_lookup */
+static void agg_lookup_free_class_callback (void *user_class) /* {{{ */
+{
+  agg_destroy ((aggregation_t *) user_class);
+} /* }}} void agg_lookup_free_class_callback */
+
+/* lookup_free_obj_callback_t for utils_vl_lookup */
+static void agg_lookup_free_obj_callback (void *user_obj) /* {{{ */
+{
+  agg_instance_destroy ((agg_instance_t *) user_obj);
+} /* }}} void agg_lookup_free_obj_callback */
+
+/*
+ * <Plugin "aggregation">
+ *   <Aggregation>
+ *     Plugin "cpu"
+ *     Type "cpu"
+ *
+ *     GroupBy Host
+ *     GroupBy TypeInstance
+ *
+ *     CalculateNum true
+ *     CalculateSum true
+ *     CalculateAverage true
+ *     CalculateMinimum true
+ *     CalculateMaximum true
+ *     CalculateStddev true
+ *   </Aggregation>
+ * </Plugin>
+ */
+static int agg_config_handle_group_by (oconfig_item_t const *ci, /* {{{ */
+    aggregation_t *agg)
+{
+  int i;
+
+  for (i = 0; i < ci->values_num; i++)
+  {
+    char const *value;
+
+    if (ci->values[i].type != OCONFIG_TYPE_STRING)
+    {
+      ERROR ("aggregation plugin: Argument %i of the \"GroupBy\" option "
+          "is not a string.", i + 1);
+      continue;
+    }
+
+    value = ci->values[i].value.string;
+
+    if (strcasecmp ("Host", value) == 0)
+      agg->group_by |= LU_GROUP_BY_HOST;
+    else if (strcasecmp ("Plugin", value) == 0)
+      agg->group_by |= LU_GROUP_BY_PLUGIN;
+    else if (strcasecmp ("PluginInstance", value) == 0)
+      agg->group_by |= LU_GROUP_BY_PLUGIN_INSTANCE;
+    else if (strcasecmp ("TypeInstance", value) == 0)
+      agg->group_by |= LU_GROUP_BY_TYPE_INSTANCE;
+    else if (strcasecmp ("Type", value) == 0)
+      ERROR ("aggregation plugin: Grouping by type is not supported.");
+    else
+      WARNING ("aggregation plugin: The \"%s\" argument to the \"GroupBy\" "
+          "option is invalid and will be ignored.", value);
+  } /* for (ci->values) */
+
+  return (0);
+} /* }}} int agg_config_handle_group_by */
+
+static int agg_config_aggregation (oconfig_item_t *ci) /* {{{ */
+{
+  aggregation_t *agg;
+  _Bool is_valid;
+  int status;
+  int i;
+
+  agg = malloc (sizeof (*agg));
+  if (agg == NULL)
+  {
+    ERROR ("aggregation plugin: malloc failed.");
+    return (-1);
+  }
+  memset (agg, 0, sizeof (*agg));
+
+  sstrncpy (agg->ident.host, "/.*/", 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, "/.*/", sizeof (agg->ident.type));
+  sstrncpy (agg->ident.type_instance, "/.*/",
+      sizeof (agg->ident.type_instance));
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Host", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.host,
+          sizeof (agg->ident.host));
+    else if (strcasecmp ("Plugin", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.plugin,
+          sizeof (agg->ident.plugin));
+    else if (strcasecmp ("PluginInstance", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.plugin_instance,
+          sizeof (agg->ident.plugin_instance));
+    else if (strcasecmp ("Type", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.type,
+          sizeof (agg->ident.type));
+    else if (strcasecmp ("TypeInstance", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.type_instance,
+          sizeof (agg->ident.type_instance));
+    else if (strcasecmp ("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)
+      cf_util_get_boolean (child, &agg->calc_num);
+    else if (strcasecmp ("CalculateSum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_sum);
+    else if (strcasecmp ("CalculateAverage", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_average);
+    else if (strcasecmp ("CalculateMinimum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_min);
+    else if (strcasecmp ("CalculateMaximum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_max);
+    else if (strcasecmp ("CalculateStddev", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_stddev);
+    else
+      WARNING ("aggregation plugin: The \"%s\" key is not allowed inside "
+          "<Aggregation /> blocks and will be ignored.", child->key);
+  }
+
+  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 (strcmp ("/.*/", agg->ident.type) == 0) /* {{{ */
+  {
+    ERROR ("aggregation plugin: It appears you did not specify the required "
+        "\"Type\" option in this aggregation. "
+        "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+        "Type \"%s\", TypeInstance \"%s\")",
+        agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+        agg->ident.type, agg->ident.type_instance);
+    is_valid = 0;
+  }
+  else if (strchr (agg->ident.type, '/') != NULL)
+  {
+    ERROR ("aggregation plugin: The \"Type\" may not contain the '/' "
+        "character. Especially, it may not be a regex. The current "
+        "value is \"%s\".", agg->ident.type);
+    is_valid = 0;
+  } /* }}} */
+
+  /* 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 "
+        "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,
+        agg->ident.type, agg->ident.type_instance);
+    is_valid = 0;
+  } /* }}} */
+
+  if (!agg->calc_num && !agg->calc_sum && !agg->calc_average /* {{{ */
+      && !agg->calc_min && !agg->calc_max && !agg->calc_stddev)
+  {
+    ERROR ("aggregation plugin: No aggregation function has been specified. "
+        "Without this, I don't know what I should be calculating. "
+        "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+        "Type \"%s\", TypeInstance \"%s\")",
+        agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+        agg->ident.type, agg->ident.type_instance);
+    is_valid = 0;
+  } /* }}} */
+
+  if (!is_valid) /* {{{ */
+  {
+    sfree (agg);
+    return (-1);
+  } /* }}} */
+
+  status = lookup_add (lookup, &agg->ident, agg->group_by, agg);
+  if (status != 0)
+  {
+    ERROR ("aggregation plugin: lookup_add failed with status %i.", status);
+    sfree (agg);
+    return (-1);
+  }
+
+  DEBUG ("aggregation plugin: Successfully added aggregation: "
+      "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+      "Type \"%s\", TypeInstance \"%s\")",
+      agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+      agg->ident.type, agg->ident.type_instance);
+  return (0);
+} /* }}} int agg_config_aggregation */
+
+static int agg_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  pthread_mutex_lock (&agg_instance_list_lock);
+
+  if (lookup == NULL)
+  {
+    lookup = lookup_create (agg_lookup_class_callback,
+        agg_lookup_obj_callback,
+        agg_lookup_free_class_callback,
+        agg_lookup_free_obj_callback);
+    if (lookup == NULL)
+    {
+      pthread_mutex_unlock (&agg_instance_list_lock);
+      ERROR ("aggregation plugin: lookup_create failed.");
+      return (-1);
+    }
+  }
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Aggregation", child->key) == 0)
+      agg_config_aggregation (child);
+    else
+      WARNING ("aggregation plugin: The \"%s\" key is not allowed inside "
+          "<Plugin aggregation /> blocks and will be ignored.", child->key);
+  }
+
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  return (0);
+} /* }}} int agg_config */
+
+static int agg_read (void) /* {{{ */
+{
+  agg_instance_t *this;
+  cdtime_t t;
+  int success;
+
+  t = cdtime ();
+  success = 0;
+
+  pthread_mutex_lock (&agg_instance_list_lock);
+
+  /* agg_instance_list_head only holds data, after the "write" callback has
+   * been called with a matching value list at least once. So on startup,
+   * there's a race between the aggregations read() and write() callback. If
+   * the read() callback is called first, agg_instance_list_head is NULL and
+   * "success" may be zero. This is expected and should not result in an error.
+   * Therefore we need to handle this case separately. */
+  if (agg_instance_list_head == NULL)
+  {
+    pthread_mutex_unlock (&agg_instance_list_lock);
+    return (0);
+  }
+
+  for (this = agg_instance_list_head; this != NULL; this = this->next)
+  {
+    int status;
+
+    status = agg_instance_read (this, t);
+    if (status != 0)
+      WARNING ("aggregation plugin: Reading an aggregation instance "
+          "failed with status %i.", status);
+    else
+      success++;
+  }
+
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  return ((success > 0) ? 0 : -1);
+} /* }}} int agg_read */
+
+static int agg_write (data_set_t const *ds, value_list_t const *vl, /* {{{ */
+    __attribute__((unused)) user_data_t *user_data)
+{
+  _Bool created_by_aggregation = 0;
+  int status;
+
+  /* Ignore values that were created by the aggregation plugin to avoid weird
+   * effects. */
+  (void) meta_data_get_boolean (vl->meta, "aggregation:created",
+      &created_by_aggregation);
+  if (created_by_aggregation)
+    return (0);
+
+  if (lookup == NULL)
+    status = ENOENT;
+  else
+  {
+    status = lookup_search (lookup, ds, vl);
+    if (status > 0)
+      status = 0;
+  }
+
+  return (status);
+} /* }}} int agg_write */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("aggregation", agg_config);
+  plugin_register_read ("aggregation", agg_read);
+  plugin_register_write ("aggregation", agg_write, /* user_data = */ NULL);
+}
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
diff --git a/src/amqp.c b/src/amqp.c
new file mode 100644 (file)
index 0000000..861cb5c
--- /dev/null
@@ -0,0 +1,1090 @@
+/**
+ * collectd - src/amqp.c
+ * Copyright (C) 2009       Sebastien Pahl
+ * Copyright (C) 2010-2012  Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * 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:
+ *   Sebastien Pahl <sebastien.pahl at dotcloud.com>
+ *   Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_cmd_putval.h"
+#include "utils_format_json.h"
+#include "utils_format_graphite.h"
+
+#include <pthread.h>
+
+#include <amqp.h>
+#include <amqp_framing.h>
+
+#ifdef HAVE_AMQP_TCP_SOCKET_H
+# include <amqp_tcp_socket.h>
+#endif
+#ifdef HAVE_AMQP_SOCKET_H
+# include <amqp_socket.h>
+#endif
+#ifdef HAVE_AMQP_TCP_SOCKET
+#if defined HAVE_DECL_AMQP_SOCKET_CLOSE && !HAVE_DECL_AMQP_SOCKET_CLOSE
+/* rabbitmq-c does not currently ship amqp_socket.h
+ * and, thus, does not define this function. */
+int amqp_socket_close(amqp_socket_t *);
+#endif
+#endif
+
+/* Defines for the delivery mode. I have no idea why they're not defined by the
+ * library.. */
+#define CAMQP_DM_VOLATILE   1
+#define CAMQP_DM_PERSISTENT 2
+
+#define CAMQP_FORMAT_COMMAND    1
+#define CAMQP_FORMAT_JSON       2
+#define CAMQP_FORMAT_GRAPHITE   3
+
+#define CAMQP_CHANNEL 1
+
+/*
+ * Data types
+ */
+struct camqp_config_s
+{
+    _Bool   publish;
+    char   *name;
+
+    char   *host;
+    int     port;
+    char   *vhost;
+    char   *user;
+    char   *password;
+
+    char   *exchange;
+    char   *routing_key;
+
+    /* publish only */
+    uint8_t delivery_mode;
+    _Bool   store_rates;
+    int     format;
+    /* publish & graphite format only */
+    char    *prefix;
+    char    *postfix;
+    char    escape_char;
+    unsigned int graphite_flags;
+
+    /* subscribe only */
+    char   *exchange_type;
+    char   *queue;
+
+    amqp_connection_state_t connection;
+    pthread_mutex_t lock;
+};
+typedef struct camqp_config_s camqp_config_t;
+
+/*
+ * Global variables
+ */
+static const char *def_host       = "localhost";
+static const char *def_vhost      = "/";
+static const char *def_user       = "guest";
+static const char *def_password   = "guest";
+static const char *def_exchange   = "amq.fanout";
+
+static pthread_t *subscriber_threads     = NULL;
+static size_t     subscriber_threads_num = 0;
+static _Bool      subscriber_threads_running = 1;
+
+#define CONF(c,f) (((c)->f != NULL) ? (c)->f : def_##f)
+
+/*
+ * Functions
+ */
+static void camqp_close_connection (camqp_config_t *conf) /* {{{ */
+{
+    int sockfd;
+
+    if ((conf == NULL) || (conf->connection == NULL))
+        return;
+
+    sockfd = amqp_get_sockfd (conf->connection);
+    amqp_channel_close (conf->connection, CAMQP_CHANNEL, AMQP_REPLY_SUCCESS);
+    amqp_connection_close (conf->connection, AMQP_REPLY_SUCCESS);
+    amqp_destroy_connection (conf->connection);
+    close (sockfd);
+    conf->connection = NULL;
+} /* }}} void camqp_close_connection */
+
+static void camqp_config_free (void *ptr) /* {{{ */
+{
+    camqp_config_t *conf = ptr;
+
+    if (conf == NULL)
+        return;
+
+    camqp_close_connection (conf);
+
+    sfree (conf->name);
+    sfree (conf->host);
+    sfree (conf->vhost);
+    sfree (conf->user);
+    sfree (conf->password);
+    sfree (conf->exchange);
+    sfree (conf->exchange_type);
+    sfree (conf->queue);
+    sfree (conf->routing_key);
+    sfree (conf->prefix);
+    sfree (conf->postfix);
+
+
+    sfree (conf);
+} /* }}} void camqp_config_free */
+
+static char *camqp_bytes_cstring (amqp_bytes_t *in) /* {{{ */
+{
+    char *ret;
+
+    if ((in == NULL) || (in->bytes == NULL))
+        return (NULL);
+
+    ret = malloc (in->len + 1);
+    if (ret == NULL)
+        return (NULL);
+
+    memcpy (ret, in->bytes, in->len);
+    ret[in->len] = 0;
+
+    return (ret);
+} /* }}} char *camqp_bytes_cstring */
+
+static _Bool camqp_is_error (camqp_config_t *conf) /* {{{ */
+{
+    amqp_rpc_reply_t r;
+
+    r = amqp_get_rpc_reply (conf->connection);
+    if (r.reply_type == AMQP_RESPONSE_NORMAL)
+        return (0);
+
+    return (1);
+} /* }}} _Bool camqp_is_error */
+
+static char *camqp_strerror (camqp_config_t *conf, /* {{{ */
+        char *buffer, size_t buffer_size)
+{
+    amqp_rpc_reply_t r;
+
+    r = amqp_get_rpc_reply (conf->connection);
+    switch (r.reply_type)
+    {
+        case AMQP_RESPONSE_NORMAL:
+            sstrncpy (buffer, "Success", sizeof (buffer));
+            break;
+
+        case AMQP_RESPONSE_NONE:
+            sstrncpy (buffer, "Missing RPC reply type", sizeof (buffer));
+            break;
+
+        case AMQP_RESPONSE_LIBRARY_EXCEPTION:
+#if HAVE_AMQP_RPC_REPLY_T_LIBRARY_ERRNO
+            if (r.library_errno)
+                return (sstrerror (r.library_errno, buffer, buffer_size));
+#else
+            if (r.library_error)
+                return (sstrerror (r.library_error, buffer, buffer_size));
+#endif
+            else
+                sstrncpy (buffer, "End of stream", sizeof (buffer));
+            break;
+
+        case AMQP_RESPONSE_SERVER_EXCEPTION:
+            if (r.reply.id == AMQP_CONNECTION_CLOSE_METHOD)
+            {
+                amqp_connection_close_t *m = r.reply.decoded;
+                char *tmp = camqp_bytes_cstring (&m->reply_text);
+                ssnprintf (buffer, buffer_size, "Server connection error %d: %s",
+                        m->reply_code, tmp);
+                sfree (tmp);
+            }
+            else if (r.reply.id == AMQP_CHANNEL_CLOSE_METHOD)
+            {
+                amqp_channel_close_t *m = r.reply.decoded;
+                char *tmp = camqp_bytes_cstring (&m->reply_text);
+                ssnprintf (buffer, buffer_size, "Server channel error %d: %s",
+                        m->reply_code, tmp);
+                sfree (tmp);
+            }
+            else
+            {
+                ssnprintf (buffer, buffer_size, "Server error method %#"PRIx32,
+                        r.reply.id);
+            }
+            break;
+
+        default:
+            ssnprintf (buffer, buffer_size, "Unknown reply type %i",
+                    (int) r.reply_type);
+    }
+
+    return (buffer);
+} /* }}} char *camqp_strerror */
+
+#if HAVE_AMQP_RPC_REPLY_T_LIBRARY_ERRNO
+static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */
+{
+    amqp_exchange_declare_ok_t *ed_ret;
+
+    if (conf->exchange_type == NULL)
+        return (0);
+
+    ed_ret = amqp_exchange_declare (conf->connection,
+            /* channel     = */ CAMQP_CHANNEL,
+            /* exchange    = */ amqp_cstring_bytes (conf->exchange),
+            /* type        = */ amqp_cstring_bytes (conf->exchange_type),
+            /* passive     = */ 0,
+            /* durable     = */ 0,
+            /* auto_delete = */ 1,
+            /* arguments   = */ AMQP_EMPTY_TABLE);
+    if ((ed_ret == NULL) && camqp_is_error (conf))
+    {
+        char errbuf[1024];
+        ERROR ("amqp plugin: amqp_exchange_declare failed: %s",
+                camqp_strerror (conf, errbuf, sizeof (errbuf)));
+        camqp_close_connection (conf);
+        return (-1);
+    }
+
+    INFO ("amqp plugin: Successfully created exchange \"%s\" "
+            "with type \"%s\".",
+            conf->exchange, conf->exchange_type);
+
+    return (0);
+} /* }}} int camqp_create_exchange */
+#else
+static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */
+{
+    amqp_exchange_declare_ok_t *ed_ret;
+    amqp_table_t argument_table;
+    struct amqp_table_entry_t_ argument_table_entries[1];
+
+    if (conf->exchange_type == NULL)
+        return (0);
+
+    /* Valid arguments: "auto_delete", "internal" */
+    argument_table.num_entries = STATIC_ARRAY_SIZE (argument_table_entries);
+    argument_table.entries = argument_table_entries;
+    argument_table_entries[0].key = amqp_cstring_bytes ("auto_delete");
+    argument_table_entries[0].value.kind = AMQP_FIELD_KIND_BOOLEAN;
+    argument_table_entries[0].value.value.boolean = 1;
+
+    ed_ret = amqp_exchange_declare (conf->connection,
+            /* channel     = */ CAMQP_CHANNEL,
+            /* exchange    = */ amqp_cstring_bytes (conf->exchange),
+            /* type        = */ amqp_cstring_bytes (conf->exchange_type),
+            /* passive     = */ 0,
+            /* durable     = */ 0,
+            /* arguments   = */ argument_table);
+    if ((ed_ret == NULL) && camqp_is_error (conf))
+    {
+        char errbuf[1024];
+        ERROR ("amqp plugin: amqp_exchange_declare failed: %s",
+                camqp_strerror (conf, errbuf, sizeof (errbuf)));
+        camqp_close_connection (conf);
+        return (-1);
+    }
+
+    INFO ("amqp plugin: Successfully created exchange \"%s\" "
+            "with type \"%s\".",
+            conf->exchange, conf->exchange_type);
+
+    return (0);
+} /* }}} int camqp_create_exchange */
+#endif
+
+static int camqp_setup_queue (camqp_config_t *conf) /* {{{ */
+{
+    amqp_queue_declare_ok_t *qd_ret;
+    amqp_basic_consume_ok_t *cm_ret;
+
+    qd_ret = amqp_queue_declare (conf->connection,
+            /* channel     = */ CAMQP_CHANNEL,
+            /* queue       = */ (conf->queue != NULL)
+            ? amqp_cstring_bytes (conf->queue)
+            : AMQP_EMPTY_BYTES,
+            /* passive     = */ 0,
+            /* durable     = */ 0,
+            /* exclusive   = */ 0,
+            /* auto_delete = */ 1,
+            /* arguments   = */ AMQP_EMPTY_TABLE);
+    if (qd_ret == NULL)
+    {
+        ERROR ("amqp plugin: amqp_queue_declare failed.");
+        camqp_close_connection (conf);
+        return (-1);
+    }
+
+    if (conf->queue == NULL)
+    {
+        conf->queue = camqp_bytes_cstring (&qd_ret->queue);
+        if (conf->queue == NULL)
+        {
+            ERROR ("amqp plugin: camqp_bytes_cstring failed.");
+            camqp_close_connection (conf);
+            return (-1);
+        }
+
+        INFO ("amqp plugin: Created queue \"%s\".", conf->queue);
+    }
+    DEBUG ("amqp plugin: Successfully created queue \"%s\".", conf->queue);
+
+    /* bind to an exchange */
+    if (conf->exchange != NULL)
+    {
+        amqp_queue_bind_ok_t *qb_ret;
+
+        assert (conf->queue != NULL);
+        qb_ret = amqp_queue_bind (conf->connection,
+                /* channel     = */ CAMQP_CHANNEL,
+                /* queue       = */ amqp_cstring_bytes (conf->queue),
+                /* exchange    = */ amqp_cstring_bytes (conf->exchange),
+                /* routing_key = */ (conf->routing_key != NULL)
+                ? amqp_cstring_bytes (conf->routing_key)
+                : AMQP_EMPTY_BYTES,
+                /* arguments   = */ AMQP_EMPTY_TABLE);
+        if ((qb_ret == NULL) && camqp_is_error (conf))
+        {
+            char errbuf[1024];
+            ERROR ("amqp plugin: amqp_queue_bind failed: %s",
+                    camqp_strerror (conf, errbuf, sizeof (errbuf)));
+            camqp_close_connection (conf);
+            return (-1);
+        }
+
+        DEBUG ("amqp plugin: Successfully bound queue \"%s\" to exchange \"%s\".",
+                conf->queue, conf->exchange);
+    } /* if (conf->exchange != NULL) */
+
+    cm_ret = amqp_basic_consume (conf->connection,
+            /* channel      = */ CAMQP_CHANNEL,
+            /* queue        = */ amqp_cstring_bytes (conf->queue),
+            /* consumer_tag = */ AMQP_EMPTY_BYTES,
+            /* no_local     = */ 0,
+            /* no_ack       = */ 1,
+            /* exclusive    = */ 0,
+            /* arguments    = */ AMQP_EMPTY_TABLE
+        );
+    if ((cm_ret == NULL) && camqp_is_error (conf))
+    {
+        char errbuf[1024];
+        ERROR ("amqp plugin: amqp_basic_consume failed: %s",
+                    camqp_strerror (conf, errbuf, sizeof (errbuf)));
+        camqp_close_connection (conf);
+        return (-1);
+    }
+
+    return (0);
+} /* }}} int camqp_setup_queue */
+
+static int camqp_connect (camqp_config_t *conf) /* {{{ */
+{
+    amqp_rpc_reply_t reply;
+    int status;
+#ifdef HAVE_AMQP_TCP_SOCKET
+    amqp_socket_t *socket;
+#else
+    int sockfd;
+#endif
+
+    if (conf->connection != NULL)
+        return (0);
+
+    conf->connection = amqp_new_connection ();
+    if (conf->connection == NULL)
+    {
+        ERROR ("amqp plugin: amqp_new_connection failed.");
+        return (ENOMEM);
+    }
+
+#ifdef HAVE_AMQP_TCP_SOCKET
+# define CLOSE_SOCKET() /* amqp_destroy_connection() closes the socket for us */
+    /* TODO: add support for SSL using amqp_ssl_socket_new
+     *       and related functions */
+    socket = amqp_tcp_socket_new (conf->connection);
+    if (! socket)
+    {
+        ERROR ("amqp plugin: amqp_tcp_socket_new failed.");
+        amqp_destroy_connection (conf->connection);
+        conf->connection = NULL;
+        return (ENOMEM);
+    }
+
+    status = amqp_socket_open (socket, CONF(conf, host), conf->port);
+    if (status < 0)
+    {
+        char errbuf[1024];
+        status *= -1;
+        ERROR ("amqp plugin: amqp_socket_open failed: %s",
+                sstrerror (status, errbuf, sizeof (errbuf)));
+        amqp_destroy_connection (conf->connection);
+        conf->connection = NULL;
+        return (status);
+    }
+#else /* HAVE_AMQP_TCP_SOCKET */
+# define CLOSE_SOCKET() close(sockfd)
+    /* this interface is deprecated as of rabbitmq-c 0.4 */
+    sockfd = amqp_open_socket (CONF(conf, host), conf->port);
+    if (sockfd < 0)
+    {
+        char errbuf[1024];
+        status = (-1) * sockfd;
+        ERROR ("amqp plugin: amqp_open_socket failed: %s",
+                sstrerror (status, errbuf, sizeof (errbuf)));
+        amqp_destroy_connection (conf->connection);
+        conf->connection = NULL;
+        return (status);
+    }
+    amqp_set_sockfd (conf->connection, sockfd);
+#endif
+
+    reply = amqp_login (conf->connection, CONF(conf, vhost),
+            /* channel max = */      0,
+            /* frame max   = */ 131072,
+            /* heartbeat   = */      0,
+            /* authentication = */ AMQP_SASL_METHOD_PLAIN,
+            CONF(conf, user), CONF(conf, password));
+    if (reply.reply_type != AMQP_RESPONSE_NORMAL)
+    {
+        ERROR ("amqp plugin: amqp_login (vhost = %s, user = %s) failed.",
+                CONF(conf, vhost), CONF(conf, user));
+        amqp_destroy_connection (conf->connection);
+        CLOSE_SOCKET ();
+        conf->connection = NULL;
+        return (1);
+    }
+
+    amqp_channel_open (conf->connection, /* channel = */ 1);
+    /* FIXME: Is checking "reply.reply_type" really correct here? How does
+     * it get set? --octo */
+    if (reply.reply_type != AMQP_RESPONSE_NORMAL)
+    {
+        ERROR ("amqp plugin: amqp_channel_open failed.");
+        amqp_connection_close (conf->connection, AMQP_REPLY_SUCCESS);
+        amqp_destroy_connection (conf->connection);
+        CLOSE_SOCKET ();
+        conf->connection = NULL;
+        return (1);
+    }
+
+    INFO ("amqp plugin: Successfully opened connection to vhost \"%s\" "
+            "on %s:%i.", CONF(conf, vhost), CONF(conf, host), conf->port);
+
+    status = camqp_create_exchange (conf);
+    if (status != 0)
+        return (status);
+
+    if (!conf->publish)
+        return (camqp_setup_queue (conf));
+    return (0);
+} /* }}} int camqp_connect */
+
+static int camqp_shutdown (void) /* {{{ */
+{
+    size_t i;
+
+    DEBUG ("amqp plugin: Shutting down %zu subscriber threads.",
+            subscriber_threads_num);
+
+    subscriber_threads_running = 0;
+    for (i = 0; i < subscriber_threads_num; i++)
+    {
+        /* FIXME: Sending a signal is not very elegant here. Maybe find out how
+         * to use a timeout in the thread and check for the variable in regular
+         * intervals. */
+        pthread_kill (subscriber_threads[i], SIGTERM);
+        pthread_join (subscriber_threads[i], /* retval = */ NULL);
+    }
+
+    subscriber_threads_num = 0;
+    sfree (subscriber_threads);
+
+    DEBUG ("amqp plugin: All subscriber threads exited.");
+
+    return (0);
+} /* }}} int camqp_shutdown */
+
+/*
+ * Subscribing code
+ */
+static int camqp_read_body (camqp_config_t *conf, /* {{{ */
+        size_t body_size, const char *content_type)
+{
+    char body[body_size + 1];
+    char *body_ptr;
+    size_t received;
+    amqp_frame_t frame;
+    int status;
+
+    memset (body, 0, sizeof (body));
+    body_ptr = &body[0];
+    received = 0;
+
+    while (received < body_size)
+    {
+        status = amqp_simple_wait_frame (conf->connection, &frame);
+        if (status < 0)
+        {
+            char errbuf[1024];
+            status = (-1) * status;
+            ERROR ("amqp plugin: amqp_simple_wait_frame failed: %s",
+                    sstrerror (status, errbuf, sizeof (errbuf)));
+            camqp_close_connection (conf);
+            return (status);
+        }
+
+        if (frame.frame_type != AMQP_FRAME_BODY)
+        {
+            NOTICE ("amqp plugin: Unexpected frame type: %#"PRIx8,
+                    frame.frame_type);
+            return (-1);
+        }
+
+        if ((body_size - received) < frame.payload.body_fragment.len)
+        {
+            WARNING ("amqp plugin: Body is larger than indicated by header.");
+            return (-1);
+        }
+
+        memcpy (body_ptr, frame.payload.body_fragment.bytes,
+                frame.payload.body_fragment.len);
+        body_ptr += frame.payload.body_fragment.len;
+        received += frame.payload.body_fragment.len;
+    } /* while (received < body_size) */
+
+    if (strcasecmp ("text/collectd", content_type) == 0)
+    {
+        status = handle_putval (stderr, body);
+        if (status != 0)
+            ERROR ("amqp plugin: handle_putval failed with status %i.",
+                    status);
+        return (status);
+    }
+    else if (strcasecmp ("application/json", content_type) == 0)
+    {
+        ERROR ("amqp plugin: camqp_read_body: Parsing JSON data has not "
+                "been implemented yet. FIXME!");
+        return (0);
+    }
+    else
+    {
+        ERROR ("amqp plugin: camqp_read_body: Unknown content type \"%s\".",
+                content_type);
+        return (EINVAL);
+    }
+
+    /* not reached */
+    return (0);
+} /* }}} int camqp_read_body */
+
+static int camqp_read_header (camqp_config_t *conf) /* {{{ */
+{
+    int status;
+    amqp_frame_t frame;
+    amqp_basic_properties_t *properties;
+    char *content_type;
+
+    status = amqp_simple_wait_frame (conf->connection, &frame);
+    if (status < 0)
+    {
+        char errbuf[1024];
+        status = (-1) * status;
+        ERROR ("amqp plugin: amqp_simple_wait_frame failed: %s",
+                    sstrerror (status, errbuf, sizeof (errbuf)));
+        camqp_close_connection (conf);
+        return (status);
+    }
+
+    if (frame.frame_type != AMQP_FRAME_HEADER)
+    {
+        NOTICE ("amqp plugin: Unexpected frame type: %#"PRIx8,
+                frame.frame_type);
+        return (-1);
+    }
+
+    properties = frame.payload.properties.decoded;
+    content_type = camqp_bytes_cstring (&properties->content_type);
+    if (content_type == NULL)
+    {
+        ERROR ("amqp plugin: Unable to determine content type.");
+        return (-1);
+    }
+
+    status = camqp_read_body (conf,
+            (size_t) frame.payload.properties.body_size,
+            content_type);
+
+    sfree (content_type);
+    return (status);
+} /* }}} int camqp_read_header */
+
+static void *camqp_subscribe_thread (void *user_data) /* {{{ */
+{
+    camqp_config_t *conf = user_data;
+    int status;
+
+    cdtime_t interval = plugin_get_interval ();
+
+    while (subscriber_threads_running)
+    {
+        amqp_frame_t frame;
+
+        status = camqp_connect (conf);
+        if (status != 0)
+        {
+            struct timespec ts_interval;
+            ERROR ("amqp plugin: camqp_connect failed. "
+                    "Will sleep for %.3f seconds.",
+                    CDTIME_T_TO_DOUBLE (interval));
+            CDTIME_T_TO_TIMESPEC (interval, &ts_interval);
+            nanosleep (&ts_interval, /* remaining = */ NULL);
+            continue;
+        }
+
+        status = amqp_simple_wait_frame (conf->connection, &frame);
+        if (status < 0)
+        {
+            struct timespec ts_interval;
+            ERROR ("amqp plugin: amqp_simple_wait_frame failed. "
+                    "Will sleep for %.3f seconds.",
+                    CDTIME_T_TO_DOUBLE (interval));
+            camqp_close_connection (conf);
+            CDTIME_T_TO_TIMESPEC (interval, &ts_interval);
+            nanosleep (&ts_interval, /* remaining = */ NULL);
+            continue;
+        }
+
+        if (frame.frame_type != AMQP_FRAME_METHOD)
+        {
+            DEBUG ("amqp plugin: Unexpected frame type: %#"PRIx8,
+                    frame.frame_type);
+            continue;
+        }
+
+        if (frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD)
+        {
+            DEBUG ("amqp plugin: Unexpected method id: %#"PRIx32,
+                    frame.payload.method.id);
+            continue;
+        }
+
+        status = camqp_read_header (conf);
+
+        amqp_maybe_release_buffers (conf->connection);
+    } /* while (subscriber_threads_running) */
+
+    camqp_config_free (conf);
+    pthread_exit (NULL);
+    return (NULL);
+} /* }}} void *camqp_subscribe_thread */
+
+static int camqp_subscribe_init (camqp_config_t *conf) /* {{{ */
+{
+    int status;
+    pthread_t *tmp;
+
+    tmp = realloc (subscriber_threads,
+            sizeof (*subscriber_threads) * (subscriber_threads_num + 1));
+    if (tmp == NULL)
+    {
+        ERROR ("amqp plugin: realloc failed.");
+        camqp_config_free (conf);
+        return (ENOMEM);
+    }
+    subscriber_threads = tmp;
+    tmp = subscriber_threads + subscriber_threads_num;
+    memset (tmp, 0, sizeof (*tmp));
+
+    status = plugin_thread_create (tmp, /* attr = */ NULL,
+            camqp_subscribe_thread, conf);
+    if (status != 0)
+    {
+        char errbuf[1024];
+        ERROR ("amqp plugin: pthread_create failed: %s",
+                sstrerror (status, errbuf, sizeof (errbuf)));
+        camqp_config_free (conf);
+        return (status);
+    }
+
+    subscriber_threads_num++;
+
+    return (0);
+} /* }}} int camqp_subscribe_init */
+
+/*
+ * Publishing code
+ */
+/* XXX: You must hold "conf->lock" when calling this function! */
+static int camqp_write_locked (camqp_config_t *conf, /* {{{ */
+        const char *buffer, const char *routing_key)
+{
+    amqp_basic_properties_t props;
+    int status;
+
+    status = camqp_connect (conf);
+    if (status != 0)
+        return (status);
+
+    memset (&props, 0, sizeof (props));
+    props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG
+        | AMQP_BASIC_DELIVERY_MODE_FLAG
+        | AMQP_BASIC_APP_ID_FLAG;
+    if (conf->format == CAMQP_FORMAT_COMMAND)
+        props.content_type = amqp_cstring_bytes("text/collectd");
+    else if (conf->format == CAMQP_FORMAT_JSON)
+        props.content_type = amqp_cstring_bytes("application/json");
+    else if (conf->format == CAMQP_FORMAT_GRAPHITE)
+        props.content_type = amqp_cstring_bytes("text/graphite");
+    else
+        assert (23 == 42);
+    props.delivery_mode = conf->delivery_mode;
+    props.app_id = amqp_cstring_bytes("collectd");
+
+    status = amqp_basic_publish(conf->connection,
+                /* channel = */ 1,
+                amqp_cstring_bytes(CONF(conf, exchange)),
+                amqp_cstring_bytes (routing_key),
+                /* mandatory = */ 0,
+                /* immediate = */ 0,
+                &props,
+                amqp_cstring_bytes(buffer));
+    if (status != 0)
+    {
+        ERROR ("amqp plugin: amqp_basic_publish failed with status %i.",
+                status);
+        camqp_close_connection (conf);
+    }
+
+    return (status);
+} /* }}} int camqp_write_locked */
+
+static int camqp_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
+        user_data_t *user_data)
+{
+    camqp_config_t *conf = user_data->data;
+    char routing_key[6 * DATA_MAX_NAME_LEN];
+    char buffer[4096];
+    int status;
+
+    if ((ds == NULL) || (vl == NULL) || (conf == NULL))
+        return (EINVAL);
+
+    memset (buffer, 0, sizeof (buffer));
+
+    if (conf->routing_key != NULL)
+    {
+        sstrncpy (routing_key, conf->routing_key, sizeof (routing_key));
+    }
+    else
+    {
+        size_t i;
+        ssnprintf (routing_key, sizeof (routing_key), "collectd/%s/%s/%s/%s/%s",
+                vl->host,
+                vl->plugin, vl->plugin_instance,
+                vl->type, vl->type_instance);
+
+        /* Switch slashes (the only character forbidden by collectd) and dots
+         * (the separation character used by AMQP). */
+        for (i = 0; routing_key[i] != 0; i++)
+        {
+            if (routing_key[i] == '.')
+                routing_key[i] = '/';
+            else if (routing_key[i] == '/')
+                routing_key[i] = '.';
+        }
+    }
+
+    if (conf->format == CAMQP_FORMAT_COMMAND)
+    {
+        status = create_putval (buffer, sizeof (buffer), ds, vl);
+        if (status != 0)
+        {
+            ERROR ("amqp plugin: create_putval failed with status %i.",
+                    status);
+            return (status);
+        }
+    }
+    else if (conf->format == CAMQP_FORMAT_JSON)
+    {
+        size_t bfree = sizeof (buffer);
+        size_t bfill = 0;
+
+        format_json_initialize (buffer, &bfill, &bfree);
+        format_json_value_list (buffer, &bfill, &bfree, ds, vl, conf->store_rates);
+        format_json_finalize (buffer, &bfill, &bfree);
+    }
+    else if (conf->format == CAMQP_FORMAT_GRAPHITE)
+    {
+        status = format_graphite (buffer, sizeof (buffer), ds, vl,
+                    conf->prefix, conf->postfix, conf->escape_char,
+                    conf->graphite_flags);
+        if (status != 0)
+        {
+            ERROR ("amqp plugin: format_graphite failed with status %i.",
+                    status);
+            return (status);
+        }
+    }
+    else
+    {
+        ERROR ("amqp plugin: Invalid format (%i).", conf->format);
+        return (-1);
+    }
+
+    pthread_mutex_lock (&conf->lock);
+    status = camqp_write_locked (conf, buffer, routing_key);
+    pthread_mutex_unlock (&conf->lock);
+
+    return (status);
+} /* }}} int camqp_write */
+
+/*
+ * Config handling
+ */
+static int camqp_config_set_format (oconfig_item_t *ci, /* {{{ */
+        camqp_config_t *conf)
+{
+    char *string;
+    int status;
+
+    string = NULL;
+    status = cf_util_get_string (ci, &string);
+    if (status != 0)
+        return (status);
+
+    assert (string != NULL);
+    if (strcasecmp ("Command", string) == 0)
+        conf->format = CAMQP_FORMAT_COMMAND;
+    else if (strcasecmp ("JSON", string) == 0)
+        conf->format = CAMQP_FORMAT_JSON;
+    else if (strcasecmp ("Graphite", string) == 0)
+        conf->format = CAMQP_FORMAT_GRAPHITE;
+    else
+    {
+        WARNING ("amqp plugin: Invalid format string: %s",
+                string);
+    }
+
+    free (string);
+
+    return (0);
+} /* }}} int config_set_string */
+
+static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
+        _Bool publish)
+{
+    camqp_config_t *conf;
+    int status;
+    int i;
+
+    conf = malloc (sizeof (*conf));
+    if (conf == NULL)
+    {
+        ERROR ("amqp plugin: malloc failed.");
+        return (ENOMEM);
+    }
+
+    /* Initialize "conf" {{{ */
+    memset (conf, 0, sizeof (*conf));
+    conf->publish = publish;
+    conf->name = NULL;
+    conf->format = CAMQP_FORMAT_COMMAND;
+    conf->host = NULL;
+    conf->port = 5672;
+    conf->vhost = NULL;
+    conf->user = NULL;
+    conf->password = NULL;
+    conf->exchange = NULL;
+    conf->routing_key = NULL;
+    /* publish only */
+    conf->delivery_mode = CAMQP_DM_VOLATILE;
+    conf->store_rates = 0;
+    /* publish & graphite only */
+    conf->prefix = NULL;
+    conf->postfix = NULL;
+    conf->escape_char = '_';
+    /* subscribe only */
+    conf->exchange_type = NULL;
+    conf->queue = NULL;
+    /* general */
+    conf->connection = NULL;
+    pthread_mutex_init (&conf->lock, /* attr = */ NULL);
+    /* }}} */
+
+    status = cf_util_get_string (ci, &conf->name);
+    if (status != 0)
+    {
+        sfree (conf);
+        return (status);
+    }
+
+    for (i = 0; i < ci->children_num; i++)
+    {
+        oconfig_item_t *child = ci->children + i;
+
+        if (strcasecmp ("Host", child->key) == 0)
+            status = cf_util_get_string (child, &conf->host);
+        else if (strcasecmp ("Port", child->key) == 0)
+        {
+            status = cf_util_get_port_number (child);
+            if (status > 0)
+            {
+                conf->port = status;
+                status = 0;
+            }
+        }
+        else if (strcasecmp ("VHost", child->key) == 0)
+            status = cf_util_get_string (child, &conf->vhost);
+        else if (strcasecmp ("User", child->key) == 0)
+            status = cf_util_get_string (child, &conf->user);
+        else if (strcasecmp ("Password", child->key) == 0)
+            status = cf_util_get_string (child, &conf->password);
+        else if (strcasecmp ("Exchange", child->key) == 0)
+            status = cf_util_get_string (child, &conf->exchange);
+        else if ((strcasecmp ("ExchangeType", child->key) == 0) && !publish)
+            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 ("RoutingKey", child->key) == 0)
+            status = cf_util_get_string (child, &conf->routing_key);
+        else if ((strcasecmp ("Persistent", child->key) == 0) && publish)
+        {
+            _Bool tmp = 0;
+            status = cf_util_get_boolean (child, &tmp);
+            if (tmp)
+                conf->delivery_mode = CAMQP_DM_PERSISTENT;
+            else
+                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 ("GraphitePrefix", child->key) == 0) && publish)
+            status = cf_util_get_string (child, &conf->prefix);
+        else if ((strcasecmp ("GraphitePostfix", child->key) == 0) && publish)
+            status = cf_util_get_string (child, &conf->postfix);
+        else if ((strcasecmp ("GraphiteEscapeChar", child->key) == 0) && publish)
+        {
+            char *tmp_buff = NULL;
+            status = cf_util_get_string (child, &tmp_buff);
+            if (strlen (tmp_buff) > 1)
+                WARNING ("amqp plugin: The option \"GraphiteEscapeChar\" handles "
+                        "only one character. Others will be ignored.");
+            conf->escape_char = tmp_buff[0];
+            sfree (tmp_buff);
+        }
+        else
+            WARNING ("amqp plugin: Ignoring unknown "
+                    "configuration option \"%s\".", child->key);
+
+        if (status != 0)
+            break;
+    } /* for (i = 0; i < ci->children_num; i++) */
+
+    if ((status == 0) && (conf->exchange == NULL))
+    {
+        if (conf->exchange_type != NULL)
+            WARNING ("amqp plugin: The option \"ExchangeType\" was given "
+                    "without the \"Exchange\" option. It will be ignored.");
+
+        if (!publish && (conf->routing_key != NULL))
+            WARNING ("amqp plugin: The option \"RoutingKey\" was given "
+                    "without the \"Exchange\" option. It will be ignored.");
+
+    }
+
+    if (status != 0)
+    {
+        camqp_config_free (conf);
+        return (status);
+    }
+
+    if (conf->exchange != NULL)
+    {
+        DEBUG ("amqp plugin: camqp_config_connection: exchange = %s;",
+                conf->exchange);
+    }
+
+    if (publish)
+    {
+        char cbname[128];
+        user_data_t ud = { conf, camqp_config_free };
+
+        ssnprintf (cbname, sizeof (cbname), "amqp/%s", conf->name);
+
+        status = plugin_register_write (cbname, camqp_write, &ud);
+        if (status != 0)
+        {
+            camqp_config_free (conf);
+            return (status);
+        }
+    }
+    else
+    {
+        status = camqp_subscribe_init (conf);
+        if (status != 0)
+        {
+            camqp_config_free (conf);
+            return (status);
+        }
+    }
+
+    return (0);
+} /* }}} int camqp_config_connection */
+
+static int camqp_config (oconfig_item_t *ci) /* {{{ */
+{
+    int i;
+
+    for (i = 0; i < ci->children_num; i++)
+    {
+        oconfig_item_t *child = ci->children + i;
+
+        if (strcasecmp ("Publish", child->key) == 0)
+            camqp_config_connection (child, /* publish = */ 1);
+        else if (strcasecmp ("Subscribe", child->key) == 0)
+            camqp_config_connection (child, /* publish = */ 0);
+        else
+            WARNING ("amqp plugin: Ignoring unknown config option \"%s\".",
+                    child->key);
+    } /* for (ci->children_num) */
+
+    return (0);
+} /* }}} int camqp_config */
+
+void module_register (void)
+{
+    plugin_register_complex_config ("amqp", camqp_config);
+    plugin_register_shutdown ("amqp", camqp_shutdown);
+} /* void module_register */
+
+/* vim: set sw=4 sts=4 et fdm=marker : */
index 23bba3e..f0ab22a 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/apache.c
- * Copyright (C) 2006-2009  Florian octo Forster
+ * Copyright (C) 2006-2010  Florian octo Forster
  * Copyright (C) 2007       Florent EppO Monbillard
  * Copyright (C) 2009       Amit Gupta
  *
@@ -144,6 +144,8 @@ static size_t apache_header_callback (void *buf, size_t size, size_t nmemb,
                st->server_type = APACHE;
        else if (strstr (buf, "lighttpd") != NULL)
                st->server_type = LIGHTTPD;
+       else if (strstr (buf, "IBM_HTTP_Server") != NULL)
+               st->server_type = APACHE;
        else
        {
                const char *hdr = buf;
@@ -333,57 +335,22 @@ static int config (oconfig_item_t *ci)
 {
        int status = 0;
        int i;
-       oconfig_item_t *lci = NULL; /* legacy config */
 
        for (i = 0; i < ci->children_num; i++)
        {
                oconfig_item_t *child = ci->children + i;
 
-               if (strcasecmp ("Instance", child->key) == 0 && child->children_num > 0)
+               if (strcasecmp ("Instance", child->key) == 0)
                        config_add (child);
                else
-               {
-                       /* legacy mode - convert to <Instance ...> config */
-                       if (lci == NULL)
-                       {
-                               lci = malloc (sizeof(*lci));
-                               if (lci == NULL)
-                               {
-                                       ERROR ("apache plugin: malloc failed.");
-                                       return (-1);
-                               }
-                               memset (lci, '\0', sizeof (*lci));
-                       }
-
-                       lci->children_num++;
-                       lci->children =
-                               realloc (lci->children,
-                                        lci->children_num * sizeof (*child));
-                       if (lci->children == NULL)
-                       {
-                               ERROR ("apache plugin: realloc failed.");
-                               return (-1);
-                       }
-                       memcpy (&lci->children[lci->children_num-1], child, sizeof (*child));
-               }
+                       WARNING ("apache plugin: The configuration option "
+                                       "\"%s\" is not allowed here. Did you "
+                                       "forget to add an <Instance /> block "
+                                       "around the configuration?",
+                                       child->key);
        } /* for (ci->children) */
 
-       if (lci)
-       {
-               /* create a <Instance ""> entry */
-               lci->key = "Instance";
-               lci->values_num = 1;
-               lci->values = (oconfig_value_t *) malloc (lci->values_num * sizeof (oconfig_value_t));
-               lci->values[0].type = OCONFIG_TYPE_STRING;
-               lci->values[0].value.string = "";
-
-               status = config_add (lci);
-               sfree (lci->values);
-               sfree (lci->children);
-               sfree (lci);
-       }
-
-       return status;
+       return (status);
 } /* int config */
 
 /* initialize curl for each host */
@@ -421,6 +388,8 @@ static int init_host (apache_t *st) /* {{{ */
                        st->server_type = APACHE;
                else if (strcasecmp(st->server, "lighttpd") == 0)
                        st->server_type = LIGHTTPD;
+               else if (strcasecmp(st->server, "ibm_http_server") == 0)
+                       st->server_type = APACHE;
                else
                        WARNING ("apache plugin: Unknown `Server' setting: %s",
                                        st->server);
@@ -509,13 +478,13 @@ static void submit_value (const char *type, const char *type_instance,
        plugin_dispatch_values (&vl);
 } /* void submit_value */
 
-static void submit_counter (const char *type, const char *type_instance,
-               counter_t c, apache_t *st)
+static void submit_derive (const char *type, const char *type_instance,
+               derive_t c, apache_t *st)
 {
        value_t v;
-       v.counter = c;
+       v.derive = c;
        submit_value (type, type_instance, v, st);
-} /* void submit_counter */
+} /* void submit_derive */
 
 static void submit_gauge (const char *type, const char *type_instance,
                gauge_t g, apache_t *st)
@@ -677,11 +646,11 @@ static int apache_read_host (user_data_t *user_data) /* {{{ */
                {
                        if ((strcmp (fields[0], "Total") == 0)
                                        && (strcmp (fields[1], "Accesses:") == 0))
-                               submit_counter ("apache_requests", "",
+                               submit_derive ("apache_requests", "",
                                                atoll (fields[2]), st);
                        else if ((strcmp (fields[0], "Total") == 0)
                                        && (strcmp (fields[1], "kBytes:") == 0))
-                               submit_counter ("apache_bytes", "",
+                               submit_derive ("apache_bytes", "",
                                                1024LL * atoll (fields[2]), st);
                }
                else if (fields_num == 2)
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;
 
index 19d95d4..ddde840 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/bind.c
- * Copyright (C) 2009  Bruno Prémont
- * Copyright (C) 2009  Florian Forster
+ * Copyright (C) 2009       Bruno Prémont
+ * Copyright (C) 2009,2010  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:
  *   Bruno Prémont <bonbons at linux-vserver.org>
- *   Florian Forster <octo at verplant.org>
+ *   Florian Forster <octo at collectd.org>
  **/
 
 #include "config.h"
 
-#ifndef _XOPEN_SOURCE
-# define _XOPEN_SOURCE 600 /* glibc2 needs this for strptime */
-#endif
+#if STRPTIME_NEEDS_STANDARDS
+# 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
+#endif /* STRPTIME_NEEDS_STANDARDS */
 
 #include "collectd.h"
 #include "common.h"
@@ -90,6 +98,10 @@ struct list_info_ptr_s
 };
 typedef struct list_info_ptr_s list_info_ptr_t;
 
+/* FIXME: Enabled by default for backwards compatibility. */
+/* TODO: Remove time parsing code. */
+static _Bool config_parse_time = 1;
+
 static char *url                   = NULL;
 static int global_opcodes          = 1;
 static int global_qtypes           = 1;
@@ -241,7 +253,8 @@ static void submit (time_t ts, const char *plugin_instance, /* {{{ */
 
   vl.values = values;
   vl.values_len = 1;
-  vl.time = ts;
+  if (config_parse_time)
+    vl.time = TIME_T_TO_CDTIME_T (ts);
   sstrncpy(vl.host, hostname_g, sizeof(vl.host));
   sstrncpy(vl.plugin, "bind", sizeof(vl.plugin));
   if (plugin_instance) {
@@ -337,36 +350,31 @@ static int bind_xml_list_callback (const char *name, /* {{{ */
   return (0);
 } /* }}} int bind_xml_list_callback */
 
-static int bind_xml_read_counter (xmlDoc *doc, xmlNode *node, /* {{{ */
-    counter_t *ret_value)
+static int bind_xml_read_derive (xmlDoc *doc, xmlNode *node, /* {{{ */
+    derive_t *ret_value)
 {
-  char *str_ptr, *end_ptr;
-  long long int value;
+  char *str_ptr;
+  value_t value;
+  int status;
 
   str_ptr = (char *) xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
   if (str_ptr == NULL)
   {
-    ERROR ("bind plugin: bind_xml_read_counter: xmlNodeListGetString failed.");
+    ERROR ("bind plugin: bind_xml_read_derive: xmlNodeListGetString failed.");
     return (-1);
   }
 
-  errno = 0;
-  value = strtoll (str_ptr, &end_ptr, 10);
-  xmlFree(str_ptr);
-  if (str_ptr == end_ptr || errno)
+  status = parse_value (str_ptr, &value, DS_TYPE_DERIVE);
+  if (status != 0)
   {
-    if (errno && (value < 0))
-      ERROR ("bind plugin: bind_xml_read_counter: strtoll failed with underflow.");
-    else if (errno && (value > 0))
-      ERROR ("bind plugin: bind_xml_read_counter: strtoll failed with overflow.");
-    else
-      ERROR ("bind plugin: bind_xml_read_counter: strtoll failed.");
+    ERROR ("bind plugin: Parsing string \"%s\" to derive value failed.",
+        str_ptr);
     return (-1);
   }
 
-  *ret_value = value;
+  *ret_value = value.derive;
   return (0);
-} /* }}} int bind_xml_read_counter */
+} /* }}} int bind_xml_read_derive */
 
 static int bind_xml_read_gauge (xmlDoc *doc, xmlNode *node, /* {{{ */
     gauge_t *ret_value)
@@ -527,7 +535,7 @@ static int bind_parse_generic_name_value (const char *xpath_expression, /* {{{ *
       if (ds_type == DS_TYPE_GAUGE)
         status = bind_xml_read_gauge (doc, counter, &value.gauge);
       else
-        status = bind_xml_read_counter (doc, counter, &value.counter);
+        status = bind_xml_read_derive (doc, counter, &value.derive);
       if (status != 0)
         continue;
 
@@ -600,7 +608,7 @@ static int bind_parse_generic_value_list (const char *xpath_expression, /* {{{ *
       if (ds_type == DS_TYPE_GAUGE)
         status = bind_xml_read_gauge (doc, child, &value.gauge);
       else
-        status = bind_xml_read_counter (doc, child, &value.counter);
+        status = bind_xml_read_derive (doc, child, &value.derive);
       if (status != 0)
         continue;
 
@@ -1361,6 +1369,8 @@ static int bind_config (oconfig_item_t *ci) /* {{{ */
       bind_config_set_bool ("MemoryStats", &global_memory_stats, child);
     else if (strcasecmp ("View", child->key) == 0)
       bind_config_add_view (child);
+    else if (strcasecmp ("ParseTime", child->key) == 0)
+      cf_util_get_boolean (child, &config_parse_time);
     else
     {
       WARNING ("bind plugin: Unknown configuration option "
index e19d13e..6f58542 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-email - Documentation of collectd's C<email plugin>
index 81b3a2e..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>
 
@@ -273,6 +271,14 @@ to make use of collectd's more powerful interface.
 The user, the binary is executed as, may not have root privileges, i.E<nbsp>e.
 must have an UID that is non-zero. This is for your own good.
 
+=item
+
+Early versions of the plugin did not use a command but treated all lines as if
+they were arguments to the I<PUTVAL> command. When the I<PUTNOTIF> command was
+implemented, this behavior was kept for lines which start with an unknown
+command for backwards compatibility. This compatibility code has been removed
+in I<collectdE<nbsp>5>.
+
 =back
 
 =head1 SEE ALSO
index 9c0c6eb..00f209a 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-java - Documentation of collectd's "java plugin"
@@ -667,6 +669,14 @@ will be used.
 Use I<password> to authenticate to the server. If not given, unauthenticated
 access is used.
 
+=item B<InstancePrefix> I<prefix>
+
+Prefixes the generated I<plugin instance> with I<prefix>. If a second
+I<InstancePrefix> is specified in a referenced I<MBean> block, the prefix
+specified in the I<Connection> block will appear at the beginning of the
+I<plugin instance>, the prefix specified in the I<MBean> block will be appended
+to it.
+
 =item B<Collect> I<mbean_block_name>
 
 Configures which of the I<MBean> blocks to use with this connection. May be
index 45162bd..e31d95c 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd-nagios - src/collectd-nagios.c
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008-2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
 #include <strings.h>
 #include <assert.h>
 
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include "libcollectdclient/client.h"
-
-/*
- * This is copied directly from collectd.h. Make changes there!
- */
 #if NAN_STATIC_DEFAULT
 # include <math.h>
 /* #endif NAN_STATIC_DEFAULT*/
 # ifndef isnan
 #  define isnan(f) ((f) != (f))
 # endif /* !defined(isnan) */
+# ifndef isfinite
+#  define isfinite(f) (((f) - (f)) == 0.0)
+# endif
+# ifndef isinf
+#  define isinf(f) (!isfinite(f) && !isnan(f))
+# endif
 #endif /* NAN_ZERO_ZERO */
 
+#include "libcollectdclient/collectd/client.h"
+
 #define RET_OKAY     0
 #define RET_WARNING  1
 #define RET_CRITICAL 2
@@ -96,6 +96,7 @@ static char *hostname_g = NULL;
 static range_t range_critical_g;
 static range_t range_warning_g;
 static int consolitation_g = CON_NONE;
+static _Bool nan_is_error_g = 0;
 
 static char **match_ds_g = NULL;
 static int    match_ds_num_g = 0;
@@ -254,6 +255,7 @@ static void usage (const char *name)
                        "  -H <host>      Hostname to query the values for.\n"
                        "  -c <range>     Critical range\n"
                        "  -w <range>     Warning range\n"
+                       "  -m             Treat \"Not a Number\" (NaN) as critical (default: warning)\n"
                        "\n"
                        "Consolidation functions:\n"
                        "  none:          Apply the warning- and critical-ranges to each data-source\n"
@@ -267,6 +269,67 @@ static void usage (const char *name)
        exit (1);
 } /* void usage */
 
+static int do_listval (lcc_connection_t *connection)
+{
+       lcc_identifier_t *ret_ident = NULL;
+       size_t ret_ident_num = 0;
+
+       char *hostname = NULL;
+
+       int status;
+       size_t i;
+
+       status = lcc_listval (connection, &ret_ident, &ret_ident_num);
+       if (status != 0) {
+               printf ("UNKNOWN: %s\n", lcc_strerror (connection));
+               if (ret_ident != NULL)
+                       free (ret_ident);
+               return (RET_UNKNOWN);
+       }
+
+       status = lcc_sort_identifiers (connection, ret_ident, ret_ident_num);
+       if (status != 0) {
+               printf ("UNKNOWN: %s\n", lcc_strerror (connection));
+               if (ret_ident != NULL)
+                       free (ret_ident);
+               return (RET_UNKNOWN);
+       }
+
+       for (i = 0; i < ret_ident_num; ++i) {
+               char id[1024];
+
+               if ((hostname_g != NULL) && (strcasecmp (hostname_g, ret_ident[i].host)))
+                       continue;
+
+               if ((hostname == NULL) || strcasecmp (hostname, ret_ident[i].host))
+               {
+                       if (hostname != NULL)
+                               free (hostname);
+                       hostname = strdup (ret_ident[i].host);
+                       printf ("Host: %s\n", hostname);
+               }
+
+               /* empty hostname; not to be printed again */
+               ret_ident[i].host[0] = '\0';
+
+               status = lcc_identifier_to_string (connection,
+                               id, sizeof (id), ret_ident + i);
+               if (status != 0) {
+                       printf ("ERROR: listval: Failed to convert returned "
+                                       "identifier to a string: %s\n",
+                                       lcc_strerror (connection));
+                       continue;
+               }
+
+               /* skip over the (empty) hostname and following '/' */
+               printf ("\t%s\n", id + 1);
+       }
+
+       if (ret_ident != NULL)
+               free (ret_ident);
+       return (RET_OKAY);
+} /* int do_listval */
+
 static int do_check_con_none (size_t values_num,
                double *values, char **values_names)
 {
@@ -280,7 +343,12 @@ static int do_check_con_none (size_t values_num,
        for (i = 0; i < values_num; i++)
        {
                if (isnan (values[i]))
-                       num_warning++;
+               {
+                       if (nan_is_error_g)
+                               num_critical++;
+                       else
+                               num_warning++;
+               }
                else if (match_range (&range_critical_g, values[i]) != 0)
                        num_critical++;
                else if (match_range (&range_warning_g, values[i]) != 0)
@@ -337,11 +405,18 @@ static int do_check_con_average (size_t values_num,
        total_num = 0;
        for (i = 0; i < values_num; i++)
        {
-               if (!isnan (values[i]))
+               if (isnan (values[i]))
                {
-                       total += values[i];
-                       total_num++;
+                       if (!nan_is_error_g)
+                               continue;
+
+                       printf ("CRITICAL: Data source \"%s\" is NaN\n",
+                                       values_names[i]);
+                       return (RET_CRITICAL);
                }
+
+               total += values[i];
+               total_num++;
        }
 
        if (total_num == 0)
@@ -389,11 +464,18 @@ static int do_check_con_sum (size_t values_num,
        total_num = 0;
        for (i = 0; i < values_num; i++)
        {
-               if (!isnan (values[i]))
+               if (isnan (values[i]))
                {
-                       total += values[i];
-                       total_num++;
+                       if (!nan_is_error_g)
+                               continue;
+
+                       printf ("CRITICAL: Data source \"%s\" is NaN\n",
+                                       values_names[i]);
+                       return (RET_CRITICAL);
                }
+
+               total += values[i];
+               total_num++;
        }
 
        if (total_num == 0)
@@ -443,8 +525,19 @@ static int do_check_con_percentage (size_t values_num,
        }
 
        for (i = 0; i < values_num; i++)
-               if (!isnan (values[i]))
-                       sum += values[i];
+       {
+               if (isnan (values[i]))
+               {
+                       if (!nan_is_error_g)
+                               continue;
+
+                       printf ("CRITICAL: Data source \"%s\" is NaN\n",
+                                       values_names[i]);
+                       return (RET_CRITICAL);
+               }
+
+               sum += values[i];
+       }
 
        if (sum == 0.0)
        {
@@ -476,34 +569,20 @@ static int do_check_con_percentage (size_t values_num,
        return (status_code);
 } /* int do_check_con_percentage */
 
-static int do_check (void)
+static int do_check (lcc_connection_t *connection)
 {
-       lcc_connection_t *connection;
        gauge_t *values;
        char   **values_names;
        size_t   values_num;
-       char address[1024];
        char ident_str[1024];
        lcc_identifier_t ident;
        size_t i;
        int status;
 
-       snprintf (address, sizeof (address), "unix:%s", socket_file_g);
-       address[sizeof (address) - 1] = 0;
-
        snprintf (ident_str, sizeof (ident_str), "%s/%s",
                        hostname_g, value_string_g);
        ident_str[sizeof (ident_str) - 1] = 0;
 
-       connection = NULL;
-       status = lcc_connect (address, &connection);
-       if (status != 0)
-       {
-               printf ("ERROR: Connecting to daemon at %s failed.\n",
-                               socket_file_g);
-               return (RET_CRITICAL);
-       }
-
        memset (&ident, 0, sizeof (ident));
        status = lcc_string_to_identifier (connection, &ident, ident_str);
        if (status != 0)
@@ -551,6 +630,11 @@ static int do_check (void)
 
 int main (int argc, char **argv)
 {
+       char address[1024];
+       lcc_connection_t *connection;
+
+       int status;
+
        range_critical_g.min = NAN;
        range_critical_g.max = NAN;
        range_critical_g.invert = 0;
@@ -563,7 +647,7 @@ int main (int argc, char **argv)
        {
                int c;
 
-               c = getopt (argc, argv, "w:c:s:n:H:g:d:h");
+               c = getopt (argc, argv, "w:c:s:n:H:g:d:hm");
                if (c < 0)
                        break;
 
@@ -623,17 +707,35 @@ int main (int argc, char **argv)
                                match_ds_num_g++;
                                break;
                        }
+                       case 'm':
+                               nan_is_error_g = 1;
+                               break;
                        default:
                                usage (argv[0]);
                } /* switch (c) */
        }
 
        if ((socket_file_g == NULL) || (value_string_g == NULL)
-                       || (hostname_g == NULL))
+                       || ((hostname_g == NULL) && (strcasecmp (value_string_g, "LIST"))))
        {
                fprintf (stderr, "Missing required arguments.\n");
                usage (argv[0]);
        }
 
-       return (do_check ());
+       snprintf (address, sizeof (address), "unix:%s", socket_file_g);
+       address[sizeof (address) - 1] = 0;
+
+       connection = NULL;
+       status = lcc_connect (address, &connection);
+       if (status != 0)
+       {
+               printf ("ERROR: Connecting to daemon at %s failed.\n",
+                               socket_file_g);
+               return (RET_CRITICAL);
+       }
+
+       if (0 == strcasecmp (value_string_g, "LIST"))
+               return (do_listval (connection));
+
+       return (do_check (connection));
 } /* int main */
index c6347ea..aab753c 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-nagios - Nagios plugin for querying collectd
@@ -94,6 +96,12 @@ I<min> (and the colon) may be omitted,
 I<min> is then assumed to be zero. If I<max> (but not the trailing colon) is
 omitted, I<max> is assumed to be positive infinity.
 
+=item B<-m>
+
+If this option is given, "Not a Number" (NaN) is treated as I<critical>. By
+default, the I<none> consolidation reports NaNs as I<warning>. Other
+consolidations simply ignore NaN values.
+
 =back
 
 =head1 RETURN VALUE
index 5c11b65..669e3f4 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-perl - Documentation of collectd's C<perl plugin>
@@ -208,7 +210,7 @@ layout looks like this:
   {
     values => [123, 0.5],
     time   => time (),
-    interval => $interval_g,
+    interval => plugin_get_interval (),
     host   => $hostname_g,
     plugin => 'myplugin',
     type   => 'myplugin',
@@ -376,11 +378,6 @@ is found (and the number of values matches the number of data-sources) then the
 type, data-set and value-list is passed to all write-callbacks that are
 registered with the daemon.
 
-B<Note>: Prior to version 4.4 of collectd, the data-set type used to be passed
-as the first argument to B<plugin_register>. This syntax is still supported
-for backwards compatibility but has been deprecated and will be removed in
-some future version of collectd.
-
 =item B<plugin_write> ([B<plugins> => I<...>][, B<datasets> => I<...>],
 B<valuelists> => I<...>)
 
@@ -405,23 +402,6 @@ argument has been specified, only named plugins will be flushed. The value of
 the B<plugins> and B<identifiers> arguments may either be a string or a
 reference to an array of strings.
 
-=item B<plugin_flush_one> (I<timeout>, I<plugin>)
-
-This is identical to using "plugin_flush (timeout =E<gt> I<timeout>, plugins
-=E<gt> I<plugin>".
-
-B<Note>: Starting with version 4.5 of collectd, B<plugin_flush_one> has been
-deprecated and will be removed in some future version of collectd. Use
-B<plugin_flush> instead.
-
-=item B<plugin_flush_all> (I<timeout>)
-
-This is identical to using "plugin_flush (timeout =E<gt> I<timeout>)".
-
-B<Note>: Starting with version 4.5 of collectd, B<plugin_flush_all> has been
-deprecated and will be removed in some future version of collectd. Use
-B<plugin_flush> instead.
-
 =item B<plugin_dispatch_notification> (I<notification>)
 
 Submits a I<notification> to the daemon which will then pass it to all
@@ -437,6 +417,13 @@ The message is passed to all log-callbacks that are registered with collectd.
 Wrappers around B<plugin_log>, using B<LOG_ERR>, B<LOG_WARNING>,
 B<LOG_NOTICE>, B<LOG_INFO> and B<LOG_DEBUG> respectively as I<log-level>.
 
+=item B<plugin_get_interval> ()
+
+Returns the interval of the current plugin as a floating point number in
+seconds. This value depends on the interval configured within the
+C<LoadPlugin perl> block or the global interval (see L<collectd.conf(5)> for
+details).
+
 =back
 
 The following function provides the filter chain C-interface to Perl-modules.
@@ -516,6 +503,11 @@ B<FQDNLookup> configuration options (see L<collectd.conf(5)> for details).
 This variable keeps the interval in seconds in which the read functions are
 queried (see the B<Interval> configuration option).
 
+B<Note:> This variable should no longer be used in favor of
+C<plugin_get_interval()> (see above). This function takes any plugin-specific
+interval settings into account (see the C<Interval> option of C<LoadPlugin> in
+L<collectd.conf(5)> for details).
+
 =back
 
 Any changes to these variables will be globally visible in collectd.
@@ -678,9 +670,9 @@ A very simple read function might look like:
 
   sub foobar_read
   {
-    my $vl = { plugin => 'foobar' };
+    my $vl = { plugin => 'foobar', type => 'gauge' };
     $vl->{'values'} = [ rand(42) ];
-    plugin_dispatch_values ('gauge', $vl);
+    plugin_dispatch_values ($vl);
     return 1;
   }
 
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..fd7a508 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-snmp - Documentation of collectd's C<snmp plugin>
diff --git a/src/collectd-tg.c b/src/collectd-tg.c
new file mode 100644 (file)
index 0000000..9fec340
--- /dev/null
@@ -0,0 +1,435 @@
+/**
+ * collectd-td - collectd traffic generator
+ * Copyright (C) 2010-2012  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200809L
+#endif
+
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 700
+#endif
+
+#if !__GNUC__
+# define __attribute__(x) /**/
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "utils_heap.h"
+
+#include "libcollectdclient/collectd/client.h"
+#include "libcollectdclient/collectd/network.h"
+#include "libcollectdclient/collectd/network_buffer.h"
+
+#define DEF_NUM_HOSTS    1000
+#define DEF_NUM_PLUGINS    20
+#define DEF_NUM_VALUES 100000
+#define DEF_INTERVAL       10.0
+
+static int conf_num_hosts = DEF_NUM_HOSTS;
+static int conf_num_plugins = DEF_NUM_PLUGINS;
+static int conf_num_values = DEF_NUM_VALUES;
+static double conf_interval = DEF_INTERVAL;
+static const char *conf_destination = NET_DEFAULT_V6_ADDR;
+static const char *conf_service = NET_DEFAULT_PORT;
+
+static lcc_network_t *net;
+
+static c_heap_t *values_heap = NULL;
+
+static struct sigaction sigint_action;
+static struct sigaction sigterm_action;
+
+static _Bool loop = 1;
+
+__attribute__((noreturn))
+static void exit_usage (int exit_status) /* {{{ */
+{
+  fprintf ((exit_status == EXIT_FAILURE) ? stderr : stdout,
+      "collectd-tg -- collectd traffic generator\n"
+      "\n"
+      "  Usage: collectd-ng [OPTION]\n"
+      "\n"
+      "  Valid options:\n"
+      "    -n <number>    Number of value lists. (Default: %i)\n"
+      "    -H <number>    Number of hosts to emulate. (Default: %i)\n"
+      "    -p <number>    Number of plugins to emulate. (Default: %i)\n"
+      "    -i <seconds>   Interval of each value in seconds. (Default: %.3f)\n"
+      "    -d <dest>      Destination address of the network packets.\n"
+      "                   (Default: %s)\n"
+      "    -D <port>      Destination port of the network packets.\n"
+      "                   (Default: %s)\n"
+      "    -h             Print usage information (this output).\n"
+      "\n"
+      "Copyright (C) 2010-2012  Florian Forster\n"
+      "Licensed under the GNU General Public License, version 2 (GPLv2)\n",
+      DEF_NUM_VALUES, DEF_NUM_HOSTS, DEF_NUM_PLUGINS,
+      DEF_INTERVAL,
+      NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT);
+  exit (exit_status);
+} /* }}} void exit_usage */
+
+static void signal_handler (int signal) /* {{{ */
+{
+  loop = 0;
+} /* }}} void signal_handler */
+
+static int compare_time (const void *v0, const void *v1) /* {{{ */
+{
+  const lcc_value_list_t *vl0 = v0;
+  const lcc_value_list_t *vl1 = v1;
+
+  if (vl0->time < vl1->time)
+    return (-1);
+  else if (vl0->time > vl1->time)
+    return (1);
+  else
+    return (0);
+} /* }}} int compare_time */
+
+static int get_boundet_random (int min, int max) /* {{{ */
+{
+  int range;
+
+  if (min >= max)
+    return (-1);
+  if (min == (max - 1))
+    return (min);
+
+  range = max - min;
+
+  return (min + ((int) (((double) range) * ((double) random ()) / (((double) RAND_MAX) + 1.0))));
+} /* }}} int get_boundet_random */
+
+static lcc_value_list_t *create_value_list (void) /* {{{ */
+{
+  lcc_value_list_t *vl;
+  int host_num;
+
+  vl = malloc (sizeof (*vl));
+  if (vl == NULL)
+  {
+    fprintf (stderr, "malloc failed.\n");
+    return (NULL);
+  }
+  memset (vl, 0, sizeof (*vl));
+
+  vl->values = calloc (/* nmemb = */ 1, sizeof (*vl->values));
+  if (vl->values == NULL)
+  {
+    fprintf (stderr, "calloc failed.\n");
+    free (vl);
+    return (NULL);
+  }
+
+  vl->values_types = calloc (/* nmemb = */ 1, sizeof (*vl->values_types));
+  if (vl->values_types == NULL)
+  {
+    fprintf (stderr, "calloc failed.\n");
+    free (vl->values);
+    free (vl);
+    return (NULL);
+  }
+
+  vl->values_len = 1;
+
+  host_num = get_boundet_random (0, conf_num_hosts);
+
+  vl->interval = conf_interval;
+  vl->time = 1.0 + time (NULL)
+    + (host_num % (1 + (int) vl->interval));
+
+  if (get_boundet_random (0, 2) == 0)
+    vl->values_types[0] = LCC_TYPE_GAUGE;
+  else
+    vl->values_types[0] = LCC_TYPE_DERIVE;
+
+  snprintf (vl->identifier.host, sizeof (vl->identifier.host),
+      "host%04i", host_num);
+  snprintf (vl->identifier.plugin, sizeof (vl->identifier.plugin),
+      "plugin%03i", get_boundet_random (0, conf_num_plugins));
+  strncpy (vl->identifier.type,
+      (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive",
+      sizeof (vl->identifier.type));
+  snprintf (vl->identifier.type_instance, sizeof (vl->identifier.type_instance),
+      "ti%li", random ());
+
+  return (vl);
+} /* }}} int create_value_list */
+
+static void destroy_value_list (lcc_value_list_t *vl) /* {{{ */
+{
+  if (vl == NULL)
+    return;
+
+  free (vl->values);
+  free (vl->values_types);
+  free (vl);
+} /* }}} void destroy_value_list */
+
+static int send_value (lcc_value_list_t *vl) /* {{{ */
+{
+  int status;
+
+  if (vl->values_types[0] == LCC_TYPE_GAUGE)
+    vl->values[0].gauge = 100.0 * ((gauge_t) random ()) / (((gauge_t) RAND_MAX) + 1.0);
+  else
+    vl->values[0].derive += get_boundet_random (0, 100);
+
+  status = lcc_network_values_send (net, vl);
+  if (status != 0)
+    fprintf (stderr, "lcc_network_values_send failed with status %i.\n", status);
+
+  vl->time += vl->interval;
+
+  return (0);
+} /* }}} int send_value */
+
+static int get_integer_opt (const char *str, int *ret_value) /* {{{ */
+{
+  char *endptr;
+  int tmp;
+
+  errno = 0;
+  endptr = NULL;
+  tmp = (int) strtol (str, &endptr, /* base = */ 0);
+  if (errno != 0)
+  {
+    fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
+        str, strerror (errno));
+    exit (EXIT_FAILURE);
+  }
+  else if (endptr == str)
+  {
+    fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
+    exit (EXIT_FAILURE);
+  }
+  else if (*endptr != 0)
+  {
+    fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
+    exit (EXIT_FAILURE);
+  }
+
+  *ret_value = tmp;
+  return (0);
+} /* }}} int get_integer_opt */
+
+static int get_double_opt (const char *str, double *ret_value) /* {{{ */
+{
+  char *endptr;
+  double tmp;
+
+  errno = 0;
+  endptr = NULL;
+  tmp = strtod (str, &endptr);
+  if (errno != 0)
+  {
+    fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
+        str, strerror (errno));
+    exit (EXIT_FAILURE);
+  }
+  else if (endptr == str)
+  {
+    fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
+    exit (EXIT_FAILURE);
+  }
+  else if (*endptr != 0)
+  {
+    fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
+    exit (EXIT_FAILURE);
+  }
+
+  *ret_value = tmp;
+  return (0);
+} /* }}} int get_double_opt */
+
+static int read_options (int argc, char **argv) /* {{{ */
+{
+  int opt;
+
+  while ((opt = getopt (argc, argv, "n:H:p:i:d:D:h")) != -1)
+  {
+    switch (opt)
+    {
+      case 'n':
+        get_integer_opt (optarg, &conf_num_values);
+        break;
+
+      case 'H':
+        get_integer_opt (optarg, &conf_num_hosts);
+        break;
+
+      case 'p':
+        get_integer_opt (optarg, &conf_num_plugins);
+        break;
+
+      case 'i':
+        get_double_opt (optarg, &conf_interval);
+        break;
+
+      case 'd':
+        conf_destination = optarg;
+        break;
+
+      case 'D':
+        conf_service = optarg;
+        break;
+
+      case 'h':
+        exit_usage (EXIT_SUCCESS);
+
+      default:
+        exit_usage (EXIT_FAILURE);
+    } /* switch (opt) */
+  } /* while (getopt) */
+
+  return (0);
+} /* }}} int read_options */
+
+int main (int argc, char **argv) /* {{{ */
+{
+  int i;
+  time_t last_time;
+  int values_sent = 0;
+
+  read_options (argc, argv);
+
+  sigint_action.sa_handler = signal_handler;
+  sigaction (SIGINT, &sigint_action, /* old = */ NULL);
+
+  sigterm_action.sa_handler = signal_handler;
+  sigaction (SIGTERM, &sigterm_action, /* old = */ NULL);
+
+
+  values_heap = c_heap_create (compare_time);
+  if (values_heap == NULL)
+  {
+    fprintf (stderr, "c_heap_create failed.\n");
+    exit (EXIT_FAILURE);
+  }
+
+  net = lcc_network_create ();
+  if (net == NULL)
+  {
+    fprintf (stderr, "lcc_network_create failed.\n");
+    exit (EXIT_FAILURE);
+  }
+  else
+  {
+    lcc_server_t *srv;
+    
+    srv = lcc_server_create (net, conf_destination, conf_service);
+    if (srv == NULL)
+    {
+      fprintf (stderr, "lcc_server_create failed.\n");
+      exit (EXIT_FAILURE);
+    }
+
+    lcc_server_set_ttl (srv, 42);
+#if 0
+    lcc_server_set_security_level (srv, ENCRYPT,
+        "admin", "password1");
+#endif
+  }
+
+  fprintf (stdout, "Creating %i values ... ", conf_num_values);
+  fflush (stdout);
+  for (i = 0; i < conf_num_values; i++)
+  {
+    lcc_value_list_t *vl;
+
+    vl = create_value_list ();
+    if (vl == NULL)
+    {
+      fprintf (stderr, "create_value_list failed.\n");
+      exit (EXIT_FAILURE);
+    }
+
+    c_heap_insert (values_heap, vl);
+  }
+  fprintf (stdout, "done\n");
+
+  last_time = 0;
+  while (loop)
+  {
+    lcc_value_list_t *vl = c_heap_get_root (values_heap);
+
+    if (vl == NULL)
+      break;
+
+    if (vl->time != last_time)
+    {
+      printf ("%i values have been sent.\n", values_sent);
+
+      /* Check if we need to sleep */
+      time_t now = time (NULL);
+
+      while (now < vl->time)
+      {
+        /* 1 / 100 second */
+        struct timespec ts = { 0, 10000000 };
+        nanosleep (&ts, /* remaining = */ NULL);
+        now = time (NULL);
+
+        if (!loop)
+          break;
+      }
+      last_time = vl->time;
+    }
+
+    send_value (vl);
+    values_sent++;
+
+    c_heap_insert (values_heap, vl);
+  }
+
+  fprintf (stdout, "Shutting down.\n");
+  fflush (stdout);
+
+  while (42)
+  {
+    lcc_value_list_t *vl = c_heap_get_root (values_heap);
+    if (vl == NULL)
+      break;
+    destroy_value_list (vl);
+  }
+  c_heap_destroy (values_heap);
+
+  lcc_network_destroy (net);
+  exit (EXIT_SUCCESS);
+  return (0);
+} /* }}} int main */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
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
diff --git a/src/collectd-threshold.pod b/src/collectd-threshold.pod
new file mode 100644 (file)
index 0000000..35f8a9f
--- /dev/null
@@ -0,0 +1,205 @@
+=encoding UTF-8
+
+=head1 NAME
+
+collectd-threshold - Documentation of collectd's I<Threshold plugin>
+
+=head1 SYNOPSIS
+
+ LoadPlugin "threshold"
+ <Plugin "threshold">
+   <Type "foo">
+     WarningMin    0.00
+     WarningMax 1000.00
+     FailureMin    0.00
+     FailureMax 1200.00
+     Invert false
+     Instance "bar"
+   </Type>
+ </Plugin>
+
+=head1 DESCRIPTION
+
+Starting with version C<4.3.0> I<collectd> has support for B<monitoring>. By
+that we mean that the values are not only stored or sent somewhere, but that
+they are judged and, if a problem is recognized, acted upon. The only action
+the I<Threshold plugin> takes itself is to generate and dispatch a
+I<notification>. Other plugins can register to receive notifications and
+perform appropriate further actions.
+
+Since systems and what you expect them to do differ a lot, you can configure
+I<thresholds> for your values freely. This gives you a lot of flexibility but
+also a lot of responsibility.
+
+Every time a value is out of range, a notification is dispatched. This means
+that the idle percentage of your CPU needs to be less then the configured
+threshold only once for a notification to be generated. There's no such thing
+as a moving average or similar - at least not now.
+
+Also, all values that match a threshold are considered to be relevant or
+"interesting". As a consequence collectd will issue a notification if they are
+not received for B<Timeout> iterations. The B<Timeout> configuration option is
+explained in section L<collectd.conf(5)/"GLOBAL OPTIONS">. If, for example,
+B<Timeout> is set to "2" (the default) and some hosts sends it's CPU statistics
+to the server every 60 seconds, a notification will be dispatched after about
+120 seconds. It may take a little longer because the timeout is checked only
+once each B<Interval> on the server.
+
+When a value comes within range again or is received after it was missing, an
+"OKAY-notification" is dispatched.
+
+=head1 CONFIGURATION
+
+Here is a configuration example to get you started. Read below for more
+information.
+
+ LoadPlugin "threshold"
+ <Plugin "threshold">
+   <Type "foo">
+     WarningMin    0.00
+     WarningMax 1000.00
+     FailureMin    0.00
+     FailureMax 1200.00
+     Invert false
+     Instance "bar"
+   </Type>
+   
+   <Plugin "interface">
+     Instance "eth0"
+     <Type "if_octets">
+       FailureMax 10000000
+       DataSource "rx"
+     </Type>
+   </Plugin>
+   
+   <Host "hostname">
+     <Type "cpu">
+       Instance "idle"
+       FailureMin 10
+     </Type>
+   
+     <Plugin "memory">
+       <Type "memory">
+         Instance "cached"
+         WarningMin 100000000
+       </Type>
+     </Plugin>
+   
+     <Type "load">
+       DataSource "midterm"
+       FailureMax 4
+       Hits 3
+       Hysteresis 3
+     </Type>
+   </Host>
+ </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
+configured. The C<Plugin> and C<Type> blocks may be specified further using the
+C<Instance> option. You can combine the block by nesting the blocks, though
+they must be nested in the above order, i.e. C<Host> may contain either
+C<Plugin> and C<Type> blocks, C<Plugin> may only contain C<Type> blocks and
+C<Type> may not contain other blocks. If multiple blocks apply to the same
+value the most specific block is used.
+
+The other statements specify the threshold to configure. They B<must> be
+included in a C<Type> block. Currently the following statements are recognized:
+
+=over 4
+
+=item B<FailureMax> I<Value>
+
+=item B<WarningMax> I<Value>
+
+Sets the upper bound of acceptable values. If unset defaults to positive
+infinity. If a value is greater than B<FailureMax> a B<FAILURE> notification
+will be created. If the value is greater than B<WarningMax> but less than (or
+equal to) B<FailureMax> a B<WARNING> notification will be created.
+
+=item B<FailureMin> I<Value>
+
+=item B<WarningMin> I<Value>
+
+Sets the lower bound of acceptable values. If unset defaults to negative
+infinity. If a value is less than B<FailureMin> a B<FAILURE> notification will
+be created. If the value is less than B<WarningMin> but greater than (or equal
+to) B<FailureMin> a B<WARNING> notification will be created.
+
+=item B<DataSource> I<DSName>
+
+Some data sets have more than one "data source". Interesting examples are the
+C<if_octets> data set, which has received (C<rx>) and sent (C<tx>) bytes and
+the C<disk_ops> data set, which holds C<read> and C<write> operations. The
+system load data set, C<load>, even has three data sources: C<shortterm>,
+C<midterm>, and C<longterm>.
+
+Normally, all data sources are checked against a configured threshold. If this
+is undesirable, or if you want to specify different limits for each data
+source, you can use the B<DataSource> option to have a threshold apply only to
+one data source.
+
+=item B<Invert> B<true>|B<false>
+
+If set to B<true> the range of acceptable values is inverted, i.e. values
+between B<FailureMin> and B<FailureMax> (B<WarningMin> and B<WarningMax>) are
+not okay. Defaults to B<false>.
+
+=item B<Persist> B<true>|B<false>
+
+Sets how often notifications are generated. If set to B<true> one notification
+will be generated for each value that is out of the acceptable range. If set to
+B<false> (the default) then a notification is only generated if a value is out
+of range but the previous value was okay.
+
+This applies to missing values, too: If set to B<true> a notification about a
+missing value is generated once every B<Interval> seconds. If set to B<false>
+only one such notification is generated until the value appears again.
+
+=item B<PersistOK> B<true>|B<false>
+
+Sets how OKAY notifications act. If set to B<true> one notification will be
+generated for each value that is in the acceptable range. If set to B<false>
+(the default) then a notification is only generated if a value is in range but
+the previous value was not.
+
+=item B<Percentage> B<true>|B<false>
+
+If set to B<true>, the minimum and maximum values given are interpreted as
+percentage value, relative to the other data sources. This is helpful for
+example for the "df" type, where you may want to issue a warning when less than
+5E<nbsp>% of the total space is available. Defaults to B<false>.
+
+=item B<Hits> I<Value>
+
+Sets the number of occurrences which the threshold must be raised before to
+dispatch any notification or, in other words, the number of B<Interval>s
+that the threshold must be match before dispatch any notification.
+
+=item B<Hysteresis> I<Value>
+
+Sets the hysteresis value for threshold. The hysteresis is a method to prevent
+flapping between states, until a new received value for a previously matched
+threshold down below the threshold condition (B<WarningMax>, B<FailureMin> or
+everything else) minus the hysteresis value, the failure (respectively warning)
+state will be keep.
+
+=item B<Interesting> B<true>|B<false>
+
+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
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>
+
+=head1 AUTHOR
+
+Florian Forster E<lt>octoE<nbsp>atE<nbsp>collectd.orgE<gt>
index 83802a1..c572df1 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd-unixsock - Documentation of collectd's C<unixsock plugin>
@@ -11,6 +13,7 @@ collectd-unixsock - Documentation of collectd's C<unixsock plugin>
     SocketFile "/path/to/socket"
     SocketGroup "collectd"
     SocketPerms "0770"
+    DeleteSocket false
   </Plugin>
 
 =head1 DESCRIPTION
index 6b77d59..d259753 100644 (file)
@@ -40,7 +40,7 @@
  * Global variables
  */
 char hostname_g[DATA_MAX_NAME_LEN];
-int  interval_g;
+cdtime_t interval_g;
 int  timeout_g;
 #if HAVE_LIBKSTAT
 kstat_ctl_t *kc;
@@ -51,7 +51,9 @@ static int loop = 0;
 static void *do_flush (void __attribute__((unused)) *arg)
 {
        INFO ("Flushing all data.");
-       plugin_flush (NULL, -1, NULL);
+       plugin_flush (/* plugin = */ NULL,
+                       /* timeout = */ 0,
+                       /* ident = */ NULL);
        INFO ("Finished flushing all data.");
        pthread_exit (NULL);
        return NULL;
@@ -116,7 +118,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);
        }
@@ -136,19 +138,11 @@ static int init_hostname (void)
 
 static int init_global_variables (void)
 {
-       const char *str;
+       char const *str;
 
-       str = global_option_get ("Interval");
-       if (str == NULL)
-               str = "10";
-       interval_g = atoi (str);
-       if (interval_g <= 0)
-       {
-               fprintf (stderr, "Cannot set the interval to a correct value.\n"
-                               "Please check your settings.\n");
-               return (-1);
-       }
-       DEBUG ("interval_g = %i;", interval_g);
+       interval_g = cf_get_default_interval ();
+       assert (interval_g > 0);
+       DEBUG ("interval_g = %.3f;", CDTIME_T_TO_DOUBLE (interval_g));
 
        str = global_option_get ("Timeout");
        if (str == NULL)
@@ -315,22 +309,15 @@ static int do_init (void)
 
 static int do_loop (void)
 {
-       struct timeval tv_now;
-       struct timeval tv_next;
-       struct timeval tv_wait;
-       struct timespec ts_wait;
+       cdtime_t interval = cf_get_default_interval ();
+       cdtime_t wait_until;
+
+       wait_until = cdtime () + interval;
 
        while (loop == 0)
        {
-               if (gettimeofday (&tv_next, NULL) < 0)
-               {
-                       char errbuf[1024];
-                       ERROR ("gettimeofday failed: %s",
-                                       sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-                       return (-1);
-               }
-               tv_next.tv_sec += interval_g;
+               struct timespec ts_wait = { 0, 0 };
+               cdtime_t now;
 
 #if HAVE_LIBKSTAT
                update_kstat ();
@@ -339,27 +326,20 @@ static int do_loop (void)
                /* Issue all plugins */
                plugin_read_all ();
 
-               if (gettimeofday (&tv_now, NULL) < 0)
-               {
-                       char errbuf[1024];
-                       ERROR ("gettimeofday failed: %s",
-                                       sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-                       return (-1);
-               }
-
-               if (timeval_cmp (tv_next, tv_now, &tv_wait) <= 0)
+               now = cdtime ();
+               if (now >= wait_until)
                {
                        WARNING ("Not sleeping because the next interval is "
-                                       "%i.%06i seconds in the past!",
-                                       (int) tv_wait.tv_sec, (int) tv_wait.tv_usec);
+                                       "%.3f seconds in the past!",
+                                       CDTIME_T_TO_DOUBLE (now - wait_until));
+                       wait_until = now + interval;
                        continue;
                }
 
-               ts_wait.tv_sec  = tv_wait.tv_sec;
-               ts_wait.tv_nsec = (long) (1000 * tv_wait.tv_usec);
+               CDTIME_T_TO_TIMESPEC (wait_until - now, &ts_wait);
+               wait_until = wait_until + interval;
 
-               while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) == -1))
+               while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) != 0))
                {
                        if (errno != EINTR)
                        {
@@ -372,7 +352,6 @@ static int do_loop (void)
                }
        } /* while (loop == 0) */
 
-       DEBUG ("return (0);");
        return (0);
 } /* int do_loop */
 
@@ -476,6 +455,8 @@ int main (int argc, char **argv)
        if (optind < argc)
                exit_usage (1);
 
+       plugin_init_ctx ();
+
        /*
         * Read options from the config file, the environment and the command
         * line (in that order, with later options overwriting previous ones in
index 21d0eb1..331fad7 100644 (file)
 ##############################################################################
 
 #Hostname    "localhost"
-FQDNLookup   true
-#BaseDir     "@prefix@/var/lib/@PACKAGE_NAME@"
-#PIDFile     "@prefix@/var/run/@PACKAGE_NAME@.pid"
-#PluginDir   "@prefix@/lib/@PACKAGE_NAME@"
+#FQDNLookup   true
+#BaseDir     "@localstatedir@/lib/@PACKAGE_NAME@"
+#PIDFile     "@localstatedir@/run/@PACKAGE_NAME@.pid"
+#PluginDir   "@libdir@/@PACKAGE_NAME@"
 #TypesDB     "@prefix@/share/@PACKAGE_NAME@/types.db"
+
+#----------------------------------------------------------------------------#
+# 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
 
 ##############################################################################
 # Logging                                                                    #
@@ -52,6 +62,8 @@ FQDNLookup   true
 # to missing dependencies or because they have been deactivated explicitly.  #
 ##############################################################################
 
+#@BUILD_PLUGIN_AGGREGATION_TRUE@LoadPlugin aggregation
+#@BUILD_PLUGIN_AMQP_TRUE@LoadPlugin amqp
 #@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
 #@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
 #@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors
@@ -72,6 +84,7 @@ FQDNLookup   true
 #@BUILD_PLUGIN_DNS_TRUE@LoadPlugin dns
 #@BUILD_PLUGIN_EMAIL_TRUE@LoadPlugin email
 #@BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy
+#@BUILD_PLUGIN_ETHSTAT_TRUE@LoadPlugin ethstat
 #@BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec
 #@BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount
 #@BUILD_PLUGIN_FSCACHE_TRUE@LoadPlugin fscache
@@ -85,8 +98,10 @@ FQDNLookup   true
 #@BUILD_PLUGIN_JAVA_TRUE@LoadPlugin java
 #@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt
 @BUILD_PLUGIN_LOAD_TRUE@@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load
+#@BUILD_PLUGIN_LPAR_TRUE@LoadPlugin lpar
 #@BUILD_PLUGIN_MADWIFI_TRUE@LoadPlugin madwifi
 #@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon
+#@BUILD_PLUGIN_MD_TRUE@LoadPlugin md
 #@BUILD_PLUGIN_MEMCACHEC_TRUE@LoadPlugin memcachec
 #@BUILD_PLUGIN_MEMCACHED_TRUE@LoadPlugin memcached
 @BUILD_PLUGIN_MEMORY_TRUE@@BUILD_PLUGIN_MEMORY_TRUE@LoadPlugin memory
@@ -101,6 +116,7 @@ FQDNLookup   true
 #@BUILD_PLUGIN_NOTIFY_DESKTOP_TRUE@LoadPlugin notify_desktop
 #@BUILD_PLUGIN_NOTIFY_EMAIL_TRUE@LoadPlugin notify_email
 #@BUILD_PLUGIN_NTPD_TRUE@LoadPlugin ntpd
+#@BUILD_PLUGIN_NUMA_TRUE@LoadPlugin numa
 #@BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut
 #@BUILD_PLUGIN_OLSRD_TRUE@LoadPlugin olsrd
 #@BUILD_PLUGIN_ONEWIRE_TRUE@LoadPlugin onewire
@@ -118,6 +134,7 @@ FQDNLookup   true
 #@BUILD_PLUGIN_PYTHON_TRUE@<LoadPlugin python>
 #@BUILD_PLUGIN_PYTHON_TRUE@  Globals true
 #@BUILD_PLUGIN_PYTHON_TRUE@</LoadPlugin>
+#@BUILD_PLUGIN_REDIS_TRUE@LoadPlugin redis
 #@BUILD_PLUGIN_ROUTEROS_TRUE@LoadPlugin routeros
 #@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached
 @LOAD_PLUGIN_RRDTOOL@LoadPlugin rrdtool
@@ -127,6 +144,7 @@ FQDNLookup   true
 #@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
@@ -137,10 +155,15 @@ FQDNLookup   true
 #@BUILD_PLUGIN_UPTIME_TRUE@LoadPlugin uptime
 #@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
 #@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
+#@BUILD_PLUGIN_VARNISH_TRUE@LoadPlugin varnish
 #@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_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
 
@@ -151,16 +174,53 @@ FQDNLookup   true
 # ription of those options is available in the collectd.conf(5) manual page. #
 ##############################################################################
 
+#<Plugin "aggregation">
+#  <Aggregation>
+#    #Host "unspecified"
+#    Plugin "cpu"
+#    #PluginInstance "unspecified"
+#    Type "cpu"
+#    #TypeInstance "unspecified"
+#
+#    GroupBy "Host"
+#    GroupBy "TypeInstance"
+#
+#    CalculateNum false
+#    CalculateSum false
+#    CalculateAverage true
+#    CalculateMinimum false
+#    CalculateMaximum false
+#    CalculateStddev false
+#  </Aggregation>
+#</Plugin>
+
+#<Plugin "amqp">
+#  <Publish "name">
+#    Host "localhost"
+#    Port "5672"
+#    VHost "/"
+#    User "guest"
+#    Password "guest"
+#    Exchange "amq.fanout"
+#    RoutingKey "collectd"
+#    Persistent false
+#    StoreRates false
+#  </Publish>
+#</Plugin>
+
 #<Plugin apache>
-#      URL "http://localhost/status?auto"
-#      User "www-user"
-#      Password "secret"
-#      CACert "/etc/ssl/ca.crt"
+#  <Instance "local">
+#    URL "http://localhost/status?auto"
+#    User "www-user"
+#    Password "secret"
+#    CACert "/etc/ssl/ca.crt"
+#  </Instance>
 #</Plugin>
 
 #<Plugin apcups>
 #      Host "localhost"
 #      Port "3551"
+#      ReportSeconds true
 #</Plugin>
 
 #<Plugin ascent>
@@ -172,6 +232,7 @@ FQDNLookup   true
 
 #<Plugin "bind">
 #  URL "http://localhost:8053/"
+#  ParseTime       false
 #  OpCodes         true
 #  QTypes          true
 #
@@ -190,7 +251,7 @@ FQDNLookup   true
 #</Plugin>
 
 #<Plugin csv>
-#      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/csv"
+#      DataDir "@localstatedir@/lib/@PACKAGE_NAME@/csv"
 #      StoreRates false
 #</Plugin>
 
@@ -277,6 +338,7 @@ FQDNLookup   true
 #              #SelectDB "custdb0"
 #              Query "num_of_customers"
 #              #Query "..."
+#              #Host "..."
 #      </Database>
 #</Plugin>
 
@@ -303,12 +365,19 @@ FQDNLookup   true
 #</Plugin>
 
 #<Plugin email>
-#      SocketFile "@prefix@/var/run/@PACKAGE_NAME@-email"
+#      SocketFile "@localstatedir@/run/@PACKAGE_NAME@-email"
 #      SocketGroup "collectd"
 #      SocketPerms "0770"
 #      MaxConns 5
 #</Plugin>
 
+#<Plugin ethstat>
+#      Interface "eth0"
+#      Map "rx_csum_offload_errors" "if_rx_errors" "checksum_offload"
+#      Map "multicast" "if_multicast"
+#      MappedOnly false
+#</Plugin>
+
 #<Plugin exec>
 #      Exec "user:group" "/path/to/exec"
 #      NotificationExec "user:group" "/path/to/exec"
@@ -342,13 +411,6 @@ FQDNLookup   true
 #<Plugin hddtemp>
 #  Host "127.0.0.1"
 #  Port "7634"
-#
-#  #----------------------------------------------------------------#
-#  # `TranslateDevicename' enables backwards compatibility behavior #
-#  # and is enabled by default. Setting this option to `false' is   #
-#  # highly recommended.                                            #
-#  #----------------------------------------------------------------#
-#  TranslateDevicename false
 #</Plugin>
 
 #<Plugin interface>
@@ -394,6 +456,12 @@ FQDNLookup   true
 #      InterfaceDevice "name:device"
 #      IgnoreSelected false
 #      HostnameFormat name
+#      InterfaceFormat name
+#</Plugin>
+
+#<Plugin lpar>
+#      CpuPoolStats   false
+#      ReportBySerial false
 #</Plugin>
 
 #<Plugin madwifi>
@@ -412,6 +480,11 @@ FQDNLookup   true
 #      Port "411"
 #</Plugin>
 
+#<Plugin md>
+#      Device "/dev/md0"
+#      IgnoreSelected false
+#</Plugin>
+
 #<Plugin memcachec>
 #      <Page "plugin_instance">
 #              Server "localhost"
@@ -427,8 +500,10 @@ FQDNLookup   true
 #</Plugin>
 
 #<Plugin memcached>
-#      Host "127.0.0.1"
-#      Port "11211"
+#      <Instance "local">
+#              Host "127.0.0.1"
+#              Port "11211"
+#      </Instance>
 #</Plugin>
 
 #<Plugin modbus>
@@ -547,7 +622,7 @@ FQDNLookup   true
 #              AuthFile "/etc/collectd/passwd"
 #              Interface "eth0"
 #      </Listen>
-#      MaxPacketSize 1024
+#      MaxPacketSize 1452
 #
 #      # proxy setup (client and server as above):
 #      Forward true
@@ -588,6 +663,7 @@ FQDNLookup   true
 #      Host "localhost"
 #      Port 123
 #      ReverseLookups false
+#      IncludeUnitID true
 #</Plugin>
 
 #<Plugin nut>
@@ -690,6 +766,11 @@ FQDNLookup   true
 #                      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"
@@ -705,6 +786,12 @@ FQDNLookup   true
 #              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>
@@ -741,6 +828,14 @@ FQDNLookup   true
 #      </Module>
 #</Plugin>
 
+#<Plugin redis>
+#   <Node example>
+#      Host "redis.example.com"
+#      Port "6379"
+#      Timeout 2000
+#   </Node>
+#</Plugin>
+
 #<Plugin routeros>
 #      <Router>
 #              Host "router.example.com"
@@ -758,18 +853,22 @@ FQDNLookup   true
 
 #<Plugin rrdcached>
 #      DaemonAddress "unix:/tmp/rrdcached.sock"
-#      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/rrd"
+#      DataDir "@localstatedir@/lib/@PACKAGE_NAME@/rrd"
 #      CreateFiles true
+#      CreateFilesAsync false
 #      CollectStatistics true
 #</Plugin>
 
 #<Plugin rrdtool>
-#      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/rrd"
+#      DataDir "@localstatedir@/lib/@PACKAGE_NAME@/rrd"
+#      CreateFilesAsync false
 #      CacheTimeout 120
 #      CacheFlush   900
+#      WritesPerSecond 50
 #</Plugin>
 
 #<Plugin sensors>
+#      SensorConfigFile "/etc/sensors.conf"
 #      Sensor "it8712-isa-0290/temperature-temp1"
 #      Sensor "it8712-isa-0290/fanspeed-fan3"
 #      Sensor "it8712-isa-0290/voltage-in8"
@@ -818,6 +917,11 @@ FQDNLookup   true
 #   </Host>
 #</Plugin>
 
+#<Plugin "swap">
+#      ReportByDevice false
+#      ReportBytes true
+#</Plugin>
+
 #<Plugin "table">
 #      <Table "/proc/slabinfo">
 #              Instance "slabinfo"
@@ -856,6 +960,33 @@ FQDNLookup   true
 #  </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"
@@ -888,16 +1019,50 @@ FQDNLookup   true
 #      SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock"
 #      SocketGroup "collectd"
 #      SocketPerms "0660"
+#      DeleteSocket false
 #</Plugin>
 
 #<Plugin uuid>
 #      UUIDFile "/etc/uuid"
 #</Plugin>
 
+#<Plugin varnish>
+#   This tag support an argument if you want to
+#   monitor the local instance just use </Instance>
+#   If you prefer defining another instance you can do
+#   so by using <Instance "myinstance">
+#   <Instance>
+#      CollectCache true
+#      CollectBackend true
+#      CollectConnections true
+#      CollectSHM true
+#      CollectESI false
+#      CollectFetch false
+#      CollectHCB false
+#      CollectSMA false
+#      CollectSMS false
+#      CollectSM false
+#      CollectTotals false
+#      CollectWorkers false
+#   </Instance>
+#</Plugin>
+
 #<Plugin vmem>
 #      Verbose false
 #</Plugin>
 
+#<Plugin write_graphite>
+#  <Node "example">
+#    Host "localhost"
+#    Port "2003"
+#    Prefix "collectd"
+#    Postfix "collectd"
+#    StoreRates true
+#    AlwaysAppendDS false
+#    EscapeCharacter "_"
+#  </Node>
+#</Plugin>
+
 #<Plugin write_http>
 #      <URL "http://example.com/collectd-post">
 #              User "collectd"
@@ -910,6 +1075,37 @@ FQDNLookup   true
 #      </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"
+#              Port "6379"
+#              Timeout 1000
+#      </Node>
+#</Plugin>
+
+#<Plugin write_riemann>
+#      <Node "example">
+#              Host "localhost"
+#              Port 5555
+#              Protocol UDP
+#              StoreRates true
+#              AlwaysAppendDS false
+#      </Node>
+#      Tag "foobar"
+#</Plugin>
+
 ##############################################################################
 # Filter configuration                                                       #
 #----------------------------------------------------------------------------#
@@ -930,6 +1126,7 @@ FQDNLookup   true
 #@BUILD_PLUGIN_TARGET_REPLACE_TRUE@LoadPlugin target_replace
 #@BUILD_PLUGIN_TARGET_SCALE_TRUE@LoadPlugin target_scale
 #@BUILD_PLUGIN_TARGET_SET_TRUE@LoadPlugin target_set
+#@BUILD_PLUGIN_TARGET_V5UPGRADE_TRUE@LoadPlugin target_v5upgrade
 
 #----------------------------------------------------------------------------#
 # The following block demonstrates the default behavior if no filtering is   #
@@ -939,3 +1136,52 @@ FQDNLookup   true
 #<Chain "PostCache">
 #  Target "write"
 #</Chain>
+
+##############################################################################
+# Threshold configuration                                                    #
+#----------------------------------------------------------------------------#
+# The following outlines how to configure collectd's threshold checking      #
+# plugin. The plugin and possible configuration options are documented in    #
+# the collectd-threshold(5) manual page.                                     #
+##############################################################################
+
+#@BUILD_PLUGIN_THRESHOLD_TRUE@LoadPlugin "threshold"
+#<Plugin "threshold">
+#  <Type "foo">
+#    WarningMin    0.00
+#    WarningMax 1000.00
+#    FailureMin    0.00
+#    FailureMax 1200.00
+#    Invert false
+#    Instance "bar"
+#  </Type>
+#
+#  <Plugin "interface">
+#    Instance "eth0"
+#    <Type "if_octets">
+#      FailureMax 10000000
+#      DataSource "rx"
+#    </Type>
+#  </Plugin>
+#
+#  <Host "hostname">
+#    <Type "cpu">
+#      Instance "idle"
+#      FailureMin 10
+#    </Type>
+#
+#    <Plugin "memory">
+#      <Type "memory">
+#        Instance "cached"
+#        WarningMin 100000000
+#      </Type>
+#    </Plugin>
+#
+#    <Type "load">
+#      DataSource "midterm"
+#      FailureMax 4
+#      Hits 3
+#      Hysteresis 3
+#    </Type>
+#  </Host>
+#</Plugin>
index 75f7942..7a4eb93 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectd.conf - Configuration for the system statistics collection daemon B<collectd>
@@ -7,11 +9,15 @@ collectd.conf - Configuration for the system statistics collection daemon B<coll
   BaseDir "/path/to/data/"
   PIDFile "/path/to/pidfile/collectd.pid"
   Server  "123.123.123.123" 12345
-
+  
   LoadPlugin cpu
   LoadPlugin load
+  
+  <LoadPlugin df>
+    Interval 3600
+  </LoadPlugin>
+  
   LoadPlugin ping
-
   <Plugin ping>
     Host "example.org"
     Host "provider.net"
@@ -73,6 +79,7 @@ options are allowed inside a B<LoadPlugin> block:
 
   <LoadPlugin perl>
     Globals true
+    Interval 10
   </LoadPlugin>
 
 =over 4
@@ -82,15 +89,25 @@ options are allowed inside a B<LoadPlugin> block:
 If enabled, collectd will export all global symbols of the plugin (and of all
 libraries loaded as dependencies of the plugin) and, thus, makes those symbols
 available for resolving unresolved symbols in subsequently loaded plugins if
-that is supported by your system. By default, this is disabled.
+that is supported by your system.
 
-This is useful (or possibly even required), e.E<nbsp>g., when loading a plugin
-that embeds some scripting language into the daemon (e.E<nbsp>g. the C<perl>
-or C<python> plugins). Scripting languages usually provide means to load
+This is useful (or possibly even required), e.g., when loading a plugin that
+embeds some scripting language into the daemon (e.g. the I<Perl> and
+I<Python plugins>). Scripting languages usually provide means to load
 extensions written in C. Those extensions require symbols provided by the
-interpreter, which is loaded as a dependency of the respective collectd
-plugin. See the documentation of those plugins (e.E<nbsp>g.,
-L<collectd-perl(5)> or L<collectd-python(5)>) for details.
+interpreter, which is loaded as a dependency of the respective collectd plugin.
+See the documentation of those plugins (e.g., L<collectd-perl(5)> or
+L<collectd-python(5)>) for details.
+
+By default, this is disabled. As a special exception, if the plugin name is
+either C<perl> or C<python>, the default is changed to enabled in order to keep
+the average user from ever having to deal with this low level linking stuff.
+
+=item B<Interval> I<Seconds>
+
+Sets a plugin-specific interval for collecting metrics. This overrides the
+global B<Interval> setting. If a plugin provides own support for specifying an
+interval, that setting will take precedence.
 
 =back
 
@@ -104,6 +121,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
@@ -151,14 +191,20 @@ missing when no update has been received for twice the update interval. Since
 this setting uses iterations, the maximum allowed time without update depends
 on the I<Interval> information contained in each value list. This is used in
 the I<Threshold> configuration to dispatch notifications about missing values,
-see L<"THRESHOLD CONFIGURATION"> below.
+see L<collectd-threshold(5)> for details.
 
 =item B<ReadThreads> I<Num>
 
 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<Hostname> I<Name>
 
@@ -169,13 +215,8 @@ hostname will be determined using the L<gethostname(2)> system call.
 
 If B<Hostname> is determined automatically this setting controls whether or not
 the daemon should try to figure out the "fully qualified domain name", FQDN.
-This is done using a lookup of the name returned by C<gethostname>.
-
-Using this feature (i.E<nbsp>e. setting this option to B<true>) is recommended.
-However, to preserve backwards compatibility the default is set to B<false>.
-The sample config file that is installed with C<makeE<nbsp>install> includes a
-line which sets this option, though, so that default installations will have
-this setting enabled.
+This is done using a lookup of the name returned by C<gethostname>. This option
+is enabled by default.
 
 =item B<PreCacheChain> I<ChainName>
 
@@ -194,12 +235,360 @@ C<Plugin>-Section. Which options exist depends on the plugin used. Some plugins
 require external configuration, too. The C<apache plugin>, for example,
 required C<mod_status> to be configured in the webserver you're going to
 collect data from. These plugins are listed below as well, even if they don't
-require any configuration within collectd's configfile.
+require any configuration within collectd's configuration file.
 
 A list of all plugins and a short summary for each plugin can be found in the
 F<README> file shipped with the sourcecode and hopefully binary packets as
 well.
 
+=head2 Plugin C<aggregation>
+
+The I<Aggregation plugin> makes it possible to aggregate several values into
+one using aggregation functions such as I<sum>, I<average>, I<min> and I<max>.
+This can be put to a wide variety of uses, e.g. average and total CPU
+statistics for your entire fleet.
+
+The grouping is powerful but, as with many powerful tools, may be a bit
+difficult to wrap your head around. The grouping will therefore be
+demonstrated using an example: The average and sum of the CPU usage across
+all CPUs of each host is to be calculated.
+
+To select all the affected values for our example, set C<Plugin cpu> and
+C<Type cpu>. The other values are left unspecified, meaning "all values". The
+I<Host>, I<Plugin>, I<PluginInstance>, I<Type> and I<TypeInstance> options
+work as if they were specified in the C<WHERE> clause of an C<SELECT> SQL
+statement.
+
+  Plugin "cpu"
+  Type "cpu"
+
+Although the I<Host>, I<PluginInstance> (CPU number, i.e. 0, 1, 2, ...)  and
+I<TypeInstance> (idle, user, system, ...) fields are left unspecified in the
+example, the intention is to have a new value for each host / type instance
+pair. This is achieved by "grouping" the values using the C<GroupBy> option.
+It can be specified multiple times to group by more than one field.
+
+  GroupBy "Host"
+  GroupBy "TypeInstance"
+
+We do neither specify nor group by I<plugin instance> (the CPU number), so all
+metrics that differ in the CPU number only will be aggregated. Each
+aggregation needs I<at least one> such field, otherwise no aggregation would
+take place.
+
+The full example configuration looks like this:
+
+ <Plugin "aggregation">
+   <Aggregation>
+     Plugin "cpu"
+     Type "cpu"
+     
+     GroupBy "Host"
+     GroupBy "TypeInstance"
+     
+     CalculateSum true
+     CalculateAverage true
+   </Aggregation>
+ </Plugin>
+
+There are a couple of limitations you should be aware of:
+
+=over 4
+
+=item
+
+The I<Type> cannot be left unspecified, because it is not reasonable to add
+apples to oranges. Also, the internal lookup structure won't work if you try
+to group by type.
+
+=item
+
+There must be at least one unspecified, ungrouped field. Otherwise nothing
+will be aggregated.
+
+=back
+
+As you can see in the example above, each aggregation has its own
+B<Aggregation> block. You can have multiple aggregation blocks and aggregation
+blocks may match the same values, i.e. one value list can update multiple
+aggregations. The following options are valid inside B<Aggregation> blocks:
+
+=over 4
+
+=item B<Host> I<Host>
+
+=item B<Plugin> I<Plugin>
+
+=item B<PluginInstance> I<PluginInstance>
+
+=item B<Type> I<Type>
+
+=item B<TypeInstance> I<TypeInstance>
+
+Selects the value lists to be added to this aggregation. B<Type> must be a
+valid data set name, see L<types.db(5)> for details.
+
+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>
+
+=item B<CalculateAverage> B<true>|B<false>
+
+=item B<CalculateMinimum> B<true>|B<false>
+
+=item B<CalculateMaximum> B<true>|B<false>
+
+=item B<CalculateStddev> B<true>|B<false>
+
+Boolean options for enabling calculation of the number of value lists, their
+sum, average, minimum, maximum andE<nbsp>/ or standard deviation. All options
+are disabled by default.
+
+=back
+
+=head2 Plugin C<amqp>
+
+The I<AMQMP plugin> can be used to communicate with other instances of
+I<collectd> or third party applications using an AMQP message broker. Values
+are sent to or received from the broker, which handles routing, queueing and
+possibly filtering or messages.
+
+ <Plugin "amqp">
+   # Send values to an AMQP broker
+   <Publish "some_name">
+     Host "localhost"
+     Port "5672"
+     VHost "/"
+     User "guest"
+     Password "guest"
+     Exchange "amq.fanout"
+ #   ExchangeType "fanout"
+ #   RoutingKey "collectd"
+ #   Persistent false
+ #   Format "command"
+ #   StoreRates false
+ #   GraphitePrefix "collectd."
+ #   GraphiteEscapeChar "_"
+ #   GraphiteSeparateInstances false
+ #   GraphiteAlwaysAppendDS false
+   </Publish>
+   
+   # Receive values from an AMQP broker
+   <Subscribe "some_name">
+     Host "localhost"
+     Port "5672"
+     VHost "/"
+     User "guest"
+     Password "guest"
+     Exchange "amq.fanout"
+ #   ExchangeType "fanout"
+ #   Queue "queue_name"
+ #   RoutingKey "collectd.#"
+   </Subscribe>
+ </Plugin>
+
+The plugin's configuration consists of a number of I<Publish> and I<Subscribe>
+blocks, which configure sending and receiving of values respectively. The two
+blocks are very similar, so unless otherwise noted, an option can be used in
+either block. The name given in the blocks starting tag is only used for
+reporting messages, but may be used to support I<flushing> of certain
+I<Publish> blocks in the future.
+
+=over 4
+
+=item B<Host> I<Host>
+
+Hostname or IP-address of the AMQP broker. Defaults to the default behavior of
+the underlying communications library, I<rabbitmq-c>, which is "localhost".
+
+=item B<Port> I<Port>
+
+Service name or port number on which the AMQP broker accepts connections. This
+argument must be a string, even if the numeric form is used. Defaults to
+"5672".
+
+=item B<VHost> I<VHost>
+
+Name of the I<virtual host> on the AMQP broker to use. Defaults to "/".
+
+=item B<User> I<User>
+
+=item B<Password> I<Password>
+
+Credentials used to authenticate to the AMQP broker. By default "guest"/"guest"
+is used.
+
+=item B<Exchange> I<Exchange>
+
+In I<Publish> blocks, this option specifies the I<exchange> to send values to.
+By default, "amq.fanout" will be used.
+
+In I<Subscribe> blocks this option is optional. If given, a I<binding> between
+the given exchange and the I<queue> is created, using the I<routing key> if
+configured. See the B<Queue> and B<RoutingKey> options below.
+
+=item B<ExchangeType> I<Type>
+
+If given, the plugin will try to create the configured I<exchange> with this
+I<type> after connecting. When in a I<Subscribe> block, the I<queue> will then
+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
+explicitly, a unique queue name will be created by the broker.
+
+=item B<RoutingKey> I<Key>
+
+In I<Publish> blocks, this configures the routing key to set on all outgoing
+messages. If not given, the routing key will be computed from the I<identifier>
+of the value. The host, plugin, type and the two instances are concatenated
+together using dots as the separator and all containing dots replaced with
+slashes. For example "collectd.host/example/com.cpu.0.cpu.user". This makes it
+possible to receive only specific values using a "topic" exchange.
+
+In I<Subscribe> blocks, configures the I<routing key> used when creating a
+I<binding> between an I<exchange> and the I<queue>. The usual wildcards can be
+used to filter messages when using a "topic" exchange. If you're only
+interested in CPU statistics, you could use the routing key "collectd.*.cpu.#"
+for example.
+
+=item B<Persistent> B<true>|B<false> (Publish only)
+
+Selects the I<delivery method> to use. If set to B<true>, the I<persistent>
+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>|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
+identical to the syntax used by the I<Exec> and I<UnixSock plugins>. In this
+case, the C<Content-Type> header field will be set to C<text/collectd>.
+
+If set to B<JSON>, the values are encoded in the I<JavaScript Object Notation>,
+an easy and straight forward exchange format. The C<Content-Type> header field
+will be set to C<application/json>.
+
+If set to B<Graphite>, values are encoded in the I<Graphite> format, which is
+"<metric> <value> <timestamp>\n". The C<Content-Type> header field will be set to
+C<text/graphite>.
+
+A subscribing client I<should> use the C<Content-Type> header field to
+determine how to decode the values. Currently, the I<AMQP plugin> itself can
+only decode the B<Command> format.
+
+=item B<StoreRates> B<true>|B<false> (Publish only)
+
+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> (Publish and B<Format>=I<Graphite> only)
+
+A prefix can be added in the metric name when outputting in the I<Graphite> format.
+It's added before the I<Host> name.
+Metric name will be "<prefix><host><postfix><plugin><type><name>"
+
+=item B<GraphitePostfix> (Publish and B<Format>=I<Graphite> only)
+
+A postfix can be added in the metric name when outputting in the I<Graphite> format.
+It's added after the I<Host> name.
+Metric name will be "<prefix><host><postfix><plugin><type><name>"
+
+=item B<GraphiteEscapeChar> (Publish and B<Format>=I<Graphite> only)
+
+Specify a character to replace dots (.) in the host part of the metric name.
+In I<Graphite> metric name, dots are used as separators between different
+metric parts (host, plugin, type).
+Default is "_" (I<Underscore>).
+
+=item B<GraphiteSeparateInstances> B<true>|B<false>
+
+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<GraphiteAlwaysAppendDS> B<true>|B<false>
+
+If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
+identifier. If set to B<false> (the default), this is only done when there is
+more than one DS.
+
+=back
+
 =head2 Plugin C<apache>
 
 To configure the C<apache>-plugin you first need to configure the Apache
@@ -218,7 +607,25 @@ Since its C<mod_status> module is very similar to Apache's, B<lighttpd> is
 also supported. It introduces a new field, called C<BusyServers>, to count the
 number of currently connected clients. This field is also supported.
 
-The following options are accepted by the C<apache>-plugin:
+The configuration of the I<Apache> plugin consists of one or more
+C<E<lt>InstanceE<nbsp>/E<gt>> blocks. Each block requires one string argument
+as the instance name. For example:
+
+ <Plugin "apache">
+   <Instance "www1">
+     URL "http://www1.example.com/mod_status?auto"
+   </Instance>
+   <Instance "www2">
+     URL "http://www2.example.com/mod_status?auto"
+   </Instance>
+ </Plugin>
+
+The instance name will be used as the I<plugin instance>. To emulate the old
+(versionE<nbsp>4) behavior, you can use an empty string (""). In order for the
+plugin to work correctly, each instance name must be unique. This is not
+enforced by the plugin and it is your responsibility to ensure it.
+
+The following options are accepted within each I<Instance> block:
 
 =over 4
 
@@ -226,7 +633,7 @@ The following options are accepted by the C<apache>-plugin:
 
 Sets the URL of the C<mod_status> output. This needs to be the output generated
 by C<ExtendedStatus on> and it needs to be the machine readable output
-generated by appending the C<?auto> argument.
+generated by appending the C<?auto> argument. This option is I<mandatory>.
 
 =item B<User> I<Username>
 
@@ -271,6 +678,12 @@ 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<ascent>
@@ -339,6 +752,7 @@ Synopsis:
 
  <Plugin "bind">
    URL "http://localhost:8053/"
+   ParseTime       false
    OpCodes         true
    QTypes          true
  
@@ -365,35 +779,44 @@ The bind plugin accepts the following configuration options:
 URL from which to retrieve the XML data. If not specified,
 C<http://localhost:8053/> will be used.
 
-=item B<OpCodes> I<true>|I<false>
+=item B<ParseTime> B<true>|B<false>
+
+When set to B<true>, the time provided by BIND will be parsed and used to
+dispatch the values. When set to B<false>, the local time source is queried.
+
+This setting is set to B<true> by default for backwards compatibility; setting
+this to B<false> is I<recommended> to avoid problems with timezones and
+localization.
+
+=item B<OpCodes> B<true>|B<false>
 
 When enabled, statistics about the I<"OpCodes">, for example the number of
 C<QUERY> packets, are collected.
 
 Default: Enabled.
 
-=item B<QTypes> I<true>|I<false>
+=item B<QTypes> B<true>|B<false>
 
 When enabled, the number of I<incoming> queries by query types (for example
 C<A>, C<MX>, C<AAAA>) is collected.
 
 Default: Enabled.
 
-=item B<ServerStats> I<true>|I<false>
+=item B<ServerStats> B<true>|B<false>
 
 Collect global server statistics, such as requests received over IPv4 and IPv6,
 successful queries, and failed updates.
 
 Default: Enabled.
 
-=item B<ZoneMaintStats> I<true>|I<false>
+=item B<ZoneMaintStats> B<true>|B<false>
 
 Collect zone maintenance statistics, mostly information about notifications
 (zone updates) and zone transfers.
 
 Default: Enabled.
 
-=item B<ResolverStats> I<true>|I<false>
+=item B<ResolverStats> B<true>|B<false>
 
 Collect resolver statistics, i.E<nbsp>e. statistics about outgoing requests
 (e.E<nbsp>g. queries over IPv4, lame servers). Since the global resolver
@@ -422,21 +845,21 @@ configured, no detailed view statistics will be collected.
 
 =over 4
 
-=item B<QTypes> I<true>|I<false>
+=item B<QTypes> B<true>|B<false>
 
 If enabled, the number of I<outgoing> queries by query type (e.E<nbsp>g. C<A>,
 C<MX>) is collected.
 
 Default: Enabled.
 
-=item B<ResolverStats> I<true>|I<false>
+=item B<ResolverStats> B<true>|B<false>
 
 Collect resolver statistics, i.E<nbsp>e. statistics about outgoing requests
 (e.E<nbsp>g. queries over IPv4, lame servers).
 
 Default: Enabled.
 
-=item B<CacheRRSets> I<true>|I<false>
+=item B<CacheRRSets> B<true>|B<false>
 
 If enabled, the number of entries (I<"RR sets">) in the view's cache by query
 type is collected. Negative entries (queries which resulted in an error, for
@@ -552,6 +975,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>
@@ -595,22 +1031,6 @@ runtime statistics module of CouchDB
     </URL>
   </Plugin>
 
-Another CouchDB example:
-The following example will collect the status values from each database:
-
-  <URL "http://localhost:5984/_all_dbs">
-    Instance "dbs"
-    <Key "*/doc_count">
-      Type "gauge"
-    </Key>
-    <Key "*/doc_del_count">
-      Type "counter"
-    </Key>
-    <Key "*/disk_size">
-      Type "bytes"
-    </Key>
-  </URL>
-
 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
@@ -626,31 +1046,15 @@ The following options are valid within B<URL> blocks:
 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<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>
+=item B<Header> I<Header>
+=item B<Post> I<Body>
 
-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.
+These options behave exactly equivalent to the appropriate options of the
+I<cURL> plugin. Please see there for a detailed description.
 
 =back
 
@@ -719,14 +1123,34 @@ 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<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>
 
@@ -990,11 +1414,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>
 
@@ -1009,6 +1445,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>
@@ -1040,22 +1481,6 @@ Report using the device name rather than the mountpoint. i.e. with this I<false>
 (the default), it will report a disk as "root", but with it I<true>, it will be
 "sda1" (or whichever).
 
-=item B<ReportReserved> B<true>|B<false>
-
-When enabled, the blocks reserved for root are reported separately. When
-disabled (the default for backwards compatibility reasons) the reserved space
-will be included in the "free" space.
-
-When disabled, the "df" type will be used to store "free" and "used" space. The
-mount point or disk name (see option B<ReportByDevice>) is used as type
-instance in this case (again: backwards compatibility).
-
-When enabled, the type "df_complex" is used and three files are created. The
-mount point or disk name is used as plugin instance and the type instance is
-set to "free", "reserved" and "used" as appropriate.
-
-Enabling this option is recommended.
-
 =item B<ReportInodes> B<true>|B<false>
 
 Enables or disables reporting of free, reserved and used inodes. Defaults to
@@ -1150,6 +1575,41 @@ at most B<16384> to prevent typos and dumb mistakes.
 
 =back
 
+=head2 Plugin C<ethstat>
+
+The I<ethstat plugin> collects information about network interface cards (NICs)
+by talking directly with the underlying kernel driver using L<ioctl(2)>.
+
+B<Synopsis:>
+
+ <Plugin "ethstat">
+   Interface "eth0"
+   Map "rx_csum_offload_errors" "if_rx_errors" "checksum_offload"
+   Map "multicast" "if_multicast"
+ </Plugin>
+
+B<Options:>
+
+=over 4
+
+=item B<Interface> I<Name>
+
+Collect statistical information about interface I<Name>.
+
+=item B<Map> I<Name> I<Type> [I<TypeInstance>]
+
+By default, the plugin will submit values as type C<derive> and I<type
+instance> set to I<Name>, the name of the metric as reported by the driver. If
+an appropriate B<Map> option exists, the given I<Type> and, optionally,
+I<TypeInstance> will be used.
+
+=item B<MappedOnly> B<true>|B<false>
+
+When set to B<true>, only metrics that can be mapped to to a I<type> will be
+collected, all other metrics will be ignored. Defaults to B<false>.
+
+=back
+
 =head2 Plugin C<exec>
 
 Please make sure to read L<collectd-exec(5)> before using this plugin. It
@@ -1375,13 +1835,6 @@ Hostname to connect to. Defaults to B<127.0.0.1>.
 
 TCP-Port to connect to. Defaults to B<7634>.
 
-=item B<TranslateDevicename> I<true>|I<false>
-
-If enabled, translate the disk names to major/minor device numbers
-(e.E<nbsp>g. "8-0" for /dev/sda). For backwards compatibility this defaults to
-I<true> but it's recommended to disable it as it will probably be removed in
-the next major version.
-
 =back
 
 =head2 Plugin C<interface>
@@ -1609,6 +2062,16 @@ You can also specify combinations of these fields. For example B<name uuid>
 means to concatenate the guest name and UUID (with a literal colon character
 between, thus I<"foo:1234-1234-1234-1234">).
 
+=item B<InterfaceFormat> B<name>|B<address>
+
+When the libvirt plugin logs interface data, it sets the name of the collected
+data according to this setting. The default is to use the path as provided by
+the hypervisor (the "dev" property of the target node), which is equal to
+setting B<name>.
+
+B<address> means use the interface's mac address. This is useful since the
+interface path might change between reboots of a guest or across migrations.
+
 =back
 
 =head2 Plugin C<logfile>
@@ -1627,8 +2090,8 @@ debugging support.
 
 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 collectd is
-running in foreground- or non-daemon-mode.
+channels, respectively. This, of course, only makes much sense when I<collectd>
+is running in foreground- or non-daemon-mode.
 
 =item B<Timestamp> B<true>|B<false>
 
@@ -1645,6 +2108,33 @@ 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
+virtualization technique for IBM POWER processors. It takes into account CPU
+time stolen from or donated to a partition, in addition to the usual user,
+system, I/O statistics.
+
+The following configuration options are available:
+
+=over 4
+
+=item B<CpuPoolStats> B<false>|B<true>
+
+When enabled, statistics about the processor pool are read, too. The partition
+needs to have pool authority in order to be able to acquire this information.
+Defaults to false.
+
+=item B<ReportBySerial> B<false>|B<true>
+
+If enabled, the serial of the physical machine the partition is currently
+running on is reported as I<hostname> and the logical hostname of the machine
+is reported in the I<plugin instance>. Otherwise, the logical hostname will be
+used (just like other plugins) and the I<plugin instance> will be empty.
+Defaults to false.
+
+=back
+
 =head2 Plugin C<mbmon>
 
 The C<mbmon plugin> uses mbmon to retrieve temperature, voltage, etc.
@@ -1670,6 +2160,31 @@ TCP-Port to connect to. Defaults to B<411>.
 
 =back
 
+=head2 Plugin C<md>
+
+The C<md plugin> collects information from Linux Software-RAID devices (md).
+
+All reported values are of the type C<md_disks>. Reported type instances are
+I<active>, I<failed> (present but not operational), I<spare> (hot stand-by) and
+I<missing> (physically absent) disks.
+
+=over 4
+
+=item B<Device> I<Device>
+
+Select md devices based on device name. The I<device name> is the basename of
+the device, i.e. the name of the block device without the leading C</dev/>.
+See B<IgnoreSelected> for more details.
+
+=item B<IgnoreSelected> B<true>|B<false>
+
+Invert device selection: If set to B<true>, all md devices B<except> those
+listed using B<Device> are collected. If B<false> (the default), only those
+listed are collected. If no configuration is given, the B<md> plugin will
+collect data from all md devices.
+
+=back
+
 =head2 Plugin C<memcachec>
 
 The C<memcachec plugin> connects to a memcached server, queries one or more
@@ -1727,6 +2242,17 @@ The C<memcached plugin> connects to a memcached server and queries statistics
 about cache utilization, memory and bandwidth used.
 L<http://www.danga.com/memcached/>
 
+ <Plugin "memcached">
+   <Instance "name">
+     Host "memcache.example.com"
+     Port 11211
+   </Instance>
+ </Plugin>
+
+The plugin configuration consists of one or more B<Instance> blocks which
+specify one I<memcached> connection each. Within the B<Instance> blocks, the
+following options are allowed:
+
 =over 4
 
 =item B<Host> I<Hostname>
@@ -1737,6 +2263,11 @@ Hostname to connect to. Defaults to B<127.0.0.1>.
 
 TCP-Port to connect to. Defaults to B<11211>.
 
+=item B<Socket> I<Path>
+
+Connect to I<memcached> using the UNIX domain socket at I<Path>. If this
+setting is given, the B<Host> and B<Port> settings are ignored.
+
 =back
 
 =head2 Plugin C<modbus>
@@ -1791,11 +2322,11 @@ Configures the base register to read from the device. If the option
 B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
 register will be read (the register number is increased by one).
 
-=item B<RegisterType> B<Uint16>|B<Uint32>|B<Float>
+=item B<RegisterType> B<Int16>|B<Int32>|B<Uint16>|B<Uint32>|B<Float>
 
-Specifies what kind of data is returned by the device. If the type is B<Uint32>
-or B<Float>, two 16E<nbsp>bit registers will be read and the data is combined
-into one value. Defaults to B<Uint16>.
+Specifies what kind of data is returned by the device. If the type is B<Int32>,
+B<Uint32> or B<Float>, two 16E<nbsp>bit registers will be read and the data is
+combined into one value. Defaults to B<Uint16>.
 
 =item B<Type> I<Type>
 
@@ -1870,7 +2401,7 @@ B<Collect> option is mandatory.
 The C<mysql plugin> requires B<mysqlclient> to be installed. It connects to
 one or more databases when started and keeps the connection up as long as
 possible. When the connection is interrupted for whatever reason it will try
-to re-connect. The plugin will complaint loudly in case anything goes wrong.
+to re-connect. The plugin will complain loudly in case anything goes wrong.
 
 This plugin issues the MySQL C<SHOW STATUS> / C<SHOW GLOBAL STATUS> command
 and collects information about MySQL network traffic, executed statements,
@@ -2033,6 +2564,14 @@ Required capabilities are documented below.
       IgnoreSelectedSnapshot false
     </VolumeUsage>
     
+    <Quota>
+      Interval 60
+    </Quota>
+    
+    <Snapvault>
+      Interval 30
+    </Snapvault>
+    
     <System>
       Interval 30
       GetCPULoad     true
@@ -2040,6 +2579,13 @@ Required capabilities are documented below.
       GetDiskOps     true
       GetDiskIO      true
     </System>
+
+    <VFiler vfilerA>
+      Interval 60
+
+      SnapVault true
+      # ...
+    </VFiler>
    </Host>
  </Plugin>
 
@@ -2050,7 +2596,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>
 
@@ -2094,6 +2660,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>
@@ -2424,6 +3003,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
@@ -2652,7 +3263,18 @@ operating systems.
 =item B<MaxPacketSize> I<1024-65535>
 
 Set the maximum size for datagrams received over the network. Packets larger
-than this will be truncated.
+than this will be truncated. Defaults to 1452E<nbsp>bytes, which is the maximum
+payload size that can be transmitted in one Ethernet frame using IPv6E<nbsp>/
+UDP.
+
+On the server side, this limit should be set to the largest value used on
+I<any> client. Likewise, the value on the client must not be larger than the
+value on the server, or data will be lost.
+
+B<Compatibility:> Versions prior to I<versionE<nbsp>4.8> used a fixed sized
+buffer of 1024E<nbsp>bytes. Versions I<4.8>, I<4.9> and I<4.10> used a default
+value of 1024E<nbsp>bytes to avoid problems when sending data to an older
+server.
 
 =item B<Forward> I<true|false>
 
@@ -2663,16 +3285,6 @@ the same multicast group. While this results in more network traffic than
 necessary it's not a huge problem since the plugin has a duplicate detection,
 so the values will not loop.
 
-=item B<CacheFlush> I<Seconds>
-
-For each host/plugin/type combination the C<network plugin> caches the time of
-the last value being sent or received. Every I<Seconds> seconds the plugin
-searches and removes all entries that are older than I<Seconds> seconds, thus
-freeing the unused memory again. Since this process is somewhat expensive and
-normally doesn't do much, this value should not be too small. The default is
-1800 seconds, but setting this to 86400 seconds (one day) will not do much harm
-either.
-
 =item B<ReportStats> B<true>|B<false>
 
 The network plugin cannot only receive and send statistics, it can also create
@@ -2830,6 +3442,16 @@ IP-address may be used in a filename it is recommended to disable reverse
 lookups. The default is to do reverse lookups to preserve backwards
 compatibility, though.
 
+=item B<IncludeUnitID> B<true>|B<false>
+
+When a peer is a refclock, include the unit ID in the I<type instance>.
+Defaults to B<false> for backward compatibility.
+
+If two refclock peers use the same driver and this is B<false>, the plugin will
+try to write simultaneous measurements from both to the same type instance.
+This will result in error messages in the log and only one set of measurements
+making it through.
+
 =back
 
 =head2 Plugin C<nut>
@@ -3055,6 +3677,11 @@ values submitted to the daemon. Other than that, that name is not used.
 Defines the "database alias" or "service name" to connect to. Usually, these
 names are defined in the file named C<$ORACLE_HOME/network/admin/tnsnames.ora>.
 
+=item B<Host> I<Host>
+
+Hostname to use when dispatching values for this database. Defaults to using
+the global hostname of the I<collectd> instance.
+
 =item B<Username> I<Username>
 
 Username used for authentication.
@@ -3232,6 +3859,13 @@ which are available in a PostgreSQL database or use future or special
 statistics provided by PostgreSQL without the need to upgrade your collectd
 installation.
 
+Starting with version 5.2, the C<postgresql> plugin supports writing data to
+PostgreSQL databases as well. This has been implemented in a generic way. You
+need to specify an SQL statement which will then be executed by collectd in
+order to write the data (see below for details). The benefit of that approach
+is that there is no fixed database layout. Rather, the layout may be optimized
+for the current setup.
+
 The B<PostgreSQL Documentation> manual can be found at
 L<http://www.postgresql.org/docs/manuals/>.
 
@@ -3261,6 +3895,11 @@ L<http://www.postgresql.org/docs/manuals/>.
       </Result>
     </Query>
 
+    <Writer sqlstore>
+      Statement "SELECT collectd_insert($1, $2, $3, $4, $5, $6, $7, $8, $9);"
+      StoreRates true
+    </Writer>
+
     <Database foo>
       Host "hostname"
       Port "5432"
@@ -3277,6 +3916,12 @@ L<http://www.postgresql.org/docs/manuals/>.
       Query backend # predefined
       Query rt36_tickets
     </Database>
+
+    <Database qux>
+      # ...
+      Writer sqlstore
+      CommitInterval 10
+    </Database>
   </Plugin>
 
 The B<Query> block defines one database query which may later be used by a
@@ -3307,11 +3952,6 @@ allowed as long as a single non-empty command has been specified only.
 
 The returned lines will be handled separately one after another.
 
-=item B<Query> I<sql query statement>
-
-This is a deprecated synonym for B<Statement>. It will be removed in version 5
-of collectd.
-
 =item B<Param> I<hostname>|I<database>|I<username>|I<interval>
 
 Specify the parameters which should be passed to the SQL query. The parameters
@@ -3330,6 +3970,11 @@ used, the parameter expands to "localhost".
 
 The name of the database of the current connection.
 
+=item I<instance>
+
+The name of the database plugin instance. See the B<Instance> option of the
+database specification below for details.
+
 =item I<username>
 
 The username used to connect to the database.
@@ -3387,21 +4032,6 @@ This option is required inside a B<Result> block and may be specified multiple
 times. If multiple B<ValuesFrom> options are specified, the columns are read
 in the given order.
 
-=item B<Column> I<type> [I<type instance>]
-
-This is a deprecated alternative to a B<Result> block. It will be removed in
-version 5 of collectd. It is equivalent to the following B<Result> block:
-
-  <Result>
-    Type I<type>
-    InstancePrefix I<type instance>
-    ValuesFrom I<name of the x. column>
-  </Result>
-
-The order of the B<Column> options defines which columns of the query result
-should be used. The first option specifies the data found in the first column,
-the second option that of the second column, and so on.
-
 =item B<MinVersion> I<version>
 
 =item B<MaxVersion> I<version>
@@ -3416,13 +4046,6 @@ The I<version> has to be specified as the concatenation of the major, minor
 and patch-level versions, each represented as two-decimal-digit numbers. For
 example, version 8.2.3 will become 80203.
 
-=item B<MinPGVersion> I<version>
-
-=item B<MaxPGVersion> I<version>
-
-These are deprecated synonyms for B<MinVersion> and B<MaxVersion>
-respectively. They will be removed in version 5 of collectd.
-
 =back
 
 The following predefined queries are available (the definitions can be found
@@ -3465,6 +4088,101 @@ This query collects the on-disk size of the database in bytes.
 
 =back
 
+In addition, the following detailed queries are available by default. Please
+note that each of those queries collects information B<by table>, thus,
+potentially producing B<a lot> of data. For details see the description of the
+non-by_table queries above.
+
+=over 4
+
+=item B<queries_by_table>
+
+=item B<query_plans_by_table>
+
+=item B<table_states_by_table>
+
+=item B<disk_io_by_table>
+
+=back
+
+The B<Writer> block defines a PostgreSQL writer backend. It accepts a single
+mandatory argument specifying the name of the writer. This will then be used
+in the B<Database> specification in order to activate the writer instance. The
+names of all writers have to be unique. The following options may be
+specified:
+
+=over 4
+
+=item B<Statement> I<sql statement>
+
+This mandatory option specifies the SQL statement that will be executed for
+each submitted value. A single SQL statement is allowed only. Anything after
+the first semicolon will be ignored.
+
+Nine parameters will be passed to the statement and should be specified as
+tokens B<$1>, B<$2>, through B<$9> in the statement string. The following
+values are made available through those parameters:
+
+=over 4
+
+=item B<$1>
+
+The timestamp of the queried value as a floating point number.
+
+=item B<$2>
+
+The hostname of the queried value.
+
+=item B<$3>
+
+The plugin name of the queried value.
+
+=item B<$4>
+
+The plugin instance of the queried value. This value may be B<NULL> if there
+is no plugin instance.
+
+=item B<$5>
+
+The type of the queried value (cf. L<types.db(5)>).
+
+=item B<$6>
+
+The type instance of the queried value. This value may be B<NULL> if there is
+no type instance.
+
+=item B<$7>
+
+An array of names for the submitted values (i.E<nbsp>e., the name of the data
+sources of the submitted value-list).
+
+=item B<$8>
+
+An array of types for the submitted values (i.E<nbsp>e., the type of the data
+sources of the submitted value-list; C<counter>, C<gauge>, ...). Note, that if
+B<StoreRates> is enabled (which is the default, see below), all types will be
+C<gauge>.
+
+=item B<$9>
+
+An array of the submitted values. The dimensions of the value name and value
+arrays match.
+
+=back
+
+In general, it is advisable to create and call a custom function in the
+PostgreSQL database for this purpose. Any procedural language supported by
+PostgreSQL will do (see chapter "Server Programming" in the PostgreSQL manual
+for details).
+
+=item B<StoreRates> B<false>|B<true>
+
+If set to B<true> (the default), convert counter values to rates. If set to
+B<false> counter values are stored as is, i.E<nbsp>e. as an increasing integer
+number.
+
+=back
+
 The B<Database> block defines one PostgreSQL database for which to collect
 statistics. It accepts a single mandatory argument which specifies the
 database name. None of the other options are required. PostgreSQL will use
@@ -3480,6 +4198,17 @@ for details.
 Specify the interval with which the database should be queried. The default is
 to use the global B<Interval> setting.
 
+=item B<CommitInterval> I<seconds>
+
+This option may be used for database connections which have "writers" assigned
+(see above). If specified, it causes a writer to put several updates into a
+single transaction. This transaction will last for the specified amount of
+time. By default, each update will be executed in a separate transaction. Each
+transaction generates a fair amount of overhead which can, thus, be reduced by
+activating this option. The draw-back is, that data covering the specified
+amount of time will be lost, for example, if a single statement within the
+transaction fails or if the database server crashes.
+
 =item B<Host> I<hostname>
 
 Specify the hostname or IP of the PostgreSQL server to connect to. If the
@@ -3510,6 +4239,13 @@ Specify the password to be used when connecting to the server.
 Specify whether to use an SSL connection when contacting the server. The
 following modes are supported:
 
+=item B<Instance> I<name>
+
+Specify the plugin instance name that should be used instead of the database
+name (which is the default, if this option has not been specified). This
+allows to query multiple databases of the same name on the same host (e.g.
+when running multiple database server versions in parallel).
+
 =over 4
 
 =item I<disable>
@@ -3545,11 +4281,36 @@ B<PostgreSQL Documentation> for details.
 
 =item B<Query> I<query>
 
-Specify a I<query> which should be executed for the database connection. This
-may be any of the predefined or user-defined queries. If no such option is
-given, it defaults to "backends", "transactions", "queries", "query_plans",
-"table_states", "disk_io" and "disk_usage". Else, the specified queries are
-used only.
+Specifies a I<query> which should be executed in the context of the database
+connection. This may be any of the predefined or user-defined queries. If no
+such option is given, it defaults to "backends", "transactions", "queries",
+"query_plans", "table_states", "disk_io" and "disk_usage" (unless a B<Writer>
+has been specified). Else, the specified queries are used only.
+
+=item B<Writer> I<writer>
+
+Assigns the specified I<writer> backend to the database connection. This
+causes all collected data to be send to the database using the settings
+defined in the writer configuration (see the section "FILTER CONFIGURATION"
+below for details on how to selectively send data to certain plugins).
+
+Each writer will register a flush callback which may be used when having long
+transactions enabled (see the B<CommitInterval> option above). When issuing
+the B<FLUSH> command (see L<collectd-unixsock(5)> for details) the current
+transaction will be committed right away. Two different kinds of flush
+callbacks are available with the C<postgresql> plugin:
+
+=over 4
+
+=item B<postgresql>
+
+Flush all writer backends.
+
+=item B<postgresql->I<database>
+
+Flush all writers of the specified I<database> only.
+
+=back
 
 =back
 
@@ -3833,6 +4594,56 @@ Defaults to B<false>.
 
 =back
 
+=head2 Plugin C<redis>
+
+The I<Redis plugin> connects to one or more Redis servers and gathers
+information about each server's state. For each server there is a I<Node> block
+which configures the connection parameters for this node.
+
+  <Plugin redis>
+    <Node "example">
+        Host "localhost"
+        Port "6379"
+        Timeout 2000
+    </Node>
+  </Plugin>
+
+The information shown in the synopsis above is the I<default configuration>
+which is used by the plugin if no configuration is present.
+
+=over 4
+
+=item B<Node> I<Nodename>
+
+The B<Node> block identifies a new Redis node, that is a new Redis instance
+running in an specified host and port. The name for node is a canonical
+identifier which is used as I<plugin instance>. It is limited to
+64E<nbsp>characters in length.
+
+=item B<Host> I<Hostname>
+
+The B<Host> option is the hostname or IP-address where the Redis instance is
+running on.
+
+=item B<Port> I<Port>
+
+The B<Port> option is the TCP port on which the Redis instance accepts
+connections. Either a service name of a port number may be given. Please note
+that numerical port numbers must be given as a string, too.
+
+=item B<Password> I<Password>
+
+Use I<Password> to authenticate when connecting to I<Redis>.
+
+=item B<Timeout> I<Timeout in miliseconds>
+
+The B<Timeout> option set the socket timeout for node response. Since the Redis
+read function is blocking, you should keep this value as low as possible. Keep
+in mind that the sum of all B<Timeout> values for all B<Nodes> should be lower
+than B<Interval> defined globally.
+
+=back
+
 =head2 Plugin C<rrdcached>
 
 The C<rrdcached> plugin uses the RRDtool accelerator daemon, L<rrdcached(1)>,
@@ -3881,6 +4692,60 @@ 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)
+this setting is unset and the stepsize is set to the interval in which the data
+is collected. Do not use this option unless you absolutely have to for some
+reason. Setting this option may cause problems with the C<snmp plugin>, the
+C<exec plugin> or when the daemon is set up to receive data from other hosts.
+
+=item B<HeartBeat> I<Seconds>
+
+B<Force> the heartbeat of newly created RRD-files. This setting should be unset
+in which case the heartbeat is set to twice the B<StepSize> which should equal
+the interval in which data is collected. Do not set this option unless you have
+a very good reason to do so.
+
+=item B<RRARows> I<NumRows>
+
+The C<rrdtool plugin> calculates the number of PDPs per CDP based on the
+B<StepSize>, this setting and a timespan. This plugin creates RRD-files with
+three times five RRAs, i. e. five RRAs with the CFs B<MIN>, B<AVERAGE>, and
+B<MAX>. The five RRAs are optimized for graphs covering one hour, one day, one
+week, one month, and one year.
+
+So for each timespan, it calculates how many PDPs need to be consolidated into
+one CDP by calculating:
+  number of PDPs = timespan / (stepsize * rrarows)
+
+Bottom line is, set this no smaller than the width of you graphs in pixels. The
+default is 1200.
+
+=item B<RRATimespan> I<Seconds>
+
+Adds an RRA-timespan, given in seconds. Use this option multiple times to have
+more then one RRA. If this option is never used, the built-in default of (3600,
+86400, 604800, 2678400, 31622400) is used.
+
+For more information on how RRA-sizes are calculated see B<RRARows> above.
+
+=item B<XFF> I<Factor>
+
+Set the "XFiles Factor". The default is 0.1. If unsure, don't set this option.
+I<Factor> must be in the range C<[0.0-1.0)>, i.e. between zero (inclusive) and
+one (exclusive).
+
 =back
 
 =head2 Plugin C<rrdtool>
@@ -3894,8 +4759,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>
 
@@ -3916,7 +4791,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.
 
@@ -3938,6 +4813,8 @@ For more information on how RRA-sizes are calculated see B<RRARows> above.
 =item B<XFF> I<Factor>
 
 Set the "XFiles Factor". The default is 0.1. If unsure, don't set this option.
+I<Factor> must be in the range C<[0.0-1.0)>, i.e. between zero (inclusive) and
+one (exclusive).
 
 =item B<CacheFlush> I<Seconds>
 
@@ -3996,7 +4873,7 @@ because all values were added to the internal cache at roughly the same time.
 
 =head2 Plugin C<sensors>
 
-The C<sensors plugin> uses B<lm_sensors> to retrieve sensor-values. This means
+The I<Sensors plugin> uses B<lm_sensors> to retrieve sensor-values. This means
 that all the needed modules have to be loaded and lm_sensors has to be
 configured (most likely by editing F</etc/sensors.conf>. Read
 L<sensors.conf(5)> for details.
@@ -4006,6 +4883,11 @@ L<http://secure.netroedge.com/~lm78/>.
 
 =over 4
 
+=item B<SensorConfigFile> I<File>
+
+Read the I<lm_sensors> configuration from I<File>. When unset (recommended),
+the library's default will be used.
+
 =item B<Sensor> I<chip-bus-address/type-feature>
 
 Selects the name of the sensor which you want to collect or ignore, depending
@@ -4031,6 +4913,30 @@ 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<swap>
+
+The I<Swap plugin> collects information about used and available swap space. On
+I<Linux> and I<Solaris>, the following options are available:
+
+=over 4
+
+=item B<ReportByDevice> B<false>|B<true>
+
+Configures how to report physical swap devices. If set to B<false> (the
+default), the summary over all swap devices is reported only, i.e. the globally
+used and available space over all devices. If B<true> is configured, the used
+and available space of each device will be reported separately.
+
+This option is only available if the I<Swap plugin> can read C</proc/swaps>
+(under Linux) or use the L<swapctl(2)> mechanism (under I<Solaris>).
+
+=item B<ReportBytes> B<false>|B<true>
+
+When enabled, the I<swap I/O> is reported in bytes. When disabled, the default,
+I<swap I/O> is reported in pages. This option is available under Linux only.
+
+=back
+
 =head2 Plugin C<syslog>
 
 =over 4
@@ -4044,6 +4950,15 @@ syslog-daemon.
 Please note that B<debug> is only available if collectd has been compiled with
 debugging support.
 
+=item B<NotifyLevel> B<OKAY>|B<WARNING>|B<FAILURE>
+
+Controls which notifications should be sent to syslog. The default behaviour is
+not to send any. Less severe notifications always imply logging more severe
+notifications: Setting this to B<OKAY> means all notifications will be sent to
+syslog, setting this to B<WARNING> will send B<WARNING> and B<FAILURE>
+notifications but will dismiss B<OKAY> notifications. Setting this option to
+B<FAILURE> will only send failures to syslog.
+
 =back
 
 =head2 Plugin C<table>
@@ -4272,6 +5187,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
@@ -4380,7 +5386,7 @@ port in numeric form.
 
 =item B<ForceUseProcfs> I<true>|I<false>
 
-By default, the C<thermal> plugin tries to read the statistics from the Linux
+By default, the I<Thermal plugin> tries to read the statistics from the Linux
 C<sysfs> interface. If that is not available, the plugin falls back to the
 C<procfs> interface. By setting this option to I<true>, you can force the
 plugin to use the latter. This option defaults to I<false>.
@@ -4400,9 +5406,18 @@ selection is configured at all, B<all> devices are selected.
 
 =back
 
+=head2 Plugin C<threshold>
+
+The I<Threshold plugin> checks values collected or received by I<collectd>
+against a configurable I<threshold> and issues I<notifications> if values are
+out of bounds.
+
+Documentation for this plugin is available in the L<collectd-threshold(5)>
+manual page.
+
 =head2 Plugin C<tokyotyrant>
 
-The C<tokyotyrant plugin> connects to a TokyoTyrant server and collects a
+The I<TokyoTyrant plugin> connects to a TokyoTyrant server and collects a
 couple metrics: number of records, and database size on disk.
 
 =over 4
@@ -4439,6 +5454,13 @@ Change the file permissions of the UNIX-socket after it has been created. The
 permissions must be given as a numeric, octal value as you would pass to
 L<chmod(1)>. Defaults to B<0770>.
 
+=item B<DeleteSocket> B<false>|B<true>
+
+If set to B<true>, delete the socket file before calling L<bind(2)>, if a file
+with the given name already exists. If I<collectd> crashes a socket file may be
+left over, preventing the daemon from opening a new socket when restarted.
+Since this is potentially dangerous, this defaults to B<false>.
+
 =back
 
 =head2 Plugin C<uuid>
@@ -4482,6 +5504,94 @@ Take the UUID from the given file (default I</etc/uuid>).
 
 =back
 
+=head2 Plugin C<varnish>
+
+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
+
+=item B<CollectCache> B<true>|B<false>
+
+Cache hits and misses. True by default.
+
+=item B<CollectConnections> B<true>|B<false>
+
+Number of client connections received, accepted and dropped. True by default.
+
+=item B<CollectBackend> B<true>|B<false>
+
+Back-end connection statistics, such as successful, reused,
+and closed connections. True by default.
+
+=item B<CollectSHM> B<true>|B<false>
+
+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<CollectESI> B<true>|B<false>
+
+Edge Side Includes (ESI) parse statistics. False by default.
+
+=item B<CollectFetch> B<true>|B<false>
+
+Statistics about fetches (HTTP requests sent to the backend). False by default.
+
+=item B<CollectHCB> B<true>|B<false>
+
+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<CollectSMA> B<true>|B<false>
+
+malloc or umem (umem_alloc(3MALLOC) based) storage statistics.
+The umem storage component is Solaris specific. False by default.
+
+=item B<CollectSMS> B<true>|B<false>
+
+synth (synthetic content) storage statistics. This storage
+component is used internally only. False by default.
+
+=item B<CollectSM> B<true>|B<false>
+
+file (memory mapped file) storage statistics. 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<CollectWorkers> B<true>|B<false>
+
+Collect statistics about worker threads. False by default.
+
+=back
+
 =head2 Plugin C<vmem>
 
 The C<vmem> plugin collects information about the usage of virtual memory.
@@ -4516,6 +5626,128 @@ traffic (e.E<nbsp>g. due to headers and retransmission). If you want to
 collect on-wire traffic you could, for example, use the logging facilities of
 iptables to feed data for the guest IPs into the iptables plugin.
 
+=head2 Plugin C<write_graphite>
+
+The C<write_graphite> plugin writes data to I<Graphite>, an open-source metrics
+storage and graphing project. The plugin connects to I<Carbon>, the data layer
+of I<Graphite>, and sends data via the "line based" protocol (per default using
+portE<nbsp>2003). The data will be sent in blocks of at most 1428 bytes to
+minimize the number of network packets.
+
+Synopsis:
+
+ <Plugin write_graphite>
+   <Node "example">
+     Host "localhost"
+     Port "2003"
+     Prefix "collectd"
+   </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>
+
+Hostname or address to connect to. Defaults to C<localhost>.
+
+=item B<Port> I<Service>
+
+Service name or port number to connect to. Defaults to C<2003>.
+
+=item B<Prefix> I<String>
+
+When set, I<String> is added in front of the host name. Dots and whitespace are
+I<not> escaped in this string (see B<EscapeCharacter> below).
+
+=item B<Postfix> I<String>
+
+When set, I<String> is appended to the host name. Dots and whitespace are
+I<not> escaped in this string (see B<EscapeCharacter> below).
+
+=item B<EscapeCharacter> I<Char>
+
+I<Carbon> uses the dot (C<.>) as escape character and doesn't allow whitespace
+in the identifier. The B<EscapeCharacter> option determines which character
+dots, whitespace and control characters are replaced with. Defaults to
+underscore (C<_>).
+
+=item B<StoreRates> B<false>|B<true>
+
+If set to B<true> (the default), convert counter values to rates. If set to
+B<false> counter values are stored as is, i.E<nbsp>e. as an increasing integer
+number.
+
+=item B<SeparateInstances> B<false>|B<true>
+
+If set to B<true>, the plugin instance and type instance will be in their own
+path component, for example C<host.cpu.0.cpu.idle>. If set to B<false> (the
+default), the plugin and plugin instance (and likewise the type and type
+instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
+
+=item B<AlwaysAppendDS> B<false>|B<true>
+
+If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
+identifier. If set to B<false> (the default), this is only done when there is
+more than one DS.
+
+=back
+
+=head2 Plugin C<write_mongodb>
+
+The I<write_mongodb plugin> will send values to I<MongoDB>, a schema-less
+NoSQL database.
+
+B<Synopsis:>
+
+ <Plugin "write_mongodb">
+   <Node "default">
+     Host "localhost"
+     Port "27017"
+     Timeout 1000
+     StoreRates true
+   </Node>
+ </Plugin>
+
+The plugin can send values to multiple instances of I<MongoDB> by specifying
+one B<Node> block for each instance. Within the B<Node> blocks, the following
+options are available:
+
+=over 4
+
+=item B<Host> I<Address>
+
+Hostname or address to connect to. Defaults to C<localhost>.
+
+=item B<Port> I<Service>
+
+Service name or port number to connect to. Defaults to C<27017>.
+
+=item B<Timeout> I<Timeout>
+
+Set the timeout for each operation on I<MongoDB> to I<Timeout> milliseconds.
+Setting this option to zero means no timeout, which is the default.
+
+=item B<StoreRates> B<false>|B<true>
+
+If set to B<true> (the default), convert counter values to rates. If set to
+B<false> counter values are stored as is, i.e. as an increasing integer
+number.
+
+=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>
 
 This output plugin submits values to an http server by POST them using the
@@ -4580,6 +5812,132 @@ number.
 
 =back
 
+=head2 Plugin C<write_redis>
+
+The I<write_redis plugin> submits values to I<Redis>, a data structure server.
+
+Synopsis:
+
+  <Plugin "write_redis">
+    <Node "example">
+        Host "localhost"
+        Port "6379"
+        Timeout 1000
+    </Node>
+  </Plugin>
+
+Values are submitted to I<Sorted Sets>, using the metric name as the key, and
+the timestamp as the score. Retrieving a date range can then be done using the
+C<ZRANGEBYSCORE> I<Redis> command. Additionnally, all the identifiers of these
+I<Sorted Sets> are kept in a I<Set> called C<collectd/values> and can be
+retrieved using the C<SMEMBERS> I<Redis> command. See
+L<http://redis.io/commands#sorted_set> and L<http://redis.io/commands#set> for
+details.
+
+The information shown in the synopsis above is the I<default configuration>
+which is used by the plugin if no configuration is present.
+
+The plugin can send values to multiple instances of I<Redis> by specifying
+one B<Node> block for each instance. Within the B<Node> blocks, the following
+options are available:
+
+=over 4
+
+=item B<Node> I<Nodename>
+
+The B<Node> block identifies a new I<Redis> node, that is a new I<Redis>
+instance running in an specified host and port. The name for node is a
+canonical identifier which is used as I<plugin instance>. It is limited to
+64E<nbsp>characters in length.
+
+=item B<Host> I<Hostname>
+
+The B<Host> option is the hostname or IP-address where the I<Redis> instance is
+running on.
+
+=item B<Port> I<Port>
+
+The B<Port> option is the TCP port on which the Redis instance accepts
+connections. Either a service name of a port number may be given. Please note
+that numerical port numbers must be given as a string, too.
+
+=item B<Timeout> I<Timeout in miliseconds>
+
+The B<Timeout> option sets the socket connection timeout, in milliseconds.
+
+=back
+
+=head2 Plugin C<write_riemann>
+
+The I<write_riemann plugin> will send values to I<Riemann>, a powerfull 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
+     Delay 10
+   </Node>
+   Tag "foobar"
+ </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.
+
+=back
+
+=item B<Tag> I<String>
+
+Add the given string as an additional tag to the metric being sent to
+I<Riemann>.
+
+=back
+
 =head1 THRESHOLD CONFIGURATION
 
 Starting with version C<4.3.0> collectd has support for B<monitoring>. By that
@@ -4612,7 +5970,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
@@ -4643,7 +6001,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
@@ -5050,19 +6408,36 @@ Available options:
 =item B<Plugin> I<Name>
 
 Name of the write plugin to which the data should be sent. This option may be
-given multiple times to send the data to more than one write plugin.
+given multiple times to send the data to more than one write plugin. If the
+plugin supports multiple instances, the plugin's instance(s) must also be
+specified.
 
 =back
 
 If no plugin is explicitly specified, the values will be sent to all available
 write plugins.
 
-Example:
+Single-instance plugin example:
 
  <Target "write">
    Plugin "rrdtool"
  </Target>
 
+Multi-instance plugin example:
+
+ <Plugin "write_graphite">
+   <Node "foo">
+   ...
+   </Node>
+   <Node "bar">
+   ...
+   </Node>
+ </Plugin>
+  ...
+ <Target "write">
+   Plugin "write_graphite/foo"
+ </Target>
+
 =item B<jump>
 
 Starts processing the rules of another chain, see L<"Flow control"> above. If
@@ -5369,7 +6744,7 @@ convert counter values to rates.
 
 Please note that these placeholders are B<case sensitive>!
 
-=item B<Severity> B<"FATAL">|B<"WARNING">|B<"OKAY">
+=item B<Severity> B<"FAILURE">|B<"WARNING">|B<"OKAY">
 
 Sets the severity of the message. If omitted, the severity B<"WARNING"> is
 used.
index e7fc4e3..7cb405c 100644 (file)
 #if HAVE_STDINT_H
 # include <stdint.h>
 #endif
-#if HAVE_STDBOOL_H
-# include <stdbool.h>
-#else
-# ifndef HAVE__BOOL
-#  ifdef __cplusplus
-typedef bool _Bool;
-#  else
-#   define _Bool signed char
-#  endif
-# endif
-# define bool _Bool
-# define false 0
-# define true 1
-# define __bool_true_false_are_defined 1
-#endif
 #if HAVE_UNISTD_H
 # include <unistd.h>
 #endif
@@ -112,6 +97,12 @@ typedef bool _Bool;
 # define assert(...) /* nop */
 #endif
 
+#if !defined(HAVE__BOOL) || !HAVE__BOOL
+typedef int _Bool;
+# undef HAVE__BOOL
+# define HAVE__BOOL 1
+#endif
+
 #if NAN_STATIC_DEFAULT
 # include <math.h>
 /* #endif NAN_STATIC_DEFAULT*/
@@ -267,7 +258,9 @@ typedef bool _Bool;
 # define COLLECTD_GRP_NAME "collectd"
 #endif
 
-#define STATIC_ARRAY_LEN(array) (sizeof (array) / sizeof ((array)[0]))
+#ifndef COLLECTD_DEFAULT_INTERVAL
+# define COLLECTD_DEFAULT_INTERVAL 10.0
+#endif
 
 /* Remove GNU specific __attribute__ settings when using another compiler */
 #if !__GNUC__
@@ -294,8 +287,11 @@ typedef bool _Bool;
 # endif
 #endif
 
-extern char hostname_g[];
-extern int  interval_g;
-extern int  timeout_g;
+/* Type for time as used by "utils_time.h" */
+typedef uint64_t cdtime_t;
+
+extern char     hostname_g[];
+extern cdtime_t interval_g;
+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
diff --git a/src/collectdctl.c b/src/collectdctl.c
new file mode 100644 (file)
index 0000000..0b8d0c1
--- /dev/null
@@ -0,0 +1,608 @@
+/**
+ * collectd - src/collectdctl.c
+ * Copyright (C) 2010 Håkon J Dugstad Johnsen
+ * Copyright (C) 2010 Sebastian Harl
+ *
+ * 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:
+ *   Håkon J Dugstad Johnsen <hakon-dugstad.johnsen at telenor.com>
+ *   Sebastian "tokkee" Harl <sh@tokkee.org>
+ **/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include <assert.h>
+#include <errno.h>
+
+#if NAN_STATIC_DEFAULT
+# include <math.h>
+/* #endif NAN_STATIC_DEFAULT*/
+#elif NAN_STATIC_ISOC
+# ifndef __USE_ISOC99
+#  define DISABLE_ISOC99 1
+#  define __USE_ISOC99 1
+# endif /* !defined(__USE_ISOC99) */
+# include <math.h>
+# if DISABLE_ISOC99
+#  undef DISABLE_ISOC99
+#  undef __USE_ISOC99
+# endif /* DISABLE_ISOC99 */
+/* #endif NAN_STATIC_ISOC */
+#elif NAN_ZERO_ZERO
+# include <math.h>
+# ifdef NAN
+#  undef NAN
+# endif
+# define NAN (0.0 / 0.0)
+# ifndef isnan
+#  define isnan(f) ((f) != (f))
+# endif /* !defined(isnan) */
+# ifndef isfinite
+#  define isfinite(f) (((f) - (f)) == 0.0)
+# endif
+# ifndef isinf
+#  define isinf(f) (!isfinite(f) && !isnan(f))
+# endif
+#endif /* NAN_ZERO_ZERO */
+
+#include "libcollectdclient/collectd/client.h"
+
+#define DEFAULT_SOCK LOCALSTATEDIR"/run/"PACKAGE_NAME"-unixsock"
+
+extern char *optarg;
+extern int   optind;
+
+static void exit_usage (const char *name, int status) {
+  fprintf ((status == 0) ? stdout : stderr,
+      "Usage: %s [options] <command> [cmd options]\n\n"
+
+      "Available options:\n"
+      "  -s       Path to collectd's UNIX socket.\n"
+      "           Default: "DEFAULT_SOCK"\n"
+
+      "\n  -h       Display this help and exit.\n"
+
+      "\nAvailable commands:\n\n"
+
+      " * getval <identifier>\n"
+      " * flush [timeout=<seconds>] [plugin=<name>] [identifier=<id>]\n"
+      " * listval\n"
+      " * putval <identifier> [interval=<seconds>] <value-list(s)>\n"
+
+      "\nIdentifiers:\n\n"
+
+      "An identifier has the following format:\n\n"
+
+      "  [<hostname>/]<plugin>[-<plugin_instance>]/<type>[-<type_instance>]\n\n"
+
+      "Hostname defaults to the local hostname if omitted (e.g., uptime/uptime).\n"
+      "No error is returned if the specified identifier does not exist.\n"
+
+      "\n"PACKAGE" "VERSION", http://collectd.org/\n"
+      "by Florian octo Forster <octo@verplant.org>\n"
+      "for contributions see `AUTHORS'\n"
+      , name);
+  exit (status);
+}
+
+/* Count the number of occurrences of the character 'chr'
+ * in the specified string. */
+static int count_chars (const char *str, char chr) {
+  int count = 0;
+
+  while (*str != '\0') {
+    if (*str == chr) {
+      count++;
+    }
+    str++;
+  }
+
+  return count;
+} /* count_chars */
+
+static int array_grow (void **array, int *array_len, size_t elem_size)
+{
+  void *tmp;
+
+  assert ((array != NULL) && (array_len != NULL));
+
+  tmp = realloc (*array, (*array_len + 1) * elem_size);
+  if (tmp == NULL) {
+    fprintf (stderr, "ERROR: Failed to allocate memory.\n");
+    return (-1);
+  }
+
+  *array = tmp;
+  ++(*array_len);
+  return (0);
+} /* array_grow */
+
+static int parse_identifier (lcc_connection_t *c,
+    const char *value, lcc_identifier_t *ident)
+{
+  char hostname[1024];
+  char ident_str[1024] = "";
+  int  n_slashes;
+
+  int status;
+
+  n_slashes = count_chars (value, '/');
+  if (n_slashes == 1) {
+    /* The user has omitted the hostname part of the identifier
+     * (there is only one '/' in the identifier)
+     * Let's add the local hostname */
+    if (gethostname (hostname, sizeof (hostname)) != 0) {
+      fprintf (stderr, "ERROR: Failed to get local hostname: %s",
+          strerror (errno));
+      return (-1);
+    }
+    hostname[sizeof (hostname) - 1] = '\0';
+
+    snprintf (ident_str, sizeof (ident_str), "%s/%s", hostname, value);
+    ident_str[sizeof(ident_str) - 1] = '\0';
+  }
+  else {
+    strncpy (ident_str, value, sizeof (ident_str));
+    ident_str[sizeof (ident_str) - 1] = '\0';
+  }
+
+  status = lcc_string_to_identifier (c, ident, ident_str);
+  if (status != 0) {
+    fprintf (stderr, "ERROR: Failed to parse identifier ``%s'': %s.\n",
+        ident_str, lcc_strerror(c));
+    return (-1);
+  }
+  return (0);
+} /* parse_identifier */
+
+static int getval (lcc_connection_t *c, int argc, char **argv)
+{
+  lcc_identifier_t ident;
+
+  size_t   ret_values_num   = 0;
+  gauge_t *ret_values       = NULL;
+  char   **ret_values_names = NULL;
+
+  int status;
+  size_t i;
+
+  assert (strcasecmp (argv[0], "getval") == 0);
+
+  if (argc != 2) {
+    fprintf (stderr, "ERROR: getval: Missing identifier.\n");
+    return (-1);
+  }
+
+  memset (&ident, 0, sizeof (ident));
+  status = parse_identifier (c, argv[1], &ident);
+  if (status != 0)
+    return (status);
+
+#define BAIL_OUT(s) \
+  do { \
+    if (ret_values != NULL) \
+      free (ret_values); \
+    if (ret_values_names != NULL) { \
+      for (i = 0; i < ret_values_num; ++i) \
+        free (ret_values_names[i]); \
+      free (ret_values_names); \
+    } \
+    ret_values_num = 0; \
+    return (s); \
+  } while (0)
+
+  status = lcc_getval (c, &ident,
+      &ret_values_num, &ret_values, &ret_values_names);
+  if (status != 0) {
+    fprintf (stderr, "ERROR: %s\n", lcc_strerror (c));
+    BAIL_OUT (-1);
+  }
+
+  for (i = 0; i < ret_values_num; ++i)
+    printf ("%s=%e\n", ret_values_names[i], ret_values[i]);
+  BAIL_OUT (0);
+#undef BAIL_OUT
+} /* getval */
+
+static int flush (lcc_connection_t *c, int argc, char **argv)
+{
+  int timeout = -1;
+
+  lcc_identifier_t *identifiers = NULL;
+  int identifiers_num = 0;
+
+  char **plugins = NULL;
+  int plugins_num = 0;
+
+  int status;
+  int i;
+
+  assert (strcasecmp (argv[0], "flush") == 0);
+
+#define BAIL_OUT(s) \
+  do { \
+    if (identifiers != NULL) \
+      free (identifiers); \
+    identifiers_num = 0; \
+    if (plugins != NULL) \
+      free (plugins); \
+    plugins_num = 0; \
+    return (s); \
+  } while (0)
+
+  for (i = 1; i < argc; ++i) {
+    char *key, *value;
+
+    key   = argv[i];
+    value = strchr (argv[i], (int)'=');
+
+    if (! value) {
+      fprintf (stderr, "ERROR: flush: Invalid option ``%s''.\n", argv[i]);
+      BAIL_OUT (-1);
+    }
+
+    *value = '\0';
+    ++value;
+
+    if (strcasecmp (key, "timeout") == 0) {
+      char *endptr = NULL;
+
+      timeout = (int) strtol (value, &endptr, 0);
+
+      if (endptr == value) {
+        fprintf (stderr, "ERROR: Failed to parse timeout as number: %s.\n",
+            value);
+        BAIL_OUT (-1);
+      }
+      else if ((endptr != NULL) && (*endptr != '\0')) {
+        fprintf (stderr, "WARNING: Ignoring trailing garbage after timeout: "
+            "%s.\n", endptr);
+      }
+    }
+    else if (strcasecmp (key, "plugin") == 0) {
+      status = array_grow ((void *)&plugins, &plugins_num,
+          sizeof (*plugins));
+      if (status != 0)
+        BAIL_OUT (status);
+
+      plugins[plugins_num - 1] = value;
+    }
+    else if (strcasecmp (key, "identifier") == 0) {
+      status = array_grow ((void *)&identifiers, &identifiers_num,
+          sizeof (*identifiers));
+      if (status != 0)
+        BAIL_OUT (status);
+
+      memset (identifiers + (identifiers_num - 1), 0, sizeof (*identifiers));
+      status = parse_identifier (c, value,
+          identifiers + (identifiers_num - 1));
+      if (status != 0)
+        BAIL_OUT (status);
+    }
+    else {
+      fprintf (stderr, "ERROR: flush: Unknown option `%s'.\n", key);
+      BAIL_OUT (-1);
+    }
+  }
+
+  if (plugins_num == 0) {
+    status = array_grow ((void *)&plugins, &plugins_num, sizeof (*plugins));
+    if (status != 0)
+      BAIL_OUT (status);
+
+    assert (plugins_num == 1);
+    plugins[0] = NULL;
+  }
+
+  for (i = 0; i < plugins_num; ++i) {
+    if (identifiers_num == 0) {
+      status = lcc_flush (c, plugins[i], NULL, timeout);
+      if (status != 0)
+        fprintf (stderr, "ERROR: Failed to flush plugin `%s': %s.\n",
+            (plugins[i] == NULL) ? "(all)" : plugins[i], lcc_strerror (c));
+    }
+    else {
+      int j;
+
+      for (j = 0; j < identifiers_num; ++j) {
+        status = lcc_flush (c, plugins[i], identifiers + j, timeout);
+        if (status != 0) {
+          char id[1024];
+
+          lcc_identifier_to_string (c, id, sizeof (id), identifiers + j);
+          fprintf (stderr, "ERROR: Failed to flush plugin `%s', "
+              "identifier `%s': %s.\n",
+              (plugins[i] == NULL) ? "(all)" : plugins[i],
+              id, lcc_strerror (c));
+        }
+      }
+    }
+  }
+
+  BAIL_OUT (0);
+#undef BAIL_OUT
+} /* flush */
+
+static int listval (lcc_connection_t *c, int argc, char **argv)
+{
+  lcc_identifier_t *ret_ident     = NULL;
+  size_t            ret_ident_num = 0;
+
+  int status;
+  size_t i;
+
+  assert (strcasecmp (argv[0], "listval") == 0);
+
+  if (argc != 1) {
+    fprintf (stderr, "ERROR: listval: Does not accept any arguments.\n");
+    return (-1);
+  }
+
+#define BAIL_OUT(s) \
+  do { \
+    if (ret_ident != NULL) \
+      free (ret_ident); \
+    ret_ident_num = 0; \
+    return (s); \
+  } while (0)
+
+  status = lcc_listval (c, &ret_ident, &ret_ident_num);
+  if (status != 0) {
+    fprintf (stderr, "ERROR: %s\n", lcc_strerror (c));
+    BAIL_OUT (status);
+  }
+
+  for (i = 0; i < ret_ident_num; ++i) {
+    char id[1024];
+
+    status = lcc_identifier_to_string (c, id, sizeof (id), ret_ident + i);
+    if (status != 0) {
+      fprintf (stderr, "ERROR: listval: Failed to convert returned "
+          "identifier to a string: %s\n", lcc_strerror (c));
+      continue;
+    }
+
+    printf ("%s\n", id);
+  }
+  BAIL_OUT (0);
+#undef BAIL_OUT
+} /* listval */
+
+static int putval (lcc_connection_t *c, int argc, char **argv)
+{
+  lcc_value_list_t vl = LCC_VALUE_LIST_INIT;
+
+  /* 64 ought to be enough for anybody ;-) */
+  value_t values[64];
+  int     values_types[64];
+  size_t  values_len = 0;
+
+  int status;
+  int i;
+
+  assert (strcasecmp (argv[0], "putval") == 0);
+
+  if (argc < 3) {
+    fprintf (stderr, "ERROR: putval: Missing identifier "
+        "and/or value list.\n");
+    return (-1);
+  }
+
+  vl.values       = values;
+  vl.values_types = values_types;
+
+  status = parse_identifier (c, argv[1], &vl.identifier);
+  if (status != 0)
+    return (status);
+
+  for (i = 2; i < argc; ++i) {
+    char *tmp;
+
+    tmp = strchr (argv[i], (int)'=');
+
+    if (tmp != NULL) { /* option */
+      char *key   = argv[i];
+      char *value = tmp;
+
+      *value = '\0';
+      ++value;
+
+      if (strcasecmp (key, "interval") == 0) {
+        char *endptr;
+
+        vl.interval = strtol (value, &endptr, 0);
+
+        if (endptr == value) {
+          fprintf (stderr, "ERROR: Failed to parse interval as number: %s.\n",
+              value);
+          return (-1);
+        }
+        else if ((endptr != NULL) && (*endptr != '\0')) {
+          fprintf (stderr, "WARNING: Ignoring trailing garbage after "
+              "interval: %s.\n", endptr);
+        }
+      }
+      else {
+        fprintf (stderr, "ERROR: putval: Unknown option `%s'.\n", key);
+        return (-1);
+      }
+    }
+    else { /* value list */
+      char *value;
+
+      tmp = strchr (argv[i], (int)':');
+
+      if (tmp == NULL) {
+        fprintf (stderr, "ERROR: putval: Invalid value list: %s.\n",
+            argv[i]);
+        return (-1);
+      }
+
+      *tmp = '\0';
+      ++tmp;
+
+      if (strcasecmp (argv[i], "N") == 0) {
+        vl.time = 0;
+      }
+      else {
+        char *endptr;
+
+        vl.time = strtol (argv[i], &endptr, 0);
+
+        if (endptr == argv[i]) {
+          fprintf (stderr, "ERROR: Failed to parse time as number: %s.\n",
+              argv[i]);
+          return (-1);
+        }
+        else if ((endptr != NULL) && (*endptr != '\0')) {
+          fprintf (stderr, "ERROR: Garbage after time: %s.\n", endptr);
+          return (-1);
+        }
+      }
+
+      values_len = 0;
+      value = tmp;
+      while (value != 0) {
+        char *dot, *endptr;
+
+        tmp = strchr (value, (int)':');
+
+        if (tmp != NULL) {
+          *tmp = '\0';
+          ++tmp;
+        }
+
+        /* This is a bit of a hack, but parsing types.db just does not make
+         * much sense imho -- the server might have different types defined
+         * anyway. Also, lcc uses the type information for formatting the
+         * number only, so the real meaning does not matter. -tokkee */
+        dot = strchr (value, (int)'.');
+        endptr = NULL;
+        if (strcasecmp (value, "U") == 0) {
+          values[values_len].gauge = NAN;
+          values_types[values_len] = LCC_TYPE_GAUGE;
+        }
+        else if (dot) { /* floating point value */
+          values[values_len].gauge = strtod (value, &endptr);
+          values_types[values_len] = LCC_TYPE_GAUGE;
+        }
+        else { /* integer */
+          values[values_len].counter = strtol (value, &endptr, 0);
+          values_types[values_len] = LCC_TYPE_COUNTER;
+        }
+        ++values_len;
+
+        if (endptr == value) {
+          fprintf (stderr, "ERROR: Failed to parse value as number: %s.\n",
+              argv[i]);
+          return (-1);
+        }
+        else if ((endptr != NULL) && (*endptr != '\0')) {
+          fprintf (stderr, "ERROR: Garbage after value: %s.\n", endptr);
+          return (-1);
+        }
+
+        value = tmp;
+      }
+
+      assert (values_len >= 1);
+      vl.values_len = values_len;
+
+      status = lcc_putval (c, &vl);
+      if (status != 0) {
+        fprintf (stderr, "ERROR: %s\n", lcc_strerror (c));
+        return (-1);
+      }
+    }
+  }
+
+  if (values_len == 0) {
+    fprintf (stderr, "ERROR: putval: Missing value list(s).\n");
+    return (-1);
+  }
+  return (0);
+} /* putval */
+
+int main (int argc, char **argv) {
+  char address[1024] = "unix:"DEFAULT_SOCK;
+
+  lcc_connection_t *c;
+
+  int status;
+
+  while (42) {
+    int c;
+
+    c = getopt (argc, argv, "s:h");
+
+    if (c == -1)
+      break;
+
+    switch (c) {
+      case 's':
+        snprintf (address, sizeof (address), "unix:%s", optarg);
+        address[sizeof (address) - 1] = '\0';
+        break;
+      case 'h':
+        exit_usage (argv[0], 0);
+        break;
+      default:
+        exit_usage (argv[0], 1);
+    }
+  }
+
+  if (optind >= argc) {
+    fprintf (stderr, "%s: missing command\n", argv[0]);
+    exit_usage (argv[0], 1);
+  }
+
+  c = NULL;
+  status = lcc_connect (address, &c);
+  if (status != 0) {
+    fprintf (stderr, "ERROR: Failed to connect to daemon at %s: %s.\n",
+        address, strerror (errno));
+    return (1);
+  }
+
+  if (strcasecmp (argv[optind], "getval") == 0)
+    status = getval (c, argc - optind, argv + optind);
+  else if (strcasecmp (argv[optind], "flush") == 0)
+    status = flush (c, argc - optind, argv + optind);
+  else if (strcasecmp (argv[optind], "listval") == 0)
+    status = listval (c, argc - optind, argv + optind);
+  else if (strcasecmp (argv[optind], "putval") == 0)
+    status = putval (c, argc - optind, argv + optind);
+  else {
+    fprintf (stderr, "%s: invalid command: %s\n", argv[0], argv[optind]);
+    return (1);
+  }
+
+  LCC_DESTROY (c);
+
+  if (status != 0)
+    return (status);
+  return (0);
+} /* main */
+
+/* vim: set sw=2 ts=2 tw=78 expandtab : */
+
diff --git a/src/collectdctl.pod b/src/collectdctl.pod
new file mode 100644 (file)
index 0000000..52b8ef2
--- /dev/null
@@ -0,0 +1,162 @@
+=encoding UTF-8
+
+=head1 NAME
+
+collectdctl - Control interface for collectd
+
+=head1 SYNOPSIS
+
+collectdctl I<[options]> I<E<lt>commandE<gt>> I<[command options]>
+
+=head1 DESCRIPTION
+
+collectdctl provides a control interface for collectd, which may be used to
+interact with the daemon using the C<unixsock plugin>.
+
+=head1 OPTIONS
+
+collectdctl supports the following options:
+
+=over 4
+
+=item B<-s> I<socket>
+
+Path to the UNIX socket opened by collectd's C<unixsock plugin>.
+Default: /var/run/collectd-unixsock
+
+=item B<-h>
+
+Display usage information and exit.
+
+=back
+
+=head1 AVAILABLE COMMANDS
+
+The following commands are supported:
+
+=over 4
+
+=item B<getval> I<E<lt>identifierE<gt>>
+
+Query the latest collected value identified by the specified
+I<E<lt>identifierE<gt>> (see below). The value-list associated with that
+data-set is returned as a list of key-value-pairs, each on its own line. Keys
+and values are separated by the equal sign (C<=>).
+
+=item B<flush> [B<timeout=>I<E<lt>secondsE<gt>>] [B<plugin=>I<E<lt>nameE<gt>>]
+[B<identifier=>I<E<lt>idE<gt>>]
+
+Flush the daemon. This is useful, e.E<nbsp>g., to make sure that the latest
+values have been written to the respective RRD file before graphing them or
+copying them to somewhere else.
+
+The following options are supported by the flush command:
+
+=over 4
+
+=item B<timeout=>I<E<lt>secondsE<gt>>
+
+Flush values older than the specified timeout (in seconds) only.
+
+=item B<plugin=>I<E<lt>nameE<gt>>
+
+Flush the specified plugin only. I.E<nbsp>e., data cached by the specified
+plugin is written to disk (or network or whatever), if the plugin supports
+that operation.
+
+Example: B<rrdtool>.
+
+=item B<identifier=>I<E<lt>idE<gt>>
+
+If this option is present, only the data specified by the specified identifier
+(see below) will be flushed. Note that this option is not supported by all
+plugins (e.E<nbsp>g., the C<network> plugin does not support this).
+
+=back
+
+The B<plugin> and B<identifier> options may be specified more than once. In
+that case, all combinations of specified plugins and identifiers will be
+flushed only.
+
+=item B<listval>
+
+Returns a list of all values (by their identifier) available to the
+C<unixsock> plugin. Each value is printed on its own line. I.E<nbsp>e., this
+command returns a list of valid identifiers that may be used with the other
+commands.
+
+=item B<putval> I<E<lt>identifierE<gt>> [B<interval=>I<E<lt>secondsE<gt>>]
+I<E<lt>value-list(s)E<gt>>
+
+Submit one or more values (identified by I<E<lt>identifierE<gt>>, see below)
+to the daemon which will then dispatch them to the write plugins. B<interval>
+specifies the interval (in seconds) used to collect the values following that
+option. It defaults to the default of the running collectd instance receiving
+the data. Multiple I<E<lt>value-list(s)E<gt>> (see below) may be specified.
+Each of them will be submitted to the daemon. The values have to match the
+data-set definition specified by the type as given in the identifier (see
+L<types.db(5)> for details).
+
+=back
+
+=head1 IDENTIFIERS
+
+An identifier has the following format:
+
+[I<hostname>/]I<plugin>[-I<plugin_instance>]/I<type>[-I<type_instance>]
+
+Examples:
+ somehost/cpu-0/cpu-idle
+ uptime/uptime
+ otherhost/memory/memory-used
+
+Hostname defaults to the local (non-fully qualified) hostname if omitted. No
+error is returned if the specified identifier does not exist (this is a
+limitation in the C<libcollectdclient> library).
+
+=head1 VALUE-LIST
+
+A value list describes one data-set as handled by collectd. It is a colon
+(C<:>) separated list of the time and the values. Each value is either given
+as an integer if the data-type is a counter, or as a double if the data-type
+is a gauge value. A literal C<U> is interpreted as an undefined gauge value.
+The number of values and the data-types have to match the type specified in
+the identifier (see L<types.db(5)> for details). The time is specified as
+epoch (i.E<nbsp>e., standard UNIX time) or as a literal C<N> which will be
+interpreted as now.
+
+=head1 EXAMPLES
+
+=over 4
+
+=item C<collectdctl flush plugin=rrdtool identifier=somehost/cpu-0/cpu-wait>
+
+Flushes all CPU wait RRD values of the first CPU of the local host.
+I.E<nbsp>e., writes all pending RRD updates of that data-source to disk.
+
+=item C<for ident in `collectdctl listval | grep users/users`; do
+      collectdctl getval $ident;
+  done>
+
+Query the latest number of logged in users on all hosts known to the local
+collectd instance.
+
+=back
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>,
+L<collectd-unixsock(5)>,
+L<types.db(5)>
+
+=head1 AUTHOR
+
+collectd has been written by Florian Forster E<lt>octo at verplant.orgE<gt>
+and many contributors (see `AUTHORS').
+
+collectdctl has been written by
+Håkon J Dugstad Johnsen E<lt>hakon-dugstad.johnsenE<nbsp>atE<nbsp>telenor.comE<gt>
+and Sebastian Harl E<lt>sh at tokkee.orgE<gt>.
+
+=cut
index 73ba6b8..3151e11 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 collectdmon - Monitoring daemon for collectd
index 35e006a..b771f62 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/common.c
- * Copyright (C) 2005-2009  Florian octo Forster
+ * Copyright (C) 2005-2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -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>
  *   Niki W. Waibel <niki.waibel@gmx.net>
  *   Sebastian Harl <sh at tokkee.org>
  *   Michał Mirosław <mirq-linux at rere.qmqm.pl>
@@ -29,6 +29,7 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
+#include "utils_cache.h"
 
 #if HAVE_PTHREAD_H
 # include <pthread.h>
 # include <math.h>
 #endif
 
-/* for ntohl and htonl */
-#if HAVE_ARPA_INET_H
-# include <arpa/inet.h>
-#endif
-
 /* for getaddrinfo */
 #include <sys/types.h>
 #include <sys/socket.h>
 # include <netinet/in.h>
 #endif
 
+/* for ntohl and htonl */
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
 #ifdef HAVE_LIBKSTAT
 extern kstat_ctl_t *kc;
 #endif
@@ -360,6 +361,22 @@ int strunescape (char *buf, size_t buf_len)
        return (0);
 } /* int strunescape */
 
+size_t strstripnewline (char *buffer)
+{
+       size_t buffer_len = strlen (buffer);
+
+       while (buffer_len > 0)
+       {
+               if ((buffer[buffer_len - 1] != '\n')
+                               && (buffer[buffer_len - 1] != '\r'))
+                       break;
+               buffer_len--;
+               buffer[buffer_len] = 0;
+       }
+
+       return (buffer_len);
+} /* size_t strstripnewline */
+
 int escape_slashes (char *buf, int buf_len)
 {
        int i;
@@ -663,7 +680,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 */
@@ -770,38 +787,114 @@ int format_name (char *ret, int ret_len,
                const char *plugin, const char *plugin_instance,
                const char *type, const char *type_instance)
 {
-       int  status;
-
-       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);
-       }
+  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);
+
+  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, /* {{{ */
+               const data_set_t *ds, const value_list_t *vl,
+               _Bool store_rates)
+{
+        size_t offset = 0;
+        int status;
+        int i;
+        gauge_t *rates = NULL;
+
+        assert (0 == strcmp (ds->type, vl->type));
+
+        memset (ret, 0, ret_len);
+
+#define BUFFER_ADD(...) do { \
+        status = ssnprintf (ret + offset, ret_len - offset, \
+                        __VA_ARGS__); \
+        if (status < 1) \
+        { \
+                sfree (rates); \
+                return (-1); \
+        } \
+        else if (((size_t) status) >= (ret_len - offset)) \
+        { \
+                sfree (rates); \
+                return (-1); \
+        } \
+        else \
+                offset += ((size_t) status); \
+} while (0)
+
+        BUFFER_ADD ("%.3f", CDTIME_T_TO_DOUBLE (vl->time));
+
+        for (i = 0; i < ds->ds_num; i++)
+        {
+                if (ds->ds[i].type == DS_TYPE_GAUGE)
+                        BUFFER_ADD (":%f", vl->values[i].gauge);
+                else if (store_rates)
+                {
+                        if (rates == NULL)
+                                rates = uc_get_rate (ds, vl);
+                        if (rates == NULL)
+                        {
+                                WARNING ("format_values: "
+                                               "uc_get_rate failed.");
+                                return (-1);
+                        }
+                        BUFFER_ADD (":%g", rates[i]);
+                }
+                else if (ds->ds[i].type == DS_TYPE_COUNTER)
+                        BUFFER_ADD (":%llu", vl->values[i].counter);
+                else if (ds->ds[i].type == DS_TYPE_DERIVE)
+                        BUFFER_ADD (":%"PRIi64, vl->values[i].derive);
+                else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+                        BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
+                else
+                {
+                        ERROR ("format_values plugin: Unknown data source type: %i",
+                                        ds->ds[i].type);
+                        sfree (rates);
+                        return (-1);
+                }
+        } /* for ds->ds_num */
+
+#undef BUFFER_ADD
+
+        sfree (rates);
+        return (0);
+} /* }}} int format_values */
+
 int parse_identifier (char *str, char **ret_host,
                char **ret_plugin, char **ret_plugin_instance,
                char **ret_type, char **ret_type_instance)
@@ -848,6 +941,40 @@ int parse_identifier (char *str, char **ret_host,
        return (0);
 } /* int parse_identifier */
 
+int parse_identifier_vl (const char *str, value_list_t *vl) /* {{{ */
+{
+       char str_copy[6 * DATA_MAX_NAME_LEN];
+       char *host = NULL;
+       char *plugin = NULL;
+       char *plugin_instance = NULL;
+       char *type = NULL;
+       char *type_instance = NULL;
+       int status;
+
+       if ((str == NULL) || (vl == NULL))
+               return (EINVAL);
+
+       sstrncpy (str_copy, str, sizeof (str_copy));
+
+       status = parse_identifier (str_copy, &host,
+                       &plugin, &plugin_instance,
+                       &type, &type_instance);
+       if (status != 0)
+               return (status);
+
+       sstrncpy (vl->host, host, sizeof (vl->host));
+       sstrncpy (vl->plugin, plugin, sizeof (vl->plugin));
+       sstrncpy (vl->plugin_instance,
+                       (plugin_instance != NULL) ? plugin_instance : "",
+                       sizeof (vl->plugin_instance));
+       sstrncpy (vl->type, type, sizeof (vl->type));
+       sstrncpy (vl->type_instance,
+                       (type_instance != NULL) ? type_instance : "",
+                       sizeof (vl->type_instance));
+
+       return (0);
+} /* }}} int parse_identifier_vl */
+
 int parse_value (const char *value_orig, value_t *ret_value, int ds_type)
 {
   char *value;
@@ -893,9 +1020,9 @@ int parse_value (const char *value_orig, value_t *ret_value, int ds_type)
   }
 
   if (value == endptr) {
-    sfree (value);
     ERROR ("parse_value: Failed to parse string as %s: %s.",
         DS_TYPE_TO_STRING (ds_type), value);
+    sfree (value);
     return -1;
   }
   else if ((NULL != endptr) && ('\0' != *endptr))
@@ -931,9 +1058,22 @@ int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
                if (i == -1)
                {
                        if (strcmp ("N", ptr) == 0)
-                               vl->time = time (NULL);
+                               vl->time = cdtime ();
                        else
-                               vl->time = (time_t) atoi (ptr);
+                       {
+                               char *endptr = NULL;
+                               double tmp;
+
+                               errno = 0;
+                               tmp = strtod (ptr, &endptr);
+                               if ((errno != 0)                    /* Overflow */
+                                               || (endptr == ptr)  /* Invalid string */
+                                               || (endptr == NULL) /* This should not happen */
+                                               || (*endptr != 0))  /* Trailing chars */
+                                       return (-1);
+
+                               vl->time = DOUBLE_TO_CDTIME_T (tmp);
+                       }
                }
                else
                {
@@ -1112,7 +1252,102 @@ counter_t counter_diff (counter_t old_value, counter_t new_value)
        }
 
        return (diff);
-} /* counter_t counter_to_gauge */
+} /* counter_t counter_diff */
+
+int rate_to_value (value_t *ret_value, gauge_t rate, /* {{{ */
+               rate_to_value_state_t *state,
+               int ds_type, cdtime_t t)
+{
+       gauge_t delta_gauge;
+       cdtime_t delta_t;
+
+       if (ds_type == DS_TYPE_GAUGE)
+       {
+               state->last_value.gauge = rate;
+               state->last_time = t;
+
+               *ret_value = state->last_value;
+               return (0);
+       }
+
+       /* Counter and absolute can't handle negative rates. Reset "last time"
+        * to zero, so that the next valid rate will re-initialize the
+        * structure. */
+       if ((rate < 0.0)
+                       && ((ds_type == DS_TYPE_COUNTER)
+                               || (ds_type == DS_TYPE_ABSOLUTE)))
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       /* Another invalid state: The time is not increasing. */
+       if (t <= state->last_time)
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       delta_t = t - state->last_time;
+       delta_gauge = (rate * CDTIME_T_TO_DOUBLE (delta_t)) + state->residual;
+
+       /* Previous value is invalid. */
+       if (state->last_time == 0) /* {{{ */
+       {
+               if (ds_type == DS_TYPE_DERIVE)
+               {
+                       state->last_value.derive = (derive_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.derive);
+               }
+               else if (ds_type == DS_TYPE_COUNTER)
+               {
+                       state->last_value.counter = (counter_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.counter);
+               }
+               else if (ds_type == DS_TYPE_ABSOLUTE)
+               {
+                       state->last_value.absolute = (absolute_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.absolute);
+               }
+               else
+               {
+                       assert (23 == 42);
+               }
+
+               state->last_time = t;
+               return (EAGAIN);
+       } /* }}} */
+
+       if (ds_type == DS_TYPE_DERIVE)
+       {
+               derive_t delta_derive = (derive_t) delta_gauge;
+
+               state->last_value.derive += delta_derive;
+               state->residual = delta_gauge - ((gauge_t) delta_derive);
+       }
+       else if (ds_type == DS_TYPE_COUNTER)
+       {
+               counter_t delta_counter = (counter_t) delta_gauge;
+
+               state->last_value.counter += delta_counter;
+               state->residual = delta_gauge - ((gauge_t) delta_counter);
+       }
+       else if (ds_type == DS_TYPE_ABSOLUTE)
+       {
+               absolute_t delta_absolute = (absolute_t) delta_gauge;
+
+               state->last_value.absolute = delta_absolute;
+               state->residual = delta_gauge - ((gauge_t) delta_absolute);
+       }
+       else
+       {
+               assert (23 == 42);
+       }
+
+        state->last_time = t;
+       *ret_value = state->last_value;
+       return (0);
+} /* }}} value_t rate_to_value */
 
 int service_name_to_port_number (const char *service_name)
 {
@@ -1184,3 +1419,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 c292abf..ae8e311 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/common.h
- * Copyright (C) 2005-2009  Florian octo Forster
+ * Copyright (C) 2005-2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -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>
  *   Niki W. Waibel <niki.waibel@gmx.net>
 **/
 
                || (strcasecmp ("no", (s)) == 0) \
                || (strcasecmp ("off", (s)) == 0))
 
+struct rate_to_value_state_s
+{
+  value_t last_value;
+  cdtime_t last_time;
+  gauge_t residual;
+};
+typedef struct rate_to_value_state_s rate_to_value_state_t;
+
 char *sstrncpy (char *dest, const char *src, size_t n);
 int ssnprintf (char *dest, size_t n, const char *format, ...);
 char *sstrdup(const char *s);
@@ -200,6 +208,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
@@ -258,10 +273,14 @@ int format_name (char *ret, int ret_len,
 #define FORMAT_VL(ret, ret_len, vl) \
        format_name (ret, ret_len, (vl)->host, (vl)->plugin, (vl)->plugin_instance, \
                        (vl)->type, (vl)->type_instance)
+int format_values (char *ret, size_t ret_len,
+               const data_set_t *ds, const value_list_t *vl,
+               _Bool store_rates);
 
 int parse_identifier (char *str, char **ret_host,
                char **ret_plugin, char **ret_plugin_instance,
                char **ret_type, char **ret_type_instance);
+int parse_identifier_vl (const char *str, value_list_t *vl);
 int parse_value (const char *value, value_t *ret_value, int ds_type);
 int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds);
 
@@ -274,10 +293,10 @@ int notification_init (notification_t *n, int severity, const char *message,
                const char *host,
                const char *plugin, const char *plugin_instance,
                const char *type, const char *type_instance);
-#define NOTIFICATION_INIT_VL(n, vl, ds) \
+#define NOTIFICATION_INIT_VL(n, vl) \
        notification_init (n, NOTIF_FAILURE, NULL, \
                        (vl)->host, (vl)->plugin, (vl)->plugin_instance, \
-                       (ds)->type, (vl)->type_instance)
+                       (vl)->type, (vl)->type_instance)
 
 typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename,
                void *user_data);
@@ -288,6 +307,15 @@ int read_file_contents (const char *filename, char *buf, int bufsize);
 
 counter_t counter_diff (counter_t old_value, counter_t new_value);
 
+/* Convert a rate back to a value_t. When converting to a derive_t, counter_t
+ * or absoltue_t, take fractional residuals into account. This is important
+ * when scaling counters, for example.
+ * Returns zero on success. Returns EAGAIN when called for the first time; in
+ * this case the value_t is invalid and the next call should succeed. Other
+ * return values indicate an error. */
+int rate_to_value (value_t *ret_value, gauge_t rate,
+               rate_to_value_state_t *state, int ds_type, cdtime_t t);
+
 /* Converts a service name (a string) to a port number
  * (in the range [1-65535]). Returns less than zero on error. */
 int service_name_to_port_number (const char *service_name);
@@ -296,4 +324,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 983d995..7e06baa 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/configfile.c
- * Copyright (C) 2005-2009  Florian octo Forster
+ * Copyright (C) 2005-2011  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
@@ -17,7 +17,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Sebastian tokkee Harl <sh at tokkee.org>
  **/
 
 #include "plugin.h"
 #include "configfile.h"
 #include "types_list.h"
-#include "utils_threshold.h"
 #include "filter_chain.h"
 
 #if HAVE_WORDEXP_H
 # 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))
 
 /*
@@ -47,6 +54,7 @@ typedef struct cf_callback
        int  (*callback) (const char *, const char *);
        const char **keys;
        int    keys_num;
+       plugin_ctx_t ctx;
        struct cf_callback *next;
 } cf_callback_t;
 
@@ -54,6 +62,7 @@ typedef struct cf_complex_callback_s
 {
        char *type;
        int (*callback) (oconfig_item_t *);
+       plugin_ctx_t ctx;
        struct cf_complex_callback_s *next;
 } cf_complex_callback_t;
 
@@ -89,21 +98,22 @@ 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[] =
 {
        {"BaseDir",     NULL, PKGLOCALSTATEDIR},
        {"PIDFile",     NULL, PIDFILE},
        {"Hostname",    NULL, NULL},
-       {"FQDNLookup",  NULL, "false"},
-       {"Interval",    NULL, "10"},
+       {"FQDNLookup",  NULL, "true"},
+       {"Interval",    NULL, NULL},
        {"ReadThreads", NULL, "5"},
+       {"WriteThreads", NULL, "5"},
        {"Timeout",     NULL, "2"},
        {"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;
 
@@ -129,6 +139,7 @@ static int cf_dispatch (const char *type, const char *orig_key,
                const char *orig_value)
 {
        cf_callback_t *cf_cb;
+       plugin_ctx_t old_ctx;
        char *key;
        char *value;
        int ret;
@@ -157,6 +168,8 @@ static int cf_dispatch (const char *type, const char *orig_key,
 
        ret = -1;
 
+       old_ctx = plugin_set_ctx (cf_cb->ctx);
+
        for (i = 0; i < cf_cb->keys_num; i++)
        {
                if ((cf_cb->keys[i] != NULL)
@@ -167,6 +180,8 @@ static int cf_dispatch (const char *type, const char *orig_key,
                }
        }
 
+       plugin_set_ctx (old_ctx);
+
        if (i >= cf_cb->keys_num)
                WARNING ("Plugin `%s' did not register for value `%s'.", type, key);
 
@@ -243,7 +258,12 @@ static int dispatch_value_plugindir (const oconfig_item_t *ci)
 static int dispatch_loadplugin (const oconfig_item_t *ci)
 {
        int i;
-       uint32_t flags = 0;
+       const char *name;
+       unsigned int flags = 0;
+       plugin_ctx_t ctx;
+       plugin_ctx_t old_ctx;
+       int ret_val;
+
        assert (strcasecmp (ci->key, "LoadPlugin") == 0);
 
        if (ci->values_num != 1)
@@ -251,19 +271,53 @@ static int dispatch_loadplugin (const oconfig_item_t *ci)
        if (ci->values[0].type != OCONFIG_TYPE_STRING)
                return (-1);
 
+       name = ci->values[0].value.string;
+
+       /* default to the global interval set before loading this plugin */
+       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 (ci->children[i].values_num != 1 ||
-                               ci->children[i].values[0].type != OCONFIG_TYPE_BOOLEAN) {
-                       WARNING("Ignoring unknown LoadPlugin option %s for plugin %s", ci->children[i].key, ci->values[0].value.string);
-                       continue;
+               if (strcasecmp("Globals", ci->children[i].key) == 0)
+                       cf_util_get_flag (ci->children + i, &flags, PLUGIN_FLAGS_GLOBAL);
+               else if (strcasecmp ("Interval", ci->children[i].key) == 0) {
+                       double interval = 0.0;
+
+                       if (cf_util_get_double (ci->children + i, &interval) != 0) {
+                               /* cf_util_get_double will log an error */
+                               continue;
+                       }
+
+                       ctx.interval = DOUBLE_TO_CDTIME_T (interval);
                }
-               if (strcasecmp(ci->children[i].key, "globals") == 0) {
-                       flags |= PLUGIN_FLAGS_GLOBAL;
-               } else {
-                       WARNING("Ignoring unknown LoadPlugin option %s for plugin %s", ci->children[i].key, ci->values[0].value.string);
+               else {
+                       WARNING("Ignoring unknown LoadPlugin option \"%s\" "
+                                       "for plugin \"%s\"",
+                                       ci->children[i].key, ci->values[0].value.string);
                }
        }
-       return (plugin_load (ci->values[0].value.string, flags));
+
+       old_ctx = plugin_set_ctx (ctx);
+       ret_val = plugin_load (name, (uint32_t) flags);
+       /* reset to the "global" context */
+       plugin_set_ctx (old_ctx);
+
+       return (ret_val);
 } /* int dispatch_value_loadplugin */
 
 static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci)
@@ -342,8 +396,18 @@ static int dispatch_block_plugin (oconfig_item_t *ci)
 
        /* Check for a complex callback first */
        for (cb = complex_callback_head; cb != NULL; cb = cb->next)
+       {
                if (strcasecmp (name, cb->type) == 0)
-                       return (cb->callback (ci));
+               {
+                       plugin_ctx_t old_ctx;
+                       int ret_val;
+
+                       old_ctx = plugin_set_ctx (cb->ctx);
+                       ret_val = (cb->callback (ci));
+                       plugin_set_ctx (old_ctx);
+                       return (ret_val);
+               }
+       }
 
        /* Hm, no complex plugin found. Dispatch the values one by one */
        for (i = 0; i < ci->children_num; i++)
@@ -372,8 +436,6 @@ static int dispatch_block (oconfig_item_t *ci)
                return (dispatch_loadplugin (ci));
        else if (strcasecmp (ci->key, "Plugin") == 0)
                return (dispatch_block_plugin (ci));
-       else if (strcasecmp (ci->key, "Threshold") == 0)
-               return (ut_config (ci));
        else if (strcasecmp (ci->key, "Chain") == 0)
                return (fc_configure (ci));
 
@@ -488,7 +550,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)
 {
@@ -499,9 +562,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;
@@ -515,9 +578,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'. */
                if (cf_ci_replace_child (root, new, i) < 0)
@@ -533,12 +609,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)
        {
@@ -546,7 +645,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 */
@@ -556,7 +660,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;
@@ -631,7 +736,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. */
@@ -662,7 +767,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;
@@ -715,9 +821,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 "
@@ -737,18 +843,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;
@@ -771,9 +872,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);
@@ -822,6 +923,29 @@ const char *global_option_get (const char *option)
                        : cf_global_options[i].def);
 } /* char *global_option_get */
 
+cdtime_t cf_get_default_interval (void)
+{
+  char const *str = global_option_get ("Interval");
+  double interval_double = COLLECTD_DEFAULT_INTERVAL;
+
+  if (str != NULL)
+  {
+    char *endptr = NULL;
+    double tmp = strtod (str, &endptr);
+
+    if ((endptr == NULL) || (endptr == str) || (*endptr != 0))
+      ERROR ("cf_get_default_interval: Unable to parse string \"%s\" "
+          "as number.", str);
+    else if (tmp <= 0.0)
+      ERROR ("cf_get_default_interval: Interval must be a positive number. "
+          "The current number is %g.", tmp);
+    else
+      interval_double = tmp;
+  }
+
+  return (DOUBLE_TO_CDTIME_T (interval_double));
+} /* }}} cdtime_t cf_get_default_interval */
+
 void cf_unregister (const char *type)
 {
        cf_callback_t *this, *prev;
@@ -878,6 +1002,7 @@ void cf_register (const char *type,
        cf_cb->callback = callback;
        cf_cb->keys     = keys;
        cf_cb->keys_num = keys_num;
+       cf_cb->ctx      = plugin_get_ctx ();
 
        cf_cb->next = first_callback;
        first_callback = cf_cb;
@@ -901,6 +1026,8 @@ int cf_register_complex (const char *type, int (*callback) (oconfig_item_t *))
        new->callback = callback;
        new->next = NULL;
 
+       new->ctx = plugin_get_ctx ();
+
        if (complex_callback_head == NULL)
        {
                complex_callback_head = new;
@@ -921,12 +1048,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++)
        {
@@ -1009,6 +1142,23 @@ int cf_util_get_int (const oconfig_item_t *ci, int *ret_value) /* {{{ */
        return (0);
 } /* }}} int cf_util_get_int */
 
+int cf_util_get_double (const oconfig_item_t *ci, double *ret_value) /* {{{ */
+{
+       if ((ci == NULL) || (ret_value == NULL))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+       {
+               ERROR ("cf_util_get_double: The %s option requires "
+                               "exactly one numeric argument.", ci->key);
+               return (-1);
+       }
+
+       *ret_value = ci->values[0].value.number;
+
+       return (0);
+} /* }}} int cf_util_get_double */
+
 int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
 {
        if ((ci == NULL) || (ret_bool == NULL))
@@ -1021,22 +1171,142 @@ int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
                return (-1);
        }
 
-       *ret_bool = ci->values[0].value.boolean ? true : false;
+       *ret_bool = ci->values[0].value.boolean ? 1 : 0;
 
        return (0);
 } /* }}} int cf_util_get_boolean */
 
-/* Assures that the config option is a string. The string is then converted to
- * a port number using `service_name_to_port_number' and returned. Returns the
- * port number in the range [1-65535] or less than zero upon failure. */
+int cf_util_get_flag (const oconfig_item_t *ci, /* {{{ */
+               unsigned int *ret_value, unsigned int flag)
+{
+       int status;
+       _Bool b;
+
+       if (ret_value == NULL)
+               return (EINVAL);
+
+       b = 0;
+       status = cf_util_get_boolean (ci, &b);
+       if (status != 0)
+               return (status);
+
+       if (b)
+       {
+               *ret_value |= flag;
+       }
+       else
+       {
+               *ret_value &= ~flag;
+       }
+
+       return (0);
+} /* }}} int cf_util_get_flag */
+
+/* Assures that the config option is a string or a number if the correct range
+ * of 1-65535. The string is then converted to a port number using
+ * `service_name_to_port_number' and returned.
+ * Returns the port number in the range [1-65535] or less than zero upon
+ * failure. */
 int cf_util_get_port_number (const oconfig_item_t *ci) /* {{{ */
 {
-       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       int tmp;
+
+       if ((ci->values_num != 1)
+                       || ((ci->values[0].type != OCONFIG_TYPE_STRING)
+                               && (ci->values[0].type != OCONFIG_TYPE_NUMBER)))
        {
-               ERROR ("cf_util_get_port_number: The %s option requires "
+               ERROR ("cf_util_get_port_number: The \"%s\" option requires "
                                "exactly one string argument.", ci->key);
                return (-1);
        }
 
-       return (service_name_to_port_number (ci->values[0].value.string));
+       if (ci->values[0].type == OCONFIG_TYPE_STRING)
+               return (service_name_to_port_number (ci->values[0].value.string));
+
+       assert (ci->values[0].type == OCONFIG_TYPE_NUMBER);
+       tmp = (int) (ci->values[0].value.number + 0.5);
+       if ((tmp < 1) || (tmp > 65535))
+       {
+               ERROR ("cf_util_get_port_number: The \"%s\" option requires "
+                               "a service name or a port number. The number "
+                               "you specified, %i, is not in the valid "
+                               "range of 1-65535.",
+                               ci->key, tmp);
+               return (-1);
+       }
+
+       return (tmp);
 } /* }}} int cf_util_get_port_number */
+
+int cf_util_get_service (const oconfig_item_t *ci, char **ret_string) /* {{{ */
+{
+       int port;
+       char *service;
+       int status;
+
+       if (ci->values_num != 1)
+       {
+               ERROR ("cf_util_get_service: The %s option requires exactly "
+                               "one argument.", ci->key);
+               return (-1);
+       }
+
+       if (ci->values[0].type == OCONFIG_TYPE_STRING)
+               return (cf_util_get_string (ci, ret_string));
+       if (ci->values[0].type != OCONFIG_TYPE_NUMBER)
+       {
+               ERROR ("cf_util_get_service: The %s option requires "
+                               "exactly one string or numeric argument.",
+                               ci->key);
+       }
+
+       port = 0;
+       status = cf_util_get_int (ci, &port);
+       if (status != 0)
+               return (status);
+       else if ((port < 1) || (port > 65535))
+       {
+               ERROR ("cf_util_get_service: The port number given "
+                               "for the %s option is out of "
+                               "range (%i).", ci->key, port);
+               return (-1);
+       }
+
+       service = malloc (6);
+       if (service == NULL)
+       {
+               ERROR ("cf_util_get_service: Out of memory.");
+               return (-1);
+       }
+       ssnprintf (service, 6, "%i", port);
+
+       sfree (*ret_string);
+       *ret_string = service;
+
+       return (0);
+} /* }}} int cf_util_get_service */
+
+int cf_util_get_cdtime (const oconfig_item_t *ci, cdtime_t *ret_value) /* {{{ */
+{
+       if ((ci == NULL) || (ret_value == NULL))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+       {
+               ERROR ("cf_util_get_cdtime: The %s option requires "
+                               "exactly one numeric argument.", ci->key);
+               return (-1);
+       }
+
+       if (ci->values[0].value.number < 0.0)
+       {
+               ERROR ("cf_util_get_cdtime: The numeric argument of the %s "
+                               "option must not be negative.", ci->key);
+               return (-1);
+       }
+
+       *ret_value = DOUBLE_TO_CDTIME_T (ci->values[0].value.number);
+
+       return (0);
+} /* }}} int cf_util_get_cdtime */
+
index 432e09f..5a719a4 100644 (file)
@@ -2,7 +2,7 @@
 #define CONFIGFILE_H
 /**
  * collectd - src/configfile.h
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2011  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
+#include "utils_time.h"
 #include "liboconfig/oconfig.h"
 
 /*
@@ -86,6 +87,8 @@ int cf_read (char *filename);
 int global_option_set (const char *option, const char *value);
 const char *global_option_get (const char *option);
 
+cdtime_t cf_get_default_interval (void);
+
 /* Assures the config option is a string, duplicates it and returns the copy in
  * "ret_string". If necessary "*ret_string" is freed first. Returns zero upon
  * success. */
@@ -99,13 +102,31 @@ int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer,
 /* Assures the config option is a number and returns it as an int. */
 int cf_util_get_int (const oconfig_item_t *ci, int *ret_value);
 
+/* Assures the config option is a number and returns it as a double. */
+int cf_util_get_double (const oconfig_item_t *ci, double *ret_value);
+
 /* Assures the config option is a boolean and assignes it to `ret_bool'.
  * Otherwise, `ret_bool' is not changed and non-zero is returned. */
 int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool);
 
-/* Assures that the config option is a string. The string is then converted to
- * a port number using `service_name_to_port_number' and returned. Returns the
- * port number in the range [1-65535] or less than zero upon failure. */
+/* Assures the config option is a boolean and set or unset the given flag in
+ * `ret_value' as appropriate. Returns non-zero on error. */
+int cf_util_get_flag (const oconfig_item_t *ci,
+               unsigned int *ret_value, unsigned int flag);
+
+/* Assures that the config option is a string or a number if the correct range
+ * of 1-65535. The string is then converted to a port number using
+ * `service_name_to_port_number' and returned.
+ * Returns the port number in the range [1-65535] or less than zero upon
+ * failure. */
 int cf_util_get_port_number (const oconfig_item_t *ci);
 
+/* Assures that the config option is either a service name (a string) or a port
+ * number (an integer in the appropriate range) and returns a newly allocated
+ * string. If ret_string points to a non-NULL pointer, it is freed before
+ * assigning a new value. */
+int cf_util_get_service (const oconfig_item_t *ci, char **ret_string);
+
+int cf_util_get_cdtime (const oconfig_item_t *ci, cdtime_t *ret_value);
+
 #endif /* defined(CONFIGFILE_H) */
index 06055ca..344f76e 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/contextswitch.c
  * Copyright (C) 2009  Patrik Weiskircher
+ * Copyright (C) 2010  Kimo Rosenbaum
  *
  * 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:
  *   Patrik Weiskircher <weiskircher at inqnet.at>
+ *   Kimo Rosenbaum <http://github.com/kimor79>
  **/
 
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 
-#if !KERNEL_LINUX
+#ifdef HAVE_SYS_SYSCTL_H
+# include <sys/sysctl.h>
+#endif
+
+#if HAVE_SYSCTLBYNAME
+/* no global variables */
+/* #endif HAVE_SYSCTLBYNAME */
+
+#elif KERNEL_LINUX
+/* no global variables */
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_PERFSTAT
+# include <sys/protosw.h>
+# include <libperfstat.h>
+/* #endif HAVE_PERFSTAT */
+
+#else
 # error "No applicable input method."
 #endif
 
@@ -45,6 +64,25 @@ static void cs_submit (derive_t context_switches)
 
 static int cs_read (void)
 {
+#if HAVE_SYSCTLBYNAME
+       int value = 0;
+       size_t value_len = sizeof (value);
+       int status;
+
+       status = sysctlbyname ("vm.stats.sys.v_swtch",
+                       &value, &value_len,
+                       /* new pointer = */ NULL, /* new length = */ 0);
+       if (status != 0)
+       {
+               ERROR("contextswitch plugin: sysctlbyname "
+                               "(vm.stats.sys.v_swtch) failed");
+               return (-1);
+       }
+
+       cs_submit (value);
+/* #endif HAVE_SYSCTLBYNAME */
+
+#elif KERNEL_LINUX
        FILE *fh;
        char buffer[64];
        int numfields;
@@ -88,6 +126,24 @@ static int cs_read (void)
 
        if (status == -2)
                ERROR ("contextswitch plugin: Unable to find context switch value.");
+/* #endif  KERNEL_LINUX */
+
+#elif HAVE_PERFSTAT
+       int status = 0;
+       perfstat_cpu_total_t perfcputotal;
+
+       status = perfstat_cpu_total(NULL, &perfcputotal, sizeof(perfstat_cpu_total_t), 1);
+       if (status < 0)
+       {
+               char errbuf[1024];
+               ERROR ("contextswitch plugin: perfstat_cpu_total: %s",
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       cs_submit(perfcputotal.pswitch);
+       status = 0;
+#endif /* defined(HAVE_PERFSTAT) */
 
        return status;
 }
index 8581590..1c4e5f6 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -1,6 +1,6 @@
 /**
  * collectd - src/cpu.c
- * Copyright (C) 2005-2009  Florian octo Forster
+ * Copyright (C) 2005-2010  Florian octo Forster
  * Copyright (C) 2008       Oleg King
  * Copyright (C) 2009       Simon Kuhnle
  * Copyright (C) 2009       Manuel Sanmartin
 static mach_port_t port_host;
 static processor_port_array_t cpu_list;
 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;
-#endif /* PROCESSOR_TEMPERATURE */
 /* #endif PROCESSOR_CPU_LOAD_INFO */
 
 #elif defined(KERNEL_LINUX)
@@ -147,7 +141,7 @@ static int pnumcpu;
 
 static int init (void)
 {
-#if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE
+#if PROCESSOR_CPU_LOAD_INFO
        kern_return_t status;
 
        port_host = mach_host_self ();
@@ -163,7 +157,7 @@ static int init (void)
        DEBUG ("host_processors returned %i %s", (int) cpu_list_len, cpu_list_len == 1 ? "processor" : "processors");
        INFO ("cpu plugin: Found %i processor%s.", (int) cpu_list_len, cpu_list_len == 1 ? "" : "s");
 
-       cpu_temp_retry_max = 86400 / interval_g;
+       cpu_temp_retry_max = 86400 / CDTIME_T_TO_TIME_T (plugin_get_interval ());
 /* #endif PROCESSOR_CPU_LOAD_INFO */
 
 #elif defined(HAVE_LIBKSTAT)
@@ -241,12 +235,12 @@ static int init (void)
        return (0);
 } /* int init */
 
-static void submit (int cpu_num, const char *type_instance, counter_t value)
+static void submit (int cpu_num, const char *type_instance, derive_t value)
 {
        value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = value;
+       values[0].derive = value;
 
        vl.values = values;
        vl.values_len = 1;
@@ -262,96 +256,47 @@ static void submit (int cpu_num, const char *type_instance, counter_t value)
 
 static int cpu_read (void)
 {
-#if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE
+#if PROCESSOR_CPU_LOAD_INFO
        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;
-#endif
-#if PROCESSOR_TEMPERATURE
-       processor_info_data_t          cpu_temp;
-       mach_msg_type_number_t         cpu_temp_len;
-#endif
 
        host_t cpu_host;
 
        for (cpu = 0; cpu < cpu_list_len; cpu++)
        {
-#if PROCESSOR_CPU_LOAD_INFO
                cpu_host = 0;
                cpu_info_len = PROCESSOR_BASIC_INFO_COUNT;
 
-               if ((status = processor_info (cpu_list[cpu],
-                                               PROCESSOR_CPU_LOAD_INFO, &cpu_host,
-                                               (processor_info_t) &cpu_info, &cpu_info_len)) != KERN_SUCCESS)
-               {
-                       ERROR ("cpu plugin: processor_info failed with status %i", (int) status);
-                       continue;
-               }
-
-               if (cpu_info_len < CPU_STATE_MAX)
-               {
-                       ERROR ("cpu plugin: processor_info returned only %i elements..", cpu_info_len);
-                       continue;
-               }
-
-               submit (cpu, "user", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]);
-               submit (cpu, "nice", (counter_t) cpu_info.cpu_ticks[CPU_STATE_NICE]);
-               submit (cpu, "system", (counter_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM]);
-               submit (cpu, "idle", (counter_t) cpu_info.cpu_ticks[CPU_STATE_IDLE]);
-#endif /* PROCESSOR_CPU_LOAD_INFO */
-#if PROCESSOR_TEMPERATURE
-               /*
-                * Not all Apple computers do have this ability. To minimize
-                * the messages sent to the syslog we do an exponential
-                * stepback if `processor_info' fails. We still try ~once a day
-                * though..
-                */
-               if (cpu_temp_retry_counter > 0)
-               {
-                       cpu_temp_retry_counter--;
-                       continue;
-               }
-
-               cpu_temp_len = PROCESSOR_INFO_MAX;
-
-               status = processor_info (cpu_list[cpu],
-                               PROCESSOR_TEMPERATURE,
-                               &cpu_host,
-                               cpu_temp, &cpu_temp_len);
+               status = processor_info (cpu_list[cpu], PROCESSOR_CPU_LOAD_INFO, &cpu_host,
+                               (processor_info_t) &cpu_info, &cpu_info_len);
                if (status != KERN_SUCCESS)
                {
-                       ERROR ("cpu plugin: processor_info failed: %s",
+                       ERROR ("cpu plugin: processor_info (PROCESSOR_CPU_LOAD_INFO) failed: %s",
                                        mach_error_string (status));
-
-                       cpu_temp_retry_counter = cpu_temp_retry_step;
-                       cpu_temp_retry_step *= 2;
-                       if (cpu_temp_retry_step > cpu_temp_retry_max)
-                               cpu_temp_retry_step = cpu_temp_retry_max;
-
                        continue;
                }
 
-               if (cpu_temp_len != 1)
+               if (cpu_info_len < CPU_STATE_MAX)
                {
-                       DEBUG ("processor_info (PROCESSOR_TEMPERATURE) returned %i elements..?",
-                                       (int) cpu_temp_len);
+                       ERROR ("cpu plugin: processor_info returned only %i elements..", cpu_info_len);
                        continue;
                }
 
-               cpu_temp_retry_counter = 0;
-               cpu_temp_retry_step    = 1;
-#endif /* PROCESSOR_TEMPERATURE */
+               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]);
        }
 /* #endif PROCESSOR_CPU_LOAD_INFO */
 
 #elif defined(KERNEL_LINUX)
        int cpu;
-       counter_t user, nice, syst, idle;
-       counter_t wait, intr, sitr; /* sitr == soft interrupt */
+       derive_t user, nice, syst, idle;
+       derive_t wait, intr, sitr; /* sitr == soft interrupt */
        FILE *fh;
        char buf[1024];
 
@@ -408,7 +353,7 @@ static int cpu_read (void)
 
 #elif defined(HAVE_LIBKSTAT)
        int cpu;
-       counter_t user, syst, idle, wait;
+       derive_t user, syst, idle, wait;
        static cpu_stat_t cs;
 
        if (kc == NULL)
@@ -419,10 +364,10 @@ static int cpu_read (void)
                if (kstat_read (kc, ksp[cpu], &cs) == -1)
                        continue; /* error message? */
 
-               idle = (counter_t) cs.cpu_sysinfo.cpu[CPU_IDLE];
-               user = (counter_t) cs.cpu_sysinfo.cpu[CPU_USER];
-               syst = (counter_t) cs.cpu_sysinfo.cpu[CPU_KERNEL];
-               wait = (counter_t) cs.cpu_sysinfo.cpu[CPU_WAIT];
+               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);
@@ -549,12 +494,12 @@ static int cpu_read (void)
                return (-1);
        }
 
-       submit (0, "idle",   (counter_t) cs->idle);
-       submit (0, "nice",   (counter_t) cs->nice);
-       submit (0, "swap",   (counter_t) cs->swap);
-       submit (0, "system", (counter_t) cs->kernel);
-       submit (0, "user",   (counter_t) cs->user);
-       submit (0, "wait",   (counter_t) cs->iowait);
+       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);
 /* #endif HAVE_LIBSTATGRAB */
 
 #elif defined(HAVE_PERFSTAT)
@@ -589,10 +534,10 @@ static int cpu_read (void)
 
        for (i = 0; i < cpus; i++) 
        {
-               submit (i, "idle",   (counter_t) perfcpu[i].idle);
-               submit (i, "system", (counter_t) perfcpu[i].sys);
-               submit (i, "user",   (counter_t) perfcpu[i].user);
-               submit (i, "wait",   (counter_t) perfcpu[i].wait);
+               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);
        }
 #endif /* HAVE_PERFSTAT */
 
index 46e2301..4b8aa72 100644 (file)
@@ -188,7 +188,7 @@ typedef struct {
        PluginData data;
        PyObject *values;    /* Sequence */
        PyObject *meta;      /* dict */
-       int interval;
+       double interval;
 } Values;
 PyTypeObject ValuesType;
 #define Values_New() PyObject_CallFunctionObjArgs((PyObject *) &ValuesType, (void *) 0)
index f149edc..958e34b 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -53,7 +53,8 @@ static int value_list_to_string (char *buffer, int buffer_len,
 
        memset (buffer, '\0', buffer_len);
 
-       status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
+       status = ssnprintf (buffer, buffer_len, "%.3f",
+                       CDTIME_T_TO_DOUBLE (vl->time));
        if ((status < 1) || (status >= buffer_len))
                return (-1);
        offset = status;
@@ -120,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);
@@ -217,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;
@@ -274,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);
@@ -298,8 +300,10 @@ static int csv_write (const data_set_t *ds, const value_list_t *vl,
                }
 
                fprintf (use_stdio == 1 ? stdout : stderr,
-                        "PUTVAL %s interval=%i %s\n",
-                        filename, vl->interval, values);
+                        "PUTVAL %s interval=%.3f %s\n",
+                        filename,
+                        CDTIME_T_TO_DOUBLE (vl->interval),
+                        values);
                return (0);
        }
 
index 8d2893f..0d4677f 100644 (file)
@@ -57,10 +57,12 @@ struct web_page_s /* {{{ */
   char *user;
   char *pass;
   char *credentials;
-  int   verify_peer;
-  int   verify_host;
+  _Bool verify_peer;
+  _Bool verify_host;
   char *cacert;
-  int   response_time;
+  struct curl_slist *headers;
+  char *post_body;
+  _Bool response_time;
 
   CURL *curl;
   char curl_errbuf[CURL_ERROR_SIZE];
@@ -148,6 +150,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 +160,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 +169,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 +280,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);
@@ -405,6 +394,10 @@ static int cc_page_init_curl (web_page_t *wp) /* {{{ */
       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 */
@@ -450,22 +443,26 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("URL", child->key) == 0)
-      status = cc_config_add_string ("URL", &page->url, child);
+      status = cf_util_get_string (child, &page->url);
     else if (strcasecmp ("User", child->key) == 0)
-      status = cc_config_add_string ("User", &page->user, child);
+      status = cf_util_get_string (child, &page->user);
     else if (strcasecmp ("Password", child->key) == 0)
-      status = cc_config_add_string ("Password", &page->pass, child);
+      status = cf_util_get_string (child, &page->pass);
     else if (strcasecmp ("VerifyPeer", child->key) == 0)
-      status = cc_config_set_boolean ("VerifyPeer", &page->verify_peer, child);
+      status = cf_util_get_boolean (child, &page->verify_peer);
     else if (strcasecmp ("VerifyHost", child->key) == 0)
-      status = cc_config_set_boolean ("VerifyHost", &page->verify_host, child);
+      status = cf_util_get_boolean (child, &page->verify_host);
     else if (strcasecmp ("MeasureResponseTime", child->key) == 0)
-      status = cc_config_set_boolean (child->key, &page->response_time, child);
+      status = cf_util_get_boolean (child, &page->response_time);
     else if (strcasecmp ("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);
@@ -580,12 +577,12 @@ static void cc_submit (const web_page_t *wp, const web_match_t *wm, /* {{{ */
 
   vl.values = values;
   vl.values_len = 1;
-  vl.time = time (NULL);
   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, 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 */
@@ -599,7 +596,6 @@ static void cc_submit_response_time (const web_page_t *wp, double seconds) /* {{
 
   vl.values = values;
   vl.values_len = 1;
-  vl.time = time (NULL);
   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));
@@ -654,6 +650,7 @@ static int cc_read_page (web_page_t *wp) /* {{{ */
     }
 
     cc_submit (wp, wm, mv);
+    match_value_reset (mv);
   } /* for (wm = wp->matches; wm != NULL; wm = wm->next) */
 
   return (0);
index 0948962..a4afa02 100644 (file)
@@ -40,7 +40,7 @@
 
 #define CJ_DEFAULT_HOST "localhost"
 #define CJ_KEY_MAGIC 0x43484b59UL /* CHKY */
-#define CJ_IS_KEY(key) (key)->magic == CJ_KEY_MAGIC
+#define CJ_IS_KEY(key) ((key)->magic == CJ_KEY_MAGIC)
 #define CJ_ANY "*"
 #define COUCH_MIN(x,y) ((x) < (y) ? (x) : (y))
 
@@ -48,10 +48,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;
 };
 /* }}} */
 
@@ -64,9 +64,11 @@ struct cj_s /* {{{ */
   char *user;
   char *pass;
   char *credentials;
-  int   verify_peer;
-  int   verify_host;
+  _Bool verify_peer;
+  _Bool verify_host;
   char *cacert;
+  struct curl_slist *headers;
+  char *post_body;
 
   CURL *curl;
   char curl_errbuf[CURL_ERROR_SIZE];
@@ -122,7 +124,8 @@ static size_t cj_curl_callback (void *buf, /* {{{ */
   if (status != yajl_status_ok)
   {
     unsigned char *msg =
-      yajl_get_error(db->yajl, 1, (unsigned char *)buf, len);
+      yajl_get_error(db->yajl, /* verbose = */ 1,
+          /* jsonText = */ (unsigned char *) buf, (unsigned int) len);
     ERROR ("curl_json plugin: yajl_parse failed: %s", msg);
     yajl_free_error(db->yajl, msg);
     return (0); /* abort write callback */
@@ -177,41 +180,21 @@ static int cj_cb_number (void *ctx,
 
   cj_t *db = (cj_t *)ctx;
   cj_key_t *key = db->state[db->depth].key;
-  char *endptr;
   value_t vt;
   int type;
+  int status;
 
-  if (key == NULL)
+  if ((key == NULL) || !CJ_IS_KEY (key))
     return (CJ_CB_CONTINUE);
 
   memcpy (buffer, number, number_len);
   buffer[sizeof (buffer) - 1] = 0;
 
   type = cj_get_type (key);
-  if (type < 0)
-    return (CJ_CB_CONTINUE);
-
-  endptr = NULL;
-  errno = 0;
-
-  if (type == DS_TYPE_COUNTER)
-    vt.counter = (counter_t) strtoull (buffer, &endptr, /* base = */ 0);
-  else if (type == DS_TYPE_GAUGE)
-    vt.gauge = (gauge_t) strtod (buffer, &endptr);
-  else if (type == DS_TYPE_DERIVE)
-    vt.derive = (derive_t) strtoll (buffer, &endptr, /* base = */ 0);
-  else if (type == DS_TYPE_ABSOLUTE)
-    vt.absolute = (absolute_t) strtoull (buffer, &endptr, /* base = */ 0);
-  else
+  status = parse_value (buffer, &vt, type);
+  if (status != 0)
   {
-    ERROR ("curl_json plugin: Unknown data source type: \"%s\"", key->type);
-    return (CJ_CB_ABORT);
-  }
-
-  if ((endptr == &buffer[0]) || (errno != 0))
-  {
-    NOTICE ("curl_json plugin: Overflow while parsing number. "
-        "Ignoring this value.");
+    NOTICE ("curl_json plugin: Unable to parse number: \"%s\"", buffer);
     return (CJ_CB_CONTINUE);
   }
 
@@ -251,34 +234,26 @@ static int cj_cb_string (void *ctx, const unsigned char *val,
     yajl_len_t len)
 {
   cj_t *db = (cj_t *)ctx;
-  c_avl_tree_t *tree;
-  char *ptr;
+  char str[len + 1];
 
-  if (db->depth != 1) /* e.g. _all_dbs */
-    return (CJ_CB_CONTINUE);
+  /* Create a null-terminated version of the string. */
+  memcpy (str, val, len);
+  str[len] = 0;
 
-  cj_cb_map_key (ctx, val, len); /* same logic */
-
-  tree = db->state[db->depth].tree;
+  /* No configuration for this string -> simply return. */
+  if (db->state[db->depth].key == NULL)
+    return (CJ_CB_CONTINUE);
 
-  if ((tree != NULL) && (ptr = rindex (db->url, '/')))
+  if (!CJ_IS_KEY (db->state[db->depth].key))
   {
-    char url[PATH_MAX];
-    CURL *curl;
-
-    /* url =~ s,[^/]+$,$name, */
-    len = (ptr - db->url) + 1;
-    ptr = url;
-    sstrncpy (ptr, db->url, sizeof (url));
-    sstrncpy (ptr + len, db->state[db->depth].name, sizeof (url) - len);
-
-    curl = curl_easy_duphandle (db->curl);
-    curl_easy_setopt (curl, CURLOPT_URL, url);
-    cj_curl_perform (db, curl);
-    curl_easy_cleanup (curl);
+    NOTICE ("curl_json plugin: Found string \"%s\", but the configuration "
+        "expects a map here.", str);
+    return (CJ_CB_CONTINUE);
   }
-  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 */
 
 static int cj_cb_start (void *ctx)
 {
@@ -394,14 +369,21 @@ static void cj_free (void *arg) /* {{{ */
   sfree (db->pass);
   sfree (db->credentials);
   sfree (db->cacert);
+  sfree (db->post_body);
+  curl_slist_free_all (db->headers);
 
   sfree (db);
 } /* }}} void cj_free */
 
 /* Configuration handling functions {{{ */
 
-static int cj_config_add_string (const char *name, char **dest, /* {{{ */
-                                      oconfig_item_t *ci)
+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))
   {
@@ -409,32 +391,12 @@ static int cj_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 cj_config_add_string */
-
-static int cj_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_json plugin: `%s' needs exactly one boolean argument.", name);
-    return (-1);
-  }
-
-  *dest = ci->values[0].value.boolean ? 1 : 0;
-
-  return (0);
-} /* }}} int cj_config_set_boolean */
-
-static c_avl_tree_t *cj_avl_create(void)
-{
-  return c_avl_create ((int (*) (const void *, const void *)) strcmp);
-}
+} /* }}} int cj_config_append_string */
 
 static int cj_config_add_key (cj_t *db, /* {{{ */
                                    oconfig_item_t *ci)
@@ -462,7 +424,7 @@ static int cj_config_add_key (cj_t *db, /* {{{ */
 
   if (strcasecmp ("Key", ci->key) == 0)
   {
-    status = cj_config_add_string ("Key", &key->path, ci);
+    status = cf_util_get_string (ci, &key->path);
     if (status != 0)
     {
       sfree (key);
@@ -482,9 +444,9 @@ static int cj_config_add_key (cj_t *db, /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("Type", child->key) == 0)
-      status = cj_config_add_string ("Type", &key->type, child);
+      status = cf_util_get_string (child, &key->type);
     else if (strcasecmp ("Instance", child->key) == 0)
-      status = cj_config_add_string ("Instance", &key->instance, child);
+      status = cf_util_get_string (child, &key->instance);
     else
     {
       WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
@@ -538,6 +500,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)
@@ -605,6 +568,10 @@ static int cj_init_curl (cj_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 cj_init_curl */
@@ -633,7 +600,7 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
 
   if (strcasecmp ("URL", ci->key) == 0)
   {
-    status = cj_config_add_string ("URL", &db->url, ci);
+    status = cf_util_get_string (ci, &db->url);
     if (status != 0)
     {
       sfree (db);
@@ -653,19 +620,23 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("Instance", child->key) == 0)
-      status = cj_config_add_string ("Instance", &db->instance, child);
+      status = cf_util_get_string (child, &db->instance);
     else if (strcasecmp ("Host", child->key) == 0)
-      status = cj_config_add_string ("Host", &db->host, child);
+      status = cf_util_get_string (child, &db->host);
     else if (strcasecmp ("User", child->key) == 0)
-      status = cj_config_add_string ("User", &db->user, child);
+      status = cf_util_get_string (child, &db->user);
     else if (strcasecmp ("Password", child->key) == 0)
-      status = cj_config_add_string ("Password", &db->pass, child);
+      status = cf_util_get_string (child, &db->pass);
     else if (strcasecmp ("VerifyPeer", child->key) == 0)
-      status = cj_config_set_boolean ("VerifyPeer", &db->verify_peer, child);
+      status = cf_util_get_boolean (child, &db->verify_peer);
     else if (strcasecmp ("VerifyHost", child->key) == 0)
-      status = cj_config_set_boolean ("VerifyHost", &db->verify_host, child);
+      status = cf_util_get_boolean (child, &db->verify_host);
     else if (strcasecmp ("CACert", child->key) == 0)
-      status = cj_config_add_string ("CACert", &db->cacert, child);
+      status = cf_util_get_string (child, &db->cacert);
+    else if (strcasecmp ("Header", child->key) == 0)
+      status = cj_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 ("Key", child->key) == 0)
       status = cj_config_add_key (db, child);
     else
@@ -817,48 +788,52 @@ 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 != CURLE_OK)
   {
     ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
-           status, db->curl_errbuf, url);
+           status, db->curl_errbuf, (url != NULL) ? url : "<null>");
     yajl_free (db->yajl);
     db->yajl = yprev;
     return (-1);
   }
 
-  curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
   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))
   {
-    ERROR ("curl_json plugin: curl_easy_perform failed with response code %ld (%s)",
-           rc, url);
+    ERROR ("curl_json plugin: curl_easy_perform failed with "
+        "response code %ld (%s)", rc, url);
     yajl_free (db->yajl);
     db->yajl = yprev;
     return (-1);
   }
 
 #if HAVE_YAJL_V2
-  status = yajl_complete_parse(db->yajl);
+    status = yajl_complete_parse(db->yajl);
 #else
-  status = yajl_parse_complete(db->yajl);
+    status = yajl_parse_complete(db->yajl);
 #endif
   if (status != yajl_status_ok)
   {
-    ERROR ("curl_json plugin: %s failed with status %i.",
-#if HAVE_YAJL_V2
-        "yajl_complete_parse",
-#else
-        "yajl_parse_complete",
-#endif
-        status);
+    unsigned char *errmsg;
+
+    errmsg = yajl_get_error (db->yajl, /* verbose = */ 0,
+        /* jsonText = */ NULL, /* jsonTextLen = */ 0);
+    ERROR ("curl_json plugin: yajl_parse_complete failed: %s",
+        (char *) errmsg);
+    yajl_free_error (db->yajl, errmsg);
+    yajl_free (db->yajl);
+    db->yajl = yprev;
+    return (-1);
   }
 
   yajl_free (db->yajl);
   db->yajl = yprev;
-
   return (0);
 } /* }}} int cj_curl_perform */
 
index 28b2ded..7ca9831 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;
@@ -70,6 +79,11 @@ struct cx_s /* {{{ */
   _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 +174,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 +199,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 +265,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 +383,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)
   {
@@ -520,6 +561,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 +579,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);
@@ -727,6 +785,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) /* {{{ */
 {
@@ -770,6 +868,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 */
@@ -833,6 +935,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);
index eef97db..80488d8 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;
 
@@ -159,7 +165,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);
 
@@ -200,10 +207,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);
   }
 
@@ -217,6 +225,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)
@@ -225,12 +234,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++;
@@ -280,6 +298,8 @@ static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
     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);
@@ -373,7 +393,7 @@ static int cdbi_config (oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
     if (strcasecmp ("Query", child->key) == 0)
       udb_query_create (&queries, &queries_num, child,
-          /* callback = */ NULL, /* legacy mode = */ 0);
+          /* callback = */ NULL);
     else if (strcasecmp ("Database", child->key) == 0)
       cdbi_config_add_database (child);
     else
@@ -537,9 +557,9 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
     sstrncpy (column_names[i], column_name, DATA_MAX_NAME_LEN);
   } /* }}} for (i = 0; i < column_num; i++) */
 
-  udb_query_prepare_result (q, 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 = */ -1);
+      column_names, column_num, /* interval = */ 0);
 
   /* 0 = error; 1 = success; */
   status = dbi_result_first_row (res); /* {{{ */
@@ -661,24 +681,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);
index dc7be1e..ded374b 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -62,9 +62,8 @@ static ignorelist_t *il_device = NULL;
 static ignorelist_t *il_mountpoint = NULL;
 static ignorelist_t *il_fstype = NULL;
 
-static _Bool by_device = false;
-static _Bool report_reserved = false;
-static _Bool report_inodes = false;
+static _Bool by_device = 0;
+static _Bool report_inodes = 0;
 
 static int df_init (void)
 {
@@ -119,25 +118,16 @@ static int df_config (const char *key, const char *value)
        else if (strcasecmp (key, "ReportByDevice") == 0)
        {
                if (IS_TRUE (value))
-                       by_device = true;
-
-               return (0);
-       }
-       else if (strcasecmp (key, "ReportReserved") == 0)
-       {
-               if (IS_TRUE (value))
-                       report_reserved = true;
-               else
-                       report_reserved = false;
+                       by_device = 1;
 
                return (0);
        }
        else if (strcasecmp (key, "ReportInodes") == 0)
        {
                if (IS_TRUE (value))
-                       report_inodes = true;
+                       report_inodes = 1;
                else
-                       report_inodes = false;
+                       report_inodes = 0;
 
                return (0);
        }
@@ -146,28 +136,6 @@ static int df_config (const char *key, const char *value)
        return (-1);
 }
 
-static void df_submit_two (char *df_name,
-               const char *type,
-               gauge_t df_used,
-               gauge_t df_free)
-{
-       value_t values[2];
-       value_list_t vl = VALUE_LIST_INIT;
-
-       values[0].gauge = df_used;
-       values[1].gauge = df_free;
-
-       vl.values = values;
-       vl.values_len = 2;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "df", sizeof (vl.plugin));
-       sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-       sstrncpy (vl.type_instance, df_name, sizeof (vl.type_instance));
-
-       plugin_dispatch_values (&vl);
-} /* void df_submit_two */
-
 __attribute__ ((nonnull(2)))
 static void df_submit_one (char *plugin_instance,
                const char *type, const char *type_instance,
@@ -215,6 +183,9 @@ static int df_read (void)
        {
                unsigned long long blocksize;
                char disk_name[256];
+               uint64_t blk_free;
+               uint64_t blk_reserved;
+               uint64_t blk_used;
 
                if (ignorelist_match (il_device,
                                        (mnt_ptr->spec_device != NULL)
@@ -276,56 +247,39 @@ static int df_read (void)
 
                blocksize = BLOCKSIZE(statbuf);
 
-               if (report_reserved)
-               {
-                       uint64_t blk_free;
-                       uint64_t blk_reserved;
-                       uint64_t blk_used;
-
-                       /*
-                        * Sanity-check for the values in the struct
-                        */
-                       /* Check for negative "available" byes. For example UFS can
-                        * report negative free space for user. Notice. blk_reserved
-                        * will start to diminish after this. */
+               /*
+                * Sanity-check for the values in the struct
+                */
+               /* Check for negative "available" byes. For example UFS can
+                * report negative free space for user. Notice. blk_reserved
+                * will start to diminish after this. */
 #if HAVE_STATVFS
-                       /* Cast and temporary variable are needed to avoid
-                        * compiler warnings.
-                        * ((struct statvfs).f_bavail is unsigned (POSIX)) */
-                       int64_t signed_bavail = (int64_t) statbuf.f_bavail;
-                       if (signed_bavail < 0)
-                               statbuf.f_bavail = 0;
+               /* Cast and temporary variable are needed to avoid
+                * compiler warnings.
+                * ((struct statvfs).f_bavail is unsigned (POSIX)) */
+               int64_t signed_bavail = (int64_t) statbuf.f_bavail;
+               if (signed_bavail < 0)
+                       statbuf.f_bavail = 0;
 #elif HAVE_STATFS
-                       if (statbuf.f_bavail < 0)
-                               statbuf.f_bavail = 0;
+               if (statbuf.f_bavail < 0)
+                       statbuf.f_bavail = 0;
 #endif
-                       /* Make sure that f_blocks >= f_bfree >= f_bavail */
-                       if (statbuf.f_bfree < statbuf.f_bavail)
-                               statbuf.f_bfree = statbuf.f_bavail;
-                       if (statbuf.f_blocks < statbuf.f_bfree)
-                               statbuf.f_blocks = statbuf.f_bfree;
-
-                       blk_free = (uint64_t) statbuf.f_bavail;
-                       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",
-                                       (gauge_t) (blk_free * blocksize));
-                       df_submit_one (disk_name, "df_complex", "reserved",
-                                       (gauge_t) (blk_reserved * blocksize));
-                       df_submit_one (disk_name, "df_complex", "used",
-                                       (gauge_t) (blk_used * blocksize));
-               }
-               else /* compatibility code */
-               {
-                       gauge_t df_free;
-                       gauge_t df_used;
-
-                       df_free = statbuf.f_bfree * blocksize;
-                       df_used = (statbuf.f_blocks - statbuf.f_bfree) * blocksize;
-
-                       df_submit_two (disk_name, "df", df_used, df_free);
-               }
+               /* Make sure that f_blocks >= f_bfree >= f_bavail */
+               if (statbuf.f_bfree < statbuf.f_bavail)
+                       statbuf.f_bfree = statbuf.f_bavail;
+               if (statbuf.f_blocks < statbuf.f_bfree)
+                       statbuf.f_blocks = statbuf.f_bfree;
+
+               blk_free     = (uint64_t) statbuf.f_bavail;
+               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",
+                               (gauge_t) (blk_free * blocksize));
+               df_submit_one (disk_name, "df_complex", "reserved",
+                               (gauge_t) (blk_reserved * blocksize));
+               df_submit_one (disk_name, "df_complex", "used",
+                               (gauge_t) (blk_used * blocksize));
 
                /* inode handling */
                if (report_inodes)
index 4a78f1b..36d0a0c 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/disk.c
- * Copyright (C) 2005-2008  Florian octo Forster
+ * Copyright (C) 2005-2012  Florian octo Forster
  * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -17,7 +17,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Manuel Sanmartin
  **/
 
@@ -75,6 +75,9 @@
 
 #if HAVE_IOKIT_IOKITLIB_H
 static mach_port_t io_master_port = MACH_PORT_NULL;
+/* This defaults to false for backwards compatibility. Please fix in the next
+ * major version. */
+static _Bool use_bsd_name = 0;
 /* #endif HAVE_IOKIT_IOKITLIB_H */
 
 #elif KERNEL_LINUX
@@ -85,19 +88,19 @@ typedef struct diskstats
        /* This overflows in roughly 1361 years */
        unsigned int poll_count;
 
-       counter_t read_sectors;
-       counter_t write_sectors;
+       derive_t read_sectors;
+       derive_t write_sectors;
 
-       counter_t read_bytes;
-       counter_t write_bytes;
+       derive_t read_bytes;
+       derive_t write_bytes;
 
-       counter_t read_ops;
-       counter_t write_ops;
-       counter_t read_time;
-       counter_t write_time;
+       derive_t read_ops;
+       derive_t write_ops;
+       derive_t read_time;
+       derive_t write_time;
 
-       counter_t avg_read_time;
-       counter_t avg_write_time;
+       derive_t avg_read_time;
+       derive_t avg_write_time;
 
        struct diskstats *next;
 } diskstats_t;
@@ -106,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,6 +131,7 @@ static int pnumdisk;
 static const char *config_keys[] =
 {
        "Disk",
+       "UseBSDName",
        "IgnoreSelected"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
@@ -152,6 +156,15 @@ static int disk_config (const char *key, const char *value)
       invert = 0;
     ignorelist_set_invert (ignorelist, invert);
   }
+  else if (strcasecmp ("UseBSDName", key) == 0)
+  {
+#if HAVE_IOKIT_IOKITLIB_H
+    use_bsd_name = IS_TRUE (value) ? 1 : 0;
+#else
+    WARNING ("disk plugin: The \"UseBSDName\" option is only supported "
+        "on Mach / Mac OS X and will be ignored.");
+#endif
+  }
   else
   {
     return (-1);
@@ -212,7 +225,7 @@ static int disk_init (void)
 
 static void disk_submit (const char *plugin_instance,
                const char *type,
-               counter_t read, counter_t write)
+               derive_t read, derive_t write)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
@@ -221,8 +234,8 @@ static void disk_submit (const char *plugin_instance,
        if (ignorelist_match (ignorelist, plugin_instance) != 0)
          return;
 
-       values[0].counter = read;
-       values[1].counter = write;
+       values[0].derive = read;
+       values[1].derive = write;
 
        vl.values = values;
        vl.values_len = 2;
@@ -238,8 +251,9 @@ static void disk_submit (const char *plugin_instance,
 #if KERNEL_LINUX
 static counter_t disk_calc_time_incr (counter_t delta_time, counter_t delta_ops)
 {
+       double interval = CDTIME_T_TO_DOUBLE (plugin_get_interval ());
        double avg_time = ((double) delta_time) / ((double) delta_ops);
-       double avg_time_incr = ((double) interval_g) * avg_time;
+       double avg_time_incr = interval * avg_time;
 
        return ((counter_t) (avg_time_incr + .5));
 }
@@ -291,7 +305,8 @@ static int disk_read (void)
        CFDictionaryRef         props_dict;
        CFDictionaryRef         stats_dict;
        CFDictionaryRef         child_dict;
-       kern_return_t           status;
+       CFStringRef             tmp_cf_string_ref;
+       kern_return_t           status;
 
        signed long long read_ops;
        signed long long read_byt;
@@ -302,7 +317,8 @@ static int disk_read (void)
 
        int  disk_major;
        int  disk_minor;
-       char disk_name[64];
+       char disk_name[DATA_MAX_NAME_LEN];
+       char disk_name_bsd[DATA_MAX_NAME_LEN];
 
        /* Get the list of all disk objects. */
        if (IOServiceGetMatchingServices (io_master_port,
@@ -350,12 +366,41 @@ static int disk_read (void)
                        continue;
                }
 
+               /* tmp_cf_string_ref doesn't need to be released. */
+               tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (props_dict,
+                               CFSTR(kIOBSDNameKey));
+               if (!tmp_cf_string_ref)
+               {
+                       DEBUG ("disk plugin: CFDictionaryGetValue("
+                                       "kIOBSDNameKey) failed.");
+                       CFRelease (props_dict);
+                       IOObjectRelease (disk_child);
+                       IOObjectRelease (disk);
+                       continue;
+               }
+               assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ());
+
+               memset (disk_name_bsd, 0, sizeof (disk_name_bsd));
+               CFStringGetCString (tmp_cf_string_ref,
+                               disk_name_bsd, sizeof (disk_name_bsd),
+                               kCFStringEncodingUTF8);
+               if (disk_name_bsd[0] == 0)
+               {
+                       ERROR ("disk plugin: CFStringGetCString() failed.");
+                       CFRelease (props_dict);
+                       IOObjectRelease (disk_child);
+                       IOObjectRelease (disk);
+                       continue;
+               }
+               DEBUG ("disk plugin: disk_name_bsd = \"%s\"", disk_name_bsd);
+
                stats_dict = (CFDictionaryRef) CFDictionaryGetValue (props_dict,
                                CFSTR (kIOBlockStorageDriverStatisticsKey));
 
                if (stats_dict == NULL)
                {
-                       DEBUG ("CFDictionaryGetValue (%s) failed.",
+                       DEBUG ("disk plugin: CFDictionaryGetValue ("
+                                       "%s) failed.",
                                        kIOBlockStorageDriverStatisticsKey);
                        CFRelease (props_dict);
                        IOObjectRelease (disk_child);
@@ -369,7 +414,8 @@ static int disk_read (void)
                                        kNilOptions)
                                != kIOReturnSuccess)
                {
-                       DEBUG ("IORegistryEntryCreateCFProperties (disk_child) failed.");
+                       DEBUG ("disk plugin: IORegistryEntryCreateCFProperties ("
+                                       "disk_child) failed.");
                        IOObjectRelease (disk_child);
                        CFRelease (props_dict);
                        IOObjectRelease (disk);
@@ -399,17 +445,12 @@ static int disk_read (void)
                write_tme = dict_get_value (stats_dict,
                                kIOBlockStorageDriverStatisticsTotalWriteTimeKey);
 
-               if (ssnprintf (disk_name, sizeof (disk_name),
-                               "%i-%i", disk_major, disk_minor) >= sizeof (disk_name))
-               {
-                       DEBUG ("snprintf (major, minor) failed.");
-                       CFRelease (child_dict);
-                       IOObjectRelease (disk_child);
-                       CFRelease (props_dict);
-                       IOObjectRelease (disk);
-                       continue;
-               }
-               DEBUG ("disk_name = %s", disk_name);
+               if (use_bsd_name)
+                       sstrncpy (disk_name, disk_name_bsd, sizeof (disk_name));
+               else
+                       ssnprintf (disk_name, sizeof (disk_name), "%i-%i",
+                                       disk_major, disk_minor);
+               DEBUG ("disk plugin: disk_name = \"%s\"", disk_name);
 
                if ((read_byt != -1LL) || (write_byt != -1LL))
                        disk_submit (disk_name, "disk_octets", read_byt, write_byt);
@@ -438,15 +479,15 @@ static int disk_read (void)
 
        int minor = 0;
 
-       counter_t read_sectors  = 0;
-       counter_t write_sectors = 0;
+       derive_t read_sectors  = 0;
+       derive_t write_sectors = 0;
 
-       counter_t read_ops      = 0;
-       counter_t read_merged   = 0;
-       counter_t read_time     = 0;
-       counter_t write_ops     = 0;
-       counter_t write_merged  = 0;
-       counter_t write_time    = 0;
+       derive_t read_ops      = 0;
+       derive_t read_merged   = 0;
+       derive_t read_time     = 0;
+       derive_t write_ops     = 0;
+       derive_t write_merged  = 0;
+       derive_t write_time    = 0;
        int is_disk = 0;
 
        diskstats_t *ds, *pre_ds;
@@ -531,8 +572,8 @@ static int disk_read (void)
                }
 
                {
-                       counter_t diff_read_sectors;
-                       counter_t diff_write_sectors;
+                       derive_t diff_read_sectors;
+                       derive_t diff_write_sectors;
 
                /* If the counter wraps around, it's only 32 bits.. */
                        if (read_sectors < ds->read_sectors)
@@ -555,18 +596,18 @@ static int disk_read (void)
                /* Calculate the average time an io-op needs to complete */
                if (is_disk)
                {
-                       counter_t diff_read_ops;
-                       counter_t diff_write_ops;
-                       counter_t diff_read_time;
-                       counter_t diff_write_time;
+                       derive_t diff_read_ops;
+                       derive_t diff_write_ops;
+                       derive_t diff_read_time;
+                       derive_t diff_write_time;
 
                        if (read_ops < ds->read_ops)
                                diff_read_ops = 1 + read_ops
                                        + (UINT_MAX - ds->read_ops);
                        else
                                diff_read_ops = read_ops - ds->read_ops;
-                       DEBUG ("disk plugin: disk_name = %s; read_ops = %llu; "
-                                       "ds->read_ops = %llu; diff_read_ops = %llu;",
+                       DEBUG ("disk plugin: disk_name = %s; read_ops = %"PRIi64"; "
+                                       "ds->read_ops = %"PRIi64"; diff_read_ops = %"PRIi64";",
                                        disk_name,
                                        read_ops, ds->read_ops, diff_read_ops);
 
@@ -706,12 +747,12 @@ static int disk_read (void)
 /* #endif defined(HAVE_LIBSTATGRAB) */
 
 #elif defined(HAVE_PERFSTAT)
-       counter_t read_sectors;
-       counter_t write_sectors;
-       counter_t read_time;
-       counter_t write_time;
-       counter_t read_ops;
-       counter_t write_ops;
+       derive_t read_sectors;
+       derive_t write_sectors;
+       derive_t read_time;
+       derive_t write_time;
+       derive_t read_ops;
+       derive_t write_ops;
        perfstat_id_t firstpath;
        int rnumdisk;
        int i;
index 364b958..00fcff1 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -33,9 +33,7 @@
 #include <poll.h>
 
 #include <pcap.h>
-#if HAVE_PCAP_BPF_H
-# include <pcap-bpf.h>
-#endif
+#include <pcap-bpf.h>
 
 /*
  * Private data types
@@ -63,8 +61,8 @@ static int select_numeric_qtype = 1;
 #define PCAP_SNAPLEN 1460
 static char   *pcap_device = NULL;
 
-static counter_t       tr_queries;
-static counter_t       tr_responses;
+static derive_t       tr_queries;
+static derive_t       tr_responses;
 static counter_list_t *qtype_list;
 static counter_list_t *opcode_list;
 static counter_list_t *rcode_list;
@@ -84,14 +82,10 @@ static counter_list_t *counter_list_search (counter_list_t **list, unsigned int
 {
        counter_list_t *entry;
 
-       DEBUG ("counter_list_search (list = %p, key = %u)",
-                       (void *) *list, key);
-
        for (entry = *list; entry != NULL; entry = entry->next)
                if (entry->key == key)
                        break;
 
-       DEBUG ("return (%p)", (void *) entry);
        return (entry);
 }
 
@@ -100,9 +94,6 @@ static counter_list_t *counter_list_create (counter_list_t **list,
 {
        counter_list_t *entry;
 
-       DEBUG ("counter_list_create (list = %p, key = %u, value = %u)",
-                       (void *) *list, key, value);
-
        entry = (counter_list_t *) malloc (sizeof (counter_list_t));
        if (entry == NULL)
                return (NULL);
@@ -126,7 +117,6 @@ static counter_list_t *counter_list_create (counter_list_t **list,
                last->next = entry;
        }
 
-       DEBUG ("return (%p)", (void *) entry);
        return (entry);
 }
 
@@ -135,9 +125,6 @@ static void counter_list_add (counter_list_t **list,
 {
        counter_list_t *entry;
 
-       DEBUG ("counter_list_add (list = %p, key = %u, increment = %u)",
-                       (void *) *list, key, increment);
-
        entry = counter_list_search (list, key);
 
        if (entry != NULL)
@@ -148,7 +135,6 @@ static void counter_list_add (counter_list_t **list,
        {
                counter_list_create (list, key, increment);
        }
-       DEBUG ("return ()");
 }
 
 static int dns_config (const char *key, const char *value)
@@ -222,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];
@@ -242,7 +228,7 @@ static void *dns_child_loop (__attribute__((unused)) void *dummy)
        pcap_obj = pcap_open_live ((pcap_device != NULL) ? pcap_device : "any",
                        PCAP_SNAPLEN,
                        0 /* Not promiscuous */,
-                       interval_g,
+                       (int) CDTIME_T_TO_MS (plugin_get_interval () / 2),
                        pcap_error);
        if (pcap_obj == NULL)
        {
@@ -250,22 +236,27 @@ 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 ("PCAP object created.");
+       DEBUG ("dns plugin: PCAP object created.");
 
        dnstop_set_pcap_obj (pcap_obj);
        dnstop_set_callback (dns_child_callback);
@@ -273,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 ("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)
 {
@@ -300,7 +337,7 @@ static int dns_init (void)
        if (listen_thread_init != 0)
                return (-1);
 
-       status = pthread_create (&listen_thread, NULL, dns_child_loop,
+       status = plugin_thread_create (&listen_thread, NULL, dns_child_loop,
                        (void *) 0);
        if (status != 0)
        {
@@ -315,13 +352,13 @@ static int dns_init (void)
        return (0);
 } /* int dns_init */
 
-static void submit_counter (const char *type, const char *type_instance,
-               counter_t value)
+static void submit_derive (const char *type, const char *type_instance,
+               derive_t value)
 {
        value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = value;
+       values[0].derive = value;
 
        vl.values = values;
        vl.values_len = 1;
@@ -331,15 +368,15 @@ static void submit_counter (const char *type, const char *type_instance,
        sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
-} /* void submit_counter */
+} /* void submit_derive */
 
-static void submit_octets (counter_t queries, counter_t responses)
+static void submit_octets (derive_t queries, derive_t responses)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = queries;
-       values[1].counter = responses;
+       values[0].derive = queries;
+       values[1].derive = responses;
 
        vl.values = values;
        vl.values_len = 2;
@@ -348,7 +385,7 @@ static void submit_octets (counter_t queries, counter_t responses)
        sstrncpy (vl.type, "dns_octets", sizeof (vl.type));
 
        plugin_dispatch_values (&vl);
-} /* void submit_counter */
+} /* void submit_octets */
 
 static int dns_read (void)
 {
@@ -379,8 +416,8 @@ static int dns_read (void)
 
        for (i = 0; i < len; i++)
        {
-               DEBUG ("qtype = %u; counter = %u;", keys[i], values[i]);
-               submit_counter ("dns_qtype", qtype_str (keys[i]), values[i]);
+               DEBUG ("dns plugin: qtype = %u; counter = %u;", keys[i], values[i]);
+               submit_derive ("dns_qtype", qtype_str (keys[i]), values[i]);
        }
 
        pthread_mutex_lock (&opcode_mutex);
@@ -395,8 +432,8 @@ static int dns_read (void)
 
        for (i = 0; i < len; i++)
        {
-               DEBUG ("opcode = %u; counter = %u;", keys[i], values[i]);
-               submit_counter ("dns_opcode", opcode_str (keys[i]), values[i]);
+               DEBUG ("dns plugin: opcode = %u; counter = %u;", keys[i], values[i]);
+               submit_derive ("dns_opcode", opcode_str (keys[i]), values[i]);
        }
 
        pthread_mutex_lock (&rcode_mutex);
@@ -411,8 +448,8 @@ static int dns_read (void)
 
        for (i = 0; i < len; i++)
        {
-               DEBUG ("rcode = %u; counter = %u;", keys[i], values[i]);
-               submit_counter ("dns_rcode", rcode_str (keys[i]), values[i]);
+               DEBUG ("dns plugin: rcode = %u; counter = %u;", keys[i], values[i]);
+               submit_derive ("dns_rcode", rcode_str (keys[i]), values[i]);
        }
 
        return (0);
index 8fc5509..8f633cd 100644 (file)
@@ -376,6 +376,7 @@ static void *collect (void *arg)
        } /* while (1) */
 
        pthread_exit ((void *)0);
+       return ((void *) 0);
 } /* static void *collect (void *) */
 
 static void *open_connection (void __attribute__((unused)) *arg)
@@ -481,8 +482,8 @@ static void *open_connection (void __attribute__((unused)) *arg)
                        collectors[i] = (collector_t *)smalloc (sizeof (collector_t));
                        collectors[i]->socket = NULL;
 
-                       if (0 != (err = pthread_create (&collectors[i]->thread, &ptattr,
-                                                       collect, collectors[i]))) {
+                       if (0 != (err = plugin_thread_create (&collectors[i]->thread,
+                                                       &ptattr, collect, collectors[i]))) {
                                char errbuf[1024];
                                log_err ("pthread_create() failed: %s",
                                                sstrerror (errno, errbuf, sizeof (errbuf)));
@@ -548,14 +549,16 @@ static void *open_connection (void __attribute__((unused)) *arg)
 
                pthread_cond_signal (&conn_available);
        }
-       pthread_exit ((void *)0);
+
+       pthread_exit ((void *) 0);
+       return ((void *) 0);
 } /* static void *open_connection (void *) */
 
 static int email_init (void)
 {
        int err = 0;
 
-       if (0 != (err = pthread_create (&connector, NULL,
+       if (0 != (err = plugin_thread_create (&connector, NULL,
                                open_connection, NULL))) {
                char errbuf[1024];
                disabled = 1;
diff --git a/src/ethstat.c b/src/ethstat.c
new file mode 100644 (file)
index 0000000..dec14f3
--- /dev/null
@@ -0,0 +1,368 @@
+/**
+ * collectd - src/ethstat.c
+ * Copyright (C) 2011       Cyril Feraudet
+ * Copyright (C) 2012       Florian "octo" Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Cyril Feraudet <cyril at feraudet.com>
+ *   Florian "octo" Forster <octo@collectd.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_avltree.h"
+#include "utils_complain.h"
+
+#if HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+#if HAVE_LINUX_SOCKIOS_H
+# include <linux/sockios.h>
+#endif
+#if HAVE_LINUX_ETHTOOL_H
+# include <linux/ethtool.h>
+#endif
+
+struct value_map_s
+{
+  char type[DATA_MAX_NAME_LEN];
+  char type_instance[DATA_MAX_NAME_LEN];
+};
+typedef struct value_map_s value_map_t;
+
+static char **interfaces = NULL;
+static size_t interfaces_num = 0;
+
+static c_avl_tree_t *value_map = NULL;
+
+static _Bool collect_mapped_only = 0;
+
+static int ethstat_add_interface (const oconfig_item_t *ci) /* {{{ */
+{
+  char **tmp;
+  int status;
+
+  tmp = realloc (interfaces,
+      sizeof (*interfaces) * (interfaces_num + 1));
+  if (tmp == NULL)
+    return (-1);
+  interfaces = tmp;
+  interfaces[interfaces_num] = NULL;
+
+  status = cf_util_get_string (ci, interfaces + interfaces_num);
+  if (status != 0)
+    return (status);
+
+  interfaces_num++;
+  INFO("ethstat plugin: Registered interface %s",
+      interfaces[interfaces_num - 1]);
+
+  return (0);
+} /* }}} int ethstat_add_interface */
+
+static int ethstat_add_map (const oconfig_item_t *ci) /* {{{ */
+{
+  value_map_t *map;
+  int status;
+  char *key;
+
+  if ((ci->values_num < 2)
+      || (ci->values_num > 3)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING)
+      || (ci->values[1].type != OCONFIG_TYPE_STRING)
+      || ((ci->values_num == 3)
+        && (ci->values[2].type != OCONFIG_TYPE_STRING)))
+  {
+    ERROR ("ethstat plugin: The %s option requires "
+        "two or three string arguments.", ci->key);
+    return (-1);
+  }
+
+  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);
+  }
+  memset (map, 0, sizeof (*map));
+
+  sstrncpy (map->type, ci->values[1].value.string, sizeof (map->type));
+  if (ci->values_num == 3)
+    sstrncpy (map->type_instance, ci->values[2].value.string,
+        sizeof (map->type_instance));
+
+  if (value_map == NULL)
+  {
+    value_map = c_avl_create ((void *) strcmp);
+    if (value_map == NULL)
+    {
+      sfree (map);
+      sfree (key);
+      ERROR ("ethstat plugin: c_avl_create() failed.");
+      return (-1);
+    }
+  }
+
+  status = c_avl_insert (value_map,
+      /* key = */ key,
+      /* value = */ map);
+  if (status != 0)
+  {
+    if (status > 0)
+      ERROR ("ethstat plugin: Multiple mappings for \"%s\".", key);
+    else
+      ERROR ("ethstat plugin: c_avl_insert(\"%s\") failed.", key);
+
+    sfree (map);
+    sfree (key);
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int ethstat_add_map */
+
+static int ethstat_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Interface", child->key) == 0)
+      ethstat_add_interface (child);
+    else if (strcasecmp ("Map", child->key) == 0)
+      ethstat_add_map (child);
+    else if (strcasecmp ("MappedOnly", child->key) == 0)
+      (void) cf_util_get_boolean (child, &collect_mapped_only);
+    else
+      WARNING ("ethstat plugin: The config option \"%s\" is unknown.",
+          child->key);
+  }
+
+  return (0);
+} /* }}} */
+
+static void ethstat_submit_value (const char *device,
+    const char *type_instance, derive_t value)
+{
+  static c_complain_t complain_no_map = C_COMPLAIN_INIT_STATIC;
+
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+  value_map_t *map = NULL;
+
+  if (value_map != NULL)
+    c_avl_get (value_map, type_instance, (void *) &map);
+
+  /* If the "MappedOnly" option is specified, ignore unmapped values. */
+  if (collect_mapped_only && (map == NULL))
+  {
+    if (value_map == NULL)
+      c_complain (LOG_WARNING, &complain_no_map,
+          "ethstat plugin: The \"MappedOnly\" option has been set to true, "
+          "but no mapping has been configured. All values will be ignored!");
+    return;
+  }
+
+  values[0].derive = value;
+  vl.values = values;
+  vl.values_len = 1;
+
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "ethstat", sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, device, sizeof (vl.plugin_instance));
+  if (map != NULL)
+  {
+    sstrncpy (vl.type, map->type, sizeof (vl.type));
+    sstrncpy (vl.type_instance, map->type_instance,
+        sizeof (vl.type_instance));
+  }
+  else
+  {
+    sstrncpy (vl.type, "derive", sizeof (vl.type));
+    sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+  }
+
+  plugin_dispatch_values (&vl);
+}
+
+static int ethstat_read_interface (char *device)
+{
+  int fd;
+  struct ifreq req;
+  struct ethtool_drvinfo drvinfo;
+  struct ethtool_gstrings *strings;
+  struct ethtool_stats *stats;
+  size_t n_stats;
+  size_t strings_size;
+  size_t stats_size;
+  size_t i;
+  int status;
+
+  memset (&req, 0, sizeof (req));
+  sstrncpy(req.ifr_name, device, sizeof (req.ifr_name));
+
+  fd = socket(AF_INET, SOCK_DGRAM, /* protocol = */ 0);
+  if (fd < 0)
+  {
+    char errbuf[1024];
+    ERROR("ethstat plugin: Failed to open control socket: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return 1;
+  }
+
+  memset (&drvinfo, 0, sizeof (drvinfo));
+  drvinfo.cmd = ETHTOOL_GDRVINFO;
+  req.ifr_data = (void *) &drvinfo;
+  status = ioctl (fd, SIOCETHTOOL, &req);
+  if (status < 0)
+  {
+    char errbuf[1024];
+    close (fd);
+    ERROR ("ethstat plugin: Failed to get driver information "
+        "from %s: %s", device,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  n_stats = (size_t) drvinfo.n_stats;
+  if (n_stats < 1)
+  {
+    close (fd);
+    ERROR("ethstat plugin: No stats available for %s", device);
+    return (-1);
+  }
+
+  strings_size = sizeof (struct ethtool_gstrings)
+    + (n_stats * ETH_GSTRING_LEN);
+  stats_size = sizeof (struct ethtool_stats)
+    + (n_stats * sizeof (uint64_t));
+
+  strings = malloc (strings_size);
+  stats = malloc (stats_size);
+  if ((strings == NULL) || (stats == NULL))
+  {
+    close (fd);
+    sfree (strings);
+    sfree (stats);
+    ERROR("ethstat plugin: malloc(3) failed.");
+    return (-1);
+  }
+
+  strings->cmd = ETHTOOL_GSTRINGS;
+  strings->string_set = ETH_SS_STATS;
+  strings->len = n_stats;
+  req.ifr_data = (void *) strings;
+  status = ioctl (fd, SIOCETHTOOL, &req);
+  if (status < 0)
+  {
+    char errbuf[1024];
+    close (fd);
+    free (strings);
+    free (stats);
+    ERROR ("ethstat plugin: Cannot get strings from %s: %s",
+        device,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  stats->cmd = ETHTOOL_GSTATS;
+  stats->n_stats = n_stats;
+  req.ifr_data = (void *) stats;
+  status = ioctl (fd, SIOCETHTOOL, &req);
+  if (status < 0)
+  {
+    char errbuf[1024];
+    close (fd);
+    free(strings);
+    free(stats);
+    ERROR("ethstat plugin: Reading statistics from %s failed: %s",
+        device,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  for (i = 0; i < n_stats; i++)
+  {
+    const char *stat_name;
+
+    stat_name = (void *) &strings->data[i * ETH_GSTRING_LEN];
+    DEBUG("ethstat plugin: device = \"%s\": %s = %"PRIu64,
+        device, stat_name, (uint64_t) stats->data[i]);
+    ethstat_submit_value (device,
+        stat_name, (derive_t) stats->data[i]);
+  }
+
+  close (fd);
+  sfree (strings);
+  sfree (stats);
+
+  return (0);
+} /* }}} ethstat_read_interface */
+
+static int ethstat_read(void)
+{
+  size_t i;
+
+  for (i = 0; i < interfaces_num; i++)
+    ethstat_read_interface (interfaces[i]);
+
+  return 0;
+}
+
+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 9e3a20e..b9a7365 100644 (file)
@@ -270,13 +270,15 @@ static void set_environment (void) /* {{{ */
   char buffer[1024];
 
 #ifdef HAVE_SETENV
-  ssnprintf (buffer, sizeof (buffer), "%i", interval_g);
+  ssnprintf (buffer, sizeof (buffer), "%.3f",
+      CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
   setenv ("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
 
   ssnprintf (buffer, sizeof (buffer), "%s", hostname_g);
   setenv ("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
 #else
-  ssnprintf (buffer, sizeof (buffer), "COLLECTD_INTERVAL=%i", interval_g);
+  ssnprintf (buffer, sizeof (buffer), "COLLECTD_INTERVAL=%.3f",
+      CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
   putenv (buffer);
 
   ssnprintf (buffer, sizeof (buffer), "COLLECTD_HOSTNAME=%s", hostname_g);
@@ -285,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)
   {
@@ -427,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);
 
@@ -454,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)
   {
@@ -503,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 */
   }
 
@@ -537,12 +540,9 @@ static int parse_line (char *buffer) /* {{{ */
     return (handle_putnotif (stdout, buffer));
   else
   {
-    /* For backwards compatibility */
-    char tmp[1220];
-    /* Let's annoy the user a bit.. */
-    INFO ("exec plugin: Prepending `PUTVAL' to this line: %s", buffer);
-    ssnprintf (tmp, sizeof (tmp), "PUTVAL %s", buffer);
-    return (handle_putval (stdout, tmp));
+    ERROR ("exec plugin: Unable to parse command, ignoring line: \"%s\"",
+       buffer);
+    return (-1);
   }
 } /* int parse_line }}} */
 
@@ -745,7 +745,7 @@ static void *exec_notification_one (void *arg) /* {{{ */
   fprintf (fh,
       "Severity: %s\n"
       "Time: %u\n",
-      severity, (unsigned int) n->time);
+      severity, (unsigned int)CDTIME_T_TO_TIME_T(n->time));
 
   /* Print the optional fields */
   if (strlen (n->host) > 0)
@@ -828,7 +828,7 @@ static int exec_read (void) /* {{{ */
 
     pthread_attr_init (&attr);
     pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-    pthread_create (&t, &attr, exec_read_one, (void *) pl);
+    plugin_thread_create (&t, &attr, exec_read_one, (void *) pl);
     pthread_attr_destroy (&attr);
   } /* for (pl) */
 
@@ -872,7 +872,7 @@ static int exec_notification (const notification_t *n, /* {{{ */
 
     pthread_attr_init (&attr);
     pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-    pthread_create (&t, &attr, exec_notification_one, (void *) pln);
+    plugin_thread_create (&t, &attr, exec_notification_one, (void *) pln);
     pthread_attr_destroy (&attr);
   } /* for (pl) */
 
index 7d8369b..5ad9655 100644 (file)
@@ -73,6 +73,15 @@ struct fc_chain_s /* {{{ */
   fc_chain_t  *next;
 }; /* }}} */
 
+/* Writer configuration. */
+struct fc_writer_s;
+typedef struct fc_writer_s fc_writer_t; /* {{{ */
+struct fc_writer_s
+{
+  char *plugin;
+  c_complain_t complaint;
+}; /* }}} */
+
 /*
  * Global variables
  */
@@ -609,16 +618,13 @@ static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */
 {
   int i;
 
-  char **plugin_list;
-  size_t plugin_list_len;
-
-  plugin_list = NULL;
-  plugin_list_len = 0;
+  fc_writer_t *plugin_list = NULL;
+  size_t plugin_list_len = 0;
 
   for (i = 0; i < ci->children_num; i++)
   {
     oconfig_item_t *child = ci->children + i;
-    char **temp;
+    fc_writer_t *temp;
     int j;
 
     if (strcasecmp ("Plugin", child->key) != 0)
@@ -631,14 +637,17 @@ static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */
 
     for (j = 0; j < child->values_num; j++)
     {
+      char *plugin;
+
       if (child->values[j].type != OCONFIG_TYPE_STRING)
       {
         ERROR ("Filter subsystem: Built-in target `write': "
             "The `Plugin' option accepts only string arguments.");
         continue;
       }
+      plugin = child->values[j].value.string;
 
-      temp = (char **) realloc (plugin_list, (plugin_list_len + 2)
+      temp = (fc_writer_t *) realloc (plugin_list, (plugin_list_len + 2)
           * (sizeof (*plugin_list)));
       if (temp == NULL)
       {
@@ -647,14 +656,15 @@ static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */
       }
       plugin_list = temp;
 
-      plugin_list[plugin_list_len] = fc_strdup (child->values[j].value.string);
-      if (plugin_list[plugin_list_len] == NULL)
+      plugin_list[plugin_list_len].plugin = fc_strdup (plugin);
+      if (plugin_list[plugin_list_len].plugin == NULL)
       {
         ERROR ("fc_bit_write_create: fc_strdup failed.");
         continue;
       }
+      C_COMPLAIN_INIT (&plugin_list[plugin_list_len].complaint);
       plugin_list_len++;
-      plugin_list[plugin_list_len] = NULL;
+      plugin_list[plugin_list_len].plugin = NULL;
     } /* for (j = 0; j < child->values_num; j++) */
   } /* for (i = 0; i < ci->children_num; i++) */
 
@@ -665,7 +675,7 @@ static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */
 
 static int fc_bit_write_destroy (void **user_data) /* {{{ */
 {
-  char **plugin_list;
+  fc_writer_t *plugin_list;
   size_t i;
 
   if ((user_data == NULL) || (*user_data == NULL))
@@ -673,8 +683,8 @@ static int fc_bit_write_destroy (void **user_data) /* {{{ */
 
   plugin_list = *user_data;
 
-  for (i = 0; plugin_list[i] != NULL; i++)
-    free (plugin_list[i]);
+  for (i = 0; plugin_list[i].plugin != NULL; i++)
+    free (plugin_list[i].plugin);
   free (plugin_list);
 
   return (0);
@@ -684,23 +694,23 @@ static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */
     value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
     void **user_data)
 {
-  char **plugin_list;
+  fc_writer_t *plugin_list;
   int status;
 
   plugin_list = NULL;
   if (user_data != NULL)
     plugin_list = *user_data;
 
-  if ((plugin_list == NULL) || (plugin_list[0] == NULL))
+  if ((plugin_list == NULL) || (plugin_list[0].plugin == NULL))
   {
-    static c_complain_t enoent_complaint = C_COMPLAIN_INIT_STATIC;
+    static c_complain_t write_complaint = C_COMPLAIN_INIT_STATIC;
 
     status = plugin_write (/* plugin = */ NULL, ds, vl);
     if (status == ENOENT)
     {
       /* in most cases this is a permanent error, so use the complain
        * mechanism rather than spamming the logs */
-      c_complain (LOG_INFO, &enoent_complaint,
+      c_complain (LOG_INFO, &write_complaint,
           "Filter subsystem: Built-in target `write': Dispatching value to "
           "all write plugins failed with status %i (ENOENT). "
           "Most likely this means you didn't load any write plugins.",
@@ -708,13 +718,16 @@ static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */
     }
     else if (status != 0)
     {
-      INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
+      /* often, this is a permanent error (e.g. target system unavailable),
+       * so use the complain mechanism rather than spamming the logs */
+      c_complain (LOG_INFO, &write_complaint,
+          "Filter subsystem: Built-in target `write': Dispatching value to "
           "all write plugins failed with status %i.", status);
     }
     else
     {
       assert (status == 0);
-      c_release (LOG_INFO, &enoent_complaint, "Filter subsystem: "
+      c_release (LOG_INFO, &write_complaint, "Filter subsystem: "
           "Built-in target `write': Some write plugin is back to normal "
           "operation. `write' succeeded.");
     }
@@ -723,13 +736,21 @@ static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */
   {
     size_t i;
 
-    for (i = 0; plugin_list[i] != NULL; i++)
+    for (i = 0; plugin_list[i].plugin != NULL; i++)
     {
-      status = plugin_write (plugin_list[i], ds, vl);
+      status = plugin_write (plugin_list[i].plugin, ds, vl);
       if (status != 0)
       {
-        INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
-            "the `%s' plugin failed with status %i.", plugin_list[i], status);
+        c_complain (LOG_INFO, &plugin_list[i].complaint,
+            "Filter subsystem: Built-in target `write': Dispatching value to "
+            "the `%s' plugin failed with status %i.",
+            plugin_list[i].plugin, status);
+      }
+      else
+      {
+        c_release (LOG_INFO, &plugin_list[i].complaint,
+            "Filter subsystem: Built-in target `write': Plugin `%s' is back "
+            "to normal operation. `write' succeeded.", plugin_list[i].plugin);
       }
     } /* for (i = 0; plugin_list[i] != NULL; i++) */
   }
index dc0eb11..8fbd271 100644 (file)
@@ -107,16 +107,13 @@ Ops pend=N  Number of times async ops added to pending queues
 63 events to collect in 13 groups
 */
 static void fscache_submit (const char *section, const char *name,
-        counter_t counter_value)
+        value_t value)
 {
-    value_t values[1];
     value_list_t vl = VALUE_LIST_INIT;
 
-    vl.values = values;
+    vl.values = &value;
     vl.values_len = 1;
 
-    vl.values[0].counter = counter_value;
-
     sstrncpy(vl.host, hostname_g, sizeof (vl.host));
     sstrncpy(vl.plugin, "fscache", sizeof (vl.plugin));
     sstrncpy(vl.plugin_instance, section, sizeof (vl.plugin_instance));
@@ -189,8 +186,8 @@ static void fscache_read_stats_file (FILE *fh)
         {
             char *field_name;
             char *field_value_str;
-            char *endptr;
-            counter_t field_value_cnt;
+            value_t field_value_cnt;
+            int status;
 
             field_name = fields[i];
             assert (field_name != NULL);
@@ -201,11 +198,9 @@ static void fscache_read_stats_file (FILE *fh)
             *field_value_str = 0;
             field_value_str++;
 
-            errno = 0;
-            endptr = NULL;
-            field_value_cnt = (counter_t) strtoull (field_value_str,
-                    &endptr, /* base = */ 10);
-            if ((errno != 0) || (endptr == field_value_str))
+            status = parse_value (field_value_str, &field_value_cnt,
+                    DS_TYPE_DERIVE);
+            if (status != 0)
                 continue;
 
             fscache_submit (section, field_name, field_value_cnt);
index 2ffc42a..28be092 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/gmond.c
- * Copyright (C) 2009  Florian octo Forster
+ * Copyright (C) 2009,2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -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"
@@ -545,6 +545,8 @@ static int staging_entry_update (const char *host, const char *name, /* {{{ */
     se->vl.values[ds_index].derive += value.derive;
   else if (ds_type == DS_TYPE_ABSOLUTE)
     se->vl.values[ds_index].absolute = value.absolute;
+  else
+    assert (23 == 42);
 
   se->flags |= (0x01 << ds_index);
 
@@ -593,32 +595,24 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
     case gmetric_string:
     {
       Ganglia_gmetric_string msg_string;
-      char *endptr;
+      int status;
 
       msg_string = msg->Ganglia_value_msg_u.gstr;
 
       host = msg_string.metric_id.host;
       name = msg_string.metric_id.name;
 
-      endptr = NULL;
-      errno = 0;
-      value_counter.counter = (counter_t) strtoull (msg_string.str,
-          &endptr, /* base = */ 0);
-      if ((endptr == msg_string.str) || (errno != 0))
-        value_counter.counter = -1;
-
-      endptr = NULL;
-      errno = 0;
-      value_gauge.gauge = (gauge_t) strtod (msg_string.str, &endptr);
-      if ((endptr == msg_string.str) || (errno != 0))
+      status = parse_value (msg_string.str, &value_derive, DS_TYPE_DERIVE);
+      if (status != 0)
+        value_derive.derive = -1;
+
+      status = parse_value (msg_string.str, &value_gauge, DS_TYPE_GAUGE);
+      if (status != 0)
         value_gauge.gauge = NAN;
 
-      endptr = NULL;
-      errno = 0;
-      value_derive.derive = (derive_t) strtoll (msg_string.str,
-          &endptr, /* base = */ 0);
-      if ((endptr == msg_string.str) || (errno != 0))
-        value_derive.derive = 0;
+      status = parse_value (msg_string.str, &value_counter, DS_TYPE_COUNTER);
+      if (status != 0)
+        value_counter.counter = 0;
 
       break;
     }
@@ -663,11 +657,15 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
   {
     value_t val_copy;
 
-    val_copy = value_counter;
+    if ((map->ds_type == DS_TYPE_COUNTER)
+        || (map->ds_type == DS_TYPE_ABSOLUTE))
+      val_copy = value_counter;
     if (map->ds_type == DS_TYPE_GAUGE)
       val_copy = value_gauge;
     else if (map->ds_type == DS_TYPE_DERIVE)
       val_copy = value_derive;
+    else
+      assert (23 == 42);
 
     return (staging_entry_update (host, name,
           map->type, map->type_instance,
@@ -719,7 +717,7 @@ static int mc_handle_metadata_msg (Ganglia_metadata_msg *msg) /* {{{ */
           map->type, map->type_instance,
           ds->ds_num);
       if (se != NULL)
-        se->vl.interval = (int) msg_meta.metric.tmax;
+        se->vl.interval = TIME_T_TO_CDTIME_T (msg_meta.metric.tmax);
       pthread_mutex_unlock (&staging_lock);
 
       if (se == NULL)
@@ -881,7 +879,7 @@ static int mc_receive_thread_start (void) /* {{{ */
 
   mc_receive_thread_loop = 1;
 
-  status = pthread_create (&mc_receive_thread_id, /* attr = */ NULL,
+  status = plugin_thread_create (&mc_receive_thread_id, /* attr = */ NULL,
       mc_receive_thread, /* args = */ NULL);
   if (status != 0)
   {
index 4e08375..4428b75 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/hddtemp.c
  * Copyright (C) 2005,2006  Vincent Stehlé
- * Copyright (C) 2006,2007  Florian octo Forster
+ * Copyright (C) 2006-2010  Florian octo Forster
  * Copyright (C) 2008       Sebastian Harl
  *
  * This program is free software; you can redistribute it and/or modify it
 static const char *config_keys[] =
 {
        "Host",
-       "Port",
-       "TranslateDevicename"
+       "Port"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-typedef struct hddname
-{
-       int major;
-       int minor;
-       char *name;
-       struct hddname *next;
-} hddname_t;
-
-static hddname_t *first_hddname = NULL;
 static char *hddtemp_host = NULL;
 static char hddtemp_port[16];
-static int translate_devicename = 1;
 
 /*
  * NAME
@@ -231,13 +220,6 @@ static int hddtemp_config (const char *key, const char *value)
                else
                        sstrncpy (hddtemp_port, value, sizeof (hddtemp_port));
        }
-       else if (strcasecmp (key, "TranslateDevicename") == 0)
-       {
-               if (IS_TRUE (value))
-                       translate_devicename = 1;
-               else
-                       translate_devicename = 0;
-       }
        else
        {
                return (-1);
@@ -246,199 +228,6 @@ static int hddtemp_config (const char *key, const char *value)
        return (0);
 }
 
-/* In the init-function we initialize the `hddname_t' list used to translate
- * disk-names. Under Linux that's done using `/proc/partitions'. Under other
- * operating-systems, it's not done at all. */
-static int hddtemp_init (void)
-{
-#if KERNEL_LINUX
-       FILE *fh;
-       char buf[1024];
-       int buflen;
-
-       char *fields[16];
-       int num_fields;
-
-       int major;
-       int minor;
-       char *name;
-       hddname_t *next;
-       hddname_t *entry;
-
-       next = first_hddname;
-       while (next != NULL)
-       {
-               entry = next;
-               next = entry->next;
-
-               free (entry->name);
-               free (entry);
-       }
-       first_hddname = NULL;
-
-       if ((fh = fopen ("/proc/partitions", "r")) != NULL)
-       {
-               DEBUG ("hddtemp plugin: Looking at /proc/partitions...");
-
-               while (fgets (buf, sizeof (buf), fh) != NULL)
-               {
-                       /* Delete trailing newlines */
-                       buflen = strlen (buf);
-
-                       while ((buflen > 0) && ((buf[buflen-1] == '\n') || (buf[buflen-1] == '\r')))
-                               buf[--buflen] = '\0';
-
-                       /* We want lines of the form:
-                        *
-                        *     3     1   77842926 hda1
-                        *
-                        * ...so, skip everything else. */
-                       if (buflen == 0)
-                               continue;
-                       
-                       num_fields = strsplit (buf, fields, 16);
-
-                       if (num_fields != 4)
-                               continue;
-
-                       major = atoi (fields[0]);
-                       minor = atoi (fields[1]);
-
-                       /* We try to keep only entries, which may correspond to
-                        * physical disks and that may have a corresponding
-                        * entry in the hddtemp daemon. Basically, this means
-                        * IDE and SCSI. */
-                       switch (major)
-                       {
-                               /* SCSI. */
-                               case SCSI_DISK0_MAJOR:
-                               case SCSI_DISK1_MAJOR:
-                               case SCSI_DISK2_MAJOR:
-                               case SCSI_DISK3_MAJOR:
-                               case SCSI_DISK4_MAJOR:
-                               case SCSI_DISK5_MAJOR:
-                               case SCSI_DISK6_MAJOR:
-                               case SCSI_DISK7_MAJOR:
-#ifdef SCSI_DISK8_MAJOR
-                               case SCSI_DISK8_MAJOR:
-                               case SCSI_DISK9_MAJOR:
-                               case SCSI_DISK10_MAJOR:
-                               case SCSI_DISK11_MAJOR:
-                               case SCSI_DISK12_MAJOR:
-                               case SCSI_DISK13_MAJOR:
-                               case SCSI_DISK14_MAJOR:
-                               case SCSI_DISK15_MAJOR:
-#endif /* SCSI_DISK8_MAJOR */
-                                       /* SCSI disks minors are multiples of 16.
-                                        * Keep only those. */
-                                       if (minor % 16)
-                                               continue;
-                                       break;
-
-                               /* IDE. */
-                               case IDE0_MAJOR:
-                               case IDE1_MAJOR:
-                               case IDE2_MAJOR:
-                               case IDE3_MAJOR:
-                               case IDE4_MAJOR:
-                               case IDE5_MAJOR:
-                               case IDE6_MAJOR:
-                               case IDE7_MAJOR:
-                               case IDE8_MAJOR:
-                               case IDE9_MAJOR:
-                                       /* IDE disks minors can only be 0 or 64.
-                                        * Keep only those. */
-                                       if(minor != 0 && minor != 64)
-                                               continue;
-                                       break;
-
-                               /* Skip all other majors. */
-                               default:
-                                       DEBUG ("hddtemp plugin: Skipping unknown major %i", major);
-                                       continue;
-                       } /* switch (major) */
-
-                       if ((name = strdup (fields[3])) == NULL)
-                       {
-                               ERROR ("hddtemp plugin: strdup(%s) == NULL", fields[3]);
-                               continue;
-                       }
-
-                       if ((entry = (hddname_t *) malloc (sizeof (hddname_t))) == NULL)
-                       {
-                               ERROR ("hddtemp plugin: malloc (%u) == NULL",
-                                               (unsigned int) sizeof (hddname_t));
-                               free (name);
-                               continue;
-                       }
-
-                       DEBUG ("hddtemp plugin: Found disk: %s (%u:%u).", name, major, minor);
-
-                       entry->major = major;
-                       entry->minor = minor;
-                       entry->name  = name;
-                       entry->next  = NULL;
-
-                       if (first_hddname == NULL)
-                       {
-                               first_hddname = entry;
-                       }
-                       else
-                       {
-                               entry->next = first_hddname;
-                               first_hddname = entry;
-                       }
-               }
-               fclose (fh);
-       }
-#if COLLECT_DEBUG
-       else
-       {
-               char errbuf[1024];
-               DEBUG ("hddtemp plugin: Could not open /proc/partitions: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-       }
-#endif /* COLLECT_DEBUG */
-#endif /* KERNEL_LINUX */
-
-       return (0);
-} /* int hddtemp_init */
-
-/*
- * hddtemp_get_major_minor
- *
- * Description:
- *   Try to "cook" a bit the drive name as returned
- *   by the hddtemp daemon. The intend is to transform disk
- *   names into <major>-<minor> when possible.
- */
-static char *hddtemp_get_major_minor (char *drive)
-{
-       hddname_t *list;
-       char *ret;
-
-       for (list = first_hddname; list != NULL; list = list->next)
-               if (strcmp (drive, list->name) == 0)
-                       break;
-
-       if (list == NULL)
-       {
-               DEBUG ("hddtemp plugin: Don't know %s, keeping name as-is.", drive);
-               return (strdup (drive));
-       }
-
-       if ((ret = (char *) malloc (128 * sizeof (char))) == NULL)
-               return (NULL);
-
-       if (ssnprintf (ret, 128, "%i-%i", list->major, list->minor) >= 128)
-       {
-               free (ret);
-               return (NULL);
-       }
-
-       return (ret);
-}
-
 static void hddtemp_submit (char *type_instance, double value)
 {
        value_t values[1];
@@ -487,7 +276,7 @@ static int hddtemp_read (void)
 
        for (i = 0; i < num_disks; i++)
        {
-               char *name, *major_minor;
+               char *name;
                double temperature;
                char *mode;
 
@@ -504,16 +293,7 @@ static int hddtemp_read (void)
                if (mode[0] == 'F')
                        temperature = (temperature - 32.0) * 5.0 / 9.0;
 
-               if (translate_devicename
-                               && (major_minor = hddtemp_get_major_minor (name)) != NULL)
-               {
-                       hddtemp_submit (major_minor, temperature);
-                       free (major_minor);
-               }
-               else
-               {
-                       hddtemp_submit (name, temperature);
-               }
+               hddtemp_submit (name, temperature);
        }
        
        return (0);
@@ -525,6 +305,5 @@ void module_register (void)
 {
        plugin_register_config ("hddtemp", hddtemp_config,
                        config_keys, config_keys_num);
-       plugin_register_init ("hddtemp", hddtemp_init);
        plugin_register_read ("hddtemp", hddtemp_read);
 }
index 9b566ea..c618af9 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/interface.c
- * Copyright (C) 2005-2008  Florian octo Forster
+ * Copyright (C) 2005-2010  Florian octo Forster
  * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -128,7 +128,7 @@ static int interface_config (const char *key, const char *value)
 static int interface_init (void)
 {
        kstat_t *ksp_chain;
-       unsigned long long val;
+       derive_t val;
 
        numif = 0;
 
@@ -155,8 +155,8 @@ static int interface_init (void)
 #endif /* HAVE_LIBKSTAT */
 
 static void if_submit (const char *dev, const char *type,
-               unsigned long long rx,
-               unsigned long long tx)
+               derive_t rx,
+               derive_t tx)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
@@ -164,15 +164,15 @@ static void if_submit (const char *dev, const char *type,
        if (ignorelist_match (ignorelist, dev) != 0)
                return;
 
-       values[0].counter = rx;
-       values[1].counter = tx;
+       values[0].derive = rx;
+       values[1].derive = tx;
 
        vl.values = values;
        vl.values_len = 2;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "interface", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance));
        sstrncpy (vl.type, type, sizeof (vl.type));
-       sstrncpy (vl.type_instance, dev, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
 } /* void if_submit */
@@ -234,7 +234,7 @@ static int interface_read (void)
 #elif KERNEL_LINUX
        FILE *fh;
        char buffer[1024];
-       unsigned long long incoming, outgoing;
+       derive_t incoming, outgoing;
        char *device;
 
        char *dummy;
@@ -286,8 +286,8 @@ static int interface_read (void)
 
 #elif HAVE_LIBKSTAT
        int i;
-       unsigned long long rx;
-       unsigned long long tx;
+       derive_t rx;
+       derive_t tx;
 
        if (kc == NULL)
                return (-1);
index 95b3dbf..fada5bd 100644 (file)
@@ -136,7 +136,7 @@ static void sensor_read_handler (ipmi_sensor_t *sensor,
 
         if (c_ipmi_nofiy_notpresent)
         {
-          notification_t n = { NOTIF_WARNING, time(NULL), "", "", "ipmi",
+          notification_t n = { NOTIF_WARNING, cdtime (), "", "", "ipmi",
             "", "", "", NULL };
 
           sstrncpy (n.host, hostname_g, sizeof (n.host));
@@ -190,7 +190,7 @@ static void sensor_read_handler (ipmi_sensor_t *sensor,
 
     if (c_ipmi_nofiy_notpresent)
     {
-      notification_t n = { NOTIF_OKAY, time(NULL), "", "", "ipmi",
+      notification_t n = { NOTIF_OKAY, cdtime (), "", "", "ipmi",
         "", "", "", NULL };
 
       sstrncpy (n.host, hostname_g, sizeof (n.host));
@@ -363,7 +363,7 @@ static int sensor_list_add (ipmi_sensor_t *sensor)
 
   if (c_ipmi_nofiy_add && (c_ipmi_init_in_progress == 0))
   {
-    notification_t n = { NOTIF_OKAY, time(NULL), "", "", "ipmi",
+    notification_t n = { NOTIF_OKAY, cdtime (), "", "", "ipmi",
                          "", "", "", NULL };
 
     sstrncpy (n.host, hostname_g, sizeof (n.host));
@@ -417,7 +417,7 @@ static int sensor_list_remove (ipmi_sensor_t *sensor)
 
   if (c_ipmi_nofiy_remove && c_ipmi_active)
   {
-    notification_t n = { NOTIF_WARNING, time(NULL), "", "",
+    notification_t n = { NOTIF_WARNING, cdtime (), "", "",
                          "ipmi", "", "", "", NULL };
 
     sstrncpy (n.host, hostname_g, sizeof (n.host));
@@ -664,11 +664,12 @@ static int c_ipmi_init (void)
   int status;
 
   /* Don't send `ADD' notifications during startup (~ 1 minute) */
-  c_ipmi_init_in_progress = 1 + (60 / interval_g);
+  time_t iv = CDTIME_T_TO_TIME_T (plugin_get_interval ());
+  c_ipmi_init_in_progress = 1 + (60 / iv);
 
   c_ipmi_active = 1;
 
-  status = pthread_create (&thread_id, /* attr = */ NULL, thread_main,
+  status = plugin_thread_create (&thread_id, /* attr = */ NULL, thread_main,
       /* user data = */ NULL);
   if (status != 0)
   {
index aa53074..49454f0 100644 (file)
@@ -1,8 +1,8 @@
 /**
  * collectd - src/iptables.c
- * Copyright (C) 2007 Sjoerd van der Berg
- * Copyright (C) 2007 Florian octo Forster
- * Copyright (C) 2009 Marco Chiappero
+ * Copyright (C) 2007       Sjoerd van der Berg
+ * Copyright (C) 2007-2010  Florian octo Forster
+ * Copyright (C) 2009       Marco Chiappero
  *
  * 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
@@ -20,7 +20,7 @@
  *
  * Authors:
  *  Sjoerd van der Berg <harekiet at users.sourceforge.net>
- *  Florian Forster <octo at verplant.org>
+ *  Florian Forster <octo at collectd.org>
  *  Marco Chiappero <marco at absence.it>
  **/
 
 
 #include <sys/socket.h>
 
-#if OWN_LIBIPTC
-# include "owniptc/libiptc.h"
-# include "owniptc/libip6tc.h"
-
-# define HAVE_IPTC_HANDLE_T 1
-# define HAVE_IP6TC_HANDLE_T 1
-
-#else /* if !OWN_LIBIPTC */
-# include <libiptc/libiptc.h>
-# include <libiptc/libip6tc.h>
+#include <libiptc/libiptc.h>
+#include <libiptc/libip6tc.h>
 
 /*
  * iptc_handle_t was available before libiptc was officially available as a
  * this is somewhat hacky, I didn't find better way to solve that :-/
  * -tokkee
  */
-# ifndef HAVE_IPTC_HANDLE_T
+#ifndef HAVE_IPTC_HANDLE_T
 typedef struct iptc_handle iptc_handle_t;
-# endif
-# ifndef HAVE_IP6TC_HANDLE_T
+#endif
+#ifndef HAVE_IP6TC_HANDLE_T
 typedef struct ip6tc_handle ip6tc_handle_t;
-# endif
-#endif /* !OWN_LIBIPTC */
+#endif
 
 /*
  * (Module-)Global variables
@@ -297,11 +288,11 @@ static int submit6_match (const struct ip6t_entry_match *match,
     }
 
     sstrncpy (vl.type, "ipt_bytes", sizeof (vl.type));
-    values[0].counter = (counter_t) entry->counters.bcnt;
+    values[0].derive = (derive_t) entry->counters.bcnt;
     plugin_dispatch_values (&vl);
 
     sstrncpy (vl.type, "ipt_packets", sizeof (vl.type));
-    values[0].counter = (counter_t) entry->counters.pcnt;
+    values[0].derive = (derive_t) entry->counters.pcnt;
     plugin_dispatch_values (&vl);
 
     return (0);
@@ -358,11 +349,11 @@ static int submit_match (const struct ipt_entry_match *match,
     }
 
     sstrncpy (vl.type, "ipt_bytes", sizeof (vl.type));
-    values[0].counter = (counter_t) entry->counters.bcnt;
+    values[0].derive = (derive_t) entry->counters.bcnt;
     plugin_dispatch_values (&vl);
 
     sstrncpy (vl.type, "ipt_packets", sizeof (vl.type));
-    values[0].counter = (counter_t) entry->counters.pcnt;
+    values[0].derive = (derive_t) entry->counters.pcnt;
     plugin_dispatch_values (&vl);
 
     return (0);
index 7bd8c69..fa89489 100644 (file)
@@ -227,18 +227,16 @@ static int get_ti (struct ip_vs_dest_entry *de, char *ti, size_t size)
        return 0;
 } /* get_ti */
 
-static void cipvs_submit_connections (char *pi, char *ti, counter_t value)
+static void cipvs_submit_connections (char *pi, char *ti, derive_t value)
 {
        value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = value;
+       values[0].derive = value;
 
        vl.values     = values;
        vl.values_len = 1;
 
-       vl.interval = interval_g;
-
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "ipvs", sizeof (vl.plugin));
        sstrncpy (vl.plugin_instance, pi, sizeof (vl.plugin_instance));
@@ -251,19 +249,17 @@ static void cipvs_submit_connections (char *pi, char *ti, counter_t value)
 } /* cipvs_submit_connections */
 
 static void cipvs_submit_if (char *pi, char *t, char *ti,
-               counter_t rx, counter_t tx)
+               derive_t rx, derive_t tx)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = rx;
-       values[1].counter = tx;
+       values[0].derive = rx;
+       values[1].derive = tx;
 
        vl.values     = values;
        vl.values_len = 2;
 
-       vl.interval = interval_g;
-
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "ipvs", sizeof (vl.plugin));
        sstrncpy (vl.plugin_instance, pi, sizeof (vl.plugin_instance));
index 70d6b5a..1168b97 100644 (file)
--- a/src/irq.c
+++ b/src/irq.c
@@ -1,6 +1,7 @@
 /**
  * collectd - src/irq.c
  * Copyright (C) 2007  Peter Holik
+ * Copyright (C) 2011  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
@@ -24,6 +25,7 @@
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
+#include "utils_ignorelist.h"
 
 #if !KERNEL_LINUX
 # error "No applicable input method."
@@ -39,82 +41,44 @@ static const char *config_keys[] =
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-static char         **irq_list;
-static unsigned int   irq_list_num = 0;
+static ignorelist_t *ignorelist = NULL;
 
-/* 
- * irq_list_action:
- * 0 => default is to collect selected irqs
- * 1 => ignore selcted irqs
+/*
+ * Private functions
  */
-static int irq_list_action;
-
 static int irq_config (const char *key, const char *value)
 {
+       if (ignorelist == NULL)
+               ignorelist = ignorelist_create (/* invert = */ 1);
+
        if (strcasecmp (key, "Irq") == 0)
        {
-               char **temp;
-
-               temp = realloc (irq_list, (irq_list_num + 1) * sizeof (*irq_list));
-               if (temp == NULL)
-               {
-                       fprintf (stderr, "irq plugin: Cannot allocate more memory.\n");
-                       ERROR ("irq plugin: Cannot allocate more memory.");
-                       return (1);
-               }
-               irq_list = temp;
-
-               irq_list[irq_list_num] = strdup (value);
-               if (irq_list[irq_list_num] == NULL)
-               {
-                       ERROR ("irq plugin: strdup(3) failed.");
-                       return (1);
-               }
-
-               irq_list_num++;
+               ignorelist_add (ignorelist, value);
        }
        else if (strcasecmp (key, "IgnoreSelected") == 0)
        {
+               int invert = 1;
                if (IS_TRUE (value))
-                       irq_list_action = 1;
-               else
-                       irq_list_action = 0;
+                       invert = 0;
+               ignorelist_set_invert (ignorelist, invert);
        }
        else
        {
                return (-1);
        }
-       return (0);
-}
 
-/*
- * Check if this interface/instance should be ignored. This is called from
- * both, `submit' and `write' to give client and server the ability to
- * ignore certain stuff..
- */
-static int check_ignore_irq (const char *irq)
-{
-       unsigned int i;
-
-       if (irq_list_num < 1)
-               return (0);
-
-       for (i = 0; i < irq_list_num; i++)
-               if (strcmp (irq, irq_list[i]) == 0)
-                       return (irq_list_action);
-
-       return (1 - irq_list_action);
+       return (0);
 }
 
-static void irq_submit (const char *irq_name, counter_t value)
+static void irq_submit (const char *irq_name, derive_t value)
 {
        value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       if (check_ignore_irq (irq_name))
+       if (ignorelist_match (ignorelist, irq_name) != 0)
                return;
 
-       values[0].counter = value;
+       values[0].derive = value;
 
        vl.values = values;
        vl.values_len = 1;
@@ -130,30 +94,57 @@ static int irq_read (void)
 {
        FILE *fh;
        char buffer[1024];
-       unsigned long long irq_value;
-       unsigned long long value;
-       char *endptr;
-       int i;
-
-       char *fields[64];
-       int fields_num;
-
-       if ((fh = fopen ("/proc/interrupts", "r")) == NULL)
+       int  cpu_count;
+       char *fields[256];
+
+       /*
+        * Example content:
+        *         CPU0       CPU1       CPU2       CPU3
+        * 0:       2574          1          3          2   IO-APIC-edge      timer
+        * 1:     102553     158669     218062      70587   IO-APIC-edge      i8042
+        * 8:          0          0          0          1   IO-APIC-edge      rtc0
+        */
+       fh = fopen ("/proc/interrupts", "r");
+       if (fh == NULL)
        {
                char errbuf[1024];
-               WARNING ("irq plugin: fopen (/proc/interrupts): %s",
+               ERROR ("irq plugin: fopen (/proc/interrupts): %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
+
+       /* Get CPU count from the first line */
+       if(fgets (buffer, sizeof (buffer), fh) != NULL) {
+               cpu_count = strsplit (buffer, fields,
+                               STATIC_ARRAY_SIZE (fields));
+       } else {
+               ERROR ("irq plugin: unable to get CPU count from first line "
+                               "of /proc/interrupts");
+               return (-1);
+       }
+
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
                char *irq_name;
                size_t irq_name_len;
+               derive_t irq_value;
+               int i;
+               int fields_num;
+               int irq_values_to_parse;
 
-               fields_num = strsplit (buffer, fields, 64);
+               fields_num = strsplit (buffer, fields,
+                               STATIC_ARRAY_SIZE (fields));
                if (fields_num < 2)
                        continue;
 
+               /* Parse this many numeric fields, skip the rest
+                * (+1 because first there is a name of irq in each line) */
+               if (fields_num >= cpu_count + 1)
+                       irq_values_to_parse = cpu_count;
+               else
+                       irq_values_to_parse = fields_num - 1;
+
+               /* First field is irq name and colon */
                irq_name = fields[0];
                irq_name_len = strlen (irq_name);
                if (irq_name_len < 2)
@@ -168,25 +159,24 @@ static int irq_read (void)
                irq_name_len--;
 
                irq_value = 0;
-               for (i = 1; i < fields_num; i++)
+               for (i = 1; i <= irq_values_to_parse; i++)
                {
-                       errno = 0;
-                       endptr = NULL;
-                       value = strtoull (fields[i], &endptr, 10);
+                       /* Per-CPU value */
+                       value_t v;
+                       int status;
 
-                       /* Ignore all fields following a non-numeric field. */
-                       if ((errno != 0) || (endptr == NULL) || (*endptr != 0))
+                       status = parse_value (fields[i], &v, DS_TYPE_DERIVE);
+                       if (status != 0)
                                break;
 
-                       irq_value += value;
+                       irq_value += v.derive;
                } /* for (i) */
 
                /* No valid fields -> do not submit anything. */
                if (i <= 1)
                        continue;
 
-               /* Force 32bit wrap-around */
-               irq_submit (irq_name, irq_value % 4294967296ULL);
+               irq_submit (irq_name, irq_value);
        }
 
        fclose (fh);
index a8ffd8e..83cd353 100644 (file)
@@ -109,7 +109,7 @@ static int cjni_callback_register (JNIEnv *jvm_env, jobject o_name,
 static int cjni_read (user_data_t *user_data);
 static int cjni_write (const data_set_t *ds, const value_list_t *vl,
     user_data_t *ud);
-static int cjni_flush (int timeout, const char *identifier, user_data_t *ud);
+static int cjni_flush (cdtime_t timeout, const char *identifier, user_data_t *ud);
 static void cjni_log (int severity, const char *message, user_data_t *ud);
 static int cjni_notification (const notification_t *n, user_data_t *ud);
 
@@ -809,7 +809,7 @@ static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */
 #undef SET_STRING
 
   /* Set the `time' member. Java stores time in milliseconds. */
-  status = ctoj_long (jvm_env, ((jlong) vl->time) * ((jlong) 1000),
+  status = ctoj_long (jvm_env, (jlong) CDTIME_T_TO_MS (vl->time),
       c_valuelist, o_valuelist, "setTime");
   if (status != 0)
   {
@@ -819,7 +819,8 @@ static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */
   }
 
   /* Set the `interval' member.. */
-  status = ctoj_long (jvm_env, (jlong) vl->interval,
+  status = ctoj_long (jvm_env,
+      (jlong) CDTIME_T_TO_MS (vl->interval),
       c_valuelist, o_valuelist, "setInterval");
   if (status != 0)
   {
@@ -905,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)
   {
@@ -914,7 +915,7 @@ static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */
     return (NULL);
   }
 
-  /* Set the `interval' member.. */
+  /* Set the `severity' member.. */
   status = ctoj_int (jvm_env, (jint) n->severity,
       c_notification, o_notification, "setSeverity");
   if (status != 0)
@@ -1242,7 +1243,7 @@ static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
     return (-1);
   }
   /* Java measures time in milliseconds. */
-  vl->time = (time_t) (tmp_long / ((jlong) 1000));
+  vl->time = MS_TO_CDTIME_T (tmp_long);
 
   status = jtoc_long (jvm_env, &tmp_long,
       class_ptr, object_ptr, "getInterval");
@@ -1251,7 +1252,7 @@ static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
     ERROR ("java plugin: jtoc_value_list: jtoc_long (getInterval) failed.");
     return (-1);
   }
-  vl->interval = (int) tmp_long;
+  vl->interval = MS_TO_CDTIME_T (tmp_long);
 
   status = jtoc_values_array (jvm_env, ds, vl, class_ptr, object_ptr);
   if (status != 0)
@@ -1305,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");
@@ -1729,7 +1730,7 @@ static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{
 
     case CB_TYPE_FLUSH:
       method_name = "flush";
-      method_signature = "(ILjava/lang/String;)I";
+      method_signature = "(Ljava/lang/Number;Ljava/lang/String;)I";
       break;
 
     case CB_TYPE_SHUTDOWN:
@@ -2114,7 +2115,7 @@ static int cjni_thread_detach (void) /* {{{ */
   cjni_env->jvm_env = NULL;
 
   return (0);
-} /* }}} JNIEnv *cjni_thread_attach */
+} /* }}} int cjni_thread_detach */
 
 static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */
 {
@@ -2467,7 +2468,6 @@ static int cjni_read (user_data_t *ud) /* {{{ */
 {
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2491,13 +2491,7 @@ static int cjni_read (user_data_t *ud) /* {{{ */
   ret_status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object,
       cbi->method);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_read */
 
@@ -2508,7 +2502,6 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
   jobject vl_java;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2533,6 +2526,7 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
   if (vl_java == NULL)
   {
     ERROR ("java plugin: cjni_write: ctoj_value_list failed.");
+    cjni_thread_detach ();
     return (-1);
   }
 
@@ -2541,24 +2535,18 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
 
   (*jvm_env)->DeleteLocalRef (jvm_env, vl_java);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_write: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_write */
 
 /* Call the CB_TYPE_FLUSH callback pointed to by the `user_data_t' pointer. */
-static int cjni_flush (int timeout, const char *identifier, /* {{{ */
+static int cjni_flush (cdtime_t timeout, const char *identifier, /* {{{ */
     user_data_t *ud)
 {
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
+  jobject o_timeout;
   jobject o_identifier;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2579,29 +2567,36 @@ static int cjni_flush (int timeout, const char *identifier, /* {{{ */
 
   cbi = (cjni_callback_info_t *) ud->data;
 
+  o_timeout = ctoj_jdouble_to_number (jvm_env,
+      (jdouble) CDTIME_T_TO_DOUBLE (timeout));
+  if (o_timeout == NULL)
+  {
+    ERROR ("java plugin: cjni_flush: Converting double "
+        "to Number object failed.");
+    cjni_thread_detach ();
+    return (-1);
+  }
+
   o_identifier = NULL;
   if (identifier != NULL)
   {
     o_identifier = (*jvm_env)->NewStringUTF (jvm_env, identifier);
     if (o_identifier == NULL)
     {
+      (*jvm_env)->DeleteLocalRef (jvm_env, o_timeout);
       ERROR ("java plugin: cjni_flush: NewStringUTF failed.");
+      cjni_thread_detach ();
       return (-1);
     }
   }
 
   ret_status = (*jvm_env)->CallIntMethod (jvm_env,
-      cbi->object, cbi->method, (jint) timeout, o_identifier);
+      cbi->object, cbi->method, o_timeout, o_identifier);
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_identifier);
+  (*jvm_env)->DeleteLocalRef (jvm_env, o_timeout);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_flush: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_flush */
 
@@ -2627,7 +2622,10 @@ static void cjni_log (int severity, const char *message, /* {{{ */
 
   o_message = (*jvm_env)->NewStringUTF (jvm_env, message);
   if (o_message == NULL)
+  {
+    cjni_thread_detach ();
     return;
+  }
 
   (*jvm_env)->CallVoidMethod (jvm_env,
       cbi->object, cbi->method, (jint) severity, o_message);
@@ -2645,7 +2643,6 @@ static int cjni_notification (const notification_t *n, /* {{{ */
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
   jobject o_notification;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2670,6 +2667,7 @@ static int cjni_notification (const notification_t *n, /* {{{ */
   if (o_notification == NULL)
   {
     ERROR ("java plugin: cjni_notification: ctoj_notification failed.");
+    cjni_thread_detach ();
     return (-1);
   }
 
@@ -2678,13 +2676,7 @@ static int cjni_notification (const notification_t *n, /* {{{ */
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_notification */
 
@@ -2712,24 +2704,20 @@ static int cjni_match_target_create (const oconfig_item_t *ci, /* {{{ */
       (*jvm_env)->DeleteLocalRef (jvm_env, cbi_ret->object); \
   } \
   free (cbi_ret); \
-  if (jvm_env != NULL) { \
-    if (o_ci != NULL) \
-      (*jvm_env)->DeleteLocalRef (jvm_env, o_ci); \
-    cjni_thread_detach (); \
-  } \
+  if (o_ci != NULL) \
+    (*jvm_env)->DeleteLocalRef (jvm_env, o_ci); \
+  cjni_thread_detach (); \
   return (status)
 
   if (jvm == NULL)
   {
     ERROR ("java plugin: cjni_read: jvm == NULL");
-    BAIL_OUT (-1);
+    return (-1);
   }
 
   jvm_env = cjni_thread_attach ();
   if (jvm_env == NULL)
-  {
-    BAIL_OUT (-1);
-  }
+    return (-1);
 
   /* Find out whether to create a match or a target. */
   if (strcasecmp ("Match", ci->key) == 0)
@@ -2923,10 +2911,7 @@ static int cjni_match_target_invoke (const data_set_t *ds, /* {{{ */
     }
   } /* if (cbi->type == CB_TYPE_TARGET) */
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-    ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_match_target_invoke */
 
index 6044145..1d4dff5 100644 (file)
@@ -4,11 +4,18 @@ if COMPILER_IS_GCC
 AM_CFLAGS = -Wall -Werror
 endif
 
-pkginclude_HEADERS = client.h lcc_features.h
+pkginclude_HEADERS = collectd/client.h collectd/network.h collectd/network_buffer.h collectd/lcc_features.h
 lib_LTLIBRARIES = libcollectdclient.la
 nodist_pkgconfig_DATA = libcollectdclient.pc
 
-BUILT_SOURCES = lcc_features.h
+BUILT_SOURCES = collectd/lcc_features.h
 
-libcollectdclient_la_SOURCES = client.c
-libcollectdclient_la_LDFLAGS = -version-info 0:0:0
+libcollectdclient_la_SOURCES = client.c network.c network_buffer.c
+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
+libcollectdclient_la_CPPFLAGS += $(GCRYPT_CPPFLAGS)
+libcollectdclient_la_LDFLAGS += $(GCRYPT_LDFLAGS)
+libcollectdclient_la_LIBADD += $(GCRYPT_LIBS)
+endif
index 2f427a8..726f25d 100644 (file)
@@ -1,22 +1,27 @@
 /**
  * libcollectdclient - src/libcollectdclient/client.c
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008-2012  Florian octo Forster
  *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
+ * 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:
  *
- * 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.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * 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
+ * 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 verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #if HAVE_CONFIG_H
@@ -27,7 +32,7 @@
 # define __attribute__(x) /**/
 #endif
 
-#include "lcc_features.h"
+#include "collectd/lcc_features.h"
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -41,7 +46,7 @@
 #include <math.h>
 #include <netdb.h>
 
-#include "client.h"
+#include "collectd/client.h"
 
 /* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11
  * to no longer define it. We'll use the old, RFC 2553 value here. */
@@ -164,27 +169,14 @@ static int lcc_set_errno (lcc_connection_t *c, int err) /* {{{ */
   return (0);
 } /* }}} int lcc_set_errno */
 
-/* lcc_strdup: Since `strdup' is an XSI extension, we provide our own version
- * here. */
-__attribute__((malloc, nonnull (1)))
-static char *lcc_strdup (const char *str) /* {{{ */
-{
-  size_t strsize;
-  char *ret;
-
-  strsize = strlen (str) + 1;
-  ret = (char *) malloc (strsize);
-  if (ret != NULL)
-    memcpy (ret, str, strsize);
-  return (ret);
-} /* }}} char *lcc_strdup */
-
-__attribute__((nonnull (1, 2)))
 static char *lcc_strescape (char *dest, const char *src, size_t dest_size) /* {{{ */
 {
   size_t dest_pos;
   size_t src_pos;
 
+  if ((dest == NULL) || (src == NULL))
+    return (NULL);
+
   dest_pos = 0;
   src_pos = 0;
 
@@ -338,7 +330,7 @@ static int lcc_receive (lcc_connection_t *c, /* {{{ */
     lcc_chomp (buffer);
     LCC_DEBUG ("receive: <-- %s\n", buffer);
 
-    res.lines[i] = lcc_strdup (buffer);
+    res.lines[i] = strdup (buffer);
     if (res.lines[i] == NULL)
     {
       lcc_set_errno (c, ENOMEM);
@@ -733,7 +725,7 @@ int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */
 
     if (values_names != NULL)
     {
-      values_names[i] = lcc_strdup (key);
+      values_names[i] = strdup (key);
       if (values_names[i] == NULL)
         BAIL_OUT (ENOMEM);
     }
@@ -775,11 +767,11 @@ int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */
   SSTRCATF (command, "PUTVAL %s",
       lcc_strescape (ident_esc, ident_str, sizeof (ident_esc)));
 
-  if (vl->interval > 0)
-    SSTRCATF (command, " interval=%i", vl->interval);
+  if (vl->interval > 0.0)
+    SSTRCATF (command, " interval=%.3f", vl->interval);
 
-  if (vl->time > 0)
-    SSTRCATF (command, " %u", (unsigned int) vl->time);
+  if (vl->time > 0.0)
+    SSTRCATF (command, " %.3f", vl->time);
   else
     SSTRCAT (command, " N");
 
@@ -790,7 +782,7 @@ int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */
     else if (vl->values_types[i] == LCC_TYPE_GAUGE)
     {
       if (isnan (vl->values[i].gauge))
-        SSTRCPY (command, ":U");
+        SSTRCATF (command, ":U");
       else
         SSTRCATF (command, ":%g", vl->values[i].gauge);
     }
@@ -1015,7 +1007,7 @@ int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */
   char *type;
   char *type_instance;
 
-  string_copy = lcc_strdup (string);
+  string_copy = strdup (string);
   if (string_copy == NULL)
   {
     lcc_set_errno (c, ENOMEM);
@@ -1071,4 +1063,47 @@ int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */
   return (0);
 } /* }}} int lcc_string_to_identifier */
 
+int lcc_identifier_compare (const lcc_identifier_t *i0, /* {{{ */
+    const lcc_identifier_t *i1)
+{
+  int status;
+
+  if ((i0 == NULL) && (i1 == NULL))
+    return (0);
+  else if (i0 == NULL)
+    return (-1);
+  else if (i1 == NULL)
+    return (1);
+
+#define CMP_FIELD(f) do {         \
+  status = strcmp (i0->f, i1->f); \
+  if (status != 0)                \
+    return (status);              \
+} while (0);
+
+    CMP_FIELD (host);
+    CMP_FIELD (plugin);
+    CMP_FIELD (plugin_instance);
+    CMP_FIELD (type);
+    CMP_FIELD (type_instance);
+
+#undef CMP_FIELD
+
+    return (0);
+} /* }}} int lcc_identifier_compare */
+
+int lcc_sort_identifiers (lcc_connection_t *c, /* {{{ */
+    lcc_identifier_t *idents, size_t idents_num)
+{
+  if (idents == NULL)
+  {
+    lcc_set_errno (c, EINVAL);
+    return (-1);
+  }
+
+  qsort (idents, idents_num, sizeof (*idents),
+      (void *) lcc_identifier_compare);
+  return (0);
+} /* }}} int lcc_sort_identifiers */
+
 /* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/libcollectdclient/client.h b/src/libcollectdclient/client.h
deleted file mode 100644 (file)
index 11e7b13..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/**
- * libcollectdclient - src/libcollectdclient/client.h
- * Copyright (C) 2008  Florian octo Forster
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- *
- * Authors:
- *   Florian octo Forster <octo at verplant.org>
- **/
-
-#ifndef LIBCOLLECTD_COLLECTDCLIENT_H
-#define LIBCOLLECTD_COLLECTDCLIENT_H 1
-
-#include "lcc_features.h"
-
-/*
- * Includes (for data types)
- */
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#include <inttypes.h>
-#include <time.h>
-
-/*
- * Defines
- */
-#define LCC_NAME_LEN 64
-#define LCC_DEFAULT_PORT "25826"
-
-/*
- * Types
- */
-#define LCC_TYPE_COUNTER 0
-#define LCC_TYPE_GAUGE   1
-#define LCC_TYPE_DERIVE   2
-#define LCC_TYPE_ABSOLUTE   3
-
-LCC_BEGIN_DECLS
-
-typedef uint64_t counter_t;
-typedef double gauge_t;
-typedef uint64_t derive_t;
-typedef uint64_t absolute_t;
-
-union value_u
-{
-  counter_t counter;
-  gauge_t   gauge;
-  derive_t  derive;
-  absolute_t absolute;
-};
-typedef union value_u value_t;
-
-struct lcc_identifier_s
-{
-  char host[LCC_NAME_LEN];
-  char plugin[LCC_NAME_LEN];
-  char plugin_instance[LCC_NAME_LEN];
-  char type[LCC_NAME_LEN];
-  char type_instance[LCC_NAME_LEN];
-};
-typedef struct lcc_identifier_s lcc_identifier_t;
-#define LCC_IDENTIFIER_INIT { "localhost", "", "", "", "" }
-
-struct lcc_value_list_s
-{
-  value_t *values;
-  int     *values_types;
-  size_t   values_len;
-  time_t   time;
-  int      interval;
-  lcc_identifier_t identifier;
-};
-typedef struct lcc_value_list_s lcc_value_list_t;
-#define LCC_VALUE_LIST_INIT { NULL, 0, 0, 0, LCC_IDENTIFIER_INIT }
-
-struct lcc_connection_s;
-typedef struct lcc_connection_s lcc_connection_t;
-
-/*
- * Functions
- */
-int lcc_connect (const char *address, lcc_connection_t **ret_con);
-int lcc_disconnect (lcc_connection_t *c);
-#define LCC_DESTROY(c) do { lcc_disconnect (c); (c) = NULL; } while (0)
-
-int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident,
-    size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names);
-
-int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl);
-
-int lcc_flush (lcc_connection_t *c, const char *plugin,
-    lcc_identifier_t *ident, int timeout);
-
-int lcc_listval (lcc_connection_t *c,
-    lcc_identifier_t **ret_ident, size_t *ret_ident_num);
-
-/* TODO: putnotif */
-
-const char *lcc_strerror (lcc_connection_t *c);
-
-int lcc_identifier_to_string (lcc_connection_t *c,
-    char *string, size_t string_size, const lcc_identifier_t *ident);
-int lcc_string_to_identifier (lcc_connection_t *c,
-    lcc_identifier_t *ident, const char *string);
-
-LCC_END_DECLS
-
-/* vim: set sw=2 sts=2 et : */
-#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */
diff --git a/src/libcollectdclient/collectd/client.h b/src/libcollectdclient/collectd/client.h
new file mode 100644 (file)
index 0000000..6ae8598
--- /dev/null
@@ -0,0 +1,135 @@
+/**
+ * libcollectdclient - src/libcollectdclient/collectd/client.h
+ * Copyright (C) 2008-2012  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>
+ **/
+
+#ifndef LIBCOLLECTD_COLLECTDCLIENT_H
+#define LIBCOLLECTD_COLLECTDCLIENT_H 1
+
+#include "lcc_features.h"
+
+/*
+ * Includes (for data types)
+ */
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <inttypes.h>
+#include <time.h>
+
+/*
+ * Defines
+ */
+#define LCC_NAME_LEN 64
+#define LCC_DEFAULT_PORT "25826"
+
+/*
+ * Types
+ */
+#define LCC_TYPE_COUNTER 0
+#define LCC_TYPE_GAUGE   1
+#define LCC_TYPE_DERIVE   2
+#define LCC_TYPE_ABSOLUTE   3
+
+LCC_BEGIN_DECLS
+
+typedef uint64_t counter_t;
+typedef double gauge_t;
+typedef uint64_t derive_t;
+typedef uint64_t absolute_t;
+
+union value_u
+{
+  counter_t counter;
+  gauge_t   gauge;
+  derive_t  derive;
+  absolute_t absolute;
+};
+typedef union value_u value_t;
+
+struct lcc_identifier_s
+{
+  char host[LCC_NAME_LEN];
+  char plugin[LCC_NAME_LEN];
+  char plugin_instance[LCC_NAME_LEN];
+  char type[LCC_NAME_LEN];
+  char type_instance[LCC_NAME_LEN];
+};
+typedef struct lcc_identifier_s lcc_identifier_t;
+#define LCC_IDENTIFIER_INIT { "localhost", "", "", "", "" }
+
+struct lcc_value_list_s
+{
+  value_t *values;
+  int     *values_types;
+  size_t   values_len;
+  double   time;
+  double   interval;
+  lcc_identifier_t identifier;
+};
+typedef struct lcc_value_list_s lcc_value_list_t;
+#define LCC_VALUE_LIST_INIT { NULL, NULL, 0, 0, 0, LCC_IDENTIFIER_INIT }
+
+struct lcc_connection_s;
+typedef struct lcc_connection_s lcc_connection_t;
+
+/*
+ * Functions
+ */
+int lcc_connect (const char *address, lcc_connection_t **ret_con);
+int lcc_disconnect (lcc_connection_t *c);
+#define LCC_DESTROY(c) do { lcc_disconnect (c); (c) = NULL; } while (0)
+
+int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident,
+    size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names);
+
+int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl);
+
+int lcc_flush (lcc_connection_t *c, const char *plugin,
+    lcc_identifier_t *ident, int timeout);
+
+int lcc_listval (lcc_connection_t *c,
+    lcc_identifier_t **ret_ident, size_t *ret_ident_num);
+
+/* TODO: putnotif */
+
+const char *lcc_strerror (lcc_connection_t *c);
+
+int lcc_identifier_to_string (lcc_connection_t *c,
+    char *string, size_t string_size, const lcc_identifier_t *ident);
+int lcc_string_to_identifier (lcc_connection_t *c,
+    lcc_identifier_t *ident, const char *string);
+
+/* Compares the identifiers "i0" and "i1" and returns less than zero or greater
+ * than zero if "i0" is smaller than or greater than "i1", respectively. If
+ * "i0" and "i1" are identical, zero is returned. */
+int lcc_identifier_compare (const lcc_identifier_t *i0,
+    const lcc_identifier_t *i1);
+int lcc_sort_identifiers (lcc_connection_t *c,
+    lcc_identifier_t *idents, size_t idents_num);
+
+LCC_END_DECLS
+
+/* vim: set sw=2 sts=2 et : */
+#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */
diff --git a/src/libcollectdclient/collectd/lcc_features.h.in b/src/libcollectdclient/collectd/lcc_features.h.in
new file mode 100644 (file)
index 0000000..0e6fcd4
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * libcollectdclient - src/libcollectdclient/lcc_features.h
+ * Copyright (C) 2009  Sebastian Harl
+ *
+ * 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:
+ *   Sebastian tokkee Harl <sh at tokkee.org>
+ **/
+
+#ifndef LIBCOLLECTD_LCC_FEATURES_H
+#define LIBCOLLECTD_LCC_FEATURES_H 1
+
+#ifdef __cplusplus
+# define LCC_BEGIN_DECLS extern "C" {
+# define LCC_END_DECLS   }
+#else
+# define LCC_BEGIN_DECLS
+# define LCC_END_DECLS
+#endif
+
+#define LCC_API_VERSION 0
+
+#define LCC_VERSION_MAJOR @LCC_VERSION_MAJOR@
+#define LCC_VERSION_MINOR @LCC_VERSION_MINOR@
+#define LCC_VERSION_PATCH @LCC_VERSION_PATCH@
+
+#define LCC_VERSION_EXTRA "@LCC_VERSION_EXTRA@"
+
+#define LCC_VERSION_STRING "@LCC_VERSION_STRING@"
+
+#define LCC_VERSION_ENCODE(major, minor, patch) \
+       ((major) * 10000 + (minor) * 100 + (patch))
+
+#define LCC_VERSION \
+       LCC_VERSION_ENCODE(LCC_VERSION_MAJOR, LCC_VERSION_MINOR, LCC_VERSION_PATCH)
+
+LCC_BEGIN_DECLS
+
+unsigned int lcc_version (void);
+
+const char *lcc_version_string (void);
+
+const char *lcc_version_extra (void);
+
+LCC_END_DECLS
+
+#endif /* ! LIBCOLLECTD_LCC_FEATURES_H */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
diff --git a/src/libcollectdclient/collectd/network.h b/src/libcollectdclient/collectd/network.h
new file mode 100644 (file)
index 0000000..3962666
--- /dev/null
@@ -0,0 +1,83 @@
+/**
+ * collectd - src/libcollectdclient/collectd/network.h
+ * Copyright (C) 2005-2012  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>
+ **/
+
+#ifndef LIBCOLLECTDCLIENT_NETWORK_H
+#define LIBCOLLECTDCLIENT_NETWORK_H 1
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "client.h"
+
+#define NET_DEFAULT_V4_ADDR "239.192.74.66"
+#define NET_DEFAULT_V6_ADDR "ff18::efc0:4a42"
+#define NET_DEFAULT_PORT    "25826"
+
+struct lcc_network_s;
+typedef struct lcc_network_s lcc_network_t;
+
+struct lcc_server_s;
+typedef struct lcc_server_s lcc_server_t;
+
+enum lcc_security_level_e
+{
+  NONE,
+  SIGN,
+  ENCRYPT
+};
+typedef enum lcc_security_level_e lcc_security_level_t;
+
+/*
+ * Create / destroy object
+ */
+lcc_network_t *lcc_network_create (void);
+void lcc_network_destroy (lcc_network_t *net);
+
+/* 
+ * Add servers
+ */
+lcc_server_t *lcc_server_create (lcc_network_t *net,
+    const char *node, const char *service);
+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_security_level (lcc_server_t *srv,
+    lcc_security_level_t level,
+    const char *username, const char *password);
+
+/*
+ * Send data
+ */
+int lcc_network_values_send (lcc_network_t *net,
+    const lcc_value_list_t *vl);
+#if 0
+int lcc_network_notification_send (lcc_network_t *net,
+    const lcc_notification_t *notif);
+#endif
+
+/* vim: set sw=2 sts=2 et : */
+#endif /* LIBCOLLECTDCLIENT_NETWORK_H */
diff --git a/src/libcollectdclient/collectd/network_buffer.h b/src/libcollectdclient/collectd/network_buffer.h
new file mode 100644 (file)
index 0000000..edf49ff
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * collectd - src/libcollectdclient/collectd/network_buffer.h
+ * Copyright (C) 2010-2012  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>
+ **/
+
+#ifndef LIBCOLLECTDCLIENT_NETWORK_BUFFER_H
+#define LIBCOLLECTDCLIENT_NETWORK_BUFFER_H 1
+
+/* FIXME */
+#include "client.h"
+#include "network.h"
+
+/* Ethernet frame - (IPv6 header + UDP header) */
+#define LCC_NETWORK_BUFFER_SIZE_DEFAULT 1452
+
+struct lcc_network_buffer_s;
+typedef struct lcc_network_buffer_s lcc_network_buffer_t;
+
+lcc_network_buffer_t *lcc_network_buffer_create (size_t size);
+void lcc_network_buffer_destroy (lcc_network_buffer_t *nb);
+
+int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb,
+    lcc_security_level_t level,
+    const char *user, const char *password);
+
+int lcc_network_buffer_initialize (lcc_network_buffer_t *nb);
+int lcc_network_buffer_finalize (lcc_network_buffer_t *nb);
+
+int lcc_network_buffer_add_value (lcc_network_buffer_t *nb,
+    const lcc_value_list_t *vl);
+
+int lcc_network_buffer_get (lcc_network_buffer_t *nb,
+    void *buffer, size_t *buffer_size);
+
+#endif /* LIBCOLLECTDCLIENT_NETWORK_BUFFER_H */
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/libcollectdclient/lcc_features.h.in b/src/libcollectdclient/lcc_features.h.in
deleted file mode 100644 (file)
index 3916a17..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * libcollectdclient - src/libcollectdclient/lcc_features.h
- * Copyright (C) 2009  Sebastian Harl
- *
- * 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:
- *   Sebastian tokkee Harl <sh at tokkee.org>
- **/
-
-#ifndef LIBCOLLECTD_LCC_FEATURES_H
-#define LIBCOLLECTD_LCC_FEATURES_H 1
-
-#ifdef __cplusplus
-# define LCC_BEGIN_DECLS extern "C" {
-# define LCC_END_DECLS   }
-#else
-# define LCC_BEGIN_DECLS
-# define LCC_END_DECLS
-#endif
-
-#define LCC_API_VERSION 0
-
-#define LCC_VERSION_MAJOR @LCC_VERSION_MAJOR@
-#define LCC_VERSION_MINOR @LCC_VERSION_MINOR@
-#define LCC_VERSION_PATCH @LCC_VERSION_PATCH@
-
-#define LCC_VERSION_EXTRA "@LCC_VERSION_EXTRA@"
-
-#define LCC_VERSION_STRING "@LCC_VERSION_STRING@"
-
-#define LCC_VERSION_ENCODE(major, minor, patch) \
-       ((major) * 10000 + (minor) * 100 + (patch))
-
-#define LCC_VERSION \
-       LCC_VERSION_ENCODE(LCC_VERSION_MAJOR, LCC_VERSION_MINOR, LCC_VERSION_PATCH)
-
-LCC_BEGIN_DECLS
-
-unsigned int lcc_version (void);
-
-const char *lcc_version_string (void);
-
-const char *lcc_version_extra (void);
-
-LCC_END_DECLS
-
-#endif /* ! LIBCOLLECTD_LCC_FEATURES_H */
-
-/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
-
diff --git a/src/libcollectdclient/network.c b/src/libcollectdclient/network.c
new file mode 100644 (file)
index 0000000..6b6450c
--- /dev/null
@@ -0,0 +1,404 @@
+/**
+ * collectd - src/libcollectdclient/network.c
+ * Copyright (C) 2005-2012  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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#include "collectd/network.h"
+#include "collectd/network_buffer.h"
+
+/*
+ * Private data types
+ */
+struct lcc_network_s
+{
+  lcc_server_t *servers;
+};
+
+struct lcc_server_s
+{
+  char *node;
+  char *service;
+
+  int ttl;
+  lcc_security_level_t security_level;
+  char *username;
+  char *password;
+
+  int fd;
+  struct sockaddr *sa;
+  socklen_t sa_len;
+
+  lcc_network_buffer_t *buffer;
+
+  lcc_server_t *next;
+};
+
+/*
+ * Private functions
+ */
+static int server_close_socket (lcc_server_t *srv) /* {{{ */
+{
+  if (srv == NULL)
+    return (EINVAL);
+
+  if (srv->fd < 0)
+    return (0);
+
+  close (srv->fd);
+  free (srv->sa);
+  srv->sa = NULL;
+  srv->sa_len = 0;
+
+  return (0);
+} /* }}} int server_close_socket */
+
+static void int_server_destroy (lcc_server_t *srv) /* {{{ */
+{
+  lcc_server_t *next;
+
+  if (srv == NULL)
+    return;
+
+  server_close_socket (srv);
+
+  next = srv->next;
+
+  if (srv->fd >= 0)
+  {
+    close (srv->fd);
+    srv->fd = -1;
+  }
+
+  free (srv->node);
+  free (srv->service);
+  free (srv->username);
+  free (srv->password);
+  free (srv);
+
+  int_server_destroy (next);
+} /* }}} void int_server_destroy */
+
+static int server_open_socket (lcc_server_t *srv) /* {{{ */
+{
+  struct addrinfo ai_hints = { 0 };
+  struct addrinfo *ai_list = NULL;
+  struct addrinfo *ai_ptr;
+  int status;
+
+  if (srv == NULL)
+    return (EINVAL);
+
+  if (srv->fd >= 0)
+    server_close_socket (srv);
+
+#ifdef AI_ADDRCONFIG
+  ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+  ai_hints.ai_family   = AF_UNSPEC;
+  ai_hints.ai_socktype = SOCK_DGRAM;
+
+  status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
+  if (status != 0)
+    return (status);
+  assert (ai_list != NULL);
+
+  for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    srv->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (srv->fd < 0)
+      continue;
+
+    if (ai_ptr->ai_family == AF_INET)
+    {
+
+      struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
+      int optname;
+
+      if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+        optname = IP_MULTICAST_TTL;
+      else
+        optname = IP_TTL;
+
+      setsockopt (srv->fd, IPPROTO_IP, optname,
+          &srv->ttl,
+          sizeof (srv->ttl));
+    }
+    else if (ai_ptr->ai_family == AF_INET6)
+    {
+      /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
+      struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
+      int optname;
+
+      if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+        optname = IPV6_MULTICAST_HOPS;
+      else
+        optname = IPV6_UNICAST_HOPS;
+
+      setsockopt (srv->fd, IPPROTO_IPV6, optname,
+          &srv->ttl,
+          sizeof (srv->ttl));
+    }
+
+    srv->sa = malloc (ai_ptr->ai_addrlen);
+    if (srv->sa == NULL)
+    {
+      close (srv->fd);
+      srv->fd = -1;
+      continue;
+    }
+
+    memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    srv->sa_len = ai_ptr->ai_addrlen;
+    break;
+  }
+
+  freeaddrinfo (ai_list);
+
+  if (srv->fd < 0)
+    return (-1);
+  return (0);
+} /* }}} int server_open_socket */
+
+static int server_send_buffer (lcc_server_t *srv) /* {{{ */
+{
+  char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
+  size_t buffer_size;
+  int status;
+
+  if (srv->fd < 0)
+  {
+    status = server_open_socket (srv);
+    if (status != 0)
+      return (status);
+  }
+
+  memset (buffer, 0, sizeof (buffer));
+  buffer_size = sizeof (buffer);
+
+  lcc_network_buffer_finalize (srv->buffer);
+  status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size);
+  lcc_network_buffer_initialize (srv->buffer);
+
+  if (status != 0)
+    return (status);
+
+  if (buffer_size > sizeof (buffer))
+    buffer_size = sizeof (buffer);
+
+  while (42)
+  {
+    assert (srv->fd >= 0);
+    assert (srv->sa != NULL);
+    status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0,
+        srv->sa, srv->sa_len);
+    if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
+      continue;
+
+    break;
+  }
+
+  if (status < 0)
+    return (status);
+  return (0);
+} /* }}} int server_send_buffer */
+
+static int server_value_add (lcc_server_t *srv, /* {{{ */
+    const lcc_value_list_t *vl)
+{
+  int status;
+
+  status = lcc_network_buffer_add_value (srv->buffer, vl);
+  if (status == 0)
+    return (0);
+
+  server_send_buffer (srv);
+  return (lcc_network_buffer_add_value (srv->buffer, vl));
+} /* }}} int server_value_add */
+
+/*
+ * Public functions
+ */
+lcc_network_t *lcc_network_create (void) /* {{{ */
+{
+  lcc_network_t *net;
+
+  net = malloc (sizeof (*net));
+  if (net == NULL)
+    return (NULL);
+  memset (net, 0, sizeof (*net));
+
+  net->servers = NULL;
+
+  return (net);
+} /* }}} lcc_network_t *lcc_network_create */
+
+void lcc_network_destroy (lcc_network_t *net) /* {{{ */
+{
+  if (net == NULL)
+    return;
+  int_server_destroy (net->servers);
+  free (net);
+} /* }}} void lcc_network_destroy */
+
+lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
+    const char *node, const char *service)
+{
+  lcc_server_t *srv;
+
+  if ((net == NULL) || (node == NULL))
+    return (NULL);
+  if (service == NULL)
+    service = NET_DEFAULT_PORT;
+
+  srv = malloc (sizeof (*srv));
+  if (srv == NULL)
+    return (NULL);
+  memset (srv, 0, sizeof (*srv));
+
+  srv->fd = -1;
+  srv->security_level = NONE;
+  srv->username = NULL;
+  srv->password = NULL;
+  srv->next = NULL;
+
+  srv->node = strdup (node);
+  if (srv->node == NULL)
+  {
+    free (srv);
+    return (NULL);
+  }
+
+  srv->service = strdup (service);
+  if (srv->service == NULL)
+  {
+    free (srv->node);
+    free (srv);
+    return (NULL);
+  }
+
+  srv->buffer = lcc_network_buffer_create (/* size = */ 0);
+  if (srv->buffer == NULL)
+  {
+    free (srv->service);
+    free (srv->node);
+    free (srv);
+    return (NULL);
+  }
+
+  if (net->servers == NULL)
+  {
+    net->servers = srv;
+  }
+  else
+  {
+    lcc_server_t *last = net->servers;
+
+    while (last->next != NULL)
+      last = last->next;
+
+    last->next = srv;
+  }
+
+  return (srv);
+} /* }}} lcc_server_t *lcc_server_create */
+
+int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
+{
+  if ((net == NULL) || (srv == NULL))
+    return (EINVAL);
+
+  if (net->servers == srv)
+  {
+    net->servers = srv->next;
+    srv->next = NULL;
+  }
+  else
+  {
+    lcc_server_t *prev = net->servers;
+
+    while ((prev != NULL) && (prev->next != srv))
+      prev = prev->next;
+
+    if (prev == NULL)
+      return (ENOENT);
+
+    prev->next = srv->next;
+    srv->next = NULL;
+  }
+
+  int_server_destroy (srv);
+
+  return (0);
+} /* }}} int lcc_server_destroy */
+
+int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
+{
+  if (srv == NULL)
+    return (EINVAL);
+
+  srv->ttl = (int) ttl;
+
+  return (0);
+} /* }}} int lcc_server_set_ttl */
+
+int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
+    lcc_security_level_t level,
+    const char *username, const char *password)
+{
+  return (lcc_network_buffer_set_security_level (srv->buffer,
+        level, username, password));
+} /* }}} int lcc_server_set_security_level */
+
+int lcc_network_values_send (lcc_network_t *net, /* {{{ */
+    const lcc_value_list_t *vl)
+{
+  lcc_server_t *srv;
+
+  if ((net == NULL) || (vl == NULL))
+    return (EINVAL);
+
+  for (srv = net->servers; srv != NULL; srv = srv->next)
+    server_value_add (srv, vl);
+
+  return (0);
+} /* }}} int lcc_network_values_send */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/libcollectdclient/network_buffer.c b/src/libcollectdclient/network_buffer.c
new file mode 100644 (file)
index 0000000..61c7c22
--- /dev/null
@@ -0,0 +1,836 @@
+/**
+ * collectd - src/libcollectdclient/network_buffer.c
+ * Copyright (C) 2010-2012  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 "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include <errno.h>
+#include <arpa/inet.h> /* htons */
+
+#include <pthread.h>
+
+#if HAVE_LIBGCRYPT
+# include <pthread.h>
+# if defined __APPLE__
+/* default xcode compiler throws warnings even when deprecated functionality
+ * is not used. -Werror breaks the build because of erroneous warnings.
+ * http://stackoverflow.com/questions/10556299/compiler-warnings-with-libgcrypt-v1-5-0/12830209#12830209
+ */
+#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+# endif
+/* FreeBSD's copy of libgcrypt extends the existing GCRYPT_NO_DEPRECATED
+ * to properly hide all deprecated functionality.
+ * http://svnweb.freebsd.org/ports/head/security/libgcrypt/files/patch-src__gcrypt.h.in
+ */
+# define GCRYPT_NO_DEPRECATED
+# include <gcrypt.h>
+# if defined __APPLE__
+/* Re enable deprecation warnings */
+#  pragma GCC diagnostic warning "-Wdeprecated-declarations"
+# endif
+# if GCRYPT_VERSION_NUMBER < 0x010600
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+# endif
+#endif
+
+#include "collectd/network_buffer.h"
+
+#define TYPE_HOST            0x0000
+#define TYPE_TIME            0x0001
+#define TYPE_TIME_HR         0x0008
+#define TYPE_PLUGIN          0x0002
+#define TYPE_PLUGIN_INSTANCE 0x0003
+#define TYPE_TYPE            0x0004
+#define TYPE_TYPE_INSTANCE   0x0005
+#define TYPE_VALUES          0x0006
+#define TYPE_INTERVAL        0x0007
+#define TYPE_INTERVAL_HR     0x0009
+
+/* Types to transmit notifications */
+#define TYPE_MESSAGE         0x0100
+#define TYPE_SEVERITY        0x0101
+
+#define TYPE_SIGN_SHA256     0x0200
+#define TYPE_ENCR_AES256     0x0210
+
+#define PART_SIGNATURE_SHA256_SIZE 36
+#define PART_ENCRYPTION_AES256_SIZE 42
+
+#define ADD_GENERIC(nb,srcptr,size) do {         \
+  assert ((size) <= (nb)->free);                 \
+  memcpy ((nb)->ptr, (srcptr), (size));          \
+  (nb)->ptr += (size);                           \
+  (nb)->free -= (size);                          \
+} while (0)
+
+#define ADD_STATIC(nb,var) \
+  ADD_GENERIC(nb,&(var),sizeof(var));
+
+/*
+ * Data types
+ */
+struct lcc_network_buffer_s
+{
+  char *buffer;
+  size_t size;
+
+  lcc_value_list_t state;
+  char *ptr;
+  size_t free;
+
+  lcc_security_level_t seclevel;
+  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 { \
+  strncpy ((dst), (src), (sz));   \
+  (dst)[(sz) - 1] = 0;            \
+} while (0)
+
+/*
+ * Private functions
+ */
+static _Bool have_gcrypt (void) /* {{{ */
+{
+  static _Bool result = 0;
+  static _Bool need_init = 1;
+
+  if (!need_init)
+    return (result);
+  need_init = 0;
+
+#if HAVE_LIBGCRYPT
+# if GCRYPT_VERSION_NUMBER < 0x010600
+  gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+# endif
+
+  if (!gcry_check_version (GCRYPT_VERSION))
+    return (0);
+
+  gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+  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;
+
+  uint32_t hi;
+  uint32_t lo;
+
+  if (config == 0)
+  {
+    uint16_t h = 0x1234;
+    uint16_t n = htons (h);
+
+    if (h == n)
+      config = 1;
+    else
+      config = 2;
+  }
+
+  if (config == 1)
+    return (val);
+
+  hi = (uint32_t) (val >> 32);
+  lo = (uint32_t) (val & 0x00000000FFFFFFFF);
+
+  hi = htonl (hi);
+  lo = htonl (lo);
+
+  return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
+} /* }}} uint64_t htonll */
+#endif
+
+static double htond (double val) /* {{{ */
+{
+  static int config = 0;
+
+  union { uint8_t byte[8]; double floating; } in;
+  union { uint8_t byte[8]; double floating; } out;
+
+  if (config == 0)
+  {
+    double d = 8.642135e130;
+    uint8_t c[8];
+
+    memcpy (c, &d, 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))
+      config = 1; /* need nothing */
+    else if ((c[7] == 0x2f) && (c[6] == 0x25)
+        && (c[5] == 0xc0) && (c[4] == 0xc7)
+        && (c[3] == 0x43) && (c[2] == 0x2b)
+        && (c[1] == 0x1f) && (c[0] == 0x5b))
+      config = 2; /* endian flip */
+    else if ((c[4] == 0x2f) && (c[5] == 0x25)
+        && (c[6] == 0xc0) && (c[7] == 0xc7)
+        && (c[0] == 0x43) && (c[1] == 0x2b)
+        && (c[2] == 0x1f) && (c[3] == 0x5b))
+      config = 3; /* int swap */
+    else
+      config = 4;
+  }
+
+  if (isnan (val))
+  {
+    out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
+    out.byte[4] = out.byte[5] = 0x00;
+    out.byte[6] = 0xf8;
+    out.byte[7] = 0x7f;
+    return (out.floating);
+  }
+  else if (config == 1)
+    return (val);
+  else if (config == 2)
+  {
+    in.floating = val;
+    out.byte[0] = in.byte[7];
+    out.byte[1] = in.byte[6];
+    out.byte[2] = in.byte[5];
+    out.byte[3] = in.byte[4];
+    out.byte[4] = in.byte[3];
+    out.byte[5] = in.byte[2];
+    out.byte[6] = in.byte[1];
+    out.byte[7] = in.byte[0];
+    return (out.floating);
+  }
+  else if (config == 3)
+  {
+    in.floating = val;
+    out.byte[0] = in.byte[4];
+    out.byte[1] = in.byte[5];
+    out.byte[2] = in.byte[6];
+    out.byte[3] = in.byte[7];
+    out.byte[4] = in.byte[0];
+    out.byte[5] = in.byte[1];
+    out.byte[6] = in.byte[2];
+    out.byte[7] = in.byte[3];
+    return (out.floating);
+  }
+  else
+  {
+    /* If in doubt, just copy the value back to the caller. */
+    return (val);
+  }
+} /* }}} double htond */
+
+static int nb_add_values (char **ret_buffer, /* {{{ */
+    size_t *ret_buffer_len,
+    const lcc_value_list_t *vl)
+{
+  char *packet_ptr;
+  size_t packet_len;
+
+  uint16_t      pkg_type;
+  uint16_t      pkg_length;
+  uint16_t      pkg_num_values;
+  uint8_t       pkg_values_types[vl->values_len];
+  value_t       pkg_values[vl->values_len];
+
+  size_t offset;
+  size_t i;
+
+  packet_len = sizeof (pkg_type) + sizeof (pkg_length)
+    + sizeof (pkg_num_values)
+    + sizeof (pkg_values_types)
+    + sizeof (pkg_values);
+
+  if (*ret_buffer_len < packet_len)
+    return (ENOMEM);
+
+  pkg_type = htons (TYPE_VALUES);
+  pkg_length = htons ((uint16_t) packet_len);
+  pkg_num_values = htons ((uint16_t) vl->values_len);
+
+  for (i = 0; i < vl->values_len; i++)
+  {
+    pkg_values_types[i] = (uint8_t) vl->values_types[i];
+    switch (vl->values_types[i])
+    {
+      case LCC_TYPE_COUNTER:
+        pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
+        break;
+
+      case LCC_TYPE_GAUGE:
+        pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
+        break;
+
+      case LCC_TYPE_DERIVE:
+        pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
+        break;
+
+      case LCC_TYPE_ABSOLUTE:
+        pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
+        break;
+
+      default:
+        return (EINVAL);
+    } /* switch (vl->values_types[i]) */
+  } /* for (vl->values_len) */
+
+  /*
+   * Use `memcpy' to write everything to the buffer, because the pointer
+   * may be unaligned and some architectures, such as SPARC, can't handle
+   * that.
+   */
+  packet_ptr = *ret_buffer;
+  offset = 0;
+  memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
+  offset += sizeof (pkg_type);
+  memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
+  offset += sizeof (pkg_length);
+  memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
+  offset += sizeof (pkg_num_values);
+  memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
+  offset += sizeof (pkg_values_types);
+  memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
+  offset += sizeof (pkg_values);
+
+  assert (offset == packet_len);
+
+  *ret_buffer = packet_ptr + packet_len;
+  *ret_buffer_len -= packet_len;
+  return (0);
+} /* }}} int nb_add_values */
+
+static int nb_add_number (char **ret_buffer, /* {{{ */
+    size_t *ret_buffer_len,
+    uint16_t type, uint64_t value)
+{
+  char *packet_ptr;
+  size_t packet_len;
+
+  uint16_t pkg_type;
+  uint16_t pkg_length;
+  uint64_t pkg_value;
+
+  size_t offset;
+
+  packet_len = sizeof (pkg_type)
+    + sizeof (pkg_length)
+    + sizeof (pkg_value);
+
+  if (*ret_buffer_len < packet_len)
+    return (ENOMEM);
+
+  pkg_type = htons (type);
+  pkg_length = htons ((uint16_t) packet_len);
+  pkg_value = htonll (value);
+
+  packet_ptr = *ret_buffer;
+  offset = 0;
+  memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
+  offset += sizeof (pkg_type);
+  memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
+  offset += sizeof (pkg_length);
+  memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
+  offset += sizeof (pkg_value);
+
+  assert (offset == packet_len);
+
+  *ret_buffer = packet_ptr + packet_len;
+  *ret_buffer_len -= packet_len;
+  return (0);
+} /* }}} int nb_add_number */
+
+static int nb_add_time (char **ret_buffer, /* {{{ */
+    size_t *ret_buffer_len,
+    uint16_t type, double value)
+{
+  /* Convert to collectd's "cdtime" representation. */
+  uint64_t cdtime_value = (uint64_t) (value * 1073741824.0);
+  return (nb_add_number (ret_buffer, ret_buffer_len, type, cdtime_value));
+} /* }}} int nb_add_time */
+
+static int nb_add_string (char **ret_buffer, /* {{{ */
+    size_t *ret_buffer_len,
+    uint16_t type, const char *str, size_t str_len)
+{
+  char *packet_ptr;
+  size_t packet_len;
+
+  uint16_t pkg_type;
+  uint16_t pkg_length;
+
+  size_t offset;
+
+  packet_len = sizeof (pkg_type)
+    + sizeof (pkg_length)
+    + str_len + 1;
+  if (*ret_buffer_len < packet_len)
+    return (ENOMEM);
+
+  pkg_type = htons (type);
+  pkg_length = htons ((uint16_t) packet_len);
+
+  packet_ptr = *ret_buffer;
+  offset = 0;
+  memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
+  offset += sizeof (pkg_type);
+  memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
+  offset += sizeof (pkg_length);
+  memcpy (packet_ptr + offset, str, str_len);
+  offset += str_len;
+  memset (packet_ptr + offset, 0, 1);
+  offset += 1;
+
+  assert (offset == packet_len);
+
+  *ret_buffer = packet_ptr + packet_len;
+  *ret_buffer_len -= packet_len;
+  return (0);
+} /* }}} int nb_add_string */
+
+static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
+    const lcc_value_list_t *vl)
+{
+  char *buffer = nb->ptr;
+  size_t buffer_size = nb->free;
+
+  const lcc_identifier_t *ident_src;
+  lcc_identifier_t *ident_dst;
+
+  ident_src = &vl->identifier;
+  ident_dst = &nb->state.identifier;
+
+  if (strcmp (ident_dst->host, ident_src->host) != 0)
+  {
+    if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
+          ident_src->host, strlen (ident_src->host)) != 0)
+      return (-1);
+    SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
+  }
+
+  if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
+  {
+    if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
+          ident_src->plugin, strlen (ident_src->plugin)) != 0)
+      return (-1);
+    SSTRNCPY (ident_dst->plugin, ident_src->plugin,
+        sizeof (ident_dst->plugin));
+  }
+
+  if (strcmp (ident_dst->plugin_instance,
+        ident_src->plugin_instance) != 0)
+  {
+    if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
+          ident_src->plugin_instance,
+          strlen (ident_src->plugin_instance)) != 0)
+      return (-1);
+    SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
+        sizeof (ident_dst->plugin_instance));
+  }
+
+  if (strcmp (ident_dst->type, ident_src->type) != 0)
+  {
+    if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
+          ident_src->type, strlen (ident_src->type)) != 0)
+      return (-1);
+    SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
+  }
+
+  if (strcmp (ident_dst->type_instance,
+        ident_src->type_instance) != 0)
+  {
+    if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
+          ident_src->type_instance,
+          strlen (ident_src->type_instance)) != 0)
+      return (-1);
+    SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
+        sizeof (ident_dst->type_instance));
+  }
+
+  if (nb->state.time != vl->time)
+  {
+    if (nb_add_time (&buffer, &buffer_size, TYPE_TIME_HR, vl->time))
+      return (-1);
+    nb->state.time = vl->time;
+  }
+
+  if (nb->state.interval != vl->interval)
+  {
+    if (nb_add_time (&buffer, &buffer_size, TYPE_INTERVAL_HR, vl->interval))
+      return (-1);
+    nb->state.interval = vl->interval;
+  }
+
+  if (nb_add_values (&buffer, &buffer_size, vl) != 0)
+    return (-1);
+
+  nb->ptr = buffer;
+  nb->free = buffer_size;
+  return (0);
+} /* }}} int nb_add_value_list */
+
+#if HAVE_LIBGCRYPT
+static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
+{
+  char *buffer;
+  size_t buffer_size;
+
+  gcry_md_hd_t hd;
+  gcry_error_t err;
+  unsigned char *hash;
+  const size_t hash_length = 32;
+
+  /* The type, length and username have already been filled in by
+   * "lcc_network_buffer_initialize". All we do here is calculate the hash over
+   * the username and the data and add the hash value to the buffer. */
+
+  buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
+  assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
+  buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
+
+  hd = NULL;
+  err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+  if (err != 0)
+    return (-1);
+
+  assert (nb->password != NULL);
+  err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
+  if (err != 0)
+  {
+    gcry_md_close (hd);
+    return (-1);
+  }
+
+  gcry_md_write (hd, buffer, buffer_size);
+  hash = gcry_md_read (hd, GCRY_MD_SHA256);
+  if (hash == NULL)
+  {
+    gcry_md_close (hd);
+    return (-1);
+  }
+
+  assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
+  memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
+
+  gcry_md_close (hd);
+  return (0);
+} /* }}} int nb_add_signature */
+
+static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
+{
+  size_t package_length;
+  char *encr_ptr; /* pointer to data being encrypted */
+  size_t encr_size;
+
+  char *hash_ptr; /* pointer to data being hashed */
+  size_t hash_size;
+  char hash[20];
+
+  uint16_t pkg_length;
+  gcry_error_t err;
+
+  /* Fill in the package length */
+  package_length = nb->size - nb->free;
+  pkg_length = htons ((uint16_t) package_length);
+  memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
+
+  /* Calculate what to hash */
+  hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
+  hash_size = package_length - nb->encr_header_len;
+
+  /* Calculate what to encrypt */
+  encr_ptr = hash_ptr - sizeof (hash);
+  encr_size = hash_size + sizeof (hash);
+
+  /* Calculate the SHA-1 hash */
+  gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size);
+  memcpy (encr_ptr, hash, sizeof (hash));
+
+  if (nb->encr_cypher == NULL)
+  {
+    unsigned char password_hash[32];
+
+    err = gcry_cipher_open (&nb->encr_cypher,
+        GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
+    if (err != 0)
+      return (-1);
+
+    /* Calculate our 256bit key used for AES */
+    gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
+        nb->password, strlen (nb->password));
+
+    err = gcry_cipher_setkey (nb->encr_cypher,
+        password_hash, sizeof (password_hash));
+    if (err != 0)
+    {
+      gcry_cipher_close (nb->encr_cypher);
+      nb->encr_cypher = NULL;
+      return (-1);
+    }
+  }
+  else /* if (nb->encr_cypher != NULL) */
+  {
+    gcry_cipher_reset (nb->encr_cypher);
+  }
+
+  /* Set the initialization vector */
+  err = gcry_cipher_setiv (nb->encr_cypher,
+      nb->encr_iv, sizeof (nb->encr_iv));
+  if (err != 0)
+  {
+    gcry_cipher_close (nb->encr_cypher);
+    nb->encr_cypher = NULL;
+    return (-1);
+  }
+
+  /* Encrypt the buffer in-place */
+  err = gcry_cipher_encrypt (nb->encr_cypher,
+      encr_ptr, encr_size,
+      /* in = */ NULL, /* in len = */ 0);
+  if (err != 0)
+  {
+    gcry_cipher_close (nb->encr_cypher);
+    nb->encr_cypher = NULL;
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int nb_add_encryption */
+#endif
+
+/*
+ * Public functions
+ */
+lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
+{
+  lcc_network_buffer_t *nb;
+
+  if (size == 0)
+    size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
+
+  if (size < 128)
+  {
+    errno = EINVAL;
+    return (NULL);
+  }
+
+  nb = malloc (sizeof (*nb));
+  if (nb == NULL)
+    return (NULL);
+  memset (nb, 0, sizeof (*nb));
+
+  nb->size = size;
+  nb->buffer = malloc (nb->size);
+  if (nb->buffer == NULL)
+  {
+    free (nb);
+    return (NULL);
+  }
+  memset (nb->buffer, 0, nb->size);
+
+  nb->ptr = nb->buffer;
+  nb->free = nb->size;
+
+  nb->seclevel = NONE;
+  nb->username = NULL;
+  nb->password = NULL;
+
+  return (nb);
+} /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
+
+void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
+{
+  if (nb == NULL)
+    return;
+
+  free (nb->buffer);
+  free (nb);
+} /* }}} void lcc_network_buffer_destroy */
+
+int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
+    lcc_security_level_t level,
+    const char *username, const char *password)
+{
+  char *username_copy;
+  char *password_copy;
+
+  if (level == NONE)
+  {
+    free (nb->username);
+    free (nb->password);
+    nb->username = NULL;
+    nb->password = NULL;
+    nb->seclevel = NONE;
+    lcc_network_buffer_initialize (nb);
+    return (0);
+  }
+
+  if (!have_gcrypt ())
+    return (ENOTSUP);
+
+  username_copy = strdup (username);
+  password_copy = strdup (password);
+  if ((username_copy == NULL) || (password_copy == NULL))
+  {
+    free (username_copy);
+    free (password_copy);
+    return (ENOMEM);
+  }
+
+  free (nb->username);
+  free (nb->password);
+  nb->username = username_copy;
+  nb->password = password_copy;
+  nb->seclevel = level;
+
+  lcc_network_buffer_initialize (nb);
+  return (0);
+} /* }}} int lcc_network_buffer_set_security_level */
+
+int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
+{
+  if (nb == NULL)
+    return (EINVAL);
+
+  memset (nb->buffer, 0, nb->size);
+  memset (&nb->state, 0, sizeof (nb->state));
+  nb->ptr = nb->buffer;
+  nb->free = nb->size;
+
+#if HAVE_LIBGCRYPT
+  if (nb->seclevel == SIGN)
+  {
+    size_t username_len;
+    uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
+    uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
+
+    assert (nb->username != NULL);
+    username_len = strlen (nb->username);
+    pkg_length = htons (pkg_length + ((uint16_t) username_len));
+
+    /* Fill in everything but the hash value here. */
+    memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
+    memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
+    nb->ptr += PART_SIGNATURE_SHA256_SIZE;
+    nb->free -= PART_SIGNATURE_SHA256_SIZE;
+
+    memcpy (nb->ptr, nb->username, username_len);
+    nb->ptr += username_len;
+    nb->free -= username_len;
+  }
+  else if (nb->seclevel == ENCRYPT)
+  {
+    size_t username_length = strlen (nb->username);
+    uint16_t pkg_type = htons (TYPE_ENCR_AES256);
+    uint16_t pkg_length = 0; /* Filled in in finalize. */
+    uint16_t pkg_user_len = htons ((uint16_t) username_length);
+    char hash[20];
+
+    nb->encr_header_len = username_length;
+    nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
+
+    gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
+        GCRY_STRONG_RANDOM);
+
+    /* Filled in in finalize. */
+    memset (hash, 0, sizeof (hash));
+
+    ADD_STATIC (nb, pkg_type);
+    ADD_STATIC (nb, pkg_length);
+    ADD_STATIC (nb, pkg_user_len);
+    ADD_GENERIC (nb, nb->username, username_length);
+    ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv));
+    ADD_GENERIC (nb, hash, sizeof (hash));
+    assert ((nb->encr_header_len + nb->free) == nb->size);
+  }
+#endif
+
+  return (0);
+} /* }}} int lcc_network_buffer_initialize */
+
+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 */
+
+int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
+    const lcc_value_list_t *vl)
+{
+  int status;
+
+  if ((nb == NULL) || (vl == NULL))
+    return (EINVAL);
+
+  status = nb_add_value_list (nb, vl);
+  return (status);
+} /* }}} int lcc_network_buffer_add_value */
+
+int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
+    void *buffer, size_t *buffer_size)
+{
+  size_t sz_required;
+  size_t sz_available;
+
+  if ((nb == NULL) || (buffer_size == NULL))
+    return (EINVAL);
+
+  assert (nb->size >= nb->free);
+  sz_required = nb->size - nb->free;
+  sz_available = *buffer_size;
+
+  *buffer_size = sz_required;
+  if (buffer != NULL)
+    memcpy (buffer, nb->buffer,
+        (sz_available < sz_required) ? sz_available : sz_required);
+
+  return (0);
+} /* }}} int lcc_network_buffer_get */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
index 5b7aa94..19f58b2 100644 (file)
@@ -148,6 +148,19 @@ block:
         $$.children = $2.statement;
         $$.children_num = $2.statement_num;
        }
+       | block_begin block_end
+       {
+        if (strcmp ($1.key, $2) != 0)
+        {
+               printf ("block_begin = %s; block_end = %s;\n", $1.key, $2);
+               yyerror ("Block not closed..\n");
+               exit (1);
+        }
+        free ($2); $2 = NULL;
+        $$ = $1;
+        $$.children = NULL;
+        $$.children_num = 0;
+       }
        ;
 
 statement:
@@ -191,6 +204,13 @@ entire_file:
         ci_root->children = $1.statement;
         ci_root->children_num = $1.statement_num;
        }
+       | /* epsilon */
+       {
+        ci_root = malloc (sizeof (oconfig_item_t));
+        memset (ci_root, '\0', sizeof (oconfig_item_t));
+        ci_root->children = NULL;
+        ci_root->children_num = 0;
+       }
        ;
 
 %%
index 1f62879..cfabaaa 100644 (file)
@@ -43,6 +43,7 @@ static const char *config_keys[] = {
     "IgnoreSelected",
 
     "HostnameFormat",
+    "InterfaceFormat",
 
     NULL
 };
@@ -89,13 +90,15 @@ static int add_block_device (virDomainPtr dom, const char *path);
 struct interface_device {
     virDomainPtr dom;           /* domain */
     char *path;                 /* name of interface device */
+    char *address;              /* mac address of interface device */
+    char *number;               /* interface device number */
 };
 
 static struct interface_device *interface_devices = NULL;
 static int nr_interface_devices = 0;
 
 static void free_interface_devices (void);
-static int add_interface_device (virDomainPtr dom, const char *path);
+static int add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number);
 
 /* HostnameFormat. */
 #define HF_MAX_FIELDS 3
@@ -110,22 +113,20 @@ enum hf_field {
 static enum hf_field hostname_format[HF_MAX_FIELDS] =
     { hf_name };
 
+/* InterfaceFormat. */
+enum if_field {
+    if_address,
+    if_name,
+    if_number
+};
+
+static enum if_field interface_format = if_name;
+
 /* Time that we last refreshed. */
 static time_t last_refresh = (time_t) 0;
 
 static int refresh_lists (void);
 
-/* Submit functions. */
-static void cpu_submit (unsigned long long cpu_time,
-                        time_t t,
-                        virDomainPtr dom, const char *type);
-static void vcpu_submit (unsigned long long cpu_time,
-                         time_t t,
-                         virDomainPtr dom, int vcpu_nr, const char *type);
-static void submit_counter2 (const char *type, counter_t v0, counter_t v1,
-             time_t t,
-             virDomainPtr dom, const char *devname);
-
 /* ERROR(...) macro for virterrors. */
 #define VIRT_ERROR(conn,s) do {                 \
         virErrorPtr err;                        \
@@ -133,6 +134,126 @@ static void submit_counter2 (const char *type, counter_t v0, counter_t v1,
         if (err) ERROR ("%s: %s", (s), err->message);                   \
     } while(0)
 
+static void
+init_value_list (value_list_t *vl, virDomainPtr dom)
+{
+    int i, n;
+    const char *name;
+    char uuid[VIR_UUID_STRING_BUFLEN];
+
+    sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin));
+
+    vl->host[0] = '\0';
+
+    /* Construct the hostname field according to HostnameFormat. */
+    for (i = 0; i < HF_MAX_FIELDS; ++i) {
+        if (hostname_format[i] == hf_none)
+            continue;
+
+        n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2;
+
+        if (i > 0 && n >= 1) {
+            strncat (vl->host, ":", 1);
+            n--;
+        }
+
+        switch (hostname_format[i]) {
+        case hf_none: break;
+        case hf_hostname:
+            strncat (vl->host, hostname_g, n);
+            break;
+        case hf_name:
+            name = virDomainGetName (dom);
+            if (name)
+                strncat (vl->host, name, n);
+            break;
+        case hf_uuid:
+            if (virDomainGetUUIDString (dom, uuid) == 0)
+                strncat (vl->host, uuid, n);
+            break;
+        }
+    }
+
+    vl->host[sizeof (vl->host) - 1] = '\0';
+} /* 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)
+{
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].derive = cpu_time;
+
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, type, sizeof (vl.type));
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
+vcpu_submit (derive_t cpu_time,
+             virDomainPtr dom, int vcpu_nr, const char *type)
+{
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].derive = cpu_time;
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, type, sizeof (vl.type));
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
+submit_derive2 (const char *type, derive_t v0, derive_t v1,
+             virDomainPtr dom, const char *devname)
+{
+    value_t values[2];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].derive = v0;
+    values[1].derive = v1;
+    vl.values = values;
+    vl.values_len = 2;
+
+    sstrncpy (vl.type, type, sizeof (vl.type));
+    sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+} /* void submit_derive2 */
+
 static int
 lv_init (void)
 {
@@ -215,7 +336,7 @@ lv_config (const char *key, const char *value)
 
         n = strsplit (value_copy, fields, HF_MAX_FIELDS);
         if (n < 1) {
-            free (value_copy);
+            sfree (value_copy);
             ERROR ("HostnameFormat: no fields");
             return -1;
         }
@@ -228,12 +349,12 @@ lv_config (const char *key, const char *value)
             else if (strcasecmp (fields[i], "uuid") == 0)
                 hostname_format[i] = hf_uuid;
             else {
-                free (value_copy);
+                sfree (value_copy);
                 ERROR ("unknown HostnameFormat field: %s", fields[i]);
                 return -1;
             }
         }
-        free (value_copy);
+        sfree (value_copy);
 
         for (i = n; i < HF_MAX_FIELDS; ++i)
             hostname_format[i] = hf_none;
@@ -241,6 +362,20 @@ lv_config (const char *key, const char *value)
         return 0;
     }
 
+    if (strcasecmp (key, "InterfaceFormat") == 0) {
+        if (strcasecmp (value, "name") == 0)
+            interface_format = if_name;
+        else if (strcasecmp (value, "address") == 0)
+            interface_format = if_address;
+        else if (strcasecmp (value, "number") == 0)
+            interface_format = if_number;
+        else {
+            ERROR ("unknown InterfaceFormat: %s", value);
+            return -1;
+        }
+        return 0;
+    }
+
     /* Unrecognised option. */
     return -1;
 }
@@ -291,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;
@@ -306,7 +441,8 @@ lv_read (void)
             continue;
         }
 
-        cpu_submit (info.cpuTime, t, domains[i], "virt_cpu_total");
+        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) {
@@ -326,9 +462,9 @@ lv_read (void)
 
         for (j = 0; j < info.nrVirtCpu; ++j)
             vcpu_submit (vinfo[j].cpuTime,
-                    t, domains[i], vinfo[j].number, "virt_vcpu");
+                    domains[i], vinfo[j].number, "virt_vcpu");
 
-        free (vinfo);
+        sfree (vinfo);
     }
 
     /* Get block device stats for each domain. */
@@ -340,19 +476,33 @@ lv_read (void)
             continue;
 
         if ((stats.rd_req != -1) && (stats.wr_req != -1))
-            submit_counter2 ("disk_ops",
-                    (counter_t) stats.rd_req, (counter_t) stats.wr_req,
-                    t, block_devices[i].dom, block_devices[i].path);
+            submit_derive2 ("disk_ops",
+                    (derive_t) stats.rd_req, (derive_t) stats.wr_req,
+                    block_devices[i].dom, block_devices[i].path);
 
         if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
-            submit_counter2 ("disk_octets",
-                    (counter_t) stats.rd_bytes, (counter_t) stats.wr_bytes,
-                    t, block_devices[i].dom, block_devices[i].path);
+            submit_derive2 ("disk_octets",
+                    (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes,
+                    block_devices[i].dom, block_devices[i].path);
     } /* for (nr_block_devices) */
 
     /* Get interface stats for each domain. */
     for (i = 0; i < nr_interface_devices; ++i) {
         struct _virDomainInterfaceStats stats;
+        char *display_name = NULL;
+
+
+        switch (interface_format) {
+            case if_address:
+                display_name = interface_devices[i].address;
+                break;
+            case if_number:
+                display_name = interface_devices[i].number;
+                break;
+            case if_name:
+            default:
+                display_name = interface_devices[i].path;
+        }
 
         if (virDomainInterfaceStats (interface_devices[i].dom,
                     interface_devices[i].path,
@@ -360,24 +510,24 @@ lv_read (void)
             continue;
 
        if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
-           submit_counter2 ("if_octets",
-                   (counter_t) stats.rx_bytes, (counter_t) stats.tx_bytes,
-                   t, interface_devices[i].dom, interface_devices[i].path);
+           submit_derive2 ("if_octets",
+                   (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes,
+                   interface_devices[i].dom, display_name);
 
        if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
-           submit_counter2 ("if_packets",
-                   (counter_t) stats.rx_packets, (counter_t) stats.tx_packets,
-                   t, interface_devices[i].dom, interface_devices[i].path);
+           submit_derive2 ("if_packets",
+                   (derive_t) stats.rx_packets, (derive_t) stats.tx_packets,
+                   interface_devices[i].dom, display_name);
 
        if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
-           submit_counter2 ("if_errors",
-                   (counter_t) stats.rx_errs, (counter_t) stats.tx_errs,
-                   t, interface_devices[i].dom, interface_devices[i].path);
+           submit_derive2 ("if_errors",
+                   (derive_t) stats.rx_errs, (derive_t) stats.tx_errs,
+                   interface_devices[i].dom, display_name);
 
        if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
-           submit_counter2 ("if_dropped",
-                   (counter_t) stats.rx_drop, (counter_t) stats.tx_drop,
-                   t, interface_devices[i].dom, interface_devices[i].path);
+           submit_derive2 ("if_dropped",
+                   (derive_t) stats.rx_drop, (derive_t) stats.tx_drop,
+                   interface_devices[i].dom, display_name);
     } /* for (nr_interface_devices) */
 
     return 0;
@@ -408,7 +558,7 @@ refresh_lists (void)
         n = virConnectListDomains (conn, domids, n);
         if (n < 0) {
             VIRT_ERROR (conn, "reading list of domains");
-            free (domids);
+            sfree (domids);
             return -1;
         }
 
@@ -492,38 +642,54 @@ refresh_lists (void)
 
             /* Network interfaces. */
             xpath_obj = xmlXPathEval
-                ((xmlChar *) "/domain/devices/interface/target[@dev]",
+                ((xmlChar *) "/domain/devices/interface[target[@dev]]",
                  xpath_ctx);
             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
                 xpath_obj->nodesetval == NULL)
                 goto cont;
 
-            for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
-                xmlNodePtr node;
+            xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
+
+            for (j = 0; j < xml_interfaces->nodeNr; ++j) {
                 char *path = NULL;
+                char *address = NULL;
+                xmlNodePtr xml_interface;
 
-                node = xpath_obj->nodesetval->nodeTab[j];
-                if (!node) continue;
-                path = (char *) xmlGetProp (node, (xmlChar *) "dev");
-                if (!path) continue;
+                xml_interface = xml_interfaces->nodeTab[j];
+                if (!xml_interface) continue;
+                xmlNodePtr child = NULL;
+
+                for (child = xml_interface->children; child; child = child->next) {
+                    if (child->type != XML_ELEMENT_NODE) continue;
+
+                    if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
+                        path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
+                        if (!path) continue;
+                    } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
+                        address = (char *) xmlGetProp (child, (const xmlChar *) "address");
+                        if (!address) continue;
+                    }
+                }
 
                 if (il_interface_devices &&
-                    ignore_device_match (il_interface_devices, name, path) != 0)
+                    (ignore_device_match (il_interface_devices, name, path) != 0 ||
+                     ignore_device_match (il_interface_devices, name, address) != 0))
                     goto cont3;
 
-                add_interface_device (dom, path);
-            cont3:
-                if (path) xmlFree (path);
+                add_interface_device (dom, path, address, j+1);
+                cont3:
+                    if (path) xmlFree (path);
+                    if (address) xmlFree (address);
             }
 
         cont:
             if (xpath_obj) xmlXPathFreeObject (xpath_obj);
             if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
             if (xml_doc) xmlFreeDoc (xml_doc);
-            if (xml) free (xml);
+            sfree (xml);
         }
 
-        free (domids);
+        sfree (domids);
     }
 
     return 0;
@@ -537,7 +703,7 @@ free_domains ()
     if (domains) {
         for (i = 0; i < nr_domains; ++i)
             virDomainFree (domains[i]);
-        free (domains);
+        sfree (domains);
     }
     domains = NULL;
     nr_domains = 0;
@@ -569,8 +735,8 @@ free_block_devices ()
 
     if (block_devices) {
         for (i = 0; i < nr_block_devices; ++i)
-            free (block_devices[i].path);
-        free (block_devices);
+            sfree (block_devices[i].path);
+        sfree (block_devices);
     }
     block_devices = NULL;
     nr_block_devices = 0;
@@ -593,7 +759,7 @@ add_block_device (virDomainPtr dom, const char *path)
         new_ptr = malloc (new_size);
 
     if (new_ptr == NULL) {
-        free (path_copy);
+        sfree (path_copy);
         return -1;
     }
     block_devices = new_ptr;
@@ -608,36 +774,50 @@ free_interface_devices ()
     int i;
 
     if (interface_devices) {
-        for (i = 0; i < nr_interface_devices; ++i)
-            free (interface_devices[i].path);
-        free (interface_devices);
+        for (i = 0; i < nr_interface_devices; ++i) {
+            sfree (interface_devices[i].path);
+            sfree (interface_devices[i].address);
+            sfree (interface_devices[i].number);
+        }
+        sfree (interface_devices);
     }
     interface_devices = NULL;
     nr_interface_devices = 0;
 }
 
 static int
-add_interface_device (virDomainPtr dom, const char *path)
+add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number)
 {
     struct interface_device *new_ptr;
     int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
-    char *path_copy;
+    char *path_copy, *address_copy, number_string[15];
 
     path_copy = strdup (path);
     if (!path_copy) return -1;
 
+    address_copy = strdup (address);
+    if (!address_copy) {
+        sfree(path_copy);
+        return -1;
+    }
+
+    snprintf(number_string, sizeof (number_string), "interface-%u", number);
+
     if (interface_devices)
         new_ptr = realloc (interface_devices, new_size);
     else
         new_ptr = malloc (new_size);
 
     if (new_ptr == NULL) {
-        free (path_copy);
+        sfree (path_copy);
+        sfree (address_copy);
         return -1;
     }
     interface_devices = new_ptr;
     interface_devices[nr_interface_devices].dom = dom;
     interface_devices[nr_interface_devices].path = path_copy;
+    interface_devices[nr_interface_devices].address = address_copy;
+    interface_devices[nr_interface_devices].number = strdup(number_string);
     return nr_interface_devices++;
 }
 
@@ -655,117 +835,10 @@ ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
     }
     ssnprintf (name, n, "%s:%s", domname, devpath);
     r = ignorelist_match (il, name);
-    free (name);
+    sfree (name);
     return r;
 }
 
-static void
-init_value_list (value_list_t *vl, time_t t, virDomainPtr dom)
-{
-    int i, n;
-    const char *name;
-    char uuid[VIR_UUID_STRING_BUFLEN];
-
-    vl->time = t;
-    vl->interval = interval_g;
-
-    sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin));
-
-    vl->host[0] = '\0';
-
-    /* Construct the hostname field according to HostnameFormat. */
-    for (i = 0; i < HF_MAX_FIELDS; ++i) {
-        if (hostname_format[i] == hf_none)
-            continue;
-
-        n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2;
-
-        if (i > 0 && n >= 1) {
-            strncat (vl->host, ":", 1);
-            n--;
-        }
-
-        switch (hostname_format[i]) {
-        case hf_none: break;
-        case hf_hostname:
-            strncat (vl->host, hostname_g, n);
-            break;
-        case hf_name:
-            name = virDomainGetName (dom);
-            if (name)
-                strncat (vl->host, name, n);
-            break;
-        case hf_uuid:
-            if (virDomainGetUUIDString (dom, uuid) == 0)
-                strncat (vl->host, uuid, n);
-            break;
-        }
-    }
-
-    vl->host[sizeof (vl->host) - 1] = '\0';
-} /* void init_value_list */
-
-static void
-cpu_submit (unsigned long long cpu_time,
-            time_t t,
-            virDomainPtr dom, const char *type)
-{
-    value_t values[1];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, t, dom);
-
-    values[0].counter = cpu_time;
-
-    vl.values = values;
-    vl.values_len = 1;
-
-    sstrncpy (vl.type, type, sizeof (vl.type));
-
-    plugin_dispatch_values (&vl);
-}
-
-static void
-vcpu_submit (counter_t cpu_time,
-             time_t t,
-             virDomainPtr dom, int vcpu_nr, const char *type)
-{
-    value_t values[1];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, t, dom);
-
-    values[0].counter = cpu_time;
-    vl.values = values;
-    vl.values_len = 1;
-
-    sstrncpy (vl.type, type, sizeof (vl.type));
-    ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
-
-    plugin_dispatch_values (&vl);
-}
-
-static void
-submit_counter2 (const char *type, counter_t v0, counter_t v1,
-             time_t t,
-             virDomainPtr dom, const char *devname)
-{
-    value_t values[2];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, t, dom);
-
-    values[0].counter = v0;
-    values[1].counter = v1;
-    vl.values = values;
-    vl.values_len = 2;
-
-    sstrncpy (vl.type, type, sizeof (vl.type));
-    sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
-
-    plugin_dispatch_values (&vl);
-} /* void submit_counter2 */
-
 static int
 lv_shutdown (void)
 {
@@ -774,7 +847,7 @@ lv_shutdown (void)
     free_domains ();
 
     if (conn != NULL)
-       virConnectClose (conn);
+        virConnectClose (conn);
     conn = NULL;
 
     ignorelist_free (il_domains);
@@ -791,8 +864,8 @@ void
 module_register (void)
 {
     plugin_register_config ("libvirt",
-           lv_config,
-           config_keys, NR_CONFIG_KEYS);
+    lv_config,
+    config_keys, NR_CONFIG_KEYS);
     plugin_register_init ("libvirt", lv_init);
     plugin_register_read ("libvirt", lv_read);
     plugin_register_shutdown ("libvirt", lv_shutdown);
index e13c09e..63448cb 100644 (file)
@@ -53,23 +53,12 @@ static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 static int logfile_config (const char *key, const char *value)
 {
        if (0 == strcasecmp (key, "LogLevel")) {
-               if ((0 == strcasecmp (value, "emerg"))
-                               || (0 == strcasecmp (value, "alert"))
-                               || (0 == strcasecmp (value, "crit"))
-                               || (0 == strcasecmp (value, "err")))
-                       log_level = LOG_ERR;
-               else if (0 == strcasecmp (value, "warning"))
-                       log_level = LOG_WARNING;
-               else if (0 == strcasecmp (value, "notice"))
-                       log_level = LOG_NOTICE;
-               else if (0 == strcasecmp (value, "info"))
+               log_level = parse_log_severity(value);
+               if (log_level < 0) {
                        log_level = LOG_INFO;
-#if COLLECT_DEBUG
-               else if (0 == strcasecmp (value, "debug"))
-                       log_level = LOG_DEBUG;
-#endif /* COLLECT_DEBUG */
-               else
-                       return 1;
+                       ERROR ("logfile: invalid loglevel [%s] defaulting to 'info'", value);
+                       return (1);
+               }
        }
        else if (0 == strcasecmp (key, "File")) {
                sfree (log_file);
@@ -92,7 +81,8 @@ static int logfile_config (const char *key, const char *value)
        return 0;
 } /* int logfile_config (const char *, const char *) */
 
-static void logfile_print (const char *msg, int severity, time_t timestamp_time)
+static void logfile_print (const char *msg, int severity,
+               cdtime_t timestamp_time)
 {
        FILE *fh;
        _Bool do_close = 0;
@@ -126,7 +116,8 @@ static void logfile_print (const char *msg, int severity, time_t timestamp_time)
 
        if (print_timestamp)
        {
-               localtime_r (&timestamp_time, &timestamp_tm);
+               time_t tt = CDTIME_T_TO_TIME_T (timestamp_time);
+               localtime_r (&tt, &timestamp_tm);
 
                strftime (timestamp_str, sizeof (timestamp_str), "%Y-%m-%d %H:%M:%S",
                                &timestamp_tm);
@@ -182,7 +173,7 @@ static void logfile_log (int severity, const char *msg,
        if (severity > log_level)
                return;
 
-       logfile_print (msg, severity, time (NULL));
+       logfile_print (msg, severity, cdtime ());
 } /* void logfile_log (int, const char *) */
 
 static int logfile_notification (const notification_t *n,
@@ -221,7 +212,7 @@ static int logfile_notification (const notification_t *n,
        buf[sizeof (buf) - 1] = '\0';
 
        logfile_print (buf, LOG_INFO,
-                       (n->time > 0) ? n->time : time (NULL));
+                       (n->time != 0) ? n->time : cdtime ());
 
        return (0);
 } /* int logfile_notification */
diff --git a/src/lpar.c b/src/lpar.c
new file mode 100644 (file)
index 0000000..4d53447
--- /dev/null
@@ -0,0 +1,273 @@
+/**
+ * collectd - src/lpar.c
+ * Copyright (C) 2010  Aurélien Reynaud
+ *
+ * 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:
+ *   Aurélien Reynaud <collectd at wattapower.net>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <sys/protosw.h>
+#include <libperfstat.h>
+#include <sys/utsname.h>
+
+/* XINTFRAC was defined in libperfstat.h somewhere between AIX 5.3 and 6.1 */
+#ifndef XINTFRAC
+# include <sys/systemcfg.h>
+# define XINTFRAC ((double)(_system_configuration.Xint) / \
+                   (double)(_system_configuration.Xfrac))
+#endif
+
+#define CLOCKTICKS_TO_TICKS(cticks) ((cticks) / XINTFRAC)
+
+static const char *config_keys[] =
+{
+  "CpuPoolStats",
+  "ReportBySerial"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static _Bool pool_stats = 0;
+static _Bool report_by_serial = 0;
+#if PERFSTAT_SUPPORTS_DONATION
+static _Bool donate_flag = 0;
+#endif
+static char serial[SYS_NMLN];
+
+static perfstat_partition_total_t lparstats_old;
+
+static int lpar_config (const char *key, const char *value)
+{
+       if (strcasecmp ("CpuPoolStats", key) == 0)
+       {
+               if (IS_TRUE (value))
+                       pool_stats = 1;
+               else
+                       pool_stats = 0;
+       }
+       else if (strcasecmp ("ReportBySerial", key) == 0)
+       {
+               if (IS_TRUE (value))
+                       report_by_serial = 1;
+               else
+                       report_by_serial = 0;
+       }
+       else
+       {
+               return (-1);
+       }
+
+       return (0);
+} /* int lpar_config */
+
+static int lpar_init (void)
+{
+       int status;
+
+       /* Retrieve the initial metrics. Returns the number of structures filled. */
+       status = perfstat_partition_total (/* name = */ NULL, /* (must be NULL) */
+                       &lparstats_old, sizeof (perfstat_partition_total_t),
+                       /* number = */ 1 /* (must be 1) */);
+       if (status != 1)
+       {
+               char errbuf[1024];
+               ERROR ("lpar plugin: perfstat_partition_total failed: %s (%i)",
+                               sstrerror (errno, errbuf, sizeof (errbuf)),
+                               status);
+               return (-1);
+       }
+
+#if PERFSTAT_SUPPORTS_DONATION
+       if (!lparstats_old.type.b.shared_enabled
+                       && lparstats_old.type.b.donate_enabled)
+       {
+               donate_flag = 1;
+       }
+#endif
+
+       if (pool_stats && !lparstats_old.type.b.pool_util_authority)
+       {
+               WARNING ("lpar plugin: This partition does not have pool authority. "
+                               "Disabling CPU pool statistics collection.");
+               pool_stats = 0;
+       }
+
+       return (0);
+} /* int lpar_init */
+
+static void lpar_submit (const char *type_instance, double value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = (gauge_t)value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       if (report_by_serial)
+       {
+               sstrncpy (vl.host, serial, sizeof (vl.host));
+               sstrncpy (vl.plugin_instance, hostname_g, sizeof (vl.plugin));
+       }
+       else
+       {
+               sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       }
+       sstrncpy (vl.plugin, "lpar", sizeof (vl.plugin));
+       sstrncpy (vl.type, "vcpu", sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* void lpar_submit */
+
+static int lpar_read (void)
+{
+       perfstat_partition_total_t lparstats;
+       int status;
+       struct utsname name;
+       u_longlong_t ticks;
+       u_longlong_t user_ticks, syst_ticks, wait_ticks, idle_ticks;
+       u_longlong_t consumed_ticks;
+       double entitled_proc_capacity;
+
+       /* An LPAR has the same serial number as the physical system it is currently
+          running on. It is a convenient way of tracking LPARs as they are moved
+          from chassis to chassis through Live Partition Mobility (LPM). */
+       if (uname (&name) != 0)
+       {
+               ERROR ("lpar plugin: uname failed.");
+               return (-1);
+       }
+       sstrncpy (serial, name.machine, sizeof (serial));
+
+       /* Retrieve the current metrics. Returns the number of structures filled. */
+       status = perfstat_partition_total (/* name = */ NULL, /* (must be NULL) */
+                       &lparstats, sizeof (perfstat_partition_total_t),
+                       /* number = */ 1 /* (must be 1) */);
+       if (status != 1)
+       {
+               char errbuf[1024];
+               ERROR ("lpar plugin: perfstat_partition_total failed: %s (%i)",
+                               sstrerror (errno, errbuf, sizeof (errbuf)),
+                               status);
+               return (-1);
+       }
+
+       /* Number of ticks since we last run. */
+       ticks = lparstats.timebase_last - lparstats_old.timebase_last;
+       if (ticks == 0)
+       {
+               /* The stats have not been updated. Return now to avoid
+                * dividing by zero */
+               return (0);
+       }
+
+       /*
+        * On a shared partition, we're "entitled" to a certain amount of
+        * processing power, for example 250/100 of a physical CPU. Processing
+        * capacity not used by the partition may be assigned to a different
+        * partition by the hypervisor, so "idle" is hopefully a very small
+        * number.
+        *
+        * A dedicated partition may donate its CPUs to another partition and
+        * may steal ticks from somewhere else (another partition or maybe the
+        * shared pool, I don't know --octo).
+        */
+
+       /* entitled_proc_capacity is in 1/100th of a CPU */
+       entitled_proc_capacity = 0.01 * ((double) lparstats.entitled_proc_capacity);
+       lpar_submit ("entitled", entitled_proc_capacity);
+
+       /* The number of ticks actually spent in the various states */
+       user_ticks = lparstats.puser - lparstats_old.puser;
+       syst_ticks = lparstats.psys  - lparstats_old.psys;
+       wait_ticks = lparstats.pwait - lparstats_old.pwait;
+       idle_ticks = lparstats.pidle - lparstats_old.pidle;
+       consumed_ticks = user_ticks + syst_ticks + wait_ticks + idle_ticks;
+
+       lpar_submit ("user", (double) user_ticks / (double) ticks);
+       lpar_submit ("system", (double) syst_ticks / (double) ticks);
+       lpar_submit ("wait", (double) wait_ticks / (double) ticks);
+       lpar_submit ("idle", (double) idle_ticks / (double) ticks);
+
+#if PERFSTAT_SUPPORTS_DONATION
+       if (donate_flag)
+       {
+               /* donated => ticks given to another partition
+                * stolen  => ticks received from another partition */
+               u_longlong_t idle_donated_ticks, busy_donated_ticks;
+               u_longlong_t idle_stolen_ticks, busy_stolen_ticks;
+
+               /* FYI:  PURR == Processor Utilization of Resources Register
+                *      SPURR == Scaled PURR */
+               idle_donated_ticks = lparstats.idle_donated_purr - lparstats_old.idle_donated_purr;
+               busy_donated_ticks = lparstats.busy_donated_purr - lparstats_old.busy_donated_purr;
+               idle_stolen_ticks  = lparstats.idle_stolen_purr  - lparstats_old.idle_stolen_purr;
+               busy_stolen_ticks  = lparstats.busy_stolen_purr  - lparstats_old.busy_stolen_purr;
+
+               lpar_submit ("idle_donated", (double) idle_donated_ticks / (double) ticks);
+               lpar_submit ("busy_donated", (double) busy_donated_ticks / (double) ticks);
+               lpar_submit ("idle_stolen",  (double) idle_stolen_ticks  / (double) ticks);
+               lpar_submit ("busy_stolen",  (double) busy_stolen_ticks  / (double) ticks);
+
+               /* Donated ticks will be accounted for as stolen ticks in other LPARs */
+               consumed_ticks += idle_stolen_ticks + busy_stolen_ticks;
+       }
+#endif
+
+       lpar_submit ("consumed", (double) consumed_ticks / (double) ticks);
+
+       if (pool_stats)
+       {
+               char typinst[DATA_MAX_NAME_LEN];
+               u_longlong_t pool_idle_cticks;
+               double pool_idle_cpus;
+               double pool_busy_cpus;
+
+               /* We're calculating "busy" from "idle" and the total number of
+                * CPUs, because the "busy" member didn't exist in early versions
+                * of libperfstat. It was added somewhere between AIX 5.3 ML5 and ML9. */
+               pool_idle_cticks = lparstats.pool_idle_time - lparstats_old.pool_idle_time;
+               pool_idle_cpus = CLOCKTICKS_TO_TICKS ((double) pool_idle_cticks) / (double) ticks;
+               pool_busy_cpus = ((double) lparstats.phys_cpus_pool) - pool_idle_cpus;
+               if (pool_busy_cpus < 0.0)
+                       pool_busy_cpus = 0.0;
+
+               ssnprintf (typinst, sizeof (typinst), "pool-%X-busy", lparstats.pool_id);
+               lpar_submit (typinst, pool_busy_cpus);
+
+               ssnprintf (typinst, sizeof (typinst), "pool-%X-idle", lparstats.pool_id);
+               lpar_submit (typinst, pool_idle_cpus);
+       }
+
+       memcpy (&lparstats_old, &lparstats, sizeof (lparstats_old));
+
+       return (0);
+} /* int lpar_read */
+
+void module_register (void)
+{
+       plugin_register_config ("lpar", lpar_config,
+                               config_keys, config_keys_num);
+       plugin_register_init ("lpar", lpar_init);
+       plugin_register_read ("lpar", lpar_read);
+} /* void module_register */
+
+/* vim: set sw=8 noet : */
+
index 8b3266d..13301ff 100644 (file)
@@ -561,20 +561,20 @@ static void submit (const char *dev, const char *type, const char *ti1,
        plugin_dispatch_values (&vl);
 }
 
-static void submit_counter (const char *dev, const char *type, const char *ti1,
-                               const char *ti2, counter_t val)
+static void submit_derive (const char *dev, const char *type, const char *ti1,
+                               const char *ti2, derive_t val)
 {
        value_t item;
-       item.counter = val;
+       item.derive = val;
        submit (dev, type, ti1, ti2, &item, 1);
 }
 
-static void submit_counter2 (const char *dev, const char *type, const char *ti1,
-                               const char *ti2, counter_t val1, counter_t val2)
+static void submit_derive2 (const char *dev, const char *type, const char *ti1,
+                               const char *ti2, derive_t val1, derive_t val2)
 {
        value_t items[2];
-       items[0].counter = val1;
-       items[1].counter = val2;
+       items[0].derive = val1;
+       items[1].derive = val2;
        submit (dev, type, ti1, ti2, items, 2);
 }
 
@@ -598,8 +598,8 @@ static void submit_antx (const char *dev, const char *name,
                        continue;
 
                ssnprintf (ti2, sizeof (ti2), "%i", i);
-               submit_counter (dev, "ath_stat", name, ti2,
-                               (counter_t) vals[i]);
+               submit_derive (dev, "ath_stat", name, ti2,
+                               (derive_t) vals[i]);
        }
 }
 
@@ -625,14 +625,14 @@ process_stat_struct (int which, const void *ptr, const char *dev, const char *ma
                uint32_t val = *(uint32_t *)(((char *) ptr) + specs[i].offset) ;
 
                if (item_watched (i) && (val != 0))
-                       submit_counter (dev, type_name, specs[i].name, mac, val);
+                       submit_derive (dev, type_name, specs[i].name, mac, val);
 
                if (item_summed (i))
                        misc += val;
        }
        
        if (misc != 0)
-               submit_counter (dev, type_name, misc_name, mac, misc);
+               submit_derive (dev, type_name, misc_name, mac, misc);
 
 }
 
@@ -734,13 +734,13 @@ process_station (int sk, const char *dev, struct ieee80211req_sta_info *si)
        /* These two stats are handled as a special case as they are
           a pair of 64bit values */
        if (item_watched (STAT_NODE_OCTETS))
-               submit_counter2 (dev, "node_octets", mac, NULL,
+               submit_derive2 (dev, "node_octets", mac, NULL,
                        ns->ns_rx_bytes, ns->ns_tx_bytes);
 
        /* This stat is handled as a special case, because it is stored
           as uin64_t, but we will ignore upper half */
        if (item_watched (STAT_NS_RX_BEACONS))
-               submit_counter (dev, "node_stat", "ns_rx_beacons", mac,
+               submit_derive (dev, "node_stat", "ns_rx_beacons", mac,
                        (ns->ns_rx_beacons & 0xFFFFFFFF));
 
        /* All other node statistics */
index 4ac944a..2e27415 100644 (file)
@@ -34,29 +34,13 @@ struct mt_match_s;
 typedef struct mt_match_s mt_match_t;
 struct mt_match_s
 {
-  time_t future;
-  time_t past;
+  cdtime_t future;
+  cdtime_t past;
 };
 
 /*
  * internal helper functions
  */
-static int mt_config_add_time_t (time_t *ret_value, /* {{{ */
-    oconfig_item_t *ci)
-{
-
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
-  {
-    ERROR ("timediff match: `%s' needs exactly one numeric argument.",
-        ci->key);
-    return (-1);
-  }
-
-  *ret_value = (time_t) ci->values[0].value.number;
-
-  return (0);
-} /* }}} int mt_config_add_time_t */
-
 static int mt_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
 {
   mt_match_t *m;
@@ -80,9 +64,9 @@ static int mt_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("Future", child->key) == 0)
-      status = mt_config_add_time_t (&m->future, child);
+      status = cf_util_get_cdtime (child, &m->future);
     else if (strcasecmp ("Past", child->key) == 0)
-      status = mt_config_add_time_t (&m->past, child);
+      status = cf_util_get_cdtime (child, &m->past);
     else
     {
       ERROR ("timediff match: The `%s' configuration option is not "
@@ -132,13 +116,13 @@ static int mt_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
     notification_meta_t __attribute__((unused)) **meta, void **user_data)
 {
   mt_match_t *m;
-  time_t now;
+  cdtime_t now;
 
   if ((user_data == NULL) || (*user_data == NULL))
     return (-1);
 
   m = *user_data;
-  now = time (NULL);
+  now = cdtime ();
 
   if (m->future != 0)
   {
diff --git a/src/md.c b/src/md.c
new file mode 100644 (file)
index 0000000..3725f9a
--- /dev/null
+++ b/src/md.c
@@ -0,0 +1,218 @@
+/**
+ * collectd - src/md.c
+ * Copyright (C) 2010,2011  Michael Hanselmann
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Michael Hanselmann
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_ignorelist.h"
+
+#include <sys/ioctl.h>
+
+#include <linux/major.h>
+#include <linux/raid/md_u.h>
+
+#define PROC_DISKSTATS "/proc/diskstats"
+#define DEV_DIR "/dev"
+
+static const char *config_keys[] =
+{
+  "Device",
+  "IgnoreSelected"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *ignorelist = NULL;
+
+static int md_config (const char *key, const char *value)
+{
+  if (ignorelist == NULL)
+    ignorelist = ignorelist_create (/* invert = */ 1);
+  if (ignorelist == NULL)
+    return (1);
+
+  if (strcasecmp (key, "Device") == 0)
+  {
+    ignorelist_add (ignorelist, value);
+  }
+  else if (strcasecmp (key, "IgnoreSelected") == 0)
+  {
+    ignorelist_set_invert (ignorelist, IS_TRUE (value) ? 0 : 1);
+  }
+  else
+  {
+    return (-1);
+  }
+
+  return (0);
+}
+
+static void md_submit (const int minor, const char *type_instance,
+    gauge_t value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = value;
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "md", sizeof (vl.plugin));
+  ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+      "%i", minor);
+  sstrncpy (vl.type, "md_disks", sizeof (vl.type));
+  sstrncpy (vl.type_instance, type_instance,
+      sizeof (vl.type_instance));
+
+  plugin_dispatch_values (&vl);
+} /* void md_submit */
+
+static void md_process (const int minor, const char *path)
+{
+  char errbuf[1024];
+  int fd;
+  struct stat st;
+  mdu_array_info_t array;
+  gauge_t disks_missing;
+
+  fd = open (path, O_RDONLY);
+  if (fd < 0)
+  {
+    WARNING ("md: open(%s): %s", path,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return;
+  }
+
+  if (fstat (fd, &st) < 0)
+  {
+    WARNING ("md: Unable to fstat file descriptor for %s: %s", path,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    close (fd);
+    return;
+  }
+
+  if (! S_ISBLK (st.st_mode))
+  {
+    WARNING ("md: %s is no block device", path);
+    close (fd);
+    return;
+  }
+
+  if (st.st_rdev != makedev (MD_MAJOR, minor))
+  {
+    WARNING ("md: Major/minor of %s are %i:%i, should be %i:%i",
+        path, (int)major(st.st_rdev), (int)minor(st.st_rdev),
+        (int)MD_MAJOR, minor);
+    close (fd);
+    return;
+  }
+
+  /* Retrieve md information */
+  if (ioctl (fd, GET_ARRAY_INFO, &array) < 0) {
+    WARNING ("md: Unable to retrieve array info from %s: %s", path,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    close (fd);
+    return;
+  }
+
+  close (fd);
+
+  /*
+   * The mdu_array_info_t structure contains numbers of disks in the array.
+   * However, disks are accounted for more than once:
+   *
+   * active:  Number of active (in sync) disks.
+   * spare:   Number of stand-by disks.
+   * working: Number of working disks. (active + sync)
+   * failed:  Number of failed disks.
+   * nr:      Number of physically present disks. (working + failed)
+   * raid:    Number of disks in the RAID. This may be larger than "nr" if
+   *          disks are missing and smaller than "nr" when spare disks are
+   *          around.
+   */
+  md_submit (minor, "active",  (gauge_t) array.active_disks);
+  md_submit (minor, "failed",  (gauge_t) array.failed_disks);
+  md_submit (minor, "spare",   (gauge_t) array.spare_disks);
+
+  disks_missing = 0.0;
+  if (array.raid_disks > array.nr_disks)
+    disks_missing = (gauge_t) (array.raid_disks - array.nr_disks);
+  md_submit (minor, "missing", disks_missing);
+} /* void md_process */
+
+static int md_read (void)
+{
+  FILE *fh;
+  char buffer[1024];
+
+  fh = fopen (PROC_DISKSTATS, "r");
+  if (fh == NULL) {
+    char errbuf[1024];
+    WARNING ("md: Unable to open %s: %s",
+        PROC_DISKSTATS ,
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  /* Iterate md devices */
+  while (fgets (buffer, sizeof (buffer), fh) != NULL)
+  {
+    char path[PATH_MAX];
+    char *fields[4];
+    char *name;
+    int major, minor;
+
+    /* Extract interesting fields */
+    if (strsplit (buffer, fields, STATIC_ARRAY_SIZE(fields)) < 3)
+      continue;
+
+    major = atoi (fields[0]);
+
+    if (major != MD_MAJOR)
+      continue;
+
+    minor = atoi (fields[1]);
+    name = fields[2];
+
+    if (ignorelist_match (ignorelist, name))
+      continue;
+
+    /* FIXME: Don't hardcode path. Walk /dev collecting major,
+     * minor and name, then use lookup table to find device.
+     * Alternatively create a temporary device file with correct
+     * major/minor, but that again can be tricky if the filesystem
+     * with the device file is mounted using the "nodev" option.
+     */
+    ssnprintf (path, sizeof (path), "%s/%s", DEV_DIR, name);
+
+    md_process (minor, path);
+  }
+
+  fclose (fh);
+
+  return (0);
+} /* int md_read */
+
+void module_register (void)
+{
+  plugin_register_config ("md", md_config, config_keys, config_keys_num);
+  plugin_register_read ("md", md_read);
+} /* void module_register */
index 73faa50..7c8528d 100644 (file)
@@ -452,7 +452,6 @@ static void cmc_submit (const web_page_t *wp, const web_match_t *wm, /* {{{ */
 
   vl.values = values;
   vl.values_len = 1;
-  vl.time = time (NULL);
   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
   sstrncpy (vl.plugin, "memcachec", sizeof (vl.plugin));
   sstrncpy (vl.plugin_instance, wp->instance, sizeof (vl.plugin_instance));
@@ -501,6 +500,7 @@ static int cmc_read_page (web_page_t *wp) /* {{{ */
     }
 
     cmc_submit (wp, wm, mv);
+    match_value_reset (mv);
   } /* for (wm = wp->matches; wm != NULL; wm = wm->next) */
 
   sfree (wp->buffer);
index 4c9e6ad..2a5e0f0 100644 (file)
@@ -1,9 +1,10 @@
 /**
  * collectd - src/memcached.c, based on src/hddtemp.c
  * Copyright (C) 2007       Antony Dovgal
- * Copyright (C) 2007-2009  Florian Forster
+ * Copyright (C) 2007-2012  Florian Forster
  * Copyright (C) 2009       Doug MacEachern
  * Copyright (C) 2009       Franck Lombardi
+ * Copyright (C) 2012       Nicolas Szalay
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  *
  * Authors:
  *   Antony Dovgal <tony at daylessday dot org>
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Doug MacEachern <dougm at hyperic.com>
  *   Franck Lombardi
+ *   Nicolas Szalay
  **/
 
 #include "collectd.h"
 #include "plugin.h"
 #include "configfile.h"
 
-# include <poll.h>
-# include <netdb.h>
-# include <sys/socket.h>
-# include <sys/un.h>
-# include <netinet/in.h>
-# include <netinet/tcp.h>
-
-/* Hack to work around the missing define in AIX */
-#ifndef MSG_DONTWAIT
-# define MSG_DONTWAIT MSG_NONBLOCK
-#endif
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
 
 #define MEMCACHED_DEF_HOST "127.0.0.1"
 #define MEMCACHED_DEF_PORT "11211"
 
-#define MEMCACHED_RETRY_COUNT 100
-
-static const char *config_keys[] =
+struct memcached_s
 {
-       "Socket",
-       "Host",
-       "Port"
+  char *name;
+  char *socket;
+  char *host;
+  char *port;
 };
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+typedef struct memcached_s memcached_t;
+
+static _Bool memcached_have_instances = 0;
+
+static void memcached_free (memcached_t *st)
+{
+  if (st == NULL)
+    return;
 
-static char *memcached_socket = NULL;
-static char *memcached_host = NULL;
-static char memcached_port[16];
+  sfree (st->name);
+  sfree (st->socket);
+  sfree (st->host);
+  sfree (st->port);
+}
 
-static int memcached_query_daemon (char *buffer, int buffer_size) /* {{{ */
+static int memcached_connect_unix (memcached_t *st)
+{
+  struct sockaddr_un serv_addr;
+  int fd;
+
+  memset (&serv_addr, 0, sizeof (serv_addr));
+  serv_addr.sun_family = AF_UNIX;
+  sstrncpy (serv_addr.sun_path, st->socket,
+      sizeof (serv_addr.sun_path));
+
+  /* create our socket descriptor */
+  fd = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (fd < 0)
+  {
+    char errbuf[1024];
+    ERROR ("memcached plugin: memcached_connect_unix: socket(2) failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  /* 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 */
+
+static int memcached_connect_inet (memcached_t *st)
 {
-       int fd;
-       ssize_t status;
-       int buffer_fill;
-       int i = 0;
-
-       if (memcached_socket != NULL) {
-               struct sockaddr_un serv_addr;
-
-               memset (&serv_addr, 0, sizeof (serv_addr));
-               serv_addr.sun_family = AF_UNIX;
-               sstrncpy (serv_addr.sun_path, memcached_socket,
-                               sizeof (serv_addr.sun_path));
-
-               /* create our socket descriptor */
-               fd = socket (AF_UNIX, SOCK_STREAM, 0);
-               if (fd < 0) {
-                       char errbuf[1024];
-                       ERROR ("memcached: unix socket: %s", sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-                       return -1;
-               }
-
-               /* connect to the memcached daemon */
-               status = (ssize_t) connect (fd, (struct sockaddr *) &serv_addr,
-                               sizeof (serv_addr));
-               if (status != 0) {
-                       shutdown (fd, SHUT_RDWR);
-                       close (fd);
-                       fd = -1;
-               }
-       }
-       else { /* if (memcached_socket == NULL) */
-               const char *host;
-               const char *port;
-
-               struct addrinfo  ai_hints;
-               struct addrinfo *ai_list, *ai_ptr;
-               int              ai_return = 0;
-
-               memset (&ai_hints, '\0', sizeof (ai_hints));
-               ai_hints.ai_flags    = 0;
+  char *host;
+  char *port;
+
+  struct addrinfo  ai_hints;
+  struct addrinfo *ai_list, *ai_ptr;
+  int status;
+  int fd = -1;
+
+  memset (&ai_hints, 0, sizeof (ai_hints));
+  ai_hints.ai_flags    = 0;
 #ifdef AI_ADDRCONFIG
-               /*      ai_hints.ai_flags   |= AI_ADDRCONFIG; */
+  ai_hints.ai_flags   |= AI_ADDRCONFIG;
 #endif
-               ai_hints.ai_family   = AF_INET;
-               ai_hints.ai_socktype = SOCK_STREAM;
-               ai_hints.ai_protocol = 0;
-
-               host = memcached_host;
-               if (host == NULL) {
-                       host = MEMCACHED_DEF_HOST;
-               }
-
-               port = memcached_port;
-               if (strlen (port) == 0) {
-                       port = MEMCACHED_DEF_PORT;
-               }
-
-               if ((ai_return = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0) {
-                       char errbuf[1024];
-                       ERROR ("memcached: getaddrinfo (%s, %s): %s",
-                                       host, port,
-                                       (ai_return == EAI_SYSTEM)
-                                       ? sstrerror (errno, errbuf, sizeof (errbuf))
-                                       : gai_strerror (ai_return));
-                       return -1;
-               }
-
-               fd = -1;
-               for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
-                       /* create our socket descriptor */
-                       fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
-                       if (fd < 0) {
-                               char errbuf[1024];
-                               ERROR ("memcached: socket: %s", sstrerror (errno, errbuf, sizeof (errbuf)));
-                               continue;
-                       }
-
-                       /* connect to the memcached daemon */
-                       status = (ssize_t) connect (fd, (struct sockaddr *) ai_ptr->ai_addr, ai_ptr->ai_addrlen);
-                       if (status != 0) {
-                               shutdown (fd, SHUT_RDWR);
-                               close (fd);
-                               fd = -1;
-                               continue;
-                       }
-
-                       /* A socket could be opened and connecting succeeded. We're
-                        * done. */
-                       break;
-               }
-
-               freeaddrinfo (ai_list);
-       }
-
-       if (fd < 0) {
-               ERROR ("memcached: Could not connect to daemon.");
-               return -1;
-       }
-
-       if (send(fd, "stats\r\n", sizeof("stats\r\n") - 1, MSG_DONTWAIT) != (sizeof("stats\r\n") - 1)) {
-               ERROR ("memcached: Could not send command to the memcached daemon.");
-               return -1;
-       }
-
-       {
-               struct pollfd p;
-               int status;
-
-               memset (&p, 0, sizeof (p));
-               p.fd = fd;
-               p.events = POLLIN | POLLERR | POLLHUP;
-               p.revents = 0;
-
-               status = poll (&p, /* nfds = */ 1, /* timeout = */ 1000 * interval_g);
-               if (status <= 0)
-               {
-                       if (status == 0)
-                       {
-                               ERROR ("memcached: poll(2) timed out after %i seconds.", interval_g);
-                       }
-                       else
-                       {
-                               char errbuf[1024];
-                               ERROR ("memcached: poll(2) failed: %s",
-                                               sstrerror (errno, errbuf, sizeof (errbuf)));
-                       }
-                       shutdown (fd, SHUT_RDWR);
-                       close (fd);
-                       return (-1);
-               }
-       }
-
-       /* receive data from the memcached daemon */
-       memset (buffer, '\0', buffer_size);
-
-       buffer_fill = 0;
-       while ((status = recv (fd, buffer + buffer_fill, buffer_size - buffer_fill, MSG_DONTWAIT)) != 0) {
-               if (i > MEMCACHED_RETRY_COUNT) {
-                       ERROR("recv() timed out");
-                       break;
-               }
-               i++;
-
-               if (status == -1) {
-                       char errbuf[1024];
-
-                       if (errno == EAGAIN) {
-                               continue;
-                       }
-
-                       ERROR ("memcached: Error reading from socket: %s",
-                                       sstrerror (errno, errbuf, sizeof (errbuf)));
-                       shutdown(fd, SHUT_RDWR);
-                       close (fd);
-                       return -1;
-               }
-               buffer_fill += status;
-
-               if (buffer_fill > 3 && buffer[buffer_fill-5] == 'E' && buffer[buffer_fill-4] == 'N' && buffer[buffer_fill-3] == 'D') {
-                       /* we got all the data */
-                       break;
-               }
-       }
-
-       if (buffer_fill >= buffer_size) {
-               buffer[buffer_size - 1] = '\0';
-               WARNING ("memcached: Message from memcached has been truncated.");
-       } else if (buffer_fill == 0) {
-               WARNING ("memcached: Peer has unexpectedly shut down the socket. "
-                               "Buffer: `%s'", buffer);
-               shutdown(fd, SHUT_RDWR);
-               close(fd);
-               return -1;
-       }
-
-       shutdown(fd, SHUT_RDWR);
-       close(fd);
-       return 0;
+  ai_hints.ai_family   = AF_UNSPEC;
+  ai_hints.ai_socktype = SOCK_STREAM;
+  ai_hints.ai_protocol = 0;
+
+  host = (st->host != NULL) ? st->host : MEMCACHED_DEF_HOST;
+  port = (st->port != NULL) ? st->port : MEMCACHED_DEF_PORT;
+
+  ai_list = NULL;
+  status = getaddrinfo (host, port, &ai_hints, &ai_list);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("memcached plugin: memcached_connect_inet: "
+        "getaddrinfo(%s,%s) failed: %s",
+        host, port,
+        (status == EAI_SYSTEM)
+        ? sstrerror (errno, errbuf, sizeof (errbuf))
+        : gai_strerror (status));
+    return (-1);
+  }
+
+  for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    /* create our socket descriptor */
+    fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (fd < 0)
+    {
+      char errbuf[1024];
+      WARNING ("memcached plugin: memcached_connect_inet: "
+          "socket(2) failed: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      continue;
+    }
+
+    /* connect to the memcached daemon */
+    status = (int) connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    if (status != 0)
+    {
+      shutdown (fd, SHUT_RDWR);
+      close (fd);
+      fd = -1;
+      continue;
+    }
+
+    /* A socket could be opened and connecting succeeded. We're done. */
+    break;
+  }
+
+  freeaddrinfo (ai_list);
+  return (fd);
+} /* int memcached_connect_inet */
+
+static int memcached_connect (memcached_t *st)
+{
+  if (st->socket != NULL)
+    return (memcached_connect_unix (st));
+  else
+    return (memcached_connect_inet (st));
 }
-/* }}} */
 
-static int memcached_config (const char *key, const char *value) /* {{{ */
+static int memcached_query_daemon (char *buffer, size_t buffer_size, memcached_t *st)
+{
+  int fd = -1;
+  int status;
+  size_t buffer_fill;
+
+  fd = memcached_connect (st);
+  if (fd < 0) {
+    ERROR ("memcached plugin: Instance \"%s\" could not connect to daemon.",
+        st->name);
+    return -1;
+  }
+
+  status = (int) swrite (fd, "stats\r\n", strlen ("stats\r\n"));
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("memcached plugin: write(2) failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    shutdown(fd, SHUT_RDWR);
+    close (fd);
+    return (-1);
+  }
+
+  /* receive data from the memcached daemon */
+  memset (buffer, 0, buffer_size);
+
+  buffer_fill = 0;
+  while ((status = (int) recv (fd, buffer + buffer_fill,
+          buffer_size - buffer_fill, /* flags = */ 0)) != 0)
+  {
+    char const end_token[5] = {'E', 'N', 'D', '\r', '\n'};
+    if (status < 0)
+    {
+      char errbuf[1024];
+
+      if ((errno == EAGAIN) || (errno == EINTR))
+          continue;
+
+      ERROR ("memcached: Error reading from socket: %s",
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      shutdown(fd, SHUT_RDWR);
+      close (fd);
+      return (-1);
+    }
+
+    buffer_fill += (size_t) status;
+    if (buffer_fill > buffer_size)
+    {
+      buffer_fill = buffer_size;
+      WARNING ("memcached plugin: Message was truncated.");
+      break;
+    }
+
+    /* If buffer ends in end_token, we have all the data. */
+    if (memcmp (buffer + buffer_fill - sizeof (end_token),
+          end_token, sizeof (end_token)) == 0)
+      break;
+  } /* while (recv) */
+
+  status = 0;
+  if (buffer_fill == 0)
+  {
+    WARNING ("memcached plugin: No data returned by memcached.");
+    status = -1;
+  }
+
+  shutdown(fd, SHUT_RDWR);
+  close(fd);
+  return (status);
+} /* int memcached_query_daemon */
+
+static void memcached_init_vl (value_list_t *vl, memcached_t const *st)
 {
-       if (strcasecmp (key, "Socket") == 0) {
-               if (memcached_socket != NULL) {
-                       free (memcached_socket);
-               }
-               memcached_socket = strdup (value);
-       } else if (strcasecmp (key, "Host") == 0) {
-               if (memcached_host != NULL) {
-                       free (memcached_host);
-               }
-               memcached_host = strdup (value);
-       } else if (strcasecmp (key, "Port") == 0) {
-               int port = (int) (atof (value));
-               if ((port > 0) && (port <= 65535)) {
-                       ssnprintf (memcached_port, sizeof (memcached_port), "%i", port);
-               } else {
-                       sstrncpy (memcached_port, value, sizeof (memcached_port));
-               }
-       } else {
-               return -1;
-       }
-
-       return 0;
+  sstrncpy (vl->plugin, "memcached", sizeof (vl->plugin));
+  if (strcmp (st->name, "__legacy__") == 0) /* legacy mode */
+  {
+    sstrncpy (vl->host, hostname_g, sizeof (vl->host));
+  }
+  else
+  {
+    if (st->socket != NULL)
+      sstrncpy (vl->host, hostname_g, sizeof (vl->host));
+    else
+      sstrncpy (vl->host,
+          (st->host != NULL) ? st->host : MEMCACHED_DEF_HOST,
+          sizeof (vl->host));
+    sstrncpy (vl->plugin_instance, st->name, sizeof (vl->plugin_instance));
+  }
 }
-/* }}} */
 
-static void submit_counter (const char *type, const char *type_inst,
-               counter_t value) /* {{{ */
+static void submit_derive (const char *type, const char *type_inst,
+    derive_t value, memcached_t *st)
 {
-       value_t values[1];
-       value_list_t vl = VALUE_LIST_INIT;
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+  memcached_init_vl (&vl, st);
 
-       values[0].counter = value;
+  values[0].derive = value;
 
-       vl.values = values;
-       vl.values_len = 1;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-       if (type_inst != NULL)
-               sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.type, type, sizeof (vl.type));
+  if (type_inst != NULL)
+    sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 
-       plugin_dispatch_values (&vl);
-} /* void memcached_submit_cmd */
-/* }}} */
+  plugin_dispatch_values (&vl);
+}
 
-static void submit_counter2 (const char *type, const char *type_inst,
-               counter_t value0, counter_t value1) /* {{{ */
+static void submit_derive2 (const char *type, const char *type_inst,
+    derive_t value0, derive_t value1, memcached_t *st)
 {
-       value_t values[2];
-       value_list_t vl = VALUE_LIST_INIT;
+  value_t values[2];
+  value_list_t vl = VALUE_LIST_INIT;
+  memcached_init_vl (&vl, st);
 
-       values[0].counter = value0;
-       values[1].counter = value1;
+  values[0].derive = value0;
+  values[1].derive = value1;
 
-       vl.values = values;
-       vl.values_len = 2;
-       vl.time = time (NULL);
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-       if (type_inst != NULL)
-               sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+  vl.values = values;
+  vl.values_len = 2;
+  sstrncpy (vl.type, type, sizeof (vl.type));
+  if (type_inst != NULL)
+    sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 
-       plugin_dispatch_values (&vl);
-} /* void memcached_submit_cmd */
-/* }}} */
+  plugin_dispatch_values (&vl);
+}
 
 static void submit_gauge (const char *type, const char *type_inst,
-               gauge_t value) /* {{{ */
+    gauge_t value, memcached_t *st)
 {
-       value_t values[1];
-       value_list_t vl = VALUE_LIST_INIT;
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+  memcached_init_vl (&vl, st);
 
-       values[0].gauge = value;
+  values[0].gauge = value;
 
-       vl.values = values;
-       vl.values_len = 1;
-       vl.time = time (NULL);
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-       if (type_inst != NULL)
-               sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.type, type, sizeof (vl.type));
+  if (type_inst != NULL)
+    sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 
-       plugin_dispatch_values (&vl);
+  plugin_dispatch_values (&vl);
 }
-/* }}} */
 
 static void submit_gauge2 (const char *type, const char *type_inst,
-               gauge_t value0, gauge_t value1) /* {{{ */
+    gauge_t value0, gauge_t value1, memcached_t *st)
 {
-       value_t values[2];
-       value_list_t vl = VALUE_LIST_INIT;
-
-       values[0].gauge = value0;
-       values[1].gauge = value1;
-
-       vl.values = values;
-       vl.values_len = 2;
-       vl.time = time (NULL);
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-       if (type_inst != NULL)
-               sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
-
-       plugin_dispatch_values (&vl);
+  value_t values[2];
+  value_list_t vl = VALUE_LIST_INIT;
+  memcached_init_vl (&vl, st);
+
+  values[0].gauge = value0;
+  values[1].gauge = value1;
+
+  vl.values = values;
+  vl.values_len = 2;
+  sstrncpy (vl.type, type, sizeof (vl.type));
+  if (type_inst != NULL)
+    sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+
+  plugin_dispatch_values (&vl);
 }
-/* }}} */
 
-static int memcached_read (void) /* {{{ */
+static int memcached_read (user_data_t *user_data)
 {
-       char buf[4096];
-       char *fields[3];
-       char *ptr;
-       char *line;
-       char *saveptr;
-       int fields_num;
-
-       gauge_t bytes_used = NAN;
-       gauge_t bytes_total = NAN;
-       gauge_t hits = NAN;
-       gauge_t gets = NAN;
-       counter_t rusage_user = 0;
-       counter_t rusage_syst = 0;
-       counter_t octets_rx = 0;
-       counter_t octets_tx = 0;
-
-       /* get data from daemon */
-       if (memcached_query_daemon (buf, sizeof (buf)) < 0) {
-               return -1;
-       }
+  char buf[4096];
+  char *fields[3];
+  char *ptr;
+  char *line;
+  char *saveptr;
+  int fields_num;
+
+  gauge_t bytes_used = NAN;
+  gauge_t bytes_total = NAN;
+  gauge_t hits = NAN;
+  gauge_t gets = NAN;
+  derive_t rusage_user = 0;
+  derive_t rusage_syst = 0;
+  derive_t octets_rx = 0;
+  derive_t octets_tx = 0;
+
+  memcached_t *st;
+  st = user_data->data;
+
+  /* get data from daemon */
+  if (memcached_query_daemon (buf, sizeof (buf), st) < 0) {
+    return -1;
+  }
 
 #define FIELD_IS(cnst) \
-       (((sizeof(cnst) - 1) == name_len) && (strcmp (cnst, fields[1]) == 0))
-
-       ptr = buf;
-       saveptr = NULL;
-       while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL)
-       {
-               int name_len;
-
-               ptr = NULL;
-
-               fields_num = strsplit(line, fields, 3);
-               if (fields_num != 3)
-                       continue;
-
-               name_len = strlen(fields[1]);
-               if (name_len == 0)
-                       continue;
-
-               /*
-                * For an explanation on these fields please refer to
-                * <http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt>
-                */
-
-               /*
-                * CPU time consumed by the memcached process
-                */
-               if (FIELD_IS ("rusage_user"))
-               {
-                       rusage_user = atoll (fields[2]);
-               }
-               else if (FIELD_IS ("rusage_system"))
-               {
-                       rusage_syst = atoll(fields[2]);
-               }
-
-               /*
-                * Number of threads of this instance
-                */
-               else if (FIELD_IS ("threads"))
-               {
-                       submit_gauge2 ("ps_count", NULL, NAN, atof (fields[2]));
-               }
-
-               /*
-                * Number of items stored
-                */
-               else if (FIELD_IS ("curr_items"))
-               {
-                       submit_gauge ("memcached_items", "current", atof (fields[2]));
-               }
-
-               /*
-                * Number of bytes used and available (total - used)
-                */
-               else if (FIELD_IS ("bytes"))
-               {
-                       bytes_used = atof (fields[2]);
-               }
-               else if (FIELD_IS ("limit_maxbytes"))
-               {
-                       bytes_total = atof(fields[2]);
-               }
-
-               /*
-                * Connections
-                */
-               else if (FIELD_IS ("curr_connections"))
-               {
-                       submit_gauge ("memcached_connections", "current", atof (fields[2]));
-               }
-
-               /*
-                * Commands
-                */
-               else if ((name_len > 4) && (strncmp (fields[1], "cmd_", 4) == 0))
-               {
-                       const char *name = fields[1] + 4;
-                       submit_counter ("memcached_command", name, atoll (fields[2]));
-                       if (strcmp (name, "get") == 0)
-                               gets = atof (fields[2]);
-               }
-
-               /*
-                * Operations on the cache, i. e. cache hits, cache misses and evictions of items
-                */
-               else if (FIELD_IS ("get_hits"))
-               {
-                       submit_counter ("memcached_ops", "hits", atoll (fields[2]));
-                       hits = atof (fields[2]);
-               }
-               else if (FIELD_IS ("get_misses"))
-               {
-                       submit_counter ("memcached_ops", "misses", atoll (fields[2]));
-               }
-               else if (FIELD_IS ("evictions"))
-               {
-                       submit_counter ("memcached_ops", "evictions", atoll (fields[2]));
-               }
-
-               /*
-                * Network traffic
-                */
-               else if (FIELD_IS ("bytes_read"))
-               {
-                       octets_rx = atoll (fields[2]);
-               }
-               else if (FIELD_IS ("bytes_written"))
-               {
-                       octets_tx = atoll (fields[2]);
-               }
-       } /* while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL) */
-
-       if (!isnan (bytes_used) && !isnan (bytes_total) && (bytes_used <= bytes_total))
-               submit_gauge2 ("df", "cache", bytes_used, bytes_total - bytes_used);
-
-       if ((rusage_user != 0) || (rusage_syst != 0))
-               submit_counter2 ("ps_cputime", NULL, rusage_user, rusage_syst);
-
-       if ((octets_rx != 0) || (octets_tx != 0))
-               submit_counter2 ("memcached_octets", NULL, octets_rx, octets_tx);
-
-       if (!isnan (gets) && !isnan (hits))
-       {
-               gauge_t rate = NAN;
-
-               if (gets != 0.0)
-                       rate = 100.0 * hits / gets;
-
-               submit_gauge ("percent", "hitratio", rate);
-       }
-
-       return 0;
+  (((sizeof(cnst) - 1) == name_len) && (strcmp (cnst, fields[1]) == 0))
+
+  ptr = buf;
+  saveptr = NULL;
+  while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL)
+  {
+    int name_len;
+
+    ptr = NULL;
+
+    fields_num = strsplit(line, fields, 3);
+    if (fields_num != 3)
+      continue;
+
+    name_len = strlen(fields[1]);
+    if (name_len == 0)
+      continue;
+
+    /*
+     * For an explanation on these fields please refer to
+     * <http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt>
+     */
+
+    /*
+     * CPU time consumed by the memcached process
+     */
+    if (FIELD_IS ("rusage_user"))
+    {
+      rusage_user = atoll (fields[2]);
+    }
+    else if (FIELD_IS ("rusage_system"))
+    {
+      rusage_syst = atoll(fields[2]);
+    }
+
+    /*
+     * Number of threads of this instance
+     */
+    else if (FIELD_IS ("threads"))
+    {
+      submit_gauge2 ("ps_count", NULL, NAN, atof (fields[2]), st);
+    }
+
+    /*
+     * Number of items stored
+     */
+    else if (FIELD_IS ("curr_items"))
+    {
+      submit_gauge ("memcached_items", "current", atof (fields[2]), st);
+    }
+
+    /*
+     * Number of bytes used and available (total - used)
+     */
+    else if (FIELD_IS ("bytes"))
+    {
+      bytes_used = atof (fields[2]);
+    }
+    else if (FIELD_IS ("limit_maxbytes"))
+    {
+      bytes_total = atof(fields[2]);
+    }
+
+    /*
+     * Connections
+     */
+    else if (FIELD_IS ("curr_connections"))
+    {
+      submit_gauge ("memcached_connections", "current", atof (fields[2]), st);
+    }
+
+    /*
+     * Commands
+     */
+    else if ((name_len > 4) && (strncmp (fields[1], "cmd_", 4) == 0))
+    {
+      const char *name = fields[1] + 4;
+      submit_derive ("memcached_command", name, atoll (fields[2]), st);
+      if (strcmp (name, "get") == 0)
+        gets = atof (fields[2]);
+    }
+
+    /*
+     * Operations on the cache, i. e. cache hits, cache misses and evictions of items
+     */
+    else if (FIELD_IS ("get_hits"))
+    {
+      submit_derive ("memcached_ops", "hits", atoll (fields[2]), st);
+      hits = atof (fields[2]);
+    }
+    else if (FIELD_IS ("get_misses"))
+    {
+      submit_derive ("memcached_ops", "misses", atoll (fields[2]), st);
+    }
+    else if (FIELD_IS ("evictions"))
+    {
+      submit_derive ("memcached_ops", "evictions", atoll (fields[2]), st);
+    }
+
+    /*
+     * Network traffic
+     */
+    else if (FIELD_IS ("bytes_read"))
+    {
+      octets_rx = atoll (fields[2]);
+    }
+    else if (FIELD_IS ("bytes_written"))
+    {
+      octets_tx = atoll (fields[2]);
+    }
+  } /* while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL) */
+
+  if (!isnan (bytes_used) && !isnan (bytes_total) && (bytes_used <= bytes_total))
+    submit_gauge2 ("df", "cache", bytes_used, bytes_total - bytes_used, st);
+
+  if ((rusage_user != 0) || (rusage_syst != 0))
+    submit_derive2 ("ps_cputime", NULL, rusage_user, rusage_syst, st);
+
+  if ((octets_rx != 0) || (octets_tx != 0))
+    submit_derive2 ("memcached_octets", NULL, octets_rx, octets_tx, st);
+
+  if (!isnan (gets) && !isnan (hits))
+  {
+    gauge_t rate = NAN;
+
+    if (gets != 0.0)
+      rate = 100.0 * hits / gets;
+
+    submit_gauge ("percent", "hitratio", rate, st);
+  }
+
+  return 0;
+} /* int memcached_read */
+
+static int memcached_add_read_callback (memcached_t *st)
+{
+  user_data_t ud;
+  char callback_name[3*DATA_MAX_NAME_LEN];
+  int status;
+
+  memset (&ud, 0, sizeof (ud));
+  ud.data = st;
+  ud.free_func = (void *) memcached_free;
+
+  assert (st->name != NULL);
+  ssnprintf (callback_name, sizeof (callback_name), "memcached/%s", st->name);
+
+  status = plugin_register_complex_read (/* group = */ "memcached",
+      /* name      = */ callback_name,
+      /* callback  = */ memcached_read,
+      /* interval  = */ NULL,
+      /* user_data = */ &ud);
+  return (status);
+} /* int memcached_add_read_callback */
+
+/* Configuration handling functiions
+ * <Plugin memcached>
+ *   <Instance "instance_name">
+ *     Host foo.zomg.com
+ *     Port "1234"
+ *   </Instance>
+ * </Plugin>
+ */
+static int config_add_instance(oconfig_item_t *ci)
+{
+  memcached_t *st;
+  int i;
+  int status = 0;
+
+  /* Disable automatic generation of default instance in the init callback. */
+  memcached_have_instances = 1;
+
+  st = malloc (sizeof (*st));
+  if (st == NULL)
+  {
+    ERROR ("memcached plugin: malloc failed.");
+    return (-1);
+  }
+
+  memset (st, 0, sizeof (*st));
+  st->name = NULL;
+  st->socket = NULL;
+  st->host = NULL;
+  st->port = NULL;
+
+  if (strcasecmp (ci->key, "Plugin") == 0) /* default instance */
+    st->name = sstrdup ("__legacy__");
+  else /* <Instance /> block */
+    status = cf_util_get_string (ci, &st->name);
+  if (status != 0)
+  {
+    sfree (st);
+    return (status);
+  }
+  assert (st->name != NULL);
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Socket", child->key) == 0)
+      status = cf_util_get_string (child, &st->socket);
+    else if (strcasecmp ("Host", child->key) == 0)
+      status = cf_util_get_string (child, &st->host);
+    else if (strcasecmp ("Port", child->key) == 0)
+      status = cf_util_get_service (child, &st->port);
+    else
+    {
+      WARNING ("memcached plugin: Option `%s' not allowed here.",
+          child->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  if (status == 0)
+    status = memcached_add_read_callback (st);
+
+  if (status != 0)
+  {
+    memcached_free(st);
+    return (-1);
+  }
+
+  return (0);
 }
-/* }}} */
 
-void module_register (void) /* {{{ */
+static int memcached_config (oconfig_item_t *ci)
 {
-       plugin_register_config ("memcached", memcached_config, config_keys, config_keys_num);
-       plugin_register_read ("memcached", memcached_read);
+  int status = 0;
+  _Bool have_instance_block = 0;
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Instance", child->key) == 0)
+    {
+      config_add_instance (child);
+      have_instance_block = 1;
+    }
+    else if (!have_instance_block)
+    {
+      /* Non-instance option: Assume legacy configuration (without <Instance />
+       * blocks) and call config_add_instance() with the <Plugin /> block. */
+      return (config_add_instance (ci));
+    }
+    else
+      WARNING ("memcached plugin: The configuration option "
+          "\"%s\" is not allowed here. Did you "
+          "forget to add an <Instance /> block "
+          "around the configuration?",
+          child->key);
+  } /* for (ci->children) */
+
+  return (status);
 }
-/* }}} */
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: sw=4 ts=4 fdm=marker noexpandtab
- * vim<600: sw=4 ts=4 noexpandtab
- */
 
+static int memcached_init (void)
+{
+  memcached_t *st;
+  int status;
+
+  if (memcached_have_instances)
+    return (0);
+
+  /* No instances were configured, lets start a default instance. */
+  st = malloc (sizeof (*st));
+  if (st == NULL)
+    return (ENOMEM);
+  memset (st, 0, sizeof (*st));
+  st->name = sstrdup ("__legacy__");
+  st->socket = NULL;
+  st->host = NULL;
+  st->port = NULL;
+
+  status = memcached_add_read_callback (st);
+  if (status == 0)
+    memcached_have_instances = 1;
+  else
+    memcached_free (st);
+
+  return (status);
+} /* int memcached_init */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("memcached", memcached_config);
+  plugin_register_init ("memcached", memcached_init);
+}
index ea98ba9..1b58735 100644 (file)
@@ -313,7 +313,13 @@ int meta_data_toc (meta_data_t *md, char ***toc) /* {{{ */
   for (e = md->head; e != NULL; e = e->next)
     ++count;    
 
-  *toc = malloc(count * sizeof(**toc));
+  if (count == 0)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (count);
+  }
+
+  *toc = calloc(count, sizeof(**toc));
   for (e = md->head; e != NULL; e = e->next)
     (*toc)[i++] = strdup(e->key);
   
index 40b6c57..887c63c 100644 (file)
@@ -1,19 +1,20 @@
 /**
  * collectd - src/modbus.c
- * Copyright (C) 2010  noris network AG
+ * Copyright (C) 2010,2011  noris network AG
  *
  * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; only version 2.1 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.
+ * Lesser 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
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  * Authors:
  *   Florian Forster <octo at noris.net>
 
 #include <modbus/modbus.h>
 
+#ifndef LIBMODBUS_VERSION_CHECK
+/* Assume version 2.0.3 */
+# define LEGACY_LIBMODBUS 1
+#else
+/* Assume version 2.9.2 */
+#endif
+
 #ifndef MODBUS_TCP_DEFAULT_PORT
 # ifdef MODBUS_TCP_PORT
 #  define MODBUS_TCP_DEFAULT_PORT MODBUS_TCP_PORT
@@ -61,6 +69,8 @@
  */
 enum mb_register_type_e /* {{{ */
 {
+  REG_TYPE_INT16,
+  REG_TYPE_INT32,
   REG_TYPE_UINT16,
   REG_TYPE_UINT32,
   REG_TYPE_FLOAT
@@ -94,14 +104,17 @@ struct mb_host_s /* {{{ */
   char node[NI_MAXHOST];
   /* char service[NI_MAXSERV]; */
   int port;
-  int interval;
+  cdtime_t interval;
 
   mb_slave_t *slaves;
   size_t slaves_num;
 
+#if LEGACY_LIBMODBUS
   modbus_param_t connection;
+#else
+  modbus_t *connection;
+#endif
   _Bool is_connected;
-  _Bool have_reconnected;
 }; /* }}} */
 typedef struct mb_host_s mb_host_t;
 
@@ -223,7 +236,7 @@ static int mb_submit (mb_host_t *host, mb_slave_t *slave, /* {{{ */
     return (EINVAL);
 
   if (host->interval <= 0)
-    host->interval = interval_g;
+    host->interval = plugin_get_interval ();
 
   if (slave->instance[0] == 0)
     ssnprintf (slave->instance, sizeof (slave->instance), "slave_%i",
@@ -246,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;
 
@@ -265,6 +279,8 @@ static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */
   return (conv.f);
 } /* }}} float mb_register_to_float */
 
+#if LEGACY_LIBMODBUS
+/* Version 2.0.3 */
 static int mb_init_connection (mb_host_t *host) /* {{{ */
 {
   int status;
@@ -272,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. */
@@ -303,9 +312,53 @@ 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 */
+
+#else /* if !LEGACY_LIBMODBUS */
+/* Version 2.9.2 */
+static int mb_init_connection (mb_host_t *host) /* {{{ */
+{
+  int status;
+
+  if (host == NULL)
+    return (EINVAL);
+
+  if (host->connection != NULL)
+    return (0);
+
+  if ((host->port < 1) || (host->port > 65535))
+    host->port = MODBUS_TCP_DEFAULT_PORT;
+
+  DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
+      host->node, host->port);
+
+  host->connection = modbus_new_tcp (host->node, host->port);
+  if (host->connection == NULL)
+  {
+    ERROR ("Modbus plugin: Creating new Modbus/TCP object failed.");
+    return (-1);
+  }
+
+  modbus_set_debug (host->connection, 1);
+
+  /* We'll do the error handling ourselves. */
+  modbus_set_error_recovery (host->connection, 0);
+
+  status = modbus_connect (host->connection);
+  if (status != 0)
+  {
+    ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.",
+        host->node, host->port, status);
+    modbus_free (host->connection);
+    host->connection = NULL;
+    return (status);
+  }
+
+  return (0);
+} /* }}} int mb_init_connection */
+#endif /* !LEGACY_LIBMODBUS */
 
 #define CAST_TO_VALUE_T(ds,vt,raw) do { \
   if ((ds)->ds[0].type == DS_TYPE_COUNTER) \
@@ -325,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);
@@ -346,6 +398,7 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 
   if ((ds->ds[0].type != DS_TYPE_GAUGE)
+      && (data->register_type != REG_TYPE_INT32)
       && (data->register_type != REG_TYPE_UINT32))
   {
     NOTICE ("Modbus plugin: The data source of type \"%s\" is %s, not gauge. "
@@ -354,52 +407,86 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 
   memset (values, 0, sizeof (values));
-  if ((data->register_type == REG_TYPE_UINT32)
+  if ((data->register_type == REG_TYPE_INT32)
+      || (data->register_type == REG_TYPE_UINT32)
       || (data->register_type == REG_TYPE_FLOAT))
     values_num = 2;
   else
     values_num = 1;
 
-  for (i = 0; i < 2; i++)
+  status = 0;
+  if (host->connection == NULL)
   {
-    status = read_holding_registers (&host->connection,
-        /* slave = */ slave->id, /* start_addr = */ data->register_base,
-        /* num_registers = */ values_num, /* buffer = */ values);
-    if (status > 0)
-      break;
-
-    if (host->is_connected)
-      modbus_close (&host->connection);
-    host->is_connected = 0;
+    status = EBADF;
+  }
+  else
+  {
+    struct sockaddr sockaddr;
+    socklen_t saddrlen = sizeof (sockaddr);
 
-    /* If we already tried reconnecting this round, give up. */
-    if (host->have_reconnected)
-    {
-      ERROR ("Modbus plugin: read_holding_registers (%s) failed. "
-          "Reconnecting has already been tried. Giving up.", host->host);
-      return (-1);
-    }
+    status = getpeername (modbus_get_socket (host->connection),
+        &sockaddr, &saddrlen);
+    if (status != 0)
+      status = errno;
+  }
 
-    /* Maybe the device closed the connection during the waiting interval.
-     * Try re-establishing the connection. */
+  if ((status == EBADF) || (status == ENOTSOCK) || (status == ENOTCONN))
+  {
     status = mb_init_connection (host);
     if (status != 0)
     {
-      ERROR ("Modbus plugin: read_holding_registers (%s) failed. "
-          "While trying to reconnect, connecting to \"%s\" failed. "
-          "Giving up.",
+      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". */
+# define modbus_read_registers(ctx, addr, nb, dest) \
+  read_holding_registers (&(ctx), slave->id, (addr), (nb), (dest))
+#else /* if !LEGACY_LIBMODBUS */
+  /* Version 2.9.2: Set the slave id once before querying the registers. */
+  status = modbus_set_slave (host->connection, slave->id);
+  if (status != 0)
+  {
+    ERROR ("Modbus plugin: modbus_set_slave (%i) failed with status %i.",
+        slave->id, status);
+    return (-1);
+  }
+#endif
 
-    DEBUG ("Modbus plugin: Re-established connection to %s", host->host);
-
-    /* try again */
-    continue;
-  } /* for (i = 0, 1) */
+  status = modbus_read_registers (host->connection,
+        /* start_addr = */ data->register_base,
+        /* num_registers = */ values_num, /* buffer = */ values);
+  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);
+#else
+    modbus_close (host->connection);
+    modbus_free (host->connection);
+#endif
+    host->connection = NULL;
+    return (-1);
+  }
 
   DEBUG ("Modbus plugin: mb_read_data: Success! "
-      "read_holding_registers returned with status %i.", status);
+      "modbus_read_registers returned with status %i.", status);
 
   if (data->register_type == REG_TYPE_FLOAT)
   {
@@ -413,12 +500,47 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
     CAST_TO_VALUE_T (ds, vt, float_value);
     mb_submit (host, slave, data, vt);
   }
+  else if (data->register_type == REG_TYPE_INT32)
+  {
+    union
+    {
+      uint32_t u32;
+      int32_t  i32;
+    } v;
+    value_t vt;
+
+    v.u32 = (((uint32_t) values[0]) << 16)
+      | ((uint32_t) values[1]);
+    DEBUG ("Modbus plugin: mb_read_data: "
+        "Returned int32 value is %"PRIi32, v.i32);
+
+    CAST_TO_VALUE_T (ds, vt, v.i32);
+    mb_submit (host, slave, data, vt);
+  }
+  else if (data->register_type == REG_TYPE_INT16)
+  {
+    union
+    {
+      uint16_t u16;
+      int16_t  i16;
+    } v;
+    value_t vt;
+
+    v.u16 = values[0];
+
+    DEBUG ("Modbus plugin: mb_read_data: "
+        "Returned int16 value is %"PRIi16, v.i16);
+
+    CAST_TO_VALUE_T (ds, vt, v.i16);
+    mb_submit (host, slave, data, vt);
+  }
   else if (data->register_type == REG_TYPE_UINT32)
   {
     uint32_t v32;
     value_t vt;
 
-    v32 = (values[0] << 16) | values[1];
+    v32 = (((uint32_t) values[0]) << 16)
+      | ((uint32_t) values[1]);
     DEBUG ("Modbus plugin: mb_read_data: "
         "Returned uint32 value is %"PRIu32, v32);
 
@@ -474,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++)
   {
@@ -574,6 +693,10 @@ static int mb_config_add_data (oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string_buffer (child, tmp, sizeof (tmp));
       if (status != 0)
         /* do nothing */;
+      else if (strcasecmp ("Int16", tmp) == 0)
+        data.register_type = REG_TYPE_INT16;
+      else if (strcasecmp ("Int32", tmp) == 0)
+        data.register_type = REG_TYPE_INT32;
       else if (strcasecmp ("Uint16", tmp) == 0)
         data.register_type = REG_TYPE_UINT16;
       else if (strcasecmp ("Uint32", tmp) == 0)
@@ -767,7 +890,7 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */
         status = -1;
     }
     else if (strcasecmp ("Interval", child->key) == 0)
-      status = cf_util_get_int (child, &host->interval);
+      status = cf_util_get_cdtime (child, &host->interval);
     else if (strcasecmp ("Slave", child->key) == 0)
       /* Don't set status: Gracefully continue if a slave fails. */
       mb_config_add_slave (host, child);
@@ -793,21 +916,19 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */
   {
     user_data_t ud;
     char name[1024];
-    struct timespec interval;
+    struct timespec interval = { 0, 0 };
 
     ud.data = host;
     ud.free_func = host_free;
 
     ssnprintf (name, sizeof (name), "modbus-%s", host->host);
 
-    interval.tv_nsec = 0;
-    if (host->interval > 0)
-      interval.tv_sec = host->interval;
-    else
-      interval.tv_sec = 0;
+    CDTIME_T_TO_TIMESPEC (host->interval, &interval);
 
     plugin_register_complex_read (/* group = */ NULL, name,
-        mb_read, (interval.tv_sec > 0) ? &interval : NULL, &ud);
+        /* callback = */ mb_read,
+        /* interval = */ (host->interval > 0) ? &interval : NULL,
+        &ud);
   }
   else
   {
index f93a442..d15a055 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/mysql.c
- * Copyright (C) 2006-2009  Florian octo Forster
+ * Copyright (C) 2006-2010  Florian octo Forster
  * Copyright (C) 2008       Mirko Buffoni
  * Copyright (C) 2009       Doug MacEachern
  * Copyright (C) 2009       Sebastian tokkee Harl
@@ -20,7 +20,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>
  *   Mirko Buffoni <briareos at eswat.org>
  *   Doug MacEachern <dougm at hyperic.com>
  *   Sebastian tokkee Harl <sh at tokkee.org>
@@ -42,7 +42,6 @@
 
 struct mysql_database_s /* {{{ */
 {
-       /* instance == NULL  =>  legacy mode */
        char *instance;
        char *host;
        char *user;
@@ -51,12 +50,12 @@ struct mysql_database_s /* {{{ */
        char *socket;
        int   port;
 
-       int   master_stats;
-       int   slave_stats;
+       _Bool master_stats;
+       _Bool slave_stats;
 
-       int   slave_notif;
-       int   slave_io_running;
-       int   slave_sql_running;
+       _Bool slave_notif;
+       _Bool slave_io_running;
+       _Bool slave_sql_running;
 
        MYSQL *con;
        _Bool  is_connected;
@@ -98,88 +97,9 @@ static void mysql_database_free (void *arg) /* {{{ */
  *   </Database>
  * </Plugin>
  */
-
-static int mysql_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 ("mysql 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 ("mysql plugin: strdup failed.");
-               return (-1);
-       }
-
-       if (*ret_string != NULL)
-               free (*ret_string);
-       *ret_string = string;
-
-       return (0);
-} /* }}} int mysql_config_set_string */
-
-static int mysql_config_set_int (int *ret_int, /* {{{ */
-                                oconfig_item_t *ci)
-{
-       if ((ci->values_num != 1)
-           || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
-       {
-               WARNING ("mysql plugin: The `%s' config option "
-                        "needs exactly one string argument.", ci->key);
-               return (-1);
-       }
-
-       *ret_int = ci->values[0].value.number;
-
-       return (0);
-} /* }}} int mysql_config_set_int */
-
-static int mysql_config_set_boolean (int *ret_boolean, /* {{{ */
-                               oconfig_item_t *ci)
-{
-       int status = 0;
-
-       if (ci->values_num != 1)
-               status = -1;
-
-       if (status == 0)
-       {
-               if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
-                       *ret_boolean = ci->values[0].value.boolean;
-               else if (ci->values[0].type == OCONFIG_TYPE_STRING)
-               {
-                       if (IS_TRUE (ci->values[0].value.string))
-                               *ret_boolean = 1;
-                       else if (IS_FALSE (ci->values[0].value.string))
-                               *ret_boolean = 0;
-                       else
-                               status = -1;
-               }
-               else
-                       status = -1;
-       }
-
-       if (status != 0)
-       {
-               WARNING ("mysql plugin: The `%s' config option "
-                       "needs exactly one boolean argument.", ci->key);
-               return (-1);
-       }
-       return (0);
-} /* }}} mysql_config_set_boolean */
-
-static int mysql_config (oconfig_item_t *ci) /* {{{ */
+static int mysql_config_database (oconfig_item_t *ci) /* {{{ */
 {
        mysql_database_t *db;
-       int plugin_block;
        int status = 0;
        int i;
 
@@ -211,28 +131,13 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */
        db->slave_io_running  = 1;
        db->slave_sql_running = 1;
 
-       plugin_block = 1;
-       if (strcasecmp ("Plugin", ci->key) == 0)
-       {
-               db->instance = NULL;
-       }
-       else if (strcasecmp ("Database", ci->key) == 0)
-       {
-               plugin_block = 0;
-               status = mysql_config_set_string (&db->instance, ci);
-               if (status != 0)
-               {
-                       sfree (db);
-                       return (status);
-               }
-               assert (db->instance != NULL);
-       }
-       else
+       status = cf_util_get_string (ci, &db->instance);
+       if (status != 0)
        {
-               ERROR ("mysql plugin: mysql_config: "
-                               "Invalid key: %s", ci->key);
-               return (-1);
+               sfree (db);
+               return (status);
        }
+       assert (db->instance != NULL);
 
        /* Fill the `mysql_database_t' structure.. */
        for (i = 0; i < ci->children_num; i++)
@@ -240,36 +145,30 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */
                oconfig_item_t *child = ci->children + i;
 
                if (strcasecmp ("Host", child->key) == 0)
-                       status = mysql_config_set_string (&db->host, child);
+                       status = cf_util_get_string (child, &db->host);
                else if (strcasecmp ("User", child->key) == 0)
-                       status = mysql_config_set_string (&db->user, child);
+                       status = cf_util_get_string (child, &db->user);
                else if (strcasecmp ("Password", child->key) == 0)
-                       status = mysql_config_set_string (&db->pass, child);
+                       status = cf_util_get_string (child, &db->pass);
                else if (strcasecmp ("Port", child->key) == 0)
-                       status = mysql_config_set_int (&db->port, child);
-               else if (strcasecmp ("Socket", child->key) == 0)
-                       status = mysql_config_set_string (&db->socket, child);
-               /* Check if we're currently handling the `Plugin' block. If so,
-                * handle `Database' _blocks_, too. */
-               else if ((plugin_block != 0)
-                               && (strcasecmp ("Database", child->key) == 0)
-                               && (child->children != NULL))
                {
-                       /* If `plugin_block > 1', there has been at least one
-                        * `Database' block */
-                       plugin_block++;
-                       status = mysql_config (child);
+                       status = cf_util_get_port_number (child);
+                       if (status > 0)
+                       {
+                               db->port = status;
+                               status = 0;
+                       }
                }
-               /* Now handle ordinary `Database' options (without children) */
-               else if ((strcasecmp ("Database", child->key) == 0)
-                               && (child->children == NULL))
-                       status = mysql_config_set_string (&db->database, child);
+               else if (strcasecmp ("Socket", child->key) == 0)
+                       status = cf_util_get_string (child, &db->socket);
+               else if (strcasecmp ("Database", child->key) == 0)
+                       status = cf_util_get_string (child, &db->database);
                else if (strcasecmp ("MasterStats", child->key) == 0)
-                       status = mysql_config_set_boolean (&db->master_stats, child);
+                       status = cf_util_get_boolean (child, &db->master_stats);
                else if (strcasecmp ("SlaveStats", child->key) == 0)
-                       status = mysql_config_set_boolean (&db->slave_stats, child);
+                       status = cf_util_get_boolean (child, &db->slave_stats);
                else if (strcasecmp ("SlaveNotifications", child->key) == 0)
-                       status = mysql_config_set_boolean (&db->slave_notif, child);
+                       status = cf_util_get_boolean (child, &db->slave_notif);
                else
                {
                        WARNING ("mysql plugin: Option `%s' not allowed here.", child->key);
@@ -280,49 +179,6 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */
                        break;
        }
 
-       /* Check if there were any `Database' blocks. */
-       if (plugin_block > 1)
-       {
-               /* There were connection blocks. Don't use any legacy stuff. */
-               if ((db->host != NULL)
-                       || (db->user != NULL)
-                       || (db->pass != NULL)
-                       || (db->database != NULL)
-                       || (db->socket != NULL)
-                       || (db->port != 0))
-               {
-                       WARNING ("mysql plugin: At least one <Database> "
-                                       "block has been found. The legacy "
-                                       "configuration will be ignored.");
-               }
-               mysql_database_free (db);
-               return (0);
-       }
-       else if (plugin_block != 0)
-       {
-               WARNING ("mysql plugin: You're using the legacy "
-                               "configuration options. Please consider "
-                               "updating your configuration!");
-       }
-
-       /* Check that all necessary options have been given. */
-       while (status == 0)
-       {
-               /* Zero is allowed and automatically handled by
-                * `mysql_real_connect'. */
-               if ((db->port < 0) || (db->port > 65535))
-               {
-                       ERROR ("mysql plugin: Database %s: Port number out "
-                                       "of range: %i",
-                                       (db->instance != NULL)
-                                       ? db->instance
-                                       : "<legacy>",
-                                       db->port);
-                       status = -1;
-               }
-               break;
-       } /* while (status == 0) */
-
        /* If all went well, register this database for reading */
        if (status == 0)
        {
@@ -336,9 +192,9 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */
                ud.data = (void *) db;
                ud.free_func = mysql_database_free;
 
-               if (db->database != NULL)
+               if (db->instance != NULL)
                        ssnprintf (cb_name, sizeof (cb_name), "mysql-%s",
-                                       db->database);
+                                       db->instance);
                else
                        sstrncpy (cb_name, "mysql", sizeof (cb_name));
 
@@ -353,6 +209,28 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */
        }
 
        return (0);
+} /* }}} int mysql_config_database */
+
+static int mysql_config (oconfig_item_t *ci) /* {{{ */
+{
+       int i;
+
+       if (ci == NULL)
+               return (EINVAL);
+
+       /* Fill the `mysql_database_t' structure.. */
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if (strcasecmp ("Database", child->key) == 0)
+                       mysql_config_database (child);
+               else
+                       WARNING ("mysql plugin: Option \"%s\" not allowed here.",
+                                       child->key);
+       }
+
+       return (0);
 } /* }}} int mysql_config */
 
 /* }}} End of configuration handling functions */
@@ -368,10 +246,7 @@ static MYSQL *getconnection (mysql_database_t *db)
                        return (db->con);
 
                WARNING ("mysql plugin: Lost connection to instance \"%s\": %s",
-                               (db->instance != NULL)
-                               ? db->instance
-                               : "<legacy>",
-                               mysql_error (db->con));
+                               db->instance, mysql_error (db->con));
        }
        db->is_connected = 0;
 
@@ -410,29 +285,13 @@ static MYSQL *getconnection (mysql_database_t *db)
 
 static void set_host (mysql_database_t *db, char *buf, size_t buflen)
 {
-       /* XXX legacy mode - use hostname_g */
-       if (db->instance == NULL)
+       if ((db->host == NULL)
+                       || (strcmp ("", db->host) == 0)
+                       || (strcmp ("localhost", db->host) == 0))
                sstrncpy (buf, hostname_g, buflen);
        else
-       {
-               if ((db->host == NULL)
-                               || (strcmp ("", db->host) == 0)
-                               || (strcmp ("localhost", db->host) == 0))
-                       sstrncpy (buf, hostname_g, buflen);
-               else
-                       sstrncpy (buf, db->host, buflen);
-       }
-}
-
-static void set_plugin_instance (mysql_database_t *db,
-               char *buf, size_t buflen)
-{
-       /* XXX legacy mode - no plugin_instance */
-       if (db->instance == NULL)
-               sstrncpy (buf, "", buflen);
-       else
-               sstrncpy (buf, db->instance, buflen);
-}
+               sstrncpy (buf, db->host, buflen);
+} /* void set_host */
 
 static void submit (const char *type, const char *type_instance,
                value_t *values, size_t values_len, mysql_database_t *db)
@@ -445,7 +304,10 @@ static void submit (const char *type, const char *type_instance,
        set_host (db, vl.host, sizeof (vl.host));
 
        sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin));
-       set_plugin_instance (db, vl.plugin_instance, sizeof (vl.plugin_instance));
+
+       /* Assured by "mysql_config_database" */
+       assert (db->instance != NULL);
+       sstrncpy (vl.plugin_instance, db->instance, sizeof (vl.plugin_instance));
 
        sstrncpy (vl.type, type, sizeof (vl.type));
        if (type_instance != NULL)
@@ -455,11 +317,11 @@ static void submit (const char *type, const char *type_instance,
 } /* submit */
 
 static void counter_submit (const char *type, const char *type_instance,
-               counter_t value, mysql_database_t *db)
+               derive_t value, mysql_database_t *db)
 {
        value_t values[1];
 
-       values[0].counter = value;
+       values[0].derive = value;
        submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db);
 } /* void counter_submit */
 
@@ -472,40 +334,21 @@ static void gauge_submit (const char *type, const char *type_instance,
        submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db);
 } /* void gauge_submit */
 
-static void qcache_submit (counter_t hits, counter_t inserts,
-               counter_t not_cached, counter_t lowmem_prunes,
-               gauge_t queries_in_cache, mysql_database_t *db)
+static void derive_submit (const char *type, const char *type_instance,
+               derive_t value, mysql_database_t *db)
 {
-       value_t values[5];
-
-       values[0].counter = hits;
-       values[1].counter = inserts;
-       values[2].counter = not_cached;
-       values[3].counter = lowmem_prunes;
-       values[4].gauge   = queries_in_cache;
-
-       submit ("mysql_qcache", NULL, values, STATIC_ARRAY_SIZE (values), db);
-} /* void qcache_submit */
-
-static void threads_submit (gauge_t running, gauge_t connected, gauge_t cached,
-               counter_t created, mysql_database_t *db)
-{
-       value_t values[4];
-
-       values[0].gauge   = running;
-       values[1].gauge   = connected;
-       values[2].gauge   = cached;
-       values[3].counter = created;
+       value_t values[1];
 
-       submit ("mysql_threads", NULL, values, STATIC_ARRAY_SIZE (values), db);
-} /* void threads_submit */
+       values[0].derive = value;
+       submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db);
+} /* void derive_submit */
 
-static void traffic_submit (counter_t rx, counter_t tx, mysql_database_t *db)
+static void traffic_submit (derive_t rx, derive_t tx, mysql_database_t *db)
 {
        value_t values[2];
 
-       values[0].counter = rx;
-       values[1].counter = tx;
+       values[0].derive = rx;
+       values[1].derive = tx;
 
        submit ("mysql_octets", NULL, values, STATIC_ARRAY_SIZE (values), db);
 } /* void traffic_submit */
@@ -642,7 +485,7 @@ static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con)
 
        if (db->slave_notif)
        {
-               notification_t n = { 0, time (NULL), "", "",
+               notification_t n = { 0, cdtime (), "", "",
                        "mysql", "", "time_offset", "", NULL };
 
                char *io, *sql;
@@ -651,8 +494,10 @@ static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con)
                sql = row[SLAVE_SQL_RUNNING_IDX];
 
                set_host (db, n.host, sizeof (n.host));
-               set_plugin_instance (db,
-                               n.plugin_instance, sizeof (n.plugin_instance));
+
+               /* Assured by "mysql_config_database" */
+               assert (db->instance != NULL);
+               sstrncpy (n.plugin_instance, db->instance, sizeof (n.plugin_instance));
 
                if (((io == NULL) || (strcasecmp (io, "yes") != 0))
                                && (db->slave_io_running))
@@ -711,16 +556,16 @@ static int mysql_read (user_data_t *ud)
        MYSQL_ROW  row;
        char      *query;
 
-       unsigned long long qcache_hits          = 0ULL;
-       unsigned long long qcache_inserts       = 0ULL;
-       unsigned long long qcache_not_cached    = 0ULL;
-       unsigned long long qcache_lowmem_prunes = 0ULL;
-       int qcache_queries_in_cache = -1;
+       derive_t qcache_hits          = 0;
+       derive_t qcache_inserts       = 0;
+       derive_t qcache_not_cached    = 0;
+       derive_t qcache_lowmem_prunes = 0;
+       gauge_t qcache_queries_in_cache = NAN;
 
-       int threads_running   = -1;
-       int threads_connected = -1;
-       int threads_cached    = -1;
-       unsigned long long threads_created = 0ULL;
+       gauge_t threads_running   = NAN;
+       gauge_t threads_connected = NAN;
+       gauge_t threads_cached    = NAN;
+       derive_t threads_created = 0;
 
        unsigned long long traffic_incoming = 0ULL;
        unsigned long long traffic_outgoing = 0ULL;
@@ -779,15 +624,15 @@ static int mysql_read (user_data_t *ud)
                                                strlen ("Qcache_")) == 0)
                {
                        if (strcmp (key, "Qcache_hits") == 0)
-                               qcache_hits = val;
+                               qcache_hits = (derive_t) val;
                        else if (strcmp (key, "Qcache_inserts") == 0)
-                               qcache_inserts = val;
+                               qcache_inserts = (derive_t) val;
                        else if (strcmp (key, "Qcache_not_cached") == 0)
-                               qcache_not_cached = val;
+                               qcache_not_cached = (derive_t) val;
                        else if (strcmp (key, "Qcache_lowmem_prunes") == 0)
-                               qcache_lowmem_prunes = val;
+                               qcache_lowmem_prunes = (derive_t) val;
                        else if (strcmp (key, "Qcache_queries_in_cache") == 0)
-                               qcache_queries_in_cache = (int) val;
+                               qcache_queries_in_cache = (gauge_t) val;
                }
                else if (strncmp (key, "Bytes_", 
                                        strlen ("Bytes_")) == 0)
@@ -801,13 +646,13 @@ static int mysql_read (user_data_t *ud)
                                                strlen ("Threads_")) == 0)
                {
                        if (strcmp (key, "Threads_running") == 0)
-                               threads_running = (int) val;
+                               threads_running = (gauge_t) val;
                        else if (strcmp (key, "Threads_connected") == 0)
-                               threads_connected = (int) val;
+                               threads_connected = (gauge_t) val;
                        else if (strcmp (key, "Threads_cached") == 0)
-                               threads_cached = (int) val;
+                               threads_cached = (gauge_t) val;
                        else if (strcmp (key, "Threads_created") == 0)
-                               threads_created = val;
+                               threads_created = (derive_t) val;
                }
                else if (strncmp (key, "Table_locks_",
                                        strlen ("Table_locks_")) == 0)
@@ -819,16 +664,36 @@ static int mysql_read (user_data_t *ud)
        }
        mysql_free_result (res); res = NULL;
 
-       if ((qcache_hits != 0ULL)
-                       || (qcache_inserts != 0ULL)
-                       || (qcache_not_cached != 0ULL)
-                       || (qcache_lowmem_prunes != 0ULL))
-               qcache_submit (qcache_hits, qcache_inserts, qcache_not_cached,
-                              qcache_lowmem_prunes, qcache_queries_in_cache, db);
+       if ((qcache_hits != 0)
+                       || (qcache_inserts != 0)
+                       || (qcache_not_cached != 0)
+                       || (qcache_lowmem_prunes != 0))
+       {
+               derive_submit ("cache_result", "qcache-hits",
+                               qcache_hits, db);
+               derive_submit ("cache_result", "qcache-inserts",
+                               qcache_inserts, db);
+               derive_submit ("cache_result", "qcache-not_cached",
+                               qcache_not_cached, db);
+               derive_submit ("cache_result", "qcache-prunes",
+                               qcache_lowmem_prunes, db);
+
+               gauge_submit ("cache_size", "qcache",
+                               qcache_queries_in_cache, db);
+       }
 
-       if (threads_created != 0ULL)
-               threads_submit (threads_running, threads_connected,
-                               threads_cached, threads_created, db);
+       if (threads_created != 0)
+       {
+               gauge_submit ("threads", "running",
+                               threads_running, db);
+               gauge_submit ("threads", "connected",
+                               threads_connected, db);
+               gauge_submit ("threads", "cached",
+                               threads_cached, db);
+
+               derive_submit ("total_threads", "created",
+                               threads_created, db);
+       }
 
        traffic_submit  (traffic_incoming, traffic_outgoing, db);
 
index c9ff6b5..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"
@@ -38,8 +40,8 @@ typedef void service_handler_t(host_config_t *host, na_elem_t *result, void *dat
 
 struct cna_interval_s
 {
-       time_t interval;
-       time_t last_read;
+       cdtime_t interval;
+       cdtime_t last_read;
 };
 typedef struct cna_interval_s cna_interval_t;
 
@@ -79,7 +81,7 @@ typedef struct {
        cna_interval_t interval;
        na_elem_t *query;
 
-       time_t timestamp;
+       cdtime_t timestamp;
        uint64_t name_cache_hit;
        uint64_t name_cache_miss;
        uint64_t find_dir_hit;
@@ -104,7 +106,7 @@ typedef struct {
 typedef struct disk_s {
        char *name;
        uint32_t flags;
-       time_t timestamp;
+       cdtime_t timestamp;
        uint64_t disk_busy;
        uint64_t base_for_disk_busy;
        double disk_busy_percent;
@@ -153,7 +155,7 @@ typedef struct data_volume_perf_s data_volume_perf_t;
 struct data_volume_perf_s {
        char *name;
        uint32_t flags;
-       time_t timestamp;
+       cdtime_t timestamp;
 
        uint64_t read_bytes;
        uint64_t write_bytes;
@@ -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,13 +268,16 @@ struct host_config_s {
        int port;
        char *username;
        char *password;
-       int interval;
+       char *vfiler;
+       cdtime_t interval;
 
        na_server_t *srv;
        cfg_wafl_t *cfg_wafl;
        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)
@@ -566,7 +620,7 @@ static int submit_values (const char *host, /* {{{ */
                const char *plugin_inst,
                const char *type, const char *type_inst,
                value_t *values, int values_len,
-               time_t timestamp, int interval)
+               cdtime_t timestamp, cdtime_t interval)
 {
        value_list_t vl = VALUE_LIST_INIT;
 
@@ -593,33 +647,34 @@ static int submit_values (const char *host, /* {{{ */
        return (plugin_dispatch_values (&vl));
 } /* }}} int submit_uint64 */
 
-static int submit_two_counters (const char *host, const char *plugin_inst, /* {{{ */
-               const char *type, const char *type_inst, counter_t val0, counter_t val1,
-               time_t timestamp, int interval)
+static int submit_two_derive (const char *host, const char *plugin_inst, /* {{{ */
+               const char *type, const char *type_inst, derive_t val0, derive_t val1,
+               cdtime_t timestamp, cdtime_t interval)
 {
        value_t values[2];
 
-       values[0].counter = val0;
-       values[1].counter = val1;
+       values[0].derive = val0;
+       values[1].derive = val1;
 
        return (submit_values (host, plugin_inst, type, type_inst,
                                values, 2, timestamp, interval));
-} /* }}} int submit_two_counters */
+} /* }}} int submit_two_derive */
 
-static int submit_counter (const char *host, const char *plugin_inst, /* {{{ */
-               const char *type, const char *type_inst, counter_t counter, time_t timestamp, int interval)
+static int submit_derive (const char *host, const char *plugin_inst, /* {{{ */
+               const char *type, const char *type_inst, derive_t counter,
+               cdtime_t timestamp, cdtime_t interval)
 {
        value_t v;
 
-       v.counter = counter;
+       v.derive = counter;
 
        return (submit_values (host, plugin_inst, type, type_inst,
                                &v, 1, timestamp, interval));
-} /* }}} int submit_counter */
+} /* }}} int submit_derive */
 
 static int submit_two_gauge (const char *host, const char *plugin_inst, /* {{{ */
                const char *type, const char *type_inst, gauge_t val0, gauge_t val1,
-               time_t timestamp, int interval)
+               cdtime_t timestamp, cdtime_t interval)
 {
        value_t values[2];
 
@@ -631,7 +686,8 @@ static int submit_two_gauge (const char *host, const char *plugin_inst, /* {{{ *
 } /* }}} int submit_two_gauge */
 
 static int submit_double (const char *host, const char *plugin_inst, /* {{{ */
-               const char *type, const char *type_inst, double d, time_t timestamp, int interval)
+               const char *type, const char *type_inst, double d,
+               cdtime_t timestamp, cdtime_t interval)
 {
        value_t v;
 
@@ -650,8 +706,8 @@ static int submit_cache_ratio (const char *host, /* {{{ */
                uint64_t new_misses,
                uint64_t old_hits,
                uint64_t old_misses,
-               time_t timestamp,
-               int interval)
+               cdtime_t timestamp,
+               cdtime_t interval)
 {
        value_t v;
 
@@ -673,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)
@@ -742,16 +798,16 @@ static int submit_volume_perf_data (const char *hostname, /* {{{ */
        if (HAS_ALL_FLAGS (old_data->flags, CFG_VOLUME_PERF_IO)
                        && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_BYTES_READ | HAVE_VOLUME_PERF_BYTES_WRITE))
        {
-               submit_two_counters (hostname, plugin_instance, "disk_octets", /* type instance = */ NULL,
-                               (counter_t) new_data->read_bytes, (counter_t) new_data->write_bytes, new_data->timestamp, interval);
+               submit_two_derive (hostname, plugin_instance, "disk_octets", /* type instance = */ NULL,
+                               (derive_t) new_data->read_bytes, (derive_t) new_data->write_bytes, new_data->timestamp, interval);
        }
 
        /* Check for and submit disk-operations values */
        if (HAS_ALL_FLAGS (old_data->flags, CFG_VOLUME_PERF_OPS)
                        && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE))
        {
-               submit_two_counters (hostname, plugin_instance, "disk_ops", /* type instance = */ NULL,
-                               (counter_t) new_data->read_ops, (counter_t) new_data->write_ops, new_data->timestamp, interval);
+               submit_two_derive (hostname, plugin_instance, "disk_ops", /* type instance = */ NULL,
+                               (derive_t) new_data->read_ops, (derive_t) new_data->write_ops, new_data->timestamp, interval);
        }
 
        /* Check for, calculate and submit disk-latency values */
@@ -816,6 +872,16 @@ static int submit_volume_perf_data (const char *hostname, /* {{{ */
        return (0);
 } /* }}} int submit_volume_perf_data */
 
+static cdtime_t cna_child_get_cdtime (na_elem_t *data) /* {{{ */
+{
+       time_t t;
+
+       t = (time_t) na_child_get_uint64 (data, "timestamp", /* default = */ 0);
+
+       return (TIME_T_TO_CDTIME_T (t));
+} /* }}} cdtime_t cna_child_get_cdtime */
+
+
 /* 
  * Query functions
  *
@@ -824,7 +890,7 @@ static int submit_volume_perf_data (const char *hostname, /* {{{ */
  */
 /* 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;
@@ -835,7 +901,7 @@ static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /*
 
        memset (&perf_data, 0, sizeof (perf_data));
        
-       perf_data.timestamp = (time_t) na_child_get_uint64 (data, "timestamp", 0);
+       perf_data.timestamp = cna_child_get_cdtime (data);
 
        instances = na_elem_child(na_elem_child (data, "instances"), "instance-data");
        if (instances == NULL)
@@ -950,7 +1016,7 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */
 {
        na_elem_t *data;
        int status;
-       time_t now;
+       cdtime_t now;
 
        if (host == NULL)
                return (EINVAL);
@@ -959,7 +1025,7 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */
        if (host->cfg_wafl == NULL)
                return (0);
 
-       now = time (NULL);
+       now = cdtime ();
        if ((host->cfg_wafl->interval.interval + host->cfg_wafl->interval.last_read) > now)
                return (0);
 
@@ -977,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;
@@ -988,9 +1055,9 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */
 
 /* Data corresponding to <Disks /> */
 static int cna_handle_disk_data (const char *hostname, /* {{{ */
-               cfg_disk_t *cfg_disk, na_elem_t *data, int interval)
+               cfg_disk_t *cfg_disk, na_elem_t *data, cdtime_t interval)
 {
-       time_t timestamp;
+       cdtime_t timestamp;
        na_elem_t *instances;
        na_elem_t *instance;
        na_elem_iter_t instance_iter;
@@ -999,7 +1066,7 @@ static int cna_handle_disk_data (const char *hostname, /* {{{ */
        if ((cfg_disk == NULL) || (data == NULL))
                return (EINVAL);
        
-       timestamp = (time_t) na_child_get_uint64(data, "timestamp", 0);
+       timestamp = cna_child_get_cdtime (data);
 
        instances = na_elem_child (data, "instances");
        if (instances == NULL)
@@ -1144,7 +1211,7 @@ static int cna_query_disk (host_config_t *host) /* {{{ */
 {
        na_elem_t *data;
        int status;
-       time_t now;
+       cdtime_t now;
 
        if (host == NULL)
                return (EINVAL);
@@ -1154,7 +1221,7 @@ static int cna_query_disk (host_config_t *host) /* {{{ */
        if (host->cfg_disk == NULL)
                return (0);
 
-       now = time (NULL);
+       now = cdtime ();
        if ((host->cfg_disk->interval.interval + host->cfg_disk->interval.last_read) > now)
                return (0);
 
@@ -1172,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;
@@ -1183,14 +1251,14 @@ static int cna_query_disk (host_config_t *host) /* {{{ */
 
 /* Data corresponding to <VolumePerf /> */
 static int cna_handle_volume_perf_data (const char *hostname, /* {{{ */
-               cfg_volume_perf_t *cvp, na_elem_t *data, int interval)
+               cfg_volume_perf_t *cvp, na_elem_t *data, cdtime_t interval)
 {
-       time_t timestamp;
+       cdtime_t timestamp;
        na_elem_t *elem_instances;
        na_elem_iter_t iter_instances;
        na_elem_t *elem_instance;
        
-       timestamp = (time_t) na_child_get_uint64(data, "timestamp", 0);
+       timestamp = cna_child_get_cdtime (data);
 
        elem_instances = na_elem_child(data, "instances");
        if (elem_instances == NULL)
@@ -1315,7 +1383,7 @@ static int cna_query_volume_perf (host_config_t *host) /* {{{ */
 {
        na_elem_t *data;
        int status;
-       time_t now;
+       cdtime_t now;
 
        if (host == NULL)
                return (EINVAL);
@@ -1325,7 +1393,7 @@ static int cna_query_volume_perf (host_config_t *host) /* {{{ */
        if (host->cfg_volume_perf == NULL)
                return (0);
 
-       now = time (NULL);
+       now = cdtime ();
        if ((host->cfg_volume_perf->interval.interval + host->cfg_volume_perf->interval.last_read) > now)
                return (0);
 
@@ -1343,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;
@@ -1365,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;
@@ -1409,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",
@@ -1444,7 +1525,7 @@ static int cna_change_volume_status (const char *hostname, /* {{{ */
        notification_t n;
 
        memset (&n, 0, sizeof (&n));
-       n.time = time (NULL);
+       n.time = cdtime ();
        sstrncpy (n.host, hostname, sizeof (n.host));
        sstrncpy (n.plugin, "netapp", sizeof (n.plugin));
        sstrncpy (n.plugin_instance, v->name, sizeof (n.plugin_instance));
@@ -1517,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)
 {
@@ -1544,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)
@@ -1587,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) /* {{{ */
@@ -1682,7 +1793,7 @@ static int cna_query_volume_usage (host_config_t *host) /* {{{ */
 {
        na_elem_t *data;
        int status;
-       time_t now;
+       cdtime_t now;
 
        if (host == NULL)
                return (EINVAL);
@@ -1692,7 +1803,7 @@ static int cna_query_volume_usage (host_config_t *host) /* {{{ */
        if (host->cfg_volume_usage == NULL)
                return (0);
 
-       now = time (NULL);
+       now = cdtime ();
        if ((host->cfg_volume_usage->interval.interval + host->cfg_volume_usage->interval.last_read) > now)
                return (0);
 
@@ -1719,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)
@@ -1727,15 +2124,15 @@ static int cna_handle_system_data (const char *hostname, /* {{{ */
        na_elem_t *counter;
        na_elem_iter_t counter_iter;
 
-       counter_t disk_read = 0, disk_written = 0;
-       counter_t net_recv = 0, net_sent = 0;
-       counter_t cpu_busy = 0, cpu_total = 0;
+       derive_t disk_read = 0, disk_written = 0;
+       derive_t net_recv = 0, net_sent = 0;
+       derive_t cpu_busy = 0, cpu_total = 0;
        uint32_t counter_flags = 0;
 
        const char *instance;
-       time_t timestamp;
+       cdtime_t timestamp;
        
-       timestamp = (time_t) na_child_get_uint64 (data, "timestamp", 0);
+       timestamp = cna_child_get_cdtime (data);
 
        instances = na_elem_child(na_elem_child (data, "instances"), "instance-data");
        if (instances == NULL)
@@ -1772,47 +2169,47 @@ static int cna_handle_system_data (const char *hostname, /* {{{ */
                        continue;
 
                if (!strcmp(name, "disk_data_read")) {
-                       disk_read = (counter_t) (value * 1024);
+                       disk_read = (derive_t) (value * 1024);
                        counter_flags |= 0x01;
                } else if (!strcmp(name, "disk_data_written")) {
-                       disk_written = (counter_t) (value * 1024);
+                       disk_written = (derive_t) (value * 1024);
                        counter_flags |= 0x02;
                } else if (!strcmp(name, "net_data_recv")) {
-                       net_recv = (counter_t) (value * 1024);
+                       net_recv = (derive_t) (value * 1024);
                        counter_flags |= 0x04;
                } else if (!strcmp(name, "net_data_sent")) {
-                       net_sent = (counter_t) (value * 1024);
+                       net_sent = (derive_t) (value * 1024);
                        counter_flags |= 0x08;
                } else if (!strcmp(name, "cpu_busy")) {
-                       cpu_busy = (counter_t) value;
+                       cpu_busy = (derive_t) value;
                        counter_flags |= 0x10;
                } else if (!strcmp(name, "cpu_elapsed_time")) {
-                       cpu_total = (counter_t) value;
+                       cpu_total = (derive_t) value;
                        counter_flags |= 0x20;
                } else if ((cfg_system->flags & CFG_SYSTEM_OPS)
                                && (value > 0) && (strlen(name) > 4)
                                && (!strcmp(name + strlen(name) - 4, "_ops"))) {
-                       submit_counter (hostname, instance, "disk_ops_complex", name,
-                                       (counter_t) value, timestamp, interval);
+                       submit_derive (hostname, instance, "disk_ops_complex", name,
+                                       (derive_t) value, timestamp, interval);
                }
        } /* for (counter) */
 
        if ((cfg_system->flags & CFG_SYSTEM_DISK)
                        && (HAS_ALL_FLAGS (counter_flags, 0x01 | 0x02)))
-               submit_two_counters (hostname, instance, "disk_octets", NULL,
+               submit_two_derive (hostname, instance, "disk_octets", NULL,
                                disk_read, disk_written, timestamp, interval);
                                
        if ((cfg_system->flags & CFG_SYSTEM_NET)
                        && (HAS_ALL_FLAGS (counter_flags, 0x04 | 0x08)))
-               submit_two_counters (hostname, instance, "if_octets", NULL,
+               submit_two_derive (hostname, instance, "if_octets", NULL,
                                net_recv, net_sent, timestamp, interval);
 
        if ((cfg_system->flags & CFG_SYSTEM_CPU)
                        && (HAS_ALL_FLAGS (counter_flags, 0x10 | 0x20)))
        {
-               submit_counter (hostname, instance, "cpu", "system",
+               submit_derive (hostname, instance, "cpu", "system",
                                cpu_busy, timestamp, interval);
-               submit_counter (hostname, instance, "cpu", "idle",
+               submit_derive (hostname, instance, "cpu", "idle",
                                cpu_total - cpu_busy, timestamp, interval);
        }
 
@@ -1842,7 +2239,7 @@ static int cna_query_system (host_config_t *host) /* {{{ */
 {
        na_elem_t *data;
        int status;
-       time_t now;
+       cdtime_t now;
 
        if (host == NULL)
                return (EINVAL);
@@ -1851,7 +2248,7 @@ static int cna_query_system (host_config_t *host) /* {{{ */
        if (host->cfg_system == NULL)
                return (0);
 
-       now = time (NULL);
+       now = cdtime ();
        if ((host->cfg_system->interval.interval + host->cfg_system->interval.last_read) > now)
                return (0);
 
@@ -1869,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;
@@ -1908,23 +2306,12 @@ static int cna_config_bool_to_flag (const oconfig_item_t *ci, /* {{{ */
 static int cna_config_get_interval (const oconfig_item_t *ci, /* {{{ */
                cna_interval_t *out_interval)
 {
-       time_t tmp;
-
-       if ((ci == NULL) || (out_interval == NULL))
-               return (EINVAL);
-
-       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
-       {
-               WARNING ("netapp plugin: The `Interval' option needs exactly one numeric argument.");
-               return (-1);
-       }
+       cdtime_t tmp = 0;
+       int status;
 
-       tmp = (time_t) (ci->values[0].value.number + .5);
-       if (tmp < 1)
-       {
-               WARNING ("netapp plugin: The `Interval' option needs a positive integer argument.");
-               return (-1);
-       }
+       status = cf_util_get_cdtime (ci, &tmp);
+       if (status != 0)
+               return (status);
 
        out_interval->interval = tmp;
        out_interval->last_read = 0;
@@ -2135,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;
@@ -2310,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)
@@ -2366,39 +2823,132 @@ 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;
 
@@ -2415,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;
@@ -2424,11 +2974,7 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
                } else if (!strcasecmp(item->key, "Password")) {
                        status = cf_util_get_string (item, &host->password);
                } else if (!strcasecmp(item->key, "Interval")) {
-                       if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_NUMBER || item->values[0].value.number != (int) item->values[0].value.number || item->values[0].value.number < 2) {
-                               WARNING("netapp plugin: \"Interval\" of host %s needs exactly one integer argument.", ci->values[0].value.string);
-                               continue;
-                       }
-                       host->interval = item->values[0].value.number;
+                       status = cf_util_get_cdtime (item, &host->interval);
                } else if (!strcasecmp(item->key, "WAFL")) {
                        cna_config_wafl(host, item);
                } else if (!strcasecmp(item->key, "Disks")) {
@@ -2437,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)
@@ -2451,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;
 
@@ -2464,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 */
 
 /*
@@ -2479,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);
@@ -2500,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 */
 
@@ -2536,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);
@@ -2577,28 +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);
-
-                       memset (&interval, 0, sizeof (interval));
-                       interval.tv_sec = host->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 bac8757..39536d9 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/netlink.c
- * Copyright (C) 2007  Florian octo Forster
+ * Copyright (C) 2007-2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -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"
@@ -161,12 +161,12 @@ static int check_ignorelist (const char *dev,
 } /* int check_ignorelist */
 
 static void submit_one (const char *dev, const char *type,
-    const char *type_instance, counter_t value)
+    const char *type_instance, derive_t value)
 {
   value_t values[1];
   value_list_t vl = VALUE_LIST_INIT;
 
-  values[0].counter = value;
+  values[0].derive = value;
 
   vl.values = values;
   vl.values_len = 1;
@@ -183,13 +183,13 @@ static void submit_one (const char *dev, const char *type,
 
 static void submit_two (const char *dev, const char *type,
     const char *type_instance,
-    counter_t rx, counter_t tx)
+    derive_t rx, derive_t tx)
 {
   value_t values[2];
   value_list_t vl = VALUE_LIST_INIT;
 
-  values[0].counter = rx;
-  values[1].counter = tx;
+  values[0].derive = rx;
+  values[1].derive = tx;
 
   vl.values = values;
   vl.values_len = 2;
index f379a5c..a2221e5 100644 (file)
@@ -76,7 +76,9 @@
 /* Re enable deprecation warnings */
 #  pragma GCC diagnostic warning "-Wdeprecated-declarations"
 # endif
+# if GCRYPT_VERSION_NUMBER < 0x010600
 GCRY_THREAD_OPTION_PTHREAD_IMPL;
+# endif
 #endif
 
 #ifndef IPV6_ADD_MEMBERSHIP
@@ -276,7 +278,8 @@ typedef struct receive_list_entry_s receive_list_entry_t;
  * Private variables
  */
 static int network_config_ttl = 0;
-static size_t network_config_packet_size = 1024;
+/* Ethernet - (IPv6 + UDP) = 1500 - (40 + 8) = 1452 */
+static size_t network_config_packet_size = 1452;
 static int network_config_forward = 0;
 static int network_config_stats = 0;
 
@@ -313,14 +316,14 @@ static pthread_mutex_t  send_buffer_lock = PTHREAD_MUTEX_INITIALIZER;
  * example). Only if neither is true, the stats_lock is acquired. The counters
  * are always read without holding a lock in the hope that writing 8 bytes to
  * memory is an atomic operation. */
-static uint64_t stats_octets_rx  = 0;
-static uint64_t stats_octets_tx  = 0;
-static uint64_t stats_packets_rx = 0;
-static uint64_t stats_packets_tx = 0;
-static uint64_t stats_values_dispatched = 0;
-static uint64_t stats_values_not_dispatched = 0;
-static uint64_t stats_values_sent = 0;
-static uint64_t stats_values_not_sent = 0;
+static derive_t stats_octets_rx  = 0;
+static derive_t stats_octets_tx  = 0;
+static derive_t stats_packets_rx = 0;
+static derive_t stats_packets_tx = 0;
+static derive_t stats_values_dispatched = 0;
+static derive_t stats_values_not_dispatched = 0;
+static derive_t stats_values_sent = 0;
+static derive_t stats_values_not_sent = 0;
 static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
 
 /*
@@ -337,30 +340,30 @@ static _Bool check_receive_okay (const value_list_t *vl) /* {{{ */
   /* This is a value we already sent. Don't allow it to be received again in
    * order to avoid looping. */
   if ((status == 0) && (time_sent >= ((uint64_t) vl->time)))
-    return (false);
+    return (0);
 
-  return (true);
+  return (1);
 } /* }}} _Bool check_receive_okay */
 
 static _Bool check_send_okay (const value_list_t *vl) /* {{{ */
 {
-  _Bool received = false;
+  _Bool received = 0;
   int status;
 
   if (network_config_forward != 0)
-    return (true);
+    return (1);
 
   if (vl->meta == NULL)
-    return (true);
+    return (1);
 
   status = meta_data_get_boolean (vl->meta, "network:received", &received);
   if (status == -ENOENT)
-    return (true);
+    return (1);
   else if (status != 0)
   {
     ERROR ("network plugin: check_send_okay: meta_data_get_boolean failed "
        "with status %i.", status);
-    return (true);
+    return (1);
   }
 
   /* By default, only *send* value lists that were not *received* by the
@@ -394,7 +397,7 @@ static _Bool check_send_notify_okay (const notification_t *n) /* {{{ */
   {
     c_complain_once (LOG_ERR, &complain_forwarding,
         "network plugin: A notification has been received via the network "
-        "forwarding if enabled. Forwarding of notifications is currently "
+        "and forwarding is enabled. Forwarding of notifications is currently "
         "not supported, because there is not loop-deteciton available. "
         "Please contact the collectd mailing list if you need this "
         "feature.");
@@ -438,7 +441,7 @@ static int network_dispatch_values (value_list_t *vl, /* {{{ */
     return (-ENOMEM);
   }
 
-  status = meta_data_add_boolean (vl->meta, "network:received", true);
+  status = meta_data_add_boolean (vl->meta, "network:received", 1);
   if (status != 0)
   {
     ERROR ("network plugin: meta_data_add_boolean failed.");
@@ -459,7 +462,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);
@@ -507,7 +510,9 @@ static void network_init_gcrypt (void) /* {{{ */
   * above doesn't count, as it doesn't implicitly initalize Libgcrypt.
   *
   * tl;dr: keep all these gry_* statements in this exact order please. */
+# if GCRYPT_VERSION_NUMBER < 0x010600
   gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+# endif
   gcry_check_version (NULL);
   gcry_control (GCRYCTL_INIT_SECMEM, 32768);
   gcry_control (GCRYCTL_INITIALIZATION_FINISHED);
@@ -1477,8 +1482,19 @@ static int parse_packet (sockent_t *se, /* {{{ */
                                        &tmp);
                        if (status == 0)
                        {
-                               vl.time = (time_t) tmp;
-                               n.time = (time_t) tmp;
+                               vl.time = TIME_T_TO_CDTIME_T (tmp);
+                               n.time  = TIME_T_TO_CDTIME_T (tmp);
+                       }
+               }
+               else if (pkg_type == TYPE_TIME_HR)
+               {
+                       uint64_t tmp = 0;
+                       status = parse_part_number (&buffer, &buffer_size,
+                                       &tmp);
+                       if (status == 0)
+                       {
+                               vl.time = (cdtime_t) tmp;
+                               n.time  = (cdtime_t) tmp;
                        }
                }
                else if (pkg_type == TYPE_INTERVAL)
@@ -1487,7 +1503,15 @@ static int parse_packet (sockent_t *se, /* {{{ */
                        status = parse_part_number (&buffer, &buffer_size,
                                        &tmp);
                        if (status == 0)
-                               vl.interval = (int) tmp;
+                               vl.interval = TIME_T_TO_CDTIME_T (tmp);
+               }
+               else if (pkg_type == TYPE_INTERVAL_HR)
+               {
+                       uint64_t tmp = 0;
+                       status = parse_part_number (&buffer, &buffer_size,
+                                       &tmp);
+                       if (status == 0)
+                               vl.interval = (cdtime_t) tmp;
                }
                else if (pkg_type == TYPE_HOST)
                {
@@ -1972,14 +1996,19 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
 
 /* Initialize a sockent structure. `type' must be either `SOCKENT_TYPE_CLIENT'
  * or `SOCKENT_TYPE_SERVER' */
-static int sockent_init (sockent_t *se, int type) /* {{{ */
+static sockent_t *sockent_create (int type) /* {{{ */
 {
-       if (se == NULL)
-               return (-1);
+       sockent_t *se;
+
+       if ((type != SOCKENT_TYPE_CLIENT) && (type != SOCKENT_TYPE_SERVER))
+               return (NULL);
 
+       se = malloc (sizeof (*se));
+       if (se == NULL)
+               return (NULL);
        memset (se, 0, sizeof (*se));
 
-       se->type = SOCKENT_TYPE_CLIENT;
+       se->type = type;
        se->node = NULL;
        se->service = NULL;
        se->interface = 0;
@@ -1987,7 +2016,6 @@ static int sockent_init (sockent_t *se, int type) /* {{{ */
 
        if (type == SOCKENT_TYPE_SERVER)
        {
-               se->type = SOCKENT_TYPE_SERVER;
                se->data.server.fd = NULL;
 #if HAVE_LIBGCRYPT
                se->data.server.security_level = SECURITY_LEVEL_NONE;
@@ -2008,23 +2036,11 @@ static int sockent_init (sockent_t *se, int type) /* {{{ */
 #endif
        }
 
-       return (0);
-} /* }}} int sockent_init */
+       return (se);
+} /* }}} sockent_t *sockent_create */
 
-/* Open the file descriptors for a initialized sockent structure. */
-static int sockent_open (sockent_t *se) /* {{{ */
+static int sockent_init_crypto (sockent_t *se) /* {{{ */
 {
-       struct addrinfo  ai_hints;
-       struct addrinfo *ai_list, *ai_ptr;
-       int              ai_return;
-
-        const char *node;
-        const char *service;
-
-       if (se == NULL)
-               return (-1);
-
-       /* Set up the security structures. */
 #if HAVE_LIBGCRYPT /* {{{ */
        if (se->type == SOCKENT_TYPE_CLIENT)
        {
@@ -2075,13 +2091,134 @@ static int sockent_open (sockent_t *se) /* {{{ */
        }
 #endif /* }}} HAVE_LIBGCRYPT */
 
+       return (0);
+} /* }}} int sockent_init_crypto */
+
+static int sockent_client_connect (sockent_t *se) /* {{{ */
+{
+       static c_complain_t complaint = C_COMPLAIN_INIT_STATIC;
+
+       struct sockent_client *client;
+       struct addrinfo  ai_hints;
+       struct addrinfo *ai_list = NULL, *ai_ptr;
+       int status;
+
+       if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
+               return (EINVAL);
+
+       client = &se->data.client;
+       if (client->fd >= 0) /* already connected */
+               return (0);
+
+       memset (&ai_hints, 0, sizeof (ai_hints));
+#ifdef AI_ADDRCONFIG
+       ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+       ai_hints.ai_family   = AF_UNSPEC;
+       ai_hints.ai_socktype = SOCK_DGRAM;
+       ai_hints.ai_protocol = IPPROTO_UDP;
+
+       status = getaddrinfo (se->node,
+                       (se->service != NULL) ? se->service : NET_DEFAULT_PORT,
+                       &ai_hints, &ai_list);
+       if (status != 0)
+       {
+               c_complain (LOG_ERR, &complaint,
+                               "network plugin: getaddrinfo (%s, %s) failed: %s",
+                               (se->node == NULL) ? "(null)" : se->node,
+                               (se->service == NULL) ? "(null)" : se->service,
+                               gai_strerror (status));
+               return (-1);
+       }
+       else
+       {
+               c_release (LOG_NOTICE, &complaint,
+                               "network plugin: Successfully resolved \"%s\".",
+                               se->node);
+       }
+
+       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+       {
+               client->fd = socket (ai_ptr->ai_family,
+                               ai_ptr->ai_socktype,
+                               ai_ptr->ai_protocol);
+               if (client->fd < 0)
+               {
+                       char errbuf[1024];
+                       ERROR ("network plugin: socket(2) failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
+                       continue;
+               }
+
+               client->addr = malloc (sizeof (*client->addr));
+               if (client->addr == NULL)
+               {
+                       ERROR ("network plugin: malloc failed.");
+                       close (client->fd);
+                       client->fd = -1;
+                       continue;
+               }
+
+               memset (client->addr, 0, sizeof (*client->addr));
+               assert (sizeof (*client->addr) >= ai_ptr->ai_addrlen);
+               memcpy (client->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+               client->addrlen = ai_ptr->ai_addrlen;
+
+               network_set_ttl (se, ai_ptr);
+               network_set_interface (se, ai_ptr);
+
+               /* We don't open more than one write-socket per
+                * node/service pair.. */
+               break;
+       }
+
+       freeaddrinfo (ai_list);
+       if (client->fd < 0)
+               return (-1);
+       return (0);
+} /* }}} int sockent_client_connect */
+
+static int sockent_client_disconnect (sockent_t *se) /* {{{ */
+{
+       struct sockent_client *client;
+
+       if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
+               return (EINVAL);
+
+       client = &se->data.client;
+       if (client->fd >= 0) /* connected */
+       {
+               close (client->fd);
+               client->fd = -1;
+       }
+
+       sfree (client->addr);
+       client->addrlen = 0;
+
+       return (0);
+} /* }}} int sockent_client_disconnect */
+
+/* Open the file descriptors for a initialized sockent structure. */
+static int sockent_server_listen (sockent_t *se) /* {{{ */
+{
+       struct addrinfo  ai_hints;
+       struct addrinfo *ai_list, *ai_ptr;
+       int              status;
+
+        const char *node;
+        const char *service;
+
+       if (se == NULL)
+               return (-1);
+
         node = se->node;
         service = se->service;
 
         if (service == NULL)
           service = NET_DEFAULT_PORT;
 
-        DEBUG ("network plugin: sockent_open: node = %s; service = %s;",
+        DEBUG ("network plugin: sockent_server_listen: node = %s; service = %s;",
             node, service);
 
        memset (&ai_hints, 0, sizeof (ai_hints));
@@ -2096,109 +2233,59 @@ static int sockent_open (sockent_t *se) /* {{{ */
        ai_hints.ai_socktype = SOCK_DGRAM;
        ai_hints.ai_protocol = IPPROTO_UDP;
 
-       ai_return = getaddrinfo (node, service, &ai_hints, &ai_list);
-       if (ai_return != 0)
+       status = getaddrinfo (node, service, &ai_hints, &ai_list);
+       if (status != 0)
        {
                ERROR ("network plugin: getaddrinfo (%s, %s) failed: %s",
                                (se->node == NULL) ? "(null)" : se->node,
                                (se->service == NULL) ? "(null)" : se->service,
-                               gai_strerror (ai_return));
+                               gai_strerror (status));
                return (-1);
        }
 
        for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
        {
-               int status;
+               int *tmp;
 
-               if (se->type == SOCKENT_TYPE_SERVER) /* {{{ */
+               tmp = realloc (se->data.server.fd,
+                               sizeof (*tmp) * (se->data.server.fd_num + 1));
+               if (tmp == NULL)
                {
-                       int *tmp;
-
-                       tmp = realloc (se->data.server.fd,
-                                       sizeof (*tmp) * (se->data.server.fd_num + 1));
-                       if (tmp == NULL)
-                       {
-                               ERROR ("network plugin: realloc failed.");
-                               continue;
-                       }
-                       se->data.server.fd = tmp;
-                       tmp = se->data.server.fd + se->data.server.fd_num;
-
-                       *tmp = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
-                                       ai_ptr->ai_protocol);
-                       if (*tmp < 0)
-                       {
-                               char errbuf[1024];
-                               ERROR ("network plugin: socket(2) failed: %s",
-                                               sstrerror (errno, errbuf,
-                                                       sizeof (errbuf)));
-                               continue;
-                       }
-
-                       status = network_bind_socket (*tmp, ai_ptr, se->interface);
-                       if (status != 0)
-                       {
-                               close (*tmp);
-                               *tmp = -1;
-                               continue;
-                       }
-
-                       se->data.server.fd_num++;
+                       ERROR ("network plugin: realloc failed.");
                        continue;
-               } /* }}} if (se->type == SOCKENT_TYPE_SERVER) */
-               else /* if (se->type == SOCKENT_TYPE_CLIENT) {{{ */
-               {
-                       se->data.client.fd = socket (ai_ptr->ai_family,
-                                       ai_ptr->ai_socktype,
-                                       ai_ptr->ai_protocol);
-                       if (se->data.client.fd < 0)
-                       {
-                               char errbuf[1024];
-                               ERROR ("network plugin: socket(2) failed: %s",
-                                               sstrerror (errno, errbuf,
-                                                       sizeof (errbuf)));
-                               continue;
-                       }
-
-                       se->data.client.addr = malloc (sizeof (*se->data.client.addr));
-                       if (se->data.client.addr == NULL)
-                       {
-                               ERROR ("network plugin: malloc failed.");
-                               close (se->data.client.fd);
-                               se->data.client.fd = -1;
-                               continue;
-                       }
+               }
+               se->data.server.fd = tmp;
+               tmp = se->data.server.fd + se->data.server.fd_num;
 
-                       memset (se->data.client.addr, 0, sizeof (*se->data.client.addr));
-                       assert (sizeof (*se->data.client.addr) >= ai_ptr->ai_addrlen);
-                       memcpy (se->data.client.addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
-                       se->data.client.addrlen = ai_ptr->ai_addrlen;
+               *tmp = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
+                               ai_ptr->ai_protocol);
+               if (*tmp < 0)
+               {
+                       char errbuf[1024];
+                       ERROR ("network plugin: socket(2) failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
+                       continue;
+               }
 
-                       network_set_ttl (se, ai_ptr);
-                       network_set_interface (se, ai_ptr);
+               status = network_bind_socket (*tmp, ai_ptr, se->interface);
+               if (status != 0)
+               {
+                       close (*tmp);
+                       *tmp = -1;
+                       continue;
+               }
 
-                       /* We don't open more than one write-socket per
-                        * node/service pair.. */
-                       break;
-               } /* }}} if (se->type == SOCKENT_TYPE_CLIENT) */
+               se->data.server.fd_num++;
+               continue;
        } /* for (ai_list) */
 
        freeaddrinfo (ai_list);
 
-       /* Check if all went well. */
-       if (se->type == SOCKENT_TYPE_SERVER)
-       {
-               if (se->data.server.fd_num <= 0)
-                       return (-1);
-       }
-       else /* if (se->type == SOCKENT_TYPE_CLIENT) */
-       {
-               if (se->data.client.fd < 0)
-                       return (-1);
-       }
-
+       if (se->data.server.fd_num <= 0)
+               return (-1);
        return (0);
-} /* }}} int sockent_open */
+} /* }}} int sockent_server_listen */
 
 /* Add a sockent to the global list of sockets */
 static int sockent_add (sockent_t *se) /* {{{ */
@@ -2466,26 +2553,32 @@ static void network_init_buffer (void)
        memset (&send_buffer_vl, 0, sizeof (send_buffer_vl));
 } /* int network_init_buffer */
 
-static void networt_send_buffer_plain (const sockent_t *se, /* {{{ */
+static void networt_send_buffer_plain (sockent_t *se, /* {{{ */
                const char *buffer, size_t buffer_size)
 {
        int status;
 
        while (42)
        {
+               status = sockent_client_connect (se);
+               if (status != 0)
+                       return;
+
                status = sendto (se->data.client.fd, buffer, buffer_size,
-                    /* flags = */ 0,
-                    (struct sockaddr *) se->data.client.addr,
-                    se->data.client.addrlen);
-                if (status < 0)
+                               /* flags = */ 0,
+                               (struct sockaddr *) se->data.client.addr,
+                               se->data.client.addrlen);
+               if (status < 0)
                {
                        char errbuf[1024];
-                       if (errno == EINTR)
+
+                       if ((errno == EINTR) || (errno == EAGAIN))
                                continue;
-                       ERROR ("network plugin: sendto failed: %s",
-                                       sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-                       break;
+
+                       ERROR ("network plugin: sendto failed: %s. Closing sending socket.",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       sockent_client_disconnect (se);
+                       return;
                }
 
                break;
@@ -2498,7 +2591,7 @@ static void networt_send_buffer_plain (const sockent_t *se, /* {{{ */
   buffer_offset += (s); \
 } while (0)
 
-static void networt_send_buffer_signed (const sockent_t *se, /* {{{ */
+static void networt_send_buffer_signed (sockent_t *se, /* {{{ */
                const char *in_buffer, size_t in_buffer_size)
 {
   part_signature_sha256_t ps;
@@ -2692,7 +2785,7 @@ static int add_to_buffer (char *buffer, int buffer_size, /* {{{ */
 
        if (vl_def->time != vl->time)
        {
-               if (write_part_number (&buffer, &buffer_size, TYPE_TIME,
+               if (write_part_number (&buffer, &buffer_size, TYPE_TIME_HR,
                                        (uint64_t) vl->time))
                        return (-1);
                vl_def->time = vl->time;
@@ -2700,7 +2793,7 @@ static int add_to_buffer (char *buffer, int buffer_size, /* {{{ */
 
        if (vl_def->interval != vl->interval)
        {
-               if (write_part_number (&buffer, &buffer_size, TYPE_INTERVAL,
+               if (write_part_number (&buffer, &buffer_size, TYPE_INTERVAL_HR,
                                        (uint64_t) vl->interval))
                        return (-1);
                vl_def->interval = vl->interval;
@@ -2994,13 +3087,12 @@ static int network_config_add_listen (const oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
-  se = malloc (sizeof (*se));
+  se = sockent_create (SOCKENT_TYPE_SERVER);
   if (se == NULL)
   {
-    ERROR ("network plugin: malloc failed.");
+    ERROR ("network plugin: sockent_create failed.");
     return (-1);
   }
-  sockent_init (se, SOCKENT_TYPE_SERVER);
 
   se->node = strdup (ci->values[0].value.string);
   if (ci->values_num >= 2)
@@ -3040,10 +3132,18 @@ static int network_config_add_listen (const oconfig_item_t *ci) /* {{{ */
   }
 #endif /* HAVE_LIBGCRYPT */
 
-  status = sockent_open (se);
+  status = sockent_init_crypto (se);
   if (status != 0)
   {
-    ERROR ("network plugin: network_config_add_listen: sockent_open failed.");
+    ERROR ("network plugin: network_config_add_listen: sockent_init_crypto() failed.");
+    sockent_destroy (se);
+    return (-1);
+  }
+
+  status = sockent_server_listen (se);
+  if (status != 0)
+  {
+    ERROR ("network plugin: network_config_add_server: sockent_server_listen failed.");
     sockent_destroy (se);
     return (-1);
   }
@@ -3074,13 +3174,12 @@ static int network_config_add_server (const oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
-  se = malloc (sizeof (*se));
+  se = sockent_create (SOCKENT_TYPE_CLIENT);
   if (se == NULL)
   {
-    ERROR ("network plugin: malloc failed.");
+    ERROR ("network plugin: sockent_create failed.");
     return (-1);
   }
-  sockent_init (se, SOCKENT_TYPE_CLIENT);
 
   se->node = strdup (ci->values[0].value.string);
   if (ci->values_num >= 2)
@@ -3123,14 +3222,17 @@ static int network_config_add_server (const oconfig_item_t *ci) /* {{{ */
   }
 #endif /* HAVE_LIBGCRYPT */
 
-  status = sockent_open (se);
+  status = sockent_init_crypto (se);
   if (status != 0)
   {
-    ERROR ("network plugin: network_config_add_server: sockent_open failed.");
+    ERROR ("network plugin: network_config_add_server: sockent_init_crypto() failed.");
     sockent_destroy (se);
     return (-1);
   }
 
+  /* No call to sockent_client_connect() here -- it is called from
+   * networt_send_buffer_plain(). */
+
   status = sockent_add (se);
   if (status != 0)
   {
@@ -3162,8 +3264,6 @@ static int network_config (oconfig_item_t *ci) /* {{{ */
       network_config_set_boolean (child, &network_config_forward);
     else if (strcasecmp ("ReportStats", child->key) == 0)
       network_config_set_boolean (child, &network_config_stats);
-    else if (strcasecmp ("CacheFlush", child->key) == 0)
-      /* no op for backwards compatibility only */;
     else
     {
       WARNING ("network plugin: Option `%s' is not allowed here.",
@@ -3187,7 +3287,7 @@ static int network_notification (const notification_t *n,
 
   memset (buffer, 0, sizeof (buffer));
 
-  status = write_part_number (&buffer_ptr, &buffer_free, TYPE_TIME,
+  status = write_part_number (&buffer_ptr, &buffer_free, TYPE_TIME_HR,
       (uint64_t) n->time);
   if (status != 0)
     return (-1);
@@ -3250,6 +3350,8 @@ static int network_notification (const notification_t *n,
 
 static int network_shutdown (void)
 {
+       sockent_t *se;
+
        listen_loop++;
 
        /* Kill the listening thread */
@@ -3280,7 +3382,9 @@ static int network_shutdown (void)
 
        sfree (send_buffer);
 
-       /* TODO: Close `sending_sockets' */
+       for (se = sending_sockets; se != NULL; se = se->next)
+               sockent_client_disconnect (se);
+       sockent_destroy (sending_sockets);
 
        plugin_unregister_config ("network");
        plugin_unregister_init ("network");
@@ -3292,15 +3396,15 @@ static int network_shutdown (void)
 
 static int network_stats_read (void) /* {{{ */
 {
-       uint64_t copy_octets_rx;
-       uint64_t copy_octets_tx;
-       uint64_t copy_packets_rx;
-       uint64_t copy_packets_tx;
-       uint64_t copy_values_dispatched;
-       uint64_t copy_values_not_dispatched;
-       uint64_t copy_values_sent;
-       uint64_t copy_values_not_sent;
-       uint64_t copy_receive_list_length;
+       derive_t copy_octets_rx;
+       derive_t copy_octets_tx;
+       derive_t copy_packets_rx;
+       derive_t copy_packets_tx;
+       derive_t copy_values_dispatched;
+       derive_t copy_values_not_dispatched;
+       derive_t copy_values_sent;
+       derive_t copy_values_not_sent;
+       derive_t copy_receive_list_length;
        value_list_t vl = VALUE_LIST_INIT;
        value_t values[2];
 
@@ -3318,21 +3422,20 @@ static int network_stats_read (void) /* {{{ */
        vl.values = values;
        vl.values_len = 2;
        vl.time = 0;
-       vl.interval = interval_g;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "network", sizeof (vl.plugin));
 
        /* Octets received / sent */
-       vl.values[0].counter = (counter_t) copy_octets_rx;
-       vl.values[1].counter = (counter_t) copy_octets_tx;
+       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].counter = (counter_t) copy_packets_rx;
-       vl.values[1].counter = (counter_t) copy_packets_tx;
+       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));
@@ -3341,41 +3444,41 @@ 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 */
 
 static int network_init (void)
 {
-       static _Bool have_init = false;
+       static _Bool have_init = 0;
 
        /* Check if we were already initialized. If so, just return - there's
         * nothing more to do (for now, that is). */
        if (have_init)
                return (0);
-       have_init = true;
+       have_init = 1;
 
 #if HAVE_LIBGCRYPT
        network_init_gcrypt ();
@@ -3412,7 +3515,7 @@ static int network_init (void)
        if (dispatch_thread_running == 0)
        {
                int status;
-               status = pthread_create (&dispatch_thread_id,
+               status = plugin_thread_create (&dispatch_thread_id,
                                NULL /* no attributes */,
                                dispatch_thread,
                                NULL /* no argument */);
@@ -3432,7 +3535,7 @@ static int network_init (void)
        if (receive_thread_running == 0)
        {
                int status;
-               status = pthread_create (&receive_thread_id,
+               status = plugin_thread_create (&receive_thread_id,
                                NULL /* no attributes */,
                                receive_thread,
                                NULL /* no argument */);
@@ -3459,9 +3562,9 @@ static int network_init (void)
  * just send the buffer if `flush'  is called - if the requested value was in
  * there, good. If not, well, then there is nothing to flush.. -octo
  */
-static int network_flush (int timeout,
-               const char __attribute__((unused)) *identifier,
-               user_data_t __attribute__((unused)) *user_data)
+static int network_flush (__attribute__((unused)) cdtime_t timeout,
+               __attribute__((unused)) const char *identifier,
+               __attribute__((unused)) user_data_t *user_data)
 {
        pthread_mutex_lock (&send_buffer_lock);
 
index 777616c..1b35456 100644 (file)
 
 #define TYPE_HOST            0x0000
 #define TYPE_TIME            0x0001
+#define TYPE_TIME_HR         0x0008
 #define TYPE_PLUGIN          0x0002
 #define TYPE_PLUGIN_INSTANCE 0x0003
 #define TYPE_TYPE            0x0004
 #define TYPE_TYPE_INSTANCE   0x0005
 #define TYPE_VALUES          0x0006
 #define TYPE_INTERVAL        0x0007
+#define TYPE_INTERVAL_HR     0x0009
 
 /* Types to transmit notifications */
 #define TYPE_MESSAGE         0x0100
index e4d12e3..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
  *
  * 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>
  **/
 
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 
-#if !KERNEL_LINUX
-# error "No applicable input method."
+#if HAVE_KSTAT_H
+#include <kstat.h>
 #endif
 
 /*
@@ -92,10 +94,9 @@ static const char *nfs2_procedures_names[] =
        "mkdir",
        "rmdir",
        "readdir",
-       "fsstat",
-       NULL
+       "fsstat"
 };
-static int nfs2_procedures_names_num = 18;
+static size_t nfs2_procedures_names_num = STATIC_ARRAY_SIZE (nfs2_procedures_names);
 
 static const char *nfs3_procedures_names[] =
 {
@@ -120,12 +121,57 @@ static const char *nfs3_procedures_names[] =
        "fsstat",
        "fsinfo",
        "pathconf",
+       "commit"
+};
+static size_t nfs3_procedures_names_num = STATIC_ARRAY_SIZE (nfs3_procedures_names);
+
+#if HAVE_LIBKSTAT
+static const char *nfs4_procedures_names[] =
+{
+       "null",
+       "compound",
+       "reserved",
+       "access",
+       "close",
        "commit",
-       NULL
+       "create",
+       "delegpurge",
+       "delegreturn",
+       "getattr",
+       "getfh",
+       "link",
+       "lock",
+       "lockt",
+       "locku",
+       "lookup",
+       "lookupp",
+       "nverify",
+       "open",
+       "openattr",
+       "open_confirm",
+       "open_downgrade",
+       "putfh",
+       "putpubfh",
+       "putrootfh",
+       "read",
+       "readdir",
+       "readlink",
+       "remove",
+       "rename",
+       "renew",
+       "restorefh",
+       "savefh",
+       "secinfo",
+       "setattr",
+       "setclientid",
+       "setclientid_confirm",
+       "verify",
+       "write"
 };
-static int nfs3_procedures_names_num = 22;
+static size_t nfs4_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_procedures_names);
+#endif
 
-#if HAVE_LIBKSTAT && 0
+#if HAVE_LIBKSTAT
 extern kstat_ctl_t *kc;
 static kstat_t *nfs2_ksp_client;
 static kstat_t *nfs2_ksp_server;
@@ -137,11 +183,17 @@ static kstat_t *nfs4_ksp_server;
 
 /* Possibly TODO: NFSv4 statistics */
 
-#if 0
+#if KERNEL_LINUX
 static int nfs_init (void)
 {
-#if HAVE_LIBKSTAT && 0
-       kstat_t *ksp_chain;
+       return (0);
+}
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKSTAT
+static int nfs_init (void)
+{
+       kstat_t *ksp_chain = NULL;
 
        nfs2_ksp_client = NULL;
        nfs2_ksp_server = NULL;
@@ -149,9 +201,9 @@ static int nfs_init (void)
        nfs3_ksp_server = NULL;
        nfs4_ksp_client = NULL;
        nfs4_ksp_server = NULL;
-       
+
        if (kc == NULL)
-               return;
+               return (-1);
 
        for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
                        ksp_chain = ksp_chain->ks_next)
@@ -171,192 +223,174 @@ static int nfs_init (void)
                else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
                        nfs4_ksp_client = ksp_chain;
        }
-#endif
 
        return (0);
 } /* int nfs_init */
 #endif
 
-#define BUFSIZE 1024
 static void nfs_procedures_submit (const char *plugin_instance,
-               unsigned long long *val, const char **names, int len)
+               const char **type_instances,
+               value_t *values, size_t values_num)
 {
-       value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
-       int i;
+       size_t i;
 
-       vl.values = values;
        vl.values_len = 1;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "nfs", sizeof (vl.plugin));
        sstrncpy (vl.plugin_instance, plugin_instance,
-                       sizeof (vl.plugin_instance));
+                       sizeof (vl.plugin_instance));
        sstrncpy (vl.type, "nfs_procedure", sizeof (vl.type));
 
-       for (i = 0; i < len; i++)
+       for (i = 0; i < values_num; i++)
        {
-               values[0].counter = val[i];
-               sstrncpy (vl.type_instance, names[i],
+               vl.values = values + i;
+               sstrncpy (vl.type_instance, type_instances[i],
                                sizeof (vl.type_instance));
-               DEBUG ("%s-%s/nfs_procedure-%s = %llu",
-                               vl.plugin, vl.plugin_instance,
-                               vl.type_instance, val[i]);
                plugin_dispatch_values (&vl);
        }
 } /* void nfs_procedures_submit */
 
-static void nfs_read_stats_file (FILE *fh, char *inst)
+#if KERNEL_LINUX
+static int nfs_submit_fields (int nfs_version, const char *instance,
+               char **fields, size_t fields_num,
+               const char **proc_names, size_t proc_names_num)
 {
-       char buffer[BUFSIZE];
-
        char plugin_instance[DATA_MAX_NAME_LEN];
+       value_t values[fields_num];
+       size_t i;
+
+       if (fields_num != proc_names_num)
+       {
+               WARNING ("nfs plugin: Wrong number of fields for "
+                               "NFSv%i %s statistics. Expected %zu, got %zu.",
+                               nfs_version, instance,
+                               proc_names_num, fields_num);
+               return (EINVAL);
+       }
+
+       ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
+                       nfs_version, instance);
+
+       for (i = 0; i < proc_names_num; i++)
+               (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
+
+       nfs_procedures_submit (plugin_instance, proc_names, values,
+                       proc_names_num);
+
+       return (0);
+}
+
+static void nfs_read_linux (FILE *fh, char *inst)
+{
+       char buffer[1024];
 
        char *fields[48];
-       int numfields = 0;
+       int fields_num = 0;
 
        if (fh == NULL)
                return;
 
-       while (fgets (buffer, BUFSIZE, fh) != NULL)
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
-               numfields = strsplit (buffer, fields, 48);
+               fields_num = strsplit (buffer,
+                               fields, STATIC_ARRAY_SIZE (fields));
 
-               if (((numfields - 2) != nfs2_procedures_names_num)
-                               && ((numfields - 2)
-                                       != nfs3_procedures_names_num))
+               if (fields_num < 3)
                        continue;
 
                if (strcmp (fields[0], "proc2") == 0)
                {
-                       int i;
-                       unsigned long long *values;
-
-                       if ((numfields - 2) != nfs2_procedures_names_num)
-                       {
-                               WARNING ("nfs plugin: Wrong "
-                                               "number of fields (= %i) "
-                                               "for NFSv2 statistics.",
-                                               numfields - 2);
-                               continue;
-                       }
-
-                       ssnprintf (plugin_instance, sizeof (plugin_instance),
-                                       "v2%s", inst);
-
-                       values = (unsigned long long *) malloc (nfs2_procedures_names_num * sizeof (unsigned long long));
-                       if (values == NULL)
-                       {
-                               char errbuf[1024];
-                               ERROR ("nfs plugin: malloc "
-                                               "failed: %s",
-                                               sstrerror (errno, errbuf, sizeof (errbuf)));
-                               continue;
-                       }
-
-                       for (i = 0; i < nfs2_procedures_names_num; i++)
-                               values[i] = atoll (fields[i + 2]);
-
-                       nfs_procedures_submit (plugin_instance, values,
+                       nfs_submit_fields (/* version = */ 2, inst,
+                                       fields + 2, (size_t) (fields_num - 2),
                                        nfs2_procedures_names,
                                        nfs2_procedures_names_num);
-
-                       free (values);
                }
                else if (strncmp (fields[0], "proc3", 5) == 0)
                {
-                       int i;
-                       unsigned long long *values;
-
-                       if ((numfields - 2) != nfs3_procedures_names_num)
-                       {
-                               WARNING ("nfs plugin: Wrong "
-                                               "number of fields (= %i) "
-                                               "for NFSv3 statistics.",
-                                               numfields - 2);
-                               continue;
-                       }
-
-                       ssnprintf (plugin_instance, sizeof (plugin_instance),
-                                       "v3%s", inst);
-
-                       values = (unsigned long long *) malloc (nfs3_procedures_names_num * sizeof (unsigned long long));
-                       if (values == NULL)
-                       {
-                               char errbuf[1024];
-                               ERROR ("nfs plugin: malloc "
-                                               "failed: %s",
-                                               sstrerror (errno, errbuf, sizeof (errbuf)));
-                               continue;
-                       }
-
-                       for (i = 0; i < nfs3_procedures_names_num; i++)
-                               values[i] = atoll (fields[i + 2]);
-
-                       nfs_procedures_submit (plugin_instance, values,
+                       nfs_submit_fields (/* version = */ 3, inst,
+                                       fields + 2, (size_t) (fields_num - 2),
                                        nfs3_procedures_names,
                                        nfs3_procedures_names_num);
-
-                       free (values);
                }
-       } /* while (fgets (buffer, BUFSIZE, fh) != NULL) */
-} /* void nfs_read_stats_file */
-#undef BUFSIZE
+       } /* while (fgets) */
+} /* void nfs_read_linux */
+#endif /* KERNEL_LINUX */
 
-#if HAVE_LIBKSTAT && 0
-static void nfs2_read_kstat (kstat_t *ksp, char *inst)
+#if HAVE_LIBKSTAT
+static int nfs_read_kstat (kstat_t *ksp, int nfs_version, char *inst,
+               char const **proc_names, size_t proc_names_num)
 {
-       unsigned long long values[18];
-
-       values[0] = get_kstat_value (ksp, "null");
-       values[1] = get_kstat_value (ksp, "getattr");
-       values[2] = get_kstat_value (ksp, "setattr");
-       values[3] = get_kstat_value (ksp, "root");
-       values[4] = get_kstat_value (ksp, "lookup");
-       values[5] = get_kstat_value (ksp, "readlink");
-       values[6] = get_kstat_value (ksp, "read");
-       values[7] = get_kstat_value (ksp, "wrcache");
-       values[8] = get_kstat_value (ksp, "write");
-       values[9] = get_kstat_value (ksp, "create");
-       values[10] = get_kstat_value (ksp, "remove");
-       values[11] = get_kstat_value (ksp, "rename");
-       values[12] = get_kstat_value (ksp, "link");
-       values[13] = get_kstat_value (ksp, "symlink");
-       values[14] = get_kstat_value (ksp, "mkdir");
-       values[15] = get_kstat_value (ksp, "rmdir");
-       values[16] = get_kstat_value (ksp, "readdir");
-       values[17] = get_kstat_value (ksp, "statfs");
-
-       nfs2_procedures_submit (values, inst);
+       char plugin_instance[DATA_MAX_NAME_LEN];
+       value_t values[proc_names_num];
+       size_t i;
+
+       if (ksp == NULL)
+               return (EINVAL);
+
+       ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
+                       nfs_version, inst);
+
+       kstat_read(kc, ksp, NULL);
+       for (i = 0; i < proc_names_num; i++)
+       {
+               /* 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);
+       return (0);
 }
 #endif
 
+#if KERNEL_LINUX
 static int nfs_read (void)
 {
        FILE *fh;
 
        if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
        {
-               nfs_read_stats_file (fh, "client");
+               nfs_read_linux (fh, "client");
                fclose (fh);
        }
 
        if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
        {
-               nfs_read_stats_file (fh, "server");
+               nfs_read_linux (fh, "server");
                fclose (fh);
        }
 
-#if HAVE_LIBKSTAT && 0
-       if (nfs2_ksp_client != NULL)
-               nfs2_read_kstat (nfs2_ksp_client, "client");
-       if (nfs2_ksp_server != NULL)
-               nfs2_read_kstat (nfs2_ksp_server, "server");
-#endif /* defined(HAVE_LIBKSTAT) */
+       return (0);
+}
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKSTAT
+static int nfs_read (void)
+{
+       nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
+                       nfs2_procedures_names, nfs2_procedures_names_num);
+       nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
+                       nfs2_procedures_names, nfs2_procedures_names_num);
+       nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
+                       nfs3_procedures_names, nfs3_procedures_names_num);
+       nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
+                       nfs3_procedures_names, nfs3_procedures_names_num);
+       nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
+                       nfs4_procedures_names, nfs4_procedures_names_num);
+       nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
+                       nfs4_procedures_names, nfs4_procedures_names_num);
 
        return (0);
 }
+#endif /* HAVE_LIBKSTAT */
 
 void module_register (void)
 {
+       plugin_register_init ("nfs", nfs_init);
        plugin_register_read ("nfs", nfs_read);
 } /* void module_register */
index a0fa74f..7568a2c 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/nginx.c
- * Copyright (C) 2006,2007  Florian octo Forster
+ * Copyright (C) 2006-2010  Florian octo Forster
  * Copyright (C) 2008       Sebastian Harl
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -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>
  **/
 
@@ -38,10 +38,9 @@ static char *cacert      = NULL;
 
 static CURL *curl = NULL;
 
-#define ABUFFER_SIZE 16384
-static char nginx_buffer[ABUFFER_SIZE];
-static int  nginx_buffer_len = 0;
-static char nginx_curl_error[CURL_ERROR_SIZE];
+static char   nginx_buffer[16384];
+static size_t nginx_buffer_len = 0;
+static char   nginx_curl_error[CURL_ERROR_SIZE];
 
 static const char *config_keys[] =
 {
@@ -59,17 +58,19 @@ static size_t nginx_curl_callback (void *buf, size_t size, size_t nmemb,
 {
   size_t len = size * nmemb;
 
-  if ((nginx_buffer_len + len) >= ABUFFER_SIZE)
+  /* Check if the data fits into the memory. If not, truncate it. */
+  if ((nginx_buffer_len + len) >= sizeof (nginx_buffer))
   {
-    len = (ABUFFER_SIZE - 1) - nginx_buffer_len;
+    assert (sizeof (nginx_buffer) > nginx_buffer_len);
+    len = (sizeof (nginx_buffer) - 1) - nginx_buffer_len;
   }
 
   if (len <= 0)
     return (len);
 
-  memcpy (nginx_buffer + nginx_buffer_len, (char *) buf, len);
+  memcpy (&nginx_buffer[nginx_buffer_len], buf, len);
   nginx_buffer_len += len;
-  nginx_buffer[nginx_buffer_len] = '\0';
+  nginx_buffer[nginx_buffer_len] = 0;
 
   return (len);
 }
@@ -179,7 +180,7 @@ static void submit (char *type, char *inst, long long value)
   if (strcmp (type, "nginx_connections") == 0)
     values[0].gauge = value;
   else if (strcmp (type, "nginx_requests") == 0)
-    values[0].counter = value;
+    values[0].derive = value;
   else
     return;
 
index 6b4678a..24c66ec 100644 (file)
@@ -229,6 +229,7 @@ static int notify_email_notification (const notification_t *n,
     user_data_t __attribute__((unused)) *user_data)
 {
 
+  time_t tt;
   struct tm timestamp_tm;
   char timestamp_str[64];
 
@@ -248,7 +249,8 @@ static int notify_email_notification (const notification_t *n,
       (email_subject == NULL) ? DEFAULT_SMTP_SUBJECT : email_subject,
       severity, n->host);
 
-  localtime_r (&n->time, &timestamp_tm);
+  tt = CDTIME_T_TO_TIME_T (n->time);
+  localtime_r (&tt, &timestamp_tm);
   strftime (timestamp_str, sizeof (timestamp_str), "%Y-%m-%d %H:%M:%S",
       &timestamp_tm);
   timestamp_str[sizeof (timestamp_str) - 1] = '\0';
@@ -256,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 8bbf74d..bbc455f 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/ntpd.c
- * Copyright (C) 2006-2007  Florian octo Forster
+ * Copyright (C) 2006-2012  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -16,7 +16,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #define _BSD_SOURCE /* For NI_MAXHOST */
@@ -52,11 +52,17 @@ static const char *config_keys[] =
 {
        "Host",
        "Port",
-       "ReverseLookups"
+       "ReverseLookups",
+       "IncludeUnitID"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-static int do_reverse_lookups = 1;
+static _Bool do_reverse_lookups = 1;
+
+/* This option only exists for backward compatibility. If it is false and two
+ * ntpd peers use the same refclock driver, the plugin will try to write
+ * simultaneous measurements from both to the same type instance. */
+static _Bool include_unit_id = 0;
 
 # define NTPD_DEFAULT_HOST "localhost"
 # define NTPD_DEFAULT_PORT "123"
@@ -283,6 +289,13 @@ static int ntpd_config (const char *key, const char *value)
                else
                        do_reverse_lookups = 0;
        }
+       else if (strcasecmp (key, "IncludeUnitID") == 0)
+       {
+               if (IS_TRUE (value))
+                       include_unit_id = 1;
+               else
+                       include_unit_id = 0;
+       }
        else
        {
                return (-1);
@@ -309,6 +322,18 @@ static void ntpd_submit (char *type, char *type_inst, double value)
        plugin_dispatch_values (&vl);
 }
 
+/* Each time a peer is polled, ntpd shifts the reach register to the left and
+ * sets the LSB based on whether the peer was reachable. If the LSB is zero,
+ * the values are out of date. */
+static void ntpd_submit_reach (char *type, char *type_inst, uint8_t reach,
+               double value)
+{
+       if (!(reach & 1))
+               value = NAN;
+
+       ntpd_submit (type, type_inst, value);
+}
+
 static int ntpd_connect (void)
 {
        char *host;
@@ -763,6 +788,108 @@ static double ntpd_read_fp (int32_t val_int)
        return (val_double);
 }
 
+static uint32_t ntpd_get_refclock_id (struct info_peer_summary const *peer_info)
+{
+       uint32_t addr = ntohl (peer_info->srcadr);
+       uint32_t refclock_id = (addr >> 8) & 0x00FF;
+
+       return (refclock_id);
+}
+
+static int ntpd_get_name_from_address (char *buffer, size_t buffer_size,
+               struct info_peer_summary const *peer_info, _Bool do_reverse_lookup)
+{
+       struct sockaddr_storage sa;
+       socklen_t sa_len;
+       int flags = 0;
+       int status;
+
+       memset (&sa, 0, sizeof (sa));
+
+       if (peer_info->v6_flag)
+       {
+               struct sockaddr_in6 sa6;
+
+               assert (sizeof (sa) >= sizeof (sa6));
+
+               memset (&sa6, 0, sizeof (sa6));
+               sa6.sin6_family = AF_INET6;
+               sa6.sin6_port = htons (123);
+               memcpy (&sa6.sin6_addr, &peer_info->srcadr6,
+                               sizeof (struct in6_addr));
+               sa_len = sizeof (sa6);
+
+               memcpy (&sa, &sa6, sizeof (sa6));
+       }
+       else
+       {
+               struct sockaddr_in sa4;
+
+               assert (sizeof (sa) >= sizeof (sa4));
+
+               memset (&sa4, 0, sizeof (sa4));
+               sa4.sin_family = AF_INET;
+               sa4.sin_port = htons (123);
+               memcpy (&sa4.sin_addr, &peer_info->srcadr,
+                               sizeof (struct in_addr));
+               sa_len = sizeof (sa4);
+
+               memcpy (&sa, &sa4, sizeof (sa4));
+       }
+
+       if (!do_reverse_lookup)
+               flags |= NI_NUMERICHOST;
+
+       status = getnameinfo ((struct sockaddr const *) &sa, sa_len,
+                       buffer, buffer_size,
+                       NULL, 0, /* No port name */
+                       flags);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("ntpd plugin: getnameinfo failed: %s",
+                               (status == EAI_SYSTEM)
+                               ? sstrerror (errno, errbuf, sizeof (errbuf))
+                               : gai_strerror (status));
+               return (-1);
+       }
+
+       return (0);
+} /* ntpd_get_name_from_address */
+
+static int ntpd_get_name_refclock (char *buffer, size_t buffer_size,
+               struct info_peer_summary const *peer_info)
+{
+       uint32_t refclock_id = ntpd_get_refclock_id (peer_info);
+       uint32_t unit_id = ntohl (peer_info->srcadr) & 0x00FF;
+
+       if (refclock_id >= refclock_names_num)
+               return (ntpd_get_name_from_address (buffer, buffer_size,
+                                       peer_info,
+                                       /* do_reverse_lookup = */ 0));
+
+       if (include_unit_id)
+               ssnprintf (buffer, buffer_size, "%s-%"PRIu32,
+                               refclock_names[refclock_id], unit_id);
+       else
+               sstrncpy (buffer, refclock_names[refclock_id], buffer_size);
+
+       return (0);
+} /* int ntpd_get_name_refclock */
+
+static int ntpd_get_name (char *buffer, size_t buffer_size,
+               struct info_peer_summary const *peer_info)
+{
+       uint32_t addr = ntohl (peer_info->srcadr);
+
+       if (!peer_info->v6_flag && ((addr & REFCLOCK_MASK) == REFCLOCK_ADDR))
+               return (ntpd_get_name_refclock (buffer, buffer_size,
+                                       peer_info));
+       else
+               return (ntpd_get_name_from_address (buffer, buffer_size,
+                                       peer_info, do_reverse_lookups));
+} /* int ntpd_addr_to_name */
+
 static int ntpd_read (void)
 {
        struct info_kernel *ik;
@@ -837,99 +964,26 @@ static int ntpd_read (void)
                double offset;
 
                char peername[NI_MAXHOST];
-               int refclock_id;
-               
-               ptr = ps + i;
-               refclock_id = 0;
+               uint32_t refclock_id;
 
-               /* Convert the `long floating point' offset value to double */
-               M_LFPTOD (ntohl (ptr->offset_int), ntohl (ptr->offset_frc), offset);
+               ptr = ps + i;
 
-               /* Special IP addresses for hardware clocks and stuff.. */
-               if (!ptr->v6_flag
-                               && ((ntohl (ptr->srcadr) & REFCLOCK_MASK)
-                                       == REFCLOCK_ADDR))
+               status = ntpd_get_name (peername, sizeof (peername), ptr);
+               if (status != 0)
                {
-                       struct in_addr  addr_obj;
-                       char *addr_str;
-
-                       refclock_id = (ntohl (ptr->srcadr) >> 8) & 0x000000FF;
-
-                       if (refclock_id < refclock_names_num)
-                       {
-                               sstrncpy (peername, refclock_names[refclock_id],
-                                               sizeof (peername));
-                       }
-                       else
-                       {
-                               memset ((void *) &addr_obj, '\0', sizeof (addr_obj));
-                               addr_obj.s_addr = ptr->srcadr;
-                               addr_str = inet_ntoa (addr_obj);
-
-                               sstrncpy (peername, addr_str, sizeof (peername));
-                       }
+                       ERROR ("ntpd plugin: Determining name of peer failed.");
+                       continue;
                }
-               else /* Normal network host. */
-               {
-                       struct sockaddr_storage sa;
-                       socklen_t sa_len;
-                       int flags = 0;
-
-                       memset (&sa, '\0', sizeof (sa));
-
-                       if (ptr->v6_flag)
-                       {
-                               struct sockaddr_in6 sa6;
-
-                               assert (sizeof (sa) >= sizeof (sa6));
-
-                               memset (&sa6, 0, sizeof (sa6));
-                               sa6.sin6_family = AF_INET6;
-                               sa6.sin6_port = htons (123);
-                               memcpy (&sa6.sin6_addr, &ptr->srcadr6,
-                                               sizeof (struct in6_addr));
-                               sa_len = sizeof (sa6);
-
-                               memcpy (&sa, &sa6, sizeof (sa6));
-                       }
-                       else
-                       {
-                               struct sockaddr_in sa4;
-
-                               assert (sizeof (sa) >= sizeof (sa4));
 
-                               memset (&sa4, 0, sizeof (sa4));
-                               sa4.sin_family = AF_INET;
-                               sa4.sin_port = htons (123);
-                               memcpy (&sa4.sin_addr, &ptr->srcadr,
-                                               sizeof (struct in_addr));
-                               sa_len = sizeof (sa4);
+               refclock_id = ntpd_get_refclock_id (ptr);
 
-                               memcpy (&sa, &sa4, sizeof (sa4));
-                       }
-
-                       if (do_reverse_lookups == 0)
-                               flags |= NI_NUMERICHOST;
-
-                       status = getnameinfo ((const struct sockaddr *) &sa,
-                                       sa_len,
-                                       peername, sizeof (peername),
-                                       NULL, 0, /* No port name */
-                                       flags);
-                       if (status != 0)
-                       {
-                               char errbuf[1024];
-                               ERROR ("ntpd plugin: getnameinfo failed: %s",
-                                               (status == EAI_SYSTEM)
-                                               ? sstrerror (errno, errbuf, sizeof (errbuf))
-                                               : gai_strerror (status));
-                               continue;
-                       }
-               }
+               /* Convert the `long floating point' offset value to double */
+               M_LFPTOD (ntohl (ptr->offset_int), ntohl (ptr->offset_frc), offset);
 
                DEBUG ("peer %i:\n"
                                "  peername   = %s\n"
                                "  srcadr     = 0x%08x\n"
+                               "  reach      = 0%03o\n"
                                "  delay      = %f\n"
                                "  offset_int = %i\n"
                                "  offset_frc = %i\n"
@@ -938,6 +992,7 @@ static int ntpd_read (void)
                                i,
                                peername,
                                ntohl (ptr->srcadr),
+                               ptr->reach,
                                ntpd_read_fp (ptr->delay),
                                ntohl (ptr->offset_int),
                                ntohl (ptr->offset_frc),
@@ -945,10 +1000,13 @@ static int ntpd_read (void)
                                ntpd_read_fp (ptr->dispersion));
 
                if (refclock_id != 1) /* not the system clock (offset will always be zero.. */
-                       ntpd_submit ("time_offset", peername, offset);
-               ntpd_submit ("time_dispersion", peername, ntpd_read_fp (ptr->dispersion));
+                       ntpd_submit_reach ("time_offset", peername, ptr->reach,
+                                       offset);
+               ntpd_submit_reach ("time_dispersion", peername, ptr->reach,
+                               ntpd_read_fp (ptr->dispersion));
                if (refclock_id == 0) /* not a reference clock */
-                       ntpd_submit ("delay", peername, ntpd_read_fp (ptr->delay));
+                       ntpd_submit_reach ("delay", peername, ptr->reach,
+                                       ntpd_read_fp (ptr->delay));
        }
 
        free (ps);
diff --git a/src/numa.c b/src/numa.c
new file mode 100644 (file)
index 0000000..4b4ef20
--- /dev/null
@@ -0,0 +1,168 @@
+/**
+ * collectd - src/numa.c
+ * Copyright (C) 2012  Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if !KERNEL_LINUX
+# error "No applicable input method."
+#endif
+
+#ifndef NUMA_ROOT_DIR
+# define NUMA_ROOT_DIR "/sys/devices/system/node"
+#endif
+
+static int max_node = -1;
+
+static void numa_dispatch_value (int node, /* {{{ */
+    const char *type_instance, value_t v)
+{
+  value_list_t vl = VALUE_LIST_INIT;
+
+  vl.values = &v;
+  vl.values_len = 1;
+
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "numa", sizeof (vl.plugin));
+  ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), "node%i", node);
+  sstrncpy (vl.type, "vmpage_action", sizeof (vl.type));
+  sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+  plugin_dispatch_values (&vl);
+} /* }}} void numa_dispatch_value */
+
+static int numa_read_node (int node) /* {{{ */
+{
+  char path[PATH_MAX];
+  FILE *fh;
+  char buffer[128];
+  int status;
+  int success;
+
+  ssnprintf (path, sizeof (path), NUMA_ROOT_DIR "/node%i/numastat", node);
+
+  fh = fopen (path, "r");
+  if (fh == NULL)
+  {
+    char errbuf[1024];
+    ERROR ("numa plugin: Reading node %i failed: open(%s): %s",
+        node, path, sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  success = 0;
+  while (fgets (buffer, sizeof (buffer), fh) != NULL)
+  {
+    char *fields[4];
+    value_t v;
+
+    status = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+    if (status != 2)
+    {
+      WARNING ("numa plugin: Ignoring line with unexpected "
+          "number of fields (node %i).", node);
+      continue;
+    }
+
+    v.derive = 0;
+    status = parse_value (fields[1], &v, DS_TYPE_DERIVE);
+    if (status != 0)
+      continue;
+
+    numa_dispatch_value (node, fields[0], v);
+    success++;
+  }
+
+  fclose (fh);
+  return (success ? 0 : -1);
+} /* }}} int numa_read_node */
+
+static int numa_read (void) /* {{{ */
+{
+  int i;
+  int status;
+  int success;
+
+  if (max_node < 0)
+  {
+    WARNING ("numa plugin: No NUMA nodes were detected.");
+    return (-1);
+  }
+
+  success = 0;
+  for (i = 0; i <= max_node; i++)
+  {
+    status = numa_read_node (i);
+    if (status == 0)
+      success++;
+  }
+
+  return (success ? 0 : -1);
+} /* }}} int numa_read */
+
+static int numa_init (void) /* {{{ */
+{
+  /* Determine the number of nodes on this machine. */
+  while (42)
+  {
+    char path[PATH_MAX];
+    struct stat statbuf;
+    int status;
+
+    ssnprintf (path, sizeof (path), NUMA_ROOT_DIR "/node%i", max_node + 1);
+    memset (&statbuf, 0, sizeof (statbuf));
+
+    status = stat (path, &statbuf);
+    if (status == 0)
+    {
+      max_node++;
+      continue;
+    }
+    else if (errno == ENOENT)
+    {
+      break;
+    }
+    else /* ((status != 0) && (errno != ENOENT)) */
+    {
+      char errbuf[1024];
+      ERROR ("numa plugin: stat(%s) failed: %s", path,
+          sstrerror (errno, errbuf, sizeof (errbuf)));
+      return (-1);
+    }
+  }
+
+  DEBUG ("numa plugin: Found %i nodes.", max_node + 1);
+  return (0);
+} /* }}} int numa_init */
+
+void module_register (void)
+{
+  plugin_register_init ("numa", numa_init);
+  plugin_register_read ("numa", numa_read);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et : */
index 462458c..09a6bf0 100644 (file)
@@ -59,7 +59,7 @@ static ow_family_features_t ow_family_features[] =
 static int ow_family_features_num = STATIC_ARRAY_SIZE (ow_family_features);
 
 static char *device_g = NULL;
-static int   ow_interval = 0;
+static cdtime_t ow_interval = 0;
 
 static const char *config_keys[] =
 {
@@ -106,10 +106,10 @@ static int cow_load_config (const char *key, const char *value)
   }
   else if (strcasecmp ("Interval", key) == 0)
   {
-    int tmp;
-    tmp = atoi (value);
-    if (tmp > 0)
-      ow_interval = tmp;
+    double tmp;
+    tmp = atof (value);
+    if (tmp > 0.0)
+      ow_interval = DOUBLE_TO_CDTIME_T (tmp);
     else
       ERROR ("onewire plugin: Invalid `Interval' setting: %s", value);
   }
@@ -306,12 +306,11 @@ static int cow_init (void)
     return (1);
   }
 
-  memset (&cb_interval, 0, sizeof (cb_interval));
-  if (ow_interval > 0)
-    cb_interval.tv_sec = (time_t) ow_interval;
+  CDTIME_T_TO_TIMESPEC (ow_interval, &cb_interval);
 
   plugin_register_complex_read (/* group = */ NULL, "onewire", cow_read,
-      &cb_interval, /* user data = */ NULL);
+      (ow_interval != 0) ? &cb_interval : NULL,
+      /* user data = */ NULL);
   plugin_register_shutdown ("onewire", cow_shutdown);
 
   return (0);
index 2aca414..9ce23b4 100644 (file)
@@ -1,9 +1,9 @@
 /**
  * collectd - src/openvpn.c
- * Copyright (C) 2008  Doug MacEachern
- * Copyright (C) 2009  Florian octo Forster
- * Copyright (C) 2009  Marco Chiappero
- * Copyright (C) 2009  Fabian Schuh
+ * Copyright (C) 2008       Doug MacEachern
+ * Copyright (C) 2009,2010  Florian octo Forster
+ * Copyright (C) 2009       Marco Chiappero
+ * Copyright (C) 2009       Fabian Schuh
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -20,7 +20,7 @@
  *
  * Authors:
  *   Doug MacEachern <dougm at hyperic.com>
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Marco Chiappero <marco at absence.it>
  *   Fabian Schuh <mail at xeroc.org>
  **/
@@ -114,13 +114,13 @@ static void numusers_submit (char *pinst, char *tinst, gauge_t value)
 } /* void numusers_submit */
 
 /* dispatches stats about traffic (TCP or UDP) generated by the tunnel per single endpoint */
-static void iostats_submit (char *pinst, char *tinst, counter_t rx, counter_t tx)
+static void iostats_submit (char *pinst, char *tinst, derive_t rx, derive_t tx)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = rx;
-       values[1].counter = tx;
+       values[0].derive = rx;
+       values[1].derive = tx;
 
        /* NOTE ON THE NEW NAMING SCHEMA:
         *       using plugin_instance to identify each vpn config (and
@@ -144,13 +144,13 @@ static void iostats_submit (char *pinst, char *tinst, counter_t rx, counter_t tx
 
 /* dispatches stats about data compression shown when in single mode */
 static void compression_submit (char *pinst, char *tinst,
-               counter_t uncompressed, counter_t compressed)
+               derive_t uncompressed, derive_t compressed)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = uncompressed;
-       values[1].counter = compressed;
+       values[0].derive = uncompressed;
+       values[1].derive = compressed;
 
        vl.values = values;
        vl.values_len = STATIC_ARRAY_SIZE (values);
@@ -173,11 +173,11 @@ static int single_read (char *name, FILE *fh)
        const int max_fields = STATIC_ARRAY_SIZE (fields);
        int  fields_num, read = 0;
 
-       counter_t link_rx, link_tx;
-       counter_t tun_rx, tun_tx;
-       counter_t pre_compress, post_compress;
-       counter_t pre_decompress, post_decompress;
-       counter_t overhead_rx, overhead_tx;
+       derive_t link_rx, link_tx;
+       derive_t tun_rx, tun_tx;
+       derive_t pre_compress, post_compress;
+       derive_t pre_decompress, post_decompress;
+       derive_t overhead_rx, overhead_tx;
 
        link_rx = 0;
        link_tx = 0;
index 828fc2e..ab0812b 100644 (file)
@@ -59,6 +59,7 @@
 struct o_database_s
 {
   char *name;
+  char *host;
   char *connect_id;
   char *username;
   char *password;
@@ -183,33 +184,6 @@ static void o_database_free (o_database_t *db) /* {{{ */
  * </Plugin>
  */
 
-static int o_config_set_string (char **ret_string, /* {{{ */
-    oconfig_item_t *ci)
-{
-  char *string;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("oracle plugin: The `%s' config option "
-        "needs exactly one string argument.", ci->key);
-    return (-1);
-  }
-
-  string = strdup (ci->values[0].value.string);
-  if (string == NULL)
-  {
-    ERROR ("oracle plugin: strdup failed.");
-    return (-1);
-  }
-
-  if (*ret_string != NULL)
-    free (*ret_string);
-  *ret_string = string;
-
-  return (0);
-} /* }}} int o_config_set_string */
-
 static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
 {
   o_database_t *db;
@@ -231,8 +205,13 @@ static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
   memset (db, 0, sizeof (*db));
+  db->name = NULL;
+  db->host = NULL;
+  db->connect_id = NULL;
+  db->username = NULL;
+  db->password = NULL;
 
-  status = o_config_set_string (&db->name, ci);
+  status = cf_util_get_string (ci, &db->name);
   if (status != 0)
   {
     sfree (db);
@@ -245,11 +224,13 @@ static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp ("ConnectID", child->key) == 0)
-      status = o_config_set_string (&db->connect_id, child);
+      status = cf_util_get_string (child, &db->connect_id);
+    else if (strcasecmp ("Host", child->key) == 0)
+      status = cf_util_get_string (child, &db->host);
     else if (strcasecmp ("Username", child->key) == 0)
-      status = o_config_set_string (&db->username, child);
+      status = cf_util_get_string (child, &db->username);
     else if (strcasecmp ("Password", child->key) == 0)
-      status = o_config_set_string (&db->password, child);
+      status = cf_util_get_string (child, &db->password);
     else if (strcasecmp ("Query", child->key) == 0)
       status = udb_query_pick_from_list (child, queries, queries_num,
           &db->queries, &db->queries_num);
@@ -352,7 +333,7 @@ static int o_config (oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
     if (strcasecmp ("Query", child->key) == 0)
       udb_query_create (&queries, &queries_num, child,
-          /* callback = */ NULL, /* legacy mode = */ 0);
+          /* callback = */ NULL);
     else if (strcasecmp ("Database", child->key) == 0)
       o_config_add_database (child);
     else
@@ -613,9 +594,10 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
   } /* for (j = 1; j <= param_counter; j++) */
   /* }}} End of the ``define'' stuff. */
 
-  status = udb_query_prepare_result (q, prep_area, hostname_g,
+  status = udb_query_prepare_result (q, prep_area,
+      (db->host != NULL) ? db->host : hostname_g,
       /* plugin = */ "oracle", db->name, column_names, column_num,
-      /* interval = */ -1);
+      /* interval = */ 0);
   if (status != 0)
   {
     ERROR ("oracle plugin: o_read_database_query (%s, %s): "
diff --git a/src/owniptc/Makefile.am b/src/owniptc/Makefile.am
deleted file mode 100644 (file)
index d51e993..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-AUTOMAKE_OPTIONS = foreign no-dependencies
-
-EXTRA_DIST = libiptc.c README.collectd
-
-if COMPILER_IS_GCC
-AM_CFLAGS = -Wall -Werror
-endif
-
-noinst_LTLIBRARIES = libiptc.la
-
-libiptc_la_SOURCES = libip4tc.c libip6tc.c \
-               ipt_kernel_headers.h libip6tc.h libiptc.h linux_list.h \
-               xtables.h libxtc.h
-
diff --git a/src/owniptc/README.collectd b/src/owniptc/README.collectd
deleted file mode 100644 (file)
index adb53b0..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
- libiptc (IPTables Chains) in collectd
-=======================================
-http://netfilter.org/
-http://collectd.org/
-
-About
------
-
-  This is libiptc taken from the iptables source distribution. As it is not
-  meant to be a public interface by upstream it is not shipped in some binary
-  distributions. Thus, collectd ships its own copy as a fall-back.
-
-  The presently available version was imported from iptables 1.4.1.1.
-
-Changes to the iptables upstream sources:
------------------------------------------
-
-  * Added copyright headers mentioning the "Netfilter Core Team" as copyright
-    holder.
-
-  * Changed "libiptc/*" includes to "*".
-
-  * Use the shipped copy of "xtables.h" instead of the one possibly available
-    on the system.
-
diff --git a/src/owniptc/ipt_kernel_headers.h b/src/owniptc/ipt_kernel_headers.h
deleted file mode 100644 (file)
index bf81f6e..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * This file was imported from the iptables sources.
- * Copyright (C) 1999-2008 Netfilter Core Team
- *
- * 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
- */
-
-/* This is the userspace/kernel interface for Generic IP Chains,
-   required for libc6. */
-#ifndef _FWCHAINS_KERNEL_HEADERS_H
-#define _FWCHAINS_KERNEL_HEADERS_H
-
-#include <limits.h>
-
-#if defined(__GLIBC__) && __GLIBC__ == 2
-#include <netinet/ip.h>
-#include <netinet/in.h>
-#include <netinet/ip_icmp.h>
-#include <netinet/tcp.h>
-#include <netinet/udp.h>
-#include <net/if.h>
-#include <sys/types.h>
-#else /* libc5 */
-#include <sys/socket.h>
-#include <linux/ip.h>
-#include <linux/in.h>
-#include <linux/if.h>
-#include <linux/icmp.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
-#include <linux/types.h>
-#include <linux/in6.h>
-#endif
-#endif
diff --git a/src/owniptc/libip4tc.c b/src/owniptc/libip4tc.c
deleted file mode 100644 (file)
index bf7327c..0000000
+++ /dev/null
@@ -1,517 +0,0 @@
-/**
- * This file was imported from the iptables sources.
- * Copyright (C) 1999-2008 Netfilter Core Team
- *
- * 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
- */
-
-/* Library which manipulates firewall rules.  Version 0.1. */
-
-/* Architecture of firewall rules is as follows:
- *
- * Chains go INPUT, FORWARD, OUTPUT then user chains.
- * Each user chain starts with an ERROR node.
- * Every chain ends with an unconditional jump: a RETURN for user chains,
- * and a POLICY for built-ins.
- */
-
-/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
-   COPYING for details). */
-
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#ifdef DEBUG_CONNTRACK
-#define inline
-#endif
-
-#if !defined(__GLIBC__) || (__GLIBC__ < 2)
-typedef unsigned int socklen_t;
-#endif
-
-#include "libiptc.h"
-
-#define IP_VERSION     4
-#define IP_OFFSET      0x1FFF
-
-#define HOOK_PRE_ROUTING       NF_IP_PRE_ROUTING
-#define HOOK_LOCAL_IN          NF_IP_LOCAL_IN
-#define HOOK_FORWARD           NF_IP_FORWARD
-#define HOOK_LOCAL_OUT         NF_IP_LOCAL_OUT
-#define HOOK_POST_ROUTING      NF_IP_POST_ROUTING
-#ifdef NF_IP_DROPPING
-#define HOOK_DROPPING          NF_IP_DROPPING
-#endif
-
-#define STRUCT_ENTRY_TARGET    struct ipt_entry_target
-#define STRUCT_ENTRY           struct ipt_entry
-#define STRUCT_ENTRY_MATCH     struct ipt_entry_match
-#define STRUCT_GETINFO         struct ipt_getinfo
-#define STRUCT_GET_ENTRIES     struct ipt_get_entries
-#define STRUCT_COUNTERS                struct ipt_counters
-#define STRUCT_COUNTERS_INFO   struct ipt_counters_info
-#define STRUCT_STANDARD_TARGET struct ipt_standard_target
-#define STRUCT_REPLACE         struct ipt_replace
-
-#define STRUCT_TC_HANDLE       struct iptc_handle
-#define TC_HANDLE_T            iptc_handle_t
-
-#define ENTRY_ITERATE          IPT_ENTRY_ITERATE
-#define TABLE_MAXNAMELEN       IPT_TABLE_MAXNAMELEN
-#define FUNCTION_MAXNAMELEN    IPT_FUNCTION_MAXNAMELEN
-
-#define GET_TARGET             ipt_get_target
-
-#define ERROR_TARGET           IPT_ERROR_TARGET
-#define NUMHOOKS               NF_IP_NUMHOOKS
-
-#define IPT_CHAINLABEL         ipt_chainlabel
-
-#define TC_DUMP_ENTRIES                dump_entries
-#define TC_IS_CHAIN            iptc_is_chain
-#define TC_FIRST_CHAIN         iptc_first_chain
-#define TC_NEXT_CHAIN          iptc_next_chain
-#define TC_FIRST_RULE          iptc_first_rule
-#define TC_NEXT_RULE           iptc_next_rule
-#define TC_GET_TARGET          iptc_get_target
-#define TC_BUILTIN             iptc_builtin
-#define TC_GET_POLICY          iptc_get_policy
-#define TC_INSERT_ENTRY                iptc_insert_entry
-#define TC_REPLACE_ENTRY       iptc_replace_entry
-#define TC_APPEND_ENTRY                iptc_append_entry
-#define TC_DELETE_ENTRY                iptc_delete_entry
-#define TC_DELETE_NUM_ENTRY    iptc_delete_num_entry
-#define TC_CHECK_PACKET                iptc_check_packet
-#define TC_FLUSH_ENTRIES       iptc_flush_entries
-#define TC_ZERO_ENTRIES                iptc_zero_entries
-#define TC_READ_COUNTER                iptc_read_counter
-#define TC_ZERO_COUNTER                iptc_zero_counter
-#define TC_SET_COUNTER         iptc_set_counter
-#define TC_CREATE_CHAIN                iptc_create_chain
-#define TC_GET_REFERENCES      iptc_get_references
-#define TC_DELETE_CHAIN                iptc_delete_chain
-#define TC_RENAME_CHAIN                iptc_rename_chain
-#define TC_SET_POLICY          iptc_set_policy
-#define TC_GET_RAW_SOCKET      iptc_get_raw_socket
-#define TC_INIT                        iptc_init
-#define TC_FREE                        iptc_free
-#define TC_COMMIT              iptc_commit
-#define TC_STRERROR            iptc_strerror
-#define TC_NUM_RULES           iptc_num_rules
-#define TC_GET_RULE            iptc_get_rule
-
-#define TC_AF                  AF_INET
-#define TC_IPPROTO             IPPROTO_IP
-
-#define SO_SET_REPLACE         IPT_SO_SET_REPLACE
-#define SO_SET_ADD_COUNTERS    IPT_SO_SET_ADD_COUNTERS
-#define SO_GET_INFO            IPT_SO_GET_INFO
-#define SO_GET_ENTRIES         IPT_SO_GET_ENTRIES
-#define SO_GET_VERSION         IPT_SO_GET_VERSION
-
-#define STANDARD_TARGET                IPT_STANDARD_TARGET
-#define LABEL_RETURN           IPTC_LABEL_RETURN
-#define LABEL_ACCEPT           IPTC_LABEL_ACCEPT
-#define LABEL_DROP             IPTC_LABEL_DROP
-#define LABEL_QUEUE            IPTC_LABEL_QUEUE
-
-#define ALIGN                  IPT_ALIGN
-#define RETURN                 IPT_RETURN
-
-#include "libiptc.c"
-
-#define IP_PARTS_NATIVE(n)                     \
-(unsigned int)((n)>>24)&0xFF,                  \
-(unsigned int)((n)>>16)&0xFF,                  \
-(unsigned int)((n)>>8)&0xFF,                   \
-(unsigned int)((n)&0xFF)
-
-#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
-
-int
-dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
-{
-       size_t i;
-       STRUCT_ENTRY_TARGET *t;
-
-       printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
-              iptcb_entry2offset(handle, e));
-       printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
-              IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
-       printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
-              IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr));
-       printf("Interface: `%s'/", e->ip.iniface);
-       for (i = 0; i < IFNAMSIZ; i++)
-               printf("%c", e->ip.iniface_mask[i] ? 'X' : '.');
-       printf("to `%s'/", e->ip.outiface);
-       for (i = 0; i < IFNAMSIZ; i++)
-               printf("%c", e->ip.outiface_mask[i] ? 'X' : '.');
-       printf("\nProtocol: %u\n", e->ip.proto);
-       printf("Flags: %02X\n", e->ip.flags);
-       printf("Invflags: %02X\n", e->ip.invflags);
-       printf("Counters: %llu packets, %llu bytes\n",
-              (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
-       printf("Cache: %08X\n", e->nfcache);
-
-       IPT_MATCH_ITERATE(e, print_match);
-
-       t = GET_TARGET(e);
-       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
-       if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) {
-               const unsigned char *data = t->data;
-               int pos = *(const int *)data;
-               if (pos < 0)
-                       printf("verdict=%s\n",
-                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
-                              : pos == -NF_DROP-1 ? "NF_DROP"
-                              : pos == -NF_QUEUE-1 ? "NF_QUEUE"
-                              : pos == RETURN ? "RETURN"
-                              : "UNKNOWN");
-               else
-                       printf("verdict=%u\n", pos);
-       } else if (strcmp(t->u.user.name, IPT_ERROR_TARGET) == 0)
-               printf("error=`%s'\n", t->data);
-
-       printf("\n");
-       return 0;
-}
-
-static unsigned char *
-is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
-{
-       unsigned int i;
-       unsigned char *mptr;
-
-       /* Always compare head structures: ignore mask here. */
-       if (a->ip.src.s_addr != b->ip.src.s_addr
-           || a->ip.dst.s_addr != b->ip.dst.s_addr
-           || a->ip.smsk.s_addr != b->ip.smsk.s_addr
-           || a->ip.dmsk.s_addr != b->ip.dmsk.s_addr
-           || a->ip.proto != b->ip.proto
-           || a->ip.flags != b->ip.flags
-           || a->ip.invflags != b->ip.invflags)
-               return NULL;
-
-       for (i = 0; i < IFNAMSIZ; i++) {
-               if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
-                       return NULL;
-               if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
-                   != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
-                       return NULL;
-               if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
-                       return NULL;
-               if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
-                   != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
-                       return NULL;
-       }
-
-       if (a->target_offset != b->target_offset
-           || a->next_offset != b->next_offset)
-               return NULL;
-
-       mptr = matchmask + sizeof(STRUCT_ENTRY);
-       if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
-               return NULL;
-       mptr += IPT_ALIGN(sizeof(struct ipt_entry_target));
-
-       return mptr;
-}
-
-#if 0
-/***************************** DEBUGGING ********************************/
-static inline int
-unconditional(const struct ipt_ip *ip)
-{
-       unsigned int i;
-
-       for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++)
-               if (((u_int32_t *)ip)[i])
-                       return 0;
-
-       return 1;
-}
-
-static inline int
-check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off)
-{
-       assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH));
-       assert(ALIGN(m->u.match_size) == m->u.match_size);
-
-       (*off) += m->u.match_size;
-       return 0;
-}
-
-static inline int
-check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
-           unsigned int user_offset, int *was_return,
-           TC_HANDLE_T h)
-{
-       unsigned int toff;
-       STRUCT_STANDARD_TARGET *t;
-
-       assert(e->target_offset >= sizeof(STRUCT_ENTRY));
-       assert(e->next_offset >= e->target_offset
-              + sizeof(STRUCT_ENTRY_TARGET));
-       toff = sizeof(STRUCT_ENTRY);
-       IPT_MATCH_ITERATE(e, check_match, &toff);
-
-       assert(toff == e->target_offset);
-
-       t = (STRUCT_STANDARD_TARGET *)
-               GET_TARGET((STRUCT_ENTRY *)e);
-       /* next_offset will have to be multiple of entry alignment. */
-       assert(e->next_offset == ALIGN(e->next_offset));
-       assert(e->target_offset == ALIGN(e->target_offset));
-       assert(t->target.u.target_size == ALIGN(t->target.u.target_size));
-       assert(!TC_IS_CHAIN(t->target.u.user.name, h));
-
-       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) {
-               assert(t->target.u.target_size
-                      == ALIGN(sizeof(STRUCT_STANDARD_TARGET)));
-
-               assert(t->verdict == -NF_DROP-1
-                      || t->verdict == -NF_ACCEPT-1
-                      || t->verdict == RETURN
-                      || t->verdict < (int)h->entries->size);
-
-               if (t->verdict >= 0) {
-                       STRUCT_ENTRY *te = get_entry(h, t->verdict);
-                       int idx;
-
-                       idx = iptcb_entry2index(h, te);
-                       assert(strcmp(GET_TARGET(te)->u.user.name,
-                                     IPT_ERROR_TARGET)
-                              != 0);
-                       assert(te != e);
-
-                       /* Prior node must be error node, or this node. */
-                       assert(t->verdict == iptcb_entry2offset(h, e)+e->next_offset
-                              || strcmp(GET_TARGET(index2entry(h, idx-1))
-                                        ->u.user.name, IPT_ERROR_TARGET)
-                              == 0);
-               }
-
-               if (t->verdict == RETURN
-                   && unconditional(&e->ip)
-                   && e->target_offset == sizeof(*e))
-                       *was_return = 1;
-               else
-                       *was_return = 0;
-       } else if (strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0) {
-               assert(t->target.u.target_size
-                      == ALIGN(sizeof(struct ipt_error_target)));
-
-               /* If this is in user area, previous must have been return */
-               if (*off > user_offset)
-                       assert(*was_return);
-
-               *was_return = 0;
-       }
-       else *was_return = 0;
-
-       if (*off == user_offset)
-               assert(strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0);
-
-       (*off) += e->next_offset;
-       (*i)++;
-       return 0;
-}
-
-#ifdef IPTC_DEBUG
-/* Do every conceivable sanity check on the handle */
-static void
-do_check(TC_HANDLE_T h, unsigned int line)
-{
-       unsigned int i, n;
-       unsigned int user_offset; /* Offset of first user chain */
-       int was_return;
-
-       assert(h->changed == 0 || h->changed == 1);
-       if (strcmp(h->info.name, "filter") == 0) {
-               assert(h->info.valid_hooks
-                      == (1 << NF_IP_LOCAL_IN
-                          | 1 << NF_IP_FORWARD
-                          | 1 << NF_IP_LOCAL_OUT));
-
-               /* Hooks should be first three */
-               assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
-
-               n = get_chain_end(h, 0);
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP_FORWARD] == n);
-
-               n = get_chain_end(h, n);
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
-
-               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
-       } else if (strcmp(h->info.name, "nat") == 0) {
-               assert((h->info.valid_hooks
-                       == (1 << NF_IP_PRE_ROUTING
-                           | 1 << NF_IP_POST_ROUTING
-                           | 1 << NF_IP_LOCAL_OUT)) ||
-                      (h->info.valid_hooks
-                       == (1 << NF_IP_PRE_ROUTING
-                           | 1 << NF_IP_LOCAL_IN
-                           | 1 << NF_IP_POST_ROUTING
-                           | 1 << NF_IP_LOCAL_OUT)));
-
-               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
-
-               n = get_chain_end(h, 0);
-
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
-               n = get_chain_end(h, n);
-
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
-               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
-
-               if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
-                       n = get_chain_end(h, n);
-                       n += get_entry(h, n)->next_offset;
-                       assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
-                       user_offset = h->info.hook_entry[NF_IP_LOCAL_IN];
-               }
-
-       } else if (strcmp(h->info.name, "mangle") == 0) {
-               /* This code is getting ugly because linux < 2.4.18-pre6 had
-                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
-                * */
-               assert((h->info.valid_hooks
-                       == (1 << NF_IP_PRE_ROUTING
-                           | 1 << NF_IP_LOCAL_OUT)) || 
-                      (h->info.valid_hooks
-                       == (1 << NF_IP_PRE_ROUTING
-                           | 1 << NF_IP_LOCAL_IN
-                           | 1 << NF_IP_FORWARD
-                           | 1 << NF_IP_LOCAL_OUT
-                           | 1 << NF_IP_POST_ROUTING)));
-
-               /* Hooks should be first five */
-               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
-
-               n = get_chain_end(h, 0);
-
-               if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
-                       n += get_entry(h, n)->next_offset;
-                       assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
-                       n = get_chain_end(h, n);
-               }
-
-               if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) {
-                       n += get_entry(h, n)->next_offset;
-                       assert(h->info.hook_entry[NF_IP_FORWARD] == n);
-                       n = get_chain_end(h, n);
-               }
-
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
-               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
-
-               if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) {
-                       n = get_chain_end(h, n);
-                       n += get_entry(h, n)->next_offset;
-                       assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
-                       user_offset = h->info.hook_entry[NF_IP_POST_ROUTING];
-               }
-       } else if (strcmp(h->info.name, "raw") == 0) {
-               assert(h->info.valid_hooks
-                      == (1 << NF_IP_PRE_ROUTING
-                          | 1 << NF_IP_LOCAL_OUT));
-
-               /* Hooks should be first three */
-               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
-
-               n = get_chain_end(h, n);
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
-
-               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
-
-#ifdef NF_IP_DROPPING
-       } else if (strcmp(h->info.name, "drop") == 0) {
-               assert(h->info.valid_hooks == (1 << NF_IP_DROPPING));
-
-               /* Hook should be first */
-               assert(h->info.hook_entry[NF_IP_DROPPING] == 0);
-               user_offset = 0;
-#endif
-       } else {
-               fprintf(stderr, "Unknown table `%s'\n", h->info.name);
-               abort();
-       }
-
-       /* User chain == end of last builtin + policy entry */
-       user_offset = get_chain_end(h, user_offset);
-       user_offset += get_entry(h, user_offset)->next_offset;
-
-       /* Overflows should be end of entry chains, and unconditional
-           policy nodes. */
-       for (i = 0; i < NUMHOOKS; i++) {
-               STRUCT_ENTRY *e;
-               STRUCT_STANDARD_TARGET *t;
-
-               if (!(h->info.valid_hooks & (1 << i)))
-                       continue;
-               assert(h->info.underflow[i]
-                      == get_chain_end(h, h->info.hook_entry[i]));
-
-               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
-               assert(unconditional(&e->ip));
-               assert(e->target_offset == sizeof(*e));
-               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
-               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
-               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
-
-               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
-               assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
-
-               /* Hooks and underflows must be valid entries */
-               entry2index(h, get_entry(h, h->info.hook_entry[i]));
-               entry2index(h, get_entry(h, h->info.underflow[i]));
-       }
-
-       assert(h->info.size
-              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
-                                        +sizeof(STRUCT_STANDARD_TARGET)));
-
-       assert(h->entries.size
-              >= (h->new_number
-                  * (sizeof(STRUCT_ENTRY)
-                     + sizeof(STRUCT_STANDARD_TARGET))));
-       assert(strcmp(h->info.name, h->entries.name) == 0);
-
-       i = 0; n = 0;
-       was_return = 0;
-       /* Check all the entries. */
-       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
-                     check_entry, &i, &n, user_offset, &was_return, h);
-
-       assert(i == h->new_number);
-       assert(n == h->entries.size);
-
-       /* Final entry must be error node */
-       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
-                     ->u.user.name,
-                     ERROR_TARGET) == 0);
-}
-#endif /*IPTC_DEBUG*/
-
-#endif
diff --git a/src/owniptc/libip6tc.c b/src/owniptc/libip6tc.c
deleted file mode 100644 (file)
index 672dae1..0000000
+++ /dev/null
@@ -1,456 +0,0 @@
-/**
- * This file was imported from the iptables sources.
- * Copyright (C) 1999-2008 Netfilter Core Team
- *
- * 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
- */
-
-/* Library which manipulates firewall rules.  Version 0.1. */
-
-/* Architecture of firewall rules is as follows:
- *
- * Chains go INPUT, FORWARD, OUTPUT then user chains.
- * Each user chain starts with an ERROR node.
- * Every chain ends with an unconditional jump: a RETURN for user chains,
- * and a POLICY for built-ins.
- */
-
-/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
-   COPYING for details). */
-
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-
-#ifdef DEBUG_CONNTRACK
-#define inline
-#endif
-
-#if !defined(__GLIBC__) || (__GLIBC__ < 2)
-typedef unsigned int socklen_t;
-#endif
-
-#include "libip6tc.h"
-
-#define HOOK_PRE_ROUTING       NF_IP6_PRE_ROUTING
-#define HOOK_LOCAL_IN          NF_IP6_LOCAL_IN
-#define HOOK_FORWARD           NF_IP6_FORWARD
-#define HOOK_LOCAL_OUT         NF_IP6_LOCAL_OUT
-#define HOOK_POST_ROUTING      NF_IP6_POST_ROUTING
-
-#define STRUCT_ENTRY_TARGET    struct ip6t_entry_target
-#define STRUCT_ENTRY           struct ip6t_entry
-#define STRUCT_ENTRY_MATCH     struct ip6t_entry_match
-#define STRUCT_GETINFO         struct ip6t_getinfo
-#define STRUCT_GET_ENTRIES     struct ip6t_get_entries
-#define STRUCT_COUNTERS                struct ip6t_counters
-#define STRUCT_COUNTERS_INFO   struct ip6t_counters_info
-#define STRUCT_STANDARD_TARGET struct ip6t_standard_target
-#define STRUCT_REPLACE         struct ip6t_replace
-
-#define STRUCT_TC_HANDLE       struct ip6tc_handle
-#define TC_HANDLE_T            ip6tc_handle_t
-
-#define ENTRY_ITERATE          IP6T_ENTRY_ITERATE
-#define TABLE_MAXNAMELEN       IP6T_TABLE_MAXNAMELEN
-#define FUNCTION_MAXNAMELEN    IP6T_FUNCTION_MAXNAMELEN
-
-#define GET_TARGET             ip6t_get_target
-
-#define ERROR_TARGET           IP6T_ERROR_TARGET
-#define NUMHOOKS               NF_IP6_NUMHOOKS
-
-#define IPT_CHAINLABEL         ip6t_chainlabel
-
-#define TC_DUMP_ENTRIES                dump_entries6
-#define TC_IS_CHAIN            ip6tc_is_chain
-#define TC_FIRST_CHAIN         ip6tc_first_chain
-#define TC_NEXT_CHAIN          ip6tc_next_chain
-#define TC_FIRST_RULE          ip6tc_first_rule
-#define TC_NEXT_RULE           ip6tc_next_rule
-#define TC_GET_TARGET          ip6tc_get_target
-#define TC_BUILTIN             ip6tc_builtin
-#define TC_GET_POLICY          ip6tc_get_policy
-#define TC_INSERT_ENTRY                ip6tc_insert_entry
-#define TC_REPLACE_ENTRY       ip6tc_replace_entry
-#define TC_APPEND_ENTRY                ip6tc_append_entry
-#define TC_DELETE_ENTRY                ip6tc_delete_entry
-#define TC_DELETE_NUM_ENTRY    ip6tc_delete_num_entry
-#define TC_CHECK_PACKET                ip6tc_check_packet
-#define TC_FLUSH_ENTRIES       ip6tc_flush_entries
-#define TC_ZERO_ENTRIES                ip6tc_zero_entries
-#define TC_ZERO_COUNTER                ip6tc_zero_counter
-#define TC_READ_COUNTER                ip6tc_read_counter
-#define TC_SET_COUNTER         ip6tc_set_counter
-#define TC_CREATE_CHAIN                ip6tc_create_chain
-#define TC_GET_REFERENCES      ip6tc_get_references
-#define TC_DELETE_CHAIN                ip6tc_delete_chain
-#define TC_RENAME_CHAIN                ip6tc_rename_chain
-#define TC_SET_POLICY          ip6tc_set_policy
-#define TC_GET_RAW_SOCKET      ip6tc_get_raw_socket
-#define TC_INIT                        ip6tc_init
-#define TC_FREE                        ip6tc_free
-#define TC_COMMIT              ip6tc_commit
-#define TC_STRERROR            ip6tc_strerror
-#define TC_NUM_RULES           ip6tc_num_rules
-#define TC_GET_RULE            ip6tc_get_rule
-
-#define TC_AF                  AF_INET6
-#define TC_IPPROTO             IPPROTO_IPV6
-
-#define SO_SET_REPLACE         IP6T_SO_SET_REPLACE
-#define SO_SET_ADD_COUNTERS    IP6T_SO_SET_ADD_COUNTERS
-#define SO_GET_INFO            IP6T_SO_GET_INFO
-#define SO_GET_ENTRIES         IP6T_SO_GET_ENTRIES
-#define SO_GET_VERSION         IP6T_SO_GET_VERSION
-
-#define STANDARD_TARGET                IP6T_STANDARD_TARGET
-#define LABEL_RETURN           IP6TC_LABEL_RETURN
-#define LABEL_ACCEPT           IP6TC_LABEL_ACCEPT
-#define LABEL_DROP             IP6TC_LABEL_DROP
-#define LABEL_QUEUE            IP6TC_LABEL_QUEUE
-
-#define ALIGN                  IP6T_ALIGN
-#define RETURN                 IP6T_RETURN
-
-#include "libiptc.c"
-
-#define BIT6(a, l) \
- ((ntohl(a->s6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
-
-int
-ipv6_prefix_length(const struct in6_addr *a)
-{
-       int l, i;
-       for (l = 0; l < 128; l++) {
-               if (BIT6(a, l) == 0)
-                       break;
-       }
-       for (i = l + 1; i < 128; i++) {
-               if (BIT6(a, i) == 1)
-                       return -1;
-       }
-       return l;
-}
-
-static int
-dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
-{
-       size_t i;
-       char buf[40];
-       int len;
-       struct ip6t_entry_target *t;
-       
-       printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
-              iptcb_entry2offset(handle, e));
-       puts("SRC IP: ");
-       inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
-       puts(buf);
-       putchar('/');
-       len = ipv6_prefix_length(&e->ipv6.smsk);
-       if (len != -1)
-               printf("%d", len);
-       else {
-               inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
-               puts(buf);
-       }
-       putchar('\n');
-       
-       puts("DST IP: ");
-       inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
-       puts(buf);
-       putchar('/');
-       len = ipv6_prefix_length(&e->ipv6.dmsk);
-       if (len != -1)
-               printf("%d", len);
-       else {
-               inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
-               puts(buf);
-       }
-       putchar('\n');
-       
-       printf("Interface: `%s'/", e->ipv6.iniface);
-       for (i = 0; i < IFNAMSIZ; i++)
-               printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
-       printf("to `%s'/", e->ipv6.outiface);
-       for (i = 0; i < IFNAMSIZ; i++)
-               printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
-       printf("\nProtocol: %u\n", e->ipv6.proto);
-       if (e->ipv6.flags & IP6T_F_TOS)
-               printf("TOS: %u\n", e->ipv6.tos);
-       printf("Flags: %02X\n", e->ipv6.flags);
-       printf("Invflags: %02X\n", e->ipv6.invflags);
-       printf("Counters: %llu packets, %llu bytes\n",
-              (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
-       printf("Cache: %08X\n", e->nfcache);
-       
-       IP6T_MATCH_ITERATE(e, print_match);
-
-       t = ip6t_get_target(e);
-       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
-       if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
-               const unsigned char *data = t->data;
-               int pos = *(const int *)data;
-               if (pos < 0)
-                       printf("verdict=%s\n",
-                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
-                              : pos == -NF_DROP-1 ? "NF_DROP"
-                              : pos == IP6T_RETURN ? "RETURN"
-                              : "UNKNOWN");
-               else
-                       printf("verdict=%u\n", pos);
-       } else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
-               printf("error=`%s'\n", t->data);
-
-       printf("\n");
-       return 0;
-}
-
-static unsigned char *
-is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
-       unsigned char *matchmask)
-{
-       unsigned int i;
-       unsigned char *mptr;
-
-       /* Always compare head structures: ignore mask here. */
-       if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
-           || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
-           || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
-           || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
-           || a->ipv6.proto != b->ipv6.proto
-           || a->ipv6.tos != b->ipv6.tos
-           || a->ipv6.flags != b->ipv6.flags
-           || a->ipv6.invflags != b->ipv6.invflags)
-               return NULL;
-
-       for (i = 0; i < IFNAMSIZ; i++) {
-               if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
-                       return NULL;
-               if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
-                   != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
-                       return NULL;
-               if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
-                       return NULL;
-               if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
-                   != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
-                       return NULL;
-       }
-
-       if (a->target_offset != b->target_offset
-           || a->next_offset != b->next_offset)
-               return NULL;
-
-       mptr = matchmask + sizeof(STRUCT_ENTRY);
-       if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
-               return NULL;
-       mptr += IP6T_ALIGN(sizeof(struct ip6t_entry_target));
-
-       return mptr;
-}
-
-/* All zeroes == unconditional rule. */
-static inline int
-unconditional(const struct ip6t_ip6 *ipv6)
-{
-       unsigned int i;
-
-       for (i = 0; i < sizeof(*ipv6); i++)
-               if (((char *)ipv6)[i])
-                       break;
-
-       return (i == sizeof(*ipv6));
-}
-
-#ifdef IPTC_DEBUG
-/* Do every conceivable sanity check on the handle */
-static void
-do_check(TC_HANDLE_T h, unsigned int line)
-{
-       unsigned int i, n;
-       unsigned int user_offset; /* Offset of first user chain */
-       int was_return;
-
-       assert(h->changed == 0 || h->changed == 1);
-       if (strcmp(h->info.name, "filter") == 0) {
-               assert(h->info.valid_hooks
-                      == (1 << NF_IP6_LOCAL_IN
-                          | 1 << NF_IP6_FORWARD
-                          | 1 << NF_IP6_LOCAL_OUT));
-
-               /* Hooks should be first three */
-               assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
-
-               n = get_chain_end(h, 0);
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
-
-               n = get_chain_end(h, n);
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
-
-               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
-       } else if (strcmp(h->info.name, "nat") == 0) {
-               assert((h->info.valid_hooks
-                       == (1 << NF_IP6_PRE_ROUTING
-                           | 1 << NF_IP6_LOCAL_OUT
-                           | 1 << NF_IP6_POST_ROUTING)) ||
-                      (h->info.valid_hooks
-                       == (1 << NF_IP6_PRE_ROUTING
-                           | 1 << NF_IP6_LOCAL_IN
-                           | 1 << NF_IP6_LOCAL_OUT
-                           | 1 << NF_IP6_POST_ROUTING)));
-
-               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
-
-               n = get_chain_end(h, 0);
-
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
-               n = get_chain_end(h, n);
-
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
-               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
-
-               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
-                       n = get_chain_end(h, n);
-                       n += get_entry(h, n)->next_offset;
-                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
-                       user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
-               }
-
-       } else if (strcmp(h->info.name, "mangle") == 0) {
-               /* This code is getting ugly because linux < 2.4.18-pre6 had
-                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
-                * */
-               assert((h->info.valid_hooks
-                       == (1 << NF_IP6_PRE_ROUTING
-                           | 1 << NF_IP6_LOCAL_OUT)) ||
-                      (h->info.valid_hooks
-                       == (1 << NF_IP6_PRE_ROUTING
-                           | 1 << NF_IP6_LOCAL_IN
-                           | 1 << NF_IP6_FORWARD
-                           | 1 << NF_IP6_LOCAL_OUT
-                           | 1 << NF_IP6_POST_ROUTING)));
-
-               /* Hooks should be first five */
-               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
-
-               n = get_chain_end(h, 0);
-
-               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
-                       n += get_entry(h, n)->next_offset;
-                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
-                       n = get_chain_end(h, n);
-               }
-
-               if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
-                       n += get_entry(h, n)->next_offset;
-                       assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
-                       n = get_chain_end(h, n);
-               }
-
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
-               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
-
-               if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
-                       n = get_chain_end(h, n);
-                       n += get_entry(h, n)->next_offset;
-                       assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
-                       user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
-               }
-       } else if (strcmp(h->info.name, "raw") == 0) {
-               assert(h->info.valid_hooks
-                      == (1 << NF_IP6_PRE_ROUTING
-                          | 1 << NF_IP6_LOCAL_OUT));
-
-               /* Hooks should be first three */
-               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
-
-               n = get_chain_end(h, n);
-               n += get_entry(h, n)->next_offset;
-               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
-
-               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
-       } else {
-                fprintf(stderr, "Unknown table `%s'\n", h->info.name);
-               abort();
-       }
-
-       /* User chain == end of last builtin + policy entry */
-       user_offset = get_chain_end(h, user_offset);
-       user_offset += get_entry(h, user_offset)->next_offset;
-
-       /* Overflows should be end of entry chains, and unconditional
-           policy nodes. */
-       for (i = 0; i < NUMHOOKS; i++) {
-               STRUCT_ENTRY *e;
-               STRUCT_STANDARD_TARGET *t;
-
-               if (!(h->info.valid_hooks & (1 << i)))
-                       continue;
-               assert(h->info.underflow[i]
-                      == get_chain_end(h, h->info.hook_entry[i]));
-
-               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
-               assert(unconditional(&e->ipv6));
-               assert(e->target_offset == sizeof(*e));
-               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
-               printf("target_size=%u, align=%u\n",
-                       t->target.u.target_size, ALIGN(sizeof(*t)));
-               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
-               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
-
-               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
-               assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
-
-               /* Hooks and underflows must be valid entries */
-               iptcb_entry2index(h, get_entry(h, h->info.hook_entry[i]));
-               iptcb_entry2index(h, get_entry(h, h->info.underflow[i]));
-       }
-
-       assert(h->info.size
-              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
-                                        +sizeof(STRUCT_STANDARD_TARGET)));
-
-       assert(h->entries.size
-              >= (h->new_number
-                  * (sizeof(STRUCT_ENTRY)
-                     + sizeof(STRUCT_STANDARD_TARGET))));
-       assert(strcmp(h->info.name, h->entries.name) == 0);
-
-       i = 0; n = 0;
-       was_return = 0;
-
-#if 0
-       /* Check all the entries. */
-       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
-                     check_entry, &i, &n, user_offset, &was_return, h);
-
-       assert(i == h->new_number);
-       assert(n == h->entries.size);
-
-       /* Final entry must be error node */
-       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
-                     ->u.user.name,
-                     ERROR_TARGET) == 0);
-#endif
-}
-#endif /*IPTC_DEBUG*/
diff --git a/src/owniptc/libip6tc.h b/src/owniptc/libip6tc.h
deleted file mode 100644 (file)
index 9253e11..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/**
- * This file was imported from the iptables sources.
- * Copyright (C) 1999-2008 Netfilter Core Team
- *
- * 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
- */
-
-#ifndef _LIBIP6TC_H
-#define _LIBIP6TC_H
-/* Library which manipulates firewall rules. Version 0.2. */
-
-#include <linux/types.h>
-#include "ipt_kernel_headers.h"
-#include <linux/netfilter_ipv6/ip6_tables.h>
-
-#ifndef IP6T_MIN_ALIGN
-#define IP6T_MIN_ALIGN (__alignof__(struct ip6t_entry))
-#endif
-#define IP6T_ALIGN(s) (((s) + (IP6T_MIN_ALIGN-1)) & ~(IP6T_MIN_ALIGN-1))
-
-typedef char ip6t_chainlabel[32];
-
-#define IP6TC_LABEL_ACCEPT "ACCEPT"
-#define IP6TC_LABEL_DROP "DROP"
-#define IP6TC_LABEL_QUEUE   "QUEUE"
-#define IP6TC_LABEL_RETURN "RETURN"
-
-/* Transparent handle type. */
-typedef struct ip6tc_handle *ip6tc_handle_t;
-
-/* Does this chain exist? */
-int ip6tc_is_chain(const char *chain, const ip6tc_handle_t handle);
-
-/* Take a snapshot of the rules. Returns NULL on error. */
-ip6tc_handle_t ip6tc_init(const char *tablename);
-
-/* Cleanup after ip6tc_init(). */
-void ip6tc_free(ip6tc_handle_t *h);
-
-/* Iterator functions to run through the chains.  Returns NULL at end. */
-const char *ip6tc_first_chain(ip6tc_handle_t *handle);
-const char *ip6tc_next_chain(ip6tc_handle_t *handle);
-
-/* Get first rule in the given chain: NULL for empty chain. */
-const struct ip6t_entry *ip6tc_first_rule(const char *chain,
-                                         ip6tc_handle_t *handle);
-
-/* Returns NULL when rules run out. */
-const struct ip6t_entry *ip6tc_next_rule(const struct ip6t_entry *prev,
-                                        ip6tc_handle_t *handle);
-
-/* Returns a pointer to the target name of this position. */
-const char *ip6tc_get_target(const struct ip6t_entry *e,
-                            ip6tc_handle_t *handle);
-
-/* Is this a built-in chain? */
-int ip6tc_builtin(const char *chain, const ip6tc_handle_t handle);
-
-/* Get the policy of a given built-in chain */
-const char *ip6tc_get_policy(const char *chain,
-                            struct ip6t_counters *counters,
-                            ip6tc_handle_t *handle);
-
-/* These functions return TRUE for OK or 0 and set errno. If errno ==
-   0, it means there was a version error (ie. upgrade libiptc). */
-/* Rule numbers start at 1 for the first rule. */
-
-/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
-int ip6tc_insert_entry(const ip6t_chainlabel chain,
-                      const struct ip6t_entry *e,
-                      unsigned int rulenum,
-                      ip6tc_handle_t *handle);
-
-/* Atomically replace rule `rulenum' in `chain' with `fw'. */
-int ip6tc_replace_entry(const ip6t_chainlabel chain,
-                       const struct ip6t_entry *e,
-                       unsigned int rulenum,
-                       ip6tc_handle_t *handle);
-
-/* Append entry `fw' to chain `chain'. Equivalent to insert with
-   rulenum = length of chain. */
-int ip6tc_append_entry(const ip6t_chainlabel chain,
-                      const struct ip6t_entry *e,
-                      ip6tc_handle_t *handle);
-
-/* Delete the first rule in `chain' which matches `fw'. */
-int ip6tc_delete_entry(const ip6t_chainlabel chain,
-                      const struct ip6t_entry *origfw,
-                      unsigned char *matchmask,
-                      ip6tc_handle_t *handle);
-
-/* Delete the rule in position `rulenum' in `chain'. */
-int ip6tc_delete_num_entry(const ip6t_chainlabel chain,
-                          unsigned int rulenum,
-                          ip6tc_handle_t *handle);
-
-/* Check the packet `fw' on chain `chain'. Returns the verdict, or
-   NULL and sets errno. */
-const char *ip6tc_check_packet(const ip6t_chainlabel chain,
-                              struct ip6t_entry *,
-                              ip6tc_handle_t *handle);
-
-/* Flushes the entries in the given chain (ie. empties chain). */
-int ip6tc_flush_entries(const ip6t_chainlabel chain,
-                       ip6tc_handle_t *handle);
-
-/* Zeroes the counters in a chain. */
-int ip6tc_zero_entries(const ip6t_chainlabel chain,
-                      ip6tc_handle_t *handle);
-
-/* Creates a new chain. */
-int ip6tc_create_chain(const ip6t_chainlabel chain,
-                      ip6tc_handle_t *handle);
-
-/* Deletes a chain. */
-int ip6tc_delete_chain(const ip6t_chainlabel chain,
-                      ip6tc_handle_t *handle);
-
-/* Renames a chain. */
-int ip6tc_rename_chain(const ip6t_chainlabel oldname,
-                      const ip6t_chainlabel newname,
-                      ip6tc_handle_t *handle);
-
-/* Sets the policy on a built-in chain. */
-int ip6tc_set_policy(const ip6t_chainlabel chain,
-                    const ip6t_chainlabel policy,
-                    struct ip6t_counters *counters,
-                    ip6tc_handle_t *handle);
-
-/* Get the number of references to this chain */
-int ip6tc_get_references(unsigned int *ref, const ip6t_chainlabel chain,
-                        ip6tc_handle_t *handle);
-
-/* read packet and byte counters for a specific rule */
-struct ip6t_counters *ip6tc_read_counter(const ip6t_chainlabel chain,
-                                       unsigned int rulenum,
-                                       ip6tc_handle_t *handle);
-
-/* zero packet and byte counters for a specific rule */
-int ip6tc_zero_counter(const ip6t_chainlabel chain,
-                      unsigned int rulenum,
-                      ip6tc_handle_t *handle);
-
-/* set packet and byte counters for a specific rule */
-int ip6tc_set_counter(const ip6t_chainlabel chain,
-                     unsigned int rulenum,
-                     struct ip6t_counters *counters,
-                     ip6tc_handle_t *handle);
-
-/* Makes the actual changes. */
-int ip6tc_commit(ip6tc_handle_t *handle);
-
-/* Get raw socket. */
-int ip6tc_get_raw_socket(void);
-
-/* Translates errno numbers into more human-readable form than strerror. */
-const char *ip6tc_strerror(int err);
-
-/* Return prefix length, or -1 if not contiguous */
-int ipv6_prefix_length(const struct in6_addr *a);
-
-extern void dump_entries6(const ip6tc_handle_t);
-
-#endif /* _LIBIP6TC_H */
diff --git a/src/owniptc/libiptc.c b/src/owniptc/libiptc.c
deleted file mode 100644 (file)
index 7e48e18..0000000
+++ /dev/null
@@ -1,2714 +0,0 @@
-/**
- * This file was imported from the iptables sources.
- * Copyright (C) 1999-2008 Netfilter Core Team
- *
- * 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
- */
-
-/* Library which manipulates firewall rules.  Version $Revision: 7138 $ */
-
-/* Architecture of firewall rules is as follows:
- *
- * Chains go INPUT, FORWARD, OUTPUT then user chains.
- * Each user chain starts with an ERROR node.
- * Every chain ends with an unconditional jump: a RETURN for user chains,
- * and a POLICY for built-ins.
- */
-
-/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
- * COPYING for details). 
- * (C) 2000-2004 by the Netfilter Core Team <coreteam@netfilter.org>
- *
- * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
- *     - Reimplementation of chain cache to use offsets instead of entries
- * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
- *     - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
- *       don't rebuild the chain cache after every operation, instead fix it
- *       up after a ruleset change.  
- * 2004-Aug-18: Harald Welte <laforge@netfilter.org>:
- *     - futher performance work: total reimplementation of libiptc.
- *     - libiptc now has a real internal (linked-list) represntation of the
- *       ruleset and a parser/compiler from/to this internal representation
- *     - again sponsored by Astaro AG (http://www.astaro.com/)
- */
-#include <sys/types.h>
-#include <sys/socket.h>
-#include "xtables.h"
-
-#include "linux_list.h"
-
-//#define IPTC_DEBUG2 1
-
-#ifdef IPTC_DEBUG2
-#include <fcntl.h>
-#define DEBUGP(x, args...)     fprintf(stderr, "%s: " x, __FUNCTION__, ## args)
-#define DEBUGP_C(x, args...)   fprintf(stderr, x, ## args)
-#else
-#define DEBUGP(x, args...)
-#define DEBUGP_C(x, args...)
-#endif
-
-#ifdef DEBUG
-#define debug(x, args...)      fprintf(stderr, x, ## args)
-#else
-#define debug(x, args...)
-#endif
-
-static int sockfd = -1;
-static int sockfd_use = 0;
-static void *iptc_fn = NULL;
-
-static const char *hooknames[] = {
-       [HOOK_PRE_ROUTING]      = "PREROUTING",
-       [HOOK_LOCAL_IN]         = "INPUT",
-       [HOOK_FORWARD]          = "FORWARD",
-       [HOOK_LOCAL_OUT]        = "OUTPUT",
-       [HOOK_POST_ROUTING]     = "POSTROUTING",
-#ifdef HOOK_DROPPING
-       [HOOK_DROPPING]         = "DROPPING"
-#endif
-};
-
-/* Convenience structures */
-struct ipt_error_target
-{
-       STRUCT_ENTRY_TARGET t;
-       char error[TABLE_MAXNAMELEN];
-};
-
-struct chain_head;
-struct rule_head;
-
-struct counter_map
-{
-       enum {
-               COUNTER_MAP_NOMAP,
-               COUNTER_MAP_NORMAL_MAP,
-               COUNTER_MAP_ZEROED,
-               COUNTER_MAP_SET
-       } maptype;
-       unsigned int mappos;
-};
-
-enum iptcc_rule_type {
-       IPTCC_R_STANDARD,               /* standard target (ACCEPT, ...) */
-       IPTCC_R_MODULE,                 /* extension module (SNAT, ...) */
-       IPTCC_R_FALLTHROUGH,            /* fallthrough rule */
-       IPTCC_R_JUMP,                   /* jump to other chain */
-};
-
-struct rule_head
-{
-       struct list_head list;
-       struct chain_head *chain;
-       struct counter_map counter_map;
-
-       unsigned int index;             /* index (needed for counter_map) */
-       unsigned int offset;            /* offset in rule blob */
-
-       enum iptcc_rule_type type;
-       struct chain_head *jump;        /* jump target, if IPTCC_R_JUMP */
-
-       unsigned int size;              /* size of entry data */
-       STRUCT_ENTRY entry[0];
-};
-
-struct chain_head
-{
-       struct list_head list;
-       char name[TABLE_MAXNAMELEN];
-       unsigned int hooknum;           /* hook number+1 if builtin */
-       unsigned int references;        /* how many jumps reference us */
-       int verdict;                    /* verdict if builtin */
-
-       STRUCT_COUNTERS counters;       /* per-chain counters */
-       struct counter_map counter_map;
-
-       unsigned int num_rules;         /* number of rules in list */
-       struct list_head rules;         /* list of rules */
-
-       unsigned int index;             /* index (needed for jump resolval) */
-       unsigned int head_offset;       /* offset in rule blob */
-       unsigned int foot_index;        /* index (needed for counter_map) */
-       unsigned int foot_offset;       /* offset in rule blob */
-};
-
-STRUCT_TC_HANDLE
-{
-       int changed;                     /* Have changes been made? */
-
-       struct list_head chains;
-       
-       struct chain_head *chain_iterator_cur;
-       struct rule_head *rule_iterator_cur;
-
-       unsigned int num_chains;         /* number of user defined chains */
-
-       struct chain_head **chain_index;   /* array for fast chain list access*/
-       unsigned int        chain_index_sz;/* size of chain index array */
-
-       STRUCT_GETINFO info;
-       STRUCT_GET_ENTRIES *entries;
-};
-
-/* allocate a new chain head for the cache */
-static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum)
-{
-       struct chain_head *c = malloc(sizeof(*c));
-       if (!c)
-               return NULL;
-       memset(c, 0, sizeof(*c));
-
-       strncpy(c->name, name, TABLE_MAXNAMELEN);
-       c->hooknum = hooknum;
-       INIT_LIST_HEAD(&c->rules);
-
-       return c;
-}
-
-/* allocate and initialize a new rule for the cache */
-static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int size)
-{
-       struct rule_head *r = malloc(sizeof(*r)+size);
-       if (!r)
-               return NULL;
-       memset(r, 0, sizeof(*r));
-
-       r->chain = c;
-       r->size = size;
-
-       return r;
-}
-
-/* notify us that the ruleset has been modified by the user */
-static inline void
-set_changed(TC_HANDLE_T h)
-{
-       h->changed = 1;
-}
-
-#ifdef IPTC_DEBUG
-static void do_check(TC_HANDLE_T h, unsigned int line);
-#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
-#else
-#define CHECK(h)
-#endif
-
-
-/**********************************************************************
- * iptc blob utility functions (iptcb_*)
- **********************************************************************/
-
-static inline int
-iptcb_get_number(const STRUCT_ENTRY *i,
-          const STRUCT_ENTRY *seek,
-          unsigned int *pos)
-{
-       if (i == seek)
-               return 1;
-       (*pos)++;
-       return 0;
-}
-
-static inline int
-iptcb_get_entry_n(STRUCT_ENTRY *i,
-           unsigned int number,
-           unsigned int *pos,
-           STRUCT_ENTRY **pe)
-{
-       if (*pos == number) {
-               *pe = i;
-               return 1;
-       }
-       (*pos)++;
-       return 0;
-}
-
-static inline STRUCT_ENTRY *
-iptcb_get_entry(TC_HANDLE_T h, unsigned int offset)
-{
-       return (STRUCT_ENTRY *)((char *)h->entries->entrytable + offset);
-}
-
-static unsigned int
-iptcb_entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
-{
-       unsigned int pos = 0;
-
-       if (ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
-                         iptcb_get_number, seek, &pos) == 0) {
-               fprintf(stderr, "ERROR: offset %u not an entry!\n",
-                       (unsigned int)((char *)seek - (char *)h->entries->entrytable));
-               abort();
-       }
-       return pos;
-}
-
-static inline STRUCT_ENTRY *
-iptcb_offset2entry(TC_HANDLE_T h, unsigned int offset)
-{
-       return (STRUCT_ENTRY *) ((void *)h->entries->entrytable+offset);
-}
-
-
-static inline unsigned long
-iptcb_entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
-{
-       return (void *)e - (void *)h->entries->entrytable;
-}
-
-static inline unsigned int
-iptcb_offset2index(const TC_HANDLE_T h, unsigned int offset)
-{
-       return iptcb_entry2index(h, iptcb_offset2entry(h, offset));
-}
-
-/* Returns 0 if not hook entry, else hooknumber + 1 */
-static inline unsigned int
-iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
-{
-       unsigned int i;
-
-       for (i = 0; i < NUMHOOKS; i++) {
-               if ((h->info.valid_hooks & (1 << i))
-                   && iptcb_get_entry(h, h->info.hook_entry[i]) == e)
-                       return i+1;
-       }
-       return 0;
-}
-
-
-/**********************************************************************
- * Chain index (cache utility) functions
- **********************************************************************
- * The chain index is an array with pointers into the chain list, with
- * CHAIN_INDEX_BUCKET_LEN spacing.  This facilitates the ability to
- * speedup chain list searching, by find a more optimal starting
- * points when searching the linked list.
- *
- * The starting point can be found fast by using a binary search of
- * the chain index. Thus, reducing the previous search complexity of
- * O(n) to O(log(n/k) + k) where k is CHAIN_INDEX_BUCKET_LEN.
- *
- * A nice property of the chain index, is that the "bucket" list
- * length is max CHAIN_INDEX_BUCKET_LEN (when just build, inserts will
- * change this). Oppose to hashing, where the "bucket" list length can
- * vary a lot.
- */
-#ifndef CHAIN_INDEX_BUCKET_LEN
-#define CHAIN_INDEX_BUCKET_LEN 40
-#endif
-
-/* Another nice property of the chain index is that inserting/creating
- * chains in chain list don't change the correctness of the chain
- * index, it only causes longer lists in the buckets.
- *
- * To mitigate the performance penalty of longer bucket lists and the
- * penalty of rebuilding, the chain index is rebuild only when
- * CHAIN_INDEX_INSERT_MAX chains has been added.
- */
-#ifndef CHAIN_INDEX_INSERT_MAX
-#define CHAIN_INDEX_INSERT_MAX 355
-#endif
-
-static inline unsigned int iptcc_is_builtin(struct chain_head *c);
-
-
-/* Use binary search in the chain index array, to find a chain_head
- * pointer closest to the place of the searched name element.
- *
- * Notes that, binary search (obviously) requires that the chain list
- * is sorted by name.
- */
-static struct list_head *
-iptcc_bsearch_chain_index(const char *name, unsigned int *idx, TC_HANDLE_T handle)
-{
-       unsigned int pos, end;
-       int res;
-
-       struct list_head *list_pos;
-       list_pos=&handle->chains;
-
-       /* Check for empty array, e.g. no user defined chains */
-       if (handle->chain_index_sz == 0) {
-               debug("WARNING: handle->chain_index_sz == 0\n");
-               return list_pos;
-       }
-
-       /* Init */
-       end = handle->chain_index_sz;
-       pos = end / 2;
-
-       debug("bsearch Find chain:%s (pos:%d end:%d)\n", name, pos, end);
-
-       /* Loop */
- loop:
-       if (!handle->chain_index[pos]) {
-               fprintf(stderr, "ERROR: NULL pointer chain_index[%d]\n", pos);
-               return &handle->chains; /* Be safe, return orig start pos */
-       }
-
-       res = strcmp(name, handle->chain_index[pos]->name);
-       list_pos = &handle->chain_index[pos]->list;
-       *idx = pos;
-
-       debug("bsearch Index[%d] name:%s res:%d ",
-             pos, handle->chain_index[pos]->name, res);
-
-       if (res == 0) { /* Found element, by direct hit */
-               debug("[found] Direct hit pos:%d end:%d\n", pos, end);
-               return list_pos;
-       } else if (res < 0) { /* Too far, jump back */
-               end = pos;
-               pos = pos / 2;
-
-               /* Exit case: First element of array */
-               if (end == 0) {
-                       debug("[found] Reached first array elem (end%d)\n",end);
-                       return list_pos;
-               }
-               debug("jump back to pos:%d (end:%d)\n", pos, end);
-               goto loop;
-       } else if (res > 0 ){ /* Not far enough, jump forward */
-
-               /* Exit case: Last element of array */
-               if (pos == handle->chain_index_sz-1) {
-                       debug("[found] Last array elem (end:%d)\n", end);
-                       return list_pos;
-               }
-
-               /* Exit case: Next index less, thus elem in this list section */
-               res = strcmp(name, handle->chain_index[pos+1]->name);
-               if (res < 0) {
-                       debug("[found] closest list (end:%d)\n", end);
-                       return list_pos;
-               }
-
-               pos = (pos+end)/2;
-               debug("jump forward to pos:%d (end:%d)\n", pos, end);
-               goto loop;
-       }
-
-       return list_pos;
-}
-
-#ifdef DEBUG
-/* Trivial linear search of chain index. Function used for verifying
-   the output of bsearch function */
-static struct list_head *
-iptcc_linearly_search_chain_index(const char *name, TC_HANDLE_T handle)
-{
-       unsigned int i=0;
-       int res=0;
-
-       struct list_head *list_pos;
-       list_pos = &handle->chains;
-
-       if (handle->chain_index_sz)
-               list_pos = &handle->chain_index[0]->list;
-
-       /* Linearly walk of chain index array */
-
-       for (i=0; i < handle->chain_index_sz; i++) {
-               if (handle->chain_index[i]) {
-                       res = strcmp(handle->chain_index[i]->name, name);
-                       if (res > 0)
-                               break; // One step too far
-                       list_pos = &handle->chain_index[i]->list;
-                       if (res == 0)
-                               break; // Direct hit
-               }
-       }
-
-       return list_pos;
-}
-#endif
-
-static int iptcc_chain_index_alloc(TC_HANDLE_T h)
-{
-       unsigned int list_length = CHAIN_INDEX_BUCKET_LEN;
-       unsigned int array_elems;
-       unsigned int array_mem;
-
-       /* Allocate memory for the chain index array */
-       array_elems = (h->num_chains / list_length) +
-                      (h->num_chains % list_length ? 1 : 0);
-       array_mem   = sizeof(h->chain_index) * array_elems;
-
-       debug("Alloc Chain index, elems:%d mem:%d bytes\n",
-             array_elems, array_mem);
-
-       h->chain_index = malloc(array_mem);
-       if (!h->chain_index) {
-               h->chain_index_sz = 0;
-               return -ENOMEM;
-       }
-       memset(h->chain_index, 0, array_mem);
-       h->chain_index_sz = array_elems;
-
-       return 1;
-}
-
-static void iptcc_chain_index_free(TC_HANDLE_T h)
-{
-       h->chain_index_sz = 0;
-       free(h->chain_index);
-}
-
-
-#ifdef DEBUG
-static void iptcc_chain_index_dump(TC_HANDLE_T h)
-{
-       unsigned int i = 0;
-
-       /* Dump: contents of chain index array */
-       for (i=0; i < h->chain_index_sz; i++) {
-               if (h->chain_index[i]) {
-                       fprintf(stderr, "Chain index[%d].name: %s\n",
-                               i, h->chain_index[i]->name);
-               }
-       }
-}
-#endif
-
-/* Build the chain index */
-static int iptcc_chain_index_build(TC_HANDLE_T h)
-{
-       unsigned int list_length = CHAIN_INDEX_BUCKET_LEN;
-       unsigned int chains = 0;
-       unsigned int cindex = 0;
-       struct chain_head *c;
-
-       /* Build up the chain index array here */
-       debug("Building chain index\n");
-
-       debug("Number of user defined chains:%d bucket_sz:%d array_sz:%d\n",
-               h->num_chains, list_length, h->chain_index_sz);
-
-       if (h->chain_index_sz == 0)
-               return 0;
-
-       list_for_each_entry(c, &h->chains, list) {
-
-               /* Issue: The index array needs to start after the
-                * builtin chains, as they are not sorted */
-               if (!iptcc_is_builtin(c)) {
-                       cindex=chains / list_length;
-
-                       /* Safe guard, break out on array limit, this
-                        * is useful if chains are added and array is
-                        * rebuild, without realloc of memory. */
-                       if (cindex >= h->chain_index_sz)
-                               break;
-
-                       if ((chains % list_length)== 0) {
-                               debug("\nIndex[%d] Chains:", cindex);
-                               h->chain_index[cindex] = c;
-                       }
-                       chains++;
-               }
-               debug("%s, ", c->name);
-       }
-       debug("\n");
-
-       return 1;
-}
-
-static int iptcc_chain_index_rebuild(TC_HANDLE_T h)
-{
-       debug("REBUILD chain index array\n");
-       iptcc_chain_index_free(h);
-       if ((iptcc_chain_index_alloc(h)) < 0)
-               return -ENOMEM;
-       iptcc_chain_index_build(h);
-       return 1;
-}
-
-/* Delete chain (pointer) from index array.  Removing an element from
- * the chain list only affects the chain index array, if the chain
- * index points-to/uses that list pointer.
- *
- * There are different strategies, the simple and safe is to rebuild
- * the chain index every time.  The more advanced is to update the
- * array index to point to the next element, but that requires some
- * house keeping and boundry checks.  The advanced is implemented, as
- * the simple approach behaves badly when all chains are deleted
- * because list_for_each processing will always hit the first chain
- * index, thus causing a rebuild for every chain.
- */
-static int iptcc_chain_index_delete_chain(struct chain_head *c, TC_HANDLE_T h)
-{
-       struct list_head *index_ptr, *next;
-       struct chain_head *c2;
-       unsigned int idx, idx2;
-
-       index_ptr = iptcc_bsearch_chain_index(c->name, &idx, h);
-
-       debug("Del chain[%s] c->list:%p index_ptr:%p\n",
-             c->name, &c->list, index_ptr);
-
-       /* Save the next pointer */
-       next = c->list.next;
-       list_del(&c->list);
-
-       if (index_ptr == &c->list) { /* Chain used as index ptr */
-
-               /* See if its possible to avoid a rebuild, by shifting
-                * to next pointer.  Its possible if the next pointer
-                * is located in the same index bucket.
-                */
-               c2         = list_entry(next, struct chain_head, list);
-               iptcc_bsearch_chain_index(c2->name, &idx2, h);
-               if (idx != idx2) {
-                       /* Rebuild needed */
-                       return iptcc_chain_index_rebuild(h);
-               } else {
-                       /* Avoiding rebuild */
-                       debug("Update cindex[%d] with next ptr name:[%s]\n",
-                             idx, c2->name);
-                       h->chain_index[idx]=c2;
-                       return 0;
-               }
-       }
-       return 0;
-}
-
-
-/**********************************************************************
- * iptc cache utility functions (iptcc_*)
- **********************************************************************/
-
-/* Is the given chain builtin (1) or user-defined (0) */
-static inline unsigned int iptcc_is_builtin(struct chain_head *c)
-{
-       return (c->hooknum ? 1 : 0);
-}
-
-/* Get a specific rule within a chain */
-static struct rule_head *iptcc_get_rule_num(struct chain_head *c,
-                                           unsigned int rulenum)
-{
-       struct rule_head *r;
-       unsigned int num = 0;
-
-       list_for_each_entry(r, &c->rules, list) {
-               num++;
-               if (num == rulenum)
-                       return r;
-       }
-       return NULL;
-}
-
-/* Get a specific rule within a chain backwards */
-static struct rule_head *iptcc_get_rule_num_reverse(struct chain_head *c,
-                                           unsigned int rulenum)
-{
-       struct rule_head *r;
-       unsigned int num = 0;
-
-       list_for_each_entry_reverse(r, &c->rules, list) {
-               num++;
-               if (num == rulenum)
-                       return r;
-       }
-       return NULL;
-}
-
-/* Returns chain head if found, otherwise NULL. */
-static struct chain_head *
-iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset)
-{
-       struct list_head *pos;
-
-       if (list_empty(&handle->chains))
-               return NULL;
-
-       list_for_each(pos, &handle->chains) {
-               struct chain_head *c = list_entry(pos, struct chain_head, list);
-               if (offset >= c->head_offset && offset <= c->foot_offset)
-                       return c;
-       }
-
-       return NULL;
-}
-
-/* Returns chain head if found, otherwise NULL. */
-static struct chain_head *
-iptcc_find_label(const char *name, TC_HANDLE_T handle)
-{
-       struct list_head *pos;
-       struct list_head *list_start_pos;
-       unsigned int i=0;
-       int res;
-
-       if (list_empty(&handle->chains))
-               return NULL;
-
-       /* First look at builtin chains */
-       list_for_each(pos, &handle->chains) {
-               struct chain_head *c = list_entry(pos, struct chain_head, list);
-               if (!iptcc_is_builtin(c))
-                       break;
-               if (!strcmp(c->name, name))
-                       return c;
-       }
-
-       /* Find a smart place to start the search via chain index */
-       //list_start_pos = iptcc_linearly_search_chain_index(name, handle);
-       list_start_pos = iptcc_bsearch_chain_index(name, &i, handle);
-
-       /* Handel if bsearch bails out early */
-       if (list_start_pos == &handle->chains) {
-               list_start_pos = pos;
-       }
-#ifdef DEBUG
-       else {
-               /* Verify result of bsearch against linearly index search */
-               struct list_head *test_pos;
-               struct chain_head *test_c, *tmp_c;
-               test_pos = iptcc_linearly_search_chain_index(name, handle);
-               if (list_start_pos != test_pos) {
-                       debug("BUG in chain_index search\n");
-                       test_c=list_entry(test_pos,      struct chain_head,list);
-                       tmp_c =list_entry(list_start_pos,struct chain_head,list);
-                       debug("Verify search found:\n");
-                       debug(" Chain:%s\n", test_c->name);
-                       debug("BSearch found:\n");
-                       debug(" Chain:%s\n", tmp_c->name);
-                       exit(42);
-               }
-       }
-#endif
-
-       /* Initial/special case, no user defined chains */
-       if (handle->num_chains == 0)
-               return NULL;
-
-       /* Start searching through the chain list */
-       list_for_each(pos, list_start_pos->prev) {
-               struct chain_head *c = list_entry(pos, struct chain_head, list);
-               res = strcmp(c->name, name);
-               debug("List search name:%s == %s res:%d\n", name, c->name, res);
-               if (res==0)
-                       return c;
-
-               /* We can stop earlier as we know list is sorted */
-               if (res>0 && !iptcc_is_builtin(c)) { /* Walked too far*/
-                       debug(" Not in list, walked too far, sorted list\n");
-                       return NULL;
-               }
-
-               /* Stop on wrap around, if list head is reached */
-               if (pos == &handle->chains) {
-                       debug("Stop, list head reached\n");
-                       return NULL;
-               }
-       }
-
-       debug("List search NOT found name:%s\n", name);
-       return NULL;
-}
-
-/* called when rule is to be removed from cache */
-static void iptcc_delete_rule(struct rule_head *r)
-{
-       DEBUGP("deleting rule %p (offset %u)\n", r, r->offset);
-       /* clean up reference count of called chain */
-       if (r->type == IPTCC_R_JUMP
-           && r->jump)
-               r->jump->references--;
-
-       list_del(&r->list);
-       free(r);
-}
-
-
-/**********************************************************************
- * RULESET PARSER (blob -> cache)
- **********************************************************************/
-
-/* Delete policy rule of previous chain, since cache doesn't contain
- * chain policy rules.
- * WARNING: This function has ugly design and relies on a lot of context, only
- * to be called from specific places within the parser */
-static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num)
-{
-       const unsigned char *data;
-
-       if (h->chain_iterator_cur) {
-               /* policy rule is last rule */
-               struct rule_head *pr = (struct rule_head *)
-                       h->chain_iterator_cur->rules.prev;
-
-               /* save verdict */
-               data = GET_TARGET(pr->entry)->data;
-               h->chain_iterator_cur->verdict = *(const int *)data;
-
-               /* save counter and counter_map information */
-               h->chain_iterator_cur->counter_map.maptype = 
-                                               COUNTER_MAP_NORMAL_MAP;
-               h->chain_iterator_cur->counter_map.mappos = num-1;
-               memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters, 
-                       sizeof(h->chain_iterator_cur->counters));
-
-               /* foot_offset points to verdict rule */
-               h->chain_iterator_cur->foot_index = num;
-               h->chain_iterator_cur->foot_offset = pr->offset;
-
-               /* delete rule from cache */
-               iptcc_delete_rule(pr);
-               h->chain_iterator_cur->num_rules--;
-
-               return 1;
-       }
-       return 0;
-}
-
-/* alphabetically insert a chain into the list */
-static inline void iptc_insert_chain(TC_HANDLE_T h, struct chain_head *c)
-{
-       struct chain_head *tmp;
-       struct list_head  *list_start_pos;
-       unsigned int i=1;
-
-       /* Find a smart place to start the insert search */
-       list_start_pos = iptcc_bsearch_chain_index(c->name, &i, h);
-
-       /* Handle the case, where chain.name is smaller than index[0] */
-       if (i==0 && strcmp(c->name, h->chain_index[0]->name) <= 0) {
-               h->chain_index[0] = c; /* Update chain index head */
-               list_start_pos = h->chains.next;
-               debug("Update chain_index[0] with %s\n", c->name);
-       }
-
-       /* Handel if bsearch bails out early */
-       if (list_start_pos == &h->chains) {
-               list_start_pos = h->chains.next;
-       }
-
-       /* sort only user defined chains */
-       if (!c->hooknum) {
-               list_for_each_entry(tmp, list_start_pos->prev, list) {
-                       if (!tmp->hooknum && strcmp(c->name, tmp->name) <= 0) {
-                               list_add(&c->list, tmp->list.prev);
-                               return;
-                       }
-
-                       /* Stop if list head is reached */
-                       if (&tmp->list == &h->chains) {
-                               debug("Insert, list head reached add to tail\n");
-                               break;
-                       }
-               }
-       }
-
-       /* survived till end of list: add at tail */
-       list_add_tail(&c->list, &h->chains);
-}
-
-/* Another ugly helper function split out of cache_add_entry to make it less
- * spaghetti code */
-static void __iptcc_p_add_chain(TC_HANDLE_T h, struct chain_head *c,
-                               unsigned int offset, unsigned int *num)
-{
-       struct list_head  *tail = h->chains.prev;
-       struct chain_head *ctail;
-
-       __iptcc_p_del_policy(h, *num);
-
-       c->head_offset = offset;
-       c->index = *num;
-
-       /* Chains from kernel are already sorted, as they are inserted
-        * sorted. But there exists an issue when shifting to 1.4.0
-        * from an older version, as old versions allow last created
-        * chain to be unsorted.
-        */
-       if (iptcc_is_builtin(c)) /* Only user defined chains are sorted*/
-               list_add_tail(&c->list, &h->chains);
-       else {
-               ctail = list_entry(tail, struct chain_head, list);
-               if (strcmp(c->name, ctail->name) > 0)
-                       list_add_tail(&c->list, &h->chains);/* Already sorted*/
-               else
-                       iptc_insert_chain(h, c);/* Was not sorted */
-       }
-
-       h->chain_iterator_cur = c;
-}
-
-/* main parser function: add an entry from the blob to the cache */
-static int cache_add_entry(STRUCT_ENTRY *e, 
-                          TC_HANDLE_T h, 
-                          STRUCT_ENTRY **prev,
-                          unsigned int *num)
-{
-       unsigned int builtin;
-       unsigned int offset = (char *)e - (char *)h->entries->entrytable;
-
-       DEBUGP("entering...");
-
-       /* Last entry ("policy rule"). End it.*/
-       if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) {
-               /* This is the ERROR node at the end of the chain */
-               DEBUGP_C("%u:%u: end of table:\n", *num, offset);
-
-               __iptcc_p_del_policy(h, *num);
-
-               h->chain_iterator_cur = NULL;
-               goto out_inc;
-       }
-
-       /* We know this is the start of a new chain if it's an ERROR
-        * target, or a hook entry point */
-
-       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
-               struct chain_head *c = 
-                       iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0);
-               DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset, 
-                       (char *)c->name, c);
-               if (!c) {
-                       errno = -ENOMEM;
-                       return -1;
-               }
-               h->num_chains++; /* New user defined chain */
-
-               __iptcc_p_add_chain(h, c, offset, num);
-
-       } else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) {
-               struct chain_head *c =
-                       iptcc_alloc_chain_head((char *)hooknames[builtin-1], 
-                                               builtin);
-               DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n", 
-                       *num, offset, c, &c->rules);
-               if (!c) {
-                       errno = -ENOMEM;
-                       return -1;
-               }
-
-               c->hooknum = builtin;
-
-               __iptcc_p_add_chain(h, c, offset, num);
-
-               /* FIXME: this is ugly. */
-               goto new_rule;
-       } else {
-               /* has to be normal rule */
-               struct rule_head *r;
-new_rule:
-
-               if (!(r = iptcc_alloc_rule(h->chain_iterator_cur, 
-                                          e->next_offset))) {
-                       errno = ENOMEM;
-                       return -1;
-               }
-               DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
-
-               r->index = *num;
-               r->offset = offset;
-               memcpy(r->entry, e, e->next_offset);
-               r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
-               r->counter_map.mappos = r->index;
-
-               /* handling of jumps, etc. */
-               if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) {
-                       STRUCT_STANDARD_TARGET *t;
-
-                       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
-                       if (t->target.u.target_size
-                           != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
-                               errno = EINVAL;
-                               return -1;
-                       }
-
-                       if (t->verdict < 0) {
-                               DEBUGP_C("standard, verdict=%d\n", t->verdict);
-                               r->type = IPTCC_R_STANDARD;
-                       } else if (t->verdict == r->offset+e->next_offset) {
-                               DEBUGP_C("fallthrough\n");
-                               r->type = IPTCC_R_FALLTHROUGH;
-                       } else {
-                               DEBUGP_C("jump, target=%u\n", t->verdict);
-                               r->type = IPTCC_R_JUMP;
-                               /* Jump target fixup has to be deferred
-                                * until second pass, since we migh not
-                                * yet have parsed the target */
-                       }
-               } else {
-                       DEBUGP_C("module, target=%s\n", GET_TARGET(e)->u.user.name);
-                       r->type = IPTCC_R_MODULE;
-               }
-
-               list_add_tail(&r->list, &h->chain_iterator_cur->rules);
-               h->chain_iterator_cur->num_rules++;
-       }
-out_inc:
-       (*num)++;
-       return 0;
-}
-
-
-/* parse an iptables blob into it's pieces */
-static int parse_table(TC_HANDLE_T h)
-{
-       STRUCT_ENTRY *prev;
-       unsigned int num = 0;
-       struct chain_head *c;
-
-       /* First pass: over ruleset blob */
-       ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
-                       cache_add_entry, h, &prev, &num);
-
-       /* Build the chain index, used for chain list search speedup */
-       if ((iptcc_chain_index_alloc(h)) < 0)
-               return -ENOMEM;
-       iptcc_chain_index_build(h);
-
-       /* Second pass: fixup parsed data from first pass */
-       list_for_each_entry(c, &h->chains, list) {
-               struct rule_head *r;
-               list_for_each_entry(r, &c->rules, list) {
-                       struct chain_head *lc;
-                       STRUCT_STANDARD_TARGET *t;
-
-                       if (r->type != IPTCC_R_JUMP)
-                               continue;
-
-                       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
-                       lc = iptcc_find_chain_by_offset(h, t->verdict);
-                       if (!lc)
-                               return -1;
-                       r->jump = lc;
-                       lc->references++;
-               }
-       }
-
-       /* FIXME: sort chains */
-
-       return 1;
-}
-
-
-/**********************************************************************
- * RULESET COMPILATION (cache -> blob)
- **********************************************************************/
-
-/* Convenience structures */
-struct iptcb_chain_start{
-       STRUCT_ENTRY e;
-       struct ipt_error_target name;
-};
-#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) +                 \
-                                ALIGN(sizeof(struct ipt_error_target)))
-
-struct iptcb_chain_foot {
-       STRUCT_ENTRY e;
-       STRUCT_STANDARD_TARGET target;
-};
-#define IPTCB_CHAIN_FOOT_SIZE  (sizeof(STRUCT_ENTRY) +                 \
-                                ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
-
-struct iptcb_chain_error {
-       STRUCT_ENTRY entry;
-       struct ipt_error_target target;
-};
-#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) +                 \
-                                ALIGN(sizeof(struct ipt_error_target)))
-
-
-
-/* compile rule from cache into blob */
-static inline int iptcc_compile_rule (TC_HANDLE_T h, STRUCT_REPLACE *repl, struct rule_head *r)
-{
-       /* handle jumps */
-       if (r->type == IPTCC_R_JUMP) {
-               STRUCT_STANDARD_TARGET *t;
-               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
-               /* memset for memcmp convenience on delete/replace */
-               memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
-               strcpy(t->target.u.user.name, STANDARD_TARGET);
-               /* Jumps can only happen to builtin chains, so we
-                * can safely assume that they always have a header */
-               t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
-       } else if (r->type == IPTCC_R_FALLTHROUGH) {
-               STRUCT_STANDARD_TARGET *t;
-               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
-               t->verdict = r->offset + r->size;
-       }
-       
-       /* copy entry from cache to blob */
-       memcpy((char *)repl->entries+r->offset, r->entry, r->size);
-
-       return 1;
-}
-
-/* compile chain from cache into blob */
-static int iptcc_compile_chain(TC_HANDLE_T h, STRUCT_REPLACE *repl, struct chain_head *c)
-{
-       int ret;
-       struct rule_head *r;
-       struct iptcb_chain_start *head;
-       struct iptcb_chain_foot *foot;
-
-       /* only user-defined chains have heaer */
-       if (!iptcc_is_builtin(c)) {
-               /* put chain header in place */
-               head = (void *)repl->entries + c->head_offset;
-               head->e.target_offset = sizeof(STRUCT_ENTRY);
-               head->e.next_offset = IPTCB_CHAIN_START_SIZE;
-               strcpy(head->name.t.u.user.name, ERROR_TARGET);
-               head->name.t.u.target_size = 
-                               ALIGN(sizeof(struct ipt_error_target));
-               strcpy(head->name.error, c->name);
-       } else {
-               repl->hook_entry[c->hooknum-1] = c->head_offset;        
-               repl->underflow[c->hooknum-1] = c->foot_offset;
-       }
-
-       /* iterate over rules */
-       list_for_each_entry(r, &c->rules, list) {
-               ret = iptcc_compile_rule(h, repl, r);
-               if (ret < 0)
-                       return ret;
-       }
-
-       /* put chain footer in place */
-       foot = (void *)repl->entries + c->foot_offset;
-       foot->e.target_offset = sizeof(STRUCT_ENTRY);
-       foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
-       strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
-       foot->target.target.u.target_size =
-                               ALIGN(sizeof(STRUCT_STANDARD_TARGET));
-       /* builtin targets have verdict, others return */
-       if (iptcc_is_builtin(c))
-               foot->target.verdict = c->verdict;
-       else
-               foot->target.verdict = RETURN;
-       /* set policy-counters */
-       memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
-
-       return 0;
-}
-
-/* calculate offset and number for every rule in the cache */
-static int iptcc_compile_chain_offsets(TC_HANDLE_T h, struct chain_head *c,
-                                      unsigned int *offset, unsigned int *num)
-{
-       struct rule_head *r;
-
-       c->head_offset = *offset;
-       DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset);
-
-       if (!iptcc_is_builtin(c))  {
-               /* Chain has header */
-               *offset += sizeof(STRUCT_ENTRY) 
-                            + ALIGN(sizeof(struct ipt_error_target));
-               (*num)++;
-       }
-
-       list_for_each_entry(r, &c->rules, list) {
-               DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num);
-               r->offset = *offset;
-               r->index = *num;
-               *offset += r->size;
-               (*num)++;
-       }
-
-       DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num, 
-               *offset, *num);
-       c->foot_offset = *offset;
-       c->foot_index = *num;
-       *offset += sizeof(STRUCT_ENTRY)
-                  + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
-       (*num)++;
-
-       return 1;
-}
-
-/* put the pieces back together again */
-static int iptcc_compile_table_prep(TC_HANDLE_T h, unsigned int *size)
-{
-       struct chain_head *c;
-       unsigned int offset = 0, num = 0;
-       int ret = 0;
-
-       /* First pass: calculate offset for every rule */
-       list_for_each_entry(c, &h->chains, list) {
-               ret = iptcc_compile_chain_offsets(h, c, &offset, &num);
-               if (ret < 0)
-                       return ret;
-       }
-
-       /* Append one error rule at end of chain */
-       num++;
-       offset += sizeof(STRUCT_ENTRY)
-                 + ALIGN(sizeof(struct ipt_error_target));
-
-       /* ruleset size is now in offset */
-       *size = offset;
-       return num;
-}
-
-static int iptcc_compile_table(TC_HANDLE_T h, STRUCT_REPLACE *repl)
-{
-       struct chain_head *c;
-       struct iptcb_chain_error *error;
-
-       /* Second pass: copy from cache to offsets, fill in jumps */
-       list_for_each_entry(c, &h->chains, list) {
-               int ret = iptcc_compile_chain(h, repl, c);
-               if (ret < 0)
-                       return ret;
-       }
-
-       /* Append error rule at end of chain */
-       error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
-       error->entry.target_offset = sizeof(STRUCT_ENTRY);
-       error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
-       error->target.t.u.user.target_size = 
-               ALIGN(sizeof(struct ipt_error_target));
-       strcpy((char *)&error->target.t.u.user.name, ERROR_TARGET);
-       strcpy((char *)&error->target.error, "ERROR");
-
-       return 1;
-}
-
-/**********************************************************************
- * EXTERNAL API (operates on cache only)
- **********************************************************************/
-
-/* Allocate handle of given size */
-static TC_HANDLE_T
-alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
-{
-       TC_HANDLE_T h;
-
-
-       h = malloc(sizeof(STRUCT_TC_HANDLE));
-       if (!h) {
-               errno = ENOMEM;
-               return NULL;
-       }
-       memset(h, 0, sizeof(*h));
-       INIT_LIST_HEAD(&h->chains);
-       strcpy(h->info.name, tablename);
-
-       h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + size);
-       if (!h->entries)
-               goto out_free_handle;
-
-       strcpy(h->entries->name, tablename);
-       h->entries->size = size;
-
-       return h;
-
-out_free_handle:
-       free(h);
-
-       return NULL;
-}
-
-
-TC_HANDLE_T
-TC_INIT(const char *tablename)
-{
-       TC_HANDLE_T h;
-       STRUCT_GETINFO info;
-       unsigned int tmp;
-       socklen_t s;
-
-       iptc_fn = TC_INIT;
-
-       if (strlen(tablename) >= TABLE_MAXNAMELEN) {
-               errno = EINVAL;
-               return NULL;
-       }
-       
-       if (sockfd_use == 0) {
-               sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
-               if (sockfd < 0)
-                       return NULL;
-       }
-       sockfd_use++;
-retry:
-       s = sizeof(info);
-
-       strcpy(info.name, tablename);
-       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) {
-               if (--sockfd_use == 0) {
-                       close(sockfd);
-                       sockfd = -1;
-               }
-               return NULL;
-       }
-
-       DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
-               info.valid_hooks, info.num_entries, info.size);
-
-       if ((h = alloc_handle(info.name, info.size, info.num_entries))
-           == NULL) {
-               if (--sockfd_use == 0) {
-                       close(sockfd);
-                       sockfd = -1;
-               }
-               return NULL;
-       }
-
-       /* Initialize current state */
-       h->info = info;
-
-       h->entries->size = h->info.size;
-
-       tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
-
-       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
-                      &tmp) < 0)
-               goto error;
-
-#ifdef IPTC_DEBUG2
-       {
-               int fd = open("/tmp/libiptc-so_get_entries.blob", 
-                               O_CREAT|O_WRONLY);
-               if (fd >= 0) {
-                       write(fd, h->entries, tmp);
-                       close(fd);
-               }
-       }
-#endif
-
-       if (parse_table(h) < 0)
-               goto error;
-
-       CHECK(h);
-       return h;
-error:
-       TC_FREE(&h);
-       /* A different process changed the ruleset size, retry */
-       if (errno == EAGAIN)
-               goto retry;
-       return NULL;
-}
-
-void
-TC_FREE(TC_HANDLE_T *h)
-{
-       struct chain_head *c, *tmp;
-
-       iptc_fn = TC_FREE;
-       if (--sockfd_use == 0) {
-               close(sockfd);
-               sockfd = -1;
-       }
-
-       list_for_each_entry_safe(c, tmp, &(*h)->chains, list) {
-               struct rule_head *r, *rtmp;
-
-               list_for_each_entry_safe(r, rtmp, &c->rules, list) {
-                       free(r);
-               }
-
-               free(c);
-       }
-
-       iptcc_chain_index_free(*h);
-
-       free((*h)->entries);
-       free(*h);
-
-       *h = NULL;
-}
-
-static inline int
-print_match(const STRUCT_ENTRY_MATCH *m)
-{
-       printf("Match name: `%s'\n", m->u.user.name);
-       return 0;
-}
-
-static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
-void
-TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
-{
-       iptc_fn = TC_DUMP_ENTRIES;
-       CHECK(handle);
-
-       printf("libiptc v%s. %u bytes.\n",
-              XTABLES_VERSION, handle->entries->size);
-       printf("Table `%s'\n", handle->info.name);
-       printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
-              handle->info.hook_entry[HOOK_PRE_ROUTING],
-              handle->info.hook_entry[HOOK_LOCAL_IN],
-              handle->info.hook_entry[HOOK_FORWARD],
-              handle->info.hook_entry[HOOK_LOCAL_OUT],
-              handle->info.hook_entry[HOOK_POST_ROUTING]);
-       printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
-              handle->info.underflow[HOOK_PRE_ROUTING],
-              handle->info.underflow[HOOK_LOCAL_IN],
-              handle->info.underflow[HOOK_FORWARD],
-              handle->info.underflow[HOOK_LOCAL_OUT],
-              handle->info.underflow[HOOK_POST_ROUTING]);
-
-       ENTRY_ITERATE(handle->entries->entrytable, handle->entries->size,
-                     dump_entry, handle);
-}
-
-/* Does this chain exist? */
-int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
-{
-       iptc_fn = TC_IS_CHAIN;
-       return iptcc_find_label(chain, handle) != NULL;
-}
-
-static void iptcc_chain_iterator_advance(TC_HANDLE_T handle)
-{
-       struct chain_head *c = handle->chain_iterator_cur;
-
-       if (c->list.next == &handle->chains)
-               handle->chain_iterator_cur = NULL;
-       else
-               handle->chain_iterator_cur = 
-                       list_entry(c->list.next, struct chain_head, list);
-}
-
-/* Iterator functions to run through the chains. */
-const char *
-TC_FIRST_CHAIN(TC_HANDLE_T *handle)
-{
-       struct chain_head *c = list_entry((*handle)->chains.next,
-                                         struct chain_head, list);
-
-       iptc_fn = TC_FIRST_CHAIN;
-
-
-       if (list_empty(&(*handle)->chains)) {
-               DEBUGP(": no chains\n");
-               return NULL;
-       }
-
-       (*handle)->chain_iterator_cur = c;
-       iptcc_chain_iterator_advance(*handle);
-
-       DEBUGP(": returning `%s'\n", c->name);
-       return c->name;
-}
-
-/* Iterator functions to run through the chains.  Returns NULL at end. */
-const char *
-TC_NEXT_CHAIN(TC_HANDLE_T *handle)
-{
-       struct chain_head *c = (*handle)->chain_iterator_cur;
-
-       iptc_fn = TC_NEXT_CHAIN;
-
-       if (!c) {
-               DEBUGP(": no more chains\n");
-               return NULL;
-       }
-
-       iptcc_chain_iterator_advance(*handle);
-       
-       DEBUGP(": returning `%s'\n", c->name);
-       return c->name;
-}
-
-/* Get first rule in the given chain: NULL for empty chain. */
-const STRUCT_ENTRY *
-TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r;
-
-       iptc_fn = TC_FIRST_RULE;
-
-       DEBUGP("first rule(%s): ", chain);
-
-       c = iptcc_find_label(chain, *handle);
-       if (!c) {
-               errno = ENOENT;
-               return NULL;
-       }
-
-       /* Empty chain: single return/policy rule */
-       if (list_empty(&c->rules)) {
-               DEBUGP_C("no rules, returning NULL\n");
-               return NULL;
-       }
-
-       r = list_entry(c->rules.next, struct rule_head, list);
-       (*handle)->rule_iterator_cur = r;
-       DEBUGP_C("%p\n", r);
-
-       return r->entry;
-}
-
-/* Returns NULL when rules run out. */
-const STRUCT_ENTRY *
-TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
-{
-       struct rule_head *r;
-
-       iptc_fn = TC_NEXT_RULE;
-       DEBUGP("rule_iterator_cur=%p...", (*handle)->rule_iterator_cur);
-
-       if (!(*handle)->rule_iterator_cur) {
-               DEBUGP_C("returning NULL\n");
-               return NULL;
-       }
-       
-       r = list_entry((*handle)->rule_iterator_cur->list.next, 
-                       struct rule_head, list);
-
-       iptc_fn = TC_NEXT_RULE;
-
-       DEBUGP_C("next=%p, head=%p...", &r->list, 
-               &(*handle)->rule_iterator_cur->chain->rules);
-
-       if (&r->list == &(*handle)->rule_iterator_cur->chain->rules) {
-               (*handle)->rule_iterator_cur = NULL;
-               DEBUGP_C("finished, returning NULL\n");
-               return NULL;
-       }
-
-       (*handle)->rule_iterator_cur = r;
-
-       /* NOTE: prev is without any influence ! */
-       DEBUGP_C("returning rule %p\n", r);
-       return r->entry;
-}
-
-/* How many rules in this chain? */
-#if 0
-static unsigned int
-TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       iptc_fn = TC_NUM_RULES;
-       CHECK(*handle);
-
-       c = iptcc_find_label(chain, *handle);
-       if (!c) {
-               errno = ENOENT;
-               return (unsigned int)-1;
-       }
-       
-       return c->num_rules;
-}
-#endif
-
-#if 0
-static const STRUCT_ENTRY *
-TC_GET_RULE(const char *chain, unsigned int n, TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r;
-       
-       iptc_fn = TC_GET_RULE;
-
-       CHECK(*handle);
-
-       c = iptcc_find_label(chain, *handle);
-       if (!c) {
-               errno = ENOENT;
-               return NULL;
-       }
-
-       r = iptcc_get_rule_num(c, n);
-       if (!r)
-               return NULL;
-       return r->entry;
-}
-#endif
-
-/* Returns a pointer to the target name of this position. */
-static const char *standard_target_map(int verdict)
-{
-       switch (verdict) {
-               case RETURN:
-                       return LABEL_RETURN;
-                       break;
-               case -NF_ACCEPT-1:
-                       return LABEL_ACCEPT;
-                       break;
-               case -NF_DROP-1:
-                       return LABEL_DROP;
-                       break;
-               case -NF_QUEUE-1:
-                       return LABEL_QUEUE;
-                       break;
-               default:
-                       fprintf(stderr, "ERROR: %d not a valid target)\n",
-                               verdict);
-                       abort();
-                       break;
-       }
-       /* not reached */
-       return NULL;
-}
-
-/* Returns a pointer to the target name of this position. */
-const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
-                         TC_HANDLE_T *handle)
-{
-       STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
-       struct rule_head *r = container_of(e, struct rule_head, entry[0]);
-       const unsigned char *data;
-
-       iptc_fn = TC_GET_TARGET;
-
-       switch(r->type) {
-               int spos;
-               case IPTCC_R_FALLTHROUGH:
-                       return "";
-                       break;
-               case IPTCC_R_JUMP:
-                       DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name);
-                       return r->jump->name;
-                       break;
-               case IPTCC_R_STANDARD:
-                       data = GET_TARGET(e)->data;
-                       spos = *(const int *)data;
-                       DEBUGP("r=%p, spos=%d'\n", r, spos);
-                       return standard_target_map(spos);
-                       break;
-               case IPTCC_R_MODULE:
-                       return GET_TARGET(e)->u.user.name;
-                       break;
-       }
-       return NULL;
-}
-/* Is this a built-in chain?  Actually returns hook + 1. */
-int
-TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
-{
-       struct chain_head *c;
-       
-       iptc_fn = TC_BUILTIN;
-
-       c = iptcc_find_label(chain, handle);
-       if (!c) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       return iptcc_is_builtin(c);
-}
-
-/* Get the policy of a given built-in chain */
-const char *
-TC_GET_POLICY(const char *chain,
-             STRUCT_COUNTERS *counters,
-             TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-
-       iptc_fn = TC_GET_POLICY;
-
-       DEBUGP("called for chain %s\n", chain);
-
-       c = iptcc_find_label(chain, *handle);
-       if (!c) {
-               errno = ENOENT;
-               return NULL;
-       }
-
-       if (!iptcc_is_builtin(c))
-               return NULL;
-
-       *counters = c->counters;
-
-       return standard_target_map(c->verdict);
-}
-
-static int
-iptcc_standard_map(struct rule_head *r, int verdict)
-{
-       STRUCT_ENTRY *e = r->entry;
-       STRUCT_STANDARD_TARGET *t;
-
-       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
-
-       if (t->target.u.target_size
-           != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
-               errno = EINVAL;
-               return 0;
-       }
-       /* memset for memcmp convenience on delete/replace */
-       memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
-       strcpy(t->target.u.user.name, STANDARD_TARGET);
-       t->verdict = verdict;
-
-       r->type = IPTCC_R_STANDARD;
-
-       return 1;
-}
-
-static int
-iptcc_map_target(const TC_HANDLE_T handle,
-          struct rule_head *r)
-{
-       STRUCT_ENTRY *e = r->entry;
-       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
-
-       /* Maybe it's empty (=> fall through) */
-       if (strcmp(t->u.user.name, "") == 0) {
-               r->type = IPTCC_R_FALLTHROUGH;
-               return 1;
-       }
-       /* Maybe it's a standard target name... */
-       else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
-               return iptcc_standard_map(r, -NF_ACCEPT - 1);
-       else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
-               return iptcc_standard_map(r, -NF_DROP - 1);
-       else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
-               return iptcc_standard_map(r, -NF_QUEUE - 1);
-       else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
-               return iptcc_standard_map(r, RETURN);
-       else if (TC_BUILTIN(t->u.user.name, handle)) {
-               /* Can't jump to builtins. */
-               errno = EINVAL;
-               return 0;
-       } else {
-               /* Maybe it's an existing chain name. */
-               struct chain_head *c;
-               DEBUGP("trying to find chain `%s': ", t->u.user.name);
-
-               c = iptcc_find_label(t->u.user.name, handle);
-               if (c) {
-                       DEBUGP_C("found!\n");
-                       r->type = IPTCC_R_JUMP;
-                       r->jump = c;
-                       c->references++;
-                       return 1;
-               }
-               DEBUGP_C("not found :(\n");
-       }
-
-       /* Must be a module?  If not, kernel will reject... */
-       /* memset to all 0 for your memcmp convenience: don't clear version */
-       memset(t->u.user.name + strlen(t->u.user.name),
-              0,
-              FUNCTION_MAXNAMELEN - 1 - strlen(t->u.user.name));
-       r->type = IPTCC_R_MODULE;
-       set_changed(handle);
-       return 1;
-}
-
-/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
-int
-TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
-               const STRUCT_ENTRY *e,
-               unsigned int rulenum,
-               TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r;
-       struct list_head *prev;
-
-       iptc_fn = TC_INSERT_ENTRY;
-
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       /* first rulenum index = 0
-          first c->num_rules index = 1 */
-       if (rulenum > c->num_rules) {
-               errno = E2BIG;
-               return 0;
-       }
-
-       /* If we are inserting at the end just take advantage of the
-          double linked list, insert will happen before the entry
-          prev points to. */
-       if (rulenum == c->num_rules) {
-               prev = &c->rules;
-       } else if (rulenum + 1 <= c->num_rules/2) {
-               r = iptcc_get_rule_num(c, rulenum + 1);
-               prev = &r->list;
-       } else {
-               r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
-               prev = &r->list;
-       }
-
-       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
-               errno = ENOMEM;
-               return 0;
-       }
-
-       memcpy(r->entry, e, e->next_offset);
-       r->counter_map.maptype = COUNTER_MAP_SET;
-
-       if (!iptcc_map_target(*handle, r)) {
-               free(r);
-               return 0;
-       }
-
-       list_add_tail(&r->list, prev);
-       c->num_rules++;
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-/* Atomically replace rule `rulenum' in `chain' with `fw'. */
-int
-TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
-                const STRUCT_ENTRY *e,
-                unsigned int rulenum,
-                TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r, *old;
-
-       iptc_fn = TC_REPLACE_ENTRY;
-
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       if (rulenum >= c->num_rules) {
-               errno = E2BIG;
-               return 0;
-       }
-
-       /* Take advantage of the double linked list if possible. */
-       if (rulenum + 1 <= c->num_rules/2) {
-               old = iptcc_get_rule_num(c, rulenum + 1);
-       } else {
-               old = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
-       }
-
-       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
-               errno = ENOMEM;
-               return 0;
-       }
-
-       memcpy(r->entry, e, e->next_offset);
-       r->counter_map.maptype = COUNTER_MAP_SET;
-
-       if (!iptcc_map_target(*handle, r)) {
-               free(r);
-               return 0;
-       }
-
-       list_add(&r->list, &old->list);
-       iptcc_delete_rule(old);
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-/* Append entry `fw' to chain `chain'.  Equivalent to insert with
-   rulenum = length of chain. */
-int
-TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
-               const STRUCT_ENTRY *e,
-               TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r;
-
-       iptc_fn = TC_APPEND_ENTRY;
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               DEBUGP("unable to find chain `%s'\n", chain);
-               errno = ENOENT;
-               return 0;
-       }
-
-       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
-               DEBUGP("unable to allocate rule for chain `%s'\n", chain);
-               errno = ENOMEM;
-               return 0;
-       }
-
-       memcpy(r->entry, e, e->next_offset);
-       r->counter_map.maptype = COUNTER_MAP_SET;
-
-       if (!iptcc_map_target(*handle, r)) {
-               DEBUGP("unable to map target of rule for chain `%s'\n", chain);
-               free(r);
-               return 0;
-       }
-
-       list_add_tail(&r->list, &c->rules);
-       c->num_rules++;
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-static inline int
-match_different(const STRUCT_ENTRY_MATCH *a,
-               const unsigned char *a_elems,
-               const unsigned char *b_elems,
-               unsigned char **maskptr)
-{
-       const STRUCT_ENTRY_MATCH *b;
-       unsigned int i;
-
-       /* Offset of b is the same as a. */
-       b = (void *)b_elems + ((unsigned char *)a - a_elems);
-
-       if (a->u.match_size != b->u.match_size)
-               return 1;
-
-       if (strcmp(a->u.user.name, b->u.user.name) != 0)
-               return 1;
-
-       *maskptr += ALIGN(sizeof(*a));
-
-       for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
-               if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
-                       return 1;
-       *maskptr += i;
-       return 0;
-}
-
-static inline int
-target_same(struct rule_head *a, struct rule_head *b,const unsigned char *mask)
-{
-       unsigned int i;
-       STRUCT_ENTRY_TARGET *ta, *tb;
-
-       if (a->type != b->type)
-               return 0;
-
-       ta = GET_TARGET(a->entry);
-       tb = GET_TARGET(b->entry);
-
-       switch (a->type) {
-       case IPTCC_R_FALLTHROUGH:
-               return 1;
-       case IPTCC_R_JUMP:
-               return a->jump == b->jump;
-       case IPTCC_R_STANDARD:
-               return ((STRUCT_STANDARD_TARGET *)ta)->verdict
-                       == ((STRUCT_STANDARD_TARGET *)tb)->verdict;
-       case IPTCC_R_MODULE:
-               if (ta->u.target_size != tb->u.target_size)
-                       return 0;
-               if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
-                       return 0;
-
-               for (i = 0; i < ta->u.target_size - sizeof(*ta); i++)
-                       if (((ta->data[i] ^ tb->data[i]) & mask[i]) != 0)
-                               return 0;
-               return 1;
-       default:
-               fprintf(stderr, "ERROR: bad type %i\n", a->type);
-               abort();
-       }
-}
-
-static unsigned char *
-is_same(const STRUCT_ENTRY *a,
-       const STRUCT_ENTRY *b,
-       unsigned char *matchmask);
-
-/* Delete the first rule in `chain' which matches `fw'. */
-int
-TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
-               const STRUCT_ENTRY *origfw,
-               unsigned char *matchmask,
-               TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r, *i;
-
-       iptc_fn = TC_DELETE_ENTRY;
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       /* Create a rule_head from origfw. */
-       r = iptcc_alloc_rule(c, origfw->next_offset);
-       if (!r) {
-               errno = ENOMEM;
-               return 0;
-       }
-
-       memcpy(r->entry, origfw, origfw->next_offset);
-       r->counter_map.maptype = COUNTER_MAP_NOMAP;
-       if (!iptcc_map_target(*handle, r)) {
-               DEBUGP("unable to map target of rule for chain `%s'\n", chain);
-               free(r);
-               return 0;
-       } else {
-               /* iptcc_map_target increment target chain references
-                * since this is a fake rule only used for matching
-                * the chain references count is decremented again. 
-                */
-               if (r->type == IPTCC_R_JUMP
-                   && r->jump)
-                       r->jump->references--;
-       }
-
-       list_for_each_entry(i, &c->rules, list) {
-               unsigned char *mask;
-
-               mask = is_same(r->entry, i->entry, matchmask);
-               if (!mask)
-                       continue;
-
-               if (!target_same(r, i, mask))
-                       continue;
-
-               /* If we are about to delete the rule that is the
-                * current iterator, move rule iterator back.  next
-                * pointer will then point to real next node */
-               if (i == (*handle)->rule_iterator_cur) {
-                       (*handle)->rule_iterator_cur = 
-                               list_entry((*handle)->rule_iterator_cur->list.prev,
-                                          struct rule_head, list);
-               }
-
-               c->num_rules--;
-               iptcc_delete_rule(i);
-
-               set_changed(*handle);
-               free(r);
-               return 1;
-       }
-
-       free(r);
-       errno = ENOENT;
-       return 0;
-}
-
-
-/* Delete the rule in position `rulenum' in `chain'. */
-int
-TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
-                   unsigned int rulenum,
-                   TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r;
-
-       iptc_fn = TC_DELETE_NUM_ENTRY;
-
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       if (rulenum >= c->num_rules) {
-               errno = E2BIG;
-               return 0;
-       }
-
-       /* Take advantage of the double linked list if possible. */
-       if (rulenum + 1 <= c->num_rules/2) {
-               r = iptcc_get_rule_num(c, rulenum + 1);
-       } else {
-               r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
-       }
-
-       /* If we are about to delete the rule that is the current
-        * iterator, move rule iterator back.  next pointer will then
-        * point to real next node */
-       if (r == (*handle)->rule_iterator_cur) {
-               (*handle)->rule_iterator_cur = 
-                       list_entry((*handle)->rule_iterator_cur->list.prev,
-                                  struct rule_head, list);
-       }
-
-       c->num_rules--;
-       iptcc_delete_rule(r);
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-/* Check the packet `fw' on chain `chain'.  Returns the verdict, or
-   NULL and sets errno. */
-const char *
-TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
-               STRUCT_ENTRY *entry,
-               TC_HANDLE_T *handle)
-{
-       iptc_fn = TC_CHECK_PACKET;
-       errno = ENOSYS;
-       return NULL;
-}
-
-/* Flushes the entries in the given chain (ie. empties chain). */
-int
-TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r, *tmp;
-
-       iptc_fn = TC_FLUSH_ENTRIES;
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       list_for_each_entry_safe(r, tmp, &c->rules, list) {
-               iptcc_delete_rule(r);
-       }
-
-       c->num_rules = 0;
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-/* Zeroes the counters in a chain. */
-int
-TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r;
-
-       iptc_fn = TC_ZERO_ENTRIES;
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       if (c->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
-               c->counter_map.maptype = COUNTER_MAP_ZEROED;
-
-       list_for_each_entry(r, &c->rules, list) {
-               if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
-                       r->counter_map.maptype = COUNTER_MAP_ZEROED;
-       }
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-STRUCT_COUNTERS *
-TC_READ_COUNTER(const IPT_CHAINLABEL chain,
-               unsigned int rulenum,
-               TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r;
-
-       iptc_fn = TC_READ_COUNTER;
-       CHECK(*handle);
-
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               errno = ENOENT;
-               return NULL;
-       }
-
-       if (!(r = iptcc_get_rule_num(c, rulenum))) {
-               errno = E2BIG;
-               return NULL;
-       }
-
-       return &r->entry[0].counters;
-}
-
-int
-TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
-               unsigned int rulenum,
-               TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r;
-       
-       iptc_fn = TC_ZERO_COUNTER;
-       CHECK(*handle);
-
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       if (!(r = iptcc_get_rule_num(c, rulenum))) {
-               errno = E2BIG;
-               return 0;
-       }
-
-       if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
-               r->counter_map.maptype = COUNTER_MAP_ZEROED;
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-int 
-TC_SET_COUNTER(const IPT_CHAINLABEL chain,
-              unsigned int rulenum,
-              STRUCT_COUNTERS *counters,
-              TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       struct rule_head *r;
-       STRUCT_ENTRY *e;
-
-       iptc_fn = TC_SET_COUNTER;
-       CHECK(*handle);
-
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       if (!(r = iptcc_get_rule_num(c, rulenum))) {
-               errno = E2BIG;
-               return 0;
-       }
-
-       e = r->entry;
-       r->counter_map.maptype = COUNTER_MAP_SET;
-
-       memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-/* Creates a new chain. */
-/* To create a chain, create two rules: error node and unconditional
- * return. */
-int
-TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
-{
-       static struct chain_head *c;
-       int capacity;
-       int exceeded;
-
-       iptc_fn = TC_CREATE_CHAIN;
-
-       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
-           QUEUE, RETURN. */
-       if (iptcc_find_label(chain, *handle)
-           || strcmp(chain, LABEL_DROP) == 0
-           || strcmp(chain, LABEL_ACCEPT) == 0
-           || strcmp(chain, LABEL_QUEUE) == 0
-           || strcmp(chain, LABEL_RETURN) == 0) {
-               DEBUGP("Chain `%s' already exists\n", chain);
-               errno = EEXIST;
-               return 0;
-       }
-
-       if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
-               DEBUGP("Chain name `%s' too long\n", chain);
-               errno = EINVAL;
-               return 0;
-       }
-
-       c = iptcc_alloc_chain_head(chain, 0);
-       if (!c) {
-               DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
-               errno = ENOMEM;
-               return 0;
-
-       }
-       (*handle)->num_chains++; /* New user defined chain */
-
-       DEBUGP("Creating chain `%s'\n", chain);
-       iptc_insert_chain(*handle, c); /* Insert sorted */
-
-       /* Inserting chains don't change the correctness of the chain
-        * index (except if its smaller than index[0], but that
-        * handled by iptc_insert_chain).  It only causes longer lists
-        * in the buckets. Thus, only rebuild chain index when the
-        * capacity is exceed with CHAIN_INDEX_INSERT_MAX chains.
-        */
-       capacity = (*handle)->chain_index_sz * CHAIN_INDEX_BUCKET_LEN;
-       exceeded = ((((*handle)->num_chains)-capacity));
-       if (exceeded > CHAIN_INDEX_INSERT_MAX) {
-               debug("Capacity(%d) exceeded(%d) rebuild (chains:%d)\n",
-                     capacity, exceeded, (*handle)->num_chains);
-               iptcc_chain_index_rebuild(*handle);
-       }
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-/* Get the number of references to this chain. */
-int
-TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
-                 TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-
-       iptc_fn = TC_GET_REFERENCES;
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       *ref = c->references;
-
-       return 1;
-}
-
-/* Deletes a chain. */
-int
-TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
-{
-       unsigned int references;
-       struct chain_head *c;
-
-       iptc_fn = TC_DELETE_CHAIN;
-
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               DEBUGP("cannot find chain `%s'\n", chain);
-               errno = ENOENT;
-               return 0;
-       }
-
-       if (TC_BUILTIN(chain, *handle)) {
-               DEBUGP("cannot remove builtin chain `%s'\n", chain);
-               errno = EINVAL;
-               return 0;
-       }
-
-       if (!TC_GET_REFERENCES(&references, chain, handle)) {
-               DEBUGP("cannot get references on chain `%s'\n", chain);
-               return 0;
-       }
-
-       if (references > 0) {
-               DEBUGP("chain `%s' still has references\n", chain);
-               errno = EMLINK;
-               return 0;
-       }
-
-       if (c->num_rules) {
-               DEBUGP("chain `%s' is not empty\n", chain);
-               errno = ENOTEMPTY;
-               return 0;
-       }
-
-       /* If we are about to delete the chain that is the current
-        * iterator, move chain iterator forward. */
-       if (c == (*handle)->chain_iterator_cur)
-               iptcc_chain_iterator_advance(*handle);
-
-       (*handle)->num_chains--; /* One user defined chain deleted */
-
-       //list_del(&c->list); /* Done in iptcc_chain_index_delete_chain() */
-       iptcc_chain_index_delete_chain(c, *handle);
-       free(c);
-
-       DEBUGP("chain `%s' deleted\n", chain);
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-/* Renames a chain. */
-int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
-                   const IPT_CHAINLABEL newname,
-                   TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-       iptc_fn = TC_RENAME_CHAIN;
-
-       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
-           QUEUE, RETURN. */
-       if (iptcc_find_label(newname, *handle)
-           || strcmp(newname, LABEL_DROP) == 0
-           || strcmp(newname, LABEL_ACCEPT) == 0
-           || strcmp(newname, LABEL_QUEUE) == 0
-           || strcmp(newname, LABEL_RETURN) == 0) {
-               errno = EEXIST;
-               return 0;
-       }
-
-       if (!(c = iptcc_find_label(oldname, *handle))
-           || TC_BUILTIN(oldname, *handle)) {
-               errno = ENOENT;
-               return 0;
-       }
-
-       if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
-               errno = EINVAL;
-               return 0;
-       }
-
-       strncpy(c->name, newname, sizeof(IPT_CHAINLABEL));
-       
-       set_changed(*handle);
-
-       return 1;
-}
-
-/* Sets the policy on a built-in chain. */
-int
-TC_SET_POLICY(const IPT_CHAINLABEL chain,
-             const IPT_CHAINLABEL policy,
-             STRUCT_COUNTERS *counters,
-             TC_HANDLE_T *handle)
-{
-       struct chain_head *c;
-
-       iptc_fn = TC_SET_POLICY;
-
-       if (!(c = iptcc_find_label(chain, *handle))) {
-               DEBUGP("cannot find chain `%s'\n", chain);
-               errno = ENOENT;
-               return 0;
-       }
-
-       if (!iptcc_is_builtin(c)) {
-               DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
-               errno = ENOENT;
-               return 0;
-       }
-
-       if (strcmp(policy, LABEL_ACCEPT) == 0)
-               c->verdict = -NF_ACCEPT - 1;
-       else if (strcmp(policy, LABEL_DROP) == 0)
-               c->verdict = -NF_DROP - 1;
-       else {
-               errno = EINVAL;
-               return 0;
-       }
-
-       if (counters) {
-               /* set byte and packet counters */
-               memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS));
-               c->counter_map.maptype = COUNTER_MAP_SET;
-       } else {
-               c->counter_map.maptype = COUNTER_MAP_NOMAP;
-       }
-
-       set_changed(*handle);
-
-       return 1;
-}
-
-/* Without this, on gcc 2.7.2.3, we get:
-   libiptc.c: In function `TC_COMMIT':
-   libiptc.c:833: fixed or forbidden register was spilled.
-   This may be due to a compiler bug or to impossible asm
-   statements or clauses.
-*/
-static void
-subtract_counters(STRUCT_COUNTERS *answer,
-                 const STRUCT_COUNTERS *a,
-                 const STRUCT_COUNTERS *b)
-{
-       answer->pcnt = a->pcnt - b->pcnt;
-       answer->bcnt = a->bcnt - b->bcnt;
-}
-
-
-static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters, unsigned int idx)
-{
-       newcounters->counters[idx] = ((STRUCT_COUNTERS) { 0, 0});
-       DEBUGP_C("NOMAP => zero\n");
-}
-
-static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
-                               STRUCT_REPLACE *repl, unsigned int idx,
-                               unsigned int mappos)
-{
-       /* Original read: X.
-        * Atomic read on replacement: X + Y.
-        * Currently in kernel: Z.
-        * Want in kernel: X + Y + Z.
-        * => Add in X + Y
-        * => Add in replacement read.
-        */
-       newcounters->counters[idx] = repl->counters[mappos];
-       DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
-}
-
-static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
-                               STRUCT_REPLACE *repl, unsigned int idx,
-                               unsigned int mappos, STRUCT_COUNTERS *counters)
-{
-       /* Original read: X.
-        * Atomic read on replacement: X + Y.
-        * Currently in kernel: Z.
-        * Want in kernel: Y + Z.
-        * => Add in Y.
-        * => Add in (replacement read - original read).
-        */
-       subtract_counters(&newcounters->counters[idx],
-                         &repl->counters[mappos],
-                         counters);
-       DEBUGP_C("ZEROED => mappos %u\n", mappos);
-}
-
-static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
-                             unsigned int idx, STRUCT_COUNTERS *counters)
-{
-       /* Want to set counter (iptables-restore) */
-
-       memcpy(&newcounters->counters[idx], counters,
-               sizeof(STRUCT_COUNTERS));
-
-       DEBUGP_C("SET\n");
-}
-
-
-int
-TC_COMMIT(TC_HANDLE_T *handle)
-{
-       /* Replace, then map back the counters. */
-       STRUCT_REPLACE *repl;
-       STRUCT_COUNTERS_INFO *newcounters;
-       struct chain_head *c;
-       int ret;
-       size_t counterlen;
-       int new_number;
-       unsigned int new_size;
-
-       iptc_fn = TC_COMMIT;
-       CHECK(*handle);
-
-       /* Don't commit if nothing changed. */
-       if (!(*handle)->changed)
-               goto finished;
-
-       new_number = iptcc_compile_table_prep(*handle, &new_size);
-       if (new_number < 0) {
-               errno = ENOMEM;
-               goto out_zero;
-       }
-
-       repl = malloc(sizeof(*repl) + new_size);
-       if (!repl) {
-               errno = ENOMEM;
-               goto out_zero;
-       }
-       memset(repl, 0, sizeof(*repl) + new_size);
-
-#if 0
-       TC_DUMP_ENTRIES(*handle);
-#endif
-
-       counterlen = sizeof(STRUCT_COUNTERS_INFO)
-                       + sizeof(STRUCT_COUNTERS) * new_number;
-
-       /* These are the old counters we will get from kernel */
-       repl->counters = malloc(sizeof(STRUCT_COUNTERS)
-                               * (*handle)->info.num_entries);
-       if (!repl->counters) {
-               errno = ENOMEM;
-               goto out_free_repl;
-       }
-       /* These are the counters we're going to put back, later. */
-       newcounters = malloc(counterlen);
-       if (!newcounters) {
-               errno = ENOMEM;
-               goto out_free_repl_counters;
-       }
-       memset(newcounters, 0, counterlen);
-
-       strcpy(repl->name, (*handle)->info.name);
-       repl->num_entries = new_number;
-       repl->size = new_size;
-
-       repl->num_counters = (*handle)->info.num_entries;
-       repl->valid_hooks = (*handle)->info.valid_hooks;
-
-       DEBUGP("num_entries=%u, size=%u, num_counters=%u\n",
-               repl->num_entries, repl->size, repl->num_counters);
-
-       ret = iptcc_compile_table(*handle, repl);
-       if (ret < 0) {
-               errno = ret;
-               goto out_free_newcounters;
-       }
-
-
-#ifdef IPTC_DEBUG2
-       {
-               int fd = open("/tmp/libiptc-so_set_replace.blob", 
-                               O_CREAT|O_WRONLY);
-               if (fd >= 0) {
-                       write(fd, repl, sizeof(*repl) + repl->size);
-                       close(fd);
-               }
-       }
-#endif
-
-       ret = setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
-                        sizeof(*repl) + repl->size);
-       if (ret < 0)
-               goto out_free_newcounters;
-
-       /* Put counters back. */
-       strcpy(newcounters->name, (*handle)->info.name);
-       newcounters->num_counters = new_number;
-
-       list_for_each_entry(c, &(*handle)->chains, list) {
-               struct rule_head *r;
-
-               /* Builtin chains have their own counters */
-               if (iptcc_is_builtin(c)) {
-                       DEBUGP("counter for chain-index %u: ", c->foot_index);
-                       switch(c->counter_map.maptype) {
-                       case COUNTER_MAP_NOMAP:
-                               counters_nomap(newcounters, c->foot_index);
-                               break;
-                       case COUNTER_MAP_NORMAL_MAP:
-                               counters_normal_map(newcounters, repl,
-                                                   c->foot_index, 
-                                                   c->counter_map.mappos);
-                               break;
-                       case COUNTER_MAP_ZEROED:
-                               counters_map_zeroed(newcounters, repl,
-                                                   c->foot_index, 
-                                                   c->counter_map.mappos,
-                                                   &c->counters);
-                               break;
-                       case COUNTER_MAP_SET:
-                               counters_map_set(newcounters, c->foot_index,
-                                                &c->counters);
-                               break;
-                       }
-               }
-
-               list_for_each_entry(r, &c->rules, list) {
-                       DEBUGP("counter for index %u: ", r->index);
-                       switch (r->counter_map.maptype) {
-                       case COUNTER_MAP_NOMAP:
-                               counters_nomap(newcounters, r->index);
-                               break;
-
-                       case COUNTER_MAP_NORMAL_MAP:
-                               counters_normal_map(newcounters, repl,
-                                                   r->index, 
-                                                   r->counter_map.mappos);
-                               break;
-
-                       case COUNTER_MAP_ZEROED:
-                               counters_map_zeroed(newcounters, repl,
-                                                   r->index,
-                                                   r->counter_map.mappos,
-                                                   &r->entry->counters);
-                               break;
-
-                       case COUNTER_MAP_SET:
-                               counters_map_set(newcounters, r->index,
-                                                &r->entry->counters);
-                               break;
-                       }
-               }
-       }
-
-#ifdef IPTC_DEBUG2
-       {
-               int fd = open("/tmp/libiptc-so_set_add_counters.blob", 
-                               O_CREAT|O_WRONLY);
-               if (fd >= 0) {
-                       write(fd, newcounters, counterlen);
-                       close(fd);
-               }
-       }
-#endif
-
-       ret = setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
-                        newcounters, counterlen);
-       if (ret < 0)
-               goto out_free_newcounters;
-
-       free(repl->counters);
-       free(repl);
-       free(newcounters);
-
-finished:
-       TC_FREE(handle);
-       return 1;
-
-out_free_newcounters:
-       free(newcounters);
-out_free_repl_counters:
-       free(repl->counters);
-out_free_repl:
-       free(repl);
-out_zero:
-       return 0;
-}
-
-/* Get raw socket. */
-int
-TC_GET_RAW_SOCKET(void)
-{
-       return sockfd;
-}
-
-/* Translates errno numbers into more human-readable form than strerror. */
-const char *
-TC_STRERROR(int err)
-{
-       unsigned int i;
-       struct table_struct {
-               void *fn;
-               int err;
-               const char *message;
-       } table [] =
-         { { TC_INIT, EPERM, "Permission denied (you must be root)" },
-           { TC_INIT, EINVAL, "Module is wrong version" },
-           { TC_INIT, ENOENT, 
-                   "Table does not exist (do you need to insmod?)" },
-           { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
-           { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
-           { TC_DELETE_CHAIN, EMLINK,
-             "Can't delete chain with references left" },
-           { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
-           { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
-           { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
-           { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
-           { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
-           { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
-           { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
-           { TC_INSERT_ENTRY, EINVAL, "Target problem" },
-           /* EINVAL for CHECK probably means bad interface. */
-           { TC_CHECK_PACKET, EINVAL,
-             "Bad arguments (does that interface exist?)" },
-           { TC_CHECK_PACKET, ENOSYS,
-             "Checking will most likely never get implemented" },
-           /* ENOENT for DELETE probably means no matching rule */
-           { TC_DELETE_ENTRY, ENOENT,
-             "Bad rule (does a matching rule exist in that chain?)" },
-           { TC_SET_POLICY, ENOENT,
-             "Bad built-in chain name" },
-           { TC_SET_POLICY, EINVAL,
-             "Bad policy name" },
-
-           { NULL, 0, "Incompatible with this kernel" },
-           { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
-           { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
-           { NULL, ENOMEM, "Memory allocation problem" },
-           { NULL, ENOENT, "No chain/target/match by that name" },
-         };
-
-       for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
-               if ((!table[i].fn || table[i].fn == iptc_fn)
-                   && table[i].err == err)
-                       return table[i].message;
-       }
-
-       return strerror(err);
-}
diff --git a/src/owniptc/libiptc.h b/src/owniptc/libiptc.h
deleted file mode 100644 (file)
index 3fc25b6..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/**
- * This file was imported from the iptables sources.
- * Copyright (C) 1999-2008 Netfilter Core Team
- *
- * 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
- */
-
-#ifndef _LIBIPTC_H
-#define _LIBIPTC_H
-/* Library which manipulates filtering rules. */
-
-#include <linux/types.h>
-#include "ipt_kernel_headers.h"
-#include <linux/netfilter_ipv4/ip_tables.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef IPT_MIN_ALIGN
-/* ipt_entry has pointers and u_int64_t's in it, so if you align to
-   it, you'll also align to any crazy matches and targets someone
-   might write */
-#define IPT_MIN_ALIGN (__alignof__(struct ipt_entry))
-#endif
-
-#define IPT_ALIGN(s) (((s) + ((IPT_MIN_ALIGN)-1)) & ~((IPT_MIN_ALIGN)-1))
-
-typedef char ipt_chainlabel[32];
-
-#define IPTC_LABEL_ACCEPT  "ACCEPT"
-#define IPTC_LABEL_DROP    "DROP"
-#define IPTC_LABEL_QUEUE   "QUEUE"
-#define IPTC_LABEL_RETURN  "RETURN"
-
-/* Transparent handle type. */
-typedef struct iptc_handle *iptc_handle_t;
-
-/* Does this chain exist? */
-int iptc_is_chain(const char *chain, const iptc_handle_t handle);
-
-/* Take a snapshot of the rules.  Returns NULL on error. */
-iptc_handle_t iptc_init(const char *tablename);
-
-/* Cleanup after iptc_init(). */
-void iptc_free(iptc_handle_t *h);
-
-/* Iterator functions to run through the chains.  Returns NULL at end. */
-const char *iptc_first_chain(iptc_handle_t *handle);
-const char *iptc_next_chain(iptc_handle_t *handle);
-
-/* Get first rule in the given chain: NULL for empty chain. */
-const struct ipt_entry *iptc_first_rule(const char *chain,
-                                       iptc_handle_t *handle);
-
-/* Returns NULL when rules run out. */
-const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev,
-                                      iptc_handle_t *handle);
-
-/* Returns a pointer to the target name of this entry. */
-const char *iptc_get_target(const struct ipt_entry *e,
-                           iptc_handle_t *handle);
-
-/* Is this a built-in chain? */
-int iptc_builtin(const char *chain, const iptc_handle_t handle);
-
-/* Get the policy of a given built-in chain */
-const char *iptc_get_policy(const char *chain,
-                           struct ipt_counters *counter,
-                           iptc_handle_t *handle);
-
-/* These functions return TRUE for OK or 0 and set errno.  If errno ==
-   0, it means there was a version error (ie. upgrade libiptc). */
-/* Rule numbers start at 1 for the first rule. */
-
-/* Insert the entry `e' in chain `chain' into position `rulenum'. */
-int iptc_insert_entry(const ipt_chainlabel chain,
-                     const struct ipt_entry *e,
-                     unsigned int rulenum,
-                     iptc_handle_t *handle);
-
-/* Atomically replace rule `rulenum' in `chain' with `e'. */
-int iptc_replace_entry(const ipt_chainlabel chain,
-                      const struct ipt_entry *e,
-                      unsigned int rulenum,
-                      iptc_handle_t *handle);
-
-/* Append entry `e' to chain `chain'.  Equivalent to insert with
-   rulenum = length of chain. */
-int iptc_append_entry(const ipt_chainlabel chain,
-                     const struct ipt_entry *e,
-                     iptc_handle_t *handle);
-
-/* Delete the first rule in `chain' which matches `e', subject to
-   matchmask (array of length == origfw) */
-int iptc_delete_entry(const ipt_chainlabel chain,
-                     const struct ipt_entry *origfw,
-                     unsigned char *matchmask,
-                     iptc_handle_t *handle);
-
-/* Delete the rule in position `rulenum' in `chain'. */
-int iptc_delete_num_entry(const ipt_chainlabel chain,
-                         unsigned int rulenum,
-                         iptc_handle_t *handle);
-
-/* Check the packet `e' on chain `chain'.  Returns the verdict, or
-   NULL and sets errno. */
-const char *iptc_check_packet(const ipt_chainlabel chain,
-                             struct ipt_entry *entry,
-                             iptc_handle_t *handle);
-
-/* Flushes the entries in the given chain (ie. empties chain). */
-int iptc_flush_entries(const ipt_chainlabel chain,
-                      iptc_handle_t *handle);
-
-/* Zeroes the counters in a chain. */
-int iptc_zero_entries(const ipt_chainlabel chain,
-                     iptc_handle_t *handle);
-
-/* Creates a new chain. */
-int iptc_create_chain(const ipt_chainlabel chain,
-                     iptc_handle_t *handle);
-
-/* Deletes a chain. */
-int iptc_delete_chain(const ipt_chainlabel chain,
-                     iptc_handle_t *handle);
-
-/* Renames a chain. */
-int iptc_rename_chain(const ipt_chainlabel oldname,
-                     const ipt_chainlabel newname,
-                     iptc_handle_t *handle);
-
-/* Sets the policy on a built-in chain. */
-int iptc_set_policy(const ipt_chainlabel chain,
-                   const ipt_chainlabel policy,
-                   struct ipt_counters *counters,
-                   iptc_handle_t *handle);
-
-/* Get the number of references to this chain */
-int iptc_get_references(unsigned int *ref,
-                       const ipt_chainlabel chain,
-                       iptc_handle_t *handle);
-
-/* read packet and byte counters for a specific rule */
-struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain,
-                                      unsigned int rulenum,
-                                      iptc_handle_t *handle);
-
-/* zero packet and byte counters for a specific rule */
-int iptc_zero_counter(const ipt_chainlabel chain,
-                     unsigned int rulenum,
-                     iptc_handle_t *handle);
-
-/* set packet and byte counters for a specific rule */
-int iptc_set_counter(const ipt_chainlabel chain,
-                    unsigned int rulenum,
-                    struct ipt_counters *counters,
-                    iptc_handle_t *handle);
-
-/* Makes the actual changes. */
-int iptc_commit(iptc_handle_t *handle);
-
-/* Get raw socket. */
-int iptc_get_raw_socket(void);
-
-/* Translates errno numbers into more human-readable form than strerror. */
-const char *iptc_strerror(int err);
-
-extern void dump_entries(const iptc_handle_t);
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif /* _LIBIPTC_H */
diff --git a/src/owniptc/libxtc.h b/src/owniptc/libxtc.h
deleted file mode 100644 (file)
index 2ed03f4..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * This file was imported from the iptables sources.
- * Copyright (C) 1999-2008 Netfilter Core Team
- *
- * 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
- */
-
-#ifndef _LIBXTC_H
-#define _LIBXTC_H
-/* Library which manipulates filtering rules. */
-
-#include "ipt_kernel_headers.h"
-#include <linux/netfilter/x_tables.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef XT_MIN_ALIGN
-/* xt_entry has pointers and u_int64_t's in it, so if you align to
-   it, you'll also align to any crazy matches and targets someone
-   might write */
-#define XT_MIN_ALIGN (__alignof__(struct xt_entry))
-#endif
-
-#ifndef XT_ALIGN
-#define XT_ALIGN(s) (((s) + ((XT_MIN_ALIGN)-1)) & ~((XT_MIN_ALIGN)-1))
-#endif
-
-typedef char xt_chainlabel[32];
-
-#define XTC_LABEL_ACCEPT  "ACCEPT"
-#define XTC_LABEL_DROP    "DROP"
-#define XTC_LABEL_QUEUE   "QUEUE"
-#define XTC_LABEL_RETURN  "RETURN"
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBXTC_H */
diff --git a/src/owniptc/linux_list.h b/src/owniptc/linux_list.h
deleted file mode 100644 (file)
index 56d9a26..0000000
+++ /dev/null
@@ -1,741 +0,0 @@
-/**
- * This file was imported from the iptables sources.
- * Copyright (C) 1999-2008 Netfilter Core Team
- *
- * 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
- */
-
-#ifndef _LINUX_LIST_H
-#define _LINUX_LIST_H
-
-#undef offsetof
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-
-/**
- * container_of - cast a member of a structure out to the containing structure
- *
- * @ptr:       the pointer to the member.
- * @type:      the type of the container struct this is embedded in.
- * @member:    the name of the member within the struct.
- *
- */
-#define container_of(ptr, type, member) ({                     \
-        const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
-        (type *)( (char *)__mptr - offsetof(type,member) );})
-
-/*
- * Check at compile time that something is of a particular type.
- * Always evaluates to 1 so you may use it easily in comparisons.
- */
-#define typecheck(type,x) \
-({     type __dummy; \
-       typeof(x) __dummy2; \
-       (void)(&__dummy == &__dummy2); \
-       1; \
-})
-
-#define prefetch(x)            1
-
-/* empty define to make this work in userspace -HW */
-#define smp_wmb()
-
-/*
- * These are non-NULL pointers that will result in page faults
- * under normal circumstances, used to verify that nobody uses
- * non-initialized list entries.
- */
-#define LIST_POISON1  ((void *) 0x00100100)
-#define LIST_POISON2  ((void *) 0x00200200)
-
-/*
- * Simple doubly linked list implementation.
- *
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-struct list_head {
-       struct list_head *next, *prev;
-};
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
-       struct list_head name = LIST_HEAD_INIT(name)
-
-#define INIT_LIST_HEAD(ptr) do { \
-       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *new,
-                             struct list_head *prev,
-                             struct list_head *next)
-{
-       next->prev = new;
-       new->next = next;
-       new->prev = prev;
-       prev->next = new;
-}
-
-/**
- * list_add - add a new entry
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
- */
-static inline void list_add(struct list_head *new, struct list_head *head)
-{
-       __list_add(new, head, head->next);
-}
-
-/**
- * list_add_tail - add a new entry
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- */
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
-{
-       __list_add(new, head->prev, head);
-}
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add_rcu(struct list_head * new,
-               struct list_head * prev, struct list_head * next)
-{
-       new->next = next;
-       new->prev = prev;
-       smp_wmb();
-       next->prev = new;
-       prev->next = new;
-}
-
-/**
- * list_add_rcu - add a new entry to rcu-protected list
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as list_add_rcu()
- * or list_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * list_for_each_entry_rcu().
- */
-static inline void list_add_rcu(struct list_head *new, struct list_head *head)
-{
-       __list_add_rcu(new, head, head->next);
-}
-
-/**
- * list_add_tail_rcu - add a new entry to rcu-protected list
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as list_add_tail_rcu()
- * or list_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * list_for_each_entry_rcu().
- */
-static inline void list_add_tail_rcu(struct list_head *new,
-                                       struct list_head *head)
-{
-       __list_add_rcu(new, head->prev, head);
-}
-
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_del(struct list_head * prev, struct list_head * next)
-{
-       next->prev = prev;
-       prev->next = next;
-}
-
-/**
- * list_del - deletes entry from list.
- * @entry: the element to delete from the list.
- * Note: list_empty on entry does not return true after this, the entry is
- * in an undefined state.
- */
-static inline void list_del(struct list_head *entry)
-{
-       __list_del(entry->prev, entry->next);
-       entry->next = LIST_POISON1;
-       entry->prev = LIST_POISON2;
-}
-
-/**
- * list_del_rcu - deletes entry from list without re-initialization
- * @entry: the element to delete from the list.
- *
- * Note: list_empty on entry does not return true after this,
- * the entry is in an undefined state. It is useful for RCU based
- * lockfree traversal.
- *
- * In particular, it means that we can not poison the forward
- * pointers that may still be used for walking the list.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as list_del_rcu()
- * or list_add_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * list_for_each_entry_rcu().
- *
- * Note that the caller is not permitted to immediately free
- * the newly deleted entry.  Instead, either synchronize_kernel()
- * or call_rcu() must be used to defer freeing until an RCU
- * grace period has elapsed.
- */
-static inline void list_del_rcu(struct list_head *entry)
-{
-       __list_del(entry->prev, entry->next);
-       entry->prev = LIST_POISON2;
-}
-
-/**
- * list_del_init - deletes entry from list and reinitialize it.
- * @entry: the element to delete from the list.
- */
-static inline void list_del_init(struct list_head *entry)
-{
-       __list_del(entry->prev, entry->next);
-       INIT_LIST_HEAD(entry);
-}
-
-/**
- * list_move - delete from one list and add as another's head
- * @list: the entry to move
- * @head: the head that will precede our entry
- */
-static inline void list_move(struct list_head *list, struct list_head *head)
-{
-        __list_del(list->prev, list->next);
-        list_add(list, head);
-}
-
-/**
- * list_move_tail - delete from one list and add as another's tail
- * @list: the entry to move
- * @head: the head that will follow our entry
- */
-static inline void list_move_tail(struct list_head *list,
-                                 struct list_head *head)
-{
-        __list_del(list->prev, list->next);
-        list_add_tail(list, head);
-}
-
-/**
- * list_empty - tests whether a list is empty
- * @head: the list to test.
- */
-static inline int list_empty(const struct list_head *head)
-{
-       return head->next == head;
-}
-
-/**
- * list_empty_careful - tests whether a list is
- * empty _and_ checks that no other CPU might be
- * in the process of still modifying either member
- *
- * NOTE: using list_empty_careful() without synchronization
- * can only be safe if the only activity that can happen
- * to the list entry is list_del_init(). Eg. it cannot be used
- * if another CPU could re-list_add() it.
- *
- * @head: the list to test.
- */
-static inline int list_empty_careful(const struct list_head *head)
-{
-       struct list_head *next = head->next;
-       return (next == head) && (next == head->prev);
-}
-
-static inline void __list_splice(struct list_head *list,
-                                struct list_head *head)
-{
-       struct list_head *first = list->next;
-       struct list_head *last = list->prev;
-       struct list_head *at = head->next;
-
-       first->prev = head;
-       head->next = first;
-
-       last->next = at;
-       at->prev = last;
-}
-
-/**
- * list_splice - join two lists
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- */
-static inline void list_splice(struct list_head *list, struct list_head *head)
-{
-       if (!list_empty(list))
-               __list_splice(list, head);
-}
-
-/**
- * list_splice_init - join two lists and reinitialise the emptied list.
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- *
- * The list at @list is reinitialised
- */
-static inline void list_splice_init(struct list_head *list,
-                                   struct list_head *head)
-{
-       if (!list_empty(list)) {
-               __list_splice(list, head);
-               INIT_LIST_HEAD(list);
-       }
-}
-
-/**
- * list_entry - get the struct for this entry
- * @ptr:       the &struct list_head pointer.
- * @type:      the type of the struct this is embedded in.
- * @member:    the name of the list_struct within the struct.
- */
-#define list_entry(ptr, type, member) \
-       container_of(ptr, type, member)
-
-/**
- * list_for_each       -       iterate over a list
- * @pos:       the &struct list_head to use as a loop counter.
- * @head:      the head for your list.
- */
-#define list_for_each(pos, head) \
-       for (pos = (head)->next, prefetch(pos->next); pos != (head); \
-               pos = pos->next, prefetch(pos->next))
-
-/**
- * __list_for_each     -       iterate over a list
- * @pos:       the &struct list_head to use as a loop counter.
- * @head:      the head for your list.
- *
- * This variant differs from list_for_each() in that it's the
- * simplest possible list iteration code, no prefetching is done.
- * Use this for code that knows the list to be very short (empty
- * or 1 entry) most of the time.
- */
-#define __list_for_each(pos, head) \
-       for (pos = (head)->next; pos != (head); pos = pos->next)
-
-/**
- * list_for_each_prev  -       iterate over a list backwards
- * @pos:       the &struct list_head to use as a loop counter.
- * @head:      the head for your list.
- */
-#define list_for_each_prev(pos, head) \
-       for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
-               pos = pos->prev, prefetch(pos->prev))
-
-/**
- * list_for_each_safe  -       iterate over a list safe against removal of list entry
- * @pos:       the &struct list_head to use as a loop counter.
- * @n:         another &struct list_head to use as temporary storage
- * @head:      the head for your list.
- */
-#define list_for_each_safe(pos, n, head) \
-       for (pos = (head)->next, n = pos->next; pos != (head); \
-               pos = n, n = pos->next)
-
-/**
- * list_for_each_entry -       iterate over list of given type
- * @pos:       the type * to use as a loop counter.
- * @head:      the head for your list.
- * @member:    the name of the list_struct within the struct.
- */
-#define list_for_each_entry(pos, head, member)                         \
-       for (pos = list_entry((head)->next, typeof(*pos), member),      \
-                    prefetch(pos->member.next);                        \
-            &pos->member != (head);                                    \
-            pos = list_entry(pos->member.next, typeof(*pos), member),  \
-                    prefetch(pos->member.next))
-
-/**
- * list_for_each_entry_reverse - iterate backwards over list of given type.
- * @pos:       the type * to use as a loop counter.
- * @head:      the head for your list.
- * @member:    the name of the list_struct within the struct.
- */
-#define list_for_each_entry_reverse(pos, head, member)                 \
-       for (pos = list_entry((head)->prev, typeof(*pos), member),      \
-                    prefetch(pos->member.prev);                        \
-            &pos->member != (head);                                    \
-            pos = list_entry(pos->member.prev, typeof(*pos), member),  \
-                    prefetch(pos->member.prev))
-
-/**
- * list_prepare_entry - prepare a pos entry for use as a start point in
- *                     list_for_each_entry_continue
- * @pos:       the type * to use as a start point
- * @head:      the head of the list
- * @member:    the name of the list_struct within the struct.
- */
-#define list_prepare_entry(pos, head, member) \
-       ((pos) ? : list_entry(head, typeof(*pos), member))
-
-/**
- * list_for_each_entry_continue -      iterate over list of given type
- *                     continuing after existing point
- * @pos:       the type * to use as a loop counter.
- * @head:      the head for your list.
- * @member:    the name of the list_struct within the struct.
- */
-#define list_for_each_entry_continue(pos, head, member)                \
-       for (pos = list_entry(pos->member.next, typeof(*pos), member),  \
-                    prefetch(pos->member.next);                        \
-            &pos->member != (head);                                    \
-            pos = list_entry(pos->member.next, typeof(*pos), member),  \
-                    prefetch(pos->member.next))
-
-/**
- * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @pos:       the type * to use as a loop counter.
- * @n:         another type * to use as temporary storage
- * @head:      the head for your list.
- * @member:    the name of the list_struct within the struct.
- */
-#define list_for_each_entry_safe(pos, n, head, member)                 \
-       for (pos = list_entry((head)->next, typeof(*pos), member),      \
-               n = list_entry(pos->member.next, typeof(*pos), member); \
-            &pos->member != (head);                                    \
-            pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
-/**
- * list_for_each_rcu   -       iterate over an rcu-protected list
- * @pos:       the &struct list_head to use as a loop counter.
- * @head:      the head for your list.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_rcu(pos, head) \
-       for (pos = (head)->next, prefetch(pos->next); pos != (head); \
-               pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
-
-#define __list_for_each_rcu(pos, head) \
-       for (pos = (head)->next; pos != (head); \
-               pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
-
-/**
- * list_for_each_safe_rcu      -       iterate over an rcu-protected list safe
- *                                     against removal of list entry
- * @pos:       the &struct list_head to use as a loop counter.
- * @n:         another &struct list_head to use as temporary storage
- * @head:      the head for your list.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_safe_rcu(pos, n, head) \
-       for (pos = (head)->next, n = pos->next; pos != (head); \
-               pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
-
-/**
- * list_for_each_entry_rcu     -       iterate over rcu list of given type
- * @pos:       the type * to use as a loop counter.
- * @head:      the head for your list.
- * @member:    the name of the list_struct within the struct.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_entry_rcu(pos, head, member)                     \
-       for (pos = list_entry((head)->next, typeof(*pos), member),      \
-                    prefetch(pos->member.next);                        \
-            &pos->member != (head);                                    \
-            pos = list_entry(pos->member.next, typeof(*pos), member),  \
-                    ({ smp_read_barrier_depends(); 0;}),               \
-                    prefetch(pos->member.next))
-
-
-/**
- * list_for_each_continue_rcu  -       iterate over an rcu-protected list
- *                     continuing after existing point.
- * @pos:       the &struct list_head to use as a loop counter.
- * @head:      the head for your list.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_continue_rcu(pos, head) \
-       for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
-               (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
-
-/*
- * Double linked lists with a single pointer list head.
- * Mostly useful for hash tables where the two pointer list head is
- * too wasteful.
- * You lose the ability to access the tail in O(1).
- */
-
-struct hlist_head {
-       struct hlist_node *first;
-};
-
-struct hlist_node {
-       struct hlist_node *next, **pprev;
-};
-
-#define HLIST_HEAD_INIT { .first = NULL }
-#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
-#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
-#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
-
-static inline int hlist_unhashed(const struct hlist_node *h)
-{
-       return !h->pprev;
-}
-
-static inline int hlist_empty(const struct hlist_head *h)
-{
-       return !h->first;
-}
-
-static inline void __hlist_del(struct hlist_node *n)
-{
-       struct hlist_node *next = n->next;
-       struct hlist_node **pprev = n->pprev;
-       *pprev = next;
-       if (next)
-               next->pprev = pprev;
-}
-
-static inline void hlist_del(struct hlist_node *n)
-{
-       __hlist_del(n);
-       n->next = LIST_POISON1;
-       n->pprev = LIST_POISON2;
-}
-
-/**
- * hlist_del_rcu - deletes entry from hash list without re-initialization
- * @n: the element to delete from the hash list.
- *
- * Note: list_unhashed() on entry does not return true after this,
- * the entry is in an undefined state. It is useful for RCU based
- * lockfree traversal.
- *
- * In particular, it means that we can not poison the forward
- * pointers that may still be used for walking the hash list.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as hlist_add_head_rcu()
- * or hlist_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * hlist_for_each_entry().
- */
-static inline void hlist_del_rcu(struct hlist_node *n)
-{
-       __hlist_del(n);
-       n->pprev = LIST_POISON2;
-}
-
-static inline void hlist_del_init(struct hlist_node *n)
-{
-       if (n->pprev)  {
-               __hlist_del(n);
-               INIT_HLIST_NODE(n);
-       }
-}
-
-#define hlist_del_rcu_init hlist_del_init
-
-static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
-{
-       struct hlist_node *first = h->first;
-       n->next = first;
-       if (first)
-               first->pprev = &n->next;
-       h->first = n;
-       n->pprev = &h->first;
-}
-
-
-/**
- * hlist_add_head_rcu - adds the specified element to the specified hlist,
- * while permitting racing traversals.
- * @n: the element to add to the hash list.
- * @h: the list to add to.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as hlist_add_head_rcu()
- * or hlist_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * hlist_for_each_entry(), but only if smp_read_barrier_depends()
- * is used to prevent memory-consistency problems on Alpha CPUs.
- * Regardless of the type of CPU, the list-traversal primitive
- * must be guarded by rcu_read_lock().
- *
- * OK, so why don't we have an hlist_for_each_entry_rcu()???
- */
-static inline void hlist_add_head_rcu(struct hlist_node *n,
-                                       struct hlist_head *h)
-{
-       struct hlist_node *first = h->first;
-       n->next = first;
-       n->pprev = &h->first;
-       smp_wmb();
-       if (first)
-               first->pprev = &n->next;
-       h->first = n;
-}
-
-/* next must be != NULL */
-static inline void hlist_add_before(struct hlist_node *n,
-                                       struct hlist_node *next)
-{
-       n->pprev = next->pprev;
-       n->next = next;
-       next->pprev = &n->next;
-       *(n->pprev) = n;
-}
-
-static inline void hlist_add_after(struct hlist_node *n,
-                                       struct hlist_node *next)
-{
-       next->next = n->next;
-       n->next = next;
-       next->pprev = &n->next;
-
-       if(next->next)
-               next->next->pprev  = &next->next;
-}
-
-#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
-
-#define hlist_for_each(pos, head) \
-       for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
-            pos = pos->next)
-
-#define hlist_for_each_safe(pos, n, head) \
-       for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
-            pos = n)
-
-/**
- * hlist_for_each_entry        - iterate over list of given type
- * @tpos:      the type * to use as a loop counter.
- * @pos:       the &struct hlist_node to use as a loop counter.
- * @head:      the head for your list.
- * @member:    the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry(tpos, pos, head, member)                   \
-       for (pos = (head)->first;                                        \
-            pos && ({ prefetch(pos->next); 1;}) &&                      \
-               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-            pos = pos->next)
-
-/**
- * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
- * @tpos:      the type * to use as a loop counter.
- * @pos:       the &struct hlist_node to use as a loop counter.
- * @member:    the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_continue(tpos, pos, member)                \
-       for (pos = (pos)->next;                                          \
-            pos && ({ prefetch(pos->next); 1;}) &&                      \
-               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-            pos = pos->next)
-
-/**
- * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
- * @tpos:      the type * to use as a loop counter.
- * @pos:       the &struct hlist_node to use as a loop counter.
- * @member:    the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_from(tpos, pos, member)                    \
-       for (; pos && ({ prefetch(pos->next); 1;}) &&                    \
-               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-            pos = pos->next)
-
-/**
- * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @tpos:      the type * to use as a loop counter.
- * @pos:       the &struct hlist_node to use as a loop counter.
- * @n:         another &struct hlist_node to use as temporary storage
- * @head:      the head for your list.
- * @member:    the name of the hlist_node within the struct.
- */
-#define hlist_for_each_entry_safe(tpos, pos, n, head, member)           \
-       for (pos = (head)->first;                                        \
-            pos && ({ n = pos->next; 1; }) &&                           \
-               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-            pos = n)
-
-/**
- * hlist_for_each_entry_rcu - iterate over rcu list of given type
- * @pos:       the type * to use as a loop counter.
- * @pos:       the &struct hlist_node to use as a loop counter.
- * @head:      the head for your list.
- * @member:    the name of the hlist_node within the struct.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as hlist_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define hlist_for_each_entry_rcu(tpos, pos, head, member)               \
-       for (pos = (head)->first;                                        \
-            pos && ({ prefetch(pos->next); 1;}) &&                      \
-               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-            pos = pos->next, ({ smp_read_barrier_depends(); 0; }) )
-
-#endif
diff --git a/src/owniptc/xtables.h b/src/owniptc/xtables.h
deleted file mode 100644 (file)
index 47e47dc..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-/**
- * This file was imported from the iptables sources.
- * Copyright (C) 1999-2008 Netfilter Core Team
- *
- * 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
- */
-
-#ifndef _XTABLES_H
-#define _XTABLES_H
-
-#include <sys/types.h>
-#include <linux/types.h>
-#include <linux/netfilter/x_tables.h>
-#include "libxtc.h"
-#include <stdbool.h>
-
-#ifndef IPPROTO_SCTP
-#define IPPROTO_SCTP 132
-#endif
-#ifndef IPPROTO_DCCP
-#define IPPROTO_DCCP 33
-#endif
-#ifndef IPPROTO_UDPLITE
-#define IPPROTO_UDPLITE        136
-#endif
-
-#define XTABLES_VERSION "1.4.1.1"
-#define XTABLES_VERSION_CODE (0x10000 * 1 + 0x100 * 4 + 1)
-
-#define XTABLES_API_VERSION(x,y,z)    (0x10000*(x) + 0x100*(y) + z)
-
-/* Include file for additions: new matches and targets. */
-struct xtables_match
-{
-       struct xtables_match *next;
-
-       xt_chainlabel name;
-
-       /* Revision of match (0 by default). */
-       u_int8_t revision;
-
-       u_int16_t family;
-
-       const char *version;
-
-       /* Size of match data. */
-       size_t size;
-
-       /* Size of match data relevent for userspace comparison purposes */
-       size_t userspacesize;
-
-       /* Function which prints out usage message. */
-       void (*help)(void);
-
-       /* Initialize the match. */
-       void (*init)(struct xt_entry_match *m);
-
-       /* Function which parses command options; returns true if it
-           ate an option */
-       /* entry is struct ipt_entry for example */
-       int (*parse)(int c, char **argv, int invert, unsigned int *flags,
-                    const void *entry,
-                    struct xt_entry_match **match);
-
-       /* Final check; exit if not ok. */
-       void (*final_check)(unsigned int flags);
-
-       /* Prints out the match iff non-NULL: put space at end */
-       /* ip is struct ipt_ip * for example */
-       void (*print)(const void *ip,
-                     const struct xt_entry_match *match, int numeric);
-
-       /* Saves the match info in parsable form to stdout. */
-       /* ip is struct ipt_ip * for example */
-       void (*save)(const void *ip, const struct xt_entry_match *match);
-
-       /* Pointer to list of extra command-line options */
-       const struct option *extra_opts;
-
-       /* Ignore these men behind the curtain: */
-       unsigned int option_offset;
-       struct xt_entry_match *m;
-       unsigned int mflags;
-#ifdef NO_SHARED_LIBS
-       unsigned int loaded; /* simulate loading so options are merged properly */
-#endif
-};
-
-struct xtables_target
-{
-       struct xtables_target *next;
-
-       xt_chainlabel name;
-
-       /* Revision of target (0 by default). */
-       u_int8_t revision;
-
-       u_int16_t family;
-
-       const char *version;
-
-       /* Size of target data. */
-       size_t size;
-
-       /* Size of target data relevent for userspace comparison purposes */
-       size_t userspacesize;
-
-       /* Function which prints out usage message. */
-       void (*help)(void);
-
-       /* Initialize the target. */
-       void (*init)(struct xt_entry_target *t);
-
-       /* Function which parses command options; returns true if it
-           ate an option */
-       /* entry is struct ipt_entry for example */
-       int (*parse)(int c, char **argv, int invert, unsigned int *flags,
-                    const void *entry,
-                    struct xt_entry_target **targetinfo);
-
-       /* Final check; exit if not ok. */
-       void (*final_check)(unsigned int flags);
-
-       /* Prints out the target iff non-NULL: put space at end */
-       void (*print)(const void *ip,
-                     const struct xt_entry_target *target, int numeric);
-
-       /* Saves the targinfo in parsable form to stdout. */
-       void (*save)(const void *ip,
-                    const struct xt_entry_target *target);
-
-       /* Pointer to list of extra command-line options */
-       const struct option *extra_opts;
-
-       /* Ignore these men behind the curtain: */
-       unsigned int option_offset;
-       struct xt_entry_target *t;
-       unsigned int tflags;
-       unsigned int used;
-#ifdef NO_SHARED_LIBS
-       unsigned int loaded; /* simulate loading so options are merged properly */
-#endif
-};
-
-/* Your shared library should call one of these. */
-extern void xtables_register_match(struct xtables_match *me);
-extern void xtables_register_target(struct xtables_target *me);
-
-extern int string_to_number_ll(const char *s,
-                              unsigned long long min,
-                              unsigned long long max,
-                              unsigned long long *ret);
-extern int string_to_number_l(const char *s,
-                             unsigned long min,
-                             unsigned long max,
-                             unsigned long *ret);
-extern int string_to_number(const char *s,
-                           unsigned int min,
-                           unsigned int max,
-                           unsigned int *ret);
-extern bool strtonuml(const char *, char **, unsigned long *,
-       unsigned long, unsigned long);
-extern bool strtonum(const char *, char **, unsigned int *,
-       unsigned int, unsigned int);
-extern int service_to_port(const char *name, const char *proto);
-extern u_int16_t parse_port(const char *port, const char *proto);
-extern void
-parse_interface(const char *arg, char *vianame, unsigned char *mask);
-
-enum exittype {
-       OTHER_PROBLEM = 1,
-       PARAMETER_PROBLEM,
-       VERSION_PROBLEM,
-       RESOURCE_PROBLEM,
-       P_ONLY_ONCE,
-       P_NO_INVERT,
-       P_BAD_VALUE,
-       P_ONE_ACTION,
-};
-
-/* this is a special 64bit data type that is 8-byte aligned */
-#define aligned_u64 u_int64_t __attribute__((aligned(8)))
-
-int check_inverse(const char option[], int *invert, int *my_optind, int argc);
-void exit_error(enum exittype, const char *, ...)__attribute__((noreturn,
-                                                         format(printf,2,3)));
-extern void param_act(unsigned int, const char *, ...);
-extern const char *program_name, *program_version;
-
-extern const char *ipaddr_to_numeric(const struct in_addr *);
-extern const char *ipaddr_to_anyname(const struct in_addr *);
-extern const char *ipmask_to_numeric(const struct in_addr *);
-extern struct in_addr *numeric_to_ipaddr(const char *);
-extern struct in_addr *numeric_to_ipmask(const char *);
-extern void ipparse_hostnetworkmask(const char *, struct in_addr **,
-       struct in_addr *, unsigned int *);
-
-extern struct in6_addr *numeric_to_ip6addr(const char *);
-extern const char *ip6addr_to_numeric(const struct in6_addr *);
-extern const char *ip6addr_to_anyname(const struct in6_addr *);
-extern const char *ip6mask_to_numeric(const struct in6_addr *);
-extern void ip6parse_hostnetworkmask(const char *, struct in6_addr **,
-       struct in6_addr *, unsigned int *);
-
-/**
- * Print the specified value to standard output, quoting dangerous
- * characters if required.
- */
-extern void save_string(const char *value);
-
-#ifdef NO_SHARED_LIBS
-#      ifdef _INIT
-#              undef _init
-#              define _init _INIT
-#      endif
-       extern void init_extensions(void);
-#else
-#      define _init __attribute__((constructor)) _INIT
-#endif
-
-/* Present in both iptables.c and ip6tables.c */
-extern u_int16_t parse_protocol(const char *s);
-
-#ifdef XTABLES_INTERNAL
-#      include <xtables/internal.h>
-#endif
-
-#endif /* _XTABLES_H */
index 41e763d..78e508a 100644 (file)
 
 #include "configfile.h"
 
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif
+
 #include <EXTERN.h>
 #include <perl.h>
 
@@ -98,6 +102,7 @@ void boot_DynaLoader (PerlInterpreter *, CV *);
 static XS (Collectd_plugin_register_ds);
 static XS (Collectd_plugin_unregister_ds);
 static XS (Collectd_plugin_dispatch_values);
+static XS (Collectd_plugin_get_interval);
 static XS (Collectd__plugin_write);
 static XS (Collectd__plugin_flush);
 static XS (Collectd_plugin_dispatch_notification);
@@ -173,6 +178,7 @@ static struct {
        { "Collectd::plugin_register_data_set",   Collectd_plugin_register_ds },
        { "Collectd::plugin_unregister_data_set", Collectd_plugin_unregister_ds },
        { "Collectd::plugin_dispatch_values",     Collectd_plugin_dispatch_values },
+       { "Collectd::plugin_get_interval",        Collectd_plugin_get_interval },
        { "Collectd::_plugin_write",              Collectd__plugin_write },
        { "Collectd::_plugin_flush",              Collectd__plugin_flush },
        { "Collectd::plugin_dispatch_notification",
@@ -231,15 +237,6 @@ struct {
        { "", NULL }
 };
 
-struct {
-       char  name[64];
-       int  *var;
-} g_integers[] =
-{
-       { "Collectd::interval_g", &interval_g },
-       { "", NULL }
-};
-
 /*
  * Helper functions for data type conversion.
  */
@@ -393,10 +390,16 @@ static int hv2value_list (pTHX_ HV *hash, value_list_t *vl)
        }
 
        if (NULL != (tmp = hv_fetch (hash, "time", 4, 0)))
-               vl->time = (time_t)SvIV (*tmp);
+       {
+               double t = SvNV (*tmp);
+               vl->time = DOUBLE_TO_CDTIME_T (t);
+       }
 
        if (NULL != (tmp = hv_fetch (hash, "interval", 8, 0)))
-               vl->interval = SvIV (*tmp);
+       {
+               double t = SvNV (*tmp);
+               vl->interval = DOUBLE_TO_CDTIME_T (t);
+       }
 
        if (NULL != (tmp = hv_fetch (hash, "host", 4, 0)))
                sstrncpy (vl->host, SvPV_nolen (*tmp), sizeof (vl->host));
@@ -548,9 +551,12 @@ static int hv2notification (pTHX_ HV *hash, notification_t *n)
                n->severity = NOTIF_FAILURE;
 
        if (NULL != (tmp = hv_fetch (hash, "time", 4, 0)))
-               n->time = (time_t)SvIV (*tmp);
+       {
+               double t = SvNV (*tmp);
+               n->time = DOUBLE_TO_CDTIME_T (t);
+       }
        else
-               n->time = time (NULL);
+               n->time = cdtime ();
 
        if (NULL != (tmp = hv_fetch (hash, "message", 7, 0)))
                sstrncpy (n->message, SvPV_nolen (*tmp), sizeof (n->message));
@@ -668,11 +674,17 @@ static int value_list2hv (pTHX_ value_list_t *vl, data_set_t *ds, HV *hash)
                return -1;
 
        if (0 != vl->time)
-               if (NULL == hv_store (hash, "time", 4, newSViv (vl->time), 0))
+       {
+               double t = CDTIME_T_TO_DOUBLE (vl->time);
+               if (NULL == hv_store (hash, "time", 4, newSVnv (t), 0))
                        return -1;
+       }
 
-       if (NULL == hv_store (hash, "interval", 8, newSViv (vl->interval), 0))
-               return -1;
+       {
+               double t = CDTIME_T_TO_DOUBLE (vl->interval);
+               if (NULL == hv_store (hash, "interval", 8, newSVnv (t), 0))
+                       return -1;
+       }
 
        if ('\0' != vl->host[0])
                if (NULL == hv_store (hash, "host", 4, newSVpv (vl->host, 0), 0))
@@ -750,8 +762,11 @@ static int notification2hv (pTHX_ notification_t *n, HV *hash)
                return -1;
 
        if (0 != n->time)
-               if (NULL == hv_store (hash, "time", 4, newSViv (n->time), 0))
+       {
+               double t = CDTIME_T_TO_DOUBLE (n->time);
+               if (NULL == hv_store (hash, "time", 4, newSVnv (t), 0))
                        return -1;
+       }
 
        if ('\0' != *n->message)
                if (NULL == hv_store (hash, "message", 7, newSVpv (n->message, 0), 0))
@@ -1102,11 +1117,15 @@ static int pplugin_call_all (pTHX_ int type, ...)
                XPUSHs (sv_2mortal (newRV_noinc ((SV *)notif)));
        }
        else if (PLUGIN_FLUSH == type) {
+               cdtime_t timeout;
+
                /*
                 * $_[0] = $timeout;
                 * $_[1] = $identifier;
                 */
-               XPUSHs (sv_2mortal (newSViv (va_arg (ap, int))));
+               timeout = va_arg (ap, cdtime_t);
+
+               XPUSHs (sv_2mortal (newSVnv (CDTIME_T_TO_DOUBLE (timeout))));
                XPUSHs (sv_2mortal (newSVpv (va_arg (ap, char *), 0)));
        }
 
@@ -1610,40 +1629,29 @@ static XS (Collectd_plugin_unregister_ds)
 static XS (Collectd_plugin_dispatch_values)
 {
        SV *values     = NULL;
-       int values_idx = 0;
 
        int ret = 0;
 
        dXSARGS;
 
-       if (2 == items) {
-               log_warn ("Collectd::plugin_dispatch_values with two arguments "
-                               "is deprecated - pass the type through values->{type}.");
-               values_idx = 1;
-       }
-       else if (1 != items) {
+       if (1 != items) {
                log_err ("Usage: Collectd::plugin_dispatch_values(values)");
                XSRETURN_EMPTY;
        }
 
        log_debug ("Collectd::plugin_dispatch_values: values=\"%s\"",
-                       SvPV_nolen (ST (values_idx)));
+                       SvPV_nolen (ST (/* stack index = */ 0)));
 
-       values = ST (values_idx);
+       values = ST (/* stack index = */ 0);
 
+       /* Make sure the argument is a hash reference. */
        if (! (SvROK (values) && (SVt_PVHV == SvTYPE (SvRV (values))))) {
                log_err ("Collectd::plugin_dispatch_values: Invalid values.");
                XSRETURN_EMPTY;
        }
 
-       if (((2 == items) && (NULL == ST (0))) || (NULL == values))
-               XSRETURN_EMPTY;
-
-       if ((2 == items) && (NULL == hv_store ((HV *)SvRV (values), "type", 4,
-                       newSVsv (ST (0)), 0))) {
-               log_err ("Collectd::plugin_dispatch_values: Could not store type.");
+       if (NULL == values)
                XSRETURN_EMPTY;
-       }
 
        ret = pplugin_dispatch_values (aTHX_ (HV *)SvRV (values));
 
@@ -1653,6 +1661,21 @@ static XS (Collectd_plugin_dispatch_values)
                XSRETURN_EMPTY;
 } /* static XS (Collectd_plugin_dispatch_values) */
 
+/*
+ * Collectd::plugin_get_interval ().
+ */
+static XS (Collectd_plugin_get_interval)
+{
+       dXSARGS;
+
+       /* make sure we don't get any unused variable warnings for 'items';
+        * don't abort, though */
+       if (items)
+               log_err ("Usage: Collectd::plugin_get_interval()");
+
+       XSRETURN_NV ((NV) CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
+} /* static XS (Collectd_plugin_get_interval) */
+
 /* Collectd::plugin_write (plugin, ds, vl).
  *
  * plugin:
@@ -2020,7 +2043,7 @@ static int perl_notify (const notification_t *notif,
        return pplugin_call_all (aTHX_ PLUGIN_NOTIF, notif);
 } /* static int perl_notify (const notification_t *) */
 
-static int perl_flush (int timeout, const char *identifier,
+static int perl_flush (cdtime_t timeout, const char *identifier,
                user_data_t __attribute__((unused)) *user_data)
 {
        dTHX;
@@ -2122,19 +2145,24 @@ static int g_pv_set (pTHX_ SV *var, MAGIC *mg)
        return 0;
 } /* static int g_pv_set (pTHX_ SV *, MAGIC *) */
 
-static int g_iv_get (pTHX_ SV *var, MAGIC *mg)
+static int g_interval_get (pTHX_ SV *var, MAGIC *mg)
 {
-       int *iv = (int *)mg->mg_ptr;
-       sv_setiv (var, *iv);
+       log_warn ("Accessing $interval_g is deprecated (and might not "
+                       "give the desired results) - plugin_get_interval() should "
+                       "be used instead.");
+       sv_setnv (var, CDTIME_T_TO_DOUBLE (interval_g));
        return 0;
-} /* static int g_iv_get (pTHX_ SV *, MAGIC *) */
+} /* static int g_interval_get (pTHX_ SV *, MAGIC *) */
 
-static int g_iv_set (pTHX_ SV *var, MAGIC *mg)
+static int g_interval_set (pTHX_ SV *var, MAGIC *mg)
 {
-       int *iv = (int *)mg->mg_ptr;
-       *iv = (int)SvIV (var);
+       double nv = (double)SvNV (var);
+       log_warn ("Accessing $interval_g is deprecated (and might not "
+                       "give the desired results) - plugin_get_interval() should "
+                       "be used instead.");
+       interval_g = DOUBLE_TO_CDTIME_T (nv);
        return 0;
-} /* static int g_iv_set (pTHX_ SV *, MAGIC *) */
+} /* static int g_interval_set (pTHX_ SV *, MAGIC *) */
 
 static MGVTBL g_pv_vtbl = {
        g_pv_get, g_pv_set, NULL, NULL, NULL, NULL, NULL
@@ -2142,8 +2170,8 @@ static MGVTBL g_pv_vtbl = {
                , NULL
 #endif
 };
-static MGVTBL g_iv_vtbl = {
-       g_iv_get, g_iv_set, NULL, NULL, NULL, NULL, NULL
+static MGVTBL g_interval_vtbl = {
+       g_interval_get, g_interval_set, NULL, NULL, NULL, NULL, NULL
 #if HAVE_PERL_STRUCT_MGVTBL_SVT_LOCAL
                , NULL
 #endif
@@ -2185,12 +2213,11 @@ static void xs_init (pTHX)
                                g_strings[i].var, 0);
        }
 
-       /* global integers */
-       for (i = 0; '\0' != g_integers[i].name[0]; ++i) {
-               tmp = get_sv (g_integers[i].name, 1);
-               sv_magicext (tmp, NULL, PERL_MAGIC_ext, &g_iv_vtbl,
-                               (char *)g_integers[i].var, 0);
-       }
+       tmp = get_sv ("Collectd::interval_g", /* create = */ 1);
+       sv_magicext (tmp, NULL, /* how = */ PERL_MAGIC_ext,
+                       /* vtbl = */ &g_interval_vtbl,
+                       /* name = */ NULL, /* namelen = */ 0);
+
        return;
 } /* static void xs_init (pTHX) */
 
diff --git a/src/pf.c b/src/pf.c
new file mode 100644 (file)
index 0000000..44f0c7b
--- /dev/null
+++ b/src/pf.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2010 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2011 Stefan Rinkes <stefan.rinkes@gmail.org>
+ *
+ * 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 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.
+ */
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.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>
+
+#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[SCNT_MAX+1] = SCNT_NAMES;
+
+static char const *pf_device = "/dev/pf";
+
+static void pf_submit (char const *type, char const *type_instance,
+               uint64_t val, _Bool is_gauge)
+{
+       value_t         values[1];
+       value_list_t    vl = VALUE_LIST_INIT;
+
+       if (is_gauge)
+               values[0].gauge = (gauge_t) val;
+       else
+               values[0].derive = (derive_t) val;
+
+       vl.values = values;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "pf", sizeof (vl.plugin));
+       sstrncpy (vl.type, type, sizeof(vl.type));
+       sstrncpy (vl.type_instance, type_instance, sizeof(vl.type_instance));
+
+       plugin_dispatch_values(&vl);
+} /* void pf_submit */
+
+static int pf_read (void)
+{
+       struct pf_status state;
+       int fd;
+       int status;
+       int i;
+
+       fd = open (pf_device, O_RDONLY);
+       if (fd < 0)
+       {
+               char errbuf[1024];
+               ERROR("pf plugin: Unable to open %s: %s",
+                               pf_device,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       memset (&state, 0, sizeof (state));
+       status = ioctl (fd, DIOCGETSTATUS, &state);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR("pf plugin: ioctl(DIOCGETSTATUS) failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               close(fd);
+               return (-1);
+       }
+
+       close (fd);
+       fd = -1;
+
+       if (!state.running)
+       {
+               WARNING ("pf plugin: PF is not running.");
+               return (-1);
+       }
+
+       for (i = 0; i < PFRES_MAX; i++)
+               pf_submit ("pf_counters", pf_reasons[i], state.counters[i],
+                               /* is gauge = */ 0);
+       for (i = 0; i < LCNT_MAX; i++)
+               pf_submit ("pf_limits", pf_lcounters[i], state.lcounters[i],
+                               /* is gauge = */ 0);
+       for (i = 0; i < FCNT_MAX; i++)
+               pf_submit ("pf_state", pf_fcounters[i], state.fcounters[i],
+                               /* is gauge = */ 0);
+       for (i = 0; i < SCNT_MAX; i++)
+               pf_submit ("pf_source", pf_scounters[i], state.scounters[i],
+                               /* is gauge = */ 0);
+
+       pf_submit ("pf_states", "current", (uint32_t) state.states,
+                       /* is gauge = */ 1);
+
+       return (0);
+} /* int pf_read */
+
+void module_register (void)
+{
+       plugin_register_read ("pf", pf_read);
+}
index a6fd06f..6879733 100644 (file)
@@ -617,7 +617,7 @@ static int plugin_config (oconfig_item_t *ci) /* {{{ */
     if (strcasecmp ("Address", child->key) == 0)
       cf_util_get_string (child, &conf_node);
     else if (strcasecmp ("Port", child->key) == 0)
-      cf_util_get_string (child, &conf_service);
+      cf_util_get_service (child, &conf_service);
     else if (strcasecmp ("View", child->key) == 0)
       pinba_config_view (child);
     else
@@ -645,7 +645,7 @@ static int plugin_init (void) /* {{{ */
   if (collector_thread_running)
     return (0);
 
-  status = pthread_create (&collector_thread_id,
+  status = plugin_thread_create (&collector_thread_id,
       /* attrs = */ NULL,
       collector_thread,
       /* args = */ NULL);
index ab1459e..8bbb807 100644 (file)
@@ -379,7 +379,7 @@ static int start_thread (void) /* {{{ */
 
   ping_thread_loop = 1;
   ping_thread_error = 0;
-  status = pthread_create (&ping_thread_id, /* attr = */ NULL,
+  status = plugin_thread_create (&ping_thread_id, /* attr = */ NULL,
       ping_thread, /* arg = */ (void *) 0);
   if (status != 0)
   {
index f4461b5..ed962a7 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/plugin.c
- * Copyright (C) 2005-2009  Florian octo Forster
+ * Copyright (C) 2005-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
  * 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>
  **/
 
 #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 "utils_threshold.h"
-#include "filter_chain.h"
+#include "utils_time.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+#include <ltdl.h>
 
 /*
  * Private structures
@@ -46,6 +45,7 @@ struct callback_func_s
 {
        void *cf_callback;
        user_data_t cf_udata;
+       plugin_ctx_t cf_ctx;
 };
 typedef struct callback_func_s callback_func_t;
 
@@ -58,22 +58,33 @@ struct read_func_s
         * The `rf_super' member MUST be the first one in this structure! */
 #define rf_callback rf_super.cf_callback
 #define rf_udata rf_super.cf_udata
+#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];
        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 llist_t *list_init;
 static llist_t *list_write;
 static llist_t *list_flush;
+static llist_t *list_missing;
 static llist_t *list_shutdown;
 static llist_t *list_log;
 static llist_t *list_notification;
@@ -93,9 +104,22 @@ 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 _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 functions
  */
+static int plugin_dispatch_values_internal (value_list_t *vl);
+
 static const char *plugin_get_dir (void)
 {
        if (plugindir == NULL)
@@ -246,6 +270,8 @@ static int create_register_callback (llist_t **list, /* {{{ */
                cf->cf_udata = *ud;
        }
 
+       cf->cf_ctx = plugin_get_ctx ();
+
        return (register_callback (list, name, cf));
 } /* }}} int create_register_callback */
 
@@ -291,7 +317,7 @@ static int plugin_load_file (char *file, uint32_t flags)
                dlh = lt_dlopenadvise(file, advise);
                lt_dladvise_destroy(&advise);
        } else {
-               dlh = lt_dlopen (file);
+               dlh = lt_dlopen (file);
        }
 #else /* if LIBTOOL_VERSION == 1 */
        if (flags & PLUGIN_FLAGS_GLOBAL)
@@ -334,52 +360,40 @@ 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)
        {
                read_func_t *rf;
-               struct timeval now;
+               plugin_ctx_t old_ctx;
+               cdtime_t now;
                int status;
                int rf_type;
                int rc;
 
-               /* Get the read function that needs to be read next. */
+               /* Get the read function that needs to be read next.
+                * We don't need to hold "read_lock" for the heap, but we need
+                * to call c_heap_get_root() and pthread_cond_wait() in the
+                * same protected block. */
+               pthread_mutex_lock (&read_lock);
                rf = c_heap_get_root (read_heap);
                if (rf == NULL)
                {
-                       struct timespec abstime;
-
-                       gettimeofday (&now, /* timezone = */ NULL);
-
-                       abstime.tv_sec = now.tv_sec + interval_g;
-                       abstime.tv_nsec = 1000 * now.tv_usec;
-
-                       pthread_mutex_lock (&read_lock);
-                       pthread_cond_timedwait (&read_cond, &read_lock,
-                                       &abstime);
-                       pthread_mutex_unlock (&read_lock);
+                       pthread_cond_wait (&read_cond, &read_lock);
+                        pthread_mutex_unlock (&read_lock);
                        continue;
                }
+               pthread_mutex_unlock (&read_lock);
 
-               if ((rf->rf_interval.tv_sec == 0) && (rf->rf_interval.tv_nsec == 0))
+               if (rf->rf_interval == 0)
                {
-                       gettimeofday (&now, /* timezone = */ NULL);
-
-                       rf->rf_interval.tv_sec = interval_g;
-                       rf->rf_interval.tv_nsec = 0;
-
+                       /* this should not happen, because the interval is set
+                        * for each plugin when loading it
+                        * XXX: issue a warning? */
+                       rf->rf_interval = plugin_get_interval ();
                        rf->rf_effective_interval = rf->rf_interval;
 
-                       rf->rf_next_read.tv_sec = now.tv_sec;
-                       rf->rf_next_read.tv_nsec = 1000 * now.tv_usec;
+                       rf->rf_next_read = cdtime ();
                }
 
                /* sleep until this entry is due,
@@ -391,11 +405,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'. */
@@ -426,6 +444,8 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
 
                DEBUG ("plugin_read_thread: Handling `%s'.", rf->rf_name);
 
+               old_ctx = plugin_set_ctx (rf->rf_ctx);
+
                if (rf_type == RF_SIMPLE)
                {
                        int (*callback) (void);
@@ -443,24 +463,20 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                        status = (*callback) (&rf->rf_udata);
                }
 
+               plugin_set_ctx (old_ctx);
+
                /* If the function signals failure, we will increase the
                 * 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
                {
@@ -469,38 +485,29 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                }
 
                /* update the ``next read due'' field */
-               gettimeofday (&now, /* timezone = */ NULL);
+               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 ((rf->rf_next_read.tv_sec < now.tv_sec)
-                               || ((rf->rf_next_read.tv_sec == now.tv_sec)
-                                       && (rf->rf_next_read.tv_nsec < (1000 * now.tv_usec))))
+               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. */
-                       rf->rf_next_read.tv_sec = now.tv_sec;
-                       rf->rf_next_read.tv_nsec = 1000 * now.tv_usec;
+                       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);
@@ -567,6 +574,244 @@ 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;
+       }
+       else
+       {
+               write_queue_tail->next = q;
+               write_queue_tail = q;
+       }
+
+       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;
+       if (write_queue_head == NULL)
+               write_queue_tail = NULL;
+
+       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;
+       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
  */
@@ -598,8 +843,6 @@ int plugin_load (const char *type, uint32_t flags)
        struct dirent *de;
        int status;
 
-       DEBUG ("type = %s", type);
-
        dir = plugin_get_dir ();
        ret = 1;
 
@@ -608,7 +851,7 @@ int plugin_load (const char *type, uint32_t flags)
        status = ssnprintf (typename, sizeof (typename), "%s.so", type);
        if ((status < 0) || ((size_t) status >= sizeof (typename)))
        {
-               WARNING ("snprintf: truncated: `%s.so'", type);
+               WARNING ("plugin_load: Filename too long: \"%s.so\"", type);
                return (-1);
        }
        typename_len = strlen (typename);
@@ -616,7 +859,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);
        }
@@ -630,25 +873,29 @@ 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 */
                        ret = 0;
@@ -656,14 +903,16 @@ int plugin_load (const char *type, uint32_t flags)
                }
                else
                {
-                       fprintf (stderr, "Unable to load plugin %s.\n", type);
+                       ERROR ("plugin_load: Load plugin \"%s\" failed with "
+                                       "status %i.", type, 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",
+                               type, dir);
 
        return (ret);
 }
@@ -700,13 +949,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);
@@ -720,6 +965,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)
@@ -744,6 +992,17 @@ static int plugin_insert_read (read_func_t *rf)
                }
        }
 
+       le = llist_search (read_list, rf->rf_name);
+       if (le != NULL)
+       {
+               pthread_mutex_unlock (&read_lock);
+               WARNING ("The read function \"%s\" is already registered. "
+                               "Check for duplicate \"LoadPlugin\" lines "
+                               "in your configuration!",
+                               rf->rf_name);
+               return (EINVAL);
+       }
+
        le = llentry_create (rf->rf_name, rf);
        if (le == NULL)
        {
@@ -764,6 +1023,8 @@ static int plugin_insert_read (read_func_t *rf)
        /* This does not fail. */
        llist_append (read_list, le);
 
+       /* Wake up all the read threads. */
+       pthread_cond_broadcast (&read_cond);
        pthread_mutex_unlock (&read_lock);
        return (0);
 } /* int plugin_insert_read */
@@ -772,28 +1033,30 @@ int plugin_register_read (const char *name,
                int (*callback) (void))
 {
        read_func_t *rf;
+       int status;
 
-       rf = (read_func_t *) malloc (sizeof (read_func_t));
+       rf = malloc (sizeof (*rf));
        if (rf == NULL)
        {
-               char errbuf[1024];
-               ERROR ("plugin_register_read: malloc failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (-1);
+               ERROR ("plugin_register_read: malloc failed.");
+               return (ENOMEM);
        }
 
        memset (rf, 0, sizeof (read_func_t));
        rf->rf_callback = (void *) callback;
        rf->rf_udata.data = NULL;
        rf->rf_udata.free_func = NULL;
+       rf->rf_ctx = plugin_get_ctx ();
        rf->rf_group[0] = '\0';
        sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
        rf->rf_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 ();
 
-       return (plugin_insert_read (rf));
+       status = plugin_insert_read (rf);
+       if (status != 0)
+               sfree (rf);
+
+       return (status);
 } /* int plugin_register_read */
 
 int plugin_register_complex_read (const char *group, const char *name,
@@ -802,12 +1065,13 @@ int plugin_register_complex_read (const char *group, const char *name,
                user_data_t *user_data)
 {
        read_func_t *rf;
+       int status;
 
-       rf = (read_func_t *) malloc (sizeof (read_func_t));
+       rf = malloc (sizeof (*rf));
        if (rf == NULL)
        {
                ERROR ("plugin_register_complex_read: malloc failed.");
-               return (-1);
+               return (ENOMEM);
        }
 
        memset (rf, 0, sizeof (read_func_t));
@@ -819,10 +1083,9 @@ int plugin_register_complex_read (const char *group, const char *name,
        sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
        rf->rf_type = RF_COMPLEX;
        if (interval != NULL)
-       {
-               rf->rf_interval = *interval;
-       }
-       rf->rf_effective_interval = rf->rf_interval;
+               rf->rf_interval = TIMESPEC_TO_CDTIME_T (interval);
+       else
+               rf->rf_interval = plugin_get_interval ();
 
        /* Set user data */
        if (user_data == NULL)
@@ -835,7 +1098,13 @@ int plugin_register_complex_read (const char *group, const char *name,
                rf->rf_udata = *user_data;
        }
 
-       return (plugin_insert_read (rf));
+       rf->rf_ctx = plugin_get_ctx ();
+
+       status = plugin_insert_read (rf);
+       if (status != 0)
+               sfree (rf);
+
+       return (status);
 } /* int plugin_register_complex_read */
 
 int plugin_register_write (const char *name,
@@ -852,6 +1121,13 @@ int plugin_register_flush (const char *name,
                                (void *) callback, ud));
 } /* int plugin_register_flush */
 
+int plugin_register_missing (const char *name,
+               plugin_missing_cb callback, user_data_t *ud)
+{
+       return (create_register_callback (&list_missing, name,
+                               (void *) callback, ud));
+} /* int plugin_register_missing */
+
 int plugin_register_shutdown (const char *name,
                int (*callback) (void))
 {
@@ -859,6 +1135,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;
@@ -1038,6 +1335,11 @@ int plugin_unregister_flush (const char *name)
        return (plugin_unregister (list_flush, name));
 }
 
+int plugin_unregister_missing (const char *name)
+{
+       return (plugin_unregister (list_missing, name));
+}
+
 int plugin_unregister_shutdown (const char *name)
 {
        return (plugin_unregister (list_shutdown, name));
@@ -1084,6 +1386,15 @@ void plugin_init_all (void)
        chain_name = global_option_get ("PostCacheChain");
        post_cache_chain = fc_chain_get_by_name (chain_name);
 
+       {
+               char const *tmp = global_option_get ("WriteThreads");
+               int num = atoi (tmp);
+
+               if (num < 1)
+                       num = 5;
+
+               start_write_threads ((size_t) num);
+       }
 
        if ((list_init == NULL) && (read_heap == NULL))
                return;
@@ -1096,10 +1407,13 @@ void plugin_init_all (void)
        {
                callback_func_t *cf;
                plugin_init_cb callback;
+               plugin_ctx_t old_ctx;
 
                cf = le->value;
+               old_ctx = plugin_set_ctx (cf->cf_ctx);
                callback = cf->cf_callback;
                status = (*callback) ();
+               plugin_set_ctx (old_ctx);
 
                if (status != 0)
                {
@@ -1152,11 +1466,14 @@ int plugin_read_all_once (void)
        while (42)
        {
                read_func_t *rf;
+               plugin_ctx_t old_ctx;
 
                rf = c_heap_get_root (read_heap);
                if (rf == NULL)
                        break;
 
+               old_ctx = plugin_set_ctx (rf->rf_ctx);
+
                if (rf->rf_type == RF_SIMPLE)
                {
                        int (*callback) (void);
@@ -1172,6 +1489,8 @@ int plugin_read_all_once (void)
                        status = (*callback) (&rf->rf_udata);
                }
 
+               plugin_set_ctx (old_ctx);
+
                if (status != 0)
                {
                        NOTICE ("read-function of plugin `%s' failed.",
@@ -1218,6 +1537,9 @@ int plugin_write (const char *plugin, /* {{{ */
       callback_func_t *cf = le->value;
       plugin_write_cb callback;
 
+      /* do not switch plugin context; rather keep the context (interval)
+       * information of the calling read plugin */
+
       DEBUG ("plugin: plugin_write: Writing values via %s.", le->key);
       callback = cf->cf_callback;
       status = (*callback) (ds, vl, &cf->cf_udata);
@@ -1253,6 +1575,9 @@ int plugin_write (const char *plugin, /* {{{ */
 
     cf = le->value;
 
+    /* do not switch plugin context; rather keep the context (interval)
+     * information of the calling read plugin */
+
     DEBUG ("plugin: plugin_write: Writing values via %s.", le->key);
     callback = cf->cf_callback;
     status = (*callback) (ds, vl, &cf->cf_udata);
@@ -1261,7 +1586,7 @@ int plugin_write (const char *plugin, /* {{{ */
   return (status);
 } /* }}} int plugin_write */
 
-int plugin_flush (const char *plugin, int timeout, const char *identifier)
+int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier)
 {
   llentry_t *le;
 
@@ -1273,6 +1598,7 @@ int plugin_flush (const char *plugin, int timeout, const char *identifier)
   {
     callback_func_t *cf;
     plugin_flush_cb callback;
+    plugin_ctx_t old_ctx;
 
     if ((plugin != NULL)
         && (strcmp (plugin, le->key) != 0))
@@ -1282,10 +1608,13 @@ int plugin_flush (const char *plugin, int timeout, const char *identifier)
     }
 
     cf = le->value;
+    old_ctx = plugin_set_ctx (cf->cf_ctx);
     callback = cf->cf_callback;
 
     (*callback) (timeout, identifier, &cf->cf_udata);
 
+    plugin_set_ctx (old_ctx);
+
     le = le->next;
   }
   return (0);
@@ -1306,7 +1635,8 @@ void plugin_shutdown_all (void)
 
        destroy_read_heap ();
 
-       plugin_flush (/* plugin = */ NULL, /* timeout = */ -1,
+       plugin_flush (/* plugin = */ NULL,
+                       /* timeout = */ 0,
                        /* identifier = */ NULL);
 
        le = NULL;
@@ -1317,8 +1647,10 @@ void plugin_shutdown_all (void)
        {
                callback_func_t *cf;
                plugin_shutdown_cb callback;
+               plugin_ctx_t old_ctx;
 
                cf = le->value;
+               old_ctx = plugin_set_ctx (cf->cf_ctx);
                callback = cf->cf_callback;
 
                /* Advance the pointer before calling the callback allows
@@ -1328,22 +1660,70 @@ void plugin_shutdown_all (void)
                le = le->next;
 
                (*callback) ();
+
+               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
         * the real free function when registering the write callback. This way
         * the data isn't freed twice. */
        destroy_all_callbacks (&list_flush);
+       destroy_all_callbacks (&list_missing);
        destroy_all_callbacks (&list_write);
 
        destroy_all_callbacks (&list_notification);
        destroy_all_callbacks (&list_shutdown);
        destroy_all_callbacks (&list_log);
+
+       plugin_free_data_sets ();
 } /* void plugin_shutdown_all */
 
-int plugin_dispatch_values (value_list_t *vl)
+int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
+{
+  llentry_t *le;
+
+  if (list_missing == NULL)
+    return (0);
+
+  le = llist_head (list_missing);
+  while (le != NULL)
+  {
+    callback_func_t *cf;
+    plugin_missing_cb callback;
+    plugin_ctx_t old_ctx;
+    int status;
+
+    cf = le->value;
+    old_ctx = plugin_set_ctx (cf->cf_ctx);
+    callback = cf->cf_callback;
+
+    status = (*callback) (vl, &cf->cf_udata);
+    plugin_set_ctx (old_ctx);
+    if (status != 0)
+    {
+      if (status < 0)
+      {
+        ERROR ("plugin_dispatch_missing: Callback function \"%s\" "
+            "failed with status %i.",
+            le->key, status);
+        return (status);
+      }
+      else
+      {
+        return (0);
+      }
+    }
+
+    le = le->next;
+  }
+  return (0);
+} /* int }}} plugin_dispatch_missing */
+
+static int plugin_dispatch_values_internal (value_list_t *vl)
 {
        int status;
        static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC;
@@ -1394,17 +1774,17 @@ int plugin_dispatch_values (value_list_t *vl)
                return (-1);
        }
 
-       if (vl->time == 0)
-               vl->time = time (NULL);
-
-       if (vl->interval <= 0)
-               vl->interval = interval_g;
+       /* 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 = %u; interval = %i; "
+       DEBUG ("plugin_dispatch_values: time = %.3f; interval = %.3f; "
                        "host = %s; "
                        "plugin = %s; plugin_instance = %s; "
                        "type = %s; type_instance = %s;",
-                       (unsigned int) vl->time, vl->interval,
+                       CDTIME_T_TO_DOUBLE (vl->time),
+                       CDTIME_T_TO_DOUBLE (vl->interval),
                        vl->host,
                        vl->plugin, vl->plugin_instance,
                        vl->type, vl->type_instance);
@@ -1488,9 +1868,6 @@ int plugin_dispatch_values (value_list_t *vl)
        /* Update the value cache */
        uc_update (ds, vl);
 
-       /* Initiate threshold checking */
-       ut_check_threshold (ds, vl);
-
        if (post_cache_chain != NULL)
        {
                status = fc_process_chain (ds, vl, post_cache_chain);
@@ -1521,53 +1898,24 @@ 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)
+int plugin_dispatch_values (value_list_t const *vl)
 {
-  value_list_t vl_copy;
-  int status;
-
-  if (vl == NULL)
-    return EINVAL;
-
-  memcpy (&vl_copy, vl, sizeof (vl_copy));
-
-  /* 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));
-
-  /* Set pointers to NULL, just to be on the save side. */
-  vl_copy.values = NULL;
-  vl_copy.meta = NULL;
-
-  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);
-
-  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) */
-
-  status = plugin_dispatch_values (&vl_copy);
+       int status;
 
-  meta_data_destroy (vl_copy.meta);
-  free (vl_copy.values);
+       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 (status);
-} /* int plugin_dispatch_values_secure */
+       return (0);
+}
 
 int plugin_dispatch_notification (const notification_t *notif)
 {
@@ -1575,9 +1923,9 @@ int plugin_dispatch_notification (const notification_t *notif)
        /* Possible TODO: Add flap detection here */
 
        DEBUG ("plugin_dispatch_notification: severity = %i; message = %s; "
-                       "time = %u; host = %s;",
+                       "time = %.3f; host = %s;",
                        notif->severity, notif->message,
-                       (unsigned int) notif->time, notif->host);
+                       CDTIME_T_TO_DOUBLE (notif->time), notif->host);
 
        /* Nobody cares for notifications */
        if (list_notification == NULL)
@@ -1590,6 +1938,9 @@ int plugin_dispatch_notification (const notification_t *notif)
                plugin_notification_cb callback;
                int status;
 
+               /* do not switch plugin context; rather keep the context
+                * (interval) information of the calling plugin */
+
                cf = le->value;
                callback = cf->cf_callback;
                status = (*callback) (notif, &cf->cf_udata);
@@ -1637,16 +1988,63 @@ void plugin_log (int level, const char *format, ...)
                cf = le->value;
                callback = cf->cf_callback;
 
+               /* do not switch plugin context; rather keep the context
+                * (interval) information of the calling plugin */
+
                (*callback) (level, msg, &cf->cf_udata);
 
                le = le->next;
        }
 } /* void plugin_log */
 
+int parse_log_severity (const char *severity)
+{
+       int log_level = -1;
+
+       if ((0 == strcasecmp (severity, "emerg"))
+                       || (0 == strcasecmp (severity, "alert"))
+                       || (0 == strcasecmp (severity, "crit"))
+                       || (0 == strcasecmp (severity, "err")))
+               log_level = LOG_ERR;
+       else if (0 == strcasecmp (severity, "warning"))
+               log_level = LOG_WARNING;
+       else if (0 == strcasecmp (severity, "notice"))
+               log_level = LOG_NOTICE;
+       else if (0 == strcasecmp (severity, "info"))
+               log_level = LOG_INFO;
+#if COLLECT_DEBUG
+       else if (0 == strcasecmp (severity, "debug"))
+               log_level = LOG_DEBUG;
+#endif /* COLLECT_DEBUG */
+
+       return (log_level);
+} /* int parse_log_severity */
+
+int parse_notif_severity (const char *severity)
+{
+       int notif_severity = -1;
+
+       if (strcasecmp (severity, "FAILURE") == 0)
+               notif_severity = NOTIF_FAILURE;
+       else if (strcmp (severity, "OKAY") == 0)
+               notif_severity = NOTIF_OKAY;
+       else if ((strcmp (severity, "WARNING") == 0)
+                       || (strcmp (severity, "WARN") == 0))
+               notif_severity = NOTIF_WARNING;
+
+       return (notif_severity);
+} /* int parse_notif_severity */
+
 const data_set_t *plugin_get_ds (const char *name)
 {
        data_set_t *ds;
 
+       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);
@@ -1711,7 +2109,7 @@ static int plugin_notification_meta_add (notification_t *n,
     }
     case NM_TYPE_BOOLEAN:
     {
-      meta->nm_value.nm_boolean = *((bool *) value);
+      meta->nm_value.nm_boolean = *((_Bool *) value);
       break;
     }
     default:
@@ -1765,7 +2163,7 @@ int plugin_notification_meta_add_double (notification_t *n,
 
 int plugin_notification_meta_add_boolean (notification_t *n,
     const char *name,
-    bool value)
+    _Bool value)
 {
   return (plugin_notification_meta_add (n, name, NM_TYPE_BOOLEAN, &value));
 }
@@ -1831,4 +2229,122 @@ int plugin_notification_meta_free (notification_meta_t *n)
   return (0);
 } /* int plugin_notification_meta_free */
 
+static void plugin_ctx_destructor (void *ctx)
+{
+       sfree (ctx);
+} /* void plugin_ctx_destructor */
+
+static plugin_ctx_t ctx_init = { /* interval = */ 0 };
+
+static plugin_ctx_t *plugin_ctx_create (void)
+{
+       plugin_ctx_t *ctx;
+
+       ctx = malloc (sizeof (*ctx));
+       if (ctx == NULL) {
+               char errbuf[1024];
+               ERROR ("Failed to allocate plugin context: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return NULL;
+       }
+
+       *ctx = ctx_init;
+       assert (plugin_ctx_key_initialized);
+       pthread_setspecific (plugin_ctx_key, ctx);
+       DEBUG("Created new plugin context.");
+       return (ctx);
+} /* int plugin_ctx_create */
+
+void plugin_init_ctx (void)
+{
+       pthread_key_create (&plugin_ctx_key, plugin_ctx_destructor);
+       plugin_ctx_key_initialized = 1;
+} /* void plugin_init_ctx */
+
+plugin_ctx_t plugin_get_ctx (void)
+{
+       plugin_ctx_t *ctx;
+
+       assert (plugin_ctx_key_initialized);
+       ctx = pthread_getspecific (plugin_ctx_key);
+
+       if (ctx == NULL) {
+               ctx = plugin_ctx_create ();
+               /* this must no happen -- exit() instead? */
+               if (ctx == NULL)
+                       return ctx_init;
+       }
+
+       return (*ctx);
+} /* plugin_ctx_t plugin_get_ctx */
+
+plugin_ctx_t plugin_set_ctx (plugin_ctx_t ctx)
+{
+       plugin_ctx_t *c;
+       plugin_ctx_t old;
+
+       assert (plugin_ctx_key_initialized);
+       c = pthread_getspecific (plugin_ctx_key);
+
+       if (c == NULL) {
+               c = plugin_ctx_create ();
+               /* this must no happen -- exit() instead? */
+               if (c == NULL)
+                       return ctx_init;
+       }
+
+       old = *c;
+       *c = ctx;
+
+       return (old);
+} /* void plugin_set_ctx */
+
+cdtime_t plugin_get_interval (void)
+{
+       cdtime_t interval;
+
+       interval = plugin_get_ctx().interval;
+       if (interval > 0)
+               return interval;
+
+       return cf_get_default_interval ();
+} /* cdtime_t plugin_get_interval */
+
+typedef struct {
+       plugin_ctx_t ctx;
+       void *(*start_routine) (void *);
+       void *arg;
+} plugin_thread_t;
+
+static void *plugin_thread_start (void *arg)
+{
+       plugin_thread_t *plugin_thread = arg;
+
+       void *(*start_routine) (void *) = plugin_thread->start_routine;
+       void *plugin_arg = plugin_thread->arg;
+
+       plugin_set_ctx (plugin_thread->ctx);
+
+       free (plugin_thread);
+
+       return start_routine (plugin_arg);
+} /* void *plugin_thread_start */
+
+int plugin_thread_create (pthread_t *thread, const pthread_attr_t *attr,
+               void *(*start_routine) (void *), void *arg)
+{
+       plugin_thread_t *plugin_thread;
+
+       plugin_thread = malloc (sizeof (*plugin_thread));
+       if (plugin_thread == NULL)
+               return -1;
+
+       plugin_thread->ctx           = plugin_get_ctx ();
+       plugin_thread->start_routine = start_routine;
+       plugin_thread->arg           = arg;
+
+       return pthread_create (thread, attr,
+                       plugin_thread_start, plugin_thread);
+} /* int plugin_thread_create */
+
 /* vim: set sw=8 ts=8 noet fdm=marker : */
index 0b34ab1..635ff30 100644 (file)
  * 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>
  **/
 
 #include "collectd.h"
 #include "configfile.h"
 #include "meta_data.h"
+#include "utils_time.h"
 
 #define PLUGIN_FLAGS_GLOBAL 0x0001
 
@@ -64,6 +65,8 @@
 #define NOTIF_WARNING 2
 #define NOTIF_OKAY    4
 
+#define plugin_interval (plugin_get_ctx().interval)
+
 /*
  * Public data types
  */
@@ -85,8 +88,8 @@ struct value_list_s
 {
        value_t *values;
        int      values_len;
-       time_t   time;
-       int      interval;
+       cdtime_t time;
+       cdtime_t interval;
        char     host[DATA_MAX_NAME_LEN];
        char     plugin[DATA_MAX_NAME_LEN];
        char     plugin_instance[DATA_MAX_NAME_LEN];
@@ -96,7 +99,8 @@ struct value_list_s
 };
 typedef struct value_list_s value_list_t;
 
-#define VALUE_LIST_INIT { NULL, 0, 0, interval_g, "localhost", "", "", "", "", NULL }
+#define VALUE_LIST_INIT { NULL, 0, 0, plugin_get_interval (), \
+       "localhost", "", "", "", "", NULL }
 #define VALUE_LIST_STATIC { NULL, 0, 0, 0, "localhost", "", "", "", "", NULL }
 
 struct data_source_s
@@ -135,7 +139,7 @@ typedef struct notification_meta_s
                int64_t nm_signed_int;
                uint64_t nm_unsigned_int;
                double nm_double;
-               bool nm_boolean;
+               _Bool nm_boolean;
        } nm_value;
        struct notification_meta_s *next;
 } notification_meta_t;
@@ -143,7 +147,7 @@ typedef struct notification_meta_s
 typedef struct notification_s
 {
        int    severity;
-       time_t time;
+       cdtime_t time;
        char   message[NOTIF_MAX_MSG_LEN];
        char   host[DATA_MAX_NAME_LEN];
        char   plugin[DATA_MAX_NAME_LEN];
@@ -160,6 +164,12 @@ struct user_data_s
 };
 typedef struct user_data_s user_data_t;
 
+struct plugin_ctx_s
+{
+       cdtime_t interval;
+};
+typedef struct plugin_ctx_s plugin_ctx_t;
+
 /*
  * Callback types
  */
@@ -167,8 +177,12 @@ typedef int (*plugin_init_cb) (void);
 typedef int (*plugin_read_cb) (user_data_t *);
 typedef int (*plugin_write_cb) (const data_set_t *, const value_list_t *,
                user_data_t *);
-typedef int (*plugin_flush_cb) (int timeout, const char *identifier,
+typedef int (*plugin_flush_cb) (cdtime_t timeout, const char *identifier,
                user_data_t *);
+/* "missing" callback. Returns less than zero on failure, zero if other
+ * callbacks should be called, greater than zero if no more callbacks should be
+ * called. */
+typedef int (*plugin_missing_cb) (const value_list_t *, user_data_t *);
 typedef void (*plugin_log_cb) (int severity, const char *message,
                user_data_t *);
 typedef int (*plugin_shutdown_cb) (void);
@@ -248,7 +262,7 @@ void plugin_shutdown_all (void);
 int plugin_write (const char *plugin,
     const data_set_t *ds, const value_list_t *vl);
 
-int plugin_flush (const char *plugin, int timeout, const char *identifier);
+int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier);
 
 /*
  * The `plugin_register_*' functions are used to make `config', `init',
@@ -264,6 +278,8 @@ int plugin_register_init (const char *name,
                plugin_init_cb callback);
 int plugin_register_read (const char *name,
                int (*callback) (void));
+/* "user_data" will be freed automatically, unless
+ * "plugin_register_complex_read" returns an error (non-zero). */
 int plugin_register_complex_read (const char *group, const char *name,
                plugin_read_cb callback,
                const struct timespec *interval,
@@ -272,6 +288,8 @@ int plugin_register_write (const char *name,
                plugin_write_cb callback, user_data_t *user_data);
 int plugin_register_flush (const char *name,
                plugin_flush_cb callback, user_data_t *user_data);
+int plugin_register_missing (const char *name,
+               plugin_missing_cb callback, user_data_t *user_data);
 int plugin_register_shutdown (const char *name,
                plugin_shutdown_cb callback);
 int plugin_register_data_set (const data_set_t *ds);
@@ -287,6 +305,7 @@ int plugin_unregister_read (const char *name);
 int plugin_unregister_read_group (const char *group);
 int plugin_unregister_write (const char *name);
 int plugin_unregister_flush (const char *name);
+int plugin_unregister_missing (const char *name);
 int plugin_unregister_shutdown (const char *name);
 int plugin_unregister_data_set (const char *name);
 int plugin_unregister_log (const char *name);
@@ -307,14 +326,18 @@ 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);
+int plugin_dispatch_missing (const value_list_t *vl);
 
 int plugin_dispatch_notification (const notification_t *notif);
 
 void plugin_log (int level, const char *format, ...)
        __attribute__ ((format(printf,2,3)));
 
+/* These functions return the parsed severity or less than zero on failure. */
+int parse_log_severity (const char *severity);
+int parse_notif_severity (const char *severity);
+
 #define ERROR(...)   plugin_log (LOG_ERR,     __VA_ARGS__)
 #define WARNING(...) plugin_log (LOG_WARNING, __VA_ARGS__)
 #define NOTICE(...)  plugin_log (LOG_NOTICE,  __VA_ARGS__)
@@ -341,11 +364,38 @@ int plugin_notification_meta_add_double (notification_t *n,
     double value);
 int plugin_notification_meta_add_boolean (notification_t *n,
     const char *name,
-    bool value);
+    _Bool value);
 
 int plugin_notification_meta_copy (notification_t *dst,
     const notification_t *src);
 
 int plugin_notification_meta_free (notification_meta_t *n);
 
+/*
+ * Plugin context management.
+ */
+
+void plugin_init_ctx (void);
+
+plugin_ctx_t plugin_get_ctx (void);
+plugin_ctx_t plugin_set_ctx (plugin_ctx_t ctx);
+
+/*
+ * NAME
+ *  plugin_get_interval
+ *
+ * DESCRIPTION
+ *  This function returns the current value of the plugin's interval. The
+ *  return value will be strictly greater than zero in all cases. If
+ *  everything else fails, it will fall back to 10 seconds.
+ */
+cdtime_t plugin_get_interval (void);
+
+/*
+ * Context-aware thread management.
+ */
+
+int plugin_thread_create (pthread_t *thread, const pthread_attr_t *attr,
+               void *(*start_routine) (void *), void *arg);
+
 #endif /* PLUGIN_H */
index dd53cb4..1eaf6bd 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/postgresql.c
- * Copyright (C) 2008, 2009  Sebastian Harl
- * Copyright (C) 2009        Florian Forster
+ * Copyright (C) 2008-2012  Sebastian Harl
+ * Copyright (C) 2009       Florian Forster
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "configfile.h"
 #include "plugin.h"
 
+#include "utils_cache.h"
 #include "utils_db_query.h"
 #include "utils_complain.h"
 
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
 #include <pg_config_manual.h>
 #include <libpq-fe.h>
 
 #define log_err(...) ERROR ("postgresql: " __VA_ARGS__)
 #define log_warn(...) WARNING ("postgresql: " __VA_ARGS__)
 #define log_info(...) INFO ("postgresql: " __VA_ARGS__)
+#define log_debug(...) DEBUG ("postgresql: " __VA_ARGS__)
 
 #ifndef C_PSQL_DEFAULT_CONF
 # define C_PSQL_DEFAULT_CONF PKGDATADIR "/postgresql_default.conf"
@@ -95,6 +101,7 @@ typedef enum {
        C_PSQL_PARAM_DB,
        C_PSQL_PARAM_USER,
        C_PSQL_PARAM_INTERVAL,
+       C_PSQL_PARAM_INSTANCE,
 } c_psql_param_t;
 
 /* Parameter configuration. Stored as `user data' in the query objects. */
@@ -104,6 +111,12 @@ typedef struct {
 } c_psql_user_data_t;
 
 typedef struct {
+       char *name;
+       char *statement;
+       _Bool store_rates;
+} c_psql_writer_t;
+
+typedef struct {
        PGconn      *conn;
        c_complain_t conn_complaint;
 
@@ -117,7 +130,17 @@ typedef struct {
        udb_query_t    **queries;
        size_t           queries_num;
 
-       int interval;
+       c_psql_writer_t **writers;
+       size_t            writers_num;
+
+       /* make sure we don't access the database object in parallel */
+       pthread_mutex_t   db_lock;
+
+       cdtime_t interval;
+
+       /* writer "caching" settings */
+       cdtime_t commit_interval;
+       cdtime_t next_commit;
 
        char *host;
        char *port;
@@ -125,11 +148,15 @@ typedef struct {
        char *user;
        char *password;
 
+       char *instance;
+
        char *sslmode;
 
        char *krbsrvname;
 
        char *service;
+
+       int ref_cnt;
 } c_psql_database_t;
 
 static char *def_queries[] = {
@@ -143,19 +170,77 @@ static char *def_queries[] = {
 };
 static int def_queries_num = STATIC_ARRAY_SIZE (def_queries);
 
-static udb_query_t      **queries       = NULL;
-static size_t             queries_num   = 0;
+static c_psql_database_t **databases     = NULL;
+static size_t              databases_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 int c_psql_begin (c_psql_database_t *db)
+{
+       PGresult *r = PQexec (db->conn, "BEGIN");
+
+       int status = 1;
+
+       if (r != NULL) {
+               if (PGRES_COMMAND_OK == PQresultStatus (r)) {
+                       db->next_commit = cdtime() + db->commit_interval;
+                       status = 0;
+               }
+               else
+                       log_warn ("Failed to initiate ('BEGIN') transaction: %s",
+                                       PQerrorMessage (db->conn));
+               PQclear (r);
+       }
+       return status;
+} /* c_psql_begin */
+
+static int c_psql_commit (c_psql_database_t *db)
+{
+       PGresult *r = PQexec (db->conn, "COMMIT");
+
+       int status = 1;
+
+       if (r != NULL) {
+               if (PGRES_COMMAND_OK == PQresultStatus (r)) {
+                       db->next_commit = 0;
+                       log_debug ("Successfully committed transaction.");
+                       status = 0;
+               }
+               else
+                       log_warn ("Failed to commit transaction: %s",
+                                       PQerrorMessage (db->conn));
+               PQclear (r);
+       }
+       return status;
+} /* c_psql_commit */
 
 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 *)malloc (sizeof (*db));
+       db = (c_psql_database_t *)malloc (sizeof(*db));
        if (NULL == db) {
                log_err ("Out of memory.");
                return NULL;
        }
 
+       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;
 
        C_COMPLAIN_INIT (&db->conn_complaint);
@@ -169,19 +254,31 @@ static c_psql_database_t *c_psql_database_new (const char *name)
        db->queries        = NULL;
        db->queries_num    = 0;
 
+       db->writers        = NULL;
+       db->writers_num    = 0;
+
+       pthread_mutex_init (&db->db_lock, /* attrs = */ NULL);
+
        db->interval   = 0;
 
+       db->commit_interval = 0;
+       db->next_commit     = 0;
+
        db->database   = sstrdup (name);
        db->host       = NULL;
        db->port       = NULL;
        db->user       = NULL;
        db->password   = NULL;
 
+       db->instance   = sstrdup (name);
+
        db->sslmode    = NULL;
 
        db->krbsrvname = NULL;
 
        db->service    = NULL;
+
+       db->ref_cnt    = 0;
        return db;
 } /* c_psql_database_new */
 
@@ -191,6 +288,17 @@ static void c_psql_database_delete (void *data)
 
        c_psql_database_t *db = data;
 
+       --db->ref_cnt;
+       /* readers and writers may access this database */
+       if (db->ref_cnt > 0)
+               return;
+
+       /* wait for the lock to be released by the last writer */
+       pthread_mutex_lock (&db->db_lock);
+
+       if (db->next_commit > 0)
+               c_psql_commit (db);
+
        PQfinish (db->conn);
        db->conn = NULL;
 
@@ -202,17 +310,31 @@ static void c_psql_database_delete (void *data)
        sfree (db->queries);
        db->queries_num = 0;
 
+       sfree (db->writers);
+       db->writers_num = 0;
+
+       pthread_mutex_unlock (&db->db_lock);
+
+       pthread_mutex_destroy (&db->db_lock);
+
        sfree (db->database);
        sfree (db->host);
        sfree (db->port);
        sfree (db->user);
        sfree (db->password);
 
+       sfree (db->instance);
+
        sfree (db->sslmode);
 
        sfree (db->krbsrvname);
 
        sfree (db->service);
+
+       /* don't care about freeing or reordering the 'databases' array
+        * 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 */
 
@@ -223,7 +345,7 @@ static int c_psql_connect (c_psql_database_t *db)
        int   buf_len = sizeof (conninfo);
        int   status;
 
-       if (! db)
+       if ((! db) || (! db->database))
                return -1;
 
        status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
@@ -259,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);
 
@@ -271,8 +390,9 @@ static int c_psql_check_connection (c_psql_database_t *db)
 
                if (CONNECTION_OK != PQstatus (db->conn)) {
                        c_complain (LOG_ERR, &db->conn_complaint,
-                                       "Failed to connect to database %s: %s",
-                                       db->database, PQerrorMessage (db->conn));
+                                       "Failed to connect to database %s (%s): %s",
+                                       db->database, db->instance,
+                                       PQerrorMessage (db->conn));
                        return -1;
                }
 
@@ -335,10 +455,15 @@ static PGresult *c_psql_exec_query_params (c_psql_database_t *db,
                                params[i] = db->user;
                                break;
                        case C_PSQL_PARAM_INTERVAL:
-                               ssnprintf (interval, sizeof (interval), "%i",
-                                               db->interval > 0 ? db->interval : interval_g);
+                               ssnprintf (interval, sizeof (interval), "%.3f",
+                                               (db->interval > 0)
+                                               ? CDTIME_T_TO_DOUBLE (db->interval)
+                                               : plugin_get_interval ());
                                params[i] = interval;
                                break;
+                       case C_PSQL_PARAM_INSTANCE:
+                               params[i] = db->instance;
+                               break;
                        default:
                                assert (0);
                }
@@ -350,6 +475,7 @@ static PGresult *c_psql_exec_query_params (c_psql_database_t *db,
                        NULL, NULL, /* return text data */ 0);
 } /* c_psql_exec_query_params */
 
+/* db->db_lock must be locked when calling this function */
 static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
                udb_query_preparation_area_t *prep_area)
 {
@@ -376,30 +502,46 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
        else if ((NULL == data) || (0 == data->params_num))
                res = c_psql_exec_query_noparams (db, q);
        else {
-               log_err ("Connection to database \"%s\" does not support parameters "
-                               "(protocol version %d) - cannot execute query \"%s\".",
-                               db->database, db->proto_version,
+               log_err ("Connection to database \"%s\" (%s) does not support "
+                               "parameters (protocol version %d) - "
+                               "cannot execute query \"%s\".",
+                               db->database, db->instance, db->proto_version,
                                udb_query_get_name (q));
                return -1;
        }
 
+       /* give c_psql_write() a chance to acquire the lock if called recursively
+        * through dispatch_values(); this will happen if, both, queries and
+        * writers are configured for a single connection */
+       pthread_mutex_unlock (&db->db_lock);
+
        column_names = NULL;
        column_values = NULL;
 
-#define BAIL_OUT(status) \
-       sfree (column_names); \
-       sfree (column_values); \
-       PQclear (res); \
-       return status
-
        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",
                                udb_query_get_statement (q));
-               BAIL_OUT (-1);
+               PQclear (res);
+               return -1;
        }
 
+#define BAIL_OUT(status) \
+       sfree (column_names); \
+       sfree (column_values); \
+       PQclear (res); \
+       pthread_mutex_lock (&db->db_lock); \
+       return status
+
        rows_num = PQntuples (res);
        if (1 > rows_num) {
                BAIL_OUT (0);
@@ -435,7 +577,7 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
                host = db->host;
 
        status = udb_query_prepare_result (q, prep_area, host, "postgresql",
-                       db->database, column_names, (size_t) column_num, db->interval);
+                       db->instance, column_names, (size_t) column_num, db->interval);
        if (0 != status) {
                log_err ("udb_query_prepare_result failed with status %i.",
                                status);
@@ -486,9 +628,15 @@ static int c_psql_read (user_data_t *ud)
        db = ud->data;
 
        assert (NULL != db->database);
+       assert (NULL != db->instance);
+       assert (NULL != db->queries);
 
-       if (0 != c_psql_check_connection (db))
+       pthread_mutex_lock (&db->db_lock);
+
+       if (0 != c_psql_check_connection (db)) {
+               pthread_mutex_unlock (&db->db_lock);
                return -1;
+       }
 
        for (i = 0; i < db->queries_num; ++i)
        {
@@ -506,56 +654,384 @@ static int c_psql_read (user_data_t *ud)
                        success = 1;
        }
 
+       pthread_mutex_unlock (&db->db_lock);
+
        if (! success)
                return -1;
        return 0;
 } /* c_psql_read */
 
-static int c_psql_shutdown (void)
+static char *values_name_to_sqlarray (const data_set_t *ds,
+               char *string, size_t string_len)
 {
-       plugin_unregister_read_group ("postgresql");
+       char  *str_ptr;
+       size_t str_len;
 
-       udb_query_free (queries, queries_num);
-       queries = NULL;
-       queries_num = 0;
+       int i;
 
-       return 0;
-} /* c_psql_shutdown */
+       str_ptr = string;
+       str_len = string_len;
+
+       for (i = 0; i < ds->ds_num; ++i) {
+               int status = ssnprintf (str_ptr, str_len, ",'%s'", ds->ds[i].name);
+
+               if (status < 1)
+                       return NULL;
+               else if ((size_t)status >= str_len) {
+                       str_len = 0;
+                       break;
+               }
+               else {
+                       str_ptr += status;
+                       str_len -= (size_t)status;
+               }
+       }
 
-static int config_set_s (char *name, char **var, const oconfig_item_t *ci)
+       if (str_len <= 2) {
+               log_err ("c_psql_write: Failed to stringify value names");
+               return NULL;
+       }
+
+       /* overwrite the first comma */
+       string[0] = '{';
+       str_ptr[0] = '}';
+       str_ptr[1] = '\0';
+
+       return string;
+} /* values_name_to_sqlarray */
+
+static char *values_type_to_sqlarray (const data_set_t *ds,
+               char *string, size_t string_len, _Bool store_rates)
 {
-       if ((0 != ci->children_num) || (1 != ci->values_num)
-                       || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
-               log_err ("%s expects a single string argument.", name);
-               return 1;
+       char  *str_ptr;
+       size_t str_len;
+
+       int i;
+
+       str_ptr = string;
+       str_len = string_len;
+
+       for (i = 0; i < ds->ds_num; ++i) {
+               int status;
+
+               if (store_rates)
+                       status = ssnprintf(str_ptr, str_len, ",'gauge'");
+               else
+                       status = ssnprintf(str_ptr, str_len, ",'%s'",
+                                       DS_TYPE_TO_STRING (ds->ds[i].type));
+
+               if (status < 1) {
+                       str_len = 0;
+                       break;
+               }
+               else if ((size_t)status >= str_len) {
+                       str_len = 0;
+                       break;
+               }
+               else {
+                       str_ptr += status;
+                       str_len -= (size_t)status;
+               }
        }
 
-       sfree (*var);
-       *var = sstrdup (ci->values[0].value.string);
-       return 0;
-} /* config_set_s */
+       if (str_len <= 2) {
+               log_err ("c_psql_write: Failed to stringify value types");
+               return NULL;
+       }
 
-static int config_set_i (char *name, int *var,
-               const oconfig_item_t *ci, int min)
+       /* overwrite the first comma */
+       string[0] = '{';
+       str_ptr[0] = '}';
+       str_ptr[1] = '\0';
+
+       return string;
+} /* values_type_to_sqlarray */
+
+static char *values_to_sqlarray (const data_set_t *ds, const value_list_t *vl,
+               char *string, size_t string_len, _Bool store_rates)
 {
-       int value;
+       char  *str_ptr;
+       size_t str_len;
 
-       if ((0 != ci->children_num) || (1 != ci->values_num)
-                       || (OCONFIG_TYPE_NUMBER != ci->values[0].type)) {
-               log_err ("%s expects a single number argument.", name);
-               return 1;
+       gauge_t *rates = NULL;
+
+       int i;
+
+       str_ptr = string;
+       str_len = string_len;
+
+       for (i = 0; i < vl->values_len; ++i) {
+               int status = 0;
+
+               if ((ds->ds[i].type != DS_TYPE_GAUGE)
+                               && (ds->ds[i].type != DS_TYPE_COUNTER)
+                               && (ds->ds[i].type != DS_TYPE_DERIVE)
+                               && (ds->ds[i].type != DS_TYPE_ABSOLUTE)) {
+                       log_err ("c_psql_write: Unknown data source type: %i",
+                                       ds->ds[i].type);
+                       sfree (rates);
+                       return NULL;
+               }
+
+               if (ds->ds[i].type == DS_TYPE_GAUGE)
+                       status = ssnprintf (str_ptr, str_len,
+                                       ",%f", vl->values[i].gauge);
+               else if (store_rates) {
+                       if (rates == NULL)
+                               rates = uc_get_rate (ds, vl);
+
+                       if (rates == NULL) {
+                               log_err ("c_psql_write: Failed to determine rate");
+                               return NULL;
+                       }
+
+                       status = ssnprintf (str_ptr, str_len,
+                                       ",%lf", rates[i]);
+               }
+               else if (ds->ds[i].type == DS_TYPE_COUNTER)
+                       status = ssnprintf (str_ptr, str_len,
+                                       ",%llu", vl->values[i].counter);
+               else if (ds->ds[i].type == DS_TYPE_DERIVE)
+                       status = ssnprintf (str_ptr, str_len,
+                                       ",%"PRIi64, vl->values[i].derive);
+               else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+                       status = ssnprintf (str_ptr, str_len,
+                                       ",%"PRIu64, vl->values[i].absolute);
+
+               if (status < 1) {
+                       str_len = 0;
+                       break;
+               }
+               else if ((size_t)status >= str_len) {
+                       str_len = 0;
+                       break;
+               }
+               else {
+                       str_ptr += status;
+                       str_len -= (size_t)status;
+               }
        }
 
-       value = (int)ci->values[0].value.number;
+       sfree (rates);
 
-       if (value < min) {
-               log_err ("%s expects a number greater or equal to %i.", name, min);
-               return 1;
+       if (str_len <= 2) {
+               log_err ("c_psql_write: Failed to stringify value list");
+               return NULL;
        }
 
-       *var = value;
+       /* overwrite the first comma */
+       string[0] = '{';
+       str_ptr[0] = '}';
+       str_ptr[1] = '\0';
+
+       return string;
+} /* values_to_sqlarray */
+
+static int c_psql_write (const data_set_t *ds, const value_list_t *vl,
+               user_data_t *ud)
+{
+       c_psql_database_t *db;
+
+       char time_str[32];
+       char values_name_str[1024];
+       char values_type_str[1024];
+       char values_str[1024];
+
+       const char *params[9];
+
+       int success = 0;
+       int i;
+
+       if ((ud == NULL) || (ud->data == NULL)) {
+               log_err ("c_psql_write: Invalid user data.");
+               return -1;
+       }
+
+       db = ud->data;
+       assert (db->database != NULL);
+       assert (db->writers != NULL);
+
+       if (cdtime_to_iso8601 (time_str, sizeof (time_str), vl->time) == 0) {
+               log_err ("c_psql_write: Failed to convert time to ISO 8601 format");
+               return -1;
+       }
+
+       if (values_name_to_sqlarray (ds,
+                               values_name_str, sizeof (values_name_str)) == NULL)
+               return -1;
+
+#define VALUE_OR_NULL(v) ((((v) == NULL) || (*(v) == '\0')) ? NULL : (v))
+
+       params[0] = time_str;
+       params[1] = vl->host;
+       params[2] = vl->plugin;
+       params[3] = VALUE_OR_NULL(vl->plugin_instance);
+       params[4] = vl->type;
+       params[5] = VALUE_OR_NULL(vl->type_instance);
+       params[6] = values_name_str;
+
+#undef VALUE_OR_NULL
+
+       pthread_mutex_lock (&db->db_lock);
+
+       if (0 != c_psql_check_connection (db)) {
+               pthread_mutex_unlock (&db->db_lock);
+               return -1;
+       }
+
+       if ((db->commit_interval > 0)
+                       && (db->next_commit == 0))
+               c_psql_begin (db);
+
+       for (i = 0; i < db->writers_num; ++i) {
+               c_psql_writer_t *writer;
+               PGresult *res;
+
+               writer = db->writers[i];
+
+               if (values_type_to_sqlarray (ds,
+                                       values_type_str, sizeof (values_type_str),
+                                       writer->store_rates) == NULL) {
+                       pthread_mutex_unlock (&db->db_lock);
+                       return -1;
+               }
+
+               if (values_to_sqlarray (ds, vl,
+                                       values_str, sizeof (values_str),
+                                       writer->store_rates) == NULL) {
+                       pthread_mutex_unlock (&db->db_lock);
+                       return -1;
+               }
+
+               params[7] = values_type_str;
+               params[8] = values_str;
+
+               res = PQexecParams (db->conn, writer->statement,
+                               STATIC_ARRAY_SIZE (params), NULL,
+                               (const char *const *)params,
+                               NULL, NULL, /* return text data */ 0);
+
+               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))) {
+                               /* try again */
+                               res = PQexecParams (db->conn, writer->statement,
+                                               STATIC_ARRAY_SIZE (params), NULL,
+                                               (const char *const *)params,
+                                               NULL, NULL, /* return text data */ 0);
+
+                               if ((PGRES_COMMAND_OK == PQresultStatus (res))
+                                               || (PGRES_TUPLES_OK == PQresultStatus (res))) {
+                                       PQclear (res);
+                                       success = 1;
+                                       continue;
+                               }
+                       }
+
+                       log_err ("Failed to execute SQL query: %s",
+                                       PQerrorMessage (db->conn));
+                       log_info ("SQL query was: '%s', "
+                                       "params: %s, %s, %s, %s, %s, %s, %s, %s",
+                                       writer->statement,
+                                       params[0], params[1], params[2], params[3],
+                                       params[4], params[5], params[6], params[7]);
+
+                       /* this will abort any current transaction -> restart */
+                       if (db->next_commit > 0)
+                               c_psql_commit (db);
+
+                       pthread_mutex_unlock (&db->db_lock);
+                       return -1;
+               }
+
+               PQclear (res);
+               success = 1;
+       }
+
+       if ((db->next_commit > 0)
+                       && (cdtime () > db->next_commit))
+               c_psql_commit (db);
+
+       pthread_mutex_unlock (&db->db_lock);
+
+       if (! success)
+               return -1;
+       return 0;
+} /* c_psql_write */
+
+/* We cannot flush single identifiers as all we do is to commit the currently
+ * running transaction, thus making sure that all written data is actually
+ * visible to everybody. */
+static int c_psql_flush (cdtime_t timeout,
+               __attribute__((unused)) const char *ident,
+               user_data_t *ud)
+{
+       c_psql_database_t **dbs = databases;
+       size_t dbs_num = databases_num;
+       size_t i;
+
+       if ((ud != NULL) && (ud->data != NULL)) {
+               dbs = (void *)&ud->data;
+               dbs_num = 1;
+       }
+
+       for (i = 0; i < dbs_num; ++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
+                * committed */
+               if ((db->next_commit > 0) && (db->commit_interval > timeout))
+                       c_psql_commit (db);
+       }
+       return 0;
+} /* c_psql_flush */
+
+static int c_psql_shutdown (void)
+{
+       size_t i = 0;
+
+       _Bool had_flush = 0;
+
+       plugin_unregister_read_group ("postgresql");
+
+       for (i = 0; i < databases_num; ++i) {
+               c_psql_database_t *db = databases[i];
+
+               if (db->writers_num > 0) {
+                       char cb_name[DATA_MAX_NAME_LEN];
+                       ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s",
+                                       db->database);
+
+                       if (! had_flush) {
+                               plugin_unregister_flush ("postgresql");
+                               had_flush = 1;
+                       }
+
+                       plugin_unregister_flush (cb_name);
+                       plugin_unregister_write (cb_name);
+               }
+
+               sfree (db);
+       }
+
+       udb_query_free (queries, queries_num);
+       queries = NULL;
+       queries_num = 0;
+
+       sfree (writers);
+       writers = NULL;
+       writers_num = 0;
+
+       sfree (databases);
+       databases = NULL;
+       databases_num = 0;
+
        return 0;
-} /* config_set_s */
+} /* c_psql_shutdown */
 
 static int config_query_param_add (udb_query_t *q, oconfig_item_t *ci)
 {
@@ -592,6 +1068,8 @@ static int config_query_param_add (udb_query_t *q, oconfig_item_t *ci)
                data->params[data->params_num] = C_PSQL_PARAM_USER;
        else if (0 == strcasecmp (param_str, "interval"))
                data->params[data->params_num] = C_PSQL_PARAM_INTERVAL;
+       else if (0 == strcasecmp (param_str, "instance"))
+               data->params[data->params_num] = C_PSQL_PARAM_INSTANCE;
        else {
                log_err ("Invalid parameter \"%s\".", param_str);
                return 1;
@@ -613,14 +1091,113 @@ static int config_query_callback (udb_query_t *q, oconfig_item_t *ci)
        return (-1);
 } /* config_query_callback */
 
+static int config_add_writer (oconfig_item_t *ci,
+               c_psql_writer_t *src_writers, size_t src_writers_num,
+               c_psql_writer_t ***dst_writers, size_t *dst_writers_num)
+{
+       char *name;
+
+       size_t i;
+
+       if ((ci == NULL) || (dst_writers == NULL) || (dst_writers_num == NULL))
+               return -1;
+
+       if ((ci->values_num != 1)
+                       || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+               log_err ("`Writer' expects a single string argument.");
+               return 1;
+       }
+
+       name = ci->values[0].value.string;
+
+       for (i = 0; i < src_writers_num; ++i) {
+               c_psql_writer_t **tmp;
+
+               if (strcasecmp (name, src_writers[i].name) != 0)
+                       continue;
+
+               tmp = (c_psql_writer_t **)realloc (*dst_writers,
+                               sizeof (**dst_writers) * (*dst_writers_num + 1));
+               if (tmp == NULL) {
+                       log_err ("Out of memory.");
+                       return -1;
+               }
+
+               tmp[*dst_writers_num] = src_writers + i;
+
+               *dst_writers = tmp;
+               ++(*dst_writers_num);
+               break;
+       }
+
+       if (i >= src_writers_num) {
+               log_err ("No such writer: `%s'", name);
+               return -1;
+       }
+
+       return 0;
+} /* config_add_writer */
+
+static int c_psql_config_writer (oconfig_item_t *ci)
+{
+       c_psql_writer_t *writer;
+       c_psql_writer_t *tmp;
+
+       int status = 0;
+       int i;
+
+       if ((ci->values_num != 1)
+                       || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+               log_err ("<Writer> expects a single string argument.");
+               return 1;
+       }
+
+       tmp = (c_psql_writer_t *)realloc (writers,
+                       sizeof (*writers) * (writers_num + 1));
+       if (tmp == NULL) {
+               log_err ("Out of memory.");
+               return -1;
+       }
+
+       writers = tmp;
+       writer  = writers + writers_num;
+       ++writers_num;
+
+       writer->name = sstrdup (ci->values[0].value.string);
+       writer->statement = NULL;
+       writer->store_rates = 1;
+
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *c = ci->children + i;
+
+               if (strcasecmp ("Statement", c->key) == 0)
+                       status = cf_util_get_string (c, &writer->statement);
+               else if (strcasecmp ("StoreRates", c->key) == 0)
+                       status = cf_util_get_boolean (c, &writer->store_rates);
+               else
+                       log_warn ("Ignoring unknown config key \"%s\".", c->key);
+       }
+
+       if (status != 0) {
+               sfree (writer->statement);
+               sfree (writer->name);
+               sfree (writer);
+               return status;
+       }
+
+       return 0;
+} /* c_psql_config_writer */
+
 static int c_psql_config_database (oconfig_item_t *ci)
 {
        c_psql_database_t *db;
 
        char cb_name[DATA_MAX_NAME_LEN];
-       struct timespec cb_interval;
+       struct timespec cb_interval = { 0, 0 };
        user_data_t ud;
 
+       static _Bool have_flush = 0;
+
        int i;
 
        if ((1 != ci->values_num)
@@ -639,30 +1216,37 @@ static int c_psql_config_database (oconfig_item_t *ci)
                oconfig_item_t *c = ci->children + i;
 
                if (0 == strcasecmp (c->key, "Host"))
-                       config_set_s ("Host", &db->host, c);
+                       cf_util_get_string (c, &db->host);
                else if (0 == strcasecmp (c->key, "Port"))
-                       config_set_s ("Port", &db->port, c);
+                       cf_util_get_service (c, &db->port);
                else if (0 == strcasecmp (c->key, "User"))
-                       config_set_s ("User", &db->user, c);
+                       cf_util_get_string (c, &db->user);
                else if (0 == strcasecmp (c->key, "Password"))
-                       config_set_s ("Password", &db->password, c);
+                       cf_util_get_string (c, &db->password);
+               else if (0 == strcasecmp (c->key, "Instance"))
+                       cf_util_get_string (c, &db->instance);
                else if (0 == strcasecmp (c->key, "SSLMode"))
-                       config_set_s ("SSLMode", &db->sslmode, c);
+                       cf_util_get_string (c, &db->sslmode);
                else if (0 == strcasecmp (c->key, "KRBSrvName"))
-                       config_set_s ("KRBSrvName", &db->krbsrvname, c);
+                       cf_util_get_string (c, &db->krbsrvname);
                else if (0 == strcasecmp (c->key, "Service"))
-                       config_set_s ("Service", &db->service, c);
+                       cf_util_get_string (c, &db->service);
                else if (0 == strcasecmp (c->key, "Query"))
                        udb_query_pick_from_list (c, queries, queries_num,
                                        &db->queries, &db->queries_num);
+               else if (0 == strcasecmp (c->key, "Writer"))
+                       config_add_writer (c, writers, writers_num,
+                                       &db->writers, &db->writers_num);
                else if (0 == strcasecmp (c->key, "Interval"))
-                       config_set_i ("Interval", &db->interval, c, /* min = */ 1);
+                       cf_util_get_cdtime (c, &db->interval);
+               else if (strcasecmp ("CommitInterval", c->key) == 0)
+                       cf_util_get_cdtime (c, &db->commit_interval);
                else
                        log_warn ("Ignoring unknown config key \"%s\".", c->key);
        }
 
        /* If no `Query' options were given, add the default queries.. */
-       if (db->queries_num == 0) {
+       if ((db->queries_num == 0) && (db->writers_num == 0)){
                for (i = 0; i < def_queries_num; i++)
                        udb_query_pick_from_list_by_name (def_queries[i],
                                        queries, queries_num,
@@ -699,14 +1283,36 @@ static int c_psql_config_database (oconfig_item_t *ci)
        ud.data = db;
        ud.free_func = c_psql_database_delete;
 
-       ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->database);
+       ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->instance);
+
+       if (db->queries_num > 0) {
+               CDTIME_T_TO_TIMESPEC (db->interval, &cb_interval);
 
-       memset (&cb_interval, 0, sizeof (cb_interval));
-       if (db->interval > 0)
-               cb_interval.tv_sec = (time_t)db->interval;
+               ++db->ref_cnt;
+               plugin_register_complex_read ("postgresql", cb_name, c_psql_read,
+                               /* interval = */ (db->interval > 0) ? &cb_interval : NULL,
+                               &ud);
+       }
+       if (db->writers_num > 0) {
+               ++db->ref_cnt;
+               plugin_register_write (cb_name, c_psql_write, &ud);
+
+               if (! have_flush) {
+                       /* flush all */
+                       plugin_register_flush ("postgresql",
+                                       c_psql_flush, /* user data = */ NULL);
+                       have_flush = 1;
+               }
 
-       plugin_register_complex_read ("postgresql", cb_name, c_psql_read,
-                       /* interval = */ &cb_interval, &ud);
+               /* flush this connection only */
+               ++db->ref_cnt;
+               plugin_register_flush (cb_name, c_psql_flush, &ud);
+       }
+       else if (db->commit_interval > 0) {
+               log_warn ("Database '%s': You do not have any writers assigned to "
+                               "this database connection. Setting 'CommitInterval' does "
+                               "not have any effect.", db->database);
+       }
        return 0;
 } /* c_psql_config_database */
 
@@ -737,8 +1343,9 @@ static int c_psql_config (oconfig_item_t *ci)
 
                if (0 == strcasecmp (c->key, "Query"))
                        udb_query_create (&queries, &queries_num, c,
-                                       /* callback = */ config_query_callback,
-                                       /* legacy mode = */ 1);
+                                       /* callback = */ config_query_callback);
+               else if (0 == strcasecmp (c->key, "Writer"))
+                       c_psql_config_writer (c);
                else if (0 == strcasecmp (c->key, "Database"))
                        c_psql_config_database (c);
                else
index 83a32c7..f905eb2 100644 (file)
        MinVersion 80300
 </Query>
 
+<Query queries_by_table>
+       Statement "SELECT schemaname, relname, \
+                       n_tup_ins AS ins, \
+                       n_tup_upd AS upd, \
+                       n_tup_del AS del \
+               FROM pg_stat_user_tables;"
+
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "ins"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "ins"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "upd"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "upd"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "del"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "del"
+       </Result>
+
+       MaxVersion 80299
+</Query>
+
+<Query queries_by_table>
+       Statement "SELECT schemaname, relname, \
+                       n_tup_ins AS ins, \
+                       n_tup_upd AS upd, \
+                       n_tup_del AS del, \
+                       n_tup_hot_upd AS hot_upd \
+               FROM pg_stat_user_tables;"
+
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "ins"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "ins"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "upd"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "upd"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "del"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "del"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "hot_upd"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "hot_upd"
+       </Result>
+
+       MinVersion 80300
+</Query>
+
 <Query query_plans>
        Statement "SELECT sum(seq_scan) AS seq, \
                        sum(seq_tup_read) AS seq_tup_read, \
        MinVersion 80300
 </Query>
 
+<Query query_plans_by_table>
+       Statement "SELECT schemaname, relname, \
+                       seq_scan AS seq, \
+                       seq_tup_read AS seq_tup_read, \
+                       idx_scan AS idx, \
+                       idx_tup_fetch AS idx_tup_fetch \
+               FROM pg_stat_user_tables;"
+
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "seq"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "seq"
+       </Result>
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "seq_tup_read"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "seq_tup_read"
+       </Result>
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "idx"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "idx"
+       </Result>
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "idx_tup_fetch"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "idx_tup_fetch"
+       </Result>
+</Query>
+
+<Query table_states_by_table>
+       Statement "SELECT schemaname, relname, \
+                       n_live_tup AS live, n_dead_tup AS dead \
+               FROM pg_stat_user_tables;"
+
+       <Result>
+               Type "pg_n_tup_g"
+               InstancePrefix "live"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "live"
+       </Result>
+       <Result>
+               Type "pg_n_tup_g"
+               InstancePrefix "dead"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "dead"
+       </Result>
+
+       MinVersion 80300
+</Query>
+
 <Query disk_io>
        Statement "SELECT coalesce(sum(heap_blks_read), 0) AS heap_read, \
                        coalesce(sum(heap_blks_hit), 0) AS heap_hit, \
        </Result>
 </Query>
 
+<Query disk_io_by_table>
+       Statement "SELECT schemaname, relname, \
+                       coalesce(heap_blks_read, 0) AS heap_read, \
+                       coalesce(heap_blks_hit, 0) AS heap_hit, \
+                       coalesce(idx_blks_read, 0) AS idx_read, \
+                       coalesce(idx_blks_hit, 0) AS idx_hit, \
+                       coalesce(toast_blks_read, 0) AS toast_read, \
+                       coalesce(toast_blks_hit, 0) AS toast_hit, \
+                       coalesce(tidx_blks_read, 0) AS tidx_read, \
+                       coalesce(tidx_blks_hit, 0) AS tidx_hit \
+               FROM pg_statio_user_tables;"
+
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "heap_read"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "heap_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "heap_hit"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "heap_hit"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "idx_read"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "idx_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "idx_hit"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "idx_hit"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "toast_read"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "toast_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "toast_hit"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "toast_hit"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "tidx_read"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "tidx_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "tidx_hit"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "tidx_hit"
+       </Result>
+</Query>
+
 <Query disk_usage>
        Statement "SELECT pg_database_size($1) AS size;"
 
index 29f6bca..a140a12 100644 (file)
@@ -321,6 +321,9 @@ static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */
 
   struct sockaddr_un sa_unix;
 
+  struct timeval stv_timeout;
+  cdtime_t cdt_timeout;
+
   sd = socket (PF_UNIX, item->socktype, 0);
   if (sd < 0)
   {
@@ -361,12 +364,13 @@ static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */
       break;
     }
 
-    struct timeval timeout;
-    timeout.tv_sec=2;
-    if (timeout.tv_sec < interval_g * 3 / 4)
-      timeout.tv_sec = interval_g * 3 / 4;
-    timeout.tv_usec=0;
-    status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout));
+    cdt_timeout = plugin_get_interval () * 3 / 4;
+    if (cdt_timeout < TIME_T_TO_CDTIME_T (2))
+      cdt_timeout = TIME_T_TO_CDTIME_T (2);
+
+    CDTIME_T_TO_TIMEVAL (cdt_timeout, &stv_timeout);
+
+    status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &stv_timeout, sizeof (stv_timeout));
     if (status != 0)
     {
       FUNC_ERROR ("setsockopt");
index 5e448cf..c77859d 100644 (file)
@@ -1,12 +1,13 @@
 /**
  * collectd - src/processes.c
  * Copyright (C) 2005       Lyonel Vincent
- * Copyright (C) 2006-2008  Florian octo Forster
+ * Copyright (C) 2006-2010  Florian octo Forster
  * Copyright (C) 2008       Oleg King
  * Copyright (C) 2009       Sebastian Harl
  * Copyright (C) 2009       Andrés J. Díaz
  * Copyright (C) 2009       Manuel Sanmartin
  * Copyright (C) 2010       Clément Stenac
+ * Copyright (C) 2012       Cosmin Ioiart
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -30,6 +31,7 @@
  *   Andrés J. Díaz <ajdiaz at connectical.com>
  *   Manuel Sanmartin
  *   Clément Stenac <clement.stenac at diwi.org>
+ *   Cosmin Ioiart <cioiart at gmail.com>
  **/
 
 #include "collectd.h"
 #define MAXARGLN 1024
 /* #endif HAVE_PROCINFO_H */
 
+#elif KERNEL_SOLARIS
+# include <procfs.h>
+# include <dirent.h>
+/* #endif KERNEL_SOLARIS */
+
 #else
 # error "No applicable input method."
 #endif
 # include <regex.h>
 #endif
 
+#if HAVE_KSTAT_H
+# include <kstat.h>
+#endif
+
 #ifndef ARG_MAX
 #  define ARG_MAX 4096
 #endif
@@ -136,13 +147,13 @@ typedef struct procstat_entry_s
 
        unsigned long vmem_minflt;
        unsigned long vmem_majflt;
-       unsigned long vmem_minflt_counter;
-       unsigned long vmem_majflt_counter;
+       derive_t      vmem_minflt_counter;
+       derive_t      vmem_majflt_counter;
 
        unsigned long cpu_user;
        unsigned long cpu_system;
-       unsigned long cpu_user_counter;
-       unsigned long cpu_system_counter;
+       derive_t      cpu_user_counter;
+       derive_t      cpu_system_counter;
 
        /* io data */
        derive_t io_rchar;
@@ -169,11 +180,11 @@ typedef struct procstat
        unsigned long vmem_code;
        unsigned long stack_size;
 
-       unsigned long vmem_minflt_counter;
-       unsigned long vmem_majflt_counter;
+       derive_t vmem_minflt_counter;
+       derive_t vmem_majflt_counter;
 
-       unsigned long cpu_user_counter;
-       unsigned long cpu_system_counter;
+       derive_t cpu_user_counter;
+       derive_t cpu_system_counter;
 
        /* io data */
        derive_t io_rchar;
@@ -200,7 +211,7 @@ static long pagesize_g;
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
-/* no global variables */
+static int pagesize;
 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
 
 #elif HAVE_PROCINFO_H
@@ -216,8 +227,8 @@ int getargs (struct procentry64 *processBuffer, int bufferLen, char *argsBuffer,
 #endif /* HAVE_PROCINFO_H */
 
 /* put name of process from config to list_head_g tree
  list_head_g is a list of 'procstat_t' structs with
  processes names we want to watch */
* list_head_g is a list of 'procstat_t' structs with
* processes names we want to watch */
 static void ps_list_register (const char *name, const char *regexp)
 {
        procstat_t *new;
@@ -609,7 +620,7 @@ static int ps_init (void)
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
-/* no initialization */
+       pagesize = getpagesize();
 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
 
 #elif HAVE_PROCINFO_H
@@ -676,8 +687,8 @@ static void ps_submit_proc_list (procstat_t *ps)
        plugin_dispatch_values (&vl);
 
        sstrncpy (vl.type, "ps_cputime", sizeof (vl.type));
-       vl.values[0].counter = ps->cpu_user_counter;
-       vl.values[1].counter = ps->cpu_system_counter;
+       vl.values[0].derive = ps->cpu_user_counter;
+       vl.values[1].derive = ps->cpu_system_counter;
        vl.values_len = 2;
        plugin_dispatch_values (&vl);
 
@@ -688,8 +699,8 @@ static void ps_submit_proc_list (procstat_t *ps)
        plugin_dispatch_values (&vl);
 
        sstrncpy (vl.type, "ps_pagefaults", sizeof (vl.type));
-       vl.values[0].counter = ps->vmem_minflt_counter;
-       vl.values[1].counter = ps->vmem_majflt_counter;
+       vl.values[0].derive = ps->vmem_minflt_counter;
+       vl.values[1].derive = ps->vmem_majflt_counter;
        vl.values_len = 2;
        plugin_dispatch_values (&vl);
 
@@ -712,10 +723,10 @@ static void ps_submit_proc_list (procstat_t *ps)
        }
 
        DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; "
-                        "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
+                       "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
                        "vmem_code = %lu; "
-                       "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
-                       "cpu_user_counter = %lu; cpu_system_counter = %lu; "
+                       "vmem_minflt_counter = %"PRIi64"; vmem_majflt_counter = %"PRIi64"; "
+                       "cpu_user_counter = %"PRIi64"; cpu_system_counter = %"PRIi64"; "
                        "io_rchar = %"PRIi64"; io_wchar = %"PRIi64"; "
                        "io_syscr = %"PRIi64"; io_syscw = %"PRIi64";",
                        ps->name, ps->num_proc, ps->num_lwp,
@@ -726,6 +737,26 @@ static void ps_submit_proc_list (procstat_t *ps)
                        ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
 } /* void ps_submit_proc_list */
 
+#if KERNEL_LINUX || KERNEL_SOLARIS
+static void ps_submit_fork_rate (derive_t value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].derive = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       sstrncpy(vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy(vl.plugin, "processes", sizeof (vl.plugin));
+       sstrncpy(vl.plugin_instance, "", sizeof (vl.plugin_instance));
+       sstrncpy(vl.type, "fork_rate", sizeof (vl.type));
+       sstrncpy(vl.type_instance, "", sizeof (vl.type_instance));
+
+       plugin_dispatch_values(&vl);
+}
+#endif /* KERNEL_LINUX || KERNEL_SOLARIS*/
+
 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
 #if KERNEL_LINUX
 static int ps_read_tasks (int pid)
@@ -780,7 +811,7 @@ static procstat_t *ps_read_vmem (int pid, procstat_t *ps)
                        continue;
 
                numfields = strsplit (buffer, fields,
-                                      STATIC_ARRAY_SIZE (fields));
+                               STATIC_ARRAY_SIZE (fields));
 
                if (numfields < 2)
                        continue;
@@ -790,7 +821,7 @@ static procstat_t *ps_read_vmem (int pid, procstat_t *ps)
                tmp = strtoll (fields[1], &endptr, /* base = */ 10);
                if ((errno == 0) && (endptr != fields[1]))
                {
-                       if (strncmp (buffer, "VmData", 6) == 0) 
+                       if (strncmp (buffer, "VmData", 6) == 0)
                        {
                                data = tmp;
                        }
@@ -888,8 +919,8 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        size_t name_end_pos;
        size_t name_len;
 
-       long long unsigned cpu_user_counter;
-       long long unsigned cpu_system_counter;
+       derive_t cpu_user_counter;
+       derive_t cpu_system_counter;
        long long unsigned vmem_size;
        long long unsigned vmem_rss;
        long long unsigned stack_size;
@@ -1001,8 +1032,8 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
                DEBUG("ps_read_process: did not get vmem data for pid %i",pid);
        }
 
-       ps->cpu_user_counter = (unsigned long) cpu_user_counter;
-       ps->cpu_system_counter = (unsigned long) cpu_system_counter;
+       ps->cpu_user_counter = cpu_user_counter;
+       ps->cpu_system_counter = cpu_system_counter;
        ps->vmem_size = (unsigned long) vmem_size;
        ps->vmem_rss = (unsigned long) vmem_rss;
        ps->stack_size = (unsigned long) stack_size;
@@ -1117,70 +1148,226 @@ static char *ps_get_cmdline (pid_t pid, char *name, char *buf, size_t buf_len)
        return buf;
 } /* char *ps_get_cmdline (...) */
 
-static unsigned long read_fork_rate ()
+static int read_fork_rate ()
 {
        FILE *proc_stat;
-       char buf[1024];
-       unsigned long result = 0;
-       int numfields;
-       char *fields[3];
+       char buffer[1024];
+       value_t value;
+       _Bool value_valid = 0;
 
-       proc_stat = fopen("/proc/stat", "r");
-       if (proc_stat == NULL) {
+       proc_stat = fopen ("/proc/stat", "r");
+       if (proc_stat == NULL)
+       {
                char errbuf[1024];
                ERROR ("processes plugin: fopen (/proc/stat) failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
-               return ULONG_MAX;
+               return (-1);
        }
 
-       while (fgets (buf, sizeof(buf), proc_stat) != NULL)
+       while (fgets (buffer, sizeof (buffer), proc_stat) != NULL)
        {
-               char *endptr;
+               int status;
+               char *fields[3];
+               int fields_num;
 
-               numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE (fields));
-               if (numfields != 2)
+               fields_num = strsplit (buffer, fields,
+                               STATIC_ARRAY_SIZE (fields));
+               if (fields_num != 2)
                        continue;
 
                if (strcmp ("processes", fields[0]) != 0)
                        continue;
 
-               errno = 0;
-               endptr = NULL;
-               result = strtoul(fields[1], &endptr, /* base = */ 10);
-               if ((endptr == fields[1]) || (errno != 0)) {
-                       ERROR ("processes plugin: Cannot parse fork rate: %s",
-                                       fields[1]);
-                       result = ULONG_MAX;
-                       break;
-               }
+               status = parse_value (fields[1], &value, DS_TYPE_DERIVE);
+               if (status == 0)
+                       value_valid = 1;
 
                break;
        }
-
        fclose(proc_stat);
 
-       return result;
+       if (!value_valid)
+               return (-1);
+
+       ps_submit_fork_rate (value.derive);
+       return (0);
 }
+#endif /*KERNEL_LINUX */
 
-static void ps_submit_fork_rate (unsigned long value)
+#if KERNEL_SOLARIS
+static const char *ps_get_cmdline (pid_t pid, /* {{{ */
+               char *buffer, size_t buffer_size)
 {
-       value_t values[1];
-       value_list_t vl = VALUE_LIST_INIT;
+       char path[PATH_MAX];
+       psinfo_t info;
+       int status;
 
-       values[0].derive = (derive_t) value;
+       snprintf(path, sizeof (path), "/proc/%i/psinfo", pid);
 
-       vl.values = values;
-       vl.values_len = 1;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
-       sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
-       sstrncpy (vl.type, "fork_rate", sizeof (vl.type));
-       sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
+       status = read_file_contents (path, (void *) &info, sizeof (info));
+       if (status != ((int) buffer_size))
+       {
+               ERROR ("processes plugin: Unexpected return value "
+                               "while reading \"%s\": "
+                               "Returned %i but expected %zu.",
+                               path, status, buffer_size);
+               return (NULL);
+       }
 
-       plugin_dispatch_values (&vl);
+       info.pr_psargs[sizeof (info.pr_psargs) - 1] = 0;
+       sstrncpy (buffer, info.pr_psargs, buffer_size);
+
+       return (buffer);
+} /* }}} int ps_get_cmdline */
+
+/*
+ * Reads process information on the Solaris OS. The information comes mainly from
+ * /proc/PID/status, /proc/PID/psinfo and /proc/PID/usage
+ * The values for input and ouput chars are calculated "by hand"
+ * Added a few "solaris" specific process states as well
+ */
+static int ps_read_process(int pid, procstat_t *ps, char *state)
+{
+       char filename[64];
+       char f_psinfo[64], f_usage[64];
+       char *buffer;
+
+       pstatus_t *myStatus;
+       psinfo_t *myInfo;
+       prusage_t *myUsage;
+
+       snprintf(filename, sizeof (filename), "/proc/%i/status", pid);
+       snprintf(f_psinfo, sizeof (f_psinfo), "/proc/%i/psinfo", pid);
+       snprintf(f_usage, sizeof (f_usage), "/proc/%i/usage", pid);
+
+
+       buffer = malloc(sizeof (pstatus_t));
+       memset(buffer, 0, sizeof (pstatus_t));
+       read_file_contents(filename, buffer, sizeof (pstatus_t));
+       myStatus = (pstatus_t *) buffer;
+
+       buffer = malloc(sizeof (psinfo_t));
+       memset(buffer, 0, sizeof(psinfo_t));
+       read_file_contents(f_psinfo, buffer, sizeof (psinfo_t));
+       myInfo = (psinfo_t *) buffer;
+
+       buffer = malloc(sizeof (prusage_t));
+       memset(buffer, 0, sizeof(prusage_t));
+       read_file_contents(f_usage, buffer, sizeof (prusage_t));
+       myUsage = (prusage_t *) buffer;
+
+       sstrncpy(ps->name, myInfo->pr_fname, sizeof (myInfo->pr_fname));
+       ps->num_lwp = myStatus->pr_nlwp;
+       if (myInfo->pr_wstat != 0) {
+               ps->num_proc = 0;
+               ps->num_lwp = 0;
+               *state = (char) 'Z';
+               return (0);
+       } else {
+               ps->num_proc = 1;
+               ps->num_lwp = myInfo->pr_nlwp;
+       }
+
+       /*
+        * Convert system time and user time from nanoseconds to microseconds
+        * for compatibility with the linux module
+        */
+       ps->cpu_system_counter = myStatus -> pr_stime.tv_nsec / 1000;
+       ps->cpu_user_counter = myStatus -> pr_utime.tv_nsec / 1000;
+
+       /*
+        * Convert rssize from KB to bytes to be consistent w/ the linux module
+        */
+       ps->vmem_rss = myInfo->pr_rssize * 1024;
+       ps->vmem_size = myInfo->pr_size * 1024;
+       ps->vmem_minflt_counter = myUsage->pr_minf;
+       ps->vmem_majflt_counter = myUsage->pr_majf;
+
+       /*
+        * TODO: Data and code segment calculations for Solaris
+        */
+
+       ps->vmem_data = -1;
+       ps->vmem_code = -1;
+       ps->stack_size = myStatus->pr_stksize;
+
+       /*
+        * Calculating input/ouput chars
+        * Formula used is total chars / total blocks => chars/block
+        * then convert input/output blocks to chars
+        */
+       ulong_t tot_chars = myUsage->pr_ioch;
+       ulong_t tot_blocks = myUsage->pr_inblk + myUsage->pr_oublk;
+       ulong_t chars_per_block = 1;
+       if (tot_blocks != 0)
+               chars_per_block = tot_chars / tot_blocks;
+       ps->io_rchar = myUsage->pr_inblk * chars_per_block;
+       ps->io_wchar = myUsage->pr_oublk * chars_per_block;
+       ps->io_syscr = myUsage->pr_sysc;
+       ps->io_syscw = myUsage->pr_sysc;
+
+
+       /*
+        * TODO: Find way of setting BLOCKED and PAGING status
+        */
+
+       *state = (char) 'R';
+       if (myStatus->pr_flags & PR_ASLEEP)
+               *state = (char) 'S';
+       else if (myStatus->pr_flags & PR_STOPPED)
+               *state = (char) 'T';
+       else if (myStatus->pr_flags & PR_DETACH)
+               *state = (char) 'E';
+       else if (myStatus->pr_flags & PR_DAEMON)
+               *state = (char) 'A';
+       else if (myStatus->pr_flags & PR_ISSYS)
+               *state = (char) 'Y';
+       else if (myStatus->pr_flags & PR_ORPHAN)
+               *state = (char) 'O';
+
+       sfree(myStatus);
+       sfree(myInfo);
+       sfree(myUsage);
+
+       return (0);
 }
 
-#endif /* KERNEL_LINUX */
+/*
+ * Reads the number of threads created since the last reboot. On Solaris these
+ * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
+ * The result is the sum for all the threads created on each cpu
+ */
+static int read_fork_rate()
+{
+       extern kstat_ctl_t *kc;
+       kstat_t *ksp_chain = NULL;
+       derive_t result = 0;
+
+       if (kc == NULL)
+               return (-1);
+
+       for (ksp_chain = kc->kc_chain;
+                       ksp_chain != NULL;
+                       ksp_chain = ksp_chain->ks_next)
+       {
+               if ((strcmp (ksp_chain->ks_module, "cpu") == 0)
+                               && (strcmp (ksp_chain->ks_name, "sys") == 0)
+                               && (strcmp (ksp_chain->ks_class, "misc") == 0))
+               {
+                       long long tmp;
+
+                       kstat_read (kc, ksp_chain, NULL);
+
+                       tmp = get_kstat_value(ksp_chain, "nthreads");
+                       if (tmp != -1LL)
+                               result += tmp;
+               }
+       }
+
+       ps_submit_fork_rate (result);
+       return (0);
+}
+#endif /* KERNEL_SOLARIS */
 
 #if HAVE_THREAD_INFO
 static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
@@ -1506,8 +1693,6 @@ static int ps_read (void)
        procstat_entry_t pse;
        char       state;
 
-       unsigned long fork_rate;
-
        procstat_t *ps_ptr;
 
        running = sleeping = zombies = stopped = paging = blocked = 0;
@@ -1589,9 +1774,7 @@ static int ps_read (void)
        for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
                ps_submit_proc_list (ps_ptr);
 
-       fork_rate = read_fork_rate();
-       if (fork_rate != ULONG_MAX)
-               ps_submit_fork_rate(fork_rate);
+       read_fork_rate();
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
@@ -1605,11 +1788,9 @@ static int ps_read (void)
 
        kvm_t *kd;
        char errbuf[1024];
-       char cmdline[ARG_MAX];
-       char *cmdline_ptr;
-       struct kinfo_proc *procs;          /* array of processes */
-       char **argv;
-       int count;                         /* returns number of processes */
+       struct kinfo_proc *procs;          /* array of processes */
+       struct kinfo_proc *proc_ptr = NULL;
+       int count;                         /* returns number of processes */
        int i;
 
        procstat_t *ps_ptr;
@@ -1639,64 +1820,80 @@ static int ps_read (void)
        /* Iterate through the processes in kinfo_proc */
        for (i = 0; i < count; i++)
        {
-               /* retrieve the arguments */
-               cmdline[0] = 0;
-               cmdline_ptr = NULL;
-
-               argv = kvm_getargv (kd, (const struct kinfo_proc *) &(procs[i]), 0);
-               if (argv != NULL)
+               /* Create only one process list entry per _process_, i.e.
+                * filter out threads (duplicate PID entries). */
+               if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid))
                {
-                       int status;
-                       int argc;
+                       char cmdline[ARG_MAX] = "";
+                       _Bool have_cmdline = 0;
 
-                       argc = 0;
-                       while (argv[argc] != NULL)
-                               argc++;
-
-                       status = strjoin (cmdline, sizeof (cmdline),
-                                       argv, argc, " ");
-
-                       if (status < 0)
-                       {
-                               WARNING ("processes plugin: Command line did "
-                                               "not fit into buffer.");
-                       }
-                       else
+                       proc_ptr = &(procs[i]);
+                       /* Don't probe system processes and processes without arguments */
+                       if (((procs[i].ki_flag & P_SYSTEM) == 0)
+                                       && (procs[i].ki_args != NULL))
                        {
-                               cmdline_ptr = &cmdline[0];
-                       }
-               }
+                               char **argv;
+                               int argc;
+                               int status;
+
+                               /* retrieve the arguments */
+                               argv = kvm_getargv (kd, proc_ptr, /* nchr = */ 0);
+                               argc = 0;
+                               if ((argv != NULL) && (argv[0] != NULL))
+                               {
+                                       while (argv[argc] != NULL)
+                                               argc++;
+
+                                       status = strjoin (cmdline, sizeof (cmdline), argv, argc, " ");
+                                       if (status < 0)
+                                               WARNING ("processes plugin: Command line did not fit into buffer.");
+                                       else
+                                               have_cmdline = 1;
+                               }
+                       } /* if (process has argument list) */
 
-               pse.id       = procs[i].ki_pid;
-               pse.age      = 0;
+                       pse.id       = procs[i].ki_pid;
+                       pse.age      = 0;
 
-               pse.num_proc = 1;
-               pse.num_lwp  = procs[i].ki_numthreads;
+                       pse.num_proc = 1;
+                       pse.num_lwp  = procs[i].ki_numthreads;
 
-               pse.vmem_size = procs[i].ki_size;
-               pse.vmem_rss = procs[i].ki_rssize * getpagesize();
-               pse.vmem_data = procs[i].ki_dsize * getpagesize();
-               pse.vmem_code = procs[i].ki_tsize * getpagesize();
-               pse.stack_size = procs[i].ki_ssize * getpagesize();
-               pse.vmem_minflt = 0;
-               pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
-               pse.vmem_majflt = 0;
-               pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
+                       pse.vmem_size = procs[i].ki_size;
+                       pse.vmem_rss = procs[i].ki_rssize * pagesize;
+                       pse.vmem_data = procs[i].ki_dsize * pagesize;
+                       pse.vmem_code = procs[i].ki_tsize * pagesize;
+                       pse.stack_size = procs[i].ki_ssize * pagesize;
+                       pse.vmem_minflt = 0;
+                       pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
+                       pse.vmem_majflt = 0;
+                       pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
 
-               pse.cpu_user = 0;
-               pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_sec
-                       * 1000
-                       + procs[i].ki_rusage.ru_utime.tv_usec;
-               pse.cpu_system = 0;
-               pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_sec
-                       * 1000
-                       + procs[i].ki_rusage.ru_stime.tv_usec;
+                       pse.cpu_user = 0;
+                       pse.cpu_system = 0;
+                       pse.cpu_user_counter = 0;
+                       pse.cpu_system_counter = 0;
+                       /*
+                        * The u-area might be swapped out, and we can't get
+                        * at it because we have a crashdump and no swap.
+                        * If it's here fill in these fields, otherwise, just
+                        * leave them 0.
+                        */
+                       if (procs[i].ki_flag & P_INMEM)
+                       {
+                               pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_usec
+                                       + (1000000lu * procs[i].ki_rusage.ru_utime.tv_sec);
+                               pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_usec
+                                       + (1000000lu * procs[i].ki_rusage.ru_stime.tv_sec);
+                       }
 
-               /* no io data */
-               pse.io_rchar = -1;
-               pse.io_wchar = -1;
-               pse.io_syscr = -1;
-               pse.io_syscw = -1;
+                       /* no I/O data */
+                       pse.io_rchar = -1;
+                       pse.io_wchar = -1;
+                       pse.io_syscr = -1;
+                       pse.io_syscw = -1;
+
+                       ps_list_add (procs[i].ki_comm, have_cmdline ? cmdline : NULL, &pse);
+               } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */
 
                switch (procs[i].ki_stat)
                {
@@ -1708,8 +1905,6 @@ static int ps_read (void)
                        case SLOCK:     blocked++;      break;
                        case SZOMB:     zombies++;      break;
                }
-
-               ps_list_add (procs[i].ki_comm, cmdline_ptr, &pse);
        }
 
        kvm_close(kd);
@@ -1766,7 +1961,7 @@ static int ps_read (void)
                                if (procentry[i].pi_pid == 0)
                                        cmdline = "swapper";
                                cargs = cmdline;
-                       }
+                       }
                        else
                        {
                                if (getargs(&procentry[i], sizeof(struct procentry64), arglist, MAXARGLN) >= 0)
@@ -1858,7 +2053,118 @@ static int ps_read (void)
 
        for (ps = list_head_g; ps != NULL; ps = ps->next)
                ps_submit_proc_list (ps);
-#endif /* HAVE_PROCINFO_H */
+/* #endif HAVE_PROCINFO_H */
+
+#elif KERNEL_SOLARIS
+       /*
+        * The Solaris section adds a few more process states and removes some
+        * process states compared to linux. Most notably there is no "PAGING"
+        * and "BLOCKED" state for a process.  The rest is similar to the linux
+        * code.
+        */
+       int running = 0;
+       int sleeping = 0;
+       int zombies = 0;
+       int stopped = 0;
+       int detached = 0;
+       int daemon = 0;
+       int system = 0;
+       int orphan = 0;
+
+       struct dirent *ent;
+       DIR *proc;
+
+       int status;
+       procstat_t *ps_ptr;
+       char state;
+
+       char cmdline[PRARGSZ];
+
+       ps_list_reset ();
+
+       proc = opendir ("/proc");
+       if (proc == NULL)
+               return (-1);
+
+       while ((ent = readdir(proc)) != NULL)
+       {
+               int pid;
+               struct procstat ps;
+               procstat_entry_t pse;
+
+               if (!isdigit ((int) ent->d_name[0]))
+                       continue;
+
+               if ((pid = atoi (ent->d_name)) < 1)
+                       continue;
+
+               status = ps_read_process (pid, &ps, &state);
+               if (status != 0)
+               {
+                       DEBUG("ps_read_process failed: %i", status);
+                       continue;
+               }
+
+               pse.id = pid;
+               pse.age = 0;
+
+               pse.num_proc   = ps.num_proc;
+               pse.num_lwp    = ps.num_lwp;
+               pse.vmem_size  = ps.vmem_size;
+               pse.vmem_rss   = ps.vmem_rss;
+               pse.vmem_data  = ps.vmem_data;
+               pse.vmem_code  = ps.vmem_code;
+               pse.stack_size = ps.stack_size;
+
+               pse.vmem_minflt = 0;
+               pse.vmem_minflt_counter = ps.vmem_minflt_counter;
+               pse.vmem_majflt = 0;
+               pse.vmem_majflt_counter = ps.vmem_majflt_counter;
+
+               pse.cpu_user = 0;
+               pse.cpu_user_counter = ps.cpu_user_counter;
+               pse.cpu_system = 0;
+               pse.cpu_system_counter = ps.cpu_system_counter;
+
+               pse.io_rchar = ps.io_rchar;
+               pse.io_wchar = ps.io_wchar;
+               pse.io_syscr = ps.io_syscr;
+               pse.io_syscw = ps.io_syscw;
+
+               switch (state)
+               {
+                       case 'R': running++;  break;
+                       case 'S': sleeping++; break;
+                       case 'E': detached++; break;
+                       case 'Z': zombies++;  break;
+                       case 'T': stopped++;  break;
+                       case 'A': daemon++;   break;
+                       case 'Y': system++;   break;
+                       case 'O': orphan++;   break;
+               }
+
+
+               ps_list_add (ps.name,
+                               ps_get_cmdline ((pid_t) pid,
+                                       cmdline, sizeof (cmdline)),
+                               &pse);
+       } /* while(readdir) */
+       closedir (proc);
+
+       ps_submit_state ("running",  running);
+       ps_submit_state ("sleeping", sleeping);
+       ps_submit_state ("zombies",  zombies);
+       ps_submit_state ("stopped",  stopped);
+       ps_submit_state ("detached", detached);
+       ps_submit_state ("daemon",   daemon);
+       ps_submit_state ("system",   system);
+       ps_submit_state ("orphan",   orphan);
+
+       for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
+               ps_submit_proc_list (ps_ptr);
+
+       read_fork_rate();
+#endif /* KERNEL_SOLARIS */
 
        return (0);
 } /* int ps_read */
index e90c1a4..0dfba21 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/protocols.c
- * Copyright (C) 2009  Florian octo Forster
+ * Copyright (C) 2009,2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -51,14 +51,10 @@ static void submit (const char *protocol_name,
 {
   value_t values[1];
   value_list_t vl = VALUE_LIST_INIT;
+  int status;
 
-  char *tmp_ptr;
-
-  errno = 0;
-  tmp_ptr = NULL;
-  values[0].counter = (counter_t) strtoll (str_value, &tmp_ptr,
-      /* base = */ 0);
-  if ((errno != 0) || (tmp_ptr == str_value))
+  status = parse_value (str_value, values, DS_TYPE_DERIVE);
+  if (status != 0)
   {
     ERROR ("protocols plugin: Parsing string as integer failed: %s",
         str_value);
index 22f3db6..10ac8f0 100644 (file)
@@ -433,8 +433,8 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                sstrncpy(v->data.type_instance, value_list->type_instance, sizeof(v->data.type_instance));
                sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
                sstrncpy(v->data.plugin_instance, value_list->plugin_instance, sizeof(v->data.plugin_instance));
-               v->data.time = value_list->time;
-               v->interval = value_list->interval;
+               v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
+               v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
                Py_CLEAR(v->values);
                v->values = list;
                Py_CLEAR(v->meta);
@@ -463,7 +463,7 @@ static int cpy_notification_callback(const notification_t *notification, user_da
                sstrncpy(n->data.type_instance, notification->type_instance, sizeof(n->data.type_instance));
                sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
                sstrncpy(n->data.plugin_instance, notification->plugin_instance, sizeof(n->data.plugin_instance));
-               n->data.time = notification->time;
+               n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
                sstrncpy(n->message, notification->message, sizeof(n->message));
                n->severity = notification->severity;
                ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
@@ -933,7 +933,7 @@ static int cpy_init(void) {
        pthread_sigmask(SIG_BLOCK, &sigset, NULL);
        state = PyEval_SaveThread();
        if (do_interactive) {
-               if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
+               if (plugin_thread_create(&thread, NULL, cpy_interactive, NULL)) {
                        ERROR("python: Error creating thread for interactive interpreter.");
                }
        }
index fd07882..4f5c4ce 100644 (file)
@@ -99,7 +99,7 @@ static PyObject *cpy_common_repr(PyObject *s) {
 
        if (self->time != 0) {
                CPY_STRCAT(&ret, l_time);
-               tmp = PyInt_FromLong(self->time);
+               tmp = PyFloat_FromDouble(self->time);
                CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
                CPY_STRCAT_AND_DEL(&ret, tmp);
        }
@@ -362,14 +362,13 @@ static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Values *self = (Values *) s;
-       int interval = 0;
-       double time = 0;
+       double interval = 0, time = 0;
        PyObject *values = NULL, *meta = NULL, *tmp;
        char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", "meta", NULL};
        
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdiO", kwlist,
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
                        NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
                        NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return -1;
@@ -420,7 +419,7 @@ static meta_data_t *cpy_build_meta(PyObject *meta) {
        meta_data_t *m = NULL;
        PyObject *l;
        
-       if (!meta)
+       if ((meta == NULL) || (meta == Py_None))
                return NULL;
 
        l = PyDict_Items(meta); /* New reference. */
@@ -428,8 +427,12 @@ static meta_data_t *cpy_build_meta(PyObject *meta) {
                cpy_log_exception("building meta data");
                return NULL;
        }
-       m = meta_data_create();
+
        s = PyList_Size(l);
+       if (s < 0)
+               return NULL;
+
+       m = meta_data_create();
        for (i = 0; i < s; ++i) {
                const char *string, *keystring;
                PyObject *key, *value, *item, *tmp;
@@ -504,13 +507,12 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        value_t *value;
        value_list_t value_list = VALUE_LIST_INIT;
        PyObject *values = self->values, *meta = self->meta;
-       double time = self->data.time;
-       int interval = self->interval;
+       double time = self->data.time, interval = self->interval;
        char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
        
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", "meta", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdiO", kwlist,
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
                        NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
                        NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return NULL;
@@ -589,8 +591,8 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        value_list.values = value;
        value_list.meta = cpy_build_meta(meta);
        value_list.values_len = size;
-       value_list.time = time;
-       value_list.interval = interval;
+       value_list.time = DOUBLE_TO_CDTIME_T(time);
+       value_list.interval = DOUBLE_TO_CDTIME_T(interval);
        if (value_list.host[0] == 0)
                sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
        if (value_list.plugin[0] == 0)
@@ -614,8 +616,7 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        value_t *value;
        value_list_t value_list = VALUE_LIST_INIT;
        PyObject *values = self->values, *meta = self->meta;
-       double time = self->data.time;
-       int interval = self->interval;
+       double time = self->data.time, interval = self->interval;
        char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
        
        static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
@@ -693,8 +694,8 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        }
        value_list.values = value;
        value_list.values_len = size;
-       value_list.time = time;
-       value_list.interval = interval;
+       value_list.time = DOUBLE_TO_CDTIME_T(time);
+       value_list.interval = DOUBLE_TO_CDTIME_T(interval);
        value_list.meta = cpy_build_meta(meta);;
        if (value_list.host[0] == 0)
                sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
@@ -732,7 +733,7 @@ static PyObject *Values_repr(PyObject *s) {
        ret = cpy_common_repr(s);
        if (self->interval != 0) {
                CPY_STRCAT(&ret, l_interval);
-               tmp = PyInt_FromLong(self->interval);
+               tmp = PyFloat_FromDouble(self->interval);
                CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
                CPY_STRCAT_AND_DEL(&ret, tmp);
        }
@@ -770,7 +771,7 @@ static void Values_dealloc(PyObject *self) {
 }
 
 static PyMemberDef Values_members[] = {
-       {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
+       {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
        {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
        {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
        {NULL}
@@ -885,7 +886,7 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj
                        NULL, &plugin, NULL, &host, &t, &severity))
                return NULL;
 
-       notification.time = t;
+       notification.time = DOUBLE_TO_CDTIME_T(t);
        notification.severity = severity;
        sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
        sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
@@ -907,8 +908,8 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj
                return NULL;
        }
 
-       if (notification.time < 1)
-               notification.time = time(0);
+       if (notification.time == 0)
+               notification.time = cdtime();
        if (notification.host[0] == 0)
                sstrncpy(notification.host, hostname_g, sizeof(notification.host));
        if (notification.plugin[0] == 0)
diff --git a/src/redis.c b/src/redis.c
new file mode 100644 (file)
index 0000000..85a8354
--- /dev/null
@@ -0,0 +1,331 @@
+/**
+ * collectd - src/redis.c, based on src/memcached.c
+ * Copyright (C) 2010       Andrés J. Díaz <ajdiaz@connectical.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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Andrés J. Díaz <ajdiaz@connectical.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <pthread.h>
+#include <credis.h>
+
+#ifndef HOST_NAME_MAX
+# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
+#endif
+
+#define REDIS_DEF_HOST   "localhost"
+#define REDIS_DEF_PASSWD ""
+#define REDIS_DEF_PORT    6379
+#define REDIS_DEF_TIMEOUT 2000
+#define MAX_REDIS_NODE_NAME 64
+
+/* Redis plugin configuration example:
+ *
+ * <Plugin redis>
+ *   <Node "mynode">
+ *     Host "localhost"
+ *     Port "6379"
+ *     Timeout 2000
+ *   </Node>
+ * </Plugin>
+ */
+
+struct redis_node_s;
+typedef struct redis_node_s redis_node_t;
+struct redis_node_s
+{
+  char name[MAX_REDIS_NODE_NAME];
+  char host[HOST_NAME_MAX];
+  char passwd[HOST_NAME_MAX];
+  int port;
+  int timeout;
+
+  redis_node_t *next;
+};
+
+static redis_node_t *nodes_head = NULL;
+
+static int redis_node_add (const redis_node_t *rn) /* {{{ */
+{
+  redis_node_t *rn_copy;
+  redis_node_t *rn_ptr;
+
+  /* Check for duplicates first */
+  for (rn_ptr = nodes_head; rn_ptr != NULL; rn_ptr = rn_ptr->next)
+    if (strcmp (rn->name, rn_ptr->name) == 0)
+      break;
+
+  if (rn_ptr != NULL)
+  {
+    ERROR ("redis plugin: A node with the name `%s' already exists.",
+        rn->name);
+    return (-1);
+  }
+
+  rn_copy = malloc (sizeof (*rn_copy));
+  if (rn_copy == NULL)
+  {
+    ERROR ("redis plugin: malloc failed adding redis_node to the tree.");
+    return (-1);
+  }
+
+  memcpy (rn_copy, rn, sizeof (*rn_copy));
+  rn_copy->next = NULL;
+
+  DEBUG ("redis plugin: Adding node \"%s\".", rn->name);
+
+  if (nodes_head == NULL)
+    nodes_head = rn_copy;
+  else
+  {
+    rn_ptr = nodes_head;
+    while (rn_ptr->next != NULL)
+      rn_ptr = rn_ptr->next;
+    rn_ptr->next = rn_copy;
+  }
+
+  return (0);
+} /* }}} */
+
+static int redis_config_node (oconfig_item_t *ci) /* {{{ */
+{
+  redis_node_t rn;
+  int i;
+  int status;
+
+  memset (&rn, 0, sizeof (rn));
+  sstrncpy (rn.host, REDIS_DEF_HOST, sizeof (rn.host));
+  rn.port = REDIS_DEF_PORT;
+  rn.timeout = REDIS_DEF_TIMEOUT;
+
+  status = cf_util_get_string_buffer (ci, rn.name, sizeof (rn.name));
+  if (status != 0)
+    return (status);
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *option = ci->children + i;
+
+    if (strcasecmp ("Host", option->key) == 0)
+      status = cf_util_get_string_buffer (option, rn.host, sizeof (rn.host));
+    else if (strcasecmp ("Port", option->key) == 0)
+    {
+      status = cf_util_get_port_number (option);
+      if (status > 0)
+      {
+        rn.port = status;
+        status = 0;
+      }
+    }
+    else if (strcasecmp ("Timeout", option->key) == 0)
+      status = cf_util_get_int (option, &rn.timeout);
+    else if (strcasecmp ("Password", option->key) == 0)
+      status = cf_util_get_string_buffer (option, rn.passwd, sizeof (rn.passwd));
+    else
+      WARNING ("redis plugin: Option `%s' not allowed inside a `Node' "
+          "block. I'll ignore this option.", option->key);
+
+    if (status != 0)
+      break;
+  }
+
+  if (status != 0)
+    return (status);
+
+  return (redis_node_add (&rn));
+} /* }}} int redis_config_node */
+
+static int redis_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *option = ci->children + i;
+
+    if (strcasecmp ("Node", option->key) == 0)
+      redis_config_node (option);
+    else
+      WARNING ("redis plugin: Option `%s' not allowed in redis"
+          " configuration. It will be ignored.", option->key);
+  }
+
+  if (nodes_head == NULL)
+  {
+    ERROR ("redis plugin: No valid node configuration could be found.");
+    return (ENOENT);
+  }
+
+  return (0);
+} /* }}} */
+
+  __attribute__ ((nonnull(2)))
+static void redis_submit_g (char *plugin_instance,
+    const char *type, 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, "redis", 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);
+} /* }}} */
+
+  __attribute__ ((nonnull(2)))
+static void redis_submit_d (char *plugin_instance,
+    const char *type, const char *type_instance,
+    derive_t value) /* {{{ */
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].derive = value;
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "redis", 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);
+} /* }}} */
+
+static int redis_init (void) /* {{{ */
+{
+  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);
+
+  return (0);
+} /* }}} int redis_init */
+
+static int redis_read (void) /* {{{ */
+{
+  redis_node_t *rn;
+
+  for (rn = nodes_head; rn != NULL; rn = rn->next)
+  {
+    REDIS rh;
+    REDIS_INFO info;
+
+    int status;
+
+    DEBUG ("redis plugin: querying info from node `%s' (%s:%d).", rn->name, rn->host, rn->port);
+
+    rh = credis_connect (rn->host, rn->port, rn->timeout);
+    if (rh == NULL)
+    {
+      ERROR ("redis plugin: unable to connect to node `%s' (%s:%d).", rn->name, rn->host, rn->port);
+      continue;
+    }
+
+    if (strlen (rn->passwd) > 0)
+    {
+      DEBUG ("redis plugin: authenticanting node `%s' passwd(%s).", rn->name, rn->passwd);
+      status = credis_auth(rh, rn->passwd);
+      if (status != 0)
+      {
+        WARNING ("redis plugin: unable to authenticate on node `%s'.", rn->name);
+        credis_close (rh);
+        continue;
+      }
+    }
+
+    memset (&info, 0, sizeof (info));
+    status = credis_info (rh, &info);
+    if (status != 0)
+    {
+      WARNING ("redis plugin: unable to get info from node `%s'.", rn->name);
+      credis_close (rh);
+      continue;
+    }
+
+    /* typedef struct _cr_info {
+     *   char redis_version[CREDIS_VERSION_STRING_SIZE];
+     *   int bgsave_in_progress;
+     *   int connected_clients;
+     *   int connected_slaves;
+     *   unsigned int used_memory;
+     *   long long changes_since_last_save;
+     *   int last_save_time;
+     *   long long total_connections_received;
+     *   long long total_commands_processed;
+     *   int uptime_in_seconds;
+     *   int uptime_in_days;
+     *   int role;
+     * } REDIS_INFO; */
+
+    DEBUG ("redis plugin: received info from node `%s': connected_clients = %d; "
+        "connected_slaves = %d; used_memory = %lu; changes_since_last_save = %lld; "
+        "bgsave_in_progress = %d; total_connections_received = %lld; "
+        "total_commands_processed = %lld; uptime_in_seconds = %ld", rn->name,
+        info.connected_clients, info.connected_slaves, info.used_memory,
+        info.changes_since_last_save, info.bgsave_in_progress,
+        info.total_connections_received, info.total_commands_processed,
+        info.uptime_in_seconds);
+
+    redis_submit_g (rn->name, "current_connections", "clients", info.connected_clients);
+    redis_submit_g (rn->name, "current_connections", "slaves", info.connected_slaves);
+    redis_submit_g (rn->name, "memory", "used", info.used_memory);
+    redis_submit_g (rn->name, "volatile_changes", NULL, info.changes_since_last_save);
+    redis_submit_d (rn->name, "total_connections", NULL, info.total_connections_received);
+    redis_submit_d (rn->name, "total_operations", NULL, info.total_commands_processed);
+
+    credis_close (rh);
+  }
+
+  return 0;
+}
+/* }}} */
+
+void module_register (void) /* {{{ */
+{
+  plugin_register_complex_config ("redis", redis_config);
+  plugin_register_init ("redis", redis_init);
+  plugin_register_read ("redis", redis_read);
+  /* TODO: plugin_register_write: one redis list per value id with
+   * X elements */
+}
+/* }}} */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
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 ff8789e..2ade3bb 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/routeros.c
- * Copyright (C) 2009  Florian octo Forster
+ * Copyright (C) 2009,2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -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"
@@ -44,13 +44,13 @@ struct cr_data_s
 typedef struct cr_data_s cr_data_t;
 
 static void cr_submit_io (cr_data_t *rd, const char *type, /* {{{ */
-    const char *type_instance, counter_t rx, counter_t tx)
+    const char *type_instance, derive_t rx, derive_t tx)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = rx;
-       values[1].counter = tx;
+       values[0].derive = rx;
+       values[1].derive = tx;
 
        vl.values = values;
        vl.values_len = STATIC_ARRAY_SIZE (values);
@@ -75,13 +75,13 @@ static void submit_interface (cr_data_t *rd, /* {{{ */
   }
 
   cr_submit_io (rd, "if_packets", i->name,
-      (counter_t) i->rx_packets, (counter_t) i->tx_packets);
+      (derive_t) i->rx_packets, (derive_t) i->tx_packets);
   cr_submit_io (rd, "if_octets", i->name,
-      (counter_t) i->rx_bytes, (counter_t) i->tx_bytes);
+      (derive_t) i->rx_bytes, (derive_t) i->tx_bytes);
   cr_submit_io (rd, "if_errors", i->name,
-      (counter_t) i->rx_errors, (counter_t) i->tx_errors);
+      (derive_t) i->rx_errors, (derive_t) i->tx_errors);
   cr_submit_io (rd, "if_dropped", i->name,
-      (counter_t) i->rx_drops, (counter_t) i->tx_drops);
+      (derive_t) i->rx_drops, (derive_t) i->tx_drops);
 
   submit_interface (rd, i->next);
 } /* }}} void submit_interface */
@@ -116,12 +116,12 @@ static void cr_submit_gauge (cr_data_t *rd, const char *type, /* {{{ */
 
 #if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0)
 static void cr_submit_counter (cr_data_t *rd, const char *type, /* {{{ */
-    const char *type_instance, counter_t value)
+    const char *type_instance, derive_t value)
 {
        value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = value;
+       values[0].derive = value;
 
        vl.values = values;
        vl.values_len = STATIC_ARRAY_SIZE (values);
@@ -166,7 +166,7 @@ static void submit_regtable (cr_data_t *rd, /* {{{ */
   ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
       r->interface, r->radio_name);
   cr_submit_io (rd, "if_octets", type_instance,
-      (counter_t) r->rx_bytes, (counter_t) r->tx_bytes);
+      (derive_t) r->rx_bytes, (derive_t) r->tx_bytes);
   cr_submit_gauge (rd, "snr", type_instance, (gauge_t) r->signal_to_noise);
 
   submit_regtable (rd, r->next);
@@ -212,7 +212,7 @@ static int handle_system_resource (__attribute__((unused)) ros_connection_t *c,
 
   if (rd->collect_disk)
   {
-    cr_submit_counter (rd, "counter", "secors_written", (counter_t) r->write_sect_total);
+    cr_submit_counter (rd, "counter", "secors_written", (derive_t) r->write_sect_total);
     cr_submit_gauge (rd, "gauge", "bad_blocks", (gauge_t) r->bad_blocks);
   }
 
@@ -341,7 +341,7 @@ static int cr_config_router (oconfig_item_t *ci) /* {{{ */
     if (strcasecmp ("Host", child->key) == 0)
       status = cf_util_get_string (child, &router_data->node);
     else if (strcasecmp ("Port", child->key) == 0)
-      status = cf_util_get_string (child, &router_data->service);
+      status = cf_util_get_service (child, &router_data->service);
     else if (strcasecmp ("User", child->key) == 0)
       status = cf_util_get_string (child, &router_data->username);
     else if (strcasecmp ("Password", child->key) == 0)
index 34e860b..e77be2d 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/rrdcached.c
- * 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>
  **/
 
 #include "collectd.h"
 /*
  * Private variables
  */
-static const char *config_keys[] =
-{
-  "DaemonAddress",
-  "DataDir",
-  "CreateFiles",
-  "CollectStatistics"
-};
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
-
 static char *datadir = NULL;
 static char *daemon_address = NULL;
-static int config_create_files = 1;
-static int config_collect_stats = 1;
+static _Bool config_create_files = 1;
+static _Bool config_collect_stats = 1;
 static rrdcreate_config_t rrdcreate_config =
 {
        /* stepsize = */ 0,
@@ -55,21 +46,33 @@ static rrdcreate_config_t rrdcreate_config =
        /* timespans_num = */ 0,
 
        /* consolidation_functions = */ NULL,
-       /* consolidation_functions_num = */ 0
+       /* consolidation_functions_num = */ 0,
+
+       /* async = */ 0
 };
 
+/*
+ * Prototypes.
+ */
+static int rc_write (const data_set_t *ds, const value_list_t *vl,
+    user_data_t __attribute__((unused)) *user_data);
+static int rc_flush (__attribute__((unused)) cdtime_t timeout,
+    const char *identifier, __attribute__((unused)) user_data_t *ud);
+
 static int value_list_to_string (char *buffer, int buffer_len,
     const data_set_t *ds, const value_list_t *vl)
 {
   int offset;
   int status;
   int i;
+  time_t t;
 
   assert (0 == strcmp (ds->type, vl->type));
 
   memset (buffer, '\0', buffer_len);
 
-  status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
+  t = CDTIME_T_TO_TIME_T (vl->time);
+  status = ssnprintf (buffer, buffer_len, "%lu", (unsigned long) t);
   if ((status < 1) || (status >= buffer_len))
     return (-1);
   offset = status;
@@ -111,104 +114,172 @@ 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 */
 
-static int rc_config (const char *key, const char *value)
+static int rc_config_get_int_positive (oconfig_item_t const *ci, int *ret)
 {
-  if (strcasecmp ("DataDir", key) == 0)
+  int status;
+  int tmp = 0;
+
+  status = cf_util_get_int (ci, &tmp);
+  if (status != 0)
+    return (status);
+  if (tmp < 0)
+    return (EINVAL);
+
+  *ret = tmp;
+  return (0);
+} /* int rc_config_get_int_positive */
+
+static int rc_config_get_xff (oconfig_item_t const *ci, double *ret)
+{
+  double value;
+
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
   {
-    if (datadir != NULL)
-      free (datadir);
-    datadir = strdup (value);
-    if (datadir != NULL)
+    ERROR ("rrdcached plugin: The \"%s\" needs exactly one numeric argument "
+        "in the range [0.0, 1.0)", ci->key);
+    return (EINVAL);
+  }
+
+  value = ci->values[0].value.number;
+  if ((value >= 0.0) && (value < 1.0))
+  {
+    *ret = value;
+    return (0);
+  }
+
+  ERROR ("rrdcached plugin: The \"%s\" needs exactly one numeric argument "
+      "in the range [0.0, 1.0)", ci->key);
+  return (EINVAL);
+} /* int rc_config_get_xff */
+
+static int rc_config_add_timespan (int timespan)
+{
+  int *tmp;
+
+  if (timespan <= 0)
+    return (EINVAL);
+
+  tmp = realloc (rrdcreate_config.timespans,
+      sizeof (*rrdcreate_config.timespans)
+      * (rrdcreate_config.timespans_num + 1));
+  if (tmp == NULL)
+    return (ENOMEM);
+  rrdcreate_config.timespans = tmp;
+
+  rrdcreate_config.timespans[rrdcreate_config.timespans_num] = timespan;
+  rrdcreate_config.timespans_num++;
+
+  return (0);
+} /* int rc_config_add_timespan */
+
+static int rc_config (oconfig_item_t *ci)
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t const *child = ci->children + i;
+    const char *key = child->key;
+    int status = 0;
+
+    if (strcasecmp ("DataDir", key) == 0)
     {
-      int len = strlen (datadir);
-      while ((len > 0) && (datadir[len - 1] == '/'))
-      {
-        len--;
-        datadir[len] = '\0';
-      }
-      if (len <= 0)
+      status = cf_util_get_string (child, &datadir);
+      if (status == 0)
       {
-        free (datadir);
-        datadir = NULL;
+        int len = strlen (datadir);
+
+        while ((len > 0) && (datadir[len - 1] == '/'))
+        {
+          len--;
+          datadir[len] = 0;
+        }
+
+        if (len <= 0)
+          sfree (datadir);
       }
     }
-  }
-  else if (strcasecmp ("DaemonAddress", key) == 0)
-  {
-    sfree (daemon_address);
-    daemon_address = strdup (value);
-    if (daemon_address == NULL)
+    else if (strcasecmp ("DaemonAddress", key) == 0)
+      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)
     {
-      ERROR ("rrdcached plugin: strdup failed.");
-      return (1);
+      int tmp = -1;
+
+      status = rc_config_get_int_positive (child, &tmp);
+      if (status == 0)
+        rrdcreate_config.stepsize = (unsigned long) tmp;
     }
-  }
-  else if (strcasecmp ("CreateFiles", key) == 0)
-  {
-    if (IS_FALSE (value))
-      config_create_files = 0;
-    else
-      config_create_files = 1;
-  }
-  else if (strcasecmp ("CollectStatistics", key) == 0)
-  {
-    if (IS_FALSE (value))
-      config_collect_stats = 0;
+    else if (strcasecmp ("HeartBeat", key) == 0)
+      status = rc_config_get_int_positive (child, &rrdcreate_config.heartbeat);
+    else if (strcasecmp ("RRARows", key) == 0)
+      status = rc_config_get_int_positive (child, &rrdcreate_config.rrarows);
+    else if (strcasecmp ("RRATimespan", key) == 0)
+    {
+      int tmp = -1;
+      status = rc_config_get_int_positive (child, &tmp);
+      if (status == 0)
+        status = rc_config_add_timespan (tmp);
+    }
+    else if (strcasecmp ("XFF", key) == 0)
+      status = rc_config_get_xff (child, &rrdcreate_config.xff);
     else
-      config_collect_stats = 1;
+    {
+      WARNING ("rrdcached plugin: Ignoring invalid option %s.", key);
+      continue;
+    }
+
+    if (status != 0)
+      WARNING ("rrdcached plugin: Handling the \"%s\" option failed.", key);
   }
-  else
+
+  if (daemon_address != NULL)
   {
-    return (-1);
+    plugin_register_write ("rrdcached", rc_write, /* user_data = */ NULL);
+    plugin_register_flush ("rrdcached", rc_flush, /* user_data = */ NULL);
   }
   return (0);
 } /* int rc_config */
@@ -225,7 +296,7 @@ static int rc_read (void)
   if (daemon_address == NULL)
     return (-1);
 
-  if (config_collect_stats == 0)
+  if (!config_collect_stats)
     return (-1);
 
   vl.values = values;
@@ -238,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)
@@ -317,7 +396,7 @@ static int rc_read (void)
 
 static int rc_init (void)
 {
-  if (config_collect_stats != 0)
+  if (config_collect_stats)
     plugin_register_read ("rrdcached", rc_read);
 
   return (0);
@@ -326,7 +405,7 @@ static int rc_init (void)
 static int rc_write (const data_set_t *ds, const value_list_t *vl,
     user_data_t __attribute__((unused)) *user_data)
 {
-  char filename[512];
+  char filename[PATH_MAX];
   char values[512];
   char *values_array[2];
   int status;
@@ -344,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);
@@ -359,7 +438,7 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl,
   values_array[0] = values;
   values_array[1] = NULL;
 
-  if (config_create_files != 0)
+  if (config_create_files)
   {
     struct stat statbuf;
 
@@ -381,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);
     }
   }
 
@@ -404,6 +485,41 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl,
   return (0);
 } /* int rc_write */
 
+static int rc_flush (__attribute__((unused)) cdtime_t timeout, /* {{{ */
+    const char *identifier,
+    __attribute__((unused)) user_data_t *ud)
+{
+  char filename[PATH_MAX + 1];
+  int status;
+
+  if (identifier == NULL)
+    return (EINVAL);
+
+  if (datadir != NULL)
+    ssnprintf (filename, sizeof (filename), "%s/%s.rrd", datadir, identifier);
+  else
+    ssnprintf (filename, sizeof (filename), "%s.rrd", identifier);
+
+  status = rrdc_connect (daemon_address);
+  if (status != 0)
+  {
+    ERROR ("rrdcached plugin: rrdc_connect (%s) failed with status %i.",
+        daemon_address, status);
+    return (-1);
+  }
+
+  status = rrdc_flush (filename);
+  if (status != 0)
+  {
+    ERROR ("rrdcached plugin: rrdc_flush (%s) failed with status %i.",
+        filename, status);
+    return (-1);
+  }
+  DEBUG ("rrdcached plugin: rrdc_flush (%s): Success.", filename);
+
+  return (0);
+} /* }}} int rc_flush */
+
 static int rc_shutdown (void)
 {
   rrdc_disconnect ();
@@ -412,10 +528,8 @@ static int rc_shutdown (void)
 
 void module_register (void)
 {
-  plugin_register_config ("rrdcached", rc_config,
-      config_keys, config_keys_num);
+  plugin_register_complex_config ("rrdcached", rc_config);
   plugin_register_init ("rrdcached", rc_init);
-  plugin_register_write ("rrdcached", rc_write, /* user_data = */ NULL);
   plugin_register_shutdown ("rrdcached", rc_shutdown);
 } /* void module_register */
 
index 2b384d0..71da547 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>
  */
 struct rrd_cache_s
 {
-       int    values_num;
-       char **values;
-       time_t first_value;
-       time_t last_value;
-       int random_variation;
+       int      values_num;
+       char   **values;
+       cdtime_t first_value;
+       cdtime_t last_value;
+       int64_t  random_variation;
        enum
        {
                FLAG_NONE   = 0x00,
@@ -75,6 +76,7 @@ static const char *config_keys[] =
 {
        "CacheTimeout",
        "CacheFlush",
+       "CreateFilesAsync",
        "DataDir",
        "StepSize",
        "HeartBeat",
@@ -102,15 +104,17 @@ 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,
  * ALWAYS lock `cache_lock' first! */
-static int         cache_timeout = 0;
-static int         cache_flush_timeout = 0;
-static int         random_timeout = 1;
-static time_t      cache_flush_last;
+static cdtime_t    cache_timeout = 0;
+static cdtime_t    cache_flush_timeout = 0;
+static cdtime_t    random_timeout = TIME_T_TO_CDTIME_T (1);
+static cdtime_t    cache_flush_last;
 static c_avl_tree_t *cache = NULL;
 static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
 
@@ -199,11 +203,13 @@ static int value_list_to_string (char *buffer, int buffer_len,
 {
        int offset;
        int status;
+       time_t tt;
        int i;
 
        memset (buffer, '\0', buffer_len);
 
-       status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
+       tt = CDTIME_T_TO_TIME_T (vl->time);
+       status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) tt);
        if ((status < 1) || (status >= buffer_len))
                return (-1);
        offset = status;
@@ -238,47 +244,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 __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 */
 
@@ -303,7 +303,7 @@ static void *rrd_queue_thread (void __attribute__((unused)) *data)
 
                 pthread_mutex_lock (&queue_lock);
                 /* Wait for values to arrive */
-                while (true)
+                while (42)
                 {
                   struct timespec ts_wait;
 
@@ -342,7 +342,7 @@ static void *rrd_queue_thread (void __attribute__((unused)) *data)
                       &ts_wait);
                   if (status == ETIMEDOUT)
                     break;
-                } /* while (true) */
+                } /* while (42) */
 
                 /* XXX: If you need to lock both, cache_lock and queue_lock, at
                  * the same time, ALWAYS lock `cache_lock' first! */
@@ -510,10 +510,11 @@ static int rrd_queue_dequeue (const char *filename,
   return (0);
 } /* int rrd_queue_dequeue */
 
-static void rrd_cache_flush (int timeout)
+/* XXX: You must hold "cache_lock" when calling this function! */
+static void rrd_cache_flush (cdtime_t timeout)
 {
        rrd_cache_t *rc;
-       time_t       now;
+       cdtime_t     now;
 
        char **keys = NULL;
        int    keys_num = 0;
@@ -522,9 +523,11 @@ static void rrd_cache_flush (int timeout)
        c_avl_iterator_t *iter;
        int i;
 
-       DEBUG ("rrdtool plugin: Flushing cache, timeout = %i", timeout);
+       DEBUG ("rrdtool plugin: Flushing cache, timeout = %.3f",
+                       CDTIME_T_TO_DOUBLE (timeout));
 
-       now = time (NULL);
+       now = cdtime ();
+       timeout = TIME_T_TO_CDTIME_T (timeout);
 
        /* Build a list of entries to be flushed */
        iter = c_avl_get_iterator (cache);
@@ -532,7 +535,9 @@ static void rrd_cache_flush (int timeout)
        {
                if (rc->flags != FLAG_NONE)
                        continue;
-               else if ((now - rc->first_value) < timeout)
+               /* timeout == 0  =>  flush everything */
+               else if ((timeout != 0)
+                               && ((now - rc->first_value) < timeout))
                        continue;
                else if (rc->values_num > 0)
                {
@@ -585,10 +590,11 @@ static void rrd_cache_flush (int timeout)
        cache_flush_last = now;
 } /* void rrd_cache_flush */
 
-static int rrd_cache_flush_identifier (int timeout, const char *identifier)
+static int rrd_cache_flush_identifier (cdtime_t timeout,
+    const char *identifier)
 {
   rrd_cache_t *rc;
-  time_t now;
+  cdtime_t now;
   int status;
   char key[2048];
 
@@ -598,7 +604,7 @@ static int rrd_cache_flush_identifier (int timeout, const char *identifier)
     return (0);
   }
 
-  now = time (NULL);
+  now = cdtime ();
 
   if (datadir == NULL)
     snprintf (key, sizeof (key), "%s.rrd",
@@ -642,8 +648,30 @@ static int rrd_cache_flush_identifier (int timeout, const char *identifier)
   return (status);
 } /* int rrd_cache_flush_identifier */
 
+static int64_t rrd_get_random_variation (void)
+{
+  long min;
+  long max;
+
+  if (random_timeout <= 0)
+    return (0);
+
+  /* Assure that "cache_timeout + random_variation" is never negative. */
+  if (random_timeout > cache_timeout)
+  {
+         INFO ("rrdtool plugin: Adjusting \"RandomTimeout\" to %.3f seconds.",
+                         CDTIME_T_TO_DOUBLE (cache_timeout));
+         random_timeout = cache_timeout;
+  }
+
+  max = (long) (random_timeout / 2);
+  min = max - ((long) random_timeout);
+
+  return ((int64_t) cdrand_range (min, max));
+} /* int64_t rrd_get_random_variation */
+
 static int rrd_cache_insert (const char *filename,
-               const char *value, time_t value_time)
+               const char *value, cdtime_t value_time)
 {
        rrd_cache_t *rc = NULL;
        int new_rc = 0;
@@ -664,14 +692,14 @@ static int rrd_cache_insert (const char *filename,
 
        if (rc == NULL)
        {
-               rc = (rrd_cache_t *) malloc (sizeof (rrd_cache_t));
+               rc = malloc (sizeof (*rc));
                if (rc == NULL)
                        return (-1);
                rc->values_num = 0;
                rc->values = NULL;
                rc->first_value = 0;
                rc->last_value = 0;
-               rc->random_variation = 0;
+               rc->random_variation = rrd_get_random_variation ();
                rc->flags = FLAG_NONE;
                new_rc = 1;
        }
@@ -679,9 +707,9 @@ static int rrd_cache_insert (const char *filename,
        if (rc->last_value >= value_time)
        {
                pthread_mutex_unlock (&cache_lock);
-               DEBUG ("rrdtool plugin: (rc->last_value = %u) >= (value_time = %u)",
-                               (unsigned int) rc->last_value,
-                               (unsigned int) value_time);
+               DEBUG ("rrdtool plugin: (rc->last_value = %"PRIu64") "
+                               ">= (value_time = %"PRIu64")",
+                               rc->last_value, value_time);
                return (-1);
        }
 
@@ -738,11 +766,11 @@ static int rrd_cache_insert (const char *filename,
        }
 
        DEBUG ("rrdtool plugin: rrd_cache_insert: file = %s; "
-                       "values_num = %i; age = %lu;",
+                       "values_num = %i; age = %.3f;",
                        filename, rc->values_num,
-                       (unsigned long)(rc->last_value - rc->first_value));
+                       CDTIME_T_TO_DOUBLE (rc->last_value - rc->first_value));
 
-       if ((rc->last_value + rc->random_variation - rc->first_value) >= cache_timeout)
+       if ((rc->last_value - rc->first_value) >= (cache_timeout + rc->random_variation))
        {
                /* XXX: If you need to lock both, cache_lock and queue_lock, at
                 * the same time, ALWAYS lock `cache_lock' first! */
@@ -754,17 +782,7 @@ static int rrd_cache_insert (const char *filename,
                        if (status == 0)
                                rc->flags = FLAG_QUEUED;
 
-                       /* Update the jitter value. Negative values are
-                        * slightly preferred. */
-                       if (random_timeout > 0)
-                       {
-                               rc->random_variation = (rand () % (2 * random_timeout))
-                                       - random_timeout;
-                       }
-                       else
-                       {
-                               rc->random_variation = 0;
-                       }
+                        rc->random_variation = rrd_get_random_variation ();
                }
                else
                {
@@ -773,7 +791,7 @@ static int rrd_cache_insert (const char *filename,
        }
 
        if ((cache_timeout > 0) &&
-                       ((time (NULL) - cache_flush_last) > cache_flush_timeout))
+                       ((cdtime () - cache_flush_last) > cache_flush_timeout))
                rrd_cache_flush (cache_flush_timeout);
 
        pthread_mutex_unlock (&cache_lock);
@@ -863,7 +881,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)
@@ -877,6 +895,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
                {
@@ -899,8 +919,8 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl,
        return (status);
 } /* int rrd_write */
 
-static int rrd_flush (int timeout, const char *identifier,
-               user_data_t __attribute__((unused)) *user_data)
+static int rrd_flush (cdtime_t timeout, const char *identifier,
+               __attribute__((unused)) user_data_t *user_data)
 {
        pthread_mutex_lock (&cache_lock);
 
@@ -919,7 +939,7 @@ static int rrd_config (const char *key, const char *value)
 {
        if (strcasecmp ("CacheTimeout", key) == 0)
        {
-               int tmp = atoi (value);
+               double tmp = atof (value);
                if (tmp < 0)
                {
                        fprintf (stderr, "rrdtool: `CacheTimeout' must "
@@ -928,7 +948,7 @@ static int rrd_config (const char *key, const char *value)
                                        "be greater than 0.\n");
                        return (1);
                }
-               cache_timeout = tmp;
+               cache_timeout = DOUBLE_TO_CDTIME_T (tmp);
        }
        else if (strcasecmp ("CacheFlush", key) == 0)
        {
@@ -965,7 +985,7 @@ static int rrd_config (const char *key, const char *value)
        }
        else if (strcasecmp ("StepSize", key) == 0)
        {
-               int temp = atoi (value);
+               unsigned long temp = strtoul (value, NULL, 0);
                if (temp > 0)
                        rrdcreate_config.stepsize = temp;
        }
@@ -975,6 +995,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);
@@ -1061,10 +1088,10 @@ static int rrd_config (const char *key, const char *value)
        }
        else if (strcasecmp ("RandomTimeout", key) == 0)
         {
-               int tmp;
+               double tmp;
 
-               tmp = atoi (value);
-               if (tmp < 0)
+               tmp = atof (value);
+               if (tmp < 0.0)
                {
                        fprintf (stderr, "rrdtool: `RandomTimeout' must "
                                        "be greater than or equal to zero.\n");
@@ -1073,7 +1100,7 @@ static int rrd_config (const char *key, const char *value)
                }
                else
                {
-                       random_timeout = tmp;
+                       random_timeout = DOUBLE_TO_CDTIME_T (tmp);
                }
        }
        else
@@ -1086,7 +1113,7 @@ static int rrd_config (const char *key, const char *value)
 static int rrd_shutdown (void)
 {
        pthread_mutex_lock (&cache_lock);
-       rrd_cache_flush (-1);
+       rrd_cache_flush (0);
        pthread_mutex_unlock (&cache_lock);
 
        pthread_mutex_lock (&queue_lock);
@@ -1128,22 +1155,9 @@ static int rrd_init (void)
                return (0);
        init_once = 1;
 
-       if (rrdcreate_config.stepsize < 0)
-               rrdcreate_config.stepsize = 0;
        if (rrdcreate_config.heartbeat <= 0)
                rrdcreate_config.heartbeat = 2 * rrdcreate_config.stepsize;
 
-       if ((rrdcreate_config.heartbeat > 0)
-                       && (rrdcreate_config.heartbeat < interval_g))
-               WARNING ("rrdtool plugin: Your `heartbeat' is "
-                               "smaller than your `interval'. This will "
-                               "likely cause problems.");
-       else if ((rrdcreate_config.stepsize > 0)
-                       && (rrdcreate_config.stepsize < interval_g))
-               WARNING ("rrdtool plugin: Your `stepsize' is "
-                               "smaller than your `interval'. This will "
-                               "create needlessly big RRD-files.");
-
        /* Set the cache up */
        pthread_mutex_lock (&cache_lock);
 
@@ -1154,10 +1168,9 @@ static int rrd_init (void)
                return (-1);
        }
 
-       cache_flush_last = time (NULL);
-       if (cache_timeout < 2)
+       cache_flush_last = cdtime ();
+       if (cache_timeout == 0)
        {
-               cache_timeout = 0;
                cache_flush_timeout = 0;
        }
        else if (cache_flush_timeout < cache_timeout)
@@ -1165,7 +1178,7 @@ static int rrd_init (void)
 
        pthread_mutex_unlock (&cache_lock);
 
-       status = pthread_create (&queue_thread, /* attr = */ NULL,
+       status = plugin_thread_create (&queue_thread, /* attr = */ NULL,
                        rrd_queue_thread, /* args = */ NULL);
        if (status != 0)
        {
@@ -1174,7 +1187,7 @@ static int rrd_init (void)
        }
        queue_thread_running = 1;
 
-       DEBUG ("rrdtool plugin: rrd_init: datadir = %s; stepsize = %i;"
+       DEBUG ("rrdtool plugin: rrd_init: datadir = %s; stepsize = %lu;"
                        " heartbeat = %i; rrarows = %i; xff = %lf;",
                        (datadir == NULL) ? "(null)" : datadir,
                        rrdcreate_config.stepsize,
index 8391346..209482e 100644 (file)
  *   - honor sensors.conf's ignored
  *   - config Sensor option
  *   - config IgnoreSelected option
+ *
+ *   Henrique de Moraes Holschuh <hmh at debian.org>
+ *   - use default libsensors config file on API 0x400
+ *   - config SensorConfigFile option
  **/
 
 #include "collectd.h"
@@ -132,7 +136,8 @@ static int known_features_num = STATIC_ARRAY_SIZE (known_features);
 static const char *config_keys[] =
 {
        "Sensor",
-       "IgnoreSelected"
+       "IgnoreSelected",
+       "SensorConfigFile"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -148,6 +153,7 @@ typedef struct featurelist
 # ifndef SENSORS_CONF_PATH
 #  define SENSORS_CONF_PATH "/etc/sensors.conf"
 # endif
+static char *conffile = SENSORS_CONF_PATH;
 /* #endif SENSORS_API_VERSION < 0x400 */
 
 #elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500)
@@ -159,9 +165,7 @@ typedef struct featurelist
        struct featurelist         *next;
 } featurelist_t;
 
-# ifndef SENSORS_CONF_PATH
-#  define SENSORS_CONF_PATH "/etc/sensors3.conf"
-# endif
+static char *conffile = NULL;
 /* #endif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */
 
 #else /* if SENSORS_API_VERSION >= 0x500 */
@@ -169,10 +173,8 @@ typedef struct featurelist
        "as bug."
 #endif
 
-static const char *conffile = SENSORS_CONF_PATH;
 featurelist_t *first_feature = NULL;
 static ignorelist_t *sensor_list;
-static time_t sensors_config_mtime = 0;
 
 #if SENSORS_API_VERSION < 0x400
 /* full chip name logic borrowed from lm_sensors */
@@ -225,7 +227,19 @@ static int sensors_config (const char *key, const char *value)
        if (sensor_list == NULL)
                sensor_list = ignorelist_create (1);
 
-       if (strcasecmp (key, "Sensor") == 0)
+       /* TODO: This setting exists for compatibility with old versions of
+        * lm-sensors. Remove support for those ancient versions in the next
+        * major release. */
+       if (strcasecmp (key, "SensorConfigFile") == 0)
+       {
+               char *tmp = strdup (value);
+               if (tmp != NULL)
+               {
+                       sfree (conffile);
+                       conffile = tmp;
+               }
+       }
+       else if (strcasecmp (key, "Sensor") == 0)
        {
                if (ignorelist_add (sensor_list, value))
                {
@@ -268,47 +282,37 @@ void sensors_free_features (void)
 
 static int sensors_load_conf (void)
 {
-       FILE *fh;
+       static int call_once = 0;
+
+       FILE *fh = NULL;
        featurelist_t *last_feature = NULL;
        
        const sensors_chip_name *chip;
        int chip_num;
 
-       struct stat statbuf;
        int status;
-       
-       status = stat (conffile, &statbuf);
-       if (status != 0)
-       {
-               char errbuf[1024];
-               ERROR ("sensors plugin: stat (%s) failed: %s", conffile,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               sensors_config_mtime = 0;
-       }
 
-       if ((sensors_config_mtime != 0)
-                       && (sensors_config_mtime == statbuf.st_mtime))
-               return (0);
+       if (call_once)
+               return 0;
 
-       if (sensors_config_mtime != 0)
-       {
-               NOTICE ("sensors plugin: Reloading config from %s",
-                               conffile);
-               sensors_free_features ();
-               sensors_config_mtime = 0;
-       }
+       call_once = 1;
 
-       fh = fopen (conffile, "r");
-       if (fh == NULL)
+       if (conffile != NULL)
        {
-               char errbuf[1024];
-               ERROR ("sensors plugin: fopen(%s) failed: %s", conffile,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (-1);
+               fh = fopen (conffile, "r");
+               if (fh == NULL)
+               {
+                       char errbuf[1024];
+                       ERROR ("sensors plugin: fopen(%s) failed: %s", conffile,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       return (-1);
+               }
        }
 
        status = sensors_init (fh);
-       fclose (fh);
+       if (fh)
+               fclose (fh);
+
        if (status != 0)
        {
                ERROR ("sensors plugin: Cannot initialize sensors. "
@@ -316,8 +320,6 @@ static int sensors_load_conf (void)
                return (-1);
        }
 
-       sensors_config_mtime = statbuf.st_mtime;
-
 #if SENSORS_API_VERSION < 0x400
        chip_num = 0;
        while ((chip = sensors_get_detected_chips (&chip_num)) != NULL)
index cfa26bb..9bd885d 100644 (file)
 #endif
 
 static void serial_submit (const char *type_instance,
-               counter_t rx, counter_t tx)
+               derive_t rx, derive_t tx)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = rx;
-       values[1].counter = tx;
+       values[0].derive = rx;
+       values[1].derive = tx;
 
        vl.values = values;
        vl.values_len = 2;
@@ -54,8 +54,8 @@ static int serial_read (void)
        FILE *fh;
        char buffer[1024];
 
-       counter_t rx = 0;
-       counter_t tx = 0;
+       derive_t rx = 0;
+       derive_t tx = 0;
        
        char *fields[16];
        int i, numfields;
index 8ab2bf8..7d340d1 100644 (file)
@@ -69,7 +69,7 @@ struct host_definition_s
   int version;
   void *sess_handle;
   c_complain_t complaint;
-  uint32_t interval;
+  cdtime_t interval;
   data_definition_t **data_list;
   int data_list_len;
 };
@@ -108,7 +108,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;
 }
@@ -207,7 +207,6 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */
  *      +-> csnmp_config_add_host_community
  *      +-> csnmp_config_add_host_version
  *      +-> csnmp_config_add_host_collect
- *      +-> csnmp_config_add_host_interval
  */
 static void call_snmp_init_once (void)
 {
@@ -591,22 +590,6 @@ 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_interval (host_definition_t *hd, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
-  {
-    WARNING ("snmp plugin: The `Interval' config option needs exactly one number argument.");
-    return (-1);
-  }
-
-  hd->interval = ci->values[0].value.number >= 0
-    ? (uint32_t) ci->values[0].value.number
-    : 0;
-
-  return (0);
-} /* int csnmp_config_add_host_interval */
-
 static int csnmp_config_add_host (oconfig_item_t *ci)
 {
   host_definition_t *hd;
@@ -655,7 +638,7 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
     else if (strcasecmp ("Collect", option->key) == 0)
       csnmp_config_add_host_collect (hd, option);
     else if (strcasecmp ("Interval", option->key) == 0)
-      csnmp_config_add_host_interval (hd, option);
+      cf_util_get_cdtime (option, &hd->interval);
     else
     {
       WARNING ("snmp plugin: csnmp_config_add_host: Option `%s' not allowed here.", option->key);
@@ -699,9 +682,7 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
   cb_data.data = hd;
   cb_data.free_func = csnmp_host_definition_destroy;
 
-  memset (&cb_interval, 0, sizeof (cb_interval));
-  if (hd->interval != 0)
-    cb_interval.tv_sec = (time_t) hd->interval;
+  CDTIME_T_TO_TIMESPEC (hd->interval, &cb_interval);
 
   status = plugin_register_complex_read (/* group = */ NULL, cb_name,
       csnmp_read_host, /* interval = */ &cb_interval,
@@ -770,7 +751,8 @@ static void csnmp_host_open_session (host_definition_t *host)
 
 /* TODO: Check if negative values wrap around. Problem: negative temperatures. */
 static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
-    double scale, double shift)
+    double scale, double shift,
+    const char *host_name, const char *data_name)
 {
   value_t ret;
   uint64_t tmp_unsigned = 0;
@@ -822,8 +804,11 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
           oid_buffer);
     else
 #endif
-      WARNING ("snmp plugin: I don't know the ASN type \"%i\" (OID: %s)",
-          (int) vl->type, oid_buffer);
+      WARNING ("snmp plugin: I don't know the ASN type #%i "
+               "(OID: \"%s\", data block \"%s\", host block \"%s\")",
+          (int) vl->type, oid_buffer,
+          (data_name != NULL) ? data_name : "UNKNOWN",
+          (host_name != NULL) ? host_name : "UNKNOWN");
 
     defined = 0;
   }
@@ -911,71 +896,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)
 {
@@ -1044,8 +964,8 @@ static int csnmp_strvbcopy (char *dst, /* {{{ */
 
 static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     csnmp_list_instances_t **tail,
-    struct snmp_pdu const *res,
-    oid_t const *root)
+    const struct snmp_pdu *res,
+    const host_definition_t *hd, const data_definition_t *dd)
 {
   csnmp_list_instances_t *il;
   struct variable_list *vb;
@@ -1071,7 +991,7 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
   memset (il, 0, sizeof (*il));
   il->next = NULL;
 
-  status = csnmp_oid_suffix (&il->suffix, &vb_name, root);
+  status = csnmp_oid_suffix (&il->suffix, &vb_name, &dd->instance.oid);
   if (status != 0)
   {
     sfree (il);
@@ -1096,7 +1016,8 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
   }
   else
   {
-    value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER, 1.0, 0.0);
+    value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER,
+        /* scale = */ 1.0, /* shift = */ 0.0, hd->name, dd->name);
     ssnprintf (il->instance, sizeof (il->instance),
         "%llu", val.counter);
   }
@@ -1278,11 +1199,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
@@ -1316,19 +1244,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. */
@@ -1337,7 +1261,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);
@@ -1349,6 +1272,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)
     {
@@ -1357,12 +1282,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;
@@ -1385,6 +1324,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,
@@ -1398,103 +1338,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, &data->instance.oid) != 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;
-      }
-
-      /* 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);
-        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;
+        }
       }
-
-      vt = malloc (sizeof (*vt));
-      if (vt == NULL)
+      else /* The variable we are processing is a normal value */
       {
-        ERROR ("snmp plugin: malloc failed.");
-        status = -1;
-        break;
+        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->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);
-      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);
@@ -1532,7 +1466,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 */
@@ -1637,7 +1570,7 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
       if (snmp_oid_compare (data->values[i].oid, data->values[i].oid_len,
             vb->name, vb->name_length) == 0)
         vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type,
-            data->scale, data->shift);
+            data->scale, data->shift, host->name, data->name);
   } /* for (res->variables) */
 
   if (res != NULL)
@@ -1654,8 +1587,8 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
 static int csnmp_read_host (user_data_t *ud)
 {
   host_definition_t *host;
-  time_t time_start;
-  time_t time_end;
+  cdtime_t time_start;
+  cdtime_t time_end;
   int status;
   int success;
   int i;
@@ -1663,11 +1596,9 @@ static int csnmp_read_host (user_data_t *ud)
   host = ud->data;
 
   if (host->interval == 0)
-    host->interval = interval_g;
+    host->interval = plugin_get_interval ();
 
-  time_start = time (NULL);
-  DEBUG ("snmp plugin: csnmp_read_host (%s) started at %u;", host->name,
-      (unsigned int) time_start);
+  time_start = cdtime ();
 
   if (host->sess_handle == NULL)
     csnmp_host_open_session (host);
@@ -1689,14 +1620,14 @@ static int csnmp_read_host (user_data_t *ud)
       success++;
   }
 
-  time_end = time (NULL);
-  DEBUG ("snmp plugin: csnmp_read_host (%s) finished at %u;", host->name,
-      (unsigned int) time_end);
-  if ((uint32_t) (time_end - time_start) > host->interval)
+  time_end = cdtime ();
+  if ((time_end - time_start) > host->interval)
   {
-    WARNING ("snmp plugin: Host `%s' should be queried every %"PRIu32
-        " seconds, but reading all values takes %u seconds.",
-        host->name, host->interval, (unsigned int) (time_end - time_start));
+    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));
   }
 
   if (success == 0)
index 46ba665..1621f7a 100644 (file)
@@ -1,8 +1,9 @@
 /**
  * collectd - src/swap.c
- * Copyright (C) 2005-2009  Florian octo Forster
+ * Copyright (C) 2005-2012  Florian octo Forster
  * Copyright (C) 2009       Stefan Völkel
  * Copyright (C) 2009       Manuel Sanmartin
+ * Copyright (C) 2010       Aurélien Reynaud
  *
  * 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
@@ -18,8 +19,9 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Manuel Sanmartin
+ *   Aurélien Reynaud <collectd at wattapower.net>
  **/
 
 #if HAVE_CONFIG_H
 #define MAX(x,y) ((x) > (y) ? (x) : (y))
 
 #if KERNEL_LINUX
-/* No global variables */
+# define SWAP_HAVE_REPORT_BY_DEVICE 1
+static derive_t pagesize;
+static _Bool report_bytes = 0;
+static _Bool report_by_device = 0;
 /* #endif KERNEL_LINUX */
 
-#elif HAVE_LIBKSTAT
+#elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
+# define SWAP_HAVE_REPORT_BY_DEVICE 1
 static derive_t pagesize;
-static kstat_t *ksp;
-/* #endif HAVE_LIBKSTAT */
-
-#elif HAVE_SWAPCTL
-/* No global variables */
-/* #endif HAVE_SWAPCTL */
+static _Bool report_by_device = 0;
+/* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
 
 #elif defined(VM_SWAPUSAGE)
 /* No global variables */
@@ -102,21 +104,55 @@ static perfstat_memory_total_t pmemory;
 # error "No applicable input method."
 #endif /* HAVE_LIBSTATGRAB */
 
-static int swap_init (void)
+static const char *config_keys[] =
 {
+       "ReportBytes",
+       "ReportByDevice"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static int swap_config (const char *key, const char *value) /* {{{ */
+{
+       if (strcasecmp ("ReportBytes", key) == 0)
+       {
 #if KERNEL_LINUX
-       /* No init stuff */
+               report_bytes = IS_TRUE (value) ? 1 : 0;
+#else
+               WARNING ("swap plugin: The \"ReportBytes\" option is only "
+                               "valid under Linux. "
+                               "The option is going to be ignored.");
+#endif
+       }
+       else if (strcasecmp ("ReportByDevice", key) == 0)
+       {
+#if SWAP_HAVE_REPORT_BY_DEVICE
+               if (IS_TRUE (value))
+                       report_by_device = 1;
+               else
+                       report_by_device = 0;
+#else
+               WARNING ("swap plugin: The \"ReportByDevice\" option is not "
+                               "supported on this platform. "
+                               "The option is going to be ignored.");
+#endif /* SWAP_HAVE_REPORT_BY_DEVICE */
+       }
+       else
+       {
+               return (-1);
+       }
+
+       return (0);
+} /* }}} int swap_config */
+
+static int swap_init (void) /* {{{ */
+{
+#if KERNEL_LINUX
+       pagesize = (derive_t) sysconf (_SC_PAGESIZE);
 /* #endif KERNEL_LINUX */
 
-#elif HAVE_LIBKSTAT
+#elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
        /* getpagesize(3C) tells me this does not fail.. */
        pagesize = (derive_t) getpagesize ();
-       if (get_kstat (&ksp, "unix", 0, "system_pages"))
-               ksp = NULL;
-/* #endif HAVE_LIBKSTAT */
-
-#elif HAVE_SWAPCTL
-       /* No init stuff */
 /* #endif HAVE_SWAPCTL */
 
 #elif defined(VM_SWAPUSAGE)
@@ -153,93 +189,191 @@ static int swap_init (void)
 #endif /* HAVE_PERFSTAT */
 
        return (0);
-}
+} /* }}} int swap_init */
 
-static void swap_submit (const char *type_instance, derive_t value, unsigned type)
+static void swap_submit (const char *plugin_instance, /* {{{ */
+               const char *type, const char *type_instance,
+               value_t value)
 {
-       value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       switch (type)
-       {
-               case DS_TYPE_GAUGE:
-                       values[0].gauge = (gauge_t) value;
-                       sstrncpy (vl.type, "swap", sizeof (vl.type));
-                       break;
-               case DS_TYPE_DERIVE:
-                       values[0].derive = value;
-                       sstrncpy (vl.type, "swap_io", sizeof (vl.type));
-                       break;
-               default:
-                       ERROR ("swap plugin: swap_submit called with wrong"
-                               " type");
-       }
+       assert (type != NULL);
 
-       vl.values = values;
+       vl.values = &value;
        vl.values_len = 1;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "swap", sizeof (vl.plugin));
-       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+       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 */
+} /* }}} void swap_submit_inst */
+
+static void swap_submit_gauge (const char *plugin_instance, /* {{{ */
+               const char *type_instance, gauge_t value)
+{
+       value_t v;
+
+       v.gauge = value;
+       swap_submit (plugin_instance, "swap", type_instance, v);
+} /* }}} void swap_submit_gauge */
 
-static int swap_read (void)
+#if KERNEL_LINUX || HAVE_PERFSTAT
+static void swap_submit_derive (const char *plugin_instance, /* {{{ */
+               const char *type_instance, derive_t value)
 {
+       value_t v;
+
+       v.derive = value;
+       swap_submit (plugin_instance, "swap_io", type_instance, v);
+} /* }}} void swap_submit_derive */
+#endif
+
 #if KERNEL_LINUX
+static int swap_read_separate (void) /* {{{ */
+{
        FILE *fh;
        char buffer[1024];
 
-       char *fields[8];
-       int numfields;
+       fh = fopen ("/proc/swaps", "r");
+       if (fh == NULL)
+       {
+               char errbuf[1024];
+               WARNING ("swap plugin: fopen (/proc/swaps) failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
 
-       _Bool old_kernel=0;
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               char *fields[8];
+               int numfields;
+               char *endptr;
+
+               char path[PATH_MAX];
+               gauge_t size;
+               gauge_t used;
+               gauge_t free;
+
+               numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+               if (numfields != 5)
+                       continue;
+
+               sstrncpy (path, fields[0], sizeof (path));
+               escape_slashes (path, sizeof (path));
+
+               errno = 0;
+               endptr = NULL;
+               size = strtod (fields[2], &endptr);
+               if ((endptr == fields[2]) || (errno != 0))
+                       continue;
+
+               errno = 0;
+               endptr = NULL;
+               used = strtod (fields[3], &endptr);
+               if ((endptr == fields[3]) || (errno != 0))
+                       continue;
+
+               if (size < used)
+                       continue;
+
+               free = size - used;
+
+               swap_submit_gauge (path, "used", used);
+               swap_submit_gauge (path, "free", free);
+       }
+
+       fclose (fh);
+
+       return (0);
+} /* }}} int swap_read_separate */
+
+static int swap_read_combined (void) /* {{{ */
+{
+       FILE *fh;
+       char buffer[1024];
 
-       derive_t swap_used   = 0;
-       derive_t swap_cached = 0;
-       derive_t swap_free   = 0;
-       derive_t swap_total  = 0;
-       derive_t swap_in     = 0;
-       derive_t swap_out    = 0;
+       uint8_t have_data = 0;
+       gauge_t swap_used   = 0.0;
+       gauge_t swap_cached = 0.0;
+       gauge_t swap_free   = 0.0;
+       gauge_t swap_total  = 0.0;
 
-       if ((fh = fopen ("/proc/meminfo", "r")) == NULL)
+       fh = fopen ("/proc/meminfo", "r");
+       if (fh == NULL)
        {
                char errbuf[1024];
-               WARNING ("memory: fopen: %s",
+               WARNING ("swap plugin: fopen (/proc/meminfo) failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
 
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
+               char *fields[8];
+               int numfields;
+
                numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
                if (numfields < 2)
                        continue;
 
                if (strcasecmp (fields[0], "SwapTotal:") == 0)
-                       strtoderive (fields[1], &swap_total);
+               {
+                       swap_total = strtod (fields[1], /* endptr = */ NULL);
+                       have_data |= 0x01;
+               }
                else if (strcasecmp (fields[0], "SwapFree:") == 0)
-                       strtoderive (fields[1], &swap_free);
+               {
+                       swap_free = strtod (fields[1], /* endptr = */ NULL);
+                       have_data |= 0x02;
+               }
                else if (strcasecmp (fields[0], "SwapCached:") == 0)
-                       strtoderive (fields[1], &swap_cached);
+               {
+                       swap_cached = strtod (fields[1], /* endptr = */ NULL);
+                       have_data |= 0x04;
+               }
        }
 
-       if (fclose (fh))
-       {
-               char errbuf[1024];
-               WARNING ("memory: fclose: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-       }
+       fclose (fh);
 
-       if ((swap_total == 0LL) || ((swap_free + swap_cached) > swap_total))
-               return (-1);
+       if ((have_data & 0x03) != 0x03)
+               return (ENOENT);
+
+       if (isnan (swap_total)
+                       || (swap_total <= 0.0)
+                       || ((swap_free + swap_cached) > swap_total))
+               return (EINVAL);
 
        swap_used = swap_total - (swap_free + swap_cached);
 
-       if ((fh = fopen ("/proc/vmstat", "r")) == NULL)
+       swap_submit_gauge (NULL, "used",   1024.0 * swap_used);
+       swap_submit_gauge (NULL, "free",   1024.0 * swap_free);
+       if (have_data & 0x04)
+               swap_submit_gauge (NULL, "cached", 1024.0 * swap_cached);
+
+       return (0);
+} /* }}} int swap_read_combined */
+
+static int swap_read_io (void) /* {{{ */
+{
+       FILE *fh;
+       char buffer[1024];
+
+       _Bool old_kernel = 0;
+
+       uint8_t have_data = 0;
+       derive_t swap_in  = 0;
+       derive_t swap_out = 0;
+
+       fh = fopen ("/proc/vmstat", "r");
+       if (fh == NULL)
        {
-               // /proc/vmstat does not exist in kernels <2.6
-               if ((fh = fopen ("/proc/stat", "r")) == NULL )
+               /* /proc/vmstat does not exist in kernels <2.6 */
+               fh = fopen ("/proc/stat", "r");
+               if (fh == NULL)
                {
                        char errbuf[1024];
                        WARNING ("swap: fopen: %s",
@@ -252,6 +386,9 @@ static int swap_read (void)
 
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
+               char *fields[8];
+               int numfields;
+
                numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
 
                if (!old_kernel)
@@ -260,9 +397,15 @@ static int swap_read (void)
                                continue;
 
                        if (strcasecmp ("pswpin", fields[0]) == 0)
+                       {
                                strtoderive (fields[1], &swap_in);
+                               have_data |= 0x01;
+                       }
                        else if (strcasecmp ("pswpout", fields[0]) == 0)
+                       {
                                strtoderive (fields[1], &swap_out);
+                               have_data |= 0x02;
+                       }
                }
                else /* if (old_kernel) */
                {
@@ -277,21 +420,49 @@ static int swap_read (void)
                }
        } /* while (fgets) */
 
-       if (fclose (fh))
+       fclose (fh);
+
+       if (have_data != 0x03)
+               return (ENOENT);
+
+       if (report_bytes)
        {
-               char errbuf[1024];
-               WARNING ("swap: fclose: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               swap_in = swap_in * pagesize;
+               swap_out = swap_out * pagesize;
        }
 
-       swap_submit ("used",   1024 * swap_used,   DS_TYPE_GAUGE);
-       swap_submit ("free",   1024 * swap_free,   DS_TYPE_GAUGE);
-       swap_submit ("cached", 1024 * swap_cached, DS_TYPE_GAUGE);
-       swap_submit ("in",  swap_in,  DS_TYPE_DERIVE);
-       swap_submit ("out", swap_out, DS_TYPE_DERIVE);
+       swap_submit_derive (NULL, "in",  swap_in);
+       swap_submit_derive (NULL, "out", swap_out);
+
+       return (0);
+} /* }}} int swap_read_io */
+
+static int swap_read (void) /* {{{ */
+{
+       if (report_by_device)
+               swap_read_separate ();
+       else
+               swap_read_combined ();
+
+       swap_read_io ();
+
+       return (0);
+} /* }}} int swap_read */
 /* #endif KERNEL_LINUX */
 
-#elif HAVE_LIBKSTAT
+/*
+ * Under Solaris, two mechanisms can be used to read swap statistics, swapctl
+ * and kstat. The former reads physical space used on a device, the latter
+ * reports the view from the virtual memory system. It was decided that the
+ * kstat-based information should be moved to the "vmem" plugin, but nobody
+ * with enough Solaris experience was available at that time to do this. The
+ * code below is still there for your reference but it won't be activated in
+ * *this* plugin again. --octo
+ */
+#elif 0 && HAVE_LIBKSTAT
+/* kstat-based read function */
+static int swap_read_kstat (void) /* {{{ */
+{
        derive_t swap_alloc;
        derive_t swap_resv;
        derive_t swap_avail;
@@ -332,12 +503,139 @@ static int swap_read (void)
                        * pagesize);
        swap_avail  = (derive_t) ((ai.ani_max - ai.ani_resv) * pagesize);
 
-       swap_submit ("used", swap_alloc, DS_TYPE_GAUGE);
-       swap_submit ("free", swap_avail, DS_TYPE_GAUGE);
-       swap_submit ("reserved", swap_resv, DS_TYPE_GAUGE);
-/* #endif HAVE_LIBKSTAT */
+       swap_submit_gauge (NULL, "used", swap_alloc);
+       swap_submit_gauge (NULL, "free", swap_avail);
+       swap_submit_gauge (NULL, "reserved", swap_resv);
 
-#elif HAVE_SWAPCTL
+       return (0);
+} /* }}} int swap_read_kstat */
+/* #endif 0 && HAVE_LIBKSTAT */
+
+#elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
+/* swapctl-based read function */
+static int swap_read (void) /* {{{ */
+{
+        swaptbl_t *s;
+       char *s_paths;
+        int swap_num;
+        int status;
+        int i;
+
+        derive_t avail = 0;
+        derive_t total = 0;
+
+        swap_num = swapctl (SC_GETNSWP, NULL);
+        if (swap_num < 0)
+        {
+                ERROR ("swap plugin: swapctl (SC_GETNSWP) failed with status %i.",
+                                swap_num);
+                return (-1);
+        }
+        else if (swap_num == 0)
+                return (0);
+
+       /* Allocate and initialize the swaptbl_t structure */
+        s = (swaptbl_t *) smalloc (swap_num * sizeof (swapent_t) + sizeof (struct swaptable));
+        if (s == NULL)
+        {
+                ERROR ("swap plugin: smalloc failed.");
+                return (-1);
+        }
+
+       /* Memory to store the path names. We only use these paths when the
+        * separate option has been configured, but it's easier to just
+        * allocate enough memory in any case. */
+       s_paths = calloc (swap_num, PATH_MAX);
+       if (s_paths == NULL)
+       {
+               ERROR ("swap plugin: malloc failed.");
+               sfree (s);
+               return (-1);
+       }
+        for (i = 0; i < swap_num; i++)
+               s->swt_ent[i].ste_path = s_paths + (i * PATH_MAX);
+        s->swt_n = swap_num;
+
+        status = swapctl (SC_LIST, s);
+        if (status < 0)
+        {
+               char errbuf[1024];
+                ERROR ("swap plugin: swapctl (SC_LIST) failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               sfree (s_paths);
+                sfree (s);
+                return (-1);
+        }
+       else if (swap_num < status)
+       {
+               /* more elements returned than requested */
+               ERROR ("swap plugin: I allocated memory for %i structure%s, "
+                               "but swapctl(2) claims to have returned %i. "
+                               "I'm confused and will give up.",
+                               swap_num, (swap_num == 1) ? "" : "s",
+                               status);
+               sfree (s_paths);
+                sfree (s);
+                return (-1);
+       }
+       else if (swap_num > status)
+               /* less elements returned than requested */
+               swap_num = status;
+
+        for (i = 0; i < swap_num; i++)
+        {
+               char path[PATH_MAX];
+               derive_t this_total;
+               derive_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;
+
+               /* Shortcut for the "combined" setting (default) */
+               if (!report_by_device)
+               {
+                       avail += this_avail;
+                       total += this_total;
+                       continue;
+               }
+
+               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);
+        } /* for (swap_num) */
+
+        if (total < avail)
+        {
+                ERROR ("swap plugin: Total swap space (%"PRIi64") "
+                                "is less than free swap space (%"PRIi64").",
+                                total, avail);
+               sfree (s_paths);
+                sfree (s);
+                return (-1);
+        }
+
+       /* If the "separate" option was specified (report_by_device == 2), 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);
+       }
+
+       sfree (s_paths);
+        sfree (s);
+       return (0);
+} /* }}} int swap_read */
+/* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
+
+#elif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS
+static int swap_read (void) /* {{{ */
+{
        struct swapent *swap_entries;
        int swap_num;
        int status;
@@ -346,18 +644,6 @@ static int swap_read (void)
        derive_t used  = 0;
        derive_t total = 0;
 
-       /*
-        * XXX: This is the syntax for the *BSD `swapctl', which has the
-        * following prototype:
-        *   swapctl (int cmd, void *arg, int misc);
-        *
-        * HP-UX and Solaris (and possibly other UNIXes) provide `swapctl',
-        * too, but with the following prototype:
-        *   swapctl (int cmd, void *arg);
-        *
-        * Solaris is usually handled in the KSTAT case above. For other UNIXes
-        * a separate case for the other version of `swapctl' may be necessary.
-        */
        swap_num = swapctl (SWAP_NSWAP, NULL, 0);
        if (swap_num < 0)
        {
@@ -409,13 +695,18 @@ static int swap_read (void)
                return (-1);
        }
 
-       swap_submit ("used", used, DS_TYPE_GAUGE);
-       swap_submit ("free", total - used, DS_TYPE_GAUGE);
+       swap_submit_gauge (NULL, "used", (gauge_t) used);
+       swap_submit_gauge (NULL, "free", (gauge_t) (total - used));
 
        sfree (swap_entries);
-/* #endif HAVE_SWAPCTL */
+
+       return (0);
+} /* }}} int swap_read */
+/* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS */
 
 #elif defined(VM_SWAPUSAGE)
+static int swap_read (void) /* {{{ */
+{
        int              mib[3];
        size_t           mib_len;
        struct xsw_usage sw_usage;
@@ -431,11 +722,16 @@ static int swap_read (void)
                return (-1);
 
        /* The returned values are bytes. */
-       swap_submit ("used", (derive_t) sw_usage.xsu_used, DS_TYPE_GAUGE);
-       swap_submit ("free", (derive_t) sw_usage.xsu_avail, DS_TYPE_GAUGE);
+       swap_submit_gauge (NULL, "used", (gauge_t) sw_usage.xsu_used);
+       swap_submit_gauge (NULL, "free", (gauge_t) sw_usage.xsu_avail);
+
+       return (0);
+} /* }}} int swap_read */
 /* #endif VM_SWAPUSAGE */
 
 #elif HAVE_LIBKVM_GETSWAPINFO
+static int swap_read (void) /* {{{ */
+{
        struct kvm_swap data_s;
        int             status;
 
@@ -459,11 +755,16 @@ static int swap_read (void)
 
        free = total - used;
 
-       swap_submit ("used", used, DS_TYPE_GAUGE);
-       swap_submit ("free", free, DS_TYPE_GAUGE);
+       swap_submit_gauge (NULL, "used", (gauge_t) used);
+       swap_submit_gauge (NULL, "free", (gauge_t) free);
+
+       return (0);
+} /* }}} int swap_read */
 /* #endif HAVE_LIBKVM_GETSWAPINFO */
 
 #elif HAVE_LIBSTATGRAB
+static int swap_read (void) /* {{{ */
+{
        sg_swap_stats *swap;
 
        swap = sg_get_swap_stats ();
@@ -471,11 +772,16 @@ static int swap_read (void)
        if (swap == NULL)
                return (-1);
 
-       swap_submit ("used", (derive_t) swap->used, DS_TYPE_GAUGE);
-       swap_submit ("free", (derive_t) swap->free, DS_TYPE_GAUGE);
+       swap_submit_gauge (NULL, "used", (gauge_t) swap->used);
+       swap_submit_gauge (NULL, "free", (gauge_t) swap->free);
+
+       return (0);
+} /* }}} int swap_read */
 /* #endif  HAVE_LIBSTATGRAB */
 
 #elif HAVE_PERFSTAT
+static int swap_read (void) /* {{{ */
+{
         if(perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1) < 0)
        {
                 char errbuf[1024];
@@ -483,15 +789,23 @@ static int swap_read (void)
                         sstrerror (errno, errbuf, sizeof (errbuf)));
                 return (-1);
         }
-       swap_submit ("used", (derive_t) (pmemory.pgsp_total - pmemory.pgsp_free) * pagesize, DS_TYPE_GAUGE);
-       swap_submit ("free", (derive_t) pmemory.pgsp_free * pagesize , DS_TYPE_GAUGE);
-#endif /* HAVE_PERFSTAT */
+
+       swap_submit_gauge (NULL, "used", (gauge_t) (pmemory.pgsp_total - pmemory.pgsp_free) * pagesize);
+       swap_submit_gauge (NULL, "free", (gauge_t) pmemory.pgsp_free * pagesize );
+       swap_submit_gauge (NULL, "reserved", (gauge_t) pmemory.pgsp_rsvd * pagesize);
+       swap_submit_derive (NULL, "in",  (derive_t) pmemory.pgspins * pagesize);
+       swap_submit_derive (NULL, "out", (derive_t) pmemory.pgspouts * pagesize);
 
        return (0);
-} /* int swap_read */
+} /* }}} int swap_read */
+#endif /* HAVE_PERFSTAT */
 
 void module_register (void)
 {
+       plugin_register_config ("swap", swap_config,
+                       config_keys, config_keys_num);
        plugin_register_init ("swap", swap_init);
        plugin_register_read ("swap", swap_read);
 } /* void module_register */
+
+/* vim: set fdm=marker : */
index ace9dc6..834ba79 100644 (file)
@@ -33,10 +33,12 @@ static int log_level = LOG_DEBUG;
 #else
 static int log_level = LOG_INFO;
 #endif /* COLLECT_DEBUG */
+static int notif_severity = 0;
 
 static const char *config_keys[] =
 {
-       "LogLevel"
+       "LogLevel",
+       "NotifyLevel",
 };
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
@@ -44,26 +46,20 @@ static int sl_config (const char *key, const char *value)
 {
        if (strcasecmp (key, "LogLevel") == 0)
        {
-               if ((strcasecmp (value, "emerg") == 0)
-                               || (strcasecmp (value, "alert") == 0)
-                               || (strcasecmp (value, "crit") == 0)
-                               || (strcasecmp (value, "err") == 0))
-                       log_level = LOG_ERR;
-               else if (strcasecmp (value, "warning") == 0)
-                       log_level = LOG_WARNING;
-               else if (strcasecmp (value, "notice") == 0)
-                       log_level = LOG_NOTICE;
-               else if (strcasecmp (value, "info") == 0)
+               log_level = parse_log_severity (value);
+               if (log_level < 0)
+               {
                        log_level = LOG_INFO;
-#if COLLECT_DEBUG
-               else if (strcasecmp (value, "debug") == 0)
-                       log_level = LOG_DEBUG;
-#endif
-               else
+                       ERROR ("syslog: invalid loglevel [%s] defaulting to 'info'", value);
+                       return (1);
+               }
+       }
+       else if (strcasecmp (key, "NotifyLevel") == 0)
+       {
+               notif_severity = parse_notif_severity (value);
+               if (notif_severity < 0)
                        return (1);
        }
-       else
-               return (-1);
 
        return (0);
 } /* int sl_config */
@@ -84,11 +80,77 @@ static int sl_shutdown (void)
        return (0);
 }
 
+static int sl_notification (const notification_t *n,
+               user_data_t __attribute__((unused)) *user_data)
+{
+       char  buf[1024] = "";
+       size_t offset = 0;
+       int log_severity;
+       char *severity_string;
+       int status;
+
+       if (n->severity > notif_severity)
+               return (0);
+
+       switch (n->severity)
+       {
+               case NOTIF_FAILURE:
+                       severity_string = "FAILURE";
+                       log_severity = LOG_ERR;
+                       break;
+               case NOTIF_WARNING:
+                       severity_string = "WARNING";
+                       log_severity = LOG_WARNING;
+                       break;
+               case NOTIF_OKAY:
+                       severity_string = "OKAY";
+                       log_severity = LOG_NOTICE;
+                       break;
+               default:
+                       severity_string = "UNKNOWN";
+                       log_severity = LOG_ERR;
+       }
+
+#define BUFFER_ADD(...) do { \
+       status = ssnprintf (&buf[offset], sizeof (buf) - offset, \
+                       __VA_ARGS__); \
+       if (status < 1) \
+               return (-1); \
+       else if (((size_t) status) >= (sizeof (buf) - offset)) \
+               return (-ENOMEM); \
+       else \
+               offset += ((size_t) status); \
+} while (0)
+
+#define BUFFER_ADD_FIELD(field) do { \
+       if (n->field[0]) \
+               BUFFER_ADD (", " #field " = %s", n->field); \
+} while (0)
+
+       BUFFER_ADD ("Notification: severity = %s", severity_string);
+       BUFFER_ADD_FIELD (host);
+       BUFFER_ADD_FIELD (plugin);
+       BUFFER_ADD_FIELD (plugin_instance);
+       BUFFER_ADD_FIELD (type);
+       BUFFER_ADD_FIELD (type_instance);
+       BUFFER_ADD_FIELD (message);
+
+#undef BUFFER_ADD_FIELD
+#undef BUFFER_ADD
+
+       buf[sizeof (buf) - 1] = '\0';
+
+       sl_log (log_severity, buf, NULL);
+
+       return (0);
+} /* int sl_notification */
+
 void module_register (void)
 {
        openlog ("collectd", LOG_CONS | LOG_PID, LOG_DAEMON);
 
        plugin_register_config ("syslog", sl_config, config_keys, config_keys_num);
        plugin_register_log ("syslog", sl_log, /* user_data = */ NULL);
+       plugin_register_notification ("syslog", sl_notification, NULL);
        plugin_register_shutdown ("syslog", sl_shutdown);
 } /* void module_register(void) */
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 32b4296..a8e7dc4 100644 (file)
@@ -58,13 +58,13 @@ static int tape_init (void)
 
 static void tape_submit (const char *plugin_instance,
                const char *type,
-               counter_t read, counter_t write)
+               derive_t read, derive_t write)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = read;
-       values[1].counter = write;
+       values[0].derive = read;
+       values[1].derive = write;
 
        vl.values = values;
        vl.values_len = 2;
index 96598af..cb68048 100644 (file)
@@ -209,7 +209,7 @@ static int tn_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
   /* Initialize the structure. */
   memset (&n, 0, sizeof (n));
   n.severity = data->severity;
-  n.time = time (NULL);
+  n.time = cdtime ();
   sstrncpy (n.message, data->message, sizeof (n.message));
   sstrncpy (n.host, vl->host, sizeof (n.host));
   sstrncpy (n.plugin, vl->plugin, sizeof (n.plugin));
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 29fecdf..bef03e5 100644 (file)
@@ -29,6 +29,9 @@ struct ts_data_s
 {
        double factor;
        double offset;
+
+       char **data_sources;
+       size_t data_sources_num;
 };
 typedef struct ts_data_s ts_data_t;
 
@@ -96,7 +99,7 @@ static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */
                {
                        difference = curr_counter - prev_counter;
                }
-               rate = ((double) difference) / ((double) vl->interval);
+               rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval);
 
                /* Modify the rate. */
                if (!isnan (data->factor))
@@ -105,7 +108,7 @@ static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */
                        rate += data->offset;
 
                /* Calculate the internal counter. */
-               int_fraction += (rate * ((double) vl->interval));
+               int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
                difference = (uint64_t) int_fraction;
                int_fraction -= ((double) difference);
                int_counter  += difference;
@@ -199,7 +202,7 @@ static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */
 
                /* Calcualte the rate */
                difference = curr_derive - prev_derive;
-               rate = ((double) difference) / ((double) vl->interval);
+               rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval);
 
                /* Modify the rate. */
                if (!isnan (data->factor))
@@ -208,7 +211,7 @@ static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */
                        rate += data->offset;
 
                /* Calculate the internal derive. */
-               int_fraction += (rate * ((double) vl->interval));
+               int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
                if (int_fraction < 0.0) /* handle negative integer rounding correctly */
                        difference = ((int64_t) int_fraction) - 1;
                else
@@ -263,7 +266,7 @@ static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */
        if (status != 0)
                int_fraction = 0.0;
 
-       rate = ((double) curr_absolute) / ((double) vl->interval);
+       rate = ((double) curr_absolute) / CDTIME_T_TO_DOUBLE (vl->interval);
 
        /* Modify the rate. */
        if (!isnan (data->factor))
@@ -272,7 +275,7 @@ static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */
                rate += data->offset;
 
        /* Calculate the new absolute. */
-       int_fraction += (rate * ((double) vl->interval));
+       int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
        curr_absolute = (uint64_t) int_fraction;
        int_fraction -= ((double) curr_absolute);
 
@@ -300,17 +303,86 @@ static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
        return (0);
 } /* }}} int ts_config_set_double */
 
+static int ts_config_add_data_source(ts_data_t *data, /* {{{ */
+               oconfig_item_t *ci)
+{
+       size_t new_data_sources_num;
+       char **temp;
+       int i;
+
+       /* Check number of arbuments. */
+       if (ci->values_num < 1)
+       {
+               ERROR ("`value' match: `%s' needs at least one argument.",
+                               ci->key);
+               return (-1);
+       }
+
+       /* Check type of arguments */
+       for (i = 0; i < ci->values_num; i++)
+       {
+               if (ci->values[i].type == OCONFIG_TYPE_STRING)
+                       continue;
+
+               ERROR ("`value' match: `%s' accepts only string arguments "
+                               "(argument %i is a %s).",
+                               ci->key, i + 1,
+                               (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
+                               ? "truth value" : "number");
+               return (-1);
+       }
+
+       /* Allocate space for the char pointers */
+       new_data_sources_num = data->data_sources_num + ((size_t) ci->values_num);
+       temp = (char **) realloc (data->data_sources,
+                       new_data_sources_num * sizeof (char *));
+       if (temp == NULL)
+       {
+               ERROR ("`value' match: realloc failed.");
+               return (-1);
+       }
+       data->data_sources = temp;
+
+       /* Copy the strings, allocating memory as needed.  */
+       for (i = 0; i < ci->values_num; i++)
+       {
+               size_t j;
+
+               /* If we get here, there better be memory for us to write to.  */
+               assert (data->data_sources_num < new_data_sources_num);
+
+               j = data->data_sources_num;
+               data->data_sources[j] = sstrdup (ci->values[i].value.string);
+               if (data->data_sources[j] == NULL)
+               {
+                       ERROR ("`value' match: sstrdup failed.");
+                       continue;
+               }
+               data->data_sources_num++;
+       }
+
+       return (0);
+} /* }}} int ts_config_add_data_source */
+
 static int ts_destroy (void **user_data) /* {{{ */
 {
-       ts_data_t **data;
+       ts_data_t *data;
 
        if (user_data == NULL)
                return (-EINVAL);
 
-       data = (ts_data_t **) user_data;
+       data = (ts_data_t *) *user_data;
 
-       free (*data);
-       *data = NULL;
+       if ((data != NULL) && (data->data_sources != NULL))
+       {
+               size_t i;
+               for (i = 0; i < data->data_sources_num; i++)
+                       sfree (data->data_sources[i]);
+               sfree (data->data_sources);
+       }
+
+       sfree (data);
+       *user_data = NULL;
 
        return (0);
 } /* }}} int ts_destroy */
@@ -341,6 +413,8 @@ static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
                                status = ts_config_set_double (&data->factor, child);
                else if (strcasecmp ("Offset", child->key) == 0)
                                status = ts_config_set_double (&data->offset, child);
+               else if (strcasecmp ("DataSource", child->key) == 0)
+                               status = ts_config_add_data_source(data, child);
                else
                {
                        ERROR ("Target `scale': The `%s' configuration option is not understood "
@@ -393,6 +467,18 @@ static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
 
        for (i = 0; i < ds->ds_num; i++)
        {
+               /* If we've got a list of data sources, is it in the list? */
+               if (data->data_sources) {
+                       size_t j;
+                       for (j = 0; j < data->data_sources_num; j++)
+                               if (strcasecmp(ds->ds[i].name, data->data_sources[j]) == 0)
+                                       break;
+
+                       /* No match, ignore */
+                       if (j >= data->data_sources_num)
+                               continue;
+               }
+
                if (ds->ds[i].type == DS_TYPE_COUNTER)
                        ts_invoke_counter (ds, vl, data, i);
                else if (ds->ds[i].type == DS_TYPE_GAUGE)
index 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;
     }
 
diff --git a/src/target_v5upgrade.c b/src/target_v5upgrade.c
new file mode 100644 (file)
index 0000000..25f4637
--- /dev/null
@@ -0,0 +1,472 @@
+/**
+ * collectd - src/target_set.c
+ * Copyright (C) 2008-2010  Florian Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; only version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Authors:
+ *   Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "filter_chain.h"
+
+static void v5_swap_instances (value_list_t *vl) /* {{{ */
+{
+  char tmp[DATA_MAX_NAME_LEN];
+
+  assert (sizeof (tmp) == sizeof (vl->plugin_instance));
+  assert (sizeof (tmp) == sizeof (vl->type_instance));
+
+  memcpy (tmp, vl->plugin_instance, sizeof (tmp));
+  memcpy (vl->plugin_instance, vl->type_instance, sizeof (tmp));
+  memcpy (vl->type_instance, tmp, sizeof (tmp));
+} /* }}} void v5_swap_instances */
+
+/*
+ * Df type
+ *
+ * By default, the "df" plugin of version 4.* uses the "df" type and puts the
+ * mount point in the type instance. Detect this behavior and convert the type
+ * to "df_complex". This can be selected in versions 4.9 and 4.10 by setting
+ * the "ReportReserved" option of the "df" plugin.
+ */
+static int v5_df (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  value_list_t new_vl;
+  value_t new_value;
+
+  /* Can't upgrade if both instances have been set. */
+  if ((vl->plugin_instance[0] != 0)
+      && (vl->type_instance[0] != 0))
+    return (FC_TARGET_CONTINUE);
+
+  /* Copy everything: Time, interval, host, ... */
+  memcpy (&new_vl, vl, sizeof (new_vl));
+
+  /* Reset data we can't simply copy */
+  new_vl.values = &new_value;
+  new_vl.values_len = 1;
+  new_vl.meta = NULL;
+
+  /* Move the mount point name to the plugin instance */
+  if (new_vl.plugin_instance[0] == 0)
+    v5_swap_instances (&new_vl);
+
+  /* Change the type to "df_complex" */
+  sstrncpy (new_vl.type, "df_complex", sizeof (new_vl.type));
+
+  /* Dispatch two new value lists instead of this one */
+  new_vl.values[0].gauge = vl->values[0].gauge;
+  sstrncpy (new_vl.type_instance, "used", sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  new_vl.values[0].gauge = vl->values[1].gauge;
+  sstrncpy (new_vl.type_instance, "free", sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  /* Abort processing */
+  return (FC_TARGET_STOP);
+} /* }}} int v5_df */
+
+/*
+ * Interface plugin
+ *
+ * 4.* stores the interface in the type instance and leaves the plugin
+ * instance empty. If this is the case, put the interface name into the plugin
+ * instance and clear the type instance.
+ */
+static int v5_interface (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  if ((vl->plugin_instance[0] != 0) || (vl->type_instance[0] == 0))
+    return (FC_TARGET_CONTINUE);
+
+  v5_swap_instances (vl);
+  return (FC_TARGET_CONTINUE);
+} /* }}} int v5_interface */
+
+/*
+ * MySQL query cache
+ *
+ * 4.* uses the "mysql_qcache" type which mixes different types of
+ * information. In 5.* this has been broken up.
+ */
+static int v5_mysql_qcache (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  value_list_t new_vl;
+  value_t new_value;
+
+  if (vl->values_len != 5)
+    return (FC_TARGET_STOP);
+
+  /* Copy everything: Time, interval, host, ... */
+  memcpy (&new_vl, vl, sizeof (new_vl));
+
+  /* Reset data we can't simply copy */
+  new_vl.values = &new_value;
+  new_vl.values_len = 1;
+  new_vl.meta = NULL;
+
+  /* Change the type to "cache_result" */
+  sstrncpy (new_vl.type, "cache_result", sizeof (new_vl.type));
+
+  /* Dispatch new value lists instead of this one */
+  new_vl.values[0].derive = (derive_t) vl->values[0].counter;
+  sstrncpy (new_vl.type_instance, "qcache-hits",
+      sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  new_vl.values[0].derive = (derive_t) vl->values[1].counter;
+  sstrncpy (new_vl.type_instance, "qcache-inserts",
+      sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  new_vl.values[0].derive = (derive_t) vl->values[2].counter;
+  sstrncpy (new_vl.type_instance, "qcache-not_cached",
+      sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  new_vl.values[0].derive = (derive_t) vl->values[3].counter;
+  sstrncpy (new_vl.type_instance, "qcache-prunes",
+      sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  /* The last data source is a gauge value, so we have to use a different type
+   * here. */
+  new_vl.values[0].gauge = vl->values[4].gauge;
+  sstrncpy (new_vl.type, "cache_size", sizeof (new_vl.type));
+  sstrncpy (new_vl.type_instance, "qcache",
+      sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  /* Abort processing */
+  return (FC_TARGET_STOP);
+} /* }}} int v5_mysql_qcache */
+
+/*
+ * MySQL thread count
+ *
+ * 4.* uses the "mysql_threads" type which mixes different types of
+ * information. In 5.* this has been broken up.
+ */
+static int v5_mysql_threads (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  value_list_t new_vl;
+  value_t new_value;
+
+  if (vl->values_len != 4)
+    return (FC_TARGET_STOP);
+
+  /* Copy everything: Time, interval, host, ... */
+  memcpy (&new_vl, vl, sizeof (new_vl));
+
+  /* Reset data we can't simply copy */
+  new_vl.values = &new_value;
+  new_vl.values_len = 1;
+  new_vl.meta = NULL;
+
+  /* Change the type to "threads" */
+  sstrncpy (new_vl.type, "threads", sizeof (new_vl.type));
+
+  /* Dispatch new value lists instead of this one */
+  new_vl.values[0].gauge = vl->values[0].gauge;
+  sstrncpy (new_vl.type_instance, "running",
+      sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  new_vl.values[0].gauge = vl->values[1].gauge;
+  sstrncpy (new_vl.type_instance, "connected",
+      sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  new_vl.values[0].gauge = vl->values[2].gauge;
+  sstrncpy (new_vl.type_instance, "cached",
+      sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  /* The last data source is a counter value, so we have to use a different
+   * type here. */
+  new_vl.values[0].derive = (derive_t) vl->values[3].counter;
+  sstrncpy (new_vl.type, "total_threads", sizeof (new_vl.type));
+  sstrncpy (new_vl.type_instance, "created",
+      sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  /* Abort processing */
+  return (FC_TARGET_STOP);
+} /* }}} int v5_mysql_threads */
+
+/*
+ * ZFS ARC hit and miss counters
+ *
+ * 4.* uses the flawed "arc_counts" type. In 5.* this has been replaced by the
+ * more generic "cache_result" type.
+ */
+static int v5_zfs_arc_counts (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  value_list_t new_vl;
+  value_t new_value;
+  _Bool is_hits;
+
+  if (vl->values_len != 4)
+    return (FC_TARGET_STOP);
+
+  if (strcmp ("hits", vl->type_instance) == 0)
+    is_hits = 1;
+  else if (strcmp ("misses", vl->type_instance) == 0)
+    is_hits = 0;
+  else
+    return (FC_TARGET_STOP);
+
+  /* Copy everything: Time, interval, host, ... */
+  memcpy (&new_vl, vl, sizeof (new_vl));
+
+  /* Reset data we can't simply copy */
+  new_vl.values = &new_value;
+  new_vl.values_len = 1;
+  new_vl.meta = NULL;
+
+  /* Change the type to "cache_result" */
+  sstrncpy (new_vl.type, "cache_result", sizeof (new_vl.type));
+
+  /* Dispatch new value lists instead of this one */
+  new_vl.values[0].derive = (derive_t) vl->values[0].counter;
+  ssnprintf (new_vl.type_instance, sizeof (new_vl.type_instance),
+      "demand_data-%s",
+      is_hits ? "hit" : "miss");
+  plugin_dispatch_values (&new_vl);
+
+  new_vl.values[0].derive = (derive_t) vl->values[1].counter;
+  ssnprintf (new_vl.type_instance, sizeof (new_vl.type_instance),
+      "demand_metadata-%s",
+      is_hits ? "hit" : "miss");
+  plugin_dispatch_values (&new_vl);
+
+  new_vl.values[0].derive = (derive_t) vl->values[2].counter;
+  ssnprintf (new_vl.type_instance, sizeof (new_vl.type_instance),
+      "prefetch_data-%s",
+      is_hits ? "hit" : "miss");
+  plugin_dispatch_values (&new_vl);
+
+  new_vl.values[0].derive = (derive_t) vl->values[3].counter;
+  ssnprintf (new_vl.type_instance, sizeof (new_vl.type_instance),
+      "prefetch_metadata-%s",
+      is_hits ? "hit" : "miss");
+  plugin_dispatch_values (&new_vl);
+
+  /* Abort processing */
+  return (FC_TARGET_STOP);
+} /* }}} int v5_zfs_arc_counts */
+
+/*
+ * ZFS ARC L2 bytes
+ *
+ * "arc_l2_bytes" -> "io_octets-L2".
+ */
+static int v5_zfs_arc_l2_bytes (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  value_list_t new_vl;
+  value_t new_values[2];
+
+  if (vl->values_len != 2)
+    return (FC_TARGET_STOP);
+
+  /* Copy everything: Time, interval, host, ... */
+  memcpy (&new_vl, vl, sizeof (new_vl));
+
+  /* Reset data we can't simply copy */
+  new_vl.values = new_values;
+  new_vl.values_len = 2;
+  new_vl.meta = NULL;
+
+  /* Change the type/-instance to "io_octets-L2" */
+  sstrncpy (new_vl.type, "io_octets", sizeof (new_vl.type));
+  sstrncpy (new_vl.type_instance, "L2", sizeof (new_vl.type_instance));
+
+  /* Copy the actual values. */
+  new_vl.values[0].derive = (derive_t) vl->values[0].counter;
+  new_vl.values[1].derive = (derive_t) vl->values[1].counter;
+
+  /* Dispatch new value lists instead of this one */
+  plugin_dispatch_values (&new_vl);
+
+  /* Abort processing */
+  return (FC_TARGET_STOP);
+} /* }}} int v5_zfs_arc_l2_bytes */
+
+/*
+ * ZFS ARC L2 cache size
+ *
+ * 4.* uses a separate type for this. 5.* uses the generic "cache_size" type
+ * instead.
+ */
+static int v5_zfs_arc_l2_size (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  value_list_t new_vl;
+  value_t new_value;
+
+  if (vl->values_len != 1)
+    return (FC_TARGET_STOP);
+
+  /* Copy everything: Time, interval, host, ... */
+  memcpy (&new_vl, vl, sizeof (new_vl));
+
+  /* Reset data we can't simply copy */
+  new_vl.values = &new_value;
+  new_vl.values_len = 1;
+  new_vl.meta = NULL;
+
+  new_vl.values[0].gauge = (gauge_t) vl->values[0].gauge;
+
+  /* Change the type to "cache_size" */
+  sstrncpy (new_vl.type, "cache_size", sizeof (new_vl.type));
+
+  /* Adapt the type instance */
+  sstrncpy (new_vl.type_instance, "L2", sizeof (new_vl.type_instance));
+
+  /* Dispatch new value lists instead of this one */
+  plugin_dispatch_values (&new_vl);
+
+  /* Abort processing */
+  return (FC_TARGET_STOP);
+} /* }}} int v5_zfs_arc_l2_size */
+
+/*
+ * ZFS ARC ratio
+ *
+ * "arc_ratio-L1" -> "cache_ratio-arc"
+ * "arc_ratio-L2" -> "cache_ratio-L2"
+ */
+static int v5_zfs_arc_ratio (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  value_list_t new_vl;
+  value_t new_value;
+
+  if (vl->values_len != 1)
+    return (FC_TARGET_STOP);
+
+  /* Copy everything: Time, interval, host, ... */
+  memcpy (&new_vl, vl, sizeof (new_vl));
+
+  /* Reset data we can't simply copy */
+  new_vl.values = &new_value;
+  new_vl.values_len = 1;
+  new_vl.meta = NULL;
+
+  new_vl.values[0].gauge = (gauge_t) vl->values[0].gauge;
+
+  /* Change the type to "cache_ratio" */
+  sstrncpy (new_vl.type, "cache_ratio", sizeof (new_vl.type));
+
+  /* Adapt the type instance */
+  if (strcmp ("L1", vl->type_instance) == 0)
+    sstrncpy (new_vl.type_instance, "arc", sizeof (new_vl.type_instance));
+
+  /* Dispatch new value lists instead of this one */
+  plugin_dispatch_values (&new_vl);
+
+  /* Abort processing */
+  return (FC_TARGET_STOP);
+} /* }}} int v5_zfs_arc_ratio */
+
+/*
+ * ZFS ARC size
+ *
+ * 4.* uses the "arc_size" type with four data sources. In 5.* this has been
+ * replaces with the "cache_size" type and static data has been removed.
+ */
+static int v5_zfs_arc_size (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  value_list_t new_vl;
+  value_t new_value;
+
+  if (vl->values_len != 4)
+    return (FC_TARGET_STOP);
+
+  /* Copy everything: Time, interval, host, ... */
+  memcpy (&new_vl, vl, sizeof (new_vl));
+
+  /* Reset data we can't simply copy */
+  new_vl.values = &new_value;
+  new_vl.values_len = 1;
+  new_vl.meta = NULL;
+
+  /* Change the type to "cache_size" */
+  sstrncpy (new_vl.type, "cache_size", sizeof (new_vl.type));
+
+  /* Dispatch new value lists instead of this one */
+  new_vl.values[0].derive = (derive_t) vl->values[0].counter;
+  sstrncpy (new_vl.type_instance, "arc", sizeof (new_vl.type_instance));
+  plugin_dispatch_values (&new_vl);
+
+  /* Abort processing */
+  return (FC_TARGET_STOP);
+} /* }}} int v5_zfs_arc_size */
+
+static int v5_destroy (void **user_data) /* {{{ */
+{
+  return (0);
+} /* }}} int v5_destroy */
+
+static int v5_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+  *user_data = NULL;
+  return (0);
+} /* }}} int v5_create */
+
+static int v5_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
+    notification_meta_t __attribute__((unused)) **meta,
+    void __attribute__((unused)) **user_data)
+{
+  if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
+    return (-EINVAL);
+
+  if (strcmp ("df", vl->type) == 0)
+    return (v5_df (ds, vl));
+  else if (strcmp ("interface", vl->plugin) == 0)
+    return (v5_interface (ds, vl));
+  else if (strcmp ("mysql_qcache", vl->type) == 0)
+    return (v5_mysql_qcache (ds, vl));
+  else if (strcmp ("mysql_threads", vl->type) == 0)
+    return (v5_mysql_threads (ds, vl));
+  else if (strcmp ("arc_counts", vl->type) == 0)
+    return (v5_zfs_arc_counts (ds, vl));
+  else if (strcmp ("arc_l2_bytes", vl->type) == 0)
+    return (v5_zfs_arc_l2_bytes (ds, vl));
+  else if (strcmp ("arc_l2_size", vl->type) == 0)
+    return (v5_zfs_arc_l2_size (ds, vl));
+  else if (strcmp ("arc_ratio", vl->type) == 0)
+    return (v5_zfs_arc_ratio (ds, vl));
+  else if (strcmp ("arc_size", vl->type) == 0)
+    return (v5_zfs_arc_size (ds, vl));
+
+  return (FC_TARGET_CONTINUE);
+} /* }}} int v5_invoke */
+
+void module_register (void)
+{
+       target_proc_t tproc;
+
+       memset (&tproc, 0, sizeof (tproc));
+       tproc.create  = v5_create;
+       tproc.destroy = v5_destroy;
+       tproc.invoke  = v5_invoke;
+       fc_register_target ("v5upgrade", tproc);
+} /* module_register */
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
+
index 78c337b..6351c7b 100644 (file)
 #undef HAVE_SYSCTLBYNAME /* force HAVE_LIBKVM_NLIST path */
 #endif
 
-#if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME && !HAVE_LIBKVM_NLIST
+#if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME && !HAVE_LIBKVM_NLIST && !KERNEL_AIX
 # error "No applicable input method."
 #endif
 
 #if KERNEL_LINUX
+# include <asm/types.h>
+/* sys/socket.h is necessary to compile when using netlink on older systems. */
+# include <sys/socket.h>
+# include <linux/netlink.h>
+#if HAVE_LINUX_INET_DIAG_H
+# include <linux/inet_diag.h>
+#endif
+# include <sys/socket.h>
+# include <arpa/inet.h>
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_SYSCTLBYNAME
 #  include <bsd/nlist.h>
 # endif
 # include <kvm.h>
-#endif /* HAVE_LIBKVM_NLIST */
+/* #endif HAVE_LIBKVM_NLIST */
+
+#elif KERNEL_AIX
+# include <arpa/inet.h>
+# include <sys/socketvar.h>
+#endif /* KERNEL_AIX */
 
 #if KERNEL_LINUX
+#if HAVE_STRUCT_LINUX_INET_DIAG_REQ
+struct nlreq {
+  struct nlmsghdr nlh;
+  struct inet_diag_req r;
+};
+#endif
+
 static const char *tcp_state[] =
 {
   "", /* 0 */
@@ -190,7 +211,49 @@ struct inpcbtable *inpcbtable_ptr = NULL;
 # define TCP_STATE_LISTEN 1
 # define TCP_STATE_MIN 1
 # define TCP_STATE_MAX 10
-#endif /* HAVE_LIBKVM_NLIST */
+/* #endif HAVE_LIBKVM_NLIST */
+
+#elif KERNEL_AIX
+static const char *tcp_state[] =
+{
+  "CLOSED",
+  "LISTEN",
+  "SYN_SENT",
+  "SYN_RCVD",
+  "ESTABLISHED",
+  "CLOSE_WAIT",
+  "FIN_WAIT_1",
+  "CLOSING",
+  "LAST_ACK",
+  "FIN_WAIT_2",
+  "TIME_WAIT"
+};
+
+# define TCP_STATE_LISTEN 1
+# define TCP_STATE_MIN 0
+# define TCP_STATE_MAX 10
+
+struct netinfo_conn {
+  uint32_t unknow1[2];
+  uint16_t dstport;
+  uint16_t unknow2;
+  struct in6_addr dstaddr;
+  uint16_t srcport;
+  uint16_t unknow3;
+  struct in6_addr srcaddr;
+  uint32_t unknow4[36];
+  uint16_t tcp_state;
+  uint16_t unknow5[7];
+};
+
+struct netinfo_header {
+  unsigned int proto;
+  unsigned int size;
+};
+
+# define NETINFO_TCP 3
+extern int netinfo (int proto, void *data, int *size,  int n);
+#endif /* KERNEL_AIX */
 
 #define PORT_COLLECT_LOCAL  0x01
 #define PORT_COLLECT_REMOTE 0x02
@@ -216,6 +279,22 @@ static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 static int port_collect_listening = 0;
 static port_entry_t *port_list_head = NULL;
 
+#if KERNEL_LINUX
+#if HAVE_STRUCT_LINUX_INET_DIAG_REQ
+/* This depends on linux inet_diag_req because if this structure is missing,
+ * sequence_number is useless and we get a compilation warning.
+ */
+static uint32_t sequence_number = 0;
+#endif
+
+enum
+{
+  SRC_DUNNO,
+  SRC_NETLINK,
+  SRC_PROC
+} linux_source = SRC_DUNNO;
+#endif
+
 static void conn_submit_port_entry (port_entry_t *pe)
 {
   value_t values[1];
@@ -372,6 +451,144 @@ static int conn_handle_ports (uint16_t port_local, uint16_t port_remote, uint8_t
 } /* int conn_handle_ports */
 
 #if KERNEL_LINUX
+/* Returns zero on success, less than zero on socket error and greater than
+ * zero on other errors. */
+static int conn_read_netlink (void)
+{
+#if HAVE_STRUCT_LINUX_INET_DIAG_REQ
+  int fd;
+  struct sockaddr_nl nladdr;
+  struct nlreq req;
+  struct msghdr msg;
+  struct iovec iov;
+  struct inet_diag_msg *r;
+  char buf[8192];
+
+  /* If this fails, it's likely a permission problem. We'll fall back to
+   * reading this information from files below. */
+  fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG);
+  if (fd < 0)
+  {
+    ERROR ("tcpconns plugin: conn_read_netlink: socket(AF_NETLINK, SOCK_RAW, "
+       "NETLINK_INET_DIAG) failed: %s",
+       sstrerror (errno, buf, sizeof (buf)));
+    return (-1);
+  }
+
+  memset(&nladdr, 0, sizeof(nladdr));
+  nladdr.nl_family = AF_NETLINK;
+
+  memset(&req, 0, sizeof(req));
+  req.nlh.nlmsg_len = sizeof(req);
+  req.nlh.nlmsg_type = TCPDIAG_GETSOCK;
+  /* NLM_F_ROOT: return the complete table instead of a single entry.
+   * NLM_F_MATCH: return all entries matching criteria (not implemented)
+   * NLM_F_REQUEST: must be set on all request messages */
+  req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+  req.nlh.nlmsg_pid = 0;
+  /* The sequence_number is used to track our messages. Since netlink is not
+   * reliable, we don't want to end up with a corrupt or incomplete old
+   * message in case the system is/was out of memory. */
+  req.nlh.nlmsg_seq = ++sequence_number;
+  req.r.idiag_family = AF_INET;
+  req.r.idiag_states = 0xfff;
+  req.r.idiag_ext = 0;
+
+  memset(&iov, 0, sizeof(iov));
+  iov.iov_base = &req;
+  iov.iov_len = sizeof(req);
+
+  memset(&msg, 0, sizeof(msg));
+  msg.msg_name = (void*)&nladdr;
+  msg.msg_namelen = sizeof(nladdr);
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+
+  if (sendmsg (fd, &msg, 0) < 0)
+  {
+    ERROR ("tcpconns plugin: conn_read_netlink: sendmsg(2) failed: %s",
+       sstrerror (errno, buf, sizeof (buf)));
+    close (fd);
+    return (-1);
+  }
+
+  iov.iov_base = buf;
+  iov.iov_len = sizeof(buf);
+
+  while (1)
+  {
+    int status;
+    struct nlmsghdr *h;
+
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = (void*)&nladdr;
+    msg.msg_namelen = sizeof(nladdr);
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    status = recvmsg(fd, (void *) &msg, /* flags = */ 0);
+    if (status < 0)
+    {
+      if ((errno == EINTR) || (errno == EAGAIN))
+        continue;
+
+      ERROR ("tcpconns plugin: conn_read_netlink: recvmsg(2) failed: %s",
+         sstrerror (errno, buf, sizeof (buf)));
+      close (fd);
+      return (-1);
+    }
+    else if (status == 0)
+    {
+      close (fd);
+      DEBUG ("tcpconns plugin: conn_read_netlink: Unexpected zero-sized "
+         "reply from netlink socket.");
+      return (0);
+    }
+
+    h = (struct nlmsghdr*)buf;
+    while (NLMSG_OK(h, status))
+    {
+      if (h->nlmsg_seq != sequence_number)
+      {
+       h = NLMSG_NEXT(h, status);
+       continue;
+      }
+
+      if (h->nlmsg_type == NLMSG_DONE)
+      {
+       close (fd);
+       return (0);
+      }
+      else if (h->nlmsg_type == NLMSG_ERROR)
+      {
+       struct nlmsgerr *msg_error;
+
+       msg_error = NLMSG_DATA(h);
+       WARNING ("tcpconns plugin: conn_read_netlink: Received error %i.",
+           msg_error->error);
+
+       close (fd);
+       return (1);
+      }
+
+      r = NLMSG_DATA(h);
+
+      /* This code does not (need to) distinguish between IPv4 and IPv6. */
+      conn_handle_ports (ntohs(r->id.idiag_sport),
+         ntohs(r->id.idiag_dport),
+         r->idiag_state);
+
+      h = NLMSG_NEXT(h, status);
+    } /* while (NLMSG_OK) */
+  } /* while (1) */
+
+  /* Not reached because the while() loop above handles the exit condition. */
+  return (0);
+#else
+  return (1);
+#endif /* HAVE_STRUCT_LINUX_INET_DIAG_REQ */
+} /* int conn_read_netlink */
+
 static int conn_handle_line (char *buffer)
 {
   char *fields[32];
@@ -506,26 +723,55 @@ static int conn_init (void)
 
 static int conn_read (void)
 {
-  int errors_num = 0;
+  int status;
 
   conn_reset_port_entry ();
 
-  if (conn_read_file ("/proc/net/tcp") != 0)
-    errors_num++;
-  if (conn_read_file ("/proc/net/tcp6") != 0)
-    errors_num++;
-
-  if (errors_num < 2)
+  if (linux_source == SRC_NETLINK)
   {
-    conn_submit_all ();
+    status = conn_read_netlink ();
   }
-  else
+  else if (linux_source == SRC_PROC)
   {
-    ERROR ("tcpconns plugin: Neither /proc/net/tcp nor /proc/net/tcp6 "
-       "coult be read.");
-    return (-1);
+    int errors_num = 0;
+
+    if (conn_read_file ("/proc/net/tcp") != 0)
+      errors_num++;
+    if (conn_read_file ("/proc/net/tcp6") != 0)
+      errors_num++;
+
+    if (errors_num < 2)
+      status = 0;
+    else
+      status = ENOENT;
+  }
+  else /* if (linux_source == SRC_DUNNO) */
+  {
+    /* Try to use netlink for getting this data, it is _much_ faster on systems
+     * with a large amount of connections. */
+    status = conn_read_netlink ();
+    if (status == 0)
+    {
+      INFO ("tcpconns plugin: Reading from netlink succeeded. "
+         "Will use the netlink method from now on.");
+      linux_source = SRC_NETLINK;
+    }
+    else
+    {
+      INFO ("tcpconns plugin: Reading from netlink failed. "
+         "Will read from /proc from now on.");
+      linux_source = SRC_PROC;
+
+      /* return success here to avoid the "plugin failed" message. */
+      return (0);
+    }
   }
 
+  if (status == 0)
+    conn_submit_all ();
+  else
+    return (status);
+
   return (0);
 } /* int conn_read */
 /* #endif KERNEL_LINUX */
@@ -710,7 +956,67 @@ static int conn_read (void)
 
   return (0);
 }
-#endif /* HAVE_LIBKVM_NLIST */
+/* #endif HAVE_LIBKVM_NLIST */
+
+#elif KERNEL_AIX
+
+static int conn_read (void)
+{
+  int size;
+  int i;
+  int nconn;
+  void *data;
+  struct netinfo_header *header;
+  struct netinfo_conn *conn;
+
+  conn_reset_port_entry ();
+
+  size = netinfo(NETINFO_TCP, 0, 0, 0);
+  if (size < 0)
+  {
+    ERROR ("tcpconns plugin: netinfo failed return: %i", size);
+    return (-1);
+  }
+
+  if (size == 0)
+    return (0);
+
+  if ((size - sizeof (struct netinfo_header)) % sizeof (struct netinfo_conn))
+  {
+    ERROR ("tcpconns plugin: invalid buffer size");
+    return (-1);
+  }
+
+  data = malloc(size);
+  if (data == NULL)
+  {
+    ERROR ("tcpconns plugin: malloc failed");
+    return (-1);
+  }
+
+  if (netinfo(NETINFO_TCP, data, &size, 0) < 0)
+  {
+    ERROR ("tcpconns plugin: netinfo failed");
+    free(data);
+    return (-1);
+  }
+
+  header = (struct netinfo_header *)data;
+  nconn = header->size;
+  conn = (struct netinfo_conn *)(data + sizeof(struct netinfo_header));
+
+  for (i=0; i < nconn; conn++, i++)
+  {
+    conn_handle_ports (conn->srcport, conn->dstport, conn->tcp_state);
+  }
+
+  free(data);
+
+  conn_submit_all ();
+
+  return (0);
+}
+#endif /* KERNEL_AIX */
 
 void module_register (void)
 {
@@ -722,6 +1028,8 @@ void module_register (void)
        /* no initialization */
 #elif HAVE_LIBKVM_NLIST
        plugin_register_init ("tcpconns", conn_init);
+#elif KERNEL_AIX
+       /* no initialization */
 #endif
        plugin_register_read ("tcpconns", conn_read);
 } /* void module_register */
index 502145d..2552ad3 100644 (file)
@@ -25,8 +25,8 @@
 #include "common.h"
 #include "plugin.h"
 
-#include <arpa/inet.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netdb.h>
@@ -146,7 +146,7 @@ static void tss2_submit_gauge (const char *plugin_instance,
 } /* void tss2_submit_gauge */
 
 static void tss2_submit_io (const char *plugin_instance, const char *type,
-               counter_t rx, counter_t tx)
+               derive_t rx, derive_t tx)
 {
        /*
         * Submits the io rx/tx tuple to the collectd daemon
@@ -154,8 +154,8 @@ static void tss2_submit_io (const char *plugin_instance, const char *type,
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = rx;
-       values[1].counter = tx;
+       values[0].derive = rx;
+       values[1].derive = tx;
 
        vl.values     = values;
        vl.values_len = 2;
@@ -505,10 +505,10 @@ static int tss2_read_vserver (vserver_list_t *vserver)
        gauge_t users = NAN;
        gauge_t channels = NAN;
        gauge_t servers = NAN;
-       counter_t rx_octets = 0;
-       counter_t tx_octets = 0;
-       counter_t rx_packets = 0;
-       counter_t tx_packets = 0;
+       derive_t rx_octets = 0;
+       derive_t tx_octets = 0;
+       derive_t rx_packets = 0;
+       derive_t tx_packets = 0;
        gauge_t packet_loss = NAN;
        int valid = 0;
 
index 8dc00e5..bf519bb 100644 (file)
--- a/src/ted.c
+++ b/src/ted.c
@@ -271,7 +271,6 @@ static void ted_submit (char *type, double value)
 
     values[0].gauge = value;
 
-    vl.time = time (NULL);
     vl.values = values;
     vl.values_len = 1;
     sstrncpy (vl.host, hostname_g, sizeof (vl.host));
index 5d3da89..603f85b 100644 (file)
 # error "This module is for Linux only."
 #endif
 
+static const char *config_keys[] = {
+       "Device",
+       "IgnoreSelected",
+       "ForceUseProcfs"
+};
+
 const char *const dirname_sysfs = "/sys/class/thermal";
 const char *const dirname_procfs = "/proc/acpi/thermal_zone";
 
-static char force_procfs = 0;
+static _Bool force_procfs = 0;
 static ignorelist_t *device_list;
-static value_list_t vl_temp_template = VALUE_LIST_STATIC;
-static value_list_t vl_state_template = VALUE_LIST_STATIC;
 
 enum dev_type {
        TEMP = 0,
@@ -45,19 +49,21 @@ enum dev_type {
 static void thermal_submit (const char *plugin_instance, enum dev_type dt,
                gauge_t value)
 {
-       value_list_t vl = (dt == TEMP) ? vl_temp_template : vl_state_template;
-       value_t vt;
+       value_list_t vl = VALUE_LIST_INIT;
+       value_t v;
 
-       vt.gauge = value;
+       v.gauge = value;
+       vl.values = &v;
 
-       vl.values = &vt;
        vl.values_len = 1;
 
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "thermal", sizeof(vl.plugin));
-       sstrncpy (vl.plugin_instance, plugin_instance,
-                       sizeof(vl.plugin_instance));
-       sstrncpy (vl.type, (dt == TEMP) ? "temperature" : "gauge",
+       if (plugin_instance != NULL)
+               sstrncpy (vl.plugin_instance, plugin_instance,
+                               sizeof (vl.plugin_instance));
+       sstrncpy (vl.type,
+                       (dt == TEMP) ? "temperature" : "gauge",
                        sizeof (vl.type));
 
        plugin_dispatch_values (&vl);
@@ -69,7 +75,7 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
        char filename[256];
        char data[1024];
        int len;
-       int ok = 0;
+       _Bool success = 0;
 
        if (device_list && ignorelist_match (device_list, name))
                return -1;
@@ -90,7 +96,7 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
 
                if (endptr == data + len && errno == 0) {
                        thermal_submit(name, TEMP, temp);
-                       ++ok;
+                       success = 1;
                }
        }
 
@@ -110,11 +116,11 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir,
 
                if (endptr == data + len && errno == 0) {
                        thermal_submit(name, COOLING_DEV, state);
-                       ++ok;
+                       success = 1;
                }
        }
 
-       return ok ? 0 : -1;
+       return (success ? 0 : -1);
 }
 
 static int thermal_procfs_device_read (const char __attribute__((unused)) *dir,
@@ -144,17 +150,17 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir,
                        && (! strncmp(data, str_temp, sizeof(str_temp)-1))) {
                char *endptr = NULL;
                double temp;
-               double celsius, add;
+               double factor, add;
                
                if (data[--len] == 'C') {
                        add = 0;
-                       celsius = 1;
+                       factor = 1.0;
                } else if (data[len] == 'F') {
                        add = -32;
-                       celsius = 5/9;
+                       factor = 5.0/9.0;
                } else if (data[len] == 'K') {
                        add = -273.15;
-                       celsius = 1;
+                       factor = 1.0;
                } else
                        return -1;
 
@@ -167,7 +173,7 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir,
                ++len;
 
                errno = 0;
-               temp = (strtod (data + len, &endptr) + add) * celsius;
+               temp = (strtod (data + len, &endptr) + add) * factor;
 
                if (endptr != data + len && errno == 0) {
                        thermal_submit(name, TEMP, temp);
@@ -178,12 +184,6 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir,
        return -1;
 }
 
-static const char *config_keys[] = {
-       "Device",
-       "IgnoreSelected",
-       "ForceUseProcfs"
-};
-
 static int thermal_config (const char *key, const char *value)
 {
        if (device_list == NULL)
@@ -240,21 +240,6 @@ static int thermal_init (void)
                ret = plugin_register_read ("thermal", thermal_procfs_read);
        }
 
-       if (!ret) {
-               vl_temp_template.values_len = 1;
-               vl_temp_template.interval = interval_g;
-               sstrncpy (vl_temp_template.host, hostname_g,
-                       sizeof(vl_temp_template.host));
-               sstrncpy (vl_temp_template.plugin, "thermal",
-                       sizeof(vl_temp_template.plugin));
-               sstrncpy (vl_temp_template.type_instance, "temperature",
-                       sizeof(vl_temp_template.type_instance));
-
-               vl_state_template = vl_temp_template;
-               sstrncpy (vl_state_template.type_instance, "cooling_state",
-                       sizeof(vl_state_template.type_instance));
-       }
-
        return ret;
 }
 
diff --git a/src/threshold.c b/src/threshold.c
new file mode 100644 (file)
index 0000000..887dbca
--- /dev/null
@@ -0,0 +1,1033 @@
+/**
+ * collectd - src/threshold.c
+ * Copyright (C) 2007-2010  Florian Forster
+ * Copyright (C) 2008-2009  Sebastian Harl
+ * Copyright (C) 2009       Andrés J. Díaz
+ *
+ * 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:
+ *   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 <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
+ *
+ * Adds a threshold configuration to the list of thresholds. The threshold_t
+ * structure is copied and may be destroyed after this call. Returns zero on
+ * success, non-zero otherwise.
+ */
+static int ut_threshold_add (const threshold_t *th)
+{ /* {{{ */
+  char name[6 * DATA_MAX_NAME_LEN];
+  char *name_copy;
+  threshold_t *th_copy;
+  threshold_t *th_ptr;
+  int status = 0;
+
+  if (format_name (name, sizeof (name), th->host,
+       th->plugin, th->plugin_instance,
+       th->type, th->type_instance) != 0)
+  {
+    ERROR ("ut_threshold_add: format_name failed.");
+    return (-1);
+  }
+
+  name_copy = strdup (name);
+  if (name_copy == NULL)
+  {
+    ERROR ("ut_threshold_add: strdup failed.");
+    return (-1);
+  }
+
+  th_copy = (threshold_t *) malloc (sizeof (threshold_t));
+  if (th_copy == NULL)
+  {
+    sfree (name_copy);
+    ERROR ("ut_threshold_add: malloc failed.");
+    return (-1);
+  }
+  memcpy (th_copy, th, sizeof (threshold_t));
+  th_ptr = NULL;
+
+  DEBUG ("ut_threshold_add: Adding entry `%s'", name);
+
+  pthread_mutex_lock (&threshold_lock);
+
+  th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
+      th->type, th->type_instance);
+
+  while ((th_ptr != NULL) && (th_ptr->next != NULL))
+    th_ptr = th_ptr->next;
+
+  if (th_ptr == NULL) /* no such threshold yet */
+  {
+    status = c_avl_insert (threshold_tree, name_copy, th_copy);
+  }
+  else /* th_ptr points to the last threshold in the list */
+  {
+    th_ptr->next = th_copy;
+    /* name_copy isn't needed */
+    sfree (name_copy);
+  }
+
+  pthread_mutex_unlock (&threshold_lock);
+
+  if (status != 0)
+  {
+    ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
+    sfree (name_copy);
+    sfree (th_copy);
+  }
+
+  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
+ * =============
+ * The following approximately two hundred functions are used to handle the
+ * configuration and fill the threshold list.
+ * {{{ */
+static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("threshold values: The `DataSource' option needs exactly one "
+       "string argument.");
+    return (-1);
+  }
+
+  sstrncpy (th->data_source, ci->values[0].value.string,
+      sizeof (th->data_source));
+
+  return (0);
+} /* int ut_config_type_datasource */
+
+static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("threshold values: The `Instance' option needs exactly one "
+       "string argument.");
+    return (-1);
+  }
+
+  sstrncpy (th->type_instance, ci->values[0].value.string,
+      sizeof (th->type_instance));
+
+  return (0);
+} /* int ut_config_type_instance */
+
+static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+  {
+    WARNING ("threshold values: The `%s' option needs exactly one "
+       "number argument.", ci->key);
+    return (-1);
+  }
+
+  if (strcasecmp (ci->key, "WarningMax") == 0)
+    th->warning_max = ci->values[0].value.number;
+  else
+    th->failure_max = ci->values[0].value.number;
+
+  return (0);
+} /* int ut_config_type_max */
+
+static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+  {
+    WARNING ("threshold values: The `%s' option needs exactly one "
+       "number argument.", ci->key);
+    return (-1);
+  }
+
+  if (strcasecmp (ci->key, "WarningMin") == 0)
+    th->warning_min = ci->values[0].value.number;
+  else
+    th->failure_min = ci->values[0].value.number;
+
+  return (0);
+} /* int ut_config_type_min */
+
+static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+  {
+    WARNING ("threshold values: The `%s' option needs exactly one "
+      "number argument.", ci->key);
+    return (-1);
+  }
+
+  th->hits = ci->values[0].value.number;
+
+  return (0);
+} /* int ut_config_type_hits */
+
+static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+  {
+    WARNING ("threshold values: The `%s' option needs exactly one "
+      "number argument.", ci->key);
+    return (-1);
+  }
+
+  th->hysteresis = ci->values[0].value.number;
+
+  return (0);
+} /* int ut_config_type_hysteresis */
+
+static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
+{
+  int i;
+  threshold_t th;
+  int status = 0;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("threshold values: The `Type' block needs exactly one string "
+       "argument.");
+    return (-1);
+  }
+
+  if (ci->children_num < 1)
+  {
+    WARNING ("threshold values: The `Type' block needs at least one option.");
+    return (-1);
+  }
+
+  memcpy (&th, th_orig, sizeof (th));
+  sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
+
+  th.warning_min = NAN;
+  th.warning_max = NAN;
+  th.failure_min = NAN;
+  th.failure_max = NAN;
+  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;
+    status = 0;
+
+    if (strcasecmp ("Instance", option->key) == 0)
+      status = ut_config_type_instance (&th, option);
+    else if (strcasecmp ("DataSource", option->key) == 0)
+      status = ut_config_type_datasource (&th, option);
+    else if ((strcasecmp ("WarningMax", option->key) == 0)
+       || (strcasecmp ("FailureMax", option->key) == 0))
+      status = ut_config_type_max (&th, option);
+    else if ((strcasecmp ("WarningMin", option->key) == 0)
+       || (strcasecmp ("FailureMin", option->key) == 0))
+      status = ut_config_type_min (&th, option);
+    else if (strcasecmp ("Interesting", option->key) == 0)
+      status = cf_util_get_flag (option, &th.flags, UT_FLAG_INTERESTING);
+    else if (strcasecmp ("Invert", option->key) == 0)
+      status = cf_util_get_flag (option, &th.flags, UT_FLAG_INVERT);
+    else if (strcasecmp ("Persist", option->key) == 0)
+      status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST);
+    else if (strcasecmp ("PersistOK", option->key) == 0)
+      status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST_OK);
+    else if (strcasecmp ("Percentage", option->key) == 0)
+      status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERCENTAGE);
+    else if (strcasecmp ("Hits", option->key) == 0)
+      status = ut_config_type_hits (&th, option);
+    else if (strcasecmp ("Hysteresis", option->key) == 0)
+      status = ut_config_type_hysteresis (&th, option);
+    else
+    {
+      WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
+         "block.", option->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  if (status == 0)
+  {
+    status = ut_threshold_add (&th);
+  }
+
+  return (status);
+} /* int ut_config_type */
+
+static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("threshold values: The `Instance' option needs exactly one "
+       "string argument.");
+    return (-1);
+  }
+
+  sstrncpy (th->plugin_instance, ci->values[0].value.string,
+      sizeof (th->plugin_instance));
+
+  return (0);
+} /* int ut_config_plugin_instance */
+
+static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
+{
+  int i;
+  threshold_t th;
+  int status = 0;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("threshold values: The `Plugin' block needs exactly one string "
+       "argument.");
+    return (-1);
+  }
+
+  if (ci->children_num < 1)
+  {
+    WARNING ("threshold values: The `Plugin' block needs at least one nested "
+       "block.");
+    return (-1);
+  }
+
+  memcpy (&th, th_orig, sizeof (th));
+  sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *option = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Type", option->key) == 0)
+      status = ut_config_type (&th, option);
+    else if (strcasecmp ("Instance", option->key) == 0)
+      status = ut_config_plugin_instance (&th, option);
+    else
+    {
+      WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
+         "block.", option->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  return (status);
+} /* int ut_config_plugin */
+
+static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
+{
+  int i;
+  threshold_t th;
+  int status = 0;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("threshold values: The `Host' block needs exactly one string "
+       "argument.");
+    return (-1);
+  }
+
+  if (ci->children_num < 1)
+  {
+    WARNING ("threshold values: The `Host' block needs at least one nested "
+       "block.");
+    return (-1);
+  }
+
+  memcpy (&th, th_orig, sizeof (th));
+  sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *option = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Type", option->key) == 0)
+      status = ut_config_type (&th, option);
+    else if (strcasecmp ("Plugin", option->key) == 0)
+      status = ut_config_plugin (&th, option);
+    else
+    {
+      WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
+         "block.", option->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  return (status);
+} /* int ut_config_host */
+/*
+ * End of the functions used to configure threshold values.
+ */
+/* }}} */
+
+/*
+ * int ut_report_state
+ *
+ * Checks if the `state' differs from the old state and creates a notification
+ * if appropriate.
+ * Does not fail.
+ */
+static int ut_report_state (const data_set_t *ds,
+    const value_list_t *vl,
+    const threshold_t *th,
+    const gauge_t *values,
+    int ds_index,
+    int state)
+{ /* {{{ */
+  int state_old;
+  notification_t n;
+
+  char *buf;
+  size_t bufsize;
+
+  int status;
+
+  /* Check if hits matched */
+  if ( (th->hits != 0) )
+  {
+    int hits = uc_get_hits(ds,vl);
+    /* STATE_OKAY resets hits unless PERSIST_OK flag is set. Hits resets if
+     * threshold is hit. */
+    if ( ( (state == STATE_OKAY) && ((th->flags & UT_FLAG_PERSIST_OK) == 0) ) || (hits > th->hits) )
+    {
+        DEBUG("ut_report_state: reset uc_get_hits = 0");
+        uc_set_hits(ds,vl,0); /* reset hit counter and notify */
+    } else {
+      DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
+      (void) uc_inc_hits(ds,vl,1); /* increase hit counter */
+      return (0);
+    }
+  } /* end check hits */
+
+  state_old = uc_get_state (ds, vl);
+
+  /* If the state didn't change, report if `persistent' is specified. If the
+   * state is `okay', then only report if `persist_ok` flag is set. */
+  if (state == state_old)
+  {
+    if ((th->flags & UT_FLAG_PERSIST) == 0)
+      return (0);
+    else if ( (state == STATE_OKAY) && ((th->flags & UT_FLAG_PERSIST_OK) == 0) )
+      return (0);
+  }
+
+  if (state != state_old)
+    uc_set_state (ds, vl, state);
+
+  NOTIFICATION_INIT_VL (&n, vl);
+
+  buf = n.message;
+  bufsize = sizeof (n.message);
+
+  if (state == STATE_OKAY)
+    n.severity = NOTIF_OKAY;
+  else if (state == STATE_WARNING)
+    n.severity = NOTIF_WARNING;
+  else
+    n.severity = NOTIF_FAILURE;
+
+  n.time = vl->time;
+
+  status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
+      vl->host, vl->plugin);
+  buf += status;
+  bufsize -= status;
+
+  if (vl->plugin_instance[0] != '\0')
+  {
+    status = ssnprintf (buf, bufsize, " (instance %s)",
+       vl->plugin_instance);
+    buf += status;
+    bufsize -= status;
+  }
+
+  status = ssnprintf (buf, bufsize, " type %s", vl->type);
+  buf += status;
+  bufsize -= status;
+
+  if (vl->type_instance[0] != '\0')
+  {
+    status = ssnprintf (buf, bufsize, " (instance %s)",
+       vl->type_instance);
+    buf += status;
+    bufsize -= status;
+  }
+
+  plugin_notification_meta_add_string (&n, "DataSource",
+      ds->ds[ds_index].name);
+  plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
+  plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
+  plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
+  plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
+  plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
+
+  /* Send an okay notification */
+  if (state == STATE_OKAY)
+  {
+    if (state_old == STATE_MISSING)
+      status = ssnprintf (buf, bufsize,
+          ": Value is no longer missing.");
+    else
+      status = ssnprintf (buf, bufsize,
+          ": All data sources are within range again.");
+    buf += status;
+    bufsize -= status;
+  }
+  else
+  {
+    double min;
+    double max;
+
+    min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
+    max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
+
+    if (th->flags & UT_FLAG_INVERT)
+    {
+      if (!isnan (min) && !isnan (max))
+      {
+        status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
+            "%f. That is within the %s region of %f%s and %f%s.",
+            ds->ds[ds_index].name, values[ds_index],
+            (state == STATE_ERROR) ? "failure" : "warning",
+            min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
+            max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
+      }
+      else
+      {
+       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
+           "%f. That is %s the %s threshold of %f%s.",
+           ds->ds[ds_index].name, values[ds_index],
+           isnan (min) ? "below" : "above",
+           (state == STATE_ERROR) ? "failure" : "warning",
+           isnan (min) ? max : min,
+           ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
+      }
+    }
+    else if (th->flags & UT_FLAG_PERCENTAGE)
+    {
+      gauge_t value;
+      gauge_t sum;
+      int i;
+
+      sum = 0.0;
+      for (i = 0; i < vl->values_len; i++)
+      {
+        if (isnan (values[i]))
+          continue;
+
+        sum += values[i];
+      }
+
+      if (sum == 0.0)
+        value = NAN;
+      else
+        value = 100.0 * values[ds_index] / sum;
+
+      status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
+          "%g (%.2f%%). That is %s the %s threshold of %.2f%%.",
+          ds->ds[ds_index].name, values[ds_index], value,
+          (value < min) ? "below" : "above",
+          (state == STATE_ERROR) ? "failure" : "warning",
+          (value < min) ? min : max);
+    }
+    else /* is not inverted */
+    {
+      status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
+         "%f. That is %s the %s threshold of %f.",
+         ds->ds[ds_index].name, values[ds_index],
+         (values[ds_index] < min) ? "below" : "above",
+         (state == STATE_ERROR) ? "failure" : "warning",
+         (values[ds_index] < min) ? min : max);
+    }
+    buf += status;
+    bufsize -= status;
+  }
+
+  plugin_dispatch_notification (&n);
+
+  plugin_notification_meta_free (n.meta);
+  return (0);
+} /* }}} int ut_report_state */
+
+/*
+ * 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 *ret_ds_index)
+{ /* {{{ */
+  int ret = -1;
+  int ds_index = -1;
+  int i;
+  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++)
+  {
+    int status;
+
+    status = ut_check_one_data_source (ds, vl, th, values_copy, i);
+    if (ret < status)
+    {
+      ret = status;
+      ds_index = i;
+    }
+  } /* for (ds->ds_num) */
+
+  if (ret_ds_index != NULL)
+    *ret_ds_index = ds_index;
+
+  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.
+ */
+static int ut_check_threshold (const data_set_t *ds, const value_list_t *vl,
+    __attribute__((unused)) user_data_t *ud)
+{ /* {{{ */
+  threshold_t *th;
+  gauge_t *values;
+  int status;
+
+  int worst_state = -1;
+  threshold_t *worst_th = NULL;
+  int worst_ds_index = -1;
+
+  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)
+  {
+    int ds_index = -1;
+
+    status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
+    if (status < 0)
+    {
+      ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
+      sfree (values);
+      return (-1);
+    }
+
+    if (worst_state < status)
+    {
+      worst_state = status;
+      worst_th = th;
+      worst_ds_index = ds_index;
+    }
+
+    th = th->next;
+  } /* while (th) */
+
+  status = ut_report_state (ds, vl, worst_th, values,
+      worst_ds_index, worst_state);
+  if (status != 0)
+  {
+    ERROR ("ut_check_threshold: ut_report_state failed.");
+    sfree (values);
+    return (-1);
+  }
+
+  sfree (values);
+
+  return (0);
+} /* }}} int ut_check_threshold */
+
+/*
+ * int ut_missing
+ *
+ * This function is called whenever a value goes "missing".
+ */
+static int ut_missing (const value_list_t *vl,
+    __attribute__((unused)) user_data_t *ud)
+{ /* {{{ */
+  threshold_t *th;
+  cdtime_t missing_time;
+  char identifier[6 * DATA_MAX_NAME_LEN];
+  notification_t n;
+  cdtime_t now;
+
+  if (threshold_tree == NULL)
+    return (0);
+
+  th = threshold_search (vl);
+  /* dispatch notifications for "interesting" values only */
+  if ((th == NULL) || ((th->flags & UT_FLAG_INTERESTING) == 0))
+    return (0);
+
+  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);
+
+  return (0);
+} /* }}} int ut_missing */
+
+int ut_config (oconfig_item_t *ci)
+{ /* {{{ */
+  int i;
+  int status = 0;
+
+  threshold_t th;
+
+  if (threshold_tree == NULL)
+  {
+    threshold_tree = c_avl_create ((void *) strcmp);
+    if (threshold_tree == NULL)
+    {
+      ERROR ("ut_config: c_avl_create failed.");
+      return (-1);
+    }
+  }
+
+  memset (&th, '\0', sizeof (th));
+  th.warning_min = NAN;
+  th.warning_max = NAN;
+  th.failure_min = NAN;
+  th.failure_max = NAN;
+
+  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;
+    status = 0;
+
+    if (strcasecmp ("Type", option->key) == 0)
+      status = ut_config_type (&th, option);
+    else if (strcasecmp ("Plugin", option->key) == 0)
+      status = ut_config_plugin (&th, option);
+    else if (strcasecmp ("Host", option->key) == 0)
+      status = ut_config_host (&th, option);
+    else
+    {
+      WARNING ("threshold values: Option `%s' not allowed here.", option->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  if (c_avl_size (threshold_tree) > 0) {
+    plugin_register_missing ("threshold", ut_missing,
+        /* user data = */ NULL);
+    plugin_register_write ("threshold", ut_check_threshold,
+        /* user data = */ NULL);
+  }
+
+  return (status);
+} /* }}} int um_config */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("threshold", ut_config);
+}
+
+/* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */
index ad54240..78de5cf 100644 (file)
-absolute               count:ABSOLUTE:0:U
-apache_bytes           count:COUNTER:0:134217728
-apache_connections     count:GAUGE:0:65535
-apache_idle_workers    count:GAUGE:0:65535
-apache_requests                count:COUNTER:0:134217728
-apache_scoreboard      count:GAUGE:0:65535
-arc_counts             demand_data:COUNTER:0:U, demand_metadata:COUNTER:0:U, prefetch_data:COUNTER:0:U, prefetch_metadata:COUNTER:0:U
-arc_l2_bytes           read:COUNTER:0:U, write:COUNTER:0:U
-arc_l2_size            value:GAUGE:0:U
-arc_ratio              value:GAUGE:0:U
-arc_size               current:GAUGE:0:U, target:GAUGE:0:U, minlimit:GAUGE:0:U, maxlimit:GAUGE:0:U
+absolute               value:ABSOLUTE:0:U
+apache_bytes           value:DERIVE:0:U
+apache_connections     value:GAUGE:0:65535
+apache_idle_workers    value:GAUGE:0:65535
+apache_requests                value:DERIVE:0:U
+apache_scoreboard      value:GAUGE:0:65535
 ath_nodes              value:GAUGE:0:65535
-ath_stat               value:COUNTER:0:4294967295
+ath_stat               value:DERIVE:0:U
 bitrate                        value:GAUGE:0:4294967295
 bytes                  value:GAUGE:0:U
+cache_eviction         value:DERIVE:0:U
+cache_operation                value:DERIVE:0:U
 cache_ratio            value:GAUGE:0:100
-cache_result           value:COUNTER:0:4294967295
-cache_size             value:GAUGE:0:4294967295
+cache_result           value:DERIVE:0:U
+cache_size             value:GAUGE:0:U
 charge                 value:GAUGE:0:U
-compression            uncompressed:COUNTER:0:U, compressed:COUNTER:0:U
 compression_ratio      value:GAUGE:0:2
-connections            value:COUNTER:0:U
-conntrack              entropy:GAUGE:0:4294967295
-contextswitch          contextswitches:DERIVE:0:U
+compression            uncompressed:DERIVE:0:U, compressed:DERIVE:0:U
+connections            value:DERIVE:0:U
+conntrack              value:GAUGE:0:4294967295
+contextswitch          value:DERIVE:0:U
 counter                        value:COUNTER:U:U
 cpufreq                        value:GAUGE:0:U
-cpu                    value:COUNTER:0:4294967295
+cpu                    value:DERIVE:0:U
+current_connections    value:GAUGE:0:U
+current_sessions       value:GAUGE:0:U
 current                        value:GAUGE:U:U
-delay                  seconds:GAUGE:-1000000:1000000
+delay                  value:GAUGE:-1000000:1000000
 derive                 value:DERIVE:0:U
-df                     used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623
 df_complex             value:GAUGE:0:U
 df_inodes              value:GAUGE:0:U
+df                     used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623
 disk_latency           read:GAUGE:0:U, write:GAUGE:0:U
-disk_merged            read:COUNTER:0:4294967295, write:COUNTER:0:4294967295
-disk_octets            read:COUNTER:0:17179869183, write:COUNTER:0:17179869183
-disk_ops               read:COUNTER:0:4294967295, write:COUNTER:0:4294967295
-disk_ops_complex       value:COUNTER:0:4294967296
-disk_time              read:COUNTER:0:1000000, write:COUNTER:0:1000000
-dns_answer             value:COUNTER:0:65535
-dns_notify             value:COUNTER:0:65535
-dns_octets             queries:COUNTER:0:125000000, responses:COUNTER:0:125000000
-dns_opcode             value:COUNTER:0:65535
+disk_merged            read:DERIVE:0:U, write:DERIVE:0:U
+disk_octets            read:DERIVE:0:U, write:DERIVE:0:U
+disk_ops_complex       value:DERIVE:0:U
+disk_ops               read:DERIVE:0:U, write:DERIVE:0:U
+disk_time              read:DERIVE:0:U, write:DERIVE:0:U
+dns_answer             value:DERIVE:0:U
+dns_notify             value:DERIVE:0:U
+dns_octets             queries:DERIVE:0:U, responses:DERIVE:0:U
+dns_opcode             value:DERIVE:0:U
 dns_qtype_cached       value:GAUGE:0:4294967295
-dns_qtype              value:COUNTER:0:65535
-dns_query              value:COUNTER:0:65535
-dns_question           value:COUNTER:0:65535
-dns_rcode              value:COUNTER:0:65535
-dns_reject             value:COUNTER:0:65535
-dns_request            value:COUNTER:0:65535
-dns_resolver           value:COUNTER:0:65535
-dns_response           value:COUNTER:0:65535
-dns_transfer           value:COUNTER:0:65535
-dns_update             value:COUNTER:0:65535
-dns_zops               value:COUNTER:0:65535
+dns_qtype              value:DERIVE:0:U
+dns_query              value:DERIVE:0:U
+dns_question           value:DERIVE:0:U
+dns_rcode              value:DERIVE:0:U
+dns_reject             value:DERIVE:0:U
+dns_request            value:DERIVE:0:U
+dns_resolver           value:DERIVE:0:U
+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
-entropy                        entropy:GAUGE:0:4294967295
+entropy                        value:GAUGE:0:4294967295
 fanspeed               value:GAUGE:0:U
-file_size              bytes:GAUGE:0:U
+file_size              value:GAUGE:0:U
 files                  value:GAUGE:0:U
-frequency              frequency:GAUGE:0:U
-frequency_offset       ppm:GAUGE:-1000000:1000000
-fscache_stat           value:COUNTER:0:4294967295
 fork_rate              value:DERIVE:0:U
+frequency_offset       value:GAUGE:-1000000:1000000
+frequency              value:GAUGE:0:U
+fscache_stat           value:DERIVE:0:U
 gauge                  value:GAUGE:U:U
-http_request_methods   count:COUNTER:0:134217728
-http_requests          count:COUNTER:0:134217728
-http_response_codes    count:COUNTER:0:134217728
+hash_collisions                value:DERIVE:0:U
+http_request_methods   value:DERIVE:0:U
+http_requests          value:DERIVE:0:U
+http_response_codes    value:DERIVE:0:U
 humidity               value:GAUGE:0:100
-if_collisions          value:COUNTER:0:4294967295
-if_dropped             rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
-if_errors              rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
-if_multicast           value:COUNTER:0:4294967295
-if_octets              rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
-if_packets             rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
-if_rx_errors           value:COUNTER:0:4294967295
-if_tx_errors           value:COUNTER:0:4294967295
+if_collisions          value:DERIVE:0:U
+if_dropped             rx:DERIVE:0:U, tx:DERIVE:0:U
+if_errors              rx:DERIVE:0:U, tx:DERIVE:0:U
+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:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
-io_packets             rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
-ipt_bytes              value:COUNTER:0:134217728
-ipt_packets            value:COUNTER:0:134217728
-irq                    value:COUNTER:U:65535
+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
 links                  value:GAUGE:0:U
-load                   shortterm:GAUGE:0:100, midterm:GAUGE:0:100, longterm:GAUGE:0:100
-memcached_command      value:COUNTER:0:U
+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
 memcached_items                value:GAUGE:0:U
-memcached_octets       rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
-memcached_ops          value:COUNTER:0:134217728
+memcached_octets       rx:DERIVE:0:U, tx:DERIVE:0:U
+memcached_ops          value:DERIVE:0:U
 memory                 value:GAUGE:0:281474976710656
 multimeter             value:GAUGE:U:U
-mysql_commands         value:COUNTER:0:U
-mysql_handler          value:COUNTER:0:U
-mysql_locks            value:COUNTER:0:U
-mysql_log_position     value:COUNTER:0:4294967295
-mysql_octets           rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
-mysql_qcache           hits:COUNTER:0:U, inserts:COUNTER:0:U, not_cached:COUNTER:0:U, lowmem_prunes:COUNTER:0:U, queries_in_cache:GAUGE:0:U
-mysql_threads          running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created:COUNTER:0:U
-nfs_procedure          value:COUNTER:0:4294967295
+mutex_operations       value:DERIVE:0:U
+mysql_commands         value:DERIVE:0:U
+mysql_handler          value:DERIVE:0:U
+mysql_locks            value:DERIVE:0:U
+mysql_log_position     value:DERIVE:0:U
+mysql_octets           rx:DERIVE:0:U, tx:DERIVE:0:U
+nfs_procedure          value:DERIVE:0:U
 nginx_connections      value:GAUGE:0:U
-nginx_requests         value:COUNTER:0:134217728
-node_octets            rx:COUNTER:0:U, tx:COUNTER:0:U
+nginx_requests         value:DERIVE:0:U
+node_octets            rx:DERIVE:0:U, tx:DERIVE:0:U
 node_rssi              value:GAUGE:0:255
-node_stat              value:COUNTER:0:4294967295
+node_stat              value:DERIVE:0:U
 node_tx_rate           value:GAUGE:0:127
-operations             value:COUNTER:0:4294967295
-percent                        percent:GAUGE:0:100.1
-pg_blks                        value:COUNTER:0:U
+operations             value:DERIVE:0:U
+percent                        value:GAUGE:0:100.1
+pf_counters            value:DERIVE:0:U
+pf_limits              value:DERIVE:0:U
+pf_source              value:DERIVE:0:U
+pf_states              value:GAUGE:0:U
+pf_state               value:DERIVE:0:U
+pg_blks                        value:DERIVE:0:U
 pg_db_size             value:GAUGE:0:U
-pg_n_tup_c             value:COUNTER:0:U
+pg_n_tup_c             value:DERIVE:0:U
 pg_n_tup_g             value:GAUGE:0:U
 pg_numbackends         value:GAUGE:0:U
-pg_scan                        value:COUNTER:0:U
-pg_xact                        value:COUNTER:0:U
+pg_scan                        value:DERIVE:0:U
+pg_xact                        value:DERIVE:0:U
 ping_droprate          value:GAUGE:0:100
-ping                   ping:GAUGE:0:65535
 ping_stddev            value:GAUGE:0:65535
+ping                   value:GAUGE:0:65535
 players                        value:GAUGE:0:1000000
 power                  value:GAUGE:0:U
-protocol_counter       value:COUNTER:0:U
+protocol_counter       value:DERIVE:0:U
+ps_code                        value:GAUGE:0:9223372036854775807
 ps_count               processes:GAUGE:0:1000000, threads:GAUGE:0:1000000
-ps_cputime             user:COUNTER:0:16000000, syst:COUNTER:0:16000000
-ps_pagefaults          minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807
+ps_cputime             user:DERIVE:0:U, syst:DERIVE:0:U
+ps_data                        value:GAUGE:0:9223372036854775807
 ps_disk_octets         read:DERIVE:0:U, write:DERIVE:0:U
 ps_disk_ops            read:DERIVE:0:U, write:DERIVE:0:U
+ps_pagefaults          minflt:DERIVE:0:U, majflt:DERIVE:0:U
 ps_rss                 value:GAUGE:0:9223372036854775807
-ps_code                        value:GAUGE:0:9223372036854775807
-ps_data                        value:GAUGE:0:9223372036854775807
 ps_stacksize           value:GAUGE:0:9223372036854775807
 ps_state               value:GAUGE:0:65535
 ps_vm                  value:GAUGE:0:9223372036854775807
 queue_length           value:GAUGE:0:U
+records                        value:GAUGE:0:U
+requests               value:GAUGE:0:U
 response_time          value:GAUGE:0:U
-records                        count:GAUGE:0:U
 route_etx              value:GAUGE:0:U
 route_metric           value:GAUGE:0:U
 routes                 value:GAUGE:0:U
-serial_octets          rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
+serial_octets          rx:DERIVE:0:U, tx:DERIVE:0:U
 signal_noise           value:GAUGE:U:0
 signal_power           value:GAUGE:U:0
 signal_quality         value:GAUGE:0:U
 snr                    value:GAUGE:0:U
 spam_check             value:GAUGE:0:U
 spam_score             value:GAUGE:U:U
-swap_io                        value:DERIVE:0:1099511627776
+swap_io                        value:DERIVE:0:U
 swap                   value:GAUGE:0:1099511627776
 tcp_connections                value:GAUGE:0:4294967295
 temperature            value:GAUGE:-273.15:U
 threads                        value:GAUGE:0:U
-time_dispersion                seconds:GAUGE:-1000000:1000000
-timeleft               timeleft:GAUGE:0:3600
-time_offset            seconds:GAUGE:-1000000:1000000
+time_dispersion                value:GAUGE:-1000000:1000000
+timeleft               value:GAUGE:0:U
+time_offset            value:GAUGE:-1000000:1000000
 total_bytes            value:DERIVE:0:U
+total_connections      value:DERIVE:0:U
+total_operations       value:DERIVE:0:U
 total_requests         value:DERIVE:0:U
+total_sessions         value:DERIVE:0:U
+total_threads          value:DERIVE:0:U
 total_time_in_ms       value:DERIVE:0:U
 total_values           value:DERIVE:0:U
 uptime                 value:GAUGE:0:4294967295
-users                  users:GAUGE:0:65535
-virt_cpu_total         ns:COUNTER:0:256000000000
-virt_vcpu              ns:COUNTER:0:1000000000
-vmpage_action          value:COUNTER:0:4294967295
-vmpage_faults          minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807
-vmpage_io              in:COUNTER:0:4294967295, out:COUNTER:0:4294967295
+users                  value:GAUGE:0:65535
+vcpu                   value:GAUGE:0:U
+virt_cpu_total         value:DERIVE:0:U
+virt_vcpu              value:DERIVE:0:U
+vmpage_action          value:DERIVE:0:U
+vmpage_faults          minflt:DERIVE:0:U, majflt:DERIVE:0:U
+vmpage_io              in:DERIVE:0:U, out:DERIVE:0:U
 vmpage_number          value:GAUGE:0:4294967295
+volatile_changes       value:GAUGE:0:U
 voltage_threshold      value:GAUGE:U:U, threshold:GAUGE:U:U
 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)
+#
+arc_counts             demand_data:COUNTER:0:U, demand_metadata:COUNTER:0:U, prefetch_data:COUNTER:0:U, prefetch_metadata:COUNTER:0:U
+arc_l2_bytes           read:COUNTER:0:U, write:COUNTER:0:U
+arc_l2_size            value:GAUGE:0:U
+arc_ratio              value:GAUGE:0:U
+arc_size               current:GAUGE:0:U, target:GAUGE:0:U, minlimit:GAUGE:0:U, maxlimit:GAUGE:0:U
+mysql_qcache           hits:COUNTER:0:U, inserts:COUNTER:0:U, not_cached:COUNTER:0:U, lowmem_prunes:COUNTER:0:U, queries_in_cache:GAUGE:0:U
+mysql_threads          running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created:COUNTER:0:U
index 67f9881..111742b 100644 (file)
@@ -1,3 +1,5 @@
+=encoding UTF-8
+
 =head1 NAME
 
 types.db - Data-set specifications for the system statistics collection daemon
index 4971417..10cb4f2 100644 (file)
@@ -102,6 +102,10 @@ static void parse_line (char *buf)
   if (fields_num < 2)
     return;
 
+  /* Ignore lines which begin with a hash sign. */
+  if (fields[0][0] == '#')
+    return;
+
   ds = (data_set_t *) malloc (sizeof (data_set_t));
   if (ds == NULL)
     return;
index 35c7712..2c1665f 100644 (file)
@@ -26,7 +26,6 @@
 
 #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"
@@ -54,7 +53,8 @@ static const char *config_keys[] =
 {
        "SocketFile",
        "SocketGroup",
-       "SocketPerms"
+       "SocketPerms",
+       "DeleteSocket"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -65,6 +65,7 @@ static int   sock_fd    = -1;
 static char *sock_file  = NULL;
 static char *sock_group = NULL;
 static int   sock_perms = S_IRWXU | S_IRWXG;
+static _Bool delete_socket = 0;
 
 static pthread_t listen_thread = (pthread_t) 0;
 
@@ -89,10 +90,27 @@ static int us_open_socket (void)
        sa.sun_family = AF_UNIX;
        sstrncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
                        sizeof (sa.sun_path));
-       /* unlink (sa.sun_path); */
 
        DEBUG ("unixsock plugin: socket path = %s", sa.sun_path);
 
+       if (delete_socket)
+       {
+               errno = 0;
+               status = unlink (sa.sun_path);
+               if ((status != 0) && (errno != ENOENT))
+               {
+                       char errbuf[1024];
+                       WARNING ("unixsock plugin: Deleting socket file \"%s\" failed: %s",
+                                       sa.sun_path,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               }
+               else if (status == 0)
+               {
+                       INFO ("unixsock plugin: Successfully deleted socket file \"%s\".",
+                                       sa.sun_path);
+               }
+       }
+
        status = bind (sock_fd, (struct sockaddr *) &sa, sizeof (sa));
        if (status != 0)
        {
@@ -263,10 +281,6 @@ 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);
@@ -349,7 +363,8 @@ static void *us_server_thread (void __attribute__((unused)) *arg)
 
                DEBUG ("Spawning child to handle connection on fd #%i", *remote_fd);
 
-               status = pthread_create (&th, &th_attr, us_handle_client, (void *) remote_fd);
+               status = plugin_thread_create (&th, &th_attr,
+                               us_handle_client, (void *) remote_fd);
                if (status != 0)
                {
                        char errbuf[1024];
@@ -401,6 +416,13 @@ static int us_config (const char *key, const char *val)
        {
                sock_perms = (int) strtol (val, NULL, 8);
        }
+       else if (strcasecmp (key, "DeleteSocket") == 0)
+       {
+               if (IS_TRUE (val))
+                       delete_socket = 1;
+               else
+                       delete_socket = 0;
+       }
        else
        {
                return (-1);
@@ -422,7 +444,8 @@ static int us_init (void)
 
        loop = 1;
 
-       status = pthread_create (&listen_thread, NULL, us_server_thread, NULL);
+       status = plugin_thread_create (&listen_thread, NULL,
+                       us_server_thread, NULL);
        if (status != 0)
        {
                char errbuf[1024];
index d2ba963..064c3ce 100644 (file)
 /* Using sysctl interface to retrieve the boot time on *BSD / Darwin / OS X systems */
 /* #endif HAVE_SYS_SYSCTL_H */
 
+#elif HAVE_PERFSTAT
+# include <sys/protosw.h>
+# include <libperfstat.h>
+/* Using perfstat_cpu_total to retrive the boot time in AIX */
+/* #endif HAVE_PERFSTAT */
+
 #else
 # error "No applicable input method."
 #endif
@@ -203,7 +209,29 @@ static int uptime_init (void) /* {{{ */
                                "but `boottime' is zero!");
                return (-1);
        }
-#endif /* HAVE_SYS_SYSCTL_H */
+/* #endif HAVE_SYS_SYSCTL_H */
+
+#elif HAVE_PERFSTAT
+       int status;
+       perfstat_cpu_total_t cputotal;
+       int hertz;
+
+       status = perfstat_cpu_total(NULL, &cputotal,
+               sizeof(perfstat_cpu_total_t), 1);
+       if (status < 0)
+       {
+               char errbuf[1024];
+               ERROR ("uptime plugin: perfstat_cpu_total: %s",
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       hertz = sysconf(_SC_CLK_TCK);
+       if (hertz <= 0)
+               hertz = HZ;
+
+       boottime = time(NULL) - cputotal.lbolt / hertz;
+#endif /* HAVE_PERFSTAT */
 
        return (0);
 } /* }}} int uptime_init */
index 12310d3..f71b1fd 100644 (file)
@@ -51,6 +51,7 @@ struct c_avl_tree_s
 {
        c_avl_node_t *root;
        int (*compare) (const void *, const void *);
+       int size;
 };
 
 struct c_avl_iterator_s
@@ -479,6 +480,7 @@ c_avl_tree_t *c_avl_create (int (*compare) (const void *, const void *))
 
        t->root = NULL;
        t->compare = compare;
+       t->size = 0;
 
        return (t);
 }
@@ -510,6 +512,7 @@ int c_avl_insert (c_avl_tree_t *t, void *key, void *value)
        {
                new->parent = NULL;
                t->root = new;
+               t->size = 1;
                return (0);
        }
 
@@ -555,6 +558,7 @@ int c_avl_insert (c_avl_tree_t *t, void *key, void *value)
        } /* while (42) */
 
        verify_tree (t->root);
+       ++t->size;
        return (0);
 } /* int c_avl_insert */
 
@@ -576,6 +580,7 @@ int c_avl_remove (c_avl_tree_t *t, const void *key, void **rkey, void **rvalue)
 
        status = _remove (t, n);
        verify_tree (t->root);
+       --t->size;
        return (status);
 } /* void *c_avl_remove */
 
@@ -712,3 +717,10 @@ void c_avl_iterator_destroy (c_avl_iterator_t *iter)
 {
        free (iter);
 }
+
+int c_avl_size (c_avl_tree_t *t)
+{
+       if (t == NULL)
+               return (0);
+       return (t->size);
+}
index 355ced2..10fb5cb 100644 (file)
@@ -148,4 +148,19 @@ int c_avl_iterator_next (c_avl_iterator_t *iter, void **key, void **value);
 int c_avl_iterator_prev (c_avl_iterator_t *iter, void **key, void **value);
 void c_avl_iterator_destroy (c_avl_iterator_t *iter);
 
+/*
+ * NAME
+ *   c_avl_size
+ *
+ * DESCRIPTION
+ *   Return the size (number of nodes) of the specified tree.
+ *
+ * PARAMETERS
+ *   `t'        AVL-tree to get the size of.
+ *
+ * RETURN VALUE
+ *   Number of nodes in the tree, 0 if the tree is empty or NULL.
+ */
+int c_avl_size (c_avl_tree_t *t);
+
 #endif /* UTILS_AVLTREE_H */
index aeb662d..fa6e660 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/utils_cache.c
- * Copyright (C) 2007,2008  Florian octo Forster
+ * Copyright (C) 2007-2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -16,7 +16,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Author:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
@@ -24,7 +24,6 @@
 #include "plugin.h"
 #include "utils_avltree.h"
 #include "utils_cache.h"
-#include "utils_threshold.h"
 #include "meta_data.h"
 
 #include <assert.h>
@@ -38,13 +37,13 @@ typedef struct cache_entry_s
        value_t   *values_raw;
        /* Time contained in the package
         * (for calculating rates) */
-       time_t last_time;
+       cdtime_t last_time;
        /* Time according to the local clock
         * (for purging old entries) */
-       time_t last_update;
+       cdtime_t last_update;
        /* Interval in which the data is collected
         * (for purding old entries) */
-       int interval;
+       cdtime_t interval;
        int state;
        int hits;
 
@@ -120,80 +119,6 @@ static void cache_free (cache_entry_t *ce)
   sfree (ce);
 } /* void cache_free */
 
-static int uc_send_notification (const char *name)
-{
-  cache_entry_t *ce = NULL;
-  int status;
-
-  char *name_copy;
-  char *host;
-  char *plugin;
-  char *plugin_instance;
-  char *type;
-  char *type_instance;
-
-  notification_t n;
-
-  name_copy = strdup (name);
-  if (name_copy == NULL)
-  {
-    ERROR ("uc_send_notification: strdup failed.");
-    return (-1);
-  }
-
-  status = parse_identifier (name_copy, &host,
-      &plugin, &plugin_instance,
-      &type, &type_instance);
-  if (status != 0)
-  {
-    ERROR ("uc_send_notification: Cannot parse name `%s'", name);
-    return (-1);
-  }
-
-  /* Copy the associative members */
-  notification_init (&n, NOTIF_FAILURE, /* host = */ NULL,
-      host, plugin, plugin_instance, type, type_instance);
-
-  sfree (name_copy);
-  name_copy = host = plugin = plugin_instance = type = type_instance = NULL;
-
-  pthread_mutex_lock (&cache_lock);
-
-  /*
-   * Set the time _after_ getting the lock because we don't know how long
-   * acquiring the lock takes and we will use this time later to decide
-   * whether or not the state is OKAY.
-   */
-  n.time = time (NULL);
-
-  status = c_avl_get (cache_tree, name, (void *) &ce);
-  if (status != 0)
-  {
-    pthread_mutex_unlock (&cache_lock);
-    sfree (name_copy);
-    return (-1);
-  }
-    
-  /* Check if the entry has been updated in the meantime */
-  if ((n.time - ce->last_update) < (timeout_g * ce->interval))
-  {
-    ce->state = STATE_OKAY;
-    pthread_mutex_unlock (&cache_lock);
-    sfree (name_copy);
-    return (-1);
-  }
-
-  ssnprintf (n.message, sizeof (n.message),
-      "%s has not been updated for %i seconds.", name,
-      (int) (n.time - ce->last_update));
-
-  pthread_mutex_unlock (&cache_lock);
-
-  plugin_dispatch_notification (&n);
-
-  return (0);
-} /* int uc_send_notification */
-
 static void uc_check_range (const data_set_t *ds, cache_entry_t *ce)
 {
   int i;
@@ -258,7 +183,7 @@ static int uc_insert (const data_set_t *ds, const value_list_t *vl,
        ce->values_gauge[i] = NAN;
        if (vl->interval > 0)
          ce->values_gauge[i] = ((double) vl->values[i].absolute)
-           / ((double) vl->interval);
+           / CDTIME_T_TO_DOUBLE (vl->interval);
        ce->values_raw[i].absolute = vl->values[i].absolute;
        break;
        
@@ -274,7 +199,7 @@ static int uc_insert (const data_set_t *ds, const value_list_t *vl,
   uc_check_range (ds, ce);
 
   ce->last_time = vl->time;
-  ce->last_update = time (NULL);
+  ce->last_update = cdtime ();
   ce->interval = vl->interval;
   ce->state = STATE_OKAY;
 
@@ -300,145 +225,133 @@ int uc_init (void)
 
 int uc_check_timeout (void)
 {
-  time_t now;
+  cdtime_t now;
   cache_entry_t *ce;
 
   char **keys = NULL;
+  cdtime_t *keys_time = NULL;
+  cdtime_t *keys_interval = NULL;
   int keys_len = 0;
 
   char *key;
   c_avl_iterator_t *iter;
+
+  int status;
   int i;
   
   pthread_mutex_lock (&cache_lock);
 
-  now = time (NULL);
+  now = cdtime ();
 
   /* Build a list of entries to be flushed */
   iter = c_avl_get_iterator (cache_tree);
   while (c_avl_iterator_next (iter, (void *) &key, (void *) &ce) == 0)
   {
-    /* If entry has not been updated, add to `keys' array */
-    if ((now - ce->last_update) >= (timeout_g * ce->interval))
-    {
-      char **tmp;
+    char **tmp;
+    cdtime_t *tmp_time;
 
-      tmp = (char **) realloc ((void *) keys,
-         (keys_len + 1) * sizeof (char *));
-      if (tmp == NULL)
-      {
-       ERROR ("uc_check_timeout: realloc failed.");
-       c_avl_iterator_destroy (iter);
-       sfree (keys);
-       pthread_mutex_unlock (&cache_lock);
-       return (-1);
-      }
-
-      keys = tmp;
-      keys[keys_len] = strdup (key);
-      if (keys[keys_len] == NULL)
-      {
-       ERROR ("uc_check_timeout: strdup failed.");
-       continue;
-      }
-      keys_len++;
-    }
-  } /* while (c_avl_iterator_next) */
-
-  ce = NULL;
-
-  for (i = 0; i < keys_len; i++)
-  {
-    int status;
-
-    status = ut_check_interesting (keys[i]);
+    /* If the entry is fresh enough, continue. */
+    if ((now - ce->last_update) < (ce->interval * timeout_g))
+      continue;
 
-    if (status < 0)
+    /* If entry has not been updated, add to `keys' array */
+    tmp = (char **) realloc ((void *) keys,
+       (keys_len + 1) * sizeof (char *));
+    if (tmp == NULL)
     {
-      ERROR ("uc_check_timeout: ut_check_interesting failed.");
-      sfree (keys[i]);
+      ERROR ("uc_check_timeout: realloc failed.");
       continue;
     }
-    else if (status == 0) /* ``service'' is uninteresting */
+    keys = tmp;
+
+    tmp_time = realloc (keys_time, (keys_len + 1) * sizeof (*keys_time));
+    if (tmp_time == NULL)
     {
-      DEBUG ("uc_check_timeout: %s is missing but ``uninteresting''",
-         keys[i]);
-      ce = NULL;
-      status = c_avl_remove (cache_tree, keys[i],
-         (void *) &key, (void *) &ce);
-      if (status != 0)
-      {
-       ERROR ("uc_check_timeout: c_avl_remove (%s) failed.", keys[i]);
-      }
-      sfree (keys[i]);
-      sfree (key);
-      if (ce != NULL)
-        cache_free (ce);
+      ERROR ("uc_check_timeout: realloc failed.");
       continue;
     }
+    keys_time = tmp_time;
 
-    /* If we get here, the value is ``interesting''. Query the record from the
-     * cache and update the state field. */
-    if (c_avl_get (cache_tree, keys[i], (void *) &ce) != 0)
+    tmp_time = realloc (keys_interval, (keys_len + 1) * sizeof (*keys_interval));
+    if (tmp_time == NULL)
     {
-      ERROR ("uc_check_timeout: cannot get data for %s from cache", keys[i]);
-      /* Do not free `keys[i]' so a notification is sent further down. */
+      ERROR ("uc_check_timeout: realloc failed.");
       continue;
     }
-    assert (ce != NULL);
+    keys_interval = tmp_time;
 
-    if (status == 2) /* persist */
-    {
-      DEBUG ("uc_check_timeout: %s is missing, sending notification.",
-         keys[i]);
-      ce->state = STATE_MISSING;
-      /* Do not free `keys[i]' so a notification is sent further down. */
-    }
-    else if (status == 1) /* do not persist */
+    keys[keys_len] = strdup (key);
+    if (keys[keys_len] == NULL)
     {
-      if (ce->state == STATE_MISSING)
-      {
-       DEBUG ("uc_check_timeout: %s is missing but "
-           "notification has already been sent.",
-           keys[i]);
-       /* Set `keys[i]' to NULL to no notification is sent. */
-       sfree (keys[i]);
-      }
-      else /* (ce->state != STATE_MISSING) */
-      {
-       DEBUG ("uc_check_timeout: %s is missing, sending one notification.",
-           keys[i]);
-       ce->state = STATE_MISSING;
-       /* Do not free `keys[i]' so a notification is sent further down. */
-      }
-    }
-    else
-    {
-      WARNING ("uc_check_timeout: ut_check_interesting (%s) returned "
-         "invalid status %i.",
-         keys[i], status);
-      sfree (keys[i]);
+      ERROR ("uc_check_timeout: strdup failed.");
+      continue;
     }
+    keys_time[keys_len] = ce->last_time;
+    keys_interval[keys_len] = ce->interval;
 
-    /* Make really sure the next iteration doesn't work with this pointer.
-     * There have been too many bugs in the past.. :/  -- octo */
-    ce = NULL;
-  } /* for (keys[i]) */
+    keys_len++;
+  } /* while (c_avl_iterator_next) */
 
   c_avl_iterator_destroy (iter);
-
   pthread_mutex_unlock (&cache_lock);
 
+  if (keys_len == 0)
+    return (0);
+
+  /* Call the "missing" callback for each value. Do this before removing the
+   * value from the cache, so that callbacks can still access the data stored,
+   * including plugin specific meta data, rates, history, …. This must be done
+   * without holding the lock, otherwise we will run into a deadlock if a
+   * plugin calls the cache interface. */
   for (i = 0; i < keys_len; i++)
   {
-    if (keys[i] == NULL)
+    value_list_t vl = VALUE_LIST_INIT;
+
+    vl.values = NULL;
+    vl.values_len = 0;
+    vl.meta = NULL;
+
+    status = parse_identifier_vl (keys[i], &vl);
+    if (status != 0)
+    {
+      ERROR ("uc_check_timeout: parse_identifier_vl (\"%s\") failed.", keys[i]);
+      cache_free (ce);
       continue;
+    }
+
+    vl.time = keys_time[i];
+    vl.interval = keys_interval[i];
+
+    plugin_dispatch_missing (&vl);
+  } /* for (i = 0; i < keys_len; i++) */
+
+  /* Now actually remove all the values from the cache. We don't re-evaluate
+   * the timestamp again, so in theory it is possible we remove a value after
+   * it is updated here. */
+  pthread_mutex_lock (&cache_lock);
+  for (i = 0; i < keys_len; i++)
+  {
+    key = NULL;
+    ce = NULL;
+
+    status = c_avl_remove (cache_tree, keys[i],
+       (void *) &key, (void *) &ce);
+    if (status != 0)
+    {
+      ERROR ("uc_check_timeout: c_avl_remove (\"%s\") failed.", keys[i]);
+      sfree (keys[i]);
+      continue;
+    }
 
-    uc_send_notification (keys[i]);
     sfree (keys[i]);
-  }
+    sfree (key);
+    cache_free (ce);
+  } /* for (i = 0; i < keys_len; i++) */
+  pthread_mutex_unlock (&cache_lock);
 
   sfree (keys);
+  sfree (keys_time);
+  sfree (keys_interval);
 
   return (0);
 } /* int uc_check_timeout */
@@ -447,9 +360,6 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
 {
   char name[6 * DATA_MAX_NAME_LEN];
   cache_entry_t *ce = NULL;
-  int send_okay_notification = 0;
-  time_t update_delay = 0;
-  notification_t n;
   int status;
   int i;
 
@@ -475,21 +385,14 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
   if (ce->last_time >= vl->time)
   {
     pthread_mutex_unlock (&cache_lock);
-    NOTICE ("uc_update: Value too old: name = %s; value time = %u; "
-       "last cache update = %u;",
-       name, (unsigned int) vl->time, (unsigned int) ce->last_time);
+    NOTICE ("uc_update: Value too old: name = %s; value time = %.3f; "
+       "last cache update = %.3f;",
+       name,
+       CDTIME_T_TO_DOUBLE (vl->time),
+       CDTIME_T_TO_DOUBLE (ce->last_time));
     return (-1);
   }
 
-  /* Send a notification (after the lock has been released) if we switch the
-   * state from something else to `okay'. */
-  if (ce->state == STATE_MISSING)
-  {
-    send_okay_notification = 1;
-    ce->state = STATE_OKAY;
-    update_delay = time (NULL) - ce->last_update;
-  }
-
   for (i = 0; i < ds->ds_num; i++)
   {
     switch (ds->ds[i].type)
@@ -514,7 +417,7 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
          }
 
          ce->values_gauge[i] = ((double) diff)
-           / ((double) (vl->time - ce->last_time));
+           / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time));
          ce->values_raw[i].counter = vl->values[i].counter;
        }
        break;
@@ -531,14 +434,14 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
          diff = vl->values[i].derive - ce->values_raw[i].derive;
 
          ce->values_gauge[i] = ((double) diff)
-           / ((double) (vl->time - ce->last_time));
+           / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time));
          ce->values_raw[i].derive = vl->values[i].derive;
        }
        break;
 
       case DS_TYPE_ABSOLUTE:
        ce->values_gauge[i] = ((double) vl->values[i].absolute)
-         / ((double) (vl->time - ce->last_time));
+         / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time));
        ce->values_raw[i].absolute = vl->values[i].absolute;
        break;
 
@@ -571,33 +474,11 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
   uc_check_range (ds, ce);
 
   ce->last_time = vl->time;
-  ce->last_update = time (NULL);
+  ce->last_update = cdtime ();
   ce->interval = vl->interval;
 
   pthread_mutex_unlock (&cache_lock);
 
-  if (send_okay_notification == 0)
-    return (0);
-
-  /* Do not send okay notifications for uninteresting values, i. e. values for
-   * which no threshold is configured. */
-  status = ut_check_interesting (name);
-  if (status <= 0)
-    return (0);
-
-  /* Initialize the notification */
-  memset (&n, '\0', sizeof (n));
-  NOTIFICATION_INIT_VL (&n, vl, ds);
-
-  n.severity = NOTIF_OKAY;
-  n.time = vl->time;
-
-  ssnprintf (n.message, sizeof (n.message),
-      "Received a value for %s. It was missing for %u seconds.",
-      name, (unsigned int) update_delay);
-
-  plugin_dispatch_notification (&n);
-
   return (0);
 } /* int uc_update */
 
@@ -682,15 +563,16 @@ gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl)
   return (ret);
 } /* gauge_t *uc_get_rate */
 
-int uc_get_names (char ***ret_names, time_t **ret_times, size_t *ret_number)
+int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number)
 {
   c_avl_iterator_t *iter;
   char *key;
   cache_entry_t *value;
 
   char **names = NULL;
-  time_t *times = NULL;
+  cdtime_t *times = NULL;
   size_t number = 0;
+  size_t size_arrays = 0;
 
   int status = 0;
 
@@ -699,42 +581,47 @@ int uc_get_names (char ***ret_names, time_t **ret_times, size_t *ret_number)
 
   pthread_mutex_lock (&cache_lock);
 
+  size_arrays = (size_t) c_avl_size (cache_tree);
+  if (size_arrays < 1)
+  {
+    /* Handle the "no values" case here, to avoid the error message when
+     * calloc() returns NULL. */
+    pthread_mutex_unlock (&cache_lock);
+    return (0);
+  }
+
+  names = calloc (size_arrays, sizeof (*names));
+  times = calloc (size_arrays, sizeof (*times));
+  if ((names == NULL) || (times == NULL))
+  {
+    ERROR ("uc_get_names: calloc failed.");
+    sfree (names);
+    sfree (times);
+    pthread_mutex_unlock (&cache_lock);
+    return (ENOMEM);
+  }
+
   iter = c_avl_get_iterator (cache_tree);
   while (c_avl_iterator_next (iter, (void *) &key, (void *) &value) == 0)
   {
-    char **temp;
-
     /* remove missing values when list values */
     if (value->state == STATE_MISSING)
       continue;
 
-    if (ret_times != NULL)
-    {
-      time_t *tmp_times;
+    /* c_avl_size does not return a number smaller than the number of elements
+     * returned by c_avl_iterator_next. */
+    assert (number < size_arrays);
 
-      tmp_times = (time_t *) realloc (times, sizeof (time_t) * (number + 1));
-      if (tmp_times == NULL)
-      {
-       status = -1;
-       break;
-      }
-      times = tmp_times;
+    if (ret_times != NULL)
       times[number] = value->last_time;
-    }
 
-    temp = (char **) realloc (names, sizeof (char *) * (number + 1));
-    if (temp == NULL)
-    {
-      status = -1;
-      break;
-    }
-    names = temp;
     names[number] = strdup (key);
     if (names[number] == NULL)
     {
       status = -1;
       break;
     }
+
     number++;
   } /* while (c_avl_iterator_next) */
 
index f8059ec..87f93c0 100644 (file)
@@ -35,7 +35,7 @@ int uc_update (const data_set_t *ds, const value_list_t *vl);
 int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_values_num);
 gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl);
 
-int uc_get_names (char ***ret_names, time_t **ret_times, size_t *ret_number);
+int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number);
 
 int uc_get_state (const data_set_t *ds, const value_list_t *vl);
 int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state);
index 0e7b350..3584f3b 100644 (file)
@@ -54,7 +54,7 @@ int handle_flush (FILE *fh, char *buffer)
        int success = 0;
        int error   = 0;
 
-       int timeout = -1;
+       double timeout = 0.0;
        char **plugins = NULL;
        int plugins_num = 0;
        char **identifiers = NULL;
@@ -106,9 +106,9 @@ int handle_flush (FILE *fh, char *buffer)
                        
                        errno = 0;
                        endptr = NULL;
-                       timeout = strtol (opt_value, &endptr, 0);
+                       timeout = strtod (opt_value, &endptr);
 
-                       if ((endptr == opt_value) || (errno != 0))
+                       if ((endptr == opt_value) || (errno != 0) || (!isfinite (timeout)))
                        {
                                print_to_socket (fh, "-1 Invalid value for option `timeout': "
                                                "%s\n", opt_value);
@@ -116,8 +116,10 @@ int handle_flush (FILE *fh, char *buffer)
                                sfree (identifiers);
                                return (-1);
                        }
-                       else if (timeout <= 0)
-                               timeout = -1;
+                       else if (timeout < 0.0)
+                       {
+                               timeout = 0.0;
+                       }
                }
                else
                {
@@ -149,7 +151,9 @@ int handle_flush (FILE *fh, char *buffer)
                        int status;
 
                        identifier = identifiers[j];
-                       status = plugin_flush (plugin, timeout, identifier);
+                       status = plugin_flush (plugin,
+                                       DOUBLE_TO_CDTIME_T (timeout),
+                                       identifier);
                        if (status == 0)
                                success++;
                        else
index 4ca9646..ef66af5 100644 (file)
@@ -50,7 +50,7 @@ int handle_listval (FILE *fh, char *buffer)
 {
   char *command;
   char **names = NULL;
-  time_t *times = NULL;
+  cdtime_t *times = NULL;
   size_t number = 0;
   size_t i;
   int status;
@@ -90,7 +90,8 @@ int handle_listval (FILE *fh, char *buffer)
   print_to_socket (fh, "%i Value%s found\n",
       (int) number, (number == 1) ? "" : "s");
   for (i = 0; i < number; i++)
-    print_to_socket (fh, "%u %s\n", (unsigned int) times[i], names[i]);
+    print_to_socket (fh, "%.3f %s\n", CDTIME_T_TO_DOUBLE (times[i]),
+               names[i]);
 
   free_everything_and_return (0);
 } /* int handle_listval */
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 ec2b5f8..4cbc2f1 100644 (file)
@@ -56,16 +56,16 @@ static int set_option (value_list_t *vl, const char *key, const char *value)
 
        if (strcasecmp ("interval", key) == 0)
        {
-               int tmp;
+               double tmp;
                char *endptr;
 
                endptr = NULL;
                errno = 0;
-               tmp = strtol (value, &endptr, 0);
+               tmp = strtod (value, &endptr);
 
                if ((errno == 0) && (endptr != NULL)
-                               && (endptr != value) && (tmp > 0))
-                       vl->interval = tmp;
+                               && (endptr != value) && (tmp > 0.0))
+                       vl->interval = DOUBLE_TO_CDTIME_T (tmp);
        }
        else
                return (1);
@@ -227,3 +227,31 @@ int handle_putval (FILE *fh, char *buffer)
        return (0);
 } /* int handle_putval */
 
+int create_putval (char *ret, size_t ret_len, /* {{{ */
+       const data_set_t *ds, const value_list_t *vl)
+{
+       char buffer_ident[6 * DATA_MAX_NAME_LEN];
+       char buffer_values[1024];
+       int status;
+
+       status = FORMAT_VL (buffer_ident, sizeof (buffer_ident), vl);
+       if (status != 0)
+               return (status);
+       escape_string (buffer_ident, sizeof (buffer_ident));
+
+       status = format_values (buffer_values, sizeof (buffer_values),
+                       ds, vl, /* store rates = */ 0);
+       if (status != 0)
+               return (status);
+       escape_string (buffer_values, sizeof (buffer_values));
+
+       ssnprintf (ret, ret_len,
+                       "PUTVAL %s interval=%.3f %s",
+                       buffer_ident,
+                       (vl->interval > 0)
+                       ? CDTIME_T_TO_DOUBLE (vl->interval)
+                       : CDTIME_T_TO_DOUBLE (plugin_get_interval ()),
+                       buffer_values);
+
+       return (0);
+} /* }}} int create_putval */
index 8460b13..9c92fd3 100644 (file)
 
 #include <stdio.h>
 
+#include "plugin.h"
+
 int handle_putval (FILE *fh, char *buffer);
 
+int create_putval (char *ret, size_t ret_len,
+               const data_set_t *ds, const value_list_t *vl);
+
 #endif /* UTILS_CMD_PUTVAL_H */
index 9074b18..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;
 
        c->last = now;
 
-       if (c->interval < interval_g)
-               c->interval = interval_g;
+       if (c->interval < plugin_get_interval ())
+               c->interval = plugin_get_interval ();
        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
index 675272c..aadf9c5 100644 (file)
@@ -39,15 +39,6 @@ struct udb_result_s
   char   **values;
   size_t   values_num;
 
-  /* Legacy data */
-  int legacy_mode;
-  size_t legacy_position;
-  /* When in legacy mode:
-   * - type/ds hold the format of the data
-   * - instance_prefix is used as type-instance if non-NULL
-   * - legacy_position holds the index of the column to use as value.
-   */
-
   udb_result_t *next;
 }; /* }}} */
 
@@ -57,8 +48,6 @@ struct udb_query_s /* {{{ */
   char *statement;
   void *user_data;
 
-  int legacy_mode;
-
   unsigned int min_version;
   unsigned int max_version;
 
@@ -84,7 +73,7 @@ struct udb_query_preparation_area_s /* {{{ */
   char *plugin;
   char *db_name;
 
-  int interval;
+  cdtime_t interval;
 
   udb_result_preparation_area_t *result_prep_areas;
 }; /* }}} */
@@ -191,173 +180,6 @@ static int udb_config_set_uint (unsigned int *ret_value, /* {{{ */
 } /* }}} int udb_config_set_uint */
 
 /*
- * Legacy result private functions
- */
-static void udb_legacy_result_finish_result (udb_result_t const *r, /* {{{ */
-    udb_result_preparation_area_t *prep_area)
-{
-  if ((r == NULL) || (prep_area))
-    return;
-
-  assert (r->legacy_mode == 1);
-
-  prep_area->ds = NULL;
-} /* }}} void udb_legacy_result_finish_result */
-
-static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
-    udb_query_preparation_area_t *q_area,
-    udb_result_preparation_area_t *r_area,
-    udb_query_t const *q, char **column_values)
-{
-  value_list_t vl = VALUE_LIST_INIT;
-  value_t value;
-  char *value_str;
-
-  assert (r->legacy_mode == 1);
-  assert (r_area->ds != NULL);
-  assert (r_area->ds->ds_num == 1);
-
-  vl.values = &value;
-  vl.values_len = 1;
-
-  value_str = column_values[r->legacy_position];
-  if (0 != parse_value (value_str, &vl.values[0], r_area->ds->ds[0].type))
-  {
-    ERROR ("db query utils: udb_legacy_result_handle_result: "
-        "Parsing `%s' as %s failed.", value_str,
-        DS_TYPE_TO_STRING (r_area->ds->ds[0].type));
-    errno = EINVAL;
-    return (-1);
-  }
-
-  if (q_area->interval > 0)
-    vl.interval = q_area->interval;
-
-  sstrncpy (vl.host, q_area->host, sizeof (vl.host));
-  sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.plugin_instance));
-  sstrncpy (vl.type, r->type, sizeof (vl.type));
-
-  if (r->instance_prefix != NULL)
-    sstrncpy (vl.type_instance, r->instance_prefix,
-        sizeof (vl.type_instance));
-
-  plugin_dispatch_values (&vl);
-
-  return (0);
-} /* }}} int udb_legacy_result_handle_result */
-
-static int udb_legacy_result_prepare_result (udb_result_t const *r, /* {{{ */
-    udb_result_preparation_area_t *prep_area,
-    char **column_names, size_t column_num)
-{
-  if (r == NULL)
-    return (-EINVAL);
-
-  assert (r->legacy_mode == 1);
-
-  /* Make sure previous preparations are cleaned up. */
-  udb_legacy_result_finish_result (r, prep_area);
-
-  if (r->legacy_position >= column_num)
-  {
-    ERROR ("db query utils: The legacy configuration specified (at least) "
-        "%zu `Column's, but the query returned only %zu columns!",
-        r->legacy_position + 1, column_num);
-    return (-ENOENT);
-  }
-
-  /* Read `ds' and check number of values {{{ */
-  prep_area->ds = plugin_get_ds (r->type);
-  if (prep_area->ds == NULL)
-  {
-    ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
-        "known by the daemon. See types.db(5) for details.",
-        r->type);
-    return (-1);
-  }
-
-  if (prep_area->ds->ds_num != 1)
-  {
-    ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
-        "requires exactly %i values, but the legacy configuration "
-        "requires exactly one!",
-        r->type,
-        prep_area->ds->ds_num);
-    return (-1);
-  }
-  /* }}} */
-
-  return (0);
-} /* }}} int udb_legacy_result_prepare_result */
-
-static int udb_legacy_result_create (const char *query_name, /* {{{ */
-    udb_result_t **r_head, oconfig_item_t *ci, size_t position)
-{
-  udb_result_t *r;
-
-  if ((ci->values_num < 1) || (ci->values_num > 2)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING)
-      || ((ci->values_num == 2)
-        && (ci->values[1].type != OCONFIG_TYPE_STRING)))
-  {
-    WARNING ("db query utils: The `Column' block needs either one or two "
-        "string arguments.");
-    return (-1);
-  }
-
-  r = (udb_result_t *) malloc (sizeof (*r));
-  if (r == NULL)
-  {
-    ERROR ("db query utils: malloc failed.");
-    return (-1);
-  }
-  memset (r, 0, sizeof (*r));
-
-  r->legacy_mode = 1;
-  r->legacy_position = position;
-
-  r->type = strdup (ci->values[0].value.string);
-  if (r->type == NULL)
-  {
-    ERROR ("db query utils: strdup failed.");
-    free (r);
-    return (-1);
-  }
-
-  r->instance_prefix = NULL;
-  if (ci->values_num == 2)
-  {
-    r->instance_prefix = strdup (ci->values[1].value.string);
-    if (r->instance_prefix == NULL)
-    {
-      ERROR ("db query utils: strdup failed.");
-      free (r->type);
-      free (r);
-      return (-1);
-    }
-  }
-
-  /* If all went well, add this result to the list of results. */
-  if (*r_head == NULL)
-  {
-    *r_head = r;
-  }
-  else
-  {
-    udb_result_t *last;
-
-    last = *r_head;
-    while (last->next != NULL)
-      last = last->next;
-
-    last->next = r;
-  }
-
-  return (0);
-} /* }}} int udb_legacy_result_create */
-
-/*
  * Result private functions
  */
 static int udb_result_submit (udb_result_t *r, /* {{{ */
@@ -368,7 +190,6 @@ static int udb_result_submit (udb_result_t *r, /* {{{ */
   size_t i;
 
   assert (r != NULL);
-  assert (r->legacy_mode == 0);
   assert (r_area->ds != NULL);
   assert (((size_t) r_area->ds->ds_num) == r->values_num);
 
@@ -444,14 +265,6 @@ static void udb_result_finish_result (udb_result_t const *r, /* {{{ */
   if ((r == NULL) || (prep_area == NULL))
     return;
 
-  if (r->legacy_mode == 1)
-  {
-    udb_legacy_result_finish_result (r, prep_area);
-    return;
-  }
-
-  assert (r->legacy_mode == 0);
-
   prep_area->ds = NULL;
   sfree (prep_area->instances_pos);
   sfree (prep_area->values_pos);
@@ -468,12 +281,6 @@ static int udb_result_handle_result (udb_result_t *r, /* {{{ */
 
   assert (r && q_area && r_area);
 
-  if (r->legacy_mode == 1)
-    return (udb_legacy_result_handle_result (r, q_area, r_area,
-          q, column_values));
-
-  assert (r->legacy_mode == 0);
-
   for (i = 0; i < r->instances_num; i++)
     r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]];
 
@@ -492,12 +299,6 @@ static int udb_result_prepare_result (udb_result_t const *r, /* {{{ */
   if ((r == NULL) || (prep_area == NULL))
     return (-EINVAL);
 
-  if (r->legacy_mode == 1)
-    return (udb_legacy_result_prepare_result (r, prep_area,
-          column_names, column_num));
-
-  assert (r->legacy_mode == 0);
-
 #define BAIL_OUT(status) \
   prep_area->ds = NULL; \
   sfree (prep_area->instances_pos); \
@@ -759,7 +560,7 @@ void udb_query_free_one (udb_query_t *q) /* {{{ */
  */
 int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */
     size_t *ret_query_list_len, oconfig_item_t *ci,
-    udb_query_create_callback_t cb, int legacy_mode)
+    udb_query_create_callback_t cb)
 {
   udb_query_t **query_list;
   size_t        query_list_len;
@@ -768,8 +569,6 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */
   int status;
   int i;
 
-  size_t legacy_position;
-
   if ((ret_query_list == NULL) || (ret_query_list_len == NULL))
     return (-EINVAL);
   query_list     = *ret_query_list;
@@ -790,12 +589,9 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */
     return (-1);
   }
   memset (q, 0, sizeof (*q));
-  q->legacy_mode = legacy_mode;
   q->min_version = 0;
   q->max_version = UINT_MAX;
 
-  legacy_position = 0;
-
   status = udb_config_set_string (&q->name, ci);
   if (status != 0)
   {
@@ -817,42 +613,6 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */
     else if (strcasecmp ("MaxVersion", child->key) == 0)
       status = udb_config_set_uint (&q->max_version, child);
 
-    /* PostgreSQL compatibility code */
-    else if ((strcasecmp ("Query", child->key) == 0)
-        && (q->legacy_mode == 1))
-    {
-      WARNING ("db query utils: Query `%s': The `Query' option is "
-          "deprecated. Please use `Statement' instead.",
-          q->name);
-      status = udb_config_set_string (&q->statement, child);
-    }
-    else if ((strcasecmp ("Column", child->key) == 0)
-        && (q->legacy_mode == 1))
-    {
-      WARNING ("db query utils: Query `%s': The `Column' option is "
-          "deprecated. Please use the new syntax instead.",
-          q->name);
-      status = udb_legacy_result_create (q->name, &q->results, child,
-          legacy_position);
-      legacy_position++;
-    }
-    else if ((strcasecmp ("MinPGVersion", child->key) == 0)
-        && (q->legacy_mode == 1))
-    {
-      WARNING ("db query utils: Query `%s': The `MinPGVersion' option is "
-          "deprecated. Please use `MinVersion' instead.",
-          q->name);
-      status = udb_config_set_uint (&q->min_version, child);
-    }
-    else if ((strcasecmp ("MaxPGVersion", child->key) == 0)
-        && (q->legacy_mode == 1))
-    {
-      WARNING ("db query utils: Query `%s': The `MaxPGVersion' option is "
-          "deprecated. Please use `MaxVersion' instead.",
-          q->name);
-      status = udb_config_set_uint (&q->max_version, child);
-    }
-
     /* Call custom callbacks */
     else if (cb != NULL)
     {
@@ -1079,7 +839,7 @@ void udb_query_finish_result (udb_query_t const *q, /* {{{ */
   sfree (prep_area->plugin);
   sfree (prep_area->db_name);
 
-  prep_area->interval = -1;
+  prep_area->interval = 0;
 
   for (r = q->results, r_area = prep_area->result_prep_areas;
       r != NULL; r = r->next, r_area = r_area->next)
@@ -1147,7 +907,7 @@ int udb_query_handle_result (udb_query_t const *q, /* {{{ */
 int udb_query_prepare_result (udb_query_t const *q, /* {{{ */
     udb_query_preparation_area_t *prep_area,
     const char *host, const char *plugin, const char *db_name,
-    char **column_names, size_t column_num, int interval)
+    char **column_names, size_t column_num, cdtime_t interval)
 {
   udb_result_preparation_area_t *r_area;
   udb_result_t *r;
index 7856bdb..b6f4cea 100644 (file)
@@ -41,7 +41,7 @@ typedef int (*udb_query_create_callback_t) (udb_query_t *q,
  */
 int udb_query_create (udb_query_t ***ret_query_list,
     size_t *ret_query_list_len, oconfig_item_t *ci,
-    udb_query_create_callback_t cb, int legacy_mode);
+    udb_query_create_callback_t cb);
 void udb_query_free (udb_query_t **query_list, size_t query_list_len);
 
 int udb_query_pick_from_list_by_name (const char *name,
@@ -68,7 +68,7 @@ int udb_query_check_version (udb_query_t *q, unsigned int version);
 int udb_query_prepare_result (udb_query_t const *q,
     udb_query_preparation_area_t *prep_area,
     const char *host, const char *plugin, const char *db_name,
-    char **column_names, size_t column_num, int interval);
+    char **column_names, size_t column_num, cdtime_t interval);
 int udb_query_handle_result (udb_query_t const *q,
     udb_query_preparation_area_t *prep_area, char **column_values);
 void udb_query_finish_result (udb_query_t const *q,
index 2899bd4..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_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
@@ -291,13 +294,18 @@ rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
     off_t no = 0;
     unsigned char c;
     size_t len;
-    assert(ns > 0);
+    static int loop_detect = 0;
+    if (loop_detect > 2)
+       return 4;               /* compression loop */
+    if (ns <= 0)
+       return 4;               /* probably compression loop */
     do {
        if ((*off) >= sz)
            break;
        c = *(buf + (*off));
        if (c > 191) {
            /* blasted compression */
+           int rc;
            unsigned short s;
            off_t ptr;
            memcpy(&s, buf + (*off), sizeof(s));
@@ -305,18 +313,23 @@ rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
            (*off) += sizeof(s);
            /* Sanity check */
            if ((*off) >= sz)
-               return 1;
+               return 1;       /* message too short */
            ptr = s & 0x3FFF;
            /* Make sure the pointer is inside this message */
            if (ptr >= sz)
-               return 2;
-           return rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
+               return 2;       /* bad compression ptr */
+           if (ptr < DNS_MSG_HDR_SZ)
+               return 2;       /* bad compression ptr */
+           loop_detect++;
+           rc = rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
+           loop_detect--;
+           return rc;
        } else if (c > RFC1035_MAXLABELSZ) {
            /*
             * "(The 10 and 01 combinations are reserved for future use.)"
             */
+           return 3;           /* reserved label/compression flags */
            break;
-           return 3;
        } else {
            (*off)++;
            len = (size_t) c;
@@ -324,15 +337,18 @@ rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
                break;
            if (len > (ns - 1))
                len = ns - 1;
-           if ((*off) + len > sz)      /* message is too short */
-               return 4;
+           if ((*off) + len > sz)
+               return 4;       /* message is too short */
+           if (no + len + 1 > ns)
+               return 5;       /* qname would overflow name buffer */
            memcpy(name + no, buf + (*off), len);
            (*off) += len;
            no += len;
            *(name + (no++)) = '.';
        }
     } while (c > 0);
-    *(name + no - 1) = '\0';
+    if (no > 0)
+       *(name + no - 1) = '\0';
     /* make sure we didn't allow someone to overflow the name buffer */
     assert(no <= ns);
     return 0;
@@ -345,10 +361,10 @@ handle_dns(const char *buf, int len)
     uint16_t us;
     off_t offset;
     char *t;
-    int x;
+    int status;
 
     /* The DNS header is 12 bytes long */
-    if (len < 12)
+    if (len < DNS_MSG_HDR_SZ)
        return 0;
 
     memcpy(&us, buf + 0, 2);
@@ -379,11 +395,15 @@ handle_dns(const char *buf, int len)
     memcpy(&us, buf + 10, 2);
     qh.arcount = ntohs(us);
 
-    offset = 12;
+    offset = DNS_MSG_HDR_SZ;
     memset(qh.qname, '\0', MAX_QNAME_SZ);
-    x = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ);
-    if (0 != x)
+    status = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ);
+    if (status != 0)
+    {
+       INFO ("utils_dns: handle_dns: rfc1035NameUnpack failed "
+               "with status %i.", status);
        return 0;
+    }
     if ('\0' == qh.qname[0])
        sstrncpy (qh.qname, ".", sizeof (qh.qname));
     while ((t = strchr(qh.qname, '\n')))
@@ -424,6 +444,7 @@ handle_udp(const struct udphdr *udp, int len)
     return 1;
 }
 
+#if HAVE_NETINET_IP6_H
 static int
 handle_ipv6 (struct ip6_hdr *ipv6, int len)
 {
@@ -492,6 +513,16 @@ handle_ipv6 (struct ip6_hdr *ipv6, int len)
 
     return (1); /* Success */
 } /* int handle_ipv6 */
+/* #endif HAVE_NETINET_IP6_H */
+
+#else /* if !HAVE_NETINET_IP6_H */
+static int
+handle_ipv6 (__attribute__((unused)) void *pkg,
+       __attribute__((unused)) int len)
+{
+    return (0);
+}
+#endif /* !HAVE_NETINET_IP6_H */
 
 static int
 handle_ip(const struct ip *ip, int len)
@@ -502,7 +533,7 @@ handle_ip(const struct ip *ip, int len)
     struct in6_addr c_dst_addr;
 
     if (ip->ip_v == 6)
-       return (handle_ipv6 ((struct ip6_hdr *) ip, len));
+       return (handle_ipv6 ((void *) ip, len));
 
     in6_addr_from_buffer (&c_src_addr, &ip->ip_src.s_addr, sizeof (ip->ip_src.s_addr), AF_INET);
     in6_addr_from_buffer (&c_dst_addr, &ip->ip_dst.s_addr, sizeof (ip->ip_dst.s_addr), AF_INET);
@@ -600,7 +631,7 @@ handle_ether(const u_char * pkt, int len)
        return 0;
     memcpy(buf, pkt, len);
     if (ETHERTYPE_IPV6 == etype)
-       return (handle_ipv6 ((struct ip6_hdr *) buf, len));
+       return (handle_ipv6 ((void *) buf, len));
     else
        return handle_ip((struct ip *) buf, len);
 }
@@ -633,7 +664,7 @@ handle_linux_sll (const u_char *pkt, int len)
        return 0;
 
     if (ETHERTYPE_IPV6 == etype)
-       return (handle_ipv6 ((struct ip6_hdr *) pkt, len));
+       return (handle_ipv6 ((void *) pkt, len));
     else
        return handle_ip((struct ip *) pkt, len);
 }
@@ -644,10 +675,6 @@ void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt
 {
     int status;
 
-    DEBUG ("handle_pcap (udata = %p, hdr = %p, pkt = %p): hdr->caplen = %i\n",
-                   (void *) udata, (void *) hdr, (void *) pkt,
-                   hdr->caplen);
-
     if (hdr->caplen < ETHER_HDR_LEN)
        return;
 
@@ -681,7 +708,7 @@ void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt
            break;
 
        default:
-           ERROR ("handle_pcap: unsupported data link type %d\n",
+           ERROR ("handle_pcap: unsupported data link type %d",
                    pcap_datalink(pcap_obj));
            status = 0;
            break;
index efc7903..56213af 100644 (file)
@@ -42,6 +42,8 @@
 # include <pcap.h>
 #endif
 
+#define DNS_MSG_HDR_SZ 12
+
 #define T_MAX 65536
 #define OP_MAX 16
 #define C_MAX 65536
diff --git a/src/utils_format_graphite.c b/src/utils_format_graphite.c
new file mode 100644 (file)
index 0000000..b7d4494
--- /dev/null
@@ -0,0 +1,259 @@
+/**
+ * collectd - src/utils_format_graphite.c
+ * Copyright (C) 2012  Thomas Meson
+ * Copyright (C) 2012  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Thomas Meson <zllak at hycik.org>
+ *   Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+
+#include "utils_format_graphite.h"
+#include "utils_cache.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 */
+
+static int gr_format_values (char *ret, size_t ret_len,
+        int ds_num, const data_set_t *ds, const value_list_t *vl,
+        gauge_t const *rates)
+{
+    size_t offset = 0;
+    int status;
+
+    assert (0 == strcmp (ds->type, vl->type));
+
+    memset (ret, 0, ret_len);
+
+#define BUFFER_ADD(...) do { \
+    status = ssnprintf (ret + offset, ret_len - offset, \
+            __VA_ARGS__); \
+    if (status < 1) \
+    { \
+        return (-1); \
+    } \
+    else if (((size_t) status) >= (ret_len - offset)) \
+    { \
+        return (-1); \
+    } \
+    else \
+    offset += ((size_t) status); \
+} while (0)
+
+    if (ds->ds[ds_num].type == DS_TYPE_GAUGE)
+        BUFFER_ADD ("%f", vl->values[ds_num].gauge);
+    else if (rates != NULL)
+        BUFFER_ADD ("%f", rates[ds_num]);
+    else if (ds->ds[ds_num].type == DS_TYPE_COUNTER)
+        BUFFER_ADD ("%llu", vl->values[ds_num].counter);
+    else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
+        BUFFER_ADD ("%"PRIi64, vl->values[ds_num].derive);
+    else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
+        BUFFER_ADD ("%"PRIu64, vl->values[ds_num].absolute);
+    else
+    {
+        ERROR ("gr_format_values plugin: Unknown data source type: %i",
+                ds->ds[ds_num].type);
+        return (-1);
+    }
+
+#undef BUFFER_ADD
+
+    return (0);
+}
+
+static void gr_copy_escape_part (char *dst, const char *src, size_t dst_len,
+    char escape_char)
+{
+    size_t i;
+
+    memset (dst, 0, dst_len);
+
+    if (src == NULL)
+        return;
+
+    for (i = 0; i < dst_len; i++)
+    {
+        if (src[i] == 0)
+        {
+            dst[i] = 0;
+            break;
+        }
+
+        if ((src[i] == '.')
+                || isspace ((int) src[i])
+                || iscntrl ((int) src[i]))
+            dst[i] = escape_char;
+        else
+            dst[i] = src[i];
+    }
+}
+
+static int gr_format_name (char *ret, int ret_len,
+        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];
+    char n_plugin_instance[DATA_MAX_NAME_LEN];
+    char n_type[DATA_MAX_NAME_LEN];
+    char n_type_instance[DATA_MAX_NAME_LEN];
+
+    char tmp_plugin[2 * DATA_MAX_NAME_LEN + 1];
+    char tmp_type[2 * DATA_MAX_NAME_LEN + 1];
+
+    if (prefix == NULL)
+        prefix = "";
+
+    if (postfix == NULL)
+        postfix = "";
+
+    gr_copy_escape_part (n_host, vl->host,
+            sizeof (n_host), escape_char);
+    gr_copy_escape_part (n_plugin, vl->plugin,
+            sizeof (n_plugin), escape_char);
+    gr_copy_escape_part (n_plugin_instance, vl->plugin_instance,
+            sizeof (n_plugin_instance), escape_char);
+    gr_copy_escape_part (n_type, vl->type,
+            sizeof (n_type), escape_char);
+    gr_copy_escape_part (n_type_instance, vl->type_instance,
+            sizeof (n_type_instance), escape_char);
+
+    if (n_plugin_instance[0] != '\0')
+        ssnprintf (tmp_plugin, sizeof (tmp_plugin), "%s%c%s",
+            n_plugin,
+            (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+            n_plugin_instance);
+    else
+        sstrncpy (tmp_plugin, n_plugin, sizeof (tmp_plugin));
+
+    if (n_type_instance[0] != '\0')
+        ssnprintf (tmp_type, sizeof (tmp_type), "%s%c%s",
+            n_type,
+            (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);
+    else
+        ssnprintf (ret, ret_len, "%s%s%s.%s.%s",
+            prefix, n_host, postfix, tmp_plugin, tmp_type);
+
+    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,
+    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 (flags & GRAPHITE_STORE_RATES)
+      rates = uc_get_rate (ds, vl);
+
+    for (i = 0; i < ds->ds_num; i++)
+    {
+        char const *ds_name = NULL;
+        char        key[10*DATA_MAX_NAME_LEN];
+        char        values[512];
+        size_t      message_len;
+        char        message[1024];
+
+        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, flags);
+        if (status != 0)
+        {
+            ERROR ("format_graphite: error with gr_format_name");
+            sfree (rates);
+            return (status);
+        }
+
+        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);
+        if (status != 0)
+        {
+            ERROR ("format_graphite: error with gr_format_values");
+            sfree (rates);
+            return (status);
+        }
+
+        /* Compute the graphite command */
+        message_len = (size_t) ssnprintf (message, sizeof (message),
+                "%s %s %u\r\n",
+                key,
+                values,
+                (unsigned int) CDTIME_T_TO_TIME_T (vl->time));
+        if (message_len >= sizeof (message)) {
+            ERROR ("format_graphite: message buffer too small: "
+                    "Need %zu bytes.", message_len + 1);
+            sfree (rates);
+            return (-ENOMEM);
+        }
+
+        /* Append it in case we got multiple data set */
+        if ((buffer_pos + message_len) >= buffer_size)
+        {
+            ERROR ("format_graphite: target buffer too small");
+            sfree (rates);
+            return (-ENOMEM);
+        }
+        memcpy((void *) (buffer + buffer_pos), message, message_len);
+        buffer_pos += message_len;
+    }
+    sfree (rates);
+    return (status);
+} /* int format_graphite */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/utils_format_graphite.h b/src/utils_format_graphite.h
new file mode 100644 (file)
index 0000000..398defb
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * collectd - src/utils_format_graphite.h
+ * Copyright (C) 2012  Thomas Meson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Thomas Meson <zllak at hycik.org>
+ **/
+
+#ifndef UTILS_FORMAT_GRAPHITE_H
+#define UTILS_FORMAT_GRAPHITE_H 1
+
+#include "collectd.h"
+#include "plugin.h"
+
+#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,
+    unsigned int flags);
+
+#endif /* UTILS_FORMAT_GRAPHITE_H */
index ac88c0f..898b172 100644 (file)
@@ -70,7 +70,7 @@ static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
 #undef BUFFER_ADD
 
   return (0);
-} /* }}} int buffer_add_string */
+} /* }}} int escape_string */
 
 static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
                 const data_set_t *ds, const value_list_t *vl, int store_rates)
@@ -152,7 +152,7 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
 } /* }}} int values_to_json */
 
 static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */
-                const data_set_t *ds, const value_list_t *vl)
+                const data_set_t *ds)
 {
   size_t offset = 0;
   int i;
@@ -189,7 +189,7 @@ static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */
 } /* }}} int dstypes_to_json */
 
 static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */
-                const data_set_t *ds, const value_list_t *vl)
+                const data_set_t *ds)
 {
   size_t offset = 0;
   int i;
@@ -225,6 +225,95 @@ static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */
   return (0);
 } /* }}} int dsnames_to_json */
 
+static int meta_data_to_json (char *buffer, size_t buffer_size, /* {{{ */
+    meta_data_t *meta)
+{
+  size_t offset = 0;
+  char **keys = NULL;
+  int keys_num;
+  int status;
+  int i;
+
+  buffer[0] = 0;
+
+  if (meta == NULL)
+    return (EINVAL);
+
+#define BUFFER_ADD(...) do { \
+  status = ssnprintf (buffer + offset, buffer_size - offset, \
+      __VA_ARGS__); \
+  if (status < 1) \
+    return (-1); \
+  else if (((size_t) status) >= (buffer_size - offset)) \
+    return (-ENOMEM); \
+  else \
+    offset += ((size_t) status); \
+} while (0)
+
+  keys_num = meta_data_toc (meta, &keys);
+  if (keys_num == 0)
+  {
+    sfree (keys);
+    return (0);
+  }
+
+  for (i = 0; i < keys_num; ++i)
+  {
+    int type;
+    char *key = keys[i];
+
+    type = meta_data_type (meta, key);
+    if (type == MD_TYPE_STRING)
+    {
+      char *value = NULL;
+      if (meta_data_get_string (meta, key, &value) == 0)
+      {
+        char temp[512] = "";
+        escape_string (temp, sizeof (temp), value);
+        sfree (value);
+        BUFFER_ADD (",\"%s\":%s", key, temp);
+      }
+    }
+    else if (type == MD_TYPE_SIGNED_INT)
+    {
+      int64_t value = 0;
+      if (meta_data_get_signed_int (meta, key, &value) == 0)
+        BUFFER_ADD (",\"%s\":%"PRIi64, key, value);
+    }
+    else if (type == MD_TYPE_UNSIGNED_INT)
+    {
+      uint64_t value = 0;
+      if (meta_data_get_unsigned_int (meta, key, &value) == 0)
+        BUFFER_ADD (",\"%s\":%"PRIu64, key, value);
+    }
+    else if (type == MD_TYPE_DOUBLE)
+    {
+      double value = 0.0;
+      if (meta_data_get_double (meta, key, &value) == 0)
+        BUFFER_ADD (",\"%s\":%f", key, value);
+    }
+    else if (type == MD_TYPE_BOOLEAN)
+    {
+      _Bool value = 0;
+      if (meta_data_get_boolean (meta, key, &value) == 0)
+        BUFFER_ADD (",\"%s\":%s", key, value ? "true" : "false");
+    }
+
+    free (key);
+  } /* for (keys) */
+  free (keys);
+
+  if (offset <= 0)
+    return (ENOENT);
+
+  buffer[0] = '{'; /* replace leading ',' */
+  BUFFER_ADD ("}");
+
+#undef BUFFER_ADD
+
+  return (0);
+} /* }}} int meta_data_to_json */
+
 static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
                 const data_set_t *ds, const value_list_t *vl, int store_rates)
 {
@@ -254,18 +343,18 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
     return (status);
   BUFFER_ADD ("\"values\":%s", temp);
 
-  status = dstypes_to_json (temp, sizeof (temp), ds, vl);
+  status = dstypes_to_json (temp, sizeof (temp), ds);
   if (status != 0)
     return (status);
   BUFFER_ADD (",\"dstypes\":%s", temp);
 
-  status = dsnames_to_json (temp, sizeof (temp), ds, vl);
+  status = dsnames_to_json (temp, sizeof (temp), ds);
   if (status != 0)
     return (status);
   BUFFER_ADD (",\"dsnames\":%s", temp);
 
-  BUFFER_ADD (",\"time\":%lu", (unsigned long) vl->time);
-  BUFFER_ADD (",\"interval\":%i", vl->interval);
+  BUFFER_ADD (",\"time\":%.3f", CDTIME_T_TO_DOUBLE (vl->time));
+  BUFFER_ADD (",\"interval\":%.3f", CDTIME_T_TO_DOUBLE (vl->interval));
 
 #define BUFFER_ADD_KEYVAL(key, value) do { \
   status = escape_string (temp, sizeof (temp), (value)); \
@@ -280,6 +369,17 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
   BUFFER_ADD_KEYVAL ("type", vl->type);
   BUFFER_ADD_KEYVAL ("type_instance", vl->type_instance);
 
+  if (vl->meta != NULL)
+  {
+    char meta_buffer[buffer_size];
+    memset (meta_buffer, 0, sizeof (meta_buffer));
+    status = meta_data_to_json (meta_buffer, sizeof (meta_buffer), vl->meta);
+    if (status != 0)
+      return (status);
+
+    BUFFER_ADD (",\"meta\":%s", meta_buffer);
+  } /* if (vl->meta != NULL) */
+
   BUFFER_ADD ("}");
 
 #undef BUFFER_ADD_KEYVAL
index 062bcfe..bb53a9a 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/utils_match.c
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008-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
@@ -279,6 +279,18 @@ cu_match_t *match_create_simple (const char *regex,
   return (obj);
 } /* cu_match_t *match_create_simple */
 
+void match_value_reset (cu_match_value_t *mv)
+{
+  if (mv == NULL)
+    return;
+
+  if (mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
+  {
+    mv->value.gauge = NAN;
+    mv->values_num = 0;
+  }
+} /* }}} void match_value_reset */
+
 void match_destroy (cu_match_t *obj)
 {
   if (obj == NULL)
index 36abe30..24517b3 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/utils_match.h
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008-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
@@ -119,6 +119,17 @@ cu_match_t *match_create_simple (const char *regex,
 
 /*
  * NAME
+ *  match_value_reset
+ *
+ * DESCRIPTION
+ *   Resets the internal state, if applicable. This function must be called
+ *   after each iteration for "simple" matches, usually after dispatching the
+ *   metrics.
+ */
+void match_value_reset (cu_match_value_t *mv);
+
+/*
+ * NAME
  *  match_destroy
  *
  * DESCRIPTION
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 47bf9f6..5368059 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,72 @@ 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);
+  }
+  sfree (args);
+} /* 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 *
  * * * * * * * * * */
@@ -103,7 +190,10 @@ static int rra_get (char ***ret, const value_list_t *vl, /* {{{ */
     return (-1);
   }
 
-  ss = (cfg->stepsize > 0) ? cfg->stepsize : vl->interval;
+  if (cfg->stepsize > 0)
+    ss = cfg->stepsize;
+  else
+    ss = (int) CDTIME_T_TO_TIME_T (vl->interval);
   if (ss <= 0)
   {
     *ret = NULL;
@@ -241,7 +331,9 @@ static int ds_get (char ***ret, /* {{{ */
     status = ssnprintf (buffer, sizeof (buffer),
         "DS:%s:%s:%i:%s:%s",
         d->name, type,
-        (cfg->heartbeat > 0) ? cfg->heartbeat : (2 * vl->interval),
+        (cfg->heartbeat > 0)
+        ? cfg->heartbeat
+        : (int) CDTIME_T_TO_TIME_T (2 * vl->interval),
         min, max);
     if ((status < 1) || ((size_t) status >= sizeof (buffer)))
       break;
@@ -323,7 +415,7 @@ static int srrd_create (const char *filename, /* {{{ */
     last_up = time (NULL) - 10;
 
   ssnprintf (pdp_step_str, sizeof (pdp_step_str), "%lu", pdp_step);
-  ssnprintf (last_up_str, sizeof (last_up_str), "%u", (unsigned int) last_up);
+  ssnprintf (last_up_str, sizeof (last_up_str), "%lu", (unsigned long) last_up);
 
   new_argv[0] = "create";
   new_argv[1] = (void *) filename;
@@ -354,6 +446,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
  */
@@ -368,6 +651,8 @@ int cu_rrd_create_file (const char *filename, /* {{{ */
   char **ds_def;
   int ds_num;
   int status = 0;
+  time_t last_up;
+  unsigned long stepsize;
 
   if (check_create_dir (filename))
     return (-1);
@@ -398,26 +683,46 @@ int cu_rrd_create_file (const char *filename, /* {{{ */
   memcpy (argv + ds_num, rra_def, rra_num * sizeof (char *));
   argv[ds_num + rra_num] = NULL;
 
-  status = srrd_create (filename,
-      (cfg->stepsize > 0) ? cfg->stepsize : vl->interval,
-      (vl->time > 10) ? (vl->time - 10) : vl->time,
-      argc, (const char **) argv);
+  last_up = CDTIME_T_TO_TIME_T (vl->time);
+  if (last_up <= 0)
+    last_up = time (NULL);
+  last_up -= 1;
 
-  free (argv);
-  ds_free (ds_num, ds_def);
-  rra_free (rra_num, rra_def);
+  if (cfg->stepsize > 0)
+    stepsize = cfg->stepsize;
+  else
+    stepsize = (unsigned long) CDTIME_T_TO_TIME_T (vl->interval);
 
-  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 935e4e0..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
@@ -28,7 +28,7 @@
 
 struct rrdcreate_config_s
 {
-  int    stepsize;
+  unsigned long stepsize;
   int    heartbeat;
   int    rrarows;
   double xff;
@@ -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);
diff --git a/src/utils_threshold.c b/src/utils_threshold.c
deleted file mode 100644 (file)
index b14c79b..0000000
+++ /dev/null
@@ -1,1080 +0,0 @@
-/**
- * collectd - src/utils_threshold.c
- * Copyright (C) 2007-2009  Florian octo Forster
- * Copyright (C) 2008-2009  Sebastian Harl
- * Copyright (C) 2009       Andrés J. Díaz
- *
- *
- * 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:
- *   Florian octo Forster <octo at verplant.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 <pthread.h>
-
-/*
- * Private data structures
- * {{{ */
-#define UT_FLAG_INVERT  0x01
-#define UT_FLAG_PERSIST 0x02
-#define UT_FLAG_PERCENTAGE 0x04
-/* }}} */
-
-/*
- * 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.
- * {{{ */
-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 */
-
-static int ut_threshold_add (const threshold_t *th)
-{
-  char name[6 * DATA_MAX_NAME_LEN];
-  char *name_copy;
-  threshold_t *th_copy;
-  threshold_t *th_ptr;
-  int status = 0;
-
-  if (format_name (name, sizeof (name), th->host,
-       th->plugin, th->plugin_instance,
-       th->type, th->type_instance) != 0)
-  {
-    ERROR ("ut_threshold_add: format_name failed.");
-    return (-1);
-  }
-
-  name_copy = strdup (name);
-  if (name_copy == NULL)
-  {
-    ERROR ("ut_threshold_add: strdup failed.");
-    return (-1);
-  }
-
-  th_copy = (threshold_t *) malloc (sizeof (threshold_t));
-  if (th_copy == NULL)
-  {
-    sfree (name_copy);
-    ERROR ("ut_threshold_add: malloc failed.");
-    return (-1);
-  }
-  memcpy (th_copy, th, sizeof (threshold_t));
-  th_ptr = NULL;
-
-  DEBUG ("ut_threshold_add: Adding entry `%s'", name);
-
-  pthread_mutex_lock (&threshold_lock);
-
-  th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
-      th->type, th->type_instance);
-
-  while ((th_ptr != NULL) && (th_ptr->next != NULL))
-    th_ptr = th_ptr->next;
-
-  if (th_ptr == NULL) /* no such threshold yet */
-  {
-    status = c_avl_insert (threshold_tree, name_copy, th_copy);
-  }
-  else /* th_ptr points to the last threshold in the list */
-  {
-    th_ptr->next = th_copy;
-    /* name_copy isn't needed */
-    sfree (name_copy);
-  }
-
-  pthread_mutex_unlock (&threshold_lock);
-
-  if (status != 0)
-  {
-    ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
-    sfree (name_copy);
-    sfree (th_copy);
-  }
-
-  return (status);
-} /* int ut_threshold_add */
-/*
- * End of the threshold management functions
- * }}} */
-
-/*
- * Configuration
- * =============
- * The following approximately two hundred functions are used to handle the
- * configuration and fill the threshold list.
- * {{{ */
-static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("threshold values: The `DataSource' option needs exactly one "
-       "string argument.");
-    return (-1);
-  }
-
-  sstrncpy (th->data_source, ci->values[0].value.string,
-      sizeof (th->data_source));
-
-  return (0);
-} /* int ut_config_type_datasource */
-
-static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("threshold values: The `Instance' option needs exactly one "
-       "string argument.");
-    return (-1);
-  }
-
-  sstrncpy (th->type_instance, ci->values[0].value.string,
-      sizeof (th->type_instance));
-
-  return (0);
-} /* int ut_config_type_instance */
-
-static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
-  {
-    WARNING ("threshold values: The `%s' option needs exactly one "
-       "number argument.", ci->key);
-    return (-1);
-  }
-
-  if (strcasecmp (ci->key, "WarningMax") == 0)
-    th->warning_max = ci->values[0].value.number;
-  else
-    th->failure_max = ci->values[0].value.number;
-
-  return (0);
-} /* int ut_config_type_max */
-
-static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
-  {
-    WARNING ("threshold values: The `%s' option needs exactly one "
-       "number argument.", ci->key);
-    return (-1);
-  }
-
-  if (strcasecmp (ci->key, "WarningMin") == 0)
-    th->warning_min = ci->values[0].value.number;
-  else
-    th->failure_min = ci->values[0].value.number;
-
-  return (0);
-} /* int ut_config_type_min */
-
-static int ut_config_type_invert (threshold_t *th, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
-  {
-    WARNING ("threshold values: The `Invert' option needs exactly one "
-       "boolean argument.");
-    return (-1);
-  }
-
-  if (ci->values[0].value.boolean)
-    th->flags |= UT_FLAG_INVERT;
-  else
-    th->flags &= ~UT_FLAG_INVERT;
-
-  return (0);
-} /* int ut_config_type_invert */
-
-static int ut_config_type_persist (threshold_t *th, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
-  {
-    WARNING ("threshold values: The `Persist' option needs exactly one "
-       "boolean argument.");
-    return (-1);
-  }
-
-  if (ci->values[0].value.boolean)
-    th->flags |= UT_FLAG_PERSIST;
-  else
-    th->flags &= ~UT_FLAG_PERSIST;
-
-  return (0);
-} /* int ut_config_type_persist */
-
-static int ut_config_type_percentage(threshold_t *th, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
-  {
-    WARNING ("threshold values: The `Percentage' option needs exactly one "
-       "boolean argument.");
-    return (-1);
-  }
-
-  if (ci->values[0].value.boolean)
-    th->flags |= UT_FLAG_PERCENTAGE;
-  else
-    th->flags &= ~UT_FLAG_PERCENTAGE;
-
-  return (0);
-} /* int ut_config_type_percentage */
-
-static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
-  {
-    WARNING ("threshold values: The `%s' option needs exactly one "
-      "number argument.", ci->key);
-    return (-1);
-  }
-
-  th->hits = ci->values[0].value.number;
-
-  return (0);
-} /* int ut_config_type_hits */
-
-static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
-  {
-    WARNING ("threshold values: The `%s' option needs exactly one "
-      "number argument.", ci->key);
-    return (-1);
-  }
-
-  th->hysteresis = ci->values[0].value.number;
-
-  return (0);
-} /* int ut_config_type_hysteresis */
-
-static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
-{
-  int i;
-  threshold_t th;
-  int status = 0;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("threshold values: The `Type' block needs exactly one string "
-       "argument.");
-    return (-1);
-  }
-
-  if (ci->children_num < 1)
-  {
-    WARNING ("threshold values: The `Type' block needs at least one option.");
-    return (-1);
-  }
-
-  memcpy (&th, th_orig, sizeof (th));
-  sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
-
-  th.warning_min = NAN;
-  th.warning_max = NAN;
-  th.failure_min = NAN;
-  th.failure_max = NAN;
-  th.hits = 0;
-  th.hysteresis = 0;
-
-  for (i = 0; i < ci->children_num; i++)
-  {
-    oconfig_item_t *option = ci->children + i;
-    status = 0;
-
-    if (strcasecmp ("Instance", option->key) == 0)
-      status = ut_config_type_instance (&th, option);
-    else if (strcasecmp ("DataSource", option->key) == 0)
-      status = ut_config_type_datasource (&th, option);
-    else if ((strcasecmp ("WarningMax", option->key) == 0)
-       || (strcasecmp ("FailureMax", option->key) == 0))
-      status = ut_config_type_max (&th, option);
-    else if ((strcasecmp ("WarningMin", option->key) == 0)
-       || (strcasecmp ("FailureMin", option->key) == 0))
-      status = ut_config_type_min (&th, option);
-    else if (strcasecmp ("Invert", option->key) == 0)
-      status = ut_config_type_invert (&th, option);
-    else if (strcasecmp ("Persist", option->key) == 0)
-      status = ut_config_type_persist (&th, option);
-    else if (strcasecmp ("Percentage", option->key) == 0)
-      status = ut_config_type_percentage (&th, option);
-    else if (strcasecmp ("Hits", option->key) == 0)
-      status = ut_config_type_hits (&th, option);
-    else if (strcasecmp ("Hysteresis", option->key) == 0)
-      status = ut_config_type_hysteresis (&th, option);
-    else
-    {
-      WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
-         "block.", option->key);
-      status = -1;
-    }
-
-    if (status != 0)
-      break;
-  }
-
-  if (status == 0)
-  {
-    status = ut_threshold_add (&th);
-  }
-
-  return (status);
-} /* int ut_config_type */
-
-static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
-{
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("threshold values: The `Instance' option needs exactly one "
-       "string argument.");
-    return (-1);
-  }
-
-  sstrncpy (th->plugin_instance, ci->values[0].value.string,
-      sizeof (th->plugin_instance));
-
-  return (0);
-} /* int ut_config_plugin_instance */
-
-static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
-{
-  int i;
-  threshold_t th;
-  int status = 0;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("threshold values: The `Plugin' block needs exactly one string "
-       "argument.");
-    return (-1);
-  }
-
-  if (ci->children_num < 1)
-  {
-    WARNING ("threshold values: The `Plugin' block needs at least one nested "
-       "block.");
-    return (-1);
-  }
-
-  memcpy (&th, th_orig, sizeof (th));
-  sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
-
-  for (i = 0; i < ci->children_num; i++)
-  {
-    oconfig_item_t *option = ci->children + i;
-    status = 0;
-
-    if (strcasecmp ("Type", option->key) == 0)
-      status = ut_config_type (&th, option);
-    else if (strcasecmp ("Instance", option->key) == 0)
-      status = ut_config_plugin_instance (&th, option);
-    else
-    {
-      WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
-         "block.", option->key);
-      status = -1;
-    }
-
-    if (status != 0)
-      break;
-  }
-
-  return (status);
-} /* int ut_config_plugin */
-
-static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
-{
-  int i;
-  threshold_t th;
-  int status = 0;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("threshold values: The `Host' block needs exactly one string "
-       "argument.");
-    return (-1);
-  }
-
-  if (ci->children_num < 1)
-  {
-    WARNING ("threshold values: The `Host' block needs at least one nested "
-       "block.");
-    return (-1);
-  }
-
-  memcpy (&th, th_orig, sizeof (th));
-  sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
-
-  for (i = 0; i < ci->children_num; i++)
-  {
-    oconfig_item_t *option = ci->children + i;
-    status = 0;
-
-    if (strcasecmp ("Type", option->key) == 0)
-      status = ut_config_type (&th, option);
-    else if (strcasecmp ("Plugin", option->key) == 0)
-      status = ut_config_plugin (&th, option);
-    else
-    {
-      WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
-         "block.", option->key);
-      status = -1;
-    }
-
-    if (status != 0)
-      break;
-  }
-
-  return (status);
-} /* int ut_config_host */
-
-int ut_config (const oconfig_item_t *ci)
-{
-  int i;
-  int status = 0;
-
-  threshold_t th;
-
-  if (ci->values_num != 0)
-  {
-    ERROR ("threshold values: The `Threshold' block may not have any "
-       "arguments.");
-    return (-1);
-  }
-
-  if (threshold_tree == NULL)
-  {
-    threshold_tree = c_avl_create ((void *) strcmp);
-    if (threshold_tree == NULL)
-    {
-      ERROR ("ut_config: c_avl_create failed.");
-      return (-1);
-    }
-  }
-
-  memset (&th, '\0', sizeof (th));
-  th.warning_min = NAN;
-  th.warning_max = NAN;
-  th.failure_min = NAN;
-  th.failure_max = NAN;
-
-  th.hits = 0;
-  th.hysteresis = 0;
-    
-  for (i = 0; i < ci->children_num; i++)
-  {
-    oconfig_item_t *option = ci->children + i;
-    status = 0;
-
-    if (strcasecmp ("Type", option->key) == 0)
-      status = ut_config_type (&th, option);
-    else if (strcasecmp ("Plugin", option->key) == 0)
-      status = ut_config_plugin (&th, option);
-    else if (strcasecmp ("Host", option->key) == 0)
-      status = ut_config_host (&th, option);
-    else
-    {
-      WARNING ("threshold values: Option `%s' not allowed here.", option->key);
-      status = -1;
-    }
-
-    if (status != 0)
-      break;
-  }
-
-  return (status);
-} /* int um_config */
-/*
- * End of the functions used to configure threshold values.
- */
-/* }}} */
-
-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 */
-
-/*
- * int ut_report_state
- *
- * Checks if the `state' differs from the old state and creates a notification
- * if appropriate.
- * Does not fail.
- */
-static int ut_report_state (const data_set_t *ds,
-    const value_list_t *vl,
-    const threshold_t *th,
-    const gauge_t *values,
-    int ds_index,
-    int state)
-{ /* {{{ */
-  int state_old;
-  notification_t n;
-
-  char *buf;
-  size_t bufsize;
-
-  int status;
-
-  /* Check if hits matched */
-  if ( (th->hits != 0) )
-  {
-    int hits = uc_get_hits(ds,vl);
-    /* The STATE_OKAY always reset hits, or if hits reaise the limit */
-    if ( (state == STATE_OKAY) || (hits > th->hits) )
-    {
-        DEBUG("ut_report_state: reset uc_get_hits = 0");
-        uc_set_hits(ds,vl,0); /* reset hit counter and notify */
-    } else {
-      DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
-      (void) uc_inc_hits(ds,vl,1); /* increase hit counter */
-      return (0);
-    }
-  } /* end check hits */
-
-  state_old = uc_get_state (ds, vl);
-
-  /* If the state didn't change, only report if `persistent' is specified and
-   * the state is not `okay'. */
-  if (state == state_old)
-  {
-    if ((th->flags & UT_FLAG_PERSIST) == 0)
-      return (0);
-    else if (state == STATE_OKAY)
-      return (0);
-  }
-
-  if (state != state_old)
-    uc_set_state (ds, vl, state);
-
-  NOTIFICATION_INIT_VL (&n, vl, ds);
-
-  buf = n.message;
-  bufsize = sizeof (n.message);
-
-  if (state == STATE_OKAY)
-    n.severity = NOTIF_OKAY;
-  else if (state == STATE_WARNING)
-    n.severity = NOTIF_WARNING;
-  else
-    n.severity = NOTIF_FAILURE;
-
-  n.time = vl->time;
-
-  status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
-      vl->host, vl->plugin);
-  buf += status;
-  bufsize -= status;
-
-  if (vl->plugin_instance[0] != '\0')
-  {
-    status = ssnprintf (buf, bufsize, " (instance %s)",
-       vl->plugin_instance);
-    buf += status;
-    bufsize -= status;
-  }
-
-  status = ssnprintf (buf, bufsize, " type %s", vl->type);
-  buf += status;
-  bufsize -= status;
-
-  if (vl->type_instance[0] != '\0')
-  {
-    status = ssnprintf (buf, bufsize, " (instance %s)",
-       vl->type_instance);
-    buf += status;
-    bufsize -= status;
-  }
-
-  plugin_notification_meta_add_string (&n, "DataSource",
-      ds->ds[ds_index].name);
-  plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
-  plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
-  plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
-  plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
-  plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
-
-  /* Send an okay notification */
-  if (state == STATE_OKAY)
-  {
-    status = ssnprintf (buf, bufsize, ": All data sources are within range again.");
-    buf += status;
-    bufsize -= status;
-  }
-  else
-  {
-    double min;
-    double max;
-
-    min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
-    max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
-
-    if (th->flags & UT_FLAG_INVERT)
-    {
-      if (!isnan (min) && !isnan (max))
-      {
-        status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
-            "%f. That is within the %s region of %f%s and %f%s.",
-            ds->ds[ds_index].name, values[ds_index],
-            (state == STATE_ERROR) ? "failure" : "warning",
-            min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
-            max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
-      }
-      else
-      {
-       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
-           "%f. That is %s the %s threshold of %f%s.",
-           ds->ds[ds_index].name, values[ds_index],
-           isnan (min) ? "below" : "above",
-           (state == STATE_ERROR) ? "failure" : "warning",
-           isnan (min) ? max : min,
-           ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
-      }
-    }
-    else if (th->flags & UT_FLAG_PERCENTAGE)
-    {
-      gauge_t value;
-      gauge_t sum;
-      int i;
-
-      sum = 0.0;
-      for (i = 0; i < vl->values_len; i++)
-      {
-        if (isnan (values[i]))
-          continue;
-
-        sum += values[i];
-      }
-
-      if (sum == 0.0)
-        value = NAN;
-      else
-        value = 100.0 * values[ds_index] / sum;
-
-      status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
-          "%g (%.2f%%). That is %s the %s threshold of %.2f%%.",
-          ds->ds[ds_index].name, values[ds_index], value,
-          (value < min) ? "below" : "above",
-          (state == STATE_ERROR) ? "failure" : "warning",
-          (value < min) ? min : max);
-    }
-    else /* is not inverted */
-    {
-      status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
-         "%f. That is %s the %s threshold of %f.",
-         ds->ds[ds_index].name, values[ds_index],
-         (values[ds_index] < min) ? "below" : "above",
-         (state == STATE_ERROR) ? "failure" : "warning",
-         (values[ds_index] < min) ? min : max);
-    }
-    buf += status;
-    bufsize -= status;
-  }
-
-  plugin_dispatch_notification (&n);
-
-  plugin_notification_meta_free (n.meta);
-  return (0);
-} /* }}} int ut_report_state */
-
-/*
- * 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 *ret_ds_index)
-{ /* {{{ */
-  int ret = -1;
-  int ds_index = -1;
-  int i;
-  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++)
-  {
-    int status;
-
-    status = ut_check_one_data_source (ds, vl, th, values_copy, i);
-    if (ret < status)
-    {
-      ret = status;
-      ds_index = i;
-    }
-  } /* for (ds->ds_num) */
-
-  if (ret_ds_index != NULL)
-    *ret_ds_index = ds_index;
-
-  return (ret);
-} /* }}} int ut_check_one_threshold */
-
-/*
- * int ut_check_threshold (PUBLIC)
- *
- * 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 ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
-{ /* {{{ */
-  threshold_t *th;
-  gauge_t *values;
-  int status;
-
-  int worst_state = -1;
-  threshold_t *worst_th = NULL;
-  int worst_ds_index = -1;
-
-  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)
-  {
-    int ds_index = -1;
-
-    status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
-    if (status < 0)
-    {
-      ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
-      sfree (values);
-      return (-1);
-    }
-
-    if (worst_state < status)
-    {
-      worst_state = status;
-      worst_th = th;
-      worst_ds_index = ds_index;
-    }
-
-    th = th->next;
-  } /* while (th) */
-
-  status = ut_report_state (ds, vl, worst_th, values,
-      worst_ds_index, worst_state);
-  if (status != 0)
-  {
-    ERROR ("ut_check_threshold: ut_report_state failed.");
-    sfree (values);
-    return (-1);
-  }
-
-  sfree (values);
-
-  return (0);
-} /* }}} int ut_check_threshold */
-
-/*
- * int ut_check_interesting (PUBLIC)
- *
- * Given an identification returns
- * 0: No threshold is defined.
- * 1: A threshold has been found. The flag `persist' is off.
- * 2: A threshold has been found. The flag `persist' is on.
- *    (That is, it is expected that many notifications are sent until the
- *    problem disappears.)
- */
-int ut_check_interesting (const char *name)
-{ /* {{{ */
-  char *name_copy = NULL;
-  char *host = NULL;
-  char *plugin = NULL;
-  char *plugin_instance = NULL;
-  char *type = NULL;
-  char *type_instance = NULL;
-  int status;
-  data_set_t ds;
-  value_list_t vl;
-  threshold_t *th;
-
-  /* If there is no tree nothing is interesting. */
-  if (threshold_tree == NULL)
-    return (0);
-
-  name_copy = strdup (name);
-  if (name_copy == NULL)
-  {
-    ERROR ("ut_check_interesting: strdup failed.");
-    return (-1);
-  }
-
-  status = parse_identifier (name_copy, &host,
-      &plugin, &plugin_instance, &type, &type_instance);
-  if (status != 0)
-  {
-    ERROR ("ut_check_interesting: parse_identifier failed.");
-    sfree (name_copy);
-    return (-1);
-  }
-
-  memset (&ds, '\0', sizeof (ds));
-  memset (&vl, '\0', sizeof (vl));
-
-  sstrncpy (vl.host, host, sizeof (vl.host));
-  sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
-  if (plugin_instance != NULL)
-    sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
-  sstrncpy (ds.type, type, sizeof (ds.type));
-  sstrncpy (vl.type, type, sizeof (vl.type));
-  if (type_instance != NULL)
-    sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
-
-  sfree (name_copy);
-  host = plugin = plugin_instance = type = type_instance = NULL;
-
-  th = threshold_search (&vl);
-  if (th == NULL)
-    return (0);
-  if ((th->flags & UT_FLAG_PERSIST) == 0)
-    return (1);
-  return (2);
-} /* }}} int ut_check_interesting */
-
-int ut_search_threshold (const value_list_t *vl, /* {{{ */
-    threshold_t *ret_threshold)
-{
-  threshold_t *t;
-
-  if (vl == NULL)
-    return (EINVAL);
-
-  t = threshold_search (vl);
-  if (t == NULL)
-    return (ENOENT);
-
-  memcpy (ret_threshold, t, sizeof (*ret_threshold));
-  ret_threshold->next = NULL;
-
-  return (0);
-} /* }}} int ut_search_threshold */
-
-/* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */
diff --git a/src/utils_threshold.h b/src/utils_threshold.h
deleted file mode 100644 (file)
index 8aaf34c..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * collectd - src/utils_threshold.h
- * Copyright (C) 2007-2009  Florian octo Forster
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- *
- * Author:
- *   Florian octo Forster <octo at verplant.org>
- **/
-
-#ifndef UTILS_THRESHOLD_H
-#define UTILS_THRESHOLD_H 1
-
-#include "collectd.h"
-#include "liboconfig/oconfig.h"
-#include "plugin.h"
-
-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;
-  int flags;
-  int hits;
-  struct threshold_s *next;
-} threshold_t;
-
-/*
- * ut_config
- *
- * Parses the configuration and sets up the module. This is called from
- * `src/configfile.c'.
- */
-int ut_config (const oconfig_item_t *ci);
-
-/*
- * ut_check_threshold
- *
- * Checks if a threshold is defined for this value and if such a threshold is
- * configured, check if the value within the acceptable range. If it is not, a
- * notification is dispatched to inform the user that a problem exists. This is
- * called from `plugin_read_all'.
- */
-int ut_check_threshold (const data_set_t *ds, const value_list_t *vl);
-
-/*
- * Given an identification returns
- * 0: No threshold is defined.
- * 1: A threshold has been found. The flag `persist' is off.
- * 2: A threshold has been found. The flag `persist' is on.
- *    (That is, it is expected that many notifications are sent until the
- *    problem disappears.)
- */
-int ut_check_interesting (const char *name);
-
-/* 
- * Given an identifier in form of a `value_list_t', searches for the best
- * matching threshold configuration. `ret_threshold' may be NULL.
- *
- * Returns:
- *        0: Success. Threshold configuration has been copied to
- *           `ret_threshold' (if it is non-NULL).
- *   ENOENT: No configuration for this identifier found.
- *     else: Error.
- */
-int ut_search_threshold (const value_list_t *vl, threshold_t *ret_threshold);
-
-#endif /* UTILS_THRESHOLD_H */
diff --git a/src/utils_time.c b/src/utils_time.c
new file mode 100644 (file)
index 0000000..6789758
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ * collectd - src/utils_time.h
+ * Copyright (C) 2010  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <ff at octo.it>
+ **/
+
+#include "collectd.h"
+#include "utils_time.h"
+#include "plugin.h"
+#include "common.h"
+
+#if HAVE_CLOCK_GETTIME
+cdtime_t cdtime (void) /* {{{ */
+{
+  int status;
+  struct timespec ts = { 0, 0 };
+
+  status = clock_gettime (CLOCK_REALTIME, &ts);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("cdtime: clock_gettime failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (0);
+  }
+
+  return (TIMESPEC_TO_CDTIME_T (&ts));
+} /* }}} cdtime_t cdtime */
+#else
+/* Work around for Mac OS X which doesn't have clock_gettime(2). *sigh* */
+cdtime_t cdtime (void) /* {{{ */
+{
+  int status;
+  struct timeval tv = { 0, 0 };
+
+  status = gettimeofday (&tv, /* struct timezone = */ NULL);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("cdtime: gettimeofday failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (0);
+  }
+
+  return (TIMEVAL_TO_CDTIME_T (&tv));
+} /* }}} cdtime_t cdtime */
+#endif
+
+size_t cdtime_to_iso8601 (char *s, size_t max, cdtime_t t) /* {{{ */
+{
+  struct timespec t_spec;
+  struct tm t_tm;
+
+  size_t len;
+
+  CDTIME_T_TO_TIMESPEC (t, &t_spec);
+  NORMALIZE_TIMESPEC (t_spec);
+
+  if (localtime_r ((time_t *)&t_spec.tv_sec, &t_tm) == NULL) {
+    char errbuf[1024];
+    ERROR ("cdtime_to_iso8601: localtime_r failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (0);
+  }
+
+  len = strftime (s, max, "%Y-%m-%dT%H:%M:%S", &t_tm);
+  if (len == 0)
+    return 0;
+
+  if (max - len > 2) {
+    int n = snprintf (s + len, max - len, ".%09i", (int)t_spec.tv_nsec);
+    len += (n < max - len) ? n : max - len;
+  }
+
+  if (max - len > 3) {
+    int n = strftime (s + len, max - len, "%z", &t_tm);
+    len += (n < max - len) ? n : max - len;
+  }
+
+  s[max - 1] = '\0';
+  return len;
+} /* }}} size_t cdtime_to_iso8601 */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/utils_time.h b/src/utils_time.h
new file mode 100644 (file)
index 0000000..0081957
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * collectd - src/utils_time.h
+ * Copyright (C) 2010  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <ff at octo.it>
+ **/
+
+#ifndef UTILS_TIME_H
+#define UTILS_TIME_H 1
+
+#include "collectd.h"
+
+/*
+ * "cdtime_t" is a 64bit unsigned integer. The time is stored at a 2^-30 second
+ * resolution, i.e. the most significant 34 bit are used to store the time in
+ * seconds, the least significant bits store the sub-second part in something
+ * very close to nanoseconds. *The* big advantage of storing time in this
+ * manner is that comparing times and calculating differences is as simple as
+ * it is with "time_t", i.e. a simple integer comparison / subtraction works.
+ */
+/* 
+ * cdtime_t is defined in "collectd.h" */
+/* typedef uint64_t cdtime_t; */
+
+/* 2^30 = 1073741824 */
+#define TIME_T_TO_CDTIME_T(t) (((cdtime_t) (t)) * 1073741824)
+#define CDTIME_T_TO_TIME_T(t) ((time_t) ((t) / 1073741824))
+
+#define CDTIME_T_TO_DOUBLE(t) (((double) (t)) / 1073741824.0)
+#define DOUBLE_TO_CDTIME_T(d) ((cdtime_t) ((d) * 1073741824.0))
+
+#define MS_TO_CDTIME_T(ms) ((cdtime_t)    (((double) (ms)) * 1073741.824))
+#define CDTIME_T_TO_MS(t)  ((long)        (((double) (t))  / 1073741.824))
+#define US_TO_CDTIME_T(us) ((cdtime_t)    (((double) (us)) * 1073.741824))
+#define CDTIME_T_TO_US(t)  ((suseconds_t) (((double) (t))  / 1073.741824))
+#define NS_TO_CDTIME_T(ns) ((cdtime_t)    (((double) (ns)) * 1.073741824))
+#define CDTIME_T_TO_NS(t)  ((long)        (((double) (t))  / 1.073741824))
+
+#define CDTIME_T_TO_TIMEVAL(cdt,tvp) do {                                    \
+        (tvp)->tv_sec = CDTIME_T_TO_TIME_T (cdt);                            \
+        (tvp)->tv_usec = CDTIME_T_TO_US ((cdt) % 1073741824);                \
+} while (0)
+#define TIMEVAL_TO_CDTIME_T(tv) (TIME_T_TO_CDTIME_T ((tv)->tv_sec)           \
+    + US_TO_CDTIME_T ((tv)->tv_usec))
+
+#define CDTIME_T_TO_TIMESPEC(cdt,tsp) do {                                   \
+  (tsp)->tv_sec = CDTIME_T_TO_TIME_T (cdt);                                  \
+  (tsp)->tv_nsec = CDTIME_T_TO_NS ((cdt) % 1073741824);                      \
+} while (0)
+#define TIMESPEC_TO_CDTIME_T(ts) (TIME_T_TO_CDTIME_T ((ts)->tv_sec)           \
+    + NS_TO_CDTIME_T ((ts)->tv_nsec))
+
+cdtime_t cdtime (void);
+
+/* format a cdtime_t value in ISO 8601 format:
+ * returns the number of characters written to the string (not including the
+ * terminating null byte or 0 on error; the function ensures that the string
+ * is null terminated */
+size_t cdtime_to_iso8601 (char *s, size_t max, cdtime_t t);
+
+#endif /* UTILS_TIME_H */
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/utils_vl_lookup.c b/src/utils_vl_lookup.c
new file mode 100644 (file)
index 0000000..8180d0d
--- /dev/null
@@ -0,0 +1,655 @@
+/**
+ * collectd - src/utils_vl_lookup.c
+ * Copyright (C) 2012  Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include <pthread.h>
+#include <regex.h>
+
+#include "common.h"
+#include "utils_vl_lookup.h"
+#include "utils_avltree.h"
+
+#if BUILD_TEST
+# define sstrncpy strncpy
+# define plugin_log(s, ...) do { \
+  printf ("[severity %i] ", s); \
+  printf (__VA_ARGS__); \
+  printf ("\n"); \
+} while (0)
+#endif
+
+/*
+ * Types
+ */
+struct 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;
+
+  lookup_class_callback_t cb_user_class;
+  lookup_obj_callback_t cb_user_obj;
+  lookup_free_class_callback_t cb_free_class;
+  lookup_free_obj_callback_t cb_free_obj;
+};
+
+struct user_obj_s;
+typedef struct user_obj_s user_obj_t;
+struct user_obj_s
+{
+  void *user_obj;
+  identifier_t ident;
+
+  user_obj_t *next;
+};
+
+struct user_class_s
+{
+  pthread_mutex_t lock;
+  void *user_class;
+  identifier_match_t match;
+  user_obj_t *user_obj_list; /* list of user_obj */
+};
+typedef struct user_class_s user_class_t;
+
+struct user_class_list_s;
+typedef struct user_class_list_s user_class_list_t;
+struct user_class_list_s
+{
+  user_class_t entry;
+  user_class_list_t *next;
+};
+
+struct by_type_entry_s
+{
+  c_avl_tree_t *by_plugin_tree; /* plugin -> user_class_list_t */
+  user_class_list_t *wildcard_plugin_list;
+};
+typedef struct by_type_entry_s by_type_entry_t;
+
+/*
+ * Private functions
+ */
+static _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 */
+
+/* user_class->lock must be held when calling this function */
+static void *lu_create_user_obj (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl,
+    user_class_t *user_class)
+{
+  user_obj_t *user_obj;
+
+  user_obj = malloc (sizeof (*user_obj));
+  if (user_obj == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    return (NULL);
+  }
+  memset (user_obj, 0, sizeof (*user_obj));
+  user_obj->next = NULL;
+
+  user_obj->user_obj = obj->cb_user_class (ds, vl, user_class->user_class);
+  if (user_obj->user_obj == NULL)
+  {
+    sfree (user_obj);
+    WARNING("utils_vl_lookup: User-provided constructor failed.");
+    return (NULL);
+  }
+
+#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)
+  {
+    user_class->user_obj_list = user_obj;
+  }
+  else
+  {
+    user_obj_t *last = user_class->user_obj_list;
+    while (last->next != NULL)
+      last = last->next;
+    last->next = user_obj;
+  }
+
+  return (user_obj);
+} /* }}} void *lu_create_user_obj */
+
+/* user_class->lock must be held when calling this function */
+static user_obj_t *lu_find_user_obj (user_class_t *user_class, /* {{{ */
+    value_list_t const *vl)
+{
+  user_obj_t *ptr;
+
+  for (ptr = user_class->user_obj_list;
+      ptr != NULL;
+      ptr = ptr->next)
+  {
+    if (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 (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 (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);
+  }
+
+  return (NULL);
+} /* }}} user_obj_t *lu_find_user_obj */
+
+static int lu_handle_user_class (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl,
+    user_class_t *user_class)
+{
+  user_obj_t *user_obj;
+  int status;
+
+  assert (strcmp (vl->type, user_class->match.type.str) == 0);
+  assert (user_class->match.plugin.is_regex
+      || (strcmp (vl->plugin, user_class->match.plugin.str)) == 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);
+
+  pthread_mutex_lock (&user_class->lock);
+  user_obj = lu_find_user_obj (user_class, vl);
+  if (user_obj == NULL)
+  {
+    /* call lookup_class_callback_t() and insert into the list of user objects. */
+    user_obj = lu_create_user_obj (obj, ds, vl, user_class);
+    pthread_mutex_unlock (&user_class->lock);
+    if (user_obj == NULL)
+      return (-1);
+  }
+  pthread_mutex_unlock (&user_class->lock);
+
+  status = obj->cb_user_obj (ds, vl,
+      user_class->user_class, user_obj->user_obj);
+  if (status != 0)
+  {
+    ERROR ("utils_vl_lookup: The user object callback failed with status %i.",
+        status);
+    /* Returning a negative value means: abort! */
+    if (status < 0)
+      return (status);
+    else
+      return (1);
+  }
+
+  return (0);
+} /* }}} int lu_handle_user_class */
+
+static int lu_handle_user_class_list (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl,
+    user_class_list_t *user_class_list)
+{
+  user_class_list_t *ptr;
+  int retval = 0;
+  
+  for (ptr = user_class_list; ptr != NULL; ptr = ptr->next)
+  {
+    int status;
+
+    status = lu_handle_user_class (obj, ds, vl, &ptr->entry);
+    if (status < 0)
+      return (status);
+    else if (status == 0)
+      retval++;
+  }
+
+  return (retval);
+} /* }}} int lu_handle_user_class_list */
+
+static by_type_entry_t *lu_search_by_type (lookup_t *obj, /* {{{ */
+    char const *type, _Bool allocate_if_missing)
+{
+  by_type_entry_t *by_type;
+  char *type_copy;
+  int status;
+
+  status = c_avl_get (obj->by_type_tree, type, (void *) &by_type);
+  if (status == 0)
+    return (by_type);
+
+  if (!allocate_if_missing)
+    return (NULL);
+
+  type_copy = strdup (type);
+  if (type_copy == NULL)
+  {
+    ERROR ("utils_vl_lookup: strdup failed.");
+    return (NULL);
+  }
+
+  by_type = malloc (sizeof (*by_type));
+  if (by_type == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    sfree (type_copy);
+    return (NULL);
+  }
+  memset (by_type, 0, sizeof (*by_type));
+  by_type->wildcard_plugin_list = NULL;
+  
+  by_type->by_plugin_tree = c_avl_create ((void *) strcmp);
+  if (by_type->by_plugin_tree == NULL)
+  {
+    ERROR ("utils_vl_lookup: c_avl_create failed.");
+    sfree (by_type);
+    sfree (type_copy);
+    return (NULL);
+  }
+
+  status = c_avl_insert (obj->by_type_tree,
+      /* key = */ type_copy, /* value = */ by_type);
+  assert (status <= 0); /* >0 => entry exists => race condition. */
+  if (status != 0)
+  {
+    ERROR ("utils_vl_lookup: c_avl_insert failed.");
+    c_avl_destroy (by_type->by_plugin_tree);
+    sfree (by_type);
+    sfree (type_copy);
+    return (NULL);
+  }
+  
+  return (by_type);
+} /* }}} by_type_entry_t *lu_search_by_type */
+
+static int lu_add_by_plugin (by_type_entry_t *by_type, /* {{{ */
+    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 block returns immediately. Otherwise they will
+   * set "ptr" to non-NULL. */
+  if (match->plugin.is_regex)
+  {
+    if (by_type->wildcard_plugin_list == NULL)
+    {
+      by_type->wildcard_plugin_list = user_class_list;
+      return (0);
+    }
+
+    ptr = by_type->wildcard_plugin_list;
+  } /* if (plugin is wildcard) */
+  else /* (plugin is not wildcard) */
+  {
+    int status;
+
+    status = c_avl_get (by_type->by_plugin_tree,
+        match->plugin.str, (void *) &ptr);
+
+    if (status != 0) /* plugin not yet in tree */
+    {
+      char *plugin_copy = strdup (match->plugin.str);
+
+      if (plugin_copy == NULL)
+      {
+        ERROR ("utils_vl_lookup: strdup failed.");
+        sfree (user_class_list);
+        return (ENOMEM);
+      }
+
+      status = c_avl_insert (by_type->by_plugin_tree,
+          plugin_copy, user_class_list);
+      if (status != 0)
+      {
+        ERROR ("utils_vl_lookup: c_avl_insert(\"%s\") failed with status %i.",
+            plugin_copy, status);
+        sfree (plugin_copy);
+        sfree (user_class_list);
+        return (status);
+      }
+      else
+      {
+        return (0);
+      }
+    } /* if (plugin not yet in tree) */
+  } /* if (plugin is not wildcard) */
+
+  assert (ptr != NULL);
+
+  while (ptr->next != NULL)
+    ptr = ptr->next;
+  ptr->next = user_class_list;
+
+  return (0);
+} /* }}} int lu_add_by_plugin */
+
+static void lu_destroy_user_obj (lookup_t *obj, /* {{{ */
+    user_obj_t *user_obj)
+{
+  while (user_obj != NULL)
+  {
+    user_obj_t *next = user_obj->next;
+
+    if (obj->cb_free_obj != NULL)
+      obj->cb_free_obj (user_obj->user_obj);
+    user_obj->user_obj = NULL;
+
+    sfree (user_obj);
+    user_obj = next;
+  }
+} /* }}} void lu_destroy_user_obj */
+
+static void lu_destroy_user_class_list (lookup_t *obj, /* {{{ */
+    user_class_list_t *user_class_list)
+{
+  while (user_class_list != NULL)
+  {
+    user_class_list_t *next = user_class_list->next;
+
+    if (obj->cb_free_class != NULL)
+      obj->cb_free_class (user_class_list->entry.user_class);
+    user_class_list->entry.user_class = NULL;
+
+    lu_destroy_user_obj (obj, user_class_list->entry.user_obj_list);
+    user_class_list->entry.user_obj_list = NULL;
+    pthread_mutex_destroy (&user_class_list->entry.lock);
+
+    sfree (user_class_list);
+    user_class_list = next;
+  }
+} /* }}} void lu_destroy_user_class_list */
+
+static void lu_destroy_by_type (lookup_t *obj, /* {{{ */
+    by_type_entry_t *by_type)
+{
+  
+  while (42)
+  {
+    char *plugin = NULL;
+    user_class_list_t *user_class_list = NULL;
+    int status;
+
+    status = c_avl_pick (by_type->by_plugin_tree,
+        (void *) &plugin, (void *) &user_class_list);
+    if (status != 0)
+      break;
+
+    DEBUG ("utils_vl_lookup: lu_destroy_by_type: Destroying plugin \"%s\".",
+        plugin);
+    sfree (plugin);
+    lu_destroy_user_class_list (obj, user_class_list);
+  }
+
+  c_avl_destroy (by_type->by_plugin_tree);
+  by_type->by_plugin_tree = NULL;
+
+  lu_destroy_user_class_list (obj, by_type->wildcard_plugin_list);
+  by_type->wildcard_plugin_list = NULL;
+
+  sfree (by_type);
+} /* }}} int lu_destroy_by_type */
+
+/*
+ * Public functions
+ */
+lookup_t *lookup_create (lookup_class_callback_t cb_user_class, /* {{{ */
+    lookup_obj_callback_t cb_user_obj,
+    lookup_free_class_callback_t cb_free_class,
+    lookup_free_obj_callback_t cb_free_obj)
+{
+  lookup_t *obj = malloc (sizeof (*obj));
+  if (obj == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    return (NULL);
+  }
+  memset (obj, 0, sizeof (*obj));
+
+  obj->by_type_tree = c_avl_create ((void *) strcmp);
+  if (obj->by_type_tree == NULL)
+  {
+    ERROR ("utils_vl_lookup: c_avl_create failed.");
+    sfree (obj);
+    return (NULL);
+  }
+
+  obj->cb_user_class = cb_user_class;
+  obj->cb_user_obj = cb_user_obj;
+  obj->cb_free_class = cb_free_class;
+  obj->cb_free_obj = cb_free_obj;
+
+  return (obj);
+} /* }}} lookup_t *lookup_create */
+
+void lookup_destroy (lookup_t *obj) /* {{{ */
+{
+  int status;
+
+  if (obj == NULL)
+    return;
+
+  while (42)
+  {
+    char *type = NULL;
+    by_type_entry_t *by_type = NULL;
+
+    status = c_avl_pick (obj->by_type_tree, (void *) &type, (void *) &by_type);
+    if (status != 0)
+      break;
+
+    DEBUG ("utils_vl_lookup: lookup_destroy: Destroying type \"%s\".", type);
+    sfree (type);
+    lu_destroy_by_type (obj, by_type);
+  }
+
+  c_avl_destroy (obj->by_type_tree);
+  obj->by_type_tree = NULL;
+
+  sfree (obj);
+} /* }}} void lookup_destroy */
+
+int lookup_add (lookup_t *obj, /* {{{ */
+    identifier_t const *ident, unsigned int group_by, void *user_class)
+{
+  by_type_entry_t *by_type = NULL;
+  user_class_list_t *user_class_obj;
+
+  by_type = lu_search_by_type (obj, ident->type, /* allocate = */ 1);
+  if (by_type == NULL)
+    return (-1);
+
+  user_class_obj = malloc (sizeof (*user_class_obj));
+  if (user_class_obj == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    return (ENOMEM);
+  }
+  memset (user_class_obj, 0, sizeof (*user_class_obj));
+  pthread_mutex_init (&user_class_obj->entry.lock, /* attr = */ NULL);
+  user_class_obj->entry.user_class = user_class;
+  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, user_class_obj));
+} /* }}} int lookup_add */
+
+/* returns the number of successful calls to the callback function */
+int lookup_search (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl)
+{
+  by_type_entry_t *by_type = NULL;
+  user_class_list_t *user_class_list = NULL;
+  int retval = 0;
+  int status;
+
+  if ((obj == NULL) || (ds == NULL) || (vl == NULL))
+    return (-EINVAL);
+
+  by_type = lu_search_by_type (obj, vl->type, /* allocate = */ 0);
+  if (by_type == NULL)
+    return (0);
+
+  status = c_avl_get (by_type->by_plugin_tree,
+      vl->plugin, (void *) &user_class_list);
+  if (status == 0)
+  {
+    status = lu_handle_user_class_list (obj, ds, vl, user_class_list);
+    if (status < 0)
+      return (status);
+    retval += status;
+  }
+
+  if (by_type->wildcard_plugin_list != NULL)
+  {
+    status = lu_handle_user_class_list (obj, ds, vl,
+        by_type->wildcard_plugin_list);
+    if (status < 0)
+      return (status);
+    retval += status;
+  }
+    
+  return (retval);
+} /* }}} lookup_search */
diff --git a/src/utils_vl_lookup.h b/src/utils_vl_lookup.h
new file mode 100644 (file)
index 0000000..31787f5
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * collectd - src/utils_vl_lookup.h
+ * Copyright (C) 2012  Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_VL_LOOKUP_H
+#define UTILS_VL_LOOKUP_H 1
+
+#include "plugin.h"
+
+/*
+ * Types
+ */
+struct lookup_s;
+typedef struct lookup_s lookup_t;
+
+/* Given a user_class, constructs a new user_obj. */
+typedef void *(*lookup_class_callback_t) (data_set_t const *ds,
+    value_list_t const *vl, void *user_class);
+
+/* Given a user_class and a ds/vl combination, does stuff with the data.
+ * This is the main working horse of the module. */
+typedef int (*lookup_obj_callback_t) (data_set_t const *ds,
+    value_list_t const *vl,
+    void *user_class, void *user_obj);
+
+/* Used to free user_class pointers. May be NULL in which case nothing is
+ * freed. */
+typedef void (*lookup_free_class_callback_t) (void *user_class);
+
+/* Used to free user_obj pointers. May be NULL in which case nothing is
+ * freed. */
+typedef void (*lookup_free_obj_callback_t) (void *user_obj);
+
+struct identifier_s
+{
+  char host[DATA_MAX_NAME_LEN];
+  char plugin[DATA_MAX_NAME_LEN];
+  char plugin_instance[DATA_MAX_NAME_LEN];
+  char type[DATA_MAX_NAME_LEN];
+  char type_instance[DATA_MAX_NAME_LEN];
+};
+typedef struct identifier_s identifier_t;
+
+#define LU_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
+ */
+__attribute__((nonnull(1,2)))
+lookup_t *lookup_create (lookup_class_callback_t,
+    lookup_obj_callback_t,
+    lookup_free_class_callback_t,
+    lookup_free_obj_callback_t);
+void lookup_destroy (lookup_t *obj);
+
+int lookup_add (lookup_t *obj,
+    identifier_t const *ident, unsigned int group_by, void *user_class);
+
+/* TODO(octo): Pass lookup_obj_callback_t to lookup_search()? */
+int lookup_search (lookup_t *obj,
+    data_set_t const *ds, value_list_t const *vl);
+
+#endif /* UTILS_VL_LOOKUP_H */
diff --git a/src/utils_vl_lookup_test.c b/src/utils_vl_lookup_test.c
new file mode 100644 (file)
index 0000000..bbb3a67
--- /dev/null
@@ -0,0 +1,244 @@
+/**
+ * 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,
+    unsigned int group_by)
+{
+  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, group_by, 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, "/.*/", "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);
+}
+
+static void testcase1 (void)
+{
+  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);
+}
+
+static void testcase2 (void)
+{
+  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);
+}
+
+static void testcase3 (void)
+{
+  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);
+}
+
+int main (int argc, char **argv) /* {{{ */
+{
+  testcase0 ();
+  testcase1 ();
+  testcase2 ();
+  testcase3 ();
+  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);
 }
 
 /*
diff --git a/src/varnish.c b/src/varnish.c
new file mode 100644 (file)
index 0000000..5de3389
--- /dev/null
@@ -0,0 +1,678 @@
+/**
+ * collectd - src/varnish.c
+ * 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
+ * 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:
+ *   Jérôme Renard <jerome.renard at gmail.com>
+ *   Marc Fournier <marc.fournier at camptocamp.com>
+ *   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"
+#include "configfile.h"
+
+#include <varnish/varnishapi.h>
+
+#if HAVE_VARNISH_V3
+# include <varnish/vsc.h>
+typedef struct VSC_C_main c_varnish_stats_t;
+#endif
+
+#if HAVE_VARNISH_V2
+typedef struct varnish_stats c_varnish_stats_t;
+#endif
+
+/* {{{ user_config_s */
+struct user_config_s {
+       char *instance;
+
+       _Bool collect_cache;
+       _Bool collect_connections;
+       _Bool collect_esi;
+       _Bool collect_backend;
+       _Bool collect_fetch;
+       _Bool collect_hcb;
+       _Bool collect_shm;
+       _Bool collect_sms;
+#if HAVE_VARNISH_V2
+       _Bool collect_sm;
+       _Bool collect_sma;
+#endif
+       _Bool collect_totals;
+       _Bool collect_workers;
+};
+typedef struct user_config_s user_config_t; /* }}} */
+
+static _Bool have_instance = 0;
+
+static int varnish_submit (const char *plugin_instance, /* {{{ */
+               const char *category, const char *type, const char *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, "varnish", sizeof (vl.plugin));
+
+       if (plugin_instance == NULL)
+               plugin_instance = "default";
+
+       ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+               "%s-%s", plugin_instance, category);
+
+       sstrncpy (vl.type, type, sizeof (vl.type));
+
+       if (type_instance != NULL)
+               sstrncpy (vl.type_instance, type_instance,
+                               sizeof (vl.type_instance));
+
+       return (plugin_dispatch_values (&vl));
+} /* }}} int varnish_submit */
+
+static int varnish_submit_gauge (const char *plugin_instance, /* {{{ */
+               const char *category, const char *type, const char *type_instance,
+               uint64_t gauge_value)
+{
+       value_t value;
+
+       value.gauge = (gauge_t) gauge_value;
+
+       return (varnish_submit (plugin_instance, category, type, type_instance, value));
+} /* }}} int varnish_submit_gauge */
+
+static int varnish_submit_derive (const char *plugin_instance, /* {{{ */
+               const char *category, const char *type, const char *type_instance,
+               uint64_t derive_value)
+{
+       value_t value;
+
+       value.derive = (derive_t) derive_value;
+
+       return (varnish_submit (plugin_instance, category, type, type_instance, value));
+} /* }}} int varnish_submit_derive */
+
+static void varnish_monitor (const user_config_t *conf, /* {{{ */
+               const c_varnish_stats_t *stats)
+{
+       if (conf->collect_cache)
+       {
+               /* Cache hits */
+               varnish_submit_derive (conf->instance, "cache", "cache_result", "hit",     stats->cache_hit);
+               /* Cache misses */
+               varnish_submit_derive (conf->instance, "cache", "cache_result", "miss",    stats->cache_miss);
+               /* Cache hits for pass */
+               varnish_submit_derive (conf->instance, "cache", "cache_result", "hitpass", stats->cache_hitpass);
+       }
+
+       if (conf->collect_connections)
+       {
+               /* Client connections accepted */
+               varnish_submit_derive (conf->instance, "connections", "connections", "accepted", stats->client_conn);
+               /* Connection dropped, no sess */
+               varnish_submit_derive (conf->instance, "connections", "connections", "dropped" , stats->client_drop);
+               /* Client requests received    */
+               varnish_submit_derive (conf->instance, "connections", "connections", "received", stats->client_req);
+       }
+
+       if (conf->collect_esi)
+       {
+               /* ESI parse errors (unlock)   */
+               varnish_submit_derive (conf->instance, "esi", "total_operations", "error",  stats->esi_errors);
+#if HAVE_VARNISH_V2
+               /* Objects ESI parsed (unlock) */
+               varnish_submit_derive (conf->instance, "esi", "total_operations", "parsed", stats->esi_parse);
+#endif
+       }
+
+       if (conf->collect_backend)
+       {
+               /* Backend conn. success       */
+               varnish_submit_derive (conf->instance, "backend", "connections", "success"      , stats->backend_conn);
+               /* Backend conn. not attempted */
+               varnish_submit_derive (conf->instance, "backend", "connections", "not-attempted", stats->backend_unhealthy);
+               /* Backend conn. too many      */
+               varnish_submit_derive (conf->instance, "backend", "connections", "too-many"     , stats->backend_busy);
+               /* Backend conn. failures      */
+               varnish_submit_derive (conf->instance, "backend", "connections", "failures"     , stats->backend_fail);
+               /* Backend conn. reuses        */
+               varnish_submit_derive (conf->instance, "backend", "connections", "reuses"       , stats->backend_reuse);
+               /* Backend conn. was closed    */
+               varnish_submit_derive (conf->instance, "backend", "connections", "was-closed"   , stats->backend_toolate);
+               /* Backend conn. recycles      */
+               varnish_submit_derive (conf->instance, "backend", "connections", "recycled"     , stats->backend_recycle);
+#if HAVE_VARNISH_V2
+               /* Backend conn. unused        */
+               varnish_submit_derive (conf->instance, "backend", "connections", "unused"       , stats->backend_unused);
+#endif
+       }
+
+       if (conf->collect_fetch)
+       {
+               /* Fetch head                */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "head"       , stats->fetch_head);
+               /* Fetch with length         */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "length"     , stats->fetch_length);
+               /* Fetch chunked             */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "chunked"    , stats->fetch_chunked);
+               /* Fetch EOF                 */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "eof"        , stats->fetch_eof);
+               /* Fetch bad headers         */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "bad_headers", stats->fetch_bad);
+               /* Fetch wanted close        */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "close"      , stats->fetch_close);
+               /* Fetch pre HTTP/1.1 closed */
+               varnish_submit_derive (conf->instance, "fetch", "http_requests", "oldhttp"    , stats->fetch_oldhttp);
+               /* Fetch zero len            */
+               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 (conf->collect_hcb)
+       {
+               /* HCB Lookups without lock */
+               varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_nolock", stats->hcb_nolock);
+               /* HCB Lookups with lock    */
+               varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_lock",   stats->hcb_lock);
+               /* HCB Inserts              */
+               varnish_submit_derive (conf->instance, "hcb", "cache_operation", "insert",        stats->hcb_insert);
+       }
+
+       if (conf->collect_shm)
+       {
+               /* SHM records                 */
+               varnish_submit_derive (conf->instance, "shm", "total_operations", "records"   , stats->shm_records);
+               /* SHM writes                  */
+               varnish_submit_derive (conf->instance, "shm", "total_operations", "writes"    , stats->shm_writes);
+               /* SHM flushes due to overflow */
+               varnish_submit_derive (conf->instance, "shm", "total_operations", "flushes"   , stats->shm_flushes);
+               /* SHM MTX contention          */
+               varnish_submit_derive (conf->instance, "shm", "total_operations", "contention", stats->shm_cont);
+               /* SHM cycles through buffer   */
+               varnish_submit_derive (conf->instance, "shm", "total_operations", "cycles"    , stats->shm_cycles);
+       }
+
+#if HAVE_VARNISH_V2
+       if (conf->collect_sm)
+       {
+               /* allocator requests */
+               varnish_submit_derive (conf->instance, "sm", "total_requests", "nreq",         stats->sm_nreq);
+               /* outstanding allocations */
+               varnish_submit_gauge (conf->instance,  "sm", "requests", "outstanding",        stats->sm_nobj);
+               /* bytes allocated */
+               varnish_submit_derive (conf->instance,  "sm", "total_bytes", "allocated",      stats->sm_balloc);
+               /* bytes free */
+               varnish_submit_derive (conf->instance,  "sm", "total_bytes", "free",           stats->sm_bfree);
+       }
+
+       if (conf->collect_sma)
+       {
+               /* SMA allocator requests */
+               varnish_submit_derive (conf->instance, "sma", "total_requests", "nreq",    stats->sma_nreq);
+               /* SMA outstanding allocations */
+               varnish_submit_gauge (conf->instance,  "sma", "requests", "outstanding",   stats->sma_nobj);
+               /* SMA outstanding bytes */
+               varnish_submit_gauge (conf->instance,  "sma", "bytes", "outstanding",      stats->sma_nbytes);
+               /* SMA bytes allocated */
+               varnish_submit_derive (conf->instance,  "sma", "total_bytes", "allocated", stats->sma_balloc);
+               /* SMA bytes free */
+               varnish_submit_derive (conf->instance,  "sma", "total_bytes", "free" ,     stats->sma_bfree);
+       }
+#endif
+
+       if (conf->collect_sms)
+       {
+               /* SMS allocator requests */
+               varnish_submit_derive (conf->instance, "sms", "total_requests", "allocator", stats->sms_nreq);
+               /* SMS outstanding allocations */
+               varnish_submit_gauge (conf->instance,  "sms", "requests", "outstanding",     stats->sms_nobj);
+               /* SMS outstanding bytes */
+               varnish_submit_gauge (conf->instance,  "sms", "bytes", "outstanding",        stats->sms_nbytes);
+               /* SMS bytes allocated */
+               varnish_submit_derive (conf->instance,  "sms", "total_bytes", "allocated",   stats->sms_balloc);
+               /* SMS bytes freed */
+               varnish_submit_derive (conf->instance,  "sms", "total_bytes", "free",        stats->sms_bfree);
+       }
+
+       if (conf->collect_totals)
+       {
+               /* Total Sessions */
+               varnish_submit_derive (conf->instance, "totals", "total_sessions", "sessions",  stats->s_sess);
+               /* Total Requests */
+               varnish_submit_derive (conf->instance, "totals", "total_requests", "requests",  stats->s_req);
+               /* Total pipe */
+               varnish_submit_derive (conf->instance, "totals", "total_operations", "pipe",    stats->s_pipe);
+               /* Total pass */
+               varnish_submit_derive (conf->instance, "totals", "total_operations", "pass",    stats->s_pass);
+               /* Total fetch */
+               varnish_submit_derive (conf->instance, "totals", "total_operations", "fetches", stats->s_fetch);
+               /* Total header bytes */
+               varnish_submit_derive (conf->instance, "totals", "total_bytes", "header-bytes", stats->s_hdrbytes);
+               /* Total body byte */
+               varnish_submit_derive (conf->instance, "totals", "total_bytes", "body-bytes",   stats->s_bodybytes);
+       }
+
+       if (conf->collect_workers)
+       {
+               /* worker threads */
+               varnish_submit_gauge (conf->instance, "workers", "threads", "worker",             stats->n_wrk);
+               /* worker threads created */
+               varnish_submit_derive (conf->instance, "workers", "total_threads", "created",     stats->n_wrk_create);
+               /* worker threads not created */
+               varnish_submit_derive (conf->instance, "workers", "total_threads", "failed",      stats->n_wrk_failed);
+               /* worker threads limited */
+               varnish_submit_derive (conf->instance, "workers", "total_threads", "limited",     stats->n_wrk_max);
+               /* dropped work requests */
+               varnish_submit_derive (conf->instance, "workers", "total_requests", "dropped",    stats->n_wrk_drop);
+#ifdef HAVE_VARNISH_V2
+               /* queued work requests */
+               varnish_submit_derive (conf->instance, "workers", "total_requests", "queued",     stats->n_wrk_queue);
+               /* overflowed work requests */
+               varnish_submit_derive (conf->instance, "workers", "total_requests", "overflowed", stats->n_wrk_overflow);
+#endif
+       }
+} /* }}} void varnish_monitor */
+
+#if HAVE_VARNISH_V3
+static int varnish_read (user_data_t *ud) /* {{{ */
+{
+       struct VSM_data *vd;
+       const c_varnish_stats_t *stats;
+
+       user_config_t *conf;
+
+       if ((ud == NULL) || (ud->data == NULL))
+               return (EINVAL);
+
+       conf = ud->data;
+
+       vd = VSM_New();
+       VSC_Setup(vd);
+
+       if (conf->instance != NULL)
+       {
+               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.");
+
+               return (-1);
+       }
+
+       stats = VSC_Main(vd);
+
+       varnish_monitor (conf, stats);
+       VSM_Close (vd);
+
+       return (0);
+} /* }}} */
+#else /* if HAVE_VARNISH_V2 */
+static int varnish_read (user_data_t *ud) /* {{{ */
+{
+       const c_varnish_stats_t *stats;
+
+       user_config_t *conf;
+
+       if ((ud == NULL) || (ud->data == NULL))
+               return (EINVAL);
+
+       conf = ud->data;
+
+       stats = VSL_OpenStats (conf->instance);
+       if (stats == NULL)
+       {
+               ERROR ("Varnish plugin : unable to load statistics");
+
+               return (-1);
+       }
+
+       varnish_monitor (conf, stats);
+
+       return (0);
+} /* }}} */
+#endif
+
+static void varnish_config_free (void *ptr) /* {{{ */
+{
+       user_config_t *conf = ptr;
+
+       if (conf == NULL)
+               return;
+
+       sfree (conf->instance);
+       sfree (conf);
+} /* }}} */
+
+static int varnish_config_apply_default (user_config_t *conf) /* {{{ */
+{
+       if (conf == NULL)
+               return (EINVAL);
+
+       conf->collect_backend     = 1;
+       conf->collect_cache       = 1;
+       conf->collect_connections = 1;
+       conf->collect_esi         = 0;
+       conf->collect_fetch       = 0;
+       conf->collect_hcb         = 0;
+       conf->collect_shm         = 1;
+#if HAVE_VARNISH_V2
+       conf->collect_sm          = 0;
+       conf->collect_sma         = 0;
+#endif
+       conf->collect_sms         = 0;
+       conf->collect_totals      = 0;
+
+       return (0);
+} /* }}} int varnish_config_apply_default */
+
+static int varnish_init (void) /* {{{ */
+{
+       user_config_t *conf;
+       user_data_t ud;
+
+       if (have_instance)
+               return (0);
+
+       conf = malloc (sizeof (*conf));
+       if (conf == NULL)
+               return (ENOMEM);
+       memset (conf, 0, sizeof (*conf));
+
+       /* Default settings: */
+       conf->instance = NULL;
+
+       varnish_config_apply_default (conf);
+
+       ud.data = conf;
+       ud.free_func = varnish_config_free;
+
+       plugin_register_complex_read (/* group = */ "varnish",
+                       /* name      = */ "varnish/localhost",
+                       /* callback  = */ varnish_read,
+                       /* interval  = */ NULL,
+                       /* user data = */ &ud);
+
+       return (0);
+} /* }}} int varnish_init */
+
+static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
+{
+       user_config_t *conf;
+       user_data_t ud;
+       char callback_name[DATA_MAX_NAME_LEN];
+       int i;
+
+       conf = malloc (sizeof (*conf));
+       if (conf == NULL)
+               return (ENOMEM);
+       memset (conf, 0, sizeof (*conf));
+       conf->instance = NULL;
+
+       varnish_config_apply_default (conf);
+
+       if (ci->values_num == 1)
+       {
+               int status;
+
+               status = cf_util_get_string (ci, &conf->instance);
+               if (status != 0)
+               {
+                       sfree (conf);
+                       return (status);
+               }
+               assert (conf->instance != NULL);
+
+               if (strcmp ("localhost", conf->instance) == 0)
+               {
+                       sfree (conf->instance);
+                       conf->instance = NULL;
+               }
+       }
+       else if (ci->values_num > 1)
+       {
+               WARNING ("Varnish plugin: \"Instance\" blocks accept only "
+                               "one argument.");
+               return (EINVAL);
+       }
+
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if (strcasecmp ("CollectCache", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_cache);
+               else if (strcasecmp ("CollectConnections", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_connections);
+               else if (strcasecmp ("CollectESI", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_esi);
+               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 ("CollectSHM", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_shm);
+               else if (strcasecmp ("CollectSMS", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_sms);
+#if HAVE_VARNISH_V2
+               else if (strcasecmp ("CollectSMA", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_sma);
+               else if (strcasecmp ("CollectSM", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_sm);
+#endif
+               else if (strcasecmp ("CollectTotals", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_totals);
+               else if (strcasecmp ("CollectWorkers", child->key) == 0)
+                       cf_util_get_boolean (child, &conf->collect_workers);
+               else
+               {
+                       WARNING ("Varnish plugin: Ignoring unknown "
+                                       "configuration option: \"%s\". Did "
+                                       "you forget to add an <Instance /> "
+                                       "block around the configuration?",
+                                       child->key);
+               }
+       }
+
+       if (!conf->collect_cache
+                       && !conf->collect_connections
+                       && !conf->collect_esi
+                       && !conf->collect_backend
+                       && !conf->collect_fetch
+                       && !conf->collect_hcb
+                       && !conf->collect_shm
+                       && !conf->collect_sms
+#if HAVE_VARNISH_V2
+                       && !conf->collect_sma
+                       && !conf->collect_sm
+#endif
+                       && !conf->collect_totals
+                       && !conf->collect_workers)
+       {
+               WARNING ("Varnish plugin: No metric has been configured for "
+                               "instance \"%s\". Disabling this instance.",
+                               (conf->instance == NULL) ? "localhost" : conf->instance);
+               return (EINVAL);
+       }
+
+       ssnprintf (callback_name, sizeof (callback_name), "varnish/%s",
+                       (conf->instance == NULL) ? "localhost" : conf->instance);
+
+       ud.data = conf;
+       ud.free_func = varnish_config_free;
+
+       plugin_register_complex_read (/* group = */ "varnish",
+                       /* name      = */ callback_name,
+                       /* callback  = */ varnish_read,
+                       /* interval  = */ NULL,
+                       /* user data = */ &ud);
+
+       have_instance = 1;
+
+       return (0);
+} /* }}} int varnish_config_instance */
+
+static int varnish_config (oconfig_item_t *ci) /* {{{ */
+{
+       int i;
+
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if (strcasecmp ("Instance", child->key) == 0)
+                       varnish_config_instance (child);
+               else
+               {
+                       WARNING ("Varnish plugin: Ignoring unknown "
+                                       "configuration option: \"%s\"",
+                                       child->key);
+               }
+       }
+
+       return (0);
+} /* }}} int varnish_config */
+
+void module_register (void) /* {{{ */
+{
+       plugin_register_complex_config ("varnish", varnish_config);
+       plugin_register_init ("varnish", varnish_init);
+} /* }}} */
+
+/* vim: set sw=8 noet fdm=marker : */
index d32f1db..56997bf 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/vmem.c
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008-2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -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"
@@ -57,12 +57,12 @@ static void submit (const char *plugin_instance, const char *type,
 } /* void vmem_submit */
 
 static void submit_two (const char *plugin_instance, const char *type,
-    const char *type_instance, counter_t c0, counter_t c1)
+    const char *type_instance, derive_t c0, derive_t c1)
 {
   value_t values[2];
 
-  values[0].counter = c0;
-  values[1].counter = c1;
+  values[0].derive = c0;
+  values[1].derive = c1;
 
   submit (plugin_instance, type, type_instance, values, 2);
 } /* void submit_one */
@@ -93,16 +93,16 @@ static int vmem_config (const char *key, const char *value)
 static int vmem_read (void)
 {
 #if KERNEL_LINUX
-  counter_t pgpgin = 0;
-  counter_t pgpgout = 0;
+  derive_t pgpgin = 0;
+  derive_t pgpgout = 0;
   int pgpgvalid = 0;
 
-  counter_t pswpin = 0;
-  counter_t pswpout = 0;
+  derive_t pswpin = 0;
+  derive_t pswpout = 0;
   int pswpvalid = 0;
 
-  counter_t pgfault = 0;
-  counter_t pgmajfault = 0;
+  derive_t pgfault = 0;
+  derive_t pgmajfault = 0;
   int pgfaultvalid = 0;
 
   FILE *fh;
@@ -123,7 +123,7 @@ static int vmem_read (void)
     int fields_num;
     char *key;
     char *endptr;
-    counter_t counter;
+    derive_t counter;
     gauge_t gauge;
 
     fields_num = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
@@ -205,31 +205,31 @@ static int vmem_read (void)
     else if (strncmp ("pgalloc_", key, strlen ("pgalloc_")) == 0)
     {
       char *inst = key + strlen ("pgalloc_");
-      value_t value  = { .counter = counter };
+      value_t value  = { .derive = counter };
       submit_one (inst, "vmpage_action", "alloc", value);
     }
     else if (strncmp ("pgrefill_", key, strlen ("pgrefill_")) == 0)
     {
       char *inst = key + strlen ("pgrefill_");
-      value_t value  = { .counter = counter };
+      value_t value  = { .derive = counter };
       submit_one (inst, "vmpage_action", "refill", value);
     }
     else if (strncmp ("pgsteal_", key, strlen ("pgsteal_")) == 0)
     {
       char *inst = key + strlen ("pgsteal_");
-      value_t value  = { .counter = counter };
+      value_t value  = { .derive = counter };
       submit_one (inst, "vmpage_action", "steal", value);
     }
     else if (strncmp ("pgscan_kswapd_", key, strlen ("pgscan_kswapd_")) == 0)
     {
       char *inst = key + strlen ("pgscan_kswapd_");
-      value_t value  = { .counter = counter };
+      value_t value  = { .derive = counter };
       submit_one (inst, "vmpage_action", "scan_kswapd", value);
     }
     else if (strncmp ("pgscan_direct_", key, strlen ("pgscan_direct_")) == 0)
     {
       char *inst = key + strlen ("pgscan_direct_");
-      value_t value  = { .counter = counter };
+      value_t value  = { .derive = counter };
       submit_one (inst, "vmpage_action", "scan_direct", value);
     }
 
@@ -241,17 +241,17 @@ static int vmem_read (void)
      */
     else if (strcmp ("pgfree", key) == 0)
     {
-      value_t value  = { .counter = counter };
+      value_t value  = { .derive = counter };
       submit_one (NULL, "vmpage_action", "free", value);
     }
     else if (strcmp ("pgactivate", key) == 0)
     {
-      value_t value  = { .counter = counter };
+      value_t value  = { .derive = counter };
       submit_one (NULL, "vmpage_action", "activate", value);
     }
     else if (strcmp ("pgdeactivate", key) == 0)
     {
-      value_t value  = { .counter = counter };
+      value_t value  = { .derive = counter };
       submit_one (NULL, "vmpage_action", "deactivate", value);
     }
   } /* while (fgets) */
index 1615ca0..d80717c 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/vserver.c
  * Copyright (C) 2006,2007  Sebastian Harl
- * Copyright (C) 2007,2008  Florian octo Forster
+ * Copyright (C) 2007-2010  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -48,13 +48,13 @@ static int vserver_init (void)
 } /* static void vserver_init(void) */
 
 static void traffic_submit (const char *plugin_instance,
-               const char *type_instance, counter_t rx, counter_t tx)
+               const char *type_instance, derive_t rx, derive_t tx)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].counter = rx;
-       values[1].counter = tx;
+       values[0].derive = rx;
+       values[1].derive = tx;
 
        vl.values = values;
        vl.values_len = STATIC_ARRAY_SIZE (values);
@@ -107,14 +107,21 @@ static void submit_gauge (const char *plugin_instance, const char *type,
        plugin_dispatch_values (&vl);
 } /* void submit_gauge */
 
-static inline long long __get_sock_bytes(const char *s)
+static derive_t vserver_get_sock_bytes(const char *s)
 {
+       value_t v;
+       int status;
+
        while (s[0] != '/')
                ++s;
 
        /* Remove '/' */
        ++s;
-       return atoll(s);
+
+       status = parse_value (s, &v, DS_TYPE_DERIVE);
+       if (status != 0)
+               return (-1);
+       return (v.derive);
 }
 
 static int vserver_read (void)
@@ -201,8 +208,8 @@ static int vserver_read (void)
 
                while ((fh != NULL) && (NULL != fgets (buffer, BUFSIZE, fh)))
                {
-                       counter_t rx;
-                       counter_t tx;
+                       derive_t rx;
+                       derive_t tx;
                        char *type_instance;
 
                        if (strsplit (buffer, cols, 4) < 4)
@@ -221,8 +228,8 @@ static int vserver_read (void)
                        else
                                continue;
 
-                       rx = __get_sock_bytes (cols[1]);
-                       tx = __get_sock_bytes (cols[2]);
+                       rx = vserver_get_sock_bytes (cols[1]);
+                       tx = vserver_get_sock_bytes (cols[2]);
                        /* cols[3] == errors */
 
                        traffic_submit (dent->d_name, type_instance, rx, tx);
diff --git a/src/write_graphite.c b/src/write_graphite.c
new file mode 100644 (file)
index 0000000..392d570
--- /dev/null
@@ -0,0 +1,551 @@
+/**
+ * collectd - src/write_graphite.c
+ * Copyright (C) 2012       Pierre-Yves Ritschard
+ * Copyright (C) 2011       Scott Sanders
+ * Copyright (C) 2009       Paul Sadauskas
+ * Copyright (C) 2009       Doug MacEachern
+ * Copyright (C) 2007-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
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ *   Doug MacEachern <dougm at hyperic.com>
+ *   Paul Sadauskas <psadauskas at gmail.com>
+ *   Scott Sanders <scott at jssjr.com>
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ *
+ * Based on the write_http plugin.
+ **/
+
+ /* write_graphite plugin configuation example
+  *
+  * <Plugin write_graphite>
+  *   <Carbon>
+  *     Host "localhost"
+  *     Port "2003"
+  *     Prefix "collectd"
+  *   </Carbon>
+  * </Plugin>
+  */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include "utils_cache.h"
+#include "utils_complain.h"
+#include "utils_parse_option.h"
+#include "utils_format_graphite.h"
+
+/* Folks without pthread will need to disable this plugin. */
+#include <pthread.h>
+
+#include <sys/socket.h>
+#include <netdb.h>
+
+#ifndef WG_DEFAULT_NODE
+# define WG_DEFAULT_NODE "localhost"
+#endif
+
+#ifndef WG_DEFAULT_SERVICE
+# define WG_DEFAULT_SERVICE "2003"
+#endif
+
+#ifndef WG_DEFAULT_ESCAPE
+# define WG_DEFAULT_ESCAPE '_'
+#endif
+
+/* Ethernet - (IPv6 + TCP) = 1500 - (40 + 32) = 1428 */
+#ifndef WG_SEND_BUF_SIZE
+# define WG_SEND_BUF_SIZE 1428
+#endif
+
+/*
+ * Private variables
+ */
+struct wg_callback
+{
+    int      sock_fd;
+
+    char    *name;
+
+    char    *node;
+    char    *service;
+    char    *prefix;
+    char    *postfix;
+    char     escape_char;
+
+    unsigned int format_flags;
+
+    char     send_buf[WG_SEND_BUF_SIZE];
+    size_t   send_buf_free;
+    size_t   send_buf_fill;
+    cdtime_t send_buf_init_time;
+
+    pthread_mutex_t send_lock;
+    c_complain_t init_complaint;
+};
+
+
+/*
+ * Functions
+ */
+static void wg_reset_buffer (struct wg_callback *cb)
+{
+    memset (cb->send_buf, 0, sizeof (cb->send_buf));
+    cb->send_buf_free = sizeof (cb->send_buf);
+    cb->send_buf_fill = 0;
+    cb->send_buf_init_time = cdtime ();
+}
+
+static int wg_send_buffer (struct wg_callback *cb)
+{
+    ssize_t status = 0;
+
+    status = swrite (cb->sock_fd, cb->send_buf, strlen (cb->send_buf));
+    if (status < 0)
+    {
+        char errbuf[1024];
+        ERROR ("write_graphite plugin: send failed with status %zi (%s)",
+                status, sstrerror (errno, errbuf, sizeof (errbuf)));
+
+
+        close (cb->sock_fd);
+        cb->sock_fd = -1;
+
+        return (-1);
+    }
+
+    return (0);
+}
+
+/* NOTE: You must hold cb->send_lock when calling this function! */
+static int wg_flush_nolock (cdtime_t timeout, struct wg_callback *cb)
+{
+    int status;
+
+    DEBUG ("write_graphite plugin: wg_flush_nolock: timeout = %.3f; "
+            "send_buf_fill = %zu;",
+            (double)timeout,
+            cb->send_buf_fill);
+
+    /* timeout == 0  => flush unconditionally */
+    if (timeout > 0)
+    {
+        cdtime_t now;
+
+        now = cdtime ();
+        if ((cb->send_buf_init_time + timeout) > now)
+            return (0);
+    }
+
+    if (cb->send_buf_fill <= 0)
+    {
+        cb->send_buf_init_time = cdtime ();
+        return (0);
+    }
+
+    status = wg_send_buffer (cb);
+    wg_reset_buffer (cb);
+
+    return (status);
+}
+
+static int wg_callback_init (struct wg_callback *cb)
+{
+    struct addrinfo ai_hints;
+    struct addrinfo *ai_list;
+    struct addrinfo *ai_ptr;
+    int status;
+
+    const char *node = cb->node ? cb->node : WG_DEFAULT_NODE;
+    const char *service = cb->service ? cb->service : WG_DEFAULT_SERVICE;
+
+    char connerr[1024] = "";
+
+    if (cb->sock_fd > 0)
+        return (0);
+
+    memset (&ai_hints, 0, sizeof (ai_hints));
+#ifdef AI_ADDRCONFIG
+    ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+    ai_hints.ai_family = AF_UNSPEC;
+    ai_hints.ai_socktype = SOCK_STREAM;
+
+    ai_list = NULL;
+
+    status = getaddrinfo (node, service, &ai_hints, &ai_list);
+    if (status != 0)
+    {
+        ERROR ("write_graphite plugin: getaddrinfo (%s, %s) failed: %s",
+                node, service, gai_strerror (status));
+        return (-1);
+    }
+
+    assert (ai_list != NULL);
+    for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+    {
+        cb->sock_fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
+                ai_ptr->ai_protocol);
+        if (cb->sock_fd < 0) {
+            char errbuf[1024];
+            snprintf (connerr, sizeof (connerr), "failed to open socket: %s",
+                    sstrerror (errno, errbuf, sizeof (errbuf)));
+            continue;
+        }
+
+        status = connect (cb->sock_fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+        if (status != 0)
+        {
+            char errbuf[1024];
+            snprintf (connerr, sizeof (connerr), "failed to connect to remote "
+                    "host: %s", sstrerror (errno, errbuf, sizeof (errbuf)));
+            close (cb->sock_fd);
+            cb->sock_fd = -1;
+            continue;
+        }
+
+        break;
+    }
+
+    freeaddrinfo (ai_list);
+
+    if (cb->sock_fd < 0)
+    {
+        if (connerr[0] == '\0')
+            /* this should not happen but try to get a message anyway */
+            sstrerror (errno, connerr, sizeof (connerr));
+        c_complain (LOG_ERR, &cb->init_complaint,
+                "write_graphite plugin: Connecting to %s:%s failed. "
+                "The last error was: %s", node, service, connerr);
+        return (-1);
+    }
+    else
+    {
+        c_release (LOG_INFO, &cb->init_complaint,
+                "write_graphite plugin: Successfully connected to %s:%s.",
+                node, service);
+    }
+
+    wg_reset_buffer (cb);
+
+    return (0);
+}
+
+static void wg_callback_free (void *data)
+{
+    struct wg_callback *cb;
+
+    if (data == NULL)
+        return;
+
+    cb = data;
+
+    pthread_mutex_lock (&cb->send_lock);
+
+    wg_flush_nolock (/* timeout = */ 0, cb);
+
+    if (cb->sock_fd >= 0)
+    {
+        close (cb->sock_fd);
+        cb->sock_fd = -1;
+    }
+
+    sfree(cb->name);
+    sfree(cb->node);
+    sfree(cb->service);
+    sfree(cb->prefix);
+    sfree(cb->postfix);
+
+    pthread_mutex_destroy (&cb->send_lock);
+
+    sfree(cb);
+}
+
+static int wg_flush (cdtime_t timeout,
+        const char *identifier __attribute__((unused)),
+        user_data_t *user_data)
+{
+    struct wg_callback *cb;
+    int status;
+
+    if (user_data == NULL)
+        return (-EINVAL);
+
+    cb = user_data->data;
+
+    pthread_mutex_lock (&cb->send_lock);
+
+    if (cb->sock_fd < 0)
+    {
+        status = wg_callback_init (cb);
+        if (status != 0)
+        {
+            /* An error message has already been printed. */
+            pthread_mutex_unlock (&cb->send_lock);
+            return (-1);
+        }
+    }
+
+    status = wg_flush_nolock (timeout, cb);
+    pthread_mutex_unlock (&cb->send_lock);
+
+    return (status);
+}
+
+static int wg_send_message (char const *message, struct wg_callback *cb)
+{
+    int status;
+    size_t message_len;
+
+    message_len = strlen (message);
+
+    pthread_mutex_lock (&cb->send_lock);
+
+    if (cb->sock_fd < 0)
+    {
+        status = wg_callback_init (cb);
+        if (status != 0)
+        {
+            /* An error message has already been printed. */
+            pthread_mutex_unlock (&cb->send_lock);
+            return (-1);
+        }
+    }
+
+    if (message_len >= cb->send_buf_free)
+    {
+        status = wg_flush_nolock (/* timeout = */ 0, cb);
+        if (status != 0)
+        {
+            pthread_mutex_unlock (&cb->send_lock);
+            return (status);
+        }
+    }
+
+    /* Assert that we have enough space for this message. */
+    assert (message_len < cb->send_buf_free);
+
+    /* `message_len + 1' because `message_len' does not include the
+     * trailing null byte. Neither does `send_buffer_fill'. */
+    memcpy (cb->send_buf + cb->send_buf_fill,
+            message, message_len + 1);
+    cb->send_buf_fill += message_len;
+    cb->send_buf_free -= message_len;
+
+    DEBUG ("write_graphite plugin: [%s]:%s buf %zu/%zu (%.1f %%) \"%s\"",
+            cb->node,
+            cb->service,
+            cb->send_buf_fill, sizeof (cb->send_buf),
+            100.0 * ((double) cb->send_buf_fill) / ((double) sizeof (cb->send_buf)),
+            message);
+
+    pthread_mutex_unlock (&cb->send_lock);
+
+    return (0);
+}
+
+static int wg_write_messages (const data_set_t *ds, const value_list_t *vl,
+        struct wg_callback *cb)
+{
+    char buffer[WG_SEND_BUF_SIZE];
+    int status;
+
+    if (0 != strcmp (ds->type, vl->type))
+    {
+        ERROR ("write_graphite plugin: DS type does not match "
+                "value list type");
+        return -1;
+    }
+
+    memset (buffer, 0, sizeof (buffer));
+    status = format_graphite (buffer, sizeof (buffer), ds, vl,
+            cb->prefix, cb->postfix, cb->escape_char, cb->format_flags);
+    if (status != 0) /* error message has been printed already. */
+        return (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 */
+
+static int wg_write (const data_set_t *ds, const value_list_t *vl,
+        user_data_t *user_data)
+{
+    struct wg_callback *cb;
+    int status;
+
+    if (user_data == NULL)
+        return (EINVAL);
+
+    cb = user_data->data;
+
+    status = wg_write_messages (ds, vl, cb);
+
+    return (status);
+}
+
+static int config_set_char (char *dest,
+        oconfig_item_t *ci)
+{
+    char buffer[4];
+    int status;
+
+    memset (buffer, 0, sizeof (buffer));
+
+    status = cf_util_get_string_buffer (ci, buffer, sizeof (buffer));
+    if (status != 0)
+        return (status);
+
+    if (buffer[0] == 0)
+    {
+        ERROR ("write_graphite plugin: Cannot use an empty string for the "
+                "\"EscapeCharacter\" option.");
+        return (-1);
+    }
+
+    if (buffer[1] != 0)
+    {
+        WARNING ("write_graphite plugin: Only the first character of the "
+                "\"EscapeCharacter\" option ('%c') will be used.",
+                (int) buffer[0]);
+    }
+
+    *dest = buffer[0];
+
+    return (0);
+}
+
+static int wg_config_node (oconfig_item_t *ci)
+{
+    struct wg_callback *cb;
+    user_data_t user_data;
+    char callback_name[DATA_MAX_NAME_LEN];
+    int i;
+
+    cb = malloc (sizeof (*cb));
+    if (cb == NULL)
+    {
+        ERROR ("write_graphite plugin: malloc failed.");
+        return (-1);
+    }
+    memset (cb, 0, sizeof (*cb));
+    cb->sock_fd = -1;
+    cb->name = NULL;
+    cb->node = NULL;
+    cb->service = NULL;
+    cb->prefix = NULL;
+    cb->postfix = NULL;
+    cb->escape_char = WG_DEFAULT_ESCAPE;
+    cb->format_flags = GRAPHITE_STORE_RATES;
+
+    /* FIXME: Legacy configuration syntax. */
+    if (strcasecmp ("Carbon", ci->key) != 0)
+    {
+        int 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++)
+    {
+        oconfig_item_t *child = ci->children + i;
+
+        if (strcasecmp ("Host", child->key) == 0)
+            cf_util_get_string (child, &cb->node);
+        else if (strcasecmp ("Port", child->key) == 0)
+            cf_util_get_service (child, &cb->service);
+        else if (strcasecmp ("Prefix", child->key) == 0)
+            cf_util_get_string (child, &cb->prefix);
+        else if (strcasecmp ("Postfix", child->key) == 0)
+            cf_util_get_string (child, &cb->postfix);
+        else if (strcasecmp ("StoreRates", child->key) == 0)
+            cf_util_get_flag (child, &cb->format_flags,
+                    GRAPHITE_STORE_RATES);
+        else if (strcasecmp ("SeparateInstances", child->key) == 0)
+            cf_util_get_flag (child, &cb->format_flags,
+                    GRAPHITE_SEPARATE_INSTANCES);
+        else if (strcasecmp ("AlwaysAppendDS", child->key) == 0)
+            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);
+        }
+    }
+
+    /* FIXME: Legacy configuration syntax. */
+    if (cb->name == NULL)
+        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);
+    else
+        ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s",
+                cb->name);
+
+    memset (&user_data, 0, sizeof (user_data));
+    user_data.data = cb;
+    user_data.free_func = wg_callback_free;
+    plugin_register_write (callback_name, wg_write, &user_data);
+
+    user_data.free_func = NULL;
+    plugin_register_flush (callback_name, wg_flush, &user_data);
+
+    return (0);
+}
+
+static int wg_config (oconfig_item_t *ci)
+{
+    int i;
+
+    for (i = 0; i < ci->children_num; i++)
+    {
+        oconfig_item_t *child = ci->children + i;
+
+        if (strcasecmp ("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 "
+                    "option: %s.", child->key);
+        }
+    }
+
+    return (0);
+}
+
+void module_register (void)
+{
+    plugin_register_complex_config ("write_graphite", wg_config);
+}
+
+/* vim: set sw=4 ts=4 sts=4 tw=78 et : */
index 0a13444..34ea46d 100644 (file)
@@ -61,7 +61,7 @@ struct wh_callback_s
         char   send_buffer[4096];
         size_t send_buffer_free;
         size_t send_buffer_fill;
-        time_t send_buffer_init_time;
+        cdtime_t send_buffer_init_time;
 
         pthread_mutex_t send_lock;
 };
@@ -72,7 +72,7 @@ static void wh_reset_buffer (wh_callback_t *cb)  /* {{{ */
         memset (cb->send_buffer, 0, sizeof (cb->send_buffer));
         cb->send_buffer_free = sizeof (cb->send_buffer);
         cb->send_buffer_fill = 0;
-        cb->send_buffer_init_time = time (NULL);
+        cb->send_buffer_init_time = cdtime ();
 
         if (cb->format == WH_FORMAT_JSON)
         {
@@ -158,19 +158,21 @@ static int wh_callback_init (wh_callback_t *cb) /* {{{ */
         return (0);
 } /* }}} int wh_callback_init */
 
-static int wh_flush_nolock (int timeout, wh_callback_t *cb) /* {{{ */
+static int wh_flush_nolock (cdtime_t timeout, wh_callback_t *cb) /* {{{ */
 {
         int status;
 
-        DEBUG ("write_http plugin: wh_flush_nolock: timeout = %i; "
+        DEBUG ("write_http plugin: wh_flush_nolock: timeout = %.3f; "
                         "send_buffer_fill = %zu;",
-                        timeout, cb->send_buffer_fill);
+                        CDTIME_T_TO_DOUBLE (timeout),
+                        cb->send_buffer_fill);
 
+        /* timeout == 0  => flush unconditionally */
         if (timeout > 0)
         {
-                time_t now;
+                cdtime_t now;
 
-                now = time (NULL);
+                now = cdtime ();
                 if ((cb->send_buffer_init_time + timeout) > now)
                         return (0);
         }
@@ -179,7 +181,7 @@ static int wh_flush_nolock (int timeout, wh_callback_t *cb) /* {{{ */
         {
                 if (cb->send_buffer_fill <= 0)
                 {
-                        cb->send_buffer_init_time = time (NULL);
+                        cb->send_buffer_init_time = cdtime ();
                         return (0);
                 }
 
@@ -190,7 +192,7 @@ static int wh_flush_nolock (int timeout, wh_callback_t *cb) /* {{{ */
         {
                 if (cb->send_buffer_fill <= 2)
                 {
-                        cb->send_buffer_init_time = time (NULL);
+                        cb->send_buffer_init_time = cdtime ();
                         return (0);
                 }
 
@@ -219,7 +221,7 @@ static int wh_flush_nolock (int timeout, wh_callback_t *cb) /* {{{ */
         return (status);
 } /* }}} wh_flush_nolock */
 
-static int wh_flush (int timeout, /* {{{ */
+static int wh_flush (cdtime_t timeout, /* {{{ */
                 const char *identifier __attribute__((unused)),
                 user_data_t *user_data)
 {
@@ -259,7 +261,7 @@ static void wh_callback_free (void *data) /* {{{ */
 
         cb = data;
 
-        wh_flush_nolock (/* timeout = */ -1, cb);
+        wh_flush_nolock (/* timeout = */ 0, cb);
 
         curl_easy_cleanup (cb->curl);
         sfree (cb->location);
@@ -271,76 +273,6 @@ static void wh_callback_free (void *data) /* {{{ */
         sfree (cb);
 } /* }}} void wh_callback_free */
 
-static int wh_value_list_to_string (char *buffer, /* {{{ */
-                size_t buffer_size,
-                const data_set_t *ds, const value_list_t *vl,
-                wh_callback_t *cb)
-{
-        size_t offset = 0;
-        int status;
-        int i;
-        gauge_t *rates = NULL;
-
-        assert (0 == strcmp (ds->type, vl->type));
-
-        memset (buffer, 0, buffer_size);
-
-#define BUFFER_ADD(...) do { \
-        status = ssnprintf (buffer + offset, buffer_size - offset, \
-                        __VA_ARGS__); \
-        if (status < 1) \
-        { \
-                sfree (rates); \
-                return (-1); \
-        } \
-        else if (((size_t) status) >= (buffer_size - offset)) \
-        { \
-                sfree (rates); \
-                return (-1); \
-        } \
-        else \
-                offset += ((size_t) status); \
-} while (0)
-
-        BUFFER_ADD ("%lu", (unsigned long) vl->time);
-
-        for (i = 0; i < ds->ds_num; i++)
-        {
-                if (ds->ds[i].type == DS_TYPE_GAUGE)
-                        BUFFER_ADD (":%f", vl->values[i].gauge);
-                else if (cb->store_rates)
-                {
-                        if (rates == NULL)
-                                rates = uc_get_rate (ds, vl);
-                        if (rates == NULL)
-                        {
-                                WARNING ("write_http plugin: "
-                                                "uc_get_rate failed.");
-                                return (-1);
-                        }
-                        BUFFER_ADD (":%g", rates[i]);
-                }
-                else if (ds->ds[i].type == DS_TYPE_COUNTER)
-                        BUFFER_ADD (":%llu", vl->values[i].counter);
-                else if (ds->ds[i].type == DS_TYPE_DERIVE)
-                        BUFFER_ADD (":%"PRIi64, vl->values[i].derive);
-                else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
-                        BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
-                else
-                {
-                        ERROR ("write_http plugin: Unknown data source type: %i",
-                                        ds->ds[i].type);
-                        sfree (rates);
-                        return (-1);
-                }
-        } /* for ds->ds_num */
-
-#undef BUFFER_ADD
-
-        sfree (rates);
-        return (0);
-} /* }}} int wh_value_list_to_string */
-
 static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{{ */
                 wh_callback_t *cb)
 {
@@ -367,7 +299,7 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{
 
         /* Convert the values to an ASCII representation and put that into
          * `values'. */
-        status = wh_value_list_to_string (values, sizeof (values), ds, vl, cb);
+        status = format_values (values, sizeof (values), ds, vl, cb->store_rates);
         if (status != 0) {
                 ERROR ("write_http plugin: error with "
                                 "wh_value_list_to_string");
@@ -375,8 +307,10 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{
         }
 
         command_len = (size_t) ssnprintf (command, sizeof (command),
-                        "PUTVAL %s interval=%i %s\r\n",
-                        key, vl->interval, values);
+                        "PUTVAL %s interval=%.3f %s\r\n",
+                        key,
+                        CDTIME_T_TO_DOUBLE (vl->interval),
+                        values);
         if (command_len >= sizeof (command)) {
                 ERROR ("write_http plugin: Command buffer too small: "
                                 "Need %zu bytes.", command_len + 1);
@@ -398,7 +332,7 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{
 
         if (command_len >= cb->send_buffer_free)
         {
-                status = wh_flush_nolock (/* timeout = */ -1, cb);
+                status = wh_flush_nolock (/* timeout = */ 0, cb);
                 if (status != 0)
                 {
                         pthread_mutex_unlock (&cb->send_lock);
@@ -450,7 +384,7 @@ static int wh_write_json (const data_set_t *ds, const value_list_t *vl, /* {{{ *
                         ds, vl, cb->store_rates);
         if (status == (-ENOMEM))
         {
-                status = wh_flush_nolock (/* timeout = */ -1, cb);
+                status = wh_flush_nolock (/* timeout = */ 0, cb);
                 if (status != 0)
                 {
                         wh_reset_buffer (cb);
diff --git a/src/write_mongodb.c b/src/write_mongodb.c
new file mode 100644 (file)
index 0000000..30e261c
--- /dev/null
@@ -0,0 +1,378 @@
+/**
+ * collectd - src/write_mongodb.c
+ * Copyright (C) 2010-2013  Florian Forster
+ * Copyright (C) 2010       Akkarit Sangpetch
+ * Copyright (C) 2012       Chris Lundquist
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * 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>
+ *   Akkarit Sangpetch <asangpet at andrew.cmu.edu>
+ *   Chris Lundquist <clundquist at bluebox.net>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+#include "utils_cache.h"
+
+#include <pthread.h>
+
+#if HAVE_STDINT_H
+# define MONGO_HAVE_STDINT 1
+#else
+# define MONGO_USE_LONG_LONG_INT 1
+#endif
+#include <mongo.h>
+
+struct wm_node_s
+{
+  char name[DATA_MAX_NAME_LEN];
+
+  char *host;
+  int port;
+  int timeout;
+
+  /* Authentication information */
+  char *db;
+  char *user;
+  char *passwd;
+
+  _Bool store_rates;
+
+  mongo conn[1];
+  pthread_mutex_t lock;
+};
+typedef struct wm_node_s wm_node_t;
+
+/*
+ * Functions
+ */
+static bson *wm_create_bson (const data_set_t *ds, /* {{{ */
+    const value_list_t *vl,
+    _Bool store_rates)
+{
+  bson *ret;
+  gauge_t *rates;
+  int i;
+
+  ret = bson_create ();
+  if (ret == NULL)
+  {
+    ERROR ("write_mongodb plugin: bson_create failed.");
+    return (NULL);
+  }
+
+  if (store_rates)
+  {
+    rates = uc_get_rate (ds, vl);
+    if (rates == NULL)
+    {
+      ERROR ("write_mongodb plugin: uc_get_rate() failed.");
+      return (NULL);
+    }
+  }
+  else
+  {
+    rates = NULL;
+  }
+
+  bson_init (ret);
+  bson_append_date (ret, "time", (bson_date_t) CDTIME_T_TO_MS (vl->time));
+  bson_append_string (ret, "host", vl->host);
+  bson_append_string (ret, "plugin", vl->plugin);
+  bson_append_string (ret, "plugin_instance", vl->plugin_instance);
+  bson_append_string (ret, "type", vl->type);
+  bson_append_string (ret, "type_instance", vl->type_instance);
+
+  bson_append_start_array (ret, "values"); /* {{{ */
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    char key[16];
+
+    ssnprintf (key, sizeof (key), "%i", i);
+
+    if (ds->ds[i].type == DS_TYPE_GAUGE)
+      bson_append_double(ret, key, vl->values[i].gauge);
+    else if (store_rates)
+      bson_append_double(ret, key, (double) rates[i]);
+    else if (ds->ds[i].type == DS_TYPE_COUNTER)
+      bson_append_long(ret, key, vl->values[i].counter);
+    else if (ds->ds[i].type == DS_TYPE_DERIVE)
+      bson_append_long(ret, key, vl->values[i].derive);
+    else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+      bson_append_long(ret, key, vl->values[i].absolute);
+    else
+      assert (23 == 42);
+  }
+  bson_append_finish_array (ret); /* }}} values */
+
+  bson_append_start_array (ret, "dstypes"); /* {{{ */
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    char key[16];
+
+    ssnprintf (key, sizeof (key), "%i", i);
+
+    if (store_rates)
+      bson_append_string (ret, key, "gauge");
+    else
+      bson_append_string (ret, key, DS_TYPE_TO_STRING (ds->ds[i].type));
+  }
+  bson_append_finish_array (ret); /* }}} dstypes */
+
+  bson_append_start_array (ret, "dsnames"); /* {{{ */
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    char key[16];
+
+    ssnprintf (key, sizeof (key), "%i", i);
+    bson_append_string (ret, key, ds->ds[i].name);
+  }
+  bson_append_finish_array (ret); /* }}} dsnames */
+
+  bson_finish (ret);
+
+  sfree (rates);
+  return (ret);
+} /* }}} bson *wm_create_bson */
+
+static int wm_write (const data_set_t *ds, /* {{{ */
+    const value_list_t *vl,
+    user_data_t *ud)
+{
+  wm_node_t *node = ud->data;
+  char collection_name[512];
+  bson *bson_record;
+  int status;
+
+  ssnprintf (collection_name, sizeof (collection_name), "collectd.%s",
+      vl->plugin);
+
+  bson_record = wm_create_bson (ds, vl, node->store_rates);
+  if (bson_record == NULL)
+    return (ENOMEM);
+
+  pthread_mutex_lock (&node->lock);
+
+  if (!mongo_is_connected (node->conn))
+  {
+    INFO ("write_mongodb plugin: Connecting to [%s]:%i",
+        (node->host != NULL) ? node->host : "localhost",
+        (node->port != 0) ? node->port : MONGO_DEFAULT_PORT);
+    status = mongo_connect (node->conn, node->host, node->port);
+    if (status != MONGO_OK) {
+      ERROR ("write_mongodb plugin: Connecting to [%s]:%i failed.",
+          (node->host != NULL) ? node->host : "localhost",
+          (node->port != 0) ? node->port : MONGO_DEFAULT_PORT);
+      mongo_destroy (node->conn);
+      pthread_mutex_unlock (&node->lock);
+      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) {
+        WARNING ("write_mongodb plugin: mongo_set_op_timeout(%i) failed: %s",
+            node->timeout, node->conn->errstr);
+      }
+    }
+  }
+
+  /* Assert if the connection has been established */
+  assert (mongo_is_connected (node->conn));
+
+  #if MONGO_MINOR >= 6
+    /* There was an API change in 0.6.0 as linked below */
+    /* https://github.com/mongodb/mongo-c-driver/blob/master/HISTORY.md */
+    status = mongo_insert (node->conn, collection_name, bson_record, NULL);
+  #else
+    status = mongo_insert (node->conn, collection_name, bson_record);
+  #endif
+
+  if (status != MONGO_OK)
+  {
+    ERROR ( "write_mongodb plugin: error inserting record: %d", node->conn->err);
+    if (node->conn->err != MONGO_BSON_INVALID)
+      ERROR ("write_mongodb plugin: %s", node->conn->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)
+        && (node->conn->err != MONGO_BSON_NOT_FINISHED))
+      mongo_destroy (node->conn);
+  }
+
+  pthread_mutex_unlock (&node->lock);
+
+  /* free our resource as not to leak memory */
+  bson_dispose (bson_record);
+
+  return (0);
+} /* }}} int wm_write */
+
+static void wm_config_free (void *ptr) /* {{{ */
+{
+  wm_node_t *node = ptr;
+
+  if (node == NULL)
+    return;
+
+  if (mongo_is_connected (node->conn))
+    mongo_destroy (node->conn);
+
+  sfree (node->host);
+  sfree (node);
+} /* }}} void wm_config_free */
+
+static int wm_config_node (oconfig_item_t *ci) /* {{{ */
+{
+  wm_node_t *node;
+  int status;
+  int i;
+
+  node = malloc (sizeof (*node));
+  if (node == NULL)
+    return (ENOMEM);
+  memset (node, 0, sizeof (*node));
+  mongo_init (node->conn);
+  node->host = NULL;
+  node->store_rates = 1;
+  pthread_mutex_init (&node->lock, /* attr = */ NULL);
+
+  status = cf_util_get_string_buffer (ci, node->name, sizeof (node->name));
+
+  if (status != 0)
+  {
+    sfree (node);
+    return (status);
+  }
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Host", child->key) == 0)
+      status = cf_util_get_string (child, &node->host);
+    else if (strcasecmp ("Port", child->key) == 0)
+    {
+      status = cf_util_get_port_number (child);
+      if (status > 0)
+      {
+        node->port = status;
+        status = 0;
+      }
+    }
+    else if (strcasecmp ("Timeout", child->key) == 0)
+      status = cf_util_get_int (child, &node->timeout);
+    else if (strcasecmp ("StoreRates", child->key) == 0)
+      status = cf_util_get_boolean (child, &node->store_rates);
+    else 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);
+
+    if (status != 0)
+      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];
+    user_data_t ud;
+
+    ssnprintf (cb_name, sizeof (cb_name), "write_mongodb/%s", node->name);
+
+    ud.data = node;
+    ud.free_func = wm_config_free;
+
+    status = plugin_register_write (cb_name, wm_write, &ud);
+    INFO ("write_mongodb plugin: registered write plugin %s %d",cb_name,status);
+  }
+
+  if (status != 0)
+    wm_config_free (node);
+
+  return (status);
+} /* }}} int wm_config_node */
+
+static int wm_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Node", child->key) == 0)
+      wm_config_node (child);
+    else
+      WARNING ("write_mongodb plugin: Ignoring unknown "
+          "configuration option \"%s\" at top level.", child->key);
+  }
+
+  return (0);
+} /* }}} int wm_config */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("write_mongodb", wm_config);
+}
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
diff --git a/src/write_redis.c b/src/write_redis.c
new file mode 100644 (file)
index 0000000..1d9ec27
--- /dev/null
@@ -0,0 +1,238 @@
+/**
+ * collectd - src/write_redis.c
+ * Copyright (C) 2010  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 "plugin.h"
+#include "common.h"
+#include "configfile.h"
+
+#include <pthread.h>
+#include <credis.h>
+
+struct wr_node_s
+{
+  char name[DATA_MAX_NAME_LEN];
+
+  char *host;
+  int port;
+  int timeout;
+
+  REDIS conn;
+  pthread_mutex_t lock;
+};
+typedef struct wr_node_s wr_node_t;
+
+/*
+ * Functions
+ */
+static int wr_write (const data_set_t *ds, /* {{{ */
+    const value_list_t *vl,
+    user_data_t *ud)
+{
+  wr_node_t *node = ud->data;
+  char ident[512];
+  char key[512];
+  char value[512];
+  size_t value_size;
+  char *value_ptr;
+  int status;
+  int i;
+
+  status = FORMAT_VL (ident, sizeof (ident), vl);
+  if (status != 0)
+    return (status);
+  ssnprintf (key, sizeof (key), "collectd/%s", ident);
+
+  memset (value, 0, sizeof (value));
+  value_size = sizeof (value);
+  value_ptr = &value[0];
+
+#define APPEND(...) do {                                             \
+  status = snprintf (value_ptr, value_size, __VA_ARGS__);            \
+  if (((size_t) status) > value_size)                                \
+  {                                                                  \
+    value_ptr += value_size;                                         \
+    value_size = 0;                                                  \
+  }                                                                  \
+  else                                                               \
+  {                                                                  \
+    value_ptr += status;                                             \
+    value_size -= status;                                            \
+  }                                                                  \
+} while (0)
+
+  APPEND ("%lu:", (unsigned long) vl->time);
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    if (ds->ds[i].type == DS_TYPE_COUNTER)
+      APPEND ("%llu", vl->values[i].counter);
+    else if (ds->ds[i].type == DS_TYPE_GAUGE)
+      APPEND ("%g", vl->values[i].gauge);
+    else if (ds->ds[i].type == DS_TYPE_DERIVE)
+      APPEND ("%"PRIi64, vl->values[i].derive);
+    else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+      APPEND ("%"PRIu64, vl->values[i].absolute);
+    else
+      assert (23 == 42);
+  }
+
+#undef APPEND
+
+  pthread_mutex_lock (&node->lock);
+
+  if (node->conn == NULL)
+  {
+    node->conn = credis_connect (node->host, node->port, node->timeout);
+    if (node->conn == NULL)
+    {
+      ERROR ("write_redis plugin: Connecting to host \"%s\" (port %i) failed.",
+          (node->host != NULL) ? node->host : "localhost",
+          (node->port != 0) ? node->port : 6379);
+      pthread_mutex_unlock (&node->lock);
+      return (-1);
+    }
+  }
+
+  /* "credis_zadd" doesn't handle a NULL pointer gracefully, so I'd rather
+   * have a meaningful assertion message than a normal segmentation fault. */
+  assert (node->conn != NULL);
+  status = credis_zadd (node->conn, key, (double) vl->time, value);
+
+  credis_sadd (node->conn, "collectd/values", ident);
+
+  pthread_mutex_unlock (&node->lock);
+
+  return (0);
+} /* }}} int wr_write */
+
+static void wr_config_free (void *ptr) /* {{{ */
+{
+  wr_node_t *node = ptr;
+
+  if (node == NULL)
+    return;
+
+  if (node->conn != NULL)
+  {
+    credis_close (node->conn);
+    node->conn = NULL;
+  }
+
+  sfree (node->host);
+  sfree (node);
+} /* }}} void wr_config_free */
+
+static int wr_config_node (oconfig_item_t *ci) /* {{{ */
+{
+  wr_node_t *node;
+  int status;
+  int i;
+
+  node = malloc (sizeof (*node));
+  if (node == NULL)
+    return (ENOMEM);
+  memset (node, 0, sizeof (*node));
+  node->host = NULL;
+  node->port = 0;
+  node->timeout = 1000;
+  node->conn = NULL;
+  pthread_mutex_init (&node->lock, /* attr = */ NULL);
+
+  status = cf_util_get_string_buffer (ci, node->name, sizeof (node->name));
+  if (status != 0)
+  {
+    sfree (node);
+    return (status);
+  }
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Host", child->key) == 0)
+      status = cf_util_get_string (child, &node->host);
+    else if (strcasecmp ("Port", child->key) == 0)
+    {
+      status = cf_util_get_port_number (child);
+      if (status > 0)
+      {
+        node->port = status;
+        status = 0;
+      }
+    }
+    else if (strcasecmp ("Timeout", child->key) == 0)
+      status = cf_util_get_int (child, &node->timeout);
+    else
+      WARNING ("write_redis plugin: Ignoring unknown config option \"%s\".",
+          child->key);
+
+    if (status != 0)
+      break;
+  } /* for (i = 0; i < ci->children_num; i++) */
+
+  if (status == 0)
+  {
+    char cb_name[DATA_MAX_NAME_LEN];
+    user_data_t ud;
+
+    ssnprintf (cb_name, sizeof (cb_name), "write_redis/%s", node->name);
+
+    ud.data = node;
+    ud.free_func = wr_config_free;
+
+    status = plugin_register_write (cb_name, wr_write, &ud);
+  }
+
+  if (status != 0)
+    wr_config_free (node);
+
+  return (status);
+} /* }}} int wr_config_node */
+
+static int wr_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Node", child->key) == 0)
+      wr_config_node (child);
+    else
+      WARNING ("write_redis plugin: Ignoring unknown "
+          "configuration option \"%s\" at top level.", child->key);
+  }
+
+  return (0);
+} /* }}} int wr_config */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("write_redis", wr_config);
+}
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
diff --git a/src/write_riemann.c b/src/write_riemann.c
new file mode 100644 (file)
index 0000000..b558b06
--- /dev/null
@@ -0,0 +1,816 @@
+/**
+ * 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"
+
+struct riemann_host {
+       char                    *name;
+#define F_CONNECT               0x01
+       uint8_t                  flags;
+       pthread_mutex_t          lock;
+       _Bool                    store_rates;
+       _Bool                    always_append_ds;
+       char                    *node;
+       char                    *service;
+       _Bool                    use_tcp;
+       int                      s;
+
+       int                      reference_count;
+};
+
+static char    **riemann_tags;
+static size_t    riemann_tags_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 succesful 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_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]);
+
+       /* Pull in values from threshold */
+       for (meta = n->meta; meta != NULL; meta = meta->next)
+       {
+               if (strcasecmp ("CurrentValue", meta->name) != 0)
+                       continue;
+
+               event->metric_d = meta->nm_value.nm_double;
+               event->has_metric_d = 1;
+               break;
+       }
+
+       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)
+{
+       Event *event;
+       char name_buffer[5 * DATA_MAX_NAME_LEN];
+       char service_buffer[6 * DATA_MAX_NAME_LEN];
+       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;
+       event->ttl = CDTIME_T_TO_TIME_T (2 * vl->interval);
+       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_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)
+{
+       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);
+               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;
+
+       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;
+       struct riemann_host     *host = ud->data;
+       Msg                     *msg;
+
+       msg = riemann_value_list_to_protobuf (host, ds, vl);
+       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->store_rates = 1;
+       host->always_append_ds = 0;
+       host->use_tcp = 0;
+
+       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 ("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 {
+                       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, "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 : */
index 79a234f..cc98862 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/zfs_arc.c
  * Copyright (C) 2009  Anthony Dewhurst
+ * Copyright (C) 2012  Aurelien Rougemont
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -17,6 +18,7 @@
  *
  * Authors:
  *   Anthony Dewhurst <dewhurst at gmail>
+ *   Aurelien Rougemont <beorn at gandi.net>
  **/
 
 #include "collectd.h"
@@ -46,54 +48,67 @@ static void za_submit (const char* type, const char* type_instance, value_t* val
 
 static void za_submit_gauge (const char* type, const char* type_instance, gauge_t value)
 {
-       value_t values[1];
+       value_t vv;
 
-       values[0].gauge = value;
-
-       za_submit (type, type_instance, values, STATIC_ARRAY_SIZE(values));
+       vv.gauge = value;
+       za_submit (type, type_instance, &vv, 1);
 }
 
-static void za_submit_size (gauge_t size, gauge_t size_target, gauge_t limit_min, gauge_t limit_max)
+static int za_read_derive (kstat_t *ksp, const char *kstat_value,
+    const char *type, const char *type_instance)
 {
-       value_t values[4];
-
-       values[0].gauge = size;
-       values[1].gauge = size_target;
-       values[2].gauge = limit_min;
-       values[3].gauge = limit_max;
-
-       za_submit ("arc_size", "", values, STATIC_ARRAY_SIZE(values));
+  long long tmp;
+  value_t v;
+
+  tmp = get_kstat_value (ksp, (char *)kstat_value);
+  if (tmp == -1LL)
+  {
+    WARNING ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
+    return (-1);
+  }
+
+  v.derive = (derive_t) tmp;
+  za_submit (type, type_instance, /* values = */ &v, /* values_num = */ 1);
+  return (0);
 }
 
-static void za_submit_bytes (counter_t read, counter_t write)
+static int za_read_gauge (kstat_t *ksp, const char *kstat_value,
+    const char *type, const char *type_instance)
 {
-       value_t values[2];
-
-       values[0].counter = read;
-       values[1].counter = write;
-
-       za_submit ("arc_l2_bytes", "", values, STATIC_ARRAY_SIZE(values));
+  long long tmp;
+  value_t v;
+
+  tmp = get_kstat_value (ksp, (char *)kstat_value);
+  if (tmp == -1LL)
+  {
+    WARNING ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
+    return (-1);
+  }
+
+  v.gauge = (gauge_t) tmp;
+  za_submit (type, type_instance, /* values = */ &v, /* values_num = */ 1);
+  return (0);
 }
 
-static void za_submit_counts (char *type_instance, counter_t demand_data, counter_t demand_metadata,
-       counter_t prefetch_data, counter_t prefetch_metadata)
+static void za_submit_ratio (const char* type_instance, gauge_t hits, gauge_t misses)
 {
-       value_t values[4];
+       gauge_t ratio = NAN;
+
+       if (!isfinite (hits) || (hits < 0.0))
+               hits = 0.0;
+       if (!isfinite (misses) || (misses < 0.0))
+               misses = 0.0;
 
-       values[0].counter = demand_data;
-       values[1].counter = demand_metadata;
-       values[2].counter = prefetch_data;
-       values[3].counter = prefetch_metadata;
+       if ((hits != 0.0) || (misses != 0.0))
+               ratio = hits / (hits + misses);
 
-       za_submit ("arc_counts", type_instance, values, STATIC_ARRAY_SIZE(values));
+       za_submit_gauge ("cache_ratio", type_instance, ratio);
 }
 
 static int za_read (void)
 {
-       gauge_t   arcsize, targetsize, minlimit, maxlimit, hits, misses, l2_size, l2_hits, l2_misses;
-       counter_t demand_data_hits, demand_metadata_hits, prefetch_data_hits, prefetch_metadata_hits;
-       counter_t demand_data_misses, demand_metadata_misses, prefetch_data_misses, prefetch_metadata_misses;
-       counter_t l2_read_bytes, l2_write_bytes;
+       gauge_t  arc_hits, arc_misses, l2_hits, l2_misses;
+       value_t  l2_io[2];
        kstat_t  *ksp   = NULL;
 
        get_kstat (&ksp, "zfs", 0, "arcstats");
@@ -103,46 +118,53 @@ static int za_read (void)
                return (-1);
        }
 
-       arcsize    = get_kstat_value(ksp, "size");
-       targetsize = get_kstat_value(ksp, "c");
-       minlimit   = get_kstat_value(ksp, "c_min");
-       maxlimit   = get_kstat_value(ksp, "c_max");
-
-       demand_data_hits       = get_kstat_value(ksp, "demand_data_hits");
-       demand_metadata_hits   = get_kstat_value(ksp, "demand_metadata_hits");
-       prefetch_data_hits     = get_kstat_value(ksp, "prefetch_data_hits");
-       prefetch_metadata_hits = get_kstat_value(ksp, "prefetch_metadata_hits");
-
-       demand_data_misses       = get_kstat_value(ksp, "demand_data_misses");
-       demand_metadata_misses   = get_kstat_value(ksp, "demand_metadata_misses");
-       prefetch_data_misses     = get_kstat_value(ksp, "prefetch_data_misses");
-       prefetch_metadata_misses = get_kstat_value(ksp, "prefetch_metadata_misses");
-
-       hits   = get_kstat_value(ksp, "hits");
-       misses = get_kstat_value(ksp, "misses");
-
-       l2_size        = get_kstat_value(ksp, "l2_size");
-       l2_read_bytes  = get_kstat_value(ksp, "l2_read_bytes");
-       l2_write_bytes = get_kstat_value(ksp, "l2_write_bytes");
-       l2_hits        = get_kstat_value(ksp, "l2_hits");
-       l2_misses      = get_kstat_value(ksp, "l2_misses");
-
-
-       za_submit_size (arcsize, targetsize, minlimit, maxlimit);
-       za_submit_gauge ("arc_l2_size", "", l2_size);
-
-       za_submit_counts ("hits",   demand_data_hits,     demand_metadata_hits,
-                                   prefetch_data_hits,   prefetch_metadata_hits);
-       za_submit_counts ("misses", demand_data_misses,   demand_metadata_misses,
-                                   prefetch_data_misses, prefetch_metadata_misses);
-
-       za_submit_gauge ("arc_ratio", "L1", hits / (hits + misses));
-       za_submit_gauge ("arc_ratio", "L2", l2_hits / (l2_hits + l2_misses));
-
-       za_submit_bytes (l2_read_bytes, l2_write_bytes);
+       /* Sizes */
+       za_read_gauge (ksp, "size",    "cache_size", "arc");
+       za_read_gauge (ksp, "l2_size", "cache_size", "L2");
+
+        /* 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_operations", "miss");
+       za_read_derive (ksp, "hash_collisions", "hash_collisions", "");
+       
+        /* Evictions */
+       za_read_derive (ksp, "evict_l2_cached",     "cache_eviction", "cached");
+       za_read_derive (ksp, "evict_l2_eligible",   "cache_eviction", "eligible");
+       za_read_derive (ksp, "evict_l2_ineligible", "cache_eviction", "ineligible");
+
+       /* Hits / misses */
+       za_read_derive (ksp, "demand_data_hits",         "cache_result", "demand_data-hit");
+       za_read_derive (ksp, "demand_metadata_hits",     "cache_result", "demand_metadata-hit");
+       za_read_derive (ksp, "prefetch_data_hits",       "cache_result", "prefetch_data-hit");
+       za_read_derive (ksp, "prefetch_metadata_hits",   "cache_result", "prefetch_metadata-hit");
+       za_read_derive (ksp, "demand_data_misses",       "cache_result", "demand_data-miss");
+       za_read_derive (ksp, "demand_metadata_misses",   "cache_result", "demand_metadata-miss");
+       za_read_derive (ksp, "prefetch_data_misses",     "cache_result", "prefetch_data-miss");
+       za_read_derive (ksp, "prefetch_metadata_misses", "cache_result", "prefetch_metadata-miss");
+
+       /* Ratios */
+       arc_hits   = (gauge_t) get_kstat_value(ksp, "hits");
+       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");
+
+       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");
+
+       za_submit ("io_octets", "L2", l2_io, /* num values = */ 2);
 
        return (0);
-}
+} /* int za_read */
 
 static int za_init (void) /* {{{ */
 {
index 0b75fc2..1ac1bc1 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-DEFAULT_VERSION="4.10.9.git"
+DEFAULT_VERSION="5.3.1.git"
 
 VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"
 
@@ -10,8 +10,4 @@ fi
 
 VERSION="`echo \"$VERSION\" | sed -e 's/-/./g'`"
 
-if test "x`uname -s`" = "xAIX" || test "x`uname -s`" = "xSunOS" ; then
-       echo "$VERSION\c"
-else 
-       echo -n "$VERSION"
-fi
+echo -n "$VERSION"