Merge branch 'collectd-4.1' into collectd-4.2
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Thu, 10 Jan 2008 08:48:52 +0000 (09:48 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Thu, 10 Jan 2008 08:48:52 +0000 (09:48 +0100)
39 files changed:
AUTHORS
ChangeLog
Makefile.am
README
bindings/Makefile.am [new file with mode: 0644]
bindings/perl/Collectd.pm [new file with mode: 0644]
bindings/perl/Collectd/Makefile.PL [new file with mode: 0644]
bindings/perl/Collectd/Unixsock.pm [new file with mode: 0644]
bindings/perl/Makefile.PL [new file with mode: 0644]
configure.in
contrib/PerlLib/Collectd.pm [deleted file]
contrib/PerlLib/Collectd/Unixsock.pm [deleted file]
contrib/README
contrib/collection.cgi
contrib/exec-smartctl [new file with mode: 0755]
contrib/fedora/collectd.spec
contrib/snmp-data.conf [new file with mode: 0644]
src/Makefile.am
src/collectd-exec.pod
src/collectd-perl.pod
src/collectd-snmp.pod
src/collectd-unixsock.pod
src/collectd.conf.in
src/collectd.conf.pod
src/configfile.c
src/email.c
src/ipvs.c [new file with mode: 0644]
src/logfile.c
src/memcached.c [new file with mode: 0644]
src/nginx.c [new file with mode: 0644]
src/perl.c
src/rrdtool.c
src/snmp.c
src/tcpconns.c [new file with mode: 0644]
src/types.db
src/utils_avltree.c
src/utils_llist.c
src/utils_mount.c
version-gen.sh

diff --git a/AUTHORS b/AUTHORS
index 0c1b322..ee7e392 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -16,6 +16,9 @@ iptables module by:
 mbmon plugin by:
   Flavio Stanchina <flavio at stanchina.net>
 
+memcached plugin by:
+  Antony Dovgal <tony at daylessday.org>
+
 nfs module by:
   Jason Pepas <cell at ices.utexas.edu>
 
index 74a13e0..618f22c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,47 @@
+2007-12-28, Version 4.2.3
+       * sensors plugin: Updated the plugin to build and work with version 3
+         of the libsensors library.
+
+2007-12-15, Version 4.2.2
+       * nginx plugin: Incorrect comparison of strings lead to a segfault
+         when using the plugin. Thanks to Saulius Grigaliunas for fixing
+         this.
+       * logfile plugin: The config option `Timestamp' was handled
+         incorrectly and basically always active. Thanks to Luke Heberling
+         for fixing this.
+
+2007-11-08, Version 4.2.1
+       * tcpconns plugin: Don't complain about a missing file if IPv6 is not
+         enabled on the host.
+       * snmp plugin: Fix a memory leak.
+
+2007-10-27, Version 4.2.0
+       * collectd: The new config option `Include' lets you include other
+         configfiles and thus split up your config into smaller parts. This
+         may be especially interesting for the snmp plugin to keep the data
+         definitions separate from the host definitions.
+       * ipvs plugin: The new `ipvs' plugin collects IPVS connection statistics
+         (number of connections, octets and packets for each service and
+         destination). Thanks to Sebastian Harl for this plugin.
+       * memcached plugin: The new `memcached' plugin connects to a memcached
+         daemon process and collects statistics of this distributed caching
+         system. Thanks to Antony Dovgal for contributing this plugin.
+       * nginx plugin: The new `nginx' plugin reads the status page of an
+         nginx daemon and saves the handled connections and requests.
+       * perl plugin: Many changes, including the added `EnableDebugger'
+         config option which lets you debug your Perl plugins more easily.
+       * rrdtool plugin: Use the thread-safe RRD-library if available. Try to
+         be more thread-safe otherwise by locking calls to the library.
+       * snmp plugin: Added the options `Scale' and `Shift' to Data-blocks to
+         correct the values returned by SNMP-agents. If a <data> block is
+         defined as `table' the instance is now optional. The sequence number
+         is used as the type-instance in this case. The new `InstancePrefix'
+         option allows to add arbitrary prefixes to the type-instance.
+       * tcpconns plugin: The new `tcpconns' plugin collects the number of
+         certain TCP connections and what state they're in. This can be used
+         to see how many connections your FTP server has to handle or how
+         many outgoing connections your mailserver has open.
+
 2007-12-27, Version 4.1.5
        * rrdtool plugin: Fix a memory leak that only occured in very-low-
          memory situations.
@@ -17,8 +61,8 @@
 2007-10-24, Version 4.1.3
        * collectd: A build issue under Solaris has been resolved by renaming
          data types.
-       * rrdtool plugin: Use the threadsafe RRD-library if available. Try to
-         be more threadsafe otherwise by locking calls to the library.
+       * rrdtool plugin: Use the thread-safe RRD-library if available. Try to
+         be more thread-safe otherwise by locking calls to the library.
 
 2007-09-28, Version 4.1.2
        * apcups plugin: Fix reporting of the `load percent' data.
        * The sample config file has been improved.
        * Errors in the manpages have been corrected.
        * The ping-plugin now adds hosts during initialization, not during
-         startup. This speeds up starup when no network connectivity is
+         startup. This speeds up startup when no network connectivity is
          available. Also, the hosts are being added later when the network is
          available.
        * Improved BSD-support for the df-plugin.
        * A plugin to monitor APC UPSes using `apcupsd' has been added. Thanks
          to Anthony Gialluca for contributing this plugin and providing me
          with a test environment :)
-       * A plugin for moniroting an NTP instance and the local clock drift
+       * A plugin for monitoring an NTP instance and the local clock drift
          has been added.
 
 2006-06-25, Version 3.9.4
        * A plugin for wireless LAN cards has been added. It monitors signal
          strength, link quality and noise ratio..
        * A plugin for Apple hardware sensors has been added.
-       * An option to compile collectd with different `step' and `hearbeat'
+       * An option to compile collectd with different `step' and `heartbeat'
          settings has been added. The size of RRAs is no longer static but
          calculated based on the settings for `step' and `width'.
        * The `ping' plugin can now be configured to use a certain TTL.
          `/etc/mtab'.
 
 2006-03-13, Version 3.8.1
-       * Fixes for building collectd unter FreeBSD, Mac OS X and Solaris.
+       * Fixes for building collectd under FreeBSD, Mac OS X and Solaris.
        * Fixes in the debian `postinst' and `init.d' scripts.
 
 2006-03-09, Version 3.8.0
 
 2006-02-18, Version 3.7.2
        * A simple bug in the `battery' plugin has been fixed. It should now
-         work with ACPI based batteries as well. Thankt to Sebastian for
+         work with ACPI based batteries as well. Thanks to Sebastian for
          fixing this.
        * Fixing a bug that prevented collectd to be built without librrd.
          Thanks to Werner Heuser for reporting it.
        * A `df' plugin has been added.
        * A `mysql' plugin has been added.
        * The `ping' plugin doesn't entirely give up hope when a socket error
-         occured, but will back of and increase the intervals between tries.
+         occurred, but will back of and increase the intervals between tries.
 
 2006-01-21, Version 3.5.2
        * Fixed yet another bug in the signal handling.. Stupid typo..
          - `serial', for monitoring traffic on the serial interfaces
          - `nfs', for graphing NFS procedure calls
          - `tape', traffic from/to tape devices
-       * The the memory.rrd now accepts more than 4Gig of memory.
+       * The memory.rrd now accepts more than 4Gig of memory.
 
 2005-10-26, Version 3.2.0 (Revision 200)
        * Support for graphing the processes has been added (thanks to Lyonel
          Vincent)
-       * If reading from hddtemp failes collectd will increase the time
+       * If reading from hddtemp fails collectd will increase the time
          between polls up to one day.
        * The init.d files have been improved.
        * Problems with the spec file have been fixed.
 2005-09-09, Version 2.0.0 (Revision 135)
        * Filenames can no longer be configured at program startup. The only
          options as of this version are the directory and ping hosts.
-       * CPU statistics now include Wait-IO. If privided under Linux IRQ and
+       * CPU statistics now include Wait-IO. If provided under Linux IRQ and
          Soft-IRQ statistics are added to `System'. 
        * Diskstats now collect read and write bytes, not sectors.
        * Ping statistics can now be collected for more than one host. There
 2005-09-01, Version 1.8.1 (Revision 123)
        * Much improved configure-script: libraries and features may now be
          disabled.
-       * More detailed warnings/error messages when RRD update failes.
+       * More detailed warnings/error messages when RRD update fails.
 
 2005-08-29, Version 1.8.0:
        * Support for collecting disk statistics under Solaris.
          libstatgrab returns insufficient information. I will contact the
          authors.
        * Improved the CPU-initialization code for Solaris. Apparently CPUs
-         aren't neccessarily counted linear which is now handled correctly.
+         aren't necessarily counted linear which is now handled correctly.
        [1]: http://www.i-scream.org/libstatgrab/
 
 2005-08-21, Version 1.6.0:
        * Basic support for Solaris: System load and cpu-usage can be
-         collected unter Solaris, too. Other stats will follow later.
+         collected under Solaris, too. Other stats will follow later.
        * Many fixes in the autoconf-script
-       * Collection/Museum scripts have been added unter contrib/museum
+       * Collection/Museum scripts have been added under contrib/museum
        * collectd may now be started in unprivileged mode, though ping
          statistics will not work.
 
          about the disks and partitions.
 
 2005-07-11, Version 1.4.2:
-       * The meminfo mofule has been changed to work with more platforms
+       * The meminfo module has been changed to work with more platforms
          and/or kernel versions.
 
 2005-07-10, Version 1.4.1: Correct traffic stats
        * The default directory is now /var/db/collectd
 
 2004-07-10, Version 1.1: Minor changes
-       * Nothing really usefull to say ;)
+       * Nothing really useful to say ;)
 
 2004-07-09, Version 1.0: Initial Version
        * The following modules are provided:
index 34c36cc..9a96eb2 100644 (file)
@@ -1,8 +1,8 @@
-SUBDIRS = libltdl src
+SUBDIRS = libltdl src bindings
 
 INCLUDES = $(LTDLINCL)
 
-EXTRA_DIST = contrib
+EXTRA_DIST = contrib version-gen.sh
 
 dist-hook:
        find $(distdir) -type d -name '.svn' | xargs rm -rf
diff --git a/README b/README
index 37eee3e..2406154 100644 (file)
--- a/README
+++ b/README
@@ -69,6 +69,11 @@ Features
       Iptables' counters: Number of bytes that were matched by a certain
       iptables rule.
 
+    - ipvs
+      IPVS connection statistics (number of connections, octets and packets
+      for each service and destination).
+      See http://www.linuxvirtualserver.org/software/index.html.
+
     - irq
       IRQ counters: Frequency in which certain interrupts occur.
 
@@ -79,6 +84,10 @@ Features
       Motherboard sensors: temperature, fanspeed and voltage information,
       using mbmon(1).
 
+    - memcached
+      Statistics of the memcached distributed caching system.
+      <http://www.danga.com/memcached/>
+
     - memory
       Memory utilization: Memory occupied by running processes, page cache,
       buffer cache and free.
@@ -105,6 +114,10 @@ Features
       NFS Procedures: Which NFS command were called how often. Only NFSv2 and
       NFSv3 right now.
 
+    - nginx
+      Collects statistics from `nginx' (speak: engine X), a HTTP and mail
+      server/proxy.
+
     - ntp
       NTP daemon statistics: Local clock drift, offset to peers, etc.
 
@@ -145,6 +158,9 @@ Features
     - tape
       Bytes and operations read and written on tape devices. Solaris only.
 
+    - tcpconns
+      Number of TCP connections to specific local and remote ports.
+
     - users
       Users currently logged in.
 
@@ -261,16 +277,19 @@ Prerequisites
     platforms.
 
   * libcurl (optional)
-    If you want to use the `apache' plugin
+    If you want to use the `apache' and/or `nginx' plugins.
 
   * libiptc (optional)
     For querying iptables counters.
 
   * libmysqlclient (optional)
+    Unsurprisingly used by the `mysql' plugin.
 
   * libnetlink (optional)
+    Used, obviously, for the `netlink' plugin.
 
   * libnetsnmp (optional)
+    For the `snmp' plugin.
 
   * liboping (optional, if not found a version shipped with this distribution
     can be used)
@@ -286,7 +305,7 @@ Prerequisites
     instead.
 
   * libsensors (optional)
-    To read from `lm_sensors'.
+    To read from `lm_sensors', see the `sensors' plugin.
 
   * libstatgrab may be used to collect statistics on systems other than Linux
     and/or Solaris. Note that CPU- and disk-statistics, while being provided
diff --git a/bindings/Makefile.am b/bindings/Makefile.am
new file mode 100644 (file)
index 0000000..c724725
--- /dev/null
@@ -0,0 +1,28 @@
+EXTRA_DIST = perl/Collectd.pm perl/Makefile.PL perl/Collectd/Makefile.PL perl/Collectd/Unixsock.pm
+
+all-local: @PERL_BINDINGS@
+
+install-exec-local:
+       [ ! -f perl/Makefile ] || ( cd perl && $(MAKE) install )
+
+clean-local:
+       [ ! -f perl/Makefile ] || ( cd perl && $(MAKE) realclean )
+
+perl: perl/Makefile
+       cd perl && $(MAKE)
+
+perl/Makefile: .perl-directory-stamp perl/Makefile.PL perl/Collectd/Makefile.PL
+       cd perl && @PERL@ Makefile.PL PREFIX=$(prefix) @PERL_BINDINGS_OPTIONS@
+
+.perl-directory-stamp:
+       if test ! -d perl; then \
+         mkdir -p perl/Collectd; \
+         cp $(srcdir)/perl/Collectd.pm perl/; \
+         cp $(srcdir)/perl/Makefile.PL perl/; \
+         cp $(srcdir)/perl/Collectd/Unixsock.pm perl/Collectd/; \
+         cp $(srcdir)/perl/Collectd/Makefile.PL perl/Collectd/; \
+       fi
+       touch $@
+
+.PHONY: perl
+
diff --git a/bindings/perl/Collectd.pm b/bindings/perl/Collectd.pm
new file mode 100644 (file)
index 0000000..fd5632a
--- /dev/null
@@ -0,0 +1,231 @@
+# collectd - Collectd.pm
+# 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>
+
+package Collectd;
+
+use strict;
+use warnings;
+
+require Exporter;
+
+our @ISA = qw( Exporter );
+
+our %EXPORT_TAGS = (
+       'plugin' => [ qw(
+                       plugin_register
+                       plugin_unregister
+                       plugin_dispatch_values
+                       plugin_log
+       ) ],
+       'types' => [ qw(
+                       TYPE_INIT
+                       TYPE_READ
+                       TYPE_WRITE
+                       TYPE_SHUTDOWN
+                       TYPE_LOG
+                       TYPE_DATASET
+       ) ],
+       'ds_types' => [ qw(
+                       DS_TYPE_COUNTER
+                       DS_TYPE_GAUGE
+       ) ],
+       'log' => [ qw(
+                       ERROR
+                       WARNING
+                       NOTICE
+                       INFO
+                       DEBUG
+                       LOG_ERR
+                       LOG_WARNING
+                       LOG_NOTICE
+                       LOG_INFO
+                       LOG_DEBUG
+       ) ],
+);
+
+{
+       my %seen;
+       push @{$EXPORT_TAGS{'all'}}, grep {! $seen{$_}++ } @{$EXPORT_TAGS{$_}}
+               foreach keys %EXPORT_TAGS;
+}
+
+Exporter::export_ok_tags ('all');
+
+my @plugins  = ();
+my @datasets = ();
+
+my %types = (
+       TYPE_INIT,     "init",
+       TYPE_READ,     "read",
+       TYPE_WRITE,    "write",
+       TYPE_SHUTDOWN, "shutdown",
+       TYPE_LOG,      "log"
+);
+
+foreach my $type (keys %types) {
+       $plugins[$type] = {};
+}
+
+sub _log {
+       my $caller = shift;
+       my $lvl    = shift;
+       my $msg    = shift;
+
+       if ("Collectd" eq $caller) {
+               $msg = "perl: $msg";
+       }
+       return plugin_log ($lvl, $msg);
+}
+
+sub ERROR   { _log (scalar caller, LOG_ERR,     shift); }
+sub WARNING { _log (scalar caller, LOG_WARNING, shift); }
+sub NOTICE  { _log (scalar caller, LOG_NOTICE,  shift); }
+sub INFO    { _log (scalar caller, LOG_INFO,    shift); }
+sub DEBUG   { _log (scalar caller, LOG_DEBUG,   shift); }
+
+sub plugin_call_all {
+       my $type = shift;
+
+       if (! defined $type) {
+               return;
+       }
+
+       if (TYPE_LOG != $type) {
+               DEBUG ("Collectd::plugin_call: type = \"$type\", args=\"@_\"");
+       }
+
+       if (! defined $plugins[$type]) {
+               ERROR ("Collectd::plugin_call: unknown type \"$type\"");
+               return;
+       }
+
+       foreach my $plugin (keys %{$plugins[$type]}) {
+               my $p = $plugins[$type]->{$plugin};
+
+               if ($p->{'wait_left'} > 0) {
+                       # TODO: use interval_g
+                       $p->{'wait_left'} -= 10;
+               }
+
+               next if ($p->{'wait_left'} > 0);
+
+               if (my $status = $p->{'code'}->(@_)) {
+                       $p->{'wait_left'} = 0;
+                       $p->{'wait_time'} = 10;
+               }
+               elsif (TYPE_READ == $type) {
+                       $p->{'wait_left'} = $p->{'wait_time'};
+                       $p->{'wait_time'} *= 2;
+
+                       if ($p->{'wait_time'} > 86400) {
+                               $p->{'wait_time'} = 86400;
+                       }
+
+                       WARNING ("${plugin}->read() failed with status $status. "
+                               . "Will suspend it for $p->{'wait_left'} seconds.");
+               }
+               elsif (TYPE_INIT == $type) {
+                       foreach my $type (keys %types) {
+                               plugin_unregister ($type, $plugin);
+                       }
+
+                       ERROR ("${plugin}->init() failed with status $status. "
+                               . "Plugin will be disabled.");
+               }
+               elsif (TYPE_LOG != $type) {
+                       WARNING ("${plugin}->$types{$type}() failed with status $status.");
+               }
+       }
+       return 1;
+}
+
+# Collectd::plugin_register (type, name, data).
+#
+# type:
+#   init, read, write, shutdown, data set
+#
+# name:
+#   name of the plugin
+#
+# data:
+#   reference to the plugin's subroutine that does the work or the data set
+#   definition
+sub plugin_register {
+       my $type = shift;
+       my $name = shift;
+       my $data = shift;
+
+       DEBUG ("Collectd::plugin_register: "
+               . "type = \"$type\", name = \"$name\", data = \"$data\"");
+
+       if (! ((defined $type) && (defined $name) && (defined $data))) {
+               ERROR ("Usage: Collectd::plugin_register (type, name, data)");
+               return;
+       }
+
+       if ((! defined $plugins[$type]) && (TYPE_DATASET != $type)) {
+               ERROR ("Collectd::plugin_register: Invalid type \"$type\"");
+               return;
+       }
+
+       if ((TYPE_DATASET == $type) && ("ARRAY" eq ref $data)) {
+               return plugin_register_data_set ($name, $data);
+       }
+       elsif ("CODE" eq ref $data) {
+               # TODO: make interval_g available at configuration time
+               $plugins[$type]->{$name} = {
+                               wait_time => 10,
+                               wait_left => 0,
+                               code      => $data,
+               };
+       }
+       else {
+               ERROR ("Collectd::plugin_register: Invalid data.");
+               return;
+       }
+       return 1;
+}
+
+sub plugin_unregister {
+       my $type = shift;
+       my $name = shift;
+
+       DEBUG ("Collectd::plugin_unregister: type = \"$type\", name = \"$name\"");
+
+       if (! ((defined $type) && (defined $name))) {
+               ERROR ("Usage: Collectd::plugin_unregister (type, name)");
+               return;
+       }
+
+       if (TYPE_DATASET == $type) {
+               return plugin_unregister_data_set ($name);
+       }
+       elsif (defined $plugins[$type]) {
+               delete $plugins[$type]->{$name};
+       }
+       else {
+               ERROR ("Collectd::plugin_unregister: Invalid type.");
+               return;
+       }
+}
+
+1;
+
+# vim: set sw=4 ts=4 tw=78 noexpandtab :
+
diff --git a/bindings/perl/Collectd/Makefile.PL b/bindings/perl/Collectd/Makefile.PL
new file mode 100644 (file)
index 0000000..baf7166
--- /dev/null
@@ -0,0 +1,8 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+       'NAME'          => 'Collectd::Unixsock',
+       'AUTHOR'        => 'Florian Forster <octo@verplant.org>',
+);
+
+# vim: set sw=4 ts=4 tw=78 noexpandtab :
diff --git a/bindings/perl/Collectd/Unixsock.pm b/bindings/perl/Collectd/Unixsock.pm
new file mode 100644 (file)
index 0000000..3b8a91c
--- /dev/null
@@ -0,0 +1,322 @@
+package Collectd::Unixsock;
+
+=head1 NAME
+
+Collectd::Unixsock - Abstraction layer for accessing the functionality by collectd's unixsock plugin.
+
+=head1 SYNOPSIS
+
+  use Collectd::Unixsock ();
+
+  my $sock = Collectd::Unixsock->new ($path);
+
+  my $value = $sock->getval (%identifier);
+  $sock->putval (%identifier,
+                 time => time (),
+                values => [123, 234, 345]);
+
+  $sock->destroy ();
+
+=head1 DESCRIPTION
+
+collectd's unixsock plugin allows external programs to access the values it has
+collected or received and to submit own values. This Perl-module is simply a
+little abstraction layer over this interface to make it even easier for
+programmers to interact with the daemon.
+
+=cut
+
+use strict;
+use warnings;
+
+use Carp (qw(cluck confess));
+use IO::Socket::UNIX;
+use Regexp::Common (qw(number));
+
+return (1);
+
+sub _create_socket
+{
+       my $path = shift;
+       my $sock = IO::Socket::UNIX->new (Type => SOCK_STREAM, Peer => $path);
+       if (!$sock)
+       {
+               cluck ("Cannot open UNIX-socket $path: $!");
+               return;
+       }
+       return ($sock);
+} # _create_socket
+
+=head1 VALUE IDENTIFIER
+
+The values in the collectd are identified using an five-tupel (host, plugin,
+plugin-instance, type, type-instance) where only plugin-instance and
+type-instance may be NULL (or undefined). Many functions expect an
+I<%identifier> hash that has at least the members B<host>, B<plugin>, and
+B<type>, possibly completed by B<plugin_instance> and B<type_instance>.
+
+Usually you can pass this hash as follows:
+
+  $obj->method (host => $host, plugin => $plugin, type => $type, %other_args);
+
+=cut
+
+sub _create_identifier
+{
+       my $args = shift;
+       my $host;
+       my $plugin;
+       my $type;
+
+       if (!$args->{'host'} || !$args->{'plugin'} || !$args->{'type'})
+       {
+               cluck ("Need `host', `plugin' and `type'");
+               return;
+       }
+
+       $host = $args->{'host'};
+       $plugin = $args->{'plugin'};
+       $plugin .= '-' . $args->{'plugin_instance'} if (defined ($args->{'plugin_instance'}));
+       $type = $args->{'type'};
+       $type .= '-' . $args->{'type_instance'} if (defined ($args->{'type_instance'}));
+
+       return ("$host/$plugin/$type");
+} # _create_identifier
+
+sub _parse_identifier
+{
+       my $string = shift;
+       my $host;
+       my $plugin;
+       my $plugin_instance;
+       my $type;
+       my $type_instance;
+       my $ident;
+
+       ($host, $plugin, $type) = split ('/', $string);
+
+       ($plugin, $plugin_instance) = split ('-', $plugin, 2);
+       ($type, $type_instance) = split ('-', $type, 2);
+
+       $ident =
+       {
+               host => $host,
+               plugin => $plugin,
+               type => $type
+       };
+       $ident->{'plugin_instance'} = $plugin_instance if (defined ($plugin_instance));
+       $ident->{'type_instance'} = $type_instance if (defined ($type_instance));
+
+       return ($ident);
+} # _parse_identifier
+
+=head1 PUBLIC METHODS
+
+=over 4
+
+=item I<$obj> = Collectd::Unixsock->B<new> ([I<$path>]);
+
+Creates a new connection to the daemon. The optional I<$path> argument gives
+the path to the UNIX socket of the C<unixsock plugin> and defaults to
+F</var/run/collectd-unixsock>. Returns the newly created object on success and
+false on error.
+
+=cut
+
+sub new
+{
+       my $pkg = shift;
+       my $path = @_ ? shift : '/var/run/collectd-unixsock';
+       my $sock = _create_socket ($path) or return;
+       my $obj = bless (
+               {
+                       path => $path,
+                       sock => $sock,
+                       error => 'No error'
+               }, $pkg);
+       return ($obj);
+} # new
+
+=item I<$res> = I<$obj>-E<gt>B<getval> (I<%identifier>);
+
+Requests a value-list from the daemon. On success a hash-ref is returned with
+the name of each data-source as the key and the according value as, well, the
+value. On error false is returned.
+
+=cut
+
+sub getval
+{
+       my $obj = shift;
+       my %args = @_;
+
+       my $status;
+       my $fh = $obj->{'sock'} or confess;
+       my $msg;
+       my $identifier;
+
+       my $ret = {};
+
+       $identifier = _create_identifier (\%args) or return;
+
+       $msg = "GETVAL $identifier\n";
+       #print "-> $msg";
+       send ($fh, $msg, 0) or confess ("send: $!");
+
+       $msg = undef;
+       recv ($fh, $msg, 1024, 0) or confess ("recv: $!");
+       #print "<- $msg";
+
+       ($status, $msg) = split (' ', $msg, 2);
+       if ($status <= 0)
+       {
+               $obj->{'error'} = $msg;
+               return;
+       }
+
+       for (split (' ', $msg))
+       {
+               my $entry = $_;
+               if ($entry =~ m/^(\w+)=NaN$/)
+               {
+                       $ret->{$1} = undef;
+               }
+               elsif ($entry =~ m/^(\w+)=($RE{num}{real})$/)
+               {
+                       $ret->{$1} = 0.0 + $2;
+               }
+       }
+
+       return ($ret);
+} # getval
+
+=item I<$obj>-E<gt>B<putval> (I<%identifier>, B<time> => I<$time>, B<values> => [...]);
+
+Submits a value-list to the daemon. If the B<time> argument is omitted
+C<time()> is used. The requierd argument B<values> is a reference to an array
+of values that is to be submitted. The number of values must match the number
+of values expected for the given B<type> (see L<VALUE IDENTIFIER>), though this
+is checked by the daemon, not the Perl module. Also, gauge data-sources
+(e.E<nbsp>g. system-load) may be C<undef>. Returns true upon success and false
+otherwise.
+
+=cut
+
+sub putval
+{
+       my $obj = shift;
+       my %args = @_;
+
+       my $status;
+       my $fh = $obj->{'sock'} or confess;
+       my $msg;
+       my $identifier;
+       my $values;
+
+       $identifier = _create_identifier (\%args) or return;
+       if (!$args{'values'})
+       {
+               cluck ("Need argument `values'");
+               return;
+       }
+
+       if (!ref ($args{'values'}))
+       {
+               $values = $args{'values'};
+       }
+       else
+       {
+               my $time = $args{'time'} ? $args{'time'} : time ();
+               $values = join (':', $time, map { defined ($_) ? $_ : 'U' } (@{$args{'values'}}));
+       }
+
+       $msg = "PUTVAL $identifier $values\n";
+       #print "-> $msg";
+       send ($fh, $msg, 0) or confess ("send: $!");
+       $msg = undef;
+       recv ($fh, $msg, 1024, 0) or confess ("recv: $!");
+       #print "<- $msg";
+
+       ($status, $msg) = split (' ', $msg, 2);
+       return (1) if ($status == 0);
+
+       $obj->{'error'} = $msg;
+       return;
+} # putval
+
+=item I<$res> = I<$obj>-E<gt>B<listval> ()
+
+Queries a list of values from the daemon. The list is returned as an array of
+hash references, where each hash reference is a valid identifier. The C<time>
+member of each hash holds the epoch value of the last update of that value.
+
+=cut
+
+sub listval
+{
+       my $obj = shift;
+       my $msg;
+       my @ret = ();
+       my $status;
+       my $fh = $obj->{'sock'} or confess;
+
+       $msg = "LISTVAL\n";
+       send ($fh, $msg, 0) or confess ("send: $!");
+
+       $msg = <$fh>;
+       ($status, $msg) = split (' ', $msg, 2);
+       if ($status < 0)
+       {
+               $obj->{'error'} = $msg;
+               return;
+       }
+
+       for (my $i = 0; $i < $status; $i++)
+       {
+               my $time;
+               my $ident;
+
+               $msg = <$fh>;
+               chomp ($msg);
+
+               ($time, $ident) = split (' ', $msg, 2);
+
+               $ident = _parse_identifier ($ident);
+               $ident->{'time'} = int ($time);
+
+               push (@ret, $ident);
+       } # for (i = 0 .. $status)
+
+       return (@ret);
+} # listval
+
+=item I<$obj>-E<gt>destroy ();
+
+Closes the socket before the object is destroyed. This function is also
+automatically called then the object goes out of scope.
+
+=back
+
+=cut
+
+sub destroy
+{
+       my $obj = shift;
+       if ($obj->{'sock'})
+       {
+               close ($obj->{'sock'});
+               delete ($obj->{'sock'});
+       }
+}
+
+sub DESTROY
+{
+       my $obj = shift;
+       $obj->destroy ();
+}
+
+=head1 AUTHOR
+
+Florian octo Forster E<lt>octo@verplant.orgE<gt>
+
+=cut
diff --git a/bindings/perl/Makefile.PL b/bindings/perl/Makefile.PL
new file mode 100644 (file)
index 0000000..f2ef2fd
--- /dev/null
@@ -0,0 +1,8 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+       'NAME'          => 'Collectd',
+       'AUTHOR'        => 'Sebastian Harl <sh@tokkee.org>',
+);
+
+# vim: set sw=4 ts=4 tw=78 noexpandtab :
index 5555c8a..75bc00f 100644 (file)
@@ -271,6 +271,15 @@ AC_CHECK_HEADERS(linux/netdevice.h, [], [],
 #endif
 ])
 
+# For ipvs module
+have_net_ip_vs_h="no"
+have_ip_vs_h="no"
+if test "x$ac_system" = "xLinux"
+then
+       AC_CHECK_HEADERS(net/ip_vs.h, [have_net_ip_vs_h="yes"])
+       AC_CHECK_HEADERS(ip_vs.h, [have_ip_vs_h="yes"])
+fi
+
 # For quota module
 AC_CHECK_HEADERS(sys/ucred.h, [], [],
 [
@@ -346,11 +355,6 @@ have_termios_h="no"
 AC_CHECK_HEADERS(termios.h, [have_termios_h="yes"])
 
 #
-# Checking for libraries
-#
-AC_CHECK_LIB(m, ext)
-
-#
 # Checks for typedefs, structures, and compiler characteristics.
 #
 AC_C_CONST
@@ -921,13 +925,21 @@ then
 fi
 AM_CONDITIONAL(BUILD_WITH_LIBKVM, test "x$with_libkvm" = "xyes")
 
+with_sensors_cflags=""
+with_sensors_ldflags=""
 AC_ARG_WITH(lm-sensors, [AS_HELP_STRING([--with-lm-sensors@<:@=PREFIX@:>@], [Path to lm_sensors.])],
 [
-       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       if test "x$withval" = "xno"
        then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_lm_sensors="no"
+       else
                with_lm_sensors="yes"
+               if test "x$withval" != "xyes"
+               then
+                       with_sensors_cflags="-I$withval/include"
+                       with_sensors_ldflags="-L$withval/lib"
+                       with_lm_sensors="yes"
+               fi
        fi
 ],
 [
@@ -940,28 +952,41 @@ AC_ARG_WITH(lm-sensors, [AS_HELP_STRING([--with-lm-sensors@<:@=PREFIX@:>@], [Pat
 ])
 if test "x$with_lm_sensors" = "xyes"
 then
-       AC_CHECK_LIB(sensors, sensors_init,
-       [
-               AC_DEFINE(HAVE_LIBSENSORS, 1, [Define to 1 if you have the sensors library (-lsensors).])
-       ],
-       [with_lm_sensors="no (libsensors not found)"])
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
+
+#      AC_CHECK_HEADERS(sensors/sensors.h,
+#      [
+#              AC_DEFINE(HAVE_SENSORS_SENSORS_H, 1, [Define to 1 if you have the <sensors/sensors.h> header file.])
+#      ],
+#      [with_lm_sensors="no (sensors/sensors.h not found)"])
+       AC_CHECK_HEADERS(sensors/sensors.h, [], [with_lm_sensors="no (sensors/sensors.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
 fi
 if test "x$with_lm_sensors" = "xyes"
 then
-       AC_CHECK_HEADERS(sensors/sensors.h,
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_sensors_cflags"
+       LDFLAGS="$LDFLAGS $with_sensors_ldflags"
+
+       AC_CHECK_LIB(sensors, sensors_init,
        [
-               AC_DEFINE(HAVE_SENSORS_SENSORS_H, 1, [Define to 1 if you have the <sensors/sensors.h> header file.])
+               AC_DEFINE(HAVE_LIBSENSORS, 1, [Define to 1 if you have the sensors library (-lsensors).])
        ],
-       [with_lm_sensors="no (sensors/sensors.h not found)"])
+       [with_lm_sensors="no (libsensors not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
 fi
 if test "x$with_lm_sensors" = "xyes"
 then
-       collect_lm_sensors=1
-else
-       collect_lm_sensors=0
+       BUILD_WITH_LIBSENSORS_CFLAGS="$with_sensors_cflags"
+       BUILD_WITH_LIBSENSORS_LDFLAGS="$with_sensors_ldflags"
+       AC_SUBST(BUILD_WITH_LIBSENSORS_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBSENSORS_LDFLAGS)
 fi
-AC_DEFINE_UNQUOTED(COLLECT_LM_SENSORS, [$collect_lm_sensors],
-       [Wether or not to use sensors library])
 AM_CONDITIONAL(BUILD_WITH_LM_SENSORS, test "x$with_lm_sensors" = "xyes")
 
 with_mysql_config="mysql_config"
@@ -1176,6 +1201,9 @@ AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to l
 [
        with_libperl="yes"
 ])
+
+AC_SUBST(PERL, "$perl_interpreter")
+
 if test "x$with_libperl" = "xyes"
 then
   SAVE_CFLAGS=$CFLAGS
@@ -1680,6 +1708,7 @@ plugin_df="no"
 plugin_disk="no"
 plugin_entropy="no"
 plugin_interface="no"
+plugin_ipvs="no"
 plugin_irq="no"
 plugin_load="no"
 plugin_memory="no"
@@ -1689,6 +1718,7 @@ plugin_processes="no"
 plugin_serial="no"
 plugin_swap="no"
 plugin_tape="no"
+plugin_tcpconns="no"
 plugin_users="no"
 plugin_vserver="no"
 plugin_wireless="no"
@@ -1709,8 +1739,14 @@ then
        plugin_processes="yes"
        plugin_serial="yes"
        plugin_swap="yes"
+       plugin_tcpconns="yes"
        plugin_vserver="yes"
        plugin_wireless="yes"
+
+       if test "x$have_net_ip_vs_h" = "xyes" -o "x$have_ip_vs_h" = "xyes"
+       then
+               plugin_ipvs="yes"
+       fi
 fi
 
 # Mac OS X devices
@@ -1748,6 +1784,7 @@ if test "x$have_sysctlbyname" = "xyes"
 then
        plugin_cpu="yes"
        plugin_memory="yes"
+       plugin_tcpconns="yes"
 fi
 
 if test "x$have_statfs" = "xyes"
@@ -1820,16 +1857,19 @@ AC_PLUGIN([exec],        [yes],                [Execution of external programs])
 AC_PLUGIN([hddtemp],     [yes],                [Query hddtempd])
 AC_PLUGIN([interface],   [$plugin_interface],  [Interface traffic statistics])
 AC_PLUGIN([iptables],    [$with_libiptc],      [IPTables rule counters])
+AC_PLUGIN([ipvs],        [$plugin_ipvs],       [IPVS connection statistics])
 AC_PLUGIN([irq],         [$plugin_irq],        [IRQ statistics])
 AC_PLUGIN([load],        [$plugin_load],       [System load])
 AC_PLUGIN([logfile],     [yes],                [File logging plugin])
 AC_PLUGIN([mbmon],       [yes],                [Query mbmond])
+AC_PLUGIN([memcached],   [yes],                [memcached statistics])
 AC_PLUGIN([memory],      [$plugin_memory],     [Memory usage])
 AC_PLUGIN([multimeter],  [$plugin_multimeter], [Read multimeter values])
 AC_PLUGIN([mysql],       [$with_libmysql],     [MySQL statistics])
 AC_PLUGIN([netlink],     [$with_libnetlink],   [Enhanced Linux network statistics])
 AC_PLUGIN([network],     [yes],                [Network communication plugin])
 AC_PLUGIN([nfs],         [$plugin_nfs],        [NFS statistics])
+AC_PLUGIN([nginx],       [$with_libcurl],      [nginx statistics])
 AC_PLUGIN([ntpd],        [yes],                [NTPd statistics])
 AC_PLUGIN([nut],         [$with_libupsclient], [Network UPS tools statistics])
 AC_PLUGIN([perl],        [$with_libperl],      [Embed a Perl interpreter])
@@ -1842,13 +1882,42 @@ AC_PLUGIN([snmp],        [$with_libnetsnmp],   [SNMP querying plugin])
 AC_PLUGIN([swap],        [$plugin_swap],       [Swap usage statistics])
 AC_PLUGIN([syslog],      [$have_syslog],       [Syslog logging plugin])
 AC_PLUGIN([tape],        [$plugin_tape],       [Tape drive statistics])
+AC_PLUGIN([tcpconns],    [$plugin_tcpconns],   [TCP connection statistics])
 AC_PLUGIN([unixsock],    [yes],                [Unixsock communication plugin])
 AC_PLUGIN([users],       [$plugin_users],      [User statistics])
 AC_PLUGIN([vserver],     [$plugin_vserver],    [Linux VServer statistics])
 AC_PLUGIN([wireless],    [$plugin_wireless],   [Wireless statistics])
 AC_PLUGIN([xmms],        [$with_libxmms],      [XMMS statistics])
 
-AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/liboconfig/Makefile src/liboping/Makefile)
+dnl ip_vs.h
+if test "x$ac_system" = "xLinux" -a "x$have_net_ip_vs_h$have_ip_vs_h" = "xnono"
+then
+       enable_ipvs="$enable_ipvs (ip_vs.h not found)"
+fi
+
+dnl Perl bindings
+AC_ARG_WITH(perl-bindings, [AS_HELP_STRING([--with-perl-bindings@<:@=OPTIONS@:>@], [Options passed to "perl Makefile.PL".])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               PERL_BINDINGS_OPTIONS="$withval"
+               with_perl_bindings="yes"
+       fi
+],
+[
+       PERL_BINDINGS_OPTIONS=""
+       with_perl_bindings="yes"
+])
+if test "x$with_perl_bindings" = "xyes"
+then
+       PERL_BINDINGS="perl"
+else
+       PERL_BINDINGS=""
+fi
+AC_SUBST(PERL_BINDINGS)
+AC_SUBST(PERL_BINDINGS_OPTIONS)
+
+AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/liboconfig/Makefile src/liboping/Makefile bindings/Makefile)
 
 if test "x$with_liboping" = "xyes" -a "x$with_own_liboping" = "xyes"
 then
@@ -1857,11 +1926,16 @@ fi
 
 if test "x$with_libperl" = "xyes"
 then
-       with_libperl="yes (version `perl -MConfig -e 'print $Config{version};'`)"
+       with_libperl="yes (version `$perl_interpreter -MConfig -e 'print $Config{version};'`)"
 else
        enable_perl="no (needs libperl)"
 fi
 
+if test "x$with_perl_bindings" = "xyes" -a "x$PERL_BINDINGS_OPTIONS" != "x"
+then
+       with_perl_bindings="yes ($PERL_BINDINGS_OPTIONS)"
+fi
+
 cat <<EOF;
 
 Configuration:
@@ -1889,6 +1963,9 @@ Configuration:
     daemon mode . . . . $enable_daemon
     debug . . . . . . . $enable_debug
 
+  Bindings:
+    perl  . . . . . . . $with_perl_bindings
+
   Modules:
     apache  . . . . . . $enable_apache
     apcups  . . . . . . $enable_apcups
@@ -1906,16 +1983,19 @@ Configuration:
     hddtemp . . . . . . $enable_hddtemp
     interface . . . . . $enable_interface
     iptables  . . . . . $enable_iptables
+    ipvs  . . . . . . . $enable_ipvs
     irq . . . . . . . . $enable_irq
     load  . . . . . . . $enable_load
     logfile . . . . . . $enable_logfile
     mbmon . . . . . . . $enable_mbmon
+    memcached . . . . . $enable_memcached
     memory  . . . . . . $enable_memory
     multimeter  . . . . $enable_multimeter
     mysql . . . . . . . $enable_mysql
     netlink . . . . . . $enable_netlink
     network . . . . . . $enable_network
     nfs . . . . . . . . $enable_nfs
+    nginx . . . . . . . $enable_nginx
     ntpd  . . . . . . . $enable_ntpd
     nut . . . . . . . . $enable_nut
     perl  . . . . . . . $enable_perl
@@ -1928,6 +2008,7 @@ Configuration:
     swap  . . . . . . . $enable_swap
     syslog  . . . . . . $enable_syslog
     tape  . . . . . . . $enable_tape
+    tcpconns  . . . . . $enable_tcpconns
     unixsock  . . . . . $enable_unixsock
     users . . . . . . . $enable_users
     vserver . . . . . . $enable_vserver
diff --git a/contrib/PerlLib/Collectd.pm b/contrib/PerlLib/Collectd.pm
deleted file mode 100644 (file)
index 8f6024d..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-# collectd - Collectd.pm
-# 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>
-
-package Collectd;
-
-use strict;
-use warnings;
-
-require Exporter;
-
-our @ISA = qw( Exporter );
-
-our %EXPORT_TAGS = (
-       'funcs'    => [ qw( plugin_register plugin_unregister
-                           plugin_dispatch_values plugin_log ) ],
-       'types'    => [ qw( TYPE_INIT TYPE_READ TYPE_WRITE TYPE_SHUTDOWN TYPE_LOG
-                           TYPE_DATASET ) ],
-       'ds_types' => [ qw( DS_TYPE_COUNTER DS_TYPE_GAUGE ) ],
-       'log'      => [ qw( LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG ) ],
-);
-
-{
-       my %seen;
-
-       push @{$EXPORT_TAGS{'all'}}, grep {! $seen{$_}++ } @{$EXPORT_TAGS{$_}}
-               foreach keys %EXPORT_TAGS;
-}
-
-Exporter::export_ok_tags('all');
-
-bootstrap Collectd "4.1.4";
-
-1;
-
-# vim: set sw=4 ts=4 tw=78 noexpandtab :
-
diff --git a/contrib/PerlLib/Collectd/Unixsock.pm b/contrib/PerlLib/Collectd/Unixsock.pm
deleted file mode 100644 (file)
index 3b8a91c..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-package Collectd::Unixsock;
-
-=head1 NAME
-
-Collectd::Unixsock - Abstraction layer for accessing the functionality by collectd's unixsock plugin.
-
-=head1 SYNOPSIS
-
-  use Collectd::Unixsock ();
-
-  my $sock = Collectd::Unixsock->new ($path);
-
-  my $value = $sock->getval (%identifier);
-  $sock->putval (%identifier,
-                 time => time (),
-                values => [123, 234, 345]);
-
-  $sock->destroy ();
-
-=head1 DESCRIPTION
-
-collectd's unixsock plugin allows external programs to access the values it has
-collected or received and to submit own values. This Perl-module is simply a
-little abstraction layer over this interface to make it even easier for
-programmers to interact with the daemon.
-
-=cut
-
-use strict;
-use warnings;
-
-use Carp (qw(cluck confess));
-use IO::Socket::UNIX;
-use Regexp::Common (qw(number));
-
-return (1);
-
-sub _create_socket
-{
-       my $path = shift;
-       my $sock = IO::Socket::UNIX->new (Type => SOCK_STREAM, Peer => $path);
-       if (!$sock)
-       {
-               cluck ("Cannot open UNIX-socket $path: $!");
-               return;
-       }
-       return ($sock);
-} # _create_socket
-
-=head1 VALUE IDENTIFIER
-
-The values in the collectd are identified using an five-tupel (host, plugin,
-plugin-instance, type, type-instance) where only plugin-instance and
-type-instance may be NULL (or undefined). Many functions expect an
-I<%identifier> hash that has at least the members B<host>, B<plugin>, and
-B<type>, possibly completed by B<plugin_instance> and B<type_instance>.
-
-Usually you can pass this hash as follows:
-
-  $obj->method (host => $host, plugin => $plugin, type => $type, %other_args);
-
-=cut
-
-sub _create_identifier
-{
-       my $args = shift;
-       my $host;
-       my $plugin;
-       my $type;
-
-       if (!$args->{'host'} || !$args->{'plugin'} || !$args->{'type'})
-       {
-               cluck ("Need `host', `plugin' and `type'");
-               return;
-       }
-
-       $host = $args->{'host'};
-       $plugin = $args->{'plugin'};
-       $plugin .= '-' . $args->{'plugin_instance'} if (defined ($args->{'plugin_instance'}));
-       $type = $args->{'type'};
-       $type .= '-' . $args->{'type_instance'} if (defined ($args->{'type_instance'}));
-
-       return ("$host/$plugin/$type");
-} # _create_identifier
-
-sub _parse_identifier
-{
-       my $string = shift;
-       my $host;
-       my $plugin;
-       my $plugin_instance;
-       my $type;
-       my $type_instance;
-       my $ident;
-
-       ($host, $plugin, $type) = split ('/', $string);
-
-       ($plugin, $plugin_instance) = split ('-', $plugin, 2);
-       ($type, $type_instance) = split ('-', $type, 2);
-
-       $ident =
-       {
-               host => $host,
-               plugin => $plugin,
-               type => $type
-       };
-       $ident->{'plugin_instance'} = $plugin_instance if (defined ($plugin_instance));
-       $ident->{'type_instance'} = $type_instance if (defined ($type_instance));
-
-       return ($ident);
-} # _parse_identifier
-
-=head1 PUBLIC METHODS
-
-=over 4
-
-=item I<$obj> = Collectd::Unixsock->B<new> ([I<$path>]);
-
-Creates a new connection to the daemon. The optional I<$path> argument gives
-the path to the UNIX socket of the C<unixsock plugin> and defaults to
-F</var/run/collectd-unixsock>. Returns the newly created object on success and
-false on error.
-
-=cut
-
-sub new
-{
-       my $pkg = shift;
-       my $path = @_ ? shift : '/var/run/collectd-unixsock';
-       my $sock = _create_socket ($path) or return;
-       my $obj = bless (
-               {
-                       path => $path,
-                       sock => $sock,
-                       error => 'No error'
-               }, $pkg);
-       return ($obj);
-} # new
-
-=item I<$res> = I<$obj>-E<gt>B<getval> (I<%identifier>);
-
-Requests a value-list from the daemon. On success a hash-ref is returned with
-the name of each data-source as the key and the according value as, well, the
-value. On error false is returned.
-
-=cut
-
-sub getval
-{
-       my $obj = shift;
-       my %args = @_;
-
-       my $status;
-       my $fh = $obj->{'sock'} or confess;
-       my $msg;
-       my $identifier;
-
-       my $ret = {};
-
-       $identifier = _create_identifier (\%args) or return;
-
-       $msg = "GETVAL $identifier\n";
-       #print "-> $msg";
-       send ($fh, $msg, 0) or confess ("send: $!");
-
-       $msg = undef;
-       recv ($fh, $msg, 1024, 0) or confess ("recv: $!");
-       #print "<- $msg";
-
-       ($status, $msg) = split (' ', $msg, 2);
-       if ($status <= 0)
-       {
-               $obj->{'error'} = $msg;
-               return;
-       }
-
-       for (split (' ', $msg))
-       {
-               my $entry = $_;
-               if ($entry =~ m/^(\w+)=NaN$/)
-               {
-                       $ret->{$1} = undef;
-               }
-               elsif ($entry =~ m/^(\w+)=($RE{num}{real})$/)
-               {
-                       $ret->{$1} = 0.0 + $2;
-               }
-       }
-
-       return ($ret);
-} # getval
-
-=item I<$obj>-E<gt>B<putval> (I<%identifier>, B<time> => I<$time>, B<values> => [...]);
-
-Submits a value-list to the daemon. If the B<time> argument is omitted
-C<time()> is used. The requierd argument B<values> is a reference to an array
-of values that is to be submitted. The number of values must match the number
-of values expected for the given B<type> (see L<VALUE IDENTIFIER>), though this
-is checked by the daemon, not the Perl module. Also, gauge data-sources
-(e.E<nbsp>g. system-load) may be C<undef>. Returns true upon success and false
-otherwise.
-
-=cut
-
-sub putval
-{
-       my $obj = shift;
-       my %args = @_;
-
-       my $status;
-       my $fh = $obj->{'sock'} or confess;
-       my $msg;
-       my $identifier;
-       my $values;
-
-       $identifier = _create_identifier (\%args) or return;
-       if (!$args{'values'})
-       {
-               cluck ("Need argument `values'");
-               return;
-       }
-
-       if (!ref ($args{'values'}))
-       {
-               $values = $args{'values'};
-       }
-       else
-       {
-               my $time = $args{'time'} ? $args{'time'} : time ();
-               $values = join (':', $time, map { defined ($_) ? $_ : 'U' } (@{$args{'values'}}));
-       }
-
-       $msg = "PUTVAL $identifier $values\n";
-       #print "-> $msg";
-       send ($fh, $msg, 0) or confess ("send: $!");
-       $msg = undef;
-       recv ($fh, $msg, 1024, 0) or confess ("recv: $!");
-       #print "<- $msg";
-
-       ($status, $msg) = split (' ', $msg, 2);
-       return (1) if ($status == 0);
-
-       $obj->{'error'} = $msg;
-       return;
-} # putval
-
-=item I<$res> = I<$obj>-E<gt>B<listval> ()
-
-Queries a list of values from the daemon. The list is returned as an array of
-hash references, where each hash reference is a valid identifier. The C<time>
-member of each hash holds the epoch value of the last update of that value.
-
-=cut
-
-sub listval
-{
-       my $obj = shift;
-       my $msg;
-       my @ret = ();
-       my $status;
-       my $fh = $obj->{'sock'} or confess;
-
-       $msg = "LISTVAL\n";
-       send ($fh, $msg, 0) or confess ("send: $!");
-
-       $msg = <$fh>;
-       ($status, $msg) = split (' ', $msg, 2);
-       if ($status < 0)
-       {
-               $obj->{'error'} = $msg;
-               return;
-       }
-
-       for (my $i = 0; $i < $status; $i++)
-       {
-               my $time;
-               my $ident;
-
-               $msg = <$fh>;
-               chomp ($msg);
-
-               ($time, $ident) = split (' ', $msg, 2);
-
-               $ident = _parse_identifier ($ident);
-               $ident->{'time'} = int ($time);
-
-               push (@ret, $ident);
-       } # for (i = 0 .. $status)
-
-       return (@ret);
-} # listval
-
-=item I<$obj>-E<gt>destroy ();
-
-Closes the socket before the object is destroyed. This function is also
-automatically called then the object goes out of scope.
-
-=back
-
-=cut
-
-sub destroy
-{
-       my $obj = shift;
-       if ($obj->{'sock'})
-       {
-               close ($obj->{'sock'});
-               delete ($obj->{'sock'});
-       }
-}
-
-sub DESTROY
-{
-       my $obj = shift;
-       $obj->destroy ();
-}
-
-=head1 AUTHOR
-
-Florian octo Forster E<lt>octo@verplant.orgE<gt>
-
-=cut
index 6f0836f..398d13b 100644 (file)
@@ -47,6 +47,11 @@ plugins to use the output format understood by the exec-plugin is recommended.
 See the embedded POD documentation for more details:
  $ perldoc contrib/exec-munin.px
 
+exec-smartctl
+-------------
+  Sample script for the exec plugin. Please refer to the documentation in the
+file - you will have to adapt it to your needs anyway.
+
 extractDS.px
 ------------
   Creates a new RRD-file with only one data-source (DS) of the source-RRD-
@@ -67,3 +72,12 @@ 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.
+
+snmp-data.conf
+--------------
+  Sample configuration for the SNMP plugin. This config includes a few standard
+<Data ..> definitions that you can include in your own config using the
+`Include' statement (available since version 4.2.0). The config includes some
+data that is defined in the IF-MIB, e. g. octet or packet counters, UPS-MIB and
+whatever people have send in. If you have some more definitions please send
+them in, so others can profit from it.
index 7919760..7da7e1c 100755 (executable)
@@ -209,8 +209,8 @@ sub _find_plugins
     for (@tmp)
     {
       my ($plugin, $instance) = split (m/-/, $_, 2);
-      $plugins{$plugin} = [] if (!$plugins{$plugin});
-      push (@{$plugins{$plugin}}, $instance) if (defined ($instance));
+      $plugins{$plugin} = [] if (!exists $plugins{$plugin});
+      push (@{$plugins{$plugin}}, $instance);
     }
   } # for (@DataDirs)
 
@@ -267,9 +267,11 @@ sub _find_files_for_host
 
     for (@$plugin_instances)
     {
-      my $plugin_instance = $_;
+      my $plugin_instance = defined ($_) ? $_ : '-';
       my %types = _find_types ($host, $plugin,
-       ($plugin_instance ne '-') ? $plugin_instance : undef);
+       ($plugin_instance ne '-')
+       ? $plugin_instance
+       : undef);
 
       $ret->{$plugin}{$plugin_instance} = {};
 
@@ -1289,6 +1291,18 @@ sub load_graph_definitions
     'GPRINT:temp_max:MAX:%4.1lf Max,',
     'GPRINT:temp_avg:LAST:%4.1lf Last\l'
     ],
+    humidity => ['-v', 'Percent',
+    'DEF:temp_avg={file}:value:AVERAGE',
+    'DEF:temp_min={file}:value:MIN',
+    'DEF:temp_max={file}:value:MAX',
+    "AREA:temp_max#$HalfGreen",
+    "AREA:temp_min#$Canvas",
+    "LINE1:temp_avg#$FullGreen:Temperature",
+    'GPRINT:temp_min:MIN:%4.1lf%% Min,',
+    'GPRINT:temp_avg:AVERAGE:%4.1lf%% Avg,',
+    'GPRINT:temp_max:MAX:%4.1lf%% Max,',
+    'GPRINT:temp_avg:LAST:%4.1lf%% Last\l'
+    ],
     if_errors => ['-v', 'Errors/s',
     'DEF:tx_min={file}:tx:MIN',
     'DEF:tx_avg={file}:tx:AVERAGE',
@@ -1881,6 +1895,18 @@ sub load_graph_definitions
     'GPRINT:ping_avg:AVERAGE:%4.1lf ms Avg,',
     'GPRINT:ping_max:MAX:%4.1lf ms Max,',
     'GPRINT:ping_avg:LAST:%4.1lf ms Last'],
+    power => ['-v', 'Watt',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Watt",
+    'GPRINT:min:MIN:%5.1lf%sW Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%sW Avg,',
+    'GPRINT:max:MAX:%5.1lf%sW Max,',
+    'GPRINT:avg:LAST:%5.1lf%sW Last\l'
+    ],
     processes => [
     "DEF:running_avg={file}:running:AVERAGE",
     "DEF:running_min={file}:running:MIN",
@@ -1943,18 +1969,27 @@ sub load_graph_definitions
     'GPRINT:sleeping_max:MAX:%5.1lf Max,',
     'GPRINT:sleeping_avg:LAST:%5.1lf Last\l'
     ],
-    ps_rss => [
-    'DEF:avg={file}:byte:AVERAGE',
-    'DEF:min={file}:byte:MIN',
-    'DEF:max={file}:byte:MAX',
-    "AREA:avg#$HalfBlue",
-    "LINE1:avg#$FullBlue:RSS",
-    'GPRINT:min:MIN:%5.1lf%s Min,',
-    'GPRINT:avg:AVERAGE:%5.1lf%s Avg,',
-    'GPRINT:max:MAX:%5.1lf%s Max,',
-    'GPRINT:avg:LAST:%5.1lf%s Last\l'
+    ps_count => ['-v', 'Processes',
+    'DEF:procs_avg={file}:processes:AVERAGE',
+    'DEF:procs_min={file}:processes:MIN',
+    'DEF:procs_max={file}:processes:MAX',
+    'DEF:thrds_avg={file}:threads:AVERAGE',
+    'DEF:thrds_min={file}:threads:MIN',
+    'DEF:thrds_max={file}:threads:MAX',
+    "AREA:thrds_avg#$HalfBlue",
+    "AREA:procs_avg#$HalfRed",
+    "LINE1:thrds_avg#$FullBlue:Threads  ",
+    'GPRINT:thrds_min:MIN:%5.1lf Min,',
+    'GPRINT:thrds_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:thrds_max:MAX:%5.1lf Max,',
+    'GPRINT:thrds_avg:LAST:%5.1lf Last\l',
+    "LINE1:procs_avg#$FullRed:Processes",
+    'GPRINT:procs_min:MIN:%5.1lf Min,',
+    'GPRINT:procs_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:procs_max:MAX:%5.1lf Max,',
+    'GPRINT:procs_avg:LAST:%5.1lf Last\l'
     ],
-    ps_cputime => [
+    ps_cputime => ['-v', 'Jiffies',
     'DEF:user_avg_raw={file}:user:AVERAGE',
     'DEF:user_min_raw={file}:user:MIN',
     'DEF:user_max_raw={file}:user:MAX',
@@ -1981,27 +2016,7 @@ sub load_graph_definitions
     'GPRINT:syst_max:MAX:%5.1lf%s Max,',
     'GPRINT:syst_avg:LAST:%5.1lf%s Last\l'
     ],
-    ps_count => [
-    'DEF:procs_avg={file}:processes:AVERAGE',
-    'DEF:procs_min={file}:processes:MIN',
-    'DEF:procs_max={file}:processes:MAX',
-    'DEF:thrds_avg={file}:threads:AVERAGE',
-    'DEF:thrds_min={file}:threads:MIN',
-    'DEF:thrds_max={file}:threads:MAX',
-    "AREA:thrds_avg#$HalfBlue",
-    "AREA:procs_avg#$HalfRed",
-    "LINE1:thrds_avg#$FullBlue:Threads  ",
-    'GPRINT:thrds_min:MIN:%5.1lf Min,',
-    'GPRINT:thrds_avg:AVERAGE:%5.1lf Avg,',
-    'GPRINT:thrds_max:MAX:%5.1lf Max,',
-    'GPRINT:thrds_avg:LAST:%5.1lf Last\l',
-    "LINE1:procs_avg#$FullRed:Processes",
-    'GPRINT:procs_min:MIN:%5.1lf Min,',
-    'GPRINT:procs_avg:AVERAGE:%5.1lf Avg,',
-    'GPRINT:procs_max:MAX:%5.1lf Max,',
-    'GPRINT:procs_avg:LAST:%5.1lf Last\l'
-    ],
-    ps_pagefaults => [
+    ps_pagefaults => ['-v', 'Pagefaults/s',
     'DEF:minor_avg={file}:minflt:AVERAGE',
     'DEF:minor_min={file}:minflt:MIN',
     'DEF:minor_max={file}:minflt:MAX',
@@ -2022,6 +2037,17 @@ sub load_graph_definitions
     'GPRINT:major_max:MAX:%5.1lf%s Max,',
     'GPRINT:major_avg:LAST:%5.1lf%s Last\l'
     ],
+    ps_rss => ['-v', 'Bytes',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:avg#$HalfBlue",
+    "LINE1:avg#$FullBlue:RSS",
+    'GPRINT:min:MIN:%5.1lf%s Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:max:MAX:%5.1lf%s Max,',
+    'GPRINT:avg:LAST:%5.1lf%s Last\l'
+    ],
     ps_state => ['-v', 'Processes',
     'DEF:avg={file}:value:AVERAGE',
     'DEF:min={file}:value:MIN',
@@ -2070,7 +2096,7 @@ sub load_graph_definitions
     'GPRINT:max:MAX:%6.2lf%sByte Max,',
     'GPRINT:avg:LAST:%6.2lf%sByte Last\l'
     ],
-    ols_swap => [
+    old_swap => [
     'DEF:used_avg={file}:used:AVERAGE',
     'DEF:used_min={file}:used:MIN',
     'DEF:used_max={file}:used:MAX',
@@ -2114,6 +2140,18 @@ sub load_graph_definitions
     'GPRINT:used_max:MAX:%5.1lf%s Max,',
     'GPRINT:used_avg:LAST:%5.1lf%s Last\l'
     ],
+    tcp_connections => ['-v', 'Connections',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Connections",
+    'GPRINT:min:MIN:%4.1lf Min,',
+    'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+    'GPRINT:max:MAX:%4.1lf Max,',
+    'GPRINT:avg:LAST:%4.1lf Last\l'
+    ],
     temperature => ['-v', 'Celsius',
     'DEF:temp_avg={file}:value:AVERAGE',
     'DEF:temp_min={file}:value:MIN',
@@ -2127,7 +2165,7 @@ sub load_graph_definitions
     'GPRINT:temp_max:MAX:%4.1lf Max,',
     'GPRINT:temp_avg:LAST:%4.1lf Last\l'
     ],
-    timeleft => [
+    timeleft => ['-v', 'Minutes',
     'DEF:avg={file}:timeleft:AVERAGE',
     'DEF:min={file}:timeleft:MIN',
     'DEF:max={file}:timeleft:MAX',
@@ -2211,7 +2249,7 @@ sub load_graph_definitions
     'GPRINT:multimeter_max:MAX:%4.1lf Max,',
     'GPRINT:multimeter_avg:LAST:%4.1lf Last\l'
     ],
-    users => [
+    users => ['-v', 'Users',
     'DEF:users_avg={file}:users:AVERAGE',
     'DEF:users_min={file}:users:MIN',
     'DEF:users_max={file}:users:MAX',
@@ -2328,6 +2366,7 @@ sub load_graph_definitions
   $MetaGraphDefs->{'swap'} = \&meta_graph_swap;
   $MetaGraphDefs->{'mysql_commands'} = \&meta_graph_mysql_commands;
   $MetaGraphDefs->{'mysql_handler'} = \&meta_graph_mysql_commands;
+  $MetaGraphDefs->{'tcp_connections'} = \&meta_graph_tcp_connections;
 } # load_graph_definitions
 
 sub meta_graph_generic_stack
@@ -2444,6 +2483,8 @@ sub meta_graph_cpu
   $opts->{'title'} = "$host/$plugin"
   . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
 
+  $opts->{'rrd_opts'} = ['-v', 'Percent'];
+
   my @files = ();
 
   $opts->{'colors'} =
@@ -2701,6 +2742,7 @@ sub meta_graph_ps_state
 
   $opts->{'title'} = "$host/$plugin"
   . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
+  $opts->{'rrd_opts'} = ['-v', 'Processes'];
 
   my @files = ();
 
@@ -2760,6 +2802,7 @@ sub meta_graph_swap
   $opts->{'title'} = "$host/$plugin"
   . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
   $opts->{'number_format'} = '%5.1lf%s';
+  $opts->{'rrd_opts'} = ['-v', 'Bytes'];
 
   my @files = ();
 
@@ -2801,4 +2844,70 @@ sub meta_graph_swap
   return (meta_graph_generic_stack ($opts, $sources));
 } # meta_graph_swap
 
+sub meta_graph_tcp_connections
+{
+  confess ("Wrong number of arguments") if (@_ != 5);
+
+  my $host = shift;
+  my $plugin = shift;
+  my $plugin_instance = shift;
+  my $type = shift;
+  my $type_instances = shift;
+
+  my $opts = {};
+  my $sources = [];
+
+  $opts->{'title'} = "$host/$plugin"
+  . (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
+  $opts->{'number_format'} = '%6.2lf';
+
+  $opts->{'rrd_opts'} = ['-v', 'Connections'];
+
+  my @files = ();
+
+  $opts->{'colors'} =
+  {
+    ESTABLISHED          => '00e000',
+    SYN_SENT     => '00e0ff',
+    SYN_RECV     => '00e0a0',
+    FIN_WAIT1    => 'f000f0',
+    FIN_WAIT2    => 'f000a0',
+    TIME_WAIT    => 'ffb000',
+    CLOSE        => '0000f0',
+    CLOSE_WAIT   => '0000a0',
+    LAST_ACK     => '000080',
+    LISTEN       => 'ff0000',
+    CLOSING      => '000000'
+  };
+
+  _custom_sort_arrayref ($type_instances,
+    [reverse qw(ESTABLISHED SYN_SENT SYN_RECV FIN_WAIT1 FIN_WAIT2 TIME_WAIT CLOSE
+    CLOSE_WAIT LAST_ACK CLOSING LISTEN)]);
+
+  for (@$type_instances)
+  {
+    my $inst = $_;
+    my $file = '';
+    my $title = $opts->{'title'};
+
+    for (@DataDirs)
+    {
+      if (-e "$_/$title-$inst.rrd")
+      {
+       $file = "$_/$title-$inst.rrd";
+       last;
+      }
+    }
+    confess ("No file found for $title") if ($file eq '');
+
+    push (@$sources,
+      {
+       name => $inst,
+       file => $file
+      }
+    );
+  } # for (@$type_instances)
+
+  return (meta_graph_generic_stack ($opts, $sources));
+} # meta_graph_tcp_connections
 # vim: shiftwidth=2:softtabstop=2:tabstop=8
diff --git a/contrib/exec-smartctl b/contrib/exec-smartctl
new file mode 100755 (executable)
index 0000000..d469816
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Sample script for the exec plugin (collectd-exec(5))
+#
+# This script uses smartctl(8) to read HDD temperatures. The drives are
+# attached to a 3ware RAID controller which hddtempd can't handle.
+# Unfortunately the smartmontools don't have a library so we can't write a
+# C-plugin, at least not easily.
+# Please note that only root can read the SMART attributes from harddrives,
+# because special ``capabilities'' are necessary. However, the exec plugin will
+# refuse to run scripts as root, which is why `sudo' is used here for
+# fine-grained root privileges for the user `smart'. This isn't as straigt
+# forward as one might hope, but we think that the gained security is worth it.
+
+# The sudo configuration looks something like this:
+# -- 8< --
+# Cmnd_Alias      SMARTCTL = /usr/sbin/smartctl -d 3ware\,0 -A /dev/twe0, /usr/sbin/smartctl -d 3ware\,1 -A /dev/twe0, /usr/sbin/smartctl -d ata -A /dev/sda
+# smart   ALL = (root) NOPASSWD: SMARTCTL
+# -- >8 --
+
+HOST="huhu"
+INTERVAL=60
+
+while true
+do
+       TEMP=$((sudo smartctl -d 3ware,0 -A /dev/twe0 | grep Temperature_Celsius | awk '{ print $10; }') 2>/dev/null);
+       if [ $? -ne 0 ]
+       then
+               TEMP="U"
+       fi
+       echo "$HOST/exec-smart/temperature-3ware_0 interval=$INTERVAL N:$TEMP"
+
+       TEMP=$((sudo smartctl -d 3ware,1 -A /dev/twe0 | grep Temperature_Celsius | awk '{ print $10; }') 2>/dev/null);
+       if [ $? -ne 0 ]
+       then
+               TEMP="U"
+       fi
+       echo "$HOST/exec-smart/temperature-3ware_1 interval=$INTERVAL N:$TEMP"
+
+       TEMP=$((sudo smartctl -d ata -A /dev/sda | grep Temperature_Celsius | awk '{ print $10; }') 2>/dev/null);
+       if [ $? -ne 0 ]
+       then
+               TEMP="U"
+       fi
+       echo "$HOST/exec-smart/temperature-sata_0 interval=$INTERVAL N:$TEMP"
+
+       sleep $INTERVAL
+done
index d9f0552..89ce123 100644 (file)
@@ -1,13 +1,17 @@
 Summary:       Statistics collection daemon for filling RRD files.
 Name:           collectd
-Version:       4.0.6
-Release:       0.fc7
+Version:       4.2.0
+Release:       1.fc6
 Source:                http://collectd.org/files/%{name}-%{version}.tar.gz
 License:       GPL
 Group:         System Environment/Daemons
 BuildRoot:     %{_tmppath}/%{name}-%{version}-root
-BuildPrereq:   lm_sensors-devel, mysql-devel, rrdtool-devel
+BuildPrereq:   lm_sensors-devel
+BuildPrereq:   mysql-devel
+BuildPrereq:   rrdtool-devel
+BuildPrereq:   net-snmp-devel
 Requires:      rrdtool
+Requires:      perl-Regexp-Common
 Packager:      Florian octo Forster <octo@verplant.org>
 Vendor:                Florian octo Forster <octo@verplant.org>
 
@@ -63,6 +67,7 @@ mkdir -p $RPM_BUILD_ROOT/var/www/cgi-bin
 cp src/collectd.conf $RPM_BUILD_ROOT/etc/collectd.conf
 cp contrib/fedora/init.d-collectd $RPM_BUILD_ROOT/etc/rc.d/init.d/collectd
 cp contrib/collection.cgi $RPM_BUILD_ROOT/var/www/cgi-bin
+cp contrib/collection.conf $RPM_BUILD_ROOT/etc/collection.conf
 mkdir -p $RPM_BUILD_ROOT/var/lib/collectd
 
 %clean
@@ -90,6 +95,7 @@ exit 0
 %defattr(-,root,root)
 %doc AUTHORS COPYING ChangeLog INSTALL NEWS README
 %attr(0644,root,root) %config(noreplace) /etc/collectd.conf
+%attr(0644,root,root) %config(noreplace) /etc/collection.conf
 %attr(0755,root,root) /etc/rc.d/init.d/collectd
 %attr(0755,root,root) /var/www/cgi-bin/collection.cgi
 %attr(0755,root,root) %{_sbindir}/collectd
@@ -97,11 +103,18 @@ exit 0
 %attr(0644,root,root) %{_mandir}/man1/*
 %attr(0644,root,root) %{_mandir}/man5/*
 
+%attr(0644,root,root) /usr/lib/perl5/5.8.8/i386-linux-thread-multi/perllocal.pod
+%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd.pm
+%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd/Unixsock.pm
+%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/auto/Collectd/.packlist
+%attr(0644,root,root) %{_mandir}/man3/Collectd::Unixsock.3pm.gz
+
 %attr(0644,root,root) %{_libdir}/%{name}/apcups.so*
 %attr(0644,root,root) %{_libdir}/%{name}/apcups.la
 
-%attr(0644,root,root) %{_libdir}/%{name}/apple_sensors.so*
-%attr(0644,root,root) %{_libdir}/%{name}/apple_sensors.la
+# FIXME!!!
+#%attr(0644,root,root) %{_libdir}/%{name}/apple_sensors.so*
+#%attr(0644,root,root) %{_libdir}/%{name}/apple_sensors.la
 
 %attr(0644,root,root) %{_libdir}/%{name}/battery.so*
 %attr(0644,root,root) %{_libdir}/%{name}/battery.la
@@ -148,9 +161,12 @@ exit 0
 %attr(0644,root,root) %{_libdir}/%{name}/logfile.so*
 %attr(0644,root,root) %{_libdir}/%{name}/logfile.la
 
-%attr(0644,root,root) %{_libdir}/%{name}/mbmon.so
+%attr(0644,root,root) %{_libdir}/%{name}/mbmon.so*
 %attr(0644,root,root) %{_libdir}/%{name}/mbmon.la
 
+%attr(0644,root,root) %{_libdir}/%{name}/memcached.so*
+%attr(0644,root,root) %{_libdir}/%{name}/memcached.la
+
 %attr(0644,root,root) %{_libdir}/%{name}/memory.so*
 %attr(0644,root,root) %{_libdir}/%{name}/memory.la
 
@@ -163,11 +179,15 @@ exit 0
 %attr(0644,root,root) %{_libdir}/%{name}/nfs.so*
 %attr(0644,root,root) %{_libdir}/%{name}/nfs.la
 
+%attr(0644,root,root) %{_libdir}/%{name}/nginx.so*
+%attr(0644,root,root) %{_libdir}/%{name}/nginx.la
+
 %attr(0644,root,root) %{_libdir}/%{name}/ntpd.so*
 %attr(0644,root,root) %{_libdir}/%{name}/ntpd.la
 
-%attr(0644,root,root) %{_libdir}/%{name}/nut.so*
-%attr(0644,root,root) %{_libdir}/%{name}/nut.la
+# FIXME!!!
+#%attr(0644,root,root) %{_libdir}/%{name}/nut.so*
+#%attr(0644,root,root) %{_libdir}/%{name}/nut.la
 
 %attr(0644,root,root) %{_libdir}/%{name}/perl.so*
 %attr(0644,root,root) %{_libdir}/%{name}/perl.la
@@ -187,11 +207,18 @@ exit 0
 %attr(0644,root,root) %{_libdir}/%{name}/swap.so*
 %attr(0644,root,root) %{_libdir}/%{name}/swap.la
 
+%attr(0644,root,root) %{_libdir}/%{name}/snmp.so*
+%attr(0644,root,root) %{_libdir}/%{name}/snmp.la
+
 %attr(0644,root,root) %{_libdir}/%{name}/syslog.so*
 %attr(0644,root,root) %{_libdir}/%{name}/syslog.la
 
-%attr(0644,root,root) %{_libdir}/%{name}/tape.so*
-%attr(0644,root,root) %{_libdir}/%{name}/tape.la
+# FIXME!!!
+#%attr(0644,root,root) %{_libdir}/%{name}/tape.so*
+#%attr(0644,root,root) %{_libdir}/%{name}/tape.la
+
+%attr(0644,root,root) %{_libdir}/%{name}/tcpconns.so*
+%attr(0644,root,root) %{_libdir}/%{name}/tcpconns.la
 
 %attr(0644,root,root) %{_libdir}/%{name}/unixsock.so*
 %attr(0644,root,root) %{_libdir}/%{name}/unixsock.la
@@ -226,11 +253,15 @@ exit 0
 %attr(0644,root,root) %{_libdir}/%{name}/sensors.la
 
 %changelog
+* Wed Oct 31 2007 Iain Lea <iain@bricbrac.de> 4.2.0
+- New major release
+- Changes to support 4.2.0 (ie. contrib/collection.conf)
+
 * Mon Aug 06 2007 Kjell Randa <Kjell.Randa@broadpark.no> 4.0.6
 - New upstream version
 
 * Wed Jul 25 2007 Kjell Randa <Kjell.Randa@broadpark.no> 4.0.5
-- New major releas
+- New major release
 - Changes to support 4.0.5 
 
 * Wed Jan 11 2007 Iain Lea <iain@bricbrac.de> 3.11.0-0
diff --git a/contrib/snmp-data.conf b/contrib/snmp-data.conf
new file mode 100644 (file)
index 0000000..fd8d404
--- /dev/null
@@ -0,0 +1,455 @@
+<Plugin snmp>
+    #
+    # IF-MIB
+    # Interface statistics using the IF-MIB
+    #
+    <Data "ifmib_if_octets32">
+       Type "if_octets"
+       Table true
+       Instance "IF-MIB::ifDescr"
+       Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
+    </Data>
+    <Data "ifmib_if_octets64">
+       Type "if_octets"
+       Table true
+       Instance "IF-MIB::ifName"
+       Values "IF-MIB::ifHCInOctets" "IF-MIB::ifHCOutOctets"
+    </Data>
+    <Data "ifmib_if_packets32">
+       Type "if_packets"
+       Table true
+       Instance "IF-MIB::ifDescr"
+       Values "IF-MIB::ifInUcastPkts" "IF-MIB::ifOutUcastPkts"
+    </Data>
+    <Data "ifmib_if_packets64">
+       Type "if_packets"
+       Table true
+       Instance "IF-MIB::ifName"
+       Values "IF-MIB::ifHCInUcastPkts" "IF-MIB::ifHCOutUcastPkts"
+    </Data>
+    <Data "ifmib_if_errors32">
+       Type "if_errors"
+       Table true
+       Instance "IF-MIB::ifDescr"
+       Values "IF-MIB::ifInErrors" "IF-MIB::ifOutErrors"
+    </Data>
+    <Data "ifmib_if_errors64">
+       Type "if_errors"
+       Table true
+       Instance "IF-MIB::ifName"
+       Values "IF-MIB::ifHCInErrors" "IF-MIB::ifHCOutErrors"
+    </Data>
+
+    #
+    # UPS-MIB
+    # Statistics about your UPS using the UPS-MIB from the RFC1628.
+    #
+    # Battery branch
+    <Data "upsmib_timeleft_battery">
+       Type "timeleft"
+       Table false
+       Instance "battery"
+       Values ".1.3.6.1.2.1.33.1.2.3.0"
+    </Data>
+    <Data "upsmib_charge_battery">
+       Type "percent"
+       Table false
+       Instance "charge-battery"
+       Values ".1.3.6.1.2.1.33.1.2.4.0"
+    </Data>
+    <Data "upsmib_voltage_battery">
+       Type "voltage"
+       Table false
+       Instance "battery"
+       Values ".1.3.6.1.2.1.33.1.2.5.0"
+       Scale 0.1
+    </Data>
+    <Data "upsmib_current_battery">
+       Type "current"
+       Table false
+       Instance "battery"
+       Values ".1.3.6.1.2.1.33.1.2.6.0"
+       Scale 0.1
+    </Data>
+    <Data "upsmib_temperature_battery">
+       Type "temperature"
+       Table false
+       Instance "battery"
+       Values ".1.3.6.1.2.1.33.1.2.7.0"
+    </Data>
+    # Input branch
+    <Data "upsmib_frequency_input">
+       Type "frequency"
+       Table true
+       InstancePrefix "input"
+       Values ".1.3.6.1.2.1.33.1.3.3.1.2"
+       Scale 0.1
+    </Data>
+    <Data "upsmib_voltage_input">
+       Type "voltage"
+       Table true
+       InstancePrefix "input"
+       Values ".1.3.6.1.2.1.33.1.3.3.1.3"
+    </Data>
+    <Data "upsmib_current_input">
+       Type "current"
+       Table true
+       InstancePrefix "input"
+       Values ".1.3.6.1.2.1.33.1.3.3.1.4"
+       Scale 0.1
+    </Data>
+    <Data "upsmib_power_input">
+       Type "power"
+       Table true
+       InstancePrefix "input"
+       Values ".1.3.6.1.2.1.33.1.3.3.1.5"
+    </Data>
+    # Output branch
+    <Data "upsmib_frequency_output">
+       Type "frequency"
+       Table false
+       Instance "output"
+       Values ".1.3.6.1.2.1.33.1.4.2.0"
+       Scale 0.1
+    </Data>
+    <Data "upsmib_voltage_output">
+       Type "voltage"
+       Table true
+       InstancePrefix "output"
+       Values ".1.3.6.1.2.1.33.1.4.4.1.2"
+    </Data>
+    <Data "upsmib_current_output">
+       Type "current"
+       Table true
+       InstancePrefix "output"
+       Values ".1.3.6.1.2.1.33.1.4.4.1.3"
+       Scale 0.1
+    </Data>
+    <Data "upsmib_power_output">
+       Type "power"
+       Table true
+       InstancePrefix "output"
+       Values ".1.3.6.1.2.1.33.1.4.4.1.4"
+    </Data>
+    <Data "upsmib_load_output">
+       Type "percent"
+       Table true
+       InstancePrefix "load-output"
+       Values ".1.3.6.1.2.1.33.1.4.4.1.5"
+    </Data>
+    # Bypass branch
+    <Data "upsmib_frequency_bypass">
+       Type "frequency"
+       Table false
+       Instance "output"
+       Values ".1.3.6.1.2.1.33.1.5.1.0"
+       Scale 0.1
+    </Data>
+    <Data "upsmib_voltage_bypass">
+       Type "voltage"
+       Table true
+       InstancePrefix "bypass"
+       Values ".1.3.6.1.2.1.33.1.5.3.1.2"
+    </Data>
+    <Data "upsmib_current_bypass">
+       Type "current"
+       Table true
+       InstancePrefix "bypass"
+       Values ".1.3.6.1.2.1.33.1.5.3.1.3"
+       Scale 0.1
+    </Data>
+    <Data "upsmib_power_bypass">
+       Type "power"
+       Table true
+       InstancePrefix "bypass"
+       Values ".1.3.6.1.2.1.33.1.5.3.1.4"
+    </Data>
+    # Special definitions for broken UPSes
+    <Data "upsmib_voltage_battery_unscaled">
+       Type "voltage"
+       Table false
+       Instance "battery"
+       Values ".1.3.6.1.2.1.33.1.2.5.0"
+    </Data>
+    <Data "upsmib_current_input_unscaled">
+       Type "current"
+       Table true
+       InstancePrefix "input"
+       Values ".1.3.6.1.2.1.33.1.3.3.1.4"
+    </Data>
+    <Data "upsmib_current_output_unscaled">
+       Type "current"
+       Table true
+       InstancePrefix "output"
+       Values ".1.3.6.1.2.1.33.1.4.4.1.3"
+    </Data>
+
+    #
+    # Riello UPS
+    # Temperatures for UPSes by Riello, <http://www.riello-ups.de/>
+    #
+    <Data "riello_temperature_system">
+       Type "temperature"
+       Table false
+       Instance "system"
+       Values ".1.3.6.1.4.1.5491.1.51.1.5.4.0"
+    </Data>
+    <Data "riello_temperature_rectifier">
+       Type "temperature"
+       Table false
+       Instance "rectifier"
+       Values ".1.3.6.1.4.1.5491.1.51.1.5.5.0"
+    </Data>
+    <Data "riello_temperature_inverter">
+       Type "temperature"
+       Table false
+       Instance "inverter"
+       Values ".1.3.6.1.4.1.5491.1.51.1.5.6.0"
+    </Data>
+
+    #
+    # PowerPlus UPS, manufactured by Gamatronic, <http://www.gamatronic.com/>,
+    # distributed in Germany by AdPoS, <http://adpos-usv.de/>
+    #
+    # Global inputs
+    <Data "powerplus_voltage_input">
+       Type "voltage"
+       Table true
+       InstancePrefix "input"
+       Values ".1.3.6.1.4.1.6050.5.4.1.1.2"
+    </Data>
+    <Data "powerplus_current_input">
+       Type "current"
+       Table true
+       InstancePrefix "input"
+       Values ".1.3.6.1.4.1.6050.5.4.1.1.3"
+    </Data>
+    <Data "powerplus_power_apparent_input">
+       Type "power"
+       Table true
+       InstancePrefix "apparent-input"
+       Values ".1.3.6.1.4.1.6050.5.4.1.1.4"
+       Scale 100.0
+    </Data>
+    <Data "powerplus_power_active_input">
+       Type "power"
+       Table true
+       InstancePrefix "active-input"
+       Values ".1.3.6.1.4.1.6050.5.4.1.1.5"
+       Scale 100.0
+    </Data>
+    <Data "powerplus_performance_factor_input">
+       Type "percent"
+       Table true
+       InstancePrefix "performance_factor-input"
+       Values ".1.3.6.1.4.1.6050.5.4.1.1.6"
+    </Data>
+    # Global outputs
+    <Data "powerplus_voltage_output">
+       Type "voltage"
+       Table true
+       InstancePrefix "output"
+       Values ".1.3.6.1.4.1.6050.5.5.1.1.2"
+    </Data>
+    <Data "powerplus_current_output">
+       Type "current"
+       Table true
+       InstancePrefix "output"
+       Values ".1.3.6.1.4.1.6050.5.5.1.1.3"
+    </Data>
+    <Data "powerplus_power_apparent_output">
+       Type "power"
+       Table true
+       InstancePrefix "apparent-output"
+       Values ".1.3.6.1.4.1.6050.5.5.1.1.4"
+       Scale 100.0
+    </Data>
+    <Data "powerplus_power_active_output">
+       Type "power"
+       Table true
+       InstancePrefix "active-output"
+       Values ".1.3.6.1.4.1.6050.5.5.1.1.5"
+       Scale 100.0
+    </Data>
+    <Data "powerplus_load_level_output">
+       Type "percent"
+       Table true
+       InstancePrefix "load_level-output"
+       Values ".1.3.6.1.4.1.6050.5.5.1.1.6"
+    </Data>
+    <Data "powerplus_active_load_level_output">
+       Type "percent"
+       Table true
+       InstancePrefix "active_load_level-output"
+       Values ".1.3.6.1.4.1.6050.5.5.1.1.7"
+    </Data>
+    <Data "powerplus_performance_factor_output">
+       Type "percent"
+       Table true
+       InstancePrefix "performance_factor-output"
+       Values ".1.3.6.1.4.1.6050.5.5.1.1.8"
+    </Data>
+    # Global DC
+    <Data "powerplus_global_dc_positive">
+       Type "voltage"
+       Table false
+       Instance "dc_positive-global"
+       Values ".1.3.6.1.4.1.6050.5.6.1.0"
+    </Data>
+    <Data "powerplus_global_dc_negative">
+       Type "voltage"
+       Table false
+       Instance "dc_negative-global"
+       Values ".1.3.6.1.4.1.6050.5.6.2.0"
+    </Data>
+    <Data "powerplus_global_dc_total">
+       Type "voltage"
+       Table false
+       Instance "dc_total-global"
+       Values ".1.3.6.1.4.1.6050.5.6.3.0"
+    </Data>
+
+    #
+    # NetApp
+    # Some simple statistics of storage systems by NetApp.
+    #
+    <Data "netapp_cpu_system">
+       Type "cpu"
+       Table false
+       Instance "system"
+       Values ".1.3.6.1.4.1.789.1.2.1.2.0"
+    </Data>
+    <Data "netapp_cpu_idle">
+       Type "cpu"
+       Table false
+       Instance "idle"
+       Values ".1.3.6.1.4.1.789.1.2.1.4.0"
+    </Data>
+    <Data "netapp_if_octets">
+       Type "if_octets"
+       Table false
+       Instance "net"
+       Values ".1.3.6.1.4.1.789.1.2.2.12.0" ".1.3.6.1.4.1.789.1.2.2.14.0"
+    </Data>
+
+    #
+    # Juniper SSL
+    # Some stats of an SSL-appliance by Juniper.
+    #
+    <Data "juniperssl_users_web">
+       Type "users"
+       Table false
+       Instance "web"
+       Values ".1.3.6.1.4.1.12532.2.0"
+    </Data>
+    <Data "juniperssl_users_mail">
+       Type "users"
+       Table false
+       Instance "mail"
+       Values ".1.3.6.1.4.1.12532.3.0"
+    </Data>
+    <Data "juniperssl_percent_logfull">
+       Type "percent"
+       Table false
+       Instance "logfull"
+       Values ".1.3.6.1.4.1.12532.1.0"
+    </Data>
+    <Data "juniperssl_percent_diskfull">
+       Type "percent"
+       Table false
+       Instance "diskfull"
+       Values ".1.3.6.1.4.1.12532.25.0"
+    </Data>
+
+
+    #
+    # WuT
+    # Some thermometers and digital IO devices from WuT
+    # <http://www.wut.de/>
+    #
+    <Data "wut_an8graph">
+       Type "temperature"
+       Table true
+       Instance ".1.3.6.1.4.1.5040.1.2.6.3.2.1.1.2"
+       Values ".1.3.6.1.4.1.5040.1.2.6.1.4.1.1"
+       Scale 0.1
+    </Data>
+    <Data "wut_an2graph">
+       Type "temperature"
+       Table true
+       Instance ".1.3.6.1.4.1.5040.1.2.7.3.2.1.1.2"
+       Values ".1.3.6.1.4.1.5040.1.2.7.1.4.1.1"
+       Scale 0.1
+    </Data>
+    <Data "wut_an1graph">
+       Type "temperature"
+       Table true
+       Instance ".1.3.6.1.4.1.5040.1.2.8.3.2.1.1.2"
+       Values ".1.3.6.1.4.1.5040.1.2.8.1.4.1.1"
+       Scale 0.1
+    </Data>
+    <Data "wut_thermo8">
+       Type "temperature"
+       Table true
+       Instance ".1.3.6.1.4.1.5040.1.2.1.3.2.1.1.2"
+       Values ".1.3.6.1.4.1.5040.1.2.1.1.4.1.1"
+       Scale 0.1
+    </Data>
+    <Data "wut_thermo2">
+       Type "temperature"
+       Table true
+       Instance ".1.3.6.1.4.1.5040.1.2.2.3.2.1.1.2"
+       Values ".1.3.6.1.4.1.5040.1.2.2.1.4.1.1"
+       Scale 0.1
+    </Data>
+    <Data "wut_thermo1">
+       Type "temperature"
+       Table true
+       Instance ".1.3.6.1.4.1.5040.1.2.3.3.2.1.1.2"
+       Values ".1.3.6.1.4.1.5040.1.2.3.1.4.1.1"
+       Scale 0.1
+    </Data>
+
+    #
+    # Infratec
+    # Rack monitoring devices by Infratec, <http://www.infratec-ag.de/>
+    #
+    # Model H2-17
+    <Data "infratec_h2_17_temperature">
+       Type "temperature"
+       Table true
+       Instance ".1.3.6.1.4.1.4519.10.4.1.1.2"
+       Values ".1.3.6.1.4.1.4519.10.4.1.1.3"
+    </Data>
+    <Data "infratec_h2_17_humidity">
+       Type "humidity"
+       Table true
+       Instance ".1.3.6.1.4.1.4519.10.5.1.1.2"
+       Values ".1.3.6.1.4.1.4519.10.5.1.1.3"
+    </Data>
+    <Data "infratec_h2_17_voltage">
+       Type "voltage"
+       Table true
+       InstancePrefix "input"
+       Values ".1.3.6.1.4.1.4519.10.6.1.1.3"
+    </Data>
+    # Model H2-30
+    <Data "infratec_h2_30_temperature">
+       Type "temperature"
+       Table true
+       Instance ".1.3.6.1.4.1.1909.10.4.1.1.2"
+       Values ".1.3.6.1.4.1.1909.10.4.1.1.3"
+    </Data>
+    <Data "infratec_h2_30_humidity">
+       Type "humidity"
+       Table true
+       Instance ".1.3.6.1.4.1.1909.10.5.1.1.2"
+       Values ".1.3.6.1.4.1.1909.10.5.1.1.3"
+    </Data>
+    <Data "infratec_h2_30_voltage">
+       Type "voltage"
+       Table true
+       InstancePrefix "input"
+       Values ".1.3.6.1.4.1.1909.10.6.1.1.3"
+    </Data>
+</Plugin>
index 9818580..d305d85 100644 (file)
@@ -260,6 +260,14 @@ collectd_LDADD += "-dlopen" iptables.la
 collectd_DEPENDENCIES += iptables.la
 endif
 
+if BUILD_PLUGIN_IPVS
+pkglib_LTLIBRARIES += ipvs.la
+ipvs_la_SOURCES = ipvs.c
+ipvs_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" ipvs.la
+collectd_DEPENDENCIES += ipvs.la
+endif
+
 if BUILD_PLUGIN_IRQ
 pkglib_LTLIBRARIES += irq.la
 irq_la_SOURCES = irq.c
@@ -304,6 +312,19 @@ collectd_LDADD += "-dlopen" mbmon.la
 collectd_DEPENDENCIES += mbmon.la
 endif
 
+if BUILD_PLUGIN_MEMCACHED
+pkglib_LTLIBRARIES += memcached.la
+memcached_la_SOURCES = memcached.c
+memcached_la_LDFLAGS = -module -avoid-version
+memcached_la_CFLAGS =
+memcached_la_LIBADD =
+if BUILD_WITH_LIBSOCKET
+memcached_la_LDFLAGS += -lsocket
+endif
+collectd_LDADD += "-dlopen" memcached.la
+collectd_DEPENDENCIES += memcached.la
+endif
+
 if BUILD_PLUGIN_MEMORY
 pkglib_LTLIBRARIES += memory.la
 memory_la_SOURCES = memory.c
@@ -381,6 +402,20 @@ collectd_LDADD += "-dlopen" nfs.la
 collectd_DEPENDENCIES += nfs.la
 endif
 
+if BUILD_PLUGIN_NGINX
+pkglib_LTLIBRARIES += nginx.la
+nginx_la_SOURCES = nginx.c
+nginx_la_CFLAGS = $(AM_CFLAGS)
+nginx_la_LIBADD =
+nginx_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBCURL
+nginx_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
+nginx_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
+endif
+collectd_LDADD += "-dlopen" nginx.la
+collectd_DEPENDENCIES += nginx.la
+endif
+
 if BUILD_PLUGIN_NTPD
 pkglib_LTLIBRARIES += ntpd.la
 ntpd_la_SOURCES = ntpd.c
@@ -448,10 +483,9 @@ endif
 if BUILD_PLUGIN_SENSORS
 pkglib_LTLIBRARIES += sensors.la
 sensors_la_SOURCES = sensors.c
-sensors_la_LDFLAGS = -module -avoid-version
-if BUILD_WITH_LM_SENSORS
-sensors_la_LDFLAGS += -lsensors
-endif
+sensors_la_CFLAGS = $(BUILD_WITH_LIBSENSORS_CFLAGS)
+sensors_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBSENSORS_LDFLAGS)
+sensors_la_LIBADD = -lsensors
 collectd_LDADD += "-dlopen" sensors.la
 collectd_DEPENDENCIES += sensors.la
 endif
@@ -520,6 +554,14 @@ collectd_LDADD += "-dlopen" tape.la
 collectd_DEPENDENCIES += tape.la
 endif
 
+if BUILD_PLUGIN_TCPCONNS
+pkglib_LTLIBRARIES += tcpconns.la
+tcpconns_la_SOURCES = tcpconns.c
+tcpconns_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" tcpconns.la
+collectd_DEPENDENCIES += tcpconns.la
+endif
+
 if BUILD_PLUGIN_UNIXSOCK
 pkglib_LTLIBRARIES += unixsock.la
 unixsock_la_SOURCES = unixsock.c utils_cmd_putval.h utils_cmd_putval.c
@@ -571,11 +613,15 @@ dist_man_MANS = collectd.1 collectd-nagios.1 collectd.conf.5 \
 
 EXTRA_DIST = types.db
 
+EXTRA_DIST += collectd-email.pod collectd-exec.pod collectd-nagios.pod \
+       collectd-perl.pod collectd-snmp.pod collectd-unixsock.pod \
+       collectd.conf.pod collectd.pod
+
 .pod.1:
-       pod2man --release=$(VERSION) --center=$(PACKAGE) $< >$@
+       pod2man --release=$(VERSION) --center=$(PACKAGE) $< >.pod2man.tmp 2>/dev/null && mv -f .pod2man.tmp $@ || true
 
 .pod.5:
-       pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< >$@
+       pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< >.pod2man.tmp 2>/dev/null && mv -f .pod2man.tmp $@ || true
 
 install-exec-hook:
        $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
index 27d0eed..29ff45b 100644 (file)
@@ -48,7 +48,7 @@ The I<OptionList> is an optional list of I<Options>, where each option if a
 key-value-pair. A list of currently understood options can be found below, all
 other options will be ignored.
 
-I<Valuelist> is a colon-seperated list of the time and the values, each either
+I<Valuelist> is a colon-separated list of the time and the values, each either
 an integer if the data-source is a counter, of a double if the data-source if
 of type "gauge". You can submit an undefined gauge-value by using B<U>. When
 submitting B<U> to a counter the behavior is undefined. The time is given as
index 3e15b88..9707fa3 100644 (file)
@@ -4,29 +4,119 @@ collectd-perl - Documentation of collectd's C<perl plugin>
 
 =head1 SYNOPSIS
 
-  # See collectd.conf(5)
   LoadPlugin perl
   # ...
   <Plugin perl>
     IncludeDir "/path/to/perl/plugins"
     BaseName "Collectd::Plugin"
+    EnableDebugger ""
     LoadPlugin "FooBar"
   </Plugin>
 
 =head1 DESCRIPTION
 
-The C<perl plugin> includes a Perl-interpreter in collectd and provides
-Perl-equivalents of the plugin-functions. This makes it possible to write
-plugins for collectd in Perl. This is a lot more efficient than executing a
+The C<perl plugin> embeds a Perl-interpreter into collectd and provides an
+interface to collectd's plugin system. This makes it possible to write plugins
+for collectd in Perl. This is a lot more efficient than executing a
 Perl-script every time you want to read a value with the C<exec plugin> (see
 L<collectd-exec(5)>) and provides a lot more functionality, too.
 
 Please note that this is still considered to be experimental and subject to
 change between minor releases.
 
+=head1 CONFIGURATION
+
+=over 4
+
+=item B<LoadPlugin> I<Plugin>
+
+Loads the Perl plugin I<Plugin>. This does basically the same as B<use> would
+do in a Perl program. As a side effect, the first occurrence of this option
+causes the Perl-interpreter to be initialized.
+
+=item B<BaseName> I<Name>
+
+Prepends I<Name>B<::> to all plugin names loaded after this option. This is
+provided for convenience to keep plugin names short.
+
+=item B<EnableDebugger> I<Package>[=I<option>,...]
+
+Run collectd under the control of the Perl source debugger. If I<Package> is
+not the empty string, control is passed to the debugging, profiling, or
+tracing module installed as Devel::I<Package>. A comma-separated list of
+options may be specified after the "=" character. Please note that you may not
+leave out the I<Package> option even if you specify B<"">. This is the same as
+using the B<-d:Package> command line option.
+
+See L<perldebug> for detailed documentation about debugging Perl.
+
+=item B<IncludeDir> I<Dir>
+
+Adds I<Dir> to the B<@INC> array. This is the same as using the B<-IDir>
+command line option or B<use lib Dir> in the source code. Please note that it
+only has effect on plugins loaded after this option.
+
+=back
+
+=head1 WRITING YOUR OWN PLUGINS
+
+Writing your own plugins is quite simple. collectd manages plugins by means of
+B<dispatch functions> which call the appropriate B<callback functions>
+registered by the plugins. Any plugin basically consists of the implementation
+of these callback functions and initializing code which registers the
+functions with collectd. See the section "EXAMPLES" below for a really basic
+example. The following types of B<callback functions> are known to collectd
+(all of these are optional):
+
+=over 4
+
+=item init functions
+
+This type of functions is called once after loading the module and before any
+calls to the read and write functions. It should be used to initialize the
+internal state of the plugin (e.E<nbsp>g. open sockets, ...). If the return
+value evaluates to B<false>, the plugin will be disabled.
+
+=item read functions
+
+This type of function is used to collect the actual data. It is called once
+per interval (see the B<Interval> configuration option of collectd). Usually
+it will call B<plugin_dispatch_values> to dispatch the values to collectd
+which will pass them on to all registered B<write functions>. If the return
+value evaluates to B<false> the plugin will be skipped for an increasing
+amount of time until it returns B<true> again.
+
+=item write functions
+
+This type of function is used to write the dispatched values. It is called
+once for each call to B<plugin_dispatch_values>.
+
+=item log functions
+
+This type of function is used to pass messages of plugins or the daemon itself
+to the user.
+
+=item shutdown functions
+
+This type of function is called once before the daemon shuts down. It should
+be used to clean up the plugin (e.g. close sockets, ...).
+
+=back
+
+See the documentation of the B<plugin_register> method in the section
+"METHODS" below for the number and types of arguments passed to each
+B<callback function>. This section also explains how to register B<callback
+functions> with collectd.
+
+To enable a plugin, copy it to a place where Perl can find it (i.E<nbsp>e. a
+directory listed in the B<@INC> array) just as any other Perl plugin and add
+an appropriate B<LoadPlugin> option to the configuration file. After
+restarting collectd you're done.
+
 =head1 DATA TYPES
 
-There are two more complex types you need to know about:
+The following complex types are used to pass values between the Perl plugin
+and collectd:
 
 =over 4
 
@@ -46,10 +136,10 @@ structure. The general layout looks like this:
 =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:
+identify the values, i.E<nbsp>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],
@@ -65,8 +155,7 @@ this:
 =head1 METHODS
 
 The following functions provide the C-interface to Perl-modules. They are
-automatically exported into the module's namespace. You don't need to C<use>
-any special Modules to access them.
+exported by the ":plugin" export tag (see the section "EXPORTS" below).
 
 =over 4
 
@@ -100,7 +189,9 @@ 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.
+data-source. For the exact layout see B<Data-Set> above. Please note that
+there is a large number of predefined data-sets available in the B<types.db>
+file which are automatically registered with collectd.
 
 If the I<type> argument is any of the other types (B<TYPE_INIT>, B<TYPE_READ>,
 ...) then I<data> is expected to be a function reference. These functions are
@@ -149,19 +240,151 @@ registered with the daemon.
 Submits a I<message> of level I<log-level> to collectd's logging mechanism.
 The message is passed to all log-callbacks that are registered with collectd.
 
+=item B<ERROR>, B<WARNING>, B<NOTICE>, B<INFO>, B<DEBUG> (I<message>)
+
+Wrappers around B<plugin_log>, using B<LOG_ERR>, B<LOG_WARNING>,
+B<LOG_NOTICE>, B<LOG_INFO> and B<LOG_DEBUG> respectively as I<log-level>.
+
+=back
+
+=head1 EXPORTS
+
+By default no symbols are exported. However, the following export tags are
+available (B<:all> will export all of them):
+
+=over 4
+
+=item B<:plugin>
+
+=over 4
+
+=item B<plugin_register> ()
+
+=item B<plugin_unregister> ()
+
+=item B<plugin_dispatch_values> ()
+
+=item B<plugin_log> ()
+
+=back
+
+=item B<:types>
+
+=over 4
+
+=item B<TYPE_INIT>
+
+=item B<TYPE_READ>
+
+=item B<TYPE_WRITE>
+
+=item B<TYPE_SHUTDOWN>
+
+=item B<TYPE_LOG>
+
+=back
+
+=item B<:ds_types>
+
+=over 4
+
+=item B<DS_TYPE_COUNTER>
+
+=item B<DS_TYPE_GAUGE>
+
+=back
+
+=item B<:log>
+
+=over 4
+
+=item B<ERROR> ()
+
+=item B<WARNING> ()
+
+=item B<NOTICE> ()
+
+=item B<INFO> ()
+
+=item B<DEBUG> ()
+
+=item B<LOG_ERR>
+
+=item B<LOG_WARNING>
+
+=item B<LOG_NOTICE>
+
+=item B<LOG_INFO>
+
+=item B<LOG_DEBUG>
+
 =back
 
+=back
+
+=head1 EXAMPLES
+
+Any Perl plugin will start similar to:
+
+  package Collectd::Plugins::FooBar;
+
+  use strict;
+  use warnings;
+
+  use Collectd qw( :all );
+
+A very simple read function will look like:
+
+  sub foobar_read
+  {
+    my $vl = { plugin => 'foobar' };
+    $vl->{'values'} = [ rand(42) ];
+    plugin_dispatch_values ('gauge', $vl);
+    return 1;
+  }
+
+A very simple write function will look like:
+
+  sub foobar_write
+  {
+    my ($type, $ds, $vl) = @_;
+    for (my $i = 0; $i < scalar (@$ds); ++$i) {
+      print "$vl->{'plugin'} ($vl->{'type'}): $vl->{'values'}->[$i]\n";
+    }
+    return 1;
+  }
+
+To register those functions with collectd:
+
+  plugin_register (TYPE_READ, "foobar", \&foobar_read);
+  plugin_register (TYPE_WRITE, "foobar", \&foobar_write);
+
+See the section "DATA TYPES" above for a complete documentation of the data
+types used by the read and write functions.
+
+=head1 BUGS
+
+This plugin does not yet work correctly if collectd uses multiple threads.
+Perl does not allow multiple threads to access a single interpreter at the
+same time. As a temporary workaround you should use a single read thread only
+(see collectd's B<ReadThread> configuration option).
+
 =head1 SEE ALSO
 
 L<collectd(1)>,
 L<collectd.conf(5)>,
 L<collectd-exec(5)>,
-L<perl(1)>
+L<perl(1)>,
+L<perldebug(1)>
 
 =head1 AUTHOR
 
-The C<perl plugin> has been written by Sebastian Harl E<lt>shE<nbsp>atE<nbsp>tokkee.orgE<gt>.
+The C<perl plugin> has been written by Sebastian Harl
+E<lt>shE<nbsp>atE<nbsp>tokkee.orgE<gt>.
 
-This manpage has been written by Florian Forster E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>.
+This manpage has been written by Florian Forster
+E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt> and Sebastian Harl
+E<lt>shE<nbsp>atE<nbsp>tokkee.orgE<gt>.
 
 =cut
+
index 00d41e5..ea49029 100644 (file)
@@ -11,12 +11,14 @@ collectd-snmp - Documentation of collectd's C<snmp plugin>
       Type "voltage"
       Table false
       Instance "input_line1"
+      Scale 0.1
       Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1"
     </Data>
     <Data "hr_users">
       Type "users"
       Table false
       Instance ""
+      Shift -1
       Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0"
     </Data>
     <Data "std_traffic">
@@ -128,14 +130,31 @@ Sets the type-instance of the values that are dispatched. The meaning of this
 setting depends on whether B<Table> is set to I<true> or I<false>:
 
 If B<Table> is set to I<true>, I<Instance> is interpreted as an SNMP-prefix
-that will return a list of strings. Those strings are then used as the actual
+that will return a list of values. Those values are then used as the actual
 type-instance. An example would be the C<IF-MIB::ifDescr> subtree.
 L<variables(5)> from the SNMP distribution describes the format of OIDs.
 
+If B<Table> is set to I<true> and B<Instance> is omitted, then "SUBID" will be
+used as the instance.
+
 If B<Table> is set to I<false> the actual string configured for I<Instance> is
 copied into the value-list. In this case I<Instance> may be empty, i.E<nbsp>e.
 "".
 
+=item B<InstancePrefix> I<String>
+
+If B<Table> is set to I<true>, you may feel the need to add something to the
+instance of the files. If set, I<String> is prepended to the instance as
+determined by querying the agent. When B<Table> is set to I<false> this option
+has no effect.
+
+The C<UPS-MIB> is an example where you need this setting: It has voltages of
+the inlets, outlets and the battery of an UPS. However, it doesn't provide a
+descriptive column for these voltages. In this case having 1, 2,E<nbsp>... as
+instances is not enough, because the inlet voltages and outlet voltages may
+both have the subids 1, 2,E<nbsp>... You can use this setting to distinguish
+between the different voltages.
+
 =item B<Values> I<OID> [I<OID> ...]
 
 Configures the values to be queried from the SNMP host. The meaning slightly
@@ -151,6 +170,24 @@ If B<Table> is set to I<false>, each I<OID> must be the OID of exactly one
 value, e.E<nbsp>g. C<IF-MIB::ifInOctets.3> for the third counter of incoming
 traffic.
 
+=item B<Scale> I<Value>
+
+The gauge-values returned by the SNMP-agent are multiplied by I<Value>.  This
+is useful when values are transfered as a fixed point real number. For example,
+thermometers may transfer B<243> but actually mean B<24.3>, so you can specify
+a scale value of B<0.1> to correct this. The default value is of course B<1.0>.
+
+This value is not applied to counter-values.
+
+=item B<Shift> I<Value>
+
+I<Value> is added to gauge-values returned by the SNMP-agent after they have
+been multiplied by any B<Scale> value. If, for example, a thermometer returns
+degrees Kelvin you could specify a shift of B<273.15> here to store values in
+degrees Celsius. The default value is is course B<0.0>.
+
+This value is not applied to counter-values.
+
 =back
 
 =head2 The Host block
index 1be0654..5769329 100644 (file)
@@ -52,13 +52,13 @@ Example:
 
 =item B<LISTVAL>
 
-Returnes a list of the values available in the value cache together with the
+Returns a list of the values available in the value cache together with the
 time of the last update, so that querying applications can issue a B<GETVAL>
 command for the values that have changed.
 
 The first line's status number is the number of identifiers returned or less
-than zero if an error occured. Each of the following lines containes the
-update time as an epoch value and the identifier, seperated by a space.
+than zero if an error occurred. Each of the following lines contains the
+update time as an epoch value and the identifier, separated by a space.
 
 Example:
   -> | LISTVAL
@@ -83,7 +83,7 @@ The I<OptionList> is an optional list of I<Options>, where each option if a
 key-value-pair. A list of currently understood options can be found below, all
 other options will be ignored.
 
-I<Valuelist> is a colon-seperated list of the time and the values, each either
+I<Valuelist> is a colon-separated list of the time and the values, each either
 an integer if the data-source is a counter, of a double if the data-source if
 of type "gauge". You can submit an undefined gauge-value by using B<U>. When
 submitting B<U> to a counter the behavior is undefined. The time is given as
index 5bb1fc7..58de525 100644 (file)
 @BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
 @BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
 @BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables
+@BUILD_PLUGIN_IPVS_TRUE@LoadPlugin ipvs
 @BUILD_PLUGIN_IRQ_TRUE@LoadPlugin irq
 @BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load
 @BUILD_PLUGIN_LOGFILE_TRUE@LoadPlugin logfile
 @BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon
+@BUILD_PLUGIN_MEMCACHED_TRUE@LoadPlugin memcached
 @BUILD_PLUGIN_MEMORY_TRUE@LoadPlugin memory
 @BUILD_PLUGIN_MULTIMETER_TRUE@LoadPlugin multimeter
 @BUILD_PLUGIN_MYSQL_TRUE@LoadPlugin mysql
 @BUILD_PLUGIN_NETLINK_TRUE@LoadPlugin netlink
 @BUILD_PLUGIN_NETWORK_TRUE@LoadPlugin network
 @BUILD_PLUGIN_NFS_TRUE@LoadPlugin nfs
+@BUILD_PLUGIN_NGINX_TRUE@LoadPlugin nginx
 @BUILD_PLUGIN_NTPD_TRUE@LoadPlugin ntpd
 @BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut
 @BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl
@@ -50,6 +53,7 @@
 @BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap
 @BUILD_PLUGIN_SYSLOG_TRUE@LoadPlugin syslog
 @BUILD_PLUGIN_TAPE_TRUE@LoadPlugin tape
+@BUILD_PLUGIN_TCPCONNS_TRUE@LoadPlugin tcpconns
 @BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock
 @BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
 @BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
 
 #<Plugin hddtemp>
 #      Host "127.0.0.1"
-#      Port 7634
+#      Port "7634"
 #</Plugin>
 
 #<Plugin interface>
 
 #<Plugin mbmon>
 #      Host "127.0.0.1"
-#      Port 411
+#      Port "411"
+#</Plugin>
+
+#<Plugin memcached>
+#      Host "127.0.0.1"
+#      Port "11211"
 #</Plugin>
 
 #<Plugin mysql>
 #      CacheFlush 1800
 #</Plugin>
 
+#<Plugin nginx>
+#      URL "http://localhost/status?auto"
+#      User "www-user"
+#      Password "secret"
+#      CACert "/etc/ssl/ca.crt"
+#</Plugin>
+
 #<Plugin ntpd>
 #      Host "localhost"
 #      Port 123
 #      LogLevel info
 #</Plugin>
 
+#<Plugin tcpconns>
+#      ListeningPorts false
+#      LocalPort "25"
+#      RemotePort "25"
+#</Plugin>
+
 #<Plugin unixsock>
 #      SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock"
 #      SocketGroup "collectd"
index 3ff24bb..72c2430 100644 (file)
@@ -24,7 +24,7 @@ B<collectd> behaves. The most significant option is B<LoadPlugin>, which
 controls which plugins to load. These plugins ultimately define collectd's
 behavior.
 
-The syntax of this config file is similar to the config file of the famos
+The syntax of this config file is similar to the config file of the famous
 B<Apache Webserver>. Each line contains either a key-value-pair or a
 section-start or -end. Empty lines and everything after the hash-symbol `#' is
 ignored. Values are either string, enclosed in double-quotes,
@@ -53,6 +53,15 @@ directory for the daemon.
 Loads the plugin I<Plugin>. There must be at least one such line or B<collectd>
 will be mostly useless.
 
+=item B<Include> I<File>
+
+Includes the file I<File> as if it was copy and pasted here. To prevent loops
+and shooting yourself in the foot in interesting ways the nesting is limited to
+a depth of 8E<nbsp>levels, which should be sufficient for most uses.
+
+It is no problem to have a block like C<E<lt>Plugin fooE<gt>> in more than one
+file, but you cannot include files from within blocks.
+
 =item B<PIDFile> I<File>
 
 Sets where to write the PID file to. This file is overwritten when it exists
@@ -371,6 +380,10 @@ B<stderr> can be used to write to the standard output and standard error
 channels, respectively. This, of course, only makes much sense when collectd is
 running in foreground- or non-daemon-mode.
 
+=item B<Timestamp> B<true>|B<false>
+
+Prefix all lines printed by the current time. Defaults to B<true>.
+
 =back
 
 =head2 Plugin C<mbmon>
@@ -398,6 +411,24 @@ TCP-Port to connect to. Defaults to B<411>.
 
 =back
 
+=head2 Plugin C<memcached>
+
+The C<memcached plugin> connects to a memcached server and queries statistics
+about cache utilization, memory and bandwidth used.
+L<http://www.danga.com/memcached/>
+
+=over 4
+
+=item B<Host> I<Hostname>
+
+Hostname to connect to. Defaults to B<127.0.0.1>.
+
+=item B<Port> I<Port>
+
+TCP-Port to connect to. Defaults to B<11211>.
+
+=back
+
 =head2 Plugin C<mysql>
 
 The C<mysql plugin> requires B<mysqlclient> to be installed. It connects to the
@@ -569,6 +600,39 @@ either.
 
 =back
 
+=head2 Plugin C<nginx>
+
+This plugin collects the number of connections and requests handled by the
+C<nginx daemon> (speak: engineE<nbsp>X), a HTTP and mail server/proxy. It
+queries the page provided by the C<ngx_http_stub_status_module> module, which
+isn't compiled by default. Please refer to
+L<http://wiki.codemongers.com/NginxStubStatusModule> for more information on
+how to compile and configure nginx and this module.
+
+The following options are accepted by the C<nginx plugin>:
+
+=over 4
+
+=item B<URL> I<http://host/nginx_status>
+
+Sets the URL of the C<ngx_http_stub_status_module> output.
+
+=item B<User> I<Username>
+
+Optional user name needed for authentication.
+
+=item B<Password> I<Password>
+
+Optional password needed for authentication.
+
+=item B<CACert> I<File>
+
+File that holds one or more SSL certificates. If you want to use HTTPS you will
+possibly need this option. What CA certificates come bundled with C<libcurl>
+and are checked by default depends on the distribution you use.
+
+=back
+
 =head2 Plugin C<ntpd>
 
 =over 4
@@ -596,24 +660,8 @@ L<upsc(8)>.
 
 =head2 Plugin C<perl>
 
-=over 4
-
-=item B<LoadPlugin> I<Plugin>
-
-Loads the Perl plugin I<Plugin>. This does basically the same as B<use> would
-do in a Perl program.
-
-=item B<BaseName> I<Name>
-
-Prepends I<Name>B<::> to all plugin names loaded after this option. This is
-provided for convenience to keep plugin names short.
-
-=item B<IncludeDir> I<Dir>
-
-Adds I<Dir> to the B<@INC> array. This is the same as using the B<-IDir>
-command line option or B<use lib Dir> in the source code.
-
-=back
+This plugin embeds a Perl-interpreter into collectd and provides an interface
+to collectd's plugin system. See L<collectd-perl(5)> for its documentation.
 
 =head2 Plugin C<ping>
 
@@ -774,6 +822,44 @@ debugging support.
 
 =back
 
+=head2 Plugin C<tcpconns>
+
+The C<tcpconns plugin> counts the number of currently established TCP
+connections based on the local port and/or the remote port. Since there may be
+a lot of connections the default if to count all connections with a local port,
+for which a listening socket is opened. You can use the following options to
+fine-tune the ports you are interested in:
+
+=over 4
+
+=item B<ListeningPorts> I<true>|I<false>
+
+If this option is set to I<true>, statistics for all local ports for which a
+listening socket exists are collected. The default depends on B<LocalPort> and
+B<RemotePort> (see below): If no port at all is specifically selected, the
+default is to collect listening ports. If specific ports (no matter if local or
+remote ports) are selected, this option defaults to I<false>, i.E<nbsp>e. only
+the selected ports will be collected unless this option is set to I<true>
+specifically.
+
+=item B<LocalPort> I<Port>
+
+Count the connections to a specific local port. This can be used to see how
+many connections are handled by a specific daemon, e.E<nbsp>g. the mailserver.
+You have to specify the port in numeric form, so for the mailserver example
+you'd need to set B<25>.
+
+=item B<RemotePort> I<Port>
+
+Count the connections to a specific remote port. This is useful to see how
+much a remote service is used. This is most useful if you want to know how many
+connections a local service has opened to remote services, e.E<nbsp>g. how many
+connections a mail server or news server has to other mail or news servers, or
+how many connections a web proxy holds to web servers. You have to give the
+port in numeric form.
+
+=back
+
 =head2 Plugin C<unixsock>
 
 =over 4
@@ -809,6 +895,7 @@ The B<VServer> homepage can be found at L<http://linux-vserver.org/>.
 
 L<collectd(1)>,
 L<collectd-exec(5)>,
+L<collectd-perl(5)>,
 L<collectd-unixsock(5)>,
 L<hddtemp(8)>,
 L<kstat(3KSTAT)>,
index a5cdb1a..e6b8ce8 100644 (file)
@@ -304,6 +304,128 @@ static int dispatch_block (oconfig_item_t *ci)
        return (0);
 }
 
+#define CF_MAX_DEPTH 8
+static oconfig_item_t *cf_read_file (const char *file, int depth);
+
+static int cf_include_all (oconfig_item_t *root, int depth)
+{
+       int i;
+
+       for (i = 0; i < root->children_num; i++)
+       {
+               oconfig_item_t *new;
+               oconfig_item_t *old;
+
+               /* Ignore all blocks, including `Include' blocks. */
+               if (root->children[i].children_num != 0)
+                       continue;
+
+               if (strcasecmp (root->children[i].key, "Include") != 0)
+                       continue;
+
+               old = root->children + i;
+
+               if ((old->values_num != 1)
+                               || (old->values[0].type != OCONFIG_TYPE_STRING))
+               {
+                       ERROR ("configfile: `Include' needs exactly one string argument.");
+                       continue;
+               }
+
+               new = cf_read_file (old->values[0].value.string, depth + 1);
+               if (new == NULL)
+                       continue;
+
+               /* There are more children now. We need to expand
+                * root->children. */
+               if (new->children_num > 1)
+               {
+                       oconfig_item_t *temp;
+
+                       DEBUG ("configfile: Resizing root-children from %i to %i elements.",
+                                       root->children_num,
+                                       root->children_num + new->children_num - 1);
+
+                       temp = (oconfig_item_t *) realloc (root->children,
+                                       sizeof (oconfig_item_t)
+                                       * (root->children_num + new->children_num - 1));
+                       if (temp == NULL)
+                       {
+                               ERROR ("configfile: realloc failed.");
+                               oconfig_free (new);
+                               continue;
+                       }
+                       root->children = temp;
+               }
+
+               /* Clean up the old include directive while we still have a
+                * valid pointer */
+               DEBUG ("configfile: Cleaning up `old'");
+               /* sfree (old->values[0].value.string); */
+               sfree (old->values);
+
+               /* If there are trailing children and the number of children
+                * changes, we need to move the trailing ones either one to the
+                * front or (new->num - 1) to the back */
+               if (((root->children_num - i) > 1)
+                               && (new->children_num != 1))
+               {
+                       DEBUG ("configfile: Moving trailing children.");
+                       memmove (root->children + i + new->children_num,
+                                       root->children + i + 1,
+                                       sizeof (oconfig_item_t)
+                                       * (root->children_num - (i + 1)));
+               }
+
+               /* Now copy the new children to where the include statement was */
+               if (new->children_num > 0)
+               {
+                       DEBUG ("configfile: Copying new children.");
+                       memcpy (root->children + i,
+                                       new->children,
+                                       sizeof (oconfig_item_t)
+                                       * new->children_num);
+               }
+
+               /* Adjust the number of children and the position in the list. */
+               root->children_num = root->children_num + new->children_num - 1;
+               i = i + new->children_num - 1;
+
+               /* Clean up the `new' struct. We set `new->children' to NULL so
+                * the stuff we've just copied pointers to isn't freed by
+                * `oconfig_free' */
+               DEBUG ("configfile: Cleaning up `new'");
+               sfree (new->values); /* should be NULL anyway */
+               sfree (new);
+               new = NULL;
+       } /* for (i = 0; i < root->children_num; i++) */
+
+       return (0);
+} /* int cf_include_all */
+
+static oconfig_item_t *cf_read_file (const char *file, int depth)
+{
+       oconfig_item_t *root;
+
+       if (depth >= CF_MAX_DEPTH)
+       {
+               ERROR ("configfile: Not including `%s' because the maximum nesting depth has been reached.",
+                               file);
+               return (NULL);
+       }
+
+       root = oconfig_parse_file (file);
+       if (root == NULL)
+       {
+               ERROR ("configfile: Cannot read file `%s'.", file);
+               return (NULL);
+       }
+
+       cf_include_all (root, depth);
+
+       return (root);
+} /* oconfig_item_t *cf_read_file */
+
 /* 
  * Public functions
  */
@@ -445,7 +567,7 @@ int cf_read (char *filename)
        oconfig_item_t *conf;
        int i;
 
-       conf = oconfig_parse_file (filename);
+       conf = cf_read_file (filename, 0 /* depth */);
        if (conf == NULL)
        {
                ERROR ("Unable to read config file %s.", filename);
index f5a544a..869b7c3 100644 (file)
@@ -39,6 +39,8 @@
 
 #include "configfile.h"
 
+#include <stddef.h>
+
 #if HAVE_LIBPTHREAD
 # include <pthread.h>
 #endif
diff --git a/src/ipvs.c b/src/ipvs.c
new file mode 100644 (file)
index 0000000..68a3d7f
--- /dev/null
@@ -0,0 +1,346 @@
+/**
+ * collectd - src/ipvs.c (based on ipvsadm and libipvs)
+ * Copyright (C) 1997  Steven Clarke <steven@monmouth.demon.co.uk>
+ * Copyright (C) 1998-2004  Wensong Zhang <wensong@linuxvirtualserver.org>
+ * Copyright (C) 2003-2004  Peter Kese <peter.kese@ijs.si>
+ * 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
+ *
+ * Authors:
+ *   Sebastian Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This plugin collects statistics about IPVS connections. It requires Linux
+ * kernels >= 2.6.
+ *
+ * See http://www.linuxvirtualserver.org/software/index.html for more
+ * information about IPVS.
+ */
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+/* this can probably only be found in the kernel sources */
+#if HAVE_NET_IP_VS_H
+# include <net/ip_vs.h>
+#elif HAVE_IP_VS_H
+# include <ip_vs.h>
+#endif /* HAVE_IP_VS_H */
+
+#define log_err(...) ERROR ("ipvs: " __VA_ARGS__)
+#define log_info(...) INFO ("ipvs: " __VA_ARGS__)
+
+/*
+ * private variables
+ */
+static int sockfd = -1;
+
+/*
+ * libipvs API
+ */
+static struct ip_vs_get_services *ipvs_get_services (void)
+{
+       struct ip_vs_getinfo       ipvs_info;
+       struct ip_vs_get_services *ret;
+
+       socklen_t len;
+
+       len = sizeof (ipvs_info);
+
+       if (0 != getsockopt (sockfd, IPPROTO_IP, IP_VS_SO_GET_INFO,
+                               (void *)&ipvs_info, &len)) {
+               char errbuf[1024];
+               log_err ("ip_vs_get_services: getsockopt() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return NULL;
+       }
+
+       len = sizeof (*ret) +
+               sizeof (struct ip_vs_service_entry) * ipvs_info.num_services;
+
+       if (NULL == (ret = malloc (len))) {
+               log_err ("ipvs_get_services: Out of memory.");
+               exit (3);
+       }
+
+       ret->num_services = ipvs_info.num_services;
+
+       if (0 != getsockopt (sockfd, IPPROTO_IP, IP_VS_SO_GET_SERVICES,
+                               (void *)ret, &len)) {
+               char errbuf[1024];
+               log_err ("ipvs_get_services: getsockopt failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+
+               free(ret);
+               return NULL;
+       }
+       return ret;
+} /* ipvs_get_services */
+
+static struct ip_vs_get_dests *ipvs_get_dests (struct ip_vs_service_entry *se)
+{
+       struct ip_vs_get_dests *ret;
+       socklen_t len;
+
+       len = sizeof (*ret) + sizeof (struct ip_vs_dest_entry) * se->num_dests;
+
+       if (NULL == (ret = malloc (len))) {
+               log_err ("ipvs_get_dests: Out of memory.");
+               exit (3);
+       }
+
+       ret->fwmark    = se->fwmark;
+       ret->protocol  = se->protocol;
+       ret->addr      = se->addr;
+       ret->port      = se->port;
+       ret->num_dests = se->num_dests;
+
+       if (0 != getsockopt (sockfd, IPPROTO_IP, IP_VS_SO_GET_DESTS,
+                               (void *)ret, &len)) {
+               char errbuf[1024];
+               log_err ("ipvs_get_dests: getsockopt() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               free (ret);
+               return NULL;
+       }
+       return ret;
+} /* ip_vs_get_dests */
+
+/*
+ * collectd plugin API and helper functions
+ */
+static int cipvs_init (void)
+{
+       struct ip_vs_getinfo ipvs_info;
+
+       socklen_t len;
+
+       if (-1 == (sockfd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW))) {
+               char errbuf[1024];
+               log_err ("cipvs_init: socket() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return -1;
+       }
+
+       len = sizeof (ipvs_info);
+
+       if (0 != getsockopt (sockfd, IPPROTO_IP, IP_VS_SO_GET_INFO,
+                               (void *)&ipvs_info, &len)) {
+               char errbuf[1024];
+               log_err ("cipvs_init: getsockopt() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               close (sockfd);
+               sockfd = -1;
+               return -1;
+       }
+
+       /* we need IPVS >= 1.1.4 */
+       if (ipvs_info.version < ((1 << 16) + (1 << 8) + 4)) {
+               log_err ("cipvs_init: IPVS version too old (%d.%d.%d < %d.%d.%d)",
+                               NVERSION (ipvs_info.version), 1, 1, 4);
+               close (sockfd);
+               sockfd = -1;
+               return -1;
+       }
+       else {
+               log_info ("Successfully connected to IPVS %d.%d.%d",
+                               NVERSION (ipvs_info.version));
+       }
+       return 0;
+} /* cipvs_init */
+
+/*
+ * ipvs-<virtual IP>_{UDP,TCP}<port>/<type>-total
+ * ipvs-<virtual IP>_{UDP,TCP}<port>/<type>-<real IP>_<port>
+ */
+
+/* plugin instance */
+static int get_pi (struct ip_vs_service_entry *se, char *pi, size_t size)
+{
+       struct in_addr addr;
+       int len = 0;
+
+       if ((NULL == se) || (NULL == pi))
+               return 0;
+
+       addr.s_addr = se->addr;
+
+       /* inet_ntoa() returns a pointer to a statically allocated buffer
+        * I hope non-glibc systems behave the same */
+       len = snprintf (pi, size, "%s_%s%u", inet_ntoa (addr),
+                       (se->protocol == IPPROTO_TCP) ? "TCP" : "UDP",
+                       ntohs (se->port));
+
+       if ((0 > len) || (size <= len)) {
+               log_err ("plugin instance truncated: %s", pi);
+               return -1;
+       }
+       return 0;
+} /* get_pi */
+
+/* type instance */
+static int get_ti (struct ip_vs_dest_entry *de, char *ti, size_t size)
+{
+       struct in_addr addr;
+       int len = 0;
+
+       if ((NULL == de) || (NULL == ti))
+               return 0;
+
+       addr.s_addr = de->addr;
+
+       /* inet_ntoa() returns a pointer to a statically allocated buffer
+        * I hope non-glibc systems behave the same */
+       len = snprintf (ti, size, "%s_%u", inet_ntoa (addr),
+                       ntohs (de->port));
+
+       if ((0 > len) || (size <= len)) {
+               log_err ("type instance truncated: %s", ti);
+               return -1;
+       }
+       return 0;
+} /* get_ti */
+
+static void cipvs_submit_connections (char *pi, char *ti, counter_t value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = value;
+
+       vl.values     = values;
+       vl.values_len = 1;
+
+       vl.time     = time (NULL);
+       vl.interval = interval_g;
+
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "ipvs");
+       strcpy (vl.plugin_instance, pi);
+       strcpy (vl.type_instance, (NULL != ti) ? ti : "total");
+
+       plugin_dispatch_values ("connections", &vl);
+       return;
+} /* cipvs_submit_connections */
+
+static void cipvs_submit_if (char *pi, char *t, char *ti,
+               counter_t rx, counter_t tx)
+{
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = rx;
+       values[1].counter = tx;
+
+       vl.values     = values;
+       vl.values_len = 2;
+
+       vl.time     = time (NULL);
+       vl.interval = interval_g;
+
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "ipvs");
+       strcpy (vl.plugin_instance, pi);
+       strcpy (vl.type_instance, (NULL != ti) ? ti : "total");
+
+       plugin_dispatch_values (t, &vl);
+       return;
+} /* cipvs_submit_if */
+
+static void cipvs_submit_dest (char *pi, struct ip_vs_dest_entry *de) {
+       struct ip_vs_stats_user stats = de->stats;
+
+       char ti[DATA_MAX_NAME_LEN];
+
+       if (0 != get_ti (de, ti, DATA_MAX_NAME_LEN))
+               return;
+
+       cipvs_submit_connections (pi, ti, stats.conns);
+       cipvs_submit_if (pi, "if_packets", ti, stats.inpkts, stats.outpkts);
+       cipvs_submit_if (pi, "if_octets", ti, stats.inbytes, stats.outbytes);
+       return;
+} /* cipvs_submit_dest */
+
+static void cipvs_submit_service (struct ip_vs_service_entry *se)
+{
+       struct ip_vs_stats_user  stats = se->stats;
+       struct ip_vs_get_dests  *dests = ipvs_get_dests (se);
+
+       char pi[DATA_MAX_NAME_LEN];
+
+       int i = 0;
+
+       if (0 != get_pi (se, pi, DATA_MAX_NAME_LEN))
+               return;
+
+       cipvs_submit_connections (pi, NULL, stats.conns);
+       cipvs_submit_if (pi, "if_packets", NULL, stats.inpkts, stats.outpkts);
+       cipvs_submit_if (pi, "if_octets", NULL, stats.inbytes, stats.outbytes);
+
+       for (i = 0; i < dests->num_dests; ++i)
+               cipvs_submit_dest (pi, &dests->entrytable[i]);
+
+       free (dests);
+       return;
+} /* cipvs_submit_service */
+
+static int cipvs_read (void)
+{
+       struct ip_vs_get_services *services = NULL;
+       int i = 0;
+
+       if (sockfd < 0)
+               return (-1);
+
+       if (NULL == (services = ipvs_get_services ()))
+               return -1;
+
+       for (i = 0; i < services->num_services; ++i)
+               cipvs_submit_service (&services->entrytable[i]);
+
+       free (services);
+       return 0;
+} /* cipvs_read */
+
+static int cipvs_shutdown (void)
+{
+       if (sockfd >= 0)
+               close (sockfd);
+       sockfd = -1;
+
+       return 0;
+} /* cipvs_shutdown */
+
+void module_register (void)
+{
+       plugin_register_init ("ipvs", cipvs_init);
+       plugin_register_read ("ipvs", cipvs_read);
+       plugin_register_shutdown ("ipvs", cipvs_shutdown);
+       return;
+} /* module_register */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
index 102f12b..789cb95 100644 (file)
@@ -35,11 +35,13 @@ static int log_level = LOG_INFO;
 static pthread_mutex_t file_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static char *log_file = NULL;
+static int print_timestamp = 1;
 
 static const char *config_keys[] =
 {
        "LogLevel",
-       "File"
+       "File",
+       "Timestamp"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -68,6 +70,14 @@ static int logfile_config (const char *key, const char *value)
                sfree (log_file);
                log_file = strdup (value);
        }
+       else if (0 == strcasecmp (key, "Timestamp")) {
+               if ((strcasecmp (value, "false") == 0)
+                               || (strcasecmp (value, "no") == 0)
+                               || (strcasecmp (value, "off") == 0))
+                       print_timestamp = 0;
+               else
+                       print_timestamp = 1;
+       }
        else {
                return -1;
        }
@@ -78,10 +88,23 @@ static void logfile_log (int severity, const char *msg)
 {
        FILE *fh;
        int do_close = 0;
+       time_t timestamp_time;
+       struct tm timestamp_tm;
+       char timestamp_str[64];
 
        if (severity > log_level)
                return;
 
+       if (print_timestamp)
+       {
+               timestamp_time = time (NULL);
+               localtime_r (&timestamp_time, &timestamp_tm);
+
+               strftime (timestamp_str, sizeof (timestamp_str), "%Y-%m-%d %H:%M:%S",
+                               &timestamp_tm);
+               timestamp_str[sizeof (timestamp_str) - 1] = '\0';
+       }
+
        pthread_mutex_lock (&file_lock);
 
        if ((log_file == NULL) || (strcasecmp (log_file, "stderr") == 0))
@@ -103,7 +126,11 @@ static void logfile_log (int severity, const char *msg)
        }
        else
        {
-               fprintf (fh, "%s\n", msg);
+               if (print_timestamp)
+                       fprintf (fh, "[%s] %s\n", timestamp_str, msg);
+               else
+                       fprintf (fh, "%s\n", msg);
+
                if (do_close != 0)
                        fclose (fh);
        }
diff --git a/src/memcached.c b/src/memcached.c
new file mode 100644 (file)
index 0000000..a182578
--- /dev/null
@@ -0,0 +1,489 @@
+/**
+ * collectd - src/memcached.c, based on src/hddtemp.c
+ * Copyright (C) 2007  Antony Dovgal
+ * Copyright (C) 2005,2006  Vincent Stehlé
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Antony Dovgal <tony at daylessday dot org>
+ *   Vincent Stehlé <vincent.stehle at free.fr>
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+# include <poll.h>
+# include <netdb.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <netinet/tcp.h>
+
+#define MEMCACHED_DEF_HOST "127.0.0.1"
+#define MEMCACHED_DEF_PORT "11211"
+
+#define MEMCACHED_RETRY_COUNT 100
+
+static const char *config_keys[] =
+{
+       "Host",
+       "Port"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static char *memcached_host = NULL;
+static char memcached_port[16];
+
+static int memcached_query_daemon (char *buffer, int buffer_size) /* {{{ */
+{
+       int fd;
+       ssize_t status;
+       int buffer_fill;
+
+       const char *host;
+       const char *port;
+
+       struct addrinfo  ai_hints;
+       struct addrinfo *ai_list, *ai_ptr;
+       int              ai_return, i = 0;
+
+       memset (&ai_hints, '\0', sizeof (ai_hints));
+       ai_hints.ai_flags    = 0;
+#ifdef AI_ADDRCONFIG
+/*     ai_hints.ai_flags   |= AI_ADDRCONFIG; */
+#endif
+       ai_hints.ai_family   = AF_INET;
+       ai_hints.ai_socktype = SOCK_STREAM;
+       ai_hints.ai_protocol = 0;
+
+       host = memcached_host;
+       if (host == NULL) {
+               host = MEMCACHED_DEF_HOST;
+       }
+
+       port = memcached_port;
+       if (strlen (port) == 0) {
+               port = MEMCACHED_DEF_PORT;
+       }
+
+       if ((ai_return = getaddrinfo (host, port, NULL, &ai_list)) != 0) {
+               char errbuf[1024];
+               ERROR ("memcached: getaddrinfo (%s, %s): %s",
+                               host, port,
+                               (ai_return == EAI_SYSTEM)
+                               ? sstrerror (errno, errbuf, sizeof (errbuf))
+                               : gai_strerror (ai_return));
+               return -1;
+       }
+
+       fd = -1;
+       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
+               /* create our socket descriptor */
+               if ((fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol)) < 0) {
+                       char errbuf[1024];
+                       ERROR ("memcached: socket: %s", sstrerror (errno, errbuf, sizeof (errbuf)));
+                       continue;
+               }
+
+               /* connect to the memcached daemon */
+               if (connect (fd, (struct sockaddr *) ai_ptr->ai_addr, ai_ptr->ai_addrlen)) {
+                       shutdown(fd, SHUT_RDWR);
+                       close(fd);
+                       fd = -1;
+                       continue;
+               }
+
+               /* A socket could be opened and connecting succeeded. We're
+                * done. */
+               break;
+       }
+
+       freeaddrinfo (ai_list);
+
+       if (fd < 0) {
+               ERROR ("memcached: Could not connect to daemon.");
+               return -1;
+       }
+
+       if (send(fd, "stats\r\n", sizeof("stats\r\n") - 1, MSG_DONTWAIT) != (sizeof("stats\r\n") - 1)) {
+               ERROR ("memcached: Could not send command to the memcached daemon.");
+               return -1;
+       }
+
+       {
+               struct pollfd p;
+               int n;
+
+               p.fd = fd;
+               p.events = POLLIN|POLLERR|POLLHUP;
+               p.revents = 0;
+
+               n = poll(&p, 1, 3);
+
+               if (n <= 0) {
+                       ERROR ("memcached: poll() failed or timed out");
+                       return -1;
+               }
+       }
+
+       /* receive data from the memcached daemon */
+       memset (buffer, '\0', buffer_size);
+
+       buffer_fill = 0;
+       while ((status = recv (fd, buffer + buffer_fill, buffer_size - buffer_fill, MSG_DONTWAIT)) != 0) {
+               if (i > MEMCACHED_RETRY_COUNT) {
+                       ERROR("recv() timed out");
+                       break;
+               }
+               i++;
+
+               if (status == -1) {
+                       char errbuf[1024];
+
+                       if (errno == EAGAIN) {
+                               continue;
+                       }
+
+                       ERROR ("memcached: Error reading from socket: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       shutdown(fd, SHUT_RDWR);
+                       close (fd);
+                       return -1;
+               }
+               buffer_fill += status;
+
+               if (buffer_fill > 3 && buffer[buffer_fill-5] == 'E' && buffer[buffer_fill-4] == 'N' && buffer[buffer_fill-3] == 'D') {
+                       /* we got all the data */
+                       break;
+               }
+       }
+
+       if (buffer_fill >= buffer_size) {
+               buffer[buffer_size - 1] = '\0';
+               WARNING ("memcached: Message from memcached has been truncated.");
+       } else if (buffer_fill == 0) {
+               WARNING ("memcached: Peer has unexpectedly shut down the socket. "
+                               "Buffer: `%s'", buffer);
+               shutdown(fd, SHUT_RDWR);
+               close(fd);
+               return -1;
+       }
+
+       shutdown(fd, SHUT_RDWR);
+       close(fd);
+       return 0;
+}
+/* }}} */
+
+static int memcached_config (const char *key, const char *value) /* {{{ */
+{
+       if (strcasecmp (key, "Host") == 0) {
+               if (memcached_host != NULL) {
+                       free (memcached_host);
+               }
+               memcached_host = strdup (value);
+       } else if (strcasecmp (key, "Port") == 0) {
+               int port = (int) (atof (value));
+               if ((port > 0) && (port <= 65535)) {
+                       snprintf (memcached_port, sizeof (memcached_port), "%i", port);
+               } else {
+                       strncpy (memcached_port, value, sizeof (memcached_port));
+               }
+               memcached_port[sizeof (memcached_port) - 1] = '\0';
+       } else {
+               return -1;
+       }
+
+       return 0;
+}
+/* }}} */
+
+static void submit_counter (const char *type, const char *type_inst,
+               counter_t value) /* {{{ */
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "memcached");
+       if (type_inst != NULL)
+       {
+               strncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+               vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+       }
+
+       plugin_dispatch_values (type, &vl);
+} /* void memcached_submit_cmd */
+/* }}} */
+
+static void submit_counter2 (const char *type, const char *type_inst,
+               counter_t value0, counter_t value1) /* {{{ */
+{
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = value0;
+       values[1].counter = value1;
+
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "memcached");
+       if (type_inst != NULL)
+       {
+               strncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+               vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+       }
+
+       plugin_dispatch_values (type, &vl);
+} /* void memcached_submit_cmd */
+/* }}} */
+
+static void submit_gauge (const char *type, const char *type_inst,
+               gauge_t value) /* {{{ */
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "memcached");
+       if (type_inst != NULL)
+       {
+               strncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+               vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+       }
+
+       plugin_dispatch_values (type, &vl);
+}
+/* }}} */
+
+static void submit_gauge2 (const char *type, const char *type_inst,
+               gauge_t value0, gauge_t value1) /* {{{ */
+{
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = value0;
+       values[1].gauge = value1;
+
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "memcached");
+       if (type_inst != NULL)
+       {
+               strncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+               vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+       }
+
+       plugin_dispatch_values (type, &vl);
+}
+/* }}} */
+
+static int memcached_read (void) /* {{{ */
+{
+       char buf[1024];
+       char *fields[3];
+       char *ptr;
+       char *line;
+       char *saveptr;
+       int fields_num;
+
+       gauge_t bytes_used = NAN;
+       gauge_t bytes_total = NAN;
+       counter_t rusage_user = 0;
+       counter_t rusage_syst = 0;
+       counter_t octets_rx = 0;
+       counter_t octets_tx = 0;
+
+       /* get data from daemon */
+       if (memcached_query_daemon (buf, sizeof (buf)) < 0) {
+               return -1;
+       }
+
+#define FIELD_IS(cnst) \
+       (((sizeof(cnst) - 1) == name_len) && (strcmp (cnst, fields[1]) == 0))
+
+    ptr = buf;
+    saveptr = NULL;
+    while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL)
+       {
+               int name_len;
+
+        ptr = NULL;
+
+               fields_num = strsplit(line, fields, 3);
+               if (fields_num != 3)
+                       continue;
+
+               name_len = strlen(fields[1]);
+               if (name_len == 0)
+                       continue;
+
+               /*
+                * For an explanation on these fields please refer to
+                * <http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt>
+                */
+
+               /*
+                * CPU time consumed by the memcached process
+                */
+               if (FIELD_IS ("rusage_user"))
+               {
+                       rusage_user = atoll (fields[2]);
+               }
+               else if (FIELD_IS ("rusage_system"))
+               {
+                       rusage_syst = atoll(fields[2]);
+               }
+       
+               /*
+                * Number of threads of this instance
+                */
+               else if (FIELD_IS ("threads"))
+               {
+                       submit_gauge2 ("ps_count", NULL, NAN, atof (fields[2]));
+               }
+
+               /*
+                * Number of items stored
+                */
+               else if (FIELD_IS ("curr_items"))
+               {
+                       submit_gauge ("memcached_items", "current", atof (fields[2]));
+               }
+/*             
+               else if (FIELD_IS ("total_items"))
+               {
+                       total_items = atoll(fields[2]);
+               }
+ */
+
+               /*
+                * Number of bytes used and available (total - used)
+                */
+               else if (FIELD_IS ("bytes"))
+               {
+                       bytes_used = atof (fields[2]);
+               }
+               else if (FIELD_IS ("limit_maxbytes"))
+               {
+                       bytes_total = atof(fields[2]);
+               }
+
+               /*
+                * Connections
+                */
+               else if (FIELD_IS ("curr_connections"))
+               {
+                       submit_gauge ("memcached_connections", "current", atof (fields[2]));
+               }
+/*
+               else if (FIELD_IS("total_connections"))
+               {
+                       total_connections = atoll(fields[2]);
+               }
+*/
+
+/*
+ * ``Number of connection structures allocated by the server''
+               else if (FIELD_IS ("connection_structures"))
+               {
+                       connection_structures = atof(fields[2]);
+               }
+ */
+
+               /*
+                * Commands
+                */
+               else if ((name_len > 4) && (strncmp (fields[1], "cmd_", 4) == 0))
+               {
+                       const char *name = fields[1] + 4;
+                       submit_counter ("memcached_command", name, atoll (fields[2]));
+               }
+
+               /*
+                * Operations on the cache, i. e. cache hits, cache misses and evictions of items
+                */
+               else if (FIELD_IS ("get_hits"))
+               {
+                       submit_counter ("memcached_ops", "hits", atoll (fields[2]));
+               }
+               else if (FIELD_IS ("get_misses"))
+               {
+                       submit_counter ("memcached_ops", "misses", atoll (fields[2]));
+               }
+               else if (FIELD_IS ("evictions"))
+               {
+                       submit_counter ("memcached_ops", "evictions", atoll (fields[2]));
+               }
+
+               /*
+                * Network traffic
+                */
+               else if (FIELD_IS ("bytes_read"))
+               {
+                       octets_rx = atoll (fields[2]);
+               }
+               else if (FIELD_IS ("bytes_written"))
+               {
+                       octets_tx = atoll (fields[2]);
+               }
+       } /* while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL) */
+
+       if (!isnan (bytes_used) && !isnan (bytes_total) && (bytes_used <= bytes_total))
+               submit_gauge2 ("df", "cache", bytes_used, bytes_total - bytes_used);
+       
+       if ((rusage_user != 0) || (rusage_syst != 0))
+               submit_counter2 ("ps_cputime", NULL, rusage_user, rusage_syst);
+
+       if ((octets_rx != 0) || (octets_tx != 0))
+               submit_counter2 ("memcached_octets", NULL, octets_rx, octets_tx);
+       
+       return 0;
+}
+/* }}} */
+
+void module_register (void) /* {{{ */
+{
+       plugin_register_config ("memcached", memcached_config, config_keys, config_keys_num);
+       plugin_register_read ("memcached", memcached_read);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
+
diff --git a/src/nginx.c b/src/nginx.c
new file mode 100644 (file)
index 0000000..a44e8a5
--- /dev/null
@@ -0,0 +1,252 @@
+/**
+ * collectd - src/nginx.c
+ * Copyright (C) 2006,2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <curl/curl.h>
+
+static char *url    = NULL;
+static char *user   = NULL;
+static char *pass   = NULL;
+static char *cacert = NULL;
+
+static CURL *curl = NULL;
+
+#define ABUFFER_SIZE 16384
+static char nginx_buffer[ABUFFER_SIZE];
+static int  nginx_buffer_len = 0;
+static char nginx_curl_error[CURL_ERROR_SIZE];
+
+static const char *config_keys[] =
+{
+  "URL",
+  "User",
+  "Password",
+  "CACert"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static size_t nginx_curl_callback (void *buf, size_t size, size_t nmemb, void *stream)
+{
+  size_t len = size * nmemb;
+
+  if ((nginx_buffer_len + len) >= ABUFFER_SIZE)
+  {
+    len = (ABUFFER_SIZE - 1) - nginx_buffer_len;
+  }
+
+  if (len <= 0)
+    return (len);
+
+  memcpy (nginx_buffer + nginx_buffer_len, (char *) buf, len);
+  nginx_buffer_len += len;
+  nginx_buffer[nginx_buffer_len] = '\0';
+
+  return (len);
+}
+
+static int config_set (char **var, const char *value)
+{
+  if (*var != NULL)
+  {
+    free (*var);
+    *var = NULL;
+  }
+
+  if ((*var = strdup (value)) == NULL)
+    return (1);
+  else
+    return (0);
+}
+
+static int config (const char *key, const char *value)
+{
+  if (strcasecmp (key, "url") == 0)
+    return (config_set (&url, value));
+  else if (strcasecmp (key, "user") == 0)
+    return (config_set (&user, value));
+  else if (strcasecmp (key, "password") == 0)
+    return (config_set (&pass, value));
+  else if (strcasecmp (key, "cacert") == 0)
+    return (config_set (&cacert, value));
+  else
+    return (-1);
+} /* int config */
+
+static int init (void)
+{
+  static char credentials[1024];
+
+  if (curl != NULL)
+    curl_easy_cleanup (curl);
+
+  if ((curl = curl_easy_init ()) == NULL)
+  {
+    ERROR ("nginx plugin: curl_easy_init failed.");
+    return (-1);
+  }
+
+  curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, nginx_curl_callback);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, nginx_curl_error);
+
+  if (user != NULL)
+  {
+    if (snprintf (credentials, 1024, "%s:%s", user, pass == NULL ? "" : pass) >= 1024)
+    {
+      ERROR ("nginx plugin: Credentials would have been truncated.");
+      return (-1);
+    }
+
+    curl_easy_setopt (curl, CURLOPT_USERPWD, credentials);
+  }
+
+  if (url != NULL)
+  {
+    curl_easy_setopt (curl, CURLOPT_URL, url);
+  }
+
+  if (cacert != NULL)
+  {
+    curl_easy_setopt (curl, CURLOPT_CAINFO, cacert);
+  }
+
+  return (0);
+} /* void init */
+
+static void submit (char *type, char *inst, long long value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  if (strcmp (type, "nginx_connections") == 0)
+    values[0].gauge = value;
+  else if (strcmp (type, "nginx_requests") == 0)
+    values[0].counter = value;
+  else
+    return;
+
+  vl.values = values;
+  vl.values_len = 1;
+  vl.time = time (NULL);
+  strcpy (vl.host, hostname_g);
+  strcpy (vl.plugin, "nginx");
+  strcpy (vl.plugin_instance, "");
+
+  if (inst != NULL)
+  {
+    strncpy (vl.type_instance, inst, sizeof (vl.type_instance));
+    vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+  }
+
+  plugin_dispatch_values (type, &vl);
+} /* void submit */
+
+static int nginx_read (void)
+{
+  int i;
+
+  char *ptr;
+  char *lines[16];
+  int   lines_num = 0;
+
+  char *fields[16];
+  int   fields_num;
+
+  if (curl == NULL)
+    return (-1);
+  if (url == NULL)
+    return (-1);
+
+  nginx_buffer_len = 0;
+  if (curl_easy_perform (curl) != 0)
+  {
+    WARNING ("nginx plugin: curl_easy_perform failed: %s", nginx_curl_error);
+    return (-1);
+  }
+
+  ptr = nginx_buffer;
+  while ((lines[lines_num] = strtok (ptr, "\n\r")) != NULL)
+  {
+    ptr = NULL;
+    lines_num++;
+
+    if (lines_num >= 16)
+      break;
+  }
+
+  /*
+   * Active connections: 291
+   * server accepts handled requests
+   *  16630948 16630948 31070465
+   * Reading: 6 Writing: 179 Waiting: 106
+   */
+  for (i = 0; i < lines_num; i++)
+  {
+    fields_num = strsplit (lines[i], fields,
+       (sizeof (fields) / sizeof (fields[0])));
+
+    if (fields_num == 3)
+    {
+      if ((strcmp (fields[0], "Active") == 0)
+         && (strcmp (fields[1], "connections:") == 0))
+      {
+       submit ("nginx_connections", "active", atoll (fields[2]));
+      }
+      else if ((atoll (fields[0]) != 0)
+         && (atoll (fields[1]) != 0)
+         && (atoll (fields[2]) != 0))
+      {
+       submit ("nginx_requests", NULL, atoll (fields[2]));
+      }
+    }
+    else if (fields_num == 6)
+    {
+      if ((strcmp (fields[0], "Reading:") == 0)
+         && (strcmp (fields[2], "Writing:") == 0)
+         && (strcmp (fields[4], "Waiting:") == 0))
+      {
+       submit ("nginx_connections", "reading", atoll (fields[1]));
+       submit ("nginx_connections", "writing", atoll (fields[3]));
+       submit ("nginx_connections", "waiting", atoll (fields[5]));
+      }
+    }
+  }
+
+  nginx_buffer_len = 0;
+
+  return (0);
+} /* int nginx_read */
+
+void module_register (void)
+{
+  plugin_register_config ("nginx", config, config_keys, config_keys_num);
+  plugin_register_init ("nginx", init);
+  plugin_register_read ("nginx", nginx_read);
+} /* void module_register */
+
+/*
+ * vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
+ */
index f2cb7b6..1ed9d00 100644 (file)
 #define PLUGIN_DATASET  255
 
 #define log_debug(...) DEBUG ("perl: " __VA_ARGS__)
+#define log_info(...) INFO ("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_register_ds);
+static XS (Collectd_plugin_unregister_ds);
 static XS (Collectd_plugin_dispatch_values);
 static XS (Collectd_plugin_log);
 
-
-/*
- * 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",
-       "BaseName",
-       "IncludeDir"
-};
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
-
 static PerlInterpreter *perl = NULL;
 
-static char base_name[DATA_MAX_NAME_LEN] = "";
+static int  perl_argc   = 0;
+static char **perl_argv = NULL;
 
-static char *plugin_types[] = { "init", "read", "write", "shutdown" };
-static HV   *plugins[PLUGIN_TYPES];
-static HV   *data_sets;
+static char base_name[DATA_MAX_NAME_LEN] = "";
 
 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 },
-       { "Collectd::plugin_log",             Collectd_plugin_log },
+       { "Collectd::plugin_register_data_set",   Collectd_plugin_register_ds },
+       { "Collectd::plugin_unregister_data_set", Collectd_plugin_unregister_ds },
+       { "Collectd::plugin_dispatch_values",     Collectd_plugin_dispatch_values },
+       { "Collectd::plugin_log",                 Collectd_plugin_log },
        { "", NULL }
 };
 
+struct {
+       char name[64];
+       int  value;
+} constants[] =
+{
+       { "Collectd::TYPE_INIT",       PLUGIN_INIT },
+       { "Collectd::TYPE_READ",       PLUGIN_READ },
+       { "Collectd::TYPE_WRITE",      PLUGIN_WRITE },
+       { "Collectd::TYPE_SHUTDOWN",   PLUGIN_SHUTDOWN },
+       { "Collectd::TYPE_LOG",        PLUGIN_LOG },
+       { "Collectd::TYPE_DATASET",    PLUGIN_DATASET },
+       { "Collectd::DS_TYPE_COUNTER", DS_TYPE_COUNTER },
+       { "Collectd::DS_TYPE_GAUGE",   DS_TYPE_GAUGE },
+       { "Collectd::LOG_ERR",         LOG_ERR },
+       { "Collectd::LOG_WARNING",     LOG_WARNING },
+       { "Collectd::LOG_NOTICE",      LOG_NOTICE },
+       { "Collectd::LOG_INFO",        LOG_INFO },
+       { "Collectd::LOG_DEBUG",       LOG_DEBUG },
+       { "", 0 }
+};
 
 /*
  * Helper functions for data type conversion.
@@ -175,9 +167,7 @@ static int hv2data_source (HV *hash, data_source_t *ds)
 
 static int av2value (char *name, AV *array, value_t *value, int len)
 {
-       SV **tmp = NULL;
-
-       ds_types_t *ds = NULL;
+       const data_set_t *ds;
 
        int i = 0;
 
@@ -190,23 +180,22 @@ static int av2value (char *name, AV *array, value_t *value, int len)
        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);
+       ds = plugin_get_ds (name);
+       if (NULL == ds) {
+               log_err ("av2value: Unknown dataset \"%s\"", name);
                return -1;
        }
-       ds = (ds_types_t *)SvIV ((SV *)SvRV (*tmp));
 
-       if (ds->len < len) {
+       if (ds->ds_num < len) {
                log_warn ("av2value: Value length exceeds data set length.");
-               len = ds->len;
+               len = ds->ds_num;
        }
 
        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])
+                       if (DS_TYPE_COUNTER == ds->ds[i].type)
                                value[i].counter = SvIV (*tmp);
                        else
                                value[i].gauge = SvNV (*tmp);
@@ -320,7 +309,6 @@ static int value_list2hv (value_list_t *vl, data_set_t *ds, HV *hash)
        return 0;
 } /* static int value2av (value_list_t *, data_set_t *, HV *) */
 
-
 /*
  * Internal functions.
  */
@@ -338,62 +326,6 @@ static char *get_module_name (char *buf, size_t buf_len, const char *module) {
 } /* char *get_module_name */
 
 /*
- * 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)
@@ -404,8 +336,6 @@ static int pplugin_register_data_set (char *name, AV *dataset)
        data_source_t *ds  = NULL;
        data_set_t    *set = NULL;
 
-       ds_types_t *types = NULL;
-
        if ((NULL == name) || (NULL == dataset))
                return -1;
 
@@ -417,10 +347,6 @@ static int pplugin_register_data_set (char *name, AV *dataset)
        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);
 
@@ -435,16 +361,11 @@ static int pplugin_register_data_set (char *name, AV *dataset)
                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';
 
@@ -458,20 +379,8 @@ static int pplugin_register_data_set (char *name, AV *dataset)
  */
 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 *) */
 
@@ -510,6 +419,9 @@ static int pplugin_dispatch_values (char *name, HV *values)
                AV  *array = (AV *)SvRV (*tmp);
                int len    = Perl_av_len (perl, array) + 1;
 
+               if (len <= 0)
+                       return -1;
+
                val = (value_t *)smalloc (len * sizeof (value_t));
 
                list.values_len = av2value (name, (AV *)SvRV (*tmp), val, len);
@@ -559,13 +471,13 @@ static int pplugin_dispatch_values (char *name, HV *values)
 } /* static int pplugin_dispatch_values (char *, HV *) */
 
 /*
- * Call a plugin's working function.
+ * Call all working functions of the given type.
  */
-static int pplugin_call (int type, char *name, SV *sub, va_list ap)
+static int pplugin_call_all (int type, ...)
 {
        int retvals = 0;
-       I32 xflags  = G_NOARGS;
 
+       va_list ap;
        int ret = 0;
 
        dSP;
@@ -573,11 +485,15 @@ static int pplugin_call (int type, char *name, SV *sub, va_list ap)
        if ((type < 0) || (type >= PLUGIN_TYPES))
                return -1;
 
+       va_start (ap, type);
+
        ENTER;
        SAVETMPS;
 
        PUSHMARK (SP);
 
+       XPUSHs (sv_2mortal (Perl_newSViv (perl, (IV)type)));
+
        if (PLUGIN_WRITE == type) {
                /*
                 * $_[0] = $plugin_type;
@@ -621,8 +537,6 @@ static int pplugin_call (int type, char *name, SV *sub, va_list ap)
                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) {
                /*
@@ -632,27 +546,14 @@ static int pplugin_call (int type, char *name, SV *sub, va_list ap)
                 */
                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 %s::%s->%s()",
-                               base_name, name, plugin_types[type]);
-
-       retvals = Perl_call_sv (perl, sub, G_SCALAR | xflags);
+       retvals = Perl_call_pv (perl, "Collectd::plugin_call_all", G_SCALAR);
 
        SPAGAIN;
-       if (1 > retvals) {
-               if (PLUGIN_LOG != type)
-                       log_warn ("pplugin_call: "
-                                       "%s::%s->%s() returned void - assuming true",
-                                       base_name, name, plugin_types[type]);
-       }
-       else {
+       if (0 < retvals) {
                SV *tmp = POPs;
                if (! SvTRUE (tmp))
                        ret = -1;
@@ -661,125 +562,48 @@ static int pplugin_call (int type, char *name, SV *sub, va_list ap)
        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 ("%s->read() failed. Will suspend it for %i seconds.",
-                                       plugin, p->wait_left);
-               }
-               else if (PLUGIN_INIT == type) {
-                       int i = 0;
-
-                       log_err ("%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 ("%s->%s() failed with status %i.",
-                                       plugin, plugin_types[type], status);
-               }
-
-               va_end (ap);
-       }
-       return 0;
+       va_end (ap);
+       return ret;
 } /* static int pplugin_call_all (int, ...) */
 
-
 /*
  * Exported Perl API.
  */
 
 /*
- * Collectd::plugin_register (type, name, data).
+ * Collectd::plugin_register_data_set (type, dataset).
  *
  * type:
- *   init, read, write, shutdown, data set
- *
- * name:
- *   name of the plugin
+ *   type of the dataset
  *
- * data:
- *   reference to the plugin's subroutine that does the work or the data set
- *   definition
+ * dataset:
+ *   dataset to be registered
  */
-static XS (Collectd_plugin_register)
+static XS (Collectd_plugin_register_ds)
 {
-       int type  = 0;
        SV  *data = NULL;
-
-       int ret = 0;
+       int ret   = 0;
 
        dXSARGS;
 
-       if (3 != items) {
-               log_err ("Usage: Collectd::plugin_register(type, name, data)");
+       if (2 != items) {
+               log_err ("Usage: Collectd::plugin_register_data_set(type, dataset)");
                XSRETURN_EMPTY;
        }
 
-       log_debug ("Collectd::plugin_register: "
-                       "type = \"%i\", name = \"%s\", \"%s\"",
-                       (int)SvIV (ST (0)), SvPV_nolen (ST (1)), SvPV_nolen (ST (2)));
+       log_debug ("Collectd::plugin_register_data_set: "
+                       "type = \"%s\", dataset = \"%s\"",
+                       SvPV_nolen (ST (0)), SvPV_nolen (ST (1)));
 
-       type = (int)SvIV (ST (0));
-       data = ST (2);
+       data = ST (1);
 
-       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)),
+       if (SvROK (data) && (SVt_PVAV == SvTYPE (SvRV (data)))) {
+               ret = pplugin_register_data_set (SvPV_nolen (ST (0)),
                                (AV *)SvRV (data));
        }
        else {
-               log_err ("Collectd::plugin_register: Invalid data.");
+               log_err ("Collectd::plugin_register_data_set: Invalid data.");
                XSRETURN_EMPTY;
        }
 
@@ -787,50 +611,31 @@ static XS (Collectd_plugin_register)
                XSRETURN_YES;
        else
                XSRETURN_EMPTY;
-} /* static XS (Collectd_plugin_register) */
+} /* static XS (Collectd_plugin_register_ds) */
 
 /*
- * Collectd::plugin_unregister (type, name).
+ * Collectd::plugin_unregister_data_set (type).
  *
  * type:
- *   init, read, write, shutdown, data set
- *
- * name:
- *   name of the plugin
+ *   type of the dataset
  */
-static XS (Collectd_plugin_unregister)
+static XS (Collectd_plugin_unregister_ds)
 {
-       int type = 0;
-       int ret  = 0;
-
        dXSARGS;
 
-       if (2 != items) {
-               log_err ("Usage: Collectd::plugin_unregister(type, name)");
+       if (1 != items) {
+               log_err ("Usage: Collectd::plugin_unregister_data_set(type)");
                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;
-       }
+       log_debug ("Collectd::plugin_unregister_data_set: type = \"%s\"",
+                       SvPV_nolen (ST (0)));
 
-       if (0 == ret)
+       if (0 == pplugin_unregister_data_set (SvPV_nolen (ST (1))))
                XSRETURN_YES;
        else
                XSRETURN_EMPTY;
-} /* static XS (Collectd_plugin_unregister) */
+} /* static XS (Collectd_plugin_register_ds) */
 
 /*
  * Collectd::plugin_dispatch_values (name, values).
@@ -894,108 +699,18 @@ static XS (Collectd_plugin_log)
                XSRETURN_EMPTY;
        }
 
-       log_debug ("Collectd::plugin_log: level = %i, message = \"%s\"",
-                       SvIV (ST (0)), SvPV_nolen (ST (1)));
        plugin_log (SvIV (ST (0)), SvPV_nolen (ST (1)));
        XSRETURN_YES;
 } /* static XS (Collectd_plugin_log) */
 
 /*
- * 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)
-{
-       assert (NULL != perl);
-
-       log_debug ("perl_config: key = \"%s\", value=\"%s\"", key, value);
-
-       if (0 == strcasecmp (key, "LoadPlugin")) {
-               char module_name[DATA_MAX_NAME_LEN];
-
-               if (get_module_name (module_name, sizeof (module_name), value)
-                               == NULL) {
-                       log_err ("Invalid module name %s", value);
-                       return (1);
-               } /* if (get_module_name == NULL) */
-
-               log_debug ("perl_config: loading perl plugin \"%s\"", value);
-               Perl_load_module (perl, PERL_LOADMOD_NOIMPORT,
-                               Perl_newSVpv (perl, module_name, strlen (module_name)),
-                               Nullsv);
-       }
-       else if (0 == strcasecmp (key, "BaseName")) {
-               log_debug ("perl_config: Setting plugin basename to \"%s\"", value);
-               strncpy (base_name, value, sizeof (base_name));
-               base_name[sizeof (base_name) - 1] = '\0';
-       }
-       else if (0 == strcasecmp (key, "IncludeDir")) {
-               Perl_av_unshift (perl, GvAVn (PL_incgv), 1);
-               Perl_av_store (perl, GvAVn (PL_incgv),
-                               0, Perl_newSVpv (perl, value, strlen (value)));
-       }
-       else {
-               return -1;
-       }
-       return 0;
-} /* static int perl_config (char *, char *) */
-
 static int perl_init (void)
 {
-       assert (NULL != perl);
+       if (NULL == perl)
+               return 0;
 
        PERL_SET_CONTEXT (perl);
        return pplugin_call_all (PLUGIN_INIT);
@@ -1003,7 +718,8 @@ static int perl_init (void)
 
 static int perl_read (void)
 {
-       assert (NULL != perl);
+       if (NULL == perl)
+               return 0;
 
        PERL_SET_CONTEXT (perl);
        return pplugin_call_all (PLUGIN_READ);
@@ -1011,7 +727,8 @@ static int perl_read (void)
 
 static int perl_write (const data_set_t *ds, const value_list_t *vl)
 {
-       assert (NULL != perl);
+       if (NULL == perl)
+               return 0;
 
        PERL_SET_CONTEXT (perl);
        return pplugin_call_all (PLUGIN_WRITE, ds, vl);
@@ -1019,7 +736,8 @@ static int perl_write (const data_set_t *ds, const value_list_t *vl)
 
 static void perl_log (int level, const char *msg)
 {
-       assert (NULL != perl);
+       if (NULL == perl)
+               return;
 
        PERL_SET_CONTEXT (perl);
        pplugin_call_all (PLUGIN_LOG, level, msg);
@@ -1028,44 +746,21 @@ static void perl_log (int level, const char *msg)
 
 static int perl_shutdown (void)
 {
-       int i   = 0;
        int ret = 0;
 
+       plugin_unregister_complex_config ("perl");
+
+       if (NULL == perl)
+               return 0;
+
        plugin_unregister_log ("perl");
-       plugin_unregister_config ("perl");
        plugin_unregister_init ("perl");
        plugin_unregister_read ("perl");
        plugin_unregister_write ("perl");
 
-       assert (NULL != perl);
-
        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 */
@@ -1080,31 +775,47 @@ static int perl_shutdown (void)
        return ret;
 } /* static void perl_shutdown (void) */
 
+/* bootstrap the Collectd module */
 static void xs_init (pTHX)
 {
-       char *file = __FILE__;
+       HV   *stash = NULL;
+       char *file  = __FILE__;
 
-       dXSUB_SYS;
+       int i = 0;
 
-       /* build the Collectd module into the perl interpreter */
-       Perl_newXS (perl, "Collectd::bootstrap", boot_Collectd, file);
+       dXSUB_SYS;
 
        /* enable usage of Perl modules using shared libraries */
        Perl_newXS (perl, "DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+
+       /* 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; '\0' != constants[i].name[0]; ++i)
+               Perl_newCONSTSUB (perl, stash, constants[i].name,
+                               Perl_newSViv (perl, constants[i].value));
        return;
 } /* static void xs_init (pTHX) */
 
-/*
- * Create the perl interpreter and register it with collectd.
- */
-void module_register (void)
+/* Initialize the global Perl interpreter. */
+static int init_pi (int argc, char **argv)
 {
-       char *embed_argv[] = { "", "-e", "bootstrap Collectd \""VERSION"\"", NULL };
-       int  embed_argc    = 3;
+       if (NULL != perl)
+               return 0;
 
-       int i = 0;
+       log_info ("Initializing Perl interpreter...");
+#if COLLECT_DEBUG
+       {
+               int i = 0;
 
-       log_debug ("module_register: Registering perl plugin...");
+               for (i = 0; i < argc; ++i)
+                       log_debug ("argv[%i] = \"%s\"", i, argv[i]);
+       }
+#endif /* COLLECT_DEBUG */
 
        PERL_SYS_INIT3 (&argc, &argv, &environ);
 
@@ -1116,25 +827,179 @@ void module_register (void)
 
        PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
 
-       if (0 != perl_parse (perl, xs_init, embed_argc, embed_argv, NULL)) {
+       if (0 != perl_parse (perl, xs_init, argc, 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);
+       /* Set $0 to "collectd" because perl_parse() has to set it to "-e". */
+       Perl_sv_setpv (perl, Perl_get_sv (perl, "0", 0), "collectd");
 
-       data_sets = Perl_newHV (perl);
+       perl_run (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 0;
+} /* static int init_pi (const char **, const int) */
+
+/*
+ * LoadPlugin "<Plugin>"
+ */
+static int perl_config_loadplugin (oconfig_item_t *ci)
+{
+       char module_name[DATA_MAX_NAME_LEN];
+
+       char *value = NULL;
+
+       if ((0 != ci->children_num) || (1 != ci->values_num)
+                       || (OCONFIG_TYPE_STRING != ci->values[0].type))
+               return 1;
+
+       value = ci->values[0].value.string;
+
+       if (NULL == get_module_name (module_name, sizeof (module_name), value)) {
+               log_err ("Invalid module name %s", value);
+               return (1);
+       }
+
+       init_pi (perl_argc, perl_argv);
+
+       log_debug ("perl_config: loading perl plugin \"%s\"", value);
+       Perl_load_module (perl, PERL_LOADMOD_NOIMPORT,
+                       Perl_newSVpv (perl, module_name, strlen (module_name)),
+                       Nullsv);
+       return 0;
+} /* static int perl_config_loadplugin (oconfig_item_it *) */
+
+/*
+ * BaseName "<Name>"
+ */
+static int perl_config_basename (oconfig_item_t *ci)
+{
+       char *value = NULL;
+
+       if ((0 != ci->children_num) || (1 != ci->values_num)
+                       || (OCONFIG_TYPE_STRING != ci->values[0].type))
+               return 1;
+
+       value = ci->values[0].value.string;
+
+       log_debug ("perl_config: Setting plugin basename to \"%s\"", value);
+       strncpy (base_name, value, sizeof (base_name));
+       base_name[sizeof (base_name) - 1] = '\0';
+       return 0;
+} /* static int perl_config_basename (oconfig_item_it *) */
+
+/*
+ * EnableDebugger "<Package>"|""
+ */
+static int perl_config_enabledebugger (oconfig_item_t *ci)
+{
+       char *value = NULL;
+
+       if ((0 != ci->children_num) || (1 != ci->values_num)
+                       || (OCONFIG_TYPE_STRING != ci->values[0].type))
+               return 1;
+
+       value = ci->values[0].value.string;
+
+       perl_argv = (char **)realloc (perl_argv,
+                       (++perl_argc + 1) * sizeof (char *));
+
+       if (NULL == perl_argv) {
+               log_err ("perl_config: Not enough memory.");
+               exit (3);
+       }
+
+       if ('\0' == value[0]) {
+               perl_argv[perl_argc - 1] = "-d";
+       }
+       else {
+               perl_argv[perl_argc - 1] = (char *)smalloc (strlen (value) + 4);
+               sstrncpy (perl_argv[perl_argc - 1], "-d:", 4);
+               sstrncpy (perl_argv[perl_argc - 1] + 3, value, strlen (value) + 1);
+       }
+
+       perl_argv[perl_argc] = NULL;
+       return 0;
+} /* static int perl_config_enabledebugger (oconfig_item_it *) */
+
+/*
+ * IncludeDir "<Dir>"
+ */
+static int perl_config_includedir (oconfig_item_t *ci)
+{
+       char *value = NULL;
+
+       if ((0 != ci->children_num) || (1 != ci->values_num)
+                       || (OCONFIG_TYPE_STRING != ci->values[0].type))
+               return 1;
+
+       value = ci->values[0].value.string;
+
+       if (NULL == perl) {
+               perl_argv = (char **)realloc (perl_argv,
+                               (++perl_argc + 1) * sizeof (char *));
+
+               if (NULL == perl_argv) {
+                       log_err ("perl_config: Not enough memory.");
+                       exit (3);
+               }
+
+               perl_argv[perl_argc - 1] = (char *)smalloc (strlen (value) + 3);
+               sstrncpy(perl_argv[perl_argc - 1], "-I", 3);
+               sstrncpy(perl_argv[perl_argc - 1] + 2, value, strlen (value) + 1);
+
+               perl_argv[perl_argc] = NULL;
+       }
+       else {
+               /* prepend the directory to @INC */
+               Perl_av_unshift (perl, GvAVn (PL_incgv), 1);
+               Perl_av_store (perl, GvAVn (PL_incgv),
+                               0, Perl_newSVpv (perl, value, strlen (value)));
+       }
+       return 0;
+} /* static int perl_config_includedir (oconfig_item_it *) */
+
+static int perl_config (oconfig_item_t *ci)
+{
+       int i = 0;
+
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *c = ci->children + i;
+
+               if (0 == strcasecmp (c->key, "LoadPlugin"))
+                       perl_config_loadplugin (c);
+               else if (0 == strcasecmp (c->key, "BaseName"))
+                       perl_config_basename (c);
+               else if (0 == strcasecmp (c->key, "EnableDebugger"))
+                       perl_config_enabledebugger (c);
+               else if (0 == strcasecmp (c->key, "IncludeDir"))
+                       perl_config_includedir (c);
+               else
+                       log_warn ("Ignoring unknown config key \"%s\".", c->key);
+       }
+       return 0;
+} /* static int perl_config (oconfig_item_t *) */
+
+void module_register (void)
+{
+       perl_argc = 4;
+       perl_argv = (char **)smalloc ((perl_argc + 1) * sizeof (char *));
+
+       /* default options for the Perl interpreter */
+       perl_argv[0] = "";
+       perl_argv[1] = "-MCollectd";
+       perl_argv[2] = "-e";
+       perl_argv[3] = "1";
+       perl_argv[4] = NULL;
+
+       plugin_register_complex_config ("perl", perl_config);
        return;
 } /* void module_register (void) */
 
index 79331bf..dc06d68 100644 (file)
@@ -654,6 +654,8 @@ static void *rrd_queue_thread (void *data)
 
                /* Write the values to the RRD-file */
                srrd_update (queue_entry->filename, NULL, values_num, values);
+               DEBUG ("rrdtool plugin: queue thread: Wrote %i values to %s",
+                               values_num, queue_entry->filename);
 
                for (i = 0; i < values_num; i++)
                {
index 3006476..4311ff1 100644 (file)
@@ -51,8 +51,11 @@ struct data_definition_s
   char *type; /* used to find the data_set */
   int is_table;
   instance_t instance;
+  char *instance_prefix;
   oid_t *values;
   int values_len;
+  double scale;
+  double shift;
   struct data_definition_s *next;
 };
 typedef struct data_definition_s data_definition_t;
@@ -124,6 +127,7 @@ static pthread_cond_t  host_cond = PTHREAD_COND_INITIALIZER;
  *  !   +-> csnmp_config_add_data_type
  *  !   +-> csnmp_config_add_data_table
  *  !   +-> csnmp_config_add_data_instance
+ *  !   +-> csnmp_config_add_data_instance_prefix
  *  !   +-> csnmp_config_add_data_values
  *  +-> csnmp_config_add_host
  *      +-> csnmp_config_add_host_address
@@ -149,9 +153,7 @@ static int csnmp_config_add_data_type (data_definition_t *dd, oconfig_item_t *ci
     return (-1);
   }
 
-  if (dd->type != NULL)
-    free (dd->type);
-
+  sfree (dd->type);
   dd->type = strdup (ci->values[0].value.string);
   if (dd->type == NULL)
     return (-1);
@@ -202,6 +204,30 @@ static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t
   return (0);
 } /* int csnmp_config_add_data_instance */
 
+static int csnmp_config_add_data_instance_prefix (data_definition_t *dd,
+    oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("snmp plugin: `InstancePrefix' needs exactly one string argument.");
+    return (-1);
+  }
+
+  if (!dd->is_table)
+  {
+    WARNING ("snmp plugin: data %s: InstancePrefix is ignored when `Table' "
+       "is set to `false'.", dd->name);
+    return (-1);
+  }
+
+  sfree (dd->instance_prefix);
+  dd->instance_prefix = strdup (ci->values[0].value.string);
+  if (dd->instance_prefix == NULL)
+    return (-1);
+
+  return (0);
+} /* int csnmp_config_add_data_instance_prefix */
+
 static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *ci)
 {
   int i;
@@ -219,8 +245,8 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *
       return (-1);
     }
 
-  if (dd->values != NULL)
-    free (dd->values);
+  sfree (dd->values);
+  dd->values_len = 0;
   dd->values = (oid_t *) malloc (sizeof (oid_t) * ci->values_num);
   if (dd->values == NULL)
     return (-1);
@@ -245,6 +271,34 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *
   return (0);
 } /* int csnmp_config_add_data_instance */
 
+static int csnmp_config_add_data_shift (data_definition_t *dd, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+  {
+    WARNING ("snmp plugin: The `Scale' config option needs exactly one number argument.");
+    return (-1);
+  }
+
+  dd->shift = ci->values[0].value.number;
+
+  return (0);
+} /* int csnmp_config_add_data_shift */
+
+static int csnmp_config_add_data_scale (data_definition_t *dd, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+  {
+    WARNING ("snmp plugin: The `Scale' config option needs exactly one number argument.");
+    return (-1);
+  }
+
+  dd->scale = ci->values[0].value.number;
+
+  return (0);
+} /* int csnmp_config_add_data_scale */
+
 static int csnmp_config_add_data (oconfig_item_t *ci)
 {
   data_definition_t *dd;
@@ -269,6 +323,8 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
     free (dd);
     return (-1);
   }
+  dd->scale = 1.0;
+  dd->shift = 0.0;
 
   for (i = 0; i < ci->children_num; i++)
   {
@@ -281,8 +337,14 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
       status = csnmp_config_add_data_table (dd, option);
     else if (strcasecmp ("Instance", option->key) == 0)
       status = csnmp_config_add_data_instance (dd, option);
+    else if (strcasecmp ("InstancePrefix", option->key) == 0)
+      status = csnmp_config_add_data_instance_prefix (dd, option);
     else if (strcasecmp ("Values", option->key) == 0)
       status = csnmp_config_add_data_values (dd, option);
+    else if (strcasecmp ("Shift", option->key) == 0)
+      status = csnmp_config_add_data_shift (dd, option);
+    else if (strcasecmp ("Scale", option->key) == 0)
+      status = csnmp_config_add_data_scale (dd, option);
     else
     {
       WARNING ("snmp plugin: Option `%s' not allowed here.", option->key);
@@ -314,6 +376,7 @@ static int csnmp_config_add_data (oconfig_item_t *ci)
   if (status != 0)
   {
     sfree (dd->name);
+    sfree (dd->instance_prefix);
     sfree (dd->values);
     sfree (dd);
     return (-1);
@@ -628,7 +691,8 @@ static void csnmp_host_open_session (host_definition_t *host)
   }
 } /* void csnmp_host_open_session */
 
-static value_t csnmp_value_list_to_value (struct variable_list *vl, int type)
+static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
+    double scale, double shift)
 {
   value_t ret;
   uint64_t temp = 0;
@@ -666,12 +730,143 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type)
   {
     ret.gauge = NAN;
     if (defined != 0)
-      ret.gauge = temp;
+      ret.gauge = (scale * temp) + shift;
   }
 
   return (ret);
 } /* value_t csnmp_value_list_to_value */
 
+/* Returns true if all OIDs have left their subtree */
+static int csnmp_check_res_left_subtree (const host_definition_t *host,
+    const data_definition_t *data,
+    struct snmp_pdu *res)
+{
+  struct variable_list *vb;
+  int num_checked;
+  int num_left_subtree;
+  int i;
+
+  vb = res->variables;
+  if (vb == NULL)
+    return (-1);
+
+  num_checked = 0;
+  num_left_subtree = 0;
+
+  /* check all the variables and count how many have left their subtree */
+  for (vb = res->variables, i = 0;
+      (vb != NULL) && (i < data->values_len);
+      vb = vb->next_variable, i++)
+  {
+    num_checked++;
+    if (snmp_oid_ncompare (data->values[i].oid,
+         data->values[i].oid_len,
+         vb->name, vb->name_length,
+         data->values[i].oid_len) != 0)
+      num_left_subtree++;
+  }
+
+  /* check if enough variables have been returned */
+  if (i < data->values_len)
+  {
+    ERROR ("snmp plugin: host %s: Expected %i variables, but got only %i",
+       host->name, data->values_len, i);
+    return (-1);
+  }
+
+  if (data->instance.oid.oid_len > 0)
+  {
+    if (vb == NULL)
+    {
+      ERROR ("snmp plugin: host %s: Expected one more variable for "
+         "the instance..");
+      return (-1);
+    }
+
+    num_checked++;
+    if (snmp_oid_ncompare (data->instance.oid.oid,
+         data->instance.oid.oid_len,
+         vb->name, vb->name_length,
+         data->instance.oid.oid_len) != 0)
+      num_left_subtree++;
+  }
+
+  DEBUG ("snmp plugin: csnmp_check_res_left_subtree: %i of %i variables have "
+      "left their subtree",
+      num_left_subtree, num_checked);
+  if (num_left_subtree >= num_checked)
+    return (1);
+  return (0);
+} /* int csnmp_check_res_left_subtree */
+
+static int csnmp_instance_list_add (csnmp_list_instances_t **head,
+    csnmp_list_instances_t **tail,
+    const struct snmp_pdu *res)
+{
+  csnmp_list_instances_t *il;
+  struct variable_list *vb;
+
+  /* Set vb on the last variable */
+  for (vb = res->variables;
+      (vb != NULL) && (vb->next_variable != NULL);
+      vb = vb->next_variable)
+    /* do nothing */;
+  if (vb == NULL)
+    return (-1);
+
+  il = (csnmp_list_instances_t *) malloc (sizeof (csnmp_list_instances_t));
+  if (il == NULL)
+  {
+    ERROR ("snmp plugin: malloc failed.");
+    return (-1);
+  }
+  il->subid = vb->name[vb->name_length - 1];
+  il->next = NULL;
+
+  /* Get instance name */
+  if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR))
+  {
+    char *ptr;
+    size_t instance_len;
+
+    instance_len = sizeof (il->instance) - 1;
+    if (instance_len > vb->val_len)
+      instance_len = vb->val_len;
+
+    strncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR)
+         ? vb->val.string
+         : vb->val.bitstring),
+       instance_len);
+    il->instance[instance_len] = '\0';
+
+    for (ptr = il->instance; *ptr != '\0'; ptr++)
+    {
+      if ((*ptr > 0) && (*ptr < 32))
+       *ptr = ' ';
+      else if (*ptr == '/')
+       *ptr = '_';
+    }
+    DEBUG ("snmp plugin: il->instance = `%s';", il->instance);
+  }
+  else
+  {
+    value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER, 1.0, 0.0);
+    snprintf (il->instance, sizeof (il->instance),
+       "%llu", val.counter);
+  }
+  il->instance[sizeof (il->instance) - 1] = '\0';
+
+  /* TODO: Debugging output */
+
+  if (*head == NULL)
+    *head = il;
+  else
+    (*tail)->next = il;
+  *tail = il;
+
+  return (0);
+} /* int csnmp_instance_list_add */
+
 static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *data,
     csnmp_list_instances_t *instance_list,
     csnmp_table_values_t **value_table)
@@ -683,6 +878,8 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
   csnmp_table_values_t **value_table_ptr;
 
   int i;
+  oid subid;
+  int have_more;
 
   ds = plugin_get_ds (data->type);
   if (!ds)
@@ -692,6 +889,8 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
   }
   assert (ds->ds_num == data->values_len);
 
+  instance_list_ptr = instance_list;
+
   value_table_ptr = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *)
       * data->values_len);
   if (value_table_ptr == NULL)
@@ -703,6 +902,7 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
   vl.values = (value_t *) malloc (sizeof (value_t) * vl.values_len);
   if (vl.values == NULL)
   {
+    ERROR ("snmp plugin: malloc failed.");
     sfree (value_table_ptr);
     return (-1);
   }
@@ -714,36 +914,91 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
   vl.interval = host->interval;
   vl.time = time (NULL);
 
-  for (instance_list_ptr = instance_list;
-      instance_list_ptr != NULL;
-      instance_list_ptr = instance_list_ptr->next)
+  subid = 0;
+  have_more = 1;
+
+  while (have_more != 0)
   {
-    strncpy (vl.type_instance, instance_list_ptr->instance, sizeof (vl.type_instance));
-    vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+    if (instance_list != NULL)
+    {
+      while ((instance_list_ptr != NULL)
+         && (instance_list_ptr->subid < subid))
+       instance_list_ptr = instance_list_ptr->next;
+
+      if (instance_list_ptr == NULL)
+      {
+       have_more = 0;
+       continue;
+      }
+      else if (instance_list_ptr->subid > subid)
+      {
+       subid = instance_list_ptr->subid;
+       continue;
+      }
+    } /* if (instance_list != NULL) */
 
     for (i = 0; i < data->values_len; i++)
     {
       while ((value_table_ptr[i] != NULL)
-         && (value_table_ptr[i]->subid < instance_list_ptr->subid))
+         && (value_table_ptr[i]->subid < subid))
        value_table_ptr[i] = value_table_ptr[i]->next;
-      if ((value_table_ptr[i] == NULL)
-         || (value_table_ptr[i]->subid != instance_list_ptr->subid))
-       break;
-      vl.values[i] = value_table_ptr[i]->value;
-    } /* for (data->values_len) */
 
-    /* If the for-loop was aborted early, not all subid's match. */
+      if (value_table_ptr[i] == NULL)
+      {
+       have_more = 0;
+       break;
+      }
+      else if (value_table_ptr[i]->subid > subid)
+      {
+       subid = value_table_ptr[i]->subid;
+       break;
+      }
+    } /* for (i = 0; i < columns; i++) */
+    /* The subid has been increased - start scanning from the beginning
+     * again.. */
     if (i < data->values_len)
-    {
-      DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
-         "Skipping SUBID %i",
-         host->name, data->name, i, instance_list_ptr->subid);
       continue;
+
+    /* if we reach this line, all value_table_ptr[i] are non-NULL and are set
+     * to the same subid. instance_list_ptr is either NULL or points to the
+     * same subid, too. */
+#if COLLECT_DEBUG
+    for (i = 1; i < data->values_len; i++)
+    {
+      assert (value_table_ptr[i] != NULL);
+      assert (value_table_ptr[i-1]->subid == value_table_ptr[i]->subid);
+    }
+    assert ((instance_list_ptr == NULL)
+       || (instance_list_ptr->subid == value_table_ptr[0]->subid));
+#endif
+
+    {
+      char temp[DATA_MAX_NAME_LEN];
+
+      if (instance_list_ptr == NULL)
+       snprintf (temp, sizeof (temp), "%u",
+           (uint32_t) subid);
+      else
+       strncpy (temp, instance_list_ptr->instance,
+           sizeof (temp));
+      temp[sizeof (temp) - 1] = '\0';
+
+      if (data->instance_prefix == NULL)
+       strncpy (vl.type_instance, temp, sizeof (vl.type_instance));
+      else
+       snprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
+           data->instance_prefix, temp);
+      vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
     }
 
+    for (i = 0; i < data->values_len; i++)
+      vl.values[i] = value_table_ptr[i]->value;
+
     /* If we get here `vl.type_instance' and all `vl.values' have been set */
     plugin_dispatch_values (data->type, &vl);
-  } /* for (instance_list) */
+
+    subid++;
+  } /* while (have_more != 0) */
 
   sfree (vl.values);
   sfree (value_table_ptr);
@@ -799,16 +1054,22 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   oid_list_len = data->values_len + 1;
   oid_list = (oid_t *) malloc (sizeof (oid_t) * (oid_list_len));
   if (oid_list == NULL)
+  {
+    ERROR ("snmp plugin: csnmp_read_table: malloc failed.");
     return (-1);
-  memcpy (oid_list, &data->instance.oid, sizeof (oid_t));
-  for (i = 0; i < data->values_len; i++)
-    memcpy (oid_list + (i + 1), data->values + i, sizeof (oid_t));
+  }
+  memcpy (oid_list, data->values, data->values_len * sizeof (oid_t));
+  if (data->instance.oid.oid_len > 0)
+    memcpy (oid_list + data->values_len, &data->instance.oid, sizeof (oid_t));
+  else
+    oid_list_len--;
 
   /* Allocate the `value_table' */
   value_table = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *)
       * 2 * data->values_len);
   if (value_table == NULL)
   {
+    ERROR ("snmp plugin: csnmp_read_table: malloc failed.");
     sfree (oid_list);
     return (-1);
   }
@@ -821,8 +1082,6 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   status = 0;
   while (status == 0)
   {
-    csnmp_list_instances_t *il;
-
     req = snmp_pdu_create (SNMP_MSG_GETNEXT);
     if (req == NULL)
     {
@@ -869,89 +1128,53 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       break;
     }
 
-    /* Check if we left the subtree */
-    if (snmp_oid_ncompare (data->instance.oid.oid, data->instance.oid.oid_len,
-         vb->name, vb->name_length,
-         data->instance.oid.oid_len) != 0)
-    {
-      if (res != NULL)
-       snmp_free_pdu (res);
-      res = NULL;
-
-      break;
-    }
-
-    /* Allocate a new `csnmp_list_instances_t', insert the instance name and
-     * add it to the list */
-    il = (csnmp_list_instances_t *) malloc (sizeof (csnmp_list_instances_t));
-    if (il == NULL)
+    /* Check if all values (and possibly the instance) have left their
+     * subtree */
+    if (csnmp_check_res_left_subtree (host, data, res) != 0)
     {
       if (res != NULL)
        snmp_free_pdu (res);
       res = NULL;
 
-      status = -1;
       break;
     }
-    il->subid = vb->name[vb->name_length - 1];
-    il->next = NULL;
 
-    /* Get instance name */
-    if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR))
+    /* if an instance-OID is configured.. */
+    if (data->instance.oid.oid_len > 0)
     {
-      char *ptr;
-      size_t instance_len;
-
-      instance_len = sizeof (il->instance) - 1;
-      if (instance_len > vb->val_len)
-       instance_len = vb->val_len;
-
-      strncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR)
-           ? vb->val.string
-           : vb->val.bitstring),
-         instance_len);
-      il->instance[instance_len] = '\0';
-
-      for (ptr = il->instance; *ptr != '\0'; ptr++)
+      /* Allocate a new `csnmp_list_instances_t', insert the instance name and
+       * add it to the list */
+      if (csnmp_instance_list_add (&instance_list, &instance_list_ptr,
+           res) != 0)
       {
-       if ((*ptr > 0) && (*ptr < 32))
-         *ptr = ' ';
-       else if (*ptr == '/')
-         *ptr = '_';
+       ERROR ("snmp plugin: csnmp_instance_list_add failed.");
+       status = -1;
+       break;
       }
-      DEBUG ("snmp plugin: il->instance = `%s';", il->instance);
-    }
-    else
-    {
-      value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER);
-      snprintf (il->instance, sizeof (il->instance),
-         "%llu", val.counter);
-    }
-    il->instance[sizeof (il->instance) - 1] = '\0';
-    DEBUG ("snmp plugin: data = `%s'; il->instance = `%s';",
-       data->name, il->instance);
-
-    if (instance_list_ptr == NULL)
-      instance_list = il;
-    else
-      instance_list_ptr->next = il;
-    instance_list_ptr = il;
-
-    /* Copy OID to oid_list[0] */
-    memcpy (oid_list[0].oid, vb->name, sizeof (oid) * vb->name_length);
-    oid_list[0].oid_len = vb->name_length;
-
-    for (i = 0; i < data->values_len; i++)
-    {
-      csnmp_table_values_t *vt;
 
-      vb = vb->next_variable;
+      /* Set vb on the last variable */
+      for (vb = res->variables;
+         (vb != NULL) && (vb->next_variable != NULL);
+         vb = vb->next_variable)
+       /* do nothing */;
       if (vb == NULL)
       {
        status = -1;
        break;
       }
 
+      /* Copy OID to oid_list[data->values_len] */
+      memcpy (oid_list[data->values_len].oid, vb->name,
+         sizeof (oid) * vb->name_length);
+      oid_list[data->values_len].oid_len = vb->name_length;
+    }
+
+    for (vb = res->variables, i = 0;
+       (vb != NULL) && (i < data->values_len);
+       vb = vb->next_variable, i++)
+    {
+      csnmp_table_values_t *vt;
+
       /* Check if we left the subtree */
       if (snmp_oid_ncompare (data->values[i].oid,
            data->values[i].oid_len,
@@ -966,28 +1189,34 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       if ((value_table_ptr[i] != NULL)
          && (vb->name[vb->name_length - 1] <= value_table_ptr[i]->subid))
       {
-       DEBUG ("snmp plugin: host = %s; data = %s; i = %i; SUBID is not increasing.",
+       DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
+           "SUBID is not increasing.",
            host->name, data->name, i);
        continue;
       }
 
       vt = (csnmp_table_values_t *) malloc (sizeof (csnmp_table_values_t));
-      if (vt != NULL)
+      if (vt == NULL)
       {
-       vt->subid = vb->name[vb->name_length - 1];
-       vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type);
-       vt->next = NULL;
-
-       if (value_table_ptr[i] == NULL)
-         value_table[i] = vt;
-       else
-         value_table_ptr[i]->next = vt;
-       value_table_ptr[i] = vt;
+       ERROR ("snmp plugin: malloc failed.");
+       status = -1;
+       break;
       }
 
+      vt->subid = vb->name[vb->name_length - 1];
+      vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type,
+         data->scale, data->shift);
+      vt->next = NULL;
+
+      if (value_table_ptr[i] == NULL)
+       value_table[i] = vt;
+      else
+       value_table_ptr[i]->next = vt;
+      value_table_ptr[i] = vt;
+
       /* Copy OID to oid_list[i + 1] */
-      memcpy (oid_list[i + 1].oid, vb->name, sizeof (oid) * vb->name_length);
-      oid_list[i + 1].oid_len = vb->name_length;
+      memcpy (oid_list[i].oid, vb->name, sizeof (oid) * vb->name_length);
+      oid_list[i].oid_len = vb->name_length;
     } /* for (i = data->values_len) */
 
     if (res != NULL)
@@ -1124,7 +1353,8 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
     for (i = 0; i < data->values_len; i++)
       if (snmp_oid_compare (data->values[i].oid, data->values[i].oid_len,
            vb->name, vb->name_length) == 0)
-       vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type);
+       vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type,
+           data->scale, data->shift);
   } /* for (res->variables) */
 
   if (res != NULL)
diff --git a/src/tcpconns.c b/src/tcpconns.c
new file mode 100644 (file)
index 0000000..00cad0e
--- /dev/null
@@ -0,0 +1,521 @@
+/**
+ * collectd - src/tcpconns.c
+ * Copyright (C) 2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME
+# error "No applicable input method."
+#endif
+
+#if KERNEL_LINUX
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_SYSCTLBYNAME
+# include <sys/socketvar.h>
+# include <sys/sysctl.h>
+# include <net/route.h>
+# include <netinet/in.h>
+# include <netinet/in_systm.h>
+# include <netinet/ip.h>
+# include <netinet/ip6.h>
+# include <netinet/in_pcb.h>
+# include <netinet/ip_var.h>
+# include <netinet/tcp.h>
+# include <netinet/tcpip.h>
+# include <netinet/tcp_seq.h>
+# include <netinet/tcp_var.h>
+#endif /* HAVE_SYSCTLBYNAME */
+
+#if KERNEL_LINUX
+static const char *tcp_state[] =
+{
+  "", /* 0 */
+  "ESTABLISHED",
+  "SYN_SENT",
+  "SYN_RECV",
+  "FIN_WAIT1",
+  "FIN_WAIT2",
+  "TIME_WAIT",
+  "CLOSED",
+  "CLOSE_WAIT",
+  "LAST_ACK",
+  "LISTEN", /* 10 */
+  "CLOSING"
+};
+
+# define TCP_STATE_LISTEN 10
+# define TCP_STATE_MIN 1
+# define TCP_STATE_MAX 11
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_SYSCTLBYNAME
+static const char *tcp_state[] =
+{
+  "CLOSED",
+  "LISTEN",
+  "SYN_SENT",
+  "SYN_RECV",
+  "ESTABLISHED",
+  "CLOSE_WAIT",
+  "FIN_WAIT1",
+  "CLOSING",
+  "LAST_ACK",
+  "FIN_WAIT2",
+  "TIME_WAIT"
+};
+
+# define TCP_STATE_LISTEN 1
+# define TCP_STATE_MIN 0
+# define TCP_STATE_MAX 10
+#endif /* HAVE_SYSCTLBYNAME */
+
+#define PORT_COLLECT_LOCAL  0x01
+#define PORT_COLLECT_REMOTE 0x02
+#define PORT_IS_LISTENING   0x04
+
+typedef struct port_entry_s
+{
+  uint16_t port;
+  uint16_t flags;
+  uint32_t count_local[TCP_STATE_MAX + 1];
+  uint32_t count_remote[TCP_STATE_MAX + 1];
+  struct port_entry_s *next;
+} port_entry_t;
+
+static const char *config_keys[] =
+{
+  "ListeningPorts",
+  "LocalPort",
+  "RemotePort"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static int port_collect_listening = 0;
+static port_entry_t *port_list_head = NULL;
+
+static void conn_submit_port_entry (port_entry_t *pe)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+  int i;
+
+  vl.values = values;
+  vl.values_len = 1;
+  vl.time = time (NULL);
+  strcpy (vl.host, hostname_g);
+  strcpy (vl.plugin, "tcpconns");
+
+  if (((port_collect_listening != 0) && (pe->flags & PORT_IS_LISTENING))
+      || (pe->flags & PORT_COLLECT_LOCAL))
+  {
+    snprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+       "%hu-local", pe->port);
+    vl.plugin_instance[sizeof (vl.plugin_instance) - 1] = '\0';
+
+    for (i = 1; i <= TCP_STATE_MAX; i++)
+    {
+      vl.values[0].gauge = pe->count_local[i];
+
+      strncpy (vl.type_instance, tcp_state[i], sizeof (vl.type_instance));
+      vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+
+      plugin_dispatch_values ("tcp_connections", &vl);
+    }
+  }
+
+  if (pe->flags & PORT_COLLECT_REMOTE)
+  {
+    snprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+       "%hu-remote", pe->port);
+    vl.plugin_instance[sizeof (vl.plugin_instance) - 1] = '\0';
+
+    for (i = 1; i <= TCP_STATE_MAX; i++)
+    {
+      vl.values[0].gauge = pe->count_remote[i];
+
+      strncpy (vl.type_instance, tcp_state[i], sizeof (vl.type_instance));
+      vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+
+      plugin_dispatch_values ("tcp_connections", &vl);
+    }
+  }
+} /* void conn_submit */
+
+static void conn_submit_all (void)
+{
+  port_entry_t *pe;
+
+  for (pe = port_list_head; pe != NULL; pe = pe->next)
+    conn_submit_port_entry (pe);
+} /* void conn_submit_all */
+
+static port_entry_t *conn_get_port_entry (uint16_t port, int create)
+{
+  port_entry_t *ret;
+
+  ret = port_list_head;
+  while (ret != NULL)
+  {
+    if (ret->port == port)
+      break;
+    ret = ret->next;
+  }
+
+  if ((ret == NULL) && (create != 0))
+  {
+    ret = (port_entry_t *) malloc (sizeof (port_entry_t));
+    if (ret == NULL)
+      return (NULL);
+    memset (ret, '\0', sizeof (port_entry_t));
+
+    ret->port = port;
+    ret->next = port_list_head;
+    port_list_head = ret;
+  }
+
+  return (ret);
+} /* port_entry_t *conn_get_port_entry */
+
+/* Removes ports that were added automatically due to the `ListeningPorts'
+ * setting but which are no longer listening. */
+static void conn_reset_port_entry (void)
+{
+  port_entry_t *prev = NULL;
+  port_entry_t *pe = port_list_head;
+
+  while (pe != NULL)
+  {
+    /* If this entry was created while reading the files (ant not when handling
+     * the configuration) remove it now. */
+    if ((pe->flags & (PORT_COLLECT_LOCAL
+           | PORT_COLLECT_REMOTE
+           | PORT_IS_LISTENING)) == 0)
+    {
+      port_entry_t *next = pe->next;
+
+      DEBUG ("tcpconns plugin: Removing temporary entry "
+         "for listening port %hu", pe->port);
+
+      if (prev == NULL)
+       port_list_head = next;
+      else
+       prev->next = next;
+
+      sfree (pe);
+      pe = next;
+
+      continue;
+    }
+
+    memset (pe->count_local, '\0', sizeof (pe->count_local));
+    memset (pe->count_remote, '\0', sizeof (pe->count_remote));
+    pe->flags &= ~PORT_IS_LISTENING;
+
+    pe = pe->next;
+  }
+} /* void conn_reset_port_entry */
+
+static int conn_handle_ports (uint16_t port_local, uint16_t port_remote, uint8_t state)
+{
+  port_entry_t *pe = NULL;
+
+  if ((state > TCP_STATE_MAX)
+#if TCP_STATE_MIN > 0
+      || (state < TCP_STATE_MIN)
+#endif
+     )
+  {
+    NOTICE ("tcpconns plugin: Ignoring connection with unknown state 0x%02x.",
+       state);
+    return (-1);
+  }
+
+  /* Listening sockets */
+  if ((state == TCP_STATE_LISTEN) && (port_collect_listening != 0))
+  {
+    pe = conn_get_port_entry (port_local, 1 /* create */);
+    if (pe != NULL)
+      pe->flags |= PORT_IS_LISTENING;
+  }
+
+  DEBUG ("tcpconns plugin: Connection %hu <-> %hu (%s)",
+      port_local, port_remote, tcp_state[state]);
+
+  pe = conn_get_port_entry (port_local, 0 /* no create */);
+  if (pe != NULL)
+    pe->count_local[state]++;
+
+  pe = conn_get_port_entry (port_remote, 0 /* no create */);
+  if (pe != NULL)
+    pe->count_remote[state]++;
+
+  return (0);
+} /* int conn_handle_ports */
+
+#if KERNEL_LINUX
+static int conn_handle_line (char *buffer)
+{
+  char *fields[32];
+  int   fields_len;
+
+  char *endptr;
+
+  char *port_local_str;
+  char *port_remote_str;
+  uint16_t port_local;
+  uint16_t port_remote;
+
+  uint8_t state;
+
+  int buffer_len = strlen (buffer);
+
+  while ((buffer_len > 0) && (buffer[buffer_len - 1] < 32))
+    buffer[--buffer_len] = '\0';
+  if (buffer_len <= 0)
+    return (-1);
+
+  fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+  if (fields_len < 12)
+  {
+    DEBUG ("tcpconns plugin: Got %i fields, expected at least 12.", fields_len);
+    return (-1);
+  }
+
+  port_local_str  = strchr (fields[1], ':');
+  port_remote_str = strchr (fields[2], ':');
+
+  if ((port_local_str == NULL) || (port_remote_str == NULL))
+    return (-1);
+  port_local_str++;
+  port_remote_str++;
+  if ((*port_local_str == '\0') || (*port_remote_str == '\0'))
+    return (-1);
+
+  endptr = NULL;
+  port_local = (uint16_t) strtol (port_local_str, &endptr, 16);
+  if ((endptr == NULL) || (*endptr != '\0'))
+    return (-1);
+
+  endptr = NULL;
+  port_remote = (uint16_t) strtol (port_remote_str, &endptr, 16);
+  if ((endptr == NULL) || (*endptr != '\0'))
+    return (-1);
+
+  endptr = NULL;
+  state = (uint8_t) strtol (fields[3], &endptr, 16);
+  if ((endptr == NULL) || (*endptr != '\0'))
+    return (-1);
+
+  return (conn_handle_ports (port_local, port_remote, state));
+} /* int conn_handle_line */
+
+static int conn_read_file (const char *file)
+{
+  FILE *fh;
+  char buffer[1024];
+
+  fh = fopen (file, "r");
+  if (fh == NULL)
+    return (-1);
+
+  while (fgets (buffer, sizeof (buffer), fh) != NULL)
+  {
+    conn_handle_line (buffer);
+  } /* while (fgets) */
+
+  fclose (fh);
+
+  return (0);
+} /* int conn_read_file */
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_SYSCTLBYNAME
+#endif /* HAVE_SYSCTLBYNAME */
+
+static int conn_config (const char *key, const char *value)
+{
+  if (strcasecmp (key, "ListeningPorts") == 0)
+  {
+    if ((strcasecmp (value, "Yes") == 0)
+       || (strcasecmp (value, "True") == 0)
+       || (strcasecmp (value, "On") == 0))
+      port_collect_listening = 1;
+    else
+      port_collect_listening = 0;
+  }
+  else if ((strcasecmp (key, "LocalPort") == 0)
+      || (strcasecmp (key, "RemotePort") == 0))
+  {
+      port_entry_t *pe;
+      int port = atoi (value);
+
+      if ((port < 1) || (port > 65535))
+      {
+       ERROR ("tcpconns plugin: Invalid port: %i", port);
+       return (1);
+      }
+
+      pe = conn_get_port_entry ((uint16_t) port, 1 /* create */);
+      if (pe == NULL)
+      {
+       ERROR ("tcpconns plugin: conn_get_port_entry failed.");
+       return (1);
+      }
+
+      if (strcasecmp (key, "LocalPort") == 0)
+       pe->flags |= PORT_COLLECT_LOCAL;
+      else
+       pe->flags |= PORT_COLLECT_REMOTE;
+  }
+  else
+  {
+    return (-1);
+  }
+
+  return (0);
+} /* int conn_config */
+
+#if KERNEL_LINUX
+static int conn_init (void)
+{
+  if (port_list_head == NULL)
+    port_collect_listening = 1;
+
+  return (0);
+} /* int conn_init */
+
+static int conn_read (void)
+{
+  int errors_num = 0;
+
+  conn_reset_port_entry ();
+
+  if (conn_read_file ("/proc/net/tcp") != 0)
+    errors_num++;
+  if (conn_read_file ("/proc/net/tcp6") != 0)
+    errors_num++;
+
+  if (errors_num < 2)
+  {
+    conn_submit_all ();
+  }
+  else
+  {
+    ERROR ("tcpconns plugin: Neither /proc/net/tcp nor /proc/net/tcp6 "
+       "coult be read.");
+    return (-1);
+  }
+
+  return (0);
+} /* int conn_read */
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_SYSCTLBYNAME
+static int conn_read (void)
+{
+  int status;
+  char *buffer;
+  size_t buffer_len;;
+
+  struct xinpgen *in_orig;
+  struct xinpgen *in_ptr;
+
+  conn_reset_port_entry ();
+
+  buffer_len = 0;
+  status = sysctlbyname ("net.inet.tcp.pcblist", NULL, &buffer_len, 0, 0);
+  if (status < 0)
+  {
+    ERROR ("tcpconns plugin: sysctlbyname failed.");
+    return (-1);
+  }
+
+  buffer = (char *) malloc (buffer_len);
+  if (buffer == NULL)
+  {
+    ERROR ("tcpconns plugin: malloc failed.");
+    return (-1);
+  }
+
+  status = sysctlbyname ("net.inet.tcp.pcblist", buffer, &buffer_len, 0, 0);
+  if (status < 0)
+  {
+    ERROR ("tcpconns plugin: sysctlbyname failed.");
+    sfree (buffer);
+    return (-1);
+  }
+
+  if (buffer_len <= sizeof (struct xinpgen))
+  {
+    ERROR ("tcpconns plugin: (buffer_len <= sizeof (struct xinpgen))");
+    sfree (buffer);
+    return (-1);
+  }
+
+  in_orig = (struct xinpgen *) buffer;
+  for (in_ptr = (struct xinpgen *) (((char *) in_orig) + in_orig->xig_len);
+      in_ptr->xig_len > sizeof (struct xinpgen);
+      in_ptr = (struct xinpgen *) (((char *) in_ptr) + in_ptr->xig_len))
+  {
+    struct tcpcb *tp = &((struct xtcpcb *) in_ptr)->xt_tp;
+    struct inpcb *inp = &((struct xtcpcb *) in_ptr)->xt_inp;
+    struct xsocket *so = &((struct xtcpcb *) in_ptr)->xt_socket;
+
+    /* Ignore non-TCP sockets */
+    if (so->xso_protocol != IPPROTO_TCP)
+      continue;
+
+    /* Ignore PCBs which were freed during copyout. */
+    if (inp->inp_gencnt > in_orig->xig_gen)
+      continue;
+
+    if (((inp->inp_vflag & INP_IPV4) == 0)
+       && ((inp->inp_vflag & INP_IPV6) == 0))
+      continue;
+
+    conn_handle_ports (inp->inp_lport, inp->inp_fport, tp->t_state);
+  } /* for (in_ptr) */
+
+  in_orig = NULL;
+  in_ptr = NULL;
+  sfree (buffer);
+
+  conn_submit_all ();
+
+  return (0);
+} /* int conn_read */
+#endif /* HAVE_SYSCTLBYNAME */
+
+void module_register (void)
+{
+       plugin_register_config ("tcpconns", conn_config,
+                       config_keys, config_keys_num);
+#if KERNEL_LINUX
+       plugin_register_init ("tcpconns", conn_init);
+#endif
+       plugin_register_read ("tcpconns", conn_read);
+} /* void module_register */
+
+/*
+ * vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
+ */
index c0463c4..bfd400c 100644 (file)
@@ -4,6 +4,7 @@ apache_requests         count:COUNTER:0:134217728
 apache_scoreboard      count:GAUGE:0:65535
 bitrate                        value:GAUGE:0:4294967295
 charge                 value:GAUGE:0:U
+connections            value:COUNTER:0:U
 counter                        value:COUNTER:U:U
 cpufreq                        value:GAUGE:0:U
 cpu                    value:COUNTER:0:4294967295
@@ -47,6 +48,13 @@ mysql_octets         rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
 mysql_qcache           hits:COUNTER:0:U, inserts:COUNTER:0:U, not_cached:COUNTER:0:U, lowmem_prunes:COUNTER:0:U, queries_in_cache:GAUGE:0:U
 mysql_threads          running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created:COUNTER:0:U
 nfs_procedure          value:COUNTER:0:4294967295
+memcached_command      value:COUNTER:0:U
+memcached_connections  value:GAUGE:0:U
+memcached_items                value:GAUGE:0:U
+memcached_octets       rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
+memcached_ops          value:COUNTER:0:134217728
+nginx_connections      value:GAUGE:0:U
+nginx_requests         value:COUNTER:0:134217728
 percent                        percent:GAUGE:0:100.1
 ping                   ping:GAUGE:0:65535
 power                  value:GAUGE:0:U
@@ -61,6 +69,7 @@ signal_power          value:GAUGE:U:0
 signal_quality         value:GAUGE:0:U
 spam_score             value:GAUGE:U:U
 swap                   value:GAUGE:0:1099511627776
+tcp_connections                value:GAUGE:0:4294967295
 temperature            value:GAUGE:-273.15:U
 time_dispersion                seconds:GAUGE:-1000000:1000000
 timeleft               timeleft:GAUGE:0:3600
index e460959..83821fe 100644 (file)
@@ -581,13 +581,12 @@ int c_avl_get (c_avl_tree_t *t, const void *key, void **value)
 {
        c_avl_node_t *n;
 
-       assert (value != NULL);
-
        n = search (t, key);
        if (n == NULL)
                return (-1);
 
-       *value = n->value;
+       if (value != NULL)
+               *value = n->value;
 
        return (0);
 }
index d8694e3..d5db9dc 100644 (file)
@@ -110,6 +110,9 @@ void llist_prepend (llist_t *l, llentry_t *e)
 {
        e->next = l->head;
        l->head = e;
+
+       if (l->tail == NULL)
+               l->tail = e;
 }
 
 void llist_remove (llist_t *l, llentry_t *e)
index efaa92f..44ad7ea 100644 (file)
@@ -383,9 +383,11 @@ static cu_mount_t *cu_mount_listmntent (void)
 
        struct tabmntent *mntlist;
        if(listmntent(&mntlist, COLLECTD_MNTTAB, NULL, NULL) < 0) {
+#if COLLECT_DEBUG
                char errbuf[1024];
                DEBUG("utils_mount: calling listmntent() failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif /* COLLECT_DEBUG */
        }
 
        for(p = mntlist; p; p = p->next) {
@@ -454,9 +456,11 @@ static cu_mount_t *cu_mount_getfsstat (void)
        /* Get the number of mounted file systems */
        if ((bufsize = CMD_STATFS (NULL, 0, FLAGS_STATFS)) < 1)
        {
+#if COLLECT_DEBUG
                char errbuf[1024];
                DEBUG ("utils_mount: getv?fsstat failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif /* COLLECT_DEBUG */
                return (NULL);
        }
 
@@ -469,9 +473,11 @@ static cu_mount_t *cu_mount_getfsstat (void)
         * manpage.. -octo */
        if ((num = CMD_STATFS (buf, bufsize * sizeof (STRUCT_STATFS), FLAGS_STATFS)) < 1)
        {
+#if COLLECT_DEBUG
                char errbuf[1024];
                DEBUG ("utils_mount: getv?fsstat failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif /* COLLECT_DEBUG */
                free (buf);
                return (NULL);
        }
index 081ef96..c4222aa 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-DEFAULT_VERSION="4.1.5.git"
+DEFAULT_VERSION="4.2.3.git"
 
 VERSION="$( git describe 2> /dev/null | sed -e 's/^collectd-//' )"