Merge branch 'pull/collectd-4' into collectd-4
authorFlorian Forster <octo@huhu.verplant.org>
Mon, 16 Apr 2007 12:53:12 +0000 (14:53 +0200)
committerFlorian Forster <octo@huhu.verplant.org>
Mon, 16 Apr 2007 12:53:12 +0000 (14:53 +0200)
16 files changed:
AUTHORS
ChangeLog
README
configure.in
contrib/README
contrib/extractDS.px [new file with mode: 0755]
contrib/migrate-3-4.px [new file with mode: 0755]
contrib/sles10.1/collectd.spec [new file with mode: 0644]
contrib/sles10.1/init.d-collectd [new file with mode: 0755]
debian/changelog
src/Makefile.am
src/collectd.conf.in
src/collectd.pod
src/perl.c [new file with mode: 0644]
src/plugin.c
src/utils_dns.c

diff --git a/AUTHORS b/AUTHORS
index 2539108..0c1b322 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -19,6 +19,9 @@ mbmon plugin by:
 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>
 
index 6629ab0..b3aaed8 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'.
diff --git a/README b/README
index 4a09fee..8d6c7ed 100644 (file)
--- a/README
+++ b/README
@@ -45,9 +45,24 @@ Features
     - 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)
 
@@ -70,6 +85,9 @@ Features
     - 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)
 
@@ -88,9 +106,6 @@ Features
     - Tape
       (Read and write bytes and operations on tape devices)
 
-    - Traffic
-      (In/Outbound traffic on the interfaces)
-
     - Users
       (Currently logged in users)
 
@@ -100,6 +115,9 @@ Features
     - 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.
@@ -116,13 +134,13 @@ Operation
     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.
@@ -131,8 +149,8 @@ Operation
     (`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
@@ -142,33 +160,49 @@ 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
index 7a21b07..91d13e8 100644 (file)
@@ -1073,6 +1073,57 @@ AC_DEFINE_UNQUOTED(COLLECT_LIBPCAP, [$collect_libpcap],
        [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"
@@ -1237,6 +1288,7 @@ AC_COLLECTD([network],   [disable], [module], [network functionality])
 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])
@@ -1252,6 +1304,11 @@ AC_COLLECTD([wireless],  [disable], [module], [wireless link 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:
@@ -1263,6 +1320,7 @@ Configuration:
     libmysql  . . . . . $with_libmysql
     liboping  . . . . . $with_liboping
     libpcap . . . . . . $with_libpcap
+    libperl . . . . . . $with_libperl
     libpthread  . . . . $with_libpthread
     librrd  . . . . . . $with_rrdtool
     libsensors  . . . . $with_lm_sensors
@@ -1301,6 +1359,7 @@ Configuration:
     nfs . . . . . . . . $enable_nfs
     ntpd  . . . . . . . $enable_ntpd
     nut . . . . . . . . $enable_nut
+    perl  . . . . . . . $enable_perl
     ping  . . . . . . . $enable_ping
     processes . . . . . $enable_processes
     sensors . . . . . . $enable_sensors
index 7f21040..ab3c978 100644 (file)
@@ -2,11 +2,16 @@ The files in this directory may be used to perform common tasks that aren't
 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.
@@ -17,15 +22,28 @@ collectd2html.pl
 `/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
new file mode 100755 (executable)
index 0000000..5826c65
--- /dev/null
@@ -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
new file mode 100755 (executable)
index 0000000..b656f7f
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..2d558bd
--- /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
new file mode 100755 (executable)
index 0000000..edc415e
--- /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
index fead672..8d04e16 100644 (file)
@@ -1,3 +1,9 @@
+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.
index 23d82aa..1016bfe 100644 (file)
@@ -396,6 +396,20 @@ collectd_LDADD += "-dlopen" nut.la
 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
index 020bdeb..3aa1306 100644 (file)
@@ -37,6 +37,7 @@
 @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>
index bc65f57..0852490 100644 (file)
@@ -266,6 +266,132 @@ C<Qcache_*> are put in F<mysql_qcache.rrd> and values of C<Threads_*> are put
 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
new file mode 100644 (file)
index 0000000..0c5e882
--- /dev/null
@@ -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 : */
+
index c927522..3363650 100644 (file)
@@ -186,6 +186,8 @@ static void *plugin_read_thread (void *args)
 
                        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)
@@ -500,8 +502,9 @@ void plugin_init_all (void)
                {
                        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);
                }
 
index 6541b89..0943727 100644 (file)
 #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>
@@ -67,9 +67,6 @@
 # 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