author | Florian Forster <octo@leeloo.lan.home.verplant.org> | |
Thu, 8 Nov 2007 18:49:17 +0000 (19:49 +0100) | ||
committer | Florian Forster <octo@leeloo.lan.home.verplant.org> | |
Thu, 8 Nov 2007 18:49:17 +0000 (19:49 +0100) |
Conflicts:
ChangeLog
contrib/PerlLib/Collectd.pm
version-gen.sh
ChangeLog
contrib/PerlLib/Collectd.pm
version-gen.sh
36 files changed:
index 0c1b3225868db4d32fa85a9f7d17be50760f8af2..ee7e392081950c9ddd359aaf4debbbe1e7a8727c 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
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>
diff --git a/ChangeLog b/ChangeLog
index 269520c8a2df1a8532139b7b14b49728603fe890..526d463dedfc678d7b09e342f7ea1e7975447dd9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
+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-11-08, Version 4.1.4
* Build system: Improve detection of the rrd library, especially if
it's in a non-standard location.
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:
diff --git a/Makefile.am b/Makefile.am
index 34c36cc0d0d8e66e3e7ab99158847827c03e3036..9a96eb27c8594f65ae0079f6bbe48e74a4e0faaa 100644 (file)
--- a/Makefile.am
+++ b/Makefile.am
-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
index 37eee3eaeb2f51004ec9791532edda1fc9347564..24061549c8a201b5b2af50b3be59a3bf9189b125 100644 (file)
--- a/README
+++ b/README
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.
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.
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.
- 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.
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)
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
--- /dev/null
+++ b/bindings/Makefile.am
@@ -0,0 +1,18 @@
+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 ] || $(MAKE) -C perl install
+
+clean-local:
+ [ ! -f perl/Makefile ] || $(MAKE) -C perl realclean
+
+perl: perl/Makefile
+ $(MAKE) -C perl
+
+perl/Makefile: perl/Makefile.PL perl/Collectd/Makefile.PL
+ cd perl && @PERL@ Makefile.PL PREFIX=$(prefix) @PERL_BINDINGS_OPTIONS@
+
+.PHONY: perl
+
diff --git a/bindings/perl/Collectd.pm b/bindings/perl/Collectd.pm
--- /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
--- /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
--- /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
--- /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 :
diff --git a/configure.in b/configure.in
index 5555c8a0a966b70aa33586f0daf9f8b05bd7c87b..75bc00f4271dca477cf1e83b25e2c027e2b47ecd 100644 (file)
--- a/configure.in
+++ b/configure.in
#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, [], [],
[
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.
#
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
plugin_disk="no"
plugin_entropy="no"
plugin_interface="no"
+plugin_ipvs="no"
plugin_irq="no"
plugin_load="no"
plugin_memory="no"
plugin_serial="no"
plugin_swap="no"
plugin_tape="no"
+plugin_tcpconns="no"
plugin_users="no"
plugin_vserver="no"
plugin_wireless="no"
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
then
plugin_cpu="yes"
plugin_memory="yes"
+ plugin_tcpconns="yes"
fi
if test "x$have_statfs" = "xyes"
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])
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
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:
daemon mode . . . . $enable_daemon
debug . . . . . . . $enable_debug
+ Bindings:
+ perl . . . . . . . $with_perl_bindings
+
Modules:
apache . . . . . . $enable_apache
apcups . . . . . . $enable_apcups
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
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
+++ /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
+++ /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
diff --git a/contrib/README b/contrib/README
index 6f0836f82387d90613aa0b8f98a84746e5b433fe..398d13ba9bdd0dcab5020e5fb2b51f210c31d273 100644 (file)
--- a/contrib/README
+++ b/contrib/README
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-
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.
diff --git a/contrib/collection.cgi b/contrib/collection.cgi
index 791976027827adba69de670ca6c93b97d67b6a9b..7da7e1c62f857a8bd9d1be956e51ff102d6da64e 100755 (executable)
--- a/contrib/collection.cgi
+++ b/contrib/collection.cgi
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)
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} = {};
'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',
'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",
'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',
'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',
'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',
'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',
'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',
'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',
'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',
$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
$opts->{'title'} = "$host/$plugin"
. (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
+ $opts->{'rrd_opts'} = ['-v', 'Percent'];
+
my @files = ();
$opts->{'colors'} =
$opts->{'title'} = "$host/$plugin"
. (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
+ $opts->{'rrd_opts'} = ['-v', 'Processes'];
my @files = ();
$opts->{'title'} = "$host/$plugin"
. (defined ($plugin_instance) ? "-$plugin_instance" : '') . "/$type";
$opts->{'number_format'} = '%5.1lf%s';
+ $opts->{'rrd_opts'} = ['-v', 'Bytes'];
my @files = ();
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
--- /dev/null
+++ b/contrib/exec-smartctl
@@ -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 d9f05521698f48adfba62b3d78f556519bdd61f2..89ce1237499395aaad888badce63528810ebf103 100644 (file)
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>
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
%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
%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
%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
%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
%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
%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
--- /dev/null
+++ b/contrib/snmp-data.conf
@@ -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>
diff --git a/src/Makefile.am b/src/Makefile.am
index 9818580462860123c3640a65ce81789858f3e683..d229407f595143cd7211d54bcf6bc7e0a109d482 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
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
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
collectd_DEPENDENCIES += nfs.la
endif
+if BUILD_PLUGIN_NGINX
+pkglib_LTLIBRARIES += nginx.la
+nginx_la_SOURCES = nginx.c
+nginx_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBCURL
+nginx_la_LDFLAGS += $(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
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
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
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)
diff --git a/src/collectd-exec.pod b/src/collectd-exec.pod
index 27d0eedca01956d44787ef9b7c08a0a8caca9237..29ff45bd83dbea0053af1420e040e4c2f932e40f 100644 (file)
--- a/src/collectd-exec.pod
+++ b/src/collectd-exec.pod
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
diff --git a/src/collectd-perl.pod b/src/collectd-perl.pod
index 3e15b88c60bbceac19d219404cfec1cb27c81e67..b6537470de793847fcbc6424c94f3ea31591b78e 100644 (file)
--- a/src/collectd-perl.pod
+++ b/src/collectd-perl.pod
=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 simply. 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
=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],
=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
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
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
+
diff --git a/src/collectd-snmp.pod b/src/collectd-snmp.pod
index 00d41e50cc62827d007902bc118dba8b93d6e43d..ea490293f17ab21990536ed06eb533056edd1d77 100644 (file)
--- a/src/collectd-snmp.pod
+++ b/src/collectd-snmp.pod
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">
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
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 1be0654d8b971407ebe2de77996bc7b27f220cb2..5769329fa1e4df6078c5e17a7bd6ed3e3599b4dd 100644 (file)
=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
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
diff --git a/src/collectd.conf.in b/src/collectd.conf.in
index 5bb1fc776d0f0ef76bbd25294fa3ef1fa408a10d..58de5258cb2b19ba1150531671c23c66b752bf27 100644 (file)
--- a/src/collectd.conf.in
+++ b/src/collectd.conf.in
@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
@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"
diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod
index db87f9c5614582f69bc68fc1bf016c79c694b3bb..da12f863a0b642627975a67325c0ee6091731cf3 100644 (file)
--- a/src/collectd.conf.pod
+++ b/src/collectd.conf.pod
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,
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
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>
=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
=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
=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>
=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
L<collectd(1)>,
L<collectd-exec(5)>,
+L<collectd-perl(5)>,
L<collectd-unixsock(5)>,
L<hddtemp(8)>,
L<kstat(3KSTAT)>,
diff --git a/src/configfile.c b/src/configfile.c
index f4e9c604fe26cafd75ee04620158b11f5aed6726..0310ca8aff417f37036a737ee55043f5182bc955 100644 (file)
--- a/src/configfile.c
+++ b/src/configfile.c
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
*/
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);
diff --git a/src/email.c b/src/email.c
index f5a544ac4ea0a2745eaa2b7648112859c904c67a..869b7c36aa620cff5f7ae2f2d806d79868084eea 100644 (file)
--- a/src/email.c
+++ b/src/email.c
#include "configfile.h"
+#include <stddef.h>
+
#if HAVE_LIBPTHREAD
# include <pthread.h>
#endif
diff --git a/src/ipvs.c b/src/ipvs.c
--- /dev/null
+++ b/src/ipvs.c
@@ -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 : */
diff --git a/src/logfile.c b/src/logfile.c
index 102f12b3a9b7589a5d340fadc6f1ef43e89ae888..03c2f9233c0d4ac9b5e23a4f157bf2214e464555 100644 (file)
--- a/src/logfile.c
+++ b/src/logfile.c
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);
sfree (log_file);
log_file = strdup (value);
}
+ else if (0 == strcasecmp (key, "File")) {
+ if ((strcasecmp (value, "false") == 0)
+ || (strcasecmp (value, "no") == 0)
+ || (strcasecmp (value, "off") == 0))
+ print_timestamp = 0;
+ else
+ print_timestamp = 1;
+ }
else {
return -1;
}
{
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 (×tamp_time, ×tamp_tm);
+
+ strftime (timestamp_str, sizeof (timestamp_str), "%Y-%m-%d %H:%M:%S",
+ ×tamp_tm);
+ timestamp_str[sizeof (timestamp_str) - 1] = '\0';
+ }
+
pthread_mutex_lock (&file_lock);
if ((log_file == NULL) || (strcasecmp (log_file, "stderr") == 0))
}
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
--- /dev/null
+++ b/src/memcached.c
@@ -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
--- /dev/null
+++ b/src/nginx.c
@@ -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 (strcpy (type, "nginx_connections") == 0)
+ values[0].gauge = value;
+ else if (strcpy (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 :
+ */
diff --git a/src/perl.c b/src/perl.c
index f2cb7b6687a40ced37e5f05572cdf07c563e5b1f..1ed9d003d12ad84d3ce105fd18bdca70f8c6931d 100644 (file)
--- a/src/perl.c
+++ b/src/perl.c
#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.
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;
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);
return 0;
} /* static int value2av (value_list_t *, data_set_t *, HV *) */
-
/*
* Internal functions.
*/
return (buf);
} /* 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.
*/
data_source_t *ds = NULL;
data_set_t *set = NULL;
- ds_types_t *types = NULL;
-
if ((NULL == name) || (NULL == dataset))
return -1;
ds = (data_source_t *)smalloc ((len + 1) * sizeof (data_source_t));
set = (data_set_t *)smalloc (sizeof (data_set_t));
- types = (ds_types_t *)smalloc (sizeof (ds_types_t));
- types->len = len + 1;
- types->values = (int *)smalloc ((types->len) * sizeof (int));
-
for (i = 0; i <= len; ++i) {
SV **elem = Perl_av_fetch (perl, dataset, i, 0);
if (-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';
*/
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 *) */
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);
} /* 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;
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;
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) {
/*
*/
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;
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;
}
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).
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);
static int perl_read (void)
{
- assert (NULL != perl);
+ if (NULL == perl)
+ return 0;
PERL_SET_CONTEXT (perl);
return pplugin_call_all (PLUGIN_READ);
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);
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);
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 */
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);
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) */
diff --git a/src/rrdtool.c b/src/rrdtool.c
index d274ba021b65146cd2cf7195421e045c52a1f5d5..e356bf19d6d91fb4a275fffb840df0c6f3827a4b 100644 (file)
--- a/src/rrdtool.c
+++ b/src/rrdtool.c
/* 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++)
{
diff --git a/src/snmp.c b/src/snmp.c
index 300647659a7e31b6b539abfe953e56c8ccaec73c..4311ff1670b5f92a6e2927d16420e2c1c5f8a011 100644 (file)
--- a/src/snmp.c
+++ b/src/snmp.c
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;
* ! +-> 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;
free (dd);
return (-1);
}
+ dd->scale = 1.0;
+ dd->shift = 0.0;
for (i = 0; i < ci->children_num; i++)
{
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);
if (status != 0)
{
sfree (dd->name);
+ sfree (dd->instance_prefix);
sfree (dd->values);
sfree (dd);
return (-1);
}
} /* 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);
}
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
--- /dev/null
+++ b/src/tcpconns.c
@@ -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 :
+ */
diff --git a/src/types.db b/src/types.db
index c0463c4adf5b65d423c47e5f86c43863bb832770..bfd400c029256b2a13987151319c8fb6818e8e55 100644 (file)
--- a/src/types.db
+++ b/src/types.db
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
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
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
diff --git a/version-gen.sh b/version-gen.sh
index 9ec71481cebac1b8bb97074dc11a59f012e5acc4..ccd3a2ef32da4aff497d4d5a8a70846826220e19 100755 (executable)
--- a/version-gen.sh
+++ b/version-gen.sh
#!/bin/sh
-DEFAULT_VERSION="4.1.4.git"
+DEFAULT_VERSION="4.2.0.git"
VERSION="$( git describe 2> /dev/null | sed -e 's/^collectd-//' )"