src/collectd*.5
src/types.db.5
src/config.h.in~
+src/libiptc/.libs
+src/libiptc/*.la
+src/libiptc/*.lo
src/liboconfig/.libs
src/liboconfig/*.la
src/liboconfig/*.lo
apcups plugin by:
Anthony Gialluca <tonyabg at charter.net>
-cpufreq, multimeter and irq plugin by:
+cpufreq, multimeter and irq plugin, as well as some bugfixes in the exec plugin
+by:
Peter Holik <peter at holik.at>
hddtemp plugin by:
perl plugin by:
Sebastian Harl <sh at tokkee.org>
+powerdns plugin and initial `tail' subsystem by:
+ Luke Herberling <collectd at c-ware.com>
+
processes plugin by:
Lyonel Vincent <lyonel at ezix.org>
tape plugin by:
Scott Garrett <sgarrett at technomancer.com>
+teamspeak2 plugin by:
+ Stefan Hacker <stefan.hacker at web.de>
+
users plugin by:
Sebastian Harl <sh at tokkee.org>
uuid plugin by:
- Dan Berrange <berrange@redhat.com>
- Richard W.M. Jones <rjones@redhat.com>
+ Dan Berrange <berrange at redhat.com>
+ Richard W.M. Jones <rjones at redhat.com>
vserver plugin by:
Sebastian Harl <sh at tokkee.org>
contributed by:
Luboš Staněk <lubek at users.sourceforge.net>
+Support for the statgrab library has been added to the cpu, disk and users
+plugins by
+ Oleg King <king2 at kaluga.ru>
+
collectd is available at:
<http://collectd.org/>
+2008-05-06, Version 4.4.0
+ * collectd: Internal code cleanups.
+ * collectd: Added support for a `Flush' command in the unixsock and
+ exec plugins. This command can be used to force a plugin (or all) to
+ flush its values to disk.
+ * collectd: Thresholds can now be configured to apply to one data
+ source only, making it possible to configure different thresholds
+ for each data source.
+ * apache, nginx plugins: Added the possibility to disable host and/or
+ peer verification.
+ * ascent plugin: The new ascent plugin reads and parses the statistics
+ page of an Ascent server.
+ * cpu plugin: Support for the statgrab library has been added.
+ * disk plugin: The possibility to ignore certain disks or collect only
+ specific disks has been added.
+ * disk plugin: Support for the statgrab library has been added.
+ * ipmi plugin: The new ipmi plugin uses the OpenIPMI library to read
+ sensor values via IPMI, the intelligent platform management
+ interface.
+ * iptables plugin: The iptc library that is used by the iptables
+ plugin has been added to the distribution, because it is not
+ provided by all distributions and removed from at least one.
+ * powerdns plugin: The new powerdns plugin reads statistics from an
+ authoritative or a recursing PowerDNS name server.
+ * rrdtool plugin: The size of the files generated with the default
+ configuration has been decreased.
+ * tail plugin: The new tail plugin can be used to gather statistics by
+ continuously reading from log files.
+ * teamspeak2 plugin: The new teamspeak2 plugin connects to a
+ TeamSpeak2 server and collects statistics about the number of users
+ and number of channels.
+ * users plugin: Support for the statgrab library has been added.
+ * vmem plugin: The new vmem plugin collects very detailed statistics
+ about the virtual memory subsystem of Linux.
+
2008-04-22, Version 4.3.3
* build system: Improved detection of several libraries, especially if
they are in non-standard paths.
Sensors in Macs running Mac OS X / Darwin: Temperature, fanspeed and
voltage sensors.
+ - ascent
+ Statistics about Ascent, a free server for the game `World of Warcraft'.
+
- battery
Batterycharge, -current and voltage of ACPI and PMU based laptop
batteries.
- swap
Pages swapped out onto harddisk or whatever is called `swap' by the OS..
+ - tail
+ Follows (tails) logfiles, parses them by lines and submits matched
+ values.
+
- tape
Bytes and operations read and written on tape devices. Solaris only.
- users
Users currently logged in.
+ - vmem
+ Virtual memory statistics, e. g. the number of page-ins/-outs or the
+ number of pagefaults.
+
- vserver
System resources used by Linux VServers.
See <http://linux-vserver.org/>.
platforms.
* libcurl (optional)
- If you want to use the `apache' and/or `nginx' plugins.
+ If you want to use the `apache', `ascent', or `nginx' plugin.
* libhal (optional)
If present, the uuid plugin will check for UUID from HAL.
Collect statistics from virtual machines.
* libxml2 (optional)
- Parse XML data provided by libvirt.
+ Parse XML data. This is needed for the `ascent' and `libvirt' plugins.
Configuring / Compiling / Installing
+For version 4.4:
+* PowerDNS plugin
+
For version 4.3:
* unixsock plugin: Remove the custom cache if possible.
plugin_register
plugin_unregister
plugin_dispatch_values
+ plugin_flush
+ plugin_flush_one
+ plugin_flush_all
plugin_dispatch_notification
plugin_log
) ],
TYPE_SHUTDOWN
TYPE_LOG
TYPE_NOTIF
+ TYPE_FLUSH
TYPE_DATASET
) ],
'ds_types' => [ qw(
TYPE_WRITE, "write",
TYPE_SHUTDOWN, "shutdown",
TYPE_LOG, "log",
- TYPE_NOTIF, "notify"
+ TYPE_NOTIF, "notify",
+ TYPE_FLUSH, "flush"
);
foreach my $type (keys %types) {
}
}
+sub plugin_flush {
+ my %args = @_;
+
+ my $timeout = -1;
+
+ DEBUG ("Collectd::plugin_flush:"
+ . (defined ($args{'timeout'}) ? " timeout = $args{'timeout'}" : "")
+ . (defined ($args{'plugins'}) ? " plugins = $args{'plugins'}" : ""));
+
+ if (defined ($args{'timeout'}) && ($args{'timeout'} > 0)) {
+ $timeout = $args{'timeout'};
+ }
+
+ if (! defined $args{'plugins'}) {
+ plugin_flush_all ($timeout);
+ }
+ else {
+ if ("ARRAY" eq ref ($args{'plugins'})) {
+ foreach my $plugin (@{$args{'plugins'}}) {
+ plugin_flush_one ($timeout, $plugin);
+ }
+ }
+ else {
+ plugin_flush_one ($timeout, $args{'plugins'});
+ }
+ }
+}
+
1;
# vim: set sw=4 ts=4 tw=78 noexpandtab :
return;
} # putnotif
+=item I<$obj>-E<gt>B<flush> (B<timeout> =E<gt> I<$timeout>, B<plugins> =E<gt> [...]);
+
+Flush cached data.
+
+Valid options are:
+
+=over 4
+
+=item B<timeout>
+
+If this option is specified, only data older than I<$timeout> seconds is
+flushed.
+
+=item B<plugins>
+
+If this option is specified, only the selected plugins will be flushed.
+
+=back
+
+=cut
+
+sub flush
+{
+ my $obj = shift;
+ my %args = @_;
+
+ my $fh = $obj->{'sock'} or confess;
+
+ my $status = 0;
+ my $msg = "FLUSH";
+
+ if ($args{'timeout'})
+ {
+ $msg .= " timeout=" . $args{'timeout'};
+ }
+
+ if ($args{'plugins'})
+ {
+ foreach my $plugin (@{$args{'plugins'}})
+ {
+ $msg .= " plugin=" . $plugin;
+ }
+ }
+
+ $msg .= "\n";
+
+ send ($fh, $msg, 0) or confess ("send: $!");
+ $msg = undef;
+ recv ($fh, $msg, 1024, 0) or confess ("recv: $!");
+
+ ($status, $msg) = split (' ', $msg, 2);
+ return (1) if ($status == 0);
+
+ $obj->{'error'} = $msg;
+ return;
+}
+
=item I<$obj>-E<gt>destroy ();
Closes the socket before the object is destroyed. This function is also
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$withval" != "xno" && test "x$withval" != "xyes"
AC_CHECK_LIB(iptc, iptc_init,
[
AC_DEFINE(HAVE_LIBIPTC, 1, [Define to 1 if you have the iptc library (-liptc).])
- ], [with_libiptc="no (libiptc not found)"])
+ ],
+ [
+ with_libiptc="yes"
+ with_own_libiptc="yes"
+ ])
fi
-if test "x$with_libiptc" = "xyes"
+if test "x$with_libiptc" = "xyes" -a "x$with_own_libiptc" != "xyes"
then
AC_CHECK_HEADERS(libiptc/libiptc.h,
[
AC_DEFINE(HAVE_LIBIPTC_LIBIPTC_H, 1, [Define to 1 if you have the <libiptc/libiptc.h> header file.])
- ], [with_libiptc="no (libiptc/libiptc.h not found)"])
+ ],
+ [
+ with_libiptc="yes"
+ with_own_libiptc="yes"
+ ])
fi
if test "x$with_libiptc" = "xyes"
then
- collect_libiptc=1
-else
- collect_libiptc=0
+ SAVE_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS -I$KERNEL_DIR/include"
+
+ AC_CHECK_HEADERS(linux/netfilter_ipv4/ip_tables.h linux/netfilter_ipv6/ip6_tables.h, [],
+ [
+ with_libiptc="no (Linux iptables headers not found - check KERNEL_DIR)"
+ with_own_libiptc="no"
+ ],
+ [
+#include "$srcdir/src/libiptc/ipt_kernel_headers.h"
+ ])
+
+ CFLAGS=$SAVE_CFLAGS
fi
AM_CONDITIONAL(BUILD_WITH_LIBIPTC, test "x$with_libiptc" = "xyes")
+AM_CONDITIONAL(BUILD_WITH_OWN_LIBIPTC, test "x$with_own_libiptc" = "xyes")
+if test "x$with_own_libiptc" = "xyes"
+then
+ AC_DEFINE(OWN_LIBIPTC, 1, [Define to 1 if we use the shipped iptc library.])
+fi
with_snmp_config="net-snmp-config"
with_snmp_cflags=""
fi
AM_CONDITIONAL(BUILD_WITH_LIBNETLINK, test "x$with_libnetlink" = "xyes")
+with_libopenipmipthread="yes"
+with_libopenipmipthread_cflags=""
+with_libopenipmipthread_libs=""
+
+AC_MSG_CHECKING([for pkg-config])
+temp_result="no"
+if test "x$PKG_CONFIG" = "x"
+then
+ with_libopenipmipthread="no"
+ temp_result="no"
+else
+ temp_result="$PKG_CONFIG"
+fi
+AC_MSG_RESULT([$temp_result])
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+ AC_MSG_CHECKING([for libOpenIPMIpthread])
+ $PKG_CONFIG --exists OpenIPMIpthread 2>/dev/null
+ if test "$?" != "0"
+ then
+ with_libopenipmipthread="no ($PKG_CONFIG doesn't know OpenIPMIpthread)"
+ fi
+ AC_MSG_RESULT([$with_libopenipmipthread])
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+ AC_MSG_CHECKING([for libOpenIPMIpthread CFLAGS])
+ temp_result="`$PKG_CONFIG --cflags OpenIPMIpthread`"
+ if test "$?" = "0"
+ then
+ with_libopenipmipthread_cflags="$temp_result"
+ else
+ with_libopenipmipthread="no ($PKG_CONFIG --cflags OpenIPMIpthread failed)"
+ temp_result="$PKG_CONFIG --cflags OpenIPMIpthread failed"
+ fi
+ AC_MSG_RESULT([$temp_result])
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+ AC_MSG_CHECKING([for libOpenIPMIpthread LDFLAGS])
+ temp_result="`$PKG_CONFIG --libs OpenIPMIpthread`"
+ if test "$?" = "0"
+ then
+ with_libopenipmipthread_ldflags="$temp_result"
+ else
+ with_libopenipmipthread="no ($PKG_CONFIG --libs OpenIPMIpthread failed)"
+ temp_result="$PKG_CONFIG --libs OpenIPMIpthread failed"
+ fi
+ AC_MSG_RESULT([$temp_result])
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libopenipmipthread_cflags"
+
+ AC_CHECK_HEADERS(OpenIPMI/ipmi_smi.h,
+ [with_libopenipmipthread="yes"],
+ [with_libopenipmipthread="no (OpenIPMI/ipmi_smi.h not found)"],
+[#include <OpenIPMI/ipmiif.h>
+#include <OpenIPMI/ipmi_err.h>
+#include <OpenIPMI/ipmi_posix.h>
+#include <OpenIPMI/ipmi_conn.h>
+])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+ BUILD_WITH_OPENIPMI_CFLAGS="$with_libopenipmipthread_cflags"
+ BUILD_WITH_OPENIPMI_LIBS="$with_libopenipmipthread_ldflags"
+ AC_SUBST(BUILD_WITH_OPENIPMI_CFLAGS)
+ AC_SUBST(BUILD_WITH_OPENIPMI_LIBS)
+fi
+
dnl Check for libvirt and libxml2 libraries.
with_libxml2="no (pkg-config isn't available)"
with_libxml2_cflags=""
with_libvirt="no (pkg-config isn't available)"
with_libvirt_cflags=""
with_libvirt_ldflags=""
-PKG_PROG_PKG_CONFIG
if test "x$PKG_CONFIG" != "x"
then
pkg-config --exists 'libxml-2.0' 2>/dev/null
AC_COLLECTD([daemon], [disable], [feature], [daemon mode])
AC_COLLECTD([getifaddrs],[enable], [feature], [getifaddrs under Linux])
+plugin_ascent="no"
plugin_battery="no"
plugin_cpu="no"
plugin_cpufreq="no"
plugin_disk="no"
plugin_entropy="no"
plugin_interface="no"
+plugin_ipmi="no"
plugin_ipvs="no"
plugin_irq="no"
plugin_libvirt="no"
plugin_tape="no"
plugin_tcpconns="no"
plugin_users="no"
+plugin_vmem="no"
plugin_vserver="no"
plugin_wireless="no"
plugin_serial="yes"
plugin_swap="yes"
plugin_tcpconns="yes"
+ plugin_vmem="yes"
plugin_vserver="yes"
plugin_wireless="yes"
# libstatgrab
if test "x$with_libstatgrab" = "xyes"
then
+ plugin_cpu="yes"
+ plugin_disk="yes"
plugin_interface="yes"
plugin_load="yes"
plugin_memory="yes"
plugin_swap="yes"
+ plugin_users="yes"
+fi
+
+if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
+then
+ plugin_ascent="yes"
+fi
+
+if test "x$with_libopenipmipthread" = "xyes"
+then
+ plugin_ipmi="yes"
fi
if test "x$have_processor_info" = "xyes"
AC_PLUGIN([apache], [$with_libcurl], [Apache httpd statistics])
AC_PLUGIN([apcups], [yes], [Statistics of UPSes by APC])
AC_PLUGIN([apple_sensors], [$with_libiokit], [Apple's hardware sensors])
+AC_PLUGIN([ascent], [$plugin_ascent], [AscentEmu player statistics])
AC_PLUGIN([battery], [$plugin_battery], [Battery statistics])
AC_PLUGIN([cpu], [$plugin_cpu], [CPU usage statistics])
AC_PLUGIN([cpufreq], [$plugin_cpufreq], [CPU frequency statistics])
AC_PLUGIN([hddtemp], [yes], [Query hddtempd])
AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics])
AC_PLUGIN([iptables], [$with_libiptc], [IPTables rule counters])
+AC_PLUGIN([ipmi], [$plugin_ipmi], [IPMI sensor statistics])
AC_PLUGIN([ipvs], [$plugin_ipvs], [IPVS connection statistics])
AC_PLUGIN([irq], [$plugin_irq], [IRQ statistics])
AC_PLUGIN([libvirt], [$plugin_libvirt], [Virtual machine statistics])
AC_PLUGIN([nut], [$with_libupsclient], [Network UPS tools statistics])
AC_PLUGIN([perl], [$plugin_perl], [Embed a Perl interpreter])
AC_PLUGIN([ping], [$with_liboping], [Network latency statistics])
+AC_PLUGIN([powerdns], [yes], [PowerDNS statistics])
AC_PLUGIN([processes], [$plugin_processes], [Process statistics])
AC_PLUGIN([rrdtool], [$with_rrdtool], [RRDTool output plugin])
AC_PLUGIN([sensors], [$with_lm_sensors], [lm_sensors statistics])
AC_PLUGIN([snmp], [$with_libnetsnmp], [SNMP querying plugin])
AC_PLUGIN([swap], [$plugin_swap], [Swap usage statistics])
AC_PLUGIN([syslog], [$have_syslog], [Syslog logging plugin])
+AC_PLUGIN([tail], [yes], [Parsing of logfiles])
AC_PLUGIN([tape], [$plugin_tape], [Tape drive statistics])
AC_PLUGIN([tcpconns], [$plugin_tcpconns], [TCP connection statistics])
+AC_PLUGIN([teamspeak2], [yes], [TeamSpeak2 server statistics])
AC_PLUGIN([unixsock], [yes], [Unixsock communication plugin])
AC_PLUGIN([users], [$plugin_users], [User statistics])
AC_PLUGIN([uuid], [yes], [UUID as hostname plugin])
+AC_PLUGIN([vmem], [$plugin_vmem], [Virtual memory statistics])
AC_PLUGIN([vserver], [$plugin_vserver], [Linux VServer statistics])
AC_PLUGIN([wireless], [$plugin_wireless], [Wireless statistics])
AC_PLUGIN([xmms], [$with_libxmms], [XMMS statistics])
AC_SUBST(PERL_BINDINGS)
AC_SUBST(PERL_BINDINGS_OPTIONS)
-AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/liboconfig/Makefile src/liboping/Makefile bindings/Makefile)
+AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libiptc/Makefile src/liboconfig/Makefile src/liboping/Makefile bindings/Makefile)
if test "x$with_rrdtool" = "xyes" \
&& test "x$librrd_threadsafe" != "xyes"
with_liboping="yes (shipped version)"
fi
+if test "x$with_libiptc" = "xyes" -a "x$with_own_libiptc" = "xyes"
+then
+ with_libiptc="yes (shipped version)"
+fi
+
if test "x$with_libperl" = "xyes"
then
with_libperl="yes (version `$perl_interpreter -MConfig -e 'print $Config{version};'`)"
libnetlink . . . . $with_libnetlink
libnetsnmp . . . . $with_libnetsnmp
liboconfig . . . . $with_liboconfig
+ libopenipmi . . . . $with_libopenipmipthread
liboping . . . . . $with_liboping
libpcap . . . . . . $with_libpcap
libperl . . . . . . $with_libperl
apache . . . . . . $enable_apache
apcups . . . . . . $enable_apcups
apple_sensors . . . $enable_apple_sensors
+ ascent . . . . . . $enable_ascent
battery . . . . . . $enable_battery
cpu . . . . . . . . $enable_cpu
cpufreq . . . . . . $enable_cpufreq
hddtemp . . . . . . $enable_hddtemp
interface . . . . . $enable_interface
iptables . . . . . $enable_iptables
+ ipmi . . . . . . . $enable_ipmi
ipvs . . . . . . . $enable_ipvs
irq . . . . . . . . $enable_irq
libvirt . . . . . . $enable_libvirt
nut . . . . . . . . $enable_nut
perl . . . . . . . $enable_perl
ping . . . . . . . $enable_ping
+ powerdns . . . . . $enable_powerdns
processes . . . . . $enable_processes
rrdtool . . . . . . $enable_rrdtool
sensors . . . . . . $enable_sensors
snmp . . . . . . . $enable_snmp
swap . . . . . . . $enable_swap
syslog . . . . . . $enable_syslog
+ tail . . . . . . . $enable_tail
tape . . . . . . . $enable_tape
tcpconns . . . . . $enable_tcpconns
+ teamspeak2 . . . . $enable_teamspeak2
unixsock . . . . . $enable_unixsock
users . . . . . . . $enable_users
uuid . . . . . . . $enable_uuid
+ vmem . . . . . . . $enable_vmem
vserver . . . . . . $enable_vserver
wireless . . . . . $enable_wireless
xmms . . . . . . . $enable_xmms
#!/usr/bin/perl
#
# collectd - contrib/cussh.pl
-# Copyright (C) 2007 Sebastian Harl
+# Copyright (C) 2007-2008 Sebastian Harl
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
my $path = $ARGV[0] || "/var/run/collectd-unixsock";
my $sock = Collectd::Unixsock->new($path);
+ my $cmds = {
+ PUTVAL => \&putval,
+ GETVAL => \&getval,
+ FLUSH => \&flush,
+ LISTVAL => \&listval,
+ };
+
if (! $sock) {
print STDERR "Unable to connect to $path!\n";
exit 1;
}
- print "cussh version 0.1, Copyright (C) 2007 Sebastian Harl\n"
+ print "cussh version 0.2, Copyright (C) 2007-2008 Sebastian Harl\n"
. "cussh comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
. "and you are welcome to redistribute it under certain conditions.\n"
. "See the GNU General Public License 2 for more details.\n\n";
print "cussh> ";
my $line = <STDIN>;
- last if ((! $line) || ($line =~ m/^quit$/i));
+ last if (! $line);
+
+ chomp $line;
- my ($cmd) = $line =~ m/^(\w+)\s+/;
+ last if ($line =~ m/^quit$/i);
+
+ my ($cmd) = $line =~ m/^(\w+)\s*/;
$line = $';
next if (! $cmd);
$cmd = uc $cmd;
my $f = undef;
- if ($cmd eq "PUTVAL") {
- $f = \&putval;
- }
- elsif ($cmd eq "GETVAL") {
- $f = \&getval;
+ if (defined $cmds->{$cmd}) {
+ $f = $cmds->{$cmd};
}
else {
print STDERR "ERROR: Unknown command $cmd!\n";
return \%id;
}
+sub putid {
+ my $ident = shift || return;
+
+ my $string;
+
+ $string = $ident->{'host'} . "/" . $ident->{'plugin'};
+
+ if (defined $ident->{'plugin_instance'}) {
+ $string .= "-" . $ident->{'plugin_instance'};
+ }
+
+ $string .= "/" . $ident->{'type'};
+
+ if (defined $ident->{'type_instance'}) {
+ $string .= "-" . $ident->{'type_instance'};
+ }
+ return $string;
+}
+
=head1 COMMANDS
=over 4
=item B<GETVAL> I<Identifier>
-=item B<PUTVAL> I<Identifier> I<Valuelist>
-
-These commands follow the exact same syntax as described in
-L<collectd-unixsock(5)>.
-
=cut
sub putval {
my $id = getid(\$line);
- return if (! $id);
+ if (! $id) {
+ print STDERR $sock->{'error'} . $/;
+ return;
+ }
my ($time, @values) = split m/:/, $line;
- return $sock->putval(%$id, $time, \@values);
+ return $sock->putval(%$id, time => $time, values => \@values);
}
+=item B<PUTVAL> I<Identifier> I<Valuelist>
+
+=cut
+
sub getval {
my $sock = shift || return;
my $line = shift || return;
my $id = getid(\$line);
- return if (! $id);
+ if (! $id) {
+ print STDERR $sock->{'error'} . $/;
+ return;
+ }
my $vals = $sock->getval(%$id);
- return if (! $vals);
+ if (! $vals) {
+ print STDERR $sock->{'error'} . $/;
+ return;
+ }
foreach my $key (keys %$vals) {
print "\t$key: $vals->{$key}\n";
return 1;
}
+=item B<FLUSH> [B<timeout>=I<$timeout>] [B<plugin>=I<$plugin>[ ...]]
+
+=cut
+
+sub flush {
+ my $sock = shift || return;
+ my $line = shift;
+
+ my $res;
+
+ if (! $line) {
+ $res = $sock->flush();
+ }
+ else {
+ my %args = ();
+
+ foreach my $i (split m/ /, $line) {
+ my ($option, $value) = $i =~ m/^([^=]+)=(.+)$/;
+ next if (! ($option && $value));
+
+ if ($option eq "plugin") {
+ push @{$args{"plugins"}}, $value;
+ }
+ elsif ($option eq "timeout") {
+ $args{"timeout"} = $value;
+ }
+ else {
+ print STDERR "Invalid option \"$option\".\n";
+ return;
+ }
+ }
+
+ $res = $sock->flush(%args);
+ }
+
+ if (! $res) {
+ print STDERR $sock->{'error'} . $/;
+ return;
+ }
+ return 1;
+}
+
+=item B<LISTVAL>
+
+=cut
+
+sub listval {
+ my $sock = shift || return;
+
+ my @res;
+
+ @res = $sock->listval();
+
+ if (! @res) {
+ print STDERR $sock->{'error'} . $/;
+ return;
+ }
+
+ foreach my $ident (@res) {
+ print $ident->{'time'} . " " . putid($ident) . $/;
+ }
+ return 1;
+}
+
+=back
+
+These commands follow the exact same syntax as described in
+L<collectd-unixsock(5)>.
+
=head1 SEE ALSO
L<collectd(1)>, L<collectd-unisock(5)>
print "./rrd_filter.px -i '$InDir/$orig_filename' -m '${src_ds}:${dst_ds}' -o '$OutDir/$dest_filename'\n";
}
}
- elsif (exists ($TypeRename{$orig->{'type'}}))
+ else
+ {
+ print "cp '$InDir/$orig_filename' '$OutDir/$dest_filename'\n";
+ }
+
+ if (exists ($TypeRename{$orig->{'type'}}))
{
my $src_dses = $TypeRename{$orig->{'type'}}->{'from'};
my $dst_dses = $TypeRename{$orig->{'type'}}->{'to'};
- my @sed_prog = ();
+ print "rrdtool tune '$OutDir/$dest_filename'";
for (my $i = 0; $i < @$src_dses; $i++)
{
- push (@sed_prog, 's/^' . $src_dses->[$i] . '$/' . $dst_dses->[$i] . '/g;');
+ print " --data-source-rename "
+ . $src_dses->[$i] . ':' . $dst_dses->[$i];
}
-
- print "rrdtool dump '$InDir/$orig_filename' | sed -e '" . join (' ', @sed_prog) . "' | rrdtool restore - '$OutDir/$dest_filename'\n";
- }
- else
- {
- print "cp '$InDir/$orig_filename' '$OutDir/$dest_filename'\n";
+ print "\n";
}
}
SUBDIRS =
+if BUILD_WITH_OWN_LIBIPTC
+SUBDIRS += libiptc
+endif
if BUILD_WITH_OWN_LIBOCONFIG
SUBDIRS += liboconfig
endif
utils_cache.c utils_cache.h \
utils_ignorelist.c utils_ignorelist.h \
utils_llist.c utils_llist.h \
+ utils_tail_match.c utils_tail_match.h \
+ utils_match.c utils_match.h \
utils_mount.c utils_mount.h \
+ utils_tail.c utils_tail.h \
utils_threshold.c utils_threshold.h \
types_list.c types_list.h
collectd_DEPENDENCIES += apple_sensors.la
endif
+if BUILD_PLUGIN_ASCENT
+pkglib_LTLIBRARIES += ascent.la
+ascent_la_SOURCES = ascent.c
+ascent_la_LDFLAGS = -module -avoid-version
+ascent_la_CFLAGS = $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
+ascent_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
+collectd_LDADD += "-dlopen" apache.la
+collectd_DEPENDENCIES += ascent.la
+endif
+
if BUILD_PLUGIN_BATTERY
pkglib_LTLIBRARIES += battery.la
battery_la_SOURCES = battery.c
if BUILD_PLUGIN_CPU
pkglib_LTLIBRARIES += cpu.la
cpu_la_SOURCES = cpu.c
+cpu_la_CFLAGS =
cpu_la_LDFLAGS = -module -avoid-version
+cpu_la_LIBADD =
if BUILD_WITH_LIBKSTAT
cpu_la_LDFLAGS += -lkstat
endif
if BUILD_WITH_LIBDEVINFO
cpu_la_LDFLAGS += -ldevinfo
endif
+if BUILD_WITH_LIBSTATGRAB
+cpu_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
+cpu_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
+endif
collectd_LDADD += "-dlopen" cpu.la
collectd_DEPENDENCIES += cpu.la
endif
if BUILD_PLUGIN_DISK
pkglib_LTLIBRARIES += disk.la
disk_la_SOURCES = disk.c
+disk_la_CFLAGS =
disk_la_LDFLAGS = -module -avoid-version
+disk_la_LIBADD =
if BUILD_WITH_LIBKSTAT
disk_la_LDFLAGS += -lkstat
endif
if BUILD_WITH_LIBIOKIT
disk_la_LDFLAGS += -lIOKit
endif
+if BUILD_WITH_LIBSTATGRAB
+disk_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
+disk_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
+endif
collectd_LDADD += "-dlopen" disk.la
collectd_DEPENDENCIES += disk.la
endif
if BUILD_PLUGIN_IPTABLES
pkglib_LTLIBRARIES += iptables.la
iptables_la_SOURCES = iptables.c
-iptables_la_LDFLAGS = -module -avoid-version -liptc
+iptables_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_OWN_LIBIPTC
+iptables_la_LIBADD = libiptc/libiptc.la
+iptables_la_DEPENDENCIES = libiptc/libiptc.la
+else
+iptables_la_LDFLAGS += -liptc
+endif
collectd_LDADD += "-dlopen" iptables.la
collectd_DEPENDENCIES += iptables.la
endif
+if BUILD_PLUGIN_IPMI
+pkglib_LTLIBRARIES += ipmi.la
+ipmi_la_SOURCES = ipmi.c
+ipmi_la_CFLAGS = $(BUILD_WITH_OPENIPMI_CFLAGS)
+ipmi_la_LDFLAGS = -module -avoid-version
+ipmi_la_LIBADD = $(BUILD_WITH_OPENIPMI_LIBS)
+collectd_LDADD += "-dlopen" ipmi.la
+collectd_DEPENDENCIES += ipmi.la
+endif
+
if BUILD_PLUGIN_IPVS
pkglib_LTLIBRARIES += ipvs.la
ipvs_la_SOURCES = ipvs.c
collectd_DEPENDENCIES += ping.la
endif
+if BUILD_PLUGIN_POWERDNS
+pkglib_LTLIBRARIES += powerdns.la
+powerdns_la_SOURCES = powerdns.c
+powerdns_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" powerdns.la
+collectd_DEPENDENCIES += powerdns.la
+endif
+
if BUILD_PLUGIN_PROCESSES
pkglib_LTLIBRARIES += processes.la
processes_la_SOURCES = processes.c
collectd_DEPENDENCIES += syslog.la
endif
+if BUILD_PLUGIN_TAIL
+pkglib_LTLIBRARIES += tail.la
+tail_la_SOURCES = tail.c
+tail_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" tail.la
+collectd_DEPENDENCIES += tail.la
+endif
+
if BUILD_PLUGIN_TAPE
pkglib_LTLIBRARIES += tape.la
tape_la_SOURCES = tape.c
collectd_DEPENDENCIES += tcpconns.la
endif
+if BUILD_PLUGIN_TEAMSPEAK2
+pkglib_LTLIBRARIES += teamspeak2.la
+teamspeak2_la_SOURCES = teamspeak2.c
+teamspeak2_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" teamspeak2.la
+collectd_DEPENDENCIES += teamspeak2.la
+endif
+
if BUILD_PLUGIN_UNIXSOCK
pkglib_LTLIBRARIES += unixsock.la
-unixsock_la_SOURCES = unixsock.c utils_cmd_putval.h utils_cmd_putval.c utils_cmd_putnotif.h utils_cmd_putnotif.c
+unixsock_la_SOURCES = unixsock.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 \
+ utils_cmd_putval.h utils_cmd_putval.c \
+ utils_cmd_putnotif.h utils_cmd_putnotif.c
unixsock_la_LDFLAGS = -module -avoid-version -lpthread
collectd_LDADD += "-dlopen" unixsock.la
collectd_DEPENDENCIES += unixsock.la
if BUILD_PLUGIN_USERS
pkglib_LTLIBRARIES += users.la
users_la_SOURCES = users.c
+users_la_CFLAGS =
users_la_LDFLAGS = -module -avoid-version
+users_la_LIBADD =
+if BUILD_WITH_LIBSTATGRAB
+users_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
+users_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
+endif
collectd_LDADD += "-dlopen" users.la
collectd_DEPENDENCIES += users.la
endif
collectd_DEPENDENCIES += uuid.la
endif
+if BUILD_PLUGIN_VMEM
+pkglib_LTLIBRARIES += vmem.la
+vmem_la_SOURCES = vmem.c
+vmem_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" vmem.la
+collectd_DEPENDENCIES += vmem.la
+endif
+
if BUILD_PLUGIN_VSERVER
pkglib_LTLIBRARIES += vserver.la
vserver_la_SOURCES = vserver.c
#include <curl/curl.h>
-static char *url = NULL;
-static char *user = NULL;
-static char *pass = NULL;
-static char *cacert = NULL;
+static char *url = NULL;
+static char *user = NULL;
+static char *pass = NULL;
+static char *verify_peer = NULL;
+static char *verify_host = NULL;
+static char *cacert = NULL;
static CURL *curl = NULL;
"URL",
"User",
"Password",
+ "VerifyPeer",
+ "VerifyHost",
"CACert"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
return (config_set (&user, value));
else if (strcasecmp (key, "password") == 0)
return (config_set (&pass, value));
+ else if (strcasecmp (key, "verifypeer") == 0)
+ return (config_set (&verify_peer, value));
+ else if (strcasecmp (key, "verifyhost") == 0)
+ return (config_set (&verify_host, value));
else if (strcasecmp (key, "cacert") == 0)
return (config_set (&cacert, value));
else
curl_easy_setopt (curl, CURLOPT_URL, url);
+ if ((verify_peer == NULL) || (strcmp (verify_peer, "true") == 0))
+ {
+ curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1);
+ }
+ else
+ {
+ curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0);
+ }
+
+ if ((verify_host == NULL) || (strcmp (verify_host, "true") == 0))
+ {
+ curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2);
+ }
+ else
+ {
+ curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0);
+ }
+
if (cacert != NULL)
{
curl_easy_setopt (curl, CURLOPT_CAINFO, cacert);
--- /dev/null
+/**
+ * collectd - src/ascent.c
+ * 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:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <curl/curl.h>
+#include <libxml/parser.h>
+
+static char *races_list[] = /* {{{ */
+{
+ NULL,
+ "Human", /* 1 */
+ "Orc", /* 2 */
+ "Dwarf", /* 3 */
+ "Nightelf", /* 4 */
+ "Undead", /* 5 */
+ "Tauren", /* 6 */
+ "Gnome", /* 7 */
+ "Troll", /* 8 */
+ NULL,
+ "Bloodelf", /* 10 */
+ "Draenei" /* 11 */
+}; /* }}} */
+#define RACES_LIST_LENGTH STATIC_ARRAY_SIZE (races_list)
+
+static char *classes_list[] = /* {{{ */
+{
+ NULL,
+ "Warrior", /* 1 */
+ "Paladin", /* 2 */
+ "Hunter", /* 3 */
+ "Rogue", /* 4 */
+ "Priest", /* 5 */
+ NULL,
+ "Shaman", /* 7 */
+ "Mage", /* 8 */
+ "Warlock", /* 9 */
+ NULL,
+ "Druid" /* 11 */
+}; /* }}} */
+#define CLASSES_LIST_LENGTH STATIC_ARRAY_SIZE (classes_list)
+
+static char *genders_list[] = /* {{{ */
+{
+ "Male",
+ "Female"
+}; /* }}} */
+#define GENDERS_LIST_LENGTH STATIC_ARRAY_SIZE (genders_list)
+
+struct player_stats_s
+{
+ int races[RACES_LIST_LENGTH];
+ int classes[CLASSES_LIST_LENGTH];
+ int genders[GENDERS_LIST_LENGTH];
+ int level_sum;
+ int level_num;
+ int latency_sum;
+ int latency_num;
+};
+typedef struct player_stats_s player_stats_t;
+
+struct player_info_s
+{
+ int race;
+ int class;
+ int gender;
+ int level;
+ int latency;
+};
+typedef struct player_info_s player_info_t;
+#define PLAYER_INFO_STATIC_INIT { -1, -1, -1, -1, -1 }
+
+static char *url = NULL;
+static char *user = NULL;
+static char *pass = NULL;
+static char *cacert = NULL;
+
+static CURL *curl = NULL;
+
+static char *ascent_buffer = NULL;
+static size_t ascent_buffer_size = 0;
+static size_t ascent_buffer_fill = 0;
+static char ascent_curl_error[CURL_ERROR_SIZE];
+
+static const char *config_keys[] =
+{
+ "URL",
+ "User",
+ "Password",
+ "CACert"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static int ascent_submit_gauge (const char *plugin_instance, /* {{{ */
+ const char *type, const char *type_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 = 1;
+ vl.time = time (NULL);
+ strcpy (vl.host, hostname_g);
+ strcpy (vl.plugin, "ascent");
+
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance,
+ sizeof (vl.plugin_instance));
+
+ if (type_instance != NULL)
+ sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (type, &vl);
+ return (0);
+} /* }}} int ascent_submit_gauge */
+
+static size_t ascent_curl_callback (void *buf, size_t size, size_t nmemb, /* {{{ */
+ void *stream)
+{
+ size_t len = size * nmemb;
+
+ if (len <= 0)
+ return (len);
+
+ if ((ascent_buffer_fill + len) >= ascent_buffer_size)
+ {
+ char *temp;
+
+ temp = (char *) realloc (ascent_buffer,
+ ascent_buffer_fill + len + 1);
+ if (temp == NULL)
+ {
+ ERROR ("ascent plugin: realloc failed.");
+ return (0);
+ }
+ ascent_buffer = temp;
+ ascent_buffer_size = ascent_buffer_fill + len + 1;
+ }
+
+ memcpy (ascent_buffer + ascent_buffer_fill, (char *) buf, len);
+ ascent_buffer_fill += len;
+ ascent_buffer[ascent_buffer_fill] = 0;
+
+ return (len);
+} /* }}} size_t ascent_curl_callback */
+
+static int ascent_submit_players (player_stats_t *ps) /* {{{ */
+{
+ int i;
+ gauge_t value;
+
+ for (i = 0; i < RACES_LIST_LENGTH; i++)
+ if (races_list[i] != NULL)
+ ascent_submit_gauge ("by-race", "players", races_list[i],
+ (gauge_t) ps->races[i]);
+
+ for (i = 0; i < CLASSES_LIST_LENGTH; i++)
+ if (classes_list[i] != NULL)
+ ascent_submit_gauge ("by-class", "players", classes_list[i],
+ (gauge_t) ps->classes[i]);
+
+ for (i = 0; i < GENDERS_LIST_LENGTH; i++)
+ if (genders_list[i] != NULL)
+ ascent_submit_gauge ("by-gender", "players", genders_list[i],
+ (gauge_t) ps->genders[i]);
+
+ if (ps->level_num <= 0)
+ value = NAN;
+ else
+ value = ((double) ps->level_sum) / ((double) ps->level_num);
+ ascent_submit_gauge (NULL, "gauge", "avg-level", value);
+
+ /* Latency is in ms, but we store seconds. */
+ if (ps->latency_num <= 0)
+ value = NAN;
+ else
+ value = ((double) ps->latency_sum) / (1000.0 * ((double) ps->latency_num));
+ ascent_submit_gauge (NULL, "latency", "average", value);
+
+ return (0);
+} /* }}} int ascent_submit_players */
+
+static int ascent_account_player (player_stats_t *ps, /* {{{ */
+ player_info_t *pi)
+{
+ if (pi->race >= 0)
+ {
+ if ((pi->race >= RACES_LIST_LENGTH)
+ || (races_list[pi->race] == NULL))
+ ERROR ("ascent plugin: Ignoring invalid numeric race %i.", pi->race);
+ else
+ ps->races[pi->race]++;
+ }
+
+ if (pi->class >= 0)
+ {
+ if ((pi->class >= CLASSES_LIST_LENGTH)
+ || (classes_list[pi->class] == NULL))
+ ERROR ("ascent plugin: Ignoring invalid numeric class %i.", pi->class);
+ else
+ ps->classes[pi->class]++;
+ }
+
+ if (pi->gender >= 0)
+ {
+ if ((pi->gender >= GENDERS_LIST_LENGTH)
+ || (genders_list[pi->gender] == NULL))
+ ERROR ("ascent plugin: Ignoring invalid numeric gender %i.",
+ pi->gender);
+ else
+ ps->genders[pi->gender]++;
+ }
+
+
+ if (pi->level > 0)
+ {
+ ps->level_sum += pi->level;
+ ps->level_num++;
+ }
+
+ if (pi->latency >= 0)
+ {
+ ps->latency_sum += pi->latency;
+ ps->latency_num++;
+ }
+
+ return (0);
+} /* }}} int ascent_account_player */
+
+static int ascent_xml_submit_gauge (xmlDoc *doc, xmlNode *node, /* {{{ */
+ const char *plugin_instance, const char *type, const char *type_instance)
+{
+ char *str_ptr;
+ gauge_t value;
+
+ str_ptr = (char *) xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
+ if (str_ptr == NULL)
+ {
+ ERROR ("ascent plugin: ascent_xml_submit_gauge: xmlNodeListGetString failed.");
+ return (-1);
+ }
+
+ if (strcasecmp ("N/A", str_ptr) == 0)
+ value = NAN;
+ else
+ {
+ char *end_ptr = NULL;
+ value = strtod (str_ptr, &end_ptr);
+ if (str_ptr == end_ptr)
+ {
+ ERROR ("ascent plugin: ascent_xml_submit_gauge: strtod failed.");
+ return (-1);
+ }
+ }
+
+ return (ascent_submit_gauge (plugin_instance, type, type_instance, value));
+} /* }}} int ascent_xml_submit_gauge */
+
+static int ascent_xml_read_int (xmlDoc *doc, xmlNode *node, /* {{{ */
+ int *ret_value)
+{
+ char *str_ptr;
+ int value;
+
+ str_ptr = (char *) xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
+ if (str_ptr == NULL)
+ {
+ ERROR ("ascent plugin: ascent_xml_read_int: xmlNodeListGetString failed.");
+ return (-1);
+ }
+
+ if (strcasecmp ("N/A", str_ptr) == 0)
+ value = -1;
+ else
+ {
+ char *end_ptr = NULL;
+ value = strtol (str_ptr, &end_ptr, 0);
+ if (str_ptr == end_ptr)
+ {
+ ERROR ("ascent plugin: ascent_xml_read_int: strtol failed.");
+ return (-1);
+ }
+ }
+
+ *ret_value = value;
+ return (0);
+} /* }}} int ascent_xml_read_int */
+
+static int ascent_xml_sessions_plr (xmlDoc *doc, xmlNode *node, /* {{{ */
+ player_info_t *pi)
+{
+ xmlNode *child;
+
+ for (child = node->xmlChildrenNode; child != NULL; child = child->next)
+ {
+ if ((xmlStrcmp ((const xmlChar *) "comment", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "text", child->name) == 0))
+ /* ignore */;
+ else if (xmlStrcmp ((const xmlChar *) "race", child->name) == 0)
+ ascent_xml_read_int (doc, child, &pi->race);
+ else if (xmlStrcmp ((const xmlChar *) "class", child->name) == 0)
+ ascent_xml_read_int (doc, child, &pi->class);
+ else if (xmlStrcmp ((const xmlChar *) "gender", child->name) == 0)
+ ascent_xml_read_int (doc, child, &pi->gender);
+ else if (xmlStrcmp ((const xmlChar *) "level", child->name) == 0)
+ ascent_xml_read_int (doc, child, &pi->level);
+ else if (xmlStrcmp ((const xmlChar *) "latency", child->name) == 0)
+ ascent_xml_read_int (doc, child, &pi->latency);
+ else if ((xmlStrcmp ((const xmlChar *) "name", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "pvprank", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "map", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "areaid", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "xpos", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "ypos", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "onime", child->name) == 0))
+ /* ignore */;
+ else
+ {
+ WARNING ("ascent plugin: ascent_xml_status: Unknown tag: %s", child->name);
+ }
+ } /* for (child) */
+
+ return (0);
+} /* }}} int ascent_xml_sessions_plr */
+
+static int ascent_xml_sessions (xmlDoc *doc, xmlNode *node) /* {{{ */
+{
+ xmlNode *child;
+ player_stats_t ps;
+
+ memset (&ps, 0, sizeof (ps));
+
+ for (child = node->xmlChildrenNode; child != NULL; child = child->next)
+ {
+ if ((xmlStrcmp ((const xmlChar *) "comment", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "text", child->name) == 0))
+ /* ignore */;
+ else if (xmlStrcmp ((const xmlChar *) "plr", child->name) == 0)
+ {
+ int status;
+ player_info_t pi = PLAYER_INFO_STATIC_INIT;
+
+ status = ascent_xml_sessions_plr (doc, child, &pi);
+ if (status == 0)
+ ascent_account_player (&ps, &pi);
+ }
+ else
+ {
+ WARNING ("ascent plugin: ascent_xml_status: Unknown tag: %s", child->name);
+ }
+ } /* for (child) */
+
+ ascent_submit_players (&ps);
+
+ return (0);
+} /* }}} int ascent_xml_sessions */
+
+static int ascent_xml_status (xmlDoc *doc, xmlNode *node) /* {{{ */
+{
+ xmlNode *child;
+
+ for (child = node->xmlChildrenNode; child != NULL; child = child->next)
+ {
+ if ((xmlStrcmp ((const xmlChar *) "comment", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "text", child->name) == 0))
+ /* ignore */;
+ else if (xmlStrcmp ((const xmlChar *) "alliance", child->name) == 0)
+ ascent_xml_submit_gauge (doc, child, NULL, "players", "alliance");
+ else if (xmlStrcmp ((const xmlChar *) "horde", child->name) == 0)
+ ascent_xml_submit_gauge (doc, child, NULL, "players", "horde");
+ else if (xmlStrcmp ((const xmlChar *) "qplayers", child->name) == 0)
+ ascent_xml_submit_gauge (doc, child, NULL, "players", "queued");
+ else if ((xmlStrcmp ((const xmlChar *) "acceptedconns", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "avglat", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "cdbquerysize", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "cpu", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "fthreads", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "gmcount", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "lastupdate", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "ontime", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "oplayers", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "peakcount", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "platform", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "ram", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "threads", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "uptime", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "wdbquerysize", child->name) == 0))
+ /* ignore */;
+ else
+ {
+ WARNING ("ascent plugin: ascent_xml_status: Unknown tag: %s", child->name);
+ }
+ } /* for (child) */
+
+ return (0);
+} /* }}} int ascent_xml_status */
+
+static int ascent_xml (const char *data) /* {{{ */
+{
+ xmlDoc *doc;
+ xmlNode *cur;
+ xmlNode *child;
+
+#if 0
+ doc = xmlParseMemory (data, strlen (data),
+ /* URL = */ "ascent.xml",
+ /* encoding = */ NULL,
+ /* options = */ 0);
+#else
+ doc = xmlParseMemory (data, strlen (data));
+#endif
+ if (doc == NULL)
+ {
+ ERROR ("ascent plugin: xmlParseMemory failed.");
+ return (-1);
+ }
+
+ cur = xmlDocGetRootElement (doc);
+ if (cur == NULL)
+ {
+ ERROR ("ascent plugin: XML document is empty.");
+ xmlFreeDoc (doc);
+ return (-1);
+ }
+
+ if (xmlStrcmp ((const xmlChar *) "serverpage", cur->name) != 0)
+ {
+ ERROR ("ascent plugin: XML root element is not \"serverpage\".");
+ xmlFreeDoc (doc);
+ return (-1);
+ }
+
+ for (child = cur->xmlChildrenNode; child != NULL; child = child->next)
+ {
+ if ((xmlStrcmp ((const xmlChar *) "comment", child->name) == 0)
+ || (xmlStrcmp ((const xmlChar *) "text", child->name) == 0))
+ /* ignore */;
+ else if (xmlStrcmp ((const xmlChar *) "status", child->name) == 0)
+ ascent_xml_status (doc, child);
+ else if (xmlStrcmp ((const xmlChar *) "instances", child->name) == 0)
+ /* ignore for now */;
+ else if (xmlStrcmp ((const xmlChar *) "gms", child->name) == 0)
+ /* ignore for now */;
+ else if (xmlStrcmp ((const xmlChar *) "sessions", child->name) == 0)
+ ascent_xml_sessions (doc, child);
+ else
+ {
+ WARNING ("ascent plugin: ascent_xml: Unknown tag: %s", child->name);
+ }
+ } /* for (child) */
+
+ xmlFreeDoc (doc);
+ return (0);
+} /* }}} int ascent_xml */
+
+static int config_set (char **var, const char *value) /* {{{ */
+{
+ if (*var != NULL)
+ {
+ free (*var);
+ *var = NULL;
+ }
+
+ if ((*var = strdup (value)) == NULL)
+ return (1);
+ else
+ return (0);
+} /* }}} int config_set */
+
+static int ascent_config (const char *key, const char *value) /* {{{ */
+{
+ if (strcasecmp (key, "URL") == 0)
+ return (config_set (&url, value));
+ else if (strcasecmp (key, "User") == 0)
+ return (config_set (&user, value));
+ else if (strcasecmp (key, "Password") == 0)
+ return (config_set (&pass, value));
+ else if (strcasecmp (key, "CACert") == 0)
+ return (config_set (&cacert, value));
+ else
+ return (-1);
+} /* }}} int ascent_config */
+
+static int ascent_init (void) /* {{{ */
+{
+ static char credentials[1024];
+
+ if (url == NULL)
+ {
+ WARNING ("ascent plugin: ascent_init: No URL configured, "
+ "returning an error.");
+ return (-1);
+ }
+
+ if (curl != NULL)
+ {
+ curl_easy_cleanup (curl);
+ }
+
+ if ((curl = curl_easy_init ()) == NULL)
+ {
+ ERROR ("ascent plugin: ascent_init: curl_easy_init failed.");
+ return (-1);
+ }
+
+ curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ascent_curl_callback);
+ curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+ curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, ascent_curl_error);
+
+ if (user != NULL)
+ {
+ int status;
+
+ status = snprintf (credentials, sizeof (credentials), "%s:%s",
+ user, (pass == NULL) ? "" : pass);
+ if (status >= sizeof (credentials))
+ {
+ ERROR ("ascent plugin: ascent_init: Returning an error because the "
+ "credentials have been truncated.");
+ return (-1);
+ }
+ credentials[sizeof (credentials) - 1] = '\0';
+
+ curl_easy_setopt (curl, CURLOPT_USERPWD, credentials);
+ }
+
+ curl_easy_setopt (curl, CURLOPT_URL, url);
+
+ if (cacert != NULL)
+ curl_easy_setopt (curl, CURLOPT_CAINFO, cacert);
+
+ return (0);
+} /* }}} int ascent_init */
+
+static int ascent_read (void) /* {{{ */
+{
+ int status;
+
+ if (curl == NULL)
+ {
+ ERROR ("ascent plugin: I don't have a CURL object.");
+ return (-1);
+ }
+
+ if (url == NULL)
+ {
+ ERROR ("ascent plugin: No URL has been configured.");
+ return (-1);
+ }
+
+ ascent_buffer_fill = 0;
+ if (curl_easy_perform (curl) != 0)
+ {
+ ERROR ("ascent plugin: curl_easy_perform failed: %s",
+ ascent_curl_error);
+ return (-1);
+ }
+
+ status = ascent_xml (ascent_buffer);
+ if (status != 0)
+ return (-1);
+ else
+ return (0);
+} /* }}} int ascent_read */
+
+void module_register (void)
+{
+ plugin_register_config ("ascent", ascent_config, config_keys, config_keys_num);
+ plugin_register_init ("ascent", ascent_init);
+ plugin_register_read ("ascent", ascent_read);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 ts=8 et fdm=marker : */
struct sockaddr_un sa;
int status;
int fd;
- FILE *fh;
+ FILE *fh_in, *fh_out;
char buffer[4096];
int values_num;
return (-1);
}
- fh = fdopen (fd, "r+");
- if (fh == NULL)
+ fh_in = fdopen (fd, "r");
+ if (fh_in == NULL)
{
fprintf (stderr, "fdopen failed: %s\n",
strerror (errno));
return (-1);
}
- fprintf (fh, "GETVAL %s/%s\n", hostname_g, value_string_g);
- fflush (fh);
+ fh_out = fdopen (fd, "w");
+ if (fh_out == NULL)
+ {
+ fprintf (stderr, "fdopen failed: %s\n",
+ strerror (errno));
+ fclose (fh_in);
+ return (-1);
+ }
- if (fgets (buffer, sizeof (buffer), fh) == NULL)
+ fprintf (fh_out, "GETVAL %s/%s\n", hostname_g, value_string_g);
+ fflush (fh_out);
+
+ if (fgets (buffer, sizeof (buffer), fh_in) == NULL)
{
fprintf (stderr, "fgets failed: %s\n",
strerror (errno));
- close (fd);
+ fclose (fh_in);
+ fclose (fh_out);
return (-1);
}
- close (fd); fd = -1;
- values_num = atoi (buffer);
- if (values_num < 1)
- return (-1);
+ {
+ char *ptr = strchr (buffer, ' ');
+
+ if (ptr != NULL)
+ *ptr = '\0';
+
+ values_num = atoi (buffer);
+ if (values_num < 1)
+ return (-1);
+ }
values = (double *) malloc (values_num * sizeof (double));
if (values == NULL)
return (-1);
}
+ i = 0;
+ while (fgets (buffer, sizeof (buffer), fh_in) != NULL)
{
- char *ptr = strchr (buffer, ' ') + 1;
char *key;
char *value;
- i = 0;
- while ((key = strtok (ptr, " \t")) != NULL)
- {
- ptr = NULL;
- value = strchr (key, '=');
- if (value == NULL)
- continue;
- *value = '\0'; value++;
+ key = buffer;
- if (ignore_ds (key) != 0)
- continue;
+ value = strchr (key, '=');
+ if (value == NULL)
+ continue;
+ *value = '\0'; value++;
- values_names[i] = strdup (key);
- values[i] = atof (value);
+ if (ignore_ds (key) != 0)
+ continue;
- i++;
- if (i >= values_num)
- break;
- }
- values_num = i;
+ values_names[i] = strdup (key);
+ values[i] = atof (value);
+
+ i++;
+ if (i >= values_num)
+ break;
}
+ values_num = i;
+
+ fclose (fh_in); fh_in = NULL; fd = -1;
+ fclose (fh_out); fh_out = NULL;
*ret_values_num = values_num;
*ret_values = values;
of these callback functions and initializing code which registers the
functions with collectd. See the section "EXAMPLES" below for a really basic
example. The following types of B<callback functions> are known to collectd
-(all of these are optional):
+(all of them are optional):
=over 4
This type of function is used to write the dispatched values. It is called
once for each call to B<plugin_dispatch_values>.
+=item flush functions
+
+This type of function is used to flush internal caches of plugins. It is
+usually triggered by the user only. Any plugin which caches data before
+writing it to disk should provide this kind of callback function.
+
=item log functions
This type of function is used to pass messages of plugins or the daemon itself
=item TYPE_WRITE
+=item TYPE_FLUSH
+
=item TYPE_LOG
=item TYPE_NOTIF
=item TYPE_SHUTDOWN
-No arguments are passed
+No arguments are passed.
=item TYPE_WRITE
The arguments passed are I<type>, I<data-set>, and I<value-list>. I<type> is a
string. For the layout of I<data-set> and I<value-list> see above.
+=item TYPE_FLUSH
+
+The only argument passed is I<timeout> which indicates that only data older
+than I<timeout> seconds is to be flushed.
+
=item TYPE_LOG
The arguments are I<log-level> and I<message>. The log level is small for
type, data-set and value-list is passed to all write-callbacks that are
registered with the daemon.
+=item B<plugin_flush> ([B<timeout> => I<timeout>,] [B<plugins> => 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.
+
+=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>".
+
+=item B<plugin_flush_all> (I<timeout>)
+
+This is identical to using "plugin_flush (timeout =E<gt> I<timeout>)".
+
=item B<plugin_dispatch_notification> (I<notification>)
Submits a I<notification> to the daemon which will then pass it to all
=item B<plugin_dispatch_values> ()
+=item B<plugin_flush> ()
+
+=item B<plugin_flush_one> ()
+
+=item B<plugin_flush_all> ()
+
=item B<plugin_dispatch_notification> ()
=item B<plugin_log> ()
=item B<TYPE_WRITE>
+=item B<TYPE_FLUSH>
+
=item B<TYPE_SHUTDOWN>
=item B<TYPE_LOG>
=back
+=head1 KNOWN BUGS
+
+=over 4
+
+=item
+
+Currently, it is not possible to flush a single Perl plugin only. You can
+either flush all Perl plugins or none at all and you have to use C<perl> as
+plugin name when doing so.
+
+=back
+
=head1 SEE ALSO
L<collectd(1)>,
connections. Once a connection is established the client can send commands to
the daemon which it will answer, if it understand them.
+In general the plugin answers with a status line of the following form:
+
+I<Status> I<Message>
+
+If I<Status> is greater than or equal to zero the message indicates success,
+if I<Status> is less than zero the message indicates failure. I<Message> is a
+human-readable string that further describes the return value.
+
+On success, I<Status> furthermore indicates the number of subsequent lines of
+output (not including the status line). Each such lines usually contains a
+single return value. See the description of each command for details.
+
The following commands are implemented:
=over 4
=item B<GETVAL> I<Identifier>
If the value identified by I<Identifier> (see below) is found the complete
-value-list is returned. The response is a space separated list of
-name-value-pairs:
-
-I<num> I<name>B<=>I<value>[ I<name>B<=>I<value>[ ...]]
-
-If I<num> is less then zero, an error occurred. Otherwise it contains the
-number of values that follow. Each value is of the form I<name>B<=>I<value>.
+value-list is returned. The response is a list of name-value-pairs, each pair
+on its own line (the number of lines is indicated by the status line - see
+above). Each name-value-pair is of the form I<name>B<=>I<value>.
Counter-values are converted to a rate, e.E<nbsp>g. bytes per second.
Undefined values are returned as B<NaN>.
Example:
-> | GETVAL myhost/cpu-0/cpu-user
- <- | 1 value=1.260000e+00
+ <- | 1 Value found
+ <- | value=1.260000e+00
=item B<LISTVAL>
Returns a list of the values available in the value cache together with the
time of the last update, so that querying applications can issue a B<GETVAL>
-command for the values that have changed.
-
-The first line's status number is the number of identifiers returned or less
-than zero if an error occurred. Each of the following lines contains the
-update time as an epoch value and the identifier, separated by a space.
+command for the values that have changed. Each return value consists of the
+update time as an epoch value and the identifier, separated by a space. The
+update time is the time of the last value, as provided by the collecting
+instance and may be very different from the time the server considers to be
+"now".
Example:
-> | LISTVAL
<- | 69 Values found
- <- | 1182204284 leeloo/cpu-0/cpu-idle
- <- | 1182204284 leeloo/cpu-0/cpu-nice
- <- | 1182204284 leeloo/cpu-0/cpu-system
- <- | 1182204284 leeloo/cpu-0/cpu-user
+ <- | 1182204284 myhost/cpu-0/cpu-idle
+ <- | 1182204284 myhost/cpu-0/cpu-nice
+ <- | 1182204284 myhost/cpu-0/cpu-system
+ <- | 1182204284 myhost/cpu-0/cpu-user
...
=item B<PUTVAL> I<Identifier> [I<OptionList>] I<Valuelist>
-> | PUTNOTIF type=temperature severity=warning time=1201094702 message=The roof is on fire!
<- | 0 Success
+=item B<FLUSH> [B<timeout=>I<Timeout>] [B<plugin=>I<Plugin> [...]]
+
+Flushes all cached data older than I<Timeout> seconds. If no timeout has been
+specified, it defaults to -1 which causes all data to be flushed. B<timeout>
+may be specified multiple times - each occurrence applies to plugins listed
+afterwards.
+
+If specified, only specific plugins are flushed. Otherwise all plugins
+providing a flush callback are flushed.
+
+Example:
+ -> | FLUSH
+ <- | 0 Done
+
=back
=head2 Identifiers
myhost/memory/memory-used
myhost/disk-sda/disk_octets
-=head2 Return values
-
-Unless otherwise noted the plugin answers with a line of the following form:
-
-I<Num> I<Message>
-
-If I<Num> is zero the message indicates success, if I<Num> is non-zero the
-message indicates failure. I<Message> is a human-readable string that describes
-the return value further.
-
-Commands that return values may use I<Num> to return the number of values that
-follow, such as the B<GETVAL> command. These commands usually return a negative
-value on failure and never return zero.
-
=head1 ABSTRACTION LAYER
B<collectd> ships the Perl-Module L<Collectd::Unixsock> which
#include <sys/socket.h>
#include <netdb.h>
+#include <pthread.h>
+
#include "plugin.h"
#include "configfile.h"
static int loop = 0;
-static void sigIntHandler (int signal)
+static void *do_flush (void *arg)
+{
+ INFO ("Flushing all data.");
+ plugin_flush_all (-1);
+ INFO ("Finished flushing all data.");
+ pthread_exit (NULL);
+ return NULL;
+}
+
+static void sig_int_handler (int signal)
{
loop++;
}
-static void sigTermHandler (int signal)
+static void sig_term_handler (int signal)
{
loop++;
}
+static void sig_usr1_handler (int signal)
+{
+ pthread_t thread;
+ pthread_attr_t attr;
+
+ /* flushing the data might take a while,
+ * so it should be done asynchronously */
+ pthread_attr_init (&attr);
+ pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+ pthread_create (&thread, &attr, do_flush, NULL);
+}
+
static int init_hostname (void)
{
const char *str;
" General:\n"
" -C <file> Configuration file.\n"
" Default: "CONFIGFILE"\n"
+ " -t Test config and exit.\n"
" -P <file> PID-file.\n"
" Default: "PIDFILE"\n"
#if COLLECT_DAEMON
" -f Don't fork to the background.\n"
#endif
+ " -h Display help (this message)\n"
"\nBuiltin defaults:\n"
" Config-File "CONFIGFILE"\n"
" PID-File "PIDFILE"\n"
int main (int argc, char **argv)
{
- struct sigaction sigIntAction;
- struct sigaction sigTermAction;
+ struct sigaction sig_int_action;
+ struct sigaction sig_term_action;
+ struct sigaction sig_usr1_action;
+ struct sigaction sig_pipe_action;
char *configfile = CONFIGFILE;
int test_config = 0;
const char *basedir;
#if COLLECT_DAEMON
- struct sigaction sigChldAction;
+ struct sigaction sig_chld_action;
pid_t pid;
int daemonize = 1;
#endif
/*
* fork off child
*/
- memset (&sigChldAction, '\0', sizeof (sigChldAction));
- sigChldAction.sa_handler = SIG_IGN;
- sigaction (SIGCHLD, &sigChldAction, NULL);
+ memset (&sig_chld_action, '\0', sizeof (sig_chld_action));
+ sig_chld_action.sa_handler = SIG_IGN;
+ sigaction (SIGCHLD, &sig_chld_action, NULL);
if (daemonize)
{
} /* if (daemonize) */
#endif /* COLLECT_DAEMON */
+ memset (&sig_pipe_action, '\0', sizeof (sig_pipe_action));
+ sig_pipe_action.sa_handler = SIG_IGN;
+ sigaction (SIGPIPE, &sig_pipe_action, NULL);
+
/*
* install signal handlers
*/
- memset (&sigIntAction, '\0', sizeof (sigIntAction));
- sigIntAction.sa_handler = sigIntHandler;
- sigaction (SIGINT, &sigIntAction, NULL);
+ memset (&sig_int_action, '\0', sizeof (sig_int_action));
+ sig_int_action.sa_handler = sig_int_handler;
+ if (0 != sigaction (SIGINT, &sig_int_action, NULL)) {
+ char errbuf[1024];
+ ERROR ("Error: Failed to install a signal handler for signal INT: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (1);
+ }
+
+ memset (&sig_term_action, '\0', sizeof (sig_term_action));
+ sig_term_action.sa_handler = sig_term_handler;
+ if (0 != sigaction (SIGTERM, &sig_term_action, NULL)) {
+ char errbuf[1024];
+ ERROR ("Error: Failed to install a signal handler for signal TERM: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (1);
+ }
- memset (&sigTermAction, '\0', sizeof (sigTermAction));
- sigTermAction.sa_handler = sigTermHandler;
- sigaction (SIGTERM, &sigTermAction, NULL);
+ memset (&sig_usr1_action, '\0', sizeof (sig_usr1_action));
+ sig_usr1_action.sa_handler = sig_usr1_handler;
+ if (0 != sigaction (SIGUSR1, &sig_usr1_action, NULL)) {
+ char errbuf[1024];
+ ERROR ("Error: Failed to install a signal handler for signal USR1: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (1);
+ }
/*
* run the actual loops
@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors
+@BUILD_PLUGIN_ASCENT_TRUE@LoadPlugin ascent
@BUILD_PLUGIN_BATTERY_TRUE@LoadPlugin battery
@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq
@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
@BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables
+@BUILD_PLUGIN_IPMI_TRUE@LoadPlugin ipmi
@BUILD_PLUGIN_IPVS_TRUE@LoadPlugin ipvs
@BUILD_PLUGIN_IRQ_TRUE@LoadPlugin irq
@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt
@BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut
@BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl
@BUILD_PLUGIN_PING_TRUE@LoadPlugin ping
+@BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns
@BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes
@BUILD_PLUGIN_RRDTOOL_TRUE@LoadPlugin rrdtool
@BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors
@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
+@BUILD_PLUGIN_TEAMSPEAK2_TRUE@LoadPlugin teamspeak2
@BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock
@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
+@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless
@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms
# Port "3551"
#</Plugin>
+#<Plugin ascent>
+# URL "http://localhost/ascent/status/"
+# User "www-user"
+# Password "secret"
+# CACert "/etc/ssl/ca.crt"
+#</Plugin>
+
#<Plugin csv>
# DataDir "@prefix@/var/lib/@PACKAGE_NAME@/csv"
# StoreRates false
# IgnoreSelected false
#</Plugin>
+#<Plugin df>
+# Disk "/^[hs]d[a-f][0-9]?$/"
+# IgnoreSelected false
+#</Plugin>
+
#<Plugin dns>
# Interface "eth0"
# IgnoreSource "192.168.0.1"
# TTL 255
#</Plugin>
+#<Plugin powerdns>
+# <Server "server_name">
+# Collect "latency"
+# Collect "udp-answers" "udp-queries"
+# Socket "/var/run/pdns.controlsocket"
+# </Server>
+# <Recursor "recursor_name">
+# Collect "questions"
+# Collect "cache-hits" "cache-misses"
+# Socket "/var/run/pdns_recursor.controlsocket"
+# </Recursor>
+# LocalSocket "/opt/collectd/var/run/collectd-powerdns"
+#</Plugin>
+
#<Plugin processes>
# Process "name"
#</Plugin>
# LogLevel info
#</Plugin>
+#<Plugin "tail">
+# <File "/var/log/exim4/mainlog">
+# Instance "exim"
+# <Match>
+# Regex "S=([1-9][0-9]*)"
+# DSType "CounterAdd"
+# Type "ipt_bytes"
+# Instance "total"
+# </Match>
+# <Match>
+# Regex "\\<R=local_user\\>"
+# DSType "CounterInc"
+# Type "email_count"
+# Instance "local_user"
+# </Match>
+# </File>
+#</Plugin>
+
#<Plugin tcpconns>
# ListeningPorts false
# LocalPort "25"
# RemotePort "25"
#</Plugin>
+#<Plugin teamspeak2>
+# Host "127.0.0.1"
+# Port "51234"
+# Server "8767"
+#</Plugin>
+
#<Plugin unixsock>
# SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock"
# SocketGroup "collectd"
# UUIDFile "/etc/uuid"
#</Plugin>
+#<Plugin vmem>
+# Verbose false
+#</Plugin>
+
Optional password needed for authentication.
+=item B<VerifyPeer> B<true|false>
+
+Enable or disable peer SSL certificate verification. See
+L<http://curl.haxx.se/docs/sslcerts.html> for details. Enabled by default.
+
+=item B<VerifyHost> B<true|false>
+
+Enable or disable peer host name verification. If enabled, the plugin checks
+if the C<Common Name> or a C<Subject Alternate Name> field of the SSL
+certificate matches the host name provided by the B<URL> option. If this
+identity check fails, the connection is aborted. Obviously, only works when
+connecting to a SSL enabled server. Enabled by default.
+
=item B<CACert> I<File>
File that holds one or more SSL certificates. If you want to use HTTPS you will
=back
+=head2 Plugin C<ascent>
+
+This plugin collects information about an Ascent server, a free server for the
+"World of Warcraft" game. This plugin gathers the information by fetching the
+XML status page using C<libcurl> and parses it using C<libxml2>.
+
+The configuration options are the same as for the C<apache> plugin above:
+
+=over 4
+
+=item B<URL> I<http://localhost/ascent/status/>
+
+Sets the URL of the XML status output.
+
+=item B<User> I<Username>
+
+Optional user name needed for authentication.
+
+=item B<Password> I<Password>
+
+Optional password needed for authentication.
+
+=item B<CACert> I<File>
+
+File that holds one or more SSL certificates. If you want to use HTTPS you will
+possibly need this option. What CA certificates come bundled with C<libcurl>
+and are checked by default depends on the distribution you use.
+
+=back
+
=head2 Plugin C<cpufreq>
This plugin doesn't have any options. It reads
=back
+=head2 Plugin C<disk>
+
+The C<disk> plugin collects information about the usage of physical disks and
+logical disks (partitions). Values collected are the number of octets written
+to and read from a disk or partition, the number of read/write operations
+issued to the disk and a rather complex "time" it took for these commands to be
+issued.
+
+Using the following two options you can ignore some disks or configure the
+collection only of specific disks.
+
+=over 4
+
+=item B<Disk> I<Name>
+
+Select the disk I<Name>. Whether it is collected or ignored depends on the
+B<IgnoreSelected> setting, see below. As with other plugins that use the
+daemon's ignorelist functionality, a string that starts and ends with a slash
+is interpreted as a regular expression. Examples:
+
+ Disk "sdd"
+ Disk "/hda[34]/"
+
+=item B<IgnoreSelected> B<true>|B<false>
+
+Sets whether selected disks, i.E<nbsp>e. the ones matches by any of the B<Disk>
+statements, are ignored or if all other disks are ignored. The behavior
+(hopefully) is intuitive: If no B<Disk> option is configured, all disks are
+collected. If at least one B<Disk> option is given and no B<IgnoreSelected> or
+set to B<false>, B<only> matching disks will be collected. If B<IgnoreSelected>
+is set to B<true>, all disks are collected B<except> the ones matched.
+
+=back
+
=head2 Plugin C<dns>
=over 4
Optional password needed for authentication.
+=item B<VerifyPeer> B<true|false>
+
+Enable or disable peer SSL certificate verification. See
+L<http://curl.haxx.se/docs/sslcerts.html> for details. Enabled by default.
+
+=item B<VerifyHost> B<true|false>
+
+Enable or disable peer host name verification. If enabled, the plugin checks
+if the C<Common Name> or a C<Subject Alternate Name> field of the SSL
+certificate matches the host name provided by the B<URL> option. If this
+identity check fails, the connection is aborted. Obviously, only works when
+connecting to a SSL enabled server. Enabled by default.
+
=item B<CACert> I<File>
File that holds one or more SSL certificates. If you want to use HTTPS you will
=back
+=head2 Plugin C<powerdns>
+
+The C<powerdns> plugin queries statistics from an authoritative PowerDNS
+nameserver and/or a PowerDNS recursor. Since both offer a wide variety of
+values, many of which are probably meaningless to most users, but may be useful
+for some. So you may chose which values to collect, but if you don't, some
+reasonable defaults will be collected.
+
+ <Plugin "powerdns">
+ <Server "server_name">
+ Collect "latency"
+ Collect "udp-answers" "udp-queries"
+ Socket "/var/run/pdns.controlsocket"
+ </Server>
+ <Recursor "recursor_name">
+ Collect "questions"
+ Collect "cache-hits" "cache-misses"
+ Socket "/var/run/pdns_recursor.controlsocket"
+ </Recursor>
+ LocalSocket "/opt/collectd/var/run/collectd-powerdns"
+ </Plugin>
+
+=over 4
+
+=item B<Server> and B<Recursor> block
+
+The B<Server> block defines one authoritative server to query, the B<Recursor>
+does the same for an recursing server. The possible options in both blocks are
+the same, though. The argument defines a name for the serverE<nbsp>/ recursor
+and is required.
+
+=over 4
+
+=item B<Collect> I<Field>
+
+Using the B<Collect> statement you can select which values to collect. Here,
+you specify the name of the values as used by the PowerDNS servers, e.E<nbsp>g.
+C<dlg-only-drops>, C<answers10-100>.
+
+The method of getting the values differs for B<Server> and B<Recursor> blocks:
+When querying the server a C<SHOW *> command is issued in any case, because
+that's the only way of getting multiple values out of the server at once.
+collectd then picks out the values you have selected. When querying the
+recursor, a command is generated to query exactly these values. So if you
+specify invalid fields when querying the recursor, a syntax error may be
+returned by the daemon and collectd may not collect any values at all.
+
+If no B<Collect> statement is given, the following B<Server> values will be
+collected:
+
+=over 4
+
+=item latency
+
+=item packetcache-hit
+
+=item packetcache-miss
+
+=item packetcache-size
+
+=item query-cache-hit
+
+=item query-cache-miss
+
+=item recursing-answers
+
+=item recursing-questions
+
+=item tcp-answers
+
+=item tcp-queries
+
+=item udp-answers
+
+=item udp-queries
+
+=back
+
+The following B<Recursor> values will be collected by default:
+
+=over 4
+
+=item noerror-answers
+
+=item nxdomain-answers
+
+=item servfail-answers
+
+=item sys-msec
+
+=item user-msec
+
+=item qa-latency
+
+=item cache-entries
+
+=item cache-hits
+
+=item cache-misses
+
+=item questions
+
+=back
+
+Please note that up to that point collectd doesn't know what values are
+available on the server and values that are added do not need a change of the
+mechanism so far. However, the values must be mapped to collectd's naming
+scheme, which is done using a lookup table that lists all known values. If
+values are added in the future and collectd does not know about them, you will
+get an error much like this:
+
+ powerdns plugin: submit: Not found in lookup table: foobar = 42
+
+In this case please file a bug report with the collectd team.
+
+=item B<Socket> I<Path>
+
+Configures the path to the UNIX domain socket to be used when connecting to the
+daemon. By default C</var/run/pdns.controlsocket> will be used for an
+authoritative server and C</var/run/pdns_recursor.controlsocket> will be used
+for the recursor.
+
+=back
+
+=item B<LocalSocket> I<Path>
+
+Querying the recursor is done using UDP. When using UDP over UNIX domain
+sockets, the client socket needs a name in the file system, too. You can set
+this local name to I<Path> using the B<LocalSocket> option. The default is
+C<I<prefix>/var/run/collectd-powerdns>.
+
+=back
+
=head2 Plugin C<processes>
=over 4
=back
+=head2 Plugin C<tail>
+
+The C<tail plugin> plugins follows logfiles, just like L<tail(1)> does, parses
+each line and dispatches found values. What is matched can be configured by the
+user using (extended) regular expressions, as described in L<regex(7)>.
+
+ <Plugin "tail">
+ <File "/var/log/exim4/mainlog">
+ Instance "exim"
+ <Match>
+ Regex "S=([1-9][0-9]*)"
+ DSType "CounterAdd"
+ Type "ipt_bytes"
+ Instance "total"
+ </Match>
+ <Match>
+ Regex "\\<R=local_user\\>"
+ DSType "CounterInc"
+ Type "email_count"
+ Instance "local_user"
+ </Match>
+ </File>
+ </Plugin>
+
+The config consists of one or more B<File> blocks, each of which configures one
+logfile to parse. Within each B<File> block, there are one or more B<Match>
+blocks, which configure a regular expression to search for.
+
+The B<Instance> option in the B<File> block may be used to set the plugin
+instance. So in the above example the plugin name C<tail-foo> would be used.
+This plugin instance is for all B<Match> blocks that B<follow> it, until the
+next B<Instance> option. This way you can extract several plugin instances from
+one logfile, handy when parsing syslog and the like.
+
+Each B<Match> block has the following options to describe how the match should
+be performed:
+
+=over 4
+
+=item B<Regex> I<regex>
+
+Sets the regular expression to use for matching against a line. The first
+subexpression has to match something that can be turned into a number by
+L<strtoll(3)> or L<strtod(3)>, depending on the value of C<CounterAdd>, see
+below. Because B<extended> regular expressions are used, you do not need to use
+backslashes for subexpressions! If in doubt, please consult L<regex(7)>. Due to
+collectd's config parsing you need to escape backslashes, though. So if you
+want to match literal parentheses you need to do the following:
+
+ Regex "SPAM \\(Score: (-?[0-9]+\\.[0-9]+)\\)"
+
+=item B<DSType> I<Type>
+
+Sets how the values are cumulated. I<Type> is one of:
+
+=over 4
+
+=item B<GaugeAverage>
+
+Calculate the average.
+
+=item B<GaugeMin>
+
+Use the smallest number only.
+
+=item B<GaugeMax>
+
+Use the greatest number only.
+
+=item B<GaugeLast>
+
+Use the last number found.
+
+=item B<CounterSet>
+
+The matched number is a counter. Simply sets the internal counter to this
+value.
+
+=item B<CounterAdd>
+
+Add the matched value to the internal counter.
+
+=item B<CounterInc>
+
+Increase the internal counter by one. This B<DSType> is the only one that does
+not use the matched subexpression, but simply counts the number of matched
+lines. Thus, you may use a regular expression without submatch in this case.
+
+=back
+
+As you'd expect the B<Gauge*> types interpret the submatch as a floating point
+number, using L<strtod(3)>. The B<CounterSet> and B<CounterAdd> interpret the
+submatch as an integer using L<strtoll(3)>. B<CounterInc> does not use the
+submatch at all and it may be omitted in this case.
+
+=item B<Type> I<Type>
+
+Sets the type used to dispatch this value. Detailed information about types and
+their configuration can be found in L<types.db(5)>.
+
+=item B<Instance> I<TypeInstance>
+
+This optional setting sets the type instance to use.
+
+=back
+
+=head2 Plugin C<teamspeak2>
+
+The C<teamspeak2 plugin> connects to the query port of a teamspeak2 server and
+polls interesting global and virtual server data. The plugin can query only one
+physical server but unlimited virtual servers. You can use the following
+options to configure it:
+
+=over 4
+
+=item B<Host> I<hostname/ip>
+
+The hostname or ip which identifies the physical server.
+Default: 127.0.0.1
+
+=item B<Port> I<port>
+
+The query port of the physical server. This needs to be a string.
+Default: "51234"
+
+=item B<Server> I<port>
+
+This option has to be added once for every virtual server the plugin should
+query. If you want to query the virtual server on port 8767 this is what the
+option would look like:
+
+ Server "8767"
+
+This option, although numeric, needs to be a string, i.E<nbsp>e. you B<must>
+use quotes around it! If no such statement is given only global information
+will be collected.
+
=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
=back
+=head2 Plugin C<vmem>
+
+The C<vmem> plugin collects information about the usage of virtual memory.
+Since the statistics provided by the Linux kernel are very detailed, they are
+collected very detailed. However, to get all the details, you have to switch
+them on manually. Most people just want an overview over, such as the number of
+pages read from swap space.
+
+=over 4
+
+=item B<Verbose> B<true>|B<false>
+
+Enables verbose collection of information. This will start collecting page
+"actions", e.E<nbsp>g. page allocations, (de)activations, steals and so on.
+Part of these statistics are collected on a "per zone" basis.
+
+=back
+
=head2 Plugin C<vserver>
This plugin doesn't have any options. B<VServer> support is only available for
Instance "eth0"
<Type "if_octets">
FailureMax 10000000
+ DataSource "rx"
</Type>
</Plugin>
be created. If the value is less than B<WarningMin> but greater than (or equal
to) B<FailureMin> a B<WARNING> notification will be created.
+=item B<DataSource> I<DSName>
+
+Some data sets have more than one "data source". Interesting examples are the
+C<if_octets> data set, which has received (C<rx>) and sent (C<tx>) bytes and
+the C<disk_ops> data set, which holds C<read> and C<write> operations. The
+system load data set, C<load>, even has three data sources: C<shortterm>,
+C<midterm>, and C<longterm>.
+
+Normally, all data sources are checked against a configured threshold. If this
+is undesirable, or if you want to specify different limits for each data
+source, you can use the B<DataSource> option to have a threshold apply only to
+one data source.
+
=item B<Invert> B<true>|B<false>
If set to B<true> the range of acceptable values is inverted, i.E<nbsp>e.
detail. In particular those are L<collectd-email(5)>, L<collectd-exec(5)>,
L<collectd-perl(5)>, L<collectd-snmp(5)>, and L<collectd-unixsock(5)>
+=head1 SIGNALS
+
+B<collectd> accepts the following signals:
+
+=over 4
+
+=item B<SIGINT>, B<SIGTERM>
+
+These signals cause B<collectd> to shut down all plugins and terminate.
+
+=item B<SIGUSR1>
+
+This signal causes B<collectd> to signal all plugins to flush data from
+internal caches. E.E<nbsp>g. the C<rrdtool plugin> will write all pending data
+to the RRD files. This is the same as using the C<FLUSH -1> command of the
+C<unixsock plugin>.
+
=head1 SEE ALSO
L<collectd.conf(5)>,
# endif
#endif /* HAVE_SYSCTLBYNAME */
-#if !PROCESSOR_CPU_LOAD_INFO && !KERNEL_LINUX && !HAVE_LIBKSTAT && !HAVE_SYSCTLBYNAME
+#if HAVE_STATGRAB_H
+# include <statgrab.h>
+#endif
+
+#if !PROCESSOR_CPU_LOAD_INFO && !KERNEL_LINUX && !HAVE_LIBKSTAT \
+ && !HAVE_SYSCTLBYNAME && !HAVE_LIBSTATGRAB
# error "No applicable input method."
#endif
#elif defined(HAVE_SYSCTLBYNAME)
static int numcpu;
-#endif /* HAVE_SYSCTLBYNAME */
+/* #endif HAVE_SYSCTLBYNAME */
+
+#elif defined(HAVE_LIBSTATGRAB)
+/* no variables needed */
+#endif /* HAVE_LIBSTATGRAB */
static int init (void)
{
if (numcpu != 1)
NOTICE ("cpu: Only one processor supported when using `sysctlbyname' (found %i)", numcpu);
-#endif
+/* #endif HAVE_SYSCTLBYNAME */
+
+#elif defined(HAVE_LIBSTATGRAB)
+ /* nothing to initialize */
+#endif /* HAVE_LIBSTATGRAB */
return (0);
} /* int init */
submit (0, "nice", cpuinfo[CP_NICE]);
submit (0, "system", cpuinfo[CP_SYS]);
submit (0, "idle", cpuinfo[CP_IDLE]);
-#endif
+/* #endif HAVE_SYSCTLBYNAME */
+
+#elif defined(HAVE_LIBSTATGRAB)
+ sg_cpu_stats *cs;
+ cs = sg_get_cpu_stats ();
+
+ if (cs == NULL)
+ {
+ ERROR ("cpu plugin: sg_get_cpu_stats failed.");
+ return (-1);
+ }
+
+ submit (0, "idle", (counter_t) cs->idle);
+ submit (0, "nice", (counter_t) cs->nice);
+ submit (0, "swap", (counter_t) cs->swap);
+ submit (0, "system", (counter_t) cs->kernel);
+ submit (0, "user", (counter_t) cs->user);
+ submit (0, "wait", (counter_t) cs->iowait);
+#endif /* HAVE_LIBSTATGRAB */
return (0);
}
/**
* collectd - src/disk.c
- * Copyright (C) 2005-2007 Florian octo Forster
+ * Copyright (C) 2005-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 "collectd.h"
#include "common.h"
#include "plugin.h"
+#include "utils_ignorelist.h"
#if HAVE_MACH_MACH_TYPES_H
# include <mach/mach_types.h>
# define UINT_MAX 4294967295U
#endif
+#if HAVE_STATGRAB_H
+# include <statgrab.h>
+#endif
+
#if HAVE_IOKIT_IOKITLIB_H
static mach_port_t io_master_port = MACH_PORT_NULL;
/* #endif HAVE_IOKIT_IOKITLIB_H */
static int numdisk = 0;
/* #endif HAVE_LIBKSTAT */
+#elif defined(HAVE_LIBSTATGRAB)
+/* #endif HAVE_LIBKSTATGRAB */
+
#else
# error "No applicable input method."
#endif
+static const char *config_keys[] =
+{
+ "Disk",
+ "IgnoreSelected"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *ignorelist = NULL;
+
+static int disk_config (const char *key, const char *value)
+{
+ if (ignorelist == NULL)
+ ignorelist = ignorelist_create (/* invert = */ 1);
+ if (ignorelist == NULL)
+ return (1);
+
+ if (strcasecmp ("Disk", key) == 0)
+ {
+ ignorelist_add (ignorelist, value);
+ }
+ else if (strcasecmp ("IgnoreSelected", key) == 0)
+ {
+ int invert = 1;
+ if ((strcasecmp ("True", value) == 0)
+ || (strcasecmp ("Yes", value) == 0)
+ || (strcasecmp ("On", value) == 0))
+ invert = 0;
+ ignorelist_set_invert (ignorelist, invert);
+ }
+ else
+ {
+ return (-1);
+ }
+
+ return (0);
+} /* int disk_config */
+
static int disk_init (void)
{
#if HAVE_IOKIT_IOKITLIB_H
value_t values[2];
value_list_t vl = VALUE_LIST_INIT;
+ /* Both `ignorelist' and `plugin_instance' may be NULL. */
+ if (ignorelist_match (ignorelist, plugin_instance) != 0)
+ return;
+
values[0].counter = read;
values[1].counter = write;
kio.KIO_ROPS, kio.KIO_WOPS);
}
}
-#endif /* defined(HAVE_LIBKSTAT) */
+/* #endif defined(HAVE_LIBKSTAT) */
+
+#elif defined(HAVE_LIBSTATGRAB)
+ sg_disk_io_stats *ds;
+ int disks, counter;
+ char name[DATA_MAX_NAME_LEN];
+
+ if ((ds = sg_get_disk_io_stats(&disks)) == NULL)
+ return (0);
+
+ for (counter=0; counter < disks; counter++) {
+ strncpy(name, ds->disk_name, sizeof(name));
+ name[sizeof(name)-1] = '\0'; /* strncpy doesn't terminate longer strings */
+ disk_submit (name, "disk_octets", ds->read_bytes, ds->write_bytes);
+ ds++;
+ }
+#endif /* defined(HAVE_LIBSTATGRAB) */
return (0);
} /* int disk_read */
void module_register (void)
{
- plugin_register_init ("disk", disk_init);
- plugin_register_read ("disk", disk_read);
+ plugin_register_config ("disk", disk_config,
+ config_keys, config_keys_num);
+ plugin_register_init ("disk", disk_init);
+ plugin_register_read ("disk", disk_read);
} /* void module_register */
/**
* collectd - src/email.c
- * Copyright (C) 2006,2007 Sebastian Harl
+ * Copyright (C) 2006-2008 Sebastian Harl
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
# include <grp.h>
#endif /* HAVE_GRP_H */
-#define MODULE_NAME "email"
-
-/* 256 bytes ought to be enough for anybody ;-) */
-#define BUFSIZE 256
-
#define SOCK_PATH LOCALSTATEDIR"/run/"PACKAGE_NAME"-email"
#define MAX_CONNS 5
#define MAX_CONNS_LIMIT 16384
-#define log_err(...) ERROR (MODULE_NAME": "__VA_ARGS__)
-#define log_warn(...) WARNING (MODULE_NAME": "__VA_ARGS__)
+#define log_debug(...) DEBUG ("email: "__VA_ARGS__)
+#define log_err(...) ERROR ("email: "__VA_ARGS__)
+#define log_warn(...) WARNING ("email: "__VA_ARGS__)
/*
* Private data structures
pthread_t thread;
/* socket descriptor of the current/last connection */
- int socket;
+ FILE *socket;
} collector_t;
/* linked list of pending connections */
typedef struct conn {
/* socket to read data from */
- int socket;
-
- /* buffer to read data to */
- char *buffer;
- int idx; /* current write position in buffer */
- int length; /* length of the current line, i.e. index of '\0' */
+ FILE *socket;
+ /* linked list of connections */
struct conn *next;
} conn_t;
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
/* socket configuration */
-static char *sock_file = SOCK_PATH;
-static char *sock_group = COLLECTD_GRP_NAME;
+static char *sock_file = NULL;
+static char *sock_group = NULL;
static int sock_perms = S_IRWXU | S_IRWXG;
static int max_conns = MAX_CONNS;
static int available_collectors;
static pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
-static type_list_t count;
+static type_list_t list_count;
+static type_list_t list_count_copy;
static pthread_mutex_t size_mutex = PTHREAD_MUTEX_INITIALIZER;
-static type_list_t size;
+static type_list_t list_size;
+static type_list_t list_size_copy;
static pthread_mutex_t score_mutex = PTHREAD_MUTEX_INITIALIZER;
static double score;
static int score_count;
static pthread_mutex_t check_mutex = PTHREAD_MUTEX_INITIALIZER;
-static type_list_t check;
+static type_list_t list_check;
+static type_list_t list_check_copy;
/*
* Private functions
static int email_config (const char *key, const char *value)
{
if (0 == strcasecmp (key, "SocketFile")) {
+ if (NULL != sock_file)
+ free (sock_file);
sock_file = sstrdup (value);
}
else if (0 == strcasecmp (key, "SocketGroup")) {
+ if (NULL != sock_group)
+ free (sock_group);
sock_group = sstrdup (value);
}
else if (0 == strcasecmp (key, "SocketPerms")) {
return;
} /* static void type_list_incr (type_list_t *, char *) */
-/* Read a single character from the socket. If an error occurs or end-of-file
- * is reached return '\0'. */
-static char read_char (conn_t *src)
-{
- char ret = '\0';
-
- fd_set fdset;
-
- FD_ZERO (&fdset);
- FD_SET (src->socket, &fdset);
-
- if (-1 == select (src->socket + 1, &fdset, NULL, NULL, NULL)) {
- char errbuf[1024];
- log_err ("select() failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- return '\0';
- }
-
- assert (FD_ISSET (src->socket, &fdset));
-
- do {
- ssize_t len = 0;
-
- errno = 0;
- if (0 > (len = read (src->socket, (void *)&ret, 1))) {
- if (EINTR != errno) {
- char errbuf[1024];
- log_err ("read() failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- return '\0';
- }
- }
-
- if (0 == len)
- return '\0';
- } while (EINTR == errno);
- return ret;
-} /* static char read_char (conn_t *) */
-
-/* Read a single line (terminated by '\n') from the the socket.
- *
- * The return value is zero terminated and does not contain any newline
- * characters.
- *
- * If an error occurs or end-of-file is reached return NULL.
- *
- * IMPORTANT NOTE: If there is no newline character found in BUFSIZE
- * characters of the input stream, the line will will be ignored! By
- * definition we should not get any longer input lines, thus this is
- * acceptable in this case ;-) */
-static char *read_line (conn_t *src)
-{
- int i = 0;
-
- assert ((BUFSIZE >= src->idx) && (src->idx >= 0));
- assert ((src->idx > src->length) || (src->length == 0));
-
- if (src->length > 0) { /* remove old line */
- src->idx -= (src->length + 1);
- memmove (src->buffer, src->buffer + src->length + 1, src->idx);
- src->length = 0;
- }
-
- for (i = 0; i < src->idx; ++i) {
- if ('\n' == src->buffer[i])
- break;
- }
-
- if (i == src->idx) {
- fd_set fdset;
-
- ssize_t len = 0;
-
- FD_ZERO (&fdset);
- FD_SET (src->socket, &fdset);
-
- if (-1 == select (src->socket + 1, &fdset, NULL, NULL, NULL)) {
- char errbuf[1024];
- log_err ("select() failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- return NULL;
- }
-
- assert (FD_ISSET (src->socket, &fdset));
-
- do {
- errno = 0;
- if (0 > (len = read (src->socket,
- (void *)(src->buffer + src->idx),
- BUFSIZE - src->idx))) {
- if (EINTR != errno) {
- char errbuf[1024];
- log_err ("read() failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- return NULL;
- }
- }
-
- if (0 == len)
- return NULL;
- } while (EINTR == errno);
-
- src->idx += len;
-
- for (i = src->idx - len; i < src->idx; ++i) {
- if ('\n' == src->buffer[i])
- break;
- }
-
- if (i == src->idx) {
- src->length = 0;
-
- if (BUFSIZE == src->idx) { /* no space left in buffer */
- while ('\n' != read_char (src))
- /* ignore complete line */;
-
- src->idx = 0;
- }
- return read_line (src);
- }
- }
-
- src->buffer[i] = '\0';
- src->length = i;
-
- return src->buffer;
-} /* static char *read_line (conn_t *) */
-
static void *collect (void *arg)
{
collector_t *this = (collector_t *)arg;
-
- char *buffer = (char *)smalloc (BUFSIZE);
+ pthread_t self = pthread_self ();
while (1) {
int loop = 1;
conns.tail = NULL;
}
- this->socket = connection->socket;
-
pthread_mutex_unlock (&conns_mutex);
- connection->buffer = buffer;
- connection->idx = 0;
- connection->length = 0;
+ /* make the socket available to the global
+ * thread and connection management */
+ this->socket = connection->socket;
- { /* put the socket in non-blocking mode */
- int flags = 0;
+ log_debug ("[thread #%5lu] handling connection on fd #%i",
+ self, fileno (this->socket));
- errno = 0;
- if (-1 == fcntl (connection->socket, F_GETFL, &flags)) {
- char errbuf[1024];
- log_err ("fcntl() failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- loop = 0;
- }
+ while (loop) {
+ /* 256 bytes ought to be enough for anybody ;-) */
+ char line[256 + 1]; /* line + '\0' */
+ int len = 0;
errno = 0;
- if (-1 == fcntl (connection->socket, F_SETFL, flags | O_NONBLOCK)) {
- char errbuf[1024];
- log_err ("fcntl() failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ if (NULL == fgets (line, sizeof (line), this->socket)) {
loop = 0;
+
+ if (0 != errno) {
+ char errbuf[1024];
+ log_err ("[thread #%5lu] reading from socket (fd #%i) "
+ "failed: %s", self, fileno (this->socket),
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ }
+ break;
}
- }
- while (loop) {
- char *line = read_line (connection);
+ len = strlen (line);
+ if (('\n' != line[len - 1]) && ('\r' != line[len - 1])) {
+ log_warn ("[thread #%5lu] line too long (> %lu characters): "
+ "'%s' (truncated)", self, sizeof (line) - 1, line);
- if (NULL == line) {
- loop = 0;
- break;
+ while (NULL != fgets (line, sizeof (line), this->socket))
+ if (('\n' == line[len - 1]) || ('\r' == line[len - 1]))
+ break;
+ continue;
}
+ line[len - 1] = '\0';
+
+ log_debug ("[thread #%5lu] line = '%s'", self, line);
+
if (':' != line[1]) {
- log_err ("syntax error in line '%s'", line);
+ log_err ("[thread #%5lu] syntax error in line '%s'",
+ self, line);
continue;
}
int bytes = 0;
if (NULL == tmp) {
- log_err ("syntax error in line '%s'", line);
+ log_err ("[thread #%5lu] syntax error in line '%s'",
+ self, line);
continue;
}
bytes = atoi (tmp);
pthread_mutex_lock (&count_mutex);
- type_list_incr (&count, type, 1);
+ type_list_incr (&list_count, type, 1);
pthread_mutex_unlock (&count_mutex);
if (bytes > 0) {
pthread_mutex_lock (&size_mutex);
- type_list_incr (&size, type, bytes);
+ type_list_incr (&list_size, type, bytes);
pthread_mutex_unlock (&size_mutex);
}
}
do {
pthread_mutex_lock (&check_mutex);
- type_list_incr (&check, type, 1);
+ type_list_incr (&list_check, type, 1);
pthread_mutex_unlock (&check_mutex);
} while (NULL != (type = strtok_r (NULL, ",", &ptr)));
}
else {
- log_err ("unknown type '%c'", line[0]);
+ log_err ("[thread #%5lu] unknown type '%c'", self, line[0]);
}
} /* while (loop) */
- close (connection->socket);
+ log_debug ("[thread #%5lu] shutting down connection on fd #%i",
+ pthread_self (), fileno (this->socket));
+
+ fclose (connection->socket);
free (connection);
- this->socket = -1;
+ this->socket = NULL;
pthread_mutex_lock (&available_mutex);
++available_collectors;
pthread_cond_signal (&collector_available);
} /* while (1) */
- free (buffer);
pthread_exit ((void *)0);
} /* static void *collect (void *) */
{
struct sockaddr_un addr;
+ char *path = (NULL == sock_file) ? SOCK_PATH : sock_file;
+ char *group = (NULL == sock_group) ? COLLECTD_GRP_NAME : sock_group;
+
/* create UNIX socket */
errno = 0;
if (-1 == (connector_socket = socket (PF_UNIX, SOCK_STREAM, 0))) {
addr.sun_family = AF_UNIX;
- strncpy (addr.sun_path, sock_file, (size_t)(UNIX_PATH_MAX - 1));
+ strncpy (addr.sun_path, path, (size_t)(UNIX_PATH_MAX - 1));
addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
unlink (addr.sun_path);
+ strlen(addr.sun_path))) {
char errbuf[1024];
disabled = 1;
- connector_socket = -1; /* TODO: close? */
+ close (connector_socket);
+ connector_socket = -1;
log_err ("bind() failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
pthread_exit ((void *)1);
if (-1 == listen (connector_socket, 5)) {
char errbuf[1024];
disabled = 1;
- connector_socket = -1; /* TODO: close? */
+ close (connector_socket);
+ connector_socket = -1;
log_err ("listen() failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
pthread_exit ((void *)1);
}
- if ((uid_t) 0 == geteuid ())
{
struct group sg;
struct group *grp;
int status;
grp = NULL;
- status = getgrnam_r (sock_group, &sg, grbuf, sizeof (grbuf), &grp);
+ status = getgrnam_r (group, &sg, grbuf, sizeof (grbuf), &grp);
if (status != 0)
{
char errbuf[1024];
- log_warn ("getgrnam_r (%s) failed: %s", sock_group,
+ log_warn ("getgrnam_r (%s) failed: %s", group,
sstrerror (errno, errbuf, sizeof (errbuf)));
}
else if (grp == NULL)
{
- log_warn ("No such group: `%s'", sock_group);
+ log_warn ("No such group: `%s'", group);
}
else
{
- status = chown (sock_file, (uid_t) -1, grp->gr_gid);
+ status = chown (path, (uid_t) -1, grp->gr_gid);
if (status != 0)
{
char errbuf[1024];
log_warn ("chown (%s, -1, %i) failed: %s",
- sock_file, (int) grp->gr_gid,
+ path, (int) grp->gr_gid,
sstrerror (errno, errbuf, sizeof (errbuf)));
}
}
}
- else /* geteuid != 0 */
- {
- log_warn ("not running as root");
- }
errno = 0;
- if (0 != chmod (sock_file, sock_perms)) {
+ if (0 != chmod (path, sock_perms)) {
char errbuf[1024];
log_warn ("chmod() failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
for (i = 0; i < max_conns; ++i) {
collectors[i] = (collector_t *)smalloc (sizeof (collector_t));
- collectors[i]->socket = -1;
+ collectors[i]->socket = NULL;
if (0 != (err = pthread_create (&collectors[i]->thread, &ptattr,
collect, collectors[i]))) {
if (EINTR != errno) {
char errbuf[1024];
disabled = 1;
- connector_socket = -1; /* TODO: close? */
+ close (connector_socket);
+ connector_socket = -1;
log_err ("accept() failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
pthread_exit ((void *)1);
connection = (conn_t *)smalloc (sizeof (conn_t));
- connection->socket = remote;
+ connection->socket = fdopen (remote, "r");
connection->next = NULL;
+ if (NULL == connection->socket) {
+ close (remote);
+ continue;
+ }
+
pthread_mutex_lock (&conns_mutex);
if (NULL == conns.head) {
static int email_shutdown (void)
{
+ type_t *ptr = NULL;
+
int i = 0;
if (connector != ((pthread_t) 0)) {
/* don't allow any more connections to be processed */
pthread_mutex_lock (&conns_mutex);
+ available_collectors = 0;
+
if (collectors != NULL) {
for (i = 0; i < max_conns; ++i) {
if (collectors[i] == NULL)
collectors[i]->thread = (pthread_t) 0;
}
- if (collectors[i]->socket >= 0) {
- close (collectors[i]->socket);
- collectors[i]->socket = -1;
+ if (collectors[i]->socket != NULL) {
+ fclose (collectors[i]->socket);
+ collectors[i]->socket = NULL;
}
+
+ sfree (collectors[i]);
}
+ sfree (collectors);
} /* if (collectors != NULL) */
pthread_mutex_unlock (&conns_mutex);
- unlink (sock_file);
- errno = 0;
+ for (ptr = list_count.head; NULL != ptr; ptr = ptr->next) {
+ free (ptr->name);
+ free (ptr);
+ }
+
+ for (ptr = list_count_copy.head; NULL != ptr; ptr = ptr->next) {
+ free (ptr->name);
+ free (ptr);
+ }
+
+ for (ptr = list_size.head; NULL != ptr; ptr = ptr->next) {
+ free (ptr->name);
+ free (ptr);
+ }
+ for (ptr = list_size_copy.head; NULL != ptr; ptr = ptr->next) {
+ free (ptr->name);
+ free (ptr);
+ }
+
+ for (ptr = list_check.head; NULL != ptr; ptr = ptr->next) {
+ free (ptr->name);
+ free (ptr);
+ }
+
+ for (ptr = list_check_copy.head; NULL != ptr; ptr = ptr->next) {
+ free (ptr->name);
+ free (ptr);
+ }
+
+ unlink ((NULL == sock_file) ? SOCK_PATH : sock_file);
+
+ sfree (sock_file);
+ sfree (sock_group);
return (0);
} /* static void email_shutdown (void) */
double score_old;
int score_count_old;
- static type_list_t *cnt;
- static type_list_t *sz;
- static type_list_t *chk;
-
if (disabled)
return (-1);
- if (NULL == cnt) {
- cnt = (type_list_t *)smalloc (sizeof (type_list_t));
- cnt->head = NULL;
- }
-
- if (NULL == sz) {
- sz = (type_list_t *)smalloc (sizeof (type_list_t));
- sz->head = NULL;
- }
-
- if (NULL == chk) {
- chk = (type_list_t *)smalloc (sizeof (type_list_t));
- chk->head = NULL;
- }
-
/* email count */
pthread_mutex_lock (&count_mutex);
- copy_type_list (&count, cnt);
+ copy_type_list (&list_count, &list_count_copy);
pthread_mutex_unlock (&count_mutex);
- for (ptr = cnt->head; NULL != ptr; ptr = ptr->next) {
+ for (ptr = list_count_copy.head; NULL != ptr; ptr = ptr->next) {
email_submit ("email_count", ptr->name, ptr->value);
}
/* email size */
pthread_mutex_lock (&size_mutex);
- copy_type_list (&size, sz);
+ copy_type_list (&list_size, &list_size_copy);
pthread_mutex_unlock (&size_mutex);
- for (ptr = sz->head; NULL != ptr; ptr = ptr->next) {
+ for (ptr = list_size_copy.head; NULL != ptr; ptr = ptr->next) {
email_submit ("email_size", ptr->name, ptr->value);
}
/* spam checks */
pthread_mutex_lock (&check_mutex);
- copy_type_list (&check, chk);
+ copy_type_list (&list_check, &list_check_copy);
pthread_mutex_unlock (&check_mutex);
- for (ptr = chk->head; NULL != ptr; ptr = ptr->next)
+ for (ptr = list_check_copy.head; NULL != ptr; ptr = ptr->next)
email_submit ("spam_check", ptr->name, ptr->value);
return (0);
} /* void exec_child }}} */
/*
- * Creates two pipes (one for reading, ong for writing), forks a child, sets up
- * the pipes so that fd_in is connected to STDIN of the child and fd_out is
- * connected to STDOUT and STDERR of the child. Then is calls `exec_child'.
+ * Creates three pipes (one for reading, one for writing and one for errors),
+ * forks a child, sets up the pipes so that fd_in is connected to STDIN of
+ * the child and fd_out is connected to STDOUT and fd_err is connected to STDERR
+ * of the child. Then is calls `exec_child'.
*/
-static int fork_child (program_list_t *pl, int *fd_in, int *fd_out) /* {{{ */
+static int fork_child (program_list_t *pl, int *fd_in, int *fd_out, int *fd_err) /* {{{ */
{
int fd_pipe_in[2];
int fd_pipe_out[2];
+ int fd_pipe_err[2];
+ char errbuf[1024];
int status;
int pid;
status = pipe (fd_pipe_in);
if (status != 0)
{
- char errbuf[1024];
ERROR ("exec plugin: pipe failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
return (-1);
status = pipe (fd_pipe_out);
if (status != 0)
{
- char errbuf[1024];
+ ERROR ("exec plugin: pipe failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+
+ status = pipe (fd_pipe_err);
+ if (status != 0)
+ {
ERROR ("exec plugin: pipe failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
return (-1);
pid = fork ();
if (pid < 0)
{
- char errbuf[1024];
ERROR ("exec plugin: fork failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
return (-1);
fd_num = getdtablesize ();
for (fd = 0; fd < fd_num; fd++)
{
- if ((fd == fd_pipe_in[0]) || (fd == fd_pipe_out[1]))
+ if ((fd == fd_pipe_in[0])
+ || (fd == fd_pipe_out[1])
+ || (fd == fd_pipe_err[1]))
continue;
close (fd);
}
- /* If the `out' pipe has the filedescriptor STDIN we have to be careful
- * with the `dup's below. So, if this is the case we have to handle the
- * `out' pipe first. */
- if (fd_pipe_out[1] == STDIN_FILENO)
- {
- int new_fileno = (fd_pipe_in[0] == STDOUT_FILENO)
- ? STDERR_FILENO : STDOUT_FILENO;
- dup2 (fd_pipe_out[1], new_fileno);
- close (fd_pipe_out[1]);
- fd_pipe_out[1] = new_fileno;
- }
- /* Now `fd_pipe_out[1]' is either `STDOUT' or `STDERR', but definitely not
- * `STDIN_FILENO'. */
-
/* Connect the `in' pipe to STDIN */
if (fd_pipe_in[0] != STDIN_FILENO)
{
dup2 (fd_pipe_in[0], STDIN_FILENO);
close (fd_pipe_in[0]);
- fd_pipe_in[0] = STDIN_FILENO;
}
- /* Now connect the `out' pipe to STDOUT and STDERR */
+ /* Now connect the `out' pipe to STDOUT */
if (fd_pipe_out[1] != STDOUT_FILENO)
+ {
dup2 (fd_pipe_out[1], STDOUT_FILENO);
- if (fd_pipe_out[1] != STDERR_FILENO)
- dup2 (fd_pipe_out[1], STDERR_FILENO);
+ close (fd_pipe_out[1]);
+ }
- /* If the pipe has some FD that's something completely different, close it
- * now. */
- if ((fd_pipe_out[1] != STDOUT_FILENO) && (fd_pipe_out[1] != STDERR_FILENO))
+ /* Now connect the `out' pipe to STDOUT */
+ if (fd_pipe_err[1] != STDERR_FILENO)
{
- close (fd_pipe_out[1]);
- fd_pipe_out[1] = STDOUT_FILENO;
+ dup2 (fd_pipe_err[1], STDERR_FILENO);
+ close (fd_pipe_err[1]);
}
exec_child (pl);
close (fd_pipe_in[0]);
close (fd_pipe_out[1]);
+ close (fd_pipe_err[1]);
if (fd_in != NULL)
*fd_in = fd_pipe_in[1];
else
close (fd_pipe_out[0]);
+ if (fd_err != NULL)
+ *fd_err = fd_pipe_err[0];
+ else
+ close (fd_pipe_err[0]);
+
return (pid);
} /* int fork_child }}} */
static void *exec_read_one (void *arg) /* {{{ */
{
program_list_t *pl = (program_list_t *) arg;
- int fd;
- FILE *fh;
- char buffer[1024];
+ int fd, fd_err, highest_fd;
+ fd_set fdset, copy;
int status;
+ char buffer[1200]; /* if not completely read */
+ char buffer_err[1024];
+ char *pbuffer = buffer;
+ char *pbuffer_err = buffer_err;
- status = fork_child (pl, NULL, &fd);
+ status = fork_child (pl, NULL, &fd, &fd_err);
if (status < 0)
pthread_exit ((void *) 1);
pl->pid = status;
assert (pl->pid != 0);
- fh = fdopen (fd, "r");
- if (fh == NULL)
- {
- char errbuf[1024];
- ERROR ("exec plugin: fdopen (%i) failed: %s", fd,
- sstrerror (errno, errbuf, sizeof (errbuf)));
- kill (pl->pid, SIGTERM);
- pl->pid = 0;
- close (fd);
- pthread_exit ((void *) 1);
- }
+ FD_ZERO( &fdset );
+ FD_SET(fd, &fdset);
+ FD_SET(fd_err, &fdset);
- buffer[0] = '\0';
- while (fgets (buffer, sizeof (buffer), fh) != NULL)
+ /* Determine the highest file descriptor */
+ highest_fd = (fd > fd_err) ? fd : fd_err;
+
+ /* We use a copy of fdset, as select modifies it */
+ copy = fdset;
+
+ while (select(highest_fd + 1, ©, NULL, NULL, NULL ) > 0)
{
int len;
- len = strlen (buffer);
+ if (FD_ISSET(fd, ©))
+ {
+ char *pnl;
- /* Remove newline from end. */
- while ((len > 0) && ((buffer[len - 1] == '\n')
- || (buffer[len - 1] == '\r')))
- buffer[--len] = '\0';
+ len = read(fd, pbuffer, sizeof(buffer) - 1 - (pbuffer - buffer));
- DEBUG ("exec plugin: exec_read_one: buffer = %s", buffer);
+ if (len < 0)
+ {
+ if (errno == EAGAIN || errno == EINTR) continue;
+ break;
+ }
+ else if (len == 0) break; /* We've reached EOF */
- parse_line (buffer);
- } /* while (fgets) */
+ pbuffer[len] = '\0';
- fclose (fh);
+ len += pbuffer - buffer;
+ pbuffer = buffer;
+
+ while ((pnl = strchr(pbuffer, '\n')))
+ {
+ *pnl = '\0';
+ if (*(pnl-1) == '\r' ) *(pnl-1) = '\0';
+
+ parse_line (pbuffer);
+
+ pbuffer = ++pnl;
+ }
+ /* not completely read ? */
+ if (pbuffer - buffer < len)
+ {
+ len -= pbuffer - buffer;
+ memmove(buffer, pbuffer, len);
+ pbuffer = buffer + len;
+ }
+ else
+ pbuffer = buffer;
+ }
+ else if (FD_ISSET(fd_err, ©))
+ {
+ char *pnl;
+
+ len = read(fd_err, pbuffer_err, sizeof(buffer_err) - 1 - (pbuffer_err - buffer_err));
+
+ if (len < 0)
+ {
+ if (errno == EAGAIN || errno == EINTR) continue;
+ break;
+ }
+ else if (len == 0) break; /* We've reached EOF */
+
+ pbuffer_err[len] = '\0';
+
+ len += pbuffer_err - buffer_err;
+ pbuffer_err = buffer_err;
+
+ while ((pnl = strchr(pbuffer_err, '\n')))
+ {
+ *pnl = '\0';
+ if (*(pnl-1) == '\r' ) *(pnl-1) = '\0';
+
+ ERROR ("exec plugin: exec_read_one: error = %s", pbuffer_err);
+
+ pbuffer_err = ++pnl;
+ }
+ /* not completely read ? */
+ if (pbuffer_err - buffer_err < len)
+ {
+ len -= pbuffer_err - buffer_err;
+ memmove(buffer_err, pbuffer_err, len);
+ pbuffer_err = buffer_err + len;
+ }
+ else
+ pbuffer_err = buffer_err;
+ }
+ /* reset copy */
+ copy = fdset;
+ }
if (waitpid (pl->pid, &status, 0) > 0)
pl->status = status;
pl->flags &= ~PL_RUNNING;
pthread_mutex_unlock (&pl_lock);
+ close (fd);
+ close (fd_err);
+
pthread_exit ((void *) 0);
return (NULL);
} /* void *exec_read_one }}} */
int status;
const char *severity;
- pid = fork_child (pl, &fd, NULL);
+ pid = fork_child (pl, &fd, NULL, NULL);
if (pid < 0) {
sfree (arg);
pthread_exit ((void *) 1);
--- /dev/null
+/**
+ * collectd - src/ipmi.c
+ * 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:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_ignorelist.h"
+
+#include <pthread.h>
+
+#include <OpenIPMI/ipmiif.h>
+#include <OpenIPMI/ipmi_err.h>
+#include <OpenIPMI/ipmi_posix.h>
+#include <OpenIPMI/ipmi_conn.h>
+#include <OpenIPMI/ipmi_smi.h>
+
+/*
+ * Private data types
+ */
+struct c_ipmi_sensor_list_s;
+typedef struct c_ipmi_sensor_list_s c_ipmi_sensor_list_t;
+
+struct c_ipmi_sensor_list_s
+{
+ ipmi_sensor_id_t sensor_id;
+ c_ipmi_sensor_list_t *next;
+};
+
+/*
+ * Module global variables
+ */
+static pthread_mutex_t sensor_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static c_ipmi_sensor_list_t *sensor_list = NULL;
+
+static int c_ipmi_active = 0;
+static pthread_t thread_id = (pthread_t) 0;
+
+static const char *config_keys[] =
+{
+ "Sensor",
+ "IgnoreSelected"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *ignorelist = NULL;
+
+/*
+ * Misc private functions
+ */
+static void c_ipmi_error (const char *func, int status)
+{
+ char errbuf[4096];
+
+ memset (errbuf, 0, sizeof (errbuf));
+
+ if (IPMI_IS_OS_ERR (status))
+ {
+ sstrerror_r (IPMI_GET_OS_ERR (status), errbuf, sizeof (errbuf));
+ }
+ else if (IPMI_IS_IPMI_ERR (status))
+ {
+ ipmi_get_error_string (IPMI_GET_IPMI_ERR (status), errbuf, sizeof (errbuf));
+ }
+
+ if (errbuf[0] == 0)
+ {
+ ssnprintf (errbuf, sizeof (errbuf), "Unknown error %#x", status);
+ }
+ errbuf[sizeof (errbuf) - 1] = 0;
+
+ ERROR ("ipmi plugin: %s failed: %s", func, errbuf);
+} /* void c_ipmi_error */
+
+/*
+ * Sensor handlers
+ */
+/* Prototype for sensor_list_remove, so sensor_read_handler can call it. */
+static int sensor_list_remove (ipmi_sensor_t *sensor);
+
+static void sensor_read_handler (ipmi_sensor_t *sensor,
+ int err,
+ enum ipmi_value_present_e value_present,
+ unsigned int raw_value,
+ double value,
+ ipmi_states_t *states,
+ void *user_data)
+{
+ 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;
+
+ 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);
+ return;
+ }
+
+ 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,
+ (value_present == IPMI_RAW_VALUE_PRESENT)
+ ? "only the raw value"
+ : "no value");
+ sensor_list_remove (sensor);
+ return;
+ }
+
+ /* Both `ignorelist' and `plugin_instance' may be NULL. */
+ if (ignorelist_match (ignorelist, sensor_name_ptr) != 0)
+ {
+ sensor_list_remove (sensor);
+ return;
+ }
+
+ /* FIXME: Use rate unit or base unit to scale the value */
+
+ sensor_type = ipmi_sensor_get_sensor_type (sensor);
+ switch (sensor_type)
+ {
+ case IPMI_SENSOR_TYPE_TEMPERATURE:
+ type = "temperature";
+ break;
+
+ case IPMI_SENSOR_TYPE_VOLTAGE:
+ type = "voltage";
+ break;
+
+ case IPMI_SENSOR_TYPE_CURRENT:
+ type = "current";
+ break;
+
+ case IPMI_SENSOR_TYPE_FAN:
+ type = "fanspeed";
+ break;
+
+ 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, "
+ "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;
+ }
+ } /* 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_instance, sensor_name_ptr, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (type, &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;
+ for (list_item = sensor_list;
+ list_item != NULL;
+ list_item = list_item->next)
+ {
+ if (ipmi_cmp_sensor_id (sensor_id, list_item->sensor_id) == 0)
+ break;
+ list_prev = list_item;
+ } /* for (list_item) */
+
+ if (list_item != NULL)
+ {
+ pthread_mutex_unlock (&sensor_list_lock);
+ return (0);
+ }
+
+ list_item = (c_ipmi_sensor_list_t *) calloc (1, sizeof (c_ipmi_sensor_list_t));
+ if (list_item == NULL)
+ {
+ pthread_mutex_unlock (&sensor_list_lock);
+ return (-1);
+ }
+
+ list_item->sensor_id = ipmi_sensor_convert_to_id (sensor);
+
+ if (list_prev != NULL)
+ list_prev->next = list_item;
+ else
+ sensor_list = list_item;
+
+ pthread_mutex_unlock (&sensor_list_lock);
+
+ return (0);
+} /* int sensor_list_add */
+
+static int sensor_list_remove (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;
+ for (list_item = sensor_list;
+ list_item != NULL;
+ list_item = list_item->next)
+ {
+ if (ipmi_cmp_sensor_id (sensor_id, list_item->sensor_id) == 0)
+ break;
+ list_prev = list_item;
+ } /* for (list_item) */
+
+ if (list_item == NULL)
+ {
+ pthread_mutex_unlock (&sensor_list_lock);
+ return (-1);
+ }
+
+ if (list_prev == NULL)
+ sensor_list = list_item->next;
+ else
+ list_prev->next = list_item->next;
+
+ list_prev = NULL;
+ list_item->next = NULL;
+
+ pthread_mutex_unlock (&sensor_list_lock);
+
+ free (list_item);
+ return (0);
+} /* int sensor_list_remove */
+
+static int sensor_list_read_all (void)
+{
+ c_ipmi_sensor_list_t *list_item;
+
+ pthread_mutex_lock (&sensor_list_lock);
+
+ for (list_item = sensor_list;
+ list_item != NULL;
+ list_item = list_item->next)
+ {
+ ipmi_sensor_id_get_reading (list_item->sensor_id,
+ sensor_read_handler, /* user data = */ NULL);
+ } /* for (list_item) */
+
+ pthread_mutex_unlock (&sensor_list_lock);
+
+ return (0);
+} /* int sensor_list_read_all */
+
+static int sensor_list_remove_all (void)
+{
+ c_ipmi_sensor_list_t *list_item;
+
+ pthread_mutex_lock (&sensor_list_lock);
+
+ list_item = sensor_list;
+ sensor_list = NULL;
+
+ pthread_mutex_unlock (&sensor_list_lock);
+
+ while (list_item != NULL)
+ {
+ c_ipmi_sensor_list_t *list_next = list_item->next;
+
+ free (list_item);
+
+ list_item = list_next;
+ } /* while (list_item) */
+
+ return (0);
+} /* int sensor_list_remove_all */
+
+/*
+ * Entity handlers
+ */
+static void entity_sensor_update_handler (enum ipmi_update_e op,
+ ipmi_entity_t *entity,
+ ipmi_sensor_t *sensor,
+ void *user_data)
+{
+ /* TODO: Ignore sensors we cannot read */
+
+ if ((op == IPMI_ADDED) || (op == IPMI_CHANGED))
+ {
+ /* Will check for duplicate entries.. */
+ sensor_list_add (sensor);
+ }
+ else if (op == IPMI_DELETED)
+ {
+ sensor_list_remove (sensor);
+ }
+} /* void entity_sensor_update_handler */
+
+/*
+ * Domain handlers
+ */
+static void domain_entity_update_handler (enum ipmi_update_e op,
+ ipmi_domain_t *domain,
+ ipmi_entity_t *entity,
+ void *user_data)
+{
+ int status;
+
+ if (op == IPMI_ADDED)
+ {
+ status = ipmi_entity_add_sensor_update_handler (entity,
+ entity_sensor_update_handler, /* user data = */ NULL);
+ if (status != 0)
+ {
+ c_ipmi_error ("ipmi_entity_add_sensor_update_handler", status);
+ }
+ }
+ else if (op == IPMI_DELETED)
+ {
+ status = ipmi_entity_remove_sensor_update_handler (entity,
+ entity_sensor_update_handler, /* user data = */ NULL);
+ if (status != 0)
+ {
+ c_ipmi_error ("ipmi_entity_remove_sensor_update_handler", status);
+ }
+ }
+} /* void domain_entity_update_handler */
+
+static void domain_connection_change_handler (ipmi_domain_t *domain,
+ int err,
+ unsigned int conn_num,
+ unsigned int port_num,
+ int still_connected,
+ void *user_data)
+{
+ int status;
+
+ printf ("domain_connection_change_handler (domain = %p, err = %i, "
+ "conn_num = %u, port_num = %u, still_connected = %i, "
+ "user_data = %p);\n",
+ (void *) domain, err, conn_num, port_num, still_connected, user_data);
+
+ status = ipmi_domain_add_entity_update_handler (domain,
+ domain_entity_update_handler, /* user data = */ NULL);
+ if (status != 0)
+ {
+ c_ipmi_error ("ipmi_domain_add_entity_update_handler", status);
+ }
+} /* void domain_connection_change_handler */
+
+static int thread_init (os_handler_t **ret_os_handler)
+{
+ os_handler_t *os_handler;
+ ipmi_open_option_t open_option[1];
+ ipmi_con_t *smi_connection = NULL;
+ ipmi_domain_id_t domain_id;
+ int status;
+
+ os_handler = ipmi_posix_thread_setup_os_handler (SIGUSR2);
+ if (os_handler == NULL)
+ {
+ ERROR ("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
+ return (-1);
+ }
+
+ ipmi_init (os_handler);
+
+ status = ipmi_smi_setup_con (/* if_num = */ 0,
+ os_handler,
+ /* user data = */ NULL,
+ &smi_connection);
+ if (status != 0)
+ {
+ c_ipmi_error ("ipmi_smi_setup_con", status);
+ return (-1);
+ }
+
+ memset (open_option, 0, sizeof (open_option));
+ open_option[0].option = IPMI_OPEN_OPTION_ALL;
+ open_option[0].ival = 1;
+
+ status = ipmi_open_domain ("mydomain", &smi_connection, /* num_con = */ 1,
+ domain_connection_change_handler, /* user data = */ NULL,
+ /* domain_fully_up_handler = */ NULL, /* user data = */ NULL,
+ open_option, sizeof (open_option) / sizeof (open_option[0]),
+ &domain_id);
+ if (status != 0)
+ {
+ c_ipmi_error ("ipmi_open_domain", status);
+ return (-1);
+ }
+
+ *ret_os_handler = os_handler;
+ return (0);
+} /* int thread_init */
+
+static void *thread_main (void *user_data)
+{
+ int status;
+ os_handler_t *os_handler = NULL;
+
+ status = thread_init (&os_handler);
+ if (status != 0)
+ {
+ fprintf (stderr, "ipmi plugin: thread_init failed.\n");
+ return ((void *) -1);
+ }
+
+ while (c_ipmi_active != 0)
+ {
+ struct timeval tv = { 1, 0 };
+ os_handler->perform_one_op (os_handler, &tv);
+ }
+
+ ipmi_posix_thread_free_os_handler (os_handler);
+
+ return ((void *) 0);
+} /* void *thread_main */
+
+static int c_ipmi_config (const char *key, const char *value)
+{
+ if (ignorelist == NULL)
+ ignorelist = ignorelist_create (/* invert = */ 1);
+ if (ignorelist == NULL)
+ return (1);
+
+ if (strcasecmp ("Sensor", key) == 0)
+ {
+ ignorelist_add (ignorelist, value);
+ }
+ else if (strcasecmp ("IgnoreSelected", key) == 0)
+ {
+ int invert = 1;
+ if ((strcasecmp ("True", value) == 0)
+ || (strcasecmp ("Yes", value) == 0)
+ || (strcasecmp ("On", value) == 0))
+ invert = 0;
+ ignorelist_set_invert (ignorelist, invert);
+ }
+ else
+ {
+ return (-1);
+ }
+
+ return (0);
+} /* int c_ipmi_config */
+
+static int c_ipmi_init (void)
+{
+ int status;
+
+ c_ipmi_active = 1;
+
+ status = pthread_create (&thread_id, /* attr = */ NULL, thread_main,
+ /* user data = */ NULL);
+ if (status != 0)
+ {
+ c_ipmi_active = 0;
+ thread_id = (pthread_t) 0;
+ ERROR ("ipmi plugin: pthread_create failed.");
+ return (-1);
+ }
+
+ return (0);
+} /* int c_ipmi_init */
+
+static int c_ipmi_read (void)
+{
+ if ((c_ipmi_active == 0) || (thread_id == (pthread_t) 0))
+ {
+ INFO ("ipmi plugin: c_ipmi_read: I'm not active, returning false.");
+ return (-1);
+ }
+
+ sensor_list_read_all ();
+
+ return (0);
+} /* int c_ipmi_read */
+
+static int c_ipmi_shutdown (void)
+{
+ c_ipmi_active = 0;
+
+ if (thread_id != (pthread_t) 0)
+ {
+ pthread_join (thread_id, NULL);
+ thread_id = (pthread_t) 0;
+ }
+
+ sensor_list_remove_all ();
+
+ return (0);
+} /* int c_ipmi_shutdown */
+
+void module_register (void)
+{
+ plugin_register_config ("ipmi", c_ipmi_config,
+ config_keys, config_keys_num);
+ plugin_register_init ("ipmi", c_ipmi_init);
+ plugin_register_read ("ipmi", c_ipmi_read);
+ plugin_register_shutdown ("ipmi", c_ipmi_shutdown);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 ts=8 fdm=marker et : */
#include "plugin.h"
#include "configfile.h"
-#if HAVE_LIBIPTC_LIBIPTC_H
+#if OWN_LIBIPTC
+# include "libiptc/libiptc.h"
+#else
# include <libiptc/libiptc.h>
#endif
--- /dev/null
+AUTOMAKE_OPTIONS = foreign no-dependencies
+
+EXTRA_DIST = libiptc.c
+
+if COMPILER_IS_GCC
+AM_CFLAGS = -Wall -Werror
+endif
+
+noinst_LTLIBRARIES = libiptc.la
+
+libiptc_la_CFLAGS = -DIPTABLES_VERSION=\"1.4.0\" -I$(KERNEL_DIR)/include
+libiptc_la_SOURCES = libip4tc.c libip6tc.c \
+ ipt_kernel_headers.h libip6tc.h libiptc.h linux_list.h
+
--- /dev/null
+/**
+ * This file was imported from the iptables sources.
+ * Copyright (C) 1999-2008 Netfilter Core Team
+ *
+ * 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
+ */
+
+/* This is the userspace/kernel interface for Generic IP Chains,
+ required for libc6. */
+#ifndef _FWCHAINS_KERNEL_HEADERS_H
+#define _FWCHAINS_KERNEL_HEADERS_H
+
+#include <limits.h>
+
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <netinet/ip.h>
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <sys/types.h>
+#else /* libc5 */
+#include <sys/socket.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/types.h>
+#include <linux/in6.h>
+#endif
+#endif
--- /dev/null
+/**
+ * This file was imported from the iptables sources.
+ * Copyright (C) 1999-2008 Netfilter Core Team
+ *
+ * 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
+ */
+
+/* Library which manipulates firewall rules. Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+ COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc.h"
+
+#define IP_VERSION 4
+#define IP_OFFSET 0x1FFF
+
+#define HOOK_PRE_ROUTING NF_IP_PRE_ROUTING
+#define HOOK_LOCAL_IN NF_IP_LOCAL_IN
+#define HOOK_FORWARD NF_IP_FORWARD
+#define HOOK_LOCAL_OUT NF_IP_LOCAL_OUT
+#define HOOK_POST_ROUTING NF_IP_POST_ROUTING
+#ifdef NF_IP_DROPPING
+#define HOOK_DROPPING NF_IP_DROPPING
+#endif
+
+#define STRUCT_ENTRY_TARGET struct ipt_entry_target
+#define STRUCT_ENTRY struct ipt_entry
+#define STRUCT_ENTRY_MATCH struct ipt_entry_match
+#define STRUCT_GETINFO struct ipt_getinfo
+#define STRUCT_GET_ENTRIES struct ipt_get_entries
+#define STRUCT_COUNTERS struct ipt_counters
+#define STRUCT_COUNTERS_INFO struct ipt_counters_info
+#define STRUCT_STANDARD_TARGET struct ipt_standard_target
+#define STRUCT_REPLACE struct ipt_replace
+
+#define STRUCT_TC_HANDLE struct iptc_handle
+#define TC_HANDLE_T iptc_handle_t
+
+#define ENTRY_ITERATE IPT_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN IPT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN IPT_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET ipt_get_target
+
+#define ERROR_TARGET IPT_ERROR_TARGET
+#define NUMHOOKS NF_IP_NUMHOOKS
+
+#define IPT_CHAINLABEL ipt_chainlabel
+
+#define TC_DUMP_ENTRIES dump_entries
+#define TC_IS_CHAIN iptc_is_chain
+#define TC_FIRST_CHAIN iptc_first_chain
+#define TC_NEXT_CHAIN iptc_next_chain
+#define TC_FIRST_RULE iptc_first_rule
+#define TC_NEXT_RULE iptc_next_rule
+#define TC_GET_TARGET iptc_get_target
+#define TC_BUILTIN iptc_builtin
+#define TC_GET_POLICY iptc_get_policy
+#define TC_INSERT_ENTRY iptc_insert_entry
+#define TC_REPLACE_ENTRY iptc_replace_entry
+#define TC_APPEND_ENTRY iptc_append_entry
+#define TC_DELETE_ENTRY iptc_delete_entry
+#define TC_DELETE_NUM_ENTRY iptc_delete_num_entry
+#define TC_CHECK_PACKET iptc_check_packet
+#define TC_FLUSH_ENTRIES iptc_flush_entries
+#define TC_ZERO_ENTRIES iptc_zero_entries
+#define TC_READ_COUNTER iptc_read_counter
+#define TC_ZERO_COUNTER iptc_zero_counter
+#define TC_SET_COUNTER iptc_set_counter
+#define TC_CREATE_CHAIN iptc_create_chain
+#define TC_GET_REFERENCES iptc_get_references
+#define TC_DELETE_CHAIN iptc_delete_chain
+#define TC_RENAME_CHAIN iptc_rename_chain
+#define TC_SET_POLICY iptc_set_policy
+#define TC_GET_RAW_SOCKET iptc_get_raw_socket
+#define TC_INIT iptc_init
+#define TC_FREE iptc_free
+#define TC_COMMIT iptc_commit
+#define TC_STRERROR iptc_strerror
+#define TC_NUM_RULES iptc_num_rules
+#define TC_GET_RULE iptc_get_rule
+
+#define TC_AF AF_INET
+#define TC_IPPROTO IPPROTO_IP
+
+#define SO_SET_REPLACE IPT_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS IPT_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO IPT_SO_GET_INFO
+#define SO_GET_ENTRIES IPT_SO_GET_ENTRIES
+#define SO_GET_VERSION IPT_SO_GET_VERSION
+
+#define STANDARD_TARGET IPT_STANDARD_TARGET
+#define LABEL_RETURN IPTC_LABEL_RETURN
+#define LABEL_ACCEPT IPTC_LABEL_ACCEPT
+#define LABEL_DROP IPTC_LABEL_DROP
+#define LABEL_QUEUE IPTC_LABEL_QUEUE
+
+#define ALIGN IPT_ALIGN
+#define RETURN IPT_RETURN
+
+#include "libiptc.c"
+
+#define IP_PARTS_NATIVE(n) \
+(unsigned int)((n)>>24)&0xFF, \
+(unsigned int)((n)>>16)&0xFF, \
+(unsigned int)((n)>>8)&0xFF, \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+int
+dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
+{
+ size_t i;
+ STRUCT_ENTRY_TARGET *t;
+
+ printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
+ iptcb_entry2offset(handle, e));
+ printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+ IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
+ printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+ IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr));
+ printf("Interface: `%s'/", e->ip.iniface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ip.iniface_mask[i] ? 'X' : '.');
+ printf("to `%s'/", e->ip.outiface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ip.outiface_mask[i] ? 'X' : '.');
+ printf("\nProtocol: %u\n", e->ip.proto);
+ printf("Flags: %02X\n", e->ip.flags);
+ printf("Invflags: %02X\n", e->ip.invflags);
+ printf("Counters: %llu packets, %llu bytes\n",
+ (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+ printf("Cache: %08X\n", e->nfcache);
+
+ IPT_MATCH_ITERATE(e, print_match);
+
+ t = GET_TARGET(e);
+ printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+ if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) {
+ int pos = *(int *)t->data;
+ if (pos < 0)
+ printf("verdict=%s\n",
+ pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+ : pos == -NF_DROP-1 ? "NF_DROP"
+ : pos == -NF_QUEUE-1 ? "NF_QUEUE"
+ : pos == RETURN ? "RETURN"
+ : "UNKNOWN");
+ else
+ printf("verdict=%u\n", pos);
+ } else if (strcmp(t->u.user.name, IPT_ERROR_TARGET) == 0)
+ printf("error=`%s'\n", t->data);
+
+ printf("\n");
+ return 0;
+}
+
+static unsigned char *
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
+{
+ unsigned int i;
+ unsigned char *mptr;
+
+ /* Always compare head structures: ignore mask here. */
+ if (a->ip.src.s_addr != b->ip.src.s_addr
+ || a->ip.dst.s_addr != b->ip.dst.s_addr
+ || a->ip.smsk.s_addr != b->ip.smsk.s_addr
+ || a->ip.dmsk.s_addr != b->ip.dmsk.s_addr
+ || a->ip.proto != b->ip.proto
+ || a->ip.flags != b->ip.flags
+ || a->ip.invflags != b->ip.invflags)
+ return NULL;
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
+ return NULL;
+ if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
+ != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
+ return NULL;
+ if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
+ return NULL;
+ if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
+ != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
+ return NULL;
+ }
+
+ if (a->target_offset != b->target_offset
+ || a->next_offset != b->next_offset)
+ return NULL;
+
+ mptr = matchmask + sizeof(STRUCT_ENTRY);
+ if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+ return NULL;
+ mptr += IPT_ALIGN(sizeof(struct ipt_entry_target));
+
+ return mptr;
+}
+
+#if 0
+/***************************** DEBUGGING ********************************/
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++)
+ if (((u_int32_t *)ip)[i])
+ return 0;
+
+ return 1;
+}
+
+static inline int
+check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off)
+{
+ assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH));
+ assert(ALIGN(m->u.match_size) == m->u.match_size);
+
+ (*off) += m->u.match_size;
+ return 0;
+}
+
+static inline int
+check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
+ unsigned int user_offset, int *was_return,
+ TC_HANDLE_T h)
+{
+ unsigned int toff;
+ STRUCT_STANDARD_TARGET *t;
+
+ assert(e->target_offset >= sizeof(STRUCT_ENTRY));
+ assert(e->next_offset >= e->target_offset
+ + sizeof(STRUCT_ENTRY_TARGET));
+ toff = sizeof(STRUCT_ENTRY);
+ IPT_MATCH_ITERATE(e, check_match, &toff);
+
+ assert(toff == e->target_offset);
+
+ t = (STRUCT_STANDARD_TARGET *)
+ GET_TARGET((STRUCT_ENTRY *)e);
+ /* next_offset will have to be multiple of entry alignment. */
+ assert(e->next_offset == ALIGN(e->next_offset));
+ assert(e->target_offset == ALIGN(e->target_offset));
+ assert(t->target.u.target_size == ALIGN(t->target.u.target_size));
+ assert(!TC_IS_CHAIN(t->target.u.user.name, h));
+
+ if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) {
+ assert(t->target.u.target_size
+ == ALIGN(sizeof(STRUCT_STANDARD_TARGET)));
+
+ assert(t->verdict == -NF_DROP-1
+ || t->verdict == -NF_ACCEPT-1
+ || t->verdict == RETURN
+ || t->verdict < (int)h->entries->size);
+
+ if (t->verdict >= 0) {
+ STRUCT_ENTRY *te = get_entry(h, t->verdict);
+ int idx;
+
+ idx = iptcb_entry2index(h, te);
+ assert(strcmp(GET_TARGET(te)->u.user.name,
+ IPT_ERROR_TARGET)
+ != 0);
+ assert(te != e);
+
+ /* Prior node must be error node, or this node. */
+ assert(t->verdict == iptcb_entry2offset(h, e)+e->next_offset
+ || strcmp(GET_TARGET(index2entry(h, idx-1))
+ ->u.user.name, IPT_ERROR_TARGET)
+ == 0);
+ }
+
+ if (t->verdict == RETURN
+ && unconditional(&e->ip)
+ && e->target_offset == sizeof(*e))
+ *was_return = 1;
+ else
+ *was_return = 0;
+ } else if (strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0) {
+ assert(t->target.u.target_size
+ == ALIGN(sizeof(struct ipt_error_target)));
+
+ /* If this is in user area, previous must have been return */
+ if (*off > user_offset)
+ assert(*was_return);
+
+ *was_return = 0;
+ }
+ else *was_return = 0;
+
+ if (*off == user_offset)
+ assert(strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0);
+
+ (*off) += e->next_offset;
+ (*i)++;
+ return 0;
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+ unsigned int i, n;
+ unsigned int user_offset; /* Offset of first user chain */
+ int was_return;
+
+ assert(h->changed == 0 || h->changed == 1);
+ if (strcmp(h->info.name, "filter") == 0) {
+ assert(h->info.valid_hooks
+ == (1 << NF_IP_LOCAL_IN
+ | 1 << NF_IP_FORWARD
+ | 1 << NF_IP_LOCAL_OUT));
+
+ /* Hooks should be first three */
+ assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
+
+ n = get_chain_end(h, 0);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+ } else if (strcmp(h->info.name, "nat") == 0) {
+ assert((h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_POST_ROUTING
+ | 1 << NF_IP_LOCAL_OUT)) ||
+ (h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_LOCAL_IN
+ | 1 << NF_IP_POST_ROUTING
+ | 1 << NF_IP_LOCAL_OUT)));
+
+ assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, 0);
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+ n = get_chain_end(h, n);
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+ if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_IN];
+ }
+
+ } else if (strcmp(h->info.name, "mangle") == 0) {
+ /* This code is getting ugly because linux < 2.4.18-pre6 had
+ * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+ * */
+ assert((h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_LOCAL_OUT)) ||
+ (h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_LOCAL_IN
+ | 1 << NF_IP_FORWARD
+ | 1 << NF_IP_LOCAL_OUT
+ | 1 << NF_IP_POST_ROUTING)));
+
+ /* Hooks should be first five */
+ assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, 0);
+
+ if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+ n = get_chain_end(h, n);
+ }
+
+ if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) {
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+ n = get_chain_end(h, n);
+ }
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+ if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) {
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+ user_offset = h->info.hook_entry[NF_IP_POST_ROUTING];
+ }
+ } else if (strcmp(h->info.name, "raw") == 0) {
+ assert(h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_LOCAL_OUT));
+
+ /* Hooks should be first three */
+ assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+#ifdef NF_IP_DROPPING
+ } else if (strcmp(h->info.name, "drop") == 0) {
+ assert(h->info.valid_hooks == (1 << NF_IP_DROPPING));
+
+ /* Hook should be first */
+ assert(h->info.hook_entry[NF_IP_DROPPING] == 0);
+ user_offset = 0;
+#endif
+ } else {
+ fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+ abort();
+ }
+
+ /* User chain == end of last builtin + policy entry */
+ user_offset = get_chain_end(h, user_offset);
+ user_offset += get_entry(h, user_offset)->next_offset;
+
+ /* Overflows should be end of entry chains, and unconditional
+ policy nodes. */
+ for (i = 0; i < NUMHOOKS; i++) {
+ STRUCT_ENTRY *e;
+ STRUCT_STANDARD_TARGET *t;
+
+ if (!(h->info.valid_hooks & (1 << i)))
+ continue;
+ assert(h->info.underflow[i]
+ == get_chain_end(h, h->info.hook_entry[i]));
+
+ e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+ assert(unconditional(&e->ip));
+ assert(e->target_offset == sizeof(*e));
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+ assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+ assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+ assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+ assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+ /* Hooks and underflows must be valid entries */
+ entry2index(h, get_entry(h, h->info.hook_entry[i]));
+ entry2index(h, get_entry(h, h->info.underflow[i]));
+ }
+
+ assert(h->info.size
+ >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+ +sizeof(STRUCT_STANDARD_TARGET)));
+
+ assert(h->entries.size
+ >= (h->new_number
+ * (sizeof(STRUCT_ENTRY)
+ + sizeof(STRUCT_STANDARD_TARGET))));
+ assert(strcmp(h->info.name, h->entries.name) == 0);
+
+ i = 0; n = 0;
+ was_return = 0;
+ /* Check all the entries. */
+ ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+ check_entry, &i, &n, user_offset, &was_return, h);
+
+ assert(i == h->new_number);
+ assert(n == h->entries.size);
+
+ /* Final entry must be error node */
+ assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+ ->u.user.name,
+ ERROR_TARGET) == 0);
+}
+#endif /*IPTC_DEBUG*/
+
+#endif
--- /dev/null
+/**
+ * This file was imported from the iptables sources.
+ * Copyright (C) 1999-2008 Netfilter Core Team
+ *
+ * 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
+ */
+
+/* Library which manipulates firewall rules. Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+ COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libip6tc.h"
+
+#define HOOK_PRE_ROUTING NF_IP6_PRE_ROUTING
+#define HOOK_LOCAL_IN NF_IP6_LOCAL_IN
+#define HOOK_FORWARD NF_IP6_FORWARD
+#define HOOK_LOCAL_OUT NF_IP6_LOCAL_OUT
+#define HOOK_POST_ROUTING NF_IP6_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET struct ip6t_entry_target
+#define STRUCT_ENTRY struct ip6t_entry
+#define STRUCT_ENTRY_MATCH struct ip6t_entry_match
+#define STRUCT_GETINFO struct ip6t_getinfo
+#define STRUCT_GET_ENTRIES struct ip6t_get_entries
+#define STRUCT_COUNTERS struct ip6t_counters
+#define STRUCT_COUNTERS_INFO struct ip6t_counters_info
+#define STRUCT_STANDARD_TARGET struct ip6t_standard_target
+#define STRUCT_REPLACE struct ip6t_replace
+
+#define STRUCT_TC_HANDLE struct ip6tc_handle
+#define TC_HANDLE_T ip6tc_handle_t
+
+#define ENTRY_ITERATE IP6T_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN IP6T_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN IP6T_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET ip6t_get_target
+
+#define ERROR_TARGET IP6T_ERROR_TARGET
+#define NUMHOOKS NF_IP6_NUMHOOKS
+
+#define IPT_CHAINLABEL ip6t_chainlabel
+
+#define TC_DUMP_ENTRIES dump_entries6
+#define TC_IS_CHAIN ip6tc_is_chain
+#define TC_FIRST_CHAIN ip6tc_first_chain
+#define TC_NEXT_CHAIN ip6tc_next_chain
+#define TC_FIRST_RULE ip6tc_first_rule
+#define TC_NEXT_RULE ip6tc_next_rule
+#define TC_GET_TARGET ip6tc_get_target
+#define TC_BUILTIN ip6tc_builtin
+#define TC_GET_POLICY ip6tc_get_policy
+#define TC_INSERT_ENTRY ip6tc_insert_entry
+#define TC_REPLACE_ENTRY ip6tc_replace_entry
+#define TC_APPEND_ENTRY ip6tc_append_entry
+#define TC_DELETE_ENTRY ip6tc_delete_entry
+#define TC_DELETE_NUM_ENTRY ip6tc_delete_num_entry
+#define TC_CHECK_PACKET ip6tc_check_packet
+#define TC_FLUSH_ENTRIES ip6tc_flush_entries
+#define TC_ZERO_ENTRIES ip6tc_zero_entries
+#define TC_ZERO_COUNTER ip6tc_zero_counter
+#define TC_READ_COUNTER ip6tc_read_counter
+#define TC_SET_COUNTER ip6tc_set_counter
+#define TC_CREATE_CHAIN ip6tc_create_chain
+#define TC_GET_REFERENCES ip6tc_get_references
+#define TC_DELETE_CHAIN ip6tc_delete_chain
+#define TC_RENAME_CHAIN ip6tc_rename_chain
+#define TC_SET_POLICY ip6tc_set_policy
+#define TC_GET_RAW_SOCKET ip6tc_get_raw_socket
+#define TC_INIT ip6tc_init
+#define TC_FREE ip6tc_free
+#define TC_COMMIT ip6tc_commit
+#define TC_STRERROR ip6tc_strerror
+#define TC_NUM_RULES ip6tc_num_rules
+#define TC_GET_RULE ip6tc_get_rule
+
+#define TC_AF AF_INET6
+#define TC_IPPROTO IPPROTO_IPV6
+
+#define SO_SET_REPLACE IP6T_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS IP6T_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO IP6T_SO_GET_INFO
+#define SO_GET_ENTRIES IP6T_SO_GET_ENTRIES
+#define SO_GET_VERSION IP6T_SO_GET_VERSION
+
+#define STANDARD_TARGET IP6T_STANDARD_TARGET
+#define LABEL_RETURN IP6TC_LABEL_RETURN
+#define LABEL_ACCEPT IP6TC_LABEL_ACCEPT
+#define LABEL_DROP IP6TC_LABEL_DROP
+#define LABEL_QUEUE IP6TC_LABEL_QUEUE
+
+#define ALIGN IP6T_ALIGN
+#define RETURN IP6T_RETURN
+
+#include "libiptc.c"
+
+#define BIT6(a, l) \
+ ((ntohl(a->in6_u.u6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+
+int
+ipv6_prefix_length(const struct in6_addr *a)
+{
+ int l, i;
+ for (l = 0; l < 128; l++) {
+ if (BIT6(a, l) == 0)
+ break;
+ }
+ for (i = l + 1; i < 128; i++) {
+ if (BIT6(a, i) == 1)
+ return -1;
+ }
+ return l;
+}
+
+static int
+dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
+{
+ size_t i;
+ char buf[40];
+ int len;
+ struct ip6t_entry_target *t;
+
+ printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
+ iptcb_entry2offset(handle, e));
+ puts("SRC IP: ");
+ inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
+ puts(buf);
+ putchar('/');
+ len = ipv6_prefix_length(&e->ipv6.smsk);
+ if (len != -1)
+ printf("%d", len);
+ else {
+ inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
+ puts(buf);
+ }
+ putchar('\n');
+
+ puts("DST IP: ");
+ inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
+ puts(buf);
+ putchar('/');
+ len = ipv6_prefix_length(&e->ipv6.dmsk);
+ if (len != -1)
+ printf("%d", len);
+ else {
+ inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
+ puts(buf);
+ }
+ putchar('\n');
+
+ printf("Interface: `%s'/", e->ipv6.iniface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
+ printf("to `%s'/", e->ipv6.outiface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
+ printf("\nProtocol: %u\n", e->ipv6.proto);
+ if (e->ipv6.flags & IP6T_F_TOS)
+ printf("TOS: %u\n", e->ipv6.tos);
+ printf("Flags: %02X\n", e->ipv6.flags);
+ printf("Invflags: %02X\n", e->ipv6.invflags);
+ printf("Counters: %llu packets, %llu bytes\n",
+ (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+ printf("Cache: %08X\n", e->nfcache);
+
+ IP6T_MATCH_ITERATE(e, print_match);
+
+ t = ip6t_get_target(e);
+ printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+ if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
+ int pos = *(int *)t->data;
+ if (pos < 0)
+ printf("verdict=%s\n",
+ pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+ : pos == -NF_DROP-1 ? "NF_DROP"
+ : pos == IP6T_RETURN ? "RETURN"
+ : "UNKNOWN");
+ else
+ printf("verdict=%u\n", pos);
+ } else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
+ printf("error=`%s'\n", t->data);
+
+ printf("\n");
+ return 0;
+}
+
+static unsigned char *
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
+ unsigned char *matchmask)
+{
+ unsigned int i;
+ unsigned char *mptr;
+
+ /* Always compare head structures: ignore mask here. */
+ if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
+ || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
+ || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
+ || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
+ || a->ipv6.proto != b->ipv6.proto
+ || a->ipv6.tos != b->ipv6.tos
+ || a->ipv6.flags != b->ipv6.flags
+ || a->ipv6.invflags != b->ipv6.invflags)
+ return NULL;
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
+ return NULL;
+ if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
+ != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
+ return NULL;
+ if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
+ return NULL;
+ if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
+ != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
+ return NULL;
+ }
+
+ if (a->target_offset != b->target_offset
+ || a->next_offset != b->next_offset)
+ return NULL;
+
+ mptr = matchmask + sizeof(STRUCT_ENTRY);
+ if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+ return NULL;
+ mptr += IP6T_ALIGN(sizeof(struct ip6t_entry_target));
+
+ return mptr;
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(*ipv6); i++)
+ if (((char *)ipv6)[i])
+ break;
+
+ return (i == sizeof(*ipv6));
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+ unsigned int i, n;
+ unsigned int user_offset; /* Offset of first user chain */
+ int was_return;
+
+ assert(h->changed == 0 || h->changed == 1);
+ if (strcmp(h->info.name, "filter") == 0) {
+ assert(h->info.valid_hooks
+ == (1 << NF_IP6_LOCAL_IN
+ | 1 << NF_IP6_FORWARD
+ | 1 << NF_IP6_LOCAL_OUT));
+
+ /* Hooks should be first three */
+ assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
+
+ n = get_chain_end(h, 0);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+ } else if (strcmp(h->info.name, "nat") == 0) {
+ assert((h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_OUT
+ | 1 << NF_IP6_POST_ROUTING)) ||
+ (h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_IN
+ | 1 << NF_IP6_LOCAL_OUT
+ | 1 << NF_IP6_POST_ROUTING)));
+
+ assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, 0);
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+ n = get_chain_end(h, n);
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+ if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
+ }
+
+ } else if (strcmp(h->info.name, "mangle") == 0) {
+ /* This code is getting ugly because linux < 2.4.18-pre6 had
+ * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+ * */
+ assert((h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_OUT)) ||
+ (h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_IN
+ | 1 << NF_IP6_FORWARD
+ | 1 << NF_IP6_LOCAL_OUT
+ | 1 << NF_IP6_POST_ROUTING)));
+
+ /* Hooks should be first five */
+ assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, 0);
+
+ if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+ n = get_chain_end(h, n);
+ }
+
+ if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+ n = get_chain_end(h, n);
+ }
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+ if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+ user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
+ }
+ } else if (strcmp(h->info.name, "raw") == 0) {
+ assert(h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_OUT));
+
+ /* Hooks should be first three */
+ assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+ } else {
+ fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+ abort();
+ }
+
+ /* User chain == end of last builtin + policy entry */
+ user_offset = get_chain_end(h, user_offset);
+ user_offset += get_entry(h, user_offset)->next_offset;
+
+ /* Overflows should be end of entry chains, and unconditional
+ policy nodes. */
+ for (i = 0; i < NUMHOOKS; i++) {
+ STRUCT_ENTRY *e;
+ STRUCT_STANDARD_TARGET *t;
+
+ if (!(h->info.valid_hooks & (1 << i)))
+ continue;
+ assert(h->info.underflow[i]
+ == get_chain_end(h, h->info.hook_entry[i]));
+
+ e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+ assert(unconditional(&e->ipv6));
+ assert(e->target_offset == sizeof(*e));
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+ printf("target_size=%u, align=%u\n",
+ t->target.u.target_size, ALIGN(sizeof(*t)));
+ assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+ assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+ assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+ assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+ /* Hooks and underflows must be valid entries */
+ iptcb_entry2index(h, get_entry(h, h->info.hook_entry[i]));
+ iptcb_entry2index(h, get_entry(h, h->info.underflow[i]));
+ }
+
+ assert(h->info.size
+ >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+ +sizeof(STRUCT_STANDARD_TARGET)));
+
+ assert(h->entries.size
+ >= (h->new_number
+ * (sizeof(STRUCT_ENTRY)
+ + sizeof(STRUCT_STANDARD_TARGET))));
+ assert(strcmp(h->info.name, h->entries.name) == 0);
+
+ i = 0; n = 0;
+ was_return = 0;
+
+#if 0
+ /* Check all the entries. */
+ ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+ check_entry, &i, &n, user_offset, &was_return, h);
+
+ assert(i == h->new_number);
+ assert(n == h->entries.size);
+
+ /* Final entry must be error node */
+ assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+ ->u.user.name,
+ ERROR_TARGET) == 0);
+#endif
+}
+#endif /*IPTC_DEBUG*/
--- /dev/null
+/**
+ * This file was imported from the iptables sources.
+ * Copyright (C) 1999-2008 Netfilter Core Team
+ *
+ * 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
+ */
+
+#ifndef _LIBIP6TC_H
+#define _LIBIP6TC_H
+/* Library which manipulates firewall rules. Version 0.2. */
+
+#include <linux/types.h>
+#include "ipt_kernel_headers.h"
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+#ifndef IP6T_MIN_ALIGN
+#define IP6T_MIN_ALIGN (__alignof__(struct ip6t_entry))
+#endif
+#define IP6T_ALIGN(s) (((s) + (IP6T_MIN_ALIGN-1)) & ~(IP6T_MIN_ALIGN-1))
+
+typedef char ip6t_chainlabel[32];
+
+#define IP6TC_LABEL_ACCEPT "ACCEPT"
+#define IP6TC_LABEL_DROP "DROP"
+#define IP6TC_LABEL_QUEUE "QUEUE"
+#define IP6TC_LABEL_RETURN "RETURN"
+
+/* Transparent handle type. */
+typedef struct ip6tc_handle *ip6tc_handle_t;
+
+/* Does this chain exist? */
+int ip6tc_is_chain(const char *chain, const ip6tc_handle_t handle);
+
+/* Take a snapshot of the rules. Returns NULL on error. */
+ip6tc_handle_t ip6tc_init(const char *tablename);
+
+/* Cleanup after ip6tc_init(). */
+void ip6tc_free(ip6tc_handle_t *h);
+
+/* Iterator functions to run through the chains. Returns NULL at end. */
+const char *ip6tc_first_chain(ip6tc_handle_t *handle);
+const char *ip6tc_next_chain(ip6tc_handle_t *handle);
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const struct ip6t_entry *ip6tc_first_rule(const char *chain,
+ ip6tc_handle_t *handle);
+
+/* Returns NULL when rules run out. */
+const struct ip6t_entry *ip6tc_next_rule(const struct ip6t_entry *prev,
+ ip6tc_handle_t *handle);
+
+/* Returns a pointer to the target name of this position. */
+const char *ip6tc_get_target(const struct ip6t_entry *e,
+ ip6tc_handle_t *handle);
+
+/* Is this a built-in chain? */
+int ip6tc_builtin(const char *chain, const ip6tc_handle_t handle);
+
+/* Get the policy of a given built-in chain */
+const char *ip6tc_get_policy(const char *chain,
+ struct ip6t_counters *counters,
+ ip6tc_handle_t *handle);
+
+/* These functions return TRUE for OK or 0 and set errno. If errno ==
+ 0, it means there was a version error (ie. upgrade libiptc). */
+/* Rule numbers start at 1 for the first rule. */
+
+/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
+int ip6tc_insert_entry(const ip6t_chainlabel chain,
+ const struct ip6t_entry *e,
+ unsigned int rulenum,
+ ip6tc_handle_t *handle);
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int ip6tc_replace_entry(const ip6t_chainlabel chain,
+ const struct ip6t_entry *e,
+ unsigned int rulenum,
+ ip6tc_handle_t *handle);
+
+/* Append entry `fw' to chain `chain'. Equivalent to insert with
+ rulenum = length of chain. */
+int ip6tc_append_entry(const ip6t_chainlabel chain,
+ const struct ip6t_entry *e,
+ ip6tc_handle_t *handle);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int ip6tc_delete_entry(const ip6t_chainlabel chain,
+ const struct ip6t_entry *origfw,
+ unsigned char *matchmask,
+ ip6tc_handle_t *handle);
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int ip6tc_delete_num_entry(const ip6t_chainlabel chain,
+ unsigned int rulenum,
+ ip6tc_handle_t *handle);
+
+/* Check the packet `fw' on chain `chain'. Returns the verdict, or
+ NULL and sets errno. */
+const char *ip6tc_check_packet(const ip6t_chainlabel chain,
+ struct ip6t_entry *,
+ ip6tc_handle_t *handle);
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int ip6tc_flush_entries(const ip6t_chainlabel chain,
+ ip6tc_handle_t *handle);
+
+/* Zeroes the counters in a chain. */
+int ip6tc_zero_entries(const ip6t_chainlabel chain,
+ ip6tc_handle_t *handle);
+
+/* Creates a new chain. */
+int ip6tc_create_chain(const ip6t_chainlabel chain,
+ ip6tc_handle_t *handle);
+
+/* Deletes a chain. */
+int ip6tc_delete_chain(const ip6t_chainlabel chain,
+ ip6tc_handle_t *handle);
+
+/* Renames a chain. */
+int ip6tc_rename_chain(const ip6t_chainlabel oldname,
+ const ip6t_chainlabel newname,
+ ip6tc_handle_t *handle);
+
+/* Sets the policy on a built-in chain. */
+int ip6tc_set_policy(const ip6t_chainlabel chain,
+ const ip6t_chainlabel policy,
+ struct ip6t_counters *counters,
+ ip6tc_handle_t *handle);
+
+/* Get the number of references to this chain */
+int ip6tc_get_references(unsigned int *ref, const ip6t_chainlabel chain,
+ ip6tc_handle_t *handle);
+
+/* read packet and byte counters for a specific rule */
+struct ip6t_counters *ip6tc_read_counter(const ip6t_chainlabel chain,
+ unsigned int rulenum,
+ ip6tc_handle_t *handle);
+
+/* zero packet and byte counters for a specific rule */
+int ip6tc_zero_counter(const ip6t_chainlabel chain,
+ unsigned int rulenum,
+ ip6tc_handle_t *handle);
+
+/* set packet and byte counters for a specific rule */
+int ip6tc_set_counter(const ip6t_chainlabel chain,
+ unsigned int rulenum,
+ struct ip6t_counters *counters,
+ ip6tc_handle_t *handle);
+
+/* Makes the actual changes. */
+int ip6tc_commit(ip6tc_handle_t *handle);
+
+/* Get raw socket. */
+int ip6tc_get_raw_socket();
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *ip6tc_strerror(int err);
+
+/* Return prefix length, or -1 if not contiguous */
+int ipv6_prefix_length(const struct in6_addr *a);
+
+#endif /* _LIBIP6TC_H */
--- /dev/null
+/**
+ * This file was imported from the iptables sources.
+ * Copyright (C) 1999-2008 Netfilter Core Team
+ *
+ * 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
+ */
+
+/* Library which manipulates firewall rules. Version $Revision: 7138 $ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+ * COPYING for details).
+ * (C) 2000-2004 by the Netfilter Core Team <coreteam@netfilter.org>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
+ * - Reimplementation of chain cache to use offsets instead of entries
+ * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
+ * - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
+ * don't rebuild the chain cache after every operation, instead fix it
+ * up after a ruleset change.
+ * 2004-Aug-18: Harald Welte <laforge@netfilter.org>:
+ * - futher performance work: total reimplementation of libiptc.
+ * - libiptc now has a real internal (linked-list) represntation of the
+ * ruleset and a parser/compiler from/to this internal representation
+ * - again sponsored by Astaro AG (http://www.astaro.com/)
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "linux_list.h"
+
+//#define IPTC_DEBUG2 1
+
+#ifdef IPTC_DEBUG2
+#include <fcntl.h>
+#define DEBUGP(x, args...) fprintf(stderr, "%s: " x, __FUNCTION__, ## args)
+#define DEBUGP_C(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#define DEBUGP_C(x, args...)
+#endif
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/local/lib/iptables"
+#endif
+
+static int sockfd = -1;
+static int sockfd_use = 0;
+static void *iptc_fn = NULL;
+
+static const char *hooknames[] = {
+ [HOOK_PRE_ROUTING] = "PREROUTING",
+ [HOOK_LOCAL_IN] = "INPUT",
+ [HOOK_FORWARD] = "FORWARD",
+ [HOOK_LOCAL_OUT] = "OUTPUT",
+ [HOOK_POST_ROUTING] = "POSTROUTING",
+#ifdef HOOK_DROPPING
+ [HOOK_DROPPING] = "DROPPING"
+#endif
+};
+
+/* Convenience structures */
+struct ipt_error_target
+{
+ STRUCT_ENTRY_TARGET t;
+ char error[TABLE_MAXNAMELEN];
+};
+
+struct chain_head;
+struct rule_head;
+
+struct counter_map
+{
+ enum {
+ COUNTER_MAP_NOMAP,
+ COUNTER_MAP_NORMAL_MAP,
+ COUNTER_MAP_ZEROED,
+ COUNTER_MAP_SET
+ } maptype;
+ unsigned int mappos;
+};
+
+enum iptcc_rule_type {
+ IPTCC_R_STANDARD, /* standard target (ACCEPT, ...) */
+ IPTCC_R_MODULE, /* extension module (SNAT, ...) */
+ IPTCC_R_FALLTHROUGH, /* fallthrough rule */
+ IPTCC_R_JUMP, /* jump to other chain */
+};
+
+struct rule_head
+{
+ struct list_head list;
+ struct chain_head *chain;
+ struct counter_map counter_map;
+
+ unsigned int index; /* index (needed for counter_map) */
+ unsigned int offset; /* offset in rule blob */
+
+ enum iptcc_rule_type type;
+ struct chain_head *jump; /* jump target, if IPTCC_R_JUMP */
+
+ unsigned int size; /* size of entry data */
+ STRUCT_ENTRY entry[0];
+};
+
+struct chain_head
+{
+ struct list_head list;
+ char name[TABLE_MAXNAMELEN];
+ unsigned int hooknum; /* hook number+1 if builtin */
+ unsigned int references; /* how many jumps reference us */
+ int verdict; /* verdict if builtin */
+
+ STRUCT_COUNTERS counters; /* per-chain counters */
+ struct counter_map counter_map;
+
+ unsigned int num_rules; /* number of rules in list */
+ struct list_head rules; /* list of rules */
+
+ unsigned int index; /* index (needed for jump resolval) */
+ unsigned int head_offset; /* offset in rule blob */
+ unsigned int foot_index; /* index (needed for counter_map) */
+ unsigned int foot_offset; /* offset in rule blob */
+};
+
+STRUCT_TC_HANDLE
+{
+ int changed; /* Have changes been made? */
+
+ struct list_head chains;
+
+ struct chain_head *chain_iterator_cur;
+ struct rule_head *rule_iterator_cur;
+
+ STRUCT_GETINFO info;
+ STRUCT_GET_ENTRIES *entries;
+};
+
+/* allocate a new chain head for the cache */
+static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum)
+{
+ struct chain_head *c = malloc(sizeof(*c));
+ if (!c)
+ return NULL;
+ memset(c, 0, sizeof(*c));
+
+ strncpy(c->name, name, TABLE_MAXNAMELEN);
+ c->hooknum = hooknum;
+ INIT_LIST_HEAD(&c->rules);
+
+ return c;
+}
+
+/* allocate and initialize a new rule for the cache */
+static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int size)
+{
+ struct rule_head *r = malloc(sizeof(*r)+size);
+ if (!r)
+ return NULL;
+ memset(r, 0, sizeof(*r));
+
+ r->chain = c;
+ r->size = size;
+
+ return r;
+}
+
+/* notify us that the ruleset has been modified by the user */
+static void
+set_changed(TC_HANDLE_T h)
+{
+ h->changed = 1;
+}
+
+#ifdef IPTC_DEBUG
+static void do_check(TC_HANDLE_T h, unsigned int line);
+#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+
+/**********************************************************************
+ * iptc blob utility functions (iptcb_*)
+ **********************************************************************/
+
+static inline int
+iptcb_get_number(const STRUCT_ENTRY *i,
+ const STRUCT_ENTRY *seek,
+ unsigned int *pos)
+{
+ if (i == seek)
+ return 1;
+ (*pos)++;
+ return 0;
+}
+
+static inline int
+iptcb_get_entry_n(STRUCT_ENTRY *i,
+ unsigned int number,
+ unsigned int *pos,
+ STRUCT_ENTRY **pe)
+{
+ if (*pos == number) {
+ *pe = i;
+ return 1;
+ }
+ (*pos)++;
+ return 0;
+}
+
+static inline STRUCT_ENTRY *
+iptcb_get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+ return (STRUCT_ENTRY *)((char *)h->entries->entrytable + offset);
+}
+
+static unsigned int
+iptcb_entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
+{
+ unsigned int pos = 0;
+
+ if (ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
+ iptcb_get_number, seek, &pos) == 0) {
+ fprintf(stderr, "ERROR: offset %u not an entry!\n",
+ (unsigned int)((char *)seek - (char *)h->entries->entrytable));
+ abort();
+ }
+ return pos;
+}
+
+static inline STRUCT_ENTRY *
+iptcb_offset2entry(TC_HANDLE_T h, unsigned int offset)
+{
+ return (STRUCT_ENTRY *) ((void *)h->entries->entrytable+offset);
+}
+
+
+static inline unsigned long
+iptcb_entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+ return (void *)e - (void *)h->entries->entrytable;
+}
+
+static inline unsigned int
+iptcb_offset2index(const TC_HANDLE_T h, unsigned int offset)
+{
+ return iptcb_entry2index(h, iptcb_offset2entry(h, offset));
+}
+
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUMHOOKS; i++) {
+ if ((h->info.valid_hooks & (1 << i))
+ && iptcb_get_entry(h, h->info.hook_entry[i]) == e)
+ return i+1;
+ }
+ return 0;
+}
+
+
+/**********************************************************************
+ * iptc cache utility functions (iptcc_*)
+ **********************************************************************/
+
+/* Is the given chain builtin (1) or user-defined (0) */
+static unsigned int iptcc_is_builtin(struct chain_head *c)
+{
+ return (c->hooknum ? 1 : 0);
+}
+
+/* Get a specific rule within a chain */
+static struct rule_head *iptcc_get_rule_num(struct chain_head *c,
+ unsigned int rulenum)
+{
+ struct rule_head *r;
+ unsigned int num = 0;
+
+ list_for_each_entry(r, &c->rules, list) {
+ num++;
+ if (num == rulenum)
+ return r;
+ }
+ return NULL;
+}
+
+/* Get a specific rule within a chain backwards */
+static struct rule_head *iptcc_get_rule_num_reverse(struct chain_head *c,
+ unsigned int rulenum)
+{
+ struct rule_head *r;
+ unsigned int num = 0;
+
+ list_for_each_entry_reverse(r, &c->rules, list) {
+ num++;
+ if (num == rulenum)
+ return r;
+ }
+ return NULL;
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset)
+{
+ struct list_head *pos;
+
+ if (list_empty(&handle->chains))
+ return NULL;
+
+ list_for_each(pos, &handle->chains) {
+ struct chain_head *c = list_entry(pos, struct chain_head, list);
+ if (offset >= c->head_offset && offset <= c->foot_offset)
+ return c;
+ }
+
+ return NULL;
+}
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+iptcc_find_label(const char *name, TC_HANDLE_T handle)
+{
+ struct list_head *pos;
+
+ if (list_empty(&handle->chains))
+ return NULL;
+
+ list_for_each(pos, &handle->chains) {
+ struct chain_head *c = list_entry(pos, struct chain_head, list);
+ if (!strcmp(c->name, name))
+ return c;
+ }
+
+ return NULL;
+}
+
+/* called when rule is to be removed from cache */
+static void iptcc_delete_rule(struct rule_head *r)
+{
+ DEBUGP("deleting rule %p (offset %u)\n", r, r->offset);
+ /* clean up reference count of called chain */
+ if (r->type == IPTCC_R_JUMP
+ && r->jump)
+ r->jump->references--;
+
+ list_del(&r->list);
+ free(r);
+}
+
+
+/**********************************************************************
+ * RULESET PARSER (blob -> cache)
+ **********************************************************************/
+
+/* Delete policy rule of previous chain, since cache doesn't contain
+ * chain policy rules.
+ * WARNING: This function has ugly design and relies on a lot of context, only
+ * to be called from specific places within the parser */
+static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num)
+{
+ if (h->chain_iterator_cur) {
+ /* policy rule is last rule */
+ struct rule_head *pr = (struct rule_head *)
+ h->chain_iterator_cur->rules.prev;
+
+ /* save verdict */
+ h->chain_iterator_cur->verdict =
+ *(int *)GET_TARGET(pr->entry)->data;
+
+ /* save counter and counter_map information */
+ h->chain_iterator_cur->counter_map.maptype =
+ COUNTER_MAP_NORMAL_MAP;
+ h->chain_iterator_cur->counter_map.mappos = num-1;
+ memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters,
+ sizeof(h->chain_iterator_cur->counters));
+
+ /* foot_offset points to verdict rule */
+ h->chain_iterator_cur->foot_index = num;
+ h->chain_iterator_cur->foot_offset = pr->offset;
+
+ /* delete rule from cache */
+ iptcc_delete_rule(pr);
+ h->chain_iterator_cur->num_rules--;
+
+ return 1;
+ }
+ return 0;
+}
+
+/* alphabetically insert a chain into the list */
+static inline void iptc_insert_chain(TC_HANDLE_T h, struct chain_head *c)
+{
+ struct chain_head *tmp;
+
+ /* sort only user defined chains */
+ if (!c->hooknum) {
+ list_for_each_entry(tmp, &h->chains, list) {
+ if (!tmp->hooknum && strcmp(c->name, tmp->name) <= 0) {
+ list_add(&c->list, tmp->list.prev);
+ return;
+ }
+ }
+ }
+
+ /* survived till end of list: add at tail */
+ list_add_tail(&c->list, &h->chains);
+}
+
+/* Another ugly helper function split out of cache_add_entry to make it less
+ * spaghetti code */
+static void __iptcc_p_add_chain(TC_HANDLE_T h, struct chain_head *c,
+ unsigned int offset, unsigned int *num)
+{
+ struct list_head *tail = h->chains.prev;
+ struct chain_head *ctail;
+
+ __iptcc_p_del_policy(h, *num);
+
+ c->head_offset = offset;
+ c->index = *num;
+
+ /* Chains from kernel are already sorted, as they are inserted
+ * sorted. But there exists an issue when shifting to 1.4.0
+ * from an older version, as old versions allow last created
+ * chain to be unsorted.
+ */
+ if (iptcc_is_builtin(c)) /* Only user defined chains are sorted*/
+ list_add_tail(&c->list, &h->chains);
+ else {
+ ctail = list_entry(tail, struct chain_head, list);
+ if (strcmp(c->name, ctail->name) > 0)
+ list_add_tail(&c->list, &h->chains);/* Already sorted*/
+ else
+ iptc_insert_chain(h, c);/* Was not sorted */
+ }
+
+ h->chain_iterator_cur = c;
+}
+
+/* main parser function: add an entry from the blob to the cache */
+static int cache_add_entry(STRUCT_ENTRY *e,
+ TC_HANDLE_T h,
+ STRUCT_ENTRY **prev,
+ unsigned int *num)
+{
+ unsigned int builtin;
+ unsigned int offset = (char *)e - (char *)h->entries->entrytable;
+
+ DEBUGP("entering...");
+
+ /* Last entry ("policy rule"). End it.*/
+ if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) {
+ /* This is the ERROR node at the end of the chain */
+ DEBUGP_C("%u:%u: end of table:\n", *num, offset);
+
+ __iptcc_p_del_policy(h, *num);
+
+ h->chain_iterator_cur = NULL;
+ goto out_inc;
+ }
+
+ /* We know this is the start of a new chain if it's an ERROR
+ * target, or a hook entry point */
+
+ if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+ struct chain_head *c =
+ iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0);
+ DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset,
+ (char *)c->name, c);
+ if (!c) {
+ errno = -ENOMEM;
+ return -1;
+ }
+
+ __iptcc_p_add_chain(h, c, offset, num);
+
+ } else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) {
+ struct chain_head *c =
+ iptcc_alloc_chain_head((char *)hooknames[builtin-1],
+ builtin);
+ DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n",
+ *num, offset, c, &c->rules);
+ if (!c) {
+ errno = -ENOMEM;
+ return -1;
+ }
+
+ c->hooknum = builtin;
+
+ __iptcc_p_add_chain(h, c, offset, num);
+
+ /* FIXME: this is ugly. */
+ goto new_rule;
+ } else {
+ /* has to be normal rule */
+ struct rule_head *r;
+new_rule:
+
+ if (!(r = iptcc_alloc_rule(h->chain_iterator_cur,
+ e->next_offset))) {
+ errno = ENOMEM;
+ return -1;
+ }
+ DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
+
+ r->index = *num;
+ r->offset = offset;
+ memcpy(r->entry, e, e->next_offset);
+ r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
+ r->counter_map.mappos = r->index;
+
+ /* handling of jumps, etc. */
+ if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) {
+ STRUCT_STANDARD_TARGET *t;
+
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+ if (t->target.u.target_size
+ != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (t->verdict < 0) {
+ DEBUGP_C("standard, verdict=%d\n", t->verdict);
+ r->type = IPTCC_R_STANDARD;
+ } else if (t->verdict == r->offset+e->next_offset) {
+ DEBUGP_C("fallthrough\n");
+ r->type = IPTCC_R_FALLTHROUGH;
+ } else {
+ DEBUGP_C("jump, target=%u\n", t->verdict);
+ r->type = IPTCC_R_JUMP;
+ /* Jump target fixup has to be deferred
+ * until second pass, since we migh not
+ * yet have parsed the target */
+ }
+ } else {
+ DEBUGP_C("module, target=%s\n", GET_TARGET(e)->u.user.name);
+ r->type = IPTCC_R_MODULE;
+ }
+
+ list_add_tail(&r->list, &h->chain_iterator_cur->rules);
+ h->chain_iterator_cur->num_rules++;
+ }
+out_inc:
+ (*num)++;
+ return 0;
+}
+
+
+/* parse an iptables blob into it's pieces */
+static int parse_table(TC_HANDLE_T h)
+{
+ STRUCT_ENTRY *prev;
+ unsigned int num = 0;
+ struct chain_head *c;
+
+ /* First pass: over ruleset blob */
+ ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
+ cache_add_entry, h, &prev, &num);
+
+ /* Second pass: fixup parsed data from first pass */
+ list_for_each_entry(c, &h->chains, list) {
+ struct rule_head *r;
+ list_for_each_entry(r, &c->rules, list) {
+ struct chain_head *c;
+ STRUCT_STANDARD_TARGET *t;
+
+ if (r->type != IPTCC_R_JUMP)
+ continue;
+
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+ c = iptcc_find_chain_by_offset(h, t->verdict);
+ if (!c)
+ return -1;
+ r->jump = c;
+ c->references++;
+ }
+ }
+
+ /* FIXME: sort chains */
+
+ return 1;
+}
+
+
+/**********************************************************************
+ * RULESET COMPILATION (cache -> blob)
+ **********************************************************************/
+
+/* Convenience structures */
+struct iptcb_chain_start{
+ STRUCT_ENTRY e;
+ struct ipt_error_target name;
+};
+#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) + \
+ ALIGN(sizeof(struct ipt_error_target)))
+
+struct iptcb_chain_foot {
+ STRUCT_ENTRY e;
+ STRUCT_STANDARD_TARGET target;
+};
+#define IPTCB_CHAIN_FOOT_SIZE (sizeof(STRUCT_ENTRY) + \
+ ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
+
+struct iptcb_chain_error {
+ STRUCT_ENTRY entry;
+ struct ipt_error_target target;
+};
+#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) + \
+ ALIGN(sizeof(struct ipt_error_target)))
+
+
+
+/* compile rule from cache into blob */
+static inline int iptcc_compile_rule (TC_HANDLE_T h, STRUCT_REPLACE *repl, struct rule_head *r)
+{
+ /* handle jumps */
+ if (r->type == IPTCC_R_JUMP) {
+ STRUCT_STANDARD_TARGET *t;
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+ /* memset for memcmp convenience on delete/replace */
+ memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+ strcpy(t->target.u.user.name, STANDARD_TARGET);
+ /* Jumps can only happen to builtin chains, so we
+ * can safely assume that they always have a header */
+ t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
+ } else if (r->type == IPTCC_R_FALLTHROUGH) {
+ STRUCT_STANDARD_TARGET *t;
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+ t->verdict = r->offset + r->size;
+ }
+
+ /* copy entry from cache to blob */
+ memcpy((char *)repl->entries+r->offset, r->entry, r->size);
+
+ return 1;
+}
+
+/* compile chain from cache into blob */
+static int iptcc_compile_chain(TC_HANDLE_T h, STRUCT_REPLACE *repl, struct chain_head *c)
+{
+ int ret;
+ struct rule_head *r;
+ struct iptcb_chain_start *head;
+ struct iptcb_chain_foot *foot;
+
+ /* only user-defined chains have heaer */
+ if (!iptcc_is_builtin(c)) {
+ /* put chain header in place */
+ head = (void *)repl->entries + c->head_offset;
+ head->e.target_offset = sizeof(STRUCT_ENTRY);
+ head->e.next_offset = IPTCB_CHAIN_START_SIZE;
+ strcpy(head->name.t.u.user.name, ERROR_TARGET);
+ head->name.t.u.target_size =
+ ALIGN(sizeof(struct ipt_error_target));
+ strcpy(head->name.error, c->name);
+ } else {
+ repl->hook_entry[c->hooknum-1] = c->head_offset;
+ repl->underflow[c->hooknum-1] = c->foot_offset;
+ }
+
+ /* iterate over rules */
+ list_for_each_entry(r, &c->rules, list) {
+ ret = iptcc_compile_rule(h, repl, r);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* put chain footer in place */
+ foot = (void *)repl->entries + c->foot_offset;
+ foot->e.target_offset = sizeof(STRUCT_ENTRY);
+ foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
+ strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
+ foot->target.target.u.target_size =
+ ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+ /* builtin targets have verdict, others return */
+ if (iptcc_is_builtin(c))
+ foot->target.verdict = c->verdict;
+ else
+ foot->target.verdict = RETURN;
+ /* set policy-counters */
+ memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
+
+ return 0;
+}
+
+/* calculate offset and number for every rule in the cache */
+static int iptcc_compile_chain_offsets(TC_HANDLE_T h, struct chain_head *c,
+ unsigned int *offset, unsigned int *num)
+{
+ struct rule_head *r;
+
+ c->head_offset = *offset;
+ DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset);
+
+ if (!iptcc_is_builtin(c)) {
+ /* Chain has header */
+ *offset += sizeof(STRUCT_ENTRY)
+ + ALIGN(sizeof(struct ipt_error_target));
+ (*num)++;
+ }
+
+ list_for_each_entry(r, &c->rules, list) {
+ DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num);
+ r->offset = *offset;
+ r->index = *num;
+ *offset += r->size;
+ (*num)++;
+ }
+
+ DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num,
+ *offset, *num);
+ c->foot_offset = *offset;
+ c->foot_index = *num;
+ *offset += sizeof(STRUCT_ENTRY)
+ + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+ (*num)++;
+
+ return 1;
+}
+
+/* put the pieces back together again */
+static int iptcc_compile_table_prep(TC_HANDLE_T h, unsigned int *size)
+{
+ struct chain_head *c;
+ unsigned int offset = 0, num = 0;
+ int ret = 0;
+
+ /* First pass: calculate offset for every rule */
+ list_for_each_entry(c, &h->chains, list) {
+ ret = iptcc_compile_chain_offsets(h, c, &offset, &num);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Append one error rule at end of chain */
+ num++;
+ offset += sizeof(STRUCT_ENTRY)
+ + ALIGN(sizeof(struct ipt_error_target));
+
+ /* ruleset size is now in offset */
+ *size = offset;
+ return num;
+}
+
+static int iptcc_compile_table(TC_HANDLE_T h, STRUCT_REPLACE *repl)
+{
+ struct chain_head *c;
+ struct iptcb_chain_error *error;
+
+ /* Second pass: copy from cache to offsets, fill in jumps */
+ list_for_each_entry(c, &h->chains, list) {
+ int ret = iptcc_compile_chain(h, repl, c);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Append error rule at end of chain */
+ error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
+ error->entry.target_offset = sizeof(STRUCT_ENTRY);
+ error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
+ error->target.t.u.user.target_size =
+ ALIGN(sizeof(struct ipt_error_target));
+ strcpy((char *)&error->target.t.u.user.name, ERROR_TARGET);
+ strcpy((char *)&error->target.error, "ERROR");
+
+ return 1;
+}
+
+/**********************************************************************
+ * EXTERNAL API (operates on cache only)
+ **********************************************************************/
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
+{
+ size_t len;
+ TC_HANDLE_T h;
+
+ len = sizeof(STRUCT_TC_HANDLE) + size;
+
+ h = malloc(sizeof(STRUCT_TC_HANDLE));
+ if (!h) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ memset(h, 0, sizeof(*h));
+ INIT_LIST_HEAD(&h->chains);
+ strcpy(h->info.name, tablename);
+
+ h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + size);
+ if (!h->entries)
+ goto out_free_handle;
+
+ strcpy(h->entries->name, tablename);
+ h->entries->size = size;
+
+ return h;
+
+out_free_handle:
+ free(h);
+
+ return NULL;
+}
+
+
+TC_HANDLE_T
+TC_INIT(const char *tablename)
+{
+ TC_HANDLE_T h;
+ STRUCT_GETINFO info;
+ unsigned int tmp;
+ socklen_t s;
+
+ iptc_fn = TC_INIT;
+
+ if (strlen(tablename) >= TABLE_MAXNAMELEN) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (sockfd_use == 0) {
+ sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
+ if (sockfd < 0)
+ return NULL;
+ }
+ sockfd_use++;
+
+ s = sizeof(info);
+
+ strcpy(info.name, tablename);
+ if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) {
+ if (--sockfd_use == 0) {
+ close(sockfd);
+ sockfd = -1;
+ }
+ return NULL;
+ }
+
+ DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
+ info.valid_hooks, info.num_entries, info.size);
+
+ if ((h = alloc_handle(info.name, info.size, info.num_entries))
+ == NULL) {
+ if (--sockfd_use == 0) {
+ close(sockfd);
+ sockfd = -1;
+ }
+ return NULL;
+ }
+
+ /* Initialize current state */
+ h->info = info;
+
+ h->entries->size = h->info.size;
+
+ tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
+
+ if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
+ &tmp) < 0)
+ goto error;
+
+#ifdef IPTC_DEBUG2
+ {
+ int fd = open("/tmp/libiptc-so_get_entries.blob",
+ O_CREAT|O_WRONLY);
+ if (fd >= 0) {
+ write(fd, h->entries, tmp);
+ close(fd);
+ }
+ }
+#endif
+
+ if (parse_table(h) < 0)
+ goto error;
+
+ CHECK(h);
+ return h;
+error:
+ TC_FREE(&h);
+ return NULL;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+ struct chain_head *c, *tmp;
+
+ iptc_fn = TC_FREE;
+ if (--sockfd_use == 0) {
+ close(sockfd);
+ sockfd = -1;
+ }
+
+ list_for_each_entry_safe(c, tmp, &(*h)->chains, list) {
+ struct rule_head *r, *rtmp;
+
+ list_for_each_entry_safe(r, rtmp, &c->rules, list) {
+ free(r);
+ }
+
+ free(c);
+ }
+
+ free((*h)->entries);
+ free(*h);
+
+ *h = NULL;
+}
+
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+ printf("Match name: `%s'\n", m->u.user.name);
+ return 0;
+}
+
+static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
+
+void
+TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
+{
+ iptc_fn = TC_DUMP_ENTRIES;
+ CHECK(handle);
+
+ printf("libiptc v%s. %u bytes.\n",
+ IPTABLES_VERSION, handle->entries->size);
+ printf("Table `%s'\n", handle->info.name);
+ printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+ handle->info.hook_entry[HOOK_PRE_ROUTING],
+ handle->info.hook_entry[HOOK_LOCAL_IN],
+ handle->info.hook_entry[HOOK_FORWARD],
+ handle->info.hook_entry[HOOK_LOCAL_OUT],
+ handle->info.hook_entry[HOOK_POST_ROUTING]);
+ printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+ handle->info.underflow[HOOK_PRE_ROUTING],
+ handle->info.underflow[HOOK_LOCAL_IN],
+ handle->info.underflow[HOOK_FORWARD],
+ handle->info.underflow[HOOK_LOCAL_OUT],
+ handle->info.underflow[HOOK_POST_ROUTING]);
+
+ ENTRY_ITERATE(handle->entries->entrytable, handle->entries->size,
+ dump_entry, handle);
+}
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+ iptc_fn = TC_IS_CHAIN;
+ return iptcc_find_label(chain, handle) != NULL;
+}
+
+static void iptcc_chain_iterator_advance(TC_HANDLE_T handle)
+{
+ struct chain_head *c = handle->chain_iterator_cur;
+
+ if (c->list.next == &handle->chains)
+ handle->chain_iterator_cur = NULL;
+ else
+ handle->chain_iterator_cur =
+ list_entry(c->list.next, struct chain_head, list);
+}
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+ struct chain_head *c = list_entry((*handle)->chains.next,
+ struct chain_head, list);
+
+ iptc_fn = TC_FIRST_CHAIN;
+
+
+ if (list_empty(&(*handle)->chains)) {
+ DEBUGP(": no chains\n");
+ return NULL;
+ }
+
+ (*handle)->chain_iterator_cur = c;
+ iptcc_chain_iterator_advance(*handle);
+
+ DEBUGP(": returning `%s'\n", c->name);
+ return c->name;
+}
+
+/* Iterator functions to run through the chains. Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+{
+ struct chain_head *c = (*handle)->chain_iterator_cur;
+
+ iptc_fn = TC_NEXT_CHAIN;
+
+ if (!c) {
+ DEBUGP(": no more chains\n");
+ return NULL;
+ }
+
+ iptcc_chain_iterator_advance(*handle);
+
+ DEBUGP(": returning `%s'\n", c->name);
+ return c->name;
+}
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const STRUCT_ENTRY *
+TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r;
+
+ iptc_fn = TC_FIRST_RULE;
+
+ DEBUGP("first rule(%s): ", chain);
+
+ c = iptcc_find_label(chain, *handle);
+ if (!c) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ /* Empty chain: single return/policy rule */
+ if (list_empty(&c->rules)) {
+ DEBUGP_C("no rules, returning NULL\n");
+ return NULL;
+ }
+
+ r = list_entry(c->rules.next, struct rule_head, list);
+ (*handle)->rule_iterator_cur = r;
+ DEBUGP_C("%p\n", r);
+
+ return r->entry;
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+{
+ struct rule_head *r;
+
+ iptc_fn = TC_NEXT_RULE;
+ DEBUGP("rule_iterator_cur=%p...", (*handle)->rule_iterator_cur);
+
+ if (!(*handle)->rule_iterator_cur) {
+ DEBUGP_C("returning NULL\n");
+ return NULL;
+ }
+
+ r = list_entry((*handle)->rule_iterator_cur->list.next,
+ struct rule_head, list);
+
+ iptc_fn = TC_NEXT_RULE;
+
+ DEBUGP_C("next=%p, head=%p...", &r->list,
+ &(*handle)->rule_iterator_cur->chain->rules);
+
+ if (&r->list == &(*handle)->rule_iterator_cur->chain->rules) {
+ (*handle)->rule_iterator_cur = NULL;
+ DEBUGP_C("finished, returning NULL\n");
+ return NULL;
+ }
+
+ (*handle)->rule_iterator_cur = r;
+
+ /* NOTE: prev is without any influence ! */
+ DEBUGP_C("returning rule %p\n", r);
+ return r->entry;
+}
+
+/* How many rules in this chain? */
+unsigned int
+TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ iptc_fn = TC_NUM_RULES;
+ CHECK(*handle);
+
+ c = iptcc_find_label(chain, *handle);
+ if (!c) {
+ errno = ENOENT;
+ return (unsigned int)-1;
+ }
+
+ return c->num_rules;
+}
+
+const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
+ unsigned int n,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r;
+
+ iptc_fn = TC_GET_RULE;
+
+ CHECK(*handle);
+
+ c = iptcc_find_label(chain, *handle);
+ if (!c) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ r = iptcc_get_rule_num(c, n);
+ if (!r)
+ return NULL;
+ return r->entry;
+}
+
+/* Returns a pointer to the target name of this position. */
+static const char *standard_target_map(int verdict)
+{
+ switch (verdict) {
+ case RETURN:
+ return LABEL_RETURN;
+ break;
+ case -NF_ACCEPT-1:
+ return LABEL_ACCEPT;
+ break;
+ case -NF_DROP-1:
+ return LABEL_DROP;
+ break;
+ case -NF_QUEUE-1:
+ return LABEL_QUEUE;
+ break;
+ default:
+ fprintf(stderr, "ERROR: %d not a valid target)\n",
+ verdict);
+ abort();
+ break;
+ }
+ /* not reached */
+ return NULL;
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
+ TC_HANDLE_T *handle)
+{
+ STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
+ struct rule_head *r = container_of(e, struct rule_head, entry[0]);
+
+ iptc_fn = TC_GET_TARGET;
+
+ switch(r->type) {
+ int spos;
+ case IPTCC_R_FALLTHROUGH:
+ return "";
+ break;
+ case IPTCC_R_JUMP:
+ DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name);
+ return r->jump->name;
+ break;
+ case IPTCC_R_STANDARD:
+ spos = *(int *)GET_TARGET(e)->data;
+ DEBUGP("r=%p, spos=%d'\n", r, spos);
+ return standard_target_map(spos);
+ break;
+ case IPTCC_R_MODULE:
+ return GET_TARGET(e)->u.user.name;
+ break;
+ }
+ return NULL;
+}
+/* Is this a built-in chain? Actually returns hook + 1. */
+int
+TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
+{
+ struct chain_head *c;
+
+ iptc_fn = TC_BUILTIN;
+
+ c = iptcc_find_label(chain, handle);
+ if (!c) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ return iptcc_is_builtin(c);
+}
+
+/* Get the policy of a given built-in chain */
+const char *
+TC_GET_POLICY(const char *chain,
+ STRUCT_COUNTERS *counters,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+
+ iptc_fn = TC_GET_POLICY;
+
+ DEBUGP("called for chain %s\n", chain);
+
+ c = iptcc_find_label(chain, *handle);
+ if (!c) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (!iptcc_is_builtin(c))
+ return NULL;
+
+ *counters = c->counters;
+
+ return standard_target_map(c->verdict);
+}
+
+static int
+iptcc_standard_map(struct rule_head *r, int verdict)
+{
+ STRUCT_ENTRY *e = r->entry;
+ STRUCT_STANDARD_TARGET *t;
+
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+ if (t->target.u.target_size
+ != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+ errno = EINVAL;
+ return 0;
+ }
+ /* memset for memcmp convenience on delete/replace */
+ memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+ strcpy(t->target.u.user.name, STANDARD_TARGET);
+ t->verdict = verdict;
+
+ r->type = IPTCC_R_STANDARD;
+
+ return 1;
+}
+
+static int
+iptcc_map_target(const TC_HANDLE_T handle,
+ struct rule_head *r)
+{
+ STRUCT_ENTRY *e = r->entry;
+ STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+ /* Maybe it's empty (=> fall through) */
+ if (strcmp(t->u.user.name, "") == 0) {
+ r->type = IPTCC_R_FALLTHROUGH;
+ return 1;
+ }
+ /* Maybe it's a standard target name... */
+ else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+ return iptcc_standard_map(r, -NF_ACCEPT - 1);
+ else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+ return iptcc_standard_map(r, -NF_DROP - 1);
+ else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+ return iptcc_standard_map(r, -NF_QUEUE - 1);
+ else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+ return iptcc_standard_map(r, RETURN);
+ else if (TC_BUILTIN(t->u.user.name, handle)) {
+ /* Can't jump to builtins. */
+ errno = EINVAL;
+ return 0;
+ } else {
+ /* Maybe it's an existing chain name. */
+ struct chain_head *c;
+ DEBUGP("trying to find chain `%s': ", t->u.user.name);
+
+ c = iptcc_find_label(t->u.user.name, handle);
+ if (c) {
+ DEBUGP_C("found!\n");
+ r->type = IPTCC_R_JUMP;
+ r->jump = c;
+ c->references++;
+ return 1;
+ }
+ DEBUGP_C("not found :(\n");
+ }
+
+ /* Must be a module? If not, kernel will reject... */
+ /* memset to all 0 for your memcmp convenience: don't clear version */
+ memset(t->u.user.name + strlen(t->u.user.name),
+ 0,
+ FUNCTION_MAXNAMELEN - 1 - strlen(t->u.user.name));
+ r->type = IPTCC_R_MODULE;
+ set_changed(handle);
+ return 1;
+}
+
+/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
+int
+TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
+ const STRUCT_ENTRY *e,
+ unsigned int rulenum,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r;
+ struct list_head *prev;
+
+ iptc_fn = TC_INSERT_ENTRY;
+
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ /* first rulenum index = 0
+ first c->num_rules index = 1 */
+ if (rulenum > c->num_rules) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ /* If we are inserting at the end just take advantage of the
+ double linked list, insert will happen before the entry
+ prev points to. */
+ if (rulenum == c->num_rules) {
+ prev = &c->rules;
+ } else if (rulenum + 1 <= c->num_rules/2) {
+ r = iptcc_get_rule_num(c, rulenum + 1);
+ prev = &r->list;
+ } else {
+ r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+ prev = &r->list;
+ }
+
+ if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ memcpy(r->entry, e, e->next_offset);
+ r->counter_map.maptype = COUNTER_MAP_SET;
+
+ if (!iptcc_map_target(*handle, r)) {
+ free(r);
+ return 0;
+ }
+
+ list_add_tail(&r->list, prev);
+ c->num_rules++;
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
+ const STRUCT_ENTRY *e,
+ unsigned int rulenum,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r, *old;
+
+ iptc_fn = TC_REPLACE_ENTRY;
+
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (rulenum >= c->num_rules) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ /* Take advantage of the double linked list if possible. */
+ if (rulenum + 1 <= c->num_rules/2) {
+ old = iptcc_get_rule_num(c, rulenum + 1);
+ } else {
+ old = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+ }
+
+ if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ memcpy(r->entry, e, e->next_offset);
+ r->counter_map.maptype = COUNTER_MAP_SET;
+
+ if (!iptcc_map_target(*handle, r)) {
+ free(r);
+ return 0;
+ }
+
+ list_add(&r->list, &old->list);
+ iptcc_delete_rule(old);
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+/* Append entry `fw' to chain `chain'. Equivalent to insert with
+ rulenum = length of chain. */
+int
+TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
+ const STRUCT_ENTRY *e,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r;
+
+ iptc_fn = TC_APPEND_ENTRY;
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ DEBUGP("unable to find chain `%s'\n", chain);
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+ DEBUGP("unable to allocate rule for chain `%s'\n", chain);
+ errno = ENOMEM;
+ return 0;
+ }
+
+ memcpy(r->entry, e, e->next_offset);
+ r->counter_map.maptype = COUNTER_MAP_SET;
+
+ if (!iptcc_map_target(*handle, r)) {
+ DEBUGP("unable to map target of rule for chain `%s'\n", chain);
+ free(r);
+ return 0;
+ }
+
+ list_add_tail(&r->list, &c->rules);
+ c->num_rules++;
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+ const unsigned char *a_elems,
+ const unsigned char *b_elems,
+ unsigned char **maskptr)
+{
+ const STRUCT_ENTRY_MATCH *b;
+ unsigned int i;
+
+ /* Offset of b is the same as a. */
+ b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+ if (a->u.match_size != b->u.match_size)
+ return 1;
+
+ if (strcmp(a->u.user.name, b->u.user.name) != 0)
+ return 1;
+
+ *maskptr += ALIGN(sizeof(*a));
+
+ for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+ if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+ return 1;
+ *maskptr += i;
+ return 0;
+}
+
+static inline int
+target_same(struct rule_head *a, struct rule_head *b,const unsigned char *mask)
+{
+ unsigned int i;
+ STRUCT_ENTRY_TARGET *ta, *tb;
+
+ if (a->type != b->type)
+ return 0;
+
+ ta = GET_TARGET(a->entry);
+ tb = GET_TARGET(b->entry);
+
+ switch (a->type) {
+ case IPTCC_R_FALLTHROUGH:
+ return 1;
+ case IPTCC_R_JUMP:
+ return a->jump == b->jump;
+ case IPTCC_R_STANDARD:
+ return ((STRUCT_STANDARD_TARGET *)ta)->verdict
+ == ((STRUCT_STANDARD_TARGET *)tb)->verdict;
+ case IPTCC_R_MODULE:
+ if (ta->u.target_size != tb->u.target_size)
+ return 0;
+ if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+ return 0;
+
+ for (i = 0; i < ta->u.target_size - sizeof(*ta); i++)
+ if (((ta->data[i] ^ tb->data[i]) & mask[i]) != 0)
+ return 0;
+ return 1;
+ default:
+ fprintf(stderr, "ERROR: bad type %i\n", a->type);
+ abort();
+ }
+}
+
+static unsigned char *
+is_same(const STRUCT_ENTRY *a,
+ const STRUCT_ENTRY *b,
+ unsigned char *matchmask);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+ const STRUCT_ENTRY *origfw,
+ unsigned char *matchmask,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r, *i;
+
+ iptc_fn = TC_DELETE_ENTRY;
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ /* Create a rule_head from origfw. */
+ r = iptcc_alloc_rule(c, origfw->next_offset);
+ if (!r) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ memcpy(r->entry, origfw, origfw->next_offset);
+ r->counter_map.maptype = COUNTER_MAP_NOMAP;
+ if (!iptcc_map_target(*handle, r)) {
+ DEBUGP("unable to map target of rule for chain `%s'\n", chain);
+ free(r);
+ return 0;
+ } else {
+ /* iptcc_map_target increment target chain references
+ * since this is a fake rule only used for matching
+ * the chain references count is decremented again.
+ */
+ if (r->type == IPTCC_R_JUMP
+ && r->jump)
+ r->jump->references--;
+ }
+
+ list_for_each_entry(i, &c->rules, list) {
+ unsigned char *mask;
+
+ mask = is_same(r->entry, i->entry, matchmask);
+ if (!mask)
+ continue;
+
+ if (!target_same(r, i, mask))
+ continue;
+
+ /* If we are about to delete the rule that is the
+ * current iterator, move rule iterator back. next
+ * pointer will then point to real next node */
+ if (i == (*handle)->rule_iterator_cur) {
+ (*handle)->rule_iterator_cur =
+ list_entry((*handle)->rule_iterator_cur->list.prev,
+ struct rule_head, list);
+ }
+
+ c->num_rules--;
+ iptcc_delete_rule(i);
+
+ set_changed(*handle);
+ free(r);
+ return 1;
+ }
+
+ free(r);
+ errno = ENOENT;
+ return 0;
+}
+
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int
+TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
+ unsigned int rulenum,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r;
+
+ iptc_fn = TC_DELETE_NUM_ENTRY;
+
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (rulenum >= c->num_rules) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ /* Take advantage of the double linked list if possible. */
+ if (rulenum + 1 <= c->num_rules/2) {
+ r = iptcc_get_rule_num(c, rulenum + 1);
+ } else {
+ r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+ }
+
+ /* If we are about to delete the rule that is the current
+ * iterator, move rule iterator back. next pointer will then
+ * point to real next node */
+ if (r == (*handle)->rule_iterator_cur) {
+ (*handle)->rule_iterator_cur =
+ list_entry((*handle)->rule_iterator_cur->list.prev,
+ struct rule_head, list);
+ }
+
+ c->num_rules--;
+ iptcc_delete_rule(r);
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+/* Check the packet `fw' on chain `chain'. Returns the verdict, or
+ NULL and sets errno. */
+const char *
+TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
+ STRUCT_ENTRY *entry,
+ TC_HANDLE_T *handle)
+{
+ iptc_fn = TC_CHECK_PACKET;
+ errno = ENOSYS;
+ return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int
+TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r, *tmp;
+
+ iptc_fn = TC_FLUSH_ENTRIES;
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ list_for_each_entry_safe(r, tmp, &c->rules, list) {
+ iptcc_delete_rule(r);
+ }
+
+ c->num_rules = 0;
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+/* Zeroes the counters in a chain. */
+int
+TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r;
+
+ iptc_fn = TC_ZERO_ENTRIES;
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (c->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+ c->counter_map.maptype = COUNTER_MAP_ZEROED;
+
+ list_for_each_entry(r, &c->rules, list) {
+ if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+ r->counter_map.maptype = COUNTER_MAP_ZEROED;
+ }
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+STRUCT_COUNTERS *
+TC_READ_COUNTER(const IPT_CHAINLABEL chain,
+ unsigned int rulenum,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r;
+
+ iptc_fn = TC_READ_COUNTER;
+ CHECK(*handle);
+
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (!(r = iptcc_get_rule_num(c, rulenum))) {
+ errno = E2BIG;
+ return NULL;
+ }
+
+ return &r->entry[0].counters;
+}
+
+int
+TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
+ unsigned int rulenum,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r;
+
+ iptc_fn = TC_ZERO_COUNTER;
+ CHECK(*handle);
+
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (!(r = iptcc_get_rule_num(c, rulenum))) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+ r->counter_map.maptype = COUNTER_MAP_ZEROED;
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+int
+TC_SET_COUNTER(const IPT_CHAINLABEL chain,
+ unsigned int rulenum,
+ STRUCT_COUNTERS *counters,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *r;
+ STRUCT_ENTRY *e;
+
+ iptc_fn = TC_SET_COUNTER;
+ CHECK(*handle);
+
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (!(r = iptcc_get_rule_num(c, rulenum))) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ e = r->entry;
+ r->counter_map.maptype = COUNTER_MAP_SET;
+
+ memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+int
+TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+ static struct chain_head *c;
+
+ iptc_fn = TC_CREATE_CHAIN;
+
+ /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+ QUEUE, RETURN. */
+ if (iptcc_find_label(chain, *handle)
+ || strcmp(chain, LABEL_DROP) == 0
+ || strcmp(chain, LABEL_ACCEPT) == 0
+ || strcmp(chain, LABEL_QUEUE) == 0
+ || strcmp(chain, LABEL_RETURN) == 0) {
+ DEBUGP("Chain `%s' already exists\n", chain);
+ errno = EEXIST;
+ return 0;
+ }
+
+ if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+ DEBUGP("Chain name `%s' too long\n", chain);
+ errno = EINVAL;
+ return 0;
+ }
+
+ c = iptcc_alloc_chain_head(chain, 0);
+ if (!c) {
+ DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
+ errno = ENOMEM;
+ return 0;
+
+ }
+
+ DEBUGP("Creating chain `%s'\n", chain);
+ iptc_insert_chain(*handle, c); /* Insert sorted */
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+
+ iptc_fn = TC_GET_REFERENCES;
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ *ref = c->references;
+
+ return 1;
+}
+
+/* Deletes a chain. */
+int
+TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+ unsigned int references;
+ struct chain_head *c;
+
+ iptc_fn = TC_DELETE_CHAIN;
+
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ DEBUGP("cannot find chain `%s'\n", chain);
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (TC_BUILTIN(chain, *handle)) {
+ DEBUGP("cannot remove builtin chain `%s'\n", chain);
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (!TC_GET_REFERENCES(&references, chain, handle)) {
+ DEBUGP("cannot get references on chain `%s'\n", chain);
+ return 0;
+ }
+
+ if (references > 0) {
+ DEBUGP("chain `%s' still has references\n", chain);
+ errno = EMLINK;
+ return 0;
+ }
+
+ if (c->num_rules) {
+ DEBUGP("chain `%s' is not empty\n", chain);
+ errno = ENOTEMPTY;
+ return 0;
+ }
+
+ /* If we are about to delete the chain that is the current
+ * iterator, move chain iterator firward. */
+ if (c == (*handle)->chain_iterator_cur)
+ iptcc_chain_iterator_advance(*handle);
+
+ list_del(&c->list);
+ free(c);
+
+ DEBUGP("chain `%s' deleted\n", chain);
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+/* Renames a chain. */
+int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
+ const IPT_CHAINLABEL newname,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ iptc_fn = TC_RENAME_CHAIN;
+
+ /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+ QUEUE, RETURN. */
+ if (iptcc_find_label(newname, *handle)
+ || strcmp(newname, LABEL_DROP) == 0
+ || strcmp(newname, LABEL_ACCEPT) == 0
+ || strcmp(newname, LABEL_QUEUE) == 0
+ || strcmp(newname, LABEL_RETURN) == 0) {
+ errno = EEXIST;
+ return 0;
+ }
+
+ if (!(c = iptcc_find_label(oldname, *handle))
+ || TC_BUILTIN(oldname, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ strncpy(c->name, newname, sizeof(IPT_CHAINLABEL));
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const IPT_CHAINLABEL chain,
+ const IPT_CHAINLABEL policy,
+ STRUCT_COUNTERS *counters,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+
+ iptc_fn = TC_SET_POLICY;
+
+ if (!(c = iptcc_find_label(chain, *handle))) {
+ DEBUGP("cannot find chain `%s'\n", chain);
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (!iptcc_is_builtin(c)) {
+ DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (strcmp(policy, LABEL_ACCEPT) == 0)
+ c->verdict = -NF_ACCEPT - 1;
+ else if (strcmp(policy, LABEL_DROP) == 0)
+ c->verdict = -NF_DROP - 1;
+ else {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (counters) {
+ /* set byte and packet counters */
+ memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS));
+ c->counter_map.maptype = COUNTER_MAP_SET;
+ } else {
+ c->counter_map.maptype = COUNTER_MAP_NOMAP;
+ }
+
+ set_changed(*handle);
+
+ return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+ libiptc.c: In function `TC_COMMIT':
+ libiptc.c:833: fixed or forbidden register was spilled.
+ This may be due to a compiler bug or to impossible asm
+ statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+ const STRUCT_COUNTERS *a,
+ const STRUCT_COUNTERS *b)
+{
+ answer->pcnt = a->pcnt - b->pcnt;
+ answer->bcnt = a->bcnt - b->bcnt;
+}
+
+
+static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters,
+ unsigned int index)
+{
+ newcounters->counters[index] = ((STRUCT_COUNTERS) { 0, 0});
+ DEBUGP_C("NOMAP => zero\n");
+}
+
+static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
+ STRUCT_REPLACE *repl,
+ unsigned int index,
+ unsigned int mappos)
+{
+ /* Original read: X.
+ * Atomic read on replacement: X + Y.
+ * Currently in kernel: Z.
+ * Want in kernel: X + Y + Z.
+ * => Add in X + Y
+ * => Add in replacement read.
+ */
+ newcounters->counters[index] = repl->counters[mappos];
+ DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
+}
+
+static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
+ STRUCT_REPLACE *repl,
+ unsigned int index,
+ unsigned int mappos,
+ STRUCT_COUNTERS *counters)
+{
+ /* Original read: X.
+ * Atomic read on replacement: X + Y.
+ * Currently in kernel: Z.
+ * Want in kernel: Y + Z.
+ * => Add in Y.
+ * => Add in (replacement read - original read).
+ */
+ subtract_counters(&newcounters->counters[index],
+ &repl->counters[mappos],
+ counters);
+ DEBUGP_C("ZEROED => mappos %u\n", mappos);
+}
+
+static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
+ unsigned int index,
+ STRUCT_COUNTERS *counters)
+{
+ /* Want to set counter (iptables-restore) */
+
+ memcpy(&newcounters->counters[index], counters,
+ sizeof(STRUCT_COUNTERS));
+
+ DEBUGP_C("SET\n");
+}
+
+
+int
+TC_COMMIT(TC_HANDLE_T *handle)
+{
+ /* Replace, then map back the counters. */
+ STRUCT_REPLACE *repl;
+ STRUCT_COUNTERS_INFO *newcounters;
+ struct chain_head *c;
+ int ret;
+ size_t counterlen;
+ int new_number;
+ unsigned int new_size;
+
+ iptc_fn = TC_COMMIT;
+ CHECK(*handle);
+
+ /* Don't commit if nothing changed. */
+ if (!(*handle)->changed)
+ goto finished;
+
+ new_number = iptcc_compile_table_prep(*handle, &new_size);
+ if (new_number < 0) {
+ errno = ENOMEM;
+ goto out_zero;
+ }
+
+ repl = malloc(sizeof(*repl) + new_size);
+ if (!repl) {
+ errno = ENOMEM;
+ goto out_zero;
+ }
+ memset(repl, 0, sizeof(*repl) + new_size);
+
+#if 0
+ TC_DUMP_ENTRIES(*handle);
+#endif
+
+ counterlen = sizeof(STRUCT_COUNTERS_INFO)
+ + sizeof(STRUCT_COUNTERS) * new_number;
+
+ /* These are the old counters we will get from kernel */
+ repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+ * (*handle)->info.num_entries);
+ if (!repl->counters) {
+ errno = ENOMEM;
+ goto out_free_repl;
+ }
+ /* These are the counters we're going to put back, later. */
+ newcounters = malloc(counterlen);
+ if (!newcounters) {
+ errno = ENOMEM;
+ goto out_free_repl_counters;
+ }
+ memset(newcounters, 0, counterlen);
+
+ strcpy(repl->name, (*handle)->info.name);
+ repl->num_entries = new_number;
+ repl->size = new_size;
+
+ repl->num_counters = (*handle)->info.num_entries;
+ repl->valid_hooks = (*handle)->info.valid_hooks;
+
+ DEBUGP("num_entries=%u, size=%u, num_counters=%u\n",
+ repl->num_entries, repl->size, repl->num_counters);
+
+ ret = iptcc_compile_table(*handle, repl);
+ if (ret < 0) {
+ errno = ret;
+ goto out_free_newcounters;
+ }
+
+
+#ifdef IPTC_DEBUG2
+ {
+ int fd = open("/tmp/libiptc-so_set_replace.blob",
+ O_CREAT|O_WRONLY);
+ if (fd >= 0) {
+ write(fd, repl, sizeof(*repl) + repl->size);
+ close(fd);
+ }
+ }
+#endif
+
+ ret = setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+ sizeof(*repl) + repl->size);
+ if (ret < 0)
+ goto out_free_newcounters;
+
+ /* Put counters back. */
+ strcpy(newcounters->name, (*handle)->info.name);
+ newcounters->num_counters = new_number;
+
+ list_for_each_entry(c, &(*handle)->chains, list) {
+ struct rule_head *r;
+
+ /* Builtin chains have their own counters */
+ if (iptcc_is_builtin(c)) {
+ DEBUGP("counter for chain-index %u: ", c->foot_index);
+ switch(c->counter_map.maptype) {
+ case COUNTER_MAP_NOMAP:
+ counters_nomap(newcounters, c->foot_index);
+ break;
+ case COUNTER_MAP_NORMAL_MAP:
+ counters_normal_map(newcounters, repl,
+ c->foot_index,
+ c->counter_map.mappos);
+ break;
+ case COUNTER_MAP_ZEROED:
+ counters_map_zeroed(newcounters, repl,
+ c->foot_index,
+ c->counter_map.mappos,
+ &c->counters);
+ break;
+ case COUNTER_MAP_SET:
+ counters_map_set(newcounters, c->foot_index,
+ &c->counters);
+ break;
+ }
+ }
+
+ list_for_each_entry(r, &c->rules, list) {
+ DEBUGP("counter for index %u: ", r->index);
+ switch (r->counter_map.maptype) {
+ case COUNTER_MAP_NOMAP:
+ counters_nomap(newcounters, r->index);
+ break;
+
+ case COUNTER_MAP_NORMAL_MAP:
+ counters_normal_map(newcounters, repl,
+ r->index,
+ r->counter_map.mappos);
+ break;
+
+ case COUNTER_MAP_ZEROED:
+ counters_map_zeroed(newcounters, repl,
+ r->index,
+ r->counter_map.mappos,
+ &r->entry->counters);
+ break;
+
+ case COUNTER_MAP_SET:
+ counters_map_set(newcounters, r->index,
+ &r->entry->counters);
+ break;
+ }
+ }
+ }
+
+#ifdef IPTC_DEBUG2
+ {
+ int fd = open("/tmp/libiptc-so_set_add_counters.blob",
+ O_CREAT|O_WRONLY);
+ if (fd >= 0) {
+ write(fd, newcounters, counterlen);
+ close(fd);
+ }
+ }
+#endif
+
+ ret = setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+ newcounters, counterlen);
+ if (ret < 0)
+ goto out_free_newcounters;
+
+ free(repl->counters);
+ free(repl);
+ free(newcounters);
+
+finished:
+ TC_FREE(handle);
+ return 1;
+
+out_free_newcounters:
+ free(newcounters);
+out_free_repl_counters:
+ free(repl->counters);
+out_free_repl:
+ free(repl);
+out_zero:
+ return 0;
+}
+
+/* Get raw socket. */
+int
+TC_GET_RAW_SOCKET(void)
+{
+ return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+ unsigned int i;
+ struct table_struct {
+ void *fn;
+ int err;
+ const char *message;
+ } table [] =
+ { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+ { TC_INIT, EINVAL, "Module is wrong version" },
+ { TC_INIT, ENOENT,
+ "Table does not exist (do you need to insmod?)" },
+ { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+ { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+ { TC_DELETE_CHAIN, EMLINK,
+ "Can't delete chain with references left" },
+ { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+ { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+ { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+ { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+ { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+ { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+ { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+ { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+ /* EINVAL for CHECK probably means bad interface. */
+ { TC_CHECK_PACKET, EINVAL,
+ "Bad arguments (does that interface exist?)" },
+ { TC_CHECK_PACKET, ENOSYS,
+ "Checking will most likely never get implemented" },
+ /* ENOENT for DELETE probably means no matching rule */
+ { TC_DELETE_ENTRY, ENOENT,
+ "Bad rule (does a matching rule exist in that chain?)" },
+ { TC_SET_POLICY, ENOENT,
+ "Bad built-in chain name" },
+ { TC_SET_POLICY, EINVAL,
+ "Bad policy name" },
+
+ { NULL, 0, "Incompatible with this kernel" },
+ { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+ { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
+ { NULL, ENOMEM, "Memory allocation problem" },
+ { NULL, ENOENT, "No chain/target/match by that name" },
+ };
+
+ for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+ if ((!table[i].fn || table[i].fn == iptc_fn)
+ && table[i].err == err)
+ return table[i].message;
+ }
+
+ return strerror(err);
+}
--- /dev/null
+/**
+ * This file was imported from the iptables sources.
+ * Copyright (C) 1999-2008 Netfilter Core Team
+ *
+ * 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
+ */
+
+#ifndef _LIBIPTC_H
+#define _LIBIPTC_H
+/* Library which manipulates filtering rules. */
+
+#include <linux/types.h>
+#include "ipt_kernel_headers.h"
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef IPT_MIN_ALIGN
+/* ipt_entry has pointers and u_int64_t's in it, so if you align to
+ it, you'll also align to any crazy matches and targets someone
+ might write */
+#define IPT_MIN_ALIGN (__alignof__(struct ipt_entry))
+#endif
+
+#define IPT_ALIGN(s) (((s) + ((IPT_MIN_ALIGN)-1)) & ~((IPT_MIN_ALIGN)-1))
+
+typedef char ipt_chainlabel[32];
+
+#define IPTC_LABEL_ACCEPT "ACCEPT"
+#define IPTC_LABEL_DROP "DROP"
+#define IPTC_LABEL_QUEUE "QUEUE"
+#define IPTC_LABEL_RETURN "RETURN"
+
+/* Transparent handle type. */
+typedef struct iptc_handle *iptc_handle_t;
+
+/* Does this chain exist? */
+int iptc_is_chain(const char *chain, const iptc_handle_t handle);
+
+/* Take a snapshot of the rules. Returns NULL on error. */
+iptc_handle_t iptc_init(const char *tablename);
+
+/* Cleanup after iptc_init(). */
+void iptc_free(iptc_handle_t *h);
+
+/* Iterator functions to run through the chains. Returns NULL at end. */
+const char *iptc_first_chain(iptc_handle_t *handle);
+const char *iptc_next_chain(iptc_handle_t *handle);
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const struct ipt_entry *iptc_first_rule(const char *chain,
+ iptc_handle_t *handle);
+
+/* Returns NULL when rules run out. */
+const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev,
+ iptc_handle_t *handle);
+
+/* Returns a pointer to the target name of this entry. */
+const char *iptc_get_target(const struct ipt_entry *e,
+ iptc_handle_t *handle);
+
+/* Is this a built-in chain? */
+int iptc_builtin(const char *chain, const iptc_handle_t handle);
+
+/* Get the policy of a given built-in chain */
+const char *iptc_get_policy(const char *chain,
+ struct ipt_counters *counter,
+ iptc_handle_t *handle);
+
+/* These functions return TRUE for OK or 0 and set errno. If errno ==
+ 0, it means there was a version error (ie. upgrade libiptc). */
+/* Rule numbers start at 1 for the first rule. */
+
+/* Insert the entry `e' in chain `chain' into position `rulenum'. */
+int iptc_insert_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *e,
+ unsigned int rulenum,
+ iptc_handle_t *handle);
+
+/* Atomically replace rule `rulenum' in `chain' with `e'. */
+int iptc_replace_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *e,
+ unsigned int rulenum,
+ iptc_handle_t *handle);
+
+/* Append entry `e' to chain `chain'. Equivalent to insert with
+ rulenum = length of chain. */
+int iptc_append_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *e,
+ iptc_handle_t *handle);
+
+/* Delete the first rule in `chain' which matches `e', subject to
+ matchmask (array of length == origfw) */
+int iptc_delete_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *origfw,
+ unsigned char *matchmask,
+ iptc_handle_t *handle);
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int iptc_delete_num_entry(const ipt_chainlabel chain,
+ unsigned int rulenum,
+ iptc_handle_t *handle);
+
+/* Check the packet `e' on chain `chain'. Returns the verdict, or
+ NULL and sets errno. */
+const char *iptc_check_packet(const ipt_chainlabel chain,
+ struct ipt_entry *entry,
+ iptc_handle_t *handle);
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int iptc_flush_entries(const ipt_chainlabel chain,
+ iptc_handle_t *handle);
+
+/* Zeroes the counters in a chain. */
+int iptc_zero_entries(const ipt_chainlabel chain,
+ iptc_handle_t *handle);
+
+/* Creates a new chain. */
+int iptc_create_chain(const ipt_chainlabel chain,
+ iptc_handle_t *handle);
+
+/* Deletes a chain. */
+int iptc_delete_chain(const ipt_chainlabel chain,
+ iptc_handle_t *handle);
+
+/* Renames a chain. */
+int iptc_rename_chain(const ipt_chainlabel oldname,
+ const ipt_chainlabel newname,
+ iptc_handle_t *handle);
+
+/* Sets the policy on a built-in chain. */
+int iptc_set_policy(const ipt_chainlabel chain,
+ const ipt_chainlabel policy,
+ struct ipt_counters *counters,
+ iptc_handle_t *handle);
+
+/* Get the number of references to this chain */
+int iptc_get_references(unsigned int *ref,
+ const ipt_chainlabel chain,
+ iptc_handle_t *handle);
+
+/* read packet and byte counters for a specific rule */
+struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain,
+ unsigned int rulenum,
+ iptc_handle_t *handle);
+
+/* zero packet and byte counters for a specific rule */
+int iptc_zero_counter(const ipt_chainlabel chain,
+ unsigned int rulenum,
+ iptc_handle_t *handle);
+
+/* set packet and byte counters for a specific rule */
+int iptc_set_counter(const ipt_chainlabel chain,
+ unsigned int rulenum,
+ struct ipt_counters *counters,
+ iptc_handle_t *handle);
+
+/* Makes the actual changes. */
+int iptc_commit(iptc_handle_t *handle);
+
+/* Get raw socket. */
+int iptc_get_raw_socket();
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *iptc_strerror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _LIBIPTC_H */
--- /dev/null
+/**
+ * This file was imported from the iptables sources.
+ * Copyright (C) 1999-2008 Netfilter Core Team
+ *
+ * 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
+ */
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({ type __dummy; \
+ typeof(x) __dummy2; \
+ (void)(&__dummy == &__dummy2); \
+ 1; \
+})
+
+#define prefetch(x) 1
+
+/* empty define to make this work in userspace -HW */
+#define smp_wmb()
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add_rcu(struct list_head * new,
+ struct list_head * prev, struct list_head * next)
+{
+ new->next = next;
+ new->prev = prev;
+ smp_wmb();
+ next->prev = new;
+ prev->next = new;
+}
+
+/**
+ * list_add_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_rcu(struct list_head *new, struct list_head *head)
+{
+ __list_add_rcu(new, head, head->next);
+}
+
+/**
+ * list_add_tail_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_tail_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_tail_rcu(struct list_head *new,
+ struct list_head *head)
+{
+ __list_add_rcu(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_rcu - deletes entry from list without re-initialization
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_del_rcu()
+ * or list_add_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ *
+ * Note that the caller is not permitted to immediately free
+ * the newly deleted entry. Instead, either synchronize_kernel()
+ * or call_rcu() must be used to defer freeing until an RCU
+ * grace period has elapsed.
+ */
+static inline void list_del_rcu(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is
+ * empty _and_ checks that no other CPU might be
+ * in the process of still modifying either member
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ *
+ * @head: the list to test.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+ struct list_head *next = head->next;
+ return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+ pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ prefetch(pos->member.prev); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member), \
+ prefetch(pos->member.prev))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use as a start point in
+ * list_for_each_entry_continue
+ * @pos: the type * to use as a start point
+ * @head: the head of the list
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_prepare_entry(pos, head, member) \
+ ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - iterate over list of given type
+ * continuing after existing point
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_rcu - iterate over an rcu-protected list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_rcu(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __list_for_each_rcu(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * list_for_each_safe_rcu - iterate over an rcu-protected list safe
+ * against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_safe_rcu(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * list_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_entry_rcu(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member), \
+ ({ smp_read_barrier_depends(); 0;}), \
+ prefetch(pos->member.next))
+
+
+/**
+ * list_for_each_continue_rcu - iterate over an rcu-protected list
+ * continuing after existing point.
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_continue_rcu(pos, head) \
+ for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+ (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+ return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = LIST_POISON1;
+ n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: list_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry().
+ */
+static inline void hlist_del_rcu(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+ if (n->pprev) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
+
+#define hlist_del_rcu_init hlist_del_init
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+
+/**
+ * hlist_add_head_rcu - adds the specified element to the specified hlist,
+ * while permitting racing traversals.
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry(), but only if smp_read_barrier_depends()
+ * is used to prevent memory-consistency problems on Alpha CPUs.
+ * Regardless of the type of CPU, the list-traversal primitive
+ * must be guarded by rcu_read_lock().
+ *
+ * OK, so why don't we have an hlist_for_each_entry_rcu()???
+ */
+static inline void hlist_add_head_rcu(struct hlist_node *n,
+ struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ n->pprev = &h->first;
+ smp_wmb();
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+ next->pprev = &n->next;
+ *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ next->next = n->next;
+ n->next = next;
+ next->pprev = &n->next;
+
+ if(next->next)
+ next->next->pprev = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+ pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+/**
+ * hlist_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member) \
+ for (pos = (pos)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member) \
+ for (; pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @n: another &struct hlist_node to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0; }) )
+
+#endif
return (0);
} /* int network_init */
+static int network_flush (int timeout)
+{
+ pthread_mutex_lock (&send_buffer_lock);
+
+ if (((time (NULL) - cache_flush_last) >= timeout)
+ && (send_buffer_fill > 0))
+ {
+ flush_buffer ();
+ }
+
+ pthread_mutex_unlock (&send_buffer_lock);
+
+ return (0);
+} /* int network_flush */
+
void module_register (void)
{
plugin_register_config ("network", network_config,
config_keys, config_keys_num);
plugin_register_init ("network", network_init);
+ plugin_register_flush ("network", network_flush);
} /* void module_register */
#include <curl/curl.h>
-static char *url = NULL;
-static char *user = NULL;
-static char *pass = NULL;
-static char *cacert = NULL;
+static char *url = NULL;
+static char *user = NULL;
+static char *pass = NULL;
+static char *verify_peer = NULL;
+static char *verify_host = NULL;
+static char *cacert = NULL;
static CURL *curl = NULL;
"URL",
"User",
"Password",
+ "VerifyPeer",
+ "VerifyHost",
"CACert"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
return (config_set (&user, value));
else if (strcasecmp (key, "password") == 0)
return (config_set (&pass, value));
+ else if (strcasecmp (key, "verifypeer") == 0)
+ return (config_set (&verify_peer, value));
+ else if (strcasecmp (key, "verifyhost") == 0)
+ return (config_set (&verify_host, value));
else if (strcasecmp (key, "cacert") == 0)
return (config_set (&cacert, value));
else
curl_easy_setopt (curl, CURLOPT_URL, url);
}
+ if ((verify_peer == NULL) || (strcmp (verify_peer, "true") == 0))
+ {
+ curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1);
+ }
+ else
+ {
+ curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0);
+ }
+
+ if ((verify_host == NULL) || (strcmp (verify_host, "true") == 0))
+ {
+ curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2);
+ }
+ else
+ {
+ curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0);
+ }
+
if (cacert != NULL)
{
curl_easy_setopt (curl, CURLOPT_CAINFO, cacert);
if (pkt_item_len < res_item_size)
pkt_padding = res_item_size - pkt_item_len;
DEBUG ("res_item_size = %i; pkt_padding = %i;",
- res_item_size, pkt_padding);
+ res_item_size, (int) pkt_padding);
/* Extract the sequence number */
pkt_sequence = INFO_SEQ (res.auth_seq);
* We start by allocating some more memory.
*/
DEBUG ("realloc (%p, %i)", (void *) *res_data,
- (items_num + pkt_item_num) * res_item_size);
+ (int) (items_num + pkt_item_num) * res_item_size);
items = realloc ((void *) *res_data,
(items_num + pkt_item_num) * res_item_size);
if (items == NULL)
#define PLUGIN_SHUTDOWN 3
#define PLUGIN_LOG 4
#define PLUGIN_NOTIF 5
+#define PLUGIN_FLUSH 6
-#define PLUGIN_TYPES 6
+#define PLUGIN_TYPES 7
#define PLUGIN_DATASET 255
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_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_dispatch_notification",
Collectd_plugin_dispatch_notification },
{ "Collectd::plugin_log", Collectd_plugin_log },
{ "Collectd::TYPE_SHUTDOWN", PLUGIN_SHUTDOWN },
{ "Collectd::TYPE_LOG", PLUGIN_LOG },
{ "Collectd::TYPE_NOTIF", PLUGIN_NOTIF },
+ { "Collectd::TYPE_FLUSH", PLUGIN_FLUSH },
{ "Collectd::TYPE_DATASET", PLUGIN_DATASET },
{ "Collectd::DS_TYPE_COUNTER", DS_TYPE_COUNTER },
{ "Collectd::DS_TYPE_GAUGE", DS_TYPE_GAUGE },
XPUSHs (sv_2mortal (newRV_noinc ((SV *)notif)));
}
+ else if (PLUGIN_FLUSH == type) {
+ /*
+ * $_[0] = $timeout;
+ */
+ XPUSHs (sv_2mortal (newSViv (va_arg (ap, int))));
+ }
PUTBACK;
} /* static XS (Collectd_plugin_dispatch_values) */
/*
+ * Collectd::plugin_flush_one (timeout, name).
+ *
+ * timeout:
+ * timeout to use when flushing the data
+ *
+ * name:
+ * name of the plugin to flush
+ */
+static XS (Collectd_plugin_flush_one)
+{
+ dXSARGS;
+
+ if (2 != items) {
+ log_err ("Usage: Collectd::plugin_flush_one(timeout, name)");
+ XSRETURN_EMPTY;
+ }
+
+ log_debug ("Collectd::plugin_flush_one: timeout = %i, name = \"%s\"",
+ (int)SvIV (ST (0)), SvPV_nolen (ST (1)));
+
+ if (0 == plugin_flush_one ((int)SvIV (ST (0)), SvPV_nolen (ST (1))))
+ XSRETURN_YES;
+ else
+ XSRETURN_EMPTY;
+} /* static XS (Collectd_plugin_flush_one) */
+
+/*
+ * Collectd::plugin_flush_all (timeout).
+ *
+ * timeout:
+ * timeout to use when flushing the data
+ */
+static XS (Collectd_plugin_flush_all)
+{
+ dXSARGS;
+
+ 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)));
+
+ plugin_flush_all ((int)SvIV (ST (0)));
+ XSRETURN_YES;
+} /* static XS (Collectd_plugin_flush_all) */
+
+/*
* Collectd::plugin_dispatch_notification (notif).
*
* notif:
return pplugin_call_all (aTHX_ PLUGIN_NOTIF, notif);
} /* static int perl_notify (const notification_t *) */
+static int perl_flush (const int timeout)
+{
+ dTHX;
+
+ if (NULL == perl_threads)
+ return 0;
+
+ if (NULL == aTHX) {
+ c_ithread_t *t = NULL;
+
+ pthread_mutex_lock (&perl_threads->mutex);
+ t = c_ithread_create (perl_threads->head->interp);
+ pthread_mutex_unlock (&perl_threads->mutex);
+
+ aTHX = t->interp;
+ }
+ return pplugin_call_all (aTHX_ PLUGIN_FLUSH, timeout);
+} /* static int perl_flush (const int) */
+
static int perl_shutdown (void)
{
c_ithread_t *t = NULL;
plugin_unregister_init ("perl");
plugin_unregister_read ("perl");
plugin_unregister_write ("perl");
+ plugin_unregister_flush ("perl");
ret = pplugin_call_all (aTHX_ PLUGIN_SHUTDOWN);
plugin_register_read ("perl", perl_read);
plugin_register_write ("perl", perl_write);
+ plugin_register_flush ("perl", perl_flush);
plugin_register_shutdown ("perl", perl_shutdown);
return 0;
} /* static int init_pi (const char **, const int) */
/**
* collectd - src/plugin.c
- * Copyright (C) 2005,2006 Florian octo Forster
+ * Copyright (C) 2005-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
*
* Authors:
* Florian octo Forster <octo at verplant.org>
+ * Sebastian Harl <sh at tokkee.org>
**/
#include "collectd.h"
static llist_t *list_init;
static llist_t *list_read;
static llist_t *list_write;
+static llist_t *list_flush;
static llist_t *list_shutdown;
static llist_t *list_log;
static llist_t *list_notification;
return (register_callback (&list_write, name, (void *) callback));
} /* int plugin_register_write */
+int plugin_register_flush (const char *name, int (*callback) (const int))
+{
+ return (register_callback (&list_flush, name, (void *) callback));
+} /* int plugin_register_flush */
+
int plugin_register_shutdown (char *name,
int (*callback) (void))
{
return (plugin_unregister (list_write, name));
}
+int plugin_unregister_flush (const char *name)
+{
+ return (plugin_unregister (list_flush, name));
+}
+
int plugin_unregister_shutdown (const char *name)
{
return (plugin_unregister (list_shutdown, name));
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 */
+
void plugin_shutdown_all (void)
{
int (*callback) (void);
#define PLUGIN_H
/**
* collectd - src/plugin.h
- * Copyright (C) 2005-2007 Florian octo Forster
+ * Copyright (C) 2005-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
*
* Authors:
* Florian octo Forster <octo at verplant.org>
+ * Sebastian Harl <sh at tokkee.org>
**/
#include "collectd.h"
void plugin_init_all (void);
void plugin_read_all (void);
+void plugin_flush_all (int timeout);
void plugin_shutdown_all (void);
+int plugin_flush_one (int timeout, const char *name);
+
/*
* The `plugin_register_*' functions are used to make `config', `init',
* `read', `write' and `shutdown' functions known to the plugin
int (*callback) (void));
int plugin_register_write (const char *name,
int (*callback) (const data_set_t *ds, const value_list_t *vl));
+int plugin_register_flush (const char *name,
+ int (*callback) (const int));
int plugin_register_shutdown (char *name,
int (*callback) (void));
int plugin_register_data_set (const data_set_t *ds);
int plugin_unregister_init (const char *name);
int plugin_unregister_read (const char *name);
int plugin_unregister_write (const char *name);
+int plugin_unregister_flush (const char *name);
int plugin_unregister_shutdown (const char *name);
int plugin_unregister_data_set (const char *name);
int plugin_unregister_log (const char *name);
--- /dev/null
+/**
+ * collectd - src/powerdns.c
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ * 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:
+ * Luke Heberling <lukeh at c-ware.com>
+ * Florian Forster <octo at verplant.org>
+ *
+ * DESCRIPTION
+ * Queries a PowerDNS control socket for statistics
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_llist.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
+#endif
+#define FUNC_ERROR(func) do { char errbuf[1024]; ERROR ("powerdns plugin: %s failed: %s", func, sstrerror (errno, errbuf, sizeof (errbuf))); } while (0)
+
+#define SERVER_SOCKET "/var/run/pdns.controlsocket"
+#define SERVER_COMMAND "SHOW *"
+
+#define RECURSOR_SOCKET "/var/run/pdns_recursor.controlsocket"
+#define RECURSOR_COMMAND "get noerror-answers nxdomain-answers " \
+ "servfail-answers sys-msec user-msec qa-latency cache-entries cache-hits " \
+ "cache-misses questions"
+
+struct list_item_s;
+typedef struct list_item_s list_item_t;
+
+struct list_item_s
+{
+ enum
+ {
+ SRV_AUTHORITATIVE,
+ SRV_RECURSOR
+ } server_type;
+ int (*func) (list_item_t *item);
+ char *instance;
+
+ char **fields;
+ int fields_num;
+ char *command;
+
+ struct sockaddr_un sockaddr;
+ int socktype;
+};
+
+struct statname_lookup_s
+{
+ char *name;
+ char *type;
+ char *type_instance;
+};
+typedef struct statname_lookup_s statname_lookup_t;
+
+/* Description of statistics returned by the recursor: {{{
+all-outqueries counts the number of outgoing UDP queries since starting
+answers0-1 counts the number of queries answered within 1 milisecond
+answers100-1000 counts the number of queries answered within 1 second
+answers10-100 counts the number of queries answered within 100 miliseconds
+answers1-10 counts the number of queries answered within 10 miliseconds
+answers-slow counts the number of queries answered after 1 second
+cache-entries shows the number of entries in the cache
+cache-hits counts the number of cache hits since starting
+cache-misses counts the number of cache misses since starting
+chain-resends number of queries chained to existing outstanding query
+client-parse-errors counts number of client packets that could not be parsed
+concurrent-queries shows the number of MThreads currently running
+dlg-only-drops number of records dropped because of delegation only setting
+negcache-entries shows the number of entries in the Negative answer cache
+noerror-answers counts the number of times it answered NOERROR since starting
+nsspeeds-entries shows the number of entries in the NS speeds map
+nsset-invalidations number of times an nsset was dropped because it no longer worked
+nxdomain-answers counts the number of times it answered NXDOMAIN since starting
+outgoing-timeouts counts the number of timeouts on outgoing UDP queries since starting
+qa-latency shows the current latency average
+questions counts all End-user initiated queries with the RD bit set
+resource-limits counts number of queries that could not be performed because of resource limits
+server-parse-errors counts number of server replied packets that could not be parsed
+servfail-answers counts the number of times it answered SERVFAIL since starting
+spoof-prevents number of times PowerDNS considered itself spoofed, and dropped the data
+sys-msec number of CPU milliseconds spent in 'system' mode
+tcp-client-overflow number of times an IP address was denied TCP access because it already had too many connections
+tcp-outqueries counts the number of outgoing TCP queries since starting
+tcp-questions counts all incoming TCP queries (since starting)
+throttled-out counts the number of throttled outgoing UDP queries since starting
+throttle-entries shows the number of entries in the throttle map
+unauthorized-tcp number of TCP questions denied because of allow-from restrictions
+unauthorized-udp number of UDP questions denied because of allow-from restrictions
+unexpected-packets number of answers from remote servers that were unexpected (might point to spoofing)
+uptime number of seconds process has been running (since 3.1.5)
+user-msec number of CPU milliseconds spent in 'user' mode
+}}} */
+
+const char* const default_server_fields[] = /* {{{ */
+{
+ "latency"
+ "packetcache-hit",
+ "packetcache-miss",
+ "packetcache-size",
+ "query-cache-hit",
+ "query-cache-miss",
+ "recursing-answers",
+ "recursing-questions",
+ "tcp-answers",
+ "tcp-queries",
+ "udp-answers",
+ "udp-queries",
+}; /* }}} */
+int default_server_fields_num = STATIC_ARRAY_SIZE (default_server_fields);
+
+statname_lookup_t lookup_table[] = /* {{{ */
+{
+ /*********************
+ * Server statistics *
+ *********************/
+ /* Questions */
+ {"recursing-questions", "dns_question", "recurse"},
+ {"tcp-queries", "dns_question", "tcp"},
+ {"udp-queries", "dns_question", "udp"},
+
+ /* Answers */
+ {"recursing-answers", "dns_answer", "recurse"},
+ {"tcp-answers", "dns_answer", "tcp"},
+ {"udp-answers", "dns_answer", "udp"},
+
+ /* Cache stuff */
+ {"packetcache-hit", "cache_result", "packet-hit"},
+ {"packetcache-miss", "cache_result", "packet-miss"},
+ {"packetcache-size", "cache_size", "packet"},
+ {"query-cache-hit", "cache_result", "query-hit"},
+ {"query-cache-miss", "cache_result", "query-miss"},
+
+ /* Latency */
+ {"latency", "latency", NULL},
+
+ /* Other stuff.. */
+ {"corrupt-packets", "io_packets", "corrupt"},
+ {"deferred-cache-inserts", "counter", "cache-deferred_insert"},
+ {"deferred-cache-lookup", "counter", "cache-deferred_lookup"},
+ {"qsize-a", "cache_size", "answers"},
+ {"qsize-q", "cache_size", "questions"},
+ {"servfail-packets", "io_packets", "servfail"},
+ {"timedout-packets", "io_packets", "timeout"},
+ {"udp4-answers", "dns_answer", "udp4"},
+ {"udp4-queries", "dns_question", "queries-udp4"},
+ {"udp6-answers", "dns_answer", "udp6"},
+ {"udp6-queries", "dns_question", "queries-udp6"},
+
+ /***********************
+ * Recursor statistics *
+ ***********************/
+ /* Answers by return code */
+ {"noerror-answers", "dns_rcode", "NOERROR"},
+ {"nxdomain-answers", "dns_rcode", "NXDOMAIN"},
+ {"servfail-answers", "dns_rcode", "SERVFAIL"},
+
+ /* CPU utilization */
+ {"sys-msec", "cpu", "system"},
+ {"user-msec", "cpu", "user"},
+
+ /* Question-to-answer latency */
+ {"qa-latency", "latency", NULL},
+
+ /* Cache */
+ {"cache-entries", "cache_size", NULL},
+ {"cache-hits", "cache_result", "hit"},
+ {"cache-misses", "cache_result", "miss"},
+
+ /* Total number of questions.. */
+ {"questions", "dns_qtype", "total"},
+
+ /* All the other stuff.. */
+ {"all-outqueries", "dns_question", "outgoing"},
+ {"answers0-1", "dns_answer", "0_1"},
+ {"answers1-10", "dns_answer", "1_10"},
+ {"answers10-100", "dns_answer", "10_100"},
+ {"answers100-1000", "dns_answer", "100_1000"},
+ {"answers-slow", "dns_answer", "slow"},
+ {"chain-resends", "dns_question", "chained"},
+ {"client-parse-errors", "counter", "drops-client_parse_error"},
+ {"concurrent-queries", "dns_question", "concurrent"},
+ {"dlg-only-drops", "counter", "drops-delegation_only"},
+ {"negcache-entries", "cache_size", "negative"},
+ {"nsspeeds-entries", "gauge", "entries-ns_speeds"},
+ {"nsset-invalidations", "counter", "ns_set_invalidation"},
+ {"outgoing-timeouts", "counter", "drops-timeout_outgoing"},
+ {"resource-limits", "counter", "drops-resource_limit"},
+ {"server-parse-errors", "counter", "drops-server_parse_error"},
+ {"spoof-prevents", "counter", "drops-spoofed"},
+ {"tcp-client-overflow", "counter", "denied-client_overflow_tcp"},
+ {"tcp-outqueries", "dns_question", "outgoing-tcp"},
+ {"tcp-questions", "dns_question", "incoming-tcp"},
+ {"throttled-out", "dns_question", "outgoing-throttled"},
+ {"throttle-entries", "gauge", "entries-throttle"},
+ {"unauthorized-tcp", "counter", "denied-unauthorized_tcp"},
+ {"unauthorized-udp", "counter", "denied-unauthorized_udp"},
+ {"unexpected-packets", "dns_answer", "unexpected"}
+ /* {"uptime", "", ""} */
+}; /* }}} */
+int lookup_table_length = STATIC_ARRAY_SIZE (lookup_table);
+
+static llist_t *list = NULL;
+
+#define PDNS_LOCAL_SOCKPATH LOCALSTATEDIR"/run/"PACKAGE_NAME"-powerdns"
+static char *local_sockpath = NULL;
+
+/* TODO: Do this before 4.4:
+ * - Recursor:
+ * - Complete list of known pdns -> collectd mappings.
+ * - Update the collectd.conf(5) manpage.
+ *
+ * -octo
+ */
+
+/* <http://doc.powerdns.com/recursor-stats.html> */
+static void submit (const char *plugin_instance, /* {{{ */
+ const char *pdns_type, const char *value)
+{
+ value_list_t vl = VALUE_LIST_INIT;
+ value_t values[1];
+
+ const char *type = NULL;
+ const char *type_instance = NULL;
+ const data_set_t *ds;
+
+ int i;
+
+ for (i = 0; i < lookup_table_length; i++)
+ if (strcmp (lookup_table[i].name, pdns_type) == 0)
+ break;
+
+ if (lookup_table[i].type == NULL)
+ return;
+
+ if (i >= lookup_table_length)
+ {
+ INFO ("powerdns plugin: submit: Not found in lookup table: %s = %s;",
+ pdns_type, value);
+ return;
+ }
+
+ type = lookup_table[i].type;
+ type_instance = lookup_table[i].type_instance;
+
+ ds = plugin_get_ds (type);
+ if (ds == NULL)
+ {
+ ERROR ("powerdns plugin: The lookup table returned type `%s', "
+ "but I cannot find it via `plugin_get_ds'.",
+ type);
+ return;
+ }
+
+ if (ds->ds_num != 1)
+ {
+ ERROR ("powerdns plugin: type `%s' has %i data sources, "
+ "but I can only handle one.",
+ type, ds->ds_num);
+ return;
+ }
+
+ if (ds->ds[0].type == DS_TYPE_GAUGE)
+ {
+ char *endptr = NULL;
+
+ values[0].gauge = strtod (value, &endptr);
+
+ if (endptr == value)
+ {
+ ERROR ("powerdns plugin: Cannot convert `%s' "
+ "to a floating point number.", value);
+ return;
+ }
+ }
+ else
+ {
+ char *endptr = NULL;
+
+ values[0].counter = strtoll (value, &endptr, 0);
+ if (endptr == value)
+ {
+ ERROR ("powerdns plugin: Cannot convert `%s' "
+ "to an integer number.", value);
+ return;
+ }
+ }
+
+ vl.values = values;
+ vl.values_len = 1;
+ vl.time = time (NULL);
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "powerdns", sizeof (vl.plugin));
+ if (type_instance != NULL)
+ sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+ sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+
+ plugin_dispatch_values (type, &vl);
+} /* }}} static void submit */
+
+static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */
+ char **ret_buffer,
+ size_t *ret_buffer_size)
+{
+ int sd;
+ int status;
+
+ char temp[4096];
+ char *buffer = NULL;
+ size_t buffer_size = 0;
+
+ struct sockaddr_un sa_unix;
+
+ sd = socket (PF_UNIX, item->socktype, 0);
+ if (sd < 0)
+ {
+ FUNC_ERROR ("socket");
+ return (-1);
+ }
+
+ memset (&sa_unix, 0, sizeof (sa_unix));
+ sa_unix.sun_family = AF_UNIX;
+ strncpy (sa_unix.sun_path,
+ (local_sockpath != NULL) ? local_sockpath : PDNS_LOCAL_SOCKPATH,
+ sizeof (sa_unix.sun_path));
+ sa_unix.sun_path[sizeof (sa_unix.sun_path) - 1] = 0;
+
+ status = unlink (sa_unix.sun_path);
+ if ((status != 0) && (errno != ENOENT))
+ {
+ FUNC_ERROR ("unlink");
+ close (sd);
+ return (-1);
+ }
+
+ do /* while (0) */
+ {
+ /* We need to bind to a specific path, because this is a datagram socket
+ * and otherwise the daemon cannot answer. */
+ status = bind (sd, (struct sockaddr *) &sa_unix, sizeof (sa_unix));
+ if (status != 0)
+ {
+ FUNC_ERROR ("bind");
+ break;
+ }
+
+ /* Make the socket writeable by the daemon.. */
+ status = chmod (sa_unix.sun_path, 0666);
+ if (status != 0)
+ {
+ FUNC_ERROR ("chmod");
+ break;
+ }
+
+ status = connect (sd, (struct sockaddr *) &item->sockaddr,
+ sizeof (item->sockaddr));
+ if (status != 0)
+ {
+ FUNC_ERROR ("connect");
+ break;
+ }
+
+ status = send (sd, item->command, strlen (item->command), 0);
+ if (status < 0)
+ {
+ FUNC_ERROR ("send");
+ break;
+ }
+
+ status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
+ if (status < 0)
+ {
+ FUNC_ERROR ("recv");
+ break;
+ }
+ buffer_size = status + 1;
+ status = 0;
+ } while (0);
+
+ close (sd);
+ unlink (sa_unix.sun_path);
+
+ if (status != 0)
+ return (-1);
+
+ assert (buffer_size > 0);
+ buffer = (char *) malloc (buffer_size);
+ if (buffer == NULL)
+ {
+ FUNC_ERROR ("malloc");
+ return (-1);
+ }
+
+ memcpy (buffer, temp, buffer_size - 1);
+ buffer[buffer_size - 1] = 0;
+
+ *ret_buffer = buffer;
+ *ret_buffer_size = buffer_size;
+
+ return (0);
+} /* }}} int powerdns_get_data_dgram */
+
+static int powerdns_get_data_stream (list_item_t *item, /* {{{ */
+ char **ret_buffer,
+ size_t *ret_buffer_size)
+{
+ int sd;
+ int status;
+
+ char temp[4096];
+ char *buffer = NULL;
+ size_t buffer_size = 0;
+
+ sd = socket (PF_UNIX, item->socktype, 0);
+ if (sd < 0)
+ {
+ FUNC_ERROR ("socket");
+ return (-1);
+ }
+
+ status = connect (sd, (struct sockaddr *) &item->sockaddr,
+ sizeof (item->sockaddr));
+ if (status != 0)
+ {
+ FUNC_ERROR ("connect");
+ close (sd);
+ return (-1);
+ }
+
+ /* strlen + 1, because we need to send the terminating NULL byte, too. */
+ status = send (sd, item->command, strlen (item->command) + 1,
+ /* flags = */ 0);
+ if (status < 0)
+ {
+ FUNC_ERROR ("send");
+ close (sd);
+ return (-1);
+ }
+
+ while (42)
+ {
+ char *buffer_new;
+
+ status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
+ if (status < 0)
+ {
+ FUNC_ERROR ("recv");
+ break;
+ }
+ else if (status == 0)
+ break;
+
+ buffer_new = (char *) realloc (buffer, buffer_size + status + 1);
+ if (buffer_new == NULL)
+ {
+ FUNC_ERROR ("realloc");
+ status = -1;
+ break;
+ }
+ buffer = buffer_new;
+
+ memcpy (buffer + buffer_size, temp, status);
+ buffer_size += status;
+ buffer[buffer_size] = 0;
+ } /* while (42) */
+ close (sd);
+ sd = -1;
+
+ if (status < 0)
+ {
+ sfree (buffer);
+ }
+ else
+ {
+ assert (status == 0);
+ *ret_buffer = buffer;
+ *ret_buffer_size = buffer_size;
+ }
+
+ return (status);
+} /* }}} int powerdns_get_data_stream */
+
+static int powerdns_get_data (list_item_t *item, char **ret_buffer,
+ size_t *ret_buffer_size)
+{
+ if (item->socktype == SOCK_DGRAM)
+ return (powerdns_get_data_dgram (item, ret_buffer, ret_buffer_size));
+ else if (item->socktype == SOCK_STREAM)
+ return (powerdns_get_data_stream (item, ret_buffer, ret_buffer_size));
+ else
+ {
+ ERROR ("powerdns plugin: Unknown socket type: %i", (int) item->socktype);
+ return (-1);
+ }
+} /* int powerdns_get_data */
+
+static int powerdns_read_server (list_item_t *item) /* {{{ */
+{
+ char *buffer = NULL;
+ size_t buffer_size = 0;
+ int status;
+
+ char *dummy;
+ char *saveptr;
+
+ char *key;
+ char *value;
+
+ const char* const *fields;
+ int fields_num;
+
+ if (item->command == NULL)
+ item->command = strdup ("SHOW *");
+ if (item->command == NULL)
+ {
+ ERROR ("powerdns plugin: strdup failed.");
+ return (-1);
+ }
+
+ status = powerdns_get_data (item, &buffer, &buffer_size);
+ if (status != 0)
+ return (-1);
+
+ if (item->fields_num != 0)
+ {
+ fields = (const char* const *) item->fields;
+ fields_num = item->fields_num;
+ }
+ else
+ {
+ fields = default_server_fields;
+ fields_num = default_server_fields_num;
+ }
+
+ assert (fields != NULL);
+ assert (fields_num > 0);
+
+ /* corrupt-packets=0,deferred-cache-inserts=0,deferred-cache-lookup=0,latency=0,packetcache-hit=0,packetcache-miss=0,packetcache-size=0,qsize-q=0,query-cache-hit=0,query-cache-miss=0,recursing-answers=0,recursing-questions=0,servfail-packets=0,tcp-answers=0,tcp-queries=0,timedout-packets=0,udp-answers=0,udp-queries=0,udp4-answers=0,udp4-queries=0,udp6-answers=0,udp6-queries=0, */
+ dummy = buffer;
+ saveptr = NULL;
+ while ((key = strtok_r (dummy, ",", &saveptr)) != NULL)
+ {
+ int i;
+
+ dummy = NULL;
+
+ value = strchr (key, '=');
+ if (value == NULL)
+ break;
+
+ *value = '\0';
+ value++;
+
+ if (value[0] == '\0')
+ continue;
+
+ /* Check if this item was requested. */
+ for (i = 0; i < fields_num; i++)
+ if (strcasecmp (key, fields[i]) == 0)
+ break;
+ if (i >= fields_num)
+ continue;
+
+ submit (item->instance, key, value);
+ } /* while (strtok_r) */
+
+ sfree (buffer);
+
+ return (0);
+} /* }}} int powerdns_read_server */
+
+/*
+ * powerdns_update_recursor_command
+ *
+ * Creates a string that holds the command to be sent to the recursor. This
+ * string is stores in the `command' member of the `list_item_t' passed to the
+ * function. This function is called by `powerdns_read_recursor'.
+ */
+static int powerdns_update_recursor_command (list_item_t *li) /* {{{ */
+{
+ char buffer[4096];
+ int status;
+
+ if (li == NULL)
+ return (0);
+
+ if (li->fields_num < 1)
+ {
+ sstrncpy (buffer, RECURSOR_COMMAND, sizeof (buffer));
+ }
+ else
+ {
+ strcpy (buffer, "get ");
+ status = strjoin (&buffer[4], sizeof (buffer) - strlen ("get "),
+ li->fields, li->fields_num,
+ /* seperator = */ " ");
+ if (status < 0)
+ {
+ ERROR ("powerdns plugin: strjoin failed.");
+ return (-1);
+ }
+ }
+
+ buffer[sizeof (buffer) - 1] = 0;
+ li->command = strdup (buffer);
+ if (li->command == NULL)
+ {
+ ERROR ("powerdns plugin: strdup failed.");
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int powerdns_update_recursor_command */
+
+static int powerdns_read_recursor (list_item_t *item) /* {{{ */
+{
+ char *buffer = NULL;
+ size_t buffer_size = 0;
+ int status;
+
+ char *dummy;
+
+ char *keys_list;
+ char *key;
+ char *key_saveptr;
+ char *value;
+ char *value_saveptr;
+
+ if (item->command == NULL)
+ {
+ status = powerdns_update_recursor_command (item);
+ if (status != 0)
+ {
+ ERROR ("powerdns plugin: powerdns_update_recursor_command failed.");
+ return (-1);
+ }
+
+ DEBUG ("powerdns plugin: powerdns_read_recursor: item->command = %s;",
+ item->command);
+ }
+ assert (item->command != NULL);
+
+ status = powerdns_get_data (item, &buffer, &buffer_size);
+ if (status != 0)
+ {
+ ERROR ("powerdns plugin: powerdns_get_data failed.");
+ return (-1);
+ }
+
+ keys_list = strdup (item->command);
+ if (keys_list == NULL)
+ {
+ FUNC_ERROR ("strdup");
+ sfree (buffer);
+ return (-1);
+ }
+
+ key_saveptr = NULL;
+ value_saveptr = NULL;
+
+ /* Skip the `get' at the beginning */
+ strtok_r (keys_list, " \t", &key_saveptr);
+
+ dummy = buffer;
+ while ((value = strtok_r (dummy, " \t\n\r", &value_saveptr)) != NULL)
+ {
+ dummy = NULL;
+
+ key = strtok_r (NULL, " \t", &key_saveptr);
+ if (key == NULL)
+ break;
+
+ submit (item->instance, key, value);
+ } /* while (strtok_r) */
+
+ sfree (buffer);
+ sfree (keys_list);
+
+ return (0);
+} /* }}} int powerdns_read_recursor */
+
+static int powerdns_config_add_string (const char *name, /* {{{ */
+ char **dest,
+ oconfig_item_t *ci)
+{
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
+ name);
+ return (-1);
+ }
+
+ sfree (*dest);
+ *dest = strdup (ci->values[0].value.string);
+ if (*dest == NULL)
+ return (-1);
+
+ return (0);
+} /* }}} int powerdns_config_add_string */
+
+static int powerdns_config_add_collect (list_item_t *li, /* {{{ */
+ oconfig_item_t *ci)
+{
+ int i;
+ char **temp;
+
+ if (ci->values_num < 1)
+ {
+ WARNING ("powerdns plugin: The `Collect' option needs "
+ "at least one argument.");
+ return (-1);
+ }
+
+ for (i = 0; i < ci->values_num; i++)
+ if (ci->values[i].type != OCONFIG_TYPE_STRING)
+ {
+ WARNING ("powerdns plugin: Only string arguments are allowed to "
+ "the `Collect' option.");
+ return (-1);
+ }
+
+ temp = (char **) realloc (li->fields,
+ sizeof (char *) * (li->fields_num + ci->values_num));
+ if (temp == NULL)
+ {
+ WARNING ("powerdns plugin: realloc failed.");
+ return (-1);
+ }
+ li->fields = temp;
+
+ for (i = 0; i < ci->values_num; i++)
+ {
+ li->fields[li->fields_num] = strdup (ci->values[i].value.string);
+ if (li->fields[li->fields_num] == NULL)
+ {
+ WARNING ("powerdns plugin: strdup failed.");
+ continue;
+ }
+ li->fields_num++;
+ }
+
+ /* Invalidate a previously computed command */
+ sfree (li->command);
+
+ return (0);
+} /* }}} int powerdns_config_add_collect */
+
+static int powerdns_config_add_server (oconfig_item_t *ci) /* {{{ */
+{
+ char *socket_temp;
+
+ list_item_t *item;
+ int status;
+ int i;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
+ ci->key);
+ return (-1);
+ }
+
+ item = (list_item_t *) malloc (sizeof (list_item_t));
+ if (item == NULL)
+ {
+ ERROR ("powerdns plugin: malloc failed.");
+ return (-1);
+ }
+ memset (item, '\0', sizeof (list_item_t));
+
+ item->instance = strdup (ci->values[0].value.string);
+ if (item->instance == NULL)
+ {
+ ERROR ("powerdns plugin: strdup failed.");
+ sfree (item);
+ return (-1);
+ }
+
+ /*
+ * Set default values for the members of list_item_t
+ */
+ if (strcasecmp ("Server", ci->key) == 0)
+ {
+ item->server_type = SRV_AUTHORITATIVE;
+ item->func = powerdns_read_server;
+ item->socktype = SOCK_STREAM;
+ socket_temp = strdup (SERVER_SOCKET);
+ }
+ else if (strcasecmp ("Recursor", ci->key) == 0)
+ {
+ item->server_type = SRV_RECURSOR;
+ item->func = powerdns_read_recursor;
+ item->socktype = SOCK_DGRAM;
+ socket_temp = strdup (RECURSOR_SOCKET);
+ }
+ else
+ {
+ /* We must never get here.. */
+ assert (0);
+ return (-1);
+ }
+
+ status = 0;
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp ("Collect", option->key) == 0)
+ status = powerdns_config_add_collect (item, option);
+ else if (strcasecmp ("Socket", option->key) == 0)
+ status = powerdns_config_add_string ("Socket", &socket_temp, option);
+ else
+ {
+ ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ while (status == 0)
+ {
+ llentry_t *e;
+
+ if (socket_temp == NULL)
+ {
+ ERROR ("powerdns plugin: socket_temp == NULL.");
+ status = -1;
+ break;
+ }
+
+ item->sockaddr.sun_family = AF_UNIX;
+ sstrncpy (item->sockaddr.sun_path, socket_temp, UNIX_PATH_MAX);
+
+ e = llentry_create (item->instance, item);
+ if (e == NULL)
+ {
+ ERROR ("powerdns plugin: llentry_create failed.");
+ status = -1;
+ break;
+ }
+ llist_append (list, e);
+
+ break;
+ }
+
+ if (status != 0)
+ {
+ sfree (item);
+ return (-1);
+ }
+
+ DEBUG ("powerdns plugin: Add server: instance = %s;", item->instance);
+
+ return (0);
+} /* }}} int powerdns_config_add_server */
+
+static int powerdns_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ DEBUG ("powerdns plugin: powerdns_config (ci = %p);", (void *) ci);
+
+ if (list == NULL)
+ {
+ list = llist_create ();
+
+ if (list == NULL)
+ {
+ ERROR ("powerdns plugin: `llist_create' failed.");
+ return (-1);
+ }
+ }
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if ((strcasecmp ("Server", option->key) == 0)
+ || (strcasecmp ("Recursor", option->key) == 0))
+ powerdns_config_add_server (option);
+ else if (strcasecmp ("LocalSocket", option->key) == 0)
+ {
+ char *temp = strdup (option->key);
+ if (temp == NULL)
+ return (1);
+ sfree (local_sockpath);
+ local_sockpath = temp;
+ }
+ else
+ {
+ ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
+ }
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ return (0);
+} /* }}} int powerdns_config */
+
+static int powerdns_read (void)
+{
+ llentry_t *e;
+
+ for (e = llist_head (list); e != NULL; e = e->next)
+ {
+ list_item_t *item = e->value;
+ item->func (item);
+ }
+
+ return (0);
+} /* static int powerdns_read */
+
+static int powerdns_shutdown (void)
+{
+ llentry_t *e;
+
+ if (list == NULL)
+ return (0);
+
+ for (e = llist_head (list); e != NULL; e = e->next)
+ {
+ list_item_t *item = (list_item_t *) e->value;
+ e->value = NULL;
+
+ sfree (item->instance);
+ sfree (item->command);
+ sfree (item);
+ }
+
+ llist_destroy (list);
+ list = NULL;
+
+ return (0);
+} /* static int powerdns_shutdown */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("powerdns", powerdns_config);
+ plugin_register_read ("powerdns", powerdns_read);
+ plugin_register_shutdown ("powerdns", powerdns_shutdown );
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 ts=8 fdm=marker : */
span = rts[i];
if ((span / ss) < rrarows)
- continue;
+ span = ss * rrarows;
if (cdp_len == 0)
cdp_len = 1;
return (status);
} /* int rrd_write */
+static int rrd_flush (const int timeout)
+{
+ pthread_mutex_lock (&cache_lock);
+
+ if (cache == NULL) {
+ pthread_mutex_unlock (&cache_lock);
+ return (0);
+ }
+
+ rrd_cache_flush (timeout);
+ pthread_mutex_unlock (&cache_lock);
+ return (0);
+} /* int rrd_flush */
+
static int rrd_config (const char *key, const char *value)
{
if (strcasecmp ("CacheTimeout", key) == 0)
config_keys, config_keys_num);
plugin_register_init ("rrdtool", rrd_init);
plugin_register_write ("rrdtool", rrd_write);
+ plugin_register_flush ("rrdtool", rrd_flush);
plugin_register_shutdown ("rrdtool", rrd_shutdown);
}
/**
* collectd - src/sensors.c
- * Copyright (C) 2005-2007 Florian octo Forster
+ * Copyright (C) 2005-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
--- /dev/null
+/**
+ * collectd - src/tail.c
+ * 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:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_tail_match.h"
+
+/*
+ * <Plugin tail>
+ * <File "/var/log/exim4/mainlog">
+ * Instance "exim"
+ * <Match>
+ * Regex "S=([1-9][0-9]*)"
+ * DSType "CounterAdd"
+ * Type "ipt_bytes"
+ * Instance "total"
+ * </Match>
+ * </File>
+ * </Plugin>
+ */
+
+struct ctail_config_match_s
+{
+ char *regex;
+ int flags;
+ char *type;
+ char *type_instance;
+};
+typedef struct ctail_config_match_s ctail_config_match_t;
+
+cu_tail_match_t **tail_match_list = NULL;
+size_t tail_match_list_num = 0;
+
+static int ctail_config_add_string (const char *name, char **dest, oconfig_item_t *ci)
+{
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("tail plugin: `%s' needs exactly one string argument.", name);
+ return (-1);
+ }
+
+ sfree (*dest);
+ *dest = strdup (ci->values[0].value.string);
+ if (*dest == NULL)
+ return (-1);
+
+ return (0);
+} /* int ctail_config_add_string */
+
+static int ctail_config_add_match_dstype (ctail_config_match_t *cm,
+ oconfig_item_t *ci)
+{
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("tail plugin: `DSType' needs exactly one string argument.");
+ return (-1);
+ }
+
+ if (strncasecmp ("Gauge", ci->values[0].value.string, strlen ("Gauge")) == 0)
+ {
+ cm->flags = UTILS_MATCH_DS_TYPE_GAUGE;
+ if (strcasecmp ("GaugeAverage", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_GAUGE_AVERAGE;
+ else if (strcasecmp ("GaugeMin", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_GAUGE_MIN;
+ else if (strcasecmp ("GaugeMax", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_GAUGE_MAX;
+ else if (strcasecmp ("GaugeLast", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_GAUGE_LAST;
+ else
+ cm->flags = 0;
+ }
+ else if (strncasecmp ("Counter", ci->values[0].value.string, strlen ("Counter")) == 0)
+ {
+ cm->flags = UTILS_MATCH_DS_TYPE_COUNTER;
+ if (strcasecmp ("CounterSet", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_COUNTER_SET;
+ else if (strcasecmp ("CounterAdd", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_COUNTER_ADD;
+ else if (strcasecmp ("CounterInc", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_COUNTER_INC;
+ else
+ cm->flags = 0;
+ }
+ else
+ {
+ cm->flags = 0;
+ }
+
+ if (cm->flags == 0)
+ {
+ WARNING ("tail plugin: `%s' is not a valid argument to `DSType'.",
+ ci->values[0].value.string);
+ return (-1);
+ }
+
+ return (0);
+} /* int ctail_config_add_match_dstype */
+
+static int ctail_config_add_match (cu_tail_match_t *tm,
+ const char *plugin_instance, oconfig_item_t *ci)
+{
+ ctail_config_match_t cm;
+ int status;
+ int i;
+
+ memset (&cm, '\0', sizeof (cm));
+
+ if (ci->values_num != 0)
+ {
+ WARNING ("tail plugin: Ignoring arguments for the `Match' block.");
+ }
+
+ status = 0;
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp ("Regex", option->key) == 0)
+ status = ctail_config_add_string ("Regex", &cm.regex, option);
+ else if (strcasecmp ("DSType", option->key) == 0)
+ status = ctail_config_add_match_dstype (&cm, option);
+ else if (strcasecmp ("Type", option->key) == 0)
+ status = ctail_config_add_string ("Type", &cm.type, option);
+ else if (strcasecmp ("Instance", option->key) == 0)
+ status = ctail_config_add_string ("Instance", &cm.type_instance, option);
+ else
+ {
+ WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ while (status == 0)
+ {
+ if (cm.regex == NULL)
+ {
+ WARNING ("tail plugin: `Regex' missing in `Match' block.");
+ status = -1;
+ break;
+ }
+
+ if (cm.type == NULL)
+ {
+ WARNING ("tail plugin: `Type' missing in `Match' block.");
+ status = -1;
+ break;
+ }
+
+ if (cm.flags == 0)
+ {
+ WARNING ("tail plugin: `DSType' missing in `Match' block.");
+ status = -1;
+ break;
+ }
+
+ break;
+ } /* while (status == 0) */
+
+ if (status == 0)
+ {
+ status = tail_match_add_match_simple (tm, cm.regex, cm.flags,
+ "tail", plugin_instance, cm.type, cm.type_instance);
+
+ if (status != 0)
+ {
+ ERROR ("tail plugin: tail_match_add_match_simple failed.");
+ }
+ }
+
+ sfree (cm.regex);
+ sfree (cm.type);
+ sfree (cm.type_instance);
+
+ return (status);
+} /* int ctail_config_add_match */
+
+static int ctail_config_add_file (oconfig_item_t *ci)
+{
+ cu_tail_match_t *tm;
+ char *plugin_instance = NULL;
+ int num_matches = 0;
+ int status;
+ int i;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("tail plugin: `File' needs exactly one string argument.");
+ return (-1);
+ }
+
+ tm = tail_match_create (ci->values[0].value.string);
+ if (tm == NULL)
+ {
+ ERROR ("tail plugin: tail_match_create (%s) failed.",
+ ci->values[0].value.string);
+ return (-1);
+ }
+
+ status = 0;
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp ("Match", option->key) == 0)
+ {
+ status = ctail_config_add_match (tm, plugin_instance, option);
+ if (status == 0)
+ num_matches++;
+ /* Be mild with failed matches.. */
+ status = 0;
+ }
+ else if (strcasecmp ("Instance", option->key) == 0)
+ status = ctail_config_add_string ("Instance", &plugin_instance, option);
+ else
+ {
+ WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ if (num_matches == 0)
+ {
+ ERROR ("tail plugin: No (valid) matches found for file `%s'.",
+ ci->values[0].value.string);
+ tail_match_destroy (tm);
+ return (-1);
+ }
+ else
+ {
+ cu_tail_match_t **temp;
+
+ temp = (cu_tail_match_t **) realloc (tail_match_list,
+ sizeof (cu_tail_match_t *) * (tail_match_list_num + 1));
+ if (temp == NULL)
+ {
+ ERROR ("tail plugin: realloc failed.");
+ tail_match_destroy (tm);
+ return (-1);
+ }
+
+ tail_match_list = temp;
+ tail_match_list[tail_match_list_num] = tm;
+ tail_match_list_num++;
+ }
+
+ return (0);
+} /* int ctail_config_add_file */
+
+static int ctail_config (oconfig_item_t *ci)
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp ("File", option->key) == 0)
+ ctail_config_add_file (option);
+ else
+ {
+ WARNING ("tail plugin: Option `%s' not allowed here.", option->key);
+ }
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ return (0);
+} /* int ctail_config */
+
+static int ctail_init (void)
+{
+ if (tail_match_list_num == 0)
+ {
+ WARNING ("tail plugin: File list is empty. Returning an error.");
+ return (-1);
+ }
+
+ return (0);
+} /* int ctail_init */
+
+static int ctail_read (void)
+{
+ int success = 0;
+ int i;
+
+ for (i = 0; i < tail_match_list_num; i++)
+ {
+ int status;
+
+ status = tail_match_read (tail_match_list[i]);
+ if (status != 0)
+ {
+ ERROR ("tail plugin: tail_match_read[%i] failed.", i);
+ }
+ else
+ {
+ success++;
+ }
+ }
+
+ if (success == 0)
+ return (-1);
+ return (0);
+} /* int ctail_read */
+
+static int ctail_shutdown (void)
+{
+ int i;
+
+ for (i = 0; i < tail_match_list_num; i++)
+ {
+ tail_match_destroy (tail_match_list[i]);
+ tail_match_list[i] = NULL;
+ }
+ sfree (tail_match_list);
+ tail_match_list_num = 0;
+
+ return (0);
+} /* int ctail_shutdown */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("tail", ctail_config);
+ plugin_register_init ("tail", ctail_init);
+ plugin_register_read ("tail", ctail_read);
+ plugin_register_shutdown ("tail", ctail_shutdown);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 ts=8 : */
--- /dev/null
+/**
+ * collectd - src/teamspeak2.c
+ * Copyright (C) 2008 Stefan Hacker
+ * 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
+ *
+ * Authors:
+ * Stefan Hacker <d0t at dbclan dot de>
+ * Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+/*
+ * Defines
+ */
+/* Default host and port */
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT "51234"
+
+/*
+ * Variables
+ */
+/* Server linked list structure */
+typedef struct vserver_list_s
+{
+ int port;
+ struct vserver_list_s *next;
+} vserver_list_t;
+static vserver_list_t *server_list = NULL;
+
+/* Host data */
+static char *config_host = NULL;
+static char *config_port = NULL;
+
+static FILE *global_read_fh = NULL;
+static FILE *global_write_fh = NULL;
+
+/* Config data */
+static const char *config_keys[] =
+{
+ "Host",
+ "Port",
+ "Server"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+/*
+ * Functions
+ */
+static int tss2_add_vserver (int vserver_port)
+{
+ /*
+ * Adds a new vserver to the linked list
+ */
+ vserver_list_t *entry;
+
+ /* Check port range */
+ if ((vserver_port <= 0) || (vserver_port > 65535))
+ {
+ ERROR ("teamspeak2 plugin: VServer port is invalid: %i",
+ vserver_port);
+ return (-1);
+ }
+
+ /* Allocate memory */
+ entry = (vserver_list_t *) malloc (sizeof (vserver_list_t));
+ if (entry == NULL)
+ {
+ ERROR ("teamspeak2 plugin: malloc failed.");
+ return (-1);
+ }
+ memset (entry, 0, sizeof (vserver_list_t));
+
+ /* Save data */
+ entry->port = vserver_port;
+
+ /* Insert to list */
+ if(server_list == NULL) {
+ /* Add the server as the first element */
+ server_list = entry;
+ }
+ else {
+ vserver_list_t *prev;
+
+ /* Add the server to the end of the list */
+ prev = server_list;
+ while (prev->next != NULL)
+ prev = prev->next;
+ prev->next = entry;
+ }
+
+ INFO ("teamspeak2 plugin: Registered new vserver: %i", vserver_port);
+
+ return (0);
+} /* int tss2_add_vserver */
+
+static void tss2_submit_gauge (const char *plugin_instance,
+ const char *type, const char *type_instance,
+ gauge_t value)
+{
+ /*
+ * Submits a gauge value to the collectd daemon
+ */
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].gauge = value;
+
+ vl.values = values;
+ vl.values_len = 1;
+ vl.time = time (NULL);
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "teamspeak2", sizeof (vl.plugin));
+
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance,
+ sizeof (vl.plugin_instance));
+
+ if (type_instance != NULL)
+ sstrncpy (vl.type_instance, type_instance,
+ sizeof (vl.type_instance));
+
+ plugin_dispatch_values (type, &vl);
+} /* void tss2_submit_gauge */
+
+static void tss2_submit_io (const char *plugin_instance, const char *type,
+ counter_t rx, counter_t tx)
+{
+ /*
+ * Submits the io rx/tx tuple to the collectd daemon
+ */
+ value_t values[2];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].counter = rx;
+ values[1].counter = tx;
+
+ vl.values = values;
+ vl.values_len = 2;
+ vl.time = time (NULL);
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "teamspeak2", sizeof (vl.plugin));
+
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance,
+ sizeof (vl.plugin_instance));
+
+ plugin_dispatch_values (type, &vl);
+} /* void tss2_submit_gauge */
+
+static void tss2_close_socket (void)
+{
+ /*
+ * Closes all sockets
+ */
+ if (global_write_fh != NULL)
+ {
+ fputs ("quit\r\n", global_write_fh);
+ }
+
+ if (global_read_fh != NULL)
+ {
+ fclose (global_read_fh);
+ global_read_fh = NULL;
+ }
+
+ if (global_write_fh != NULL)
+ {
+ fclose (global_write_fh);
+ global_write_fh = NULL;
+ }
+} /* void tss2_close_socket */
+
+static int tss2_get_socket (FILE **ret_read_fh, FILE **ret_write_fh)
+{
+ /*
+ * Returns connected file objects or establishes the connection
+ * if it's not already present
+ */
+ struct addrinfo ai_hints;
+ struct addrinfo *ai_head;
+ struct addrinfo *ai_ptr;
+ int sd = -1;
+ int status;
+
+ /* Check if we already got opened connections */
+ if ((global_read_fh != NULL) && (global_write_fh != NULL))
+ {
+ /* If so, use them */
+ if (ret_read_fh != NULL)
+ *ret_read_fh = global_read_fh;
+ if (ret_write_fh != NULL)
+ *ret_write_fh = global_write_fh;
+ return (0);
+ }
+
+ /* Get all addrs for this hostname */
+ memset (&ai_hints, 0, sizeof (ai_hints));
+#ifdef AI_ADDRCONFIG
+ ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+ ai_hints.ai_family = AF_UNSPEC;
+ ai_hints.ai_socktype = SOCK_STREAM;
+
+ status = getaddrinfo ((config_host != NULL) ? config_host : DEFAULT_HOST,
+ (config_port != NULL) ? config_port : DEFAULT_PORT,
+ &ai_hints,
+ &ai_head);
+ if (status != 0)
+ {
+ ERROR ("teamspeak2 plugin: getaddrinfo failed: %s",
+ gai_strerror (status));
+ return (-1);
+ }
+
+ /* Try all given hosts until we can connect to one */
+ for (ai_ptr = ai_head; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+ {
+ /* Create socket */
+ sd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
+ ai_ptr->ai_protocol);
+ if (sd < 0)
+ {
+ char errbuf[1024];
+ WARNING ("teamspeak2 plugin: socket failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ continue;
+ }
+
+ /* Try to connect */
+ status = connect (sd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ WARNING ("teamspeak2 plugin: connect failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ close (sd);
+ continue;
+ }
+
+ /*
+ * Success, we can break. Don't need more than one connection
+ */
+ break;
+ } /* for (ai_ptr) */
+
+ freeaddrinfo (ai_head);
+
+ /* Check if we really got connected */
+ if (sd < 0)
+ return (-1);
+
+ /* Create file objects from sockets */
+ global_read_fh = fdopen (sd, "r");
+ if (global_read_fh == NULL)
+ {
+ char errbuf[1024];
+ ERROR ("teamspeak2 plugin: fdopen failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ close (sd);
+ return (-1);
+ }
+
+ global_write_fh = fdopen (sd, "w");
+ if (global_write_fh == NULL)
+ {
+ char errbuf[1024];
+ ERROR ("teamspeak2 plugin: fdopen failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ tss2_close_socket ();
+ return (-1);
+ }
+
+ { /* Check that the server correctly identifies itself. */
+ char buffer[4096];
+ char *buffer_ptr;
+
+ buffer_ptr = fgets (buffer, sizeof (buffer), global_read_fh);
+ buffer[sizeof (buffer) - 1] = 0;
+
+ if (memcmp ("[TS]\r\n", buffer, 6) != 0)
+ {
+ ERROR ("teamspeak2 plugin: Unexpected response when connecting "
+ "to server. Expected ``[TS]'', got ``%s''.",
+ buffer);
+ tss2_close_socket ();
+ return (-1);
+ }
+ DEBUG ("teamspeak2 plugin: Server send correct banner, connected!");
+ }
+
+ /* Copy the new filehandles to the given pointers */
+ if (ret_read_fh != NULL)
+ *ret_read_fh = global_read_fh;
+ if (ret_write_fh != NULL)
+ *ret_write_fh = global_write_fh;
+ return (0);
+} /* int tss2_get_socket */
+
+static int tss2_send_request (FILE *fh, const char *request)
+{
+ /*
+ * This function puts a request to the server socket
+ */
+ int status;
+
+ status = fputs (request, fh);
+ if (status < 0)
+ {
+ ERROR ("teamspeak2 plugin: fputs failed.");
+ tss2_close_socket ();
+ return (-1);
+ }
+ fflush (fh);
+
+ return (0);
+} /* int tss2_send_request */
+
+static int tss2_receive_line (FILE *fh, char *buffer, int buffer_size)
+{
+ /*
+ * Receive a single line from the given file object
+ */
+ char *temp;
+
+ /*
+ * fgets is blocking but much easier then doing anything else
+ * TODO: Non-blocking Version would be safer
+ */
+ temp = fgets (buffer, buffer_size, fh);
+ if (temp == NULL)
+ {
+ char errbuf[1024];
+ ERROR ("teamspeak2 plugin: fgets failed: %s",
+ sstrerror (errno, errbuf, sizeof(errbuf)));
+ tss2_close_socket ();
+ return (-1);
+ }
+
+ buffer[buffer_size - 1] = 0;
+ return (0);
+} /* int tss2_receive_line */
+
+static int tss2_select_vserver (FILE *read_fh, FILE *write_fh, vserver_list_t *vserver)
+{
+ /*
+ * Tell the server to select the given vserver
+ */
+ char command[128];
+ char response[128];
+ int status;
+
+ /* Send request */
+ snprintf (command, sizeof (command), "sel %i\r\n", vserver->port);
+ command[sizeof (command) - 1] = 0;
+
+ status = tss2_send_request (write_fh, command);
+ if (status != 0)
+ {
+ ERROR ("teamspeak2 plugin: tss2_send_request (%s) failed.", command);
+ return (-1);
+ }
+
+ /* Get answer */
+ status = tss2_receive_line (read_fh, response, sizeof (response));
+ if (status != 0)
+ {
+ ERROR ("teamspeak2 plugin: tss2_receive_line failed.");
+ return (-1);
+ }
+ response[sizeof (response) - 1] = 0;
+
+ /* Check answer */
+ if ((strncasecmp ("OK", response, 2) == 0)
+ && ((response[2] == 0)
+ || (response[2] == '\n')
+ || (response[2] == '\r')))
+ return (0);
+
+ ERROR ("teamspeak2 plugin: Command ``%s'' failed. "
+ "Response received from server was: ``%s''.",
+ command, response);
+ return (-1);
+} /* int tss2_select_vserver */
+
+static int tss2_vserver_gapl (FILE *read_fh, FILE *write_fh,
+ vserver_list_t *vserver, gauge_t *ret_value)
+{
+ /*
+ * Reads the vserver's average packet loss and submits it to collectd.
+ * Be sure to run the tss2_read_vserver function before calling this so
+ * the vserver is selected correctly.
+ */
+ gauge_t packet_loss = NAN;
+ int status;
+
+ status = tss2_send_request (write_fh, "gapl\r\n");
+ if (status != 0)
+ {
+ ERROR("teamspeak2 plugin: tss2_send_request (gapl) failed.");
+ return (-1);
+ }
+
+ while (42)
+ {
+ char buffer[4096];
+ char *value;
+ char *endptr = NULL;
+
+ status = tss2_receive_line (read_fh, buffer, sizeof (buffer));
+ if (status != 0)
+ {
+ /* Set to NULL just to make sure noone uses these FHs anymore. */
+ read_fh = NULL;
+ write_fh = NULL;
+ ERROR ("teamspeak2 plugin: tss2_receive_line failed.");
+ return (-1);
+ }
+ buffer[sizeof (buffer) - 1] = 0;
+
+ if (strncmp ("average_packet_loss=", buffer,
+ strlen ("average_packet_loss=")) == 0)
+ {
+ /* Got average packet loss, now interpret it */
+ value = &buffer[20];
+ /* Replace , with . */
+ while (*value != 0)
+ {
+ if (*value == ',')
+ {
+ *value = '.';
+ break;
+ }
+ value++;
+ }
+
+ value = &buffer[20];
+
+ packet_loss = strtod (value, &endptr);
+ if (value == endptr)
+ {
+ /* Failed */
+ WARNING ("teamspeak2 plugin: Could not read average package "
+ "loss from string: %s", buffer);
+ continue;
+ }
+ }
+ else if (strncasecmp ("OK", buffer, 2) == 0)
+ {
+ break;
+ }
+ else if (strncasecmp ("ERROR", buffer, 5) == 0)
+ {
+ ERROR ("teamspeak2 plugin: Server returned an error: %s", buffer);
+ return (-1);
+ }
+ else
+ {
+ WARNING ("teamspeak2 plugin: Server returned unexpected string: %s",
+ buffer);
+ }
+ }
+
+ *ret_value = packet_loss;
+ return (0);
+} /* int tss2_vserver_gapl */
+
+static int tss2_read_vserver (vserver_list_t *vserver)
+{
+ /*
+ * Poll information for the given vserver and submit it to collect.
+ * If vserver is NULL the global server information will be queried.
+ */
+ int status;
+
+ gauge_t users = NAN;
+ gauge_t channels = NAN;
+ gauge_t servers = NAN;
+ counter_t rx_octets = 0;
+ counter_t tx_octets = 0;
+ counter_t rx_packets = 0;
+ counter_t tx_packets = 0;
+ gauge_t packet_loss = NAN;
+ int valid = 0;
+
+ char plugin_instance[DATA_MAX_NAME_LEN];
+
+ FILE *read_fh;
+ FILE *write_fh;
+
+ /* Get the send/receive sockets */
+ status = tss2_get_socket (&read_fh, &write_fh);
+ if (status != 0)
+ {
+ ERROR ("teamspeak2 plugin: tss2_get_socket failed.");
+ return (-1);
+ }
+
+ if (vserver == NULL)
+ {
+ /* Request global information */
+ memset (plugin_instance, 0, sizeof (plugin_instance));
+
+ status = tss2_send_request (write_fh, "gi\r\n");
+ }
+ else
+ {
+ /* Request server information */
+ snprintf (plugin_instance, sizeof (plugin_instance), "vserver%i",
+ vserver->port);
+ plugin_instance[sizeof (plugin_instance) - 1] = 0;
+
+ /* Select the server */
+ status = tss2_select_vserver (read_fh, write_fh, vserver);
+ if (status != 0)
+ return (status);
+
+ status = tss2_send_request (write_fh, "si\r\n");
+ }
+
+ if (status != 0)
+ {
+ ERROR ("teamspeak2 plugin: tss2_send_request failed.");
+ return (-1);
+ }
+
+ /* Loop until break */
+ while (42)
+ {
+ char buffer[4096];
+ char *key;
+ char *value;
+ char *endptr = NULL;
+
+ /* Read one line of the server's answer */
+ status = tss2_receive_line (read_fh, buffer, sizeof (buffer));
+ if (status != 0)
+ {
+ /* Set to NULL just to make sure noone uses these FHs anymore. */
+ read_fh = NULL;
+ write_fh = NULL;
+ ERROR ("teamspeak2 plugin: tss2_receive_line failed.");
+ break;
+ }
+
+ if (strncasecmp ("ERROR", buffer, 5) == 0)
+ {
+ ERROR ("teamspeak2 plugin: Server returned an error: %s",
+ buffer);
+ break;
+ }
+ else if (strncasecmp ("OK", buffer, 2) == 0)
+ {
+ break;
+ }
+
+ /* Split line into key and value */
+ key = strchr (buffer, '_');
+ if (key == NULL)
+ {
+ DEBUG ("teamspeak2 plugin: Cannot parse line: %s", buffer);
+ continue;
+ }
+ key++;
+
+ /* Evaluate assignment */
+ value = strchr (key, '=');
+ if (value == NULL)
+ {
+ DEBUG ("teamspeak2 plugin: Cannot parse line: %s", buffer);
+ continue;
+ }
+ *value = 0;
+ value++;
+
+ /* Check for known key and save the given value */
+ /* global info: users_online,
+ * server info: currentusers. */
+ if ((strcmp ("currentusers", key) == 0)
+ || (strcmp ("users_online", key) == 0))
+ {
+ users = strtod (value, &endptr);
+ if (value != endptr)
+ valid |= 0x01;
+ }
+ /* global info: channels,
+ * server info: currentchannels. */
+ else if ((strcmp ("currentchannels", key) == 0)
+ || (strcmp ("channels", key) == 0))
+ {
+ channels = strtod (value, &endptr);
+ if (value != endptr)
+ valid |= 0x40;
+ }
+ /* global only */
+ else if (strcmp ("servers", key) == 0)
+ {
+ servers = strtod (value, &endptr);
+ if (value != endptr)
+ valid |= 0x80;
+ }
+ else if (strcmp ("bytesreceived", key) == 0)
+ {
+ rx_octets = strtoll (value, &endptr, 0);
+ if (value != endptr)
+ valid |= 0x02;
+ }
+ else if (strcmp ("bytessend", key) == 0)
+ {
+ tx_octets = strtoll (value, &endptr, 0);
+ if (value != endptr)
+ valid |= 0x04;
+ }
+ else if (strcmp ("packetsreceived", key) == 0)
+ {
+ rx_packets = strtoll (value, &endptr, 0);
+ if (value != endptr)
+ valid |= 0x08;
+ }
+ else if (strcmp ("packetssend", key) == 0)
+ {
+ tx_packets = strtoll (value, &endptr, 0);
+ if (value != endptr)
+ valid |= 0x10;
+ }
+ else if ((strncmp ("allow_codec_", key, strlen ("allow_codec_")) == 0)
+ || (strncmp ("bwinlast", key, strlen ("bwinlast")) == 0)
+ || (strncmp ("bwoutlast", key, strlen ("bwoutlast")) == 0)
+ || (strncmp ("webpost_", key, strlen ("webpost_")) == 0)
+ || (strcmp ("adminemail", key) == 0)
+ || (strcmp ("clan_server", key) == 0)
+ || (strcmp ("countrynumber", key) == 0)
+ || (strcmp ("id", key) == 0)
+ || (strcmp ("ispname", key) == 0)
+ || (strcmp ("linkurl", key) == 0)
+ || (strcmp ("maxusers", key) == 0)
+ || (strcmp ("name", key) == 0)
+ || (strcmp ("password", key) == 0)
+ || (strcmp ("platform", key) == 0)
+ || (strcmp ("server_platform", key) == 0)
+ || (strcmp ("server_uptime", key) == 0)
+ || (strcmp ("server_version", key) == 0)
+ || (strcmp ("udpport", key) == 0)
+ || (strcmp ("uptime", key) == 0)
+ || (strcmp ("users_maximal", key) == 0)
+ || (strcmp ("welcomemessage", key) == 0))
+ /* ignore */;
+ else
+ {
+ INFO ("teamspeak2 plugin: Unknown key-value-pair: "
+ "key = %s; value = %s;", key, value);
+ }
+ } /* while (42) */
+
+ /* Collect vserver packet loss rates only if the loop above did not exit
+ * with an error. */
+ if ((status == 0) && (vserver != NULL))
+ {
+ status = tss2_vserver_gapl (read_fh, write_fh, vserver, &packet_loss);
+ if (status == 0)
+ {
+ valid |= 0x20;
+ }
+ else
+ {
+ WARNING ("teamspeak2 plugin: Reading package loss "
+ "for vserver %i failed.", vserver->port);
+ }
+ }
+
+ if ((valid & 0x01) == 0x01)
+ tss2_submit_gauge (plugin_instance, "users", NULL, users);
+
+ if ((valid & 0x06) == 0x06)
+ tss2_submit_io (plugin_instance, "io_octets", rx_octets, tx_octets);
+
+ if ((valid & 0x18) == 0x18)
+ tss2_submit_io (plugin_instance, "io_packets", rx_packets, tx_packets);
+
+ if ((valid & 0x20) == 0x20)
+ tss2_submit_gauge (plugin_instance, "percent", "packet_loss", packet_loss);
+
+ if ((valid & 0x40) == 0x40)
+ tss2_submit_gauge (plugin_instance, "gauge", "channels", channels);
+
+ if ((valid & 0x80) == 0x80)
+ tss2_submit_gauge (plugin_instance, "gauge", "servers", servers);
+
+ if (valid == 0)
+ return (-1);
+ return (0);
+} /* int tss2_read_vserver */
+
+static int tss2_config (const char *key, const char *value)
+{
+ /*
+ * Interpret configuration values
+ */
+ if (strcasecmp ("Host", key) == 0)
+ {
+ char *temp;
+
+ temp = strdup (value);
+ if (temp == NULL)
+ {
+ ERROR("teamspeak2 plugin: strdup failed.");
+ return (1);
+ }
+ sfree (config_host);
+ config_host = temp;
+ }
+ else if (strcasecmp ("Port", key) == 0)
+ {
+ char *temp;
+
+ temp = strdup (value);
+ if (temp == NULL)
+ {
+ ERROR("teamspeak2 plugin: strdup failed.");
+ return (1);
+ }
+ sfree (config_port);
+ config_port = temp;
+ }
+ else if (strcasecmp ("Server", key) == 0)
+ {
+ /* Server variable found */
+ int status;
+
+ status = tss2_add_vserver (atoi (value));
+ if (status != 0)
+ return (1);
+ }
+ else
+ {
+ /* Unknown variable found */
+ return (-1);
+ }
+
+ return 0;
+} /* int tss2_config */
+
+static int tss2_read (void)
+{
+ /*
+ * Poll function which collects global and vserver information
+ * and submits it to collectd
+ */
+ vserver_list_t *vserver;
+ int success = 0;
+ int status;
+
+ /* Handle global server variables */
+ status = tss2_read_vserver (NULL);
+ if (status == 0)
+ {
+ success++;
+ }
+ else
+ {
+ WARNING ("teamspeak2 plugin: Reading global server variables failed.");
+ }
+
+ /* Handle vservers */
+ for (vserver = server_list; vserver != NULL; vserver = vserver->next)
+ {
+ status = tss2_read_vserver (vserver);
+ if (status == 0)
+ {
+ success++;
+ }
+ else
+ {
+ WARNING ("teamspeak2 plugin: Reading statistics "
+ "for vserver %i failed.", vserver->port);
+ continue;
+ }
+ }
+
+ if (success == 0)
+ return (-1);
+ return (0);
+} /* int tss2_read */
+
+static int tss2_shutdown(void)
+{
+ /*
+ * Shutdown handler
+ */
+ vserver_list_t *entry;
+
+ tss2_close_socket ();
+
+ entry = server_list;
+ server_list = NULL;
+ while (entry != NULL)
+ {
+ vserver_list_t *next;
+
+ next = entry->next;
+ sfree (entry);
+ entry = next;
+ }
+
+ /* Get rid of the configuration */
+ sfree (config_host);
+ sfree (config_port);
+
+ return (0);
+} /* int tss2_shutdown */
+
+void module_register(void)
+{
+ /*
+ * Mandatory module_register function
+ */
+ plugin_register_config ("teamspeak2", tss2_config,
+ config_keys, config_keys_num);
+ plugin_register_read ("teamspeak2", tss2_read);
+ plugin_register_shutdown ("teamspeak2", tss2_shutdown);
+} /* void module_register */
+
+/* vim: set sw=4 ts=4 : */
apache_requests count:COUNTER:0:134217728
apache_scoreboard count:GAUGE:0:65535
bitrate value:GAUGE:0:4294967295
+cache_result value:COUNTER:0:4294967295
+cache_size value:GAUGE:0:4294967295
charge value:GAUGE:0:U
connections value:COUNTER:0:U
counter value:COUNTER:U:U
disk_octets read:COUNTER:0:17179869183, write:COUNTER:0:17179869183
disk_ops read:COUNTER:0:4294967295, write:COUNTER:0:4294967295
disk_time read:COUNTER:0:1000000, write:COUNTER:0:1000000
+dns_answer value:COUNTER:0:65535
dns_octets queries:COUNTER:0:125000000, responses:COUNTER:0:125000000
dns_opcode value:COUNTER:0:65535
dns_qtype value:COUNTER:0:65535
+dns_question value:COUNTER:0:65535
dns_rcode value:COUNTER:0:65535
email_check value:GAUGE:0:U
email_count value:GAUGE:0:U
if_packets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
if_rx_errors value:COUNTER:0:4294967295
if_tx_errors value:COUNTER:0:4294967295
+io_octets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
+io_packets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
ipt_bytes value:COUNTER:0:134217728
ipt_packets value:COUNTER:0:134217728
irq value:COUNTER:U:65535
+latency value:GAUGE:0:65535
load shortterm:GAUGE:0:100, midterm:GAUGE:0:100, longterm:GAUGE:0:100
memcached_command value:COUNTER:0:U
memcached_connections value:GAUGE:0:U
nginx_requests value:COUNTER:0:134217728
percent percent:GAUGE:0:100.1
ping ping:GAUGE:0:65535
+players value:GAUGE:0:1000000
power value:GAUGE:0:U
ps_count processes:GAUGE:0:1000000, threads:GAUGE:0:1000000
ps_cputime user:COUNTER:0:16000000, syst:COUNTER:0:16000000
users users:GAUGE:0:65535
virt_cpu_total ns:COUNTER:0:256000000000
virt_vcpu ns:COUNTER:0:1000000000
+vmpage_action value:COUNTER:0:4294967295
+vmpage_faults minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807
+vmpage_io in:COUNTER:0:4294967295, out:COUNTER:0:4294967295
+vmpage_number value:GAUGE:0:4294967295
voltage_threshold value:GAUGE:U:U, threshold:GAUGE:U:U
voltage value:GAUGE:U:U
vs_memory value:GAUGE:0:9223372036854775807
else
dsrc->max = atof (fields[3]);
- DEBUG ("parse_ds: dsrc = {%s, %i, %lf, %lf};",
- dsrc->name, dsrc->type, dsrc->min, dsrc->max);
-
return (0);
} /* int parse_ds */
return;
}
- DEBUG ("parse_line: ds = {%s, %i, %p};",
- ds->type, ds->ds_num, (void *) ds->ds);
-
plugin_register_data_set (ds);
sfree (ds->ds);
#include "plugin.h"
#include "configfile.h"
+#include "utils_cmd_flush.h"
+#include "utils_cmd_getval.h"
+#include "utils_cmd_listval.h"
#include "utils_cmd_putval.h"
#include "utils_cmd_putnotif.h"
#define US_DEFAULT_PATH LOCALSTATEDIR"/run/"PACKAGE_NAME"-unixsock"
/*
- * Private data structures
- */
-/* linked list of cached values */
-typedef struct value_cache_s
-{
- char name[4*DATA_MAX_NAME_LEN];
- int values_num;
- gauge_t *gauge;
- counter_t *counter;
- const data_set_t *ds;
- time_t time;
- struct value_cache_s *next;
-} value_cache_t;
-
-/*
* Private variables
*/
/* valid configuration file keys */
{
"SocketFile",
"SocketGroup",
- "SocketPerms",
- NULL
+ "SocketPerms"
};
-static int config_keys_num = 3;
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
static int loop = 0;
static pthread_t listen_thread = (pthread_t) 0;
-/* Linked list and auxilliary variables for saving values */
-static value_cache_t *cache_head = NULL;
-static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
-static time_t cache_oldest = -1;
-
/*
* Functions
*/
-static value_cache_t *cache_search (const char *name)
-{
- value_cache_t *vc;
-
- for (vc = cache_head; vc != NULL; vc = vc->next)
- {
- if (strcmp (vc->name, name) == 0)
- break;
- } /* for vc = cache_head .. NULL */
-
- return (vc);
-} /* value_cache_t *cache_search */
-
-static int cache_insert (const data_set_t *ds, const value_list_t *vl)
-{
- /* We're called from `cache_update' so we don't need to lock the mutex */
- value_cache_t *vc;
- int i;
-
- DEBUG ("unixsock plugin: cache_insert: ds->type = %s; ds->ds_num = %i;"
- " vl->values_len = %i;",
- ds->type, ds->ds_num, vl->values_len);
-#if COLLECT_DEBUG
- assert (ds->ds_num == vl->values_len);
-#else
- if (ds->ds_num != vl->values_len)
- {
- ERROR ("unixsock plugin: ds->type = %s: (ds->ds_num = %i) != "
- "(vl->values_len = %i)",
- ds->type, ds->ds_num, vl->values_len);
- return (-1);
- }
-#endif
-
- vc = (value_cache_t *) malloc (sizeof (value_cache_t));
- if (vc == NULL)
- {
- char errbuf[1024];
- pthread_mutex_unlock (&cache_lock);
- ERROR ("unixsock plugin: malloc failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- return (-1);
- }
-
- vc->gauge = (gauge_t *) malloc (sizeof (gauge_t) * vl->values_len);
- if (vc->gauge == NULL)
- {
- char errbuf[1024];
- pthread_mutex_unlock (&cache_lock);
- ERROR ("unixsock plugin: malloc failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- free (vc);
- return (-1);
- }
-
- vc->counter = (counter_t *) malloc (sizeof (counter_t) * vl->values_len);
- if (vc->counter == NULL)
- {
- char errbuf[1024];
- pthread_mutex_unlock (&cache_lock);
- ERROR ("unixsock plugin: malloc failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- free (vc->gauge);
- free (vc);
- return (-1);
- }
-
- if (FORMAT_VL (vc->name, sizeof (vc->name), vl, ds))
- {
- pthread_mutex_unlock (&cache_lock);
- ERROR ("unixsock plugin: FORMAT_VL failed.");
- free (vc->counter);
- free (vc->gauge);
- free (vc);
- return (-1);
- }
-
- for (i = 0; i < ds->ds_num; i++)
- {
- if (ds->ds[i].type == DS_TYPE_COUNTER)
- {
- vc->gauge[i] = 0.0;
- vc->counter[i] = vl->values[i].counter;
- }
- else if (ds->ds[i].type == DS_TYPE_GAUGE)
- {
- vc->gauge[i] = vl->values[i].gauge;
- vc->counter[i] = 0;
- }
- else
- {
- vc->gauge[i] = 0.0;
- vc->counter[i] = 0;
- }
- }
- vc->values_num = ds->ds_num;
- vc->ds = ds;
-
- vc->next = cache_head;
- cache_head = vc;
-
- vc->time = vl->time;
- if ((vc->time < cache_oldest) || (-1 == cache_oldest))
- cache_oldest = vc->time;
-
- pthread_mutex_unlock (&cache_lock);
- return (0);
-} /* int cache_insert */
-
-static int cache_update (const data_set_t *ds, const value_list_t *vl)
-{
- char name[4*DATA_MAX_NAME_LEN];;
- value_cache_t *vc;
- int i;
-
- if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
- return (-1);
-
- pthread_mutex_lock (&cache_lock);
-
- vc = cache_search (name);
-
- /* pthread_mutex_lock is called by cache_insert. */
- if (vc == NULL)
- return (cache_insert (ds, vl));
-
- assert (vc->values_num == ds->ds_num);
- assert (vc->values_num == vl->values_len);
-
- /* Avoid floating-point exceptions due to division by zero. */
- if (vc->time >= vl->time)
- {
- pthread_mutex_unlock (&cache_lock);
- ERROR ("unixsock plugin: vc->time >= vl->time. vc->time = %u; "
- "vl->time = %u; vl = %s;",
- (unsigned int) vc->time, (unsigned int) vl->time,
- name);
- return (-1);
- } /* if (vc->time >= vl->time) */
-
- /*
- * Update the values. This is possibly a lot more that you'd expect
- * because we honor min and max values and handle counter overflows here.
- */
- for (i = 0; i < ds->ds_num; i++)
- {
- if (ds->ds[i].type == DS_TYPE_COUNTER)
- {
- if (vl->values[i].counter < vc->counter[i])
- {
- if (vl->values[i].counter <= 4294967295U)
- {
- vc->gauge[i] = ((4294967295U - vl->values[i].counter)
- + vc->counter[i]) / (vl->time - vc->time);
- }
- else
- {
- vc->gauge[i] = ((18446744073709551615ULL - vl->values[i].counter)
- + vc->counter[i]) / (vl->time - vc->time);
- }
- }
- else
- {
- vc->gauge[i] = (vl->values[i].counter - vc->counter[i])
- / (vl->time - vc->time);
- }
-
- vc->counter[i] = vl->values[i].counter;
- }
- else if (ds->ds[i].type == DS_TYPE_GAUGE)
- {
- vc->gauge[i] = vl->values[i].gauge;
- vc->counter[i] = 0;
- }
- else
- {
- vc->gauge[i] = NAN;
- vc->counter[i] = 0;
- }
-
- if (isnan (vc->gauge[i])
- || (!isnan (ds->ds[i].min) && (vc->gauge[i] < ds->ds[i].min))
- || (!isnan (ds->ds[i].max) && (vc->gauge[i] > ds->ds[i].max)))
- vc->gauge[i] = NAN;
- } /* for i = 0 .. ds->ds_num */
-
- vc->ds = ds;
- vc->time = vl->time;
-
- if ((vc->time < cache_oldest) || (-1 == cache_oldest))
- cache_oldest = vc->time;
-
- pthread_mutex_unlock (&cache_lock);
- return (0);
-} /* int cache_update */
-
-static void cache_flush (int max_age)
-{
- value_cache_t *this;
- value_cache_t *prev;
- time_t now;
-
- pthread_mutex_lock (&cache_lock);
-
- now = time (NULL);
-
- if ((now - cache_oldest) <= max_age)
- {
- pthread_mutex_unlock (&cache_lock);
- return;
- }
-
- cache_oldest = now;
-
- prev = NULL;
- this = cache_head;
-
- while (this != NULL)
- {
- if ((now - this->time) <= max_age)
- {
- if (this->time < cache_oldest)
- cache_oldest = this->time;
-
- prev = this;
- this = this->next;
- continue;
- }
-
- if (prev == NULL)
- cache_head = this->next;
- else
- prev->next = this->next;
-
- free (this->gauge);
- free (this->counter);
- free (this);
-
- if (prev == NULL)
- this = cache_head;
- else
- this = prev->next;
- } /* while (this != NULL) */
-
- pthread_mutex_unlock (&cache_lock);
-} /* void cache_flush */
-
static int us_open_socket (void)
{
struct sockaddr_un sa;
return (0);
} /* int us_open_socket */
-static int us_handle_getval (FILE *fh, char **fields, int fields_num)
-{
- char *hostname;
- char *plugin;
- char *plugin_instance;
- char *type;
- char *type_instance;
- char name[4*DATA_MAX_NAME_LEN];
- value_cache_t *vc;
- int status;
- int i;
-
- if (fields_num != 2)
- {
- DEBUG ("unixsock plugin: Wrong number of fields: %i", fields_num);
- fprintf (fh, "-1 Wrong number of fields: Got %i, expected 2.\n",
- fields_num);
- fflush (fh);
- return (-1);
- }
- DEBUG ("unixsock plugin: Got query for `%s'", fields[1]);
-
- status = parse_identifier (fields[1], &hostname,
- &plugin, &plugin_instance,
- &type, &type_instance);
- if (status != 0)
- {
- DEBUG ("unixsock plugin: Cannot parse `%s'", fields[1]);
- fprintf (fh, "-1 Cannot parse identifier.\n");
- fflush (fh);
- return (-1);
- }
-
- status = format_name (name, sizeof (name),
- hostname, plugin, plugin_instance, type, type_instance);
- if (status != 0)
- {
- fprintf (fh, "-1 format_name failed.\n");
- return (-1);
- }
-
- pthread_mutex_lock (&cache_lock);
-
- DEBUG ("vc = cache_search (%s)", name);
- vc = cache_search (name);
-
- if (vc == NULL)
- {
- DEBUG ("Did not find cache entry.");
- fprintf (fh, "-1 No such value");
- }
- else
- {
- DEBUG ("Found cache entry.");
- fprintf (fh, "%i", vc->values_num);
- for (i = 0; i < vc->values_num; i++)
- {
- fprintf (fh, " %s=", vc->ds->ds[i].name);
- if (isnan (vc->gauge[i]))
- fprintf (fh, "NaN");
- else
- fprintf (fh, "%12e", vc->gauge[i]);
- }
- }
-
- /* Free the mutex as soon as possible and definitely before flushing */
- pthread_mutex_unlock (&cache_lock);
-
- fprintf (fh, "\n");
- fflush (fh);
-
- return (0);
-} /* int us_handle_getval */
-
-static int us_handle_listval (FILE *fh, char **fields, int fields_num)
-{
- char buffer[1024];
- char **value_list = NULL;
- int value_list_len = 0;
- value_cache_t *entry;
- int i;
-
- if (fields_num != 1)
- {
- DEBUG ("unixsock plugin: us_handle_listval: "
- "Wrong number of fields: %i", fields_num);
- fprintf (fh, "-1 Wrong number of fields: Got %i, expected 1.\n",
- fields_num);
- fflush (fh);
- return (-1);
- }
-
- pthread_mutex_lock (&cache_lock);
-
- for (entry = cache_head; entry != NULL; entry = entry->next)
- {
- char **tmp;
-
- snprintf (buffer, sizeof (buffer), "%u %s\n",
- (unsigned int) entry->time, entry->name);
- buffer[sizeof (buffer) - 1] = '\0';
-
- tmp = realloc (value_list, sizeof (char *) * (value_list_len + 1));
- if (tmp == NULL)
- continue;
- value_list = tmp;
-
- value_list[value_list_len] = strdup (buffer);
-
- if (value_list[value_list_len] != NULL)
- value_list_len++;
- } /* for (entry) */
-
- pthread_mutex_unlock (&cache_lock);
-
- DEBUG ("unixsock plugin: us_handle_listval: value_list_len = %i", value_list_len);
- fprintf (fh, "%i Values found\n", value_list_len);
- for (i = 0; i < value_list_len; i++)
- fputs (value_list[i], fh);
- fflush (fh);
-
- return (0);
-} /* int us_handle_listval */
-
static void *us_handle_client (void *arg)
{
int fd;
pthread_exit ((void *) 1);
}
- while (fgets (buffer, sizeof (buffer), fhin) != NULL)
+ /* change output buffer to line buffered mode */
+ if (setvbuf (fhout, NULL, _IOLBF, 0) != 0)
+ {
+ char errbuf[1024];
+ ERROR ("unixsock plugin: setvbuf failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ fclose (fhin);
+ fclose (fhout);
+ pthread_exit ((void *) 1);
+ }
+
+ while (42)
{
int len;
+ errno = 0;
+ if (fgets (buffer, sizeof (buffer), fhin) == NULL)
+ {
+ if (errno != 0)
+ {
+ char errbuf[1024];
+ WARNING ("unixsock plugin: failed to read from socket #%i: %s",
+ fileno (fhin),
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ }
+ break;
+ }
+
len = strlen (buffer);
while ((len > 0)
&& ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
if (strcasecmp (fields[0], "getval") == 0)
{
- us_handle_getval (fhout, fields, fields_num);
+ handle_getval (fhout, fields, fields_num);
}
else if (strcasecmp (fields[0], "putval") == 0)
{
}
else if (strcasecmp (fields[0], "listval") == 0)
{
- us_handle_listval (fhout, fields, fields_num);
+ handle_listval (fhout, fields, fields_num);
}
else if (strcasecmp (fields[0], "putnotif") == 0)
{
handle_putnotif (fhout, fields, fields_num);
}
+ else if (strcasecmp (fields[0], "flush") == 0)
+ {
+ handle_flush (fhout, fields, fields_num);
+ }
else
{
- fprintf (fhout, "-1 Unknown command: %s\n", fields[0]);
- fflush (fhout);
+ if (fprintf (fhout, "-1 Unknown command: %s\n", fields[0]) < 0)
+ {
+ char errbuf[1024];
+ WARNING ("unixsock plugin: failed to write to socket #%i: %s",
+ fileno (fhout),
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ break;
+ }
}
} /* while (fgets) */
{
if (strcasecmp (key, "SocketFile") == 0)
{
+ char *new_sock_file = strdup (val);
+ if (new_sock_file == NULL)
+ return (1);
+
sfree (sock_file);
- sock_file = strdup (val);
+ sock_file = new_sock_file;
}
else if (strcasecmp (key, "SocketGroup") == 0)
{
+ char *new_sock_group = strdup (val);
+ if (new_sock_group == NULL)
+ return (1);
+
sfree (sock_group);
- sock_group = strdup (val);
+ sock_group = new_sock_group;
}
else if (strcasecmp (key, "SocketPerms") == 0)
{
return (0);
} /* int us_shutdown */
-static int us_write (const data_set_t *ds, const value_list_t *vl)
-{
- cache_update (ds, vl);
- cache_flush (2 * interval_g);
-
- return (0);
-}
-
void module_register (void)
{
plugin_register_config ("unixsock", us_config,
config_keys, config_keys_num);
plugin_register_init ("unixsock", us_init);
- plugin_register_write ("unixsock", us_write);
plugin_register_shutdown ("unixsock", us_shutdown);
} /* void module_register (void) */
#include "common.h"
#include "plugin.h"
+#if HAVE_STATGRAB_H
+# include <statgrab.h>
+#endif /* HAVE_STATGRAB_H */
+
#if HAVE_UTMPX_H
# include <utmpx.h>
/* #endif HAVE_UTMPX_H */
users_submit (users);
/* #endif HAVE_GETUTENT */
+#elif HAVE_LIBSTATGRAB
+ sg_user_stats *us;
+
+ us = sg_get_user_stats ();
+ if (us == NULL)
+ return (-1);
+
+ users_submit ((gauge_t) us->num_entries);
+/* #endif HAVE_LIBSTATGRAB */
+
#else
# error "No applicable input method."
#endif
/**
* collectd - src/utils_cache.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
return (0);
} /* int uc_update */
-gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl)
+int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_values_num)
{
- char name[6 * DATA_MAX_NAME_LEN];
gauge_t *ret = NULL;
+ size_t ret_num = 0;
cache_entry_t *ce = NULL;
-
- if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
- {
- ERROR ("uc_get_rate: FORMAT_VL failed.");
- return (NULL);
- }
+ int status = 0;
pthread_mutex_lock (&cache_lock);
if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
{
assert (ce != NULL);
- assert (ce->values_num == ds->ds_num);
- ret = (gauge_t *) malloc (ce->values_num * sizeof (gauge_t));
+ ret_num = ce->values_num;
+ ret = (gauge_t *) malloc (ret_num * sizeof (gauge_t));
if (ret == NULL)
{
- ERROR ("uc_get_rate: malloc failed.");
+ ERROR ("utils_cache: uc_get_rate_by_name: malloc failed.");
+ status = -1;
}
else
{
- memcpy (ret, ce->values_gauge, ce->values_num * sizeof (gauge_t));
+ memcpy (ret, ce->values_gauge, ret_num * sizeof (gauge_t));
}
}
+ else
+ {
+ DEBUG ("utils_cache: uc_get_rate_by_name: No such value: %s", name);
+ status = -1;
+ }
pthread_mutex_unlock (&cache_lock);
+ if (status == 0)
+ {
+ *ret_values = ret;
+ *ret_values_num = ret_num;
+ }
+
+ return (status);
+} /* gauge_t *uc_get_rate_by_name */
+
+gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl)
+{
+ char name[6 * DATA_MAX_NAME_LEN];
+ gauge_t *ret = NULL;
+ size_t ret_num = 0;
+ int status;
+
+ if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+ {
+ ERROR ("uc_insert: FORMAT_VL failed.");
+ return (NULL);
+ }
+
+ status = uc_get_rate_by_name (name, &ret, &ret_num);
+ if (status != 0)
+ return (NULL);
+
+ /* This is important - the caller has no other way of knowing how many
+ * values are returned. */
+ if (ret_num != ds->ds_num)
+ {
+ ERROR ("utils_cache: uc_get_rate: ds[%s] has %i values, "
+ "but uc_get_rate_by_name returned %i.",
+ ds->type, ds->ds_num, ret_num);
+ sfree (ret);
+ return (NULL);
+ }
+
return (ret);
} /* gauge_t *uc_get_rate */
+int uc_get_names (char ***ret_names, time_t **ret_times, size_t *ret_number)
+{
+ c_avl_iterator_t *iter;
+ char *key;
+ cache_entry_t *value;
+
+ char **names = NULL;
+ time_t *times = NULL;
+ size_t number = 0;
+
+ int status = 0;
+
+ if ((ret_names == NULL) || (ret_number == NULL))
+ return (-1);
+
+ pthread_mutex_lock (&cache_lock);
+
+ iter = c_avl_get_iterator (cache_tree);
+ while (c_avl_iterator_next (iter, (void *) &key, (void *) &value) == 0)
+ {
+ char **temp;
+
+ if (ret_times != NULL)
+ {
+ time_t *tmp_times;
+
+ tmp_times = (time_t *) realloc (times, sizeof (time_t) * (number + 1));
+ if (tmp_times == NULL)
+ {
+ status = -1;
+ break;
+ }
+ times = tmp_times;
+ times[number] = value->last_time;
+ }
+
+ temp = (char **) realloc (names, sizeof (char *) * (number + 1));
+ if (temp == NULL)
+ {
+ status = -1;
+ break;
+ }
+ names = temp;
+ names[number] = strdup (key);
+ if (names[number] == NULL)
+ {
+ status = -1;
+ break;
+ }
+ number++;
+ } /* while (c_avl_iterator_next) */
+
+ c_avl_iterator_destroy (iter);
+ pthread_mutex_unlock (&cache_lock);
+
+ if (status != 0)
+ {
+ size_t i;
+
+ for (i = 0; i < number; i++)
+ {
+ sfree (names[i]);
+ }
+ sfree (names);
+
+ return (-1);
+ }
+
+ *ret_names = names;
+ if (ret_times != NULL)
+ *ret_times = times;
+ *ret_number = number;
+
+ return (0);
+} /* int uc_get_names */
+
int uc_get_state (const data_set_t *ds, const value_list_t *vl)
{
char name[6 * DATA_MAX_NAME_LEN];
int uc_init (void);
int uc_check_timeout (void);
int uc_update (const data_set_t *ds, const value_list_t *vl);
+int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_values_num);
gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl);
+int uc_get_names (char ***ret_names, time_t **ret_times, size_t *ret_number);
+
int uc_get_state (const data_set_t *ds, const value_list_t *vl);
int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state);
--- /dev/null
+/**
+ * collectd - src/utils_cmd_flush.c
+ * Copyright (C) 2008 Sebastian Harl
+ * 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
+ *
+ * Authors:
+ * Sebastian "tokkee" Harl <sh at tokkee.org>
+ * Florian "octo" Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#define print_to_socket(fh, ...) \
+ if (fprintf (fh, __VA_ARGS__) < 0) { \
+ char errbuf[1024]; \
+ WARNING ("handle_flush: failed to write to socket #%i: %s", \
+ fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
+ return -1; \
+ }
+
+int handle_flush (FILE *fh, char **fields, int fields_num)
+{
+ int success = 0;
+ int error = 0;
+
+ int timeout = -1;
+
+ int i;
+
+ for (i = 1; i < fields_num; i++)
+ {
+ char *option = fields[i];
+ int status = 0;
+
+ if (strncasecmp ("plugin=", option, strlen ("plugin=")) == 0)
+ {
+ char *plugin = option + strlen ("plugin=");
+
+ if (0 == plugin_flush_one (timeout, plugin))
+ ++success;
+ else
+ ++error;
+ }
+ else if (strncasecmp ("timeout=", option, strlen ("timeout=")) == 0)
+ {
+ char *endptr = NULL;
+ char *value = option + strlen ("timeout=");
+
+ errno = 0;
+ timeout = strtol (value, &endptr, 0);
+
+ if ((endptr == value) || (0 != errno))
+ status = -1;
+ else if (0 >= timeout)
+ timeout = -1;
+ }
+ else
+ status = -1;
+
+ if (status != 0)
+ {
+ print_to_socket (fh, "-1 Cannot parse option %s\n", option);
+ return (-1);
+ }
+ }
+
+ if ((success + error) > 0)
+ {
+ print_to_socket (fh, "0 Done: %i successful, %i errors\n",
+ success, error);
+ }
+ else
+ {
+ plugin_flush_all (timeout);
+ print_to_socket (fh, "0 Done\n");
+ }
+
+ return (0);
+} /* int handle_flush */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
--- /dev/null
+/**
+ * collectd - src/utils_cmd_flush.h
+ * Copyright (C) 2008 Sebastian Harl
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Sebastian "tokkee" Harl <sh at tokkee.org>
+ **/
+
+#ifndef UTILS_CMD_FLUSH_H
+#define UTILS_CMD_FLUSH_H 1
+
+int handle_flush (FILE *fh, char **fields, int fields_num);
+
+#endif /* UTILS_CMD_FLUSH_H */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
--- /dev/null
+/**
+ * collectd - src/utils_cms_getval.c
+ * 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 verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include "utils_cache.h"
+
+#define print_to_socket(fh, ...) \
+ if (fprintf (fh, __VA_ARGS__) < 0) { \
+ char errbuf[1024]; \
+ WARNING ("handle_getval: failed to write to socket #%i: %s", \
+ fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
+ return -1; \
+ }
+
+int handle_getval (FILE *fh, char **fields, int fields_num)
+{
+ char *hostname;
+ char *plugin;
+ char *plugin_instance;
+ char *type;
+ char *type_instance;
+ gauge_t *values;
+ size_t values_num;
+
+ char *identifier_copy;
+
+ const data_set_t *ds;
+
+ int status;
+ int i;
+
+ if (fields_num != 2)
+ {
+ 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);
+ return (-1);
+ }
+ DEBUG ("unixsock plugin: Got query for `%s'", fields[1]);
+
+ if (strlen (fields[1]) < strlen ("h/p/t"))
+ {
+ print_to_socket (fh, "-1 Invalied identifier, %s\n", fields[1]);
+ return (-1);
+ }
+
+ /* parse_identifier() modifies its first argument,
+ * returning pointers into it */
+ identifier_copy = sstrdup (fields[1]);
+
+ 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");
+ sfree (identifier_copy);
+ return (-1);
+ }
+
+ ds = plugin_get_ds (type);
+ if (ds == NULL)
+ {
+ DEBUG ("unixsock plugin: 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);
+ if (status != 0)
+ {
+ print_to_socket (fh, "-1 No such value\n");
+ sfree (identifier_copy);
+ return (-1);
+ }
+
+ if (ds->ds_num != values_num)
+ {
+ ERROR ("ds[%s]->ds_num = %i, "
+ "but uc_get_rate_by_name returned %u values.",
+ ds->type, ds->ds_num, (unsigned int) values_num);
+ print_to_socket (fh, "-1 Error reading value from cache.\n");
+ sfree (values);
+ sfree (identifier_copy);
+ return (-1);
+ }
+
+ print_to_socket (fh, "%u Value%s found\n", (unsigned int) values_num,
+ (values_num == 1) ? "" : "s");
+ for (i = 0; i < values_num; i++)
+ {
+ print_to_socket (fh, "%s=", ds->ds[i].name);
+ if (isnan (values[i]))
+ {
+ print_to_socket (fh, "NaN\n");
+ }
+ else
+ {
+ print_to_socket (fh, "%12e\n", values[i]);
+ }
+ }
+
+ sfree (values);
+ sfree (identifier_copy);
+
+ return (0);
+} /* int handle_getval */
+
+/* vim: set sw=2 sts=2 ts=8 : */
--- /dev/null
+/**
+ * collectd - src/utils_cms_getval.h
+ * 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 verplant.org>
+ **/
+
+#ifndef UTILS_CMD_GETVAL_H
+#define UTILS_CMD_GETVAL_H 1
+
+int handle_getval (FILE *fh, char **fields, int fields_num);
+
+#endif /* UTILS_CMD_GETVAL_H */
+
+/* vim: set sw=2 sts=2 ts=8 : */
--- /dev/null
+/**
+ * collectd - src/utils_cms_listval.c
+ * 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 verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include "utils_cmd_listval.h"
+#include "utils_cache.h"
+
+#define print_to_socket(fh, ...) \
+ if (fprintf (fh, __VA_ARGS__) < 0) { \
+ char errbuf[1024]; \
+ WARNING ("handle_listval: failed to write to socket #%i: %s", \
+ fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
+ return -1; \
+ }
+
+int handle_listval (FILE *fh, char **fields, int fields_num)
+{
+ char **names = NULL;
+ time_t *times = NULL;
+ size_t number = 0;
+ size_t i;
+ int status;
+
+ if (fields_num != 1)
+ {
+ 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);
+ return (-1);
+ }
+
+ status = uc_get_names (&names, ×, &number);
+ if (status != 0)
+ {
+ DEBUG ("command listval: uc_get_names failed with status %i", status);
+ print_to_socket (fh, "-1 uc_get_names failed.\n");
+ return (-1);
+ }
+
+ print_to_socket (fh, "%i Value%s found\n",
+ (int) number, (number == 1) ? "" : "s");
+ for (i = 0; i < number; i++)
+ print_to_socket (fh, "%u %s\n", (unsigned int) times[i], names[i]);
+
+ return (0);
+} /* int handle_listval */
+
+/* vim: set sw=2 sts=2 ts=8 : */
--- /dev/null
+/**
+ * collectd - src/utils_cms_listval.h
+ * 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 verplant.org>
+ **/
+
+#ifndef UTILS_CMD_LISTVAL_H
+#define UTILS_CMD_LISTVAL_H 1
+
+int handle_listval (FILE *fh, char **fields, int fields_num);
+
+#endif /* UTILS_CMD_LISTVAL_H */
+
+/* vim: set sw=2 sts=2 ts=8 : */
#include "common.h"
#include "plugin.h"
+#define print_to_socket(fh, ...) \
+ if (fprintf (fh, __VA_ARGS__) < 0) { \
+ char errbuf[1024]; \
+ WARNING ("handle_putnotif: failed to write to socket #%i: %s", \
+ fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
+ return -1; \
+ }
+
static int parse_option_severity (notification_t *n, char *value)
{
if (strcasecmp (value, "Failure") == 0)
if (fields_num < 4)
{
DEBUG ("cmd putnotif: Wrong number of fields: %i", fields_num);
- fprintf (fh, "-1 Wrong number of fields: Got %i, expected at least 4.\n",
+ print_to_socket (fh, "-1 Wrong number of fields: Got %i, "
+ "expected at least 4.\n",
fields_num);
- fflush (fh);
return (-1);
}
status = parse_message (&n, fields + i, fields_num - i);
if (status != 0)
{
- fprintf (fh, "-1 Error parsing the message. Have you hit the "
+ print_to_socket (fh, "-1 Error parsing the message. Have you hit the "
"limit of %u bytes?\n", (unsigned int) sizeof (n.message));
}
break;
status = parse_option (&n, fields[i]);
if (status != 0)
{
- fprintf (fh, "-1 Error parsing option `%s'\n", fields[i]);
+ print_to_socket (fh, "-1 Error parsing option `%s'\n", fields[i]);
break;
}
}
/* Check for required fields and complain if anything is missing. */
if ((status == 0) && (n.severity == 0))
{
- fprintf (fh, "-1 Option `severity' missing.\n");
+ print_to_socket (fh, "-1 Option `severity' missing.\n");
status = -1;
}
if ((status == 0) && (n.time == 0))
{
- fprintf (fh, "-1 Option `time' missing.\n");
+ print_to_socket (fh, "-1 Option `time' missing.\n");
status = -1;
}
if ((status == 0) && (strlen (n.message) == 0))
{
- fprintf (fh, "-1 No message or message of length 0 given.\n");
+ print_to_socket (fh, "-1 No message or message of length 0 given.\n");
status = -1;
}
if (status == 0)
{
plugin_dispatch_notification (&n);
- fprintf (fh, "0 Success\n");
+ print_to_socket (fh, "0 Success\n");
}
- fflush (fh);
return (0);
} /* int handle_putnotif */
#ifndef UTILS_CMD_PUTNOTIF_H
#define UTILS_CMD_PUTNOTIF_H 1
-#include "plugin.h"
-
int handle_putnotif (FILE *fh, char **fields, int fields_num);
/* vim: set shiftwidth=2 softtabstop=2 tabstop=8 : */
#include "common.h"
#include "plugin.h"
+#define print_to_socket(fh, ...) \
+ if (fprintf (fh, __VA_ARGS__) < 0) { \
+ char errbuf[1024]; \
+ WARNING ("handle_putval: failed to write to socket #%i: %s", \
+ fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
+ return -1; \
+ }
+
static int parse_value (const data_set_t *ds, value_list_t *vl,
const char *type,
FILE *fh, char *buffer)
char *value_str = strchr (time_str, ':');
if (value_str == NULL)
{
- fprintf (fh, "-1 No time found.\n");
+ print_to_socket (fh, "-1 No time found.\n");
return (-1);
}
*value_str = '\0'; value_str++;
"Number of values incorrect: "
"Got %i, expected %i. Identifier is `%s'.",
i, vl->values_len, identifier);
- fprintf (fh, "-1 Number of values incorrect: "
+ print_to_socket (fh, "-1 Number of values incorrect: "
"Got %i, expected %i.\n",
i, vl->values_len);
return (-1);
{
DEBUG ("cmd putval: Wrong number of fields: %i",
fields_num);
- fprintf (fh, "-1 Wrong number of fields: Got %i, "
+ print_to_socket (fh, "-1 Wrong number of fields: Got %i, "
"expected at least 3.\n",
fields_num);
- fflush (fh);
return (-1);
}
if (status != 0)
{
DEBUG ("cmd putval: Cannot parse `%s'", fields[1]);
- fprintf (fh, "-1 Cannot parse identifier.\n");
- fflush (fh);
+ print_to_socket (fh, "-1 Cannot parse identifier.\n");
sfree (identifier_copy);
return (-1);
}
|| ((type_instance != NULL)
&& (strlen (type_instance) >= sizeof (vl.type_instance))))
{
- fprintf (fh, "-1 Identifier too long.\n");
- fflush (fh);
+ print_to_socket (fh, "-1 Identifier too long.\n");
sfree (identifier_copy);
return (-1);
}
vl.values = (value_t *) malloc (vl.values_len * sizeof (value_t));
if (vl.values == NULL)
{
- fprintf (fh, "-1 malloc failed.\n");
- fflush (fh);
+ print_to_socket (fh, "-1 malloc failed.\n");
sfree (identifier_copy);
return (-1);
}
{
if (parse_option (&vl, fields[i]) != 0)
{
- fprintf (fh, "-1 Error parsing option `%s'\n",
+ print_to_socket (fh, "-1 Error parsing option `%s'\n",
fields[i]);
break;
}
/* Done parsing the options. */
if (i == fields_num)
- fprintf (fh, "0 Success\n");
- fflush (fh);
+ print_to_socket (fh, "0 Success\n");
sfree (vl.values);
sfree (identifier_copy);
#ifndef UTILS_CMD_PUTVAL_H
#define UTILS_CMD_PUTVAL_H 1
-#include "plugin.h"
-
int handle_putval (FILE *fh, char **fields, int fields_num);
#endif /* UTILS_CMD_PUTVAL_H */
/**
* collectd - src/utils_ignorelist.c
* Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
+ * Copyright (C) 2008 Florian 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 Li-
*
* Authors:
* Lubos Stanek <lubek at users.sourceforge.net>
+ * Florian Forster <octo at verplant.org>
**/
/**
* ignorelist handles plugin's list of configured collectable
{
ignorelist_item_t *traverse;
- assert (il != NULL);
-
/* if no entries, collect all */
- if (il->head == NULL)
+ if ((il == NULL) || (il->head == NULL))
return (0);
if ((entry == NULL) || (strlen (entry) == 0))
--- /dev/null
+/**
+ * collectd - src/utils_match.c
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include "utils_match.h"
+
+#include <regex.h>
+
+#define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
+
+struct cu_match_s
+{
+ regex_t regex;
+ int flags;
+
+ int (*callback) (const char *str, char * const *matches, size_t matches_num,
+ void *user_data);
+ void *user_data;
+};
+
+/*
+ * Private functions
+ */
+static char *match_substr (const char *str, int begin, int end)
+{
+ char *ret;
+ size_t ret_len;
+
+ if ((begin < 0) || (end < 0) || (begin >= end))
+ return (NULL);
+ if (end > (strlen (str) + 1))
+ {
+ ERROR ("utils_match: match_substr: `end' points after end of string.");
+ return (NULL);
+ }
+
+ ret_len = end - begin;
+ ret = (char *) malloc (sizeof (char) * (ret_len + 1));
+ if (ret == NULL)
+ {
+ ERROR ("utils_match: match_substr: malloc failed.");
+ return (NULL);
+ }
+
+ sstrncpy (ret, str + begin, ret_len + 1);
+ return (ret);
+} /* char *match_substr */
+
+static int default_callback (const char *str,
+ char * const *matches, size_t matches_num, void *user_data)
+{
+ cu_match_value_t *data = (cu_match_value_t *) user_data;
+
+ if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
+ {
+ gauge_t value;
+ char *endptr = NULL;
+
+ if (matches_num < 2)
+ return (-1);
+
+ value = strtod (matches[1], &endptr);
+ if (matches[1] == endptr)
+ return (-1);
+
+ if ((data->values_num == 0)
+ || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST))
+ {
+ data->value.gauge = value;
+ }
+ else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE)
+ {
+ double f = ((double) data->values_num)
+ / ((double) (data->values_num + 1));
+ data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
+ }
+ else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN)
+ {
+ if (data->value.gauge > value)
+ data->value.gauge = value;
+ }
+ else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX)
+ {
+ if (data->value.gauge < value)
+ data->value.gauge = value;
+ }
+ else
+ {
+ ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+ return (-1);
+ }
+
+ data->values_num++;
+ }
+ else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER)
+ {
+ counter_t value;
+ char *endptr = NULL;
+
+ if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
+ {
+ data->value.counter++;
+ data->values_num++;
+ return (0);
+ }
+
+ if (matches_num < 2)
+ return (-1);
+
+ value = strtoll (matches[1], &endptr, 0);
+ if (matches[1] == endptr)
+ return (-1);
+
+ if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
+ data->value.counter = value;
+ else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
+ data->value.counter += value;
+ else
+ {
+ ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+ return (-1);
+ }
+
+ data->values_num++;
+ }
+ else
+ {
+ ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+ return (-1);
+ }
+
+ return (0);
+} /* int default_callback */
+
+/*
+ * Public functions
+ */
+cu_match_t *match_create_callback (const char *regex,
+ int (*callback) (const char *str,
+ char * const *matches, size_t matches_num, void *user_data),
+ void *user_data)
+{
+ cu_match_t *obj;
+ int status;
+
+ DEBUG ("utils_match: match_create_callback: regex = %s", regex);
+
+ obj = (cu_match_t *) malloc (sizeof (cu_match_t));
+ if (obj == NULL)
+ return (NULL);
+ memset (obj, '\0', sizeof (cu_match_t));
+
+ status = regcomp (&obj->regex, regex, REG_EXTENDED);
+ if (status != 0)
+ {
+ ERROR ("Compiling the regular expression \"%s\" failed.", regex);
+ sfree (obj);
+ return (NULL);
+ }
+
+ obj->callback = callback;
+ obj->user_data = user_data;
+
+ return (obj);
+} /* cu_match_t *match_create_callback */
+
+cu_match_t *match_create_simple (const char *regex, int match_ds_type)
+{
+ cu_match_value_t *user_data;
+ cu_match_t *obj;
+
+ user_data = (cu_match_value_t *) malloc (sizeof (cu_match_value_t));
+ if (user_data == NULL)
+ return (NULL);
+ memset (user_data, '\0', sizeof (cu_match_value_t));
+ user_data->ds_type = match_ds_type;
+
+ obj = match_create_callback (regex, default_callback, user_data);
+ if (obj == NULL)
+ {
+ sfree (user_data);
+ return (NULL);
+ }
+
+ obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
+
+ return (obj);
+} /* cu_match_t *match_create_simple */
+
+void match_destroy (cu_match_t *obj)
+{
+ if (obj == NULL)
+ return;
+
+ if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
+ {
+ sfree (obj->user_data);
+ }
+
+ sfree (obj);
+} /* void match_destroy */
+
+int match_apply (cu_match_t *obj, const char *str)
+{
+ int status;
+ regmatch_t re_match[32];
+ char *matches[32];
+ size_t matches_num;
+ int i;
+
+ if ((obj == NULL) || (str == NULL))
+ return (-1);
+
+ status = regexec (&obj->regex, str,
+ STATIC_ARRAY_SIZE (re_match), re_match,
+ /* eflags = */ 0);
+
+ /* Regex did not match */
+ if (status != 0)
+ return (0);
+
+ memset (matches, '\0', sizeof (matches));
+ for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
+ {
+ if ((re_match[matches_num].rm_so < 0)
+ || (re_match[matches_num].rm_eo < 0))
+ break;
+
+ matches[matches_num] = match_substr (str,
+ re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
+ if (matches[matches_num] == NULL)
+ {
+ status = -1;
+ break;
+ }
+ }
+
+ if (status != 0)
+ {
+ ERROR ("utils_match: match_apply: match_substr failed.");
+ }
+ else
+ {
+ status = obj->callback (str, matches, matches_num, obj->user_data);
+ if (status != 0)
+ {
+ ERROR ("utils_match: match_apply: callback failed.");
+ }
+ }
+
+ for (i = 0; i < matches_num; i++)
+ {
+ sfree (matches[i]);
+ }
+
+ return (status);
+} /* int match_apply */
+
+void *match_get_user_data (cu_match_t *obj)
+{
+ if (obj == NULL)
+ return (NULL);
+ return (obj->user_data);
+} /* void *match_get_user_data */
+
+/* vim: set sw=2 sts=2 ts=8 : */
--- /dev/null
+/**
+ * collectd - src/utils_match.h
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#ifndef UTILS_MATCH_H
+#define UTILS_MATCH_H 1
+
+#include "plugin.h"
+
+/*
+ * Defines
+ */
+#define UTILS_MATCH_DS_TYPE_GAUGE 0x10
+#define UTILS_MATCH_DS_TYPE_COUNTER 0x20
+
+#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
+#define UTILS_MATCH_CF_GAUGE_MIN 0x02
+#define UTILS_MATCH_CF_GAUGE_MAX 0x04
+#define UTILS_MATCH_CF_GAUGE_LAST 0x08
+
+#define UTILS_MATCH_CF_COUNTER_SET 0x01
+#define UTILS_MATCH_CF_COUNTER_ADD 0x02
+#define UTILS_MATCH_CF_COUNTER_INC 0x04
+
+/*
+ * Data types
+ */
+struct cu_match_s;
+typedef struct cu_match_s cu_match_t;
+
+struct cu_match_value_s
+{
+ int ds_type;
+ value_t value;
+ unsigned int values_num;
+};
+typedef struct cu_match_value_s cu_match_value_t;
+
+/*
+ * Prototypes
+ */
+/*
+ * NAME
+ * match_create_callback
+ *
+ * DESCRIPTION
+ * Creates a new `cu_match_t' object which will use the regular expression
+ * `regex' to match lines, see the `match_apply' method below. If the line
+ * matches, the callback passed in `callback' will be called along with the
+ * pointer `user_pointer'.
+ * The string that's passed to the callback depends on the regular expression:
+ * If the regular expression includes a sub-match, i. e. something like
+ * "value=([0-9][0-9]*)"
+ * then only the submatch (the part in the parenthesis) will be passed to the
+ * callback. If there is no submatch, then the entire string is passed to the
+ * callback.
+ */
+cu_match_t *match_create_callback (const char *regex,
+ int (*callback) (const char *str,
+ char * const *matches, size_t matches_num, void *user_data),
+ void *user_data);
+
+/*
+ * NAME
+ * match_create_simple
+ *
+ * DESCRIPTION
+ * Creates a new `cu_match_t' with a default callback. The user data for that
+ * default callback will be a `cu_match_value_t' structure, with
+ * `ds_type' copied to the structure. The default callback will handle the
+ * string as containing a number (see strtoll(3) and strtod(3)) and store that
+ * number in the `value' member. How that is done depends on `ds_type':
+ *
+ * UTILS_MATCH_DS_TYPE_GAUGE
+ * The function will search for a floating point number in the string and
+ * store it in value.gauge.
+ * UTILS_MATCH_DS_TYPE_COUNTER_SET
+ * The function will search for an integer in the string and store it in
+ * value.counter.
+ * UTILS_MATCH_DS_TYPE_COUNTER_ADD
+ * The function will search for an integer in the string and add it to the
+ * value in value.counter.
+ * UTILS_MATCH_DS_TYPE_COUNTER_INC
+ * The function will not search for anything in the string and increase
+ * value.counter by one.
+ */
+cu_match_t *match_create_simple (const char *regex, int ds_type);
+
+/*
+ * NAME
+ * match_destroy
+ *
+ * DESCRIPTION
+ * Destroys the object and frees all internal resources.
+ */
+void match_destroy (cu_match_t *obj);
+
+/*
+ * NAME
+ * match_apply
+ *
+ * DESCRIPTION
+ * Tries to match the string `str' with the regular expression of `obj'. If
+ * the string matches, calls the callback in `obj' with the (sub-)match.
+ *
+ * The user_data pointer passed to `match_create_callback' is NOT freed
+ * automatically. The `cu_match_value_t' structure allocated by
+ * `match_create_callback' is freed automatically.
+ */
+int match_apply (cu_match_t *obj, const char *str);
+
+/*
+ * NAME
+ * match_get_user_data
+ *
+ * DESCRIPTION
+ * Returns the pointer passed to `match_create_callback' or a pointer to the
+ * `cu_match_value_t' structure allocated by `match_create_callback'.
+ */
+void *match_get_user_data (cu_match_t *obj);
+
+#endif /* UTILS_MATCH_H */
+
+/* vim: set sw=2 sts=2 ts=8 : */
--- /dev/null
+/**
+ * collectd - src/utils_tail.c
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ * 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:
+ * Luke Heberling <lukeh at c-ware.com>
+ * Florian Forster <octo at verplant.org>
+ *
+ * Description:
+ * Encapsulates useful code for plugins which must watch for appends to
+ * the end of a file.
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_tail.h"
+
+struct cu_tail_s
+{
+ char *file;
+ FILE *fh;
+ struct stat stat;
+};
+
+static int cu_tail_reopen (cu_tail_t *obj)
+{
+ int seek_end = 0;
+ FILE *fh;
+ struct stat stat_buf;
+ int status;
+
+ memset (&stat_buf, 0, sizeof (stat_buf));
+ status = stat (obj->file, &stat_buf);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("utils_tail: stat (%s) failed: %s", obj->file,
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+
+ /* The file is already open.. */
+ if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino))
+ {
+ /* Seek to the beginning if file was truncated */
+ if (stat_buf.st_size < obj->stat.st_size)
+ {
+ INFO ("utils_tail: File `%s' was truncated.", obj->file);
+ status = fseek (obj->fh, 0, SEEK_SET);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("utils_tail: fseek (%s) failed: %s", obj->file,
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ fclose (obj->fh);
+ obj->fh = NULL;
+ return (-1);
+ }
+ }
+ memcpy (&obj->stat, &stat_buf, sizeof (struct stat));
+ return (1);
+ }
+
+ /* Seek to the end if we re-open the same file again or the file opened
+ * is the first at all or the first after an error */
+ if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino))
+ seek_end = 1;
+
+ fh = fopen (obj->file, "r");
+ if (fh == NULL)
+ {
+ char errbuf[1024];
+ ERROR ("utils_tail: fopen (%s) failed: %s", obj->file,
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+
+ if (seek_end != 0)
+ {
+ status = fseek (fh, 0, SEEK_END);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("utils_tail: fseek (%s) failed: %s", obj->file,
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ fclose (fh);
+ return (-1);
+ }
+ }
+
+ if (obj->fh != NULL)
+ fclose (obj->fh);
+ obj->fh = fh;
+ memcpy (&obj->stat, &stat_buf, sizeof (struct stat));
+
+ return (0);
+} /* int cu_tail_reopen */
+
+cu_tail_t *cu_tail_create (const char *file)
+{
+ cu_tail_t *obj;
+
+ obj = (cu_tail_t *) malloc (sizeof (cu_tail_t));
+ if (obj == NULL)
+ return (NULL);
+ memset (obj, '\0', sizeof (cu_tail_t));
+
+ obj->file = strdup (file);
+ if (obj->file == NULL)
+ {
+ free (obj);
+ return (NULL);
+ }
+
+ obj->fh = NULL;
+
+ return (obj);
+} /* cu_tail_t *cu_tail_create */
+
+int cu_tail_destroy (cu_tail_t *obj)
+{
+ if (obj->fh != NULL)
+ fclose (obj->fh);
+ free (obj->file);
+ free (obj);
+
+ return (0);
+} /* int cu_tail_destroy */
+
+int cu_tail_readline (cu_tail_t *obj, char *buf, int buflen)
+{
+ int status;
+
+ if (buflen < 1)
+ {
+ ERROR ("utils_tail: cu_tail_readline: buflen too small: %i bytes.",
+ buflen);
+ return (-1);
+ }
+
+ if (obj->fh == NULL)
+ {
+ status = cu_tail_reopen (obj);
+ if (status < 0)
+ return (status);
+ }
+ assert (obj->fh != NULL);
+
+ /* Try to read from the filehandle. If that succeeds, everything appears to
+ * be fine and we can return. */
+ if (fgets (buf, buflen, obj->fh) != NULL)
+ {
+ buf[buflen - 1] = 0;
+ return (0);
+ }
+
+ /* Check if we encountered an error */
+ if (ferror (obj->fh) != 0)
+ {
+ /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */
+ fclose (obj->fh);
+ obj->fh = NULL;
+ }
+ /* else: eof -> check if the file was moved away and reopen the new file if
+ * so.. */
+
+ status = cu_tail_reopen (obj);
+ /* error -> return with error */
+ if (status < 0)
+ return (status);
+ /* file end reached and file not reopened -> nothing more to read */
+ else if (status > 0)
+ {
+ buf[0] = 0;
+ return (0);
+ }
+
+ /* If we get here: file was re-opened and there may be more to read.. Let's
+ * try again. */
+ if (fgets (buf, buflen, obj->fh) != NULL)
+ {
+ buf[buflen - 1] = 0;
+ return (0);
+ }
+
+ if (ferror (obj->fh) != 0)
+ {
+ char errbuf[1024];
+ WARNING ("utils_tail: fgets (%s) returned an error: %s", obj->file,
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ fclose (obj->fh);
+ obj->fh = NULL;
+ return (-1);
+ }
+
+ /* EOf, well, apparently the new file is empty.. */
+ buf[0] = 0;
+ return (0);
+} /* int cu_tail_readline */
+
+int cu_tail_read (cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
+ void *data)
+{
+ int status;
+
+ while (42)
+ {
+ status = cu_tail_readline (obj, buf, buflen);
+ if (status != 0)
+ {
+ ERROR ("utils_tail: cu_tail_read: cu_tail_readline "
+ "failed.");
+ break;
+ }
+
+ /* check for EOF */
+ if (buf[0] == 0)
+ break;
+
+ status = callback (data, buf, buflen);
+ if (status != 0)
+ {
+ ERROR ("utils_tail: cu_tail_read: callback returned "
+ "status %i.", status);
+ break;
+ }
+ }
+
+ return status;
+} /* int cu_tail_read */
--- /dev/null
+/**
+ * collectd - src/utils_tail.h
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ *
+ * 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:
+ * Luke Heberling <lukeh at c-ware.com>
+ *
+ * DESCRIPTION
+ * Facilitates reading information that is appended to a file, taking into
+ * account that the file may be rotated and a new file created under the
+ * same name.
+ **/
+
+#ifndef UTILS_TAIL_H
+#define UTILS_TAIL_H 1
+
+struct cu_tail_s;
+typedef struct cu_tail_s cu_tail_t;
+
+typedef int tailfunc_t(void *data, char *buf, int buflen);
+
+/*
+ * NAME
+ * cu_tail_create
+ *
+ * DESCRIPTION
+ * Allocates a new tail object..
+ *
+ * PARAMETERS
+ * `file' The name of the file to be tailed.
+ */
+cu_tail_t *cu_tail_create (const char *file);
+
+/*
+ * cu_tail_destroy
+ *
+ * Takes a tail object returned by `cu_tail_create' and destroys it, freeing
+ * all internal memory.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_destroy (cu_tail_t *obj);
+
+/*
+ * cu_tail_readline
+ *
+ * Reads from the file until `buflen' characters are read, a newline
+ * character is read, or an eof condition is encountered. `buf' is
+ * always null-terminated on successful return and isn't touched when non-zero
+ * is returned.
+ *
+ * You can check if the EOF condition is reached by looking at the buffer: If
+ * the length of the string stored in the buffer is zero, EOF occurred.
+ * Otherwise at least the newline character will be in the buffer.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_readline (cu_tail_t *obj, char *buf, int buflen);
+
+/*
+ * cu_tail_readline
+ *
+ * Reads from the file until eof condition or an error is encountered.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_read (cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
+ void *data);
+
+#endif /* UTILS_TAIL_H */
--- /dev/null
+/*
+ * collectd - src/utils_tail_match.c
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ * 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:
+ * Luke Heberling <lukeh at c-ware.com>
+ * Florian Forster <octo at verplant.org>
+ *
+ * Description:
+ * Encapsulates useful code to plugins which must parse a log file.
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_match.h"
+#include "utils_tail.h"
+#include "utils_tail_match.h"
+
+struct cu_tail_match_simple_s
+{
+ char plugin[DATA_MAX_NAME_LEN];
+ char plugin_instance[DATA_MAX_NAME_LEN];
+ char type[DATA_MAX_NAME_LEN];
+ char type_instance[DATA_MAX_NAME_LEN];
+};
+typedef struct cu_tail_match_simple_s cu_tail_match_simple_t;
+
+struct cu_tail_match_match_s
+{
+ cu_match_t *match;
+ void *user_data;
+ int (*submit) (cu_match_t *match, void *user_data);
+ void (*free) (void *user_data);
+};
+typedef struct cu_tail_match_match_s cu_tail_match_match_t;
+
+struct cu_tail_match_s
+{
+ int flags;
+ cu_tail_t *tail;
+
+ cu_tail_match_match_t *matches;
+ size_t matches_num;
+};
+
+/*
+ * Private functions
+ */
+static int simple_submit_match (cu_match_t *match, void *user_data)
+{
+ cu_tail_match_simple_t *data = (cu_tail_match_simple_t *) user_data;
+ cu_match_value_t *match_value;
+ value_list_t vl = VALUE_LIST_INIT;
+ value_t values[1];
+
+ match_value = (cu_match_value_t *) match_get_user_data (match);
+ if (match_value == NULL)
+ return (-1);
+
+ if ((match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
+ && (match_value->values_num == 0))
+ values[0].gauge = NAN;
+ else
+ values[0] = match_value->value;
+
+ vl.values = values;
+ vl.values_len = 1;
+ vl.time = time (NULL);
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, data->plugin, sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, data->plugin_instance,
+ sizeof (vl.plugin_instance));
+ sstrncpy (vl.type_instance, data->type_instance,
+ sizeof (vl.type_instance));
+
+ plugin_dispatch_values (data->type, &vl);
+
+ if (match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
+ {
+ match_value->value.gauge = NAN;
+ match_value->values_num = 0;
+ }
+
+ return (0);
+} /* int simple_submit_match */
+
+static int tail_callback (void *data, char *buf, int buflen)
+{
+ cu_tail_match_t *obj = (cu_tail_match_t *) data;
+ int i;
+
+ for (i = 0; i < obj->matches_num; i++)
+ match_apply (obj->matches[i].match, buf);
+
+ return (0);
+} /* int tail_callback */
+
+/*
+ * Public functions
+ */
+cu_tail_match_t *tail_match_create (const char *filename)
+{
+ cu_tail_match_t *obj;
+
+ obj = (cu_tail_match_t *) malloc (sizeof (cu_tail_match_t));
+ if (obj == NULL)
+ return (NULL);
+ memset (obj, '\0', sizeof (cu_tail_match_t));
+
+ obj->tail = cu_tail_create (filename);
+ if (obj->tail == NULL)
+ {
+ sfree (obj);
+ return (NULL);
+ }
+
+ return (obj);
+} /* cu_tail_match_t *tail_match_create */
+
+void tail_match_destroy (cu_tail_match_t *obj)
+{
+ int i;
+
+ if (obj == NULL)
+ return;
+
+ if (obj->tail != NULL)
+ {
+ cu_tail_destroy (obj->tail);
+ obj->tail = NULL;
+ }
+
+ for (i = 0; i < obj->matches_num; i++)
+ {
+ cu_tail_match_match_t *match = obj->matches + i;
+ if (match->match != NULL)
+ {
+ match_destroy (match->match);
+ match->match = NULL;
+ }
+
+ if ((match->user_data != NULL)
+ && (match->free != NULL))
+ (*match->free) (match->user_data);
+ match->user_data = NULL;
+ }
+
+ sfree (obj->matches);
+ sfree (obj);
+} /* void tail_match_destroy */
+
+int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
+ int (*submit_match) (cu_match_t *match, void *user_data),
+ void *user_data,
+ void (*free_user_data) (void *user_data))
+{
+ cu_tail_match_match_t *temp;
+
+ temp = (cu_tail_match_match_t *) realloc (obj->matches,
+ sizeof (cu_tail_match_match_t) * (obj->matches_num + 1));
+ if (temp == NULL)
+ return (-1);
+
+ obj->matches = temp;
+ obj->matches_num++;
+
+ temp = obj->matches + (obj->matches_num - 1);
+
+ temp->match = match;
+ temp->user_data = user_data;
+ temp->submit = submit_match;
+ temp->free = free_user_data;
+
+ return (0);
+} /* int tail_match_add_match */
+
+int tail_match_add_match_simple (cu_tail_match_t *obj,
+ const char *regex, int ds_type,
+ const char *plugin, const char *plugin_instance,
+ const char *type, const char *type_instance)
+{
+ cu_match_t *match;
+ cu_tail_match_simple_t *user_data;
+ int status;
+
+ match = match_create_simple (regex, ds_type);
+ if (match == NULL)
+ return (-1);
+
+ user_data = (cu_tail_match_simple_t *) malloc (sizeof (cu_tail_match_simple_t));
+ if (user_data == NULL)
+ {
+ match_destroy (match);
+ return (-1);
+ }
+ memset (user_data, '\0', sizeof (cu_tail_match_simple_t));
+
+ sstrncpy (user_data->plugin, plugin, sizeof (user_data->plugin));
+ if (plugin_instance != NULL)
+ sstrncpy (user_data->plugin_instance, plugin_instance,
+ sizeof (user_data->plugin_instance));
+
+ sstrncpy (user_data->type, type, sizeof (user_data->type));
+ if (type_instance != NULL)
+ sstrncpy (user_data->type_instance, type_instance,
+ sizeof (user_data->type_instance));
+
+ status = tail_match_add_match (obj, match, simple_submit_match,
+ user_data, free);
+
+ if (status != 0)
+ {
+ match_destroy (match);
+ sfree (user_data);
+ }
+
+ return (status);
+} /* int tail_match_add_match_simple */
+
+int tail_match_read (cu_tail_match_t *obj)
+{
+ char buffer[4096];
+ int status;
+ int i;
+
+ status = cu_tail_read (obj->tail, buffer, sizeof (buffer), tail_callback,
+ (void *) obj);
+ if (status != 0)
+ {
+ ERROR ("tail_match: cu_tail_read failed.");
+ return (status);
+ }
+
+ for (i = 0; i < obj->matches_num; i++)
+ {
+ cu_tail_match_match_t *lt_match = obj->matches + i;
+
+ if (lt_match->submit == NULL)
+ continue;
+
+ (*lt_match->submit) (lt_match->match, lt_match->user_data);
+ }
+
+ return (0);
+} /* int tail_match_read */
+
+/* vim: set sw=2 sts=2 ts=8 : */
--- /dev/null
+/*
+ * collectd - src/utils_tail_match.h
+ * Copyright (C) 2007-2008 C-Ware, Inc.
+ * 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
+ *
+ * Authors:
+ * Luke Heberling <lukeh at c-ware.com>
+ * Florian Forster <octo at verplant.org>
+ *
+ * Description:
+ * `tail_match' uses `utils_tail' and `utils_match' to tail a file and try to
+ * match it using several regular expressions. Matches are then passed to
+ * user-provided callback functions or default handlers. This should keep all
+ * of the parsing logic out of the actual plugin, which only operate with
+ * regular expressions.
+ */
+
+#include "utils_match.h"
+
+struct cu_tail_match_s;
+typedef struct cu_tail_match_s cu_tail_match_t;
+
+/*
+ * NAME
+ * tail_match_create
+ *
+ * DESCRIPTION
+ * Allocates, initializes and returns a new `cu_tail_match_t' object.
+ *
+ * PARAMETERS
+ * `filename' The name to read data from.
+ *
+ * RETURN VALUE
+ * Returns NULL upon failure, non-NULL otherwise.
+ */
+cu_tail_match_t *tail_match_create (const char *filename);
+
+/*
+ * NAME
+ * tail_match_destroy
+ *
+ * DESCRIPTION
+ * Releases resources used by the `cu_tail_match_t' object.
+ *
+ * PARAMETERS
+ * The object to destroy.
+ */
+void tail_match_destroy (cu_tail_match_t *obj);
+
+/*
+ * NAME
+ * tail_match_add_match
+ *
+ * DESCRIPTION
+ * Adds a match, in form of a `cu_match_t' object, to the object.
+ * After data has been read from the logfile (using utils_tail) the callback
+ * function `submit_match' is called with the match object and the user
+ * supplied data.
+ * Please note that his function is called regardless whether this match
+ * matched any lines recently or not.
+ * When `tail_match_destroy' is called the `user_data' pointer is freed using
+ * the `free_user_data' callback - if it is not NULL.
+ * When using this interface the `tail_match' module doesn't dispatch any values
+ * itself - all that has to happen in either the match-callbacks or the
+ * submit_match callback.
+ *
+ * RETURN VALUE
+ * Zero upon success, non-zero otherwise.
+ */
+int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
+ int (*submit_match) (cu_match_t *match, void *user_data),
+ void *user_data,
+ void (*free_user_data) (void *user_data));
+
+/*
+ * NAME
+ * tail_match_add_match_simple
+ *
+ * DESCRIPTION
+ * A simplified version of `tail_match_add_match'. The regular expressen `regex'
+ * must match a number, which is then dispatched according to `ds_type'. See
+ * the `match_create_simple' function in utils_match.h for a description how
+ * this flag effects calculation of a new value.
+ * The values gathered are dispatched by the tail_match module in this case. The
+ * passed `plugin', `plugin_instance', `type', and `type_instance' are
+ * directly used when submitting these values.
+ *
+ * RETURN VALUE
+ * Zero upon success, non-zero otherwise.
+ */
+int tail_match_add_match_simple (cu_tail_match_t *obj,
+ const char *regex, int ds_type,
+ const char *plugin, const char *plugin_instance,
+ const char *type, const char *type_instance);
+
+/*
+ * NAME
+ * tail_match_read
+ *
+ * DESCRIPTION
+ * This function should be called periodically by plugins. It reads new lines
+ * from the logfile using `utils_tail' and tries to match them using all
+ * added `utils_match' objects.
+ * After all lines have been read and processed, the submit_match callback is
+ * called or, in case of tail_match_add_match_simple, the data is dispatched to
+ * the daemon directly.
+ *
+ * RETURN VALUE
+ * Zero on success, nonzero on failure.
+*/
+int tail_match_read (cu_tail_match_t *obj);
+
+/* vim: set sw=2 sts=2 ts=8 : */
char plugin_instance[DATA_MAX_NAME_LEN];
char type[DATA_MAX_NAME_LEN];
char type_instance[DATA_MAX_NAME_LEN];
+ char data_source[DATA_MAX_NAME_LEN];
gauge_t warning_min;
gauge_t warning_max;
gauge_t failure_min;
gauge_t failure_max;
int flags;
+ struct threshold_s *next;
} threshold_t;
/* }}} */
* The following functions add, delete, search, etc. configured thresholds to
* the underlying AVL trees.
* {{{ */
+static threshold_t *threshold_get (const char *hostname,
+ const char *plugin, const char *plugin_instance,
+ const char *type, const char *type_instance)
+{
+ char name[6 * DATA_MAX_NAME_LEN];
+ threshold_t *th = NULL;
+
+ format_name (name, sizeof (name),
+ (hostname == NULL) ? "" : hostname,
+ (plugin == NULL) ? "" : plugin, plugin_instance,
+ (type == NULL) ? "" : type, type_instance);
+ name[sizeof (name) - 1] = '\0';
+
+ if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
+ return (th);
+ else
+ return (NULL);
+} /* threshold_t *threshold_get */
+
static int ut_threshold_add (const threshold_t *th)
{
char name[6 * DATA_MAX_NAME_LEN];
char *name_copy;
threshold_t *th_copy;
+ threshold_t *th_ptr;
int status = 0;
if (format_name (name, sizeof (name), th->host,
return (-1);
}
memcpy (th_copy, th, sizeof (threshold_t));
+ th_ptr = NULL;
DEBUG ("ut_threshold_add: Adding entry `%s'", name);
pthread_mutex_lock (&threshold_lock);
- status = c_avl_insert (threshold_tree, name_copy, th_copy);
+
+ th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
+ th->type, th->type_instance);
+
+ while ((th_ptr != NULL) && (th_ptr->next != NULL))
+ th_ptr = th_ptr->next;
+
+ if (th_ptr == NULL) /* no such threshold yet */
+ {
+ status = c_avl_insert (threshold_tree, name_copy, th_copy);
+ }
+ else /* th_ptr points to the last threshold in the list */
+ {
+ th_ptr->next = th_copy;
+ /* name_copy isn't needed */
+ sfree (name_copy);
+ }
+
pthread_mutex_unlock (&threshold_lock);
if (status != 0)
* The following approximately two hundred functions are used to handle the
* configuration and fill the threshold list.
* {{{ */
+static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci)
+{
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("threshold values: The `DataSource' option needs exactly one "
+ "string argument.");
+ return (-1);
+ }
+
+ sstrncpy (th->data_source, ci->values[0].value.string,
+ sizeof (th->data_source));
+
+ return (0);
+} /* int ut_config_type_datasource */
+
static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
{
if ((ci->values_num != 1)
if (strcasecmp ("Instance", option->key) == 0)
status = ut_config_type_instance (&th, option);
+ else if (strcasecmp ("DataSource", option->key) == 0)
+ status = ut_config_type_datasource (&th, option);
else if ((strcasecmp ("WarningMax", option->key) == 0)
|| (strcasecmp ("FailureMax", option->key) == 0))
status = ut_config_type_max (&th, option);
*/
/* }}} */
-static threshold_t *threshold_get (const char *hostname,
- const char *plugin, const char *plugin_instance,
- const char *type, const char *type_instance)
-{
- char name[6 * DATA_MAX_NAME_LEN];
- threshold_t *th = NULL;
-
- format_name (name, sizeof (name),
- (hostname == NULL) ? "" : hostname,
- (plugin == NULL) ? "" : plugin, plugin_instance,
- (type == NULL) ? "" : type, type_instance);
- name[sizeof (name) - 1] = '\0';
-
- if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
- return (th);
- else
- return (NULL);
-} /* threshold_t *threshold_get */
-
static threshold_t *threshold_search (const data_set_t *ds,
const value_list_t *vl)
{
return (NULL);
} /* threshold_t *threshold_search */
-int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
-{
+/*
+ * int ut_report_state
+ *
+ * Checks if the `state' differs from the old state and creates a notification
+ * if appropriate.
+ * Does not fail.
+ */
+static int ut_report_state (const data_set_t *ds,
+ const value_list_t *vl,
+ const threshold_t *th,
+ const gauge_t *values,
+ int ds_index,
+ int state)
+{ /* {{{ */
+ int state_old;
notification_t n;
- threshold_t *th;
- gauge_t *values;
- int i;
-
- int state_orig;
- int state_new = STATE_OKAY;
- int ds_index = 0;
char *buf;
size_t bufsize;
- int status;
-
- if (threshold_tree == NULL)
- return (0);
- /* Is this lock really necessary? So far, thresholds are only inserted at
- * startup. -octo */
- pthread_mutex_lock (&threshold_lock);
- th = threshold_search (ds, vl);
- pthread_mutex_unlock (&threshold_lock);
- if (th == NULL)
- return (0);
-
- DEBUG ("ut_check_threshold: Found matching threshold");
-
- values = uc_get_rate (ds, vl);
- if (values == NULL)
- return (0);
+ int status;
- state_orig = uc_get_state (ds, vl);
+ state_old = uc_get_state (ds, vl);
- for (i = 0; i < ds->ds_num; i++)
+ /* If the state didn't change, only report if `persistent' is specified and
+ * the state is not `okay'. */
+ if (state == state_old)
{
- int is_inverted = 0;
- int is_warning = 0;
- int is_failure = 0;
-
- if ((th->flags & UT_FLAG_INVERT) != 0)
- {
- is_inverted = 1;
- is_warning--;
- is_failure--;
- }
- if ((!isnan (th->failure_min) && (th->failure_min > values[i]))
- || (!isnan (th->failure_max) && (th->failure_max < values[i])))
- is_failure++;
- if ((!isnan (th->warning_min) && (th->warning_min > values[i]))
- || (!isnan (th->warning_max) && (th->warning_max < values[i])))
- is_warning++;
-
- if ((is_failure != 0) && (state_new != STATE_ERROR))
- {
- state_new = STATE_ERROR;
- ds_index = i;
- }
- else if ((is_warning != 0)
- && (state_new != STATE_ERROR)
- && (state_new != STATE_WARNING))
- {
- state_new = STATE_WARNING;
- ds_index = i;
- }
+ if ((th->flags & UT_FLAG_PERSIST) == 0)
+ return (0);
+ else if (state == STATE_OKAY)
+ return (0);
}
- if (state_new != state_orig)
- uc_set_state (ds, vl, state_new);
-
- /* Return here if we're not going to send a notification */
- if ((state_new == state_orig)
- && ((state_new == STATE_OKAY)
- || ((th->flags & UT_FLAG_PERSIST) == 0)))
- {
- sfree (values);
- return (0);
- }
+ if (state != state_old)
+ uc_set_state (ds, vl, state);
NOTIFICATION_INIT_VL (&n, vl, ds);
- {
- /* Copy the associative members */
- if (state_new == STATE_OKAY)
- n.severity = NOTIF_OKAY;
- else if (state_new == STATE_WARNING)
- n.severity = NOTIF_WARNING;
- else
- n.severity = NOTIF_FAILURE;
- n.time = vl->time;
+ buf = n.message;
+ bufsize = sizeof (n.message);
+
+ if (state == STATE_OKAY)
+ n.severity = NOTIF_OKAY;
+ else if (state == STATE_WARNING)
+ n.severity = NOTIF_WARNING;
+ else
+ n.severity = NOTIF_FAILURE;
+
+ n.time = vl->time;
- buf = n.message;
- bufsize = sizeof (n.message);
+ status = snprintf (buf, bufsize, "Host %s, plugin %s",
+ vl->host, vl->plugin);
+ buf += status;
+ bufsize -= status;
- status = snprintf (buf, bufsize, "Host %s, plugin %s",
- vl->host, vl->plugin);
+ if (vl->plugin_instance[0] != '\0')
+ {
+ status = snprintf (buf, bufsize, " (instance %s)",
+ vl->plugin_instance);
buf += status;
bufsize -= status;
+ }
- if (vl->plugin_instance[0] != '\0')
- {
- status = snprintf (buf, bufsize, " (instance %s)",
- vl->plugin_instance);
- buf += status;
- bufsize -= status;
- }
+ status = snprintf (buf, bufsize, " type %s", ds->type);
+ buf += status;
+ bufsize -= status;
- status = snprintf (buf, bufsize, " type %s", ds->type);
+ if (vl->type_instance[0] != '\0')
+ {
+ status = snprintf (buf, bufsize, " (instance %s)",
+ vl->type_instance);
buf += status;
bufsize -= status;
-
- if (vl->type_instance[0] != '\0')
- {
- status = snprintf (buf, bufsize, " (instance %s)",
- vl->type_instance);
- buf += status;
- bufsize -= status;
- }
}
- /* Send a okay notification */
- if (state_new == STATE_OKAY)
+ /* Send an okay notification */
+ if (state == STATE_OKAY)
{
status = snprintf (buf, bufsize, ": All data sources are within range again.");
buf += status;
double min;
double max;
- min = (state_new == STATE_ERROR) ? th->failure_min : th->warning_min;
- max = (state_new == STATE_ERROR) ? th->failure_max : th->warning_max;
+ min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
+ max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
if (th->flags & UT_FLAG_INVERT)
{
status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
"%f. That is within the %s region of %f and %f.",
ds->ds[ds_index].name, values[ds_index],
- (state_new == STATE_ERROR) ? "failure" : "warning",
- min, min);
+ (state == STATE_ERROR) ? "failure" : "warning",
+ min, max);
}
else
{
"%f. That is %s the %s threshold of %f.",
ds->ds[ds_index].name, values[ds_index],
isnan (min) ? "below" : "above",
- (state_new == STATE_ERROR) ? "failure" : "warning",
+ (state == STATE_ERROR) ? "failure" : "warning",
isnan (min) ? max : min);
}
}
"%f. That is %s the %s threshold of %f.",
ds->ds[ds_index].name, values[ds_index],
(values[ds_index] < min) ? "below" : "above",
- (state_new == STATE_ERROR) ? "failure" : "warning",
+ (state == STATE_ERROR) ? "failure" : "warning",
(values[ds_index] < min) ? min : max);
}
buf += status;
plugin_dispatch_notification (&n);
+ return (0);
+} /* }}} int ut_report_state */
+
+/*
+ * int ut_check_one_data_source
+ *
+ * Checks one data source against the given threshold configuration. If the
+ * `DataSource' option is set in the threshold, and the name does NOT match,
+ * `okay' is returned. If the threshold does match, its failure and warning
+ * min and max values are checked and `failure' or `warning' is returned if
+ * appropriate.
+ * Does not fail.
+ */
+static int ut_check_one_data_source (const data_set_t *ds,
+ const value_list_t *vl,
+ const threshold_t *th,
+ const gauge_t *values,
+ int ds_index)
+{ /* {{{ */
+ const char *ds_name;
+ int is_warning = 0;
+ int is_failure = 0;
+
+ /* check if this threshold applies to this data source */
+ ds_name = ds->ds[ds_index].name;
+ if ((th->data_source[0] != 0)
+ && (strcmp (ds_name, th->data_source) != 0))
+ return (STATE_OKAY);
+
+ if ((th->flags & UT_FLAG_INVERT) != 0)
+ {
+ is_warning--;
+ is_failure--;
+ }
+
+ if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
+ || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
+ is_failure++;
+ if (is_failure != 0)
+ return (STATE_ERROR);
+
+ if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
+ || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
+ is_warning++;
+ if (is_warning != 0)
+ return (STATE_WARNING);
+
+ return (STATE_OKAY);
+} /* }}} int ut_check_one_data_source */
+
+/*
+ * int ut_check_one_threshold
+ *
+ * Checks all data sources of a value list against the given threshold, using
+ * the ut_check_one_data_source function above. Returns the worst status,
+ * which is `okay' if nothing has failed.
+ * Returns less than zero if the data set doesn't have any data sources.
+ */
+static int ut_check_one_threshold (const data_set_t *ds,
+ const value_list_t *vl,
+ const threshold_t *th,
+ const gauge_t *values,
+ int *ret_ds_index)
+{ /* {{{ */
+ int ret = -1;
+ int ds_index = -1;
+ int i;
+
+ for (i = 0; i < ds->ds_num; i++)
+ {
+ int status;
+
+ status = ut_check_one_data_source (ds, vl, th, values, i);
+ if (ret < status)
+ {
+ ret = status;
+ ds_index = i;
+ }
+ } /* for (ds->ds_num) */
+
+ if (ret_ds_index != NULL)
+ *ret_ds_index = ds_index;
+
+ return (ret);
+} /* }}} int ut_check_one_threshold */
+
+/*
+ * int ut_check_threshold (PUBLIC)
+ *
+ * Gets a list of matching thresholds and searches for the worst status by one
+ * of the thresholds. Then reports that status using the ut_report_state
+ * function above.
+ * Returns zero on success and if no threshold has been configured. Returns
+ * less than zero on failure.
+ */
+int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
+{ /* {{{ */
+ threshold_t *th;
+ gauge_t *values;
+ int status;
+
+ int worst_state = -1;
+ threshold_t *worst_th = NULL;
+ int worst_ds_index = -1;
+
+ if (threshold_tree == NULL)
+ return (0);
+
+ /* Is this lock really necessary? So far, thresholds are only inserted at
+ * startup. -octo */
+ pthread_mutex_lock (&threshold_lock);
+ th = threshold_search (ds, vl);
+ pthread_mutex_unlock (&threshold_lock);
+ if (th == NULL)
+ return (0);
+
+ DEBUG ("ut_check_threshold: Found matching threshold(s)");
+
+ values = uc_get_rate (ds, vl);
+ if (values == NULL)
+ return (0);
+
+ while (th != NULL)
+ {
+ int ds_index = -1;
+
+ status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
+ if (status < 0)
+ {
+ ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
+ sfree (values);
+ return (-1);
+ }
+
+ if (worst_state < status)
+ {
+ worst_state = status;
+ worst_th = th;
+ worst_ds_index = ds_index;
+ }
+
+ th = th->next;
+ } /* while (th) */
+
+ status = ut_report_state (ds, vl, worst_th, values,
+ worst_ds_index, worst_state);
+ if (status != 0)
+ {
+ ERROR ("ut_check_threshold: ut_report_state failed.");
+ sfree (values);
+ return (-1);
+ }
+
sfree (values);
return (0);
-} /* int ut_check_threshold */
+} /* }}} int ut_check_threshold */
int ut_check_interesting (const char *name)
{
--- /dev/null
+/**
+ * collectd - src/vmem.c
+ * 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:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if KERNEL_LINUX
+static const char *config_keys[] =
+{
+ "Verbose"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static int verbose_output = 0;
+/* #endif KERNEL_LINUX */
+
+#else
+# error "No applicable input method."
+#endif /* HAVE_LIBSTATGRAB */
+
+static void submit (const char *plugin_instance, const char *type,
+ const char *type_instance, value_t *values, int values_len)
+{
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = values;
+ vl.values_len = values_len;
+
+ vl.time = time (NULL);
+ strcpy (vl.host, hostname_g);
+ strcpy (vl.plugin, "vmem");
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+ if (type_instance != NULL)
+ sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (type, &vl);
+} /* void vmem_submit */
+
+static void submit_two (const char *plugin_instance, const char *type,
+ const char *type_instance, counter_t c0, counter_t c1)
+{
+ value_t values[2];
+
+ values[0].counter = c0;
+ values[1].counter = c1;
+
+ submit (plugin_instance, type, type_instance, values, 2);
+} /* void submit_one */
+
+static void submit_one (const char *plugin_instance, const char *type,
+ const char *type_instance, value_t value)
+{
+ submit (plugin_instance, type, type_instance, &value, 1);
+} /* void submit_one */
+
+static int vmem_config (const char *key, const char *value)
+{
+ if (strcasecmp ("Verbose", key) == 0)
+ {
+ if ((strcasecmp ("true", value) == 0)
+ || (strcasecmp ("yes", value) == 0)
+ || (strcasecmp ("on", value) == 0))
+ verbose_output = 1;
+ else
+ verbose_output = 0;
+ }
+ else
+ {
+ return (-1);
+ }
+
+ return (0);
+} /* int vmem_config */
+
+static int vmem_read (void)
+{
+#if KERNEL_LINUX
+ counter_t pgpgin = 0;
+ counter_t pgpgout = 0;
+ int pgpgvalid = 0;
+
+ counter_t pswpin = 0;
+ counter_t pswpout = 0;
+ int pswpvalid = 0;
+
+ counter_t pgfault = 0;
+ counter_t pgmajfault = 0;
+ int pgfaultvalid = 0;
+
+ FILE *fh;
+ char buffer[1024];
+
+ fh = fopen ("/proc/vmstat", "r");
+ if (fh == NULL)
+ {
+ char errbuf[1024];
+ ERROR ("vmem plugin: fopen (/proc/vmstat) failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+
+ while (fgets (buffer, sizeof (buffer), fh) != NULL)
+ {
+ char *fields[4];
+ int fields_num;
+ char *key;
+ char *endptr;
+ counter_t counter;
+ gauge_t gauge;
+
+ fields_num = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+ if (fields_num != 2)
+ continue;
+
+ key = fields[0];
+
+ endptr = NULL;
+ counter = strtoll (fields[1], &endptr, 10);
+ if (fields[1] == endptr)
+ continue;
+
+ endptr = NULL;
+ gauge = strtod (fields[1], &endptr);
+ if (fields[1] == endptr)
+ continue;
+
+ /*
+ * Number of pages
+ *
+ * The total number of {inst} pages, e. g dirty pages.
+ */
+ if (strncmp ("nr_", key, strlen ("nr_")) == 0)
+ {
+ char *inst = key + strlen ("nr_");
+ value_t value = { .gauge = gauge };
+ submit_one (NULL, "vmpage_number", inst, value);
+ }
+
+ /*
+ * Page in and page outs. For memory and swap.
+ */
+ else if (strcmp ("pgpgin", key) == 0)
+ {
+ pgpgin = counter;
+ pgpgvalid |= 0x01;
+ }
+ else if (strcmp ("pgpgout", key) == 0)
+ {
+ pgpgout = counter;
+ pgpgvalid |= 0x02;
+ }
+ else if (strcmp ("pswpin", key) == 0)
+ {
+ pswpin = counter;
+ pswpvalid |= 0x01;
+ }
+ else if (strcmp ("pswpout", key) == 0)
+ {
+ pswpout = counter;
+ pswpvalid |= 0x02;
+ }
+
+ /*
+ * Pagefaults
+ */
+ else if (strcmp ("pgfault", key) == 0)
+ {
+ pgfault = counter;
+ pgfaultvalid |= 0x01;
+ }
+ else if (strcmp ("pgmajfault", key) == 0)
+ {
+ pgmajfault = counter;
+ pgfaultvalid |= 0x02;
+ }
+
+ /*
+ * Skip the other statistics if verbose output is disabled.
+ */
+ else if (verbose_output == 0)
+ continue;
+
+ /*
+ * Number of page allocations, refills, steals and scans. This is collected
+ * ``per zone'', i. e. for DMA, DMA32, normal and possibly highmem.
+ */
+ else if (strncmp ("pgalloc_", key, strlen ("pgalloc_")) == 0)
+ {
+ char *inst = key + strlen ("pgalloc_");
+ value_t value = { .counter = counter };
+ submit_one (inst, "vmpage_action", "alloc", value);
+ }
+ else if (strncmp ("pgrefill_", key, strlen ("pgrefill_")) == 0)
+ {
+ char *inst = key + strlen ("pgrefill_");
+ value_t value = { .counter = counter };
+ submit_one (inst, "vmpage_action", "refill", value);
+ }
+ else if (strncmp ("pgsteal_", key, strlen ("pgsteal_")) == 0)
+ {
+ char *inst = key + strlen ("pgsteal_");
+ value_t value = { .counter = counter };
+ submit_one (inst, "vmpage_action", "steal", value);
+ }
+ else if (strncmp ("pgscan_kswapd_", key, strlen ("pgscan_kswapd_")) == 0)
+ {
+ char *inst = key + strlen ("pgscan_kswapd_");
+ value_t value = { .counter = counter };
+ submit_one (inst, "vmpage_action", "scan_kswapd", value);
+ }
+ else if (strncmp ("pgscan_direct_", key, strlen ("pgscan_direct_")) == 0)
+ {
+ char *inst = key + strlen ("pgscan_direct_");
+ value_t value = { .counter = counter };
+ submit_one (inst, "vmpage_action", "scan_direct", value);
+ }
+
+ /*
+ * Page action
+ *
+ * number of pages moved to the active or inactive lists and freed, i. e.
+ * removed from either list.
+ */
+ else if (strcmp ("pgfree", key) == 0)
+ {
+ value_t value = { .counter = counter };
+ submit_one (NULL, "vmpage_action", "free", value);
+ }
+ else if (strcmp ("pgactivate", key) == 0)
+ {
+ value_t value = { .counter = counter };
+ submit_one (NULL, "vmpage_action", "activate", value);
+ }
+ else if (strcmp ("pgdeactivate", key) == 0)
+ {
+ value_t value = { .counter = counter };
+ submit_one (NULL, "vmpage_action", "deactivate", value);
+ }
+ } /* while (fgets) */
+
+ fclose (fh);
+ fh = NULL;
+
+ if (pgfaultvalid == 0x03)
+ submit_two (NULL, "vmpage_faults", NULL, pgfault, pgmajfault);
+
+ if (pgpgvalid == 0x03)
+ submit_two (NULL, "vmpage_io", "memory", pgpgin, pgpgout);
+
+ if (pswpvalid == 0x03)
+ submit_two (NULL, "vmpage_io", "swap", pswpin, pswpout);
+#endif /* KERNEL_LINUX */
+
+ return (0);
+} /* int vmem_read */
+
+void module_register (void)
+{
+ plugin_register_config ("vmem", vmem_config,
+ config_keys, config_keys_num);
+ plugin_register_read ("vmem", vmem_read);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 ts=8 : */
#!/bin/sh
-DEFAULT_VERSION="4.3.3.git"
+DEFAULT_VERSION="4.4.0.git"
VERSION="$( git describe 2> /dev/null | sed -e 's/^collectd-//' )"