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>
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>
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/>
+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.
],
[with_rrdtool="no (symbol 'rrd_update' not found)"],
[-lm])
- ]
+ ],
[-lm])
CPPFLAGS="$SAVE_CPPFLAGS"
+++ /dev/null
-#!/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>
-
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'}}))
$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
--- /dev/null
+#!/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>
+
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
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)
{
#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);
*
* Authors:
* Florian octo Forster <octo at verplant.org>
+ * Sebastian tokkee Harl <sh at tokkee.org>
**/
#include "collectd.h"
{
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));
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);
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;
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 */
}
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;
ERROR ("configfile: stat (%s) failed: %s",
path_ptr,
sstrerror (errno, errbuf, sizeof (errbuf)));
+ oconfig_free (root);
return (NULL);
}
continue;
}
+ if (temp == NULL) {
+ oconfig_free (root);
+ return (NULL);
+ }
+
cf_ci_append_children (root, temp);
sfree (temp->children);
sfree (temp);
{
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);
}
/*
{
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';
}
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 */
{
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))
{
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 */
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 */
{
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;
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)
&& (strlen (type) > 0)
&& (cache_check (type, &vl) == 0))
{
- DEBUG ("network plugin: parse_packet:"
- " dispatching values");
plugin_dispatch_values (type, &vl);
}
else
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)
}
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)
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,
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) {
pthread_mutex_unlock (&read_lock);
pthread_exit (NULL);
+ return ((void *) 0);
} /* void *plugin_read_thread */
static void start_threads (int num)
close (fd);
pthread_exit ((void *) 0);
+ return ((void *) 0);
} /* void *us_handle_client */
static void *us_server_thread (void *arg)
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)
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]);
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]) */
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)
{
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)
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.");
#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);
}
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 */
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);
if (values == NULL)
return (0);
+ state_orig = uc_get_state (ds, vl);
+
for (i = 0; i < ds->ds_num; i++)
{
int is_inverted = 0;
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);
#!/bin/sh
-DEFAULT_VERSION="4.3.0.git"
+DEFAULT_VERSION="4.3.1.git"
VERSION="$( git describe 2> /dev/null | sed -e 's/^collectd-//' )"