Code

Merge branch 'sr/pf'
authorFlorian Forster <octo@collectd.org>
Sat, 17 Nov 2012 08:42:55 +0000 (09:42 +0100)
committerFlorian Forster <octo@collectd.org>
Sat, 17 Nov 2012 08:42:55 +0000 (09:42 +0100)
89 files changed:
ChangeLog
Makefile.am
bindings/Makefile.am
bindings/java/org/collectd/java/GenericJMXConfConnection.java
bindings/perl/lib/Collectd.pm
bindings/perl/lib/Collectd/Plugins/OpenVZ.pm
configure.in
contrib/collectd_network.py
contrib/collection3/bin/index.cgi
contrib/postgresql/collectd_insert.sql [new file with mode: 0644]
src/Makefile.am
src/aggregation.c [new file with mode: 0644]
src/amqp.c
src/apcups.c
src/collectd-nagios.c
src/collectd-perl.pod
src/collectd-python.pod
src/collectd-tg.c [new file with mode: 0644]
src/collectd.c
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.h
src/collectdctl.c
src/common.c
src/common.h
src/configfile.c
src/configfile.h
src/contextswitch.c
src/cpu.c
src/curl_xml.c
src/disk.c
src/dns.c
src/email.c
src/ethstat.c
src/exec.c
src/gmond.c
src/ipmi.c
src/libcollectdclient/Makefile.am
src/libcollectdclient/client.c
src/libcollectdclient/client.h [deleted file]
src/libcollectdclient/collectd/client.h [new file with mode: 0644]
src/libcollectdclient/collectd/lcc_features.h.in [new file with mode: 0644]
src/libcollectdclient/collectd/network.h [new file with mode: 0644]
src/libcollectdclient/collectd/network_buffer.h [new file with mode: 0644]
src/libcollectdclient/lcc_features.h.in [deleted file]
src/libcollectdclient/network.c [new file with mode: 0644]
src/libcollectdclient/network_buffer.c [new file with mode: 0644]
src/liboconfig/parser.y
src/libvirt.c
src/meta_data.c
src/modbus.c
src/netlink.c
src/network.c
src/ntpd.c
src/perl.c
src/pinba.c
src/ping.c
src/plugin.c
src/plugin.h
src/postgresql.c
src/postgresql_default.conf
src/powerdns.c
src/processes.c
src/python.c
src/pyvalues.c
src/rrdcached.c
src/rrdtool.c
src/snmp.c
src/swap.c
src/tcpconns.c
src/unixsock.c
src/uptime.c
src/utils_avltree.c
src/utils_cache.c
src/utils_cmd_putval.c
src/utils_complain.c
src/utils_db_query.c
src/utils_db_query.h
src/utils_fbhash.c
src/utils_format_graphite.c
src/utils_format_graphite.h
src/utils_time.c
src/utils_time.h
src/utils_vl_lookup.c [new file with mode: 0644]
src/utils_vl_lookup.h [new file with mode: 0644]
src/utils_vl_lookup_test.c [new file with mode: 0644]
src/write_graphite.c
src/zfs_arc.c
version-gen.sh

index 4b90b1525ccf1a4d5f21e724d0771f28c6da5fa8..ef2023a9bba9b828ac699c9c7a8a732a9930443d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,62 @@
+2012-11-11, Version 5.1.1
+       * collectd: Create new directories with mode 0777 and let umask remove
+         unwanted permission bits.
+       * collectd: Build issues have been fixed.
+       * collectd: An incorrect assertion has been fixed in some common code
+         for Solaris. This should resolve pseudo-random assertion failures
+         under Solaris. Thanks to Jeff Blane for his help debugging this.
+       * collectd: A couple of memory leaks through PThread thread attributes
+         have been fixed. Thanks to Gerrie Roos for fixing these.
+       * collectdctl: Fix PUTVAL for data sets with multiple data sources.
+         Thanks to Cyril Feraudet for reporting this problem.
+       * contrib/migrate-4-5.px: Handle to "df" to "df_complex" conversion
+         correctly.
+       * apcups plugin: Improve the reconnect behavior.
+       * curl_xml plugin: The "Host" setting was silently ignored. Thanks to
+         Fabien Wernli for fixing this.
+       * df plugin: Ignore "rootfs" devices under Linux to avoid having them
+         reported twice. Thanks to Brune Prémont for fixing this.
+       * disk plugin: Fix incorrect computation of read and write latency (the
+         "disk_time" type). Previously, the numbers reported where too small
+         by a factor of "interval", e.g. when the interval is set to 10
+         seconds, the values were too low by a factor of 10. Thanks to Manuel
+         Sanmartin for reporting this problem.
+       * dns plugin: A build issue under Solaris has been fixed. A erroneous
+         define that could lead to the reporting of bad data has been fixed by
+         Daniel Sutto.
+       * ethstat plugin: An off-by-one error and potential use of
+         uninitialized memory has been fixed. Thanks to Mark Voelker for
+         reporting these problems.
+       * memcachec plugin: A bug in the configuration handling has been fixed.
+         Thanks to Pascal Hofmann for fixing this issue.
+       * mysql plugin: Fix a bug when registering multiple databases. Thanks
+         to Sebastian Harl for fixing this.
+       * netapp plugin: Correctly close the connection on communication
+         errors.
+       * netlink plugin: The function used to query statistics has been
+         changed to be more in line with iproute2's behavior. Thanks to
+         "KIvosak" for the patch.
+       * network plugin: Initialization of libgcrypt has been fixed. Thanks to
+         Chris Lundquist for his patch.
+       * oracle plugin: Error messages have been improved.
+       * ping plugin: Don't enter the exponential back-off mode when
+         ping_send() fails. This should make recovery after a network failure
+         much faster.
+       * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert
+         and Sven Trenkel for fixing this.
+       * redis plugin: Fix a compilation problem on FreeBSD. Thanks to
+         "biancalana" for the fix.
+       * rrdtool plugin: Fix an out-of-bounds array access when printing a
+         warning message. Thanks to Will Hawkins for fixing this bug.
+       * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been
+         added. Support for more complex / unusual MIBs / subtrees has been
+         added. Thanks to Mark Juric to test the changes and point out these
+         problems.
+       * varnish plugin: Support for multiple instances of Varnish 3 has been
+         fixed. Thanks to Jonathan Huot for the patch.
+       * write_mongodb plugin: Add compatibility with libmongo 0.6.0 and
+         later. Thanks to Chris Lundquist for this patch.
+
 2012-04-01, Version 5.1.0
        * Build system, iptables plugin: The shipped version of libiptc has
          been removed.
        * scale target: Support for scaling specific data sources only has been
          added. Thanks to Gerrie Roos for his patch.
 
+2012-11-11, Version 5.0.5
+       * collectd: Create new directories with mode 0777 and let umask remove
+         unwanted permission bits.
+       * collectd: Build issues have been fixed.
+       * collectd: An incorrect assertion has been fixed in some common code
+         for Solaris. This should resolve pseudo-random assertion failures
+         under Solaris. Thanks to Jeff Blane for his help debugging this.
+       * collectd: A couple of memory leaks through PThread thread attributes
+         have been fixed. Thanks to Gerrie Roos for fixing these.
+       * collectdctl: Fix PUTVAL for data sets with multiple data sources.
+         Thanks to Cyril Feraudet for reporting this problem.
+       * contrib/migrate-4-5.px: Handle to "df" to "df_complex" conversion
+         correctly.
+       * apcups plugin: Improve the reconnect behavior.
+       * curl_xml plugin: The "Host" setting was silently ignored. Thanks to
+         Fabien Wernli for fixing this.
+       * df plugin: Ignore "rootfs" devices under Linux to avoid having them
+         reported twice. Thanks to Brune Prémont for fixing this.
+       * disk plugin: Fix incorrect computation of read and write latency (the
+         "disk_time" type). Previously, the numbers reported where too small
+         by a factor of "interval", e.g. when the interval is set to 10
+         seconds, the values were too low by a factor of 10. Thanks to Manuel
+         Sanmartin for reporting this problem.
+       * dns plugin: A build issue under Solaris has been fixed. A erroneous
+         define that could lead to the reporting of bad data has been fixed by
+         Daniel Sutto.
+       * memcachec plugin: A bug in the configuration handling has been fixed.
+         Thanks to Pascal Hofmann for fixing this issue.
+       * mysql plugin: Fix a bug when registering multiple databases. Thanks
+         to Sebastian Harl for fixing this.
+       * netapp plugin: Correctly close the connection on communication
+         errors.
+       * netlink plugin: The function used to query statistics has been
+         changed to be more in line with iproute2's behavior. Thanks to
+         "KIvosak" for the patch.
+       * network plugin: Initialization of libgcrypt has been fixed. Thanks to
+         Chris Lundquist for his patch.
+       * oracle plugin: Error messages have been improved.
+       * ping plugin: Don't enter the exponential back-off mode when
+         ping_send() fails. This should make recovery after a network failure
+         much faster.
+       * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert
+         and Sven Trenkel for fixing this.
+       * redis plugin: Fix a compilation problem on FreeBSD. Thanks to
+         "biancalana" for the fix.
+       * rrdtool plugin: Fix an out-of-bounds array access when printing a
+         warning message. Thanks to Will Hawkins for fixing this bug.
+       * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been
+         added. Support for more complex / unusual MIBs / subtrees has been
+         added. Thanks to Mark Juric to test the changes and point out these
+         problems.
 
 2012-04-01, Version 5.0.4
        * Build system: Fix the use of a libltdl macro. Thanks to Clemens Lang
        * v5upgrade target: Target for converting v4 data sets to the v5
          schema.
 
+2012-11-11, Version 4.10.8
+       * collectd: Create new directories with mode 0777 and let umask remove
+         unwanted permission bits.
+       * collectd: Build issues have been fixed.
+       * collectd: An incorrect assertion has been fixed in some common code
+         for Solaris. This should resolve pseudo-random assertion failures
+         under Solaris. Thanks to Jeff Blane for his help debugging this.
+       * collectd: A couple of memory leaks through PThread thread attributes
+         have been fixed. Thanks to Gerrie Roos for fixing these.
+       * apcups plugin: Improve the reconnect behavior.
+       * df plugin: Ignore "rootfs" devices under Linux to avoid having them
+         reported twice. Thanks to Brune Prémont for fixing this.
+       * disk plugin: Fix incorrect computation of read and write latency (the
+         "disk_time" type). Previously, the numbers reported where too small
+         by a factor of "interval", e.g. when the interval is set to 10
+         seconds, the values were too low by a factor of 10. Thanks to Manuel
+         Sanmartin for reporting this problem.
+       * dns plugin: A build issue under Solaris has been fixed. A erroneous
+         define that could lead to the reporting of bad data has been fixed by
+         Daniel Sutto.
+       * memcachec plugin: A bug in the configuration handling has been fixed.
+         Thanks to Pascal Hofmann for fixing this issue.
+       * netapp plugin: Correctly close the connection on communication
+         errors.
+       * netlink plugin: The function used to query statistics has been
+         changed to be more in line with iproute2's behavior. Thanks to
+         "KIvosak" for the patch.
+       * network plugin: Initialization of libgcrypt has been fixed. Thanks to
+         Chris Lundquist for his patch.
+       * oracle plugin: Error messages have been improved.
+       * ping plugin: Don't enter the exponential back-off mode when
+         ping_send() fails. This should make recovery after a network failure
+         much faster.
+       * python plugin: Memory leaks have been fixed. Thanks to Tommie Gannert
+         and Sven Trenkel for fixing this.
+       * rrdtool plugin: Fix an out-of-bounds array access when printing a
+         warning message. Thanks to Will Hawkins for fixing this bug.
+       * snmp plugin: Support for the SNMP_ENDOFMIBVIEW return value has been
+         added. Support for more complex / unusual MIBs / subtrees has been
+         added. Thanks to Mark Juric to test the changes and point out these
+         problems.
+
 2012-04-01, Version 4.10.7
        * Build system: Fix the use of a libltdl macro. Thanks to Clemens Lang
          for fixing this. Adresses some issues with building the iptables
index 9e3feac406f152d79e8a0df5ff3fc47367137012..52671235e52289d3c1d67088e61e0606e0a5260a 100644 (file)
@@ -1,6 +1,6 @@
 ACLOCAL_AMFLAGS = -I libltdl/m4
 
-SUBDIRS = libltdl src bindings
+SUBDIRS = libltdl src bindings .
 
 INCLUDES = $(LTDLINCL)
 
@@ -10,3 +10,8 @@ install-exec-hook:
        $(mkinstalldirs) $(DESTDIR)$(localstatedir)/run
        $(mkinstalldirs) $(DESTDIR)$(localstatedir)/lib/$(PACKAGE_NAME)
        $(mkinstalldirs) $(DESTDIR)$(localstatedir)/log
+
+maintainer-clean-local:
+       -rm -f -r libltdl
+       -rm -f INSTALL
+       -rm -f aclocal.m4
index f39e9bbb2778b60a47c138a4ea3ee8f02563126d..3876cc2c06d3f6ae70b08243cd18343b1c3f5a6f 100644 (file)
@@ -10,28 +10,55 @@ EXTRA_DIST = perl/Makefile.PL \
             perl/lib/Collectd/Plugins/Monitorus.pm \
             perl/lib/Collectd/Plugins/OpenVZ.pm
 
+CLEANFILES = \
+       buildperl/Collectd.pm \
+       buildperl/Collectd/Plugins/OpenVZ.pm \
+       buildperl/Collectd/Unixsock.pm \
+       buildperl/Makefile.PL \
+       .perl-directory-stamp
+
+DISTCLEANFILES = \
+       buildperl/Collectd.pm \
+       buildperl/Collectd/Plugins/OpenVZ.pm \
+       buildperl/Collectd/Unixsock.pm \
+       buildperl/Makefile.PL \
+       .perl-directory-stamp
+
 all-local: @PERL_BINDINGS@
 
+
 install-exec-local:
-       [ ! -f perl/Makefile ] || ( cd perl && $(MAKE) install )
+       [ ! -f buildperl/Makefile ] || ( cd buildperl && $(MAKE) install )
+
+# Perl 'make uninstall' does not work as well as wanted.
+# So we do the work here.
+uninstall-local:
+       rm -f $(DESTDIR)$(mandir)/man3/Collectd::Unixsock.3pm
+       rm -f $(DESTDIR)$(datarootdir)/perl5/Collectd.pm
+       rm -f $(DESTDIR)$(datarootdir)/perl5/Collectd/Plugins/OpenVZ.pm
+       rm -f $(DESTDIR)$(datarootdir)/perl5/Collectd/Unixsock.pm
+       rm -f $(DESTDIR)$(prefix)/lib64/perl5/perllocal.pod
+       rm -f $(DESTDIR)$(prefix)/lib64/perl5/auto/Collectd/.packlist
 
 clean-local:
-       [ ! -f perl/Makefile ] || ( cd perl && $(MAKE) realclean )
+       rm -rf buildperl
 
-perl: perl/Makefile
-       cd perl && $(MAKE)
+perl: buildperl/Makefile
+       cd buildperl && $(MAKE)
 
-perl/Makefile: .perl-directory-stamp perl/Makefile.PL \
+buildperl/Makefile: .perl-directory-stamp buildperl/Makefile.PL \
        $(top_builddir)/config.status
-       cd perl && @PERL@ Makefile.PL PREFIX=$(prefix) @PERL_BINDINGS_OPTIONS@
+       cd buildperl && @PERL@ Makefile.PL INSTALL_BASE=$(prefix) @PERL_BINDINGS_OPTIONS@
+
+buildperl/Makefile.PL: .perl-directory-stamp $(top_builddir)/config.status
 
 .perl-directory-stamp:
-       if test ! -d perl; then \
-         mkdir -p perl/Collectd/Plugins; \
-         cp $(srcdir)/perl/Collectd.pm perl/; \
-         cp $(srcdir)/perl/Makefile.PL perl/; \
-         cp $(srcdir)/perl/Collectd/Unixsock.pm perl/Collectd/; \
-         cp $(srcdir)/perl/Collectd/Plugins/OpenVZ.pm perl/Collectd/Plugins/; \
+       if test ! -d buildperl; then \
+         mkdir -p buildperl/Collectd/Plugins; \
+         cp $(srcdir)/perl/lib/Collectd.pm buildperl/; \
+         cp $(srcdir)/perl/Makefile.PL buildperl/; \
+         cp $(srcdir)/perl/lib/Collectd/Unixsock.pm buildperl/Collectd/; \
+         cp $(srcdir)/perl/lib/Collectd/Plugins/OpenVZ.pm buildperl/Collectd/Plugins/; \
        fi
        touch $@
 
index 0c81bc9ad941760f07021530d6663f32ee34b75f..9f062a9c7172ed2493234d23f93b17010fa245fe 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * collectd/java - org/collectd/java/GenericJMXConfConnection.java
- * Copyright (C) 2009,2010  Florian octo Forster
+ * Copyright (C) 2009-2012  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
@@ -16,7 +16,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  */
 
 package org.collectd.java;
@@ -26,6 +26,7 @@ import java.util.Map;
 import java.util.Iterator;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.net.InetAddress;
 
 import javax.management.MBeanServerConnection;
 import javax.management.ObjectName;
@@ -77,6 +78,24 @@ class GenericJMXConfConnection
     return (v.getString ());
   } /* }}} String getConfigString */
 
+  private String getHost () /* {{{ */
+  {
+    if (this._host != null)
+    {
+      return (this._host);
+    }
+
+    try
+    {
+      InetAddress localHost = InetAddress.getLocalHost();
+      return (localHost.getHostName ());
+    }
+    catch (UnknownHostException e)
+    {
+      return ("localhost");
+    }
+  } /* }}} String getHost */
+
 private void connect () /* {{{ */
 {
   JMXServiceURL service_url;
@@ -211,7 +230,7 @@ private void connect () /* {{{ */
         + ((this._host != null) ? this._host : "(null)"));
 
     pd = new PluginData ();
-    pd.setHost ((this._host != null) ? this._host : "localhost");
+    pd.setHost (this.getHost ());
     pd.setPlugin ("GenericJMX");
 
     for (int i = 0; i < this._mbeans.size (); i++)
index ca3b5d2349805a505ae22df4faad8c7b9feda529..c1adf4426212f3c6da5800853be49611a899730c 100644 (file)
@@ -42,6 +42,7 @@ our %EXPORT_TAGS = (
                        plugin_register
                        plugin_unregister
                        plugin_dispatch_values
+                       plugin_get_interval
                        plugin_write
                        plugin_flush
                        plugin_flush_one
@@ -171,6 +172,7 @@ sub plugin_call_all {
        my $type = shift;
 
        my %plugins;
+       my $interval;
 
        our $cb_name = undef;
 
@@ -194,13 +196,15 @@ sub plugin_call_all {
                %plugins = %{$plugins[$type]};
        }
 
+       $interval = plugin_get_interval ();
+
        foreach my $plugin (keys %plugins) {
                my $p = $plugins{$plugin};
 
                my $status = 0;
 
                if ($p->{'wait_left'} > 0) {
-                       $p->{'wait_left'} -= $interval_g;
+                       $p->{'wait_left'} -= $interval;
                }
 
                next if ($p->{'wait_left'} > 0);
@@ -227,11 +231,11 @@ sub plugin_call_all {
 
                if ($status) {
                        $p->{'wait_left'} = 0;
-                       $p->{'wait_time'} = $interval_g;
+                       $p->{'wait_time'} = $interval;
                }
                elsif (TYPE_READ == $type) {
-                       if ($p->{'wait_time'} < $interval_g) {
-                               $p->{'wait_time'} = $interval_g;
+                       if ($p->{'wait_time'} < $interval) {
+                               $p->{'wait_time'} = $interval;
                        }
 
                        $p->{'wait_left'} = $p->{'wait_time'};
@@ -313,7 +317,7 @@ sub plugin_register {
                }
 
                %p = (
-                       wait_time => $interval_g,
+                       wait_time => plugin_get_interval (),
                        wait_left => 0,
                        cb_name   => $data,
                );
index 294415747c616f91f252ef30418c5fa583830336..ea3cee99e16deeedb75b3de3efee0fe1592f15c2 100644 (file)
@@ -36,7 +36,7 @@ my $last_stat = {};
 
 sub openvz_read
 {
-    my %v = (time => time(), interval => $interval_g);
+    my %v = (time => time(), interval => plugin_get_interval());
     my (@veids, $veid, $name, $key, $val, $i, @lines, @parts, @counters);
 
     @veids = map { s/ //g; $_; } split(/\n/, `$vzlist -Ho veid`);
index c5544c1219acf256fd90c48038ed5d3de04c3499..cd24f536c0328fbf0c28307fb9af5e0b76ad7d41 100644 (file)
@@ -1231,6 +1231,7 @@ AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct
                have_struct_kinfo_proc_freebsd="no"
        ],
        [
+AC_INCLUDES_DEFAULT
 #include <kvm.h>
 #include <sys/param.h>
 #include <sys/sysctl.h>
@@ -1247,6 +1248,7 @@ AC_CHECK_MEMBERS([struct kinfo_proc.kp_proc, struct kinfo_proc.kp_eproc],
                have_struct_kinfo_proc_openbsd="no"
        ],
        [
+AC_INCLUDES_DEFAULT
 #include <sys/param.h>
 #include <sys/sysctl.h>
 #include <kvm.h>
@@ -1906,10 +1908,12 @@ fi
 # This could be in iptc or ip4tc
 if test "x$with_libiptc" = "xpkgconfig"
 then
+       SAVE_LIBS="$LIBS"
        AC_SEARCH_LIBS(iptc_init, [iptc ip4tc],
                        [with_libiptc="pkgconfig"],
                        [with_libiptc="no"],
                        [$with_libiptc_libs])
+       LIBS="$SAVE_LIBS"
 fi
 if test "x$with_libiptc" = "xpkgconfig"
 then
@@ -2465,21 +2469,20 @@ then
 #include <asm/types.h>
 #include <sys/socket.h>])
 
-       AC_COMPILE_IFELSE(
-[#include <stdio.h>
-#include <sys/types.h>
-#include <asm/types.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
-int main (void)
-{
-       int retval = TCA_STATS2;
-       return (retval);
-}],
-       [AC_DEFINE([HAVE_TCA_STATS2], 1, [True if the enum-member TCA_STATS2 exists])]
-       []);
+        AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+                           [
+                            #include <stdio.h>
+                            #include <sys/types.h>
+                            #include <asm/types.h>
+                            #include <sys/socket.h>
+                            #include <linux/netlink.h>
+                            #include <linux/rtnetlink.h>
+                            ], [
+                                int retval = TCA_STATS2;
+                                return (retval);
+                                ]
+                            )],
+                            [AC_DEFINE([HAVE_TCA_STATS2], [1], [True if the enum-member TCA_STATS2 exists])])
 
        AC_COMPILE_IFELSE(
 [#include <stdio.h>
@@ -2508,6 +2511,76 @@ then
 fi
 if test "x$with_libnetlink" = "xyes"
 then
+       SAVE_CFLAGS="$CFLAGS"
+       CFLAGS="$CFLAGS $with_libnetlink_cflags"
+
+       AC_CACHE_CHECK(
+               [if function 'rtnl_dump_filter' expects five arguments],
+               [c_cv_rtnl_dump_filter_five_args],
+               AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+                               [
+AC_INCLUDES_DEFAULT
+#include <asm/types.h>
+#include <sys/socket.h>
+#if HAVE_LIBNETLINK_H
+# include <libnetlink.h>
+#elif HAVE_IPROUTE_LIBNETLINK_H
+# include <iproute/libnetlink.h>
+#elif HAVE_LINUX_LIBNETLINK_H
+# include <linux/libnetlink.h>
+#endif
+                               ],
+                               [
+if (rtnl_dump_filter(NULL, NULL, NULL, NULL, NULL))
+       return 1;
+return 0;
+                               ]
+                       )],
+                       [c_cv_rtnl_dump_filter_five_args="yes"],
+                       [c_cv_rtnl_dump_filter_five_args="no"]
+               )
+       )
+
+       AC_CACHE_CHECK(
+               [if function 'rtnl_dump_filter' expects three arguments],
+               [c_cv_rtnl_dump_filter_three_args],
+               AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+                               [
+AC_INCLUDES_DEFAULT
+#include <asm/types.h>
+#include <sys/socket.h>
+#if HAVE_LIBNETLINK_H
+# include <libnetlink.h>
+#elif HAVE_IPROUTE_LIBNETLINK_H
+# include <iproute/libnetlink.h>
+#elif HAVE_LINUX_LIBNETLINK_H
+# include <linux/libnetlink.h>
+#endif
+                               ],
+                               [
+if (rtnl_dump_filter(NULL, NULL, NULL))
+       return 1;
+return 0;
+                               ]
+                       )],
+                       [c_cv_rtnl_dump_filter_three_args="yes"],
+                       [c_cv_rtnl_dump_filter_three_args="no"]
+               )
+       )
+
+       CFLAGS="$SAVE_CFLAGS"
+
+       if test "x$c_cv_rtnl_dump_filter_five_args" = "xyes"
+       then
+               AC_DEFINE(RTNL_DUMP_FILTER_FIVE_ARGS, 1,
+                               [Define to 1 if function 'rtnl_dump_filter' expects five arguments.])
+       fi
+       if test "x$c_cv_rtnl_dump_filter_three_args" = "xyes"
+       then
+               AC_DEFINE(RTNL_DUMP_FILTER_THREE_ARGS, 1,
+                               [Define to 1 if function 'rtnl_dump_filter' expects three arguments.])
+       fi
+
        BUILD_WITH_LIBNETLINK_CFLAGS="$with_libnetlink_cflags"
        BUILD_WITH_LIBNETLINK_LIBS="$with_libnetlink_libs"
        AC_SUBST(BUILD_WITH_LIBNETLINK_CFLAGS)
@@ -2915,7 +2988,7 @@ AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes")
 perl_interpreter="perl"
 AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
 [
-       if test -x "$withval"
+       if test -f "$withval" && test -x "$withval"
        then
                perl_interpreter="$withval"
                with_libperl="yes"
@@ -4063,7 +4136,7 @@ AC_ARG_WITH(libvarnish, [AS_HELP_STRING([--with-libvarnish@<:@=PREFIX@:>@], [Pat
        then
                AC_MSG_NOTICE([Not checking for libvarnish: Manually configured])
                with_libvarnish_cflags="-I$withval/include"
-               with_libvarnish_libs="-L$withval/lib -lvarnish -lvarnishcompat -lvarnishapi"
+               with_libvarnish_libs="-L$withval/lib -lvarnishapi"
                with_libvarnish="yes"
        fi; fi; fi
 ],
@@ -4566,11 +4639,13 @@ fi
 if test "x$with_perfstat" = "xyes"
 then
        plugin_cpu="yes"
+       plugin_contextswitch="yes"
        plugin_disk="yes"
        plugin_memory="yes"
        plugin_swap="yes"
        plugin_interface="yes"
        plugin_load="yes"
+       plugin_uptime="yes"
 fi
 
 if test "x$with_procinfo" = "xyes"
@@ -4780,6 +4855,7 @@ AC_ARG_ENABLE([all-plugins],
 
 m4_divert_once([HELP_ENABLE], [])
 
+AC_PLUGIN([aggregation], [yes],                [Aggregation plugin])
 AC_PLUGIN([amqp],        [$with_librabbitmq],  [AMQP output plugin])
 AC_PLUGIN([apache],      [$with_libcurl],      [Apache httpd statistics])
 AC_PLUGIN([apcups],      [yes],                [Statistics of UPSes by APC])
@@ -5030,7 +5106,7 @@ AC_SUBST(LCC_VERSION_PATCH)
 AC_SUBST(LCC_VERSION_EXTRA)
 AC_SUBST(LCC_VERSION_STRING)
 
-AC_CONFIG_FILES(src/libcollectdclient/lcc_features.h)
+AC_CONFIG_FILES(src/libcollectdclient/collectd/lcc_features.h)
 
 AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile bindings/java/Makefile)
 
@@ -5113,6 +5189,7 @@ Configuration:
     perl  . . . . . . . . $with_perl_bindings
 
   Modules:
+    aggregation . . . . . $enable_aggregation
     amqp    . . . . . . . $enable_amqp
     apache  . . . . . . . $enable_apache
     apcups  . . . . . . . $enable_apcups
index 0a5d57e6cfe6030eec55df16fecb28b45a828c66..9af0fb008a72790e48562e7e6440c3bec8c6dd2a 100644 (file)
@@ -4,7 +4,7 @@
 #
 # Copyright Â© 2009 Adrian Perez <aperez@igalia.com>
 #
-# Distributed under terms of the GPLv2 license.
+# Distributed under terms of the GPLv2 license or newer.
 # 
 # Frank Marien (frank@apsu.be) 6 Sep 2012
 # - quick fixes for 5.1 binary protocol
index a85027176c426dfd0f4e59c8fb109953d7237d42..4723af964e9ecf7f6ae5a965b00855748a8adcfc 100755 (executable)
@@ -262,28 +262,6 @@ sub contains_invalid_chars
   return;
 }
 
-sub contains_invalid_chars
-{
-  my $str = shift;
-
-  for (split (m//, $str))
-  {
-    my $n = ord ($_);
-
-    # Whitespace is allowed.
-    if (($n >= 9) && ($n <= 13))
-    {
-      next;
-    }
-    elsif ($n < 32)
-    {
-      return (1);
-    }
-  }
-
-  return;
-}
-
 sub show_selector
 {
   my $timespan_selection = get_timespan_selection ();
diff --git a/contrib/postgresql/collectd_insert.sql b/contrib/postgresql/collectd_insert.sql
new file mode 100644 (file)
index 0000000..fb44bb4
--- /dev/null
@@ -0,0 +1,234 @@
+-- collectd - contrib/postgresql/collectd_insert.sql
+-- Copyright (C) 2012 Sebastian 'tokkee' Harl
+-- All rights reserved.
+--
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions
+-- are met:
+--
+-- - Redistributions of source code must retain the above copyright
+--   notice, this list of conditions and the following disclaimer.
+--
+-- - Redistributions in binary form must reproduce the above copyright
+--   notice, this list of conditions and the following disclaimer in the
+--   documentation and/or other materials provided with the distribution.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+-- POSSIBILITY OF SUCH DAMAGE.
+
+-- Description:
+-- ------------
+--
+-- This is a sample database setup that may be used to write data collected by
+-- collectd to a PostgreSQL database. We're using two tables, 'identifiers'
+-- and 'values' to store the value-list identifier and the actual values
+-- respectively.
+--
+-- The 'values' table is partitioned to improve performance and maintainance.
+-- Please note that additional maintainance scripts are required in order to
+-- keep the setup running -- see the comments below for details.
+--
+-- The function 'collectd_insert' may be used to actually insert values
+-- submitted by collectd into those tables.
+--
+-- Sample configuration:
+-- ---------------------
+--
+-- <Plugin postgresql>
+--     <Writer sqlstore>
+--         Statement "SELECT collectd_insert($1, $2, $3, $4, $5, $6, $7, $8, $9);"
+--     </Writer>
+--     <Database foo>
+--         # ...
+--         Writer sqlstore
+--     </Database>
+-- </Plugin>
+
+CREATE TABLE identifiers (
+    id integer NOT NULL,
+    host character varying(64) NOT NULL,
+    plugin character varying(64) NOT NULL,
+    plugin_inst character varying(64) DEFAULT NULL::character varying,
+    type character varying(64) NOT NULL,
+    type_inst character varying(64) DEFAULT NULL::character varying
+);
+CREATE SEQUENCE identifiers_id_seq
+    START WITH 1
+    INCREMENT BY 1
+    NO MINVALUE
+    NO MAXVALUE
+    CACHE 1;
+ALTER SEQUENCE identifiers_id_seq OWNED BY identifiers.id;
+ALTER TABLE ONLY identifiers
+    ALTER COLUMN id SET DEFAULT nextval('identifiers_id_seq'::regclass);
+ALTER TABLE ONLY identifiers
+    ADD CONSTRAINT identifiers_host_plugin_plugin_inst_type_type_inst_key
+        UNIQUE (host, plugin, plugin_inst, type, type_inst);
+ALTER TABLE ONLY identifiers
+    ADD CONSTRAINT identifiers_pkey PRIMARY KEY (id);
+
+-- optionally, create indexes for the identifier fields
+CREATE INDEX identifiers_host ON identifiers USING btree (host);
+CREATE INDEX identifiers_plugin ON identifiers USING btree (plugin);
+CREATE INDEX identifiers_plugin_inst ON identifiers USING btree (plugin_inst);
+CREATE INDEX identifiers_type ON identifiers USING btree (type);
+CREATE INDEX identifiers_type_inst ON identifiers USING btree (type_inst);
+
+CREATE TABLE "values" (
+    id integer NOT NULL,
+    tstamp timestamp with time zone NOT NULL,
+    name character varying(64) NOT NULL,
+    value double precision NOT NULL
+);
+
+CREATE OR REPLACE VIEW collectd
+    AS SELECT host, plugin, plugin_inst, type, type_inst,
+            host
+                || '/' || plugin
+                || CASE
+                    WHEN plugin_inst IS NOT NULL THEN '-'
+                    ELSE ''
+                END
+                || coalesce(plugin_inst, '')
+                || '/' || type
+                || CASE
+                    WHEN type_inst IS NOT NULL THEN '-'
+                    ELSE ''
+                END
+                || coalesce(plugin_inst, '') AS identifier,
+            tstamp, name, value
+        FROM identifiers
+            JOIN values
+            ON values.id = identifiers.id;
+
+-- partition "values" by day (or week, month, ...)
+
+-- create the child tables for today and the next 'days' days:
+-- this may, for example, be used in a daily cron-job (or similar) to create
+-- the tables for the next couple of days
+CREATE OR REPLACE FUNCTION values_update_childs(
+        integer
+    ) RETURNS SETOF text
+    LANGUAGE plpgsql
+    AS $_$
+DECLARE
+    days alias for $1;
+    cur_day date;
+    next_day date;
+    i integer;
+BEGIN
+    IF days < 1 THEN
+        RAISE EXCEPTION 'Cannot have negative number of days';
+    END IF;
+
+    i := 0;
+    LOOP
+        EXIT WHEN i > days;
+
+        SELECT CAST ('now'::date + i * '1day'::interval AS date) INTO cur_day;
+        SELECT CAST ('now'::date + (i + 1) * '1day'::interval AS date) INTO next_day;
+
+        i := i + 1;
+
+        BEGIN
+            EXECUTE 'CREATE TABLE "values$' || cur_day || '" (
+                CHECK (tstamp >= TIMESTAMP ''' || cur_day || ''' '
+                    || 'AND tstamp < TIMESTAMP ''' || next_day || ''')
+            ) INHERITS (values)';
+        EXCEPTION WHEN duplicate_table THEN
+            CONTINUE;
+        END;
+
+        RETURN NEXT 'values$' || cur_day::text;
+
+        EXECUTE 'ALTER TABLE ONLY "values$' || cur_day || '"
+            ADD CONSTRAINT "values_' || cur_day || '_pkey"
+                PRIMARY KEY (id, tstamp, name, value)';
+        EXECUTE 'ALTER TABLE ONLY "values$' || cur_day || '"
+            ADD CONSTRAINT "values_' || cur_day || '_id_fkey"
+                FOREIGN KEY (id) REFERENCES identifiers(id)';
+    END LOOP;
+    RETURN;
+END;
+$_$;
+
+-- create initial child tables
+SELECT values_update_childs(2);
+
+CREATE OR REPLACE FUNCTION values_insert_trigger()
+    RETURNS trigger
+    LANGUAGE plpgsql
+    AS $_$
+DECLARE
+    child_tbl character varying;
+BEGIN
+    SELECT 'values$' || CAST (NEW.tstamp AS DATE) INTO child_tbl;
+    -- Rather than using 'EXECUTE', some if-cascade checking the date may also
+    -- be used. However, this would require frequent updates of the trigger
+    -- function while this example works automatically.
+    EXECUTE 'INSERT INTO "' || child_tbl || '" VALUES ($1.*)' USING NEW;
+    RETURN NULL;
+END;
+$_$;
+
+CREATE TRIGGER insert_values_trigger
+    BEFORE INSERT ON values
+    FOR EACH ROW EXECUTE PROCEDURE values_insert_trigger();
+
+-- when querying values make sure to enable constraint exclusion
+-- SET constraint_exclusion = on;
+
+CREATE OR REPLACE FUNCTION collectd_insert(
+        timestamp with time zone, character varying,
+        character varying, character varying,
+        character varying, character varying,
+        character varying[], character varying[], double precision[]
+    ) RETURNS void
+    LANGUAGE plpgsql
+    AS $_$
+DECLARE
+    p_time alias for $1;
+    p_host alias for $2;
+    p_plugin alias for $3;
+    p_plugin_instance alias for $4;
+    p_type alias for $5;
+    p_type_instance alias for $6;
+    p_value_names alias for $7;
+    -- don't use the type info; for 'StoreRates true' it's 'gauge' anyway
+    -- p_type_names alias for $8;
+    p_values alias for $9;
+    ds_id integer;
+    i integer;
+BEGIN
+    SELECT id INTO ds_id
+        FROM identifiers
+        WHERE host = p_host
+            AND plugin = p_plugin
+            AND COALESCE(plugin_inst, '') = COALESCE(p_plugin_instance, '')
+            AND type = p_type
+            AND COALESCE(type_inst, '') = COALESCE(p_type_instance, '');
+    IF NOT FOUND THEN
+        INSERT INTO identifiers (host, plugin, plugin_inst, type, type_inst)
+            VALUES (p_host, p_plugin, p_plugin_instance, p_type, p_type_instance)
+            RETURNING id INTO ds_id;
+    END IF;
+    i := 1;
+    LOOP
+        EXIT WHEN i > array_upper(p_value_names, 1);
+        INSERT INTO values (id, tstamp, name, value)
+            VALUES (ds_id, p_time, p_value_names[i], p_values[i]);
+        i := i + 1;
+    END LOOP;
+END;
+$_$;
+
+-- vim: set expandtab :
index fb94c7616c0cd7892b01f13643fe7bdd4f327cac..26c2355e50a0aead892ddc9fb3bc10156dc05a54 100644 (file)
@@ -18,7 +18,7 @@ AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"'
 AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"'
 
 sbin_PROGRAMS = collectd collectdmon
-bin_PROGRAMS = collectd-nagios collectdctl
+bin_PROGRAMS = collectd-nagios collectdctl collectd-tg
 
 collectd_SOURCES = collectd.c collectd.h \
                   common.c common.h \
@@ -113,12 +113,34 @@ endif
 collectdctl_LDADD += libcollectdclient/libcollectdclient.la
 collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
+collectd_tg_SOURCES = collectd-tg.c \
+                     utils_heap.c utils_heap.h
+collectd_tg_LDADD =
+if BUILD_WITH_LIBSOCKET
+collectd_tg_LDADD += -lsocket
+endif
+if BUILD_AIX
+collectd_tg_LDADD += -lm
+endif
+collectd_tg_LDADD += libcollectdclient/libcollectdclient.la
+collectd_tg_DEPENDENCIES = libcollectdclient/libcollectdclient.la
+
 
 pkglib_LTLIBRARIES = 
 
 BUILT_SOURCES = 
 CLEANFILES = 
 
+if BUILD_PLUGIN_AGGREGATION
+pkglib_LTLIBRARIES += aggregation.la
+aggregation_la_SOURCES = aggregation.c \
+                         utils_vl_lookup.c utils_vl_lookup.h
+aggregation_la_LDFLAGS = -module -avoid-version
+aggregation_la_LIBADD =
+collectd_LDADD += "-dlopen" aggregation.la
+collectd_DEPENDENCIES += aggregation.la
+endif
+
 if BUILD_PLUGIN_AMQP
 pkglib_LTLIBRARIES += amqp.la
 amqp_la_SOURCES = amqp.c \
@@ -213,6 +235,10 @@ if BUILD_PLUGIN_CONTEXTSWITCH
 pkglib_LTLIBRARIES += contextswitch.la
 contextswitch_la_SOURCES = contextswitch.c
 contextswitch_la_LDFLAGS = -module -avoid-version
+contextswitch_la_LIBADD =
+if BUILD_WITH_PERFSTAT
+contextswitch_la_LIBADD += -lperfstat
+endif
 collectd_LDADD += "-dlopen" contextswitch.la
 collectd_DEPENDENCIES += contextswitch.la
 endif
@@ -1200,6 +1226,9 @@ uptime_la_LIBADD =
 if BUILD_WITH_LIBKSTAT
 uptime_la_LIBADD += -lkstat
 endif
+if BUILD_WITH_PERFSTAT
+uptime_la_LIBADD += -lperfstat
+endif
 collectd_LDADD += "-dlopen" uptime.la
 collectd_DEPENDENCIES += uptime.la
 endif
@@ -1327,6 +1356,8 @@ collectd_LDADD += "-dlopen" zfs_arc.la
 collectd_DEPENDENCIES += zfs_arc.la
 endif
 
+BUILT_SOURCES += $(dist_man_MANS)
+
 dist_man_MANS = collectd.1 \
                collectd.conf.5 \
                collectd-email.5 \
@@ -1393,3 +1424,21 @@ install-exec-hook:
        $(INSTALL) -m 0644 $(srcdir)/types.db $(DESTDIR)$(pkgdatadir)/types.db;
        $(INSTALL) -m 0644 $(srcdir)/postgresql_default.conf \
                $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
+
+uninstall-hook:
+       rm -f $(DESTDIR)$(pkgdatadir)/types.db;
+       rm -f $(DESTDIR)$(sysconfdir)/collectd.conf
+       rm -f $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
+
+if BUILD_FEATURE_DEBUG
+bin_PROGRAMS += utils_vl_lookup_test
+utils_vl_lookup_test_SOURCES = utils_vl_lookup_test.c \
+                               utils_vl_lookup.h utils_vl_lookup.c \
+                               utils_avltree.c utils_avltree.h \
+                               common.h
+
+utils_vl_lookup_test_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL) -DBUILD_TEST=1
+utils_vl_lookup_test_CFLAGS = $(AM_CFLAGS)
+utils_vl_lookup_test_LDFLAGS = -export-dynamic
+utils_vl_lookup_test_LDADD =
+endif
diff --git a/src/aggregation.c b/src/aggregation.c
new file mode 100644 (file)
index 0000000..db33c17
--- /dev/null
@@ -0,0 +1,684 @@
+/**
+ * collectd - src/aggregation.c
+ * Copyright (C) 2012       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+#include "meta_data.h"
+#include "utils_cache.h" /* for uc_get_rate() */
+#include "utils_vl_lookup.h"
+
+#include <pthread.h>
+
+struct aggregation_s /* {{{ */
+{
+  identifier_t ident;
+
+  _Bool calc_num;
+  _Bool calc_sum;
+  _Bool calc_average;
+  _Bool calc_min;
+  _Bool calc_max;
+  _Bool calc_stddev;
+}; /* }}} */
+typedef struct aggregation_s aggregation_t;
+
+struct agg_instance_s;
+typedef struct agg_instance_s agg_instance_t;
+struct agg_instance_s /* {{{ */
+{
+  pthread_mutex_t lock;
+  identifier_t ident;
+
+  int ds_type;
+
+  derive_t num;
+  gauge_t sum;
+  gauge_t squares_sum;
+
+  gauge_t min;
+  gauge_t max;
+
+  rate_to_value_state_t *state_num;
+  rate_to_value_state_t *state_sum;
+  rate_to_value_state_t *state_average;
+  rate_to_value_state_t *state_min;
+  rate_to_value_state_t *state_max;
+  rate_to_value_state_t *state_stddev;
+
+  agg_instance_t *next;
+}; /* }}} */
+
+static lookup_t *lookup = NULL;
+
+static pthread_mutex_t agg_instance_list_lock = PTHREAD_MUTEX_INITIALIZER;
+static agg_instance_t *agg_instance_list_head = NULL;
+
+static void agg_destroy (aggregation_t *agg) /* {{{ */
+{
+  sfree (agg);
+} /* }}} void agg_destroy */
+
+/* Frees all dynamically allocated memory within the instance. */
+static void agg_instance_destroy (agg_instance_t *inst) /* {{{ */
+{
+  if (inst == NULL)
+    return;
+
+  /* Remove this instance from the global list of instances. */
+  pthread_mutex_lock (&agg_instance_list_lock);
+  if (agg_instance_list_head == inst)
+    agg_instance_list_head = inst->next;
+  else if (agg_instance_list_head != NULL)
+  {
+    agg_instance_t *prev = agg_instance_list_head;
+    while ((prev != NULL) && (prev->next != inst))
+      prev = prev->next;
+    if (prev != NULL)
+      prev->next = inst->next;
+  }
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  sfree (inst->state_num);
+  sfree (inst->state_sum);
+  sfree (inst->state_average);
+  sfree (inst->state_min);
+  sfree (inst->state_max);
+  sfree (inst->state_stddev);
+
+  memset (inst, 0, sizeof (*inst));
+  inst->ds_type = -1;
+  inst->min = NAN;
+  inst->max = NAN;
+} /* }}} void agg_instance_destroy */
+
+/* Create a new aggregation instance. */
+static agg_instance_t *agg_instance_create (data_set_t const *ds, /* {{{ */
+    value_list_t const *vl, aggregation_t *agg)
+{
+  agg_instance_t *inst;
+
+  DEBUG ("aggregation plugin: Creating new instance.");
+
+  inst = malloc (sizeof (*inst));
+  if (inst == NULL)
+  {
+    ERROR ("aggregation plugin: malloc() failed.");
+    return (NULL);
+  }
+  memset (inst, 0, sizeof (*inst));
+  pthread_mutex_init (&inst->lock, /* attr = */ NULL);
+
+  inst->ds_type = ds->ds[0].type;
+
+#define COPY_FIELD(fld) do { \
+  sstrncpy (inst->ident.fld, \
+      LU_IS_ANY (agg->ident.fld) ? vl->fld : agg->ident.fld, \
+      sizeof (inst->ident.fld)); \
+} while (0)
+
+  COPY_FIELD (host);
+  COPY_FIELD (plugin);
+  COPY_FIELD (plugin_instance);
+  COPY_FIELD (type);
+  COPY_FIELD (type_instance);
+
+#undef COPY_FIELD
+
+  inst->min = NAN;
+  inst->max = NAN;
+
+#define INIT_STATE(field) do { \
+  inst->state_ ## field = NULL; \
+  if (agg->calc_ ## field) { \
+    inst->state_ ## field = malloc (sizeof (*inst->state_ ## field)); \
+    if (inst->state_ ## field == NULL) { \
+      agg_instance_destroy (inst); \
+      ERROR ("aggregation plugin: malloc() failed."); \
+      return (NULL); \
+    } \
+    memset (inst->state_ ## field, 0, sizeof (*inst->state_ ## field)); \
+  } \
+} while (0)
+
+  INIT_STATE (num);
+  INIT_STATE (sum);
+  INIT_STATE (average);
+  INIT_STATE (min);
+  INIT_STATE (max);
+  INIT_STATE (stddev);
+
+#undef INIT_STATE
+
+  pthread_mutex_lock (&agg_instance_list_lock);
+  inst->next = agg_instance_list_head;
+  agg_instance_list_head = inst;
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  return (inst);
+} /* }}} agg_instance_t *agg_instance_create */
+
+/* Update the num, sum, min, max, ... fields of the aggregation instance, if
+ * the rate of the value list is available. Value lists with more than one data
+ * source are not supported and will return an error. Returns zero on success
+ * and non-zero otherwise. */
+static int agg_instance_update (agg_instance_t *inst, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl)
+{
+  gauge_t *rate;
+
+  if (ds->ds_num != 1)
+  {
+    ERROR ("aggregation plugin: The \"%s\" type (data set) has more than one "
+        "data source. This is currently not supported by this plugin. "
+        "Sorry.", ds->type);
+    return (EINVAL);
+  }
+
+  rate = uc_get_rate (ds, vl);
+  if (rate == NULL)
+  {
+    char ident[6 * DATA_MAX_NAME_LEN];
+    FORMAT_VL (ident, sizeof (ident), vl);
+    ERROR ("aggregation plugin: Unable to read the current rate of \"%s\".",
+        ident);
+    return (ENOENT);
+  }
+
+  if (isnan (rate[0]))
+  {
+    sfree (rate);
+    return (0);
+  }
+
+  pthread_mutex_lock (&inst->lock);
+
+  inst->num++;
+  inst->sum += rate[0];
+  inst->squares_sum += (rate[0] * rate[0]);
+
+  if (isnan (inst->min) || (inst->min > rate[0]))
+    inst->min = rate[0];
+  if (isnan (inst->max) || (inst->max < rate[0]))
+    inst->max = rate[0];
+
+  pthread_mutex_unlock (&inst->lock);
+
+  sfree (rate);
+  return (0);
+} /* }}} int agg_instance_update */
+
+static int agg_instance_read_func (agg_instance_t *inst, /* {{{ */
+  char const *func, gauge_t rate, rate_to_value_state_t *state,
+  value_list_t *vl, char const *pi_prefix, cdtime_t t)
+{
+  value_t v;
+  int status;
+
+  if (pi_prefix[0] != 0)
+    ssnprintf (vl->plugin_instance, sizeof (vl->plugin_instance), "%s-%s",
+        pi_prefix, func);
+  else
+    sstrncpy (vl->plugin_instance, func, sizeof (vl->plugin_instance));
+
+  memset (&v, 0, sizeof (v));
+  status = rate_to_value (&v, rate, state, inst->ds_type, t);
+  if (status != 0)
+  {
+    /* If this is the first iteration and rate_to_value() was asked to return a
+     * COUNTER or a DERIVE, it will return EAGAIN. Catch this and handle
+     * gracefully. */
+    if (status == EAGAIN)
+      return (0);
+
+    WARNING ("aggregation plugin: rate_to_value failed with status %i.",
+        status);
+    return (-1);
+  }
+
+  vl->values = &v;
+  vl->values_len = 1;
+
+  plugin_dispatch_values_secure (vl);
+
+  vl->values = NULL;
+  vl->values_len = 0;
+
+  return (0);
+} /* }}} int agg_instance_read_func */
+
+static int agg_instance_read (agg_instance_t *inst, cdtime_t t) /* {{{ */
+{
+  value_list_t vl = VALUE_LIST_INIT;
+  char pi_prefix[DATA_MAX_NAME_LEN];
+
+  /* Pre-set all the fields in the value list that will not change per
+   * aggregation type (sum, average, ...). The struct will be re-used and must
+   * therefore be dispatched using the "secure" function. */
+
+  vl.time = t;
+  vl.interval = 0;
+
+  vl.meta = meta_data_create ();
+  if (vl.meta == NULL)
+  {
+    ERROR ("aggregation plugin: meta_data_create failed.");
+    return (-1);
+  }
+  meta_data_add_boolean (vl.meta, "aggregation:created", 1);
+
+  if (LU_IS_ALL (inst->ident.host))
+    sstrncpy (vl.host, "global", sizeof (vl.host));
+  else
+    sstrncpy (vl.host, inst->ident.host, sizeof (vl.host));
+
+  sstrncpy (vl.plugin, "aggregation", sizeof (vl.plugin));
+
+  if (LU_IS_ALL (inst->ident.plugin))
+  {
+    if (LU_IS_ALL (inst->ident.plugin_instance))
+      sstrncpy (pi_prefix, "", sizeof (pi_prefix));
+    else
+      sstrncpy (pi_prefix, inst->ident.plugin_instance, sizeof (pi_prefix));
+  }
+  else
+  {
+    if (LU_IS_ALL (inst->ident.plugin_instance))
+      sstrncpy (pi_prefix, inst->ident.plugin, sizeof (pi_prefix));
+    else
+      ssnprintf (pi_prefix, sizeof (pi_prefix),
+          "%s-%s", inst->ident.plugin, inst->ident.plugin_instance);
+  }
+
+  sstrncpy (vl.type, inst->ident.type, sizeof (vl.type));
+
+  if (!LU_IS_ALL (inst->ident.type_instance))
+    sstrncpy (vl.type_instance, inst->ident.type_instance,
+        sizeof (vl.type_instance));
+
+#define READ_FUNC(func, rate) do { \
+  if (inst->state_ ## func != NULL) { \
+    agg_instance_read_func (inst, #func, rate, \
+        inst->state_ ## func, &vl, pi_prefix, t); \
+  } \
+} while (0)
+
+  pthread_mutex_lock (&inst->lock);
+
+  READ_FUNC (num, (gauge_t) inst->num);
+
+  /* All other aggregations are only defined when there have been any values
+   * at all. */
+  if (inst->num > 0)
+  {
+    READ_FUNC (sum, inst->sum);
+    READ_FUNC (average, (inst->sum / ((gauge_t) inst->num)));
+    READ_FUNC (min, inst->min);
+    READ_FUNC (max, inst->max);
+    READ_FUNC (stddev, sqrt((((gauge_t) inst->num) * inst->squares_sum)
+          - (inst->sum * inst->sum)) / ((gauge_t) inst->num));
+  }
+
+  /* Reset internal state. */
+  inst->num = 0;
+  inst->sum = 0.0;
+  inst->squares_sum = 0.0;
+  inst->min = NAN;
+  inst->max = NAN;
+
+  pthread_mutex_unlock (&inst->lock);
+
+  meta_data_destroy (vl.meta);
+  vl.meta = NULL;
+
+  return (0);
+} /* }}} int agg_instance_read */
+
+/* lookup_class_callback_t for utils_vl_lookup */
+static void *agg_lookup_class_callback ( /* {{{ */
+    __attribute__((unused)) data_set_t const *ds,
+    value_list_t const *vl, void *user_class)
+{
+  return (agg_instance_create (ds, vl, (aggregation_t *) user_class));
+} /* }}} void *agg_class_callback */
+
+/* lookup_obj_callback_t for utils_vl_lookup */
+static int agg_lookup_obj_callback (data_set_t const *ds, /* {{{ */
+    value_list_t const *vl,
+    __attribute__((unused)) void *user_class,
+    void *user_obj)
+{
+  return (agg_instance_update ((agg_instance_t *) user_obj, ds, vl));
+} /* }}} int agg_lookup_obj_callback */
+
+/* lookup_free_class_callback_t for utils_vl_lookup */
+static void agg_lookup_free_class_callback (void *user_class) /* {{{ */
+{
+  agg_destroy ((aggregation_t *) user_class);
+} /* }}} void agg_lookup_free_class_callback */
+
+/* lookup_free_obj_callback_t for utils_vl_lookup */
+static void agg_lookup_free_obj_callback (void *user_obj) /* {{{ */
+{
+  agg_instance_destroy ((agg_instance_t *) user_obj);
+} /* }}} void agg_lookup_free_obj_callback */
+
+/*
+ * <Plugin "aggregation">
+ *   <Aggregation>
+ *     Plugin "cpu"
+ *     Type "cpu"
+ *
+ *     GroupBy Host
+ *     GroupBy TypeInstance
+ *
+ *     CalculateNum true
+ *     CalculateSum true
+ *     CalculateAverage true
+ *     CalculateMinimum true
+ *     CalculateMaximum true
+ *     CalculateStddev true
+ *   </Aggregation>
+ * </Plugin>
+ */
+static int agg_config_handle_group_by (oconfig_item_t const *ci, /* {{{ */
+    aggregation_t *agg)
+{
+  int i;
+
+  for (i = 0; i < ci->values_num; i++)
+  {
+    char const *value;
+
+    if (ci->values[i].type != OCONFIG_TYPE_STRING)
+    {
+      ERROR ("aggregation plugin: Argument %i of the \"GroupBy\" option "
+          "is not a string.", i + 1);
+      continue;
+    }
+
+    value = ci->values[i].value.string;
+
+    if (strcasecmp ("Host", value) == 0)
+      sstrncpy (agg->ident.host, LU_ANY, sizeof (agg->ident.host));
+    else if (strcasecmp ("Plugin", value) == 0)
+      sstrncpy (agg->ident.plugin, LU_ANY, sizeof (agg->ident.plugin));
+    else if (strcasecmp ("PluginInstance", value) == 0)
+      sstrncpy (agg->ident.plugin_instance, LU_ANY,
+          sizeof (agg->ident.plugin_instance));
+    else if (strcasecmp ("TypeInstance", value) == 0)
+      sstrncpy (agg->ident.type_instance, LU_ANY, sizeof (agg->ident.type_instance));
+    else if (strcasecmp ("Type", value) == 0)
+      ERROR ("aggregation plugin: Grouping by type is not supported.");
+    else
+      WARNING ("aggregation plugin: The \"%s\" argument to the \"GroupBy\" "
+          "option is invalid and will be ignored.", value);
+  } /* for (ci->values) */
+
+  return (0);
+} /* }}} int agg_config_handle_group_by */
+
+static int agg_config_aggregation (oconfig_item_t *ci) /* {{{ */
+{
+  aggregation_t *agg;
+  _Bool is_valid;
+  int status;
+  int i;
+
+  agg = malloc (sizeof (*agg));
+  if (agg == NULL)
+  {
+    ERROR ("aggregation plugin: malloc failed.");
+    return (-1);
+  }
+  memset (agg, 0, sizeof (*agg));
+
+  sstrncpy (agg->ident.host, LU_ALL, sizeof (agg->ident.host));
+  sstrncpy (agg->ident.plugin, LU_ALL, sizeof (agg->ident.plugin));
+  sstrncpy (agg->ident.plugin_instance, LU_ALL,
+      sizeof (agg->ident.plugin_instance));
+  sstrncpy (agg->ident.type, LU_ALL, sizeof (agg->ident.type));
+  sstrncpy (agg->ident.type_instance, LU_ALL,
+      sizeof (agg->ident.type_instance));
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Host", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.host,
+          sizeof (agg->ident.host));
+    else if (strcasecmp ("Plugin", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.plugin,
+          sizeof (agg->ident.plugin));
+    else if (strcasecmp ("PluginInstance", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.plugin_instance,
+          sizeof (agg->ident.plugin_instance));
+    else if (strcasecmp ("Type", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.type,
+          sizeof (agg->ident.type));
+    else if (strcasecmp ("TypeInstance", child->key) == 0)
+      cf_util_get_string_buffer (child, agg->ident.type_instance,
+          sizeof (agg->ident.type_instance));
+    else if (strcasecmp ("GroupBy", child->key) == 0)
+      agg_config_handle_group_by (child, agg);
+    else if (strcasecmp ("CalculateNum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_num);
+    else if (strcasecmp ("CalculateSum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_sum);
+    else if (strcasecmp ("CalculateAverage", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_average);
+    else if (strcasecmp ("CalculateMinimum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_min);
+    else if (strcasecmp ("CalculateMaximum", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_max);
+    else if (strcasecmp ("CalculateStddev", child->key) == 0)
+      cf_util_get_boolean (child, &agg->calc_stddev);
+    else
+      WARNING ("aggregation plugin: The \"%s\" key is not allowed inside "
+          "<Aggregation /> blocks and will be ignored.", child->key);
+  }
+
+  /* Sanity checking */
+  is_valid = 1;
+  if (LU_IS_ALL (agg->ident.type)) /* {{{ */
+  {
+    ERROR ("aggregation plugin: It appears you did not specify the required "
+        "\"Type\" option in this aggregation. "
+        "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+        "Type \"%s\", TypeInstance \"%s\")",
+        agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+        agg->ident.type, agg->ident.type_instance);
+    is_valid = 0;
+  }
+  else if (strchr (agg->ident.type, '/') != NULL)
+  {
+    ERROR ("aggregation plugin: The \"Type\" may not contain the '/' "
+        "character. Especially, it may not be a wildcard. The current "
+        "value is \"%s\".", agg->ident.type);
+    is_valid = 0;
+  } /* }}} */
+
+  if (!LU_IS_ALL (agg->ident.host) /* {{{ */
+      && !LU_IS_ALL (agg->ident.plugin)
+      && !LU_IS_ALL (agg->ident.plugin_instance)
+      && !LU_IS_ALL (agg->ident.type_instance))
+  {
+    ERROR ("aggregation plugin: An aggregation must contain at least one "
+        "wildcard. This is achieved by leaving at least one of the \"Host\", "
+        "\"Plugin\", \"PluginInstance\" and \"TypeInstance\" options blank "
+        "and not grouping by that field. "
+        "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+        "Type \"%s\", TypeInstance \"%s\")",
+        agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+        agg->ident.type, agg->ident.type_instance);
+    is_valid = 0;
+  } /* }}} */
+
+  if (!agg->calc_num && !agg->calc_sum && !agg->calc_average /* {{{ */
+      && !agg->calc_min && !agg->calc_max && !agg->calc_stddev)
+  {
+    ERROR ("aggregation plugin: No aggregation function has been specified. "
+        "Without this, I don't know what I should be calculating. "
+        "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+        "Type \"%s\", TypeInstance \"%s\")",
+        agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+        agg->ident.type, agg->ident.type_instance);
+    is_valid = 0;
+  } /* }}} */
+
+  if (!is_valid) /* {{{ */
+  {
+    sfree (agg);
+    return (-1);
+  } /* }}} */
+
+  status = lookup_add (lookup, &agg->ident, agg);
+  if (status != 0)
+  {
+    ERROR ("aggregation plugin: lookup_add failed with status %i.", status);
+    sfree (agg);
+    return (-1);
+  }
+
+  DEBUG ("aggregation plugin: Successfully added aggregation: "
+      "(Host \"%s\", Plugin \"%s\", PluginInstance \"%s\", "
+      "Type \"%s\", TypeInstance \"%s\")",
+      agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
+      agg->ident.type, agg->ident.type_instance);
+  return (0);
+} /* }}} int agg_config_aggregation */
+
+static int agg_config (oconfig_item_t *ci) /* {{{ */
+{
+  int i;
+
+  pthread_mutex_lock (&agg_instance_list_lock);
+
+  if (lookup == NULL)
+  {
+    lookup = lookup_create (agg_lookup_class_callback,
+        agg_lookup_obj_callback,
+        agg_lookup_free_class_callback,
+        agg_lookup_free_obj_callback);
+    if (lookup == NULL)
+    {
+      pthread_mutex_unlock (&agg_instance_list_lock);
+      ERROR ("aggregation plugin: lookup_create failed.");
+      return (-1);
+    }
+  }
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Aggregation", child->key) == 0)
+      agg_config_aggregation (child);
+    else
+      WARNING ("aggregation plugin: The \"%s\" key is not allowed inside "
+          "<Plugin aggregation /> blocks and will be ignored.", child->key);
+  }
+
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  return (0);
+} /* }}} int agg_config */
+
+static int agg_read (void) /* {{{ */
+{
+  agg_instance_t *this;
+  cdtime_t t;
+  int success;
+
+  t = cdtime ();
+  success = 0;
+
+  pthread_mutex_lock (&agg_instance_list_lock);
+
+  /* agg_instance_list_head only holds data, after the "write" callback has
+   * been called with a matching value list at least once. So on startup,
+   * there's a race between the aggregations read() and write() callback. If
+   * the read() callback is called first, agg_instance_list_head is NULL and
+   * "success" may be zero. This is expected and should not result in an error.
+   * Therefore we need to handle this case separately. */
+  if (agg_instance_list_head == NULL)
+  {
+    pthread_mutex_unlock (&agg_instance_list_lock);
+    return (0);
+  }
+
+  for (this = agg_instance_list_head; this != NULL; this = this->next)
+  {
+    int status;
+
+    status = agg_instance_read (this, t);
+    if (status != 0)
+      WARNING ("aggregation plugin: Reading an aggregation instance "
+          "failed with status %i.", status);
+    else
+      success++;
+  }
+
+  pthread_mutex_unlock (&agg_instance_list_lock);
+
+  return ((success > 0) ? 0 : -1);
+} /* }}} int agg_read */
+
+static int agg_write (data_set_t const *ds, value_list_t const *vl, /* {{{ */
+    __attribute__((unused)) user_data_t *user_data)
+{
+  _Bool created_by_aggregation = 0;
+  int status;
+
+  /* Ignore values that were created by the aggregation plugin to avoid weird
+   * effects. */
+  (void) meta_data_get_boolean (vl->meta, "aggregation:created",
+      &created_by_aggregation);
+  if (created_by_aggregation)
+    return (0);
+
+  if (lookup == NULL)
+    status = ENOENT;
+  else
+  {
+    status = lookup_search (lookup, ds, vl);
+    if (status > 0)
+      status = 0;
+  }
+
+  return (status);
+} /* }}} int agg_write */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("aggregation", agg_config);
+  plugin_register_read ("aggregation", agg_read);
+  plugin_register_write ("aggregation", agg_write, /* user_data = */ NULL);
+}
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
index 876f7e9e2cb0fb034cf365156d6165d002367c73..9c8c6e537c6cb1d2f3907402f7c602e2f8b326b6 100644 (file)
@@ -600,6 +600,8 @@ static void *camqp_subscribe_thread (void *user_data) /* {{{ */
     camqp_config_t *conf = user_data;
     int status;
 
+    cdtime_t interval = plugin_get_interval ();
+
     while (subscriber_threads_running)
     {
         amqp_frame_t frame;
@@ -610,8 +612,8 @@ static void *camqp_subscribe_thread (void *user_data) /* {{{ */
             struct timespec ts_interval;
             ERROR ("amqp plugin: camqp_connect failed. "
                     "Will sleep for %.3f seconds.",
-                    CDTIME_T_TO_DOUBLE (interval_g));
-            CDTIME_T_TO_TIMESPEC (interval_g, &ts_interval);
+                    CDTIME_T_TO_DOUBLE (interval));
+            CDTIME_T_TO_TIMESPEC (interval, &ts_interval);
             nanosleep (&ts_interval, /* remaining = */ NULL);
             continue;
         }
@@ -622,9 +624,9 @@ static void *camqp_subscribe_thread (void *user_data) /* {{{ */
             struct timespec ts_interval;
             ERROR ("amqp plugin: amqp_simple_wait_frame failed. "
                     "Will sleep for %.3f seconds.",
-                    CDTIME_T_TO_DOUBLE (interval_g));
+                    CDTIME_T_TO_DOUBLE (interval));
             camqp_close_connection (conf);
-            CDTIME_T_TO_TIMESPEC (interval_g, &ts_interval);
+            CDTIME_T_TO_TIMESPEC (interval, &ts_interval);
             nanosleep (&ts_interval, /* remaining = */ NULL);
             continue;
         }
@@ -670,7 +672,7 @@ static int camqp_subscribe_init (camqp_config_t *conf) /* {{{ */
     tmp = subscriber_threads + subscriber_threads_num;
     memset (tmp, 0, sizeof (*tmp));
 
-    status = pthread_create (tmp, /* attr = */ NULL,
+    status = plugin_thread_create (tmp, /* attr = */ NULL,
             camqp_subscribe_thread, conf);
     if (status != 0)
     {
@@ -791,7 +793,8 @@ static int camqp_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
     else if (conf->format == CAMQP_FORMAT_GRAPHITE)
     {
         status = format_graphite (buffer, sizeof (buffer), ds, vl,
-                    conf->prefix, conf->postfix, conf->escape_char);
+                    conf->prefix, conf->postfix, conf->escape_char,
+                    conf->store_rates);
         if (status != 0)
         {
             ERROR ("amqp plugin: format_graphite failed with status %i.",
index a0629d5f6ae0a87e13b08f1e53f6d69b0c1a85f3..4fbbde63aad113dcbefdbc2db47e545ca3b5c241 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * collectd - src/apcups.c
- * Copyright (C) 2006-2007  Florian octo Forster
+ * Copyright (C) 2006-2012  Florian octo Forster
  * Copyright (C) 2006       Anthony Gialluca <tonyabg at charter.net>
  * Copyright (C) 2000-2004  Kern Sibbald
  * Copyright (C) 1996-1999  Andre M. Hedrick <andre at suse.com>
@@ -73,6 +73,10 @@ static int   conf_port = NISPORT;
 
 static int global_sockfd = -1;
 
+static int count_retries = 0;
+static int count_iterations = 0;
+static _Bool close_socket = 0;
+
 static const char *config_keys[] =
 {
        "Host",
@@ -81,26 +85,31 @@ static const char *config_keys[] =
 };
 static int config_keys_num = 2;
 
-/* Close the network connection */
-static int apcups_shutdown (void)
+static int net_shutdown (int *fd)
 {
        uint16_t packet_size = 0;
 
-       if (global_sockfd < 0)
-               return (0);
+       if ((fd == NULL) || (*fd < 0))
+               return (EINVAL);
 
-       DEBUG ("Gracefully shutting down socket %i.", global_sockfd);
+       swrite (*fd, (void *) &packet_size, sizeof (packet_size));
+       close (*fd);
+       *fd = -1;
 
-       /* send EOF sentinel */
-       swrite (global_sockfd, (void *) &packet_size, sizeof (packet_size));
+       return (0);
+} /* int net_shutdown */
 
-       close (global_sockfd);
-       global_sockfd = -1;
+/* Close the network connection */
+static int apcups_shutdown (void)
+{
+       if (global_sockfd < 0)
+               return (0);
 
+       net_shutdown (&global_sockfd);
        return (0);
 } /* int apcups_shutdown */
 
-/*     
+/*
  * Open a TCP connection to the UPS network server
  * Returns -1 on error
  * Returns socket file descriptor otherwise
@@ -170,7 +179,7 @@ static int net_open (char *host, int port)
        return (sd);
 } /* int net_open (char *host, char *service, int port) */
 
-/* 
+/*
  * Receive a message from the other end. Each message consists of
  * two packets. The first is a header that contains the size
  * of the data that follows in the second packet.
@@ -186,6 +195,7 @@ static int net_recv (int *sockfd, char *buf, int buflen)
        /* get data size -- in short */
        if (sread (*sockfd, (void *) &packet_size, sizeof (packet_size)) != 0)
        {
+               close (*sockfd);
                *sockfd = -1;
                return (-1);
        }
@@ -193,7 +203,11 @@ static int net_recv (int *sockfd, char *buf, int buflen)
        packet_size = ntohs (packet_size);
        if (packet_size > buflen)
        {
-               DEBUG ("record length too large");
+               ERROR ("apcups plugin: Received %"PRIu16" bytes of payload "
+                               "but have only %i bytes of buffer available.",
+                               packet_size, buflen);
+               close (*sockfd);
+               *sockfd = -1;
                return (-2);
        }
 
@@ -203,6 +217,7 @@ static int net_recv (int *sockfd, char *buf, int buflen)
        /* now read the actual data */
        if (sread (*sockfd, (void *) buf, packet_size) != 0)
        {
+               close (*sockfd);
                *sockfd = -1;
                return (-1);
        }
@@ -229,6 +244,7 @@ static int net_send (int *sockfd, char *buff, int len)
 
        if (swrite (*sockfd, (void *) &packet_size, sizeof (packet_size)) != 0)
        {
+               close (*sockfd);
                *sockfd = -1;
                return (-1);
        }
@@ -236,6 +252,7 @@ static int net_send (int *sockfd, char *buff, int len)
        /* send data packet */
        if (swrite (*sockfd, (void *) buff, len) != 0)
        {
+               close (*sockfd);
                *sockfd = -1;
                return (-2);
        }
@@ -253,6 +270,8 @@ static int apc_query_server (char *host, int port,
        char   *toksaveptr;
        char   *key;
        double  value;
+       _Bool retry = 1;
+       int status;
 
 #if APCMAIN
 # define PRINT_VALUE(name, val) printf("  Found property: name = %s; value = %f;\n", name, val)
@@ -260,21 +279,51 @@ static int apc_query_server (char *host, int port,
 # define PRINT_VALUE(name, val) /**/
 #endif
 
-       if (global_sockfd < 0)
+       while (retry)
        {
-               global_sockfd = net_open (host, port);
                if (global_sockfd < 0)
                {
-                       ERROR ("apcups plugin: Connecting to the "
-                                       "apcupsd failed.");
+                       global_sockfd = net_open (host, port);
+                       if (global_sockfd < 0)
+                       {
+                               ERROR ("apcups plugin: Connecting to the "
+                                               "apcupsd failed.");
+                               return (-1);
+                       }
+               }
+
+
+               status = net_send (&global_sockfd, "status", strlen ("status"));
+               if (status != 0)
+               {
+                       /* net_send is closing the socket on error. */
+                       assert (global_sockfd < 0);
+                       if (retry)
+                       {
+                               retry = 0;
+                               count_retries++;
+                               continue;
+                       }
+
+                       ERROR ("apcups plugin: Writing to the socket failed.");
                        return (-1);
                }
-       }
 
-       if (net_send (&global_sockfd, "status", 6) < 0)
+               break;
+       } /* while (retry) */
+
+        /* When collectd's collection interval is larger than apcupsd's
+         * timeout, we would have to retry / re-connect each iteration. Try to
+         * detect this situation and shut down the socket gracefully in that
+         * case. Otherwise, keep the socket open to avoid overhead. */
+       count_iterations++;
+       if ((count_iterations == 10) && (count_retries > 2))
        {
-               ERROR ("apcups plugin: Writing to the socket failed.");
-               return (-1);
+               NOTICE ("apcups plugin: There have been %i retries in the "
+                               "first %i iterations. Will close the socket "
+                               "in future iterations.",
+                               count_retries, count_iterations);
+               close_socket = 1;
        }
 
        while ((n = net_recv (&global_sockfd, recvline, sizeof (recvline) - 1)) > 0)
@@ -298,7 +347,7 @@ static int apc_query_server (char *host, int port,
 
                        if (strcmp ("LINEV", key) == 0)
                                apcups_detail->linev = value;
-                       else if (strcmp ("BATTV", key) == 0) 
+                       else if (strcmp ("BATTV", key) == 0)
                                apcups_detail->battv = value;
                        else if (strcmp ("ITEMP", key) == 0)
                                apcups_detail->itemp = value;
@@ -316,10 +365,16 @@ static int apc_query_server (char *host, int port,
                        tokptr = strtok_r (NULL, ":", &toksaveptr);
                } /* while (tokptr != NULL) */
        }
-       
+       status = errno; /* save errno, net_shutdown() may re-set it. */
+
+       if (close_socket)
+               net_shutdown (&global_sockfd);
+
        if (n < 0)
        {
-               WARNING ("apcups plugin: Error reading from socket");
+               char errbuf[1024];
+               ERROR ("apcups plugin: Reading from socket failed: %s",
+                               sstrerror (status, errbuf, sizeof (errbuf)));
                return (-1);
        }
 
@@ -398,12 +453,12 @@ static int apcups_read (void)
        apcups_detail.timeleft =   -1.0;
        apcups_detail.itemp    = -300.0;
        apcups_detail.linefreq =   -1.0;
-  
+
        status = apc_query_server (conf_host == NULL
                        ? APCUPS_DEFAULT_HOST
                        : conf_host,
                        conf_port, &apcups_detail);
+
        /*
         * if we did not connect then do not bother submitting
         * zeros. We want rrd files to have NAN.
index 88a53023fb788a7c1439d2e41bf00e71b1a0b377..e31d95ca31f3e52ebd2923e0482b3e7f7973f294 100644 (file)
@@ -66,7 +66,7 @@
 # endif
 #endif /* NAN_ZERO_ZERO */
 
-#include "libcollectdclient/client.h"
+#include "libcollectdclient/collectd/client.h"
 
 #define RET_OKAY     0
 #define RET_WARNING  1
index d5401dd4c7aac55bf1f08d8c5cf5ecbd0c9ff733..ee05e002d4fea57996ed5cf6543c38d166a35c13 100644 (file)
@@ -208,7 +208,7 @@ layout looks like this:
   {
     values => [123, 0.5],
     time   => time (),
-    interval => $interval_g,
+    interval => plugin_get_interval (),
     host   => $hostname_g,
     plugin => 'myplugin',
     type   => 'myplugin',
@@ -415,6 +415,13 @@ The message is passed to all log-callbacks that are registered with collectd.
 Wrappers around B<plugin_log>, using B<LOG_ERR>, B<LOG_WARNING>,
 B<LOG_NOTICE>, B<LOG_INFO> and B<LOG_DEBUG> respectively as I<log-level>.
 
+=item B<plugin_get_interval> ()
+
+Returns the interval of the current plugin as a floating point number in
+seconds. This value depends on the interval configured within the
+C<LoadPlugin perl> block or the global interval (see L<collectd.conf(5)> for
+details).
+
 =back
 
 The following function provides the filter chain C-interface to Perl-modules.
@@ -494,6 +501,11 @@ B<FQDNLookup> configuration options (see L<collectd.conf(5)> for details).
 This variable keeps the interval in seconds in which the read functions are
 queried (see the B<Interval> configuration option).
 
+B<Note:> This variable should no longer be used in favor of
+C<plugin_get_interval()> (see above). This function takes any plugin-specific
+interval settings into account (see the C<Interval> option of C<LoadPlugin> in
+L<collectd.conf(5)> for details).
+
 =back
 
 Any changes to these variables will be globally visible in collectd.
index 5fd1f4f934e58228f99796ce9f30876bf7f85a7e..39503ad0ba15dfb89ceaf66606ab3afeca442328 100644 (file)
@@ -37,7 +37,7 @@ for collectd in Python. This is a lot more efficient than executing a
 Python-script every time you want to read a value with the C<exec plugin> (see
 L<collectd-exec(5)>) and provides a lot more functionality, too.
 
-At least python I<version 2.3> is required.
+The minimum required Python version is I<2.3>.
 
 =head1 CONFIGURATION
 
@@ -47,9 +47,9 @@ At least python I<version 2.3> is required.
 
 Loads the Python plugin I<Plugin>. Unlike most other LoadPlugin lines, this one
 should be a block containing the line "Globals true". This will cause collectd
-to export the name of all objects in the python interpreter for all plugins to
+to export the name of all objects in the Python interpreter for all plugins to
 see. If you don't do this or your platform does not support it, the embedded
-interpreter will start anyway but you won't be able to load certain python
+interpreter will start anyway but you won't be able to load certain Python
 modules, e.g. "time".
 
 =item B<Encoding> I<Name>
@@ -68,25 +68,25 @@ use multiple B<ModulePath> lines to add more than one directory.
 
 =item B<LogTraces> I<bool>
 
-If a python script throws an exception it will be logged by collectd with the
+If a Python script throws an exception it will be logged by collectd with the
 name of the exception and the message. If you set this option to true it will
 also log the full stacktrace just like the default output of an interactive
-python interpreter. This should probably be set to false most of the time but
+Python interpreter. This should probably be set to false most of the time but
 is very useful for development and debugging of new modules.
 
 =item B<Interactive> I<bool>
 
-This option will cause the module to launch an interactive python interpreter
+This option will cause the module to launch an interactive Python interpreter
 that reads from and writes to the terminal. Note that collectd will terminate
 right after starting up if you try to run it as a daemon while this option is
-enabled to make sure to start collectd with the B<-f> option.
+enabled so make sure to start collectd with the B<-f> option.
 
 The B<collectd> module is I<not> imported into the interpreter's globals. You
 have to do it manually. Be sure to read the help text of the module, it can be
 used as a reference guide during coding.
 
 This interactive session will behave slightly differently from a daemonized
-collectd script as well as from a normal python interpreter:
+collectd script as well as from a normal Python interpreter:
 
 =over 4
 
@@ -107,22 +107,22 @@ To quit collectd send I<EOF> (press I<Ctrl+D> at the beginning of a new line).
 
 =item
 
-B<3.> collectd handles I<SIGCHLD>. This means that python won't be able to
+B<3.> collectd handles I<SIGCHLD>. This means that Python won't be able to
 determine the return code of spawned processes with system(), popen() and
-subprocess. This will result in python not using external programs like less
+subprocess. This will result in Python not using external programs like less
 to display help texts. You can override this behavior with the B<PAGER>
 environment variable, e.g. I<export PAGER=less> before starting collectd.
-Depending on your version of python this might or might not result in an
+Depending on your version of Python this might or might not result in an
 B<OSError> exception which can be ignored.
 
-If you really need to spawn new processes from python you can register an init
+If you really need to spawn new processes from Python you can register an init
 callback and reset the action for SIGCHLD to the default behavior. Please note
 that this I<will> break the exec plugin. Do not even load the exec plugin if
 you intend to do this!
 
 There is an example script located in B<contrib/python/getsigchld.py>  to do
 this. If you import this from I<collectd.conf> SIGCHLD will be handled
-normally and spawning processes from python will work as intended.
+normally and spawning processes from Python will work as intended.
 
 =back
 
@@ -139,22 +139,22 @@ The I<name> identifies the callback.
 
 =head1 STRINGS
 
-There are a lot of places where strings are send from collectd to python and
-from python to collectd. How exactly this works depends on wheather byte or
-unicode strings or python2 or python3 are used.
+There are a lot of places where strings are sent from collectd to Python and
+from Python to collectd. How exactly this works depends on whether byte or
+unicode strings or Python2 or Python3 are used.
 
 Python2 has I<str>, which is just bytes, and I<unicode>. Python3 has I<str>,
 which is a unicode object, and I<bytes>.
 
-When passing strings from python to collectd all of these object are supported
+When passing strings from Python to collectd all of these object are supported
 in all places, however I<str> should be used if possible. These strings must
 not contain a NUL byte. Ignoring this will result in a I<TypeError> exception.
 If a byte string was used it will be used as is by collectd. If a unicode
 object was used it will be encoded using the default encoding (see above). If
-this is not possible python will raise a I<UnicodeEncodeError> exception.
+this is not possible Python will raise a I<UnicodeEncodeError> exception.
 
-Wenn passing strings from collectd to python the behavior depends on the
-python version used. Python2 will always receive a I<str> object. Python3 will
+When passing strings from collectd to Python the behavior depends on the
+Python version used. Python2 will always receive a I<str> object. Python3 will
 usually receive a I<str> object as well, however the original string will be
 decoded to unicode using the default encoding. If this fails because the
 string is not a valid sequence for this encoding a I<bytes> object will be
@@ -174,7 +174,7 @@ example. The following types of B<callback functions> are known to collectd
 
 =item configuration functions
 
-This type of functions is called during configuration if an appropriate
+These are called during configuration if an appropriate
 B<Module> block has been encountered. It is called once for each B<Module>
 block which matches the name of the callback as provided with the
 B<register_config> method - see below.
@@ -184,14 +184,14 @@ threading functions here!
 
 =item init functions
 
-This type of functions is called once after loading the module and before any
+These are called once after loading the module and before any
 calls to the read and write functions. It should be used to initialize the
 internal state of the plugin (e.E<nbsp>g. open sockets, ...). This is the
 earliest point where you may use threads.
 
 =item read functions
 
-This type of function is used to collect the actual data. It is called once
+These are used to collect the actual data. It is called once
 per interval (see the B<Interval> configuration option of collectd). Usually
 it will call B<plugin_dispatch_values> to dispatch the values to collectd
 which will pass them on to all registered B<write functions>. If this function
@@ -200,23 +200,23 @@ amount of time until it returns normally again.
 
 =item write functions
 
-This type of function is used to write the dispatched values. It is called
+These are used to write the dispatched values. It is called
 once for every value that was dispatched by any plugin.
 
 =item flush functions
 
-This type of function is used to flush internal caches of plugins. It is
+These are 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
+These are used to pass messages of plugins or the daemon itself
 to the user.
 
 =item notification function
 
-This type of function is used to act upon notifications. In general, a
+These are used to act upon notifications. In general, a
 notification is a status message that may be associated with a data instance.
 Usually, a notification is generated by the daemon if a configured threshold
 has been exceeded (see the section "THRESHOLD CONFIGURATION" in
@@ -225,12 +225,12 @@ notifications as well.
 
 =item shutdown functions
 
-This type of function is called once before the daemon shuts down. It should
+These are called once before the daemon shuts down. It should
 be used to clean up the plugin (e.g. close sockets, ...).
 
 =back
 
-Any function (except log functions) may set throw an exception in case of any
+Any function (except log functions) may throw an exception in case of
 errors. The exception will be passed on to the user using collectd's logging
 mechanism. If a log callback throws an exception it will be printed to standard
 error instead.
@@ -306,7 +306,7 @@ config file. It will always be a string.
 This is a tuple (which might be empty) of all value, i.e. words following the
 keyword in any given line in the config file.
 
-Every item in this tuple will be either a string or a float or a boolean,
+Every item in this tuple will be either a string, a float or a boolean,
 depending on the contents of the configuration file.
 
 =item children
@@ -367,7 +367,7 @@ Type instance string. May be empty.
 
 =head2 Values
 
-A Value is an object which features a sequence of values. It is based on then
+A Value is an object which features a sequence of values. It is based on the
 I<PluginData> type and uses its members to identify the values.
 
  class Values(PluginData)
@@ -495,7 +495,7 @@ Data descriptors defined here:
 
 =item message
 
-Some kind of description what's going on and why this Notification was
+Some kind of description of what's going on and why this Notification was
 generated.
 
 =item severity
@@ -534,9 +534,12 @@ your callback, not even None.
 I<name> is an optional identifier for this callback. The default name is
 B<python>.I<module>. I<module> is taken from the B<__module__> attribute of
 your callback function. Every callback needs a unique identifier, so if you
-want to register the same callback multiple time in the same module you need to
-specify a name here. Otherwise it's save to ignore this parameter I<identifier>
-is the full identifier assigned to this callback.
+want to register the same callback multiple times in the same module you need to
+specify a name here. Otherwise it's safe to ignore this parameter.
+
+=item
+
+I<identifier> is the full identifier assigned to this callback.
 
 =back
 
@@ -549,7 +552,7 @@ L<"WRITING YOUR OWN PLUGINS"> above) and are passed the following arguments:
 
 The only argument passed is a I<Config> object. See above for the layout of this
 data type.
-Note that you can not receive the whole config files this way, only B<Module>
+Note that you cannot receive the whole config files this way, only B<Module>
 blocks inside the Python configuration block. Additionally you will only
 receive blocks where your callback identifier matches B<python.>I<blockname>.
 
@@ -570,7 +573,7 @@ The callback will be called without arguments.
 
 =item register_write
 
-The callback function will be called with one arguments passed, which will be a
+The callback function will be called with one argument passed, which will be a
 I<Values> object. For the layout of I<Values> see above.
 If this callback function throws an exception the next call will be delayed by
 an increasing interval.
@@ -657,7 +660,7 @@ types used by the read, write and match functions.
 
 =item
 
-Please feel free to send in new plugins to collectd's mailinglist at
+Please feel free to send in new plugins to collectd's mailing list at
 E<lt>collectdE<nbsp>atE<nbsp>verplant.orgE<gt> for review and, possibly,
 inclusion in the main distribution. In the latter case, we will take care of
 keeping the plugin up to date and adapting it to new versions of collectd.
@@ -673,7 +676,7 @@ L<http://collectd.org/dev-info.shtml>.
 
 =item
 
-collectd is heavily multi-threaded. Each collectd thread accessing the python
+collectd is heavily multi-threaded. Each collectd thread accessing the Python
 plugin will be mapped to a Python interpreter thread. Any such thread will be
 created and destroyed transparently and on-the-fly.
 
@@ -703,7 +706,7 @@ dispatched by the python plugin after upgrades.
 
 =item
 
-Not all aspects of the collectd API are accessible from python. This includes
+Not all aspects of the collectd API are accessible from Python. This includes
 but is not limited to filters and data sets.
 
 =back
diff --git a/src/collectd-tg.c b/src/collectd-tg.c
new file mode 100644 (file)
index 0000000..e5b2d1f
--- /dev/null
@@ -0,0 +1,405 @@
+/**
+ * collectd-td - collectd traffic generator
+ * Copyright (C) 2010  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 Forster <ff at octo.it>
+ **/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200809L
+#endif
+
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 700
+#endif
+
+#if !__GNUC__
+# define __attribute__(x) /**/
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "utils_heap.h"
+
+#include "libcollectdclient/collectd/client.h"
+#include "libcollectdclient/collectd/network.h"
+#include "libcollectdclient/collectd/network_buffer.h"
+
+#define DEF_NUM_HOSTS    1000
+#define DEF_NUM_PLUGINS    20
+#define DEF_NUM_VALUES 100000
+#define DEF_INTERVAL       10
+
+static int conf_num_hosts = DEF_NUM_HOSTS;
+static int conf_num_plugins = DEF_NUM_PLUGINS;
+static int conf_num_values = DEF_NUM_VALUES;
+static int conf_interval = DEF_INTERVAL;
+static const char *conf_destination = NET_DEFAULT_V6_ADDR;
+static const char *conf_service = NET_DEFAULT_PORT;
+
+static lcc_network_t *net;
+
+static c_heap_t *values_heap = NULL;
+
+static struct sigaction sigint_action;
+static struct sigaction sigterm_action;
+
+static _Bool loop = 1;
+
+__attribute__((noreturn))
+static void exit_usage (int exit_status) /* {{{ */
+{
+  fprintf ((exit_status == EXIT_FAILURE) ? stderr : stdout,
+      "collectd-tg -- collectd traffic generator\n"
+      "\n"
+      "  Usage: collectd-ng [OPTION]\n"
+      "\n"
+      "  Valid options:\n"
+      "    -n <number>    Number of value lists. (Default: %i)\n"
+      "    -H <number>    Number of hosts to emulate. (Default: %i)\n"
+      "    -p <number>    Number of plugins to emulate. (Default: %i)\n"
+      "    -i <seconds>   Interval of each value in seconds. (Default: %i)\n"
+      "    -d <dest>      Destination address of the network packets.\n"
+      "                   (Default: %s)\n"
+      "    -D <port>      Destination port of the network packets.\n"
+      "                   (Default: %s)\n"
+      "    -h             Print usage information (this output).\n"
+      "\n"
+      "Copyright (C) 2010  Florian Forster\n"
+      "Licensed under the GNU General Public License, version 2 (GPLv2)\n",
+      DEF_NUM_VALUES, DEF_NUM_HOSTS, DEF_NUM_PLUGINS,
+      DEF_INTERVAL,
+      NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT);
+  exit (exit_status);
+} /* }}} void exit_usage */
+
+static void signal_handler (int signal) /* {{{ */
+{
+  loop = 0;
+} /* }}} void signal_handler */
+
+static int compare_time (const void *v0, const void *v1) /* {{{ */
+{
+  const lcc_value_list_t *vl0 = v0;
+  const lcc_value_list_t *vl1 = v1;
+
+  if (vl0->time < vl1->time)
+    return (-1);
+  else if (vl0->time > vl1->time)
+    return (1);
+  else
+    return (0);
+} /* }}} int compare_time */
+
+static int get_boundet_random (int min, int max) /* {{{ */
+{
+  int range;
+
+  if (min >= max)
+    return (-1);
+  if (min == (max - 1))
+    return (min);
+
+  range = max - min;
+
+  return (min + ((int) (((double) range) * ((double) random ()) / (((double) RAND_MAX) + 1.0))));
+} /* }}} int get_boundet_random */
+
+static lcc_value_list_t *create_value_list (void) /* {{{ */
+{
+  lcc_value_list_t *vl;
+  int host_num;
+
+  vl = malloc (sizeof (*vl));
+  if (vl == NULL)
+  {
+    fprintf (stderr, "malloc failed.\n");
+    return (NULL);
+  }
+  memset (vl, 0, sizeof (*vl));
+
+  vl->values = calloc (/* nmemb = */ 1, sizeof (*vl->values));
+  if (vl->values == NULL)
+  {
+    fprintf (stderr, "calloc failed.\n");
+    free (vl);
+    return (NULL);
+  }
+
+  vl->values_types = calloc (/* nmemb = */ 1, sizeof (*vl->values_types));
+  if (vl->values_types == NULL)
+  {
+    fprintf (stderr, "calloc failed.\n");
+    free (vl->values);
+    free (vl);
+    return (NULL);
+  }
+
+  vl->values_len = 1;
+
+  host_num = get_boundet_random (0, conf_num_hosts);
+
+  vl->interval = conf_interval;
+  vl->time = time (NULL) + (host_num % vl->interval) + 1;
+
+  if (get_boundet_random (0, 2) == 0)
+    vl->values_types[0] = LCC_TYPE_GAUGE;
+  else
+    vl->values_types[0] = LCC_TYPE_DERIVE;
+
+  snprintf (vl->identifier.host, sizeof (vl->identifier.host),
+      "host%04i", host_num);
+  snprintf (vl->identifier.plugin, sizeof (vl->identifier.plugin),
+      "plugin%03i", get_boundet_random (0, conf_num_plugins));
+  strncpy (vl->identifier.type,
+      (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive",
+      sizeof (vl->identifier.type));
+  snprintf (vl->identifier.type_instance, sizeof (vl->identifier.type_instance),
+      "ti%li", random ());
+
+  return (vl);
+} /* }}} int create_value_list */
+
+static void destroy_value_list (lcc_value_list_t *vl) /* {{{ */
+{
+  if (vl == NULL)
+    return;
+
+  free (vl->values);
+  free (vl->values_types);
+  free (vl);
+} /* }}} void destroy_value_list */
+
+static int send_value (lcc_value_list_t *vl) /* {{{ */
+{
+  int status;
+
+  if (vl->values_types[0] == LCC_TYPE_GAUGE)
+    vl->values[0].gauge = 100.0 * ((gauge_t) random ()) / (((gauge_t) RAND_MAX) + 1.0);
+  else
+    vl->values[0].derive += get_boundet_random (0, 100);
+
+  status = lcc_network_values_send (net, vl);
+  if (status != 0)
+    fprintf (stderr, "lcc_network_values_send failed with status %i.\n", status);
+
+  vl->time += vl->interval;
+
+  return (0);
+} /* }}} int send_value */
+
+static int get_integer_opt (const char *str, int *ret_value) /* {{{ */
+{
+  char *endptr;
+  int tmp;
+
+  errno = 0;
+  endptr = NULL;
+  tmp = (int) strtol (str, &endptr, /* base = */ 0);
+  if (errno != 0)
+  {
+    fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
+        str, strerror (errno));
+    exit (EXIT_FAILURE);
+  }
+  else if (endptr == str)
+  {
+    fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
+    exit (EXIT_FAILURE);
+  }
+  else if (*endptr != 0)
+  {
+    fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
+    exit (EXIT_FAILURE);
+  }
+
+  *ret_value = tmp;
+  return (0);
+} /* }}} int get_integer_opt */
+
+static int read_options (int argc, char **argv) /* {{{ */
+{
+  int opt;
+
+  while ((opt = getopt (argc, argv, "n:H:p:i:d:D:h")) != -1)
+  {
+    switch (opt)
+    {
+      case 'n':
+        get_integer_opt (optarg, &conf_num_values);
+        break;
+
+      case 'H':
+        get_integer_opt (optarg, &conf_num_hosts);
+        break;
+
+      case 'p':
+        get_integer_opt (optarg, &conf_num_plugins);
+        break;
+
+      case 'i':
+        get_integer_opt (optarg, &conf_interval);
+        break;
+
+      case 'd':
+        conf_destination = optarg;
+        break;
+
+      case 'D':
+        conf_service = optarg;
+        break;
+
+      case 'h':
+        exit_usage (EXIT_SUCCESS);
+
+      default:
+        exit_usage (EXIT_FAILURE);
+    } /* switch (opt) */
+  } /* while (getopt) */
+
+  return (0);
+} /* }}} int read_options */
+
+int main (int argc, char **argv) /* {{{ */
+{
+  int i;
+  time_t last_time;
+  int values_sent = 0;
+
+  read_options (argc, argv);
+
+  sigint_action.sa_handler = signal_handler;
+  sigaction (SIGINT, &sigint_action, /* old = */ NULL);
+
+  sigterm_action.sa_handler = signal_handler;
+  sigaction (SIGTERM, &sigterm_action, /* old = */ NULL);
+
+
+  values_heap = c_heap_create (compare_time);
+  if (values_heap == NULL)
+  {
+    fprintf (stderr, "c_heap_create failed.\n");
+    exit (EXIT_FAILURE);
+  }
+
+  net = lcc_network_create ();
+  if (net == NULL)
+  {
+    fprintf (stderr, "lcc_network_create failed.\n");
+    exit (EXIT_FAILURE);
+  }
+  else
+  {
+    lcc_server_t *srv;
+    
+    srv = lcc_server_create (net, conf_destination, conf_service);
+    if (srv == NULL)
+    {
+      fprintf (stderr, "lcc_server_create failed.\n");
+      exit (EXIT_FAILURE);
+    }
+
+    lcc_server_set_ttl (srv, 42);
+#if 0
+    lcc_server_set_security_level (srv, ENCRYPT,
+        "admin", "password1");
+#endif
+  }
+
+  fprintf (stdout, "Creating %i values ... ", conf_num_values);
+  fflush (stdout);
+  for (i = 0; i < conf_num_values; i++)
+  {
+    lcc_value_list_t *vl;
+
+    vl = create_value_list ();
+    if (vl == NULL)
+    {
+      fprintf (stderr, "create_value_list failed.\n");
+      exit (EXIT_FAILURE);
+    }
+
+    c_heap_insert (values_heap, vl);
+  }
+  fprintf (stdout, "done\n");
+
+  last_time = 0;
+  while (loop)
+  {
+    lcc_value_list_t *vl = c_heap_get_root (values_heap);
+
+    if (vl == NULL)
+      break;
+
+    if (vl->time != last_time)
+    {
+      printf ("%i values have been sent.\n", values_sent);
+
+      /* Check if we need to sleep */
+      time_t now = time (NULL);
+
+      while (now < vl->time)
+      {
+        /* 1 / 100 second */
+        struct timespec ts = { 0, 10000000 };
+        nanosleep (&ts, /* remaining = */ NULL);
+        now = time (NULL);
+
+        if (!loop)
+          break;
+      }
+      last_time = vl->time;
+    }
+
+    send_value (vl);
+    values_sent++;
+
+    c_heap_insert (values_heap, vl);
+  }
+
+  fprintf (stdout, "Shutting down.\n");
+  fflush (stdout);
+
+  while (42)
+  {
+    lcc_value_list_t *vl = c_heap_get_root (values_heap);
+    if (vl == NULL)
+      break;
+    destroy_value_list (vl);
+  }
+  c_heap_destroy (values_heap);
+
+  lcc_network_destroy (net);
+  exit (EXIT_SUCCESS);
+  return (0);
+} /* }}} int main */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
index 915560c0cc80490ba61100b146b1a86b8c42832a..d96d676790d0d8c730610484637c9322e2a27b5b 100644 (file)
@@ -79,6 +79,7 @@ static void sig_usr1_handler (int __attribute__((unused)) signal)
        pthread_attr_init (&attr);
        pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
        pthread_create (&thread, &attr, do_flush, NULL);
+       pthread_attr_destroy (&attr);
 }
 
 static int init_hostname (void)
@@ -137,28 +138,10 @@ static int init_hostname (void)
 
 static int init_global_variables (void)
 {
-       const char *str;
-
-       str = global_option_get ("Interval");
-       if (str == NULL)
-       {
-               interval_g = TIME_T_TO_CDTIME_T (10);
-       }
-       else
-       {
-               double tmp;
-
-               tmp = atof (str);
-               if (tmp <= 0.0)
-               {
-                       fprintf (stderr, "Cannot set the interval to a "
-                                       "correct value.\n"
-                                       "Please check your settings.\n");
-                       return (-1);
-               }
+       char const *str;
 
-               interval_g = DOUBLE_TO_CDTIME_T (tmp);
-       }
+       interval_g = cf_get_default_interval ();
+       assert (interval_g > 0);
        DEBUG ("interval_g = %.3f;", CDTIME_T_TO_DOUBLE (interval_g));
 
        str = global_option_get ("Timeout");
@@ -326,9 +309,10 @@ static int do_init (void)
 
 static int do_loop (void)
 {
+       cdtime_t interval = cf_get_default_interval ();
        cdtime_t wait_until;
 
-       wait_until = cdtime () + interval_g;
+       wait_until = cdtime () + interval;
 
        while (loop == 0)
        {
@@ -348,12 +332,12 @@ static int do_loop (void)
                        WARNING ("Not sleeping because the next interval is "
                                        "%.3f seconds in the past!",
                                        CDTIME_T_TO_DOUBLE (now - wait_until));
-                       wait_until = now + interval_g;
+                       wait_until = now + interval;
                        continue;
                }
 
                CDTIME_T_TO_TIMESPEC (wait_until - now, &ts_wait);
-               wait_until = wait_until + interval_g;
+               wait_until = wait_until + interval;
 
                while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) != 0))
                {
@@ -471,6 +455,8 @@ int main (int argc, char **argv)
        if (optind < argc)
                exit_usage (1);
 
+       plugin_init_ctx ();
+
        /*
         * Read options from the config file, the environment and the command
         * line (in that order, with later options overwriting previous ones in
index 7f3e780f2d911f5fc44966e9a532191f920d549f..9f5dedcdf0ad64d38566212bb53fdb4f48f03fea 100644 (file)
@@ -52,6 +52,7 @@
 # to missing dependencies or because they have been deactivated explicitly.  #
 ##############################################################################
 
+#@BUILD_PLUGIN_AGGREGATION_TRUE@LoadPlugin aggregation
 #@BUILD_PLUGIN_AMQP_TRUE@LoadPlugin amqp
 #@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
 #@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
 # ription of those options is available in the collectd.conf(5) manual page. #
 ##############################################################################
 
+#<Plugin "aggregation">
+#  <Aggregation>
+#    #Host "unspecified"
+#    Plugin "cpu"
+#    #PluginInstance "unspecified"
+#    Type "cpu"
+#    #TypeInstance "unspecified"
+#
+#    GroupBy "Host"
+#    GroupBy "TypeInstance"
+#
+#    CalculateNum false
+#    CalculateSum false
+#    CalculateAverage true
+#    CalculateMinimum false
+#    CalculateMaximum false
+#    CalculateStddev false
+#  </Aggregation>
+#</Plugin>
+
 #<Plugin "amqp">
 #  <Publish "name">
 #    Host "localhost"
 #      Host "localhost"
 #      Port 123
 #      ReverseLookups false
+#      IncludeUnitID true
 #</Plugin>
 
 #<Plugin nut>
index 774d918b4aa5f0384b264a1561b631111a2188b5..417af0d7082e09147de1777ec90e803a19b6fee5 100644 (file)
@@ -96,6 +96,12 @@ By default, this is disabled. As a special exception, if the plugin name is
 either C<perl> or C<python>, the default is changed to enabled in order to keep
 the average user from ever having to deal with this low level linking stuff.
 
+=item B<Interval> I<Seconds>
+
+Sets a plugin-specific interval for collecting metrics. This overrides the
+global B<Interval> setting. If a plugin provides own support for specifying an
+interval, that setting will take precedence.
+
 =back
 
 =item B<Include> I<Path>
@@ -193,12 +199,122 @@ C<Plugin>-Section. Which options exist depends on the plugin used. Some plugins
 require external configuration, too. The C<apache plugin>, for example,
 required C<mod_status> to be configured in the webserver you're going to
 collect data from. These plugins are listed below as well, even if they don't
-require any configuration within collectd's configfile.
+require any configuration within collectd's configuration file.
 
 A list of all plugins and a short summary for each plugin can be found in the
 F<README> file shipped with the sourcecode and hopefully binary packets as
 well.
 
+=head2 Plugin C<aggregation>
+
+The I<Aggregation plugin> makes it possible to aggregate several values into
+one using aggregation functions such as I<sum>, I<average>, I<min> and I<max>.
+This can be put to a wide variety of uses, e.g. average and total CPU
+statistics for your entire fleet.
+
+The grouping is powerful but, as with many powerful tools, may be a bit
+difficult to wrap your head around. The grouping will therefore be
+demonstrated using an example: The average and sum of the CPU usage across
+all CPUs of each host is to be calculated.
+
+To select all the affected values for our example, set C<Plugin cpu> and
+C<Type cpu>. The other values are left unspecified, meaning "all values". The
+I<Host>, I<Plugin>, I<PluginInstance>, I<Type> and I<TypeInstance> options
+work as if they were specified in the C<WHERE> clause of an C<SELECT> SQL
+statement.
+
+  Plugin "cpu"
+  Type "cpu"
+
+Although the I<Host>, I<PluginInstance> (CPU number, i.e. 0, 1, 2, ...)  and
+I<TypeInstance> (idle, user, system, ...) fields are left unspecified in the
+example, the intention is to have a new value for each host / type instance
+pair. This is achieved by "grouping" the values using the C<GroupBy> option.
+It can be specified multiple times to group by more than one field.
+
+  GroupBy "Host"
+  GroupBy "TypeInstance"
+
+We do neither specify nor group by I<plugin instance> (the CPU number), so all
+metrics that differ in the CPU number only will be aggregated. Each
+aggregation needs I<at least one> such field, otherwise no aggregation would
+take place.
+
+The full example configuration looks like this:
+
+ <Plugin "aggregation">
+   <Aggregation>
+     Plugin "cpu"
+     Type "cpu"
+     
+     GroupBy "Host"
+     GroupBy "TypeInstance"
+     
+     CalculateSum true
+     CalculateAverage true
+   </Aggregation>
+ </Plugin>
+
+There are a couple of limitations you should be aware of:
+
+=over 4
+
+=item
+
+The I<Type> cannot be left unspecified, because it is not reasonable to add
+apples to oranges. Also, the internal lookup structure won't work if you try
+to group by type.
+
+=item
+
+There must be at least one unspecified, ungrouped field. Otherwise nothing
+will be aggregated.
+
+=back
+
+As you can see in the example above, each aggregation has its own
+B<Aggregation> block. You can have multiple aggregation blocks and aggregation
+blocks may match the same values, i.e. one value list can update multiple
+aggregations. The following options are valid inside B<Aggregation> blocks:
+
+=over 4
+
+=item B<Host> I<Host>
+
+=item B<Plugin> I<Plugin>
+
+=item B<PluginInstance> I<PluginInstance>
+
+=item B<Type> I<Type>
+
+=item B<TypeInstance> I<TypeInstance>
+
+Selects the value lists to be added to this aggregation. B<Type> must be a
+valid data set name, see L<types.db(5)> for details.
+
+=item B<GroupBy> B<Host>|B<Plugin>|B<PluginInstance>|B<TypeInstance>
+
+Group valued by the specified field. The B<GroupBy> option may be repeated to
+group by multiple fields.
+
+=item B<CalculateNum> B<true>|B<false>
+
+=item B<CalculateSum> B<true>|B<false>
+
+=item B<CalculateAverage> B<true>|B<false>
+
+=item B<CalculateMinimum> B<true>|B<false>
+
+=item B<CalculateMaximum> B<true>|B<false>
+
+=item B<CalculateStddev> B<true>|B<false>
+
+Boolean options for enabling calculation of the number of value lists, their
+sum, average, minimum, maximum andE<nbsp>/ or standard deviation. All options
+are disabled by default.
+
+=back
+
 =head2 Plugin C<amqp>
 
 The I<AMQMP plugin> can be used to communicate with other instances of
@@ -3094,6 +3210,16 @@ IP-address may be used in a filename it is recommended to disable reverse
 lookups. The default is to do reverse lookups to preserve backwards
 compatibility, though.
 
+=item B<IncludeUnitID> B<true>|B<false>
+
+When a peer is a refclock, include the unit ID in the I<type instance>.
+Defaults to B<false> for backward compatibility.
+
+If two refclock peers use the same driver and this is B<false>, the plugin will
+try to write simultaneous measurements from both to the same type instance.
+This will result in error messages in the log and only one set of measurements
+making it through.
+
 =back
 
 =head2 Plugin C<nut>
@@ -3501,6 +3627,13 @@ which are available in a PostgreSQL database or use future or special
 statistics provided by PostgreSQL without the need to upgrade your collectd
 installation.
 
+Starting with version 5.2, the C<postgresql> plugin supports writing data to
+PostgreSQL databases as well. This has been implemented in a generic way. You
+need to specify an SQL statement which will then be executed by collectd in
+order to write the data (see below for details). The benefit of that approach
+is that there is no fixed database layout. Rather, the layout may be optimized
+for the current setup.
+
 The B<PostgreSQL Documentation> manual can be found at
 L<http://www.postgresql.org/docs/manuals/>.
 
@@ -3530,6 +3663,11 @@ L<http://www.postgresql.org/docs/manuals/>.
       </Result>
     </Query>
 
+    <Writer sqlstore>
+      Statement "SELECT collectd_insert($1, $2, $3, $4, $5, $6, $7, $8, $9);"
+      StoreRates true
+    </Writer>
+
     <Database foo>
       Host "hostname"
       Port "5432"
@@ -3546,6 +3684,12 @@ L<http://www.postgresql.org/docs/manuals/>.
       Query backend # predefined
       Query rt36_tickets
     </Database>
+
+    <Database qux>
+      # ...
+      Writer sqlstore
+      CommitInterval 10
+    </Database>
   </Plugin>
 
 The B<Query> block defines one database query which may later be used by a
@@ -3594,6 +3738,11 @@ used, the parameter expands to "localhost".
 
 The name of the database of the current connection.
 
+=item I<instance>
+
+The name of the database plugin instance. See the B<Instance> option of the
+database specification below for details.
+
 =item I<username>
 
 The username used to connect to the database.
@@ -3707,6 +3856,101 @@ This query collects the on-disk size of the database in bytes.
 
 =back
 
+In addition, the following detailed queries are available by default. Please
+note that each of those queries collects information B<by table>, thus,
+potentially producing B<a lot> of data. For details see the description of the
+non-by_table queries above.
+
+=over 4
+
+=item B<queries_by_table>
+
+=item B<query_plans_by_table>
+
+=item B<table_states_by_table>
+
+=item B<disk_io_by_table>
+
+=back
+
+The B<Writer> block defines a PostgreSQL writer backend. It accepts a single
+mandatory argument specifying the name of the writer. This will then be used
+in the B<Database> specification in order to activate the writer instance. The
+names of all writers have to be unique. The following options may be
+specified:
+
+=over 4
+
+=item B<Statement> I<sql statement>
+
+This mandatory option specifies the SQL statement that will be executed for
+each submitted value. A single SQL statement is allowed only. Anything after
+the first semicolon will be ignored.
+
+Nine parameters will be passed to the statement and should be specified as
+tokens B<$1>, B<$2>, through B<$9> in the statement string. The following
+values are made available through those parameters:
+
+=over 4
+
+=item B<$1>
+
+The timestamp of the queried value as a floating point number.
+
+=item B<$2>
+
+The hostname of the queried value.
+
+=item B<$3>
+
+The plugin name of the queried value.
+
+=item B<$4>
+
+The plugin instance of the queried value. This value may be B<NULL> if there
+is no plugin instance.
+
+=item B<$5>
+
+The type of the queried value (cf. L<types.db(5)>).
+
+=item B<$6>
+
+The type instance of the queried value. This value may be B<NULL> if there is
+no type instance.
+
+=item B<$7>
+
+An array of names for the submitted values (i.E<nbsp>e., the name of the data
+sources of the submitted value-list).
+
+=item B<$8>
+
+An array of types for the submitted values (i.E<nbsp>e., the type of the data
+sources of the submitted value-list; C<counter>, C<gauge>, ...). Note, that if
+B<StoreRates> is enabled (which is the default, see below), all types will be
+C<gauge>.
+
+=item B<$9>
+
+An array of the submitted values. The dimensions of the value name and value
+arrays match.
+
+=back
+
+In general, it is advisable to create and call a custom function in the
+PostgreSQL database for this purpose. Any procedural language supported by
+PostgreSQL will do (see chapter "Server Programming" in the PostgreSQL manual
+for details).
+
+=item B<StoreRates> B<false>|B<true>
+
+If set to B<true> (the default), convert counter values to rates. If set to
+B<false> counter values are stored as is, i.E<nbsp>e. as an increasing integer
+number.
+
+=back
+
 The B<Database> block defines one PostgreSQL database for which to collect
 statistics. It accepts a single mandatory argument which specifies the
 database name. None of the other options are required. PostgreSQL will use
@@ -3722,6 +3966,17 @@ for details.
 Specify the interval with which the database should be queried. The default is
 to use the global B<Interval> setting.
 
+=item B<CommitInterval> I<seconds>
+
+This option may be used for database connections which have "writers" assigned
+(see above). If specified, it causes a writer to put several updates into a
+single transaction. This transaction will last for the specified amount of
+time. By default, each update will be executed in a separate transaction. Each
+transaction generates a fair amount of overhead which can, thus, be reduced by
+activating this option. The draw-back is, that data covering the specified
+amount of time will be lost, for example, if a single statement within the
+transaction fails or if the database server crashes.
+
 =item B<Host> I<hostname>
 
 Specify the hostname or IP of the PostgreSQL server to connect to. If the
@@ -3752,6 +4007,13 @@ Specify the password to be used when connecting to the server.
 Specify whether to use an SSL connection when contacting the server. The
 following modes are supported:
 
+=item B<Instance> I<name>
+
+Specify the plugin instance name that should be used instead of the database
+name (which is the default, if this option has not been specified). This
+allows to query multiple databases of the same name on the same host (e.g.
+when running multiple database server versions in parallel).
+
 =over 4
 
 =item I<disable>
@@ -3787,11 +4049,36 @@ B<PostgreSQL Documentation> for details.
 
 =item B<Query> I<query>
 
-Specify a I<query> which should be executed for the database connection. This
-may be any of the predefined or user-defined queries. If no such option is
-given, it defaults to "backends", "transactions", "queries", "query_plans",
-"table_states", "disk_io" and "disk_usage". Else, the specified queries are
-used only.
+Specifies a I<query> which should be executed in the context of the database
+connection. This may be any of the predefined or user-defined queries. If no
+such option is given, it defaults to "backends", "transactions", "queries",
+"query_plans", "table_states", "disk_io" and "disk_usage" (unless a B<Writer>
+has been specified). Else, the specified queries are used only.
+
+=item B<Writer> I<writer>
+
+Assigns the specified I<writer> backend to the database connection. This
+causes all collected data to be send to the database using the settings
+defined in the writer configuration (see the section "FILTER CONFIGURATION"
+below for details on how to selectively send data to certain plugins).
+
+Each writer will register a flush callback which may be used when having long
+transactions enabled (see the B<CommitInterval> option above). When issuing
+the B<FLUSH> command (see L<collectd-unixsock(5)> for details) the current
+transaction will be committed right away. Two different kinds of flush
+callbacks are available with the C<postgresql> plugin:
+
+=over 4
+
+=item B<postgresql>
+
+Flush all writer backends.
+
+=item B<postgresql->I<database>
+
+Flush all writers of the specified I<database> only.
+
+=back
 
 =back
 
@@ -4173,6 +4460,50 @@ Enables or disables the creation of RRD files. If the daemon is not running
 locally, or B<DataDir> is set to a relative path, this will not work as
 expected. Default is B<true>.
 
+=item B<StepSize> I<Seconds>
+
+B<Force> the stepsize of newly created RRD-files. Ideally (and per default)
+this setting is unset and the stepsize is set to the interval in which the data
+is collected. Do not use this option unless you absolutely have to for some
+reason. Setting this option may cause problems with the C<snmp plugin>, the
+C<exec plugin> or when the daemon is set up to receive data from other hosts.
+
+=item B<HeartBeat> I<Seconds>
+
+B<Force> the heartbeat of newly created RRD-files. This setting should be unset
+in which case the heartbeat is set to twice the B<StepSize> which should equal
+the interval in which data is collected. Do not set this option unless you have
+a very good reason to do so.
+
+=item B<RRARows> I<NumRows>
+
+The C<rrdtool plugin> calculates the number of PDPs per CDP based on the
+B<StepSize>, this setting and a timespan. This plugin creates RRD-files with
+three times five RRAs, i. e. five RRAs with the CFs B<MIN>, B<AVERAGE>, and
+B<MAX>. The five RRAs are optimized for graphs covering one hour, one day, one
+week, one month, and one year.
+
+So for each timespan, it calculates how many PDPs need to be consolidated into
+one CDP by calculating:
+  number of PDPs = timespan / (stepsize * rrarows)
+
+Bottom line is, set this no smaller than the width of you graphs in pixels. The
+default is 1200.
+
+=item B<RRATimespan> I<Seconds>
+
+Adds an RRA-timespan, given in seconds. Use this option multiple times to have
+more then one RRA. If this option is never used, the built-in default of (3600,
+86400, 604800, 2678400, 31622400) is used.
+
+For more information on how RRA-sizes are calculated see B<RRARows> above.
+
+=item B<XFF> I<Factor>
+
+Set the "XFiles Factor". The default is 0.1. If unsure, don't set this option.
+I<Factor> must be in the range C<[0.0-1.0)>, i.e. between zero (inclusive) and
+one (exclusive).
+
 =back
 
 =head2 Plugin C<rrdtool>
@@ -4230,6 +4561,8 @@ For more information on how RRA-sizes are calculated see B<RRARows> above.
 =item B<XFF> I<Factor>
 
 Set the "XFiles Factor". The default is 0.1. If unsure, don't set this option.
+I<Factor> must be in the range C<[0.0-1.0)>, i.e. between zero (inclusive) and
+one (exclusive).
 
 =item B<CacheFlush> I<Seconds>
 
index 4079ad1f2872c4d70aa708f0c523b3aed1f517fb..c0994d19276e501a1170ed2cfde979c112eece79 100644 (file)
@@ -258,6 +258,10 @@ typedef int _Bool;
 # define COLLECTD_GRP_NAME "collectd"
 #endif
 
+#ifndef COLLECTD_DEFAULT_INTERVAL
+# define COLLECTD_DEFAULT_INTERVAL 10.0
+#endif
+
 #define STATIC_ARRAY_LEN(array) (sizeof (array) / sizeof ((array)[0]))
 
 /* Remove GNU specific __attribute__ settings when using another compiler */
index 61b7fc55e2c24ff880730580abf2d48e0e0f571d..0b8d0c1e89ee1547f955c0795bf388d1568eb518 100644 (file)
@@ -65,7 +65,7 @@
 # endif
 #endif /* NAN_ZERO_ZERO */
 
-#include "libcollectdclient/client.h"
+#include "libcollectdclient/collectd/client.h"
 
 #define DEFAULT_SOCK LOCALSTATEDIR"/run/"PACKAGE_NAME"-unixsock"
 
index 406482ad6a84c5d13642d4a0f8b5bb5909b01434..b679bf70f4ccff6a385c47b5fb4f47ccbd26a8b1 100644 (file)
@@ -636,24 +636,23 @@ long long get_kstat_value (kstat_t *ksp, char *name)
        kstat_named_t *kn;
        long long retval = -1LL;
 
-#ifdef assert
-       assert (ksp != NULL);
-       assert (ksp->ks_type == KSTAT_TYPE_NAMED);
-#else
        if (ksp == NULL)
        {
-               ERROR ("ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
+               ERROR ("get_kstat_value (\"%s\"): ksp is NULL.", name);
                return (-1LL);
        }
        else if (ksp->ks_type != KSTAT_TYPE_NAMED)
        {
-               ERROR ("ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
+               ERROR ("get_kstat_value (\"%s\"): ksp->ks_type (%#x) "
+                               "is not KSTAT_TYPE_NAMED (%#x).",
+                               name,
+                               (unsigned int) ksp->ks_type,
+                               (unsigned int) KSTAT_TYPE_NAMED);
                return (-1LL);
        }
-#endif
 
        if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
-               return (retval);
+               return (-1LL);
 
        if (kn->data_type == KSTAT_DATA_INT32)
                retval = (long long) kn->value.i32;
@@ -1230,7 +1229,102 @@ counter_t counter_diff (counter_t old_value, counter_t new_value)
        }
 
        return (diff);
-} /* counter_t counter_to_gauge */
+} /* counter_t counter_diff */
+
+int rate_to_value (value_t *ret_value, gauge_t rate, /* {{{ */
+               rate_to_value_state_t *state,
+               int ds_type, cdtime_t t)
+{
+       gauge_t delta_gauge;
+       cdtime_t delta_t;
+
+       if (ds_type == DS_TYPE_GAUGE)
+       {
+               state->last_value.gauge = rate;
+               state->last_time = t;
+
+               *ret_value = state->last_value;
+               return (0);
+       }
+
+       /* Counter and absolute can't handle negative rates. Reset "last time"
+        * to zero, so that the next valid rate will re-initialize the
+        * structure. */
+       if ((rate < 0.0)
+                       && ((ds_type == DS_TYPE_COUNTER)
+                               || (ds_type == DS_TYPE_ABSOLUTE)))
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       /* Another invalid state: The time is not increasing. */
+       if (t <= state->last_time)
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       delta_t = t - state->last_time;
+       delta_gauge = (rate * CDTIME_T_TO_DOUBLE (delta_t)) + state->residual;
+
+       /* Previous value is invalid. */
+       if (state->last_time == 0) /* {{{ */
+       {
+               if (ds_type == DS_TYPE_DERIVE)
+               {
+                       state->last_value.derive = (derive_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.derive);
+               }
+               else if (ds_type == DS_TYPE_COUNTER)
+               {
+                       state->last_value.counter = (counter_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.counter);
+               }
+               else if (ds_type == DS_TYPE_ABSOLUTE)
+               {
+                       state->last_value.absolute = (absolute_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.absolute);
+               }
+               else
+               {
+                       assert (23 == 42);
+               }
+
+               state->last_time = t;
+               return (EAGAIN);
+       } /* }}} */
+
+       if (ds_type == DS_TYPE_DERIVE)
+       {
+               derive_t delta_derive = (derive_t) delta_gauge;
+
+               state->last_value.derive += delta_derive;
+               state->residual = delta_gauge - ((gauge_t) delta_derive);
+       }
+       else if (ds_type == DS_TYPE_COUNTER)
+       {
+               counter_t delta_counter = (counter_t) delta_gauge;
+
+               state->last_value.counter += delta_counter;
+               state->residual = delta_gauge - ((gauge_t) delta_counter);
+       }
+       else if (ds_type == DS_TYPE_ABSOLUTE)
+       {
+               absolute_t delta_absolute = (absolute_t) delta_gauge;
+
+               state->last_value.absolute = delta_absolute;
+               state->residual = delta_gauge - ((gauge_t) delta_absolute);
+       }
+       else
+       {
+               assert (23 == 42);
+       }
+
+        state->last_time = t;
+       *ret_value = state->last_value;
+       return (0);
+} /* }}} value_t rate_to_value */
 
 int service_name_to_port_number (const char *service_name)
 {
index 6b11b538db8a69ef001e7e7b28843d519d9ebe6b..8a7d986534919efa65e449c17313882f363cce93 100644 (file)
                || (strcasecmp ("no", (s)) == 0) \
                || (strcasecmp ("off", (s)) == 0))
 
+struct rate_to_value_state_s
+{
+  value_t last_value;
+  cdtime_t last_time;
+  gauge_t residual;
+};
+typedef struct rate_to_value_state_s rate_to_value_state_t;
+
 char *sstrncpy (char *dest, const char *src, size_t n);
 int ssnprintf (char *dest, size_t n, const char *format, ...);
 char *sstrdup(const char *s);
@@ -292,6 +300,15 @@ int read_file_contents (const char *filename, char *buf, int bufsize);
 
 counter_t counter_diff (counter_t old_value, counter_t new_value);
 
+/* Convert a rate back to a value_t. When converting to a derive_t, counter_t
+ * or absoltue_t, take fractional residuals into account. This is important
+ * when scaling counters, for example.
+ * Returns zero on success. Returns EAGAIN when called for the first time; in
+ * this case the value_t is invalid and the next call should succeed. Other
+ * return values indicate an error. */
+int rate_to_value (value_t *ret_value, gauge_t rate,
+               rate_to_value_state_t *state, int ds_type, cdtime_t t);
+
 /* Converts a service name (a string) to a port number
  * (in the range [1-65535]). Returns less than zero on error. */
 int service_name_to_port_number (const char *service_name);
index 4fe50cc2e11ee71a00dcd53bd843460cf68a5973..5920c53129628c3691af816843aff70af8f55e5c 100644 (file)
@@ -46,6 +46,7 @@ typedef struct cf_callback
        int  (*callback) (const char *, const char *);
        const char **keys;
        int    keys_num;
+       plugin_ctx_t ctx;
        struct cf_callback *next;
 } cf_callback_t;
 
@@ -53,6 +54,7 @@ typedef struct cf_complex_callback_s
 {
        char *type;
        int (*callback) (oconfig_item_t *);
+       plugin_ctx_t ctx;
        struct cf_complex_callback_s *next;
 } cf_complex_callback_t;
 
@@ -96,7 +98,7 @@ static cf_global_option_t cf_global_options[] =
        {"PIDFile",     NULL, PIDFILE},
        {"Hostname",    NULL, NULL},
        {"FQDNLookup",  NULL, "true"},
-       {"Interval",    NULL, "10"},
+       {"Interval",    NULL, NULL},
        {"ReadThreads", NULL, "5"},
        {"Timeout",     NULL, "2"},
        {"PreCacheChain",  NULL, "PreCache"},
@@ -128,6 +130,7 @@ static int cf_dispatch (const char *type, const char *orig_key,
                const char *orig_value)
 {
        cf_callback_t *cf_cb;
+       plugin_ctx_t old_ctx;
        char *key;
        char *value;
        int ret;
@@ -156,6 +159,8 @@ static int cf_dispatch (const char *type, const char *orig_key,
 
        ret = -1;
 
+       old_ctx = plugin_set_ctx (cf_cb->ctx);
+
        for (i = 0; i < cf_cb->keys_num; i++)
        {
                if ((cf_cb->keys[i] != NULL)
@@ -166,6 +171,8 @@ static int cf_dispatch (const char *type, const char *orig_key,
                }
        }
 
+       plugin_set_ctx (old_ctx);
+
        if (i >= cf_cb->keys_num)
                WARNING ("Plugin `%s' did not register for value `%s'.", type, key);
 
@@ -244,6 +251,10 @@ static int dispatch_loadplugin (const oconfig_item_t *ci)
        int i;
        const char *name;
        unsigned int flags = 0;
+       plugin_ctx_t ctx;
+       plugin_ctx_t old_ctx;
+       int ret_val;
+
        assert (strcasecmp (ci->key, "LoadPlugin") == 0);
 
        if (ci->values_num != 1)
@@ -253,6 +264,10 @@ static int dispatch_loadplugin (const oconfig_item_t *ci)
 
        name = ci->values[0].value.string;
 
+       /* default to the global interval set before loading this plugin */
+       memset (&ctx, 0, sizeof (ctx));
+       ctx.interval = cf_get_default_interval ();
+
        /*
         * XXX: Magic at work:
         *
@@ -271,6 +286,16 @@ static int dispatch_loadplugin (const oconfig_item_t *ci)
        for (i = 0; i < ci->children_num; ++i) {
                if (strcasecmp("Globals", ci->children[i].key) == 0)
                        cf_util_get_flag (ci->children + i, &flags, PLUGIN_FLAGS_GLOBAL);
+               else if (strcasecmp ("Interval", ci->children[i].key) == 0) {
+                       double interval = 0.0;
+
+                       if (cf_util_get_double (ci->children + i, &interval) != 0) {
+                               /* cf_util_get_double will log an error */
+                               continue;
+                       }
+
+                       ctx.interval = DOUBLE_TO_CDTIME_T (interval);
+               }
                else {
                        WARNING("Ignoring unknown LoadPlugin option \"%s\" "
                                        "for plugin \"%s\"",
@@ -278,7 +303,12 @@ static int dispatch_loadplugin (const oconfig_item_t *ci)
                }
        }
 
-       return (plugin_load (name, (uint32_t) flags));
+       old_ctx = plugin_set_ctx (ctx);
+       ret_val = plugin_load (name, (uint32_t) flags);
+       /* reset to the "global" context */
+       plugin_set_ctx (old_ctx);
+
+       return (ret_val);
 } /* int dispatch_value_loadplugin */
 
 static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci)
@@ -357,8 +387,18 @@ static int dispatch_block_plugin (oconfig_item_t *ci)
 
        /* Check for a complex callback first */
        for (cb = complex_callback_head; cb != NULL; cb = cb->next)
+       {
                if (strcasecmp (name, cb->type) == 0)
-                       return (cb->callback (ci));
+               {
+                       plugin_ctx_t old_ctx;
+                       int ret_val;
+
+                       old_ctx = plugin_set_ctx (cb->ctx);
+                       ret_val = (cb->callback (ci));
+                       plugin_set_ctx (old_ctx);
+                       return (ret_val);
+               }
+       }
 
        /* Hm, no complex plugin found. Dispatch the values one by one */
        for (i = 0; i < ci->children_num; i++)
@@ -828,6 +868,29 @@ const char *global_option_get (const char *option)
                        : cf_global_options[i].def);
 } /* char *global_option_get */
 
+cdtime_t cf_get_default_interval (void)
+{
+  char const *str = global_option_get ("Interval");
+  double interval_double = COLLECTD_DEFAULT_INTERVAL;
+
+  if (str != NULL)
+  {
+    char *endptr = NULL;
+    double tmp = strtod (str, &endptr);
+
+    if ((endptr == NULL) || (endptr == str) || (*endptr != 0))
+      ERROR ("cf_get_default_interval: Unable to parse string \"%s\" "
+          "as number.", str);
+    else if (tmp <= 0.0)
+      ERROR ("cf_get_default_interval: Interval must be a positive number. "
+          "The current number is %g.", tmp);
+    else
+      interval_double = tmp;
+  }
+
+  return (DOUBLE_TO_CDTIME_T (interval_double));
+} /* }}} cdtime_t cf_get_default_interval */
+
 void cf_unregister (const char *type)
 {
        cf_callback_t *this, *prev;
@@ -884,6 +947,7 @@ void cf_register (const char *type,
        cf_cb->callback = callback;
        cf_cb->keys     = keys;
        cf_cb->keys_num = keys_num;
+       cf_cb->ctx      = plugin_get_ctx ();
 
        cf_cb->next = first_callback;
        first_callback = cf_cb;
@@ -907,6 +971,8 @@ int cf_register_complex (const char *type, int (*callback) (oconfig_item_t *))
        new->callback = callback;
        new->next = NULL;
 
+       new->ctx = plugin_get_ctx ();
+
        if (complex_callback_head == NULL)
        {
                complex_callback_head = new;
@@ -1015,6 +1081,23 @@ int cf_util_get_int (const oconfig_item_t *ci, int *ret_value) /* {{{ */
        return (0);
 } /* }}} int cf_util_get_int */
 
+int cf_util_get_double (const oconfig_item_t *ci, double *ret_value) /* {{{ */
+{
+       if ((ci == NULL) || (ret_value == NULL))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+       {
+               ERROR ("cf_util_get_double: The %s option requires "
+                               "exactly one numeric argument.", ci->key);
+               return (-1);
+       }
+
+       *ret_value = ci->values[0].value.number;
+
+       return (0);
+} /* }}} int cf_util_get_double */
+
 int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
 {
        if ((ci == NULL) || (ret_bool == NULL))
index fbeafff8b956b01451b3fc714ec3107305ca1577..5a719a421a66a25df0073d6bdc4f24e8cbe9ebac 100644 (file)
@@ -87,6 +87,8 @@ int cf_read (char *filename);
 int global_option_set (const char *option, const char *value);
 const char *global_option_get (const char *option);
 
+cdtime_t cf_get_default_interval (void);
+
 /* Assures the config option is a string, duplicates it and returns the copy in
  * "ret_string". If necessary "*ret_string" is freed first. Returns zero upon
  * success. */
@@ -100,6 +102,9 @@ int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer,
 /* Assures the config option is a number and returns it as an int. */
 int cf_util_get_int (const oconfig_item_t *ci, int *ret_value);
 
+/* Assures the config option is a number and returns it as a double. */
+int cf_util_get_double (const oconfig_item_t *ci, double *ret_value);
+
 /* Assures the config option is a boolean and assignes it to `ret_bool'.
  * Otherwise, `ret_bool' is not changed and non-zero is returned. */
 int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool);
index c207318f9d62425fbfb18e7e86727bce62b65bcb..344f76e76f0c29242ac20821d8efb25f83f12871 100644 (file)
 /* no global variables */
 /* #endif KERNEL_LINUX */
 
+#elif HAVE_PERFSTAT
+# include <sys/protosw.h>
+# include <libperfstat.h>
+/* #endif HAVE_PERFSTAT */
+
 #else
 # error "No applicable input method."
 #endif
@@ -121,7 +126,24 @@ static int cs_read (void)
 
        if (status == -2)
                ERROR ("contextswitch plugin: Unable to find context switch value.");
-#endif /* KERNEL_LINUX */
+/* #endif  KERNEL_LINUX */
+
+#elif HAVE_PERFSTAT
+       int status = 0;
+       perfstat_cpu_total_t perfcputotal;
+
+       status = perfstat_cpu_total(NULL, &perfcputotal, sizeof(perfstat_cpu_total_t), 1);
+       if (status < 0)
+       {
+               char errbuf[1024];
+               ERROR ("contextswitch plugin: perfstat_cpu_total: %s",
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       cs_submit(perfcputotal.pswitch);
+       status = 0;
+#endif /* defined(HAVE_PERFSTAT) */
 
        return status;
 }
index 12071a2ca8bae25cf86f25782a682ad1bf898846..5448003a52b3fc8a6777002ba5ede168dd6ff500 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -163,7 +163,7 @@ static int init (void)
        DEBUG ("host_processors returned %i %s", (int) cpu_list_len, cpu_list_len == 1 ? "processor" : "processors");
        INFO ("cpu plugin: Found %i processor%s.", (int) cpu_list_len, cpu_list_len == 1 ? "" : "s");
 
-       cpu_temp_retry_max = 86400 / CDTIME_T_TO_TIME_T (interval_g);
+       cpu_temp_retry_max = 86400 / CDTIME_T_TO_TIME_T (plugin_get_interval ());
 /* #endif PROCESSOR_CPU_LOAD_INFO */
 
 #elif defined(HAVE_LIBKSTAT)
index 052ea1e61244d92d509501ea0daf800f19f454f3..46e0d490967456156863413912fe234deeb5abda 100644 (file)
@@ -419,7 +419,8 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
   return (0);
 } /* }}} int cx_handle_instance_xpath */
 
-static int  cx_handle_base_xpath (char *plugin_instance, /* {{{ */
+static int  cx_handle_base_xpath (char const *plugin_instance, /* {{{ */
+    char const *host,
     xmlXPathContextPtr xpath_ctx, const data_set_t *ds, 
     char *base_xpath, cx_xpath_t *xpath)
 {
@@ -461,7 +462,7 @@ static int  cx_handle_base_xpath (char *plugin_instance, /* {{{ */
   vl.values_len = ds->ds_num;
   sstrncpy (vl.type, xpath->type, sizeof (vl.type));
   sstrncpy (vl.plugin, "curl_xml", sizeof (vl.plugin));
-  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.host, (host != NULL) ? host : hostname_g, sizeof (vl.host));
   if (plugin_instance != NULL)
     sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); 
 
@@ -504,7 +505,8 @@ static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
     ds = plugin_get_ds (xpath->type);
 
     if ( (cx_check_type(ds, xpath) == 0) &&
-         (cx_handle_base_xpath(db->instance, xpath_ctx, ds, le->key, xpath) == 0) )
+         (cx_handle_base_xpath(db->instance, db->host,
+                               xpath_ctx, ds, le->key, xpath) == 0) )
       status = 0; /* we got atleast one success */
 
     le = le->next;
index fde0dcde9abd5112c48633a6dcd588ef8bb0f69b..3728d556531619bcdf093ca758fbcf0b88fe71b2 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/disk.c
- * Copyright (C) 2005-2010  Florian octo Forster
+ * Copyright (C) 2005-2012  Florian octo Forster
  * Copyright (C) 2009       Manuel Sanmartin
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -17,7 +17,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Manuel Sanmartin
  **/
 
@@ -75,6 +75,9 @@
 
 #if HAVE_IOKIT_IOKITLIB_H
 static mach_port_t io_master_port = MACH_PORT_NULL;
+/* This defaults to false for backwards compatibility. Please fix in the next
+ * major version. */
+static _Bool use_bsd_name = 0;
 /* #endif HAVE_IOKIT_IOKITLIB_H */
 
 #elif KERNEL_LINUX
@@ -128,6 +131,7 @@ static int pnumdisk;
 static const char *config_keys[] =
 {
        "Disk",
+       "UseBSDName",
        "IgnoreSelected"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
@@ -152,6 +156,15 @@ static int disk_config (const char *key, const char *value)
       invert = 0;
     ignorelist_set_invert (ignorelist, invert);
   }
+  else if (strcasecmp ("UseBSDName", key) == 0)
+  {
+#if HAVE_IOKIT_IOKITLIB_H
+    use_bsd_name = IS_TRUE (value) ? 1 : 0;
+#else
+    WARNING ("disk plugin: The \"UseBSDName\" option is only supported "
+        "on Mach / Mac OS X and will be ignored.");
+#endif
+  }
   else
   {
     return (-1);
@@ -235,6 +248,17 @@ static void disk_submit (const char *plugin_instance,
        plugin_dispatch_values (&vl);
 } /* void disk_submit */
 
+#if KERNEL_LINUX
+static counter_t disk_calc_time_incr (counter_t delta_time, counter_t delta_ops)
+{
+       double interval = CDTIME_T_TO_DOUBLE (plugin_get_interval ());
+       double avg_time = ((double) delta_time) / ((double) delta_ops);
+       double avg_time_incr = interval * avg_time;
+
+       return ((counter_t) (avg_time_incr + .5));
+}
+#endif
+
 #if HAVE_IOKIT_IOKITLIB_H
 static signed long long dict_get_value (CFDictionaryRef dict, const char *key)
 {
@@ -281,7 +305,8 @@ static int disk_read (void)
        CFDictionaryRef         props_dict;
        CFDictionaryRef         stats_dict;
        CFDictionaryRef         child_dict;
-       kern_return_t           status;
+       CFStringRef             tmp_cf_string_ref;
+       kern_return_t           status;
 
        signed long long read_ops;
        signed long long read_byt;
@@ -292,7 +317,8 @@ static int disk_read (void)
 
        int  disk_major;
        int  disk_minor;
-       char disk_name[64];
+       char disk_name[DATA_MAX_NAME_LEN];
+       char disk_name_bsd[DATA_MAX_NAME_LEN];
 
        /* Get the list of all disk objects. */
        if (IOServiceGetMatchingServices (io_master_port,
@@ -340,12 +366,41 @@ static int disk_read (void)
                        continue;
                }
 
+               /* tmp_cf_string_ref doesn't need to be released. */
+               tmp_cf_string_ref = (CFStringRef) CFDictionaryGetValue (props_dict,
+                               CFSTR(kIOBSDNameKey));
+               if (!tmp_cf_string_ref)
+               {
+                       DEBUG ("disk plugin: CFDictionaryGetValue("
+                                       "kIOBSDNameKey) failed.");
+                       CFRelease (props_dict);
+                       IOObjectRelease (disk_child);
+                       IOObjectRelease (disk);
+                       continue;
+               }
+               assert (CFGetTypeID (tmp_cf_string_ref) == CFStringGetTypeID ());
+
+               memset (disk_name_bsd, 0, sizeof (disk_name_bsd));
+               CFStringGetCString (tmp_cf_string_ref,
+                               disk_name_bsd, sizeof (disk_name_bsd),
+                               kCFStringEncodingUTF8);
+               if (disk_name_bsd[0] == 0)
+               {
+                       ERROR ("disk plugin: CFStringGetCString() failed.");
+                       CFRelease (props_dict);
+                       IOObjectRelease (disk_child);
+                       IOObjectRelease (disk);
+                       continue;
+               }
+               DEBUG ("disk plugin: disk_name_bsd = \"%s\"", disk_name_bsd);
+
                stats_dict = (CFDictionaryRef) CFDictionaryGetValue (props_dict,
                                CFSTR (kIOBlockStorageDriverStatisticsKey));
 
                if (stats_dict == NULL)
                {
-                       DEBUG ("CFDictionaryGetValue (%s) failed.",
+                       DEBUG ("disk plugin: CFDictionaryGetValue ("
+                                       "%s) failed.",
                                        kIOBlockStorageDriverStatisticsKey);
                        CFRelease (props_dict);
                        IOObjectRelease (disk_child);
@@ -359,7 +414,8 @@ static int disk_read (void)
                                        kNilOptions)
                                != kIOReturnSuccess)
                {
-                       DEBUG ("IORegistryEntryCreateCFProperties (disk_child) failed.");
+                       DEBUG ("disk plugin: IORegistryEntryCreateCFProperties ("
+                                       "disk_child) failed.");
                        IOObjectRelease (disk_child);
                        CFRelease (props_dict);
                        IOObjectRelease (disk);
@@ -389,17 +445,12 @@ static int disk_read (void)
                write_tme = dict_get_value (stats_dict,
                                kIOBlockStorageDriverStatisticsTotalWriteTimeKey);
 
-               if (ssnprintf (disk_name, sizeof (disk_name),
-                               "%i-%i", disk_major, disk_minor) >= sizeof (disk_name))
-               {
-                       DEBUG ("snprintf (major, minor) failed.");
-                       CFRelease (child_dict);
-                       IOObjectRelease (disk_child);
-                       CFRelease (props_dict);
-                       IOObjectRelease (disk);
-                       continue;
-               }
-               DEBUG ("disk_name = %s", disk_name);
+               if (use_bsd_name)
+                       sstrncpy (disk_name, disk_name_bsd, sizeof (disk_name));
+               else
+                       ssnprintf (disk_name, sizeof (disk_name), "%i-%i",
+                                       disk_major, disk_minor);
+               DEBUG ("disk plugin: disk_name = \"%s\"", disk_name);
 
                if ((read_byt != -1LL) || (write_byt != -1LL))
                        disk_submit (disk_name, "disk_octets", read_byt, write_byt);
@@ -579,13 +630,11 @@ static int disk_read (void)
                                diff_write_time = write_time - ds->write_time;
 
                        if (diff_read_ops != 0)
-                               ds->avg_read_time += (diff_read_time
-                                               + (diff_read_ops / 2))
-                                       / diff_read_ops;
+                               ds->avg_read_time += disk_calc_time_incr (
+                                               diff_read_time, diff_read_ops);
                        if (diff_write_ops != 0)
-                               ds->avg_write_time += (diff_write_time
-                                               + (diff_write_ops / 2))
-                                       / diff_write_ops;
+                               ds->avg_write_time += disk_calc_time_incr (
+                                               diff_write_time, diff_write_ops);
 
                        ds->read_ops = read_ops;
                        ds->read_time = read_time;
index 95797f543ea67138e23c967645905354991e408e..fe3b672a21d844b2190c80e7b2f7bffd9adb9b50 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -228,7 +228,7 @@ static void *dns_child_loop (__attribute__((unused)) void *dummy)
        pcap_obj = pcap_open_live ((pcap_device != NULL) ? pcap_device : "any",
                        PCAP_SNAPLEN,
                        0 /* Not promiscuous */,
-                       (int) CDTIME_T_TO_MS (interval_g / 2),
+                       (int) CDTIME_T_TO_MS (plugin_get_interval () / 2),
                        pcap_error);
        if (pcap_obj == NULL)
        {
@@ -286,7 +286,7 @@ static int dns_init (void)
        if (listen_thread_init != 0)
                return (-1);
 
-       status = pthread_create (&listen_thread, NULL, dns_child_loop,
+       status = plugin_thread_create (&listen_thread, NULL, dns_child_loop,
                        (void *) 0);
        if (status != 0)
        {
index 5870ab1de1498593d4636973a65444d4e918d558..8f633cd1d93e75224209d71017f48ff302bfb241 100644 (file)
@@ -482,8 +482,8 @@ static void *open_connection (void __attribute__((unused)) *arg)
                        collectors[i] = (collector_t *)smalloc (sizeof (collector_t));
                        collectors[i]->socket = NULL;
 
-                       if (0 != (err = pthread_create (&collectors[i]->thread, &ptattr,
-                                                       collect, collectors[i]))) {
+                       if (0 != (err = plugin_thread_create (&collectors[i]->thread,
+                                                       &ptattr, collect, collectors[i]))) {
                                char errbuf[1024];
                                log_err ("pthread_create() failed: %s",
                                                sstrerror (errno, errbuf, sizeof (errbuf)));
@@ -558,7 +558,7 @@ static int email_init (void)
 {
        int err = 0;
 
-       if (0 != (err = pthread_create (&connector, NULL,
+       if (0 != (err = plugin_thread_create (&connector, NULL,
                                open_connection, NULL))) {
                char errbuf[1024];
                disabled = 1;
index ae1b68931d898830cca2a67e9aa2cc8034371803..08381a821087e275fb03922a32bc54c35ca7c3cd 100644 (file)
@@ -66,6 +66,7 @@ static int ethstat_add_interface (const oconfig_item_t *ci) /* {{{ */
   if (tmp == NULL)
     return (-1);
   interfaces = tmp;
+  interfaces[interfaces_num] = NULL;
 
   status = cf_util_get_string (ci, interfaces + interfaces_num);
   if (status != 0)
@@ -104,7 +105,7 @@ static int ethstat_add_map (const oconfig_item_t *ci) /* {{{ */
   memset (map, 0, sizeof (*map));
 
   sstrncpy (map->type, ci->values[1].value.string, sizeof (map->type));
-  if (ci->values_num == 2)
+  if (ci->values_num == 3)
     sstrncpy (map->type_instance, ci->values[2].value.string,
         sizeof (map->type_instance));
 
index c5ea155ebc53f10e4d191bc84dca0828476a953a..14d288f1f419f78df74f26fd2b9b10afcbfe63d5 100644 (file)
@@ -270,14 +270,15 @@ static void set_environment (void) /* {{{ */
   char buffer[1024];
 
 #ifdef HAVE_SETENV
-  ssnprintf (buffer, sizeof (buffer), "%.3f", CDTIME_T_TO_DOUBLE (interval_g));
+  ssnprintf (buffer, sizeof (buffer), "%.3f",
+      CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
   setenv ("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
 
   ssnprintf (buffer, sizeof (buffer), "%s", hostname_g);
   setenv ("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
 #else
   ssnprintf (buffer, sizeof (buffer), "COLLECTD_INTERVAL=%.3f",
-      CDTIME_T_TO_DOUBLE (interval_g));
+      CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
   putenv (buffer);
 
   ssnprintf (buffer, sizeof (buffer), "COLLECTD_HOSTNAME=%s", hostname_g);
@@ -635,17 +636,22 @@ static void *exec_read_one (void *arg) /* {{{ */
 
       if (len < 0)
       {
-        if (errno == EAGAIN || errno == EINTR)  continue;
+        if (errno == EAGAIN || errno == EINTR)
+          continue;
         break;
       }
       else if (len == 0)
       {
         /* We've reached EOF */
-        NOTICE ("exec plugin: Program `%s' has closed STDERR.",
-            pl->exec);
-        close (fd_err);
+        NOTICE ("exec plugin: Program `%s' has closed STDERR.", pl->exec);
+
+        /* Remove file descriptor form select() set. */
         FD_CLR (fd_err, &fdset);
+        copy = fdset;
         highest_fd = fd;
+
+        /* Clean up file descriptor */
+        close (fd_err);
         fd_err = -1;
         continue;
       }
@@ -821,7 +827,8 @@ static int exec_read (void) /* {{{ */
 
     pthread_attr_init (&attr);
     pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-    pthread_create (&t, &attr, exec_read_one, (void *) pl);
+    plugin_thread_create (&t, &attr, exec_read_one, (void *) pl);
+    pthread_attr_destroy (&attr);
   } /* for (pl) */
 
   return (0);
@@ -864,7 +871,8 @@ static int exec_notification (const notification_t *n, /* {{{ */
 
     pthread_attr_init (&attr);
     pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-    pthread_create (&t, &attr, exec_notification_one, (void *) pln);
+    plugin_thread_create (&t, &attr, exec_notification_one, (void *) pln);
+    pthread_attr_destroy (&attr);
   } /* for (pl) */
 
   return (0);
index 3c746c48887c3e678d42e2417b3d11bd38a7b1ec..28be0920ccf51cd43731f9fd2e4549e780fe4ea5 100644 (file)
@@ -879,7 +879,7 @@ static int mc_receive_thread_start (void) /* {{{ */
 
   mc_receive_thread_loop = 1;
 
-  status = pthread_create (&mc_receive_thread_id, /* attr = */ NULL,
+  status = plugin_thread_create (&mc_receive_thread_id, /* attr = */ NULL,
       mc_receive_thread, /* args = */ NULL);
   if (status != 0)
   {
index f341320ddec54ed9135f6cec34759f30eb74ecd7..fada5bdc3462a1d40396c9aa3fc4fe71f1dbe1e9 100644 (file)
@@ -664,12 +664,12 @@ static int c_ipmi_init (void)
   int status;
 
   /* Don't send `ADD' notifications during startup (~ 1 minute) */
-  time_t iv = CDTIME_T_TO_TIME_T (interval_g);
+  time_t iv = CDTIME_T_TO_TIME_T (plugin_get_interval ());
   c_ipmi_init_in_progress = 1 + (60 / iv);
 
   c_ipmi_active = 1;
 
-  status = pthread_create (&thread_id, /* attr = */ NULL, thread_main,
+  status = plugin_thread_create (&thread_id, /* attr = */ NULL, thread_main,
       /* user data = */ NULL);
   if (status != 0)
   {
index 60441452474484dce73192406d80347bbb468f68..baf7a21bc3778a9dacd0cc308f19ca2d785c67ee 100644 (file)
@@ -4,11 +4,18 @@ if COMPILER_IS_GCC
 AM_CFLAGS = -Wall -Werror
 endif
 
-pkginclude_HEADERS = client.h lcc_features.h
+pkginclude_HEADERS = collectd/client.h collectd/network_buffer.h collectd/lcc_features.h
 lib_LTLIBRARIES = libcollectdclient.la
 nodist_pkgconfig_DATA = libcollectdclient.pc
 
-BUILT_SOURCES = lcc_features.h
+BUILT_SOURCES = collectd/lcc_features.h
 
-libcollectdclient_la_SOURCES = client.c
+libcollectdclient_la_SOURCES = client.c network.c network_buffer.c
+libcollectdclient_la_CPPFLAGS = $(AM_CPPFLAGS)
 libcollectdclient_la_LDFLAGS = -version-info 0:0:0
+libcollectdclient_la_LIBADD = 
+if BUILD_WITH_LIBGCRYPT
+libcollectdclient_la_CPPFLAGS += $(GCRYPT_CPPFLAGS)
+libcollectdclient_la_LDFLAGS += $(GCRYPT_LDFLAGS)
+libcollectdclient_la_LIBADD += $(GCRYPT_LIBS)
+endif
index 2adf6d48821d49590bde1186dd8c047c78eaa17a..850edaaff45841d82c1a7536258e26a74d9229a1 100644 (file)
@@ -1,22 +1,27 @@
 /**
  * libcollectdclient - src/libcollectdclient/client.c
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008-2012  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.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
  *
- * 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.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * 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
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #if HAVE_CONFIG_H
@@ -27,7 +32,7 @@
 # define __attribute__(x) /**/
 #endif
 
-#include "lcc_features.h"
+#include "collectd/lcc_features.h"
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -41,7 +46,7 @@
 #include <math.h>
 #include <netdb.h>
 
-#include "client.h"
+#include "collectd/client.h"
 
 /* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11
  * to no longer define it. We'll use the old, RFC 2553 value here. */
@@ -228,53 +233,6 @@ static void lcc_chomp (char *str) /* {{{ */
   }
 } /* }}} void lcc_chomp */
 
-static int lcc_identifier_cmp (const void *a, const void *b)
-{
-  const lcc_identifier_t *ident_a, *ident_b;
-
-  int status;
-
-  ident_a = a;
-  ident_b = b;
-
-  status = strcasecmp (ident_a->host, ident_b->host);
-  if (status != 0)
-    return (status);
-
-  status = strcmp (ident_a->plugin, ident_b->plugin);
-  if (status != 0)
-    return (status);
-
-  if ((*ident_a->plugin_instance != '\0') || (*ident_b->plugin_instance != '\0'))
-  {
-    if (*ident_a->plugin_instance == '\0')
-      return (-1);
-    else if (*ident_b->plugin_instance == '\0')
-      return (1);
-
-    status = strcmp (ident_a->plugin_instance, ident_b->plugin_instance);
-    if (status != 0)
-      return (status);
-  }
-
-  status = strcmp (ident_a->type, ident_b->type);
-  if (status != 0)
-    return (status);
-
-  if ((*ident_a->type_instance != '\0') || (*ident_b->type_instance != '\0'))
-  {
-    if (*ident_a->type_instance == '\0')
-      return (-1);
-    else if (*ident_b->type_instance == '\0')
-      return (1);
-
-    status = strcmp (ident_a->type_instance, ident_b->type_instance);
-    if (status != 0)
-      return (status);
-  }
-  return (0);
-} /* }}} int lcc_identifier_cmp */
-
 static void lcc_response_free (lcc_response_t *res) /* {{{ */
 {
   size_t i;
@@ -1105,6 +1063,35 @@ int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */
   return (0);
 } /* }}} int lcc_string_to_identifier */
 
+int lcc_identifier_compare (const lcc_identifier_t *i0, /* {{{ */
+    const lcc_identifier_t *i1)
+{
+  int status;
+
+  if ((i0 == NULL) && (i1 == NULL))
+    return (0);
+  else if (i0 == NULL)
+    return (-1);
+  else if (i1 == NULL)
+    return (1);
+
+#define CMP_FIELD(f) do {         \
+  status = strcmp (i0->f, i1->f); \
+  if (status != 0)                \
+    return (status);              \
+} while (0);
+
+    CMP_FIELD (host);
+    CMP_FIELD (plugin);
+    CMP_FIELD (plugin_instance);
+    CMP_FIELD (type);
+    CMP_FIELD (type_instance);
+
+#undef CMP_FIELD
+
+    return (0);
+} /* }}} int lcc_identifier_compare */
+
 int lcc_sort_identifiers (lcc_connection_t *c, /* {{{ */
     lcc_identifier_t *idents, size_t idents_num)
 {
@@ -1114,7 +1101,8 @@ int lcc_sort_identifiers (lcc_connection_t *c, /* {{{ */
     return (-1);
   }
 
-  qsort (idents, idents_num, sizeof (*idents), lcc_identifier_cmp);
+  qsort (idents, idents_num, sizeof (*idents),
+      (void *) lcc_identifier_compare);
   return (0);
 } /* }}} int lcc_sort_identifiers */
 
diff --git a/src/libcollectdclient/client.h b/src/libcollectdclient/client.h
deleted file mode 100644 (file)
index 36b1d4d..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * libcollectdclient - src/libcollectdclient/client.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
- *
- * Authors:
- *   Florian octo Forster <octo at verplant.org>
- **/
-
-#ifndef LIBCOLLECTD_COLLECTDCLIENT_H
-#define LIBCOLLECTD_COLLECTDCLIENT_H 1
-
-#include "lcc_features.h"
-
-/*
- * Includes (for data types)
- */
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#include <inttypes.h>
-#include <time.h>
-
-/*
- * Defines
- */
-#define LCC_NAME_LEN 64
-#define LCC_DEFAULT_PORT "25826"
-
-/*
- * Types
- */
-#define LCC_TYPE_COUNTER 0
-#define LCC_TYPE_GAUGE   1
-#define LCC_TYPE_DERIVE   2
-#define LCC_TYPE_ABSOLUTE   3
-
-LCC_BEGIN_DECLS
-
-typedef uint64_t counter_t;
-typedef double gauge_t;
-typedef uint64_t derive_t;
-typedef uint64_t absolute_t;
-
-union value_u
-{
-  counter_t counter;
-  gauge_t   gauge;
-  derive_t  derive;
-  absolute_t absolute;
-};
-typedef union value_u value_t;
-
-struct lcc_identifier_s
-{
-  char host[LCC_NAME_LEN];
-  char plugin[LCC_NAME_LEN];
-  char plugin_instance[LCC_NAME_LEN];
-  char type[LCC_NAME_LEN];
-  char type_instance[LCC_NAME_LEN];
-};
-typedef struct lcc_identifier_s lcc_identifier_t;
-#define LCC_IDENTIFIER_INIT { "localhost", "", "", "", "" }
-
-struct lcc_value_list_s
-{
-  value_t *values;
-  int     *values_types;
-  size_t   values_len;
-  time_t   time;
-  int      interval;
-  lcc_identifier_t identifier;
-};
-typedef struct lcc_value_list_s lcc_value_list_t;
-#define LCC_VALUE_LIST_INIT { NULL, NULL, 0, 0, 0, LCC_IDENTIFIER_INIT }
-
-struct lcc_connection_s;
-typedef struct lcc_connection_s lcc_connection_t;
-
-/*
- * Functions
- */
-int lcc_connect (const char *address, lcc_connection_t **ret_con);
-int lcc_disconnect (lcc_connection_t *c);
-#define LCC_DESTROY(c) do { lcc_disconnect (c); (c) = NULL; } while (0)
-
-int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident,
-    size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names);
-
-int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl);
-
-int lcc_flush (lcc_connection_t *c, const char *plugin,
-    lcc_identifier_t *ident, int timeout);
-
-int lcc_listval (lcc_connection_t *c,
-    lcc_identifier_t **ret_ident, size_t *ret_ident_num);
-
-/* TODO: putnotif */
-
-const char *lcc_strerror (lcc_connection_t *c);
-
-int lcc_identifier_to_string (lcc_connection_t *c,
-    char *string, size_t string_size, const lcc_identifier_t *ident);
-int lcc_string_to_identifier (lcc_connection_t *c,
-    lcc_identifier_t *ident, const char *string);
-
-int lcc_sort_identifiers (lcc_connection_t *c,
-    lcc_identifier_t *idents, size_t idents_num);
-
-LCC_END_DECLS
-
-/* vim: set sw=2 sts=2 et : */
-#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */
diff --git a/src/libcollectdclient/collectd/client.h b/src/libcollectdclient/collectd/client.h
new file mode 100644 (file)
index 0000000..146f342
--- /dev/null
@@ -0,0 +1,135 @@
+/**
+ * libcollectdclient - src/libcollectdclient/collectd/client.h
+ * Copyright (C) 2008-2012  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef LIBCOLLECTD_COLLECTDCLIENT_H
+#define LIBCOLLECTD_COLLECTDCLIENT_H 1
+
+#include "lcc_features.h"
+
+/*
+ * Includes (for data types)
+ */
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <inttypes.h>
+#include <time.h>
+
+/*
+ * Defines
+ */
+#define LCC_NAME_LEN 64
+#define LCC_DEFAULT_PORT "25826"
+
+/*
+ * Types
+ */
+#define LCC_TYPE_COUNTER 0
+#define LCC_TYPE_GAUGE   1
+#define LCC_TYPE_DERIVE   2
+#define LCC_TYPE_ABSOLUTE   3
+
+LCC_BEGIN_DECLS
+
+typedef uint64_t counter_t;
+typedef double gauge_t;
+typedef uint64_t derive_t;
+typedef uint64_t absolute_t;
+
+union value_u
+{
+  counter_t counter;
+  gauge_t   gauge;
+  derive_t  derive;
+  absolute_t absolute;
+};
+typedef union value_u value_t;
+
+struct lcc_identifier_s
+{
+  char host[LCC_NAME_LEN];
+  char plugin[LCC_NAME_LEN];
+  char plugin_instance[LCC_NAME_LEN];
+  char type[LCC_NAME_LEN];
+  char type_instance[LCC_NAME_LEN];
+};
+typedef struct lcc_identifier_s lcc_identifier_t;
+#define LCC_IDENTIFIER_INIT { "localhost", "", "", "", "" }
+
+struct lcc_value_list_s
+{
+  value_t *values;
+  int     *values_types;
+  size_t   values_len;
+  time_t   time;
+  int      interval;
+  lcc_identifier_t identifier;
+};
+typedef struct lcc_value_list_s lcc_value_list_t;
+#define LCC_VALUE_LIST_INIT { NULL, NULL, 0, 0, 0, LCC_IDENTIFIER_INIT }
+
+struct lcc_connection_s;
+typedef struct lcc_connection_s lcc_connection_t;
+
+/*
+ * Functions
+ */
+int lcc_connect (const char *address, lcc_connection_t **ret_con);
+int lcc_disconnect (lcc_connection_t *c);
+#define LCC_DESTROY(c) do { lcc_disconnect (c); (c) = NULL; } while (0)
+
+int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident,
+    size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names);
+
+int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl);
+
+int lcc_flush (lcc_connection_t *c, const char *plugin,
+    lcc_identifier_t *ident, int timeout);
+
+int lcc_listval (lcc_connection_t *c,
+    lcc_identifier_t **ret_ident, size_t *ret_ident_num);
+
+/* TODO: putnotif */
+
+const char *lcc_strerror (lcc_connection_t *c);
+
+int lcc_identifier_to_string (lcc_connection_t *c,
+    char *string, size_t string_size, const lcc_identifier_t *ident);
+int lcc_string_to_identifier (lcc_connection_t *c,
+    lcc_identifier_t *ident, const char *string);
+
+/* Compares the identifiers "i0" and "i1" and returns less than zero or greater
+ * than zero if "i0" is smaller than or greater than "i1", respectively. If
+ * "i0" and "i1" are identical, zero is returned. */
+int lcc_identifier_compare (const lcc_identifier_t *i0,
+    const lcc_identifier_t *i1);
+int lcc_sort_identifiers (lcc_connection_t *c,
+    lcc_identifier_t *idents, size_t idents_num);
+
+LCC_END_DECLS
+
+/* vim: set sw=2 sts=2 et : */
+#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */
diff --git a/src/libcollectdclient/collectd/lcc_features.h.in b/src/libcollectdclient/collectd/lcc_features.h.in
new file mode 100644 (file)
index 0000000..3916a17
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * libcollectdclient - src/libcollectdclient/lcc_features.h
+ * Copyright (C) 2009  Sebastian Harl
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Sebastian tokkee Harl <sh at tokkee.org>
+ **/
+
+#ifndef LIBCOLLECTD_LCC_FEATURES_H
+#define LIBCOLLECTD_LCC_FEATURES_H 1
+
+#ifdef __cplusplus
+# define LCC_BEGIN_DECLS extern "C" {
+# define LCC_END_DECLS   }
+#else
+# define LCC_BEGIN_DECLS
+# define LCC_END_DECLS
+#endif
+
+#define LCC_API_VERSION 0
+
+#define LCC_VERSION_MAJOR @LCC_VERSION_MAJOR@
+#define LCC_VERSION_MINOR @LCC_VERSION_MINOR@
+#define LCC_VERSION_PATCH @LCC_VERSION_PATCH@
+
+#define LCC_VERSION_EXTRA "@LCC_VERSION_EXTRA@"
+
+#define LCC_VERSION_STRING "@LCC_VERSION_STRING@"
+
+#define LCC_VERSION_ENCODE(major, minor, patch) \
+       ((major) * 10000 + (minor) * 100 + (patch))
+
+#define LCC_VERSION \
+       LCC_VERSION_ENCODE(LCC_VERSION_MAJOR, LCC_VERSION_MINOR, LCC_VERSION_PATCH)
+
+LCC_BEGIN_DECLS
+
+unsigned int lcc_version (void);
+
+const char *lcc_version_string (void);
+
+const char *lcc_version_extra (void);
+
+LCC_END_DECLS
+
+#endif /* ! LIBCOLLECTD_LCC_FEATURES_H */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
diff --git a/src/libcollectdclient/collectd/network.h b/src/libcollectdclient/collectd/network.h
new file mode 100644 (file)
index 0000000..3962666
--- /dev/null
@@ -0,0 +1,83 @@
+/**
+ * collectd - src/libcollectdclient/collectd/network.h
+ * Copyright (C) 2005-2012  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef LIBCOLLECTDCLIENT_NETWORK_H
+#define LIBCOLLECTDCLIENT_NETWORK_H 1
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "client.h"
+
+#define NET_DEFAULT_V4_ADDR "239.192.74.66"
+#define NET_DEFAULT_V6_ADDR "ff18::efc0:4a42"
+#define NET_DEFAULT_PORT    "25826"
+
+struct lcc_network_s;
+typedef struct lcc_network_s lcc_network_t;
+
+struct lcc_server_s;
+typedef struct lcc_server_s lcc_server_t;
+
+enum lcc_security_level_e
+{
+  NONE,
+  SIGN,
+  ENCRYPT
+};
+typedef enum lcc_security_level_e lcc_security_level_t;
+
+/*
+ * Create / destroy object
+ */
+lcc_network_t *lcc_network_create (void);
+void lcc_network_destroy (lcc_network_t *net);
+
+/* 
+ * Add servers
+ */
+lcc_server_t *lcc_server_create (lcc_network_t *net,
+    const char *node, const char *service);
+int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv);
+
+/* Configure servers */
+int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl);
+int lcc_server_set_security_level (lcc_server_t *srv,
+    lcc_security_level_t level,
+    const char *username, const char *password);
+
+/*
+ * Send data
+ */
+int lcc_network_values_send (lcc_network_t *net,
+    const lcc_value_list_t *vl);
+#if 0
+int lcc_network_notification_send (lcc_network_t *net,
+    const lcc_notification_t *notif);
+#endif
+
+/* vim: set sw=2 sts=2 et : */
+#endif /* LIBCOLLECTDCLIENT_NETWORK_H */
diff --git a/src/libcollectdclient/collectd/network_buffer.h b/src/libcollectdclient/collectd/network_buffer.h
new file mode 100644 (file)
index 0000000..edf49ff
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * collectd - src/libcollectdclient/collectd/network_buffer.h
+ * Copyright (C) 2010-2012  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef LIBCOLLECTDCLIENT_NETWORK_BUFFER_H
+#define LIBCOLLECTDCLIENT_NETWORK_BUFFER_H 1
+
+/* FIXME */
+#include "client.h"
+#include "network.h"
+
+/* Ethernet frame - (IPv6 header + UDP header) */
+#define LCC_NETWORK_BUFFER_SIZE_DEFAULT 1452
+
+struct lcc_network_buffer_s;
+typedef struct lcc_network_buffer_s lcc_network_buffer_t;
+
+lcc_network_buffer_t *lcc_network_buffer_create (size_t size);
+void lcc_network_buffer_destroy (lcc_network_buffer_t *nb);
+
+int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb,
+    lcc_security_level_t level,
+    const char *user, const char *password);
+
+int lcc_network_buffer_initialize (lcc_network_buffer_t *nb);
+int lcc_network_buffer_finalize (lcc_network_buffer_t *nb);
+
+int lcc_network_buffer_add_value (lcc_network_buffer_t *nb,
+    const lcc_value_list_t *vl);
+
+int lcc_network_buffer_get (lcc_network_buffer_t *nb,
+    void *buffer, size_t *buffer_size);
+
+#endif /* LIBCOLLECTDCLIENT_NETWORK_BUFFER_H */
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/libcollectdclient/lcc_features.h.in b/src/libcollectdclient/lcc_features.h.in
deleted file mode 100644 (file)
index 3916a17..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * libcollectdclient - src/libcollectdclient/lcc_features.h
- * Copyright (C) 2009  Sebastian Harl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- *
- * Authors:
- *   Sebastian tokkee Harl <sh at tokkee.org>
- **/
-
-#ifndef LIBCOLLECTD_LCC_FEATURES_H
-#define LIBCOLLECTD_LCC_FEATURES_H 1
-
-#ifdef __cplusplus
-# define LCC_BEGIN_DECLS extern "C" {
-# define LCC_END_DECLS   }
-#else
-# define LCC_BEGIN_DECLS
-# define LCC_END_DECLS
-#endif
-
-#define LCC_API_VERSION 0
-
-#define LCC_VERSION_MAJOR @LCC_VERSION_MAJOR@
-#define LCC_VERSION_MINOR @LCC_VERSION_MINOR@
-#define LCC_VERSION_PATCH @LCC_VERSION_PATCH@
-
-#define LCC_VERSION_EXTRA "@LCC_VERSION_EXTRA@"
-
-#define LCC_VERSION_STRING "@LCC_VERSION_STRING@"
-
-#define LCC_VERSION_ENCODE(major, minor, patch) \
-       ((major) * 10000 + (minor) * 100 + (patch))
-
-#define LCC_VERSION \
-       LCC_VERSION_ENCODE(LCC_VERSION_MAJOR, LCC_VERSION_MINOR, LCC_VERSION_PATCH)
-
-LCC_BEGIN_DECLS
-
-unsigned int lcc_version (void);
-
-const char *lcc_version_string (void);
-
-const char *lcc_version_extra (void);
-
-LCC_END_DECLS
-
-#endif /* ! LIBCOLLECTD_LCC_FEATURES_H */
-
-/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
-
diff --git a/src/libcollectdclient/network.c b/src/libcollectdclient/network.c
new file mode 100644 (file)
index 0000000..02a8e96
--- /dev/null
@@ -0,0 +1,398 @@
+/**
+ * collectd - src/libcollectdclient/network.c
+ * Copyright (C) 2005-2012  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ **/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "collectd/network.h"
+#include "collectd/network_buffer.h"
+
+/*
+ * Private data types
+ */
+struct lcc_network_s
+{
+  lcc_server_t *servers;
+};
+
+struct lcc_server_s
+{
+  char *node;
+  char *service;
+
+  int ttl;
+  lcc_security_level_t security_level;
+  char *username;
+  char *password;
+
+  int fd;
+  struct sockaddr *sa;
+  socklen_t sa_len;
+
+  lcc_network_buffer_t *buffer;
+
+  lcc_server_t *next;
+};
+
+/*
+ * Private functions
+ */
+static int server_close_socket (lcc_server_t *srv) /* {{{ */
+{
+  if (srv == NULL)
+    return (EINVAL);
+
+  if (srv->fd < 0)
+    return (0);
+
+  close (srv->fd);
+  free (srv->sa);
+  srv->sa = NULL;
+  srv->sa_len = 0;
+
+  return (0);
+} /* }}} int server_close_socket */
+
+static void int_server_destroy (lcc_server_t *srv) /* {{{ */
+{
+  lcc_server_t *next;
+
+  if (srv == NULL)
+    return;
+
+  server_close_socket (srv);
+
+  next = srv->next;
+
+  if (srv->fd >= 0)
+  {
+    close (srv->fd);
+    srv->fd = -1;
+  }
+
+  free (srv->node);
+  free (srv->service);
+  free (srv->username);
+  free (srv->password);
+  free (srv);
+
+  int_server_destroy (next);
+} /* }}} void int_server_destroy */
+
+static int server_open_socket (lcc_server_t *srv) /* {{{ */
+{
+  struct addrinfo ai_hints = { 0 };
+  struct addrinfo *ai_list = NULL;
+  struct addrinfo *ai_ptr;
+  int status;
+
+  if (srv == NULL)
+    return (EINVAL);
+
+  if (srv->fd >= 0)
+    server_close_socket (srv);
+
+#ifdef AI_ADDRCONFIG
+  ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+  ai_hints.ai_family   = AF_UNSPEC;
+  ai_hints.ai_socktype = SOCK_DGRAM;
+
+  status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
+  if (status != 0)
+    return (status);
+  assert (ai_list != NULL);
+
+  for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+  {
+    srv->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (srv->fd < 0)
+      continue;
+
+    if (ai_ptr->ai_family == AF_INET)
+    {
+
+      struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
+      int optname;
+
+      if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+        optname = IP_MULTICAST_TTL;
+      else
+        optname = IP_TTL;
+
+      setsockopt (srv->fd, IPPROTO_IP, optname,
+          &srv->ttl,
+          sizeof (srv->ttl));
+    }
+    else if (ai_ptr->ai_family == AF_INET6)
+    {
+      /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
+      struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
+      int optname;
+
+      if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+        optname = IPV6_MULTICAST_HOPS;
+      else
+        optname = IPV6_UNICAST_HOPS;
+
+      setsockopt (srv->fd, IPPROTO_IPV6, optname,
+          &srv->ttl,
+          sizeof (srv->ttl));
+    }
+
+    srv->sa = malloc (ai_ptr->ai_addrlen);
+    if (srv->sa == NULL)
+    {
+      close (srv->fd);
+      srv->fd = -1;
+      continue;
+    }
+
+    memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    srv->sa_len = ai_ptr->ai_addrlen;
+    break;
+  }
+
+  freeaddrinfo (ai_list);
+
+  if (srv->fd < 0)
+    return (-1);
+  return (0);
+} /* }}} int server_open_socket */
+
+static int server_send_buffer (lcc_server_t *srv) /* {{{ */
+{
+  char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
+  size_t buffer_size;
+  int status;
+
+  if (srv->fd < 0)
+  {
+    status = server_open_socket (srv);
+    if (status != 0)
+      return (status);
+  }
+
+  memset (buffer, 0, sizeof (buffer));
+  buffer_size = sizeof (buffer);
+
+  lcc_network_buffer_finalize (srv->buffer);
+  status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size);
+  lcc_network_buffer_initialize (srv->buffer);
+
+  if (status != 0)
+    return (status);
+
+  if (buffer_size > sizeof (buffer))
+    buffer_size = sizeof (buffer);
+
+  while (42)
+  {
+    assert (srv->fd >= 0);
+    assert (srv->sa != NULL);
+    status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0,
+        srv->sa, srv->sa_len);
+    if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
+      continue;
+
+    break;
+  }
+
+  if (status < 0)
+    return (status);
+  return (0);
+} /* }}} int server_send_buffer */
+
+static int server_value_add (lcc_server_t *srv, /* {{{ */
+    const lcc_value_list_t *vl)
+{
+  int status;
+
+  status = lcc_network_buffer_add_value (srv->buffer, vl);
+  if (status == 0)
+    return (0);
+
+  server_send_buffer (srv);
+  return (lcc_network_buffer_add_value (srv->buffer, vl));
+} /* }}} int server_value_add */
+
+/*
+ * Public functions
+ */
+lcc_network_t *lcc_network_create (void) /* {{{ */
+{
+  lcc_network_t *net;
+
+  net = malloc (sizeof (*net));
+  if (net == NULL)
+    return (NULL);
+  memset (net, 0, sizeof (*net));
+
+  net->servers = NULL;
+
+  return (net);
+} /* }}} lcc_network_t *lcc_network_create */
+
+void lcc_network_destroy (lcc_network_t *net) /* {{{ */
+{
+  if (net == NULL)
+    return;
+  int_server_destroy (net->servers);
+  free (net);
+} /* }}} void lcc_network_destroy */
+
+lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
+    const char *node, const char *service)
+{
+  lcc_server_t *srv;
+
+  if ((net == NULL) || (node == NULL))
+    return (NULL);
+  if (service == NULL)
+    service = NET_DEFAULT_PORT;
+
+  srv = malloc (sizeof (*srv));
+  if (srv == NULL)
+    return (NULL);
+  memset (srv, 0, sizeof (*srv));
+
+  srv->fd = -1;
+  srv->security_level = NONE;
+  srv->username = NULL;
+  srv->password = NULL;
+  srv->next = NULL;
+
+  srv->node = strdup (node);
+  if (srv->node == NULL)
+  {
+    free (srv);
+    return (NULL);
+  }
+
+  srv->service = strdup (service);
+  if (srv->service == NULL)
+  {
+    free (srv->node);
+    free (srv);
+    return (NULL);
+  }
+
+  srv->buffer = lcc_network_buffer_create (/* size = */ 0);
+  if (srv->buffer == NULL)
+  {
+    free (srv->service);
+    free (srv->node);
+    free (srv);
+    return (NULL);
+  }
+
+  if (net->servers == NULL)
+  {
+    net->servers = srv;
+  }
+  else
+  {
+    lcc_server_t *last = net->servers;
+
+    while (last->next != NULL)
+      last = last->next;
+
+    last->next = srv;
+  }
+
+  return (srv);
+} /* }}} lcc_server_t *lcc_server_create */
+
+int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
+{
+  if ((net == NULL) || (srv == NULL))
+    return (EINVAL);
+
+  if (net->servers == srv)
+  {
+    net->servers = srv->next;
+    srv->next = NULL;
+  }
+  else
+  {
+    lcc_server_t *prev = net->servers;
+
+    while ((prev != NULL) && (prev->next != srv))
+      prev = prev->next;
+
+    if (prev == NULL)
+      return (ENOENT);
+
+    prev->next = srv->next;
+    srv->next = NULL;
+  }
+
+  int_server_destroy (srv);
+
+  return (0);
+} /* }}} int lcc_server_destroy */
+
+int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
+{
+  if (srv == NULL)
+    return (EINVAL);
+
+  srv->ttl = (int) ttl;
+
+  return (0);
+} /* }}} int lcc_server_set_ttl */
+
+int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
+    lcc_security_level_t level,
+    const char *username, const char *password)
+{
+  return (lcc_network_buffer_set_security_level (srv->buffer,
+        level, username, password));
+} /* }}} int lcc_server_set_security_level */
+
+int lcc_network_values_send (lcc_network_t *net, /* {{{ */
+    const lcc_value_list_t *vl)
+{
+  lcc_server_t *srv;
+
+  if ((net == NULL) || (vl == NULL))
+    return (EINVAL);
+
+  for (srv = net->servers; srv != NULL; srv = srv->next)
+    server_value_add (srv, vl);
+
+  return (0);
+} /* }}} int lcc_network_values_send */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/libcollectdclient/network_buffer.c b/src/libcollectdclient/network_buffer.c
new file mode 100644 (file)
index 0000000..3da3b44
--- /dev/null
@@ -0,0 +1,792 @@
+/**
+ * collectd - src/libcollectdclient/network_buffer.c
+ * Copyright (C) 2010-2012  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include <errno.h>
+#include <arpa/inet.h> /* htons */
+
+#include <pthread.h>
+
+#if HAVE_LIBGCRYPT
+#include <gcrypt.h>
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+
+#include "collectd/network_buffer.h"
+
+#define TYPE_HOST            0x0000
+#define TYPE_TIME            0x0001
+#define TYPE_PLUGIN          0x0002
+#define TYPE_PLUGIN_INSTANCE 0x0003
+#define TYPE_TYPE            0x0004
+#define TYPE_TYPE_INSTANCE   0x0005
+#define TYPE_VALUES          0x0006
+#define TYPE_INTERVAL        0x0007
+
+/* Types to transmit notifications */
+#define TYPE_MESSAGE         0x0100
+#define TYPE_SEVERITY        0x0101
+
+#define TYPE_SIGN_SHA256     0x0200
+#define TYPE_ENCR_AES256     0x0210
+
+#define PART_SIGNATURE_SHA256_SIZE 36
+#define PART_ENCRYPTION_AES256_SIZE 42
+
+#define ADD_GENERIC(nb,srcptr,size) do {         \
+  assert ((size) <= (nb)->free);                 \
+  memcpy ((nb)->ptr, (srcptr), (size));          \
+  (nb)->ptr += (size);                           \
+  (nb)->free -= (size);                          \
+} while (0)
+
+#define ADD_STATIC(nb,var) \
+  ADD_GENERIC(nb,&(var),sizeof(var));
+
+/*
+ * Data types
+ */
+struct lcc_network_buffer_s
+{
+  char *buffer;
+  size_t size;
+
+  lcc_value_list_t state;
+  char *ptr;
+  size_t free;
+
+  lcc_security_level_t seclevel;
+  char *username;
+  char *password;
+
+  gcry_cipher_hd_t encr_cypher;
+  size_t encr_header_len;
+  char encr_iv[16];
+};
+
+#define SSTRNCPY(dst,src,sz) do { \
+  strncpy ((dst), (src), (sz));   \
+  (dst)[(sz) - 1] = 0;            \
+} while (0)
+
+/*
+ * Private functions
+ */
+static _Bool have_gcrypt (void) /* {{{ */
+{
+  static _Bool result = 0;
+  static _Bool need_init = 1;
+
+  if (!need_init)
+    return (result);
+  need_init = 0;
+
+  gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+
+  if (!gcry_check_version (GCRYPT_VERSION))
+    return (0);
+
+  gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+  result = 1;
+  return (1);
+} /* }}} _Bool have_gcrypt */
+
+static uint64_t htonll (uint64_t val) /* {{{ */
+{
+  static int config = 0;
+
+  uint32_t hi;
+  uint32_t lo;
+
+  if (config == 0)
+  {
+    uint16_t h = 0x1234;
+    uint16_t n = htons (h);
+
+    if (h == n)
+      config = 1;
+    else
+      config = 2;
+  }
+
+  if (config == 1)
+    return (val);
+
+  hi = (uint32_t) (val >> 32);
+  lo = (uint32_t) (val & 0x00000000FFFFFFFF);
+
+  hi = htonl (hi);
+  lo = htonl (lo);
+
+  return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
+} /* }}} uint64_t htonll */
+
+static double htond (double val) /* {{{ */
+{
+  static int config = 0;
+
+  union { uint8_t byte[8]; double floating; } in;
+  union { uint8_t byte[8]; double floating; } out;
+
+  if (config == 0)
+  {
+    double d = 8.642135e130;
+    uint8_t c[8];
+
+    memcpy (c, &d, 8);
+
+    if ((c[0] == 0x2f) && (c[1] == 0x25)
+        && (c[2] == 0xc0) && (c[3] == 0xc7)
+        && (c[4] == 0x43) && (c[5] == 0x2b)
+        && (c[6] == 0x1f) && (c[7] == 0x5b))
+      config = 1; /* need nothing */
+    else if ((c[7] == 0x2f) && (c[6] == 0x25)
+        && (c[5] == 0xc0) && (c[4] == 0xc7)
+        && (c[3] == 0x43) && (c[2] == 0x2b)
+        && (c[1] == 0x1f) && (c[0] == 0x5b))
+      config = 2; /* endian flip */
+    else if ((c[4] == 0x2f) && (c[5] == 0x25)
+        && (c[6] == 0xc0) && (c[7] == 0xc7)
+        && (c[0] == 0x43) && (c[1] == 0x2b)
+        && (c[2] == 0x1f) && (c[3] == 0x5b))
+      config = 3; /* int swap */
+    else
+      config = 4;
+  }
+
+  if (isnan (val))
+  {
+    out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
+    out.byte[4] = out.byte[5] = 0x00;
+    out.byte[6] = 0xf8;
+    out.byte[7] = 0x7f;
+    return (out.floating);
+  }
+  else if (config == 1)
+    return (val);
+  else if (config == 2)
+  {
+    in.floating = val;
+    out.byte[0] = in.byte[7];
+    out.byte[1] = in.byte[6];
+    out.byte[2] = in.byte[5];
+    out.byte[3] = in.byte[4];
+    out.byte[4] = in.byte[3];
+    out.byte[5] = in.byte[2];
+    out.byte[6] = in.byte[1];
+    out.byte[7] = in.byte[0];
+    return (out.floating);
+  }
+  else if (config == 3)
+  {
+    in.floating = val;
+    out.byte[0] = in.byte[4];
+    out.byte[1] = in.byte[5];
+    out.byte[2] = in.byte[6];
+    out.byte[3] = in.byte[7];
+    out.byte[4] = in.byte[0];
+    out.byte[5] = in.byte[1];
+    out.byte[6] = in.byte[2];
+    out.byte[7] = in.byte[3];
+    return (out.floating);
+  }
+  else
+  {
+    /* If in doubt, just copy the value back to the caller. */
+    return (val);
+  }
+} /* }}} double htond */
+
+static int nb_add_values (char **ret_buffer, /* {{{ */
+    size_t *ret_buffer_len,
+    const lcc_value_list_t *vl)
+{
+  char *packet_ptr;
+  size_t packet_len;
+
+  uint16_t      pkg_type;
+  uint16_t      pkg_length;
+  uint16_t      pkg_num_values;
+  uint8_t       pkg_values_types[vl->values_len];
+  value_t       pkg_values[vl->values_len];
+
+  size_t offset;
+  size_t i;
+
+  packet_len = sizeof (pkg_type) + sizeof (pkg_length)
+    + sizeof (pkg_num_values)
+    + sizeof (pkg_values_types)
+    + sizeof (pkg_values);
+
+  if (*ret_buffer_len < packet_len)
+    return (ENOMEM);
+
+  pkg_type = htons (TYPE_VALUES);
+  pkg_length = htons ((uint16_t) packet_len);
+  pkg_num_values = htons ((uint16_t) vl->values_len);
+
+  for (i = 0; i < vl->values_len; i++)
+  {
+    pkg_values_types[i] = (uint8_t) vl->values_types[i];
+    switch (vl->values_types[i])
+    {
+      case LCC_TYPE_COUNTER:
+        pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
+        break;
+
+      case LCC_TYPE_GAUGE:
+        pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
+        break;
+
+      case LCC_TYPE_DERIVE:
+        pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
+        break;
+
+      case LCC_TYPE_ABSOLUTE:
+        pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
+        break;
+
+      default:
+        return (EINVAL);
+    } /* switch (vl->values_types[i]) */
+  } /* for (vl->values_len) */
+
+  /*
+   * Use `memcpy' to write everything to the buffer, because the pointer
+   * may be unaligned and some architectures, such as SPARC, can't handle
+   * that.
+   */
+  packet_ptr = *ret_buffer;
+  offset = 0;
+  memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
+  offset += sizeof (pkg_type);
+  memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
+  offset += sizeof (pkg_length);
+  memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
+  offset += sizeof (pkg_num_values);
+  memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
+  offset += sizeof (pkg_values_types);
+  memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
+  offset += sizeof (pkg_values);
+
+  assert (offset == packet_len);
+
+  *ret_buffer = packet_ptr + packet_len;
+  *ret_buffer_len -= packet_len;
+  return (0);
+} /* }}} int nb_add_values */
+
+static int nb_add_number (char **ret_buffer, /* {{{ */
+    size_t *ret_buffer_len,
+    uint16_t type, uint64_t value)
+{
+  char *packet_ptr;
+  size_t packet_len;
+
+  uint16_t pkg_type;
+  uint16_t pkg_length;
+  uint64_t pkg_value;
+
+  size_t offset;
+
+  packet_len = sizeof (pkg_type)
+    + sizeof (pkg_length)
+    + sizeof (pkg_value);
+
+  if (*ret_buffer_len < packet_len)
+    return (ENOMEM);
+
+  pkg_type = htons (type);
+  pkg_length = htons ((uint16_t) packet_len);
+  pkg_value = htonll (value);
+
+  packet_ptr = *ret_buffer;
+  offset = 0;
+  memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
+  offset += sizeof (pkg_type);
+  memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
+  offset += sizeof (pkg_length);
+  memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
+  offset += sizeof (pkg_value);
+
+  assert (offset == packet_len);
+
+  *ret_buffer = packet_ptr + packet_len;
+  *ret_buffer_len -= packet_len;
+  return (0);
+} /* }}} int nb_add_number */
+
+static int nb_add_string (char **ret_buffer, /* {{{ */
+    size_t *ret_buffer_len,
+    uint16_t type, const char *str, size_t str_len)
+{
+  char *packet_ptr;
+  size_t packet_len;
+
+  uint16_t pkg_type;
+  uint16_t pkg_length;
+
+  size_t offset;
+
+  packet_len = sizeof (pkg_type)
+    + sizeof (pkg_length)
+    + str_len + 1;
+  if (*ret_buffer_len < packet_len)
+    return (ENOMEM);
+
+  pkg_type = htons (type);
+  pkg_length = htons ((uint16_t) packet_len);
+
+  packet_ptr = *ret_buffer;
+  offset = 0;
+  memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
+  offset += sizeof (pkg_type);
+  memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
+  offset += sizeof (pkg_length);
+  memcpy (packet_ptr + offset, str, str_len);
+  offset += str_len;
+  memset (packet_ptr + offset, 0, 1);
+  offset += 1;
+
+  assert (offset == packet_len);
+
+  *ret_buffer = packet_ptr + packet_len;
+  *ret_buffer_len -= packet_len;
+  return (0);
+} /* }}} int nb_add_string */
+
+static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
+    const lcc_value_list_t *vl)
+{
+  char *buffer = nb->ptr;
+  size_t buffer_size = nb->free;
+
+  const lcc_identifier_t *ident_src;
+  lcc_identifier_t *ident_dst;
+
+  ident_src = &vl->identifier;
+  ident_dst = &nb->state.identifier;
+
+  if (strcmp (ident_dst->host, ident_src->host) != 0)
+  {
+    if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
+          ident_src->host, strlen (ident_src->host)) != 0)
+      return (-1);
+    SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
+  }
+
+  if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
+  {
+    if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
+          ident_src->plugin, strlen (ident_src->plugin)) != 0)
+      return (-1);
+    SSTRNCPY (ident_dst->plugin, ident_src->plugin,
+        sizeof (ident_dst->plugin));
+  }
+
+  if (strcmp (ident_dst->plugin_instance,
+        ident_src->plugin_instance) != 0)
+  {
+    if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
+          ident_src->plugin_instance,
+          strlen (ident_src->plugin_instance)) != 0)
+      return (-1);
+    SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
+        sizeof (ident_dst->plugin_instance));
+  }
+
+  if (strcmp (ident_dst->type, ident_src->type) != 0)
+  {
+    if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
+          ident_src->type, strlen (ident_src->type)) != 0)
+      return (-1);
+    SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
+  }
+
+  if (strcmp (ident_dst->type_instance,
+        ident_src->type_instance) != 0)
+  {
+    if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
+          ident_src->type_instance,
+          strlen (ident_src->type_instance)) != 0)
+      return (-1);
+    SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
+        sizeof (ident_dst->type_instance));
+  }
+
+  if (nb->state.time != vl->time)
+  {
+    if (nb_add_number (&buffer, &buffer_size, TYPE_TIME,
+          (uint64_t) vl->time))
+      return (-1);
+    nb->state.time = vl->time;
+  }
+
+  if (nb->state.interval != vl->interval)
+  {
+    if (nb_add_number (&buffer, &buffer_size, TYPE_INTERVAL,
+          (uint64_t) vl->interval))
+      return (-1);
+    nb->state.interval = vl->interval;
+  }
+
+  if (nb_add_values (&buffer, &buffer_size, vl) != 0)
+    return (-1);
+
+  nb->ptr = buffer;
+  nb->free = buffer_size;
+  return (0);
+} /* }}} int nb_add_value_list */
+
+static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
+{
+  char *buffer;
+  size_t buffer_size;
+
+  gcry_md_hd_t hd;
+  gcry_error_t err;
+  unsigned char *hash;
+  const size_t hash_length = 32;
+
+  /* The type, length and username have already been filled in by
+   * "lcc_network_buffer_initialize". All we do here is calculate the hash over
+   * the username and the data and add the hash value to the buffer. */
+
+  buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
+  assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
+  buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
+
+  hd = NULL;
+  err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+  if (err != 0)
+    return (-1);
+
+  assert (nb->password != NULL);
+  err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
+  if (err != 0)
+  {
+    gcry_md_close (hd);
+    return (-1);
+  }
+
+  gcry_md_write (hd, buffer, buffer_size);
+  hash = gcry_md_read (hd, GCRY_MD_SHA256);
+  if (hash == NULL)
+  {
+    gcry_md_close (hd);
+    return (-1);
+  }
+
+  assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
+  memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
+
+  gcry_md_close (hd);
+  return (0);
+} /* }}} int nb_add_signature */
+
+static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
+{
+  size_t package_length;
+  char *encr_ptr; /* pointer to data being encrypted */
+  size_t encr_size;
+
+  char *hash_ptr; /* pointer to data being hashed */
+  size_t hash_size;
+  char hash[20];
+
+  uint16_t pkg_length;
+  gcry_error_t err;
+
+  /* Fill in the package length */
+  package_length = nb->size - nb->free;
+  pkg_length = htons ((uint16_t) package_length);
+  memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
+
+  /* Calculate what to hash */
+  hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
+  hash_size = package_length - nb->encr_header_len;
+
+  /* Calculate what to encrypt */
+  encr_ptr = hash_ptr - sizeof (hash);
+  encr_size = hash_size + sizeof (hash);
+
+  /* Calculate the SHA-1 hash */
+  gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size);
+  memcpy (encr_ptr, hash, sizeof (hash));
+
+  if (nb->encr_cypher == NULL)
+  {
+    unsigned char password_hash[32];
+
+    err = gcry_cipher_open (&nb->encr_cypher,
+        GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
+    if (err != 0)
+      return (-1);
+
+    /* Calculate our 256bit key used for AES */
+    gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
+        nb->password, strlen (nb->password));
+
+    err = gcry_cipher_setkey (nb->encr_cypher,
+        password_hash, sizeof (password_hash));
+    if (err != 0)
+    {
+      gcry_cipher_close (nb->encr_cypher);
+      nb->encr_cypher = NULL;
+      return (-1);
+    }
+  }
+  else /* if (nb->encr_cypher != NULL) */
+  {
+    gcry_cipher_reset (nb->encr_cypher);
+  }
+
+  /* Set the initialization vector */
+  err = gcry_cipher_setiv (nb->encr_cypher,
+      nb->encr_iv, sizeof (nb->encr_iv));
+  if (err != 0)
+  {
+    gcry_cipher_close (nb->encr_cypher);
+    nb->encr_cypher = NULL;
+    return (-1);
+  }
+
+  /* Encrypt the buffer in-place */
+  err = gcry_cipher_encrypt (nb->encr_cypher,
+      encr_ptr, encr_size,
+      /* in = */ NULL, /* in len = */ 0);
+  if (err != 0)
+  {
+    gcry_cipher_close (nb->encr_cypher);
+    nb->encr_cypher = NULL;
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int nb_add_encryption */
+
+/*
+ * Public functions
+ */
+lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
+{
+  lcc_network_buffer_t *nb;
+
+  if (size == 0)
+    size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
+
+  if (size < 128)
+  {
+    errno = EINVAL;
+    return (NULL);
+  }
+
+  nb = malloc (sizeof (*nb));
+  if (nb == NULL)
+    return (NULL);
+  memset (nb, 0, sizeof (*nb));
+
+  nb->size = size;
+  nb->buffer = malloc (nb->size);
+  if (nb->buffer == NULL)
+  {
+    free (nb);
+    return (NULL);
+  }
+  memset (nb->buffer, 0, nb->size);
+
+  nb->ptr = nb->buffer;
+  nb->free = nb->size;
+
+  nb->seclevel = NONE;
+  nb->username = NULL;
+  nb->password = NULL;
+
+  return (nb);
+} /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
+
+void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
+{
+  if (nb == NULL)
+    return;
+
+  free (nb->buffer);
+  free (nb);
+} /* }}} void lcc_network_buffer_destroy */
+
+int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
+    lcc_security_level_t level,
+    const char *username, const char *password)
+{
+  char *username_copy;
+  char *password_copy;
+
+  if (level == NONE)
+  {
+    free (nb->username);
+    free (nb->password);
+    nb->username = NULL;
+    nb->password = NULL;
+    nb->seclevel = NONE;
+    lcc_network_buffer_initialize (nb);
+    return (0);
+  }
+
+  if (!have_gcrypt ())
+    return (ENOTSUP);
+
+  username_copy = strdup (username);
+  password_copy = strdup (password);
+  if ((username_copy == NULL) || (password_copy == NULL))
+  {
+    free (username_copy);
+    free (password_copy);
+    return (ENOMEM);
+  }
+
+  free (nb->username);
+  free (nb->password);
+  nb->username = username_copy;
+  nb->password = password_copy;
+  nb->seclevel = level;
+
+  lcc_network_buffer_initialize (nb);
+  return (0);
+} /* }}} int lcc_network_buffer_set_security_level */
+
+int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
+{
+  if (nb == NULL)
+    return (EINVAL);
+
+  memset (nb->buffer, 0, nb->size);
+  memset (&nb->state, 0, sizeof (nb->state));
+  nb->ptr = nb->buffer;
+  nb->free = nb->size;
+
+  if (nb->seclevel == SIGN)
+  {
+    size_t username_len;
+    uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
+    uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
+
+    assert (nb->username != NULL);
+    username_len = strlen (nb->username);
+    pkg_length = htons (pkg_length + ((uint16_t) username_len));
+
+    /* Fill in everything but the hash value here. */
+    memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
+    memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
+    nb->ptr += PART_SIGNATURE_SHA256_SIZE;
+    nb->free -= PART_SIGNATURE_SHA256_SIZE;
+
+    memcpy (nb->ptr, nb->username, username_len);
+    nb->ptr += username_len;
+    nb->free -= username_len;
+  }
+  else if (nb->seclevel == ENCRYPT)
+  {
+    size_t username_length = strlen (nb->username);
+    uint16_t pkg_type = htons (TYPE_ENCR_AES256);
+    uint16_t pkg_length = 0; /* Filled in in finalize. */
+    uint16_t pkg_user_len = htons ((uint16_t) username_length);
+    char hash[20];
+
+    nb->encr_header_len = username_length;
+    nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
+
+    gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
+        GCRY_STRONG_RANDOM);
+
+    /* Filled in in finalize. */
+    memset (hash, 0, sizeof (hash));
+
+    ADD_STATIC (nb, pkg_type);
+    ADD_STATIC (nb, pkg_length);
+    ADD_STATIC (nb, pkg_user_len);
+    ADD_GENERIC (nb, nb->username, username_length);
+    ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv));
+    ADD_GENERIC (nb, hash, sizeof (hash));
+    assert ((nb->encr_header_len + nb->free) == nb->size);
+  }
+
+  return (0);
+} /* }}} int lcc_network_buffer_initialize */
+
+int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
+{
+  if (nb == NULL)
+    return (EINVAL);
+
+  if (nb->seclevel == SIGN)
+    nb_add_signature (nb);
+  else if (nb->seclevel == ENCRYPT)
+    nb_add_encryption (nb);
+
+  return (0);
+} /* }}} int lcc_network_buffer_finalize */
+
+int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
+    const lcc_value_list_t *vl)
+{
+  int status;
+
+  if ((nb == NULL) || (vl == NULL))
+    return (EINVAL);
+
+  status = nb_add_value_list (nb, vl);
+  return (status);
+} /* }}} int lcc_network_buffer_add_value */
+
+int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
+    void *buffer, size_t *buffer_size)
+{
+  size_t sz_required;
+  size_t sz_available;
+
+  if ((nb == NULL) || (buffer_size == NULL))
+    return (EINVAL);
+
+  assert (nb->size >= nb->free);
+  sz_required = nb->size - nb->free;
+  sz_available = *buffer_size;
+
+  *buffer_size = sz_required;
+  if (buffer != NULL)
+    memcpy (buffer, nb->buffer,
+        (sz_available < sz_required) ? sz_available : sz_required);
+
+  return (0);
+} /* }}} int lcc_network_buffer_get */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
index 5b7aa94a9e4deec315c5175f8503a4900536e3c1..19f58b2b75c4f19b9b196f5d15b85f8f78dd21ac 100644 (file)
@@ -148,6 +148,19 @@ block:
         $$.children = $2.statement;
         $$.children_num = $2.statement_num;
        }
+       | block_begin block_end
+       {
+        if (strcmp ($1.key, $2) != 0)
+        {
+               printf ("block_begin = %s; block_end = %s;\n", $1.key, $2);
+               yyerror ("Block not closed..\n");
+               exit (1);
+        }
+        free ($2); $2 = NULL;
+        $$ = $1;
+        $$.children = NULL;
+        $$.children_num = 0;
+       }
        ;
 
 statement:
@@ -191,6 +204,13 @@ entire_file:
         ci_root->children = $1.statement;
         ci_root->children_num = $1.statement_num;
        }
+       | /* epsilon */
+       {
+        ci_root = malloc (sizeof (oconfig_item_t));
+        memset (ci_root, '\0', sizeof (oconfig_item_t));
+        ci_root->children = NULL;
+        ci_root->children_num = 0;
+       }
        ;
 
 %%
index 774067cdf016a09e1c9279f9ec2e86d987a3fd0b..c53a81d4b5d1e4261667954b079d26e36a797e07 100644 (file)
@@ -91,13 +91,14 @@ struct interface_device {
     virDomainPtr dom;           /* domain */
     char *path;                 /* name of interface device */
     char *address;              /* mac address of interface device */
+    char *number;               /* interface device number */
 };
 
 static struct interface_device *interface_devices = NULL;
 static int nr_interface_devices = 0;
 
 static void free_interface_devices (void);
-static int add_interface_device (virDomainPtr dom, const char *path, const char *address);
+static int add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number);
 
 /* HostnameFormat. */
 #define HF_MAX_FIELDS 3
@@ -115,7 +116,8 @@ static enum hf_field hostname_format[HF_MAX_FIELDS] =
 /* InterfaceFormat. */
 enum if_field {
     if_address,
-    if_name
+    if_name,
+    if_number
 };
 
 static enum if_field interface_format = if_name;
@@ -139,8 +141,6 @@ init_value_list (value_list_t *vl, virDomainPtr dom)
     const char *name;
     char uuid[VIR_UUID_STRING_BUFLEN];
 
-    vl->interval = interval_g;
-
     sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin));
 
     vl->host[0] = '\0';
@@ -348,6 +348,8 @@ lv_config (const char *key, const char *value)
             interface_format = if_name;
         else if (strcasecmp (value, "address") == 0)
             interface_format = if_address;
+        else if (strcasecmp (value, "number") == 0)
+            interface_format = if_number;
         else {
             ERROR ("unknown InterfaceFormat: %s", value);
             return -1;
@@ -467,10 +469,20 @@ lv_read (void)
     /* Get interface stats for each domain. */
     for (i = 0; i < nr_interface_devices; ++i) {
         struct _virDomainInterfaceStats stats;
-        char *display_name = interface_devices[i].path;
-
-        if (interface_format == if_address)
-            display_name = interface_devices[i].address;
+        char *display_name = NULL;
+
+
+        switch (interface_format) {
+            case if_address:
+                display_name = interface_devices[i].address;
+                break;
+            case if_number:
+                display_name = interface_devices[i].number;
+                break;
+            case if_name:
+            default:
+                display_name = interface_devices[i].path;
+        }
 
         if (virDomainInterfaceStats (interface_devices[i].dom,
                     interface_devices[i].path,
@@ -644,7 +656,7 @@ refresh_lists (void)
                      ignore_device_match (il_interface_devices, name, address) != 0))
                     goto cont3;
 
-                add_interface_device (dom, path, address);
+                add_interface_device (dom, path, address, j+1);
                 cont3:
                     if (path) xmlFree (path);
                     if (address) xmlFree (address);
@@ -745,6 +757,7 @@ free_interface_devices ()
         for (i = 0; i < nr_interface_devices; ++i) {
             sfree (interface_devices[i].path);
             sfree (interface_devices[i].address);
+            sfree (interface_devices[i].number);
         }
         sfree (interface_devices);
     }
@@ -753,17 +766,22 @@ free_interface_devices ()
 }
 
 static int
-add_interface_device (virDomainPtr dom, const char *path, const char *address)
+add_interface_device (virDomainPtr dom, const char *path, const char *address, unsigned int number)
 {
     struct interface_device *new_ptr;
     int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
-    char *path_copy, *address_copy;
+    char *path_copy, *address_copy, number_string[15];
 
     path_copy = strdup (path);
     if (!path_copy) return -1;
 
     address_copy = strdup (address);
-    if (!address_copy) return -1;
+    if (!address_copy) {
+        sfree(path_copy);
+        return -1;
+    }
+
+    snprintf(number_string, sizeof (number_string), "interface-%u", number);
 
     if (interface_devices)
         new_ptr = realloc (interface_devices, new_size);
@@ -779,6 +797,7 @@ add_interface_device (virDomainPtr dom, const char *path, const char *address)
     interface_devices[nr_interface_devices].dom = dom;
     interface_devices[nr_interface_devices].path = path_copy;
     interface_devices[nr_interface_devices].address = address_copy;
+    interface_devices[nr_interface_devices].number = strdup(number_string);
     return nr_interface_devices++;
 }
 
@@ -808,7 +827,7 @@ lv_shutdown (void)
     free_domains ();
 
     if (conn != NULL)
-       virConnectClose (conn);
+        virConnectClose (conn);
     conn = NULL;
 
     ignorelist_free (il_domains);
@@ -825,8 +844,8 @@ void
 module_register (void)
 {
     plugin_register_config ("libvirt",
-           lv_config,
-           config_keys, NR_CONFIG_KEYS);
+    lv_config,
+    config_keys, NR_CONFIG_KEYS);
     plugin_register_init ("libvirt", lv_init);
     plugin_register_read ("libvirt", lv_read);
     plugin_register_shutdown ("libvirt", lv_shutdown);
index b502b377295141a8a095ba248d7e6de0ab285072..ea98ba94abf44670583ab47601e39ce6a0f68394 100644 (file)
@@ -250,6 +250,7 @@ void meta_data_destroy (meta_data_t *md) /* {{{ */
   if (md == NULL)
     return;
 
+  pthread_mutex_destroy(&md->lock);
   md_entry_free (md->head);
   pthread_mutex_destroy (&md->lock);
   free (md);
index 19848b0b330c056b71a82b92888631c0f1b59830..8a9fe93fd83eaf3b6a32221ea9c92598c225802a 100644 (file)
@@ -237,7 +237,7 @@ static int mb_submit (mb_host_t *host, mb_slave_t *slave, /* {{{ */
     return (EINVAL);
 
   if (host->interval <= 0)
-    host->interval = interval_g;
+    host->interval = plugin_get_interval ();
 
   if (slave->instance[0] == 0)
     ssnprintf (slave->instance, sizeof (slave->instance), "slave_%i",
index ef851d354937a0069969b7822b5ce6de0879a739..39536d92ec76f712581d42df8dcea18b7103c093 100644 (file)
@@ -223,7 +223,7 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
 
   msg = NLMSG_DATA (nmh);
 
-  msg_len = nmh->nlmsg_len - sizeof (struct ifinfomsg);
+  msg_len = nmh->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifinfomsg));
   if (msg_len < 0)
   {
     ERROR ("netlink plugin: link_filter: msg_len = %i < 0;", msg_len);
@@ -554,24 +554,26 @@ static int ir_init (void)
 
 static int ir_read (void)
 {
-  struct ifinfomsg im;
   struct tcmsg tm;
   int ifindex;
 
   static const int type_id[] = { RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER };
   static const char *type_name[] = { "qdisc", "class", "filter" };
 
-  memset (&im, '\0', sizeof (im));
-  im.ifi_type = AF_UNSPEC;
-
-  if (rtnl_dump_request (&rth, RTM_GETLINK, &im, sizeof (im)) < 0)
+  if (rtnl_wilddump_request (&rth, AF_UNSPEC, RTM_GETLINK) < 0)
   {
-    ERROR ("netlink plugin: ir_read: rtnl_dump_request failed.");
+    ERROR ("netlink plugin: ir_read: rtnl_wilddump_request failed.");
     return (-1);
   }
 
+#ifdef RTNL_DUMP_FILTER_FIVE_ARGS
   if (rtnl_dump_filter (&rth, link_filter, /* arg1 = */ NULL,
        NULL, NULL) != 0)
+#elif defined(RTNL_DUMP_FILTER_THREE_ARGS)
+  if (rtnl_dump_filter (&rth, link_filter, /* arg = */ NULL) != 0)
+#else
+#error "Failed to determine the number of arguments to 'rtnl_dump_filter'!"
+#endif
   {
     ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
     return (-1);
@@ -608,8 +610,14 @@ static int ir_read (void)
        continue;
       }
 
+#ifdef RTNL_DUMP_FILTER_FIVE_ARGS
       if (rtnl_dump_filter (&rth, qos_filter, (void *) &ifindex,
            NULL, NULL) != 0)
+#elif defined(RTNL_DUMP_FILTER_THREE_ARGS)
+      if (rtnl_dump_filter (&rth, qos_filter, /* arg = */ &ifindex) != 0)
+#else
+#error "Failed to determine the number of arguments to 'rtnl_dump_filter'!"
+#endif
       {
        ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
        continue;
index 5fed1b196933a5bb00d39d8e9f3c9d828930bd84..e8b45a33bb53f77613abb004e6f97ec6e09ccd80 100644 (file)
 #endif
 
 #if HAVE_LIBGCRYPT
+# include <pthread.h>
+# if defined __APPLE__
+/* default xcode compiler throws warnings even when deprecated functionality
+ * is not used. -Werror breaks the build because of erroneous warnings.
+ * http://stackoverflow.com/questions/10556299/compiler-warnings-with-libgcrypt-v1-5-0/12830209#12830209
+ */
+#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+# endif
 # include <gcrypt.h>
+# if defined __APPLE__
+/* Re enable deprecation warnings */
+#  pragma GCC diagnostic warning "-Wdeprecated-declarations"
+# endif
 GCRY_THREAD_OPTION_PTHREAD_IMPL;
 #endif
 
@@ -661,7 +673,7 @@ static int write_part_number (char **ret_buffer, int *ret_buffer_len,
 
        part_header_t pkg_head;
        uint64_t pkg_value;
-       
+
        int offset;
 
        packet_len = sizeof (pkg_head) + sizeof (pkg_value);
@@ -1665,7 +1677,7 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
                                        sizeof (network_config_ttl)) != 0)
                {
                        char errbuf[1024];
-                       ERROR ("setsockopt: %s",
+                       ERROR ("network plugin: setsockopt (ipv4-ttl): %s",
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
                        return (-1);
                }
@@ -1686,7 +1698,7 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
                                        sizeof (network_config_ttl)) != 0)
                {
                        char errbuf[1024];
-                       ERROR ("setsockopt: %s",
+                       ERROR ("network plugin: setsockopt(ipv6-ttl): %s",
                                        sstrerror (errno, errbuf,
                                                sizeof (errbuf)));
                        return (-1);
@@ -1733,7 +1745,7 @@ static int network_set_interface (const sockent_t *se, const struct addrinfo *ai
                                                &mreq, sizeof (mreq)) != 0)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (ipv4-multicast-if): %s",
                                                sstrerror (errno, errbuf, sizeof (errbuf)));
                                return (-1);
                        }
@@ -1752,7 +1764,7 @@ static int network_set_interface (const sockent_t *se, const struct addrinfo *ai
                                                sizeof (se->interface)) != 0)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (ipv6-multicast-if): %s",
                                                sstrerror (errno, errbuf,
                                                        sizeof (errbuf)));
                                return (-1);
@@ -1778,7 +1790,7 @@ static int network_set_interface (const sockent_t *se, const struct addrinfo *ai
                                        sizeof(interface_name)) == -1 )
                {
                        char errbuf[1024];
-                       ERROR ("setsockopt: %s",
+                       ERROR ("network plugin: setsockopt (bind-if): %s",
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
                        return (-1);
                }
@@ -1802,14 +1814,18 @@ static int network_set_interface (const sockent_t *se, const struct addrinfo *ai
 
 static int network_bind_socket (int fd, const struct addrinfo *ai, const int interface_idx)
 {
+#if KERNEL_SOLARIS
+       char loop   = 0;
+#else
        int loop = 0;
+#endif
        int yes  = 1;
 
        /* allow multiple sockets to use the same PORT number */
        if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
                                &yes, sizeof(yes)) == -1) {
                 char errbuf[1024];
-                ERROR ("setsockopt: %s", 
+                ERROR ("network plugin: setsockopt (reuseaddr): %s",
                                 sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
@@ -1852,7 +1868,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
                                                &loop, sizeof (loop)) == -1)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (multicast-loop): %s",
                                                sstrerror (errno, errbuf,
                                                        sizeof (errbuf)));
                                return (-1);
@@ -1862,7 +1878,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
                                                &mreq, sizeof (mreq)) == -1)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (add-membership): %s",
                                                sstrerror (errno, errbuf,
                                                        sizeof (errbuf)));
                                return (-1);
@@ -1900,7 +1916,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
                                                &loop, sizeof (loop)) == -1)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (ipv6-multicast-loop): %s",
                                                sstrerror (errno, errbuf,
                                                        sizeof (errbuf)));
                                return (-1);
@@ -1910,7 +1926,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
                                                &mreq, sizeof (mreq)) == -1)
                        {
                                char errbuf[1024];
-                               ERROR ("setsockopt: %s",
+                               ERROR ("network plugin: setsockopt (ipv6-add-membership): %s",
                                                sstrerror (errno, errbuf,
                                                        sizeof (errbuf)));
                                return (-1);
@@ -1938,7 +1954,7 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
                                        sizeof(interface_name)) == -1 )
                {
                        char errbuf[1024];
-                       ERROR ("setsockopt: %s",
+                       ERROR ("network plugin: setsockopt (bind-if): %s",
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
                        return (-1);
                }
@@ -2713,7 +2729,7 @@ static int add_to_buffer (char *buffer, int buffer_size, /* {{{ */
                        return (-1);
                sstrncpy (vl_def->type_instance, vl->type_instance, sizeof (vl_def->type_instance));
        }
-       
+
        if (write_part_values (&buffer, &buffer_size, ds, vl) != 0)
                return (-1);
 
@@ -3290,7 +3306,6 @@ static int network_stats_read (void) /* {{{ */
        vl.values = values;
        vl.values_len = 2;
        vl.time = 0;
-       vl.interval = interval_g;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "network", sizeof (vl.plugin));
 
@@ -3350,9 +3365,17 @@ static int network_init (void)
        have_init = 1;
 
 #if HAVE_LIBGCRYPT
-       gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
-       gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
-       gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+    /* http://lists.gnupg.org/pipermail/gcrypt-devel/2003-August/000458.html
+     * Because you can't know in a library whether another library has
+     * already initialized the library
+     */
+    if (!gcry_control (GCRYCTL_ANY_INITIALIZATION_P))
+    {
+        gcry_check_version(NULL); /* before calling any other functions */
+        gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+        gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
+        gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+    }
 #endif
 
        if (network_config_stats != 0)
@@ -3386,7 +3409,7 @@ static int network_init (void)
        if (dispatch_thread_running == 0)
        {
                int status;
-               status = pthread_create (&dispatch_thread_id,
+               status = plugin_thread_create (&dispatch_thread_id,
                                NULL /* no attributes */,
                                dispatch_thread,
                                NULL /* no argument */);
@@ -3406,7 +3429,7 @@ static int network_init (void)
        if (receive_thread_running == 0)
        {
                int status;
-               status = pthread_create (&receive_thread_id,
+               status = plugin_thread_create (&receive_thread_id,
                                NULL /* no attributes */,
                                receive_thread,
                                NULL /* no argument */);
@@ -3426,7 +3449,7 @@ static int network_init (void)
        return (0);
 } /* int network_init */
 
-/* 
+/*
  * The flush option of the network plugin cannot flush individual identifiers.
  * All the values are added to a buffer and sent when the buffer is full, the
  * requested value may or may not be in there, it's not worth finding out. We
index 8bbf74d7d965b94ac0d21939fc3a4776116a7539..bbc455f6dd0603a1f6135c52ae1b199ff57f24f2 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/ntpd.c
- * Copyright (C) 2006-2007  Florian octo Forster
+ * Copyright (C) 2006-2012  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
@@ -16,7 +16,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #define _BSD_SOURCE /* For NI_MAXHOST */
@@ -52,11 +52,17 @@ static const char *config_keys[] =
 {
        "Host",
        "Port",
-       "ReverseLookups"
+       "ReverseLookups",
+       "IncludeUnitID"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-static int do_reverse_lookups = 1;
+static _Bool do_reverse_lookups = 1;
+
+/* This option only exists for backward compatibility. If it is false and two
+ * ntpd peers use the same refclock driver, the plugin will try to write
+ * simultaneous measurements from both to the same type instance. */
+static _Bool include_unit_id = 0;
 
 # define NTPD_DEFAULT_HOST "localhost"
 # define NTPD_DEFAULT_PORT "123"
@@ -283,6 +289,13 @@ static int ntpd_config (const char *key, const char *value)
                else
                        do_reverse_lookups = 0;
        }
+       else if (strcasecmp (key, "IncludeUnitID") == 0)
+       {
+               if (IS_TRUE (value))
+                       include_unit_id = 1;
+               else
+                       include_unit_id = 0;
+       }
        else
        {
                return (-1);
@@ -309,6 +322,18 @@ static void ntpd_submit (char *type, char *type_inst, double value)
        plugin_dispatch_values (&vl);
 }
 
+/* Each time a peer is polled, ntpd shifts the reach register to the left and
+ * sets the LSB based on whether the peer was reachable. If the LSB is zero,
+ * the values are out of date. */
+static void ntpd_submit_reach (char *type, char *type_inst, uint8_t reach,
+               double value)
+{
+       if (!(reach & 1))
+               value = NAN;
+
+       ntpd_submit (type, type_inst, value);
+}
+
 static int ntpd_connect (void)
 {
        char *host;
@@ -763,6 +788,108 @@ static double ntpd_read_fp (int32_t val_int)
        return (val_double);
 }
 
+static uint32_t ntpd_get_refclock_id (struct info_peer_summary const *peer_info)
+{
+       uint32_t addr = ntohl (peer_info->srcadr);
+       uint32_t refclock_id = (addr >> 8) & 0x00FF;
+
+       return (refclock_id);
+}
+
+static int ntpd_get_name_from_address (char *buffer, size_t buffer_size,
+               struct info_peer_summary const *peer_info, _Bool do_reverse_lookup)
+{
+       struct sockaddr_storage sa;
+       socklen_t sa_len;
+       int flags = 0;
+       int status;
+
+       memset (&sa, 0, sizeof (sa));
+
+       if (peer_info->v6_flag)
+       {
+               struct sockaddr_in6 sa6;
+
+               assert (sizeof (sa) >= sizeof (sa6));
+
+               memset (&sa6, 0, sizeof (sa6));
+               sa6.sin6_family = AF_INET6;
+               sa6.sin6_port = htons (123);
+               memcpy (&sa6.sin6_addr, &peer_info->srcadr6,
+                               sizeof (struct in6_addr));
+               sa_len = sizeof (sa6);
+
+               memcpy (&sa, &sa6, sizeof (sa6));
+       }
+       else
+       {
+               struct sockaddr_in sa4;
+
+               assert (sizeof (sa) >= sizeof (sa4));
+
+               memset (&sa4, 0, sizeof (sa4));
+               sa4.sin_family = AF_INET;
+               sa4.sin_port = htons (123);
+               memcpy (&sa4.sin_addr, &peer_info->srcadr,
+                               sizeof (struct in_addr));
+               sa_len = sizeof (sa4);
+
+               memcpy (&sa, &sa4, sizeof (sa4));
+       }
+
+       if (!do_reverse_lookup)
+               flags |= NI_NUMERICHOST;
+
+       status = getnameinfo ((struct sockaddr const *) &sa, sa_len,
+                       buffer, buffer_size,
+                       NULL, 0, /* No port name */
+                       flags);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("ntpd plugin: getnameinfo failed: %s",
+                               (status == EAI_SYSTEM)
+                               ? sstrerror (errno, errbuf, sizeof (errbuf))
+                               : gai_strerror (status));
+               return (-1);
+       }
+
+       return (0);
+} /* ntpd_get_name_from_address */
+
+static int ntpd_get_name_refclock (char *buffer, size_t buffer_size,
+               struct info_peer_summary const *peer_info)
+{
+       uint32_t refclock_id = ntpd_get_refclock_id (peer_info);
+       uint32_t unit_id = ntohl (peer_info->srcadr) & 0x00FF;
+
+       if (refclock_id >= refclock_names_num)
+               return (ntpd_get_name_from_address (buffer, buffer_size,
+                                       peer_info,
+                                       /* do_reverse_lookup = */ 0));
+
+       if (include_unit_id)
+               ssnprintf (buffer, buffer_size, "%s-%"PRIu32,
+                               refclock_names[refclock_id], unit_id);
+       else
+               sstrncpy (buffer, refclock_names[refclock_id], buffer_size);
+
+       return (0);
+} /* int ntpd_get_name_refclock */
+
+static int ntpd_get_name (char *buffer, size_t buffer_size,
+               struct info_peer_summary const *peer_info)
+{
+       uint32_t addr = ntohl (peer_info->srcadr);
+
+       if (!peer_info->v6_flag && ((addr & REFCLOCK_MASK) == REFCLOCK_ADDR))
+               return (ntpd_get_name_refclock (buffer, buffer_size,
+                                       peer_info));
+       else
+               return (ntpd_get_name_from_address (buffer, buffer_size,
+                                       peer_info, do_reverse_lookups));
+} /* int ntpd_addr_to_name */
+
 static int ntpd_read (void)
 {
        struct info_kernel *ik;
@@ -837,99 +964,26 @@ static int ntpd_read (void)
                double offset;
 
                char peername[NI_MAXHOST];
-               int refclock_id;
-               
-               ptr = ps + i;
-               refclock_id = 0;
+               uint32_t refclock_id;
 
-               /* Convert the `long floating point' offset value to double */
-               M_LFPTOD (ntohl (ptr->offset_int), ntohl (ptr->offset_frc), offset);
+               ptr = ps + i;
 
-               /* Special IP addresses for hardware clocks and stuff.. */
-               if (!ptr->v6_flag
-                               && ((ntohl (ptr->srcadr) & REFCLOCK_MASK)
-                                       == REFCLOCK_ADDR))
+               status = ntpd_get_name (peername, sizeof (peername), ptr);
+               if (status != 0)
                {
-                       struct in_addr  addr_obj;
-                       char *addr_str;
-
-                       refclock_id = (ntohl (ptr->srcadr) >> 8) & 0x000000FF;
-
-                       if (refclock_id < refclock_names_num)
-                       {
-                               sstrncpy (peername, refclock_names[refclock_id],
-                                               sizeof (peername));
-                       }
-                       else
-                       {
-                               memset ((void *) &addr_obj, '\0', sizeof (addr_obj));
-                               addr_obj.s_addr = ptr->srcadr;
-                               addr_str = inet_ntoa (addr_obj);
-
-                               sstrncpy (peername, addr_str, sizeof (peername));
-                       }
+                       ERROR ("ntpd plugin: Determining name of peer failed.");
+                       continue;
                }
-               else /* Normal network host. */
-               {
-                       struct sockaddr_storage sa;
-                       socklen_t sa_len;
-                       int flags = 0;
-
-                       memset (&sa, '\0', sizeof (sa));
-
-                       if (ptr->v6_flag)
-                       {
-                               struct sockaddr_in6 sa6;
-
-                               assert (sizeof (sa) >= sizeof (sa6));
-
-                               memset (&sa6, 0, sizeof (sa6));
-                               sa6.sin6_family = AF_INET6;
-                               sa6.sin6_port = htons (123);
-                               memcpy (&sa6.sin6_addr, &ptr->srcadr6,
-                                               sizeof (struct in6_addr));
-                               sa_len = sizeof (sa6);
-
-                               memcpy (&sa, &sa6, sizeof (sa6));
-                       }
-                       else
-                       {
-                               struct sockaddr_in sa4;
-
-                               assert (sizeof (sa) >= sizeof (sa4));
 
-                               memset (&sa4, 0, sizeof (sa4));
-                               sa4.sin_family = AF_INET;
-                               sa4.sin_port = htons (123);
-                               memcpy (&sa4.sin_addr, &ptr->srcadr,
-                                               sizeof (struct in_addr));
-                               sa_len = sizeof (sa4);
+               refclock_id = ntpd_get_refclock_id (ptr);
 
-                               memcpy (&sa, &sa4, sizeof (sa4));
-                       }
-
-                       if (do_reverse_lookups == 0)
-                               flags |= NI_NUMERICHOST;
-
-                       status = getnameinfo ((const struct sockaddr *) &sa,
-                                       sa_len,
-                                       peername, sizeof (peername),
-                                       NULL, 0, /* No port name */
-                                       flags);
-                       if (status != 0)
-                       {
-                               char errbuf[1024];
-                               ERROR ("ntpd plugin: getnameinfo failed: %s",
-                                               (status == EAI_SYSTEM)
-                                               ? sstrerror (errno, errbuf, sizeof (errbuf))
-                                               : gai_strerror (status));
-                               continue;
-                       }
-               }
+               /* Convert the `long floating point' offset value to double */
+               M_LFPTOD (ntohl (ptr->offset_int), ntohl (ptr->offset_frc), offset);
 
                DEBUG ("peer %i:\n"
                                "  peername   = %s\n"
                                "  srcadr     = 0x%08x\n"
+                               "  reach      = 0%03o\n"
                                "  delay      = %f\n"
                                "  offset_int = %i\n"
                                "  offset_frc = %i\n"
@@ -938,6 +992,7 @@ static int ntpd_read (void)
                                i,
                                peername,
                                ntohl (ptr->srcadr),
+                               ptr->reach,
                                ntpd_read_fp (ptr->delay),
                                ntohl (ptr->offset_int),
                                ntohl (ptr->offset_frc),
@@ -945,10 +1000,13 @@ static int ntpd_read (void)
                                ntpd_read_fp (ptr->dispersion));
 
                if (refclock_id != 1) /* not the system clock (offset will always be zero.. */
-                       ntpd_submit ("time_offset", peername, offset);
-               ntpd_submit ("time_dispersion", peername, ntpd_read_fp (ptr->dispersion));
+                       ntpd_submit_reach ("time_offset", peername, ptr->reach,
+                                       offset);
+               ntpd_submit_reach ("time_dispersion", peername, ptr->reach,
+                               ntpd_read_fp (ptr->dispersion));
                if (refclock_id == 0) /* not a reference clock */
-                       ntpd_submit ("delay", peername, ntpd_read_fp (ptr->delay));
+                       ntpd_submit_reach ("delay", peername, ptr->reach,
+                                       ntpd_read_fp (ptr->delay));
        }
 
        free (ps);
index a2568da2621fb8f7ea79445f199f7171822f6abb..78e508ae4d1f1bb7c0b9ae5c9ca340b9685e79e6 100644 (file)
@@ -102,6 +102,7 @@ void boot_DynaLoader (PerlInterpreter *, CV *);
 static XS (Collectd_plugin_register_ds);
 static XS (Collectd_plugin_unregister_ds);
 static XS (Collectd_plugin_dispatch_values);
+static XS (Collectd_plugin_get_interval);
 static XS (Collectd__plugin_write);
 static XS (Collectd__plugin_flush);
 static XS (Collectd_plugin_dispatch_notification);
@@ -177,6 +178,7 @@ static struct {
        { "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_get_interval",        Collectd_plugin_get_interval },
        { "Collectd::_plugin_write",              Collectd__plugin_write },
        { "Collectd::_plugin_flush",              Collectd__plugin_flush },
        { "Collectd::plugin_dispatch_notification",
@@ -1659,6 +1661,21 @@ static XS (Collectd_plugin_dispatch_values)
                XSRETURN_EMPTY;
 } /* static XS (Collectd_plugin_dispatch_values) */
 
+/*
+ * Collectd::plugin_get_interval ().
+ */
+static XS (Collectd_plugin_get_interval)
+{
+       dXSARGS;
+
+       /* make sure we don't get any unused variable warnings for 'items';
+        * don't abort, though */
+       if (items)
+               log_err ("Usage: Collectd::plugin_get_interval()");
+
+       XSRETURN_NV ((NV) CDTIME_T_TO_DOUBLE (plugin_get_interval ()));
+} /* static XS (Collectd_plugin_get_interval) */
+
 /* Collectd::plugin_write (plugin, ds, vl).
  *
  * plugin:
@@ -2130,23 +2147,20 @@ static int g_pv_set (pTHX_ SV *var, MAGIC *mg)
 
 static int g_interval_get (pTHX_ SV *var, MAGIC *mg)
 {
-       cdtime_t *interval = (cdtime_t *)mg->mg_ptr;
-       double nv;
-
-       nv = CDTIME_T_TO_DOUBLE (*interval);
-
-       sv_setnv (var, nv);
+       log_warn ("Accessing $interval_g is deprecated (and might not "
+                       "give the desired results) - plugin_get_interval() should "
+                       "be used instead.");
+       sv_setnv (var, CDTIME_T_TO_DOUBLE (interval_g));
        return 0;
 } /* static int g_interval_get (pTHX_ SV *, MAGIC *) */
 
 static int g_interval_set (pTHX_ SV *var, MAGIC *mg)
 {
-       cdtime_t *interval = (cdtime_t *)mg->mg_ptr;
-       double nv;
-
-       nv = (double)SvNV (var);
-
-       *interval = DOUBLE_TO_CDTIME_T (nv);
+       double nv = (double)SvNV (var);
+       log_warn ("Accessing $interval_g is deprecated (and might not "
+                       "give the desired results) - plugin_get_interval() should "
+                       "be used instead.");
+       interval_g = DOUBLE_TO_CDTIME_T (nv);
        return 0;
 } /* static int g_interval_set (pTHX_ SV *, MAGIC *) */
 
@@ -2202,7 +2216,7 @@ static void xs_init (pTHX)
        tmp = get_sv ("Collectd::interval_g", /* create = */ 1);
        sv_magicext (tmp, NULL, /* how = */ PERL_MAGIC_ext,
                        /* vtbl = */ &g_interval_vtbl,
-                       /* name = */ (char *) &interval_g, /* namelen = */ 0);
+                       /* name = */ NULL, /* namelen = */ 0);
 
        return;
 } /* static void xs_init (pTHX) */
index 26aa539b413a2fefcddeec0734e18c5f65dc0f68..6879733501cbedaa32cbfaa4afde3ca527771894 100644 (file)
@@ -645,7 +645,7 @@ static int plugin_init (void) /* {{{ */
   if (collector_thread_running)
     return (0);
 
-  status = pthread_create (&collector_thread_id,
+  status = plugin_thread_create (&collector_thread_id,
       /* attrs = */ NULL,
       collector_thread,
       /* args = */ NULL);
index b536f42966639f4d7e3ca260e238b7944440294f..8bbb80765f54a1b76021b3be4dea1ad5d49a832d 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/ping.c
- * Copyright (C) 2005-2009  Florian octo Forster
+ * Copyright (C) 2005-2012  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
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
+#include "utils_complain.h"
 
 #include <pthread.h>
 #include <netinet/in.h>
@@ -142,6 +143,97 @@ static void time_calc (struct timespec *ts_dest, /* {{{ */
   time_normalize (ts_dest);
 } /* }}} void time_calc */
 
+static int ping_dispatch_all (pingobj_t *pingobj) /* {{{ */
+{
+  pingobj_iter_t *iter;
+  hostlist_t *hl;
+  int status;
+
+  for (iter = ping_iterator_get (pingobj);
+      iter != NULL;
+      iter = ping_iterator_next (iter))
+  { /* {{{ */
+    char userhost[NI_MAXHOST];
+    double latency;
+    size_t param_size;
+
+    param_size = sizeof (userhost);
+    status = ping_iterator_get_info (iter,
+#ifdef PING_INFO_USERNAME
+        PING_INFO_USERNAME,
+#else
+        PING_INFO_HOSTNAME,
+#endif
+        userhost, &param_size);
+    if (status != 0)
+    {
+      WARNING ("ping plugin: ping_iterator_get_info failed: %s",
+          ping_get_error (pingobj));
+      continue;
+    }
+
+    for (hl = hostlist_head; hl != NULL; hl = hl->next)
+      if (strcmp (userhost, hl->host) == 0)
+        break;
+
+    if (hl == NULL)
+    {
+      WARNING ("ping plugin: Cannot find host %s.", userhost);
+      continue;
+    }
+
+    param_size = sizeof (latency);
+    status = ping_iterator_get_info (iter, PING_INFO_LATENCY,
+        (void *) &latency, &param_size);
+    if (status != 0)
+    {
+      WARNING ("ping plugin: ping_iterator_get_info failed: %s",
+          ping_get_error (pingobj));
+      continue;
+    }
+
+    hl->pkg_sent++;
+    if (latency >= 0.0)
+    {
+      hl->pkg_recv++;
+      hl->latency_total += latency;
+      hl->latency_squared += (latency * latency);
+
+      /* reset missed packages counter */
+      hl->pkg_missed = 0;
+    } else
+      hl->pkg_missed++;
+
+    /* if the host did not answer our last N packages, trigger a resolv. */
+    if (ping_max_missed >= 0 && hl->pkg_missed >= ping_max_missed)
+    { /* {{{ */
+      /* we reset the missed package counter here, since we only want to
+       * trigger a resolv every N packages and not every package _AFTER_ N
+       * missed packages */
+      hl->pkg_missed = 0;
+
+      WARNING ("ping plugin: host %s has not answered %d PING requests,"
+          " triggering resolve", hl->host, ping_max_missed);
+
+      /* we trigger the resolv simply be removeing and adding the host to our
+       * ping object */
+      status = ping_host_remove (pingobj, hl->host);
+      if (status != 0)
+      {
+        WARNING ("ping plugin: ping_host_remove (%s) failed.", hl->host);
+      }
+      else
+      {
+        status = ping_host_add (pingobj, hl->host);
+        if (status != 0)
+          ERROR ("ping plugin: ping_host_add (%s) failed.", hl->host);
+      }
+    } /* }}} ping_max_missed */
+  } /* }}} for (iter) */
+
+  return (0);
+} /* }}} int ping_dispatch_all */
+
 static void *ping_thread (void *arg) /* {{{ */
 {
   static pingobj_t *pingobj = NULL;
@@ -154,6 +246,8 @@ static void *ping_thread (void *arg) /* {{{ */
   hostlist_t *hl;
   int count;
 
+  c_complain_t complaint = C_COMPLAIN_INIT_STATIC;
+
   pthread_mutex_lock (&ping_lock);
 
   pingobj = ping_construct ();
@@ -213,8 +307,8 @@ static void *ping_thread (void *arg) /* {{{ */
 
   while (ping_thread_loop > 0)
   {
-    pingobj_iter_t *iter;
     int status;
+    _Bool send_successful = 0;
 
     if (gettimeofday (&tv_begin, NULL) < 0)
     {
@@ -230,10 +324,13 @@ static void *ping_thread (void *arg) /* {{{ */
     status = ping_send (pingobj);
     if (status < 0)
     {
-      ERROR ("ping plugin: ping_send failed: %s", ping_get_error (pingobj));
-      pthread_mutex_lock (&ping_lock);
-      ping_thread_error = 1;
-      break;
+      c_complain (LOG_ERR, &complaint, "ping plugin: ping_send failed: %s",
+          ping_get_error (pingobj));
+    }
+    else
+    {
+      c_release (LOG_NOTICE, &complaint, "ping plugin: ping_send succeeded.");
+      send_successful = 1;
     }
 
     pthread_mutex_lock (&ping_lock);
@@ -241,87 +338,8 @@ static void *ping_thread (void *arg) /* {{{ */
     if (ping_thread_loop <= 0)
       break;
 
-    for (iter = ping_iterator_get (pingobj);
-        iter != NULL;
-        iter = ping_iterator_next (iter))
-    { /* {{{ */
-      char userhost[NI_MAXHOST];
-      double latency;
-      size_t param_size;
-
-      param_size = sizeof (userhost);
-      status = ping_iterator_get_info (iter,
-#ifdef PING_INFO_USERNAME
-          PING_INFO_USERNAME,
-#else
-          PING_INFO_HOSTNAME,
-#endif
-          userhost, &param_size);
-      if (status != 0)
-      {
-        WARNING ("ping plugin: ping_iterator_get_info failed: %s",
-            ping_get_error (pingobj));
-        continue;
-      }
-
-      for (hl = hostlist_head; hl != NULL; hl = hl->next)
-        if (strcmp (userhost, hl->host) == 0)
-          break;
-
-      if (hl == NULL)
-      {
-        WARNING ("ping plugin: Cannot find host %s.", userhost);
-        continue;
-      }
-
-      param_size = sizeof (latency);
-      status = ping_iterator_get_info (iter, PING_INFO_LATENCY,
-          (void *) &latency, &param_size);
-      if (status != 0)
-      {
-        WARNING ("ping plugin: ping_iterator_get_info failed: %s",
-            ping_get_error (pingobj));
-        continue;
-      }
-
-      hl->pkg_sent++;
-      if (latency >= 0.0)
-      {
-        hl->pkg_recv++;
-        hl->latency_total += latency;
-        hl->latency_squared += (latency * latency);
-
-        /* reset missed packages counter */
-        hl->pkg_missed = 0;
-      } else
-        hl->pkg_missed++;
-
-      /* if the host did not answer our last N packages, trigger a resolv. */
-      if (ping_max_missed >= 0 && hl->pkg_missed >= ping_max_missed)
-      { /* {{{ */
-        /* we reset the missed package counter here, since we only want to
-         * trigger a resolv every N packages and not every package _AFTER_ N
-         * missed packages */
-        hl->pkg_missed = 0;
-
-        WARNING ("ping plugin: host %s has not answered %d PING requests,"
-          " triggering resolve", hl->host, ping_max_missed);
-
-        /* we trigger the resolv simply be removeing and adding the host to our
-         * ping object */
-        status = ping_host_remove (pingobj, hl->host);
-        if (status != 0)
-        {
-          WARNING ("ping plugin: ping_host_remove (%s) failed.", hl->host);
-        }
-        else
-        {
-          status = ping_host_add (pingobj, hl->host);
-          if (status != 0)
-            WARNING ("ping plugin: ping_host_add (%s) failed.", hl->host);
-        }
-      } /* }}} ping_max_missed */
-    } /* }}} for (iter) */
+    if (send_successful)
+      (void) ping_dispatch_all (pingobj);
 
     if (gettimeofday (&tv_end, NULL) < 0)
     {
@@ -361,7 +379,7 @@ static int start_thread (void) /* {{{ */
 
   ping_thread_loop = 1;
   ping_thread_error = 0;
-  status = pthread_create (&ping_thread_id, /* attr = */ NULL,
+  status = plugin_thread_create (&ping_thread_id, /* attr = */ NULL,
       ping_thread, /* arg = */ (void *) 0);
   if (status != 0)
   {
index cdd56bd79dd8f2f4d0c6374c7d33fe5ebbe224d7..bbede051bbfaea4972c120b4c3e4f79f5e777fe8 100644 (file)
@@ -45,6 +45,7 @@ struct callback_func_s
 {
        void *cf_callback;
        user_data_t cf_udata;
+       plugin_ctx_t cf_ctx;
 };
 typedef struct callback_func_s callback_func_t;
 
@@ -57,6 +58,7 @@ struct read_func_s
         * The `rf_super' member MUST be the first one in this structure! */
 #define rf_callback rf_super.cf_callback
 #define rf_udata rf_super.cf_udata
+#define rf_ctx rf_super.cf_ctx
        callback_func_t rf_super;
        char rf_group[DATA_MAX_NAME_LEN];
        char rf_name[DATA_MAX_NAME_LEN];
@@ -93,6 +95,9 @@ static pthread_cond_t  read_cond = PTHREAD_COND_INITIALIZER;
 static pthread_t      *read_threads = NULL;
 static int             read_threads_num = 0;
 
+static pthread_key_t   plugin_ctx_key;
+static _Bool           plugin_ctx_key_initialized = 0;
+
 /*
  * Static functions
  */
@@ -246,6 +251,8 @@ static int create_register_callback (llist_t **list, /* {{{ */
                cf->cf_udata = *ud;
        }
 
+       cf->cf_ctx = plugin_get_ctx ();
+
        return (register_callback (list, name, cf));
 } /* }}} int create_register_callback */
 
@@ -291,7 +298,7 @@ static int plugin_load_file (char *file, uint32_t flags)
                dlh = lt_dlopenadvise(file, advise);
                lt_dladvise_destroy(&advise);
        } else {
-               dlh = lt_dlopen (file);
+               dlh = lt_dlopen (file);
        }
 #else /* if LIBTOOL_VERSION == 1 */
        if (flags & PLUGIN_FLAGS_GLOBAL)
@@ -346,33 +353,34 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
        while (read_loop != 0)
        {
                read_func_t *rf;
+               plugin_ctx_t old_ctx;
                cdtime_t now;
                int status;
                int rf_type;
                int rc;
 
-               /* Get the read function that needs to be read next. */
+               /* Get the read function that needs to be read next.
+                * We don't need to hold "read_lock" for the heap, but we need
+                * to call c_heap_get_root() and pthread_cond_wait() in the
+                * same protected block. */
+               pthread_mutex_lock (&read_lock);
                rf = c_heap_get_root (read_heap);
                if (rf == NULL)
                {
-                       struct timespec abstime;
-
-                       now = cdtime ();
-
-                       CDTIME_T_TO_TIMESPEC (now + interval_g, &abstime);
-
-                       pthread_mutex_lock (&read_lock);
-                       pthread_cond_timedwait (&read_cond, &read_lock,
-                                       &abstime);
-                       pthread_mutex_unlock (&read_lock);
+                       pthread_cond_wait (&read_cond, &read_lock);
+                        pthread_mutex_unlock (&read_lock);
                        continue;
                }
+               pthread_mutex_unlock (&read_lock);
 
                if ((rf->rf_interval.tv_sec == 0) && (rf->rf_interval.tv_nsec == 0))
                {
+                       /* this should not happen, because the interval is set
+                        * for each plugin when loading it
+                        * XXX: issue a warning? */
                        now = cdtime ();
 
-                       CDTIME_T_TO_TIMESPEC (interval_g, &rf->rf_interval);
+                       CDTIME_T_TO_TIMESPEC (plugin_get_interval (), &rf->rf_interval);
 
                        rf->rf_effective_interval = rf->rf_interval;
 
@@ -423,6 +431,8 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
 
                DEBUG ("plugin_read_thread: Handling `%s'.", rf->rf_name);
 
+               old_ctx = plugin_set_ctx (rf->rf_ctx);
+
                if (rf_type == RF_SIMPLE)
                {
                        int (*callback) (void);
@@ -440,6 +450,8 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                        status = (*callback) (&rf->rf_udata);
                }
 
+               plugin_set_ctx (old_ctx);
+
                /* If the function signals failure, we will increase the
                 * intervals in which it will be called. */
                if (status != 0)
@@ -769,16 +781,49 @@ static int plugin_insert_read (read_func_t *rf)
        /* This does not fail. */
        llist_append (read_list, le);
 
+       /* Wake up all the read threads. */
+       pthread_cond_broadcast (&read_cond);
        pthread_mutex_unlock (&read_lock);
        return (0);
 } /* int plugin_insert_read */
 
+static int read_cb_wrapper (user_data_t *ud)
+{
+       int (*callback) (void);
+
+       if (ud == NULL)
+               return -1;
+
+       callback = ud->data;
+       return callback();
+} /* int read_cb_wrapper */
+
 int plugin_register_read (const char *name,
                int (*callback) (void))
 {
        read_func_t *rf;
+       plugin_ctx_t ctx = plugin_get_ctx ();
        int status;
 
+       if (ctx.interval != 0) {
+               /* If ctx.interval is not zero (== use the plugin or global
+                * interval), we need to use the "complex" read callback,
+                * because only that allows to specify a different interval.
+                * Wrap the callback using read_cb_wrapper(). */
+               struct timespec interval;
+               user_data_t user_data;
+
+               user_data.data = callback;
+               user_data.free_func = NULL;
+
+               CDTIME_T_TO_TIMESPEC (ctx.interval, &interval);
+               return plugin_register_complex_read (/* group = */ NULL,
+                               name, read_cb_wrapper, &interval, &user_data);
+       }
+
+       DEBUG ("plugin_register_read: default_interval = %.3f",
+                       CDTIME_T_TO_DOUBLE(plugin_get_interval ()));
+
        rf = malloc (sizeof (*rf));
        if (rf == NULL)
        {
@@ -790,6 +835,7 @@ int plugin_register_read (const char *name,
        rf->rf_callback = (void *) callback;
        rf->rf_udata.data = NULL;
        rf->rf_udata.free_func = NULL;
+       rf->rf_ctx = ctx;
        rf->rf_group[0] = '\0';
        sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
        rf->rf_type = RF_SIMPLE;
@@ -810,6 +856,7 @@ int plugin_register_complex_read (const char *group, const char *name,
                user_data_t *user_data)
 {
        read_func_t *rf;
+       plugin_ctx_t ctx = plugin_get_ctx ();
        int status;
 
        rf = malloc (sizeof (*rf));
@@ -831,8 +878,16 @@ int plugin_register_complex_read (const char *group, const char *name,
        {
                rf->rf_interval = *interval;
        }
+       else if (ctx.interval != 0)
+       {
+               CDTIME_T_TO_TIMESPEC (ctx.interval, &rf->rf_interval);
+       }
        rf->rf_effective_interval = rf->rf_interval;
 
+       DEBUG ("plugin_register_read: interval = %i.%09i",
+                       (int) rf->rf_interval.tv_sec,
+                       (int) rf->rf_interval.tv_nsec);
+
        /* Set user data */
        if (user_data == NULL)
        {
@@ -844,6 +899,8 @@ int plugin_register_complex_read (const char *group, const char *name,
                rf->rf_udata = *user_data;
        }
 
+       rf->rf_ctx = ctx;
+
        status = plugin_insert_read (rf);
        if (status != 0)
                sfree (rf);
@@ -1121,10 +1178,13 @@ void plugin_init_all (void)
        {
                callback_func_t *cf;
                plugin_init_cb callback;
+               plugin_ctx_t old_ctx;
 
                cf = le->value;
+               old_ctx = plugin_set_ctx (cf->cf_ctx);
                callback = cf->cf_callback;
                status = (*callback) ();
+               plugin_set_ctx (old_ctx);
 
                if (status != 0)
                {
@@ -1177,11 +1237,14 @@ int plugin_read_all_once (void)
        while (42)
        {
                read_func_t *rf;
+               plugin_ctx_t old_ctx;
 
                rf = c_heap_get_root (read_heap);
                if (rf == NULL)
                        break;
 
+               old_ctx = plugin_set_ctx (rf->rf_ctx);
+
                if (rf->rf_type == RF_SIMPLE)
                {
                        int (*callback) (void);
@@ -1197,6 +1260,8 @@ int plugin_read_all_once (void)
                        status = (*callback) (&rf->rf_udata);
                }
 
+               plugin_set_ctx (old_ctx);
+
                if (status != 0)
                {
                        NOTICE ("read-function of plugin `%s' failed.",
@@ -1243,6 +1308,9 @@ int plugin_write (const char *plugin, /* {{{ */
       callback_func_t *cf = le->value;
       plugin_write_cb callback;
 
+      /* do not switch plugin context; rather keep the context (interval)
+       * information of the calling read plugin */
+
       DEBUG ("plugin: plugin_write: Writing values via %s.", le->key);
       callback = cf->cf_callback;
       status = (*callback) (ds, vl, &cf->cf_udata);
@@ -1278,6 +1346,9 @@ int plugin_write (const char *plugin, /* {{{ */
 
     cf = le->value;
 
+    /* do not switch plugin context; rather keep the context (interval)
+     * information of the calling read plugin */
+
     DEBUG ("plugin: plugin_write: Writing values via %s.", le->key);
     callback = cf->cf_callback;
     status = (*callback) (ds, vl, &cf->cf_udata);
@@ -1298,6 +1369,7 @@ int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier)
   {
     callback_func_t *cf;
     plugin_flush_cb callback;
+    plugin_ctx_t old_ctx;
 
     if ((plugin != NULL)
         && (strcmp (plugin, le->key) != 0))
@@ -1307,10 +1379,13 @@ int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier)
     }
 
     cf = le->value;
+    old_ctx = plugin_set_ctx (cf->cf_ctx);
     callback = cf->cf_callback;
 
     (*callback) (timeout, identifier, &cf->cf_udata);
 
+    plugin_set_ctx (old_ctx);
+
     le = le->next;
   }
   return (0);
@@ -1343,8 +1418,10 @@ void plugin_shutdown_all (void)
        {
                callback_func_t *cf;
                plugin_shutdown_cb callback;
+               plugin_ctx_t old_ctx;
 
                cf = le->value;
+               old_ctx = plugin_set_ctx (cf->cf_ctx);
                callback = cf->cf_callback;
 
                /* Advance the pointer before calling the callback allows
@@ -1354,6 +1431,8 @@ void plugin_shutdown_all (void)
                le = le->next;
 
                (*callback) ();
+
+               plugin_set_ctx (old_ctx);
        }
 
        /* Write plugins which use the `user_data' pointer usually need the
@@ -1382,12 +1461,15 @@ int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
   {
     callback_func_t *cf;
     plugin_missing_cb callback;
+    plugin_ctx_t old_ctx;
     int status;
 
     cf = le->value;
+    old_ctx = plugin_set_ctx (cf->cf_ctx);
     callback = cf->cf_callback;
 
     status = (*callback) (vl, &cf->cf_udata);
+    plugin_set_ctx (old_ctx);
     if (status != 0)
     {
       if (status < 0)
@@ -1463,7 +1545,25 @@ int plugin_dispatch_values (value_list_t *vl)
                vl->time = cdtime ();
 
        if (vl->interval <= 0)
-               vl->interval = interval_g;
+       {
+               plugin_ctx_t ctx = plugin_get_ctx ();
+
+               if (ctx.interval != 0)
+                       vl->interval = ctx.interval;
+               else
+               {
+                       char name[6 * DATA_MAX_NAME_LEN];
+                       FORMAT_VL (name, sizeof (name), vl);
+                       ERROR ("plugin_dispatch_values: Unable to determine "
+                                       "interval from context for "
+                                       "value list \"%s\". "
+                                       "This indicates a broken plugin. "
+                                       "Please report this problem to the "
+                                       "collectd mailing list or at "
+                                       "<http://collectd.org/bugs/>.", name);
+                       vl->interval = cf_get_default_interval ();
+               }
+       }
 
        DEBUG ("plugin_dispatch_values: time = %.3f; interval = %.3f; "
                        "host = %s; "
@@ -1653,6 +1753,9 @@ int plugin_dispatch_notification (const notification_t *notif)
                plugin_notification_cb callback;
                int status;
 
+               /* do not switch plugin context; rather keep the context
+                * (interval) information of the calling plugin */
+
                cf = le->value;
                callback = cf->cf_callback;
                status = (*callback) (notif, &cf->cf_udata);
@@ -1700,6 +1803,9 @@ void plugin_log (int level, const char *format, ...)
                cf = le->value;
                callback = cf->cf_callback;
 
+               /* do not switch plugin context; rather keep the context
+                * (interval) information of the calling plugin */
+
                (*callback) (level, msg, &cf->cf_udata);
 
                le = le->next;
@@ -1932,4 +2038,122 @@ int plugin_notification_meta_free (notification_meta_t *n)
   return (0);
 } /* int plugin_notification_meta_free */
 
+static void plugin_ctx_destructor (void *ctx)
+{
+       sfree (ctx);
+} /* void plugin_ctx_destructor */
+
+static plugin_ctx_t ctx_init = { /* interval = */ 0 };
+
+static plugin_ctx_t *plugin_ctx_create (void)
+{
+       plugin_ctx_t *ctx;
+
+       ctx = malloc (sizeof (*ctx));
+       if (ctx == NULL) {
+               char errbuf[1024];
+               ERROR ("Failed to allocate plugin context: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return NULL;
+       }
+
+       *ctx = ctx_init;
+       assert (plugin_ctx_key_initialized);
+       pthread_setspecific (plugin_ctx_key, ctx);
+       DEBUG("Created new plugin context.");
+       return (ctx);
+} /* int plugin_ctx_create */
+
+void plugin_init_ctx (void)
+{
+       pthread_key_create (&plugin_ctx_key, plugin_ctx_destructor);
+       plugin_ctx_key_initialized = 1;
+} /* void plugin_init_ctx */
+
+plugin_ctx_t plugin_get_ctx (void)
+{
+       plugin_ctx_t *ctx;
+
+       assert (plugin_ctx_key_initialized);
+       ctx = pthread_getspecific (plugin_ctx_key);
+
+       if (ctx == NULL) {
+               ctx = plugin_ctx_create ();
+               /* this must no happen -- exit() instead? */
+               if (ctx == NULL)
+                       return ctx_init;
+       }
+
+       return (*ctx);
+} /* plugin_ctx_t plugin_get_ctx */
+
+plugin_ctx_t plugin_set_ctx (plugin_ctx_t ctx)
+{
+       plugin_ctx_t *c;
+       plugin_ctx_t old;
+
+       assert (plugin_ctx_key_initialized);
+       c = pthread_getspecific (plugin_ctx_key);
+
+       if (c == NULL) {
+               c = plugin_ctx_create ();
+               /* this must no happen -- exit() instead? */
+               if (c == NULL)
+                       return ctx_init;
+       }
+
+       old = *c;
+       *c = ctx;
+
+       return (old);
+} /* void plugin_set_ctx */
+
+cdtime_t plugin_get_interval (void)
+{
+       cdtime_t interval;
+
+       interval = plugin_get_ctx().interval;
+       if (interval > 0)
+               return interval;
+
+       return cf_get_default_interval ();
+} /* cdtime_t plugin_get_interval */
+
+typedef struct {
+       plugin_ctx_t ctx;
+       void *(*start_routine) (void *);
+       void *arg;
+} plugin_thread_t;
+
+static void *plugin_thread_start (void *arg)
+{
+       plugin_thread_t *plugin_thread = arg;
+
+       void *(*start_routine) (void *) = plugin_thread->start_routine;
+       void *plugin_arg = plugin_thread->arg;
+
+       plugin_set_ctx (plugin_thread->ctx);
+
+       free (plugin_thread);
+
+       return start_routine (plugin_arg);
+} /* void *plugin_thread_start */
+
+int plugin_thread_create (pthread_t *thread, const pthread_attr_t *attr,
+               void *(*start_routine) (void *), void *arg)
+{
+       plugin_thread_t *plugin_thread;
+
+       plugin_thread = malloc (sizeof (*plugin_thread));
+       if (plugin_thread == NULL)
+               return -1;
+
+       plugin_thread->ctx           = plugin_get_ctx ();
+       plugin_thread->start_routine = start_routine;
+       plugin_thread->arg           = arg;
+
+       return pthread_create (thread, attr,
+                       plugin_thread_start, plugin_thread);
+} /* int plugin_thread_create */
+
 /* vim: set sw=8 ts=8 noet fdm=marker : */
index dd65b5982c6eb6ca80e5f8b63d947a00a05ca132..0f35de5615b9357b616b1bbb60d9a1b7c069f894 100644 (file)
@@ -65,6 +65,8 @@
 #define NOTIF_WARNING 2
 #define NOTIF_OKAY    4
 
+#define plugin_interval (plugin_get_ctx().interval)
+
 /*
  * Public data types
  */
@@ -97,7 +99,8 @@ struct value_list_s
 };
 typedef struct value_list_s value_list_t;
 
-#define VALUE_LIST_INIT { NULL, 0, 0, interval_g, "localhost", "", "", "", "", NULL }
+#define VALUE_LIST_INIT { NULL, 0, 0, plugin_get_interval (), \
+       "localhost", "", "", "", "", NULL }
 #define VALUE_LIST_STATIC { NULL, 0, 0, 0, "localhost", "", "", "", "", NULL }
 
 struct data_source_s
@@ -161,6 +164,12 @@ struct user_data_s
 };
 typedef struct user_data_s user_data_t;
 
+struct plugin_ctx_s
+{
+       cdtime_t interval;
+};
+typedef struct plugin_ctx_s plugin_ctx_t;
+
 /*
  * Callback types
  */
@@ -363,4 +372,31 @@ int plugin_notification_meta_copy (notification_t *dst,
 
 int plugin_notification_meta_free (notification_meta_t *n);
 
+/*
+ * Plugin context management.
+ */
+
+void plugin_init_ctx (void);
+
+plugin_ctx_t plugin_get_ctx (void);
+plugin_ctx_t plugin_set_ctx (plugin_ctx_t ctx);
+
+/*
+ * NAME
+ *  plugin_get_interval
+ *
+ * DESCRIPTION
+ *  This function returns the current value of the plugin's interval. The
+ *  return value will be strictly greater than zero in all cases. If
+ *  everything else fails, it will fall back to 10 seconds.
+ */
+cdtime_t plugin_get_interval (void);
+
+/*
+ * Context-aware thread management.
+ */
+
+int plugin_thread_create (pthread_t *thread, const pthread_attr_t *attr,
+               void *(*start_routine) (void *), void *arg);
+
 #endif /* PLUGIN_H */
index 0a5e66c25d934acd4de939cd049e6070a3121ca9..15d4666b9eaf910c2d91f80ce2552291eb9d6e45 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/postgresql.c
- * Copyright (C) 2008, 2009  Sebastian Harl
- * Copyright (C) 2009        Florian Forster
+ * Copyright (C) 2008-2012  Sebastian Harl
+ * Copyright (C) 2009       Florian Forster
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "configfile.h"
 #include "plugin.h"
 
+#include "utils_cache.h"
 #include "utils_db_query.h"
 #include "utils_complain.h"
 
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
 #include <pg_config_manual.h>
 #include <libpq-fe.h>
 
 #define log_err(...) ERROR ("postgresql: " __VA_ARGS__)
 #define log_warn(...) WARNING ("postgresql: " __VA_ARGS__)
 #define log_info(...) INFO ("postgresql: " __VA_ARGS__)
+#define log_debug(...) DEBUG ("postgresql: " __VA_ARGS__)
 
 #ifndef C_PSQL_DEFAULT_CONF
 # define C_PSQL_DEFAULT_CONF PKGDATADIR "/postgresql_default.conf"
@@ -95,6 +101,7 @@ typedef enum {
        C_PSQL_PARAM_DB,
        C_PSQL_PARAM_USER,
        C_PSQL_PARAM_INTERVAL,
+       C_PSQL_PARAM_INSTANCE,
 } c_psql_param_t;
 
 /* Parameter configuration. Stored as `user data' in the query objects. */
@@ -103,6 +110,12 @@ typedef struct {
        int             params_num;
 } c_psql_user_data_t;
 
+typedef struct {
+       char *name;
+       char *statement;
+       _Bool store_rates;
+} c_psql_writer_t;
+
 typedef struct {
        PGconn      *conn;
        c_complain_t conn_complaint;
@@ -117,19 +130,33 @@ typedef struct {
        udb_query_t    **queries;
        size_t           queries_num;
 
+       c_psql_writer_t **writers;
+       size_t            writers_num;
+
+       /* make sure we don't access the database object in parallel */
+       pthread_mutex_t   db_lock;
+
        cdtime_t interval;
 
+       /* writer "caching" settings */
+       cdtime_t commit_interval;
+       cdtime_t next_commit;
+
        char *host;
        char *port;
        char *database;
        char *user;
        char *password;
 
+       char *instance;
+
        char *sslmode;
 
        char *krbsrvname;
 
        char *service;
+
+       int ref_cnt;
 } c_psql_database_t;
 
 static char *def_queries[] = {
@@ -143,19 +170,69 @@ static char *def_queries[] = {
 };
 static int def_queries_num = STATIC_ARRAY_SIZE (def_queries);
 
+static c_psql_database_t *databases     = NULL;
+static size_t             databases_num = 0;
+
 static udb_query_t      **queries       = NULL;
 static size_t             queries_num   = 0;
 
+static c_psql_writer_t   *writers       = NULL;
+static size_t             writers_num   = 0;
+
+static int c_psql_begin (c_psql_database_t *db)
+{
+       PGresult *r = PQexec (db->conn, "BEGIN");
+
+       int status = 1;
+
+       if (r != NULL) {
+               if (PGRES_COMMAND_OK == PQresultStatus (r)) {
+                       db->next_commit = cdtime() + db->commit_interval;
+                       status = 0;
+               }
+               else
+                       log_warn ("Failed to initiate ('BEGIN') transaction: %s",
+                                       PQerrorMessage (db->conn));
+               PQclear (r);
+       }
+       return status;
+} /* c_psql_begin */
+
+static int c_psql_commit (c_psql_database_t *db)
+{
+       PGresult *r = PQexec (db->conn, "COMMIT");
+
+       int status = 1;
+
+       if (r != NULL) {
+               if (PGRES_COMMAND_OK == PQresultStatus (r)) {
+                       db->next_commit = 0;
+                       log_debug ("Successfully committed transaction.");
+                       status = 0;
+               }
+               else
+                       log_warn ("Failed to commit transaction: %s",
+                                       PQerrorMessage (db->conn));
+               PQclear (r);
+       }
+       return status;
+} /* c_psql_commit */
+
 static c_psql_database_t *c_psql_database_new (const char *name)
 {
        c_psql_database_t *db;
 
-       db = (c_psql_database_t *)malloc (sizeof (*db));
+       db = (c_psql_database_t *)realloc (databases,
+                       (databases_num + 1) * sizeof (*db));
        if (NULL == db) {
                log_err ("Out of memory.");
                return NULL;
        }
 
+       databases = db;
+       db = databases + databases_num;
+       ++databases_num;
+
        db->conn = NULL;
 
        C_COMPLAIN_INIT (&db->conn_complaint);
@@ -169,19 +246,31 @@ static c_psql_database_t *c_psql_database_new (const char *name)
        db->queries        = NULL;
        db->queries_num    = 0;
 
+       db->writers        = NULL;
+       db->writers_num    = 0;
+
+       pthread_mutex_init (&db->db_lock, /* attrs = */ NULL);
+
        db->interval   = 0;
 
+       db->commit_interval = 0;
+       db->next_commit     = 0;
+
        db->database   = sstrdup (name);
        db->host       = NULL;
        db->port       = NULL;
        db->user       = NULL;
        db->password   = NULL;
 
+       db->instance   = sstrdup (name);
+
        db->sslmode    = NULL;
 
        db->krbsrvname = NULL;
 
        db->service    = NULL;
+
+       db->ref_cnt    = 0;
        return db;
 } /* c_psql_database_new */
 
@@ -191,6 +280,17 @@ static void c_psql_database_delete (void *data)
 
        c_psql_database_t *db = data;
 
+       --db->ref_cnt;
+       /* readers and writers may access this database */
+       if (db->ref_cnt > 0)
+               return;
+
+       /* wait for the lock to be released by the last writer */
+       pthread_mutex_lock (&db->db_lock);
+
+       if (db->next_commit > 0)
+               c_psql_commit (db);
+
        PQfinish (db->conn);
        db->conn = NULL;
 
@@ -202,17 +302,29 @@ static void c_psql_database_delete (void *data)
        sfree (db->queries);
        db->queries_num = 0;
 
+       sfree (db->writers);
+       db->writers_num = 0;
+
+       pthread_mutex_unlock (&db->db_lock);
+
+       pthread_mutex_destroy (&db->db_lock);
+
        sfree (db->database);
        sfree (db->host);
        sfree (db->port);
        sfree (db->user);
        sfree (db->password);
 
+       sfree (db->instance);
+
        sfree (db->sslmode);
 
        sfree (db->krbsrvname);
 
        sfree (db->service);
+
+       /* don't care about freeing or reordering the 'databases' array
+        * this is done in 'shutdown' */
        return;
 } /* c_psql_database_delete */
 
@@ -223,7 +335,7 @@ static int c_psql_connect (c_psql_database_t *db)
        int   buf_len = sizeof (conninfo);
        int   status;
 
-       if (! db)
+       if ((! db) || (! db->database))
                return -1;
 
        status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
@@ -271,8 +383,9 @@ static int c_psql_check_connection (c_psql_database_t *db)
 
                if (CONNECTION_OK != PQstatus (db->conn)) {
                        c_complain (LOG_ERR, &db->conn_complaint,
-                                       "Failed to connect to database %s: %s",
-                                       db->database, PQerrorMessage (db->conn));
+                                       "Failed to connect to database %s (%s): %s",
+                                       db->database, db->instance,
+                                       PQerrorMessage (db->conn));
                        return -1;
                }
 
@@ -337,9 +450,13 @@ static PGresult *c_psql_exec_query_params (c_psql_database_t *db,
                        case C_PSQL_PARAM_INTERVAL:
                                ssnprintf (interval, sizeof (interval), "%.3f",
                                                (db->interval > 0)
-                                               ? CDTIME_T_TO_DOUBLE (db->interval) : interval_g);
+                                               ? CDTIME_T_TO_DOUBLE (db->interval)
+                                               : plugin_get_interval ());
                                params[i] = interval;
                                break;
+                       case C_PSQL_PARAM_INSTANCE:
+                               params[i] = db->instance;
+                               break;
                        default:
                                assert (0);
                }
@@ -351,6 +468,7 @@ static PGresult *c_psql_exec_query_params (c_psql_database_t *db,
                        NULL, NULL, /* return text data */ 0);
 } /* c_psql_exec_query_params */
 
+/* db->db_lock must be locked when calling this function */
 static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
                udb_query_preparation_area_t *prep_area)
 {
@@ -377,30 +495,40 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
        else if ((NULL == data) || (0 == data->params_num))
                res = c_psql_exec_query_noparams (db, q);
        else {
-               log_err ("Connection to database \"%s\" does not support parameters "
-                               "(protocol version %d) - cannot execute query \"%s\".",
-                               db->database, db->proto_version,
+               log_err ("Connection to database \"%s\" (%s) does not support "
+                               "parameters (protocol version %d) - "
+                               "cannot execute query \"%s\".",
+                               db->database, db->instance, db->proto_version,
                                udb_query_get_name (q));
                return -1;
        }
 
+       /* give c_psql_write() a chance to acquire the lock if called recursively
+        * through dispatch_values(); this will happen if, both, queries and
+        * writers are configured for a single connection */
+       pthread_mutex_unlock (&db->db_lock);
+
        column_names = NULL;
        column_values = NULL;
 
-#define BAIL_OUT(status) \
-       sfree (column_names); \
-       sfree (column_values); \
-       PQclear (res); \
-       return status
-
        if (PGRES_TUPLES_OK != PQresultStatus (res)) {
+               pthread_mutex_lock (&db->db_lock);
+
                log_err ("Failed to execute SQL query: %s",
                                PQerrorMessage (db->conn));
                log_info ("SQL query was: %s",
                                udb_query_get_statement (q));
-               BAIL_OUT (-1);
+               PQclear (res);
+               return -1;
        }
 
+#define BAIL_OUT(status) \
+       sfree (column_names); \
+       sfree (column_values); \
+       PQclear (res); \
+       pthread_mutex_lock (&db->db_lock); \
+       return status
+
        rows_num = PQntuples (res);
        if (1 > rows_num) {
                BAIL_OUT (0);
@@ -436,7 +564,7 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
                host = db->host;
 
        status = udb_query_prepare_result (q, prep_area, host, "postgresql",
-                       db->database, column_names, (size_t) column_num, db->interval);
+                       db->instance, column_names, (size_t) column_num, db->interval);
        if (0 != status) {
                log_err ("udb_query_prepare_result failed with status %i.",
                                status);
@@ -487,9 +615,15 @@ static int c_psql_read (user_data_t *ud)
        db = ud->data;
 
        assert (NULL != db->database);
+       assert (NULL != db->instance);
+       assert (NULL != db->queries);
+
+       pthread_mutex_lock (&db->db_lock);
 
-       if (0 != c_psql_check_connection (db))
+       if (0 != c_psql_check_connection (db)) {
+               pthread_mutex_unlock (&db->db_lock);
                return -1;
+       }
 
        for (i = 0; i < db->queries_num; ++i)
        {
@@ -507,19 +641,377 @@ static int c_psql_read (user_data_t *ud)
                        success = 1;
        }
 
+       pthread_mutex_unlock (&db->db_lock);
+
        if (! success)
                return -1;
        return 0;
 } /* c_psql_read */
 
+static char *values_name_to_sqlarray (const data_set_t *ds,
+               char *string, size_t string_len)
+{
+       char  *str_ptr;
+       size_t str_len;
+
+       int i;
+
+       str_ptr = string;
+       str_len = string_len;
+
+       for (i = 0; i < ds->ds_num; ++i) {
+               int status = ssnprintf (str_ptr, str_len, ",'%s'", ds->ds[i].name);
+
+               if (status < 1)
+                       return NULL;
+               else if ((size_t)status >= str_len) {
+                       str_len = 0;
+                       break;
+               }
+               else {
+                       str_ptr += status;
+                       str_len -= (size_t)status;
+               }
+       }
+
+       if (str_len <= 2) {
+               log_err ("c_psql_write: Failed to stringify value names");
+               return NULL;
+       }
+
+       /* overwrite the first comma */
+       string[0] = '{';
+       str_ptr[0] = '}';
+       str_ptr[1] = '\0';
+
+       return string;
+} /* values_name_to_sqlarray */
+
+static char *values_type_to_sqlarray (const data_set_t *ds,
+               char *string, size_t string_len, _Bool store_rates)
+{
+       char  *str_ptr;
+       size_t str_len;
+
+       int i;
+
+       str_ptr = string;
+       str_len = string_len;
+
+       for (i = 0; i < ds->ds_num; ++i) {
+               int status;
+
+               if (store_rates)
+                       status = ssnprintf(str_ptr, str_len, ",'gauge'");
+               else
+                       status = ssnprintf(str_ptr, str_len, ",'%s'",
+                                       DS_TYPE_TO_STRING (ds->ds[i].type));
+
+               if (status < 1) {
+                       str_len = 0;
+                       break;
+               }
+               else if ((size_t)status >= str_len) {
+                       str_len = 0;
+                       break;
+               }
+               else {
+                       str_ptr += status;
+                       str_len -= (size_t)status;
+               }
+       }
+
+       if (str_len <= 2) {
+               log_err ("c_psql_write: Failed to stringify value types");
+               return NULL;
+       }
+
+       /* overwrite the first comma */
+       string[0] = '{';
+       str_ptr[0] = '}';
+       str_ptr[1] = '\0';
+
+       return string;
+} /* values_type_to_sqlarray */
+
+static char *values_to_sqlarray (const data_set_t *ds, const value_list_t *vl,
+               char *string, size_t string_len, _Bool store_rates)
+{
+       char  *str_ptr;
+       size_t str_len;
+
+       gauge_t *rates = NULL;
+
+       int i;
+
+       str_ptr = string;
+       str_len = string_len;
+
+       for (i = 0; i < vl->values_len; ++i) {
+               int status = 0;
+
+               if ((ds->ds[i].type != DS_TYPE_GAUGE)
+                               && (ds->ds[i].type != DS_TYPE_COUNTER)
+                               && (ds->ds[i].type != DS_TYPE_DERIVE)
+                               && (ds->ds[i].type != DS_TYPE_ABSOLUTE)) {
+                       log_err ("c_psql_write: Unknown data source type: %i",
+                                       ds->ds[i].type);
+                       sfree (rates);
+                       return NULL;
+               }
+
+               if (ds->ds[i].type == DS_TYPE_GAUGE)
+                       status = ssnprintf (str_ptr, str_len,
+                                       ",%f", vl->values[i].gauge);
+               else if (store_rates) {
+                       if (rates == NULL)
+                               rates = uc_get_rate (ds, vl);
+
+                       if (rates == NULL) {
+                               log_err ("c_psql_write: Failed to determine rate");
+                               return NULL;
+                       }
+
+                       status = ssnprintf (str_ptr, str_len,
+                                       ",%lf", rates[i]);
+               }
+               else if (ds->ds[i].type == DS_TYPE_COUNTER)
+                       status = ssnprintf (str_ptr, str_len,
+                                       ",%llu", vl->values[i].counter);
+               else if (ds->ds[i].type == DS_TYPE_DERIVE)
+                       status = ssnprintf (str_ptr, str_len,
+                                       ",%"PRIi64, vl->values[i].derive);
+               else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+                       status = ssnprintf (str_ptr, str_len,
+                                       ",%"PRIu64, vl->values[i].absolute);
+
+               if (status < 1) {
+                       str_len = 0;
+                       break;
+               }
+               else if ((size_t)status >= str_len) {
+                       str_len = 0;
+                       break;
+               }
+               else {
+                       str_ptr += status;
+                       str_len -= (size_t)status;
+               }
+       }
+
+       sfree (rates);
+
+       if (str_len <= 2) {
+               log_err ("c_psql_write: Failed to stringify value list");
+               return NULL;
+       }
+
+       /* overwrite the first comma */
+       string[0] = '{';
+       str_ptr[0] = '}';
+       str_ptr[1] = '\0';
+
+       return string;
+} /* values_to_sqlarray */
+
+static int c_psql_write (const data_set_t *ds, const value_list_t *vl,
+               user_data_t *ud)
+{
+       c_psql_database_t *db;
+
+       char time_str[32];
+       char values_name_str[1024];
+       char values_type_str[1024];
+       char values_str[1024];
+
+       const char *params[9];
+
+       int success = 0;
+       int i;
+
+       if ((ud == NULL) || (ud->data == NULL)) {
+               log_err ("c_psql_write: Invalid user data.");
+               return -1;
+       }
+
+       db = ud->data;
+       assert (db->database != NULL);
+       assert (db->writers != NULL);
+
+       if (cdtime_to_iso8601 (time_str, sizeof (time_str), vl->time) == 0) {
+               log_err ("c_psql_write: Failed to convert time to ISO 8601 format");
+               return -1;
+       }
+
+       if (values_name_to_sqlarray (ds,
+                               values_name_str, sizeof (values_name_str)) == NULL)
+               return -1;
+
+#define VALUE_OR_NULL(v) ((((v) == NULL) || (*(v) == '\0')) ? NULL : (v))
+
+       params[0] = time_str;
+       params[1] = vl->host;
+       params[2] = vl->plugin;
+       params[3] = VALUE_OR_NULL(vl->plugin_instance);
+       params[4] = vl->type;
+       params[5] = VALUE_OR_NULL(vl->type_instance);
+       params[6] = values_name_str;
+
+#undef VALUE_OR_NULL
+
+       pthread_mutex_lock (&db->db_lock);
+
+       if (0 != c_psql_check_connection (db)) {
+               pthread_mutex_unlock (&db->db_lock);
+               return -1;
+       }
+
+       if ((db->commit_interval > 0)
+                       && (db->next_commit == 0))
+               c_psql_begin (db);
+
+       for (i = 0; i < db->writers_num; ++i) {
+               c_psql_writer_t *writer;
+               PGresult *res;
+
+               writer = db->writers[i];
+
+               if (values_type_to_sqlarray (ds,
+                                       values_type_str, sizeof (values_type_str),
+                                       writer->store_rates) == NULL) {
+                       pthread_mutex_unlock (&db->db_lock);
+                       return -1;
+               }
+
+               if (values_to_sqlarray (ds, vl,
+                                       values_str, sizeof (values_str),
+                                       writer->store_rates) == NULL) {
+                       pthread_mutex_unlock (&db->db_lock);
+                       return -1;
+               }
+
+               params[7] = values_type_str;
+               params[8] = values_str;
+
+               res = PQexecParams (db->conn, writer->statement,
+                               STATIC_ARRAY_SIZE (params), NULL,
+                               (const char *const *)params,
+                               NULL, NULL, /* return text data */ 0);
+
+               if ((PGRES_COMMAND_OK != PQresultStatus (res))
+                               && (PGRES_TUPLES_OK != PQresultStatus (res))) {
+                       if ((CONNECTION_OK != PQstatus (db->conn))
+                                       && (0 == c_psql_check_connection (db))) {
+                               PQclear (res);
+
+                               /* try again */
+                               res = PQexecParams (db->conn, writer->statement,
+                                               STATIC_ARRAY_SIZE (params), NULL,
+                                               (const char *const *)params,
+                                               NULL, NULL, /* return text data */ 0);
+
+                               if ((PGRES_COMMAND_OK == PQresultStatus (res))
+                                               || (PGRES_TUPLES_OK == PQresultStatus (res))) {
+                                       success = 1;
+                                       continue;
+                               }
+                       }
+
+                       log_err ("Failed to execute SQL query: %s",
+                                       PQerrorMessage (db->conn));
+                       log_info ("SQL query was: '%s', "
+                                       "params: %s, %s, %s, %s, %s, %s, %s, %s",
+                                       writer->statement,
+                                       params[0], params[1], params[2], params[3],
+                                       params[4], params[5], params[6], params[7]);
+
+                       /* this will abort any current transaction -> restart */
+                       if (db->next_commit > 0)
+                               c_psql_commit (db);
+
+                       pthread_mutex_unlock (&db->db_lock);
+                       return -1;
+               }
+               success = 1;
+       }
+
+       if ((db->next_commit > 0)
+                       && (cdtime () > db->next_commit))
+               c_psql_commit (db);
+
+       pthread_mutex_unlock (&db->db_lock);
+
+       if (! success)
+               return -1;
+       return 0;
+} /* c_psql_write */
+
+/* We cannot flush single identifiers as all we do is to commit the currently
+ * running transaction, thus making sure that all written data is actually
+ * visible to everybody. */
+static int c_psql_flush (cdtime_t timeout,
+               __attribute__((unused)) const char *ident,
+               user_data_t *ud)
+{
+       c_psql_database_t *dbs = databases;
+       size_t dbs_num = databases_num;
+       size_t i;
+
+       if ((ud != NULL) && (ud->data != NULL)) {
+               dbs = ud->data;
+               dbs_num = 1;
+       }
+
+       for (i = 0; i < dbs_num; ++i) {
+               c_psql_database_t *db = dbs + i;
+
+               /* don't commit if the timeout is larger than the regular commit
+                * interval as in that case all requested data has already been
+                * committed */
+               if ((db->next_commit > 0) && (db->commit_interval > timeout))
+                       c_psql_commit (db);
+       }
+       return 0;
+} /* c_psql_flush */
+
 static int c_psql_shutdown (void)
 {
+       size_t i = 0;
+
+       _Bool had_flush = 0;
+
        plugin_unregister_read_group ("postgresql");
 
+       for (i = 0; i < databases_num; ++i) {
+               c_psql_database_t *db = databases + i;
+
+               if (db->writers_num > 0) {
+                       char cb_name[DATA_MAX_NAME_LEN];
+                       ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s",
+                                       db->database);
+
+                       if (! had_flush) {
+                               plugin_unregister_flush ("postgresql");
+                               had_flush = 1;
+                       }
+
+                       plugin_unregister_flush (cb_name);
+                       plugin_unregister_write (cb_name);
+               }
+       }
+
        udb_query_free (queries, queries_num);
        queries = NULL;
        queries_num = 0;
 
+       sfree (writers);
+       writers = NULL;
+       writers_num = 0;
+
+       sfree (databases);
+       databases = NULL;
+       databases_num = 0;
+
        return 0;
 } /* c_psql_shutdown */
 
@@ -558,6 +1050,8 @@ static int config_query_param_add (udb_query_t *q, oconfig_item_t *ci)
                data->params[data->params_num] = C_PSQL_PARAM_USER;
        else if (0 == strcasecmp (param_str, "interval"))
                data->params[data->params_num] = C_PSQL_PARAM_INTERVAL;
+       else if (0 == strcasecmp (param_str, "instance"))
+               data->params[data->params_num] = C_PSQL_PARAM_INSTANCE;
        else {
                log_err ("Invalid parameter \"%s\".", param_str);
                return 1;
@@ -579,6 +1073,103 @@ static int config_query_callback (udb_query_t *q, oconfig_item_t *ci)
        return (-1);
 } /* config_query_callback */
 
+static int config_add_writer (oconfig_item_t *ci,
+               c_psql_writer_t *src_writers, size_t src_writers_num,
+               c_psql_writer_t ***dst_writers, size_t *dst_writers_num)
+{
+       char *name;
+
+       size_t i;
+
+       if ((ci == NULL) || (dst_writers == NULL) || (dst_writers_num == NULL))
+               return -1;
+
+       if ((ci->values_num != 1)
+                       || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+               log_err ("`Writer' expects a single string argument.");
+               return 1;
+       }
+
+       name = ci->values[0].value.string;
+
+       for (i = 0; i < src_writers_num; ++i) {
+               c_psql_writer_t **tmp;
+
+               if (strcasecmp (name, src_writers[i].name) != 0)
+                       continue;
+
+               tmp = (c_psql_writer_t **)realloc (*dst_writers,
+                               sizeof (**dst_writers) * (*dst_writers_num + 1));
+               if (tmp == NULL) {
+                       log_err ("Out of memory.");
+                       return -1;
+               }
+
+               tmp[*dst_writers_num] = src_writers + i;
+
+               *dst_writers = tmp;
+               ++(*dst_writers_num);
+               break;
+       }
+
+       if (i >= src_writers_num) {
+               log_err ("No such writer: `%s'", name);
+               return -1;
+       }
+
+       return 0;
+} /* config_add_writer */
+
+static int c_psql_config_writer (oconfig_item_t *ci)
+{
+       c_psql_writer_t *writer;
+       c_psql_writer_t *tmp;
+
+       int status = 0;
+       int i;
+
+       if ((ci->values_num != 1)
+                       || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+               log_err ("<Writer> expects a single string argument.");
+               return 1;
+       }
+
+       tmp = (c_psql_writer_t *)realloc (writers,
+                       sizeof (*writers) * (writers_num + 1));
+       if (tmp == NULL) {
+               log_err ("Out of memory.");
+               return -1;
+       }
+
+       writers = tmp;
+       writer  = writers + writers_num;
+       ++writers_num;
+
+       writer->name = sstrdup (ci->values[0].value.string);
+       writer->statement = NULL;
+       writer->store_rates = 1;
+
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *c = ci->children + i;
+
+               if (strcasecmp ("Statement", c->key) == 0)
+                       status = cf_util_get_string (c, &writer->statement);
+               else if (strcasecmp ("StoreRates", c->key) == 0)
+                       status = cf_util_get_boolean (c, &writer->store_rates);
+               else
+                       log_warn ("Ignoring unknown config key \"%s\".", c->key);
+       }
+
+       if (status != 0) {
+               sfree (writer->statement);
+               sfree (writer->name);
+               sfree (writer);
+               return status;
+       }
+
+       return 0;
+} /* c_psql_config_writer */
+
 static int c_psql_config_database (oconfig_item_t *ci)
 {
        c_psql_database_t *db;
@@ -587,6 +1178,8 @@ static int c_psql_config_database (oconfig_item_t *ci)
        struct timespec cb_interval = { 0, 0 };
        user_data_t ud;
 
+       static _Bool have_flush = 0;
+
        int i;
 
        if ((1 != ci->values_num)
@@ -612,6 +1205,8 @@ static int c_psql_config_database (oconfig_item_t *ci)
                        cf_util_get_string (c, &db->user);
                else if (0 == strcasecmp (c->key, "Password"))
                        cf_util_get_string (c, &db->password);
+               else if (0 == strcasecmp (c->key, "Instance"))
+                       cf_util_get_string (c, &db->instance);
                else if (0 == strcasecmp (c->key, "SSLMode"))
                        cf_util_get_string (c, &db->sslmode);
                else if (0 == strcasecmp (c->key, "KRBSrvName"))
@@ -621,14 +1216,19 @@ static int c_psql_config_database (oconfig_item_t *ci)
                else if (0 == strcasecmp (c->key, "Query"))
                        udb_query_pick_from_list (c, queries, queries_num,
                                        &db->queries, &db->queries_num);
+               else if (0 == strcasecmp (c->key, "Writer"))
+                       config_add_writer (c, writers, writers_num,
+                                       &db->writers, &db->writers_num);
                else if (0 == strcasecmp (c->key, "Interval"))
                        cf_util_get_cdtime (c, &db->interval);
+               else if (strcasecmp ("CommitInterval", c->key) == 0)
+                       cf_util_get_cdtime (c, &db->commit_interval);
                else
                        log_warn ("Ignoring unknown config key \"%s\".", c->key);
        }
 
        /* If no `Query' options were given, add the default queries.. */
-       if (db->queries_num == 0) {
+       if ((db->queries_num == 0) && (db->writers_num == 0)){
                for (i = 0; i < def_queries_num; i++)
                        udb_query_pick_from_list_by_name (def_queries[i],
                                        queries, queries_num,
@@ -665,13 +1265,36 @@ static int c_psql_config_database (oconfig_item_t *ci)
        ud.data = db;
        ud.free_func = c_psql_database_delete;
 
-       ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->database);
+       ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->instance);
 
-       CDTIME_T_TO_TIMESPEC (db->interval, &cb_interval);
+       if (db->queries_num > 0) {
+               CDTIME_T_TO_TIMESPEC (db->interval, &cb_interval);
 
-       plugin_register_complex_read ("postgresql", cb_name, c_psql_read,
-                       /* interval = */ (db->interval > 0) ? &cb_interval : NULL,
-                       &ud);
+               ++db->ref_cnt;
+               plugin_register_complex_read ("postgresql", cb_name, c_psql_read,
+                               /* interval = */ (db->interval > 0) ? &cb_interval : NULL,
+                               &ud);
+       }
+       if (db->writers_num > 0) {
+               ++db->ref_cnt;
+               plugin_register_write (cb_name, c_psql_write, &ud);
+
+               if (! have_flush) {
+                       /* flush all */
+                       plugin_register_flush ("postgresql",
+                                       c_psql_flush, /* user data = */ NULL);
+                       have_flush = 1;
+               }
+
+               /* flush this connection only */
+               ++db->ref_cnt;
+               plugin_register_flush (cb_name, c_psql_flush, &ud);
+       }
+       else if (db->commit_interval > 0) {
+               log_warn ("Database '%s': You do not have any writers assigned to "
+                               "this database connection. Setting 'CommitInterval' does "
+                               "not have any effect.", db->database);
+       }
        return 0;
 } /* c_psql_config_database */
 
@@ -703,6 +1326,8 @@ static int c_psql_config (oconfig_item_t *ci)
                if (0 == strcasecmp (c->key, "Query"))
                        udb_query_create (&queries, &queries_num, c,
                                        /* callback = */ config_query_callback);
+               else if (0 == strcasecmp (c->key, "Writer"))
+                       c_psql_config_writer (c);
                else if (0 == strcasecmp (c->key, "Database"))
                        c_psql_config_database (c);
                else
index 83a32c77b3fd7b1883c82de65cb1b60e1acbdc45..f905eb2a09a3f0bfcd2a8be817a05d57b9728e7b 100644 (file)
        MinVersion 80300
 </Query>
 
+<Query queries_by_table>
+       Statement "SELECT schemaname, relname, \
+                       n_tup_ins AS ins, \
+                       n_tup_upd AS upd, \
+                       n_tup_del AS del \
+               FROM pg_stat_user_tables;"
+
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "ins"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "ins"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "upd"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "upd"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "del"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "del"
+       </Result>
+
+       MaxVersion 80299
+</Query>
+
+<Query queries_by_table>
+       Statement "SELECT schemaname, relname, \
+                       n_tup_ins AS ins, \
+                       n_tup_upd AS upd, \
+                       n_tup_del AS del, \
+                       n_tup_hot_upd AS hot_upd \
+               FROM pg_stat_user_tables;"
+
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "ins"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "ins"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "upd"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "upd"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "del"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "del"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "hot_upd"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "hot_upd"
+       </Result>
+
+       MinVersion 80300
+</Query>
+
 <Query query_plans>
        Statement "SELECT sum(seq_scan) AS seq, \
                        sum(seq_tup_read) AS seq_tup_read, \
        MinVersion 80300
 </Query>
 
+<Query query_plans_by_table>
+       Statement "SELECT schemaname, relname, \
+                       seq_scan AS seq, \
+                       seq_tup_read AS seq_tup_read, \
+                       idx_scan AS idx, \
+                       idx_tup_fetch AS idx_tup_fetch \
+               FROM pg_stat_user_tables;"
+
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "seq"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "seq"
+       </Result>
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "seq_tup_read"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "seq_tup_read"
+       </Result>
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "idx"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "idx"
+       </Result>
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "idx_tup_fetch"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "idx_tup_fetch"
+       </Result>
+</Query>
+
+<Query table_states_by_table>
+       Statement "SELECT schemaname, relname, \
+                       n_live_tup AS live, n_dead_tup AS dead \
+               FROM pg_stat_user_tables;"
+
+       <Result>
+               Type "pg_n_tup_g"
+               InstancePrefix "live"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "live"
+       </Result>
+       <Result>
+               Type "pg_n_tup_g"
+               InstancePrefix "dead"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "dead"
+       </Result>
+
+       MinVersion 80300
+</Query>
+
 <Query disk_io>
        Statement "SELECT coalesce(sum(heap_blks_read), 0) AS heap_read, \
                        coalesce(sum(heap_blks_hit), 0) AS heap_hit, \
        </Result>
 </Query>
 
+<Query disk_io_by_table>
+       Statement "SELECT schemaname, relname, \
+                       coalesce(heap_blks_read, 0) AS heap_read, \
+                       coalesce(heap_blks_hit, 0) AS heap_hit, \
+                       coalesce(idx_blks_read, 0) AS idx_read, \
+                       coalesce(idx_blks_hit, 0) AS idx_hit, \
+                       coalesce(toast_blks_read, 0) AS toast_read, \
+                       coalesce(toast_blks_hit, 0) AS toast_hit, \
+                       coalesce(tidx_blks_read, 0) AS tidx_read, \
+                       coalesce(tidx_blks_hit, 0) AS tidx_hit \
+               FROM pg_statio_user_tables;"
+
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "heap_read"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "heap_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "heap_hit"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "heap_hit"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "idx_read"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "idx_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "idx_hit"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "idx_hit"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "toast_read"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "toast_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "toast_hit"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "toast_hit"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "tidx_read"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "tidx_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "tidx_hit"
+               InstancesFrom "schemaname" "relname"
+               ValuesFrom "tidx_hit"
+       </Result>
+</Query>
+
 <Query disk_usage>
        Statement "SELECT pg_database_size($1) AS size;"
 
index a1b23555bec080592a261f91e1d6e02ae769bd6a..a140a126a98380b5277ed352d1adee51c9e2bf49 100644 (file)
@@ -364,7 +364,7 @@ static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */
       break;
     }
 
-    cdt_timeout = interval_g * 3 / 4;
+    cdt_timeout = plugin_get_interval () * 3 / 4;
     if (cdt_timeout < TIME_T_TO_CDTIME_T (2))
       cdt_timeout = TIME_T_TO_CDTIME_T (2);
 
index 66237dc376be56414b29d62000d8a39c6fd70e1b..c77859d9e7d9f6953abe3fb87c3e16aebdd44357 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 2009       Andrés J. Díaz
  * Copyright (C) 2009       Manuel Sanmartin
  * Copyright (C) 2010       Clément Stenac
+ * Copyright (C) 2012       Cosmin Ioiart
  *
  * 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
@@ -30,6 +31,7 @@
  *   Andrés J. Díaz <ajdiaz at connectical.com>
  *   Manuel Sanmartin
  *   Clément Stenac <clement.stenac at diwi.org>
+ *   Cosmin Ioiart <cioiart at gmail.com>
  **/
 
 #include "collectd.h"
 #define MAXARGLN 1024
 /* #endif HAVE_PROCINFO_H */
 
+#elif KERNEL_SOLARIS
+# include <procfs.h>
+# include <dirent.h>
+/* #endif KERNEL_SOLARIS */
+
 #else
 # error "No applicable input method."
 #endif
 # include <regex.h>
 #endif
 
+#if HAVE_KSTAT_H
+# include <kstat.h>
+#endif
+
 #ifndef ARG_MAX
 #  define ARG_MAX 4096
 #endif
@@ -216,8 +227,8 @@ int getargs (struct procentry64 *processBuffer, int bufferLen, char *argsBuffer,
 #endif /* HAVE_PROCINFO_H */
 
 /* put name of process from config to list_head_g tree
  list_head_g is a list of 'procstat_t' structs with
  processes names we want to watch */
* list_head_g is a list of 'procstat_t' structs with
* processes names we want to watch */
 static void ps_list_register (const char *name, const char *regexp)
 {
        procstat_t *new;
@@ -712,7 +723,7 @@ static void ps_submit_proc_list (procstat_t *ps)
        }
 
        DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; "
-                        "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
+                       "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
                        "vmem_code = %lu; "
                        "vmem_minflt_counter = %"PRIi64"; vmem_majflt_counter = %"PRIi64"; "
                        "cpu_user_counter = %"PRIi64"; cpu_system_counter = %"PRIi64"; "
@@ -726,6 +737,26 @@ static void ps_submit_proc_list (procstat_t *ps)
                        ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
 } /* void ps_submit_proc_list */
 
+#if KERNEL_LINUX || KERNEL_SOLARIS
+static void ps_submit_fork_rate (derive_t value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].derive = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       sstrncpy(vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy(vl.plugin, "processes", sizeof (vl.plugin));
+       sstrncpy(vl.plugin_instance, "", sizeof (vl.plugin_instance));
+       sstrncpy(vl.type, "fork_rate", sizeof (vl.type));
+       sstrncpy(vl.type_instance, "", sizeof (vl.type_instance));
+
+       plugin_dispatch_values(&vl);
+}
+#endif /* KERNEL_LINUX || KERNEL_SOLARIS*/
+
 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
 #if KERNEL_LINUX
 static int ps_read_tasks (int pid)
@@ -780,7 +811,7 @@ static procstat_t *ps_read_vmem (int pid, procstat_t *ps)
                        continue;
 
                numfields = strsplit (buffer, fields,
-                                      STATIC_ARRAY_SIZE (fields));
+                               STATIC_ARRAY_SIZE (fields));
 
                if (numfields < 2)
                        continue;
@@ -1117,70 +1148,226 @@ static char *ps_get_cmdline (pid_t pid, char *name, char *buf, size_t buf_len)
        return buf;
 } /* char *ps_get_cmdline (...) */
 
-static unsigned long read_fork_rate ()
+static int read_fork_rate ()
 {
        FILE *proc_stat;
-       char buf[1024];
-       unsigned long result = 0;
-       int numfields;
-       char *fields[3];
+       char buffer[1024];
+       value_t value;
+       _Bool value_valid = 0;
 
-       proc_stat = fopen("/proc/stat", "r");
-       if (proc_stat == NULL) {
+       proc_stat = fopen ("/proc/stat", "r");
+       if (proc_stat == NULL)
+       {
                char errbuf[1024];
                ERROR ("processes plugin: fopen (/proc/stat) failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
-               return ULONG_MAX;
+               return (-1);
        }
 
-       while (fgets (buf, sizeof(buf), proc_stat) != NULL)
+       while (fgets (buffer, sizeof (buffer), proc_stat) != NULL)
        {
-               char *endptr;
+               int status;
+               char *fields[3];
+               int fields_num;
 
-               numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE (fields));
-               if (numfields != 2)
+               fields_num = strsplit (buffer, fields,
+                               STATIC_ARRAY_SIZE (fields));
+               if (fields_num != 2)
                        continue;
 
                if (strcmp ("processes", fields[0]) != 0)
                        continue;
 
-               errno = 0;
-               endptr = NULL;
-               result = strtoul(fields[1], &endptr, /* base = */ 10);
-               if ((endptr == fields[1]) || (errno != 0)) {
-                       ERROR ("processes plugin: Cannot parse fork rate: %s",
-                                       fields[1]);
-                       result = ULONG_MAX;
-                       break;
-               }
+               status = parse_value (fields[1], &value, DS_TYPE_DERIVE);
+               if (status == 0)
+                       value_valid = 1;
 
                break;
        }
-
        fclose(proc_stat);
 
-       return result;
+       if (!value_valid)
+               return (-1);
+
+       ps_submit_fork_rate (value.derive);
+       return (0);
 }
+#endif /*KERNEL_LINUX */
 
-static void ps_submit_fork_rate (unsigned long value)
+#if KERNEL_SOLARIS
+static const char *ps_get_cmdline (pid_t pid, /* {{{ */
+               char *buffer, size_t buffer_size)
 {
-       value_t values[1];
-       value_list_t vl = VALUE_LIST_INIT;
+       char path[PATH_MAX];
+       psinfo_t info;
+       int status;
 
-       values[0].derive = (derive_t) value;
+       snprintf(path, sizeof (path), "/proc/%i/psinfo", pid);
 
-       vl.values = values;
-       vl.values_len = 1;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
-       sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
-       sstrncpy (vl.type, "fork_rate", sizeof (vl.type));
-       sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
+       status = read_file_contents (path, (void *) &info, sizeof (info));
+       if (status != ((int) buffer_size))
+       {
+               ERROR ("processes plugin: Unexpected return value "
+                               "while reading \"%s\": "
+                               "Returned %i but expected %zu.",
+                               path, status, buffer_size);
+               return (NULL);
+       }
 
-       plugin_dispatch_values (&vl);
+       info.pr_psargs[sizeof (info.pr_psargs) - 1] = 0;
+       sstrncpy (buffer, info.pr_psargs, buffer_size);
+
+       return (buffer);
+} /* }}} int ps_get_cmdline */
+
+/*
+ * Reads process information on the Solaris OS. The information comes mainly from
+ * /proc/PID/status, /proc/PID/psinfo and /proc/PID/usage
+ * The values for input and ouput chars are calculated "by hand"
+ * Added a few "solaris" specific process states as well
+ */
+static int ps_read_process(int pid, procstat_t *ps, char *state)
+{
+       char filename[64];
+       char f_psinfo[64], f_usage[64];
+       char *buffer;
+
+       pstatus_t *myStatus;
+       psinfo_t *myInfo;
+       prusage_t *myUsage;
+
+       snprintf(filename, sizeof (filename), "/proc/%i/status", pid);
+       snprintf(f_psinfo, sizeof (f_psinfo), "/proc/%i/psinfo", pid);
+       snprintf(f_usage, sizeof (f_usage), "/proc/%i/usage", pid);
+
+
+       buffer = malloc(sizeof (pstatus_t));
+       memset(buffer, 0, sizeof (pstatus_t));
+       read_file_contents(filename, buffer, sizeof (pstatus_t));
+       myStatus = (pstatus_t *) buffer;
+
+       buffer = malloc(sizeof (psinfo_t));
+       memset(buffer, 0, sizeof(psinfo_t));
+       read_file_contents(f_psinfo, buffer, sizeof (psinfo_t));
+       myInfo = (psinfo_t *) buffer;
+
+       buffer = malloc(sizeof (prusage_t));
+       memset(buffer, 0, sizeof(prusage_t));
+       read_file_contents(f_usage, buffer, sizeof (prusage_t));
+       myUsage = (prusage_t *) buffer;
+
+       sstrncpy(ps->name, myInfo->pr_fname, sizeof (myInfo->pr_fname));
+       ps->num_lwp = myStatus->pr_nlwp;
+       if (myInfo->pr_wstat != 0) {
+               ps->num_proc = 0;
+               ps->num_lwp = 0;
+               *state = (char) 'Z';
+               return (0);
+       } else {
+               ps->num_proc = 1;
+               ps->num_lwp = myInfo->pr_nlwp;
+       }
+
+       /*
+        * Convert system time and user time from nanoseconds to microseconds
+        * for compatibility with the linux module
+        */
+       ps->cpu_system_counter = myStatus -> pr_stime.tv_nsec / 1000;
+       ps->cpu_user_counter = myStatus -> pr_utime.tv_nsec / 1000;
+
+       /*
+        * Convert rssize from KB to bytes to be consistent w/ the linux module
+        */
+       ps->vmem_rss = myInfo->pr_rssize * 1024;
+       ps->vmem_size = myInfo->pr_size * 1024;
+       ps->vmem_minflt_counter = myUsage->pr_minf;
+       ps->vmem_majflt_counter = myUsage->pr_majf;
+
+       /*
+        * TODO: Data and code segment calculations for Solaris
+        */
+
+       ps->vmem_data = -1;
+       ps->vmem_code = -1;
+       ps->stack_size = myStatus->pr_stksize;
+
+       /*
+        * Calculating input/ouput chars
+        * Formula used is total chars / total blocks => chars/block
+        * then convert input/output blocks to chars
+        */
+       ulong_t tot_chars = myUsage->pr_ioch;
+       ulong_t tot_blocks = myUsage->pr_inblk + myUsage->pr_oublk;
+       ulong_t chars_per_block = 1;
+       if (tot_blocks != 0)
+               chars_per_block = tot_chars / tot_blocks;
+       ps->io_rchar = myUsage->pr_inblk * chars_per_block;
+       ps->io_wchar = myUsage->pr_oublk * chars_per_block;
+       ps->io_syscr = myUsage->pr_sysc;
+       ps->io_syscw = myUsage->pr_sysc;
+
+
+       /*
+        * TODO: Find way of setting BLOCKED and PAGING status
+        */
+
+       *state = (char) 'R';
+       if (myStatus->pr_flags & PR_ASLEEP)
+               *state = (char) 'S';
+       else if (myStatus->pr_flags & PR_STOPPED)
+               *state = (char) 'T';
+       else if (myStatus->pr_flags & PR_DETACH)
+               *state = (char) 'E';
+       else if (myStatus->pr_flags & PR_DAEMON)
+               *state = (char) 'A';
+       else if (myStatus->pr_flags & PR_ISSYS)
+               *state = (char) 'Y';
+       else if (myStatus->pr_flags & PR_ORPHAN)
+               *state = (char) 'O';
+
+       sfree(myStatus);
+       sfree(myInfo);
+       sfree(myUsage);
+
+       return (0);
 }
 
-#endif /* KERNEL_LINUX */
+/*
+ * Reads the number of threads created since the last reboot. On Solaris these
+ * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
+ * The result is the sum for all the threads created on each cpu
+ */
+static int read_fork_rate()
+{
+       extern kstat_ctl_t *kc;
+       kstat_t *ksp_chain = NULL;
+       derive_t result = 0;
+
+       if (kc == NULL)
+               return (-1);
+
+       for (ksp_chain = kc->kc_chain;
+                       ksp_chain != NULL;
+                       ksp_chain = ksp_chain->ks_next)
+       {
+               if ((strcmp (ksp_chain->ks_module, "cpu") == 0)
+                               && (strcmp (ksp_chain->ks_name, "sys") == 0)
+                               && (strcmp (ksp_chain->ks_class, "misc") == 0))
+               {
+                       long long tmp;
+
+                       kstat_read (kc, ksp_chain, NULL);
+
+                       tmp = get_kstat_value(ksp_chain, "nthreads");
+                       if (tmp != -1LL)
+                               result += tmp;
+               }
+       }
+
+       ps_submit_fork_rate (result);
+       return (0);
+}
+#endif /* KERNEL_SOLARIS */
 
 #if HAVE_THREAD_INFO
 static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
@@ -1506,8 +1693,6 @@ static int ps_read (void)
        procstat_entry_t pse;
        char       state;
 
-       unsigned long fork_rate;
-
        procstat_t *ps_ptr;
 
        running = sleeping = zombies = stopped = paging = blocked = 0;
@@ -1589,9 +1774,7 @@ static int ps_read (void)
        for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
                ps_submit_proc_list (ps_ptr);
 
-       fork_rate = read_fork_rate();
-       if (fork_rate != ULONG_MAX)
-               ps_submit_fork_rate(fork_rate);
+       read_fork_rate();
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
@@ -1605,9 +1788,9 @@ static int ps_read (void)
 
        kvm_t *kd;
        char errbuf[1024];
-       struct kinfo_proc *procs;          /* array of processes */
+       struct kinfo_proc *procs;          /* array of processes */
        struct kinfo_proc *proc_ptr = NULL;
-       int count;                         /* returns number of processes */
+       int count;                         /* returns number of processes */
        int i;
 
        procstat_t *ps_ptr;
@@ -1778,7 +1961,7 @@ static int ps_read (void)
                                if (procentry[i].pi_pid == 0)
                                        cmdline = "swapper";
                                cargs = cmdline;
-                       }
+                       }
                        else
                        {
                                if (getargs(&procentry[i], sizeof(struct procentry64), arglist, MAXARGLN) >= 0)
@@ -1870,7 +2053,118 @@ static int ps_read (void)
 
        for (ps = list_head_g; ps != NULL; ps = ps->next)
                ps_submit_proc_list (ps);
-#endif /* HAVE_PROCINFO_H */
+/* #endif HAVE_PROCINFO_H */
+
+#elif KERNEL_SOLARIS
+       /*
+        * The Solaris section adds a few more process states and removes some
+        * process states compared to linux. Most notably there is no "PAGING"
+        * and "BLOCKED" state for a process.  The rest is similar to the linux
+        * code.
+        */
+       int running = 0;
+       int sleeping = 0;
+       int zombies = 0;
+       int stopped = 0;
+       int detached = 0;
+       int daemon = 0;
+       int system = 0;
+       int orphan = 0;
+
+       struct dirent *ent;
+       DIR *proc;
+
+       int status;
+       procstat_t *ps_ptr;
+       char state;
+
+       char cmdline[PRARGSZ];
+
+       ps_list_reset ();
+
+       proc = opendir ("/proc");
+       if (proc == NULL)
+               return (-1);
+
+       while ((ent = readdir(proc)) != NULL)
+       {
+               int pid;
+               struct procstat ps;
+               procstat_entry_t pse;
+
+               if (!isdigit ((int) ent->d_name[0]))
+                       continue;
+
+               if ((pid = atoi (ent->d_name)) < 1)
+                       continue;
+
+               status = ps_read_process (pid, &ps, &state);
+               if (status != 0)
+               {
+                       DEBUG("ps_read_process failed: %i", status);
+                       continue;
+               }
+
+               pse.id = pid;
+               pse.age = 0;
+
+               pse.num_proc   = ps.num_proc;
+               pse.num_lwp    = ps.num_lwp;
+               pse.vmem_size  = ps.vmem_size;
+               pse.vmem_rss   = ps.vmem_rss;
+               pse.vmem_data  = ps.vmem_data;
+               pse.vmem_code  = ps.vmem_code;
+               pse.stack_size = ps.stack_size;
+
+               pse.vmem_minflt = 0;
+               pse.vmem_minflt_counter = ps.vmem_minflt_counter;
+               pse.vmem_majflt = 0;
+               pse.vmem_majflt_counter = ps.vmem_majflt_counter;
+
+               pse.cpu_user = 0;
+               pse.cpu_user_counter = ps.cpu_user_counter;
+               pse.cpu_system = 0;
+               pse.cpu_system_counter = ps.cpu_system_counter;
+
+               pse.io_rchar = ps.io_rchar;
+               pse.io_wchar = ps.io_wchar;
+               pse.io_syscr = ps.io_syscr;
+               pse.io_syscw = ps.io_syscw;
+
+               switch (state)
+               {
+                       case 'R': running++;  break;
+                       case 'S': sleeping++; break;
+                       case 'E': detached++; break;
+                       case 'Z': zombies++;  break;
+                       case 'T': stopped++;  break;
+                       case 'A': daemon++;   break;
+                       case 'Y': system++;   break;
+                       case 'O': orphan++;   break;
+               }
+
+
+               ps_list_add (ps.name,
+                               ps_get_cmdline ((pid_t) pid,
+                                       cmdline, sizeof (cmdline)),
+                               &pse);
+       } /* while(readdir) */
+       closedir (proc);
+
+       ps_submit_state ("running",  running);
+       ps_submit_state ("sleeping", sleeping);
+       ps_submit_state ("zombies",  zombies);
+       ps_submit_state ("stopped",  stopped);
+       ps_submit_state ("detached", detached);
+       ps_submit_state ("daemon",   daemon);
+       ps_submit_state ("system",   system);
+       ps_submit_state ("orphan",   orphan);
+
+       for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
+               ps_submit_proc_list (ps_ptr);
+
+       read_fork_rate();
+#endif /* KERNEL_SOLARIS */
 
        return (0);
 } /* int ps_read */
index 4a828b44c6352c6a3be8c5757b2cc6ddebafb96f..10ac8f0d149f815fa9d00e54bdda3e0123246029 100644 (file)
@@ -282,17 +282,13 @@ void cpy_log_exception(const char *context) {
        Py_END_ALLOW_THREADS
        Py_XDECREF(tn);
        Py_XDECREF(m);
-       if (!cpy_format_exception) {
+       if (!cpy_format_exception || !traceback) {
                PyErr_Clear();
-               Py_XDECREF(type);
+               Py_DECREF(type);
                Py_XDECREF(value);
                Py_XDECREF(traceback);
                return;
        }
-       if (!traceback) {
-               PyErr_Clear();
-               return;
-       }
        list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
        if (list)
                l = PyObject_Length(list);
@@ -313,6 +309,9 @@ void cpy_log_exception(const char *context) {
        }
        Py_XDECREF(list);
        PyErr_Clear();
+       Py_DECREF(type);
+       Py_XDECREF(value);
+       Py_XDECREF(traceback);
 }
 
 static int cpy_read_callback(user_data_t *data) {
@@ -335,7 +334,7 @@ static int cpy_read_callback(user_data_t *data) {
 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
        int i;
        cpy_callback_t *c = data->data;
-       PyObject *ret, *list, *temp, *dict = NULL, *val;
+       PyObject *ret, *list, *temp, *dict = NULL;
        Values *v;
 
        CPY_LOCK_THREADS
@@ -375,7 +374,7 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                                CPY_RETURN_FROM_THREADS 0;
                        }
                }
-               dict = PyDict_New();
+               dict = PyDict_New();  /* New reference. */
                if (value_list->meta) {
                        int i, num;
                        char **table;
@@ -394,26 +393,26 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                                if (type == MD_TYPE_STRING) {
                                        if (meta_data_get_string(meta, table[i], &string))
                                                continue;
-                                       temp = cpy_string_to_unicode_or_bytes(string);
+                                       temp = cpy_string_to_unicode_or_bytes(string);  /* New reference. */
                                        free(string);
                                        PyDict_SetItemString(dict, table[i], temp);
                                        Py_XDECREF(temp);
                                } else if (type == MD_TYPE_SIGNED_INT) {
                                        if (meta_data_get_signed_int(meta, table[i], &si))
                                                continue;
-                                       temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0);
+                                       temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0);  /* New reference. */
                                        PyDict_SetItemString(dict, table[i], temp);
                                        Py_XDECREF(temp);
                                } else if (type == MD_TYPE_UNSIGNED_INT) {
                                        if (meta_data_get_unsigned_int(meta, table[i], &ui))
                                                continue;
-                                       temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0);
+                                       temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0);  /* New reference. */
                                        PyDict_SetItemString(dict, table[i], temp);
                                        Py_XDECREF(temp);
                                } else if (type == MD_TYPE_DOUBLE) {
                                        if (meta_data_get_double(meta, table[i], &d))
                                                continue;
-                                       temp = PyFloat_FromDouble(d);
+                                       temp = PyFloat_FromDouble(d);  /* New reference. */
                                        PyDict_SetItemString(dict, table[i], temp);
                                        Py_XDECREF(temp);
                                } else if (type == MD_TYPE_BOOLEAN) {
@@ -428,8 +427,7 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                        }
                        free(table);
                }
-               val = Values_New(); /* New reference. */
-               v = (Values *) val; 
+               v = (Values *) Values_New(); /* New reference. */
                sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
                sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
                sstrncpy(v->data.type_instance, value_list->type_instance, sizeof(v->data.type_instance));
@@ -440,9 +438,9 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li
                Py_CLEAR(v->values);
                v->values = list;
                Py_CLEAR(v->meta);
-               v->meta = dict;
+               v->meta = dict;  /* Steals a reference. */
                ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
-               Py_XDECREF(val);
+               Py_XDECREF(v);
                if (ret == NULL) {
                        cpy_log_exception("write callback");
                } else {
@@ -484,11 +482,11 @@ static void cpy_log_callback(int severity, const char *message, user_data_t *dat
        PyObject *ret, *text;
 
        CPY_LOCK_THREADS
-       text = cpy_string_to_unicode_or_bytes(message);
+       text = cpy_string_to_unicode_or_bytes(message);  /* New reference. */
        if (c->data == NULL)
-               ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. */
+               ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. Steals a reference from "text". */
        else
-               ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. */
+               ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. Steals a reference from "text". */
 
        if (ret == NULL) {
                /* FIXME */
@@ -525,12 +523,13 @@ static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
        char buf[512];
        cpy_callback_t *c;
-       const char *name = NULL;
+       char *name = NULL;
        PyObject *callback = NULL, *data = NULL, *mod = NULL;
        static char *kwlist[] = {"callback", "data", "name", NULL};
        
        if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
        if (PyCallable_Check(callback) == 0) {
+               PyMem_Free(name);
                PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
                return NULL;
        }
@@ -545,18 +544,21 @@ static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args
        c->next = *list_head;
        *list_head = c;
        Py_XDECREF(mod);
+       PyMem_Free(name);
        return cpy_string_to_unicode_or_bytes(buf);
 }
 
 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
        int timeout = -1;
-       const char *plugin = NULL, *identifier = NULL;
+       char *plugin = NULL, *identifier = NULL;
        static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
        
        if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin, &timeout, NULL, &identifier) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_flush(plugin, timeout, identifier);
        Py_END_ALLOW_THREADS
+       PyMem_Free(plugin);
+       PyMem_Free(identifier);
        Py_RETURN_NONE;
 }
 
@@ -575,16 +577,18 @@ static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObjec
        reg_function_t *register_function = (reg_function_t *) reg;
        cpy_callback_t *c = NULL;
        user_data_t *user_data = NULL;
-       const char *name = NULL;
+       char *name = NULL;
        PyObject *callback = NULL, *data = NULL;
        static char *kwlist[] = {"callback", "data", "name", NULL};
        
        if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
        if (PyCallable_Check(callback) == 0) {
+               PyMem_Free(name);
                PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
                return NULL;
        }
        cpy_build_name(buf, sizeof(buf), callback, name);
+       PyMem_Free(name);
        
        Py_INCREF(callback);
        Py_XINCREF(data);
@@ -605,17 +609,19 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwd
        cpy_callback_t *c = NULL;
        user_data_t *user_data = NULL;
        double interval = 0;
-       const char *name = NULL;
+       char *name = NULL;
        PyObject *callback = NULL, *data = NULL;
        struct timespec ts;
        static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
        
        if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
        if (PyCallable_Check(callback) == 0) {
+               PyMem_Free(name);
                PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
                return NULL;
        }
        cpy_build_name(buf, sizeof(buf), callback, name);
+       PyMem_Free(name);
        
        Py_INCREF(callback);
        Py_XINCREF(data);
@@ -659,48 +665,53 @@ static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject
 }
 
 static PyObject *cpy_error(PyObject *self, PyObject *args) {
-       const char *text;
+       char *text;
        if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_ERR, "%s", text);
        Py_END_ALLOW_THREADS
+       PyMem_Free(text);
        Py_RETURN_NONE;
 }
 
 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
-       const char *text;
+       char *text;
        if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_WARNING, "%s", text);
        Py_END_ALLOW_THREADS
+       PyMem_Free(text);
        Py_RETURN_NONE;
 }
 
 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
-       const char *text;
+       char *text;
        if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_NOTICE, "%s", text);
        Py_END_ALLOW_THREADS
+       PyMem_Free(text);
        Py_RETURN_NONE;
 }
 
 static PyObject *cpy_info(PyObject *self, PyObject *args) {
-       const char *text;
+       char *text;
        if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_INFO, "%s", text);
        Py_END_ALLOW_THREADS
+       PyMem_Free(text);
        Py_RETURN_NONE;
 }
 
 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
 #ifdef COLLECT_DEBUG
-       const char *text;
+       char *text;
        if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
        Py_BEGIN_ALLOW_THREADS
        plugin_log(LOG_DEBUG, "%s", text);
        Py_END_ALLOW_THREADS
+       PyMem_Free(text);
 #endif
        Py_RETURN_NONE;
 }
@@ -922,7 +933,7 @@ static int cpy_init(void) {
        pthread_sigmask(SIG_BLOCK, &sigset, NULL);
        state = PyEval_SaveThread();
        if (do_interactive) {
-               if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
+               if (plugin_thread_create(&thread, NULL, cpy_interactive, NULL)) {
                        ERROR("python: Error creating thread for interactive interpreter.");
                }
        }
index 9d8760a5171f2c44cef0d595196dcd0a65e7f4e7..307af175474a16aee1585ba5e4582a4c7c2a2309 100644 (file)
 
 #include "cpython.h"
 
+#define FreeAll() do {\
+       PyMem_Free(type);\
+       PyMem_Free(plugin_instance);\
+       PyMem_Free(type_instance);\
+       PyMem_Free(plugin);\
+       PyMem_Free(host);\
+} while(0)
+
 static PyObject *cpy_common_repr(PyObject *s) {
        PyObject *ret, *tmp;
        static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL, *l_plugin_instance = NULL;
@@ -143,7 +151,7 @@ static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kw
 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
        PluginData *self = (PluginData *) s;
        double time = 0;
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "plugin_instance", "type_instance",
                        "plugin", "host", "time", NULL};
        
@@ -151,18 +159,21 @@ static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
                        NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
                return -1;
        
-       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+       if (type && plugin_get_ds(type) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
                return -1;
        }
 
-       sstrncpy(self->host, host, sizeof(self->host));
-       sstrncpy(self->plugin, plugin, sizeof(self->plugin));
-       sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
-       sstrncpy(self->type, type, sizeof(self->type));
-       sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
-       
+       sstrncpy(self->host, host ? host : "", sizeof(self->host));
+       sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
+       sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->plugin_instance));
+       sstrncpy(self->type, type ? type : "", sizeof(self->type));
+       sstrncpy(self->type_instance, type_instance ? type_instance : "", sizeof(self->type_instance));
        self->time = time;
+
+       FreeAll();
+
        return 0;
 }
 
@@ -353,7 +364,7 @@ static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Values *self = (Values *) s;
        double interval = 0, time = 0;
        PyObject *values = NULL, *meta = NULL, *tmp;
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", "meta", NULL};
        
@@ -362,18 +373,21 @@ static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
                        NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return -1;
        
-       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+       if (type && plugin_get_ds(type) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
                return -1;
        }
 
-       sstrncpy(self->data.host, host, sizeof(self->data.host));
-       sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
-       sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
-       sstrncpy(self->data.type, type, sizeof(self->data.type));
-       sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
+       sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
+       sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
+       sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
+       sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
+       sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
        self->data.time = time;
 
+       FreeAll();
+
        if (values == NULL) {
                values = PyList_New(0);
                PyErr_Clear();
@@ -490,11 +504,7 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        value_list_t value_list = VALUE_LIST_INIT;
        PyObject *values = self->values, *meta = self->meta;
        double time = self->data.time, interval = self->interval;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
        
        static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", "meta", NULL};
@@ -503,13 +513,20 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
                        NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return NULL;
 
-       if (type[0] == 0) {
+       sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
+       sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
+       sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
+       sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
+       sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
+       FreeAll();
+       if (value_list.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
+               FreeAll();
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(value_list.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
                return NULL;
        }
        if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
@@ -522,7 +539,7 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        }
        size = (int) PySequence_Length(values);
        if (size != ds->ds_num) {
-               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
+               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", value_list.type, ds->ds_num, size);
                return NULL;
        }
        value = malloc(size * sizeof(*value));
@@ -559,7 +576,7 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
                        }
                } else {
                        free(value);
-                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
+                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
                        return NULL;
                }
                if (PyErr_Occurred() != NULL) {
@@ -572,11 +589,6 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
        value_list.values_len = size;
        value_list.time = DOUBLE_TO_CDTIME_T(time);
        value_list.interval = DOUBLE_TO_CDTIME_T(interval);
-       sstrncpy(value_list.host, host, sizeof(value_list.host));
-       sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
-       sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
-       sstrncpy(value_list.type, type, sizeof(value_list.type));
-       sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
        if (value_list.host[0] == 0)
                sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
        if (value_list.plugin[0] == 0)
@@ -601,27 +613,28 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        value_list_t value_list = VALUE_LIST_INIT;
        PyObject *values = self->values, *meta = self->meta;
        double time = self->data.time, interval = self->interval;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
-       const char *dest = NULL;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
        
        static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "interval", "meta", NULL};
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest,
                        NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
                        NULL, &plugin, NULL, &host, &time, &interval, &meta))
                return NULL;
 
-       if (type[0] == 0) {
+       sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
+       sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
+       sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
+       sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
+       sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
+       FreeAll();
+       if (value_list.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(value_list.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
                return NULL;
        }
        if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
@@ -630,7 +643,7 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        }
        size = (int) PySequence_Length(values);
        if (size != ds->ds_num) {
-               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
+               PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", value_list.type, ds->ds_num, size);
                return NULL;
        }
        value = malloc(size * sizeof(*value));
@@ -667,7 +680,7 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
                        }
                } else {
                        free(value);
-                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
+                       PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
                        return NULL;
                }
                if (PyErr_Occurred() != NULL) {
@@ -679,11 +692,6 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
        value_list.values_len = size;
        value_list.time = DOUBLE_TO_CDTIME_T(time);
        value_list.interval = DOUBLE_TO_CDTIME_T(interval);
-       sstrncpy(value_list.host, host, sizeof(value_list.host));
-       sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
-       sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
-       sstrncpy(value_list.type, type, sizeof(value_list.type));
-       sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
        value_list.meta = cpy_build_meta(meta);;
        if (value_list.host[0] == 0)
                sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
@@ -827,8 +835,8 @@ static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
        Notification *self = (Notification *) s;
        int severity = 0;
        double time = 0;
-       const char *message = "";
-       const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
+       char *message = NULL;
+       char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
        static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "severity", NULL};
        
@@ -837,20 +845,24 @@ static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
                        NULL, &plugin, NULL, &host, &time, &severity))
                return -1;
        
-       if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+       if (type && plugin_get_ds(type) == NULL) {
                PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               FreeAll();
+               PyMem_Free(message);
                return -1;
        }
 
-       sstrncpy(self->data.host, host, sizeof(self->data.host));
-       sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
-       sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
-       sstrncpy(self->data.type, type, sizeof(self->data.type));
-       sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
+       sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
+       sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
+       sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
+       sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
+       sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
+       sstrncpy(self->message, message ? message : "", sizeof(self->message));
        self->data.time = time;
-
-       sstrncpy(self->message, message, sizeof(self->message));
        self->severity = severity;
+
+       FreeAll();
+       PyMem_Free(message);
        return 0;
 }
 
@@ -860,12 +872,8 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj
        notification_t notification;
        double t = self->data.time;
        int severity = self->severity;
-       const char *host = self->data.host;
-       const char *plugin = self->data.plugin;
-       const char *plugin_instance = self->data.plugin_instance;
-       const char *type = self->data.type;
-       const char *type_instance = self->data.type_instance;
-       const char *message = self->message;
+       char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
+       char *message = NULL;
        
        static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
                        "plugin", "host", "time", "severity", NULL};
@@ -874,25 +882,28 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj
                        NULL, &plugin, NULL, &host, &t, &severity))
                return NULL;
 
-       if (type[0] == 0) {
+       notification.time = DOUBLE_TO_CDTIME_T(t);
+       notification.severity = severity;
+       sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
+       sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
+       sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin, sizeof(notification.plugin));
+       sstrncpy(notification.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(notification.plugin_instance));
+       sstrncpy(notification.type, type ? type : self->data.type, sizeof(notification.type));
+       sstrncpy(notification.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(notification.type_instance));
+       notification.meta = NULL;
+       FreeAll();
+       PyMem_Free(message);
+
+       if (notification.type[0] == 0) {
                PyErr_SetString(PyExc_RuntimeError, "type not set");
                return NULL;
        }
-       ds = plugin_get_ds(type);
+       ds = plugin_get_ds(notification.type);
        if (ds == NULL) {
-               PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
                return NULL;
        }
 
-       notification.time = DOUBLE_TO_CDTIME_T(t);
-       notification.severity = severity;
-       sstrncpy(notification.message, message, sizeof(notification.message));
-       sstrncpy(notification.host, host, sizeof(notification.host));
-       sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
-       sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
-       sstrncpy(notification.type, type, sizeof(notification.type));
-       sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
-       notification.meta = NULL;
        if (notification.time == 0)
                notification.time = cdtime();
        if (notification.host[0] == 0)
index 11c1c6a7f85c3e96c2380602094c81f09e4bb97f..45553b7f250ee708ec620f8058a6f18691711f34 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/rrdcached.c
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008-2012  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
@@ -16,7 +16,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
@@ -33,8 +33,8 @@
  */
 static char *datadir = NULL;
 static char *daemon_address = NULL;
-static int config_create_files = 1;
-static int config_collect_stats = 1;
+static _Bool config_create_files = 1;
+static _Bool config_collect_stats = 1;
 static rrdcreate_config_t rrdcreate_config =
 {
        /* stepsize = */ 0,
@@ -161,89 +161,130 @@ static int value_list_to_filename (char *buffer, int buffer_len,
   return (0);
 } /* int value_list_to_filename */
 
-static const char *config_get_string (oconfig_item_t *ci)
+static int rc_config_get_int_positive (oconfig_item_t const *ci, int *ret)
 {
-  if ((ci->children_num != 0) || (ci->values_num != 1)
-      || ((ci->values[0].type != OCONFIG_TYPE_STRING)
-        && (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)))
+  int status;
+  int tmp = 0;
+
+  status = cf_util_get_int (ci, &tmp);
+  if (status != 0)
+    return (status);
+  if (tmp < 0)
+    return (EINVAL);
+
+  *ret = tmp;
+  return (0);
+} /* int rc_config_get_int_positive */
+
+static int rc_config_get_xff (oconfig_item_t const *ci, double *ret)
+{
+  double value;
+
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
   {
-    ERROR ("rrdcached plugin: %s expects a single string argument.",
-        ci->key);
-    return (NULL);
+    ERROR ("rrdcached plugin: The \"%s\" needs exactly one numeric argument "
+        "in the range [0.0, 1.0)", ci->key);
+    return (EINVAL);
   }
 
-  if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN) {
-    if (ci->values[0].value.boolean)
-      return "true";
-    else
-      return "false";
+  value = ci->values[0].value.number;
+  if ((value >= 0.0) && (value < 1.0))
+  {
+    *ret = value;
+    return (0);
   }
-  return (ci->values[0].value.string);
-} /* const char *config_get_string */
+
+  ERROR ("rrdcached plugin: The \"%s\" needs exactly one numeric argument "
+      "in the range [0.0, 1.0)", ci->key);
+  return (EINVAL);
+} /* int rc_config_get_xff */
+
+static int rc_config_add_timespan (int timespan)
+{
+  int *tmp;
+
+  if (timespan <= 0)
+    return (EINVAL);
+
+  tmp = realloc (rrdcreate_config.timespans,
+      sizeof (*rrdcreate_config.timespans)
+      * (rrdcreate_config.timespans_num + 1));
+  if (tmp == NULL)
+    return (ENOMEM);
+  rrdcreate_config.timespans = tmp;
+
+  rrdcreate_config.timespans[rrdcreate_config.timespans_num] = timespan;
+  rrdcreate_config.timespans_num++;
+
+  return (0);
+} /* int rc_config_add_timespan */
 
 static int rc_config (oconfig_item_t *ci)
 {
   int i;
 
-  for (i = 0; i < ci->children_num; ++i) {
-    const char *key = ci->children[i].key;
-    const char *value = config_get_string (ci->children + i);
-
-    if (value == NULL) /* config_get_strings prints error message */
-      continue;
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t const *child = ci->children + i;
+    const char *key = child->key;
+    int status = 0;
 
     if (strcasecmp ("DataDir", key) == 0)
     {
-      if (datadir != NULL)
-        free (datadir);
-      datadir = strdup (value);
-      if (datadir != NULL)
+      status = cf_util_get_string (child, &datadir);
+      if (status == 0)
       {
         int len = strlen (datadir);
+
         while ((len > 0) && (datadir[len - 1] == '/'))
         {
           len--;
-          datadir[len] = '\0';
+          datadir[len] = 0;
         }
+
         if (len <= 0)
-        {
-          free (datadir);
-          datadir = NULL;
-        }
+          sfree (datadir);
       }
     }
     else if (strcasecmp ("DaemonAddress", key) == 0)
-    {
-      sfree (daemon_address);
-      daemon_address = strdup (value);
-      if (daemon_address == NULL)
-      {
-        ERROR ("rrdcached plugin: strdup failed.");
-        continue;
-      }
-    }
+      status = cf_util_get_string (child, &daemon_address);
     else if (strcasecmp ("CreateFiles", key) == 0)
+      status = cf_util_get_boolean (child, &config_create_files);
+    else if (strcasecmp ("CollectStatistics", key) == 0)
+      status = cf_util_get_boolean (child, &config_collect_stats);
+    else if (strcasecmp ("StepSize", key) == 0)
     {
-      if (IS_FALSE (value))
-        config_create_files = 0;
-      else
-        config_create_files = 1;
+      int tmp = -1;
+
+      status = rc_config_get_int_positive (child, &tmp);
+      if (status == 0)
+        rrdcreate_config.stepsize = (unsigned long) tmp;
     }
-    else if (strcasecmp ("CollectStatistics", key) == 0)
+    else if (strcasecmp ("HeartBeat", key) == 0)
+      status = rc_config_get_int_positive (child, &rrdcreate_config.heartbeat);
+    else if (strcasecmp ("RRARows", key) == 0)
+      status = rc_config_get_int_positive (child, &rrdcreate_config.rrarows);
+    else if (strcasecmp ("RRATimespan", key) == 0)
     {
-      if (IS_FALSE (value))
-        config_collect_stats = 0;
-      else
-        config_collect_stats = 1;
+      int tmp = -1;
+      status = rc_config_get_int_positive (child, &tmp);
+      if (status == 0)
+        status = rc_config_add_timespan (tmp);
     }
+    else if (strcasecmp ("XFF", key) == 0)
+      status = rc_config_get_xff (child, &rrdcreate_config.xff);
     else
     {
       WARNING ("rrdcached plugin: Ignoring invalid option %s.", key);
       continue;
     }
+
+    if (status != 0)
+      WARNING ("rrdcached plugin: Handling the \"%s\" option failed.", key);
   }
 
-  if (daemon_address != NULL) {
+  if (daemon_address != NULL)
+  {
     plugin_register_write ("rrdcached", rc_write, /* user_data = */ NULL);
     plugin_register_flush ("rrdcached", rc_flush, /* user_data = */ NULL);
   }
@@ -262,7 +303,7 @@ static int rc_read (void)
   if (daemon_address == NULL)
     return (-1);
 
-  if (config_collect_stats == 0)
+  if (!config_collect_stats)
     return (-1);
 
   vl.values = values;
@@ -354,7 +395,7 @@ static int rc_read (void)
 
 static int rc_init (void)
 {
-  if (config_collect_stats != 0)
+  if (config_collect_stats)
     plugin_register_read ("rrdcached", rc_read);
 
   return (0);
@@ -396,7 +437,7 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl,
   values_array[0] = values;
   values_array[1] = NULL;
 
-  if (config_create_files != 0)
+  if (config_create_files)
   {
     struct stat statbuf;
 
index e5f964e5bdc4fe01084267ee0b821e298fd42650..b1d13ee3c19ee279c836741739b521359872c63b 100644 (file)
@@ -1164,17 +1164,6 @@ static int rrd_init (void)
        if (rrdcreate_config.heartbeat <= 0)
                rrdcreate_config.heartbeat = 2 * rrdcreate_config.stepsize;
 
-       if ((rrdcreate_config.heartbeat > 0)
-                       && (rrdcreate_config.heartbeat < CDTIME_T_TO_TIME_T (interval_g)))
-               WARNING ("rrdtool plugin: Your `heartbeat' is "
-                               "smaller than your `interval'. This will "
-                               "likely cause problems.");
-       else if ((rrdcreate_config.stepsize > 0)
-                       && (rrdcreate_config.stepsize < CDTIME_T_TO_TIME_T (interval_g)))
-               WARNING ("rrdtool plugin: Your `stepsize' is "
-                               "smaller than your `interval'. This will "
-                               "create needlessly big RRD-files.");
-
        /* Set the cache up */
        pthread_mutex_lock (&cache_lock);
 
@@ -1195,7 +1184,7 @@ static int rrd_init (void)
 
        pthread_mutex_unlock (&cache_lock);
 
-       status = pthread_create (&queue_thread, /* attr = */ NULL,
+       status = plugin_thread_create (&queue_thread, /* attr = */ NULL,
                        rrd_queue_thread, /* args = */ NULL);
        if (status != 0)
        {
index b68baf378f088d38ea69eecdde50a83a0bdd1854..c4d043b9e70283e13136b20fd6e71e0805155aa7 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/snmp.c
- * Copyright (C) 2007  Florian octo Forster
+ * Copyright (C) 2007-2012  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
@@ -16,7 +16,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
@@ -79,7 +79,7 @@ typedef struct host_definition_s host_definition_t;
  * gaps in tables. */
 struct csnmp_list_instances_s
 {
-  oid subid;
+  oid_t suffix;
   char instance[DATA_MAX_NAME_LEN];
   struct csnmp_list_instances_s *next;
 };
@@ -87,7 +87,7 @@ typedef struct csnmp_list_instances_s csnmp_list_instances_t;
 
 struct csnmp_table_values_s
 {
-  oid subid;
+  oid_t suffix;
   value_t value;
   struct csnmp_table_values_s *next;
 };
@@ -106,6 +106,54 @@ static int csnmp_read_host (user_data_t *ud);
 /*
  * Private functions
  */
+static void csnmp_oid_init (oid_t *dst, oid const *src, size_t n)
+{
+  assert (n <= STATIC_ARRAY_LEN (dst->oid));
+  memcpy (dst->oid, src, sizeof (*src) * n);
+  dst->oid_len = n;
+}
+
+static int csnmp_oid_compare (oid_t const *left, oid_t const *right)
+{
+  return (snmp_oid_compare (left->oid, left->oid_len,
+        right->oid, right->oid_len));
+}
+
+static int csnmp_oid_suffix (oid_t *dst, oid_t const *src,
+    oid_t const *root)
+{
+  /* Make sure "src" is in "root"s subtree. */
+  if (src->oid_len <= root->oid_len)
+    return (EINVAL);
+  if (snmp_oid_ncompare (root->oid, root->oid_len,
+        src->oid, src->oid_len,
+        /* n = */ root->oid_len) != 0)
+    return (EINVAL);
+
+  memset (dst, 0, sizeof (*dst));
+  dst->oid_len = src->oid_len - root->oid_len;
+  memcpy (dst->oid, &src->oid[root->oid_len],
+      dst->oid_len * sizeof (dst->oid[0]));
+  return (0);
+}
+
+static int csnmp_oid_to_string (char *buffer, size_t buffer_size,
+    oid_t const *o)
+{
+  char oid_str[MAX_OID_LEN][16];
+  char *oid_str_ptr[MAX_OID_LEN];
+  size_t i;
+
+  for (i = 0; i < o->oid_len; i++)
+  {
+    ssnprintf (oid_str[i], sizeof (oid_str[i]), "%lu", (unsigned long) o->oid[i]);
+    oid_str_ptr[i] = oid_str[i];
+  }
+
+  return (strjoin (buffer, buffer_size,
+        oid_str_ptr, o->oid_len, /* separator = */ "."));
+}
+
 static void csnmp_host_close_session (host_definition_t *host) /* {{{ */
 {
   if (host->sess_handle == NULL)
@@ -127,7 +175,7 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */
   if (hd->name != NULL)
   {
     DEBUG ("snmp plugin: Destroying host definition for host `%s'.",
-       hd->name);
+        hd->name);
   }
 
   csnmp_host_close_session (hd);
@@ -212,10 +260,10 @@ static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t
     dd->instance.oid.oid_len = MAX_OID_LEN;
 
     if (!read_objid (ci->values[0].value.string,
-         dd->instance.oid.oid, &dd->instance.oid.oid_len))
+          dd->instance.oid.oid, &dd->instance.oid.oid_len))
     {
       ERROR ("snmp plugin: read_objid (%s) failed.",
-         ci->values[0].value.string);
+          ci->values[0].value.string);
       return (-1);
     }
   }
@@ -223,7 +271,7 @@ static int csnmp_config_add_data_instance (data_definition_t *dd, oconfig_item_t
   {
     /* Instance is a simple string */
     sstrncpy (dd->instance.string, ci->values[0].value.string,
-       sizeof (dd->instance.string));
+        sizeof (dd->instance.string));
   }
 
   return (0);
@@ -241,7 +289,7 @@ static int csnmp_config_add_data_instance_prefix (data_definition_t *dd,
   if (!dd->is_table)
   {
     WARNING ("snmp plugin: data %s: InstancePrefix is ignored when `Table' "
-       "is set to `false'.", dd->name);
+        "is set to `false'.", dd->name);
     return (-1);
   }
 
@@ -282,10 +330,10 @@ static int csnmp_config_add_data_values (data_definition_t *dd, oconfig_item_t *
     dd->values[i].oid_len = MAX_OID_LEN;
 
     if (NULL == snmp_parse_oid (ci->values[i].value.string,
-         dd->values[i].oid, &dd->values[i].oid_len))
+          dd->values[i].oid, &dd->values[i].oid_len))
     {
       ERROR ("snmp plugin: snmp_parse_oid (%s) failed.",
-         ci->values[i].value.string);
+          ci->values[i].value.string);
       free (dd->values);
       dd->values = NULL;
       dd->values_len = 0;
@@ -523,17 +571,17 @@ static int csnmp_config_add_host_collect (host_definition_t *host,
   {
     for (data = data_head; data != NULL; data = data->next)
       if (strcasecmp (ci->values[i].value.string, data->name) == 0)
-       break;
+        break;
 
     if (data == NULL)
     {
       WARNING ("snmp plugin: No such data configured: `%s'",
-         ci->values[i].value.string);
+          ci->values[i].value.string);
       continue;
     }
 
     DEBUG ("snmp plugin: Collect: host = %s, data[%i] = %s;",
-       host->name, host->data_list_len, data->name);
+        host->name, host->data_list_len, data->name);
 
     host->data_list[host->data_list_len] = data;
     host->data_list_len++;
@@ -696,7 +744,7 @@ static void csnmp_host_open_session (host_definition_t *host)
     snmp_error (&sess, NULL, NULL, &errstr);
 
     ERROR ("snmp plugin: host %s: snmp_sess_open failed: %s",
-       host->name, (errstr == NULL) ? "Unknown problem" : errstr);
+        host->name, (errstr == NULL) ? "Unknown problem" : errstr);
     sfree (errstr);
   }
 } /* void csnmp_host_open_session */
@@ -748,12 +796,12 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
 
     memset (oid_buffer, 0, sizeof (oid_buffer));
     snprint_objid (oid_buffer, sizeof (oid_buffer) - 1,
-       vl->name, vl->name_length);
+        vl->name, vl->name_length);
 
 #ifdef ASN_NULL
     if (vl->type == ASN_NULL)
       INFO ("snmp plugin: OID \"%s\" is undefined (type ASN_NULL)",
-         oid_buffer);
+          oid_buffer);
     else
 #endif
       WARNING ("snmp plugin: I don't know the ASN type #%i "
@@ -776,7 +824,7 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
 
       string_length = sizeof (string) - 1;
       if (vl->val_len < string_length)
-       string_length = vl->val_len;
+        string_length = vl->val_len;
 
       /* The strings we get from the Net-SNMP library may not be null
        * terminated. That is why we're using `memcpy' here and not `strcpy'.
@@ -788,8 +836,8 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
       status = parse_value (string, &ret, type);
       if (status != 0)
       {
-       ERROR ("snmp plugin: csnmp_value_list_to_value: Parsing string as %s failed: %s",
-           DS_TYPE_TO_STRING (type), string);
+        ERROR ("snmp plugin: csnmp_value_list_to_value: Parsing string as %s failed: %s",
+            DS_TYPE_TO_STRING (type), string);
       }
     }
 
@@ -797,20 +845,20 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
     {
       switch (type)
       {
-       case DS_TYPE_COUNTER:
-       case DS_TYPE_DERIVE:
-       case DS_TYPE_ABSOLUTE:
-         memset (&ret, 0, sizeof (ret));
-         break;
-
-       case DS_TYPE_GAUGE:
-         ret.gauge = NAN;
-         break;
-
-       default:
-         ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown "
-             "data source type: %i.", type);
-         ret.gauge = NAN;
+        case DS_TYPE_COUNTER:
+        case DS_TYPE_DERIVE:
+        case DS_TYPE_ABSOLUTE:
+          memset (&ret, 0, sizeof (ret));
+          break;
+
+        case DS_TYPE_GAUGE:
+          ret.gauge = NAN;
+          break;
+
+        default:
+          ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown "
+              "data source type: %i.", type);
+          ret.gauge = NAN;
       }
     }
   } /* if (vl->type == ASN_OCTET_STR) */
@@ -841,7 +889,7 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
   else
   {
     ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown data source "
-       "type: %i.", type);
+        "type: %i.", type);
     ret.gauge = NAN;
   }
 
@@ -873,10 +921,10 @@ static int csnmp_check_res_left_subtree (const host_definition_t *host,
     num_checked++;
 
     if ((vb->type == SNMP_ENDOFMIBVIEW)
-       || (snmp_oid_ncompare (data->values[i].oid,
-           data->values[i].oid_len,
-           vb->name, vb->name_length,
-           data->values[i].oid_len) != 0))
+        || (snmp_oid_ncompare (data->values[i].oid,
+            data->values[i].oid_len,
+            vb->name, vb->name_length,
+            data->values[i].oid_len) != 0))
       num_left_subtree++;
   }
 
@@ -884,7 +932,7 @@ static int csnmp_check_res_left_subtree (const host_definition_t *host,
   if (i < data->values_len)
   {
     ERROR ("snmp plugin: host %s: Expected %i variables, but got only %i",
-       host->name, data->values_len, i);
+        host->name, data->values_len, i);
     return (-1);
   }
 
@@ -893,15 +941,15 @@ static int csnmp_check_res_left_subtree (const host_definition_t *host,
     if (vb == NULL)
     {
       ERROR ("snmp plugin: host %s: Expected one more variable for "
-         "the instance..", host->name);
+          "the instance..", host->name);
       return (-1);
     }
 
     num_checked++;
     if (snmp_oid_ncompare (data->instance.oid.oid,
-         data->instance.oid.oid_len,
-         vb->name, vb->name_length,
-         data->instance.oid.oid_len) != 0)
+          data->instance.oid.oid_len,
+          vb->name, vb->name_length,
+          data->instance.oid.oid_len) != 0)
       num_left_subtree++;
   }
 
@@ -928,7 +976,7 @@ static int csnmp_strvbcopy_hexstring (char *dst, /* {{{ */
     int status;
 
     status = snprintf (buffer_ptr, buffer_free,
-       (i == 0) ? "%02x" : ":%02x", (unsigned int) vb->val.bitstring[i]);
+        (i == 0) ? "%02x" : ":%02x", (unsigned int) vb->val.bitstring[i]);
 
     if (status >= buffer_free)
     {
@@ -986,6 +1034,8 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
 {
   csnmp_list_instances_t *il;
   struct variable_list *vb;
+  oid_t vb_name;
+  int status;
 
   /* Set vb on the last variable */
   for (vb = res->variables;
@@ -995,15 +1045,24 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
   if (vb == NULL)
     return (-1);
 
-  il = (csnmp_list_instances_t *) malloc (sizeof (csnmp_list_instances_t));
+  csnmp_oid_init (&vb_name, vb->name, vb->name_length);
+
+  il = malloc (sizeof (*il));
   if (il == NULL)
   {
     ERROR ("snmp plugin: malloc failed.");
     return (-1);
   }
-  il->subid = vb->name[vb->name_length - 1];
+  memset (il, 0, sizeof (*il));
   il->next = NULL;
 
+  status = csnmp_oid_suffix (&il->suffix, &vb_name, &dd->instance.oid);
+  if (status != 0)
+  {
+    sfree (il);
+    return (status);
+  }
+
   /* Get instance name */
   if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR))
   {
@@ -1014,9 +1073,9 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     for (ptr = il->instance; *ptr != '\0'; ptr++)
     {
       if ((*ptr > 0) && (*ptr < 32))
-       *ptr = ' ';
+        *ptr = ' ';
       else if (*ptr == '/')
-       *ptr = '_';
+        *ptr = '_';
     }
     DEBUG ("snmp plugin: il->instance = `%s';", il->instance);
   }
@@ -1025,7 +1084,7 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER,
         /* scale = */ 1.0, /* shift = */ 0.0, hd->name, dd->name);
     ssnprintf (il->instance, sizeof (il->instance),
-       "%llu", val.counter);
+        "%llu", val.counter);
   }
 
   /* TODO: Debugging output */
@@ -1050,8 +1109,8 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
   csnmp_table_values_t **value_table_ptr;
 
   int i;
-  oid subid;
-  int have_more;
+  _Bool have_more;
+  oid_t current_suffix;
 
   ds = plugin_get_ds (data->type);
   if (!ds)
@@ -1063,15 +1122,14 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
 
   instance_list_ptr = instance_list;
 
-  value_table_ptr = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *)
-      * data->values_len);
+  value_table_ptr = malloc (sizeof (*value_table_ptr) * data->values_len);
   if (value_table_ptr == NULL)
     return (-1);
   for (i = 0; i < data->values_len; i++)
     value_table_ptr[i] = value_table[i];
 
   vl.values_len = ds->ds_num;
-  vl.values = (value_t *) malloc (sizeof (value_t) * vl.values_len);
+  vl.values = malloc (sizeof (*vl.values) * vl.values_len);
   if (vl.values == NULL)
   {
     ERROR ("snmp plugin: malloc failed.");
@@ -1084,50 +1142,70 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
 
   vl.interval = host->interval;
 
-  subid = 0;
   have_more = 1;
-
-  while (have_more != 0)
+  memset (&current_suffix, 0, sizeof (current_suffix));
+  while (have_more)
   {
+    _Bool suffix_skipped = 0;
+
+    /* Determine next suffix to handle. */
     if (instance_list != NULL)
     {
-      while ((instance_list_ptr != NULL)
-         && (instance_list_ptr->subid < subid))
-       instance_list_ptr = instance_list_ptr->next;
-
       if (instance_list_ptr == NULL)
       {
-       have_more = 0;
-       continue;
+        have_more = 0;
+        continue;
       }
-      else if (instance_list_ptr->subid > subid)
+
+      memcpy (&current_suffix, &instance_list_ptr->suffix, sizeof (current_suffix));
+    }
+    else /* no instance configured */
+    {
+      csnmp_table_values_t *ptr = value_table_ptr[0];
+      if (ptr == NULL)
       {
-       subid = instance_list_ptr->subid;
-       continue;
+        have_more = 0;
+        continue;
       }
-    } /* if (instance_list != NULL) */
 
+      memcpy (&current_suffix, &ptr->suffix, sizeof (current_suffix));
+    }
+
+    /* Update all the value_table_ptr to point at the entry with the same
+     * trailing partial OID */
     for (i = 0; i < data->values_len; i++)
     {
       while ((value_table_ptr[i] != NULL)
-         && (value_table_ptr[i]->subid < subid))
-       value_table_ptr[i] = value_table_ptr[i]->next;
+          && (csnmp_oid_compare (&value_table_ptr[i]->suffix, &current_suffix) < 0))
+        value_table_ptr[i] = value_table_ptr[i]->next;
 
       if (value_table_ptr[i] == NULL)
       {
-       have_more = 0;
-       break;
+        have_more = 0;
+        break;
       }
-      else if (value_table_ptr[i]->subid > subid)
+      else if (csnmp_oid_compare (&value_table_ptr[i]->suffix, &current_suffix) > 0)
       {
-       subid = value_table_ptr[i]->subid;
-       break;
+        /* This suffix is missing in the subtree. Indicate this with the
+         * "suffix_skipped" flag and try the next instance / suffix. */
+        suffix_skipped = 1;
+        break;
       }
     } /* for (i = 0; i < columns; i++) */
-    /* The subid has been increased - start scanning from the beginning
-     * again.. */
-    if (i < data->values_len)
+
+    if (!have_more)
+      break;
+
+    /* Matching the values failed. Start from the beginning again. */
+    if (suffix_skipped)
+    {
+      if (instance_list != NULL)
+        instance_list_ptr = instance_list_ptr->next;
+      else
+        value_table_ptr[0] = value_table_ptr[0]->next;
+
       continue;
+    }
 
     /* if we reach this line, all value_table_ptr[i] are non-NULL and are set
      * to the same subid. instance_list_ptr is either NULL or points to the
@@ -1136,10 +1214,12 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
     for (i = 1; i < data->values_len; i++)
     {
       assert (value_table_ptr[i] != NULL);
-      assert (value_table_ptr[i-1]->subid == value_table_ptr[i]->subid);
+      assert (csnmp_oid_compare (&value_table_ptr[i-1]->suffix,
+            &value_table_ptr[i]->suffix) == 0);
     }
     assert ((instance_list_ptr == NULL)
-       || (instance_list_ptr->subid == value_table_ptr[0]->subid));
+        || (csnmp_oid_compare (&instance_list_ptr->suffix,
+            &value_table_ptr[0]->suffix) == 0));
 #endif
 
     sstrncpy (vl.type, data->type, sizeof (vl.type));
@@ -1148,15 +1228,15 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
       char temp[DATA_MAX_NAME_LEN];
 
       if (instance_list_ptr == NULL)
-       ssnprintf (temp, sizeof (temp), "%"PRIu32, (uint32_t) subid);
+        csnmp_oid_to_string (temp, sizeof (temp), &current_suffix);
       else
-       sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
+        sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
 
       if (data->instance_prefix == NULL)
-       sstrncpy (vl.type_instance, temp, sizeof (vl.type_instance));
+        sstrncpy (vl.type_instance, temp, sizeof (vl.type_instance));
       else
-       ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
-           data->instance_prefix, temp);
+        ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
+            data->instance_prefix, temp);
     }
 
     for (i = 0; i < data->values_len; i++)
@@ -1165,8 +1245,11 @@ static int csnmp_dispatch_table (host_definition_t *host, data_definition_t *dat
     /* If we get here `vl.type_instance' and all `vl.values' have been set */
     plugin_dispatch_values (&vl);
 
-    subid++;
-  } /* while (have_more != 0) */
+    if (instance_list != NULL)
+      instance_list_ptr = instance_list_ptr->next;
+    else
+      value_table_ptr[0] = value_table_ptr[0]->next;
+  } /* while (have_more) */
 
   sfree (vl.values);
   sfree (value_table_ptr);
@@ -1187,13 +1270,13 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   int status;
   int i;
 
-  /* `value_table' and `value_table_ptr' implement a linked list for each
-   * value. `instance_list' and `instance_list_ptr' implement a linked list of
+  /* `value_list_head' and `value_list_tail' implement a linked list for each
+   * value. `instance_list_head' and `instance_list_tail' implement a linked list of
    * instance names. This is used to jump gaps in the table. */
-  csnmp_list_instances_t *instance_list;
-  csnmp_list_instances_t *instance_list_ptr;
-  csnmp_table_values_t **value_table;
-  csnmp_table_values_t **value_table_ptr;
+  csnmp_list_instances_t *instance_list_head;
+  csnmp_list_instances_t *instance_list_tail;
+  csnmp_table_values_t **value_list_head;
+  csnmp_table_values_t **value_list_tail;
 
   DEBUG ("snmp plugin: csnmp_read_table (host = %s, data = %s)",
       host->name, data->name);
@@ -1214,7 +1297,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   if (ds->ds_num != data->values_len)
   {
     ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i",
-       data->type, ds->ds_num, data->values_len);
+        data->type, ds->ds_num, data->values_len);
     return (-1);
   }
 
@@ -1232,20 +1315,22 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   else
     oid_list_len--;
 
-  /* Allocate the `value_table' */
-  value_table = (csnmp_table_values_t **) malloc (sizeof (csnmp_table_values_t *)
-      * 2 * data->values_len);
-  if (value_table == NULL)
+  /* We're going to construct n linked lists, one for each "value".
+   * value_list_head will contain pointers to the heads of these linked lists,
+   * value_list_tail will contain pointers to the tail of the lists. */
+  value_list_head = calloc (data->values_len, sizeof (*value_list_head));
+  value_list_tail = calloc (data->values_len, sizeof (*value_list_tail));
+  if ((value_list_head == NULL) || (value_list_tail == NULL))
   {
-    ERROR ("snmp plugin: csnmp_read_table: malloc failed.");
+    ERROR ("snmp plugin: csnmp_read_table: calloc failed.");
     sfree (oid_list);
+    sfree (value_list_head);
+    sfree (value_list_tail);
     return (-1);
   }
-  memset (value_table, '\0', sizeof (csnmp_table_values_t *) * 2 * data->values_len);
-  value_table_ptr = value_table + data->values_len;
-  
-  instance_list = NULL;
-  instance_list_ptr = NULL;
+
+  instance_list_head = NULL;
+  instance_list_tail = NULL;
 
   status = 0;
   while (status == 0)
@@ -1271,11 +1356,11 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
 
       c_complain (LOG_ERR, &host->complaint,
-         "snmp plugin: host %s: snmp_sess_synch_response failed: %s",
-         host->name, (errstr == NULL) ? "Unknown problem" : errstr);
+          "snmp plugin: host %s: snmp_sess_synch_response failed: %s",
+          host->name, (errstr == NULL) ? "Unknown problem" : errstr);
 
       if (res != NULL)
-       snmp_free_pdu (res);
+        snmp_free_pdu (res);
       res = NULL;
 
       sfree (errstr);
@@ -1287,8 +1372,8 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
     status = 0;
     assert (res != NULL);
     c_release (LOG_INFO, &host->complaint,
-       "snmp plugin: host %s: snmp_sess_synch_response successful.",
-       host->name);
+        "snmp plugin: host %s: snmp_sess_synch_response successful.",
+        host->name);
 
     vb = res->variables;
     if (vb == NULL)
@@ -1305,76 +1390,90 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       break;
     }
 
-    /* if an instance-OID is configured.. */
+    /* Copy the OID of the value used as instance to oid_list, if an instance
+     * is configured. */
     if (data->instance.oid.oid_len > 0)
     {
       /* Allocate a new `csnmp_list_instances_t', insert the instance name and
        * add it to the list */
-      if (csnmp_instance_list_add (&instance_list, &instance_list_ptr,
+      if (csnmp_instance_list_add (&instance_list_head, &instance_list_tail,
            res, host, data) != 0)
       {
-       ERROR ("snmp plugin: csnmp_instance_list_add failed.");
-       status = -1;
-       break;
+        ERROR ("snmp plugin: csnmp_instance_list_add failed.");
+        status = -1;
+        break;
       }
 
-      /* Set vb on the last variable */
+      /* The instance OID is added to the list of OIDs to GET from the
+       * snmp agent last, so set vb on the last variable returned and copy
+       * that OID. */
       for (vb = res->variables;
-         (vb != NULL) && (vb->next_variable != NULL);
-         vb = vb->next_variable)
-       /* do nothing */;
+          (vb != NULL) && (vb->next_variable != NULL);
+          vb = vb->next_variable)
+        /* do nothing */;
       assert (vb != NULL);
 
-      /* Copy OID to oid_list[data->values_len] */
+      /* Copy the OID of the instance value to oid_list[data->values_len].
+       * "oid_list" is used for the next GETNEXT request. */
       memcpy (oid_list[data->values_len].oid, vb->name,
-         sizeof (oid) * vb->name_length);
+          sizeof (oid) * vb->name_length);
       oid_list[data->values_len].oid_len = vb->name_length;
     }
 
+    /* Iterate over all the (non-instance) values returned by the agent. The
+     * (i < value_len) check will make sure we're not handling the instance OID
+     * twice. */
     for (vb = res->variables, i = 0;
-       (vb != NULL) && (i < data->values_len);
-       vb = vb->next_variable, i++)
+        (vb != NULL) && (i < data->values_len);
+        vb = vb->next_variable, i++)
     {
       csnmp_table_values_t *vt;
+      oid_t vb_name;
+      oid_t suffix;
 
-      /* Check if we left the subtree */
-      if (snmp_oid_ncompare (data->values[i].oid,
-           data->values[i].oid_len,
-           vb->name, vb->name_length,
-           data->values[i].oid_len) != 0)
+      csnmp_oid_init (&vb_name, vb->name, vb->name_length);
+
+      /* Calculate the current suffix. This is later used to check that the
+       * suffix is increasing. This also checks if we left the subtree */
+      status = csnmp_oid_suffix (&suffix, &vb_name, data->values + i);
+      if (status != 0)
       {
-       DEBUG ("snmp plugin: host = %s; data = %s; Value %i left its subtree.",
-           host->name, data->name, i);
-       continue;
+        DEBUG ("snmp plugin: host = %s; data = %s; Value %i failed. "
+            "It probably left its subtree.",
+            host->name, data->name, i);
+        continue;
       }
 
-      if ((value_table_ptr[i] != NULL)
-         && (vb->name[vb->name_length - 1] <= value_table_ptr[i]->subid))
+      /* Make sure the OIDs returned by the agent are increasing. Otherwise our
+       * table matching algorithm will get confused. */
+      if ((value_list_tail[i] != NULL)
+          && (csnmp_oid_compare (&suffix, &value_list_tail[i]->suffix) <= 0))
       {
-       DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
-           "SUBID is not increasing.",
-           host->name, data->name, i);
-       continue;
+        DEBUG ("snmp plugin: host = %s; data = %s; i = %i; "
+            "Suffix is not increasing.",
+            host->name, data->name, i);
+        continue;
       }
 
-      vt = (csnmp_table_values_t *) malloc (sizeof (csnmp_table_values_t));
+      vt = malloc (sizeof (*vt));
       if (vt == NULL)
       {
-       ERROR ("snmp plugin: malloc failed.");
-       status = -1;
-       break;
+        ERROR ("snmp plugin: malloc failed.");
+        status = -1;
+        break;
       }
+      memset (vt, 0, sizeof (*vt));
 
-      vt->subid = vb->name[vb->name_length - 1];
       vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type,
           data->scale, data->shift, host->name, data->name);
+      memcpy (&vt->suffix, &suffix, sizeof (vt->suffix));
       vt->next = NULL;
 
-      if (value_table_ptr[i] == NULL)
-       value_table[i] = vt;
+      if (value_list_tail[i] == NULL)
+        value_list_head[i] = vt;
       else
-       value_table_ptr[i]->next = vt;
-      value_table_ptr[i] = vt;
+        value_list_tail[i]->next = vt;
+      value_list_tail[i] = vt;
 
       /* Copy OID to oid_list[i + 1] */
       memcpy (oid_list[i].oid, vb->name, sizeof (oid) * vb->name_length);
@@ -1391,28 +1490,28 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
   res = NULL;
 
   if (status == 0)
-    csnmp_dispatch_table (host, data, instance_list, value_table);
+    csnmp_dispatch_table (host, data, instance_list_head, value_list_head);
 
   /* Free all allocated variables here */
-  while (instance_list != NULL)
+  while (instance_list_head != NULL)
   {
-    instance_list_ptr = instance_list->next;
-    sfree (instance_list);
-    instance_list = instance_list_ptr;
+    csnmp_list_instances_t *next = instance_list_head->next;
+    sfree (instance_list_head);
+    instance_list_head = next;
   }
 
   for (i = 0; i < data->values_len; i++)
   {
-    csnmp_table_values_t *tmp;
-    while (value_table[i] != NULL)
+    while (value_list_head[i] != NULL)
     {
-      tmp = value_table[i]->next;
-      sfree (value_table[i]);
-      value_table[i] = tmp;
+      csnmp_table_values_t *next = value_list_head[i]->next;
+      sfree (value_list_head[i]);
+      value_list_head[i] = next;
     }
   }
 
-  sfree (value_table);
+  sfree (value_list_head);
+  sfree (value_list_tail);
   sfree (oid_list);
 
   return (0);
@@ -1449,7 +1548,7 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
   if (ds->ds_num != data->values_len)
   {
     ERROR ("snmp plugin: DataSet `%s' requires %i values, but config talks about %i",
-       data->type, ds->ds_num, data->values_len);
+        data->type, ds->ds_num, data->values_len);
     return (-1);
   }
 
@@ -1492,7 +1591,7 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
 
     snmp_sess_error (host->sess_handle, NULL, NULL, &errstr);
     ERROR ("snmp plugin: host %s: snmp_sess_synch_response failed: %s",
-       host->name, (errstr == NULL) ? "Unknown problem" : errstr);
+        host->name, (errstr == NULL) ? "Unknown problem" : errstr);
 
     if (res != NULL)
       snmp_free_pdu (res);
@@ -1510,13 +1609,13 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
 #if COLLECT_DEBUG
     char buffer[1024];
     snprint_variable (buffer, sizeof (buffer),
-       vb->name, vb->name_length, vb);
+        vb->name, vb->name_length, vb);
     DEBUG ("snmp plugin: Got this variable: %s", buffer);
 #endif /* COLLECT_DEBUG */
 
     for (i = 0; i < data->values_len; i++)
       if (snmp_oid_compare (data->values[i].oid, data->values[i].oid_len,
-           vb->name, vb->name_length) == 0)
+            vb->name, vb->name_length) == 0)
         vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type,
             data->scale, data->shift, host->name, data->name);
   } /* for (res->variables) */
@@ -1544,7 +1643,7 @@ static int csnmp_read_host (user_data_t *ud)
   host = ud->data;
 
   if (host->interval == 0)
-    host->interval = interval_g;
+    host->interval = plugin_get_interval ();
 
   time_start = cdtime ();
 
index c7b634bc12d45de636d59a89850e0c8e6892e145..46d3534fcae818cd6fc286cd448d824ad22ccad8 100644 (file)
@@ -221,7 +221,7 @@ static void swap_submit_gauge (const char *plugin_instance, /* {{{ */
        swap_submit (plugin_instance, "swap", type_instance, v);
 } /* }}} void swap_submit_gauge */
 
-#if KERNEL_LINUX
+#if KERNEL_LINUX || HAVE_PERFSTAT
 static void swap_submit_derive (const char *plugin_instance, /* {{{ */
                const char *type_instance, derive_t value)
 {
@@ -230,7 +230,9 @@ static void swap_submit_derive (const char *plugin_instance, /* {{{ */
        v.derive = value;
        swap_submit (plugin_instance, "swap_io", type_instance, v);
 } /* }}} void swap_submit_derive */
+#endif
 
+#if KERNEL_LINUX
 static int swap_read_separate (void) /* {{{ */
 {
        FILE *fh;
@@ -786,8 +788,12 @@ static int swap_read (void) /* {{{ */
                         sstrerror (errno, errbuf, sizeof (errbuf)));
                 return (-1);
         }
+
        swap_submit_gauge (NULL, "used", (gauge_t) (pmemory.pgsp_total - pmemory.pgsp_free) * pagesize);
        swap_submit_gauge (NULL, "free", (gauge_t) pmemory.pgsp_free * pagesize );
+       swap_submit_gauge (NULL, "reserved", (gauge_t) pmemory.pgsp_rsvd * pagesize);
+       swap_submit_derive (NULL, "in",  (derive_t) pmemory.pgspins * pagesize);
+       swap_submit_derive (NULL, "out", (derive_t) pmemory.pgspouts * pagesize);
 
        return (0);
 } /* }}} int swap_read */
index 4d90c41a51ac8029baf4bdd34eadf767b6d1e01a..765b892e265498e0299fb4f7c4f2265eef93290c 100644 (file)
@@ -275,9 +275,9 @@ static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 static int port_collect_listening = 0;
 static port_entry_t *port_list_head = NULL;
 
+#if KERNEL_LINUX
 static uint32_t sequence_number = 0;
 
-#if KERNEL_LINUX
 enum
 {
   SRC_DUNNO,
index d729477bbc54db999322dedfe581834dc9725c21..2c1665fc9fcbbdffc3c91935838a720e4fbf6d6f 100644 (file)
@@ -205,6 +205,7 @@ static void *us_handle_client (void *arg)
                close (fdin);
                close (fdout);
                pthread_exit ((void *) 1);
+               return ((void *) 1);
        }
 
        fhout = fdopen (fdout, "w");
@@ -216,6 +217,7 @@ static void *us_handle_client (void *arg)
                fclose (fhin); /* this closes fdin as well */
                close (fdout);
                pthread_exit ((void *) 1);
+               return ((void *) 1);
        }
 
        /* change output buffer to line buffered mode */
@@ -227,6 +229,7 @@ static void *us_handle_client (void *arg)
                fclose (fhin);
                fclose (fhout);
                pthread_exit ((void *) 1);
+               return ((void *) 0);
        }
 
        while (42)
@@ -240,6 +243,9 @@ static void *us_handle_client (void *arg)
                errno = 0;
                if (fgets (buffer, sizeof (buffer), fhin) == NULL)
                {
+                       if ((errno == EINTR) || (errno == EAGAIN))
+                               continue;
+
                        if (errno != 0)
                        {
                                char errbuf[1024];
@@ -268,6 +274,7 @@ static void *us_handle_client (void *arg)
                        fclose (fhin);
                        fclose (fhout);
                        pthread_exit ((void *) 1);
+                       return ((void *) 1);
                }
 
                if (strcasecmp (fields[0], "getval") == 0)
@@ -318,6 +325,9 @@ static void *us_server_thread (void __attribute__((unused)) *arg)
        pthread_t th;
        pthread_attr_t th_attr;
 
+       pthread_attr_init (&th_attr);
+       pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED);
+
        if (us_open_socket () != 0)
                pthread_exit ((void *) 1);
 
@@ -336,6 +346,7 @@ static void *us_server_thread (void __attribute__((unused)) *arg)
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
                        close (sock_fd);
                        sock_fd = -1;
+                       pthread_attr_destroy (&th_attr);
                        pthread_exit ((void *) 1);
                }
 
@@ -352,10 +363,8 @@ static void *us_server_thread (void __attribute__((unused)) *arg)
 
                DEBUG ("Spawning child to handle connection on fd #%i", *remote_fd);
 
-               pthread_attr_init (&th_attr);
-               pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED);
-
-               status = pthread_create (&th, &th_attr, us_handle_client, (void *) remote_fd);
+               status = plugin_thread_create (&th, &th_attr,
+                               us_handle_client, (void *) remote_fd);
                if (status != 0)
                {
                        char errbuf[1024];
@@ -369,6 +378,7 @@ static void *us_server_thread (void __attribute__((unused)) *arg)
 
        close (sock_fd);
        sock_fd = -1;
+       pthread_attr_destroy (&th_attr);
 
        status = unlink ((sock_file != NULL) ? sock_file : US_DEFAULT_PATH);
        if (status != 0)
@@ -434,7 +444,8 @@ static int us_init (void)
 
        loop = 1;
 
-       status = pthread_create (&listen_thread, NULL, us_server_thread, NULL);
+       status = plugin_thread_create (&listen_thread, NULL,
+                       us_server_thread, NULL);
        if (status != 0)
        {
                char errbuf[1024];
index d2ba9633db290aeb17ac0a064874b3c652742b3a..064c3ceed4752a359c7ec2e5b9d84afa27068543 100644 (file)
 /* Using sysctl interface to retrieve the boot time on *BSD / Darwin / OS X systems */
 /* #endif HAVE_SYS_SYSCTL_H */
 
+#elif HAVE_PERFSTAT
+# include <sys/protosw.h>
+# include <libperfstat.h>
+/* Using perfstat_cpu_total to retrive the boot time in AIX */
+/* #endif HAVE_PERFSTAT */
+
 #else
 # error "No applicable input method."
 #endif
@@ -203,7 +209,29 @@ static int uptime_init (void) /* {{{ */
                                "but `boottime' is zero!");
                return (-1);
        }
-#endif /* HAVE_SYS_SYSCTL_H */
+/* #endif HAVE_SYS_SYSCTL_H */
+
+#elif HAVE_PERFSTAT
+       int status;
+       perfstat_cpu_total_t cputotal;
+       int hertz;
+
+       status = perfstat_cpu_total(NULL, &cputotal,
+               sizeof(perfstat_cpu_total_t), 1);
+       if (status < 0)
+       {
+               char errbuf[1024];
+               ERROR ("uptime plugin: perfstat_cpu_total: %s",
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       hertz = sysconf(_SC_CLK_TCK);
+       if (hertz <= 0)
+               hertz = HZ;
+
+       boottime = time(NULL) - cputotal.lbolt / hertz;
+#endif /* HAVE_PERFSTAT */
 
        return (0);
 } /* }}} int uptime_init */
index ba7cd95fde9f81cc1e68f0dc34fb6ec8f57ff3ad..f71b1fd6913bbc9676bb0e494d26bba1f829e7c2 100644 (file)
@@ -512,6 +512,7 @@ int c_avl_insert (c_avl_tree_t *t, void *key, void *value)
        {
                new->parent = NULL;
                t->root = new;
+               t->size = 1;
                return (0);
        }
 
index dd5bcb59ffc9833b2a92341a085e32d526e9e812..fa6e6603ec3e10eedcdc6ea7d56ab64a0b04db6e 100644 (file)
@@ -572,6 +572,7 @@ int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number)
   char **names = NULL;
   cdtime_t *times = NULL;
   size_t number = 0;
+  size_t size_arrays = 0;
 
   int status = 0;
 
@@ -580,42 +581,47 @@ int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number)
 
   pthread_mutex_lock (&cache_lock);
 
+  size_arrays = (size_t) c_avl_size (cache_tree);
+  if (size_arrays < 1)
+  {
+    /* Handle the "no values" case here, to avoid the error message when
+     * calloc() returns NULL. */
+    pthread_mutex_unlock (&cache_lock);
+    return (0);
+  }
+
+  names = calloc (size_arrays, sizeof (*names));
+  times = calloc (size_arrays, sizeof (*times));
+  if ((names == NULL) || (times == NULL))
+  {
+    ERROR ("uc_get_names: calloc failed.");
+    sfree (names);
+    sfree (times);
+    pthread_mutex_unlock (&cache_lock);
+    return (ENOMEM);
+  }
+
   iter = c_avl_get_iterator (cache_tree);
   while (c_avl_iterator_next (iter, (void *) &key, (void *) &value) == 0)
   {
-    char **temp;
-
     /* remove missing values when list values */
     if (value->state == STATE_MISSING)
       continue;
 
-    if (ret_times != NULL)
-    {
-      cdtime_t *tmp_times;
+    /* c_avl_size does not return a number smaller than the number of elements
+     * returned by c_avl_iterator_next. */
+    assert (number < size_arrays);
 
-      tmp_times = (cdtime_t *) realloc (times, sizeof (cdtime_t) * (number + 1));
-      if (tmp_times == NULL)
-      {
-       status = -1;
-       break;
-      }
-      times = tmp_times;
+    if (ret_times != NULL)
       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) */
 
index dd43337ee01390eb3c703a5d8c473bc93eb7ea90..4cbc2f1d96d5a2237946fa69f9c4fabad709e78c 100644 (file)
@@ -250,7 +250,7 @@ int create_putval (char *ret, size_t ret_len, /* {{{ */
                        buffer_ident,
                        (vl->interval > 0)
                        ? CDTIME_T_TO_DOUBLE (vl->interval)
-                       : CDTIME_T_TO_DOUBLE (interval_g),
+                       : CDTIME_T_TO_DOUBLE (plugin_get_interval ()),
                        buffer_values);
 
        return (0);
index 9074b183307671ae78286abe61de3148f6be1f7c..328a6ab6077113cb562ad0ae57a5abffc05d4d01 100644 (file)
@@ -39,8 +39,8 @@ static int vcomplain (int level, c_complain_t *c,
 
        c->last = now;
 
-       if (c->interval < interval_g)
-               c->interval = interval_g;
+       if (c->interval < plugin_get_interval ())
+               c->interval = plugin_get_interval ();
        else
                c->interval *= 2;
 
index dcac80764c665ca1dd98a456feea0cb2386692c4..aadf9c5ec1d7f6a4879a5701ebe19f2a25d23633 100644 (file)
@@ -184,7 +184,7 @@ static int udb_config_set_uint (unsigned int *ret_value, /* {{{ */
  */
 static int udb_result_submit (udb_result_t *r, /* {{{ */
     udb_result_preparation_area_t *r_area,
-    const udb_query_t const *q, udb_query_preparation_area_t *q_area)
+    udb_query_t const *q, udb_query_preparation_area_t *q_area)
 {
   value_list_t vl = VALUE_LIST_INIT;
   size_t i;
@@ -219,7 +219,7 @@ static int udb_result_submit (udb_result_t *r, /* {{{ */
 
   sstrncpy (vl.host, q_area->host, sizeof (vl.host));
   sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
+  sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.plugin_instance));
   sstrncpy (vl.type, r->type, sizeof (vl.type));
 
   /* Set vl.type_instance {{{ */
@@ -259,7 +259,7 @@ static int udb_result_submit (udb_result_t *r, /* {{{ */
   return (0);
 } /* }}} void udb_result_submit */
 
-static void udb_result_finish_result (const udb_result_t const *r, /* {{{ */
+static void udb_result_finish_result (udb_result_t const *r, /* {{{ */
     udb_result_preparation_area_t *prep_area)
 {
   if ((r == NULL) || (prep_area == NULL))
@@ -275,7 +275,7 @@ static void udb_result_finish_result (const udb_result_t const *r, /* {{{ */
 static int udb_result_handle_result (udb_result_t *r, /* {{{ */
     udb_query_preparation_area_t *q_area,
     udb_result_preparation_area_t *r_area,
-    const udb_query_t const *q, char **column_values)
+    udb_query_t const *q, char **column_values)
 {
   size_t i;
 
@@ -290,7 +290,7 @@ static int udb_result_handle_result (udb_result_t *r, /* {{{ */
   return udb_result_submit (r, r_area, q, q_area);
 } /* }}} int udb_result_handle_result */
 
-static int udb_result_prepare_result (const udb_result_t const *r, /* {{{ */
+static int udb_result_prepare_result (udb_result_t const *r, /* {{{ */
     udb_result_preparation_area_t *prep_area,
     char **column_names, size_t column_num)
 {
@@ -825,7 +825,7 @@ int udb_query_check_version (udb_query_t *q, unsigned int version) /* {{{ */
   return (1);
 } /* }}} int udb_query_check_version */
 
-void udb_query_finish_result (const udb_query_t const *q, /* {{{ */
+void udb_query_finish_result (udb_query_t const *q, /* {{{ */
     udb_query_preparation_area_t *prep_area)
 {
   udb_result_preparation_area_t *r_area;
@@ -851,7 +851,7 @@ void udb_query_finish_result (const udb_query_t const *q, /* {{{ */
   }
 } /* }}} void udb_query_finish_result */
 
-int udb_query_handle_result (const udb_query_t const *q, /* {{{ */
+int udb_query_handle_result (udb_query_t const *q, /* {{{ */
     udb_query_preparation_area_t *prep_area, char **column_values)
 {
   udb_result_preparation_area_t *r_area;
@@ -904,7 +904,7 @@ int udb_query_handle_result (const udb_query_t const *q, /* {{{ */
   return (0);
 } /* }}} int udb_query_handle_result */
 
-int udb_query_prepare_result (const udb_query_t const *q, /* {{{ */
+int udb_query_prepare_result (udb_query_t const *q, /* {{{ */
     udb_query_preparation_area_t *prep_area,
     const char *host, const char *plugin, const char *db_name,
     char **column_names, size_t column_num, cdtime_t interval)
index 727be03647714e8bc76f6b4d5403508eeca74616..b6f4cea00d6351c8c0edcf97ab44d0a45cfc7e86 100644 (file)
@@ -65,13 +65,13 @@ void *udb_query_get_user_data (udb_query_t *q);
  */
 int udb_query_check_version (udb_query_t *q, unsigned int version);
 
-int udb_query_prepare_result (const udb_query_t const *q,
+int udb_query_prepare_result (udb_query_t const *q,
     udb_query_preparation_area_t *prep_area,
     const char *host, const char *plugin, const char *db_name,
     char **column_names, size_t column_num, cdtime_t interval);
-int udb_query_handle_result (const udb_query_t const *q,
+int udb_query_handle_result (udb_query_t const *q,
     udb_query_preparation_area_t *prep_area, char **column_values);
-void udb_query_finish_result (const udb_query_t const *q,
+void udb_query_finish_result (udb_query_t const *q,
     udb_query_preparation_area_t *prep_area);
 
 udb_query_preparation_area_t *
index d20b7e39bdd453de1bdec5c4564ed30e994647a6..97f21a1f78317ebc4cc5d8ccce7f61dcd322b0ae 100644 (file)
@@ -234,6 +234,7 @@ void fbh_destroy (fbhash_t *h) /* {{{ */
   if (h == NULL)
     return;
 
+  pthread_mutex_destroy (&h->lock);
   free (h->filename);
   fbh_free_tree (h->tree);
 } /* }}} void fbh_destroy */
index 49a59c506959fa4938d3cdc60fb6b1fe281fb9b7..b9b906fa373943b427e02d2b72f1fb39d3f21ed6 100644 (file)
@@ -33,7 +33,8 @@
  * Largely taken from write_graphite.c as it remains the same formatting */
 
 static int gr_format_values (char *ret, size_t ret_len,
-        int ds_num, const data_set_t *ds, const value_list_t *vl)
+        int ds_num, const data_set_t *ds, const value_list_t *vl,
+        gauge_t const *rates)
 {
     size_t offset = 0;
     int status;
@@ -59,6 +60,8 @@ static int gr_format_values (char *ret, size_t ret_len,
 
     if (ds->ds[ds_num].type == DS_TYPE_GAUGE)
         BUFFER_ADD ("%f", vl->values[ds_num].gauge);
+    else if (rates != NULL)
+        BUFFER_ADD ("%f", rates[ds_num]);
     else if (ds->ds[ds_num].type == DS_TYPE_COUNTER)
         BUFFER_ADD ("%llu", vl->values[ds_num].counter);
     else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
@@ -165,12 +168,17 @@ static int gr_format_name (char *ret, int ret_len,
 
 int format_graphite (char *buffer, size_t buffer_size,
     const data_set_t *ds, const value_list_t *vl, char *prefix,
-    char *postfix, char escape_char)
+    char *postfix, char escape_char,
+    _Bool store_rates)
 {
     int status = 0;
     int i;
     int buffer_pos = 0;
 
+    gauge_t *rates = NULL;
+    if (store_rates)
+      rates = uc_get_rate (ds, vl);
+
     for (i = 0; i < ds->ds_num; i++)
     {
         const char *ds_name = NULL;
@@ -186,17 +194,19 @@ int format_graphite (char *buffer, size_t buffer_size,
                     prefix, postfix, escape_char);
         if (status != 0)
         {
-            ERROR ("amqp plugin: error with gr_format_name");
+            ERROR ("format_graphite: error with gr_format_name");
+            sfree (rates);
             return (status);
         }
 
         escape_string (key, sizeof (key));
         /* Convert the values to an ASCII representation and put that into
          * `values'. */
-        status = gr_format_values (values, sizeof (values), i, ds, vl);
+        status = gr_format_values (values, sizeof (values), i, ds, vl, rates);
         if (status != 0)
         {
             ERROR ("format_graphite: error with gr_format_values");
+            sfree (rates);
             return (status);
         }
 
@@ -209,6 +219,7 @@ int format_graphite (char *buffer, size_t buffer_size,
         if (message_len >= sizeof (message)) {
             ERROR ("format_graphite: message buffer too small: "
                     "Need %zu bytes.", message_len + 1);
+            sfree (rates);
             return (-ENOMEM);
         }
 
@@ -216,11 +227,13 @@ int format_graphite (char *buffer, size_t buffer_size,
         if ((buffer_pos + message_len) >= buffer_size)
         {
             ERROR ("format_graphite: target buffer too small");
+            sfree (rates);
             return (-ENOMEM);
         }
         memcpy((void *) (buffer + buffer_pos), message, message_len);
         buffer_pos += message_len;
     }
+    sfree (rates);
     return (status);
 } /* int format_graphite */
 
index a3c4d85c9f4f2368e3b2bb3df165977315ad06f7..a7a05bbbadfea7f006801b6649beb7ed56656f85 100644 (file)
@@ -28,6 +28,7 @@
 int format_graphite (char *buffer,
     size_t buffer_size, const data_set_t *ds,
     const value_list_t *vl, const char *prefix,
-    const char *postfix, const char escape_char);
+    const char *postfix, const char escape_char,
+    _Bool store_rates);
 
 #endif /* UTILS_FORMAT_GRAPHITE_H */
index aac6135e28f8bf7d1be5966d5316a64c28200e93..6789758d4e2460abad4259a6da047550b5903fac 100644 (file)
@@ -61,4 +61,39 @@ cdtime_t cdtime (void) /* {{{ */
 } /* }}} cdtime_t cdtime */
 #endif
 
+size_t cdtime_to_iso8601 (char *s, size_t max, cdtime_t t) /* {{{ */
+{
+  struct timespec t_spec;
+  struct tm t_tm;
+
+  size_t len;
+
+  CDTIME_T_TO_TIMESPEC (t, &t_spec);
+  NORMALIZE_TIMESPEC (t_spec);
+
+  if (localtime_r ((time_t *)&t_spec.tv_sec, &t_tm) == NULL) {
+    char errbuf[1024];
+    ERROR ("cdtime_to_iso8601: localtime_r failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (0);
+  }
+
+  len = strftime (s, max, "%Y-%m-%dT%H:%M:%S", &t_tm);
+  if (len == 0)
+    return 0;
+
+  if (max - len > 2) {
+    int n = snprintf (s + len, max - len, ".%09i", (int)t_spec.tv_nsec);
+    len += (n < max - len) ? n : max - len;
+  }
+
+  if (max - len > 3) {
+    int n = strftime (s + len, max - len, "%z", &t_tm);
+    len += (n < max - len) ? n : max - len;
+  }
+
+  s[max - 1] = '\0';
+  return len;
+} /* }}} size_t cdtime_to_iso8601 */
+
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 0fd809ac5de49327044102a9eabd9b28820abdc9..0081957dc514d8fdb9ba513132c726b639503d07 100644 (file)
 
 cdtime_t cdtime (void);
 
+/* format a cdtime_t value in ISO 8601 format:
+ * returns the number of characters written to the string (not including the
+ * terminating null byte or 0 on error; the function ensures that the string
+ * is null terminated */
+size_t cdtime_to_iso8601 (char *s, size_t max, cdtime_t t);
+
 #endif /* UTILS_TIME_H */
 /* vim: set sw=2 sts=2 et : */
diff --git a/src/utils_vl_lookup.c b/src/utils_vl_lookup.c
new file mode 100644 (file)
index 0000000..2dada24
--- /dev/null
@@ -0,0 +1,541 @@
+/**
+ * collectd - src/utils_vl_lookup.c
+ * Copyright (C) 2012  Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_vl_lookup.h"
+#include "utils_avltree.h"
+
+#if BUILD_TEST
+# define sstrncpy strncpy
+# define plugin_log(s, ...) do { \
+  printf ("[severity %i] ", s); \
+  printf (__VA_ARGS__); \
+  printf ("\n"); \
+} while (0)
+#endif
+
+/*
+ * Types
+ */
+struct lookup_s
+{
+  c_avl_tree_t *by_type_tree;
+
+  lookup_class_callback_t cb_user_class;
+  lookup_obj_callback_t cb_user_obj;
+  lookup_free_class_callback_t cb_free_class;
+  lookup_free_obj_callback_t cb_free_obj;
+};
+
+struct user_obj_s;
+typedef struct user_obj_s user_obj_t;
+struct user_obj_s
+{
+  void *user_obj;
+  identifier_t ident;
+
+  user_obj_t *next;
+};
+
+struct user_class_s
+{
+  void *user_class;
+  identifier_t ident;
+  user_obj_t *user_obj_list; /* list of user_obj */
+};
+typedef struct user_class_s user_class_t;
+
+struct user_class_list_s;
+typedef struct user_class_list_s user_class_list_t;
+struct user_class_list_s
+{
+  user_class_t entry;
+  user_class_list_t *next;
+};
+
+struct by_type_entry_s
+{
+  c_avl_tree_t *by_plugin_tree; /* plugin -> user_class_list_t */
+  user_class_list_t *wildcard_plugin_list;
+};
+typedef struct by_type_entry_s by_type_entry_t;
+
+/*
+ * Private functions
+ */
+static void *lu_create_user_obj (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl,
+    user_class_t *user_class)
+{
+  user_obj_t *user_obj;
+
+  user_obj = malloc (sizeof (*user_obj));
+  if (user_obj == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    return (NULL);
+  }
+  memset (user_obj, 0, sizeof (*user_obj));
+  user_obj->next = NULL;
+
+  user_obj->user_obj = obj->cb_user_class (ds, vl, user_class->user_class);
+  if (user_obj->user_obj == NULL)
+  {
+    sfree (user_obj);
+    WARNING("utils_vl_lookup: User-provided constructor failed.");
+    return (NULL);
+  }
+
+  sstrncpy (user_obj->ident.host,
+    LU_IS_ALL (user_class->ident.host) ?  "/all/" : vl->host,
+    sizeof (user_obj->ident.host));
+  sstrncpy (user_obj->ident.plugin,
+    LU_IS_ALL (user_class->ident.plugin) ?  "/all/" : vl->plugin,
+    sizeof (user_obj->ident.plugin));
+  sstrncpy (user_obj->ident.plugin_instance,
+    LU_IS_ALL (user_class->ident.plugin_instance) ?  "/all/" : vl->plugin_instance,
+    sizeof (user_obj->ident.plugin_instance));
+  sstrncpy (user_obj->ident.type,
+    LU_IS_ALL (user_class->ident.type) ?  "/all/" : vl->type,
+    sizeof (user_obj->ident.type));
+  sstrncpy (user_obj->ident.type_instance,
+    LU_IS_ALL (user_class->ident.type_instance) ?  "/all/" : vl->type_instance,
+    sizeof (user_obj->ident.type_instance));
+
+  if (user_class->user_obj_list == NULL)
+  {
+    user_class->user_obj_list = user_obj;
+  }
+  else
+  {
+    user_obj_t *last = user_class->user_obj_list;
+    while (last->next != NULL)
+      last = last->next;
+    last->next = user_obj;
+  }
+
+  return (user_obj);
+} /* }}} void *lu_create_user_obj */
+
+static user_obj_t *lu_find_user_obj (user_class_t *user_class, /* {{{ */
+    value_list_t const *vl)
+{
+  user_obj_t *ptr;
+
+  for (ptr = user_class->user_obj_list;
+      ptr != NULL;
+      ptr = ptr->next)
+  {
+    if (!LU_IS_ALL (ptr->ident.host)
+        && (strcmp (ptr->ident.host, vl->host) != 0))
+      continue;
+    if (!LU_IS_ALL (ptr->ident.plugin_instance)
+        && (strcmp (ptr->ident.plugin_instance, vl->plugin_instance) != 0))
+      continue;
+    if (!LU_IS_ALL (ptr->ident.type_instance)
+        && (strcmp (ptr->ident.type_instance, vl->type_instance) != 0))
+      continue;
+
+    return (ptr);
+  }
+
+  return (NULL);
+} /* }}} user_obj_t *lu_find_user_obj */
+
+static int lu_handle_user_class (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl,
+    user_class_t *user_class)
+{
+  user_obj_t *user_obj;
+  int status;
+
+  assert (strcmp (vl->type, user_class->ident.type) == 0);
+  assert (LU_IS_WILDCARD (user_class->ident.plugin)
+      || (strcmp (vl->plugin, user_class->ident.plugin) == 0));
+
+  /* When we get here, type and plugin already match the user class. Now check
+   * the rest of the fields. */
+  if (!LU_IS_WILDCARD (user_class->ident.type_instance)
+      && (strcmp (vl->type_instance, user_class->ident.type_instance) != 0))
+    return (1);
+  if (!LU_IS_WILDCARD (user_class->ident.plugin_instance)
+      && (strcmp (vl->plugin_instance,
+          user_class->ident.plugin_instance) != 0))
+    return (1);
+  if (!LU_IS_WILDCARD (user_class->ident.host)
+      && (strcmp (vl->host, user_class->ident.host) != 0))
+    return (1);
+
+  user_obj = lu_find_user_obj (user_class, vl);
+  if (user_obj == NULL)
+  {
+    /* call lookup_class_callback_t() and insert into the list of user objects. */
+    user_obj = lu_create_user_obj (obj, ds, vl, user_class);
+    if (user_obj == NULL)
+      return (-1);
+  }
+
+  status = obj->cb_user_obj (ds, vl,
+      user_class->user_class, user_obj->user_obj);
+  if (status != 0)
+  {
+    ERROR ("utils_vl_lookup: The user object callback failed with status %i.",
+        status);
+    /* Returning a negative value means: abort! */
+    if (status < 0)
+      return (status);
+    else
+      return (1);
+  }
+
+  return (0);
+} /* }}} int lu_handle_user_class */
+
+static int lu_handle_user_class_list (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl,
+    user_class_list_t *user_class_list)
+{
+  user_class_list_t *ptr;
+  int retval = 0;
+  
+  for (ptr = user_class_list; ptr != NULL; ptr = ptr->next)
+  {
+    int status;
+
+    status = lu_handle_user_class (obj, ds, vl, &ptr->entry);
+    if (status < 0)
+      return (status);
+    else if (status == 0)
+      retval++;
+  }
+
+  return (retval);
+} /* }}} int lu_handle_user_class_list */
+
+static by_type_entry_t *lu_search_by_type (lookup_t *obj, /* {{{ */
+    char const *type, _Bool allocate_if_missing)
+{
+  by_type_entry_t *by_type;
+  char *type_copy;
+  int status;
+
+  status = c_avl_get (obj->by_type_tree, type, (void *) &by_type);
+  if (status == 0)
+    return (by_type);
+
+  if (!allocate_if_missing)
+    return (NULL);
+
+  type_copy = strdup (type);
+  if (type_copy == NULL)
+  {
+    ERROR ("utils_vl_lookup: strdup failed.");
+    return (NULL);
+  }
+
+  by_type = malloc (sizeof (*by_type));
+  if (by_type == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    sfree (type_copy);
+    return (NULL);
+  }
+  memset (by_type, 0, sizeof (*by_type));
+  by_type->wildcard_plugin_list = NULL;
+  
+  by_type->by_plugin_tree = c_avl_create ((void *) strcmp);
+  if (by_type->by_plugin_tree == NULL)
+  {
+    ERROR ("utils_vl_lookup: c_avl_create failed.");
+    sfree (by_type);
+    sfree (type_copy);
+    return (NULL);
+  }
+
+  status = c_avl_insert (obj->by_type_tree,
+      /* key = */ type_copy, /* value = */ by_type);
+  assert (status <= 0); /* >0 => entry exists => race condition. */
+  if (status != 0)
+  {
+    ERROR ("utils_vl_lookup: c_avl_insert failed.");
+    c_avl_destroy (by_type->by_plugin_tree);
+    sfree (by_type);
+    sfree (type_copy);
+    return (NULL);
+  }
+  
+  return (by_type);
+} /* }}} by_type_entry_t *lu_search_by_type */
+
+static int lu_add_by_plugin (by_type_entry_t *by_type, /* {{{ */
+    identifier_t const *ident, user_class_list_t *user_class_list)
+{
+  user_class_list_t *ptr = NULL;
+
+  /* Lookup user_class_list from the per-plugin structure. If this is the first
+   * user_class to be added, the blocks return immediately. Otherwise they will
+   * set "ptr" to non-NULL. */
+  if (LU_IS_WILDCARD (ident->plugin))
+  {
+    if (by_type->wildcard_plugin_list == NULL)
+    {
+      by_type->wildcard_plugin_list = user_class_list;
+      return (0);
+    }
+
+    ptr = by_type->wildcard_plugin_list;
+  } /* if (plugin is wildcard) */
+  else /* (plugin is not wildcard) */
+  {
+    int status;
+
+    status = c_avl_get (by_type->by_plugin_tree,
+        ident->plugin, (void *) &ptr);
+
+    if (status != 0) /* plugin not yet in tree */
+    {
+      char *plugin_copy = strdup (ident->plugin);
+
+      if (plugin_copy == NULL)
+      {
+        ERROR ("utils_vl_lookup: strdup failed.");
+        sfree (user_class_list);
+        return (ENOMEM);
+      }
+
+      status = c_avl_insert (by_type->by_plugin_tree,
+          plugin_copy, user_class_list);
+      if (status != 0)
+      {
+        ERROR ("utils_vl_lookup: c_avl_insert(\"%s\") failed with status %i.",
+            plugin_copy, status);
+        sfree (plugin_copy);
+        sfree (user_class_list);
+        return (status);
+      }
+      else
+      {
+        return (0);
+      }
+    } /* if (plugin not yet in tree) */
+  } /* if (plugin is not wildcard) */
+
+  assert (ptr != NULL);
+
+  while (ptr->next != NULL)
+    ptr = ptr->next;
+  ptr->next = user_class_list;
+
+  return (0);
+} /* }}} int lu_add_by_plugin */
+
+static void lu_destroy_user_obj (lookup_t *obj, /* {{{ */
+    user_obj_t *user_obj)
+{
+  while (user_obj != NULL)
+  {
+    user_obj_t *next = user_obj->next;
+
+    if (obj->cb_free_obj != NULL)
+      obj->cb_free_obj (user_obj->user_obj);
+    user_obj->user_obj = NULL;
+
+    sfree (user_obj);
+    user_obj = next;
+  }
+} /* }}} void lu_destroy_user_obj */
+
+static void lu_destroy_user_class_list (lookup_t *obj, /* {{{ */
+    user_class_list_t *user_class_list)
+{
+  while (user_class_list != NULL)
+  {
+    user_class_list_t *next = user_class_list->next;
+
+    if (obj->cb_free_class != NULL)
+      obj->cb_free_class (user_class_list->entry.user_class);
+    user_class_list->entry.user_class = NULL;
+
+    lu_destroy_user_obj (obj, user_class_list->entry.user_obj_list);
+    user_class_list->entry.user_obj_list = NULL;
+
+    sfree (user_class_list);
+    user_class_list = next;
+  }
+} /* }}} void lu_destroy_user_class_list */
+
+static void lu_destroy_by_type (lookup_t *obj, /* {{{ */
+    by_type_entry_t *by_type)
+{
+  
+  while (42)
+  {
+    char *plugin = NULL;
+    user_class_list_t *user_class_list = NULL;
+    int status;
+
+    status = c_avl_pick (by_type->by_plugin_tree,
+        (void *) &plugin, (void *) &user_class_list);
+    if (status != 0)
+      break;
+
+    DEBUG ("utils_vl_lookup: lu_destroy_by_type: Destroying plugin \"%s\".",
+        plugin);
+    sfree (plugin);
+    lu_destroy_user_class_list (obj, user_class_list);
+  }
+
+  c_avl_destroy (by_type->by_plugin_tree);
+  by_type->by_plugin_tree = NULL;
+
+  lu_destroy_user_class_list (obj, by_type->wildcard_plugin_list);
+  by_type->wildcard_plugin_list = NULL;
+
+  sfree (by_type);
+} /* }}} int lu_destroy_by_type */
+
+/*
+ * Public functions
+ */
+lookup_t *lookup_create (lookup_class_callback_t cb_user_class, /* {{{ */
+    lookup_obj_callback_t cb_user_obj,
+    lookup_free_class_callback_t cb_free_class,
+    lookup_free_obj_callback_t cb_free_obj)
+{
+  lookup_t *obj = malloc (sizeof (*obj));
+  if (obj == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    return (NULL);
+  }
+  memset (obj, 0, sizeof (*obj));
+
+  obj->by_type_tree = c_avl_create ((void *) strcmp);
+  if (obj->by_type_tree == NULL)
+  {
+    ERROR ("utils_vl_lookup: c_avl_create failed.");
+    sfree (obj);
+    return (NULL);
+  }
+
+  obj->cb_user_class = cb_user_class;
+  obj->cb_user_obj = cb_user_obj;
+  obj->cb_free_class = cb_free_class;
+  obj->cb_free_obj = cb_free_obj;
+
+  return (obj);
+} /* }}} lookup_t *lookup_create */
+
+void lookup_destroy (lookup_t *obj) /* {{{ */
+{
+  int status;
+
+  if (obj == NULL)
+    return;
+
+  while (42)
+  {
+    char *type = NULL;
+    by_type_entry_t *by_type = NULL;
+
+    status = c_avl_pick (obj->by_type_tree, (void *) &type, (void *) &by_type);
+    if (status != 0)
+      break;
+
+    DEBUG ("utils_vl_lookup: lookup_destroy: Destroying type \"%s\".", type);
+    sfree (type);
+    lu_destroy_by_type (obj, by_type);
+  }
+
+  c_avl_destroy (obj->by_type_tree);
+  obj->by_type_tree = NULL;
+
+  sfree (obj);
+} /* }}} void lookup_destroy */
+
+int lookup_add (lookup_t *obj, /* {{{ */
+    identifier_t const *ident, void *user_class)
+{
+  by_type_entry_t *by_type = NULL;
+  user_class_list_t *user_class_obj;
+
+  by_type = lu_search_by_type (obj, ident->type, /* allocate = */ 1);
+  if (by_type == NULL)
+    return (-1);
+
+  user_class_obj = malloc (sizeof (*user_class_obj));
+  if (user_class_obj == NULL)
+  {
+    ERROR ("utils_vl_lookup: malloc failed.");
+    return (ENOMEM);
+  }
+  memset (user_class_obj, 0, sizeof (*user_class_obj));
+  user_class_obj->entry.user_class = user_class;
+  memmove (&user_class_obj->entry.ident, ident, sizeof (*ident));
+  user_class_obj->entry.user_obj_list = NULL;
+  user_class_obj->next = NULL;
+
+  return (lu_add_by_plugin (by_type, ident, user_class_obj));
+} /* }}} int lookup_add */
+
+/* returns the number of successful calls to the callback function */
+int lookup_search (lookup_t *obj, /* {{{ */
+    data_set_t const *ds, value_list_t const *vl)
+{
+  by_type_entry_t *by_type = NULL;
+  user_class_list_t *user_class_list = NULL;
+  int retval = 0;
+  int status;
+
+  if ((obj == NULL) || (ds == NULL) || (vl == NULL))
+    return (-EINVAL);
+
+  by_type = lu_search_by_type (obj, vl->type, /* allocate = */ 0);
+  if (by_type == NULL)
+    return (0);
+
+  status = c_avl_get (by_type->by_plugin_tree,
+      vl->plugin, (void *) &user_class_list);
+  if (status == 0)
+  {
+    status = lu_handle_user_class_list (obj, ds, vl, user_class_list);
+    if (status < 0)
+      return (status);
+    retval += status;
+  }
+
+  if (by_type->wildcard_plugin_list != NULL)
+  {
+    status = lu_handle_user_class_list (obj, ds, vl,
+        by_type->wildcard_plugin_list);
+    if (status < 0)
+      return (status);
+    retval += status;
+  }
+    
+  return (retval);
+} /* }}} lookup_search */
diff --git a/src/utils_vl_lookup.h b/src/utils_vl_lookup.h
new file mode 100644 (file)
index 0000000..c006fc7
--- /dev/null
@@ -0,0 +1,90 @@
+/**
+ * collectd - src/utils_vl_lookup.h
+ * Copyright (C) 2012  Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_VL_LOOKUP_H
+#define UTILS_VL_LOOKUP_H 1
+
+#include "plugin.h"
+
+/*
+ * Types
+ */
+struct lookup_s;
+typedef struct lookup_s lookup_t;
+
+/* Given a user_class, constructs a new user_obj. */
+typedef void *(*lookup_class_callback_t) (data_set_t const *ds,
+    value_list_t const *vl, void *user_class);
+
+/* Given a user_class and a ds/vl combination, does stuff with the data.
+ * This is the main working horse of the module. */
+typedef int (*lookup_obj_callback_t) (data_set_t const *ds,
+    value_list_t const *vl,
+    void *user_class, void *user_obj);
+
+/* Used to free user_class pointers. May be NULL in which case nothing is
+ * freed. */
+typedef void (*lookup_free_class_callback_t) (void *user_class);
+
+/* Used to free user_obj pointers. May be NULL in which case nothing is
+ * freed. */
+typedef void (*lookup_free_obj_callback_t) (void *user_obj);
+
+struct identifier_s
+{
+  char host[DATA_MAX_NAME_LEN];
+  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 identifier_s identifier_t;
+
+#define LU_ANY "/any/"
+#define LU_ALL "/all/"
+
+#define LU_IS_ANY(str) (strcmp (str, LU_ANY) == 0)
+#define LU_IS_ALL(str) (strcmp (str, LU_ALL) == 0)
+#define LU_IS_WILDCARD(str) (LU_IS_ANY(str) || LU_IS_ALL(str))
+
+/*
+ * Functions
+ */
+__attribute__((nonnull(1,2)))
+lookup_t *lookup_create (lookup_class_callback_t,
+    lookup_obj_callback_t,
+    lookup_free_class_callback_t,
+    lookup_free_obj_callback_t);
+void lookup_destroy (lookup_t *obj);
+
+int lookup_add (lookup_t *obj,
+    identifier_t const *ident, void *user_class);
+
+/* TODO(octo): Pass lookup_obj_callback_t to lookup_search()? */
+int lookup_search (lookup_t *obj,
+    data_set_t const *ds, value_list_t const *vl);
+
+#endif /* UTILS_VL_LOOKUP_H */
diff --git a/src/utils_vl_lookup_test.c b/src/utils_vl_lookup_test.c
new file mode 100644 (file)
index 0000000..6265b32
--- /dev/null
@@ -0,0 +1,214 @@
+/**
+ * collectd - src/utils_vl_lookup_test.c
+ * Copyright (C) 2012  Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+#include "utils_vl_lookup.h"
+
+static _Bool expect_new_obj = 0;
+static _Bool have_new_obj = 0;
+
+static identifier_t last_class_ident;
+static identifier_t last_obj_ident;
+
+static data_source_t dsrc_test = { "value", DS_TYPE_DERIVE, 0.0, NAN };
+static data_set_t const ds_test = { "test", 1, &dsrc_test };
+
+static data_source_t dsrc_unknown = { "value", DS_TYPE_DERIVE, 0.0, NAN };
+static data_set_t const ds_unknown = { "unknown", 1, &dsrc_unknown };
+
+static int lookup_obj_callback (data_set_t const *ds,
+    value_list_t const *vl,
+    void *user_class, void *user_obj)
+{
+  identifier_t *class = user_class;
+  identifier_t *obj = user_obj;
+
+  assert (expect_new_obj == have_new_obj);
+
+  memcpy (&last_class_ident, class, sizeof (last_class_ident));
+  memcpy (&last_obj_ident, obj, sizeof (last_obj_ident));
+
+  if (strcmp (obj->plugin_instance, "failure") == 0)
+    return (-1);
+
+  return (0);
+}
+
+static void *lookup_class_callback (data_set_t const *ds,
+    value_list_t const *vl, void *user_class)
+{
+  identifier_t *class = user_class;
+  identifier_t *obj;
+
+  assert (expect_new_obj);
+
+  memcpy (&last_class_ident, class, sizeof (last_class_ident));
+  
+  obj = malloc (sizeof (*obj));
+  strncpy (obj->host, vl->host, sizeof (obj->host));
+  strncpy (obj->plugin, vl->plugin, sizeof (obj->plugin));
+  strncpy (obj->plugin_instance, vl->plugin_instance, sizeof (obj->plugin_instance));
+  strncpy (obj->type, vl->type, sizeof (obj->type));
+  strncpy (obj->type_instance, vl->type_instance, sizeof (obj->type_instance));
+
+  have_new_obj = 1;
+
+  return ((void *) obj);
+}
+
+static void checked_lookup_add (lookup_t *obj, /* {{{ */
+    char const *host,
+    char const *plugin, char const *plugin_instance,
+    char const *type, char const *type_instance)
+{
+  identifier_t ident;
+  void *user_class;
+  int status;
+
+  memset (&ident, 0, sizeof (ident));
+  strncpy (ident.host, host, sizeof (ident.host));
+  strncpy (ident.plugin, plugin, sizeof (ident.plugin));
+  strncpy (ident.plugin_instance, plugin_instance, sizeof (ident.plugin_instance));
+  strncpy (ident.type, type, sizeof (ident.type));
+  strncpy (ident.type_instance, type_instance, sizeof (ident.type_instance));
+
+  user_class = malloc (sizeof (ident));
+  memmove (user_class, &ident, sizeof (ident));
+
+  status = lookup_add (obj, &ident, user_class);
+  assert (status == 0);
+} /* }}} void test_add */
+
+static int checked_lookup_search (lookup_t *obj,
+    char const *host,
+    char const *plugin, char const *plugin_instance,
+    char const *type, char const *type_instance,
+    _Bool expect_new)
+{
+  int status;
+  value_list_t vl = VALUE_LIST_STATIC;
+  data_set_t const *ds = &ds_unknown;
+
+  strncpy (vl.host, host, sizeof (vl.host));
+  strncpy (vl.plugin, plugin, sizeof (vl.plugin));
+  strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+  strncpy (vl.type, type, sizeof (vl.type));
+  strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+  if (strcmp (vl.type, "test") == 0)
+    ds = &ds_test;
+
+  expect_new_obj = expect_new;
+  have_new_obj = 0;
+
+  status = lookup_search (obj, ds, &vl);
+  return (status);
+}
+
+static lookup_t *checked_lookup_create (void)
+{
+  lookup_t *obj = lookup_create (
+      lookup_class_callback,
+      lookup_obj_callback,
+      (void *) free,
+      (void *) free);
+  assert (obj != NULL);
+  return (obj);
+}
+
+static void testcase0 (void)
+{
+  lookup_t *obj = checked_lookup_create ();
+
+  checked_lookup_add (obj, "/any/", "test", "", "test", "/all/");
+  checked_lookup_search (obj, "host0", "test", "", "test", "0",
+      /* expect new = */ 1);
+  checked_lookup_search (obj, "host0", "test", "", "test", "1",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host1", "test", "", "test", "0",
+      /* expect new = */ 1);
+  checked_lookup_search (obj, "host1", "test", "", "test", "1",
+      /* expect new = */ 0);
+
+  lookup_destroy (obj);
+}
+
+static void testcase1 (void)
+{
+  lookup_t *obj = checked_lookup_create ();
+
+  checked_lookup_add (obj, "/any/", "/all/", "/all/", "test", "/all/");
+  checked_lookup_search (obj, "host0", "plugin0", "", "test", "0",
+      /* expect new = */ 1);
+  checked_lookup_search (obj, "host0", "plugin0", "", "test", "1",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host0", "plugin1", "", "test", "0",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host0", "plugin1", "", "test", "1",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host1", "plugin0", "", "test", "0",
+      /* expect new = */ 1);
+  checked_lookup_search (obj, "host1", "plugin0", "", "test", "1",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host1", "plugin1", "", "test", "0",
+      /* expect new = */ 0);
+  checked_lookup_search (obj, "host1", "plugin1", "", "test", "1",
+      /* expect new = */ 0);
+
+  lookup_destroy (obj);
+}
+
+static void testcase2 (void)
+{
+  lookup_t *obj = checked_lookup_create ();
+  int status;
+
+  checked_lookup_add (obj, "/any/", "plugin0", "", "test", "/all/");
+  checked_lookup_add (obj, "/any/", "/all/", "", "test", "ti0");
+
+  status = checked_lookup_search (obj, "host0", "plugin1", "", "test", "",
+      /* expect new = */ 0);
+  assert (status == 0);
+  status = checked_lookup_search (obj, "host0", "plugin0", "", "test", "",
+      /* expect new = */ 1);
+  assert (status == 1);
+  status = checked_lookup_search (obj, "host0", "plugin1", "", "test", "ti0",
+      /* expect new = */ 1);
+  assert (status == 1);
+  status = checked_lookup_search (obj, "host0", "plugin0", "", "test", "ti0",
+      /* expect new = */ 0);
+  assert (status == 2);
+
+  lookup_destroy (obj);
+}
+
+int main (int argc, char **argv) /* {{{ */
+{
+  testcase0 ();
+  testcase1 ();
+  testcase2 ();
+  return (EXIT_SUCCESS);
+} /* }}} int main */
index f3753da20dbd669175b7307a2585d1f5f95176b8..2ae30efe2ca179b0fd13cfafe5914f5a031b14ff 100644 (file)
@@ -352,7 +352,7 @@ static int wg_write_messages (const data_set_t *ds, const value_list_t *vl,
 
     memset (buffer, 0, sizeof (buffer));
     status = format_graphite (buffer, sizeof (buffer), ds, vl,
-            cb->prefix, cb->postfix, cb->escape_char);
+            cb->prefix, cb->postfix, cb->escape_char, cb->store_rates);
     if (status != 0) /* error message has been printed already. */
         return (status);
 
index f4352ff48f60588b72a1ae83d337ba817ff09031..046b7b02120953c65d66de9c27dd2ff5ac482e8c 100644 (file)
@@ -28,7 +28,7 @@
 /*
  * Global variables
  */
-static kstat_t *ksp;
+
 extern kstat_ctl_t *kc;
 
 static void za_submit (const char* type, const char* type_instance, value_t* values, int values_len)
@@ -109,6 +109,7 @@ static int za_read (void)
 {
        gauge_t  arc_hits, arc_misses, l2_hits, l2_misses;
        value_t  l2_io[2];
+       kstat_t  *ksp   = NULL;
 
        get_kstat (&ksp, "zfs", 0, "arcstats");
        if (ksp == NULL)
@@ -165,8 +166,6 @@ static int za_read (void)
 
 static int za_init (void) /* {{{ */
 {
-       ksp = NULL;
-
        /* kstats chain already opened by update_kstat (using *kc), verify everything went fine. */
        if (kc == NULL)
        {
index 97a27a0d75f6bf3adb19cafe27622503eda72c21..762d54718ebd661872ddffe846ce752b0fed43bb 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-DEFAULT_VERSION="5.1.0.git"
+DEFAULT_VERSION="5.1.1.git"
 
 VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"
 
@@ -10,8 +10,4 @@ fi
 
 VERSION="`echo \"$VERSION\" | sed -e 's/-/./g'`"
 
-if test "x`uname -s`" = "xAIX" ; then
-       echo "$VERSION\c"
-else 
-       echo -n "$VERSION"
-fi
+echo -n "$VERSION"