Merge branch 'collectd-4.3'
authorFlorian Forster <octo@huhu.verplant.org>
Sat, 22 Mar 2008 08:41:42 +0000 (09:41 +0100)
committerFlorian Forster <octo@huhu.verplant.org>
Sat, 22 Mar 2008 08:41:42 +0000 (09:41 +0100)
Conflicts:

src/utils_cache.c

20 files changed:
AUTHORS
ChangeLog
configure.in
contrib/extractDS.px [deleted file]
contrib/migrate-3-4.px
contrib/rrd_filter.px [new file with mode: 0755]
src/collectd.conf.pod
src/common.c
src/common.h
src/configfile.c
src/liboconfig/oconfig.c
src/mbmon.c
src/network.c
src/perl.c
src/plugin.c
src/unixsock.c
src/utils_cache.c
src/utils_cache.h
src/utils_threshold.c
version-gen.sh

diff --git a/AUTHORS b/AUTHORS
index fec902f..bd5daa4 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,18 +4,17 @@ This package was written by:
 apcups plugin by:
   Anthony Gialluca <tonyabg at charter.net>
 
-cpufreq, multimeter and irq module by:
+cpufreq, multimeter and irq plugin by:
   Peter Holik <peter at holik.at>
 
-hddtemp module by:
+hddtemp plugin by:
   Vincent Stehlé <vincent.stehle at free.fr>
 
-iptables module by:
+iptables plugin by:
   Sjoerd van der Berg <harekiet at gmail.com>
 
-libvirt and uuid plugins by:
-  Richard Jones <rjones at redhat.com>
-  of Red Hat's Emerging Technology group
+libvirt plugin by:
+  Richard W. M. Jones <rjones at redhat.com>
 
 mbmon plugin by:
   Flavio Stanchina <flavio at stanchina.net>
@@ -23,37 +22,41 @@ mbmon plugin by:
 memcached plugin by:
   Antony Dovgal <tony at daylessday.org>
 
-nfs module by:
+nfs plugin by:
   Jason Pepas <cell at ices.utexas.edu>
 
-perl module by:
+perl plugin by:
   Sebastian Harl <sh at tokkee.org>
 
-processes module by:
+processes plugin by:
   Lyonel Vincent <lyonel at ezix.org>
 
 sensors plugin has been improved by:
   Luboš Staněk <kolektor at atlas.cz>
 
-serial module by:
+serial plugin by:
   David Bacher <drbacher at gmail.com>
 
-tape module by:
+tape plugin by:
   Scott Garrett <sgarrett at technomancer.com>
 
-users module by:
+users plugin by:
   Sebastian Harl <sh at tokkee.org>
 
-vserver module by:
+uuid plugin by:
+  Dan Berrange <berrange@redhat.com>
+  Richard W.M. Jones <rjones@redhat.com>
+
+vserver plugin by:
   Sebastian Harl <sh at tokkee.org>
 
 PID-file patch by:
   Tommie Gannert <d00-tga at d.kth.se>
 
-don't-fork-patch by:
+Don't-fork-patch by:
   Alvaro Barcellos <alvaro.barcellos at gmail.com>
 
-many autotools related fixes, libltdl code, getmnt-wizardry and much help has
+Many autotools related fixes, libltdl code, getmnt-wizardry and much help has
 contributed:
   Niki W. Waibel <niki.waibel at newlogic.com>
 
@@ -64,13 +67,6 @@ Much time and effort to find a nasty bug in the ntpd-plugin has been
 contributed by:
   Luboš Staněk <lubek at users.sourceforge.net>
 
-uuid module by:
-  Dan Berrange <berrange@redhat.com>
-  Richard W.M. Jones <rjones@redhat.com>
-
-Libvirt integration by:
-  Richard Jones <rjones@redhat.com>
-
 collectd is available at:
   <http://collectd.org/>
 
index fc72191..fceab49 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2008-03-05, Version 4.3.1
+       * exec plugin: Set supplementary group IDs.
+       * network plugin:
+         + Use `memcpy' when constructing/parsing a package to avoid
+           alignment problems on weird architectures, such as Sparc.
+         + Translate doubles to/from the x86 byte representation to ensure
+           cross-platform compatibility.
+       * ping plugin: Correct the handling of the `TTL' setting.
+       * swap plugin: Reapply a patch for Solaris.
+       * tcpconns plugin: Portability improvements.
+
 2008-02-18, Version 4.3.0
        * collectd: Notifications have been added to the daemon. Notifications
          are status messages that may be associated with a data instance.
          physical server. Thanks to Richard Jones from Red Hat's Emerging
          Technology group for this plugin.
 
+2008-03-04, Version 4.2.5
+       * apache plugin: Improved initialization and error messages.
+       * exec plugin: Set supplementary group IDs.
+       * network plugin:
+         + Create separate threads for reading from the socket and parsing
+           and dispatching incoming packets. Versions prior to this may have
+           problems in high-load situations, where the socket receive buffers
+           overflows, resulting in gaps in the data.
+         + Use `memcpy' when constructing/parsing a package to avoid
+           alignment problems on weird architectures, such as Sparc.
+         + Translate doubles to/from the x86 byte representation to ensure
+           cross-platform compatibility.
+       * ping plugin: Correct the handling of the `TTL' setting.
+       * rrdtool plugin: Ensure correct handling of the `RRATimespan' option.
+       * swap plugin: Reapply a patch for Solaris.
+       * tcpconns plugin: Portability improvements.
+
 2008-01-21, Version 4.2.4
        * unixsock plugin: A bug in the unixsock plugin caused it not to set
          the permission on the socket as documented in the manpage. Thanks to
        * collectd: The documentation has been improved.
 
 2007-12-27, Version 4.1.5
-       * rrdtool plugin: Fix a memory leak that only occured in very-low-
+       * rrdtool plugin: Fix a memory leak that only occurred in very-low-
          memory situations.
        * sensors plugin: Updated the plugin to build and work with version 3
          of the libsensors library.
index 036a9e4..e3662b7 100644 (file)
@@ -883,7 +883,7 @@ then
         ],
         [with_rrdtool="no (symbol 'rrd_update' not found)"],
         [-lm])
-       ]
+       ],
        [-lm])
 
        CPPFLAGS="$SAVE_CPPFLAGS"
diff --git a/contrib/extractDS.px b/contrib/extractDS.px
deleted file mode 100755 (executable)
index 80d873b..0000000
+++ /dev/null
@@ -1,621 +0,0 @@
-#!/usr/bin/perl
-
-# collectd - contrib/rrd_filter.px
-# 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
-#
-# Authors:
-#   Florian octo Forster <octo at verplant.org>
-
-use strict;
-use warnings;
-
-=head1 NAME
-
-rrd_filter.px - Perform same advanced non-standard operations on an RRD file.
-
-=head1 SYNOPSYS
-
-  rrd_filter.px -i input.rrd -o output.rrd [options]
-
-=head1 DEPENDENCIES
-
-extractDS.px requires the RRDTool binary, Perl and the included L<Getopt::Long>
-module.
-
-=cut
-
-use Getopt::Long ('GetOptions');
-use Data::Dumper ();
-
-our $InFile;
-our $InDS = [];
-our $OutFile;
-our $OutDS = [];
-
-our $NewRRAs = [];
-
-our $Step = 0;
-
-=head1 OPTIONS
-
-The following options can be passed on the command line:
-
-=over 4
-
-=item B<--infile> I<file>
-
-=item B<-i> I<file>
-
-Reads from I<file>. If I<file> ends in C<.rrd>, then C<rrdtool dump> is invoked
-to create an XML dump of the RRD file. Otherwise the XML dump is expected
-directly. The special filename C<-> can be used to read from STDIN.
-
-=item B<--outfile> I<file>
-
-=item B<-o> I<file>
-
-Writes output to I<file>. If I<file> ends in C<.rrd>, then C<rrdtool restore>
-is invoked to create a binary RRD file. Otherwise an XML output is written. The
-special filename C<-> can be used to write to STDOUT.
-
-=item B<--map> I<in_ds>:I<out_ds>
-
-=item B<-m> I<in_ds>:I<out_ds>
-
-Writes the datasource I<in_ds> to the output and renames it to I<out_ds>. This
-is useful to extract one DS from an RRD file.
-
-=item B<--step> I<seconds>
-
-=item B<-s> I<seconds>
-
-Changes the step of the output RRD file to be I<seconds>. The new stepsize must
-be a multiple of the old stepsize of the other way around. When increasing the
-stepsize the number of PDPs in each RRA must be dividable by the factor by
-which the stepsize is increased. The length of CDPs and the absolute length of
-RRAs (and thus the data itself) is not altered.
-
-Examples:
-
-  step =  10, rra_steps = 12   =>   step = 60, rra_steps =  2
-  step = 300, rra_steps =  1   =>   step = 10, rra_steps = 30
-
-=item B<--rra> B<RRA>:I<CF>:I<XFF>:I<steps>:I<rows>
-
-=item B<-a> B<RRA>:I<CF>:I<XFF>:I<steps>:I<rows>
-
-Inserts a new RRA in the generated RRD file. This is done B<after> the step has
-been adjusted, take that into account when specifying I<steps> and I<rows>. For
-an explanation of the format please see L<rrdcreate(1)>.
-
-=back
-
-=cut
-
-GetOptions ("infile|i=s" => \$InFile,
-       "outfile|o=s" => \$OutFile,
-       'map|m=s' => sub
-       {
-               my ($in_ds, $out_ds) = split (':', $_[1]);
-               if (!defined ($in_ds) || !defined ($out_ds))
-               {
-                       print STDERR "Argument for `map' incorrect! The format is `--map in_ds:out_ds'\n";
-                       exit (1);
-               }
-               push (@$InDS, $in_ds);
-               push (@$OutDS, $out_ds);
-       },
-       'step|s=i' => \$Step,
-       'rra|a=s' => sub
-       {
-               my ($rra, $cf, $xff, $steps, $rows) = split (':', $_[1]);
-               if (($rra ne 'RRA') || !defined ($rows))
-               {
-                       print STDERR "Please use the standard RRDTool syntax when adding RRAs. I. e. RRA:<cf><xff>:<steps>:<rows>.\n";
-                       exit (1);
-               }
-               push (@$NewRRAs, {cf => $cf, xff => $xff, steps => $steps, rows => $rows});
-       }
-) or exit (1);
-
-if (!$InFile || !$OutFile)
-{
-       print STDERR "Usage: $0 -i <infile> -m <in_ds>:<out_ds> -s <step>\n";
-       exit (1);
-}
-if ((1 + @$InDS) != (1 + @$OutDS))
-{
-       print STDERR "You need the same amount of in- and out-DSes\n";
-       exit (1);
-}
-
-main ($InFile, $OutFile);
-exit (0);
-
-{
-my $ds_index;
-my $current_index;
-# state 0 == searching for DS index
-# state 1 == parse RRA header
-# state 2 == parse values
-my $state;
-my $out_cache;
-sub handle_line_dsmap
-{
-       my $line = shift;
-       my $index = shift;
-       my $ret = '';
-
-       if ((@$InDS == 0) || (@$OutDS == 0))
-       {
-               post_line ($line, $index + 1);
-               return;
-       }
-
-       if (!defined ($state))
-       {
-               $current_index = -1;
-               $state = 0;
-               $out_cache = [];
-
-               # $ds_index->[new_index] = old_index
-               $ds_index = [];
-               for (my $i = 0; $i < @$InDS; $i++)
-               {
-                       $ds_index->[$i] = -1;
-               }
-       }
-
-       if ($state == 0)
-       {
-               if ($line =~ m/<ds>/)
-               {
-                       $current_index++;
-                       $out_cache->[$current_index] = $line;
-               }
-               elsif ($line =~ m#<name>\s*([^<\s]+)\s*</name>#)
-               {
-                       # old_index == $current_index
-                       # new_index == $i
-                       for (my $i = 0; $i < @$InDS; $i++)
-                       {
-                               next if ($ds_index->[$i] >= 0);
-
-                               if ($1 eq $InDS->[$i])
-                               {
-                                       $line =~ s#<name>\s*([^<\s]+)\s*</name>#<name> $OutDS->[$i] </name>#;
-                                       $ds_index->[$i] = $current_index;
-                                       last;
-                               }
-                       }
-
-                       $out_cache->[$current_index] .= $line;
-               }
-               elsif ($line =~ m#</ds>#)
-               {
-                       $out_cache->[$current_index] .= $line;
-               }
-               elsif ($line =~ m#<rra>#)
-               {
-                       # Print out all the DS definitions we need
-                       for (my $new_index = 0; $new_index < @$InDS; $new_index++)
-                       {
-                               my $old_index = $ds_index->[$new_index];
-                               while ($out_cache->[$old_index] =~ m/^(.*)$/gm)
-                               {
-                                       post_line ("$1\n", $index + 1);
-                               }
-                       }
-
-                       # Clear the cache - it's used in state1, too.
-                       for (my $i = 0; $i <= $current_index; $i++)
-                       {
-                               $out_cache->[$i] = '';
-                       }
-
-                       $ret .= $line;
-                       $current_index = -1;
-                       $state = 1;
-               }
-               elsif ($current_index == -1)
-               {
-                       # Print all the lines before the first DS definition
-                       $ret .= $line;
-               }
-               else
-               {
-                       # Something belonging to a DS-definition
-                       $out_cache->[$current_index] .= $line;
-               }
-       }
-       elsif ($state == 1)
-       {
-               if ($line =~ m#<ds>#)
-               {
-                       $current_index++;
-                       $out_cache->[$current_index] .= $line;
-               }
-               elsif ($line =~ m#</cdp_prep>#)
-               {
-                       # Print out all the DS definitions we need
-                       for (my $new_index = 0; $new_index < @$InDS; $new_index++)
-                       {
-                               my $old_index = $ds_index->[$new_index];
-                               while ($out_cache->[$old_index] =~ m/^(.*)$/gm)
-                               {
-                                       post_line ("$1\n", $index + 1);
-                               }
-                       }
-
-                       # Clear the cache
-                       for (my $i = 0; $i <= $current_index; $i++)
-                       {
-                               $out_cache->[$i] = '';
-                       }
-
-                       $ret .= $line;
-                       $current_index = -1;
-               }
-               elsif ($line =~ m#<database>#)
-               {
-                       $ret .= $line;
-                       $state = 2;
-               }
-               elsif ($current_index == -1)
-               {
-                       # Print all the lines before the first DS definition
-                       # and after cdp_prep
-                       $ret .= $line;
-               }
-               else
-               {
-                       # Something belonging to a DS-definition
-                       $out_cache->[$current_index] .= $line;
-               }
-       }
-       elsif ($state == 2)
-       {
-               if ($line =~ m#</database>#)
-               {
-                       $ret .= $line;
-                       $current_index = -1;
-                       $state = 1;
-               }
-               else
-               {
-                       my @values = ();
-                       my $i;
-                       
-                       $ret .= "\t\t";
-
-                       if ($line =~ m#(<!-- .*? -->)#)
-                       {
-                               $ret .= "$1 ";
-                       }
-                       $ret .= "<row> ";
-
-                       $i = 0;
-                       while ($line =~ m#<v>\s*([^<\s]+)\s*</v>#g)
-                       {
-                               $values[$i] = $1;
-                               $i++;
-                       }
-
-                       for (my $new_index = 0; $new_index < @$InDS; $new_index++)
-                       {
-                               my $old_index = $ds_index->[$new_index];
-                               $ret .= '<v> ' . $values[$old_index] . ' </v> ';
-                       }
-                       $ret .= "</row>\n";
-               }
-       }
-       else
-       {
-               die;
-       }
-
-       if ($ret)
-       {
-               post_line ($ret, $index + 1);
-       }
-}} # handle_line_dsmap
-
-#
-# The _step_ handler
-#
-{
-my $step_factor_up;
-my $step_factor_down;
-sub handle_line_step
-{
-       my $line = shift;
-       my $index = shift;
-
-       if (!$Step)
-       {
-               post_line ($line, $index + 1);
-               return;
-       }
-
-       $step_factor_up ||= 0;
-       $step_factor_down ||= 0;
-
-       if (($step_factor_up == 0) && ($step_factor_down == 0))
-       {
-               if ($line =~ m#<step>\s*(\d+)\s*</step>#i)
-               {
-                       my $old_step = 0 + $1;
-                       if ($Step < $old_step)
-                       {
-                               $step_factor_down = int ($old_step / $Step);
-                               if (($step_factor_down * $Step) != $old_step)
-                               {
-                                       print STDERR "The old step ($old_step seconds) "
-                                       . "is not a multiple of the new step "
-                                       . "($Step seconds).\n";
-                                       exit (1);
-                               }
-                               $line = "<step> $Step </step>\n";
-                       }
-                       elsif ($Step > $old_step)
-                       {
-                               $step_factor_up = int ($Step / $old_step);
-                               if (($step_factor_up * $old_step) != $Step)
-                               {
-                                       print STDERR "The new step ($Step seconds) "
-                                       . "is not a multiple of the old step "
-                                       . "($old_step seconds).\n";
-                                       exit (1);
-                               }
-                               $line = "<step> $Step </step>\n";
-                       }
-                       else
-                       {
-                               $Step = 0;
-                       }
-               }
-       }
-       elsif ($line =~ m#<pdp_per_row>\s*(\d+)\s*</pdp_per_row>#i)
-       {
-               my $old_val = 0 + $1;
-               my $new_val;
-               if ($step_factor_up)
-               {
-                       $new_val = int ($old_val / $step_factor_up);
-                       if (($new_val * $step_factor_up) != $old_val)
-                       {
-                               print STDERR "Can't divide number of PDPs per row ($old_val) by step-factor ($step_factor_up).\n";
-                               exit (1);
-                       }
-               }
-               else
-               {
-                       $new_val = $step_factor_down * $old_val;
-               }
-               $line = "<pdp_per_row> $new_val </pdp_per_row>\n";
-       }
-
-       post_line ($line, $index + 1);
-}} # handle_line_step
-
-#
-# The _add RRA_ handler
-#
-{
-my $add_rra_done;
-my $num_ds;
-sub handle_line_add_rra
-{
-  my $line = shift;
-  my $index = shift;
-
-  my $post = sub { for (@_) { post_line ($_, $index + 1); } };
-
-  $num_ds ||= 0;
-
-  if (!@$NewRRAs || $add_rra_done)
-  {
-    $post->($line);
-    return;
-  }
-
-  if ($line =~ m#<ds>#i)
-  {
-    $num_ds++;
-  }
-  elsif ($line =~ m#<rra>#i)
-  {
-    for (my $i = 0; $i < @$NewRRAs; $i++)
-    {
-      my $rra = $NewRRAs->[$i];
-      my $temp;
-      $post->("\t<rra>\n",
-      "\t\t<cf> $rra->{'cf'} </cf>\n",
-      "\t\t<pdp_per_row> $rra->{'steps'} </pdp_per_row>\n",
-      "\t\t<params>\n",
-      "\t\t\t<xff> $rra->{'xff'} </xff>\n",
-      "\t\t</params>\n",
-      "\t\t<cdp_prep>\n");
-
-      for (my $j = 0; $j < $num_ds; $j++)
-      {
-       $post->("\t\t\t<ds>\n",
-       "\t\t\t\t<primary_value> NaN </primary_value>\n",
-       "\t\t\t\t<secondary_value> NaN </secondary_value>\n",
-       "\t\t\t\t<value> NaN </value>\n",
-       "\t\t\t\t<unknown_datapoints> 0 </unknown_datapoints>\n",
-       "\t\t\t</ds>\n");
-      }
-
-      $post->("\t\t</cdp_prep>\n", "\t\t<database>\n");
-      $temp = "\t\t\t<row>" . join ('', map { "<v> NaN </v>" } (1 .. $num_ds)) . "</row>\n";
-      for (my $j = 0; $j < $rra->{'rows'}; $j++)
-      {
-       $post->($temp);
-      }
-      $post->("\t\t</database>\n");
-    }
-  }
-
-  $post->($line);
-}} # handle_line_add_rra
-
-#
-# The _output_ handler
-#
-{
-my $fh;
-sub set_output
-{
-       $fh = shift;
-}
-
-sub handle_line_output
-{
-       my $line = shift;
-       my $index = shift;
-
-       if (!defined ($fh))
-       {
-               post_line ($line, $index + 1);
-               return;
-       }
-       
-       print $fh $line;
-}} # handle_line_output
-
-#
-# Dispatching logic
-#
-{
-my @handlers = ();
-sub add_handler
-{
-       my $handler = shift;
-
-       die unless (ref ($handler) eq 'CODE');
-       push (@handlers, $handler);
-} # add_handler
-
-sub post_line
-{
-       my $line = shift;
-       my $index = shift;
-
-       if (0)
-       {
-               my $copy = $line;
-               chomp ($copy);
-               print "DEBUG: post_line ($copy, $index);\n";
-       }
-
-       if ($index > $#handlers)
-       {
-               return;
-       }
-       $handlers[$index]->($line, $index);
-}} # post_line
-
-sub handle_fh
-{
-       my $in_fh = shift;
-       my $out_fh = shift;
-
-       set_output ($out_fh);
-
-       if (@$InDS)
-       {
-         add_handler (\&handle_line_dsmap);
-       }
-
-       if ($Step)
-       {
-         add_handler (\&handle_line_step);
-       }
-
-       if (@$NewRRAs)
-       {
-         add_handler (\&handle_line_add_rra);
-       }
-
-       add_handler (\&handle_line_output);
-
-       while (my $line = <$in_fh>)
-       {
-               post_line ($line, 0);
-       }
-} # handle_fh
-
-sub main
-{
-       my $in_file = shift;
-       my $out_file = shift;
-
-       my $in_fh;
-       my $out_fh;
-
-       my $in_needs_close = 1;
-       my $out_needs_close = 1;
-
-       if ($in_file =~ m/\.rrd$/i)
-       {
-               open ($in_fh,  '-|', 'rrdtool', 'dump', $in_file) or die ("open (rrdtool): $!");
-       }
-       elsif ($in_file eq '-')
-       {
-               $in_fh = \*STDIN;
-               $in_needs_close = 0;
-       }
-       else
-       {
-               open ($in_fh, '<', $in_file) or die ("open ($in_file): $!");
-       }
-
-       if ($out_file =~ m/\.rrd$/i)
-       {
-               open ($out_fh, '|-', 'rrdtool', 'restore', '-', $out_file) or die ("open (rrdtool): $!");
-       }
-       elsif ($out_file eq '-')
-       {
-               $out_fh = \*STDOUT;
-               $out_needs_close = 0;
-       }
-       else
-       {
-               open ($out_fh, '>', $out_file) or die ("open ($out_file): $!");
-       }
-
-       handle_fh ($in_fh, $out_fh);
-
-       if ($in_needs_close)
-       {
-               close ($in_fh);
-       }
-       if ($out_needs_close)
-       {
-               close ($out_fh);
-       }
-} # main
-
-=head1 LICENSE
-
-This script is licensed under the GNU general public license, versionE<nbsp>2
-(GPLv2).
-
-=head1 AUTHOR
-
-Florian octo Forster E<lt>octo at verplant.orgE<gt>
-
index 9fa1139..673929c 100755 (executable)
@@ -166,7 +166,7 @@ for (@Files)
                        my $src_ds = $src_dses->[$i];
                        $dest->{'type_instance'} = $type_instances->[$i];
                        $dest_filename = get_filename ($dest);
-                       print "./extractDS.px -i '$InDir/$orig_filename' -s '$src_ds' -o '$OutDir/$dest_filename' -d '$dst_ds'\n";
+                       print "./rrd_filter.px -i '$InDir/$orig_filename' -m '${src_ds}:${dst_ds}' -o '$OutDir/$dest_filename'\n";
                }
        }
        elsif (exists ($TypeRename{$orig->{'type'}}))
@@ -361,19 +361,19 @@ sub special_disk
                $OutDirs{$dest_directory} = 1;
        }
 
-       print "./extractDS.px -i '$InDir/$orig_filename' -s 'rmerged' -s 'wmerged' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
+       print "./rrd_filter.px -i '$InDir/$orig_filename' -m 'rmerged:read' -m 'wmerged:write' -o '$OutDir/$dest_filename'\n";
 
        $dest->{'type'} = 'disk_octets';
        $dest_filename = get_filename ($dest);
-       print "./extractDS.px -i '$InDir/$orig_filename' -s 'rbytes' -s 'wbytes' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
+       print "./rrd_filter.px -i '$InDir/$orig_filename' -m 'rbytes:read' -m 'wbytes:write' -o '$OutDir/$dest_filename'\n";
 
        $dest->{'type'} = 'disk_ops';
        $dest_filename = get_filename ($dest);
-       print "./extractDS.px -i '$InDir/$orig_filename' -s 'rcount' -s 'wcount' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
+       print "./rrd_filter.px -i '$InDir/$orig_filename' -m 'rcount:read' -m 'wcount:write' -o '$OutDir/$dest_filename'\n";
 
        $dest->{'type'} = 'disk_time';
        $dest_filename = get_filename ($dest);
-       print "./extractDS.px -i '$InDir/$orig_filename' -s 'rtime' -s 'wtime' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
+       print "./rrd_filter.px -i '$InDir/$orig_filename' -m 'rtime:read' -m 'wtime:write' -o '$OutDir/$dest_filename'\n";
 }
 
 sub exit_usage
diff --git a/contrib/rrd_filter.px b/contrib/rrd_filter.px
new file mode 100755 (executable)
index 0000000..d28f9f2
--- /dev/null
@@ -0,0 +1,874 @@
+#!/usr/bin/perl
+
+# collectd - contrib/rrd_filter.px
+# 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
+#
+# Authors:
+#   Florian octo Forster <octo at verplant.org>
+
+use strict;
+use warnings;
+
+=head1 NAME
+
+rrd_filter.px - Perform same advanced non-standard operations on an RRD file.
+
+=head1 SYNOPSYS
+
+  rrd_filter.px -i input.rrd -o output.rrd [options]
+
+=head1 DEPENDENCIES
+
+rrd_filter.px requires the RRDTool binary, Perl and the included
+L<Getopt::Long> module.
+
+=cut
+
+use Getopt::Long ('GetOptions');
+
+our $InFile;
+our $InDS = [];
+our $OutFile;
+our $OutDS = [];
+
+our $NewDSes = [];
+our $NewRRAs = [];
+
+our $Step = 0;
+
+our $Scale = 1.0;
+our $Shift = 0.0;
+
+our $Debug = 0;
+
+=head1 OPTIONS
+
+The following options can be passed on the command line:
+
+=over 4
+
+=item B<--infile> I<file>
+
+=item B<-i> I<file>
+
+Reads from I<file>. If I<file> ends in C<.rrd>, then C<rrdtool dump> is invoked
+to create an XML dump of the RRD file. Otherwise the XML dump is expected
+directly. The special filename C<-> can be used to read from STDIN.
+
+=item B<--outfile> I<file>
+
+=item B<-o> I<file>
+
+Writes output to I<file>. If I<file> ends in C<.rrd>, then C<rrdtool restore>
+is invoked to create a binary RRD file. Otherwise an XML output is written. The
+special filename C<-> can be used to write to STDOUT.
+
+=item B<--map> I<in_ds>:I<out_ds>
+
+=item B<-m> I<in_ds>:I<out_ds>
+
+Writes the datasource I<in_ds> to the output and renames it to I<out_ds>. This
+is useful to extract one DS from an RRD file.
+
+=item B<--step> I<seconds>
+
+=item B<-s> I<seconds>
+
+Changes the step of the output RRD file to be I<seconds>. The new stepsize must
+be a multiple of the old stepsize of the other way around. When increasing the
+stepsize the number of PDPs in each RRA must be dividable by the factor by
+which the stepsize is increased. The length of CDPs and the absolute length of
+RRAs (and thus the data itself) is not altered.
+
+Examples:
+
+  step =  10, rra_steps = 12   =>   step = 60, rra_steps =  2
+  step = 300, rra_steps =  1   =>   step = 10, rra_steps = 30
+
+=item B<--rra> B<RRA>:I<CF>:I<XFF>:I<steps>:I<rows>
+
+=item B<-a> B<RRA>:I<CF>:I<XFF>:I<steps>:I<rows>
+
+Inserts a new RRA in the generated RRD file. This is done B<after> the step has
+been adjusted, take that into account when specifying I<steps> and I<rows>. For
+an explanation of the format please see L<rrdcreate(1)>.
+
+=item B<--scale> I<factor>
+
+Scales the values by the factor I<factor>, i.E<nbsp>e. all values are
+multiplied by I<factor>.
+
+=item B<--shift> I<offset>
+
+Shifts all values by I<offset>, i.E<nbsp>e. I<offset> is added to all values.
+
+=back
+
+=cut
+
+GetOptions ("infile|i=s" => \$InFile,
+       "outfile|o=s" => \$OutFile,
+       'map|m=s' => sub
+       {
+               my ($in_ds, $out_ds) = split (':', $_[1]);
+               if (!defined ($in_ds) || !defined ($out_ds))
+               {
+                       print STDERR "Argument for `map' incorrect! The format is `--map in_ds:out_ds'\n";
+                       exit (1);
+               }
+               push (@$InDS, $in_ds);
+               push (@$OutDS, $out_ds);
+       },
+       'step|s=i' => \$Step,
+       'ds|d=s' => sub
+       {
+               #DS:ds-name:GAUGE | COUNTER | DERIVE | ABSOLUTE:heartbeat:min:max
+               my ($ds, $name, $type, $hb, $min, $max) = split (':', $_[1]);
+               if (($ds ne 'DS') || !defined ($max))
+               {
+                       print STDERR "Please use the standard RRDTool syntax when adding DSes. I. e. DS:<name>:<type>:<heartbeat>:<min>:<max>.\n";
+                       exit (1);
+               }
+               push (@$NewDSes, {name => $name, type => $type, heartbeat => $hb, min => $min, max => $max});
+       },
+       'rra|a=s' => sub
+       {
+               my ($rra, $cf, $xff, $steps, $rows) = split (':', $_[1]);
+               if (($rra ne 'RRA') || !defined ($rows))
+               {
+                       print STDERR "Please use the standard RRDTool syntax when adding RRAs. I. e. RRA:<cf><xff>:<steps>:<rows>.\n";
+                       exit (1);
+               }
+               push (@$NewRRAs, {cf => $cf, xff => $xff, steps => $steps, rows => $rows});
+       },
+       'scale=f' => \$Scale,
+       'shift=f' => \$Shift
+) or exit (1);
+
+if (!$InFile || !$OutFile)
+{
+       print STDERR "Usage: $0 -i <infile> -m <in_ds>:<out_ds> -s <step>\n";
+       exit (1);
+}
+if ((1 + @$InDS) != (1 + @$OutDS))
+{
+       print STDERR "You need the same amount of in- and out-DSes\n";
+       exit (1);
+}
+main ($InFile, $OutFile);
+exit (0);
+
+{
+my $ds_index;
+my $current_index;
+# state 0 == searching for DS index
+# state 1 == parse RRA header
+# state 2 == parse values
+my $state;
+my $out_cache;
+sub handle_line_dsmap
+{
+       my $line = shift;
+       my $index = shift;
+       my $ret = '';
+
+       if ((@$InDS == 0) || (@$OutDS == 0))
+       {
+               post_line ($line, $index + 1);
+               return;
+       }
+
+       if (!defined ($state))
+       {
+               $current_index = -1;
+               $state = 0;
+               $out_cache = [];
+
+               # $ds_index->[new_index] = old_index
+               $ds_index = [];
+               for (my $i = 0; $i < @$InDS; $i++)
+               {
+                       print STDOUT "DS map $i: $InDS->[$i] -> $OutDS->[$i]\n" if ($Debug);
+                       $ds_index->[$i] = -1;
+               }
+       }
+
+       if ($state == 0)
+       {
+               if ($line =~ m/<ds>/)
+               {
+                       $current_index++;
+                       $out_cache->[$current_index] = $line;
+               }
+               elsif ($line =~ m#<name>\s*([^<\s]+)\s*</name>#)
+               {
+                       # old_index == $current_index
+                       # new_index == $i
+                       for (my $i = 0; $i < @$InDS; $i++)
+                       {
+                               next if ($ds_index->[$i] >= 0);
+
+                               if ($1 eq $InDS->[$i])
+                               {
+                                       $line =~ s#<name>\s*([^<\s]+)\s*</name>#<name> $OutDS->[$i] </name>#;
+                                       $ds_index->[$i] = $current_index;
+                                       last;
+                               }
+                       }
+
+                       $out_cache->[$current_index] .= $line;
+               }
+               elsif ($line =~ m#<last_ds>\s*([^\s>]+)\s*</last_ds>#i)
+               {
+                       $out_cache->[$current_index] .= "\t\t<last_ds> NaN </last_ds>\n";
+               }
+               elsif ($line =~ m#<value>\s*([^\s>]+)\s*</value>#i)
+               {
+                       $out_cache->[$current_index] .= "\t\t<value> NaN </value>\n";
+               }
+               elsif ($line =~ m#</ds>#)
+               {
+                       $out_cache->[$current_index] .= $line;
+               }
+               elsif ($line =~ m#<rra>#)
+               {
+                       # Print out all the DS definitions we need
+                       for (my $new_index = 0; $new_index < @$InDS; $new_index++)
+                       {
+                               my $old_index = $ds_index->[$new_index];
+                               while ($out_cache->[$old_index] =~ m/^(.*)$/gm)
+                               {
+                                       post_line ("$1\n", $index + 1);
+                               }
+                       }
+
+                       # Clear the cache - it's used in state1, too.
+                       for (my $i = 0; $i <= $current_index; $i++)
+                       {
+                               $out_cache->[$i] = '';
+                       }
+
+                       $ret .= $line;
+                       $current_index = -1;
+                       $state = 1;
+               }
+               elsif ($current_index == -1)
+               {
+                       # Print all the lines before the first DS definition
+                       $ret .= $line;
+               }
+               else
+               {
+                       # Something belonging to a DS-definition
+                       $out_cache->[$current_index] .= $line;
+               }
+       }
+       elsif ($state == 1)
+       {
+               if ($line =~ m#<ds>#)
+               {
+                       $current_index++;
+                       $out_cache->[$current_index] .= $line;
+               }
+               elsif ($line =~ m#<value>\s*([^\s>]+)\s*</value>#i)
+               {
+                       $out_cache->[$current_index] .= "\t\t\t<value> NaN </value>\n";
+               }
+               elsif ($line =~ m#</cdp_prep>#)
+               {
+                       # Print out all the DS definitions we need
+                       for (my $new_index = 0; $new_index < @$InDS; $new_index++)
+                       {
+                               my $old_index = $ds_index->[$new_index];
+                               while ($out_cache->[$old_index] =~ m/^(.*)$/gm)
+                               {
+                                       post_line ("$1\n", $index + 1);
+                               }
+                       }
+
+                       # Clear the cache
+                       for (my $i = 0; $i <= $current_index; $i++)
+                       {
+                               $out_cache->[$i] = '';
+                       }
+
+                       $ret .= $line;
+                       $current_index = -1;
+               }
+               elsif ($line =~ m#<database>#)
+               {
+                       $ret .= $line;
+                       $state = 2;
+               }
+               elsif ($current_index == -1)
+               {
+                       # Print all the lines before the first DS definition
+                       # and after cdp_prep
+                       $ret .= $line;
+               }
+               else
+               {
+                       # Something belonging to a DS-definition
+                       $out_cache->[$current_index] .= $line;
+               }
+       }
+       elsif ($state == 2)
+       {
+               if ($line =~ m#</database>#)
+               {
+                       $ret .= $line;
+                       $current_index = -1;
+                       $state = 1;
+               }
+               else
+               {
+                       my @values = ();
+                       my $i;
+                       
+                       $ret .= "\t\t";
+
+                       if ($line =~ m#(<!-- .*? -->)#)
+                       {
+                               $ret .= "$1 ";
+                       }
+                       $ret .= "<row> ";
+
+                       $i = 0;
+                       while ($line =~ m#<v>\s*([^<\s]+)\s*</v>#g)
+                       {
+                               $values[$i] = $1;
+                               $i++;
+                       }
+
+                       for (my $new_index = 0; $new_index < @$InDS; $new_index++)
+                       {
+                               my $old_index = $ds_index->[$new_index];
+                               $ret .= '<v> ' . $values[$old_index] . ' </v> ';
+                       }
+                       $ret .= "</row>\n";
+               }
+       }
+       else
+       {
+               die;
+       }
+
+       if ($ret)
+       {
+               post_line ($ret, $index + 1);
+       }
+}} # handle_line_dsmap
+
+#
+# The _step_ handler
+#
+{
+my $step_factor_up;
+my $step_factor_down;
+sub handle_line_step
+{
+       my $line = shift;
+       my $index = shift;
+
+       if (!$Step)
+       {
+               post_line ($line, $index + 1);
+               return;
+       }
+
+       if ($Debug && !defined ($step_factor_up))
+       {
+               print STDOUT "New step: $Step\n";
+       }
+
+       $step_factor_up ||= 0;
+       $step_factor_down ||= 0;
+
+       if (($step_factor_up == 0) && ($step_factor_down == 0))
+       {
+               if ($line =~ m#<step>\s*(\d+)\s*</step>#i)
+               {
+                       my $old_step = 0 + $1;
+                       if ($Step < $old_step)
+                       {
+                               $step_factor_down = int ($old_step / $Step);
+                               if (($step_factor_down * $Step) != $old_step)
+                               {
+                                       print STDERR "The old step ($old_step seconds) "
+                                       . "is not a multiple of the new step "
+                                       . "($Step seconds).\n";
+                                       exit (1);
+                               }
+                               $line = "<step> $Step </step>\n";
+                       }
+                       elsif ($Step > $old_step)
+                       {
+                               $step_factor_up = int ($Step / $old_step);
+                               if (($step_factor_up * $old_step) != $Step)
+                               {
+                                       print STDERR "The new step ($Step seconds) "
+                                       . "is not a multiple of the old step "
+                                       . "($old_step seconds).\n";
+                                       exit (1);
+                               }
+                               $line = "<step> $Step </step>\n";
+                       }
+                       else
+                       {
+                               $Step = 0;
+                       }
+               }
+       }
+       elsif ($line =~ m#<pdp_per_row>\s*(\d+)\s*</pdp_per_row>#i)
+       {
+               my $old_val = 0 + $1;
+               my $new_val;
+               if ($step_factor_up)
+               {
+                       $new_val = int ($old_val / $step_factor_up);
+                       if (($new_val * $step_factor_up) != $old_val)
+                       {
+                               print STDERR "Can't divide number of PDPs per row ($old_val) by step-factor ($step_factor_up).\n";
+                               exit (1);
+                       }
+               }
+               else
+               {
+                       $new_val = $step_factor_down * $old_val;
+               }
+               $line = "<pdp_per_row> $new_val </pdp_per_row>\n";
+       }
+
+       post_line ($line, $index + 1);
+}} # handle_line_step
+
+#
+# The _add DS_ handler
+#
+{
+my $add_ds_done;
+sub handle_line_add_ds
+{
+  my $line = shift;
+  my $index = shift;
+
+  my $post = sub { for (@_) { post_line ($_, $index + 1); } };
+
+  if (!@$NewDSes)
+  {
+    $post->($line);
+    return;
+  }
+
+  if (!$add_ds_done && ($line =~ m#<rra>#i))
+  {
+    for (my $i = 0; $i < @$NewDSes; $i++)
+    {
+      my $ds = $NewDSes->[$i];
+      my $temp;
+
+      my $min;
+      my $max;
+
+      if ($Debug)
+      {
+       print STDOUT "Adding DS: name = $ds->{'name'}, type = $ds->{'type'}, heartbeat = $ds->{'heartbeat'}, min = $ds->{'min'}, max = $ds->{'max'}\n";
+      }
+
+      $min = 'NaN';
+      if (defined ($ds->{'min'}) && ($ds->{'min'} ne 'U'))
+      {
+       $min = sprintf ('%.10e', $ds->{'min'});
+      }
+      
+      $max = 'NaN';
+      if (defined ($ds->{'max'}) && ($ds->{'max'} ne 'U'))
+      {
+       $max = sprintf ('%.10e', $ds->{'max'});
+      }
+      
+
+      $post->("\t<ds>\n",
+      "\t\t<name> $ds->{'name'} </name>\n",
+      "\t\t<type> $ds->{'type'} </type>\n",
+      "\t\t<minimal_heartbeat> $ds->{'heartbeat'} </minimal_heartbeat>\n",
+      "\t\t<min> $min </min>\n",
+      "\t\t<max> $max </max>\n",
+      "\n",
+      "\t\t<!-- PDP Status -->\n",
+      "\t\t<last_ds> UNKN </last_ds>\n",
+      "\t\t<value> NaN </value>\n",
+      "\t\t<unknown_sec> 0 </unknown_sec>\n",
+      "\t</ds>\n",
+      "\n");
+    }
+
+    $add_ds_done = 1;
+  }
+  elsif ($add_ds_done && ($line =~ m#</ds>#i)) # inside a cdp_prep block
+  {
+    $post->("\t\t\t</ds>\n",
+       "\t\t\t<ds>\n",
+       "\t\t\t<primary_value> NaN </primary_value>\n",
+       "\t\t\t<secondary_value> NaN </secondary_value>\n",
+       "\t\t\t<value> NaN </value>\n",
+       "\t\t\t<unknown_datapoints> 0 </unknown_datapoints>\n");
+  }
+  elsif ($line =~ m#<row>#i)
+  {
+         my $insert = '<v> NaN </v>' x (0 + @$NewDSes);
+         $line =~ s#</row>#$insert</row>#i;
+  }
+
+  $post->($line);
+}} # handle_line_add_ds
+
+#
+# The _add RRA_ handler
+#
+{
+my $add_rra_done;
+my $num_ds;
+sub handle_line_add_rra
+{
+  my $line = shift;
+  my $index = shift;
+
+  my $post = sub { for (@_) { post_line ($_, $index + 1); } };
+
+  $num_ds ||= 0;
+
+  if (!@$NewRRAs || $add_rra_done)
+  {
+    $post->($line);
+    return;
+  }
+
+  if ($line =~ m#<ds>#i)
+  {
+    $num_ds++;
+  }
+  elsif ($line =~ m#<rra>#i)
+  {
+    for (my $i = 0; $i < @$NewRRAs; $i++)
+    {
+      my $rra = $NewRRAs->[$i];
+      my $temp;
+
+      if ($Debug)
+      {
+       print STDOUT "Adding RRA: CF = $rra->{'cf'}, xff = $rra->{'xff'}, steps = $rra->{'steps'}, rows = $rra->{'rows'}, num_ds = $num_ds\n";
+      }
+
+      $post->("\t<rra>\n",
+      "\t\t<cf> $rra->{'cf'} </cf>\n",
+      "\t\t<pdp_per_row> $rra->{'steps'} </pdp_per_row>\n",
+      "\t\t<params>\n",
+      "\t\t\t<xff> $rra->{'xff'} </xff>\n",
+      "\t\t</params>\n",
+      "\t\t<cdp_prep>\n");
+
+      for (my $j = 0; $j < $num_ds; $j++)
+      {
+       $post->("\t\t\t<ds>\n",
+       "\t\t\t\t<primary_value> NaN </primary_value>\n",
+       "\t\t\t\t<secondary_value> NaN </secondary_value>\n",
+       "\t\t\t\t<value> NaN </value>\n",
+       "\t\t\t\t<unknown_datapoints> 0 </unknown_datapoints>\n",
+       "\t\t\t</ds>\n");
+      }
+
+      $post->("\t\t</cdp_prep>\n", "\t\t<database>\n");
+      $temp = "\t\t\t<row>" . join ('', map { "<v> NaN </v>" } (1 .. $num_ds)) . "</row>\n";
+      for (my $j = 0; $j < $rra->{'rows'}; $j++)
+      {
+       $post->($temp);
+      }
+      $post->("\t\t</database>\n", "\t</rra>\n");
+    }
+
+    $add_rra_done = 1;
+  }
+
+  $post->($line);
+}} # handle_line_add_rra
+
+#
+# The _scale/shift_ handler
+#
+sub calculate_scale_shift 
+{
+  my $value = shift;
+  my $tag = shift;
+  my $scale = shift;
+  my $shift = shift;
+
+  if (lc ("$value") eq 'nan')
+  {
+    $value = 'NaN';
+    return ("<$tag> NaN </$tag>");
+  }
+
+  $value = ($scale * (0.0 + $value)) + $shift;
+  return (sprintf ("<%s> %1.10e </%s>", $tag, $value, $tag));
+}
+
+sub handle_line_scale_shift
+{
+  my $line = shift;
+  my $index = shift;
+
+  if (($Scale != 1.0) || ($Shift != 0.0))
+  {
+    $line =~ s#<(min|max|last_ds|value|primary_value|secondary_value|v)>\s*([^\s<]+)\s*</[^>]+>#calculate_scale_shift ($2, $1, $Scale, $Shift)#eg;
+  }
+
+  post_line ($line, $index + 1);
+}
+
+#
+# The _output_ handler
+#
+# This filter is unfinished!
+#
+{
+my $fh;
+sub set_output
+{
+       $fh = shift;
+}
+
+{
+my $previous_values;
+my $previous_differences;
+my $pdp_per_row;
+sub handle_line_peak_detect
+{
+  my $line = shift;
+  my $index = shift;
+
+  if (!$previous_values)
+  {
+    $previous_values = [];
+    $previous_differences = [];
+  }
+
+  if ($line =~ m#</database>#i)
+  {
+    $previous_values = [];
+    $previous_differences = [];
+    print STDERR "==============================================================================\n";
+  }
+  elsif ($line =~ m#<pdp_per_row>\s*([1-9][0-9]*)\s*</pdp_per_row>#)
+  {
+    $pdp_per_row = int ($1);
+    print STDERR "pdp_per_row = $pdp_per_row;\n";
+  }
+  elsif ($line =~ m#<row>#)
+  {
+    my @values = ();
+    while ($line =~ m#<v>\s*([^\s>]+)\s*</v>#ig)
+    {
+      if ($1 eq 'NaN')
+      {
+       push (@values, undef);
+      }
+      else
+      {
+       push (@values, 0.0 + $1);
+      }
+    }
+
+    for (my $i = 0; $i < @values; $i++)
+    {
+      if (!defined ($values[$i]))
+      {
+       $previous_values->[$i] = undef;
+      }
+      elsif (!defined ($previous_values->[$i]))
+      {
+       $previous_values->[$i] = $values[$i];
+      }
+      elsif (!defined ($previous_differences->[$i]))
+      {
+       $previous_differences->[$i] = abs ($previous_values->[$i] - $values[$i]);
+      }
+      else
+      {
+       my $divisor = ($previous_differences->[$i] < 1.0) ? 1.0 : $previous_differences->[$i];
+       my $difference = abs ($previous_values->[$i] - $values[$i]);
+       my $change = $pdp_per_row * $difference / $divisor;
+       if (($divisor > 10.0) &&  ($change > 10e5))
+       {
+         print STDERR "i = $i; average difference = " . $previous_differences->[$i]. "; current difference = " . $difference. "; change = $change;\n";
+       }
+       $previous_values->[$i] = $values[$i];
+       $previous_differences->[$i] = (0.95 * $previous_differences->[$i]) + (0.05 * $difference);
+      }
+    }
+  }
+
+  post_line ($line, $index + 1);
+}} # handle_line_peak_detect
+
+sub handle_line_output
+{
+       my $line = shift;
+       my $index = shift;
+
+       if (!defined ($fh))
+       {
+               post_line ($line, $index + 1);
+               return;
+       }
+       
+       print $fh $line;
+}} # handle_line_output
+
+#
+# Dispatching logic
+#
+{
+my @handlers = ();
+sub add_handler
+{
+       my $handler = shift;
+
+       die unless (ref ($handler) eq 'CODE');
+       push (@handlers, $handler);
+} # add_handler
+
+sub post_line
+{
+       my $line = shift;
+       my $index = shift;
+
+       if (0)
+       {
+               my $copy = $line;
+               chomp ($copy);
+               print "DEBUG: post_line ($copy, $index);\n";
+       }
+
+       if ($index > $#handlers)
+       {
+               return;
+       }
+       $handlers[$index]->($line, $index);
+}} # post_line
+
+sub handle_fh
+{
+  my $in_fh = shift;
+  my $out_fh = shift;
+
+  set_output ($out_fh);
+
+  if (@$InDS)
+  {
+    add_handler (\&handle_line_dsmap);
+  }
+
+  if ($Step)
+  {
+    add_handler (\&handle_line_step);
+  }
+
+  if (($Scale != 1.0) || ($Shift != 0.0))
+  {
+    add_handler (\&handle_line_scale_shift);
+  }
+
+  #add_handler (\&handle_line_peak_detect);
+
+  if (@$NewDSes)
+  {
+    add_handler (\&handle_line_add_ds);
+  }
+
+  if (@$NewRRAs)
+  {
+    add_handler (\&handle_line_add_rra);
+  }
+
+  add_handler (\&handle_line_output);
+
+  while (my $line = <$in_fh>)
+  {
+    post_line ($line, 0);
+  }
+} # handle_fh
+
+sub main
+{
+       my $in_file = shift;
+       my $out_file = shift;
+
+       my $in_fh;
+       my $out_fh;
+
+       my $in_needs_close = 1;
+       my $out_needs_close = 1;
+
+       if ($in_file =~ m/\.rrd$/i)
+       {
+               open ($in_fh,  '-|', 'rrdtool', 'dump', $in_file) or die ("open (rrdtool): $!");
+       }
+       elsif ($in_file eq '-')
+       {
+               $in_fh = \*STDIN;
+               $in_needs_close = 0;
+       }
+       else
+       {
+               open ($in_fh, '<', $in_file) or die ("open ($in_file): $!");
+       }
+
+       if ($out_file =~ m/\.rrd$/i)
+       {
+               open ($out_fh, '|-', 'rrdtool', 'restore', '-', $out_file) or die ("open (rrdtool): $!");
+       }
+       elsif ($out_file eq '-')
+       {
+               $out_fh = \*STDOUT;
+               $out_needs_close = 0;
+       }
+       else
+       {
+               open ($out_fh, '>', $out_file) or die ("open ($out_file): $!");
+       }
+
+       handle_fh ($in_fh, $out_fh);
+
+       if ($in_needs_close)
+       {
+               close ($in_fh);
+       }
+       if ($out_needs_close)
+       {
+               close ($out_fh);
+       }
+} # main
+
+=head1 LICENSE
+
+This script is licensed under the GNU general public license, versionE<nbsp>2
+(GPLv2).
+
+=head1 AUTHOR
+
+Florian octo Forster E<lt>octo at verplant.orgE<gt>
+
index 7afe3f1..00f56e7 100644 (file)
@@ -63,6 +63,11 @@ use statements like the following:
 
   Include "/etc/collectd.d/*.conf"
 
+If more than one files are included by a single B<Include> option, the files
+will be included in lexicographical order (as defined by the C<strcmp>
+function). Thus, you can e.E<nbsp>g. use numbered prefixes to specify the
+order in which the files are loaded.
+
 To prevent loops and shooting yourself in the foot in interesting ways the
 nesting is limited to a depth of 8E<nbsp>levels, which should be sufficient for
 most uses. Since symlinks are followed it is still possible to crash the daemon
index 3489e8d..a2c4794 100644 (file)
@@ -52,11 +52,13 @@ static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_mutex_t strerror_r_lock = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
-void sstrncpy (char *d, const char *s, int len)
+char *sstrncpy (char *dest, const char *src, size_t n)
 {
-       strncpy (d, s, len);
-       d[len - 1] = '\0';
-}
+       strncpy (dest, src, n);
+       dest[n - 1] = '\0';
+
+       return (dest);
+} /* char *sstrncpy */
 
 char *sstrdup (const char *s)
 {
index 8e0d840..e99aea6 100644 (file)
@@ -38,7 +38,7 @@
 
 #define STATIC_ARRAY_SIZE(a) (sizeof (a) / sizeof (*(a)))
 
-void sstrncpy(char *d, const char *s, int len);
+char *sstrncpy (char *dest, const char *src, size_t n);
 char *sstrdup(const char *s);
 void *smalloc(size_t size);
 char *sstrerror (int errnum, char *buf, size_t buflen);
index ce4e774..4a9789a 100644 (file)
@@ -18,6 +18,7 @@
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   Sebastian tokkee Harl <sh at tokkee.org>
  **/
 
 #include "collectd.h"
@@ -423,6 +424,9 @@ static int cf_ci_append_children (oconfig_item_t *dst, oconfig_item_t *src)
 {
        oconfig_item_t *temp;
 
+       if ((src == NULL) || (src->children_num == 0))
+               return (0);
+
        temp = (oconfig_item_t *) realloc (dst->children,
                        sizeof (oconfig_item_t)
                        * (dst->children_num + src->children_num));
@@ -502,13 +506,20 @@ static oconfig_item_t *cf_read_file (const char *file, int depth)
        return (root);
 } /* oconfig_item_t *cf_read_file */
 
+static int cf_compare_string (const void *p1, const void *p2)
+{
+       return strcmp (*(const char **) p1, *(const char **) p2);
+}
+
 static oconfig_item_t *cf_read_dir (const char *dir, int depth)
 {
        oconfig_item_t *root = NULL;
        DIR *dh;
        struct dirent *de;
-       char name[1024];
+       char **filenames = NULL;
+       int filenames_num = 0;
        int status;
+       int i;
 
        assert (depth < CF_MAX_DEPTH);
 
@@ -531,7 +542,8 @@ static oconfig_item_t *cf_read_dir (const char *dir, int depth)
 
        while ((de = readdir (dh)) != NULL)
        {
-               oconfig_item_t *temp;
+               char   name[1024];
+               char **tmp;
 
                if ((de->d_name[0] == '.') || (de->d_name[0] == '\0'))
                        continue;
@@ -543,18 +555,55 @@ static oconfig_item_t *cf_read_dir (const char *dir, int depth)
                        ERROR ("configfile: Not including `%s/%s' because its"
                                        " name is too long.",
                                        dir, de->d_name);
-                       continue;
+                       for (i = 0; i < filenames_num; ++i)
+                               free (filenames[i]);
+                       free (filenames);
+                       free (root);
+                       return (NULL);
+               }
+
+               ++filenames_num;
+               tmp = (char **) realloc (filenames,
+                               filenames_num * sizeof (*filenames));
+               if (tmp == NULL) {
+                       ERROR ("configfile: realloc failed.");
+                       for (i = 0; i < filenames_num - 1; ++i)
+                               free (filenames[i]);
+                       free (filenames);
+                       free (root);
+                       return (NULL);
                }
+               filenames = tmp;
+
+               filenames[filenames_num - 1] = sstrdup (name);
+       }
+
+       qsort ((void *) filenames, filenames_num, sizeof (*filenames),
+                       cf_compare_string);
+
+       for (i = 0; i < filenames_num; ++i)
+       {
+               oconfig_item_t *temp;
+               char *name = filenames[i];
 
                temp = cf_read_generic (name, depth);
-               if (temp == NULL)
-                       continue;
+               if (temp == NULL) {
+                       int j;
+                       for (j = i; j < filenames_num; ++j)
+                               free (filenames[j]);
+                       free (filenames);
+                       oconfig_free (root);
+                       return (NULL);
+               }
 
                cf_ci_append_children (root, temp);
                sfree (temp->children);
                sfree (temp);
+
+               free (name);
        }
 
+       free(filenames);
        return (root);
 } /* oconfig_item_t *cf_read_dir */
 
@@ -600,6 +649,11 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth)
        }
        memset (root, '\0', sizeof (oconfig_item_t));
 
+       /* wordexp() might return a sorted list already. That's not
+        * documented though, so let's make sure we get what we want. */
+       qsort ((void *) we.we_wordv, we.we_wordc, sizeof (*we.we_wordv),
+                       cf_compare_string);
+
        for (i = 0; i < we.we_wordc; i++)
        {
                oconfig_item_t *temp;
@@ -614,6 +668,7 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth)
                        ERROR ("configfile: stat (%s) failed: %s",
                                        path_ptr,
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
+                       oconfig_free (root);
                        return (NULL);
                }
 
@@ -628,6 +683,11 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth)
                        continue;
                }
 
+               if (temp == NULL) {
+                       oconfig_free (root);
+                       return (NULL);
+               }
+
                cf_ci_append_children (root, temp);
                sfree (temp->children);
                sfree (temp);
index 2bb80cc..db9285b 100644 (file)
@@ -76,11 +76,25 @@ void oconfig_free (oconfig_item_t *ci)
 {
   int i;
 
+  if (ci == NULL)
+    return;
+
+  if (ci->key != NULL)
+    free (ci->key);
+
+  for (i = 0; i < ci->values_num; i++)
+    if ((ci->values[i].type == OCONFIG_TYPE_STRING)
+        && (NULL != ci->values[i].value.string))
+      free (ci->values[i].value.string);
+
   if (ci->values != NULL)
     free (ci->values);
 
   for (i = 0; i < ci->children_num; i++)
     oconfig_free (ci->children + i);
+
+  if (ci->children != NULL)
+    free (ci->children);
 }
 
 /*
index bad1a38..50d7363 100644 (file)
@@ -240,7 +240,7 @@ static void trim_spaces (char *s)
 {
        size_t l;
 
-       for (l = strlen (s) - 1; (l > 0) && isspace (s[l]); l--)
+       for (l = strlen (s) - 1; (l > 0) && isspace ((int) s[l]); l--)
                s[l] = '\0';
 }
 
index f81717f..e150364 100644 (file)
@@ -425,33 +425,36 @@ static int write_part_number (char **ret_buffer, int *ret_buffer_len,
 static int write_part_string (char **ret_buffer, int *ret_buffer_len,
                int type, const char *str, int str_len)
 {
-       char *packet_ptr;
-       int packet_len;
+       char *buffer;
+       int buffer_len;
 
-       part_header_t pkg_head;
+       uint16_t pkg_type;
+       uint16_t pkg_length;
 
        int offset;
 
-       packet_len = sizeof (pkg_head) + str_len + 1;
-       if (*ret_buffer_len < packet_len)
+       buffer_len = 2 * sizeof (uint16_t) + str_len + 1;
+       if (*ret_buffer_len < buffer_len)
                return (-1);
 
-       pkg_head.type = htons (type);
-       pkg_head.length = htons (packet_len);
+       pkg_type = htons (type);
+       pkg_length = htons (buffer_len);
 
-       packet_ptr = *ret_buffer;
+       buffer = *ret_buffer;
        offset = 0;
-       memcpy (packet_ptr + offset, &pkg_head, sizeof (pkg_head));
-       offset += sizeof (pkg_head);
-       memcpy (packet_ptr + offset, str, str_len);
+       memcpy (buffer + offset, (void *) &pkg_type, sizeof (pkg_type));
+       offset += sizeof (pkg_type);
+       memcpy (buffer + offset, (void *) &pkg_length, sizeof (pkg_length));
+       offset += sizeof (pkg_length);
+       memcpy (buffer + offset, str, str_len);
        offset += str_len;
-       memset (packet_ptr + offset, '\0', 1);
+       memset (buffer + offset, '\0', 1);
        offset += 1;
 
-       assert (offset == packet_len);
+       assert (offset == buffer_len);
 
-       *ret_buffer = packet_ptr + packet_len;
-       *ret_buffer_len -= packet_len;
+       *ret_buffer = buffer + buffer_len;
+       *ret_buffer_len -= buffer_len;
 
        return (0);
 } /* int write_part_string */
@@ -461,12 +464,17 @@ static int parse_part_values (void **ret_buffer, int *ret_buffer_len,
 {
        char *buffer = *ret_buffer;
        int   buffer_len = *ret_buffer_len;
-       part_values_t pv;
+
+       uint16_t tmp16;
+       size_t exp_size;
        int   i;
 
-       uint16_t h_length;
-       uint16_t h_type;
-       uint16_t h_num;
+       uint16_t pkg_length;
+       uint16_t pkg_type;
+       uint16_t pkg_numval;
+
+       uint8_t *pkg_types;
+       value_t *pkg_values;
 
        if (buffer_len < (15))
        {
@@ -475,34 +483,69 @@ static int parse_part_values (void **ret_buffer, int *ret_buffer_len,
                return (-1);
        }
 
-       pv.head = (part_header_t *) buffer;
-       h_length = ntohs (pv.head->length);
-       h_type = ntohs (pv.head->type);
+       memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
+       buffer += sizeof (tmp16);
+       pkg_type = ntohs (tmp16);
+
+       memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
+       buffer += sizeof (tmp16);
+       pkg_length = ntohs (tmp16);
 
-       assert (h_type == TYPE_VALUES);
+       memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
+       buffer += sizeof (tmp16);
+       pkg_numval = ntohs (tmp16);
 
-       pv.num_values = (uint16_t *) (pv.head + 1);
-       h_num = ntohs (*pv.num_values);
+       assert (pkg_type == TYPE_VALUES);
 
-       if (h_num != ((h_length - 6) / 9))
+       exp_size = 3 * sizeof (uint16_t)
+               + pkg_numval * (sizeof (uint8_t) + sizeof (value_t));
+       if (buffer_len < exp_size)
        {
-               DEBUG ("`length' and `num of values' don't match");
+               WARNING ("network plugin: parse_part_values: "
+                               "Packet too short: "
+                               "Chunk of size %u expected, "
+                               "but buffer has only %i bytes left.",
+                               (unsigned int) exp_size, buffer_len);
                return (-1);
        }
 
-       pv.values_types = (uint8_t *) (pv.num_values + 1);
-       pv.values = (value_t *) (pv.values_types + h_num);
+       if (pkg_length != exp_size)
+       {
+               WARNING ("network plugin: parse_part_values: "
+                               "Length and number of values "
+                               "in the packet don't match.");
+               return (-1);
+       }
 
-       for (i = 0; i < h_num; i++)
-               if (pv.values_types[i] == DS_TYPE_COUNTER)
-                       pv.values[i].counter = ntohll (pv.values[i].counter);
-               else
-                       pv.values[i].gauge = ntohd (pv.values[i].gauge);
+       pkg_types = (uint8_t *) malloc (pkg_numval * sizeof (uint8_t));
+       pkg_values = (value_t *) malloc (pkg_numval * sizeof (value_t));
+       if ((pkg_types == NULL) || (pkg_values == NULL))
+       {
+               sfree (pkg_types);
+               sfree (pkg_values);
+               ERROR ("network plugin: parse_part_values: malloc failed.");
+               return (-1);
+       }
 
-       *ret_buffer     = (void *) (pv.values + h_num);
-       *ret_buffer_len = buffer_len - h_length;
-       *ret_num_values = h_num;
-       *ret_values     = pv.values;
+       memcpy ((void *) pkg_types, (void *) buffer, pkg_numval * sizeof (uint8_t));
+       buffer += pkg_numval * sizeof (uint8_t);
+       memcpy ((void *) pkg_values, (void *) buffer, pkg_numval * sizeof (value_t));
+       buffer += pkg_numval * sizeof (value_t);
+
+       for (i = 0; i < pkg_numval; i++)
+       {
+               if (pkg_types[i] == DS_TYPE_COUNTER)
+                       pkg_values[i].counter = ntohll (pkg_values[i].counter);
+               else if (pkg_types[i] == DS_TYPE_GAUGE)
+                       pkg_values[i].gauge = ntohd (pkg_values[i].gauge);
+       }
+
+       *ret_buffer     = buffer;
+       *ret_buffer_len = buffer_len - pkg_length;
+       *ret_num_values = pkg_numval;
+       *ret_values     = pkg_values;
+
+       sfree (pkg_types);
 
        return (0);
 } /* int parse_part_values */
@@ -510,21 +553,40 @@ static int parse_part_values (void **ret_buffer, int *ret_buffer_len,
 static int parse_part_number (void **ret_buffer, int *ret_buffer_len,
                uint64_t *value)
 {
-       part_number_t pn;
-       uint16_t len;
+       char *buffer = *ret_buffer;
+       int buffer_len = *ret_buffer_len;
 
-       pn.head = (part_header_t *) *ret_buffer;
-       pn.value = (uint64_t *) (pn.head + 1);
+       uint16_t tmp16;
+       uint64_t tmp64;
+       size_t exp_size = 2 * sizeof (uint16_t) + sizeof (uint64_t);
 
-       len = ntohs (pn.head->length);
-       if (len != 12)
-               return (-1);
-       if (len > *ret_buffer_len)
+       uint16_t pkg_length;
+       uint16_t pkg_type;
+
+       if (buffer_len < exp_size)
+       {
+               WARNING ("network plugin: parse_part_number: "
+                               "Packet too short: "
+                               "Chunk of size %u expected, "
+                               "but buffer has only %i bytes left.",
+                               (unsigned int) exp_size, buffer_len);
                return (-1);
-       *value = ntohll (*pn.value);
+       }
+
+       memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
+       buffer += sizeof (tmp16);
+       pkg_type = ntohs (tmp16);
 
-       *ret_buffer = (void *) (pn.value + 1);
-       *ret_buffer_len -= len;
+       memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
+       buffer += sizeof (tmp16);
+       pkg_length = ntohs (tmp16);
+
+       memcpy ((void *) &tmp64, buffer, sizeof (tmp64));
+       buffer += sizeof (tmp64);
+       *value = ntohll (tmp64);
+
+       *ret_buffer = buffer;
+       *ret_buffer_len = buffer_len - pkg_length;
 
        return (0);
 } /* int parse_part_number */
@@ -534,61 +596,85 @@ static int parse_part_string (void **ret_buffer, int *ret_buffer_len,
 {
        char *buffer = *ret_buffer;
        int   buffer_len = *ret_buffer_len;
-       part_string_t ps;
 
-       uint16_t h_length;
-       uint16_t h_type;
+       uint16_t tmp16;
+       size_t header_size = 2 * sizeof (uint16_t);
 
-       DEBUG ("network plugin: parse_part_string: ret_buffer = %p;"
-                       " ret_buffer_len = %i; output = %p; output_len = %i;",
-                       *ret_buffer, *ret_buffer_len,
-                       (void *) output, output_len);
+       uint16_t pkg_length;
+       uint16_t pkg_type;
 
-       ps.head = (part_header_t *) buffer;
+       if (buffer_len < header_size)
+       {
+               WARNING ("network plugin: parse_part_string: "
+                               "Packet too short: "
+                               "Chunk of at least size %u expected, "
+                               "but buffer has only %i bytes left.",
+                               (unsigned int) header_size, buffer_len);
+               return (-1);
+       }
 
-       h_length = ntohs (ps.head->length);
-       h_type = ntohs (ps.head->type);
+       memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
+       buffer += sizeof (tmp16);
+       pkg_type = ntohs (tmp16);
 
-       DEBUG ("network plugin: parse_part_string: length = %hu; type = %hu;",
-                       h_length, h_type);
+       memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
+       buffer += sizeof (tmp16);
+       pkg_length = ntohs (tmp16);
 
-       if (buffer_len < h_length)
+       /* Check that packet fits in the input buffer */
+       if (pkg_length > buffer_len)
        {
-               DEBUG ("packet is too short");
+               WARNING ("network plugin: parse_part_string: "
+                               "Packet too big: "
+                               "Chunk of size %hu received, "
+                               "but buffer has only %i bytes left.",
+                               pkg_length, buffer_len);
                return (-1);
        }
-       assert ((h_type == TYPE_HOST)
-                       || (h_type == TYPE_PLUGIN)
-                       || (h_type == TYPE_PLUGIN_INSTANCE)
-                       || (h_type == TYPE_TYPE)
-                       || (h_type == TYPE_TYPE_INSTANCE)
-                       || (h_type == TYPE_MESSAGE));
-
-       ps.value = buffer + 4;
-       if (ps.value[h_length - 5] != '\0')
+
+       /* Check that pkg_length is in the valid range */
+       if (pkg_length <= header_size)
        {
-               DEBUG ("String does not end with a nullbyte");
+               WARNING ("network plugin: parse_part_string: "
+                               "Packet too short: "
+                               "Header claims this packet is only %hu "
+                               "bytes long.", pkg_length);
                return (-1);
        }
 
-       if (output_len < (h_length - 4))
+       /* Check that the package data fits into the output buffer.
+        * The previous if-statement ensures that:
+        * `pkg_length > header_size' */
+       if ((pkg_length - header_size) > output_len)
        {
-               DEBUG ("output buffer is too small");
+               WARNING ("network plugin: parse_part_string: "
+                               "Output buffer too small.");
                return (-1);
        }
-       strcpy (output, ps.value);
 
-       DEBUG ("network plugin: parse_part_string: output = %s", output);
+       /* All sanity checks successfull, let's copy the data over */
+       output_len = pkg_length - header_size;
+       memcpy ((void *) output, (void *) buffer, output_len);
+       buffer += output_len;
 
-       *ret_buffer = (void *) (buffer + h_length);
-       *ret_buffer_len = buffer_len - h_length;
+       /* For some very weird reason '\0' doesn't do the trick on SPARC in
+        * this statement. */
+       if (output[output_len - 1] != 0)
+       {
+               WARNING ("network plugin: parse_part_string: "
+                               "Received string does not end "
+                               "with a NULL-byte.");
+               return (-1);
+       }
+
+       *ret_buffer = buffer;
+       *ret_buffer_len = buffer_len - pkg_length;
 
        return (0);
 } /* int parse_part_string */
 
 static int parse_packet (void *buffer, int buffer_len)
 {
-       part_header_t *header;
        int status;
 
        value_list_t vl = VALUE_LIST_INIT;
@@ -606,24 +692,32 @@ static int parse_packet (void *buffer, int buffer_len)
        while ((status == 0) && (0 < buffer_len)
                        && ((unsigned int) buffer_len > sizeof (part_header_t)))
        {
-               header = (part_header_t *) buffer;
+               uint16_t pkg_length;
+               uint16_t pkg_type;
+
+               memcpy ((void *) &pkg_type,
+                               (void *) buffer,
+                               sizeof (pkg_type));
+               memcpy ((void *) &pkg_length,
+                               (void *) (buffer + sizeof (pkg_type)),
+                               sizeof (pkg_length));
 
-               if (ntohs (header->length) > buffer_len)
+               pkg_length = ntohs (pkg_length);
+               pkg_type = ntohs (pkg_type);
+
+               if (pkg_length > buffer_len)
                        break;
-               /* Assure that this loop terminates eventually */
-               if (ntohs (header->length) < 4)
+               /* Ensure that this loop terminates eventually */
+               if (pkg_length < (2 * sizeof (uint16_t)))
                        break;
 
-               if (ntohs (header->type) == TYPE_VALUES)
+               if (pkg_type == TYPE_VALUES)
                {
                        status = parse_part_values (&buffer, &buffer_len,
                                        &vl.values, &vl.values_len);
 
                        if (status != 0)
-                       {
-                               DEBUG ("parse_part_values failed.");
                                break;
-                       }
 
                        if ((vl.time > 0)
                                        && (strlen (vl.host) > 0)
@@ -631,8 +725,6 @@ static int parse_packet (void *buffer, int buffer_len)
                                        && (strlen (type) > 0)
                                        && (cache_check (type, &vl) == 0))
                        {
-                               DEBUG ("network plugin: parse_packet:"
-                                               " dispatching values");
                                plugin_dispatch_values (type, &vl);
                        }
                        else
@@ -640,89 +732,85 @@ static int parse_packet (void *buffer, int buffer_len)
                                DEBUG ("network plugin: parse_packet:"
                                                " NOT dispatching values");
                        }
+
+                       sfree (vl.values);
                }
-               else if (ntohs (header->type) == TYPE_TIME)
+               else if (pkg_type == TYPE_TIME)
                {
                        uint64_t tmp = 0;
-                       status = parse_part_number (&buffer, &buffer_len, &tmp);
+                       status = parse_part_number (&buffer, &buffer_len,
+                                       &tmp);
                        if (status == 0)
                        {
                                vl.time = (time_t) tmp;
                                n.time = (time_t) tmp;
                        }
                }
-               else if (ntohs (header->type) == TYPE_INTERVAL)
+               else if (pkg_type == TYPE_INTERVAL)
                {
                        uint64_t tmp = 0;
-                       status = parse_part_number (&buffer, &buffer_len, &tmp);
+                       status = parse_part_number (&buffer, &buffer_len,
+                                       &tmp);
                        if (status == 0)
                                vl.interval = (int) tmp;
                }
-               else if (ntohs (header->type) == TYPE_HOST)
+               else if (pkg_type == TYPE_HOST)
                {
                        status = parse_part_string (&buffer, &buffer_len,
                                        vl.host, sizeof (vl.host));
-                       strncpy (n.host, vl.host, sizeof (n.host));
-                       n.host[sizeof (n.host) - 1] = '\0';
-                       DEBUG ("network plugin: parse_packet: vl.host = %s",
-                                       vl.host);
+                       if (status == 0)
+                               sstrncpy (n.host, vl.host, sizeof (n.host));
                }
-               else if (ntohs (header->type) == TYPE_PLUGIN)
+               else if (pkg_type == TYPE_PLUGIN)
                {
                        status = parse_part_string (&buffer, &buffer_len,
                                        vl.plugin, sizeof (vl.plugin));
-                       strncpy (n.plugin, vl.plugin, sizeof (n.plugin));
-                       n.plugin[sizeof (n.plugin) - 1] = '\0';
-                       DEBUG ("network plugin: parse_packet: vl.plugin = %s",
-                                       vl.plugin);
+                       if (status == 0)
+                               sstrncpy (n.plugin, vl.plugin,
+                                               sizeof (n.plugin));
                }
-               else if (ntohs (header->type) == TYPE_PLUGIN_INSTANCE)
+               else if (pkg_type == TYPE_PLUGIN_INSTANCE)
                {
                        status = parse_part_string (&buffer, &buffer_len,
                                        vl.plugin_instance,
                                        sizeof (vl.plugin_instance));
-                       strncpy (n.plugin_instance, vl.plugin_instance,
-                                       sizeof (n.plugin_instance));
-                       n.plugin_instance[sizeof (n.plugin_instance) - 1] = '\0';
-                       DEBUG ("network plugin: parse_packet: "
-                                       "vl.plugin_instance = %s",
-                                       vl.plugin_instance);
+                       if (status == 0)
+                               sstrncpy (n.plugin_instance,
+                                               vl.plugin_instance,
+                                               sizeof (n.plugin_instance));
                }
-               else if (ntohs (header->type) == TYPE_TYPE)
+               else if (pkg_type == TYPE_TYPE)
                {
                        status = parse_part_string (&buffer, &buffer_len,
                                        type, sizeof (type));
-                       strncpy (n.type, type, sizeof (n.type));
-                       n.type[sizeof (n.type) - 1] = '\0';
-                       DEBUG ("network plugin: parse_packet: type = %s",
-                                       type);
+                       if (status == 0)
+                               sstrncpy (n.type, type, sizeof (n.type));
                }
-               else if (ntohs (header->type) == TYPE_TYPE_INSTANCE)
+               else if (pkg_type == TYPE_TYPE_INSTANCE)
                {
                        status = parse_part_string (&buffer, &buffer_len,
                                        vl.type_instance,
                                        sizeof (vl.type_instance));
-                       strncpy (n.type_instance, vl.type_instance,
-                                       sizeof (n.type_instance));
-                       n.type_instance[sizeof (n.type_instance) - 1] = '\0';
-                       DEBUG ("network plugin: parse_packet: "
-                                       "vl.type_instance = %s",
-                                       vl.type_instance);
+                       if (status == 0)
+                               sstrncpy (n.type_instance, vl.type_instance,
+                                               sizeof (n.type_instance));
                }
-               else if (ntohs (header->type) == TYPE_MESSAGE)
+               else if (pkg_type == TYPE_MESSAGE)
                {
                        status = parse_part_string (&buffer, &buffer_len,
                                        n.message, sizeof (n.message));
-                       DEBUG ("network plugin: parse_packet: n.message = %s",
-                                       n.message);
 
-                       if ((n.severity != NOTIF_FAILURE)
+                       if (status != 0)
+                       {
+                               /* do nothing */
+                       }
+                       else if ((n.severity != NOTIF_FAILURE)
                                        && (n.severity != NOTIF_WARNING)
                                        && (n.severity != NOTIF_OKAY))
                        {
                                INFO ("network plugin: "
                                                "Ignoring notification with "
-                                               "unknown severity %s.",
+                                               "unknown severity %i.",
                                                n.severity);
                        }
                        else if (n.time <= 0)
@@ -739,29 +827,26 @@ static int parse_packet (void *buffer, int buffer_len)
                        }
                        else
                        {
-                               /*
-                                * TODO: Let this do a separate thread so that
-                                * no packets are lost if this takes too long.
-                                */
                                plugin_dispatch_notification (&n);
                        }
                }
-               else if (ntohs (header->type) == TYPE_SEVERITY)
+               else if (pkg_type == TYPE_SEVERITY)
                {
                        uint64_t tmp = 0;
-                       status = parse_part_number (&buffer, &buffer_len, &tmp);
+                       status = parse_part_number (&buffer, &buffer_len,
+                                       &tmp);
                        if (status == 0)
                                n.severity = (int) tmp;
                }
                else
                {
                        DEBUG ("network plugin: parse_packet: Unknown part"
-                                       " type: 0x%0hx", ntohs (header->type));
-                       buffer = ((char *) buffer) + ntohs (header->length);
+                                       " type: 0x%04hx", pkg_type);
+                       buffer = ((char *) buffer) + pkg_length;
                }
        } /* while (buffer_len > sizeof (part_header_t)) */
 
-       return (0);
+       return (status);
 } /* int parse_packet */
 
 static void free_sockent (sockent_t *se)
index 6d3326f..d17551e 100644 (file)
@@ -1478,6 +1478,11 @@ static int perl_config_enabledebugger (pTHX_ oconfig_item_t *ci)
                return 1;
        }
 
+       if (NULL != perl_threads) {
+               log_warn ("EnableDebugger has no effects if used after LoadPlugin.");
+               return 1;
+       }
+
        value = ci->values[0].value.string;
 
        perl_argv = (char **)realloc (perl_argv,
@@ -1514,11 +1519,6 @@ static int perl_config_includedir (pTHX_ oconfig_item_t *ci)
                return 1;
        }
 
-       if (NULL == aTHX) {
-               log_warn ("EnableDebugger has no effects if used after LoadPlugin.");
-               return 1;
-       }
-
        value = ci->values[0].value.string;
 
        if (NULL == aTHX) {
index eaf1a41..cf03849 100644 (file)
@@ -236,6 +236,7 @@ static void *plugin_read_thread (void *args)
        pthread_mutex_unlock (&read_lock);
 
        pthread_exit (NULL);
+       return ((void *) 0);
 } /* void *plugin_read_thread */
 
 static void start_threads (int num)
index a7618c0..5845f9d 100644 (file)
@@ -550,6 +550,7 @@ static void *us_handle_client (void *arg)
        close (fd);
 
        pthread_exit ((void *) 0);
+       return ((void *) 0);
 } /* void *us_handle_client */
 
 static void *us_server_thread (void *arg)
index ad8fb19..c471ee2 100644 (file)
@@ -168,6 +168,60 @@ static int uc_send_notification (const char *name)
   return (0);
 } /* int uc_send_notification */
 
+static int uc_insert (const data_set_t *ds, const value_list_t *vl,
+    const char *key)
+{
+  int i;
+  char *key_copy;
+  cache_entry_t *ce;
+
+  /* `cache_lock' has been locked by `uc_update' */
+
+  key_copy = strdup (key);
+  if (key_copy == NULL)
+  {
+    ERROR ("uc_insert: strdup failed.");
+    return (-1);
+  }
+
+  ce = cache_alloc (ds->ds_num);
+  if (ce == NULL)
+  {
+    ERROR ("uc_insert: cache_alloc (%i) failed.", ds->ds_num);
+    return (-1);
+  }
+
+  sstrncpy (ce->name, key, sizeof (ce->name));
+
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    if (ds->ds[i].type == DS_TYPE_COUNTER)
+    {
+      ce->values_gauge[i] = NAN;
+      ce->values_counter[i] = vl->values[i].counter;
+    }
+    else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
+    {
+      ce->values_gauge[i] = vl->values[i].gauge;
+    }
+  } /* for (i) */
+
+  ce->last_time = vl->time;
+  ce->last_update = time (NULL);
+  ce->interval = vl->interval;
+  ce->state = STATE_OKAY;
+
+  if (c_avl_insert (cache_tree, key_copy, ce) != 0)
+  {
+    sfree (key_copy);
+    ERROR ("uc_insert: c_avl_insert failed.");
+    return (-1);
+  }
+
+  DEBUG ("uc_insert: Added %s to the cache.", key);
+  return (0);
+} /* int uc_insert */
+
 int uc_init (void)
 {
   if (cache_tree == NULL)
@@ -236,8 +290,10 @@ int uc_check_timeout (void)
     else if (status == 0) /* ``service'' is uninteresting */
     {
       ce = NULL;
-      DEBUG ("uc_check_timeout: %s is missing but ``uninteresting''", keys[i]);
-      status = c_avl_remove (cache_tree, keys[i], (void *) &key, (void *) &ce);
+      DEBUG ("uc_check_timeout: %s is missing but ``uninteresting''",
+         keys[i]);
+      status = c_avl_remove (cache_tree, keys[i],
+         (void *) &key, (void *) &ce);
       if (status != 0)
       {
        ERROR ("uc_check_timeout: c_avl_remove (%s) failed.", keys[i]);
@@ -245,14 +301,33 @@ int uc_check_timeout (void)
       sfree (keys[i]);
       cache_free (ce);
     }
-    else /* (status > 0); ``service'' is interesting */
+    else if (status == 1) /* persist */
+    {
+      DEBUG ("uc_check_timeout: %s is missing, sending notification.",
+         keys[i]);
+      ce->state = STATE_MISSING;
+    }
+    else if (status == 2) /* do not persist */
     {
-      /*
-       * `keys[i]' is not freed and set to NULL, so that the for-loop below
-       * will send out notifications. There's nothing else to do here.
-       */
-      DEBUG ("uc_check_timeout: %s is missing and ``interesting''", keys[i]);
-      ce->state = STATE_ERROR;
+      if (ce->state == STATE_MISSING)
+      {
+       DEBUG ("uc_check_timeout: %s is missing but "
+           "notification has already been sent.",
+           keys[i]);
+       sfree (keys[i]);
+      }
+      else /* (ce->state != STATE_MISSING) */
+      {
+       DEBUG ("uc_check_timeout: %s is missing, sending one notification.",
+           keys[i]);
+       ce->state = STATE_MISSING;
+      }
+    }
+    else
+    {
+      WARNING ("uc_check_timeout: ut_check_interesting (%s) returned ",
+         "invalid status %i.",
+         keys[i], status);
     }
   } /* for (keys[i]) */
 
@@ -279,158 +354,111 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
   char name[6 * DATA_MAX_NAME_LEN];
   cache_entry_t *ce = NULL;
   int send_okay_notification = 0;
+  time_t update_delay = 0;
+  notification_t n;
+  int status;
+  int i;
 
   if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
   {
-    ERROR ("uc_insert: FORMAT_VL failed.");
+    ERROR ("uc_update: FORMAT_VL failed.");
     return (-1);
   }
 
   pthread_mutex_lock (&cache_lock);
 
-  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
+  status = c_avl_get (cache_tree, name, (void *) &ce);
+  if (status != 0) /* entry does not yet exist */
   {
-    int i;
-
-    assert (ce != NULL);
-    assert (ce->values_num == ds->ds_num);
-
-    if (ce->last_time >= vl->time)
-    {
-      pthread_mutex_unlock (&cache_lock);
-      NOTICE ("uc_insert: Value too old: name = %s; value time = %u; "
-         "last cache update = %u;",
-         name, (unsigned int) vl->time, (unsigned int) ce->last_time);
-      return (-1);
-    }
-
-    if ((ce->last_time + ce->interval) < vl->time)
-    {
-      send_okay_notification = vl->time - ce->last_time;
-      ce->state = STATE_OKAY;
-    }
+    status = uc_insert (ds, vl, name);
+    pthread_mutex_unlock (&cache_lock);
+    return (status);
+  }
 
-    for (i = 0; i < ds->ds_num; i++)
-    {
-      if (ds->ds[i].type == DS_TYPE_COUNTER)
-      {
-       counter_t diff;
-
-       /* check if the counter has wrapped around */
-       if (vl->values[i].counter < ce->values_counter[i])
-       {
-         if (ce->values_counter[i] <= 4294967295U)
-           diff = (4294967295U - ce->values_counter[i])
-             + vl->values[i].counter;
-         else
-           diff = (18446744073709551615ULL - ce->values_counter[i])
-             + vl->values[i].counter;
-       }
-       else /* counter has NOT wrapped around */
-       {
-         diff = vl->values[i].counter - ce->values_counter[i];
-       }
-
-       ce->values_gauge[i] = ((double) diff)
-         / ((double) (vl->time - ce->last_time));
-       ce->values_counter[i] = vl->values[i].counter;
-      }
-      else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
-      {
-       ce->values_gauge[i] = vl->values[i].gauge;
-      }
-      DEBUG ("uc_insert: %s: ds[%i] = %lf", name, i, ce->values_gauge[i]);
-    } /* for (i) */
+  assert (ce != NULL);
+  assert (ce->values_num == ds->ds_num);
 
-    ce->last_time = vl->time;
-    ce->last_update = time (NULL);
-    ce->interval = vl->interval;
+  if (ce->last_time >= vl->time)
+  {
+    pthread_mutex_unlock (&cache_lock);
+    NOTICE ("uc_update: Value too old: name = %s; value time = %u; "
+       "last cache update = %u;",
+       name, (unsigned int) vl->time, (unsigned int) ce->last_time);
+    return (-1);
   }
-  else /* key is not found */
+
+  /* Send a notification (after the lock has been released) if we switch the
+   * state from something else to `okay'. */
+  if (ce->state == STATE_MISSING)
   {
-    int i;
-    char *key;
-    
-    key = strdup (name);
-    if (key == NULL)
-    {
-      pthread_mutex_unlock (&cache_lock);
-      ERROR ("uc_insert: strdup failed.");
-      return (-1);
-    }
+    send_okay_notification = 1;
+    ce->state = STATE_OKAY;
+    update_delay = time (NULL) - ce->last_update;
+  }
 
-    ce = cache_alloc (ds->ds_num);
-    if (ce == NULL)
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    if (ds->ds[i].type == DS_TYPE_COUNTER)
     {
-      pthread_mutex_unlock (&cache_lock);
-      ERROR ("uc_insert: cache_alloc (%i) failed.", ds->ds_num);
-      return (-1);
-    }
+      counter_t diff;
 
-    sstrncpy (ce->name, name, sizeof (ce->name));
-
-    for (i = 0; i < ds->ds_num; i++)
-    {
-      if (ds->ds[i].type == DS_TYPE_COUNTER)
+      /* check if the counter has wrapped around */
+      if (vl->values[i].counter < ce->values_counter[i])
       {
-       ce->values_gauge[i] = NAN;
-       ce->values_counter[i] = vl->values[i].counter;
+       if (ce->values_counter[i] <= 4294967295U)
+         diff = (4294967295U - ce->values_counter[i])
+           + vl->values[i].counter;
+       else
+         diff = (18446744073709551615ULL - ce->values_counter[i])
+           + vl->values[i].counter;
       }
-      else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
+      else /* counter has NOT wrapped around */
       {
-       ce->values_gauge[i] = vl->values[i].gauge;
+       diff = vl->values[i].counter - ce->values_counter[i];
       }
-    } /* for (i) */
-
-    ce->last_time = vl->time;
-    ce->last_update = time (NULL);
-    ce->interval = vl->interval;
-    ce->state = STATE_OKAY;
 
-    if (c_avl_insert (cache_tree, key, ce) != 0)
+      ce->values_gauge[i] = ((double) diff)
+       / ((double) (vl->time - ce->last_time));
+      ce->values_counter[i] = vl->values[i].counter;
+    }
+    else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
     {
-      pthread_mutex_unlock (&cache_lock);
-      ERROR ("uc_insert: c_avl_insert failed.");
-      return (-1);
+      ce->values_gauge[i] = vl->values[i].gauge;
     }
+    DEBUG ("uc_update: %s: ds[%i] = %lf", name, i, ce->values_gauge[i]);
+  } /* for (i) */
 
-    DEBUG ("uc_insert: Added %s to the cache.", name);
-  } /* if (key is not found) */
+  ce->last_time = vl->time;
+  ce->last_update = time (NULL);
+  ce->interval = vl->interval;
 
   pthread_mutex_unlock (&cache_lock);
 
+  if (send_okay_notification == 0)
+    return (0);
+
   /* Do not send okay notifications for uninteresting values, i. e. values for
    * which no threshold is configured. */
-  if (send_okay_notification > 0)
-  {
-    int status;
-
-    status = ut_check_interesting (name);
-    if (status <= 0)
-      send_okay_notification = 0;
-  }
-
-  if (send_okay_notification > 0)
-  {
-    notification_t n;
-    memset (&n, '\0', sizeof (n));
+  status = ut_check_interesting (name);
+  if (status <= 0)
+    return (0);
 
-    /* Copy the associative members */
-    NOTIFICATION_INIT_VL (&n, vl, ds);
+  /* Initialize the notification */
+  memset (&n, '\0', sizeof (n));
+  NOTIFICATION_INIT_VL (&n, vl, ds);
 
-    n.severity = NOTIF_OKAY;
-    n.time = vl->time;
+  n.severity = NOTIF_OKAY;
+  n.time = vl->time;
 
-    snprintf (n.message, sizeof (n.message),
-       "Received a value for %s. It was missing for %i seconds.",
-       name, send_okay_notification);
-    n.message[sizeof (n.message) - 1] = '\0';
+  snprintf (n.message, sizeof (n.message),
+      "Received a value for %s. It was missing for %u seconds.",
+      name, (unsigned int) update_delay);
+  n.message[sizeof (n.message) - 1] = '\0';
 
-    plugin_dispatch_notification (&n);
-  }
+  plugin_dispatch_notification (&n);
 
   return (0);
-} /* int uc_insert */
+} /* int uc_update */
 
 int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_values_num)
 {
@@ -439,6 +467,12 @@ int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_val
   cache_entry_t *ce = NULL;
   int status = 0;
 
+  if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+  {
+    ERROR ("uc_get_rate: FORMAT_VL failed.");
+    return (NULL);
+  }
+
   pthread_mutex_lock (&cache_lock);
 
   if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
@@ -536,11 +570,6 @@ int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state)
   cache_entry_t *ce = NULL;
   int ret = -1;
 
-  if (state < STATE_OKAY)
-    state = STATE_OKAY;
-  if (state > STATE_ERROR)
-    state = STATE_ERROR;
-
   if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
   {
     ERROR ("uc_get_state: FORMAT_VL failed.");
index ed6830b..9b6972a 100644 (file)
 
 #include "plugin.h"
 
-#define STATE_OKAY    0
-#define STATE_WARNING 1
-#define STATE_ERROR   2
+#define STATE_OKAY     0
+#define STATE_WARNING  1
+#define STATE_ERROR    2
+#define STATE_MISSING 15
 
 int uc_init (void);
 int uc_check_timeout (void);
index ea4b591..778b40b 100644 (file)
@@ -146,9 +146,9 @@ static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
   }
 
   if (strcasecmp (ci->key, "WarningMax") == 0)
-    th->warning_min = ci->values[0].value.number;
+    th->warning_max = ci->values[0].value.number;
   else
-    th->failure_min = ci->values[0].value.number;
+    th->failure_max = ci->values[0].value.number;
 
   return (0);
 } /* int ut_config_type_max */
@@ -509,12 +509,22 @@ static threshold_t *threshold_search (const data_set_t *ds,
 
 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
 {
+  notification_t n;
   threshold_t *th;
   gauge_t *values;
   int i;
 
+  int state_orig;
+  int state_new = STATE_OKAY;
+  int ds_index = 0;
+
+  char *buf;
+  size_t bufsize;
+  int status;
+
   if (threshold_tree == NULL)
     return (0);
+
   /* Is this lock really necessary? So far, thresholds are only inserted at
    * startup. -octo */
   pthread_mutex_lock (&threshold_lock);
@@ -529,6 +539,8 @@ int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
   if (values == NULL)
     return (0);
 
+  state_orig = uc_get_state (ds, vl);
+
   for (i = 0; i < ds->ds_num; i++)
   {
     int is_inverted = 0;
@@ -536,100 +548,134 @@ int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
     int is_failure = 0;
 
     if ((th->flags & UT_FLAG_INVERT) != 0)
+    {
       is_inverted = 1;
+      is_warning--;
+      is_failure--;
+    }
     if ((!isnan (th->failure_min) && (th->failure_min > values[i]))
        || (!isnan (th->failure_max) && (th->failure_max < values[i])))
-      is_failure = is_inverted - 1;
+      is_failure++;
     if ((!isnan (th->warning_min) && (th->warning_min > values[i]))
        || (!isnan (th->warning_max) && (th->warning_max < values[i])))
-      is_warning = is_inverted - 1;
+      is_warning++;
 
-    if ((is_failure != 0) || (is_warning != 0))
+    if ((is_failure != 0) && (state_new != STATE_ERROR))
+    {
+      state_new = STATE_ERROR;
+      ds_index = i;
+    }
+    else if ((is_warning != 0)
+       && (state_new != STATE_ERROR)
+       && (state_new != STATE_WARNING))
     {
-      notification_t n;
-      char *buf;
-      size_t bufsize;
-      int status;
+      state_new = STATE_WARNING;
+      ds_index = i;
+    }
+  }
 
-      double min;
-      double max;
+  if (state_new != state_orig)
+    uc_set_state (ds, vl, state_new);
 
-      min = (is_failure != 0) ? th->failure_min : th->warning_min;
-      max = (is_failure != 0) ? th->failure_max : th->warning_max;
+  /* Return here if we're not going to send a notification */
+  if ((state_new == state_orig)
+      && ((state_new == STATE_OKAY)
+       || ((th->flags & UT_FLAG_PERSIST) == 0)))
+  {
+    sfree (values);
+    return (0);
+  }
 
-      DEBUG ("ut_check_threshold: ds[%s]: %lf <= !%lf <= %lf (invert: %s)",
-         ds->ds[i].name, min, values[i], max,
-         is_inverted ? "true" : "false");
+  NOTIFICATION_INIT_VL (&n, vl, ds);
+  {
+    /* Copy the associative members */
+    if (state_new == STATE_OKAY)
+      n.severity = NOTIF_OKAY;
+    else if (state_new == STATE_WARNING)
+      n.severity = NOTIF_WARNING;
+    else
+      n.severity = NOTIF_FAILURE;
 
-      /* Copy the associative members */
-      NOTIFICATION_INIT_VL (&n, vl, ds);
+    n.time = vl->time;
 
-      n.severity = (is_failure != 0) ? NOTIF_FAILURE : NOTIF_WARNING;
-      n.time = vl->time;
+    buf = n.message;
+    bufsize = sizeof (n.message);
 
-      buf = n.message;
-      bufsize = sizeof (n.message);
+    status = snprintf (buf, bufsize, "Host %s, plugin %s",
+       vl->host, vl->plugin);
+    buf += status;
+    bufsize -= status;
 
-      status = snprintf (buf, bufsize, "Host %s, plugin %s",
-         vl->host, vl->plugin);
+    if (vl->plugin_instance[0] != '\0')
+    {
+      status = snprintf (buf, bufsize, " (instance %s)",
+         vl->plugin_instance);
       buf += status;
       bufsize -= status;
+    }
 
-      if (vl->plugin_instance[0] != '\0')
-      {
-       status = snprintf (buf, bufsize, " (instance %s)",
-           vl->plugin_instance);
-       buf += status;
-       bufsize -= status;
-      }
+    status = snprintf (buf, bufsize, " type %s", ds->type);
+    buf += status;
+    bufsize -= status;
 
-      status = snprintf (buf, bufsize, " type %s", ds->type);
+    if (vl->type_instance[0] != '\0')
+    {
+      status = snprintf (buf, bufsize, " (instance %s)",
+         vl->type_instance);
       buf += status;
       bufsize -= status;
+    }
+  }
 
-      if (vl->type_instance[0] != '\0')
-      {
-       status = snprintf (buf, bufsize, " (instance %s)",
-           vl->type_instance);
-       buf += status;
-       bufsize -= status;
-      }
+  /* Send a okay notification */
+  if (state_new == STATE_OKAY)
+  {
+    status = snprintf (buf, bufsize, ": All data sources are within range again.");
+    buf += status;
+    bufsize -= status;
+  }
+  else
+  {
+    double min;
+    double max;
 
-      if (is_inverted)
+    min = (state_new == STATE_ERROR) ? th->failure_min : th->warning_min;
+    max = (state_new == STATE_ERROR) ? th->failure_max : th->warning_max;
+
+    if (th->flags & UT_FLAG_INVERT)
+    {
+      if (!isnan (min) && !isnan (max))
       {
-       if (!isnan (min) && !isnan (max))
-       {
-         status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
-             "%f. That is within the %s region of %f and %f.",
-             ds->ds[i].name, values[i],
-             (is_failure != 0) ? "failure" : "warning",
-             min, min);
-       }
-       else
-       {
-         status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
-             "%f. That is %s the %s threshold of %f.",
-             ds->ds[i].name, values[i],
-             isnan (min) ? "below" : "above",
-             (is_failure != 0) ? "failure" : "warning",
-             isnan (min) ? max : min);
-       }
+       status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
+           "%f. That is within the %s region of %f and %f.",
+           ds->ds[ds_index].name, values[ds_index],
+           (state_new == STATE_ERROR) ? "failure" : "warning",
+           min, min);
       }
-      else /* (!is_inverted) */
+      else
       {
        status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
            "%f. That is %s the %s threshold of %f.",
-           ds->ds[i].name, values[i],
-           (values[i] < min) ? "below" : "above",
-           (is_failure != 0) ? "failure" : "warning",
-           (values[i] < min) ? min : max);
+           ds->ds[ds_index].name, values[ds_index],
+           isnan (min) ? "below" : "above",
+           (state_new == STATE_ERROR) ? "failure" : "warning",
+           isnan (min) ? max : min);
       }
-      buf += status;
-      bufsize -= status;
-
-      plugin_dispatch_notification (&n);
     }
-  } /* for (i = 0; i < ds->ds_num; i++) */
+    else /* is not inverted */
+    {
+      status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
+         "%f. That is %s the %s threshold of %f.",
+         ds->ds[ds_index].name, values[ds_index],
+         (values[ds_index] < min) ? "below" : "above",
+         (state_new == STATE_ERROR) ? "failure" : "warning",
+         (values[ds_index] < min) ? min : max);
+    }
+    buf += status;
+    bufsize -= status;
+  }
+
+  plugin_dispatch_notification (&n);
 
   sfree (values);
 
index 89a5562..580b90c 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-DEFAULT_VERSION="4.3.0.git"
+DEFAULT_VERSION="4.3.1.git"
 
 VERSION="$( git describe 2> /dev/null | sed -e 's/^collectd-//' )"