author | Florian Forster <octo@huhu.verplant.org> | |
Mon, 16 Apr 2007 12:53:12 +0000 (14:53 +0200) | ||
committer | Florian Forster <octo@huhu.verplant.org> | |
Mon, 16 Apr 2007 12:53:12 +0000 (14:53 +0200) |
16 files changed:
AUTHORS | patch | blob | history | |
ChangeLog | patch | blob | history | |
README | patch | blob | history | |
configure.in | patch | blob | history | |
contrib/README | patch | blob | history | |
contrib/extractDS.px | [new file with mode: 0755] | patch | blob |
contrib/migrate-3-4.px | [new file with mode: 0755] | patch | blob |
contrib/sles10.1/collectd.spec | [new file with mode: 0644] | patch | blob |
contrib/sles10.1/init.d-collectd | [new file with mode: 0755] | patch | blob |
debian/changelog | patch | blob | history | |
src/Makefile.am | patch | blob | history | |
src/collectd.conf.in | patch | blob | history | |
src/collectd.pod | patch | blob | history | |
src/perl.c | [new file with mode: 0644] | patch | blob |
src/plugin.c | patch | blob | history | |
src/utils_dns.c | patch | blob | history |
index 253910807de979a30f328e65ca622450ef1ca493..0c1b3225868db4d32fa85a9f7d17be50760f8af2 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
nfs module by:
Jason Pepas <cell at ices.utexas.edu>
+perl module by:
+ Sebastian Harl <sh at tokkee.org>
+
processes module by:
Lyonel Vincent <lyonel at ezix.org>
diff --git a/ChangeLog b/ChangeLog
index 6629ab078fffa87c137f2902491de2640209a94c..b3aaed8a53afda1bb6bd9c557c4a0bf3986a07c3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
* collectd-nagios: The new `collectd-nagios' binary queries values
from collectd, parses them and exits according to Nagios-standards.
+2007-04-10, Version 3.11.4
+ * dns plugin: Change the order of includes to make the plugin compile
+ under FreeBSD.
+
2007-03-30, Version 3.11.3
* configure: Have the configure-script define `HAVE_LIBKSTAT' instead
of the unused `COLLECT_KSTAT'.
index 4a09fee565581e9a26e96d8208200c18897c7939..8d6c7edb4c07b5ce4db4457918c36702415f2652 100644 (file)
--- a/README
+++ b/README
- Email statistics
(count, traffic, spam scores and checks)
+ - Entropy available
+ (Amount of entropy available to the system)
+
+ - Exec
+ (Values gatheres by a custom program or script)
+
- Harddisk temperatures
(Uhm, yeah, temperature of harddisks that is ;)
+ - Interface traffic
+ (Number of octets, packets and errors for each interface)
+
+ - Iptables' counters
+ (Number of bytes that were matched by a certain iptables rule)
+
+ - IRQ counters
+ (Frequency in which certain interrupts occur)
+
- System load
(Load average over the last 1, 5 and 15 minutes)
- NTP Daemon
(Local clock drift, offset to peers, etc)
+ - Network UPS tools
+ (UPS current, voltage, power, charge, utilisation, temperature, etc.)
+
- Ping latency
(Time to reach the default gateway or another given host)
- Tape
(Read and write bytes and operations on tape devices)
- - Traffic
- (In/Outbound traffic on the interfaces)
-
- Users
(Currently logged in users)
- Wireless
(Link quality of wireless cards)
+ * Output to CSV- and RRD-files, send values over the network and/or provide a
+ generic interface for use by other means, e. g. a Nagios-plugin.
+
* Performance: Running as a daemon collectd doesn't spend much time in
startup. Since collectd links against libping, librrd and libsensors it
doesn't need to start any other processes.
Run `collectd -h' for a list of builtin defaults. See `collectd.conf(5)'
for a list of options and a syntax description.
- * When running collectd writes system statistics in RRD-files. Per default
- they reside in `/var/lib/collectd'.
+ * When the `csv' or `rrdtool' plugins are loaded they'll write the values to
+ files. The usual place for these files is beneath `/var/lib/collectd'.
- * When using the `ping' plugin collectd needs to run as user root, since only
- root can craft ICMP packages needed to ping other hosts. collectd should
- NOT be installed setuid root since it can be used to overwrite valuable
- files..
+ * When using some of the plugins, collectd needs to run as user root, since only
+ root can do certain thing, such as craft ICMP packages needed to ping other
+ hosts. collectd should NOT be installed setuid root since it can be used to
+ overwrite valuable files..
* Sample scripts to generate graphs reside in `contrib/' in the source
package or somewhere near `/usr/share/doc/collectd' in most distributions.
(`librrds-perl' on Debian)
* The RRAs of the automatically created RRD files depend on the `step'
- and `heartbeat' settings given on compile time. For a list of the
- default RRAs take a look in the collectd(1) manpage.
+ and `heartbeat' settings given. For a list of the default RRAs take a look
+ in the collectd(1) manpage.
Prerequisites
* Usual suspects: C compiler, linker, preprocessor, make, ...
- * rrdtool (optional; headers and library; rrdtool 1.0 and 1.2 both work fine)
+ * A POSIX-threads (pthread) implementation.
+ Since gathering some statistics is slow (network connections, slow devices,
+ etc) the collectd is parellelized. The POSIX threads interface is being
+ used and should be found in various implementations for hopefully all
+ platforms.
+
+ * libcurl (optional)
+ If you want to use the `apache' plugin
+
+ * libiptc (optional)
+ For querying iptables counters.
+
+ * libmysqlclient (optional)
+
+ * liboping (optional, if not found a version shipped with this distribution
+ can be used)
+ Used by the `ping' plugin to send and receive ICMP packets.
+
+ * libpcap (optional)
+ Used to capture packets by the `dns' plugin.
+
+ * librrd (optional; headers and library; rrdtool 1.0 and 1.2 both work fine)
If built without `librrd' the resulting binary will be `client only', i.e.
will send it's values via multicast and not create any RRD files itself.
Alternatively you can chose to write CSV-files (Comma Seperated Values)
instead.
- * libmysqlclient (optional)
-
- * lm-sensors (optional)
+ * libsensors (optional)
+ To read from `lm_sensors'.
* libstatgrab may be used to collect statistics on systems other than Linux
and/or Solaris. Note that CPU- and disk-statistics, while being provided
by this library, are not supported in collectd right now..
<http://www.i-scream.org/libstatgrab/>
- * libcurl (optional)
- If you want to use the `apache' plugin
+ * libupsclient/nut (optional)
+ For the `nut' plugin which queries nut's `upsd'.
* librt, libsocket, libkstat, libdevinfo
Various standard Solaris libraries which provide system functions.
- * libpthread (optional)
- For parallelization, especially for plugins that communicate with the
- outside, e. g. with a socket.
-
* CoreFoundation.framework and IOKit.framework
- For copiling on darwin in general and the `apple_sensors' plugin in
+ For compiling on darwin in general and the `apple_sensors' plugin in
particular.
Author
diff --git a/configure.in b/configure.in
index 7a21b077da17a7d7210ed9535b27a915bf2a95dc..91d13e801a9ed0449c2153d434fdd80769ccabae 100644 (file)
--- a/configure.in
+++ b/configure.in
[Wether or not to use the pcap library])
AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes")
+AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
+[
+ if test "x$withval" != "xno" && test "x$withval" != "xyes"
+ then
+ LDFLAGS="$LDFLAGS -L$withval/lib"
+ CPPFLAGS="$CPPFLAGS -I$withval/include"
+ with_libperl="yes"
+ fi
+],
+[
+ with_libperl="yes"
+])
+if test "x$with_libperl" = "xyes"
+then
+ SAVE_CFLAGS=$CFLAGS
+ SAVE_LDFLAGS=$LDFLAGS
+ CFLAGS="$CFLAGS `perl -MExtUtils::Embed -e ccopts`"
+ LDFLAGS="$LDFLAGS `perl -MExtUtils::Embed -e ldopts`"
+
+ AC_CACHE_CHECK([for libperl],
+ [have_libperl],
+ AC_LINK_IFELSE(
+ AC_LANG_PROGRAM(
+ [[
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+ ]],
+ [[
+ PerlInterpreter *perl = NULL;
+ Perl_load_module (perl, PERL_LOADMOD_NOIMPORT,
+ Perl_newSVpvf (perl, "Collectd::Plugin::%s", "foo"),
+ Nullsv);
+ ]]),
+ [have_libperl="yes"],
+ [have_libperl="no"]
+ )
+ )
+
+ if test "x$have_libperl" = "xyes"
+ then
+ AC_DEFINE(HAVE_LIBPERL, 1, [Define if libperl is present and usable.])
+ else
+ with_libperl="no"
+ fi
+
+ CFLAGS=$SAVE_CFLAGS
+ LDFLAGS=$SAVE_LDFLAGS
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBPERL, test "x$with_libperl" = "xyes")
+
AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])],
[
if test "x$withval" != "xno" && test "x$withval" != "xyes"
AC_COLLECTD([nfs], [disable], [module], [nfs statistics])
AC_COLLECTD([ntpd], [disable], [module], [ntpd statistics])
AC_COLLECTD([nut], [disable], [module], [network UPS tools statistics])
+AC_COLLECTD([perl], [disable], [module], [embedded perl interpreter])
AC_COLLECTD([ping], [disable], [module], [ping statistics])
AC_COLLECTD([processes], [disable], [module], [processes statistics])
AC_COLLECTD([sensors], [disable], [module], [lm_sensors statistics])
AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/liboconfig/Makefile src/liboping/Makefile)
+if test "x$with_libperl" != "xyes"
+then
+ enable_perl="no (needs libperl)"
+fi
+
cat <<EOF;
Configuration:
libmysql . . . . . $with_libmysql
liboping . . . . . $with_liboping
libpcap . . . . . . $with_libpcap
+ libperl . . . . . . $with_libperl
libpthread . . . . $with_libpthread
librrd . . . . . . $with_rrdtool
libsensors . . . . $with_lm_sensors
nfs . . . . . . . . $enable_nfs
ntpd . . . . . . . $enable_ntpd
nut . . . . . . . . $enable_nut
+ perl . . . . . . . $enable_perl
ping . . . . . . . $enable_ping
processes . . . . . $enable_processes
sensors . . . . . . $enable_sensors
diff --git a/contrib/README b/contrib/README
index 7f21040bc9254e42330492cfa46b3c8dbcb37c7e..ab3c97812f2a448b4f32c530b51d70bbde671b32 100644 (file)
--- a/contrib/README
+++ b/contrib/README
exactly `collectd's job. They may or may not require in-depth knowlege of RRD
files and/or `collectd's inner workings. Use at your own risk.
+PerlLib/
+--------
+ Perl modules to be used in conjunction with collectd. See the perldoc
+documentation of the .pm-files to find out what they're good for.
+
add_rra.sh
----------
Before version 3.9.0 collectd used to create a different set of RRAs. The
-most detailed of these old RRAs hat a one minute resolution. This script can be
-used to add three more RRAs: minimum, maximum and average with a ten second
+most detailed of these old RRAs hat a one minute resolution. This script can
+be used to add three more RRAs: minimum, maximum and average with a ten second
resolution and 2200 rows (~6 hours). This will make houly statistics much more
interesting. Please note that no sanity- checking whatsoever is performed. You
can seriously fuck up your RRD files if you don't know what you're doing.
`/var/lib/collectd/' and generate an HTML file and a directory containing
several PNG files which are graphs of the RRD files found.
-collectd.conf
--------------
- A sample config file. Used by the Debian package.
-
collection.cgi
--------------
Sample CGI script that creates graphs on the fly. The Perl module `RRDs' is
needed (Debian package `librrds-perl').
-init.d-rh7
-----------
- Sample init script. Used by the RPM specfile.
+extractDS.px
+------------
+ Creates a new RRD-file with only one data-source (DS) of the source-RRD-
+file. That is very handy when you realise that you have bundled up DSes in one
+RRD-file that should have been in multiple RRD-files instead. Is is used by
+`migrate-3-4.px' to split up the cpu-, nfs-, swap-files and possibly others.
+
+fedora/
+-------
+ Init-script and Spec-file that can be used when creating RPM-packages for
+Fedora.
+
+migrate-3-4.px
+--------------
+ Migration-script to ease the switch from version 3 to version 4. Many
+RRD-files are expected in a different place, some have been changed (DSes have
+been renamed) and others have bee split up into multiple files. This script
+prints a bash-script to STDOUT which should do most of the work for you. You
+may still need to do some things by hand, read `README.migration' for more
+details.
diff --git a/contrib/extractDS.px b/contrib/extractDS.px
--- /dev/null
+++ b/contrib/extractDS.px
@@ -0,0 +1,206 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+=head1 NAME
+
+extractDS.px - Extract a single data-source from an RRD-file
+
+=head1 SYNOPSYS
+
+ extractDS.px -i input.rrd -s source_ds -o output.rrd -d destination_ds
+
+=head1 DEPENDENCIES
+
+extractDS.px requires Perl and the included L<Getopt::Long> module, as well as
+the L<XML::Simple> module.
+
+=cut
+
+use Getopt::Long ('GetOptions');
+use XML::Simple (qw(xml_in xml_out));
+use Data::Dumper ();
+
+our $InFile;
+our $InDS;
+our $OutFile;
+our $OutDS;
+
+GetOptions ("infile|i=s" => \$InFile,
+ "inds|s=s" => \$InDS,
+ "outfile|o=s" => \$OutFile,
+ "outds|d=s" => \$OutDS) or exit (1);
+
+if (!$InFile || !$OutFile || !$InDS || !$OutDS)
+{
+ print "$InFile $InDS $OutFile $OutDS\n";
+ print STDERR "Usage: $0 -i <infile> -I <inds> -o <outfile> -O <outds>\n";
+ exit (1);
+}
+if (!-f $InFile)
+{
+ print STDERR "Input file does not exist\n";
+ exit (1);
+}
+if (-f $OutFile)
+{
+ print STDERR "Output file does exist\n";
+ exit (1);
+}
+
+extract_ds ($InFile, $OutFile, $InDS, $OutDS);
+exit (0);
+
+{
+my $ds_index = -1;
+my $current_index = -1;
+# state 0 == searching for DS index
+# state 1 == parse RRA header
+# state 2 == parse <ds> in RRA header
+# state 3 == parse values
+my $state = 0;
+my $out_cache = '';
+sub handle_line
+{
+ my $fh = shift;
+ my $line = shift;
+
+ if (!defined ($state))
+ {
+ $ds_index = -1;
+ $current_index = -1;
+ $state = 0;
+ $out_cache = '';
+ }
+
+ if ($state == 0)
+ {
+ if ($line =~ m/<ds>/)
+ {
+ $out_cache = $line;
+ $current_index++;
+ }
+ elsif ($line =~ m#<name>\s*([^<\s]+)\s*</name>#)
+ {
+ if ($1 eq $InDS)
+ {
+ $ds_index = $current_index;
+ $out_cache .= "\t\t<name>$OutDS</name>\n";
+ }
+ }
+ elsif ($line =~ m#</ds>#)
+ {
+ $out_cache .= $line;
+ if ($ds_index == $current_index)
+ {
+ print $fh $out_cache;
+ }
+ }
+ elsif ($line =~ m#<rra>#)
+ {
+ print $fh $line;
+ $current_index = -1;
+ $state = 1;
+ }
+ elsif ($current_index == -1)
+ {
+ print $fh $line;
+ }
+ else
+ {
+ $out_cache .= $line;
+ }
+ }
+ elsif ($state == 1)
+ {
+ if ($line =~ m#<ds>#)
+ {
+ $current_index++;
+ if ($current_index == $ds_index)
+ {
+ print $fh $line;
+ }
+
+ if ($line =~ m#</ds>#) { $state = 1; }
+ else { $state = 2; }
+ }
+ elsif ($line =~ m#<database>#)
+ {
+ print $fh $line;
+ $state = 3;
+ }
+ else
+ {
+ print $fh $line;
+ }
+ }
+ elsif ($state == 2)
+ {
+ if ($current_index == $ds_index)
+ {
+ print STDERR $line;
+ print $fh $line;
+ }
+ if ($line =~ m#</ds>#)
+ {
+ $state = 1;
+ }
+ }
+ else
+ {
+ if ($line =~ m#</database>#)
+ {
+ print $fh $line;
+ $current_index = -1;
+ $state = 1;
+ }
+ else
+ {
+ my $line_begin = "\t\t";
+ $current_index = 0;
+ if ($line =~ m#(<!-- .*? -->)#)
+ {
+ $line_begin .= "$1 ";
+ }
+
+ while ($line =~ m#<v>\s*([^<\s]+)\s*</v>#)
+ {
+ my $value = $1;
+ if ($current_index == $ds_index)
+ {
+ print $fh "$line_begin<row> <v>$value</v> </row>\n";
+ last;
+ }
+ $current_index++;
+ }
+ }
+ }
+}} # handle_line
+
+sub extract_ds
+{
+ my $in_file = shift;
+ my $out_file = shift;
+ my $in_ds = shift;
+ my $out_ds = shift;
+
+ my $in_fh;
+ my $out_fh;
+
+ open ($in_fh, '-|', 'rrdtool', 'dump', $in_file) or die ("open (rrdtool): $!");
+ open ($out_fh, '|-', 'rrdtool', 'restore', '-', $out_file) or die ("open (rrdtool): $!");
+
+ while (my $line = <$in_fh>)
+ {
+ handle_line ($out_fh, $line);
+ }
+
+ close ($in_fh);
+ close ($out_fh);
+} # extract_ds
+
+=head1 AUTHOR
+
+Florian octo Forster E<lt>octo at verplant.orgE<gt>
+
diff --git a/contrib/migrate-3-4.px b/contrib/migrate-3-4.px
--- /dev/null
+++ b/contrib/migrate-3-4.px
@@ -0,0 +1,320 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Getopt::Long ('GetOptions');
+use Data::Dumper ();
+use File::Basename ('dirname');
+
+our $InDir = '/var/lib/collectd';
+our $OutDir = '/tmp/collectd-4';
+our $Hostname = 'localhost';
+
+# Types:
+# +------------+----------------------+----+----+----+
+# ! Subdir ! Type ! ti ! pi ! ex !
+# +------------+----------------------+----+----+----+
+# ! apache ! apache_bytes ! ! ! !
+# ! apache ! apache_requests ! ! ! !
+# ! apache ! apache_scoreboard ! x ! ! !
+# ! battery ! charge ! ! x ! !
+# ! apcups ! charge_percent ! ! ! !
+# ! ! cpu ! x ! ! x !
+# ! ! cpufreq ! x ! ! !
+# ! battery ! current ! ! x ! !
+# ! ntpd ! delay ! x ! ! !
+# ! ! df ! x ! ! !
+# ! ! disk ! x ! ! !
+# ! dns ! dns_traffic ! ! ! !
+# ! apple_se.. ! fanspeed ! x ! ! !
+# ! mbmon ! fanspeed ! x ! ! !
+# ! apcups ! frequency ! x ! ! !
+# ! ntpd ! frequency_offset ! x ! ! !
+# ! ! hddtemp ! x ! ! !
+# ! interface ! if_errors ! ! x ! !
+# ! interface ! if_packets ! ! x ! !
+# ! ! lm_sensors ! ! ! !
+# ! ! load ! ! ! !
+# ! apcups ! load_percent ! ! ! !
+# ! ! memory ! ! ! !
+# ! ! multimeter ! ! ! !
+# ! mysql ! mysql_commands ! x ! ! !
+# ! mysql ! mysql_handler ! x ! ! !
+# ! mysql ! mysql_qcache ! ! ! !
+# ! mysql ! mysql_threads ! ! ! !
+# ! ! nfs2_procedures ! x ! ! x !
+# ! ! nfs3_procedures ! x ! ! x !
+# ! dns ! opcode ! x ! ! !
+# ! ! partition ! x ! ! !
+# ! ! ping ! x ! ! !
+# ! ! processes ! ! ! !
+# ! processes ! ps_count ! x ! ! !
+# ! processes ! ps_cputime ! x ! ! !
+# ! processes ! ps_pagefaults ! x ! ! !
+# ! processes ! ps_rss ! x ! ! !
+# ! dns ! qtype ! x ! ! !
+# ! dns ! rcode ! x ! ! !
+# ! (*) ! sensors ! x ! ! !
+# ! ! serial ! x ! ! !
+# ! ! swap ! ! ! !
+# ! ! tape ! x ! ! !
+# ! apple_se.. ! temperature ! x ! ! !
+# ! mbmon ! temperature ! x ! ! !
+# ! ntpd ! time_dispersion ! x ! ! !
+# ! ntpd ! time_offset ! x ! ! !
+# ! apcups ! timeleft ! ! ! !
+# ! ! traffic ! x ! ! ! ->rx,tx
+# ! vserver ! traffic ! x ! x ! ! ->rx.tx
+# ! ! users ! ! ! !
+# ! apucups ! voltage ! x ! ! !
+# ! battery ! voltage ! ! x ! !
+# ! mbmon ! voltage ! x ! ! !
+# ! vserver ! vs_memory ! ! x ! !
+# ! vserver ! vs_processes ! ! x ! !
+# ! vserver ! vs_threads ! ! x ! !
+# ! ! wireless ! x ! ! !
+# +------------+----------------------+----+----+----+
+
+our %Subdirs =
+(
+ apache => 0,
+ apcups => 0,
+ apple_sensors => 0,
+ battery => 1,
+ dns => 0,
+ interface => 1,
+ mbmon => 0,
+ mysql => 0,
+ ntpd => 0,
+ processes => 0,
+ sensors => 1,
+ vserver => 1
+);
+
+our %TypeTranslate =
+(
+ cpu => sub { $_ = shift; $_->{'plugin_instance'} = $_->{'type_instance'}; $_->{'type_instance'} = undef; $_; },
+ if_errors => sub { $_ = shift; $_->{'type_instance'} = $_->{'plugin_instance'}; $_->{'plugin_instance'} = undef; $_; },
+ if_packets => sub { $_ = shift; $_->{'type_instance'} = $_->{'plugin_instance'}; $_->{'plugin_instance'} = undef; $_; },
+ nfs2_procedures => sub { $_ = shift; @$_{qw(plugin plugin_instance type type_instance)} = ('nfs', 'v2' . $_->{'type_instance'}, 'nfs_procedure', undef); $_; },
+ nfs3_procedures => sub { $_ = shift; @$_{qw(plugin plugin_instance type type_instance)} = ('nfs', 'v3' . $_->{'type_instance'}, 'nfs_procedure', undef); $_; },
+ partition => sub { $_ = shift; $_->{'plugin'} = 'disk'; $_; },
+ processes => sub { $_ = shift; $_->{'type'} = 'ps_state'; $_; },
+ traffic => sub { $_ = shift; $_->{'plugin'} =~ s/^traffic$/interface/; @$_{qw(plugin_instance type)} = (undef, 'if_octets'); $_; }
+);
+
+our %TypeSplit =
+(
+ cpu => { from => [qw(user nice syst idle wait)], to => 'value', type_instance => [qw(user nice system idle wait)] },
+ nfs3_procedures => { from => [qw(null getattr lookup access readlink
+ read write create mkdir symlink mknod remove rmdir rename link
+ readdir readdirplus fsstat fsinfo pathconf commit)], to => 'value' },
+ nfs2_procedures => { from => [qw(create fsstat getattr link lookup
+ mkdir null read readdir readlink remove rename rmdir root
+ setattr symlink wrcache write)], to => 'value' },
+ processes => { from => [qw(running sleeping zombies stopped paging blocked)], to => 'value' },
+ swap => { from => [qw(cached free used resv)], to => 'value', type_instance => [qw(cached free used reserved)] }
+);
+
+our %TypeRename =
+(
+ traffic => { from => [qw(incoming outgoing)], to => [qw(rx tx)] }
+);
+
+GetOptions ("indir|i=s" => \$InDir,
+ "outdir|o=s" => \$OutDir,
+ "hostname=s" => \$Hostname) or exit (1);
+
+die "No such directory: $InDir" if (!-d $InDir);
+if (!-e $OutDir)
+{
+ mkdir ($OutDir) or die ("mkdir ($OutDir): $!");
+}
+die "Not a directory: $OutDir" if (!-d $OutDir);
+
+our @Files = ();
+our %OutDirs = ();
+
+@Files = find_files ();
+
+for (@Files)
+{
+ my $orig_filename = $_;
+ my $orig = parse_file ($orig_filename);
+ my $dest = translate_file ($orig);
+ my $dest_filename = get_filename ($dest);
+
+ my $dest_directory = dirname ($dest_filename);
+ if (!exists ($OutDirs{$dest_directory}))
+ {
+ print "[ -d '$OutDir/$dest_directory' ] || mkdir -p '$OutDir/$dest_directory'\n";
+ $OutDirs{$dest_directory} = 1;
+ }
+
+ if (exists ($TypeSplit{$orig->{'type'}}))
+ {
+ my $src_dses = $TypeSplit{$orig->{'type'}}->{'from'};
+ my $dst_ds = $TypeSplit{$orig->{'type'}}->{'to'};
+ my $type_instances = exists ($TypeSplit{$orig->{'type'}}->{'type_instance'})
+ ? $TypeSplit{$orig->{'type'}}->{'type_instance'}
+ : $TypeSplit{$orig->{'type'}}->{'from'};
+
+ for (my $i = 0; $i < @$src_dses; $i++)
+ {
+ 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";
+ }
+ }
+ elsif (exists ($TypeRename{$orig->{'type'}}))
+ {
+ my $src_dses = $TypeRename{$orig->{'type'}}->{'from'};
+ my $dst_dses = $TypeRename{$orig->{'type'}}->{'to'};
+ my @sed_prog = ();
+
+ for (my $i = 0; $i < @$src_dses; $i++)
+ {
+ push (@sed_prog, 's/^' . $src_dses->[$i] . '$/' . $dst_dses->[$i] . '/g;');
+ }
+
+ print "rrdtool dump '$InDir/$orig_filename' | sed -e '" . join (' ', @sed_prog) . "' | rrdtool restore - '$OutDir/$dest_filename'\n";
+ }
+ else
+ {
+ print "cp '$InDir/$orig_filename' '$OutDir/$dest_filename'\n";
+ }
+}
+
+exit (0);
+
+sub translate_file
+{
+ my $orig = shift;
+ my $dest = {};
+ %$dest = %$orig;
+
+ if (defined ($TypeTranslate{$orig->{'type'}}))
+ {
+ $TypeTranslate{$orig->{'type'}}->($dest);
+ }
+
+ return ($dest);
+} # translate_file
+
+sub get_filename
+{
+ my $args = shift;
+ my $filename = $args->{'host'}
+ . '/' . $args->{'plugin'} . (defined ($args->{'plugin_instance'}) ? '-'.$args->{'plugin_instance'} : '')
+ . '/' . $args->{'type'} . (defined ($args->{'type_instance'}) ? '-'.$args->{'type_instance'} : '') . '.rrd';
+
+ return ($filename);
+}
+
+sub parse_file
+{
+ my $fullname = shift;
+ my @parts = split ('/', $fullname);
+
+ my $filename;
+
+ my $host;
+ my $plugin;
+ my $plugin_instance;
+ my $type;
+ my $type_instance;
+
+ $filename = pop (@parts);
+
+ if ($filename =~ m/^([^-]+)(?:-(.*))?\.rrd$/)
+ {
+ $type = $1;
+ $type_instance = $2;
+ }
+ else
+ {
+ return;
+ }
+
+ if (@parts)
+ {
+ my $dirname = pop (@parts);
+ my $regex_str = join ('|', keys (%Subdirs));
+ if ($dirname =~ m/^($regex_str)(?:-(.*))?$/)
+ {
+ $plugin = $1;
+ $plugin_instance = $2;
+ }
+ else
+ {
+ push (@parts, $dirname);
+ }
+ }
+ if (!$plugin)
+ {
+ $plugin = $type;
+ }
+
+ if (@parts)
+ {
+ $host = pop (@parts);
+ }
+ else
+ {
+ $host = $Hostname;
+ }
+
+ return
+ ({
+ host => $host,
+ plugin => $plugin,
+ plugin_instance => $plugin_instance,
+ type => $type,
+ type_instance => $type_instance
+ });
+} # parse_file
+
+sub find_files
+{
+ my $reldir = @_ ? shift : '';
+ my $absdir = $InDir . ($reldir ? "/$reldir" : '');
+
+ my $dh;
+
+ my @files = ();
+ my @dirs = ();
+
+ opendir ($dh, $absdir) or die ("opendir ($absdir): $!");
+ while (my $file = readdir ($dh))
+ {
+ next if ($file =~ m/^\./);
+ next if (-l "$absdir/$file");
+ if (-d "$absdir/$file")
+ {
+ push (@dirs, ($reldir ? "$reldir/" : '') . $file);
+ }
+ elsif ($file =~ m/\.rrd$/)
+ {
+ push (@files, ($reldir ? "$reldir/" : '') . $file);
+ }
+ }
+ closedir ($dh);
+
+ for (my $i = 0; $i < @dirs; $i++)
+ {
+ push (@files, find_files ($dirs[$i]));
+ }
+
+ return (@files);
+} # find_files
+
+sub special_cpu
+{
+ my %file_orig = @_;
+ my %file_dest = %file_orig;
+
+ $file_dest{'plugin_instance'} = $file_dest{'type_instance'}
+
+}
diff --git a/contrib/sles10.1/collectd.spec b/contrib/sles10.1/collectd.spec
--- /dev/null
@@ -0,0 +1,231 @@
+Summary: Statistics collection daemon for filling RRD files.
+Name: collectd
+Version: 3.11.1
+Release: 0.sl10.1
+Source: http://collectd.org/files/%{name}-%{version}.tar.gz
+Source1: collectd-init.d
+License: GPL
+Group: System Environment/Daemons
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+BuildPrereq: curl-devel, sensors, mysql-devel, rrdtool, libpcap
+Requires: rrdtool
+Packager: Florian octo Forster <octo@verplant.org>
+Vendor: Florian octo Forster <octo@verplant.org>
+
+%description
+collectd is a small daemon written in C for performance. It reads various
+system statistics and updates RRD files, creating them if neccessary.
+Since the daemon doesn't need to startup every time it wants to update the
+files it's very fast and easy on the system. Also, the statistics are very
+fine grained since the files are updated every 10 seconds.
+
+%package apache
+Summary: apache-plugin for collectd.
+Group: System Environment/Daemons
+Requires: collectd = %{version}, curl
+%description apache
+This plugin collects data provided by Apache's `mod_status'.
+
+%package dns
+Summary: dns-plugin for collectd.
+Group: System Environment/Daemons
+Requires: collectd = %{version}, libpcap
+%description dns
+This plugin collects information about DNS traffic, queries and responses.
+
+%package mysql
+Summary: mysql-module for collectd.
+Group: System Environment/Daemons
+Requires: collectd = %{version}, mysql
+%description mysql
+MySQL querying plugin. This plugins provides data of issued commands,
+called handlers and database traffic.
+
+%package sensors
+Summary: libsensors-module for collectd.
+Group: System Environment/Daemons
+Requires: collectd = %{version}, sensors
+%description sensors
+This plugin for collectd provides querying of sensors supported by
+lm_sensors.
+
+%prep
+rm -rf $RPM_BUILD_ROOT
+%setup
+
+%build
+./configure --prefix=%{_prefix} --sbindir=%{_sbindir} --mandir=%{_mandir} --libdir=%{_libdir} --sysconfdir=%{_sysconfdir} --localstatedir=%{_localstatedir}
+make
+
+%install
+make install DESTDIR=$RPM_BUILD_ROOT
+cp src/collectd.conf $RPM_BUILD_ROOT/etc/collectd.conf
+mkdir -p $RPM_BUILD_ROOT/var/lib/collectd
+rm -f $RPM_BUILD_ROOT%{_libdir}/%{name}/*.a
+rm -f $RPM_BUILD_ROOT%{_libdir}/%{name}/*.la
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/init.d
+cp %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/init.d/collectd
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+chkconfig collectd on
+/etc/init.d/collectd start
+
+%preun
+/etc/init.d/collectd stop
+chkconfig collectd off
+
+%files
+%defattr(-,root,root)
+%doc AUTHORS COPYING ChangeLog INSTALL NEWS README
+%doc contrib
+%config /etc/collectd.conf
+%attr(0755,root,root) /etc/init.d/collectd
+%attr(0755,root,root) %{_sbindir}/collectd
+%attr(0444,root,root) %{_mandir}/man1/*
+%attr(0444,root,root) %{_mandir}/man5/*
+%attr(0444,root,root) %{_libdir}/%{name}/apcups.so
+%attr(0444,root,root) %{_libdir}/%{name}/apple_sensors.so
+%attr(0444,root,root) %{_libdir}/%{name}/battery.so
+%attr(0444,root,root) %{_libdir}/%{name}/cpu.so
+%attr(0444,root,root) %{_libdir}/%{name}/cpufreq.so
+%attr(0444,root,root) %{_libdir}/%{name}/df.so
+%attr(0444,root,root) %{_libdir}/%{name}/disk.so
+%attr(0444,root,root) %{_libdir}/%{name}/email.so
+%attr(0444,root,root) %{_libdir}/%{name}/hddtemp.so
+%attr(0444,root,root) %{_libdir}/%{name}/irq.so
+%attr(0444,root,root) %{_libdir}/%{name}/load.so
+%attr(0444,root,root) %{_libdir}/%{name}/mbmon.so
+%attr(0444,root,root) %{_libdir}/%{name}/memory.so
+%attr(0444,root,root) %{_libdir}/%{name}/multimeter.so
+%attr(0444,root,root) %{_libdir}/%{name}/nfs.so
+%attr(0444,root,root) %{_libdir}/%{name}/ntpd.so
+%attr(0444,root,root) %{_libdir}/%{name}/ping.so
+%attr(0444,root,root) %{_libdir}/%{name}/processes.so
+%attr(0444,root,root) %{_libdir}/%{name}/serial.so
+%attr(0444,root,root) %{_libdir}/%{name}/swap.so
+%attr(0444,root,root) %{_libdir}/%{name}/tape.so
+%attr(0444,root,root) %{_libdir}/%{name}/traffic.so
+%attr(0444,root,root) %{_libdir}/%{name}/users.so
+%attr(0444,root,root) %{_libdir}/%{name}/vserver.so
+%attr(0444,root,root) %{_libdir}/%{name}/wireless.so
+
+%dir /var/lib/collectd
+
+%files apache
+%attr(0444,root,root) %{_libdir}/%{name}/apache.so
+
+%files dns
+%attr(0444,root,root) %{_libdir}/%{name}/dns.so
+
+%files mysql
+%attr(0444,root,root) %{_libdir}/%{name}/mysql.so
+
+%files sensors
+%attr(0444,root,root) %{_libdir}/%{name}/sensors.so
+
+%changelog
+* Sun Jul 09 2006 Florian octo Forster <octo@verplant.org> 3.10.0-1
+- New upstream version
+
+* Tue Jun 25 2006 Florian octo Forster <octo@verplant.org> 3.9.4-1
+- New upstream version
+
+* Tue Jun 01 2006 Florian octo Forster <octo@verplant.org> 3.9.3-1
+- New upstream version
+
+* Tue May 09 2006 Florian octo Forster <octo@verplant.org> 3.9.2-1
+- New upstream version
+
+* Tue May 09 2006 Florian octo Forster <octo@verplant.org> 3.8.5-1
+- New upstream version
+
+* Fri Apr 21 2006 Florian octo Forster <octo@verplant.org> 3.9.1-1
+- New upstream version
+
+* Fri Apr 14 2006 Florian octo Forster <octo@verplant.org> 3.9.0-1
+- New upstream version
+- Added the `apache' package.
+
+* Thu Mar 14 2006 Florian octo Forster <octo@verplant.org> 3.8.2-1
+- New upstream version
+
+* Thu Mar 13 2006 Florian octo Forster <octo@verplant.org> 3.8.1-1
+- New upstream version
+
+* Thu Mar 09 2006 Florian octo Forster <octo@verplant.org> 3.8.0-1
+- New upstream version
+
+* Sat Feb 18 2006 Florian octo Forster <octo@verplant.org> 3.7.2-1
+- Include `tape.so' so the build doesn't terminate because of missing files..
+- New upstream version
+
+* Sat Feb 04 2006 Florian octo Forster <octo@verplant.org> 3.7.1-1
+- New upstream version
+
+* Mon Jan 30 2006 Florian octo Forster <octo@verplant.org> 3.7.0-1
+- New upstream version
+- Removed the extra `hddtemp' package
+
+* Tue Jan 24 2006 Florian octo Forster <octo@verplant.org> 3.6.2-1
+- New upstream version
+
+* Fri Jan 20 2006 Florian octo Forster <octo@verplant.org> 3.6.1-1
+- New upstream version
+
+* Fri Jan 20 2006 Florian octo Forster <octo@verplant.org> 3.6.0-1
+- New upstream version
+- Added config file, `collectd.conf(5)', `df.so'
+- Added package `collectd-mysql', dependency on `mysqlclient10 | mysql'
+
+* Wed Dec 07 2005 Florian octo Forster <octo@verplant.org> 3.5.0-1
+- New upstream version
+
+* Sat Nov 26 2005 Florian octo Forster <octo@verplant.org> 3.4.0-1
+- New upstream version
+
+* Sat Nov 05 2005 Florian octo Forster <octo@verplant.org> 3.3.0-1
+- New upstream version
+
+* Tue Oct 26 2005 Florian octo Forster <octo@verplant.org> 3.2.0-1
+- New upstream version
+- Added statement to remove the `*.la' files. This fixes a problem when
+ `Unpackaged files terminate build' is in effect.
+- Added `processes.so*' to the main package
+
+* Fri Oct 14 2005 Florian octo Forster <octo@verplant.org> 3.1.0-1
+- New upstream version
+- Added package `collectd-hddtemp'
+
+* Fri Sep 30 2005 Florian octo Forster <octo@verplant.org> 3.0.0-1
+- New upstream version
+- Split the package into `collectd' and `collectd-sensors'
+
+* Fri Sep 16 2005 Florian octo Forster <octo@verplant.org> 2.1.0-1
+- New upstream version
+
+* Mon Sep 10 2005 Florian octo Forster <octo@verplant.org> 2.0.0-1
+- New upstream version
+
+* Mon Aug 29 2005 Florian octo Forster <octo@verplant.org> 1.8.0-1
+- New upstream version
+
+* Sun Aug 25 2005 Florian octo Forster <octo@verplant.org> 1.7.0-1
+- New upstream version
+
+* Sun Aug 21 2005 Florian octo Forster <octo@verplant.org> 1.6.0-1
+- New upstream version
+
+* Sun Jul 17 2005 Florian octo Forster <octo@verplant.org> 1.5.1-1
+- New upstream version
+
+* Sun Jul 17 2005 Florian octo Forster <octo@verplant.org> 1.5-1
+- New upstream version
+
+* Mon Jul 11 2005 Florian octo Forster <octo@verplant.org> 1.4.2-1
+- New upstream version
+
+* Sat Jul 09 2005 Florian octo Forster <octo@verplant.org> 1.4-1
+- Built on RedHat 7.3
diff --git a/contrib/sles10.1/init.d-collectd b/contrib/sles10.1/init.d-collectd
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides: collectd
+# Required-Start: $local_fs $remote_fs $network
+# X-UnitedLinux-Should-Start: $named $time apache mysql
+# Required-Stop: $local_fs $remote_fs $network
+# X-UnitedLinux-Should-Stop:
+# Default-Start: 3 5
+# Default-Stop: 0 1 2 6
+# Short-Description: Statistics daemon collectd
+# Description: Start the statistics daemon collectd
+### END INIT INFO
+
+
+#
+# load the configuration
+#
+test -s /etc/rc.status && . /etc/rc.status && rc_reset
+
+RETVAL=0
+ARGS=""
+prog="collectd"
+CONFIG=/etc/collectd.conf
+
+if [ -r /etc/default/$prog ]; then
+ . /etc/default/$prog
+fi
+
+start () {
+ echo -n $"Starting $prog: "
+ RETVAL=1
+ if [ -r "$CONFIG" ]
+ then
+ eval startproc /usr/sbin/collectd -C "$CONFIG"
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
+ fi
+ rc_failed $RETVAL
+ rc_status -v
+}
+stop () {
+ echo -n $"Stopping $prog: "
+ killproc $prog
+ RETVAL=$?
+ rc_failed $RETVAL
+ rc_status -v
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
+}
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status $prog
+ ;;
+ restart|reload)
+ stop
+ sleep 1
+ start
+ ;;
+ condrestart)
+ [ -f /var/lock/subsys/$prog ] && restart || :
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
+ exit 1
+esac
+
+rc_exit
+# vim:syntax=sh
diff --git a/debian/changelog b/debian/changelog
index fead67273594b0bf5a9826f8bca9f6fc77c86258..8d04e16c0df60f3ec684905f8ee5861210f033cd 100644 (file)
--- a/debian/changelog
+++ b/debian/changelog
+collectd (3.11.4-0octo1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Florian Forster <octo@leeloo.home.verplant.org> Tue, 10 Apr 2007 17:18:11 +0200
+
collectd (3.11.3-0octo1) unstable; urgency=low
* New upstream release.
diff --git a/src/Makefile.am b/src/Makefile.am
index 23d82aa5e563f57ddd0a1ebac1d255f11941a389..1016bfe871f49accd408f09b3e69deec817664e4 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
collectd_DEPENDENCIES += nut.la
endif
+if BUILD_WITH_LIBPERL
+if BUILD_MODULE_PERL
+pkglib_LTLIBRARIES += perl.la
+perl_la_SOURCES = perl.c
+perl_la_CFLAGS = $(AM_CFLAGS) \
+ $(shell perl -MExtUtils::Embed -e ccopts) \
+ -DXS_VERSION=\"$(VERSION)\" -DVERSION=\"$(VERSION)\"
+perl_la_LDFLAGS = -module -avoid-version \
+ $(shell perl -MExtUtils::Embed -e ldopts)
+collectd_LDADD += "-dlopen" perl.la
+collectd_DEPENDENCIES += perl.la
+endif
+endif
+
if BUILD_MODULE_PING
pkglib_LTLIBRARIES += ping.la
ping_la_SOURCES = ping.c
diff --git a/src/collectd.conf.in b/src/collectd.conf.in
index 020bdebed534ed9d6186d62562c5e90269bd2e32..3aa1306587edbbb9ef25b6a863db1a03f8319560 100644 (file)
--- a/src/collectd.conf.in
+++ b/src/collectd.conf.in
@BUILD_MODULE_NFS_TRUE@LoadPlugin nfs
@BUILD_MODULE_NTPD_TRUE@LoadPlugin ntpd
@BUILD_MODULE_NUT_TRUE@LoadPlugin nut
+@BUILD_MODULE_PERL_TRUE@LoadPlugin perl
@BUILD_MODULE_PING_TRUE@LoadPlugin ping
@BUILD_MODULE_PROCESSES_TRUE@LoadPlugin processes
@BUILD_WITH_RRDTOOL_TRUE@LoadPlugin rrdtool
# UPS "upsname@hostname:port"
#</Plugin>
+#<Plugin perl>
+# LoadPlugin foo
+#</Plugin>
+
#<Plugin ping>
# Host "host.foo.bar"
#</Plugin>
diff --git a/src/collectd.pod b/src/collectd.pod
index bc65f57d5031c7d82c8cf3fff1b965dc8ac458b5..08524901d982a7357075f31fa75906880e3e0fa7 100644 (file)
--- a/src/collectd.pod
+++ b/src/collectd.pod
in F<mysql_threads.rrd>. Please refer to the B<MySQL reference manual>,
I<5.2.4. Server Status Variables> for an explanation of these values.
+=head2 perl
+
+The C<perl plugin> includes a Perl-interpreter in collectd and provides
+Perl-equvalents of the plugin-functions. This makes it possible to write
+plugins in Perl.
+
+There are two more comlex types you need to know about:
+
+=over 4
+
+=item Data-Set
+
+A data-set is a list of one or more data-sources. Each data-source defines a
+name, type, min- and max-value and the data-set wraps them up into one
+structure. The general layout looks like this:
+
+ [{
+ name => 'data_source_name',
+ type => DS_TYPE_COUNTER || DS_TYPE_GAUGE
+ min => value || undef,
+ max => value || undef
+ }, ...]
+
+=item Value-List
+
+A value-list is one structure which features an array of values and fields to
+identify the values, i. e. time and host, plugin name and plugin-instance as
+well as a type and type-instance. Since the "type" is not included in the
+value-list but is passed as an extra argument, the general layout looks like
+this:
+
+ {
+ values => [123, 0.5],
+ time => time (),
+ host => 'localhost',
+ plugin => 'myplugin',
+ plugin_instance => '',
+ type_instance => ''
+ }
+
+=back
+
+The following functions provide the C-interface to Perl-modules:
+
+=over 4
+
+=item B<plugin_register> (I<type>, I<name>, I<data>)
+
+Registers a callback-function or data-set.
+
+I<type> can be one of:
+
+=over 4
+
+=item TYPE_INIT
+
+=item TYPE_READ
+
+=item TYPE_WRITE
+
+=item TYPE_LOG
+
+=item TYPE_SHUTDOWN
+
+=item TYPE_DATASET
+
+=back
+
+I<name> is the name of the callback-function or the type of the data-set,
+depending on the value of I<type>. (Please note that the type of the data-set
+is the value passed as I<name> here and has nothing to do with the I<type>
+argument which simply tells B<plugin_register> what is being registered.)
+
+The last argument, I<data>, is either a function- or an array-reference. If
+I<type> is B<TYPE_DATASET>, then the I<data> argument must be an
+array-reference which points to an array of hashes. Each hash describes one
+data-source. For the exact layout see B<Data-Set> above.
+
+If the I<type> argument is any of the other types (B<TYPE_INIT>, B<TYPE_READ>,
+...) when I<data> is expected to be a funtion reference. These functions are
+called in the various stages of the daemon and are passed the following
+arguments:
+
+=over 4
+
+=item TYPE_INIT
+
+=item TYPE_READ
+
+=item TYPE_SHUTDOWN
+
+No arguments are passed
+
+=item TYPE_WRITE
+
+The arguments passed are I<type>, I<data-set>, and I<value-list>. I<type> is a
+string. For the layout of I<data-set> and I<value-list> see above.
+
+=item TYPE_LOG
+
+The arguments are I<log-level> and I<message>. The log level is small for
+important messages and high for less important messages. The least important
+level is B<LOG_DEBUG>, the most important level is B<LOG_ERR>. In between there
+are (from least to most important): B<LOG_INFO>, B<LOG_NOTICE>, and
+B<LOG_WARNING>. I<message> is simply a string B<without> a newline at the end.
+
+=back
+
+=item B<plugin_unregister> (I<type>, I<plugin>)
+
+Removes a callback or data-set from collectd's internal list of
+functionsE<nbsp>/ datasets.
+
+=item B<plugin_dispatch_values> (I<type>, I<value-list>)
+
+Submits a I<value-list> of type I<type> to the daemon. If the data-set I<type>
+is found (and the number of values matches the number of data-sources) then the
+type, data-set and value-list is passed to all write-callbacks that are
+registered with the daemon.
+
+=item B<plugin_log> (I<log-level>, I<message>)
+
+TODO.
+
+=back
+
=head2 sensors
The B<sensors> module uses lm_sensors to retrieve sensor-values. This means
diff --git a/src/perl.c b/src/perl.c
--- /dev/null
+++ b/src/perl.c
@@ -0,0 +1,1055 @@
+/**
+ * collectd - src/perl.c
+ * Copyright (C) 2007 Sebastian Harl
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Sebastian Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This plugin embeds a Perl interpreter into collectd and provides an
+ * interface for collectd plugins written in perl.
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include "configfile.h"
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#include <XSUB.h>
+
+#define PLUGIN_INIT 0
+#define PLUGIN_READ 1
+#define PLUGIN_WRITE 2
+#define PLUGIN_SHUTDOWN 3
+#define PLUGIN_LOG 4
+
+#define PLUGIN_TYPES 5
+
+#define PLUGIN_DATASET 255
+
+#define log_debug(...) DEBUG ("perl: " __VA_ARGS__)
+#define log_warn(...) WARNING ("perl: " __VA_ARGS__)
+#define log_err(...) ERROR ("perl: " __VA_ARGS__)
+
+
+/* this is defined in DynaLoader.a */
+void boot_DynaLoader (PerlInterpreter *, CV *);
+
+static XS (Collectd_plugin_register);
+static XS (Collectd_plugin_unregister);
+static XS (Collectd_plugin_dispatch_values);
+
+
+/*
+ * private data types
+ */
+
+typedef struct {
+ int len;
+ int *values;
+} ds_types_t;
+
+typedef struct {
+ int wait_time;
+ int wait_left;
+
+ SV *sub;
+} pplugin_t;
+
+
+/*
+ * private variables
+ */
+
+/* valid configuration file keys */
+static const char *config_keys[] =
+{
+ "LoadPlugin",
+ NULL
+};
+static int config_keys_num = 1;
+
+static PerlInterpreter *perl = NULL;
+
+static char *plugin_types[] = { "init", "read", "write", "shutdown" };
+static HV *plugins[PLUGIN_TYPES];
+static HV *data_sets;
+
+static struct {
+ char name[64];
+ XS ((*f));
+} api[] =
+{
+ { "Collectd::plugin_register", Collectd_plugin_register },
+ { "Collectd::plugin_unregister", Collectd_plugin_unregister },
+ { "Collectd::plugin_dispatch_values", Collectd_plugin_dispatch_values },
+ { "", NULL }
+};
+
+
+/*
+ * Helper functions for data type conversion.
+ */
+
+/*
+ * data source:
+ * [
+ * {
+ * name => $ds_name,
+ * type => $ds_type,
+ * min => $ds_min,
+ * max => $ds_max
+ * },
+ * ...
+ * ]
+ */
+static int hv2data_source (HV *hash, data_source_t *ds)
+{
+ SV **tmp = NULL;
+
+ if ((NULL == hash) || (NULL == ds))
+ return -1;
+
+ if (NULL != (tmp = Perl_hv_fetch (perl, hash, "name", 4, 0))) {
+ strncpy (ds->name, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
+ ds->name[DATA_MAX_NAME_LEN - 1] = '\0';
+ }
+ else {
+ log_err ("hv2data_source: No DS name given.");
+ return -1;
+ }
+
+ if (NULL != (tmp = Perl_hv_fetch (perl, hash, "type", 4, 0))) {
+ ds->type = SvIV (*tmp);
+
+ if ((DS_TYPE_COUNTER != ds->type) && (DS_TYPE_GAUGE != ds->type)) {
+ log_err ("hv2data_source: Invalid DS type.");
+ return -1;
+ }
+ }
+ else {
+ ds->type = DS_TYPE_COUNTER;
+ }
+
+ if (NULL != (tmp = Perl_hv_fetch (perl, hash, "min", 3, 0)))
+ ds->min = SvNV (*tmp);
+ else
+ ds->min = NAN;
+
+ if (NULL != (tmp = Perl_hv_fetch (perl, hash, "max", 3, 0)))
+ ds->max = SvNV (*tmp);
+ else
+ ds->max = NAN;
+ return 0;
+} /* static data_source_t *hv2data_source (HV *) */
+
+static int av2value (char *name, AV *array, value_t *value, int len)
+{
+ SV **tmp = NULL;
+
+ ds_types_t *ds = NULL;
+
+ int i = 0;
+
+ if ((NULL == name) || (NULL == array) || (NULL == value))
+ return -1;
+
+ if (Perl_av_len (perl, array) < len - 1)
+ len = Perl_av_len (perl, array) + 1;
+
+ if (0 >= len)
+ return -1;
+
+ tmp = Perl_hv_fetch (perl, data_sets, name, strlen (name), 0);
+ if (NULL == tmp) {
+ log_err ("av2value: No dataset for \"%s\".", name);
+ return -1;
+ }
+ ds = (ds_types_t *)SvIV ((SV *)SvRV (*tmp));
+
+ if (ds->len < len) {
+ log_warn ("av2value: Value length exceeds data set length.");
+ len = ds->len;
+ }
+
+ for (i = 0; i < len; ++i) {
+ SV **tmp = Perl_av_fetch (perl, array, i, 0);
+
+ if (NULL != tmp) {
+ if (DS_TYPE_COUNTER == ds->values[i])
+ value[i].counter = SvIV (*tmp);
+ else
+ value[i].gauge = SvNV (*tmp);
+ }
+ else {
+ return -1;
+ }
+ }
+ return len;
+} /* static int av2value (char *, AV *, value_t *, int) */
+
+static int data_set2av (data_set_t *ds, AV *array)
+{
+ int i = 0;
+
+ if ((NULL == ds) || (NULL == array))
+ return -1;
+
+ Perl_av_extend (perl, array, ds->ds_num);
+
+ for (i = 0; i < ds->ds_num; ++i) {
+ HV *source = Perl_newHV (perl);
+
+ if (NULL == Perl_hv_store (perl, source, "name", 4,
+ Perl_newSVpv (perl, ds->ds[i].name, 0), 0))
+ return -1;
+
+ if (NULL == Perl_hv_store (perl, source, "type", 4,
+ Perl_newSViv (perl, ds->ds[i].type), 0))
+ return -1;
+
+ if (! isnan (ds->ds[i].min))
+ if (NULL == Perl_hv_store (perl, source, "min", 3,
+ Perl_newSVnv (perl, ds->ds[i].min), 0))
+ return -1;
+
+ if (! isnan (ds->ds[i].max))
+ if (NULL == Perl_hv_store (perl, source, "max", 3,
+ Perl_newSVnv (perl, ds->ds[i].max), 0))
+ return -1;
+
+ if (NULL == Perl_av_store (perl, array, i,
+ Perl_newRV_noinc (perl, (SV *)source)))
+ return -1;
+ }
+ return 0;
+} /* static int data_set2av (data_set_t *, AV *) */
+
+static int value_list2hv (value_list_t *vl, data_set_t *ds, HV *hash)
+{
+ AV *values = NULL;
+
+ int i = 0;
+ int len = 0;
+
+ if ((NULL == vl) || (NULL == ds) || (NULL == hash))
+ return -1;
+
+ len = vl->values_len;
+
+ if (ds->ds_num < len) {
+ log_warn ("value2av: Value length exceeds data set length.");
+ len = ds->ds_num;
+ }
+
+ values = Perl_newAV (perl);
+ Perl_av_extend (perl, values, len - 1);
+
+ for (i = 0; i < len; ++i) {
+ SV *val = NULL;
+
+ if (DS_TYPE_COUNTER == ds->ds[i].type)
+ val = Perl_newSViv (perl, vl->values[i].counter);
+ else
+ val = Perl_newSVnv (perl, vl->values[i].gauge);
+
+ if (NULL == Perl_av_store (perl, values, i, val)) {
+ Perl_av_undef (perl, values);
+ return -1;
+ }
+ }
+
+ if (NULL == Perl_hv_store (perl, hash, "values", 6,
+ Perl_newRV_noinc (perl, (SV *)values), 0))
+ return -1;
+
+ if (0 != vl->time)
+ if (NULL == Perl_hv_store (perl, hash, "time", 4,
+ Perl_newSViv (perl, vl->time), 0))
+ return -1;
+
+ if ('\0' != vl->host[0])
+ if (NULL == Perl_hv_store (perl, hash, "host", 4,
+ Perl_newSVpv (perl, vl->host, 0), 0))
+ return -1;
+
+ if ('\0' != vl->plugin[0])
+ if (NULL == Perl_hv_store (perl, hash, "plugin", 6,
+ Perl_newSVpv (perl, vl->plugin, 0), 0))
+ return -1;
+
+ if ('\0' != vl->plugin_instance[0])
+ if (NULL == Perl_hv_store (perl, hash, "plugin_instance", 15,
+ Perl_newSVpv (perl, vl->plugin_instance, 0), 0))
+ return -1;
+
+ if ('\0' != vl->type_instance[0])
+ if (NULL == Perl_hv_store (perl, hash, "type_instance", 13,
+ Perl_newSVpv (perl, vl->type_instance, 0), 0))
+ return -1;
+ return 0;
+} /* static int value2av (value_list_t *, data_set_t *, HV *) */
+
+
+/*
+ * Internal functions.
+ */
+
+/*
+ * Add a new plugin with the given name.
+ */
+static int pplugin_register (int type, const char *name, SV *sub)
+{
+ pplugin_t *p = NULL;
+
+ if ((type < 0) || (type >= PLUGIN_TYPES))
+ return -1;
+
+ if (NULL == name)
+ return -1;
+
+ p = (pplugin_t *)smalloc (sizeof (pplugin_t));
+ /* this happens during parsing of config file,
+ * thus interval_g is not set correctly */
+ p->wait_time = 10;
+ p->wait_left = 0;
+ p->sub = Perl_newSVsv (perl, sub);
+
+ if (NULL == Perl_hv_store (perl, plugins[type], name, strlen (name),
+ Perl_sv_setref_pv (perl, Perl_newSV (perl, 0), 0, p), 0)) {
+ log_debug ("pplugin_register: Failed to add plugin \"%s\" (\"%s\")",
+ name, SvPV_nolen (sub));
+ Perl_sv_free (perl, p->sub);
+ sfree (p);
+ return -1;
+ }
+ return 0;
+} /* static int pplugin_register (int, char *, SV *) */
+
+/*
+ * Removes the plugin with the given name and frees any ressources.
+ */
+static int pplugin_unregister (int type, char *name)
+{
+ SV *tmp = NULL;
+
+ if ((type < 0) || (type >= PLUGIN_TYPES))
+ return -1;
+
+ if (NULL == name)
+ return -1;
+
+ /* freeing the allocated memory of the element itself (pplugin_t *) causes
+ * a segfault during perl_destruct () thus I assume perl somehow takes
+ * care of this... */
+
+ tmp = Perl_hv_delete (perl, plugins[type], name, strlen (name), 0);
+ if (NULL != tmp) {
+ pplugin_t *p = (pplugin_t *)SvIV ((SV *)SvRV (tmp));
+ Perl_sv_free (perl, p->sub);
+ }
+ return 0;
+} /* static int pplugin_unregister (char *) */
+
+/*
+ * Add a plugin's data set definition.
+ */
+static int pplugin_register_data_set (char *name, AV *dataset)
+{
+ int len = -1;
+ int i = 0;
+
+ data_source_t *ds = NULL;
+ data_set_t *set = NULL;
+
+ ds_types_t *types = NULL;
+
+ if ((NULL == name) || (NULL == dataset))
+ return -1;
+
+ len = Perl_av_len (perl, dataset);
+
+ if (-1 == len)
+ return -1;
+
+ ds = (data_source_t *)smalloc ((len + 1) * sizeof (data_source_t));
+ set = (data_set_t *)smalloc (sizeof (data_set_t));
+
+ types = (ds_types_t *)smalloc (sizeof (ds_types_t));
+ types->len = len + 1;
+ types->values = (int *)smalloc ((types->len) * sizeof (int));
+
+ for (i = 0; i <= len; ++i) {
+ SV **elem = Perl_av_fetch (perl, dataset, i, 0);
+
+ if (NULL == elem)
+ return -1;
+
+ if (! (SvROK (*elem) && (SVt_PVHV == SvTYPE (SvRV (*elem))))) {
+ log_err ("pplugin_register_data_set: Invalid data source.");
+ return -1;
+ }
+
+ if (-1 == hv2data_source ((HV *)SvRV (*elem), &ds[i]))
+ return -1;
+
+ types->values[i] = ds[i].type;
+ log_debug ("pplugin_register_data_set: "
+ "DS.name = \"%s\", DS.type = %i, DS.min = %f, DS.max = %f",
+ ds[i].name, ds[i].type, ds[i].min, ds[i].max);
+ }
+
+ if (NULL == Perl_hv_store (perl, data_sets, name, strlen (name),
+ Perl_sv_setref_pv (perl, Perl_newSV (perl, 0), 0, types), 0))
+ return -1;
+
+ strncpy (set->type, name, DATA_MAX_NAME_LEN);
+ set->type[DATA_MAX_NAME_LEN - 1] = '\0';
+
+ set->ds_num = len + 1;
+ set->ds = ds;
+ return plugin_register_data_set (set);
+} /* static int pplugin_register_data_set (char *, SV *) */
+
+/*
+ * Remove a plugin's data set definition.
+ */
+static int pplugin_unregister_data_set (char *name)
+{
+ SV *tmp = NULL;
+
+ if (NULL == name)
+ return 0;
+
+ /* freeing the allocated memory of the element itself (ds_types_t *)
+ * causes a segfault during perl_destruct () thus I assume perl somehow
+ * takes care of this... */
+
+ tmp = Perl_hv_delete (perl, data_sets, name, strlen (name), 0);
+ if (NULL != tmp) {
+ ds_types_t *ds = (ds_types_t *)SvIV ((SV *)SvRV (tmp));
+ sfree (ds->values);
+ }
+ return plugin_unregister_data_set (name);
+} /* static int pplugin_unregister_data_set (char *) */
+
+/*
+ * Submit the values to the write functions.
+ *
+ * value list:
+ * {
+ * values => [ @values ],
+ * time => $time,
+ * host => $host,
+ * plugin => $plugin,
+ * plugin_instance => $pinstance,
+ * type_instance => $tinstance,
+ * }
+ */
+static int pplugin_dispatch_values (char *name, HV *values)
+{
+ value_list_t list = VALUE_LIST_INIT;
+ value_t *val = NULL;
+
+ SV **tmp = NULL;
+
+ int ret = 0;
+
+ if ((NULL == name) || (NULL == values))
+ return -1;
+
+ if ((NULL == (tmp = Perl_hv_fetch (perl, values, "values", 6, 0)))
+ || (! (SvROK (*tmp) && (SVt_PVAV == SvTYPE (SvRV (*tmp)))))) {
+ log_err ("pplugin_dispatch_values: No valid values given.");
+ return -1;
+ }
+
+ {
+ AV *array = (AV *)SvRV (*tmp);
+ int len = Perl_av_len (perl, array) + 1;
+
+ val = (value_t *)smalloc (len * sizeof (value_t));
+
+ list.values_len = av2value (name, (AV *)SvRV (*tmp), val, len);
+ list.values = val;
+
+ if (-1 == list.values_len) {
+ sfree (val);
+ return -1;
+ }
+ }
+
+ if (NULL != (tmp = Perl_hv_fetch (perl, values, "time", 4, 0))) {
+ list.time = (time_t)SvIV (*tmp);
+ }
+ else {
+ list.time = time (NULL);
+ }
+
+ if (NULL != (tmp = Perl_hv_fetch (perl, values, "host", 4, 0))) {
+ strncpy (list.host, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
+ }
+ else {
+ strcpy (list.host, hostname_g);
+ }
+
+ if (NULL != (tmp = Perl_hv_fetch (perl, values, "plugin", 6, 0))) {
+ strncpy (list.plugin, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
+ list.plugin[DATA_MAX_NAME_LEN - 1] = '\0';
+ }
+
+ if (NULL != (tmp = Perl_hv_fetch (perl, values,
+ "plugin_instance", 15, 0))) {
+ strncpy (list.plugin_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
+ list.plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+ }
+
+ if (NULL != (tmp = Perl_hv_fetch (perl, values, "type_instance", 13, 0))) {
+ strncpy (list.type_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
+ list.type_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+ }
+
+ ret = plugin_dispatch_values (name, &list);
+
+ sfree (val);
+ return ret;
+} /* static int pplugin_dispatch_values (char *, HV *) */
+
+/*
+ * Call a plugin's working function.
+ */
+static int pplugin_call (int type, char *name, SV *sub, va_list ap)
+{
+ int retvals = 0;
+ I32 xflags = G_NOARGS;
+
+ int ret = 0;
+
+ dSP;
+
+ if ((type < 0) || (type >= PLUGIN_TYPES))
+ return -1;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK (SP);
+
+ if (PLUGIN_WRITE == type) {
+ /*
+ * $_[0] = $plugin_type;
+ *
+ * $_[1] =
+ * [
+ * {
+ * name => $ds_name,
+ * type => $ds_type,
+ * min => $ds_min,
+ * max => $ds_max
+ * },
+ * ...
+ * ];
+ *
+ * $_[2] =
+ * {
+ * values => [ $v1, ... ],
+ * time => $time,
+ * host => $hostname,
+ * plugin => $plugin,
+ * plugin_instance => $instance,
+ * type_instance => $type_instance
+ * };
+ */
+ data_set_t *ds;
+ value_list_t *vl;
+
+ AV *pds = Perl_newAV (perl);
+ HV *pvl = Perl_newHV (perl);
+
+ ds = va_arg (ap, data_set_t *);
+ vl = va_arg (ap, value_list_t *);
+
+ if (-1 == data_set2av (ds, pds))
+ return -1;
+
+ if (-1 == value_list2hv (vl, ds, pvl))
+ return -1;
+
+ XPUSHs (sv_2mortal (Perl_newSVpv (perl, ds->type, 0)));
+ XPUSHs (sv_2mortal (Perl_newRV_noinc (perl, (SV *)pds)));
+ XPUSHs (sv_2mortal (Perl_newRV_noinc (perl, (SV *)pvl)));
+
+ xflags = 0;
+ }
+ else if (PLUGIN_LOG == type) {
+ /*
+ * $_[0] = $level;
+ *
+ * $_[1] = $message;
+ */
+ XPUSHs (sv_2mortal (Perl_newSViv (perl, va_arg (ap, int))));
+ XPUSHs (sv_2mortal (Perl_newSVpv (perl, va_arg (ap, char *), 0)));
+
+ xflags = 0;
+ }
+
+ PUTBACK;
+
+ /* prevent an endless loop */
+ if (PLUGIN_LOG != type)
+ log_debug ("pplugin_call: executing Collectd::plugin::%s->%s()",
+ name, plugin_types[type]);
+
+ retvals = Perl_call_sv (perl, sub, G_SCALAR | xflags);
+
+ SPAGAIN;
+ if (1 > retvals) {
+ if (PLUGIN_LOG != type)
+ log_warn ("pplugin_call: "
+ "Collectd::plugin::%s->%s() returned void - assuming true",
+ name, plugin_types[type]);
+ }
+ else {
+ SV *tmp = POPs;
+ if (! SvTRUE (tmp))
+ ret = -1;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ return ret;
+} /* static int pplugin_call (int, char *, SV *, va_list) */
+
+/*
+ * Call all working functions of the given type.
+ */
+static int pplugin_call_all (int type, ...)
+{
+ SV *tmp = NULL;
+
+ char *plugin;
+ I32 len;
+
+ if ((type < 0) || (type >= PLUGIN_TYPES))
+ return -1;
+
+ if (0 == Perl_hv_iterinit (perl, plugins[type]))
+ return 0;
+
+ while (NULL != (tmp = Perl_hv_iternextsv (perl, plugins[type],
+ &plugin, &len))) {
+ pplugin_t *p;
+ va_list ap;
+
+ int status;
+
+ va_start (ap, type);
+
+ p = (pplugin_t *)SvIV ((SV *)SvRV (tmp));
+
+ if (p->wait_left > 0)
+ p->wait_left -= interval_g;
+
+ if (p->wait_left > 0)
+ continue;
+
+ if (0 == (status = pplugin_call (type, plugin, p->sub, ap))) {
+ p->wait_left = 0;
+ p->wait_time = interval_g;
+ }
+ else if (PLUGIN_READ == type) {
+ p->wait_left = p->wait_time;
+ p->wait_time <<= 1;
+
+ if (p->wait_time > 86400)
+ p->wait_time = 86400;
+
+ log_warn ("Collectd::plugin::%s->read() failed. "
+ "Will suspend it for %i seconds.",
+ plugin, p->wait_left);
+ }
+ else if (PLUGIN_INIT == type) {
+ int i = 0;
+
+ log_err ("Collectd::plugin::%s->init() failed. "
+ "Plugin will be disabled.", plugin, status);
+
+ for (i = 0; i < PLUGIN_TYPES; ++i)
+ pplugin_unregister (i, plugin);
+ }
+ else if (PLUGIN_LOG != type) {
+ log_warn ("Collectd::plugin::%s->%s() failed with status %i.",
+ plugin, plugin_types[type], status);
+ }
+
+ va_end (ap);
+ }
+ return 0;
+} /* static int pplugin_call_all (int, ...) */
+
+
+/*
+ * Exported Perl API.
+ */
+
+/*
+ * Collectd::plugin_register (type, name, data).
+ *
+ * type:
+ * init, read, write, shutdown, data set
+ *
+ * name:
+ * name of the plugin
+ *
+ * data:
+ * reference to the plugin's subroutine that does the work or the data set
+ * definition
+ */
+static XS (Collectd_plugin_register)
+{
+ int type = 0;
+ SV *data = NULL;
+
+ int ret = 0;
+
+ dXSARGS;
+
+ if (3 != items) {
+ log_err ("Usage: Collectd::plugin_register(type, name, data)");
+ XSRETURN_EMPTY;
+ }
+
+ log_debug ("Collectd::plugin_register: "
+ "type = \"%i\", name = \"%s\", \"%s\"",
+ (int)SvIV (ST (0)), SvPV_nolen (ST (1)), SvPV_nolen (ST (2)));
+
+ type = (int)SvIV (ST (0));
+ data = ST (2);
+
+ if ((type >= 0) && (type < PLUGIN_TYPES)
+ && SvROK (data) && (SVt_PVCV == SvTYPE (SvRV (data)))) {
+ ret = pplugin_register (type, SvPV_nolen (ST (1)), data);
+ }
+ else if ((type == PLUGIN_DATASET)
+ && SvROK (data) && (SVt_PVAV == SvTYPE (SvRV (data)))) {
+ ret = pplugin_register_data_set (SvPV_nolen (ST (1)),
+ (AV *)SvRV (data));
+ }
+ else {
+ log_err ("Collectd::plugin_register: Invalid data.");
+ XSRETURN_EMPTY;
+ }
+
+ if (0 == ret)
+ XSRETURN_YES;
+ else
+ XSRETURN_EMPTY;
+} /* static XS (Collectd_plugin_register) */
+
+/*
+ * Collectd::plugin_unregister (type, name).
+ *
+ * type:
+ * init, read, write, shutdown, data set
+ *
+ * name:
+ * name of the plugin
+ */
+static XS (Collectd_plugin_unregister)
+{
+ int type = 0;
+ int ret = 0;
+
+ dXSARGS;
+
+ if (2 != items) {
+ log_err ("Usage: Collectd::plugin_unregister(type, name)");
+ XSRETURN_EMPTY;
+ }
+
+ log_debug ("Collectd::plugin_unregister: type = \"%i\", name = \"%s\"",
+ (int)SvIV (ST (0)), SvPV_nolen (ST (1)));
+
+ type = (int)SvIV (ST (0));
+
+ if ((type >= 0) && (type < PLUGIN_TYPES)) {
+ ret = pplugin_unregister (type, SvPV_nolen (ST (1)));
+ }
+ else if (type == PLUGIN_DATASET) {
+ ret = pplugin_unregister_data_set (SvPV_nolen (ST (1)));
+ }
+ else {
+ log_err ("Collectd::plugin_unregister: Invalid type.");
+ XSRETURN_EMPTY;
+ }
+
+ if (0 == ret)
+ XSRETURN_YES;
+ else
+ XSRETURN_EMPTY;
+} /* static XS (Collectd_plugin_unregister) */
+
+/*
+ * Collectd::plugin_dispatch_values (name, values).
+ *
+ * name:
+ * name of the plugin
+ *
+ * values:
+ * value list to submit
+ */
+static XS (Collectd_plugin_dispatch_values)
+{
+ SV *values = NULL;
+
+ int ret = 0;
+
+ dXSARGS;
+
+ items = 2;
+ if (2 != items) {
+ log_err ("Usage: Collectd::plugin_dispatch_values(name, values)");
+ XSRETURN_EMPTY;
+ }
+
+ log_debug ("Collectd::plugin_dispatch_values: "
+ "name = \"%s\", values=\"%s\"",
+ SvPV_nolen (ST (0)), SvPV_nolen (ST (1)));
+
+ values = ST (1);
+
+ if (! (SvROK (values) && (SVt_PVHV == SvTYPE (SvRV (values))))) {
+ log_err ("Collectd::plugin_dispatch_values: Invalid values.");
+ XSRETURN_EMPTY;
+ }
+
+ if ((NULL == ST (0)) || (NULL == values))
+ XSRETURN_EMPTY;
+
+ ret = pplugin_dispatch_values (SvPV_nolen (ST (0)), (HV *)SvRV (values));
+
+ if (0 == ret)
+ XSRETURN_YES;
+ else
+ XSRETURN_EMPTY;
+} /* static XS (Collectd_plugin_dispatch_values) */
+
+/*
+ * Collectd::bootstrap ().
+ */
+static XS (boot_Collectd)
+{
+ HV *stash = NULL;
+ char *file = __FILE__;
+
+ struct {
+ char name[64];
+ SV *value;
+ } consts[] =
+ {
+ { "Collectd::TYPE_INIT", Perl_newSViv (perl, PLUGIN_INIT) },
+ { "Collectd::TYPE_READ", Perl_newSViv (perl, PLUGIN_READ) },
+ { "Collectd::TYPE_WRITE", Perl_newSViv (perl, PLUGIN_WRITE) },
+ { "Collectd::TYPE_SHUTDOWN", Perl_newSViv (perl, PLUGIN_SHUTDOWN) },
+ { "Collectd::TYPE_LOG", Perl_newSViv (perl, PLUGIN_LOG) },
+ { "Collectd::TYPE_DATASET", Perl_newSViv (perl, PLUGIN_DATASET) },
+ { "Collectd::DS_TYPE_COUNTER", Perl_newSViv (perl, DS_TYPE_COUNTER) },
+ { "Collectd::DS_TYPE_GAUGE", Perl_newSViv (perl, DS_TYPE_GAUGE) },
+ { "Collectd::LOG_ERR", Perl_newSViv (perl, LOG_ERR) },
+ { "Collectd::LOG_WARNING", Perl_newSViv (perl, LOG_WARNING) },
+ { "Collectd::LOG_NOTICE", Perl_newSViv (perl, LOG_NOTICE) },
+ { "Collectd::LOG_INFO", Perl_newSViv (perl, LOG_INFO) },
+ { "Collectd::LOG_DEBUG", Perl_newSViv (perl, LOG_DEBUG) },
+ { "", NULL }
+ };
+
+ int i = 0;
+
+ dXSARGS;
+
+ if ((1 > items) || (2 < items)) {
+ log_err ("Usage: Collectd::bootstrap(name[, version])");
+ XSRETURN_EMPTY;
+ }
+
+ XS_VERSION_BOOTCHECK;
+
+ /* register API */
+ for (i = 0; NULL != api[i].f; ++i)
+ Perl_newXS (perl, api[i].name, api[i].f, file);
+
+ stash = Perl_gv_stashpv (perl, "Collectd", 1);
+
+ /* export "constants" */
+ for (i = 0; NULL != consts[i].value; ++i)
+ Perl_newCONSTSUB (perl, stash, consts[i].name, consts[i].value);
+ XSRETURN_YES;
+} /* static XS (boot_Collectd) */
+
+
+/*
+ * Interface to collectd.
+ */
+
+static int perl_config (const char *key, const char *value)
+{
+ log_debug ("perl_config: key = \"%s\", value=\"%s\"", key, value);
+
+ if (0 == strcasecmp (key, "LoadPlugin")) {
+ log_debug ("perl_config: loading perl plugin \"%s\"", value);
+
+ Perl_load_module (perl, PERL_LOADMOD_NOIMPORT,
+ Perl_newSVpvf (perl, "Collectd::plugin::%s", value),
+ Nullsv);
+ }
+ else {
+ return -1;
+ }
+ return 0;
+} /* static int perl_config (char *, char *) */
+
+static int perl_init (void)
+{
+ PERL_SET_CONTEXT (perl);
+ return pplugin_call_all (PLUGIN_INIT);
+} /* static int perl_init (void) */
+
+static int perl_read (void)
+{
+ PERL_SET_CONTEXT (perl);
+ return pplugin_call_all (PLUGIN_READ);
+} /* static int perl_read (void) */
+
+static int perl_write (const data_set_t *ds, const value_list_t *vl)
+{
+ PERL_SET_CONTEXT (perl);
+ return pplugin_call_all (PLUGIN_WRITE, ds, vl);
+} /* static int perl_write (const data_set_t *, const value_list_t *) */
+
+static void perl_log (int level, const char *msg)
+{
+ PERL_SET_CONTEXT (perl);
+ pplugin_call_all (PLUGIN_LOG, level, msg);
+ return;
+} /* static void perl_log (int, const char *) */
+
+static int perl_shutdown (void)
+{
+ int i = 0;
+ int ret = 0;
+
+ PERL_SET_CONTEXT (perl);
+ ret = pplugin_call_all (PLUGIN_SHUTDOWN);
+
+ for (i = 0; i < PLUGIN_TYPES; ++i) {
+ if (0 < Perl_hv_iterinit (perl, plugins[i])) {
+ char *k = NULL;
+ I32 l = 0;
+
+ while (NULL != Perl_hv_iternextsv (perl, plugins[i], &k, &l)) {
+ pplugin_unregister (i, k);
+ }
+ }
+
+ Perl_hv_undef (perl, plugins[i]);
+ }
+
+ if (0 < Perl_hv_iterinit (perl, data_sets)) {
+ char *k = NULL;
+ I32 l = 0;
+
+ while (NULL != Perl_hv_iternextsv (perl, data_sets, &k, &l)) {
+ pplugin_unregister_data_set (k);
+ }
+ }
+
+ Perl_hv_undef (perl, data_sets);
+
+#if COLLECT_DEBUG
+ Perl_sv_report_used (perl);
+#endif /* COLLECT_DEBUG */
+
+ perl_destruct (perl);
+ perl_free (perl);
+
+ PERL_SYS_TERM ();
+ return ret;
+} /* static void perl_shutdown (void) */
+
+static void xs_init (pTHX)
+{
+ char *file = __FILE__;
+
+ dXSUB_SYS;
+
+ /* build the Collectd module into the perl interpreter */
+ Perl_newXS (perl, "Collectd::bootstrap", boot_Collectd, file);
+
+ /* enable usage of Perl modules using shared libraries */
+ Perl_newXS (perl, "DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+ return;
+} /* static void xs_init (pTHX) */
+
+/*
+ * Create the perl interpreter and register it with collectd.
+ */
+void module_register (void)
+{
+ char *embed_argv[] = { "", "-e", "bootstrap Collectd \""VERSION"\"", NULL };
+ int embed_argc = 3;
+
+ int i = 0;
+
+ log_debug ("module_register: Registering perl plugin...");
+
+ PERL_SYS_INIT3 (&argc, &argv, &environ);
+
+ if (NULL == (perl = perl_alloc ())) {
+ log_err ("module_register: Not enough memory.");
+ exit (3);
+ }
+ perl_construct (perl);
+
+ PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
+
+ if (0 != perl_parse (perl, xs_init, embed_argc, embed_argv, NULL)) {
+ log_err ("module_register: Unable to bootstrap Collectd.");
+ exit (1);
+ }
+ perl_run (perl);
+
+ for (i = 0; i < PLUGIN_TYPES; ++i)
+ plugins[i] = Perl_newHV (perl);
+
+ data_sets = Perl_newHV (perl);
+
+ plugin_register_log ("perl", perl_log);
+ plugin_register_config ("perl", perl_config, config_keys, config_keys_num);
+ plugin_register_init ("perl", perl_init);
+ plugin_register_read ("perl", perl_read);
+ plugin_register_write ("perl", perl_write);
+ plugin_register_shutdown ("perl", perl_shutdown);
+ return;
+} /* void module_register (void) */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
diff --git a/src/plugin.c b/src/plugin.c
index c927522afe14f8940cdc08a1ec1a7015a8f7897e..33636507b1656c9c6d30692bb50f9c0b300ba318 100644 (file)
--- a/src/plugin.c
+++ b/src/plugin.c
if (status != 0)
{
+ if (rf->wait_time < interval_g)
+ rf->wait_time = interval_g;
rf->wait_left = rf->wait_time;
rf->wait_time = rf->wait_time * 2;
if (rf->wait_time > 86400)
{
ERROR ("Initialization of plugin `%s' "
"failed with status %i. "
- "Plugin will be unloaded. TODO!",
+ "Plugin will be unloaded.",
le->key, status);
+ /* FIXME: Unload _all_ functions */
plugin_unregister_read (le->key);
}
diff --git a/src/utils_dns.c b/src/utils_dns.c
index 6541b897ffdd68da466472269998b3d4b893d549..0943727536e92e56b384a0b7ebc9542464957724 100644 (file)
--- a/src/utils_dns.c
+++ b/src/utils_dns.c
#if HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
-#if HAVE_PCAP_H
-# include <pcap.h>
-#endif
#if HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
#if HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h>
# include <net/if_ppp.h>
#endif
-#if HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-#endif
#if HAVE_NETDB_H
# include <netdb.h>
#endif
# include <netinet/udp.h>
#endif
+#if HAVE_PCAP_H
+# include <pcap.h>
+#endif
+
#define PCAP_SNAPLEN 1460
#ifndef ETHER_HDR_LEN
#define ETHER_ADDR_LEN 6