Merge branch 'collectd-4.8' into collectd-4.9
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Mon, 15 Mar 2010 21:40:45 +0000 (22:40 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Mon, 15 Mar 2010 21:40:45 +0000 (22:40 +0100)
Conflicts:
src/exec.c

74 files changed:
AUTHORS
ChangeLog
README
TODO
bindings/Makefile.am
bindings/perl/Collectd.pm [deleted file]
bindings/perl/Collectd/Makefile.PL [deleted file]
bindings/perl/Collectd/Unixsock.pm [deleted file]
bindings/perl/lib/Collectd.pm [new file with mode: 0644]
bindings/perl/lib/Collectd/Plugins/Monitorus.pm [new file with mode: 0644]
bindings/perl/lib/Collectd/Plugins/OpenVZ.pm [new file with mode: 0644]
bindings/perl/lib/Collectd/Unixsock.pm [new file with mode: 0644]
configure.in
contrib/collection3/etc/collection.conf
contrib/redhat/collectd.spec
src/Makefile.am
src/apache.c
src/ascent.c
src/collectd-exec.pod
src/collectd-python.pod [new file with mode: 0644]
src/collectd.c
src/collectd.conf.in
src/collectd.conf.pod
src/common.c
src/common.h
src/configfile.c
src/configfile.h
src/contextswitch.c [new file with mode: 0644]
src/cpu.c
src/cpython.h [new file with mode: 0644]
src/csv.c
src/curl.c
src/df.c
src/disk.c
src/exec.c
src/hddtemp.c
src/interface.c
src/ipmi.c
src/irq.c
src/liboconfig/parser.y
src/liboconfig/scanner.l
src/libvirt.c
src/load.c
src/logfile.c
src/match_hashed.c [new file with mode: 0644]
src/memory.c
src/netapp.c [new file with mode: 0644]
src/netlink.c
src/network.c
src/nginx.c
src/ntpd.c
src/onewire.c
src/openvpn.c
src/ping.c
src/plugin.c
src/plugin.h
src/processes.c
src/protocols.c
src/pyconfig.c [new file with mode: 0644]
src/python.c [new file with mode: 0644]
src/pyvalues.c [new file with mode: 0644]
src/routeros.c [new file with mode: 0644]
src/rrdcached.c
src/sensors.c
src/swap.c
src/target_scale.c [new file with mode: 0644]
src/tcpconns.c
src/thermal.c
src/tokyotyrant.c
src/types.db
src/utils_heap.c
src/utils_heap.h
src/vmem.c
version-gen.sh

diff --git a/AUTHORS b/AUTHORS
index 8d0a022..4b133fa 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -82,9 +82,21 @@ Luke Herberling <collectd at c-ware.com>
 Lyonel Vincent <lyonel at ezix.org>
  - processes plugin.
 
+Manuel Sanmartin
+ - AIX port of the following plugins:
+   + cpu
+   + disk
+   + interface
+   + load
+   + memory
+   + processes
+   + swap
+ - Various AIX-related fixes and hacks.
+
 Marco Chiappero <marco at absence.it>
  - uptime plugin.
  - ip6tables support in the iptables plugin.
+ - openvpn plugin (support for more status file formats)
 
 Michael Stapelberg <michael+git at stapelberg.de>
  - OpenBSD port of the tcpconns plugin.
@@ -110,6 +122,11 @@ Oleg King <king2 at kaluga.ru>
 Ondrej Zajicek <santiago at crfreenet.org>
  - madwifi plugin.
 
+Patrik Weiskircher <weiskircher at inqnet.at>
+ - Contextswitch plugin.
+ - Forkrate counter in the processes plugin.
+ - INode count in the DF plugin.
+
 Paul Sadauskas <psadauskas at gmail.com>
  - tokyotyrant plugin.
  - `ReportByDevice' option of the df plugin.
@@ -147,6 +164,10 @@ Sjoerd van der Berg <harekiet at gmail.com>
 Stefan Hacker <stefan.hacker at web.de>
  - teamspeak2 plugin.
 
+Sven Trenkel <collectd at semidefinite.de>
+ - netapp plugin.
+ - python plugin.
+
 Tomasz Pala <gotar at pld-linux.org>
  - conntrack plugin.
 
index 80c3746..74c5ccb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,57 @@
+2010-01-14, Version 4.9.1
+       * Documentation: Some manpage fixes.
+       * Default config: Added sample configuration for missing plugins.
+       * apache plugin: Fix a segmentation fault in the config handling of
+         VerifyPeer / VerifyHost. Thanks to "plazmus" for his or her patch.
+       * processes plugin: Fix handling of derive data sources.
+       * rrdtool plugin: Fix a bug with random write timeouts. Due to an
+         incorrect initialization some files may be suspended basically
+         indefinitely. After flushing the files they were written regularly
+         again.
+       * routeros plugin: Use the node name for the "host" field.
+       * Monitorus.pm: Put the plugin into the "Collectd::Plugins" namespace.
+       * Perl bindings: Fix a warning that was printed when building
+         debugging output.
+
+2009-12-21, Version 4.9.0
+       * contextswitch plugin: The new ContextSwitch plugin gathers the
+         number of context switches done by the CPU. Thanks to Patrik
+         Weiskircher for the patch.
+       * cpu plugin: Support for SMP (multiple processors) under FreeBSD has
+         been added. Thanks to Doug MacEachern for the patch.
+       * curl plugin: The “MeasureResponseTime” option has been added. Thanks
+         to Aman Gupta for the patch.
+       * df plugin: Collecting the inode count and reserved space has been
+         added. Thanks to Patrik Weiskircher for the patch.
+       * exec plugin: The environment variables “COLLECTD_INTERVAL” and
+         “COLLECTD_HOSTNAME” are now set before executing the application.
+       * Monitorus plugin: This Perl-based plugin to query statistics from
+         mon.itor.us has been added. Thanks to Jeff Green for the patch.
+       * netapp plugin: New plugin to collect statistics from NetApp filers.
+         Thanks to Sven Trenkel of the noris network AG for the patch.
+       * network plugin: Statistics collection about the plugin itself has
+         been implemented.
+       * openvpn plugin: Add support for more versions of the “status file”.
+         Thanks to Marco Chiappero for the patch.
+       * OpenVZ plugin: This Perl-based plugin to gather OpenVZ statistics
+         has been added. Thanks to Jonathan Kolb for the patch.
+       * ping plugin: The config options "SourceAddress" and "Device"
+         have been added. Thanks to Sebastian Harl for the patch.
+       * processes plugin: Collection of IO-metrics has been added. Thanks to
+         Andrés J. Díaz for the patch.
+       * python plugin: The new Python plugin integrates a Python interpreter
+         into collectd and allows to execute plugins written in the scripting
+         language. Thanks to Sven Trenkel for his work.
+       * routeros plugin: The new RouterOS plugin queries interface and
+         wireless registration statistics from RouterOS.
+       * Various plugins: AIX support has been added to the cpu, disk,
+         interface, load, memory, processes, and swap plugins. Thanks to
+         Manuel Sanmartin for his patches.
+       * hashed match: This match for simple load balancing and redundant
+         storage has been added.
+       * scale target: This target to scale (multiply) values by an arbitrary
+         value has been added.
+
 2010-01-14, Version 4.8.3
        * Documentation: Some manpage fixes.
        * rrdtool plugin: Fix a bug with random write timeouts. Due to an
diff --git a/README b/README
index 37ddf8e..96fbdf3 100644 (file)
--- a/README
+++ b/README
@@ -40,6 +40,9 @@ Features
     - conntrack
       Number of nf_conntrack entries.
 
+    - contextswitch
+      Number of context switches done by the operating system.
+
     - cpu
       CPU utilization: Time spent in the system, user, nice, idle, and related
       states.
@@ -148,6 +151,10 @@ Features
       MySQL server statistics: Commands issued, handlers triggered, thread
       usage, query cache utilization and traffic/octets sent and received.
 
+    - netapp
+      Plugin to query performance values from a NetApp storage system using the
+      “Manage ONTAP” SDK provided by NetApp.
+
     - netlink
       Very detailed Linux network interface and routing statistics. You can get
       (detailed) information on interfaces, qdiscs, classes, and, if you can
@@ -210,6 +217,15 @@ Features
     - protocols
       Counts various aspects of network protocols such as IP, TCP, UDP, etc.
 
+    - python
+      The python plugin implements a Python interpreter into collectd. This
+      makes it possible to write plugins in Python which are executed by
+      collectd without the need to start a heavy interpreter every interval.
+      See collectd-python(5) for details.
+
+    - routeros
+      Query interface and wireless registration statistics from RouterOS.
+
     - rrdcached
       RRDtool caching daemon (RRDcacheD) statistics.
 
@@ -294,6 +310,10 @@ Features
       you can easily do weird stuff with the plugins we didn't dare think of
       ;) See collectd-perl(5).
 
+    - python
+      It's possible to implement write plugins in Python using the python
+      plugin. See collectd-python(5) for details.
+
     - rrdcached
       Output to round-robin-database (RRD) files using the RRDtool caching
       daemon (RRDcacheD) - see rrdcached(1). That daemon provides a general
@@ -326,6 +346,10 @@ Features
       Log messages are propagated to plugins written in Perl as well.
       See collectd-perl(5).
 
+    - python
+      It's possible to implement log plugins in Python using the python plugin.
+      See collectd-python(5) for details.
+
     - syslog
       Logs to the standard UNIX logging mechanism, syslog.
 
@@ -355,12 +379,19 @@ Features
       Notifications are propagated to plugins written in Perl as well.
       See collectd-perl(5).
 
+    - python
+      It's possible to implement notification plugins in Python using the
+      python plugin. See collectd-python(5) for details.
+
   * Value processing can be controlled using the "filter chain" infrastructure
     and "matches" and "targets". The following plugins are available:
 
     - match_empty_counter
       Match counter values which are currently zero.
 
+    - match_hashed
+      Match values using a hash function of the hostname.
+
     - match_regex
       Match values by their identifier based on regular expressions.
 
@@ -376,6 +407,9 @@ Features
     - target_replace
       Replace parts of an identifier using regular expressions.
 
+    - target_scale
+      Scale (multiply) values by an arbitrary value.
+
     - target_set
       Set (overwrite) entire parts of an identifier.
 
@@ -507,6 +541,10 @@ Prerequisites
     Unsurprisingly used by the `mysql' plugin.
     <http://dev.mysql.com/>
 
+  * libnatapp (optional)
+    Required for the “netapp” plugin.
+    This library is part of the “Manage ONTAP SDK” published by NetApp.
+
   * libnetlink (optional)
     Used, obviously, for the `netlink' plugin.
     <http://www.linuxfoundation.org/en/Net:Iproute2>
@@ -541,6 +579,14 @@ Prerequisites
     The PostgreSQL C client library used by the `postgresql' plugin.
     <http://www.postgresql.org/>
 
+  * libpython (optional)
+    Used by the `python' plugin. Currently, only 2.3 ≦ Python < 3 is supported.
+    <http://www.python.org/>
+
+  * librouteros (optional)
+    Used by the `routeros' plugin to connect to a device running `RouterOS'.
+    <http://verplant.org/librouteros/>
+
   * librrd (optional)
     Used by the `rrdtool' and `rrdcached' plugins. The latter requires RRDtool
     client support which was added after version 1.3 of RRDtool. Versions 1.0,
diff --git a/TODO b/TODO
index 3c8f49e..009eb7f 100644 (file)
--- a/TODO
+++ b/TODO
@@ -5,9 +5,6 @@
 src/battery.c: commend not working code.
 
 Wishlist:
-* Update the RPM specfile to
-  - build `collectd-apache'
-  - be free of syntax errors.
 * Port nfs module to solaris
 * Port tape module to Linux
 * Port the apple_sensors plugin to Linux/PPC.
index fb68657..f39e9bb 100644 (file)
@@ -4,8 +4,11 @@ if BUILD_WITH_JAVA
 SUBDIRS += java
 endif
 
-EXTRA_DIST = perl/Collectd.pm perl/Makefile.PL perl/Collectd/Makefile.PL \
-               perl/Collectd/Unixsock.pm
+EXTRA_DIST = perl/Makefile.PL \
+            perl/lib/Collectd.pm \
+            perl/lib/Collectd/Unixsock.pm \
+            perl/lib/Collectd/Plugins/Monitorus.pm \
+            perl/lib/Collectd/Plugins/OpenVZ.pm
 
 all-local: @PERL_BINDINGS@
 
@@ -19,16 +22,16 @@ perl: perl/Makefile
        cd perl && $(MAKE)
 
 perl/Makefile: .perl-directory-stamp perl/Makefile.PL \
-       perl/Collectd/Makefile.PL $(top_builddir)/config.status
+       $(top_builddir)/config.status
        cd perl && @PERL@ Makefile.PL PREFIX=$(prefix) @PERL_BINDINGS_OPTIONS@
 
 .perl-directory-stamp:
        if test ! -d perl; then \
-         mkdir -p perl/Collectd; \
+         mkdir -p perl/Collectd/Plugins; \
          cp $(srcdir)/perl/Collectd.pm perl/; \
          cp $(srcdir)/perl/Makefile.PL perl/; \
          cp $(srcdir)/perl/Collectd/Unixsock.pm perl/Collectd/; \
-         cp $(srcdir)/perl/Collectd/Makefile.PL perl/Collectd/; \
+         cp $(srcdir)/perl/Collectd/Plugins/OpenVZ.pm perl/Collectd/Plugins/; \
        fi
        touch $@
 
diff --git a/bindings/perl/Collectd.pm b/bindings/perl/Collectd.pm
deleted file mode 100644 (file)
index 557950c..0000000
+++ /dev/null
@@ -1,648 +0,0 @@
-# collectd - Collectd.pm
-# Copyright (C) 2007-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
-#
-# Author:
-#   Sebastian Harl <sh at tokkee.org>
-
-package Collectd;
-
-use strict;
-use warnings;
-
-use Config;
-
-use threads;
-use threads::shared;
-
-BEGIN {
-       if (! $Config{'useithreads'}) {
-               die "Perl does not support ithreads!";
-       }
-}
-
-require Exporter;
-
-our @ISA = qw( Exporter );
-
-our %EXPORT_TAGS = (
-       'plugin' => [ qw(
-                       plugin_register
-                       plugin_unregister
-                       plugin_dispatch_values
-                       plugin_write
-                       plugin_flush
-                       plugin_flush_one
-                       plugin_flush_all
-                       plugin_dispatch_notification
-                       plugin_log
-       ) ],
-       'types' => [ qw(
-                       TYPE_INIT
-                       TYPE_READ
-                       TYPE_WRITE
-                       TYPE_SHUTDOWN
-                       TYPE_LOG
-                       TYPE_NOTIF
-                       TYPE_FLUSH
-                       TYPE_CONFIG
-                       TYPE_DATASET
-       ) ],
-       'ds_types' => [ qw(
-                       DS_TYPE_COUNTER
-                       DS_TYPE_GAUGE
-       ) ],
-       'log' => [ qw(
-                       ERROR
-                       WARNING
-                       NOTICE
-                       INFO
-                       DEBUG
-                       LOG_ERR
-                       LOG_WARNING
-                       LOG_NOTICE
-                       LOG_INFO
-                       LOG_DEBUG
-       ) ],
-       'filter_chain' => [ qw(
-                       fc_register
-                       FC_MATCH_NO_MATCH
-                       FC_MATCH_MATCHES
-                       FC_TARGET_CONTINUE
-                       FC_TARGET_STOP
-                       FC_TARGET_RETURN
-       ) ],
-       'fc_types' => [ qw(
-                       FC_MATCH
-                       FC_TARGET
-       ) ],
-       'notif' => [ qw(
-                       NOTIF_FAILURE
-                       NOTIF_WARNING
-                       NOTIF_OKAY
-       ) ],
-       'globals' => [ qw(
-                       $hostname_g
-                       $interval_g
-       ) ],
-);
-
-{
-       my %seen;
-       push @{$EXPORT_TAGS{'all'}}, grep {! $seen{$_}++ } @{$EXPORT_TAGS{$_}}
-               foreach keys %EXPORT_TAGS;
-}
-
-# global variables
-our $hostname_g;
-our $interval_g;
-
-Exporter::export_ok_tags ('all');
-
-my @plugins : shared = ();
-my @fc_plugins : shared = ();
-my %cf_callbacks : shared = ();
-
-my %types = (
-       TYPE_INIT,     "init",
-       TYPE_READ,     "read",
-       TYPE_WRITE,    "write",
-       TYPE_SHUTDOWN, "shutdown",
-       TYPE_LOG,      "log",
-       TYPE_NOTIF,    "notify",
-       TYPE_FLUSH,    "flush"
-);
-
-my %fc_types = (
-       FC_MATCH,  "match",
-       FC_TARGET, "target"
-);
-
-my %fc_exec_names = (
-       FC_MATCH,  "match",
-       FC_TARGET, "invoke"
-);
-
-foreach my $type (keys %types) {
-       $plugins[$type] = &share ({});
-}
-
-foreach my $type (keys %fc_types) {
-       $fc_plugins[$type] = &share ({});
-}
-
-sub _log {
-       my $caller = shift;
-       my $lvl    = shift;
-       my $msg    = shift;
-
-       if ("Collectd" eq $caller) {
-               $msg = "perl: $msg";
-       }
-       return plugin_log ($lvl, $msg);
-}
-
-sub ERROR   { _log (scalar caller, LOG_ERR,     shift); }
-sub WARNING { _log (scalar caller, LOG_WARNING, shift); }
-sub NOTICE  { _log (scalar caller, LOG_NOTICE,  shift); }
-sub INFO    { _log (scalar caller, LOG_INFO,    shift); }
-sub DEBUG   { _log (scalar caller, LOG_DEBUG,   shift); }
-
-sub plugin_call_all {
-       my $type = shift;
-
-       my %plugins;
-
-       our $cb_name = undef;
-
-       if (! defined $type) {
-               return;
-       }
-
-       if (TYPE_LOG != $type) {
-               DEBUG ("Collectd::plugin_call: type = \"$type\", args=\"@_\"");
-       }
-
-       if (! defined $plugins[$type]) {
-               ERROR ("Collectd::plugin_call: unknown type \"$type\"");
-               return;
-       }
-
-       {
-               lock %{$plugins[$type]};
-               %plugins = %{$plugins[$type]};
-       }
-
-       foreach my $plugin (keys %plugins) {
-               my $p = $plugins{$plugin};
-
-               my $status = 0;
-
-               if ($p->{'wait_left'} > 0) {
-                       $p->{'wait_left'} -= $interval_g;
-               }
-
-               next if ($p->{'wait_left'} > 0);
-
-               $cb_name = $p->{'cb_name'};
-               $status = call_by_name (@_);
-
-               if (! $status) {
-                       my $err = undef;
-
-                       if ($@) {
-                               $err = $@;
-                       }
-                       else {
-                               $err = "callback returned false";
-                       }
-
-                       if (TYPE_LOG != $type) {
-                               ERROR ("Execution of callback \"$cb_name\" failed: $err");
-                       }
-
-                       $status = 0;
-               }
-
-               if ($status) {
-                       $p->{'wait_left'} = 0;
-                       $p->{'wait_time'} = $interval_g;
-               }
-               elsif (TYPE_READ == $type) {
-                       if ($p->{'wait_time'} < $interval_g) {
-                               $p->{'wait_time'} = $interval_g;
-                       }
-
-                       $p->{'wait_left'} = $p->{'wait_time'};
-                       $p->{'wait_time'} *= 2;
-
-                       if ($p->{'wait_time'} > 86400) {
-                               $p->{'wait_time'} = 86400;
-                       }
-
-                       WARNING ("${plugin}->read() failed with status $status. "
-                               . "Will suspend it for $p->{'wait_left'} seconds.");
-               }
-               elsif (TYPE_INIT == $type) {
-                       ERROR ("${plugin}->init() failed with status $status. "
-                               . "Plugin will be disabled.");
-
-                       foreach my $type (keys %types) {
-                               plugin_unregister ($type, $plugin);
-                       }
-               }
-               elsif (TYPE_LOG != $type) {
-                       WARNING ("${plugin}->$types{$type}() failed with status $status.");
-               }
-       }
-       return 1;
-}
-
-# Collectd::plugin_register (type, name, data).
-#
-# type:
-#   init, read, write, shutdown, data set
-#
-# name:
-#   name of the plugin
-#
-# data:
-#   reference to the plugin's subroutine that does the work or the data set
-#   definition
-sub plugin_register {
-       my $type = shift;
-       my $name = shift;
-       my $data = shift;
-
-       DEBUG ("Collectd::plugin_register: "
-               . "type = \"$type\", name = \"$name\", data = \"$data\"");
-
-       if (! ((defined $type) && (defined $name) && (defined $data))) {
-               ERROR ("Usage: Collectd::plugin_register (type, name, data)");
-               return;
-       }
-
-       if ((! defined $plugins[$type]) && (TYPE_DATASET != $type)
-                       && (TYPE_CONFIG != $type)) {
-               ERROR ("Collectd::plugin_register: Invalid type \"$type\"");
-               return;
-       }
-
-       if ((TYPE_DATASET == $type) && ("ARRAY" eq ref $data)) {
-               return plugin_register_data_set ($name, $data);
-       }
-       elsif ((TYPE_CONFIG == $type) && (! ref $data)) {
-               my $pkg = scalar caller;
-
-               if ($data !~ m/^$pkg\:\:/) {
-                       $data = $pkg . "::" . $data;
-               }
-
-               lock %cf_callbacks;
-               $cf_callbacks{$name} = $data;
-       }
-       elsif ((TYPE_DATASET != $type) && (! ref $data)) {
-               my $pkg = scalar caller;
-
-               my %p : shared;
-
-               if ($data !~ m/^$pkg\:\:/) {
-                       $data = $pkg . "::" . $data;
-               }
-
-               %p = (
-                       wait_time => $interval_g,
-                       wait_left => 0,
-                       cb_name   => $data,
-               );
-
-               lock %{$plugins[$type]};
-               $plugins[$type]->{$name} = \%p;
-       }
-       else {
-               ERROR ("Collectd::plugin_register: Invalid data.");
-               return;
-       }
-       return 1;
-}
-
-sub plugin_unregister {
-       my $type = shift;
-       my $name = shift;
-
-       DEBUG ("Collectd::plugin_unregister: type = \"$type\", name = \"$name\"");
-
-       if (! ((defined $type) && (defined $name))) {
-               ERROR ("Usage: Collectd::plugin_unregister (type, name)");
-               return;
-       }
-
-       if (TYPE_DATASET == $type) {
-               return plugin_unregister_data_set ($name);
-       }
-       elsif (TYPE_CONFIG == $type) {
-               lock %cf_callbacks;
-               delete $cf_callbacks{$name};
-       }
-       elsif (defined $plugins[$type]) {
-               lock %{$plugins[$type]};
-               delete $plugins[$type]->{$name};
-       }
-       else {
-               ERROR ("Collectd::plugin_unregister: Invalid type.");
-               return;
-       }
-}
-
-sub plugin_write {
-       my %args = @_;
-
-       my @plugins    = ();
-       my @datasets   = ();
-       my @valuelists = ();
-
-       if (! defined $args{'valuelists'}) {
-               ERROR ("Collectd::plugin_write: Missing 'valuelists' argument.");
-               return;
-       }
-
-       DEBUG ("Collectd::plugin_write:"
-               . (defined ($args{'plugins'}) ? " plugins = $args{'plugins'}" : "")
-               . (defined ($args{'datasets'}) ? " datasets = $args{'datasets'}" : "")
-               . " valueslists = $args{'valuelists'}");
-
-       if (defined ($args{'plugins'})) {
-               if ("ARRAY" eq ref ($args{'plugins'})) {
-                       @plugins = @{$args{'plugins'}};
-               }
-               else {
-                       @plugins = ($args{'plugins'});
-               }
-       }
-       else {
-               @plugins = (undef);
-       }
-
-       if ("ARRAY" eq ref ($args{'valuelists'})) {
-               @valuelists = @{$args{'valuelists'}};
-       }
-       else {
-               @valuelists = ($args{'valuelists'});
-       }
-
-       if (defined ($args{'datasets'})) {
-               if ("ARRAY" eq ref ($args{'datasets'})) {
-                       @datasets = @{$args{'datasets'}};
-               }
-               else {
-                       @datasets = ($args{'datasets'});
-               }
-       }
-       else {
-               @datasets = (undef) x scalar (@valuelists);
-       }
-
-       if ($#datasets != $#valuelists) {
-               ERROR ("Collectd::plugin_write: Invalid number of datasets.");
-               return;
-       }
-
-       foreach my $plugin (@plugins) {
-               for (my $i = 0; $i < scalar (@valuelists); ++$i) {
-                       _plugin_write ($plugin, $datasets[$i], $valuelists[$i]);
-               }
-       }
-}
-
-sub plugin_flush {
-       my %args = @_;
-
-       my $timeout = -1;
-       my @plugins = ();
-       my @ids     = ();
-
-       DEBUG ("Collectd::plugin_flush:"
-               . (defined ($args{'timeout'}) ? " timeout = $args{'timeout'}" : "")
-               . (defined ($args{'plugins'}) ? " plugins = $args{'plugins'}" : "")
-               . (defined ($args{'identifiers'})
-                       ? " identifiers = $args{'identifiers'}" : ""));
-
-       if (defined ($args{'timeout'}) && ($args{'timeout'} > 0)) {
-               $timeout = $args{'timeout'};
-       }
-
-       if (defined ($args{'plugins'})) {
-               if ("ARRAY" eq ref ($args{'plugins'})) {
-                       @plugins = @{$args{'plugins'}};
-               }
-               else {
-                       @plugins = ($args{'plugins'});
-               }
-       }
-       else {
-               @plugins = (undef);
-       }
-
-       if (defined ($args{'identifiers'})) {
-               if ("ARRAY" eq ref ($args{'identifiers'})) {
-                       @ids = @{$args{'identifiers'}};
-               }
-               else {
-                       @ids = ($args{'identifiers'});
-               }
-       }
-       else {
-               @ids = (undef);
-       }
-
-       foreach my $plugin (@plugins) {
-               foreach my $id (@ids) {
-                       _plugin_flush($plugin, $timeout, $id);
-               }
-       }
-}
-
-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;
-       my $cb_type = shift;
-
-       my %proc;
-
-       our $cb_name = undef;
-       my  $status;
-
-       if (! ((defined $type) && (defined $name) && (defined $cb_type))) {
-               ERROR ("Usage: Collectd::fc_call(type, name, cb_type, ...)");
-               return;
-       }
-
-       if (! defined $fc_plugins[$type]) {
-               ERROR ("Collectd::fc_call: Invalid type \"$type\"");
-               return;
-       }
-
-       if (! defined $fc_plugins[$type]->{$name}) {
-               ERROR ("Collectd::fc_call: Unknown "
-                       . ($type == FC_MATCH ? "match" : "target")
-                       . " \"$name\"");
-               return;
-       }
-
-       DEBUG ("Collectd::fc_call: "
-               . "type = \"$type\", name = \"$name\", cb_type = \"$cb_type\"");
-
-       {
-               lock %{$fc_plugins[$type]};
-               %proc = %{$fc_plugins[$type]->{$name}};
-       }
-
-       if (FC_CB_EXEC == $cb_type) {
-               $cb_name = $proc{$fc_exec_names{$type}};
-       }
-       elsif (FC_CB_CREATE == $cb_type) {
-               if (defined $proc{'create'}) {
-                       $cb_name = $proc{'create'};
-               }
-               else {
-                       return 1;
-               }
-       }
-       elsif (FC_CB_DESTROY == $cb_type) {
-               if (defined $proc{'destroy'}) {
-                       $cb_name = $proc{'destroy'};
-               }
-               else {
-                       return 1;
-               }
-       }
-
-       $status = call_by_name (@_);
-
-       if ($status < 0) {
-               my $err = undef;
-
-               if ($@) {
-                       $err = $@;
-               }
-               else {
-                       $err = "callback returned false";
-               }
-
-               ERROR ("Execution of fc callback \"$cb_name\" failed: $err");
-               return;
-       }
-       return $status;
-}
-
-sub fc_register {
-       my $type = shift;
-       my $name = shift;
-       my $proc = shift;
-
-       my %fc : shared;
-
-       DEBUG ("Collectd::fc_register: "
-               . "type = \"$type\", name = \"$name\", proc = \"$proc\"");
-
-       if (! ((defined $type) && (defined $name) && (defined $proc))) {
-               ERROR ("Usage: Collectd::fc_register(type, name, proc)");
-               return;
-       }
-
-       if (! defined $fc_plugins[$type]) {
-               ERROR ("Collectd::fc_register: Invalid type \"$type\"");
-               return;
-       }
-
-       if (("HASH" ne ref ($proc)) || (! defined $proc->{$fc_exec_names{$type}})
-                       || ("" ne ref ($proc->{$fc_exec_names{$type}}))) {
-               ERROR ("Collectd::fc_register: Invalid proc.");
-               return;
-       }
-
-       for my $p (qw( create destroy )) {
-               if ((defined $proc->{$p}) && ("" ne ref ($proc->{$p}))) {
-                       ERROR ("Collectd::fc_register: Invalid proc.");
-                       return;
-               }
-       }
-
-       %fc = %$proc;
-
-       foreach my $p (keys %fc) {
-               my $pkg = scalar caller;
-
-               if ($p !~ m/^(create|destroy|$fc_exec_names{$type})$/) {
-                       next;
-               }
-
-               if ($fc{$p} !~ m/^$pkg\:\:/) {
-                       $fc{$p} = $pkg . "::" . $fc{$p};
-               }
-       }
-
-       lock %{$fc_plugins[$type]};
-       if (defined $fc_plugins[$type]->{$name}) {
-               WARNING ("Collectd::fc_register: Overwriting previous "
-                       . "definition of match \"$name\".");
-       }
-
-       if (! _fc_register ($type, $name)) {
-               ERROR ("Collectd::fc_register: Failed to register \"$name\".");
-               return;
-       }
-
-       $fc_plugins[$type]->{$name} = \%fc;
-       return 1;
-}
-
-sub _plugin_dispatch_config {
-       my $plugin = shift;
-       my $config = shift;
-
-       our $cb_name = undef;
-
-       if (! (defined ($plugin) && defined ($config))) {
-               return;
-       }
-
-       if (! defined $cf_callbacks{$plugin}) {
-               WARNING ("Found a configuration for the \"$plugin\" plugin, but "
-                       . "the plugin isn't loaded or didn't register "
-                       . "a configuration callback.");
-               return;
-       }
-
-       {
-               lock %cf_callbacks;
-               $cb_name = $cf_callbacks{$plugin};
-       }
-       call_by_name ($config);
-}
-
-1;
-
-# vim: set sw=4 ts=4 tw=78 noexpandtab :
-
diff --git a/bindings/perl/Collectd/Makefile.PL b/bindings/perl/Collectd/Makefile.PL
deleted file mode 100644 (file)
index baf7166..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-use ExtUtils::MakeMaker;
-
-WriteMakefile(
-       'NAME'          => 'Collectd::Unixsock',
-       'AUTHOR'        => 'Florian Forster <octo@verplant.org>',
-);
-
-# vim: set sw=4 ts=4 tw=78 noexpandtab :
diff --git a/bindings/perl/Collectd/Unixsock.pm b/bindings/perl/Collectd/Unixsock.pm
deleted file mode 100644 (file)
index 199a47c..0000000
+++ /dev/null
@@ -1,656 +0,0 @@
-#
-# collectd - Collectd::Unixsock
-# Copyright (C) 2007,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
-#
-# Author:
-#   Florian octo Forster <octo at verplant.org>
-#
-
-package Collectd::Unixsock;
-
-=head1 NAME
-
-Collectd::Unixsock - Abstraction layer for accessing the functionality by
-collectd's unixsock plugin.
-
-=head1 SYNOPSIS
-
-  use Collectd::Unixsock ();
-
-  my $sock = Collectd::Unixsock->new ($path);
-
-  my $value = $sock->getval (%identifier);
-  $sock->putval (%identifier,
-                 time => time (),
-                values => [123, 234, 345]);
-
-  $sock->destroy ();
-
-=head1 DESCRIPTION
-
-collectd's unixsock plugin allows external programs to access the values it has
-collected or received and to submit own values. This Perl-module is simply a
-little abstraction layer over this interface to make it even easier for
-programmers to interact with the daemon.
-
-=cut
-
-use strict;
-use warnings;
-
-#use constant { NOTIF_FAILURE => 1, NOTIF_WARNING => 2, NOTIF_OKAY => 4 };
-
-use Carp (qw(cluck confess));
-use IO::Socket::UNIX;
-use Regexp::Common (qw(number));
-
-our $Debug = 0;
-
-return (1);
-
-sub _debug
-{
-       if (!$Debug)
-       {
-               return;
-       }
-       print @_;
-}
-
-sub _create_socket
-{
-       my $path = shift;
-       my $sock = IO::Socket::UNIX->new (Type => SOCK_STREAM, Peer => $path);
-       if (!$sock)
-       {
-               cluck ("Cannot open UNIX-socket $path: $!");
-               return;
-       }
-       return ($sock);
-} # _create_socket
-
-=head1 VALUE IDENTIFIERS
-
-The values in the collectd are identified using an five-tuple (host, plugin,
-plugin-instance, type, type-instance) where only plugin-instance and
-type-instance may be NULL (or undefined). Many functions expect an
-I<%identifier> hash that has at least the members B<host>, B<plugin>, and
-B<type>, possibly completed by B<plugin_instance> and B<type_instance>.
-
-Usually you can pass this hash as follows:
-
-  $obj->method (host => $host, plugin => $plugin, type => $type, %other_args);
-
-=cut
-
-sub _create_identifier
-{
-       my $args = shift;
-       my $host;
-       my $plugin;
-       my $type;
-
-       if (!$args->{'host'} || !$args->{'plugin'} || !$args->{'type'})
-       {
-               cluck ("Need `host', `plugin' and `type'");
-               return;
-       }
-
-       $host = $args->{'host'};
-       $plugin = $args->{'plugin'};
-       $plugin .= '-' . $args->{'plugin_instance'} if (defined ($args->{'plugin_instance'}));
-       $type = $args->{'type'};
-       $type .= '-' . $args->{'type_instance'} if (defined ($args->{'type_instance'}));
-
-       return ("$host/$plugin/$type");
-} # _create_identifier
-
-sub _parse_identifier
-{
-       my $string = shift;
-       my $host;
-       my $plugin;
-       my $plugin_instance;
-       my $type;
-       my $type_instance;
-       my $ident;
-
-       ($host, $plugin, $type) = split ('/', $string);
-
-       ($plugin, $plugin_instance) = split ('-', $plugin, 2);
-       ($type, $type_instance) = split ('-', $type, 2);
-
-       $ident =
-       {
-               host => $host,
-               plugin => $plugin,
-               type => $type
-       };
-       $ident->{'plugin_instance'} = $plugin_instance if (defined ($plugin_instance));
-       $ident->{'type_instance'} = $type_instance if (defined ($type_instance));
-
-       return ($ident);
-} # _parse_identifier
-
-sub _escape_argument
-{
-       my $string = shift;
-
-       if ($string =~ m/^\w+$/)
-       {
-               return ("$string");
-       }
-
-       $string =~ s#\\#\\\\#g;
-       $string =~ s#"#\\"#g;
-       $string = "\"$string\"";
-
-       return ($string);
-}
-
-=head1 PUBLIC METHODS
-
-=over 4
-
-=item I<$obj> = Collectd::Unixsock->B<new> ([I<$path>]);
-
-Creates a new connection to the daemon. The optional I<$path> argument gives
-the path to the UNIX socket of the C<unixsock plugin> and defaults to
-F</var/run/collectd-unixsock>. Returns the newly created object on success and
-false on error.
-
-=cut
-
-sub new
-{
-       my $pkg = shift;
-       my $path = @_ ? shift : '/var/run/collectd-unixsock';
-       my $sock = _create_socket ($path) or return;
-       my $obj = bless (
-               {
-                       path => $path,
-                       sock => $sock,
-                       error => 'No error'
-               }, $pkg);
-       return ($obj);
-} # new
-
-=item I<$res> = I<$obj>-E<gt>B<getval> (I<%identifier>);
-
-Requests a value-list from the daemon. On success a hash-ref is returned with
-the name of each data-source as the key and the according value as, well, the
-value. On error false is returned.
-
-=cut
-
-sub getval # {{{
-{
-       my $obj = shift;
-       my %args = @_;
-
-       my $status;
-       my $fh = $obj->{'sock'} or confess ('object has no filehandle');
-       my $msg;
-       my $identifier;
-
-       my $ret = {};
-
-       $identifier = _create_identifier (\%args) or return;
-
-       $msg = 'GETVAL ' . _escape_argument ($identifier) . "\n";
-       _debug "-> $msg";
-       print $fh $msg;
-
-       $msg = <$fh>;
-       chomp ($msg);
-       _debug "<- $msg\n";
-
-       ($status, $msg) = split (' ', $msg, 2);
-       if ($status <= 0)
-       {
-               $obj->{'error'} = $msg;
-               return;
-       }
-
-       for (my $i = 0; $i < $status; $i++)
-       {
-               my $entry = <$fh>;
-               chomp ($entry);
-               _debug "<- $entry\n";
-
-               if ($entry =~ m/^(\w+)=NaN$/)
-               {
-                       $ret->{$1} = undef;
-               }
-               elsif ($entry =~ m/^(\w+)=($RE{num}{real})$/)
-               {
-                       $ret->{$1} = 0.0 + $2;
-               }
-       }
-
-       return ($ret);
-} # }}} sub getval
-
-=item I<$res> = I<$obj>-E<gt>B<getthreshold> (I<%identifier>);
-
-Requests a threshold from the daemon. On success a hash-ref is returned with
-the threshold data. On error false is returned.
-
-=cut
-
-sub getthreshold # {{{
-{
-       my $obj = shift;
-       my %args = @_;
-
-       my $status;
-       my $fh = $obj->{'sock'} or confess ('object has no filehandle');
-       my $msg;
-       my $identifier;
-
-       my $ret = {};
-
-       $identifier = _create_identifier (\%args) or return;
-
-       $msg = 'GETTHRESHOLD ' . _escape_argument ($identifier) . "\n";
-       _debug "-> $msg";
-       print $fh $msg;
-
-       $msg = <$fh>;
-       chomp ($msg);
-       _debug "<- $msg\n";
-
-       ($status, $msg) = split (' ', $msg, 2);
-       if ($status <= 0)
-       {
-               $obj->{'error'} = $msg;
-               return;
-       }
-
-       for (my $i = 0; $i < $status; $i++)
-       {
-               my $entry = <$fh>;
-               chomp ($entry);
-               _debug "<- $entry\n";
-
-               if ($entry =~ m/^([^:]+):\s*(\S.*)$/)
-               {
-                       my $key = $1;
-                       my $value = $2;
-
-                       $key =~ s/^\s+//;
-                       $key =~ s/\s+$//;
-
-                       $ret->{$key} = $value;
-               }
-       }
-
-       return ($ret);
-} # }}} sub getthreshold
-
-=item I<$obj>-E<gt>B<putval> (I<%identifier>, B<time> =E<gt> I<$time>, B<values> =E<gt> [...]);
-
-Submits a value-list to the daemon. If the B<time> argument is omitted
-C<time()> is used. The required argument B<values> is a reference to an array
-of values that is to be submitted. The number of values must match the number
-of values expected for the given B<type> (see L<VALUE IDENTIFIERS>), though
-this is checked by the daemon, not the Perl module. Also, gauge data-sources
-(e.E<nbsp>g. system-load) may be C<undef>. Returns true upon success and false
-otherwise.
-
-=cut
-
-sub putval
-{
-       my $obj = shift;
-       my %args = @_;
-
-       my $status;
-       my $fh = $obj->{'sock'} or confess;
-       my $msg;
-       my $identifier;
-       my $values;
-       my $interval = "";
-
-       if (defined $args{'interval'})
-       {
-               $interval = ' interval='
-               . _escape_argument ($args{'interval'});
-       }
-
-       $identifier = _create_identifier (\%args) or return;
-       if (!$args{'values'})
-       {
-               cluck ("Need argument `values'");
-               return;
-       }
-
-       if (!ref ($args{'values'}))
-       {
-               $values = $args{'values'};
-       }
-       else
-       {
-               my $time;
-
-               if ("ARRAY" ne ref ($args{'values'}))
-               {
-                       cluck ("Invalid `values' argument (expected an array ref)");
-                       return;
-               }
-
-               if (! scalar @{$args{'values'}})
-               {
-                       cluck ("Empty `values' array");
-                       return;
-               }
-
-               $time = $args{'time'} ? $args{'time'} : time ();
-               $values = join (':', $time, map { defined ($_) ? $_ : 'U' } (@{$args{'values'}}));
-       }
-
-       $msg = 'PUTVAL '
-       . _escape_argument ($identifier)
-       . $interval
-       . ' ' . _escape_argument ($values) . "\n";
-       _debug "-> $msg";
-       print $fh $msg;
-
-       $msg = <$fh>;
-       chomp ($msg);
-       _debug "<- $msg\n";
-
-       ($status, $msg) = split (' ', $msg, 2);
-       return (1) if ($status == 0);
-
-       $obj->{'error'} = $msg;
-       return;
-} # putval
-
-=item I<$res> = I<$obj>-E<gt>B<listval> ()
-
-Queries a list of values from the daemon. The list is returned as an array of
-hash references, where each hash reference is a valid identifier. The C<time>
-member of each hash holds the epoch value of the last update of that value.
-
-=cut
-
-sub listval
-{
-       my $obj = shift;
-       my $msg;
-       my @ret = ();
-       my $status;
-       my $fh = $obj->{'sock'} or confess;
-
-       _debug "LISTVAL\n";
-       print $fh "LISTVAL\n";
-
-       $msg = <$fh>;
-       chomp ($msg);
-       _debug "<- $msg\n";
-       ($status, $msg) = split (' ', $msg, 2);
-       if ($status < 0)
-       {
-               $obj->{'error'} = $msg;
-               return;
-       }
-
-       for (my $i = 0; $i < $status; $i++)
-       {
-               my $time;
-               my $ident;
-
-               $msg = <$fh>;
-               chomp ($msg);
-               _debug "<- $msg\n";
-
-               ($time, $ident) = split (' ', $msg, 2);
-
-               $ident = _parse_identifier ($ident);
-               $ident->{'time'} = int ($time);
-
-               push (@ret, $ident);
-       } # for (i = 0 .. $status)
-
-       return (@ret);
-} # listval
-
-=item I<$res> = I<$obj>-E<gt>B<putnotif> (B<severity> =E<gt> I<$severity>, B<message> =E<gt> I<$message>, ...);
-
-Submits a notification to the daemon.
-
-Valid options are:
-
-=over 4
-
-=item B<severity>
-
-Sets the severity of the notification. The value must be one of the following
-strings: C<failure>, C<warning>, or C<okay>. Case does not matter. This option
-is mandatory.
-
-=item B<message>
-
-Sets the message of the notification. This option is mandatory.
-
-=item B<time>
-
-Sets the time. If omitted, C<time()> is used.
-
-=item I<Value identifier>
-
-All the other fields of the value identifiers, B<host>, B<plugin>,
-B<plugin_instance>, B<type>, and B<type_instance>, are optional. When given,
-the notification is associated with the performance data of that identifier.
-For more details, please see L<collectd-unixsock(5)>.
-
-=back
-
-=cut
-
-sub putnotif
-{
-       my $obj = shift;
-       my %args = @_;
-
-       my $status;
-       my $fh = $obj->{'sock'} or confess;
-
-       my $msg; # message sent to the socket
-       
-       if (!$args{'message'})
-       {
-               cluck ("Need argument `message'");
-               return;
-       }
-       if (!$args{'severity'})
-       {
-               cluck ("Need argument `severity'");
-               return;
-       }
-       $args{'severity'} = lc ($args{'severity'});
-       if (($args{'severity'} ne 'failure')
-               && ($args{'severity'} ne 'warning')
-               && ($args{'severity'} ne 'okay'))
-       {
-               cluck ("Invalid `severity: " . $args{'severity'});
-               return;
-       }
-
-       if (!$args{'time'})
-       {
-               $args{'time'} = time ();
-       }
-       
-       $msg = 'PUTNOTIF '
-       . join (' ', map { $_ . '=' . _escape_argument ($args{$_}) } (keys %args))
-       . "\n";
-
-       _debug "-> $msg";
-       print $fh $msg;
-
-       $msg = <$fh>;
-       chomp ($msg);
-       _debug "<- $msg\n";
-
-       ($status, $msg) = split (' ', $msg, 2);
-       return (1) if ($status == 0);
-
-       $obj->{'error'} = $msg;
-       return;
-} # putnotif
-
-=item I<$obj>-E<gt>B<flush> (B<timeout> =E<gt> I<$timeout>, B<plugins> =E<gt> [...], B<identifier>  =E<gt> [...]);
-
-Flush cached data.
-
-Valid options are:
-
-=over 4
-
-=item B<timeout>
-
-If this option is specified, only data older than I<$timeout> seconds is
-flushed.
-
-=item B<plugins>
-
-If this option is specified, only the selected plugins will be flushed. The
-argument is a reference to an array of strings.
-
-=item B<identifier>
-
-If this option is specified, only the given identifier(s) will be flushed. The
-argument is a reference to an array of identifiers. Identifiers, in this case,
-are hash references and have the members as outlined in L<VALUE IDENTIFIERS>.
-
-=back
-
-=cut
-
-sub flush
-{
-       my $obj  = shift;
-       my %args = @_;
-
-       my $fh = $obj->{'sock'} or confess;
-
-       my $status = 0;
-       my $msg    = "FLUSH";
-
-       if (defined ($args{'timeout'}))
-       {
-               $msg .= " timeout=" . $args{'timeout'};
-       }
-
-       if ($args{'plugins'})
-       {
-               foreach my $plugin (@{$args{'plugins'}})
-               {
-                       $msg .= " plugin=" . $plugin;
-               }
-       }
-
-       if ($args{'identifier'})
-       {
-               for (@{$args{'identifier'}})
-               {
-                       my $identifier = $_;
-                       my $ident_str;
-
-                       if (ref ($identifier) ne 'HASH')
-                       {
-                               cluck ("The argument of the `identifier' "
-                                       . "option must be an array reference "
-                                       . "of hash references.");
-                               return;
-                       }
-
-                       $ident_str = _create_identifier ($identifier);
-                       if (!$ident_str)
-                       {
-                               return;
-                       }
-
-                       $msg .= ' identifier=' . _escape_argument ($ident_str);
-               }
-       }
-
-       $msg .= "\n";
-
-       _debug "-> $msg";
-       print $fh $msg;
-
-       $msg = <$fh>;
-       chomp ($msg);
-       _debug "<- $msg\n";
-
-       ($status, $msg) = split (' ', $msg, 2);
-       return (1) if ($status == 0);
-
-       $obj->{'error'} = $msg;
-       return;
-}
-
-sub error
-{
-       my $obj = shift;
-       if ($obj->{'error'})
-       {
-               return ($obj->{'error'});
-       }
-       return;
-}
-
-=item I<$obj>-E<gt>destroy ();
-
-Closes the socket before the object is destroyed. This function is also
-automatically called then the object goes out of scope.
-
-=back
-
-=cut
-
-sub destroy
-{
-       my $obj = shift;
-       if ($obj->{'sock'})
-       {
-               close ($obj->{'sock'});
-               delete ($obj->{'sock'});
-       }
-}
-
-sub DESTROY
-{
-       my $obj = shift;
-       $obj->destroy ();
-}
-
-=head1 SEE ALSO
-
-L<collectd(1)>,
-L<collectd.conf(5)>,
-L<collectd-unixsock(5)>
-
-=head1 AUTHOR
-
-Florian octo Forster E<lt>octo@verplant.orgE<gt>
-
-=cut
-
-# vim: set fdm=marker :
diff --git a/bindings/perl/lib/Collectd.pm b/bindings/perl/lib/Collectd.pm
new file mode 100644 (file)
index 0000000..f1b5d85
--- /dev/null
@@ -0,0 +1,662 @@
+# collectd - Collectd.pm
+# Copyright (C) 2007-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
+#
+# Author:
+#   Sebastian Harl <sh at tokkee.org>
+
+package Collectd;
+
+use strict;
+use warnings;
+
+use Config;
+
+use threads;
+use threads::shared;
+
+BEGIN {
+       if (! $Config{'useithreads'}) {
+               die "Perl does not support ithreads!";
+       }
+}
+
+require Exporter;
+
+our @ISA = qw( Exporter );
+
+our %EXPORT_TAGS = (
+       'plugin' => [ qw(
+                       plugin_register
+                       plugin_unregister
+                       plugin_dispatch_values
+                       plugin_write
+                       plugin_flush
+                       plugin_flush_one
+                       plugin_flush_all
+                       plugin_dispatch_notification
+                       plugin_log
+       ) ],
+       'types' => [ qw(
+                       TYPE_INIT
+                       TYPE_READ
+                       TYPE_WRITE
+                       TYPE_SHUTDOWN
+                       TYPE_LOG
+                       TYPE_NOTIF
+                       TYPE_FLUSH
+                       TYPE_CONFIG
+                       TYPE_DATASET
+       ) ],
+       'ds_types' => [ qw(
+                       DS_TYPE_COUNTER
+                       DS_TYPE_GAUGE
+       ) ],
+       'log' => [ qw(
+                       ERROR
+                       WARNING
+                       NOTICE
+                       INFO
+                       DEBUG
+                       LOG_ERR
+                       LOG_WARNING
+                       LOG_NOTICE
+                       LOG_INFO
+                       LOG_DEBUG
+       ) ],
+       'filter_chain' => [ qw(
+                       fc_register
+                       FC_MATCH_NO_MATCH
+                       FC_MATCH_MATCHES
+                       FC_TARGET_CONTINUE
+                       FC_TARGET_STOP
+                       FC_TARGET_RETURN
+       ) ],
+       'fc_types' => [ qw(
+                       FC_MATCH
+                       FC_TARGET
+       ) ],
+       'notif' => [ qw(
+                       NOTIF_FAILURE
+                       NOTIF_WARNING
+                       NOTIF_OKAY
+       ) ],
+       'globals' => [ qw(
+                       $hostname_g
+                       $interval_g
+       ) ],
+);
+
+{
+       my %seen;
+       push @{$EXPORT_TAGS{'all'}}, grep {! $seen{$_}++ } @{$EXPORT_TAGS{$_}}
+               foreach keys %EXPORT_TAGS;
+}
+
+# global variables
+our $hostname_g;
+our $interval_g;
+
+Exporter::export_ok_tags ('all');
+
+my @plugins : shared = ();
+my @fc_plugins : shared = ();
+my %cf_callbacks : shared = ();
+
+my %types = (
+       TYPE_CONFIG,   "config",
+       TYPE_INIT,     "init",
+       TYPE_READ,     "read",
+       TYPE_WRITE,    "write",
+       TYPE_SHUTDOWN, "shutdown",
+       TYPE_LOG,      "log",
+       TYPE_NOTIF,    "notify",
+       TYPE_FLUSH,    "flush"
+);
+
+my %fc_types = (
+       FC_MATCH,  "match",
+       FC_TARGET, "target"
+);
+
+my %fc_exec_names = (
+       FC_MATCH,  "match",
+       FC_TARGET, "invoke"
+);
+
+my %fc_cb_types = (
+       FC_CB_EXEC, "exec",
+       FC_CB_CREATE, "create",
+       FC_CB_DESTROY, "destroy"
+);
+
+foreach my $type (keys %types) {
+       $plugins[$type] = &share ({});
+}
+
+foreach my $type (keys %fc_types) {
+       $fc_plugins[$type] = &share ({});
+}
+
+sub _log {
+       my $caller = shift;
+       my $lvl    = shift;
+       my $msg    = shift;
+
+       if ("Collectd" eq $caller) {
+               $msg = "perl: $msg";
+       }
+       return plugin_log ($lvl, $msg);
+}
+
+sub ERROR   { _log (scalar caller, LOG_ERR,     shift); }
+sub WARNING { _log (scalar caller, LOG_WARNING, shift); }
+sub NOTICE  { _log (scalar caller, LOG_NOTICE,  shift); }
+sub INFO    { _log (scalar caller, LOG_INFO,    shift); }
+sub DEBUG   { _log (scalar caller, LOG_DEBUG,   shift); }
+
+sub plugin_call_all {
+       my $type = shift;
+
+       my %plugins;
+
+       our $cb_name = undef;
+
+       if (! defined $type) {
+               return;
+       }
+
+       if (TYPE_LOG != $type) {
+               DEBUG ("Collectd::plugin_call: type = \"$type\" ("
+                       . $types{$type} . "), args=\""
+                       . join(', ', map { defined($_) ? $_ : '<undef>' } @_) . "\"");
+       }
+
+       if (! defined $plugins[$type]) {
+               ERROR ("Collectd::plugin_call: unknown type \"$type\"");
+               return;
+       }
+
+       {
+               lock %{$plugins[$type]};
+               %plugins = %{$plugins[$type]};
+       }
+
+       foreach my $plugin (keys %plugins) {
+               my $p = $plugins{$plugin};
+
+               my $status = 0;
+
+               if ($p->{'wait_left'} > 0) {
+                       $p->{'wait_left'} -= $interval_g;
+               }
+
+               next if ($p->{'wait_left'} > 0);
+
+               $cb_name = $p->{'cb_name'};
+               $status = call_by_name (@_);
+
+               if (! $status) {
+                       my $err = undef;
+
+                       if ($@) {
+                               $err = $@;
+                       }
+                       else {
+                               $err = "callback returned false";
+                       }
+
+                       if (TYPE_LOG != $type) {
+                               ERROR ("Execution of callback \"$cb_name\" failed: $err");
+                       }
+
+                       $status = 0;
+               }
+
+               if ($status) {
+                       $p->{'wait_left'} = 0;
+                       $p->{'wait_time'} = $interval_g;
+               }
+               elsif (TYPE_READ == $type) {
+                       if ($p->{'wait_time'} < $interval_g) {
+                               $p->{'wait_time'} = $interval_g;
+                       }
+
+                       $p->{'wait_left'} = $p->{'wait_time'};
+                       $p->{'wait_time'} *= 2;
+
+                       if ($p->{'wait_time'} > 86400) {
+                               $p->{'wait_time'} = 86400;
+                       }
+
+                       WARNING ("${plugin}->read() failed with status $status. "
+                               . "Will suspend it for $p->{'wait_left'} seconds.");
+               }
+               elsif (TYPE_INIT == $type) {
+                       ERROR ("${plugin}->init() failed with status $status. "
+                               . "Plugin will be disabled.");
+
+                       foreach my $type (keys %types) {
+                               plugin_unregister ($type, $plugin);
+                       }
+               }
+               elsif (TYPE_LOG != $type) {
+                       WARNING ("${plugin}->$types{$type}() failed with status $status.");
+               }
+       }
+       return 1;
+}
+
+# Collectd::plugin_register (type, name, data).
+#
+# type:
+#   init, read, write, shutdown, data set
+#
+# name:
+#   name of the plugin
+#
+# data:
+#   reference to the plugin's subroutine that does the work or the data set
+#   definition
+sub plugin_register {
+       my $type = shift;
+       my $name = shift;
+       my $data = shift;
+
+       DEBUG ("Collectd::plugin_register: "
+               . "type = \"$type\" (" . $types{$type}
+               . "), name = \"$name\", data = \"$data\"");
+
+       if (! ((defined $type) && (defined $name) && (defined $data))) {
+               ERROR ("Usage: Collectd::plugin_register (type, name, data)");
+               return;
+       }
+
+       if ((! defined $plugins[$type]) && (TYPE_DATASET != $type)
+                       && (TYPE_CONFIG != $type)) {
+               ERROR ("Collectd::plugin_register: Invalid type \"$type\"");
+               return;
+       }
+
+       if ((TYPE_DATASET == $type) && ("ARRAY" eq ref $data)) {
+               return plugin_register_data_set ($name, $data);
+       }
+       elsif ((TYPE_CONFIG == $type) && (! ref $data)) {
+               my $pkg = scalar caller;
+
+               if ($data !~ m/^$pkg\:\:/) {
+                       $data = $pkg . "::" . $data;
+               }
+
+               lock %cf_callbacks;
+               $cf_callbacks{$name} = $data;
+       }
+       elsif ((TYPE_DATASET != $type) && (! ref $data)) {
+               my $pkg = scalar caller;
+
+               my %p : shared;
+
+               if ($data !~ m/^$pkg\:\:/) {
+                       $data = $pkg . "::" . $data;
+               }
+
+               %p = (
+                       wait_time => $interval_g,
+                       wait_left => 0,
+                       cb_name   => $data,
+               );
+
+               lock %{$plugins[$type]};
+               $plugins[$type]->{$name} = \%p;
+       }
+       else {
+               ERROR ("Collectd::plugin_register: Invalid data.");
+               return;
+       }
+       return 1;
+}
+
+sub plugin_unregister {
+       my $type = shift;
+       my $name = shift;
+
+       DEBUG ("Collectd::plugin_unregister: type = \"$type\" ("
+               . $types{$type} . "), name = \"$name\"");
+
+       if (! ((defined $type) && (defined $name))) {
+               ERROR ("Usage: Collectd::plugin_unregister (type, name)");
+               return;
+       }
+
+       if (TYPE_DATASET == $type) {
+               return plugin_unregister_data_set ($name);
+       }
+       elsif (TYPE_CONFIG == $type) {
+               lock %cf_callbacks;
+               delete $cf_callbacks{$name};
+       }
+       elsif (defined $plugins[$type]) {
+               lock %{$plugins[$type]};
+               delete $plugins[$type]->{$name};
+       }
+       else {
+               ERROR ("Collectd::plugin_unregister: Invalid type.");
+               return;
+       }
+}
+
+sub plugin_write {
+       my %args = @_;
+
+       my @plugins    = ();
+       my @datasets   = ();
+       my @valuelists = ();
+
+       if (! defined $args{'valuelists'}) {
+               ERROR ("Collectd::plugin_write: Missing 'valuelists' argument.");
+               return;
+       }
+
+       DEBUG ("Collectd::plugin_write:"
+               . (defined ($args{'plugins'}) ? " plugins = $args{'plugins'}" : "")
+               . (defined ($args{'datasets'}) ? " datasets = $args{'datasets'}" : "")
+               . " valueslists = $args{'valuelists'}");
+
+       if (defined ($args{'plugins'})) {
+               if ("ARRAY" eq ref ($args{'plugins'})) {
+                       @plugins = @{$args{'plugins'}};
+               }
+               else {
+                       @plugins = ($args{'plugins'});
+               }
+       }
+       else {
+               @plugins = (undef);
+       }
+
+       if ("ARRAY" eq ref ($args{'valuelists'})) {
+               @valuelists = @{$args{'valuelists'}};
+       }
+       else {
+               @valuelists = ($args{'valuelists'});
+       }
+
+       if (defined ($args{'datasets'})) {
+               if ("ARRAY" eq ref ($args{'datasets'})) {
+                       @datasets = @{$args{'datasets'}};
+               }
+               else {
+                       @datasets = ($args{'datasets'});
+               }
+       }
+       else {
+               @datasets = (undef) x scalar (@valuelists);
+       }
+
+       if ($#datasets != $#valuelists) {
+               ERROR ("Collectd::plugin_write: Invalid number of datasets.");
+               return;
+       }
+
+       foreach my $plugin (@plugins) {
+               for (my $i = 0; $i < scalar (@valuelists); ++$i) {
+                       _plugin_write ($plugin, $datasets[$i], $valuelists[$i]);
+               }
+       }
+}
+
+sub plugin_flush {
+       my %args = @_;
+
+       my $timeout = -1;
+       my @plugins = ();
+       my @ids     = ();
+
+       DEBUG ("Collectd::plugin_flush:"
+               . (defined ($args{'timeout'}) ? " timeout = $args{'timeout'}" : "")
+               . (defined ($args{'plugins'}) ? " plugins = $args{'plugins'}" : "")
+               . (defined ($args{'identifiers'})
+                       ? " identifiers = $args{'identifiers'}" : ""));
+
+       if (defined ($args{'timeout'}) && ($args{'timeout'} > 0)) {
+               $timeout = $args{'timeout'};
+       }
+
+       if (defined ($args{'plugins'})) {
+               if ("ARRAY" eq ref ($args{'plugins'})) {
+                       @plugins = @{$args{'plugins'}};
+               }
+               else {
+                       @plugins = ($args{'plugins'});
+               }
+       }
+       else {
+               @plugins = (undef);
+       }
+
+       if (defined ($args{'identifiers'})) {
+               if ("ARRAY" eq ref ($args{'identifiers'})) {
+                       @ids = @{$args{'identifiers'}};
+               }
+               else {
+                       @ids = ($args{'identifiers'});
+               }
+       }
+       else {
+               @ids = (undef);
+       }
+
+       foreach my $plugin (@plugins) {
+               foreach my $id (@ids) {
+                       _plugin_flush($plugin, $timeout, $id);
+               }
+       }
+}
+
+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;
+       my $cb_type = shift;
+
+       my %proc;
+
+       our $cb_name = undef;
+       my  $status;
+
+       if (! ((defined $type) && (defined $name) && (defined $cb_type))) {
+               ERROR ("Usage: Collectd::fc_call(type, name, cb_type, ...)");
+               return;
+       }
+
+       if (! defined $fc_plugins[$type]) {
+               ERROR ("Collectd::fc_call: Invalid type \"$type\"");
+               return;
+       }
+
+       if (! defined $fc_plugins[$type]->{$name}) {
+               ERROR ("Collectd::fc_call: Unknown "
+                       . ($type == FC_MATCH ? "match" : "target")
+                       . " \"$name\"");
+               return;
+       }
+
+       DEBUG ("Collectd::fc_call: "
+               . "type = \"$type\" (" . $fc_types{$type}
+               . "), name = \"$name\", cb_type = \"$cb_type\" ("
+               . $fc_cb_types{$cb_type} . ")");
+
+       {
+               lock %{$fc_plugins[$type]};
+               %proc = %{$fc_plugins[$type]->{$name}};
+       }
+
+       if (FC_CB_EXEC == $cb_type) {
+               $cb_name = $proc{$fc_exec_names{$type}};
+       }
+       elsif (FC_CB_CREATE == $cb_type) {
+               if (defined $proc{'create'}) {
+                       $cb_name = $proc{'create'};
+               }
+               else {
+                       return 1;
+               }
+       }
+       elsif (FC_CB_DESTROY == $cb_type) {
+               if (defined $proc{'destroy'}) {
+                       $cb_name = $proc{'destroy'};
+               }
+               else {
+                       return 1;
+               }
+       }
+
+       $status = call_by_name (@_);
+
+       if ($status < 0) {
+               my $err = undef;
+
+               if ($@) {
+                       $err = $@;
+               }
+               else {
+                       $err = "callback returned false";
+               }
+
+               ERROR ("Execution of fc callback \"$cb_name\" failed: $err");
+               return;
+       }
+       return $status;
+}
+
+sub fc_register {
+       my $type = shift;
+       my $name = shift;
+       my $proc = shift;
+
+       my %fc : shared;
+
+       DEBUG ("Collectd::fc_register: "
+               . "type = \"$type\" (" . $fc_types{$type}
+               . "), name = \"$name\", proc = \"$proc\"");
+
+       if (! ((defined $type) && (defined $name) && (defined $proc))) {
+               ERROR ("Usage: Collectd::fc_register(type, name, proc)");
+               return;
+       }
+
+       if (! defined $fc_plugins[$type]) {
+               ERROR ("Collectd::fc_register: Invalid type \"$type\"");
+               return;
+       }
+
+       if (("HASH" ne ref ($proc)) || (! defined $proc->{$fc_exec_names{$type}})
+                       || ("" ne ref ($proc->{$fc_exec_names{$type}}))) {
+               ERROR ("Collectd::fc_register: Invalid proc.");
+               return;
+       }
+
+       for my $p (qw( create destroy )) {
+               if ((defined $proc->{$p}) && ("" ne ref ($proc->{$p}))) {
+                       ERROR ("Collectd::fc_register: Invalid proc.");
+                       return;
+               }
+       }
+
+       %fc = %$proc;
+
+       foreach my $p (keys %fc) {
+               my $pkg = scalar caller;
+
+               if ($p !~ m/^(create|destroy|$fc_exec_names{$type})$/) {
+                       next;
+               }
+
+               if ($fc{$p} !~ m/^$pkg\:\:/) {
+                       $fc{$p} = $pkg . "::" . $fc{$p};
+               }
+       }
+
+       lock %{$fc_plugins[$type]};
+       if (defined $fc_plugins[$type]->{$name}) {
+               WARNING ("Collectd::fc_register: Overwriting previous "
+                       . "definition of match \"$name\".");
+       }
+
+       if (! _fc_register ($type, $name)) {
+               ERROR ("Collectd::fc_register: Failed to register \"$name\".");
+               return;
+       }
+
+       $fc_plugins[$type]->{$name} = \%fc;
+       return 1;
+}
+
+sub _plugin_dispatch_config {
+       my $plugin = shift;
+       my $config = shift;
+
+       our $cb_name = undef;
+
+       if (! (defined ($plugin) && defined ($config))) {
+               return;
+       }
+
+       if (! defined $cf_callbacks{$plugin}) {
+               WARNING ("Found a configuration for the \"$plugin\" plugin, but "
+                       . "the plugin isn't loaded or didn't register "
+                       . "a configuration callback.");
+               return;
+       }
+
+       {
+               lock %cf_callbacks;
+               $cb_name = $cf_callbacks{$plugin};
+       }
+       call_by_name ($config);
+}
+
+1;
+
+# vim: set sw=4 ts=4 tw=78 noexpandtab :
+
diff --git a/bindings/perl/lib/Collectd/Plugins/Monitorus.pm b/bindings/perl/lib/Collectd/Plugins/Monitorus.pm
new file mode 100644 (file)
index 0000000..7054fbf
--- /dev/null
@@ -0,0 +1,104 @@
+#
+# collectd - mon.itor.us collectd plugin
+# Copyright (C) 2009  Jeff Green
+#
+# 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:
+#   Jeff Green <jeff at kikisoso.org>
+#
+
+package Collectd::Plugins::Monitorus;
+
+use strict;
+use warnings;
+
+use Collectd qw( :all );
+use LWP;
+use threads::shared;
+
+use constant NUM_OF_INTERVALS => 90;
+
+my $intervalcnt :shared;
+$intervalcnt=NUM_OF_INTERVALS;
+my $prev_value :shared;
+$prev_value=0;
+
+plugin_register (TYPE_READ, "monitorus", "monitorus_read");
+
+sub monitorus_read
+{
+        my $vl = { plugin => 'monitorus', type => 'gauge' };
+
+        # Only retrieve a value occasionally in order to not overload mon.itor.us
+        if (++$intervalcnt<NUM_OF_INTERVALS) { # e.g. 180 * 10 secs / 60 seconds/min = 30 minutes
+                $vl->{'values'} = [ $prev_value ];
+                plugin_dispatch_values ($vl);
+                return 1;
+        }
+
+        $intervalcnt=0;
+
+        my $site = 'http://mon.itor.us';
+        my $username = 'me@example.org';
+        my $target = $site.'/user/api/'.$username.'/secretpassword';
+
+        my $ua = LWP::UserAgent->new;
+        my $req = HTTP::Request->new(GET => "$target");
+        $req->header('Accept' => 'text/html');          #Accept HTML Page
+
+        my $key;
+        my $res = $ua->get($target);
+        if ($res->is_success) {# Success....all content of page has been received
+                $res->content() =~ m/\[CDATA\[(.*)\]\]/;
+                $key = $1;
+        } else {
+                INFO("monitorus: Error in retrieving login page.");
+        }
+
+        $target = $site.'/test/api/'.$key.'/testNames';
+        my $testid;
+        $res = $ua->get($target);
+        if ($res->is_success) {# Success....all content of page has been received
+                $res->content() =~ m/<test id='(.*)'><!\[CDATA\[sitetest_http\]\]/;
+                $testid = $1;
+        } else {
+                INFO("monitorus: Error in retrieving testNames page.");
+        }
+
+        #$target = $site.'/test/api/'.$key.'/testinfo/'.$testid.'/-240';
+        #$target = $site.'/test/api/'.$key.'/test/'.$testid.'/27/5/2009/1/3/-240';
+        $target = $site.'/test/api/'.$key.'/testsLastValues/1/3';
+
+        my $result;
+        my $value;
+        $res = $ua->get($target);
+        if ($res->is_success) {# Success....all content of page has been received
+                $res->content() =~ m/\<\/row\>\s*(\<row\>.*?sitetest_http.*?\<\/row\>)/s;
+                $result = $1;
+                $result =~ s/\<cell\>.*?CDATA.*?\<\/cell\>//g;
+                $result =~ m|\<cell\>([0-9]*)\<\/cell\>|;
+                $value = $1;
+        } else {
+                INFO("monitorus: Error in retrieving testsLastValues page.");
+        }
+
+        $prev_value = $value;
+        $vl->{'values'} = [ $value ];
+        plugin_dispatch_values ($vl);
+
+        return 1;
+}
+
+1;
diff --git a/bindings/perl/lib/Collectd/Plugins/OpenVZ.pm b/bindings/perl/lib/Collectd/Plugins/OpenVZ.pm
new file mode 100644 (file)
index 0000000..2944157
--- /dev/null
@@ -0,0 +1,190 @@
+#
+# collectd - OpenVZ collectd plugin
+# Copyright (C) 2009  Jonathan Kolb
+#
+# 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
+#
+# Author:
+#   Jonathan Kolb <jon at b0g.us>
+#
+
+package Collectd::Plugins::OpenVZ;
+
+use strict;
+use warnings;
+
+use Collectd qw( :all );
+
+my @cpu_instances = ('user', 'nice', 'system', 'idle', 'wait', 'interrupt', 'softirq', 'steal');
+my @if_instances = ('if_octets', 'if_packets', 'if_errors');
+my $vzctl = '/usr/sbin/vzctl';
+my $vzlist = '/usr/sbin/vzlist';
+
+my $last_stat = {};
+
+sub openvz_read
+{
+    my %v = (time => time(), interval => $interval_g);
+    my (@veids, $veid, $name, $key, $val, $i, @lines, @parts, @counters);
+
+    @veids = map { s/ //g; $_; } split(/\n/, `$vzlist -Ho veid`);
+
+    foreach $veid (@veids)
+    {
+        ($name = `$vzlist -Ho name $veid`) =~ s/^\s*(.*?)\s*$/$1/;
+        $name = $veid if ($name =~ /^-$/);
+
+        $v{'host'} = $name;
+
+        #####################################################################
+        # interface
+
+        $v{'plugin'} = 'interface';
+        delete $v{'plugin_instance'};
+
+        @lines = split(/\n/, `$vzctl exec $veid cat /proc/net/dev`);
+        foreach (@lines)
+        {
+            next if (!/:/);                
+
+            @parts = split(/:/);
+            ($key = $parts[0]) =~ s/^\s*(.*?)\s*$/$1/;
+            ($val = $parts[1]) =~ s/^\s*(.*?)\s*$/$1/;
+            @counters = split(/ +/, $val);
+
+            $v{'type_instance'} = $key;
+            for ($key = 0; $key <= $#if_instances; ++$key)
+            {
+                $v{'type'} = $if_instances[$key];
+                $v{'values'} = [ $counters[$key], $counters[$key + 8] ];
+                plugin_dispatch_values(\%v);
+            }
+        }
+
+        #####################################################################
+        # cpu
+
+        $v{'plugin'} = 'cpu';
+        $v{'type'} = 'cpu';
+
+        $i = 0;
+        @lines = split(/\n/, `$vzctl exec $veid cat /proc/stat`);
+        foreach (@lines)
+        {
+            next if (!/^cpu[0-9]/);
+
+            @counters = split(/ +/);
+            shift(@counters);
+
+            # Remove once OpenVZ bug 1376 is resolved
+            if (48485 == $counters[3])
+            {
+                $counters[3] = $last_stat->{"$veid-$i-idle"};
+                $counters[4] = $last_stat->{"$veid-$i-wait"};
+            }
+            else
+            {
+                $last_stat->{"$veid-$i-idle"} = $counters[3];
+                $last_stat->{"$veid-$i-wait"} = $counters[4];
+            }
+
+            $v{'plugin_instance'} = $i++;
+            for ($key = 0; $key <= $#counters; ++$key)
+            {
+                $v{'type_instance'} = $cpu_instances[$key];
+                $v{'values'} = [ $counters[$key] ];
+                plugin_dispatch_values(\%v);
+            }
+        }
+
+        #####################################################################
+        # df
+
+        $v{'plugin'} = 'df';
+        delete $v{'plugin_instance'};
+        $v{'type'} = 'df';
+
+        $val = join(' ', map { (split)[1] } split(/\n/, `$vzctl exec $veid cat /proc/mounts`));
+        @lines = split(/\n/, `$vzctl exec $veid stat -tf $val`);
+        foreach (@lines)
+        {
+            @parts = split(/ /);
+            next if (0 == $parts[7]);
+
+            $val = substr($parts[0], 1);
+            $val = 'root' if ($val =~ /^$/);
+            $val =~ s#/#-#g;
+
+            $v{'type_instance'} = $val;
+            $v{'values'} = [ $parts[5] * ($parts[6] - $parts[7]), $parts[5] * $parts[7] ];
+            plugin_dispatch_values(\%v);
+        }
+
+        #####################################################################
+        # load
+
+        $v{'plugin'} = 'load';
+        delete $v{'plugin_instance'};
+        $v{'type'} = 'load';
+        delete $v{'type_instance'};
+
+        @parts = split(/ +/, `$vzctl exec $veid cat /proc/loadavg`);
+        $v{'values'} = [ $parts[0], $parts[1], $parts[2] ];
+        plugin_dispatch_values(\%v);
+
+        #####################################################################
+        # processes
+
+        my $ps_states = { 'paging' => 0, 'blocked' => 0, 'zombies' => 0, 'stopped' => 0,
+            'running' => 0, 'sleeping' => 0 };
+        my $state_map = { 'R' => 'running', 'S' => 'sleeping', 'D' => 'blocked',
+            'Z' => 'zombies', 'T' => 'stopped', 'W' => 'paging' };
+
+        $v{'plugin'} = 'processes';
+        delete $v{'plugin_instance'};
+        $v{'type'} = 'ps_state';
+
+        @lines = map { (split)[2] } split(/\n/, `$vzctl exec $veid cat '/proc/[0-9]*/stat'`);
+        foreach $key (@lines)
+        {
+            ++$ps_states->{$state_map->{$key}};
+        }
+
+        foreach $key (keys %{$ps_states})
+        {
+            $v{'type_instance'} = $key;
+            $v{'values'} = [ $ps_states->{$key} ];
+            plugin_dispatch_values(\%v);
+        }
+
+        #####################################################################
+        # users
+
+        $v{'plugin'} = 'users';
+        delete $v{'plugin_instance'};
+        $v{'type'} = 'users';
+        delete $v{'type_instance'};
+
+        @lines = split(/\n/, `$vzctl exec $veid w -h`);
+        $v{'values'} = [ scalar(@lines) ];
+        plugin_dispatch_values(\%v);
+    }
+
+    return 1;
+}
+
+plugin_register(TYPE_READ, 'OpenVZ', 'openvz_read');
+
+return 1;
diff --git a/bindings/perl/lib/Collectd/Unixsock.pm b/bindings/perl/lib/Collectd/Unixsock.pm
new file mode 100644 (file)
index 0000000..199a47c
--- /dev/null
@@ -0,0 +1,656 @@
+#
+# collectd - Collectd::Unixsock
+# Copyright (C) 2007,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
+#
+# Author:
+#   Florian octo Forster <octo at verplant.org>
+#
+
+package Collectd::Unixsock;
+
+=head1 NAME
+
+Collectd::Unixsock - Abstraction layer for accessing the functionality by
+collectd's unixsock plugin.
+
+=head1 SYNOPSIS
+
+  use Collectd::Unixsock ();
+
+  my $sock = Collectd::Unixsock->new ($path);
+
+  my $value = $sock->getval (%identifier);
+  $sock->putval (%identifier,
+                 time => time (),
+                values => [123, 234, 345]);
+
+  $sock->destroy ();
+
+=head1 DESCRIPTION
+
+collectd's unixsock plugin allows external programs to access the values it has
+collected or received and to submit own values. This Perl-module is simply a
+little abstraction layer over this interface to make it even easier for
+programmers to interact with the daemon.
+
+=cut
+
+use strict;
+use warnings;
+
+#use constant { NOTIF_FAILURE => 1, NOTIF_WARNING => 2, NOTIF_OKAY => 4 };
+
+use Carp (qw(cluck confess));
+use IO::Socket::UNIX;
+use Regexp::Common (qw(number));
+
+our $Debug = 0;
+
+return (1);
+
+sub _debug
+{
+       if (!$Debug)
+       {
+               return;
+       }
+       print @_;
+}
+
+sub _create_socket
+{
+       my $path = shift;
+       my $sock = IO::Socket::UNIX->new (Type => SOCK_STREAM, Peer => $path);
+       if (!$sock)
+       {
+               cluck ("Cannot open UNIX-socket $path: $!");
+               return;
+       }
+       return ($sock);
+} # _create_socket
+
+=head1 VALUE IDENTIFIERS
+
+The values in the collectd are identified using an five-tuple (host, plugin,
+plugin-instance, type, type-instance) where only plugin-instance and
+type-instance may be NULL (or undefined). Many functions expect an
+I<%identifier> hash that has at least the members B<host>, B<plugin>, and
+B<type>, possibly completed by B<plugin_instance> and B<type_instance>.
+
+Usually you can pass this hash as follows:
+
+  $obj->method (host => $host, plugin => $plugin, type => $type, %other_args);
+
+=cut
+
+sub _create_identifier
+{
+       my $args = shift;
+       my $host;
+       my $plugin;
+       my $type;
+
+       if (!$args->{'host'} || !$args->{'plugin'} || !$args->{'type'})
+       {
+               cluck ("Need `host', `plugin' and `type'");
+               return;
+       }
+
+       $host = $args->{'host'};
+       $plugin = $args->{'plugin'};
+       $plugin .= '-' . $args->{'plugin_instance'} if (defined ($args->{'plugin_instance'}));
+       $type = $args->{'type'};
+       $type .= '-' . $args->{'type_instance'} if (defined ($args->{'type_instance'}));
+
+       return ("$host/$plugin/$type");
+} # _create_identifier
+
+sub _parse_identifier
+{
+       my $string = shift;
+       my $host;
+       my $plugin;
+       my $plugin_instance;
+       my $type;
+       my $type_instance;
+       my $ident;
+
+       ($host, $plugin, $type) = split ('/', $string);
+
+       ($plugin, $plugin_instance) = split ('-', $plugin, 2);
+       ($type, $type_instance) = split ('-', $type, 2);
+
+       $ident =
+       {
+               host => $host,
+               plugin => $plugin,
+               type => $type
+       };
+       $ident->{'plugin_instance'} = $plugin_instance if (defined ($plugin_instance));
+       $ident->{'type_instance'} = $type_instance if (defined ($type_instance));
+
+       return ($ident);
+} # _parse_identifier
+
+sub _escape_argument
+{
+       my $string = shift;
+
+       if ($string =~ m/^\w+$/)
+       {
+               return ("$string");
+       }
+
+       $string =~ s#\\#\\\\#g;
+       $string =~ s#"#\\"#g;
+       $string = "\"$string\"";
+
+       return ($string);
+}
+
+=head1 PUBLIC METHODS
+
+=over 4
+
+=item I<$obj> = Collectd::Unixsock->B<new> ([I<$path>]);
+
+Creates a new connection to the daemon. The optional I<$path> argument gives
+the path to the UNIX socket of the C<unixsock plugin> and defaults to
+F</var/run/collectd-unixsock>. Returns the newly created object on success and
+false on error.
+
+=cut
+
+sub new
+{
+       my $pkg = shift;
+       my $path = @_ ? shift : '/var/run/collectd-unixsock';
+       my $sock = _create_socket ($path) or return;
+       my $obj = bless (
+               {
+                       path => $path,
+                       sock => $sock,
+                       error => 'No error'
+               }, $pkg);
+       return ($obj);
+} # new
+
+=item I<$res> = I<$obj>-E<gt>B<getval> (I<%identifier>);
+
+Requests a value-list from the daemon. On success a hash-ref is returned with
+the name of each data-source as the key and the according value as, well, the
+value. On error false is returned.
+
+=cut
+
+sub getval # {{{
+{
+       my $obj = shift;
+       my %args = @_;
+
+       my $status;
+       my $fh = $obj->{'sock'} or confess ('object has no filehandle');
+       my $msg;
+       my $identifier;
+
+       my $ret = {};
+
+       $identifier = _create_identifier (\%args) or return;
+
+       $msg = 'GETVAL ' . _escape_argument ($identifier) . "\n";
+       _debug "-> $msg";
+       print $fh $msg;
+
+       $msg = <$fh>;
+       chomp ($msg);
+       _debug "<- $msg\n";
+
+       ($status, $msg) = split (' ', $msg, 2);
+       if ($status <= 0)
+       {
+               $obj->{'error'} = $msg;
+               return;
+       }
+
+       for (my $i = 0; $i < $status; $i++)
+       {
+               my $entry = <$fh>;
+               chomp ($entry);
+               _debug "<- $entry\n";
+
+               if ($entry =~ m/^(\w+)=NaN$/)
+               {
+                       $ret->{$1} = undef;
+               }
+               elsif ($entry =~ m/^(\w+)=($RE{num}{real})$/)
+               {
+                       $ret->{$1} = 0.0 + $2;
+               }
+       }
+
+       return ($ret);
+} # }}} sub getval
+
+=item I<$res> = I<$obj>-E<gt>B<getthreshold> (I<%identifier>);
+
+Requests a threshold from the daemon. On success a hash-ref is returned with
+the threshold data. On error false is returned.
+
+=cut
+
+sub getthreshold # {{{
+{
+       my $obj = shift;
+       my %args = @_;
+
+       my $status;
+       my $fh = $obj->{'sock'} or confess ('object has no filehandle');
+       my $msg;
+       my $identifier;
+
+       my $ret = {};
+
+       $identifier = _create_identifier (\%args) or return;
+
+       $msg = 'GETTHRESHOLD ' . _escape_argument ($identifier) . "\n";
+       _debug "-> $msg";
+       print $fh $msg;
+
+       $msg = <$fh>;
+       chomp ($msg);
+       _debug "<- $msg\n";
+
+       ($status, $msg) = split (' ', $msg, 2);
+       if ($status <= 0)
+       {
+               $obj->{'error'} = $msg;
+               return;
+       }
+
+       for (my $i = 0; $i < $status; $i++)
+       {
+               my $entry = <$fh>;
+               chomp ($entry);
+               _debug "<- $entry\n";
+
+               if ($entry =~ m/^([^:]+):\s*(\S.*)$/)
+               {
+                       my $key = $1;
+                       my $value = $2;
+
+                       $key =~ s/^\s+//;
+                       $key =~ s/\s+$//;
+
+                       $ret->{$key} = $value;
+               }
+       }
+
+       return ($ret);
+} # }}} sub getthreshold
+
+=item I<$obj>-E<gt>B<putval> (I<%identifier>, B<time> =E<gt> I<$time>, B<values> =E<gt> [...]);
+
+Submits a value-list to the daemon. If the B<time> argument is omitted
+C<time()> is used. The required argument B<values> is a reference to an array
+of values that is to be submitted. The number of values must match the number
+of values expected for the given B<type> (see L<VALUE IDENTIFIERS>), though
+this is checked by the daemon, not the Perl module. Also, gauge data-sources
+(e.E<nbsp>g. system-load) may be C<undef>. Returns true upon success and false
+otherwise.
+
+=cut
+
+sub putval
+{
+       my $obj = shift;
+       my %args = @_;
+
+       my $status;
+       my $fh = $obj->{'sock'} or confess;
+       my $msg;
+       my $identifier;
+       my $values;
+       my $interval = "";
+
+       if (defined $args{'interval'})
+       {
+               $interval = ' interval='
+               . _escape_argument ($args{'interval'});
+       }
+
+       $identifier = _create_identifier (\%args) or return;
+       if (!$args{'values'})
+       {
+               cluck ("Need argument `values'");
+               return;
+       }
+
+       if (!ref ($args{'values'}))
+       {
+               $values = $args{'values'};
+       }
+       else
+       {
+               my $time;
+
+               if ("ARRAY" ne ref ($args{'values'}))
+               {
+                       cluck ("Invalid `values' argument (expected an array ref)");
+                       return;
+               }
+
+               if (! scalar @{$args{'values'}})
+               {
+                       cluck ("Empty `values' array");
+                       return;
+               }
+
+               $time = $args{'time'} ? $args{'time'} : time ();
+               $values = join (':', $time, map { defined ($_) ? $_ : 'U' } (@{$args{'values'}}));
+       }
+
+       $msg = 'PUTVAL '
+       . _escape_argument ($identifier)
+       . $interval
+       . ' ' . _escape_argument ($values) . "\n";
+       _debug "-> $msg";
+       print $fh $msg;
+
+       $msg = <$fh>;
+       chomp ($msg);
+       _debug "<- $msg\n";
+
+       ($status, $msg) = split (' ', $msg, 2);
+       return (1) if ($status == 0);
+
+       $obj->{'error'} = $msg;
+       return;
+} # putval
+
+=item I<$res> = I<$obj>-E<gt>B<listval> ()
+
+Queries a list of values from the daemon. The list is returned as an array of
+hash references, where each hash reference is a valid identifier. The C<time>
+member of each hash holds the epoch value of the last update of that value.
+
+=cut
+
+sub listval
+{
+       my $obj = shift;
+       my $msg;
+       my @ret = ();
+       my $status;
+       my $fh = $obj->{'sock'} or confess;
+
+       _debug "LISTVAL\n";
+       print $fh "LISTVAL\n";
+
+       $msg = <$fh>;
+       chomp ($msg);
+       _debug "<- $msg\n";
+       ($status, $msg) = split (' ', $msg, 2);
+       if ($status < 0)
+       {
+               $obj->{'error'} = $msg;
+               return;
+       }
+
+       for (my $i = 0; $i < $status; $i++)
+       {
+               my $time;
+               my $ident;
+
+               $msg = <$fh>;
+               chomp ($msg);
+               _debug "<- $msg\n";
+
+               ($time, $ident) = split (' ', $msg, 2);
+
+               $ident = _parse_identifier ($ident);
+               $ident->{'time'} = int ($time);
+
+               push (@ret, $ident);
+       } # for (i = 0 .. $status)
+
+       return (@ret);
+} # listval
+
+=item I<$res> = I<$obj>-E<gt>B<putnotif> (B<severity> =E<gt> I<$severity>, B<message> =E<gt> I<$message>, ...);
+
+Submits a notification to the daemon.
+
+Valid options are:
+
+=over 4
+
+=item B<severity>
+
+Sets the severity of the notification. The value must be one of the following
+strings: C<failure>, C<warning>, or C<okay>. Case does not matter. This option
+is mandatory.
+
+=item B<message>
+
+Sets the message of the notification. This option is mandatory.
+
+=item B<time>
+
+Sets the time. If omitted, C<time()> is used.
+
+=item I<Value identifier>
+
+All the other fields of the value identifiers, B<host>, B<plugin>,
+B<plugin_instance>, B<type>, and B<type_instance>, are optional. When given,
+the notification is associated with the performance data of that identifier.
+For more details, please see L<collectd-unixsock(5)>.
+
+=back
+
+=cut
+
+sub putnotif
+{
+       my $obj = shift;
+       my %args = @_;
+
+       my $status;
+       my $fh = $obj->{'sock'} or confess;
+
+       my $msg; # message sent to the socket
+       
+       if (!$args{'message'})
+       {
+               cluck ("Need argument `message'");
+               return;
+       }
+       if (!$args{'severity'})
+       {
+               cluck ("Need argument `severity'");
+               return;
+       }
+       $args{'severity'} = lc ($args{'severity'});
+       if (($args{'severity'} ne 'failure')
+               && ($args{'severity'} ne 'warning')
+               && ($args{'severity'} ne 'okay'))
+       {
+               cluck ("Invalid `severity: " . $args{'severity'});
+               return;
+       }
+
+       if (!$args{'time'})
+       {
+               $args{'time'} = time ();
+       }
+       
+       $msg = 'PUTNOTIF '
+       . join (' ', map { $_ . '=' . _escape_argument ($args{$_}) } (keys %args))
+       . "\n";
+
+       _debug "-> $msg";
+       print $fh $msg;
+
+       $msg = <$fh>;
+       chomp ($msg);
+       _debug "<- $msg\n";
+
+       ($status, $msg) = split (' ', $msg, 2);
+       return (1) if ($status == 0);
+
+       $obj->{'error'} = $msg;
+       return;
+} # putnotif
+
+=item I<$obj>-E<gt>B<flush> (B<timeout> =E<gt> I<$timeout>, B<plugins> =E<gt> [...], B<identifier>  =E<gt> [...]);
+
+Flush cached data.
+
+Valid options are:
+
+=over 4
+
+=item B<timeout>
+
+If this option is specified, only data older than I<$timeout> seconds is
+flushed.
+
+=item B<plugins>
+
+If this option is specified, only the selected plugins will be flushed. The
+argument is a reference to an array of strings.
+
+=item B<identifier>
+
+If this option is specified, only the given identifier(s) will be flushed. The
+argument is a reference to an array of identifiers. Identifiers, in this case,
+are hash references and have the members as outlined in L<VALUE IDENTIFIERS>.
+
+=back
+
+=cut
+
+sub flush
+{
+       my $obj  = shift;
+       my %args = @_;
+
+       my $fh = $obj->{'sock'} or confess;
+
+       my $status = 0;
+       my $msg    = "FLUSH";
+
+       if (defined ($args{'timeout'}))
+       {
+               $msg .= " timeout=" . $args{'timeout'};
+       }
+
+       if ($args{'plugins'})
+       {
+               foreach my $plugin (@{$args{'plugins'}})
+               {
+                       $msg .= " plugin=" . $plugin;
+               }
+       }
+
+       if ($args{'identifier'})
+       {
+               for (@{$args{'identifier'}})
+               {
+                       my $identifier = $_;
+                       my $ident_str;
+
+                       if (ref ($identifier) ne 'HASH')
+                       {
+                               cluck ("The argument of the `identifier' "
+                                       . "option must be an array reference "
+                                       . "of hash references.");
+                               return;
+                       }
+
+                       $ident_str = _create_identifier ($identifier);
+                       if (!$ident_str)
+                       {
+                               return;
+                       }
+
+                       $msg .= ' identifier=' . _escape_argument ($ident_str);
+               }
+       }
+
+       $msg .= "\n";
+
+       _debug "-> $msg";
+       print $fh $msg;
+
+       $msg = <$fh>;
+       chomp ($msg);
+       _debug "<- $msg\n";
+
+       ($status, $msg) = split (' ', $msg, 2);
+       return (1) if ($status == 0);
+
+       $obj->{'error'} = $msg;
+       return;
+}
+
+sub error
+{
+       my $obj = shift;
+       if ($obj->{'error'})
+       {
+               return ($obj->{'error'});
+       }
+       return;
+}
+
+=item I<$obj>-E<gt>destroy ();
+
+Closes the socket before the object is destroyed. This function is also
+automatically called then the object goes out of scope.
+
+=back
+
+=cut
+
+sub destroy
+{
+       my $obj = shift;
+       if ($obj->{'sock'})
+       {
+               close ($obj->{'sock'});
+               delete ($obj->{'sock'});
+       }
+}
+
+sub DESTROY
+{
+       my $obj = shift;
+       $obj->destroy ();
+}
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>,
+L<collectd-unixsock(5)>
+
+=head1 AUTHOR
+
+Florian octo Forster E<lt>octo@verplant.orgE<gt>
+
+=cut
+
+# vim: set fdm=marker :
index f46a362..3280506 100644 (file)
@@ -9,6 +9,7 @@ m4_ifdef([LT_PACKAGE_VERSION],
         LT_CONFIG_LTDL_DIR([libltdl])
         LT_INIT([dlopen])
         LTDL_INIT([convenience])
+        AC_DEFINE(LIBTOOL_VERSION, 2, [Define to used libtool version.])
        ]
 ,
        # libtool <= 1.5
@@ -18,6 +19,7 @@ m4_ifdef([LT_PACKAGE_VERSION],
         AC_SUBST(LIBLTDL)
         AC_LIBTOOL_DLOPEN
         AC_CONFIG_SUBDIRS(libltdl)
+        AC_DEFINE(LIBTOOL_VERSION, 1, [Define to used libtool version.])
        ]
 )
 
@@ -61,6 +63,10 @@ case $host_os in
        *openbsd*)
        ac_system="OpenBSD"
        ;;
+       *aix*)
+       AC_DEFINE([KERNEL_AIX], 1, [True if program is to be compiled for a AIX kernel])
+       ac_system="AIX"
+       ;;
        *)
        ac_system="unknown"
 esac
@@ -262,6 +268,22 @@ AC_CHECK_HEADERS(sys/sysctl.h, [], [],
 #endif
 ])
 
+AC_MSG_CHECKING([for sysctl kern.cp_times])
+if test -x /sbin/sysctl
+then
+       /sbin/sysctl kern.cp_times 2>/dev/null
+       if test $? -eq 0
+       then
+               AC_MSG_RESULT([yes])
+               AC_DEFINE(HAVE_SYSCTL_KERN_CP_TIMES, 1,
+               [Define if sysctl supports kern.cp_times])
+       else
+               AC_MSG_RESULT([no])
+       fi
+else
+       AC_MSG_RESULT([no])
+fi
+
 # For hddtemp module
 AC_CHECK_HEADERS(linux/major.h libgen.h)
 
@@ -611,6 +633,8 @@ static float foo = NAN;
   fi
 fi
 if test "x$nan_type" = "xnone"; then
+  SAVE_LDFLAGS=$LDFLAGS
+  LDFLAGS="$LDFLAGS -lm"
   AC_CACHE_CHECK([whether NAN can be defined by 0/0],
     [c_cv_have_nan_zero],
     AC_RUN_IFELSE(
@@ -637,6 +661,7 @@ static float foo = NAN;
       [c_cv_have_nan_zero="no"]
     )
   )
+  LDFLAGS=$SAVE_LDFLAGS
   if test "x$c_cv_have_nan_zero" = "xyes"
   then
     nan_type="zero"
@@ -1057,6 +1082,38 @@ fi
 m4_divert_once([HELP_WITH], [
 collectd additional packages:])
 
+AM_CONDITIONAL([BUILD_AIX],[test "x$x$ac_system" = "xAIX"]) 
+
+if test "x$ac_system" = "xAIX"
+then
+       with_perfstat="yes"
+       with_procinfo="yes"
+else
+       with_perfstat="no (AIX only)"
+       with_procinfo="no (AIX only)"
+fi
+
+if test "x$with_perfstat" = "xyes"
+then
+       AC_CHECK_LIB(perfstat, perfstat_reset, [with_perfstat="yes"], [with_perfstat="no (perfstat not found)"], [])
+#      AC_CHECK_HEADERS(sys/protosw.h libperfstat.h,, [with_perfstat="no (perfstat not found)"])
+fi
+if test "x$with_perfstat" = "xyes"
+then
+        AC_DEFINE(HAVE_PERFSTAT, 1, [Define to 1 if you have the 'perfstat' library (-lperfstat)])
+fi
+AM_CONDITIONAL(BUILD_WITH_PERFSTAT, test "x$with_perfstat" = "xyes")
+
+# Processes plugin under AIX.
+if test "x$with_procinfo" = "xyes"
+then
+       AC_CHECK_HEADERS(procinfo.h,, [with_procinfo="no (procinfo.h not found)"])
+fi
+if test "x$with_procinfo" = "xyes"
+then
+        AC_DEFINE(HAVE_PROCINFO_H, 1, [Define to 1 if you have the procinfo.h])
+fi
+
 if test "x$ac_system" = "xSolaris"
 then
        with_kstat="yes"
@@ -1980,6 +2037,78 @@ fi
 AM_CONDITIONAL(BUILD_WITH_LIBNETLINK, test "x$with_libnetlink" = "xyes")
 # }}}
 
+# --with-libnetapp {{{
+AC_ARG_VAR([LIBNETAPP_CPPFLAGS], [C preprocessor flags required to build with libnetapp])
+AC_ARG_VAR([LIBNETAPP_LDFLAGS],  [Linker flags required to build with libnetapp])
+AC_ARG_VAR([LIBNETAPP_LIBS],     [Other libraries required to link against libnetapp])
+LIBNETAPP_CPPFLAGS="$LIBNETAPP_CPPFLAGS"
+LIBNETAPP_LDFLAGS="$LIBNETAPP_LDFLAGS"
+LIBNETAPP_LIBS="$LIBNETAPP_LIBS"
+AC_ARG_WITH(libnetapp, [AS_HELP_STRING([--with-libnetapp@<:@=PREFIX@:>@], [Path to libnetapp.])],
+[
+ if test -d "$withval"
+ then
+        LIBNETAPP_CPPFLAGS="$LIBNETAPP_CPPFLAGS -I$withval/include"
+        LIBNETAPP_LDFLAGS="$LIBNETAPP_LDFLAGS -L$withval/lib"
+        with_libnetapp="yes"
+ else
+        with_libnetapp="$withval"
+ fi
+],
+[
+ with_libnetapp="yes"
+])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $LIBNETAPP_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBNETAPP_LDFLAGS"
+
+if test "x$with_libnetapp" = "xyes"
+then
+       if test "x$LIBNETAPP_CPPFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([netapp CPPFLAGS: $LIBNETAPP_CPPFLAGS])
+       fi
+       AC_CHECK_HEADERS(netapp_api.h,
+               [with_libnetapp="yes"],
+               [with_libnetapp="no (netapp_api.h not found)"])
+fi
+
+if test "x$with_libnetapp" = "xyes"
+then
+       if test "x$LIBNETAPP_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([netapp LDFLAGS: $LIBNETAPP_LDFLAGS])
+       fi
+
+       if test "x$LIBNETAPP_LIBS" = "x"
+       then
+               LIBNETAPP_LIBS="-lpthread -lxml -ladt -lssl -lm -lcrypto -lz"
+       fi
+       AC_MSG_NOTICE([netapp LIBS: $LIBNETAPP_LIBS])
+
+       AC_CHECK_LIB(netapp, na_server_invoke_elem,
+               [with_libnetapp="yes"],
+               [with_libnetapp="no (symbol na_server_invoke_elem not found)"],
+               [$LIBNETAPP_LIBS])
+       LIBNETAPP_LIBS="-lnetapp $LIBNETAPP_LIBS"
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libnetapp" = "xyes"
+then
+       AC_DEFINE(HAVE_LIBNETAPP, 1, [Define to 1 if you have the netapp library (-lnetapp).])
+fi
+
+AC_SUBST(LIBNETAPP_CPPFLAGS)
+AC_SUBST(LIBNETAPP_LDFLAGS)
+AC_SUBST(LIBNETAPP_LIBS)
+AM_CONDITIONAL(BUILD_WITH_LIBNETAPP, test "x$with_libnetapp" = "xyes")
+# }}}
+
 # --with-libnetsnmp {{{
 with_snmp_config="net-snmp-config"
 with_snmp_cflags=""
@@ -2617,6 +2746,184 @@ AC_DEFINE_UNQUOTED(HAVE_LIBPTHREAD, [$collect_pthread],
 AM_CONDITIONAL(BUILD_WITH_LIBPTHREAD, test "x$with_libpthread" = "xyes")
 # }}}
 
+# --with-python {{{
+with_python_prog=""
+with_python_path="$PATH"
+AC_ARG_WITH(python, [AS_HELP_STRING([--with-python@<:@=PREFIX@:>@], [Path to the python interpreter.])],
+[
+ if test "x$withval" = "xyes" || test "x$withval" = "xno"
+ then
+        with_python="$withval"
+ else if test -x "$withval"
+ then
+        with_python_prog="$withval"
+        with_python_path="`dirname \"$withval\"`$PATH_SEPARATOR$with_python_path"
+        with_python="yes"
+ else if test -d "$withval"
+ then
+        with_python_path="$withval$PATH_SEPARATOR$with_python_path"
+        with_python="yes"
+ else
+        AC_MSG_WARN([Argument not recognized: $withval])
+ fi; fi; fi
+], [with_python="yes"])
+
+SAVE_PATH="$PATH"
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+SAVE_LIBS="$LIBS"
+
+PATH="$with_python_path"
+
+if test "x$with_python" = "xyes" && test "x$with_python_prog" = "x"
+then
+       AC_MSG_CHECKING([for python])
+       with_python_prog="`which python 2>/dev/null`"
+       if test "x$with_python_prog" = "x"
+       then
+               AC_MSG_RESULT([not found])
+               with_python="no (interpreter not found)"
+       else
+               AC_MSG_RESULT([$with_python_prog])
+       fi
+fi
+
+if test "x$with_python" = "xyes"
+then
+       AC_MSG_CHECKING([for Python CPPFLAGS])
+       python_include_path=`echo "import distutils.sysconfig;print distutils.sysconfig.get_python_inc()" | "$with_python_prog" 2>&1`
+       python_config_status=$?
+
+       if test "$python_config_status" -ne 0 || test "x$python_include_path" = "x"
+       then
+               AC_MSG_RESULT([failed with status $python_config_status (output: $python_include_path)])
+               with_python="no"
+       else
+               AC_MSG_RESULT([$python_include_path])
+       fi
+fi
+
+if test "x$with_python" = "xyes"
+then
+       CPPFLAGS="-I$python_include_path $CPPFLAGS"
+       AC_CHECK_HEADERS(Python.h,
+                        [with_python="yes"],
+                        [with_python="no ('Python.h' not found)"])
+fi
+
+if test "x$with_python" = "xyes"
+then
+       AC_MSG_CHECKING([for Python LDFLAGS])
+       python_library_path=`echo "import distutils.sysconfig;print distutils.sysconfig.get_config_vars(\"LIBDIR\").__getitem__(0)" | "$with_python_prog" 2>&1`
+       python_config_status=$?
+
+       if test "$python_config_status" -ne 0 || test "x$python_library_path" = "x"
+       then
+               AC_MSG_RESULT([failed with status $python_config_status (output: $python_library_path)])
+               with_python="no"
+       else
+               AC_MSG_RESULT([$python_library_path])
+       fi
+fi
+
+if test "x$with_python" = "xyes"
+then
+       AC_MSG_CHECKING([for Python LIBS])
+       python_library_flags=`echo "import distutils.sysconfig;print distutils.sysconfig.get_config_vars(\"BLDLIBRARY\").__getitem__(0)" | "$with_python_prog" 2>&1`
+       python_config_status=$?
+
+       if test "$python_config_status" -ne 0 || test "x$python_library_flags" = "x"
+       then
+               AC_MSG_RESULT([failed with status $python_config_status (output: $python_library_flags)])
+               with_python="no"
+       else
+               AC_MSG_RESULT([$python_library_flags])
+       fi
+fi
+
+if test "x$with_python" = "xyes"
+then
+       LDFLAGS="-L$python_library_path $LDFLAGS"
+       LIBS="$python_library_flags $LIBS"
+
+       AC_CHECK_FUNC(PyObject_CallFunction,
+                     [with_python="yes"],
+                     [with_python="no (Symbol 'PyObject_CallFunction' not found)"])
+fi
+
+PATH="$SAVE_PATH"
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+LIBS="$SAVE_LIBS"
+
+if test "x$with_python" = "xyes"
+then
+       BUILD_WITH_PYTHON_CPPFLAGS="-I$python_include_path"
+       BUILD_WITH_PYTHON_LDFLAGS="-L$python_library_path"
+       BUILD_WITH_PYTHON_LIBS="$python_library_flags"
+       AC_SUBST(BUILD_WITH_PYTHON_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_PYTHON_LDFLAGS)
+       AC_SUBST(BUILD_WITH_PYTHON_LIBS)
+fi
+# }}} --with-python
+
+# --with-librouteros {{{
+AC_ARG_WITH(librouteros, [AS_HELP_STRING([--with-librouteros@<:@=PREFIX@:>@], [Path to librouteros.])],
+[
+ if test "x$withval" = "xyes"
+ then
+        with_librouteros="yes"
+ else if test "x$withval" = "xno"
+ then
+        with_librouteros="no"
+ else
+        with_librouteros="yes"
+        LIBROUTEROS_CPPFLAGS="$LIBROUTEROS_CPPFLAGS -I$withval/include"
+        LIBROUTEROS_LDFLAGS="$LIBROUTEROS_LDFLAGS -L$withval/lib"
+ fi; fi
+],
+[with_librouteros="yes"])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $LIBROUTEROS_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBROUTEROS_LDFLAGS"
+
+if test "x$with_librouteros" = "xyes"
+then
+       if test "x$LIBROUTEROS_CPPFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([librouteros CPPFLAGS: $LIBROUTEROS_CPPFLAGS])
+       fi
+       AC_CHECK_HEADERS(routeros_api.h,
+       [with_librouteros="yes"],
+       [with_librouteros="no ('routeros_api.h' not found)"])
+fi
+if test "x$with_librouteros" = "xyes"
+then
+       if test "x$LIBROUTEROS_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([librouteros LDFLAGS: $LIBROUTEROS_LDFLAGS])
+       fi
+       AC_CHECK_LIB(routeros, ros_interface,
+       [with_librouteros="yes"],
+       [with_librouteros="no (symbol 'ros_interface' not found)"])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_librouteros" = "xyes"
+then
+       BUILD_WITH_LIBROUTEROS_CPPFLAGS="$LIBROUTEROS_CPPFLAGS"
+       BUILD_WITH_LIBROUTEROS_LDFLAGS="$LIBROUTEROS_LDFLAGS"
+       AC_SUBST(BUILD_WITH_LIBROUTEROS_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBROUTEROS_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBROUTEROS, test "x$with_librouteros" = "xyes")
+# }}}
+
 # --with-librrd {{{
 # AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given])
 librrd_cflags=""
@@ -3531,6 +3838,7 @@ plugin_ascent="no"
 plugin_battery="no"
 plugin_bind="no"
 plugin_conntrack="no"
+plugin_contextswitch="no"
 plugin_cpu="no"
 plugin_cpufreq="no"
 plugin_curl_json="no"
@@ -3568,6 +3876,7 @@ if test "x$ac_system" = "xLinux"
 then
        plugin_battery="yes"
        plugin_conntrack="yes"
+       plugin_contextswitch="yes"
        plugin_cpu="yes"
        plugin_cpufreq="yes"
        plugin_disk="yes"
@@ -3607,6 +3916,22 @@ then
        plugin_disk="yes"
 fi
 
+# AIX
+if test "x$with_perfstat" = "xyes"
+then
+       plugin_cpu="yes"
+       plugin_disk="yes"
+       plugin_memory="yes"
+       plugin_swap="yes"
+       plugin_interface="yes"
+       plugin_load="yes"
+fi
+
+if test "x$with_procinfo" = "xyes"
+then
+       plugin_processes="yes"
+fi
+
 # Solaris
 if test "x$with_kstat" = "xyes"
 then
@@ -3803,6 +4128,7 @@ AC_PLUGIN([ascent],      [$plugin_ascent],     [AscentEmu player statistics])
 AC_PLUGIN([battery],     [$plugin_battery],    [Battery statistics])
 AC_PLUGIN([bind],        [$plugin_bind],       [ISC Bind nameserver statistics])
 AC_PLUGIN([conntrack],   [$plugin_conntrack],  [nf_conntrack statistics])
+AC_PLUGIN([contextswitch], [$plugin_contextswitch], [context switch statistics])
 AC_PLUGIN([cpufreq],     [$plugin_cpufreq],    [CPU frequency statistics])
 AC_PLUGIN([cpu],         [$plugin_cpu],        [CPU usage statistics])
 AC_PLUGIN([csv],         [yes],                [CSV output plugin])
@@ -3830,6 +4156,7 @@ AC_PLUGIN([load],        [$plugin_load],       [System load])
 AC_PLUGIN([logfile],     [yes],                [File logging plugin])
 AC_PLUGIN([madwifi],     [$have_linux_wireless_h], [Madwifi wireless statistics])
 AC_PLUGIN([match_empty_counter], [yes],        [The empty counter match])
+AC_PLUGIN([match_hashed], [yes],               [The hashed match])
 AC_PLUGIN([match_regex], [yes],                [The regex match])
 AC_PLUGIN([match_timediff], [yes],             [The timediff match])
 AC_PLUGIN([match_value], [yes],                [The value match])
@@ -3839,6 +4166,7 @@ AC_PLUGIN([memcached],   [yes],                [memcached statistics])
 AC_PLUGIN([memory],      [$plugin_memory],     [Memory usage])
 AC_PLUGIN([multimeter],  [$plugin_multimeter], [Read multimeter values])
 AC_PLUGIN([mysql],       [$with_libmysql],     [MySQL statistics])
+AC_PLUGIN([netapp],      [$with_libnetapp],    [NetApp plugin])
 AC_PLUGIN([netlink],     [$with_libnetlink],   [Enhanced Linux network statistics])
 AC_PLUGIN([network],     [yes],                [Network communication plugin])
 AC_PLUGIN([nfs],         [$plugin_nfs],        [NFS statistics])
@@ -3857,6 +4185,8 @@ AC_PLUGIN([postgresql],  [$with_libpq],        [PostgreSQL database statistics])
 AC_PLUGIN([powerdns],    [yes],                [PowerDNS statistics])
 AC_PLUGIN([processes],   [$plugin_processes],  [Process statistics])
 AC_PLUGIN([protocols],   [$plugin_protocols],  [Protocol (IP, TCP, ...) statistics])
+AC_PLUGIN([python],      [$with_python],       [Embed a Python interpreter])
+AC_PLUGIN([routeros],    [$with_librouteros],  [RouterOS plugin])
 AC_PLUGIN([rrdcached],   [$librrd_rrdc_update], [RRDTool output plugin])
 AC_PLUGIN([rrdtool],     [$with_librrd],       [RRDTool output plugin])
 AC_PLUGIN([sensors],     [$with_libsensors],   [lm_sensors statistics])
@@ -3869,6 +4199,7 @@ AC_PLUGIN([tail],        [yes],                [Parsing of logfiles])
 AC_PLUGIN([tape],        [$plugin_tape],       [Tape drive statistics])
 AC_PLUGIN([target_notification], [yes],        [The notification target])
 AC_PLUGIN([target_replace], [yes],             [The replace target])
+AC_PLUGIN([target_scale],[yes],                [The scale target])
 AC_PLUGIN([target_set],  [yes],                [The set target])
 AC_PLUGIN([tcpconns],    [$plugin_tcpconns],   [TCP connection statistics])
 AC_PLUGIN([teamspeak2],  [yes],                [TeamSpeak2 server statistics])
@@ -4067,6 +4398,7 @@ Configuration:
     libkvm  . . . . . . . $with_libkvm
     libmemcached  . . . . $with_libmemcached
     libmysql  . . . . . . $with_libmysql
+    libnetapp . . . . . . $with_libnetapp
     libnetlink  . . . . . $with_libnetlink
     libnetsnmp  . . . . . $with_libnetsnmp
     libnotify . . . . . . $with_libnotify
@@ -4074,9 +4406,11 @@ Configuration:
     libopenipmi . . . . . $with_libopenipmipthread
     liboping  . . . . . . $with_liboping
     libpcap . . . . . . . $with_libpcap
+    libperfstat . . . . . $with_perfstat
     libperl . . . . . . . $with_libperl
     libpq . . . . . . . . $with_libpq
     libpthread  . . . . . $with_libpthread
+    librouteros . . . . . $with_librouteros
     librrd  . . . . . . . $with_librrd
     libsensors  . . . . . $with_libsensors
     libstatgrab . . . . . $with_libstatgrab
@@ -4087,6 +4421,7 @@ Configuration:
     libxmms . . . . . . . $with_libxmms
     libyajl . . . . . . . $with_libyajl
     oracle  . . . . . . . $with_oracle
+    python  . . . . . . . $with_python
 
   Features:
     daemon mode . . . . . $enable_daemon
@@ -4103,6 +4438,7 @@ Configuration:
     battery . . . . . . . $enable_battery
     bind  . . . . . . . . $enable_bind
     conntrack . . . . . . $enable_conntrack
+    contextswitch . . . . $enable_contextswitch
     cpu . . . . . . . . . $enable_cpu
     cpufreq . . . . . . . $enable_cpufreq
     csv . . . . . . . . . $enable_csv
@@ -4130,6 +4466,7 @@ Configuration:
     logfile . . . . . . . $enable_logfile
     madwifi . . . . . . . $enable_madwifi
     match_empty_counter . $enable_match_empty_counter
+    match_hashed  . . . . $enable_match_hashed
     match_regex . . . . . $enable_match_regex
     match_timediff  . . . $enable_match_timediff
     match_value . . . . . $enable_match_value
@@ -4139,6 +4476,7 @@ Configuration:
     memory  . . . . . . . $enable_memory
     multimeter  . . . . . $enable_multimeter
     mysql . . . . . . . . $enable_mysql
+    netapp  . . . . . . . $enable_netapp
     netlink . . . . . . . $enable_netlink
     network . . . . . . . $enable_network
     nfs . . . . . . . . . $enable_nfs
@@ -4157,6 +4495,8 @@ Configuration:
     powerdns  . . . . . . $enable_powerdns
     processes . . . . . . $enable_processes
     protocols . . . . . . $enable_protocols
+    python  . . . . . . . $enable_python
+    routeros  . . . . . . $enable_routeros
     rrdcached . . . . . . $enable_rrdcached
     rrdtool . . . . . . . $enable_rrdtool
     sensors . . . . . . . $enable_sensors
@@ -4169,6 +4509,7 @@ Configuration:
     tape  . . . . . . . . $enable_tape
     target_notification . $enable_target_notification
     target_replace  . . . $enable_target_replace
+    target_scale  . . . . $enable_target_scale
     target_set  . . . . . $enable_target_set
     tcpconns  . . . . . . $enable_tcpconns
     teamspeak2  . . . . . $enable_teamspeak2
index 1322d0d..e56017e 100644 (file)
@@ -139,21 +139,23 @@ GraphWidth 400
 <Type df_complex>
   Module GenericStacked
   DataSources value
-  RRDTitle "disk usage on {plugin_instance}"
+  RRDTitle "Disk/Volume usage on {plugin_instance}"
   RRDVerticalLabel "Byte"
-  RRDFormat "%6.2lf%s"
-  DSName "snap_used    used for snapshots"
-  DSName "snap_reserved snapshot reserve "
-  DSName "used         in use            "
-  DSName "free         free              "
-  DSName "sis_saved    sis_saved         "
-  Order free snap_used snap_reserved sis_saved used
+  RRDFormat "%5.1lf%s"
+  DSName "sis_saved         SIS saved         "
+  DSName "free              Free              "
+  DSName "used              Used              "
+  DSName "snap_normal_used  Snap used (normal)"
+  DSName "snap_reserved     Snap reserved     "
+  DSName "snap_reserve_used Snap used (resv)  "
+  Order sis_saved free used snap_normal_used snap_reserved snap_reserve_used
+  Color sis_saved 00e0e0
+  Color free  00ff00
   Color snap_reverse   ff8000
   Color used  ff0000
-  Color snap_used   000080
-  Color snap_reserved   ff8000
-  Color free  00ff00
-  Color sis_saved 00e0e0
+  Color snap_normal_used  c10640
+  Color snap_reserved     f15aef
+  Color snap_reserve_used 820c81
 </Type>
 <Type disk_latency>
   Module GenericIO
index 012fc48..4617f6c 100644 (file)
@@ -1,12 +1,17 @@
+
+%define with_java %(test -z "$JAVA_HOME" ; echo $?)
+
 Summary:       Statistics collection daemon for filling RRD files.
 Name:          collectd
-Version:       4.3.1
-Release:       0.centos5
+Version:       4.9.0
+Release:       1%{?dist}
 Source:                http://collectd.org/files/%{name}-%{version}.tar.gz
 License:       GPL
 Group:         System Environment/Daemons
 BuildRoot:     %{_tmppath}/%{name}-%{version}-root
-BuildPrereq:   lm_sensors-devel, mysql-devel, rrdtool-devel, curl-devel, libpcap-devel, net-snmp-devel, libstatgrab-devel, mysql-devel, libxml2-devel, libiptcdata-devel
+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>
 Vendor:                collectd development team <collectd@verplant.org>
@@ -19,6 +24,7 @@ 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.
 
+
 %package apache
 Summary:       apache-plugin for collectd.
 Group:         System Environment/Daemons
@@ -62,12 +68,25 @@ Requires:   collectd = %{version}, net-snmp
 %description snmp
 This plugin for collectd allows querying of network equipment using SNMP.
 
+%if %with_java
+%package java
+Summary:       java-module 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.
+%endif
+
 %prep
 rm -rf $RPM_BUILD_ROOT
 %setup
 
 %build
-./configure CFLAGS=-"DLT_LAZY_OR_NOW='RTLD_LAZY|RTLD_GLOBAL'" --prefix=%{_prefix} --sbindir=%{_sbindir} --mandir=%{_mandir} --libdir=%{_libdir} --sysconfdir=%{_sysconfdir} --enable-apache --enable-email --enable-mysql --enable-dns
+./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
 
 %install
@@ -80,14 +99,17 @@ mkdir -p $RPM_BUILD_ROOT/etc/collectd.d
 mkdir -p $RPM_BUILD_ROOT/var/lib/collectd
 ### Clean up docs
 find contrib/ -type f -exec %{__chmod} a-x {} \;
+
 ###Modify Config for Redhat Based Distros
-cp contrib/redhat/collectd.conf $RPM_BUILD_ROOT/etc/collectd.conf
 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   "/usr/lib/collectd":' $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     10:' $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
@@ -120,7 +142,7 @@ exit 0
 %files
 %defattr(-,root,root)
 %doc AUTHORS COPYING ChangeLog INSTALL NEWS README contrib/
-%attr(0644,root,root) %config(noreplace) /etc/collectd.conf
+%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
@@ -130,167 +152,142 @@ exit 0
 %attr(0644,root,root) %{_mandir}/man5/*
 %dir /etc/collectd.d
 
-%attr(0644,root,root) %{_libdir}/%{name}/apcups.so*
-%attr(0644,root,root) %{_libdir}/%{name}/apcups.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/apple_sensors.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/apple_sensors.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/battery.so*
-%attr(0644,root,root) %{_libdir}/%{name}/battery.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/conntrack.so*
-%attr(0644,root,root) %{_libdir}/%{name}/conntrack.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/cpufreq.so*
-%attr(0644,root,root) %{_libdir}/%{name}/cpufreq.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/cpu.so*
-%attr(0644,root,root) %{_libdir}/%{name}/cpu.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/csv.so*
-%attr(0644,root,root) %{_libdir}/%{name}/csv.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/df.so*
-%attr(0644,root,root) %{_libdir}/%{name}/df.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/disk.so*
-%attr(0644,root,root) %{_libdir}/%{name}/disk.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/dns.so*
-%attr(0644,root,root) %{_libdir}/%{name}/dns.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/entropy.so*
-%attr(0644,root,root) %{_libdir}/%{name}/entropy.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/exec.so*
-%attr(0644,root,root) %{_libdir}/%{name}/exec.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/hddtemp.so*
-%attr(0644,root,root) %{_libdir}/%{name}/hddtemp.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/interface.so*
-%attr(0644,root,root) %{_libdir}/%{name}/interface.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/iptables.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/iptables.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/irq.so*
-%attr(0644,root,root) %{_libdir}/%{name}/irq.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/load.so*
-%attr(0644,root,root) %{_libdir}/%{name}/load.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/logfile.so*
-%attr(0644,root,root) %{_libdir}/%{name}/logfile.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/mbmon.so
-%attr(0644,root,root) %{_libdir}/%{name}/mbmon.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/memcached.so*
-%attr(0644,root,root) %{_libdir}/%{name}/memcached.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/memory.so*
-%attr(0644,root,root) %{_libdir}/%{name}/memory.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/multimeter.so*
-%attr(0644,root,root) %{_libdir}/%{name}/multimeter.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/network.so*
-%attr(0644,root,root) %{_libdir}/%{name}/network.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/nfs.so*
-%attr(0644,root,root) %{_libdir}/%{name}/nfs.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/ntpd.so*
-%attr(0644,root,root) %{_libdir}/%{name}/ntpd.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/nut.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/nut.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/perl.so*
-%attr(0644,root,root) %{_libdir}/%{name}/perl.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/ping.so*
-%attr(0644,root,root) %{_libdir}/%{name}/ping.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/processes.so*
-%attr(0644,root,root) %{_libdir}/%{name}/processes.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/rrdtool.so*
-%attr(0644,root,root) %{_libdir}/%{name}/rrdtool.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/serial.so*
-%attr(0644,root,root) %{_libdir}/%{name}/serial.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/swap.so*
-%attr(0644,root,root) %{_libdir}/%{name}/swap.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/syslog.so*
-%attr(0644,root,root) %{_libdir}/%{name}/syslog.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/tape.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/tape.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/tcpconns.so*
-%attr(0644,root,root) %{_libdir}/%{name}/tcpconns.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/unixsock.so*
-%attr(0644,root,root) %{_libdir}/%{name}/unixsock.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/users.so*
-%attr(0644,root,root) %{_libdir}/%{name}/users.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/uuid.so*
-%attr(0644,root,root) %{_libdir}/%{name}/uuid.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/vserver.so*
-%attr(0644,root,root) %{_libdir}/%{name}/vserver.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/wireless.so*
-%attr(0644,root,root) %{_libdir}/%{name}/wireless.la
+# 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/Collectd.pm
-%attr(0644,root,root) %{_libdir}/perl5/site_perl/5.8.8/Collectd/Unixsock.pm
 %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
+%endif
+
 %files apache
-%attr(0644,root,root) %{_libdir}/%{name}/apache.so*
-%attr(0644,root,root) %{_libdir}/%{name}/apache.la
-%attr(0644,root,root) /etc/collectd.d/apache.conf
+%config %attr(0644,root,root) /etc/collectd.d/apache.conf
+%plugin_macro apache
 
 %files email
 %attr(0644,root,root) %{_libdir}/%{name}/email.so*
 %attr(0644,root,root) %{_libdir}/%{name}/email.la
-%attr(0644,root,root) /etc/collectd.d/email.conf
+%config %attr(0644,root,root) /etc/collectd.d/email.conf
 
 %files mysql
-%attr(0644,root,root) %{_libdir}/%{name}/mysql.so*
-%attr(0644,root,root) %{_libdir}/%{name}/mysql.la
-%attr(0644,root,root) /etc/collectd.d/mysql.conf
+%config %attr(0644,root,root) /etc/collectd.d/mysql.conf
+%plugin_macro mysql
 
 %files nginx
-%attr(0644,root,root) %{_libdir}/%{name}/nginx.so*
-%attr(0644,root,root) %{_libdir}/%{name}/nginx.la
-%attr(0644,root,root) /etc/collectd.d/nginx.conf
+%config %attr(0644,root,root) /etc/collectd.d/nginx.conf
+%plugin_macro nginx
 
 %files sensors
 %attr(0644,root,root) %{_libdir}/%{name}/sensors.so*
 %attr(0644,root,root) %{_libdir}/%{name}/sensors.la
-%attr(0644,root,root) /etc/collectd.d/sensors.conf
+%config %attr(0644,root,root) /etc/collectd.d/sensors.conf
 
 %files snmp
-%attr(0644,root,root) %{_libdir}/%{name}/snmp.so*
-%attr(0644,root,root) %{_libdir}/%{name}/snmp.la
 %attr(0644,root,root) /etc/collectd.d/snmp.conf
+%plugin_macro snmp
 
 %changelog
+* Tue 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
+
 * Mon Mar 17 2008 RightScale <support@rightscale.com> 4.3.1
 - New upstream version
 - Changes to support 4.3.1
index 0ed299b..d928311 100644 (file)
@@ -68,6 +68,10 @@ endif
 if BUILD_WITH_LIBDEVINFO
 collectd_LDADD += -ldevinfo
 endif
+if BUILD_AIX
+collectd_LDFLAGS += -Wl,-bexpall,-brtllib
+collectd_LDADD += -lm
+endif
 
 # The daemon needs to call sg_init, so we need to link it against libstatgrab,
 # too. -octo
@@ -91,6 +95,10 @@ collectd_nagios_LDADD =
 if BUILD_WITH_LIBSOCKET
 collectd_nagios_LDADD += -lsocket
 endif
+if BUILD_AIX
+collectd_nagios_LDADD += -lm
+endif
+
 collectd_nagios_LDADD += libcollectdclient/libcollectdclient.la
 collectd_nagios_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
@@ -173,6 +181,14 @@ collectd_LDADD += "-dlopen" conntrack.la
 collectd_DEPENDENCIES += conntrack.la
 endif
 
+if BUILD_PLUGIN_CONTEXTSWITCH
+pkglib_LTLIBRARIES += contextswitch.la
+contextswitch_la_SOURCES = contextswitch.c
+contextswitch_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" contextswitch.la
+collectd_DEPENDENCIES += contextswitch.la
+endif
+
 if BUILD_PLUGIN_CPU
 pkglib_LTLIBRARIES += cpu.la
 cpu_la_SOURCES = cpu.c
@@ -189,6 +205,9 @@ if BUILD_WITH_LIBSTATGRAB
 cpu_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
 cpu_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
 endif
+if BUILD_WITH_PERFSTAT
+cpu_la_LIBADD += -lperfstat
+endif
 collectd_LDADD += "-dlopen" cpu.la
 collectd_DEPENDENCIES += cpu.la
 endif
@@ -276,6 +295,9 @@ if BUILD_WITH_LIBSTATGRAB
 disk_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)  
 disk_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
 endif
+if BUILD_WITH_PERFSTAT
+disk_la_LIBADD += -lperfstat
+endif
 collectd_LDADD += "-dlopen" disk.la
 collectd_DEPENDENCIES += disk.la
 endif
@@ -366,6 +388,9 @@ if BUILD_WITH_LIBDEVINFO
 interface_la_LIBADD += -ldevinfo
 endif # BUILD_WITH_LIBDEVINFO
 endif # !BUILD_WITH_LIBSTATGRAB
+if BUILD_WITH_PERFSTAT
+interface_la_LIBADD += -lperfstat
+endif
 endif # BUILD_PLUGIN_INTERFACE
 
 if BUILD_PLUGIN_IPTABLES
@@ -444,6 +469,9 @@ if BUILD_WITH_LIBSTATGRAB
 load_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
 load_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
 endif # BUILD_WITH_LIBSTATGRAB
+if BUILD_WITH_PERFSTAT
+load_la_LIBADD += -lperfstat
+endif
 endif # BUILD_PLUGIN_LOAD
 
 if BUILD_PLUGIN_LOGFILE
@@ -470,6 +498,14 @@ collectd_LDADD += "-dlopen" match_empty_counter.la
 collectd_DEPENDENCIES += match_empty_counter.la
 endif
 
+if BUILD_PLUGIN_MATCH_HASHED
+pkglib_LTLIBRARIES += match_hashed.la
+match_hashed_la_SOURCES = match_hashed.c
+match_hashed_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" match_hashed.la
+collectd_DEPENDENCIES += match_hashed.la
+endif
+
 if BUILD_PLUGIN_MATCH_REGEX
 pkglib_LTLIBRARIES += match_regex.la
 match_regex_la_SOURCES = match_regex.c
@@ -546,6 +582,9 @@ if BUILD_WITH_LIBSTATGRAB
 memory_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
 memory_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
 endif
+if BUILD_WITH_PERFSTAT
+memory_la_LIBADD += -lperfstat
+endif
 endif
 
 if BUILD_PLUGIN_MULTIMETER
@@ -570,6 +609,16 @@ endif
 collectd_DEPENDENCIES += mysql.la
 endif
 
+if BUILD_PLUGIN_NETAPP
+pkglib_LTLIBRARIES += netapp.la
+netapp_la_SOURCES = netapp.c
+netapp_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBNETAPP_CPPFLAGS)
+netapp_la_LDFLAGS = -module -avoid-version $(LIBNETAPP_LDFLAGS)
+netapp_la_LIBADD = $(LIBNETAPP_LIBS)
+collectd_LDADD += "-dlopen" netapp.la
+collectd_DEPENDENCIES += netapp.la
+endif
+
 if BUILD_PLUGIN_NETLINK
 pkglib_LTLIBRARIES += netlink.la
 netlink_la_SOURCES = netlink.c
@@ -764,6 +813,16 @@ collectd_LDADD += "-dlopen" powerdns.la
 collectd_DEPENDENCIES += powerdns.la
 endif
 
+if BUILD_PLUGIN_PYTHON
+pkglib_LTLIBRARIES += python.la
+python_la_SOURCES = python.c pyconfig.c pyvalues.c cpython.h
+python_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_PYTHON_CPPFLAGS)
+python_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_PYTHON_LDFLAGS)
+python_la_LIBADD = $(BUILD_WITH_PYTHON_LIBS)
+collectd_LDADD += "-dlopen" python.la
+collectd_DEPENDENCIES += python.la
+endif
+
 if BUILD_PLUGIN_PROCESSES
 pkglib_LTLIBRARIES += processes.la
 processes_la_SOURCES = processes.c
@@ -784,6 +843,16 @@ collectd_LDADD += "-dlopen" protocols.la
 collectd_DEPENDENCIES += protocols.la
 endif
 
+if BUILD_PLUGIN_ROUTEROS
+pkglib_LTLIBRARIES += routeros.la
+routeros_la_SOURCES = routeros.c
+routeros_la_CPPFLAGS = $(BUILD_WITH_LIBROUTEROS_CPPFLAGS)
+routeros_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBROUTEROS_LDFLAGS)
+routeros_la_LIBADD = -lrouteros
+collectd_LDADD += "-dlopen" routeros.la
+collectd_DEPENDENCIES += routeros.la
+endif
+
 if BUILD_PLUGIN_RRDCACHED
 pkglib_LTLIBRARIES += rrdcached.la
 rrdcached_la_SOURCES = rrdcached.c utils_rrdcreate.c utils_rrdcreate.h
@@ -860,6 +929,10 @@ if BUILD_WITH_LIBSTATGRAB
 swap_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
 swap_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
 endif
+if BUILD_WITH_PERFSTAT
+swap_la_LIBADD += -lperfstat
+endif
+
 endif
 
 if BUILD_PLUGIN_SYSLOG
@@ -911,6 +984,14 @@ collectd_LDADD += "-dlopen" target_replace.la
 collectd_DEPENDENCIES += target_replace.la
 endif
 
+if BUILD_PLUGIN_TARGET_SCALE
+pkglib_LTLIBRARIES += target_scale.la
+target_scale_la_SOURCES = target_scale.c
+target_scale_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" target_scale.la
+collectd_DEPENDENCIES += target_scale.la
+endif
+
 if BUILD_PLUGIN_TARGET_SET
 pkglib_LTLIBRARIES += target_set.la
 target_set_la_SOURCES = target_set.c
@@ -1088,6 +1169,7 @@ dist_man_MANS = collectd.1 \
                collectdmon.1 \
                collectd-nagios.1 \
                collectd-perl.5 \
+               collectd-python.5 \
                collectd-snmp.5 \
                collectd-unixsock.5 \
                types.db.5
@@ -1103,6 +1185,7 @@ EXTRA_DIST +=   collectd.conf.pod \
                collectdmon.pod \
                collectd-nagios.pod \
                collectd-perl.pod \
+               collectd-python.pod \
                collectd.pod \
                collectd-snmp.pod \
                collectd-unixsock.pod \
index ebf245c..ad877b5 100644 (file)
@@ -202,7 +202,7 @@ static int config_set_boolean (int *ret_boolean, /* {{{ */
                return (-1);
        }
 
-       if (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)
+       if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
        {
                if (ci->values[0].value.boolean)
                        *ret_boolean = 1;
@@ -212,13 +212,9 @@ static int config_set_boolean (int *ret_boolean, /* {{{ */
        else /* if (ci->values[0].type != OCONFIG_TYPE_STRING) */
        {
                char *string = ci->values[0].value.string;
-               if ((strcasecmp ("true", string) == 0)
-                               || (strcasecmp ("yes", string) == 0)
-                               || (strcasecmp ("on", string) == 0))
+               if (IS_TRUE (string))
                        *ret_boolean = 1;
-               else if ((strcasecmp ("false", string) == 0)
-                               || (strcasecmp ("no", string) == 0)
-                               || (strcasecmp ("off", string) == 0))
+               else if (IS_FALSE (string))
                        *ret_boolean = 0;
                else
                {
index 1e7eca1..6782fce 100644 (file)
@@ -562,12 +562,12 @@ static int ascent_init (void) /* {{{ */
   curl_easy_setopt (curl, CURLOPT_URL, url);
   curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
 
-  if ((verify_peer == NULL) || (strcmp (verify_peer, "true") == 0))
+  if ((verify_peer == NULL) || IS_TRUE (verify_peer))
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1);
   else
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0);
 
-  if ((verify_host == NULL) || (strcmp (verify_host, "true") == 0))
+  if ((verify_host == NULL) || IS_TRUE (verify_host))
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2);
   else
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0);
index b95779d..81b3a2e 100644 (file)
@@ -230,6 +230,23 @@ associated with a certain value.
 
 =back
 
+=head1 ENVIRONMENT
+
+The following environment variables are set by the plugin before calling
+I<exec>:
+
+=over 4
+
+=item COLLECTD_INTERVAL
+
+Value of the global interval setting.
+
+=item COLLECTD_HOSTNAME
+
+Hostname used by I<collectd> to dispatch local values.
+
+=back
+
 =head1 USING NAGIOS PLUGINS
 
 Though the interface is far from perfect, there are tons of plugins for Nagios.
diff --git a/src/collectd-python.pod b/src/collectd-python.pod
new file mode 100644 (file)
index 0000000..45a06d1
--- /dev/null
@@ -0,0 +1,664 @@
+=head1 NAME
+
+collectd-python - Documentation of collectd's C<python plugin>
+
+=head1 SYNOPSIS
+
+  <LoadPlugin python>
+    Globals true
+  </LoadPlugin>
+  # ...
+  <Plugin python>
+    ModulePath "/path/to/your/python/modules"
+    LogTraces true
+    Interactive true
+    Import "spam"
+
+    <Module spam>
+      spam "wonderful" "lovely"
+    </Module>
+  </Plugin>
+
+=head1 DESCRIPTION
+
+The C<python plugin> embeds a Python-interpreter into collectd and provides an
+interface to collectd's plugin system. This makes it possible to write plugins
+for collectd in Python. This is a lot more efficient than executing a
+Python-script every time you want to read a value with the C<exec plugin> (see
+L<collectd-exec(5)>) and provides a lot more functionality, too.
+
+Currently only I<Python 2> is supported and at least I<version 2.3> is
+required.
+
+=head1 CONFIGURATION
+
+=over 4
+
+=item B<LoadPlugin> I<Plugin>
+
+Loads the Python plugin I<Plugin>. Unlike most other LoadPlugin lines, this one
+should be a block containing the line "Globals true". This will cause collectd
+to export the name of all objects in the python interpreter for all plugins to
+see. If you don't do this or your platform does not support it, the embedded
+interpreter will start anyway but you won't be able to load certain python
+modules, e.g. "time".
+
+=item B<Encoding> I<Name>
+
+The default encoding for Unicode objects you pass to collectd. If you omit this
+option it will default to B<ascii> on I<Python 2> and B<utf-8> on I<Python 3>.
+This is hardcoded in Python and will ignore everything else, including your
+locale.
+
+=item B<ModulePath> I<Name>
+
+Appends I<Name> to B<sys.path>. You won't be able to import any scripts you
+wrote unless they are located in one of the directories in this list. Please
+note that it only has effect on plugins loaded after this option. You can
+use multiple B<ModulePath> lines to add more than one directory.
+
+=item B<LogTraces> I<bool>
+
+If a python script throws an exception it will be logged by collectd with the
+name of the exception and the message. If you set this option to true it will
+also log the full stacktrace just like the default output of an interactive
+python interpreter. This should probably be set to false most of the time but
+is very useful for development and debugging of new modules.
+
+=item B<Interactive> I<bool>
+
+This option will cause the module to launch an interactive python interpreter
+that reads from and writes to the terminal. Note that collectd will terminate
+right after starting up if you try to run it as a daemon while this option is
+enabled to make sure to start collectd with the B<-f> option.
+
+The B<collectd> module is I<not> imported into the interpreter's globals. You
+have to do it manually. Be sure to read the help text of the module, it can be
+used as a reference guide during coding.
+
+This interactive session will behave slightly differently from a daemonized
+collectd script as well as from a normal python interpreter:
+
+=over 4
+
+=item
+
+B<1.> collectd will try to import the B<readline> module to give you a decent
+way of entering your commands. The daemonized collectd won't do that.
+
+=item
+
+B<2.> collectd will block I<SIGINT>. Pressing I<Ctrl+C> will usually cause
+collectd to shut down. This would be problematic in an interactive session,
+therefore this signal will be blocked. You can still use it to interrupt
+syscalls like sleep and pause but it won't generate a I<KeyboardInterrupt>
+exception either.
+
+To quit collectd send I<EOF> (press I<Ctrl+D> at the beginning of a new line).
+
+=item
+
+B<3.> collectd handles I<SIGCHLD>. This means that python won't be able to
+determine the return code of spawned processes with system(), popen() and
+subprocess. This will result in python not using external programs like less
+to display help texts. You can override this behavior with the B<PAGER>
+environment variable, e.g. I<export PAGER=less> before starting collectd.
+Depending on your version of python this might or might not result in an
+B<OSError> exception which can be ignored.
+
+=back
+
+=item E<lt>B<Module> I<Name>E<gt> block
+
+This block may be used to pass on configuration settings to a Python module.
+The configuration is converted into an instance of the B<Config> class which is
+passed to the registered configuration callback. See below for details about
+the B<Config> class and how to register callbacks.
+
+The I<name> identifies the callback.
+
+=back
+
+=head1 WRITING YOUR OWN PLUGINS
+
+Writing your own plugins is quite simple. collectd manages plugins by means of
+B<dispatch functions> which call the appropriate B<callback functions>
+registered by the plugins. Any plugin basically consists of the implementation
+of these callback functions and initializing code which registers the
+functions with collectd. See the section "EXAMPLES" below for a really basic
+example. The following types of B<callback functions> are known to collectd
+(all of them are optional):
+
+=over 4
+
+=item configuration functions
+
+This type of functions is called during configuration if an appropriate
+B<Module> block has been encountered. It is called once for each B<Module>
+block which matches the name of the callback as provided with the
+B<register_config> method - see below.
+
+Python thread support has not been initialized at this point so do not use any
+threading functions here!
+
+=item init functions
+
+This type of functions is called once after loading the module and before any
+calls to the read and write functions. It should be used to initialize the
+internal state of the plugin (e.E<nbsp>g. open sockets, ...). This is the
+earliest point where you may use threads.
+
+=item read functions
+
+This type of function is used to collect the actual data. It is called once
+per interval (see the B<Interval> configuration option of collectd). Usually
+it will call B<plugin_dispatch_values> to dispatch the values to collectd
+which will pass them on to all registered B<write functions>. If this function
+throws any kind of exception the plugin will be skipped for an increasing
+amount of time until it returns normally again.
+
+=item write functions
+
+This type of function is used to write the dispatched values. It is called
+once for every value that was dispatched by any plugin.
+
+=item flush functions
+
+This type of function is used to flush internal caches of plugins. It is
+usually triggered by the user only. Any plugin which caches data before
+writing it to disk should provide this kind of callback function.
+
+=item log functions
+
+This type of function is used to pass messages of plugins or the daemon itself
+to the user.
+
+=item notification function
+
+This type of function is used to act upon notifications. In general, a
+notification is a status message that may be associated with a data instance.
+Usually, a notification is generated by the daemon if a configured threshold
+has been exceeded (see the section "THRESHOLD CONFIGURATION" in
+L<collectd.conf(5)> for more details), but any plugin may dispatch
+notifications as well.
+
+=item shutdown functions
+
+This type of function is called once before the daemon shuts down. It should
+be used to clean up the plugin (e.g. close sockets, ...).
+
+=back
+
+Any function (except log functions) may set throw an exception in case of any
+errors. The exception will be passed on to the user using collectd's logging
+mechanism. If a log callback throws an exception it will be printed to standard
+error instead.
+
+See the documentation of the various B<register_> methods in the section
+"FUNCTIONS" below for the number and types of arguments passed to each
+B<callback function>. This section also explains how to register B<callback
+functions> with collectd.
+
+To enable a module, copy it to a place where Python can find it (i.E<nbsp>e. a
+directory listed in B<sys.path>) just as any other Python plugin and add
+an appropriate B<Import> option to the configuration file. After restarting
+collectd you're done.
+
+=head1 CLASSES
+
+The following complex types are used to pass values between the Python plugin
+and collectd:
+
+=head2 Config
+
+The Config class is an object which keeps the information provided in the
+configuration file. The sequence of children keeps one entry for each
+configuration option. Each such entry is another Config instance, which
+may nest further if nested blocks are used.
+
+ class Config(object)
+
+This represents a piece of collectd's config file. It is passed to scripts with
+config callbacks (see B<register_config>) and is of little use if created
+somewhere else.
+
+It has no methods beyond the bare minimum and only exists for its data members.
+
+Data descriptors defined here:
+
+=over 4
+
+=item parent
+
+This represents the parent of this node. On the root node
+of the config tree it will be None.
+
+=item key
+
+This is the keyword of this item, i.e. the first word of any given line in the
+config file. It will always be a string.
+
+=item values
+
+This is a tuple (which might be empty) of all value, i.e. words following the
+keyword in any given line in the config file.
+
+Every item in this tuple will be either a string or a float or a boolean,
+depending on the contents of the configuration file.
+
+=item children
+
+This is a tuple of child nodes. For most nodes this will be empty. If this node
+represents a block instead of a single line of the config file it will contain
+all nodes in this block.
+
+=back
+
+=head2 PluginData
+
+This should not be used directly but it is the base class for both Values and
+Notification. It is used to identify the source of a value or notification.
+
+ class PluginData(object)
+
+This is an internal class that is the base for Values and Notification. It is
+pretty useless by itself and was therefore not exported to the collectd module.
+
+Data descriptors defined here:
+
+=over 4
+
+=item host
+
+The hostname of the host this value was read from. For dispatching this can be
+set to an empty string which means the local hostname as defined in
+collectd.conf.
+
+=item plugin
+
+The name of the plugin that read the data. Setting this member to an empty
+string will insert "python" upon dispatching.
+
+=item plugin_instance
+
+Plugin instance string. May be empty.
+
+=item time
+
+This is the Unix timestamp of the time this value was read. For dispatching
+values this can be set to zero which means "now". This means the time the value
+is actually dispatched, not the time it was set to 0.
+
+=item type
+
+The type of this value. This type has to be defined in your I<types.db>.
+Attempting to set it to any other value will raise a I<TypeError> exception.
+Assigning a type is mandatory, calling dispatch without doing so will raise a
+I<RuntimeError> exception.
+
+=item type_instance
+
+Type instance string. May be empty.
+
+=back
+
+=head2 Values
+
+A Value is an object which features a sequence of values. It is based on then
+I<PluginData> type and uses its members to identify the values.
+
+ class Values(PluginData)
+
+A Values object used for dispatching values to collectd and receiving values
+from write callbacks.
+
+Method resolution order:
+
+=over 4
+
+=item Values
+
+=item PluginData
+
+=item object
+
+=back
+
+Methods defined here:
+
+=over 4
+
+=item B<dispatch>([type][, values][, plugin_instance][, type_instance][, plugin][, host][, time][, interval]) -> None.
+
+Dispatch this instance to the collectd process. The object has members for each
+of the possible arguments for this method. For a detailed explanation of these
+parameters see the member of the same same.
+
+If you do not submit a parameter the value saved in its member will be
+submitted. If you do provide a parameter it will be used instead, without
+altering the member.
+
+=item B<write>([destination][, type][, values][, plugin_instance][, type_instance][, plugin][, host][, time][, interval]) -> None.
+
+Write this instance to a single plugin or all plugins if "destination" is
+omitted. This will bypass the main collectd process and all filtering and
+caching. Other than that it works similar to "dispatch". In most cases
+"dispatch" should be used instead of "write".
+
+=back
+
+Data descriptors defined here:
+
+=over 4
+
+=item interval
+
+The interval is the timespan in seconds between two submits for the same data
+source. This value has to be a positive integer, so you can't submit more than
+one value per second. If this member is set to a non-positive value, the
+default value as specified in the config file will be used (default: 10).
+
+If you submit values more often than the specified interval, the average will
+be used. If you submit less values, your graphs will have gaps.
+
+=item values
+
+These are the actual values that get dispatched to collectd. It has to be a
+sequence (a tuple or list) of numbers. The size of the sequence and the type of
+its content depend on the type member your I<types.db> file. For more
+information on this read the L<types.db(5)> manual page.
+
+If the sequence does not have the correct size upon dispatch a I<RuntimeError>
+exception will be raised. If the content of the sequence is not a number, a
+I<TypeError> exception will be raised.
+
+=back
+
+=head2 Notification
+
+A notification is an object defining the severity and message of the status
+message as well as an identification of a data instance by means of the members
+of I<PluginData> on which it is based.
+
+class Notification(PluginData)
+The Notification class is a wrapper around the collectd notification.
+It can be used to notify other plugins about bad stuff happening. It works
+similar to Values but has a severity and a message instead of interval
+and time.
+Notifications can be dispatched at any time and can be received with
+register_notification.
+
+Method resolution order:
+
+=over 4
+
+=item Notification
+
+=item PluginData
+
+=item object
+
+=back
+
+Methods defined here:
+
+=over 4
+
+=item B<dispatch>([type][, values][, plugin_instance][, type_instance][, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.
+
+Dispatch this instance to the collectd process. The object has members for each
+of the possible arguments for this method. For a detailed explanation of these
+parameters see the member of the same same.
+
+If you do not submit a parameter the value saved in its member will be
+submitted. If you do provide a parameter it will be used instead, without
+altering the member.
+
+=back
+
+Data descriptors defined here:
+
+=over 4
+
+=item message
+
+Some kind of description what's going on and why this Notification was
+generated.
+
+=item severity
+
+The severity of this notification. Assign or compare to I<NOTIF_FAILURE>,
+I<NOTIF_WARNING> or I<NOTIF_OKAY>.
+
+=back
+
+=head1 FUNCTIONS
+
+The following functions provide the C-interface to Python-modules.
+
+=over 4
+
+=item B<register_*>(I<callback>[, I<data>][, I<name>]) -> identifier
+
+There are eight different register functions to get callback for eight
+different events. With one exception all of them are called as shown above.
+
+=over 4
+
+=item
+
+I<callback> is a callable object that will be called every time the event is
+triggered.
+
+=item
+
+I<data> is an optional object that will be passed back to the callback function
+every time it is called. If you omit this parameter no object is passed back to
+your callback, not even None.
+
+=item
+
+I<name> is an optional identifier for this callback. The default name is
+B<python>.I<module>. I<module> is taken from the B<__module__> attribute of
+your callback function. Every callback needs a unique identifier, so if you
+want to register the same callback multiple time in the same module you need to
+specify a name here. Otherwise it's save to ignore this parameter I<identifier>
+is the full identifier assigned to this callback.
+
+=back
+
+These functions are called in the various stages of the daemon (see the section
+L<"WRITING YOUR OWN PLUGINS"> above) and are passed the following arguments:
+
+=over 4
+
+=item register_config
+
+The only argument passed is a I<Config> object. See above for the layout of this
+data type.
+Note that you can not receive the whole config files this way, only B<Module>
+blocks inside the Python configuration block. Additionally you will only
+receive blocks where your callback identifier matches B<python.>I<blockname>.
+
+=item register_init
+
+The callback will be called without arguments.
+
+=item register_read(callback[, interval][, data][, name]) -> identifier
+
+This function takes an additional parameter: I<interval>. It specifies the
+time between calls to the callback function.
+
+The callback will be called without arguments.
+
+=item register_shutdown
+
+The callback will be called without arguments.
+
+=item register_write
+
+The callback function will be called with one arguments passed, which will be a
+I<Values> object. For the layout of I<Values> see above.
+If this callback function throws an exception the next call will be delayed by
+an increasing interval.
+
+=item register_flush
+
+Like B<register_config> is important for this callback because it determines
+what flush requests the plugin will receive.
+
+The arguments passed are I<timeout> and I<identifier>. I<timeout> indicates
+that only data older than I<timeout> seconds is to be flushed. I<identifier>
+specifies which values are to be flushed.
+
+=item register_log
+
+The arguments are I<severity> and I<message>. The severity is an integer and
+small for important messages and high for less important messages. The least
+important level is B<LOG_DEBUG>, the most important level is B<LOG_ERR>. In
+between there are (from least to most important): B<LOG_INFO>, B<LOG_NOTICE>,
+and B<LOG_WARNING>. I<message> is simply a string B<without> a newline at the
+end.
+
+If this callback throws an exception it will B<not> be logged. It will just be
+printed to B<sys.stderr> which usually means silently ignored.
+
+=item register_notification
+
+The only argument passed is a I<Notification> object. See above for the layout of this
+data type.
+
+=back
+
+=item B<unregister_*>(I<identifier>) -> None
+
+Removes a callback or data-set from collectd's internal list of callback
+functions. Every I<register_*> function has an I<unregister_*> function.
+I<identifier> is either the string that was returned by the register function
+or a callback function. The identifier will be constructed in the same way as
+for the register functions.
+
+=item B<flush>(I<plugin[, I<timeout>][, I<identifier>]) -> None
+
+Flush one or all plugins. I<timeout> and the specified I<identifiers> are
+passed on to the registered flush-callbacks. If omitted, the timeout defaults
+to C<-1>. The identifier defaults to None. If the B<plugin> argument has been
+specified, only named plugin will be flushed.
+
+=item B<error>, B<warning>, B<notice>, B<info>, B<debug>(I<message>)
+
+Log a message with the specified severity.
+
+=back
+
+=head1 EXAMPLES
+
+Any Python module will start similar to:
+
+  import collectd
+
+A very simple read function might look like:
+
+  def read(data=None):
+    vl = collectd.Values(type='gauge')
+    vl.plugin='python.spam'
+    vl.dispatch(values=[random.random() * 100])
+
+A very simple write function might look like:
+
+  def write(vl, data=None):
+    for i in vl.values:
+      print "%s (%s): %f" % (vl.plugin, vl.type, i)
+
+To register those functions with collectd:
+
+  collectd.register_read(read);
+  collectd.register_write(write);
+
+See the section L<"CLASSES"> above for a complete documentation of the data
+types used by the read, write and match functions.
+
+=head1 NOTES
+
+=over 4
+
+=item
+
+Please feel free to send in new plugins to collectd's mailinglist at
+E<lt>collectdE<nbsp>atE<nbsp>verplant.orgE<gt> for review and, possibly,
+inclusion in the main distribution. In the latter case, we will take care of
+keeping the plugin up to date and adapting it to new versions of collectd.
+
+Before submitting your plugin, please take a look at
+L<http://collectd.org/dev-info.shtml>.
+
+=back
+
+=head1 CAVEATS
+
+=over 4
+
+=item
+
+collectd is heavily multi-threaded. Each collectd thread accessing the python
+plugin will be mapped to a Python interpreter thread. Any such thread will be
+created and destroyed transparently and on-the-fly.
+
+Hence, any plugin has to be thread-safe if it provides several entry points
+from collectd (i.E<nbsp>e. if it registers more than one callback or if a
+registered callback may be called more than once in parallel).
+
+=item
+
+The Python thread module is initialized just before calling the init callbacks.
+This means you must not use Python's threading module prior to this point. This
+includes all config and possibly other callback as well.
+
+=item
+
+The python plugin exports the internal API of collectd which is considered
+unstable and subject to change at any time. We try hard to not break backwards
+compatibility in the Python API during the life cycle of one major release.
+However, this cannot be guaranteed at all times. Watch out for warnings
+dispatched by the python plugin after upgrades.
+
+=back
+
+=head1 KNOWN BUGS
+
+=over 4
+
+=item
+
+This plugin is not compatible with python3. Trying to compile it with python3
+will fail because of the ways string, unicode and bytearray behavior was
+changed.
+
+=item
+
+Not all aspects of the collectd API are accessible from python. This includes
+but is not limited to meta-data, filters and data sets.
+
+=back
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>,
+L<collectd-perl(5)>,
+L<collectd-exec(5)>,
+L<types.db(5)>,
+L<python(1)>,
+
+=head1 AUTHOR
+
+The C<python plugin> has been written by
+Sven Trenkel E<lt>collectdE<nbsp>atE<nbsp>semidefinite.deE<gt>.
+
+This manpage has been written by Sven Trenkel
+E<lt>collectdE<nbsp>atE<nbsp>semidefinite.deE<gt>.
+It is based on the L<collectd-perl(5)> manual page by
+Florian Forster E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt> and
+Sebastian Harl E<lt>shE<nbsp>atE<nbsp>tokkee.orgE<gt>.
+
+=cut
index 576abef..bc69a3b 100644 (file)
@@ -102,9 +102,7 @@ static int init_hostname (void)
        }
 
        str = global_option_get ("FQDNLookup");
-       if ((strcasecmp ("false", str) == 0)
-                       || (strcasecmp ("no", str) == 0)
-                       || (strcasecmp ("off", str) == 0))
+       if (IS_FALSE (str))
                return (0);
 
        memset (&ai_hints, '\0', sizeof (ai_hints));
index de52fbe..844c83b 100644 (file)
@@ -57,6 +57,7 @@ FQDNLookup   true
 #@BUILD_PLUGIN_BATTERY_TRUE@LoadPlugin battery
 #@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind
 #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack
+#@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 #@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq
 @LOAD_PLUGIN_CSV@LoadPlugin csv
@@ -88,6 +89,7 @@ FQDNLookup   true
 @BUILD_PLUGIN_MEMORY_TRUE@@BUILD_PLUGIN_MEMORY_TRUE@LoadPlugin memory
 #@BUILD_PLUGIN_MULTIMETER_TRUE@LoadPlugin multimeter
 #@BUILD_PLUGIN_MYSQL_TRUE@LoadPlugin mysql
+#@BUILD_PLUGIN_NETAPP_TRUE@LoadPlugin netapp
 #@BUILD_PLUGIN_NETLINK_TRUE@LoadPlugin netlink
 @LOAD_PLUGIN_NETWORK@LoadPlugin network
 #@BUILD_PLUGIN_NFS_TRUE@LoadPlugin nfs
@@ -106,6 +108,8 @@ FQDNLookup   true
 #@BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns
 #@BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes
 #@BUILD_PLUGIN_PROTOCOLS_TRUE@LoadPlugin protocols
+#@BUILD_PLUGIN_PYTHON_TRUE@LoadPlugin python
+#@BUILD_PLUGIN_ROUTEROS_TRUE@LoadPlugin routeros
 #@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached
 @LOAD_PLUGIN_RRDTOOL@LoadPlugin rrdtool
 #@BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors
@@ -186,6 +190,7 @@ FQDNLookup   true
 #    URL "http://finance.google.com/finance?q=NYSE%3AAMD"
 #    User "foo"
 #    Password "bar"
+#    MeasureResponseTime false
 #    <Match>
 #      Regex "<span +class=\"pr\"[^>]*> *([0-9]*\\.[0-9]+) *</span>"
 #      DSType "GaugeAverage"
@@ -254,6 +259,8 @@ FQDNLookup   true
 #      FSType "ext3"
 #      IgnoreSelected false
 #      ReportByDevice false
+#      ReportReserved false
+#      ReportInodes false
 #</Plugin>
 
 #<Plugin disk>
@@ -411,6 +418,58 @@ FQDNLookup   true
 #      </Database>
 #</Plugin>
 
+#<Plugin netapp>
+#      <Host "netapp1.example.com">
+#              Protocol      "https"
+#              Address       "10.0.0.1"
+#              Port          443
+#              User          "username"
+#              Password      "aef4Aebe"
+#              Interval      30
+#
+#              <WAFL>
+#                      Interval 30
+#                      GetNameCache   true
+#                      GetDirCache    true
+#                      GetBufferCache true
+#                      GetInodeCache  true
+#              </WAFL>
+#
+#              <Disks>
+#                      Interval 30
+#                      GetBusy true
+#              </Disks>
+#
+#              <VolumePerf>
+#                      Interval 30
+#                      GetIO      "volume0"
+#                      IgnoreSelectedIO      false
+#                      GetOps     "volume0"
+#                      IgnoreSelectedOps     false
+#                      GetLatency "volume0"
+#                      IgnoreSelectedLatency false
+#              </VolumePerf>
+#
+#              <VolumeUsage>
+#                      Interval 30
+#                      GetCapacity "vol0"
+#                      GetCapacity "vol1"
+#                      IgnoreSelectedCapacity false
+#                      GetSnapshot "vol1"
+#                      GetSnapshot "vol3"
+#                      IgnoreSelectedSnapshot false
+#              </VolumeUsage>
+#
+#              <System>
+#                      Interval 30
+#                      GetCPULoad     true
+#                      GetInterfaces  true
+#                      GetDiskOps     true
+#                      GetDiskIO      true
+#              </System>
+#      </Host>
+#</Plugin>
+
 #<Plugin netlink>
 #      Interface "All"
 #      VerboseInterface "All"
@@ -428,6 +487,7 @@ FQDNLookup   true
 #      TimeToLive "128"
 #      Forward false
 #      CacheFlush 1800
+#      ReportStats false
 @LOAD_PLUGIN_NETWORK@</Plugin>
 
 #<Plugin nginx>
@@ -504,7 +564,8 @@ FQDNLookup   true
 #      IncludeDir "/my/include/path"
 #      BaseName "Collectd::Plugin"
 #      EnableDebugger ""
-#      LoadPlugin foo
+#      LoadPlugin Monitorus
+#      LoadPlugin OpenVZ
 #
 #      <Plugin foo>
 #              Foo "Bar"
@@ -514,7 +575,12 @@ FQDNLookup   true
 
 #<Plugin ping>
 #      Host "host.foo.bar"
+#      Interval 1.0
+#      Timeout 0.9
 #      TTL 255
+#      SourceAddress "1.2.3.4"
+#      Device "eth0"
+#      MaxMissed -1
 #</Plugin>
 
 #<Plugin postgresql>
@@ -580,6 +646,28 @@ FQDNLookup   true
 #      IgnoreSelected false
 #</Plugin>
 
+#<Plugin python>
+#      ModulePath "/path/to/your/python/modules"
+#      LogTraces true
+#      Interactive true
+#      Import "spam"
+#
+#      <Module spam>
+#              spam "wonderful" "lovely"
+#      </Module>
+#</Plugin>
+
+#<Plugin routeros>
+#      <Router>
+#              Host "router.example.com"
+#              Port "8728"
+#              User "admin"
+#              Password "dozaiTh4"
+#              CollectInterface true
+#              CollectRegistrationTable true
+#      </Router>
+#</Plugin>
+
 #<Plugin rrdcached>
 #      DaemonAddress "unix:/tmp/rrdcached.sock"
 #      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/rrd"
@@ -738,6 +826,7 @@ FQDNLookup   true
 
 # Load required matches:
 #@BUILD_PLUGIN_MATCH_EMPTY_COUNTER_TRUE@LoadPlugin match_empty_counter
+#@BUILD_PLUGIN_MATCH_HASHED_TRUE@LoadPlugin match_hashed
 #@BUILD_PLUGIN_MATCH_REGEX_TRUE@LoadPlugin match_regex
 #@BUILD_PLUGIN_MATCH_VALUE_TRUE@LoadPlugin match_value
 #@BUILD_PLUGIN_MATCH_TIMEDIFF_TRUE@LoadPlugin match_timediff
@@ -745,6 +834,7 @@ FQDNLookup   true
 # Load required targets:
 #@BUILD_PLUGIN_TARGET_NOTIFICATION_TRUE@LoadPlugin target_notification
 #@BUILD_PLUGIN_TARGET_REPLACE_TRUE@LoadPlugin target_replace
+#@BUILD_PLUGIN_TARGET_SCALE_TRUE@LoadPlugin target_scale
 #@BUILD_PLUGIN_TARGET_SET_TRUE@LoadPlugin target_set
 
 #----------------------------------------------------------------------------#
index 946270f..33d4f35 100644 (file)
@@ -501,12 +501,18 @@ 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<MeasureResponseTime> B<true>|B<false>
+
+Measure response time for the request. If this setting is enabled, B<Match>
+blocks (see below) are optional. Disabled by default.
+
 =item B<E<lt>MatchE<gt>>
 
 One or more B<Match> blocks that define how to match information in the data
 returned by C<libcurl>. The C<curl> plugin uses the same infrastructure that's
 used by the C<tail> plugin, so please see the documentation of the C<tail>
-plugin below on how matches are defined.
+plugin below on how matches are defined. If the B<MeasureResponseTime> option
+is set to B<true>, B<Match> blocks are optional.
 
 =back
 
@@ -866,19 +872,44 @@ Select partitions based on the mountpoint.
 
 Select partitions based on the filesystem type.
 
-=item B<IgnoreSelected> I<true>|I<false>
+=item B<IgnoreSelected> B<true>|B<false>
 
 Invert the selection: If set to true, all partitions B<except> the ones that
 match any one of the criteria are collected. By default only selected
 partitions are collected if a selection is made. If no selection is configured
 at all, B<all> partitions are selected.
 
-=item B<ReportByDevice> I<true>|I<false>
+=item B<ReportByDevice> B<true>|B<false>
 
 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
+inode collection being disabled.
+
+Enable this option if inodes are a scarce resource for you, usually because
+many small files are stored on the disk. This is a usual scenario for mail
+transfer agents and web caches.
+
 =back
 
 =head2 Plugin C<disk>
@@ -1640,6 +1671,463 @@ or SQL threads are not running.
 
 =back
 
+=head2 Plugin C<netapp>
+
+The netapp plugin can collect various performance and capacity information
+from a NetApp filer using the NetApp API.
+
+Please note that NetApp has a wide line of products and a lot of different
+software versions for each of these products. This plugin was developed for a
+NetApp FAS3040 running OnTap 7.2.3P8 and tested on FAS2050 7.3.1.1L1,
+FAS3140 7.2.5.1 and FAS3020 7.2.4P9. It I<should> work for most combinations of
+model and software version but it is very hard to test this.
+If you have used this plugin with other models and/or software version, feel
+free to send us a mail to tell us about the results, even if it's just a short
+"It works".
+
+To collect these data collectd will log in to the NetApp via HTTP(S) and HTTP
+basic authentication.
+
+B<Do not use a regular user for this!> Create a special collectd user with just
+the minimum of capabilities needed. The user only needs the "login-http-admin"
+capability as well as a few more depending on which data will be collected.
+Required capabilities are documented below.
+
+=head3 Synopsis
+
+ <Plugin "netapp">
+   <Host "netapp1.example.com">
+    Protocol      "https"
+    Address       "10.0.0.1"
+    Port          443
+    User          "username"
+    Password      "aef4Aebe"
+    Interval      30
+    
+    <WAFL>
+      Interval 30
+      GetNameCache   true
+      GetDirCache    true
+      GetBufferCache true
+      GetInodeCache  true
+    </WAFL>
+    
+    <Disks>
+      Interval 30
+      GetBusy true
+    </Disks>
+    
+    <VolumePerf>
+      Interval 30
+      GetIO      "volume0"
+      IgnoreSelectedIO      false
+      GetOps     "volume0"
+      IgnoreSelectedOps     false
+      GetLatency "volume0"
+      IgnoreSelectedLatency false
+    </VolumePerf>
+    
+    <VolumeUsage>
+      Interval 30
+      GetCapacity "vol0"
+      GetCapacity "vol1"
+      IgnoreSelectedCapacity false
+      GetSnapshot "vol1"
+      GetSnapshot "vol3"
+      IgnoreSelectedSnapshot false
+    </VolumeUsage>
+    
+    <System>
+      Interval 30
+      GetCPULoad     true
+      GetInterfaces  true
+      GetDiskOps     true
+      GetDiskIO      true
+    </System>
+   </Host>
+ </Plugin>
+
+The netapp plugin accepts the following configuration options:
+
+=over 4
+
+=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.
+
+=item B<Protocol> B<httpd>|B<http>
+
+The protocol collectd will use to query this host.
+
+Optional
+
+Type: string
+
+Default: https
+
+Valid options: http, https
+
+=item B<Address> I<Address>
+
+The hostname or IP address of the host.
+
+Optional
+
+Type: string
+
+Default: The "host" block's name.
+
+=item B<Port> I<Port>
+
+The TCP port to connect to on the host.
+
+Optional
+
+Type: integer
+
+Default: 80 for protocol "http", 443 for protocol "https"
+
+=item B<User> I<User>
+
+=item B<Password> I<Password>
+
+The username and password to use to login to the NetApp.
+
+Mandatory
+
+Type: string
+
+=item B<Interval> I<Interval>
+
+B<TODO>
+
+=back
+
+The following options decide what kind of data will be collected. You can
+either use them as a block and fine tune various parameters inside this block,
+use them as a single statement to just accept all default values, or omit it to
+not collect any data.
+
+The following options are valid inside all blocks:
+
+=over 4
+
+=item B<Interval> I<Seconds>
+
+Collect the respective statistics every I<Seconds> seconds. Defaults to the
+host specific setting.
+
+=back
+
+=head3 The System block
+
+This will collect various performance data about the whole system.
+
+B<Note:> To get this data the collectd user needs the
+"api-perf-object-get-instances" capability.
+
+=over 4
+
+=item B<Interval> I<Seconds>
+
+Collect disk statistics every I<Seconds> seconds.
+
+=item B<GetCPULoad> B<true>|B<false>
+
+If you set this option to true the current CPU usage will be read. This will be
+the average usage between all CPUs in your NetApp without any information about
+individual CPUs.
+
+B<Note:> These are the same values that the NetApp CLI command "sysstat"
+returns in the "CPU" field.
+
+Optional
+
+Type: boolean
+
+Default: true
+
+Result: Two value lists of type "cpu", and type instances "idle" and "system".
+
+=item B<GetInterfaces> B<true>|B<false>
+
+If you set this option to true the current traffic of the network interfaces
+will be read. This will be the total traffic over all interfaces of your NetApp
+without any information about individual interfaces.
+
+B<Note:> This is the same values that the NetApp CLI command "sysstat" returns
+in the "Net kB/s" field.
+
+B<Or is it?>
+
+Optional
+
+Type: boolean
+
+Default: true
+
+Result: One value list of type "if_octects".
+
+=item B<GetDiskIO> B<true>|B<false>
+
+If you set this option to true the current IO throughput will be read. This
+will be the total IO of your NetApp without any information about individual
+disks, volumes or aggregates.
+
+B<Note:> This is the same values that the NetApp CLI command "sysstat" returns
+in the "DiskE<nbsp>kB/s" field.
+
+Optional
+
+Type: boolean
+
+Default: true
+
+Result: One value list of type "disk_octets".
+
+=item B<GetDiskOps> B<true>|B<false>
+
+If you set this option to true the current number of HTTP, NFS, CIFS, FCP,
+iSCSI, etc. operations will be read. This will be the total number of
+operations on your NetApp without any information about individual volumes or
+aggregates.
+
+B<Note:> These are the same values that the NetApp CLI command "sysstat"
+returns in the "NFS", "CIFS", "HTTP", "FCP" and "iSCSI" fields.
+
+Optional
+
+Type: boolean
+
+Default: true
+
+Result: A variable number of value lists of type "disk_ops_complex". Each type
+of operation will result in one value list with the name of the operation as
+type instance.
+
+=back
+
+=head3 The WAFL block
+
+This will collect various performance data about the WAFL file system. At the
+moment this just means cache performance.
+
+B<Note:> To get this data the collectd user needs the
+"api-perf-object-get-instances" capability.
+
+B<Note:> The interface to get these values is classified as "Diagnostics" by
+NetApp. This means that it is not guaranteed to be stable even between minor
+releases.
+
+=over 4
+
+=item B<Interval> I<Seconds>
+
+Collect disk statistics every I<Seconds> seconds.
+
+=item B<GetNameCache> B<true>|B<false>
+
+Optional
+
+Type: boolean
+
+Default: true
+
+Result: One value list of type "cache_ratio" and type instance
+"name_cache_hit".
+
+=item B<GetDirCache> B<true>|B<false>
+
+Optional
+
+Type: boolean
+
+Default: true
+
+Result: One value list of type "cache_ratio" and type instance "find_dir_hit".
+
+=item B<GetInodeCache> B<true>|B<false>
+
+Optional
+
+Type: boolean
+
+Default: true
+
+Result: One value list of type "cache_ratio" and type instance
+"inode_cache_hit".
+
+=item B<GetBufferCache> B<true>|B<false>
+
+B<Note:> This is the same value that the NetApp CLI command "sysstat" returns
+in the "Cache hit" field.
+
+Optional
+
+Type: boolean
+
+Default: true
+
+Result: One value list of type "cache_ratio" and type instance "buf_hash_hit".
+
+=back
+
+=head3 The Disks block
+
+This will collect performance data about the individual disks in the NetApp.
+
+B<Note:> To get this data the collectd user needs the
+"api-perf-object-get-instances" capability.
+
+=over 4
+
+=item B<Interval> I<Seconds>
+
+Collect disk statistics every I<Seconds> seconds.
+
+=item B<GetBusy> B<true>|B<false>
+
+If you set this option to true the busy time of all disks will be calculated
+and the value of the busiest disk in the system will be written.
+
+B<Note:> This is the same values that the NetApp CLI command "sysstat" returns
+in the "Disk util" field. Probably.
+
+Optional
+
+Type: boolean
+
+Default: true
+
+Result: One value list of type "percent" and type instance "disk_busy".
+
+=back
+
+=head3 The VolumePerf block
+
+This will collect various performance data about the individual volumes.
+
+You can select which data to collect about which volume using the following
+options. They follow the standard ignorelist semantic.
+
+B<Note:> To get this data the collectd user needs the
+I<api-perf-object-get-instances> capability.
+
+=over 4
+
+=item B<Interval> I<Seconds>
+
+Collect volume performance data every I<Seconds> seconds.
+
+=item B<GetIO> I<Volume>
+
+=item B<GetOps> I<Volume>
+
+=item B<GetLatency> I<Volume>
+
+Select the given volume for IO, operations or latency statistics collection.
+The argument is the name of the volume without the C</vol/> prefix.
+
+Since the standard ignorelist functionality is used here, you can use a string
+starting and ending with a slash to specify regular expression matching: To
+match the volumes "vol0", "vol2" and "vol7", you can use this regular
+expression:
+
+  GetIO "/^vol[027]$/"
+
+If no regular expression is specified, an exact match is required. Both,
+regular and exact matching are case sensitive.
+
+If no volume was specified at all for either of the three options, that data
+will be collected for all available volumes.
+
+=item B<IgnoreSelectedIO> B<true>|B<false>
+
+=item B<IgnoreSelectedOps> B<true>|B<false>
+
+=item B<IgnoreSelectedLatency> B<true>|B<false>
+
+When set to B<true>, the volumes selected for IO, operations or latency
+statistics collection will be ignored and the data will be collected for all
+other volumes.
+
+When set to B<false>, data will only be collected for the specified volumes and
+all other volumes will be ignored.
+
+If no volumes have been specified with the above B<Get*> options, all volumes
+will be collected regardless of the B<IgnoreSelected*> option.
+
+Defaults to B<false>
+
+=back
+
+=head3 The VolumeUsage block
+
+This will collect capacity data about the individual volumes.
+
+B<Note:> To get this data the collectd user needs the I<api-volume-list-info>
+capability.
+
+=over 4
+
+=item B<Interval> I<Seconds>
+
+Collect volume usage statistics every I<Seconds> seconds.
+
+=item B<GetCapacity> I<VolumeName>
+
+The current capacity of the volume will be collected. This will result in two
+to four value lists, depending on the configuration of the volume. All data
+sources are of type "df_complex" with the name of the volume as
+plugin_instance.
+
+There will be type_instances "used" and "free" for the number of used and
+available bytes on the volume.  If the volume has some space reserved for
+snapshots, a type_instance "snap_reserved" will be available.  If the volume
+has SIS enabled, a type_instance "sis_saved" will be available. This is the
+number of bytes saved by the SIS feature.
+
+B<Note:> The current NetApp API has a bug that results in this value being
+reported as a 32E<nbsp>bit number. This plugin tries to guess the correct
+number which works most of the time.  If you see strange values here, bug
+NetApp support to fix this.
+
+Repeat this option to specify multiple volumes.
+
+=item B<IgnoreSelectedCapacity> B<true>|B<false>
+
+Specify whether to collect only the volumes selected by the B<GetCapacity>
+option or to ignore those volumes. B<IgnoreSelectedCapacity> defaults to
+B<false>. However, if no B<GetCapacity> option is specified at all, all
+capacities will be selected anyway.
+
+=item B<GetSnapshot> I<VolumeName>
+
+Select volumes from which to collect snapshot information.
+
+Usually, the space used for snapshots is included in the space reported as
+"used". If snapshot information is collected as well, the space used for
+snapshots is subtracted from the used space.
+
+To make things even more interesting, it is possible to reserve space to be
+used for snapshots. If the space required for snapshots is less than that
+reserved space, there is "reserved free" and "reserved used" space in addition
+to "free" and "used". If the space required for snapshots exceeds the reserved
+space, that part allocated in the normal space is subtracted from the "used"
+space again.
+
+Repeat this option to specify multiple volumes.
+
+=item B<IgnoreSelectedSnapshot>
+
+Specify whether to collect only the volumes selected by the B<GetSnapshot>
+option or to ignore those volumes. B<IgnoreSelectedSnapshot> defaults to
+B<false>. However, if no B<GetSnapshot> option is specified at all, all
+capacities will be selected anyway.
+
+=back
+
 =head2 Plugin C<netlink>
 
 The C<netlink> plugin uses a netlink socket to query the Linux kernel about
@@ -1864,6 +2352,14 @@ 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
+statistics about itself. Collected data included the number of received and
+sent octets and packets, the length of the receive queue and the number of
+values handled. When set to B<true>, the I<Network plugin> will make these
+statistics available. Defaults to B<false>.
+
 =back
 
 =head2 Plugin C<nginx>
@@ -2165,6 +2661,12 @@ Available options:
 
 Specifies the location of the status file.
 
+=item B<Compression> B<true>|B<false>
+
+Sets whether or not statistics about the compression used by OpenVPN should be
+collected. This information is only available in I<single> mode. Enabled by
+default.
+
 =back
 
 =head2 Plugin C<oracle>
@@ -2274,6 +2776,24 @@ Default: B<0.9>
 
 Sets the Time-To-Live of generated ICMP packets.
 
+=item B<SourceAddress> I<host>
+
+Sets the source address to use. I<host> may either be a numerical network
+address or a network hostname.
+
+=item B<Device> I<name>
+
+Sets the outgoing network device to be used. I<name> has to specify an
+interface name (e.E<nbsp>g. C<eth0>). This might not be supported by all
+operating systems.
+
+=item B<MaxMissed> I<Packets>
+
+Trigger a DNS resolv after the host has not replied to I<Packets> packets. This
+enables the use of dynamic DNS services (like dyndns.org) with the ping plugin.
+
+Default: B<-1> (disabled)
+
 =back
 
 =head2 Plugin C<postgresql>
@@ -2750,7 +3270,7 @@ C<I<prefix>/var/run/collectd-powerdns>.
 Select more detailed statistics of processes matching this name. The statistics
 collected for these selected processes are size of the resident segment size
 (RSS), user- and system-time used, number of processes and number of threads,
-and minor and major pagefaults.
+io data (where available) and minor and major pagefaults.
 
 =item B<ProcessMatch> I<name> I<regex>
 
@@ -2796,6 +3316,71 @@ matching values will be ignored.
 
 =back
 
+=head2 Plugin C<python>
+
+This plugin embeds a Python-interpreter into collectd and provides an interface
+to collectd's plugin system. See L<collectd-python(5)> for its documentation.
+
+=head2 Plugin C<routeros>
+
+The C<routeros> plugin connects to a device running I<RouterOS>, the
+Linux-based operating system for routers by I<MikroTik>. The plugin uses
+I<librouteros> to connect and reads information about the interfaces and
+wireless connections of the device. The configuration supports querying
+multiple routers:
+
+  <Plugin "routeros">
+    <Router>
+      Host "router0.example.com"
+      User "collectd"
+      Password "secr3t"
+      CollectInterface true
+    </Router>
+    <Router>
+      Host "router1.example.com"
+      User "collectd"
+      Password "5ecret"
+      CollectInterface true
+      CollectRegistrationTable true
+    </Router>
+  </Plugin>
+
+As you can see above, the configuration of the I<routeros> plugin consists of
+one or more B<E<lt>RouterE<gt>> blocks. Within each block, the following
+options are understood:
+
+=over 4
+
+=item B<Host> I<Host>
+
+Hostname or IP-address of the router to connect to.
+
+=item B<Port> I<Port>
+
+Port name or port number used when connecting. If left unspecified, the default
+will be chosen by I<librouteros>, currently "8728". This option expects a
+string argument, even when a numeric port number is given.
+
+=item B<User> I<User>
+
+Use the user name I<User> to authenticate. Defaults to "admin".
+
+=item B<Password> I<Password>
+
+Set the password used to authenticate.
+
+=item B<CollectInterface> B<true>|B<false>
+
+When set to B<true>, interface statistics will be collected for all interfaces
+present on the device. Defaults to B<false>.
+
+=item B<CollectRegistrationTable> B<true>|B<false>
+
+When set to B<true>, information about wireless LAN connections will be
+collected. Defaults to B<false>.
+
+=back
+
 =head2 Plugin C<rrdcached>
 
 The C<rrdcached> plugin uses the RRDtool accelerator daemon, L<rrdcached(1)>,
@@ -4155,6 +4740,77 @@ time. If the counter is reset for some reason (machine or service restarted,
 usually), the graph will be empty (NAN) for a long time. People may not
 understand why.
 
+=item B<hashed>
+
+Calculates a hash value of the host name and matches values according to that
+hash value. This makes it possible to divide all hosts into groups and match
+only values that are in a specific group. The intended use is in load
+balancing, where you want to handle only part of all data and leave the rest
+for other servers.
+
+The hashing function used tries to distribute the hosts evenly. First, it
+calculates a 32E<nbsp>bit hash value using the characters of the hostname:
+
+  hash_value = 0;
+  for (i = 0; host[i] != 0; i++)
+    hash_value = (hash_value * 251) + host[i];
+
+The constant 251 is a prime number which is supposed to make this hash value
+more random. The code then checks the group for this host according to the
+I<Total> and I<Match> arguments:
+
+  if ((hash_value % Total) == Match)
+    matches;
+  else
+    does not match;
+
+Please note that when you set I<Total> to two (i.E<nbsp>e. you have only two
+groups), then the least significant bit of the hash value will be the XOR of
+all least significant bits in the host name. One consequence is that when you
+have two hosts, "server0.example.com" and "server1.example.com", where the host
+name differs in one digit only and the digits differ by one, those hosts will
+never end up in the same group.
+
+Available options:
+
+=over 4
+
+=item B<Match> I<Match> I<Total>
+
+Divide the data into I<Total> groups and match all hosts in group I<Match> as
+described above. The groups are numbered from zero, i.E<nbsp>e. I<Match> must
+be smaller than I<Total>. I<Total> must be at least one, although only values
+greater than one really do make any sense.
+
+You can repeat this option to match multiple groups, for example:
+
+  Match 3 7
+  Match 5 7
+
+The above config will divide the data into seven groups and match groups three
+and five. One use would be to keep every value on two hosts so that if one
+fails the missing data can later be reconstructed from the second host.
+
+=back
+
+Example:
+
+ # Operate on the pre-cache chain, so that ignored values are not even in the
+ # global cache.
+ <Chain "PreCache">
+   <Rule>
+     <Match "hashed">
+       # Divide all received hosts in seven groups and accept all hosts in
+       # group three.
+       Match 3 7
+     </Match>
+     # If matched: Return and continue.
+     Target "return"
+   </Rule>
+   # If not matched: Return and stop.
+   Target "stop"
+ </Chain>
+
 =back
 
 =head2 Available targets
index b9e41b1..ae57c43 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/common.c
- * Copyright (C) 2005-2008  Florian octo Forster
+ * Copyright (C) 2005-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
 # include <arpa/inet.h>
 #endif
 
+/* for getaddrinfo */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
 #ifdef HAVE_LIBKSTAT
 extern kstat_ctl_t *kc;
 #endif
@@ -1073,3 +1082,56 @@ counter_t counter_diff (counter_t old_value, counter_t new_value)
 
        return (diff);
 } /* counter_t counter_to_gauge */
+
+int service_name_to_port_number (const char *service_name)
+{
+       struct addrinfo *ai_list;
+       struct addrinfo *ai_ptr;
+       struct addrinfo ai_hints;
+       int status;
+       int service_number;
+
+       if (service_name == NULL)
+               return (-1);
+
+       ai_list = NULL;
+       memset (&ai_hints, 0, sizeof (ai_hints));
+       ai_hints.ai_family = AF_UNSPEC;
+
+       status = getaddrinfo (/* node = */ NULL, service_name,
+                       &ai_hints, &ai_list);
+       if (status != 0)
+       {
+               ERROR ("service_name_to_port_number: getaddrinfo failed: %s",
+                               gai_strerror (status));
+               return (-1);
+       }
+
+       service_number = -1;
+       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+       {
+               if (ai_ptr->ai_family == AF_INET)
+               {
+                       struct sockaddr_in *sa;
+
+                       sa = (void *) ai_ptr->ai_addr;
+                       service_number = (int) ntohs (sa->sin_port);
+               }
+               else if (ai_ptr->ai_family == AF_INET6)
+               {
+                       struct sockaddr_in6 *sa;
+
+                       sa = (void *) ai_ptr->ai_addr;
+                       service_number = (int) ntohs (sa->sin6_port);
+               }
+
+               if ((service_number > 0) && (service_number <= 65535))
+                       break;
+       }
+
+       freeaddrinfo (ai_list);
+
+       if ((service_number > 0) && (service_number <= 65535))
+               return (service_number);
+       return (-1);
+} /* int service_name_to_port_number */
index 8a9f335..7b9fa0a 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/common.h
- * Copyright (C) 2005-2008  Florian octo Forster
+ * Copyright (C) 2005-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
@@ -287,4 +287,8 @@ int read_file_contents (const char *filename, char *buf, int bufsize);
 
 counter_t counter_diff (counter_t old_value, counter_t new_value);
 
+/* 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);
+
 #endif /* COMMON_H */
index 34049f2..2eea236 100644 (file)
@@ -75,7 +75,7 @@ typedef struct cf_global_option_s
  */
 static int dispatch_value_typesdb (const oconfig_item_t *ci);
 static int dispatch_value_plugindir (const oconfig_item_t *ci);
-static int dispatch_value_loadplugin (const oconfig_item_t *ci);
+static int dispatch_loadplugin (const oconfig_item_t *ci);
 
 /*
  * Private variables
@@ -87,7 +87,7 @@ static cf_value_map_t cf_value_map[] =
 {
        {"TypesDB",    dispatch_value_typesdb},
        {"PluginDir",  dispatch_value_plugindir},
-       {"LoadPlugin", dispatch_value_loadplugin}
+       {"LoadPlugin", dispatch_loadplugin}
 };
 static int cf_value_map_num = STATIC_ARRAY_LEN (cf_value_map);
 
@@ -239,8 +239,10 @@ static int dispatch_value_plugindir (const oconfig_item_t *ci)
        return (0);
 }
 
-static int dispatch_value_loadplugin (const oconfig_item_t *ci)
+static int dispatch_loadplugin (const oconfig_item_t *ci)
 {
+       int i;
+       uint32_t flags = 0;
        assert (strcasecmp (ci->key, "LoadPlugin") == 0);
 
        if (ci->values_num != 1)
@@ -248,7 +250,19 @@ static int dispatch_value_loadplugin (const oconfig_item_t *ci)
        if (ci->values[0].type != OCONFIG_TYPE_STRING)
                return (-1);
 
-       return (plugin_load (ci->values[0].value.string));
+       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(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);
+               }
+       }
+       return (plugin_load (ci->values[0].value.string, flags));
 } /* int dispatch_value_loadplugin */
 
 static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci)
@@ -353,7 +367,9 @@ static int dispatch_block_plugin (oconfig_item_t *ci)
 
 static int dispatch_block (oconfig_item_t *ci)
 {
-       if (strcasecmp (ci->key, "Plugin") == 0)
+       if (strcasecmp (ci->key, "LoadPlugin") == 0)
+               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));
@@ -915,3 +931,60 @@ int cf_read (char *filename)
 
        return (0);
 } /* int cf_read */
+
+/* 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. */
+int cf_util_get_string (const oconfig_item_t *ci, char **ret_string) /* {{{ */
+{
+       char *string;
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               ERROR ("cf_util_get_string: The %s option requires "
+                               "exactly one string argument.", ci->key);
+               return (-1);
+       }
+
+       string = strdup (ci->values[0].value.string);
+       if (string == NULL)
+               return (-1);
+
+       if (*ret_string != NULL)
+               sfree (*ret_string);
+       *ret_string = string;
+
+       return (0);
+} /* }}} int cf_util_get_string */
+
+int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
+{
+       if ((ci == NULL) || (ret_bool == NULL))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+       {
+               ERROR ("cf_util_get_boolean: The %s option requires "
+                               "exactly one string argument.", ci->key);
+               return (-1);
+       }
+
+       *ret_bool = ci->values[0].value.boolean ? true : false;
+
+       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_port_number (const oconfig_item_t *ci) /* {{{ */
+{
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               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));
+} /* }}} int cf_util_get_port_number */
index 3952c18..a73def2 100644 (file)
@@ -86,4 +86,18 @@ int cf_read (char *filename);
 int global_option_set (const char *option, const char *value);
 const char *global_option_get (const char *option);
 
+/* 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. */
+int cf_util_get_string (const oconfig_item_t *ci, char **ret_string);
+
+/* 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. */
+int cf_util_get_port_number (const oconfig_item_t *ci);
+
 #endif /* defined(CONFIGFILE_H) */
diff --git a/src/contextswitch.c b/src/contextswitch.c
new file mode 100644 (file)
index 0000000..06055ca
--- /dev/null
@@ -0,0 +1,98 @@
+/**
+ * collectd - src/contextswitch.c
+ * Copyright (C) 2009  Patrik Weiskircher
+ *
+ * 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:
+ *   Patrik Weiskircher <weiskircher at inqnet.at>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if !KERNEL_LINUX
+# error "No applicable input method."
+#endif
+
+static void cs_submit (derive_t context_switches)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].derive = (derive_t) context_switches;
+
+       vl.values = values;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "contextswitch", sizeof (vl.plugin));
+       sstrncpy (vl.type, "contextswitch", sizeof (vl.type));
+
+       plugin_dispatch_values (&vl);
+}
+
+static int cs_read (void)
+{
+       FILE *fh;
+       char buffer[64];
+       int numfields;
+       char *fields[3];
+       derive_t result = 0;
+       int status = -2;
+
+       fh = fopen ("/proc/stat", "r");
+       if (fh == NULL) {
+               ERROR ("contextswitch plugin: unable to open /proc/stat: %s",
+                               sstrerror (errno, buffer, sizeof (buffer)));
+               return (-1);
+       }
+
+       while (fgets(buffer, sizeof(buffer), fh) != NULL)
+       {
+               char *endptr;
+
+               numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE (fields));
+               if (numfields != 2)
+                       continue;
+
+               if (strcmp("ctxt", fields[0]) != 0)
+                       continue;
+
+               errno = 0;
+               endptr = NULL;
+               result = (derive_t) strtoll (fields[1], &endptr, /* base = */ 10);
+               if ((endptr == fields[1]) || (errno != 0)) {
+                       ERROR ("contextswitch plugin: Cannot parse ctxt value: %s",
+                                       fields[1]);
+                       status = -1;
+                       break;
+               }
+
+               cs_submit(result);
+               status = 0;
+               break;
+       }
+       fclose(fh);
+
+       if (status == -2)
+               ERROR ("contextswitch plugin: Unable to find context switch value.");
+
+       return status;
+}
+
+void module_register (void)
+{
+       plugin_register_read ("contextswitch", cs_read);
+} /* void module_register */
index dc44a50..7aa6361 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -3,6 +3,7 @@
  * Copyright (C) 2005-2009  Florian octo Forster
  * Copyright (C) 2008       Oleg King
  * Copyright (C) 2009       Simon Kuhnle
+ * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -21,6 +22,7 @@
  *   Florian octo Forster <octo at verplant.org>
  *   Oleg King <king2 at kaluga.ru>
  *   Simon Kuhnle <simon at blarzwurst.de>
+ *   Manuel Sanmartin
  **/
 
 #include "collectd.h"
 # include <statgrab.h>
 #endif
 
+# ifdef HAVE_PERFSTAT
+#  include <sys/protosw.h>
+#  include <libperfstat.h>
+# endif /* HAVE_PERFSTAT */
+
 #if !PROCESSOR_CPU_LOAD_INFO && !KERNEL_LINUX && !HAVE_LIBKSTAT \
-       && !CAN_USE_SYSCTL && !HAVE_SYSCTLBYNAME && !HAVE_LIBSTATGRAB
+       && !CAN_USE_SYSCTL && !HAVE_SYSCTLBYNAME && !HAVE_LIBSTATGRAB && !HAVE_PERFSTAT
 # error "No applicable input method."
 #endif
 
@@ -123,11 +130,20 @@ static int numcpu;
 
 #elif defined(HAVE_SYSCTLBYNAME)
 static int numcpu;
+#  ifdef HAVE_SYSCTL_KERN_CP_TIMES
+static int maxcpu;
+#  endif /* HAVE_SYSCTL_KERN_CP_TIMES */
 /* #endif HAVE_SYSCTLBYNAME */
 
 #elif defined(HAVE_LIBSTATGRAB)
 /* no variables needed */
-#endif /* HAVE_LIBSTATGRAB */
+/* #endif  HAVE_LIBSTATGRAB */
+
+#elif defined(HAVE_PERFSTAT)
+static perfstat_cpu_t *perfcpu;
+static int numcpu;
+static int pnumcpu;
+#endif /* HAVE_PERFSTAT */
 
 static int init (void)
 {
@@ -193,18 +209,34 @@ static int init (void)
        if (sysctlbyname ("hw.ncpu", &numcpu, &numcpu_size, NULL, 0) < 0)
        {
                char errbuf[1024];
-               WARNING ("cpu plugin: sysctlbyname: %s",
+               WARNING ("cpu plugin: sysctlbyname(hw.ncpu): %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
 
+#ifdef HAVE_SYSCTL_KERN_CP_TIMES
+       numcpu_size = sizeof (maxcpu);
+
+       if (sysctlbyname("kern.smp.maxcpus", &maxcpu, &numcpu_size, NULL, 0) < 0)
+       {
+               char errbuf[1024];
+               WARNING ("cpu plugin: sysctlbyname(kern.smp.maxcpus): %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+#else
        if (numcpu != 1)
                NOTICE ("cpu: Only one processor supported when using `sysctlbyname' (found %i)", numcpu);
+#endif
 /* #endif HAVE_SYSCTLBYNAME */
 
 #elif defined(HAVE_LIBSTATGRAB)
        /* nothing to initialize */
-#endif /* HAVE_LIBSTATGRAB */
+/* #endif HAVE_LIBSTATGRAB */
+
+#elif defined(HAVE_PERFSTAT)
+       /* nothing to initialize */
+#endif /* HAVE_PERFSTAT */
 
        return (0);
 } /* int init */
@@ -464,7 +496,30 @@ static int cpu_read (void)
                submit (i, "interrupt", cpuinfo[i][CP_INTR]);
        }
 /* #endif CAN_USE_SYSCTL */
+#elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES)
+       long cpuinfo[maxcpu][CPUSTATES];
+       size_t cpuinfo_size;
+       int i;
 
+       memset (cpuinfo, 0, sizeof (cpuinfo));
+
+       cpuinfo_size = sizeof (cpuinfo);
+       if (sysctlbyname("kern.cp_times", &cpuinfo, &cpuinfo_size, NULL, 0) < 0)
+       {
+               char errbuf[1024];
+               ERROR ("cpu plugin: sysctlbyname failed: %s.",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       for (i = 0; i < numcpu; i++) {
+               submit (i, "user", cpuinfo[i][CP_USER]);
+               submit (i, "nice", cpuinfo[i][CP_NICE]);
+               submit (i, "system", cpuinfo[i][CP_SYS]);
+               submit (i, "idle", cpuinfo[i][CP_IDLE]);
+               submit (i, "interrupt", cpuinfo[i][CP_INTR]);
+       }
+/* #endif HAVE_SYSCTL_KERN_CP_TIMES */
 #elif defined(HAVE_SYSCTLBYNAME)
        long cpuinfo[CPUSTATES];
        size_t cpuinfo_size;
@@ -502,7 +557,46 @@ static int cpu_read (void)
        submit (0, "system", (counter_t) cs->kernel);
        submit (0, "user",   (counter_t) cs->user);
        submit (0, "wait",   (counter_t) cs->iowait);
-#endif /* HAVE_LIBSTATGRAB */
+/* #endif HAVE_LIBSTATGRAB */
+
+#elif defined(HAVE_PERFSTAT)
+       perfstat_id_t id;
+       int i, cpus;
+
+       numcpu =  perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
+       if(numcpu == -1)
+       {
+               char errbuf[1024];
+               WARNING ("cpu plugin: perfstat_cpu: %s",
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+       
+       if (pnumcpu != numcpu || perfcpu == NULL) 
+       {
+               if (perfcpu != NULL) 
+                       free(perfcpu);
+               perfcpu = malloc(numcpu * sizeof(perfstat_cpu_t));
+       }
+       pnumcpu = numcpu;
+
+       id.name[0] = '\0';
+       if ((cpus = perfstat_cpu(&id, perfcpu, sizeof(perfstat_cpu_t), numcpu)) < 0)
+       {
+               char errbuf[1024];
+               WARNING ("cpu plugin: perfstat_cpu: %s",
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       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);
+       }
+#endif /* HAVE_PERFSTAT */
 
        return (0);
 }
diff --git a/src/cpython.h b/src/cpython.h
new file mode 100644 (file)
index 0000000..661bf6a
--- /dev/null
@@ -0,0 +1,113 @@
+/**
+ * collectd - src/cpython.h
+ * Copyright (C) 2009  Sven Trenkel
+ *
+ * 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:
+ *   Sven Trenkel <collectd at semidefinite.de>  
+ **/
+
+/* These two macros are basicly Py_BEGIN_ALLOW_THREADS and Py_BEGIN_ALLOW_THREADS
+ * from the other direction. If a Python thread calls a C function
+ * Py_BEGIN_ALLOW_THREADS is used to allow other python threads to run because
+ * we don't intend to call any Python functions.
+ *
+ * These two macros are used whenever a C thread intends to call some Python
+ * function, usually because some registered callback was triggered.
+ * Just like Py_BEGIN_ALLOW_THREADS it opens a block so these macros have to be
+ * used in pairs. They aquire the GIL, create a new Python thread state and swap
+ * the current thread state with the new one. This means this thread is now allowed
+ * to execute Python code. */
+
+#define CPY_LOCK_THREADS {\
+       PyGILState_STATE gil_state;\
+       gil_state = PyGILState_Ensure();
+
+#define CPY_RETURN_FROM_THREADS \
+       PyGILState_Release(gil_state);\
+       return
+
+#define CPY_RELEASE_THREADS \
+       PyGILState_Release(gil_state);\
+}
+
+/* Python 2.4 has this macro, older versions do not. */
+#ifndef Py_VISIT
+#define Py_VISIT(o) do {\
+       int _vret;\
+       if ((o) != NULL) {\
+               _vret = visit((o), arg);\
+               if (_vret != 0)\
+               return _vret;\
+       }\
+} while (0)
+#endif
+
+/* Python 2.4 has this macro, older versions do not. */
+#ifndef Py_CLEAR
+#define Py_CLEAR(o) do {\
+       PyObject *tmp = o;\
+       (o) = NULL;\
+       Py_XDECREF(tmp);\
+} while (0)
+#endif
+
+/* Python 2.4 has this macro, older versions do not. */
+#ifndef Py_RETURN_NONE
+# define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+typedef struct {
+       PyObject_HEAD        /* No semicolon! */
+       PyObject *parent;    /* Config */
+       PyObject *key;       /* String */
+       PyObject *values;    /* Sequence */
+       PyObject *children;  /* Sequence */
+} Config;
+
+PyTypeObject ConfigType;
+
+typedef struct {
+       PyObject_HEAD        /* No semicolon! */
+       double time;
+       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];
+} PluginData;
+
+PyTypeObject PluginDataType;
+
+typedef struct {
+       PluginData data;
+       PyObject *values;    /* Sequence */
+       int interval;
+} Values;
+
+PyTypeObject ValuesType;
+
+typedef struct {
+       PluginData data;
+       int severity;
+       char message[NOTIF_MAX_MSG_LEN];
+} Notification;
+
+PyTypeObject NotificationType;
index 78037a9..0b34687 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -246,16 +246,10 @@ static int csv_config (const char *key, const char *value)
        }
        else if (strcasecmp ("StoreRates", key) == 0)
        {
-               if ((strcasecmp ("True", value) == 0)
-                               || (strcasecmp ("Yes", value) == 0)
-                               || (strcasecmp ("On", value) == 0))
-               {
+               if (IS_TRUE (value))
                        store_rates = 1;
-               }
                else
-               {
                        store_rates = 0;
-               }
        }
        else
        {
index a43e7ed..bc11c28 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/curl.c
  * Copyright (C) 2006-2009  Florian octo Forster
+ * Copyright (C) 2009       Aman Gupta
  *
  * 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:
  *   Florian octo Forster <octo at verplant.org>
+ *   Aman Gupta <aman at tmm1.net>
  **/
 
 #include "collectd.h"
@@ -57,6 +59,7 @@ struct web_page_s /* {{{ */
   int   verify_peer;
   int   verify_host;
   char *cacert;
+  int   response_time;
 
   CURL *curl;
   char curl_errbuf[CURL_ERROR_SIZE];
@@ -424,6 +427,7 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
   page->pass = NULL;
   page->verify_peer = 1;
   page->verify_host = 1;
+  page->response_time = 0;
 
   page->instance = strdup (ci->values[0].value.string);
   if (page->instance == NULL)
@@ -449,6 +453,8 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
       status = cc_config_set_boolean ("VerifyPeer", &page->verify_peer, child);
     else if (strcasecmp ("VerifyHost", child->key) == 0)
       status = cc_config_set_boolean ("VerifyHost", &page->verify_host, child);
+    else if (strcasecmp ("MeasureResponseTime", child->key) == 0)
+      status = cc_config_set_boolean (child->key, &page->response_time, child);
     else if (strcasecmp ("CACert", child->key) == 0)
       status = cc_config_add_string ("CACert", &page->cacert, child);
     else if (strcasecmp ("Match", child->key) == 0)
@@ -473,11 +479,11 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
       status = -1;
     }
 
-    if (page->matches == NULL)
+    if (page->matches == NULL && !page->response_time)
     {
       assert (page->instance != NULL);
       WARNING ("curl plugin: No (valid) `Match' block "
-          "within `Page' block `%s'.", page->instance);
+          "or MeasureResponseTime within `Page' block `%s'.", page->instance);
       status = -1;
     }
 
@@ -577,10 +583,32 @@ static void cc_submit (const web_page_t *wp, const web_match_t *wm, /* {{{ */
   plugin_dispatch_values (&vl);
 } /* }}} void cc_submit */
 
+static void cc_submit_response_time (const web_page_t *wp, double seconds) /* {{{ */
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = 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));
+  sstrncpy (vl.type, "response_time", sizeof (vl.type));
+
+  plugin_dispatch_values (&vl);
+} /* }}} void cc_submit_response_time */
+
 static int cc_read_page (web_page_t *wp) /* {{{ */
 {
   web_match_t *wm;
   int status;
+  struct timeval start, end;
+
+  if (wp->response_time)
+    gettimeofday (&start, NULL);
 
   wp->buffer_fill = 0;
   status = curl_easy_perform (wp->curl);
@@ -591,6 +619,15 @@ static int cc_read_page (web_page_t *wp) /* {{{ */
     return (-1);
   }
 
+  if (wp->response_time)
+  {
+    double secs = 0;
+    gettimeofday (&end, NULL);
+    secs += end.tv_sec - start.tv_sec;
+    secs += (end.tv_usec - start.tv_usec) / 1000000.0;
+    cc_submit_response_time (wp, secs);
+  }
+
   for (wm = wp->matches; wm != NULL; wm = wm->next)
   {
     cu_match_value_t *mv;
index 462e1c6..9185ba4 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -1,6 +1,6 @@
 /**
  * collectd - src/df.c
- * Copyright (C) 2005-2007  Florian octo Forster
+ * Copyright (C) 2005-2009  Florian octo Forster
  * Copyright (C) 2009       Paul Sadauskas
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -50,7 +50,9 @@ static const char *config_keys[] =
        "MountPoint",
        "FSType",
        "IgnoreSelected",
-        "ReportByDevice"
+       "ReportByDevice",
+       "ReportReserved",
+       "ReportInodes"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -59,6 +61,8 @@ 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 int df_init (void)
 {
@@ -117,11 +121,31 @@ static int df_config (const char *key, const char *value)
 
                return (0);
        }
+       else if (strcasecmp (key, "ReportReserved") == 0)
+       {
+               if (IS_TRUE (value))
+                       report_reserved = true;
+               else
+                       report_reserved = false;
+
+               return (0);
+       }
+       else if (strcasecmp (key, "ReportInodes") == 0)
+       {
+               if (IS_TRUE (value))
+                       report_inodes = true;
+               else
+                       report_inodes = false;
+
+               return (0);
+       }
+
 
        return (-1);
 }
 
-static void df_submit (char *df_name,
+static void df_submit_two (char *df_name,
+               const char *type,
                gauge_t df_used,
                gauge_t df_free)
 {
@@ -136,11 +160,36 @@ static void df_submit (char *df_name,
        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, "df", sizeof (vl.type));
+       sstrncpy (vl.type, type, sizeof (vl.type));
        sstrncpy (vl.type_instance, df_name, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
-} /* void df_submit */
+} /* void df_submit_two */
+
+__attribute__ ((nonnull(2)))
+static void df_submit_one (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, "df", sizeof (vl.plugin));
+       if (plugin_instance != NULL)
+               sstrncpy (vl.plugin_instance, plugin_instance,
+                               sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       if (type_instance != NULL)
+               sstrncpy (vl.type_instance, type_instance,
+                               sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* void df_submit_one */
 
 static int df_read (void)
 {
@@ -153,17 +202,15 @@ static int df_read (void)
        cu_mount_t *mnt_list;
        cu_mount_t *mnt_ptr;
 
-       unsigned long long blocksize;
-       gauge_t df_free;
-       gauge_t df_used;
-       char disk_name[256];
-
        mnt_list = NULL;
        if (cu_mount_getlist (&mnt_list) == NULL)
                return (-1);
 
        for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next)
        {
+               unsigned long long blocksize;
+               char disk_name[256];
+
                if (ignorelist_match (il_device,
                                        (mnt_ptr->spec_device != NULL)
                                        ? mnt_ptr->spec_device
@@ -186,10 +233,6 @@ static int df_read (void)
                if (!statbuf.f_blocks)
                        continue;
 
-               blocksize = BLOCKSIZE(statbuf);
-               df_free = statbuf.f_bfree * blocksize;
-               df_used = (statbuf.f_blocks - statbuf.f_bfree) * blocksize;
-
                if (by_device) 
                {
                        /* eg, /dev/hda1  -- strip off the "/dev/" */
@@ -223,7 +266,66 @@ static int df_read (void)
                        }
                }
 
-               df_submit (disk_name, df_used, df_free);
+               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 */
+                       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);
+               }
+
+               /* inode handling */
+               if (report_inodes)
+               {
+                       uint64_t inode_free;
+                       uint64_t inode_reserved;
+                       uint64_t inode_used;
+
+                       /* Sanity-check for the values in the struct */
+                       if (statbuf.f_ffree < statbuf.f_favail)
+                               statbuf.f_ffree = statbuf.f_favail;
+                       if (statbuf.f_files < statbuf.f_ffree)
+                               statbuf.f_files = statbuf.f_ffree;
+
+                       inode_free = (uint64_t) statbuf.f_favail;
+                       inode_reserved = (uint64_t) (statbuf.f_ffree - statbuf.f_favail);
+                       inode_used = (uint64_t) (statbuf.f_files - statbuf.f_ffree);
+                       
+                       df_submit_one (disk_name, "df_inodes", "free",
+                                       (gauge_t) inode_free);
+                       df_submit_one (disk_name, "df_inodes", "reserved",
+                                       (gauge_t) inode_reserved);
+                       df_submit_one (disk_name, "df_inodes", "used",
+                                       (gauge_t) inode_used);
+               }
        }
 
        cu_mount_freelist (mnt_list);
index 489770c..0a90899 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/disk.c
  * Copyright (C) 2005-2008  Florian octo Forster
+ * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -17,6 +18,7 @@
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   Manuel Sanmartin
  **/
 
 #include "collectd.h"
 # include <statgrab.h>
 #endif
 
+#if HAVE_PERFSTAT
+# ifndef _AIXVERSION_610
+# include <sys/systemcfg.h>
+# endif
+# include <sys/protosw.h>
+# include <libperfstat.h>
+#endif
+
 #if HAVE_IOKIT_IOKITLIB_H
 static mach_port_t io_master_port = MACH_PORT_NULL;
 /* #endif HAVE_IOKIT_IOKITLIB_H */
@@ -105,6 +115,12 @@ static int numdisk = 0;
 #elif defined(HAVE_LIBSTATGRAB)
 /* #endif HAVE_LIBKSTATGRAB */
 
+#elif HAVE_PERFSTAT
+static perfstat_disk_t * stat_disk;
+static int numdisk;
+static int pnumdisk;
+/* #endif HAVE_PERFSTAT */
+
 #else
 # error "No applicable input method."
 #endif
@@ -132,9 +148,7 @@ static int disk_config (const char *key, const char *value)
   else if (strcasecmp ("IgnoreSelected", key) == 0)
   {
     int invert = 1;
-    if ((strcasecmp ("True", value) == 0)
-       || (strcasecmp ("Yes", value) == 0)
-       || (strcasecmp ("On", value) == 0))
+    if (IS_TRUE (value))
       invert = 0;
     ignorelist_set_invert (ignorelist, invert);
   }
@@ -150,7 +164,7 @@ static int disk_init (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H
        kern_return_t status;
-       
+
        if (io_master_port != MACH_PORT_NULL)
        {
                mach_port_deallocate (mach_task_self (),
@@ -683,7 +697,60 @@ static int disk_read (void)
                disk_submit (name, "disk_octets", ds->read_bytes, ds->write_bytes);
                ds++;
        }
-#endif /* defined(HAVE_LIBSTATGRAB) */
+/* #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;
+       perfstat_id_t firstpath;
+       int rnumdisk;
+       int i;
+
+       if ((numdisk = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0)) < 0) 
+       {
+               char errbuf[1024];
+               WARNING ("disk plugin: perfstat_disk: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       if (numdisk != pnumdisk || stat_disk==NULL) {
+               if (stat_disk!=NULL) 
+                       free(stat_disk);
+               stat_disk = (perfstat_disk_t *)calloc(numdisk, sizeof(perfstat_disk_t));
+       } 
+       pnumdisk = numdisk;
+
+       firstpath.name[0]='\0';
+       if ((rnumdisk = perfstat_disk(&firstpath, stat_disk, sizeof(perfstat_disk_t), numdisk)) < 0) 
+       {
+               char errbuf[1024];
+               WARNING ("disk plugin: perfstat_disk : %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       for (i = 0; i < rnumdisk; i++) 
+       {
+               read_sectors = stat_disk[i].rblks*stat_disk[i].bsize;
+               write_sectors = stat_disk[i].wblks*stat_disk[i].bsize;
+               disk_submit (stat_disk[i].name, "disk_octets", read_sectors, write_sectors);
+
+               read_ops = stat_disk[i].xrate;
+               write_ops = stat_disk[i].xfers - stat_disk[i].xrate;
+               disk_submit (stat_disk[i].name, "disk_ops", read_ops, write_ops);
+
+               read_time = stat_disk[i].rserv;
+               read_time *= ((double)(_system_configuration.Xint)/(double)(_system_configuration.Xfrac)) / 1000000.0;
+               write_time = stat_disk[i].wserv;
+               write_time *= ((double)(_system_configuration.Xint)/(double)(_system_configuration.Xfrac)) / 1000000.0;
+               disk_submit (stat_disk[i].name, "disk_time", read_time, write_time);
+       }
+#endif /* defined(HAVE_PERFSTAT) */
 
        return (0);
 } /* int disk_read */
index e600658..681b94d 100644 (file)
@@ -265,6 +265,17 @@ static int exec_config (oconfig_item_t *ci) /* {{{ */
   return (0);
 } /* int exec_config }}} */
 
+static void set_environment (void) /* {{{ */
+{
+  char buffer[1024];
+
+  ssnprintf (buffer, sizeof (buffer), "%i", interval_g);
+  setenv ("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
+
+  ssnprintf (buffer, sizeof (buffer), "%s", hostname_g);
+  setenv ("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
+} /* }}} void set_environment */
+
 __attribute__((noreturn))
 static void exec_child (program_list_t *pl) /* {{{ */
 {
@@ -479,6 +490,8 @@ static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err)
       close (fd_pipe_err[1]);
     }
 
+    set_environment ();
+
     /* Unblock all signals */
     reset_signal_mask ();
 
index 2405d8c..4e08375 100644 (file)
@@ -233,9 +233,7 @@ static int hddtemp_config (const char *key, const char *value)
        }
        else if (strcasecmp (key, "TranslateDevicename") == 0)
        {
-               if ((strcasecmp ("true", value) == 0)
-                               || (strcasecmp ("yes", value) == 0)
-                               || (strcasecmp ("on", value) == 0))
+               if (IS_TRUE (value))
                        translate_devicename = 1;
                else
                        translate_devicename = 0;
index fad37db..1ba6c8c 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/interface.c
  * Copyright (C) 2005-2008  Florian octo Forster
+ * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -18,6 +19,7 @@
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
  *   Sune Marcher <sm at flork.dk>
+ *   Manuel Sanmartin
  **/
 
 #include "collectd.h"
 # include <statgrab.h>
 #endif
 
+#if HAVE_PERFSTAT
+# include <sys/protosw.h>
+# include <libperfstat.h>
+#endif
+
 /*
  * Various people have reported problems with `getifaddrs' and varying versions
  * of `glibc'. That's why it's disabled by default. Since more statistics are
 # endif /* !COLLECT_GETIFADDRS */
 #endif /* KERNEL_LINUX */
 
-#if !HAVE_GETIFADDRS && !KERNEL_LINUX && !HAVE_LIBKSTAT && !HAVE_LIBSTATGRAB
+#if HAVE_PERFSTAT
+static perfstat_netinterface_t *ifstat;
+static int nif;
+static int pnif;
+#endif /* HAVE_PERFSTAT */
+
+#if !HAVE_GETIFADDRS && !KERNEL_LINUX && !HAVE_LIBKSTAT && !HAVE_LIBSTATGRAB && !HAVE_PERFSTAT
 # error "No applicable input method."
 #endif
 
@@ -99,9 +112,7 @@ static int interface_config (const char *key, const char *value)
        else if (strcasecmp (key, "IgnoreSelected") == 0)
        {
                int invert = 1;
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        invert = 0;
                ignorelist_set_invert (ignorelist, invert);
        }
@@ -224,7 +235,7 @@ static int interface_read (void)
        char buffer[1024];
        unsigned long long incoming, outgoing;
        char *device;
-       
+
        char *dummy;
        char *fields[16];
        int numfields;
@@ -250,7 +261,7 @@ static int interface_read (void)
 
                if (device[0] == '\0')
                        continue;
-               
+
                numfields = strsplit (dummy, fields, 16);
 
                if (numfields < 11)
@@ -310,7 +321,44 @@ static int interface_read (void)
 
        for (i = 0; i < num; i++)
                if_submit (ios[i].interface_name, "if_octets", ios[i].rx, ios[i].tx);
-#endif /* HAVE_LIBSTATGRAB */
+/* #endif HAVE_LIBSTATGRAB */
+
+#elif defined(HAVE_PERFSTAT)
+       perfstat_id_t id;
+       int i, ifs;
+
+       if ((nif =  perfstat_netinterface(NULL, NULL, sizeof(perfstat_netinterface_t), 0)) < 0)
+       {
+               char errbuf[1024];
+               WARNING ("interface plugin: perfstat_netinterface: %s",
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       if (pnif != nif || ifstat == NULL)
+       {
+               if (ifstat != NULL)
+                       free(ifstat);
+               ifstat = malloc(nif * sizeof(perfstat_netinterface_t));
+       }
+       pnif = nif;
+
+       id.name[0]='\0';
+       if ((ifs = perfstat_netinterface(&id, ifstat, sizeof(perfstat_netinterface_t), nif)) < 0)
+       {
+               char errbuf[1024];
+               WARNING ("interface plugin: perfstat_netinterface (interfaces=%d): %s",
+                       nif, sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       for (i = 0; i < ifs; i++)
+       {
+               if_submit (ifstat[i].name, "if_octets", ifstat[i].ibytes, ifstat[i].obytes);
+               if_submit (ifstat[i].name, "if_packets", ifstat[i].ipackets ,ifstat[i].opackets);
+               if_submit (ifstat[i].name, "if_errors", ifstat[i].ierrors, ifstat[i].oerrors );
+       }
+#endif /* HAVE_PERFSTAT */
 
        return (0);
 } /* int interface_read */
index 441ad8f..95b3dbf 100644 (file)
@@ -632,31 +632,23 @@ static int c_ipmi_config (const char *key, const char *value)
   else if (strcasecmp ("IgnoreSelected", key) == 0)
   {
     int invert = 1;
-    if ((strcasecmp ("True", value) == 0)
-        || (strcasecmp ("Yes", value) == 0)
-        || (strcasecmp ("On", value) == 0))
+    if (IS_TRUE (value))
       invert = 0;
     ignorelist_set_invert (ignorelist, invert);
   }
   else if (strcasecmp ("NotifySensorAdd", key) == 0)
   {
-    if ((strcasecmp ("True", value) == 0)
-        || (strcasecmp ("Yes", value) == 0)
-        || (strcasecmp ("On", value) == 0))
+    if (IS_TRUE (value))
       c_ipmi_nofiy_add = 1;
   }
   else if (strcasecmp ("NotifySensorRemove", key) == 0)
   {
-    if ((strcasecmp ("True", value) == 0)
-        || (strcasecmp ("Yes", value) == 0)
-        || (strcasecmp ("On", value) == 0))
+    if (IS_TRUE (value))
       c_ipmi_nofiy_remove = 1;
   }
   else if (strcasecmp ("NotifySensorNotPresent", key) == 0)
   {
-    if ((strcasecmp ("True", value) == 0)
-        || (strcasecmp ("Yes", value) == 0)
-        || (strcasecmp ("On", value) == 0))
+    if (IS_TRUE (value))
       c_ipmi_nofiy_notpresent = 1;
   }
   else
index 1aef344..401cc6f 100644 (file)
--- a/src/irq.c
+++ b/src/irq.c
@@ -85,9 +85,7 @@ static int irq_config (const char *key, const char *value)
        }
        else if (strcasecmp (key, "IgnoreSelected") == 0)
        {
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        irq_list_action = 1;
                else
                        irq_list_action = 0;
index 49cd139..5b7aa94 100644 (file)
@@ -46,7 +46,7 @@ extern char           *c_file;
 }
 
 %token <number> NUMBER
-%token <boolean> TRUE FALSE
+%token <boolean> BTRUE BFALSE
 %token <string> QUOTED_STRING UNQUOTED_STRING
 %token SLASH OPENBRAC CLOSEBRAC EOL
 
@@ -76,8 +76,8 @@ string:
 
 argument:
        NUMBER                  {$$.value.number = $1; $$.type = OCONFIG_TYPE_NUMBER;}
-       | TRUE                  {$$.value.boolean = 1; $$.type = OCONFIG_TYPE_BOOLEAN;}
-       | FALSE                 {$$.value.boolean = 0; $$.type = OCONFIG_TYPE_BOOLEAN;}
+       | BTRUE                 {$$.value.boolean = 1; $$.type = OCONFIG_TYPE_BOOLEAN;}
+       | BFALSE                {$$.value.boolean = 0; $$.type = OCONFIG_TYPE_BOOLEAN;}
        | string                {$$.value.string = $1; $$.type = OCONFIG_TYPE_STRING;}
        ;
 
index b559e86..9f0cd8e 100644 (file)
@@ -69,8 +69,8 @@ IPV4_ADDR {IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}(:{PORT})?
 "/"                    {return (SLASH);}
 "<"                    {return (OPENBRAC);}
 ">"                    {return (CLOSEBRAC);}
-{BOOL_TRUE}            {yylval.boolean = 1; return (TRUE);}
-{BOOL_FALSE}           {yylval.boolean = 0; return (FALSE);}
+{BOOL_TRUE}            {yylval.boolean = 1; return (BTRUE);}
+{BOOL_FALSE}           {yylval.boolean = 0; return (BFALSE);}
 
 {IPV4_ADDR}            {yylval.string = yytext; return (UNQUOTED_STRING);}
 
index 6f9e5f1..bcbf0e6 100644 (file)
@@ -187,9 +187,7 @@ lv_config (const char *key, const char *value)
     }
 
     if (strcasecmp (key, "IgnoreSelected") == 0) {
-        if (strcasecmp (value, "True") == 0 ||
-            strcasecmp (value, "Yes") == 0 ||
-            strcasecmp (value, "On") == 0)
+        if (IS_TRUE (value))
         {
             ignorelist_set_invert (il_domains, 0);
             ignorelist_set_invert (il_block_devices, 0);
index cf5221b..0188da7 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/load.c
  * Copyright (C) 2005-2008  Florian octo Forster
+ * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -17,6 +18,7 @@
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   Manuel Sanmartin
  **/
 
 #define _BSD_SOURCE
 #endif
 #endif /* defined(HAVE_GETLOADAVG) */
 
+#ifdef HAVE_PERFSTAT
+# include <sys/proc.h> /* AIX 5 */
+# include <sys/protosw.h>
+# include <libperfstat.h>
+#endif /* HAVE_PERFSTAT */
+
 static void load_submit (gauge_t snum, gauge_t mnum, gauge_t lnum)
 {
        value_t values[3];
@@ -81,7 +89,7 @@ static int load_read (void)
 
        char *fields[8];
        int numfields;
-       
+
        if ((loadavg = fopen ("/proc/loadavg", "r")) == NULL)
        {
                char errbuf[1024];
@@ -132,6 +140,25 @@ static int load_read (void)
        load_submit (snum, mnum, lnum);
 /* #endif HAVE_LIBSTATGRAB */
 
+#elif HAVE_PERFSTAT
+       gauge_t snum, mnum, lnum;
+       perfstat_cpu_total_t cputotal;
+
+       if (perfstat_cpu_total(NULL,  &cputotal, sizeof(perfstat_cpu_total_t), 1) < 0)
+       {
+               char errbuf[1024];
+               WARNING ("load: perfstat_cpu : %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       snum = (float)cputotal.loadavg[0]/(float)(1<<SBITS);
+       mnum = (float)cputotal.loadavg[1]/(float)(1<<SBITS);
+       lnum = (float)cputotal.loadavg[2]/(float)(1<<SBITS);
+
+       load_submit (snum, mnum, lnum);
+/* #endif HAVE_PERFSTAT */
+
 #else
 # error "No applicable input method."
 #endif
index 03af7a3..7b96ac5 100644 (file)
@@ -74,9 +74,7 @@ static int logfile_config (const char *key, const char *value)
                log_file = strdup (value);
        }
        else if (0 == strcasecmp (key, "Timestamp")) {
-               if ((strcasecmp (value, "false") == 0)
-                               || (strcasecmp (value, "no") == 0)
-                               || (strcasecmp (value, "off") == 0))
+               if (IS_FALSE (value))
                        print_timestamp = 0;
                else
                        print_timestamp = 1;
diff --git a/src/match_hashed.c b/src/match_hashed.c
new file mode 100644 (file)
index 0000000..ee3101a
--- /dev/null
@@ -0,0 +1,184 @@
+/**
+ * collectd - src/match_hashed.c
+ * Copyright (C) 2009  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:
+ *   Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_cache.h"
+#include "filter_chain.h"
+
+/*
+ * private data types
+ */
+struct mh_hash_match_s
+{
+  uint32_t match;
+  uint32_t total;
+};
+typedef struct mh_hash_match_s mh_hash_match_t;
+
+struct mh_match_s;
+typedef struct mh_match_s mh_match_t;
+struct mh_match_s
+{
+  mh_hash_match_t *matches;
+  size_t           matches_num;
+};
+
+/*
+ * internal helper functions
+ */
+static int mh_config_match (const oconfig_item_t *ci, /* {{{ */
+    mh_match_t *m)
+{
+  mh_hash_match_t *tmp;
+
+  if ((ci->values_num != 2)
+      || (ci->values[0].type != OCONFIG_TYPE_NUMBER)
+      || (ci->values[1].type != OCONFIG_TYPE_NUMBER))
+  {
+    ERROR ("hashed match: The `Match' option requires "
+        "exactly two numeric arguments.");
+    return (-1);
+  }
+
+  if ((ci->values[0].value.number < 0)
+      || (ci->values[1].value.number < 0))
+  {
+    ERROR ("hashed match: The arguments of the `Match' "
+        "option must be positive.");
+    return (-1);
+  }
+
+  tmp = realloc (m->matches, sizeof (*tmp) * (m->matches_num + 1));
+  if (tmp == NULL)
+  {
+    ERROR ("hashed match: realloc failed.");
+    return (-1);
+  }
+  m->matches = tmp;
+  tmp = m->matches + m->matches_num;
+
+  tmp->match = (uint32_t) (ci->values[0].value.number + .5);
+  tmp->total = (uint32_t) (ci->values[1].value.number + .5);
+
+  if (tmp->match >= tmp->total)
+  {
+    ERROR ("hashed match: The first argument of the `Match' option "
+        "must be smaller than the second argument.");
+    return (-1);
+  }
+  assert (tmp->total != 0);
+
+  m->matches_num++;
+  return (0);
+} /* }}} int mh_config_match */
+
+static int mh_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+  mh_match_t *m;
+  int i;
+
+  m = (mh_match_t *) malloc (sizeof (*m));
+  if (m == NULL)
+  {
+    ERROR ("mh_create: malloc failed.");
+    return (-ENOMEM);
+  }
+  memset (m, 0, sizeof (*m));
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Match", child->key) == 0)
+      mh_config_match (child, m);
+    else
+      ERROR ("hashed match: No such config option: %s", child->key);
+  }
+
+  if (m->matches_num == 0)
+  {
+    sfree (m->matches);
+    sfree (m);
+    ERROR ("hashed match: No matches were configured. Not creating match.");
+    return (-1);
+  }
+
+  *user_data = m;
+  return (0);
+} /* }}} int mh_create */
+
+static int mh_destroy (void **user_data) /* {{{ */
+{
+  mh_match_t *mh;
+
+  if ((user_data == NULL) || (*user_data == NULL))
+    return (0);
+
+  mh = *user_data;
+  sfree (mh->matches);
+  sfree (mh);
+
+  return (0);
+} /* }}} int mh_destroy */
+
+static int mh_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
+    const value_list_t *vl,
+    notification_meta_t __attribute__((unused)) **meta, void **user_data)
+{
+  mh_match_t *m;
+  uint32_t hash_val;
+  const char *host_ptr;
+  size_t i;
+
+  if ((user_data == NULL) || (*user_data == NULL))
+    return (-1);
+
+  m = *user_data;
+
+  hash_val = 0;
+
+  for (host_ptr = vl->host; *host_ptr != 0; host_ptr++)
+  {
+    /* 2184401929 is some appropriately sized prime number. */
+    hash_val = (hash_val * UINT32_C (2184401929)) + ((uint32_t) *host_ptr);
+  }
+  DEBUG ("hashed match: host = %s; hash_val = %"PRIu32";", vl->host, hash_val);
+
+  for (i = 0; i < m->matches_num; i++)
+    if ((hash_val % m->matches[i].total) == m->matches[i].match)
+      return (FC_MATCH_MATCHES);
+
+  return (FC_MATCH_NO_MATCH);
+} /* }}} int mh_match */
+
+void module_register (void)
+{
+  match_proc_t mproc;
+
+  memset (&mproc, 0, sizeof (mproc));
+  mproc.create  = mh_create;
+  mproc.destroy = mh_destroy;
+  mproc.match   = mh_match;
+  fc_register_match ("hashed", mproc);
+} /* module_register */
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
index b2da5ef..b79a74b 100644 (file)
@@ -2,6 +2,7 @@
  * collectd - src/memory.c
  * Copyright (C) 2005-2008  Florian octo Forster
  * Copyright (C) 2009       Simon Kuhnle
+ * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -19,6 +20,7 @@
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
  *   Simon Kuhnle <simon at blarzwurst.de>
+ *   Manuel Sanmartin
  **/
 
 #include "collectd.h"
 # include <statgrab.h>
 #endif
 
+#if HAVE_PERFSTAT
+# include <sys/protosw.h>
+# include <libperfstat.h>
+#endif /* HAVE_PERFSTAT */
+
 /* vm_statistics_data_t */
 #if HAVE_HOST_STATISTICS
 static mach_port_t port_host;
@@ -75,7 +82,10 @@ static int pagesize;
 #elif HAVE_LIBSTATGRAB
 /* no global variables */
 /* endif HAVE_LIBSTATGRAB */
-
+#elif HAVE_PERFSTAT
+static int pagesize;
+static perfstat_memory_total_t pmemory;
+/* endif HAVE_PERFSTAT */
 #else
 # error "No applicable input method."
 #endif
@@ -116,8 +126,11 @@ static int memory_init (void)
 
 #elif HAVE_LIBSTATGRAB
 /* no init stuff */
-#endif /* HAVE_LIBSTATGRAB */
+/* #endif HAVE_LIBSTATGRAB */
 
+#elif HAVE_PERFSTAT
+       pagesize = getpagesize ();
+#endif /* HAVE_PERFSTAT */
        return (0);
 } /* int memory_init */
 
@@ -250,7 +263,7 @@ static int memory_read (void)
 #elif KERNEL_LINUX
        FILE *fh;
        char buffer[1024];
-       
+
        char *fields[8];
        int numfields;
 
@@ -364,7 +377,22 @@ static int memory_read (void)
                memory_submit ("cached", ios->cache);
                memory_submit ("free",   ios->free);
        }
-#endif /* HAVE_LIBSTATGRAB */
+/* #endif HAVE_LIBSTATGRAB */
+
+#elif HAVE_PERFSTAT
+       if (perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1) < 0)
+       {
+               char errbuf[1024];
+               WARNING ("memory plugin: perfstat_memory_total failed: %s",
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+       memory_submit ("used",   pmemory.real_inuse * pagesize);
+       memory_submit ("free",   pmemory.real_free * pagesize);
+       memory_submit ("cached", pmemory.numperm * pagesize);
+       memory_submit ("system", pmemory.real_system * pagesize);
+       memory_submit ("user",   pmemory.real_process * pagesize);
+#endif /* HAVE_PERFSTAT */
 
        return (0);
 }
diff --git a/src/netapp.c b/src/netapp.c
new file mode 100644 (file)
index 0000000..abd5612
--- /dev/null
@@ -0,0 +1,2590 @@
+/**
+ * collectd - src/netapp.c
+ * Copyright (C) 2009  Sven Trenkel
+ *
+ * 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:
+ *   Sven Trenkel <collectd at semidefinite.de>  
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_ignorelist.h"
+
+#include <netapp_api.h>
+#include <netapp_errno.h>
+
+#define HAS_ALL_FLAGS(has,needs) (((has) & (needs)) == (needs))
+
+typedef struct host_config_s host_config_t;
+typedef void service_handler_t(host_config_t *host, na_elem_t *result, void *data);
+
+struct cna_interval_s
+{
+       time_t interval;
+       time_t last_read;
+};
+typedef struct cna_interval_s cna_interval_t;
+
+/*! Data types for WAFL statistics {{{
+ *
+ * \brief Persistent data for WAFL performance counters. (a.k.a. cache performance)
+ *
+ * The cache counters use old counter values to calculate a hit ratio for each
+ * counter. The "cfg_wafl_t" struct therefore contains old counter values along
+ * with flags, which are set if the counter is valid.
+ *
+ * The function "cna_handle_wafl_data" will fill a new structure of this kind
+ * with new values, then pass both, new and old data, to "submit_wafl_data".
+ * That function calculates the hit ratios, submits the calculated values and
+ * updates the old counter values for the next iteration.
+ */
+#define CFG_WAFL_NAME_CACHE        0x0001
+#define CFG_WAFL_DIR_CACHE         0x0002
+#define CFG_WAFL_BUF_CACHE         0x0004
+#define CFG_WAFL_INODE_CACHE       0x0008
+#define CFG_WAFL_ALL               0x000F
+#define HAVE_WAFL_NAME_CACHE_HIT   0x0100
+#define HAVE_WAFL_NAME_CACHE_MISS  0x0200
+#define HAVE_WAFL_NAME_CACHE       (HAVE_WAFL_NAME_CACHE_HIT | HAVE_WAFL_NAME_CACHE_MISS)
+#define HAVE_WAFL_FIND_DIR_HIT     0x0400
+#define HAVE_WAFL_FIND_DIR_MISS    0x0800
+#define HAVE_WAFL_FIND_DIR         (HAVE_WAFL_FIND_DIR_HIT | HAVE_WAFL_FIND_DIR_MISS)
+#define HAVE_WAFL_BUF_HASH_HIT     0x1000
+#define HAVE_WAFL_BUF_HASH_MISS    0x2000
+#define HAVE_WAFL_BUF_HASH         (HAVE_WAFL_BUF_HASH_HIT | HAVE_WAFL_BUF_HASH_MISS)
+#define HAVE_WAFL_INODE_CACHE_HIT  0x4000
+#define HAVE_WAFL_INODE_CACHE_MISS 0x8000
+#define HAVE_WAFL_INODE_CACHE      (HAVE_WAFL_INODE_CACHE_HIT | HAVE_WAFL_INODE_CACHE_MISS)
+#define HAVE_WAFL_ALL              0xff00
+typedef struct {
+       uint32_t flags;
+       cna_interval_t interval;
+       na_elem_t *query;
+
+       time_t timestamp;
+       uint64_t name_cache_hit;
+       uint64_t name_cache_miss;
+       uint64_t find_dir_hit;
+       uint64_t find_dir_miss;
+       uint64_t buf_hash_hit;
+       uint64_t buf_hash_miss;
+       uint64_t inode_cache_hit;
+       uint64_t inode_cache_miss;
+} cfg_wafl_t;
+/* }}} cfg_wafl_t */
+
+/*! Data types for disk statistics {{{
+ *
+ * \brief A disk in the NetApp.
+ *
+ * A disk doesn't have any more information than its name at the moment.
+ * The name includes the "disk_" prefix.
+ */
+#define HAVE_DISK_BUSY   0x10
+#define HAVE_DISK_BASE   0x20
+#define HAVE_DISK_ALL    0x30
+typedef struct disk_s {
+       char *name;
+       uint32_t flags;
+       time_t timestamp;
+       uint64_t disk_busy;
+       uint64_t base_for_disk_busy;
+       double disk_busy_percent;
+       struct disk_s *next;
+} disk_t;
+
+#define CFG_DISK_BUSIEST 0x01
+#define CFG_DISK_ALL     0x01
+typedef struct {
+       uint32_t flags;
+       cna_interval_t interval;
+       na_elem_t *query;
+       disk_t *disks;
+} cfg_disk_t;
+/* }}} cfg_disk_t */
+
+/*! Data types for volume performance statistics {{{
+ *
+ * \brief Persistent data for volume performance data.
+ *
+ * The code below uses the difference of the operations and latency counters to
+ * calculate an average per-operation latency. For this, old counters need to
+ * be stored in the "data_volume_perf_t" structure. The byte-counters are just
+ * kept for completeness sake. The "flags" member indicates if each counter is
+ * valid or not.
+ *
+ * The "cna_handle_volume_perf_data" function will fill a new struct of this
+ * type and pass both, old and new data, to "submit_volume_perf_data". In that
+ * function, the per-operation latency is calculated and dispatched, then the
+ * old counters are updated.
+ */
+#define CFG_VOLUME_PERF_INIT           0x0001
+#define CFG_VOLUME_PERF_IO             0x0002
+#define CFG_VOLUME_PERF_OPS            0x0003
+#define CFG_VOLUME_PERF_LATENCY        0x0008
+#define CFG_VOLUME_PERF_ALL            0x000F
+#define HAVE_VOLUME_PERF_BYTES_READ    0x0010
+#define HAVE_VOLUME_PERF_BYTES_WRITE   0x0020
+#define HAVE_VOLUME_PERF_OPS_READ      0x0040
+#define HAVE_VOLUME_PERF_OPS_WRITE     0x0080
+#define HAVE_VOLUME_PERF_LATENCY_READ  0x0100
+#define HAVE_VOLUME_PERF_LATENCY_WRITE 0x0200
+#define HAVE_VOLUME_PERF_ALL           0x03F0
+struct data_volume_perf_s;
+typedef struct data_volume_perf_s data_volume_perf_t;
+struct data_volume_perf_s {
+       char *name;
+       uint32_t flags;
+       time_t timestamp;
+
+       uint64_t read_bytes;
+       uint64_t write_bytes;
+       uint64_t read_ops;
+       uint64_t write_ops;
+       uint64_t read_latency;
+       uint64_t write_latency;
+
+       data_volume_perf_t *next;
+};
+
+typedef struct {
+       cna_interval_t interval;
+       na_elem_t *query;
+
+       ignorelist_t *il_octets;
+       ignorelist_t *il_operations;
+       ignorelist_t *il_latency;
+
+       data_volume_perf_t *volumes;
+} cfg_volume_perf_t;
+/* }}} data_volume_perf_t */
+
+/*! Data types for volume usage statistics {{{
+ *
+ * \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
+struct data_volume_usage_s;
+typedef struct data_volume_usage_s data_volume_usage_t;
+struct data_volume_usage_s {
+       char *name;
+       uint32_t flags;
+
+       na_elem_t *snap_query;
+
+       uint64_t norm_free;
+       uint64_t norm_used;
+       uint64_t snap_reserved;
+       uint64_t snap_used;
+       uint64_t sis_saved;
+
+       data_volume_usage_t *next;
+};
+
+typedef struct {
+       cna_interval_t interval;
+       na_elem_t *query;
+
+       ignorelist_t *il_capacity;
+       ignorelist_t *il_snapshot;
+
+       data_volume_usage_t *volumes;
+} cfg_volume_usage_t;
+/* }}} cfg_volume_usage_t */
+
+/*! Data types for system statistics {{{
+ *
+ * \brief Persistent data for system performance counters
+ */
+#define CFG_SYSTEM_CPU  0x01
+#define CFG_SYSTEM_NET  0x02
+#define CFG_SYSTEM_OPS  0x04
+#define CFG_SYSTEM_DISK 0x08
+#define CFG_SYSTEM_ALL  0x0F
+typedef struct {
+       uint32_t flags;
+       cna_interval_t interval;
+       na_elem_t *query;
+} cfg_system_t;
+/* }}} cfg_system_t */
+
+struct host_config_s {
+       char *name;
+       na_server_transport_t protocol;
+       char *host;
+       int port;
+       char *username;
+       char *password;
+       int 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_system_t *cfg_system;
+
+       struct host_config_s *next;
+};
+
+/*
+ * Free functions
+ *
+ * Used to free the various structures above.
+ */
+static void free_disk (disk_t *disk) /* {{{ */
+{
+       disk_t *next;
+
+       if (disk == NULL)
+               return;
+
+       next = disk->next;
+
+       sfree (disk->name);
+       sfree (disk);
+
+       free_disk (next);
+} /* }}} void free_disk */
+
+static void free_cfg_wafl (cfg_wafl_t *cw) /* {{{ */
+{
+       if (cw == NULL)
+               return;
+
+       if (cw->query != NULL)
+               na_elem_free (cw->query);
+
+       sfree (cw);
+} /* }}} void free_cfg_wafl */
+
+static void free_cfg_disk (cfg_disk_t *cfg_disk) /* {{{ */
+{
+       if (cfg_disk == NULL)
+               return;
+
+       if (cfg_disk->query != NULL)
+               na_elem_free (cfg_disk->query);
+
+       free_disk (cfg_disk->disks);
+       sfree (cfg_disk);
+} /* }}} void free_cfg_disk */
+
+static void free_cfg_volume_perf (cfg_volume_perf_t *cvp) /* {{{ */
+{
+       data_volume_perf_t *data;
+
+       if (cvp == NULL)
+               return;
+
+       /* Free the ignorelists */
+       ignorelist_free (cvp->il_octets);
+       ignorelist_free (cvp->il_operations);
+       ignorelist_free (cvp->il_latency);
+
+       /* Free the linked list of volumes */
+       data = cvp->volumes;
+       while (data != NULL)
+       {
+               data_volume_perf_t *next = data->next;
+               sfree (data->name);
+               sfree (data);
+               data = next;
+       }
+
+       if (cvp->query != NULL)
+               na_elem_free (cvp->query);
+
+       sfree (cvp);
+} /* }}} void free_cfg_volume_perf */
+
+static void free_cfg_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */
+{
+       data_volume_usage_t *data;
+
+       if (cvu == NULL)
+               return;
+
+       /* Free the ignorelists */
+       ignorelist_free (cvu->il_capacity);
+       ignorelist_free (cvu->il_snapshot);
+
+       /* Free the linked list of volumes */
+       data = cvu->volumes;
+       while (data != NULL)
+       {
+               data_volume_usage_t *next = data->next;
+               sfree (data->name);
+               if (data->snap_query != NULL)
+                       na_elem_free(data->snap_query);
+               sfree (data);
+               data = next;
+       }
+
+       if (cvu->query != NULL)
+               na_elem_free (cvu->query);
+
+       sfree (cvu);
+} /* }}} void free_cfg_volume_usage */
+
+static void free_cfg_system (cfg_system_t *cs) /* {{{ */
+{
+       if (cs == NULL)
+               return;
+
+       if (cs->query != NULL)
+               na_elem_free (cs->query);
+
+       sfree (cs);
+} /* }}} void free_cfg_system */
+
+static void free_host_config (host_config_t *hc) /* {{{ */
+{
+       host_config_t *next;
+
+       if (hc == NULL)
+               return;
+
+       next = hc->next;
+
+       sfree (hc->name);
+       sfree (hc->host);
+       sfree (hc->username);
+       sfree (hc->password);
+
+       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_system (hc->cfg_system);
+
+       if (hc->srv != NULL)
+               na_server_close (hc->srv);
+
+       sfree (hc);
+
+       free_host_config (next);
+} /* }}} void free_host_config */
+
+/*
+ * Auxiliary functions
+ *
+ * Used to look up volumes and disks or to handle flags.
+ */
+static disk_t *get_disk(cfg_disk_t *cd, const char *name) /* {{{ */
+{
+       disk_t *d;
+
+       if ((cd == NULL) || (name == NULL))
+               return (NULL);
+
+       for (d = cd->disks; d != NULL; d = d->next) {
+               if (strcmp(d->name, name) == 0)
+                       return d;
+       }
+
+       d = malloc(sizeof(*d));
+       if (d == NULL)
+               return (NULL);
+       memset (d, 0, sizeof (*d));
+       d->next = NULL;
+
+       d->name = strdup(name);
+       if (d->name == NULL) {
+               sfree (d);
+               return (NULL);
+       }
+
+       d->next = cd->disks;
+       cd->disks = d;
+
+       return d;
+} /* }}} disk_t *get_disk */
+
+static data_volume_usage_t *get_volume_usage (cfg_volume_usage_t *cvu, /* {{{ */
+               const char *name)
+{
+       data_volume_usage_t *last;
+       data_volume_usage_t *new;
+
+       int ignore_capacity = 0;
+       int ignore_snapshot = 0;
+
+       if ((cvu == NULL) || (name == NULL))
+               return (NULL);
+
+       last = cvu->volumes;
+       while (last != NULL)
+       {
+               if (strcmp (last->name, name) == 0)
+                       return (last);
+
+               if (last->next == NULL)
+                       break;
+
+               last = last->next;
+       }
+
+       /* Check the ignorelists. If *both* tell us to ignore a volume, return NULL. */
+       ignore_capacity = ignorelist_match (cvu->il_capacity, name);
+       ignore_snapshot = ignorelist_match (cvu->il_snapshot, name);
+       if ((ignore_capacity != 0) && (ignore_snapshot != 0))
+               return (NULL);
+
+       /* Not found: allocate. */
+       new = malloc (sizeof (*new));
+       if (new == NULL)
+               return (NULL);
+       memset (new, 0, sizeof (*new));
+       new->next = NULL;
+
+       new->name = strdup (name);
+       if (new->name == NULL)
+       {
+               sfree (new);
+               return (NULL);
+       }
+
+       if (ignore_capacity == 0)
+               new->flags |= CFG_VOLUME_USAGE_DF;
+       if (ignore_snapshot == 0) {
+               new->flags |= CFG_VOLUME_USAGE_SNAP;
+               new->snap_query = na_elem_new ("snapshot-list-info");
+               na_child_add_string(new->snap_query, "target-type", "volume");
+               na_child_add_string(new->snap_query, "target-name", name);
+       } else {
+               new->snap_query = NULL;
+       }
+
+       /* Add to end of list. */
+       if (last == NULL)
+               cvu->volumes = new;
+       else
+               last->next = new;
+
+       return (new);
+} /* }}} data_volume_usage_t *get_volume_usage */
+
+static data_volume_perf_t *get_volume_perf (cfg_volume_perf_t *cvp, /* {{{ */
+               const char *name)
+{
+       data_volume_perf_t *last;
+       data_volume_perf_t *new;
+
+       int ignore_octets = 0;
+       int ignore_operations = 0;
+       int ignore_latency = 0;
+
+       if ((cvp == NULL) || (name == NULL))
+               return (NULL);
+
+       last = cvp->volumes;
+       while (last != NULL)
+       {
+               if (strcmp (last->name, name) == 0)
+                       return (last);
+
+               if (last->next == NULL)
+                       break;
+
+               last = last->next;
+       }
+
+       /* Check the ignorelists. If *all three* tell us to ignore a volume, return
+        * NULL. */
+       ignore_octets = ignorelist_match (cvp->il_octets, name);
+       ignore_operations = ignorelist_match (cvp->il_operations, name);
+       ignore_latency = ignorelist_match (cvp->il_latency, name);
+       if ((ignore_octets != 0) || (ignore_operations != 0)
+                       || (ignore_latency != 0))
+               return (NULL);
+
+       /* Not found: allocate. */
+       new = malloc (sizeof (*new));
+       if (new == NULL)
+               return (NULL);
+       memset (new, 0, sizeof (*new));
+       new->next = NULL;
+
+       new->name = strdup (name);
+       if (new->name == NULL)
+       {
+               sfree (new);
+               return (NULL);
+       }
+
+       if (ignore_octets == 0)
+               new->flags |= CFG_VOLUME_PERF_IO;
+       if (ignore_operations == 0)
+               new->flags |= CFG_VOLUME_PERF_OPS;
+       if (ignore_latency == 0)
+               new->flags |= CFG_VOLUME_PERF_LATENCY;
+
+       /* Add to end of list. */
+       if (last == NULL)
+               cvp->volumes = new;
+       else
+               last->next = new;
+
+       return (new);
+} /* }}} data_volume_perf_t *get_volume_perf */
+
+/*
+ * Various submit functions.
+ *
+ * They all eventually call "submit_values" which creates a value_list_t and
+ * dispatches it to the daemon.
+ */
+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)
+{
+       value_list_t vl = VALUE_LIST_INIT;
+
+       vl.values = values;
+       vl.values_len = values_len;
+
+       if (timestamp > 0)
+               vl.time = timestamp;
+
+       if (host != NULL)
+               sstrncpy (vl.host, host, sizeof (vl.host));
+       else
+               sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "netapp", sizeof (vl.plugin));
+       if (plugin_inst != NULL)
+               sstrncpy (vl.plugin_instance, plugin_inst, sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       if (type_inst != NULL)
+               sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+
+       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)
+{
+       value_t values[2];
+
+       values[0].counter = val0;
+       values[1].counter = val1;
+
+       return (submit_values (host, plugin_inst, type, type_inst,
+                               values, 2, timestamp));
+} /* }}} int submit_two_counters */
+
+static int submit_counter (const char *host, const char *plugin_inst, /* {{{ */
+               const char *type, const char *type_inst, counter_t counter, time_t timestamp)
+{
+       value_t v;
+
+       v.counter = counter;
+
+       return (submit_values (host, plugin_inst, type, type_inst,
+                               &v, 1, timestamp));
+} /* }}} int submit_counter */
+
+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)
+{
+       value_t values[2];
+
+       values[0].gauge = val0;
+       values[1].gauge = val1;
+
+       return (submit_values (host, plugin_inst, type, type_inst,
+                               values, 2, timestamp));
+} /* }}} 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)
+{
+       value_t v;
+
+       v.gauge = (gauge_t) d;
+
+       return (submit_values (host, plugin_inst, type, type_inst,
+                               &v, 1, timestamp));
+} /* }}} int submit_uint64 */
+
+/* Calculate hit ratio from old and new counters and submit the resulting
+ * percentage. Used by "submit_wafl_data". */
+static int submit_cache_ratio (const char *host, /* {{{ */
+               const char *plugin_inst,
+               const char *type_inst,
+               uint64_t new_hits,
+               uint64_t new_misses,
+               uint64_t old_hits,
+               uint64_t old_misses,
+               time_t timestamp)
+{
+       value_t v;
+
+       if ((new_hits >= old_hits) && (new_misses >= old_misses)) {
+               uint64_t hits;
+               uint64_t misses;
+
+               hits = new_hits - old_hits;
+               misses = new_misses - old_misses;
+
+               v.gauge = 100.0 * ((gauge_t) hits) / ((gauge_t) (hits + misses));
+       } else {
+               v.gauge = NAN;
+       }
+
+       return (submit_values (host, plugin_inst, "cache_ratio", type_inst,
+                               &v, 1, timestamp));
+} /* }}} int submit_cache_ratio */
+
+/* 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)
+{
+       /* Submit requested counters */
+       if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_NAME_CACHE | HAVE_WAFL_NAME_CACHE)
+                       && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_NAME_CACHE))
+               submit_cache_ratio (hostname, instance, "name_cache_hit",
+                               new_data->name_cache_hit, new_data->name_cache_miss,
+                               old_data->name_cache_hit, old_data->name_cache_miss,
+                               new_data->timestamp);
+
+       if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_DIR_CACHE | HAVE_WAFL_FIND_DIR)
+                       && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_FIND_DIR))
+               submit_cache_ratio (hostname, instance, "find_dir_hit",
+                               new_data->find_dir_hit, new_data->find_dir_miss,
+                               old_data->find_dir_hit, old_data->find_dir_miss,
+                               new_data->timestamp);
+
+       if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_BUF_CACHE | HAVE_WAFL_BUF_HASH)
+                       && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_BUF_HASH))
+               submit_cache_ratio (hostname, instance, "buf_hash_hit",
+                               new_data->buf_hash_hit, new_data->buf_hash_miss,
+                               old_data->buf_hash_hit, old_data->buf_hash_miss,
+                               new_data->timestamp);
+
+       if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_INODE_CACHE | HAVE_WAFL_INODE_CACHE)
+                       && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_INODE_CACHE))
+               submit_cache_ratio (hostname, instance, "inode_cache_hit",
+                               new_data->inode_cache_hit, new_data->inode_cache_miss,
+                               old_data->inode_cache_hit, old_data->inode_cache_miss,
+                               new_data->timestamp);
+
+       /* Clear old HAVE_* flags */
+       old_data->flags &= ~HAVE_WAFL_ALL;
+
+       /* Copy all counters */
+       old_data->timestamp        = new_data->timestamp;
+       old_data->name_cache_hit   = new_data->name_cache_hit;
+       old_data->name_cache_miss  = new_data->name_cache_miss;
+       old_data->find_dir_hit     = new_data->find_dir_hit;
+       old_data->find_dir_miss    = new_data->find_dir_miss;
+       old_data->buf_hash_hit     = new_data->buf_hash_hit;
+       old_data->buf_hash_miss    = new_data->buf_hash_miss;
+       old_data->inode_cache_hit  = new_data->inode_cache_hit;
+       old_data->inode_cache_miss = new_data->inode_cache_miss;
+
+       /* Copy HAVE_* flags */
+       old_data->flags |= (new_data->flags & HAVE_WAFL_ALL);
+
+       return (0);
+} /* }}} int submit_wafl_data */
+
+/* Submits volume performance data to the daemon, taking care to honor and
+ * update flags appropriately. */
+static int submit_volume_perf_data (const char *hostname, /* {{{ */
+               data_volume_perf_t *old_data,
+               const data_volume_perf_t *new_data)
+{
+       char plugin_instance[DATA_MAX_NAME_LEN];
+
+       if ((hostname == NULL) || (old_data == NULL) || (new_data == NULL))
+               return (-1);
+
+       ssnprintf (plugin_instance, sizeof (plugin_instance),
+                       "volume-%s", old_data->name);
+
+       /* Check for and submit disk-octet values */
+       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);
+       }
+
+       /* 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);
+       }
+
+       /* Check for, calculate and submit disk-latency values */
+       if (HAS_ALL_FLAGS (old_data->flags, CFG_VOLUME_PERF_LATENCY
+                               | HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE
+                               | HAVE_VOLUME_PERF_LATENCY_READ | HAVE_VOLUME_PERF_LATENCY_WRITE)
+                       && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE
+                               | HAVE_VOLUME_PERF_LATENCY_READ | HAVE_VOLUME_PERF_LATENCY_WRITE))
+       {
+               gauge_t latency_per_op_read;
+               gauge_t latency_per_op_write;
+
+               latency_per_op_read = NAN;
+               latency_per_op_write = NAN;
+
+               /* Check if a counter wrapped around. */
+               if ((new_data->read_ops > old_data->read_ops)
+                               && (new_data->read_latency > old_data->read_latency))
+               {
+                       uint64_t diff_ops_read;
+                       uint64_t diff_latency_read;
+
+                       diff_ops_read = new_data->read_ops - old_data->read_ops;
+                       diff_latency_read = new_data->read_latency - old_data->read_latency;
+
+                       if (diff_ops_read > 0)
+                               latency_per_op_read = ((gauge_t) diff_latency_read) / ((gauge_t) diff_ops_read);
+               }
+
+               if ((new_data->write_ops > old_data->write_ops)
+                               && (new_data->write_latency > old_data->write_latency))
+               {
+                       uint64_t diff_ops_write;
+                       uint64_t diff_latency_write;
+
+                       diff_ops_write = new_data->write_ops - old_data->write_ops;
+                       diff_latency_write = new_data->write_latency - old_data->write_latency;
+
+                       if (diff_ops_write > 0)
+                               latency_per_op_write = ((gauge_t) diff_latency_write) / ((gauge_t) diff_ops_write);
+               }
+
+               submit_two_gauge (hostname, plugin_instance, "disk_latency", /* type instance = */ NULL,
+                               latency_per_op_read, latency_per_op_write, new_data->timestamp);
+       }
+
+       /* Clear all HAVE_* flags. */
+       old_data->flags &= ~HAVE_VOLUME_PERF_ALL;
+
+       /* Copy all counters */
+       old_data->timestamp = new_data->timestamp;
+       old_data->read_bytes = new_data->read_bytes;
+       old_data->write_bytes = new_data->write_bytes;
+       old_data->read_ops = new_data->read_ops;
+       old_data->write_ops = new_data->write_ops;
+       old_data->read_latency = new_data->read_latency;
+       old_data->write_latency = new_data->write_latency;
+
+       /* Copy the HAVE_* flags */
+       old_data->flags |= (new_data->flags & HAVE_VOLUME_PERF_ALL);
+
+       return (0);
+} /* }}} int submit_volume_perf_data */
+
+/* 
+ * Query functions
+ *
+ * These functions are called with appropriate data returned by the libnetapp
+ * interface which is parsed and submitted with the above functions.
+ */
+/* Data corresponding to <WAFL /> */
+static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /* {{{ */
+               na_elem_t *data)
+{
+       cfg_wafl_t perf_data;
+       const char *plugin_inst;
+
+       na_elem_t *instances;
+       na_elem_t *counter;
+       na_elem_iter_t counter_iter;
+
+       memset (&perf_data, 0, sizeof (perf_data));
+       
+       perf_data.timestamp = (time_t) na_child_get_uint64 (data, "timestamp", 0);
+
+       instances = na_elem_child(na_elem_child (data, "instances"), "instance-data");
+       if (instances == NULL)
+       {
+               ERROR ("netapp plugin: cna_handle_wafl_data: "
+                               "na_elem_child (\"instances\") failed "
+                               "for host %s.", hostname);
+               return (-1);
+       }
+
+       plugin_inst = na_child_get_string(instances, "name");
+       if (plugin_inst == NULL)
+       {
+               ERROR ("netapp plugin: cna_handle_wafl_data: "
+                               "na_child_get_string (\"name\") failed "
+                               "for host %s.", hostname);
+               return (-1);
+       }
+
+       /* Iterate over all counters */
+       counter_iter = na_child_iterator (na_elem_child (instances, "counters"));
+       for (counter = na_iterator_next (&counter_iter);
+                       counter != NULL;
+                       counter = na_iterator_next (&counter_iter))
+       {
+               const char *name;
+               uint64_t value;
+
+               name = na_child_get_string(counter, "name");
+               if (name == NULL)
+                       continue;
+
+               value = na_child_get_uint64(counter, "value", UINT64_MAX);
+               if (value == UINT64_MAX)
+                       continue;
+
+               if (!strcmp(name, "name_cache_hit")) {
+                       perf_data.name_cache_hit = value;
+                       perf_data.flags |= HAVE_WAFL_NAME_CACHE_HIT;
+               } else if (!strcmp(name, "name_cache_miss")) {
+                       perf_data.name_cache_miss = value;
+                       perf_data.flags |= HAVE_WAFL_NAME_CACHE_MISS;
+               } else if (!strcmp(name, "find_dir_hit")) {
+                       perf_data.find_dir_hit = value;
+                       perf_data.flags |= HAVE_WAFL_FIND_DIR_HIT;
+               } else if (!strcmp(name, "find_dir_miss")) {
+                       perf_data.find_dir_miss = value;
+                       perf_data.flags |= HAVE_WAFL_FIND_DIR_MISS;
+               } else if (!strcmp(name, "buf_hash_hit")) {
+                       perf_data.buf_hash_hit = value;
+                       perf_data.flags |= HAVE_WAFL_BUF_HASH_HIT;
+               } else if (!strcmp(name, "buf_hash_miss")) {
+                       perf_data.buf_hash_miss = value;
+                       perf_data.flags |= HAVE_WAFL_BUF_HASH_MISS;
+               } else if (!strcmp(name, "inode_cache_hit")) {
+                       perf_data.inode_cache_hit = value;
+                       perf_data.flags |= HAVE_WAFL_INODE_CACHE_HIT;
+               } else if (!strcmp(name, "inode_cache_miss")) {
+                       perf_data.inode_cache_miss = value;
+                       perf_data.flags |= HAVE_WAFL_INODE_CACHE_MISS;
+               } else {
+                       DEBUG("netapp plugin: cna_handle_wafl_data: "
+                                       "Found unexpected child: %s "
+                                       "for host %s.", name, hostname);
+               }
+       }
+
+       return (submit_wafl_data (hostname, plugin_inst, cfg_wafl, &perf_data));
+} /* }}} void cna_handle_wafl_data */
+
+static int cna_setup_wafl (cfg_wafl_t *cw) /* {{{ */
+{
+       na_elem_t *e;
+
+       if (cw == NULL)
+               return (EINVAL);
+
+       if (cw->query != NULL)
+               return (0);
+
+       cw->query = na_elem_new("perf-object-get-instances");
+       if (cw->query == NULL)
+       {
+               ERROR ("netapp plugin: na_elem_new failed.");
+               return (-1);
+       }
+       na_child_add_string (cw->query, "objectname", "wafl");
+
+       e = na_elem_new("counters");
+       if (e == NULL)
+       {
+               na_elem_free (cw->query);
+               cw->query = NULL;
+               ERROR ("netapp plugin: na_elem_new failed.");
+               return (-1);
+       }
+       na_child_add_string(e, "counter", "name_cache_hit");
+       na_child_add_string(e, "counter", "name_cache_miss");
+       na_child_add_string(e, "counter", "find_dir_hit");
+       na_child_add_string(e, "counter", "find_dir_miss");
+       na_child_add_string(e, "counter", "buf_hash_hit");
+       na_child_add_string(e, "counter", "buf_hash_miss");
+       na_child_add_string(e, "counter", "inode_cache_hit");
+       na_child_add_string(e, "counter", "inode_cache_miss");
+
+       na_child_add(cw->query, e);
+
+       return (0);
+} /* }}} int cna_setup_wafl */
+
+static int cna_query_wafl (host_config_t *host) /* {{{ */
+{
+       na_elem_t *data;
+       int status;
+       time_t now;
+
+       if (host == NULL)
+               return (EINVAL);
+
+       /* If WAFL was not configured, return without doing anything. */
+       if (host->cfg_wafl == NULL)
+               return (0);
+
+       now = time (NULL);
+       if ((host->cfg_wafl->interval.interval + host->cfg_wafl->interval.last_read) > now)
+               return (0);
+
+       status = cna_setup_wafl (host->cfg_wafl);
+       if (status != 0)
+               return (status);
+       assert (host->cfg_wafl->query != NULL);
+
+       data = na_server_invoke_elem(host->srv, host->cfg_wafl->query);
+       if (na_results_status (data) != NA_OK)
+       {
+               ERROR ("netapp plugin: cna_query_wafl: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
+               na_elem_free (data);
+               return (-1);
+       }
+
+       status = cna_handle_wafl_data (host->name, host->cfg_wafl, data);
+
+       if (status == 0)
+               host->cfg_wafl->interval.last_read = now;
+
+       na_elem_free (data);
+       return (status);
+} /* }}} int cna_query_wafl */
+
+/* Data corresponding to <Disks /> */
+static int cna_handle_disk_data (const char *hostname, /* {{{ */
+               cfg_disk_t *cfg_disk, na_elem_t *data)
+{
+       time_t timestamp;
+       na_elem_t *instances;
+       na_elem_t *instance;
+       na_elem_iter_t instance_iter;
+       disk_t *worst_disk = NULL;
+
+       if ((cfg_disk == NULL) || (data == NULL))
+               return (EINVAL);
+       
+       timestamp = (time_t) na_child_get_uint64(data, "timestamp", 0);
+
+       instances = na_elem_child (data, "instances");
+       if (instances == NULL)
+       {
+               ERROR ("netapp plugin: cna_handle_disk_data: "
+                               "na_elem_child (\"instances\") failed "
+                               "for host %s.", hostname);
+               return (-1);
+       }
+
+       /* Iterate over all children */
+       instance_iter = na_child_iterator (instances);
+       for (instance = na_iterator_next (&instance_iter);
+                       instance != NULL;
+                       instance = na_iterator_next(&instance_iter))
+       {
+               disk_t *old_data;
+               disk_t  new_data;
+
+               na_elem_iter_t counter_iterator;
+               na_elem_t *counter;
+
+               memset (&new_data, 0, sizeof (new_data));
+               new_data.timestamp = timestamp;
+               new_data.disk_busy_percent = NAN;
+
+               old_data = get_disk(cfg_disk, na_child_get_string (instance, "name"));
+               if (old_data == NULL)
+                       continue;
+
+               /* Look for the "disk_busy" and "base_for_disk_busy" counters */
+               counter_iterator = na_child_iterator(na_elem_child(instance, "counters"));
+               for (counter = na_iterator_next(&counter_iterator);
+                               counter != NULL;
+                               counter = na_iterator_next(&counter_iterator))
+               {
+                       const char *name;
+                       uint64_t value;
+
+                       name = na_child_get_string(counter, "name");
+                       if (name == NULL)
+                               continue;
+
+                       value = na_child_get_uint64(counter, "value", UINT64_MAX);
+                       if (value == UINT64_MAX)
+                               continue;
+
+                       if (strcmp(name, "disk_busy") == 0)
+                       {
+                               new_data.disk_busy = value;
+                               new_data.flags |= HAVE_DISK_BUSY;
+                       }
+                       else if (strcmp(name, "base_for_disk_busy") == 0)
+                       {
+                               new_data.base_for_disk_busy = value;
+                               new_data.flags |= HAVE_DISK_BASE;
+                       }
+                       else
+                       {
+                               DEBUG ("netapp plugin: cna_handle_disk_data: "
+                                               "Counter not handled: %s = %"PRIu64,
+                                               name, value);
+                       }
+               }
+
+               /* If all required counters are available and did not just wrap around,
+                * calculate the busy percentage. Otherwise, the value is initialized to
+                * NAN at the top of the for-loop. */
+               if (HAS_ALL_FLAGS (old_data->flags, HAVE_DISK_BUSY | HAVE_DISK_BASE)
+                               && HAS_ALL_FLAGS (new_data.flags, HAVE_DISK_BUSY | HAVE_DISK_BASE)
+                               && (new_data.disk_busy >= old_data->disk_busy)
+                               && (new_data.base_for_disk_busy > old_data->base_for_disk_busy))
+               {
+                       uint64_t busy_diff;
+                       uint64_t base_diff;
+
+                       busy_diff = new_data.disk_busy - old_data->disk_busy;
+                       base_diff = new_data.base_for_disk_busy - old_data->base_for_disk_busy;
+
+                       new_data.disk_busy_percent = 100.0
+                               * ((gauge_t) busy_diff) / ((gauge_t) base_diff);
+               }
+
+               /* Clear HAVE_* flags */
+               old_data->flags &= ~HAVE_DISK_ALL;
+
+               /* Copy data */
+               old_data->timestamp = new_data.timestamp;
+               old_data->disk_busy = new_data.disk_busy;
+               old_data->base_for_disk_busy = new_data.base_for_disk_busy;
+               old_data->disk_busy_percent = new_data.disk_busy_percent;
+
+               /* Copy flags */
+               old_data->flags |= (new_data.flags & HAVE_DISK_ALL);
+
+               if ((worst_disk == NULL)
+                               || (worst_disk->disk_busy_percent < old_data->disk_busy_percent))
+                       worst_disk = old_data;
+       } /* for (all disks) */
+
+       if ((cfg_disk->flags & CFG_DISK_BUSIEST) && (worst_disk != NULL))
+               submit_double (hostname, "system", "percent", "disk_busy",
+                               worst_disk->disk_busy_percent, timestamp);
+
+       return (0);
+} /* }}} int cna_handle_disk_data */
+
+static int cna_setup_disk (cfg_disk_t *cd) /* {{{ */
+{
+       na_elem_t *e;
+
+       if (cd == NULL)
+               return (EINVAL);
+
+       if (cd->query != NULL)
+               return (0);
+
+       cd->query = na_elem_new ("perf-object-get-instances");
+       if (cd->query == NULL)
+       {
+               ERROR ("netapp plugin: na_elem_new failed.");
+               return (-1);
+       }
+       na_child_add_string (cd->query, "objectname", "disk");
+
+       e = na_elem_new("counters");
+       if (e == NULL)
+       {
+               na_elem_free (cd->query);
+               cd->query = NULL;
+               ERROR ("netapp plugin: na_elem_new failed.");
+               return (-1);
+       }
+       na_child_add_string(e, "counter", "disk_busy");
+       na_child_add_string(e, "counter", "base_for_disk_busy");
+       na_child_add(cd->query, e);
+
+       return (0);
+} /* }}} int cna_setup_disk */
+
+static int cna_query_disk (host_config_t *host) /* {{{ */
+{
+       na_elem_t *data;
+       int status;
+       time_t now;
+
+       if (host == NULL)
+               return (EINVAL);
+
+       /* If the user did not configure disk statistics, return without doing
+        * anything. */
+       if (host->cfg_disk == NULL)
+               return (0);
+
+       now = time (NULL);
+       if ((host->cfg_disk->interval.interval + host->cfg_disk->interval.last_read) > now)
+               return (0);
+
+       status = cna_setup_disk (host->cfg_disk);
+       if (status != 0)
+               return (status);
+       assert (host->cfg_disk->query != NULL);
+
+       data = na_server_invoke_elem(host->srv, host->cfg_disk->query);
+       if (na_results_status (data) != NA_OK)
+       {
+               ERROR ("netapp plugin: cna_query_disk: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
+               na_elem_free (data);
+               return (-1);
+       }
+
+       status = cna_handle_disk_data (host->name, host->cfg_disk, data);
+
+       if (status == 0)
+               host->cfg_disk->interval.last_read = now;
+
+       na_elem_free (data);
+       return (status);
+} /* }}} int cna_query_disk */
+
+/* Data corresponding to <VolumePerf /> */
+static int cna_handle_volume_perf_data (const char *hostname, /* {{{ */
+               cfg_volume_perf_t *cvp, na_elem_t *data)
+{
+       time_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);
+
+       elem_instances = na_elem_child(data, "instances");
+       if (elem_instances == NULL)
+       {
+               ERROR ("netapp plugin: handle_volume_perf_data: "
+                               "na_elem_child (\"instances\") failed "
+                               "for host %s.", hostname);
+               return (-1);
+       }
+
+       iter_instances = na_child_iterator (elem_instances);
+       for (elem_instance = na_iterator_next(&iter_instances);
+                       elem_instance != NULL;
+                       elem_instance = na_iterator_next(&iter_instances))
+       {
+               const char *name;
+
+               data_volume_perf_t perf_data;
+               data_volume_perf_t *v;
+
+               na_elem_t *elem_counters;
+               na_elem_iter_t iter_counters;
+               na_elem_t *elem_counter;
+
+               memset (&perf_data, 0, sizeof (perf_data));
+               perf_data.timestamp = timestamp;
+
+               name = na_child_get_string (elem_instance, "name");
+               if (name == NULL)
+                       continue;
+
+               /* get_volume_perf may return NULL if this volume is to be ignored. */
+               v = get_volume_perf (cvp, name);
+               if (v == NULL)
+                       continue;
+
+               elem_counters = na_elem_child (elem_instance, "counters");
+               if (elem_counters == NULL)
+                       continue;
+
+               iter_counters = na_child_iterator (elem_counters);
+               for (elem_counter = na_iterator_next(&iter_counters);
+                               elem_counter != NULL;
+                               elem_counter = na_iterator_next(&iter_counters))
+               {
+                       const char *name;
+                       uint64_t value;
+
+                       name = na_child_get_string (elem_counter, "name");
+                       if (name == NULL)
+                               continue;
+
+                       value = na_child_get_uint64 (elem_counter, "value", UINT64_MAX);
+                       if (value == UINT64_MAX)
+                               continue;
+
+                       if (!strcmp(name, "read_data")) {
+                               perf_data.read_bytes = value;
+                               perf_data.flags |= HAVE_VOLUME_PERF_BYTES_READ;
+                       } else if (!strcmp(name, "write_data")) {
+                               perf_data.write_bytes = value;
+                               perf_data.flags |= HAVE_VOLUME_PERF_BYTES_WRITE;
+                       } else if (!strcmp(name, "read_ops")) {
+                               perf_data.read_ops = value;
+                               perf_data.flags |= HAVE_VOLUME_PERF_OPS_READ;
+                       } else if (!strcmp(name, "write_ops")) {
+                               perf_data.write_ops = value;
+                               perf_data.flags |= HAVE_VOLUME_PERF_OPS_WRITE;
+                       } else if (!strcmp(name, "read_latency")) {
+                               perf_data.read_latency = value;
+                               perf_data.flags |= HAVE_VOLUME_PERF_LATENCY_READ;
+                       } else if (!strcmp(name, "write_latency")) {
+                               perf_data.write_latency = value;
+                               perf_data.flags |= HAVE_VOLUME_PERF_LATENCY_WRITE;
+                       }
+               } /* for (elem_counter) */
+
+               submit_volume_perf_data (hostname, v, &perf_data);
+       } /* for (volume) */
+
+       return (0);
+} /* }}} int cna_handle_volume_perf_data */
+
+static int cna_setup_volume_perf (cfg_volume_perf_t *cd) /* {{{ */
+{
+       na_elem_t *e;
+
+       if (cd == NULL)
+               return (EINVAL);
+
+       if (cd->query != NULL)
+               return (0);
+
+       cd->query = na_elem_new ("perf-object-get-instances");
+       if (cd->query == NULL)
+       {
+               ERROR ("netapp plugin: na_elem_new failed.");
+               return (-1);
+       }
+       na_child_add_string (cd->query, "objectname", "volume");
+
+       e = na_elem_new("counters");
+       if (e == NULL)
+       {
+               na_elem_free (cd->query);
+               cd->query = NULL;
+               ERROR ("netapp plugin: na_elem_new failed.");
+               return (-1);
+       }
+       na_child_add_string(e, "counter", "read_ops");
+       na_child_add_string(e, "counter", "write_ops");
+       na_child_add_string(e, "counter", "read_data");
+       na_child_add_string(e, "counter", "write_data");
+       na_child_add_string(e, "counter", "read_latency");
+       na_child_add_string(e, "counter", "write_latency");
+       na_child_add(cd->query, e);
+
+       return (0);
+} /* }}} int cna_setup_volume_perf */
+
+static int cna_query_volume_perf (host_config_t *host) /* {{{ */
+{
+       na_elem_t *data;
+       int status;
+       time_t now;
+
+       if (host == NULL)
+               return (EINVAL);
+
+       /* If the user did not configure volume performance statistics, return
+        * without doing anything. */
+       if (host->cfg_volume_perf == NULL)
+               return (0);
+
+       now = time (NULL);
+       if ((host->cfg_volume_perf->interval.interval + host->cfg_volume_perf->interval.last_read) > now)
+               return (0);
+
+       status = cna_setup_volume_perf (host->cfg_volume_perf);
+       if (status != 0)
+               return (status);
+       assert (host->cfg_volume_perf->query != NULL);
+
+       data = na_server_invoke_elem (host->srv, host->cfg_volume_perf->query);
+       if (na_results_status (data) != NA_OK)
+       {
+               ERROR ("netapp plugin: cna_query_volume_perf: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
+               na_elem_free (data);
+               return (-1);
+       }
+
+       status = cna_handle_volume_perf_data (host->name, host->cfg_volume_perf, data);
+
+       if (status == 0)
+               host->cfg_volume_perf->interval.last_read = now;
+
+       na_elem_free (data);
+       return (status);
+} /* }}} int cna_query_volume_perf */
+
+/* Data corresponding to <VolumeUsage /> */
+static int cna_submit_volume_usage_data (const char *hostname, /* {{{ */
+               cfg_volume_usage_t *cfg_volume)
+{
+       data_volume_usage_t *v;
+
+       for (v = cfg_volume->volumes; v != NULL; v = v->next)
+       {
+               char plugin_instance[DATA_MAX_NAME_LEN];
+
+               uint64_t norm_used = v->norm_used;
+               uint64_t norm_free = v->norm_free;
+               uint64_t sis_saved = v->sis_saved;
+               uint64_t snap_reserve_used = 0;
+               uint64_t snap_reserve_free = v->snap_reserved;
+               uint64_t snap_norm_used = v->snap_used;
+
+               ssnprintf (plugin_instance, sizeof (plugin_instance),
+                               "volume-%s", v->name);
+
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED | HAVE_VOLUME_USAGE_SNAP_RSVD)) {
+                       if (v->snap_reserved > v->snap_used) {
+                               snap_reserve_free = v->snap_reserved - v->snap_used;
+                               snap_reserve_used = v->snap_used;
+                               snap_norm_used = 0;
+                       } else {
+                               snap_reserve_free = 0;
+                               snap_reserve_used = v->snap_reserved;
+                               snap_norm_used = v->snap_used - v->snap_reserved;
+                       }
+               }
+
+               /* The space used by snapshots but not reserved for them is included in
+                * both, norm_used and snap_norm_used. If possible, subtract this here. */
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_USED | HAVE_VOLUME_USAGE_SNAP_USED))
+               {
+                       if (norm_used >= snap_norm_used)
+                               norm_used -= snap_norm_used;
+                       else
+                       {
+                               ERROR ("netapp plugin: (norm_used = %"PRIu64") < (snap_norm_used = "
+                                               "%"PRIu64") for host %s. Invalidating both.",
+                                               norm_used, snap_norm_used, hostname);
+                               v->flags &= ~(HAVE_VOLUME_USAGE_NORM_USED | HAVE_VOLUME_USAGE_SNAP_USED);
+                       }
+               }
+
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_FREE))
+                       submit_double (hostname, /* plugin instance = */ plugin_instance,
+                                       "df_complex", "free",
+                                       (double) norm_free, /* timestamp = */ 0);
+
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SIS_SAVED))
+                       submit_double (hostname, /* plugin instance = */ plugin_instance,
+                                       "df_complex", "sis_saved",
+                                       (double) sis_saved, /* timestamp = */ 0);
+
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_USED))
+                       submit_double (hostname, /* plugin instance = */ plugin_instance,
+                                       "df_complex", "used",
+                                       (double) norm_used, /* timestamp = */ 0);
+
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_RSVD))
+                       submit_double (hostname, /* plugin instance = */ plugin_instance,
+                                       "df_complex", "snap_reserved",
+                                       (double) snap_reserve_free, /* timestamp = */ 0);
+
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED | HAVE_VOLUME_USAGE_SNAP_RSVD))
+                       submit_double (hostname, /* plugin instance = */ plugin_instance,
+                                       "df_complex", "snap_reserve_used",
+                                       (double) snap_reserve_used, /* timestamp = */ 0);
+
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED))
+                       submit_double (hostname, /* plugin instance = */ plugin_instance,
+                                       "df_complex", "snap_normal_used",
+                                       (double) snap_norm_used, /* timestamp = */ 0);
+
+               /* Clear all the HAVE_* flags */
+               v->flags &= ~HAVE_VOLUME_USAGE_ALL;
+       } /* for (v = cfg_volume->volumes) */
+
+       return (0);
+} /* }}} int cna_submit_volume_usage_data */
+
+/* Switch the state of a volume between online and offline and send out a
+ * notification. */
+static int cna_change_volume_status (const char *hostname, /* {{{ */
+               data_volume_usage_t *v)
+{
+       notification_t n;
+
+       memset (&n, 0, sizeof (&n));
+       n.time = time (NULL);
+       sstrncpy (n.host, hostname, sizeof (n.host));
+       sstrncpy (n.plugin, "netapp", sizeof (n.plugin));
+       sstrncpy (n.plugin_instance, v->name, sizeof (n.plugin_instance));
+
+       if ((v->flags & IS_VOLUME_USAGE_OFFLINE) != 0) {
+               n.severity = NOTIF_OKAY;
+               ssnprintf (n.message, sizeof (n.message),
+                               "Volume %s is now online.", v->name);
+               v->flags &= ~IS_VOLUME_USAGE_OFFLINE;
+       } else {
+               n.severity = NOTIF_WARNING;
+               ssnprintf (n.message, sizeof (n.message),
+                               "Volume %s is now offline.", v->name);
+               v->flags |= IS_VOLUME_USAGE_OFFLINE;
+       }
+
+       return (plugin_dispatch_notification (&n));
+} /* }}} int cna_change_volume_status */
+
+static void cna_handle_volume_snap_usage(const host_config_t *host, /* {{{ */
+               data_volume_usage_t *v)
+{
+       uint64_t snap_used = 0, value;
+       na_elem_t *data, *elem_snap, *elem_snapshots;
+       na_elem_iter_t iter_snap;
+
+       data = na_server_invoke_elem(host->srv, v->snap_query);
+       if (na_results_status(data) != NA_OK)
+       {
+               if (na_results_errno(data) == EVOLUMEOFFLINE) {
+                       if ((v->flags & IS_VOLUME_USAGE_OFFLINE) == 0)
+                               cna_change_volume_status (host->name, v);
+               } else {
+                       ERROR ("netapp plugin: cna_handle_volume_snap_usage: na_server_invoke_elem for "
+                                       "volume \"%s\" on host %s failed with error %d: %s", v->name,
+                                       host->name, na_results_errno(data), na_results_reason(data));
+               }
+               na_elem_free(data);
+               return;
+       }
+
+       if ((v->flags & IS_VOLUME_USAGE_OFFLINE) != 0)
+               cna_change_volume_status (host->name, v);
+
+       elem_snapshots = na_elem_child (data, "snapshots");
+       if (elem_snapshots == NULL)
+       {
+               ERROR ("netapp plugin: cna_handle_volume_snap_usage: "
+                               "na_elem_child (\"snapshots\") failed "
+                               "for host %s.", hostname);
+               na_elem_free(data);
+               return;
+       }
+
+       iter_snap = na_child_iterator (elem_snapshots);
+       for (elem_snap = na_iterator_next (&iter_snap);
+                       elem_snap != NULL;
+                       elem_snap = na_iterator_next (&iter_snap))
+       {
+               value = na_child_get_uint64(elem_snap, "cumulative-total", 0);
+               /* "cumulative-total" is the total size of the oldest snapshot plus all
+                * newer ones in blocks (1KB). We therefore are looking for the highest
+                * number of all snapshots - that's the size required for the snapshots. */
+               if (value > snap_used)
+                       snap_used = value;
+       }
+       na_elem_free (data);
+       /* snap_used is in 1024 byte blocks */
+       v->snap_used = snap_used * 1024;
+       v->flags |= HAVE_VOLUME_USAGE_SNAP_USED;
+} /* }}} void cna_handle_volume_snap_usage */
+
+static int cna_handle_volume_usage_data (const host_config_t *host, /* {{{ */
+               cfg_volume_usage_t *cfg_volume, na_elem_t *data)
+{
+       na_elem_t *elem_volume;
+       na_elem_t *elem_volumes;
+       na_elem_iter_t iter_volume;
+
+       elem_volumes = na_elem_child (data, "volumes");
+       if (elem_volumes == NULL)
+       {
+               ERROR ("netapp plugin: cna_handle_volume_usage_data: "
+                               "na_elem_child (\"volumes\") failed "
+                               "for host %s.", hostname);
+               return (-1);
+       }
+
+       iter_volume = na_child_iterator (elem_volumes);
+       for (elem_volume = na_iterator_next (&iter_volume);
+                       elem_volume != NULL;
+                       elem_volume = na_iterator_next (&iter_volume))
+       {
+               const char *volume_name, *state;
+
+               data_volume_usage_t *v;
+               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)
+                       continue;
+
+               state = na_child_get_string (elem_volume, "state");
+               if ((state == NULL) || (strcmp(state, "online") != 0))
+                       continue;
+
+               /* get_volume_usage may return NULL if the volume is to be ignored. */
+               v = get_volume_usage (cfg_volume, volume_name);
+               if (v == NULL)
+                       continue;
+
+               if ((v->flags & CFG_VOLUME_USAGE_SNAP) != 0)
+                       cna_handle_volume_snap_usage(host, v);
+               
+               if ((v->flags & CFG_VOLUME_USAGE_DF) == 0)
+                       continue;
+
+               /* 2^4 exa-bytes? This will take a while ;) */
+               value = na_child_get_uint64(elem_volume, "size-available", UINT64_MAX);
+               if (value != UINT64_MAX) {
+                       v->norm_free = value;
+                       v->flags |= HAVE_VOLUME_USAGE_NORM_FREE;
+               }
+
+               value = na_child_get_uint64(elem_volume, "size-used", UINT64_MAX);
+               if (value != UINT64_MAX) {
+                       v->norm_used = value;
+                       v->flags |= HAVE_VOLUME_USAGE_NORM_USED;
+               }
+
+               value = na_child_get_uint64(elem_volume, "snapshot-blocks-reserved", UINT64_MAX);
+               if (value != UINT64_MAX) {
+                       /* 1 block == 1024 bytes  as per API docs */
+                       v->snap_reserved = 1024 * value;
+                       v->flags |= HAVE_VOLUME_USAGE_SNAP_RSVD;
+               }
+
+               sis = na_elem_child(elem_volume, "sis");
+               if (sis == NULL)
+                       continue;
+
+               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 */
+       } /* for (elem_volume) */
+
+       return (cna_submit_volume_usage_data (host->name, cfg_volume));
+} /* }}} int cna_handle_volume_usage_data */
+
+static int cna_setup_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */
+{
+       if (cvu == NULL)
+               return (EINVAL);
+
+       if (cvu->query != NULL)
+               return (0);
+
+       cvu->query = na_elem_new ("volume-list-info");
+       if (cvu->query == NULL)
+       {
+               ERROR ("netapp plugin: na_elem_new failed.");
+               return (-1);
+       }
+
+       return (0);
+} /* }}} int cna_setup_volume_usage */
+
+static int cna_query_volume_usage (host_config_t *host) /* {{{ */
+{
+       na_elem_t *data;
+       int status;
+       time_t now;
+
+       if (host == NULL)
+               return (EINVAL);
+
+       /* If the user did not configure volume_usage statistics, return without
+        * doing anything. */
+       if (host->cfg_volume_usage == NULL)
+               return (0);
+
+       now = time (NULL);
+       if ((host->cfg_volume_usage->interval.interval + host->cfg_volume_usage->interval.last_read) > now)
+               return (0);
+
+       status = cna_setup_volume_usage (host->cfg_volume_usage);
+       if (status != 0)
+               return (status);
+       assert (host->cfg_volume_usage->query != NULL);
+
+       data = na_server_invoke_elem(host->srv, host->cfg_volume_usage->query);
+       if (na_results_status (data) != NA_OK)
+       {
+               ERROR ("netapp plugin: cna_query_volume_usage: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
+               na_elem_free (data);
+               return (-1);
+       }
+
+       status = cna_handle_volume_usage_data (host, host->cfg_volume_usage, data);
+
+       if (status == 0)
+               host->cfg_volume_usage->interval.last_read = now;
+
+       na_elem_free (data);
+       return (status);
+} /* }}} int cna_query_volume_usage */
+
+/* Data corresponding to <System /> */
+static int cna_handle_system_data (const char *hostname, /* {{{ */
+               cfg_system_t *cfg_system, na_elem_t *data)
+{
+       na_elem_t *instances;
+       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;
+       uint32_t counter_flags = 0;
+
+       const char *instance;
+       time_t timestamp;
+       
+       timestamp = (time_t) na_child_get_uint64 (data, "timestamp", 0);
+
+       instances = na_elem_child(na_elem_child (data, "instances"), "instance-data");
+       if (instances == NULL)
+       {
+               ERROR ("netapp plugin: cna_handle_system_data: "
+                               "na_elem_child (\"instances\") failed "
+                               "for host %s.", hostname);
+               return (-1);
+       }
+
+       instance = na_child_get_string (instances, "name");
+       if (instance == NULL)
+       {
+               ERROR ("netapp plugin: cna_handle_system_data: "
+                               "na_child_get_string (\"name\") failed "
+                               "for host %s.", hostname);
+               return (-1);
+       }
+
+       counter_iter = na_child_iterator (na_elem_child (instances, "counters"));
+       for (counter = na_iterator_next (&counter_iter);
+                       counter != NULL;
+                       counter = na_iterator_next (&counter_iter))
+       {
+               const char *name;
+               uint64_t value;
+
+               name = na_child_get_string(counter, "name");
+               if (name == NULL)
+                       continue;
+
+               value = na_child_get_uint64(counter, "value", UINT64_MAX);
+               if (value == UINT64_MAX)
+                       continue;
+
+               if (!strcmp(name, "disk_data_read")) {
+                       disk_read = (counter_t) (value * 1024);
+                       counter_flags |= 0x01;
+               } else if (!strcmp(name, "disk_data_written")) {
+                       disk_written = (counter_t) (value * 1024);
+                       counter_flags |= 0x02;
+               } else if (!strcmp(name, "net_data_recv")) {
+                       net_recv = (counter_t) (value * 1024);
+                       counter_flags |= 0x04;
+               } else if (!strcmp(name, "net_data_sent")) {
+                       net_sent = (counter_t) (value * 1024);
+                       counter_flags |= 0x08;
+               } else if (!strcmp(name, "cpu_busy")) {
+                       cpu_busy = (counter_t) value;
+                       counter_flags |= 0x10;
+               } else if (!strcmp(name, "cpu_elapsed_time")) {
+                       cpu_total = (counter_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);
+               }
+       } /* for (counter) */
+
+       if ((cfg_system->flags & CFG_SYSTEM_DISK)
+                       && (HAS_ALL_FLAGS (counter_flags, 0x01 | 0x02)))
+               submit_two_counters (hostname, instance, "disk_octets", NULL,
+                               disk_read, disk_written, timestamp);
+                               
+       if ((cfg_system->flags & CFG_SYSTEM_NET)
+                       && (HAS_ALL_FLAGS (counter_flags, 0x04 | 0x08)))
+               submit_two_counters (hostname, instance, "if_octets", NULL,
+                               net_recv, net_sent, timestamp);
+
+       if ((cfg_system->flags & CFG_SYSTEM_CPU)
+                       && (HAS_ALL_FLAGS (counter_flags, 0x10 | 0x20)))
+       {
+               submit_counter (hostname, instance, "cpu", "system",
+                               cpu_busy, timestamp);
+               submit_counter (hostname, instance, "cpu", "idle",
+                               cpu_total - cpu_busy, timestamp);
+       }
+
+       return (0);
+} /* }}} int cna_handle_system_data */
+
+static int cna_setup_system (cfg_system_t *cs) /* {{{ */
+{
+       if (cs == NULL)
+               return (EINVAL);
+
+       if (cs->query != NULL)
+               return (0);
+
+       cs->query = na_elem_new ("perf-object-get-instances");
+       if (cs->query == NULL)
+       {
+               ERROR ("netapp plugin: na_elem_new failed.");
+               return (-1);
+       }
+       na_child_add_string (cs->query, "objectname", "system");
+
+       return (0);
+} /* }}} int cna_setup_system */
+
+static int cna_query_system (host_config_t *host) /* {{{ */
+{
+       na_elem_t *data;
+       int status;
+       time_t now;
+
+       if (host == NULL)
+               return (EINVAL);
+
+       /* If system statistics were not configured, return without doing anything. */
+       if (host->cfg_system == NULL)
+               return (0);
+
+       now = time (NULL);
+       if ((host->cfg_system->interval.interval + host->cfg_system->interval.last_read) > now)
+               return (0);
+
+       status = cna_setup_system (host->cfg_system);
+       if (status != 0)
+               return (status);
+       assert (host->cfg_system->query != NULL);
+
+       data = na_server_invoke_elem(host->srv, host->cfg_system->query);
+       if (na_results_status (data) != NA_OK)
+       {
+               ERROR ("netapp plugin: cna_query_system: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
+               na_elem_free (data);
+               return (-1);
+       }
+
+       status = cna_handle_system_data (host->name, host->cfg_system, data);
+
+       if (status == 0)
+               host->cfg_system->interval.last_read = now;
+
+       na_elem_free (data);
+       return (status);
+} /* }}} int cna_query_system */
+
+/*
+ * Configuration handling
+ */
+/* Sets a given flag if the boolean argument is true and unsets the flag if it
+ * is false. On error, the flag-field is not changed. */
+static int cna_config_bool_to_flag (const oconfig_item_t *ci, /* {{{ */
+               uint32_t *flags, uint32_t flag)
+{
+       if ((ci == NULL) || (flags == NULL))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+       {
+               WARNING ("netapp plugin: The %s option needs exactly one boolean argument.",
+                               ci->key);
+               return (-1);
+       }
+
+       if (ci->values[0].value.boolean)
+               *flags |= flag;
+       else
+               *flags &= ~flag;
+
+       return (0);
+} /* }}} int cna_config_bool_to_flag */
+
+/* Handling of the "Interval" option which is allowed in every block. */
+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);
+       }
+
+       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);
+       }
+
+       out_interval->interval = tmp;
+       out_interval->last_read = 0;
+
+       return (0);
+} /* }}} int cna_config_get_interval */
+
+/* Handling of the "GetIO", "GetOps" and "GetLatency" options within a
+ * <VolumePerf /> block. */
+static void cna_config_volume_perf_option (cfg_volume_perf_t *cvp, /* {{{ */
+               const oconfig_item_t *ci)
+{
+       char *name;
+       ignorelist_t * il;
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               WARNING ("netapp plugin: The %s option requires exactly one string argument.",
+                               ci->key);
+               return;
+       }
+
+       name = ci->values[0].value.string;
+
+       if (strcasecmp ("GetIO", ci->key) == 0)
+               il = cvp->il_octets;
+       else if (strcasecmp ("GetOps", ci->key) == 0)
+               il = cvp->il_operations;
+       else if (strcasecmp ("GetLatency", ci->key) == 0)
+               il = cvp->il_latency;
+       else
+               return;
+
+       ignorelist_add (il, name);
+} /* }}} void cna_config_volume_perf_option */
+
+/* Handling of the "IgnoreSelectedIO", "IgnoreSelectedOps" and
+ * "IgnoreSelectedLatency" options within a <VolumePerf /> block. */
+static void cna_config_volume_perf_default (cfg_volume_perf_t *cvp, /* {{{ */
+               const oconfig_item_t *ci)
+{
+       ignorelist_t *il;
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+       {
+               WARNING ("netapp plugin: The %s option requires exactly one string argument.",
+                               ci->key);
+               return;
+       }
+
+       if (strcasecmp ("IgnoreSelectedIO", ci->key) == 0)
+               il = cvp->il_octets;
+       else if (strcasecmp ("IgnoreSelectedOps", ci->key) == 0)
+               il = cvp->il_operations;
+       else if (strcasecmp ("IgnoreSelectedLatency", ci->key) == 0)
+               il = cvp->il_latency;
+       else
+               return;
+
+       if (ci->values[0].value.boolean)
+               ignorelist_set_invert (il, /* invert = */ 0);
+       else
+               ignorelist_set_invert (il, /* invert = */ 1);
+} /* }}} void cna_config_volume_perf_default */
+
+/* Corresponds to a <Disks /> block */
+/*
+ * <VolumePerf>
+ *   GetIO "vol0"
+ *   GetIO "vol1"
+ *   IgnoreSelectedIO false
+ *
+ *   GetOps "vol0"
+ *   GetOps "vol2"
+ *   IgnoreSelectedOps false
+ *
+ *   GetLatency "vol2"
+ *   GetLatency "vol3"
+ *   IgnoreSelectedLatency false
+ * </VolumePerf>
+ */
+/* Corresponds to a <VolumePerf /> block */
+static int cna_config_volume_performance (host_config_t *host, /* {{{ */
+               const oconfig_item_t *ci)
+{
+       cfg_volume_perf_t *cfg_volume_perf;
+       int i;
+
+       if ((host == NULL) || (ci == NULL))
+               return (EINVAL);
+
+       if (host->cfg_volume_perf == NULL)
+       {
+               cfg_volume_perf = malloc (sizeof (*cfg_volume_perf));
+               if (cfg_volume_perf == NULL)
+                       return (ENOMEM);
+               memset (cfg_volume_perf, 0, sizeof (*cfg_volume_perf));
+
+               /* Set default flags */
+               cfg_volume_perf->query = NULL;
+               cfg_volume_perf->volumes = NULL;
+
+               cfg_volume_perf->il_octets = ignorelist_create (/* invert = */ 1);
+               if (cfg_volume_perf->il_octets == NULL)
+               {
+                       sfree (cfg_volume_perf);
+                       return (ENOMEM);
+               }
+
+               cfg_volume_perf->il_operations = ignorelist_create (/* invert = */ 1);
+               if (cfg_volume_perf->il_operations == NULL)
+               {
+                       ignorelist_free (cfg_volume_perf->il_octets);
+                       sfree (cfg_volume_perf);
+                       return (ENOMEM);
+               }
+
+               cfg_volume_perf->il_latency = ignorelist_create (/* invert = */ 1);
+               if (cfg_volume_perf->il_latency == NULL)
+               {
+                       ignorelist_free (cfg_volume_perf->il_octets);
+                       ignorelist_free (cfg_volume_perf->il_operations);
+                       sfree (cfg_volume_perf);
+                       return (ENOMEM);
+               }
+
+               host->cfg_volume_perf = cfg_volume_perf;
+       }
+       cfg_volume_perf = host->cfg_volume_perf;
+       
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *item = ci->children + i;
+               
+               /* if (!item || !item->key || !*item->key) continue; */
+               if (strcasecmp(item->key, "Interval") == 0)
+                       cna_config_get_interval (item, &cfg_volume_perf->interval);
+               else if (!strcasecmp(item->key, "GetIO"))
+                       cna_config_volume_perf_option (cfg_volume_perf, item);
+               else if (!strcasecmp(item->key, "GetOps"))
+                       cna_config_volume_perf_option (cfg_volume_perf, item);
+               else if (!strcasecmp(item->key, "GetLatency"))
+                       cna_config_volume_perf_option (cfg_volume_perf, item);
+               else if (!strcasecmp(item->key, "IgnoreSelectedIO"))
+                       cna_config_volume_perf_default (cfg_volume_perf, item);
+               else if (!strcasecmp(item->key, "IgnoreSelectedOps"))
+                       cna_config_volume_perf_default (cfg_volume_perf, item);
+               else if (!strcasecmp(item->key, "IgnoreSelectedLatency"))
+                       cna_config_volume_perf_default (cfg_volume_perf, item);
+               else
+                       WARNING ("netapp plugin: The option %s is not allowed within "
+                                       "`VolumePerf' blocks.", item->key);
+       }
+
+       return (0);
+} /* }}} int cna_config_volume_performance */
+
+/* Handling of the "GetCapacity" and "GetSnapshot" options within a
+ * <VolumeUsage /> block. */
+static void cna_config_volume_usage_option (cfg_volume_usage_t *cvu, /* {{{ */
+               const oconfig_item_t *ci)
+{
+       char *name;
+       ignorelist_t * il;
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               WARNING ("netapp plugin: The %s option requires exactly one string argument.",
+                               ci->key);
+               return;
+       }
+
+       name = ci->values[0].value.string;
+
+       if (strcasecmp ("GetCapacity", ci->key) == 0)
+               il = cvu->il_capacity;
+       else if (strcasecmp ("GetSnapshot", ci->key) == 0)
+               il = cvu->il_snapshot;
+       else
+               return;
+
+       ignorelist_add (il, name);
+} /* }}} void cna_config_volume_usage_option */
+
+/* Handling of the "IgnoreSelectedCapacity" and "IgnoreSelectedSnapshot"
+ * options within a <VolumeUsage /> block. */
+static void cna_config_volume_usage_default (cfg_volume_usage_t *cvu, /* {{{ */
+               const oconfig_item_t *ci)
+{
+       ignorelist_t *il;
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+       {
+               WARNING ("netapp plugin: The %s option requires exactly one string argument.",
+                               ci->key);
+               return;
+       }
+
+       if (strcasecmp ("IgnoreSelectedCapacity", ci->key) == 0)
+               il = cvu->il_capacity;
+       else if (strcasecmp ("IgnoreSelectedSnapshot", ci->key) == 0)
+               il = cvu->il_snapshot;
+       else
+               return;
+
+       if (ci->values[0].value.boolean)
+               ignorelist_set_invert (il, /* invert = */ 0);
+       else
+               ignorelist_set_invert (il, /* invert = */ 1);
+} /* }}} void cna_config_volume_usage_default */
+
+/* Corresponds to a <Disks /> block */
+static int cna_config_disk(host_config_t *host, oconfig_item_t *ci) { /* {{{ */
+       cfg_disk_t *cfg_disk;
+       int i;
+
+       if ((host == NULL) || (ci == NULL))
+               return (EINVAL);
+
+       if (host->cfg_disk == NULL)
+       {
+               cfg_disk = malloc (sizeof (*cfg_disk));
+               if (cfg_disk == NULL)
+                       return (ENOMEM);
+               memset (cfg_disk, 0, sizeof (*cfg_disk));
+
+               /* Set default flags */
+               cfg_disk->flags = CFG_DISK_ALL;
+               cfg_disk->query = NULL;
+               cfg_disk->disks = NULL;
+
+               host->cfg_disk = cfg_disk;
+       }
+       cfg_disk = host->cfg_disk;
+       
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *item = ci->children + i;
+               
+               /* if (!item || !item->key || !*item->key) continue; */
+               if (strcasecmp(item->key, "Interval") == 0)
+                       cna_config_get_interval (item, &cfg_disk->interval);
+               else if (strcasecmp(item->key, "GetBusy") == 0)
+                       cna_config_bool_to_flag (item, &cfg_disk->flags, CFG_DISK_BUSIEST);
+       }
+
+       if ((cfg_disk->flags & CFG_DISK_ALL) == 0)
+       {
+               NOTICE ("netapp plugin: All disk related values have been disabled. "
+                               "Collection of per-disk data will be disabled entirely.");
+               free_cfg_disk (host->cfg_disk);
+               host->cfg_disk = NULL;
+       }
+
+       return (0);
+} /* }}} int cna_config_disk */
+
+/* Corresponds to a <WAFL /> block */
+static int cna_config_wafl(host_config_t *host, oconfig_item_t *ci) /* {{{ */
+{
+       cfg_wafl_t *cfg_wafl;
+       int i;
+
+       if ((host == NULL) || (ci == NULL))
+               return (EINVAL);
+
+       if (host->cfg_wafl == NULL)
+       {
+               cfg_wafl = malloc (sizeof (*cfg_wafl));
+               if (cfg_wafl == NULL)
+                       return (ENOMEM);
+               memset (cfg_wafl, 0, sizeof (*cfg_wafl));
+
+               /* Set default flags */
+               cfg_wafl->flags = CFG_WAFL_ALL;
+
+               host->cfg_wafl = cfg_wafl;
+       }
+       cfg_wafl = host->cfg_wafl;
+
+       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_wafl->interval);
+               else if (!strcasecmp(item->key, "GetNameCache"))
+                       cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_NAME_CACHE);
+               else if (!strcasecmp(item->key, "GetDirCache"))
+                       cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_DIR_CACHE);
+               else if (!strcasecmp(item->key, "GetBufferCache"))
+                       cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_BUF_CACHE);
+               else if (!strcasecmp(item->key, "GetInodeCache"))
+                       cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_INODE_CACHE);
+               else
+                       WARNING ("netapp plugin: The %s config option is not allowed within "
+                                       "`WAFL' blocks.", item->key);
+       }
+
+       if ((cfg_wafl->flags & CFG_WAFL_ALL) == 0)
+       {
+               NOTICE ("netapp plugin: All WAFL related values have been disabled. "
+                               "Collection of WAFL data will be disabled entirely.");
+               free_cfg_wafl (host->cfg_wafl);
+               host->cfg_wafl = NULL;
+       }
+
+       return (0);
+} /* }}} int cna_config_wafl */
+
+/*
+ * <VolumeUsage>
+ *   GetCapacity "vol0"
+ *   GetCapacity "vol1"
+ *   GetCapacity "vol2"
+ *   GetCapacity "vol3"
+ *   GetCapacity "vol4"
+ *   IgnoreSelectedCapacity false
+ *
+ *   GetSnapshot "vol0"
+ *   GetSnapshot "vol3"
+ *   GetSnapshot "vol4"
+ *   GetSnapshot "vol7"
+ *   IgnoreSelectedSnapshot false
+ * </VolumeUsage>
+ */
+/* Corresponds to a <VolumeUsage /> block */
+static int cna_config_volume_usage(host_config_t *host, /* {{{ */
+               const oconfig_item_t *ci)
+{
+       cfg_volume_usage_t *cfg_volume_usage;
+       int i;
+
+       if ((host == NULL) || (ci == NULL))
+               return (EINVAL);
+
+       if (host->cfg_volume_usage == NULL)
+       {
+               cfg_volume_usage = malloc (sizeof (*cfg_volume_usage));
+               if (cfg_volume_usage == NULL)
+                       return (ENOMEM);
+               memset (cfg_volume_usage, 0, sizeof (*cfg_volume_usage));
+
+               /* Set default flags */
+               cfg_volume_usage->query = NULL;
+               cfg_volume_usage->volumes = NULL;
+
+               cfg_volume_usage->il_capacity = ignorelist_create (/* invert = */ 1);
+               if (cfg_volume_usage->il_capacity == NULL)
+               {
+                       sfree (cfg_volume_usage);
+                       return (ENOMEM);
+               }
+
+               cfg_volume_usage->il_snapshot = ignorelist_create (/* invert = */ 1);
+               if (cfg_volume_usage->il_snapshot == NULL)
+               {
+                       ignorelist_free (cfg_volume_usage->il_capacity);
+                       sfree (cfg_volume_usage);
+                       return (ENOMEM);
+               }
+
+               host->cfg_volume_usage = cfg_volume_usage;
+       }
+       cfg_volume_usage = host->cfg_volume_usage;
+       
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *item = ci->children + i;
+               
+               /* if (!item || !item->key || !*item->key) continue; */
+               if (strcasecmp(item->key, "Interval") == 0)
+                       cna_config_get_interval (item, &cfg_volume_usage->interval);
+               else if (!strcasecmp(item->key, "GetCapacity"))
+                       cna_config_volume_usage_option (cfg_volume_usage, item);
+               else if (!strcasecmp(item->key, "GetSnapshot"))
+                       cna_config_volume_usage_option (cfg_volume_usage, item);
+               else if (!strcasecmp(item->key, "IgnoreSelectedCapacity"))
+                       cna_config_volume_usage_default (cfg_volume_usage, item);
+               else if (!strcasecmp(item->key, "IgnoreSelectedSnapshot"))
+                       cna_config_volume_usage_default (cfg_volume_usage, item);
+               else
+                       WARNING ("netapp plugin: The option %s is not allowed within "
+                                       "`VolumeUsage' blocks.", item->key);
+       }
+
+       return (0);
+} /* }}} int cna_config_volume_usage */
+
+/* Corresponds to a <System /> block */
+static int cna_config_system (host_config_t *host, /* {{{ */
+               oconfig_item_t *ci)
+{
+       cfg_system_t *cfg_system;
+       int i;
+       
+       if ((host == NULL) || (ci == NULL))
+               return (EINVAL);
+
+       if (host->cfg_system == NULL)
+       {
+               cfg_system = malloc (sizeof (*cfg_system));
+               if (cfg_system == NULL)
+                       return (ENOMEM);
+               memset (cfg_system, 0, sizeof (*cfg_system));
+
+               /* Set default flags */
+               cfg_system->flags = CFG_SYSTEM_ALL;
+               cfg_system->query = NULL;
+
+               host->cfg_system = cfg_system;
+       }
+       cfg_system = host->cfg_system;
+
+       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_system->interval);
+               } else if (!strcasecmp(item->key, "GetCPULoad")) {
+                       cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_CPU);
+               } else if (!strcasecmp(item->key, "GetInterfaces")) {
+                       cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_NET);
+               } else if (!strcasecmp(item->key, "GetDiskOps")) {
+                       cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_OPS);
+               } else if (!strcasecmp(item->key, "GetDiskIO")) {
+                       cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_DISK);
+               } else {
+                       WARNING ("netapp plugin: The %s config option is not allowed within "
+                                       "`System' blocks.", item->key);
+               }
+       }
+
+       if ((cfg_system->flags & CFG_SYSTEM_ALL) == 0)
+       {
+               NOTICE ("netapp plugin: All system related values have been disabled. "
+                               "Collection of system data will be disabled entirely.");
+               free_cfg_system (host->cfg_system);
+               host->cfg_system = NULL;
+       }
+
+       return (0);
+} /* }}} int cna_config_system */
+
+/* Corresponds to a <Host /> block. */
+static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
+{
+       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));
+       memset (host, 0, sizeof (*host));
+       host->name = NULL;
+       host->protocol = NA_SERVER_TRANSPORT_HTTPS;
+       host->host = NULL;
+       host->username = NULL;
+       host->password = NULL;
+       host->srv = NULL;
+       host->cfg_wafl = NULL;
+       host->cfg_disk = NULL;
+       host->cfg_volume_perf = NULL;
+       host->cfg_volume_usage = NULL;
+       host->cfg_system = NULL;
+
+       status = cf_util_get_string (ci, &host->name);
+       if (status != 0)
+       {
+               sfree (host);
+               return (NULL);
+       }
+
+       for (i = 0; i < ci->children_num; ++i) {
+               item = ci->children + i;
+
+               status = 0;
+
+               if (!strcasecmp(item->key, "Address")) {
+                       status = cf_util_get_string (item, &host->host);
+               } else if (!strcasecmp(item->key, "Port")) {
+                       int tmp;
+
+                       tmp = cf_util_get_port_number (item);
+                       if (tmp > 0)
+                               host->port = tmp;
+               } 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;
+                       }
+                       if (!strcasecmp(item->values[0].value.string, "http")) host->protocol = NA_SERVER_TRANSPORT_HTTP;
+                       else host->protocol = NA_SERVER_TRANSPORT_HTTPS;
+               } else if (!strcasecmp(item->key, "User")) {
+                       status = cf_util_get_string (item, &host->username);
+               } 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;
+               } else if (!strcasecmp(item->key, "WAFL")) {
+                       cna_config_wafl(host, item);
+               } else if (!strcasecmp(item->key, "Disks")) {
+                       cna_config_disk(host, item);
+               } else if (!strcasecmp(item->key, "VolumePerf")) {
+                       cna_config_volume_performance(host, item);
+               } else if (!strcasecmp(item->key, "VolumeUsage")) {
+                       cna_config_volume_usage(host, item);
+               } else if (!strcasecmp(item->key, "System")) {
+                       cna_config_system(host, item);
+               } else {
+                       WARNING("netapp plugin: Ignoring unknown config option \"%s\" in host block \"%s\".",
+                                       item->key, ci->values[0].value.string);
+               }
+
+               if (status != 0)
+                       break;
+       }
+
+       if (host->host == NULL)
+               host->host = strdup (host->name);
+
+       if (host->host == NULL)
+               status = -1;
+
+       if (host->port <= 0)
+               host->port = (host->protocol == NA_SERVER_TRANSPORT_HTTP) ? 80 : 443;
+
+       if ((host->username == NULL) || (host->password == NULL)) {
+               WARNING("netapp plugin: Please supply login information for host \"%s\". "
+                               "Ignoring host block.", host->name);
+               status = -1;
+       }
+
+       if (status != 0)
+       {
+               free_host_config (host);
+               return (NULL);
+       }
+
+       return host;
+} /* }}} host_config_t *cna_config_host */
+
+/*
+ * Callbacks registered with the daemon
+ *
+ * Pretty standard stuff here.
+ */
+static int cna_init_host (host_config_t *host) /* {{{ */
+{
+       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->srv == NULL) {
+               ERROR ("netapp plugin: na_server_open (%s) failed.", host->host);
+               return (-1);
+       }
+
+       na_server_set_transport_type(host->srv, host->protocol,
+                       /* transportarg = */ NULL);
+       na_server_set_port(host->srv, host->port);
+       na_server_style(host->srv, NA_STYLE_LOGIN_PASSWORD);
+       na_server_adminuser(host->srv, host->username, host->password);
+       na_server_set_timeout(host->srv, 5 /* seconds */);
+
+       return 0;
+} /* }}} int cna_init_host */
+
+static int cna_init (void) /* {{{ */
+{
+       char err[256];
+
+       memset (err, 0, sizeof (err));
+       if (!na_startup(err, sizeof(err))) {
+               err[sizeof (err) - 1] = 0;
+               ERROR("netapp plugin: Error initializing netapp API: %s", err);
+               return 1;
+       }
+
+       return (0);
+} /* }}} cna_init */
+
+static int cna_read (user_data_t *ud) { /* {{{ */
+       host_config_t *host;
+       int status;
+
+       if ((ud == NULL) || (ud->data == NULL))
+               return (-1);
+
+       host = ud->data;
+
+       status = cna_init_host (host);
+       if (status != 0)
+               return (status);
+       
+       cna_query_wafl (host);
+       cna_query_disk (host);
+       cna_query_volume_perf (host);
+       cna_query_volume_usage (host);
+       cna_query_system (host);
+
+       return 0;
+} /* }}} int cna_read */
+
+static int cna_config (oconfig_item_t *ci) { /* {{{ */
+       int i;
+       oconfig_item_t *item;
+
+       for (i = 0; i < ci->children_num; ++i) {
+               item = ci->children + i;
+
+               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)
+                               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;
+
+                       plugin_register_complex_read (cb_name,
+                                       /* callback  = */ cna_read, 
+                                       /* interval  = */ (host->interval > 0) ? &interval : NULL,
+                                       /* user data = */ &ud);
+                       continue;
+               }
+               else /* if (item->key != "Host") */
+               {
+                       WARNING("netapp plugin: Ignoring unknown config option \"%s\".", item->key);
+               }
+       }
+       return 0;
+} /* }}} int cna_config */
+
+static int cna_shutdown (void) /* {{{ */
+{
+       /* Clean up system resources and stuff. */
+       na_shutdown ();
+
+       return (0);
+} /* }}} int cna_shutdown */
+
+void module_register(void) {
+       plugin_register_complex_config("netapp", cna_config);
+       plugin_register_init("netapp", cna_init);
+       plugin_register_shutdown("netapp", cna_shutdown);
+}
+
+/* vim: set sw=2 ts=2 noet fdm=marker : */
index b15768e..49c4e99 100644 (file)
@@ -526,9 +526,7 @@ static int ir_config (const char *key, const char *value)
     }
     else
     {
-      if ((strcasecmp (fields[0], "yes") == 0)
-         || (strcasecmp (fields[0], "true") == 0)
-         || (strcasecmp (fields[0], "on") == 0))
+      if (IS_TRUE (fields[0]))
        ir_ignorelist_invert = 0;
       else
        ir_ignorelist_invert = 1;
index ac69254..8615753 100644 (file)
@@ -256,6 +256,7 @@ typedef struct receive_list_entry_s receive_list_entry_t;
 static int network_config_ttl = 0;
 static size_t network_config_packet_size = 1024;
 static int network_config_forward = 0;
+static int network_config_stats = 0;
 
 static sockent_t *sending_sockets = NULL;
 
@@ -263,6 +264,7 @@ static receive_list_entry_t *receive_list_head = NULL;
 static receive_list_entry_t *receive_list_tail = NULL;
 static pthread_mutex_t       receive_list_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t        receive_list_cond = PTHREAD_COND_INITIALIZER;
+static uint64_t              receive_list_length = 0;
 
 static sockent_t     *listen_sockets = NULL;
 static struct pollfd *listen_sockets_pollfd = NULL;
@@ -283,6 +285,22 @@ static int              send_buffer_fill;
 static value_list_t     send_buffer_vl = VALUE_LIST_STATIC;
 static pthread_mutex_t  send_buffer_lock = PTHREAD_MUTEX_INITIALIZER;
 
+/* XXX: These counters are incremented from one place only. The spot in which
+ * the values are incremented is either only reachable by one thread (the
+ * dispatch thread, for example) or locked by some lock (send_buffer_lock for
+ * 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 pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
+
 /*
  * Private functions
  */
@@ -341,12 +359,13 @@ static int network_dispatch_values (value_list_t *vl) /* {{{ */
   if (!check_receive_okay (vl))
   {
 #if COLLECT_DEBUG
-         char name[6*DATA_MAX_NAME_LEN];
-         FORMAT_VL (name, sizeof (name), vl);
-         name[sizeof (name) - 1] = 0;
-         DEBUG ("network plugin: network_dispatch_values: "
-             "NOT dispatching %s.", name);
+    char name[6*DATA_MAX_NAME_LEN];
+    FORMAT_VL (name, sizeof (name), vl);
+    name[sizeof (name) - 1] = 0;
+    DEBUG ("network plugin: network_dispatch_values: "
+       "NOT dispatching %s.", name);
 #endif
+    stats_values_not_dispatched++;
     return (0);
   }
 
@@ -369,6 +388,7 @@ static int network_dispatch_values (value_list_t *vl) /* {{{ */
   }
 
   plugin_dispatch_values (vl);
+  stats_values_dispatched++;
 
   meta_data_destroy (vl->meta);
   vl->meta = NULL;
@@ -1969,6 +1989,7 @@ static void *dispatch_thread (void __attribute__((unused)) *arg) /* {{{ */
     ent = receive_list_head;
     if (ent != NULL)
       receive_list_head = ent->next;
+    receive_list_length--;
     pthread_mutex_unlock (&receive_list_lock);
 
     /* Check whether we are supposed to exit. We do NOT check `listen_loop'
@@ -2020,11 +2041,13 @@ static int network_receive (void) /* {{{ */
 
        receive_list_entry_t *private_list_head;
        receive_list_entry_t *private_list_tail;
+       uint64_t              private_list_length;
 
         assert (listen_sockets_num > 0);
 
        private_list_head = NULL;
        private_list_tail = NULL;
+       private_list_length = 0;
 
        while (listen_loop == 0)
        {
@@ -2061,6 +2084,9 @@ static int network_receive (void) /* {{{ */
                                return (-1);
                        }
 
+                       stats_octets_rx += ((uint64_t) buffer_len);
+                       stats_packets_rx++;
+
                        /* TODO: Possible performance enhancement: Do not free
                         * these entries in the dispatch thread but put them in
                         * another list, so we don't have to allocate more and
@@ -2075,6 +2101,7 @@ static int network_receive (void) /* {{{ */
                        ent->data = malloc (network_config_packet_size);
                        if (ent->data == NULL)
                        {
+                               sfree (ent);
                                ERROR ("network plugin: malloc failed.");
                                return (-1);
                        }
@@ -2089,22 +2116,28 @@ static int network_receive (void) /* {{{ */
                        else
                                private_list_tail->next = ent;
                        private_list_tail = ent;
+                       private_list_length++;
 
                        /* Do not block here. Blocking here has led to
                         * insufficient performance in the past. */
                        if (pthread_mutex_trylock (&receive_list_lock) == 0)
                        {
+                               assert (((receive_list_head == NULL) && (receive_list_length == 0))
+                                               || ((receive_list_head != NULL) && (receive_list_length != 0)));
+
                                if (receive_list_head == NULL)
                                        receive_list_head = private_list_head;
                                else
                                        receive_list_tail->next = private_list_head;
                                receive_list_tail = private_list_tail;
-
-                               private_list_head = NULL;
-                               private_list_tail = NULL;
+                               receive_list_length += private_list_length;
 
                                pthread_cond_signal (&receive_list_cond);
                                pthread_mutex_unlock (&receive_list_lock);
+
+                               private_list_head = NULL;
+                               private_list_tail = NULL;
+                               private_list_length = 0;
                        }
                } /* for (listen_sockets_pollfd) */
        } /* while (listen_loop == 0) */
@@ -2119,9 +2152,11 @@ static int network_receive (void) /* {{{ */
                else
                        receive_list_tail->next = private_list_head;
                receive_list_tail = private_list_tail;
+               receive_list_length += private_list_length;
 
                private_list_head = NULL;
                private_list_tail = NULL;
+               private_list_length = 0;
 
                pthread_cond_signal (&receive_list_cond);
                pthread_mutex_unlock (&receive_list_lock);
@@ -2430,6 +2465,10 @@ static void flush_buffer (void)
                        send_buffer_fill);
 
        network_send_buffer (send_buffer, (size_t) send_buffer_fill);
+
+       stats_octets_tx += ((uint64_t) send_buffer_fill);
+       stats_packets_tx++;
+
        network_init_buffer ();
 }
 
@@ -2447,6 +2486,11 @@ static int network_write (const data_set_t *ds, const value_list_t *vl,
          DEBUG ("network plugin: network_write: "
              "NOT sending %s.", name);
 #endif
+         /* Counter is not protected by another lock and may be reached by
+          * multiple threads */
+         pthread_mutex_lock (&stats_lock);
+         stats_values_not_sent++;
+         pthread_mutex_unlock (&stats_lock);
          return (0);
        }
 
@@ -2464,6 +2508,8 @@ static int network_write (const data_set_t *ds, const value_list_t *vl,
                /* status == bytes added to the buffer */
                send_buffer_fill += status;
                send_buffer_ptr  += status;
+
+               stats_values_sent++;
        }
        else
        {
@@ -2478,6 +2524,8 @@ static int network_write (const data_set_t *ds, const value_list_t *vl,
                {
                        send_buffer_fill += status;
                        send_buffer_ptr  += status;
+
+                       stats_values_sent++;
                }
        }
 
@@ -2519,13 +2567,9 @@ static int network_config_set_boolean (const oconfig_item_t *ci, /* {{{ */
   {
     char *str = ci->values[0].value.string;
 
-    if ((strcasecmp ("true", str) == 0)
-        || (strcasecmp ("yes", str) == 0)
-        || (strcasecmp ("on", str) == 0))
+    if (IS_TRUE (str))
       *retval = 1;
-    else if ((strcasecmp ("false", str) == 0)
-        || (strcasecmp ("no", str) == 0)
-        || (strcasecmp ("off", str) == 0))
+    else if (IS_FALSE (str))
       *retval = 0;
     else
     {
@@ -2802,6 +2846,8 @@ static int network_config (oconfig_item_t *ci) /* {{{ */
       network_config_set_buffer_size (child);
     else if (strcasecmp ("Forward", child->key) == 0)
       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
@@ -2928,6 +2974,83 @@ static int network_shutdown (void)
        return (0);
 } /* int network_shutdown */
 
+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;
+       value_list_t vl = VALUE_LIST_INIT;
+       value_t values[2];
+
+       copy_octets_rx = stats_octets_rx;
+       copy_octets_tx = stats_octets_tx;
+       copy_packets_rx = stats_packets_rx;
+       copy_packets_tx = stats_packets_tx;
+       copy_values_dispatched = stats_values_dispatched;
+       copy_values_not_dispatched = stats_values_not_dispatched;
+       copy_values_sent = stats_values_sent;
+       copy_values_not_sent = stats_values_not_sent;
+       copy_receive_list_length = receive_list_length;
+
+       /* Initialize `vl' */
+       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;
+       sstrncpy (vl.type, "if_octets", sizeof (vl.type));
+       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;
+       sstrncpy (vl.type, "if_packets", sizeof (vl.type));
+       plugin_dispatch_values (&vl);
+
+       /* Values (not) dispatched and (not) send */
+       sstrncpy (vl.type, "total_values", sizeof (vl.type));
+       vl.values_len = 1;
+
+       vl.values[0].derive = (derive_t) copy_values_dispatched;
+       sstrncpy (vl.type_instance, "dispatch-accepted",
+                       sizeof (vl.type_instance));
+       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 (&vl);
+
+       vl.values[0].derive = (derive_t) copy_values_sent;
+       sstrncpy (vl.type_instance, "send-accepted",
+                       sizeof (vl.type_instance));
+       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 (&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 (&vl);
+
+       return (0);
+} /* }}} int network_stats_read */
+
 static int network_init (void)
 {
        static _Bool have_init = false;
@@ -2944,6 +3067,9 @@ static int network_init (void)
        gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
 #endif
 
+       if (network_config_stats != 0)
+               plugin_register_read ("network", network_stats_read);
+
        plugin_register_shutdown ("network", network_shutdown);
 
        send_buffer = malloc (network_config_packet_size);
index 53137a7..6976842 100644 (file)
@@ -143,7 +143,7 @@ static int init (void)
 
   curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
 
-  if ((verify_peer == NULL) || (strcmp (verify_peer, "true") == 0))
+  if ((verify_peer == NULL) || IS_TRUE (verify_peer))
   {
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1);
   }
@@ -152,7 +152,7 @@ static int init (void)
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0);
   }
 
-  if ((verify_host == NULL) || (strcmp (verify_host, "true") == 0))
+  if ((verify_host == NULL) || IS_TRUE (verify_host))
   {
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2);
   }
index 9d716ad..ecc87c7 100644 (file)
@@ -278,9 +278,7 @@ static int ntpd_config (const char *key, const char *value)
        }
        else if (strcasecmp (key, "ReverseLookups") == 0)
        {
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        do_reverse_lookups = 1;
                else
                        do_reverse_lookups = 0;
index 261457a..cae0d63 100644 (file)
@@ -89,9 +89,7 @@ static int cow_load_config (const char *key, const char *value)
   else if (strcasecmp (key, "IgnoreSelected") == 0)
   {
     ignorelist_set_invert (sensor_list, 1);
-    if ((strcasecmp (value, "True") == 0)
-        || (strcasecmp (value, "Yes") == 0)
-        || (strcasecmp (value, "On") == 0))
+    if (IS_TRUE (value))
       ignorelist_set_invert (sensor_list, 0);
   }
   else if (strcasecmp (key, "Device") == 0)
index 9f386f0..1ac5083 100644 (file)
@@ -2,6 +2,7 @@
  * collectd - src/openvpn.c
  * Copyright (C) 2008  Doug MacEachern
  * Copyright (C) 2009  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
  * Authors:
  *   Doug MacEachern <dougm at hyperic.com>
  *   Florian octo Forster <octo at verplant.org>
+ *   Marco Chiappero <marco at absence.it>
  **/
 
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 
-#define DEFAULT_STATUS_FILE "/etc/openvpn/openvpn-status.log"
-#define CLIENT_LIST_PREFIX  "CLIENT_LIST,"
+#define V1STRING "Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since\n"
+#define V2STRING "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t)\n"
+#define V3STRING "HEADER CLIENT_LIST Common Name Real Address Virtual Address Bytes Received Bytes Sent Connected Since Connected Since (time_t)\n"
+#define VSSTRING "OpenVPN STATISTICS\n"
 
-static char *status_file = NULL;
 
-/* For compression stats we need to convert these counters to a rate. */
-static counter_t pre_compress_old    = 0;
-static counter_t post_compress_old   = 0;
-static counter_t pre_decompress_old  = 0;
-static counter_t post_decompress_old = 0;
-static int compression_counter_valid = 0;
+struct vpn_status_s
+{
+       char *file;
+       enum
+       {
+               MULTI1 = 1, /* status-version 1 */
+               MULTI2,     /* status-version 2 */
+               MULTI3,     /* status-version 3 */
+               SINGLE = 10 /* currently no versions for single mode, maybe in the future */
+       } version;
+       char *name;
+};
+typedef struct vpn_status_s vpn_status_t;
+
+static vpn_status_t **vpn_list = NULL;
+static int vpn_num = 0;
+
+static int store_compression = 1;
+static int new_naming_schema = 0;
 
 static const char *config_keys[] =
 {
-       "StatusFile"
+       "StatusFile",
+       "Compression",
+       "ImprovedNamingSchema"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-/* copy-n-pasted from common.c - changed delim to "," */
+
+/* Helper function
+ * copy-n-pasted from common.c - changed delim to ","  */
 static int openvpn_strsplit (char *string, char **fields, size_t size)
 {
-        size_t i;
-        char *ptr;
-        char *saveptr;
-
-        i = 0;
-        ptr = string;
-        saveptr = NULL;
-        while ((fields[i] = strtok_r (ptr, ",", &saveptr)) != NULL)
-        {
-                ptr = NULL;
-                i++;
-
-                if (i >= size)
-                        break;
-        }
-
-        return (i);
+       size_t i;
+       char *ptr;
+       char *saveptr;
+
+       i = 0;
+       ptr = string;
+       saveptr = NULL;
+       while ((fields[i] = strtok_r (ptr, ",", &saveptr)) != NULL)
+       {
+               ptr = NULL;
+               i++;
+
+               if (i >= size)
+                       break;
+       }
+
+       return (i);
 } /* int openvpn_strsplit */
 
-static void openvpn_submit (char *name, counter_t rx, counter_t tx)
+/* 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)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
@@ -73,163 +94,533 @@ static void openvpn_submit (char *name, counter_t rx, counter_t tx)
        values[0].counter = rx;
        values[1].counter = tx;
 
+       /* NOTE ON THE NEW NAMING SCHEMA:
+        *       using plugin_instance to identify each vpn config (and
+        *       status) file; using type_instance to identify the endpoint
+        *       host when in multimode, traffic or overhead when in single.
+        */
+
        vl.values = values;
        vl.values_len = STATIC_ARRAY_SIZE (values);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "openvpn", sizeof (vl.plugin));
-       sstrncpy (vl.plugin_instance, name, sizeof (vl.plugin_instance));
+       if (pinst != NULL)
+               sstrncpy (vl.plugin_instance, pinst,
+                               sizeof (vl.plugin_instance));
        sstrncpy (vl.type, "if_octets", sizeof (vl.type));
+       if (tinst != NULL)
+               sstrncpy (vl.type_instance, tinst, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
-} /* void openvpn_submit */
+} /* void traffic_submit */
 
-static void compression_submit (char *type_instance, gauge_t ratio)
+/* dispatches stats about data compression shown when in single mode */
+static void compression_submit (char *pinst, char *tinst,
+               counter_t uncompressed, counter_t compressed)
 {
-       value_t values[1];
+       value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].gauge = ratio;
+       values[0].counter = uncompressed;
+       values[1].counter = compressed;
 
        vl.values = values;
        vl.values_len = STATIC_ARRAY_SIZE (values);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "openvpn", sizeof (vl.plugin));
-       sstrncpy (vl.type, "compression_ratio", sizeof (vl.type));
-       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type));
+       if (pinst != NULL)
+               sstrncpy (vl.plugin_instance, pinst,
+                               sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "compression", sizeof (vl.type));
+       if (tinst != NULL)
+               sstrncpy (vl.type_instance, tinst, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
 } /* void compression_submit */
 
-static int openvpn_read (void)
+static int single_read (char *name, FILE *fh)
 {
-       FILE *fh;
        char buffer[1024];
-       char *fields[10];
+       char *fields[4];
        const int max_fields = STATIC_ARRAY_SIZE (fields);
-       int   fields_num;
+       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;
+
+       link_rx = 0;
+       link_tx = 0;
+       tun_rx = 0;
+       tun_tx = 0;
+       pre_compress = 0;
+       post_compress = 0;
+       pre_decompress = 0;
+       post_decompress = 0;
+       overhead_rx = 0;
+       overhead_tx = 0;
 
-       counter_t pre_compress_new    = 0;
-       counter_t post_compress_new   = 0;
-       counter_t pre_decompress_new  = 0;
-       counter_t post_decompress_new = 0;
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               fields_num = openvpn_strsplit (buffer, fields, max_fields);
 
-       /* Clear the least significant four bits, just to make sure all four
-        * counters above are considered to be invalid. */
-       compression_counter_valid &= ~0x0f;
+               /* status file is generated by openvpn/sig.c:print_status()
+                * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/sig.c
+                *
+                * The line we're expecting has 2 fields. We ignore all lines
+                *  with more or less fields.
+                */
+               if (fields_num != 2)
+               {
+                       continue;
+               }
 
-       fh = fopen ((status_file != NULL)
-                       ? status_file
-                       : DEFAULT_STATUS_FILE, "r");
-       if (fh == NULL)
-               return (-1);
+               if (strcmp (fields[0], "TUN/TAP read bytes") == 0)
+               {
+                       /* read from the system and sent over the tunnel */
+                       tun_tx = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "TUN/TAP write bytes") == 0)
+               {
+                       /* read from the tunnel and written in the system */
+                       tun_rx = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "TCP/UDP read bytes") == 0)
+               {
+                       link_rx = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "TCP/UDP write bytes") == 0)
+               {
+                       link_tx = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "pre-compress bytes") == 0)
+               {
+                       pre_compress = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "post-compress bytes") == 0)
+               {
+                       post_compress = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "pre-decompress bytes") == 0)
+               {
+                       pre_decompress = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "post-decompress bytes") == 0)
+               {
+                       post_decompress = atoll (fields[1]);
+               }
+       }
+
+       iostats_submit (name, "traffic", link_rx, link_tx);
+
+       /* we need to force this order to avoid negative values with these unsigned */
+       overhead_rx = (((link_rx - pre_decompress) + post_decompress) - tun_rx);
+       overhead_tx = (((link_tx - post_compress) + pre_compress) - tun_tx);
 
-        /* status file is generated by openvpn/multi.c:multi_print_status()
-        * this plugin requires server.conf: "status-version 2"
-         * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
-         */
+       iostats_submit (name, "overhead", overhead_rx, overhead_tx);
+
+       if (store_compression)
+       {
+               compression_submit (name, "data_in", post_decompress, pre_decompress);
+               compression_submit (name, "data_out", pre_compress, post_compress);
+       }
+
+       read = 1;
+
+       return (read);
+} /* int single_read */
+
+/* for reading status version 1 */
+static int multi1_read (char *name, FILE *fh)
+{
+       char buffer[1024];
+       char *fields[10];
+       int  fields_num, read = 0, found_header = 0;
+
+       /* read the file until the "ROUTING TABLE" line is found (no more info after) */
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
-               fields_num = openvpn_strsplit (buffer, fields, max_fields);
+               if (strcmp (buffer, "ROUTING TABLE\n") == 0)
+                       break;
 
-               /* Expect at least ``key,value''. */
-               if (fields_num < 2)
+               if (strcmp (buffer, V1STRING) == 0)
+               {
+                       found_header = 1;
                        continue;
+               }
 
-               if (strcmp (fields[0], "CLIENT_LIST") == 0)
-               {
-                       char *name;
-                       counter_t rx;
-                       counter_t tx;
+               /* skip the first lines until the client list section is found */
+               if (found_header == 0)
+                       /* we can't start reading data until this string is found */
+                       continue;
 
-                       /* The line we're expecting has 8 fields. We ignore all lines
-                        * with more or less fields. */
-                       if (fields_num != 8)
-                               continue;
+               fields_num = openvpn_strsplit (buffer,
+                               fields, STATIC_ARRAY_SIZE (fields));
+               if (fields_num < 4)
+                       continue;
 
-                       name =      fields[1];  /* "Common Name" */
-                       rx = atoll (fields[4]); /* "Bytes Received */
-                       tx = atoll (fields[5]); /* "Bytes Sent" */
-                       openvpn_submit (name, rx, tx);
+               if (new_naming_schema)
+               {
+                       iostats_submit (fields[0],          /* "Common Name" */
+                                       NULL,               /* unused when in multimode */
+                                       atoll (fields[2]),  /* "Bytes Received" */
+                                       atoll (fields[3])); /* "Bytes Sent" */
                }
-               else if (strcmp (fields[0], "pre-compress") == 0)
+               else
                {
-                       pre_compress_new = atoll (fields[1]);
-                       compression_counter_valid |= 0x01;
+                       iostats_submit (name,               /* vpn instance */
+                                       fields[0],          /* "Common Name" */
+                                       atoll (fields[2]),  /* "Bytes Received" */
+                                       atoll (fields[3])); /* "Bytes Sent" */
                }
-               else if (strcmp (fields[0], "post-compress") == 0)
+
+               read = 1;
+       }
+
+       return (read);
+} /* int multi1_read */
+
+/* for reading status version 2 */
+static int multi2_read (char *name, FILE *fh)
+{
+       char buffer[1024];
+       char *fields[10];
+       const int max_fields = STATIC_ARRAY_SIZE (fields);
+       int  fields_num, read = 0;
+
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               fields_num = openvpn_strsplit (buffer, fields, max_fields);
+
+               /* status file is generated by openvpn/multi.c:multi_print_status()
+                * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
+                *
+                * The line we're expecting has 8 fields. We ignore all lines
+                *  with more or less fields.
+                */
+               if (fields_num != 8)
+                       continue;
+
+               if (strcmp (fields[0], "CLIENT_LIST") != 0)
+                       continue;
+
+               if (new_naming_schema)
                {
-                       post_compress_new = atoll (fields[1]);
-                       compression_counter_valid |= 0x02;
+                       /* plugin inst = file name, type inst = fields[1] */
+                       iostats_submit (name,               /* vpn instance */
+                                       fields[1],          /* "Common Name" */
+                                       atoll (fields[4]),  /* "Bytes Received" */
+                                       atoll (fields[5])); /* "Bytes Sent" */
                }
-               else if (strcmp (fields[0], "pre-decompress") == 0)
+               else
+               {
+                       /* plugin inst = fields[1], type inst = "" */
+                       iostats_submit (fields[1],          /* "Common Name" */
+                                       NULL,               /* unused when in multimode */
+                                       atoll (fields[4]),  /* "Bytes Received" */
+                                       atoll (fields[5])); /* "Bytes Sent" */
+               }
+
+               read = 1;
+       }
+
+       return (read);
+} /* int multi2_read */
+
+/* for reading status version 3 */
+static int multi3_read (char *name, FILE *fh)
+{
+       char buffer[1024];
+       char *fields[15];
+       const int max_fields = STATIC_ARRAY_SIZE (fields);
+       int  fields_num, read = 0;
+
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               fields_num = strsplit (buffer, fields, max_fields);
+
+               /* status file is generated by openvpn/multi.c:multi_print_status()
+                * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
+                *
+                * The line we're expecting has 12 fields. We ignore all lines
+                *  with more or less fields.
+                */
+               if (fields_num != 12)
                {
-                       pre_decompress_new = atoll (fields[1]);
-                       compression_counter_valid |= 0x04;
+                       continue;
                }
-               else if (strcmp (fields[0], "post-decompress") == 0)
+               else
                {
-                       post_decompress_new = atoll (fields[1]);
-                       compression_counter_valid |= 0x08;
+                       if (strcmp (fields[0], "CLIENT_LIST") != 0)
+                               continue;
+
+                       if (new_naming_schema)
+                       {
+                               iostats_submit (name,               /* vpn instance */
+                                               fields[1],          /* "Common Name" */
+                                               atoll (fields[4]),  /* "Bytes Received" */
+                                               atoll (fields[5])); /* "Bytes Sent" */
+                       }
+                       else
+                       {
+                               iostats_submit (fields[1],          /* "Common Name" */
+                                               NULL,               /* unused when in multimode */
+                                               atoll (fields[4]),  /* "Bytes Received" */
+                                               atoll (fields[5])); /* "Bytes Sent" */
+                       }
+
+                       read = 1;
                }
        }
-       fclose (fh);
 
-       /* Check that all four counters are valid, {pre,post}_*_{old,new}. */
-       if ((compression_counter_valid & 0x33) == 0x33)
+       return (read);
+} /* int multi3_read */
+
+/* read callback */
+static int openvpn_read (void)
+{
+       FILE *fh;
+       int  i, read;
+
+       read = 0;
+
+       /* call the right read function for every status entry in the list */
+       for (i = 0; i < vpn_num; i++)
        {
-               counter_t pre_diff;
-               counter_t post_diff;
+               fh = fopen (vpn_list[i]->file, "r");
+               if (fh == NULL)
+               {
+                       char errbuf[1024];
+                       WARNING ("openvpn plugin: fopen(%s) failed: %s", vpn_list[i]->file,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+
+                       continue;
+               }
 
-               pre_diff = counter_diff (pre_compress_old, pre_compress_new);
-               post_diff = counter_diff (post_compress_old, post_compress_new);
+               switch (vpn_list[i]->version)
+               {
+                       case SINGLE:
+                               read = single_read(vpn_list[i]->name, fh);
+                               break;
 
-               /* If we compress, we're sending. */
-               compression_submit ("tx",
-                               ((gauge_t) post_diff) / ((gauge_t) pre_diff));
+                       case MULTI1:
+                               read = multi1_read(vpn_list[i]->name, fh);
+                               break;
+
+                       case MULTI2:
+                               read = multi2_read(vpn_list[i]->name, fh);
+                               break;
+
+                       case MULTI3:
+                               read = multi3_read(vpn_list[i]->name, fh);
+                               break;
+               }
+
+               fclose (fh);
        }
 
-       /* Now check the other found counters. */
-       if ((compression_counter_valid & 0xcc) == 0xcc)
+       return (read ? 0 : -1);
+} /* int openvpn_read */
+
+static int version_detect (const char *filename)
+{
+       FILE *fh;
+       char buffer[1024];
+       int version = 0;
+
+       /* Sanity checking. We're called from the config handling routine, so
+        * better play it save. */
+       if ((filename == NULL) || (*filename == 0))
+               return (0);
+
+       fh = fopen (filename, "r");
+       if (fh == NULL)
        {
-               counter_t pre_diff;
-               counter_t post_diff;
+               char errbuf[1024];
+               WARNING ("openvpn plugin: Unable to read \"%s\": %s", filename,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (0);
+       }
 
-               pre_diff = counter_diff (pre_decompress_old, pre_decompress_new);
-               post_diff = counter_diff (post_decompress_old, post_decompress_new);
+       /* now search for the specific multimode data format */
+       while ((fgets (buffer, sizeof (buffer), fh)) != NULL)
+       {
+               /* we look at the first line searching for SINGLE mode configuration */
+               if (strcmp (buffer, VSSTRING) == 0)
+               {
+                       DEBUG ("openvpn plugin: found status file version SINGLE");
+                       version = SINGLE;
+                       break;
+               }
+               /* searching for multi version 1 */
+               else if (strcmp (buffer, V1STRING) == 0)
+               {
+                       DEBUG ("openvpn plugin: found status file version MULTI1");
+                       version = MULTI1;
+                       break;
+               }
+               /* searching for multi version 2 */
+               else if (strcmp (buffer, V2STRING) == 0)
+               {
+                       DEBUG ("openvpn plugin: found status file version MULTI2");
+                       version = MULTI2;
+                       break;
+               }
+               /* searching for multi version 3 */
+               else if (strcmp (buffer, V3STRING) == 0)
+               {
+                       DEBUG ("openvpn plugin: found status file version MULTI3");
+                       version = MULTI3;
+                       break;
+               }
+       }
 
-               /* If we decompress, we're receiving. */
-               compression_submit ("rx",
-                               ((gauge_t) pre_diff) / ((gauge_t) post_diff));
+       if (version == 0)
+       {
+               /* This is only reached during configuration, so complaining to
+                * the user is in order. */
+               NOTICE ("openvpn plugin: %s: Unknown file format, please "
+                               "report this as bug. Make sure to include "
+                               "your status file, so the plugin can "
+                               "be adapted.", filename);
        }
 
-       /* Now copy all the new counters to the old counters and move the flags
-        * up. */
-       pre_compress_old = pre_compress_new;
-       post_compress_old = post_compress_new;
-       pre_decompress_old = pre_decompress_new;
-       post_decompress_old = post_decompress_new;
-       compression_counter_valid = (compression_counter_valid & 0x0f) << 4;
+       fclose (fh);
 
-       return (0);
-} /* int openvpn_read */
+       return version;
+} /* int version_detect */
 
 static int openvpn_config (const char *key, const char *value)
 {
        if (strcasecmp ("StatusFile", key) == 0)
        {
-               sfree (status_file);
+               char    *status_file, *status_name, *filename;
+               int     status_version, i;
+               vpn_status_t *temp;
+
+               /* try to detect the status file format */
+               status_version = version_detect (value);
+
+               if (status_version == 0)
+               {
+                       WARNING ("openvpn plugin: unable to detect status version, \
+                               discarding status file \"%s\".", value);
+                       return (1);
+               }
+
                status_file = sstrdup (value);
-       }
+               if (status_file == NULL)
+               {
+                       char errbuf[1024];
+                       WARNING ("openvpn plugin: sstrdup failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                       return (1);
+               }
+
+               /* it determines the file name as string starting at location filename + 1 */
+               filename = strrchr (status_file, (int) '/');
+               if (filename == NULL)
+               {
+                       /* status_file is already the file name only */
+                       status_name = status_file;
+               }
+               else
+               {
+                       /* doesn't waste memory, uses status_file starting at filename + 1 */
+                       status_name = filename + 1;
+               }
+
+               /* scan the list looking for a clone */
+               for (i = 0; i < vpn_num; i++)
+               {
+                       if (strcasecmp (vpn_list[i]->name, status_name) == 0)
+                       {
+                               WARNING ("openvpn plugin: status filename \"%s\" "
+                                               "already used, please choose a "
+                                               "different one.", status_name);
+                               sfree (status_file);
+                               return (1);
+                       }
+               }
+
+               /* create a new vpn element since file, version and name are ok */
+               temp = (vpn_status_t *) malloc (sizeof (vpn_status_t));
+               temp->file = status_file;
+               temp->version = status_version;
+               temp->name = status_name;
+
+               vpn_list = (vpn_status_t **) realloc (vpn_list, (vpn_num + 1) * sizeof (vpn_status_t *));
+               if (vpn_list == NULL)
+               {
+                       char errbuf[1024];
+                       ERROR ("openvpn plugin: malloc failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+
+                       sfree (temp->file);
+                       sfree (temp);
+                       return (1);
+               }
+
+               vpn_list[vpn_num] = temp;
+               vpn_num++;
+
+               DEBUG ("openvpn plugin: status file \"%s\" added", temp->file);
+
+       } /* if (strcasecmp ("StatusFile", key) == 0) */
+       else if (strcasecmp ("Compression", key) == 0)
+       {
+               if (IS_TRUE (value))
+                       store_compression = 1;
+               else
+               {
+                       store_compression = 0;
+                       DEBUG ("openvpn plugin: no 'compression statistcs' collected");
+               }
+       } /* if (strcasecmp ("Compression", key) == 0) */
+       else if (strcasecmp ("ImprovedNamingSchema", key) == 0)
+       {
+               if (IS_TRUE (value))
+               {
+                       DEBUG ("openvpn plugin: using the new naming schema");
+                       new_naming_schema = 1;
+               }
+               else
+               {
+                       new_naming_schema = 0;
+               }
+       } /* if (strcasecmp ("ImprovedNamingSchema", key) == 0) */
        else
        {
                return (-1);
        }
-        return (0);
+
+       return (0);
 } /* int openvpn_config */
 
+/* shutdown callback */
+static int openvpn_shutdown (void)
+{
+       int i;
+
+       for (i = 0; i < vpn_num; i++)
+       {
+               sfree (vpn_list[i]->file);
+               sfree (vpn_list[i]);
+       }
+
+       sfree (vpn_list);
+
+       return (0);
+} /* int openvpn_shutdown */
+
 void module_register (void)
 {
        plugin_register_config ("openvpn", openvpn_config,
-                               config_keys, config_keys_num);
+                       config_keys, config_keys_num);
        plugin_register_read ("openvpn", openvpn_read);
+       plugin_register_shutdown ("openvpn", openvpn_shutdown);
 } /* void module_register */
+
+/* vim: set sw=2 ts=2 : */
index 5187e2b..b536f42 100644 (file)
 # define NI_MAXHOST 1025
 #endif
 
+#if defined(OPING_VERSION) && (OPING_VERSION >= 1003000)
+# define HAVE_OPING_1_3
+#endif
+
 /*
  * Private data types
  */
@@ -45,6 +49,7 @@ struct hostlist_s
 
   uint32_t pkg_sent;
   uint32_t pkg_recv;
+  uint32_t pkg_missed;
 
   double latency_total;
   double latency_squared;
@@ -58,9 +63,14 @@ typedef struct hostlist_s hostlist_t;
  */
 static hostlist_t *hostlist_head = NULL;
 
+static char  *ping_source = NULL;
+#ifdef HAVE_OPING_1_3
+static char  *ping_device = NULL;
+#endif
 static int    ping_ttl = PING_DEF_TTL;
 static double ping_interval = 1.0;
 static double ping_timeout = 0.9;
+static int    ping_max_missed = -1;
 
 static int             ping_thread_loop = 0;
 static int             ping_thread_error = 0;
@@ -71,9 +81,14 @@ static pthread_cond_t  ping_cond = PTHREAD_COND_INITIALIZER;
 static const char *config_keys[] =
 {
   "Host",
+  "SourceAddress",
+#ifdef HAVE_OPING_1_3
+  "Device",
+#endif
   "TTL",
   "Interval",
-  "Timeout"
+  "Timeout",
+  "MaxMissed"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -137,7 +152,7 @@ static void *ping_thread (void *arg) /* {{{ */
   struct timespec ts_int;
 
   hostlist_t *hl;
-  int status;
+  int count;
 
   pthread_mutex_lock (&ping_lock);
 
@@ -150,11 +165,23 @@ static void *ping_thread (void *arg) /* {{{ */
     return ((void *) -1);
   }
 
+  if (ping_source != NULL)
+    if (ping_setopt (pingobj, PING_OPT_SOURCE, (void *) ping_source) != 0)
+      ERROR ("ping plugin: Failed to set source address: %s",
+          ping_get_error (pingobj));
+
+#ifdef HAVE_OPING_1_3
+  if (ping_device != NULL)
+    if (ping_setopt (pingobj, PING_OPT_DEVICE, (void *) ping_device) != 0)
+      ERROR ("ping plugin: Failed to set device: %s",
+          ping_get_error (pingobj));
+#endif
+
   ping_setopt (pingobj, PING_OPT_TIMEOUT, (void *) &ping_timeout);
   ping_setopt (pingobj, PING_OPT_TTL, (void *) &ping_ttl);
 
   /* Add all the hosts to the ping object. */
-  status = 0;
+  count = 0;
   for (hl = hostlist_head; hl != NULL; hl = hl->next)
   {
     int tmp_status;
@@ -163,10 +190,10 @@ static void *ping_thread (void *arg) /* {{{ */
       WARNING ("ping plugin: ping_host_add (%s) failed: %s",
           hl->host, ping_get_error (pingobj));
     else
-      status++;
+      count++;
   }
 
-  if (status == 0)
+  if (count == 0)
   {
     ERROR ("ping plugin: No host could be added to ping object. Giving up.");
     ping_thread_error = 1;
@@ -263,7 +290,37 @@ static void *ping_thread (void *arg) /* {{{ */
         hl->pkg_recv++;
         hl->latency_total += latency;
         hl->latency_squared += (latency * latency);
-      }
+
+        /* reset missed packages counter */
+        hl->pkg_missed = 0;
+      } else
+        hl->pkg_missed++;
+
+      /* if the host did not answer our last N packages, trigger a resolv. */
+      if (ping_max_missed >= 0 && hl->pkg_missed >= ping_max_missed)
+      { /* {{{ */
+        /* we reset the missed package counter here, since we only want to
+         * trigger a resolv every N packages and not every package _AFTER_ N
+         * missed packages */
+        hl->pkg_missed = 0;
+
+        WARNING ("ping plugin: host %s has not answered %d PING requests,"
+          " triggering resolve", hl->host, ping_max_missed);
+
+        /* we trigger the resolv simply be removeing and adding the host to our
+         * ping object */
+        status = ping_host_remove (pingobj, hl->host);
+        if (status != 0)
+        {
+          WARNING ("ping plugin: ping_host_remove (%s) failed.", hl->host);
+        }
+        else
+        {
+          status = ping_host_add (pingobj, hl->host);
+          if (status != 0)
+            WARNING ("ping plugin: ping_host_add (%s) failed.", hl->host);
+        }
+      } /* }}} ping_max_missed */
     } /* }}} for (iter) */
 
     if (gettimeofday (&tv_end, NULL) < 0)
@@ -368,6 +425,26 @@ static int ping_init (void) /* {{{ */
   return (0);
 } /* }}} int ping_init */
 
+static int config_set_string (const char *name, /* {{{ */
+    char **var, const char *value)
+{
+  char *tmp;
+
+  tmp = strdup (value);
+  if (tmp == NULL)
+  {
+    char errbuf[1024];
+    ERROR ("ping plugin: Setting `%s' to `%s' failed: strdup failed: %s",
+        name, value, sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (1);
+  }
+
+  if (*var != NULL)
+    free (*var);
+  *var = tmp;
+  return (0);
+} /* }}} int config_set_string */
+
 static int ping_config (const char *key, const char *value) /* {{{ */
 {
   if (strcasecmp (key, "Host") == 0)
@@ -397,11 +474,26 @@ static int ping_config (const char *key, const char *value) /* {{{ */
     hl->host = host;
     hl->pkg_sent = 0;
     hl->pkg_recv = 0;
+    hl->pkg_missed = 0;
     hl->latency_total = 0.0;
     hl->latency_squared = 0.0;
     hl->next = hostlist_head;
     hostlist_head = hl;
   }
+  else if (strcasecmp (key, "SourceAddress") == 0)
+  {
+    int status = config_set_string (key, &ping_source, value);
+    if (status != 0)
+      return (status);
+  }
+#ifdef HAVE_OPING_1_3
+  else if (strcasecmp (key, "Device") == 0)
+  {
+    int status = config_set_string (key, &ping_device, value);
+    if (status != 0)
+      return (status);
+  }
+#endif
   else if (strcasecmp (key, "TTL") == 0)
   {
     int ttl = atoi (value);
@@ -432,6 +524,12 @@ static int ping_config (const char *key, const char *value) /* {{{ */
       WARNING ("ping plugin: Ignoring invalid timeout %g (%s)",
           tmp, value);
   }
+  else if (strcasecmp (key, "MaxMissed") == 0)
+  {
+    ping_max_missed = atoi (value);
+    if (ping_max_missed < 0)
+      INFO ("ping plugin: MaxMissed < 0, disabled re-resolving of hosts");
+  }
   else
   {
     return (-1);
index c163ca7..1eed532 100644 (file)
@@ -151,7 +151,7 @@ static void destroy_read_heap (void) /* {{{ */
        {
                callback_func_t *cf;
 
-               cf = c_head_get_root (read_heap);
+               cf = c_heap_get_root (read_heap);
                if (cf == NULL)
                        break;
 
@@ -173,7 +173,7 @@ static int register_callback (llist_t **list, /* {{{ */
                *list = llist_create ();
                if (*list == NULL)
                {
-                       ERROR ("plugin: create_register_callback: "
+                       ERROR ("plugin: register_callback: "
                                        "llist_create failed.");
                        destroy_callback (cf);
                        return (-1);
@@ -183,7 +183,7 @@ static int register_callback (llist_t **list, /* {{{ */
        key = strdup (name);
        if (key == NULL)
        {
-               ERROR ("plugin: create_register_callback: strdup failed.");
+               ERROR ("plugin: register_callback: strdup failed.");
                destroy_callback (cf);
                return (-1);
        }
@@ -194,7 +194,7 @@ static int register_callback (llist_t **list, /* {{{ */
                le = llentry_create (key, cf);
                if (le == NULL)
                {
-                       ERROR ("plugin: create_register_callback: "
+                       ERROR ("plugin: register_callback: "
                                        "llentry_create failed.");
                        free (key);
                        destroy_callback (cf);
@@ -210,6 +210,10 @@ static int register_callback (llist_t **list, /* {{{ */
                old_cf = le->value;
                le->value = cf;
 
+               WARNING ("plugin: register_callback: "
+                               "a callback named `%s' already exists - "
+                               "overwriting the old entry!", name);
+
                destroy_callback (old_cf);
                sfree (key);
        }
@@ -270,7 +274,7 @@ static int plugin_unregister (llist_t *list, const char *name) /* {{{ */
  * object, but it will bitch about a shared object not having a
  * ``module_register'' symbol..
  */
-static int plugin_load_file (char *file)
+static int plugin_load_file (char *file, uint32_t flags)
 {
        lt_dlhandle dlh;
        void (*reg_handle) (void);
@@ -280,7 +284,24 @@ static int plugin_load_file (char *file)
        lt_dlinit ();
        lt_dlerror (); /* clear errors */
 
-       if ((dlh = lt_dlopen (file)) == NULL)
+#if LIBTOOL_VERSION == 2
+       if (flags & PLUGIN_FLAGS_GLOBAL) {
+               lt_dladvise advise;
+               lt_dladvise_init(&advise);
+               lt_dladvise_global(&advise);
+               dlh = lt_dlopenadvise(file, advise);
+               lt_dladvise_destroy(&advise);
+       } else {
+               dlh = lt_dlopen (file);
+       }
+#else /* if LIBTOOL_VERSION == 1 */
+       if (flags & PLUGIN_FLAGS_GLOBAL)
+               ERROR ("plugin_load_file: The global flag is not supported, "
+                               "libtool 2 is required for this.");
+       dlh = lt_dlopen (file);
+#endif
+
+       if (dlh == NULL)
        {
                const char *error = lt_dlerror ();
 
@@ -320,7 +341,7 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                int rc;
 
                /* Get the read function that needs to be read next. */
-               rf = c_head_get_root (read_heap);
+               rf = c_heap_get_root (read_heap);
                if (rf == NULL)
                {
                        struct timespec abstime;
@@ -551,7 +572,7 @@ void plugin_set_dir (const char *dir)
 }
 
 #define BUFSIZE 512
-int plugin_load (const char *type)
+int plugin_load (const char *type, uint32_t flags)
 {
        DIR  *dh;
        const char *dir;
@@ -613,7 +634,7 @@ int plugin_load (const char *type)
                        continue;
                }
 
-               if (plugin_load_file (filename) == 0)
+               if (plugin_load_file (filename, flags) == 0)
                {
                        /* success */
                        ret = 0;
@@ -1052,7 +1073,7 @@ int plugin_read_all_once (void)
        {
                read_func_t *rf;
 
-               rf = c_head_get_root (read_heap);
+               rf = c_heap_get_root (read_heap);
                if (rf == NULL)
                        break;
 
index b35fcf1..868f44a 100644 (file)
@@ -26,6 +26,8 @@
 #include "configfile.h"
 #include "meta_data.h"
 
+#define PLUGIN_FLAGS_GLOBAL 0x0001
+
 #define DATA_MAX_NAME_LEN 64
 
 #define DS_TYPE_COUNTER  0
@@ -200,7 +202,7 @@ void plugin_set_dir (const char *dir);
  *
  * ARGUMENTS
  *  `name'      Name of the plugin to load.
- *  `mr'        Types of functions to request from the plugin.
+ *  `flags'     Hints on how to handle this plugin.
  *
  * RETURN VALUE
  *  Returns zero upon success, a value greater than zero if no plugin was found
@@ -209,7 +211,7 @@ void plugin_set_dir (const char *dir);
  * NOTES
  *  No attempt is made to re-load an already loaded module.
  */
-int plugin_load (const char *name);
+int plugin_load (const char *name, uint32_t flags);
 
 void plugin_init_all (void);
 void plugin_read_all (void);
@@ -282,7 +284,6 @@ int plugin_unregister_config (const char *name);
 int plugin_unregister_complex_config (const char *name);
 int plugin_unregister_init (const char *name);
 int plugin_unregister_read (const char *name);
-int plugin_unregister_complex_read (const char *name, void **user_data);
 int plugin_unregister_write (const char *name);
 int plugin_unregister_flush (const char *name);
 int plugin_unregister_shutdown (const char *name);
index 7430afe..f2eb0a3 100644 (file)
@@ -4,6 +4,8 @@
  * Copyright (C) 2006-2008  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
  *
  * 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 +26,8 @@
  *   Florian octo Forster <octo at verplant.org>
  *   Oleg King <king2 at kaluga.ru>
  *   Sebastian Harl <sh at tokkee.org>
+ *   Andrés J. Díaz <ajdiaz at connectical.com>
+ *   Manuel Sanmartin
  **/
 
 #include "collectd.h"
 #  include <sys/proc.h>
 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
 
+#elif HAVE_PROCINFO_H
+#  include <procinfo.h>
+#  include <sys/types.h>
+
+#define MAXPROCENTRY 32
+#define MAXTHRDENTRY 16
+#define MAXARGLN 1024
+/* #endif HAVE_PROCINFO_H */
+
 #else
 # error "No applicable input method."
 #endif
@@ -136,6 +149,12 @@ typedef struct procstat_entry_s
        unsigned long cpu_user_counter;
        unsigned long cpu_system_counter;
 
+       /* io data */
+       derive_t io_rchar;
+       derive_t io_wchar;
+       derive_t io_syscr;
+       derive_t io_syscw;
+
        struct procstat_entry_s *next;
 } procstat_entry_t;
 
@@ -159,6 +178,12 @@ typedef struct procstat
        unsigned long cpu_user_counter;
        unsigned long cpu_system_counter;
 
+       /* io data */
+       derive_t io_rchar;
+       derive_t io_wchar;
+       derive_t io_syscr;
+       derive_t io_syscw;
+
        struct procstat   *next;
        struct procstat_entry_s *instances;
 } procstat_t;
@@ -179,7 +204,19 @@ static long pagesize_g;
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
 /* no global variables */
-#endif /* HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
+/* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
+
+#elif HAVE_PROCINFO_H
+static  struct procentry64 procentry[MAXPROCENTRY];
+static  struct thrdentry64 thrdentry[MAXTHRDENTRY];
+static int pagesize;
+
+#ifndef _AIXVERSION_610
+int     getprocs64 (void *procsinfo, int sizproc, void *fdsinfo, int sizfd, pid_t *index, int count);
+int     getthrds64( pid_t, void *, int, tid64_t *, int );
+#endif
+int getargs (struct procentry64 *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
+#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
@@ -231,7 +268,7 @@ static void ps_list_register (const char *name, const char *regexp)
                return;
        }
 #endif
-       
+
        for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
        {
                if (strcmp (ptr->name, name) == 0)
@@ -307,13 +344,13 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                if ((pse == NULL) || (pse->id != entry->id))
                {
                        procstat_entry_t *new;
-                       
+
                        new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t));
                        if (new == NULL)
                                return;
                        memset (new, 0, sizeof (procstat_entry_t));
                        new->id = entry->id;
-                       
+
                        if (pse == NULL)
                                ps->instances = new;
                        else
@@ -328,6 +365,10 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                pse->vmem_size  = entry->vmem_size;
                pse->vmem_rss   = entry->vmem_rss;
                pse->stack_size = entry->stack_size;
+               pse->io_rchar   = entry->io_rchar;
+               pse->io_wchar   = entry->io_wchar;
+               pse->io_syscr   = entry->io_syscr;
+               pse->io_syscw   = entry->io_syscw;
 
                ps->num_proc   += pse->num_proc;
                ps->num_lwp    += pse->num_lwp;
@@ -335,6 +376,11 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                ps->vmem_rss   += pse->vmem_rss;
                ps->stack_size += pse->stack_size;
 
+               ps->io_rchar   += ((pse->io_rchar == -1)?0:pse->io_rchar);
+               ps->io_wchar   += ((pse->io_wchar == -1)?0:pse->io_wchar);
+               ps->io_syscr   += ((pse->io_syscr == -1)?0:pse->io_syscr);
+               ps->io_syscw   += ((pse->io_syscw == -1)?0:pse->io_syscw);
+
                if ((entry->vmem_minflt_counter == 0)
                                && (entry->vmem_majflt_counter == 0))
                {
@@ -356,7 +402,7 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                                pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter;
                        }
                        pse->vmem_minflt_counter = entry->vmem_minflt_counter;
-                       
+
                        if (entry->vmem_majflt_counter < pse->vmem_majflt_counter)
                        {
                                pse->vmem_majflt = entry->vmem_majflt_counter
@@ -393,7 +439,7 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                                pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter;
                        }
                        pse->cpu_user_counter = entry->cpu_user_counter;
-                       
+
                        if (entry->cpu_system_counter < pse->cpu_system_counter)
                        {
                                pse->cpu_system = entry->cpu_system_counter
@@ -425,6 +471,10 @@ static void ps_list_reset (void)
                ps->vmem_size   = 0;
                ps->vmem_rss    = 0;
                ps->stack_size  = 0;
+               ps->io_rchar = -1;
+               ps->io_wchar = -1;
+               ps->io_syscr = -1;
+               ps->io_syscw = -1;
 
                pse_prev = NULL;
                pse = ps->instances;
@@ -538,7 +588,11 @@ static int ps_init (void)
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
 /* no initialization */
-#endif /* HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
+/* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
+
+#elif HAVE_PROCINFO_H
+       pagesize = getpagesize();
+#endif /* HAVE_PROCINFO_H */
 
        return (0);
 } /* int ps_init */
@@ -607,12 +661,33 @@ static void ps_submit_proc_list (procstat_t *ps)
        vl.values_len = 2;
        plugin_dispatch_values (&vl);
 
+       if ( (ps->io_rchar != -1) && (ps->io_wchar != -1) )
+       {
+               sstrncpy (vl.type, "ps_disk_octets", sizeof (vl.type));
+               vl.values[0].derive = ps->io_rchar;
+               vl.values[1].derive = ps->io_wchar;
+               vl.values_len = 2;
+               plugin_dispatch_values (&vl);
+       }
+
+       if ( (ps->io_syscr != -1) && (ps->io_syscw != -1) )
+       {
+               sstrncpy (vl.type, "ps_disk_ops", sizeof (vl.type));
+               vl.values[0].derive = ps->io_syscr;
+               vl.values[1].derive = ps->io_syscw;
+               vl.values_len = 2;
+               plugin_dispatch_values (&vl);
+       }
+
        DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
                        "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
-                       "cpu_user_counter = %lu; cpu_system_counter = %lu;",
+                       "cpu_user_counter = %lu; cpu_system_counter = %lu; "
+                       "io_rchar = %"PRIi64"; io_wchar = %"PRIi64"; "
+                       "io_syscr = %"PRIi64"; io_syscw = %"PRIi64";",
                        ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
                        ps->vmem_minflt_counter, ps->vmem_majflt_counter,
-                       ps->cpu_user_counter, ps->cpu_system_counter);
+                       ps->cpu_user_counter, ps->cpu_system_counter,
+                       ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
 } /* void ps_submit_proc_list */
 
 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
@@ -644,6 +719,60 @@ static int ps_read_tasks (int pid)
        return ((count >= 1) ? count : 1);
 } /* int *ps_read_tasks */
 
+static procstat_t *ps_read_io (int pid, procstat_t *ps)
+{
+       FILE *fh;
+       char buffer[1024];
+       char filename[64];
+
+       char *fields[8];
+       int numfields;
+
+       ssnprintf (filename, sizeof (filename), "/proc/%i/io", pid);
+       if ((fh = fopen (filename, "r")) == NULL)
+               return (NULL);
+
+       while (fgets (buffer, 1024, fh) != NULL)
+       {
+               derive_t *val = NULL;
+               long long tmp;
+               char *endptr;
+
+               if (strncasecmp (buffer, "rchar:", 6) == 0)
+                       val = &(ps->io_rchar);
+               else if (strncasecmp (buffer, "wchar:", 6) == 0)
+                       val = &(ps->io_wchar);
+               else if (strncasecmp (buffer, "syscr:", 6) == 0)
+                       val = &(ps->io_syscr);
+               else if (strncasecmp (buffer, "syscw:", 6) == 0)
+                       val = &(ps->io_syscw);
+               else
+                       continue;
+
+               numfields = strsplit (buffer, fields, 8);
+
+               if (numfields < 2)
+                       continue;
+
+               errno = 0;
+               endptr = NULL;
+               tmp = strtoll (fields[1], &endptr, /* base = */ 10);
+               if ((errno != 0) || (endptr == fields[1]))
+                       *val = -1;
+               else
+                       *val = (derive_t) tmp;
+       } /* while (fgets) */
+
+       if (fclose (fh))
+       {
+               char errbuf[1024];
+               WARNING ("processes: fclose: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
+
+       return (ps);
+} /* procstat_t *ps_read_io */
+
 int ps_read_process (int pid, procstat_t *ps, char *state)
 {
        char  filename[64];
@@ -746,6 +875,17 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        ps->vmem_rss = (unsigned long) vmem_rss;
        ps->stack_size = (unsigned long) stack_size;
 
+       if ( (ps_read_io (pid, ps)) == NULL)
+       {
+               /* no io data */
+               ps->io_rchar = -1;
+               ps->io_wchar = -1;
+               ps->io_syscr = -1;
+               ps->io_syscw = -1;
+
+               DEBUG("ps_read_process: not get io data for pid %i",pid);
+       }
+
        /* success */
        return (0);
 } /* int ps_read_process (...) */
@@ -839,6 +979,70 @@ 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 ()
+{
+       FILE *proc_stat;
+       char buf[1024];
+       unsigned long result = 0;
+       int numfields;
+       char *fields[3];
+
+       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;
+       }
+
+       while (fgets (buf, sizeof(buf), proc_stat) != NULL)
+       {
+               char *endptr;
+
+               numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE (fields));
+               if (numfields != 2)
+                       continue;
+
+               if (strcmp ("processes", fields[0]) != 0)
+                       continue;
+
+               errno = 0;
+               endptr = NULL;
+               result = strtoul(fields[1], &endptr, 10);
+               if ((endptr == fields[1]) || (errno != 0)) {
+                       ERROR ("processes plugin: Cannot parse fork rate: %s",
+                                       fields[1]);
+                       result = ULONG_MAX;
+                       break;
+               }
+
+               break;
+       }
+
+       fclose(proc_stat);
+
+       return result;
+}
+
+static void ps_submit_fork_rate (unsigned long value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].derive = (derive_t) 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 */
 
 #if HAVE_THREAD_INFO
@@ -1161,6 +1365,8 @@ 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;
@@ -1208,6 +1414,11 @@ static int ps_read (void)
                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;
@@ -1234,6 +1445,10 @@ 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);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
@@ -1332,6 +1547,12 @@ static int ps_read (void)
                        * 1000
                        + procs[i].ki_rusage.ru_stime.tv_usec;
 
+               /* no io data */
+               pse.io_rchar = -1;
+               pse.io_wchar = -1;
+               pse.io_syscr = -1;
+               pse.io_syscw = -1;
+
                switch (procs[i].ki_stat)
                {
                        case SSTOP:     stopped++;      break;
@@ -1358,7 +1579,133 @@ 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);
-#endif /* HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
+/* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
+
+#elif HAVE_PROCINFO_H
+       /* AIX */
+       int running  = 0;
+       int sleeping = 0;
+       int zombies  = 0;
+       int stopped  = 0;
+       int paging   = 0;
+       int blocked  = 0;
+
+       pid_t pindex = 0;
+       int nprocs;
+
+       procstat_t *ps;
+       procstat_entry_t pse;
+
+       ps_list_reset ();
+       while ((nprocs = getprocs64 (procentry, sizeof(struct procentry64),
+                                       /* fdsinfo = */ NULL, sizeof(struct fdsinfo64),
+                                       &pindex, MAXPROCENTRY)) > 0)
+       {
+               int i;
+
+               for (i = 0; i < nprocs; i++)
+               {
+                       tid64_t thindex;
+                       int nthreads;
+                       char arglist[MAXARGLN+1];
+                       char *cargs;
+                       char *cmdline;
+
+                       if (procentry[i].pi_state == SNONE) continue;
+                       /* if (procentry[i].pi_state == SZOMB)  FIXME */
+
+                       cmdline = procentry[i].pi_comm;
+                       cargs = procentry[i].pi_comm;
+                       if ( procentry[i].pi_flags & SKPROC )
+                       {
+                               if (procentry[i].pi_pid == 0)
+                                       cmdline = "swapper";
+                               cargs = cmdline;
+                       }
+                       else
+                       {
+                               if (getargs(&procentry[i], sizeof(struct procentry64), arglist, MAXARGLN) >= 0)
+                               {
+                                       int n;
+
+                                       n = -1;
+                                       while (++n < MAXARGLN)
+                                       {
+                                               if (arglist[n] == '\0')
+                                               {
+                                                       if (arglist[n+1] == '\0')
+                                                               break;
+                                                       arglist[n] = ' ';
+                                               }
+                                       }
+                                       cargs = arglist;
+                               }
+                       }
+
+                       pse.id       = procentry[i].pi_pid;
+                       pse.age      = 0;
+                       pse.num_lwp  = procentry[i].pi_thcount;
+                       pse.num_proc = 1;
+
+                       thindex=0;
+                       while ((nthreads = getthrds64(procentry[i].pi_pid,
+                                                       thrdentry, sizeof(struct thrdentry64),
+                                                       &thindex, MAXTHRDENTRY)) > 0)
+                       {
+                               int j;
+
+                               for (j=0; j< nthreads; j++)
+                               {
+                                       switch (thrdentry[j].ti_state)
+                                       {
+                                               /* case TSNONE: break; */
+                                               case TSIDL:     blocked++;      break; /* FIXME is really blocked */
+                                               case TSRUN:     running++;      break;
+                                               case TSSLEEP:   sleeping++;     break;
+                                               case TSSWAP:    paging++;       break;
+                                               case TSSTOP:    stopped++;      break;
+                                               case TSZOMB:    zombies++;      break;
+                                       }
+                               }
+                               if (nthreads < MAXTHRDENTRY)
+                                       break;
+                       }
+
+                       pse.cpu_user = 0;
+                       /* tv_usec is nanosec ??? */
+                       pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
+                               procentry[i].pi_ru.ru_utime.tv_usec / 1000;
+
+                       pse.cpu_system = 0;
+                       /* tv_usec is nanosec ??? */
+                       pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
+                               procentry[i].pi_ru.ru_stime.tv_usec / 1000;
+
+                       pse.vmem_minflt = 0;
+                       pse.vmem_minflt_counter = procentry[i].pi_minflt;
+                       pse.vmem_majflt = 0;
+                       pse.vmem_majflt_counter = procentry[i].pi_majflt;
+
+                       pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
+                       pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
+                       pse.stack_size =  0;
+
+                       ps_list_add (cmdline, cargs, &pse);
+               } /* for (i = 0 .. nprocs) */
+
+               if (nprocs < MAXPROCENTRY)
+                       break;
+       } /* while (getprocs64() > 0) */
+       ps_submit_state ("running",  running);
+       ps_submit_state ("sleeping", sleeping);
+       ps_submit_state ("zombies",  zombies);
+       ps_submit_state ("stopped",  stopped);
+       ps_submit_state ("paging",   paging);
+       ps_submit_state ("blocked",  blocked);
+
+       for (ps = list_head_g; ps != NULL; ps = ps->next)
+               ps_submit_proc_list (ps);
+#endif /* HAVE_PROCINFO_H */
 
        return (0);
 } /* int ps_read */
index e830c42..e90c1a4 100644 (file)
@@ -224,9 +224,7 @@ static int protocols_config (const char *key, const char *value)
   else if (strcasecmp (key, "IgnoreSelected") == 0)
   {
     int invert = 1;
-    if ((strcasecmp (value, "True") == 0)
-        || (strcasecmp (value, "Yes") == 0)
-        || (strcasecmp (value, "On") == 0))
+    if (IS_TRUE (value))
       invert = 0;
     ignorelist_set_invert (values_list, invert);
   }
diff --git a/src/pyconfig.c b/src/pyconfig.c
new file mode 100644 (file)
index 0000000..bac39ae
--- /dev/null
@@ -0,0 +1,192 @@
+/**
+ * collectd - src/pyconfig.c
+ * Copyright (C) 2009  Sven Trenkel
+ *
+ * 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:
+ *   Sven Trenkel <collectd at semidefinite.de>  
+ **/
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "collectd.h"
+#include "common.h"
+
+#include "cpython.h"
+
+static char config_doc[] = "This represents a piece of collectd's config file.\n"
+               "It is passed to scripts with config callbacks (see \"register_config\")\n"
+               "and is of little use if created somewhere else.\n"
+               "\n"
+               "It has no methods beyond the bare minimum and only exists for its\n"
+               "data members";
+
+static char parent_doc[] = "This represents the parent of this node. On the root node\n"
+               "of the config tree it will be None.\n";
+
+static char key_doc[] = "This is the keyword of this item, ie the first word of any\n"
+               "given line in the config file. It will always be a string.\n";
+
+static char values_doc[] = "This is a tuple (which might be empty) of all value, ie words\n"
+               "following the keyword in any given line in the config file.\n"
+               "\n"
+               "Every item in this tuple will be either a string or a float or a bool,\n"
+               "depending on the contents of the configuration file.\n";
+
+static char children_doc[] = "This is a tuple of child nodes. For most nodes this will be\n"
+               "empty. If this node represents a block instead of a single line of the config\n"
+               "file it will contain all nodes in this block.\n";
+
+static PyObject *Config_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
+       Config *self;
+       
+       self = (Config *) type->tp_alloc(type, 0);
+       if (self == NULL)
+               return NULL;
+       
+       self->parent = NULL;
+       self->key = NULL;
+       self->values = NULL;
+       self->children = NULL;
+       return (PyObject *) self;
+}
+
+static int Config_init(PyObject *s, PyObject *args, PyObject *kwds) {
+       PyObject *key = NULL, *parent = NULL, *values = NULL, *children = NULL, *tmp;
+       Config *self = (Config *) s;
+       static char *kwlist[] = {"key", "parent", "values", "children", NULL};
+       
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "S|OOO", kwlist,
+                       &key, &parent, &values, &children))
+               return -1;
+       
+       if (values == NULL) {
+               values = PyTuple_New(0);
+               PyErr_Clear();
+       }
+       if (children == NULL) {
+               children = PyTuple_New(0);
+               PyErr_Clear();
+       }
+       tmp = self->key;
+       Py_INCREF(key);
+       self->key = key;
+       Py_XDECREF(tmp);
+       if (parent != NULL) {
+               tmp = self->parent;
+               Py_INCREF(parent);
+               self->parent = parent;
+               Py_XDECREF(tmp);
+       }
+       if (values != NULL) {
+               tmp = self->values;
+               Py_INCREF(values);
+               self->values = values;
+               Py_XDECREF(tmp);
+       }
+       if (children != NULL) {
+               tmp = self->children;
+               Py_INCREF(children);
+               self->children = children;
+               Py_XDECREF(tmp);
+       }
+       return 0;
+}
+
+static PyObject *Config_repr(PyObject *s) {
+       Config *self = (Config *) s;
+       
+       return PyString_FromFormat("<collectd.Config %snode %s>", self->parent == Py_None ? "root " : "", PyString_AsString(PyObject_Str(self->key)));
+}
+
+static int Config_traverse(PyObject *self, visitproc visit, void *arg) {
+       Config *c = (Config *) self;
+       Py_VISIT(c->parent);
+       Py_VISIT(c->key);
+       Py_VISIT(c->values);
+       Py_VISIT(c->children);
+       return 0;
+}
+
+static int Config_clear(PyObject *self) {
+       Config *c = (Config *) self;
+       Py_CLEAR(c->parent);
+       Py_CLEAR(c->key);
+       Py_CLEAR(c->values);
+       Py_CLEAR(c->children);
+       return 0;
+}
+
+static void Config_dealloc(PyObject *self) {
+       Config_clear(self);
+       self->ob_type->tp_free(self);
+}
+
+static PyMemberDef Config_members[] = {
+       {"parent", T_OBJECT, offsetof(Config, parent), 0, parent_doc},
+       {"key", T_OBJECT_EX, offsetof(Config, key), 0, key_doc},
+       {"values", T_OBJECT_EX, offsetof(Config, values), 0, values_doc},
+       {"children", T_OBJECT_EX, offsetof(Config, children), 0, children_doc},
+       {NULL}
+};
+
+PyTypeObject ConfigType = {
+       PyObject_HEAD_INIT(NULL)
+       0,                         /* Always 0 */
+       "collectd.Config",         /* tp_name */
+       sizeof(Config),            /* tp_basicsize */
+       0,                         /* Will be filled in later */
+       Config_dealloc,            /* tp_dealloc */
+       0,                         /* tp_print */
+       0,                         /* tp_getattr */
+       0,                         /* tp_setattr */
+       0,                         /* tp_compare */
+       Config_repr,               /* tp_repr */
+       0,                         /* tp_as_number */
+       0,                         /* tp_as_sequence */
+       0,                         /* tp_as_mapping */
+       0,                         /* tp_hash */
+       0,                         /* tp_call */
+       0,                         /* tp_str */
+       0,                         /* tp_getattro */
+       0,                         /* tp_setattro */
+       0,                         /* tp_as_buffer */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+       config_doc,                /* tp_doc */
+       Config_traverse,           /* tp_traverse */
+       Config_clear,              /* tp_clear */
+       0,                         /* tp_richcompare */
+       0,                         /* tp_weaklistoffset */
+       0,                         /* tp_iter */
+       0,                         /* tp_iternext */
+       0,                         /* tp_methods */
+       Config_members,            /* tp_members */
+       0,                         /* tp_getset */
+       0,                         /* tp_base */
+       0,                         /* tp_dict */
+       0,                         /* tp_descr_get */
+       0,                         /* tp_descr_set */
+       0,                         /* tp_dictoffset */
+       Config_init,               /* tp_init */
+       0,                         /* tp_alloc */
+       Config_new                 /* tp_new */
+};
+
diff --git a/src/python.c b/src/python.c
new file mode 100644 (file)
index 0000000..d750d95
--- /dev/null
@@ -0,0 +1,1035 @@
+/**
+ * collectd - src/python.c
+ * Copyright (C) 2009  Sven Trenkel
+ *
+ * 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:
+ *   Sven Trenkel <collectd at semidefinite.de>  
+ **/
+
+#include <Python.h>
+#include <structmember.h>
+
+#include <signal.h>
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+#include "collectd.h"
+#include "common.h"
+
+#include "cpython.h"
+
+typedef struct cpy_callback_s {
+       char *name;
+       PyObject *callback;
+       PyObject *data;
+       struct cpy_callback_s *next;
+} cpy_callback_t;
+
+static char log_doc[] = "This function sends a string to all logging plugins.";
+
+static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
+               "\n"
+               "Flushes the cache of another plugin.";
+
+static char unregister_doc[] = "Unregisters a callback. This function needs exactly one parameter either\n"
+               "the function to unregister or the callback identifier to unregister.";
+
+static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifier\n"
+               "\n"
+               "Register a callback function for log messages.\n"
+               "\n"
+               "'callback' is a callable object that will be called every time something\n"
+               "    is logged.\n"
+               "'data' is an optional object that will be passed back to the callback\n"
+               "    function every time it is called.\n"
+               "'name' is an optional identifier for this callback. The default name\n"
+               "    is 'python.<module>'.\n"
+               "    Every callback needs a unique identifier, so if you want to\n"
+               "    register this callback multiple time from the same module you need\n"
+               "    to specify a name here.\n"
+               "'identifier' is the full identifier assigned to this callback.\n"
+               "\n"
+               "The callback function will be called with two or three parameters:\n"
+               "severity: An integer that should be compared to the LOG_ constants.\n"
+               "message: The text to be logged.\n"
+               "data: The optional data parameter passed to the register function.\n"
+               "    If the parameter was omitted it will be omitted here, too.";
+
+static char reg_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
+               "\n"
+               "Register a callback function that will be executed once after the config.\n"
+               "file has been read, all plugins heve been loaded and the collectd has\n"
+               "forked into the background.\n"
+               "\n"
+               "'callback' is a callable object that will be executed.\n"
+               "'data' is an optional object that will be passed back to the callback\n"
+               "    function when it is called.\n"
+               "'name' is an optional identifier for this callback. The default name\n"
+               "    is 'python.<module>'.\n"
+               "    Every callback needs a unique identifier, so if you want to\n"
+               "    register this callback multiple time from the same module you need\n"
+               "    to specify a name here.\n"
+               "'identifier' is the full identifier assigned to this callback.\n"
+               "\n"
+               "The callback function will be called without parameters, except for\n"
+               "data if it was supplied.";
+
+static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
+               "\n"
+               "Register a callback function for config file entries.\n"
+               "'callback' is a callable object that will be called for every config block.\n"
+               "'data' is an optional object that will be passed back to the callback\n"
+               "    function every time it is called.\n"
+               "'name' is an optional identifier for this callback. The default name\n"
+               "    is 'python.<module>'.\n"
+               "    Every callback needs a unique identifier, so if you want to\n"
+               "    register this callback multiple time from the same module you need\n"
+               "    to specify a name here.\n"
+               "'identifier' is the full identifier assigned to this callback.\n"
+               "\n"
+               "The callback function will be called with one or two parameters:\n"
+               "config: A Config object.\n"
+               "data: The optional data parameter passed to the register function.\n"
+               "    If the parameter was omitted it will be omitted here, too.";
+
+static char reg_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
+               "\n"
+               "Register a callback function for reading data. It will just be called\n"
+               "in a fixed interval to signal that it's time to dispatch new values.\n"
+               "'callback' is a callable object that will be called every time something\n"
+               "    is logged.\n"
+               "'interval' is the number of seconds between between calls to the callback\n"
+               "    function. Full float precision is supported here.\n"
+               "'data' is an optional object that will be passed back to the callback\n"
+               "    function every time it is called.\n"
+               "'name' is an optional identifier for this callback. The default name\n"
+               "    is 'python.<module>'.\n"
+               "    Every callback needs a unique identifier, so if you want to\n"
+               "    register this callback multiple time from the same module you need\n"
+               "    to specify a name here.\n"
+               "'identifier' is the full identifier assigned to this callback.\n"
+               "\n"
+               "The callback function will be called without parameters, except for\n"
+               "data if it was supplied.";
+
+static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
+               "\n"
+               "Register a callback function to receive values dispatched by other plugins.\n"
+               "'callback' is a callable object that will be called every time a value\n"
+               "    is dispatched.\n"
+               "'data' is an optional object that will be passed back to the callback\n"
+               "    function every time it is called.\n"
+               "'name' is an optional identifier for this callback. The default name\n"
+               "    is 'python.<module>'.\n"
+               "    Every callback needs a unique identifier, so if you want to\n"
+               "    register this callback multiple time from the same module you need\n"
+               "    to specify a name here.\n"
+               "'identifier' is the full identifier assigned to this callback.\n"
+               "\n"
+               "The callback function will be called with one or two parameters:\n"
+               "values: A Values object which is a copy of the dispatched values.\n"
+               "data: The optional data parameter passed to the register function.\n"
+               "    If the parameter was omitted it will be omitted here, too.";
+
+static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
+               "\n"
+               "Register a callback function for notifications.\n"
+               "'callback' is a callable object that will be called every time a notification\n"
+               "    is dispatched.\n"
+               "'data' is an optional object that will be passed back to the callback\n"
+               "    function every time it is called.\n"
+               "'name' is an optional identifier for this callback. The default name\n"
+               "    is 'python.<module>'.\n"
+               "    Every callback needs a unique identifier, so if you want to\n"
+               "    register this callback multiple time from the same module you need\n"
+               "    to specify a name here.\n"
+               "'identifier' is the full identifier assigned to this callback.\n"
+               "\n"
+               "The callback function will be called with one or two parameters:\n"
+               "notification: A copy of the notification that was dispatched.\n"
+               "data: The optional data parameter passed to the register function.\n"
+               "    If the parameter was omitted it will be omitted here, too.";
+
+static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
+               "\n"
+               "Register a callback function for flush messages.\n"
+               "'callback' is a callable object that will be called every time a plugin\n"
+               "    requests a flush for either this or all plugins.\n"
+               "'data' is an optional object that will be passed back to the callback\n"
+               "    function every time it is called.\n"
+               "'name' is an optional identifier for this callback. The default name\n"
+               "    is 'python.<module>'.\n"
+               "    Every callback needs a unique identifier, so if you want to\n"
+               "    register this callback multiple time from the same module you need\n"
+               "    to specify a name here.\n"
+               "'identifier' is the full identifier assigned to this callback.\n"
+               "\n"
+               "The callback function will be called with two or three parameters:\n"
+               "timeout: Indicates that only data older than 'timeout' seconds is to\n"
+               "    be flushed.\n"
+               "id: Specifies which values are to be flushed.\n"
+               "data: The optional data parameter passed to the register function.\n"
+               "    If the parameter was omitted it will be omitted here, too.";
+
+static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
+               "\n"
+               "Register a callback function for collectd shutdown.\n"
+               "'callback' is a callable object that will be called once collectd is\n"
+               "    shutting down.\n"
+               "'data' is an optional object that will be passed back to the callback\n"
+               "    function if it is called.\n"
+               "'name' is an optional identifier for this callback. The default name\n"
+               "    is 'python.<module>'.\n"
+               "    Every callback needs a unique identifier, so if you want to\n"
+               "    register this callback multiple time from the same module you need\n"
+               "    to specify a name here.\n"
+               "'identifier' is the full identifier assigned to this callback.\n"
+               "\n"
+               "The callback function will be called with no parameters except for\n"
+               "    data if it was supplied.";
+
+
+static int do_interactive = 0;
+
+/* This is our global thread state. Python saves some stuff in thread-local
+ * storage. So if we allow the interpreter to run in the background
+ * (the scriptwriters might have created some threads from python), we have
+ * to save the state so we can resume it later after shutdown. */
+
+static PyThreadState *state;
+
+static PyObject *cpy_format_exception;
+
+static cpy_callback_t *cpy_config_callbacks;
+static cpy_callback_t *cpy_init_callbacks;
+static cpy_callback_t *cpy_shutdown_callbacks;
+
+static void cpy_destroy_user_data(void *data) {
+       cpy_callback_t *c = data;
+       free(c->name);
+       Py_DECREF(c->callback);
+       Py_XDECREF(c->data);
+       free(c);
+}
+
+/* You must hold the GIL to call this function!
+ * But if you managed to extract the callback parameter then you probably already do. */
+
+static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
+       const char *module = NULL;
+       PyObject *mod = NULL;
+       
+       if (name != NULL) {
+               snprintf(buf, size, "python.%s", name);
+               return;
+       }
+       
+       mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
+       if (mod != NULL)
+               module = PyString_AsString(mod);
+       
+       if (module != NULL) {
+               snprintf(buf, size, "python.%s", module);
+               Py_XDECREF(mod);
+               PyErr_Clear();
+               return;
+       }
+       Py_XDECREF(mod);
+       
+       snprintf(buf, size, "python.%p", callback);
+       PyErr_Clear();
+}
+
+static void cpy_log_exception(const char *context) {
+       int l = 0, i;
+       const char *typename = NULL, *message = NULL;
+       PyObject *type, *value, *traceback, *tn, *m, *list;
+       
+       PyErr_Fetch(&type, &value, &traceback);
+       PyErr_NormalizeException(&type, &value, &traceback);
+       if (type == NULL) return;
+       tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
+       m = PyObject_GetAttrString(value, "message"); /* New reference. */
+       if (tn != NULL)
+               typename = PyString_AsString(tn);
+       if (m != NULL)
+               message = PyString_AsString(m);
+       if (typename == NULL)
+               typename = "NamelessException";
+       if (message == NULL)
+               message = "N/A";
+       Py_BEGIN_ALLOW_THREADS
+       ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
+       Py_END_ALLOW_THREADS
+       Py_XDECREF(tn);
+       Py_XDECREF(m);
+       if (!cpy_format_exception) {
+               PyErr_Clear();
+               Py_XDECREF(type);
+               Py_XDECREF(value);
+               Py_XDECREF(traceback);
+               return;
+       }
+       if (!traceback) {
+               PyErr_Clear();
+               return;
+       }
+       list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
+       if (list)
+               l = PyObject_Length(list);
+       for (i = 0; i < l; ++i) {
+               char *s;
+               PyObject *line;
+               
+               line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
+               s = strdup(PyString_AsString(line));
+               if (s[strlen(s) - 1] == '\n')
+                       s[strlen(s) - 1] = 0;
+               Py_BEGIN_ALLOW_THREADS
+               ERROR("%s", s);
+               Py_END_ALLOW_THREADS
+               free(s);
+       }
+       Py_XDECREF(list);
+       PyErr_Clear();
+}
+
+static int cpy_read_callback(user_data_t *data) {
+       cpy_callback_t *c = data->data;
+       PyObject *ret;
+
+       CPY_LOCK_THREADS
+               ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
+               if (ret == NULL) {
+                       cpy_log_exception("read callback");
+               } else {
+                       Py_DECREF(ret);
+               }
+       CPY_RELEASE_THREADS
+       if (ret == NULL)
+               return 1;
+       return 0;
+}
+
+static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
+       int i;
+       cpy_callback_t *c = data->data;
+       PyObject *ret, *v, *list;
+
+       CPY_LOCK_THREADS
+               list = PyList_New(value_list->values_len); /* New reference. */
+               if (list == NULL) {
+                       cpy_log_exception("write callback");
+                       CPY_RETURN_FROM_THREADS 0;
+               }
+               for (i = 0; i < value_list->values_len; ++i) {
+                       if (ds->ds->type == DS_TYPE_COUNTER) {
+                               if ((long) value_list->values[i].counter == value_list->values[i].counter)
+                                       PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
+                               else
+                                       PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
+                       } else if (ds->ds->type == DS_TYPE_GAUGE) {
+                               PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
+                       } else if (ds->ds->type == DS_TYPE_DERIVE) {
+                               if ((long) value_list->values[i].derive == value_list->values[i].derive)
+                                       PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
+                               else
+                                       PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
+                       } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
+                               if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
+                                       PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
+                               else
+                                       PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
+                       } else {
+                               Py_BEGIN_ALLOW_THREADS
+                               ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
+                               Py_END_ALLOW_THREADS
+                               Py_DECREF(list);
+                               CPY_RETURN_FROM_THREADS 0;
+                       }
+                       if (PyErr_Occurred() != NULL) {
+                               cpy_log_exception("value building for write callback");
+                               CPY_RETURN_FROM_THREADS 0;
+                       }
+               }
+               v = PyObject_CallFunction((void *) &ValuesType, "sOssssdi", value_list->type, list,
+                               value_list->plugin_instance, value_list->type_instance, value_list->plugin,
+                               value_list->host, (double) value_list->time, value_list->interval);
+               Py_DECREF(list);
+               ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
+               if (ret == NULL) {
+                       cpy_log_exception("write callback");
+               } else {
+                       Py_DECREF(ret);
+               }
+       CPY_RELEASE_THREADS
+       return 0;
+}
+
+static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
+       cpy_callback_t *c = data->data;
+       PyObject *ret, *n;
+
+       CPY_LOCK_THREADS
+               n = PyObject_CallFunction((void *) &NotificationType, "ssssssdi", notification->type, notification->message,
+                               notification->plugin_instance, notification->type_instance, notification->plugin,
+                               notification->host, (double) notification->time, notification->severity);
+               ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
+               if (ret == NULL) {
+                       cpy_log_exception("notification callback");
+               } else {
+                       Py_DECREF(ret);
+               }
+       CPY_RELEASE_THREADS
+       return 0;
+}
+
+static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
+       cpy_callback_t * c = data->data;
+       PyObject *ret;
+
+       CPY_LOCK_THREADS
+       if (c->data == NULL)
+               ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
+       else
+               ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
+
+       if (ret == NULL) {
+               /* FIXME */
+               /* Do we really want to trigger a log callback because a log callback failed?
+                * Probably not. */
+               PyErr_Print();
+               /* In case someone wanted to be clever, replaced stderr and failed at that. */
+               PyErr_Clear();
+       } else {
+               Py_DECREF(ret);
+       }
+       CPY_RELEASE_THREADS
+}
+
+static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
+       cpy_callback_t * c = data->data;
+       PyObject *ret;
+
+       CPY_LOCK_THREADS
+       if (c->data == NULL)
+               ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
+       else
+               ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
+
+       if (ret == NULL) {
+               cpy_log_exception("flush callback");
+       } else {
+               Py_DECREF(ret);
+       }
+       CPY_RELEASE_THREADS
+}
+
+static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
+       char buf[512];
+       cpy_callback_t *c;
+       const char *name = NULL;
+       PyObject *callback = NULL, *data = NULL, *mod = NULL;
+       static char *kwlist[] = {"callback", "data", "name", NULL};
+       
+       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
+       if (PyCallable_Check(callback) == 0) {
+               PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
+               return NULL;
+       }
+       cpy_build_name(buf, sizeof(buf), callback, name);
+
+       Py_INCREF(callback);
+       Py_XINCREF(data);
+       c = malloc(sizeof(*c));
+       c->name = strdup(buf);
+       c->callback = callback;
+       c->data = data;
+       c->next = *list_head;
+       *list_head = c;
+       Py_XDECREF(mod);
+       return PyString_FromString(buf);
+}
+
+static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
+       int timeout = -1;
+       const char *plugin = NULL, *identifier = NULL;
+       static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
+       
+       if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
+       Py_BEGIN_ALLOW_THREADS
+       plugin_flush(plugin, timeout, identifier);
+       Py_END_ALLOW_THREADS
+       Py_RETURN_NONE;
+}
+
+static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
+       return cpy_register_generic(&cpy_config_callbacks, args, kwds);
+}
+
+static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
+       return cpy_register_generic(&cpy_init_callbacks, args, kwds);
+}
+
+typedef int reg_function_t(const char *name, void *callback, void *data);
+
+static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
+       char buf[512];
+       reg_function_t *register_function = (reg_function_t *) reg;
+       cpy_callback_t *c = NULL;
+       user_data_t *user_data = NULL;
+       const char *name = NULL;
+       PyObject *callback = NULL, *data = NULL;
+       static char *kwlist[] = {"callback", "data", "name", NULL};
+       
+       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
+       if (PyCallable_Check(callback) == 0) {
+               PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
+               return NULL;
+       }
+       cpy_build_name(buf, sizeof(buf), callback, name);
+       
+       Py_INCREF(callback);
+       Py_XINCREF(data);
+       c = malloc(sizeof(*c));
+       c->name = strdup(buf);
+       c->callback = callback;
+       c->data = data;
+       c->next = NULL;
+       user_data = malloc(sizeof(*user_data));
+       user_data->free_func = cpy_destroy_user_data;
+       user_data->data = c;
+       register_function(buf, handler, user_data);
+       return PyString_FromString(buf);
+}
+
+static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
+       char buf[512];
+       cpy_callback_t *c = NULL;
+       user_data_t *user_data = NULL;
+       double interval = 0;
+       const char *name = NULL;
+       PyObject *callback = NULL, *data = NULL;
+       struct timespec ts;
+       static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
+       
+       if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
+       if (PyCallable_Check(callback) == 0) {
+               PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
+               return NULL;
+       }
+       cpy_build_name(buf, sizeof(buf), callback, name);
+       
+       Py_INCREF(callback);
+       Py_XINCREF(data);
+       c = malloc(sizeof(*c));
+       c->name = strdup(buf);
+       c->callback = callback;
+       c->data = data;
+       c->next = NULL;
+       user_data = malloc(sizeof(*user_data));
+       user_data->free_func = cpy_destroy_user_data;
+       user_data->data = c;
+       ts.tv_sec = interval;
+       ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
+       plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
+       return PyString_FromString(buf);
+}
+
+static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
+       return cpy_register_generic_userdata((void *) plugin_register_log,
+                       (void *) cpy_log_callback, args, kwds);
+}
+
+static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
+       return cpy_register_generic_userdata((void *) plugin_register_write,
+                       (void *) cpy_write_callback, args, kwds);
+}
+
+static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
+       return cpy_register_generic_userdata((void *) plugin_register_notification,
+                       (void *) cpy_notification_callback, args, kwds);
+}
+
+static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
+       return cpy_register_generic_userdata((void *) plugin_register_flush,
+                       (void *) cpy_flush_callback, args, kwds);
+}
+
+static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
+       return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
+}
+
+static PyObject *cpy_error(PyObject *self, PyObject *args) {
+       const char *text;
+       if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+       Py_BEGIN_ALLOW_THREADS
+       plugin_log(LOG_ERR, "%s", text);
+       Py_END_ALLOW_THREADS
+       Py_RETURN_NONE;
+}
+
+static PyObject *cpy_warning(PyObject *self, PyObject *args) {
+       const char *text;
+       if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+       Py_BEGIN_ALLOW_THREADS
+       plugin_log(LOG_WARNING, "%s", text);
+       Py_END_ALLOW_THREADS
+       Py_RETURN_NONE;
+}
+
+static PyObject *cpy_notice(PyObject *self, PyObject *args) {
+       const char *text;
+       if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+       Py_BEGIN_ALLOW_THREADS
+       plugin_log(LOG_NOTICE, "%s", text);
+       Py_END_ALLOW_THREADS
+       Py_RETURN_NONE;
+}
+
+static PyObject *cpy_info(PyObject *self, PyObject *args) {
+       const char *text;
+       if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+       Py_BEGIN_ALLOW_THREADS
+       plugin_log(LOG_INFO, "%s", text);
+       Py_END_ALLOW_THREADS
+       Py_RETURN_NONE;
+}
+
+static PyObject *cpy_debug(PyObject *self, PyObject *args) {
+#ifdef COLLECT_DEBUG
+       const char *text;
+       if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+       Py_BEGIN_ALLOW_THREADS
+       plugin_log(LOG_DEBUG, "%s", text);
+       Py_END_ALLOW_THREADS
+#endif
+       Py_RETURN_NONE;
+}
+
+static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
+       char buf[512];
+       const char *name;
+       cpy_callback_t *prev = NULL, *tmp;
+
+       if (PyUnicode_Check(arg)) {
+               arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
+               if (arg == NULL)
+                       return NULL;
+               name = PyString_AsString(arg);
+               Py_DECREF(arg);
+       } else if (PyString_Check(arg)) {
+               name = PyString_AsString(arg);
+       } else {
+               if (!PyCallable_Check(arg)) {
+                       PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
+                       return NULL;
+               }
+               cpy_build_name(buf, sizeof(buf), arg, NULL);
+               name = buf;
+       }
+       for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
+               if (strcmp(name, tmp->name) == 0)
+                       break;
+       
+       if (tmp == NULL) {
+               PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
+               return NULL;
+       }
+       /* Yes, this is actually save. To call this function the caller has to
+        * hold the GIL. Well, save as long as there is only one GIL anyway ... */
+       if (prev == NULL)
+               *list_head = tmp->next;
+       else
+               prev->next = tmp->next;
+       cpy_destroy_user_data(tmp);
+       Py_RETURN_NONE;
+}
+
+typedef int cpy_unregister_function_t(const char *name);
+
+static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
+       char buf[512];
+       const char *name;
+
+       if (PyUnicode_Check(arg)) {
+               arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
+               if (arg == NULL)
+                       return NULL;
+               name = PyString_AsString(arg);
+               Py_DECREF(arg);
+       } else if (PyString_Check(arg)) {
+               name = PyString_AsString(arg);
+       } else {
+               if (!PyCallable_Check(arg)) {
+                       PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
+                       return NULL;
+               }
+               cpy_build_name(buf, sizeof(buf), arg, NULL);
+               name = buf;
+       }
+       if (unreg(name) == 0)
+               Py_RETURN_NONE;
+       PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
+       return NULL;
+}
+
+static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
+       return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
+}
+
+static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
+       return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
+}
+
+static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
+       return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
+}
+
+static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
+       return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
+}
+
+static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
+       return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
+}
+
+static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
+       return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
+}
+
+static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
+       return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
+}
+
+static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
+       return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
+}
+
+static PyMethodDef cpy_methods[] = {
+       {"debug", cpy_debug, METH_VARARGS, log_doc},
+       {"info", cpy_info, METH_VARARGS, log_doc},
+       {"notice", cpy_notice, METH_VARARGS, log_doc},
+       {"warning", cpy_warning, METH_VARARGS, log_doc},
+       {"error", cpy_error, METH_VARARGS, log_doc},
+       {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
+       {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
+       {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
+       {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
+       {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
+       {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
+       {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
+       {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
+       {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
+       {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
+       {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
+       {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
+       {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
+       {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
+       {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
+       {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
+       {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
+       {0, 0, 0, 0}
+};
+
+static int cpy_shutdown(void) {
+       cpy_callback_t *c;
+       PyObject *ret;
+       
+       /* This can happen if the module was loaded but not configured. */
+       if (state != NULL)
+               PyEval_RestoreThread(state);
+
+       for (c = cpy_shutdown_callbacks; c; c = c->next) {
+               ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
+               if (ret == NULL)
+                       cpy_log_exception("shutdown callback");
+               else
+                       Py_DECREF(ret);
+       }
+       PyErr_Print();
+       Py_Finalize();
+       return 0;
+}
+
+static void cpy_int_handler(int sig) {
+       return;
+}
+
+static void *cpy_interactive(void *data) {
+       sigset_t sigset;
+       struct sigaction sig_int_action, old;
+       
+       /* Signal handler in a plugin? Bad stuff, but the best way to
+        * handle it I guess. In an interactive session people will
+        * press Ctrl+C at some time, which will generate a SIGINT.
+        * This will cause collectd to shutdown, thus killing the
+        * interactive interpreter, and leaving the terminal in a
+        * mess. Chances are, this isn't what the user wanted to do.
+        * 
+        * So this is the plan:
+        * 1. Block SIGINT in the main thread.
+        * 2. Install our own signal handler that does nothing.
+        * 3. Unblock SIGINT in the interactive thread.
+        *
+        * This will make sure that SIGINT won't kill collectd but
+        * still interrupt syscalls like sleep and pause.
+        * It does not raise a KeyboardInterrupt exception because so
+        * far nobody managed to figure out how to do that. */
+       memset (&sig_int_action, '\0', sizeof (sig_int_action));
+       sig_int_action.sa_handler = cpy_int_handler;
+       sigaction (SIGINT, &sig_int_action, &old);
+       
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGINT);
+       pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
+       PyEval_AcquireThread(state);
+       if (PyImport_ImportModule("readline") == NULL) {
+               /* This interactive session will suck. */
+               cpy_log_exception("interactive session init");
+       }
+       PyRun_InteractiveLoop(stdin, "<stdin>");
+       PyErr_Print();
+       PyEval_ReleaseThread(state);
+       NOTICE("python: Interactive interpreter exited, stopping collectd ...");
+       /* Restore the original collectd SIGINT handler and raise SIGINT.
+        * The main thread still has SIGINT blocked and there's nothing we
+        * can do about that so this thread will handle it. But that's not
+        * important, except that it won't interrupt the main loop and so
+        * it might take a few seconds before collectd really shuts down. */
+       sigaction (SIGINT, &old, NULL);
+       raise(SIGINT);
+       pause();
+       return NULL;
+}
+
+static int cpy_init(void) {
+       cpy_callback_t *c;
+       PyObject *ret;
+       static pthread_t thread;
+       sigset_t sigset;
+       
+       PyEval_InitThreads();
+       /* Now it's finally OK to use python threads. */
+       for (c = cpy_init_callbacks; c; c = c->next) {
+               ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
+               if (ret == NULL)
+                       cpy_log_exception("init callback");
+               else
+                       Py_DECREF(ret);
+       }
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGINT);
+       pthread_sigmask(SIG_BLOCK, &sigset, NULL);
+       state = PyEval_SaveThread();
+       if (do_interactive) {
+               if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
+                       ERROR("python: Error creating thread for interactive interpreter.");
+               }
+       }
+
+       return 0;
+}
+
+static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
+       int i;
+       PyObject *item, *values, *children, *tmp;
+       
+       if (parent == NULL)
+               parent = Py_None;
+       
+       values = PyTuple_New(ci->values_num); /* New reference. */
+       for (i = 0; i < ci->values_num; ++i) {
+               if (ci->values[i].type == OCONFIG_TYPE_STRING) {
+                       PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
+               } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
+                       PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
+               } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
+                       PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
+               }
+       }
+       
+       item = PyObject_CallFunction((void *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
+       if (item == NULL)
+               return NULL;
+       children = PyTuple_New(ci->children_num); /* New reference. */
+       for (i = 0; i < ci->children_num; ++i) {
+               PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
+       }
+       tmp = ((Config *) item)->children;
+       ((Config *) item)->children = children;
+       Py_XDECREF(tmp);
+       return item;
+}
+
+static int cpy_config(oconfig_item_t *ci) {
+       int i;
+       PyObject *sys, *tb;
+       PyObject *sys_path;
+       PyObject *module;
+       
+       /* Ok in theory we shouldn't do initialization at this point
+        * but we have to. In order to give python scripts a chance
+        * to register a config callback we need to be able to execute
+        * python code during the config callback so we have to start
+        * the interpreter here. */
+       /* Do *not* use the python "thread" module at this point! */
+       Py_Initialize();
+       
+       PyType_Ready(&ConfigType);
+       PyType_Ready(&PluginDataType);
+       ValuesType.tp_base = &PluginDataType;
+       PyType_Ready(&ValuesType);
+       NotificationType.tp_base = &PluginDataType;
+       PyType_Ready(&NotificationType);
+       sys = PyImport_ImportModule("sys"); /* New reference. */
+       if (sys == NULL) {
+               cpy_log_exception("python initialization");
+               return 1;
+       }
+       sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
+       Py_DECREF(sys);
+       if (sys_path == NULL) {
+               cpy_log_exception("python initialization");
+               return 1;
+       }
+       module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
+       PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
+       PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
+       PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
+       PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
+       PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
+       PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
+       PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
+       PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
+       PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
+       PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
+       PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *item = ci->children + i;
+               
+               if (strcasecmp(item->key, "Interactive") == 0) {
+                       if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
+                               continue;
+                       do_interactive = item->values[0].value.boolean;
+               } else if (strcasecmp(item->key, "Encoding") == 0) {
+                       if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_STRING)
+                               continue;
+                       /* Why is this even necessary? And undocumented? */
+                       if (PyUnicode_SetDefaultEncoding(item->values[0].value.string))
+                               cpy_log_exception("setting default encoding");
+               } else if (strcasecmp(item->key, "LogTraces") == 0) {
+                       if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
+                               continue;
+                       if (!item->values[0].value.boolean) {
+                               Py_XDECREF(cpy_format_exception);
+                               cpy_format_exception = NULL;
+                               continue;
+                       }
+                       if (cpy_format_exception)
+                               continue;
+                       tb = PyImport_ImportModule("traceback"); /* New reference. */
+                       if (tb == NULL) {
+                               cpy_log_exception("python initialization");
+                               continue;
+                       }
+                       cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
+                       Py_DECREF(tb);
+                       if (cpy_format_exception == NULL)
+                               cpy_log_exception("python initialization");
+               } else if (strcasecmp(item->key, "ModulePath") == 0) {
+                       char *dir = NULL;
+                       PyObject *dir_object;
+                       
+                       if (cf_util_get_string(item, &dir) != 0) 
+                               continue;
+                       dir_object = PyString_FromString(dir); /* New reference. */
+                       if (dir_object == NULL) {
+                               ERROR("python plugin: Unable to convert \"%s\" to "
+                                     "a python object.", dir);
+                               free(dir);
+                               cpy_log_exception("python initialization");
+                               continue;
+                       }
+                       if (PyList_Append(sys_path, dir_object) != 0) {
+                               ERROR("python plugin: Unable to append \"%s\" to "
+                                     "python module path.", dir);
+                               cpy_log_exception("python initialization");
+                       }
+                       Py_DECREF(dir_object);
+                       free(dir);
+               } else if (strcasecmp(item->key, "Import") == 0) {
+                       char *module_name = NULL;
+                       PyObject *module;
+                       
+                       if (cf_util_get_string(item, &module_name) != 0) 
+                               continue;
+                       module = PyImport_ImportModule(module_name); /* New reference. */
+                       if (module == NULL) {
+                               ERROR("python plugin: Error importing module \"%s\".", module_name);
+                               cpy_log_exception("importing module");
+                               PyErr_Print();
+                       }
+                       free(module_name);
+                       Py_XDECREF(module);
+               } else if (strcasecmp(item->key, "Module") == 0) {
+                       char *name = NULL;
+                       cpy_callback_t *c;
+                       PyObject *ret;
+                       
+                       if (cf_util_get_string(item, &name) != 0)
+                               continue;
+                       for (c = cpy_config_callbacks; c; c = c->next) {
+                               if (strcasecmp(c->name + 7, name) == 0)
+                                       break;
+                       }
+                       if (c == NULL) {
+                               WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
+                                       "but the plugin isn't loaded or didn't register "
+                                       "a configuration callback.", name);
+                               free(name);
+                               continue;
+                       }
+                       free(name);
+                       if (c->data == NULL)
+                               ret = PyObject_CallFunction(c->callback, "N",
+                                       cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
+                       else
+                               ret = PyObject_CallFunction(c->callback, "NO",
+                                       cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
+                       if (ret == NULL)
+                               cpy_log_exception("loading module");
+                       else
+                               Py_DECREF(ret);
+               } else {
+                       WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
+               }
+       }
+       Py_DECREF(sys_path);
+       return 0;
+}
+
+void module_register(void) {
+       plugin_register_complex_config("python", cpy_config);
+       plugin_register_init("python", cpy_init);
+       plugin_register_shutdown("python", cpy_shutdown);
+}
diff --git a/src/pyvalues.c b/src/pyvalues.c
new file mode 100644 (file)
index 0000000..d83f541
--- /dev/null
@@ -0,0 +1,780 @@
+/**
+ * collectd - src/pyvalues.c
+ * Copyright (C) 2009  Sven Trenkel
+ *
+ * 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:
+ *   Sven Trenkel <collectd at semidefinite.de>  
+ **/
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "collectd.h"
+#include "common.h"
+
+#include "cpython.h"
+
+static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
+               "For dispatching values this can be set to 0 which means \"now\".\n"
+               "This means the time the value is actually dispatched, not the time\n"
+               "it was set to 0.";
+
+static char host_doc[] = "The hostname of the host this value was read from.\n"
+               "For dispatching this can be set to an empty string which means\n"
+               "the local hostname as defined in the collectd.conf.";
+
+static char type_doc[] = "The type of this value. This type has to be defined\n"
+               "in your types.db. Attempting to set it to any other value will\n"
+               "raise a TypeError exception.\n"
+               "Assigning a type is mandetory, calling dispatch without doing\n"
+               "so will raise a RuntimeError exception.";
+
+static char type_instance_doc[] = "";
+
+static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
+               "member to an empty string will insert \"python\" upon dispatching.";
+
+static char plugin_instance_doc[] = "";
+
+static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
+               "and Notification. It is pretty useless by itself and was therefore not\n"
+               "exported to the collectd module.";
+
+static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
+       PluginData *self;
+       
+       self = (PluginData *) type->tp_alloc(type, 0);
+       if (self == NULL)
+               return NULL;
+       
+       self->time = 0;
+       self->host[0] = 0;
+       self->plugin[0] = 0;
+       self->plugin_instance[0] = 0;
+       self->type[0] = 0;
+       self->type_instance[0] = 0;
+       return (PyObject *) self;
+}
+
+static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
+       PluginData *self = (PluginData *) s;
+       double time = 0;
+       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       static char *kwlist[] = {"type", "plugin_instance", "type_instance",
+                       "plugin", "host", "time", NULL};
+       
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sssssd", kwlist, &type,
+                       &plugin_instance, &type_instance, &plugin, &host, &time))
+               return -1;
+       
+       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               return -1;
+       }
+
+       sstrncpy(self->host, host, sizeof(self->host));
+       sstrncpy(self->plugin, plugin, sizeof(self->plugin));
+       sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
+       sstrncpy(self->type, type, sizeof(self->type));
+       sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
+       
+       self->time = time;
+       return 0;
+}
+
+static PyObject *PluginData_repr(PyObject *s) {
+       PluginData *self = (PluginData *) s;
+       
+       return PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu)", self->type,
+                       *self->type_instance ? "',type_instance='" : "", self->type_instance,
+                       *self->plugin ? "',plugin='" : "", self->plugin,
+                       *self->plugin_instance ? "',plugin_instance='" : "", self->plugin_instance,
+                       *self->host ? "',host='" : "", self->host,
+                       (long unsigned) self->time);
+}
+
+static PyMemberDef PluginData_members[] = {
+       {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
+       {NULL}
+};
+
+static PyObject *PluginData_getstring(PyObject *self, void *data) {
+       const char *value = ((char *) self) + (intptr_t) data;
+       
+       return PyString_FromString(value);
+}
+
+static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
+       char *old;
+       const char *new;
+       
+       if (value == NULL) {
+               PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
+               return -1;
+       }
+       new = PyString_AsString(value);
+       if (new == NULL) return -1;
+       old = ((char *) self) + (intptr_t) data;
+       sstrncpy(old, new, DATA_MAX_NAME_LEN);
+       return 0;
+}
+
+static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
+       char *old;
+       const char *new;
+       
+       if (value == NULL) {
+               PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
+               return -1;
+       }
+       new = PyString_AsString(value);
+       if (new == NULL) return -1;
+
+       if (plugin_get_ds(new) == NULL) {
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
+               return -1;
+       }
+
+       old = ((char *) self) + (intptr_t) data;
+       sstrncpy(old, new, DATA_MAX_NAME_LEN);
+       return 0;
+}
+
+static PyGetSetDef PluginData_getseters[] = {
+       {"host", PluginData_getstring, PluginData_setstring, host_doc, (void *) offsetof(PluginData, host)},
+       {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc, (void *) offsetof(PluginData, plugin)},
+       {"plugin_instance", PluginData_getstring, PluginData_setstring, plugin_instance_doc, (void *) offsetof(PluginData, plugin_instance)},
+       {"type_instance", PluginData_getstring, PluginData_setstring, type_instance_doc, (void *) offsetof(PluginData, type_instance)},
+       {"type", PluginData_getstring, PluginData_settype, type_doc, (void *) offsetof(PluginData, type)},
+       {NULL}
+};
+
+PyTypeObject PluginDataType = {
+       PyObject_HEAD_INIT(NULL)
+       0,                         /* Always 0 */
+       "collectd.PluginData",     /* tp_name */
+       sizeof(PluginData),        /* tp_basicsize */
+       0,                         /* Will be filled in later */
+       0,                         /* tp_dealloc */
+       0,                         /* tp_print */
+       0,                         /* tp_getattr */
+       0,                         /* tp_setattr */
+       0,                         /* tp_compare */
+       PluginData_repr,           /* tp_repr */
+       0,                         /* tp_as_number */
+       0,                         /* tp_as_sequence */
+       0,                         /* tp_as_mapping */
+       0,                         /* tp_hash */
+       0,                         /* tp_call */
+       0,                         /* tp_str */
+       0,                         /* tp_getattro */
+       0,                         /* tp_setattro */
+       0,                         /* tp_as_buffer */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
+       PluginData_doc,            /* tp_doc */
+       0,                         /* tp_traverse */
+       0,                         /* tp_clear */
+       0,                         /* tp_richcompare */
+       0,                         /* tp_weaklistoffset */
+       0,                         /* tp_iter */
+       0,                         /* tp_iternext */
+       0,                         /* tp_methods */
+       PluginData_members,        /* tp_members */
+       PluginData_getseters,      /* tp_getset */
+       0,                         /* tp_base */
+       0,                         /* tp_dict */
+       0,                         /* tp_descr_get */
+       0,                         /* tp_descr_set */
+       0,                         /* tp_dictoffset */
+       PluginData_init,           /* tp_init */
+       0,                         /* tp_alloc */
+       PluginData_new             /* tp_new */
+};
+
+static char interval_doc[] = "The interval is the timespan in seconds between two submits for\n"
+               "the same data source. This value has to be a positive integer, so you can't\n"
+               "submit more than one value per second. If this member is set to a\n"
+               "non-positive value, the default value as specified in the config file will\n"
+               "be used (default: 10).\n"
+               "\n"
+               "If you submit values more often than the specified interval, the average\n"
+               "will be used. If you submit less values, your graphs will have gaps.";
+
+static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
+               "It has to be a sequence (a tuple or list) of numbers.\n"
+               "The size of the sequence and the type of its content depend on the type\n"
+               "member your types.db file. For more information on this read the types.db\n"
+               "man page.\n"
+               "\n"
+               "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
+               "exception will be raised. If the content of the sequence is not a number,\n"
+               "a TypeError exception will be raised.";
+
+static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
+               "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
+               "\n"
+               "Dispatch this instance to the collectd process. The object has members\n"
+               "for each of the possible arguments for this method. For a detailed explanation\n"
+               "of these parameters see the member of the same same.\n"
+               "\n"
+               "If you do not submit a parameter the value saved in its member will be submitted.\n"
+               "If you do provide a parameter it will be used instead, without altering the member.";
+
+static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
+               "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
+               "\n"
+               "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
+               "This will bypass the main collectd process and all filtering and caching.\n"
+               "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
+               "used instead of 'write'.\n";
+
+static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
+
+static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
+       Values *self;
+       
+       self = (Values *) PluginData_new(type, args, kwds);
+       if (self == NULL)
+               return NULL;
+       
+       self->values = PyList_New(0);
+       self->interval = 0;
+       return (PyObject *) self;
+}
+
+static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
+       Values *self = (Values *) s;
+       int interval = 0, ret;
+       double time = 0;
+       PyObject *values = NULL, *tmp;
+       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
+                       "plugin", "host", "time", "interval", NULL};
+       
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
+                       &type, &values, &plugin_instance, &type_instance,
+                       &plugin, &host, &time, &interval))
+               return -1;
+       
+       tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
+       if (tmp == NULL)
+               return -1;
+       ret = PluginDataType.tp_init(s, tmp, NULL);
+       Py_DECREF(tmp);
+       if (ret != 0)
+               return -1;
+       
+       if (values == NULL) {
+               values = PyList_New(0);
+               PyErr_Clear();
+       } else {
+               Py_INCREF(values);
+       }
+       
+       tmp = self->values;
+       self->values = values;
+       Py_XDECREF(tmp);
+       
+       self->interval = interval;
+       return 0;
+}
+
+static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
+       int i, ret;
+       const data_set_t *ds;
+       int size;
+       value_t *value;
+       value_list_t value_list = VALUE_LIST_INIT;
+       PyObject *values = self->values;
+       double time = self->data.time;
+       int interval = self->interval;
+       const char *host = self->data.host;
+       const char *plugin = self->data.plugin;
+       const char *plugin_instance = self->data.plugin_instance;
+       const char *type = self->data.type;
+       const char *type_instance = self->data.type_instance;
+       
+       static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
+                       "plugin", "host", "time", "interval", NULL};
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
+                       &type, &values, &plugin_instance, &type_instance,
+                       &plugin, &host, &time, &interval))
+               return NULL;
+
+       if (type[0] == 0) {
+               PyErr_SetString(PyExc_RuntimeError, "type not set");
+               return NULL;
+       }
+       ds = plugin_get_ds(type);
+       if (ds == NULL) {
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               return NULL;
+       }
+       if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
+               PyErr_Format(PyExc_TypeError, "values must be list or tuple");
+               return NULL;
+       }
+       size = (int) PySequence_Length(values);
+       if (size != ds->ds_num) {
+               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
+               return NULL;
+       }
+       value = malloc(size * sizeof(*value));
+       for (i = 0; i < size; ++i) {
+               PyObject *item, *num;
+               item = PySequence_GetItem(values, i);
+               if (ds->ds->type == DS_TYPE_COUNTER) {
+                       num = PyNumber_Long(item);
+                       if (num != NULL)
+                               value[i].counter = PyLong_AsUnsignedLongLong(num);
+               } else if (ds->ds->type == DS_TYPE_GAUGE) {
+                       num = PyNumber_Float(item);
+                       if (num != NULL)
+                               value[i].gauge = PyFloat_AsDouble(num);
+               } else if (ds->ds->type == DS_TYPE_DERIVE) {
+                       /* This might overflow without raising an exception.
+                        * Not much we can do about it */
+                       num = PyNumber_Long(item);
+                       if (num != NULL)
+                               value[i].derive = PyLong_AsLongLong(num);
+               } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
+                       /* This might overflow without raising an exception.
+                        * Not much we can do about it */
+                       num = PyNumber_Long(item);
+                       if (num != NULL)
+                               value[i].absolute = PyLong_AsUnsignedLongLong(num);
+               } else {
+                       free(value);
+                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
+                       return NULL;
+               }
+               if (PyErr_Occurred() != NULL) {
+                       free(value);
+                       return NULL;
+               }
+       }
+       value_list.values = value;
+       value_list.values_len = size;
+       value_list.time = time;
+       value_list.interval = interval;
+       sstrncpy(value_list.host, host, sizeof(value_list.host));
+       sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
+       sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
+       sstrncpy(value_list.type, type, sizeof(value_list.type));
+       sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
+       value_list.meta = NULL;
+       if (value_list.host[0] == 0)
+               sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
+       if (value_list.plugin[0] == 0)
+               sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
+       Py_BEGIN_ALLOW_THREADS;
+       ret = plugin_dispatch_values(&value_list);
+       Py_END_ALLOW_THREADS;
+       if (ret != 0) {
+               PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
+               return NULL;
+       }
+       free(value);
+       Py_RETURN_NONE;
+}
+
+static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
+       int i, ret;
+       const data_set_t *ds;
+       int size;
+       value_t *value;
+       value_list_t value_list = VALUE_LIST_INIT;
+       PyObject *values = self->values;
+       double time = self->data.time;
+       int interval = self->interval;
+       const char *host = self->data.host;
+       const char *plugin = self->data.plugin;
+       const char *plugin_instance = self->data.plugin_instance;
+       const char *type = self->data.type;
+       const char *type_instance = self->data.type_instance;
+       const char *dest = NULL;
+       
+       static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
+                       "plugin", "host", "time", "interval", NULL};
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
+                       &type, &values, &plugin_instance, &type_instance,
+                       &plugin, &host, &time, &interval))
+               return NULL;
+
+       if (type[0] == 0) {
+               PyErr_SetString(PyExc_RuntimeError, "type not set");
+               return NULL;
+       }
+       ds = plugin_get_ds(type);
+       if (ds == NULL) {
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               return NULL;
+       }
+       if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
+               PyErr_Format(PyExc_TypeError, "values must be list or tuple");
+               return NULL;
+       }
+       size = (int) PySequence_Length(values);
+       if (size != ds->ds_num) {
+               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
+               return NULL;
+       }
+       value = malloc(size * sizeof(*value));
+       for (i = 0; i < size; ++i) {
+               PyObject *item, *num;
+               item = PySequence_GetItem(values, i);
+               if (ds->ds->type == DS_TYPE_COUNTER) {
+                       num = PyNumber_Long(item);
+                       if (num != NULL)
+                               value[i].counter = PyLong_AsUnsignedLongLong(num);
+               } else if (ds->ds->type == DS_TYPE_GAUGE) {
+                       num = PyNumber_Float(item);
+                       if (num != NULL)
+                               value[i].gauge = PyFloat_AsDouble(num);
+               } else if (ds->ds->type == DS_TYPE_DERIVE) {
+                       /* This might overflow without raising an exception.
+                        * Not much we can do about it */
+                       num = PyNumber_Long(item);
+                       if (num != NULL)
+                               value[i].derive = PyLong_AsLongLong(num);
+               } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
+                       /* This might overflow without raising an exception.
+                        * Not much we can do about it */
+                       num = PyNumber_Long(item);
+                       if (num != NULL)
+                               value[i].absolute = PyLong_AsUnsignedLongLong(num);
+               } else {
+                       free(value);
+                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
+                       return NULL;
+               }
+               if (PyErr_Occurred() != NULL) {
+                       free(value);
+                       return NULL;
+               }
+       }
+       value_list.values = value;
+       value_list.values_len = size;
+       value_list.time = time;
+       value_list.interval = interval;
+       sstrncpy(value_list.host, host, sizeof(value_list.host));
+       sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
+       sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
+       sstrncpy(value_list.type, type, sizeof(value_list.type));
+       sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
+       value_list.meta = NULL;
+       if (value_list.host[0] == 0)
+               sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
+       if (value_list.plugin[0] == 0)
+               sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
+       Py_BEGIN_ALLOW_THREADS;
+       ret = plugin_write(dest, NULL, &value_list);
+       Py_END_ALLOW_THREADS;
+       if (ret != 0) {
+               PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
+               return NULL;
+       }
+       free(value);
+       Py_RETURN_NONE;
+}
+
+static PyObject *Values_repr(PyObject *s) {
+       PyObject *ret, *valuestring = NULL;
+       Values *self = (Values *) s;
+       
+       if (self->values != NULL)
+               valuestring = PyObject_Repr(self->values);
+       if (valuestring == NULL)
+               return NULL;
+       
+       ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i,values=%s)", self->data.type,
+                       *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
+                       *self->data.plugin ? "',plugin='" : "", self->data.plugin,
+                       *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
+                       *self->data.host ? "',host='" : "", self->data.host,
+                       (long unsigned) self->data.time, self->interval,
+                       valuestring ? PyString_AsString(valuestring) : "[]");
+       Py_XDECREF(valuestring);
+       return ret;
+}
+
+static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
+       Values *v = (Values *) self;
+       Py_VISIT(v->values);
+       return 0;
+}
+
+static int Values_clear(PyObject *self) {
+       Values *v = (Values *) self;
+       Py_CLEAR(v->values);
+       return 0;
+}
+
+static void Values_dealloc(PyObject *self) {
+       Values_clear(self);
+       self->ob_type->tp_free(self);
+}
+
+static PyMemberDef Values_members[] = {
+       {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
+       {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
+       {NULL}
+};
+
+static PyMethodDef Values_methods[] = {
+       {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
+       {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
+       {NULL}
+};
+
+PyTypeObject ValuesType = {
+       PyObject_HEAD_INIT(NULL)
+       0,                         /* Always 0 */
+       "collectd.Values",         /* tp_name */
+       sizeof(Values),            /* tp_basicsize */
+       0,                         /* Will be filled in later */
+       Values_dealloc,            /* tp_dealloc */
+       0,                         /* tp_print */
+       0,                         /* tp_getattr */
+       0,                         /* tp_setattr */
+       0,                         /* tp_compare */
+       Values_repr,               /* tp_repr */
+       0,                         /* tp_as_number */
+       0,                         /* tp_as_sequence */
+       0,                         /* tp_as_mapping */
+       0,                         /* tp_hash */
+       0,                         /* tp_call */
+       0,                         /* tp_str */
+       0,                         /* tp_getattro */
+       0,                         /* tp_setattro */
+       0,                         /* tp_as_buffer */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+       Values_doc,                /* tp_doc */
+       Values_traverse,           /* tp_traverse */
+       Values_clear,              /* tp_clear */
+       0,                         /* tp_richcompare */
+       0,                         /* tp_weaklistoffset */
+       0,                         /* tp_iter */
+       0,                         /* tp_iternext */
+       Values_methods,            /* tp_methods */
+       Values_members,            /* tp_members */
+       0,                         /* tp_getset */
+       0,                         /* tp_base */
+       0,                         /* tp_dict */
+       0,                         /* tp_descr_get */
+       0,                         /* tp_descr_set */
+       0,                         /* tp_dictoffset */
+       Values_init,               /* tp_init */
+       0,                         /* tp_alloc */
+       Values_new                 /* tp_new */
+};
+
+static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
+               "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
+
+static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
+
+static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
+               "It can be used to notify other plugins about bad stuff happening. It works\n"
+               "similar to Values but has a severity and a message instead of interval\n"
+               "and time.\n"
+               "Notifications can be dispatched at any time and can be received with register_notification.";
+
+static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
+       Notification *self = (Notification *) s;
+       PyObject *tmp;
+       int severity = 0, ret;
+       double time = 0;
+       const char *message = "";
+       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
+                       "plugin", "host", "time", "severity", NULL};
+       
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
+                       &type, &message, &plugin_instance, &type_instance,
+                       &plugin, &host, &time, &severity))
+               return -1;
+       
+       tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
+       if (tmp == NULL)
+               return -1;
+       ret = PluginDataType.tp_init(s, tmp, NULL);
+       Py_DECREF(tmp);
+       if (ret != 0)
+               return -1;
+       
+       sstrncpy(self->message, message, sizeof(self->message));
+       self->severity = severity;
+       return 0;
+}
+
+static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
+       int ret;
+       const data_set_t *ds;
+       notification_t notification;
+       double t = self->data.time;
+       int severity = self->severity;
+       const char *host = self->data.host;
+       const char *plugin = self->data.plugin;
+       const char *plugin_instance = self->data.plugin_instance;
+       const char *type = self->data.type;
+       const char *type_instance = self->data.type_instance;
+       const char *message = self->message;
+       
+       static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
+                       "plugin", "host", "time", "severity", NULL};
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
+                       &type, &message, &plugin_instance, &type_instance,
+                       &plugin, &host, &t, &severity))
+               return NULL;
+
+       if (type[0] == 0) {
+               PyErr_SetString(PyExc_RuntimeError, "type not set");
+               return NULL;
+       }
+       ds = plugin_get_ds(type);
+       if (ds == NULL) {
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               return NULL;
+       }
+
+       notification.time = t;
+       notification.severity = severity;
+       sstrncpy(notification.message, message, sizeof(notification.message));
+       sstrncpy(notification.host, host, sizeof(notification.host));
+       sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
+       sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
+       sstrncpy(notification.type, type, sizeof(notification.type));
+       sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
+       notification.meta = NULL;
+       if (notification.time < 1)
+               notification.time = time(0);
+       if (notification.host[0] == 0)
+               sstrncpy(notification.host, hostname_g, sizeof(notification.host));
+       if (notification.plugin[0] == 0)
+               sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
+       Py_BEGIN_ALLOW_THREADS;
+       ret = plugin_dispatch_notification(&notification);
+       Py_END_ALLOW_THREADS;
+       if (ret != 0) {
+               PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
+               return NULL;
+       }
+       Py_RETURN_NONE;
+}
+
+static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
+       Notification *self;
+       
+       self = (Notification *) PluginData_new(type, args, kwds);
+       if (self == NULL)
+               return NULL;
+       
+       self->message[0] = 0;
+       self->severity = 0;
+       return (PyObject *) self;
+}
+
+static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
+       char *old;
+       const char *new;
+       
+       if (value == NULL) {
+               PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
+               return -1;
+       }
+       new = PyString_AsString(value);
+       if (new == NULL) return -1;
+       old = ((char *) self) + (intptr_t) data;
+       sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
+       return 0;
+}
+
+static PyObject *Notification_repr(PyObject *s) {
+       PyObject *ret;
+       Notification *self = (Notification *) s;
+       
+       ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type,
+                       *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
+                       *self->data.plugin ? "',plugin='" : "", self->data.plugin,
+                       *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
+                       *self->data.host ? "',host='" : "", self->data.host,
+                       *self->message ? "',message='" : "", self->message,
+                       (long unsigned) self->data.time, self->severity);
+       return ret;
+}
+
+static PyMethodDef Notification_methods[] = {
+       {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
+       {NULL}
+};
+
+static PyMemberDef Notification_members[] = {
+       {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
+       {NULL}
+};
+
+static PyGetSetDef Notification_getseters[] = {
+       {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
+       {NULL}
+};
+
+PyTypeObject NotificationType = {
+       PyObject_HEAD_INIT(NULL)
+       0,                         /* Always 0 */
+       "collectd.Notification",   /* tp_name */
+       sizeof(Notification),      /* tp_basicsize */
+       0,                         /* Will be filled in later */
+       0,                         /* tp_dealloc */
+       0,                         /* tp_print */
+       0,                         /* tp_getattr */
+       0,                         /* tp_setattr */
+       0,                         /* tp_compare */
+       Notification_repr,         /* tp_repr */
+       0,                         /* tp_as_number */
+       0,                         /* tp_as_sequence */
+       0,                         /* tp_as_mapping */
+       0,                         /* tp_hash */
+       0,                         /* tp_call */
+       0,                         /* tp_str */
+       0,                         /* tp_getattro */
+       0,                         /* tp_setattro */
+       0,                         /* tp_as_buffer */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+       Notification_doc,          /* tp_doc */
+       0,                         /* tp_traverse */
+       0,                         /* tp_clear */
+       0,                         /* tp_richcompare */
+       0,                         /* tp_weaklistoffset */
+       0,                         /* tp_iter */
+       0,                         /* tp_iternext */
+       Notification_methods,      /* tp_methods */
+       Notification_members,      /* tp_members */
+       Notification_getseters,    /* tp_getset */
+       0,                         /* tp_base */
+       0,                         /* tp_dict */
+       0,                         /* tp_descr_get */
+       0,                         /* tp_descr_set */
+       0,                         /* tp_dictoffset */
+       Notification_init,         /* tp_init */
+       0,                         /* tp_alloc */
+       Notification_new           /* tp_new */
+};
diff --git a/src/routeros.c b/src/routeros.c
new file mode 100644 (file)
index 0000000..4fe33fa
--- /dev/null
@@ -0,0 +1,354 @@
+/**
+ * collectd - src/routeros.c
+ * Copyright (C) 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
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <routeros_api.h>
+
+struct cr_data_s
+{
+  ros_connection_t *connection;
+
+  char *node;
+  char *service;
+  char *username;
+  char *password;
+
+  _Bool collect_interface;
+  _Bool collect_regtable;
+};
+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)
+{
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = rx;
+       values[1].counter = tx;
+
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+       sstrncpy (vl.host, rd->node, sizeof (vl.host));
+       sstrncpy (vl.plugin, "routeros", 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 cr_submit_io */
+
+static void submit_interface (cr_data_t *rd, /* {{{ */
+    const ros_interface_t *i)
+{
+  if (i == NULL)
+    return;
+
+  if (!i->running)
+  {
+    submit_interface (rd, i->next);
+    return;
+  }
+
+  cr_submit_io (rd, "if_packets", i->name,
+      (counter_t) i->rx_packets, (counter_t) i->tx_packets);
+  cr_submit_io (rd, "if_octets", i->name,
+      (counter_t) i->rx_bytes, (counter_t) i->tx_bytes);
+  cr_submit_io (rd, "if_errors", i->name,
+      (counter_t) i->rx_errors, (counter_t) i->tx_errors);
+  cr_submit_io (rd, "if_dropped", i->name,
+      (counter_t) i->rx_drops, (counter_t) i->tx_drops);
+
+  submit_interface (rd, i->next);
+} /* }}} void submit_interface */
+
+static int handle_interface (__attribute__((unused)) ros_connection_t *c, /* {{{ */
+    const ros_interface_t *i, void *user_data)
+{
+  if ((i == NULL) || (user_data == NULL))
+    return (EINVAL);
+
+  submit_interface (user_data, i);
+  return (0);
+} /* }}} int handle_interface */
+
+static void cr_submit_gauge (cr_data_t *rd, 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 = STATIC_ARRAY_SIZE (values);
+       sstrncpy (vl.host, rd->node, sizeof (vl.host));
+       sstrncpy (vl.plugin, "routeros", 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 cr_submit_gauge */
+
+static void submit_regtable (cr_data_t *rd, /* {{{ */
+    const ros_registration_table_t *r)
+{
+  char type_instance[DATA_MAX_NAME_LEN];
+
+  if (r == NULL)
+    return;
+
+  /*** RX ***/
+  ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
+      r->interface, r->radio_name);
+  cr_submit_gauge (rd, "bitrate", type_instance,
+      (gauge_t) (1000000.0 * r->rx_rate));
+  cr_submit_gauge (rd, "signal_power", type_instance,
+      (gauge_t) r->rx_signal_strength);
+  cr_submit_gauge (rd, "signal_quality", type_instance,
+      (gauge_t) r->rx_ccq);
+
+  /*** TX ***/
+  ssnprintf (type_instance, sizeof (type_instance), "%s-%s-tx",
+      r->interface, r->radio_name);
+  cr_submit_gauge (rd, "bitrate", type_instance,
+      (gauge_t) (1000000.0 * r->tx_rate));
+  cr_submit_gauge (rd, "signal_power", type_instance,
+      (gauge_t) r->tx_signal_strength);
+  cr_submit_gauge (rd, "signal_quality", type_instance,
+      (gauge_t) r->tx_ccq);
+
+  /*** RX / TX ***/
+  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);
+  cr_submit_gauge (rd, "snr", type_instance, (gauge_t) r->signal_to_noise);
+
+  submit_regtable (rd, r->next);
+} /* }}} void submit_regtable */
+
+static int handle_regtable (__attribute__((unused)) ros_connection_t *c, /* {{{ */
+    const ros_registration_table_t *r, void *user_data)
+{
+  if ((r == NULL) || (user_data == NULL))
+    return (EINVAL);
+
+  submit_regtable (user_data, r);
+  return (0);
+} /* }}} int handle_regtable */
+
+static int cr_read (user_data_t *user_data) /* {{{ */
+{
+  int status;
+  cr_data_t *rd;
+
+  if (user_data == NULL)
+    return (EINVAL);
+
+  rd = user_data->data;
+  if (rd == NULL)
+    return (EINVAL);
+
+  if (rd->connection == NULL)
+  {
+    rd->connection = ros_connect (rd->node, rd->service,
+       rd->username, rd->password);
+    if (rd->connection == NULL)
+    {
+      char errbuf[128];
+      ERROR ("routeros plugin: ros_connect failed: %s",
+         sstrerror (errno, errbuf, sizeof (errbuf)));
+      return (-1);
+    }
+  }
+  assert (rd->connection != NULL);
+
+  if (rd->collect_interface)
+  {
+    status = ros_interface (rd->connection, handle_interface,
+       /* user data = */ rd);
+    if (status != 0)
+    {
+      char errbuf[128];
+      ERROR ("routeros plugin: ros_interface failed: %s",
+         sstrerror (status, errbuf, sizeof (errbuf)));
+      ros_disconnect (rd->connection);
+      rd->connection = NULL;
+      return (-1);
+    }
+  }
+
+  if (rd->collect_regtable)
+  {
+    status = ros_registration_table (rd->connection, handle_regtable,
+       /* user data = */ rd);
+    if (status != 0)
+    {
+      char errbuf[128];
+      ERROR ("routeros plugin: ros_registration_table failed: %s",
+         sstrerror (status, errbuf, sizeof (errbuf)));
+      ros_disconnect (rd->connection);
+      rd->connection = NULL;
+      return (-1);
+    }
+  }
+
+  return (0);
+} /* }}} int cr_read */
+
+static void cr_free_data (cr_data_t *ptr) /* {{{ */
+{
+  if (ptr == NULL)
+    return;
+
+  ros_disconnect (ptr->connection);
+  ptr->connection = NULL;
+
+  sfree (ptr->node);
+  sfree (ptr->service);
+  sfree (ptr->username);
+  sfree (ptr->password);
+
+  sfree (ptr);
+} /* }}} void cr_free_data */
+
+static int cr_config_router (oconfig_item_t *ci) /* {{{ */
+{
+  cr_data_t *router_data;
+  char read_name[128];
+  user_data_t user_data;
+  int status;
+  int i;
+
+  router_data = malloc (sizeof (*router_data));
+  if (router_data == NULL)
+    return (-1);
+  memset (router_data, 0, sizeof (router_data));
+  router_data->connection = NULL;
+  router_data->node = NULL;
+  router_data->service = NULL;
+  router_data->username = NULL;
+  router_data->password = NULL;
+  router_data->collect_interface = false;
+  router_data->collect_regtable = false;
+
+  status = 0;
+  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, &router_data->node);
+    else if (strcasecmp ("Port", child->key) == 0)
+      status = cf_util_get_string (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)
+      status = cf_util_get_string (child, &router_data->password);
+    else if (strcasecmp ("CollectInterface", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_interface);
+    else if (strcasecmp ("CollectRegistrationTable", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_regtable);
+    else
+    {
+      WARNING ("routeros plugin: Unknown config option `%s'.", child->key);
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  if (status == 0)
+  {
+    if (router_data->node == NULL)
+    {
+      ERROR ("routeros plugin: No `Host' option within a `Router' block. "
+         "Where should I connect to?");
+      status = -1;
+    }
+
+    if (router_data->password == NULL)
+    {
+      ERROR ("routeros plugin: No `Password' option within a `Router' block. "
+         "How should I authenticate?");
+      status = -1;
+    }
+
+    if (!router_data->collect_interface
+       && !router_data->collect_regtable)
+    {
+      ERROR ("routeros plugin: No `Collect*' option within a `Router' block. "
+         "What statistics should I collect?");
+      status = -1;
+    }
+  }
+
+  if ((status == 0) && (router_data->username == NULL))
+  {
+    router_data->username = sstrdup ("admin");
+    if (router_data->username == NULL)
+    {
+      ERROR ("routeros plugin: sstrdup failed.");
+      status = -1;
+    }
+  }
+
+  ssnprintf (read_name, sizeof (read_name), "routeros/%s", router_data->node);
+  user_data.data = router_data;
+  user_data.free_func = (void *) cr_free_data;
+  if (status == 0)
+    status = plugin_register_complex_read (read_name, cr_read,
+       /* interval = */ NULL, &user_data);
+
+  if (status != 0)
+    cr_free_data (router_data);
+
+  return (status);
+} /* }}} int cr_config_router */
+
+static int cr_config (oconfig_item_t *ci)
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Router", child->key) == 0)
+      cr_config_router (child);
+    else
+    {
+      WARNING ("routeros plugin: Unknown config option `%s'.", child->key);
+    }
+  }
+
+  return (0);
+} /* }}} int cr_config */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("routeros", cr_config);
+} /* void module_register */
+
+/* vim: set sw=2 noet fdm=marker : */
index 191df58..aab8c11 100644 (file)
@@ -193,18 +193,14 @@ static int rc_config (const char *key, const char *value)
   }
   else if (strcasecmp ("CreateFiles", key) == 0)
   {
-    if ((strcasecmp ("false", value) == 0)
-        || (strcasecmp ("no", value) == 0)
-        || (strcasecmp ("off", value) == 0))
+    if (IS_FALSE (value))
       config_create_files = 0;
     else
       config_create_files = 1;
   }
   else if (strcasecmp ("CollectStatistics", key) == 0)
   {
-    if ((strcasecmp ("false", value) == 0)
-        || (strcasecmp ("no", value) == 0)
-        || (strcasecmp ("off", value) == 0))
+    if (IS_FALSE (value))
       config_collect_stats = 0;
     else
       config_collect_stats = 1;
index fa461a2..8391346 100644 (file)
@@ -237,9 +237,7 @@ static int sensors_config (const char *key, const char *value)
        else if (strcasecmp (key, "IgnoreSelected") == 0)
        {
                ignorelist_set_invert (sensor_list, 1);
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        ignorelist_set_invert (sensor_list, 0);
        }
        else
index 22eda1f..7f41c9e 100644 (file)
@@ -2,6 +2,7 @@
  * collectd - src/swap.c
  * Copyright (C) 2005-2009  Florian octo Forster
  * Copyright (C) 2009       Stefan Völkel
+ * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -18,6 +19,7 @@
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   Manuel Sanmartin
  **/
 
 #if HAVE_CONFIG_H
 # include <statgrab.h>
 #endif
 
+#if HAVE_PERFSTAT
+# include <sys/protosw.h>
+# include <libperfstat.h>
+#endif
+
 #undef  MAX
 #define MAX(x,y) ((x) > (y) ? (x) : (y))
 
@@ -86,6 +93,11 @@ int kvm_pagesize;
 /* No global variables */
 /* #endif HAVE_LIBSTATGRAB */
 
+#elif HAVE_PERFSTAT
+static int pagesize;
+static perfstat_memory_total_t pmemory;
+/*# endif HAVE_PERFSTAT */ 
+
 #else
 # error "No applicable input method."
 #endif /* HAVE_LIBSTATGRAB */
@@ -134,7 +146,11 @@ static int swap_init (void)
 
 #elif HAVE_LIBSTATGRAB
        /* No init stuff */
-#endif /* HAVE_LIBSTATGRAB */
+/* #endif HAVE_LIBSTATGRAB */
+
+#elif HAVE_PERFSTAT
+       pagesize = getpagesize();
+#endif /* HAVE_PERFSTAT */
 
        return (0);
 }
@@ -173,7 +189,7 @@ static int swap_read (void)
 #if KERNEL_LINUX
        FILE *fh;
        char buffer[1024];
-       
+
        char *fields[8];
        int numfields;
 
@@ -449,7 +465,19 @@ static int swap_read (void)
 
        swap_submit ("used", (derive_t) swap->used, DS_TYPE_GAUGE);
        swap_submit ("free", (derive_t) swap->free, DS_TYPE_GAUGE);
-#endif /* HAVE_LIBSTATGRAB */
+/* #endif  HAVE_LIBSTATGRAB */
+
+#elif HAVE_PERFSTAT
+        if(perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1) < 0)
+       {
+                char errbuf[1024];
+                WARNING ("memory plugin: perfstat_memory_total failed: %s",
+                        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 */
 
        return (0);
 } /* int swap_read */
diff --git a/src/target_scale.c b/src/target_scale.c
new file mode 100644 (file)
index 0000000..6b261c7
--- /dev/null
@@ -0,0 +1,420 @@
+/**
+ * collectd - src/target_scale.c
+ * Copyright (C) 2008-2009  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:
+ *   Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "filter_chain.h"
+
+#include "utils_cache.h"
+
+struct ts_data_s
+{
+       double factor;
+       double offset;
+};
+typedef struct ts_data_s ts_data_t;
+
+static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */
+               ts_data_t *data, int dsrc_index)
+{
+       uint64_t curr_counter;
+       int status;
+       int failure;
+
+       /* Required meta data */
+       uint64_t prev_counter;
+       char key_prev_counter[128];
+       uint64_t int_counter;
+       char key_int_counter[128];
+       double int_fraction;
+       char key_int_fraction[128];
+
+       curr_counter = (uint64_t) vl->values[dsrc_index].counter;
+
+       ssnprintf (key_prev_counter, sizeof (key_prev_counter),
+                       "target_scale[%p,%i]:prev_counter",
+                       (void *) data, dsrc_index);
+       ssnprintf (key_int_counter, sizeof (key_int_counter),
+                       "target_scale[%p,%i]:int_counter",
+                       (void *) data, dsrc_index);
+       ssnprintf (key_int_fraction, sizeof (key_int_fraction),
+                       "target_scale[%p,%i]:int_fraction",
+                       (void *) data, dsrc_index);
+
+       prev_counter = curr_counter;
+       int_counter = 0;
+       int_fraction = 0.0;
+
+       /* Query the meta data */
+       failure = 0;
+
+       status = uc_meta_data_get_unsigned_int (vl, key_prev_counter,
+                       &prev_counter);
+       if (status != 0)
+               failure++;
+
+       status = uc_meta_data_get_unsigned_int (vl, key_int_counter, &int_counter);
+       if (status != 0)
+               failure++;
+
+       status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
+       if (status != 0)
+               failure++;
+
+       if (failure == 0)
+       {
+               uint64_t difference;
+               double rate;
+
+               /* Calcualte the rate */
+               if (prev_counter > curr_counter) /* => counter overflow */
+               {
+                       if (prev_counter <= 4294967295UL) /* 32 bit overflow */
+                               difference = (4294967295UL - prev_counter) + curr_counter;
+                       else /* 64 bit overflow */
+                               difference = (18446744073709551615ULL - prev_counter) + curr_counter;
+               }
+               else /* no overflow */
+               {
+                       difference = curr_counter - prev_counter;
+               }
+               rate = ((double) difference) / ((double) vl->interval);
+
+               /* Modify the rate. */
+               if (!isnan (data->factor))
+                       rate *= data->factor;
+               if (!isnan (data->offset))
+                       rate += data->offset;
+
+               /* Calculate the internal counter. */
+               int_fraction += (rate * ((double) vl->interval));
+               difference = (uint64_t) int_fraction;
+               int_fraction -= ((double) difference);
+               int_counter  += difference;
+
+               assert (int_fraction >= 0.0);
+               assert (int_fraction <  1.0);
+
+               DEBUG ("Target `scale': ts_invoke_counter: %"PRIu64" -> %g -> %"PRIu64
+                               "(+%g)",
+                               curr_counter, rate, int_counter, int_fraction);
+       }
+       else /* (failure != 0) */
+       {
+               int_counter = 0;
+               int_fraction = 0.0;
+       }
+
+       vl->values[dsrc_index].counter = (counter_t) int_counter;
+
+       /* Update to the new counter value */
+       uc_meta_data_add_unsigned_int (vl, key_prev_counter, curr_counter);
+       uc_meta_data_add_unsigned_int (vl, key_int_counter, int_counter);
+       uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
+
+
+       return (0);
+} /* }}} int ts_invoke_counter */
+
+static int ts_invoke_gauge (const data_set_t *ds, value_list_t *vl, /* {{{ */
+               ts_data_t *data, int dsrc_index)
+{
+       if (!isnan (data->factor))
+               vl->values[dsrc_index].gauge *= data->factor;
+       if (!isnan (data->offset))
+               vl->values[dsrc_index].gauge += data->offset;
+
+       return (0);
+} /* }}} int ts_invoke_gauge */
+
+static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */
+               ts_data_t *data, int dsrc_index)
+{
+       int64_t curr_derive;
+       int status;
+       int failure;
+
+       /* Required meta data */
+       int64_t prev_derive;
+       char key_prev_derive[128];
+       int64_t int_derive;
+       char key_int_derive[128];
+       double int_fraction;
+       char key_int_fraction[128];
+
+       curr_derive = (int64_t) vl->values[dsrc_index].derive;
+
+       ssnprintf (key_prev_derive, sizeof (key_prev_derive),
+                       "target_scale[%p,%i]:prev_derive",
+                       (void *) data, dsrc_index);
+       ssnprintf (key_int_derive, sizeof (key_int_derive),
+                       "target_scale[%p,%i]:int_derive",
+                       (void *) data, dsrc_index);
+       ssnprintf (key_int_fraction, sizeof (key_int_fraction),
+                       "target_scale[%p,%i]:int_fraction",
+                       (void *) data, dsrc_index);
+
+       prev_derive = curr_derive;
+       int_derive = 0;
+       int_fraction = 0.0;
+
+       /* Query the meta data */
+       failure = 0;
+
+       status = uc_meta_data_get_signed_int (vl, key_prev_derive,
+                       &prev_derive);
+       if (status != 0)
+               failure++;
+
+       status = uc_meta_data_get_signed_int (vl, key_int_derive, &int_derive);
+       if (status != 0)
+               failure++;
+
+       status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
+       if (status != 0)
+               failure++;
+
+       if (failure == 0)
+       {
+               int64_t difference;
+               double rate;
+
+               /* Calcualte the rate */
+               difference = curr_derive - prev_derive;
+               rate = ((double) difference) / ((double) vl->interval);
+
+               /* Modify the rate. */
+               if (!isnan (data->factor))
+                       rate *= data->factor;
+               if (!isnan (data->offset))
+                       rate += data->offset;
+
+               /* Calculate the internal derive. */
+               int_fraction += (rate * ((double) vl->interval));
+               if (int_fraction < 0.0) /* handle negative integer rounding correctly */
+                       difference = ((int64_t) int_fraction) - 1;
+               else
+                       difference = (int64_t) int_fraction;
+               int_fraction -= ((double) difference);
+               int_derive  += difference;
+
+               assert (int_fraction >= 0.0);
+               assert (int_fraction <  1.0);
+
+               DEBUG ("Target `scale': ts_invoke_derive: %"PRIu64" -> %g -> %"PRIu64
+                               "(+%g)",
+                               curr_derive, rate, int_derive, int_fraction);
+       }
+       else /* (failure != 0) */
+       {
+               int_derive = 0;
+               int_fraction = 0.0;
+       }
+
+       vl->values[dsrc_index].derive = (derive_t) int_derive;
+
+       /* Update to the new derive value */
+       uc_meta_data_add_signed_int (vl, key_prev_derive, curr_derive);
+       uc_meta_data_add_signed_int (vl, key_int_derive, int_derive);
+       uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
+
+       return (0);
+} /* }}} int ts_invoke_derive */
+
+static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */
+               ts_data_t *data, int dsrc_index)
+{
+       uint64_t curr_absolute;
+       double rate;
+       int status;
+
+       /* Required meta data */
+       double int_fraction;
+       char key_int_fraction[128];
+
+       curr_absolute = (uint64_t) vl->values[dsrc_index].absolute;
+
+       ssnprintf (key_int_fraction, sizeof (key_int_fraction),
+                       "target_scale[%p,%i]:int_fraction",
+                       (void *) data, dsrc_index);
+
+       int_fraction = 0.0;
+
+       /* Query the meta data */
+       status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
+       if (status != 0)
+               int_fraction = 0.0;
+
+       rate = ((double) curr_absolute) / ((double) vl->interval);
+
+       /* Modify the rate. */
+       if (!isnan (data->factor))
+               rate *= data->factor;
+       if (!isnan (data->offset))
+               rate += data->offset;
+
+       /* Calculate the new absolute. */
+       int_fraction += (rate * ((double) vl->interval));
+       curr_absolute = (uint64_t) int_fraction;
+       int_fraction -= ((double) curr_absolute);
+
+       vl->values[dsrc_index].absolute = (absolute_t) curr_absolute;
+
+       /* Update to the new absolute value */
+       uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
+
+       return (0);
+} /* }}} int ts_invoke_absolute */
+
+static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
+{
+       if ((ci->values_num != 1)
+                       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+       {
+               WARNING ("scale target: The `%s' config option needs "
+                               "exactly one numeric argument.", ci->key);
+               return (-1);
+       }
+
+       *ret = ci->values[0].value.number;
+       DEBUG ("ts_config_set_double: *ret = %g", *ret);
+
+       return (0);
+} /* }}} int ts_config_set_double */
+
+static int ts_destroy (void **user_data) /* {{{ */
+{
+       if (user_data == NULL)
+               return (-EINVAL);
+
+       free (*user_data);
+       *user_data = NULL;
+
+       return (0);
+} /* }}} int ts_destroy */
+
+static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+       ts_data_t *data;
+       int status;
+       int i;
+
+       data = (ts_data_t *) malloc (sizeof (*data));
+       if (data == NULL)
+       {
+               ERROR ("ts_create: malloc failed.");
+               return (-ENOMEM);
+       }
+       memset (data, 0, sizeof (*data));
+
+       data->factor = NAN;
+       data->offset = NAN;
+
+       status = 0;
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if (strcasecmp ("Factor", child->key) == 0)
+                               status = ts_config_set_double (&data->factor, child);
+               else if (strcasecmp ("Offset", child->key) == 0)
+                               status = ts_config_set_double (&data->offset, child);
+               else
+               {
+                       ERROR ("Target `scale': The `%s' configuration option is not understood "
+                                       "and will be ignored.", child->key);
+                       status = 0;
+               }
+
+               if (status != 0)
+                       break;
+       }
+
+       /* Additional sanity-checking */
+       while (status == 0)
+       {
+               if (isnan (data->factor) && isnan (data->offset))
+               {
+                       ERROR ("Target `scale': You need to at least set either the `Factor' "
+                                       "or `Offset' option!");
+                       status = -1;
+               }
+
+               break;
+       }
+
+       if (status != 0)
+       {
+               ts_destroy ((void *) &data);
+               return (status);
+       }
+
+       *user_data = data;
+       return (0);
+} /* }}} int ts_create */
+
+static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
+               notification_meta_t __attribute__((unused)) **meta, void **user_data)
+{
+       ts_data_t *data;
+       int i;
+
+       if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
+               return (-EINVAL);
+
+       data = *user_data;
+       if (data == NULL)
+       {
+               ERROR ("Target `scale': Invoke: `data' is NULL.");
+               return (-EINVAL);
+       }
+
+       for (i = 0; i < ds->ds_num; i++)
+       {
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+                       ts_invoke_counter (ds, vl, data, i);
+               else if (ds->ds[i].type == DS_TYPE_GAUGE)
+                       ts_invoke_gauge (ds, vl, data, i);
+               else if (ds->ds[i].type == DS_TYPE_DERIVE)
+                       ts_invoke_derive (ds, vl, data, i);
+               else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+                       ts_invoke_absolute (ds, vl, data, i);
+               else
+                       ERROR ("Target `scale': Ignoring unknown data source type %i",
+                                       ds->ds[i].type);
+       }
+
+       return (FC_TARGET_CONTINUE);
+} /* }}} int ts_invoke */
+
+void module_register (void)
+{
+       target_proc_t tproc;
+
+       memset (&tproc, 0, sizeof (tproc));
+       tproc.create  = ts_create;
+       tproc.destroy = ts_destroy;
+       tproc.invoke  = ts_invoke;
+       fc_register_target ("scale", tproc);
+} /* module_register */
+
+/* vim: set sw=2 ts=2 tw=78 fdm=marker : */
+
index f164b53..d68cd09 100644 (file)
@@ -454,9 +454,7 @@ static int conn_config (const char *key, const char *value)
 {
   if (strcasecmp (key, "ListeningPorts") == 0)
   {
-    if ((strcasecmp (value, "Yes") == 0)
-       || (strcasecmp (value, "True") == 0)
-       || (strcasecmp (value, "On") == 0))
+    if (IS_TRUE (value))
       port_collect_listening = 1;
     else
       port_collect_listening = 0;
index fe54aee..2b70805 100644 (file)
@@ -198,17 +198,13 @@ static int thermal_config (const char *key, const char *value)
        else if (strcasecmp (key, "IgnoreSelected") == 0)
        {
                ignorelist_set_invert (device_list, 1);
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        ignorelist_set_invert (device_list, 0);
        }
        else if (strcasecmp (key, "ForceUseProcfs") == 0)
        {
                force_procfs = 0;
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        force_procfs = 1;
        }
        else
index 26366c9..678a341 100644 (file)
 #include "utils_cache.h"
 #include "utils_parse_option.h"
 
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-
 #include <tcrdb.h>
 
 #define DEFAULT_HOST "127.0.0.1"
@@ -46,56 +42,6 @@ static char *config_port = NULL;
 
 static TCRDB *rdb = NULL;
 
-static int parse_service_name (const char *service_name)
-{
-       struct addrinfo *ai_list;
-       struct addrinfo *ai_ptr;
-       struct addrinfo ai_hints;
-       int status;
-       int service_number;
-
-       ai_list = NULL;
-       memset (&ai_hints, 0, sizeof (ai_hints));
-       ai_hints.ai_family = AF_UNSPEC;
-
-       status = getaddrinfo (/* node = */ NULL, service_name,
-                       &ai_hints, &ai_list);
-       if (status != 0)
-       {
-               ERROR ("tokyotyrant plugin: getaddrinfo failed: %s",
-                               gai_strerror (status));
-               return (-1);
-       }
-
-       service_number = -1;
-       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
-       {
-               if (ai_ptr->ai_family == AF_INET)
-               {
-                       struct sockaddr_in *sa;
-
-                       sa = (void *) ai_ptr->ai_addr;
-                       service_number = (int) ntohs (sa->sin_port);
-               }
-               else if (ai_ptr->ai_family == AF_INET6)
-               {
-                       struct sockaddr_in6 *sa;
-
-                       sa = (void *) ai_ptr->ai_addr;
-                       service_number = (int) ntohs (sa->sin6_port);
-               }
-
-               if ((service_number > 0) && (service_number <= 65535))
-                       break;
-       }
-
-       freeaddrinfo (ai_list);
-
-       if ((service_number > 0) && (service_number <= 65535))
-               return (service_number);
-       return (-1);
-} /* int parse_service_name */
-
 static int tt_config (const char *key, const char *value)
 {
        if (strcasecmp ("Host", key) == 0)
@@ -171,7 +117,7 @@ static void tt_open_db (void)
 
        if (config_port != NULL)
        {
-               port = parse_service_name (config_port);
+               port = service_name_to_port_number (config_port);
                if (port <= 0)
                        return;
        }
index 8ac9dd4..a5872eb 100644 (file)
@@ -12,12 +12,15 @@ ath_nodes           value:GAUGE:0:65535
 ath_stat               value:COUNTER:0:4294967295
 bitrate                        value:GAUGE:0:4294967295
 bytes                  value:GAUGE:0:U
+cache_ratio            value:GAUGE:0:100
 cache_result           value:COUNTER:0:4294967295
 cache_size             value:GAUGE:0:4294967295
 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
 counter                        value:COUNTER:U:U
 cpufreq                        value:GAUGE:0:U
 cpu                    value:COUNTER:0:4294967295
@@ -25,9 +28,13 @@ current                      value:GAUGE:U:U
 delay                  seconds: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
+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
@@ -55,6 +62,7 @@ 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
 gauge                  value:GAUGE:U:U
 http_request_methods   count:COUNTER:0:134217728
 http_requests          count:COUNTER:0:134217728
@@ -116,11 +124,14 @@ protocol_counter  value:COUNTER:0:U
 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_disk_octets         read:DERIVE:0:U, write:DERIVE:0:U
+ps_disk_ops            read:DERIVE:0:U, write:DERIVE:0:U
 ps_rss                 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
+response_time          value:GAUGE:0:U
 records                        count:GAUGE:0:U
 route_etx              value:GAUGE:0:U
 route_metric           value:GAUGE:0:U
@@ -129,6 +140,7 @@ serial_octets               rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
 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
@@ -141,6 +153,7 @@ timeleft            timeleft:GAUGE:0:3600
 time_offset            seconds:GAUGE:-1000000:1000000
 total_requests         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
index 1ecd07e..086649a 100644 (file)
@@ -172,7 +172,7 @@ int c_heap_insert (c_heap_t *h, void *ptr)
   return (0);
 } /* int c_heap_insert */
 
-void *c_head_get_root (c_heap_t *h)
+void *c_heap_get_root (c_heap_t *h)
 {
   void *ret = NULL;
 
@@ -218,6 +218,6 @@ void *c_head_get_root (c_heap_t *h)
   pthread_mutex_unlock (&h->lock);
 
   return (ret);
-} /* void *c_head_get_root */
+} /* void *c_heap_get_root */
 
 /* vim: set sw=2 sts=2 et fdm=marker : */
index dd0f486..6428006 100644 (file)
@@ -78,7 +78,7 @@ int c_heap_insert (c_heap_t *h, void *ptr);
 
 /*
  * NAME
- *   c_head_get_root
+ *   c_heap_get_root
  *
  * DESCRIPTION
  *   Removes the value at the root of the heap and returns both, key and value.
@@ -90,7 +90,7 @@ int c_heap_insert (c_heap_t *h, void *ptr);
  *   The pointer passed to `c_heap_insert' or NULL if there are no more
  *   elements in the heap (or an error occurred).
  */
-void *c_head_get_root (c_heap_t *h);
+void *c_heap_get_root (c_heap_t *h);
 
 #endif /* UTILS_HEAP_H */
 /* vim: set sw=2 sts=2 et : */
index 6775d20..d32f1db 100644 (file)
@@ -77,9 +77,7 @@ static int vmem_config (const char *key, const char *value)
 {
   if (strcasecmp ("Verbose", key) == 0)
   {
-    if ((strcasecmp ("true", value) == 0)
-       || (strcasecmp ("yes", value) == 0)
-       || (strcasecmp ("on", value) == 0))
+    if (IS_TRUE (value))
       verbose_output = 1;
     else
       verbose_output = 0;
index f1a2fc2..bd87bbb 100755 (executable)
@@ -10,4 +10,8 @@ fi
 
 VERSION="`echo \"$VERSION\" | sed -e 's/-/./g'`"
 
-echo -n "$VERSION"
+if test "x`uname -s`" = "xAIX" ; then 
+       echo "$VERSION\c"
+else 
+       echo -n "$VERSION"
+fi