src/*.lo
src/*.o
src/collectd
+src/collectd-nagios
+src/collectdmon
src/collectd*.1
src/collectd*.5
src/types.db.5
src/liboconfig/parser.h
src/liboconfig/scanner.c
+# perl stuff:
+bindings/.perl-directory-stamp
+bindings/perl/Collectd/pm_to_blib
+bindings/perl/blib/
+bindings/perl/pm_to_blib
+
# make dist stuff:
collectd-*.tar.gz
collectd-*.tar.bz2
-This package was written by:
- Florian octo Forster <octo at verplant.org>
+Permanent project members
+=========================
-apcups plugin by:
- Anthony Gialluca <tonyabg at charter.net>
+Florian "octo" Forster <octo at verplant.org>
+ - Initial author.
-cpufreq, multimeter and irq plugin, as well as some bugfixes in the exec plugin
-by:
- Peter Holik <peter at holik.at>
+Sebastian "tokkee" Harl <sh at tokkee.org>
+ - Bugfixed and enhancments in many places all around the project.
+ - perl plugin.
+ - users plugin.
+ - vserver plugin.
+ - Debian package.
-hddtemp plugin by:
- Vincent Stehlé <vincent.stehle at free.fr>
-iptables plugin by:
- Sjoerd van der Berg <harekiet at gmail.com>
+Contributors (sorted alphabetically)
+====================================
-libvirt plugin by:
- Richard W. M. Jones <rjones at redhat.com>
+Alessandro Iurlano <alessandro.iurlano at gmail.com>
+ - Initial filecount plugin.
-mbmon plugin by:
- Flavio Stanchina <flavio at stanchina.net>
+Alvaro Barcellos <alvaro.barcellos at gmail.com>
+ - Don't-fork patch.
-memcached plugin by:
- Antony Dovgal <tony at daylessday.org>
+Anthony Gialluca <tonyabg at charter.net>
+ - apcups plugin.
-nfs plugin by:
- Jason Pepas <cell at ices.utexas.edu>
+Antony Dovgal <tony at daylessday.org>
+ - memcached plugin.
-perl plugin by:
- Sebastian Harl <sh at tokkee.org>
+Christophe Kalt <collectd at klb.taranis.org>
+ - The version 3 `log' mode.
+ - Many Solaris related hints and fixes.
-powerdns plugin and initial `tail' subsystem by:
- Luke Herberling <collectd at c-ware.com>
+Dan Berrange <berrange at redhat.com>
+ - uuid plugin.
-processes plugin by:
- Lyonel Vincent <lyonel at ezix.org>
+David Bacher <drbacher at gmail.com>
+ - serial plugin.
-sensors plugin has been improved by:
- Luboš Staněk <kolektor at atlas.cz>
+Flavio Stanchina <flavio at stanchina.net>
+ - mbmon plugin.
-serial plugin by:
- David Bacher <drbacher at gmail.com>
+Jason Pepas <cell at ices.utexas.edu>
+ - nfs plugin.
-tape plugin by:
- Scott Garrett <sgarrett at technomancer.com>
+Luboš Staněk <kolektor at atlas.cz>
+ - sensors plugin improvements.
+ - Time and effort to find a nasty bug in the ntpd-plugin.
-teamspeak2 plugin by:
- Stefan Hacker <stefan.hacker at web.de>
+Luke Herberling <collectd at c-ware.com>
+ - powerdns plugin.
+ - Initial `tail' subsystem by:
-users plugin by:
- Sebastian Harl <sh at tokkee.org>
+Lyonel Vincent <lyonel at ezix.org>
+ - processes plugin.
-uuid plugin by:
- Dan Berrange <berrange at redhat.com>
- Richard W.M. Jones <rjones at redhat.com>
+Michael Stapelberg <michael+git@stapelberg.de>
+ - OpenBSD port of the tcpconns plugin.
-vserver plugin by:
- Sebastian Harl <sh at tokkee.org>
+Niki W. Waibel <niki.waibel at newlogic.com>
+ - Initial autotools fixes.
+ - libltdl code.
+ - getmnt-wizardry.
-PID-file patch by:
- Tommie Gannert <d00-tga at d.kth.se>
+Oleg King <king2 at kaluga.ru>
+ - Added support for the statgrab library to
+ + the cpu plugin,
+ + the disk plugin, and
+ + the users plugin.
-Don't-fork-patch by:
- Alvaro Barcellos <alvaro.barcellos at gmail.com>
+Peter Holik <peter at holik.at>
+ - cpufreq plugin.
+ - multimeter plugin.
+ - irq plugin.
+ - Some bugfixes in the exec plugin.
+ - Notifications in the ipmi plugin.
-Many autotools related fixes, libltdl code, getmnt-wizardry and much help has
-contributed:
- Niki W. Waibel <niki.waibel at newlogic.com>
+Richard W. M. Jones <rjones at redhat.com>
+ - libvirt plugin.
+ - uuid plugin.
-The `log' mode has been written by:
- Christophe Kalt <collectd at klb.taranis.org>
+Scott Garrett <sgarrett at technomancer.com>
+ - tape plugin.
-Much time and effort to find a nasty bug in the ntpd-plugin has been
-contributed by:
- Luboš Staněk <lubek at users.sourceforge.net>
+Sjoerd van der Berg <harekiet at gmail.com>
+ - iptables plugin.
-Support for the statgrab library has been added to the cpu, disk and users
-plugins by
- Oleg King <king2 at kaluga.ru>
+Stefan Hacker <stefan.hacker at web.de>
+ - teamspeak2 plugin.
+
+Tommie Gannert <d00-tga at d.kth.se>
+ - PID-file patch.
+
+Vincent Stehlé <vincent.stehle at free.fr>
+ - hddtemp plugin.
collectd is available at:
<http://collectd.org/>
+2008-09-04, Version 4.5.0
+ * collectd: Added the ability to flush certain identifiers.
+ * collectd: The concept of `notification meta data' has been
+ introduced.
+ * filecount plugin: The new filecount plugin counts the number of
+ files in a directory and its subdirectories.
+ * ipmi plugin: Sensor names have been changed to ensure unique names.
+ Notifications upon added and removed sensors can now be generated.
+ * notify_desktop plugin: This new plugin sends notifications to the
+ X desktop using the structure defined in the `Desktop Notification
+ Specification'.
+ * notify_email plugin: This new plugin sends out notifications via
+ email, using the `esmtp' library.
+ * onewire plugin: The new experimental(!) onewire plugin reads values,
+ such as temperatures, from sensors connected to the computer via the
+ onewire bus.
+ * perl plugin: Improved synchronized access to internal data structures
+ and fixed a possible dead-lock.
+ * perl plugin: Added the ability to flush certain identifiers and marked
+ plugin_flush_all() and plugin_flush_one() as deprecated in favor of
+ plugin_flush().
+ * perl plugin: Added the ability to configure Perl plugins.
+ * postgresql plugin: The new postgresql plugin collects statistics
+ about or from a PostgreSQL database.
+ * processes plugin: The `ProcessMatch' option has been added.
+ * rrdtool plugin: Implement throttling of the `update queue' to lessen
+ IO load.
+ * tcpconns plugin: This plugin has been ported to OpenBSD.
+ * thermal plugin: The new thermal plugin collects system temperatures
+ using Linux ACPI thermal zone data.
+
+2008-09-01, Version 4.4.3
+ * collectd: Fix a memory leak in the threshold checking code.
+ * memcached plugin: Fix a too short timeout and a related file
+ descriptor leak.
+ * memory plugin: A typo in the libstatgrab code has been fixed.
+ * snmp plugin: Fix a possible memory leak.
+
2008-07-15, Version 4.4.2
* build system: Use pkg-config to detect the upsclient library.
* collectd: Try even harder to determine the endianess of the
* vmem plugin: The new vmem plugin collects very detailed statistics
about the virtual memory subsystem of Linux.
+2008-08-30, Version 4.3.4
+ * Build system: Improved detection of and linking with the statgrab
+ library.
+ * collectd: Portability fixes, especially to determine endianess more
+ reliable.
+ * Various plugins: Fix format strings.
+ * disk plugin: A fix for giving disks under Linux 2.4 the right names
+ again has been applied.
+ * memcached plugin: Fix a too short timeout and a related file
+ descriptor leak.
+ * memory plugin: A typo in the libstatgrab code has been fixed.
+ * network plugin: A fix in the initialization function solves problems
+ under Solaris.
+ * nginx plugin: A thread-unsafe function has been replaced.
+ * vserver plugin: A thread-unsafe function has been replaced.
+ * wireless plugin: A work-around for broken wireless drivers has been
+ added.
+
2008-04-22, Version 4.3.3
* build system: Improved detection of several libraries, especially if
they are in non-standard paths.
Values gathered by a custom program or script.
See collectd-exec(5).
+ - filecount
+ Count the number of files in directories.
+
- hddtemp
Harddisk temperatures using hddtempd.
Network UPS tools: UPS current, voltage, power, charge, utilisation,
temperature, etc. See upsd(8).
- - onewire
+ - onewire (EXPERIMENTAL!)
Read onewire sensors using the owcapu library of the owfs project.
+ Please read in collectd.conf(5) why this plugin is experimental.
- perl
The perl plugin implements a Perl-interpreter into collectd. You can
-For version 4.4:
-* PowerDNS plugin
-
-For version 4.3:
-* unixsock plugin: Remove the custom cache if possible.
+* Parse options/identifiers with spaces in them correctly.
+* Figure out what to do with the onewire plugin.
+* Custom notification messages?
src/battery.c: commend not working code.
TYPE_LOG
TYPE_NOTIF
TYPE_FLUSH
+ TYPE_CONFIG
TYPE_DATASET
) ],
'ds_types' => [ qw(
Exporter::export_ok_tags ('all');
my @plugins : shared = ();
+my %cf_callbacks : shared = ();
my %types = (
TYPE_INIT, "init",
sub plugin_call_all {
my $type = shift;
+ my %plugins;
+
our $cb_name = undef;
if (! defined $type) {
return;
}
- lock @plugins;
- foreach my $plugin (keys %{$plugins[$type]}) {
- my $p = $plugins[$type]->{$plugin};
+ {
+ lock %{$plugins[$type]};
+ %plugins = %{$plugins[$type]};
+ }
+
+ foreach my $plugin (keys %plugins) {
+ my $p = $plugins{$plugin};
my $status = 0;
return;
}
- if ((! defined $plugins[$type]) && (TYPE_DATASET != $type)) {
+ if ((! defined $plugins[$type]) && (TYPE_DATASET != $type)
+ && (TYPE_CONFIG != $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 ((TYPE_CONFIG == $type) && (! ref $data)) {
+ my $pkg = scalar caller;
+
+ if ($data !~ m/^$pkg\:\:/) {
+ $data = $pkg . "::" . $data;
+ }
+
+ lock %cf_callbacks;
+ $cf_callbacks{$name} = $data;
+ }
elsif ((TYPE_DATASET != $type) && (! ref $data)) {
my $pkg = scalar caller;
cb_name => $data,
);
- lock @plugins;
+ lock %{$plugins[$type]};
$plugins[$type]->{$name} = \%p;
}
else {
if (TYPE_DATASET == $type) {
return plugin_unregister_data_set ($name);
}
+ elsif (TYPE_CONFIG == $type) {
+ lock %cf_callbacks;
+ delete $cf_callbacks{$name};
+ }
elsif (defined $plugins[$type]) {
- lock @plugins;
+ lock %{$plugins[$type]};
delete $plugins[$type]->{$name};
}
else {
my %args = @_;
my $timeout = -1;
+ my @plugins = ();
+ my @ids = ();
DEBUG ("Collectd::plugin_flush:"
. (defined ($args{'timeout'}) ? " timeout = $args{'timeout'}" : "")
- . (defined ($args{'plugins'}) ? " plugins = $args{'plugins'}" : ""));
+ . (defined ($args{'plugins'}) ? " plugins = $args{'plugins'}" : "")
+ . (defined ($args{'identifiers'})
+ ? " identifiers = $args{'identifiers'}" : ""));
if (defined ($args{'timeout'}) && ($args{'timeout'} > 0)) {
$timeout = $args{'timeout'};
}
- if (! defined $args{'plugins'}) {
- plugin_flush_all ($timeout);
+ if (defined ($args{'plugins'})) {
+ if ("ARRAY" eq ref ($args{'plugins'})) {
+ @plugins = @{$args{'plugins'}};
+ }
+ else {
+ @plugins = ($args{'plugins'});
+ }
}
else {
- if ("ARRAY" eq ref ($args{'plugins'})) {
- foreach my $plugin (@{$args{'plugins'}}) {
- plugin_flush_one ($timeout, $plugin);
- }
+ @plugins = (undef);
+ }
+
+ if (defined ($args{'identifiers'})) {
+ if ("ARRAY" eq ref ($args{'identifiers'})) {
+ @ids = @{$args{'identifiers'}};
}
else {
- plugin_flush_one ($timeout, $args{'plugins'});
+ @ids = ($args{'identifiers'});
}
}
+ else {
+ @ids = (undef);
+ }
+
+ foreach my $plugin (@plugins) {
+ foreach my $id (@ids) {
+ _plugin_flush($plugin, $timeout, $id);
+ }
+ }
+}
+
+sub plugin_flush_one {
+ my $timeout = shift;
+ my $name = shift;
+
+ WARNING ("Collectd::plugin_flush_one is deprecated - "
+ . "use Collectd::plugin_flush instead.");
+
+ if (! (defined ($timeout) && defined ($name))) {
+ ERROR ("Usage: Collectd::plugin_flush_one(timeout, name)");
+ return;
+ }
+
+ plugin_flush (plugins => $name, timeout => $timeout);
+}
+
+sub plugin_flush_all {
+ my $timeout = shift;
+
+ WARNING ("Collectd::plugin_flush_all is deprecated - "
+ . "use Collectd::plugin_flush instead.");
+
+ if (! defined ($timeout)) {
+ ERROR ("Usage: Collectd::plugin_flush_all(timeout)");
+ return;
+ }
+
+ plugin_flush (timeout => $timeout);
+}
+
+sub _plugin_dispatch_config {
+ my $plugin = shift;
+ my $config = shift;
+
+ our $cb_name = undef;
+
+ if (! (defined ($plugin) && defined ($config))) {
+ return;
+ }
+
+ if (! defined $cf_callbacks{$plugin}) {
+ WARNING ("Found a configuration for the \"$plugin\" plugin, but "
+ . "the plugin isn't loaded or didn't register "
+ . "a configuration callback.");
+ return;
+ }
+
+ {
+ lock %cf_callbacks;
+ $cb_name = $cf_callbacks{$plugin};
+ }
+ call_by_name ($config);
}
1;
use IO::Socket::UNIX;
use Regexp::Common (qw(number));
+our $Debug = 0;
+
return (1);
+sub _debug
+{
+ if (!$Debug)
+ {
+ return;
+ }
+ print @_;
+}
+
sub _create_socket
{
my $path = shift;
return ($ident);
} # _parse_identifier
+sub _escape_argument
+{
+ my $string = shift;
+
+ if ($string =~ m/^\w+$/)
+ {
+ return ("$string");
+ }
+
+ $string =~ s#\\#\\\\#g;
+ $string =~ s#"#\\"#g;
+ $string = "\"$string\"";
+
+ return ($string);
+}
+
=head1 PUBLIC METHODS
=over 4
my %args = @_;
my $status;
- my $fh = $obj->{'sock'} or confess;
+ my $fh = $obj->{'sock'} or confess ('object has no filehandle');
my $msg;
my $identifier;
$identifier = _create_identifier (\%args) or return;
- $msg = "GETVAL $identifier\n";
- #print "-> $msg";
- send ($fh, $msg, 0) or confess ("send: $!");
+ $msg = 'GETVAL ' . _escape_argument ($identifier) . "\n";
+ _debug "-> $msg";
+ print $fh $msg;
- $msg = undef;
- recv ($fh, $msg, 1024, 0) or confess ("recv: $!");
- #print "<- $msg";
+ $msg = <$fh>;
+ chomp ($msg);
+ _debug "<- $msg\n";
($status, $msg) = split (' ', $msg, 2);
if ($status <= 0)
return;
}
- for (split (' ', $msg))
+ for (my $i = 0; $i < $status; $i++)
{
- my $entry = $_;
+ my $entry = <$fh>;
+ chomp ($entry);
+ _debug "<- $entry\n";
+
if ($entry =~ m/^(\w+)=NaN$/)
{
$ret->{$1} = undef;
my $msg;
my $identifier;
my $values;
+ my $interval = "";
+
+ if (defined $args{'interval'})
+ {
+ $interval = ' interval='
+ . _escape_argument ($args{'interval'});
+ }
$identifier = _create_identifier (\%args) or return;
if (!$args{'values'})
$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";
+ $msg = 'PUTVAL '
+ . _escape_argument ($identifier)
+ . $interval
+ . ' ' . _escape_argument ($values) . "\n";
+ _debug "-> $msg";
+ print $fh $msg;
+
+ $msg = <$fh>;
+ chomp ($msg);
+ _debug "<- $msg\n";
($status, $msg) = split (' ', $msg, 2);
return (1) if ($status == 0);
my $status;
my $fh = $obj->{'sock'} or confess;
- $msg = "LISTVAL\n";
- send ($fh, $msg, 0) or confess ("send: $!");
+ _debug "LISTVAL\n";
+ print $fh "LISTVAL\n";
$msg = <$fh>;
+ chomp ($msg);
+ _debug "<- $msg\n";
($status, $msg) = split (' ', $msg, 2);
if ($status < 0)
{
$msg = <$fh>;
chomp ($msg);
+ _debug "<- $msg\n";
($time, $ident) = split (' ', $msg, 2);
my $fh = $obj->{'sock'} or confess;
my $msg; # message sent to the socket
- my $opt_msg; # message of the notification
if (!$args{'message'})
{
$args{'time'} = time ();
}
- $opt_msg = $args{'message'};
- delete ($args{'message'});
-
$msg = 'PUTNOTIF '
- . join (' ', map { $_ . '=' . $args{$_} } (keys %args))
- . " message=$opt_msg\n";
+ . join (' ', map { $_ . '=' . _escape_argument ($args{$_}) } (keys %args))
+ . "\n";
- send ($fh, $msg, 0) or confess ("send: $!");
- $msg = undef;
- recv ($fh, $msg, 1024, 0) or confess ("recv: $!");
+ _debug "-> $msg";
+ print $fh $msg;
+
+ $msg = <$fh>;
+ chomp ($msg);
+ _debug "<- $msg\n";
($status, $msg) = split (' ', $msg, 2);
return (1) if ($status == 0);
return;
}
- $msg .= " identifier=$ident_str";
+ $msg .= ' identifier=' . _escape_argument ($ident_str);
}
}
$msg .= "\n";
- send ($fh, $msg, 0) or confess ("send: $!");
- $msg = undef;
- recv ($fh, $msg, 1024, 0) or confess ("recv: $!");
+ _debug "-> $msg";
+ print $fh $msg;
+
+ $msg = <$fh>;
+ chomp ($msg);
+ _debug "<- $msg\n";
($status, $msg) = split (' ', $msg, 2);
return (1) if ($status == 0);
return;
}
+sub error
+{
+ my $obj = shift;
+ if ($obj->{'error'})
+ {
+ return ($obj->{'error'});
+ }
+ return;
+}
+
=item I<$obj>-E<gt>destroy ();
Closes the socket before the object is destroyed. This function is also
*darwin*)
ac_system="Darwin"
;;
+ *openbsd*)
+ ac_system="OpenBSD"
+ ;;
*)
ac_system="unknown"
esac
AC_MSG_RESULT([$ac_system])
+if test "x$ac_system" = "xLinux"
+then
+ AC_ARG_VAR([KERNEL_DIR], [path to Linux kernel sources])
+ if test -z "$KERNEL_DIR"
+ then
+ KERNEL_DIR="/lib/modules/`uname -r`/source"
+ fi
+
+ KERNEL_CFLAGS="-I$KERNEL_DIR/include"
+ AC_SUBST(KERNEL_CFLAGS)
+fi
+
#
# Checks for header files.
#
have_ip_vs_h="no"
if test "x$ac_system" = "xLinux"
then
+ SAVE_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS $KERNEL_CFLAGS"
+
AC_CHECK_HEADERS(net/ip_vs.h, [have_net_ip_vs_h="yes"])
AC_CHECK_HEADERS(ip_vs.h, [have_ip_vs_h="yes"])
+
+ CFLAGS=$SAVE_CFLAGS
fi
# For quota module
AC_SUBST(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
fi
-AC_CHECK_LIB(kvm, kvm_getswapinfo, [with_libkvm="yes"], [with_libkvm="no"])
-if test "x$with_libkvm" = "xyes"
+with_libkvm="no"
+AC_CHECK_LIB(kvm, kvm_getprocs, [with_kvm_getprocs="yes"], [with_kvm_getprocs="no"])
+if test "x$with_kvm_getprocs" = "xyes"
+then
+ AC_DEFINE(HAVE_LIBKVM_GETPROCS, 1,
+ [Define to 1 if you have the 'kvm' library with the 'kvm_getprocs' symbol (-lkvm)])
+ with_libkvm="yes"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETPROCS, test "x$with_kvm_getprocs" = "xyes")
+
+AC_CHECK_LIB(kvm, kvm_getswapinfo, [with_kvm_getswapinfo="yes"], [with_kvm_getswapinfo="no"])
+if test "x$with_kvm_getswapinfo" = "xyes"
then
- AC_DEFINE(HAVE_LIBKVM, 1, [Define to 1 if you have the 'kvm' library (-lkvm)])
+ AC_DEFINE(HAVE_LIBKVM_GETSWAPINFO, 1,
+ [Define to 1 if you have the 'kvm' library with the 'kvm_getswapinfo' symbol (-lkvm)])
+ with_libkvm="yes"
fi
-AM_CONDITIONAL(BUILD_WITH_LIBKVM, test "x$with_libkvm" = "xyes")
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_GETSWAPINFO, test "x$with_kvm_getswapinfo" = "xyes")
+
+AC_CHECK_LIB(kvm, kvm_nlist, [with_kvm_nlist="yes"], [with_kvm_nlist="no"])
+if test "x$with_kvm_nlist" = "xyes"
+then
+ AC_DEFINE(HAVE_LIBKVM_NLIST, 1,
+ [Define to 1 if you have the 'kvm' library with the 'kvm_nlist' symbol (-lkvm)])
+ with_libkvm="yes"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBKVM_NLIST, test "x$with_kvm_nlist" = "xyes")
with_sensors_cflags=""
with_sensors_ldflags=""
perl_interpreter="perl"
AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
[
- if test "x$withval" != "xno" && test "x$withval" != "xyes"
+ if test -x "$withval"
+ then
+ perl_interpreter="$withval"
+ with_libperl="yes"
+ else if test "x$withval" != "xno" && test "x$withval" != "xyes"
then
LDFLAGS="$LDFLAGS -L$withval/lib"
CPPFLAGS="$CPPFLAGS -I$withval/include"
with_libperl="yes"
else
with_libperl="$withval"
- fi
+ fi; fi
],
[
with_libperl="yes"
perl_interpreter=`which "$perl_interpreter" 2> /dev/null`
if test -x "$perl_interpreter"
then
- AC_MSG_RESULT([yes])
+ AC_MSG_RESULT([yes ($perl_interpreter)])
else
perl_interpreter=""
AC_MSG_RESULT([no])
AC_LINK_IFELSE(
AC_LANG_PROGRAM(
[[
+#define PERL_NO_GET_CONTEXT
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
]],
[[
- PerlInterpreter *perl = NULL;
- Perl_load_module (perl, PERL_LOADMOD_NOIMPORT,
+ dTHX;
+ load_module (PERL_LOADMOD_NOIMPORT,
newSVpv ("Collectd::Plugin::FooBar", 24),
Nullsv);
]]),
LDFLAGS=$SAVE_LDFLAGS
fi
-if test "x$ac_system" = "xLinux"
-then
- AC_ARG_VAR([KERNEL_DIR], [path to Linux kernel sources])
- if test -z "$KERNEL_DIR"
- then
- KERNEL_DIR="/lib/modules/`uname -r`/source"
- fi
-fi
-
with_own_libiptc="no"
AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])],
[
if test "x$with_libiptc" = "xyes"
then
SAVE_CFLAGS=$CFLAGS
- CFLAGS="$CFLAGS -I$KERNEL_DIR/include"
+ CFLAGS="$CFLAGS $KERNEL_CFLAGS"
AC_CHECK_HEADERS(linux/netfilter_ipv4/ip_tables.h linux/netfilter_ipv6/ip6_tables.h, [],
[
fi
fi
+if test "x$ac_system" = "xOpenBSD"
+then
+ plugin_tcpconns="yes"
+fi
+
# Mac OS X devices
if test "x$with_libiokit" = "xyes"
then
plugin_processes="yes"
fi
-if test "x$with_libkvm" = "xyes"
+if test "x$with_kvm_getprocs" = "xyes"
then
plugin_processes="yes"
+fi
+
+if test "x$with_kvm_getswapinfo" = "xyes"
+then
plugin_swap="yes"
fi
+if test "x$with_kvm_nlist" = "xyes"
+then
+ plugin_tcpconns="yes"
+fi
+
if test "x$have_getutent" = "xyes"
then
plugin_users="yes"
AC_PLUGIN([email], [yes], [EMail statistics])
AC_PLUGIN([entropy], [$plugin_entropy], [Entropy statistics])
AC_PLUGIN([exec], [yes], [Execution of external programs])
+AC_PLUGIN([filecount], [yes], [Count files in directories])
AC_PLUGIN([hddtemp], [yes], [Query hddtempd])
AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics])
AC_PLUGIN([iptables], [$with_libiptc], [IPTables rule counters])
AC_PLUGIN([postgresql], [$with_libpq], [PostgreSQL database statistics])
AC_PLUGIN([powerdns], [yes], [PowerDNS statistics])
AC_PLUGIN([processes], [$plugin_processes], [Process statistics])
-AC_PLUGIN([qmail], [yes], [QMail queue statistics])
AC_PLUGIN([rrdtool], [$with_rrdtool], [RRDTool output plugin])
AC_PLUGIN([rrdcached], [$librrd_rrdc_update], [RRDTool output plugin])
AC_PLUGIN([sensors], [$with_lm_sensors], [lm_sensors statistics])
email . . . . . . . . $enable_email
entropy . . . . . . . $enable_entropy
exec . . . . . . . . $enable_exec
+ filecount . . . . . . $enable_filecount
hddtemp . . . . . . . $enable_hddtemp
interface . . . . . . $enable_interface
iptables . . . . . . $enable_iptables
postgresql . . . . . $enable_postgresql
powerdns . . . . . . $enable_powerdns
processes . . . . . . $enable_processes
- qmail . . . . . . . . $enable_qmail
rrdtool . . . . . . . $enable_rrdtool
rrdcached . . . . . . $enable_rrdcached
sensors . . . . . . . $enable_sensors
'GPRINT:pg_blks_avg:AVERAGE:%4.1lf%s Avg,',
'GPRINT:pg_blks_max:MAX:%4.1lf%s Max,',
'GPRINT:pg_blks_avg:LAST:%4.1lf%s Last'],
+ pg_db_size => ['DEF:pg_db_size_avg={file}:value:AVERAGE',
+ 'DEF:pg_db_size_min={file}:value:MIN',
+ 'DEF:pg_db_size_max={file}:value:MAX',
+ "AREA:pg_db_size_max#$HalfBlue",
+ "AREA:pg_db_size_min#$Canvas",
+ "LINE1:pg_db_size_avg#$FullBlue:Bytes",
+ 'GPRINT:pg_db_size_min:MIN:%4.1lf%s Min,',
+ 'GPRINT:pg_db_size_avg:AVERAGE:%4.1lf%s Avg,',
+ 'GPRINT:pg_db_size_max:MAX:%4.1lf%s Max,',
+ 'GPRINT:pg_db_size_avg:LAST:%4.1lf%s Last'],
pg_n_tup_c => ['DEF:pg_n_tup_avg={file}:value:AVERAGE',
'DEF:pg_n_tup_min={file}:value:MIN',
'DEF:pg_n_tup_max={file}:value:MAX',
use CGI (':cgi');
use RRDs ();
-use Collectd::Graph::TypeLoader (qw(tl_read_config tl_load_type));
+use Collectd::Graph::Config (qw(gc_read_config gc_get_scalar));
+use Collectd::Graph::TypeLoader (qw(tl_load_type));
use Collectd::Graph::Common (qw(sanitize_type get_selected_files
- epoch_to_rfc1123));
+ epoch_to_rfc1123 flush_files));
use Collectd::Graph::Type ();
our $Debug = param ('debug');
our $Begin = param ('begin');
our $End = param ('end');
+our $GraphWidth = param ('width');
if ($Debug)
{
HTTP
}
-tl_read_config ("$RealBin/../etc/collection3.conf");
+gc_read_config ("$RealBin/../etc/collection.conf");
+
+if ($GraphWidth)
+{
+ $GraphWidth =~ s/\D//g;
+}
+
+if (!$GraphWidth)
+{
+ $GraphWidth = gc_get_scalar ('GraphWidth', 400);
+}
{ # Sanitize begin and end times
$End ||= 0;
}
my $expires = time ();
+# IF (End is `now')
+# OR (Begin is before `now' AND End is after `now')
if (($End == 0) || (($Begin <= $expires) && ($End >= $expires)))
{
# 400 == width in pixels
- my $timespan = $expires - $Begin;
- $expires += int ($timespan / 400);
+ my $timespan;
+
+ if ($End == 0)
+ {
+ $timespan = $expires - $Begin;
+ }
+ else
+ {
+ $timespan = $End - $Begin;
+ }
+ $expires += int ($timespan / 400.0);
}
+# IF (End is not `now')
+# AND (End is before `now')
+# ==> Graph will never change again!
elsif (($End > 0) && ($End < $expires))
{
- $expires += 366 * 86400;
+ $expires += (366 * 86400);
}
elsif ($Begin > $expires)
{
$expires = $Begin;
}
+# Send FLUSH command to the daemon if necessary and possible.
+flush_files ($files,
+ begin => $Begin,
+ end => $End,
+ addr => gc_get_scalar ('UnixSockAddr', undef),
+ interval => gc_get_scalar ('Interval', 10));
+
print STDOUT header (-Content_type => 'image/png',
-Last_Modified => epoch_to_rfc1123 ($obj->getLastModified ()),
-Expires => epoch_to_rfc1123 ($expires));
+if ($Debug)
+{
+ print "\$expires = $expires;\n";
+}
+
my $args = $obj->getRRDArgs (0);
if ($Debug)
}
$| = 1;
- RRDs::graph ('-', '-a', 'PNG', @timesel, @$args);
+ RRDs::graph ('-', '-a', 'PNG', '--width', $GraphWidth, @timesel, @$args);
if (my $err = RRDs::error ())
{
print STDERR "RRDs::graph failed: $err\n";
use Data::Dumper;
-use Collectd::Graph::TypeLoader (qw(tl_read_config tl_load_type));
+use Collectd::Graph::Config (qw(gc_read_config gc_get_scalar));
+use Collectd::Graph::TypeLoader (qw(tl_load_type));
use Collectd::Graph::Common (qw(get_files_from_directory get_all_hosts
get_timespan_selection get_selected_files get_host_selection
- get_plugin_selection));
+ get_plugin_selection flush_files));
use Collectd::Graph::Type ();
our $Debug = param ('debug') ? 1 : 0;
exit 1;
}
-tl_read_config ("$RealBin/../etc/collection3.conf");
+gc_read_config ("$RealBin/../etc/collection.conf");
$Actions{$action}->();
exit (0);
{
return if ($html_started);
+ my $end;
+ my $begin;
+ my $timespan;
+
+ $end = time ();
+ $timespan = get_timespan_selection ();
+ $begin = $end - $timespan;
+
if (can_handle_xhtml ())
{
print <<HTML;
<title>collection.cgi, Version 3</title>
<link rel="icon" href="../share/shortcut-icon.png" type="image/png" />
<link rel="stylesheet" href="../share/style.css" type="text/css" />
+ <script type="text/javascript" src="../share/navigate.js" />
</head>
- <body>
+ <body onload="nav_init ($begin, $end);">
HTML
$html_started = 1;
}}
start_html ();
show_selector ();
- my $ident = {};
-
my $all_files;
+ my $timespan;
+
my $types = {};
+ my $id_counter = 0;
+
$all_files = get_selected_files ();
+ $timespan = get_timespan_selection ();
if ($Debug)
{
print "<pre>", Data::Dumper->Dump ([$all_files], ['all_files']), "</pre>\n";
}
+ # Send FLUSH command to the daemon if necessary and possible.
+ flush_files ($all_files,
+ begin => time () - $timespan,
+ end => time (),
+ addr => gc_get_scalar ('UnixSockAddr', undef),
+ interval => gc_get_scalar ('Interval', 10));
+
for (@$all_files)
{
my $file = $_;
my $type = $_;
my $graphs_num = $types->{$type}->getGraphsNum ();
- my $timespan = get_timespan_selection ();
-
for (my $i = 0; $i < $graphs_num; $i++)
{
my $args = $types->{$type}->getGraphArgs ($i);
my $url = encode_entities ("graph.cgi?$args;begin=-$timespan");
+ my $id = sprintf ("graph%04i", $id_counter++);
print " <tr>\n";
print " <td rowspan=\"$graphs_num\">$type</td>\n" if ($i == 0);
-
- print qq# <td><img src="$url" /></td>\n#;
+ print <<EOF;
+ <td>
+ <div class="graph_canvas">
+ <div class="graph_float">
+ <img id="${id}" class="graph_image"
+ alt="A graph"
+ src="$url" />
+ <div class="controls zoom">
+ <div title="Earlier"
+ onclick="nav_move_earlier ('${id}');">←</div>
+ <div title="Zoom out"
+ onclick="nav_zoom_out ('${id}');">-</div>
+ <div title="Zoom in"
+ onclick="nav_zoom_in ('${id}');">+</div>
+ <div title="Later"
+ onclick="nav_move_later ('${id}');">→</div>
+ </div>
+ <div class="controls preset">
+ <div title="Show current hour"
+ onclick="nav_time_reset ('${id}', 3600);">H</div>
+ <div title="Show current day"
+ onclick="nav_time_reset ('${id}', 86400);">D</div>
+ <div title="Show current week"
+ onclick="nav_time_reset ('${id}', 7 * 86400);">W</div>
+ <div title="Show current month"
+ onclick="nav_time_reset ('${id}', 31 * 86400);">M</div>
+ <div title="Show current year"
+ onclick="nav_time_reset ('${id}', 366 * 86400);">Y</div>
+ <div title="Set all images to this timespan"
+ onclick="nav_set_reference ('${id}');">!</div>
+ </div>
+ </div>
+ </div>
+ </td>
+EOF
+ # print qq# <td><img src="$url" /></td>\n#;
print " </tr>\n";
}
}
use Data::Dumper;
-use Collectd::Graph::TypeLoader (qw(tl_read_config tl_load_type));
+use Collectd::Graph::Config (qw(gc_read_config));
+use Collectd::Graph::TypeLoader (qw(tl_load_type));
use Collectd::Graph::Common (qw(get_all_hosts get_files_for_host type_to_module_name));
use Collectd::Graph::Type ();
our $Debug = param ('debug') ? 1 : 0;
-tl_read_config ("$RealBin/../etc/collection3.conf");
+gc_read_config ("$RealBin/../etc/collection.conf");
if ($Debug)
{
--- /dev/null
+GraphWidth 400
+UnixSockAddr "/var/run/collectd-unixsock"
+<Type apache_scoreboard>
+ Module GenericStacked
+ DataSources count
+ RRDTitle "Apache scoreboard on {hostname}"
+ RRDVerticalLabel "Slots"
+ RRDFormat "%6.2lf"
+ DSName closing Closing
+ DSName dnslookup DNS lookup
+ DSName finishing Finishing
+ DSName idle_cleanup Idle cleanup
+ DSName keepalive Keep alive
+ DSName logging Logging
+ DSName open Open (empty)
+ DSName reading Reading
+ DSName sending Sending
+ DSName starting Starting
+ DSName waiting Waiting
+ Order open closing dnslookup finishing idle_cleanup keepalive logging open reading sending starting waiting
+ Color closing 000080
+ Color dnslookup ff0000
+ Color finishing 008080
+ Color idle_cleanup ffff00
+ Color keepalive 0080ff
+ Color logging a000a0
+ Color open e0e0e0
+ Color reading 0000ff
+ Color sending 00e000
+ Color starting ff00ff
+ Color waiting ffb000
+</Type>
+<Type cpu>
+ Module GenericStacked
+ DataSources value
+ RRDTitle "CPU {plugin_instance} usage"
+ RRDVerticalLabel "Jiffies"
+ RRDFormat "%5.2lf"
+ DSName idle Idle
+ DSName nice Nice
+ DSName user User
+ DSName wait Wait-IO
+ DSName system System
+ DSName softirq SoftIRQ
+ DSName interrupt IRQ
+ DSName steal Steal
+ Order idle nice user wait system softirq interrupt steal
+ Color idle e8e8e8
+ Color nice 00e000
+ Color user 0000ff
+ Color wait ffb000
+ Color system ff0000
+ Color softirq ff00ff
+ Color interrupt a000a0
+ Color steal 000000
+</Type>
+<Type current>
+ DataSources value
+ DSName value Current
+ RRDTitle "Current ({type_instance})"
+ RRDVerticalLabel "Ampere"
+ RRDFormat "%4.1lfA"
+ Color value ffb000
+</Type>
+<Type df>
+ Module Df
+ DataSources free used
+</Type>
+<Type disk_octets>
+ Module GenericIO
+ DataSources read write
+ DSName "read Read "
+ DSName write Written
+ RRDTitle "Disk Traffic ({plugin_instance})"
+ RRDVerticalLabel "Bytes per second"
+# RRDOptions ...
+ RRDFormat "%5.1lf%s"
+</Type>
+<Type disk_ops>
+ Module GenericIO
+ DataSources read write
+ DSName "read Read "
+ DSName write Written
+ RRDTitle "Disk Operations ({plugin_instance})"
+ RRDVerticalLabel "Operations per second"
+# RRDOptions ...
+ RRDFormat "%5.1lf"
+</Type>
+<Type disk_merged>
+ Module GenericIO
+ DataSources read write
+ DSName "read Read "
+ DSName write Written
+ RRDTitle "Disk Merged Operations ({plugin_instance})"
+ RRDVerticalLabel "Merged operations/s"
+# RRDOptions ...
+ RRDFormat "%5.1lf"
+</Type>
+<Type disk_time>
+ Module GenericIO
+ DataSources read write
+ DSName "read Read "
+ DSName write Written
+ RRDTitle "Disk time per operation ({plugin_instance})"
+ RRDVerticalLabel "Avg. Time/Op"
+# RRDOptions ...
+ RRDFormat "%5.1lf%ss"
+ Scale 0.001
+</Type>
+<Type entropy>
+ DataSources entropy
+ DSName entropy Entropy bits
+ RRDTitle "Available entropy on {hostname}"
+ RRDVerticalLabel "Bits"
+ RRDFormat "%4.0lf"
+</Type>
+<Type fanspeed>
+ DataSources value
+ DSName value RPM
+ RRDTitle "Fanspeed ({instance})"
+ RRDVerticalLabel "RPM"
+ RRDFormat "%6.1lf"
+ Color value 00b000
+</Type>
+<Type frequency>
+ DataSources frequency
+ DSName frequency Frequency
+ RRDTitle "Frequency ({type_instance})"
+ RRDVerticalLabel "Hertz"
+ RRDFormat "%4.1lfHz"
+ Color frequency a000a0
+</Type>
+<Type humidity>
+ DataSources value
+ DSName value Humitidy
+ RRDTitle "Humitidy ({instance})"
+ RRDVerticalLabel "Percent"
+ RRDFormat "%4.1lf%%"
+ Color value 00e000
+</Type>
+<Type if_errors>
+ Module GenericIO
+ DataSources rx tx
+ DSName rx RX
+ DSName tx TX
+ RRDTitle "Interface Errors ({type_instance})"
+ RRDVerticalLabel "Errors per second"
+# RRDOptions ...
+ RRDFormat "%.3lf"
+</Type>
+<Type if_rx_errors>
+ Module GenericStacked
+ DataSources value
+ RRDTitle "Interface receive errors ({plugin_instance})"
+ RRDVerticalLabel "Erorrs/s"
+ RRDFormat "%.1lf"
+ Color length f00000
+ Color over 00e0ff
+ Color crc 00e000
+ Color frame ffb000
+ Color fifo f000c0
+ Color missed 0000f0
+</Type>
+<Type if_tx_errors>
+ Module GenericStacked
+ DataSources value
+ RRDTitle "Interface transmit errors ({plugin_instance})"
+ RRDVerticalLabel "Erorrs/s"
+ RRDFormat "%.1lf"
+ Color aborted f00000
+ Color carrier 00e0ff
+ Color fifo 00e000
+ Color heartbeat ffb000
+ Color window f000c0
+</Type>
+<Type if_octets>
+ Module GenericIO
+ DataSources rx tx
+ DSName rx RX
+ DSName tx TX
+ RRDTitle "Interface Traffic ({type_instance})"
+ RRDVerticalLabel "Bits per second"
+# RRDOptions ...
+ RRDFormat "%5.1lf%s"
+ Scale 8
+</Type>
+<Type if_packets>
+ Module GenericIO
+ DataSources rx tx
+ DSName rx RX
+ DSName tx TX
+ RRDTitle "Interface Packets ({type_instance})"
+ RRDVerticalLabel "Packets per second"
+# RRDOptions ...
+ RRDFormat "%5.1lf%s"
+</Type>
+<Type ipt_bytes>
+ DataSources value
+ DSName value Bytes/s
+ RRDTitle "Traffic ({type_instance})"
+ RRDVerticalLabel "Bytes per second"
+# RRDOptions ...
+ RRDFormat "%5.1lf%s"
+</Type>
+<Type ipt_packets>
+ DataSources value
+ DSName value Packets/s
+ RRDTitle "Packets ({type_instance})"
+ RRDVerticalLabel "Packets per second"
+# RRDOptions ...
+ RRDFormat "%5.1lf"
+</Type>
+<Type irq>
+ Module GenericStacked
+ DataSources value
+ RRDTitle "Interrupts on {hostname}"
+ RRDVerticalLabel "IRQs/s"
+ RRDFormat "%5.1lf"
+</Type>
+<Type load>
+ Module Load
+</Type>
+<Type memory>
+ Module GenericStacked
+ DataSources value
+ RRDTitle "Physical memory utilization on {hostname}"
+ RRDVerticalLabel "Bytes"
+ RRDFormat "%5.1lf%s"
+ RRDOptions -b 1024 -l 0
+ DSName "free Free "
+ DSName "cached Cached "
+ DSName "buffered Buffered"
+ DSName "used Used "
+ #Order used buffered cached free
+ Order free cached buffered used
+ Color free 00e000
+ Color cached 0000ff
+ Color buffered ffb000
+ Color used ff0000
+</Type>
+<Type percent>
+ DataSources percent
+ DSName percent Percent
+ RRDTitle "Percent ({type_instance})"
+ RRDVerticalLabel "Percent"
+ RRDFormat "%4.1lf%%"
+ Color percent 0000ff
+</Type>
+<Type ping>
+ DataSources ping
+ DSName "ping Latency"
+ RRDTitle "Network latency ({type_instance})"
+ RRDVerticalLabel "Milliseconds"
+ RRDFormat "%5.2lfms"
+</Type>
+<Type power>
+ DataSources value
+ DSName value Watts
+ RRDTitle "Power ({type_instance})"
+ RRDVerticalLabel "Watts"
+ RRDFormat "%6.2lf%sW"
+ Color value 008080
+</Type>
+<Type ps_rss>
+ DataSources value
+ DSName value RSS
+ RRDTitle "Resident Segment Size ({instance})"
+ RRDVerticalLabel "Bytes"
+ RRDFormat "%6.2lf%s"
+</Type>
+<Type ps_state>
+ Module GenericStacked
+ DataSources value
+ RRDTitle "Processes on {hostname}"
+ RRDVerticalLabel "Processes"
+ RRDFormat "%5.1lf%s"
+ DSName running Running
+ DSName sleeping Sleeping
+ DSName paging Paging
+ DSName zombies Zombies
+ DSName blocked Blocked
+ DSName stopped Stopped
+ Order paging blocked zombies stopped running sleeping
+ Color running 00e000
+ Color sleeping 0000ff
+ Color paging ffb000
+ Color zombies ff0000
+ Color blocked ff00ff
+ Color stopped a000a0
+</Type>
+<Type swap>
+ Module GenericStacked
+ DataSources value
+ RRDTitle "Swap utilization on {hostname}"
+ RRDVerticalLabel "Bytes"
+ RRDFormat "%5.1lf%s"
+ RRDOptions -b 1024 -l 0
+ DSName "free Free "
+ DSName "cached Cached "
+ DSName "used Used "
+ #Order used cached free
+ Order free cached used
+ Color free 00e000
+ Color cached 0000ff
+ Color used ff0000
+</Type>
+<Type tcp_connections>
+ Module GenericStacked
+ DataSources value
+ RRDTitle "TCP connections ({plugin_instance})"
+ RRDVerticalLabel "Connections"
+ RRDFormat "%5.1lf"
+ Order LISTEN CLOSING LAST_ACK CLOSE_WAIT CLOSE TIME_WAIT FIN_WAIT2 FIN_WAIT1 SYN_RECV SYN_SENT ESTABLISHED CLOSED
+ Color ESTABLISHED 00e000
+ Color SYN_SENT 00e0ff
+ Color SYN_RECV 00e0a0
+ Color FIN_WAIT1 f000f0
+ Color FIN_WAIT2 f000a0
+ Color TIME_WAIT ffb000
+ Color CLOSE 0000f0
+ Color CLOSE_WAIT 0000a0
+ Color LAST_ACK 000080
+ Color LISTEN ff0000
+ Color CLOSING 000000
+ Color CLOSED 0000f0
+</Type>
+<Type temperature>
+ DataSources value
+ DSName value Temp
+ RRDTitle "Temperature ({instance})"
+ RRDVerticalLabel "°Celsius"
+ RRDFormat "%4.1lf°C"
+</Type>
+<Type users>
+ DataSources users
+ DSName users Users
+ RRDTitle "Users ({type_instance}) on {hostname}"
+ RRDVerticalLabel "Users"
+ RRDFormat "%.1lf"
+ Color users 0000f0
+</Type>
+<Type voltage>
+ DataSources value
+ DSName value Volts
+ RRDTitle "Voltage ({type_instance})"
+ RRDVerticalLabel "Volts"
+ RRDFormat "%4.1lfV"
+ Color value f00000
+</Type>
+# vim: set sw=2 sts=2 et syntax=apache fileencoding=latin-1 :
+++ /dev/null
-<Type cpu>
- Module GenericStacked
- DataSources value
- RRDTitle "CPU {plugin_instance} usage"
- RRDVerticalLabel "Jiffies"
- RRDFormat "%5.2lf"
- DSName idle Idle
- DSName nice Nice
- DSName user User
- DSName wait Wait-IO
- DSName system System
- DSName softirq SoftIRQ
- DSName interrupt IRQ
- DSName steal Steal
- Order idle nice user wait system softirq interrupt steal
- Color idle e8e8e8
- Color nice 00e000
- Color user 0000ff
- Color wait ffb000
- Color system ff0000
- Color softirq ff00ff
- Color interrupt a000a0
- Color steal 000000
-</Type>
-<Type df>
- Module Df
- DataSources free used
-</Type>
-<Type disk_octets>
- Module GenericIO
- DataSources read write
- DSName "read Read "
- DSName write Written
- RRDTitle "Disk Traffic ({plugin_instance})"
- RRDVerticalLabel "Bytes per second"
-# RRDOptions ...
- RRDFormat "%5.1lf%s"
-</Type>
-<Type disk_ops>
- Module GenericIO
- DataSources read write
- DSName "read Read "
- DSName write Written
- RRDTitle "Disk Operations ({plugin_instance})"
- RRDVerticalLabel "Operations per second"
-# RRDOptions ...
- RRDFormat "%5.1lf"
-</Type>
-<Type disk_merged>
- Module GenericIO
- DataSources read write
- DSName "read Read "
- DSName write Written
- RRDTitle "Disk Merged Operations ({plugin_instance})"
- RRDVerticalLabel "Merged operations/s"
-# RRDOptions ...
- RRDFormat "%5.1lf"
-</Type>
-<Type disk_time>
- Module GenericIO
- DataSources read write
- DSName "read Read "
- DSName write Written
- RRDTitle "Disk time per operation ({plugin_instance})"
- RRDVerticalLabel "Avg. Time/Op"
-# RRDOptions ...
- RRDFormat "%5.1lf%ss"
- Scale 0.001
-</Type>
-<Type entropy>
- DataSources entropy
- DSName entropy Entropy bits
- RRDTitle "Available entropy on {hostname}"
- RRDVerticalLabel "Bits"
- RRDFormat "%4.0lf"
-</Type>
-<Type fanspeed>
- DataSources value
- DSName value RPM
- RRDTitle "Fanspeed ({type_instance})"
- RRDVerticalLabel "RPM"
- RRDFormat "%6.1lf"
- Color value 00b000
-</Type>
-<Type if_errors>
- Module GenericIO
- DataSources rx tx
- DSName rx RX
- DSName tx TX
- RRDTitle "Interface Errors ({type_instance})"
- RRDVerticalLabel "Errors per second"
-# RRDOptions ...
- RRDFormat "%.3lf"
-</Type>
-<Type if_rx_errors>
- Module GenericStacked
- DataSources value
- RRDTitle "Interface receive errors ({plugin_instance})"
- RRDVerticalLabel "Erorrs/s"
- RRDFormat "%.1lf"
- Color length f00000
- Color over 00e0ff
- Color crc 00e000
- Color frame ffb000
- Color fifo f000c0
- Color missed 0000f0
-</Type>
-<Type if_tx_errors>
- Module GenericStacked
- DataSources value
- RRDTitle "Interface transmit errors ({plugin_instance})"
- RRDVerticalLabel "Erorrs/s"
- RRDFormat "%.1lf"
- Color aborted f00000
- Color carrier 00e0ff
- Color fifo 00e000
- Color heartbeat ffb000
- Color window f000c0
-</Type>
-<Type if_octets>
- Module GenericIO
- DataSources rx tx
- DSName rx RX
- DSName tx TX
- RRDTitle "Interface Traffic ({type_instance})"
- RRDVerticalLabel "Bits per second"
-# RRDOptions ...
- RRDFormat "%5.1lf%s"
- Scale 8
-</Type>
-<Type if_packets>
- Module GenericIO
- DataSources rx tx
- DSName rx RX
- DSName tx TX
- RRDTitle "Interface Packets ({type_instance})"
- RRDVerticalLabel "Packets per second"
-# RRDOptions ...
- RRDFormat "%5.1lf%s"
-</Type>
-<Type ipt_bytes>
- DataSources value
- DSName value Bytes/s
- RRDTitle "Traffic ({type_instance})"
- RRDVerticalLabel "Bytes per second"
-# RRDOptions ...
- RRDFormat "%5.1lf%s"
-</Type>
-<Type ipt_packets>
- DataSources value
- DSName value Packets/s
- RRDTitle "Packets ({type_instance})"
- RRDVerticalLabel "Packets per second"
-# RRDOptions ...
- RRDFormat "%5.1lf"
-</Type>
-<Type irq>
- Module GenericStacked
- DataSources value
- RRDTitle "Interrupts on {hostname}"
- RRDVerticalLabel "IRQs/s"
- RRDFormat "%5.1lf"
-</Type>
-<Type load>
- Module Load
-</Type>
-<Type memory>
- Module GenericStacked
- DataSources value
- RRDTitle "Physical memory utilization on {hostname}"
- RRDVerticalLabel "Bytes"
- RRDFormat "%5.1lf%s"
- RRDOptions -b 1024 -l 0
- DSName "free Free "
- DSName "cached Cached "
- DSName "buffered Buffered"
- DSName "used Used "
- #Order used buffered cached free
- Order free cached buffered used
- Color free 00e000
- Color cached 0000ff
- Color buffered ffb000
- Color used ff0000
-</Type>
-<Type ping>
- DataSources ping
- DSName "ping Latency"
- RRDTitle "Network latency ({type_instance})"
- RRDVerticalLabel "Milliseconds"
- RRDFormat "%5.2lfms"
-</Type>
-<Type ps_state>
- Module GenericStacked
- DataSources value
- RRDTitle "Processes on {hostname}"
- RRDVerticalLabel "Processes"
- RRDFormat "%5.1lf%s"
- DSName running Running
- DSName sleeping Sleeping
- DSName paging Paging
- DSName zombies Zombies
- DSName blocked Blocked
- DSName stopped Stopped
- Order paging blocked zombies stopped running sleeping
- Color running 00e000
- Color sleeping 0000ff
- Color paging ffb000
- Color zombies ff0000
- Color blocked ff00ff
- Color stopped a000a0
-</Type>
-<Type tcp_connections>
- Module GenericStacked
- DataSources value
- RRDTitle "TCP connections ({plugin_instance})"
- RRDVerticalLabel "Connections"
- RRDFormat "%5.1lf"
- Order LISTEN CLOSING LAST_ACK CLOSE_WAIT CLOSE TIME_WAIT FIN_WAIT2 FIN_WAIT1 SYN_RECV SYN_SENT ESTABLISHED CLOSED
- Color ESTABLISHED 00e000
- Color SYN_SENT 00e0ff
- Color SYN_RECV 00e0a0
- Color FIN_WAIT1 f000f0
- Color FIN_WAIT2 f000a0
- Color TIME_WAIT ffb000
- Color CLOSE 0000f0
- Color CLOSE_WAIT 0000a0
- Color LAST_ACK 000080
- Color LISTEN ff0000
- Color CLOSING 000000
- Color CLOSED 0000f0
-</Type>
-<Type temperature>
- DataSources value
- DSName value Temp
- RRDTitle "Temperature ({type_instance})"
- RRDVerticalLabel "°Celsius"
- RRDFormat "%4.1lf°C"
-</Type>
-<Type users>
- DataSources users
- DSName users Users
- RRDTitle "Users ({type_instance}) on {hostname}"
- RRDVerticalLabel "Users"
- RRDFormat "%.1lf"
- Color users 0000f0
-</Type>
-<Type voltage>
- DataSources value
- DSName value Volts
- RRDTitle "Voltage ({type_instance})"
- RRDVerticalLabel "Volts"
- RRDFormat "%4.1lfV"
- Color value f00000
-</Type>
-# vim: set sw=2 sts=2 et syntax=apache :
--- /dev/null
+package Collectd::Graph::Config;
+
+=head1 NAME
+
+Collectd::Graph::Config - Parse the collection3 config file.
+
+=cut
+
+# Copyright (C) 2008 Florian octo Forster <octo at verplant.org>
+#
+# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+use strict;
+use warnings;
+
+use Carp (qw(cluck confess));
+use Exporter ();
+use Config::General ('ParseConfig');
+use Collectd::Graph::Type ();
+
+@Collectd::Graph::Config::ISA = ('Exporter');
+@Collectd::Graph::Config::EXPORT_OK = (qw(gc_read_config gc_get_config
+ gc_get_scalar));
+
+our $Configuration = undef;
+
+return (1);
+
+=head1 EXPORTED FUNCTIONS
+
+=over 4
+
+=item B<gc_read_config> (I<$file>)
+
+Reads the configuration from the file located at I<$file>. Returns B<true> when
+successfull and B<false> otherwise.
+
+=cut
+
+sub gc_read_config
+{
+ my $file = shift;
+ my %conf;
+
+ if ($Configuration)
+ {
+ return (1);
+ }
+
+ $file ||= "etc/collection.conf";
+
+ %conf = ParseConfig (-ConfigFile => $file,
+ -LowerCaseNames => 1,
+ -UseApacheInclude => 1,
+ -IncludeDirectories => 1,
+ ($Config::General::VERSION >= 2.38) ? (-IncludeAgain => 0) : (),
+ -MergeDuplicateBlocks => 1,
+ -CComments => 0);
+ if (!%conf)
+ {
+ return;
+ }
+
+ $Configuration = \%conf;
+ return (1);
+} # gc_read_config
+
+=item B<gc_get_config> ()
+
+Returns the hash as provided by L<Config::General>. The hash is returned as a
+hash reference. Don't change it!
+
+=cut
+
+sub gc_get_config
+{
+ return ($Configuration);
+} # gc_get_config
+
+=item B<gc_get_config> (I<$key>, [I<$default>])
+
+Returns the scalar value I<$key> from the config file. If the key does not
+exist, I<$default> will be returned. If no default is given, B<undef> will be
+used in this case.
+
+=cut
+
+sub gc_get_scalar
+{
+ my $key = shift;
+ my $default = (@_ != 0) ? shift : undef;
+ my $value;
+
+ if (!$Configuration)
+ {
+ return ($default);
+ }
+
+ $value = $Configuration->{lc ($key)};
+ if (!defined ($value))
+ {
+ return ($default);
+ }
+
+ if (ref ($value) ne '')
+ {
+ cluck ("Value for `$key' should be scalar, but actually is "
+ . ref ($value));
+ return ($default);
+ }
+
+ return ($value);
+} # gc_get_config
+
+=back
+
+=head1 DEPENDS ON
+
+L<Config::General>
+
+=head1 SEE ALSO
+
+L<Collectd::Graph::Type>
+
+=head1 AUTHOR AND LICENSE
+
+Copyright (c) 2008 by Florian Forster
+E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>. Licensed under the terms of the GNU
+General Public License, VersionE<nbsp>2 (GPLv2).
+
+=cut
+
+# vim: set shiftwidth=2 softtabstop=2 tabstop=8 et fdm=marker :
sort_idents_by_type_instance
type_to_module_name
epoch_to_rfc1123
+ flush_files
));
our $DataDir = '/var/lib/collectd/rrd';
return ($string);
}
+sub flush_files
+{
+ my $all_files = shift;
+ my %opts = @_;
+
+ my $begin;
+ my $end;
+ my $addr;
+ my $interval;
+ my $sock;
+ my $now;
+ my $files_to_flush = [];
+ my $status;
+
+ if (!defined $opts{'begin'})
+ {
+ cluck ("begin is not defined");
+ return;
+ }
+ $begin = $opts{'begin'};
+
+ if (!defined $opts{'end'})
+ {
+ cluck ("end is not defined");
+ return;
+ }
+ $end = $opts{'end'};
+
+ if (!$opts{'addr'})
+ {
+ return (1);
+ }
+
+ $interval = $opts{'interval'} || 10;
+
+ if (ref ($all_files) eq 'HASH')
+ {
+ my @tmp = ($all_files);
+ $all_files = \@tmp;
+ }
+
+ $now = time ();
+ # Don't flush anything if the timespan is in the future.
+ if (($end > $now) && ($begin > $now))
+ {
+ return (1);
+ }
+
+ for (@$all_files)
+ {
+ my $file_orig = $_;
+ my $file_name = ident_to_filename ($file_orig);
+ my $file_copy = {};
+ my @statbuf;
+ my $mtime;
+
+ @statbuf = stat ($file_name);
+ if (!@statbuf)
+ {
+ next;
+ }
+ $mtime = $statbuf[9];
+
+ # Skip if file is fresh
+ if (($now - $mtime) <= $interval)
+ {
+ next;
+ }
+ # or $end is before $mtime
+ elsif (($end != 0) && (($end - $mtime) <= 0))
+ {
+ next;
+ }
+
+ $file_copy->{'host'} = $file_orig->{'hostname'};
+ $file_copy->{'plugin'} = $file_orig->{'plugin'};
+ if (exists $file_orig->{'plugin_instance'})
+ {
+ $file_copy->{'plugin_instance'} = $file_orig->{'plugin_instance'}
+ }
+ $file_copy->{'type'} = $file_orig->{'type'};
+ if (exists $file_orig->{'type_instance'})
+ {
+ $file_copy->{'type_instance'} = $file_orig->{'type_instance'}
+ }
+
+ push (@$files_to_flush, $file_copy);
+ } # for (@$all_files)
+
+ if (!@$files_to_flush)
+ {
+ return (1);
+ }
+
+ $sock = Collectd::Unixsock->new ($opts{'addr'});
+ if (!$sock)
+ {
+ return;
+ }
+
+ $status = $sock->flush (plugins => ['rrdtool'], identifier => $files_to_flush);
+ if (!$status)
+ {
+ cluck ("FLUSH failed: " . $sock->{'error'});
+ $sock->destroy ();
+ return;
+ }
+
+ $sock->destroy ();
+ return (1);
+} # flush_files
+
# vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
--- /dev/null
+package Collectd::Graph::Config;
+
+=head1 NAME
+
+Collectd::Graph::Config - Parse the collection3 config file.
+
+=cut
+
+# Copyright (C) 2008 Florian octo Forster <octo at verplant.org>
+#
+# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+use strict;
+use warnings;
+
+use Carp (qw(cluck confess));
+use Exporter ();
+use Config::General ('ParseConfig');
+use Collectd::Graph::Type ();
+
+@Collectd::Graph::Config::ISA = ('Exporter');
+@Collectd::Graph::Config::EXPORT_OK = (qw(gc_read_config gc_get_config
+ gc_get_scalar));
+
+our $Configuration = undef;
+
+return (1);
+
+=head1 EXPORTED FUNCTIONS
+
+=over 4
+
+=item B<gc_read_config> (I<$file>)
+
+Reads the configuration from the file located at I<$file>. Returns B<true> when
+successfull and B<false> otherwise.
+
+=cut
+
+sub gc_read_config
+{
+ my $file = shift;
+ my %conf;
+
+ if ($Configuration)
+ {
+ return (1);
+ }
+
+ $file ||= "etc/collection.conf";
+
+ %conf = ParseConfig (-ConfigFile => $file,
+ -LowerCaseNames => 1,
+ -UseApacheInclude => 1,
+ -IncludeDirectories => 1,
+ ($Config::General::VERSION >= 2.38) ? (-IncludeAgain => 0) : (),
+ -MergeDuplicateBlocks => 1,
+ -CComments => 0);
+ if (!%conf)
+ {
+ return;
+ }
+
+ $Configuration = \%conf;
+ return (1);
+} # gc_read_config
+
+=item B<gc_get_config> ()
+
+Returns the hash as provided by L<Config::General>. The hash is returned as a
+hash reference. Don't change it!
+
+=cut
+
+sub gc_get_config
+{
+ return ($Configuration);
+} # gc_get_config
+
+=item B<gc_get_config> (I<$key>, [I<$default>])
+
+Returns the scalar value I<$key> from the config file. If the key does not
+exist, I<$default> will be returned. If no default is given, B<undef> will be
+used in this case.
+
+=cut
+
+sub gc_get_scalar
+{
+ my $key = shift;
+ my $default = (@_ != 0) ? shift : undef;
+ my $value;
+
+ if (!$Configuration)
+ {
+ return ($default);
+ }
+
+ $value = $Configuration->{lc ($key)};
+ if (!defined ($value))
+ {
+ return ($default);
+ }
+
+ if (ref ($value) ne '')
+ {
+ cluck ("Value for `$key' should be scalar, but actually is "
+ . ref ($value));
+ return ($default);
+ }
+
+ return ($value);
+} # gc_get_config
+
+=back
+
+=head1 DEPENDS ON
+
+L<Config::General>
+
+=head1 SEE ALSO
+
+L<Collectd::Graph::Type>
+
+=head1 AUTHOR AND LICENSE
+
+Copyright (c) 2008 by Florian Forster
+E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>. Licensed under the terms of the GNU
+General Public License, VersionE<nbsp>2 (GPLv2).
+
+=cut
+
+# vim: set shiftwidth=2 softtabstop=2 tabstop=8 et fdm=marker :
my $plugin_instance = $ident->{'plugin_instance'};
my $type = $ident->{'type'};
my $type_instance = $ident->{'type_instance'};
+ my $instance;
+
+ if (defined $type_instance)
+ {
+ $instance = $type_instance;
+ }
+ elsif (defined $plugin_instance)
+ {
+ $instance = $plugin_instance;
+ }
+ else
+ {
+ $instance = 'no instance';
+ }
if (!defined $plugin_instance)
{
$title =~ s#{plugin_instance}#$plugin_instance#g;
$title =~ s#{type}#$type#g;
$title =~ s#{type_instance}#$type_instance#g;
+ $title =~ s#{instance}#$instance#g;
return ($title);
}
my $idents = $group->{$group[$index]};
my $ds_name_len = 0;
+ my $ds = $obj->getDataSources ();
+ if (!$ds)
+ {
+ confess ("obj->getDataSources failed.");
+ }
+ if (@$ds != 1)
+ {
+ confess ("I can only work with RRD files that have "
+ . "exactly one data source!");
+ }
+ my $data_source = $ds->[0];
+
my $rrd_title = $obj->getTitle ($idents->[0]);
my $colors = $obj->{'rrd_colors'} || {};
$names[$i] =~ s/:/\\:/g;
push (@ret,
- "DEF:min${i}=${filename}:value:MIN",
- "DEF:avg${i}=${filename}:value:AVERAGE",
- "DEF:max${i}=${filename}:value:MAX");
+ "DEF:min${i}=${filename}:${data_source}:MIN",
+ "DEF:avg${i}=${filename}:${data_source}:AVERAGE",
+ "DEF:max${i}=${filename}:${data_source}:MAX");
}
for (my $i = @$idents - 1; $i >= 0; $i--)
$obj->{'rrd_format'} = '%.2lf';
$obj->{'colors'} = [qw(00ff00 0000ff ff0000)];
- print STDERR "Hi, this is Collectd::Graph::Type::Load::new\n";
-
return (bless ($obj, $pkg));
} # new
use Carp (qw(cluck confess));
use Exporter ();
use Config::General ('ParseConfig');
+use Collectd::Graph::Config ('gc_get_config');
use Collectd::Graph::Type ();
@Collectd::Graph::TypeLoader::ISA = ('Exporter');
-@Collectd::Graph::TypeLoader::EXPORT_OK = ('tl_read_config', 'tl_load_type');
-
-our $Configuration = undef;
+@Collectd::Graph::TypeLoader::EXPORT_OK = ('tl_load_type');
our @ArrayMembers = (qw(data_sources rrd_opts custom_order));
our @ScalarMembers = (qw(rrd_title rrd_format rrd_vertical scale));
return (1);
-=head1 EXPORTED FUNCTIONS
-
-=over 4
-
-=item B<tl_read_config> (I<$file>)
-
-Reads the configuration from the file located at I<$file>.
-
-=cut
-
-sub tl_read_config
-{
- my $file = shift;
- my %conf;
-
- if ($Configuration)
- {
- return (1);
- }
-
- %conf = ParseConfig (-ConfigFile => $file,
- -LowerCaseNames => 1,
- -UseApacheInclude => 1,
- -IncludeDirectories => 1,
- ($Config::General::VERSION >= 2.38) ? (-IncludeAgain => 0) : (),
- -MergeDuplicateBlocks => 1,
- -CComments => 0);
- if (!%conf)
- {
- return;
- }
-
- $Configuration = \%conf;
- return (1);
-} # tl_read_config
-
sub _create_object
{
my $module = shift;
if ($module)
{
- print STDERR "\$module = $module;\n";
$obj = _create_object ($module);
if (!$obj)
{
$obj->{$member} ||= {};
$obj->{$member}{$ds} = $val;
-
- print STDERR "\$obj->{$member}{$ds} = $val;\n";
} # for (@val_list)
} # }}} for (@DSMappedMembers)
return ($obj);
} # _load_module_generic
+=head1 EXPORTED FUNCTIONS
+
+=over 4
+
=item B<tl_load_type> (I<$type>)
Does whatever is necessary to get an object with which to graph RRD files of
sub tl_load_type
{
my $type = shift;
+ my $conf = gc_get_config ();
- if (defined $Configuration->{'type'}{$type})
+ if (defined ($conf) && defined ($conf->{'type'}{$type}))
{
- return (_load_module_from_config ($Configuration->{'type'}{$type}));
+ return (_load_module_from_config ($conf->{'type'}{$type}));
}
else
{
--- /dev/null
+function nav_init (time_begin, time_end)
+{
+ var all_images;
+ var i;
+
+ all_images = document.getElementsByTagName ("img");
+ for (i = 0; i < all_images.length; i++)
+ {
+ if (all_images[i].className != "graph_image")
+ continue;
+
+ all_images[i].navTimeBegin = new Number (time_begin);
+ all_images[i].navTimeEnd = new Number (time_end);
+
+ all_images[i].navBaseURL = all_images[i].src.replace (/;(begin|end)=[^;]*/g, '');
+
+ if (all_images[i].addEventListener) /* Mozilla */
+ {
+ all_images[i].addEventListener ('dblclick', nav_handle_dblclick,
+ false /* == bubbling */);
+ all_images[i].addEventListener ('DOMMouseScroll', nav_handle_wheel,
+ false /* == bubbling */);
+ }
+ else
+ {
+ all_images[i].ondblclick = nav_handle_dblclick;
+ all_images[i].onmousewheel = nav_handle_wheel;
+ }
+ }
+
+ return (true);
+} /* nav_init */
+
+function nav_image_repaint (img)
+{
+ if (!img || !img.navBaseURL
+ || !img.navTimeBegin || !img.navTimeEnd)
+ return;
+
+ img.src = img.navBaseURL + ";"
+ + "begin=" + img.navTimeBegin.toFixed (0) + ";"
+ + "end=" + img.navTimeEnd.toFixed (0);
+} /* nav_image_repaint */
+
+function nav_time_reset (img_id ,diff)
+{
+ var img;
+
+ img = document.getElementById (img_id);
+ if (!img)
+ return (false);
+
+ img.navTimeEnd = new Number ((new Date ()).getTime () / 1000);
+ img.navTimeBegin = new Number (img.navTimeEnd - diff);
+
+ nav_image_repaint (img);
+
+ return (true);
+}
+
+function nav_time_change_obj (img, factor_begin, factor_end)
+{
+ var diff;
+
+ if (!img || !img.navBaseURL
+ || !img.navTimeBegin || !img.navTimeEnd)
+ return (false);
+
+ diff = img.navTimeEnd - img.navTimeBegin;
+
+ /* Prevent zooming in if diff is less than five minutes */
+ if ((diff <= 300) && (factor_begin > 0.0) && (factor_end < 0.0))
+ return (true);
+
+ img.navTimeBegin += (diff * factor_begin);
+ img.navTimeEnd += (diff * factor_end);
+
+ nav_image_repaint (img);
+
+ return (true);
+} /* nav_time_change */
+
+function nav_time_change (img_id, factor_begin, factor_end)
+{
+ var diff;
+
+ if (img_id == '*')
+ {
+ var all_images;
+ var i;
+
+ all_images = document.getElementsByTagName ("img");
+ for (i = 0; i < all_images.length; i++)
+ {
+ if (all_images[i].className != "graph_image")
+ continue;
+
+ nav_time_change_obj (all_images[i], factor_begin, factor_end);
+ }
+ }
+ else
+ {
+ var img;
+
+ img = document.getElementById (img_id);
+ if (!img)
+ return (false);
+
+ nav_time_change_obj (img, factor_begin, factor_end);
+ }
+
+ return (true);
+} /* nav_time_change */
+
+function nav_move_earlier (img_id)
+{
+ return (nav_time_change (img_id, -0.2, -0.2));
+} /* nav_move_earlier */
+
+function nav_move_later (img_id)
+{
+ return (nav_time_change (img_id, +0.2, +0.2));
+} /* nav_move_later */
+
+function nav_zoom_in (img_id)
+{
+ return (nav_time_change (img_id, +0.2, -0.2));
+} /* nav_zoom_in */
+
+function nav_zoom_out (img_id)
+{
+ return (nav_time_change (img_id, (-1.0 / 3.0), (1.0 / 3.0)));
+} /* nav_zoom_in */
+
+function nav_set_reference (img_id)
+{
+ var img;
+ var all_images;
+ var tmp;
+ var i;
+
+ img = document.getElementById (img_id);
+ if (!img || (img.className != "graph_image")
+ || !img.navTimeBegin || !img.navTimeEnd)
+ return;
+
+ all_images = document.getElementsByTagName ("img");
+ for (i = 0; i < all_images.length; i++)
+ {
+ tmp = all_images[i];
+ if (!tmp || (tmp.className != "graph_image")
+ || !tmp.navTimeBegin || !tmp.navTimeEnd)
+ continue;
+
+ if (tmp.id == img_id)
+ continue;
+
+ tmp.navTimeBegin = img.navTimeBegin;
+ tmp.navTimeEnd = img.navTimeEnd;
+
+ nav_image_repaint (tmp);
+ }
+} /* nav_set_reference */
+
+/*
+ * TODO: calculate the mouse position relative to the image in a cross-browser
+ * manner.
+ */
+function nav_calculate_offset_x (obj)
+{
+ var offset = 0;
+
+ if (!obj)
+ return (offset);
+
+ offset = obj.offsetLeft;
+ if (obj.offsetParent)
+ offset += nav_calculate_offset_x (obj.offsetParent);
+
+ return (offset);
+} /* nav_calculate_offset_x */
+
+function nav_calculate_event_x (e)
+{
+ var pos = 0;
+ var off = 0;
+
+ if (!e || !e.target)
+ return;
+
+ off = nav_calculate_offset_x (e.target);
+
+ if (e.pageX || e.pageY)
+ {
+ pos = e.pageX;
+ }
+ else if (e.clientX || e.clientY)
+ {
+ pos = e.clientX + document.body.scrollLeft
+ + document.documentElement.scrollLeft;
+ }
+
+ return (pos);
+} /* nav_calculate_event_x */
+
+function nav_recenter (e)
+{
+ var x;
+ var y;
+ var img;
+ var diff;
+ var time_old_center;
+ var time_new_center;
+ var width;
+
+ img = e.target;
+ if (!img || (img.className != "graph_image")
+ || !img.navTimeBegin || !img.navTimeEnd)
+ return;
+
+ width = img.width - 97;
+
+ x = e.layerX - 70;
+ if (!x || (x < 0) || (x > width))
+ return;
+
+ y = e.layerY;
+ if (!y || (y < 35) || (y > 135))
+ return;
+
+ diff = img.navTimeEnd - img.navTimeBegin;
+
+ time_old_center = img.navTimeBegin + (diff / 2.0);
+ time_new_center = img.navTimeBegin + (x * diff / width);
+
+ img.navTimeBegin += (time_new_center - time_old_center);
+ img.navTimeEnd += (time_new_center - time_old_center);
+} /* nav_recenter */
+
+function nav_handle_dblclick (e)
+{
+ var img;
+
+ /* M$IE */
+ if (!e)
+ e = window.event;
+
+ img = e.target;
+ if (!img || (img.className != "graph_image")
+ || !img.navTimeBegin || !img.navTimeEnd)
+ return;
+
+ nav_recenter (e);
+ nav_image_repaint (img);
+
+ // e.returnValue = false;
+} /* nav_handle_dblclick */
+
+/* Taken from <http://adomas.org/javascript-mouse-wheel/> */
+function nav_handle_wheel (e)
+{
+ var delta = 0;
+ var img;
+
+ /* M$IE */
+ if (!e)
+ e = window.event;
+
+ img = e.target;
+ if (!img || (img.className != "graph_image")
+ || !img.navTimeBegin || !img.navTimeEnd)
+ return;
+
+ /* Opera and M$IE */
+ if (e.wheelDelta)
+ {
+ delta = e.wheelDelta;
+ if (window.opera)
+ delta = delta * (-1);
+ }
+ else if (e.detail)
+ {
+ delta = e.detail * (-1);
+ }
+
+ if (!delta)
+ return;
+
+ nav_recenter (e);
+ if (delta > 0)
+ nav_zoom_in (img.id);
+ else
+ nav_zoom_out (img.id);
+
+ if (e.preventDefault)
+ e.preventDefault ();
+ e.returnValue = false;
+} /* function nav_handle_wheel */
+
+/* vim: set sw=2 sts=2 et : */
+div.graph
+{
+}
+
+div.graph_canvas div.graph_float
+{
+ float: left;
+ position: relative;
+}
+
+div.graph_float div.controls
+{
+ display: none;
+ position: absolute;
+}
+
+div.graph_float:hover div.controls
+{
+ display: block;
+}
+
+div.graph_float div.controls.zoom
+{
+ right: 5px;
+ bottom: 10px;
+}
+
+div.graph_float div.controls.preset
+{
+ right: 5px;
+ top: 5px;
+}
+
+div.graph_float div.controls div
+{
+ display: block;
+
+ color: gray;
+ background: white;
+
+ text-decoration: none;
+ text-align: center;
+ font-size: small;
+
+ cursor: pointer;
+
+ border: 1px solid gray;
+ width: 1em;
+ height: 1em;
+ padding: 1px;
+ margin: 0px;
+}
+
+div.graph_float div.controls div:hover
+{
+ color: black;
+ border-color: black;
+}
+
+div.graph_float div.controls.preset div
+{
+ margin: 1px 0px 1px 0px;
+}
+
+div.graph_float div.controls.zoom div
+{
+ float: left;
+ margin: 0px 1px 0px 1px;
+}
+
table
{
border-collapse: collapse;
vl.values = values;
vl.values_len = 1;
vl.time = time (NULL);
- strcpy (vl.host, hostname_g);
- strcpy (vl.plugin, "myplugin");
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "myplugin", sizeof (vl.plugin));
/* optionally set vl.plugin_instance and vl.type_instance to reasonable
* values (default: "") */
--- /dev/null
+#!/usr/bin/perl
+#
+# collectd - snmp-probe-host.px
+# Copyright (C) 2008 Florian octo Forster
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; only version 2 of the License is applicable.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author:
+# Florian octo Forster <octo at noris.net>
+#
+
+use strict;
+use warnings;
+use SNMP;
+use Config::General ('ParseConfig');
+use Getopt::Long ('GetOptions');
+use Socket6;
+
+sub get_config
+{
+ my %conf;
+ my $file = shift;
+
+ %conf = ParseConfig (-ConfigFile => $file,
+ -LowerCaseNames => 1,
+ -UseApacheInclude => 1,
+ -IncludeDirectories => 1,
+ ($Config::General::VERSION >= 2.38) ? (-IncludeAgain => 0) : (),
+ -MergeDuplicateBlocks => 1,
+ -CComments => 0);
+ if (!%conf)
+ {
+ return;
+ }
+ return (\%conf);
+} # get_config
+
+sub probe_one
+{
+ my $sess = shift;
+ my $conf = shift;
+ my @oids;
+ my $cmd = 'GET';
+ my $vl;
+
+ if (!$conf->{'table'} || !$conf->{'values'})
+ {
+ warn "No 'table' or 'values' setting";
+ return;
+ }
+
+ @oids = split (/"\s*"/, $conf->{'values'});
+ if ($conf->{'table'} =~ m/^(true|yes|on)$/i)
+ {
+ $cmd = 'GETNEXT';
+ if (defined ($conf->{'instance'}))
+ {
+ push (@oids, $conf->{'instance'});
+ }
+ }
+
+ require Data::Dumper;
+
+ #print "probe_one: \@oids = (" . join (', ', @oids) . ");\n";
+ for (@oids)
+ {
+ my $oid_orig = $_;
+ my $vb;
+ my $status;
+
+ if ($oid_orig =~ m/[^0-9\.]/)
+ {
+ my $tmp = SNMP::translateObj ($oid_orig);
+ if (!defined ($tmp))
+ {
+ warn ("Cannot translate OID $oid_orig");
+ return;
+ }
+ $oid_orig = $tmp;
+ }
+
+ $vb = SNMP::Varbind->new ([$oid_orig]);
+
+ if ($cmd eq 'GET')
+ {
+ $status = $sess->get ($vb);
+ if ($sess->{'ErrorNum'} != 0)
+ {
+ return;
+ }
+ if (!defined ($status))
+ {
+ return;
+ }
+ if ("$status" eq 'NOSUCHOBJECT')
+ {
+ return;
+ }
+ }
+ else
+ {
+ my $oid_copy;
+
+ $status = $sess->getnext ($vb);
+ if ($sess->{'ErrorNum'} != 0)
+ {
+ return;
+ }
+
+ $oid_copy = $vb->[0];
+ if ($oid_copy =~ m/[^0-9\.]/)
+ {
+ my $tmp = SNMP::translateObj ($oid_copy);
+ if (!defined ($tmp))
+ {
+ warn ("Cannot translate OID $oid_copy");
+ return;
+ }
+ $oid_copy = $tmp;
+ }
+
+ #print "$oid_orig > $oid_copy ?\n";
+ if (substr ($oid_copy, 0, length ($oid_orig)) ne $oid_orig)
+ {
+ return;
+ }
+ }
+
+ #print STDOUT Data::Dumper->Dump ([$oid_orig, $status], [qw(oid_orig status)]);
+ } # for (@oids)
+
+ return (1);
+} # probe_one
+
+sub probe_all
+{
+ my $host = shift;
+ my $community = shift;
+ my $data = shift;
+ my $version = 2;
+ my @valid_data = ();
+ my $begin;
+ my $address;
+
+ {
+ my @status;
+
+ @status = getaddrinfo ($host, 'snmp');
+ while (@status >= 5)
+ {
+ my $family = shift (@status);
+ my $socktype = shift (@status);
+ my $proto = shift (@status);
+ my $saddr = shift (@status);
+ my $canonname = shift (@status);
+ my $host;
+ my $port;
+
+ ($host, $port) = getnameinfo ($saddr, NI_NUMERICHOST);
+ if (defined ($port))
+ {
+ $address = $host;
+ }
+ else
+ {
+ warn ("getnameinfo failed: $host");
+ }
+ }
+ }
+ if (!$address)
+ {
+ return;
+ }
+
+ while ($version > 0)
+ {
+ my $sess;
+
+ $sess = new SNMP::Session (DestHost => $host,
+ Community => $community,
+ Version => $version,
+ Timeout => 1000000,
+ UseNumeric => 1);
+ if (!$sess)
+ {
+ $version--;
+ next;
+ }
+
+ $begin = time ();
+
+ for (keys %$data)
+ {
+ my $name = $_;
+ if (probe_one ($sess, $data->{$name}))
+ {
+ push (@valid_data, $name);
+ }
+
+ if ((@valid_data == 0) && ((time () - $begin) > 10))
+ {
+ # break for loop
+ last;
+ }
+ }
+
+ if (@valid_data)
+ {
+ # break while loop
+ last;
+ }
+
+ $version--;
+ } # while ($version > 0)
+
+ print <<EOF;
+ <Host "$host">
+ Address "$address"
+ Version $version
+ Community "$community"
+EOF
+ for (sort (@valid_data))
+ {
+ print " Collect \"$_\"\n";
+ }
+ if (!@valid_data)
+ {
+ print <<EOF;
+# WARNING: Autoconfiguration failed.
+# TODO: Add one or more `Collect' statements here:
+# Collect "foo"
+EOF
+ }
+ print <<EOF;
+ Interval 60
+ </Host>
+EOF
+} # probe_all
+
+sub exit_usage
+{
+ print <<USAGE;
+Usage: snmp-probe-host.px --host <host> [options]
+
+Options are:
+ -H | --host Hostname of the device to probe.
+ -C | --config Path to config file holding the SNMP data blocks.
+ -c | --community SNMP community to use. Default: `public'.
+ -h | --help Print this information and exit.
+
+USAGE
+ exit (1);
+}
+
+=head1 NAME
+
+snmp-probe-host.px - Find out what information an SNMP device provides.
+
+=head1 SYNOPSIS
+
+ ./snmp-probe-host.px --host switch01.mycompany.com --community ei2Acoum
+
+=head1 DESCRIPTION
+
+The C<snmp-probe-host.px> script can be used to automatically generate SNMP
+configuration snippets for collectd's snmp plugin (see L<collectd-snmp(5)>).
+
+This script parses the collectd configuration and detecs all "data" blocks that
+are defined for the SNMP plugin. It then queries the device specified on the
+command line for all OIDs and registeres which OIDs could be answered correctly
+and which resulted in an error. With that information the script figures out
+which "data" blocks can be used with this hosts and prints an appropriate
+"host" block to standard output.
+
+The script first tries to contact the device via SNMPv2. If after ten seconds
+no working "data" block has been found, it will try to downgrade to SNMPv1.
+This is a bit a hack, but works for now.
+
+=cut
+
+my $host;
+my $file = '/etc/collectd/collectd.conf';
+my $community = 'public';
+my $conf;
+my $working_data;
+
+=head1 OPTIONS
+
+The following command line options are accepted:
+
+=over 4
+
+=item B<--host> I<hostname>
+
+Hostname of the device. This B<should> be a fully qualified domain name (FQDN),
+but anything the system can resolve to an IP address will word. B<Required
+argument>.
+
+=item B<--config> I<config file>
+
+Sets the name of the collectd config file which defined the SNMP "data" blocks.
+Due to limitations of the config parser used in this script
+(C<Config::General>), C<Include> statements cannot be parsed correctly.
+Defaults to F</etc/collectd/collectd.conf>.
+
+=item B<--community> I<community>
+
+SNMP community to use. Should be pretty straight forward.
+
+=back
+
+=cut
+
+GetOptions ('H|host|hostname=s' => \$host,
+ 'C|conf|config=s' => \$file,
+ 'c|community=s' => \$community,
+ 'h|help' => \&exit_usage) or die;
+
+if (!$host)
+{
+ print STDERR "No hostname given. Please use `--host'.\n";
+ exit (1);
+}
+
+$conf = get_config ($file) or die ("Cannot read config");
+
+if (!defined ($conf->{'plugin'})
+ || !defined ($conf->{'plugin'}{'snmp'})
+ || !defined ($conf->{'plugin'}{'snmp'}{'data'}))
+{
+ print STDERR "Error: No <plugin>, <snmp>, or <data> block found.\n";
+ exit (1);
+}
+
+probe_all ($host, $community, $conf->{'plugin'}{'snmp'}{'data'});
+
+exit (0);
+
+=head1 BUGS
+
+=over 4
+
+=item
+
+C<Include> statements in the config file are not handled correctly.
+
+=item
+
+SNMPv2 / SNMPv1 detection is a hack.
+
+=back
+
+=head1 AUTHOR
+
+Copyright (c) 2008 by Florian octo Forster
+E<lt>octoE<nbsp>atE<nbsp>noris.netE<gt>. Licensed under the terms of the GPLv2.
+Written for the norisE<nbsp>networkE<nbsp>AG L<http://noris.net/>.
+
+=cut
+
+# vim: set sw=2 sts=2 ts=8 et :
AM_CPPFLAGS += -DPIDFILE='"${localstatedir}/run/${PACKAGE_NAME}.pid"'
endif
AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"'
+AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"'
sbin_PROGRAMS = collectd collectdmon
bin_PROGRAMS = collectd-nagios
if BUILD_PLUGIN_EXEC
pkglib_LTLIBRARIES += exec.la
exec_la_SOURCES = exec.c \
+ utils_parse_option.h utils_parse_option.c \
utils_cmd_putnotif.c utils_cmd_putnotif.h \
utils_cmd_putval.c utils_cmd_putval.h
exec_la_LDFLAGS = -module -avoid-version
collectd_DEPENDENCIES += exec.la
endif
+if BUILD_PLUGIN_FILECOUNT
+pkglib_LTLIBRARIES += filecount.la
+filecount_la_SOURCES = filecount.c
+filecount_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" filecount.la
+collectd_DEPENDENCIES += filecount.la
+endif
+
if BUILD_PLUGIN_HDDTEMP
pkglib_LTLIBRARIES += hddtemp.la
hddtemp_la_SOURCES = hddtemp.c
if BUILD_PLUGIN_IPVS
pkglib_LTLIBRARIES += ipvs.la
ipvs_la_SOURCES = ipvs.c
+ipvs_la_CFLAGS = $(KERNEL_CFLAGS)
ipvs_la_LDFLAGS = -module -avoid-version
collectd_LDADD += "-dlopen" ipvs.la
collectd_DEPENDENCIES += ipvs.la
pkglib_LTLIBRARIES += processes.la
processes_la_SOURCES = processes.c
processes_la_LDFLAGS = -module -avoid-version
+processes_la_LIBADD =
collectd_LDADD += "-dlopen" processes.la
collectd_DEPENDENCIES += processes.la
+if BUILD_WITH_LIBKVM_GETPROCS
+processes_la_LIBADD += -lkvm
endif
-
-if BUILD_PLUGIN_QMAIL
-pkglib_LTLIBRARIES += qmail.la
-qmail_la_SOURCES = qmail.c
-qmail_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" qmail.la
-collectd_DEPENDENCIES += qmail.la
endif
if BUILD_PLUGIN_RRDCACHED
if BUILD_WITH_LIBDEVINFO
swap_la_LIBADD += -ldevinfo
endif
-if BUILD_WITH_LIBKVM
+if BUILD_WITH_LIBKVM_GETSWAPINFO
swap_la_LIBADD += -lkvm
endif
if BUILD_WITH_LIBSTATGRAB
pkglib_LTLIBRARIES += tcpconns.la
tcpconns_la_SOURCES = tcpconns.c
tcpconns_la_LDFLAGS = -module -avoid-version
+tcpconns_la_LIBADD =
collectd_LDADD += "-dlopen" tcpconns.la
collectd_DEPENDENCIES += tcpconns.la
+if BUILD_WITH_LIBKVM_NLIST
+tcpconns_la_LIBADD += -lkvm
+endif
endif
if BUILD_PLUGIN_TEAMSPEAK2
if BUILD_PLUGIN_UNIXSOCK
pkglib_LTLIBRARIES += unixsock.la
unixsock_la_SOURCES = unixsock.c \
+ utils_parse_option.h utils_parse_option.c \
utils_cmd_flush.h utils_cmd_flush.c \
utils_cmd_getval.h utils_cmd_getval.c \
utils_cmd_listval.h utils_cmd_listval.c \
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 collectdmon.pod types.db.pod
+ collectd.conf.pod collectd.pod collectdmon.pod types.db.pod \
+ postgresql_default.conf
.pod.1:
- pod2man --release=$(VERSION) --center=$(PACKAGE) $< >.pod2man.tmp 2>/dev/null && mv -f .pod2man.tmp $@ || true
+ pod2man --release=$(VERSION) --center=$(PACKAGE) $< \
+ >.pod2man.tmp 2>/dev/null && mv -f .pod2man.tmp $@ || true
+ if grep '\<POD ERRORS\>' $@ >/dev/null 2>&1; \
+ then \
+ echo "$@ has some POD errors!"; false; \
+ fi
.pod.5:
- pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< >.pod2man.tmp 2>/dev/null && mv -f .pod2man.tmp $@ || true
+ pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< \
+ >.pod2man.tmp 2>/dev/null && mv -f .pod2man.tmp $@ || true
+ if grep '\<POD ERRORS\>' $@ >/dev/null 2>&1; \
+ then \
+ echo "$@ has some POD errors!"; false; \
+ fi
install-exec-hook:
$(mkinstalldirs) $(DESTDIR)$(sysconfdir)
$(INSTALL) -m 0640 collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \
fi; \
cp -f $(srcdir)/types.db $(DESTDIR)$(pkglibdir)/;
+ $(INSTALL) -D -m 0644 $(srcdir)/postgresql_default.conf \
+ $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
sstrncpy (vl.host, hostname_g, sizeof (vl.host));
sstrncpy (vl.plugin, "apple_sensors", sizeof (vl.plugin));
sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
- sstrncpy (vl.type, type, sizeof (vl.type))
+ sstrncpy (vl.type, type, sizeof (vl.type));
sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
plugin_dispatch_values (&vl);
#endif /* HAVE_IOKIT_IOKITLIB_H */
#if KERNEL_LINUX
-static int battery_read_acpi (const char *name)
+static int battery_read_acpi (const char *dir, const char *name,
+ void *user_data)
{
double current = INVALID_VALUE;
double voltage = INVALID_VALUE;
battery_submit ("0", "voltage", voltage);
}
- walk_directory (battery_acpi_dir, battery_read_acpi);
+ walk_directory (battery_acpi_dir, battery_read_acpi,
+ /* user_data = */ NULL);
#endif /* KERNEL_LINUX */
The I<OptionList> is an optional list of I<Options>, where each option if a
key-value-pair. A list of currently understood options can be found below, all
-other options will be ignored.
+other options will be ignored. Values that contain spaces must be quoted with
+double quotes.
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
+an integer if the data-source is a counter, or a double if the data-source is
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
epoch (i.E<nbsp>e. standard UNIX time).
=item B<message=>I<Message> (B<REQUIRED>)
Sets the message of the notification. This is the message that will be made
-accessible to the user, so it should contain some useful information. This
-option must be the last option because the rest of the line will be its value,
-even if there are spaces and equal-signs following it! This option is
-mandatory.
+accessible to the user, so it should contain some useful information. As with
+all options: If the message includes spaces, it must be quoted with double
+quotes. This option is mandatory.
=item B<severity=failure>|B<warning>|B<okay> (B<REQUIRED>)
or B<type_instance> without B<type> doesn't make much sense and should be
avoided.
-Please note that this is the same format as used in the B<unixsock plugin>, see
-L<collectd-unixsock(5)>.
+=back
=back
+Please note that this is the same format as used in the B<unixsock plugin>, see
+L<collectd-unixsock(5)>.
+
When collectd exits it sends a B<SIGTERM> to all still running
child-processes upon which they have to quit.
BaseName "Collectd::Plugin"
EnableDebugger ""
LoadPlugin "FooBar"
+
+ <Plugin FooBar>
+ Foo "Bar"
+ </Plugin>
</Plugin>
=head1 DESCRIPTION
Prepends I<Name>B<::> to all plugin names loaded after this option. This is
provided for convenience to keep plugin names short.
+=item E<lt>B<Plugin> I<Name>E<gt> block
+
+This block may be used to pass on configuration settings to a Perl plugin. The
+configuration is converted into a config-item data type which is passed to the
+registered configuration callback. See below for details about the config-item
+data type and how to register callbacks.
+
+The I<name> identifies the callback. It is used literally and independent of
+the B<BaseName> setting.
+
=item B<EnableDebugger> I<Package>[=I<option>,...]
Run collectd under the control of the Perl source debugger. If I<Package> is
=over 4
+=item configuration functions
+
+This type of functions is called during configuration if an appropriate
+B<Plugin> block has been encountered. It is called once for each B<Plugin>
+block which matches the name of the callback as provided with the
+B<plugin_register> method - see below.
+
=item init functions
This type of functions is called once after loading the module and before any
=over 4
+=item Config-Item
+
+A config-item is one structure which keeps the informations provided in the
+configuration file. The array of children keeps one entry for each
+configuration option. Each such entry is another config-item structure, which
+may nest further if nested blocks are used.
+
+ {
+ key => key,
+ values => [ val1, val2, ... ],
+ children => [ { ... }, { ... }, ... ]
+ }
+
=item Data-Set
A data-set is a list of one or more data-sources. Each data-source defines a
=over 4
+=item TYPE_CONFIG
+
=item TYPE_INIT
=item TYPE_READ
=over 4
+=item TYPE_CONFIG
+
+The only argument passed is I<config-item>. See above for the layout of this
+data type.
+
=item TYPE_INIT
=item TYPE_READ
=item TYPE_FLUSH
-The only argument passed is I<timeout> which indicates that only data older
-than I<timeout> seconds is to be flushed.
+The arguments passed are I<timeout> and I<identifier>. I<timeout> indicates
+that only data older than I<timeout> seconds is to be flushed. I<identifier>
+specifies which values are to be flushed.
=item TYPE_LOG
for backwards compatibility but has been deprecated and will be removed in
some future version of collectd.
-=item B<plugin_flush> ([B<timeout> => I<timeout>,] [B<plugins> => I<...>])
+=item B<plugin_flush> ([B<timeout> => I<timeout>][, B<plugins> => I<...>][,
+B<identifiers> => I<...>])
-Flush one or more plugins. I<timeout> is passed on to the registered
-flush-callbacks. If omitted, C<-1> is used. If the I<plugins> argument has
-been specified, only named plugins will be flushed. The argument's value may
-either be a string or a reference to an array of strings.
+Flush one or more plugins. I<timeout> and the specified I<identifiers> are
+passed on to the registered flush-callbacks. If omitted, the timeout defaults
+to C<-1>. The identifier defaults to the undefined value. If the I<plugins>
+argument has been specified, only named plugins will be flushed. The value of
+the B<plugins> and B<identifiers> arguments may either be a string or a
+reference to an array of strings.
=item B<plugin_flush_one> (I<timeout>, I<plugin>)
This is identical to using "plugin_flush (timeout =E<gt> I<timeout>, plugins
=E<gt> I<plugin>".
+B<Note>: Starting with version 4.5 of collectd, B<plugin_flush_one> has been
+deprecated and will be removed in some future version of collectd. Use
+B<plugin_flush> instead.
+
=item B<plugin_flush_all> (I<timeout>)
This is identical to using "plugin_flush (timeout =E<gt> I<timeout>)".
+B<Note>: Starting with version 4.5 of collectd, B<plugin_flush_all> has been
+deprecated and will be removed in some future version of collectd. Use
+B<plugin_flush> instead.
+
=item B<plugin_dispatch_notification> (I<notification>)
Submits a I<notification> to the daemon which will then pass it to all
=over 4
+=item B<TYPE_CONFIG>
+
=item B<TYPE_INIT>
=item B<TYPE_READ>
=item B<TYPE_LOG>
+=item B<TYPE_DATASET>
+
=back
=item B<:ds_types>
down. You should not rely on END blocks anyway - use B<shutdown functions>
instead.
+=item
+
+The perl plugin exports the internal API of collectd which is considered
+unstable and subject to change at any time. We try hard to not break backwards
+compatibility in the Perl API during the life cycle of one major release.
+However, this cannot be guaranteed at all times. Watch out for warnings
+dispatched by the perl plugin after upgrades.
+
=back
=head1 KNOWN BUGS
The I<OptionList> is an optional list of I<Options>, where each option is a
key-value-pair. A list of currently understood options can be found below, all
-other options will be ignored.
+other options will be ignored. Values that contain spaces must be quoted with
+double quotes.
I<Valuelist> is a colon-separated list of the time and the values, each either
an integer if the data-source is a counter, or a double if the data-source is
=item B<message=>I<Message> (B<REQUIRED>)
Sets the message of the notification. This is the message that will be made
-accessible to the user, so it should contain some useful information. This
-option must be the last option because the rest of the line will be its value,
-even if there are spaces and equal-signs following it! This option is
-mandatory.
+accessible to the user, so it should contain some useful information. As with
+all options: If the message includes spaces, it must be quoted with double
+quotes. This option is mandatory.
=item B<severity=failure>|B<warning>|B<okay> (B<REQUIRED>)
I<Hostname>/I<Plugin>/I<Type>
Where I<Plugin> and I<Type> are both either of type "I<Name>" or
-"I<Name>-I<Instance>". This sounds more complicated than it is, so here are
+"I<Name>-I<Instance>". If the identifier includes spaces, it must be quoted
+using double quotes. This sounds more complicated than it is, so here are
some examples:
myhost/cpu-0/cpu-user
myhost/load/load
myhost/memory/memory-used
myhost/disk-sda/disk_octets
+ "myups/snmp/temperature-Outlet 1"
=head1 ABSTRACTION LAYER
static void *do_flush (void *arg)
{
INFO ("Flushing all data.");
- plugin_flush_all (-1);
+ plugin_flush (NULL, -1, NULL);
INFO ("Finished flushing all data.");
pthread_exit (NULL);
return NULL;
#Interval 10
#ReadThreads 5
+@BUILD_PLUGIN_LOGFILE_TRUE@LoadPlugin logfile
+@BUILD_PLUGIN_SYSLOG_TRUE@LoadPlugin syslog
+
+#<Plugin logfile>
+# LogLevel info
+# File STDOUT
+# Timestamp true
+#</Plugin>
+
+#<Plugin syslog>
+# LogLevel info
+#</Plugin>
+
@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors
@BUILD_PLUGIN_EMAIL_TRUE@LoadPlugin email
@BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy
@BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec
+@BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount
@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
@BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables
@BUILD_PLUGIN_IRQ_TRUE@LoadPlugin irq
@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt
@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_SERIAL_TRUE@LoadPlugin serial
@BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp
@BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap
-@BUILD_PLUGIN_SYSLOG_TRUE@LoadPlugin syslog
@BUILD_PLUGIN_TAIL_TRUE@LoadPlugin tail
@BUILD_PLUGIN_TAPE_TRUE@LoadPlugin tape
@BUILD_PLUGIN_TCPCONNS_TRUE@LoadPlugin tcpconns
# NotificationExec "user:group" "/path/to/exec"
#</Plugin>
+#<Plugin filecount>
+# <Directory "/path/to/dir">
+# Instance "foodir"
+# Name "*.conf"
+# MTime "-5m"
+# Size "+10k"
+# </Directory>
+#</Plugin>
+
@BUILD_PLUGIN_HDDTEMP_TRUE@<Plugin hddtemp>
# Host "127.0.0.1"
# Port "7634"
# HostnameFormat name
#</Plugin>
-#<Plugin logfile>
-# LogLevel info
-# File STDOUT
-# Timestamp true
-#</Plugin>
-
#<Plugin mbmon>
# Host "127.0.0.1"
# Port "411"
#</Plugin>
#<Plugin postgresql>
+# <Query magic>
+# Query "SELECT magic, spells FROM wizard WHERE host = $1;"
+# Param hostname
+# Column gauge magic
+# Column counter spells
+# </Query>
+#
# <Database foo>
# Host "hostname"
# Port 5432
#
# SSLMode "prefer"
# KRBSrvName "kerberos_service_name"
+#
+# Query magic
# </Database>
#
# <Database bar>
# </Host>
#</Plugin>
-#<Plugin syslog>
-# LogLevel info
-#</Plugin>
-
#<Plugin "tail">
# <File "/var/log/exim4/mainlog">
# Instance "exim"
=back
+=head2 Plugin C<filecount>
+
+The C<filecount> plugin counts the number of files in a certain directory (and
+its subdirectories) and their combined size. The configuration is very straight
+forward:
+
+ <Plugin "filecount">
+ <Directory "/var/qmail/queue/mess">
+ Instance "qmail-message"
+ </Directory>
+ <Directory "/var/qmail/queue/todo">
+ Instance "qmail-todo"
+ </Directory>
+ <Directory "/var/lib/php5">
+ Instance "php5-sessions"
+ Name "sess_*"
+ </Directory>
+ </Plugin>
+
+The example above counts the number of files in QMail's queue directories and
+the number of PHP5 sessions. Jfiy: The "todo" queue holds the messages that
+QMail has not yet looked at, the "message" queue holds the messages that were
+classified into "local" and "remote".
+
+As you can see, the configuration consists of one or more C<Directory> blocks,
+each of which specifies a directory in which to count the files. Within those
+blocks, the following options are recognized:
+
+=over 4
+
+=item B<Instance> I<Instance>
+
+Sets the plugin instance to I<Instance>. That instance name must be unique, but
+it's your responsibility, the plugin doesn't check for that. If not given, the
+instance is set to the directory name with all slashes replaced by underscores
+and all leading underscores removed.
+
+=item B<Name> I<Pattern>
+
+Only count files that match I<Pattern>, where I<Pattern> is a shell-like
+wildcard as understood by L<fnmatch(3)>. Only the B<filename> is checked
+against the pattern, not the entire path. In case this makes it easier for you:
+This option has been named after the B<-name> parameter to L<find(1)>.
+
+=item B<MTime> I<Age>
+
+Count only files of a specific age: If I<Age> is greater than zero, only files
+that haven't been touched in the last I<Age> seconds are counted. If I<Age> is
+a negative number, this is inversed. For example, if B<-60> is specified, only
+files that have been modified in the last minute will be counted.
+
+The number can also be followed by a "multiplier" to easily specify a larger
+timespan. When given in this notation, the argument must in quoted, i.E<nbsp>e.
+must be passed as string. So the B<-60> could also be written as B<"-1m"> (one
+minute). Valid multipliers are C<s> (second), C<m> (minute), C<h> (hour), C<d>
+(day), C<w> (week), and C<y> (year). There is no "month" multiplier. You can
+also specify fractional numbers, e.E<nbsp>g. B<"0.5d"> is identical to
+B<"12h">.
+
+=item B<Size> I<Size>
+
+Count only files of a specific size. When I<Size> is a positive number, only
+files that are at least this big are counted. If I<Size> is a negative number,
+this is inversed, i.E<nbsp>e. only files smaller than the absolute value of
+I<Size> are counted.
+
+As with the B<MTime> option, a "multiplier" may be added. For a detailed
+description see above. Valid multipliers here are C<b> (byte), C<k> (kilobyte),
+C<m> (megabyte), C<g> (gigabyte), C<t> (terabyte), and C<p> (petabyte). Please
+note that there are 1000 bytes in a kilobyte, not 1024.
+
+=back
+
=head2 Plugin C<hddtemp>
To get values from B<hddtemp> collectd connects to B<localhost> (127.0.0.1),
=back
+=head2 Plugin C<ipmi>
+
+=over 4
+
+=item B<Sensor> I<Sensor>
+
+Selects sensors to collect or to ignore, depending on B<IgnoreSelected>.
+
+=item B<IgnoreSelected> I<true>|I<false>
+
+If no configuration if given, the B<ipmi> plugin will collect data from all
+sensors found of type "temperature", "voltage", "current" and "fanspeed".
+This option enables you to do that: By setting B<IgnoreSelected> to I<true>
+the effect of B<Sensor> is inverted: All selected sensors are ignored and
+all other sensors are collected.
+
+=item B<NotifySensorAdd> I<true>|I<false>
+
+If a sensor appears after initialization time of a minute a notification
+is sent.
+
+=item B<NotifySensorRemove> I<true>|I<false>
+
+If a sensor disappears a notification is sent.
+
+=item B<NotifySensorNotPresent> I<true>|I<false>
+
+If you have for example dual power supply and one of them is (un)plugged then
+a notification is sent.
+
+=back
+
=head2 Plugin C<iptables>
=over 4
=head2 Plugin C<onewire>
+B<EXPERIMENTAL!> See notes below.
+
The C<onewire> plugin uses the B<owcapi> library from the B<owfs> project
L<http://owfs.org/> to read sensors connected via the onewire bus.
+Currently only temperature sensors (sensors with the family code C<10>,
+e.E<nbsp>g. DS1820, DS18S20, DS1920) can be read. If you have other sensors you
+would like to have included, please send a sort request to the mailing list.
+
+Hubs (the DS2409 chips) are working, but read the note, why this plugin is
+experimental, below.
+
=over 4
=item B<Device> I<Device>
Device "-s localhost:4304"
</Plugin>
+This directive is B<required> and does not have a default value.
+
=item B<Sensor> I<Sensor>
Selects sensors to collect or to ignore, depending on B<IgnoreSelected>, see
=back
+B<EXPERIMENTAL!> The C<onewire> plugin is experimental, because it doesn't yet
+work with big setups. It works with one sensor being attached to one
+controller, but as soon as you throw in a couple more senors and maybe a hub
+or two, reading all values will take more than ten seconds (the default
+interval). We will probably add some separate thread for reading the sensors
+and some cache or something like that, but it's not done yet. We will try to
+maintain backwards compatibility in the future, but we can't probmise. So in
+short: If it works for you: Great! But kaap in mind that the config I<might>
+change, though this is unlikely. Oh, and if you want to help improving this
+plugin, just send a short notice to the mailing list. ThanksE<nbsp>:)
+
=head2 Plugin C<perl>
This plugin embeds a Perl-interpreter into collectd and provides an interface
The C<postgresql> plugin queries statistics from PostgreSQL databases. It
keeps a persistent connection to all configured databases and tries to
-reconnect if the connection has been interrupted. The statistics are collected
-from PostgreSQL's B<statistics collector> which thus has to be enabled for
-this plugin to work correctly. This should usually be the case by default.
-See the section "The Statistics Collector" of the B<PostgreSQL Documentation>
-for details.
+reconnect if the connection has been interrupted. A database is configured by
+specifying a B<Database> block as described below. The default statistics are
+collected from PostgreSQL's B<statistics collector> which thus has to be
+enabled for this plugin to work correctly. This should usually be the case by
+default. See the section "The Statistics Collector" of the B<PostgreSQL
+Documentation> for details.
+
+By specifying custom database queries using a B<Query> block as described
+below, you may collect any data that is available from some PostgreSQL
+database. This way, you are able to access statistics of external daemons
+which are available in a PostgreSQL database or use future or special
+statistics provided by PostgreSQL without the need to upgrade your collectd
+installation.
The B<PostgreSQL Documentation> manual can be found at
L<http://www.postgresql.org/docs/manuals/>.
<Plugin postgresql>
+ <Query magic>
+ Query "SELECT magic, spells FROM wizard WHERE host = $1;"
+ Param hostname
+ Column gauge magic
+ Column counter spells
+ </Query>
+
<Database foo>
Host "hostname"
Port "5432"
Password "secret"
SSLMode "prefer"
KRBSrvName "kerberos_service_name"
+ Query magic
</Database>
<Database bar>
Service "service_name"
</Database>
</Plugin>
+The B<Query> block defines one database query which may later be used by a
+database definition. It accepts a single mandatory argument which specifies
+the name of the query. The names of all queries have to be unique. The
+following configuration options are available to define the query:
+
+=over 4
+
+=item B<Query> I<sql query>
+
+Specify the I<sql query> which the plugin should execute. The string may
+contain the tokens B<$1>, B<$2>, etc. which are used to reference the first,
+second, etc. parameter. The value of the parameters is specified by the
+B<Param> configuration option - see below for details. To include a literal
+B<$> character followed by a number, surround it with single quotes (B<'>).
+
+Any SQL command which may return data (such as C<SELECT> or C<SHOW>) is
+allowed. Note, however, that only a single command may be used. Semicolons are
+allowed as long as a single non-empty command has been specified only.
+
+=item B<Param> I<hostname>|I<database>|I<username>
+
+Specify the parameters which should be passed to the SQL query. The parameters
+are referred to in the SQL query as B<$1>, B<$2>, etc. in the same order as
+they appear in the configuration file. The value of the parameter is
+determined depending on the value of the B<Param> option as follows:
+
+=over 4
+
+=item I<hostname>
+
+The configured hostname of the database connection. If a UNIX domain socket is
+used, the parameter expands to "localhost".
+
+=item I<database>
+
+The name of the database of the current connection.
+
+=item I<username>
+
+The username used to connect to the database.
+
+=back
+
+Please note that parameters are only supported by PostgreSQL's protocol
+version 3 and above which was introduced in version 7.4 of PostgreSQL.
+
+=item B<Column> I<type> [I<type instance>]
+
+Specify the I<type> and optional I<type instance> used to dispatch the value
+of each result column. Detailed information about types and their
+configuration can be found in L<types.db(5)>. The number and order of the
+B<Column> options has to match the columns of the query result.
+
+=item B<MinPGVersion> I<version>
+
+=item B<MaxPGVersion> I<version>
+
+Specify the minimum or maximum version of PostgreSQL that this query should be
+used with. Some statistics might only be available with certain versions of
+PostgreSQL. This allows you to specify multiple queries with the same name but
+which apply to different versions, thus allowing you to use the same
+configuration in a heterogeneous environment.
+
+The I<version> has to be specified as the concatenation of the major, minor
+and patch-level versions, each represented as two-decimal-digit numbers. For
+example, version 8.2.3 will become 80203.
+
+=back
+
+The following predefined queries are available (the definitions can be found
+in the F<postgresql_default.conf> file which, by default, is available at
+C<I<prefix>/share/collectd/>):
+
=over 4
-=item B<Database> block
+=item B<backends>
+
+This query collects the number of backends, i.E<nbsp>e. the number of
+connected clients.
+
+=item B<transactions>
+
+This query collects the numbers of committed and rolled-back transactions of
+the user tables.
+
+=item B<queries>
+
+This query collects the numbers of various table modifications (i.E<nbsp>e.
+insertions, updates, deletions) of the user tables.
+
+=item B<query_plans>
+
+This query collects the numbers of various table scans and returned tuples of
+the user tables.
+
+=item B<table_states>
+
+This query collects the numbers of live and dead rows in the user tables.
+
+=item B<disk_io>
+
+This query collects disk block access counts for user tables.
+
+=item B<disk_usage>
+
+This query collects the on-disk size of the database in bytes.
+
+=back
The B<Database> block defines one PostgreSQL database for which to collect
statistics. It accepts a single mandatory argument which specifies the
the user collectd is run as and special environment variables. See the manpage
for details.
+=over 4
+
=item B<Host> I<hostname>
Specify the hostname or IP of the PostgreSQL server to connect to. If the
connection parameters. See the section "The Connection Service File" in the
B<PostgreSQL Documentation> for details.
+=item B<Query> I<query>
+
+Specify a I<query> which should be executed for the database connection. This
+may be any of the predefined or user-defined queries. If no such option is
+given, it defaults to "backends", "transactions", "queries", "query_plans",
+"table_states", "disk_io" and "disk_usage". Else, the specified queries are
+used only.
+
=back
=head2 Plugin C<powerdns>
=item B<CacheFlush> I<Seconds>
-When the C<rrdtool plugin> uses a cache (by setting B<CacheTimeout>, see below)
+When the C<rrdtool> plugin uses a cache (by setting B<CacheTimeout>, see below)
it writes all values for a certain RRD-file if the oldest value is older than
(or equal to) the number of seconds specified. If some RRD-file is not updated
anymore for some reason (the computer was shut down, the network is broken,
The trade off is that the graphs kind of "drag behind" and that more memory is
used.
+=item B<WritesPerSecond> B<Updates>
+
+When collecting many statistics with collectd and the C<rrdtool> plugin, you
+will run serious performance problems. The B<CacheFlush> setting and the
+internal update queue assert that collectd continues to work just fine even
+under heavy load, but the system may become very unresponsive and slow. This is
+a problem especially if create graphs from the RRD files on the same machine,
+for example using the C<graph.cgi> script included in the
+C<contrib/collection3/> directory.
+
+This setting is designed for very large setups. Setting this option to a value
+between 25 and 80 updates per second, depending on your hardware, will leave
+the server responsive enough to draw graphs even while all the cached values
+are written to disk. Flushed values, i.E<nbsp>e. values that are forced to disk
+by the B<FLUSH> command, are B<not> effected by this limit. They are still
+written as fast as possible, so that web frontends have up to date data when
+generating graphs.
+
+For example: If you have 100,000 RRD files and set B<WritesPerSecond> to 30
+updates per second, writing all values to disk will take approximately
+56E<nbsp>minutes. Together with the flushing ability that's integrated into
+"collection3" you'll end up with a responsive and fast system, up to date
+graphs and basically a "backup" of your values every hour.
+
=back
=head2 Plugin C<sensors>
<Match>
Regex "\\<R=local_user\\>"
DSType "CounterInc"
- Type "email_count"
+ Type "counter"
Instance "local_user"
</Match>
</File>
use quotes around it! If no such statement is given only global information
will be collected.
+=back
+
=head2 Plugin C<tcpconns>
The C<tcpconns plugin> counts the number of currently established TCP
=over 4
-=back
-
=item B<ListeningPorts> I<true>|I<false>
If this option is set to I<true>, statistics for all local ports for which a
#define PLUGINDIR PREFIX "/lib/" PACKAGE_NAME
#endif
+#ifndef PKGDATADIR
+#define PKGDATADIR PREFIX "/share/" PACKAGE_NAME
+#endif
+
#ifndef COLLECTD_GRP_NAME
# define COLLECTD_GRP_NAME "collectd"
#endif
to the RRD files. This is the same as using the C<FLUSH -1> command of the
C<unixsock plugin>.
+=back
+
=head1 SEE ALSO
L<collectd.conf(5)>,
return (0);
} /* int notification_init */
-int walk_directory (const char *dir, dirwalk_callback_f callback)
+int walk_directory (const char *dir, dirwalk_callback_f callback,
+ void *user_data)
{
struct dirent *ent;
DIR *dh;
- int ok = 0;
+ int success;
+ int failure;
+
+ success = 0;
+ failure = 0;
if ((dh = opendir (dir)) == NULL)
{
char errbuf[1024];
- ERROR ("Cannot open '%s': %s", dir,
+ ERROR ("walk_directory: Cannot open '%s': %s", dir,
sstrerror (errno, errbuf, sizeof (errbuf)));
return -1;
}
while ((ent = readdir (dh)) != NULL)
{
+ int status;
+
if (ent->d_name[0] == '.')
continue;
- if (!callback(ent->d_name))
- ++ok;
+ status = (*callback) (dir, ent->d_name, user_data);
+ if (status != 0)
+ failure++;
+ else
+ success++;
}
closedir (dh);
- return ok ? 0 : -1;
+ if ((success == 0) && (failure > 0))
+ return (-1);
+ return (0);
}
int read_file_contents (const char *filename, char *buf, int bufsize)
#endif
#define sfree(ptr) \
- if((ptr) != NULL) { \
- free(ptr); \
- } \
- (ptr) = NULL
+ do { \
+ if((ptr) != NULL) { \
+ free(ptr); \
+ } \
+ (ptr) = NULL; \
+ } while (0)
#define STATIC_ARRAY_SIZE(a) (sizeof (a) / sizeof (*(a)))
(vl)->host, (vl)->plugin, (vl)->plugin_instance, \
(ds)->type, (vl)->type_instance)
-typedef int (*dirwalk_callback_f)(const char *filename);
-int walk_directory (const char *dir, dirwalk_callback_f callback);
+typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename,
+ void *user_data);
+int walk_directory (const char *dir, dirwalk_callback_f callback,
+ void *user_data);
int read_file_contents (const char *filename, char *buf, int bufsize);
#endif /* COMMON_H */
static int parse_line (char *buffer) /* {{{ */
{
- char *fields[256];
- int fields_num;
-
- fields[0] = "PUTVAL";
- fields_num = strsplit (buffer, fields + 1, STATIC_ARRAY_SIZE(fields) - 1);
-
- if (strcasecmp (fields[1], "putval") == 0)
- return (handle_putval (stdout, fields + 1, fields_num));
- else if (strcasecmp (fields[1], "putnotif") == 0)
- return (handle_putnotif (stdout, fields + 1, fields_num));
-
- /* compatibility code */
- return (handle_putval (stdout, fields, fields_num + 1));
+ if (strncasecmp ("PUTVAL", buffer, strlen ("PUTVAL")) == 0)
+ return (handle_putval (stdout, buffer));
+ else if (strncasecmp ("PUTNOTIF", buffer, strlen ("PUTNOTIF")) == 0)
+ return (handle_putnotif (stdout, buffer));
+ else
+ {
+ /* For backwards compatibility */
+ char tmp[1220];
+ /* Let's annoy the user a bit.. */
+ INFO ("exec plugin: Prepending `PUTVAL' to this line: %s", buffer);
+ ssnprintf (tmp, sizeof (tmp), "PUTVAL %s", buffer);
+ return (handle_putval (stdout, tmp));
+ }
} /* int parse_line }}} */
static void *exec_read_one (void *arg) /* {{{ */
--- /dev/null
+/**
+ * collectd - src/filecount.c
+ * Copyright (C) 2008 Alessandro Iurlano
+ * Copyright (C) 2008 Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Alessandro Iurlano <alessandro.iurlano at gmail.com>
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <fnmatch.h>
+
+struct fc_directory_conf_s
+{
+ char *path;
+ char *instance;
+
+ /* Data counters */
+ uint64_t files_num;
+ uint64_t files_size;
+
+ /* Selectors */
+ char *name;
+ int64_t mtime;
+ int64_t size;
+
+ /* Helper for the recursive functions */
+ time_t now;
+};
+typedef struct fc_directory_conf_s fc_directory_conf_t;
+
+static fc_directory_conf_t **directories = NULL;
+static size_t directories_num = 0;
+
+static void fc_submit_dir (const fc_directory_conf_t *dir)
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].gauge = (gauge_t) dir->files_num;
+
+ vl.values = values;
+ vl.values_len = STATIC_ARRAY_SIZE (values);
+ vl.time = time (NULL);
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "filecount", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, dir->instance, sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, "files", sizeof (vl.type));
+
+ plugin_dispatch_values (&vl);
+
+ values[0].gauge = (gauge_t) dir->files_size;
+ sstrncpy (vl.type, "bytes", sizeof (vl.type));
+
+ plugin_dispatch_values (&vl);
+} /* void fc_submit_dir */
+
+/*
+ * Config:
+ * <Plugin filecount>
+ * <Directory /path/to/dir>
+ * Instance "foobar"
+ * Name "*.conf"
+ * MTime -3600
+ * Size "+10M"
+ * </Directory>
+ * </Plugin>
+ *
+ * Collect:
+ * - Number of files
+ * - Total size
+ */
+
+static int fc_config_set_instance (fc_directory_conf_t *dir, const char *str)
+{
+ char buffer[1024];
+ char *ptr;
+ char *copy;
+
+ sstrncpy (buffer, str, sizeof (buffer));
+ for (ptr = buffer; *ptr != 0; ptr++)
+ if (*ptr == '/')
+ *ptr = '_';
+
+ for (ptr = buffer; *ptr == '_'; ptr++)
+ /* do nothing */;
+
+ if (*ptr == 0)
+ return (-1);
+
+ copy = strdup (ptr);
+ if (copy == NULL)
+ return (-1);
+
+ sfree (dir->instance);
+ dir->instance = copy;
+
+ return (0);
+} /* int fc_config_set_instance */
+
+static int fc_config_add_dir_instance (fc_directory_conf_t *dir,
+ oconfig_item_t *ci)
+{
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("filecount plugin: The `Instance' config option needs exactly "
+ "one string argument.");
+ return (-1);
+ }
+
+ return (fc_config_set_instance (dir, ci->values[0].value.string));
+} /* int fc_config_add_dir_instance */
+
+static int fc_config_add_dir_name (fc_directory_conf_t *dir,
+ oconfig_item_t *ci)
+{
+ char *temp;
+
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("filecount plugin: The `Name' config option needs exactly one "
+ "string argument.");
+ return (-1);
+ }
+
+ temp = strdup (ci->values[0].value.string);
+ if (temp == NULL)
+ {
+ ERROR ("filecount plugin: strdup failed.");
+ return (-1);
+ }
+
+ sfree (dir->name);
+ dir->name = temp;
+
+ return (0);
+} /* int fc_config_add_dir_name */
+
+static int fc_config_add_dir_mtime (fc_directory_conf_t *dir,
+ oconfig_item_t *ci)
+{
+ char *endptr;
+ double temp;
+
+ if ((ci->values_num != 1)
+ || ((ci->values[0].type != OCONFIG_TYPE_STRING)
+ && (ci->values[0].type != OCONFIG_TYPE_NUMBER)))
+ {
+ WARNING ("filecount plugin: The `MTime' config option needs exactly one "
+ "string or numeric argument.");
+ return (-1);
+ }
+
+ if (ci->values[0].type == OCONFIG_TYPE_NUMBER)
+ {
+ dir->mtime = (int64_t) ci->values[0].value.number;
+ return (0);
+ }
+
+ errno = 0;
+ endptr = NULL;
+ temp = strtod (ci->values[0].value.string, &endptr);
+ if ((errno != 0) || (endptr == NULL)
+ || (endptr == ci->values[0].value.string))
+ {
+ WARNING ("filecount plugin: Converting `%s' to a number failed.",
+ ci->values[0].value.string);
+ return (-1);
+ }
+
+ switch (*endptr)
+ {
+ case 0:
+ case 's':
+ case 'S':
+ break;
+
+ case 'm':
+ case 'M':
+ temp *= 60;
+ break;
+
+ case 'h':
+ case 'H':
+ temp *= 3600;
+ break;
+
+ case 'd':
+ case 'D':
+ temp *= 86400;
+ break;
+
+ case 'w':
+ case 'W':
+ temp *= 7 * 86400;
+ break;
+
+ case 'y':
+ case 'Y':
+ temp *= 31557600; /* == 365.25 * 86400 */
+ break;
+
+ default:
+ WARNING ("filecount plugin: Invalid suffix for `MTime': `%c'", *endptr);
+ return (-1);
+ } /* switch (*endptr) */
+
+ dir->mtime = (int64_t) temp;
+
+ return (0);
+} /* int fc_config_add_dir_mtime */
+
+static int fc_config_add_dir_size (fc_directory_conf_t *dir,
+ oconfig_item_t *ci)
+{
+ char *endptr;
+ double temp;
+
+ if ((ci->values_num != 1)
+ || ((ci->values[0].type != OCONFIG_TYPE_STRING)
+ && (ci->values[0].type != OCONFIG_TYPE_NUMBER)))
+ {
+ WARNING ("filecount plugin: The `Size' config option needs exactly one "
+ "string or numeric argument.");
+ return (-1);
+ }
+
+ if (ci->values[0].type == OCONFIG_TYPE_NUMBER)
+ {
+ dir->size = (int64_t) ci->values[0].value.number;
+ return (0);
+ }
+
+ errno = 0;
+ endptr = NULL;
+ temp = strtod (ci->values[0].value.string, &endptr);
+ if ((errno != 0) || (endptr == NULL)
+ || (endptr == ci->values[0].value.string))
+ {
+ WARNING ("filecount plugin: Converting `%s' to a number failed.",
+ ci->values[0].value.string);
+ return (-1);
+ }
+
+ switch (*endptr)
+ {
+ case 0:
+ case 'b':
+ case 'B':
+ break;
+
+ case 'k':
+ case 'K':
+ temp *= 1000.0;
+ break;
+
+ case 'm':
+ case 'M':
+ temp *= 1000.0 * 1000.0;
+ break;
+
+ case 'g':
+ case 'G':
+ temp *= 1000.0 * 1000.0 * 1000.0;
+ break;
+
+ case 't':
+ case 'T':
+ temp *= 1000.0 * 1000.0 * 1000.0 * 1000.0;
+ break;
+
+ case 'p':
+ case 'P':
+ temp *= 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0;
+ break;
+
+ default:
+ WARNING ("filecount plugin: Invalid suffix for `Size': `%c'", *endptr);
+ return (-1);
+ } /* switch (*endptr) */
+
+ dir->size = (int64_t) temp;
+
+ return (0);
+} /* int fc_config_add_dir_size */
+
+static int fc_config_add_dir (oconfig_item_t *ci)
+{
+ fc_directory_conf_t *dir;
+ int status;
+ int i;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("filecount plugin: `Directory' needs exactly one string "
+ "argument.");
+ return (-1);
+ }
+
+ /* Initialize `dir' */
+ dir = (fc_directory_conf_t *) malloc (sizeof (*dir));
+ if (dir == NULL)
+ {
+ ERROR ("filecount plugin: malloc failed.");
+ return (-1);
+ }
+ memset (dir, 0, sizeof (*dir));
+
+ dir->path = strdup (ci->values[0].value.string);
+ if (dir->path == NULL)
+ {
+ ERROR ("filecount plugin: strdup failed.");
+ return (-1);
+ }
+
+ fc_config_set_instance (dir, dir->path);
+
+ dir->name = NULL;
+ dir->mtime = 0;
+ dir->size = 0;
+
+ status = 0;
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp ("Instance", option->key) == 0)
+ status = fc_config_add_dir_instance (dir, option);
+ else if (strcasecmp ("Name", option->key) == 0)
+ status = fc_config_add_dir_name (dir, option);
+ else if (strcasecmp ("MTime", option->key) == 0)
+ status = fc_config_add_dir_mtime (dir, option);
+ else if (strcasecmp ("Size", option->key) == 0)
+ status = fc_config_add_dir_size (dir, option);
+ else
+ {
+ WARNING ("filecount plugin: fc_config_add_dir: "
+ "Option `%s' not allowed here.", option->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (ci->children) */
+
+ if (status == 0)
+ {
+ fc_directory_conf_t **temp;
+
+ temp = (fc_directory_conf_t **) realloc (directories,
+ sizeof (*directories) * directories_num);
+ if (temp == NULL)
+ {
+ ERROR ("filecount plugin: realloc failed.");
+ status = -1;
+ }
+ else
+ {
+ directories = temp;
+ directories[directories_num] = dir;
+ directories_num++;
+ }
+ }
+
+ if (status != 0)
+ {
+ sfree (dir->name);
+ sfree (dir->instance);
+ sfree (dir->path);
+ sfree (dir);
+ return (-1);
+ }
+
+ return (0);
+} /* int fc_config_add_dir */
+
+static int fc_config (oconfig_item_t *ci)
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ if (strcasecmp ("Directory", child->key) == 0)
+ fc_config_add_dir (child);
+ else
+ {
+ WARNING ("filecount plugin: Ignoring unknown config option `%s'.",
+ child->key);
+ }
+ } /* for (ci->children) */
+
+ return (0);
+} /* int fc_config */
+
+static int fc_init (void)
+{
+ if (directories_num < 1)
+ {
+ WARNING ("filecount plugin: No directories have been configured.");
+ return (-1);
+ }
+
+ return (0);
+} /* int fc_init */
+
+static int fc_read_dir_callback (const char *dirname, const char *filename,
+ void *user_data)
+{
+ fc_directory_conf_t *dir = user_data;
+ char abs_path[PATH_MAX];
+ struct stat statbuf;
+ int status;
+
+ if (dir == NULL)
+ return (-1);
+
+ ssnprintf (abs_path, sizeof (abs_path), "%s/%s", dirname, filename);
+
+ status = lstat (abs_path, &statbuf);
+ if (status != 0)
+ {
+ ERROR ("filecount plugin: stat (%s) failed.", abs_path);
+ return (-1);
+ }
+
+ if (S_ISDIR (statbuf.st_mode))
+ {
+ status = walk_directory (abs_path, fc_read_dir_callback, dir);
+ return (status);
+ }
+ else if (!S_ISREG (statbuf.st_mode))
+ {
+ return (0);
+ }
+
+ if (dir->name != NULL)
+ {
+ status = fnmatch (dir->name, filename, /* flags = */ 0);
+ if (status != 0)
+ return (0);
+ }
+
+ if (dir->mtime != 0)
+ {
+ time_t mtime = dir->now;
+
+ if (dir->mtime < 0)
+ mtime += dir->mtime;
+ else
+ mtime -= dir->mtime;
+
+ DEBUG ("filecount plugin: Only collecting files that were touched %s %u.",
+ (dir->mtime < 0) ? "after" : "before",
+ (unsigned int) mtime);
+
+ if (((dir->mtime < 0) && (statbuf.st_mtime < mtime))
+ || ((dir->mtime > 0) && (statbuf.st_mtime > mtime)))
+ return (0);
+ }
+
+ if (dir->size != 0)
+ {
+ off_t size;
+
+ if (dir->size < 0)
+ size = (off_t) ((-1) * dir->size);
+ else
+ size = (off_t) dir->size;
+
+ if (((dir->size < 0) && (statbuf.st_size > size))
+ || ((dir->size > 0) && (statbuf.st_size < size)))
+ return (0);
+ }
+
+ dir->files_num++;
+ dir->files_size += (uint64_t) statbuf.st_size;
+
+ return (0);
+} /* int fc_read_dir_callback */
+
+static int fc_read_dir (fc_directory_conf_t *dir)
+{
+ int status;
+
+ dir->files_num = 0;
+ dir->files_size = 0;
+
+ if (dir->mtime != 0)
+ dir->now = time (NULL);
+
+ status = walk_directory (dir->path, fc_read_dir_callback, dir);
+ if (status != 0)
+ {
+ WARNING ("filecount plugin: walk_directory (%s) failed.", dir->path);
+ return (-1);
+ }
+
+ fc_submit_dir (dir);
+
+ return (0);
+} /* int fc_read_dir */
+
+static int fc_read (void)
+{
+ size_t i;
+
+ for (i = 0; i < directories_num; i++)
+ fc_read_dir (directories[i]);
+
+ return (0);
+} /* int fc_read */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("filecount", fc_config);
+ plugin_register_init ("filecount", fc_init);
+ plugin_register_read ("filecount", fc_read);
+} /* void module_register */
+
+/*
+ * vim: set sw=2 sts=2 et :
+ */
/**
* collectd - src/ipmi.c
* Copyright (C) 2008 Florian octo Forster
+ * Copyright (C) 2008 Peter Holik
*
* 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
*
* Authors:
* Florian octo Forster <octo at verplant.org>
+ * Peter Holik <peter at holik.at>
**/
#include "collectd.h"
struct c_ipmi_sensor_list_s
{
ipmi_sensor_id_t sensor_id;
+ char sensor_name[DATA_MAX_NAME_LEN];
+ char sensor_type[DATA_MAX_NAME_LEN];
+ int sensor_not_present;
c_ipmi_sensor_list_t *next;
};
static pthread_mutex_t sensor_list_lock = PTHREAD_MUTEX_INITIALIZER;
static c_ipmi_sensor_list_t *sensor_list = NULL;
+static int c_ipmi_init_in_progress = 0;
static int c_ipmi_active = 0;
static pthread_t thread_id = (pthread_t) 0;
static const char *config_keys[] =
{
"Sensor",
- "IgnoreSelected"
+ "IgnoreSelected",
+ "NotifySensorAdd",
+ "NotifySensorRemove",
+ "NotifySensorNotPresent"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
static ignorelist_t *ignorelist = NULL;
+static int c_ipmi_nofiy_add = 0;
+static int c_ipmi_nofiy_remove = 0;
+static int c_ipmi_nofiy_notpresent = 0;
+
/*
* Misc private functions
*/
value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- char sensor_name[IPMI_SENSOR_NAME_LEN];
- char *sensor_name_ptr;
- int sensor_type;
- const char *type;
-
- memset (sensor_name, 0, sizeof (sensor_name));
- ipmi_sensor_get_name (sensor, sensor_name, sizeof (sensor_name));
- sensor_name[sizeof (sensor_name) - 1] = 0;
-
- sensor_name_ptr = strstr (sensor_name, ").");
- if (sensor_name_ptr == NULL)
- sensor_name_ptr = sensor_name;
- else
- sensor_name_ptr += 2;
+ c_ipmi_sensor_list_t *list_item = (c_ipmi_sensor_list_t *)user_data;
if (err != 0)
{
- INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with status %#x.",
- sensor_name_ptr, err);
- sensor_list_remove (sensor);
+ if ((err & 0xff) == IPMI_NOT_PRESENT_CC)
+ {
+ if (list_item->sensor_not_present == 0)
+ {
+ list_item->sensor_not_present = 1;
+
+ INFO ("ipmi plugin: sensor_read_handler: sensor %s "
+ "not present.", list_item->sensor_name);
+
+ if (c_ipmi_nofiy_notpresent)
+ {
+ notification_t n = { NOTIF_WARNING, time(NULL), "", "", "ipmi",
+ "", "", "", NULL };
+
+ sstrncpy (n.host, hostname_g, sizeof (n.host));
+ sstrncpy (n.type_instance, list_item->sensor_name,
+ sizeof (n.type_instance));
+ sstrncpy (n.type, list_item->sensor_type, sizeof (n.type));
+ ssnprintf (n.message, sizeof (n.message),
+ "sensor %s not present", list_item->sensor_name);
+
+ plugin_dispatch_notification (&n);
+ }
+ }
+ }
+ else
+ {
+ INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
+ "because it failed with status %#x.",
+ list_item->sensor_name, err);
+ sensor_list_remove (sensor);
+ }
return;
}
+ else if (list_item->sensor_not_present == 1)
+ {
+ list_item->sensor_not_present = 0;
+
+ INFO ("ipmi plugin: sensor_read_handler: sensor %s present.",
+ list_item->sensor_name);
+
+ if (c_ipmi_nofiy_notpresent)
+ {
+ notification_t n = { NOTIF_OKAY, time(NULL), "", "", "ipmi",
+ "", "", "", NULL };
+
+ sstrncpy (n.host, hostname_g, sizeof (n.host));
+ sstrncpy (n.type_instance, list_item->sensor_name,
+ sizeof (n.type_instance));
+ sstrncpy (n.type, list_item->sensor_type, sizeof (n.type));
+ ssnprintf (n.message, sizeof (n.message),
+ "sensor %s present", list_item->sensor_name);
+
+ plugin_dispatch_notification (&n);
+ }
+ }
if (value_present != IPMI_BOTH_VALUES_PRESENT)
{
INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
"because it provides %s. If you need this sensor, "
"please file a bug report.",
- sensor_name_ptr,
+ list_item->sensor_name,
(value_present == IPMI_RAW_VALUE_PRESENT)
? "only the raw value"
: "no value");
return;
}
- /* Both `ignorelist' and `plugin_instance' may be NULL. */
- if (ignorelist_match (ignorelist, sensor_name_ptr) != 0)
+ values[0].gauge = value;
+
+ vl.values = values;
+ vl.values_len = 1;
+ vl.time = time (NULL);
+
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "ipmi", sizeof (vl.plugin));
+ sstrncpy (vl.type, list_item->sensor_type, sizeof (vl.type));
+ sstrncpy (vl.type_instance, list_item->sensor_name, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+} /* void sensor_read_handler */
+
+static int sensor_list_add (ipmi_sensor_t *sensor)
+{
+ ipmi_sensor_id_t sensor_id;
+ c_ipmi_sensor_list_t *list_item;
+ c_ipmi_sensor_list_t *list_prev;
+
+ char sensor_name[DATA_MAX_NAME_LEN];
+ char *sensor_name_ptr;
+ int sensor_type, len;
+ const char *type;
+ ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
+
+ sensor_id = ipmi_sensor_convert_to_id (sensor);
+
+ memset (sensor_name, 0, sizeof (sensor_name));
+ ipmi_sensor_get_name (sensor, sensor_name, sizeof (sensor_name));
+ sensor_name[sizeof (sensor_name) - 1] = 0;
+
+ len = DATA_MAX_NAME_LEN - strlen(sensor_name);
+ strncat(sensor_name, " ", len--);
+ strncat(sensor_name, ipmi_entity_get_entity_id_string(ent), len);
+
+ sensor_name_ptr = strstr (sensor_name, ").");
+ if (sensor_name_ptr == NULL)
+ sensor_name_ptr = sensor_name;
+ else
{
- sensor_list_remove (sensor);
- return;
+ char *sensor_name_ptr_id = strstr (sensor_name, "(");
+
+ sensor_name_ptr += 2;
+ len = DATA_MAX_NAME_LEN - strlen(sensor_name);
+ strncat(sensor_name, " ", len--);
+ strncat(sensor_name, sensor_name_ptr_id,
+ MIN(sensor_name_ptr - sensor_name_ptr_id - 1, len));
}
+ /* Both `ignorelist' and `plugin_instance' may be NULL. */
+ if (ignorelist_match (ignorelist, sensor_name_ptr) != 0)
+ return (0);
+
/* FIXME: Use rate unit or base unit to scale the value */
sensor_type = ipmi_sensor_get_sensor_type (sensor);
default:
{
const char *sensor_type_str;
-
+
sensor_type_str = ipmi_sensor_get_sensor_type_string (sensor);
- INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
+ INFO ("ipmi plugin: sensor_list_add: Ignore sensor %s, "
"because I don't know how to handle its type (%#x, %s). "
"If you need this sensor, please file a bug report.",
sensor_name_ptr, sensor_type, sensor_type_str);
- sensor_list_remove (sensor);
- return;
+ return (-1);
}
} /* switch (sensor_type) */
- values[0].gauge = value;
-
- vl.values = values;
- vl.values_len = 1;
- vl.time = time (NULL);
-
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
- sstrncpy (vl.plugin, "ipmi", sizeof (vl.plugin));
- sstrncpy (vl.type, type, sizeof (vl.type));
- sstrncpy (vl.type_instance, sensor_name_ptr, sizeof (vl.type_instance));
-
- plugin_dispatch_values (&vl);
-} /* void sensor_read_handler */
-
-static int sensor_list_add (ipmi_sensor_t *sensor)
-{
- ipmi_sensor_id_t sensor_id;
- c_ipmi_sensor_list_t *list_item;
- c_ipmi_sensor_list_t *list_prev;
-
- sensor_id = ipmi_sensor_convert_to_id (sensor);
-
pthread_mutex_lock (&sensor_list_lock);
list_prev = NULL;
else
sensor_list = list_item;
+ sstrncpy (list_item->sensor_name, sensor_name_ptr,
+ sizeof (list_item->sensor_name));
+ sstrncpy (list_item->sensor_type, type, sizeof (list_item->sensor_type));
+
pthread_mutex_unlock (&sensor_list_lock);
+ if (c_ipmi_nofiy_add && (c_ipmi_init_in_progress == 0))
+ {
+ notification_t n = { NOTIF_OKAY, time(NULL), "", "", "ipmi",
+ "", "", "", NULL };
+
+ sstrncpy (n.host, hostname_g, sizeof (n.host));
+ sstrncpy (n.type_instance, list_item->sensor_name,
+ sizeof (n.type_instance));
+ sstrncpy (n.type, list_item->sensor_type, sizeof (n.type));
+ ssnprintf (n.message, sizeof (n.message),
+ "sensor %s added", list_item->sensor_name);
+
+ plugin_dispatch_notification (&n);
+ }
+
return (0);
} /* int sensor_list_add */
pthread_mutex_unlock (&sensor_list_lock);
+ if (c_ipmi_nofiy_remove && c_ipmi_active)
+ {
+ notification_t n = { NOTIF_WARNING, time(NULL), "", "",
+ "ipmi", "", "", "", NULL };
+
+ sstrncpy (n.host, hostname_g, sizeof (n.host));
+ sstrncpy (n.type_instance, list_item->sensor_name,
+ sizeof (n.type_instance));
+ sstrncpy (n.type, list_item->sensor_type, sizeof (n.type));
+ ssnprintf (n.message, sizeof (n.message),
+ "sensor %s removed", list_item->sensor_name);
+
+ plugin_dispatch_notification (&n);
+ }
+
free (list_item);
return (0);
} /* int sensor_list_remove */
list_item = list_item->next)
{
ipmi_sensor_id_get_reading (list_item->sensor_id,
- sensor_read_handler, /* user data = */ NULL);
+ sensor_read_handler, /* user data = */ list_item);
} /* for (list_item) */
pthread_mutex_unlock (&sensor_list_lock);
{
int invert = 1;
if ((strcasecmp ("True", value) == 0)
- || (strcasecmp ("Yes", value) == 0)
- || (strcasecmp ("On", value) == 0))
+ || (strcasecmp ("Yes", value) == 0)
+ || (strcasecmp ("On", value) == 0))
invert = 0;
ignorelist_set_invert (ignorelist, invert);
}
+ else if (strcasecmp ("NotifySensorAdd", key) == 0)
+ {
+ if ((strcasecmp ("True", value) == 0)
+ || (strcasecmp ("Yes", value) == 0)
+ || (strcasecmp ("On", value) == 0))
+ c_ipmi_nofiy_add = 1;
+ }
+ else if (strcasecmp ("NotifySensorRemove", key) == 0)
+ {
+ if ((strcasecmp ("True", value) == 0)
+ || (strcasecmp ("Yes", value) == 0)
+ || (strcasecmp ("On", value) == 0))
+ c_ipmi_nofiy_remove = 1;
+ }
+ else if (strcasecmp ("NotifySensorNotPresent", key) == 0)
+ {
+ if ((strcasecmp ("True", value) == 0)
+ || (strcasecmp ("Yes", value) == 0)
+ || (strcasecmp ("On", value) == 0))
+ c_ipmi_nofiy_notpresent = 1;
+ }
else
{
return (-1);
{
int status;
+ /* Don't send `ADD' notifications during startup (~ 1 minute) */
+ c_ipmi_init_in_progress = 1 + (60 / interval_g);
+
c_ipmi_active = 1;
status = pthread_create (&thread_id, /* attr = */ NULL, thread_main,
}
sensor_list_read_all ();
-
+
+ if (c_ipmi_init_in_progress > 0)
+ c_ipmi_init_in_progress--;
+ else
+ c_ipmi_init_in_progress = 0;
+
return (0);
} /* int c_ipmi_read */
{
struct pollfd p;
- int n;
+ int status;
+ memset (&p, 0, sizeof (p));
p.fd = fd;
- p.events = POLLIN|POLLERR|POLLHUP;
+ 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;
+ status = poll (&p, /* nfds = */ 1, /* timeout = */ 1000 * interval_g);
+ if (status <= 0)
+ {
+ if (status == 0)
+ {
+ ERROR ("memcached: poll(2) timed out after %i seconds.", interval_g);
+ }
+ else
+ {
+ char errbuf[1024];
+ ERROR ("memcached: poll(2) failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ }
+ shutdown (fd, SHUT_RDWR);
+ close (fd);
+ return (-1);
}
}
if ((ios = sg_get_mem_stats ()) != NULL)
{
memory_submit ("used", ios->used);
- memory_submit ("cached", ios->cached);
+ memory_submit ("cached", ios->cache);
memory_submit ("free", ios->free);
}
#endif /* HAVE_LIBSTATGRAB */
#include <owcapi.h>
+#define OW_FAMILY_LENGTH 8
#define OW_FAMILY_MAX_FEATURES 2
struct ow_family_features_s
{
- char *family;
+ char family[OW_FAMILY_LENGTH];
struct
{
char filename[DATA_MAX_NAME_LEN];
static ow_family_features_t ow_family_features[] =
{
{
- /* family = */ "10",
+ /* family = */ "10.",
{
{
/* filename = */ "temperature",
static const char *config_keys[] =
{
- "Alias",
"Device",
"IgnoreSelected",
"Sensor",
sfree (device_g);
device_g = temp;
}
- else if (strcasecmp (key, "Alias") == 0)
- {
- /* azogtodo alias-list */
- }
else
{
return (-1);
return ((success > 0) ? 0 : -1);
} /* int cow_read_values */
+/* Forward declaration so the recursion below works */
+static int cow_read_bus (const char *path);
+
+/*
+ * cow_read_ds2409
+ *
+ * Handles:
+ * - DS2409 - MicroLAN Coupler
+ */
+static int cow_read_ds2409 (const char *path)
+{
+ char subpath[4096];
+ int status;
+
+ status = ssnprintf (subpath, sizeof (subpath), "%s/main", path);
+ if ((status > 0) && (status < sizeof (subpath)))
+ cow_read_bus (subpath);
+
+ status = ssnprintf (subpath, sizeof (subpath), "%s/aux", path);
+ if ((status > 0) && (status < sizeof (subpath)))
+ cow_read_bus (subpath);
+
+ return (0);
+} /* int cow_read_ds2409 */
+
static int cow_read_bus (const char *path)
{
char *buffer;
char *dummy;
char *saveptr;
char subpath[4096];
- char family_dummy[3]; /* a family only has 2 digits */
status = OW_get (path, &buffer, &buffer_size);
if (status < 0)
path, status);
return (-1);
}
+ DEBUG ("onewire plugin: OW_get (%s) returned: %s",
+ path, buffer);
dummy = buffer;
saveptr = NULL;
dummy = NULL;
- snprintf (subpath, sizeof (subpath), "%s/%s", path, buffer_ptr);
- subpath[sizeof (subpath) - 1] = 0;
+ if (strcmp ("/", path) == 0)
+ status = ssnprintf (subpath, sizeof (subpath), "/%s", buffer_ptr);
+ else
+ status = ssnprintf (subpath, sizeof (subpath), "%s/%s",
+ path, buffer_ptr);
+ if ((status <= 0) || (status >= sizeof (subpath)))
+ continue;
for (i = 0; i < ow_family_features_num; i++)
{
- snprintf (family_dummy, sizeof (family_dummy), "%s%s", ow_family_features[i].family, ".");
- if (strncmp (family_dummy, buffer_ptr, strlen (family_dummy)) != 0)
+ if (strncmp (ow_family_features[i].family, buffer_ptr,
+ strlen (ow_family_features[i].family)) != 0)
continue;
- cow_read_values (subpath, buffer_ptr + 3, ow_family_features + i);
+ cow_read_values (subpath,
+ buffer_ptr + strlen (ow_family_features[i].family),
+ ow_family_features + i);
+ break;
+ }
+ if (i < ow_family_features_num)
+ continue;
+
+ /* DS2409 */
+ if (strncmp ("1F.", buffer_ptr, strlen ("1F.")) == 0)
+ {
+ cow_read_ds2409 (subpath);
+ continue;
}
} /* while (strtok_r) */
static int cow_read (void)
{
- char *buffer;
- size_t buffer_size;
- int status;
-
- char *buffer_ptr;
- char *dummy;
- char *saveptr;
-
- status = OW_get ("/uncached/", &buffer, &buffer_size);
- if (status < 0)
- {
- ERROR ("onewire plugin: OW_get (\"/\") failed. status = %#x;",
- status);
- return (-1);
- }
-
- printf ("-- 8< --\n");
-
- dummy = buffer;
- saveptr = NULL;
- while ((buffer_ptr = strtok_r (dummy, ",/", &saveptr)) != NULL)
- {
- dummy = NULL;
- if (strncmp ("bus", buffer_ptr, strlen ("bus")) == 0)
- {
- cow_read_bus (buffer_ptr);
- }
- } /* while (strtok_r) */
-
- printf ("-- >8 --\n");
-
- free (buffer);
-
- return (0);
+ return (cow_read_bus ("/"));
} /* int cow_read */
static int cow_shutdown (void)
#define PLUGIN_TYPES 7
+#define PLUGIN_CONFIG 254
#define PLUGIN_DATASET 255
#define log_debug(...) DEBUG ("perl: " __VA_ARGS__)
static XS (Collectd_plugin_register_ds);
static XS (Collectd_plugin_unregister_ds);
static XS (Collectd_plugin_dispatch_values);
-static XS (Collectd_plugin_flush_one);
-static XS (Collectd_plugin_flush_all);
+static XS (Collectd__plugin_flush);
static XS (Collectd_plugin_dispatch_notification);
static XS (Collectd_plugin_log);
static XS (Collectd_call_by_name);
{ "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_flush_one", Collectd_plugin_flush_one },
- { "Collectd::plugin_flush_all", Collectd_plugin_flush_all },
+ { "Collectd::_plugin_flush", Collectd__plugin_flush },
{ "Collectd::plugin_dispatch_notification",
Collectd_plugin_dispatch_notification },
{ "Collectd::plugin_log", Collectd_plugin_log },
{ "Collectd::TYPE_LOG", PLUGIN_LOG },
{ "Collectd::TYPE_NOTIF", PLUGIN_NOTIF },
{ "Collectd::TYPE_FLUSH", PLUGIN_FLUSH },
+ { "Collectd::TYPE_CONFIG", PLUGIN_CONFIG },
{ "Collectd::TYPE_DATASET", PLUGIN_DATASET },
{ "Collectd::DS_TYPE_COUNTER", DS_TYPE_COUNTER },
{ "Collectd::DS_TYPE_GAUGE", DS_TYPE_GAUGE },
return 0;
} /* static int notification2hv (notification_t *, HV *) */
+static int oconfig_item2hv (pTHX_ oconfig_item_t *ci, HV *hash)
+{
+ int i;
+
+ AV *values;
+ AV *children;
+
+ if (NULL == hv_store (hash, "key", 3, newSVpv (ci->key, 0), 0))
+ return -1;
+
+ values = newAV ();
+ if (0 < ci->values_num)
+ av_extend (values, ci->values_num);
+
+ if (NULL == hv_store (hash, "values", 6, newRV_noinc ((SV *)values), 0)) {
+ av_clear (values);
+ av_undef (values);
+ return -1;
+ }
+
+ for (i = 0; i < ci->values_num; ++i) {
+ SV *value;
+
+ switch (ci->values[i].type) {
+ case OCONFIG_TYPE_STRING:
+ value = newSVpv (ci->values[i].value.string, 0);
+ break;
+ case OCONFIG_TYPE_NUMBER:
+ value = newSVnv ((NV)ci->values[i].value.number);
+ break;
+ case OCONFIG_TYPE_BOOLEAN:
+ value = ci->values[i].value.boolean ? &PL_sv_yes : &PL_sv_no;
+ break;
+ default:
+ log_err ("oconfig_item2hv: Invalid value type %i.",
+ ci->values[i].type);
+ value = &PL_sv_undef;
+ }
+
+ if (NULL == av_store (values, i, value)) {
+ sv_free (value);
+ return -1;
+ }
+ }
+
+ /* ignoring 'parent' member which is uninteresting in this case */
+
+ children = newAV ();
+ if (0 < ci->children_num)
+ av_extend (children, ci->children_num);
+
+ if (NULL == hv_store (hash, "children", 8, newRV_noinc ((SV *)children), 0)) {
+ av_clear (children);
+ av_undef (children);
+ return -1;
+ }
+
+ for (i = 0; i < ci->children_num; ++i) {
+ HV *child = newHV ();
+
+ if (0 != oconfig_item2hv (aTHX_ ci->children + i, child)) {
+ hv_clear (child);
+ hv_undef (child);
+ return -1;
+ }
+
+ if (NULL == av_store (children, i, newRV_noinc ((SV *)child))) {
+ hv_clear (child);
+ hv_undef (child);
+ return -1;
+ }
+ }
+ return 0;
+} /* static int oconfig_item2hv (pTHX_ oconfig_item_t *, HV *) */
+
/*
* Internal functions.
*/
else if (PLUGIN_FLUSH == type) {
/*
* $_[0] = $timeout;
+ * $_[1] = $identifier;
*/
XPUSHs (sv_2mortal (newSViv (va_arg (ap, int))));
+ XPUSHs (sv_2mortal (newSVpv (va_arg (ap, char *), 0)));
}
PUTBACK;
} /* static XS (Collectd_plugin_dispatch_values) */
/*
- * Collectd::plugin_flush_one (timeout, name).
+ * Collectd::_plugin_flush (plugin, timeout, identifier).
+ *
+ * plugin:
+ * name of the plugin to flush
*
* timeout:
* timeout to use when flushing the data
*
- * name:
- * name of the plugin to flush
+ * identifier:
+ * data-set identifier to flush
*/
-static XS (Collectd_plugin_flush_one)
+static XS (Collectd__plugin_flush)
{
+ char *plugin = NULL;
+ int timeout = -1;
+ char *id = NULL;
+
dXSARGS;
- if (2 != items) {
- log_err ("Usage: Collectd::plugin_flush_one(timeout, name)");
+ if (3 != items) {
+ log_err ("Usage: Collectd::_plugin_flush(plugin, timeout, id)");
XSRETURN_EMPTY;
}
- log_debug ("Collectd::plugin_flush_one: timeout = %i, name = \"%s\"",
- (int)SvIV (ST (0)), SvPV_nolen (ST (1)));
+ if (SvOK (ST (0)))
+ plugin = SvPV_nolen (ST (0));
- if (0 == plugin_flush_one ((int)SvIV (ST (0)), SvPV_nolen (ST (1))))
- XSRETURN_YES;
- else
- XSRETURN_EMPTY;
-} /* static XS (Collectd_plugin_flush_one) */
+ if (SvOK (ST (1)))
+ timeout = (int)SvIV (ST (1));
-/*
- * Collectd::plugin_flush_all (timeout).
- *
- * timeout:
- * timeout to use when flushing the data
- */
-static XS (Collectd_plugin_flush_all)
-{
- dXSARGS;
+ if (SvOK (ST (2)))
+ id = SvPV_nolen (ST (2));
- if (1 != items) {
- log_err ("Usage: Collectd::plugin_flush_all(timeout)");
- XSRETURN_EMPTY;
- }
-
- log_debug ("Collectd::plugin_flush_all: timeout = %i", (int)SvIV (ST (0)));
+ log_debug ("Collectd::_plugin_flush: plugin = \"%s\", timeout = %i, "
+ "id = \"%s\"", plugin, timeout, id);
- plugin_flush_all ((int)SvIV (ST (0)));
- XSRETURN_YES;
-} /* static XS (Collectd_plugin_flush_all) */
+ if (0 == plugin_flush (plugin, timeout, id))
+ XSRETURN_YES;
+ else
+ XSRETURN_EMPTY;
+} /* static XS (Collectd__plugin_flush) */
/*
* Collectd::plugin_dispatch_notification (notif).
return pplugin_call_all (aTHX_ PLUGIN_NOTIF, notif);
} /* static int perl_notify (const notification_t *) */
-/* TODO: Implement flushing of single identifiers. */
static int perl_flush (int timeout, const char *identifier)
{
dTHX;
aTHX = t->interp;
}
- return pplugin_call_all (aTHX_ PLUGIN_FLUSH, timeout);
+ return pplugin_call_all (aTHX_ PLUGIN_FLUSH, timeout, identifier);
} /* static int perl_flush (const int) */
static int perl_shutdown (void)
return 0;
} /* static int perl_config_includedir (oconfig_item_it *) */
+/*
+ * <Plugin> block
+ */
+static int perl_config_plugin (pTHX_ oconfig_item_t *ci)
+{
+ int retvals = 0;
+ int ret = 0;
+
+ char *plugin;
+ HV *config;
+
+ dSP;
+
+ if ((1 != ci->values_num) || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+ log_err ("LoadPlugin expects a single string argument.");
+ return 1;
+ }
+
+ plugin = ci->values[0].value.string;
+ config = newHV ();
+
+ if (0 != oconfig_item2hv (aTHX_ ci, config)) {
+ hv_clear (config);
+ hv_undef (config);
+
+ log_err ("Unable to convert configuration to a Perl hash value.");
+ config = Nullhv;
+ }
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK (SP);
+
+ XPUSHs (sv_2mortal (newSVpv (plugin, 0)));
+ XPUSHs (sv_2mortal (newRV_noinc ((SV *)config)));
+
+ PUTBACK;
+
+ retvals = call_pv ("Collectd::_plugin_dispatch_config", G_SCALAR);
+
+ SPAGAIN;
+ if (0 < retvals) {
+ SV *tmp = POPs;
+ if (! SvTRUE (tmp))
+ ret = -1;
+ }
+ else
+ ret = -1;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ return ret;
+} /* static int perl_config_plugin (oconfig_item_it *) */
+
static int perl_config (oconfig_item_t *ci)
{
int i = 0;
- dTHX;
-
- /* dTHX does not get any valid values in case Perl
- * has not been initialized */
- if (NULL == perl_threads)
- aTHX = NULL;
+ dTHXa (NULL);
for (i = 0; i < ci->children_num; ++i) {
oconfig_item_t *c = ci->children + i;
+ if (NULL != perl_threads)
+ aTHX = PERL_GET_CONTEXT;
+
if (0 == strcasecmp (c->key, "LoadPlugin"))
perl_config_loadplugin (aTHX_ c);
else if (0 == strcasecmp (c->key, "BaseName"))
perl_config_enabledebugger (aTHX_ c);
else if (0 == strcasecmp (c->key, "IncludeDir"))
perl_config_includedir (aTHX_ c);
+ else if (0 == strcasecmp (c->key, "Plugin"))
+ perl_config_plugin (aTHX_ c);
else
log_warn ("Ignoring unknown config key \"%s\".", c->key);
}
{
const char *error = lt_dlerror ();
- ERROR ("lt_dlopen failed: %s", error);
- fprintf (stderr, "lt_dlopen failed: %s\n", error);
+ ERROR ("lt_dlopen (%s) failed: %s", file, error);
+ fprintf (stderr, "lt_dlopen (%s) failed: %s\n", file, error);
return (1);
}
if ((reg_handle = (void (*) (void)) lt_dlsym (dlh, "module_register")) == NULL)
{
- WARNING ("Couldn't find symbol ``module_register'' in ``%s'': %s\n",
+ WARNING ("Couldn't find symbol `module_register' in `%s': %s\n",
file, lt_dlerror ());
lt_dlclose (dlh);
return (-1);
pthread_mutex_unlock (&read_lock);
} /* void plugin_read_all */
-int plugin_flush_one (int timeout, const char *name)
-{
- int (*callback) (int);
- llentry_t *le;
- int status;
-
- if (list_flush == NULL)
- return (-1);
-
- le = llist_search (list_flush, name);
- if (le == NULL)
- return (-1);
- callback = (int (*) (int)) le->value;
-
- status = (*callback) (timeout);
-
- return (status);
-} /* int plugin_flush_ont */
-
-void plugin_flush_all (int timeout)
-{
- int (*callback) (int);
- llentry_t *le;
-
- if (list_flush == NULL)
- return;
-
- le = llist_head (list_flush);
- while (le != NULL)
- {
- callback = (int (*) (int)) le->value;
- le = le->next;
-
- (*callback) (timeout);
- }
-} /* void plugin_flush_all */
-
int plugin_flush (const char *plugin, int timeout, const char *identifier)
{
int (*callback) (int timeout, const char *identifier);
while (le != NULL)
{
if ((plugin != NULL)
- && (strcmp (plugin, le->key) != 0))
+ && (strcmp (plugin, le->key) != 0))
+ {
+ le = le->next;
continue;
+ }
callback = (int (*) (int, const char *)) le->value;
- le = le->next;
-
(*callback) (timeout, identifier);
+
+ le = le->next;
}
return (0);
} /* int plugin_flush */
} /* data_set_t *plugin_get_ds */
static int plugin_notification_meta_add (notification_t *n,
- const char *name,
- enum notification_meta_type_e type,
- const void *value)
+ const char *name,
+ enum notification_meta_type_e type,
+ const void *value)
{
notification_meta_t *meta;
notification_meta_t *tail;
{
if (meta->type == NM_TYPE_STRING)
plugin_notification_meta_add_string (dst, meta->name,
- meta->value_string);
+ meta->value_string);
else if (meta->type == NM_TYPE_SIGNED_INT)
plugin_notification_meta_add_signed_int (dst, meta->name,
- meta->value_signed_int);
+ meta->value_signed_int);
else if (meta->type == NM_TYPE_UNSIGNED_INT)
plugin_notification_meta_add_unsigned_int (dst, meta->name,
- meta->value_unsigned_int);
+ meta->value_unsigned_int);
else if (meta->type == NM_TYPE_DOUBLE)
plugin_notification_meta_add_double (dst, meta->name,
- meta->value_double);
+ meta->value_double);
else if (meta->type == NM_TYPE_BOOLEAN)
plugin_notification_meta_add_boolean (dst, meta->name,
- meta->value_boolean);
+ meta->value_boolean);
}
return (0);
void plugin_read_all (void);
void plugin_shutdown_all (void);
-void plugin_flush_all (int timeout);
-int plugin_flush_one (int timeout, const char *name);
int plugin_flush (const char *plugin, int timeout, const char *identifier);
/*
#define log_warn(...) WARNING ("postgresql: " __VA_ARGS__)
#define log_info(...) INFO ("postgresql: " __VA_ARGS__)
+#ifndef C_PSQL_DEFAULT_CONF
+# define C_PSQL_DEFAULT_CONF PKGDATADIR "/postgresql_default.conf"
+#endif
+
/* Appends the (parameter, value) pair to the string
* pointed to by 'buf' suitable to be used as argument
* for PQconnectdb(). If value equals NULL, the pair
C_PSQL_IS_UNIX_DOMAIN_SOCKET (host) ? "/.s.PGSQL." : ":", \
port
+typedef enum {
+ C_PSQL_PARAM_HOST = 1,
+ C_PSQL_PARAM_DB,
+ C_PSQL_PARAM_USER,
+} c_psql_param_t;
+
+typedef struct {
+ char *type;
+ char *type_instance;
+ int ds_type;
+} c_psql_col_t;
+
+typedef struct {
+ char *name;
+ char *query;
+
+ c_psql_param_t *params;
+ int params_num;
+
+ c_psql_col_t *cols;
+ int cols_num;
+
+ int min_pg_version;
+ int max_pg_version;
+} c_psql_query_t;
+
typedef struct {
PGconn *conn;
c_complain_t conn_complaint;
+ int proto_version;
+
+ int max_params_num;
+
/* user configuration */
+ c_psql_query_t **queries;
+ int queries_num;
+
char *host;
char *port;
char *database;
char *service;
} c_psql_database_t;
+static char *def_queries[] = {
+ "backends",
+ "transactions",
+ "queries",
+ "query_plans",
+ "table_states",
+ "disk_io",
+ "disk_usage"
+};
+static int def_queries_num = STATIC_ARRAY_SIZE (def_queries);
+
+static c_psql_query_t *queries = NULL;
+static int queries_num = 0;
+
static c_psql_database_t *databases = NULL;
static int databases_num = 0;
+static c_psql_query_t *c_psql_query_new (const char *name)
+{
+ c_psql_query_t *query;
+
+ ++queries_num;
+ if (NULL == (queries = (c_psql_query_t *)realloc (queries,
+ queries_num * sizeof (*queries)))) {
+ log_err ("Out of memory.");
+ exit (5);
+ }
+ query = queries + queries_num - 1;
+
+ query->name = sstrdup (name);
+ query->query = NULL;
+
+ query->params = NULL;
+ query->params_num = 0;
+
+ query->cols = NULL;
+ query->cols_num = 0;
+
+ query->min_pg_version = 0;
+ query->max_pg_version = INT_MAX;
+ return query;
+} /* c_psql_query_new */
+
+static void c_psql_query_delete (c_psql_query_t *query)
+{
+ int i;
+
+ sfree (query->name);
+ sfree (query->query);
+
+ sfree (query->params);
+ query->params_num = 0;
+
+ for (i = 0; i < query->cols_num; ++i) {
+ sfree (query->cols[i].type);
+ sfree (query->cols[i].type_instance);
+ }
+ sfree (query->cols);
+ query->cols_num = 0;
+ return;
+} /* c_psql_query_delete */
+
+static c_psql_query_t *c_psql_query_get (const char *name, int server_version)
+{
+ int i;
+
+ for (i = 0; i < queries_num; ++i)
+ if (0 == strcasecmp (name, queries[i].name)
+ && ((-1 == server_version)
+ || ((queries[i].min_pg_version <= server_version)
+ && (server_version <= queries[i].max_pg_version))))
+ return queries + i;
+ return NULL;
+} /* c_psql_query_get */
+
+static c_psql_database_t *c_psql_database_new (const char *name)
+{
+ c_psql_database_t *db;
+
+ ++databases_num;
+ if (NULL == (databases = (c_psql_database_t *)realloc (databases,
+ databases_num * sizeof (*databases)))) {
+ log_err ("Out of memory.");
+ exit (5);
+ }
+
+ db = databases + (databases_num - 1);
+
+ db->conn = NULL;
+
+ db->conn_complaint.last = 0;
+ db->conn_complaint.interval = 0;
+
+ db->proto_version = 0;
+
+ db->max_params_num = 0;
+
+ db->queries = NULL;
+ db->queries_num = 0;
+
+ db->database = sstrdup (name);
+ db->host = NULL;
+ db->port = NULL;
+ db->user = NULL;
+ db->password = NULL;
+
+ db->sslmode = NULL;
+
+ db->krbsrvname = NULL;
+
+ db->service = NULL;
+ return db;
+} /* c_psql_database_new */
+
+static void c_psql_database_delete (c_psql_database_t *db)
+{
+ PQfinish (db->conn);
+
+ sfree (db->queries);
+ db->queries_num = 0;
+
+ sfree (db->database);
+ sfree (db->host);
+ sfree (db->port);
+ sfree (db->user);
+ sfree (db->password);
+
+ sfree (db->sslmode);
+
+ sfree (db->krbsrvname);
+
+ sfree (db->service);
+ return;
+} /* c_psql_database_delete */
+
static void submit (const c_psql_database_t *db,
const char *type, const char *type_instance,
value_t *values, size_t values_len)
db->database, PQerrorMessage (db->conn));
return -1;
}
+
+ db->proto_version = PQprotocolVersion (db->conn);
+ if (3 > db->proto_version)
+ log_warn ("Protocol version %d does not support parameters.",
+ db->proto_version);
}
c_release (LOG_INFO, &db->conn_complaint,
return 0;
} /* c_psql_check_connection */
-static int c_psql_stat_database (c_psql_database_t *db)
+static PGresult *c_psql_exec_query_params (c_psql_database_t *db,
+ c_psql_query_t *query)
{
- const char *const query =
- "SELECT numbackends, xact_commit, xact_rollback "
- "FROM pg_stat_database "
- "WHERE datname = $1;";
-
- PGresult *res;
-
- int n;
-
- res = PQexecParams (db->conn, query, /* number of parameters */ 1,
- NULL, (const char *const *)&db->database, NULL, NULL,
- /* return text data */ 0);
-
- if (PGRES_TUPLES_OK != PQresultStatus (res)) {
- log_err ("Failed to execute SQL query: %s",
- PQerrorMessage (db->conn));
- log_info ("SQL query was: %s", query);
- PQclear (res);
- return -1;
- }
-
- n = PQntuples (res);
- if (1 < n) {
- log_warn ("pg_stat_database has more than one entry "
- "for database %s - ignoring additional results.",
- db->database);
- }
- else if (1 > n) {
- log_err ("pg_stat_database has no entry for database %s",
- db->database);
- PQclear (res);
- return -1;
+ char *params[db->max_params_num];
+ int i;
+
+ assert (db->max_params_num >= query->params_num);
+
+ for (i = 0; i < query->params_num; ++i) {
+ switch (query->params[i]) {
+ case C_PSQL_PARAM_HOST:
+ params[i] = C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host)
+ ? "localhost" : db->host;
+ break;
+ case C_PSQL_PARAM_DB:
+ params[i] = db->database;
+ break;
+ case C_PSQL_PARAM_USER:
+ params[i] = db->user;
+ break;
+ default:
+ assert (0);
+ }
}
- submit_gauge (db, "pg_numbackends", NULL, PQgetvalue (res, 0, 0));
+ return PQexecParams (db->conn, query->query, query->params_num, NULL,
+ (const char *const *)((0 == query->params_num) ? NULL : params),
+ NULL, NULL, /* return text data */ 0);
+} /* c_psql_exec_query_params */
- submit_counter (db, "pg_xact", "commit", PQgetvalue (res, 0, 1));
- submit_counter (db, "pg_xact", "rollback", PQgetvalue (res, 0, 2));
-
- PQclear (res);
- return 0;
-} /* c_psql_stat_database */
+static PGresult *c_psql_exec_query_noparams (c_psql_database_t *db,
+ c_psql_query_t *query)
+{
+ return PQexec (db->conn, query->query);
+} /* c_psql_exec_query_noparams */
-static int c_psql_stat_user_tables (c_psql_database_t *db)
+static int c_psql_exec_query (c_psql_database_t *db, int idx)
{
- const char *const query =
- "SELECT sum(seq_scan), sum(seq_tup_read), "
- "sum(idx_scan), sum(idx_tup_fetch), "
- "sum(n_tup_ins), sum(n_tup_upd), sum(n_tup_del), "
- "sum(n_tup_hot_upd), sum(n_live_tup), sum(n_dead_tup) "
- "FROM pg_stat_user_tables;";
+ c_psql_query_t *query;
+ PGresult *res;
- PGresult *res;
+ int rows, cols;
+ int i;
- int n;
+ if (idx >= db->queries_num)
+ return -1;
- res = PQexec (db->conn, query);
+ query = db->queries[idx];
- if (PGRES_TUPLES_OK != PQresultStatus (res)) {
- log_err ("Failed to execute SQL query: %s",
- PQerrorMessage (db->conn));
- log_info ("SQL query was: %s", query);
- PQclear (res);
+ if (3 <= db->proto_version)
+ res = c_psql_exec_query_params (db, query);
+ else if (0 == query->params_num)
+ res = c_psql_exec_query_noparams (db, query);
+ else {
+ log_err ("Connection to database \"%s\" does not support parameters "
+ "(protocol version %d) - cannot execute query \"%s\".",
+ db->database, db->proto_version, query->name);
return -1;
}
- n = PQntuples (res);
- assert (1 >= n);
-
- if (1 > n) /* no user tables */
- return 0;
-
- submit_counter (db, "pg_scan", "seq", PQgetvalue (res, 0, 0));
- submit_counter (db, "pg_scan", "seq_tup_read", PQgetvalue (res, 0, 1));
- submit_counter (db, "pg_scan", "idx", PQgetvalue (res, 0, 2));
- submit_counter (db, "pg_scan", "idx_tup_fetch", PQgetvalue (res, 0, 3));
-
- submit_counter (db, "pg_n_tup_c", "ins", PQgetvalue (res, 0, 4));
- submit_counter (db, "pg_n_tup_c", "upd", PQgetvalue (res, 0, 5));
- submit_counter (db, "pg_n_tup_c", "del", PQgetvalue (res, 0, 6));
- submit_counter (db, "pg_n_tup_c", "hot_upd", PQgetvalue (res, 0, 7));
-
- submit_gauge (db, "pg_n_tup_g", "live", PQgetvalue (res, 0, 8));
- submit_gauge (db, "pg_n_tup_g", "dead", PQgetvalue (res, 0, 9));
-
- PQclear (res);
- return 0;
-} /* c_psql_stat_user_tables */
-
-static int c_psql_statio_user_tables (c_psql_database_t *db)
-{
- const char *const query =
- "SELECT sum(heap_blks_read), sum(heap_blks_hit), "
- "sum(idx_blks_read), sum(idx_blks_hit), "
- "sum(toast_blks_read), sum(toast_blks_hit), "
- "sum(tidx_blks_read), sum(tidx_blks_hit) "
- "FROM pg_statio_user_tables;";
-
- PGresult *res;
-
- int n;
-
- res = PQexec (db->conn, query);
-
if (PGRES_TUPLES_OK != PQresultStatus (res)) {
log_err ("Failed to execute SQL query: %s",
PQerrorMessage (db->conn));
- log_info ("SQL query was: %s", query);
+ log_info ("SQL query was: %s", query->query);
PQclear (res);
return -1;
}
- n = PQntuples (res);
- assert (1 >= n);
-
- if (1 > n) /* no user tables */
+ rows = PQntuples (res);
+ if (1 > rows)
return 0;
- submit_counter (db, "pg_blks", "heap_read", PQgetvalue (res, 0, 0));
- submit_counter (db, "pg_blks", "heap_hit", PQgetvalue (res, 0, 1));
+ cols = PQnfields (res);
+ if (query->cols_num != cols) {
+ log_err ("SQL query returned wrong number of fields "
+ "(expected: %i, got: %i)", query->cols_num, cols);
+ log_info ("SQL query was: %s", query->query);
+ return -1;
+ }
- submit_counter (db, "pg_blks", "idx_read", PQgetvalue (res, 0, 2));
- submit_counter (db, "pg_blks", "idx_hit", PQgetvalue (res, 0, 3));
+ for (i = 0; i < rows; ++i) {
+ int j;
- submit_counter (db, "pg_blks", "toast_read", PQgetvalue (res, 0, 4));
- submit_counter (db, "pg_blks", "toast_hit", PQgetvalue (res, 0, 5));
+ for (j = 0; j < cols; ++j) {
+ c_psql_col_t col = query->cols[j];
- submit_counter (db, "pg_blks", "tidx_read", PQgetvalue (res, 0, 6));
- submit_counter (db, "pg_blks", "tidx_hit", PQgetvalue (res, 0, 7));
+ char *value = PQgetvalue (res, i, j);
- PQclear (res);
+ if (col.ds_type == DS_TYPE_COUNTER)
+ submit_counter (db, col.type, col.type_instance, value);
+ else if (col.ds_type == DS_TYPE_GAUGE)
+ submit_gauge (db, col.type, col.type_instance, value);
+ }
+ }
return 0;
-} /* c_psql_statio_user_tables */
+} /* c_psql_exec_query */
static int c_psql_read (void)
{
for (i = 0; i < databases_num; ++i) {
c_psql_database_t *db = databases + i;
+ int j;
+
assert (NULL != db->database);
if (0 != c_psql_check_connection (db))
continue;
- c_psql_stat_database (db);
- c_psql_stat_user_tables (db);
- c_psql_statio_user_tables (db);
+ for (j = 0; j < db->queries_num; ++j)
+ c_psql_exec_query (db, j);
++success;
}
for (i = 0; i < databases_num; ++i) {
c_psql_database_t *db = databases + i;
-
- PQfinish (db->conn);
-
- sfree (db->database);
- sfree (db->host);
- sfree (db->port);
- sfree (db->user);
- sfree (db->password);
-
- sfree (db->sslmode);
-
- sfree (db->krbsrvname);
-
- sfree (db->service);
+ c_psql_database_delete (db);
}
sfree (databases);
databases_num = 0;
+
+ for (i = 0; i < queries_num; ++i) {
+ c_psql_query_t *query = queries + i;
+ c_psql_query_delete (query);
+ }
+
+ sfree (queries);
+ queries_num = 0;
return 0;
} /* c_psql_shutdown */
if ((NULL == databases) || (0 == databases_num))
return 0;
+ for (i = 0; i < queries_num; ++i) {
+ c_psql_query_t *query = queries + i;
+ int j;
+
+ for (j = 0; j < query->cols_num; ++j) {
+ c_psql_col_t *col = query->cols + j;
+ const data_set_t *ds;
+
+ ds = plugin_get_ds (col->type);
+ if (NULL == ds) {
+ log_err ("Column: Unknown type \"%s\".", col->type);
+ c_psql_shutdown ();
+ return -1;
+ }
+
+ if (1 != ds->ds_num) {
+ log_err ("Column: Invalid type \"%s\" - types defining "
+ "one data source are supported only (got: %i).",
+ col->type, ds->ds_num);
+ c_psql_shutdown ();
+ return -1;
+ }
+
+ col->ds_type = ds->ds[0].type;
+ }
+ }
+
for (i = 0; i < databases_num; ++i) {
c_psql_database_t *db = databases + i;
char *server_host;
int server_version;
+ int j;
+
status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
if (0 < status) {
buf += status;
if (0 != c_psql_check_connection (db))
continue;
+ db->proto_version = PQprotocolVersion (db->conn);
+
server_host = PQhost (db->conn);
server_version = PQserverVersion (db->conn);
log_info ("Sucessfully connected to database %s (user %s) "
"at server %s%s%s (server version: %d.%d.%d, "
"protocol version: %d, pid: %d)",
PQdb (db->conn), PQuser (db->conn),
- C_PSQL_SOCKET3(server_host, PQport (db->conn)),
+ C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
C_PSQL_SERVER_VERSION3 (server_version),
- PQprotocolVersion (db->conn), PQbackendPID (db->conn));
+ db->proto_version, PQbackendPID (db->conn));
+
+ if (3 > db->proto_version)
+ log_warn ("Protocol version %d does not support parameters.",
+ db->proto_version);
+
+ /* Now that we know the PostgreSQL server version, we can get the
+ * right version of each query definition. */
+ for (j = 0; j < db->queries_num; ++j) {
+ c_psql_query_t *tmp;
+
+ tmp = c_psql_query_get (db->queries[j]->name, server_version);
+
+ if (tmp == db->queries[j])
+ continue;
+
+ if (NULL == tmp) {
+ log_err ("Query \"%s\" not found for server version %i - "
+ "please check your configuration.",
+ db->queries[j]->name, server_version);
+
+ if (db->queries_num - j - 1 > 0)
+ memmove (db->queries + j, db->queries + j + 1,
+ (db->queries_num - j - 1) * sizeof (*db->queries));
+
+ --db->queries_num;
+ --j;
+ continue;
+ }
+
+ db->queries[j] = tmp;
+ }
}
plugin_register_read ("postgresql", c_psql_read);
return 0;
} /* c_psql_init */
-static int config_set (char *name, char **var, const oconfig_item_t *ci)
+static int config_set_s (char *name, char **var, const oconfig_item_t *ci)
{
if ((0 != ci->children_num) || (1 != ci->values_num)
|| (OCONFIG_TYPE_STRING != ci->values[0].type)) {
sfree (*var);
*var = sstrdup (ci->values[0].value.string);
return 0;
-} /* config_set */
+} /* config_set_s */
-static int c_psql_config_database (oconfig_item_t *ci)
+static int config_set_i (char *name, int *var, const oconfig_item_t *ci)
{
- c_psql_database_t *db;
+ if ((0 != ci->children_num) || (1 != ci->values_num)
+ || (OCONFIG_TYPE_NUMBER != ci->values[0].type)) {
+ log_err ("%s expects a single number argument.", name);
+ return 1;
+ }
- int i;
+ *var = (int)ci->values[0].value.number;
+ return 0;
+} /* config_set_i */
- if ((1 != ci->values_num)
+static int config_set_param (c_psql_query_t *query, const oconfig_item_t *ci)
+{
+ c_psql_param_t param;
+ char *param_str;
+
+ if ((0 != ci->children_num) || (1 != ci->values_num)
|| (OCONFIG_TYPE_STRING != ci->values[0].type)) {
- log_err ("<Database> expects a single string argument.");
+ log_err ("Param expects a single string argument.");
return 1;
}
- ++databases_num;
- if (NULL == (databases = (c_psql_database_t *)realloc (databases,
- databases_num * sizeof (*databases)))) {
+ param_str = ci->values[0].value.string;
+ if (0 == strcasecmp (param_str, "hostname"))
+ param = C_PSQL_PARAM_HOST;
+ else if (0 == strcasecmp (param_str, "database"))
+ param = C_PSQL_PARAM_DB;
+ else if (0 == strcasecmp (param_str, "username"))
+ param = C_PSQL_PARAM_USER;
+ else {
+ log_err ("Invalid parameter \"%s\".", param_str);
+ return 1;
+ }
+
+ ++query->params_num;
+ if (NULL == (query->params = (c_psql_param_t *)realloc (query->params,
+ query->params_num * sizeof (*query->params)))) {
log_err ("Out of memory.");
exit (5);
}
- db = databases + (databases_num - 1);
+ query->params[query->params_num - 1] = param;
+ return 0;
+} /* config_set_param */
- db->conn = NULL;
+static int config_set_column (c_psql_query_t *query, const oconfig_item_t *ci)
+{
+ c_psql_col_t *col;
- db->conn_complaint.last = 0;
- db->conn_complaint.interval = 0;
+ int i;
- db->database = sstrdup (ci->values[0].value.string);
- db->host = NULL;
- db->port = NULL;
- db->user = NULL;
- db->password = NULL;
+ if ((0 != ci->children_num)
+ || (1 > ci->values_num) || (2 < ci->values_num)) {
+ log_err ("Column expects either one or two arguments.");
+ return 1;
+ }
- db->sslmode = NULL;
+ for (i = 0; i < ci->values_num; ++i) {
+ if (OCONFIG_TYPE_STRING != ci->values[i].type) {
+ log_err ("Column expects either one or two string arguments.");
+ return 1;
+ }
+ }
- db->krbsrvname = NULL;
+ ++query->cols_num;
+ if (NULL == (query->cols = (c_psql_col_t *)realloc (query->cols,
+ query->cols_num * sizeof (*query->cols)))) {
+ log_err ("Out of memory.");
+ exit (5);
+ }
- db->service = NULL;
+ col = query->cols + query->cols_num - 1;
+
+ col->ds_type = -1;
+
+ col->type = sstrdup (ci->values[0].value.string);
+ col->type_instance = (2 == ci->values_num)
+ ? sstrdup (ci->values[1].value.string) : NULL;
+ return 0;
+} /* config_set_column */
+
+static int set_query (c_psql_database_t *db, const char *name)
+{
+ c_psql_query_t *query;
+
+ query = c_psql_query_get (name, -1);
+ if (NULL == query) {
+ log_err ("Query \"%s\" not found - please check your configuration.",
+ name);
+ return 1;
+ }
+
+ ++db->queries_num;
+ if (NULL == (db->queries = (c_psql_query_t **)realloc (db->queries,
+ db->queries_num * sizeof (*db->queries)))) {
+ log_err ("Out of memory.");
+ exit (5);
+ }
+
+ if (query->params_num > db->max_params_num)
+ db->max_params_num = query->params_num;
+
+ db->queries[db->queries_num - 1] = query;
+ return 0;
+} /* set_query */
+
+static int config_set_query (c_psql_database_t *db, const oconfig_item_t *ci)
+{
+ if ((0 != ci->children_num) || (1 != ci->values_num)
+ || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+ log_err ("Query expects a single string argument.");
+ return 1;
+ }
+ return set_query (db, ci->values[0].value.string);
+} /* config_set_query */
+
+static int c_psql_config_query (oconfig_item_t *ci)
+{
+ c_psql_query_t *query;
+
+ int i;
+
+ if ((1 != ci->values_num)
+ || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+ log_err ("<Query> expects a single string argument.");
+ return 1;
+ }
+
+ query = c_psql_query_new (ci->values[0].value.string);
+
+ for (i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *c = ci->children + i;
+
+ if (0 == strcasecmp (c->key, "Query"))
+ config_set_s ("Query", &query->query, c);
+ else if (0 == strcasecmp (c->key, "Param"))
+ config_set_param (query, c);
+ else if (0 == strcasecmp (c->key, "Column"))
+ config_set_column (query, c);
+ else if (0 == strcasecmp (c->key, "MinPGVersion"))
+ config_set_i ("MinPGVersion", &query->min_pg_version, c);
+ else if (0 == strcasecmp (c->key, "MaxPGVersion"))
+ config_set_i ("MaxPGVersion", &query->max_pg_version, c);
+ else
+ log_warn ("Ignoring unknown config key \"%s\".", c->key);
+ }
+
+ for (i = 0; i < queries_num - 1; ++i) {
+ c_psql_query_t *q = queries + i;
+
+ if ((0 == strcasecmp (q->name, query->name))
+ && (q->min_pg_version <= query->max_pg_version)
+ && (query->min_pg_version <= q->max_pg_version)) {
+ log_err ("Ignoring redefinition (with overlapping version ranges) "
+ "of query \"%s\".", query->name);
+ c_psql_query_delete (query);
+ --queries_num;
+ return 1;
+ }
+ }
+
+ if (query->min_pg_version > query->max_pg_version) {
+ log_err ("Query \"%s\": MinPGVersion > MaxPGVersion.",
+ query->name);
+ c_psql_query_delete (query);
+ --queries_num;
+ return 1;
+ }
+
+ if (NULL == query->query) {
+ log_err ("Query \"%s\" does not include an SQL query string - "
+ "please check your configuration.", query->name);
+ c_psql_query_delete (query);
+ --queries_num;
+ return 1;
+ }
+ return 0;
+} /* c_psql_config_query */
+
+static int c_psql_config_database (oconfig_item_t *ci)
+{
+ c_psql_database_t *db;
+
+ int i;
+
+ if ((1 != ci->values_num)
+ || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+ log_err ("<Database> expects a single string argument.");
+ return 1;
+ }
+
+ db = c_psql_database_new (ci->values[0].value.string);
for (i = 0; i < ci->children_num; ++i) {
oconfig_item_t *c = ci->children + i;
if (0 == strcasecmp (c->key, "Host"))
- config_set ("Host", &db->host, c);
+ config_set_s ("Host", &db->host, c);
else if (0 == strcasecmp (c->key, "Port"))
- config_set ("Port", &db->port, c);
+ config_set_s ("Port", &db->port, c);
else if (0 == strcasecmp (c->key, "User"))
- config_set ("User", &db->user, c);
+ config_set_s ("User", &db->user, c);
else if (0 == strcasecmp (c->key, "Password"))
- config_set ("Password", &db->password, c);
+ config_set_s ("Password", &db->password, c);
else if (0 == strcasecmp (c->key, "SSLMode"))
- config_set ("SSLMode", &db->sslmode, c);
+ config_set_s ("SSLMode", &db->sslmode, c);
else if (0 == strcasecmp (c->key, "KRBSrvName"))
- config_set ("KRBSrvName", &db->krbsrvname, c);
+ config_set_s ("KRBSrvName", &db->krbsrvname, c);
else if (0 == strcasecmp (c->key, "Service"))
- config_set ("Service", &db->service, c);
+ config_set_s ("Service", &db->service, c);
+ else if (0 == strcasecmp (c->key, "Query"))
+ config_set_query (db, c);
else
log_warn ("Ignoring unknown config key \"%s\".", c->key);
}
+
+ if (NULL == db->queries) {
+ for (i = 0; i < def_queries_num; ++i)
+ set_query (db, def_queries[i]);
+ }
return 0;
}
static int c_psql_config (oconfig_item_t *ci)
{
+ static int have_def_config = 0;
+
int i;
+ if (0 == have_def_config) {
+ oconfig_item_t *c;
+
+ have_def_config = 1;
+
+ c = oconfig_parse_file (C_PSQL_DEFAULT_CONF);
+ if (NULL == c)
+ log_err ("Failed to read default config ("C_PSQL_DEFAULT_CONF").");
+ else
+ c_psql_config (c);
+
+ if (NULL == queries)
+ log_err ("Default config ("C_PSQL_DEFAULT_CONF") did not define "
+ "any queries - please check your installation.");
+ }
+
for (i = 0; i < ci->children_num; ++i) {
oconfig_item_t *c = ci->children + i;
- if (0 == strcasecmp (c->key, "Database"))
+ if (0 == strcasecmp (c->key, "Query"))
+ c_psql_config_query (c);
+ else if (0 == strcasecmp (c->key, "Database"))
c_psql_config_database (c);
else
log_warn ("Ignoring unknown config key \"%s\".", c->key);
--- /dev/null
+# Pre-defined queries of collectd's postgresql plugin.
+
+<Query backends>
+ Query "SELECT count(*) \
+ FROM pg_stat_activity \
+ WHERE datname = $1;"
+
+ Param database
+
+ Column pg_numbackends
+</Query>
+
+<Query transactions>
+ Query "SELECT xact_commit, xact_rollback \
+ FROM pg_stat_database \
+ WHERE datname = $1;"
+
+ Param database
+
+ Column pg_xact commit
+ Column pg_xact rollback
+</Query>
+
+<Query queries>
+ Query "SELECT sum(n_tup_ins), sum(n_tup_upd), sum(n_tup_del) \
+ FROM pg_stat_user_tables;"
+
+ Column pg_n_tup_c ins
+ Column pg_n_tup_c upd
+ Column pg_n_tup_c del
+
+ MaxPGVersion 80299
+</Query>
+
+<Query queries>
+ Query "SELECT sum(n_tup_ins), sum(n_tup_upd), sum(n_tup_del), \
+ sum(n_tup_hot_upd) \
+ FROM pg_stat_user_tables;"
+
+ Column pg_n_tup_c ins
+ Column pg_n_tup_c upd
+ Column pg_n_tup_c del
+ Column pg_n_tup_c hot_upd
+
+ MinPGVersion 80300
+</Query>
+
+<Query query_plans>
+ Query "SELECT sum(seq_scan), sum(seq_tup_read), \
+ sum(idx_scan), sum(idx_tup_fetch) \
+ FROM pg_stat_user_tables;"
+
+ Column pg_scan seq
+ Column pg_scan seq_tup_read
+ Column pg_scan idx
+ Column pg_scan idx_tup_fetch
+</Query>
+
+<Query table_states>
+ Query "SELECT sum(n_live_tup), sum(n_dead_tup) \
+ FROM pg_stat_user_tables;"
+
+ Column pg_n_tup_g live
+ Column pg_n_tup_g dead
+
+ MinPGVersion 80300
+</Query>
+
+<Query disk_io>
+ Query "SELECT sum(heap_blks_read), sum(heap_blks_hit), \
+ sum(idx_blks_read), sum(idx_blks_hit), \
+ sum(toast_blks_read), sum(toast_blks_hit), \
+ sum(tidx_blks_read), sum(tidx_blks_hit) \
+ FROM pg_statio_user_tables;"
+
+ Column pg_blks heap_read
+ Column pg_blks heap_hit
+ Column pg_blks idx_read
+ Column pg_blks idx_hit
+ Column pg_blks toast_read
+ Column pg_blks toast_hit
+ Column pg_blks tidx_read
+ Column pg_blks tidx_hit
+</Query>
+
+<Query disk_usage>
+ Query "SELECT pg_database_size($1);"
+
+ Param database
+
+ Column pg_db_size
+</Query>
+
# endif
/* #endif KERNEL_LINUX */
-#elif HAVE_KVM_H
+#elif HAVE_LIBKVM_GETPROCS
# include <kvm.h>
# include <sys/user.h>
# include <sys/proc.h>
# if HAVE_SYS_SYSCTL_H
# include <sys/sysctl.h>
# endif
-/* #endif HAVE_KVM_H */
+/* #endif HAVE_LIBKVM_GETPROCS */
#else
# error "No applicable input method."
#elif KERNEL_LINUX
static long pagesize_g;
-#endif /* KERNEL_LINUX */
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKVM_GETPROCS
+/* no global variables */
+#endif /* HAVE_LIBKVM_GETPROCS */
/* put name of process from config to list_head_g tree
list_head_g is a list of 'procstat_t' structs with
pagesize_g = sysconf(_SC_PAGESIZE);
DEBUG ("pagesize_g = %li; CONFIG_HZ = %i;",
pagesize_g, CONFIG_HZ);
-#endif /* KERNEL_LINUX */
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKVM_GETPROCS
+/* no initialization */
+#endif /* HAVE_LIBKVM_GETPROCS */
return (0);
} /* int ps_init */
* There's only zombie tasks, which are
* handled above. */
default:
- WARNING ("Unknown thread status: %s",
+ WARNING ("Unknown thread status: %i",
thread_data.run_state);
break;
} /* switch (thread_data.run_state) */
ps_submit_proc_list (ps_ptr);
/* #endif KERNEL_LINUX */
-#elif HAVE_LIBKVM
+#elif HAVE_LIBKVM_GETPROCS
int running = 0;
int sleeping = 0;
int zombies = 0;
for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
ps_submit_proc_list (ps_ptr);
-#endif /* HAVE_LIBKVM */
+#endif /* HAVE_LIBKVM_GETPROCS */
return (0);
} /* int ps_read */
+++ /dev/null
-/**
- * collectd - src/qmail.c
- * Copyright (C) 2008 Alessandro Iurlano
- *
- * 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:
- * Alessandro Iurlano <alessandro.iurlano at gmail.com>
- **/
-
-#include "collectd.h"
-#include "common.h"
-#include "plugin.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-
-#define DEFAULT_BASE_DIR "/var/qmail"
-
-static char *qmail_base_dir;
-
-static const char *config_keys[] =
-{
- "QmailDir"
-};
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
-
-static void qmail_submit (const char *plugin_instance, gauge_t value)
-{
- value_t values[1];
- value_list_t vl = VALUE_LIST_INIT;
-
- values[0].gauge = value;
-
- vl.values = values;
- vl.values_len = STATIC_ARRAY_SIZE (values);
- vl.time = time (NULL);
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
- sstrncpy (vl.type, "gauge", sizeof (vl.type));
- sstrncpy (vl.plugin, "qmail", sizeof (vl.plugin));
- sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
-
- plugin_dispatch_values (&vl);
-} /* void qmail_submit */
-
-static int count_files_in_subtree (const char *path, int depth)
-{
-#if NAME_MAX < 1024
-# define DIRENT_BUFFER_SIZE (sizeof (struct dirent) + 1024 + 1)
-#else
-# define DIRENT_BUFFER_SIZE (sizeof (struct dirent) + NAME_MAX + 1)
-#endif
-
- DIR *dh;
- struct dirent *de;
- char dirent_buffer[DIRENT_BUFFER_SIZE];
- int status;
-
- char **subdirs;
- size_t subdirs_num;
-
- int count;
- int i;
-
- dh = opendir (path);
- if (dh == NULL)
- {
- ERROR ("qmail plugin: opendir (%s) failed.", path);
- return (-1);
- }
-
- subdirs = NULL;
- subdirs_num = 0;
-
- count = 0;
- while (42)
- {
- char abs_path[PATH_MAX];
- struct stat statbuf;
-
- de = NULL;
- status = readdir_r (dh, (struct dirent *) dirent_buffer, &de);
- if (status != 0)
- {
- char errbuf[4096];
- ERROR ("qmail plugin: readdir_r failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- closedir (dh);
- return (-1);
- }
- else if (de == NULL)
- {
- /* end of directory */
- break;
- }
-
- if (de->d_name[0] == '.')
- continue;
-
- ssnprintf (abs_path, sizeof (abs_path), "%s/%s", path, de->d_name);
-
- status = lstat (abs_path, &statbuf);
- if (status != 0)
- {
- ERROR ("qmail plugin: stat (%s) failed.", abs_path);
- continue;
- }
-
- if (S_ISREG (statbuf.st_mode))
- {
- count++;
- }
- else if (S_ISDIR (statbuf.st_mode))
- {
- char **temp;
-
- temp = (char **) realloc (subdirs, sizeof (char *) * (subdirs_num + 1));
- if (temp == NULL)
- {
- ERROR ("qmail plugin: realloc failed.");
- continue;
- }
- subdirs = temp;
-
- subdirs[subdirs_num] = strdup (abs_path);
- if (subdirs[subdirs_num] == NULL)
- {
- ERROR ("qmail plugin: strdup failed.");
- continue;
- }
- subdirs_num++;
- }
- }
-
- closedir (dh);
- dh = NULL;
-
- if (depth > 0)
- {
- for (i = 0; i < subdirs_num; i++)
- {
- status = count_files_in_subtree (subdirs[i], depth - 1);
- if (status > 0)
- count += status;
- }
- }
-
- for (i = 0; i < subdirs_num; i++)
- {
- sfree (subdirs[i]);
- }
- sfree (subdirs);
-
- return (count);
-} /* int count_files_in_subtree */
-
-static int read_queue_length (const char *queue_name, const char *path)
-{
- int64_t num_files;
-
- num_files = count_files_in_subtree (path, /* depth = */ 1);
- if (num_files < 0)
- {
- ERROR ("qmail plugin: Counting files in `%s' failed.", path);
- return (-1);
- }
-
- qmail_submit (queue_name, (gauge_t) num_files);
- return (0);
-} /* int read_queue_length */
-
-static int queue_len_read (void)
-{
- char path[4096];
- int success;
- int status;
-
- success = 0;
-
- ssnprintf (path, sizeof (path), "%s/queue/mess",
- (qmail_base_dir != NULL)
- ? qmail_base_dir
- : DEFAULT_BASE_DIR);
-
- status = read_queue_length ("messages", path);
- if (status == 0)
- success++;
-
- ssnprintf (path, sizeof (path), "%s/queue/todo",
- (qmail_base_dir != NULL)
- ? qmail_base_dir
- : DEFAULT_BASE_DIR);
-
- status = read_queue_length ("todo", path);
- if (status == 0)
- success++;
-
- if (success > 0)
- return 0;
- return (-1);
-} /* int queue_len_read */
-
-static int qmail_config (const char *key, const char *val)
-{
- if (strcasecmp ("QmailDir", key) == 0)
- {
- size_t qmail_base_dir_len;
-
- sfree (qmail_base_dir);
- qmail_base_dir = strdup(val);
- if (qmail_base_dir == NULL)
- {
- ERROR ("qmail plugin: strdup failed.");
- return (1);
- }
-
- qmail_base_dir_len = strlen (qmail_base_dir);
- while ((qmail_base_dir_len > 0)
- && (qmail_base_dir[qmail_base_dir_len - 1] == '/'))
- {
- qmail_base_dir[qmail_base_dir_len - 1] = 0;
- qmail_base_dir_len--;
- }
-
- if (qmail_base_dir_len == 0)
- {
- ERROR ("qmail plugin: QmailDir is invalid.");
- sfree (qmail_base_dir);
- qmail_base_dir = NULL;
- return (1);
- }
- }
- else
- {
- return (-1);
- }
-
- return (0);
-} /* int qmail_config */
-
-void module_register (void)
-{
- plugin_register_config ("qmail", qmail_config,
- config_keys, config_keys_num);
- plugin_register_read ("qmail", queue_len_read);
-} /* void module_register */
-
-/*
- * vim: set sw=2 sts=2 et :
- */
enum
{
FLAG_NONE = 0x00,
- FLAG_QUEUED = 0x01
+ FLAG_QUEUED = 0x01,
+ FLAG_FLUSHQ = 0x02
} flags;
};
typedef struct rrd_cache_s rrd_cache_t;
"HeartBeat",
"RRARows",
"RRATimespan",
- "XFF"
+ "XFF",
+ "WritesPerSecond"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
/* If datadir is zero, the daemon's basedir is used. If stepsize or heartbeat
* is zero a default, depending on the `interval' member of the value list is
* being used. */
-static char *datadir = NULL;
+static char *datadir = NULL;
+static double write_rate = 0.0;
static rrdcreate_config_t rrdcreate_config =
{
/* stepsize = */ 0,
static rrd_queue_t *queue_head = NULL;
static rrd_queue_t *queue_tail = NULL;
+static rrd_queue_t *flushq_head = NULL;
+static rrd_queue_t *flushq_tail = NULL;
static pthread_t queue_thread = 0;
static pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
static void *rrd_queue_thread (void *data)
{
+ struct timeval tv_next_update;
+ struct timeval tv_now;
+
+ gettimeofday (&tv_next_update, /* timezone = */ NULL);
+
while (42)
{
rrd_queue_t *queue_entry;
int values_num;
int i;
- /* XXX: If you need to lock both, cache_lock and queue_lock, at
- * the same time, ALWAYS lock `cache_lock' first! */
-
- /* wait until an entry is available */
- pthread_mutex_lock (&queue_lock);
- while ((queue_head == NULL) && (do_shutdown == 0))
- pthread_cond_wait (&queue_cond, &queue_lock);
-
- /* We're in the shutdown phase */
- if (queue_head == NULL)
- {
- pthread_mutex_unlock (&queue_lock);
- break;
- }
-
- /* Dequeue the first entry */
- queue_entry = queue_head;
- if (queue_head == queue_tail)
- queue_head = queue_tail = NULL;
- else
- queue_head = queue_head->next;
+ pthread_mutex_lock (&queue_lock);
+ /* Wait for values to arrive */
+ while (true)
+ {
+ struct timespec ts_wait;
+ int status;
+
+ while ((flushq_head == NULL) && (queue_head == NULL)
+ && (do_shutdown == 0))
+ pthread_cond_wait (&queue_cond, &queue_lock);
+
+ if ((flushq_head == NULL) && (queue_head == NULL))
+ break;
+
+ /* Don't delay if there's something to flush */
+ if (flushq_head != NULL)
+ break;
+
+ /* Don't delay if we're shutting down */
+ if (do_shutdown != 0)
+ break;
+
+ /* Don't delay if no delay was configured. */
+ if (write_rate <= 0.0)
+ break;
+
+ gettimeofday (&tv_now, /* timezone = */ NULL);
+ status = timeval_sub_timespec (&tv_next_update, &tv_now,
+ &ts_wait);
+ /* We're good to go */
+ if (status != 0)
+ break;
+
+ /* We're supposed to wait a bit with this update, so we'll
+ * wait for the next addition to the queue or to the end of
+ * the wait period - whichever comes first. */
+ ts_wait.tv_sec = tv_next_update.tv_sec;
+ ts_wait.tv_nsec = 1000 * tv_next_update.tv_usec;
+
+ status = pthread_cond_timedwait (&queue_cond, &queue_lock,
+ &ts_wait);
+ if (status == ETIMEDOUT)
+ break;
+ } /* while (true) */
+
+ /* XXX: If you need to lock both, cache_lock and queue_lock, at
+ * the same time, ALWAYS lock `cache_lock' first! */
+
+ /* We're in the shutdown phase */
+ if ((flushq_head == NULL) && (queue_head == NULL))
+ {
+ pthread_mutex_unlock (&queue_lock);
+ break;
+ }
+
+ if (flushq_head != NULL)
+ {
+ /* Dequeue the first flush entry */
+ queue_entry = flushq_head;
+ if (flushq_head == flushq_tail)
+ flushq_head = flushq_tail = NULL;
+ else
+ flushq_head = flushq_head->next;
+ }
+ else /* if (queue_head != NULL) */
+ {
+ /* Dequeue the first regular entry */
+ queue_entry = queue_head;
+ if (queue_head == queue_tail)
+ queue_head = queue_tail = NULL;
+ else
+ queue_head = queue_head->next;
+ }
/* Unlock the queue again */
pthread_mutex_unlock (&queue_lock);
pthread_mutex_unlock (&cache_lock);
+ /* Update `tv_next_update' */
+ if (write_rate > 0.0)
+ {
+ gettimeofday (&tv_now, /* timezone = */ NULL);
+ tv_next_update.tv_sec = tv_now.tv_sec;
+ tv_next_update.tv_usec = tv_now.tv_usec
+ + ((suseconds_t) (1000000 * write_rate));
+ while (tv_next_update.tv_usec > 1000000)
+ {
+ tv_next_update.tv_sec++;
+ tv_next_update.tv_usec -= 1000000;
+ }
+ }
+
/* Write the values to the RRD-file */
srrd_update (queue_entry->filename, NULL,
values_num, (const char **)values);
return ((void *) 0);
} /* void *rrd_queue_thread */
-static int rrd_queue_cache_entry (const char *filename, rrd_queue_dir_t dir)
+static int rrd_queue_enqueue (const char *filename,
+ rrd_queue_t **head, rrd_queue_t **tail)
{
rrd_queue_t *queue_entry;
queue_entry->next = NULL;
pthread_mutex_lock (&queue_lock);
- if (dir == QUEUE_INSERT_FRONT)
- {
- queue_entry->next = queue_head;
- queue_head = queue_entry;
- if (queue_tail == NULL)
- queue_tail = queue_head;
- }
- else /* (dir == QUEUE_INSERT_BACK) */
- {
- if (queue_tail == NULL)
- queue_head = queue_entry;
- else
- queue_tail->next = queue_entry;
- queue_tail = queue_entry;
- }
+
+ if (*tail == NULL)
+ *head = queue_entry;
+ else
+ (*tail)->next = queue_entry;
+ *tail = queue_entry;
+
pthread_cond_signal (&queue_cond);
pthread_mutex_unlock (&queue_lock);
- DEBUG ("rrdtool plugin: Put `%s' into the update queue", filename);
-
return (0);
-} /* int rrd_queue_cache_entry */
+} /* int rrd_queue_enqueue */
-static int rrd_queue_move_to_front (const char *filename)
+static int rrd_queue_dequeue (const char *filename,
+ rrd_queue_t **head, rrd_queue_t **tail)
{
rrd_queue_t *this;
rrd_queue_t *prev;
- this = NULL;
- prev = NULL;
pthread_mutex_lock (&queue_lock);
- for (this = queue_head; this != NULL; this = this->next)
+
+ prev = NULL;
+ this = *head;
+
+ while (this != NULL)
{
if (strcmp (this->filename, filename) == 0)
break;
+
prev = this;
+ this = this->next;
}
- /* Check if we found the entry and if it is NOT the first entry. */
- if ((this != NULL) && (prev != NULL))
+ if (this == NULL)
{
- prev->next = this->next;
- this->next = queue_head;
- queue_head = this;
+ pthread_mutex_unlock (&queue_lock);
+ return (-1);
}
+
+ if (prev == NULL)
+ *head = this->next;
+ else
+ prev->next = this->next;
+
+ if (this->next == NULL)
+ *tail = prev;
+
pthread_mutex_unlock (&queue_lock);
+ sfree (this->filename);
+ sfree (this);
+
return (0);
-} /* int rrd_queue_move_to_front */
+} /* int rrd_queue_dequeue */
static void rrd_cache_flush (int timeout)
{
iter = c_avl_get_iterator (cache);
while (c_avl_iterator_next (iter, (void *) &key, (void *) &rc) == 0)
{
- if (rc->flags == FLAG_QUEUED)
+ if (rc->flags != FLAG_NONE)
continue;
else if ((now - rc->first_value) < timeout)
continue;
else if (rc->values_num > 0)
{
- if (rrd_queue_cache_entry (key, QUEUE_INSERT_BACK) == 0)
+ int status;
+
+ status = rrd_queue_enqueue (key, &queue_head, &queue_tail);
+ if (status == 0)
rc->flags = FLAG_QUEUED;
}
else /* ancient and no values -> waste of memory */
now = time (NULL);
if (datadir == NULL)
- snprintf (key, sizeof (key), "%s.rrd",
- identifier);
+ snprintf (key, sizeof (key), "%s.rrd",
+ identifier);
else
- snprintf (key, sizeof (key), "%s/%s.rrd",
- datadir, identifier);
+ snprintf (key, sizeof (key), "%s/%s.rrd",
+ datadir, identifier);
key[sizeof (key) - 1] = 0;
status = c_avl_get (cache, key, (void *) &rc);
if (status != 0)
{
WARNING ("rrdtool plugin: rrd_cache_flush_identifier: "
- "c_avl_get (%s) failed. Does that file really exist?",
- key);
+ "c_avl_get (%s) failed. Does that file really exist?",
+ key);
return (status);
}
- if (rc->flags == FLAG_QUEUED)
- status = rrd_queue_move_to_front (key);
+ if (rc->flags == FLAG_FLUSHQ)
+ {
+ status = 0;
+ }
+ else if (rc->flags == FLAG_QUEUED)
+ {
+ rrd_queue_dequeue (key, &queue_head, &queue_tail);
+ status = rrd_queue_enqueue (key, &flushq_head, &flushq_tail);
+ if (status == 0)
+ rc->flags = FLAG_FLUSHQ;
+ }
else if ((now - rc->first_value) < timeout)
+ {
status = 0;
+ }
else if (rc->values_num > 0)
{
- status = rrd_queue_cache_entry (key, QUEUE_INSERT_FRONT);
+ status = rrd_queue_enqueue (key, &flushq_head, &flushq_tail);
if (status == 0)
- rc->flags = FLAG_QUEUED;
+ rc->flags = FLAG_FLUSHQ;
}
return (status);
{
/* XXX: If you need to lock both, cache_lock and queue_lock, at
* the same time, ALWAYS lock `cache_lock' first! */
- if (rc->flags != FLAG_QUEUED)
+ if (rc->flags == FLAG_NONE)
{
- if (rrd_queue_cache_entry (filename, QUEUE_INSERT_BACK) == 0)
+ int status;
+
+ status = rrd_queue_enqueue (filename, &queue_head, &queue_tail);
+ if (status == 0)
rc->flags = FLAG_QUEUED;
}
else
((time (NULL) - cache_flush_last) > cache_flush_timeout))
rrd_cache_flush (cache_flush_timeout);
-
pthread_mutex_unlock (&cache_lock);
return (0);
}
rrdcreate_config.xff = tmp;
}
+ else if (strcasecmp ("WritesPerSecond", key) == 0)
+ {
+ double wps = atof (value);
+
+ if (wps < 0.0)
+ {
+ fprintf (stderr, "rrdtool: `WritesPerSecond' must be "
+ "greater than or equal to zero.");
+ return (1);
+ }
+ else if (wps == 0.0)
+ {
+ write_rate = 0.0;
+ }
+ else
+ {
+ write_rate = 1.0 / wps;
+ }
+ }
else
{
return (-1);
char *ptr;
size_t instance_len;
+ memset (il->instance, 0, sizeof (il->instance));
instance_len = sizeof (il->instance) - 1;
if (instance_len > vb->val_len)
instance_len = vb->val_len;
sstrncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR)
? vb->val.string
: vb->val.bitstring),
- instance_len);
+ instance_len + 1);
for (ptr = il->instance; *ptr != '\0'; ptr++)
{
vb = res->variables;
if (vb == NULL)
{
- if (res != NULL)
- snmp_free_pdu (res);
- res = NULL;
-
status = -1;
break;
}
* subtree */
if (csnmp_check_res_left_subtree (host, data, res) != 0)
{
- if (res != NULL)
- snmp_free_pdu (res);
- res = NULL;
-
+ status = 0;
break;
}
(vb != NULL) && (vb->next_variable != NULL);
vb = vb->next_variable)
/* do nothing */;
- if (vb == NULL)
- {
- status = -1;
- break;
- }
+ assert (vb != NULL);
/* Copy OID to oid_list[data->values_len] */
memcpy (oid_list[data->values_len].oid, vb->name,
res = NULL;
} /* while (status == 0) */
+ if (res != NULL)
+ snmp_free_pdu (res);
+ res = NULL;
+
if (status == 0)
csnmp_dispatch_table (host, data, instance_list, value_table);
/* No global variables */
/* #endif defined(VM_SWAPUSAGE) */
-#elif HAVE_LIBKVM
+#elif HAVE_LIBKVM_GETSWAPINFO
static kvm_t *kvm_obj = NULL;
int kvm_pagesize;
-/* #endif HAVE_LIBKVM */
+/* #endif HAVE_LIBKVM_GETSWAPINFO */
#elif HAVE_LIBSTATGRAB
/* No global variables */
/* No init stuff */
/* #endif defined(VM_SWAPUSAGE) */
-#elif HAVE_LIBKVM
+#elif HAVE_LIBKVM_GETSWAPINFO
if (kvm_obj != NULL)
{
kvm_close (kvm_obj);
ERROR ("swap plugin: kvm_open failed.");
return (-1);
}
-/* #endif HAVE_LIBKVM */
+/* #endif HAVE_LIBKVM_GETSWAPINFO */
#elif HAVE_LIBSTATGRAB
/* No init stuff */
swap_submit ("free", sw_usage.xsu_avail);
/* #endif VM_SWAPUSAGE */
-#elif HAVE_LIBKVM
+#elif HAVE_LIBKVM_GETSWAPINFO
struct kvm_swap data_s;
int status;
swap_submit ("used", used);
swap_submit ("free", free);
-/* #endif HAVE_LIBKVM */
+/* #endif HAVE_LIBKVM_GETSWAPINFO */
#elif HAVE_LIBSTATGRAB
sg_swap_stats *swap;
* Florian octo Forster <octo at verplant.org>
**/
+/**
+ * Code within `HAVE_LIBKVM_NLIST' blocks is provided under the following
+ * license:
+ *
+ * $collectd: parts of tcpconns.c, 2008/08/08 03:48:30 Michael Stapelberg $
+ * $OpenBSD: inet.c,v 1.100 2007/06/19 05:28:30 ray Exp $
+ * $NetBSD: inet.c,v 1.14 1995/10/03 21:42:37 thorpej Exp $
+ *
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
#include "collectd.h"
#include "common.h"
#include "plugin.h"
-#if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME
+#if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME && !HAVE_LIBKVM_NLIST
# error "No applicable input method."
#endif
# include <netinet/tcpip.h>
# include <netinet/tcp_seq.h>
# include <netinet/tcp_var.h>
-#endif /* HAVE_SYSCTLBYNAME */
+/* #endif HAVE_SYSCTLBYNAME */
+
+/* This is for OpenBSD and possibly NetBSD. */
+#elif HAVE_LIBKVM_NLIST
+# include <sys/queue.h>
+# include <sys/socket.h>
+# include <net/route.h>
+# include <netinet/in.h>
+# include <netinet/in_systm.h>
+# include <netinet/ip.h>
+# include <netinet/in_pcb.h>
+# include <netinet/tcp.h>
+# include <netinet/tcp_timer.h>
+# include <netinet/tcp_var.h>
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <nlist.h>
+# include <kvm.h>
+#endif /* HAVE_LIBKVM_NLIST */
#if KERNEL_LINUX
static const char *tcp_state[] =
# define TCP_STATE_LISTEN 1
# define TCP_STATE_MIN 0
# define TCP_STATE_MAX 10
-#endif /* HAVE_SYSCTLBYNAME */
+/* #endif HAVE_SYSCTLBYNAME */
+
+#elif HAVE_LIBKVM_NLIST
+static const char *tcp_state[] =
+{
+ "CLOSED",
+ "LISTEN",
+ "SYN_SENT",
+ "SYN_RECV",
+ "ESTABLISHED",
+ "CLOSE_WAIT",
+ "FIN_WAIT1",
+ "CLOSING",
+ "LAST_ACK",
+ "FIN_WAIT2",
+ "TIME_WAIT"
+};
+
+static kvm_t *kvmd;
+static u_long inpcbtable_off = 0;
+struct inpcbtable *inpcbtable_ptr = NULL;
+
+# define TCP_STATE_LISTEN 1
+# define TCP_STATE_MIN 1
+# define TCP_STATE_MAX 10
+#endif /* HAVE_LIBKVM_NLIST */
#define PORT_COLLECT_LOCAL 0x01
#define PORT_COLLECT_REMOTE 0x02
|| (pe->flags & PORT_COLLECT_LOCAL))
{
ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
- "%hu-local", pe->port);
+ "%"PRIu16"-local", pe->port);
for (i = 1; i <= TCP_STATE_MAX; i++)
{
if (pe->flags & PORT_COLLECT_REMOTE)
{
ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
- "%hu-remote", pe->port);
+ "%"PRIu16"-remote", pe->port);
for (i = 1; i <= TCP_STATE_MAX; i++)
{
port_entry_t *next = pe->next;
DEBUG ("tcpconns plugin: Removing temporary entry "
- "for listening port %hu", pe->port);
+ "for listening port %"PRIu16, pe->port);
if (prev == NULL)
port_list_head = next;
#endif
)
{
- NOTICE ("tcpconns plugin: Ignoring connection with unknown state 0x%02x.",
- state);
+ NOTICE ("tcpconns plugin: Ignoring connection with "
+ "unknown state 0x%02"PRIx8".", state);
return (-1);
}
pe->flags |= PORT_IS_LISTENING;
}
- DEBUG ("tcpconns plugin: Connection %hu <-> %hu (%s)",
+ DEBUG ("tcpconns plugin: Connection %"PRIu16" <-> %"PRIu16" (%s)",
port_local, port_remote, tcp_state[state]);
pe = conn_get_port_entry (port_local, 0 /* no create */);
/* #endif KERNEL_LINUX */
#elif HAVE_SYSCTLBYNAME
-#endif /* HAVE_SYSCTLBYNAME */
+/* #endif HAVE_SYSCTLBYNAME */
+
+#elif HAVE_LIBKVM_NLIST
+#endif /* HAVE_LIBKVM_NLIST */
static int conn_config (const char *key, const char *value)
{
return (0);
} /* int conn_read */
-#endif /* HAVE_SYSCTLBYNAME */
+/* #endif HAVE_SYSCTLBYNAME */
+
+#elif HAVE_LIBKVM_NLIST
+static int kread (u_long addr, void *buf, int size)
+{
+ int status;
+
+ status = kvm_read (kvmd, addr, buf, size);
+ if (status != size)
+ {
+ ERROR ("tcpconns plugin: kvm_read failed (got %i, expected %i): %s\n",
+ status, size, kvm_geterr (kvmd));
+ return (-1);
+ }
+ return (0);
+} /* int kread */
+
+static int conn_init (void)
+{
+ char buf[_POSIX2_LINE_MAX];
+ struct nlist nl[] =
+ {
+#define N_TCBTABLE 0
+ { "_tcbtable" },
+ { "" }
+ };
+ int status;
+
+ kvmd = kvm_openfiles (NULL, NULL, NULL, O_RDONLY, buf);
+ if (kvmd == NULL)
+ {
+ ERROR ("tcpconns plugin: kvm_openfiles failed: %s", buf);
+ return (-1);
+ }
+
+ status = kvm_nlist (kvmd, nl);
+ if (status < 0)
+ {
+ ERROR ("tcpconns plugin: kvm_nlist failed with status %i.", status);
+ return (-1);
+ }
+
+ if (nl[N_TCBTABLE].n_type == 0)
+ {
+ ERROR ("tcpconns plugin: Error looking up kernel's namelist: "
+ "N_TCBTABLE is invalid.");
+ return (-1);
+ }
+
+ inpcbtable_off = (u_long) nl[N_TCBTABLE].n_value;
+ inpcbtable_ptr = (struct inpcbtable *) nl[N_TCBTABLE].n_value;
+
+ return (0);
+} /* int conn_init */
+
+static int conn_read (void)
+{
+ struct inpcbtable table;
+ struct inpcb *head;
+ struct inpcb *next;
+ struct inpcb inpcb;
+ struct tcpcb tcpcb;
+ int status;
+
+ conn_reset_port_entry ();
+
+ /* Read the pcbtable from the kernel */
+ status = kread (inpcbtable_off, &table, sizeof (table));
+ if (status != 0)
+ return (-1);
+
+ /* Get the `head' pcb */
+ head = (struct inpcb *) &(inpcbtable_ptr->inpt_queue);
+ /* Get the first pcb */
+ next = CIRCLEQ_FIRST (&table.inpt_queue);
+
+ while (next != head)
+ {
+ /* Read the pcb pointed to by `next' into `inpcb' */
+ kread ((u_long) next, &inpcb, sizeof (inpcb));
+
+ /* Advance `next' */
+ next = CIRCLEQ_NEXT (&inpcb, inp_queue);
+
+ /* Ignore sockets, that are not connected. */
+ if (!(inpcb.inp_flags & INP_IPV6)
+ && (inet_lnaof(inpcb.inp_laddr) == INADDR_ANY))
+ continue;
+ if ((inpcb.inp_flags & INP_IPV6)
+ && IN6_IS_ADDR_UNSPECIFIED (&inpcb.inp_laddr6))
+ continue;
+
+ kread ((u_long) inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
+ conn_handle_ports (ntohs(inpcb.inp_lport), ntohs(inpcb.inp_fport), tcpcb.t_state);
+ } /* while (next != head) */
+
+ conn_submit_all ();
+
+ return (0);
+}
+#endif /* HAVE_LIBKVM_NLIST */
void module_register (void)
{
config_keys, config_keys_num);
#if KERNEL_LINUX
plugin_register_init ("tcpconns", conn_init);
+#elif HAVE_SYSCTLBYNAME
+ /* no initialization */
+#elif HAVE_LIBKVM_NLIST
+ plugin_register_init ("tcpconns", conn_init);
#endif
plugin_register_read ("tcpconns", conn_read);
} /* void module_register */
/*
- * vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
+ * vim: set shiftwidth=2 softtabstop=2 tabstop=8 fdm=marker :
*/
plugin_dispatch_values (&vl);
}
-static int thermal_sysfs_device_read (const char *name)
+static int thermal_sysfs_device_read (const char *dir, const char *name,
+ void *user_data)
{
char filename[256];
char data[1024];
return ok ? 0 : -1;
}
-static int thermal_procfs_device_read (const char *name)
+static int thermal_procfs_device_read (const char *dir, const char *name,
+ void *user_data)
{
const char str_temp[] = "temperature:";
char filename[256];
static int thermal_sysfs_read (void)
{
- return walk_directory (dirname_sysfs, thermal_sysfs_device_read);
+ return walk_directory (dirname_sysfs, thermal_sysfs_device_read,
+ /* user_data = */ NULL);
}
static int thermal_procfs_read (void)
{
- return walk_directory (dirname_procfs, thermal_procfs_device_read);
+ return walk_directory (dirname_procfs, thermal_procfs_device_read,
+ /* user_data = */ NULL);
}
static int thermal_init (void)
apache_requests count:COUNTER:0:134217728
apache_scoreboard count:GAUGE:0:65535
bitrate value:GAUGE:0:4294967295
+bytes value:GAUGE:0:U
cache_result value:COUNTER:0:4294967295
cache_size value:GAUGE:0:4294967295
charge value:GAUGE:0:U
email_size value:GAUGE:0:U
entropy entropy:GAUGE:0:4294967295
fanspeed value:GAUGE:0:U
+files value:GAUGE:0:U
frequency frequency:GAUGE:0:U
frequency_offset ppm:GAUGE:-1000000:1000000
gauge value:GAUGE:U:U
operations value:COUNTER:0:4294967295
percent percent:GAUGE:0:100.1
pg_blks value:COUNTER:0:U
+pg_db_size value:GAUGE:0:U
pg_n_tup_c value:COUNTER:0:U
pg_n_tup_g value:GAUGE:0:U
pg_numbackends value:GAUGE:0:U
{
int fd;
FILE *fhin, *fhout;
- char buffer[1024];
- char *fields[128];
- int fields_num;
fd = *((int *) arg);
free (arg);
arg = NULL;
- DEBUG ("Reading from fd #%i", fd);
+ DEBUG ("unixsock plugin: us_handle_client: Reading from fd #%i", fd);
fhin = fdopen (fd, "r");
if (fhin == NULL)
while (42)
{
- int len;
+ char buffer[1024];
+ char buffer_copy[1024];
+ char *fields[128];
+ int fields_num;
+ int len;
errno = 0;
if (fgets (buffer, sizeof (buffer), fhin) == NULL)
if (len == 0)
continue;
- DEBUG ("fgets -> buffer = %s; len = %i;", buffer, len);
+ sstrncpy (buffer_copy, buffer, sizeof (buffer_copy));
- fields_num = strsplit (buffer, fields,
+ fields_num = strsplit (buffer_copy, fields,
sizeof (fields) / sizeof (fields[0]));
if (fields_num < 1)
if (strcasecmp (fields[0], "getval") == 0)
{
- handle_getval (fhout, fields, fields_num);
+ handle_getval (fhout, buffer);
}
else if (strcasecmp (fields[0], "putval") == 0)
{
- handle_putval (fhout, fields, fields_num);
+ handle_putval (fhout, buffer);
}
else if (strcasecmp (fields[0], "listval") == 0)
{
- handle_listval (fhout, fields, fields_num);
+ handle_listval (fhout, buffer);
}
else if (strcasecmp (fields[0], "putnotif") == 0)
{
- handle_putnotif (fhout, fields, fields_num);
+ handle_putnotif (fhout, buffer);
}
else if (strcasecmp (fields[0], "flush") == 0)
{
- handle_flush (fhout, fields, fields_num);
+ handle_flush (fhout, buffer);
}
else
{
}
} /* while (fgets) */
- DEBUG ("Exiting..");
+ DEBUG ("unixsock plugin: us_handle_client: Exiting..");
fclose (fhin);
fclose (fhout);
#include "collectd.h"
#include "common.h"
#include "plugin.h"
+#include "utils_parse_option.h"
#define print_to_socket(fh, ...) \
if (fprintf (fh, __VA_ARGS__) < 0) { \
return (0);
} /* int add_to_array */
-int handle_flush (FILE *fh, char **fields, int fields_num)
+int handle_flush (FILE *fh, char *buffer)
{
int success = 0;
int error = 0;
int i;
- for (i = 1; i < fields_num; i++)
+ if ((fh == NULL) || (buffer == NULL))
+ return (-1);
+
+ DEBUG ("utils_cmd_flush: handle_flush (fh = %p, buffer = %s);",
+ (void *) fh, buffer);
+
+ if (strncasecmp ("FLUSH", buffer, strlen ("FLUSH")) != 0)
{
- char *option = fields[i];
- int status = 0;
+ print_to_socket (fh, "-1 Cannot parse command.\n");
+ return (-1);
+ }
+ buffer += strlen ("FLUSH");
- if (strncasecmp ("plugin=", option, strlen ("plugin=")) == 0)
+ while (*buffer != 0)
+ {
+ char *opt_key;
+ char *opt_value;
+ int status;
+
+ opt_key = NULL;
+ opt_value = NULL;
+ status = parse_option (&buffer, &opt_key, &opt_value);
+ if (status != 0)
{
- char *plugin;
-
- plugin = option + strlen ("plugin=");
- add_to_array (&plugins, &plugins_num, plugin);
+ print_to_socket (fh, "-1 Parsing options failed.\n");
+ sfree (plugins);
+ sfree (identifiers);
+ return (-1);
}
- else if (strncasecmp ("identifier=", option, strlen ("identifier=")) == 0)
- {
- char *identifier;
- identifier = option + strlen ("identifier=");
- add_to_array (&identifiers, &identifiers_num, identifier);
+ if (strcasecmp ("plugin", opt_key) == 0)
+ {
+ add_to_array (&plugins, &plugins_num, opt_value);
}
- else if (strncasecmp ("timeout=", option, strlen ("timeout=")) == 0)
+ else if (strcasecmp ("identifier", opt_key) == 0)
{
- char *endptr = NULL;
- char *value = option + strlen ("timeout=");
-
+ add_to_array (&identifiers, &identifiers_num, opt_value);
+ }
+ else if (strcasecmp ("timeout", opt_key) == 0)
+ {
+ char *endptr;
+
errno = 0;
- timeout = strtol (value, &endptr, 0);
-
- if ((endptr == value) || (0 != errno))
- status = -1;
- else if (0 >= timeout)
+ endptr = NULL;
+ timeout = strtol (opt_value, &endptr, 0);
+
+ if ((endptr == opt_value) || (errno != 0))
+ {
+ print_to_socket (fh, "-1 Invalid value for option `timeout': "
+ "%s\n", opt_value);
+ sfree (plugins);
+ sfree (identifiers);
+ return (-1);
+ }
+ else if (timeout <= 0)
timeout = -1;
}
else
- status = -1;
-
- if (status != 0)
{
- print_to_socket (fh, "-1 Cannot parse option %s\n", option);
+ print_to_socket (fh, "-1 Cannot parse option %s\n", opt_key);
+ sfree (plugins);
+ sfree (identifiers);
return (-1);
}
- }
+ } /* while (*buffer != 0) */
/* Add NULL entries for `any plugin' and/or `any value' if nothing was
* specified. */
}
else
{
- plugin_flush_all (timeout);
+ plugin_flush (NULL, timeout, NULL);
print_to_socket (fh, "0 Done\n");
}
+ sfree (plugins);
+ sfree (identifiers);
return (0);
} /* int handle_flush */
#ifndef UTILS_CMD_FLUSH_H
#define UTILS_CMD_FLUSH_H 1
-int handle_flush (FILE *fh, char **fields, int fields_num);
+int handle_flush (FILE *fh, char *buffer);
#endif /* UTILS_CMD_FLUSH_H */
#include "plugin.h"
#include "utils_cache.h"
+#include "utils_parse_option.h"
#define print_to_socket(fh, ...) \
if (fprintf (fh, __VA_ARGS__) < 0) { \
return -1; \
}
-int handle_getval (FILE *fh, char **fields, int fields_num)
+int handle_getval (FILE *fh, char *buffer)
{
+ char *command;
+ char *identifier;
+ char *identifier_copy;
+
char *hostname;
char *plugin;
char *plugin_instance;
gauge_t *values;
size_t values_num;
- char *identifier_copy;
-
const data_set_t *ds;
int status;
int i;
- if (fields_num != 2)
+ if ((fh == NULL) || (buffer == NULL))
+ return (-1);
+
+ DEBUG ("utils_cmd_getval: handle_getval (fh = %p, buffer = %s);",
+ (void *) fh, buffer);
+
+ command = NULL;
+ status = parse_string (&buffer, &command);
+ if (status != 0)
{
- DEBUG ("unixsock plugin: Wrong number of fields: %i", fields_num);
- print_to_socket (fh, "-1 Wrong number of fields: Got %i, expected 2.\n",
- fields_num);
+ print_to_socket (fh, "-1 Cannot parse command.\n");
return (-1);
}
- DEBUG ("unixsock plugin: Got query for `%s'", fields[1]);
+ assert (command != NULL);
- if (strlen (fields[1]) < strlen ("h/p/t"))
+ if (strcasecmp ("GETVAL", command) != 0)
{
- print_to_socket (fh, "-1 Invalied identifier, %s\n", fields[1]);
+ print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
+ return (-1);
+ }
+
+ identifier = NULL;
+ status = parse_string (&buffer, &identifier);
+ if (status != 0)
+ {
+ print_to_socket (fh, "-1 Cannot parse identifier.\n");
+ return (-1);
+ }
+ assert (identifier != NULL);
+
+ if (*buffer != 0)
+ {
+ print_to_socket (fh, "-1 Garbage after end of command: %s\n", buffer);
return (-1);
}
/* parse_identifier() modifies its first argument,
* returning pointers into it */
- identifier_copy = sstrdup (fields[1]);
+ identifier_copy = sstrdup (identifier);
status = parse_identifier (identifier_copy, &hostname,
&plugin, &plugin_instance,
&type, &type_instance);
if (status != 0)
{
- DEBUG ("unixsock plugin: Cannot parse `%s'", fields[1]);
- print_to_socket (fh, "-1 Cannot parse identifier.\n");
+ DEBUG ("handle_getval: Cannot parse identifier `%s'.", identifier);
+ print_to_socket (fh, "-1 Cannot parse identifier `%s'.\n", identifier);
sfree (identifier_copy);
return (-1);
}
ds = plugin_get_ds (type);
if (ds == NULL)
{
- DEBUG ("unixsock plugin: plugin_get_ds (%s) == NULL;", type);
+ DEBUG ("handle_getval: plugin_get_ds (%s) == NULL;", type);
print_to_socket (fh, "-1 Type `%s' is unknown.\n", type);
sfree (identifier_copy);
return (-1);
values = NULL;
values_num = 0;
- status = uc_get_rate_by_name (fields[1], &values, &values_num);
+ status = uc_get_rate_by_name (identifier, &values, &values_num);
if (status != 0)
{
print_to_socket (fh, "-1 No such value\n");
#ifndef UTILS_CMD_GETVAL_H
#define UTILS_CMD_GETVAL_H 1
-int handle_getval (FILE *fh, char **fields, int fields_num);
+int handle_getval (FILE *fh, char *buffer);
#endif /* UTILS_CMD_GETVAL_H */
#include "utils_cmd_listval.h"
#include "utils_cache.h"
+#include "utils_parse_option.h"
#define print_to_socket(fh, ...) \
if (fprintf (fh, __VA_ARGS__) < 0) { \
return -1; \
}
-int handle_listval (FILE *fh, char **fields, int fields_num)
+int handle_listval (FILE *fh, char *buffer)
{
+ char *command;
char **names = NULL;
time_t *times = NULL;
size_t number = 0;
size_t i;
int status;
- if (fields_num != 1)
+ DEBUG ("utils_cmd_listval: handle_listval (fh = %p, buffer = %s);",
+ (void *) fh, buffer);
+
+ command = NULL;
+ status = parse_string (&buffer, &command);
+ if (status != 0)
+ {
+ print_to_socket (fh, "-1 Cannot parse command.\n");
+ return (-1);
+ }
+ assert (command != NULL);
+
+ if (strcasecmp ("LISTVAL", command) != 0)
+ {
+ print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
+ return (-1);
+ }
+
+ if (*buffer != 0)
{
- DEBUG ("command listval: us_handle_listval: Wrong number of fields: %i",
- fields_num);
- print_to_socket (fh, "-1 Wrong number of fields: Got %i, expected 1.\n",
- fields_num);
+ print_to_socket (fh, "-1 Garbage after end of command: %s\n", buffer);
return (-1);
}
#ifndef UTILS_CMD_LISTVAL_H
#define UTILS_CMD_LISTVAL_H 1
-int handle_listval (FILE *fh, char **fields, int fields_num);
+int handle_listval (FILE *fh, char *buffer);
#endif /* UTILS_CMD_LISTVAL_H */
#include "common.h"
#include "plugin.h"
+#include "utils_parse_option.h"
+
#define print_to_socket(fh, ...) \
if (fprintf (fh, __VA_ARGS__) < 0) { \
char errbuf[1024]; \
return -1; \
}
-static int parse_option_severity (notification_t *n, char *value)
+static int set_option_severity (notification_t *n, const char *value)
{
if (strcasecmp (value, "Failure") == 0)
n->severity = NOTIF_FAILURE;
return (-1);
return (0);
-} /* int parse_option_severity */
+} /* int set_option_severity */
-static int parse_option_time (notification_t *n, char *value)
+static int set_option_time (notification_t *n, const char *value)
{
time_t tmp;
n->time = tmp;
return (0);
-} /* int parse_option_time */
+} /* int set_option_time */
-static int parse_option (notification_t *n, char *buffer)
+static int set_option (notification_t *n, const char *option, const char *value)
{
- char *option = buffer;
- char *value;
-
- if ((n == NULL) || (option == NULL))
+ if ((n == NULL) || (option == NULL) || (value == NULL))
return (-1);
- value = strchr (option, '=');
- if (value == NULL)
- return (-1);
- *value = '\0'; value++;
+ DEBUG ("utils_cmd_putnotif: set_option (option = %s, value = %s);",
+ option, value);
if (strcasecmp ("severity", option) == 0)
- return (parse_option_severity (n, value));
+ return (set_option_severity (n, value));
else if (strcasecmp ("time", option) == 0)
- return (parse_option_time (n, value));
+ return (set_option_time (n, value));
+ else if (strcasecmp ("message", option) == 0)
+ sstrncpy (n->message, value, sizeof (n->message));
else if (strcasecmp ("host", option) == 0)
sstrncpy (n->host, value, sizeof (n->host));
else if (strcasecmp ("plugin", option) == 0)
return (1);
return (0);
-} /* int parse_option */
+} /* int set_option */
-static int parse_message (notification_t *n, char **fields, int fields_num)
+int handle_putnotif (FILE *fh, char *buffer)
{
+ char *command;
+ notification_t n;
int status;
- /* Strip off the leading `message=' */
- fields[0] += strlen ("message=");
-
- status = strjoin (n->message, sizeof (n->message), fields, fields_num, " ");
- if (status < 0)
+ if ((fh == NULL) || (buffer == NULL))
return (-1);
- return (0);
-} /* int parse_message */
+ DEBUG ("utils_cmd_putnotif: handle_putnotif (fh = %p, buffer = %s);",
+ (void *) fh, buffer);
-int handle_putnotif (FILE *fh, char **fields, int fields_num)
-{
- notification_t n;
- int status;
- int i;
+ command = NULL;
+ status = parse_string (&buffer, &command);
+ if (status != 0)
+ {
+ print_to_socket (fh, "-1 Cannot parse command.\n");
+ return (-1);
+ }
+ assert (command != NULL);
- /* Required fields: `PUTNOTIF', severity, time, message */
- if (fields_num < 4)
+ if (strcasecmp ("PUTNOTIF", command) != 0)
{
- DEBUG ("cmd putnotif: Wrong number of fields: %i", fields_num);
- print_to_socket (fh, "-1 Wrong number of fields: Got %i, "
- "expected at least 4.\n",
- fields_num);
+ print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
return (-1);
}
memset (&n, '\0', sizeof (n));
status = 0;
- for (i = 1; i < fields_num; i++)
+ while (*buffer != 0)
{
- if (strncasecmp (fields[i], "message=", strlen ("message=")) == 0)
+ char *key;
+ char *value;
+
+ status = parse_option (&buffer, &key, &value);
+ if (status != 0)
{
- status = parse_message (&n, fields + i, fields_num - i);
- if (status != 0)
- {
- print_to_socket (fh, "-1 Error parsing the message. Have you hit the "
- "limit of %u bytes?\n", (unsigned int) sizeof (n.message));
- }
+ print_to_socket (fh, "-1 Malformed option.\n");
break;
}
- else
+
+ status = set_option (&n, key, value);
+ if (status != 0)
{
- status = parse_option (&n, fields[i]);
- if (status != 0)
- {
- print_to_socket (fh, "-1 Error parsing option `%s'\n", fields[i]);
- break;
- }
+ print_to_socket (fh, "-1 Error parsing option `%s'\n", key);
+ break;
}
} /* for (i) */
#ifndef UTILS_CMD_PUTNOTIF_H
#define UTILS_CMD_PUTNOTIF_H 1
-int handle_putnotif (FILE *fh, char **fields, int fields_num);
+int handle_putnotif (FILE *fh, char *buffer);
/* vim: set shiftwidth=2 softtabstop=2 tabstop=8 : */
/**
* collectd - src/utils_cms_putval.c
- * Copyright (C) 2007 Florian octo Forster
+ * Copyright (C) 2007,2008 Florian octo Forster
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
#include "common.h"
#include "plugin.h"
+#include "utils_parse_option.h"
+
#define print_to_socket(fh, ...) \
if (fprintf (fh, __VA_ARGS__) < 0) { \
char errbuf[1024]; \
return (0);
} /* int parse_value */
-static int parse_option (value_list_t *vl, char *buffer)
+static int set_option (value_list_t *vl, const char *key, const char *value)
{
- char *option = buffer;
- char *value;
-
- if ((vl == NULL) || (option == NULL))
- return (-1);
-
- value = strchr (option, '=');
- if (value == NULL)
+ if ((vl == NULL) || (key == NULL) || (value == NULL))
return (-1);
- *value = '\0'; value++;
- if (strcasecmp ("interval", option) == 0)
+ if (strcasecmp ("interval", key) == 0)
{
- vl->interval = atoi (value);
- if (vl->interval <= 0)
- vl->interval = interval_g;
+ int tmp;
+ char *endptr;
+
+ endptr = NULL;
+ errno = 0;
+ tmp = strtol (value, &endptr, 0);
+
+ if ((errno == 0) && (endptr != NULL)
+ && (endptr != value) && (tmp > 0))
+ vl->interval = tmp;
}
else
return (1);
return (0);
} /* int parse_option */
-int handle_putval (FILE *fh, char **fields, int fields_num)
+int handle_putval (FILE *fh, char *buffer)
{
+ char *command;
+ char *identifier;
char *hostname;
char *plugin;
char *plugin_instance;
char *type;
char *type_instance;
int status;
- int i;
+ int values_submitted;
char *identifier_copy;
const data_set_t *ds;
value_list_t vl = VALUE_LIST_INIT;
- if (fields_num < 3)
+ DEBUG ("utils_cmd_putval: handle_putval (fh = %p, buffer = %s);",
+ (void *) fh, buffer);
+
+ command = NULL;
+ status = parse_string (&buffer, &command);
+ if (status != 0)
{
- DEBUG ("cmd putval: Wrong number of fields: %i",
- fields_num);
- print_to_socket (fh, "-1 Wrong number of fields: Got %i, "
- "expected at least 3.\n",
- fields_num);
+ print_to_socket (fh, "-1 Cannot parse command.\n");
return (-1);
}
+ assert (command != NULL);
+
+ if (strcasecmp ("PUTVAL", command) != 0)
+ {
+ print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
+ return (-1);
+ }
+
+ identifier = NULL;
+ status = parse_string (&buffer, &identifier);
+ if (status != 0)
+ {
+ print_to_socket (fh, "-1 Cannot parse identifier.\n");
+ return (-1);
+ }
+ assert (identifier != NULL);
/* parse_identifier() modifies its first argument,
* returning pointers into it */
- identifier_copy = sstrdup (fields[1]);
+ identifier_copy = sstrdup (identifier);
status = parse_identifier (identifier_copy, &hostname,
&plugin, &plugin_instance,
&type, &type_instance);
if (status != 0)
{
- DEBUG ("cmd putval: Cannot parse `%s'", fields[1]);
- print_to_socket (fh, "-1 Cannot parse identifier.\n");
+ DEBUG ("handle_putval: Cannot parse identifier `%s'.",
+ identifier);
+ print_to_socket (fh, "-1 Cannot parse identifier `%s'.\n",
+ identifier);
sfree (identifier_copy);
return (-1);
}
ds = plugin_get_ds (type);
if (ds == NULL) {
+ print_to_socket (fh, "-1 Type `%s' isn't defined.\n", type);
sfree (identifier_copy);
return (-1);
}
+ /* Free identifier_copy */
+ hostname = NULL;
+ plugin = NULL; plugin_instance = NULL;
+ type = NULL; type_instance = NULL;
+ sfree (identifier_copy);
+
vl.values_len = ds->ds_num;
vl.values = (value_t *) malloc (vl.values_len * sizeof (value_t));
if (vl.values == NULL)
{
print_to_socket (fh, "-1 malloc failed.\n");
- sfree (identifier_copy);
return (-1);
}
/* All the remaining fields are part of the optionlist. */
- for (i = 2; i < fields_num; i++)
+ values_submitted = 0;
+ while (*buffer != 0)
{
- if (strchr (fields[i], ':') != NULL)
+ char *string = NULL;
+ char *value = NULL;
+
+ status = parse_option (&buffer, &string, &value);
+ if (status < 0)
{
- /* It's parse_value's job to write an error to `fh'.
- * This is not the case with `parse_option below.
- * Neither will write an success message. */
- if (parse_value (ds, &vl, fh, fields[i]) != 0)
- break;
+ /* parse_option failed, buffer has been modified.
+ * => we need to abort */
+ print_to_socket (fh, "-1 Misformatted option.\n");
+ return (-1);
}
- else if (strchr (fields[i], '=') != NULL)
+ else if (status == 0)
{
- if (parse_option (&vl, fields[i]) != 0)
- {
- print_to_socket (fh, "-1 Error parsing option `%s'\n",
- fields[i]);
- break;
- }
+ assert (string != NULL);
+ assert (value != NULL);
+ set_option (&vl, string, value);
+ continue;
}
- else
+ /* else: parse_option but buffer has not been modified. This is
+ * the default if no `=' is found.. */
+
+ status = parse_string (&buffer, &string);
+ if (status != 0)
{
- WARNING ("cmd putval: handle_putval: "
- "Cannot parse field #%i `%s'; "
- "Ignoring it.\n",
- i, fields[i]);
+ print_to_socket (fh, "-1 Misformatted value.\n");
+ return (-1);
}
- }
+ assert (string != NULL);
+
+ status = parse_value (ds, &vl, fh, string);
+ if (status != 0)
+ {
+ /* An error has already been printed. */
+ return (-1);
+ }
+ values_submitted++;
+ } /* while (*buffer != 0) */
/* Done parsing the options. */
- if (i == fields_num)
- print_to_socket (fh, "0 Success\n");
+ print_to_socket (fh, "0 Success: %i %s been dispatched.\n",
+ values_submitted,
+ (values_submitted == 1) ? "value has" : "values have");
sfree (vl.values);
- sfree (identifier_copy);
return (0);
} /* int handle_putval */
#ifndef UTILS_CMD_PUTVAL_H
#define UTILS_CMD_PUTVAL_H 1
-int handle_putval (FILE *fh, char **fields, int fields_num);
+int handle_putval (FILE *fh, char *buffer);
#endif /* UTILS_CMD_PUTVAL_H */
--- /dev/null
+/**
+ * collectd - src/utils_parse_option.c
+ * Copyright (C) 2008 Florian 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"
+#include "utils_parse_option.h"
+
+int parse_string (char **ret_buffer, char **ret_string)
+{
+ char *buffer;
+ char *string;
+
+ buffer = *ret_buffer;
+
+ /* Eat up leading spaces. */
+ string = buffer;
+ while (isspace ((int) *string))
+ string++;
+ if (*string == 0)
+ return (1);
+
+ /* A quoted string */
+ if (*string == '"')
+ {
+ char *dst;
+
+ string++;
+ if (*string == 0)
+ return (1);
+
+ dst = string;
+ buffer = string;
+ while ((*buffer != '"') && (*buffer != 0))
+ {
+ /* Un-escape backslashes */
+ if (*buffer == '\\')
+ {
+ buffer++;
+ /* Catch a backslash at the end of buffer */
+ if (*buffer == 0)
+ return (-1);
+ }
+ *dst = *buffer;
+ buffer++;
+ dst++;
+ }
+ /* No quote sign has been found */
+ if (*buffer == 0)
+ return (-1);
+
+ *dst = 0;
+ dst++;
+ *buffer = 0;
+ buffer++;
+
+ /* Check for trailing spaces. */
+ if ((*buffer != 0) && !isspace ((int) *buffer))
+ return (-1);
+ }
+ else /* an unquoted string */
+ {
+ buffer = string;
+ while ((*buffer != 0) && !isspace ((int) *buffer))
+ buffer++;
+ if (*buffer != 0)
+ {
+ *buffer = 0;
+ buffer++;
+ }
+ }
+
+ /* Eat up trailing spaces */
+ while (isspace ((int) *buffer))
+ buffer++;
+
+ *ret_buffer = buffer;
+ *ret_string = string;
+
+ return (0);
+} /* int parse_string */
+
+/*
+ * parse_option
+ * ------------
+ * Parses an ``option'' as used with the unixsock and exec commands. An
+ * option is of the form:
+ * name0="value"
+ * name1="value with \"quotes\""
+ * name2="value \\ backslash"
+ * However, if the value does *not* contain a space character, you can skip
+ * the quotes.
+ */
+int parse_option (char **ret_buffer, char **ret_key, char **ret_value)
+{
+ char *buffer;
+ char *key;
+ char *value;
+ int status;
+
+ buffer = *ret_buffer;
+
+ /* Eat up leading spaces */
+ key = buffer;
+ while (isspace ((int) *key))
+ key++;
+ if (*key == 0)
+ return (1);
+
+ /* Look for the equal sign */
+ buffer = key;
+ while (isalnum ((int) *buffer))
+ buffer++;
+ if ((*buffer != '=') || (buffer == key))
+ return (1);
+ *buffer = 0;
+ buffer++;
+ /* Empty values must be written as "" */
+ if (isspace ((int) *buffer) || (*buffer == 0))
+ return (-1);
+
+ status = parse_string (&buffer, &value);
+ if (status != 0)
+ return (-1);
+
+ /* NB: parse_string will have eaten up all trailing spaces. */
+
+ *ret_buffer = buffer;
+ *ret_key = key;
+ *ret_value = value;
+
+ return (0);
+} /* int parse_option */
+
+/* vim: set sw=2 ts=8 tw=78 et : */
--- /dev/null
+/**
+ * collectd - src/utils_parse_option.h
+ * Copyright (C) 2008 Florian 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>
+ **/
+
+#ifndef UTILS_PARSE_OPTION
+#define UTILS_PARSE_OPTION 1
+
+int parse_string (char **ret_buffer, char **ret_string);
+int parse_option (char **ret_buffer, char **ret_key, char **ret_value);
+
+#endif /* UTILS_PARSE_OPTION */
+
+/* vim: set sw=2 ts=8 tw=78 et : */
if (status != 0)
{
ERROR ("ut_check_interesting: parse_identifier failed.");
+ sfree (name_copy);
return (-1);
}
#!/bin/sh
-DEFAULT_VERSION="4.4.2.git"
+DEFAULT_VERSION="4.5.0.git"
VERSION="$( git describe 2> /dev/null | sed -e 's/^collectd-//' )"