Merge pull request #719 from mfournier/openldap-improvements-rebased
authorPierre-Yves Ritschard <pyr@spootnik.org>
Sat, 15 Nov 2014 09:52:47 +0000 (10:52 +0100)
committerPierre-Yves Ritschard <pyr@spootnik.org>
Sat, 15 Nov 2014 09:52:47 +0000 (10:52 +0100)
Openldap plugin

134 files changed:
.gitignore
AUTHORS
COPYING
README
bindings/java/org/collectd/java/GenericJMXConfConnection.java
configure.ac
contrib/redhat/collectd.spec
src/Makefile.am
src/apache.c
src/battery.c
src/bind.c
src/collectd-python.pod
src/collectd.c [deleted file]
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.h [deleted file]
src/common.c [deleted file]
src/common.h [deleted file]
src/configfile.c [deleted file]
src/configfile.h [deleted file]
src/cpu.c
src/csv.c
src/curl.c
src/curl_json.c
src/curl_xml.c
src/daemon/Makefile.am [new file with mode: 0644]
src/daemon/collectd.c [new file with mode: 0644]
src/daemon/collectd.h [new file with mode: 0644]
src/daemon/common.c [new file with mode: 0644]
src/daemon/common.h [new file with mode: 0644]
src/daemon/configfile.c [new file with mode: 0644]
src/daemon/configfile.h [new file with mode: 0644]
src/daemon/filter_chain.c [new file with mode: 0644]
src/daemon/filter_chain.h [new file with mode: 0644]
src/daemon/meta_data.c [new file with mode: 0644]
src/daemon/meta_data.h [new file with mode: 0644]
src/daemon/plugin.c [new file with mode: 0644]
src/daemon/plugin.h [new file with mode: 0644]
src/daemon/types_list.c [new file with mode: 0644]
src/daemon/types_list.h [new file with mode: 0644]
src/daemon/utils_avltree.c [new file with mode: 0644]
src/daemon/utils_avltree.h [new file with mode: 0644]
src/daemon/utils_cache.c [new file with mode: 0644]
src/daemon/utils_cache.h [new file with mode: 0644]
src/daemon/utils_complain.c [new file with mode: 0644]
src/daemon/utils_complain.h [new file with mode: 0644]
src/daemon/utils_heap.c [new file with mode: 0644]
src/daemon/utils_heap.h [new file with mode: 0644]
src/daemon/utils_llist.c [new file with mode: 0644]
src/daemon/utils_llist.h [new file with mode: 0644]
src/daemon/utils_match.c [new file with mode: 0644]
src/daemon/utils_match.h [new file with mode: 0644]
src/daemon/utils_random.c [new file with mode: 0644]
src/daemon/utils_random.h [new file with mode: 0644]
src/daemon/utils_subst.c [new file with mode: 0644]
src/daemon/utils_subst.h [new file with mode: 0644]
src/daemon/utils_tail.c [new file with mode: 0644]
src/daemon/utils_tail.h [new file with mode: 0644]
src/daemon/utils_tail_match.c [new file with mode: 0644]
src/daemon/utils_tail_match.h [new file with mode: 0644]
src/daemon/utils_threshold.c [new file with mode: 0644]
src/daemon/utils_threshold.h [new file with mode: 0644]
src/daemon/utils_time.c [new file with mode: 0644]
src/daemon/utils_time.h [new file with mode: 0644]
src/disk.c
src/dns.c
src/exec.c
src/filter_chain.c [deleted file]
src/filter_chain.h [deleted file]
src/java.c
src/libcollectdclient/Makefile.am
src/libcollectdclient/network_buffer.c
src/liboconfig/scanner.l
src/libvirt.c [deleted file]
src/load.c
src/lvm.c
src/memcachec.c
src/memory.c
src/meta_data.c [deleted file]
src/meta_data.h [deleted file]
src/mysql.c
src/network.c
src/ntpd.c
src/openvpn.c
src/plugin.c [deleted file]
src/plugin.h [deleted file]
src/processes.c
src/python.c
src/pyvalues.c
src/smart.c [new file with mode: 0644]
src/swap.c
src/target_set.c
src/tcpconns.c
src/tokyotyrant.c
src/types.db
src/types_list.c [deleted file]
src/types_list.h [deleted file]
src/users.c
src/utils_avltree.c [deleted file]
src/utils_avltree.h [deleted file]
src/utils_cache.c [deleted file]
src/utils_cache.h [deleted file]
src/utils_complain.c [deleted file]
src/utils_complain.h [deleted file]
src/utils_dns.c
src/utils_format_graphite.c
src/utils_format_json.c
src/utils_heap.c [deleted file]
src/utils_heap.h [deleted file]
src/utils_llist.c [deleted file]
src/utils_llist.h [deleted file]
src/utils_match.c [deleted file]
src/utils_match.h [deleted file]
src/utils_parse_option.c
src/utils_parse_option.h
src/utils_random.c [deleted file]
src/utils_random.h [deleted file]
src/utils_rrdcreate.c
src/utils_subst.c [deleted file]
src/utils_subst.h [deleted file]
src/utils_tail.c [deleted file]
src/utils_tail.h [deleted file]
src/utils_tail_match.c [deleted file]
src/utils_tail_match.h [deleted file]
src/utils_threshold.c [deleted file]
src/utils_threshold.h [deleted file]
src/utils_time.c [deleted file]
src/utils_time.h [deleted file]
src/virt.c [new file with mode: 0644]
src/write_graphite.c
src/write_http.c
src/write_kafka.c
src/write_riemann.c
src/write_tsdb.c

index 1929b28..f005edd 100644 (file)
@@ -82,3 +82,8 @@ src/tags
 # tests stuff
 src/tests/.deps/
 src/tests/mock/.deps/
+
+# new daemon repo
+src/daemon/.deps/
+src/daemon/.dirstamp
+src/daemon/collectd
diff --git a/AUTHORS b/AUTHORS
index 680ad07..2a76b5b 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -34,6 +34,9 @@ Amit Gupta <amit.gupta221 at gmail.com>
 Andreas Henriksson <andreas at fatal.se>
  - libmnl support in the netlink plugin.
 
+Andy Parkins <andyp at fussylogic.co.uk>
+ - battery plugin: sysfs code.
+
 Anthony Dewhurst <dewhurst at gmail.com>
  - zfs_arc plugin.
 
@@ -214,7 +217,7 @@ Piotr Hosowicz <the55 at wp.pl>
  - SMF manifest for collectd.
 
 Richard W. M. Jones <rjones at redhat.com>
- - libvirt plugin.
+ - virt plugin.
  - uuid plugin.
 
 Roman Klesel <roman.klesel at noris.de>
diff --git a/COPYING b/COPYING
index d511905..191af71 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -1,3 +1,35 @@
+collectd consists of a daemon and numerous plugins. The daemon is licensed
+under the "MIT License"; its source files are located at src/daemon/. The
+plugins are licenses individually, please check the top of the plugin's source
+file(s) to see which license applies. The majority of plugins is licensed
+either under the "MIT License" or the "GNU General Public License".
+
+The "MIT License" and "GNU General Public License" follow. Other licenses, not
+included in this file, should be considered "as published by the Open Source
+Initiative (OSI)".
+
+MIT License
+===========
+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.
+
+GNU General Public License (GPL)
+================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991
 
diff --git a/README b/README
index 90550af..76d2e4f 100644 (file)
--- a/README
+++ b/README
@@ -146,9 +146,6 @@ Features
       Detailed CPU statistics of the “Logical Partitions” virtualization
       technique built into IBM's POWER processors.
 
-    - libvirt
-      CPU, memory, disk and network I/O statistics from virtual machines.
-
     - lvm
       Size of “Logical Volumes” (LV) and “Volume Groups” (VG) of Linux'
       “Logical Volume Manager” (LVM).
@@ -299,6 +296,10 @@ Features
       to have its measurements fed to collectd. This includes multimeters,
       sound level meters, thermometers, and much more.
 
+    - smart
+      Collect SMART statistics, notably load cycle count, temperature
+      and bad sectors.
+
     - snmp
       Read values from SNMP (Simple Network Management Protocol) enabled
       network devices such as switches, routers, thermometers, rack monitoring
@@ -350,6 +351,9 @@ Features
     - varnish
       Various statistics from Varnish, an HTTP accelerator.
 
+    - virt
+      CPU, memory, disk and network I/O statistics from virtual machines.
+
     - vmem
       Virtual memory statistics, e. g. the number of page-ins/-outs or the
       number of pagefaults.
@@ -782,7 +786,7 @@ Prerequisites
 
   * libxml2 (optional)
     Parse XML data. This is needed for the `ascent', `bind', `curl_xml' and
-    `libvirt' plugins.
+    `virt' plugins.
     <http://xmlsoft.org/>
 
   * libxmms (optional)
index 8f1b991..ea0f2fa 100644 (file)
@@ -35,8 +35,6 @@ import java.net.InetAddress;
 import java.net.UnknownHostException;
 
 import javax.management.MBeanServerConnection;
-import javax.management.ObjectName;
-import javax.management.MalformedObjectNameException;
 
 import javax.management.remote.JMXServiceURL;
 import javax.management.remote.JMXConnector;
index 38df1c0..6ac2554 100644 (file)
@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 AC_INIT([collectd],[m4_esyscmd(./version-gen.sh)])
-AC_CONFIG_SRCDIR(src/collectd.c)
+AC_CONFIG_SRCDIR(src/)
 AC_CONFIG_HEADERS(src/config.h)
 AC_CONFIG_AUX_DIR([libltdl/config])
 
@@ -527,7 +527,7 @@ AC_CHECK_HEADERS(linux/un.h, [], [],
 #endif
 ])
 
-AC_CHECK_HEADERS(pwd.h grp.h sys/un.h ctype.h limits.h xfs/xqm.h fs_info.h fshelp.h paths.h mntent.h mnttab.h sys/fstyp.h sys/fs_types.h sys/mntent.h sys/mnttab.h sys/statfs.h sys/statvfs.h sys/vfs.h sys/vfstab.h kvm.h wordexp.h)
+AC_CHECK_HEADERS(pwd.h grp.h sys/un.h ctype.h limits.h xfs/xqm.h fs_info.h fshelp.h paths.h mntent.h mnttab.h sys/fstyp.h sys/fs_types.h sys/mntent.h sys/mnttab.h sys/statfs.h sys/statvfs.h sys/vfs.h sys/vfstab.h kvm.h wordexp.h locale.h)
 
 # For the dns plugin
 AC_CHECK_HEADERS(arpa/nameser.h)
@@ -602,7 +602,7 @@ AC_HEADER_TIME
 # Checks for library functions.
 #
 AC_PROG_GCC_TRADITIONAL
-AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf setenv if_indextoname)
+AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf setenv if_indextoname setlocale)
 
 AC_FUNC_STRERROR_R
 
@@ -809,7 +809,9 @@ if test "x$have_swapctl" = "xyes"; then
 #  undef _LARGEFILE64_SOURCE
 #endif
 #include <sys/stat.h>
+#include <sys/param.h>
 #include <sys/swap.h>
+#include <unistd.h>
 ]]],
 [[[
 int num = swapctl(0, NULL);
@@ -829,7 +831,9 @@ int num = swapctl(0, NULL);
 #  undef _LARGEFILE64_SOURCE
 #endif
 #include <sys/stat.h>
+#include <sys/param.h>
 #include <sys/swap.h>
+#include <unistd.h>
 ]]],
 [[[
 int num = swapctl(0, NULL, 0);
@@ -1311,7 +1315,7 @@ AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct
 #include <sys/user.h>
        ])
 
-AC_CHECK_MEMBERS([struct kinfo_proc.kp_proc, struct kinfo_proc.kp_eproc],
+AC_CHECK_MEMBERS([struct kinfo_proc.p_pid, struct kinfo_proc.p_vm_rssize],
        [
                AC_DEFINE(HAVE_STRUCT_KINFO_PROC_OPENBSD, 1,
                        [Define if struct kinfo_proc exists in the OpenBSD variant.])
@@ -1328,6 +1332,7 @@ AC_CHECK_MEMBERS([struct kinfo_proc.kp_proc, struct kinfo_proc.kp_eproc],
 
 AC_CHECK_MEMBERS([struct udphdr.uh_dport, struct udphdr.uh_sport], [], [],
 [#define _BSD_SOURCE
+#define _DEFAULT_SOURCE
 #if HAVE_STDINT_H
 # include <stdint.h>
 #endif
@@ -1349,6 +1354,7 @@ AC_CHECK_MEMBERS([struct udphdr.uh_dport, struct udphdr.uh_sport], [], [],
 ])
 AC_CHECK_MEMBERS([struct udphdr.dest, struct udphdr.source], [], [],
 [#define _BSD_SOURCE
+#define _DEFAULT_SOURCE
 #if HAVE_STDINT_H
 # include <stdint.h>
 #endif
@@ -1689,6 +1695,10 @@ then
                 [with_libcurl="yes"],
                 [with_libcurl="no (symbol 'curl_easy_init' not found)"],
                 [$with_curl_libs])
+               AC_CHECK_DECL(CURLOPT_USERNAME,
+                [have_curlopt_username="yes"],
+                [have_curlopt_username="no"],
+                [[#include <curl/curl.h>]])
        fi
 fi
 if test "x$with_libcurl" = "xyes"
@@ -1697,6 +1707,11 @@ then
        BUILD_WITH_LIBCURL_LIBS="$with_curl_libs"
        AC_SUBST(BUILD_WITH_LIBCURL_CFLAGS)
        AC_SUBST(BUILD_WITH_LIBCURL_LIBS)
+
+       if test "x$have_curlopt_username" = "xyes"
+       then
+               AC_DEFINE(HAVE_CURLOPT_USERNAME, 1, [Define if libcurl supports CURLOPT_USERNAME option.])
+       fi
 fi
 AM_CONDITIONAL(BUILD_WITH_LIBCURL, test "x$with_libcurl" = "xyes")
 # }}}
@@ -4120,6 +4135,68 @@ then
   LDFLAGS="$SAVE_LDFLAGS"
 fi
 
+if test "x$with_libstatgrab" = "xyes"
+then
+  SAVE_CFLAGS="$CFLAGS"
+  SAVE_LDFLAGS="$LDFLAGS"
+
+  CFLAGS="$CFLAGS $with_libstatgrab_cflags"
+  LDFLAGS="$LDFLAGS $with_libstatgrab_ldflags"
+
+  AC_CACHE_CHECK([if sg_init() requires an argument],
+          [c_cv_have_libstatgrab_init_arg],
+          AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <stdio.h>
+#include <statgrab.h>
+]]],
+[[[
+      if (sg_init()) return 0;
+]]]
+    )],
+    [c_cv_have_libstatgrab_init_arg="no"],
+    [c_cv_have_libstatgrab_init_arg="yes"]
+          )
+  )
+
+  AC_CACHE_CHECK([if sg_get_user_stats() requires an argument],
+          [c_cv_have_libstatgrab_get_user_stats_arg],
+          AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <stdio.h>
+#include <statgrab.h>
+]]],
+[[[
+      if (sg_get_user_stats()) return 0;
+]]]
+    )],
+    [c_cv_have_libstatgrab_get_user_stats_arg="no"],
+    [c_cv_have_libstatgrab_get_user_stats_arg="yes"]
+          )
+  )
+
+  AC_CACHE_CHECK([if sg_disk_io_stats() uses size_t],
+          [c_cv_have_libstatgrab_get_disk_io_stats_sizet],
+          AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[[
+#include <stdio.h>
+#include <statgrab.h>
+]]],
+[[[
+      sg_disk_io_stats *(*ptr) (size_t *);
+      ptr = sg_get_disk_io_stats;
+      if (ptr != NULL) return 0;
+]]]
+    )],
+    [c_cv_have_libstatgrab_get_disk_io_stats_sizet="yes"],
+    [c_cv_have_libstatgrab_get_disk_io_stats_sizet="no"]
+          )
+  )
+
+  CFLAGS="$SAVE_CFLAGS"
+  LDFLAGS="$SAVE_LDFLAGS"
+fi
+
 AM_CONDITIONAL(BUILD_WITH_LIBSTATGRAB, test "x$with_libstatgrab" = "xyes")
 if test "x$with_libstatgrab" = "xyes"
 then
@@ -4128,6 +4205,18 @@ then
   BUILD_WITH_LIBSTATGRAB_LDFLAGS="$with_libstatgrab_ldflags"
   AC_SUBST(BUILD_WITH_LIBSTATGRAB_CFLAGS)
   AC_SUBST(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
+  if test "x$c_cv_have_libstatgrab_init_arg" = "xyes"
+  then
+        AC_DEFINE(HAVE_LIBSTATGRAB_INIT_ARG, 1, [Define to 1 if sg_init does require an argument])
+  fi
+  if test "x$c_cv_have_libstatgrab_get_user_stats_arg" = "xyes"
+  then
+        AC_DEFINE(HAVE_LIBSTATGRAB_GET_USER_STATS_ARG, 1, [Define to 1 if sg_get_user_stats does require an argument])
+  fi
+  if test "x$c_cv_have_libstatgrab_get_disk_io_stats_sizet" = "xyes"
+  then
+        AC_DEFINE(HAVE_LIBSTATGRAB_GET_DISK_IO_STATS_SIZET, 1, [Define to 1 if sg_get_disk_io_stats does require pointer to size_t as an argument])
+  fi
 fi
 # }}}
 
@@ -4866,6 +4955,62 @@ then
 fi
 # }}}
 
+# --with-libatasmart {{{
+with_libatasmart_cppflags=""
+with_libatasmart_ldflags=""
+AC_ARG_WITH(libatasmart, [AS_HELP_STRING([--with-libatasmart@<:@=PREFIX@:>@], [Path to libatasmart.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               with_libatasmart_cppflags="-I$withval/include"
+               with_libatasmart_ldflags="-L$withval/lib"
+               with_libatasmart="yes"
+       else
+               with_libatasmart="$withval"
+       fi
+],
+[
+       if test "x$ac_system" = "xLinux"
+       then
+               with_libatasmart="yes"
+       else
+               with_libatasmart="no (Linux only library)"
+       fi
+])
+if test "x$with_libatasmart" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libatasmart_cppflags"
+
+       AC_CHECK_HEADERS(atasmart.h, [with_libatasmart="yes"], [with_libatasmart="no (atasmart.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libatasmart" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libatasmart_cppflags"
+       LDFLAGS="$LDFLAGS $with_libatasmart_ldflags"
+
+       AC_CHECK_LIB(atasmart, sk_disk_open, [with_libatasmart="yes"], [with_libatasmart="no (Symbol 'sk_disk_open' not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libatasmart" = "xyes"
+then
+       BUILD_WITH_LIBATASMART_CPPFLAGS="$with_libatasmart_cppflags"
+       BUILD_WITH_LIBATASMART_LDFLAGS="$with_libatasmart_ldflags"
+       BUILD_WITH_LIBATASMART_LIBS="-latasmart"
+       AC_SUBST(BUILD_WITH_LIBATASMART_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBATASMART_LDFLAGS)
+       AC_SUBST(BUILD_WITH_LIBATASMART_LIBS)
+       AC_DEFINE(HAVE_LIBATASMART, 1, [Define if libatasmart is present and usable.])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBATASMART, test "x$with_libatasmart" = "xyes")
+# }}}
+
 PKG_CHECK_MODULES([LIBNOTIFY], [libnotify],
                [with_libnotify="yes"],
                [if test "x$LIBNOTIFY_PKG_ERRORS" = "x"; then
@@ -5021,7 +5166,6 @@ plugin_interface="no"
 plugin_ipmi="no"
 plugin_ipvs="no"
 plugin_irq="no"
-plugin_libvirt="no"
 plugin_load="no"
 plugin_log_logstash="no"
 plugin_memory="no"
@@ -5037,8 +5181,9 @@ plugin_tape="no"
 plugin_tcpconns="no"
 plugin_ted="no"
 plugin_thermal="no"
-plugin_users="no"
 plugin_uptime="no"
+plugin_users="no"
+plugin_virt="no"
 plugin_vmem="no"
 plugin_vserver="no"
 plugin_wireless="no"
@@ -5263,11 +5408,6 @@ then
        plugin_interface="yes"
 fi
 
-if test "x$with_libxml2" = "xyes" && test "x$with_libvirt" = "xyes"
-then
-       plugin_libvirt="yes"
-fi
-
 if test "x$have_getloadavg" = "xyes"
 then
        plugin_load="yes"
@@ -5308,6 +5448,11 @@ then
        plugin_processes="yes"
 fi
 
+if test "x$with_kvm_getprocs" = "xyes" && test "x$have_struct_kinfo_proc_openbsd" = "xyes"
+then
+       plugin_processes="yes"
+fi
+
 if test "x$with_kvm_getswapinfo" = "xyes"
 then
        plugin_swap="yes"
@@ -5332,6 +5477,12 @@ then
        plugin_users="yes"
 fi
 
+if test "x$with_libxml2" = "xyes" && test "x$with_libvirt" = "xyes"
+then
+       plugin_virt="yes"
+fi
+
+
 m4_divert_once([HELP_ENABLE], [
 collectd plugins:])
 
@@ -5390,7 +5541,6 @@ AC_PLUGIN([iptables],    [$with_libiptc],      [IPTables rule counters])
 AC_PLUGIN([ipvs],        [$plugin_ipvs],       [IPVS connection statistics])
 AC_PLUGIN([irq],         [$plugin_irq],        [IRQ statistics])
 AC_PLUGIN([java],        [$with_java],         [Embed the Java Virtual Machine])
-AC_PLUGIN([libvirt],     [$plugin_libvirt],    [Virtual machine statistics])
 AC_PLUGIN([load],        [$plugin_load],       [System load])
 AC_PLUGIN([logfile],     [yes],                [File logging plugin])
 AC_PLUGIN([log_logstash], [$plugin_log_logstash], [Logstash json_event compatible logging])
@@ -5443,6 +5593,7 @@ AC_PLUGIN([rrdtool],     [$with_librrd],       [RRDTool output plugin])
 AC_PLUGIN([sensors],     [$with_libsensors],   [lm_sensors statistics])
 AC_PLUGIN([serial],      [$plugin_serial],     [serial port traffic])
 AC_PLUGIN([sigrok],      [$with_libsigrok],    [sigrok acquisition sources])
+AC_PLUGIN([smart],       [$with_libatasmart],  [SMART statistics])
 AC_PLUGIN([snmp],        [$with_libnetsnmp],   [SNMP querying plugin])
 AC_PLUGIN([statsd],      [yes],                [StatsD plugin])
 AC_PLUGIN([swap],        [$plugin_swap],       [Swap usage statistics])
@@ -5467,6 +5618,7 @@ AC_PLUGIN([uptime],      [$plugin_uptime],     [Uptime statistics])
 AC_PLUGIN([users],       [$plugin_users],      [User statistics])
 AC_PLUGIN([uuid],        [yes],                [UUID as hostname plugin])
 AC_PLUGIN([varnish],     [$with_libvarnish],   [Varnish cache statistics])
+AC_PLUGIN([virt],        [$plugin_virt],       [Virtual machine statistics])
 AC_PLUGIN([vmem],        [$plugin_vmem],       [Virtual memory statistics])
 AC_PLUGIN([vserver],     [$plugin_vserver],    [Linux VServer statistics])
 AC_PLUGIN([wireless],    [$plugin_wireless],   [Wireless statistics])
@@ -5629,7 +5781,7 @@ AC_SUBST(LCC_VERSION_STRING)
 
 AC_CONFIG_FILES(src/libcollectdclient/collectd/lcc_features.h)
 
-AC_CONFIG_FILES([Makefile src/Makefile src/collectd.conf src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile bindings/java/Makefile])
+AC_CONFIG_FILES([Makefile src/Makefile src/daemon/Makefile src/collectd.conf src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile bindings/java/Makefile])
 AC_OUTPUT
 
 if test "x$with_librrd" = "xyes" \
@@ -5662,6 +5814,7 @@ Configuration:
   Libraries:
     intel mic . . . . . . $with_mic
     libaquaero5 . . . . . $with_libaquaero5
+    libatasmart . . . . . $with_libatasmart
     libcurl . . . . . . . $with_libcurl
     libdbi  . . . . . . . $with_libdbi
     libcredis . . . . . . $with_libcredis
@@ -5757,7 +5910,6 @@ Configuration:
     ipvs  . . . . . . . . $enable_ipvs
     irq . . . . . . . . . $enable_irq
     java  . . . . . . . . $enable_java
-    libvirt . . . . . . . $enable_libvirt
     load  . . . . . . . . $enable_load
     logfile . . . . . . . $enable_logfile
     lpar  . . . . . . . . $enable_lpar
@@ -5809,6 +5961,7 @@ Configuration:
     sensors . . . . . . . $enable_sensors
     serial  . . . . . . . $enable_serial
     sigrok  . . . . . . . $enable_sigrok
+    smart . . . . . . . . $enable_smart
     snmp  . . . . . . . . $enable_snmp
     statsd  . . . . . . . $enable_statsd
     swap  . . . . . . . . $enable_swap
@@ -5833,6 +5986,7 @@ Configuration:
     users . . . . . . . . $enable_users
     uuid  . . . . . . . . $enable_uuid
     varnish . . . . . . . $enable_varnish
+    virt  . . . . . . . . $enable_virt
     vmem  . . . . . . . . $enable_vmem
     vserver . . . . . . . $enable_vserver
     wireless  . . . . . . $enable_wireless
index 3322059..1930592 100644 (file)
@@ -1,7 +1,7 @@
 #
 # q: What is this ?
 # a: A specfile for building RPM packages of current collectd releases, for
-#    RHEL/CentOS versions 5 and 6. By default all the plugins which are
+#    RHEL/CentOS versions 5, 6 and 7. By default all the plugins which are
 #    buildable based on the libraries available in the distribution + the
 #    EPEL repository, will be built. Plugins depending on external libs will
 #    be packaged in separate RPMs.
 %{?el6:%global _has_working_libiptc 1}
 %{?el6:%global _has_ip_vs_h 1}
 %{?el6:%global _has_lvm2app_h 1}
-%{?el6:%global _has_perl_extutils_embed 1}
 %{?el6:%global _has_libmodbus 1}
+%{?el6:%global _has_libudev 1}
+%{?el6:%global _has_iproute 1}
+
+%{?el7:%global _has_libyajl 1}
+%{?el7:%global _has_recent_libpcap 1}
+%{?el7:%global _has_recent_sockios_h 1}
+%{?el7:%global _has_working_libiptc 1}
+%{?el7:%global _has_ip_vs_h 1}
+%{?el7:%global _has_lvm2app_h 1}
+%{?el7:%global _has_libudev 1}
+%{?el7:%global _has_recent_librrd 1}
+%{?el7:%global _has_varnish4 1}
+%{?el7:%global _has_broken_libmemcached 1}
+%{?el7:%global _has_iproute 1}
 
 # plugins enabled by default
 %define with_aggregation 0%{!?_without_aggregation:1}
@@ -68,6 +81,7 @@
 %define with_df 0%{!?_without_df:1}
 %define with_disk 0%{!?_without_disk:1}
 %define with_dns 0%{!?_without_dns:0%{?_has_recent_libpcap}}
+%define with_drbd 0%{!?_without_drbd:1}
 %define with_email 0%{!?_without_email:1}
 %define with_entropy 0%{!?_without_entropy:1}
 %define with_ethstat 0%{!?_without_ethstat:0%{?_has_recent_sockios_h}}
 %define with_ipvs 0%{!?_without_ipvs:0%{?_has_ip_vs_h}}
 %define with_irq 0%{!?_without_irq:1}
 %define with_java 0%{!?_without_java:1}
-%define with_libvirt 0%{!?_without_libvirt:1}
+%define with_virt 0%{!?_without_virt:1}
 %define with_load 0%{!?_without_load:1}
 %define with_logfile 0%{!?_without_logfile:1}
+%define with_log_logstash 0%{!?_without_log_logstash:0%{?_has_libyajl}}
 %define with_lvm 0%{!?_without_lvm:0%{?_has_lvm2app_h}}
 %define with_madwifi 0%{!?_without_madwifi:1}
 %define with_mbmon 0%{!?_without_mbmon:1}
 %define with_md 0%{!?_without_md:1}
-%define with_memcachec 0%{!?_without_memcachec:1}
+%define with_memcachec 0%{!?_without_memcachec:0%{!?_has_broken_libmemcached:1}}
 %define with_memcached 0%{!?_without_memcached:1}
 %define with_memory 0%{!?_without_memory:1}
 %define with_multimeter 0%{!?_without_multimeter:1}
 %define with_modbus 0%{!?_without_modbus:0%{?_has_libmodbus}}
 %define with_mysql 0%{!?_without_mysql:1}
-%define with_netlink 0%{!?_without_netlink:1}
+%define with_netlink 0%{!?_without_netlink:0%{?_has_iproute}}
 %define with_network 0%{!?_without_network:1}
 %define with_nfs 0%{!?_without_nfs:1}
 %define with_nginx 0%{!?_without_nginx:1}
 %define with_nut 0%{!?_without_nut:1}
 %define with_olsrd 0%{!?_without_olsrd:1}
 %define with_openvpn 0%{!?_without_openvpn:1}
-%define with_perl 0%{!?_without_perl:0%{?_has_perl_extutils_embed}}
+%define with_perl 0%{!?_without_perl:1}
 %define with_pinba 0%{!?_without_pinba:1}
 %define with_ping 0%{!?_without_ping:1}
 %define with_postgresql 0%{!?_without_postgresql:1}
 %define with_processes 0%{!?_without_processes:1}
 %define with_protocols 0%{!?_without_protocols:1}
 %define with_python 0%{!?_without_python:1}
+%define with_rrdcached 0%{!?_without_rrdcached:0%{?_has_recent_librrd}}
 %define with_rrdtool 0%{!?_without_rrdtool:1}
 %define with_sensors 0%{!?_without_sensors:1}
 %define with_serial 0%{!?_without_serial:1}
 %define with_uptime 0%{!?_without_uptime:1}
 %define with_users 0%{!?_without_users:1}
 %define with_uuid 0%{!?_without_uuid:1}
-%define with_varnish 0%{!?_without_varnish:1}
+%define with_varnish 0%{!?_without_varnish:0%{!?_has_varnish4:1}}
 %define with_vmem 0%{!?_without_vmem:1}
 %define with_vserver 0%{!?_without_vserver:1}
 %define with_wireless 0%{!?_without_wireless:1}
 %define with_write_graphite 0%{!?_without_write_graphite:1}
 %define with_write_http 0%{!?_without_write_http:1}
 %define with_write_riemann 0%{!?_without_write_riemann:1}
+%define with_write_tsdb 0%{!?_without_write_tsdb:1}
 %define with_zfs_arc 0%{!?_without_zfs_arc:1}
 
 # Plugins not built by default because of dependencies on libraries not
 %define with_apple_sensors 0%{!?_without_apple_sensors:0}
 # plugin aquaero disabled, requires a libaquaero5
 %define with_aquaero 0%{!?_without_aquaero:0}
+# plugin barometer disabled, requires a libi2c
+%define with_barometer 0%{!?_without_barometer:0}
 # plugin lpar disabled, requires AIX
 %define with_lpar 0%{!?_without_lpar:0}
 # plugin mic disabled, requires Mic
 %define with_mic 0%{!?_without_mic:0}
-# plugin modbus disabled, requires libmodbus
-%define with_modbus 0%{!?_without_modbus:0}
 # plugin netapp disabled, requires libnetapp
 %define with_netapp 0%{!?_without_netapp:0}
 # plugin onewire disabled, requires libowfs
 %define with_redis 0%{!?_without_redis:0}
 # plugin routeros disabled, requires librouteros
 %define with_routeros 0%{!?_without_routeros:0}
-# plugin rrdcached disabled, requires rrdtool >= 1.4
-%define with_rrdcached 0%{!?_without_rrdcached:0}
 # plugin sigrok disabled, requires libsigrok
 %define with_sigrok 0%{!?_without_sigrok:0}
 # plugin tape disabled, requires libkstat
 %define with_tape 0%{!?_without_tape:0}
 # plugin tokyotyrant disabled, requires tcrdb.h
 %define with_tokyotyrant 0%{!?_without_tokyotyrant:0}
+# plugin write_kafka disabled, requires librdkafka
+%define with_write_kafka 0%{!?_without_write_kafka:0}
 # plugin write_mongodb disabled, requires libmongoc
 %define with_write_mongodb 0%{!?_without_write_mongodb:0}
 # plugin write_redis disabled, requires credis
@@ -248,6 +265,15 @@ open-source server software for the game World of Warcraft by Blizzard
 Entertainment.
 %endif
 
+%if %{with_barometer}
+%package barometer
+Summary:       barometer plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+%description barometer
+Collects pressure and temperature from digital barometers.
+%endif
+
 %if %{with_bind}
 %package bind
 Summary:       Bind plugin for collectd
@@ -303,6 +329,17 @@ The DBI plugin uses libdbi, a database abstraction library, to execute SQL
 statements on a database and read back the result.
 %endif
 
+%if %{with_disk}
+%package disk
+Summary:       disk plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+%{?_has_libudev:BuildRequires:  libudev-devel}
+%description disk
+The "disk" plugin collects information about the usage of physical disks and
+logical disks (partitions).
+%endif
+
 %if %{with_dns}
 %package dns
 Summary:       DNS plugin for collectd
@@ -380,14 +417,14 @@ This plugin for collectd allows plugins to be written in Java and executed
 in an embedded JVM.
 %endif
 
-%if %{with_libvirt}
-%package libvirt
-Summary:       Libvirt plugin for collectd
-Group:         System Environment/Daemons
-Requires:      %{name}%{?_isa} = %{version}-%{release}
-BuildRequires: libvirt-devel
-%description libvirt
-This plugin collects information from virtualized guests.
+%if %{with_log_logstash}
+%package log_logstash
+Summary:       log_logstash plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: yajl-devel
+%description log_logstash
+This plugin logs in logstash JSON format
 %endif
 
 %if %{with_lvm}
@@ -449,10 +486,9 @@ handlers and database traffic.
 Summary:       netlink plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
-BuildRequires: libmnl-devel
+BuildRequires: libmnl-devel, iproute-devel
 %description netlink
-This plugin collects very detailed Linux network interface and routing
-statistics.
+The netlink plugin collects detailed network interface and routing statistics.
 %endif
 
 %if %{with_nginx}
@@ -503,7 +539,11 @@ Summary:   Perl plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
 Requires:      perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
+%if 0%{?rhel} >= 6
 BuildRequires: perl-ExtUtils-Embed
+%else
+BuildRequires: perl
+%endif
 %description perl
 The Perl plugin embeds a Perl interpreter into collectd and exposes the
 application programming interface (API) to Perl-scripts.
@@ -630,6 +670,16 @@ BuildRequires:     varnish-libs-devel
 The Varnish plugin collects information about Varnish, an HTTP accelerator.
 %endif
 
+%if %{with_virt}
+%package virt
+Summary:       Virt plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libvirt-devel
+%description virt
+This plugin collects information from virtualized guests.
+%endif
+
 %if %{with_write_http}
 %package write_http
 Summary:       Write-HTTP plugin for collectd
@@ -641,6 +691,16 @@ The Write-HTTP plugin sends the values collected by collectd to a web-server
 using HTTP POST requests.
 %endif
 
+%if %{with_write_kafka}
+%package write_kafka
+Summary:       Write-kafka plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: rdkafka-devel
+%description write_kafka
+The write_kafka plugin sends values to kafka, a distributed messaging system.
+%endif
+
 %if %{with_write_redis}
 %package write_redis
 Summary:       Write-Redis plugin for collectd
@@ -750,6 +810,12 @@ Development files for libcollectdclient
 %define _with_ascent --disable-ascent
 %endif
 
+%if %{with_barometer}
+%define _with_barometer --enable-barometer
+%else
+%define _with_barometer --disable-barometer
+%endif
+
 %if %{with_battery}
 %define _with_battery --enable-battery
 %else
@@ -840,6 +906,12 @@ Development files for libcollectdclient
 %define _with_dns --disable-dns
 %endif
 
+%if %{with_drbd}
+%define _with_drbd --enable-drbd
+%else
+%define _with_drbd --disable-drbd
+%endif
+
 %if %{with_email}
 %define _with_email --enable-email
 %else
@@ -924,10 +996,10 @@ Development files for libcollectdclient
 %define _with_java --disable-java
 %endif
 
-%if %{with_libvirt}
-%define _with_libvirt --enable-libvirt
+%if %{with_virt}
+%define _with_virt --enable-virt
 %else
-%define _with_libvirt --disable-libvirt
+%define _with_virt --disable-virt
 %endif
 
 %if %{with_load}
@@ -942,6 +1014,12 @@ Development files for libcollectdclient
 %define _with_logfile --disable-logfile
 %endif
 
+%if %{with_log_logstash}
+%define _with_log_logstash --enable-log_logstash
+%else
+%define _with_log_logstash --disable-log_logstash
+%endif
+
 %if %{with_lpar}
 %define _with_lpar --enable-lpar
 %else
@@ -1008,12 +1086,6 @@ Development files for libcollectdclient
 %define _with_multimeter --disable-multimeter
 %endif
 
-%if %{with_modbus}
-%define _with_modbus --enable-modbus
-%else
-%define _with_modbus --disable-modbus
-%endif
-
 %if %{with_mysql}
 %define _with_mysql --enable-mysql
 %else
@@ -1348,6 +1420,12 @@ Development files for libcollectdclient
 %define _with_write_http --disable-write_http
 %endif
 
+%if %{with_write_kafka}
+%define _with_write_kafka --enable-write_kafka
+%else
+%define _with_write_kafka --disable-write_kafka
+%endif
+
 %if %{with_write_mongodb}
 %define _with_write_mongodb --enable-write_mongodb
 %else
@@ -1366,6 +1444,12 @@ Development files for libcollectdclient
 %define _with_write_riemann --disable-write_riemann
 %endif
 
+%if %{with_write_tsdb}
+%define _with_write_tsdb --enable-write_tsdb
+%else
+%define _with_write_tsdb --disable-write_tsdb
+%endif
+
 %if %{with_xmms}
 %define _with_xmms --enable-xmms
 %else
@@ -1399,6 +1483,7 @@ Development files for libcollectdclient
        %{?_with_apple_sensors} \
        %{?_with_aquaero} \
        %{?_with_ascent} \
+       %{?_with_barometer} \
        %{?_with_battery} \
        %{?_with_bind} \
        %{?_with_cgroups} \
@@ -1414,6 +1499,7 @@ Development files for libcollectdclient
        %{?_with_df} \
        %{?_with_disk} \
        %{?_with_dns} \
+       %{?_with_drbd} \
        %{?_with_email} \
        %{?_with_entropy} \
        %{?_with_ethstat} \
@@ -1427,7 +1513,8 @@ Development files for libcollectdclient
        %{?_with_iptables} \
        %{?_with_ipvs} \
        %{?_with_java} \
-       %{?_with_libvirt} \
+       %{?_with_virt} \
+       %{?_with_log_logstash} \
        %{?_with_lpar} \
        %{?_with_lvm} \
        %{?_with_memcachec} \
@@ -1460,6 +1547,7 @@ Development files for libcollectdclient
        %{?_with_tokyotyrant} \
        %{?_with_varnish} \
        %{?_with_write_http} \
+       %{?_with_write_kafka} \
        %{?_with_write_mongodb} \
        %{?_with_write_redis} \
        %{?_with_xmms} \
@@ -1502,7 +1590,8 @@ Development files for libcollectdclient
        %{?_with_wireless}\
        %{?_with_write_graphite} \
        %{?_with_write_http} \
-       %{?_with_write_riemann}
+       %{?_with_write_riemann} \
+       %{?_with_write_tsdb}
 
 
 %{__make} %{?_smp_mflags}
@@ -1645,8 +1734,8 @@ fi
 %if %{with_df}
 %{_libdir}/%{name}/df.so
 %endif
-%if %{with_disk}
-%{_libdir}/%{name}/disk.so
+%if %{with_drbd}
+%{_libdir}/%{name}/drbd.so
 %endif
 %if %{with_ethstat}
 %{_libdir}/%{name}/ethstat.so
@@ -1783,7 +1872,12 @@ fi
 %if %{with_write_graphite}
 %{_libdir}/%{name}/write_graphite.so
 %endif
-
+%if %{with_write_tsdb}
+%{_libdir}/%{name}/write_tsdb.so
+%endif
+%if %{with_zfs_arc}
+%{_libdir}/%{name}/zfs_arc.so
+%endif
 
 %files -n libcollectdclient-devel
 %{_includedir}/collectd/client.h
@@ -1816,6 +1910,11 @@ fi
 %{_libdir}/%{name}/ascent.so
 %endif
 
+%if %{with_barometer}
+%files barometer
+%{_libdir}/%{name}/barometer.so
+%endif
+
 %if %{with_bind}
 %files bind
 %{_libdir}/%{name}/bind.so
@@ -1836,6 +1935,11 @@ fi
 %{_libdir}/%{name}/curl_xml.so
 %endif
 
+%if %{with_disk}
+%files disk
+%{_libdir}/%{name}/disk.so
+%endif
+
 %if %{with_dns}
 %files dns
 %{_libdir}/%{name}/dns.so
@@ -1879,9 +1983,14 @@ fi
 %{_mandir}/man5/collectd-java.5*
 %endif
 
-%if %{with_libvirt}
-%files libvirt
-%{_libdir}/%{name}/libvirt.so
+%if %{with_virt}
+%files virt
+%{_libdir}/%{name}/virt.so
+%endif
+
+%if %{with_log_logstash}
+%files log_logstash
+%{_libdir}/%{name}/log_logstash.so
 %endif
 
 %if %{with_lvm}
@@ -1899,6 +2008,11 @@ fi
 %{_libdir}/%{name}/mic.so
 %endif
 
+%if %{with_modbus}
+%files modbus
+%{_libdir}/%{name}/modbus.so
+%endif
+
 %if %{with_mysql}
 %files mysql
 %{_libdir}/%{name}/mysql.so
@@ -2002,6 +2116,11 @@ fi
 %{_libdir}/%{name}/write_http.so
 %endif
 
+%if %{with_write_kafka}
+%files write_kafka
+%{_libdir}/%{name}/write_kafka.so
+%endif
+
 %if %{with_write_redis}
 %files write_redis
 %{_libdir}/%{name}/write_redis.so
@@ -2024,15 +2143,19 @@ fi
 %doc contrib/
 
 %changelog
+# * TODO 5.5.0-1
+# - New upstream version
+# - New plugins enabled by default: drbd, log_logstash, write_tsdb
+# - New plugins disabled by default: barometer, write_kafka
+# - Enable zfs_arc, now supported on Linux
+# - Install disk plugin in an dedicated package, as it depends on libudev
+
 * Mon Aug 19 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.4.0-1
 - New upstream version
 - Build netlink plugin by default
 - Enable cgroups, lvm and statsd plugins
 - Enable (but don't build by default) mic, aquaero and sigrok plugins
 
-* Wed Aug 06 2014 Marc Fournier <marc.fournier@camptocamp.com> 5.3.1-2
-- Enabled modbus plugin
-
 * Tue Aug 06 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.3.1-1
 - New upstream version
 - Added RHEL5 support:
@@ -2042,6 +2165,10 @@ fi
 - Removed duplicate --enable-aggregation
 - Added some comments & usage examples
 - Replaced a couple of "Buildrequires" by "BuildRequires"
+- Enabled modbus plugin on RHEL6
+- Enabled netlink plugin on RHEL6 and RHEL7
+- Allow perl plugin to build on RHEL5
+- Add support for RHEL7
 
 * Wed Apr 10 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.3.0-1
 - New upstream version
@@ -2049,7 +2176,7 @@ fi
 - Enabled tail_csv plugin
 - Installed collectd-tc manpage
 
-* Thu Jan 11 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.2.0-3
+* Fri Jan 11 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.2.0-3
 - remove dependency on libstatgrab, which isn't required on linux
 
 * Thu Jan 03 2013 Marc Fournier <marc.fournier@camptocamp.com> 5.2.0-2
@@ -2097,11 +2224,11 @@ fi
   non-essential stuff.
 - Replaced BuildPrereq by BuildRequires
 
-* Tue Jan 03 2011 Monetate <jason.stelzer@monetate.com> 5.0.1
+* Mon Jan 03 2011 Monetate <jason.stelzer@monetate.com> 5.0.1
 - New upstream version
 - Changes to support 5.0.1
 
-* Tue Jan 04 2010 Rackspace <stu.hood@rackspace.com> 4.9.0
+* Mon Jan 04 2010 Rackspace <stu.hood@rackspace.com> 4.9.0
 - New upstream version
 - Changes to support 4.9.0
 - Added support for Java/GenericJMX plugin
@@ -2119,7 +2246,7 @@ fi
 - New major releas
 - Changes to support 4.0.5
 
-* Wed Jan 11 2007 Iain Lea <iain@bricbrac.de> 3.11.0-0
+* Thu Jan 11 2007 Iain Lea <iain@bricbrac.de> 3.11.0-0
 - fixed spec file to build correctly on fedora core
 - added improved init.d script to work with chkconfig
 - added %%post and %%postun to call chkconfig automatically
@@ -2127,10 +2254,10 @@ fi
 * Sun Jul 09 2006 Florian octo Forster <octo@verplant.org> 3.10.0-1
 - New upstream version
 
-* Tue Jun 25 2006 Florian octo Forster <octo@verplant.org> 3.9.4-1
+* Sun Jun 25 2006 Florian octo Forster <octo@verplant.org> 3.9.4-1
 - New upstream version
 
-* Tue Jun 01 2006 Florian octo Forster <octo@verplant.org> 3.9.3-1
+* Thu Jun 01 2006 Florian octo Forster <octo@verplant.org> 3.9.3-1
 - New upstream version
 
 * Tue May 09 2006 Florian octo Forster <octo@verplant.org> 3.9.2-1
@@ -2146,10 +2273,10 @@ fi
 - New upstream version
 - Added the `apache' package.
 
-* Thu Mar 14 2006 Florian octo Forster <octo@verplant.org> 3.8.2-1
+* Tue Mar 14 2006 Florian octo Forster <octo@verplant.org> 3.8.2-1
 - New upstream version
 
-* Thu Mar 13 2006 Florian octo Forster <octo@verplant.org> 3.8.1-1
+* Mon Mar 13 2006 Florian octo Forster <octo@verplant.org> 3.8.1-1
 - New upstream version
 
 * Thu Mar 09 2006 Florian octo Forster <octo@verplant.org> 3.8.0-1
@@ -2186,7 +2313,7 @@ fi
 * Sat Nov 05 2005 Florian octo Forster <octo@verplant.org> 3.3.0-1
 - New upstream version
 
-* Tue Oct 26 2005 Florian octo Forster <octo@verplant.org> 3.2.0-1
+* Wed Oct 26 2005 Florian octo Forster <octo@verplant.org> 3.2.0-1
 - New upstream version
 - Added statement to remove the `*.la' files. This fixes a problem when
   `Unpackaged files terminate build' is in effect.
@@ -2203,13 +2330,13 @@ fi
 * Fri Sep 16 2005 Florian octo Forster <octo@verplant.org> 2.1.0-1
 - New upstream version
 
-* Mon Sep 10 2005 Florian octo Forster <octo@verplant.org> 2.0.0-1
+* Sat Sep 10 2005 Florian octo Forster <octo@verplant.org> 2.0.0-1
 - New upstream version
 
 * Mon Aug 29 2005 Florian octo Forster <octo@verplant.org> 1.8.0-1
 - New upstream version
 
-* Sun Aug 25 2005 Florian octo Forster <octo@verplant.org> 1.7.0-1
+* Thu Aug 25 2005 Florian octo Forster <octo@verplant.org> 1.7.0-1
 - New upstream version
 
 * Sun Aug 21 2005 Florian octo Forster <octo@verplant.org> 1.6.0-1
index 315d195..04c77a3 100644 (file)
@@ -2,12 +2,16 @@ SUBDIRS = libcollectdclient
 if BUILD_WITH_OWN_LIBOCONFIG
 SUBDIRS += liboconfig
 endif
+SUBDIRS += daemon
+
+PLUGIN_LDFLAGS = -module -avoid-version -export-symbols-regex '\<module_register\>'
 
 if COMPILER_IS_GCC
 AM_CFLAGS = -Wall -Werror
 endif
 
-AM_CPPFLAGS = -DPREFIX='"${prefix}"'
+AM_CPPFLAGS = -I$(srcdir)/daemon
+AM_CPPFLAGS += -DPREFIX='"${prefix}"'
 AM_CPPFLAGS += -DCONFIGFILE='"${sysconfdir}/${PACKAGE_NAME}.conf"'
 AM_CPPFLAGS += -DLOCALSTATEDIR='"${localstatedir}"'
 AM_CPPFLAGS += -DPKGLOCALSTATEDIR='"${localstatedir}/lib/${PACKAGE_NAME}"'
@@ -19,83 +23,14 @@ AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"'
 
 AUTOMAKE_OPTIONS = subdir-objects
 
-sbin_PROGRAMS = collectd collectdmon
+sbin_PROGRAMS = collectdmon
 bin_PROGRAMS = collectd-nagios collectdctl collectd-tg
 
-collectd_SOURCES = collectd.c collectd.h \
-                  common.c common.h \
-                  configfile.c configfile.h \
-                  filter_chain.c filter_chain.h \
-                  meta_data.c meta_data.h \
-                  plugin.c plugin.h \
-                  utils_avltree.c utils_avltree.h \
-                  utils_cache.c utils_cache.h \
-                  utils_complain.c utils_complain.h \
-                  utils_heap.c utils_heap.h \
-                  utils_ignorelist.c utils_ignorelist.h \
-                  utils_llist.c utils_llist.h \
-                  utils_parse_option.c utils_parse_option.h \
-                  utils_random.c utils_random.h \
-                  utils_tail_match.c utils_tail_match.h \
-                  utils_match.c utils_match.h \
-                  utils_subst.c utils_subst.h \
-                  utils_tail.c utils_tail.h \
-                  utils_time.c utils_time.h \
-                  types_list.c types_list.h \
-                  utils_threshold.c utils_threshold.h
-
-
-collectd_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL)
-collectd_CFLAGS = $(AM_CFLAGS)
-collectd_LDFLAGS = -export-dynamic
-collectd_LDADD = -lm
-collectd_DEPENDENCIES =
-
-# Link to these libraries..
-if BUILD_WITH_LIBRT
-collectd_LDADD += -lrt
-endif
-if BUILD_WITH_LIBPOSIX4
-collectd_LDADD += -lposix4
-endif
-if BUILD_WITH_LIBSOCKET
-collectd_LDADD += -lsocket
-endif
-if BUILD_WITH_LIBRESOLV
-collectd_LDADD += -lresolv
-endif
-if BUILD_WITH_LIBPTHREAD
-collectd_LDADD += -lpthread
-endif
-if BUILD_WITH_LIBKSTAT
-collectd_LDADD += -lkstat
-endif
-if BUILD_WITH_LIBDEVINFO
-collectd_LDADD += -ldevinfo
-endif
-if BUILD_AIX
-collectd_LDFLAGS += -Wl,-bexpall,-brtllib
-endif
-
-# The daemon needs to call sg_init, so we need to link it against libstatgrab,
-# too. -octo
-if BUILD_WITH_LIBSTATGRAB
-collectd_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
-collectd_LDADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
-endif
-
-if BUILD_WITH_OWN_LIBOCONFIG
-collectd_LDADD += $(LIBLTDL) liboconfig/liboconfig.la
-collectd_DEPENDENCIES += liboconfig/liboconfig.la
-else
-collectd_LDADD += -loconfig
-endif
-
 collectdmon_SOURCES = collectdmon.c
 collectdmon_CPPFLAGS = $(AM_CPPFLAGS)
 
 collectd_nagios_SOURCES = collectd-nagios.c
-collectd_nagios_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/libcollectdclient/collectd
+collectd_nagios_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/libcollectdclient/collectd -I$(top_builddir)/src/libcollectdclient/collectd
 collectd_nagios_LDADD =
 if BUILD_WITH_LIBSOCKET
 collectd_nagios_LDADD += -lsocket
@@ -109,7 +44,7 @@ collectd_nagios_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
 
 collectdctl_SOURCES = collectdctl.c
-collectdctl_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/libcollectdclient/collectd
+collectdctl_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/libcollectdclient/collectd -I$(top_builddir)/src/libcollectdclient/collectd
 collectdctl_LDADD =
 if BUILD_WITH_LIBSOCKET
 collectdctl_LDADD += -lsocket
@@ -121,8 +56,8 @@ collectdctl_LDADD += libcollectdclient/libcollectdclient.la
 collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
 collectd_tg_SOURCES = collectd-tg.c \
-                     utils_heap.c utils_heap.h
-collectd_tg_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/libcollectdclient/collectd
+                     daemon/utils_heap.c daemon/utils_heap.h
+collectd_tg_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/libcollectdclient/collectd -I$(top_builddir)/src/libcollectdclient/collectd
 collectd_tg_LDADD =
 if BUILD_WITH_LIBSOCKET
 collectd_tg_LDADD += -lsocket
@@ -133,6 +68,9 @@ endif
 if BUILD_AIX
 collectd_tg_LDADD += -lm
 endif
+if BUILD_WITH_LIBPTHREAD
+collectd_tg_LDADD += -lpthread
+endif
 collectd_tg_LDADD += libcollectdclient/libcollectdclient.la
 collectd_tg_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
@@ -146,10 +84,8 @@ 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
+aggregation_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+aggregation_la_LIBADD = -lm
 endif
 
 if BUILD_PLUGIN_AMQP
@@ -158,134 +94,112 @@ amqp_la_SOURCES = amqp.c \
                  utils_cmd_putval.c utils_cmd_putval.h \
                  utils_format_graphite.c utils_format_graphite.h \
                  utils_format_json.c utils_format_json.h
-amqp_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBRABBITMQ_LDFLAGS)
+amqp_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRABBITMQ_LDFLAGS)
 amqp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRABBITMQ_CPPFLAGS)
 amqp_la_LIBADD = $(BUILD_WITH_LIBRABBITMQ_LIBS)
-collectd_LDADD += "-dlopen" amqp.la
-collectd_DEPENDENCIES += amqp.la
 endif
 
 if BUILD_PLUGIN_APACHE
 pkglib_LTLIBRARIES += apache.la
 apache_la_SOURCES = apache.c
-apache_la_LDFLAGS = -module -avoid-version
+apache_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 apache_la_CFLAGS = $(AM_CFLAGS)
 apache_la_LIBADD =
-collectd_LDADD += "-dlopen" apache.la
 if BUILD_WITH_LIBCURL
 apache_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
 apache_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
 endif
-collectd_DEPENDENCIES += apache.la
 endif
 
 if BUILD_PLUGIN_APCUPS
 pkglib_LTLIBRARIES += apcups.la
 apcups_la_SOURCES = apcups.c
-apcups_la_LDFLAGS = -module -avoid-version
+apcups_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 apcups_la_LIBADD =
 if BUILD_WITH_LIBSOCKET
 apcups_la_LIBADD += -lsocket
 endif
-collectd_LDADD += "-dlopen" apcups.la
-collectd_DEPENDENCIES += apcups.la
 endif
 
 if BUILD_PLUGIN_APPLE_SENSORS
 pkglib_LTLIBRARIES += apple_sensors.la
 apple_sensors_la_SOURCES = apple_sensors.c
-apple_sensors_la_LDFLAGS = -module -avoid-version
+apple_sensors_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 apple_sensors_la_LDFLAGS += -framework IOKit
-collectd_LDADD += "-dlopen" apple_sensors.la
-collectd_DEPENDENCIES += apple_sensors.la
 endif
 
 if BUILD_PLUGIN_AQUAERO
 pkglib_LTLIBRARIES += aquaero.la
 aquaero_la_SOURCES = aquaero.c
-aquaero_la_LDFLAGS = -module -avoid-version
+aquaero_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 aquaero_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBAQUAERO5_CFLAGS)
 aquaero_la_LIBADD = $(BUILD_WITH_LIBAQUAERO5_LDFLAGS) -laquaero5
-collectd_LDADD += "-dlopen" aquaero.la
-collectd_DEPENDENCIES += aquaero.la
 endif
 
 if BUILD_PLUGIN_ASCENT
 pkglib_LTLIBRARIES += ascent.la
 ascent_la_SOURCES = ascent.c
-ascent_la_LDFLAGS = -module -avoid-version
+ascent_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 ascent_la_CFLAGS = $(AM_CFLAGS) \
                $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
 ascent_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
-collectd_LDADD += "-dlopen" ascent.la
-collectd_DEPENDENCIES += ascent.la
 endif
 
 if BUILD_PLUGIN_BAROMETER
 pkglib_LTLIBRARIES += barometer.la
 barometer_la_SOURCES = barometer.c
-barometer_la_LDFLAGS = -module -avoid-version
+barometer_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 barometer_la_LIBADD = -lm
-collectd_LDADD += "-dlopen" barometer.la
-collectd_DEPENDENCIES += barometer.la
 endif
 
 if BUILD_PLUGIN_BATTERY
 pkglib_LTLIBRARIES += battery.la
 battery_la_SOURCES = battery.c
-battery_la_LDFLAGS = -module -avoid-version
+battery_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 battery_la_LIBADD =
 if BUILD_WITH_LIBIOKIT
 battery_la_LDFLAGS += -framework IOKit
 endif
-collectd_LDADD += "-dlopen" battery.la
-collectd_DEPENDENCIES += battery.la
 endif
 
 if BUILD_PLUGIN_BIND
 pkglib_LTLIBRARIES += bind.la
 bind_la_SOURCES = bind.c
-bind_la_LDFLAGS = -module -avoid-version
+bind_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 bind_la_CFLAGS = $(AM_CFLAGS) \
-               $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
+                $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
 bind_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
-collectd_LDADD += "-dlopen" bind.la
-collectd_DEPENDENCIES += bind.la
 endif
 
 if BUILD_PLUGIN_CGROUPS
 pkglib_LTLIBRARIES += cgroups.la
-cgroups_la_SOURCES = cgroups.c utils_mount.c utils_mount.h
-cgroups_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" cgroups.la
-collectd_DEPENDENCIES += cgroups.la
+cgroups_la_SOURCES = cgroups.c \
+                    utils_ignorelist.c utils_ignorelist.h \
+                    utils_mount.c utils_mount.h
+cgroups_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_CONNTRACK
 pkglib_LTLIBRARIES += conntrack.la
 conntrack_la_SOURCES = conntrack.c
-conntrack_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" conntrack.la
-collectd_DEPENDENCIES += conntrack.la
+conntrack_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_CONTEXTSWITCH
 pkglib_LTLIBRARIES += contextswitch.la
 contextswitch_la_SOURCES = contextswitch.c
-contextswitch_la_LDFLAGS = -module -avoid-version
+contextswitch_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 contextswitch_la_LIBADD =
 if BUILD_WITH_PERFSTAT
 contextswitch_la_LIBADD += -lperfstat
 endif
-collectd_LDADD += "-dlopen" contextswitch.la
-collectd_DEPENDENCIES += contextswitch.la
 endif
 
 if BUILD_PLUGIN_CPU
 pkglib_LTLIBRARIES += cpu.la
 cpu_la_SOURCES = cpu.c
 cpu_la_CFLAGS = $(AM_CFLAGS)
-cpu_la_LDFLAGS = -module -avoid-version
+cpu_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 cpu_la_LIBADD =
 if BUILD_WITH_LIBKSTAT
 cpu_la_LIBADD += -lkstat
@@ -300,64 +214,52 @@ endif
 if BUILD_WITH_PERFSTAT
 cpu_la_LIBADD += -lperfstat
 endif
-collectd_LDADD += "-dlopen" cpu.la
-collectd_DEPENDENCIES += cpu.la
 endif
 
 if BUILD_PLUGIN_CPUFREQ
 pkglib_LTLIBRARIES += cpufreq.la
 cpufreq_la_SOURCES = cpufreq.c
-cpufreq_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" cpufreq.la
-collectd_DEPENDENCIES += cpufreq.la
+cpufreq_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_CSV
 pkglib_LTLIBRARIES += csv.la
 csv_la_SOURCES = csv.c
-csv_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" csv.la
-collectd_DEPENDENCIES += csv.la
+csv_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_CURL
 pkglib_LTLIBRARIES += curl.la
 curl_la_SOURCES = curl.c
-curl_la_LDFLAGS = -module -avoid-version
+curl_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 curl_la_CFLAGS = $(AM_CFLAGS)
 curl_la_LIBADD =
-collectd_LDADD += "-dlopen" curl.la
 if BUILD_WITH_LIBCURL
 curl_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
 curl_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
 endif
-collectd_DEPENDENCIES += curl.la
 endif
 
 if BUILD_PLUGIN_CURL_JSON
 pkglib_LTLIBRARIES += curl_json.la
 curl_json_la_SOURCES = curl_json.c
 curl_json_la_CFLAGS = $(AM_CFLAGS)
-curl_json_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBYAJL_LDFLAGS)
-curl_json_la_CPPFLAGS = $(BUILD_WITH_LIBYAJL_CPPFLAGS)
+curl_json_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
+curl_json_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
 curl_json_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
 if BUILD_WITH_LIBCURL
 curl_json_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
 curl_json_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
 endif
-collectd_LDADD += "-dlopen" curl_json.la
-collectd_DEPENDENCIES += curl_json.la
 endif
 
 if BUILD_PLUGIN_CURL_XML
 pkglib_LTLIBRARIES += curl_xml.la
 curl_xml_la_SOURCES = curl_xml.c
-curl_xml_la_LDFLAGS = -module -avoid-version
+curl_xml_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 curl_xml_la_CFLAGS = $(AM_CFLAGS) \
                $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
 curl_xml_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
-collectd_LDADD += "-dlopen" curl_xml.la
-collectd_DEPENDENCIES += curl_xml.la
 endif
 
 if BUILD_PLUGIN_DBI
@@ -365,25 +267,24 @@ pkglib_LTLIBRARIES += dbi.la
 dbi_la_SOURCES = dbi.c \
                 utils_db_query.c utils_db_query.h
 dbi_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBDBI_CPPFLAGS)
-dbi_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBDBI_LDFLAGS)
+dbi_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBDBI_LDFLAGS)
 dbi_la_LIBADD = $(BUILD_WITH_LIBDBI_LIBS)
-collectd_LDADD += "-dlopen" dbi.la
-collectd_DEPENDENCIES += dbi.la
 endif
 
 if BUILD_PLUGIN_DF
 pkglib_LTLIBRARIES += df.la
-df_la_SOURCES = df.c utils_mount.c utils_mount.h
-df_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" df.la
-collectd_DEPENDENCIES += df.la
+df_la_SOURCES = df.c \
+               utils_ignorelist.c utils_ignorelist.h \
+               utils_mount.c utils_mount.h
+df_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_DISK
 pkglib_LTLIBRARIES += disk.la
-disk_la_SOURCES = disk.c
+disk_la_SOURCES = disk.c \
+                 utils_ignorelist.c utils_ignorelist.h
 disk_la_CFLAGS = $(AM_CFLAGS)
-disk_la_LDFLAGS = -module -avoid-version
+disk_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 disk_la_LIBADD =
 if BUILD_WITH_LIBKSTAT
 disk_la_LIBADD += -lkstat
@@ -404,102 +305,82 @@ endif
 if BUILD_WITH_PERFSTAT
 disk_la_LIBADD += -lperfstat
 endif
-collectd_LDADD += "-dlopen" disk.la
-collectd_DEPENDENCIES += disk.la
 endif
 
 if BUILD_PLUGIN_DNS
 pkglib_LTLIBRARIES += dns.la
 dns_la_SOURCES = dns.c utils_dns.c utils_dns.h
-dns_la_LDFLAGS = -module -avoid-version
+dns_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 dns_la_LIBADD = -lpcap -lpthread
-collectd_LDADD += "-dlopen" dns.la
-collectd_DEPENDENCIES += dns.la
 endif
 
 if BUILD_PLUGIN_DRBD
 pkglib_LTLIBRARIES += drbd.la
 drbd_la_SOURCES = drbd.c
-drbd_la_LDFLAGS = -module -avoid-version
+drbd_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 drbd_la_LIBADD = -lpthread
-collectd_LDADD += "-dlopen" drbd.la
-collectd_DEPENDENCIES += drbd.la
 endif
 
 if BUILD_PLUGIN_EMAIL
 pkglib_LTLIBRARIES += email.la
 email_la_SOURCES = email.c
-email_la_LDFLAGS = -module -avoid-version
+email_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 email_la_LIBADD = -lpthread
-collectd_LDADD += "-dlopen" email.la
-collectd_DEPENDENCIES += email.la
 endif
 
 if BUILD_PLUGIN_ENTROPY
 pkglib_LTLIBRARIES += entropy.la
 entropy_la_SOURCES = entropy.c
-entropy_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" entropy.la
-collectd_DEPENDENCIES += entropy.la
+entropy_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_EXEC
 pkglib_LTLIBRARIES += exec.la
 exec_la_SOURCES = exec.c \
                  utils_cmd_putnotif.c utils_cmd_putnotif.h \
-                 utils_cmd_putval.c utils_cmd_putval.h
-exec_la_LDFLAGS = -module -avoid-version
+                 utils_cmd_putval.c utils_cmd_putval.h \
+                 utils_parse_option.h utils_parse_option.c
+exec_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 exec_la_LIBADD = -lpthread
-collectd_LDADD += "-dlopen" exec.la
-collectd_DEPENDENCIES += exec.la
 endif
 
 if BUILD_PLUGIN_ETHSTAT
 pkglib_LTLIBRARIES += ethstat.la
 ethstat_la_SOURCES = ethstat.c
-ethstat_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" ethstat.la
-collectd_DEPENDENCIES += ethstat.la
+ethstat_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_FILECOUNT
 pkglib_LTLIBRARIES += filecount.la
 filecount_la_SOURCES = filecount.c
-filecount_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" filecount.la
-collectd_DEPENDENCIES += filecount.la
+filecount_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_GMOND
 pkglib_LTLIBRARIES += gmond.la
 gmond_la_SOURCES = gmond.c
 gmond_la_CPPFLAGS = $(AM_CPPFLAGS) $(GANGLIA_CPPFLAGS)
-gmond_la_LDFLAGS = -module -avoid-version $(GANGLIA_LDFLAGS)
+gmond_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(GANGLIA_LDFLAGS)
 gmond_la_LIBADD = $(GANGLIA_LIBS)
-collectd_LDADD += "-dlopen" gmond.la
-collectd_DEPENDENCIES += gmond.la
 endif
 
 if BUILD_PLUGIN_HDDTEMP
 pkglib_LTLIBRARIES += hddtemp.la
 hddtemp_la_SOURCES = hddtemp.c
-hddtemp_la_LDFLAGS = -module -avoid-version
+hddtemp_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 hddtemp_la_LIBADD =
 if BUILD_WITH_LIBSOCKET
 hddtemp_la_LIBADD += -lsocket
 endif
-collectd_LDADD += "-dlopen" hddtemp.la
-collectd_DEPENDENCIES += hddtemp.la
 endif
 
 if BUILD_PLUGIN_INTERFACE
 pkglib_LTLIBRARIES += interface.la
-interface_la_SOURCES = interface.c
+interface_la_SOURCES = interface.c \
+                      utils_ignorelist.c utils_ignorelist.h
 interface_la_CFLAGS = $(AM_CFLAGS)
-interface_la_LDFLAGS = -module -avoid-version
+interface_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 interface_la_LIBADD =
-collectd_LDADD += "-dlopen" interface.la
-collectd_DEPENDENCIES += interface.la
 if BUILD_WITH_LIBSTATGRAB
 interface_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
 interface_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
@@ -520,20 +401,17 @@ if BUILD_PLUGIN_IPTABLES
 pkglib_LTLIBRARIES += iptables.la
 iptables_la_SOURCES = iptables.c
 iptables_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBIPTC_CPPFLAGS)
-iptables_la_LDFLAGS = -module -avoid-version
+iptables_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 iptables_la_LIBADD = $(BUILD_WITH_LIBIPTC_LDFLAGS)
-collectd_LDADD += "-dlopen" iptables.la
-collectd_DEPENDENCIES += iptables.la
 endif
 
 if BUILD_PLUGIN_IPMI
 pkglib_LTLIBRARIES += ipmi.la
-ipmi_la_SOURCES = ipmi.c
+ipmi_la_SOURCES = ipmi.c \
+                 utils_ignorelist.c utils_ignorelist.h
 ipmi_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_OPENIPMI_CFLAGS)
-ipmi_la_LDFLAGS = -module -avoid-version
+ipmi_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 ipmi_la_LIBADD = $(BUILD_WITH_OPENIPMI_LIBS)
-collectd_LDADD += "-dlopen" ipmi.la
-collectd_DEPENDENCIES += ipmi.la
 endif
 
 if BUILD_PLUGIN_IPVS
@@ -542,17 +420,14 @@ ipvs_la_SOURCES = ipvs.c
 if IP_VS_H_NEEDS_KERNEL_CFLAGS
 ipvs_la_CFLAGS = $(AM_CFLAGS) $(KERNEL_CFLAGS)
 endif
-ipvs_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" ipvs.la
-collectd_DEPENDENCIES += ipvs.la
+ipvs_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_IRQ
 pkglib_LTLIBRARIES += irq.la
-irq_la_SOURCES = irq.c
-irq_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" irq.la
-collectd_DEPENDENCIES += irq.la
+irq_la_SOURCES = irq.c \
+                utils_ignorelist.c utils_ignorelist.h
+irq_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_JAVA
@@ -560,31 +435,16 @@ pkglib_LTLIBRARIES += java.la
 java_la_SOURCES = java.c
 java_la_CPPFLAGS = $(AM_CPPFLAGS) $(JAVA_CPPFLAGS)
 java_la_CFLAGS = $(AM_CFLAGS) $(JAVA_CFLAGS)
-java_la_LDFLAGS = -module -avoid-version $(JAVA_LDFLAGS)
+java_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(JAVA_LDFLAGS)
 java_la_LIBADD = $(JAVA_LIBS)
-collectd_LDADD += "-dlopen" java.la
-collectd_DEPENDENCIES += java.la
-endif
-
-if BUILD_PLUGIN_LIBVIRT
-pkglib_LTLIBRARIES += libvirt.la
-libvirt_la_SOURCES = libvirt.c
-libvirt_la_CFLAGS = $(AM_CFLAGS) \
-               $(BUILD_WITH_LIBVIRT_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
-libvirt_la_LIBADD = $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
-libvirt_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" libvirt.la
-collectd_DEPENDENCIES += libvirt.la
 endif
 
 if BUILD_PLUGIN_LOAD
 pkglib_LTLIBRARIES += load.la
 load_la_SOURCES = load.c
 load_la_CFLAGS = $(AM_CFLAGS)
-load_la_LDFLAGS = -module -avoid-version
+load_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 load_la_LIBADD =
-collectd_LDADD += "-dlopen" load.la
-collectd_DEPENDENCIES += load.la
 if BUILD_WITH_LIBSTATGRAB
 load_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
 load_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
@@ -597,138 +457,110 @@ endif # BUILD_PLUGIN_LOAD
 if BUILD_PLUGIN_LOGFILE
 pkglib_LTLIBRARIES += logfile.la
 logfile_la_SOURCES = logfile.c
-logfile_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" logfile.la
-collectd_DEPENDENCIES += logfile.la
+logfile_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_LOG_LOGSTASH
 pkglib_LTLIBRARIES += log_logstash.la
 log_logstash_la_SOURCES = log_logstash.c
 log_logstash_la_CFLAGS = $(AM_CFLAGS)
-log_logstash_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBYAJL_LDFLAGS)
+log_logstash_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
 log_logstash_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
 log_logstash_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
-collectd_LDADD += "-dlopen" log_logstash.la
-collectd_DEPENDENCIES += log_logstash.la
 endif
 
 if BUILD_PLUGIN_LPAR
 pkglib_LTLIBRARIES += lpar.la
 lpar_la_SOURCES = lpar.c
-lpar_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" lpar.la
-collectd_DEPENDENCIES += lpar.la
+lpar_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 lpar_la_LIBADD = -lperfstat
 endif
 
 if BUILD_PLUGIN_LVM
 pkglib_LTLIBRARIES += lvm.la
 lvm_la_SOURCES = lvm.c
-lvm_la_LDFLAGS = -module -avoid-version
+lvm_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 lvm_la_LIBADD = $(BUILD_WITH_LIBLVM2APP_LIBS)
-collectd_LDADD += "-dlopen" lvm.la
-collectd_DEPENDENCIES += lvm.la
 endif
 
 if BUILD_PLUGIN_MADWIFI
 pkglib_LTLIBRARIES += madwifi.la
-madwifi_la_SOURCES = madwifi.c madwifi.h
-madwifi_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" madwifi.la
-collectd_DEPENDENCIES += madwifi.la
+madwifi_la_SOURCES = madwifi.c madwifi.h \
+                    utils_ignorelist.c utils_ignorelist.h
+madwifi_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_MATCH_EMPTY_COUNTER
 pkglib_LTLIBRARIES += match_empty_counter.la
 match_empty_counter_la_SOURCES = match_empty_counter.c
-match_empty_counter_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" match_empty_counter.la
-collectd_DEPENDENCIES += match_empty_counter.la
+match_empty_counter_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_MATCH_HASHED
 pkglib_LTLIBRARIES += match_hashed.la
 match_hashed_la_SOURCES = match_hashed.c
-match_hashed_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" match_hashed.la
-collectd_DEPENDENCIES += match_hashed.la
+match_hashed_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_MATCH_REGEX
 pkglib_LTLIBRARIES += match_regex.la
 match_regex_la_SOURCES = match_regex.c
-match_regex_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" match_regex.la
-collectd_DEPENDENCIES += match_regex.la
+match_regex_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_MATCH_TIMEDIFF
 pkglib_LTLIBRARIES += match_timediff.la
 match_timediff_la_SOURCES = match_timediff.c
-match_timediff_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" match_timediff.la
-collectd_DEPENDENCIES += match_timediff.la
+match_timediff_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_MATCH_VALUE
 pkglib_LTLIBRARIES += match_value.la
 match_value_la_SOURCES = match_value.c
-match_value_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" match_value.la
-collectd_DEPENDENCIES += match_value.la
+match_value_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_MBMON
 pkglib_LTLIBRARIES += mbmon.la
 mbmon_la_SOURCES = mbmon.c
-mbmon_la_LDFLAGS = -module -avoid-version
+mbmon_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 mbmon_la_LIBADD =
 if BUILD_WITH_LIBSOCKET
 mbmon_la_LIBADD += -lsocket
 endif
-collectd_LDADD += "-dlopen" mbmon.la
-collectd_DEPENDENCIES += mbmon.la
 endif
 
 if BUILD_PLUGIN_MD
 pkglib_LTLIBRARIES += md.la
-md_la_SOURCES = md.c
-md_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" md.la
-collectd_DEPENDENCIES += md.la
+md_la_SOURCES = md.c \
+               utils_ignorelist.c utils_ignorelist.h
+md_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_MEMCACHEC
 pkglib_LTLIBRARIES += memcachec.la
 memcachec_la_SOURCES = memcachec.c
-memcachec_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBMEMCACHED_LDFLAGS)
-memcachec_la_CPPFLAGS = $(BUILD_WITH_LIBMEMCACHED_CPPFLAGS)
+memcachec_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMEMCACHED_LDFLAGS)
+memcachec_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMEMCACHED_CPPFLAGS)
 memcachec_la_LIBADD = $(BUILD_WITH_LIBMEMCACHED_LIBS)
-collectd_LDADD += "-dlopen" memcachec.la
-collectd_DEPENDENCIES += memcachec.la
 endif
 
 if BUILD_PLUGIN_MEMCACHED
 pkglib_LTLIBRARIES += memcached.la
 memcached_la_SOURCES = memcached.c
-memcached_la_LDFLAGS = -module -avoid-version
+memcached_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 memcached_la_LIBADD =
 if BUILD_WITH_LIBSOCKET
 memcached_la_LIBADD += -lsocket
 endif
-collectd_LDADD += "-dlopen" memcached.la
-collectd_DEPENDENCIES += memcached.la
 endif
 
 if BUILD_PLUGIN_MEMORY
 pkglib_LTLIBRARIES += memory.la
 memory_la_SOURCES = memory.c
 memory_la_CFLAGS = $(AM_CFLAGS)
-memory_la_LDFLAGS = -module -avoid-version
+memory_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 memory_la_LIBADD =
-collectd_LDADD += "-dlopen" memory.la
-collectd_DEPENDENCIES += memory.la
 if BUILD_WITH_LIBKSTAT
 memory_la_LIBADD += -lkstat
 endif
@@ -744,56 +576,56 @@ memory_la_LIBADD += -lperfstat
 endif
 endif
 
+if BUILD_PLUGIN_MIC
+pkglib_LTLIBRARIES += mic.la
+mic_la_SOURCES = mic.c \
+                utils_ignorelist.c utils_ignorelist.h
+mic_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_MIC_LIBPATH)
+mic_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_MIC_CPPFLAGS)
+mic_la_LIBADD = $(BUILD_WITH_MIC_LDADD)
+endif
+
 if BUILD_PLUGIN_MODBUS
 pkglib_LTLIBRARIES += modbus.la
 modbus_la_SOURCES = modbus.c
-modbus_la_LDFLAGS = -module -avoid-version
+modbus_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 modbus_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMODBUS_CFLAGS)
 modbus_la_LIBADD = $(BUILD_WITH_LIBMODBUS_LIBS)
-collectd_LDADD += "-dlopen" modbus.la
-collectd_DEPENDENCIES += modbus.la
 endif
 
 if BUILD_PLUGIN_MULTIMETER
 pkglib_LTLIBRARIES += multimeter.la
 multimeter_la_SOURCES = multimeter.c
-multimeter_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" multimeter.la
-collectd_DEPENDENCIES += multimeter.la
+multimeter_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_MYSQL
 pkglib_LTLIBRARIES += mysql.la
 mysql_la_SOURCES = mysql.c
-mysql_la_LDFLAGS = -module -avoid-version
+mysql_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 mysql_la_CFLAGS = $(AM_CFLAGS)
 mysql_la_LIBADD =
-collectd_LDADD += "-dlopen" mysql.la
 if BUILD_WITH_LIBMYSQL
 mysql_la_CFLAGS += $(BUILD_WITH_LIBMYSQL_CFLAGS)
 mysql_la_LIBADD += $(BUILD_WITH_LIBMYSQL_LIBS)
 endif
-collectd_DEPENDENCIES += mysql.la
 endif
 
 if BUILD_PLUGIN_NETAPP
 pkglib_LTLIBRARIES += netapp.la
-netapp_la_SOURCES = netapp.c
+netapp_la_SOURCES = netapp.c \
+                   utils_ignorelist.c utils_ignorelist.h
 netapp_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBNETAPP_CPPFLAGS)
-netapp_la_LDFLAGS = -module -avoid-version $(LIBNETAPP_LDFLAGS)
+netapp_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBNETAPP_LDFLAGS)
 netapp_la_LIBADD = $(LIBNETAPP_LIBS)
-collectd_LDADD += "-dlopen" netapp.la
-collectd_DEPENDENCIES += netapp.la
 endif
 
 if BUILD_PLUGIN_NETLINK
 pkglib_LTLIBRARIES += netlink.la
 netlink_la_SOURCES = netlink.c
-netlink_la_LDFLAGS = -module -avoid-version
+netlink_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 netlink_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
 netlink_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS)
-collectd_LDADD += "-dlopen" netlink.la
-collectd_DEPENDENCIES += netlink.la
 endif
 
 if BUILD_PLUGIN_NETWORK
@@ -801,7 +633,7 @@ pkglib_LTLIBRARIES += network.la
 network_la_SOURCES = network.c network.h \
                     utils_fbhash.c utils_fbhash.h
 network_la_CPPFLAGS = $(AM_CPPFLAGS)
-network_la_LDFLAGS = -module -avoid-version
+network_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 network_la_LIBADD = -lpthread
 if BUILD_WITH_LIBSOCKET
 network_la_LIBADD += -lsocket
@@ -811,24 +643,18 @@ network_la_CPPFLAGS += $(GCRYPT_CPPFLAGS)
 network_la_LDFLAGS += $(GCRYPT_LDFLAGS)
 network_la_LIBADD += $(GCRYPT_LIBS)
 endif
-collectd_LDADD += "-dlopen" network.la
-collectd_DEPENDENCIES += network.la
 endif
 
 if BUILD_PLUGIN_NFS
 pkglib_LTLIBRARIES += nfs.la
 nfs_la_SOURCES = nfs.c
-nfs_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" nfs.la
-collectd_DEPENDENCIES += nfs.la
+nfs_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_FSCACHE
 pkglib_LTLIBRARIES += fscache.la
 fscache_la_SOURCES = fscache.c
-fscache_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" fscache.la
-collectd_DEPENDENCIES += fscache.la
+fscache_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_NGINX
@@ -836,85 +662,70 @@ pkglib_LTLIBRARIES += nginx.la
 nginx_la_SOURCES = nginx.c
 nginx_la_CFLAGS = $(AM_CFLAGS)
 nginx_la_LIBADD =
-nginx_la_LDFLAGS = -module -avoid-version
+nginx_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 if BUILD_WITH_LIBCURL
 nginx_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
 nginx_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
 endif
-collectd_LDADD += "-dlopen" nginx.la
-collectd_DEPENDENCIES += nginx.la
 endif
 
 if BUILD_PLUGIN_NOTIFY_DESKTOP
 pkglib_LTLIBRARIES += notify_desktop.la
 notify_desktop_la_SOURCES = notify_desktop.c
 notify_desktop_la_CFLAGS = $(AM_CFLAGS) $(LIBNOTIFY_CFLAGS)
-notify_desktop_la_LDFLAGS = -module -avoid-version
+notify_desktop_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 notify_desktop_la_LIBADD = $(LIBNOTIFY_LIBS)
-collectd_LDADD += "-dlopen" notify_desktop.la
-collectd_DEPENDENCIES += notify_desktop.la
 endif
 
 if BUILD_PLUGIN_NOTIFY_EMAIL
 pkglib_LTLIBRARIES += notify_email.la
 notify_email_la_SOURCES = notify_email.c
-notify_email_la_LDFLAGS = -module -avoid-version
+notify_email_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 notify_email_la_LIBADD = -lesmtp -lssl -lcrypto -lpthread -ldl
-collectd_LDADD += "-dlopen" notify_email.la
-collectd_DEPENDENCIES += notify_email.la
 endif
 
 if BUILD_PLUGIN_NTPD
 pkglib_LTLIBRARIES += ntpd.la
 ntpd_la_SOURCES = ntpd.c
-ntpd_la_LDFLAGS = -module -avoid-version
+ntpd_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 ntpd_la_LIBADD =
 if BUILD_WITH_LIBSOCKET
 ntpd_la_LIBADD += -lsocket
 endif
-collectd_LDADD += "-dlopen" ntpd.la
-collectd_DEPENDENCIES += ntpd.la
 endif
 
 if BUILD_PLUGIN_NUMA
 pkglib_LTLIBRARIES += numa.la
 numa_la_SOURCES = numa.c
-numa_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" numa.la
-collectd_DEPENDENCIES += numa.la
+numa_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_NUT
 pkglib_LTLIBRARIES += nut.la
 nut_la_SOURCES = nut.c
 nut_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBUPSCLIENT_CFLAGS)
-nut_la_LDFLAGS = -module -avoid-version
+nut_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 nut_la_LIBADD = -lpthread $(BUILD_WITH_LIBUPSCLIENT_LIBS)
-collectd_LDADD += "-dlopen" nut.la
-collectd_DEPENDENCIES += nut.la
 endif
 
 if BUILD_PLUGIN_OLSRD
 pkglib_LTLIBRARIES += olsrd.la
 olsrd_la_SOURCES = olsrd.c
-olsrd_la_LDFLAGS = -module -avoid-version
+olsrd_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 olsrd_la_LIBADD =
 if BUILD_WITH_LIBSOCKET
 olsrd_la_LIBADD += -lsocket
 endif
-collectd_LDADD += "-dlopen" olsrd.la
-collectd_DEPENDENCIES += olsrd.la
 endif
 
 if BUILD_PLUGIN_ONEWIRE
 pkglib_LTLIBRARIES += onewire.la
-onewire_la_SOURCES = onewire.c
+onewire_la_SOURCES = onewire.c \
+                    utils_ignorelist.c utils_ignorelist.h
 onewire_la_CFLAGS = $(AM_CFLAGS)
-onewire_la_CPPFLAGS = $(BUILD_WITH_LIBOWCAPI_CPPFLAGS)
+onewire_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBOWCAPI_CPPFLAGS)
 onewire_la_LIBADD = $(BUILD_WITH_LIBOWCAPI_LIBS)
-onewire_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" onewire.la
-collectd_DEPENDENCIES += onewire.la
+onewire_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_OPENLDAP
@@ -929,9 +740,7 @@ if BUILD_PLUGIN_OPENVPN
 pkglib_LTLIBRARIES += openvpn.la
 openvpn_la_SOURCES = openvpn.c
 openvpn_la_CFLAGS = $(AM_CFLAGS)
-openvpn_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" openvpn.la
-collectd_DEPENDENCIES += openvpn.la
+openvpn_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_ORACLE
@@ -939,11 +748,9 @@ pkglib_LTLIBRARIES += oracle.la
 oracle_la_SOURCES = oracle.c \
        utils_db_query.c utils_db_query.h
 oracle_la_CFLAGS = $(AM_CFLAGS)
-oracle_la_CPPFLAGS = $(BUILD_WITH_ORACLE_CFLAGS)
+oracle_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_ORACLE_CFLAGS)
 oracle_la_LIBADD = $(BUILD_WITH_ORACLE_LIBS)
-oracle_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" oracle.la
-collectd_DEPENDENCIES += oracle.la
+oracle_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_PERL
@@ -960,38 +767,30 @@ perl_la_CFLAGS  = $(AM_CFLAGS) \
 if HAVE_BROKEN_PERL_LOAD_MODULE
 perl_la_CFLAGS += -Wno-nonnull
 endif
-perl_la_LDFLAGS = -module -avoid-version \
+perl_la_LDFLAGS = $(PLUGIN_LDFLAGS) \
                $(PERL_LDFLAGS)
-collectd_LDADD += "-dlopen" perl.la
-collectd_DEPENDENCIES += perl.la
 endif
 
 if BUILD_PLUGIN_PF
 pkglib_LTLIBRARIES += pf.la
 pf_la_SOURCES = pf.c
-pf_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" pf.la
-collectd_DEPENDENCIES += pf.la
+pf_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_PINBA
 pkglib_LTLIBRARIES += pinba.la
 pinba_la_SOURCES = pinba.c
 nodist_pinba_la_SOURCES = pinba.pb-c.c pinba.pb-c.h
-pinba_la_LDFLAGS = -module -avoid-version
+pinba_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 pinba_la_LIBADD = -lprotobuf-c
-collectd_LDADD += "-dlopen" pinba.la
-collectd_DEPENDENCIES += pinba.la
 endif
 
 if BUILD_PLUGIN_PING
 pkglib_LTLIBRARIES += ping.la
 ping_la_SOURCES = ping.c
 ping_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBOPING_CPPFLAGS)
-ping_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBOPING_LDFLAGS)
+ping_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBOPING_LDFLAGS)
 ping_la_LIBADD = -loping -lm
-collectd_LDADD += "-dlopen" ping.la
-collectd_DEPENDENCIES += ping.la
 endif
 
 if BUILD_PLUGIN_POSTGRESQL
@@ -999,19 +798,15 @@ pkglib_LTLIBRARIES += postgresql.la
 postgresql_la_SOURCES = postgresql.c \
                 utils_db_query.c utils_db_query.h
 postgresql_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPQ_CPPFLAGS)
-postgresql_la_LDFLAGS = -module -avoid-version \
+postgresql_la_LDFLAGS = $(PLUGIN_LDFLAGS) \
                $(BUILD_WITH_LIBPQ_LDFLAGS)
 postgresql_la_LIBADD = -lpq
-collectd_LDADD += "-dlopen" postgresql.la
-collectd_DEPENDENCIES += postgresql.la
 endif
 
 if BUILD_PLUGIN_POWERDNS
 pkglib_LTLIBRARIES += powerdns.la
 powerdns_la_SOURCES = powerdns.c
-powerdns_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" powerdns.la
-collectd_DEPENDENCIES += powerdns.la
+powerdns_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_PYTHON
@@ -1022,19 +817,15 @@ python_la_CFLAGS = $(AM_CFLAGS)
 if COMPILER_IS_GCC
 python_la_CFLAGS += -fno-strict-aliasing -Wno-strict-aliasing
 endif
-python_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_PYTHON_LDFLAGS)
+python_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_PYTHON_LDFLAGS)
 python_la_LIBADD = $(BUILD_WITH_PYTHON_LIBS)
-collectd_LDADD += "-dlopen" python.la
-collectd_DEPENDENCIES += python.la
 endif
 
 if BUILD_PLUGIN_PROCESSES
 pkglib_LTLIBRARIES += processes.la
 processes_la_SOURCES = processes.c
-processes_la_LDFLAGS = -module -avoid-version
+processes_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 processes_la_LIBADD =
-collectd_LDADD += "-dlopen" processes.la
-collectd_DEPENDENCIES += processes.la
 if BUILD_WITH_LIBKVM_GETPROCS
 processes_la_LIBADD += -lkvm
 endif
@@ -1042,84 +833,81 @@ endif
 
 if BUILD_PLUGIN_PROTOCOLS
 pkglib_LTLIBRARIES += protocols.la
-protocols_la_SOURCES = protocols.c
-protocols_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" protocols.la
-collectd_DEPENDENCIES += protocols.la
+protocols_la_SOURCES = protocols.c \
+                      utils_ignorelist.c utils_ignorelist.h
+protocols_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_REDIS
 pkglib_LTLIBRARIES += redis.la
 redis_la_SOURCES = redis.c
-redis_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBCREDIS_LDFLAGS)
+redis_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBCREDIS_LDFLAGS)
 redis_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCREDIS_CPPFLAGS)
 redis_la_LIBADD = -lcredis
-collectd_LDADD += "-dlopen" redis.la
-collectd_DEPENDENCIES += redis.la
 endif
 
 if BUILD_PLUGIN_ROUTEROS
 pkglib_LTLIBRARIES += routeros.la
 routeros_la_SOURCES = routeros.c
-routeros_la_CPPFLAGS = $(BUILD_WITH_LIBROUTEROS_CPPFLAGS)
-routeros_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBROUTEROS_LDFLAGS)
+routeros_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBROUTEROS_CPPFLAGS)
+routeros_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBROUTEROS_LDFLAGS)
 routeros_la_LIBADD = -lrouteros
-collectd_LDADD += "-dlopen" routeros.la
-collectd_DEPENDENCIES += routeros.la
 endif
 
 if BUILD_PLUGIN_RRDCACHED
 pkglib_LTLIBRARIES += rrdcached.la
 rrdcached_la_SOURCES = rrdcached.c utils_rrdcreate.c utils_rrdcreate.h
-rrdcached_la_LDFLAGS = -module -avoid-version
+rrdcached_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 rrdcached_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS)
 rrdcached_la_LIBADD = $(BUILD_WITH_LIBRRD_LDFLAGS)
-collectd_LDADD += "-dlopen" rrdcached.la
-collectd_DEPENDENCIES += rrdcached.la
 endif
 
 if BUILD_PLUGIN_RRDTOOL
 pkglib_LTLIBRARIES += rrdtool.la
 rrdtool_la_SOURCES = rrdtool.c utils_rrdcreate.c utils_rrdcreate.h
-rrdtool_la_LDFLAGS = -module -avoid-version
+rrdtool_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 rrdtool_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS)
 rrdtool_la_LIBADD = $(BUILD_WITH_LIBRRD_LDFLAGS)
-collectd_LDADD += "-dlopen" rrdtool.la
-collectd_DEPENDENCIES += rrdtool.la
 endif
 
 if BUILD_PLUGIN_SENSORS
 pkglib_LTLIBRARIES += sensors.la
-sensors_la_SOURCES = sensors.c
+sensors_la_SOURCES = sensors.c \
+                    utils_ignorelist.c utils_ignorelist.h
 sensors_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBSENSORS_CFLAGS)
-sensors_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBSENSORS_LDFLAGS)
+sensors_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBSENSORS_LDFLAGS)
 sensors_la_LIBADD = -lsensors
-collectd_LDADD += "-dlopen" sensors.la
-collectd_DEPENDENCIES += sensors.la
 endif
 
 if BUILD_PLUGIN_SERIAL
 pkglib_LTLIBRARIES += serial.la
 serial_la_SOURCES = serial.c
-serial_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" serial.la
-collectd_DEPENDENCIES += serial.la
+serial_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_SIGROK
 pkglib_LTLIBRARIES += sigrok.la
 sigrok_la_SOURCES = sigrok.c
 sigrok_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBSIGROK_CFLAGS)
-sigrok_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBSIGROK_LDFLAGS)
+sigrok_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBSIGROK_LDFLAGS)
 sigrok_la_LIBADD = -lsigrok
-collectd_LDADD += "-dlopen" sigrok.la
-collectd_DEPENDENCIES += sigrok.la
+endif
+
+if BUILD_PLUGIN_SMART
+if BUILD_WITH_LIBUDEV
+pkglib_LTLIBRARIES += smart.la
+smart_la_SOURCES = smart.c \
+                  utils_ignorelist.c utils_ignorelist.h
+smart_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBATASMART_CPPFLAGS)
+smart_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBATASMART_LDFLAGS)
+smart_la_LIBADD = $(BUILD_WITH_LIBATASMART_LIBS) -ludev
+endif
 endif
 
 if BUILD_PLUGIN_SNMP
 pkglib_LTLIBRARIES += snmp.la
 snmp_la_SOURCES = snmp.c
-snmp_la_LDFLAGS = -module -avoid-version
+snmp_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 snmp_la_CFLAGS = $(AM_CFLAGS)
 snmp_la_LIBADD =
 if BUILD_WITH_LIBNETSNMP
@@ -1129,28 +917,22 @@ endif
 if BUILD_WITH_LIBPTHREAD
 snmp_la_LIBADD += -lpthread
 endif
-collectd_LDADD += "-dlopen" snmp.la
-collectd_DEPENDENCIES += snmp.la
 endif
 
 if BUILD_PLUGIN_STATSD
 pkglib_LTLIBRARIES += statsd.la
 statsd_la_SOURCES = statsd.c \
                     utils_latency.h utils_latency.c
-statsd_la_LDFLAGS = -module -avoid-version
+statsd_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 statsd_la_LIBADD = -lpthread
-collectd_LDADD += "-dlopen" statsd.la
-collectd_DEPENDENCIES += statsd.la
 endif
 
 if BUILD_PLUGIN_SWAP
 pkglib_LTLIBRARIES += swap.la
 swap_la_SOURCES = swap.c
 swap_la_CFLAGS = $(AM_CFLAGS)
-swap_la_LDFLAGS = -module -avoid-version
+swap_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 swap_la_LIBADD =
-collectd_LDADD += "-dlopen" swap.la
-collectd_DEPENDENCIES += swap.la
 if BUILD_WITH_LIBKSTAT
 swap_la_LIBADD += -lkstat
 endif
@@ -1173,91 +955,69 @@ endif
 if BUILD_PLUGIN_SYSLOG
 pkglib_LTLIBRARIES += syslog.la
 syslog_la_SOURCES = syslog.c
-syslog_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" syslog.la
-collectd_DEPENDENCIES += syslog.la
+syslog_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TABLE
 pkglib_LTLIBRARIES += table.la
 table_la_SOURCES = table.c
-table_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" table.la
-collectd_DEPENDENCIES += table.la
+table_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TAIL
 pkglib_LTLIBRARIES += tail.la
 tail_la_SOURCES = tail.c
-tail_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" tail.la
-collectd_DEPENDENCIES += tail.la
+tail_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TAIL_CSV
 pkglib_LTLIBRARIES += tail_csv.la
 tail_csv_la_SOURCES = tail_csv.c
-tail_csv_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" tail_csv.la
-collectd_DEPENDENCIES += tail_csv.la
+tail_csv_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TAPE
 pkglib_LTLIBRARIES += tape.la
 tape_la_SOURCES = tape.c
-tape_la_LDFLAGS = -module -avoid-version
+tape_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 tape_la_LIBADD = -lkstat -ldevinfo
-collectd_LDADD += "-dlopen" tape.la
-collectd_DEPENDENCIES += tape.la
 endif
 
 if BUILD_PLUGIN_TARGET_NOTIFICATION
 pkglib_LTLIBRARIES += target_notification.la
 target_notification_la_SOURCES = target_notification.c
-target_notification_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" target_notification.la
-collectd_DEPENDENCIES += target_notification.la
+target_notification_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TARGET_REPLACE
 pkglib_LTLIBRARIES += target_replace.la
 target_replace_la_SOURCES = target_replace.c
-target_replace_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" target_replace.la
-collectd_DEPENDENCIES += target_replace.la
+target_replace_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TARGET_SCALE
 pkglib_LTLIBRARIES += target_scale.la
 target_scale_la_SOURCES = target_scale.c
-target_scale_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" target_scale.la
-collectd_DEPENDENCIES += target_scale.la
+target_scale_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TARGET_SET
 pkglib_LTLIBRARIES += target_set.la
 target_set_la_SOURCES = target_set.c
-target_set_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" target_set.la
-collectd_DEPENDENCIES += target_set.la
+target_set_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TARGET_V5UPGRADE
 pkglib_LTLIBRARIES += target_v5upgrade.la
 target_v5upgrade_la_SOURCES = target_v5upgrade.c
-target_v5upgrade_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" target_v5upgrade.la
-collectd_DEPENDENCIES += target_v5upgrade.la
+target_v5upgrade_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TCPCONNS
 pkglib_LTLIBRARIES += tcpconns.la
 tcpconns_la_SOURCES = tcpconns.c
-tcpconns_la_LDFLAGS = -module -avoid-version
+tcpconns_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 tcpconns_la_LIBADD =
-collectd_LDADD += "-dlopen" tcpconns.la
-collectd_DEPENDENCIES += tcpconns.la
 if BUILD_WITH_LIBKVM_NLIST
 tcpconns_la_LIBADD += -lkvm
 endif
@@ -1266,46 +1026,37 @@ endif
 if BUILD_PLUGIN_TEAMSPEAK2
 pkglib_LTLIBRARIES += teamspeak2.la
 teamspeak2_la_SOURCES = teamspeak2.c
-teamspeak2_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" teamspeak2.la
-collectd_DEPENDENCIES += teamspeak2.la
+teamspeak2_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TED
 pkglib_LTLIBRARIES += ted.la
 ted_la_SOURCES = ted.c
-ted_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" ted.la
-collectd_DEPENDENCIES += ted.la
+ted_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_THERMAL
 pkglib_LTLIBRARIES += thermal.la
-thermal_la_SOURCES = thermal.c
-thermal_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" thermal.la
-collectd_DEPENDENCIES += thermal.la
+thermal_la_SOURCES = thermal.c \
+                    utils_ignorelist.c utils_ignorelist.h
+thermal_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_THRESHOLD
 pkglib_LTLIBRARIES += threshold.la
 threshold_la_SOURCES = threshold.c
-threshold_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" threshold.la
-collectd_DEPENDENCIES += threshold.la
+threshold_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_TOKYOTYRANT
 pkglib_LTLIBRARIES += tokyotyrant.la
 tokyotyrant_la_SOURCES = tokyotyrant.c
 tokyotyrant_la_CPPFLAGS  = $(AM_CPPFLAGS) $(BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS)
-tokyotyrant_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS)
+tokyotyrant_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS)
 tokyotyrant_la_LIBADD  = $(BUILD_WITH_LIBTOKYOTYRANT_LIBS)
 if BUILD_WITH_LIBSOCKET
 tokyotyrant_la_LIBADD += -lsocket
 endif
-collectd_LDADD += "-dlopen" tokyotyrant.la
-collectd_DEPENDENCIES += tokyotyrant.la
 endif
 
 if BUILD_PLUGIN_UNIXSOCK
@@ -1316,18 +1067,17 @@ unixsock_la_SOURCES = unixsock.c \
                      utils_cmd_getthreshold.h utils_cmd_getthreshold.c \
                      utils_cmd_listval.h utils_cmd_listval.c \
                      utils_cmd_putval.h utils_cmd_putval.c \
-                     utils_cmd_putnotif.h utils_cmd_putnotif.c
-unixsock_la_LDFLAGS = -module -avoid-version
+                     utils_cmd_putnotif.h utils_cmd_putnotif.c \
+                     utils_parse_option.h utils_parse_option.c
+unixsock_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 unixsock_la_LIBADD = -lpthread
-collectd_LDADD += "-dlopen" unixsock.la
-collectd_DEPENDENCIES += unixsock.la
 endif
 
 if BUILD_PLUGIN_UPTIME
 pkglib_LTLIBRARIES += uptime.la
 uptime_la_SOURCES = uptime.c
 uptime_la_CFLAGS = $(AM_CFLAGS)
-uptime_la_LDFLAGS = -module -avoid-version
+uptime_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 uptime_la_LIBADD =
 if BUILD_WITH_LIBKSTAT
 uptime_la_LIBADD += -lkstat
@@ -1335,22 +1085,18 @@ endif
 if BUILD_WITH_PERFSTAT
 uptime_la_LIBADD += -lperfstat
 endif
-collectd_LDADD += "-dlopen" uptime.la
-collectd_DEPENDENCIES += uptime.la
 endif
 
 if BUILD_PLUGIN_USERS
 pkglib_LTLIBRARIES += users.la
 users_la_SOURCES = users.c
 users_la_CFLAGS = $(AM_CFLAGS)
-users_la_LDFLAGS = -module -avoid-version
+users_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 users_la_LIBADD =
 if BUILD_WITH_LIBSTATGRAB
 users_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
 users_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
 endif
-collectd_LDADD += "-dlopen" users.la
-collectd_DEPENDENCIES += users.la
 endif
 
 if BUILD_PLUGIN_UUID
@@ -1358,53 +1104,43 @@ pkglib_LTLIBRARIES += uuid.la
 uuid_la_SOURCES = uuid.c
 uuid_la_CFLAGS  = $(AM_CFLAGS) $(BUILD_WITH_LIBHAL_CFLAGS)
 uuid_la_LIBADD  = $(BUILD_WITH_LIBHAL_LIBS)
-uuid_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" uuid.la
-collectd_DEPENDENCIES += uuid.la
-endif
-
-if BUILD_PLUGIN_MIC
-pkglib_LTLIBRARIES += mic.la
-mic_la_SOURCES = mic.c
-mic_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_MIC_LIBPATH)
-mic_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_MIC_CPPFLAGS)
-mic_la_LIBADD = $(BUILD_WITH_MIC_LDADD)
-collectd_LDADD += "-dlopen" mic.la
-collectd_DEPENDENCIES += mic.la
+uuid_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_VARNISH
 pkglib_LTLIBRARIES += varnish.la
 varnish_la_SOURCES = varnish.c
-varnish_la_LDFLAGS = -module -avoid-version
+varnish_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 varnish_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBVARNISH_CFLAGS)
 varnish_la_LIBADD = $(BUILD_WITH_LIBVARNISH_LIBS)
-collectd_LDADD += "-dlopen" varnish.la
-collectd_DEPENDENCIES += varnish.la
+endif
+
+if BUILD_PLUGIN_VIRT
+pkglib_LTLIBRARIES += virt.la
+virt_la_SOURCES = virt.c \
+                 utils_ignorelist.c utils_ignorelist.h
+virt_la_CFLAGS = $(AM_CFLAGS) \
+               $(BUILD_WITH_LIBVIRT_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
+virt_la_LIBADD = $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
+virt_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_VMEM
 pkglib_LTLIBRARIES += vmem.la
 vmem_la_SOURCES = vmem.c
-vmem_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" vmem.la
-collectd_DEPENDENCIES += vmem.la
+vmem_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_VSERVER
 pkglib_LTLIBRARIES += vserver.la
 vserver_la_SOURCES = vserver.c
-vserver_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" vserver.la
-collectd_DEPENDENCIES += vserver.la
+vserver_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_WIRELESS
 pkglib_LTLIBRARIES += wireless.la
 wireless_la_SOURCES = wireless.c
-wireless_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" wireless.la
-collectd_DEPENDENCIES += wireless.la
+wireless_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_WRITE_GRAPHITE
@@ -1412,24 +1148,20 @@ pkglib_LTLIBRARIES += write_graphite.la
 write_graphite_la_SOURCES = write_graphite.c \
                         utils_format_graphite.c utils_format_graphite.h \
                         utils_format_json.c utils_format_json.h
-write_graphite_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" write_graphite.la
-collectd_DEPENDENCIES += write_graphite.la
+write_graphite_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_WRITE_HTTP
 pkglib_LTLIBRARIES += write_http.la
 write_http_la_SOURCES = write_http.c \
                        utils_format_json.c utils_format_json.h
-write_http_la_LDFLAGS = -module -avoid-version
+write_http_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 write_http_la_CFLAGS = $(AM_CFLAGS)
 write_http_la_LIBADD =
-collectd_LDADD += "-dlopen" write_http.la
 if BUILD_WITH_LIBCURL
 write_http_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
 write_http_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
 endif
-collectd_DEPENDENCIES += write_http.la
 endif
 
 if BUILD_PLUGIN_WRITE_KAFKA
@@ -1439,65 +1171,53 @@ write_kafka_la_SOURCES = write_kafka.c \
                         utils_format_json.c utils_format_json.h \
                         utils_cmd_putval.c utils_cmd_putval.h \
                         utils_crc32.c utils_crc32.h
-write_kafka_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBRDKAFKA_LDFLAGS)
+write_kafka_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRDKAFKA_LDFLAGS)
 write_kafka_la_LIBADD = $(BUILD_WITH_LIBRDKAFKA_LIBS)
-collectd_LDADD += "-dlopen" write_kafka.la
-collectd_DEPENDENCIES += write_kafka.la
 endif
 
 if BUILD_PLUGIN_WRITE_MONGODB
 pkglib_LTLIBRARIES += write_mongodb.la
 write_mongodb_la_SOURCES = write_mongodb.c
 write_mongodb_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMONGOC_CPPFLAGS)
-write_mongodb_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBMONGOC_LDFLAGS)
+write_mongodb_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMONGOC_LDFLAGS)
 write_mongodb_la_LIBADD = -lmongoc
-collectd_LDADD += "-dlopen" write_mongodb.la
-collectd_DEPENDENCIES += write_mongodb.la
 endif
 
 if BUILD_PLUGIN_WRITE_REDIS
 pkglib_LTLIBRARIES += write_redis.la
 write_redis_la_SOURCES = write_redis.c
-write_redis_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBCREDIS_LDFLAGS)
+write_redis_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBCREDIS_LDFLAGS)
 write_redis_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCREDIS_CPPFLAGS)
 write_redis_la_LIBADD = -lcredis
-collectd_LDADD += "-dlopen" write_redis.la
-collectd_DEPENDENCIES += write_redis.la
 endif
 
 if BUILD_PLUGIN_WRITE_RIEMANN
 pkglib_LTLIBRARIES += write_riemann.la
 write_riemann_la_SOURCES = write_riemann.c write_riemann_threshold.c
 nodist_write_riemann_la_SOURCES = riemann.pb-c.c riemann.pb-c.h
-write_riemann_la_LDFLAGS = -module -avoid-version
+write_riemann_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 write_riemann_la_LIBADD = -lprotobuf-c
-collectd_LDADD += "-dlopen" write_riemann.la
-collectd_DEPENDENCIES += write_riemann.la
 endif
 
 if BUILD_PLUGIN_WRITE_TSDB
 pkglib_LTLIBRARIES += write_tsdb.la
 write_tsdb_la_SOURCES = write_tsdb.c
-write_tsdb_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" write_tsdb.la
-collectd_DEPENDENCIES += write_tsdb.la
+write_tsdb_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
 if BUILD_PLUGIN_XMMS
 pkglib_LTLIBRARIES += xmms.la
 xmms_la_SOURCES = xmms.c
 xmms_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBXMMS_CFLAGS)
-xmms_la_LDFLAGS = -module -avoid-version
+xmms_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 xmms_la_LIBADD = $(BUILD_WITH_LIBXMMS_LIBS)
-collectd_LDADD += "-dlopen" xmms.la
-collectd_DEPENDENCIES += xmms.la
 endif
 
 if BUILD_PLUGIN_ZFS_ARC
 pkglib_LTLIBRARIES += zfs_arc.la
 zfs_arc_la_SOURCES = zfs_arc.c
 zfs_arc_la_CFLAGS = $(AM_CFLAGS)
-zfs_arc_la_LDFLAGS = -module -avoid-version
+zfs_arc_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 if BUILD_FREEBSD
 zfs_arc_la_LIBADD = -lm
 else
@@ -1508,8 +1228,6 @@ else
 zfs_arc_la_LIBADD = -lkstat
 endif
 endif
-collectd_LDADD += "-dlopen" zfs_arc.la
-collectd_DEPENDENCIES += zfs_arc.la
 endif
 
 BUILT_SOURCES += $(dist_man_MANS)
@@ -1609,7 +1327,7 @@ uninstall-hook:
 check_PROGRAMS = test_common test_utils_avltree test_utils_heap test_utils_mount test_utils_vl_lookup
 
 test_common_SOURCES = tests/test_common.c \
-                      common.h common.c \
+                      daemon/common.h daemon/common.c \
                       tests/mock/plugin.c \
                       tests/mock/utils_cache.c \
                       tests/mock/utils_time.c
@@ -1618,20 +1336,20 @@ test_common_LDFLAGS = -export-dynamic
 test_common_LDADD =
 
 test_utils_avltree_SOURCES = tests/test_utils_avltree.c \
-                             utils_avltree.c utils_avltree.h
+                             daemon/utils_avltree.c daemon/utils_avltree.h
 test_utils_avltree_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL)
 test_utils_avltree_LDFLAGS = -export-dynamic
 test_utils_avltree_LDADD =
 
 test_utils_heap_SOURCES = tests/test_utils_heap.c \
-                          utils_heap.c utils_heap.h
+                          daemon/utils_heap.c daemon/utils_heap.h
 test_utils_heap_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL)
 test_utils_heap_LDFLAGS = -export-dynamic
 test_utils_heap_LDADD =
 
 test_utils_mount_SOURCES = tests/test_utils_mount.c \
                            utils_mount.c utils_mount.h \
-                           common.c common.h \
+                           daemon/common.c daemon/common.h \
                            tests/mock/plugin.c \
                            tests/mock/utils_cache.c \
                            tests/mock/utils_time.c
@@ -1641,8 +1359,8 @@ test_utils_mount_LDADD =
 
 test_utils_vl_lookup_SOURCES = tests/test_utils_vl_lookup.c \
                                utils_vl_lookup.h utils_vl_lookup.c \
-                               utils_avltree.c utils_avltree.h \
-                               common.c common.h \
+                               daemon/utils_avltree.c daemon/utils_avltree.h \
+                               daemon/common.c daemon/common.h \
                                tests/mock/plugin.c \
                                tests/mock/utils_cache.c \
                                tests/mock/utils_time.c
index ad5975c..75ef3e1 100644 (file)
@@ -45,8 +45,8 @@ struct apache_s
        char *url;
        char *user;
        char *pass;
-       int   verify_peer;
-       int   verify_host;
+       _Bool verify_peer;
+       _Bool verify_host;
        char *cacert;
        char *server; /* user specific server type */
        char *apache_buffer;
@@ -165,94 +165,21 @@ static size_t apache_header_callback (void *buf, size_t size, size_t nmemb,
  *   URL ...
  * </Plugin>
  */
-static int config_set_string (char **ret_string, /* {{{ */
-                                   oconfig_item_t *ci)
-{
-       char *string;
-
-       if ((ci->values_num != 1)
-                       || (ci->values[0].type != OCONFIG_TYPE_STRING))
-       {
-               WARNING ("apache plugin: The `%s' config option "
-                               "needs exactly one string argument.", ci->key);
-               return (-1);
-       }
-
-       string = strdup (ci->values[0].value.string);
-       if (string == NULL)
-       {
-               ERROR ("apache plugin: strdup failed.");
-               return (-1);
-       }
-
-       if (*ret_string != NULL)
-               free (*ret_string);
-       *ret_string = string;
-
-       return (0);
-} /* }}} int config_set_string */
-
-static int config_set_boolean (int *ret_boolean, /* {{{ */
-                                   oconfig_item_t *ci)
-{
-       if ((ci->values_num != 1)
-                       || ((ci->values[0].type != OCONFIG_TYPE_BOOLEAN)
-                               && (ci->values[0].type != OCONFIG_TYPE_STRING)))
-       {
-               WARNING ("apache plugin: The `%s' config option "
-                               "needs exactly one boolean argument.", ci->key);
-               return (-1);
-       }
-
-       if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
-       {
-               if (ci->values[0].value.boolean)
-                       *ret_boolean = 1;
-               else
-                       *ret_boolean = 0;
-       }
-       else /* if (ci->values[0].type != OCONFIG_TYPE_STRING) */
-       {
-               char *string = ci->values[0].value.string;
-               if (IS_TRUE (string))
-                       *ret_boolean = 1;
-               else if (IS_FALSE (string))
-                       *ret_boolean = 0;
-               else
-               {
-                       ERROR ("apache plugin: Cannot parse string "
-                                       "as boolean value: %s", string);
-                       return (-1);
-               }
-       }
-
-       return (0);
-} /* }}} int config_set_boolean */
-
 static int config_add (oconfig_item_t *ci)
 {
        apache_t *st;
        int i;
        int status;
 
-       if ((ci->values_num != 1)
-               || (ci->values[0].type != OCONFIG_TYPE_STRING))
-       {
-               WARNING ("apache plugin: The `%s' config option "
-                       "needs exactly one string argument.", ci->key);
-               return (-1);
-       }
-
-       st = (apache_t *) malloc (sizeof (*st));
+       st = malloc (sizeof (*st));
        if (st == NULL)
        {
                ERROR ("apache plugin: malloc failed.");
                return (-1);
        }
-
        memset (st, 0, sizeof (*st));
 
-       status = config_set_string (&st->name, ci);
+       status = cf_util_get_string (ci, &st->name);
        if (status != 0)
        {
                sfree (st);
@@ -265,21 +192,21 @@ static int config_add (oconfig_item_t *ci)
                oconfig_item_t *child = ci->children + i;
 
                if (strcasecmp ("URL", child->key) == 0)
-                       status = config_set_string (&st->url, child);
+                       status = cf_util_get_string (child, &st->url);
                else if (strcasecmp ("Host", child->key) == 0)
-                       status = config_set_string (&st->host, child);
+                       status = cf_util_get_string (child, &st->host);
                else if (strcasecmp ("User", child->key) == 0)
-                       status = config_set_string (&st->user, child);
+                       status = cf_util_get_string (child, &st->user);
                else if (strcasecmp ("Password", child->key) == 0)
-                       status = config_set_string (&st->pass, child);
+                       status = cf_util_get_string (child, &st->pass);
                else if (strcasecmp ("VerifyPeer", child->key) == 0)
-                       status = config_set_boolean (&st->verify_peer, child);
+                       status = cf_util_get_boolean (child, &st->verify_peer);
                else if (strcasecmp ("VerifyHost", child->key) == 0)
-                       status = config_set_boolean (&st->verify_host, child);
+                       status = cf_util_get_boolean (child, &st->verify_host);
                else if (strcasecmp ("CACert", child->key) == 0)
-                       status = config_set_string (&st->cacert, child);
+                       status = cf_util_get_string (child, &st->cacert);
                else if (strcasecmp ("Server", child->key) == 0)
-                       status = config_set_string (&st->server, child);
+                       status = cf_util_get_string (child, &st->server);
                else
                {
                        WARNING ("apache plugin: Option `%s' not allowed here.",
@@ -324,7 +251,7 @@ static int config_add (oconfig_item_t *ci)
 
        if (status != 0)
        {
-               apache_free(st);
+               apache_free (st);
                return (-1);
        }
 
@@ -428,28 +355,12 @@ static int init_host (apache_t *st) /* {{{ */
        curl_easy_setopt (st->curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt (st->curl, CURLOPT_MAXREDIRS, 50L);
 
-       if (st->verify_peer != 0)
-       {
-               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYPEER, 1L);
-       }
-       else
-       {
-               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYPEER, 0L);
-       }
-
-       if (st->verify_host != 0)
-       {
-               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYHOST, 2L);
-       }
-       else
-       {
-               curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYHOST, 0L);
-       }
-
+       curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYPEER,
+                       (long) st->verify_peer);
+       curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYHOST,
+                       st->verify_host ? 2L : 0L);
        if (st->cacert != NULL)
-       {
                curl_easy_setopt (st->curl, CURLOPT_CAINFO, st->cacert);
-       }
 
        return (0);
 } /* }}} int init_host */
index ce58181..62c2964 100644 (file)
@@ -1,7 +1,8 @@
 /**
  * collectd - src/battery.c
- * Copyright (C) 2006,2007  Florian octo Forster
+ * Copyright (C) 2006-2014  Florian octo Forster
  * Copyright (C) 2008       Michał Mirosław
+ * Copyright (C) 2014       Andy Parkins
  *
  * 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
@@ -19,6 +20,7 @@
  * Authors:
  *   Florian octo Forster <octo at collectd.org>
  *   Michał Mirosław <mirq-linux at rere.qmqm.pl>
+ *   Andy Parkins <andyp at fussylogic.co.uk>
  **/
 
 #include "collectd.h"
 # error "No applicable input method."
 #endif
 
-#define INVALID_VALUE 47841.29
-
 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
        /* No global variables */
 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
 
 #elif KERNEL_LINUX
-static int   battery_pmu_num = 0;
-static char *battery_pmu_file = "/proc/pmu/battery_%i";
-static const char *battery_acpi_dir = "/proc/acpi/battery";
+# define PROC_PMU_PATH_FORMAT "/proc/pmu/battery_%i"
+# define PROC_ACPI_PATH "/proc/acpi/battery"
+# define PROC_ACPI_FACTOR 0.001
+# define SYSFS_PATH "/sys/class/power_supply"
+# define SYSFS_FACTOR 0.000001
 #endif /* KERNEL_LINUX */
 
-static int battery_init (void)
-{
-#if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
-       /* No init necessary */
-/* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
-
-#elif KERNEL_LINUX
-       int len;
-       char filename[128];
-
-       for (battery_pmu_num = 0; ; battery_pmu_num++)
-       {
-               len = ssnprintf (filename, sizeof (filename), battery_pmu_file, battery_pmu_num);
-
-               if ((len < 0) || ((unsigned int)len >= sizeof (filename)))
-                       break;
+static _Bool report_percent = 0;
+static _Bool report_degraded = 0;
 
-               if (access (filename, R_OK))
-                       break;
-       }
-#endif /* KERNEL_LINUX */
-
-       return (0);
-}
-
-static void battery_submit (const char *plugin_instance, const char *type, double value)
+static void battery_submit2 (char const *plugin_instance, /* {{{ */
+               char const *type, char const *type_instance, gauge_t value)
 {
        value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
@@ -106,12 +87,63 @@ static void battery_submit (const char *plugin_instance, const char *type, doubl
        sstrncpy (vl.plugin, "battery", sizeof (vl.plugin));
        sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
        sstrncpy (vl.type, type, sizeof (vl.type));
+       if (type_instance != NULL)
+               sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
-} /* void battery_submit */
+} /* }}} void battery_submit2 */
+
+static void battery_submit (char const *plugin_instance, /* {{{ */
+               char const *type, gauge_t value)
+{
+       battery_submit2 (plugin_instance, type, NULL, value);
+} /* }}} void battery_submit */
+
+static void submit_capacity (char const *plugin_instance, /* {{{ */
+               gauge_t capacity_charged,
+               gauge_t capacity_full,
+               gauge_t capacity_design)
+{
+       if (report_percent && (capacity_charged > capacity_full))
+               return;
+       if (report_degraded && (capacity_full > capacity_design))
+               return;
+
+       if (report_percent)
+       {
+               gauge_t capacity_max;
+
+               if (report_degraded)
+                       capacity_max = capacity_design;
+               else
+                       capacity_max = capacity_full;
+
+               battery_submit2 (plugin_instance, "percent", "charged",
+                               100.0 * capacity_charged / capacity_max);
+               battery_submit2 (plugin_instance, "percent", "discharged",
+                               100.0 * (capacity_full - capacity_charged) / capacity_max);
+
+               if (report_degraded)
+                       battery_submit2 (plugin_instance, "percent", "degraded",
+                                       100.0 * (capacity_design - capacity_full) / capacity_max);
+       }
+       else if (report_degraded) /* && !report_percent */
+       {
+               battery_submit2 (plugin_instance, "capacity", "charged",
+                               capacity_charged);
+               battery_submit2 (plugin_instance, "capacity", "discharged",
+                               (capacity_full - capacity_charged));
+               battery_submit2 (plugin_instance, "capacity", "degraded",
+                               (capacity_design - capacity_full));
+       }
+       else /* !report_percent && !report_degraded */
+       {
+               battery_submit (plugin_instance, "capacity", capacity_charged);
+       }
+} /* }}} void submit_capacity */
 
 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H || HAVE_IOKIT_IOKITLIB_H
-double dict_get_double (CFDictionaryRef dict, char *key_string)
+static double dict_get_double (CFDictionaryRef dict, char *key_string) /* {{{ */
 {
        double      val_double;
        long long   val_int;
@@ -119,18 +151,18 @@ double dict_get_double (CFDictionaryRef dict, char *key_string)
        CFStringRef key_obj;
 
        key_obj = CFStringCreateWithCString (kCFAllocatorDefault, key_string,
-                       kCFStringEncodingASCII);
+                       kCFStringEncodingASCII);
        if (key_obj == NULL)
        {
                DEBUG ("CFStringCreateWithCString (%s) failed.\n", key_string);
-               return (INVALID_VALUE);
+               return (NAN);
        }
 
        if ((val_obj = CFDictionaryGetValue (dict, key_obj)) == NULL)
        {
                DEBUG ("CFDictionaryGetValue (%s) failed.", key_string);
                CFRelease (key_obj);
-               return (INVALID_VALUE);
+               return (NAN);
        }
        CFRelease (key_obj);
 
@@ -153,15 +185,14 @@ double dict_get_double (CFDictionaryRef dict, char *key_string)
        else
        {
                DEBUG ("CFGetTypeID (val_obj) = %i", (int) CFGetTypeID (val_obj));
-               return (INVALID_VALUE);
+               return (NAN);
        }
 
        return (val_double);
-}
-#endif /* HAVE_IOKIT_PS_IOPOWERSOURCES_H || HAVE_IOKIT_IOKITLIB_H */
+} /* }}} double dict_get_double */
 
-#if HAVE_IOKIT_PS_IOPOWERSOURCES_H
-static void get_via_io_power_sources (double *ret_charge,
+# if HAVE_IOKIT_PS_IOPOWERSOURCES_H
+static void get_via_io_power_sources (double *ret_charge, /* {{{ */
                double *ret_current,
                double *ret_voltage)
 {
@@ -199,41 +230,42 @@ static void get_via_io_power_sources (double *ret_charge,
 
                /* FIXME: Check if this is really an internal battery */
 
-               if (*ret_charge == INVALID_VALUE)
+               if (isnan (*ret_charge))
                {
                        /* This is the charge in percent. */
                        temp_double = dict_get_double (ps_dict,
                                        kIOPSCurrentCapacityKey);
-                       if ((temp_double != INVALID_VALUE)
+                       if (!isnan ((temp_double))
                                        && (temp_double >= 0.0)
                                        && (temp_double <= 100.0))
                                *ret_charge = temp_double;
                }
 
-               if (*ret_current == INVALID_VALUE)
+               if (isnan (*ret_current))
                {
                        temp_double = dict_get_double (ps_dict,
                                        kIOPSCurrentKey);
-                       if (temp_double != INVALID_VALUE)
+                       if (!isnan (temp_double))
                                *ret_current = temp_double / 1000.0;
                }
 
-               if (*ret_voltage == INVALID_VALUE)
+               if (isnan (*ret_voltage))
                {
                        temp_double = dict_get_double (ps_dict,
                                        kIOPSVoltageKey);
-                       if (temp_double != INVALID_VALUE)
+                       if (!isnan (temp_double))
                                *ret_voltage = temp_double / 1000.0;
                }
        }
 
        CFRelease(ps_array);
        CFRelease(ps_raw);
-}
-#endif /* HAVE_IOKIT_PS_IOPOWERSOURCES_H */
+} /* }}} void get_via_io_power_sources */
+# endif /* HAVE_IOKIT_PS_IOPOWERSOURCES_H */
 
-#if HAVE_IOKIT_IOKITLIB_H
-static void get_via_generic_iokit (double *ret_charge,
+# if HAVE_IOKIT_IOKITLIB_H
+static void get_via_generic_iokit (double *ret_capacity_full, /* {{{ */
+               double *ret_capacity_design,
                double *ret_current,
                double *ret_voltage)
 {
@@ -280,33 +312,33 @@ static void get_via_generic_iokit (double *ret_charge,
                bat_info_arry_len = CFArrayGetCount (bat_info_arry);
 
                for (bat_info_arry_pos = 0;
-                               bat_info_arry_pos < bat_info_arry_len;
-                               bat_info_arry_pos++)
+                               bat_info_arry_pos < bat_info_arry_len;
+                               bat_info_arry_pos++)
                {
                        bat_info_dict = (CFDictionaryRef) CFArrayGetValueAtIndex (bat_info_arry, bat_info_arry_pos);
 
-                       if (*ret_charge == INVALID_VALUE)
+                       if (isnan (*ret_capacity_full))
+                       {
+                               temp_double = dict_get_double (bat_info_dict, "Capacity");
+                               *ret_capacity_full = temp_double / 1000.0;
+                       }
+
+                       if (isnan (*ret_capacity_design))
                        {
-                               temp_double = dict_get_double (bat_info_dict,
-                                               "Capacity");
-                               if (temp_double != INVALID_VALUE)
-                                       *ret_charge = temp_double / 1000.0;
+                               temp_double = dict_get_double (bat_info_dict, "AbsoluteMaxCapacity");
+                               *ret_capacity_design = temp_double / 1000.0;
                        }
 
-                       if (*ret_current == INVALID_VALUE)
+                       if (isnan (*ret_current))
                        {
-                               temp_double = dict_get_double (bat_info_dict,
-                                               "Current");
-                               if (temp_double != INVALID_VALUE)
-                                       *ret_current = temp_double / 1000.0;
+                               temp_double = dict_get_double (bat_info_dict, "Current");
+                               *ret_current = temp_double / 1000.0;
                        }
 
-                       if (*ret_voltage == INVALID_VALUE)
+                       if (isnan (*ret_voltage))
                        {
-                               temp_double = dict_get_double (bat_info_dict,
-                                               "Voltage");
-                               if (temp_double != INVALID_VALUE)
-                                       *ret_voltage = temp_double / 1000.0;
+                               temp_double = dict_get_double (bat_info_dict, "Voltage");
+                               *ret_voltage = temp_double / 1000.0;
                        }
                }
                
@@ -314,38 +346,274 @@ static void get_via_generic_iokit (double *ret_charge,
        }
 
        IOObjectRelease (iterator);
-}
-#endif /* HAVE_IOKIT_IOKITLIB_H */
+} /* }}} void get_via_generic_iokit */
+# endif /* HAVE_IOKIT_IOKITLIB_H */
 
-#if KERNEL_LINUX
-static int battery_read_acpi (const char __attribute__((unused)) *dir,
-               const char *name, void __attribute__((unused)) *user_data)
+static int battery_read (void) /* {{{ */
 {
-       double  current = INVALID_VALUE;
-       double  voltage = INVALID_VALUE;
-       double  charge  = INVALID_VALUE;
-       double *valptr = NULL;
-       int charging = 0;
+       gauge_t current = NAN; /* Current in A */
+       gauge_t voltage = NAN; /* Voltage in V */
 
-       char filename[256];
-       FILE *fh;
+       /* We only get the charged capacity as a percentage from
+        * IOPowerSources. IOKit, on the other hand, only reports the full
+        * capacity. We use the two to calculate the current charged capacity. */
+       gauge_t charge_rel = NAN; /* Current charge in percent */
+       gauge_t capacity_charged = NAN; /* Charged capacity */
+       gauge_t capacity_full = NAN; /* Total capacity */
+       gauge_t capacity_design = NAN; /* Full design capacity */
 
-       char buffer[1024];
-       char *fields[8];
-       int numfields;
-       char *endptr;
-       int len;
+#if HAVE_IOKIT_PS_IOPOWERSOURCES_H
+       get_via_io_power_sources (&charge_rel, &current, &voltage);
+#endif
+#if HAVE_IOKIT_IOKITLIB_H
+       get_via_generic_iokit (&capacity_full, &capacity_design, &current, &voltage);
+#endif
+
+       capacity_charged = charge_rel * capacity_full / 100.0;
+       submit_capacity ("0", capacity_charged, capacity_full, capacity_design);
+
+       if (!isnan (current))
+               battery_submit ("0", "current", current);
+       if (!isnan (voltage))
+               battery_submit ("0", "voltage", voltage);
+} /* }}} int battery_read */
+/* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
+
+#elif KERNEL_LINUX
+/* Reads a file which contains only a number (and optionally a trailing
+ * newline) and parses that number. */
+static int sysfs_file_to_buffer(char const *dir, /* {{{ */
+               char const *power_supply,
+               char const *basename,
+               char *buffer, size_t buffer_size)
+{
+       int status;
+       FILE *fp;
+       char filename[PATH_MAX];
 
-       len = ssnprintf (filename, sizeof (filename), "%s/%s/state", battery_acpi_dir, name);
+       ssnprintf (filename, sizeof (filename), "%s/%s/%s",
+                       dir, power_supply, basename);
 
-       if ((len < 0) || ((unsigned int)len >= sizeof (filename)))
-               return -1;
+       /* No file isn't the end of the world -- not every system will be
+        * reporting the same set of statistics */
+       if (access (filename, R_OK) != 0)
+               return ENOENT;
 
-       if ((fh = fopen (filename, "r")) == NULL) {
+       fp = fopen (filename, "r");
+       if (fp == NULL)
+       {
+               status = errno;
+               if (status != ENOENT)
+               {
+                       char errbuf[1024];
+                       WARNING ("battery plugin: fopen (%s) failed: %s", filename,
+                                       sstrerror (status, errbuf, sizeof (errbuf)));
+               }
+               return status;
+       }
+
+       if (fgets (buffer, buffer_size, fp) == NULL)
+       {
                char errbuf[1024];
-               ERROR ("Cannot open `%s': %s", filename,
-                       sstrerror (errno, errbuf, sizeof (errbuf)));
-               return -1;
+               status = errno;
+               WARNING ("battery plugin: fgets failed: %s",
+                               sstrerror (status, errbuf, sizeof (errbuf)));
+               fclose (fp);
+               return status;
+       }
+
+       strstripnewline (buffer);
+
+       fclose (fp);
+       return 0;
+} /* }}} int sysfs_file_to_buffer */
+
+/* Reads a file which contains only a number (and optionally a trailing
+ * newline) and parses that number. */
+static int sysfs_file_to_gauge(char const *dir, /* {{{ */
+               char const *power_supply,
+               char const *basename, gauge_t *ret_value)
+{
+       int status;
+       char buffer[32] = "";
+
+       status = sysfs_file_to_buffer (dir, power_supply, basename, buffer, sizeof (buffer));
+       if (status != 0)
+               return (status);
+
+       return (strtogauge (buffer, ret_value));
+} /* }}} sysfs_file_to_gauge */
+
+static int read_sysfs_capacity (char const *dir, /* {{{ */
+               char const *power_supply,
+               char const *plugin_instance)
+{
+       gauge_t capacity_charged = NAN;
+       gauge_t capacity_full = NAN;
+       gauge_t capacity_design = NAN;
+       int status;
+
+       status = sysfs_file_to_gauge (dir, power_supply, "energy_now", &capacity_charged);
+       if (status != 0)
+               return (status);
+
+       status = sysfs_file_to_gauge (dir, power_supply, "energy_full", &capacity_full);
+       if (status != 0)
+               return (status);
+
+       status = sysfs_file_to_gauge (dir, power_supply, "energy_full_design", &capacity_design);
+       if (status != 0)
+               return (status);
+
+       submit_capacity (plugin_instance,
+                       capacity_charged * SYSFS_FACTOR,
+                       capacity_full * SYSFS_FACTOR,
+                       capacity_design * SYSFS_FACTOR);
+       return (0);
+} /* }}} int read_sysfs_capacity */
+
+static int read_sysfs_callback (char const *dir, /* {{{ */
+               char const *power_supply,
+               void *user_data)
+{
+       int *battery_index = user_data;
+
+       char const *plugin_instance;
+       char buffer[32];
+       gauge_t v = NAN;
+       _Bool discharging = 0;
+       int status;
+
+       /* Ignore non-battery directories, such as AC power. */
+       status = sysfs_file_to_buffer (dir, power_supply, "type", buffer, sizeof (buffer));
+       if (status != 0)
+               return (0);
+       if (strcasecmp ("Battery", buffer) != 0)
+               return (0);
+
+       (void) sysfs_file_to_buffer (dir, power_supply, "status", buffer, sizeof (buffer));
+       if (strcasecmp ("Discharging", buffer) == 0)
+               discharging = 1;
+
+       /* FIXME: This is a dirty hack for backwards compatibility: The battery
+        * plugin, for a very long time, has had the plugin_instance
+        * hard-coded to "0". So, to keep backwards compatibility, we'll use
+        * "0" for the first battery we find and the power_supply name for all
+        * following. This should be reverted in a future major version. */
+       plugin_instance = (*battery_index == 0) ? "0" : power_supply;
+       (*battery_index)++;
+
+       read_sysfs_capacity (dir, power_supply, plugin_instance);
+
+       if (sysfs_file_to_gauge (dir, power_supply, "power_now", &v) == 0)
+       {
+               if (discharging)
+                       v *= -1.0;
+               battery_submit (plugin_instance, "power", v * SYSFS_FACTOR);
+       }
+
+       if (sysfs_file_to_gauge (dir, power_supply, "voltage_now", &v) == 0)
+               battery_submit (plugin_instance, "voltage", v * SYSFS_FACTOR);
+#if 0
+       if (sysfs_file_to_gauge (dir, power_supply, "voltage_min_design", &v) == 0)
+               battery_submit (plugin_instance, "voltage", v * SYSFS_FACTOR);
+#endif
+
+       return (0);
+} /* }}} int read_sysfs_callback */
+
+static int read_sysfs (void) /* {{{ */
+{
+       int status;
+       int battery_counter = 0;
+
+       if (access (SYSFS_PATH, R_OK) != 0)
+               return (ENOENT);
+
+       status = walk_directory (SYSFS_PATH, read_sysfs_callback,
+                       /* user_data = */ &battery_counter,
+                       /* include hidden */ 0);
+       return (status);
+} /* }}} int read_sysfs */
+
+static int read_acpi_full_capacity (char const *dir, /* {{{ */
+               char const *power_supply,
+               gauge_t *ret_capacity_full,
+               gauge_t *ret_capacity_design)
+
+{
+       char filename[PATH_MAX];
+       char buffer[1024];
+
+       FILE *fh;
+
+       ssnprintf (filename, sizeof (filename), "%s/%s/info", dir, power_supply);
+       fh = fopen (filename, "r");
+       if ((fh = fopen (filename, "r")) == NULL)
+               return (errno);
+
+       /* last full capacity:      40090 mWh */
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               gauge_t *value_ptr;
+               int fields_num;
+               char *fields[8];
+               int index;
+
+               if (strncmp ("last full capacity:", buffer, strlen ("last full capacity:")) == 0)
+               {
+                       value_ptr = ret_capacity_full;
+                       index = 3;
+               }
+               else if (strncmp ("design capacity:", buffer, strlen ("design capacity:")) == 0)
+               {
+                       value_ptr = ret_capacity_design;
+                       index = 2;
+               }
+               else
+               {
+                       continue;
+               }
+
+               fields_num = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+               if (fields_num <= index)
+                       continue;
+
+               strtogauge (fields[index], value_ptr);
+       }
+
+       fclose (fh);
+       return (0);
+} /* }}} int read_acpi_full_capacity */
+
+static int read_acpi_callback (char const *dir, /* {{{ */
+               char const *power_supply,
+               void *user_data)
+{
+       int *battery_index = user_data;
+
+       gauge_t power = NAN;
+       gauge_t voltage = NAN;
+       gauge_t capacity_charged = NAN;
+       gauge_t capacity_full = NAN;
+       gauge_t capacity_design = NAN;
+       _Bool charging = 0;
+       _Bool is_current = 0;
+
+       char const *plugin_instance;
+       char filename[PATH_MAX];
+       char buffer[1024];
+
+       FILE *fh;
+
+       ssnprintf (filename, sizeof (filename), "%s/%s/state", dir, power_supply);
+       fh = fopen (filename, "r");
+       if ((fh = fopen (filename, "r")) == NULL)
+       {
+               if ((errno == EAGAIN) || (errno == EINTR) || (errno == ENOENT))
+                       return (0);
+               else
+                       return (errno);
        }
 
        /*
@@ -359,8 +627,10 @@ static int battery_read_acpi (const char __attribute__((unused)) *dir,
         */
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
-               numfields = strsplit (buffer, fields, 8);
+               char *fields[8];
+               int numfields;
 
+               numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
                if (numfields < 3)
                        continue;
 
@@ -374,164 +644,180 @@ static int battery_read_acpi (const char __attribute__((unused)) *dir,
                        continue;
                }
 
+               /* The unit of "present rate" depends on the battery. Modern
+                * batteries export power (watts), older batteries (used to)
+                * export current (amperes). We check the fourth column and try
+                * to find old batteries this way. */
                if ((strcmp (fields[0], "present") == 0)
                                && (strcmp (fields[1], "rate:") == 0))
-                       valptr = &current;
+               {
+                       strtogauge (fields[2], &power);
+
+                       if ((numfields >= 4) && (strcmp ("mA", fields[3]) == 0))
+                               is_current = 1;
+               }
                else if ((strcmp (fields[0], "remaining") == 0)
                                && (strcmp (fields[1], "capacity:") == 0))
-                       valptr = &charge;
+                       strtogauge (fields[2], &capacity_charged);
                else if ((strcmp (fields[0], "present") == 0)
                                && (strcmp (fields[1], "voltage:") == 0))
-                       valptr = &voltage;
-               else
-                       continue;
-
-               endptr = NULL;
-               errno  = 0;
-               *valptr = strtod (fields[2], &endptr) / 1000.0;
-
-               if ((fields[2] == endptr) || (errno != 0))
-                       *valptr = INVALID_VALUE;
+                       strtogauge (fields[2], &voltage);
        } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
 
        fclose (fh);
 
-       if ((current != INVALID_VALUE) && (charging == 0))
-                       current *= -1;
+       if (!charging)
+               power *= -1.0;
 
-       if (charge != INVALID_VALUE)
-               battery_submit ("0", "charge", charge);
-       if (current != INVALID_VALUE)
-               battery_submit ("0", "current", current);
-       if (voltage != INVALID_VALUE)
-               battery_submit ("0", "voltage", voltage);
+       /* FIXME: This is a dirty hack for backwards compatibility: The battery
+        * plugin, for a very long time, has had the plugin_instance
+        * hard-coded to "0". So, to keep backwards compatibility, we'll use
+        * "0" for the first battery we find and the power_supply name for all
+        * following. This should be reverted in a future major version. */
+       plugin_instance = (*battery_index == 0) ? "0" : power_supply;
+       (*battery_index)++;
 
-       return 0;
-}
-#endif /* KERNEL_LINUX */
+       read_acpi_full_capacity (dir, power_supply, &capacity_full, &capacity_design);
 
+       submit_capacity (plugin_instance,
+                       capacity_charged * PROC_ACPI_FACTOR,
+                       capacity_full * PROC_ACPI_FACTOR,
+                       capacity_design * PROC_ACPI_FACTOR);
 
-static int battery_read (void)
-{
-#if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
-       double charge  = INVALID_VALUE; /* Current charge in Ah */
-       double current = INVALID_VALUE; /* Current in A */
-       double voltage = INVALID_VALUE; /* Voltage in V */
-
-       double charge_rel = INVALID_VALUE; /* Current charge in percent */
-       double charge_abs = INVALID_VALUE; /* Total capacity */
+       battery_submit (plugin_instance,
+                       is_current ? "current" : "power",
+                       power * PROC_ACPI_FACTOR);
+       battery_submit (plugin_instance, "voltage", voltage * PROC_ACPI_FACTOR);
 
-#if HAVE_IOKIT_PS_IOPOWERSOURCES_H
-       get_via_io_power_sources (&charge_rel, &current, &voltage);
-#endif
-#if HAVE_IOKIT_IOKITLIB_H
-       get_via_generic_iokit (&charge_abs, &current, &voltage);
-#endif
-
-       if ((charge_rel != INVALID_VALUE) && (charge_abs != INVALID_VALUE))
-               charge = charge_abs * charge_rel / 100.0;
+       return 0;
+} /* }}} int read_acpi_callback */
 
-       if (charge != INVALID_VALUE)
-               battery_submit ("0", "charge", charge);
-       if (current != INVALID_VALUE)
-               battery_submit ("0", "current", current);
-       if (voltage != INVALID_VALUE)
-               battery_submit ("0", "voltage", voltage);
-/* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
+static int read_acpi (void) /* {{{ */
+{
+       int status;
+       int battery_counter = 0;
 
-#elif KERNEL_LINUX
-       static c_complain_t acpi_dir_complaint = C_COMPLAIN_INIT_STATIC;
+       if (access (PROC_ACPI_PATH, R_OK) != 0)
+               return (ENOENT);
 
-       FILE *fh;
-       char buffer[1024];
-       char filename[256];
-       
-       char *fields[8];
-       int numfields;
+       status = walk_directory (PROC_ACPI_PATH, read_acpi_callback,
+                       /* user_data = */ &battery_counter,
+                       /* include hidden */ 0);
+       return (status);
+} /* }}} int read_acpi */
 
+static int read_pmu (void) /* {{{ */
+{
        int i;
-       int len;
 
-       for (i = 0; i < battery_pmu_num; i++)
+       /* The upper limit here is just a safeguard. If there is a system with
+        * more than 100 batteries, this can easily be increased. */
+       for (i = 0; i < 100; i++)
        {
-               char    batnum_str[256];
-               double  current = INVALID_VALUE;
-               double  voltage = INVALID_VALUE;
-               double  charge  = INVALID_VALUE;
-               double *valptr = NULL;
-
-               len = ssnprintf (filename, sizeof (filename), battery_pmu_file, i);
-               if ((len < 0) || ((unsigned int)len >= sizeof (filename)))
-                       continue;
+               FILE *fh;
 
-               len = ssnprintf (batnum_str, sizeof (batnum_str), "%i", i);
-               if ((len < 0) || ((unsigned int)len >= sizeof (batnum_str)))
-                       continue;
+               char buffer[1024];
+               char filename[PATH_MAX];
+               char plugin_instance[DATA_MAX_NAME_LEN];
 
-               if ((fh = fopen (filename, "r")) == NULL)
-                       continue;
+               gauge_t current = NAN;
+               gauge_t voltage = NAN;
+               gauge_t charge  = NAN;
+
+               ssnprintf (filename, sizeof (filename), PROC_PMU_PATH_FORMAT, i);
+               if (access (filename, R_OK) != 0)
+                       break;
+
+               ssnprintf (plugin_instance, sizeof (plugin_instance), "%i", i);
+
+               fh = fopen (filename, "r");
+               if (fh == NULL)
+               {
+                       if (errno == ENOENT)
+                               break;
+                       else if ((errno == EAGAIN) || (errno == EINTR))
+                               continue;
+                       else
+                               return (errno);
+               }
 
                while (fgets (buffer, sizeof (buffer), fh) != NULL)
                {
-                       numfields = strsplit (buffer, fields, 8);
+                       char *fields[8];
+                       int numfields;
 
+                       numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
                        if (numfields < 3)
                                continue;
 
                        if (strcmp ("current", fields[0]) == 0)
-                               valptr = &current;
+                               strtogauge (fields[2], &current);
                        else if (strcmp ("voltage", fields[0]) == 0)
-                               valptr = &voltage;
+                               strtogauge (fields[2], &voltage);
                        else if (strcmp ("charge", fields[0]) == 0)
-                               valptr = &charge;
-                       else
-                               valptr = NULL;
-
-                       if (valptr != NULL)
-                       {
-                               char *endptr;
-
-                               endptr = NULL;
-                               errno  = 0;
-
-                               *valptr = strtod (fields[2], &endptr) / 1000.0;
-
-                               if ((fields[2] == endptr) || (errno != 0))
-                                       *valptr = INVALID_VALUE;
-                       }
+                               strtogauge (fields[2], &charge);
                }
 
                fclose (fh);
                fh = NULL;
 
-               if (charge != INVALID_VALUE)
-                       battery_submit ("0", "charge", charge);
-               if (current != INVALID_VALUE)
-                       battery_submit ("0", "current", current);
-               if (voltage != INVALID_VALUE)
-                       battery_submit ("0", "voltage", voltage);
+               battery_submit (plugin_instance, "charge", charge / 1000.0);
+               battery_submit (plugin_instance, "current", current / 1000.0);
+               battery_submit (plugin_instance, "voltage", voltage / 1000.0);
        }
 
-       if (0 == access (battery_acpi_dir, R_OK))
-               walk_directory (battery_acpi_dir, battery_read_acpi,
-                               /* user_data = */ NULL,
-                               /* include hidden */ 0);
-       else
-       {
-               char errbuf[1024];
-               c_complain_once (LOG_WARNING, &acpi_dir_complaint,
-                               "battery plugin: Failed to access `%s': %s",
-                               battery_acpi_dir,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-       }
+       if (i == 0)
+               return (ENOENT);
+       return (0);
+} /* }}} int read_pmu */
 
+static int battery_read (void) /* {{{ */
+{
+       int status;
+
+       DEBUG ("battery plugin: Trying sysfs ...");
+       status = read_sysfs ();
+       if (status == 0)
+               return (0);
+
+       DEBUG ("battery plugin: Trying acpi ...");
+       status = read_acpi ();
+       if (status == 0)
+               return (0);
+
+       DEBUG ("battery plugin: Trying pmu ...");
+       status = read_pmu ();
+       if (status == 0)
+               return (0);
+
+       ERROR ("battery plugin: Add available input methods failed.");
+       return (-1);
+} /* }}} int battery_read */
 #endif /* KERNEL_LINUX */
 
+static int battery_config (oconfig_item_t *ci)
+{
+       int i;
+
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if (strcasecmp ("ValuesPercentage", child->key) == 0)
+                       cf_util_get_boolean (child, &report_percent);
+               else if (strcasecmp ("ReportDegraded", child->key) == 0)
+                       cf_util_get_boolean (child, &report_degraded);
+               else
+                       WARNING ("battery plugin: Ignoring unknown "
+                                       "configuration option \"%s\".",
+                                       child->key);
+       }
+
        return (0);
-}
+} /* }}} int battery_config */
 
 void module_register (void)
 {
-       plugin_register_init ("battery", battery_init);
+       plugin_register_complex_config ("battery", battery_config);
        plugin_register_read ("battery", battery_read);
 } /* void module_register */
index 2f990a2..d7b8c08 100644 (file)
@@ -627,6 +627,84 @@ static int bind_parse_generic_value_list (const char *xpath_expression, /* {{{ *
   return (0);
 } /* }}} int bind_parse_generic_value_list */
 
+/*
+ * bind_parse_generic_name_attr_value_list
+ *
+ * Reads statistics in the form:
+ * <foo>
+ *   <counter name="name0">123</counter>
+ *   <counter name="name1">234</counter>
+ *   <counter name="name2">345</counter>
+ *   :
+ * </foo>
+ */
+static int bind_parse_generic_name_attr_value_list (const char *xpath_expression, /* {{{ */
+    list_callback_t list_callback,
+    void *user_data,
+    xmlDoc *doc, xmlXPathContext *xpathCtx,
+    time_t current_time, int ds_type)
+{
+  xmlXPathObject *xpathObj = NULL;
+  int num_entries;
+  int i;
+
+  xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
+  if (xpathObj == NULL)
+  {
+    ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
+        xpath_expression);
+    return (-1);
+  }
+
+  num_entries = 0;
+  /* Iterate over all matching nodes. */
+  for (i = 0; xpathObj->nodesetval && (i < xpathObj->nodesetval->nodeNr); i++)
+  {
+    xmlNode *child;
+
+    /* Iterate over all child nodes. */
+    for (child = xpathObj->nodesetval->nodeTab[i]->xmlChildrenNode;
+        child != NULL;
+        child = child->next)
+    {
+      if (child->type != XML_ELEMENT_NODE)
+        continue;
+
+      if (strncmp ("counter", (char *) child->name, strlen ("counter")) != 0)
+        continue;
+
+      char *attr_name;
+      value_t value;
+      int status;
+
+      attr_name = (char *) xmlGetProp (child, BAD_CAST "name");
+      if (attr_name == NULL)
+      {
+        DEBUG ("bind plugin: found <counter> without name.");
+        continue;
+      }
+      if (ds_type == DS_TYPE_GAUGE)
+        status = bind_xml_read_gauge (doc, child, &value.gauge);
+      else
+        status = bind_xml_read_derive (doc, child, &value.derive);
+      if (status != 0)
+        continue;
+
+      status = (*list_callback) (attr_name, value, current_time, user_data);
+      if (status == 0)
+        num_entries++;
+    }
+  }
+
+  DEBUG ("bind plugin: Found %d %s for XPath expression `%s'",
+      num_entries, (num_entries == 1) ? "entry" : "entries",
+      xpath_expression);
+
+  xmlXPathFreeObject(xpathObj);
+
+  return (0);
+} /* }}} int bind_parse_generic_name_attr_value_list */
+
 static int bind_xml_stats_handle_zone (int version, xmlDoc *doc, /* {{{ */
     xmlXPathContext *path_ctx, xmlNode *node, cb_view_t *view,
     time_t current_time)
@@ -745,45 +823,68 @@ static int bind_xml_stats_search_zones (int version, xmlDoc *doc, /* {{{ */
 static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */
     xmlXPathContext *path_ctx, xmlNode *node, time_t current_time)
 {
-  xmlXPathObject *path_obj;
   char *view_name = NULL;
   cb_view_t *view;
   int i;
   size_t j;
 
-  path_obj = xmlXPathEvalExpression (BAD_CAST "name", path_ctx);
-  if (path_obj == NULL)
+  if (version == 3)
   {
-    ERROR ("bind plugin: xmlXPathEvalExpression failed.");
-    return (-1);
-  }
+    view_name = (char*) xmlGetProp(node, BAD_CAST "name");
 
-  for (i = 0; path_obj->nodesetval && (i < path_obj->nodesetval->nodeNr); i++)
-  {
-    view_name = (char *) xmlNodeListGetString (doc,
-        path_obj->nodesetval->nodeTab[i]->xmlChildrenNode, 1);
-    if (view_name != NULL)
-      break;
-  }
+    if (view_name == NULL)
+    {
+      ERROR ("bind plugin: Could not determine view name.");
+      return (-1);
+    }
+
+    for (j = 0; j < views_num; j++)
+    {
+      if (strcasecmp (view_name, views[j].name) == 0)
+        break;
+    }
 
-  if (view_name == NULL)
+    xmlFree (view_name);
+    view_name = NULL;
+  }
+  else
   {
-    ERROR ("bind plugin: Could not determine view name.");
+    xmlXPathObject *path_obj;
+    path_obj = xmlXPathEvalExpression (BAD_CAST "name", path_ctx);
+    if (path_obj == NULL)
+    {
+      ERROR ("bind plugin: xmlXPathEvalExpression failed.");
+      return (-1);
+    }
+
+    for (i = 0; path_obj->nodesetval && (i < path_obj->nodesetval->nodeNr); i++)
+    {
+      view_name = (char *) xmlNodeListGetString (doc,
+          path_obj->nodesetval->nodeTab[i]->xmlChildrenNode, 1);
+      if (view_name != NULL)
+        break;
+    }
+
+    if (view_name == NULL)
+    {
+      ERROR ("bind plugin: Could not determine view name.");
+      xmlXPathFreeObject (path_obj);
+      return (-1);
+    }
+
+    for (j = 0; j < views_num; j++)
+    {
+      if (strcasecmp (view_name, views[j].name) == 0)
+        break;
+    }
+
+    xmlFree (view_name);
     xmlXPathFreeObject (path_obj);
-    return (-1);
-  }
 
-  for (j = 0; j < views_num; j++)
-  {
-    if (strcasecmp (view_name, views[j].name) == 0)
-      break;
+    view_name = NULL;
+    path_obj = NULL;
   }
 
-  xmlFree (view_name);
-  xmlXPathFreeObject (path_obj);
-
-  view_name = NULL;
-  path_obj = NULL;
 
   if (j >= views_num)
     return (0);
@@ -804,11 +905,20 @@ static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */
 
     ssnprintf (plugin_instance, sizeof (plugin_instance), "%s-qtypes",
         view->name);
-
-    bind_parse_generic_name_value (/* xpath = */ "rdtype",
+    if (version == 3)
+    {
+      bind_parse_generic_name_attr_value_list (/* xpath = */ "counters[@type='resqtype']",
         /* callback = */ bind_xml_list_callback,
         /* user_data = */ &list_info,
         doc, path_ctx, current_time, DS_TYPE_COUNTER);
+    }
+    else
+    {
+      bind_parse_generic_name_value (/* xpath = */ "rdtype",
+        /* callback = */ bind_xml_list_callback,
+        /* user_data = */ &list_info,
+        doc, path_ctx, current_time, DS_TYPE_COUNTER);
+    }
   } /* }}} */
 
   if (view->resolver_stats != 0) /* {{{ */
@@ -823,11 +933,20 @@ static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */
 
     ssnprintf (plugin_instance, sizeof (plugin_instance),
         "%s-resolver_stats", view->name);
-
-    bind_parse_generic_name_value ("resstat",
-        /* callback = */ bind_xml_table_callback,
-        /* user_data = */ &table_ptr,
-        doc, path_ctx, current_time, DS_TYPE_COUNTER);
+    if (version == 3)
+    {
+      bind_parse_generic_name_attr_value_list ("counters[@type='resstats']",
+          /* callback = */ bind_xml_table_callback,
+          /* user_data = */ &table_ptr,
+          doc, path_ctx, current_time, DS_TYPE_COUNTER);
+    }
+    else
+    {
+      bind_parse_generic_name_value ("resstat",
+          /* callback = */ bind_xml_table_callback,
+          /* user_data = */ &table_ptr,
+          doc, path_ctx, current_time, DS_TYPE_COUNTER);
+    }
   } /* }}} */
 
   /* Record types in the cache */
@@ -849,7 +968,8 @@ static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */
         doc, path_ctx, current_time, DS_TYPE_GAUGE);
   } /* }}} */
 
-  if (view->zones_num > 0)
+  // v3 does not provide per-zone stats any more
+  if (version < 3 && view->zones_num > 0)
     bind_xml_stats_search_zones (version, doc, path_ctx, node, view,
         current_time);
 
@@ -896,28 +1016,145 @@ static int bind_xml_stats_search_views (int version, xmlDoc *doc, /* {{{ */
   return (0);
 } /* }}} int bind_xml_stats_search_views */
 
-static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */
-    xmlXPathContext *xpathCtx, xmlNode *statsnode)
+static void bind_xml_stats_v3 (xmlDoc *doc, /* {{{ */
+    xmlXPathContext *xpathCtx, xmlNode *statsnode, time_t current_time)
 {
-  time_t current_time = 0;
-  int status;
+  /* XPath:     server/counters[@type='opcode']
+   * Variables: QUERY, IQUERY, NOTIFY, UPDATE, ...
+   * Layout v3:
+   *   <counters type="opcode">
+   *     <counter name="A">1</counter>
+   *     :
+   *   </counters>
+   */
+  if (global_opcodes != 0)
+  {
+    list_info_ptr_t list_info =
+    {
+      /* plugin instance = */ "global-opcodes",
+      /* type = */ "dns_opcode"
+    };
+    bind_parse_generic_name_attr_value_list (/* xpath = */ "server/counters[@type='opcode']",
+      /* callback = */ bind_xml_list_callback,
+      /* user_data = */ &list_info,
+      doc, xpathCtx, current_time, DS_TYPE_COUNTER);
+  }
 
-  xpathCtx->node = statsnode;
+  /* XPath:     server/counters[@type='qtype']
+   * Variables: RESERVED0, A, NS, CNAME, SOA, MR, PTR, HINFO, MX, TXT, RP,
+   *            X25, PX, AAAA, LOC, SRV, NAPTR, A6, DS, RRSIG, NSEC, DNSKEY,
+   *            SPF, TKEY, IXFR, AXFR, ANY, ..., Others
+   * Layout v3:
+   *   <counters type="opcode">
+   *     <counter name="A">1</counter>
+   *     :
+   *   </counters>
+   */
+  if (global_qtypes != 0)
+  {
+    list_info_ptr_t list_info =
+    {
+      /* plugin instance = */ "global-qtypes",
+      /* type = */ "dns_qtype"
+    };
 
-  /* TODO: Check `server/boot-time' to recognize server restarts. */
+    bind_parse_generic_name_attr_value_list (/* xpath = */ "server/counters[@type='qtype']",
+        /* callback = */ bind_xml_list_callback,
+        /* user_data = */ &list_info,
+        doc, xpathCtx, current_time, DS_TYPE_COUNTER);
+  }
 
-  status = bind_xml_read_timestamp ("server/current-time",
-      doc, xpathCtx, &current_time);
-  if (status != 0)
+  /* XPath:     server/counters[@type='nsstat']
+   * Variables: Requestv4, Requestv6, ReqEdns0, ReqBadEDNSVer, ReqTSIG,
+   *            ReqSIG0, ReqBadSIG, ReqTCP, AuthQryRej, RecQryRej, XfrRej,
+   *            UpdateRej, Response, TruncatedResp, RespEDNS0, RespTSIG,
+   *            RespSIG0, QrySuccess, QryAuthAns, QryNoauthAns, QryReferral,
+   *            QryNxrrset, QrySERVFAIL, QryFORMERR, QryNXDOMAIN, QryRecursion,
+   *            QryDuplicate, QryDropped, QryFailure, XfrReqDone, UpdateReqFwd,
+   *            UpdateRespFwd, UpdateFwdFail, UpdateDone, UpdateFail,
+   *            UpdateBadPrereq
+   * Layout v3:
+   *   <counters type="nsstat"
+   *     <counter name="Requestv4">1</counter>
+   *     <counter name="Requestv6">0</counter>
+   *     :
+   *   </counter>
+   */
+  if (global_server_stats)
   {
-    ERROR ("bind plugin: Reading `server/current-time' failed.");
-    return (-1);
+    translation_table_ptr_t table_ptr =
+    {
+      nsstats_translation_table,
+      nsstats_translation_table_length,
+      /* plugin_instance = */ "global-server_stats"
+    };
+
+    bind_parse_generic_name_attr_value_list ("server/counters[@type='nsstat']",
+        /* callback = */ bind_xml_table_callback,
+        /* user_data = */ &table_ptr,
+        doc, xpathCtx, current_time, DS_TYPE_COUNTER);
   }
-  DEBUG ("bind plugin: Current server time is %i.", (int) current_time);
 
-  /* XPath:     server/requests/opcode
+  /* XPath:     server/zonestats, server/zonestat, server/counters[@type='zonestat']
+   * Variables: NotifyOutv4, NotifyOutv6, NotifyInv4, NotifyInv6, NotifyRej,
+   *            SOAOutv4, SOAOutv6, AXFRReqv4, AXFRReqv6, IXFRReqv4, IXFRReqv6,
+   *            XfrSuccess, XfrFail
+   * Layout v3:
+   *   <counters type="zonestat"
+   *     <counter name="NotifyOutv4">0</counter>
+   *     <counter name="NotifyOutv6">0</counter>
+   *     :
+   *   </counter>
+   */
+  if (global_zone_maint_stats)
+  {
+    translation_table_ptr_t table_ptr =
+    {
+      zonestats_translation_table,
+      zonestats_translation_table_length,
+      /* plugin_instance = */ "global-zone_maint_stats"
+    };
+
+    bind_parse_generic_name_attr_value_list ("server/counters[@type='zonestat']",
+        /* callback = */ bind_xml_table_callback,
+        /* user_data = */ &table_ptr,
+        doc, xpathCtx, current_time, DS_TYPE_COUNTER);
+  }
+
+  /* XPath:     server/resstats, server/counters[@type='resstat']
+   * Variables: Queryv4, Queryv6, Responsev4, Responsev6, NXDOMAIN, SERVFAIL,
+   *            FORMERR, OtherError, EDNS0Fail, Mismatch, Truncated, Lame,
+   *            Retry, GlueFetchv4, GlueFetchv6, GlueFetchv4Fail,
+   *            GlueFetchv6Fail, ValAttempt, ValOk, ValNegOk, ValFail
+   * Layout v3:
+   *   <counters type="resstat"
+   *     <counter name="Queryv4">0</counter>
+   *     <counter name="Queryv6">0</counter>
+   *     :
+   *   </counter>
+   */
+  if (global_resolver_stats != 0)
+  {
+    translation_table_ptr_t table_ptr =
+    {
+      resstats_translation_table,
+      resstats_translation_table_length,
+      /* plugin_instance = */ "global-resolver_stats"
+    };
+
+    bind_parse_generic_name_attr_value_list ("server/counters[@type='resstat']",
+        /* callback = */ bind_xml_table_callback,
+        /* user_data = */ &table_ptr,
+        doc, xpathCtx, current_time, DS_TYPE_COUNTER);
+  }
+} /* }}} bind_xml_stats_v3 */
+
+static void bind_xml_stats_v1_v2 (int version, xmlDoc *doc, /* {{{ */
+    xmlXPathContext *xpathCtx, xmlNode *statsnode, time_t current_time)
+{
+  /* XPath:     server/requests/opcode, server/counters[@type='opcode']
    * Variables: QUERY, IQUERY, NOTIFY, UPDATE, ...
-   * Layout:
+   * Layout V1 and V2:
    *   <opcode>
    *     <name>A</name>
    *     <counter>1</counter>
@@ -938,11 +1175,11 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */
         doc, xpathCtx, current_time, DS_TYPE_COUNTER);
   }
 
-  /* XPath:     server/queries-in/rdtype
+  /* XPath:     server/queries-in/rdtype, server/counters[@type='qtype']
    * Variables: RESERVED0, A, NS, CNAME, SOA, MR, PTR, HINFO, MX, TXT, RP,
    *            X25, PX, AAAA, LOC, SRV, NAPTR, A6, DS, RRSIG, NSEC, DNSKEY,
    *            SPF, TKEY, IXFR, AXFR, ANY, ..., Others
-   * Layout:
+   * Layout v1 or v2:
    *   <rdtype>
    *     <name>A</name>
    *     <counter>1</counter>
@@ -962,8 +1199,8 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */
         /* user_data = */ &list_info,
         doc, xpathCtx, current_time, DS_TYPE_COUNTER);
   }
-  
-  /* XPath:     server/nsstats, server/nsstat
+
+  /* XPath:     server/nsstats, server/nsstat, server/counters[@type='nsstat']
    * Variables: Requestv4, Requestv6, ReqEdns0, ReqBadEDNSVer, ReqTSIG,
    *            ReqSIG0, ReqBadSIG, ReqTCP, AuthQryRej, RecQryRej, XfrRej,
    *            UpdateRej, Response, TruncatedResp, RespEDNS0, RespTSIG,
@@ -992,7 +1229,7 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */
   if (global_server_stats)
   {
     translation_table_ptr_t table_ptr =
-    { 
+    {
       nsstats_translation_table,
       nsstats_translation_table_length,
       /* plugin_instance = */ "global-server_stats"
@@ -1014,7 +1251,7 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */
     }
   }
 
-  /* XPath:     server/zonestats, server/zonestat
+  /* XPath:     server/zonestats, server/zonestat, server/counters[@type='zonestat']
    * Variables: NotifyOutv4, NotifyOutv6, NotifyInv4, NotifyInv6, NotifyRej,
    *            SOAOutv4, SOAOutv6, AXFRReqv4, AXFRReqv6, IXFRReqv4, IXFRReqv6,
    *            XfrSuccess, XfrFail
@@ -1038,7 +1275,7 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */
   if (global_zone_maint_stats)
   {
     translation_table_ptr_t table_ptr =
-    { 
+    {
       zonestats_translation_table,
       zonestats_translation_table_length,
       /* plugin_instance = */ "global-zone_maint_stats"
@@ -1060,7 +1297,7 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */
     }
   }
 
-  /* XPath:     server/resstats
+  /* XPath:     server/resstats, server/counters[@type='resstat']
    * Variables: Queryv4, Queryv6, Responsev4, Responsev6, NXDOMAIN, SERVFAIL,
    *            FORMERR, OtherError, EDNS0Fail, Mismatch, Truncated, Lame,
    *            Retry, GlueFetchv4, GlueFetchv6, GlueFetchv4Fail,
@@ -1085,7 +1322,7 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */
   if (global_resolver_stats != 0)
   {
     translation_table_ptr_t table_ptr =
-    { 
+    {
       resstats_translation_table,
       resstats_translation_table_length,
       /* plugin_instance = */ "global-resolver_stats"
@@ -1106,10 +1343,39 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */
           doc, xpathCtx, current_time, DS_TYPE_COUNTER);
     }
   }
+} /* }}} bind_xml_stats_v1_v2 */
+
+static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */
+    xmlXPathContext *xpathCtx, xmlNode *statsnode)
+{
+  time_t current_time = 0;
+  int status;
+
+  xpathCtx->node = statsnode;
+
+  /* TODO: Check `server/boot-time' to recognize server restarts. */
+
+  status = bind_xml_read_timestamp ("server/current-time",
+      doc, xpathCtx, &current_time);
+  if (status != 0)
+  {
+    ERROR ("bind plugin: Reading `server/current-time' failed.");
+    return (-1);
+  }
+  DEBUG ("bind plugin: Current server time is %i.", (int) current_time);
+
+  if (version == 3)
+  {
+    bind_xml_stats_v3(doc, xpathCtx, statsnode, current_time);
+  }
+  else
+  {
+    bind_xml_stats_v1_v2(version, doc, xpathCtx, statsnode, current_time);
+  }
 
   /* XPath:  memory/summary
    * Variables: TotalUse, InUse, BlockSize, ContextSize, Lost
-   * Layout: v2:
+   * Layout: v2 and v3:
    *   <summary>
    *     <TotalUse>6587096</TotalUse>
    *     <InUse>1345424</InUse>
@@ -1163,6 +1429,64 @@ static int bind_xml (const char *data) /* {{{ */
     return (-1);
   }
 
+  //
+  // version 3.* of statistics XML (since BIND9.9)
+  //
+
+  xpathObj = xmlXPathEvalExpression (BAD_CAST "/statistics", xpathCtx);
+  if (xpathObj == NULL || xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr == 0)
+  {
+    DEBUG ("bind plugin: Statistics appears not to be v3");
+    // we will fallback to v1 or v2 detection
+    if (xpathObj != NULL) { xmlXPathFreeObject (xpathObj); }
+  }
+  else
+  {
+    for (i = 0; i < xpathObj->nodesetval->nodeNr; i++)
+    {
+      xmlNode *node;
+      char *attr_version;
+
+      node = xpathObj->nodesetval->nodeTab[i];
+      assert (node != NULL);
+
+      attr_version = (char *) xmlGetProp (node, BAD_CAST "version");
+      if (attr_version == NULL)
+      {
+        NOTICE ("bind plugin: Found <statistics> tag doesn't have a "
+            "`version' attribute.");
+        continue;
+      }
+      DEBUG ("bind plugin: Found: <statistics version=\"%s\">", attr_version);
+
+      if (strncmp ("3.", attr_version, strlen ("3.")) != 0)
+      {
+        /* TODO: Use the complaint mechanism here. */
+        NOTICE ("bind plugin: Found <statistics> tag with version `%s'. "
+            "Unfortunately I have no clue how to parse that. "
+            "Please open a bug report for this.", attr_version);
+        xmlFree (attr_version);
+        continue;
+      }
+      ret = bind_xml_stats (3, doc, xpathCtx, node);
+
+      xmlFree (attr_version);
+      /* One <statistics> node ought to be enough. */
+      break;
+    }
+
+    // we are finished, early-return
+    xmlXPathFreeObject (xpathObj);
+    xmlXPathFreeContext (xpathCtx);
+    xmlFreeDoc (doc);
+
+    return (ret);
+  }
+
+  //
+  // versions 1.* or 2.* of statistics XML
+  //
+
   xpathObj = xmlXPathEvalExpression (BAD_CAST "/isc/bind/statistics", xpathCtx);
   if (xpathObj == NULL)
   {
index 6645614..d04d0f0 100644 (file)
@@ -63,7 +63,7 @@ locale.
 
 =item B<ModulePath> I<Name>
 
-Appends I<Name> to B<sys.path>. You won't be able to import any scripts you
+Prepends I<Name> to B<sys.path>. You won't be able to import any scripts you
 wrote unless they are located in one of the directories in this list. Please
 note that it only has effect on plugins loaded after this option. You can
 use multiple B<ModulePath> lines to add more than one directory.
@@ -562,7 +562,7 @@ receive blocks where your callback identifier matches B<python.>I<blockname>.
 
 The callback will be called without arguments.
 
-=item register_read(callback[, interval][, data][, name]) -> identifier
+=item register_read(callback[, interval][, data][, name]) -> I<identifier>
 
 This function takes an additional parameter: I<interval>. It specifies the
 time between calls to the callback function.
@@ -616,7 +616,33 @@ I<identifier> is either the string that was returned by the register function
 or a callback function. The identifier will be constructed in the same way as
 for the register functions.
 
-=item B<flush>(I<plugin[, I<timeout>][, I<identifier>]) -> None
+=item B<get_dataset>(I<name>) -> I<definition>
+
+Returns the definition of a dataset specified by I<name>. I<definition> is a list
+of tuples, each representing one data source. Each tuple has 4 values:
+
+=over 4
+
+=item name
+
+A string, the name of the data source.
+
+=item type
+
+A string that is equal to either of the variables B<DS_TYPE_COUNTER>,
+B<DS_TYPE_GAUGE>, B<DS_TYPE_DERIVE> or B<DS_TYPE_ABSOLUTE>.
+
+=item min
+
+A float or None, the minimum value.
+
+=item max
+
+A float or None, the maximum value.
+
+=back
+
+=item B<flush>(I<plugin>[, I<timeout>][, I<identifier>]) -> None
 
 Flush one or all plugins. I<timeout> and the specified I<identifiers> are
 passed on to the registered flush-callbacks. If omitted, the timeout defaults
diff --git a/src/collectd.c b/src/collectd.c
deleted file mode 100644 (file)
index f711fb7..0000000
+++ /dev/null
@@ -1,624 +0,0 @@
-/**
- * collectd - src/collectd.c
- * Copyright (C) 2005-2007  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>
- *   Alvaro Barcellos <alvaro.barcellos at gmail.com>
- **/
-
-#include "collectd.h"
-#include "common.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-
-#include <pthread.h>
-
-#include "plugin.h"
-#include "configfile.h"
-
-#if HAVE_STATGRAB_H
-# include <statgrab.h>
-#endif
-
-/*
- * Global variables
- */
-char hostname_g[DATA_MAX_NAME_LEN];
-cdtime_t interval_g;
-int  pidfile_from_cli = 0;
-int  timeout_g;
-#if HAVE_LIBKSTAT
-kstat_ctl_t *kc;
-#endif /* HAVE_LIBKSTAT */
-
-static int loop = 0;
-
-static void *do_flush (void __attribute__((unused)) *arg)
-{
-       INFO ("Flushing all data.");
-       plugin_flush (/* plugin = */ NULL,
-                       /* timeout = */ 0,
-                       /* ident = */ NULL);
-       INFO ("Finished flushing all data.");
-       pthread_exit (NULL);
-       return NULL;
-}
-
-static void sig_int_handler (int __attribute__((unused)) signal)
-{
-       loop++;
-}
-
-static void sig_term_handler (int __attribute__((unused)) signal)
-{
-       loop++;
-}
-
-static void sig_usr1_handler (int __attribute__((unused)) signal)
-{
-       pthread_t      thread;
-       pthread_attr_t attr;
-
-       /* flushing the data might take a while,
-        * so it should be done asynchronously */
-       pthread_attr_init (&attr);
-       pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
-       pthread_create (&thread, &attr, do_flush, NULL);
-       pthread_attr_destroy (&attr);
-}
-
-static int init_hostname (void)
-{
-       const char *str;
-
-       struct addrinfo  ai_hints;
-       struct addrinfo *ai_list;
-       struct addrinfo *ai_ptr;
-       int status;
-
-       str = global_option_get ("Hostname");
-       if (str != NULL)
-       {
-               sstrncpy (hostname_g, str, sizeof (hostname_g));
-               return (0);
-       }
-
-       if (gethostname (hostname_g, sizeof (hostname_g)) != 0)
-       {
-               fprintf (stderr, "`gethostname' failed and no "
-                               "hostname was configured.\n");
-               return (-1);
-       }
-
-       str = global_option_get ("FQDNLookup");
-       if (IS_FALSE (str))
-               return (0);
-
-       memset (&ai_hints, '\0', sizeof (ai_hints));
-       ai_hints.ai_flags = AI_CANONNAME;
-
-       status = getaddrinfo (hostname_g, NULL, &ai_hints, &ai_list);
-       if (status != 0)
-       {
-               ERROR ("Looking up \"%s\" failed. You have set the "
-                               "\"FQDNLookup\" option, but I cannot resolve "
-                               "my hostname to a fully qualified domain "
-                               "name. Please fix the network "
-                               "configuration.", hostname_g);
-               return (-1);
-       }
-
-       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
-       {
-               if (ai_ptr->ai_canonname == NULL)
-                       continue;
-
-               sstrncpy (hostname_g, ai_ptr->ai_canonname, sizeof (hostname_g));
-               break;
-       }
-
-       freeaddrinfo (ai_list);
-       return (0);
-} /* int init_hostname */
-
-static int init_global_variables (void)
-{
-       char const *str;
-
-       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");
-       if (str == NULL)
-               str = "2";
-       timeout_g = atoi (str);
-       if (timeout_g <= 1)
-       {
-               fprintf (stderr, "Cannot set the timeout to a correct value.\n"
-                               "Please check your settings.\n");
-               return (-1);
-       }
-       DEBUG ("timeout_g = %i;", timeout_g);
-
-       if (init_hostname () != 0)
-               return (-1);
-       DEBUG ("hostname_g = %s;", hostname_g);
-
-       return (0);
-} /* int init_global_variables */
-
-static int change_basedir (const char *orig_dir)
-{
-       char *dir;
-       size_t dirlen;
-       int status;
-
-       dir = strdup (orig_dir);
-       if (dir == NULL)
-       {
-               char errbuf[1024];
-               ERROR ("strdup failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (-1);
-       }
-       
-       dirlen = strlen (dir);
-       while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
-               dir[--dirlen] = '\0';
-
-       if (dirlen <= 0)
-               return (-1);
-
-       status = chdir (dir);
-       if (status == 0)
-       {
-               free (dir);
-               return (0);
-       }
-       else if (errno != ENOENT)
-       {
-               char errbuf[1024];
-               ERROR ("change_basedir: chdir (%s): %s", dir,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               free (dir);
-               return (-1);
-       }
-
-       status = mkdir (dir, S_IRWXU | S_IRWXG | S_IRWXO);
-       if (status != 0)
-       {
-               char errbuf[1024];
-               ERROR ("change_basedir: mkdir (%s): %s", dir,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               free (dir);
-               return (-1);
-       }
-
-       status = chdir (dir);
-       if (status != 0)
-       {
-               char errbuf[1024];
-               ERROR ("change_basedir: chdir (%s): %s", dir,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               free (dir);
-               return (-1);
-       }
-
-       free (dir);
-       return (0);
-} /* static int change_basedir (char *dir) */
-
-#if HAVE_LIBKSTAT
-static void update_kstat (void)
-{
-       if (kc == NULL)
-       {
-               if ((kc = kstat_open ()) == NULL)
-                       ERROR ("Unable to open kstat control structure");
-       }
-       else
-       {
-               kid_t kid;
-               kid = kstat_chain_update (kc);
-               if (kid > 0)
-               {
-                       INFO ("kstat chain has been updated");
-                       plugin_init_all ();
-               }
-               else if (kid < 0)
-                       ERROR ("kstat chain update failed");
-               /* else: everything works as expected */
-       }
-
-       return;
-} /* static void update_kstat (void) */
-#endif /* HAVE_LIBKSTAT */
-
-/* TODO
- * Remove all settings but `-f' and `-C'
- */
-static void exit_usage (int status)
-{
-       printf ("Usage: "PACKAGE" [OPTIONS]\n\n"
-                       
-                       "Available options:\n"
-                       "  General:\n"
-                       "    -C <file>       Configuration file.\n"
-                       "                    Default: "CONFIGFILE"\n"
-                       "    -t              Test config and exit.\n"
-                       "    -T              Test plugin read and exit.\n"
-                       "    -P <file>       PID-file.\n"
-                       "                    Default: "PIDFILE"\n"
-#if COLLECT_DAEMON
-                       "    -f              Don't fork to the background.\n"
-#endif
-                       "    -h              Display help (this message)\n"
-                       "\nBuiltin defaults:\n"
-                       "  Config file       "CONFIGFILE"\n"
-                       "  PID file          "PIDFILE"\n"
-                       "  Plugin directory  "PLUGINDIR"\n"
-                       "  Data directory    "PKGLOCALSTATEDIR"\n"
-                       "\n"PACKAGE" "VERSION", http://collectd.org/\n"
-                       "by Florian octo Forster <octo@collectd.org>\n"
-                       "for contributions see `AUTHORS'\n");
-       exit (status);
-} /* static void exit_usage (int status) */
-
-static int do_init (void)
-{
-#if HAVE_LIBKSTAT
-       kc = NULL;
-       update_kstat ();
-#endif
-
-#if HAVE_LIBSTATGRAB
-       if (sg_init ())
-       {
-               ERROR ("sg_init: %s", sg_str_error (sg_get_error ()));
-               return (-1);
-       }
-
-       if (sg_drop_privileges ())
-       {
-               ERROR ("sg_drop_privileges: %s", sg_str_error (sg_get_error ()));
-               return (-1);
-       }
-#endif
-
-       plugin_init_all ();
-
-       return (0);
-} /* int do_init () */
-
-
-static int do_loop (void)
-{
-       cdtime_t interval = cf_get_default_interval ();
-       cdtime_t wait_until;
-
-       wait_until = cdtime () + interval;
-
-       while (loop == 0)
-       {
-               struct timespec ts_wait = { 0, 0 };
-               cdtime_t now;
-
-#if HAVE_LIBKSTAT
-               update_kstat ();
-#endif
-
-               /* Issue all plugins */
-               plugin_read_all ();
-
-               now = cdtime ();
-               if (now >= wait_until)
-               {
-                       WARNING ("Not sleeping because the next interval is "
-                                       "%.3f seconds in the past!",
-                                       CDTIME_T_TO_DOUBLE (now - wait_until));
-                       wait_until = now + interval;
-                       continue;
-               }
-
-               CDTIME_T_TO_TIMESPEC (wait_until - now, &ts_wait);
-               wait_until = wait_until + interval;
-
-               while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) != 0))
-               {
-                       if (errno != EINTR)
-                       {
-                               char errbuf[1024];
-                               ERROR ("nanosleep failed: %s",
-                                               sstrerror (errno, errbuf,
-                                                       sizeof (errbuf)));
-                               return (-1);
-                       }
-               }
-       } /* while (loop == 0) */
-
-       return (0);
-} /* int do_loop */
-
-static int do_shutdown (void)
-{
-       plugin_shutdown_all ();
-       return (0);
-} /* int do_shutdown */
-
-#if COLLECT_DAEMON
-static int pidfile_create (void)
-{
-       FILE *fh;
-       const char *file = global_option_get ("PIDFile");
-
-       if ((fh = fopen (file, "w")) == NULL)
-       {
-               char errbuf[1024];
-               ERROR ("fopen (%s): %s", file,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (1);
-       }
-
-       fprintf (fh, "%i\n", (int) getpid ());
-       fclose(fh);
-
-       return (0);
-} /* static int pidfile_create (const char *file) */
-
-static int pidfile_remove (void)
-{
-       const char *file = global_option_get ("PIDFile");
-
-       DEBUG ("unlink (%s)", (file != NULL) ? file : "<null>");
-       return (unlink (file));
-} /* static int pidfile_remove (const char *file) */
-#endif /* COLLECT_DAEMON */
-
-int main (int argc, char **argv)
-{
-       struct sigaction sig_int_action;
-       struct sigaction sig_term_action;
-       struct sigaction sig_usr1_action;
-       struct sigaction sig_pipe_action;
-       char *configfile = CONFIGFILE;
-       int test_config  = 0;
-       int test_readall = 0;
-       const char *basedir;
-#if COLLECT_DAEMON
-       struct sigaction sig_chld_action;
-       pid_t pid;
-       int daemonize    = 1;
-#endif
-       int exit_status = 0;
-
-       /* read options */
-       while (1)
-       {
-               int c;
-
-               c = getopt (argc, argv, "htTC:"
-#if COLLECT_DAEMON
-                               "fP:"
-#endif
-               );
-
-               if (c == -1)
-                       break;
-
-               switch (c)
-               {
-                       case 'C':
-                               configfile = optarg;
-                               break;
-                       case 't':
-                               test_config = 1;
-                               break;
-                       case 'T':
-                               test_readall = 1;
-                               global_option_set ("ReadThreads", "-1");
-#if COLLECT_DAEMON
-                               daemonize = 0;
-#endif /* COLLECT_DAEMON */
-                               break;
-#if COLLECT_DAEMON
-                       case 'P':
-                               global_option_set ("PIDFile", optarg);
-                               pidfile_from_cli = 1;
-                               break;
-                       case 'f':
-                               daemonize = 0;
-                               break;
-#endif /* COLLECT_DAEMON */
-                       case 'h':
-                               exit_usage (0);
-                               break;
-                       default:
-                               exit_usage (1);
-               } /* switch (c) */
-       } /* while (1) */
-
-       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
-        * general).
-        * Also, this will automatically load modules.
-        */
-       if (cf_read (configfile))
-       {
-               fprintf (stderr, "Error: Reading the config file failed!\n"
-                               "Read the syslog for details.\n");
-               return (1);
-       }
-
-       /*
-        * Change directory. We do this _after_ reading the config and loading
-        * modules to relative paths work as expected.
-        */
-       if ((basedir = global_option_get ("BaseDir")) == NULL)
-       {
-               fprintf (stderr, "Don't have a basedir to use. This should not happen. Ever.");
-               return (1);
-       }
-       else if (change_basedir (basedir))
-       {
-               fprintf (stderr, "Error: Unable to change to directory `%s'.\n", basedir);
-               return (1);
-       }
-
-       /*
-        * Set global variables or, if that failes, exit. We cannot run with
-        * them being uninitialized. If nothing is configured, then defaults
-        * are being used. So this means that the user has actually done
-        * something wrong.
-        */
-       if (init_global_variables () != 0)
-               return (1);
-
-       if (test_config)
-               return (0);
-
-#if COLLECT_DAEMON
-       /*
-        * fork off child
-        */
-       memset (&sig_chld_action, '\0', sizeof (sig_chld_action));
-       sig_chld_action.sa_handler = SIG_IGN;
-       sigaction (SIGCHLD, &sig_chld_action, NULL);
-
-       if (daemonize)
-       {
-               if ((pid = fork ()) == -1)
-               {
-                       /* error */
-                       char errbuf[1024];
-                       fprintf (stderr, "fork: %s",
-                                       sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-                       return (1);
-               }
-               else if (pid != 0)
-               {
-                       /* parent */
-                       /* printf ("Running (PID %i)\n", pid); */
-                       return (0);
-               }
-
-               /* Detach from session */
-               setsid ();
-
-               /* Write pidfile */
-               if (pidfile_create ())
-                       exit (2);
-
-               /* close standard descriptors */
-               close (2);
-               close (1);
-               close (0);
-
-               if (open ("/dev/null", O_RDWR) != 0)
-               {
-                       ERROR ("Error: Could not connect `STDIN' to `/dev/null'");
-                       return (1);
-               }
-               if (dup (0) != 1)
-               {
-                       ERROR ("Error: Could not connect `STDOUT' to `/dev/null'");
-                       return (1);
-               }
-               if (dup (0) != 2)
-               {
-                       ERROR ("Error: Could not connect `STDERR' to `/dev/null'");
-                       return (1);
-               }
-       } /* if (daemonize) */
-#endif /* COLLECT_DAEMON */
-
-       memset (&sig_pipe_action, '\0', sizeof (sig_pipe_action));
-       sig_pipe_action.sa_handler = SIG_IGN;
-       sigaction (SIGPIPE, &sig_pipe_action, NULL);
-
-       /*
-        * install signal handlers
-        */
-       memset (&sig_int_action, '\0', sizeof (sig_int_action));
-       sig_int_action.sa_handler = sig_int_handler;
-       if (0 != sigaction (SIGINT, &sig_int_action, NULL)) {
-               char errbuf[1024];
-               ERROR ("Error: Failed to install a signal handler for signal INT: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (1);
-       }
-
-       memset (&sig_term_action, '\0', sizeof (sig_term_action));
-       sig_term_action.sa_handler = sig_term_handler;
-       if (0 != sigaction (SIGTERM, &sig_term_action, NULL)) {
-               char errbuf[1024];
-               ERROR ("Error: Failed to install a signal handler for signal TERM: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (1);
-       }
-
-       memset (&sig_usr1_action, '\0', sizeof (sig_usr1_action));
-       sig_usr1_action.sa_handler = sig_usr1_handler;
-       if (0 != sigaction (SIGUSR1, &sig_usr1_action, NULL)) {
-               char errbuf[1024];
-               ERROR ("Error: Failed to install a signal handler for signal USR1: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (1);
-       }
-
-       /*
-        * run the actual loops
-        */
-       do_init ();
-
-       if (test_readall)
-       {
-               if (plugin_read_all_once () != 0)
-                       exit_status = 1;
-       }
-       else
-       {
-               INFO ("Initialization complete, entering read-loop.");
-               do_loop ();
-       }
-
-       /* close syslog */
-       INFO ("Exiting normally.");
-
-       do_shutdown ();
-
-#if COLLECT_DAEMON
-       if (daemonize)
-               pidfile_remove ();
-#endif /* COLLECT_DAEMON */
-
-       return (exit_status);
-} /* int main */
index b7f7b74..84410e0 100644 (file)
 #AutoLoadPlugin false
 
 #----------------------------------------------------------------------------#
+# When enabled, internal statistics are collected, using "collectd" as the   #
+# plugin name.                                                               #
+# Disabled by default.                                                       #
+#----------------------------------------------------------------------------#
+#CollectInternalStats false
+
+#----------------------------------------------------------------------------#
 # Interval at which to query values. This may be overwritten on a per-plugin #
 # base by using the 'Interval' option of the LoadPlugin block:               #
 #   <LoadPlugin foo>                                                         #
 #----------------------------------------------------------------------------#
 #Interval     10
 
-#Timeout      2
-#ReadThreads  5
-#WriteThreads 5
+#MaxReadInterval 86400
+#Timeout         2
+#ReadThreads     5
+#WriteThreads    5
 
 # Limit the size of the write queue. Default is no limit. Setting up a limit is
 # recommended for servers handling a high volume of traffic.
 #@BUILD_PLUGIN_IPVS_TRUE@LoadPlugin ipvs
 #@BUILD_PLUGIN_IRQ_TRUE@LoadPlugin irq
 #@BUILD_PLUGIN_JAVA_TRUE@LoadPlugin java
-#@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt
 @BUILD_PLUGIN_LOAD_TRUE@@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load
 #@BUILD_PLUGIN_LPAR_TRUE@LoadPlugin lpar
 #@BUILD_PLUGIN_LVM_TRUE@LoadPlugin lvm
 #@BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors
 #@BUILD_PLUGIN_SERIAL_TRUE@LoadPlugin serial
 #@BUILD_PLUGIN_SIGROK_TRUE@LoadPlugin sigrok
+#@BUILD_PLUGIN_SMART_TRUE@LoadPlugin smart
 #@BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp
 #@BUILD_PLUGIN_STATSD_TRUE@LoadPlugin statsd
 #@BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap
 #@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid
 #@BUILD_PLUGIN_VARNISH_TRUE@LoadPlugin varnish
 #@BUILD_PLUGIN_MIC_TRUE@LoadPlugin mic
+#@BUILD_PLUGIN_VIRT_TRUE@LoadPlugin virt
 #@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
 #@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
 #@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless
 #   TemperatureSensor "myserver/onewire-F10FCA000800/temperature"
 #</Plugin>
 
+#<Plugin "battery">
+#  ValuesPercentage false
+#  ReportDegraded
+#</Plugin>
+
 #<Plugin "bind">
 #  URL "http://localhost:8053/"
 #  ParseTime       false
 #</Plugin>
 
 #<Plugin cpu>
-#  ReportActive false
 #  ReportByCpu true
+#  ReportByState true
 #  ValuesPercentage false
 #</Plugin>
 #
 #    URL "http://finance.google.com/finance?q=NYSE%3AAMD"
 #    User "foo"
 #    Password "bar"
+#    Digest false
+#    VerifyPeer true
+#    VerifyHost true
+#    CACert "/path/to/ca.crt"
+#    Header "X-Custom-Header: foobar"
+#    Post "foo=bar"
+#
 #    MeasureResponseTime false
+#    MeasureResponseCode false
 #    <Match>
 #      Regex "<span +class=\"pr\"[^>]*> *([0-9]*\\.[0-9]+) *</span>"
 #      DSType "GaugeAverage"
 #    Instance "some_instance"
 #    User "collectd"
 #    Password "thaiNg0I"
+#    Digest false
 #    VerifyPeer true
 #    VerifyHost true
 #    CACert "/path/to/ca.crt"
+#    Header "X-Custom-Header: foobar"
+#    Post "foo=bar"
 #
 #    <XPath "table[@id=\"magic_level\"]/tr">
 #      Type "magic_level"
 #      </Plugin>
 #</Plugin>
 
-#<Plugin libvirt>
-#      Connection "xen:///"
-#      RefreshInterval 60
-#      Domain "name"
-#      BlockDevice "name:device"
-#      InterfaceDevice "name:device"
-#      IgnoreSelected false
-#      HostnameFormat name
-#      InterfaceFormat name
-#      PluginInstanceFormat name
-#</Plugin>
-
 #<Plugin load>
 #        ReportRelative true
 #</Plugin>
 #              Username "user"
 #              Password "secret"
 #              Interface "eth0"
+#              ResolveInterval 14400
 @LOAD_PLUGIN_NETWORK@  </Server>
 #      TimeToLive "128"
 #
 #              AuthFile "/etc/collectd/passwd"
 #              Interface "eth0"
 #      </Listen>
-#      MaxPacketSize 1024
+#      MaxPacketSize 1452
 #
 #      # proxy setup (client and server as above):
 #      Forward true
 #  </Device>
 #</Plugin>
 
+#<Plugin smart>
+#  Disk "/^[hs]d[a-f][0-9]?$/"
+#  IgnoreSelected false
+#</Plugin>
+
 #<Plugin snmp>
 #   <Data "powerplus_voltge_input">
 #       Type "voltage"
 
 #<Plugin tcpconns>
 #      ListeningPorts false
+#      AllPortsSummary false
 #      LocalPort "25"
 #      RemotePort "25"
 #</Plugin>
 #   </Instance>
 #</Plugin>
 
+#<Plugin virt>
+#      Connection "xen:///"
+#      RefreshInterval 60
+#      Domain "name"
+#      BlockDevice "name:device"
+#      InterfaceDevice "name:device"
+#      IgnoreSelected false
+#      HostnameFormat name
+#      InterfaceFormat name
+#      PluginInstanceFormat name
+#</Plugin>
+
 #<Plugin vmem>
 #      Verbose false
 #</Plugin>
 #              SSLVersion "TLSv1"
 #              Format "Command"
 #              StoreRates false
+#              BufferSize 4096
 #      </URL>
 #</Plugin>
 
index 9ca48b2..69c922b 100644 (file)
@@ -6,9 +6,9 @@ collectd.conf - Configuration for the system statistics collection daemon B<coll
 
 =head1 SYNOPSIS
 
-  BaseDir "/path/to/data/"
-  PIDFile "/path/to/pidfile/collectd.pid"
-  Server  "123.123.123.123" 12345
+  BaseDir "/var/lib/collectd"
+  PIDFile "/run/collectd.pid"
+  Interval 10.0
 
   LoadPlugin cpu
   LoadPlugin load
@@ -16,6 +16,9 @@ collectd.conf - Configuration for the system statistics collection daemon B<coll
   <LoadPlugin df>
     Interval 3600
   </LoadPlugin>
+  <Plugin df>
+    ValuesPercentage true
+  </Plugin>
 
   LoadPlugin ping
   <Plugin ping>
@@ -28,7 +31,9 @@ collectd.conf - Configuration for the system statistics collection daemon B<coll
 This config file controls how the system statistics collection daemon
 B<collectd> behaves. The most significant option is B<LoadPlugin>, which
 controls which plugins to load. These plugins ultimately define collectd's
-behavior.
+behavior. If the B<AutoLoadPlugin> option has been enabled, the explicit
+B<LoadPlugin> lines may be omitted for all plugins with a configuration block,
+i.e. a C<E<lt>PluginE<nbsp>...E<gt>> block.
 
 The syntax of this config file is similar to the config file of the famous
 I<Apache> webserver. Each line contains either an option (a key and a list of
@@ -55,8 +60,9 @@ indenting the wrapped lines.
 The configuration is read and processed in order, i.e. from top to bottom. So
 the plugins are loaded in the order listed in this config file. It is a good
 idea to load any logging plugins first in order to catch messages from plugins
-during configuration. Also, the C<LoadPlugin> option B<must> occur B<before>
-the appropriate C<E<lt>Plugin ...E<gt>> block.
+during configuration. Also, unless B<AutoLoadPlugin> is enabled, the
+B<LoadPlugin> option I<must> occur I<before> the appropriate
+C<E<lt>B<Plugin> ...E<gt>> block.
 
 =head1 GLOBAL OPTIONS
 
@@ -140,6 +146,18 @@ B<E<lt>PluginE<nbsp>...E<gt>> block acts as if it was immediately preceded by a
 B<LoadPlugin> statement. B<LoadPlugin> statements are still required for
 plugins that don't provide any configuration, e.g. the I<Load plugin>.
 
+=item B<CollectInternalStats> B<false>|B<true>
+
+When set to B<true>, various statistics about the I<collectd> daemon will be
+collected, with "collectd" as the I<plugin name>. Defaults to B<false>.
+
+The "write_queue" I<plugin instance> reports the number of elements currently
+queued and the number of elements dropped off the queue by the
+B<WriteQueueLimitLow>/B<WriteQueueLimitHigh> mechanism.
+
+The "cache" I<plugin instance> reports the number of elements in the value list
+cache (the cache you can interact with using L<collectd-unixsock(5)>).
+
 =item B<Include> I<Path> [I<pattern>]
 
 If I<Path> points to a file, includes that file. If I<Path> points to a
@@ -212,6 +230,14 @@ B<Warning:> You should set this once and then never touch it again. If you do,
 I<you will have to delete all your RRD files> or know some serious RRDtool
 magic! (Assuming you're using the I<RRDtool> or I<RRDCacheD> plugin.)
 
+=item B<MaxReadInterval> I<Seconds>
+
+Read plugin doubles interval between queries after each failed attempt
+to get data.
+
+This options limits the maximum value of the interval. The default value is
+B<86400>.
+
 =item B<Timeout> I<Iterations>
 
 Consider a value list "missing" when no update has been read or received for
@@ -262,8 +288,11 @@ If B<WriteQueueLimitHigh> is set to non-zero and B<WriteQueueLimitLow> is
 unset, the latter will default to half of B<WriteQueueLimitHigh>.
 
 If you do not want to randomly drop values when the queue size is between
-I<LowNum> and I<HighNum>, set If B<WriteQueueLimitHigh> and
-B<WriteQueueLimitLow> to same value.
+I<LowNum> and I<HighNum>, set B<WriteQueueLimitHigh> and B<WriteQueueLimitLow>
+to the same value.
+
+Enabling the B<CollectInternalStats> option is of great help to figure out the
+values to set B<WriteQueueLimitHigh> and B<WriteQueueLimitLow> to.
 
 =item B<Hostname> I<Name>
 
@@ -831,7 +860,7 @@ and are checked by default depends on the distribution you use.
 
 This plugin reads absolute air pressure using digital barometer sensor MPL115A2
 or MPL3115 from Freescale (sensor attached to any I2C bus available in
-the computer, for HW details see 
+the computer, for HW details see
 I<http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPL115A> or
 I<http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPL3115A2>).
 The sensor type - one fo these two - is detected automatically by the plugin
@@ -944,7 +973,7 @@ Temperature sensor which should be used as a reference when normalizing the pres
 When specified more sensors a minumum is found and uses each time.
 The temperature reading directly from this pressure sensor/plugin
 is typically not suitable as the pressure sensor
-will be probably inside while we want outside temperature. 
+will be probably inside while we want outside temperature.
 The collectd reference name is something like
 <hostname>/<plugin_name>-<plugin_instance>/<type>-<type_instance>
 (<type_instance> is usually omitted when there is just single value type).
@@ -952,6 +981,40 @@ Or you can figure it out from the path of the output data files.
 
 =back
 
+=head2 Plugin C<battery>
+
+The I<battery plugin> reports the remaining capacity, power and voltage of
+laptop batteries.
+
+=over 4
+
+=item B<ValuesPercentage> B<false>|B<true>
+
+When enabled, remaining capacity is reported as a percentage, e.g. "42%
+capacity remaining". Otherwise the capacity is stored as reported by the
+battery, most likely in "Wh". This option does not work with all input methods,
+in particular when only C</proc/pmu> is available on an old Linux system.
+Defaults to B<false>.
+
+=item B<ReportDegraded> B<false>|B<true>
+
+Typical laptop batteries degrade over time, meaning the capacity decreases with
+recharge cycles. The maximum charge of the previous charge cycle is tracked as
+"last full capacity" and used to determine that a battery is "fully charged".
+
+When this option is set to B<false>, the default, the I<battery plugin> will
+only report the remaining capacity. If the B<ValuesPercentage> option is
+enabled, the relative remaining capacity is calculated as the ratio of the
+"remaining capacity" and the "last full capacity". This is what most tools,
+such as the status bar of desktop environments, also do.
+
+When set to B<true>, the battery plugin will report three values: B<charged>
+(remaining capacity), B<discharged> (difference between "last full capacity"
+and "remaining capacity") and B<degraded> (difference between "design capacity"
+and "last full capacity").
+
+=back
+
 =head2 Plugin C<bind>
 
 Starting with BIND 9.5.0, the most widely used DNS server software provides
@@ -1144,29 +1207,53 @@ F</proc/sys/net/ipv4/netfilter> instead of F</proc/sys/net/netfilter/>.
 
 =head2 Plugin C<cpu>
 
-The I<CPU plugin> collects CPU usage metrics.
+The I<CPU plugin> collects CPU usage metrics. By default, CPU usage is reported
+as Jiffies, using the C<cpu> type. Two aggregations are available:
+
+=over 4
+
+=item
+
+Sum, per-state, over all CPUs installed in the system; and
+
+=item
+
+Sum, per-CPU, over all non-idle states of a CPU, creating an "active" state.
+
+=back
+
+The two aggregations can be combined, leading to I<collectd> only emitting a
+single "active" metric for the entire system. As soon as one of these
+aggregations (or both) is enabled, the I<cpu plugin> will report a percentage,
+rather than Jiffies. In addition, you can request individual, per-state,
+per-CPU metrics to be reported as percentage.
 
 The following configuration options are available:
 
 =over 4
 
-=item B<ReportActive> B<false>|B<true>
+=item B<ReportByState> B<true>|B<false>
 
-Reports non-idle CPU usage as the "active" value. Defaults to false.
+When set to B<true>, the default, reports per-state metrics, e.g. "system",
+"user" and "idle".
+When set to B<false>, aggregates (sums) all I<non-idle> states into one
+"active" metric.
 
-=item B<ReportByCpu> B<false>|B<true>
+=item B<ReportByCpu> B<true>|B<false>
 
-When true reports usage for all cores. When false, reports cpu usage
-aggregated over all cores.
-Defaults to true.
+When set to B<true>, the default, reports per-CPU (per-core) metrics.
+When set to B<false>, instead of reporting metrics for individual CPUs, only a
+global sum of CPU states is emitted.
 
 =item B<ValuesPercentage> B<false>|B<true>
 
-When true report percentage usage instead of tick values. Defaults to false.
+This option is only considered when both, B<ReportByCpu> and B<ReportByState>
+are set to B<true>. In this case, by default, metrics will be reported as
+Jiffies. By setting this option to B<true>, you can request percentage values
+in the un-aggregated (per-CPU, per-state) mode as well.
 
 =back
 
-
 =head2 Plugin C<cpufreq>
 
 This plugin doesn't have any options. It reads
@@ -1209,6 +1296,16 @@ finance page and dispatch the value to collectd.
       URL "http://finance.google.com/finance?q=NYSE%3AAMD"
       User "foo"
       Password "bar"
+      Digest false
+      VerifyPeer true
+      VerifyHost true
+      CACert "/path/to/ca.crt"
+      Header "X-Custom-Header: foobar"
+      Post "foo=bar"
+
+      MeasureResponseTime false
+      MeasureResponseCode false
+
       <Match>
         Regex "<span +class=\"pr\"[^>]*> *([0-9]*\\.[0-9]+) *</span>"
         DSType "GaugeAverage"
@@ -1281,13 +1378,19 @@ C<application/x-www-form-urlencoded>).
 Measure response time for the request. If this setting is enabled, B<Match>
 blocks (see below) are optional. Disabled by default.
 
+=item B<MeasureResponseCode> B<true>|B<false>
+
+Measure response code for the request. If this setting is enabled, B<Match>
+blocks (see below) are optional. Disabled by default.
+
 =item B<E<lt>MatchE<gt>>
 
 One or more B<Match> blocks that define how to match information in the data
 returned by C<libcurl>. The C<curl> plugin uses the same infrastructure that's
 used by the C<tail> plugin, so please see the documentation of the C<tail>
-plugin below on how matches are defined. If the B<MeasureResponseTime> option
-is set to B<true>, B<Match> blocks are optional.
+plugin below on how matches are defined. If the B<MeasureResponseTime> or
+B<MeasureResponseCode> options are set to B<true>, B<Match> blocks are
+optional.
 
 =back
 
@@ -1410,6 +1513,8 @@ The B<curl_xml plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and B<libxml2>
      VerifyPeer true
      VerifyHost true
      CACert "/path/to/ca.crt"
+     Header "X-Custom-Header: foobar"
+     Post "foo=bar"
 
      <XPath "table[@id=\"magic_level\"]/tr">
        Type "magic_level"
@@ -1705,7 +1810,7 @@ There must be at least one B<ValuesFrom> option inside each B<Result> block.
 =item B<MetadataFrom> [I<column0> I<column1> ...]
 
 Names the columns whose content is used as metadata for the data sets
-that are dispatched to the daemon. 
+that are dispatched to the daemon.
 
 The actual data type in the columns is not that important. The plugin will
 automatically cast the values to the right type if it know how to do that. So
@@ -2349,101 +2454,6 @@ independent from the I<JavaClass> argument passed to B<LoadPlugin>.
 
 =back
 
-=head2 Plugin C<libvirt>
-
-This plugin allows CPU, disk and network load to be collected for virtualized
-guests on the machine. This means that these characteristics can be collected
-for guest systems without installing any software on them - collectd only runs
-on the hosting system. The statistics are collected through libvirt
-(L<http://libvirt.org/>).
-
-Only I<Connection> is required.
-
-=over 4
-
-=item B<Connection> I<uri>
-
-Connect to the hypervisor given by I<uri>. For example if using Xen use:
-
- Connection "xen:///"
-
-Details which URIs allowed are given at L<http://libvirt.org/uri.html>.
-
-=item B<RefreshInterval> I<seconds>
-
-Refresh the list of domains and devices every I<seconds>. The default is 60
-seconds. Setting this to be the same or smaller than the I<Interval> will cause
-the list of domains and devices to be refreshed on every iteration.
-
-Refreshing the devices in particular is quite a costly operation, so if your
-virtualization setup is static you might consider increasing this. If this
-option is set to 0, refreshing is disabled completely.
-
-=item B<Domain> I<name>
-
-=item B<BlockDevice> I<name:dev>
-
-=item B<InterfaceDevice> I<name:dev>
-
-=item B<IgnoreSelected> I<true>|I<false>
-
-Select which domains and devices are collected.
-
-If I<IgnoreSelected> is not given or I<false> then only the listed domains and
-disk/network devices are collected.
-
-If I<IgnoreSelected> is I<true> then the test is reversed and the listed
-domains and disk/network devices are ignored, while the rest are collected.
-
-The domain name and device names may use a regular expression, if the name is
-surrounded by I</.../> and collectd was compiled with support for regexps.
-
-The default is to collect statistics for all domains and all their devices.
-
-Example:
-
- BlockDevice "/:hdb/"
- IgnoreSelected "true"
-
-Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
-will be collected.
-
-=item B<HostnameFormat> B<name|uuid|hostname|...>
-
-When the libvirt plugin logs data, it sets the hostname of the collected data
-according to this setting. The default is to use the guest name as provided by
-the hypervisor, which is equal to setting B<name>.
-
-B<uuid> means use the guest's UUID. This is useful if you want to track the
-same guest across migrations.
-
-B<hostname> means to use the global B<Hostname> setting, which is probably not
-useful on its own because all guests will appear to have the same name.
-
-You can also specify combinations of these fields. For example B<name uuid>
-means to concatenate the guest name and UUID (with a literal colon character
-between, thus I<"foo:1234-1234-1234-1234">).
-
-=item B<InterfaceFormat> B<name>|B<address>
-
-When the libvirt plugin logs interface data, it sets the name of the collected
-data according to this setting. The default is to use the path as provided by
-the hypervisor (the "dev" property of the target node), which is equal to
-setting B<name>.
-
-B<address> means use the interface's mac address. This is useful since the
-interface path might change between reboots of a guest or across migrations.
-
-=item B<PluginInstanceFormat> B<name|uuid>
-
-When the libvirt plugin logs data, it sets the plugin_instance of the collected 
-data according to this setting. The default is to use the guest name as provided 
-by the hypervisor, which is equal to setting B<name>.
-
-B<uuid> means use the guest's UUID.
-
-=back
-
 =head2 Plugin C<load>
 
 The I<Load plugin> collects the system load. These numbers give a rough overview
@@ -3809,6 +3819,12 @@ behavior is to let the kernel choose the appropriate interface. Be warned
 that the manual selection of an interface for unicast traffic is only
 necessary in rare cases.
 
+=item B<ResolveInterval> I<Seconds>
+
+Sets the interval at which to re-resolve the DNS for the I<Host>. This is
+useful to force a regular DNS lookup to support a high availability setup. If
+not specified, re-resolves are never attempted.
+
 =back
 
 =item B<E<lt>Listen> I<Host> [I<Port>]B<E<gt>>
@@ -5687,6 +5703,40 @@ measurements are discarded.
 
 =back
 
+=head2 Plugin C<smart>
+
+The C<smart> plugin collects SMART information from physical
+disks. Values collectd include temperature, power cycle count, poweron
+time and bad sectors. Also, all SMART attributes are collected along
+with the normalized current value, the worst value, the threshold and
+a human readable value.
+
+Using the following two options you can ignore some disks or configure the
+collection only of specific disks.
+
+=over 4
+
+=item B<Disk> I<Name>
+
+Select the disk I<Name>. Whether it is collected or ignored depends on the
+B<IgnoreSelected> setting, see below. As with other plugins that use the
+daemon's ignorelist functionality, a string that starts and ends with a slash
+is interpreted as a regular expression. Examples:
+
+  Disk "sdd"
+  Disk "/hda[34]/"
+
+=item B<IgnoreSelected> B<true>|B<false>
+
+Sets whether selected disks, i.E<nbsp>e. the ones matches by any of the B<Disk>
+statements, are ignored or if all other disks are ignored. The behavior
+(hopefully) is intuitive: If no B<Disk> option is configured, all disks are
+collected. If at least one B<Disk> option is given and no B<IgnoreSelected> or
+set to B<false>, B<only> matching disks will be collected. If B<IgnoreSelected>
+is set to B<true>, all disks are collected B<except> the ones matched.
+
+=back
+
 =head2 Plugin C<snmp>
 
 Since the configuration of the C<snmp plugin> is a little more complicated than
@@ -6224,6 +6274,11 @@ connections a mail server or news server has to other mail or news servers, or
 how many connections a web proxy holds to web servers. You have to give the
 port in numeric form.
 
+=item B<AllPortsSummary> I<true>|I<false>
+
+If this option is set to I<true> a summary of statistics from all connections
+are collectd. This option defaults to I<false>.
+
 =back
 
 =head2 Plugin C<thermal>
@@ -6481,6 +6536,101 @@ Collect statistics about worker threads. False by default.
 
 =back
 
+=head2 Plugin C<virt>
+
+This plugin allows CPU, disk and network load to be collected for virtualized
+guests on the machine. This means that these metrics can be collected for guest
+systems without installing any software on them - I<collectd> only runs on the
+host system. The statistics are collected through libvirt
+(L<http://libvirt.org/>).
+
+Only I<Connection> is required.
+
+=over 4
+
+=item B<Connection> I<uri>
+
+Connect to the hypervisor given by I<uri>. For example if using Xen use:
+
+ Connection "xen:///"
+
+Details which URIs allowed are given at L<http://libvirt.org/uri.html>.
+
+=item B<RefreshInterval> I<seconds>
+
+Refresh the list of domains and devices every I<seconds>. The default is 60
+seconds. Setting this to be the same or smaller than the I<Interval> will cause
+the list of domains and devices to be refreshed on every iteration.
+
+Refreshing the devices in particular is quite a costly operation, so if your
+virtualization setup is static you might consider increasing this. If this
+option is set to 0, refreshing is disabled completely.
+
+=item B<Domain> I<name>
+
+=item B<BlockDevice> I<name:dev>
+
+=item B<InterfaceDevice> I<name:dev>
+
+=item B<IgnoreSelected> B<true>|B<false>
+
+Select which domains and devices are collected.
+
+If I<IgnoreSelected> is not given or B<false> then only the listed domains and
+disk/network devices are collected.
+
+If I<IgnoreSelected> is B<true> then the test is reversed and the listed
+domains and disk/network devices are ignored, while the rest are collected.
+
+The domain name and device names may use a regular expression, if the name is
+surrounded by I</.../> and collectd was compiled with support for regexps.
+
+The default is to collect statistics for all domains and all their devices.
+
+Example:
+
+ BlockDevice "/:hdb/"
+ IgnoreSelected "true"
+
+Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
+will be collected.
+
+=item B<HostnameFormat> B<name|uuid|hostname|...>
+
+When the virt plugin logs data, it sets the hostname of the collected data
+according to this setting. The default is to use the guest name as provided by
+the hypervisor, which is equal to setting B<name>.
+
+B<uuid> means use the guest's UUID. This is useful if you want to track the
+same guest across migrations.
+
+B<hostname> means to use the global B<Hostname> setting, which is probably not
+useful on its own because all guests will appear to have the same name.
+
+You can also specify combinations of these fields. For example B<name uuid>
+means to concatenate the guest name and UUID (with a literal colon character
+between, thus I<"foo:1234-1234-1234-1234">).
+
+=item B<InterfaceFormat> B<name>|B<address>
+
+When the virt plugin logs interface data, it sets the name of the collected
+data according to this setting. The default is to use the path as provided by
+the hypervisor (the "dev" property of the target node), which is equal to
+setting B<name>.
+
+B<address> means use the interface's mac address. This is useful since the
+interface path might change between reboots of a guest or across migrations.
+
+=item B<PluginInstanceFormat> B<name|uuid>
+
+When the virt plugin logs data, it sets the plugin_instance of the collected
+data according to this setting. The default is to use the guest name as provided
+by the hypervisor, which is equal to setting B<name>.
+
+B<uuid> means use the guest's UUID.
+
+=back
+
 =head2 Plugin C<vmem>
 
 The C<vmem> plugin collects information about the usage of virtual memory.
@@ -6705,8 +6855,9 @@ want to use authentication all three fields must be set.
 
 =head2 Plugin C<write_http>
 
-This output plugin submits values to an http server by POST them using the
-PUTVAL plain-text protocol. Each destination you want to post data to needs to
+This output plugin submits values to an HTTP server using POST requests and
+encoding metrics with JSON or using the C<PUTVAL> command described in
+L<collectd-unixsock(5)>. Each destination you want to post data to needs to
 have one B<URL> block, within which the destination can be configured further,
 for example by specifying authentication data.
 
@@ -6716,6 +6867,7 @@ Synopsis:
    <URL "http://example.com/post-collectd">
      User "collectd"
      Password "weCh3ik0"
+     Format JSON
    </URL>
  </Plugin>
 
@@ -6789,8 +6941,16 @@ Defaults to B<Command>.
 =item B<StoreRates> B<true|false>
 
 If set to B<true>, convert counter values to rates. If set to B<false> (the
-default) counter values are stored as is, i.E<nbsp>e. as an increasing integer
-number.
+default) counter values are stored as is, i.e. as an increasing integer number.
+
+=item B<BufferSize> I<Bytes>
+
+Sets the send buffer size to I<Bytes>. By increasing this buffer, less HTTP
+requests will be generated, but more metrics will be batched / metrics are
+cached for longer before being sent, introducing additional delay until they
+are available on the server side. I<Bytes> must be at least 1024 and cannot
+exceed the size of an C<int>, i.e. 2E<nbsp>GByte.
+Defaults to C<4096>.
 
 =back
 
@@ -6843,7 +7003,7 @@ If set to B<JSON>, the values are encoded in the I<JavaScript Object Notation>,
 an easy and straight forward exchange format.
 
 If set to B<Graphite>, values are encoded in the I<Graphite> format, which is
-"<metric> <value> <timestamp>\n".
+C<E<lt>metricE<gt> E<lt>valueE<gt> E<lt>timestampE<gt>\n>.
 
 =item B<StoreRates> B<true>|B<false>
 
@@ -6857,22 +7017,24 @@ been set to B<JSON>.
 
 =item B<GraphitePrefix> (B<Format>=I<Graphite> only)
 
-A prefix can be added in the metric name when outputting in the I<Graphite> format.
-It's added before the I<Host> name.
-Metric name will be "<prefix><host><postfix><plugin><type><name>"
+A prefix can be added in the metric name when outputting in the I<Graphite>
+format. It's added before the I<Host> name.
+Metric name will be
+C<E<lt>prefixE<gt>E<lt>hostE<gt>E<lt>postfixE<gt>E<lt>pluginE<gt>E<lt>typeE<gt>E<lt>nameE<gt>>
 
 =item B<GraphitePostfix> (B<Format>=I<Graphite> only)
 
-A postfix can be added in the metric name when outputting in the I<Graphite> format.
-It's added after the I<Host> name.
-Metric name will be "<prefix><host><postfix><plugin><type><name>"
+A postfix can be added in the metric name when outputting in the I<Graphite>
+format. It's added after the I<Host> name.
+Metric name will be
+C<E<lt>prefixE<gt>E<lt>hostE<gt>E<lt>postfixE<gt>E<lt>pluginE<gt>E<lt>typeE<gt>E<lt>nameE<gt>>
 
 =item B<GraphiteEscapeChar> (B<Format>=I<Graphite> only)
 
 Specify a character to replace dots (.) in the host part of the metric name.
 In I<Graphite> metric name, dots are used as separators between different
 metric parts (host, plugin, type).
-Default is "_" (I<Underscore>).
+Default is C<_> (I<Underscore>).
 
 =item B<GraphiteSeparateInstances> B<false>|B<true>
 
@@ -6946,6 +7108,20 @@ Service name or port number to connect to. Defaults to C<5555>.
 Specify the protocol to use when communicating with I<Riemann>. Defaults to
 B<UDP>.
 
+=item B<Batch> B<true>|B<false>
+
+If set to B<true> and B<Protocol> is set to B<TCP>,
+events will be batched in memory and flushed at
+regular intervals or when B<BatchMaxSize> is exceeded.
+
+Notifications are not batched and sent as soon as possible.
+
+Defaults to false
+
+=item B<BatchMaxSize> I<size>
+
+Maximum payload size for a riemann packet. Defaults to 8192
+
 =item B<StoreRates> B<true>|B<false>
 
 If set to B<true> (the default), convert counter values to rates. If set to
diff --git a/src/collectd.h b/src/collectd.h
deleted file mode 100644 (file)
index 5650b59..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-/**
- * collectd - src/collectd.h
- * Copyright (C) 2005,2006  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 COLLECTD_H
-#define COLLECTD_H
-
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <stdio.h>
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_SYS_STAT_H
-# include <sys/stat.h>
-#endif
-#if STDC_HEADERS
-# include <stdlib.h>
-# include <stddef.h>
-#else
-# if HAVE_STDLIB_H
-#  include <stdlib.h>
-# endif
-#endif
-#if HAVE_STRING_H
-# if !STDC_HEADERS && HAVE_MEMORY_H
-#  include <memory.h>
-# endif
-# include <string.h>
-#endif
-#if HAVE_STRINGS_H
-# include <strings.h>
-#endif
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#if HAVE_SYS_WAIT_H
-# include <sys/wait.h>
-#endif
-#ifndef WEXITSTATUS
-# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
-#endif
-#ifndef WIFEXITED
-# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
-#endif
-#if HAVE_SIGNAL_H
-# include <signal.h>
-#endif
-#if HAVE_FCNTL_H
-# include <fcntl.h>
-#endif
-#if HAVE_ERRNO_H
-# include <errno.h>
-#endif
-#if HAVE_LIMITS_H
-# include <limits.h>
-#endif
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#if HAVE_ASSERT_H
-# include <assert.h>
-#else
-# define assert(...) /* nop */
-#endif
-
-#if !defined(HAVE__BOOL) || !HAVE__BOOL
-typedef int _Bool;
-# undef HAVE__BOOL
-# define HAVE__BOOL 1
-#endif
-
-#if NAN_STATIC_DEFAULT
-# include <math.h>
-/* #endif NAN_STATIC_DEFAULT*/
-#elif NAN_STATIC_ISOC
-# ifndef __USE_ISOC99
-#  define DISABLE_ISOC99 1
-#  define __USE_ISOC99 1
-# endif /* !defined(__USE_ISOC99) */
-# include <math.h>
-# if DISABLE_ISOC99
-#  undef DISABLE_ISOC99
-#  undef __USE_ISOC99
-# endif /* DISABLE_ISOC99 */
-/* #endif NAN_STATIC_ISOC */
-#elif NAN_ZERO_ZERO
-# include <math.h>
-# ifdef NAN
-#  undef NAN
-# endif
-# define NAN (0.0 / 0.0)
-# ifndef isnan
-#  define isnan(f) ((f) != (f))
-# endif /* !defined(isnan) */
-# ifndef isfinite
-#  define isfinite(f) (((f) - (f)) == 0.0)
-# endif
-# ifndef isinf
-#  define isinf(f) (!isfinite(f) && !isnan(f))
-# endif
-#endif /* NAN_ZERO_ZERO */
-
-/* Try really, really hard to determine endianess. Under NexentaStor 1.0.2 this
- * information is in <sys/isa_defs.h>, possibly some other Solaris versions do
- * this too.. */
-#if HAVE_ENDIAN_H
-# include <endian.h>
-#elif HAVE_SYS_ISA_DEFS_H
-# include <sys/isa_defs.h>
-#endif
-
-#ifndef BYTE_ORDER
-# if defined(_BYTE_ORDER)
-#  define BYTE_ORDER _BYTE_ORDER
-# elif defined(__BYTE_ORDER)
-#  define BYTE_ORDER __BYTE_ORDER
-# elif defined(__DARWIN_BYTE_ORDER)
-#  define BYTE_ORDER __DARWIN_BYTE_ORDER
-# endif
-#endif
-#ifndef BIG_ENDIAN
-# if defined(_BIG_ENDIAN)
-#  define BIG_ENDIAN _BIG_ENDIAN
-# elif defined(__BIG_ENDIAN)
-#  define BIG_ENDIAN __BIG_ENDIAN
-# elif defined(__DARWIN_BIG_ENDIAN)
-#  define BIG_ENDIAN __DARWIN_BIG_ENDIAN
-# endif
-#endif
-#ifndef LITTLE_ENDIAN
-# if defined(_LITTLE_ENDIAN)
-#  define LITTLE_ENDIAN _LITTLE_ENDIAN
-# elif defined(__LITTLE_ENDIAN)
-#  define LITTLE_ENDIAN __LITTLE_ENDIAN
-# elif defined(__DARWIN_LITTLE_ENDIAN)
-#  define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN
-# endif
-#endif
-#ifndef BYTE_ORDER
-# if defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN)
-#  undef BIG_ENDIAN
-#  define BIG_ENDIAN 4321
-#  define LITTLE_ENDIAN 1234
-#  define BYTE_ORDER BIG_ENDIAN
-# elif !defined(BIG_ENDIAN) && defined(LITTLE_ENDIAN)
-#  undef LITTLE_ENDIAN
-#  define BIG_ENDIAN 4321
-#  define LITTLE_ENDIAN 1234
-#  define BYTE_ORDER LITTLE_ENDIAN
-# endif
-#endif
-#if !defined(BYTE_ORDER) || !defined(BIG_ENDIAN)
-# error "Cannot determine byte order"
-#endif
-
-#if HAVE_DIRENT_H
-# include <dirent.h>
-# define NAMLEN(dirent) strlen((dirent)->d_name)
-#else
-# define dirent direct
-# define NAMLEN(dirent) (dirent)->d_namlen
-# if HAVE_SYS_NDIR_H
-#  include <sys/ndir.h>
-# endif
-# if HAVE_SYS_DIR_H
-#  include <sys/dir.h>
-# endif
-# if HAVE_NDIR_H
-#  include <ndir.h>
-# endif
-#endif
-
-#if HAVE_STDARG_H
-# include <stdarg.h>
-#endif
-#if HAVE_CTYPE_H
-# include <ctype.h>
-#endif
-#if HAVE_SYS_PARAM_H
-# include <sys/param.h>
-#endif
-
-#if HAVE_KSTAT_H
-# include <kstat.h>
-#endif
-
-#ifndef PACKAGE_NAME
-#define PACKAGE_NAME "collectd"
-#endif
-
-#ifndef PREFIX
-#define PREFIX "/opt/" PACKAGE_NAME
-#endif
-
-#ifndef SYSCONFDIR
-#define SYSCONFDIR PREFIX "/etc"
-#endif
-
-#ifndef CONFIGFILE
-#define CONFIGFILE SYSCONFDIR"/collectd.conf"
-#endif
-
-#ifndef LOCALSTATEDIR
-#define LOCALSTATEDIR PREFIX "/var"
-#endif
-
-#ifndef PKGLOCALSTATEDIR
-#define PKGLOCALSTATEDIR PREFIX "/var/lib/" PACKAGE_NAME
-#endif
-
-#ifndef PIDFILE
-#define PIDFILE PREFIX "/var/run/" PACKAGE_NAME ".pid"
-#endif
-
-#ifndef PLUGINDIR
-#define PLUGINDIR PREFIX "/lib/" PACKAGE_NAME
-#endif
-
-#ifndef PKGDATADIR
-#define PKGDATADIR PREFIX "/share/" PACKAGE_NAME
-#endif
-
-#ifndef COLLECTD_GRP_NAME
-# define COLLECTD_GRP_NAME "collectd"
-#endif
-
-#ifndef COLLECTD_DEFAULT_INTERVAL
-# define COLLECTD_DEFAULT_INTERVAL 10.0
-#endif
-
- #ifndef COLLECTD_USERAGENT
- # define COLLECTD_USERAGENT PACKAGE_NAME"/"PACKAGE_VERSION
- #endif
-
-/* Remove GNU specific __attribute__ settings when using another compiler */
-#if !__GNUC__
-# define __attribute__(x) /**/
-#endif
-
-#if defined(COLLECT_DEBUG) && COLLECT_DEBUG && defined(__GNUC__) && __GNUC__
-# undef strcpy
-# undef strcat
-# undef strtok
-# pragma GCC poison strcpy strcat strtok
-#endif
-
-/* 
- * Special hack for the perl plugin: Because the later included perl.h defines
- * a macro which is never used, but contains `sprintf', we cannot poison that
- * identifies just yet. The parl plugin will do that itself once perl.h is
- * included.
- */
-#ifndef DONT_POISON_SPRINTF_YET
-# if defined(COLLECT_DEBUG) && COLLECT_DEBUG && defined(__GNUC__) && __GNUC__
-#  undef sprintf
-#  pragma GCC poison sprintf
-# endif
-#endif
-
-/* Type for time as used by "utils_time.h" */
-typedef uint64_t cdtime_t;
-
-extern char     hostname_g[];
-extern cdtime_t interval_g;
-extern int      pidfile_from_cli;
-extern int      timeout_g;
-
-#endif /* COLLECTD_H */
diff --git a/src/common.c b/src/common.c
deleted file mode 100644 (file)
index 8691d3e..0000000
+++ /dev/null
@@ -1,1576 +0,0 @@
-/**
- * collectd - src/common.c
- * Copyright (C) 2005-2014  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>
- *   Niki W. Waibel <niki.waibel@gmx.net>
- *   Sebastian Harl <sh at tokkee.org>
- *   Michał Mirosław <mirq-linux at rere.qmqm.pl>
-**/
-
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "collectd.h"
-#include "common.h"
-#include "plugin.h"
-#include "utils_cache.h"
-
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
-
-#ifdef HAVE_MATH_H
-# include <math.h>
-#endif
-
-/* for getaddrinfo */
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-
-/* for ntohl and htonl */
-#if HAVE_ARPA_INET_H
-# include <arpa/inet.h>
-#endif
-
-#ifdef HAVE_LIBKSTAT
-extern kstat_ctl_t *kc;
-#endif
-
-#if !HAVE_GETPWNAM_R
-static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-#if !HAVE_STRERROR_R
-static pthread_mutex_t strerror_r_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-char *sstrncpy (char *dest, const char *src, size_t n)
-{
-       strncpy (dest, src, n);
-       dest[n - 1] = '\0';
-
-       return (dest);
-} /* char *sstrncpy */
-
-int ssnprintf (char *dest, size_t n, const char *format, ...)
-{
-       int ret = 0;
-       va_list ap;
-
-       va_start (ap, format);
-       ret = vsnprintf (dest, n, format, ap);
-       dest[n - 1] = '\0';
-       va_end (ap);
-
-       return (ret);
-} /* int ssnprintf */
-
-char *ssnprintf_alloc (char const *format, ...) /* {{{ */
-{
-       char static_buffer[1024] = "";
-       char *alloc_buffer;
-       size_t alloc_buffer_size;
-       int status;
-       va_list ap;
-
-       /* Try printing into the static buffer. In many cases it will be
-        * sufficiently large and we can simply return a strdup() of this
-        * buffer. */
-       va_start (ap, format);
-       status = vsnprintf (static_buffer, sizeof (static_buffer), format, ap);
-       va_end (ap);
-       if (status < 0)
-               return (NULL);
-
-       /* "status" does not include the null byte. */
-       alloc_buffer_size = (size_t) (status + 1);
-       if (alloc_buffer_size <= sizeof (static_buffer))
-               return (strdup (static_buffer));
-
-       /* Allocate a buffer large enough to hold the string. */
-       alloc_buffer = malloc (alloc_buffer_size);
-       if (alloc_buffer == NULL)
-               return (NULL);
-       memset (alloc_buffer, 0, alloc_buffer_size);
-
-       /* Print again into this new buffer. */
-       va_start (ap, format);
-       status = vsnprintf (alloc_buffer, alloc_buffer_size, format, ap);
-       va_end (ap);
-       if (status < 0)
-       {
-               sfree (alloc_buffer);
-               return (NULL);
-       }
-
-       return (alloc_buffer);
-} /* }}} char *ssnprintf_alloc */
-
-char *sstrdup (const char *s)
-{
-       char *r;
-       size_t sz;
-
-       if (s == NULL)
-               return (NULL);
-
-       /* Do not use `strdup' here, because it's not specified in POSIX. It's
-        * ``only'' an XSI extension. */
-       sz = strlen (s) + 1;
-       r = (char *) malloc (sizeof (char) * sz);
-       if (r == NULL)
-       {
-               ERROR ("sstrdup: Out of memory.");
-               exit (3);
-       }
-       memcpy (r, s, sizeof (char) * sz);
-
-       return (r);
-} /* char *sstrdup */
-
-/* Even though Posix requires "strerror_r" to return an "int",
- * some systems (e.g. the GNU libc) return a "char *" _and_
- * ignore the second argument ... -tokkee */
-char *sstrerror (int errnum, char *buf, size_t buflen)
-{
-       buf[0] = '\0';
-
-#if !HAVE_STRERROR_R
-       {
-               char *temp;
-
-               pthread_mutex_lock (&strerror_r_lock);
-
-               temp = strerror (errnum);
-               sstrncpy (buf, temp, buflen);
-
-               pthread_mutex_unlock (&strerror_r_lock);
-       }
-/* #endif !HAVE_STRERROR_R */
-
-#elif STRERROR_R_CHAR_P
-       {
-               char *temp;
-               temp = strerror_r (errnum, buf, buflen);
-               if (buf[0] == '\0')
-               {
-                       if ((temp != NULL) && (temp != buf) && (temp[0] != '\0'))
-                               sstrncpy (buf, temp, buflen);
-                       else
-                               sstrncpy (buf, "strerror_r did not return "
-                                               "an error message", buflen);
-               }
-       }
-/* #endif STRERROR_R_CHAR_P */
-
-#else
-       if (strerror_r (errnum, buf, buflen) != 0)
-       {
-               ssnprintf (buf, buflen, "Error #%i; "
-                               "Additionally, strerror_r failed.",
-                               errnum);
-       }
-#endif /* STRERROR_R_CHAR_P */
-
-       return (buf);
-} /* char *sstrerror */
-
-void *smalloc (size_t size)
-{
-       void *r;
-
-       if ((r = malloc (size)) == NULL)
-       {
-               ERROR ("Not enough memory.");
-               exit (3);
-       }
-
-       return (r);
-} /* void *smalloc */
-
-#if 0
-void sfree (void **ptr)
-{
-       if (ptr == NULL)
-               return;
-
-       if (*ptr != NULL)
-               free (*ptr);
-
-       *ptr = NULL;
-}
-#endif
-
-ssize_t sread (int fd, void *buf, size_t count)
-{
-       char    *ptr;
-       size_t   nleft;
-       ssize_t  status;
-
-       ptr   = (char *) buf;
-       nleft = count;
-
-       while (nleft > 0)
-       {
-               status = read (fd, (void *) ptr, nleft);
-
-               if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
-                       continue;
-
-               if (status < 0)
-                       return (status);
-
-               if (status == 0)
-               {
-                       DEBUG ("Received EOF from fd %i. "
-                                       "Closing fd and returning error.",
-                                       fd);
-                       close (fd);
-                       return (-1);
-               }
-
-               assert ((0 > status) || (nleft >= (size_t)status));
-
-               nleft = nleft - status;
-               ptr   = ptr   + status;
-       }
-
-       return (0);
-}
-
-
-ssize_t swrite (int fd, const void *buf, size_t count)
-{
-       const char *ptr;
-       size_t      nleft;
-       ssize_t     status;
-
-       ptr   = (const char *) buf;
-       nleft = count;
-
-       while (nleft > 0)
-       {
-               status = write (fd, (const void *) ptr, nleft);
-
-               if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
-                       continue;
-
-               if (status < 0)
-                       return (status);
-
-               nleft = nleft - status;
-               ptr   = ptr   + status;
-       }
-
-       return (0);
-}
-
-int strsplit (char *string, char **fields, size_t size)
-{
-       size_t i;
-       char *ptr;
-       char *saveptr;
-
-       i = 0;
-       ptr = string;
-       saveptr = NULL;
-       while ((fields[i] = strtok_r (ptr, " \t\r\n", &saveptr)) != NULL)
-       {
-               ptr = NULL;
-               i++;
-
-               if (i >= size)
-                       break;
-       }
-
-       return ((int) i);
-}
-
-int strjoin (char *dst, size_t dst_len,
-               char **fields, size_t fields_num,
-               const char *sep)
-{
-       size_t field_len;
-       size_t sep_len;
-       int i;
-
-       memset (dst, '\0', dst_len);
-
-       if (fields_num <= 0)
-               return (-1);
-
-       sep_len = 0;
-       if (sep != NULL)
-               sep_len = strlen (sep);
-
-       for (i = 0; i < (int)fields_num; i++)
-       {
-               if ((i > 0) && (sep_len > 0))
-               {
-                       if (dst_len <= sep_len)
-                               return (-1);
-
-                       strncat (dst, sep, dst_len);
-                       dst_len -= sep_len;
-               }
-
-               field_len = strlen (fields[i]);
-
-               if (dst_len <= field_len)
-                       return (-1);
-
-               strncat (dst, fields[i], dst_len);
-               dst_len -= field_len;
-       }
-
-       return (strlen (dst));
-}
-
-int strsubstitute (char *str, char c_from, char c_to)
-{
-       int ret;
-
-       if (str == NULL)
-               return (-1);
-
-       ret = 0;
-       while (*str != '\0')
-       {
-               if (*str == c_from)
-               {
-                       *str = c_to;
-                       ret++;
-               }
-               str++;
-       }
-
-       return (ret);
-} /* int strsubstitute */
-
-int strunescape (char *buf, size_t buf_len)
-{
-       size_t i;
-
-       for (i = 0; (i < buf_len) && (buf[i] != '\0'); ++i)
-       {
-               if (buf[i] != '\\')
-                       continue;
-
-               if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) {
-                       ERROR ("string unescape: backslash found at end of string.");
-                       /* Ensure null-byte at the end of the buffer. */
-                       buf[i] = 0;
-                       return (-1);
-               }
-
-               switch (buf[i + 1]) {
-                       case 't':
-                               buf[i] = '\t';
-                               break;
-                       case 'n':
-                               buf[i] = '\n';
-                               break;
-                       case 'r':
-                               buf[i] = '\r';
-                               break;
-                       default:
-                               buf[i] = buf[i + 1];
-                               break;
-               }
-
-               /* Move everything after the position one position to the left.
-                * Add a null-byte as last character in the buffer. */
-               memmove (buf + i + 1, buf + i + 2, buf_len - i - 2);
-               buf[buf_len - 1] = 0;
-       }
-       return (0);
-} /* int strunescape */
-
-size_t strstripnewline (char *buffer)
-{
-       size_t buffer_len = strlen (buffer);
-
-       while (buffer_len > 0)
-       {
-               if ((buffer[buffer_len - 1] != '\n')
-                               && (buffer[buffer_len - 1] != '\r'))
-                       break;
-               buffer[buffer_len] = 0;
-               buffer_len--;
-       }
-
-       return (buffer_len);
-} /* size_t strstripnewline */
-
-int escape_slashes (char *buffer, size_t buffer_size)
-{
-       int i;
-       size_t buffer_len;
-
-       buffer_len = strlen (buffer);
-
-       if (buffer_len <= 1)
-       {
-               if (strcmp ("/", buffer) == 0)
-               {
-                       if (buffer_size < 5)
-                               return (-1);
-                       sstrncpy (buffer, "root", buffer_size);
-               }
-               return (0);
-       }
-
-       /* Move one to the left */
-       if (buffer[0] == '/')
-       {
-               memmove (buffer, buffer + 1, buffer_len);
-               buffer_len--;
-       }
-
-       for (i = 0; i < buffer_len - 1; i++)
-       {
-               if (buffer[i] == '/')
-                       buffer[i] = '_';
-       }
-
-       return (0);
-} /* int escape_slashes */
-
-void replace_special (char *buffer, size_t buffer_size)
-{
-       size_t i;
-
-       for (i = 0; i < buffer_size; i++)
-       {
-               if (buffer[i] == 0)
-                       return;
-               if ((!isalnum ((int) buffer[i])) && (buffer[i] != '-'))
-                       buffer[i] = '_';
-       }
-} /* void replace_special */
-
-int timeval_cmp (struct timeval tv0, struct timeval tv1, struct timeval *delta)
-{
-       struct timeval *larger;
-       struct timeval *smaller;
-
-       int status;
-
-       NORMALIZE_TIMEVAL (tv0);
-       NORMALIZE_TIMEVAL (tv1);
-
-       if ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec == tv1.tv_usec))
-       {
-               if (delta != NULL) {
-                       delta->tv_sec  = 0;
-                       delta->tv_usec = 0;
-               }
-               return (0);
-       }
-
-       if ((tv0.tv_sec < tv1.tv_sec)
-                       || ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec < tv1.tv_usec)))
-       {
-               larger  = &tv1;
-               smaller = &tv0;
-               status  = -1;
-       }
-       else
-       {
-               larger  = &tv0;
-               smaller = &tv1;
-               status  = 1;
-       }
-
-       if (delta != NULL) {
-               delta->tv_sec = larger->tv_sec - smaller->tv_sec;
-
-               if (smaller->tv_usec <= larger->tv_usec)
-                       delta->tv_usec = larger->tv_usec - smaller->tv_usec;
-               else
-               {
-                       --delta->tv_sec;
-                       delta->tv_usec = 1000000 + larger->tv_usec - smaller->tv_usec;
-               }
-       }
-
-       assert ((delta == NULL)
-                       || ((0 <= delta->tv_usec) && (delta->tv_usec < 1000000)));
-
-       return (status);
-} /* int timeval_cmp */
-
-int check_create_dir (const char *file_orig)
-{
-       struct stat statbuf;
-
-       char  file_copy[512];
-       char  dir[512];
-       int   dir_len = 512;
-       char *fields[16];
-       int   fields_num;
-       char *ptr;
-       char *saveptr;
-       int   last_is_file = 1;
-       int   path_is_absolute = 0;
-       size_t len;
-       int   i;
-
-       /*
-        * Sanity checks first
-        */
-       if (file_orig == NULL)
-               return (-1);
-
-       if ((len = strlen (file_orig)) < 1)
-               return (-1);
-       else if (len >= sizeof (file_copy))
-               return (-1);
-
-       /*
-        * If `file_orig' ends in a slash the last component is a directory,
-        * otherwise it's a file. Act accordingly..
-        */
-       if (file_orig[len - 1] == '/')
-               last_is_file = 0;
-       if (file_orig[0] == '/')
-               path_is_absolute = 1;
-
-       /*
-        * Create a copy for `strtok_r' to destroy
-        */
-       sstrncpy (file_copy, file_orig, sizeof (file_copy));
-
-       /*
-        * Break into components. This will eat up several slashes in a row and
-        * remove leading and trailing slashes..
-        */
-       ptr = file_copy;
-       saveptr = NULL;
-       fields_num = 0;
-       while ((fields[fields_num] = strtok_r (ptr, "/", &saveptr)) != NULL)
-       {
-               ptr = NULL;
-               fields_num++;
-
-               if (fields_num >= 16)
-                       break;
-       }
-
-       /*
-        * For each component, do..
-        */
-       for (i = 0; i < (fields_num - last_is_file); i++)
-       {
-               /*
-                * Do not create directories that start with a dot. This
-                * prevents `../../' attacks and other likely malicious
-                * behavior.
-                */
-               if (fields[i][0] == '.')
-               {
-                       ERROR ("Cowardly refusing to create a directory that "
-                                       "begins with a `.' (dot): `%s'", file_orig);
-                       return (-2);
-               }
-
-               /*
-                * Join the components together again
-                */
-               dir[0] = '/';
-               if (strjoin (dir + path_is_absolute, dir_len - path_is_absolute,
-                                       fields, i + 1, "/") < 0)
-               {
-                       ERROR ("strjoin failed: `%s', component #%i", file_orig, i);
-                       return (-1);
-               }
-
-               while (42) {
-                       if ((stat (dir, &statbuf) == -1)
-                                       && (lstat (dir, &statbuf) == -1))
-                       {
-                               if (errno == ENOENT)
-                               {
-                                       if (mkdir (dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
-                                               break;
-
-                                       /* this might happen, if a different thread created
-                                        * the directory in the meantime
-                                        * => call stat() again to check for S_ISDIR() */
-                                       if (EEXIST == errno)
-                                               continue;
-
-                                       char errbuf[1024];
-                                       ERROR ("check_create_dir: mkdir (%s): %s", dir,
-                                                       sstrerror (errno,
-                                                               errbuf, sizeof (errbuf)));
-                                       return (-1);
-                               }
-                               else
-                               {
-                                       char errbuf[1024];
-                                       ERROR ("check_create_dir: stat (%s): %s", dir,
-                                                       sstrerror (errno, errbuf,
-                                                               sizeof (errbuf)));
-                                       return (-1);
-                               }
-                       }
-                       else if (!S_ISDIR (statbuf.st_mode))
-                       {
-                               ERROR ("check_create_dir: `%s' exists but is not "
-                                               "a directory!", dir);
-                               return (-1);
-                       }
-                       break;
-               }
-       }
-
-       return (0);
-} /* check_create_dir */
-
-#ifdef HAVE_LIBKSTAT
-int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
-{
-       char ident[128];
-
-       *ksp_ptr = NULL;
-
-       if (kc == NULL)
-               return (-1);
-
-       ssnprintf (ident, sizeof (ident), "%s,%i,%s", module, instance, name);
-
-       *ksp_ptr = kstat_lookup (kc, module, instance, name);
-       if (*ksp_ptr == NULL)
-       {
-               ERROR ("get_kstat: Cound not find kstat %s", ident);
-               return (-1);
-       }
-
-       if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
-       {
-               ERROR ("get_kstat: kstat %s has wrong type", ident);
-               *ksp_ptr = NULL;
-               return (-1);
-       }
-
-#ifdef assert
-       assert (*ksp_ptr != NULL);
-       assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
-#endif
-
-       if (kstat_read (kc, *ksp_ptr, NULL) == -1)
-       {
-               ERROR ("get_kstat: kstat %s could not be read", ident);
-               return (-1);
-       }
-
-       if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
-       {
-               ERROR ("get_kstat: kstat %s has wrong type", ident);
-               return (-1);
-       }
-
-       return (0);
-}
-
-long long get_kstat_value (kstat_t *ksp, char *name)
-{
-       kstat_named_t *kn;
-       long long retval = -1LL;
-
-       if (ksp == NULL)
-       {
-               ERROR ("get_kstat_value (\"%s\"): ksp is NULL.", name);
-               return (-1LL);
-       }
-       else if (ksp->ks_type != KSTAT_TYPE_NAMED)
-       {
-               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);
-       }
-
-       if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
-               return (-1LL);
-
-       if (kn->data_type == KSTAT_DATA_INT32)
-               retval = (long long) kn->value.i32;
-       else if (kn->data_type == KSTAT_DATA_UINT32)
-               retval = (long long) kn->value.ui32;
-       else if (kn->data_type == KSTAT_DATA_INT64)
-               retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
-       else if (kn->data_type == KSTAT_DATA_UINT64)
-               retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
-       else
-               WARNING ("get_kstat_value: Not a numeric value: %s", name);
-
-       return (retval);
-}
-#endif /* HAVE_LIBKSTAT */
-
-#ifndef HAVE_HTONLL
-unsigned long long ntohll (unsigned long long n)
-{
-#if BYTE_ORDER == BIG_ENDIAN
-       return (n);
-#else
-       return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32);
-#endif
-} /* unsigned long long ntohll */
-
-unsigned long long htonll (unsigned long long n)
-{
-#if BYTE_ORDER == BIG_ENDIAN
-       return (n);
-#else
-       return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
-#endif
-} /* unsigned long long htonll */
-#endif /* HAVE_HTONLL */
-
-#if FP_LAYOUT_NEED_NOTHING
-/* Well, we need nothing.. */
-/* #endif FP_LAYOUT_NEED_NOTHING */
-
-#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
-# if FP_LAYOUT_NEED_ENDIANFLIP
-#  define FP_CONVERT(A) ((((uint64_t)(A) & 0xff00000000000000LL) >> 56) | \
-                         (((uint64_t)(A) & 0x00ff000000000000LL) >> 40) | \
-                         (((uint64_t)(A) & 0x0000ff0000000000LL) >> 24) | \
-                         (((uint64_t)(A) & 0x000000ff00000000LL) >> 8)  | \
-                         (((uint64_t)(A) & 0x00000000ff000000LL) << 8)  | \
-                         (((uint64_t)(A) & 0x0000000000ff0000LL) << 24) | \
-                         (((uint64_t)(A) & 0x000000000000ff00LL) << 40) | \
-                         (((uint64_t)(A) & 0x00000000000000ffLL) << 56))
-# else
-#  define FP_CONVERT(A) ((((uint64_t)(A) & 0xffffffff00000000LL) >> 32) | \
-                         (((uint64_t)(A) & 0x00000000ffffffffLL) << 32))
-# endif
-
-double ntohd (double d)
-{
-       union
-       {
-               uint8_t  byte[8];
-               uint64_t integer;
-               double   floating;
-       } ret;
-
-       ret.floating = d;
-
-       /* NAN in x86 byte order */
-       if ((ret.byte[0] == 0x00) && (ret.byte[1] == 0x00)
-                       && (ret.byte[2] == 0x00) && (ret.byte[3] == 0x00)
-                       && (ret.byte[4] == 0x00) && (ret.byte[5] == 0x00)
-                       && (ret.byte[6] == 0xf8) && (ret.byte[7] == 0x7f))
-       {
-               return (NAN);
-       }
-       else
-       {
-               uint64_t tmp;
-
-               tmp = ret.integer;
-               ret.integer = FP_CONVERT (tmp);
-               return (ret.floating);
-       }
-} /* double ntohd */
-
-double htond (double d)
-{
-       union
-       {
-               uint8_t  byte[8];
-               uint64_t integer;
-               double   floating;
-       } ret;
-
-       if (isnan (d))
-       {
-               ret.byte[0] = ret.byte[1] = ret.byte[2] = ret.byte[3] = 0x00;
-               ret.byte[4] = ret.byte[5] = 0x00;
-               ret.byte[6] = 0xf8;
-               ret.byte[7] = 0x7f;
-               return (ret.floating);
-       }
-       else
-       {
-               uint64_t tmp;
-
-               ret.floating = d;
-               tmp = FP_CONVERT (ret.integer);
-               ret.integer = tmp;
-               return (ret.floating);
-       }
-} /* double htond */
-#endif /* FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP */
-
-int format_name (char *ret, int ret_len,
-               const char *hostname,
-               const char *plugin, const char *plugin_instance,
-               const char *type, const char *type_instance)
-{
-  char *buffer;
-  size_t buffer_size;
-
-  buffer = ret;
-  buffer_size = (size_t) ret_len;
-
-#define APPEND(str) do {                                               \
-  size_t l = strlen (str);                                             \
-  if (l >= buffer_size)                                                \
-    return (ENOBUFS);                                                  \
-  memcpy (buffer, (str), l);                                           \
-  buffer += l; buffer_size -= l;                                       \
-} while (0)
-
-  assert (plugin != NULL);
-  assert (type != NULL);
-
-  APPEND (hostname);
-  APPEND ("/");
-  APPEND (plugin);
-  if ((plugin_instance != NULL) && (plugin_instance[0] != 0))
-  {
-    APPEND ("-");
-    APPEND (plugin_instance);
-  }
-  APPEND ("/");
-  APPEND (type);
-  if ((type_instance != NULL) && (type_instance[0] != 0))
-  {
-    APPEND ("-");
-    APPEND (type_instance);
-  }
-  assert (buffer_size > 0);
-  buffer[0] = 0;
-
-#undef APPEND
-  return (0);
-} /* int format_name */
-
-int format_values (char *ret, size_t ret_len, /* {{{ */
-               const data_set_t *ds, const value_list_t *vl,
-               _Bool store_rates)
-{
-        size_t offset = 0;
-        int status;
-        int i;
-        gauge_t *rates = NULL;
-
-        assert (0 == strcmp (ds->type, vl->type));
-
-        memset (ret, 0, ret_len);
-
-#define BUFFER_ADD(...) do { \
-        status = ssnprintf (ret + offset, ret_len - offset, \
-                        __VA_ARGS__); \
-        if (status < 1) \
-        { \
-                sfree (rates); \
-                return (-1); \
-        } \
-        else if (((size_t) status) >= (ret_len - offset)) \
-        { \
-                sfree (rates); \
-                return (-1); \
-        } \
-        else \
-                offset += ((size_t) status); \
-} while (0)
-
-        BUFFER_ADD ("%.3f", CDTIME_T_TO_DOUBLE (vl->time));
-
-        for (i = 0; i < ds->ds_num; i++)
-        {
-                if (ds->ds[i].type == DS_TYPE_GAUGE)
-                        BUFFER_ADD (":%f", vl->values[i].gauge);
-                else if (store_rates)
-                {
-                        if (rates == NULL)
-                                rates = uc_get_rate (ds, vl);
-                        if (rates == NULL)
-                        {
-                                WARNING ("format_values: "
-                                               "uc_get_rate failed.");
-                                return (-1);
-                        }
-                        BUFFER_ADD (":%g", rates[i]);
-                }
-                else if (ds->ds[i].type == DS_TYPE_COUNTER)
-                        BUFFER_ADD (":%llu", vl->values[i].counter);
-                else if (ds->ds[i].type == DS_TYPE_DERIVE)
-                        BUFFER_ADD (":%"PRIi64, vl->values[i].derive);
-                else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
-                        BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
-                else
-                {
-                        ERROR ("format_values plugin: Unknown data source type: %i",
-                                        ds->ds[i].type);
-                        sfree (rates);
-                        return (-1);
-                }
-        } /* for ds->ds_num */
-
-#undef BUFFER_ADD
-
-        sfree (rates);
-        return (0);
-} /* }}} int format_values */
-
-int parse_identifier (char *str, char **ret_host,
-               char **ret_plugin, char **ret_plugin_instance,
-               char **ret_type, char **ret_type_instance)
-{
-       char *hostname = NULL;
-       char *plugin = NULL;
-       char *plugin_instance = NULL;
-       char *type = NULL;
-       char *type_instance = NULL;
-
-       hostname = str;
-       if (hostname == NULL)
-               return (-1);
-
-       plugin = strchr (hostname, '/');
-       if (plugin == NULL)
-               return (-1);
-       *plugin = '\0'; plugin++;
-
-       type = strchr (plugin, '/');
-       if (type == NULL)
-               return (-1);
-       *type = '\0'; type++;
-
-       plugin_instance = strchr (plugin, '-');
-       if (plugin_instance != NULL)
-       {
-               *plugin_instance = '\0';
-               plugin_instance++;
-       }
-
-       type_instance = strchr (type, '-');
-       if (type_instance != NULL)
-       {
-               *type_instance = '\0';
-               type_instance++;
-       }
-
-       *ret_host = hostname;
-       *ret_plugin = plugin;
-       *ret_plugin_instance = plugin_instance;
-       *ret_type = type;
-       *ret_type_instance = type_instance;
-       return (0);
-} /* int parse_identifier */
-
-int parse_identifier_vl (const char *str, value_list_t *vl) /* {{{ */
-{
-       char str_copy[6 * DATA_MAX_NAME_LEN];
-       char *host = NULL;
-       char *plugin = NULL;
-       char *plugin_instance = NULL;
-       char *type = NULL;
-       char *type_instance = NULL;
-       int status;
-
-       if ((str == NULL) || (vl == NULL))
-               return (EINVAL);
-
-       sstrncpy (str_copy, str, sizeof (str_copy));
-
-       status = parse_identifier (str_copy, &host,
-                       &plugin, &plugin_instance,
-                       &type, &type_instance);
-       if (status != 0)
-               return (status);
-
-       sstrncpy (vl->host, host, sizeof (vl->host));
-       sstrncpy (vl->plugin, plugin, sizeof (vl->plugin));
-       sstrncpy (vl->plugin_instance,
-                       (plugin_instance != NULL) ? plugin_instance : "",
-                       sizeof (vl->plugin_instance));
-       sstrncpy (vl->type, type, sizeof (vl->type));
-       sstrncpy (vl->type_instance,
-                       (type_instance != NULL) ? type_instance : "",
-                       sizeof (vl->type_instance));
-
-       return (0);
-} /* }}} int parse_identifier_vl */
-
-int parse_value (const char *value_orig, value_t *ret_value, int ds_type)
-{
-  char *value;
-  char *endptr = NULL;
-  size_t value_len;
-
-  if (value_orig == NULL)
-    return (EINVAL);
-
-  value = strdup (value_orig);
-  if (value == NULL)
-    return (ENOMEM);
-  value_len = strlen (value);
-
-  while ((value_len > 0) && isspace ((int) value[value_len - 1]))
-  {
-    value[value_len - 1] = 0;
-    value_len--;
-  }
-
-  switch (ds_type)
-  {
-    case DS_TYPE_COUNTER:
-      ret_value->counter = (counter_t) strtoull (value, &endptr, 0);
-      break;
-
-    case DS_TYPE_GAUGE:
-      ret_value->gauge = (gauge_t) strtod (value, &endptr);
-      break;
-
-    case DS_TYPE_DERIVE:
-      ret_value->derive = (derive_t) strtoll (value, &endptr, 0);
-      break;
-
-    case DS_TYPE_ABSOLUTE:
-      ret_value->absolute = (absolute_t) strtoull (value, &endptr, 0);
-      break;
-
-    default:
-      sfree (value);
-      ERROR ("parse_value: Invalid data source type: %i.", ds_type);
-      return -1;
-  }
-
-  if (value == endptr) {
-    ERROR ("parse_value: Failed to parse string as %s: %s.",
-        DS_TYPE_TO_STRING (ds_type), value);
-    sfree (value);
-    return -1;
-  }
-  else if ((NULL != endptr) && ('\0' != *endptr))
-    INFO ("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
-        "Input string was \"%s\".",
-        endptr, DS_TYPE_TO_STRING (ds_type), value_orig);
-
-  sfree (value);
-  return 0;
-} /* int parse_value */
-
-int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
-{
-       int i;
-       char *dummy;
-       char *ptr;
-       char *saveptr;
-
-       i = -1;
-       dummy = buffer;
-       saveptr = NULL;
-       while ((ptr = strtok_r (dummy, ":", &saveptr)) != NULL)
-       {
-               dummy = NULL;
-
-               if (i >= vl->values_len)
-               {
-                       /* Make sure i is invalid. */
-                       i = vl->values_len + 1;
-                       break;
-               }
-
-               if (i == -1)
-               {
-                       if (strcmp ("N", ptr) == 0)
-                               vl->time = cdtime ();
-                       else
-                       {
-                               char *endptr = NULL;
-                               double tmp;
-
-                               errno = 0;
-                               tmp = strtod (ptr, &endptr);
-                               if ((errno != 0)                    /* Overflow */
-                                               || (endptr == ptr)  /* Invalid string */
-                                               || (endptr == NULL) /* This should not happen */
-                                               || (*endptr != 0))  /* Trailing chars */
-                                       return (-1);
-
-                               vl->time = DOUBLE_TO_CDTIME_T (tmp);
-                       }
-               }
-               else
-               {
-                       if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
-                               vl->values[i].gauge = NAN;
-                       else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i].type))
-                               return -1;
-               }
-
-               i++;
-       } /* while (strtok_r) */
-
-       if ((ptr != NULL) || (i != vl->values_len))
-               return (-1);
-       return (0);
-} /* int parse_values */
-
-#if !HAVE_GETPWNAM_R
-int getpwnam_r (const char *name, struct passwd *pwbuf, char *buf,
-               size_t buflen, struct passwd **pwbufp)
-{
-       int status = 0;
-       struct passwd *pw;
-
-       memset (pwbuf, '\0', sizeof (struct passwd));
-
-       pthread_mutex_lock (&getpwnam_r_lock);
-
-       do
-       {
-               pw = getpwnam (name);
-               if (pw == NULL)
-               {
-                       status = (errno != 0) ? errno : ENOENT;
-                       break;
-               }
-
-#define GETPWNAM_COPY_MEMBER(member) \
-               if (pw->member != NULL) \
-               { \
-                       int len = strlen (pw->member); \
-                       if (len >= buflen) \
-                       { \
-                               status = ENOMEM; \
-                               break; \
-                       } \
-                       sstrncpy (buf, pw->member, buflen); \
-                       pwbuf->member = buf; \
-                       buf    += (len + 1); \
-                       buflen -= (len + 1); \
-               }
-               GETPWNAM_COPY_MEMBER(pw_name);
-               GETPWNAM_COPY_MEMBER(pw_passwd);
-               GETPWNAM_COPY_MEMBER(pw_gecos);
-               GETPWNAM_COPY_MEMBER(pw_dir);
-               GETPWNAM_COPY_MEMBER(pw_shell);
-
-               pwbuf->pw_uid = pw->pw_uid;
-               pwbuf->pw_gid = pw->pw_gid;
-
-               if (pwbufp != NULL)
-                       *pwbufp = pwbuf;
-       } while (0);
-
-       pthread_mutex_unlock (&getpwnam_r_lock);
-
-       return (status);
-} /* int getpwnam_r */
-#endif /* !HAVE_GETPWNAM_R */
-
-int notification_init (notification_t *n, int severity, const char *message,
-               const char *host,
-               const char *plugin, const char *plugin_instance,
-               const char *type, const char *type_instance)
-{
-       memset (n, '\0', sizeof (notification_t));
-
-       n->severity = severity;
-
-       if (message != NULL)
-               sstrncpy (n->message, message, sizeof (n->message));
-       if (host != NULL)
-               sstrncpy (n->host, host, sizeof (n->host));
-       if (plugin != NULL)
-               sstrncpy (n->plugin, plugin, sizeof (n->plugin));
-       if (plugin_instance != NULL)
-               sstrncpy (n->plugin_instance, plugin_instance,
-                               sizeof (n->plugin_instance));
-       if (type != NULL)
-               sstrncpy (n->type, type, sizeof (n->type));
-       if (type_instance != NULL)
-               sstrncpy (n->type_instance, type_instance,
-                               sizeof (n->type_instance));
-
-       return (0);
-} /* int notification_init */
-
-int walk_directory (const char *dir, dirwalk_callback_f callback,
-               void *user_data, int include_hidden)
-{
-       struct dirent *ent;
-       DIR *dh;
-       int success;
-       int failure;
-
-       success = 0;
-       failure = 0;
-
-       if ((dh = opendir (dir)) == NULL)
-       {
-               char errbuf[1024];
-               ERROR ("walk_directory: Cannot open '%s': %s", dir,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return -1;
-       }
-
-       while ((ent = readdir (dh)) != NULL)
-       {
-               int status;
-
-               if (include_hidden)
-               {
-                       if ((strcmp (".", ent->d_name) == 0)
-                                       || (strcmp ("..", ent->d_name) == 0))
-                               continue;
-               }
-               else /* if (!include_hidden) */
-               {
-                       if (ent->d_name[0]=='.')
-                               continue;
-               }
-
-               status = (*callback) (dir, ent->d_name, user_data);
-               if (status != 0)
-                       failure++;
-               else
-                       success++;
-       }
-
-       closedir (dh);
-
-       if ((success == 0) && (failure > 0))
-               return (-1);
-       return (0);
-}
-
-ssize_t read_file_contents (const char *filename, char *buf, size_t bufsize)
-{
-       FILE *fh;
-       ssize_t ret;
-
-       fh = fopen (filename, "r");
-       if (fh == NULL)
-               return (-1);
-
-       ret = (ssize_t) fread (buf, 1, bufsize, fh);
-       if ((ret == 0) && (ferror (fh) != 0))
-       {
-               ERROR ("read_file_contents: Reading file \"%s\" failed.",
-                               filename);
-               ret = -1;
-       }
-
-       fclose(fh);
-       return (ret);
-}
-
-counter_t counter_diff (counter_t old_value, counter_t new_value)
-{
-       counter_t diff;
-
-       if (old_value > new_value)
-       {
-               if (old_value <= 4294967295U)
-                       diff = (4294967295U - old_value) + new_value;
-               else
-                       diff = (18446744073709551615ULL - old_value)
-                               + new_value;
-       }
-       else
-       {
-               diff = new_value - old_value;
-       }
-
-       return (diff);
-} /* 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 value_to_rate (value_t *ret_rate, derive_t value, /* {{{ */
-               value_to_rate_state_t *state,
-               int ds_type, cdtime_t t)
-{
-       double interval;
-
-       /* Another invalid state: The time is not increasing. */
-       if (t <= state->last_time)
-       {
-               memset (state, 0, sizeof (*state));
-               return (EINVAL);
-       }
-
-       interval = CDTIME_T_TO_DOUBLE(t - state->last_time);
-
-       /* Previous value is invalid. */
-       if (state->last_time == 0) /* {{{ */
-       {
-               if (ds_type == DS_TYPE_DERIVE)
-               {
-                       state->last_value.derive = value;
-               }
-               else if (ds_type == DS_TYPE_COUNTER)
-               {
-                       state->last_value.counter = (counter_t) value;
-               }
-               else if (ds_type == DS_TYPE_ABSOLUTE)
-               {
-                       state->last_value.absolute = (absolute_t) value;
-               }
-               else
-               {
-                       assert (23 == 42);
-               }
-
-               state->last_time = t;
-               return (EAGAIN);
-       } /* }}} */
-
-       if (ds_type == DS_TYPE_DERIVE)
-       {
-               ret_rate->gauge = (value - state->last_value.derive) / interval;
-               state->last_value.derive = value;
-       }
-       else if (ds_type == DS_TYPE_COUNTER)
-       {
-               ret_rate->gauge = (((counter_t)value) - state->last_value.counter) / interval;
-               state->last_value.counter = (counter_t) value;
-       }
-       else if (ds_type == DS_TYPE_ABSOLUTE)
-       {
-               ret_rate->gauge = (((absolute_t)value) - state->last_value.absolute) / interval;
-               state->last_value.absolute = (absolute_t) value;
-       }
-       else
-       {
-               assert (23 == 42);
-       }
-
-        state->last_time = t;
-       return (0);
-} /* }}} value_t rate_to_value */
-
-int service_name_to_port_number (const char *service_name)
-{
-       struct addrinfo *ai_list;
-       struct addrinfo *ai_ptr;
-       struct addrinfo ai_hints;
-       int status;
-       int service_number;
-
-       if (service_name == NULL)
-               return (-1);
-
-       ai_list = NULL;
-       memset (&ai_hints, 0, sizeof (ai_hints));
-       ai_hints.ai_family = AF_UNSPEC;
-
-       status = getaddrinfo (/* node = */ NULL, service_name,
-                       &ai_hints, &ai_list);
-       if (status != 0)
-       {
-               ERROR ("service_name_to_port_number: getaddrinfo failed: %s",
-                               gai_strerror (status));
-               return (-1);
-       }
-
-       service_number = -1;
-       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
-       {
-               if (ai_ptr->ai_family == AF_INET)
-               {
-                       struct sockaddr_in *sa;
-
-                       sa = (void *) ai_ptr->ai_addr;
-                       service_number = (int) ntohs (sa->sin_port);
-               }
-               else if (ai_ptr->ai_family == AF_INET6)
-               {
-                       struct sockaddr_in6 *sa;
-
-                       sa = (void *) ai_ptr->ai_addr;
-                       service_number = (int) ntohs (sa->sin6_port);
-               }
-
-               if ((service_number > 0) && (service_number <= 65535))
-                       break;
-       }
-
-       freeaddrinfo (ai_list);
-
-       if ((service_number > 0) && (service_number <= 65535))
-               return (service_number);
-       return (-1);
-} /* int service_name_to_port_number */
-
-int strtoderive (const char *string, derive_t *ret_value) /* {{{ */
-{
-       derive_t tmp;
-       char *endptr;
-
-       if ((string == NULL) || (ret_value == NULL))
-               return (EINVAL);
-
-       errno = 0;
-       endptr = NULL;
-       tmp = (derive_t) strtoll (string, &endptr, /* base = */ 0);
-       if ((endptr == string) || (errno != 0))
-               return (-1);
-
-       *ret_value = tmp;
-       return (0);
-} /* }}} int strtoderive */
-
-int strarray_add (char ***ret_array, size_t *ret_array_len, char const *str) /* {{{ */
-{
-       char **array;
-       size_t array_len = *ret_array_len;
-
-       if (str == NULL)
-               return (EINVAL);
-
-       array = realloc (*ret_array,
-            (array_len + 1) * sizeof (*array));
-       if (array == NULL)
-               return (ENOMEM);
-       *ret_array = array;
-
-       array[array_len] = strdup (str);
-       if (array[array_len] == NULL)
-               return (ENOMEM);
-
-       array_len++;
-        *ret_array_len = array_len;
-       return (0);
-} /* }}} int strarray_add */
-
-void strarray_free (char **array, size_t array_len) /* {{{ */
-{
-       size_t i;
-
-       for (i = 0; i < array_len; i++)
-               sfree (array[i]);
-       sfree (array);
-} /* }}} void strarray_free */
diff --git a/src/common.h b/src/common.h
deleted file mode 100644 (file)
index b2e4c67..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-/**
- * collectd - src/common.h
- * Copyright (C) 2005-2014  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>
- *   Niki W. Waibel <niki.waibel@gmx.net>
-**/
-
-#ifndef COMMON_H
-#define COMMON_H
-
-#include "collectd.h"
-#include "plugin.h"
-
-#if HAVE_PWD_H
-# include <pwd.h>
-#endif
-
-#define sfree(ptr) \
-       do { \
-               if((ptr) != NULL) { \
-                       free(ptr); \
-               } \
-               (ptr) = NULL; \
-       } while (0)
-
-#define STATIC_ARRAY_SIZE(a) (sizeof (a) / sizeof (*(a)))
-
-#define IS_TRUE(s) ((strcasecmp ("true", (s)) == 0) \
-               || (strcasecmp ("yes", (s)) == 0) \
-               || (strcasecmp ("on", (s)) == 0))
-#define IS_FALSE(s) ((strcasecmp ("false", (s)) == 0) \
-               || (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;
-
-struct value_to_rate_state_s
-{
-  value_t last_value;
-  cdtime_t last_time;
-};
-typedef struct value_to_rate_state_s value_to_rate_state_t;
-
-char *sstrncpy (char *dest, const char *src, size_t n);
-
-__attribute__ ((format(printf,3,4)))
-int ssnprintf (char *dest, size_t n, const char *format, ...);
-
-__attribute__ ((format(printf,1,2)))
-char *ssnprintf_alloc (char const *format, ...);
-
-char *sstrdup(const char *s);
-void *smalloc(size_t size);
-char *sstrerror (int errnum, char *buf, size_t buflen);
-
-/*
- * NAME
- *   sread
- *
- * DESCRIPTION
- *   Reads exactly `n' bytes or fails. Syntax and other behavior is analogous
- *   to `read(2)'. If EOF is received the file descriptor is closed and an
- *   error is returned.
- *
- * PARAMETERS
- *   `fd'          File descriptor to write to.
- *   `buf'         Buffer that is to be written.
- *   `count'       Number of bytes in the buffer.
- *
- * RETURN VALUE
- *   Zero upon success or non-zero if an error occurred. `errno' is set in this
- *   case.
- */
-ssize_t sread (int fd, void *buf, size_t count);
-
-/*
- * NAME
- *   swrite
- *
- * DESCRIPTION
- *   Writes exactly `n' bytes or fails. Syntax and other behavior is analogous
- *   to `write(2)'.
- *
- * PARAMETERS
- *   `fd'          File descriptor to write to.
- *   `buf'         Buffer that is to be written.
- *   `count'       Number of bytes in the buffer.
- *
- * RETURN VALUE
- *   Zero upon success or non-zero if an error occurred. `errno' is set in this
- *   case.
- */
-ssize_t swrite (int fd, const void *buf, size_t count);
-
-/*
- * NAME
- *   strsplit
- *
- * DESCRIPTION
- *   Splits a string into parts and stores pointers to the parts in `fields'.
- *   The characters split at are: " ", "\t", "\r", and "\n".
- *
- * PARAMETERS
- *   `string'      String to split. This string will be modified. `fields' will
- *                 contain pointers to parts of this string, so free'ing it
- *                 will destroy `fields' as well.
- *   `fields'      Array of strings where pointers to the parts will be stored.
- *   `size'        Number of elements in the array. No more than `size'
- *                 pointers will be stored in `fields'.
- *
- * RETURN VALUE
- *    Returns the number of parts stored in `fields'.
- */
-int strsplit (char *string, char **fields, size_t size);
-
-/*
- * NAME
- *   strjoin
- *
- * DESCRIPTION
- *   Joins together several parts of a string using `sep' as a separator. This
- *   is equivalent to the Perl built-in `join'.
- *
- * PARAMETERS
- *   `dst'         Buffer where the result is stored.
- *   `dst_len'     Length of the destination buffer. No more than this many
- *                 bytes will be written to the memory pointed to by `dst',
- *                 including the trailing null-byte.
- *   `fields'      Array of strings to be joined.
- *   `fields_num'  Number of elements in the `fields' array.
- *   `sep'         String to be inserted between any two elements of `fields'.
- *                 This string is neither prepended nor appended to the result.
- *                 Instead of passing "" (empty string) one can pass NULL.
- *
- * RETURN VALUE
- *   Returns the number of characters in `dst', NOT including the trailing
- *   null-byte. If an error occurred (empty array or `dst' too small) a value
- *   smaller than zero will be returned.
- */
-int strjoin (char *dst, size_t dst_len, char **fields, size_t fields_num, const char *sep);
-
-/*
- * NAME
- *   escape_slashes
- *
- * DESCRIPTION
- *   Removes slashes ("/") from "buffer". If buffer contains a single slash,
- *   the result will be "root". Leading slashes are removed. All other slashes
- *   are replaced with underscores ("_").
- *   This function is used by plugin_dispatch_values() to escape all parts of
- *   the identifier.
- *
- * PARAMETERS
- *   `buffer'         String to be escaped.
- *   `buffer_size'    Size of the buffer. No more then this many bytes will be
- *                    written to `buffer', including the trailing null-byte.
- *
- * RETURN VALUE
- *   Returns zero upon success and a value smaller than zero upon failure.
- */
-int escape_slashes (char *buffer, size_t buffer_size);
-
-/*
- * NAME
- *   replace_special
- *
- * DESCRIPTION
- *   Replaces any special characters (anything that's not alpha-numeric or a
- *   dash) with an underscore.
- *
- *   E.g. "foo$bar&" would become "foo_bar_".
- *
- * PARAMETERS
- *   `buffer'      String to be handled.
- *   `buffer_size' Length of the string. The function returns after
- *                 encountering a null-byte or reading this many bytes.
- */
-void replace_special (char *buffer, size_t buffer_size);
-
-int strsubstitute (char *str, char c_from, char c_to);
-
-/*
- * NAME
- *   strunescape
- *
- * DESCRIPTION
- *   Replaces any escaped characters in a string with the appropriate special
- *   characters. The following escaped characters are recognized:
- *
- *     \t -> <tab>
- *     \n -> <newline>
- *     \r -> <carriage return>
- *
- *   For all other escacped characters only the backslash will be removed.
- *
- * PARAMETERS
- *   `buf'         String to be unescaped.
- *   `buf_len'     Length of the string, including the terminating null-byte.
- *
- * RETURN VALUE
- *   Returns zero upon success, a value less than zero else.
- */
-int strunescape (char *buf, size_t buf_len);
-
-/**
- * Removed trailing newline characters (CR and LF) from buffer, which must be
- * null terminated. Returns the length of the resulting string.
- */
-__attribute__((nonnull (1)))
-size_t strstripnewline (char *buffer);
-
-/*
- * NAME
- *   timeval_cmp
- *
- * DESCRIPTION
- *   Compare the two time values `tv0' and `tv1' and store the absolut value
- *   of the difference in the time value pointed to by `delta' if it does not
- *   equal NULL.
- *
- * RETURN VALUE
- *   Returns an integer less than, equal to, or greater than zero if `tv0' is
- *   less than, equal to, or greater than `tv1' respectively.
- */
-int timeval_cmp (struct timeval tv0, struct timeval tv1, struct timeval *delta);
-
-/* make sure tv_usec stores less than a second */
-#define NORMALIZE_TIMEVAL(tv) \
-       do { \
-               (tv).tv_sec += (tv).tv_usec / 1000000; \
-               (tv).tv_usec = (tv).tv_usec % 1000000; \
-       } while (0)
-
-/* make sure tv_sec stores less than a second */
-#define NORMALIZE_TIMESPEC(tv) \
-       do { \
-               (tv).tv_sec += (tv).tv_nsec / 1000000000; \
-               (tv).tv_nsec = (tv).tv_nsec % 1000000000; \
-       } while (0)
-
-int check_create_dir (const char *file_orig);
-
-#ifdef HAVE_LIBKSTAT
-int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name);
-long long get_kstat_value (kstat_t *ksp, char *name);
-#endif
-
-#ifndef HAVE_HTONLL
-unsigned long long ntohll (unsigned long long n);
-unsigned long long htonll (unsigned long long n);
-#endif
-
-#if FP_LAYOUT_NEED_NOTHING
-# define ntohd(d) (d)
-# define htond(d) (d)
-#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
-double ntohd (double d);
-double htond (double d);
-#else
-# error "Don't know how to convert between host and network representation of doubles."
-#endif
-
-int format_name (char *ret, int ret_len,
-               const char *hostname,
-               const char *plugin, const char *plugin_instance,
-               const char *type, const char *type_instance);
-#define FORMAT_VL(ret, ret_len, vl) \
-       format_name (ret, ret_len, (vl)->host, (vl)->plugin, (vl)->plugin_instance, \
-                       (vl)->type, (vl)->type_instance)
-int format_values (char *ret, size_t ret_len,
-               const data_set_t *ds, const value_list_t *vl,
-               _Bool store_rates);
-
-int parse_identifier (char *str, char **ret_host,
-               char **ret_plugin, char **ret_plugin_instance,
-               char **ret_type, char **ret_type_instance);
-int parse_identifier_vl (const char *str, value_list_t *vl);
-int parse_value (const char *value, value_t *ret_value, int ds_type);
-int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds);
-
-#if !HAVE_GETPWNAM_R
-int getpwnam_r (const char *name, struct passwd *pwbuf, char *buf,
-               size_t buflen, struct passwd **pwbufp);
-#endif
-
-int notification_init (notification_t *n, int severity, const char *message,
-               const char *host,
-               const char *plugin, const char *plugin_instance,
-               const char *type, const char *type_instance);
-#define NOTIFICATION_INIT_VL(n, vl) \
-       notification_init (n, NOTIF_FAILURE, NULL, \
-                       (vl)->host, (vl)->plugin, (vl)->plugin_instance, \
-                       (vl)->type, (vl)->type_instance)
-
-typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename,
-               void *user_data);
-int walk_directory (const char *dir, dirwalk_callback_f callback,
-               void *user_data, int hidden);
-/* Returns the number of bytes read or negative on error. */
-ssize_t read_file_contents (char const *filename, char *buf, size_t 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);
-
-int value_to_rate (value_t *ret_rate, derive_t value,
-               value_to_rate_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);
-
-/** Parse a string to a derive_t value. Returns zero on success or non-zero on
- * failure. If failure is returned, ret_value is not touched. */
-int strtoderive (const char *string, derive_t *ret_value);
-
-int strarray_add (char ***ret_array, size_t *ret_array_len, char const *str);
-void strarray_free (char **array, size_t array_len);
-
-#endif /* COMMON_H */
diff --git a/src/configfile.c b/src/configfile.c
deleted file mode 100644 (file)
index d2a307d..0000000
+++ /dev/null
@@ -1,1341 +0,0 @@
-/**
- * collectd - src/configfile.c
- * Copyright (C) 2005-2011  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>
- *   Sebastian tokkee Harl <sh at tokkee.org>
- **/
-
-#include "collectd.h"
-
-#include "liboconfig/oconfig.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "configfile.h"
-#include "types_list.h"
-#include "filter_chain.h"
-
-#if HAVE_WORDEXP_H
-# include <wordexp.h>
-#endif /* HAVE_WORDEXP_H */
-
-#if HAVE_FNMATCH_H
-# include <fnmatch.h>
-#endif /* HAVE_FNMATCH_H */
-
-#if HAVE_LIBGEN_H
-# include <libgen.h>
-#endif /* HAVE_LIBGEN_H */
-
-#define ESCAPE_NULL(str) ((str) == NULL ? "(null)" : (str))
-
-/*
- * Private types
- */
-typedef struct cf_callback
-{
-       const char  *type;
-       int  (*callback) (const char *, const char *);
-       const char **keys;
-       int    keys_num;
-       plugin_ctx_t ctx;
-       struct cf_callback *next;
-} cf_callback_t;
-
-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;
-
-typedef struct cf_value_map_s
-{
-       char *key;
-       int (*func) (const oconfig_item_t *);
-} cf_value_map_t;
-
-typedef struct cf_global_option_s
-{
-       char *key;
-       char *value;
-       char *def;
-} cf_global_option_t;
-
-/*
- * Prototypes of callback functions
- */
-static int dispatch_value_typesdb (const oconfig_item_t *ci);
-static int dispatch_value_plugindir (const oconfig_item_t *ci);
-static int dispatch_loadplugin (const oconfig_item_t *ci);
-
-/*
- * Private variables
- */
-static cf_callback_t *first_callback = NULL;
-static cf_complex_callback_t *complex_callback_head = NULL;
-
-static cf_value_map_t cf_value_map[] =
-{
-       {"TypesDB",    dispatch_value_typesdb},
-       {"PluginDir",  dispatch_value_plugindir},
-       {"LoadPlugin", dispatch_loadplugin}
-};
-static int cf_value_map_num = STATIC_ARRAY_SIZE (cf_value_map);
-
-static cf_global_option_t cf_global_options[] =
-{
-       {"BaseDir",     NULL, PKGLOCALSTATEDIR},
-       {"PIDFile",     NULL, PIDFILE},
-       {"Hostname",    NULL, NULL},
-       {"FQDNLookup",  NULL, "true"},
-       {"Interval",    NULL, NULL},
-       {"ReadThreads", NULL, "5"},
-       {"WriteThreads", NULL, "5"},
-       {"WriteQueueLimitHigh", NULL, NULL},
-       {"WriteQueueLimitLow", NULL, NULL},
-       {"Timeout",     NULL, "2"},
-       {"AutoLoadPlugin", NULL, "false"},
-       {"PreCacheChain",  NULL, "PreCache"},
-       {"PostCacheChain", NULL, "PostCache"}
-};
-static int cf_global_options_num = STATIC_ARRAY_SIZE (cf_global_options);
-
-static int cf_default_typesdb = 1;
-
-/*
- * Functions to handle register/unregister, search, and other plugin related
- * stuff
- */
-static cf_callback_t *cf_search (const char *type)
-{
-       cf_callback_t *cf_cb;
-
-       if (type == NULL)
-               return (NULL);
-
-       for (cf_cb = first_callback; cf_cb != NULL; cf_cb = cf_cb->next)
-               if (strcasecmp (cf_cb->type, type) == 0)
-                       break;
-
-       return (cf_cb);
-}
-
-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;
-       int i;
-
-       DEBUG ("type = %s, key = %s, value = %s",
-                       ESCAPE_NULL(type),
-                       ESCAPE_NULL(orig_key),
-                       ESCAPE_NULL(orig_value));
-
-       if ((cf_cb = cf_search (type)) == NULL)
-       {
-               WARNING ("Found a configuration for the `%s' plugin, but "
-                               "the plugin isn't loaded or didn't register "
-                               "a configuration callback.", type);
-               return (-1);
-       }
-
-       if ((key = strdup (orig_key)) == NULL)
-               return (1);
-       if ((value = strdup (orig_value)) == NULL)
-       {
-               free (key);
-               return (2);
-       }
-
-       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)
-                               && (strcasecmp (cf_cb->keys[i], key) == 0))
-               {
-                       ret = (*cf_cb->callback) (key, value);
-                       break;
-               }
-       }
-
-       plugin_set_ctx (old_ctx);
-
-       if (i >= cf_cb->keys_num)
-               WARNING ("Plugin `%s' did not register for value `%s'.", type, key);
-
-       free (key);
-       free (value);
-
-       DEBUG ("cf_dispatch: return (%i)", ret);
-
-       return (ret);
-} /* int cf_dispatch */
-
-static int dispatch_global_option (const oconfig_item_t *ci)
-{
-       if (ci->values_num != 1)
-               return (-1);
-       if (ci->values[0].type == OCONFIG_TYPE_STRING)
-               return (global_option_set (ci->key, ci->values[0].value.string));
-       else if (ci->values[0].type == OCONFIG_TYPE_NUMBER)
-       {
-               char tmp[128];
-               ssnprintf (tmp, sizeof (tmp), "%lf", ci->values[0].value.number);
-               return (global_option_set (ci->key, tmp));
-       }
-       else if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
-       {
-               if (ci->values[0].value.boolean)
-                       return (global_option_set (ci->key, "true"));
-               else
-                       return (global_option_set (ci->key, "false"));
-       }
-
-       return (-1);
-} /* int dispatch_global_option */
-
-static int dispatch_value_typesdb (const oconfig_item_t *ci)
-{
-       int i = 0;
-
-       assert (strcasecmp (ci->key, "TypesDB") == 0);
-
-       cf_default_typesdb = 0;
-
-       if (ci->values_num < 1) {
-               ERROR ("configfile: `TypesDB' needs at least one argument.");
-               return (-1);
-       }
-
-       for (i = 0; i < ci->values_num; ++i)
-       {
-               if (OCONFIG_TYPE_STRING != ci->values[i].type) {
-                       WARNING ("configfile: TypesDB: Skipping %i. argument which "
-                                       "is not a string.", i + 1);
-                       continue;
-               }
-
-               read_types_list (ci->values[i].value.string);
-       }
-       return (0);
-} /* int dispatch_value_typesdb */
-
-static int dispatch_value_plugindir (const oconfig_item_t *ci)
-{
-       assert (strcasecmp (ci->key, "PluginDir") == 0);
-       
-       if (ci->values_num != 1)
-               return (-1);
-       if (ci->values[0].type != OCONFIG_TYPE_STRING)
-               return (-1);
-
-       plugin_set_dir (ci->values[0].value.string);
-       return (0);
-}
-
-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)
-               return (-1);
-       if (ci->values[0].type != OCONFIG_TYPE_STRING)
-               return (-1);
-
-       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 ();
-
-       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\"",
-                                       ci->children[i].key, ci->values[0].value.string);
-               }
-       }
-
-       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)
-{
-       char  buffer[4096];
-       char *buffer_ptr;
-       int   buffer_free;
-       int i;
-
-       buffer_ptr = buffer;
-       buffer_free = sizeof (buffer);
-
-       for (i = 0; i < ci->values_num; i++)
-       {
-               int status = -1;
-
-               if (ci->values[i].type == OCONFIG_TYPE_STRING)
-                       status = ssnprintf (buffer_ptr, buffer_free, " %s",
-                                       ci->values[i].value.string);
-               else if (ci->values[i].type == OCONFIG_TYPE_NUMBER)
-                       status = ssnprintf (buffer_ptr, buffer_free, " %lf",
-                                       ci->values[i].value.number);
-               else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
-                       status = ssnprintf (buffer_ptr, buffer_free, " %s",
-                                       ci->values[i].value.boolean
-                                       ? "true" : "false");
-
-               if ((status < 0) || (status >= buffer_free))
-                       return (-1);
-               buffer_free -= status;
-               buffer_ptr  += status;
-       }
-       /* skip the initial space */
-       buffer_ptr = buffer + 1;
-
-       return (cf_dispatch (plugin, ci->key, buffer_ptr));
-} /* int dispatch_value_plugin */
-
-static int dispatch_value (const oconfig_item_t *ci)
-{
-       int ret = -2;
-       int i;
-
-       for (i = 0; i < cf_value_map_num; i++)
-               if (strcasecmp (cf_value_map[i].key, ci->key) == 0)
-               {
-                       ret = cf_value_map[i].func (ci);
-                       break;
-               }
-
-       for (i = 0; i < cf_global_options_num; i++)
-               if (strcasecmp (cf_global_options[i].key, ci->key) == 0)
-               {
-                       ret = dispatch_global_option (ci);
-                       break;
-               }
-
-       return (ret);
-} /* int dispatch_value */
-
-static int dispatch_block_plugin (oconfig_item_t *ci)
-{
-       int i;
-       char *name;
-
-       cf_complex_callback_t *cb;
-
-       if (strcasecmp (ci->key, "Plugin") != 0)
-               return (-1);
-       if (ci->values_num < 1)
-               return (-1);
-       if (ci->values[0].type != OCONFIG_TYPE_STRING)
-               return (-1);
-
-       name = ci->values[0].value.string;
-
-       if (IS_TRUE (global_option_get ("AutoLoadPlugin")))
-       {
-               int status;
-
-               status = plugin_load (name, /* flags = */ 0);
-               if (status != 0)
-               {
-                       ERROR ("Automatically loading plugin \"%s\" failed "
-                                       "with status %i.", name, status);
-                       return (status);
-               }
-       }
-
-       /* Check for a complex callback first */
-       for (cb = complex_callback_head; cb != NULL; cb = cb->next)
-       {
-               if (strcasecmp (name, cb->type) == 0)
-               {
-                       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++)
-       {
-               if (ci->children[i].children == NULL)
-                       dispatch_value_plugin (name, ci->children + i);
-               else
-               {
-                       WARNING ("There is a `%s' block within the "
-                                       "configuration for the %s plugin. "
-                                       "The plugin either only expects "
-                                       "\"simple\" configuration statements "
-                                       "or wasn't loaded using `LoadPlugin'."
-                                       " Please check your configuration.",
-                                       ci->children[i].key, name);
-               }
-       }
-
-       return (0);
-}
-
-
-static int dispatch_block (oconfig_item_t *ci)
-{
-       if (strcasecmp (ci->key, "LoadPlugin") == 0)
-               return (dispatch_loadplugin (ci));
-       else if (strcasecmp (ci->key, "Plugin") == 0)
-               return (dispatch_block_plugin (ci));
-       else if (strcasecmp (ci->key, "Chain") == 0)
-               return (fc_configure (ci));
-
-       return (0);
-}
-
-static int cf_ci_replace_child (oconfig_item_t *dst, oconfig_item_t *src,
-               int offset)
-{
-       oconfig_item_t *temp;
-       int i;
-
-       assert (offset >= 0);
-       assert (dst->children_num > offset);
-
-       /* Free the memory used by the replaced child. Usually that's the
-        * `Include "blah"' statement. */
-       temp = dst->children + offset;
-       for (i = 0; i < temp->values_num; i++)
-       {
-               if (temp->values[i].type == OCONFIG_TYPE_STRING)
-               {
-                       sfree (temp->values[i].value.string);
-               }
-       }
-       sfree (temp->values);
-       temp = NULL;
-
-       /* If (src->children_num == 0) the array size is decreased. If offset
-        * is _not_ the last element, (offset < (dst->children_num - 1)), then
-        * we need to move the trailing elements before resizing the array. */
-       if ((src->children_num == 0) && (offset < (dst->children_num - 1)))
-       {
-               int nmemb = dst->children_num - (offset + 1);
-               memmove (dst->children + offset, dst->children + offset + 1,
-                               sizeof (oconfig_item_t) * nmemb);
-       }
-
-       /* Resize the memory containing the children to be big enough to hold
-        * all children. */
-       if (dst->children_num + src->children_num - 1 == 0)
-       {
-               dst->children_num = 0;
-               return (0);
-       }
-
-       temp = (oconfig_item_t *) realloc (dst->children,
-                       sizeof (oconfig_item_t)
-                       * (dst->children_num + src->children_num - 1));
-       if (temp == NULL)
-       {
-               ERROR ("configfile: realloc failed.");
-               return (-1);
-       }
-       dst->children = temp;
-
-       /* If there are children behind the include statement, and they have
-        * not yet been moved because (src->children_num == 0), then move them
-        * to the end of the list, so that the new children have room before
-        * them. */
-       if ((src->children_num > 0)
-                       && ((dst->children_num - (offset + 1)) > 0))
-       {
-               int nmemb = dst->children_num - (offset + 1);
-               int old_offset = offset + 1;
-               int new_offset = offset + src->children_num;
-
-               memmove (dst->children + new_offset,
-                               dst->children + old_offset,
-                               sizeof (oconfig_item_t) * nmemb);
-       }
-
-       /* Last but not least: If there are new children, copy them to the
-        * memory reserved for them. */
-       if (src->children_num > 0)
-       {
-               memcpy (dst->children + offset,
-                               src->children,
-                               sizeof (oconfig_item_t) * src->children_num);
-       }
-
-       /* Update the number of children. */
-       dst->children_num += (src->children_num - 1);
-
-       return (0);
-} /* int cf_ci_replace_child */
-
-static int cf_ci_append_children (oconfig_item_t *dst, oconfig_item_t *src)
-{
-       oconfig_item_t *temp;
-
-       if ((src == NULL) || (src->children_num == 0))
-               return (0);
-
-       temp = (oconfig_item_t *) realloc (dst->children,
-                       sizeof (oconfig_item_t)
-                       * (dst->children_num + src->children_num));
-       if (temp == NULL)
-       {
-               ERROR ("configfile: realloc failed.");
-               return (-1);
-       }
-       dst->children = temp;
-
-       memcpy (dst->children + dst->children_num,
-                       src->children,
-                       sizeof (oconfig_item_t)
-                       * src->children_num);
-       dst->children_num += src->children_num;
-
-       return (0);
-} /* int cf_ci_append_children */
-
-#define CF_MAX_DEPTH 8
-static oconfig_item_t *cf_read_generic (const char *path,
-               const char *pattern, int depth);
-
-static int cf_include_all (oconfig_item_t *root, int depth)
-{
-       int i;
-
-       for (i = 0; i < root->children_num; i++)
-       {
-               oconfig_item_t *new;
-               oconfig_item_t *old;
-
-               char *pattern = NULL;
-
-               int j;
-
-               if (strcasecmp (root->children[i].key, "Include") != 0)
-                       continue;
-
-               old = root->children + i;
-
-               if ((old->values_num != 1)
-                               || (old->values[0].type != OCONFIG_TYPE_STRING))
-               {
-                       ERROR ("configfile: `Include' needs exactly one string argument.");
-                       continue;
-               }
-
-               for (j = 0; j < old->children_num; ++j)
-               {
-                       oconfig_item_t *child = old->children + j;
-
-                       if (strcasecmp (child->key, "Filter") == 0)
-                               cf_util_get_string (child, &pattern);
-                       else
-                               ERROR ("configfile: Option `%s' not allowed in <Include> block.",
-                                               child->key);
-               }
-
-               new = cf_read_generic (old->values[0].value.string, pattern, depth + 1);
-               sfree (pattern);
-
-               if (new == NULL)
-                       return (-1);
-
-               /* Now replace the i'th child in `root' with `new'. */
-               if (cf_ci_replace_child (root, new, i) < 0)
-                       return (-1);
-
-               /* ... and go back to the new i'th child. */
-               --i;
-
-               sfree (new->values);
-               sfree (new);
-       } /* for (i = 0; i < root->children_num; i++) */
-
-       return (0);
-} /* int cf_include_all */
-
-static oconfig_item_t *cf_read_file (const char *file,
-               const char *pattern, int depth)
-{
-       oconfig_item_t *root;
-       int status;
-
-       assert (depth < CF_MAX_DEPTH);
-
-       if (pattern != NULL) {
-#if HAVE_FNMATCH_H && HAVE_LIBGEN_H
-               char *tmp = sstrdup (file);
-               char *filename = basename (tmp);
-
-               if ((filename != NULL) && (fnmatch (pattern, filename, 0) != 0)) {
-                       DEBUG ("configfile: Not including `%s' because it "
-                                       "does not match pattern `%s'.",
-                                       filename, pattern);
-                       free (tmp);
-                       return (NULL);
-               }
-
-               free (tmp);
-#else
-               ERROR ("configfile: Cannot apply pattern filter '%s' "
-                               "to file '%s': functions basename() and / or "
-                               "fnmatch() not available.", pattern, file);
-#endif /* HAVE_FNMATCH_H && HAVE_LIBGEN_H */
-       }
-
-       root = oconfig_parse_file (file);
-       if (root == NULL)
-       {
-               ERROR ("configfile: Cannot read file `%s'.", file);
-               return (NULL);
-       }
-
-       status = cf_include_all (root, depth);
-       if (status != 0)
-       {
-               oconfig_free (root);
-               return (NULL);
-       }
-
-       return (root);
-} /* oconfig_item_t *cf_read_file */
-
-static int cf_compare_string (const void *p1, const void *p2)
-{
-       return strcmp (*(const char **) p1, *(const char **) p2);
-}
-
-static oconfig_item_t *cf_read_dir (const char *dir,
-               const char *pattern, int depth)
-{
-       oconfig_item_t *root = NULL;
-       DIR *dh;
-       struct dirent *de;
-       char **filenames = NULL;
-       int filenames_num = 0;
-       int status;
-       int i;
-
-       assert (depth < CF_MAX_DEPTH);
-
-       dh = opendir (dir);
-       if (dh == NULL)
-       {
-               char errbuf[1024];
-               ERROR ("configfile: opendir failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (NULL);
-       }
-
-       root = (oconfig_item_t *) malloc (sizeof (oconfig_item_t));
-       if (root == NULL)
-       {
-               ERROR ("configfile: malloc failed.");
-               return (NULL);
-       }
-       memset (root, 0, sizeof (oconfig_item_t));
-
-       while ((de = readdir (dh)) != NULL)
-       {
-               char   name[1024];
-               char **tmp;
-
-               if ((de->d_name[0] == '.') || (de->d_name[0] == 0))
-                       continue;
-
-               status = ssnprintf (name, sizeof (name), "%s/%s",
-                               dir, de->d_name);
-               if ((status < 0) || ((size_t) status >= sizeof (name)))
-               {
-                       ERROR ("configfile: Not including `%s/%s' because its"
-                                       " name is too long.",
-                                       dir, de->d_name);
-                       for (i = 0; i < filenames_num; ++i)
-                               free (filenames[i]);
-                       free (filenames);
-                       free (root);
-                       return (NULL);
-               }
-
-               ++filenames_num;
-               tmp = (char **) realloc (filenames,
-                               filenames_num * sizeof (*filenames));
-               if (tmp == NULL) {
-                       ERROR ("configfile: realloc failed.");
-                       for (i = 0; i < filenames_num - 1; ++i)
-                               free (filenames[i]);
-                       free (filenames);
-                       free (root);
-                       return (NULL);
-               }
-               filenames = tmp;
-
-               filenames[filenames_num - 1] = sstrdup (name);
-       }
-
-       qsort ((void *) filenames, filenames_num, sizeof (*filenames),
-                       cf_compare_string);
-
-       for (i = 0; i < filenames_num; ++i)
-       {
-               oconfig_item_t *temp;
-               char *name = filenames[i];
-
-               temp = cf_read_generic (name, pattern, depth);
-               if (temp == NULL)
-               {
-                       /* An error should already have been reported. */
-                       sfree (name);
-                       continue;
-               }
-
-               cf_ci_append_children (root, temp);
-               sfree (temp->children);
-               sfree (temp);
-
-               free (name);
-       }
-
-       free(filenames);
-       return (root);
-} /* oconfig_item_t *cf_read_dir */
-
-/* 
- * cf_read_generic
- *
- * Path is stat'ed and either cf_read_file or cf_read_dir is called
- * accordingly.
- *
- * There are two versions of this function: If `wordexp' exists shell wildcards
- * will be expanded and the function will include all matches found. If
- * `wordexp' (or, more precisely, it's header file) is not available the
- * simpler function is used which does not do any such expansion.
- */
-#if HAVE_WORDEXP_H
-static oconfig_item_t *cf_read_generic (const char *path,
-               const char *pattern, int depth)
-{
-       oconfig_item_t *root = NULL;
-       int status;
-       const char *path_ptr;
-       wordexp_t we;
-       size_t i;
-
-       if (depth >= CF_MAX_DEPTH)
-       {
-               ERROR ("configfile: Not including `%s' because the maximum "
-                               "nesting depth has been reached.", path);
-               return (NULL);
-       }
-
-       status = wordexp (path, &we, WRDE_NOCMD);
-       if (status != 0)
-       {
-               ERROR ("configfile: wordexp (%s) failed.", path);
-               return (NULL);
-       }
-
-       root = (oconfig_item_t *) malloc (sizeof (oconfig_item_t));
-       if (root == NULL)
-       {
-               ERROR ("configfile: malloc failed.");
-               return (NULL);
-       }
-       memset (root, '\0', sizeof (oconfig_item_t));
-
-       /* wordexp() might return a sorted list already. That's not
-        * documented though, so let's make sure we get what we want. */
-       qsort ((void *) we.we_wordv, we.we_wordc, sizeof (*we.we_wordv),
-                       cf_compare_string);
-
-       for (i = 0; i < we.we_wordc; i++)
-       {
-               oconfig_item_t *temp;
-               struct stat statbuf;
-
-               path_ptr = we.we_wordv[i];
-
-               status = stat (path_ptr, &statbuf);
-               if (status != 0)
-               {
-                       char errbuf[1024];
-                       WARNING ("configfile: stat (%s) failed: %s",
-                                       path_ptr,
-                                       sstrerror (errno, errbuf, sizeof (errbuf)));
-                       continue;
-               }
-
-               if (S_ISREG (statbuf.st_mode))
-                       temp = cf_read_file (path_ptr, pattern, depth);
-               else if (S_ISDIR (statbuf.st_mode))
-                       temp = cf_read_dir (path_ptr, pattern, depth);
-               else
-               {
-                       WARNING ("configfile: %s is neither a file nor a "
-                                       "directory.", path);
-                       continue;
-               }
-
-               if (temp == NULL) {
-                       oconfig_free (root);
-                       return (NULL);
-               }
-
-               cf_ci_append_children (root, temp);
-               sfree (temp->children);
-               sfree (temp);
-       }
-
-       wordfree (&we);
-
-       return (root);
-} /* oconfig_item_t *cf_read_generic */
-/* #endif HAVE_WORDEXP_H */
-
-#else /* if !HAVE_WORDEXP_H */
-static oconfig_item_t *cf_read_generic (const char *path,
-               const char *pattern, int depth)
-{
-       struct stat statbuf;
-       int status;
-
-       if (depth >= CF_MAX_DEPTH)
-       {
-               ERROR ("configfile: Not including `%s' because the maximum "
-                               "nesting depth has been reached.", path);
-               return (NULL);
-       }
-
-       status = stat (path, &statbuf);
-       if (status != 0)
-       {
-               char errbuf[1024];
-               ERROR ("configfile: stat (%s) failed: %s",
-                               path,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (NULL);
-       }
-
-       if (S_ISREG (statbuf.st_mode))
-               return (cf_read_file (path, pattern, depth));
-       else if (S_ISDIR (statbuf.st_mode))
-               return (cf_read_dir (path, pattern, depth));
-
-       ERROR ("configfile: %s is neither a file nor a directory.", path);
-       return (NULL);
-} /* oconfig_item_t *cf_read_generic */
-#endif /* !HAVE_WORDEXP_H */
-
-/* 
- * Public functions
- */
-int global_option_set (const char *option, const char *value)
-{
-       int i;
-
-       DEBUG ("option = %s; value = %s;", option, value);
-
-       for (i = 0; i < cf_global_options_num; i++)
-               if (strcasecmp (cf_global_options[i].key, option) == 0)
-                       break;
-
-       if (i >= cf_global_options_num)
-               return (-1);
-
-       if (strcasecmp (option, "PIDFile") == 0 && pidfile_from_cli == 1)
-       {
-               DEBUG ("Configfile: Ignoring `PIDFILE' option because "
-                       "command-line option `-P' take precedence.");
-               return (0);
-       }
-
-       sfree (cf_global_options[i].value);
-
-       if (value != NULL)
-               cf_global_options[i].value = strdup (value);
-       else
-               cf_global_options[i].value = NULL;
-
-       return (0);
-}
-
-const char *global_option_get (const char *option)
-{
-       int i;
-
-       for (i = 0; i < cf_global_options_num; i++)
-               if (strcasecmp (cf_global_options[i].key, option) == 0)
-                       break;
-
-       if (i >= cf_global_options_num)
-               return (NULL);
-       
-       return ((cf_global_options[i].value != NULL)
-                       ? cf_global_options[i].value
-                       : cf_global_options[i].def);
-} /* char *global_option_get */
-
-long global_option_get_long (const char *option, long default_value)
-{
-               const char *str;
-               long value;
-
-               str = global_option_get (option);
-               if (NULL == str)
-                       return (default_value);
-
-               errno = 0;
-               value = strtol (str, /* endptr = */ NULL, /* base = */ 0);
-               if (errno != 0)
-                       return (default_value);
-
-               return (value);
-} /* char *global_option_get_long */
-
-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;
-
-       for (prev = NULL, this = first_callback;
-                       this != NULL;
-                       prev = this, this = this->next)
-               if (strcasecmp (this->type, type) == 0)
-               {
-                       if (prev == NULL)
-                               first_callback = this->next;
-                       else
-                               prev->next = this->next;
-
-                       free (this);
-                       break;
-               }
-} /* void cf_unregister */
-
-void cf_unregister_complex (const char *type)
-{
-       cf_complex_callback_t *this, *prev;
-
-       for (prev = NULL, this = complex_callback_head;
-                       this != NULL;
-                       prev = this, this = this->next)
-               if (strcasecmp (this->type, type) == 0)
-               {
-                       if (prev == NULL)
-                               complex_callback_head = this->next;
-                       else
-                               prev->next = this->next;
-
-                       sfree (this->type);
-                       sfree (this);
-                       break;
-               }
-} /* void cf_unregister */
-
-void cf_register (const char *type,
-               int (*callback) (const char *, const char *),
-               const char **keys, int keys_num)
-{
-       cf_callback_t *cf_cb;
-
-       /* Remove this module from the list, if it already exists */
-       cf_unregister (type);
-
-       /* This pointer will be free'd in `cf_unregister' */
-       if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL)
-               return;
-
-       cf_cb->type     = 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;
-} /* void cf_register */
-
-int cf_register_complex (const char *type, int (*callback) (oconfig_item_t *))
-{
-       cf_complex_callback_t *new;
-
-       new = (cf_complex_callback_t *) malloc (sizeof (cf_complex_callback_t));
-       if (new == NULL)
-               return (-1);
-
-       new->type = strdup (type);
-       if (new->type == NULL)
-       {
-               sfree (new);
-               return (-1);
-       }
-
-       new->callback = callback;
-       new->next = NULL;
-
-       new->ctx = plugin_get_ctx ();
-
-       if (complex_callback_head == NULL)
-       {
-               complex_callback_head = new;
-       }
-       else
-       {
-               cf_complex_callback_t *last = complex_callback_head;
-               while (last->next != NULL)
-                       last = last->next;
-               last->next = new;
-       }
-
-       return (0);
-} /* int cf_register_complex */
-
-int cf_read (char *filename)
-{
-       oconfig_item_t *conf;
-       int i;
-
-       conf = cf_read_generic (filename, /* pattern = */ NULL, /* depth = */ 0);
-       if (conf == NULL)
-       {
-               ERROR ("Unable to read config file %s.", filename);
-               return (-1);
-       }
-       else if (conf->children_num == 0)
-       {
-               ERROR ("Configuration file %s is empty.", filename);
-               oconfig_free (conf);
-               return (-1);
-       }
-
-       for (i = 0; i < conf->children_num; i++)
-       {
-               if (conf->children[i].children == NULL)
-                       dispatch_value (conf->children + i);
-               else
-                       dispatch_block (conf->children + i);
-       }
-
-       oconfig_free (conf);
-
-       /* Read the default types.db if no `TypesDB' option was given. */
-       if (cf_default_typesdb)
-               read_types_list (PKGDATADIR"/types.db");
-
-       return (0);
-} /* int cf_read */
-
-/* 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. */
-int cf_util_get_string (const oconfig_item_t *ci, char **ret_string) /* {{{ */
-{
-       char *string;
-
-       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
-       {
-               ERROR ("cf_util_get_string: The %s option requires "
-                               "exactly one string argument.", ci->key);
-               return (-1);
-       }
-
-       string = strdup (ci->values[0].value.string);
-       if (string == NULL)
-               return (-1);
-
-       if (*ret_string != NULL)
-               sfree (*ret_string);
-       *ret_string = string;
-
-       return (0);
-} /* }}} int cf_util_get_string */
-
-/* Assures the config option is a string and copies it to the provided buffer.
- * Assures null-termination. */
-int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer, /* {{{ */
-               size_t buffer_size)
-{
-       if ((ci == NULL) || (buffer == NULL) || (buffer_size < 1))
-               return (EINVAL);
-
-       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
-       {
-               ERROR ("cf_util_get_string_buffer: The %s option requires "
-                               "exactly one string argument.", ci->key);
-               return (-1);
-       }
-
-       strncpy (buffer, ci->values[0].value.string, buffer_size);
-       buffer[buffer_size - 1] = 0;
-
-       return (0);
-} /* }}} int cf_util_get_string_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) /* {{{ */
-{
-       if ((ci == NULL) || (ret_value == NULL))
-               return (EINVAL);
-
-       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
-       {
-               ERROR ("cf_util_get_int: The %s option requires "
-                               "exactly one numeric argument.", ci->key);
-               return (-1);
-       }
-
-       *ret_value = (int) ci->values[0].value.number;
-
-       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))
-               return (EINVAL);
-
-       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
-       {
-               ERROR ("cf_util_get_boolean: The %s option requires "
-                               "exactly one boolean argument.", ci->key);
-               return (-1);
-       }
-
-       *ret_bool = ci->values[0].value.boolean ? 1 : 0;
-
-       return (0);
-} /* }}} int cf_util_get_boolean */
-
-int cf_util_get_flag (const oconfig_item_t *ci, /* {{{ */
-               unsigned int *ret_value, unsigned int flag)
-{
-       int status;
-       _Bool b;
-
-       if (ret_value == NULL)
-               return (EINVAL);
-
-       b = 0;
-       status = cf_util_get_boolean (ci, &b);
-       if (status != 0)
-               return (status);
-
-       if (b)
-       {
-               *ret_value |= flag;
-       }
-       else
-       {
-               *ret_value &= ~flag;
-       }
-
-       return (0);
-} /* }}} int cf_util_get_flag */
-
-/* Assures that the config option is a string or a number if the correct range
- * of 1-65535. The string is then converted to a port number using
- * `service_name_to_port_number' and returned.
- * Returns the port number in the range [1-65535] or less than zero upon
- * failure. */
-int cf_util_get_port_number (const oconfig_item_t *ci) /* {{{ */
-{
-       int tmp;
-
-       if ((ci->values_num != 1)
-                       || ((ci->values[0].type != OCONFIG_TYPE_STRING)
-                               && (ci->values[0].type != OCONFIG_TYPE_NUMBER)))
-       {
-               ERROR ("cf_util_get_port_number: The \"%s\" option requires "
-                               "exactly one string argument.", ci->key);
-               return (-1);
-       }
-
-       if (ci->values[0].type == OCONFIG_TYPE_STRING)
-               return (service_name_to_port_number (ci->values[0].value.string));
-
-       assert (ci->values[0].type == OCONFIG_TYPE_NUMBER);
-       tmp = (int) (ci->values[0].value.number + 0.5);
-       if ((tmp < 1) || (tmp > 65535))
-       {
-               ERROR ("cf_util_get_port_number: The \"%s\" option requires "
-                               "a service name or a port number. The number "
-                               "you specified, %i, is not in the valid "
-                               "range of 1-65535.",
-                               ci->key, tmp);
-               return (-1);
-       }
-
-       return (tmp);
-} /* }}} int cf_util_get_port_number */
-
-int cf_util_get_service (const oconfig_item_t *ci, char **ret_string) /* {{{ */
-{
-       int port;
-       char *service;
-       int status;
-
-       if (ci->values_num != 1)
-       {
-               ERROR ("cf_util_get_service: The %s option requires exactly "
-                               "one argument.", ci->key);
-               return (-1);
-       }
-
-       if (ci->values[0].type == OCONFIG_TYPE_STRING)
-               return (cf_util_get_string (ci, ret_string));
-       if (ci->values[0].type != OCONFIG_TYPE_NUMBER)
-       {
-               ERROR ("cf_util_get_service: The %s option requires "
-                               "exactly one string or numeric argument.",
-                               ci->key);
-       }
-
-       port = 0;
-       status = cf_util_get_int (ci, &port);
-       if (status != 0)
-               return (status);
-       else if ((port < 1) || (port > 65535))
-       {
-               ERROR ("cf_util_get_service: The port number given "
-                               "for the %s option is out of "
-                               "range (%i).", ci->key, port);
-               return (-1);
-       }
-
-       service = malloc (6);
-       if (service == NULL)
-       {
-               ERROR ("cf_util_get_service: Out of memory.");
-               return (-1);
-       }
-       ssnprintf (service, 6, "%i", port);
-
-       sfree (*ret_string);
-       *ret_string = service;
-
-       return (0);
-} /* }}} int cf_util_get_service */
-
-int cf_util_get_cdtime (const oconfig_item_t *ci, cdtime_t *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_cdtime: The %s option requires "
-                               "exactly one numeric argument.", ci->key);
-               return (-1);
-       }
-
-       if (ci->values[0].value.number < 0.0)
-       {
-               ERROR ("cf_util_get_cdtime: The numeric argument of the %s "
-                               "option must not be negative.", ci->key);
-               return (-1);
-       }
-
-       *ret_value = DOUBLE_TO_CDTIME_T (ci->values[0].value.number);
-
-       return (0);
-} /* }}} int cf_util_get_cdtime */
-
diff --git a/src/configfile.h b/src/configfile.h
deleted file mode 100644 (file)
index 92c09de..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/**
- * collectd - src/configfile.h
- * Copyright (C) 2005-2011  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 CONFIGFILE_H
-#define CONFIGFILE_H
-
-#include "collectd.h"
-#include "utils_time.h"
-#include "liboconfig/oconfig.h"
-
-/*
- * DESCRIPTION
- *  Remove a registered plugin from the internal data structures.
- * 
- * PARAMETERS
- *  `type'      Name of the plugin (must be the same as passed to
- *              `plugin_register'
- */
-void cf_unregister (const char *type);
-void cf_unregister_complex (const char *type);
-
-/*
- * DESCRIPTION
- *  `cf_register' is called by plugins that wish to receive config keys. The
- *  plugin will then receive all keys it registered for if they're found in a
- *  `<Plugin $type>' section.
- *
- * PARAMETERS
- *  `type'      Name of the plugin (must be the same as passed to
- *              `plugin_register'
- *  `callback'  Pointer to the callback function. The callback must return zero
- *              upon success, a value smaller than zero if it doesn't know how
- *              to handle the `key' passed to it (the first argument) or a
- *              value greater than zero if it knows how to handle the key but
- *              failed.
- *  `keys'      Array of key values this plugin wished to receive. The last
- *              element must be a NULL-pointer.
- *  `keys_num'  Number of elements in the array (not counting the last NULL-
- *              pointer.
- *
- * NOTES
- *  `cf_unregister' will be called for `type' to make sure only one record
- *  exists for each `type' at any time. This means that `cf_register' may be
- *  called multiple times, but only the last call will have an effect.
- */
-void cf_register (const char *type,
-               int (*callback) (const char *, const char *),
-               const char **keys, int keys_num);
-
-int cf_register_complex (const char *type, int (*callback) (oconfig_item_t *));
-
-/*
- * DESCRIPTION
- *  `cf_read' reads the config file `filename' and dispatches the read
- *  information to functions/variables. Most important: Is calls `plugin_load'
- *  to load specific plugins, depending on the current mode of operation.
- *
- * PARAMETERS
- *  `filename'  An additional filename to look for. This function calls
- *              `lc_process' which already searches many standard locations..
- *              If set to NULL will use the `CONFIGFILE' define.
- *
- * RETURN VALUE
- *  Returns zero upon success and non-zero otherwise. A error-message will have
- *  been printed in this case.
- */
-int cf_read (char *filename);
-
-int global_option_set (const char *option, const char *value);
-const char *global_option_get (const char *option);
-long global_option_get_long (const char *option, long default_value);
-long global_option_get_long_in_range (const char *option, long default_value, long min, long max);
-
-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. */
-int cf_util_get_string (const oconfig_item_t *ci, char **ret_string);
-
-/* Assures the config option is a string and copies it to the provided buffer.
- * Assures null-termination. */
-int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer,
-               size_t buffer_size);
-
-/* 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);
-
-/* Assures the config option is a boolean and set or unset the given flag in
- * `ret_value' as appropriate. Returns non-zero on error. */
-int cf_util_get_flag (const oconfig_item_t *ci,
-               unsigned int *ret_value, unsigned int flag);
-
-/* Assures that the config option is a string or a number if the correct range
- * of 1-65535. The string is then converted to a port number using
- * `service_name_to_port_number' and returned.
- * Returns the port number in the range [1-65535] or less than zero upon
- * failure. */
-int cf_util_get_port_number (const oconfig_item_t *ci);
-
-/* Assures that the config option is either a service name (a string) or a port
- * number (an integer in the appropriate range) and returns a newly allocated
- * string. If ret_string points to a non-NULL pointer, it is freed before
- * assigning a new value. */
-int cf_util_get_service (const oconfig_item_t *ci, char **ret_string);
-
-int cf_util_get_cdtime (const oconfig_item_t *ci, cdtime_t *ret_value);
-
-#endif /* defined(CONFIGFILE_H) */
index 1ab1e6e..cf0eb6f 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -1,9 +1,10 @@
 /**
  * collectd - src/cpu.c
- * Copyright (C) 2005-2010  Florian octo Forster
- * Copyright (C) 2008      Oleg King
- * Copyright (C) 2009      Simon Kuhnle
- * Copyright (C) 2009      Manuel Sanmartin
+ * Copyright (C) 2005-2014  Florian octo Forster
+ * Copyright (C) 2008       Oleg King
+ * Copyright (C) 2009       Simon Kuhnle
+ * Copyright (C) 2009       Manuel Sanmartin
+ * Copyright (C) 2013-2014  Pierre-Yves Ritschard
  *
  * 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
@@ -11,7 +12,7 @@
  *
  * 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
+ * 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
@@ -23,6 +24,7 @@
  *   Oleg King <king2 at kaluga.ru>
  *   Simon Kuhnle <simon at blarzwurst.de>
  *   Manuel Sanmartin
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
  **/
 
 #include "collectd.h"
 # define CAN_USE_SYSCTL 0
 #endif
 
-#define CPU_SUBMIT_USER 0
-#define CPU_SUBMIT_SYSTEM 1
-#define CPU_SUBMIT_WAIT 2
-#define CPU_SUBMIT_NICE 3
-#define CPU_SUBMIT_SWAP 4
-#define CPU_SUBMIT_INTERRUPT 5
-#define CPU_SUBMIT_SOFTIRQ 6
-#define CPU_SUBMIT_STEAL 7
-#define CPU_SUBMIT_IDLE 8
-#define CPU_SUBMIT_ACTIVE 9
-#define CPU_SUBMIT_MAX 10
+#define CPU_STATE_USER 0
+#define CPU_STATE_SYSTEM 1
+#define CPU_STATE_WAIT 2
+#define CPU_STATE_NICE 3
+#define CPU_STATE_SWAP 4
+#define CPU_STATE_INTERRUPT 5
+#define CPU_STATE_SOFTIRQ 6
+#define CPU_STATE_STEAL 7
+#define CPU_STATE_IDLE 8
+#define CPU_STATE_ACTIVE 9 /* sum of (!idle) */
+#define CPU_STATE_MAX 10 /* #states */
 
 #if HAVE_STATGRAB_H
 # include <statgrab.h>
@@ -132,8 +134,8 @@ static mach_msg_type_number_t cpu_list_len;
 
 #if PROCESSOR_TEMPERATURE
 static int cpu_temp_retry_counter = 0;
-static int cpu_temp_retry_step   = 1;
-static int cpu_temp_retry_max    = 1;
+static int cpu_temp_retry_step    = 1;
+static int cpu_temp_retry_max     = 1;
 #endif /* PROCESSOR_TEMPERATURE */
 /* #endif PROCESSOR_CPU_LOAD_INFO */
 
@@ -170,82 +172,53 @@ static int numcpu;
 static int pnumcpu;
 #endif /* HAVE_PERFSTAT */
 
-static value_to_rate_state_t *values = NULL;
-static gauge_t agg_values[CPU_SUBMIT_MAX] = {
-       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+#define RATE_ADD(sum, val) do { \
+       if (isnan (sum))        \
+       (sum) = (val);          \
+       else if (!isnan (val))  \
+       (sum) += (val);         \
+} while (0)
 
+struct cpu_state_s
+{
+       value_to_rate_state_t conv;
+       gauge_t rate;
+       _Bool has_value;
 };
-static int cpu_cells = 0;
-static int cpu_count = 0;
+typedef struct cpu_state_s cpu_state_t;
+
+static cpu_state_t *cpu_states = NULL;
+static size_t cpu_states_num = 0; /* #cpu_states allocated */
 
+/* Highest CPU number in the current iteration. Used by the dispatch logic to
+ * determine how many CPUs there were. Reset to 0 by cpu_reset(). */
+static size_t global_cpu_num = 0;
 
 static _Bool report_by_cpu = 1;
+static _Bool report_by_state = 1;
 static _Bool report_percent = 0;
-static _Bool report_active = 0;
 
 static const char *config_keys[] =
 {
        "ReportByCpu",
-       "ReportActive",
+       "ReportByState",
        "ValuesPercentage"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-
-static int cpu_config (const char *key, const char *value)
+static int cpu_config (char const *key, char const *value) /* {{{ */
 {
-       if (strcasecmp (key, "ReportByCpu") == 0) {
+       if (strcasecmp (key, "ReportByCpu") == 0)
                report_by_cpu = IS_TRUE (value) ? 1 : 0;
-       }
-       if (strcasecmp (key, "ValuesPercentage") == 0) {
+       else if (strcasecmp (key, "ValuesPercentage") == 0)
                report_percent = IS_TRUE (value) ? 1 : 0;
-       }
-       if (strcasecmp (key, "ReportActive") == 0)
-               report_active = IS_TRUE (value) ? 1 : 0;
-       return (-1);
-}
-
-static int cpu_states_grow (void)
-{
-  void *tmp;
-  int size;
-  int i;
-
-  size = cpu_count * CPU_SUBMIT_MAX; /* always alloc for all states */
-
-  if (size <= 0)
-         return 0;
-
-  if (cpu_cells >= size)
-         return 0;
-
-  if (values == NULL) {
-         values = malloc(size * sizeof(*values));
-         if (values == NULL)
-                 return -1;
-         for (i = 0; i < size; i++)
-                 memset(&values[i], 0, sizeof(*values));
-         cpu_cells = size;
-         return 0;
-  }
-
-  tmp = realloc(values, size * sizeof(*values));
-
-  if (tmp == NULL) {
-         ERROR ("cpu plugin: could not reserve enough space to hold states");
-         values = NULL;
-         return -1;
-  }
-
-  values = tmp;
-
-  for (i = cpu_cells ; i < size; i++)
-         memset(&values[i], 0, sizeof(*values));
-
-  cpu_cells = size;
-  return 0;
-} /* cpu_states_grow */
+       else if (strcasecmp (key, "ReportByState") == 0)
+               report_by_state = IS_TRUE (value) ? 1 : 0;
+       else
+               return (-1);
 
+       return (0);
+} /* }}} int cpu_config */
 
 static int init (void)
 {
@@ -357,11 +330,11 @@ static void submit_value (int cpu_num, int cpu_state, const char *type, value_t
        sstrncpy (vl.plugin, "cpu", sizeof (vl.plugin));
        sstrncpy (vl.type, type, sizeof (vl.type));
        sstrncpy (vl.type_instance, cpu_state_names[cpu_state],
-                 sizeof (vl.type_instance));
+                       sizeof (vl.type_instance));
 
        if (cpu_num >= 0) {
                ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
-                          "%i", cpu_num);
+                               "%i", cpu_num);
        }
        plugin_dispatch_values (&vl);
 }
@@ -370,6 +343,12 @@ static void submit_percent(int cpu_num, int cpu_state, gauge_t percent)
 {
        value_t value;
 
+       /* This function is called for all known CPU states, but each read
+        * method will only report a subset. The remaining states are left as
+        * NAN and we ignore them here. */
+       if (isnan (percent))
+               return;
+
        value.gauge = percent;
        submit_value (cpu_num, cpu_state, "percent", value);
 }
@@ -382,144 +361,220 @@ static void submit_derive(int cpu_num, int cpu_state, derive_t derive)
        submit_value (cpu_num, cpu_state, "cpu", value);
 }
 
-static void submit_flush (void)
+/* Takes the zero-index number of a CPU and makes sure that the module-global
+ * cpu_states buffer is large enough. Returne ENOMEM on erorr. */
+static int cpu_states_alloc (size_t cpu_num) /* {{{ */
 {
-       int i = 0;
-       int cpu_submit_max = CPU_SUBMIT_MAX;
+       cpu_state_t *tmp;
+       size_t sz;
 
-       if (report_by_cpu) {
-               cpu_count = 0;
-               return;
-       }
+       sz = (((size_t) cpu_num) + 1) * CPU_STATE_MAX;
+       assert (sz > 0);
 
-       if (report_active)
-               cpu_submit_max = CPU_SUBMIT_MAX;
-       else
-               cpu_submit_max = CPU_SUBMIT_ACTIVE;
-       for (i = 0; i < cpu_submit_max; i++) {
-               if (agg_values[i] == -1)
-                       continue;
+       /* We already have enough space. */
+       if (cpu_states_num >= sz)
+               return 0;
 
-               if (report_percent)
-                       submit_percent(-1, i, agg_values[i] / cpu_count);
-               else
-                       submit_derive(-1, i, agg_values[i]);
-               agg_values[i] = -1;
+       tmp = realloc (cpu_states, sz * sizeof (*cpu_states));
+       if (tmp == NULL)
+       {
+               ERROR ("cpu plugin: realloc failed.");
+               return (ENOMEM);
        }
-       cpu_count = 0;
-}
+       cpu_states = tmp;
+       tmp = cpu_states + cpu_states_num;
 
-static void submit (int cpu_num, derive_t *derives)
+       memset (tmp, 0, (sz - cpu_states_num) * sizeof (*cpu_states));
+       cpu_states_num = sz;
+       return 0;
+} /* }}} cpu_states_alloc */
+
+static cpu_state_t *get_cpu_state (size_t cpu_num, size_t state) /* {{{ */
 {
+       size_t index = ((cpu_num * CPU_STATE_MAX) + state);
 
-       int i = 0;
-       int cpu_submit_max = CPU_SUBMIT_MAX;
+       if (index >= cpu_states_num)
+               return (NULL);
 
-       if (report_active)
-               cpu_submit_max = CPU_SUBMIT_MAX;
-       else
-               cpu_submit_max = CPU_SUBMIT_ACTIVE;
+       return (&cpu_states[index]);
+} /* }}} cpu_state_t *get_cpu_state */
 
-       if (!report_percent && report_by_cpu) {
-               derive_t cpu_active = 0;
-               for (i = 0; i < CPU_SUBMIT_ACTIVE; i++)
+/* Populates the per-CPU CPU_STATE_ACTIVE rate and the global rate_by_state
+ * array. */
+static void aggregate (gauge_t *sum_by_state) /* {{{ */
+{
+       size_t cpu_num;
+       size_t state;
+
+       for (state = 0; state < CPU_STATE_MAX; state++)
+               sum_by_state[state] = NAN;
+
+       for (cpu_num = 0; cpu_num < global_cpu_num; cpu_num++)
+       {
+               cpu_state_t *this_cpu_states = get_cpu_state (cpu_num, 0);
+
+               this_cpu_states[CPU_STATE_ACTIVE].rate = NAN;
+
+               for (state = 0; state < CPU_STATE_ACTIVE; state++)
                {
-                       if (derives[i] == -1)
+                       if (!this_cpu_states[state].has_value)
                                continue;
 
-                       if (i != CPU_SUBMIT_IDLE)
-                               cpu_active += derives[i];
-
-                       submit_derive(cpu_num, i, derives[i]);
+                       RATE_ADD (sum_by_state[state], this_cpu_states[state].rate);
+                       if (state != CPU_STATE_IDLE)
+                               RATE_ADD (this_cpu_states[CPU_STATE_ACTIVE].rate, this_cpu_states[state].rate);
                }
-               if (report_active)
-                       submit_derive(cpu_num, CPU_SUBMIT_ACTIVE, cpu_active);
+
+               RATE_ADD (sum_by_state[CPU_STATE_ACTIVE], this_cpu_states[CPU_STATE_ACTIVE].rate);
        }
-       else {
-               cdtime_t cdt;
-               gauge_t value;
-               gauge_t cpu_total = 0;
-               gauge_t cpu_active = 0;
-               gauge_t local_rates[CPU_SUBMIT_MAX];
-
-               cpu_count++;
-               if (cpu_states_grow())
-                       return;
-
-               memset(local_rates, 0, sizeof(local_rates));
-
-               cdt = cdtime();
-               for (i = 0; i < CPU_SUBMIT_ACTIVE; i++) {
-                       if (report_percent) {
-                               value_t rate;
-                               int index;
-
-                               if (derives[i] == -1)
-                                       continue;
-
-                               index = (cpu_num * CPU_SUBMIT_MAX) + i;
-                               if (value_to_rate(&rate, derives[i], &values[index],
-                                                       DS_TYPE_DERIVE, cdt) != 0) {
-                                       local_rates[i] = -1;
-                                       continue;
-                               }
-
-                               local_rates[i] = rate.gauge;
-                               cpu_total += rate.gauge;
-                               if (i != CPU_SUBMIT_IDLE)
-                                       cpu_active += rate.gauge;
-                       }
-                       else {
-                               cpu_total += derives[i];
-                               if (i != CPU_SUBMIT_IDLE)
-                                       cpu_active += derives[i];
-                       }
-               }
-               if (cpu_total == 0.0)
-                       return;
+} /* }}} void aggregate */
+
+/* Commits (dispatches) the values for one CPU or the global aggregation.
+ * cpu_num is the index of the CPU to be committed or -1 in case of the global
+ * aggregation. rates is a pointer to CPU_STATE_MAX gauge_t values holding the
+ * current rate; each rate may be NAN. Calculates the percentage of each state
+ * and dispatches the metric. */
+static void cpu_commit_one (int cpu_num, /* {{{ */
+               gauge_t rates[static CPU_STATE_MAX])
+{
+       size_t state;
+       gauge_t sum;
+
+       sum = rates[CPU_STATE_ACTIVE];
+       RATE_ADD (sum, rates[CPU_STATE_IDLE]);
+
+       if (!report_by_state)
+       {
+               gauge_t percent = 100.0 * rates[CPU_STATE_ACTIVE] / sum;
+               submit_percent (cpu_num, CPU_STATE_ACTIVE, percent);
+               return;
+       }
+
+       for (state = 0; state < CPU_STATE_ACTIVE; state++)
+       {
+               gauge_t percent = 100.0 * rates[state] / sum;
+               submit_percent (cpu_num, state, percent);
+       }
+} /* }}} void cpu_commit_one */
 
-               if (report_active)
-                       local_rates[CPU_SUBMIT_ACTIVE] = cpu_active;
+/* Resets the internal aggregation. This is called by the read callback after
+ * each iteration / after each call to cpu_commit(). */
+static void cpu_reset (void) /* {{{ */
+{
+       size_t i;
+
+       for (i = 0; i < cpu_states_num; i++)
+               cpu_states[i].has_value = 0;
 
-               for (i = 0; i < cpu_submit_max; i++) {
-                       if (local_rates[i] == -1)
+       global_cpu_num = 0;
+} /* }}} void cpu_reset */
+
+/* Legacy behavior: Dispatches the raw derive values without any aggregation. */
+static void cpu_commit_without_aggregation (void) /* {{{ */
+{
+       size_t cpu_num;
+
+       for (cpu_num = 0; cpu_num < global_cpu_num; cpu_num++)
+       {
+               int state;
+
+               for (state = 0; state < CPU_STATE_ACTIVE; state++)
+               {
+                       cpu_state_t *s = get_cpu_state (cpu_num, state);
+
+                       if (!s->has_value)
                                continue;
 
-                       if (report_percent)
-                               value = (local_rates[i] / cpu_total) * 100;
-                       else
-                               value = derives[i];
-                       if (report_by_cpu) {
-                               if (report_percent) {
-                                       submit_percent (cpu_num, i, value);
-                               } else {
-                                       submit_derive(cpu_num, i, value);
-                               }
-                       }
-                       else {
-                               if (agg_values[i] == -1)
-                                       agg_values[i] = value;
-                               else
-                                       agg_values[i] += value;
-                       }
+                       submit_derive ((int) cpu_num, (int) state, s->conv.last_value.derive);
                }
        }
-}
+} /* }}} void cpu_commit_without_aggregation */
+
+/* Aggregates the internal state and dispatches the metrics. */
+static void cpu_commit (void) /* {{{ */
+{
+       gauge_t global_rates[CPU_STATE_MAX] = {
+               NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN
+       };
+       size_t cpu_num;
+
+       if (report_by_cpu && report_by_state && !report_percent)
+       {
+               cpu_commit_without_aggregation ();
+               return;
+       }
+
+       aggregate (global_rates);
+
+       if (!report_by_cpu)
+       {
+               cpu_commit_one (-1, global_rates);
+               return;
+       }
+
+       for (cpu_num = 0; cpu_num < global_cpu_num; cpu_num++)
+       {
+               cpu_state_t *this_cpu_states = get_cpu_state (cpu_num, 0);
+               gauge_t local_rates[CPU_STATE_MAX] = {
+                       NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN
+               };
+               size_t state;
+
+               for (state = 0; state < CPU_STATE_ACTIVE; state++)
+                       if (this_cpu_states[state].has_value)
+                               local_rates[state] = this_cpu_states[state].rate;
+
+               cpu_commit_one ((int) cpu_num, local_rates);
+       }
+} /* }}} void cpu_commit */
+
+/* Adds a derive value to the internal state. This should be used by each read
+ * function for each state. At the end of the iteration, the read function
+ * should call cpu_commit(). */
+static int cpu_stage (size_t cpu_num, size_t state, derive_t value, cdtime_t now) /* {{{ */
+{
+       int status;
+       cpu_state_t *s;
+       value_t v;
+
+       if (state >= CPU_STATE_ACTIVE)
+               return (EINVAL);
+
+       status = cpu_states_alloc (cpu_num);
+       if (status != 0)
+               return (status);
+
+       if (global_cpu_num <= cpu_num)
+               global_cpu_num = cpu_num + 1;
+
+       s = get_cpu_state (cpu_num, state);
+
+       v.gauge = NAN;
+       status = value_to_rate (&v, value, &s->conv, DS_TYPE_DERIVE, now);
+       if (status != 0)
+               return (status);
+
+       s->rate = v.gauge;
+       s->has_value = 1;
+       return (0);
+} /* }}} int cpu_stage */
 
 static int cpu_read (void)
 {
-#if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE
+       cdtime_t now = cdtime ();
+
+#if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE /* {{{ */
        int cpu;
 
        kern_return_t status;
 
 #if PROCESSOR_CPU_LOAD_INFO
        processor_cpu_load_info_data_t cpu_info;
-       mach_msg_type_number_t         cpu_info_len;
+       mach_msg_type_number_t         cpu_info_len;
 #endif
 #if PROCESSOR_TEMPERATURE
-       processor_info_data_t          cpu_temp;
-       mach_msg_type_number_t         cpu_temp_len;
+       processor_info_data_t          cpu_temp;
+       mach_msg_type_number_t         cpu_temp_len;
 #endif
 
        host_t cpu_host;
@@ -527,10 +582,6 @@ static int cpu_read (void)
        for (cpu = 0; cpu < cpu_list_len; cpu++)
        {
 #if PROCESSOR_CPU_LOAD_INFO
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-               memset(derives, -1, sizeof(derives));
                cpu_host = 0;
                cpu_info_len = PROCESSOR_BASIC_INFO_COUNT;
 
@@ -548,13 +599,12 @@ static int cpu_read (void)
                        continue;
                }
 
-               derives[CPU_SUBMIT_USER] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_USER];
-               derives[CPU_SUBMIT_NICE] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_NICE];
-               derives[CPU_SUBMIT_SYSTEM] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM];
-               derives[CPU_SUBMIT_IDLE] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_IDLE];
-               submit (cpu, derives);
-
+               cpu_stage (cpu, CPU_STATE_USER,   (derive_t) cpu_info.cpu_ticks[CPU_STATE_USER],   now);
+               cpu_stage (cpu, CPU_STATE_NICE,   (derive_t) cpu_info.cpu_ticks[CPU_STATE_NICE],   now);
+               cpu_stage (cpu, CPU_STATE_SYSTEM, (derive_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM], now);
+               cpu_stage (cpu, CPU_STATE_IDLE,   (derive_t) cpu_info.cpu_ticks[CPU_STATE_IDLE],   now);
 #endif /* PROCESSOR_CPU_LOAD_INFO */
+
 #if PROCESSOR_TEMPERATURE
                /*
                 * Not all Apple computers do have this ability. To minimize
@@ -598,10 +648,9 @@ static int cpu_read (void)
                cpu_temp_retry_step    = 1;
 #endif /* PROCESSOR_TEMPERATURE */
        }
-       submit_flush ();
-/* #endif PROCESSOR_CPU_LOAD_INFO */
+/* }}} #endif PROCESSOR_CPU_LOAD_INFO */
 
-#elif defined(KERNEL_LINUX)
+#elif defined(KERNEL_LINUX) /* {{{ */
        int cpu;
        FILE *fh;
        char buf[1024];
@@ -619,10 +668,6 @@ static int cpu_read (void)
 
        while (fgets (buf, 1024, fh) != NULL)
        {
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-
                if (strncmp (buf, "cpu", 3))
                        continue;
                if ((buf[3] < '0') || (buf[3] > '9'))
@@ -633,28 +678,26 @@ static int cpu_read (void)
                        continue;
 
                cpu = atoi (fields[0] + 3);
-               derives[CPU_SUBMIT_USER] = atoll(fields[1]);
-               derives[CPU_SUBMIT_NICE] = atoll(fields[2]);
-               derives[CPU_SUBMIT_SYSTEM] = atoll(fields[3]);
-               derives[CPU_SUBMIT_IDLE] = atoll(fields[4]);
+
+               cpu_stage (cpu, CPU_STATE_USER,   (derive_t) atoll(fields[1]), now);
+               cpu_stage (cpu, CPU_STATE_NICE,   (derive_t) atoll(fields[2]), now);
+               cpu_stage (cpu, CPU_STATE_SYSTEM, (derive_t) atoll(fields[3]), now);
+               cpu_stage (cpu, CPU_STATE_IDLE,   (derive_t) atoll(fields[4]), now);
 
                if (numfields >= 8)
                {
-                       derives[CPU_SUBMIT_WAIT] = atoll(fields[5]);
-                       derives[CPU_SUBMIT_INTERRUPT] = atoll(fields[6]);
-                       derives[CPU_SUBMIT_SOFTIRQ] = atoll(fields[6]);
+                       cpu_stage (cpu, CPU_STATE_WAIT,      (derive_t) atoll(fields[5]), now);
+                       cpu_stage (cpu, CPU_STATE_INTERRUPT, (derive_t) atoll(fields[6]), now);
+                       cpu_stage (cpu, CPU_STATE_SOFTIRQ,   (derive_t) atoll(fields[7]), now);
 
                        if (numfields >= 9)
-                               derives[CPU_SUBMIT_STEAL] = atoll(fields[8]);
+                               cpu_stage (cpu, CPU_STATE_STEAL, (derive_t) atoll(fields[8]), now);
                }
-               submit(cpu, derives);
        }
-       submit_flush();
-
        fclose (fh);
-/* #endif defined(KERNEL_LINUX) */
+/* }}} #endif defined(KERNEL_LINUX) */
 
-#elif defined(HAVE_LIBKSTAT)
+#elif defined(HAVE_LIBKSTAT) /* {{{ */
        int cpu;
        static cpu_stat_t cs;
 
@@ -663,24 +706,17 @@ static int cpu_read (void)
 
        for (cpu = 0; cpu < numcpu; cpu++)
        {
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-
                if (kstat_read (kc, ksp[cpu], &cs) == -1)
                        continue; /* error message? */
 
-               memset(derives, -1, sizeof(derives));
-               derives[CPU_SUBMIT_IDLE] = cs.cpu_sysinfo.cpu[CPU_IDLE];
-               derives[CPU_SUBMIT_USER] = cs.cpu_sysinfo.cpu[CPU_USER];
-               derives[CPU_SUBMIT_SYSTEM] = cs.cpu_sysinfo.cpu[CPU_KERNEL];
-               derives[CPU_SUBMIT_WAIT] = cs.cpu_sysinfo.cpu[CPU_WAIT];
-               submit (ksp[cpu]->ks_instance, derives);
+               cpu_stage (ksp[cpu]->ks_instance, CPU_STATE_IDLE,   (derive_t) cs.cpu_sysinfo.cpu[CPU_IDLE],   now);
+               cpu_stage (ksp[cpu]->ks_instance, CPU_STATE_USER,   (derive_t) cs.cpu_sysinfo.cpu[CPU_USER],   now);
+               cpu_stage (ksp[cpu]->ks_instance, CPU_STATE_SYSTEM, (derive_t) cs.cpu_sysinfo.cpu[CPU_KERNEL], now);
+               cpu_stage (ksp[cpu]->ks_instance, CPU_STATE_WAIT,   (derive_t) cs.cpu_sysinfo.cpu[CPU_WAIT],   now);
        }
-       submit_flush ();
-/* #endif defined(HAVE_LIBKSTAT) */
+/* }}} #endif defined(HAVE_LIBKSTAT) */
 
-#elif CAN_USE_SYSCTL
+#elif CAN_USE_SYSCTL /* {{{ */
        uint64_t cpuinfo[numcpu][CPUSTATES];
        size_t cpuinfo_size;
        int status;
@@ -736,20 +772,15 @@ static int cpu_read (void)
        }
 
        for (i = 0; i < numcpu; i++) {
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-
-               derives[CPU_SUBMIT_USER] = cpuinfo[i][CP_USER];
-               derives[CPU_SUBMIT_NICE] = cpuinfo[i][CP_NICE];
-               derives[CPU_SUBMIT_SYSTEM] = cpuinfo[i][CP_SYS];
-               derives[CPU_SUBMIT_IDLE] = cpuinfo[i][CP_IDLE];
-               derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[i][CP_INTR];
-               submit(i, derives);
+               cpu_stage (i, CPU_STATE_USER,      (derive_t) cpuinfo[i][CP_USER], now);
+               cpu_stage (i, CPU_STATE_NICE,      (derive_t) cpuinfo[i][CP_NICE], now);
+               cpu_stage (i, CPU_STATE_SYSTEM,    (derive_t) cpuinfo[i][CP_SYS], now);
+               cpu_stage (i, CPU_STATE_IDLE,      (derive_t) cpuinfo[i][CP_IDLE], now);
+               cpu_stage (i, CPU_STATE_INTERRUPT, (derive_t) cpuinfo[i][CP_INTR], now);
        }
-       submit_flush();
-/* #endif CAN_USE_SYSCTL */
-#elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES)
+/* }}} #endif CAN_USE_SYSCTL */
+
+#elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES) /* {{{ */
        long cpuinfo[maxcpu][CPUSTATES];
        size_t cpuinfo_size;
        int i;
@@ -766,26 +797,17 @@ static int cpu_read (void)
        }
 
        for (i = 0; i < numcpu; i++) {
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-
-               derives[CPU_SUBMIT_USER] = cpuinfo[i][CP_USER];
-               derives[CPU_SUBMIT_NICE] = cpuinfo[i][CP_NICE];
-               derives[CPU_SUBMIT_SYSTEM] = cpuinfo[i][CP_SYS];
-               derives[CPU_SUBMIT_IDLE] = cpuinfo[i][CP_IDLE];
-               derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[i][CP_INTR];
-               submit(i, derives);
+               cpu_stage (i, CPU_STATE_USER,      (derive_t) cpuinfo[i][CP_USER], now);
+               cpu_stage (i, CPU_STATE_NICE,      (derive_t) cpuinfo[i][CP_NICE], now);
+               cpu_stage (i, CPU_STATE_SYSTEM,    (derive_t) cpuinfo[i][CP_SYS], now);
+               cpu_stage (i, CPU_STATE_IDLE,      (derive_t) cpuinfo[i][CP_IDLE], now);
+               cpu_stage (i, CPU_STATE_INTERRUPT, (derive_t) cpuinfo[i][CP_INTR], now);
        }
-       submit_flush();
+/* }}} #endif HAVE_SYSCTL_KERN_CP_TIMES */
 
-/* #endif HAVE_SYSCTL_KERN_CP_TIMES */
-#elif defined(HAVE_SYSCTLBYNAME)
+#elif defined(HAVE_SYSCTLBYNAME) /* {{{ */
        long cpuinfo[CPUSTATES];
        size_t cpuinfo_size;
-       derive_t derives[CPU_SUBMIT_MAX] = {
-               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-       };
 
        cpuinfo_size = sizeof (cpuinfo);
 
@@ -797,21 +819,15 @@ static int cpu_read (void)
                return (-1);
        }
 
-       derives[CPU_SUBMIT_USER] = cpuinfo[CP_USER];
-       derives[CPU_SUBMIT_SYSTEM] = cpuinfo[CP_SYS];
-       derives[CPU_SUBMIT_NICE] = cpuinfo[CP_NICE];
-       derives[CPU_SUBMIT_IDLE] = cpuinfo[CP_IDLE];
-       derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[CP_INTR];
-       submit(0, derives);
-       submit_flush();
-
-/* #endif HAVE_SYSCTLBYNAME */
+       cpu_stage (0, CPU_STATE_USER,      (derive_t) cpuinfo[CP_USER], now);
+       cpu_stage (0, CPU_STATE_NICE,      (derive_t) cpuinfo[CP_NICE], now);
+       cpu_stage (0, CPU_STATE_SYSTEM,    (derive_t) cpuinfo[CP_SYS], now);
+       cpu_stage (0, CPU_STATE_IDLE,      (derive_t) cpuinfo[CP_IDLE], now);
+       cpu_stage (0, CPU_STATE_INTERRUPT, (derive_t) cpuinfo[CP_INTR], now);
+/* }}} #endif HAVE_SYSCTLBYNAME */
 
-#elif defined(HAVE_LIBSTATGRAB)
+#elif defined(HAVE_LIBSTATGRAB) /* {{{ */
        sg_cpu_stats *cs;
-       derive_t derives[CPU_SUBMIT_MAX] = {
-               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-       };
        cs = sg_get_cpu_stats ();
 
        if (cs == NULL)
@@ -820,17 +836,15 @@ static int cpu_read (void)
                return (-1);
        }
 
-       derives[CPU_SUBMIT_IDLE] = (derive_t) cs->idle;
-       derives[CPU_SUBMIT_NICE] = (derive_t) cs->nice;
-       derives[CPU_SUBMIT_SWAP] = (derive_t) cs->swap;
-       derives[CPU_SUBMIT_SYSTEM] = (derive_t) cs->kernel;
-       derives[CPU_SUBMIT_USER] = (derive_t) cs->user;
-       derives[CPU_SUBMIT_WAIT] = (derive_t) cs->iowait;
-       submit(0, derives);
-       submit_flush();
-/* #endif HAVE_LIBSTATGRAB */
+       cpu_state (0, CPU_STATE_IDLE,   (derive_t) cs->idle);
+       cpu_state (0, CPU_STATE_NICE,   (derive_t) cs->nice);
+       cpu_state (0, CPU_STATE_SWAP,   (derive_t) cs->swap);
+       cpu_state (0, CPU_STATE_SYSTEM, (derive_t) cs->kernel);
+       cpu_state (0, CPU_STATE_USER,   (derive_t) cs->user);
+       cpu_state (0, CPU_STATE_WAIT,   (derive_t) cs->iowait);
+/* }}} #endif HAVE_LIBSTATGRAB */
 
-#elif defined(HAVE_PERFSTAT)
+#elif defined(HAVE_PERFSTAT) /* {{{ */
        perfstat_id_t id;
        int i, cpus;
 
@@ -862,18 +876,15 @@ static int cpu_read (void)
 
        for (i = 0; i < cpus; i++)
        {
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-               derives[CPU_SUBMIT_IDLE] = perfcpu[i].idle;
-               derives[CPU_SUBMIT_SYSTEM] = perfcpu[i].sys;
-               derives[CPU_SUBMIT_USER] = perfcpu[i].user;
-               derives[CPU_SUBMIT_WAIT] = perfcpu[i].wait;
-               submit(i, derives);
+               cpu_stage (i, CPU_STATE_IDLE,   (derive_t) perfcpu[i].idle, now);
+               cpu_stage (i, CPU_STATE_SYSTEM, (derive_t) perfcpu[i].sys,  now);
+               cpu_stage (i, CPU_STATE_USER,   (derive_t) perfcpu[i].user, now);
+               cpu_stage (i, CPU_STATE_WAIT,   (derive_t) perfcpu[i].wait, now);
        }
-       submit_flush();
-#endif /* HAVE_PERFSTAT */
+#endif /* }}} HAVE_PERFSTAT */
 
+       cpu_commit ();
+       cpu_reset ();
        return (0);
 }
 
@@ -883,3 +894,5 @@ void module_register (void)
        plugin_register_config ("cpu", cpu_config, config_keys, config_keys_num);
        plugin_register_read ("cpu", cpu_read);
 } /* void module_register */
+
+/* vim: set sw=8 sts=8 noet fdm=marker : */
index e9a409d..b6e535e 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -25,7 +25,6 @@
 #include "plugin.h"
 #include "common.h"
 #include "utils_cache.h"
-#include "utils_parse_option.h"
 
 /*
  * Private variables
index f605c07..0e5d2cf 100644 (file)
@@ -373,6 +373,11 @@ static int cc_page_init_curl (web_page_t *wp) /* {{{ */
 
   if (wp->user != NULL)
   {
+#ifdef HAVE_CURLOPT_USERNAME
+    curl_easy_setopt (wp->curl, CURLOPT_USERNAME, wp->user);
+    curl_easy_setopt (wp->curl, CURLOPT_PASSWORD,
+        (wp->pass == NULL) ? "" : wp->pass);
+#else
     size_t credentials_size;
 
     credentials_size = strlen (wp->user) + 2;
@@ -389,13 +394,10 @@ static int cc_page_init_curl (web_page_t *wp) /* {{{ */
     ssnprintf (wp->credentials, credentials_size, "%s:%s",
         wp->user, (wp->pass == NULL) ? "" : wp->pass);
     curl_easy_setopt (wp->curl, CURLOPT_USERPWD, wp->credentials);
-    
+#endif
+
     if (wp->digest)
-    {
       curl_easy_setopt (wp->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
-      curl_easy_setopt (wp->curl, CURLOPT_USERNAME, wp->user);
-      curl_easy_setopt (wp->curl, CURLOPT_PASSWORD, wp->pass);
-    }
   }
 
   curl_easy_setopt (wp->curl, CURLOPT_SSL_VERIFYPEER, (long) wp->verify_peer);
@@ -690,6 +692,7 @@ static int cc_read_page (web_page_t *wp) /* {{{ */
     }
 
     cc_submit (wp, wm, mv);
+    match_value_reset (mv);
   } /* for (wm = wp->matches; wm != NULL; wm = wm->next) */
 
   return (0);
index a84cba0..8a084fe 100644 (file)
@@ -595,6 +595,11 @@ static int cj_init_curl (cj_t *db) /* {{{ */
 
   if (db->user != NULL)
   {
+#ifdef HAVE_CURLOPT_USERNAME
+    curl_easy_setopt (db->curl, CURLOPT_USERNAME, db->user);
+    curl_easy_setopt (db->curl, CURLOPT_PASSWORD,
+        (db->pass == NULL) ? "" : db->pass);
+#else
     size_t credentials_size;
 
     credentials_size = strlen (db->user) + 2;
@@ -611,13 +616,10 @@ static int cj_init_curl (cj_t *db) /* {{{ */
     ssnprintf (db->credentials, credentials_size, "%s:%s",
                db->user, (db->pass == NULL) ? "" : db->pass);
     curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
-    
+#endif
+
     if (db->digest)
-    {
       curl_easy_setopt (db->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
-      curl_easy_setopt (db->curl, CURLOPT_USERNAME, db->user);
-      curl_easy_setopt (db->curl, CURLOPT_PASSWORD, db->pass);
-    }
   }
 
   curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, (long) db->verify_peer);
index a743753..c0ab6fd 100644 (file)
@@ -845,6 +845,11 @@ static int cx_init_curl (cx_t *db) /* {{{ */
 
   if (db->user != NULL)
   {
+#ifdef HAVE_CURLOPT_USERNAME
+    curl_easy_setopt (db->curl, CURLOPT_USERNAME, db->user);
+    curl_easy_setopt (db->curl, CURLOPT_PASSWORD,
+        (db->pass == NULL) ? "" : db->pass);
+#else
     size_t credentials_size;
 
     credentials_size = strlen (db->user) + 2;
@@ -861,13 +866,10 @@ static int cx_init_curl (cx_t *db) /* {{{ */
     ssnprintf (db->credentials, credentials_size, "%s:%s",
                db->user, (db->pass == NULL) ? "" : db->pass);
     curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
-    
+#endif
+
     if (db->digest)
-    {
       curl_easy_setopt (db->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
-      curl_easy_setopt (db->curl, CURLOPT_USERNAME, db->user);
-      curl_easy_setopt (db->curl, CURLOPT_PASSWORD, db->pass);
-    }
   }
 
   curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer ? 1L : 0L);
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
new file mode 100644 (file)
index 0000000..fc81554
--- /dev/null
@@ -0,0 +1,85 @@
+if COMPILER_IS_GCC
+AM_CFLAGS = -Wall -Werror
+endif
+
+AM_CPPFLAGS = -I$(top_srcdir)/src
+AM_CPPFLAGS += -DPREFIX='"${prefix}"'
+AM_CPPFLAGS += -DCONFIGFILE='"${sysconfdir}/${PACKAGE_NAME}.conf"'
+AM_CPPFLAGS += -DLOCALSTATEDIR='"${localstatedir}"'
+AM_CPPFLAGS += -DPKGLOCALSTATEDIR='"${localstatedir}/lib/${PACKAGE_NAME}"'
+if BUILD_FEATURE_DAEMON
+AM_CPPFLAGS += -DPIDFILE='"${localstatedir}/run/${PACKAGE_NAME}.pid"'
+endif
+AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"'
+AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"'
+
+AUTOMAKE_OPTIONS = subdir-objects
+
+sbin_PROGRAMS = collectd
+
+collectd_SOURCES = collectd.c collectd.h \
+                  common.c common.h \
+                  configfile.c configfile.h \
+                  filter_chain.c filter_chain.h \
+                  meta_data.c meta_data.h \
+                  plugin.c plugin.h \
+                  utils_avltree.c utils_avltree.h \
+                  utils_cache.c utils_cache.h \
+                  utils_complain.c utils_complain.h \
+                  utils_heap.c utils_heap.h \
+                  utils_llist.c utils_llist.h \
+                  utils_random.c utils_random.h \
+                  utils_tail_match.c utils_tail_match.h \
+                  utils_match.c utils_match.h \
+                  utils_subst.c utils_subst.h \
+                  utils_tail.c utils_tail.h \
+                  utils_time.c utils_time.h \
+                  types_list.c types_list.h \
+                  utils_threshold.c utils_threshold.h
+
+
+collectd_CPPFLAGS =  $(AM_CPPFLAGS) $(LTDLINCL)
+collectd_CFLAGS = $(AM_CFLAGS)
+collectd_LDFLAGS = -export-dynamic
+collectd_LDADD = -lm
+collectd_DEPENDENCIES =
+
+# Link to these libraries..
+if BUILD_WITH_LIBRT
+collectd_LDADD += -lrt
+endif
+if BUILD_WITH_LIBPOSIX4
+collectd_LDADD += -lposix4
+endif
+if BUILD_WITH_LIBSOCKET
+collectd_LDADD += -lsocket
+endif
+if BUILD_WITH_LIBRESOLV
+collectd_LDADD += -lresolv
+endif
+if BUILD_WITH_LIBPTHREAD
+collectd_LDADD += -lpthread
+endif
+if BUILD_WITH_LIBKSTAT
+collectd_LDADD += -lkstat
+endif
+if BUILD_WITH_LIBDEVINFO
+collectd_LDADD += -ldevinfo
+endif
+if BUILD_AIX
+collectd_LDFLAGS += -Wl,-bexpall,-brtllib
+endif
+
+# The daemon needs to call sg_init, so we need to link it against libstatgrab,
+# too. -octo
+if BUILD_WITH_LIBSTATGRAB
+collectd_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
+collectd_LDADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
+endif
+
+if BUILD_WITH_OWN_LIBOCONFIG
+collectd_LDADD += $(LIBLTDL) $(top_builddir)/src/liboconfig/liboconfig.la
+collectd_DEPENDENCIES += $(top_builddir)/src/liboconfig/liboconfig.la
+else
+collectd_LDADD += -loconfig
+endif
diff --git a/src/daemon/collectd.c b/src/daemon/collectd.c
new file mode 100644 (file)
index 0000000..9cba913
--- /dev/null
@@ -0,0 +1,712 @@
+/**
+ * collectd - src/collectd.c
+ * Copyright (C) 2005-2007  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>
+ *   Alvaro Barcellos <alvaro.barcellos at gmail.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+
+#include "plugin.h"
+#include "configfile.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#include <pthread.h>
+
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#if HAVE_STATGRAB_H
+# include <statgrab.h>
+#endif
+
+#ifndef COLLECTD_LOCALE
+# define COLLECTD_LOCALE "C"
+#endif
+
+/*
+ * Global variables
+ */
+char hostname_g[DATA_MAX_NAME_LEN];
+cdtime_t interval_g;
+int  pidfile_from_cli = 0;
+int  timeout_g;
+#if HAVE_LIBKSTAT
+kstat_ctl_t *kc;
+#endif /* HAVE_LIBKSTAT */
+
+static int loop = 0;
+
+static void *do_flush (void __attribute__((unused)) *arg)
+{
+       INFO ("Flushing all data.");
+       plugin_flush (/* plugin = */ NULL,
+                       /* timeout = */ 0,
+                       /* ident = */ NULL);
+       INFO ("Finished flushing all data.");
+       pthread_exit (NULL);
+       return NULL;
+}
+
+static void sig_int_handler (int __attribute__((unused)) signal)
+{
+       loop++;
+}
+
+static void sig_term_handler (int __attribute__((unused)) signal)
+{
+       loop++;
+}
+
+static void sig_usr1_handler (int __attribute__((unused)) signal)
+{
+       pthread_t      thread;
+       pthread_attr_t attr;
+
+       /* flushing the data might take a while,
+        * so it should be done asynchronously */
+       pthread_attr_init (&attr);
+       pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+       pthread_create (&thread, &attr, do_flush, NULL);
+       pthread_attr_destroy (&attr);
+}
+
+static int init_hostname (void)
+{
+       const char *str;
+
+       struct addrinfo  ai_hints;
+       struct addrinfo *ai_list;
+       struct addrinfo *ai_ptr;
+       int status;
+
+       str = global_option_get ("Hostname");
+       if (str != NULL)
+       {
+               sstrncpy (hostname_g, str, sizeof (hostname_g));
+               return (0);
+       }
+
+       if (gethostname (hostname_g, sizeof (hostname_g)) != 0)
+       {
+               fprintf (stderr, "`gethostname' failed and no "
+                               "hostname was configured.\n");
+               return (-1);
+       }
+
+       str = global_option_get ("FQDNLookup");
+       if (IS_FALSE (str))
+               return (0);
+
+       memset (&ai_hints, '\0', sizeof (ai_hints));
+       ai_hints.ai_flags = AI_CANONNAME;
+
+       status = getaddrinfo (hostname_g, NULL, &ai_hints, &ai_list);
+       if (status != 0)
+       {
+               ERROR ("Looking up \"%s\" failed. You have set the "
+                               "\"FQDNLookup\" option, but I cannot resolve "
+                               "my hostname to a fully qualified domain "
+                               "name. Please fix the network "
+                               "configuration.", hostname_g);
+               return (-1);
+       }
+
+       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+       {
+               if (ai_ptr->ai_canonname == NULL)
+                       continue;
+
+               sstrncpy (hostname_g, ai_ptr->ai_canonname, sizeof (hostname_g));
+               break;
+       }
+
+       freeaddrinfo (ai_list);
+       return (0);
+} /* int init_hostname */
+
+static int init_global_variables (void)
+{
+       char const *str;
+
+       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");
+       if (str == NULL)
+               str = "2";
+       timeout_g = atoi (str);
+       if (timeout_g <= 1)
+       {
+               fprintf (stderr, "Cannot set the timeout to a correct value.\n"
+                               "Please check your settings.\n");
+               return (-1);
+       }
+       DEBUG ("timeout_g = %i;", timeout_g);
+
+       if (init_hostname () != 0)
+               return (-1);
+       DEBUG ("hostname_g = %s;", hostname_g);
+
+       return (0);
+} /* int init_global_variables */
+
+static int change_basedir (const char *orig_dir)
+{
+       char *dir;
+       size_t dirlen;
+       int status;
+
+       dir = strdup (orig_dir);
+       if (dir == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("strdup failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       dirlen = strlen (dir);
+       while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
+               dir[--dirlen] = '\0';
+
+       if (dirlen <= 0)
+               return (-1);
+
+       status = chdir (dir);
+       if (status == 0)
+       {
+               free (dir);
+               return (0);
+       }
+       else if (errno != ENOENT)
+       {
+               char errbuf[1024];
+               ERROR ("change_basedir: chdir (%s): %s", dir,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               free (dir);
+               return (-1);
+       }
+
+       status = mkdir (dir, S_IRWXU | S_IRWXG | S_IRWXO);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("change_basedir: mkdir (%s): %s", dir,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               free (dir);
+               return (-1);
+       }
+
+       status = chdir (dir);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("change_basedir: chdir (%s): %s", dir,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               free (dir);
+               return (-1);
+       }
+
+       free (dir);
+       return (0);
+} /* static int change_basedir (char *dir) */
+
+#if HAVE_LIBKSTAT
+static void update_kstat (void)
+{
+       if (kc == NULL)
+       {
+               if ((kc = kstat_open ()) == NULL)
+                       ERROR ("Unable to open kstat control structure");
+       }
+       else
+       {
+               kid_t kid;
+               kid = kstat_chain_update (kc);
+               if (kid > 0)
+               {
+                       INFO ("kstat chain has been updated");
+                       plugin_init_all ();
+               }
+               else if (kid < 0)
+                       ERROR ("kstat chain update failed");
+               /* else: everything works as expected */
+       }
+
+       return;
+} /* static void update_kstat (void) */
+#endif /* HAVE_LIBKSTAT */
+
+/* TODO
+ * Remove all settings but `-f' and `-C'
+ */
+static void exit_usage (int status)
+{
+       printf ("Usage: "PACKAGE" [OPTIONS]\n\n"
+
+                       "Available options:\n"
+                       "  General:\n"
+                       "    -C <file>       Configuration file.\n"
+                       "                    Default: "CONFIGFILE"\n"
+                       "    -t              Test config and exit.\n"
+                       "    -T              Test plugin read and exit.\n"
+                       "    -P <file>       PID-file.\n"
+                       "                    Default: "PIDFILE"\n"
+#if COLLECT_DAEMON
+                       "    -f              Don't fork to the background.\n"
+#endif
+                       "    -h              Display help (this message)\n"
+                       "\nBuiltin defaults:\n"
+                       "  Config file       "CONFIGFILE"\n"
+                       "  PID file          "PIDFILE"\n"
+                       "  Plugin directory  "PLUGINDIR"\n"
+                       "  Data directory    "PKGLOCALSTATEDIR"\n"
+                       "\n"PACKAGE" "VERSION", http://collectd.org/\n"
+                       "by Florian octo Forster <octo@collectd.org>\n"
+                       "for contributions see `AUTHORS'\n");
+       exit (status);
+} /* static void exit_usage (int status) */
+
+static int do_init (void)
+{
+#if HAVE_SETLOCALE
+       if (setlocale (LC_NUMERIC, COLLECTD_LOCALE) == NULL)
+               WARNING ("setlocale (\"%s\") failed.", COLLECTD_LOCALE);
+#endif
+
+#if HAVE_LIBKSTAT
+       kc = NULL;
+       update_kstat ();
+#endif
+
+#if HAVE_LIBSTATGRAB
+       if (sg_init (
+# if HAVE_LIBSTATGRAB_INIT_ARG
+                   0
+# endif
+                   ))
+       {
+               ERROR ("sg_init: %s", sg_str_error (sg_get_error ()));
+               return (-1);
+       }
+
+       if (sg_drop_privileges ())
+       {
+               ERROR ("sg_drop_privileges: %s", sg_str_error (sg_get_error ()));
+               return (-1);
+       }
+#endif
+
+       plugin_init_all ();
+
+       return (0);
+} /* int do_init () */
+
+
+static int do_loop (void)
+{
+       cdtime_t interval = cf_get_default_interval ();
+       cdtime_t wait_until;
+
+       wait_until = cdtime () + interval;
+
+       while (loop == 0)
+       {
+               struct timespec ts_wait = { 0, 0 };
+               cdtime_t now;
+
+#if HAVE_LIBKSTAT
+               update_kstat ();
+#endif
+
+               /* Issue all plugins */
+               plugin_read_all ();
+
+               now = cdtime ();
+               if (now >= wait_until)
+               {
+                       WARNING ("Not sleeping because the next interval is "
+                                       "%.3f seconds in the past!",
+                                       CDTIME_T_TO_DOUBLE (now - wait_until));
+                       wait_until = now + interval;
+                       continue;
+               }
+
+               CDTIME_T_TO_TIMESPEC (wait_until - now, &ts_wait);
+               wait_until = wait_until + interval;
+
+               while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) != 0))
+               {
+                       if (errno != EINTR)
+                       {
+                               char errbuf[1024];
+                               ERROR ("nanosleep failed: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
+                               return (-1);
+                       }
+               }
+       } /* while (loop == 0) */
+
+       return (0);
+} /* int do_loop */
+
+static int do_shutdown (void)
+{
+       plugin_shutdown_all ();
+       return (0);
+} /* int do_shutdown */
+
+#if COLLECT_DAEMON
+static int pidfile_create (void)
+{
+       FILE *fh;
+       const char *file = global_option_get ("PIDFile");
+
+       if ((fh = fopen (file, "w")) == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("fopen (%s): %s", file,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (1);
+       }
+
+       fprintf (fh, "%i\n", (int) getpid ());
+       fclose(fh);
+
+       return (0);
+} /* static int pidfile_create (const char *file) */
+
+static int pidfile_remove (void)
+{
+       const char *file = global_option_get ("PIDFile");
+
+       DEBUG ("unlink (%s)", (file != NULL) ? file : "<null>");
+       return (unlink (file));
+} /* static int pidfile_remove (const char *file) */
+#endif /* COLLECT_DAEMON */
+
+int notify_upstart (void)
+{
+    const char  *upstart_job = getenv("UPSTART_JOB");
+
+    if (upstart_job == NULL)
+        return 0;
+
+    if (strcmp(upstart_job, "collectd") != 0)
+        return 0;
+
+    WARNING ("supervised by upstart, will stop to signal readyness");
+    raise(SIGSTOP);
+    unsetenv("UPSTART_JOB");
+
+    return 1;
+}
+
+int notify_systemd (void)
+{
+    int                  fd = -1;
+    const char          *notifysocket = getenv("NOTIFY_SOCKET");
+    struct sockaddr_un   su;
+    struct iovec         iov;
+    struct msghdr        hdr;
+
+    if (notifysocket == NULL)
+        return 0;
+
+    if ((strchr("@/", notifysocket[0])) == NULL ||
+        strlen(notifysocket) < 2)
+        return 0;
+
+    WARNING ("supervised by systemd, will signal readyness");
+    if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
+        WARNING ("cannot contact systemd socket %s", notifysocket);
+        return 0;
+    }
+
+    bzero(&su, sizeof(su));
+    su.sun_family = AF_UNIX;
+    sstrncpy (su.sun_path, notifysocket, sizeof(su.sun_path));
+
+    if (notifysocket[0] == '@')
+        su.sun_path[0] = 0;
+
+    bzero(&iov, sizeof(iov));
+    iov.iov_base = "READY=1";
+    iov.iov_len = strlen("READY=1");
+
+    bzero(&hdr, sizeof(hdr));
+    hdr.msg_name = &su;
+    hdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) +
+        strlen(notifysocket);
+    hdr.msg_iov = &iov;
+    hdr.msg_iovlen = 1;
+
+    unsetenv("NOTIFY_SOCKET");
+    if (sendmsg(fd, &hdr, MSG_NOSIGNAL) < 0) {
+        WARNING ("cannot send notification to systemd");
+        close(fd);
+        return 0;
+    }
+    close(fd);
+    return 1;
+}
+
+int main (int argc, char **argv)
+{
+       struct sigaction sig_int_action;
+       struct sigaction sig_term_action;
+       struct sigaction sig_usr1_action;
+       struct sigaction sig_pipe_action;
+       char *configfile = CONFIGFILE;
+       int test_config  = 0;
+       int test_readall = 0;
+       const char *basedir;
+#if COLLECT_DAEMON
+       struct sigaction sig_chld_action;
+       pid_t pid;
+       int daemonize    = 1;
+#endif
+       int exit_status = 0;
+
+       /* read options */
+       while (1)
+       {
+               int c;
+
+               c = getopt (argc, argv, "htTC:"
+#if COLLECT_DAEMON
+                               "fP:"
+#endif
+               );
+
+               if (c == -1)
+                       break;
+
+               switch (c)
+               {
+                       case 'C':
+                               configfile = optarg;
+                               break;
+                       case 't':
+                               test_config = 1;
+                               break;
+                       case 'T':
+                               test_readall = 1;
+                               global_option_set ("ReadThreads", "-1");
+#if COLLECT_DAEMON
+                               daemonize = 0;
+#endif /* COLLECT_DAEMON */
+                               break;
+#if COLLECT_DAEMON
+                       case 'P':
+                               global_option_set ("PIDFile", optarg);
+                               pidfile_from_cli = 1;
+                               break;
+                       case 'f':
+                               daemonize = 0;
+                               break;
+#endif /* COLLECT_DAEMON */
+                       case 'h':
+                               exit_usage (0);
+                               break;
+                       default:
+                               exit_usage (1);
+               } /* switch (c) */
+       } /* while (1) */
+
+       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
+        * general).
+        * Also, this will automatically load modules.
+        */
+       if (cf_read (configfile))
+       {
+               fprintf (stderr, "Error: Reading the config file failed!\n"
+                               "Read the syslog for details.\n");
+               return (1);
+       }
+
+       /*
+        * Change directory. We do this _after_ reading the config and loading
+        * modules to relative paths work as expected.
+        */
+       if ((basedir = global_option_get ("BaseDir")) == NULL)
+       {
+               fprintf (stderr, "Don't have a basedir to use. This should not happen. Ever.");
+               return (1);
+       }
+       else if (change_basedir (basedir))
+       {
+               fprintf (stderr, "Error: Unable to change to directory `%s'.\n", basedir);
+               return (1);
+       }
+
+       /*
+        * Set global variables or, if that failes, exit. We cannot run with
+        * them being uninitialized. If nothing is configured, then defaults
+        * are being used. So this means that the user has actually done
+        * something wrong.
+        */
+       if (init_global_variables () != 0)
+               return (1);
+
+       if (test_config)
+               return (0);
+
+#if COLLECT_DAEMON
+       /*
+        * fork off child
+        */
+       memset (&sig_chld_action, '\0', sizeof (sig_chld_action));
+       sig_chld_action.sa_handler = SIG_IGN;
+       sigaction (SIGCHLD, &sig_chld_action, NULL);
+
+    /*
+     * Only daemonize if we're not being supervised
+     * by upstart or systemd.
+     */
+       if (daemonize && notify_upstart() == 0 && notify_systemd() == 0)
+       {
+               if ((pid = fork ()) == -1)
+               {
+                       /* error */
+                       char errbuf[1024];
+                       fprintf (stderr, "fork: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
+                       return (1);
+               }
+               else if (pid != 0)
+               {
+                       /* parent */
+                       /* printf ("Running (PID %i)\n", pid); */
+                       return (0);
+               }
+
+               /* Detach from session */
+               setsid ();
+
+               /* Write pidfile */
+               if (pidfile_create ())
+                       exit (2);
+
+               /* close standard descriptors */
+               close (2);
+               close (1);
+               close (0);
+
+               if (open ("/dev/null", O_RDWR) != 0)
+               {
+                       ERROR ("Error: Could not connect `STDIN' to `/dev/null'");
+                       return (1);
+               }
+               if (dup (0) != 1)
+               {
+                       ERROR ("Error: Could not connect `STDOUT' to `/dev/null'");
+                       return (1);
+               }
+               if (dup (0) != 2)
+               {
+                       ERROR ("Error: Could not connect `STDERR' to `/dev/null'");
+                       return (1);
+               }
+       } /* if (daemonize) */
+#endif /* COLLECT_DAEMON */
+
+       memset (&sig_pipe_action, '\0', sizeof (sig_pipe_action));
+       sig_pipe_action.sa_handler = SIG_IGN;
+       sigaction (SIGPIPE, &sig_pipe_action, NULL);
+
+       /*
+        * install signal handlers
+        */
+       memset (&sig_int_action, '\0', sizeof (sig_int_action));
+       sig_int_action.sa_handler = sig_int_handler;
+       if (0 != sigaction (SIGINT, &sig_int_action, NULL)) {
+               char errbuf[1024];
+               ERROR ("Error: Failed to install a signal handler for signal INT: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (1);
+       }
+
+       memset (&sig_term_action, '\0', sizeof (sig_term_action));
+       sig_term_action.sa_handler = sig_term_handler;
+       if (0 != sigaction (SIGTERM, &sig_term_action, NULL)) {
+               char errbuf[1024];
+               ERROR ("Error: Failed to install a signal handler for signal TERM: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (1);
+       }
+
+       memset (&sig_usr1_action, '\0', sizeof (sig_usr1_action));
+       sig_usr1_action.sa_handler = sig_usr1_handler;
+       if (0 != sigaction (SIGUSR1, &sig_usr1_action, NULL)) {
+               char errbuf[1024];
+               ERROR ("Error: Failed to install a signal handler for signal USR1: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (1);
+       }
+
+       /*
+        * run the actual loops
+        */
+       do_init ();
+
+       if (test_readall)
+       {
+               if (plugin_read_all_once () != 0)
+                       exit_status = 1;
+       }
+       else
+       {
+               INFO ("Initialization complete, entering read-loop.");
+               do_loop ();
+       }
+
+       /* close syslog */
+       INFO ("Exiting normally.");
+
+       do_shutdown ();
+
+#if COLLECT_DAEMON
+       if (daemonize)
+               pidfile_remove ();
+#endif /* COLLECT_DAEMON */
+
+       return (exit_status);
+} /* int main */
diff --git a/src/daemon/collectd.h b/src/daemon/collectd.h
new file mode 100644 (file)
index 0000000..6886c12
--- /dev/null
@@ -0,0 +1,315 @@
+/**
+ * collectd - src/collectd.h
+ * Copyright (C) 2005,2006  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 COLLECTD_H
+#define COLLECTD_H
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+#if HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#if HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#if HAVE_ERRNO_H
+# include <errno.h>
+#endif
+#if HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#if HAVE_ASSERT_H
+# include <assert.h>
+#else
+# define assert(...) /* nop */
+#endif
+
+#if !defined(HAVE__BOOL) || !HAVE__BOOL
+typedef int _Bool;
+# undef HAVE__BOOL
+# define HAVE__BOOL 1
+#endif
+
+#if NAN_STATIC_DEFAULT
+# include <math.h>
+/* #endif NAN_STATIC_DEFAULT*/
+#elif NAN_STATIC_ISOC
+# ifndef __USE_ISOC99
+#  define DISABLE_ISOC99 1
+#  define __USE_ISOC99 1
+# endif /* !defined(__USE_ISOC99) */
+# include <math.h>
+# if DISABLE_ISOC99
+#  undef DISABLE_ISOC99
+#  undef __USE_ISOC99
+# endif /* DISABLE_ISOC99 */
+/* #endif NAN_STATIC_ISOC */
+#elif NAN_ZERO_ZERO
+# include <math.h>
+# ifdef NAN
+#  undef NAN
+# endif
+# define NAN (0.0 / 0.0)
+# ifndef isnan
+#  define isnan(f) ((f) != (f))
+# endif /* !defined(isnan) */
+# ifndef isfinite
+#  define isfinite(f) (((f) - (f)) == 0.0)
+# endif
+# ifndef isinf
+#  define isinf(f) (!isfinite(f) && !isnan(f))
+# endif
+#endif /* NAN_ZERO_ZERO */
+
+/* Try really, really hard to determine endianess. Under NexentaStor 1.0.2 this
+ * information is in <sys/isa_defs.h>, possibly some other Solaris versions do
+ * this too.. */
+#if HAVE_ENDIAN_H
+# include <endian.h>
+#elif HAVE_SYS_ISA_DEFS_H
+# include <sys/isa_defs.h>
+#endif
+
+#ifndef BYTE_ORDER
+# if defined(_BYTE_ORDER)
+#  define BYTE_ORDER _BYTE_ORDER
+# elif defined(__BYTE_ORDER)
+#  define BYTE_ORDER __BYTE_ORDER
+# elif defined(__DARWIN_BYTE_ORDER)
+#  define BYTE_ORDER __DARWIN_BYTE_ORDER
+# endif
+#endif
+#ifndef BIG_ENDIAN
+# if defined(_BIG_ENDIAN)
+#  define BIG_ENDIAN _BIG_ENDIAN
+# elif defined(__BIG_ENDIAN)
+#  define BIG_ENDIAN __BIG_ENDIAN
+# elif defined(__DARWIN_BIG_ENDIAN)
+#  define BIG_ENDIAN __DARWIN_BIG_ENDIAN
+# endif
+#endif
+#ifndef LITTLE_ENDIAN
+# if defined(_LITTLE_ENDIAN)
+#  define LITTLE_ENDIAN _LITTLE_ENDIAN
+# elif defined(__LITTLE_ENDIAN)
+#  define LITTLE_ENDIAN __LITTLE_ENDIAN
+# elif defined(__DARWIN_LITTLE_ENDIAN)
+#  define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN
+# endif
+#endif
+#ifndef BYTE_ORDER
+# if defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN)
+#  undef BIG_ENDIAN
+#  define BIG_ENDIAN 4321
+#  define LITTLE_ENDIAN 1234
+#  define BYTE_ORDER BIG_ENDIAN
+# elif !defined(BIG_ENDIAN) && defined(LITTLE_ENDIAN)
+#  undef LITTLE_ENDIAN
+#  define BIG_ENDIAN 4321
+#  define LITTLE_ENDIAN 1234
+#  define BYTE_ORDER LITTLE_ENDIAN
+# endif
+#endif
+#if !defined(BYTE_ORDER) || !defined(BIG_ENDIAN)
+# error "Cannot determine byte order"
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+#  include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+#  include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+#  include <ndir.h>
+# endif
+#endif
+
+#if HAVE_STDARG_H
+# include <stdarg.h>
+#endif
+#if HAVE_CTYPE_H
+# include <ctype.h>
+#endif
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+#if HAVE_KSTAT_H
+# include <kstat.h>
+#endif
+
+#ifndef PACKAGE_NAME
+#define PACKAGE_NAME "collectd"
+#endif
+
+#ifndef PREFIX
+#define PREFIX "/opt/" PACKAGE_NAME
+#endif
+
+#ifndef SYSCONFDIR
+#define SYSCONFDIR PREFIX "/etc"
+#endif
+
+#ifndef CONFIGFILE
+#define CONFIGFILE SYSCONFDIR"/collectd.conf"
+#endif
+
+#ifndef LOCALSTATEDIR
+#define LOCALSTATEDIR PREFIX "/var"
+#endif
+
+#ifndef PKGLOCALSTATEDIR
+#define PKGLOCALSTATEDIR PREFIX "/var/lib/" PACKAGE_NAME
+#endif
+
+#ifndef PIDFILE
+#define PIDFILE PREFIX "/var/run/" PACKAGE_NAME ".pid"
+#endif
+
+#ifndef PLUGINDIR
+#define PLUGINDIR PREFIX "/lib/" PACKAGE_NAME
+#endif
+
+#ifndef PKGDATADIR
+#define PKGDATADIR PREFIX "/share/" PACKAGE_NAME
+#endif
+
+#ifndef COLLECTD_GRP_NAME
+# define COLLECTD_GRP_NAME "collectd"
+#endif
+
+#ifndef COLLECTD_DEFAULT_INTERVAL
+# define COLLECTD_DEFAULT_INTERVAL 10.0
+#endif
+
+ #ifndef COLLECTD_USERAGENT
+ # define COLLECTD_USERAGENT PACKAGE_NAME"/"PACKAGE_VERSION
+ #endif
+
+/* Only enable __attribute__() for compilers known to support it. */
+#if defined(__clang__)
+# define clang_attr(x) __attribute__(x)
+# define gcc_attr(x) /**/
+#elif __GNUC__
+# define clang_attr(x) /**/
+# define gcc_attr(x) __attribute__(x)
+#else
+# define clang_attr(x) /**/
+# define gcc_attr(x) /**/
+# define __attribute__(x) /**/
+#endif
+
+#if defined(COLLECT_DEBUG) && COLLECT_DEBUG && defined(__GNUC__) && __GNUC__
+# undef strcpy
+# undef strcat
+# undef strtok
+# pragma GCC poison strcpy strcat strtok
+#endif
+
+/* 
+ * Special hack for the perl plugin: Because the later included perl.h defines
+ * a macro which is never used, but contains `sprintf', we cannot poison that
+ * identifies just yet. The parl plugin will do that itself once perl.h is
+ * included.
+ */
+#ifndef DONT_POISON_SPRINTF_YET
+# if defined(COLLECT_DEBUG) && COLLECT_DEBUG && defined(__GNUC__) && __GNUC__
+#  undef sprintf
+#  pragma GCC poison sprintf
+# endif
+#endif
+
+/* Type for time as used by "utils_time.h" */
+typedef uint64_t cdtime_t;
+
+extern char     hostname_g[];
+extern cdtime_t interval_g;
+extern int      pidfile_from_cli;
+extern int      timeout_g;
+
+#endif /* COLLECTD_H */
diff --git a/src/daemon/common.c b/src/daemon/common.c
new file mode 100644 (file)
index 0000000..535dfad
--- /dev/null
@@ -0,0 +1,1650 @@
+/**
+ * collectd - src/common.c
+ * Copyright (C) 2005-2014  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>
+ *   Niki W. Waibel <niki.waibel@gmx.net>
+ *   Sebastian Harl <sh at tokkee.org>
+ *   Michał Mirosław <mirq-linux at rere.qmqm.pl>
+**/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_cache.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+#ifdef HAVE_MATH_H
+# include <math.h>
+#endif
+
+/* for getaddrinfo */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+/* for ntohl and htonl */
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_LIBKSTAT
+extern kstat_ctl_t *kc;
+#endif
+
+#if !HAVE_GETPWNAM_R
+static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+#if !HAVE_STRERROR_R
+static pthread_mutex_t strerror_r_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+char *sstrncpy (char *dest, const char *src, size_t n)
+{
+       strncpy (dest, src, n);
+       dest[n - 1] = '\0';
+
+       return (dest);
+} /* char *sstrncpy */
+
+int ssnprintf (char *dest, size_t n, const char *format, ...)
+{
+       int ret = 0;
+       va_list ap;
+
+       va_start (ap, format);
+       ret = vsnprintf (dest, n, format, ap);
+       dest[n - 1] = '\0';
+       va_end (ap);
+
+       return (ret);
+} /* int ssnprintf */
+
+char *ssnprintf_alloc (char const *format, ...) /* {{{ */
+{
+       char static_buffer[1024] = "";
+       char *alloc_buffer;
+       size_t alloc_buffer_size;
+       int status;
+       va_list ap;
+
+       /* Try printing into the static buffer. In many cases it will be
+        * sufficiently large and we can simply return a strdup() of this
+        * buffer. */
+       va_start (ap, format);
+       status = vsnprintf (static_buffer, sizeof (static_buffer), format, ap);
+       va_end (ap);
+       if (status < 0)
+               return (NULL);
+
+       /* "status" does not include the null byte. */
+       alloc_buffer_size = (size_t) (status + 1);
+       if (alloc_buffer_size <= sizeof (static_buffer))
+               return (strdup (static_buffer));
+
+       /* Allocate a buffer large enough to hold the string. */
+       alloc_buffer = malloc (alloc_buffer_size);
+       if (alloc_buffer == NULL)
+               return (NULL);
+       memset (alloc_buffer, 0, alloc_buffer_size);
+
+       /* Print again into this new buffer. */
+       va_start (ap, format);
+       status = vsnprintf (alloc_buffer, alloc_buffer_size, format, ap);
+       va_end (ap);
+       if (status < 0)
+       {
+               sfree (alloc_buffer);
+               return (NULL);
+       }
+
+       return (alloc_buffer);
+} /* }}} char *ssnprintf_alloc */
+
+char *sstrdup (const char *s)
+{
+       char *r;
+       size_t sz;
+
+       if (s == NULL)
+               return (NULL);
+
+       /* Do not use `strdup' here, because it's not specified in POSIX. It's
+        * ``only'' an XSI extension. */
+       sz = strlen (s) + 1;
+       r = (char *) malloc (sizeof (char) * sz);
+       if (r == NULL)
+       {
+               ERROR ("sstrdup: Out of memory.");
+               exit (3);
+       }
+       memcpy (r, s, sizeof (char) * sz);
+
+       return (r);
+} /* char *sstrdup */
+
+/* Even though Posix requires "strerror_r" to return an "int",
+ * some systems (e.g. the GNU libc) return a "char *" _and_
+ * ignore the second argument ... -tokkee */
+char *sstrerror (int errnum, char *buf, size_t buflen)
+{
+       buf[0] = '\0';
+
+#if !HAVE_STRERROR_R
+       {
+               char *temp;
+
+               pthread_mutex_lock (&strerror_r_lock);
+
+               temp = strerror (errnum);
+               sstrncpy (buf, temp, buflen);
+
+               pthread_mutex_unlock (&strerror_r_lock);
+       }
+/* #endif !HAVE_STRERROR_R */
+
+#elif STRERROR_R_CHAR_P
+       {
+               char *temp;
+               temp = strerror_r (errnum, buf, buflen);
+               if (buf[0] == '\0')
+               {
+                       if ((temp != NULL) && (temp != buf) && (temp[0] != '\0'))
+                               sstrncpy (buf, temp, buflen);
+                       else
+                               sstrncpy (buf, "strerror_r did not return "
+                                               "an error message", buflen);
+               }
+       }
+/* #endif STRERROR_R_CHAR_P */
+
+#else
+       if (strerror_r (errnum, buf, buflen) != 0)
+       {
+               ssnprintf (buf, buflen, "Error #%i; "
+                               "Additionally, strerror_r failed.",
+                               errnum);
+       }
+#endif /* STRERROR_R_CHAR_P */
+
+       return (buf);
+} /* char *sstrerror */
+
+void *smalloc (size_t size)
+{
+       void *r;
+
+       if ((r = malloc (size)) == NULL)
+       {
+               ERROR ("Not enough memory.");
+               exit (3);
+       }
+
+       return (r);
+} /* void *smalloc */
+
+#if 0
+void sfree (void **ptr)
+{
+       if (ptr == NULL)
+               return;
+
+       if (*ptr != NULL)
+               free (*ptr);
+
+       *ptr = NULL;
+}
+#endif
+
+ssize_t sread (int fd, void *buf, size_t count)
+{
+       char    *ptr;
+       size_t   nleft;
+       ssize_t  status;
+
+       ptr   = (char *) buf;
+       nleft = count;
+
+       while (nleft > 0)
+       {
+               status = read (fd, (void *) ptr, nleft);
+
+               if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
+                       continue;
+
+               if (status < 0)
+                       return (status);
+
+               if (status == 0)
+               {
+                       DEBUG ("Received EOF from fd %i. "
+                                       "Closing fd and returning error.",
+                                       fd);
+                       close (fd);
+                       return (-1);
+               }
+
+               assert ((0 > status) || (nleft >= (size_t)status));
+
+               nleft = nleft - status;
+               ptr   = ptr   + status;
+       }
+
+       return (0);
+}
+
+
+ssize_t swrite (int fd, const void *buf, size_t count)
+{
+       const char *ptr;
+       size_t      nleft;
+       ssize_t     status;
+
+       ptr   = (const char *) buf;
+       nleft = count;
+
+       while (nleft > 0)
+       {
+               status = write (fd, (const void *) ptr, nleft);
+
+               if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
+                       continue;
+
+               if (status < 0)
+                       return (status);
+
+               nleft = nleft - status;
+               ptr   = ptr   + status;
+       }
+
+       return (0);
+}
+
+int strsplit (char *string, char **fields, size_t size)
+{
+       size_t i;
+       char *ptr;
+       char *saveptr;
+
+       i = 0;
+       ptr = string;
+       saveptr = NULL;
+       while ((fields[i] = strtok_r (ptr, " \t\r\n", &saveptr)) != NULL)
+       {
+               ptr = NULL;
+               i++;
+
+               if (i >= size)
+                       break;
+       }
+
+       return ((int) i);
+}
+
+int strjoin (char *dst, size_t dst_len,
+               char **fields, size_t fields_num,
+               const char *sep)
+{
+       size_t field_len;
+       size_t sep_len;
+       int i;
+
+       memset (dst, '\0', dst_len);
+
+       if (fields_num <= 0)
+               return (-1);
+
+       sep_len = 0;
+       if (sep != NULL)
+               sep_len = strlen (sep);
+
+       for (i = 0; i < (int)fields_num; i++)
+       {
+               if ((i > 0) && (sep_len > 0))
+               {
+                       if (dst_len <= sep_len)
+                               return (-1);
+
+                       strncat (dst, sep, dst_len);
+                       dst_len -= sep_len;
+               }
+
+               field_len = strlen (fields[i]);
+
+               if (dst_len <= field_len)
+                       return (-1);
+
+               strncat (dst, fields[i], dst_len);
+               dst_len -= field_len;
+       }
+
+       return (strlen (dst));
+}
+
+int strsubstitute (char *str, char c_from, char c_to)
+{
+       int ret;
+
+       if (str == NULL)
+               return (-1);
+
+       ret = 0;
+       while (*str != '\0')
+       {
+               if (*str == c_from)
+               {
+                       *str = c_to;
+                       ret++;
+               }
+               str++;
+       }
+
+       return (ret);
+} /* int strsubstitute */
+
+int escape_string (char *buffer, size_t buffer_size)
+{
+  char *temp;
+  size_t i;
+  size_t j;
+
+  /* Check if we need to escape at all first */
+  temp = strpbrk (buffer, " \t\"\\");
+  if (temp == NULL)
+    return (0);
+
+  if (buffer_size < 3)
+    return (EINVAL);
+
+  temp = (char *) malloc (buffer_size);
+  if (temp == NULL)
+    return (ENOMEM);
+  memset (temp, 0, buffer_size);
+
+  temp[0] = '"';
+  j = 1;
+
+  for (i = 0; i < buffer_size; i++)
+  {
+    if (buffer[i] == 0)
+    {
+      break;
+    }
+    else if ((buffer[i] == '"') || (buffer[i] == '\\'))
+    {
+      if (j > (buffer_size - 4))
+        break;
+      temp[j] = '\\';
+      temp[j + 1] = buffer[i];
+      j += 2;
+    }
+    else
+    {
+      if (j > (buffer_size - 3))
+        break;
+      temp[j] = buffer[i];
+      j++;
+    }
+  }
+
+  assert ((j + 1) < buffer_size);
+  temp[j] = '"';
+  temp[j + 1] = 0;
+
+  sstrncpy (buffer, temp, buffer_size);
+  sfree (temp);
+  return (0);
+} /* int escape_string */
+
+int strunescape (char *buf, size_t buf_len)
+{
+       size_t i;
+
+       for (i = 0; (i < buf_len) && (buf[i] != '\0'); ++i)
+       {
+               if (buf[i] != '\\')
+                       continue;
+
+               if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) {
+                       ERROR ("string unescape: backslash found at end of string.");
+                       /* Ensure null-byte at the end of the buffer. */
+                       buf[i] = 0;
+                       return (-1);
+               }
+
+               switch (buf[i + 1]) {
+                       case 't':
+                               buf[i] = '\t';
+                               break;
+                       case 'n':
+                               buf[i] = '\n';
+                               break;
+                       case 'r':
+                               buf[i] = '\r';
+                               break;
+                       default:
+                               buf[i] = buf[i + 1];
+                               break;
+               }
+
+               /* Move everything after the position one position to the left.
+                * Add a null-byte as last character in the buffer. */
+               memmove (buf + i + 1, buf + i + 2, buf_len - i - 2);
+               buf[buf_len - 1] = 0;
+       }
+       return (0);
+} /* int strunescape */
+
+size_t strstripnewline (char *buffer)
+{
+       size_t buffer_len = strlen (buffer);
+
+       while (buffer_len > 0)
+       {
+               if ((buffer[buffer_len - 1] != '\n')
+                               && (buffer[buffer_len - 1] != '\r'))
+                       break;
+               buffer[buffer_len] = 0;
+               buffer_len--;
+       }
+
+       return (buffer_len);
+} /* size_t strstripnewline */
+
+int escape_slashes (char *buffer, size_t buffer_size)
+{
+       int i;
+       size_t buffer_len;
+
+       buffer_len = strlen (buffer);
+
+       if (buffer_len <= 1)
+       {
+               if (strcmp ("/", buffer) == 0)
+               {
+                       if (buffer_size < 5)
+                               return (-1);
+                       sstrncpy (buffer, "root", buffer_size);
+               }
+               return (0);
+       }
+
+       /* Move one to the left */
+       if (buffer[0] == '/')
+       {
+               memmove (buffer, buffer + 1, buffer_len);
+               buffer_len--;
+       }
+
+       for (i = 0; i < buffer_len - 1; i++)
+       {
+               if (buffer[i] == '/')
+                       buffer[i] = '_';
+       }
+
+       return (0);
+} /* int escape_slashes */
+
+void replace_special (char *buffer, size_t buffer_size)
+{
+       size_t i;
+
+       for (i = 0; i < buffer_size; i++)
+       {
+               if (buffer[i] == 0)
+                       return;
+               if ((!isalnum ((int) buffer[i])) && (buffer[i] != '-'))
+                       buffer[i] = '_';
+       }
+} /* void replace_special */
+
+int timeval_cmp (struct timeval tv0, struct timeval tv1, struct timeval *delta)
+{
+       struct timeval *larger;
+       struct timeval *smaller;
+
+       int status;
+
+       NORMALIZE_TIMEVAL (tv0);
+       NORMALIZE_TIMEVAL (tv1);
+
+       if ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec == tv1.tv_usec))
+       {
+               if (delta != NULL) {
+                       delta->tv_sec  = 0;
+                       delta->tv_usec = 0;
+               }
+               return (0);
+       }
+
+       if ((tv0.tv_sec < tv1.tv_sec)
+                       || ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec < tv1.tv_usec)))
+       {
+               larger  = &tv1;
+               smaller = &tv0;
+               status  = -1;
+       }
+       else
+       {
+               larger  = &tv0;
+               smaller = &tv1;
+               status  = 1;
+       }
+
+       if (delta != NULL) {
+               delta->tv_sec = larger->tv_sec - smaller->tv_sec;
+
+               if (smaller->tv_usec <= larger->tv_usec)
+                       delta->tv_usec = larger->tv_usec - smaller->tv_usec;
+               else
+               {
+                       --delta->tv_sec;
+                       delta->tv_usec = 1000000 + larger->tv_usec - smaller->tv_usec;
+               }
+       }
+
+       assert ((delta == NULL)
+                       || ((0 <= delta->tv_usec) && (delta->tv_usec < 1000000)));
+
+       return (status);
+} /* int timeval_cmp */
+
+int check_create_dir (const char *file_orig)
+{
+       struct stat statbuf;
+
+       char  file_copy[512];
+       char  dir[512];
+       int   dir_len = 512;
+       char *fields[16];
+       int   fields_num;
+       char *ptr;
+       char *saveptr;
+       int   last_is_file = 1;
+       int   path_is_absolute = 0;
+       size_t len;
+       int   i;
+
+       /*
+        * Sanity checks first
+        */
+       if (file_orig == NULL)
+               return (-1);
+
+       if ((len = strlen (file_orig)) < 1)
+               return (-1);
+       else if (len >= sizeof (file_copy))
+               return (-1);
+
+       /*
+        * If `file_orig' ends in a slash the last component is a directory,
+        * otherwise it's a file. Act accordingly..
+        */
+       if (file_orig[len - 1] == '/')
+               last_is_file = 0;
+       if (file_orig[0] == '/')
+               path_is_absolute = 1;
+
+       /*
+        * Create a copy for `strtok_r' to destroy
+        */
+       sstrncpy (file_copy, file_orig, sizeof (file_copy));
+
+       /*
+        * Break into components. This will eat up several slashes in a row and
+        * remove leading and trailing slashes..
+        */
+       ptr = file_copy;
+       saveptr = NULL;
+       fields_num = 0;
+       while ((fields[fields_num] = strtok_r (ptr, "/", &saveptr)) != NULL)
+       {
+               ptr = NULL;
+               fields_num++;
+
+               if (fields_num >= 16)
+                       break;
+       }
+
+       /*
+        * For each component, do..
+        */
+       for (i = 0; i < (fields_num - last_is_file); i++)
+       {
+               /*
+                * Do not create directories that start with a dot. This
+                * prevents `../../' attacks and other likely malicious
+                * behavior.
+                */
+               if (fields[i][0] == '.')
+               {
+                       ERROR ("Cowardly refusing to create a directory that "
+                                       "begins with a `.' (dot): `%s'", file_orig);
+                       return (-2);
+               }
+
+               /*
+                * Join the components together again
+                */
+               dir[0] = '/';
+               if (strjoin (dir + path_is_absolute, dir_len - path_is_absolute,
+                                       fields, i + 1, "/") < 0)
+               {
+                       ERROR ("strjoin failed: `%s', component #%i", file_orig, i);
+                       return (-1);
+               }
+
+               while (42) {
+                       if ((stat (dir, &statbuf) == -1)
+                                       && (lstat (dir, &statbuf) == -1))
+                       {
+                               if (errno == ENOENT)
+                               {
+                                       if (mkdir (dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
+                                               break;
+
+                                       /* this might happen, if a different thread created
+                                        * the directory in the meantime
+                                        * => call stat() again to check for S_ISDIR() */
+                                       if (EEXIST == errno)
+                                               continue;
+
+                                       char errbuf[1024];
+                                       ERROR ("check_create_dir: mkdir (%s): %s", dir,
+                                                       sstrerror (errno,
+                                                               errbuf, sizeof (errbuf)));
+                                       return (-1);
+                               }
+                               else
+                               {
+                                       char errbuf[1024];
+                                       ERROR ("check_create_dir: stat (%s): %s", dir,
+                                                       sstrerror (errno, errbuf,
+                                                               sizeof (errbuf)));
+                                       return (-1);
+                               }
+                       }
+                       else if (!S_ISDIR (statbuf.st_mode))
+                       {
+                               ERROR ("check_create_dir: `%s' exists but is not "
+                                               "a directory!", dir);
+                               return (-1);
+                       }
+                       break;
+               }
+       }
+
+       return (0);
+} /* check_create_dir */
+
+#ifdef HAVE_LIBKSTAT
+int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
+{
+       char ident[128];
+
+       *ksp_ptr = NULL;
+
+       if (kc == NULL)
+               return (-1);
+
+       ssnprintf (ident, sizeof (ident), "%s,%i,%s", module, instance, name);
+
+       *ksp_ptr = kstat_lookup (kc, module, instance, name);
+       if (*ksp_ptr == NULL)
+       {
+               ERROR ("get_kstat: Cound not find kstat %s", ident);
+               return (-1);
+       }
+
+       if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
+       {
+               ERROR ("get_kstat: kstat %s has wrong type", ident);
+               *ksp_ptr = NULL;
+               return (-1);
+       }
+
+#ifdef assert
+       assert (*ksp_ptr != NULL);
+       assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
+#endif
+
+       if (kstat_read (kc, *ksp_ptr, NULL) == -1)
+       {
+               ERROR ("get_kstat: kstat %s could not be read", ident);
+               return (-1);
+       }
+
+       if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
+       {
+               ERROR ("get_kstat: kstat %s has wrong type", ident);
+               return (-1);
+       }
+
+       return (0);
+}
+
+long long get_kstat_value (kstat_t *ksp, char *name)
+{
+       kstat_named_t *kn;
+       long long retval = -1LL;
+
+       if (ksp == NULL)
+       {
+               ERROR ("get_kstat_value (\"%s\"): ksp is NULL.", name);
+               return (-1LL);
+       }
+       else if (ksp->ks_type != KSTAT_TYPE_NAMED)
+       {
+               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);
+       }
+
+       if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
+               return (-1LL);
+
+       if (kn->data_type == KSTAT_DATA_INT32)
+               retval = (long long) kn->value.i32;
+       else if (kn->data_type == KSTAT_DATA_UINT32)
+               retval = (long long) kn->value.ui32;
+       else if (kn->data_type == KSTAT_DATA_INT64)
+               retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
+       else if (kn->data_type == KSTAT_DATA_UINT64)
+               retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
+       else
+               WARNING ("get_kstat_value: Not a numeric value: %s", name);
+
+       return (retval);
+}
+#endif /* HAVE_LIBKSTAT */
+
+#ifndef HAVE_HTONLL
+unsigned long long ntohll (unsigned long long n)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+       return (n);
+#else
+       return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32);
+#endif
+} /* unsigned long long ntohll */
+
+unsigned long long htonll (unsigned long long n)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+       return (n);
+#else
+       return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
+#endif
+} /* unsigned long long htonll */
+#endif /* HAVE_HTONLL */
+
+#if FP_LAYOUT_NEED_NOTHING
+/* Well, we need nothing.. */
+/* #endif FP_LAYOUT_NEED_NOTHING */
+
+#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
+# if FP_LAYOUT_NEED_ENDIANFLIP
+#  define FP_CONVERT(A) ((((uint64_t)(A) & 0xff00000000000000LL) >> 56) | \
+                         (((uint64_t)(A) & 0x00ff000000000000LL) >> 40) | \
+                         (((uint64_t)(A) & 0x0000ff0000000000LL) >> 24) | \
+                         (((uint64_t)(A) & 0x000000ff00000000LL) >> 8)  | \
+                         (((uint64_t)(A) & 0x00000000ff000000LL) << 8)  | \
+                         (((uint64_t)(A) & 0x0000000000ff0000LL) << 24) | \
+                         (((uint64_t)(A) & 0x000000000000ff00LL) << 40) | \
+                         (((uint64_t)(A) & 0x00000000000000ffLL) << 56))
+# else
+#  define FP_CONVERT(A) ((((uint64_t)(A) & 0xffffffff00000000LL) >> 32) | \
+                         (((uint64_t)(A) & 0x00000000ffffffffLL) << 32))
+# endif
+
+double ntohd (double d)
+{
+       union
+       {
+               uint8_t  byte[8];
+               uint64_t integer;
+               double   floating;
+       } ret;
+
+       ret.floating = d;
+
+       /* NAN in x86 byte order */
+       if ((ret.byte[0] == 0x00) && (ret.byte[1] == 0x00)
+                       && (ret.byte[2] == 0x00) && (ret.byte[3] == 0x00)
+                       && (ret.byte[4] == 0x00) && (ret.byte[5] == 0x00)
+                       && (ret.byte[6] == 0xf8) && (ret.byte[7] == 0x7f))
+       {
+               return (NAN);
+       }
+       else
+       {
+               uint64_t tmp;
+
+               tmp = ret.integer;
+               ret.integer = FP_CONVERT (tmp);
+               return (ret.floating);
+       }
+} /* double ntohd */
+
+double htond (double d)
+{
+       union
+       {
+               uint8_t  byte[8];
+               uint64_t integer;
+               double   floating;
+       } ret;
+
+       if (isnan (d))
+       {
+               ret.byte[0] = ret.byte[1] = ret.byte[2] = ret.byte[3] = 0x00;
+               ret.byte[4] = ret.byte[5] = 0x00;
+               ret.byte[6] = 0xf8;
+               ret.byte[7] = 0x7f;
+               return (ret.floating);
+       }
+       else
+       {
+               uint64_t tmp;
+
+               ret.floating = d;
+               tmp = FP_CONVERT (ret.integer);
+               ret.integer = tmp;
+               return (ret.floating);
+       }
+} /* double htond */
+#endif /* FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP */
+
+int format_name (char *ret, int ret_len,
+               const char *hostname,
+               const char *plugin, const char *plugin_instance,
+               const char *type, const char *type_instance)
+{
+  char *buffer;
+  size_t buffer_size;
+
+  buffer = ret;
+  buffer_size = (size_t) ret_len;
+
+#define APPEND(str) do {                                               \
+  size_t l = strlen (str);                                             \
+  if (l >= buffer_size)                                                \
+    return (ENOBUFS);                                                  \
+  memcpy (buffer, (str), l);                                           \
+  buffer += l; buffer_size -= l;                                       \
+} while (0)
+
+  assert (plugin != NULL);
+  assert (type != NULL);
+
+  APPEND (hostname);
+  APPEND ("/");
+  APPEND (plugin);
+  if ((plugin_instance != NULL) && (plugin_instance[0] != 0))
+  {
+    APPEND ("-");
+    APPEND (plugin_instance);
+  }
+  APPEND ("/");
+  APPEND (type);
+  if ((type_instance != NULL) && (type_instance[0] != 0))
+  {
+    APPEND ("-");
+    APPEND (type_instance);
+  }
+  assert (buffer_size > 0);
+  buffer[0] = 0;
+
+#undef APPEND
+  return (0);
+} /* int format_name */
+
+int format_values (char *ret, size_t ret_len, /* {{{ */
+               const data_set_t *ds, const value_list_t *vl,
+               _Bool store_rates)
+{
+        size_t offset = 0;
+        int status;
+        int i;
+        gauge_t *rates = NULL;
+
+        assert (0 == strcmp (ds->type, vl->type));
+
+        memset (ret, 0, ret_len);
+
+#define BUFFER_ADD(...) do { \
+        status = ssnprintf (ret + offset, ret_len - offset, \
+                        __VA_ARGS__); \
+        if (status < 1) \
+        { \
+                sfree (rates); \
+                return (-1); \
+        } \
+        else if (((size_t) status) >= (ret_len - offset)) \
+        { \
+                sfree (rates); \
+                return (-1); \
+        } \
+        else \
+                offset += ((size_t) status); \
+} while (0)
+
+        BUFFER_ADD ("%.3f", CDTIME_T_TO_DOUBLE (vl->time));
+
+        for (i = 0; i < ds->ds_num; i++)
+        {
+                if (ds->ds[i].type == DS_TYPE_GAUGE)
+                        BUFFER_ADD (":%f", vl->values[i].gauge);
+                else if (store_rates)
+                {
+                        if (rates == NULL)
+                                rates = uc_get_rate (ds, vl);
+                        if (rates == NULL)
+                        {
+                                WARNING ("format_values: "
+                                               "uc_get_rate failed.");
+                                return (-1);
+                        }
+                        BUFFER_ADD (":%g", rates[i]);
+                }
+                else if (ds->ds[i].type == DS_TYPE_COUNTER)
+                        BUFFER_ADD (":%llu", vl->values[i].counter);
+                else if (ds->ds[i].type == DS_TYPE_DERIVE)
+                        BUFFER_ADD (":%"PRIi64, vl->values[i].derive);
+                else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+                        BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
+                else
+                {
+                        ERROR ("format_values plugin: Unknown data source type: %i",
+                                        ds->ds[i].type);
+                        sfree (rates);
+                        return (-1);
+                }
+        } /* for ds->ds_num */
+
+#undef BUFFER_ADD
+
+        sfree (rates);
+        return (0);
+} /* }}} int format_values */
+
+int parse_identifier (char *str, char **ret_host,
+               char **ret_plugin, char **ret_plugin_instance,
+               char **ret_type, char **ret_type_instance)
+{
+       char *hostname = NULL;
+       char *plugin = NULL;
+       char *plugin_instance = NULL;
+       char *type = NULL;
+       char *type_instance = NULL;
+
+       hostname = str;
+       if (hostname == NULL)
+               return (-1);
+
+       plugin = strchr (hostname, '/');
+       if (plugin == NULL)
+               return (-1);
+       *plugin = '\0'; plugin++;
+
+       type = strchr (plugin, '/');
+       if (type == NULL)
+               return (-1);
+       *type = '\0'; type++;
+
+       plugin_instance = strchr (plugin, '-');
+       if (plugin_instance != NULL)
+       {
+               *plugin_instance = '\0';
+               plugin_instance++;
+       }
+
+       type_instance = strchr (type, '-');
+       if (type_instance != NULL)
+       {
+               *type_instance = '\0';
+               type_instance++;
+       }
+
+       *ret_host = hostname;
+       *ret_plugin = plugin;
+       *ret_plugin_instance = plugin_instance;
+       *ret_type = type;
+       *ret_type_instance = type_instance;
+       return (0);
+} /* int parse_identifier */
+
+int parse_identifier_vl (const char *str, value_list_t *vl) /* {{{ */
+{
+       char str_copy[6 * DATA_MAX_NAME_LEN];
+       char *host = NULL;
+       char *plugin = NULL;
+       char *plugin_instance = NULL;
+       char *type = NULL;
+       char *type_instance = NULL;
+       int status;
+
+       if ((str == NULL) || (vl == NULL))
+               return (EINVAL);
+
+       sstrncpy (str_copy, str, sizeof (str_copy));
+
+       status = parse_identifier (str_copy, &host,
+                       &plugin, &plugin_instance,
+                       &type, &type_instance);
+       if (status != 0)
+               return (status);
+
+       sstrncpy (vl->host, host, sizeof (vl->host));
+       sstrncpy (vl->plugin, plugin, sizeof (vl->plugin));
+       sstrncpy (vl->plugin_instance,
+                       (plugin_instance != NULL) ? plugin_instance : "",
+                       sizeof (vl->plugin_instance));
+       sstrncpy (vl->type, type, sizeof (vl->type));
+       sstrncpy (vl->type_instance,
+                       (type_instance != NULL) ? type_instance : "",
+                       sizeof (vl->type_instance));
+
+       return (0);
+} /* }}} int parse_identifier_vl */
+
+int parse_value (const char *value_orig, value_t *ret_value, int ds_type)
+{
+  char *value;
+  char *endptr = NULL;
+  size_t value_len;
+
+  if (value_orig == NULL)
+    return (EINVAL);
+
+  value = strdup (value_orig);
+  if (value == NULL)
+    return (ENOMEM);
+  value_len = strlen (value);
+
+  while ((value_len > 0) && isspace ((int) value[value_len - 1]))
+  {
+    value[value_len - 1] = 0;
+    value_len--;
+  }
+
+  switch (ds_type)
+  {
+    case DS_TYPE_COUNTER:
+      ret_value->counter = (counter_t) strtoull (value, &endptr, 0);
+      break;
+
+    case DS_TYPE_GAUGE:
+      ret_value->gauge = (gauge_t) strtod (value, &endptr);
+      break;
+
+    case DS_TYPE_DERIVE:
+      ret_value->derive = (derive_t) strtoll (value, &endptr, 0);
+      break;
+
+    case DS_TYPE_ABSOLUTE:
+      ret_value->absolute = (absolute_t) strtoull (value, &endptr, 0);
+      break;
+
+    default:
+      sfree (value);
+      ERROR ("parse_value: Invalid data source type: %i.", ds_type);
+      return -1;
+  }
+
+  if (value == endptr) {
+    ERROR ("parse_value: Failed to parse string as %s: %s.",
+        DS_TYPE_TO_STRING (ds_type), value);
+    sfree (value);
+    return -1;
+  }
+  else if ((NULL != endptr) && ('\0' != *endptr))
+    INFO ("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
+        "Input string was \"%s\".",
+        endptr, DS_TYPE_TO_STRING (ds_type), value_orig);
+
+  sfree (value);
+  return 0;
+} /* int parse_value */
+
+int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
+{
+       int i;
+       char *dummy;
+       char *ptr;
+       char *saveptr;
+
+       i = -1;
+       dummy = buffer;
+       saveptr = NULL;
+       while ((ptr = strtok_r (dummy, ":", &saveptr)) != NULL)
+       {
+               dummy = NULL;
+
+               if (i >= vl->values_len)
+               {
+                       /* Make sure i is invalid. */
+                       i = vl->values_len + 1;
+                       break;
+               }
+
+               if (i == -1)
+               {
+                       if (strcmp ("N", ptr) == 0)
+                               vl->time = cdtime ();
+                       else
+                       {
+                               char *endptr = NULL;
+                               double tmp;
+
+                               errno = 0;
+                               tmp = strtod (ptr, &endptr);
+                               if ((errno != 0)                    /* Overflow */
+                                               || (endptr == ptr)  /* Invalid string */
+                                               || (endptr == NULL) /* This should not happen */
+                                               || (*endptr != 0))  /* Trailing chars */
+                                       return (-1);
+
+                               vl->time = DOUBLE_TO_CDTIME_T (tmp);
+                       }
+               }
+               else
+               {
+                       if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
+                               vl->values[i].gauge = NAN;
+                       else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i].type))
+                               return -1;
+               }
+
+               i++;
+       } /* while (strtok_r) */
+
+       if ((ptr != NULL) || (i != vl->values_len))
+               return (-1);
+       return (0);
+} /* int parse_values */
+
+#if !HAVE_GETPWNAM_R
+int getpwnam_r (const char *name, struct passwd *pwbuf, char *buf,
+               size_t buflen, struct passwd **pwbufp)
+{
+       int status = 0;
+       struct passwd *pw;
+
+       memset (pwbuf, '\0', sizeof (struct passwd));
+
+       pthread_mutex_lock (&getpwnam_r_lock);
+
+       do
+       {
+               pw = getpwnam (name);
+               if (pw == NULL)
+               {
+                       status = (errno != 0) ? errno : ENOENT;
+                       break;
+               }
+
+#define GETPWNAM_COPY_MEMBER(member) \
+               if (pw->member != NULL) \
+               { \
+                       int len = strlen (pw->member); \
+                       if (len >= buflen) \
+                       { \
+                               status = ENOMEM; \
+                               break; \
+                       } \
+                       sstrncpy (buf, pw->member, buflen); \
+                       pwbuf->member = buf; \
+                       buf    += (len + 1); \
+                       buflen -= (len + 1); \
+               }
+               GETPWNAM_COPY_MEMBER(pw_name);
+               GETPWNAM_COPY_MEMBER(pw_passwd);
+               GETPWNAM_COPY_MEMBER(pw_gecos);
+               GETPWNAM_COPY_MEMBER(pw_dir);
+               GETPWNAM_COPY_MEMBER(pw_shell);
+
+               pwbuf->pw_uid = pw->pw_uid;
+               pwbuf->pw_gid = pw->pw_gid;
+
+               if (pwbufp != NULL)
+                       *pwbufp = pwbuf;
+       } while (0);
+
+       pthread_mutex_unlock (&getpwnam_r_lock);
+
+       return (status);
+} /* int getpwnam_r */
+#endif /* !HAVE_GETPWNAM_R */
+
+int notification_init (notification_t *n, int severity, const char *message,
+               const char *host,
+               const char *plugin, const char *plugin_instance,
+               const char *type, const char *type_instance)
+{
+       memset (n, '\0', sizeof (notification_t));
+
+       n->severity = severity;
+
+       if (message != NULL)
+               sstrncpy (n->message, message, sizeof (n->message));
+       if (host != NULL)
+               sstrncpy (n->host, host, sizeof (n->host));
+       if (plugin != NULL)
+               sstrncpy (n->plugin, plugin, sizeof (n->plugin));
+       if (plugin_instance != NULL)
+               sstrncpy (n->plugin_instance, plugin_instance,
+                               sizeof (n->plugin_instance));
+       if (type != NULL)
+               sstrncpy (n->type, type, sizeof (n->type));
+       if (type_instance != NULL)
+               sstrncpy (n->type_instance, type_instance,
+                               sizeof (n->type_instance));
+
+       return (0);
+} /* int notification_init */
+
+int walk_directory (const char *dir, dirwalk_callback_f callback,
+               void *user_data, int include_hidden)
+{
+       struct dirent *ent;
+       DIR *dh;
+       int success;
+       int failure;
+
+       success = 0;
+       failure = 0;
+
+       if ((dh = opendir (dir)) == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("walk_directory: Cannot open '%s': %s", dir,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return -1;
+       }
+
+       while ((ent = readdir (dh)) != NULL)
+       {
+               int status;
+
+               if (include_hidden)
+               {
+                       if ((strcmp (".", ent->d_name) == 0)
+                                       || (strcmp ("..", ent->d_name) == 0))
+                               continue;
+               }
+               else /* if (!include_hidden) */
+               {
+                       if (ent->d_name[0]=='.')
+                               continue;
+               }
+
+               status = (*callback) (dir, ent->d_name, user_data);
+               if (status != 0)
+                       failure++;
+               else
+                       success++;
+       }
+
+       closedir (dh);
+
+       if ((success == 0) && (failure > 0))
+               return (-1);
+       return (0);
+}
+
+ssize_t read_file_contents (const char *filename, char *buf, size_t bufsize)
+{
+       FILE *fh;
+       ssize_t ret;
+
+       fh = fopen (filename, "r");
+       if (fh == NULL)
+               return (-1);
+
+       ret = (ssize_t) fread (buf, 1, bufsize, fh);
+       if ((ret == 0) && (ferror (fh) != 0))
+       {
+               ERROR ("read_file_contents: Reading file \"%s\" failed.",
+                               filename);
+               ret = -1;
+       }
+
+       fclose(fh);
+       return (ret);
+}
+
+counter_t counter_diff (counter_t old_value, counter_t new_value)
+{
+       counter_t diff;
+
+       if (old_value > new_value)
+       {
+               if (old_value <= 4294967295U)
+                       diff = (4294967295U - old_value) + new_value;
+               else
+                       diff = (18446744073709551615ULL - old_value)
+                               + new_value;
+       }
+       else
+       {
+               diff = new_value - old_value;
+       }
+
+       return (diff);
+} /* 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 value_to_rate (value_t *ret_rate, derive_t value, /* {{{ */
+               value_to_rate_state_t *state,
+               int ds_type, cdtime_t t)
+{
+       double interval;
+
+       /* Another invalid state: The time is not increasing. */
+       if (t <= state->last_time)
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       interval = CDTIME_T_TO_DOUBLE(t - state->last_time);
+
+       /* Previous value is invalid. */
+       if (state->last_time == 0) /* {{{ */
+       {
+               if (ds_type == DS_TYPE_DERIVE)
+               {
+                       state->last_value.derive = value;
+               }
+               else if (ds_type == DS_TYPE_COUNTER)
+               {
+                       state->last_value.counter = (counter_t) value;
+               }
+               else if (ds_type == DS_TYPE_ABSOLUTE)
+               {
+                       state->last_value.absolute = (absolute_t) value;
+               }
+               else
+               {
+                       assert (23 == 42);
+               }
+
+               state->last_time = t;
+               return (EAGAIN);
+       } /* }}} */
+
+       if (ds_type == DS_TYPE_DERIVE)
+       {
+               ret_rate->gauge = (value - state->last_value.derive) / interval;
+               state->last_value.derive = value;
+       }
+       else if (ds_type == DS_TYPE_COUNTER)
+       {
+               ret_rate->gauge = (((counter_t)value) - state->last_value.counter) / interval;
+               state->last_value.counter = (counter_t) value;
+       }
+       else if (ds_type == DS_TYPE_ABSOLUTE)
+       {
+               ret_rate->gauge = (((absolute_t)value) - state->last_value.absolute) / interval;
+               state->last_value.absolute = (absolute_t) value;
+       }
+       else
+       {
+               assert (23 == 42);
+       }
+
+        state->last_time = t;
+       return (0);
+} /* }}} value_t rate_to_value */
+
+int service_name_to_port_number (const char *service_name)
+{
+       struct addrinfo *ai_list;
+       struct addrinfo *ai_ptr;
+       struct addrinfo ai_hints;
+       int status;
+       int service_number;
+
+       if (service_name == NULL)
+               return (-1);
+
+       ai_list = NULL;
+       memset (&ai_hints, 0, sizeof (ai_hints));
+       ai_hints.ai_family = AF_UNSPEC;
+
+       status = getaddrinfo (/* node = */ NULL, service_name,
+                       &ai_hints, &ai_list);
+       if (status != 0)
+       {
+               ERROR ("service_name_to_port_number: getaddrinfo failed: %s",
+                               gai_strerror (status));
+               return (-1);
+       }
+
+       service_number = -1;
+       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+       {
+               if (ai_ptr->ai_family == AF_INET)
+               {
+                       struct sockaddr_in *sa;
+
+                       sa = (void *) ai_ptr->ai_addr;
+                       service_number = (int) ntohs (sa->sin_port);
+               }
+               else if (ai_ptr->ai_family == AF_INET6)
+               {
+                       struct sockaddr_in6 *sa;
+
+                       sa = (void *) ai_ptr->ai_addr;
+                       service_number = (int) ntohs (sa->sin6_port);
+               }
+
+               if ((service_number > 0) && (service_number <= 65535))
+                       break;
+       }
+
+       freeaddrinfo (ai_list);
+
+       if ((service_number > 0) && (service_number <= 65535))
+               return (service_number);
+       return (-1);
+} /* int service_name_to_port_number */
+
+int strtoderive (const char *string, derive_t *ret_value) /* {{{ */
+{
+       derive_t tmp;
+       char *endptr;
+
+       if ((string == NULL) || (ret_value == NULL))
+               return (EINVAL);
+
+       errno = 0;
+       endptr = NULL;
+       tmp = (derive_t) strtoll (string, &endptr, /* base = */ 0);
+       if ((endptr == string) || (errno != 0))
+               return (-1);
+
+       *ret_value = tmp;
+       return (0);
+} /* }}} int strtoderive */
+
+int strtogauge (const char *string, gauge_t *ret_value) /* {{{ */
+{
+       gauge_t tmp;
+       char *endptr = NULL;
+
+       if ((string == NULL) || (ret_value == NULL))
+               return (EINVAL);
+
+       errno = 0;
+       endptr = NULL;
+       tmp = (gauge_t) strtod (string, &endptr);
+       if (errno != 0)
+               return (errno);
+       else if ((endptr == NULL) || (*endptr != 0))
+               return (EINVAL);
+
+       *ret_value = tmp;
+       return (0);
+} /* }}} int strtogauge */
+
+int strarray_add (char ***ret_array, size_t *ret_array_len, char const *str) /* {{{ */
+{
+       char **array;
+       size_t array_len = *ret_array_len;
+
+       if (str == NULL)
+               return (EINVAL);
+
+       array = realloc (*ret_array,
+            (array_len + 1) * sizeof (*array));
+       if (array == NULL)
+               return (ENOMEM);
+       *ret_array = array;
+
+       array[array_len] = strdup (str);
+       if (array[array_len] == NULL)
+               return (ENOMEM);
+
+       array_len++;
+        *ret_array_len = array_len;
+       return (0);
+} /* }}} int strarray_add */
+
+void strarray_free (char **array, size_t array_len) /* {{{ */
+{
+       size_t i;
+
+       for (i = 0; i < array_len; i++)
+               sfree (array[i]);
+       sfree (array);
+} /* }}} void strarray_free */
diff --git a/src/daemon/common.h b/src/daemon/common.h
new file mode 100644 (file)
index 0000000..da21cad
--- /dev/null
@@ -0,0 +1,378 @@
+/**
+ * collectd - src/common.h
+ * Copyright (C) 2005-2014  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>
+ *   Niki W. Waibel <niki.waibel@gmx.net>
+**/
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "collectd.h"
+#include "plugin.h"
+
+#if HAVE_PWD_H
+# include <pwd.h>
+#endif
+
+#define sfree(ptr) \
+       do { \
+               if((ptr) != NULL) { \
+                       free(ptr); \
+               } \
+               (ptr) = NULL; \
+       } while (0)
+
+#define STATIC_ARRAY_SIZE(a) (sizeof (a) / sizeof (*(a)))
+
+#define IS_TRUE(s) ((strcasecmp ("true", (s)) == 0) \
+               || (strcasecmp ("yes", (s)) == 0) \
+               || (strcasecmp ("on", (s)) == 0))
+#define IS_FALSE(s) ((strcasecmp ("false", (s)) == 0) \
+               || (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;
+
+struct value_to_rate_state_s
+{
+  value_t last_value;
+  cdtime_t last_time;
+};
+typedef struct value_to_rate_state_s value_to_rate_state_t;
+
+char *sstrncpy (char *dest, const char *src, size_t n);
+
+__attribute__ ((format(printf,3,4)))
+int ssnprintf (char *dest, size_t n, const char *format, ...);
+
+__attribute__ ((format(printf,1,2)))
+char *ssnprintf_alloc (char const *format, ...);
+
+char *sstrdup(const char *s);
+void *smalloc(size_t size);
+char *sstrerror (int errnum, char *buf, size_t buflen);
+
+/*
+ * NAME
+ *   sread
+ *
+ * DESCRIPTION
+ *   Reads exactly `n' bytes or fails. Syntax and other behavior is analogous
+ *   to `read(2)'. If EOF is received the file descriptor is closed and an
+ *   error is returned.
+ *
+ * PARAMETERS
+ *   `fd'          File descriptor to write to.
+ *   `buf'         Buffer that is to be written.
+ *   `count'       Number of bytes in the buffer.
+ *
+ * RETURN VALUE
+ *   Zero upon success or non-zero if an error occurred. `errno' is set in this
+ *   case.
+ */
+ssize_t sread (int fd, void *buf, size_t count);
+
+/*
+ * NAME
+ *   swrite
+ *
+ * DESCRIPTION
+ *   Writes exactly `n' bytes or fails. Syntax and other behavior is analogous
+ *   to `write(2)'.
+ *
+ * PARAMETERS
+ *   `fd'          File descriptor to write to.
+ *   `buf'         Buffer that is to be written.
+ *   `count'       Number of bytes in the buffer.
+ *
+ * RETURN VALUE
+ *   Zero upon success or non-zero if an error occurred. `errno' is set in this
+ *   case.
+ */
+ssize_t swrite (int fd, const void *buf, size_t count);
+
+/*
+ * NAME
+ *   strsplit
+ *
+ * DESCRIPTION
+ *   Splits a string into parts and stores pointers to the parts in `fields'.
+ *   The characters split at are: " ", "\t", "\r", and "\n".
+ *
+ * PARAMETERS
+ *   `string'      String to split. This string will be modified. `fields' will
+ *                 contain pointers to parts of this string, so free'ing it
+ *                 will destroy `fields' as well.
+ *   `fields'      Array of strings where pointers to the parts will be stored.
+ *   `size'        Number of elements in the array. No more than `size'
+ *                 pointers will be stored in `fields'.
+ *
+ * RETURN VALUE
+ *    Returns the number of parts stored in `fields'.
+ */
+int strsplit (char *string, char **fields, size_t size);
+
+/*
+ * NAME
+ *   strjoin
+ *
+ * DESCRIPTION
+ *   Joins together several parts of a string using `sep' as a separator. This
+ *   is equivalent to the Perl built-in `join'.
+ *
+ * PARAMETERS
+ *   `dst'         Buffer where the result is stored.
+ *   `dst_len'     Length of the destination buffer. No more than this many
+ *                 bytes will be written to the memory pointed to by `dst',
+ *                 including the trailing null-byte.
+ *   `fields'      Array of strings to be joined.
+ *   `fields_num'  Number of elements in the `fields' array.
+ *   `sep'         String to be inserted between any two elements of `fields'.
+ *                 This string is neither prepended nor appended to the result.
+ *                 Instead of passing "" (empty string) one can pass NULL.
+ *
+ * RETURN VALUE
+ *   Returns the number of characters in `dst', NOT including the trailing
+ *   null-byte. If an error occurred (empty array or `dst' too small) a value
+ *   smaller than zero will be returned.
+ */
+int strjoin (char *dst, size_t dst_len, char **fields, size_t fields_num, const char *sep);
+
+/*
+ * NAME
+ *   escape_slashes
+ *
+ * DESCRIPTION
+ *   Removes slashes ("/") from "buffer". If buffer contains a single slash,
+ *   the result will be "root". Leading slashes are removed. All other slashes
+ *   are replaced with underscores ("_").
+ *   This function is used by plugin_dispatch_values() to escape all parts of
+ *   the identifier.
+ *
+ * PARAMETERS
+ *   `buffer'         String to be escaped.
+ *   `buffer_size'    Size of the buffer. No more then this many bytes will be
+ *                    written to `buffer', including the trailing null-byte.
+ *
+ * RETURN VALUE
+ *   Returns zero upon success and a value smaller than zero upon failure.
+ */
+int escape_slashes (char *buffer, size_t buffer_size);
+
+/**
+ * NAME
+ *   escape_string
+ *
+ * DESCRIPTION
+ *   escape_string quotes and escapes a string to be usable with collectd's
+ *   plain text protocol. "simple" strings are left as they are, for example if
+ *   buffer is 'simple' before the call, it will remain 'simple'. However, if
+ *   buffer contains 'more "complex"' before the call, the returned buffer will
+ *   contain '"more \"complex\""'.
+ *
+ *   If the buffer is too small to contain the escaped string, the string will
+ *   be truncated. However, leading and trailing double quotes, as well as an
+ *   ending null byte are guaranteed.
+ *
+ * RETURN VALUE
+ *   Returns zero on success, even if the string was truncated. Non-zero on
+ *   failure.
+ */
+int escape_string (char *buffer, size_t buffer_size);
+
+/*
+ * NAME
+ *   replace_special
+ *
+ * DESCRIPTION
+ *   Replaces any special characters (anything that's not alpha-numeric or a
+ *   dash) with an underscore.
+ *
+ *   E.g. "foo$bar&" would become "foo_bar_".
+ *
+ * PARAMETERS
+ *   `buffer'      String to be handled.
+ *   `buffer_size' Length of the string. The function returns after
+ *                 encountering a null-byte or reading this many bytes.
+ */
+void replace_special (char *buffer, size_t buffer_size);
+
+int strsubstitute (char *str, char c_from, char c_to);
+
+/*
+ * NAME
+ *   strunescape
+ *
+ * DESCRIPTION
+ *   Replaces any escaped characters in a string with the appropriate special
+ *   characters. The following escaped characters are recognized:
+ *
+ *     \t -> <tab>
+ *     \n -> <newline>
+ *     \r -> <carriage return>
+ *
+ *   For all other escacped characters only the backslash will be removed.
+ *
+ * PARAMETERS
+ *   `buf'         String to be unescaped.
+ *   `buf_len'     Length of the string, including the terminating null-byte.
+ *
+ * RETURN VALUE
+ *   Returns zero upon success, a value less than zero else.
+ */
+int strunescape (char *buf, size_t buf_len);
+
+/**
+ * Removed trailing newline characters (CR and LF) from buffer, which must be
+ * null terminated. Returns the length of the resulting string.
+ */
+__attribute__((nonnull (1)))
+size_t strstripnewline (char *buffer);
+
+/*
+ * NAME
+ *   timeval_cmp
+ *
+ * DESCRIPTION
+ *   Compare the two time values `tv0' and `tv1' and store the absolut value
+ *   of the difference in the time value pointed to by `delta' if it does not
+ *   equal NULL.
+ *
+ * RETURN VALUE
+ *   Returns an integer less than, equal to, or greater than zero if `tv0' is
+ *   less than, equal to, or greater than `tv1' respectively.
+ */
+int timeval_cmp (struct timeval tv0, struct timeval tv1, struct timeval *delta);
+
+/* make sure tv_usec stores less than a second */
+#define NORMALIZE_TIMEVAL(tv) \
+       do { \
+               (tv).tv_sec += (tv).tv_usec / 1000000; \
+               (tv).tv_usec = (tv).tv_usec % 1000000; \
+       } while (0)
+
+/* make sure tv_sec stores less than a second */
+#define NORMALIZE_TIMESPEC(tv) \
+       do { \
+               (tv).tv_sec += (tv).tv_nsec / 1000000000; \
+               (tv).tv_nsec = (tv).tv_nsec % 1000000000; \
+       } while (0)
+
+int check_create_dir (const char *file_orig);
+
+#ifdef HAVE_LIBKSTAT
+int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name);
+long long get_kstat_value (kstat_t *ksp, char *name);
+#endif
+
+#ifndef HAVE_HTONLL
+unsigned long long ntohll (unsigned long long n);
+unsigned long long htonll (unsigned long long n);
+#endif
+
+#if FP_LAYOUT_NEED_NOTHING
+# define ntohd(d) (d)
+# define htond(d) (d)
+#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
+double ntohd (double d);
+double htond (double d);
+#else
+# error "Don't know how to convert between host and network representation of doubles."
+#endif
+
+int format_name (char *ret, int ret_len,
+               const char *hostname,
+               const char *plugin, const char *plugin_instance,
+               const char *type, const char *type_instance);
+#define FORMAT_VL(ret, ret_len, vl) \
+       format_name (ret, ret_len, (vl)->host, (vl)->plugin, (vl)->plugin_instance, \
+                       (vl)->type, (vl)->type_instance)
+int format_values (char *ret, size_t ret_len,
+               const data_set_t *ds, const value_list_t *vl,
+               _Bool store_rates);
+
+int parse_identifier (char *str, char **ret_host,
+               char **ret_plugin, char **ret_plugin_instance,
+               char **ret_type, char **ret_type_instance);
+int parse_identifier_vl (const char *str, value_list_t *vl);
+int parse_value (const char *value, value_t *ret_value, int ds_type);
+int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds);
+
+#if !HAVE_GETPWNAM_R
+int getpwnam_r (const char *name, struct passwd *pwbuf, char *buf,
+               size_t buflen, struct passwd **pwbufp);
+#endif
+
+int notification_init (notification_t *n, int severity, const char *message,
+               const char *host,
+               const char *plugin, const char *plugin_instance,
+               const char *type, const char *type_instance);
+#define NOTIFICATION_INIT_VL(n, vl) \
+       notification_init (n, NOTIF_FAILURE, NULL, \
+                       (vl)->host, (vl)->plugin, (vl)->plugin_instance, \
+                       (vl)->type, (vl)->type_instance)
+
+typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename,
+               void *user_data);
+int walk_directory (const char *dir, dirwalk_callback_f callback,
+               void *user_data, int hidden);
+/* Returns the number of bytes read or negative on error. */
+ssize_t read_file_contents (char const *filename, char *buf, size_t 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);
+
+int value_to_rate (value_t *ret_rate, derive_t value,
+               value_to_rate_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);
+
+/** Parse a string to a derive_t value. Returns zero on success or non-zero on
+ * failure. If failure is returned, ret_value is not touched. */
+int strtoderive (const char *string, derive_t *ret_value);
+
+/** Parse a string to a gauge_t value. Returns zero on success or non-zero on
+ * failure. If failure is returned, ret_value is not touched. */
+int strtogauge (const char *string, gauge_t *ret_value);
+
+int strarray_add (char ***ret_array, size_t *ret_array_len, char const *str);
+void strarray_free (char **array, size_t array_len);
+
+#endif /* COMMON_H */
diff --git a/src/daemon/configfile.c b/src/daemon/configfile.c
new file mode 100644 (file)
index 0000000..02fd96f
--- /dev/null
@@ -0,0 +1,1353 @@
+/**
+ * collectd - src/configfile.c
+ * Copyright (C) 2005-2011  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>
+ *   Sebastian tokkee Harl <sh at tokkee.org>
+ **/
+
+#include "collectd.h"
+
+#include "liboconfig/oconfig.h"
+
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "types_list.h"
+#include "filter_chain.h"
+
+#if HAVE_WORDEXP_H
+# include <wordexp.h>
+#endif /* HAVE_WORDEXP_H */
+
+#if HAVE_FNMATCH_H
+# include <fnmatch.h>
+#endif /* HAVE_FNMATCH_H */
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif /* HAVE_LIBGEN_H */
+
+#define ESCAPE_NULL(str) ((str) == NULL ? "(null)" : (str))
+
+/*
+ * Private types
+ */
+typedef struct cf_callback
+{
+       const char  *type;
+       int  (*callback) (const char *, const char *);
+       const char **keys;
+       int    keys_num;
+       plugin_ctx_t ctx;
+       struct cf_callback *next;
+} cf_callback_t;
+
+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;
+
+typedef struct cf_value_map_s
+{
+       char *key;
+       int (*func) (const oconfig_item_t *);
+} cf_value_map_t;
+
+typedef struct cf_global_option_s
+{
+       char *key;
+       char *value;
+       char *def;
+} cf_global_option_t;
+
+/*
+ * Prototypes of callback functions
+ */
+static int dispatch_value_typesdb (const oconfig_item_t *ci);
+static int dispatch_value_plugindir (const oconfig_item_t *ci);
+static int dispatch_loadplugin (const oconfig_item_t *ci);
+
+/*
+ * Private variables
+ */
+static cf_callback_t *first_callback = NULL;
+static cf_complex_callback_t *complex_callback_head = NULL;
+
+static cf_value_map_t cf_value_map[] =
+{
+       {"TypesDB",    dispatch_value_typesdb},
+       {"PluginDir",  dispatch_value_plugindir},
+       {"LoadPlugin", dispatch_loadplugin}
+};
+static int cf_value_map_num = STATIC_ARRAY_SIZE (cf_value_map);
+
+static cf_global_option_t cf_global_options[] =
+{
+       {"BaseDir",     NULL, PKGLOCALSTATEDIR},
+       {"PIDFile",     NULL, PIDFILE},
+       {"Hostname",    NULL, NULL},
+       {"FQDNLookup",  NULL, "true"},
+       {"Interval",    NULL, NULL},
+       {"ReadThreads", NULL, "5"},
+       {"WriteThreads", NULL, "5"},
+       {"WriteQueueLimitHigh", NULL, NULL},
+       {"WriteQueueLimitLow", NULL, NULL},
+       {"Timeout",     NULL, "2"},
+       {"AutoLoadPlugin", NULL, "false"},
+       {"CollectInternalStats", NULL, "false"},
+       {"PreCacheChain",  NULL, "PreCache"},
+       {"PostCacheChain", NULL, "PostCache"},
+       {"MaxReadInterval", NULL, "86400"}
+};
+static int cf_global_options_num = STATIC_ARRAY_SIZE (cf_global_options);
+
+static int cf_default_typesdb = 1;
+
+/*
+ * Functions to handle register/unregister, search, and other plugin related
+ * stuff
+ */
+static cf_callback_t *cf_search (const char *type)
+{
+       cf_callback_t *cf_cb;
+
+       if (type == NULL)
+               return (NULL);
+
+       for (cf_cb = first_callback; cf_cb != NULL; cf_cb = cf_cb->next)
+               if (strcasecmp (cf_cb->type, type) == 0)
+                       break;
+
+       return (cf_cb);
+}
+
+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;
+       int i;
+
+       DEBUG ("type = %s, key = %s, value = %s",
+                       ESCAPE_NULL(type),
+                       ESCAPE_NULL(orig_key),
+                       ESCAPE_NULL(orig_value));
+
+       if ((cf_cb = cf_search (type)) == NULL)
+       {
+               WARNING ("Found a configuration for the `%s' plugin, but "
+                               "the plugin isn't loaded or didn't register "
+                               "a configuration callback.", type);
+               return (-1);
+       }
+
+       if ((key = strdup (orig_key)) == NULL)
+               return (1);
+       if ((value = strdup (orig_value)) == NULL)
+       {
+               free (key);
+               return (2);
+       }
+
+       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)
+                               && (strcasecmp (cf_cb->keys[i], key) == 0))
+               {
+                       ret = (*cf_cb->callback) (key, value);
+                       break;
+               }
+       }
+
+       plugin_set_ctx (old_ctx);
+
+       if (i >= cf_cb->keys_num)
+               WARNING ("Plugin `%s' did not register for value `%s'.", type, key);
+
+       free (key);
+       free (value);
+
+       DEBUG ("cf_dispatch: return (%i)", ret);
+
+       return (ret);
+} /* int cf_dispatch */
+
+static int dispatch_global_option (const oconfig_item_t *ci)
+{
+       if (ci->values_num != 1)
+               return (-1);
+       if (ci->values[0].type == OCONFIG_TYPE_STRING)
+               return (global_option_set (ci->key, ci->values[0].value.string));
+       else if (ci->values[0].type == OCONFIG_TYPE_NUMBER)
+       {
+               char tmp[128];
+               ssnprintf (tmp, sizeof (tmp), "%lf", ci->values[0].value.number);
+               return (global_option_set (ci->key, tmp));
+       }
+       else if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
+       {
+               if (ci->values[0].value.boolean)
+                       return (global_option_set (ci->key, "true"));
+               else
+                       return (global_option_set (ci->key, "false"));
+       }
+
+       return (-1);
+} /* int dispatch_global_option */
+
+static int dispatch_value_typesdb (const oconfig_item_t *ci)
+{
+       int i = 0;
+
+       assert (strcasecmp (ci->key, "TypesDB") == 0);
+
+       cf_default_typesdb = 0;
+
+       if (ci->values_num < 1) {
+               ERROR ("configfile: `TypesDB' needs at least one argument.");
+               return (-1);
+       }
+
+       for (i = 0; i < ci->values_num; ++i)
+       {
+               if (OCONFIG_TYPE_STRING != ci->values[i].type) {
+                       WARNING ("configfile: TypesDB: Skipping %i. argument which "
+                                       "is not a string.", i + 1);
+                       continue;
+               }
+
+               read_types_list (ci->values[i].value.string);
+       }
+       return (0);
+} /* int dispatch_value_typesdb */
+
+static int dispatch_value_plugindir (const oconfig_item_t *ci)
+{
+       assert (strcasecmp (ci->key, "PluginDir") == 0);
+       
+       if (ci->values_num != 1)
+               return (-1);
+       if (ci->values[0].type != OCONFIG_TYPE_STRING)
+               return (-1);
+
+       plugin_set_dir (ci->values[0].value.string);
+       return (0);
+}
+
+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)
+               return (-1);
+       if (ci->values[0].type != OCONFIG_TYPE_STRING)
+               return (-1);
+
+       name = ci->values[0].value.string;
+       if (strcmp ("libvirt", name) == 0)
+               name = "virt";
+
+       /* default to the global interval set before loading this plugin */
+       memset (&ctx, 0, sizeof (ctx));
+       ctx.interval = cf_get_default_interval ();
+
+       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) {
+                       if (cf_util_get_cdtime (ci->children + i, &ctx.interval) != 0) {
+                               /* cf_util_get_cdtime will log an error */
+                               continue;
+                       }
+               }
+               else {
+                       WARNING("Ignoring unknown LoadPlugin option \"%s\" "
+                                       "for plugin \"%s\"",
+                                       ci->children[i].key, ci->values[0].value.string);
+               }
+       }
+
+       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)
+{
+       char  buffer[4096];
+       char *buffer_ptr;
+       int   buffer_free;
+       int i;
+
+       buffer_ptr = buffer;
+       buffer_free = sizeof (buffer);
+
+       for (i = 0; i < ci->values_num; i++)
+       {
+               int status = -1;
+
+               if (ci->values[i].type == OCONFIG_TYPE_STRING)
+                       status = ssnprintf (buffer_ptr, buffer_free, " %s",
+                                       ci->values[i].value.string);
+               else if (ci->values[i].type == OCONFIG_TYPE_NUMBER)
+                       status = ssnprintf (buffer_ptr, buffer_free, " %lf",
+                                       ci->values[i].value.number);
+               else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
+                       status = ssnprintf (buffer_ptr, buffer_free, " %s",
+                                       ci->values[i].value.boolean
+                                       ? "true" : "false");
+
+               if ((status < 0) || (status >= buffer_free))
+                       return (-1);
+               buffer_free -= status;
+               buffer_ptr  += status;
+       }
+       /* skip the initial space */
+       buffer_ptr = buffer + 1;
+
+       return (cf_dispatch (plugin, ci->key, buffer_ptr));
+} /* int dispatch_value_plugin */
+
+static int dispatch_value (const oconfig_item_t *ci)
+{
+       int ret = -2;
+       int i;
+
+       for (i = 0; i < cf_value_map_num; i++)
+               if (strcasecmp (cf_value_map[i].key, ci->key) == 0)
+               {
+                       ret = cf_value_map[i].func (ci);
+                       break;
+               }
+
+       for (i = 0; i < cf_global_options_num; i++)
+               if (strcasecmp (cf_global_options[i].key, ci->key) == 0)
+               {
+                       ret = dispatch_global_option (ci);
+                       break;
+               }
+
+       return (ret);
+} /* int dispatch_value */
+
+static int dispatch_block_plugin (oconfig_item_t *ci)
+{
+       int i;
+       char *name;
+
+       cf_complex_callback_t *cb;
+
+       if (strcasecmp (ci->key, "Plugin") != 0)
+               return (-1);
+       if (ci->values_num < 1)
+               return (-1);
+       if (ci->values[0].type != OCONFIG_TYPE_STRING)
+               return (-1);
+
+       name = ci->values[0].value.string;
+       if (strcmp ("libvirt", name) == 0)
+       {
+               /* TODO(octo): Remove this legacy. */
+               WARNING ("The \"libvirt\" plugin has been renamed to \"virt\" to avoid problems with the build system. "
+                               "Your configuration is still using the old name. "
+                               "Please change it to use \"virt\" as soon as possible. "
+                               "This compatibility code will go away eventually.");
+               name = "virt";
+       }
+
+       if (IS_TRUE (global_option_get ("AutoLoadPlugin")))
+       {
+               int status;
+
+               status = plugin_load (name, /* flags = */ 0);
+               if (status != 0)
+               {
+                       ERROR ("Automatically loading plugin \"%s\" failed "
+                                       "with status %i.", name, status);
+                       return (status);
+               }
+       }
+
+       /* Check for a complex callback first */
+       for (cb = complex_callback_head; cb != NULL; cb = cb->next)
+       {
+               if (strcasecmp (name, cb->type) == 0)
+               {
+                       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++)
+       {
+               if (ci->children[i].children == NULL)
+                       dispatch_value_plugin (name, ci->children + i);
+               else
+               {
+                       WARNING ("There is a `%s' block within the "
+                                       "configuration for the %s plugin. "
+                                       "The plugin either only expects "
+                                       "\"simple\" configuration statements "
+                                       "or wasn't loaded using `LoadPlugin'."
+                                       " Please check your configuration.",
+                                       ci->children[i].key, name);
+               }
+       }
+
+       return (0);
+}
+
+
+static int dispatch_block (oconfig_item_t *ci)
+{
+       if (strcasecmp (ci->key, "LoadPlugin") == 0)
+               return (dispatch_loadplugin (ci));
+       else if (strcasecmp (ci->key, "Plugin") == 0)
+               return (dispatch_block_plugin (ci));
+       else if (strcasecmp (ci->key, "Chain") == 0)
+               return (fc_configure (ci));
+
+       return (0);
+}
+
+static int cf_ci_replace_child (oconfig_item_t *dst, oconfig_item_t *src,
+               int offset)
+{
+       oconfig_item_t *temp;
+       int i;
+
+       assert (offset >= 0);
+       assert (dst->children_num > offset);
+
+       /* Free the memory used by the replaced child. Usually that's the
+        * `Include "blah"' statement. */
+       temp = dst->children + offset;
+       for (i = 0; i < temp->values_num; i++)
+       {
+               if (temp->values[i].type == OCONFIG_TYPE_STRING)
+               {
+                       sfree (temp->values[i].value.string);
+               }
+       }
+       sfree (temp->values);
+       temp = NULL;
+
+       /* If (src->children_num == 0) the array size is decreased. If offset
+        * is _not_ the last element, (offset < (dst->children_num - 1)), then
+        * we need to move the trailing elements before resizing the array. */
+       if ((src->children_num == 0) && (offset < (dst->children_num - 1)))
+       {
+               int nmemb = dst->children_num - (offset + 1);
+               memmove (dst->children + offset, dst->children + offset + 1,
+                               sizeof (oconfig_item_t) * nmemb);
+       }
+
+       /* Resize the memory containing the children to be big enough to hold
+        * all children. */
+       if (dst->children_num + src->children_num - 1 == 0)
+       {
+               dst->children_num = 0;
+               return (0);
+       }
+
+       temp = (oconfig_item_t *) realloc (dst->children,
+                       sizeof (oconfig_item_t)
+                       * (dst->children_num + src->children_num - 1));
+       if (temp == NULL)
+       {
+               ERROR ("configfile: realloc failed.");
+               return (-1);
+       }
+       dst->children = temp;
+
+       /* If there are children behind the include statement, and they have
+        * not yet been moved because (src->children_num == 0), then move them
+        * to the end of the list, so that the new children have room before
+        * them. */
+       if ((src->children_num > 0)
+                       && ((dst->children_num - (offset + 1)) > 0))
+       {
+               int nmemb = dst->children_num - (offset + 1);
+               int old_offset = offset + 1;
+               int new_offset = offset + src->children_num;
+
+               memmove (dst->children + new_offset,
+                               dst->children + old_offset,
+                               sizeof (oconfig_item_t) * nmemb);
+       }
+
+       /* Last but not least: If there are new children, copy them to the
+        * memory reserved for them. */
+       if (src->children_num > 0)
+       {
+               memcpy (dst->children + offset,
+                               src->children,
+                               sizeof (oconfig_item_t) * src->children_num);
+       }
+
+       /* Update the number of children. */
+       dst->children_num += (src->children_num - 1);
+
+       return (0);
+} /* int cf_ci_replace_child */
+
+static int cf_ci_append_children (oconfig_item_t *dst, oconfig_item_t *src)
+{
+       oconfig_item_t *temp;
+
+       if ((src == NULL) || (src->children_num == 0))
+               return (0);
+
+       temp = (oconfig_item_t *) realloc (dst->children,
+                       sizeof (oconfig_item_t)
+                       * (dst->children_num + src->children_num));
+       if (temp == NULL)
+       {
+               ERROR ("configfile: realloc failed.");
+               return (-1);
+       }
+       dst->children = temp;
+
+       memcpy (dst->children + dst->children_num,
+                       src->children,
+                       sizeof (oconfig_item_t)
+                       * src->children_num);
+       dst->children_num += src->children_num;
+
+       return (0);
+} /* int cf_ci_append_children */
+
+#define CF_MAX_DEPTH 8
+static oconfig_item_t *cf_read_generic (const char *path,
+               const char *pattern, int depth);
+
+static int cf_include_all (oconfig_item_t *root, int depth)
+{
+       int i;
+
+       for (i = 0; i < root->children_num; i++)
+       {
+               oconfig_item_t *new;
+               oconfig_item_t *old;
+
+               char *pattern = NULL;
+
+               int j;
+
+               if (strcasecmp (root->children[i].key, "Include") != 0)
+                       continue;
+
+               old = root->children + i;
+
+               if ((old->values_num != 1)
+                               || (old->values[0].type != OCONFIG_TYPE_STRING))
+               {
+                       ERROR ("configfile: `Include' needs exactly one string argument.");
+                       continue;
+               }
+
+               for (j = 0; j < old->children_num; ++j)
+               {
+                       oconfig_item_t *child = old->children + j;
+
+                       if (strcasecmp (child->key, "Filter") == 0)
+                               cf_util_get_string (child, &pattern);
+                       else
+                               ERROR ("configfile: Option `%s' not allowed in <Include> block.",
+                                               child->key);
+               }
+
+               new = cf_read_generic (old->values[0].value.string, pattern, depth + 1);
+               sfree (pattern);
+
+               if (new == NULL)
+                       return (-1);
+
+               /* Now replace the i'th child in `root' with `new'. */
+               if (cf_ci_replace_child (root, new, i) < 0)
+                       return (-1);
+
+               /* ... and go back to the new i'th child. */
+               --i;
+
+               sfree (new->values);
+               sfree (new);
+       } /* for (i = 0; i < root->children_num; i++) */
+
+       return (0);
+} /* int cf_include_all */
+
+static oconfig_item_t *cf_read_file (const char *file,
+               const char *pattern, int depth)
+{
+       oconfig_item_t *root;
+       int status;
+
+       assert (depth < CF_MAX_DEPTH);
+
+       if (pattern != NULL) {
+#if HAVE_FNMATCH_H && HAVE_LIBGEN_H
+               char *tmp = sstrdup (file);
+               char *filename = basename (tmp);
+
+               if ((filename != NULL) && (fnmatch (pattern, filename, 0) != 0)) {
+                       DEBUG ("configfile: Not including `%s' because it "
+                                       "does not match pattern `%s'.",
+                                       filename, pattern);
+                       free (tmp);
+                       return (NULL);
+               }
+
+               free (tmp);
+#else
+               ERROR ("configfile: Cannot apply pattern filter '%s' "
+                               "to file '%s': functions basename() and / or "
+                               "fnmatch() not available.", pattern, file);
+#endif /* HAVE_FNMATCH_H && HAVE_LIBGEN_H */
+       }
+
+       root = oconfig_parse_file (file);
+       if (root == NULL)
+       {
+               ERROR ("configfile: Cannot read file `%s'.", file);
+               return (NULL);
+       }
+
+       status = cf_include_all (root, depth);
+       if (status != 0)
+       {
+               oconfig_free (root);
+               return (NULL);
+       }
+
+       return (root);
+} /* oconfig_item_t *cf_read_file */
+
+static int cf_compare_string (const void *p1, const void *p2)
+{
+       return strcmp (*(const char **) p1, *(const char **) p2);
+}
+
+static oconfig_item_t *cf_read_dir (const char *dir,
+               const char *pattern, int depth)
+{
+       oconfig_item_t *root = NULL;
+       DIR *dh;
+       struct dirent *de;
+       char **filenames = NULL;
+       int filenames_num = 0;
+       int status;
+       int i;
+
+       assert (depth < CF_MAX_DEPTH);
+
+       dh = opendir (dir);
+       if (dh == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("configfile: opendir failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (NULL);
+       }
+
+       root = (oconfig_item_t *) malloc (sizeof (oconfig_item_t));
+       if (root == NULL)
+       {
+               ERROR ("configfile: malloc failed.");
+               return (NULL);
+       }
+       memset (root, 0, sizeof (oconfig_item_t));
+
+       while ((de = readdir (dh)) != NULL)
+       {
+               char   name[1024];
+               char **tmp;
+
+               if ((de->d_name[0] == '.') || (de->d_name[0] == 0))
+                       continue;
+
+               status = ssnprintf (name, sizeof (name), "%s/%s",
+                               dir, de->d_name);
+               if ((status < 0) || ((size_t) status >= sizeof (name)))
+               {
+                       ERROR ("configfile: Not including `%s/%s' because its"
+                                       " name is too long.",
+                                       dir, de->d_name);
+                       for (i = 0; i < filenames_num; ++i)
+                               free (filenames[i]);
+                       free (filenames);
+                       free (root);
+                       return (NULL);
+               }
+
+               ++filenames_num;
+               tmp = (char **) realloc (filenames,
+                               filenames_num * sizeof (*filenames));
+               if (tmp == NULL) {
+                       ERROR ("configfile: realloc failed.");
+                       for (i = 0; i < filenames_num - 1; ++i)
+                               free (filenames[i]);
+                       free (filenames);
+                       free (root);
+                       return (NULL);
+               }
+               filenames = tmp;
+
+               filenames[filenames_num - 1] = sstrdup (name);
+       }
+
+       qsort ((void *) filenames, filenames_num, sizeof (*filenames),
+                       cf_compare_string);
+
+       for (i = 0; i < filenames_num; ++i)
+       {
+               oconfig_item_t *temp;
+               char *name = filenames[i];
+
+               temp = cf_read_generic (name, pattern, depth);
+               if (temp == NULL)
+               {
+                       /* An error should already have been reported. */
+                       sfree (name);
+                       continue;
+               }
+
+               cf_ci_append_children (root, temp);
+               sfree (temp->children);
+               sfree (temp);
+
+               free (name);
+       }
+
+       free(filenames);
+       return (root);
+} /* oconfig_item_t *cf_read_dir */
+
+/* 
+ * cf_read_generic
+ *
+ * Path is stat'ed and either cf_read_file or cf_read_dir is called
+ * accordingly.
+ *
+ * There are two versions of this function: If `wordexp' exists shell wildcards
+ * will be expanded and the function will include all matches found. If
+ * `wordexp' (or, more precisely, it's header file) is not available the
+ * simpler function is used which does not do any such expansion.
+ */
+#if HAVE_WORDEXP_H
+static oconfig_item_t *cf_read_generic (const char *path,
+               const char *pattern, int depth)
+{
+       oconfig_item_t *root = NULL;
+       int status;
+       const char *path_ptr;
+       wordexp_t we;
+       size_t i;
+
+       if (depth >= CF_MAX_DEPTH)
+       {
+               ERROR ("configfile: Not including `%s' because the maximum "
+                               "nesting depth has been reached.", path);
+               return (NULL);
+       }
+
+       status = wordexp (path, &we, WRDE_NOCMD);
+       if (status != 0)
+       {
+               ERROR ("configfile: wordexp (%s) failed.", path);
+               return (NULL);
+       }
+
+       root = (oconfig_item_t *) malloc (sizeof (oconfig_item_t));
+       if (root == NULL)
+       {
+               ERROR ("configfile: malloc failed.");
+               return (NULL);
+       }
+       memset (root, '\0', sizeof (oconfig_item_t));
+
+       /* wordexp() might return a sorted list already. That's not
+        * documented though, so let's make sure we get what we want. */
+       qsort ((void *) we.we_wordv, we.we_wordc, sizeof (*we.we_wordv),
+                       cf_compare_string);
+
+       for (i = 0; i < we.we_wordc; i++)
+       {
+               oconfig_item_t *temp;
+               struct stat statbuf;
+
+               path_ptr = we.we_wordv[i];
+
+               status = stat (path_ptr, &statbuf);
+               if (status != 0)
+               {
+                       char errbuf[1024];
+                       WARNING ("configfile: stat (%s) failed: %s",
+                                       path_ptr,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       continue;
+               }
+
+               if (S_ISREG (statbuf.st_mode))
+                       temp = cf_read_file (path_ptr, pattern, depth);
+               else if (S_ISDIR (statbuf.st_mode))
+                       temp = cf_read_dir (path_ptr, pattern, depth);
+               else
+               {
+                       WARNING ("configfile: %s is neither a file nor a "
+                                       "directory.", path);
+                       continue;
+               }
+
+               if (temp == NULL) {
+                       oconfig_free (root);
+                       return (NULL);
+               }
+
+               cf_ci_append_children (root, temp);
+               sfree (temp->children);
+               sfree (temp);
+       }
+
+       wordfree (&we);
+
+       return (root);
+} /* oconfig_item_t *cf_read_generic */
+/* #endif HAVE_WORDEXP_H */
+
+#else /* if !HAVE_WORDEXP_H */
+static oconfig_item_t *cf_read_generic (const char *path,
+               const char *pattern, int depth)
+{
+       struct stat statbuf;
+       int status;
+
+       if (depth >= CF_MAX_DEPTH)
+       {
+               ERROR ("configfile: Not including `%s' because the maximum "
+                               "nesting depth has been reached.", path);
+               return (NULL);
+       }
+
+       status = stat (path, &statbuf);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("configfile: stat (%s) failed: %s",
+                               path,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (NULL);
+       }
+
+       if (S_ISREG (statbuf.st_mode))
+               return (cf_read_file (path, pattern, depth));
+       else if (S_ISDIR (statbuf.st_mode))
+               return (cf_read_dir (path, pattern, depth));
+
+       ERROR ("configfile: %s is neither a file nor a directory.", path);
+       return (NULL);
+} /* oconfig_item_t *cf_read_generic */
+#endif /* !HAVE_WORDEXP_H */
+
+/* 
+ * Public functions
+ */
+int global_option_set (const char *option, const char *value)
+{
+       int i;
+
+       DEBUG ("option = %s; value = %s;", option, value);
+
+       for (i = 0; i < cf_global_options_num; i++)
+               if (strcasecmp (cf_global_options[i].key, option) == 0)
+                       break;
+
+       if (i >= cf_global_options_num)
+               return (-1);
+
+       if (strcasecmp (option, "PIDFile") == 0 && pidfile_from_cli == 1)
+       {
+               DEBUG ("Configfile: Ignoring `PIDFILE' option because "
+                       "command-line option `-P' take precedence.");
+               return (0);
+       }
+
+       sfree (cf_global_options[i].value);
+
+       if (value != NULL)
+               cf_global_options[i].value = strdup (value);
+       else
+               cf_global_options[i].value = NULL;
+
+       return (0);
+}
+
+const char *global_option_get (const char *option)
+{
+       int i;
+
+       for (i = 0; i < cf_global_options_num; i++)
+               if (strcasecmp (cf_global_options[i].key, option) == 0)
+                       break;
+
+       if (i >= cf_global_options_num)
+               return (NULL);
+       
+       return ((cf_global_options[i].value != NULL)
+                       ? cf_global_options[i].value
+                       : cf_global_options[i].def);
+} /* char *global_option_get */
+
+long global_option_get_long (const char *option, long default_value)
+{
+       const char *str;
+       long value;
+
+       str = global_option_get (option);
+       if (NULL == str)
+               return (default_value);
+
+       errno = 0;
+       value = strtol (str, /* endptr = */ NULL, /* base = */ 0);
+       if (errno != 0)
+               return (default_value);
+
+       return (value);
+} /* char *global_option_get_long */
+
+cdtime_t global_option_get_time (const char *name, cdtime_t def) /* {{{ */
+{
+       char const *optstr;
+       char *endptr = NULL;
+       double v;
+
+       optstr = global_option_get (name);
+       if (optstr == NULL)
+               return (def);
+
+       errno = 0;
+       v = strtod (optstr, &endptr);
+       if ((endptr == NULL) || (*endptr != 0) || (errno != 0))
+               return (def);
+       else if (v <= 0.0)
+               return (def);
+
+       return (DOUBLE_TO_CDTIME_T (v));
+} /* }}} cdtime_t global_option_get_time */
+
+cdtime_t cf_get_default_interval (void)
+{
+       return (global_option_get_time ("Interval",
+                              DOUBLE_TO_CDTIME_T (COLLECTD_DEFAULT_INTERVAL)));
+}
+
+void cf_unregister (const char *type)
+{
+       cf_callback_t *this, *prev;
+
+       for (prev = NULL, this = first_callback;
+                       this != NULL;
+                       prev = this, this = this->next)
+               if (strcasecmp (this->type, type) == 0)
+               {
+                       if (prev == NULL)
+                               first_callback = this->next;
+                       else
+                               prev->next = this->next;
+
+                       free (this);
+                       break;
+               }
+} /* void cf_unregister */
+
+void cf_unregister_complex (const char *type)
+{
+       cf_complex_callback_t *this, *prev;
+
+       for (prev = NULL, this = complex_callback_head;
+                       this != NULL;
+                       prev = this, this = this->next)
+               if (strcasecmp (this->type, type) == 0)
+               {
+                       if (prev == NULL)
+                               complex_callback_head = this->next;
+                       else
+                               prev->next = this->next;
+
+                       sfree (this->type);
+                       sfree (this);
+                       break;
+               }
+} /* void cf_unregister */
+
+void cf_register (const char *type,
+               int (*callback) (const char *, const char *),
+               const char **keys, int keys_num)
+{
+       cf_callback_t *cf_cb;
+
+       /* Remove this module from the list, if it already exists */
+       cf_unregister (type);
+
+       /* This pointer will be free'd in `cf_unregister' */
+       if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL)
+               return;
+
+       cf_cb->type     = 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;
+} /* void cf_register */
+
+int cf_register_complex (const char *type, int (*callback) (oconfig_item_t *))
+{
+       cf_complex_callback_t *new;
+
+       new = (cf_complex_callback_t *) malloc (sizeof (cf_complex_callback_t));
+       if (new == NULL)
+               return (-1);
+
+       new->type = strdup (type);
+       if (new->type == NULL)
+       {
+               sfree (new);
+               return (-1);
+       }
+
+       new->callback = callback;
+       new->next = NULL;
+
+       new->ctx = plugin_get_ctx ();
+
+       if (complex_callback_head == NULL)
+       {
+               complex_callback_head = new;
+       }
+       else
+       {
+               cf_complex_callback_t *last = complex_callback_head;
+               while (last->next != NULL)
+                       last = last->next;
+               last->next = new;
+       }
+
+       return (0);
+} /* int cf_register_complex */
+
+int cf_read (char *filename)
+{
+       oconfig_item_t *conf;
+       int i;
+
+       conf = cf_read_generic (filename, /* pattern = */ NULL, /* depth = */ 0);
+       if (conf == NULL)
+       {
+               ERROR ("Unable to read config file %s.", filename);
+               return (-1);
+       }
+       else if (conf->children_num == 0)
+       {
+               ERROR ("Configuration file %s is empty.", filename);
+               oconfig_free (conf);
+               return (-1);
+       }
+
+       for (i = 0; i < conf->children_num; i++)
+       {
+               if (conf->children[i].children == NULL)
+                       dispatch_value (conf->children + i);
+               else
+                       dispatch_block (conf->children + i);
+       }
+
+       oconfig_free (conf);
+
+       /* Read the default types.db if no `TypesDB' option was given. */
+       if (cf_default_typesdb)
+               read_types_list (PKGDATADIR"/types.db");
+
+       return (0);
+} /* int cf_read */
+
+/* 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. */
+int cf_util_get_string (const oconfig_item_t *ci, char **ret_string) /* {{{ */
+{
+       char *string;
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               ERROR ("cf_util_get_string: The %s option requires "
+                               "exactly one string argument.", ci->key);
+               return (-1);
+       }
+
+       string = strdup (ci->values[0].value.string);
+       if (string == NULL)
+               return (-1);
+
+       if (*ret_string != NULL)
+               sfree (*ret_string);
+       *ret_string = string;
+
+       return (0);
+} /* }}} int cf_util_get_string */
+
+/* Assures the config option is a string and copies it to the provided buffer.
+ * Assures null-termination. */
+int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer, /* {{{ */
+               size_t buffer_size)
+{
+       if ((ci == NULL) || (buffer == NULL) || (buffer_size < 1))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               ERROR ("cf_util_get_string_buffer: The %s option requires "
+                               "exactly one string argument.", ci->key);
+               return (-1);
+       }
+
+       strncpy (buffer, ci->values[0].value.string, buffer_size);
+       buffer[buffer_size - 1] = 0;
+
+       return (0);
+} /* }}} int cf_util_get_string_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) /* {{{ */
+{
+       if ((ci == NULL) || (ret_value == NULL))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+       {
+               ERROR ("cf_util_get_int: The %s option requires "
+                               "exactly one numeric argument.", ci->key);
+               return (-1);
+       }
+
+       *ret_value = (int) ci->values[0].value.number;
+
+       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))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+       {
+               ERROR ("cf_util_get_boolean: The %s option requires "
+                               "exactly one boolean argument.", ci->key);
+               return (-1);
+       }
+
+       *ret_bool = ci->values[0].value.boolean ? 1 : 0;
+
+       return (0);
+} /* }}} int cf_util_get_boolean */
+
+int cf_util_get_flag (const oconfig_item_t *ci, /* {{{ */
+               unsigned int *ret_value, unsigned int flag)
+{
+       int status;
+       _Bool b;
+
+       if (ret_value == NULL)
+               return (EINVAL);
+
+       b = 0;
+       status = cf_util_get_boolean (ci, &b);
+       if (status != 0)
+               return (status);
+
+       if (b)
+       {
+               *ret_value |= flag;
+       }
+       else
+       {
+               *ret_value &= ~flag;
+       }
+
+       return (0);
+} /* }}} int cf_util_get_flag */
+
+/* Assures that the config option is a string or a number if the correct range
+ * of 1-65535. The string is then converted to a port number using
+ * `service_name_to_port_number' and returned.
+ * Returns the port number in the range [1-65535] or less than zero upon
+ * failure. */
+int cf_util_get_port_number (const oconfig_item_t *ci) /* {{{ */
+{
+       int tmp;
+
+       if ((ci->values_num != 1)
+                       || ((ci->values[0].type != OCONFIG_TYPE_STRING)
+                               && (ci->values[0].type != OCONFIG_TYPE_NUMBER)))
+       {
+               ERROR ("cf_util_get_port_number: The \"%s\" option requires "
+                               "exactly one string argument.", ci->key);
+               return (-1);
+       }
+
+       if (ci->values[0].type == OCONFIG_TYPE_STRING)
+               return (service_name_to_port_number (ci->values[0].value.string));
+
+       assert (ci->values[0].type == OCONFIG_TYPE_NUMBER);
+       tmp = (int) (ci->values[0].value.number + 0.5);
+       if ((tmp < 1) || (tmp > 65535))
+       {
+               ERROR ("cf_util_get_port_number: The \"%s\" option requires "
+                               "a service name or a port number. The number "
+                               "you specified, %i, is not in the valid "
+                               "range of 1-65535.",
+                               ci->key, tmp);
+               return (-1);
+       }
+
+       return (tmp);
+} /* }}} int cf_util_get_port_number */
+
+int cf_util_get_service (const oconfig_item_t *ci, char **ret_string) /* {{{ */
+{
+       int port;
+       char *service;
+       int status;
+
+       if (ci->values_num != 1)
+       {
+               ERROR ("cf_util_get_service: The %s option requires exactly "
+                               "one argument.", ci->key);
+               return (-1);
+       }
+
+       if (ci->values[0].type == OCONFIG_TYPE_STRING)
+               return (cf_util_get_string (ci, ret_string));
+       if (ci->values[0].type != OCONFIG_TYPE_NUMBER)
+       {
+               ERROR ("cf_util_get_service: The %s option requires "
+                               "exactly one string or numeric argument.",
+                               ci->key);
+       }
+
+       port = 0;
+       status = cf_util_get_int (ci, &port);
+       if (status != 0)
+               return (status);
+       else if ((port < 1) || (port > 65535))
+       {
+               ERROR ("cf_util_get_service: The port number given "
+                               "for the %s option is out of "
+                               "range (%i).", ci->key, port);
+               return (-1);
+       }
+
+       service = malloc (6);
+       if (service == NULL)
+       {
+               ERROR ("cf_util_get_service: Out of memory.");
+               return (-1);
+       }
+       ssnprintf (service, 6, "%i", port);
+
+       sfree (*ret_string);
+       *ret_string = service;
+
+       return (0);
+} /* }}} int cf_util_get_service */
+
+int cf_util_get_cdtime (const oconfig_item_t *ci, cdtime_t *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_cdtime: The %s option requires "
+                               "exactly one numeric argument.", ci->key);
+               return (-1);
+       }
+
+       if (ci->values[0].value.number < 0.0)
+       {
+               ERROR ("cf_util_get_cdtime: The numeric argument of the %s "
+                               "option must not be negative.", ci->key);
+               return (-1);
+       }
+
+       *ret_value = DOUBLE_TO_CDTIME_T (ci->values[0].value.number);
+
+       return (0);
+} /* }}} int cf_util_get_cdtime */
+
diff --git a/src/daemon/configfile.h b/src/daemon/configfile.h
new file mode 100644 (file)
index 0000000..5bc9b30
--- /dev/null
@@ -0,0 +1,141 @@
+/**
+ * collectd - src/configfile.h
+ * Copyright (C) 2005-2011  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 CONFIGFILE_H
+#define CONFIGFILE_H
+
+#include "collectd.h"
+#include "utils_time.h"
+#include "liboconfig/oconfig.h"
+
+/*
+ * DESCRIPTION
+ *  Remove a registered plugin from the internal data structures.
+ * 
+ * PARAMETERS
+ *  `type'      Name of the plugin (must be the same as passed to
+ *              `plugin_register'
+ */
+void cf_unregister (const char *type);
+void cf_unregister_complex (const char *type);
+
+/*
+ * DESCRIPTION
+ *  `cf_register' is called by plugins that wish to receive config keys. The
+ *  plugin will then receive all keys it registered for if they're found in a
+ *  `<Plugin $type>' section.
+ *
+ * PARAMETERS
+ *  `type'      Name of the plugin (must be the same as passed to
+ *              `plugin_register'
+ *  `callback'  Pointer to the callback function. The callback must return zero
+ *              upon success, a value smaller than zero if it doesn't know how
+ *              to handle the `key' passed to it (the first argument) or a
+ *              value greater than zero if it knows how to handle the key but
+ *              failed.
+ *  `keys'      Array of key values this plugin wished to receive. The last
+ *              element must be a NULL-pointer.
+ *  `keys_num'  Number of elements in the array (not counting the last NULL-
+ *              pointer.
+ *
+ * NOTES
+ *  `cf_unregister' will be called for `type' to make sure only one record
+ *  exists for each `type' at any time. This means that `cf_register' may be
+ *  called multiple times, but only the last call will have an effect.
+ */
+void cf_register (const char *type,
+               int (*callback) (const char *, const char *),
+               const char **keys, int keys_num);
+
+int cf_register_complex (const char *type, int (*callback) (oconfig_item_t *));
+
+/*
+ * DESCRIPTION
+ *  `cf_read' reads the config file `filename' and dispatches the read
+ *  information to functions/variables. Most important: Is calls `plugin_load'
+ *  to load specific plugins, depending on the current mode of operation.
+ *
+ * PARAMETERS
+ *  `filename'  An additional filename to look for. This function calls
+ *              `lc_process' which already searches many standard locations..
+ *              If set to NULL will use the `CONFIGFILE' define.
+ *
+ * RETURN VALUE
+ *  Returns zero upon success and non-zero otherwise. A error-message will have
+ *  been printed in this case.
+ */
+int cf_read (char *filename);
+
+int global_option_set (const char *option, const char *value);
+const char *global_option_get (const char *option);
+long global_option_get_long (const char *option, long default_value);
+long global_option_get_long_in_range (const char *option, long default_value, long min, long max);
+
+cdtime_t global_option_get_time (char const *option, cdtime_t default_value);
+
+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. */
+int cf_util_get_string (const oconfig_item_t *ci, char **ret_string);
+
+/* Assures the config option is a string and copies it to the provided buffer.
+ * Assures null-termination. */
+int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer,
+               size_t buffer_size);
+
+/* 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);
+
+/* Assures the config option is a boolean and set or unset the given flag in
+ * `ret_value' as appropriate. Returns non-zero on error. */
+int cf_util_get_flag (const oconfig_item_t *ci,
+               unsigned int *ret_value, unsigned int flag);
+
+/* Assures that the config option is a string or a number if the correct range
+ * of 1-65535. The string is then converted to a port number using
+ * `service_name_to_port_number' and returned.
+ * Returns the port number in the range [1-65535] or less than zero upon
+ * failure. */
+int cf_util_get_port_number (const oconfig_item_t *ci);
+
+/* Assures that the config option is either a service name (a string) or a port
+ * number (an integer in the appropriate range) and returns a newly allocated
+ * string. If ret_string points to a non-NULL pointer, it is freed before
+ * assigning a new value. */
+int cf_util_get_service (const oconfig_item_t *ci, char **ret_string);
+
+int cf_util_get_cdtime (const oconfig_item_t *ci, cdtime_t *ret_value);
+
+#endif /* defined(CONFIGFILE_H) */
diff --git a/src/daemon/filter_chain.c b/src/daemon/filter_chain.c
new file mode 100644 (file)
index 0000000..c87b877
--- /dev/null
@@ -0,0 +1,1058 @@
+/**
+ * collectd - src/filter_chain.c
+ * Copyright (C) 2008-2010  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 "collectd.h"
+#include "configfile.h"
+#include "plugin.h"
+#include "utils_complain.h"
+#include "common.h"
+#include "filter_chain.h"
+
+/*
+ * Data types
+ */
+/* List of matches, used in fc_rule_t and for the global `match_list_head'
+ * variable. */
+struct fc_match_s;
+typedef struct fc_match_s fc_match_t; /* {{{ */
+struct fc_match_s
+{
+  char name[DATA_MAX_NAME_LEN];
+  match_proc_t proc;
+  void *user_data;
+  fc_match_t *next;
+}; /* }}} */
+
+/* List of targets, used in fc_rule_t and for the global `target_list_head'
+ * variable. */
+struct fc_target_s;
+typedef struct fc_target_s fc_target_t; /* {{{ */
+struct fc_target_s
+{
+  char name[DATA_MAX_NAME_LEN];
+  void *user_data;
+  target_proc_t proc;
+  fc_target_t *next;
+}; /* }}} */
+
+/* List of rules, used in fc_chain_t */
+struct fc_rule_s;
+typedef struct fc_rule_s fc_rule_t; /* {{{ */
+struct fc_rule_s
+{
+  char name[DATA_MAX_NAME_LEN];
+  fc_match_t  *matches;
+  fc_target_t *targets;
+  fc_rule_t *next;
+}; /* }}} */
+
+/* List of chains, used for `chain_list_head' */
+struct fc_chain_s /* {{{ */
+{
+  char name[DATA_MAX_NAME_LEN];
+  fc_rule_t   *rules;
+  fc_target_t *targets;
+  fc_chain_t  *next;
+}; /* }}} */
+
+/*
+ * Global variables
+ */
+static fc_match_t  *match_list_head;
+static fc_target_t *target_list_head;
+static fc_chain_t  *chain_list_head;
+
+/*
+ * Private functions
+ */
+static void fc_free_matches (fc_match_t *m) /* {{{ */
+{
+  if (m == NULL)
+    return;
+
+  if (m->proc.destroy != NULL)
+    (*m->proc.destroy) (&m->user_data);
+  else if (m->user_data != NULL)
+  {
+    ERROR ("Filter subsystem: fc_free_matches: There is user data, but no "
+        "destroy functions has been specified. "
+        "Memory will probably be lost!");
+  }
+
+  if (m->next != NULL)
+    fc_free_matches (m->next);
+
+  free (m);
+} /* }}} void fc_free_matches */
+
+static void fc_free_targets (fc_target_t *t) /* {{{ */
+{
+  if (t == NULL)
+    return;
+
+  if (t->proc.destroy != NULL)
+    (*t->proc.destroy) (&t->user_data);
+  else if (t->user_data != NULL)
+  {
+    ERROR ("Filter subsystem: fc_free_targets: There is user data, but no "
+        "destroy functions has been specified. "
+        "Memory will probably be lost!");
+  }
+
+  if (t->next != NULL)
+    fc_free_targets (t->next);
+
+  free (t);
+} /* }}} void fc_free_targets */
+
+static void fc_free_rules (fc_rule_t *r) /* {{{ */
+{
+  if (r == NULL)
+    return;
+
+  fc_free_matches (r->matches);
+  fc_free_targets (r->targets);
+
+  if (r->next != NULL)
+    fc_free_rules (r->next);
+
+  free (r);
+} /* }}} void fc_free_rules */
+
+static void fc_free_chains (fc_chain_t *c) /* {{{ */
+{
+  if (c == NULL)
+    return;
+
+  fc_free_rules (c->rules);
+  fc_free_targets (c->targets);
+
+  if (c->next != NULL)
+    fc_free_chains (c->next);
+
+  free (c);
+} /* }}} void fc_free_chains */
+
+static char *fc_strdup (const char *orig) /* {{{ */
+{
+  size_t sz;
+  char *dest;
+
+  if (orig == NULL)
+    return (NULL);
+
+  sz = strlen (orig) + 1;
+  dest = (char *) malloc (sz);
+  if (dest == NULL)
+    return (NULL);
+
+  memcpy (dest, orig, sz);
+
+  return (dest);
+} /* }}} char *fc_strdup */
+
+/*
+ * Configuration.
+ *
+ * The configuration looks somewhat like this:
+ *
+ *  <Chain "PreCache">
+ *    <Rule>
+ *      <Match "regex">
+ *        Plugin "^mysql$"
+ *        Type "^mysql_command$"
+ *        TypeInstance "^show_"
+ *      </Match>
+ *      <Target "drop">
+ *      </Target>
+ *    </Rule>
+ *
+ *    <Target "write">
+ *      Plugin "rrdtool"
+ *    </Target>
+ *  </Chain>
+ */
+static int fc_config_add_match (fc_match_t **matches_head, /* {{{ */
+    oconfig_item_t *ci)
+{
+  fc_match_t *m;
+  fc_match_t *ptr;
+  int status;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("Filter subsystem: `Match' blocks require "
+        "exactly one string argument.");
+    return (-1);
+  }
+
+  ptr = match_list_head;
+  while (ptr != NULL)
+  {
+    if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
+      break;
+    ptr = ptr->next;
+  }
+
+  if (ptr == NULL)
+  {
+    WARNING ("Filter subsystem: Cannot find a \"%s\" match. "
+        "Did you load the appropriate plugin?",
+        ci->values[0].value.string);
+    return (-1);
+  }
+
+  m = (fc_match_t *) malloc (sizeof (*m));
+  if (m == NULL)
+  {
+    ERROR ("fc_config_add_match: malloc failed.");
+    return (-1);
+  }
+  memset (m, 0, sizeof (*m));
+
+  sstrncpy (m->name, ptr->name, sizeof (m->name));
+  memcpy (&m->proc, &ptr->proc, sizeof (m->proc));
+  m->user_data = NULL;
+  m->next = NULL;
+
+  if (m->proc.create != NULL)
+  {
+    status = (*m->proc.create) (ci, &m->user_data);
+    if (status != 0)
+    {
+      WARNING ("Filter subsystem: Failed to create a %s match.",
+          m->name);
+      fc_free_matches (m);
+      return (-1);
+    }
+  }
+
+  if (*matches_head != NULL)
+  {
+    ptr = *matches_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = m;
+  }
+  else
+  {
+    *matches_head = m;
+  }
+
+  return (0);
+} /* }}} int fc_config_add_match */
+
+static int fc_config_add_target (fc_target_t **targets_head, /* {{{ */
+    oconfig_item_t *ci)
+{
+  fc_target_t *t;
+  fc_target_t *ptr;
+  int status;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("Filter subsystem: `Target' blocks require "
+        "exactly one string argument.");
+    return (-1);
+  }
+
+  ptr = target_list_head;
+  while (ptr != NULL)
+  {
+    if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
+      break;
+    ptr = ptr->next;
+  }
+
+  if (ptr == NULL)
+  {
+    WARNING ("Filter subsystem: Cannot find a \"%s\" target. "
+        "Did you load the appropriate plugin?",
+        ci->values[0].value.string);
+    return (-1);
+  }
+
+  t = (fc_target_t *) malloc (sizeof (*t));
+  if (t == NULL)
+  {
+    ERROR ("fc_config_add_target: malloc failed.");
+    return (-1);
+  }
+  memset (t, 0, sizeof (*t));
+
+  sstrncpy (t->name, ptr->name, sizeof (t->name));
+  memcpy (&t->proc, &ptr->proc, sizeof (t->proc));
+  t->user_data = NULL;
+  t->next = NULL;
+
+  if (t->proc.create != NULL)
+  {
+    status = (*t->proc.create) (ci, &t->user_data);
+    if (status != 0)
+    {
+      WARNING ("Filter subsystem: Failed to create a %s target.",
+          t->name);
+      fc_free_targets (t);
+      return (-1);
+    }
+  }
+  else
+  {
+    t->user_data = NULL;
+  }
+  
+  if (*targets_head != NULL)
+  {
+    ptr = *targets_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = t;
+  }
+  else
+  {
+    *targets_head = t;
+  }
+
+  return (0);
+} /* }}} int fc_config_add_target */
+
+static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */
+    oconfig_item_t *ci)
+{
+  fc_rule_t *rule;
+  char rule_name[2*DATA_MAX_NAME_LEN] = "Unnamed rule";
+  int status = 0;
+  int i;
+
+  if (ci->values_num > 1)
+  {
+    WARNING ("Filter subsystem: `Rule' blocks have at most one argument.");
+    return (-1);
+  }
+  else if ((ci->values_num == 1)
+      && (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("Filter subsystem: `Rule' blocks expect one string argument "
+        "or no argument at all.");
+    return (-1);
+  }
+
+  rule = (fc_rule_t *) malloc (sizeof (*rule));
+  if (rule == NULL)
+  {
+    ERROR ("fc_config_add_rule: malloc failed.");
+    return (-1);
+  }
+  memset (rule, 0, sizeof (*rule));
+  rule->next = NULL;
+
+  if (ci->values_num == 1)
+  {
+    sstrncpy (rule->name, ci->values[0].value.string, sizeof (rule->name));
+    ssnprintf (rule_name, sizeof (rule_name), "Rule \"%s\"",
+        ci->values[0].value.string);
+  }
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *option = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Match", option->key) == 0)
+      status = fc_config_add_match (&rule->matches, option);
+    else if (strcasecmp ("Target", option->key) == 0)
+      status = fc_config_add_target (&rule->targets, option);
+    else
+    {
+      WARNING ("Filter subsystem: %s: Option `%s' not allowed "
+          "inside a <Rule> block.", rule_name, option->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  } /* for (ci->children) */
+
+  /* Additional sanity checking. */
+  while (status == 0)
+  {
+    if (rule->targets == NULL)
+    {
+      WARNING ("Filter subsystem: %s: No target has been specified.",
+          rule_name);
+      status = -1;
+      break;
+    }
+
+    break;
+  } /* while (status == 0) */
+
+  if (status != 0)
+  {
+    fc_free_rules (rule);
+    return (-1);
+  }
+
+  if (chain->rules != NULL)
+  {
+    fc_rule_t *ptr;
+
+    ptr = chain->rules;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = rule;
+  }
+  else
+  {
+    chain->rules = rule;
+  }
+
+  return (0);
+} /* }}} int fc_config_add_rule */
+
+static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
+{
+  fc_chain_t *chain = NULL;
+  int status = 0;
+  int i;
+  int new_chain = 1;
+
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    WARNING ("Filter subsystem: <Chain> blocks require exactly one "
+        "string argument.");
+    return (-1);
+  }
+
+  if (chain_list_head != NULL)
+  {
+    if ((chain = fc_chain_get_by_name (ci->values[0].value.string)) != NULL)
+      new_chain = 0;
+  }
+
+  if (chain == NULL)
+  {
+    chain = (fc_chain_t *) malloc (sizeof (*chain));
+    if (chain == NULL)
+    {
+      ERROR ("fc_config_add_chain: malloc failed.");
+      return (-1);
+    }
+    memset (chain, 0, sizeof (*chain));
+    sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name));
+    chain->rules = NULL;
+    chain->targets = NULL;
+    chain->next = NULL;
+  }
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *option = ci->children + i;
+    status = 0;
+
+    if (strcasecmp ("Rule", option->key) == 0)
+      status = fc_config_add_rule (chain, option);
+    else if (strcasecmp ("Target", option->key) == 0)
+      status = fc_config_add_target (&chain->targets, option);
+    else
+    {
+      WARNING ("Filter subsystem: Chain %s: Option `%s' not allowed "
+          "inside a <Chain> block.", chain->name, option->key);
+      status = -1;
+    }
+
+    if (status != 0)
+      break;
+  } /* for (ci->children) */
+
+  if (status != 0)
+  {
+    fc_free_chains (chain);
+    return (-1);
+  }
+
+  if (chain_list_head != NULL)
+  {
+    if (!new_chain)
+      return (0);
+
+    fc_chain_t *ptr;
+
+    ptr = chain_list_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = chain;
+  }
+  else
+  {
+    chain_list_head = chain;
+  }
+
+  return (0);
+} /* }}} int fc_config_add_chain */
+
+/*
+ * Built-in target "jump"
+ *
+ * Prefix `bit' like `_b_uilt-_i_n _t_arget'
+ */
+static int fc_bit_jump_create (const oconfig_item_t *ci, /* {{{ */
+    void **user_data)
+{
+  oconfig_item_t *ci_chain;
+
+  if (ci->children_num != 1)
+  {
+    ERROR ("Filter subsystem: The built-in target `jump' needs exactly "
+        "one `Chain' argument!");
+    return (-1);
+  }
+
+  ci_chain = ci->children;
+  if (strcasecmp ("Chain", ci_chain->key) != 0)
+  {
+    ERROR ("Filter subsystem: The built-in target `jump' does not "
+        "support the configuration option `%s'.",
+        ci_chain->key);
+    return (-1);
+  }
+
+  if ((ci_chain->values_num != 1)
+      || (ci_chain->values[0].type != OCONFIG_TYPE_STRING))
+  {
+    ERROR ("Filter subsystem: Built-in target `jump': The `Chain' option "
+        "needs exactly one string argument.");
+    return (-1);
+  }
+
+  *user_data = fc_strdup (ci_chain->values[0].value.string);
+  if (*user_data == NULL)
+  {
+    ERROR ("fc_bit_jump_create: fc_strdup failed.");
+    return (-1);
+  }
+
+  return (0);
+} /* }}} int fc_bit_jump_create */
+
+static int fc_bit_jump_destroy (void **user_data) /* {{{ */
+{
+  if (user_data != NULL)
+  {
+    free (*user_data);
+    *user_data = NULL;
+  }
+
+  return (0);
+} /* }}} int fc_bit_jump_destroy */
+
+static int fc_bit_jump_invoke (const data_set_t *ds, /* {{{ */
+    value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
+    void **user_data)
+{
+  char *chain_name;
+  fc_chain_t *chain;
+  int status;
+
+  chain_name = *user_data;
+
+  for (chain = chain_list_head; chain != NULL; chain = chain->next)
+    if (strcasecmp (chain_name, chain->name) == 0)
+      break;
+
+  if (chain == NULL)
+  {
+    ERROR ("Filter subsystem: Built-in target `jump': There is no chain "
+        "named `%s'.", chain_name);
+    return (-1);
+  }
+
+  status = fc_process_chain (ds, vl, chain);
+  if (status < 0)
+    return (status);
+  else if (status == FC_TARGET_STOP)
+    return (FC_TARGET_STOP);
+  else
+    return (FC_TARGET_CONTINUE);
+} /* }}} int fc_bit_jump_invoke */
+
+static int fc_bit_stop_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */
+    value_list_t __attribute__((unused)) *vl,
+    notification_meta_t __attribute__((unused)) **meta,
+    void __attribute__((unused)) **user_data)
+{
+  return (FC_TARGET_STOP);
+} /* }}} int fc_bit_stop_invoke */
+
+static int fc_bit_return_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */
+    value_list_t __attribute__((unused)) *vl,
+    notification_meta_t __attribute__((unused)) **meta,
+    void __attribute__((unused)) **user_data)
+{
+  return (FC_TARGET_RETURN);
+} /* }}} int fc_bit_return_invoke */
+
+static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */
+    void **user_data)
+{
+  int i;
+
+  char **plugin_list;
+  size_t plugin_list_len;
+
+  plugin_list = NULL;
+  plugin_list_len = 0;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+    char **temp;
+    int j;
+
+    if (strcasecmp ("Plugin", child->key) != 0)
+    {
+      ERROR ("Filter subsystem: The built-in target `write' does not "
+          "support the configuration option `%s'.",
+          child->key);
+      continue;
+    }
+
+    for (j = 0; j < child->values_num; j++)
+    {
+      if (child->values[j].type != OCONFIG_TYPE_STRING)
+      {
+        ERROR ("Filter subsystem: Built-in target `write': "
+            "The `Plugin' option accepts only string arguments.");
+        continue;
+      }
+
+      temp = (char **) realloc (plugin_list, (plugin_list_len + 2)
+          * (sizeof (*plugin_list)));
+      if (temp == NULL)
+      {
+        ERROR ("fc_bit_write_create: realloc failed.");
+        continue;
+      }
+      plugin_list = temp;
+
+      plugin_list[plugin_list_len] = fc_strdup (child->values[j].value.string);
+      if (plugin_list[plugin_list_len] == NULL)
+      {
+        ERROR ("fc_bit_write_create: fc_strdup failed.");
+        continue;
+      }
+      plugin_list_len++;
+      plugin_list[plugin_list_len] = NULL;
+    } /* for (j = 0; j < child->values_num; j++) */
+  } /* for (i = 0; i < ci->children_num; i++) */
+
+  *user_data = plugin_list;
+
+  return (0);
+} /* }}} int fc_bit_write_create */
+
+static int fc_bit_write_destroy (void **user_data) /* {{{ */
+{
+  char **plugin_list;
+  size_t i;
+
+  if ((user_data == NULL) || (*user_data == NULL))
+    return (0);
+
+  plugin_list = *user_data;
+
+  for (i = 0; plugin_list[i] != NULL; i++)
+    free (plugin_list[i]);
+  free (plugin_list);
+
+  return (0);
+} /* }}} int fc_bit_write_destroy */
+
+static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */
+    value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
+    void **user_data)
+{
+  char **plugin_list;
+  int status;
+
+  plugin_list = NULL;
+  if (user_data != NULL)
+    plugin_list = *user_data;
+
+  if ((plugin_list == NULL) || (plugin_list[0] == NULL))
+  {
+    static c_complain_t enoent_complaint = C_COMPLAIN_INIT_STATIC;
+
+    status = plugin_write (/* plugin = */ NULL, ds, vl);
+    if (status == ENOENT)
+    {
+      /* in most cases this is a permanent error, so use the complain
+       * mechanism rather than spamming the logs */
+      c_complain (LOG_INFO, &enoent_complaint,
+          "Filter subsystem: Built-in target `write': Dispatching value to "
+          "all write plugins failed with status %i (ENOENT). "
+          "Most likely this means you didn't load any write plugins.",
+          status);
+    }
+    else if (status != 0)
+    {
+      INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
+          "all write plugins failed with status %i.", status);
+    }
+    else
+    {
+      assert (status == 0);
+      c_release (LOG_INFO, &enoent_complaint, "Filter subsystem: "
+          "Built-in target `write': Some write plugin is back to normal "
+          "operation. `write' succeeded.");
+    }
+  }
+  else
+  {
+    size_t i;
+
+    for (i = 0; plugin_list[i] != NULL; i++)
+    {
+      status = plugin_write (plugin_list[i], ds, vl);
+      if (status != 0)
+      {
+        INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
+            "the `%s' plugin failed with status %i.", plugin_list[i], status);
+      }
+    } /* for (i = 0; plugin_list[i] != NULL; i++) */
+  }
+
+  return (FC_TARGET_CONTINUE);
+} /* }}} int fc_bit_write_invoke */
+
+static int fc_init_once (void) /* {{{ */
+{
+  static int done = 0;
+  target_proc_t tproc;
+
+  if (done != 0)
+    return (0);
+
+  memset (&tproc, 0, sizeof (tproc));
+  tproc.create  = fc_bit_jump_create;
+  tproc.destroy = fc_bit_jump_destroy;
+  tproc.invoke  = fc_bit_jump_invoke;
+  fc_register_target ("jump", tproc);
+
+  memset (&tproc, 0, sizeof (tproc));
+  tproc.create  = NULL;
+  tproc.destroy = NULL;
+  tproc.invoke  = fc_bit_stop_invoke;
+  fc_register_target ("stop", tproc);
+
+  memset (&tproc, 0, sizeof (tproc));
+  tproc.create  = NULL;
+  tproc.destroy = NULL;
+  tproc.invoke  = fc_bit_return_invoke;
+  fc_register_target ("return", tproc);
+
+  memset (&tproc, 0, sizeof (tproc));
+  tproc.create  = fc_bit_write_create;
+  tproc.destroy = fc_bit_write_destroy;
+  tproc.invoke  = fc_bit_write_invoke;
+  fc_register_target ("write", tproc);
+
+  done++;
+  return (0);
+} /* }}} int fc_init_once */
+
+/*
+ * Public functions
+ */
+/* Add a match to list of available matches. */
+int fc_register_match (const char *name, match_proc_t proc) /* {{{ */
+{
+  fc_match_t *m;
+
+  DEBUG ("fc_register_match (%s);", name);
+
+  m = (fc_match_t *) malloc (sizeof (*m));
+  if (m == NULL)
+    return (-ENOMEM);
+  memset (m, 0, sizeof (*m));
+
+  sstrncpy (m->name, name, sizeof (m->name));
+  memcpy (&m->proc, &proc, sizeof (m->proc));
+  m->next = NULL;
+
+  if (match_list_head == NULL)
+  {
+    match_list_head = m;
+  }
+  else
+  {
+    fc_match_t *ptr;
+
+    ptr = match_list_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = m;
+  }
+
+  return (0);
+} /* }}} int fc_register_match */
+
+/* Add a target to list of available targets. */
+int fc_register_target (const char *name, target_proc_t proc) /* {{{ */
+{
+  fc_target_t *t;
+
+  DEBUG ("fc_register_target (%s);", name);
+
+  t = (fc_target_t *) malloc (sizeof (*t));
+  if (t == NULL)
+    return (-ENOMEM);
+  memset (t, 0, sizeof (*t));
+
+  sstrncpy (t->name, name, sizeof (t->name));
+  memcpy (&t->proc, &proc, sizeof (t->proc));
+  t->next = NULL;
+
+  if (target_list_head == NULL)
+  {
+    target_list_head = t;
+  }
+  else
+  {
+    fc_target_t *ptr;
+
+    ptr = target_list_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = t;
+  }
+
+  return (0);
+} /* }}} int fc_register_target */
+
+fc_chain_t *fc_chain_get_by_name (const char *chain_name) /* {{{ */
+{
+  fc_chain_t *chain;
+
+  if (chain_name == NULL)
+    return (NULL);
+
+  for (chain = chain_list_head; chain != NULL; chain = chain->next)
+    if (strcasecmp (chain_name, chain->name) == 0)
+      return (chain);
+
+  return (NULL);
+} /* }}} int fc_chain_get_by_name */
+
+int fc_process_chain (const data_set_t *ds, value_list_t *vl, /* {{{ */
+    fc_chain_t *chain)
+{
+  fc_rule_t *rule;
+  fc_target_t *target;
+  int status;
+
+  if (chain == NULL)
+    return (-1);
+
+  DEBUG ("fc_process_chain (chain = %s);", chain->name);
+
+  status = FC_TARGET_CONTINUE;
+  for (rule = chain->rules; rule != NULL; rule = rule->next)
+  {
+    fc_match_t *match;
+
+    if (rule->name[0] != 0)
+    {
+      DEBUG ("fc_process_chain (%s): Testing the `%s' rule.",
+          chain->name, rule->name);
+    }
+
+    /* N. B.: rule->matches may be NULL. */
+    for (match = rule->matches; match != NULL; match = match->next)
+    {
+      /* FIXME: Pass the meta-data to match targets here (when implemented). */
+      status = (*match->proc.match) (ds, vl, /* meta = */ NULL,
+          &match->user_data);
+      if (status < 0)
+      {
+        WARNING ("fc_process_chain (%s): A match failed.", chain->name);
+        break;
+      }
+      else if (status != FC_MATCH_MATCHES)
+        break;
+    }
+
+    /* for-loop has been aborted: Either error or no match. */
+    if (match != NULL)
+    {
+      status = FC_TARGET_CONTINUE;
+      continue;
+    }
+
+    if (rule->name[0] != 0)
+    {
+      DEBUG ("fc_process_chain (%s): Rule `%s' matches.",
+          chain->name, rule->name);
+    }
+
+    for (target = rule->targets; target != NULL; target = target->next)
+    {
+      /* If we get here, all matches have matched the value. Execute the
+       * target. */
+      /* FIXME: Pass the meta-data to match targets here (when implemented). */
+      status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
+          &target->user_data);
+      if (status < 0)
+      {
+        WARNING ("fc_process_chain (%s): A target failed.", chain->name);
+        continue;
+      }
+      else if (status == FC_TARGET_CONTINUE)
+        continue;
+      else if (status == FC_TARGET_STOP)
+        break;
+      else if (status == FC_TARGET_RETURN)
+        break;
+      else
+      {
+        WARNING ("fc_process_chain (%s): Unknown return value "
+            "from target `%s': %i",
+            chain->name, target->name, status);
+      }
+    }
+
+    if ((status == FC_TARGET_STOP)
+        || (status == FC_TARGET_RETURN))
+    {
+      if (rule->name[0] != 0)
+      {
+        DEBUG ("fc_process_chain (%s): Rule `%s' signaled "
+            "the %s condition.",
+            chain->name, rule->name,
+            (status == FC_TARGET_STOP) ? "stop" : "return");
+      }
+      break;
+    }
+    else
+    {
+      status = FC_TARGET_CONTINUE;
+    }
+  } /* for (rule) */
+
+  if (status == FC_TARGET_STOP)
+    return (FC_TARGET_STOP);
+  else if (status == FC_TARGET_RETURN)
+    return (FC_TARGET_CONTINUE);
+
+  /* for-loop has been aborted: A target returned `FC_TARGET_STOP' */
+  if (rule != NULL)
+    return (FC_TARGET_CONTINUE);
+
+  DEBUG ("fc_process_chain (%s): Executing the default targets.",
+      chain->name);
+
+  status = FC_TARGET_CONTINUE;
+  for (target = chain->targets; target != NULL; target = target->next)
+  {
+    /* If we get here, all matches have matched the value. Execute the
+     * target. */
+    /* FIXME: Pass the meta-data to match targets here (when implemented). */
+    status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
+        &target->user_data);
+    if (status < 0)
+    {
+      WARNING ("fc_process_chain (%s): The default target failed.",
+          chain->name);
+    }
+    else if (status == FC_TARGET_CONTINUE)
+      continue;
+    else if (status == FC_TARGET_STOP)
+      break;
+    else if (status == FC_TARGET_RETURN)
+      break;
+    else
+    {
+      WARNING ("fc_process_chain (%s): Unknown return value "
+          "from target `%s': %i",
+          chain->name, target->name, status);
+    }
+  }
+
+  if ((status == FC_TARGET_STOP)
+      || (status == FC_TARGET_RETURN))
+  {
+    assert (target != NULL);
+    DEBUG ("fc_process_chain (%s): Default target `%s' signaled "
+        "the %s condition.",
+        chain->name, target->name,
+        (status == FC_TARGET_STOP) ? "stop" : "return");
+    if (status == FC_TARGET_STOP)
+      return (FC_TARGET_STOP);
+    else
+      return (FC_TARGET_CONTINUE);
+  }
+
+  DEBUG ("fc_process_chain (%s): Signaling `continue' at end of chain.",
+      chain->name);
+
+  return (FC_TARGET_CONTINUE);
+} /* }}} int fc_process_chain */
+
+/* Iterate over all rules in the chain and execute all targets for which all
+ * matches match. */
+int fc_default_action (const data_set_t *ds, value_list_t *vl) /* {{{ */
+{
+  /* FIXME: Pass the meta-data to match targets here (when implemented). */
+  return (fc_bit_write_invoke (ds, vl,
+        /* meta = */ NULL, /* user_data = */ NULL));
+} /* }}} int fc_default_action */
+
+int fc_configure (const oconfig_item_t *ci) /* {{{ */
+{
+  fc_init_once ();
+
+  if (ci == NULL)
+    return (-EINVAL);
+
+  if (strcasecmp ("Chain", ci->key) == 0)
+    return (fc_config_add_chain (ci));
+
+  WARNING ("Filter subsystem: Unknown top level config option `%s'.",
+      ci->key);
+
+  return (-1);
+} /* }}} int fc_configure */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/daemon/filter_chain.h b/src/daemon/filter_chain.h
new file mode 100644 (file)
index 0000000..2db90db
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * collectd - src/filter_chain.h
+ * Copyright (C) 2008,2009  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 FILTER_CHAIN_H
+#define FILTER_CHAIN_H 1
+
+#include "collectd.h"
+#include "plugin.h"
+
+#define FC_MATCH_NO_MATCH  0
+#define FC_MATCH_MATCHES   1
+
+#define FC_TARGET_CONTINUE 0
+#define FC_TARGET_STOP     1
+#define FC_TARGET_RETURN   2
+
+/*
+ * Match functions
+ */
+struct match_proc_s
+{
+  int (*create) (const oconfig_item_t *ci, void **user_data);
+  int (*destroy) (void **user_data);
+  int (*match) (const data_set_t *ds, const value_list_t *vl,
+      notification_meta_t **meta, void **user_data);
+};
+typedef struct match_proc_s match_proc_t;
+
+int fc_register_match (const char *name, match_proc_t proc);
+
+/*
+ * Target functions
+ */
+struct target_proc_s
+{
+  int (*create) (const oconfig_item_t *ci, void **user_data);
+  int (*destroy) (void **user_data);
+  int (*invoke) (const data_set_t *ds, value_list_t *vl,
+      notification_meta_t **meta, void **user_data);
+};
+typedef struct target_proc_s target_proc_t;
+
+struct fc_chain_s;
+typedef struct fc_chain_s fc_chain_t;
+
+int fc_register_target (const char *name, target_proc_t proc);
+
+/*
+ * TODO: Chain management
+ */
+#if 0
+int fc_chain_add (const char *chain_name,
+    const char *target_name, int target_argc, char **target_argv);
+int fc_chain_delete (const char *chain_name);
+#endif
+
+/*
+ * TODO: Rule management
+ */
+#if 0
+int fc_rule_add (const char *chain_name, int position,
+    int match_num, const char **match_name, int *match_argc, char ***match_argv,
+    const char *target_name, int target_argc, char **target_argv);
+int fc_rule_delete (const char *chain_name, int position);
+#endif
+
+/*
+ * Processing function
+ */
+fc_chain_t *fc_chain_get_by_name (const char *chain_name);
+
+int fc_process_chain (const data_set_t *ds, value_list_t *vl,
+    fc_chain_t *chain);
+
+int fc_default_action (const data_set_t *ds, value_list_t *vl);
+
+/* 
+ * Shortcut for global configuration
+ */
+int fc_configure (const oconfig_item_t *ci);
+
+#endif /* FILTER_CHAIN_H */
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/daemon/meta_data.c b/src/daemon/meta_data.c
new file mode 100644 (file)
index 0000000..d3da9bb
--- /dev/null
@@ -0,0 +1,637 @@
+/**
+ * collectd - src/meta_data.c
+ * Copyright (C) 2008-2011  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 "collectd.h"
+#include "plugin.h"
+#include "meta_data.h"
+
+#include <pthread.h>
+
+/*
+ * Data types
+ */
+union meta_value_u
+{
+  char    *mv_string;
+  int64_t  mv_signed_int;
+  uint64_t mv_unsigned_int;
+  double   mv_double;
+  _Bool    mv_boolean;
+};
+typedef union meta_value_u meta_value_t;
+
+struct meta_entry_s;
+typedef struct meta_entry_s meta_entry_t;
+struct meta_entry_s
+{
+  char         *key;
+  meta_value_t  value;
+  int           type;
+  meta_entry_t *next;
+};
+
+struct meta_data_s
+{
+  meta_entry_t   *head;
+  pthread_mutex_t lock;
+};
+
+/*
+ * Private functions
+ */
+static char *md_strdup (const char *orig) /* {{{ */
+{
+  size_t sz;
+  char *dest;
+
+  if (orig == NULL)
+    return (NULL);
+
+  sz = strlen (orig) + 1;
+  dest = (char *) malloc (sz);
+  if (dest == NULL)
+    return (NULL);
+
+  memcpy (dest, orig, sz);
+
+  return (dest);
+} /* }}} char *md_strdup */
+
+static meta_entry_t *md_entry_alloc (const char *key) /* {{{ */
+{
+  meta_entry_t *e;
+
+  e = (meta_entry_t *) malloc (sizeof (*e));
+  if (e == NULL)
+  {
+    ERROR ("md_entry_alloc: malloc failed.");
+    return (NULL);
+  }
+  memset (e, 0, sizeof (*e));
+
+  e->key = md_strdup (key);
+  if (e->key == NULL)
+  {
+    free (e);
+    ERROR ("md_entry_alloc: md_strdup failed.");
+    return (NULL);
+  }
+
+  e->type = 0;
+  e->next = NULL;
+
+  return (e);
+} /* }}} meta_entry_t *md_entry_alloc */
+
+static meta_entry_t *md_entry_clone (const meta_entry_t *orig) /* {{{ */
+{
+  meta_entry_t *copy;
+
+  if (orig == NULL)
+    return (NULL);
+
+  copy = md_entry_alloc (orig->key);
+  copy->type = orig->type;
+  if (copy->type == MD_TYPE_STRING)
+    copy->value.mv_string = strdup (orig->value.mv_string);
+  else
+    copy->value = orig->value;
+
+  copy->next = md_entry_clone (orig->next);
+  return (copy);
+} /* }}} meta_entry_t *md_entry_clone */
+
+static void md_entry_free (meta_entry_t *e) /* {{{ */
+{
+  if (e == NULL)
+    return;
+
+  free (e->key);
+
+  if (e->type == MD_TYPE_STRING)
+    free (e->value.mv_string);
+
+  if (e->next != NULL)
+    md_entry_free (e->next);
+
+  free (e);
+} /* }}} void md_entry_free */
+
+static int md_entry_insert (meta_data_t *md, meta_entry_t *e) /* {{{ */
+{
+  meta_entry_t *this;
+  meta_entry_t *prev;
+
+  if ((md == NULL) || (e == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  prev = NULL;
+  this = md->head;
+  while (this != NULL)
+  {
+    if (strcasecmp (e->key, this->key) == 0)
+      break;
+
+    prev = this;
+    this = this->next;
+  }
+
+  if (this == NULL)
+  {
+    /* This key does not exist yet. */
+    if (md->head == NULL)
+      md->head = e;
+    else
+    {
+      assert (prev != NULL);
+      prev->next = e;
+    }
+
+    e->next = NULL;
+  }
+  else /* (this != NULL) */
+  {
+    if (prev == NULL)
+      md->head = e;
+    else
+      prev->next = e;
+
+    e->next = this->next;
+  }
+
+  pthread_mutex_unlock (&md->lock);
+
+  if (this != NULL)
+  {
+    this->next = NULL;
+    md_entry_free (this);
+  }
+
+  return (0);
+} /* }}} int md_entry_insert */
+
+/* XXX: The lock on md must be held while calling this function! */
+static meta_entry_t *md_entry_lookup (meta_data_t *md, /* {{{ */
+    const char *key)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (NULL);
+
+  for (e = md->head; e != NULL; e = e->next)
+    if (strcasecmp (key, e->key) == 0)
+      break;
+
+  return (e);
+} /* }}} meta_entry_t *md_entry_lookup */
+
+/*
+ * Public functions
+ */
+meta_data_t *meta_data_create (void) /* {{{ */
+{
+  meta_data_t *md;
+
+  md = (meta_data_t *) malloc (sizeof (*md));
+  if (md == NULL)
+  {
+    ERROR ("meta_data_create: malloc failed.");
+    return (NULL);
+  }
+  memset (md, 0, sizeof (*md));
+
+  md->head = NULL;
+  pthread_mutex_init (&md->lock, /* attr = */ NULL);
+
+  return (md);
+} /* }}} meta_data_t *meta_data_create */
+
+meta_data_t *meta_data_clone (meta_data_t *orig) /* {{{ */
+{
+  meta_data_t *copy;
+
+  if (orig == NULL)
+    return (NULL);
+
+  copy = meta_data_create ();
+  if (copy == NULL)
+    return (NULL);
+
+  pthread_mutex_lock (&orig->lock);
+  copy->head = md_entry_clone (orig->head);
+  pthread_mutex_unlock (&orig->lock);
+
+  return (copy);
+} /* }}} meta_data_t *meta_data_clone */
+
+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);
+} /* }}} void meta_data_destroy */
+
+int meta_data_exists (meta_data_t *md, const char *key) /* {{{ */
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  for (e = md->head; e != NULL; e = e->next)
+  {
+    if (strcasecmp (key, e->key) == 0)
+    {
+      pthread_mutex_unlock (&md->lock);
+      return (1);
+    }
+  }
+
+  pthread_mutex_unlock (&md->lock);
+  return (0);
+} /* }}} int meta_data_exists */
+
+int meta_data_type (meta_data_t *md, const char *key) /* {{{ */
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return -EINVAL;
+
+  pthread_mutex_lock (&md->lock);
+
+  for (e = md->head; e != NULL; e = e->next)
+  {
+    if (strcasecmp (key, e->key) == 0)
+    {
+      pthread_mutex_unlock (&md->lock);
+      return e->type;
+    }
+  }
+
+  pthread_mutex_unlock (&md->lock);
+  return 0;
+} /* }}} int meta_data_type */
+
+int meta_data_toc (meta_data_t *md, char ***toc) /* {{{ */
+{
+  int i = 0, count = 0;
+  meta_entry_t *e;
+
+  if ((md == NULL) || (toc == NULL))
+    return -EINVAL;
+
+  pthread_mutex_lock (&md->lock);
+
+  for (e = md->head; e != NULL; e = e->next)
+    ++count;    
+
+  if (count == 0)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (count);
+  }
+
+  *toc = calloc(count, sizeof(**toc));
+  for (e = md->head; e != NULL; e = e->next)
+    (*toc)[i++] = strdup(e->key);
+  
+  pthread_mutex_unlock (&md->lock);
+  return count;
+} /* }}} int meta_data_toc */
+
+int meta_data_delete (meta_data_t *md, const char *key) /* {{{ */
+{
+  meta_entry_t *this;
+  meta_entry_t *prev;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  prev = NULL;
+  this = md->head;
+  while (this != NULL)
+  {
+    if (strcasecmp (key, this->key) == 0)
+      break;
+
+    prev = this;
+    this = this->next;
+  }
+
+  if (this == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (prev == NULL)
+    md->head = this->next;
+  else
+    prev->next = this->next;
+
+  pthread_mutex_unlock (&md->lock);
+
+  this->next = NULL;
+  md_entry_free (this);
+
+  return (0);
+} /* }}} int meta_data_delete */
+
+/*
+ * Add functions
+ */
+int meta_data_add_string (meta_data_t *md, /* {{{ */
+    const char *key, const char *value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  e = md_entry_alloc (key);
+  if (e == NULL)
+    return (-ENOMEM);
+
+  e->value.mv_string = md_strdup (value);
+  if (e->value.mv_string == NULL)
+  {
+    ERROR ("meta_data_add_string: md_strdup failed.");
+    md_entry_free (e);
+    return (-ENOMEM);
+  }
+  e->type = MD_TYPE_STRING;
+
+  return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_string */
+
+int meta_data_add_signed_int (meta_data_t *md, /* {{{ */
+    const char *key, int64_t value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  e = md_entry_alloc (key);
+  if (e == NULL)
+    return (-ENOMEM);
+
+  e->value.mv_signed_int = value;
+  e->type = MD_TYPE_SIGNED_INT;
+
+  return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_signed_int */
+
+int meta_data_add_unsigned_int (meta_data_t *md, /* {{{ */
+    const char *key, uint64_t value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  e = md_entry_alloc (key);
+  if (e == NULL)
+    return (-ENOMEM);
+
+  e->value.mv_unsigned_int = value;
+  e->type = MD_TYPE_UNSIGNED_INT;
+
+  return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_unsigned_int */
+
+int meta_data_add_double (meta_data_t *md, /* {{{ */
+    const char *key, double value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  e = md_entry_alloc (key);
+  if (e == NULL)
+    return (-ENOMEM);
+
+  e->value.mv_double = value;
+  e->type = MD_TYPE_DOUBLE;
+
+  return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_double */
+
+int meta_data_add_boolean (meta_data_t *md, /* {{{ */
+    const char *key, _Bool value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL))
+    return (-EINVAL);
+
+  e = md_entry_alloc (key);
+  if (e == NULL)
+    return (-ENOMEM);
+
+  e->value.mv_boolean = value;
+  e->type = MD_TYPE_BOOLEAN;
+
+  return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_boolean */
+
+/*
+ * Get functions
+ */
+int meta_data_get_string (meta_data_t *md, /* {{{ */
+    const char *key, char **value)
+{
+  meta_entry_t *e;
+  char *temp;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (e->type != MD_TYPE_STRING)
+  {
+    ERROR ("meta_data_get_signed_int: Type mismatch for key `%s'", e->key);
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  temp = md_strdup (e->value.mv_string);
+  if (temp == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    ERROR ("meta_data_get_string: md_strdup failed.");
+    return (-ENOMEM);
+  }
+  pthread_mutex_unlock (&md->lock);
+
+  *value = temp;
+
+  return (0);
+} /* }}} int meta_data_get_string */
+
+int meta_data_get_signed_int (meta_data_t *md, /* {{{ */
+    const char *key, int64_t *value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (e->type != MD_TYPE_SIGNED_INT)
+  {
+    ERROR ("meta_data_get_signed_int: Type mismatch for key `%s'", e->key);
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  *value = e->value.mv_signed_int;
+
+  pthread_mutex_unlock (&md->lock);
+  return (0);
+} /* }}} int meta_data_get_signed_int */
+
+int meta_data_get_unsigned_int (meta_data_t *md, /* {{{ */
+    const char *key, uint64_t *value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (e->type != MD_TYPE_UNSIGNED_INT)
+  {
+    ERROR ("meta_data_get_unsigned_int: Type mismatch for key `%s'", e->key);
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  *value = e->value.mv_unsigned_int;
+
+  pthread_mutex_unlock (&md->lock);
+  return (0);
+} /* }}} int meta_data_get_unsigned_int */
+
+int meta_data_get_double (meta_data_t *md, /* {{{ */
+    const char *key, double *value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (e->type != MD_TYPE_DOUBLE)
+  {
+    ERROR ("meta_data_get_double: Type mismatch for key `%s'", e->key);
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  *value = e->value.mv_double;
+
+  pthread_mutex_unlock (&md->lock);
+  return (0);
+} /* }}} int meta_data_get_double */
+
+int meta_data_get_boolean (meta_data_t *md, /* {{{ */
+    const char *key, _Bool *value)
+{
+  meta_entry_t *e;
+
+  if ((md == NULL) || (key == NULL) || (value == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&md->lock);
+
+  e = md_entry_lookup (md, key);
+  if (e == NULL)
+  {
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  if (e->type != MD_TYPE_BOOLEAN)
+  {
+    ERROR ("meta_data_get_boolean: Type mismatch for key `%s'", e->key);
+    pthread_mutex_unlock (&md->lock);
+    return (-ENOENT);
+  }
+
+  *value = e->value.mv_boolean;
+
+  pthread_mutex_unlock (&md->lock);
+  return (0);
+} /* }}} int meta_data_get_boolean */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/daemon/meta_data.h b/src/daemon/meta_data.h
new file mode 100644 (file)
index 0000000..fa48df3
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * collectd - src/meta_data.h
+ * Copyright (C) 2008-2011  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 META_DATA_H
+#define META_DATA_H
+
+#include "collectd.h"
+
+/*
+ * Defines
+ */
+#define MD_TYPE_STRING       1
+#define MD_TYPE_SIGNED_INT   2
+#define MD_TYPE_UNSIGNED_INT 3
+#define MD_TYPE_DOUBLE       4
+#define MD_TYPE_BOOLEAN      5
+
+struct meta_data_s;
+typedef struct meta_data_s meta_data_t;
+
+meta_data_t *meta_data_create (void);
+meta_data_t *meta_data_clone (meta_data_t *orig);
+void meta_data_destroy (meta_data_t *md);
+
+int meta_data_exists (meta_data_t *md, const char *key);
+int meta_data_type (meta_data_t *md, const char *key);
+int meta_data_toc (meta_data_t *md, char ***toc);
+int meta_data_delete (meta_data_t *md, const char *key);
+
+int meta_data_add_string (meta_data_t *md,
+    const char *key,
+    const char *value);
+int meta_data_add_signed_int (meta_data_t *md,
+    const char *key,
+    int64_t value);
+int meta_data_add_unsigned_int (meta_data_t *md,
+    const char *key,
+    uint64_t value);
+int meta_data_add_double (meta_data_t *md,
+    const char *key,
+    double value);
+int meta_data_add_boolean (meta_data_t *md,
+    const char *key,
+    _Bool value);
+
+int meta_data_get_string (meta_data_t *md,
+    const char *key,
+    char **value);
+int meta_data_get_signed_int (meta_data_t *md,
+    const char *key,
+    int64_t *value);
+int meta_data_get_unsigned_int (meta_data_t *md,
+    const char *key,
+    uint64_t *value);
+int meta_data_get_double (meta_data_t *md,
+    const char *key,
+    double *value);
+int meta_data_get_boolean (meta_data_t *md,
+    const char *key,
+    _Bool *value);
+
+#endif /* META_DATA_H */
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/daemon/plugin.c b/src/daemon/plugin.c
new file mode 100644 (file)
index 0000000..97b794f
--- /dev/null
@@ -0,0 +1,2652 @@
+/**
+ * collectd - src/plugin.c
+ * Copyright (C) 2005-2014  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>
+ *   Sebastian Harl <sh at tokkee.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "filter_chain.h"
+#include "utils_avltree.h"
+#include "utils_cache.h"
+#include "utils_complain.h"
+#include "utils_llist.h"
+#include "utils_heap.h"
+#include "utils_time.h"
+#include "utils_random.h"
+
+#include <ltdl.h>
+
+/*
+ * Private structures
+ */
+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;
+
+#define RF_SIMPLE  0
+#define RF_COMPLEX 1
+#define RF_REMOVE  65535
+struct read_func_s
+{
+       /* `read_func_t' "inherits" from `callback_func_t'.
+        * 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;
+       int rf_type;
+       cdtime_t rf_interval;
+       cdtime_t rf_effective_interval;
+       cdtime_t rf_next_read;
+};
+typedef struct read_func_s read_func_t;
+
+struct write_queue_s;
+typedef struct write_queue_s write_queue_t;
+struct write_queue_s
+{
+       value_list_t *vl;
+       plugin_ctx_t ctx;
+       write_queue_t *next;
+};
+
+/*
+ * Private variables
+ */
+static c_avl_tree_t *plugins_loaded = NULL;
+
+static llist_t *list_init;
+static llist_t *list_write;
+static llist_t *list_flush;
+static llist_t *list_missing;
+static llist_t *list_shutdown;
+static llist_t *list_log;
+static llist_t *list_notification;
+
+static fc_chain_t *pre_cache_chain = NULL;
+static fc_chain_t *post_cache_chain = NULL;
+
+static c_avl_tree_t *data_sets;
+
+static char *plugindir = NULL;
+
+#ifndef DEFAULT_MAX_READ_INTERVAL
+# define DEFAULT_MAX_READ_INTERVAL TIME_T_TO_CDTIME_T (86400)
+#endif
+static c_heap_t       *read_heap = NULL;
+static llist_t        *read_list;
+static int             read_loop = 1;
+static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  read_cond = PTHREAD_COND_INITIALIZER;
+static pthread_t      *read_threads = NULL;
+static int             read_threads_num = 0;
+static cdtime_t        max_read_interval = DEFAULT_MAX_READ_INTERVAL;
+
+static write_queue_t  *write_queue_head;
+static write_queue_t  *write_queue_tail;
+static long            write_queue_length = 0;
+static _Bool           write_loop = 1;
+static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  write_cond = PTHREAD_COND_INITIALIZER;
+static pthread_t      *write_threads = NULL;
+static size_t          write_threads_num = 0;
+
+static pthread_key_t   plugin_ctx_key;
+static _Bool           plugin_ctx_key_initialized = 0;
+
+static long            write_limit_high = 0;
+static long            write_limit_low = 0;
+
+static derive_t        stats_values_dropped = 0;
+static _Bool           record_statistics = 0;
+
+/*
+ * Static functions
+ */
+static int plugin_dispatch_values_internal (value_list_t *vl);
+
+static const char *plugin_get_dir (void)
+{
+       if (plugindir == NULL)
+               return (PLUGINDIR);
+       else
+               return (plugindir);
+}
+
+static void plugin_update_internal_statistics (void) { /* {{{ */
+       derive_t copy_write_queue_length;
+       value_list_t vl = VALUE_LIST_INIT;
+       value_t values[2];
+
+       copy_write_queue_length = write_queue_length;
+
+       /* Initialize `vl' */
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = 0;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "collectd", sizeof (vl.plugin));
+
+       vl.type_instance[0] = 0;
+       vl.values_len = 1;
+
+       /* Write queue */
+       sstrncpy (vl.plugin_instance, "write_queue",
+                       sizeof (vl.plugin_instance));
+
+       /* Write queue : queue length */
+       vl.values[0].gauge = (gauge_t) copy_write_queue_length;
+       sstrncpy (vl.type, "queue_length", sizeof (vl.type));
+       vl.type_instance[0] = 0;
+       plugin_dispatch_values (&vl);
+
+       /* Write queue : Values dropped (queue length > low limit) */
+       vl.values[0].derive = (derive_t) stats_values_dropped;
+       sstrncpy (vl.type, "derive", sizeof (vl.type));
+       sstrncpy (vl.type_instance, "dropped", sizeof (vl.type_instance));
+       plugin_dispatch_values (&vl);
+
+       /* Cache */
+       sstrncpy (vl.plugin_instance, "cache",
+                       sizeof (vl.plugin_instance));
+
+       /* Cache : Nb entry in cache tree */
+       vl.values[0].gauge = (gauge_t) uc_get_size();
+       sstrncpy (vl.type, "cache_size", sizeof (vl.type));
+       vl.type_instance[0] = 0;
+       plugin_dispatch_values (&vl);
+
+       return;
+} /* }}} void plugin_update_internal_statistics */
+
+static void destroy_callback (callback_func_t *cf) /* {{{ */
+{
+       if (cf == NULL)
+               return;
+
+       if ((cf->cf_udata.data != NULL) && (cf->cf_udata.free_func != NULL))
+       {
+               cf->cf_udata.free_func (cf->cf_udata.data);
+               cf->cf_udata.data = NULL;
+               cf->cf_udata.free_func = NULL;
+       }
+       sfree (cf);
+} /* }}} void destroy_callback */
+
+static void destroy_all_callbacks (llist_t **list) /* {{{ */
+{
+       llentry_t *le;
+
+       if (*list == NULL)
+               return;
+
+       le = llist_head (*list);
+       while (le != NULL)
+       {
+               llentry_t *le_next;
+
+               le_next = le->next;
+
+               sfree (le->key);
+               destroy_callback (le->value);
+               le->value = NULL;
+
+               le = le_next;
+       }
+
+       llist_destroy (*list);
+       *list = NULL;
+} /* }}} void destroy_all_callbacks */
+
+static void destroy_read_heap (void) /* {{{ */
+{
+       if (read_heap == NULL)
+               return;
+
+       while (42)
+       {
+               callback_func_t *cf;
+
+               cf = c_heap_get_root (read_heap);
+               if (cf == NULL)
+                       break;
+
+               destroy_callback (cf);
+       }
+
+       c_heap_destroy (read_heap);
+       read_heap = NULL;
+} /* }}} void destroy_read_heap */
+
+static int register_callback (llist_t **list, /* {{{ */
+               const char *name, callback_func_t *cf)
+{
+       llentry_t *le;
+       char *key;
+
+       if (*list == NULL)
+       {
+               *list = llist_create ();
+               if (*list == NULL)
+               {
+                       ERROR ("plugin: register_callback: "
+                                       "llist_create failed.");
+                       destroy_callback (cf);
+                       return (-1);
+               }
+       }
+
+       key = strdup (name);
+       if (key == NULL)
+       {
+               ERROR ("plugin: register_callback: strdup failed.");
+               destroy_callback (cf);
+               return (-1);
+       }
+
+       le = llist_search (*list, name);
+       if (le == NULL)
+       {
+               le = llentry_create (key, cf);
+               if (le == NULL)
+               {
+                       ERROR ("plugin: register_callback: "
+                                       "llentry_create failed.");
+                       free (key);
+                       destroy_callback (cf);
+                       return (-1);
+               }
+
+               llist_append (*list, le);
+       }
+       else
+       {
+               callback_func_t *old_cf;
+
+               old_cf = le->value;
+               le->value = cf;
+
+               WARNING ("plugin: register_callback: "
+                               "a callback named `%s' already exists - "
+                               "overwriting the old entry!", name);
+
+               destroy_callback (old_cf);
+               sfree (key);
+       }
+
+       return (0);
+} /* }}} int register_callback */
+
+static int create_register_callback (llist_t **list, /* {{{ */
+               const char *name, void *callback, user_data_t *ud)
+{
+       callback_func_t *cf;
+
+       cf = (callback_func_t *) malloc (sizeof (*cf));
+       if (cf == NULL)
+       {
+               ERROR ("plugin: create_register_callback: malloc failed.");
+               return (-1);
+       }
+       memset (cf, 0, sizeof (*cf));
+
+       cf->cf_callback = callback;
+       if (ud == NULL)
+       {
+               cf->cf_udata.data = NULL;
+               cf->cf_udata.free_func = NULL;
+       }
+       else
+       {
+               cf->cf_udata = *ud;
+       }
+
+       cf->cf_ctx = plugin_get_ctx ();
+
+       return (register_callback (list, name, cf));
+} /* }}} int create_register_callback */
+
+static int plugin_unregister (llist_t *list, const char *name) /* {{{ */
+{
+       llentry_t *e;
+
+       if (list == NULL)
+               return (-1);
+
+       e = llist_search (list, name);
+       if (e == NULL)
+               return (-1);
+
+       llist_remove (list, e);
+
+       sfree (e->key);
+       destroy_callback (e->value);
+
+       llentry_destroy (e);
+
+       return (0);
+} /* }}} int plugin_unregister */
+
+/*
+ * (Try to) load the shared object `file'. Won't complain if it isn't a shared
+ * object, but it will bitch about a shared object not having a
+ * ``module_register'' symbol..
+ */
+static int plugin_load_file (char *file, uint32_t flags)
+{
+       lt_dlhandle dlh;
+       void (*reg_handle) (void);
+
+       lt_dlinit ();
+       lt_dlerror (); /* clear errors */
+
+#if LIBTOOL_VERSION == 2
+       if (flags & PLUGIN_FLAGS_GLOBAL) {
+               lt_dladvise advise;
+               lt_dladvise_init(&advise);
+               lt_dladvise_global(&advise);
+               dlh = lt_dlopenadvise(file, advise);
+               lt_dladvise_destroy(&advise);
+       } else {
+               dlh = lt_dlopen (file);
+       }
+#else /* if LIBTOOL_VERSION == 1 */
+       if (flags & PLUGIN_FLAGS_GLOBAL)
+               WARNING ("plugin_load_file: The global flag is not supported, "
+                               "libtool 2 is required for this.");
+       dlh = lt_dlopen (file);
+#endif
+
+       if (dlh == NULL)
+       {
+               char errbuf[1024] = "";
+
+               ssnprintf (errbuf, sizeof (errbuf),
+                               "lt_dlopen (\"%s\") failed: %s. "
+                               "The most common cause for this problem are "
+                               "missing dependencies. Use ldd(1) to check "
+                               "the dependencies of the plugin "
+                               "/ shared object.",
+                               file, lt_dlerror ());
+
+               ERROR ("%s", errbuf);
+               /* Make sure this is printed to STDERR in any case, but also
+                * make sure it's printed only once. */
+               if (list_log != NULL)
+                       fprintf (stderr, "ERROR: %s\n", errbuf);
+
+               return (1);
+       }
+
+       if ((reg_handle = (void (*) (void)) lt_dlsym (dlh, "module_register")) == NULL)
+       {
+               WARNING ("Couldn't find symbol \"module_register\" in \"%s\": %s\n",
+                               file, lt_dlerror ());
+               lt_dlclose (dlh);
+               return (-1);
+       }
+
+       (*reg_handle) ();
+
+       return (0);
+}
+
+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.
+                * 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)
+               {
+                       pthread_cond_wait (&read_cond, &read_lock);
+                        pthread_mutex_unlock (&read_lock);
+                       continue;
+               }
+               pthread_mutex_unlock (&read_lock);
+
+               if (rf->rf_interval == 0)
+               {
+                       /* this should not happen, because the interval is set
+                        * for each plugin when loading it
+                        * XXX: issue a warning? */
+                       rf->rf_interval = plugin_get_interval ();
+                       rf->rf_effective_interval = rf->rf_interval;
+
+                       rf->rf_next_read = cdtime ();
+               }
+
+               /* sleep until this entry is due,
+                * using pthread_cond_timedwait */
+               pthread_mutex_lock (&read_lock);
+               /* In pthread_cond_timedwait, spurious wakeups are possible
+                * (and really happen, at least on NetBSD with > 1 CPU), thus
+                * we need to re-evaluate the condition every time
+                * pthread_cond_timedwait returns. */
+               rc = 0;
+               while ((read_loop != 0)
+                               && (cdtime () < rf->rf_next_read)
+                               && rc == 0)
+               {
+                       struct timespec ts = { 0 };
+
+                       CDTIME_T_TO_TIMESPEC (rf->rf_next_read, &ts);
+
+                       rc = pthread_cond_timedwait (&read_cond, &read_lock,
+                               &ts);
+               }
+
+               /* Must hold `read_lock' when accessing `rf->rf_type'. */
+               rf_type = rf->rf_type;
+               pthread_mutex_unlock (&read_lock);
+
+               /* Check if we're supposed to stop.. This may have interrupted
+                * the sleep, too. */
+               if (read_loop == 0)
+               {
+                       /* Insert `rf' again, so it can be free'd correctly */
+                       c_heap_insert (read_heap, rf);
+                       break;
+               }
+
+               /* The entry has been marked for deletion. The linked list
+                * entry has already been removed by `plugin_unregister_read'.
+                * All we have to do here is free the `read_func_t' and
+                * continue. */
+               if (rf_type == RF_REMOVE)
+               {
+                       DEBUG ("plugin_read_thread: Destroying the `%s' "
+                                       "callback.", rf->rf_name);
+                       sfree (rf->rf_name);
+                       destroy_callback ((callback_func_t *) rf);
+                       rf = NULL;
+                       continue;
+               }
+
+               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);
+
+                       callback = rf->rf_callback;
+                       status = (*callback) ();
+               }
+               else
+               {
+                       plugin_read_cb callback;
+
+                       assert (rf_type == RF_COMPLEX);
+
+                       callback = rf->rf_callback;
+                       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)
+               {
+                       rf->rf_effective_interval *= 2;
+                       if (rf->rf_effective_interval > max_read_interval)
+                               rf->rf_effective_interval = max_read_interval;
+
+                       NOTICE ("read-function of plugin `%s' failed. "
+                                       "Will suspend it for %.3f seconds.",
+                                       rf->rf_name,
+                                       CDTIME_T_TO_DOUBLE (rf->rf_effective_interval));
+               }
+               else
+               {
+                       /* Success: Restore the interval, if it was changed. */
+                       rf->rf_effective_interval = rf->rf_interval;
+               }
+
+               /* update the ``next read due'' field */
+               now = cdtime ();
+
+               DEBUG ("plugin_read_thread: Effective interval of the "
+                               "%s plugin is %.3f seconds.",
+                               rf->rf_name,
+                               CDTIME_T_TO_DOUBLE (rf->rf_effective_interval));
+
+               /* Calculate the next (absolute) time at which this function
+                * should be called. */
+               rf->rf_next_read += rf->rf_effective_interval;
+
+               /* Check, if `rf_next_read' is in the past. */
+               if (rf->rf_next_read < now)
+               {
+                       /* `rf_next_read' is in the past. Insert `now'
+                        * so this value doesn't trail off into the
+                        * past too much. */
+                       rf->rf_next_read = now;
+               }
+
+               DEBUG ("plugin_read_thread: Next read of the %s plugin at %.3f.",
+                               rf->rf_name,
+                               CDTIME_T_TO_DOUBLE (rf->rf_next_read));
+
+               /* Re-insert this read function into the heap again. */
+               c_heap_insert (read_heap, rf);
+       } /* while (read_loop) */
+
+       pthread_exit (NULL);
+       return ((void *) 0);
+} /* void *plugin_read_thread */
+
+static void start_read_threads (int num)
+{
+       int i;
+
+       if (read_threads != NULL)
+               return;
+
+       read_threads = (pthread_t *) calloc (num, sizeof (pthread_t));
+       if (read_threads == NULL)
+       {
+               ERROR ("plugin: start_read_threads: calloc failed.");
+               return;
+       }
+
+       read_threads_num = 0;
+       for (i = 0; i < num; i++)
+       {
+               if (pthread_create (read_threads + read_threads_num, NULL,
+                                       plugin_read_thread, NULL) == 0)
+               {
+                       read_threads_num++;
+               }
+               else
+               {
+                       ERROR ("plugin: start_read_threads: pthread_create failed.");
+                       return;
+               }
+       } /* for (i) */
+} /* void start_read_threads */
+
+static void stop_read_threads (void)
+{
+       int i;
+
+       if (read_threads == NULL)
+               return;
+
+       INFO ("collectd: Stopping %i read threads.", read_threads_num);
+
+       pthread_mutex_lock (&read_lock);
+       read_loop = 0;
+       DEBUG ("plugin: stop_read_threads: Signalling `read_cond'");
+       pthread_cond_broadcast (&read_cond);
+       pthread_mutex_unlock (&read_lock);
+
+       for (i = 0; i < read_threads_num; i++)
+       {
+               if (pthread_join (read_threads[i], NULL) != 0)
+               {
+                       ERROR ("plugin: stop_read_threads: pthread_join failed.");
+               }
+               read_threads[i] = (pthread_t) 0;
+       }
+       sfree (read_threads);
+       read_threads_num = 0;
+} /* void stop_read_threads */
+
+static void plugin_value_list_free (value_list_t *vl) /* {{{ */
+{
+       if (vl == NULL)
+               return;
+
+       meta_data_destroy (vl->meta);
+       sfree (vl->values);
+       sfree (vl);
+} /* }}} void plugin_value_list_free */
+
+static value_list_t *plugin_value_list_clone (value_list_t const *vl_orig) /* {{{ */
+{
+       value_list_t *vl;
+
+       if (vl_orig == NULL)
+               return (NULL);
+
+       vl = malloc (sizeof (*vl));
+       if (vl == NULL)
+               return (NULL);
+       memcpy (vl, vl_orig, sizeof (*vl));
+
+       vl->values = calloc (vl_orig->values_len, sizeof (*vl->values));
+       if (vl->values == NULL)
+       {
+               plugin_value_list_free (vl);
+               return (NULL);
+       }
+       memcpy (vl->values, vl_orig->values,
+                       vl_orig->values_len * sizeof (*vl->values));
+
+       vl->meta = meta_data_clone (vl->meta);
+       if ((vl_orig->meta != NULL) && (vl->meta == NULL))
+       {
+               plugin_value_list_free (vl);
+               return (NULL);
+       }
+
+       if (vl->time == 0)
+               vl->time = cdtime ();
+
+       /* Fill in the interval from the thread context, if it is zero. */
+       if (vl->interval == 0)
+       {
+               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_value_list_clone: 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 ();
+               }
+       }
+
+       return (vl);
+} /* }}} value_list_t *plugin_value_list_clone */
+
+static int plugin_write_enqueue (value_list_t const *vl) /* {{{ */
+{
+       write_queue_t *q;
+
+       q = malloc (sizeof (*q));
+       if (q == NULL)
+               return (ENOMEM);
+       q->next = NULL;
+
+       q->vl = plugin_value_list_clone (vl);
+       if (q->vl == NULL)
+       {
+               sfree (q);
+               return (ENOMEM);
+       }
+
+       /* Store context of caller (read plugin); otherwise, it would not be
+        * available to the write plugins when actually dispatching the
+        * value-list later on. */
+       q->ctx = plugin_get_ctx ();
+
+       pthread_mutex_lock (&write_lock);
+
+       if (write_queue_tail == NULL)
+       {
+               write_queue_head = q;
+               write_queue_tail = q;
+               write_queue_length = 1;
+       }
+       else
+       {
+               write_queue_tail->next = q;
+               write_queue_tail = q;
+               write_queue_length += 1;
+       }
+
+       pthread_cond_signal (&write_cond);
+       pthread_mutex_unlock (&write_lock);
+
+       return (0);
+} /* }}} int plugin_write_enqueue */
+
+static value_list_t *plugin_write_dequeue (void) /* {{{ */
+{
+       write_queue_t *q;
+       value_list_t *vl;
+
+       pthread_mutex_lock (&write_lock);
+
+       while (write_loop && (write_queue_head == NULL))
+               pthread_cond_wait (&write_cond, &write_lock);
+
+       if (write_queue_head == NULL)
+       {
+               pthread_mutex_unlock (&write_lock);
+               return (NULL);
+       }
+
+       q = write_queue_head;
+       write_queue_head = q->next;
+       write_queue_length -= 1;
+       if (write_queue_head == NULL) {
+               write_queue_tail = NULL;
+               assert(0 == write_queue_length);
+               }
+
+       pthread_mutex_unlock (&write_lock);
+
+       (void) plugin_set_ctx (q->ctx);
+
+       vl = q->vl;
+       sfree (q);
+       return (vl);
+} /* }}} value_list_t *plugin_write_dequeue */
+
+static void *plugin_write_thread (void __attribute__((unused)) *args) /* {{{ */
+{
+       while (write_loop)
+       {
+               value_list_t *vl = plugin_write_dequeue ();
+               if (vl == NULL)
+                       continue;
+
+               plugin_dispatch_values_internal (vl);
+
+               plugin_value_list_free (vl);
+       }
+
+       pthread_exit (NULL);
+       return ((void *) 0);
+} /* }}} void *plugin_write_thread */
+
+static void start_write_threads (size_t num) /* {{{ */
+{
+       size_t i;
+
+       if (write_threads != NULL)
+               return;
+
+       write_threads = (pthread_t *) calloc (num, sizeof (pthread_t));
+       if (write_threads == NULL)
+       {
+               ERROR ("plugin: start_write_threads: calloc failed.");
+               return;
+       }
+
+       write_threads_num = 0;
+       for (i = 0; i < num; i++)
+       {
+               int status;
+
+               status = pthread_create (write_threads + write_threads_num,
+                               /* attr = */ NULL,
+                               plugin_write_thread,
+                               /* arg = */ NULL);
+               if (status != 0)
+               {
+                       char errbuf[1024];
+                       ERROR ("plugin: start_write_threads: pthread_create failed "
+                                       "with status %i (%s).", status,
+                                       sstrerror (status, errbuf, sizeof (errbuf)));
+                       return;
+               }
+
+               write_threads_num++;
+       } /* for (i) */
+} /* }}} void start_write_threads */
+
+static void stop_write_threads (void) /* {{{ */
+{
+       write_queue_t *q;
+       int i;
+
+       if (write_threads == NULL)
+               return;
+
+       INFO ("collectd: Stopping %zu write threads.", write_threads_num);
+
+       pthread_mutex_lock (&write_lock);
+       write_loop = 0;
+       DEBUG ("plugin: stop_write_threads: Signalling `write_cond'");
+       pthread_cond_broadcast (&write_cond);
+       pthread_mutex_unlock (&write_lock);
+
+       for (i = 0; i < write_threads_num; i++)
+       {
+               if (pthread_join (write_threads[i], NULL) != 0)
+               {
+                       ERROR ("plugin: stop_write_threads: pthread_join failed.");
+               }
+               write_threads[i] = (pthread_t) 0;
+       }
+       sfree (write_threads);
+       write_threads_num = 0;
+
+       pthread_mutex_lock (&write_lock);
+       i = 0;
+       for (q = write_queue_head; q != NULL; )
+       {
+               write_queue_t *q1 = q;
+               plugin_value_list_free (q->vl);
+               q = q->next;
+               sfree (q1);
+               i++;
+       }
+       write_queue_head = NULL;
+       write_queue_tail = NULL;
+       write_queue_length = 0;
+       pthread_mutex_unlock (&write_lock);
+
+       if (i > 0)
+       {
+               WARNING ("plugin: %i value list%s left after shutting down "
+                               "the write threads.",
+                               i, (i == 1) ? " was" : "s were");
+       }
+} /* }}} void stop_write_threads */
+
+/*
+ * Public functions
+ */
+void plugin_set_dir (const char *dir)
+{
+       if (plugindir != NULL)
+               free (plugindir);
+
+       if (dir == NULL)
+               plugindir = NULL;
+       else if ((plugindir = strdup (dir)) == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("strdup failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
+}
+
+static _Bool plugin_is_loaded (char const *name)
+{
+       int status;
+
+       if (plugins_loaded == NULL)
+               plugins_loaded = c_avl_create ((void *) strcasecmp);
+       assert (plugins_loaded != NULL);
+
+       status = c_avl_get (plugins_loaded, name, /* ret_value = */ NULL);
+       return (status == 0);
+}
+
+static int plugin_mark_loaded (char const *name)
+{
+       char *name_copy;
+       int status;
+
+       name_copy = strdup (name);
+       if (name_copy == NULL)
+               return (ENOMEM);
+
+       status = c_avl_insert (plugins_loaded,
+                       /* key = */ name_copy, /* value = */ NULL);
+       return (status);
+}
+
+static void plugin_free_loaded ()
+{
+       void *key;
+       void *value;
+
+       if (plugins_loaded == NULL)
+               return;
+
+       while (c_avl_pick (plugins_loaded, &key, &value) == 0)
+       {
+               sfree (key);
+               assert (value == NULL);
+       }
+
+       c_avl_destroy (plugins_loaded);
+       plugins_loaded = NULL;
+}
+
+#define BUFSIZE 512
+int plugin_load (char const *plugin_name, uint32_t flags)
+{
+       DIR  *dh;
+       const char *dir;
+       char  filename[BUFSIZE] = "";
+       char  typename[BUFSIZE];
+       int   typename_len;
+       int   ret;
+       struct stat    statbuf;
+       struct dirent *de;
+       int status;
+
+       if (plugin_name == NULL)
+               return (EINVAL);
+
+       /* Check if plugin is already loaded and don't do anything in this
+        * case. */
+       if (plugin_is_loaded (plugin_name))
+               return (0);
+
+       dir = plugin_get_dir ();
+       ret = 1;
+
+       /*
+        * XXX: Magic at work:
+        *
+        * Some of the language bindings, for example the Python and Perl
+        * plugins, need to be able to export symbols to the scripts they run.
+        * For this to happen, the "Globals" flag needs to be set.
+        * Unfortunately, this technical detail is hard to explain to the
+        * average user and she shouldn't have to worry about this, ideally.
+        * So in order to save everyone's sanity use a different default for a
+        * handful of special plugins. --octo
+        */
+       if ((strcasecmp ("perl", plugin_name) == 0)
+                       || (strcasecmp ("python", plugin_name) == 0))
+               flags |= PLUGIN_FLAGS_GLOBAL;
+
+       /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
+        * type when matching the filename */
+       status = ssnprintf (typename, sizeof (typename), "%s.so", plugin_name);
+       if ((status < 0) || ((size_t) status >= sizeof (typename)))
+       {
+               WARNING ("plugin_load: Filename too long: \"%s.so\"", plugin_name);
+               return (-1);
+       }
+       typename_len = strlen (typename);
+
+       if ((dh = opendir (dir)) == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("plugin_load: opendir (%s) failed: %s", dir,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       while ((de = readdir (dh)) != NULL)
+       {
+               if (strncasecmp (de->d_name, typename, typename_len))
+                       continue;
+
+               status = ssnprintf (filename, sizeof (filename),
+                               "%s/%s", dir, de->d_name);
+               if ((status < 0) || ((size_t) status >= sizeof (filename)))
+               {
+                       WARNING ("plugin_load: Filename too long: \"%s/%s\"",
+                                       dir, de->d_name);
+                       continue;
+               }
+
+               if (lstat (filename, &statbuf) == -1)
+               {
+                       char errbuf[1024];
+                       WARNING ("plugin_load: stat (\"%s\") failed: %s",
+                                       filename,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       continue;
+               }
+               else if (!S_ISREG (statbuf.st_mode))
+               {
+                       /* don't follow symlinks */
+                       WARNING ("plugin_load: %s is not a regular file.",
+                                       filename);
+                       continue;
+               }
+
+               status = plugin_load_file (filename, flags);
+               if (status == 0)
+               {
+                       /* success */
+                       plugin_mark_loaded (plugin_name);
+                       ret = 0;
+                       break;
+               }
+               else
+               {
+                       ERROR ("plugin_load: Load plugin \"%s\" failed with "
+                                       "status %i.", plugin_name, status);
+               }
+       }
+
+       closedir (dh);
+
+       if (filename[0] == 0)
+               ERROR ("plugin_load: Could not find plugin \"%s\" in %s",
+                               plugin_name, dir);
+
+       return (ret);
+}
+
+/*
+ * The `register_*' functions follow
+ */
+int plugin_register_config (const char *name,
+               int (*callback) (const char *key, const char *val),
+               const char **keys, int keys_num)
+{
+       cf_register (name, callback, keys, keys_num);
+       return (0);
+} /* int plugin_register_config */
+
+int plugin_register_complex_config (const char *type,
+               int (*callback) (oconfig_item_t *))
+{
+       return (cf_register_complex (type, callback));
+} /* int plugin_register_complex_config */
+
+int plugin_register_init (const char *name,
+               int (*callback) (void))
+{
+       return (create_register_callback (&list_init, name, (void *) callback,
+                               /* user_data = */ NULL));
+} /* plugin_register_init */
+
+static int plugin_compare_read_func (const void *arg0, const void *arg1)
+{
+       const read_func_t *rf0;
+       const read_func_t *rf1;
+
+       rf0 = arg0;
+       rf1 = arg1;
+
+       if (rf0->rf_next_read < rf1->rf_next_read)
+               return (-1);
+       else if (rf0->rf_next_read > rf1->rf_next_read)
+               return (1);
+       else
+               return (0);
+} /* int plugin_compare_read_func */
+
+/* Add a read function to both, the heap and a linked list. The linked list if
+ * used to look-up read functions, especially for the remove function. The heap
+ * is used to determine which plugin to read next. */
+static int plugin_insert_read (read_func_t *rf)
+{
+       int status;
+       llentry_t *le;
+
+       rf->rf_next_read = cdtime ();
+       rf->rf_effective_interval = rf->rf_interval;
+
+       pthread_mutex_lock (&read_lock);
+
+       if (read_list == NULL)
+       {
+               read_list = llist_create ();
+               if (read_list == NULL)
+               {
+                       pthread_mutex_unlock (&read_lock);
+                       ERROR ("plugin_insert_read: read_list failed.");
+                       return (-1);
+               }
+       }
+
+       if (read_heap == NULL)
+       {
+               read_heap = c_heap_create (plugin_compare_read_func);
+               if (read_heap == NULL)
+               {
+                       pthread_mutex_unlock (&read_lock);
+                       ERROR ("plugin_insert_read: c_heap_create failed.");
+                       return (-1);
+               }
+       }
+
+       le = llist_search (read_list, rf->rf_name);
+       if (le != NULL)
+       {
+               pthread_mutex_unlock (&read_lock);
+               WARNING ("The read function \"%s\" is already registered. "
+                               "Check for duplicate \"LoadPlugin\" lines "
+                               "in your configuration!",
+                               rf->rf_name);
+               return (EINVAL);
+       }
+
+       le = llentry_create (rf->rf_name, rf);
+       if (le == NULL)
+       {
+               pthread_mutex_unlock (&read_lock);
+               ERROR ("plugin_insert_read: llentry_create failed.");
+               return (-1);
+       }
+
+       status = c_heap_insert (read_heap, rf);
+       if (status != 0)
+       {
+               pthread_mutex_unlock (&read_lock);
+               ERROR ("plugin_insert_read: c_heap_insert failed.");
+               llentry_destroy (le);
+               return (-1);
+       }
+
+       /* 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 */
+
+int plugin_register_read (const char *name,
+               int (*callback) (void))
+{
+       read_func_t *rf;
+       int status;
+
+       rf = malloc (sizeof (*rf));
+       if (rf == NULL)
+       {
+               ERROR ("plugin_register_read: malloc failed.");
+               return (ENOMEM);
+       }
+
+       memset (rf, 0, sizeof (read_func_t));
+       rf->rf_callback = (void *) callback;
+       rf->rf_udata.data = NULL;
+       rf->rf_udata.free_func = NULL;
+       rf->rf_ctx = plugin_get_ctx ();
+       rf->rf_group[0] = '\0';
+       rf->rf_name = strdup (name);
+       rf->rf_type = RF_SIMPLE;
+       rf->rf_interval = plugin_get_interval ();
+
+       status = plugin_insert_read (rf);
+       if (status != 0)
+               sfree (rf);
+
+       return (status);
+} /* int plugin_register_read */
+
+int plugin_register_complex_read (const char *group, const char *name,
+               plugin_read_cb callback,
+               const struct timespec *interval,
+               user_data_t *user_data)
+{
+       read_func_t *rf;
+       int status;
+
+       rf = malloc (sizeof (*rf));
+       if (rf == NULL)
+       {
+               ERROR ("plugin_register_complex_read: malloc failed.");
+               return (ENOMEM);
+       }
+
+       memset (rf, 0, sizeof (read_func_t));
+       rf->rf_callback = (void *) callback;
+       if (group != NULL)
+               sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
+       else
+               rf->rf_group[0] = '\0';
+       rf->rf_name = strdup (name);
+       rf->rf_type = RF_COMPLEX;
+       if (interval != NULL)
+               rf->rf_interval = TIMESPEC_TO_CDTIME_T (interval);
+       else
+               rf->rf_interval = plugin_get_interval ();
+
+       /* Set user data */
+       if (user_data == NULL)
+       {
+               rf->rf_udata.data = NULL;
+               rf->rf_udata.free_func = NULL;
+       }
+       else
+       {
+               rf->rf_udata = *user_data;
+       }
+
+       rf->rf_ctx = plugin_get_ctx ();
+
+       status = plugin_insert_read (rf);
+       if (status != 0)
+               sfree (rf);
+
+       return (status);
+} /* int plugin_register_complex_read */
+
+int plugin_register_write (const char *name,
+               plugin_write_cb callback, user_data_t *ud)
+{
+       return (create_register_callback (&list_write, name,
+                               (void *) callback, ud));
+} /* int plugin_register_write */
+
+int plugin_register_flush (const char *name,
+               plugin_flush_cb callback, user_data_t *ud)
+{
+       return (create_register_callback (&list_flush, name,
+                               (void *) callback, ud));
+} /* int plugin_register_flush */
+
+int plugin_register_missing (const char *name,
+               plugin_missing_cb callback, user_data_t *ud)
+{
+       return (create_register_callback (&list_missing, name,
+                               (void *) callback, ud));
+} /* int plugin_register_missing */
+
+int plugin_register_shutdown (const char *name,
+               int (*callback) (void))
+{
+       return (create_register_callback (&list_shutdown, name,
+                               (void *) callback, /* user_data = */ NULL));
+} /* int plugin_register_shutdown */
+
+static void plugin_free_data_sets (void)
+{
+       void *key;
+       void *value;
+
+       if (data_sets == NULL)
+               return;
+
+       while (c_avl_pick (data_sets, &key, &value) == 0)
+       {
+               data_set_t *ds = value;
+               /* key is a pointer to ds->type */
+
+               sfree (ds->ds);
+               sfree (ds);
+       }
+
+       c_avl_destroy (data_sets);
+       data_sets = NULL;
+} /* void plugin_free_data_sets */
+
+int plugin_register_data_set (const data_set_t *ds)
+{
+       data_set_t *ds_copy;
+       int i;
+
+       if ((data_sets != NULL)
+                       && (c_avl_get (data_sets, ds->type, NULL) == 0))
+       {
+               NOTICE ("Replacing DS `%s' with another version.", ds->type);
+               plugin_unregister_data_set (ds->type);
+       }
+       else if (data_sets == NULL)
+       {
+               data_sets = c_avl_create ((int (*) (const void *, const void *)) strcmp);
+               if (data_sets == NULL)
+                       return (-1);
+       }
+
+       ds_copy = (data_set_t *) malloc (sizeof (data_set_t));
+       if (ds_copy == NULL)
+               return (-1);
+       memcpy(ds_copy, ds, sizeof (data_set_t));
+
+       ds_copy->ds = (data_source_t *) malloc (sizeof (data_source_t)
+                       * ds->ds_num);
+       if (ds_copy->ds == NULL)
+       {
+               free (ds_copy);
+               return (-1);
+       }
+
+       for (i = 0; i < ds->ds_num; i++)
+               memcpy (ds_copy->ds + i, ds->ds + i, sizeof (data_source_t));
+
+       return (c_avl_insert (data_sets, (void *) ds_copy->type, (void *) ds_copy));
+} /* int plugin_register_data_set */
+
+int plugin_register_log (const char *name,
+               plugin_log_cb callback, user_data_t *ud)
+{
+       return (create_register_callback (&list_log, name,
+                               (void *) callback, ud));
+} /* int plugin_register_log */
+
+int plugin_register_notification (const char *name,
+               plugin_notification_cb callback, user_data_t *ud)
+{
+       return (create_register_callback (&list_notification, name,
+                               (void *) callback, ud));
+} /* int plugin_register_log */
+
+int plugin_unregister_config (const char *name)
+{
+       cf_unregister (name);
+       return (0);
+} /* int plugin_unregister_config */
+
+int plugin_unregister_complex_config (const char *name)
+{
+       cf_unregister_complex (name);
+       return (0);
+} /* int plugin_unregister_complex_config */
+
+int plugin_unregister_init (const char *name)
+{
+       return (plugin_unregister (list_init, name));
+}
+
+int plugin_unregister_read (const char *name) /* {{{ */
+{
+       llentry_t *le;
+       read_func_t *rf;
+
+       if (name == NULL)
+               return (-ENOENT);
+
+       pthread_mutex_lock (&read_lock);
+
+       if (read_list == NULL)
+       {
+               pthread_mutex_unlock (&read_lock);
+               return (-ENOENT);
+       }
+
+       le = llist_search (read_list, name);
+       if (le == NULL)
+       {
+               pthread_mutex_unlock (&read_lock);
+               WARNING ("plugin_unregister_read: No such read function: %s",
+                               name);
+               return (-ENOENT);
+       }
+
+       llist_remove (read_list, le);
+
+       rf = le->value;
+       assert (rf != NULL);
+       rf->rf_type = RF_REMOVE;
+
+       pthread_mutex_unlock (&read_lock);
+
+       llentry_destroy (le);
+
+       DEBUG ("plugin_unregister_read: Marked `%s' for removal.", name);
+
+       return (0);
+} /* }}} int plugin_unregister_read */
+
+static int compare_read_func_group (llentry_t *e, void *ud) /* {{{ */
+{
+       read_func_t *rf    = e->value;
+       char        *group = ud;
+
+       return strcmp (rf->rf_group, (const char *)group);
+} /* }}} int compare_read_func_group */
+
+int plugin_unregister_read_group (const char *group) /* {{{ */
+{
+       llentry_t *le;
+       read_func_t *rf;
+
+       int found = 0;
+
+       if (group == NULL)
+               return (-ENOENT);
+
+       pthread_mutex_lock (&read_lock);
+
+       if (read_list == NULL)
+       {
+               pthread_mutex_unlock (&read_lock);
+               return (-ENOENT);
+       }
+
+       while (42)
+       {
+               le = llist_search_custom (read_list,
+                               compare_read_func_group, (void *)group);
+
+               if (le == NULL)
+                       break;
+
+               ++found;
+
+               llist_remove (read_list, le);
+
+               rf = le->value;
+               assert (rf != NULL);
+               rf->rf_type = RF_REMOVE;
+
+               llentry_destroy (le);
+
+               DEBUG ("plugin_unregister_read_group: "
+                               "Marked `%s' (group `%s') for removal.",
+                               rf->rf_name, group);
+       }
+
+       pthread_mutex_unlock (&read_lock);
+
+       if (found == 0)
+       {
+               WARNING ("plugin_unregister_read_group: No such "
+                               "group of read function: %s", group);
+               return (-ENOENT);
+       }
+
+       return (0);
+} /* }}} int plugin_unregister_read_group */
+
+int plugin_unregister_write (const char *name)
+{
+       return (plugin_unregister (list_write, name));
+}
+
+int plugin_unregister_flush (const char *name)
+{
+       return (plugin_unregister (list_flush, name));
+}
+
+int plugin_unregister_missing (const char *name)
+{
+       return (plugin_unregister (list_missing, name));
+}
+
+int plugin_unregister_shutdown (const char *name)
+{
+       return (plugin_unregister (list_shutdown, name));
+}
+
+int plugin_unregister_data_set (const char *name)
+{
+       data_set_t *ds;
+
+       if (data_sets == NULL)
+               return (-1);
+
+       if (c_avl_remove (data_sets, name, NULL, (void *) &ds) != 0)
+               return (-1);
+
+       sfree (ds->ds);
+       sfree (ds);
+
+       return (0);
+} /* int plugin_unregister_data_set */
+
+int plugin_unregister_log (const char *name)
+{
+       return (plugin_unregister (list_log, name));
+}
+
+int plugin_unregister_notification (const char *name)
+{
+       return (plugin_unregister (list_notification, name));
+}
+
+void plugin_init_all (void)
+{
+       char const *chain_name;
+       long write_threads_num;
+       llentry_t *le;
+       int status;
+
+       /* Init the value cache */
+       uc_init ();
+
+       if (IS_TRUE (global_option_get ("CollectInternalStats")))
+               record_statistics = 1;
+
+       chain_name = global_option_get ("PreCacheChain");
+       pre_cache_chain = fc_chain_get_by_name (chain_name);
+
+       chain_name = global_option_get ("PostCacheChain");
+       post_cache_chain = fc_chain_get_by_name (chain_name);
+
+       write_limit_high = global_option_get_long ("WriteQueueLimitHigh",
+                       /* default = */ 0);
+       if (write_limit_high < 0)
+       {
+               ERROR ("WriteQueueLimitHigh must be positive or zero.");
+               write_limit_high = 0;
+       }
+
+       write_limit_low = global_option_get_long ("WriteQueueLimitLow",
+                       /* default = */ write_limit_high / 2);
+       if (write_limit_low < 0)
+       {
+               ERROR ("WriteQueueLimitLow must be positive or zero.");
+               write_limit_low = write_limit_high / 2;
+       }
+       else if (write_limit_low > write_limit_high)
+       {
+               ERROR ("WriteQueueLimitLow must not be larger than "
+                               "WriteQueueLimitHigh.");
+               write_limit_low = write_limit_high;
+       }
+
+       write_threads_num = global_option_get_long ("WriteThreads",
+                       /* default = */ 5);
+       if (write_threads_num < 1)
+       {
+               ERROR ("WriteThreads must be positive.");
+               write_threads_num = 5;
+       }
+
+       start_write_threads ((size_t) write_threads_num);
+
+       if ((list_init == NULL) && (read_heap == NULL))
+               return;
+
+       /* Calling all init callbacks before checking if read callbacks
+        * are available allows the init callbacks to register the read
+        * callback. */
+       le = llist_head (list_init);
+       while (le != NULL)
+       {
+               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)
+               {
+                       ERROR ("Initialization of plugin `%s' "
+                                       "failed with status %i. "
+                                       "Plugin will be unloaded.",
+                                       le->key, status);
+                       /* Plugins that register read callbacks from the init
+                        * callback should take care of appropriate error
+                        * handling themselves. */
+                       /* FIXME: Unload _all_ functions */
+                       plugin_unregister_read (le->key);
+               }
+
+               le = le->next;
+       }
+
+       max_read_interval = global_option_get_time ("MaxReadInterval",
+                       DEFAULT_MAX_READ_INTERVAL);
+
+       /* Start read-threads */
+       if (read_heap != NULL)
+       {
+               const char *rt;
+               int num;
+
+               rt = global_option_get ("ReadThreads");
+               num = atoi (rt);
+               if (num != -1)
+                       start_read_threads ((num > 0) ? num : 5);
+       }
+} /* void plugin_init_all */
+
+/* TODO: Rename this function. */
+void plugin_read_all (void)
+{
+       if(record_statistics) {
+               plugin_update_internal_statistics ();
+       }
+       uc_check_timeout ();
+
+       return;
+} /* void plugin_read_all */
+
+/* Read function called when the `-T' command line argument is given. */
+int plugin_read_all_once (void)
+{
+       int status;
+       int return_status = 0;
+
+       if (read_heap == NULL)
+       {
+               NOTICE ("No read-functions are registered.");
+               return (0);
+       }
+
+       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);
+
+                       callback = rf->rf_callback;
+                       status = (*callback) ();
+               }
+               else
+               {
+                       plugin_read_cb callback;
+
+                       callback = rf->rf_callback;
+                       status = (*callback) (&rf->rf_udata);
+               }
+
+               plugin_set_ctx (old_ctx);
+
+               if (status != 0)
+               {
+                       NOTICE ("read-function of plugin `%s' failed.",
+                                       rf->rf_name);
+                       return_status = -1;
+               }
+
+               destroy_callback ((void *) rf);
+       }
+
+       return (return_status);
+} /* int plugin_read_all_once */
+
+int plugin_write (const char *plugin, /* {{{ */
+               const data_set_t *ds, const value_list_t *vl)
+{
+  llentry_t *le;
+  int status;
+
+  if (vl == NULL)
+    return (EINVAL);
+
+  if (list_write == NULL)
+    return (ENOENT);
+
+  if (ds == NULL)
+  {
+    ds = plugin_get_ds (vl->type);
+    if (ds == NULL)
+    {
+      ERROR ("plugin_write: Unable to lookup type `%s'.", vl->type);
+      return (ENOENT);
+    }
+  }
+
+  if (plugin == NULL)
+  {
+    int success = 0;
+    int failure = 0;
+
+    le = llist_head (list_write);
+    while (le != NULL)
+    {
+      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);
+      if (status != 0)
+        failure++;
+      else
+        success++;
+
+      le = le->next;
+    }
+
+    if ((success == 0) && (failure != 0))
+      status = -1;
+    else
+      status = 0;
+  }
+  else /* plugin != NULL */
+  {
+    callback_func_t *cf;
+    plugin_write_cb callback;
+
+    le = llist_head (list_write);
+    while (le != NULL)
+    {
+      if (strcasecmp (plugin, le->key) == 0)
+        break;
+
+      le = le->next;
+    }
+
+    if (le == NULL)
+      return (ENOENT);
+
+    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);
+  }
+
+  return (status);
+} /* }}} int plugin_write */
+
+int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier)
+{
+  llentry_t *le;
+
+  if (list_flush == NULL)
+    return (0);
+
+  le = llist_head (list_flush);
+  while (le != NULL)
+  {
+    callback_func_t *cf;
+    plugin_flush_cb callback;
+    plugin_ctx_t old_ctx;
+
+    if ((plugin != NULL)
+        && (strcmp (plugin, le->key) != 0))
+    {
+      le = le->next;
+      continue;
+    }
+
+    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);
+} /* int plugin_flush */
+
+void plugin_shutdown_all (void)
+{
+       llentry_t *le;
+
+       stop_read_threads ();
+
+       destroy_all_callbacks (&list_init);
+
+       pthread_mutex_lock (&read_lock);
+       llist_destroy (read_list);
+       read_list = NULL;
+       pthread_mutex_unlock (&read_lock);
+
+       destroy_read_heap ();
+
+       plugin_flush (/* plugin = */ NULL,
+                       /* timeout = */ 0,
+                       /* identifier = */ NULL);
+
+       le = NULL;
+       if (list_shutdown != NULL)
+               le = llist_head (list_shutdown);
+
+       while (le != NULL)
+       {
+               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
+                * shutdown functions to unregister themselves. If done the
+                * other way around the memory `le' points to will be freed
+                * after callback returns. */
+               le = le->next;
+
+               (*callback) ();
+
+               plugin_set_ctx (old_ctx);
+       }
+
+       stop_write_threads ();
+
+       /* Write plugins which use the `user_data' pointer usually need the
+        * same data available to the flush callback. If this is the case, set
+        * the free_function to NULL when registering the flush callback and to
+        * the real free function when registering the write callback. This way
+        * the data isn't freed twice. */
+       destroy_all_callbacks (&list_flush);
+       destroy_all_callbacks (&list_missing);
+       destroy_all_callbacks (&list_write);
+
+       destroy_all_callbacks (&list_notification);
+       destroy_all_callbacks (&list_shutdown);
+       destroy_all_callbacks (&list_log);
+
+       plugin_free_loaded ();
+       plugin_free_data_sets ();
+} /* void plugin_shutdown_all */
+
+int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
+{
+  llentry_t *le;
+
+  if (list_missing == NULL)
+    return (0);
+
+  le = llist_head (list_missing);
+  while (le != NULL)
+  {
+    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)
+      {
+        ERROR ("plugin_dispatch_missing: Callback function \"%s\" "
+            "failed with status %i.",
+            le->key, status);
+        return (status);
+      }
+      else
+      {
+        return (0);
+      }
+    }
+
+    le = le->next;
+  }
+  return (0);
+} /* int }}} plugin_dispatch_missing */
+
+static int plugin_dispatch_values_internal (value_list_t *vl)
+{
+       int status;
+       static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC;
+
+       value_t *saved_values;
+       int      saved_values_len;
+
+       data_set_t *ds;
+
+       int free_meta_data = 0;
+
+       if ((vl == NULL) || (vl->type[0] == 0)
+                       || (vl->values == NULL) || (vl->values_len < 1))
+       {
+               ERROR ("plugin_dispatch_values: Invalid value list "
+                               "from plugin %s.", vl->plugin);
+               return (-1);
+       }
+
+       /* Free meta data only if the calling function didn't specify any. In
+        * this case matches and targets may add some and the calling function
+        * may not expect (and therefore free) that data. */
+       if (vl->meta == NULL)
+               free_meta_data = 1;
+
+       if (list_write == NULL)
+               c_complain_once (LOG_WARNING, &no_write_complaint,
+                               "plugin_dispatch_values: No write callback has been "
+                               "registered. Please load at least one output plugin, "
+                               "if you want the collected data to be stored.");
+
+       if (data_sets == NULL)
+       {
+               ERROR ("plugin_dispatch_values: No data sets registered. "
+                               "Could the types database be read? Check "
+                               "your `TypesDB' setting!");
+               return (-1);
+       }
+
+       if (c_avl_get (data_sets, vl->type, (void *) &ds) != 0)
+       {
+               char ident[6 * DATA_MAX_NAME_LEN];
+
+               FORMAT_VL (ident, sizeof (ident), vl);
+               INFO ("plugin_dispatch_values: Dataset not found: %s "
+                               "(from \"%s\"), check your types.db!",
+                               vl->type, ident);
+               return (-1);
+       }
+
+       /* Assured by plugin_value_list_clone(). The time is determined at
+        * _enqueue_ time. */
+       assert (vl->time != 0);
+       assert (vl->interval != 0);
+
+       DEBUG ("plugin_dispatch_values: time = %.3f; interval = %.3f; "
+                       "host = %s; "
+                       "plugin = %s; plugin_instance = %s; "
+                       "type = %s; type_instance = %s;",
+                       CDTIME_T_TO_DOUBLE (vl->time),
+                       CDTIME_T_TO_DOUBLE (vl->interval),
+                       vl->host,
+                       vl->plugin, vl->plugin_instance,
+                       vl->type, vl->type_instance);
+
+#if COLLECT_DEBUG
+       assert (0 == strcmp (ds->type, vl->type));
+#else
+       if (0 != strcmp (ds->type, vl->type))
+               WARNING ("plugin_dispatch_values: (ds->type = %s) != (vl->type = %s)",
+                               ds->type, vl->type);
+#endif
+
+#if COLLECT_DEBUG
+       assert (ds->ds_num == vl->values_len);
+#else
+       if (ds->ds_num != vl->values_len)
+       {
+               ERROR ("plugin_dispatch_values: ds->type = %s: "
+                               "(ds->ds_num = %i) != "
+                               "(vl->values_len = %i)",
+                               ds->type, ds->ds_num, vl->values_len);
+               return (-1);
+       }
+#endif
+
+       escape_slashes (vl->host, sizeof (vl->host));
+       escape_slashes (vl->plugin, sizeof (vl->plugin));
+       escape_slashes (vl->plugin_instance, sizeof (vl->plugin_instance));
+       escape_slashes (vl->type, sizeof (vl->type));
+       escape_slashes (vl->type_instance, sizeof (vl->type_instance));
+
+       /* Copy the values. This way, we can assure `targets' that they get
+        * dynamically allocated values, which they can free and replace if
+        * they like. */
+       if ((pre_cache_chain != NULL) || (post_cache_chain != NULL))
+       {
+               saved_values     = vl->values;
+               saved_values_len = vl->values_len;
+
+               vl->values = (value_t *) calloc (vl->values_len,
+                               sizeof (*vl->values));
+               if (vl->values == NULL)
+               {
+                       ERROR ("plugin_dispatch_values: calloc failed.");
+                       vl->values = saved_values;
+                       return (-1);
+               }
+               memcpy (vl->values, saved_values,
+                               vl->values_len * sizeof (*vl->values));
+       }
+       else /* if ((pre == NULL) && (post == NULL)) */
+       {
+               saved_values     = NULL;
+               saved_values_len = 0;
+       }
+
+       if (pre_cache_chain != NULL)
+       {
+               status = fc_process_chain (ds, vl, pre_cache_chain);
+               if (status < 0)
+               {
+                       WARNING ("plugin_dispatch_values: Running the "
+                                       "pre-cache chain failed with "
+                                       "status %i (%#x).",
+                                       status, status);
+               }
+               else if (status == FC_TARGET_STOP)
+               {
+                       /* Restore the state of the value_list so that plugins
+                        * don't get confused.. */
+                       if (saved_values != NULL)
+                       {
+                               free (vl->values);
+                               vl->values     = saved_values;
+                               vl->values_len = saved_values_len;
+                       }
+                       return (0);
+               }
+       }
+
+       /* Update the value cache */
+       uc_update (ds, vl);
+
+       if (post_cache_chain != NULL)
+       {
+               status = fc_process_chain (ds, vl, post_cache_chain);
+               if (status < 0)
+               {
+                       WARNING ("plugin_dispatch_values: Running the "
+                                       "post-cache chain failed with "
+                                       "status %i (%#x).",
+                                       status, status);
+               }
+       }
+       else
+               fc_default_action (ds, vl);
+
+       /* Restore the state of the value_list so that plugins don't get
+        * confused.. */
+       if (saved_values != NULL)
+       {
+               free (vl->values);
+               vl->values     = saved_values;
+               vl->values_len = saved_values_len;
+       }
+
+       if ((free_meta_data != 0) && (vl->meta != NULL))
+       {
+               meta_data_destroy (vl->meta);
+               vl->meta = NULL;
+       }
+
+       return (0);
+} /* int plugin_dispatch_values_internal */
+
+static double get_drop_probability (void) /* {{{ */
+{
+       long pos;
+       long size;
+       long wql;
+
+       pthread_mutex_lock (&write_lock);
+       wql = write_queue_length;
+       pthread_mutex_unlock (&write_lock);
+
+       if (wql < write_limit_low)
+               return (0.0);
+       if (wql >= write_limit_high)
+               return (1.0);
+
+       pos = 1 + wql - write_limit_low;
+       size = 1 + write_limit_high - write_limit_low;
+
+       return (((double) pos) / ((double) size));
+} /* }}} double get_drop_probability */
+
+static _Bool check_drop_value (void) /* {{{ */
+{
+       static cdtime_t last_message_time = 0;
+       static pthread_mutex_t last_message_lock = PTHREAD_MUTEX_INITIALIZER;
+
+       double p;
+       double q;
+       int status;
+
+       if (write_limit_high == 0)
+               return (0);
+
+       p = get_drop_probability ();
+       if (p == 0.0)
+               return (0);
+
+       status = pthread_mutex_trylock (&last_message_lock);
+       if (status == 0)
+       {
+               cdtime_t now;
+
+               now = cdtime ();
+               if ((now - last_message_time) > TIME_T_TO_CDTIME_T (1))
+               {
+                       last_message_time = now;
+                       ERROR ("plugin_dispatch_values: Low water mark "
+                                       "reached. Dropping %.0f%% of metrics.",
+                                       100.0 * p);
+               }
+               pthread_mutex_unlock (&last_message_lock);
+       }
+
+       if (p == 1.0)
+               return (1);
+
+       q = cdrand_d ();
+       if (q > p)
+               return (1);
+       else
+               return (0);
+} /* }}} _Bool check_drop_value */
+
+int plugin_dispatch_values (value_list_t const *vl)
+{
+       int status;
+       static pthread_mutex_t statistics_lock = PTHREAD_MUTEX_INITIALIZER;
+
+       if (check_drop_value ()) {
+               if(record_statistics) {
+                       pthread_mutex_lock(&statistics_lock);
+                       stats_values_dropped++;
+                       pthread_mutex_unlock(&statistics_lock);
+               }
+               return (0);
+       }
+
+       status = plugin_write_enqueue (vl);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("plugin_dispatch_values: plugin_write_enqueue failed "
+                               "with status %i (%s).", status,
+                               sstrerror (status, errbuf, sizeof (errbuf)));
+               return (status);
+       }
+
+       return (0);
+}
+
+__attribute__((sentinel))
+int plugin_dispatch_multivalue (value_list_t const *template, /* {{{ */
+               _Bool store_percentage, ...)
+{
+       value_list_t *vl;
+       int failed = 0;
+       gauge_t sum = 0.0;
+       va_list ap;
+
+       assert (template->values_len == 1);
+
+       va_start (ap, store_percentage);
+       while (42)
+       {
+               char const *name;
+               gauge_t value;
+
+               name = va_arg (ap, char const *);
+               if (name == NULL)
+                       break;
+
+               value = va_arg (ap, gauge_t);
+               if (!isnan (value))
+                       sum += value;
+       }
+       va_end (ap);
+
+       vl = plugin_value_list_clone (template);
+       /* plugin_value_list_clone makes sure vl->time is set to non-zero. */
+       if (store_percentage)
+               sstrncpy (vl->type, "percent", sizeof (vl->type));
+
+       va_start (ap, store_percentage);
+       while (42)
+       {
+               char const *name;
+               int status;
+
+               /* Set the type instance. */
+               name = va_arg (ap, char const *);
+               if (name == NULL)
+                       break;
+               sstrncpy (vl->type_instance, name, sizeof (vl->type_instance));
+
+               /* Set the value. */
+               vl->values[0].gauge = va_arg (ap, gauge_t);
+               if (store_percentage)
+                       vl->values[0].gauge *= 100.0 / sum;
+
+               status = plugin_write_enqueue (vl);
+               if (status != 0)
+                       failed++;
+       }
+       va_end (ap);
+
+       plugin_value_list_free (vl);
+       return (failed);
+} /* }}} int plugin_dispatch_multivalue */
+
+int plugin_dispatch_notification (const notification_t *notif)
+{
+       llentry_t *le;
+       /* Possible TODO: Add flap detection here */
+
+       DEBUG ("plugin_dispatch_notification: severity = %i; message = %s; "
+                       "time = %.3f; host = %s;",
+                       notif->severity, notif->message,
+                       CDTIME_T_TO_DOUBLE (notif->time), notif->host);
+
+       /* Nobody cares for notifications */
+       if (list_notification == NULL)
+               return (-1);
+
+       le = llist_head (list_notification);
+       while (le != NULL)
+       {
+               callback_func_t *cf;
+               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);
+               if (status != 0)
+               {
+                       WARNING ("plugin_dispatch_notification: Notification "
+                                       "callback %s returned %i.",
+                                       le->key, status);
+               }
+
+               le = le->next;
+       }
+
+       return (0);
+} /* int plugin_dispatch_notification */
+
+void plugin_log (int level, const char *format, ...)
+{
+       char msg[1024];
+       va_list ap;
+       llentry_t *le;
+
+#if !COLLECT_DEBUG
+       if (level >= LOG_DEBUG)
+               return;
+#endif
+
+       va_start (ap, format);
+       vsnprintf (msg, sizeof (msg), format, ap);
+       msg[sizeof (msg) - 1] = '\0';
+       va_end (ap);
+
+       if (list_log == NULL)
+       {
+               fprintf (stderr, "%s\n", msg);
+               return;
+       }
+
+       le = llist_head (list_log);
+       while (le != NULL)
+       {
+               callback_func_t *cf;
+               plugin_log_cb callback;
+
+               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;
+       }
+} /* void plugin_log */
+
+int parse_log_severity (const char *severity)
+{
+       int log_level = -1;
+
+       if ((0 == strcasecmp (severity, "emerg"))
+                       || (0 == strcasecmp (severity, "alert"))
+                       || (0 == strcasecmp (severity, "crit"))
+                       || (0 == strcasecmp (severity, "err")))
+               log_level = LOG_ERR;
+       else if (0 == strcasecmp (severity, "warning"))
+               log_level = LOG_WARNING;
+       else if (0 == strcasecmp (severity, "notice"))
+               log_level = LOG_NOTICE;
+       else if (0 == strcasecmp (severity, "info"))
+               log_level = LOG_INFO;
+#if COLLECT_DEBUG
+       else if (0 == strcasecmp (severity, "debug"))
+               log_level = LOG_DEBUG;
+#endif /* COLLECT_DEBUG */
+
+       return (log_level);
+} /* int parse_log_severity */
+
+int parse_notif_severity (const char *severity)
+{
+       int notif_severity = -1;
+
+       if (strcasecmp (severity, "FAILURE") == 0)
+               notif_severity = NOTIF_FAILURE;
+       else if (strcmp (severity, "OKAY") == 0)
+               notif_severity = NOTIF_OKAY;
+       else if ((strcmp (severity, "WARNING") == 0)
+                       || (strcmp (severity, "WARN") == 0))
+               notif_severity = NOTIF_WARNING;
+
+       return (notif_severity);
+} /* int parse_notif_severity */
+
+const data_set_t *plugin_get_ds (const char *name)
+{
+       data_set_t *ds;
+
+       if (data_sets == NULL)
+       {
+               ERROR ("plugin_get_ds: No data sets are defined yet.");
+               return (NULL);
+       }
+
+       if (c_avl_get (data_sets, name, (void *) &ds) != 0)
+       {
+               DEBUG ("No such dataset registered: %s", name);
+               return (NULL);
+       }
+
+       return (ds);
+} /* data_set_t *plugin_get_ds */
+
+static int plugin_notification_meta_add (notification_t *n,
+    const char *name,
+    enum notification_meta_type_e type,
+    const void *value)
+{
+  notification_meta_t *meta;
+  notification_meta_t *tail;
+
+  if ((n == NULL) || (name == NULL) || (value == NULL))
+  {
+    ERROR ("plugin_notification_meta_add: A pointer is NULL!");
+    return (-1);
+  }
+
+  meta = (notification_meta_t *) malloc (sizeof (notification_meta_t));
+  if (meta == NULL)
+  {
+    ERROR ("plugin_notification_meta_add: malloc failed.");
+    return (-1);
+  }
+  memset (meta, 0, sizeof (notification_meta_t));
+
+  sstrncpy (meta->name, name, sizeof (meta->name));
+  meta->type = type;
+
+  switch (type)
+  {
+    case NM_TYPE_STRING:
+    {
+      meta->nm_value.nm_string = strdup ((const char *) value);
+      if (meta->nm_value.nm_string == NULL)
+      {
+        ERROR ("plugin_notification_meta_add: strdup failed.");
+        sfree (meta);
+        return (-1);
+      }
+      break;
+    }
+    case NM_TYPE_SIGNED_INT:
+    {
+      meta->nm_value.nm_signed_int = *((int64_t *) value);
+      break;
+    }
+    case NM_TYPE_UNSIGNED_INT:
+    {
+      meta->nm_value.nm_unsigned_int = *((uint64_t *) value);
+      break;
+    }
+    case NM_TYPE_DOUBLE:
+    {
+      meta->nm_value.nm_double = *((double *) value);
+      break;
+    }
+    case NM_TYPE_BOOLEAN:
+    {
+      meta->nm_value.nm_boolean = *((_Bool *) value);
+      break;
+    }
+    default:
+    {
+      ERROR ("plugin_notification_meta_add: Unknown type: %i", type);
+      sfree (meta);
+      return (-1);
+    }
+  } /* switch (type) */
+
+  meta->next = NULL;
+  tail = n->meta;
+  while ((tail != NULL) && (tail->next != NULL))
+    tail = tail->next;
+
+  if (tail == NULL)
+    n->meta = meta;
+  else
+    tail->next = meta;
+
+  return (0);
+} /* int plugin_notification_meta_add */
+
+int plugin_notification_meta_add_string (notification_t *n,
+    const char *name,
+    const char *value)
+{
+  return (plugin_notification_meta_add (n, name, NM_TYPE_STRING, value));
+}
+
+int plugin_notification_meta_add_signed_int (notification_t *n,
+    const char *name,
+    int64_t value)
+{
+  return (plugin_notification_meta_add (n, name, NM_TYPE_SIGNED_INT, &value));
+}
+
+int plugin_notification_meta_add_unsigned_int (notification_t *n,
+    const char *name,
+    uint64_t value)
+{
+  return (plugin_notification_meta_add (n, name, NM_TYPE_UNSIGNED_INT, &value));
+}
+
+int plugin_notification_meta_add_double (notification_t *n,
+    const char *name,
+    double value)
+{
+  return (plugin_notification_meta_add (n, name, NM_TYPE_DOUBLE, &value));
+}
+
+int plugin_notification_meta_add_boolean (notification_t *n,
+    const char *name,
+    _Bool value)
+{
+  return (plugin_notification_meta_add (n, name, NM_TYPE_BOOLEAN, &value));
+}
+
+int plugin_notification_meta_copy (notification_t *dst,
+    const notification_t *src)
+{
+  notification_meta_t *meta;
+
+  assert (dst != NULL);
+  assert (src != NULL);
+  assert (dst != src);
+  assert ((src->meta == NULL) || (src->meta != dst->meta));
+
+  for (meta = src->meta; meta != NULL; meta = meta->next)
+  {
+    if (meta->type == NM_TYPE_STRING)
+      plugin_notification_meta_add_string (dst, meta->name,
+          meta->nm_value.nm_string);
+    else if (meta->type == NM_TYPE_SIGNED_INT)
+      plugin_notification_meta_add_signed_int (dst, meta->name,
+          meta->nm_value.nm_signed_int);
+    else if (meta->type == NM_TYPE_UNSIGNED_INT)
+      plugin_notification_meta_add_unsigned_int (dst, meta->name,
+          meta->nm_value.nm_unsigned_int);
+    else if (meta->type == NM_TYPE_DOUBLE)
+      plugin_notification_meta_add_double (dst, meta->name,
+          meta->nm_value.nm_double);
+    else if (meta->type == NM_TYPE_BOOLEAN)
+      plugin_notification_meta_add_boolean (dst, meta->name,
+          meta->nm_value.nm_boolean);
+  }
+
+  return (0);
+} /* int plugin_notification_meta_copy */
+
+int plugin_notification_meta_free (notification_meta_t *n)
+{
+  notification_meta_t *this;
+  notification_meta_t *next;
+
+  if (n == NULL)
+  {
+    ERROR ("plugin_notification_meta_free: n == NULL!");
+    return (-1);
+  }
+
+  this = n;
+  while (this != NULL)
+  {
+    next = this->next;
+
+    if (this->type == NM_TYPE_STRING)
+    {
+      free ((char *)this->nm_value.nm_string);
+      this->nm_value.nm_string = NULL;
+    }
+    sfree (this);
+
+    this = next;
+  }
+
+  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 : */
diff --git a/src/daemon/plugin.h b/src/daemon/plugin.h
new file mode 100644 (file)
index 0000000..d773e09
--- /dev/null
@@ -0,0 +1,441 @@
+/**
+ * collectd - src/plugin.h
+ * Copyright (C) 2005-2014  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>
+ *   Sebastian Harl <sh at tokkee.org>
+ **/
+
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+#include "collectd.h"
+#include "configfile.h"
+#include "meta_data.h"
+#include "utils_time.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+#define PLUGIN_FLAGS_GLOBAL 0x0001
+
+#define DATA_MAX_NAME_LEN 64
+
+#define DS_TYPE_COUNTER  0
+#define DS_TYPE_GAUGE    1
+#define DS_TYPE_DERIVE   2
+#define DS_TYPE_ABSOLUTE 3
+
+#define DS_TYPE_TO_STRING(t) (t == DS_TYPE_COUNTER)     ? "counter"  : \
+                               (t == DS_TYPE_GAUGE)    ? "gauge"    : \
+                               (t == DS_TYPE_DERIVE)   ? "derive"   : \
+                               (t == DS_TYPE_ABSOLUTE) ? "absolute" : \
+                               "unknown"
+
+
+#ifndef LOG_ERR
+# define LOG_ERR 3
+#endif
+#ifndef LOG_WARNING
+# define LOG_WARNING 4
+#endif
+#ifndef LOG_NOTICE
+# define LOG_NOTICE 5
+#endif
+#ifndef LOG_INFO
+# define LOG_INFO 6
+#endif
+#ifndef LOG_DEBUG
+# define LOG_DEBUG 7
+#endif
+
+#define NOTIF_MAX_MSG_LEN 256
+
+#define NOTIF_FAILURE 1
+#define NOTIF_WARNING 2
+#define NOTIF_OKAY    4
+
+#define plugin_interval (plugin_get_ctx().interval)
+
+/*
+ * Public data types
+ */
+typedef unsigned long long counter_t;
+typedef double gauge_t;
+typedef int64_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 value_list_s
+{
+       value_t *values;
+       int      values_len;
+       cdtime_t time;
+       cdtime_t interval;
+       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];
+       meta_data_t *meta;
+};
+typedef struct value_list_s value_list_t;
+
+#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
+{
+       char   name[DATA_MAX_NAME_LEN];
+       int    type;
+       double min;
+       double max;
+};
+typedef struct data_source_s data_source_t;
+
+struct data_set_s
+{
+       char           type[DATA_MAX_NAME_LEN];
+       int            ds_num;
+       data_source_t *ds;
+};
+typedef struct data_set_s data_set_t;
+
+enum notification_meta_type_e
+{
+       NM_TYPE_STRING,
+       NM_TYPE_SIGNED_INT,
+       NM_TYPE_UNSIGNED_INT,
+       NM_TYPE_DOUBLE,
+       NM_TYPE_BOOLEAN
+};
+
+typedef struct notification_meta_s
+{
+       char name[DATA_MAX_NAME_LEN];
+       enum notification_meta_type_e type;
+       union
+       {
+               const char *nm_string;
+               int64_t nm_signed_int;
+               uint64_t nm_unsigned_int;
+               double nm_double;
+               _Bool nm_boolean;
+       } nm_value;
+       struct notification_meta_s *next;
+} notification_meta_t;
+
+typedef struct notification_s
+{
+       int    severity;
+       cdtime_t time;
+       char   message[NOTIF_MAX_MSG_LEN];
+       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];
+       notification_meta_t *meta;
+} notification_t;
+
+struct user_data_s
+{
+       void *data;
+       void (*free_func) (void *);
+};
+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
+ */
+typedef int (*plugin_init_cb) (void);
+typedef int (*plugin_read_cb) (user_data_t *);
+typedef int (*plugin_write_cb) (const data_set_t *, const value_list_t *,
+               user_data_t *);
+typedef int (*plugin_flush_cb) (cdtime_t timeout, const char *identifier,
+               user_data_t *);
+/* "missing" callback. Returns less than zero on failure, zero if other
+ * callbacks should be called, greater than zero if no more callbacks should be
+ * called. */
+typedef int (*plugin_missing_cb) (const value_list_t *, user_data_t *);
+typedef void (*plugin_log_cb) (int severity, const char *message,
+               user_data_t *);
+typedef int (*plugin_shutdown_cb) (void);
+typedef int (*plugin_notification_cb) (const notification_t *,
+               user_data_t *);
+
+/*
+ * NAME
+ *  plugin_set_dir
+ *
+ * DESCRIPTION
+ *  Sets the current `plugindir'
+ *
+ * ARGUMENTS
+ *  `dir'       Path to the plugin directory
+ *
+ * NOTES
+ *  If `dir' is NULL the compiled in default `PLUGINDIR' is used.
+ */
+void plugin_set_dir (const char *dir);
+
+/*
+ * NAME
+ *  plugin_load
+ *
+ * DESCRIPTION
+ *  Searches the current `plugindir' (see `plugin_set_dir') for the plugin
+ *  named $type and loads it. Afterwards the plugin's `module_register'
+ *  function is called, which then calls `plugin_register' to register callback
+ *  functions.
+ *
+ * ARGUMENTS
+ *  `name'      Name of the plugin to load.
+ *  `flags'     Hints on how to handle this plugin.
+ *
+ * RETURN VALUE
+ *  Returns zero upon success, a value greater than zero if no plugin was found
+ *  and a value below zero if an error occurs.
+ *
+ * NOTES
+ *  Re-loading an already loaded module is detected and zero is returned in
+ *  this case.
+ */
+int plugin_load (const char *name, uint32_t flags);
+
+void plugin_init_all (void);
+void plugin_read_all (void);
+int plugin_read_all_once (void);
+void plugin_shutdown_all (void);
+
+/*
+ * NAME
+ *  plugin_write
+ *
+ * DESCRIPTION
+ *  Calls the write function of the given plugin with the provided data set and
+ *  value list. It differs from `plugin_dispatch_value' in that it does not
+ *  update the cache, does not do threshold checking, call the chain subsystem
+ *  and so on. It looks up the requested plugin and invokes the function, end
+ *  of story.
+ *
+ * ARGUMENTS
+ *  plugin     Name of the plugin. If NULL, the value is sent to all registered
+ *             write functions.
+ *  ds         Pointer to the data_set_t structure. If NULL, the data set is
+ *             looked up according to the `type' member in the `vl' argument.
+ *  vl         The actual value to be processed. Must not be NULL.
+ *
+ * RETURN VALUE
+ *  Returns zero upon success or non-zero if an error occurred. If `plugin' is
+ *  NULL and more than one plugin is called, an error is only returned if *all*
+ *  plugins fail.
+ *
+ * NOTES
+ *  This is the function used by the `write' built-in target. May be used by
+ *  other target plugins.
+ */
+int plugin_write (const char *plugin,
+    const data_set_t *ds, const value_list_t *vl);
+
+int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier);
+
+/*
+ * The `plugin_register_*' functions are used to make `config', `init',
+ * `read', `write' and `shutdown' functions known to the plugin
+ * infrastructure. Also, the data-formats are made public like this.
+ */
+int plugin_register_config (const char *name,
+               int (*callback) (const char *key, const char *val),
+               const char **keys, int keys_num);
+int plugin_register_complex_config (const char *type,
+               int (*callback) (oconfig_item_t *));
+int plugin_register_init (const char *name,
+               plugin_init_cb callback);
+int plugin_register_read (const char *name,
+               int (*callback) (void));
+/* "user_data" will be freed automatically, unless
+ * "plugin_register_complex_read" returns an error (non-zero). */
+int plugin_register_complex_read (const char *group, const char *name,
+               plugin_read_cb callback,
+               const struct timespec *interval,
+               user_data_t *user_data);
+int plugin_register_write (const char *name,
+               plugin_write_cb callback, user_data_t *user_data);
+int plugin_register_flush (const char *name,
+               plugin_flush_cb callback, user_data_t *user_data);
+int plugin_register_missing (const char *name,
+               plugin_missing_cb callback, user_data_t *user_data);
+int plugin_register_shutdown (const char *name,
+               plugin_shutdown_cb callback);
+int plugin_register_data_set (const data_set_t *ds);
+int plugin_register_log (const char *name,
+               plugin_log_cb callback, user_data_t *user_data);
+int plugin_register_notification (const char *name,
+               plugin_notification_cb callback, user_data_t *user_data);
+
+int plugin_unregister_config (const char *name);
+int plugin_unregister_complex_config (const char *name);
+int plugin_unregister_init (const char *name);
+int plugin_unregister_read (const char *name);
+int plugin_unregister_read_group (const char *group);
+int plugin_unregister_write (const char *name);
+int plugin_unregister_flush (const char *name);
+int plugin_unregister_missing (const char *name);
+int plugin_unregister_shutdown (const char *name);
+int plugin_unregister_data_set (const char *name);
+int plugin_unregister_log (const char *name);
+int plugin_unregister_notification (const char *name);
+
+
+/*
+ * NAME
+ *  plugin_dispatch_values
+ *
+ * DESCRIPTION
+ *  This function is called by reading processes with the values they've
+ *  aquired. The function fetches the data-set definition (that has been
+ *  registered using `plugin_register_data_set') and calls _all_ registered
+ *  write-functions.
+ *
+ * ARGUMENTS
+ *  `vl'        Value list of the values that have been read by a `read'
+ *              function.
+ */
+int plugin_dispatch_values (value_list_t const *vl);
+
+/*
+ * NAME
+ *  plugin_dispatch_multivalue
+ *
+ * SYNOPSIS
+ *  plugin_dispatch_multivalue (vl, 1,
+ *                              "free", 42.0,
+ *                              "used", 58.0,
+ *                              NULL);
+ *
+ * DESCRIPTION
+ *  Takes a list of type instances and values and dispatches that in a batch,
+ *  making sure that all values have the same time stamp. If "store_percentage"
+ *  is set to true, the "type" is set to "percent" and a percentage is
+ *  calculated and dispatched, rather than the absolute values. Values that are
+ *  NaN are dispatched as NaN and will not influence the total.
+ *
+ *  The variadic arguments is a list of type_instance / gauge pairs, that are
+ *  interpreted as type "char const *" and "gauge_t". The last argument must be
+ *  a NULL pointer to signal end-of-list.
+ *
+ * RETURNS
+ *  The number of values it failed to dispatch (zero on success).
+ */
+__attribute__((sentinel))
+int plugin_dispatch_multivalue (value_list_t const *vl,
+               _Bool store_percentage, ...);
+
+int plugin_dispatch_missing (const value_list_t *vl);
+
+int plugin_dispatch_notification (const notification_t *notif);
+
+void plugin_log (int level, const char *format, ...)
+       __attribute__ ((format(printf,2,3)));
+
+/* These functions return the parsed severity or less than zero on failure. */
+int parse_log_severity (const char *severity);
+int parse_notif_severity (const char *severity);
+
+#define ERROR(...)   plugin_log (LOG_ERR,     __VA_ARGS__)
+#define WARNING(...) plugin_log (LOG_WARNING, __VA_ARGS__)
+#define NOTICE(...)  plugin_log (LOG_NOTICE,  __VA_ARGS__)
+#define INFO(...)    plugin_log (LOG_INFO,    __VA_ARGS__)
+#if COLLECT_DEBUG
+# define DEBUG(...)  plugin_log (LOG_DEBUG,   __VA_ARGS__)
+#else /* COLLECT_DEBUG */
+# define DEBUG(...)  /* noop */
+#endif /* ! COLLECT_DEBUG */
+
+const data_set_t *plugin_get_ds (const char *name);
+
+int plugin_notification_meta_add_string (notification_t *n,
+    const char *name,
+    const char *value);
+int plugin_notification_meta_add_signed_int (notification_t *n,
+    const char *name,
+    int64_t value);
+int plugin_notification_meta_add_unsigned_int (notification_t *n,
+    const char *name,
+    uint64_t value);
+int plugin_notification_meta_add_double (notification_t *n,
+    const char *name,
+    double value);
+int plugin_notification_meta_add_boolean (notification_t *n,
+    const char *name,
+    _Bool value);
+
+int plugin_notification_meta_copy (notification_t *dst,
+    const notification_t *src);
+
+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 */
diff --git a/src/daemon/types_list.c b/src/daemon/types_list.c
new file mode 100644 (file)
index 0000000..b3cb8cf
--- /dev/null
@@ -0,0 +1,207 @@
+/**
+ * collectd - src/types_list.c
+ * Copyright (C) 2007       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 "collectd.h"
+#include "common.h"
+
+#include "plugin.h"
+#include "configfile.h"
+
+static int parse_ds (data_source_t *dsrc, char *buf, size_t buf_len)
+{
+  char *dummy;
+  char *saveptr;
+  char *fields[8];
+  int   fields_num;
+
+  if (buf_len < 11)
+  {
+    ERROR ("parse_ds: (buf_len = %zu) < 11", buf_len);
+    return (-1);
+  }
+
+  if (buf[buf_len - 1] == ',')
+  {
+    buf_len--;
+    buf[buf_len] = '\0';
+  }
+
+  dummy = buf;
+  saveptr = NULL;
+
+  fields_num = 0;
+  while (fields_num < 8)
+  {
+    if ((fields[fields_num] = strtok_r (dummy, ":", &saveptr)) == NULL)
+      break;
+    dummy = NULL;
+    fields_num++;
+  }
+
+  if (fields_num != 4)
+  {
+    ERROR ("parse_ds: (fields_num = %i) != 4", fields_num);
+    return (-1);
+  }
+
+  sstrncpy (dsrc->name, fields[0], sizeof (dsrc->name));
+
+  if (strcasecmp (fields[1], "GAUGE") == 0)
+    dsrc->type = DS_TYPE_GAUGE;
+  else if (strcasecmp (fields[1], "COUNTER") == 0)
+    dsrc->type = DS_TYPE_COUNTER;
+  else if (strcasecmp (fields[1], "DERIVE") == 0)
+    dsrc->type = DS_TYPE_DERIVE;
+  else if (strcasecmp (fields[1], "ABSOLUTE") == 0)
+    dsrc->type = DS_TYPE_ABSOLUTE;
+  else
+  {
+    ERROR ("(fields[1] = %s) != (GAUGE || COUNTER || DERIVE || ABSOLUTE)", fields[1]);
+    return (-1);
+  }
+
+  if (strcasecmp (fields[2], "U") == 0)
+    dsrc->min = NAN;
+  else
+    dsrc->min = atof (fields[2]);
+
+  if (strcasecmp (fields[3], "U") == 0)
+    dsrc->max = NAN;
+  else
+    dsrc->max = atof (fields[3]);
+
+  return (0);
+} /* int parse_ds */
+
+static void parse_line (char *buf)
+{
+  char  *fields[64];
+  size_t fields_num;
+  data_set_t *ds;
+  int i;
+
+  fields_num = strsplit (buf, fields, 64);
+  if (fields_num < 2)
+    return;
+
+  /* Ignore lines which begin with a hash sign. */
+  if (fields[0][0] == '#')
+    return;
+
+  ds = (data_set_t *) malloc (sizeof (data_set_t));
+  if (ds == NULL)
+    return;
+
+  memset (ds, '\0', sizeof (data_set_t));
+
+  sstrncpy (ds->type, fields[0], sizeof (ds->type));
+
+  ds->ds_num = fields_num - 1;
+  ds->ds = (data_source_t *) calloc (ds->ds_num, sizeof (data_source_t));
+  if (ds->ds == NULL)
+    return;
+
+  for (i = 0; i < ds->ds_num; i++)
+    if (parse_ds (ds->ds + i, fields[i + 1], strlen (fields[i + 1])) != 0)
+    {
+      sfree (ds->ds);
+      ERROR ("types_list: parse_line: Cannot parse data source #%i "
+         "of data set %s", i, ds->type);
+      return;
+    }
+
+  plugin_register_data_set (ds);
+
+  sfree (ds->ds);
+  sfree (ds);
+} /* void parse_line */
+
+static void parse_file (FILE *fh)
+{
+  char buf[4096];
+  size_t buf_len;
+
+  while (fgets (buf, sizeof (buf), fh) != NULL)
+  {
+    buf_len = strlen (buf);
+
+    if (buf_len >= 4095)
+    {
+      NOTICE ("Skipping line with more than 4095 characters.");
+      do
+      {
+       if (fgets (buf, sizeof (buf), fh) == NULL)
+         break;
+       buf_len = strlen (buf);
+      } while (buf_len >= 4095);
+      continue;
+    } /* if (buf_len >= 4095) */
+
+    if ((buf_len == 0) || (buf[0] == '#'))
+      continue;
+
+    while ((buf_len > 0) && ((buf[buf_len - 1] == '\n')
+         || (buf[buf_len - 1] == '\n')))
+      buf[--buf_len] = '\0';
+
+    if (buf_len == 0)
+      continue;
+
+    parse_line (buf);
+  } /* while (fgets) */
+} /* void parse_file */
+
+int read_types_list (const char *file)
+{
+  FILE *fh;
+
+  if (file == NULL)
+    return (-1);
+
+  fh = fopen (file, "r");
+  if (fh == NULL)
+  {
+    char errbuf[1024];
+    fprintf (stderr, "Failed to open types database `%s': %s.\n",
+       file, sstrerror (errno, errbuf, sizeof (errbuf)));
+    ERROR ("Failed to open types database `%s': %s",
+       file, sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  parse_file (fh);
+
+  fclose (fh);
+  fh = NULL;
+
+  DEBUG ("Done parsing `%s'", file);
+
+  return (0);
+} /* int read_types_list */
+
+/*
+ * vim: shiftwidth=2:softtabstop=2:tabstop=8
+ */
diff --git a/src/daemon/types_list.h b/src/daemon/types_list.h
new file mode 100644 (file)
index 0000000..f375a2f
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * collectd - src/types_list.h
+ * Copyright (C) 2007       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 TYPES_LIST_H
+#define TYPES_LIST_H 1
+
+int read_types_list (const char *file);
+
+#endif /* TYPES_LIST_H */
diff --git a/src/daemon/utils_avltree.c b/src/daemon/utils_avltree.c
new file mode 100644 (file)
index 0000000..04e5403
--- /dev/null
@@ -0,0 +1,730 @@
+/**
+ * collectd - src/utils_avltree.c
+ * Copyright (C) 2006,2007  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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "utils_avltree.h"
+
+#define BALANCE(n) ((((n)->left == NULL) ? 0 : (n)->left->height) \
+               - (((n)->right == NULL) ? 0 : (n)->right->height))
+
+/*
+ * private data types
+ */
+struct c_avl_node_s
+{
+       void *key;
+       void *value;
+
+       int height;
+       struct c_avl_node_s *left;
+       struct c_avl_node_s *right;
+       struct c_avl_node_s *parent;
+};
+typedef struct c_avl_node_s c_avl_node_t;
+
+struct c_avl_tree_s
+{
+       c_avl_node_t *root;
+       int (*compare) (const void *, const void *);
+       int size;
+};
+
+struct c_avl_iterator_s
+{
+       c_avl_tree_t *tree;
+       c_avl_node_t *node;
+};
+
+/*
+ * private functions
+ */
+#if 0
+static void verify_tree (c_avl_node_t *n)
+{
+       if (n == NULL)
+               return;
+
+       verify_tree (n->left);
+       verify_tree (n->right);
+
+       assert ((BALANCE (n) >= -1) && (BALANCE (n) <= 1));
+       assert ((n->parent == NULL) || (n->parent->right == n) || (n->parent->left == n));
+} /* void verify_tree */
+#else
+# define verify_tree(n) /**/
+#endif
+
+static void free_node (c_avl_node_t *n)
+{
+       if (n == NULL)
+               return;
+
+       if (n->left != NULL)
+               free_node (n->left);
+       if (n->right != NULL)
+               free_node (n->right);
+
+       free (n);
+}
+
+static int calc_height (c_avl_node_t *n)
+{
+       int height_left;
+       int height_right;
+
+       if (n == NULL)
+               return (0);
+
+       height_left  = (n->left == NULL)  ? 0 : n->left->height;
+       height_right = (n->right == NULL) ? 0 : n->right->height;
+
+       return (((height_left > height_right)
+                               ? height_left
+                               : height_right) + 1);
+} /* int calc_height */
+
+static c_avl_node_t *search (c_avl_tree_t *t, const void *key)
+{
+       c_avl_node_t *n;
+       int cmp;
+
+       n = t->root;
+       while (n != NULL)
+       {
+               cmp = t->compare (key, n->key);
+               if (cmp == 0)
+                       return (n);
+               else if (cmp < 0)
+                       n = n->left;
+               else
+                       n = n->right;
+       }
+
+       return (NULL);
+}
+
+/*         (x)             (y)
+ *        /   \           /   \
+ *     (y)    /\         /\    (x)
+ *    /   \  /_c\  ==>  / a\  /   \
+ *   /\   /\           /____\/\   /\
+ *  / a\ /_b\               /_b\ /_c\
+ * /____\
+ */
+static c_avl_node_t *rotate_right (c_avl_tree_t *t, c_avl_node_t *x)
+{
+       c_avl_node_t *p;
+       c_avl_node_t *y;
+       c_avl_node_t *b;
+
+       p = x->parent;
+       y = x->left;
+       b = y->right;
+
+       x->left = b;
+       if (b != NULL)
+               b->parent = x;
+
+       x->parent = y;
+       y->right = x;
+
+       y->parent = p;
+       assert ((p == NULL) || (p->left == x) || (p->right == x));
+       if (p == NULL)
+               t->root = y;
+       else if (p->left == x)
+               p->left = y;
+       else
+               p->right = y;
+
+       x->height = calc_height (x);
+       y->height = calc_height (y);
+
+       return (y);
+} /* void rotate_left */
+
+/*
+ *    (x)                   (y)
+ *   /   \                 /   \
+ *  /\    (y)           (x)    /\
+ * /_a\  /   \   ==>   /   \  / c\
+ *      /\   /\       /\   /\/____\
+ *     /_b\ / c\     /_a\ /_b\
+ *         /____\
+ */
+static c_avl_node_t *rotate_left (c_avl_tree_t *t, c_avl_node_t *x)
+{
+       c_avl_node_t *p;
+       c_avl_node_t *y;
+       c_avl_node_t *b;
+
+       p = x->parent;
+       y = x->right;
+       b = y->left;
+
+       x->right = b;
+       if (b != NULL)
+               b->parent = x;
+
+       x->parent = y;
+       y->left = x;
+
+       y->parent = p;
+       assert ((p == NULL) || (p->left == x) || (p->right == x));
+       if (p == NULL)
+               t->root = y;
+       else if (p->left == x)
+               p->left = y;
+       else
+               p->right = y;
+
+       x->height = calc_height (x);
+       y->height = calc_height (y);
+
+       return (y);
+} /* void rotate_left */
+
+static c_avl_node_t *rotate_left_right (c_avl_tree_t *t, c_avl_node_t *x)
+{
+       rotate_left (t, x->left);
+       return (rotate_right (t, x));
+} /* void rotate_left_right */
+
+static c_avl_node_t *rotate_right_left (c_avl_tree_t *t, c_avl_node_t *x)
+{
+       rotate_right (t, x->right);
+       return (rotate_left (t, x));
+} /* void rotate_right_left */
+
+static void rebalance (c_avl_tree_t *t, c_avl_node_t *n)
+{
+       int b_top;
+       int b_bottom;
+
+       while (n != NULL)
+       {
+               b_top = BALANCE (n);
+               assert ((b_top >= -2) && (b_top <= 2));
+
+               if (b_top == -2)
+               {
+                       assert (n->right != NULL);
+                       b_bottom = BALANCE (n->right);
+                       assert ((b_bottom >= -1) || (b_bottom <= 1));
+                       if (b_bottom == 1)
+                               n = rotate_right_left (t, n);
+                       else
+                               n = rotate_left (t, n);
+               }
+               else if (b_top == 2)
+               {
+                       assert (n->left != NULL);
+                       b_bottom = BALANCE (n->left);
+                       assert ((b_bottom >= -1) || (b_bottom <= 1));
+                       if (b_bottom == -1)
+                               n = rotate_left_right (t, n);
+                       else
+                               n = rotate_right (t, n);
+               }
+               else
+               {
+                       int height = calc_height (n);
+                       if (height == n->height)
+                               break;
+                       n->height = height;
+               }
+
+               assert (n->height == calc_height (n));
+
+               n = n->parent;
+       } /* while (n != NULL) */
+} /* void rebalance */
+
+static c_avl_node_t *c_avl_node_next (c_avl_node_t *n)
+{
+       c_avl_node_t *r; /* return node */
+
+       if (n == NULL)
+       {
+               return (NULL);
+       }
+
+       /* If we can't descent any further, we have to backtrack to the first
+        * parent that's bigger than we, i. e. who's _left_ child we are. */
+       if (n->right == NULL)
+       {
+               r = n->parent;
+               while ((r != NULL) && (r->parent != NULL))
+               {
+                       if (r->left == n)
+                               break;
+                       n = r;
+                       r = n->parent;
+               }
+
+               /* n->right == NULL && r == NULL => t is root and has no next
+                * r->left != n => r->right = n => r->parent == NULL */
+               if ((r == NULL) || (r->left != n))
+               {
+                       assert ((r == NULL) || (r->parent == NULL));
+                       return (NULL);
+               }
+               else
+               {
+                       assert (r->left == n);
+                       return (r);
+               }
+       }
+       else
+       {
+               r = n->right;
+               while (r->left != NULL)
+                       r = r->left;
+       }
+
+       return (r);
+} /* c_avl_node_t *c_avl_node_next */
+
+static c_avl_node_t *c_avl_node_prev (c_avl_node_t *n)
+{
+       c_avl_node_t *r; /* return node */
+
+       if (n == NULL)
+       {
+               return (NULL);
+       }
+
+       /* If we can't descent any further, we have to backtrack to the first
+        * parent that's smaller than we, i. e. who's _right_ child we are. */
+       if (n->left == NULL)
+       {
+               r = n->parent;
+               while ((r != NULL) && (r->parent != NULL))
+               {
+                       if (r->right == n)
+                               break;
+                       n = r;
+                       r = n->parent;
+               }
+
+               /* n->left == NULL && r == NULL => t is root and has no next
+                * r->right != n => r->left = n => r->parent == NULL */
+               if ((r == NULL) || (r->right != n))
+               {
+                       assert ((r == NULL) || (r->parent == NULL));
+                       return (NULL);
+               }
+               else
+               {
+                       assert (r->right == n);
+                       return (r);
+               }
+       }
+       else
+       {
+               r = n->left;
+               while (r->right != NULL)
+                       r = r->right;
+       }
+
+       return (r);
+} /* c_avl_node_t *c_avl_node_prev */
+
+static int _remove (c_avl_tree_t *t, c_avl_node_t *n)
+{
+       assert ((t != NULL) && (n != NULL));
+
+       if ((n->left != NULL) && (n->right != NULL))
+       {
+               c_avl_node_t *r; /* replacement node */
+               if (BALANCE (n) > 0) /* left subtree is higher */
+               {
+                       assert (n->left != NULL);
+                       r = c_avl_node_prev (n);
+                       
+               }
+               else /* right subtree is higher */
+               {
+                       assert (n->right != NULL);
+                       r = c_avl_node_next (n);
+               }
+
+               assert ((r->left == NULL) || (r->right == NULL));
+
+               /* copy content */
+               n->key   = r->key;
+               n->value = r->value;
+
+               n = r;
+       }
+
+       assert ((n->left == NULL) || (n->right == NULL));
+
+       if ((n->left == NULL) && (n->right == NULL))
+       {
+               /* Deleting a leave is easy */
+               if (n->parent == NULL)
+               {
+                       assert (t->root == n);
+                       t->root = NULL;
+               }
+               else
+               {
+                       assert ((n->parent->left == n)
+                                       || (n->parent->right == n));
+                       if (n->parent->left == n)
+                               n->parent->left = NULL;
+                       else
+                               n->parent->right = NULL;
+
+                       rebalance (t, n->parent);
+               }
+
+               free_node (n);
+       }
+       else if (n->left == NULL)
+       {
+               assert (BALANCE (n) == -1);
+               assert ((n->parent == NULL) || (n->parent->left == n) || (n->parent->right == n));
+               if (n->parent == NULL)
+               {
+                       assert (t->root == n);
+                       t->root = n->right;
+               }
+               else if (n->parent->left == n)
+               {
+                       n->parent->left = n->right;
+               }
+               else
+               {
+                       n->parent->right = n->right;
+               }
+               n->right->parent = n->parent;
+
+               if (n->parent != NULL)
+                       rebalance (t, n->parent);
+
+               n->right = NULL;
+               free_node (n);
+       }
+       else if (n->right == NULL)
+       {
+               assert (BALANCE (n) == 1);
+               assert ((n->parent == NULL) || (n->parent->left == n) || (n->parent->right == n));
+               if (n->parent == NULL)
+               {
+                       assert (t->root == n);
+                       t->root = n->left;
+               }
+               else if (n->parent->left == n)
+               {
+                       n->parent->left = n->left;
+               }
+               else
+               {
+                       n->parent->right = n->left;
+               }
+               n->left->parent = n->parent;
+
+               if (n->parent != NULL)
+                       rebalance (t, n->parent);
+
+               n->left = NULL;
+               free_node (n);
+       }
+       else
+       {
+               assert (0);
+       }
+
+       return (0);
+} /* void *_remove */
+
+/*
+ * public functions
+ */
+c_avl_tree_t *c_avl_create (int (*compare) (const void *, const void *))
+{
+       c_avl_tree_t *t;
+
+       if (compare == NULL)
+               return (NULL);
+
+       if ((t = (c_avl_tree_t *) malloc (sizeof (c_avl_tree_t))) == NULL)
+               return (NULL);
+
+       t->root = NULL;
+       t->compare = compare;
+       t->size = 0;
+
+       return (t);
+}
+
+void c_avl_destroy (c_avl_tree_t *t)
+{
+       if (t == NULL)
+               return;
+       free_node (t->root);
+       free (t);
+}
+
+int c_avl_insert (c_avl_tree_t *t, void *key, void *value)
+{
+       c_avl_node_t *new;
+       c_avl_node_t *nptr;
+       int cmp;
+
+       if ((new = (c_avl_node_t *) malloc (sizeof (c_avl_node_t))) == NULL)
+               return (-1);
+
+       new->key = key;
+       new->value = value;
+       new->height = 1;
+       new->left = NULL;
+       new->right = NULL;
+
+       if (t->root == NULL)
+       {
+               new->parent = NULL;
+               t->root = new;
+               t->size = 1;
+               return (0);
+       }
+
+       nptr = t->root;
+       while (42)
+       {
+               cmp = t->compare (nptr->key, new->key);
+               if (cmp == 0)
+               {
+                       free_node (new);
+                       return (1);
+               }
+               else if (cmp < 0)
+               {
+                       /* nptr < new */
+                       if (nptr->right == NULL)
+                       {
+                               nptr->right = new;
+                               new->parent = nptr;
+                               rebalance (t, nptr);
+                               break;
+                       }
+                       else
+                       {
+                               nptr = nptr->right;
+                       }
+               }
+               else /* if (cmp > 0) */
+               {
+                       /* nptr > new */
+                       if (nptr->left == NULL)
+                       {
+                               nptr->left = new;
+                               new->parent = nptr;
+                               rebalance (t, nptr);
+                               break;
+                       }
+                       else
+                       {
+                               nptr = nptr->left;
+                       }
+               }
+       } /* while (42) */
+
+       verify_tree (t->root);
+       ++t->size;
+       return (0);
+} /* int c_avl_insert */
+
+int c_avl_remove (c_avl_tree_t *t, const void *key, void **rkey, void **rvalue)
+{
+       c_avl_node_t *n;
+       int status;
+
+       assert (t != NULL);
+
+       n = search (t, key);
+       if (n == NULL)
+               return (-1);
+
+       if (rkey != NULL)
+               *rkey = n->key;
+       if (rvalue != NULL)
+               *rvalue = n->value;
+
+       status = _remove (t, n);
+       verify_tree (t->root);
+       --t->size;
+       return (status);
+} /* void *c_avl_remove */
+
+int c_avl_get (c_avl_tree_t *t, const void *key, void **value)
+{
+       c_avl_node_t *n;
+
+       assert (t != NULL);
+
+       n = search (t, key);
+       if (n == NULL)
+               return (-1);
+
+       if (value != NULL)
+               *value = n->value;
+
+       return (0);
+}
+
+int c_avl_pick (c_avl_tree_t *t, void **key, void **value)
+{
+       c_avl_node_t *n;
+       c_avl_node_t *p;
+
+       if ((key == NULL) || (value == NULL))
+               return (-1);
+       if (t->root == NULL)
+               return (-1);
+
+       n = t->root;
+       while ((n->left != NULL) || (n->right != NULL))
+       {
+               int height_left  = (n->left  == NULL) ? 0 : n->left->height;
+               int height_right = (n->right == NULL) ? 0 : n->right->height;
+
+               if (height_left > height_right)
+                       n = n->left;
+               else
+                       n = n->right;
+       }
+
+       p = n->parent;
+       if (p == NULL)
+               t->root = NULL;
+       else if (p->left == n)
+               p->left = NULL;
+       else
+               p->right = NULL;
+
+       *key   = n->key;
+       *value = n->value;
+
+       free_node (n);
+       rebalance (t, p);
+
+       return (0);
+} /* int c_avl_pick */
+
+c_avl_iterator_t *c_avl_get_iterator (c_avl_tree_t *t)
+{
+       c_avl_iterator_t *iter;
+
+       if (t == NULL)
+               return (NULL);
+
+       iter = (c_avl_iterator_t *) malloc (sizeof (c_avl_iterator_t));
+       if (iter == NULL)
+               return (NULL);
+       memset (iter, '\0', sizeof (c_avl_iterator_t));
+       iter->tree = t;
+
+       return (iter);
+} /* c_avl_iterator_t *c_avl_get_iterator */
+
+int c_avl_iterator_next (c_avl_iterator_t *iter, void **key, void **value)
+{
+       c_avl_node_t *n;
+
+       if ((iter == NULL) || (key == NULL) || (value == NULL))
+               return (-1);
+
+       if (iter->node == NULL)
+       {
+               for (n = iter->tree->root; n != NULL; n = n->left)
+                       if (n->left == NULL)
+                               break;
+               iter->node = n;
+       }
+       else
+       {
+               n = c_avl_node_next (iter->node);
+       }
+
+       if (n == NULL)
+               return (-1);
+
+       iter->node = n;
+       *key = n->key;
+       *value = n->value;
+
+       return (0);
+} /* int c_avl_iterator_next */
+
+int c_avl_iterator_prev (c_avl_iterator_t *iter, void **key, void **value)
+{
+       c_avl_node_t *n;
+
+       if ((iter == NULL) || (key == NULL) || (value == NULL))
+               return (-1);
+
+       if (iter->node == NULL)
+       {
+               for (n = iter->tree->root; n != NULL; n = n->left)
+                       if (n->right == NULL)
+                               break;
+               iter->node = n;
+       }
+       else
+       {
+               n = c_avl_node_prev (iter->node);
+       }
+
+       if (n == NULL)
+               return (-1);
+
+       iter->node = n;
+       *key = n->key;
+       *value = n->value;
+
+       return (0);
+} /* int c_avl_iterator_prev */
+
+void c_avl_iterator_destroy (c_avl_iterator_t *iter)
+{
+       free (iter);
+}
+
+int c_avl_size (c_avl_tree_t *t)
+{
+       if (t == NULL)
+               return (0);
+       return (t->size);
+}
diff --git a/src/daemon/utils_avltree.h b/src/daemon/utils_avltree.h
new file mode 100644 (file)
index 0000000..1e0f271
--- /dev/null
@@ -0,0 +1,170 @@
+/**
+ * collectd - src/utils_avltree.h
+ * Copyright (C) 2006,2007  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 UTILS_AVLTREE_H
+#define UTILS_AVLTREE_H 1
+
+struct c_avl_tree_s;
+typedef struct c_avl_tree_s c_avl_tree_t;
+
+struct c_avl_iterator_s;
+typedef struct c_avl_iterator_s c_avl_iterator_t;
+
+/*
+ * NAME
+ *   c_avl_create
+ *
+ * DESCRIPTION
+ *   Allocates a new AVL-tree.
+ *
+ * PARAMETERS
+ *   `compare'  The function-pointer `compare' is used to compare two keys. It
+ *              has to return less than zero if it's first argument is smaller
+ *              then the second argument, more than zero if the first argument
+ *              is bigger than the second argument and zero if they are equal.
+ *              If your keys are char-pointers, you can use the `strcmp'
+ *              function from the libc here.
+ *
+ * RETURN VALUE
+ *   A c_avl_tree_t-pointer upon success or NULL upon failure.
+ */
+c_avl_tree_t *c_avl_create (int (*compare) (const void *, const void *));
+
+
+/*
+ * NAME
+ *   c_avl_destroy
+ *
+ * DESCRIPTION
+ *   Deallocates an AVL-tree. Stored value- and key-pointer are lost, but of
+ *   course not freed.
+ */
+void c_avl_destroy (c_avl_tree_t *t);
+
+/*
+ * NAME
+ *   c_avl_insert
+ *
+ * DESCRIPTION
+ *   Stores the key-value-pair in the AVL-tree pointed to by `t'.
+ *
+ * PARAMETERS
+ *   `t'        AVL-tree to store the data in.
+ *   `key'      Key used to store the value under. This is used to get back to
+ *              the value again. The pointer is stored in an internal structure
+ *              and _not_ copied. So the memory pointed to may _not_ be freed
+ *              before this entry is removed. You can use the `rkey' argument
+ *              to `avl_remove' to get the original pointer back and free it.
+ *   `value'    Value to be stored.
+ *
+ * RETURN VALUE
+ *   Zero upon success, non-zero otherwise. It's less than zero if an error
+ *   occurred or greater than zero if the key is already stored in the tree.
+ */
+int c_avl_insert (c_avl_tree_t *t, void *key, void *value);
+
+/*
+ * NAME
+ *   c_avl_remove
+ *
+ * DESCRIPTION
+ *   Removes a key-value-pair from the tree t. The stored key and value may be
+ *   returned in `rkey' and `rvalue'.
+ *
+ * PARAMETERS
+ *   `t'       AVL-tree to remove key-value-pair from.
+ *   `key'      Key to identify the entry.
+ *   `rkey'     Pointer to a pointer in which to store the key. May be NULL.
+ *              Since the `key' pointer is not copied when creating an entry,
+ *              the pointer may not be available anymore from outside the tree.
+ *              You can use this argument to get the actual pointer back and
+ *              free the memory pointed to by it.
+ *   `rvalue'   Pointer to a pointer in which to store the value. May be NULL.
+ *
+ * RETURN VALUE
+ *   Zero upon success or non-zero if the key isn't found in the tree.
+ */
+int c_avl_remove (c_avl_tree_t *t, const void *key, void **rkey, void **rvalue);
+
+/*
+ * NAME
+ *   c_avl_get
+ *
+ * DESCRIPTION
+ *   Retrieve the `value' belonging to `key'.
+ *
+ * PARAMETERS
+ *   `t'       AVL-tree to get the value from.
+ *   `key'      Key to identify the entry.
+ *   `value'    Pointer to a pointer in which to store the value. May be NULL.
+ *
+ * RETURN VALUE
+ *   Zero upon success or non-zero if the key isn't found in the tree.
+ */
+int c_avl_get (c_avl_tree_t *t, const void *key, void **value);
+
+/*
+ * NAME
+ *   c_avl_pick
+ *
+ * DESCRIPTION
+ *   Remove a (pseudo-)random element from the tree and return it's `key' and
+ *   `value'. Entries are not returned in any particular order. This function
+ *   is intended for cache-flushes that don't care about the order but simply
+ *   want to remove all elements, one at a time.
+ *
+ * PARAMETERS
+ *   `t'       AVL-tree to get the value from.
+ *   `key'      Pointer to a pointer in which to store the key.
+ *   `value'    Pointer to a pointer in which to store the value.
+ *
+ * RETURN VALUE
+ *   Zero upon success or non-zero if the tree is empty or key or value is
+ *   NULL.
+ */
+int c_avl_pick (c_avl_tree_t *t, void **key, void **value);
+
+c_avl_iterator_t *c_avl_get_iterator (c_avl_tree_t *t);
+int c_avl_iterator_next (c_avl_iterator_t *iter, void **key, void **value);
+int c_avl_iterator_prev (c_avl_iterator_t *iter, void **key, void **value);
+void c_avl_iterator_destroy (c_avl_iterator_t *iter);
+
+/*
+ * NAME
+ *   c_avl_size
+ *
+ * DESCRIPTION
+ *   Return the size (number of nodes) of the specified tree.
+ *
+ * PARAMETERS
+ *   `t'        AVL-tree to get the size of.
+ *
+ * RETURN VALUE
+ *   Number of nodes in the tree, 0 if the tree is empty or NULL.
+ */
+int c_avl_size (c_avl_tree_t *t);
+
+#endif /* UTILS_AVLTREE_H */
diff --git a/src/daemon/utils_cache.c b/src/daemon/utils_cache.c
new file mode 100644 (file)
index 0000000..fe22f21
--- /dev/null
@@ -0,0 +1,988 @@
+/**
+ * collectd - src/utils_cache.c
+ * Copyright (C) 2007-2010  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 "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_avltree.h"
+#include "utils_cache.h"
+#include "meta_data.h"
+
+#include <assert.h>
+#include <pthread.h>
+
+typedef struct cache_entry_s
+{
+       char name[6 * DATA_MAX_NAME_LEN];
+       int        values_num;
+       gauge_t   *values_gauge;
+       value_t   *values_raw;
+       /* Time contained in the package
+        * (for calculating rates) */
+       cdtime_t last_time;
+       /* Time according to the local clock
+        * (for purging old entries) */
+       cdtime_t last_update;
+       /* Interval in which the data is collected
+        * (for purding old entries) */
+       cdtime_t interval;
+       int state;
+       int hits;
+
+       /*
+        * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+        * !  0  !  1  !  2  !  3  !  4  !  5  !  6  !  7  !  8  ! ...
+        * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+        * ! ds0 ! ds1 ! ds2 ! ds0 ! ds1 ! ds2 ! ds0 ! ds1 ! ds2 ! ...
+        * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+        * !      t = 0      !      t = 1      !      t = 2      ! ...
+        * +-----------------+-----------------+-----------------+----
+        */
+       gauge_t *history;
+       size_t   history_index; /* points to the next position to write to. */
+       size_t   history_length;
+
+       meta_data_t *meta;
+} cache_entry_t;
+
+static c_avl_tree_t   *cache_tree = NULL;
+static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static int cache_compare (const cache_entry_t *a, const cache_entry_t *b)
+{
+#if COLLECT_DEBUG
+  assert ((a != NULL) && (b != NULL));
+#endif
+  return (strcmp (a->name, b->name));
+} /* int cache_compare */
+
+static cache_entry_t *cache_alloc (int values_num)
+{
+  cache_entry_t *ce;
+
+  ce = (cache_entry_t *) malloc (sizeof (cache_entry_t));
+  if (ce == NULL)
+  {
+    ERROR ("utils_cache: cache_alloc: malloc failed.");
+    return (NULL);
+  }
+  memset (ce, '\0', sizeof (cache_entry_t));
+  ce->values_num = values_num;
+
+  ce->values_gauge = calloc (values_num, sizeof (*ce->values_gauge));
+  ce->values_raw   = calloc (values_num, sizeof (*ce->values_raw));
+  if ((ce->values_gauge == NULL) || (ce->values_raw == NULL))
+  {
+    sfree (ce->values_gauge);
+    sfree (ce->values_raw);
+    sfree (ce);
+    ERROR ("utils_cache: cache_alloc: calloc failed.");
+    return (NULL);
+  }
+
+  ce->history = NULL;
+  ce->history_length = 0;
+  ce->meta = NULL;
+
+  return (ce);
+} /* cache_entry_t *cache_alloc */
+
+static void cache_free (cache_entry_t *ce)
+{
+  if (ce == NULL)
+    return;
+
+  sfree (ce->values_gauge);
+  sfree (ce->values_raw);
+  sfree (ce->history);
+  if (ce->meta != NULL)
+  {
+    meta_data_destroy (ce->meta);
+    ce->meta = NULL;
+  }
+  sfree (ce);
+} /* void cache_free */
+
+static void uc_check_range (const data_set_t *ds, cache_entry_t *ce)
+{
+  int i;
+
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    if (isnan (ce->values_gauge[i]))
+      continue;
+    else if (ce->values_gauge[i] < ds->ds[i].min)
+      ce->values_gauge[i] = NAN;
+    else if (ce->values_gauge[i] > ds->ds[i].max)
+      ce->values_gauge[i] = NAN;
+  }
+} /* void uc_check_range */
+
+static int uc_insert (const data_set_t *ds, const value_list_t *vl,
+    const char *key)
+{
+  int i;
+  char *key_copy;
+  cache_entry_t *ce;
+
+  /* `cache_lock' has been locked by `uc_update' */
+
+  key_copy = strdup (key);
+  if (key_copy == NULL)
+  {
+    ERROR ("uc_insert: strdup failed.");
+    return (-1);
+  }
+
+  ce = cache_alloc (ds->ds_num);
+  if (ce == NULL)
+  {
+    sfree (key_copy);
+    ERROR ("uc_insert: cache_alloc (%i) failed.", ds->ds_num);
+    return (-1);
+  }
+
+  sstrncpy (ce->name, key, sizeof (ce->name));
+
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    switch (ds->ds[i].type)
+    {
+      case DS_TYPE_COUNTER:
+       ce->values_gauge[i] = NAN;
+       ce->values_raw[i].counter = vl->values[i].counter;
+       break;
+
+      case DS_TYPE_GAUGE:
+       ce->values_gauge[i] = vl->values[i].gauge;
+       ce->values_raw[i].gauge = vl->values[i].gauge;
+       break;
+
+      case DS_TYPE_DERIVE:
+       ce->values_gauge[i] = NAN;
+       ce->values_raw[i].derive = vl->values[i].derive;
+       break;
+
+      case DS_TYPE_ABSOLUTE:
+       ce->values_gauge[i] = NAN;
+       if (vl->interval > 0)
+         ce->values_gauge[i] = ((double) vl->values[i].absolute)
+           / CDTIME_T_TO_DOUBLE (vl->interval);
+       ce->values_raw[i].absolute = vl->values[i].absolute;
+       break;
+       
+      default:
+       /* This shouldn't happen. */
+       ERROR ("uc_insert: Don't know how to handle data source type %i.",
+           ds->ds[i].type);
+       return (-1);
+    } /* switch (ds->ds[i].type) */
+  } /* for (i) */
+
+  /* Prune invalid gauge data */
+  uc_check_range (ds, ce);
+
+  ce->last_time = vl->time;
+  ce->last_update = cdtime ();
+  ce->interval = vl->interval;
+  ce->state = STATE_OKAY;
+
+  if (c_avl_insert (cache_tree, key_copy, ce) != 0)
+  {
+    sfree (key_copy);
+    ERROR ("uc_insert: c_avl_insert failed.");
+    return (-1);
+  }
+
+  DEBUG ("uc_insert: Added %s to the cache.", key);
+  return (0);
+} /* int uc_insert */
+
+int uc_init (void)
+{
+  if (cache_tree == NULL)
+    cache_tree = c_avl_create ((int (*) (const void *, const void *))
+       cache_compare);
+
+  return (0);
+} /* int uc_init */
+
+int uc_check_timeout (void)
+{
+  cdtime_t now;
+  cache_entry_t *ce;
+
+  char **keys = NULL;
+  cdtime_t *keys_time = NULL;
+  cdtime_t *keys_interval = NULL;
+  int keys_len = 0;
+
+  char *key;
+  c_avl_iterator_t *iter;
+
+  int status;
+  int i;
+  
+  pthread_mutex_lock (&cache_lock);
+
+  now = cdtime ();
+
+  /* Build a list of entries to be flushed */
+  iter = c_avl_get_iterator (cache_tree);
+  while (c_avl_iterator_next (iter, (void *) &key, (void *) &ce) == 0)
+  {
+    char **tmp;
+    cdtime_t *tmp_time;
+
+    /* If the entry is fresh enough, continue. */
+    if ((now - ce->last_update) < (ce->interval * timeout_g))
+      continue;
+
+    /* If entry has not been updated, add to `keys' array */
+    tmp = (char **) realloc ((void *) keys,
+       (keys_len + 1) * sizeof (char *));
+    if (tmp == NULL)
+    {
+      ERROR ("uc_check_timeout: realloc failed.");
+      continue;
+    }
+    keys = tmp;
+
+    tmp_time = realloc (keys_time, (keys_len + 1) * sizeof (*keys_time));
+    if (tmp_time == NULL)
+    {
+      ERROR ("uc_check_timeout: realloc failed.");
+      continue;
+    }
+    keys_time = tmp_time;
+
+    tmp_time = realloc (keys_interval, (keys_len + 1) * sizeof (*keys_interval));
+    if (tmp_time == NULL)
+    {
+      ERROR ("uc_check_timeout: realloc failed.");
+      continue;
+    }
+    keys_interval = tmp_time;
+
+    keys[keys_len] = strdup (key);
+    if (keys[keys_len] == NULL)
+    {
+      ERROR ("uc_check_timeout: strdup failed.");
+      continue;
+    }
+    keys_time[keys_len] = ce->last_time;
+    keys_interval[keys_len] = ce->interval;
+
+    keys_len++;
+  } /* while (c_avl_iterator_next) */
+
+  c_avl_iterator_destroy (iter);
+  pthread_mutex_unlock (&cache_lock);
+
+  if (keys_len == 0)
+    return (0);
+
+  /* Call the "missing" callback for each value. Do this before removing the
+   * value from the cache, so that callbacks can still access the data stored,
+   * including plugin specific meta data, rates, history, …. This must be done
+   * without holding the lock, otherwise we will run into a deadlock if a
+   * plugin calls the cache interface. */
+  for (i = 0; i < keys_len; i++)
+  {
+    value_list_t vl = VALUE_LIST_INIT;
+
+    vl.values = NULL;
+    vl.values_len = 0;
+    vl.meta = NULL;
+
+    status = parse_identifier_vl (keys[i], &vl);
+    if (status != 0)
+    {
+      ERROR ("uc_check_timeout: parse_identifier_vl (\"%s\") failed.", keys[i]);
+      cache_free (ce);
+      continue;
+    }
+
+    vl.time = keys_time[i];
+    vl.interval = keys_interval[i];
+
+    plugin_dispatch_missing (&vl);
+  } /* for (i = 0; i < keys_len; i++) */
+
+  /* Now actually remove all the values from the cache. We don't re-evaluate
+   * the timestamp again, so in theory it is possible we remove a value after
+   * it is updated here. */
+  pthread_mutex_lock (&cache_lock);
+  for (i = 0; i < keys_len; i++)
+  {
+    key = NULL;
+    ce = NULL;
+
+    status = c_avl_remove (cache_tree, keys[i],
+       (void *) &key, (void *) &ce);
+    if (status != 0)
+    {
+      ERROR ("uc_check_timeout: c_avl_remove (\"%s\") failed.", keys[i]);
+      sfree (keys[i]);
+      continue;
+    }
+
+    sfree (keys[i]);
+    sfree (key);
+    cache_free (ce);
+  } /* for (i = 0; i < keys_len; i++) */
+  pthread_mutex_unlock (&cache_lock);
+
+  sfree (keys);
+  sfree (keys_time);
+  sfree (keys_interval);
+
+  return (0);
+} /* int uc_check_timeout */
+
+int uc_update (const data_set_t *ds, const value_list_t *vl)
+{
+  char name[6 * DATA_MAX_NAME_LEN];
+  cache_entry_t *ce = NULL;
+  int status;
+  int i;
+
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
+  {
+    ERROR ("uc_update: FORMAT_VL failed.");
+    return (-1);
+  }
+
+  pthread_mutex_lock (&cache_lock);
+
+  status = c_avl_get (cache_tree, name, (void *) &ce);
+  if (status != 0) /* entry does not yet exist */
+  {
+    status = uc_insert (ds, vl, name);
+    pthread_mutex_unlock (&cache_lock);
+    return (status);
+  }
+
+  assert (ce != NULL);
+  assert (ce->values_num == ds->ds_num);
+
+  if (ce->last_time >= vl->time)
+  {
+    pthread_mutex_unlock (&cache_lock);
+    NOTICE ("uc_update: Value too old: name = %s; value time = %.3f; "
+       "last cache update = %.3f;",
+       name,
+       CDTIME_T_TO_DOUBLE (vl->time),
+       CDTIME_T_TO_DOUBLE (ce->last_time));
+    return (-1);
+  }
+
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    switch (ds->ds[i].type)
+    {
+      case DS_TYPE_COUNTER:
+       {
+         counter_t diff;
+
+         /* check if the counter has wrapped around */
+         if (vl->values[i].counter < ce->values_raw[i].counter)
+         {
+           if (ce->values_raw[i].counter <= 4294967295U)
+             diff = (4294967295U - ce->values_raw[i].counter)
+               + vl->values[i].counter;
+           else
+             diff = (18446744073709551615ULL - ce->values_raw[i].counter)
+               + vl->values[i].counter;
+         }
+         else /* counter has NOT wrapped around */
+         {
+           diff = vl->values[i].counter - ce->values_raw[i].counter;
+         }
+
+         ce->values_gauge[i] = ((double) diff)
+           / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time));
+         ce->values_raw[i].counter = vl->values[i].counter;
+       }
+       break;
+
+      case DS_TYPE_GAUGE:
+       ce->values_raw[i].gauge = vl->values[i].gauge;
+       ce->values_gauge[i] = vl->values[i].gauge;
+       break;
+
+      case DS_TYPE_DERIVE:
+       {
+         derive_t diff;
+
+         diff = vl->values[i].derive - ce->values_raw[i].derive;
+
+         ce->values_gauge[i] = ((double) diff)
+           / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time));
+         ce->values_raw[i].derive = vl->values[i].derive;
+       }
+       break;
+
+      case DS_TYPE_ABSOLUTE:
+       ce->values_gauge[i] = ((double) vl->values[i].absolute)
+         / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time));
+       ce->values_raw[i].absolute = vl->values[i].absolute;
+       break;
+
+      default:
+       /* This shouldn't happen. */
+       pthread_mutex_unlock (&cache_lock);
+       ERROR ("uc_update: Don't know how to handle data source type %i.",
+           ds->ds[i].type);
+       return (-1);
+    } /* switch (ds->ds[i].type) */
+
+    DEBUG ("uc_update: %s: ds[%i] = %lf", name, i, ce->values_gauge[i]);
+  } /* for (i) */
+
+  /* Update the history if it exists. */
+  if (ce->history != NULL)
+  {
+    assert (ce->history_index < ce->history_length);
+    for (i = 0; i < ce->values_num; i++)
+    {
+      size_t hist_idx = (ce->values_num * ce->history_index) + i;
+      ce->history[hist_idx] = ce->values_gauge[i];
+    }
+
+    assert (ce->history_length > 0);
+    ce->history_index = (ce->history_index + 1) % ce->history_length;
+  }
+
+  /* Prune invalid gauge data */
+  uc_check_range (ds, ce);
+
+  ce->last_time = vl->time;
+  ce->last_update = cdtime ();
+  ce->interval = vl->interval;
+
+  pthread_mutex_unlock (&cache_lock);
+
+  return (0);
+} /* int uc_update */
+
+int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_values_num)
+{
+  gauge_t *ret = NULL;
+  size_t ret_num = 0;
+  cache_entry_t *ce = NULL;
+  int status = 0;
+
+  pthread_mutex_lock (&cache_lock);
+
+  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
+  {
+    assert (ce != NULL);
+
+    /* remove missing values from getval */
+    if (ce->state == STATE_MISSING)
+    {
+      status = -1;
+    }
+    else
+    {
+      ret_num = ce->values_num;
+      ret = (gauge_t *) malloc (ret_num * sizeof (gauge_t));
+      if (ret == NULL)
+      {
+        ERROR ("utils_cache: uc_get_rate_by_name: malloc failed.");
+        status = -1;
+      }
+      else
+      {
+        memcpy (ret, ce->values_gauge, ret_num * sizeof (gauge_t));
+      }
+    }
+  }
+  else
+  {
+    DEBUG ("utils_cache: uc_get_rate_by_name: No such value: %s", name);
+    status = -1;
+  }
+
+  pthread_mutex_unlock (&cache_lock);
+
+  if (status == 0)
+  {
+    *ret_values = ret;
+    *ret_values_num = ret_num;
+  }
+
+  return (status);
+} /* gauge_t *uc_get_rate_by_name */
+
+gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl)
+{
+  char name[6 * DATA_MAX_NAME_LEN];
+  gauge_t *ret = NULL;
+  size_t ret_num = 0;
+  int status;
+
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
+  {
+    ERROR ("utils_cache: uc_get_rate: FORMAT_VL failed.");
+    return (NULL);
+  }
+
+  status = uc_get_rate_by_name (name, &ret, &ret_num);
+  if (status != 0)
+    return (NULL);
+
+  /* This is important - the caller has no other way of knowing how many
+   * values are returned. */
+  if (ret_num != (size_t) ds->ds_num)
+  {
+    ERROR ("utils_cache: uc_get_rate: ds[%s] has %i values, "
+       "but uc_get_rate_by_name returned %zu.",
+       ds->type, ds->ds_num, ret_num);
+    sfree (ret);
+    return (NULL);
+  }
+
+  return (ret);
+} /* gauge_t *uc_get_rate */
+
+size_t uc_get_size() {
+  size_t size_arrays = 0;
+
+  pthread_mutex_lock (&cache_lock);
+  size_arrays = (size_t) c_avl_size (cache_tree);
+  pthread_mutex_unlock (&cache_lock);
+
+  return (size_arrays);
+}
+
+int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number)
+{
+  c_avl_iterator_t *iter;
+  char *key;
+  cache_entry_t *value;
+
+  char **names = NULL;
+  cdtime_t *times = NULL;
+  size_t number = 0;
+  size_t size_arrays = 0;
+
+  int status = 0;
+
+  if ((ret_names == NULL) || (ret_number == NULL))
+    return (-1);
+
+  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)
+  {
+    /* remove missing values when list values */
+    if (value->state == STATE_MISSING)
+      continue;
+
+    /* c_avl_size does not return a number smaller than the number of elements
+     * returned by c_avl_iterator_next. */
+    assert (number < size_arrays);
+
+    if (ret_times != NULL)
+      times[number] = value->last_time;
+
+    names[number] = strdup (key);
+    if (names[number] == NULL)
+    {
+      status = -1;
+      break;
+    }
+
+    number++;
+  } /* while (c_avl_iterator_next) */
+
+  c_avl_iterator_destroy (iter);
+  pthread_mutex_unlock (&cache_lock);
+
+  if (status != 0)
+  {
+    size_t i;
+    
+    for (i = 0; i < number; i++)
+    {
+      sfree (names[i]);
+    }
+    sfree (names);
+
+    return (-1);
+  }
+
+  *ret_names = names;
+  if (ret_times != NULL)
+    *ret_times = times;
+  *ret_number = number;
+
+  return (0);
+} /* int uc_get_names */
+
+int uc_get_state (const data_set_t *ds, const value_list_t *vl)
+{
+  char name[6 * DATA_MAX_NAME_LEN];
+  cache_entry_t *ce = NULL;
+  int ret = STATE_ERROR;
+
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
+  {
+    ERROR ("uc_get_state: FORMAT_VL failed.");
+    return (STATE_ERROR);
+  }
+
+  pthread_mutex_lock (&cache_lock);
+
+  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
+  {
+    assert (ce != NULL);
+    ret = ce->state;
+  }
+
+  pthread_mutex_unlock (&cache_lock);
+
+  return (ret);
+} /* int uc_get_state */
+
+int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state)
+{
+  char name[6 * DATA_MAX_NAME_LEN];
+  cache_entry_t *ce = NULL;
+  int ret = -1;
+
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
+  {
+    ERROR ("uc_get_state: FORMAT_VL failed.");
+    return (STATE_ERROR);
+  }
+
+  pthread_mutex_lock (&cache_lock);
+
+  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
+  {
+    assert (ce != NULL);
+    ret = ce->state;
+    ce->state = state;
+  }
+
+  pthread_mutex_unlock (&cache_lock);
+
+  return (ret);
+} /* int uc_set_state */
+
+int uc_get_history_by_name (const char *name,
+    gauge_t *ret_history, size_t num_steps, size_t num_ds)
+{
+  cache_entry_t *ce = NULL;
+  size_t i;
+  int status = 0;
+
+  pthread_mutex_lock (&cache_lock);
+
+  status = c_avl_get (cache_tree, name, (void *) &ce);
+  if (status != 0)
+  {
+    pthread_mutex_unlock (&cache_lock);
+    return (-ENOENT);
+  }
+
+  if (((size_t) ce->values_num) != num_ds)
+  {
+    pthread_mutex_unlock (&cache_lock);
+    return (-EINVAL);
+  }
+
+  /* Check if there are enough values available. If not, increase the buffer
+   * size. */
+  if (ce->history_length < num_steps)
+  {
+    gauge_t *tmp;
+    size_t i;
+
+    tmp = realloc (ce->history, sizeof (*ce->history)
+       * num_steps * ce->values_num);
+    if (tmp == NULL)
+    {
+      pthread_mutex_unlock (&cache_lock);
+      return (-ENOMEM);
+    }
+
+    for (i = ce->history_length * ce->values_num;
+       i < (num_steps * ce->values_num);
+       i++)
+      tmp[i] = NAN;
+
+    ce->history = tmp;
+    ce->history_length = num_steps;
+  } /* if (ce->history_length < num_steps) */
+
+  /* Copy the values to the output buffer. */
+  for (i = 0; i < num_steps; i++)
+  {
+    size_t src_index;
+    size_t dst_index;
+
+    if (i < ce->history_index)
+      src_index = ce->history_index - (i + 1);
+    else
+      src_index = ce->history_length + ce->history_index - (i + 1);
+    src_index = src_index * num_ds;
+
+    dst_index = i * num_ds;
+
+    memcpy (ret_history + dst_index, ce->history + src_index,
+       sizeof (*ret_history) * num_ds);
+  }
+
+  pthread_mutex_unlock (&cache_lock);
+
+  return (0);
+} /* int uc_get_history_by_name */
+
+int uc_get_history (const data_set_t *ds, const value_list_t *vl,
+    gauge_t *ret_history, size_t num_steps, size_t num_ds)
+{
+  char name[6 * DATA_MAX_NAME_LEN];
+
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
+  {
+    ERROR ("utils_cache: uc_get_history: FORMAT_VL failed.");
+    return (-1);
+  }
+
+  return (uc_get_history_by_name (name, ret_history, num_steps, num_ds));
+} /* int uc_get_history */
+
+int uc_get_hits (const data_set_t *ds, const value_list_t *vl)
+{
+  char name[6 * DATA_MAX_NAME_LEN];
+  cache_entry_t *ce = NULL;
+  int ret = STATE_ERROR;
+
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
+  {
+    ERROR ("uc_get_state: FORMAT_VL failed.");
+    return (STATE_ERROR);
+  }
+
+  pthread_mutex_lock (&cache_lock);
+
+  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
+  {
+    assert (ce != NULL);
+    ret = ce->hits;
+  }
+
+  pthread_mutex_unlock (&cache_lock);
+
+  return (ret);
+} /* int uc_get_hits */
+
+int uc_set_hits (const data_set_t *ds, const value_list_t *vl, int hits)
+{
+  char name[6 * DATA_MAX_NAME_LEN];
+  cache_entry_t *ce = NULL;
+  int ret = -1;
+
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
+  {
+    ERROR ("uc_get_state: FORMAT_VL failed.");
+    return (STATE_ERROR);
+  }
+
+  pthread_mutex_lock (&cache_lock);
+
+  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
+  {
+    assert (ce != NULL);
+    ret = ce->hits;
+    ce->hits = hits;
+  }
+
+  pthread_mutex_unlock (&cache_lock);
+
+  return (ret);
+} /* int uc_set_hits */
+
+int uc_inc_hits (const data_set_t *ds, const value_list_t *vl, int step)
+{
+  char name[6 * DATA_MAX_NAME_LEN];
+  cache_entry_t *ce = NULL;
+  int ret = -1;
+
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
+  {
+    ERROR ("uc_get_state: FORMAT_VL failed.");
+    return (STATE_ERROR);
+  }
+
+  pthread_mutex_lock (&cache_lock);
+
+  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
+  {
+    assert (ce != NULL);
+    ret = ce->hits;
+    ce->hits = ret + step;
+  }
+
+  pthread_mutex_unlock (&cache_lock);
+
+  return (ret);
+} /* int uc_inc_hits */
+
+/*
+ * Meta data interface
+ */
+/* XXX: This function will acquire `cache_lock' but will not free it! */
+static meta_data_t *uc_get_meta (const value_list_t *vl) /* {{{ */
+{
+  char name[6 * DATA_MAX_NAME_LEN];
+  cache_entry_t *ce = NULL;
+  int status;
+
+  status = FORMAT_VL (name, sizeof (name), vl);
+  if (status != 0)
+  {
+    ERROR ("utils_cache: uc_get_meta: FORMAT_VL failed.");
+    return (NULL);
+  }
+
+  pthread_mutex_lock (&cache_lock);
+
+  status = c_avl_get (cache_tree, name, (void *) &ce);
+  if (status != 0)
+  {
+    pthread_mutex_unlock (&cache_lock);
+    return (NULL);
+  }
+  assert (ce != NULL);
+
+  if (ce->meta == NULL)
+    ce->meta = meta_data_create ();
+
+  if (ce->meta == NULL)
+    pthread_mutex_unlock (&cache_lock);
+
+  return (ce->meta);
+} /* }}} meta_data_t *uc_get_meta */
+
+/* Sorry about this preprocessor magic, but it really makes this file much
+ * shorter.. */
+#define UC_WRAP(wrap_function) { \
+  meta_data_t *meta; \
+  int status; \
+  meta = uc_get_meta (vl); \
+  if (meta == NULL) return (-1); \
+  status = wrap_function (meta, key); \
+  pthread_mutex_unlock (&cache_lock); \
+  return (status); \
+}
+int uc_meta_data_exists (const value_list_t *vl, const char *key)
+  UC_WRAP (meta_data_exists)
+
+int uc_meta_data_delete (const value_list_t *vl, const char *key)
+  UC_WRAP (meta_data_delete)
+#undef UC_WRAP
+
+/* We need a new version of this macro because the following functions take
+ * two argumetns. */
+#define UC_WRAP(wrap_function) { \
+  meta_data_t *meta; \
+  int status; \
+  meta = uc_get_meta (vl); \
+  if (meta == NULL) return (-1); \
+  status = wrap_function (meta, key, value); \
+  pthread_mutex_unlock (&cache_lock); \
+  return (status); \
+}
+int uc_meta_data_add_string (const value_list_t *vl,
+    const char *key,
+    const char *value)
+  UC_WRAP(meta_data_add_string)
+int uc_meta_data_add_signed_int (const value_list_t *vl,
+    const char *key,
+    int64_t value)
+  UC_WRAP(meta_data_add_signed_int)
+int uc_meta_data_add_unsigned_int (const value_list_t *vl,
+    const char *key,
+    uint64_t value)
+  UC_WRAP(meta_data_add_unsigned_int)
+int uc_meta_data_add_double (const value_list_t *vl,
+    const char *key,
+    double value)
+  UC_WRAP(meta_data_add_double)
+int uc_meta_data_add_boolean (const value_list_t *vl,
+    const char *key,
+    _Bool value)
+  UC_WRAP(meta_data_add_boolean)
+
+int uc_meta_data_get_string (const value_list_t *vl,
+    const char *key,
+    char **value)
+  UC_WRAP(meta_data_get_string)
+int uc_meta_data_get_signed_int (const value_list_t *vl,
+    const char *key,
+    int64_t *value)
+  UC_WRAP(meta_data_get_signed_int)
+int uc_meta_data_get_unsigned_int (const value_list_t *vl,
+    const char *key,
+    uint64_t *value)
+  UC_WRAP(meta_data_get_unsigned_int)
+int uc_meta_data_get_double (const value_list_t *vl,
+    const char *key,
+    double *value)
+  UC_WRAP(meta_data_get_double)
+int uc_meta_data_get_boolean (const value_list_t *vl,
+    const char *key,
+    _Bool *value)
+  UC_WRAP(meta_data_get_boolean)
+#undef UC_WRAP
+
+/* vim: set sw=2 ts=8 sts=2 tw=78 : */
diff --git a/src/daemon/utils_cache.h b/src/daemon/utils_cache.h
new file mode 100644 (file)
index 0000000..ea3eb2f
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * collectd - src/utils_cache.h
+ * Copyright (C) 2007       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 UTILS_CACHE_H
+#define UTILS_CACHE_H 1
+
+#include "plugin.h"
+
+#define STATE_OKAY     0
+#define STATE_WARNING  1
+#define STATE_ERROR    2
+#define STATE_MISSING 15
+
+int uc_init (void);
+int uc_check_timeout (void);
+int uc_update (const data_set_t *ds, const value_list_t *vl);
+int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_values_num);
+gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl);
+
+size_t uc_get_size();
+int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number);
+
+int uc_get_state (const data_set_t *ds, const value_list_t *vl);
+int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state);
+int uc_get_hits (const data_set_t *ds, const value_list_t *vl);
+int uc_set_hits (const data_set_t *ds, const value_list_t *vl, int hits);
+int uc_inc_hits (const data_set_t *ds, const value_list_t *vl, int step);
+
+int uc_get_history (const data_set_t *ds, const value_list_t *vl,
+    gauge_t *ret_history, size_t num_steps, size_t num_ds);
+int uc_get_history_by_name (const char *name,
+    gauge_t *ret_history, size_t num_steps, size_t num_ds);
+
+/*
+ * Meta data interface
+ */
+int uc_meta_data_exists (const value_list_t *vl, const char *key);
+int uc_meta_data_delete (const value_list_t *vl, const char *key);
+
+int uc_meta_data_add_string (const value_list_t *vl,
+    const char *key,
+    const char *value);
+int uc_meta_data_add_signed_int (const value_list_t *vl,
+    const char *key,
+    int64_t value);
+int uc_meta_data_add_unsigned_int (const value_list_t *vl,
+    const char *key,
+    uint64_t value);
+int uc_meta_data_add_double (const value_list_t *vl,
+    const char *key,
+    double value);
+int uc_meta_data_add_boolean (const value_list_t *vl,
+    const char *key,
+    _Bool value);
+
+int uc_meta_data_get_string (const value_list_t *vl,
+    const char *key,
+    char **value);
+int uc_meta_data_get_signed_int (const value_list_t *vl,
+    const char *key,
+    int64_t *value);
+int uc_meta_data_get_unsigned_int (const value_list_t *vl,
+    const char *key,
+    uint64_t *value);
+int uc_meta_data_get_double (const value_list_t *vl,
+    const char *key,
+    double *value);
+int uc_meta_data_get_boolean (const value_list_t *vl,
+    const char *key,
+    _Bool *value);
+
+/* vim: set shiftwidth=2 softtabstop=2 tabstop=8 : */
+#endif /* !UTILS_CACHE_H */
diff --git a/src/daemon/utils_complain.c b/src/daemon/utils_complain.c
new file mode 100644 (file)
index 0000000..6193614
--- /dev/null
@@ -0,0 +1,105 @@
+/**
+ * collectd - src/utils_complain.c
+ * Copyright (C) 2006-2013  Florian octo Forster
+ * Copyright (C) 2008       Sebastian tokkee Harl
+ *
+ * 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>
+ *   Sebastian tokkee Harl <sh at tokkee.org>
+ **/
+
+#include "collectd.h"
+#include "utils_complain.h"
+#include "plugin.h"
+
+/* vcomplain returns 0 if it did not report, 1 else */
+static int vcomplain (int level, c_complain_t *c,
+               const char *format, va_list ap)
+{
+       cdtime_t now;
+       char   message[512];
+
+       now = cdtime ();
+
+       if (c->last + c->interval > now)
+               return 0;
+
+       c->last = now;
+
+       if (c->interval < plugin_get_interval ())
+               c->interval = plugin_get_interval ();
+       else
+               c->interval *= 2;
+
+       if (c->interval > TIME_T_TO_CDTIME_T (86400))
+               c->interval = TIME_T_TO_CDTIME_T (86400);
+
+       vsnprintf (message, sizeof (message), format, ap);
+       message[sizeof (message) - 1] = '\0';
+
+       plugin_log (level, "%s", message);
+       return 1;
+} /* vcomplain */
+
+void c_complain (int level, c_complain_t *c, const char *format, ...)
+{
+       va_list ap;
+
+       va_start (ap, format);
+       if (vcomplain (level, c, format, ap))
+               c->complained_once = 1;
+       va_end (ap);
+} /* c_complain */
+
+void c_complain_once (int level, c_complain_t *c, const char *format, ...)
+{
+       va_list ap;
+
+       if (c->complained_once)
+               return;
+
+       va_start (ap, format);
+       if (vcomplain (level, c, format, ap))
+               c->complained_once = 1;
+       va_end (ap);
+} /* c_complain_once */
+
+void c_do_release (int level, c_complain_t *c, const char *format, ...)
+{
+       char message[512];
+       va_list ap;
+
+       if (c->interval == 0)
+               return;
+
+       c->interval = 0;
+       c->complained_once = 0;
+
+       va_start (ap, format);
+       vsnprintf (message, sizeof (message), format, ap);
+       message[sizeof (message) - 1] = '\0';
+       va_end (ap);
+
+       plugin_log (level, "%s", message);
+} /* c_release */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
diff --git a/src/daemon/utils_complain.h b/src/daemon/utils_complain.h
new file mode 100644 (file)
index 0000000..390f961
--- /dev/null
@@ -0,0 +1,114 @@
+/**
+ * collectd - src/utils_complain.h
+ * Copyright (C) 2006-2013  Florian octo Forster
+ * Copyright (C) 2008       Sebastian tokkee Harl
+ *
+ * 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>
+ *   Sebastian tokkee Harl <sh at tokkee.org>
+ **/
+
+#ifndef UTILS_COMPLAIN_H
+#define UTILS_COMPLAIN_H 1
+
+#include "utils_time.h"
+
+typedef struct
+{
+       /* time of the last report */
+       cdtime_t last;
+
+       /* How long to wait until reporting again.
+        * 0 indicates that the complaint is no longer valid. */
+       cdtime_t interval;
+
+       _Bool complained_once;
+} c_complain_t;
+
+#define C_COMPLAIN_INIT_STATIC { 0, 0, 0 }
+#define C_COMPLAIN_INIT(c) do { \
+       (c)->last = 0; \
+       (c)->interval = 0; \
+       (c)->complained_once = 0; \
+} while (0)
+
+/*
+ * NAME
+ *   c_complain
+ *
+ * DESCRIPTION
+ *   Complain about something. This function will report a message (usually
+ *   indicating some error condition) using the collectd logging mechanism.
+ *   When this function is called again, reporting the message again will be
+ *   deferred by an increasing interval (up to one day) to prevent flooding
+ *   the logs. A call to `c_release' resets the counter.
+ *
+ * PARAMETERS
+ *   `level'  The log level passed to `plugin_log'.
+ *   `c'      Identifier for the complaint.
+ *   `format' Message format - see the documentation of printf(3).
+ */
+void c_complain (int level, c_complain_t *c, const char *format, ...);
+
+/*
+ * NAME
+ *   c_complain_once
+ *
+ * DESCRIPTION
+ *   Complain about something once. This function will not report anything
+ *   again, unless `c_release' has been called in between. If used after some
+ *   calls to `c_complain', it will report again on the next interval and stop
+ *   after that.
+ *
+ *   See `c_complain' for further details and a description of the parameters.
+ */
+void c_complain_once (int level, c_complain_t *c, const char *format, ...);
+
+/*
+ * NAME
+ *   c_would_release
+ *
+ * DESCRIPTION
+ *   Returns true if the specified complaint would be released, false else.
+ */
+#define c_would_release(c) ((c)->interval != 0)
+
+/*
+ * NAME
+ *   c_release
+ *
+ * DESCRIPTION
+ *   Release a complaint. This will report a message once, marking the
+ *   complaint as released.
+ *
+ *   See `c_complain' for a description of the parameters.
+ */
+void c_do_release (int level, c_complain_t *c, const char *format, ...);
+#define c_release(level, c, ...) \
+       do { \
+               if (c_would_release (c)) \
+                       c_do_release(level, c, __VA_ARGS__); \
+       } while (0)
+
+#endif /* UTILS_COMPLAIN_H */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
diff --git a/src/daemon/utils_heap.c b/src/daemon/utils_heap.c
new file mode 100644 (file)
index 0000000..1b5dca7
--- /dev/null
@@ -0,0 +1,230 @@
+/**
+ * collectd - src/utils_heap.c
+ * Copyright (C) 2009       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 <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include "utils_heap.h"
+
+struct c_heap_s
+{
+  pthread_mutex_t lock;
+  int (*compare) (const void *, const void *);
+
+  void **list;
+  size_t list_len; /* # entries used */
+  size_t list_size; /* # entries allocated */
+};
+
+enum reheap_direction
+{
+  DIR_UP,
+  DIR_DOWN
+};
+
+static void reheap (c_heap_t *h, size_t root, enum reheap_direction dir)
+{
+  size_t left;
+  size_t right;
+  size_t min;
+  int status;
+
+  /* Calculate the positions of the children */
+  left = (2 * root) + 1;
+  if (left >= h->list_len)
+    left = 0;
+
+  right = (2 * root) + 2;
+  if (right >= h->list_len)
+    right = 0;
+
+  /* Check which one of the children is smaller. */
+  if ((left == 0) && (right == 0))
+    return;
+  else if (left == 0)
+    min = right;
+  else if (right == 0)
+    min = left;
+  else
+  {
+    status = h->compare (h->list[left], h->list[right]);
+    if (status > 0)
+      min = right;
+    else
+      min = left;
+  }
+
+  status = h->compare (h->list[root], h->list[min]);
+  if (status <= 0)
+  {
+    /* We didn't need to change anything, so the rest of the tree should be
+     * okay now. */
+    return;
+  }
+  else /* if (status > 0) */
+  {
+    void *tmp;
+
+    tmp = h->list[root];
+    h->list[root] = h->list[min];
+    h->list[min] = tmp;
+  }
+
+  if ((dir == DIR_UP) && (root == 0))
+    return;
+
+  if (dir == DIR_UP)
+    reheap (h, (root - 1) / 2, dir);
+  else if (dir == DIR_DOWN)
+    reheap (h, min, dir);
+} /* void reheap */
+
+c_heap_t *c_heap_create (int (*compare) (const void *, const void *))
+{
+  c_heap_t *h;
+
+  if (compare == NULL)
+    return (NULL);
+
+  h = malloc (sizeof (*h));
+  if (h == NULL)
+    return (NULL);
+
+  memset (h, 0, sizeof (*h));
+  pthread_mutex_init (&h->lock, /* attr = */ NULL);
+  h->compare = compare;
+  
+  h->list = NULL;
+  h->list_len = 0;
+  h->list_size = 0;
+
+  return (h);
+} /* c_heap_t *c_heap_create */
+
+void c_heap_destroy (c_heap_t *h)
+{
+  if (h == NULL)
+    return;
+
+  h->list_len = 0;
+  h->list_size = 0;
+  free (h->list);
+  h->list = NULL;
+
+  pthread_mutex_destroy (&h->lock);
+
+  free (h);
+} /* void c_heap_destroy */
+
+int c_heap_insert (c_heap_t *h, void *ptr)
+{
+  size_t index;
+
+  if ((h == NULL) || (ptr == NULL))
+    return (-EINVAL);
+
+  pthread_mutex_lock (&h->lock);
+
+  assert (h->list_len <= h->list_size);
+  if (h->list_len == h->list_size)
+  {
+    void **tmp;
+
+    tmp = realloc (h->list, (h->list_size + 16) * sizeof (*h->list));
+    if (tmp == NULL)
+    {
+      pthread_mutex_unlock (&h->lock);
+      return (-ENOMEM);
+    }
+
+    h->list = tmp;
+    h->list_size += 16;
+  }
+
+  /* Insert the new node as a leaf. */
+  index = h->list_len;
+  h->list[index] = ptr;
+  h->list_len++;
+
+  /* Reorganize the heap from bottom up. */
+  reheap (h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
+  
+  pthread_mutex_unlock (&h->lock);
+  return (0);
+} /* int c_heap_insert */
+
+void *c_heap_get_root (c_heap_t *h)
+{
+  void *ret = NULL;
+
+  if (h == NULL)
+    return (NULL);
+
+  pthread_mutex_lock (&h->lock);
+
+  if (h->list_len == 0)
+  {
+    pthread_mutex_unlock (&h->lock);
+    return (NULL);
+  }
+  else if (h->list_len == 1)
+  {
+    ret = h->list[0];
+    h->list[0] = NULL;
+    h->list_len = 0;
+  }
+  else /* if (h->list_len > 1) */
+  {
+    ret = h->list[0];
+    h->list[0] = h->list[h->list_len - 1];
+    h->list[h->list_len - 1] = NULL;
+    h->list_len--;
+
+    reheap (h, /* root = */ 0, DIR_DOWN);
+  }
+
+  /* free some memory */
+  if ((h->list_len + 32) < h->list_size)
+  {
+    void **tmp;
+
+    tmp = realloc (h->list, (h->list_len + 16) * sizeof (*h->list));
+    if (tmp != NULL)
+    {
+      h->list = tmp;
+      h->list_size = h->list_len + 16;
+    }
+  }
+
+  pthread_mutex_unlock (&h->lock);
+
+  return (ret);
+} /* void *c_heap_get_root */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/daemon/utils_heap.h b/src/daemon/utils_heap.h
new file mode 100644 (file)
index 0000000..6d71c43
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * collectd - src/utils_heap.h
+ * Copyright (C) 2009       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 UTILS_HEAP_H
+#define UTILS_HEAP_H 1
+
+struct c_heap_s;
+typedef struct c_heap_s c_heap_t;
+
+/*
+ * NAME
+ *   c_heap_create
+ *
+ * DESCRIPTION
+ *   Allocates a new heap.
+ *
+ * PARAMETERS
+ *   `compare'  The function-pointer `compare' is used to compare two keys. It
+ *              has to return less than zero if it's first argument is smaller
+ *              then the second argument, more than zero if the first argument
+ *              is bigger than the second argument and zero if they are equal.
+ *              If your keys are char-pointers, you can use the `strcmp'
+ *              function from the libc here.
+ *
+ * RETURN VALUE
+ *   A c_heap_t-pointer upon success or NULL upon failure.
+ */
+c_heap_t *c_heap_create (int (*compare) (const void *, const void *));
+
+/*
+ * NAME
+ *   c_heap_destroy
+ *
+ * DESCRIPTION
+ *   Deallocates a heap. Stored value- and key-pointer are lost, but of course
+ *   not freed.
+ */
+void c_heap_destroy (c_heap_t *h);
+
+/*
+ * NAME
+ *   c_heap_insert
+ *
+ * DESCRIPTION
+ *   Stores the key-value-pair in the heap pointed to by `h'.
+ *
+ * PARAMETERS
+ *   `h'        Heap to store the data in.
+ *   `ptr'      Value to be stored. This is typically a pointer to a data
+ *              structure. The data structure is of course *not* copied and may
+ *              not be free'd before the pointer has been removed from the heap
+ *              again.
+ *
+ * RETURN VALUE
+ *   Zero upon success, non-zero otherwise. It's less than zero if an error
+ *   occurred or greater than zero if the key is already stored in the tree.
+ */
+int c_heap_insert (c_heap_t *h, void *ptr);
+
+/*
+ * NAME
+ *   c_heap_get_root
+ *
+ * DESCRIPTION
+ *   Removes the value at the root of the heap and returns both, key and value.
+ *
+ * PARAMETERS
+ *   `h'           Heap to remove key-value-pair from.
+ *
+ * RETURN VALUE
+ *   The pointer passed to `c_heap_insert' or NULL if there are no more
+ *   elements in the heap (or an error occurred).
+ */
+void *c_heap_get_root (c_heap_t *h);
+
+#endif /* UTILS_HEAP_H */
+/* vim: set sw=2 sts=2 et : */
diff --git a/src/daemon/utils_llist.c b/src/daemon/utils_llist.c
new file mode 100644 (file)
index 0000000..09c9834
--- /dev/null
@@ -0,0 +1,190 @@
+/**
+ * collectd - src/utils_llist.c
+ * Copyright (C) 2006       Florian Forster <octo at collectd.org>
+ *
+ * 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 "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils_llist.h"
+
+/*
+ * Private data types
+ */
+struct llist_s
+{
+       llentry_t *head;
+       llentry_t *tail;
+       int size;
+};
+
+/*
+ * Public functions
+ */
+llist_t *llist_create (void)
+{
+       llist_t *ret;
+
+       ret = (llist_t *) malloc (sizeof (llist_t));
+       if (ret == NULL)
+               return (NULL);
+
+       memset (ret, '\0', sizeof (llist_t));
+
+       return (ret);
+}
+
+void llist_destroy (llist_t *l)
+{
+       llentry_t *e_this;
+       llentry_t *e_next;
+
+       if (l == NULL)
+               return;
+
+       for (e_this = l->head; e_this != NULL; e_this = e_next)
+       {
+               e_next = e_this->next;
+               llentry_destroy (e_this);
+       }
+
+       free (l);
+}
+
+llentry_t *llentry_create (char *key, void *value)
+{
+       llentry_t *e;
+
+       e = (llentry_t *) malloc (sizeof (llentry_t));
+       if (e)
+       {
+               e->key   = key;
+               e->value = value;
+               e->next  = NULL;
+       }
+
+       return (e);
+}
+
+void llentry_destroy (llentry_t *e)
+{
+       free (e);
+}
+
+void llist_append (llist_t *l, llentry_t *e)
+{
+       e->next = NULL;
+
+       if (l->tail == NULL)
+               l->head = e;
+       else
+               l->tail->next = e;
+
+       l->tail = e;
+
+       ++(l->size);
+}
+
+void llist_prepend (llist_t *l, llentry_t *e)
+{
+       e->next = l->head;
+       l->head = e;
+
+       if (l->tail == NULL)
+               l->tail = e;
+
+       ++(l->size);
+}
+
+void llist_remove (llist_t *l, llentry_t *e)
+{
+       llentry_t *prev;
+
+       prev = l->head;
+       while ((prev != NULL) && (prev->next != e))
+               prev = prev->next;
+
+       if (prev != NULL)
+               prev->next = e->next;
+       if (l->head == e)
+               l->head = e->next;
+       if (l->tail == e)
+               l->tail = prev;
+
+       --(l->size);
+}
+
+int llist_size (llist_t *l)
+{
+       return (l ? l->size : 0);
+}
+
+static int llist_strcmp (llentry_t *e, void *ud)
+{
+       if ((e == NULL) || (ud == NULL))
+               return (-1);
+       return (strcmp (e->key, (const char *)ud));
+}
+
+llentry_t *llist_search (llist_t *l, const char *key)
+{
+       return (llist_search_custom (l, llist_strcmp, (void *)key));
+}
+
+llentry_t *llist_search_custom (llist_t *l,
+               int (*compare) (llentry_t *, void *), void *user_data)
+{
+       llentry_t *e;
+
+       if (l == NULL)
+               return (NULL);
+
+       e = l->head;
+       while (e != NULL) {
+               llentry_t *next = e->next;
+
+               if (compare (e, user_data) == 0)
+                       break;
+
+               e = next;
+       }
+
+       return (e);
+}
+
+llentry_t *llist_head (llist_t *l)
+{
+       if (l == NULL)
+               return (NULL);
+       return (l->head);
+}
+
+llentry_t *llist_tail (llist_t *l)
+{
+       if (l == NULL)
+               return (NULL);
+       return (l->tail);
+}
diff --git a/src/daemon/utils_llist.h b/src/daemon/utils_llist.h
new file mode 100644 (file)
index 0000000..59bf2e4
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * collectd - src/utils_llist.h
+ * Copyright (C) 2006       Florian Forster <octo at collectd.org>
+ *
+ * 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_LLIST_H
+#define UTILS_LLIST_H 1
+
+/*
+ * Data types
+ */
+struct llentry_s
+{
+       char *key;
+       void *value;
+       struct llentry_s *next;
+};
+typedef struct llentry_s llentry_t;
+
+struct llist_s;
+typedef struct llist_s llist_t;
+
+/*
+ * Functions
+ */
+llist_t *llist_create (void);
+void llist_destroy (llist_t *l);
+
+llentry_t *llentry_create (char *key, void *value);
+void llentry_destroy (llentry_t *e);
+
+void llist_append (llist_t *l, llentry_t *e);
+void llist_prepend (llist_t *l, llentry_t *e);
+void llist_remove (llist_t *l, llentry_t *e);
+
+int llist_size (llist_t *l);
+
+llentry_t *llist_search (llist_t *l, const char *key);
+llentry_t *llist_search_custom (llist_t *l,
+               int (*compare) (llentry_t *, void *), void *user_data);
+
+llentry_t *llist_head (llist_t *l);
+llentry_t *llist_tail (llist_t *l);
+
+#endif /* UTILS_LLIST_H */
diff --git a/src/daemon/utils_match.c b/src/daemon/utils_match.c
new file mode 100644 (file)
index 0000000..5083b05
--- /dev/null
@@ -0,0 +1,396 @@
+/**
+ * collectd - src/utils_match.c
+ * Copyright (C) 2008-2014  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 "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include "utils_match.h"
+
+#include <regex.h>
+
+#define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
+#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
+
+struct cu_match_s
+{
+  regex_t regex;
+  regex_t excluderegex;
+  int flags;
+
+  int (*callback) (const char *str, char * const *matches, size_t matches_num,
+      void *user_data);
+  void *user_data;
+};
+
+/*
+ * Private functions
+ */
+static char *match_substr (const char *str, int begin, int end)
+{
+  char *ret;
+  size_t ret_len;
+
+  if ((begin < 0) || (end < 0) || (begin >= end))
+    return (NULL);
+  if ((size_t) end > (strlen (str) + 1))
+  {
+    ERROR ("utils_match: match_substr: `end' points after end of string.");
+    return (NULL);
+  }
+
+  ret_len = end - begin;
+  ret = (char *) malloc (sizeof (char) * (ret_len + 1));
+  if (ret == NULL)
+  {
+    ERROR ("utils_match: match_substr: malloc failed.");
+    return (NULL);
+  }
+
+  sstrncpy (ret, str + begin, ret_len + 1);
+  return (ret);
+} /* char *match_substr */
+
+static int default_callback (const char __attribute__((unused)) *str,
+    char * const *matches, size_t matches_num, void *user_data)
+{
+  cu_match_value_t *data = (cu_match_value_t *) user_data;
+
+  if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
+  {
+    gauge_t value;
+    char *endptr = NULL;
+
+    if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC)
+    {
+      data->value.gauge = isnan (data->value.gauge) ? 1 : data->value.gauge + 1;
+      data->values_num++;
+      return(0);
+    }
+
+    if (matches_num < 2)
+      return (-1);
+
+    value = (gauge_t) strtod (matches[1], &endptr);
+    if (matches[1] == endptr)
+      return (-1);
+
+    if ((data->values_num == 0)
+       || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST))
+    {
+      data->value.gauge = value;
+    }
+    else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE)
+    {
+      double f = ((double) data->values_num)
+       / ((double) (data->values_num + 1));
+      data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
+    }
+    else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN)
+    {
+      if (data->value.gauge > value)
+       data->value.gauge = value;
+    }
+    else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX)
+    {
+      if (data->value.gauge < value)
+       data->value.gauge = value;
+    }
+    else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD)
+    {
+      data->value.gauge += value;
+    }
+    else
+    {
+      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+      return (-1);
+    }
+
+    data->values_num++;
+  }
+  else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER)
+  {
+    counter_t value;
+    char *endptr = NULL;
+
+    if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
+    {
+      data->value.counter++;
+      data->values_num++;
+      return (0);
+    }
+
+    if (matches_num < 2)
+      return (-1);
+
+    value = (counter_t) strtoull (matches[1], &endptr, 0);
+    if (matches[1] == endptr)
+      return (-1);
+
+    if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
+      data->value.counter = value;
+    else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
+      data->value.counter += value;
+    else
+    {
+      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+      return (-1);
+    }
+
+    data->values_num++;
+  }
+  else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE)
+  {
+    derive_t value;
+    char *endptr = NULL;
+
+    if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
+    {
+      data->value.counter++;
+      data->values_num++;
+      return (0);
+    }
+
+    if (matches_num < 2)
+      return (-1);
+
+    value = (derive_t) strtoll (matches[1], &endptr, 0);
+    if (matches[1] == endptr)
+      return (-1);
+
+    if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
+      data->value.derive = value;
+    else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
+      data->value.derive += value;
+    else
+    {
+      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+      return (-1);
+    }
+
+    data->values_num++;
+  }
+  else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE)
+  {
+    absolute_t value;
+    char *endptr = NULL;
+
+    if (matches_num < 2)
+      return (-1);
+
+    value = (absolute_t) strtoull (matches[1], &endptr, 0);
+    if (matches[1] == endptr)
+      return (-1);
+
+    if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
+      data->value.absolute = value;
+    else
+    {
+      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+      return (-1);
+    }
+
+    data->values_num++;
+  }
+  else
+  {
+    ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+    return (-1);
+  }
+
+  return (0);
+} /* int default_callback */
+
+/*
+ * Public functions
+ */
+cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
+               int (*callback) (const char *str,
+                 char * const *matches, size_t matches_num, void *user_data),
+               void *user_data)
+{
+  cu_match_t *obj;
+  int status;
+
+  DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
+        regex, excluderegex);
+
+  obj = (cu_match_t *) malloc (sizeof (cu_match_t));
+  if (obj == NULL)
+    return (NULL);
+  memset (obj, '\0', sizeof (cu_match_t));
+
+  status = regcomp (&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
+  if (status != 0)
+  {
+    ERROR ("Compiling the regular expression \"%s\" failed.", regex);
+    sfree (obj);
+    return (NULL);
+  }
+
+  if (excluderegex && strcmp(excluderegex, "") != 0) {
+    status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED);
+    if (status != 0)
+    {
+       ERROR ("Compiling the excluding regular expression \"%s\" failed.",
+              excluderegex);
+       sfree (obj);
+       return (NULL);
+    }
+    obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
+  }
+
+  obj->callback = callback;
+  obj->user_data = user_data;
+
+  return (obj);
+} /* cu_match_t *match_create_callback */
+
+cu_match_t *match_create_simple (const char *regex,
+                                const char *excluderegex, int match_ds_type)
+{
+  cu_match_value_t *user_data;
+  cu_match_t *obj;
+
+  user_data = (cu_match_value_t *) malloc (sizeof (cu_match_value_t));
+  if (user_data == NULL)
+    return (NULL);
+  memset (user_data, '\0', sizeof (cu_match_value_t));
+  user_data->ds_type = match_ds_type;
+
+  obj = match_create_callback (regex, excluderegex,
+                              default_callback, user_data);
+  if (obj == NULL)
+  {
+    sfree (user_data);
+    return (NULL);
+  }
+
+  obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
+
+  return (obj);
+} /* cu_match_t *match_create_simple */
+
+void match_value_reset (cu_match_value_t *mv)
+{
+  if (mv == NULL)
+    return;
+
+  if (mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
+  {
+    mv->value.gauge = NAN;
+    mv->values_num = 0;
+  }
+} /* }}} void match_value_reset */
+
+void match_destroy (cu_match_t *obj)
+{
+  if (obj == NULL)
+    return;
+
+  if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
+  {
+    sfree (obj->user_data);
+  }
+
+  sfree (obj);
+} /* void match_destroy */
+
+int match_apply (cu_match_t *obj, const char *str)
+{
+  int status;
+  regmatch_t re_match[32];
+  char *matches[32];
+  size_t matches_num;
+  size_t i;
+
+  if ((obj == NULL) || (str == NULL))
+    return (-1);
+
+  if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
+    status = regexec (&obj->excluderegex, str,
+                     STATIC_ARRAY_SIZE (re_match), re_match,
+                     /* eflags = */ 0);
+    /* Regex did match, so exclude this line */
+    if (status == 0) {
+      DEBUG("ExludeRegex matched, don't count that line\n");
+      return (0);
+    }
+  }
+
+  status = regexec (&obj->regex, str,
+      STATIC_ARRAY_SIZE (re_match), re_match,
+      /* eflags = */ 0);
+
+  /* Regex did not match */
+  if (status != 0)
+    return (0);
+
+  memset (matches, '\0', sizeof (matches));
+  for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
+  {
+    if ((re_match[matches_num].rm_so < 0)
+       || (re_match[matches_num].rm_eo < 0))
+      break;
+
+    matches[matches_num] = match_substr (str,
+       re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
+    if (matches[matches_num] == NULL)
+    {
+      status = -1;
+      break;
+    }
+  }
+
+  if (status != 0)
+  {
+    ERROR ("utils_match: match_apply: match_substr failed.");
+  }
+  else
+  {
+    status = obj->callback (str, matches, matches_num, obj->user_data);
+    if (status != 0)
+    {
+      ERROR ("utils_match: match_apply: callback failed.");
+    }
+  }
+
+  for (i = 0; i < matches_num; i++)
+  {
+    sfree (matches[i]);
+  }
+
+  return (status);
+} /* int match_apply */
+
+void *match_get_user_data (cu_match_t *obj)
+{
+  if (obj == NULL)
+    return (NULL);
+  return (obj->user_data);
+} /* void *match_get_user_data */
+
+/* vim: set sw=2 sts=2 ts=8 : */
diff --git a/src/daemon/utils_match.h b/src/daemon/utils_match.h
new file mode 100644 (file)
index 0000000..a1d1002
--- /dev/null
@@ -0,0 +1,175 @@
+/**
+ * collectd - src/utils_match.h
+ * Copyright (C) 2008-2014  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 UTILS_MATCH_H
+#define UTILS_MATCH_H 1
+
+#include "plugin.h"
+
+/*
+ * Each type may have 12 sub-types
+ * 0x1000 = 1000000000000
+ *          ^             <- Type bit
+ *           ^^^^^^^^^^^^ <- Subtype bits
+ */
+#define UTILS_MATCH_DS_TYPE_GAUGE    0x1000
+#define UTILS_MATCH_DS_TYPE_COUNTER  0x2000
+#define UTILS_MATCH_DS_TYPE_DERIVE   0x4000
+#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x8000
+
+#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
+#define UTILS_MATCH_CF_GAUGE_MIN     0x02
+#define UTILS_MATCH_CF_GAUGE_MAX     0x04
+#define UTILS_MATCH_CF_GAUGE_LAST    0x08
+#define UTILS_MATCH_CF_GAUGE_INC     0x10
+#define UTILS_MATCH_CF_GAUGE_ADD     0x20
+
+#define UTILS_MATCH_CF_COUNTER_SET   0x01
+#define UTILS_MATCH_CF_COUNTER_ADD   0x02
+#define UTILS_MATCH_CF_COUNTER_INC   0x04
+
+#define UTILS_MATCH_CF_DERIVE_SET   0x01
+#define UTILS_MATCH_CF_DERIVE_ADD   0x02
+#define UTILS_MATCH_CF_DERIVE_INC   0x04
+
+#define UTILS_MATCH_CF_ABSOLUTE_SET   0x01
+#define UTILS_MATCH_CF_ABSOLUTE_ADD   0x02
+#define UTILS_MATCH_CF_ABSOLUTE_INC   0x04
+
+/*
+ * Data types
+ */
+struct cu_match_s;
+typedef struct cu_match_s cu_match_t;
+
+struct cu_match_value_s
+{
+  int ds_type;
+  value_t value;
+  unsigned int values_num;
+};
+typedef struct cu_match_value_s cu_match_value_t;
+
+/*
+ * Prototypes
+ */
+/*
+ * NAME
+ *  match_create_callback
+ *
+ * DESCRIPTION
+ *  Creates a new `cu_match_t' object which will use the regular expression
+ *  `regex' to match lines, see the `match_apply' method below. If the line
+ *  matches, the callback passed in `callback' will be called along with the
+ *  pointer `user_pointer'.
+ *  The string that's passed to the callback depends on the regular expression:
+ *  If the regular expression includes a sub-match, i. e. something like
+ *    "value=([0-9][0-9]*)"
+ *  then only the submatch (the part in the parenthesis) will be passed to the
+ *  callback. If there is no submatch, then the entire string is passed to the
+ *  callback.
+ *  The optional `excluderegex' allows to exclude the line from the match, if
+ *  the excluderegex matches.
+ */
+cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
+               int (*callback) (const char *str,
+                 char * const *matches, size_t matches_num, void *user_data),
+               void *user_data);
+
+/*
+ * NAME
+ *  match_create_simple
+ *
+ * DESCRIPTION
+ *  Creates a new `cu_match_t' with a default callback. The user data for that
+ *  default callback will be a `cu_match_value_t' structure, with
+ *  `ds_type' copied to the structure. The default callback will handle the
+ *  string as containing a number (see strtoll(3) and strtod(3)) and store that
+ *  number in the `value' member. How that is done depends on `ds_type':
+ *
+ *  UTILS_MATCH_DS_TYPE_GAUGE
+ *    The function will search for a floating point number in the string and
+ *    store it in value.gauge.
+ *  UTILS_MATCH_DS_TYPE_COUNTER_SET
+ *    The function will search for an integer in the string and store it in
+ *    value.counter.
+ *  UTILS_MATCH_DS_TYPE_COUNTER_ADD
+ *    The function will search for an integer in the string and add it to the
+ *    value in value.counter.
+ *  UTILS_MATCH_DS_TYPE_COUNTER_INC
+ *    The function will not search for anything in the string and increase
+ *    value.counter by one.
+ */
+cu_match_t *match_create_simple (const char *regex,
+                                const char *excluderegex, int ds_type);
+
+/*
+ * NAME
+ *  match_value_reset
+ *
+ * DESCRIPTION
+ *   Resets the internal state, if applicable. This function must be called
+ *   after each iteration for "simple" matches, usually after dispatching the
+ *   metrics.
+ */
+void match_value_reset (cu_match_value_t *mv);
+
+/*
+ * NAME
+ *  match_destroy
+ *
+ * DESCRIPTION
+ *  Destroys the object and frees all internal resources.
+ */
+void match_destroy (cu_match_t *obj);
+
+/*
+ * NAME
+ *  match_apply
+ *
+ * DESCRIPTION
+ *  Tries to match the string `str' with the regular expression of `obj'. If
+ *  the string matches, calls the callback in `obj' with the (sub-)match.
+ *
+ *  The user_data pointer passed to `match_create_callback' is NOT freed
+ *  automatically. The `cu_match_value_t' structure allocated by
+ *  `match_create_callback' is freed automatically.
+ */
+int match_apply (cu_match_t *obj, const char *str);
+
+/*
+ * NAME
+ *  match_get_user_data
+ *
+ * DESCRIPTION
+ *  Returns the pointer passed to `match_create_callback' or a pointer to the
+ *  `cu_match_value_t' structure allocated by `match_create_simple'.
+ */
+void *match_get_user_data (cu_match_t *obj);
+
+#endif /* UTILS_MATCH_H */
+
+/* vim: set sw=2 sts=2 ts=8 : */
diff --git a/src/daemon/utils_random.c b/src/daemon/utils_random.c
new file mode 100644 (file)
index 0000000..b873845
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * collectd - src/utils_random.c
+ * Copyright (C) 2013       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_time.h"
+
+#include <pthread.h>
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static _Bool have_seed = 0;
+static unsigned short seed[3];
+
+static void cdrand_seed (void)
+{
+  cdtime_t t;
+
+  if (have_seed)
+    return;
+
+  t = cdtime();
+
+  seed[0] = (unsigned short) t;
+  seed[1] = (unsigned short) (t >> 16);
+  seed[2] = (unsigned short) (t >> 32);
+
+  have_seed = 1;
+}
+
+double cdrand_d (void)
+{
+  double r;
+
+  pthread_mutex_lock (&lock);
+  cdrand_seed ();
+  r = erand48 (seed);
+  pthread_mutex_unlock (&lock);
+
+  return (r);
+}
+
+long cdrand_range (long min, long max)
+{
+  long range;
+  long r;
+
+  range = 1 + max - min;
+
+  r = (long) (0.5 + (cdrand_d () * range));
+  r += min;
+
+  return (r);
+}
diff --git a/src/daemon/utils_random.h b/src/daemon/utils_random.h
new file mode 100644 (file)
index 0000000..b05f4c8
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * collectd - src/utils_random.h
+ * Copyright (C) 2013       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>
+ **/
+
+/**
+ * Returns a random double value in the range [0..1), i.e. excluding 1.
+ *
+ * This function is thread- and reentrant-safe.
+ */
+double cdrand_d (void);
+
+/**
+ * Returns a random long between min and max, inclusively.
+ *
+ * If min is larger than max, the result may be rounded incorrectly and may be
+ * outside the intended range. This function is thread- and reentrant-safe.
+ */
+long cdrand_range (long min, long max);
diff --git a/src/daemon/utils_subst.c b/src/daemon/utils_subst.c
new file mode 100644 (file)
index 0000000..2f28eb9
--- /dev/null
@@ -0,0 +1,149 @@
+/**
+ * collectd - src/utils_subst.c
+ * Copyright (C) 2008       Sebastian Harl
+ *
+ * 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:
+ *   Sebastian "tokkee" Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This module provides functions for string substitution.
+ */
+
+#include "collectd.h"
+#include "common.h"
+
+char *subst (char *buf, size_t buflen, const char *string, int off1, int off2,
+               const char *replacement)
+{
+       char  *buf_ptr = buf;
+       size_t len     = buflen;
+
+       if ((NULL == buf) || (0 >= buflen) || (NULL == string)
+                       || (0 > off1) || (0 > off2) || (off1 > off2)
+                       || (NULL == replacement))
+               return NULL;
+
+       sstrncpy (buf_ptr, string,
+                       ((size_t)off1 + 1 > buflen) ? buflen : (size_t)off1 + 1);
+       buf_ptr += off1;
+       len     -= off1;
+
+       if (0 >= len)
+               return buf;
+
+       sstrncpy (buf_ptr, replacement, len);
+       buf_ptr += strlen (replacement);
+       len     -= strlen (replacement);
+
+       if (0 >= len)
+               return buf;
+
+       sstrncpy (buf_ptr, string + off2, len);
+       return buf;
+} /* subst */
+
+char *asubst (const char *string, int off1, int off2, const char *replacement)
+{
+       char *buf;
+       int   len;
+
+       char *ret;
+
+       if ((NULL == string) || (0 > off1) || (0 > off2) || (off1 > off2)
+                       || (NULL ==replacement))
+               return NULL;
+
+       len = off1 + strlen (replacement) + strlen (string) - off2 + 1;
+
+       buf = (char *)malloc (len);
+       if (NULL == buf)
+               return NULL;
+
+       ret = subst (buf, len, string, off1, off2, replacement);
+       if (NULL == ret)
+               free (buf);
+       return ret;
+} /* asubst */
+
+char *subst_string (char *buf, size_t buflen, const char *string,
+               const char *needle, const char *replacement)
+{
+       char *temp;
+       size_t needle_len;
+       size_t i;
+
+       if ((buf == NULL) || (string == NULL)
+                       || (needle == NULL) || (replacement == NULL))
+               return (NULL);
+
+       temp = (char *) malloc (buflen);
+       if (temp == NULL)
+       {
+               ERROR ("subst_string: malloc failed.");
+               return (NULL);
+       }
+
+       needle_len = strlen (needle);
+       strncpy (buf, string, buflen);
+
+       /* Limit the loop to prevent endless loops. */
+       for (i = 0; i < buflen; i++)
+       {
+               char *begin_ptr;
+               size_t begin;
+
+               /* Find `needle' in `buf'. */
+               begin_ptr = strstr (buf, needle);
+               if (begin_ptr == NULL)
+                       break;
+
+               /* Calculate the start offset. */
+               begin = begin_ptr - buf;
+
+               /* Substitute the region using `subst'. The result is stored in
+                * `temp'. */
+               begin_ptr = subst (temp, buflen, buf,
+                               begin, begin + needle_len,
+                               replacement);
+               if (begin_ptr == NULL)
+               {
+                       WARNING ("subst_string: subst failed.");
+                       break;
+               }
+
+               /* Copy the new string in `temp' to `buf' for the next round. */
+               strncpy (buf, temp, buflen);
+       }
+
+       if (i >= buflen)
+       {
+               WARNING ("subst_string: Loop exited after %zu iterations: "
+                               "string = %s; needle = %s; replacement = %s;",
+                               i, string, needle, replacement);
+       }
+
+       sfree (temp);
+       return (buf);
+} /* char *subst_string */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
diff --git a/src/daemon/utils_subst.h b/src/daemon/utils_subst.h
new file mode 100644 (file)
index 0000000..9085286
--- /dev/null
@@ -0,0 +1,105 @@
+/**
+ * collectd - src/utils_subst.h
+ * Copyright (C) 2008       Sebastian Harl
+ *
+ * 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:
+ *   Sebastian "tokkee" Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This module provides functions for string substitution.
+ */
+
+#ifndef UTILS_SUBST_H
+#define UTILS_SUBST_H 1
+
+#include <stddef.h>
+
+/*
+ * subst:
+ *
+ * Replace a substring of a string with the specified replacement text. The
+ * resulting string is stored in the buffer pointed to by 'buf' of length
+ * 'buflen'. Upon success, the buffer will always be null-terminated. The
+ * result may be truncated if the buffer is too small.
+ *
+ * The substring to be replaces is identified by the two offsets 'off1' and
+ * 'off2' where 'off1' specifies the offset to the beginning of the substring
+ * and 'off2' specifies the offset to the first byte after the substring.
+ *
+ * The minimum buffer size to store the complete return value (including the
+ * terminating '\0' character) thus has to be:
+ * off1 + strlen(replacement) + strlen(string) - off2 + 1
+ *
+ * Example:
+ *
+ *             01234567890
+ *   string = "foo_____bar"
+ *                ^    ^
+ *                |    |
+ *              off1  off2
+ *
+ *   off1 = 3
+ *   off2 = 8
+ *
+ *   replacement = " - "
+ *
+ *   -> "foo - bar"
+ *
+ * The function returns 'buf' on success, NULL else.
+ */
+char *subst (char *buf, size_t buflen, const char *string, int off1, int off2,
+               const char *replacement);
+
+/*
+ * asubst:
+ *
+ * This function is very similar to subst(). It differs in that it
+ * automatically allocates the memory required for the return value which the
+ * user then has to free himself.
+ *
+ * Returns the newly allocated result string on success, NULL else.
+ */
+char *asubst (const char *string, int off1, int off2, const char *replacement);
+
+/*
+ * subst_string:
+ *
+ * Works like `subst', but instead of specifying start and end offsets you
+ * specify `needle', the string that is to be replaced. If `needle' is found
+ * in `string' (using strstr(3)), the offset is calculated and `subst' is
+ * called with the determined parameters.
+ *
+ * If the substring is not found, no error will be indicated and
+ * `subst_string' works mostly like `strncpy'.
+ *
+ * If the substring appears multiple times, all appearances will be replaced.
+ * If the substring has been found `buflen' times, an endless loop is assumed
+ * and the loop is broken. A warning is printed and the function returns
+ * success.
+ */
+char *subst_string (char *buf, size_t buflen, const char *string,
+               const char *needle, const char *replacement);
+
+#endif /* UTILS_SUBST_H */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
diff --git a/src/daemon/utils_tail.c b/src/daemon/utils_tail.c
new file mode 100644 (file)
index 0000000..fe5dca8
--- /dev/null
@@ -0,0 +1,260 @@
+/**
+ * collectd - src/utils_tail.c
+ * Copyright (C) 2007-2008  C-Ware, Inc.
+ * Copyright (C) 2008       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.
+ *
+ * Author:
+ *   Luke Heberling <lukeh at c-ware.com>
+ *   Florian Forster <octo at collectd.org>
+ *
+ * Description:
+ *   Encapsulates useful code for plugins which must watch for appends to
+ *   the end of a file.
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_tail.h"
+
+struct cu_tail_s
+{
+       char  *file;
+       FILE  *fh;
+       struct stat stat;
+};
+
+static int cu_tail_reopen (cu_tail_t *obj)
+{
+  int seek_end = 0;
+  FILE *fh;
+  struct stat stat_buf;
+  int status;
+
+  memset (&stat_buf, 0, sizeof (stat_buf));
+  status = stat (obj->file, &stat_buf);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("utils_tail: stat (%s) failed: %s", obj->file,
+       sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  /* The file is already open.. */
+  if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino))
+  {
+    /* Seek to the beginning if file was truncated */
+    if (stat_buf.st_size < obj->stat.st_size)
+    {
+      INFO ("utils_tail: File `%s' was truncated.", obj->file);
+      status = fseek (obj->fh, 0, SEEK_SET);
+      if (status != 0)
+      {
+       char errbuf[1024];
+       ERROR ("utils_tail: fseek (%s) failed: %s", obj->file,
+           sstrerror (errno, errbuf, sizeof (errbuf)));
+       fclose (obj->fh);
+       obj->fh = NULL;
+       return (-1);
+      }
+    }
+    memcpy (&obj->stat, &stat_buf, sizeof (struct stat));
+    return (1);
+  }
+
+  /* Seek to the end if we re-open the same file again or the file opened
+   * is the first at all or the first after an error */
+  if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino))
+    seek_end = 1;
+
+  fh = fopen (obj->file, "r");
+  if (fh == NULL)
+  {
+    char errbuf[1024];
+    ERROR ("utils_tail: fopen (%s) failed: %s", obj->file,
+       sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  if (seek_end != 0)
+  {
+    status = fseek (fh, 0, SEEK_END);
+    if (status != 0)
+    {
+      char errbuf[1024];
+      ERROR ("utils_tail: fseek (%s) failed: %s", obj->file,
+         sstrerror (errno, errbuf, sizeof (errbuf)));
+      fclose (fh);
+      return (-1);
+    }
+  }
+
+  if (obj->fh != NULL)
+    fclose (obj->fh);
+  obj->fh = fh;
+  memcpy (&obj->stat, &stat_buf, sizeof (struct stat));
+
+  return (0);
+} /* int cu_tail_reopen */
+
+cu_tail_t *cu_tail_create (const char *file)
+{
+       cu_tail_t *obj;
+
+       obj = (cu_tail_t *) malloc (sizeof (cu_tail_t));
+       if (obj == NULL)
+               return (NULL);
+       memset (obj, '\0', sizeof (cu_tail_t));
+
+       obj->file = strdup (file);
+       if (obj->file == NULL)
+       {
+               free (obj);
+               return (NULL);
+       }
+
+       obj->fh = NULL;
+
+       return (obj);
+} /* cu_tail_t *cu_tail_create */
+
+int cu_tail_destroy (cu_tail_t *obj)
+{
+       if (obj->fh != NULL)
+               fclose (obj->fh);
+       free (obj->file);
+       free (obj);
+
+       return (0);
+} /* int cu_tail_destroy */
+
+int cu_tail_readline (cu_tail_t *obj, char *buf, int buflen)
+{
+  int status;
+
+  if (buflen < 1)
+  {
+    ERROR ("utils_tail: cu_tail_readline: buflen too small: %i bytes.",
+       buflen);
+    return (-1);
+  }
+
+  if (obj->fh == NULL)
+  {
+    status = cu_tail_reopen (obj);
+    if (status < 0)
+      return (status);
+  }
+  assert (obj->fh != NULL);
+
+  /* Try to read from the filehandle. If that succeeds, everything appears to
+   * be fine and we can return. */
+  clearerr (obj->fh);
+  if (fgets (buf, buflen, obj->fh) != NULL)
+  {
+    buf[buflen - 1] = 0;
+    return (0);
+  }
+
+  /* Check if we encountered an error */
+  if (ferror (obj->fh) != 0)
+  {
+    /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */
+    fclose (obj->fh);
+    obj->fh = NULL;
+  }
+  /* else: eof -> check if the file was moved away and reopen the new file if
+   * so.. */
+
+  status = cu_tail_reopen (obj);
+  /* error -> return with error */
+  if (status < 0)
+    return (status);
+  /* file end reached and file not reopened -> nothing more to read */
+  else if (status > 0)
+  {
+    buf[0] = 0;
+    return (0);
+  }
+
+  /* If we get here: file was re-opened and there may be more to read.. Let's
+   * try again. */
+  if (fgets (buf, buflen, obj->fh) != NULL)
+  {
+    buf[buflen - 1] = 0;
+    return (0);
+  }
+
+  if (ferror (obj->fh) != 0)
+  {
+    char errbuf[1024];
+    WARNING ("utils_tail: fgets (%s) returned an error: %s", obj->file,
+       sstrerror (errno, errbuf, sizeof (errbuf)));
+    fclose (obj->fh);
+    obj->fh = NULL;
+    return (-1);
+  }
+
+  /* EOf, well, apparently the new file is empty.. */
+  buf[0] = 0;
+  return (0);
+} /* int cu_tail_readline */
+
+int cu_tail_read (cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
+               void *data)
+{
+       int status;
+
+       while (42)
+       {
+               size_t len;
+
+               status = cu_tail_readline (obj, buf, buflen);
+               if (status != 0)
+               {
+                       ERROR ("utils_tail: cu_tail_read: cu_tail_readline "
+                                       "failed.");
+                       break;
+               }
+
+               /* check for EOF */
+               if (buf[0] == 0)
+                       break;
+
+               len = strlen (buf);
+               while (len > 0) {
+                       if (buf[len - 1] != '\n')
+                               break;
+                       buf[len - 1] = '\0';
+                       len--;
+               }
+
+               status = callback (data, buf, buflen);
+               if (status != 0)
+               {
+                       ERROR ("utils_tail: cu_tail_read: callback returned "
+                                       "status %i.", status);
+                       break;
+               }
+       }
+
+       return status;
+} /* int cu_tail_read */
diff --git a/src/daemon/utils_tail.h b/src/daemon/utils_tail.h
new file mode 100644 (file)
index 0000000..6fb7013
--- /dev/null
@@ -0,0 +1,88 @@
+/**
+ * collectd - src/utils_tail.h
+ * Copyright (C) 2007-2008  C-Ware, Inc.
+ *
+ * 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.
+ *
+ * Author:
+ *   Luke Heberling <lukeh at c-ware.com>
+ *
+ * DESCRIPTION
+ *   Facilitates reading information that is appended to a file, taking into
+ *   account that the file may be rotated and a new file created under the
+ *   same name.
+ **/
+
+#ifndef UTILS_TAIL_H
+#define UTILS_TAIL_H 1
+
+struct cu_tail_s;
+typedef struct cu_tail_s cu_tail_t;
+
+typedef int tailfunc_t(void *data, char *buf, int buflen);
+
+/*
+ * NAME
+ *   cu_tail_create
+ *
+ * DESCRIPTION
+ *   Allocates a new tail object..
+ *
+ * PARAMETERS
+ *   `file'       The name of the file to be tailed.
+ */
+cu_tail_t *cu_tail_create (const char *file);
+
+/*
+ * cu_tail_destroy
+ *
+ * Takes a tail object returned by `cu_tail_create' and destroys it, freeing
+ * all internal memory.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_destroy (cu_tail_t *obj);
+
+/*
+ * cu_tail_readline
+ *
+ * Reads from the file until `buflen' characters are read, a newline
+ * character is read, or an eof condition is encountered. `buf' is
+ * always null-terminated on successful return and isn't touched when non-zero
+ * is returned.
+ *
+ * You can check if the EOF condition is reached by looking at the buffer: If
+ * the length of the string stored in the buffer is zero, EOF occurred.
+ * Otherwise at least the newline character will be in the buffer.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_readline (cu_tail_t *obj, char *buf, int buflen);
+
+/*
+ * cu_tail_readline
+ *
+ * Reads from the file until eof condition or an error is encountered.
+ *
+ * Returns 0 when successful and non-zero otherwise.
+ */
+int cu_tail_read (cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
+               void *data);
+
+#endif /* UTILS_TAIL_H */
diff --git a/src/daemon/utils_tail_match.c b/src/daemon/utils_tail_match.c
new file mode 100644 (file)
index 0000000..8776ad1
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * collectd - src/utils_tail_match.c
+ * Copyright (C) 2007-2008  C-Ware, Inc.
+ * Copyright (C) 2008       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.
+ *
+ * Author:
+ *   Luke Heberling <lukeh at c-ware.com>
+ *   Florian Forster <octo at collectd.org>
+ *
+ * Description:
+ *   Encapsulates useful code to plugins which must parse a log file.
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_match.h"
+#include "utils_tail.h"
+#include "utils_tail_match.h"
+
+struct cu_tail_match_simple_s
+{
+  char plugin[DATA_MAX_NAME_LEN];
+  char plugin_instance[DATA_MAX_NAME_LEN];
+  char type[DATA_MAX_NAME_LEN];
+  char type_instance[DATA_MAX_NAME_LEN];
+  cdtime_t interval;
+};
+typedef struct cu_tail_match_simple_s cu_tail_match_simple_t;
+
+struct cu_tail_match_match_s
+{
+  cu_match_t *match;
+  void *user_data;
+  int (*submit) (cu_match_t *match, void *user_data);
+  void (*free) (void *user_data);
+};
+typedef struct cu_tail_match_match_s cu_tail_match_match_t;
+
+struct cu_tail_match_s
+{
+  int flags;
+  cu_tail_t *tail;
+
+  cdtime_t interval;
+  cu_tail_match_match_t *matches;
+  size_t matches_num;
+};
+
+/*
+ * Private functions
+ */
+static int simple_submit_match (cu_match_t *match, void *user_data)
+{
+  cu_tail_match_simple_t *data = (cu_tail_match_simple_t *) user_data;
+  cu_match_value_t *match_value;
+  value_list_t vl = VALUE_LIST_INIT;
+  value_t values[1];
+
+  match_value = (cu_match_value_t *) match_get_user_data (match);
+  if (match_value == NULL)
+    return (-1);
+
+  if ((match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
+      && (match_value->values_num == 0))
+    values[0].gauge = NAN;
+  else
+    values[0] = match_value->value;
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, data->plugin, sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, data->plugin_instance,
+      sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, data->type, sizeof (vl.type));
+  sstrncpy (vl.type_instance, data->type_instance,
+      sizeof (vl.type_instance));
+
+  vl.interval = data->interval;
+  plugin_dispatch_values (&vl);
+
+  if (match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
+  {
+    match_value->value.gauge = NAN;
+    match_value->values_num = 0;
+  }
+
+  return (0);
+} /* int simple_submit_match */
+
+static int tail_callback (void *data, char *buf,
+    int __attribute__((unused)) buflen)
+{
+  cu_tail_match_t *obj = (cu_tail_match_t *) data;
+  size_t i;
+
+  for (i = 0; i < obj->matches_num; i++)
+    match_apply (obj->matches[i].match, buf);
+
+  return (0);
+} /* int tail_callback */
+
+/*
+ * Public functions
+ */
+cu_tail_match_t *tail_match_create (const char *filename)
+{
+  cu_tail_match_t *obj;
+
+  obj = (cu_tail_match_t *) malloc (sizeof (cu_tail_match_t));
+  if (obj == NULL)
+    return (NULL);
+  memset (obj, '\0', sizeof (cu_tail_match_t));
+
+  obj->tail = cu_tail_create (filename);
+  if (obj->tail == NULL)
+  {
+    sfree (obj);
+    return (NULL);
+  }
+
+  return (obj);
+} /* cu_tail_match_t *tail_match_create */
+
+void tail_match_destroy (cu_tail_match_t *obj)
+{
+  size_t i;
+
+  if (obj == NULL)
+    return;
+
+  if (obj->tail != NULL)
+  {
+    cu_tail_destroy (obj->tail);
+    obj->tail = NULL;
+  }
+
+  for (i = 0; i < obj->matches_num; i++)
+  {
+    cu_tail_match_match_t *match = obj->matches + i;
+    if (match->match != NULL)
+    {
+      match_destroy (match->match);
+      match->match = NULL;
+    }
+
+    if ((match->user_data != NULL)
+       && (match->free != NULL))
+      (*match->free) (match->user_data);
+    match->user_data = NULL;
+  }
+
+  sfree (obj->matches);
+  sfree (obj);
+} /* void tail_match_destroy */
+
+int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
+    int (*submit_match) (cu_match_t *match, void *user_data),
+    void *user_data,
+    void (*free_user_data) (void *user_data))
+{
+  cu_tail_match_match_t *temp;
+
+  temp = (cu_tail_match_match_t *) realloc (obj->matches,
+      sizeof (cu_tail_match_match_t) * (obj->matches_num + 1));
+  if (temp == NULL)
+    return (-1);
+
+  obj->matches = temp;
+  obj->matches_num++;
+
+  DEBUG ("tail_match_add_match interval %lf", CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval));
+  temp = obj->matches + (obj->matches_num - 1);
+
+  temp->match = match;
+  temp->user_data = user_data;
+  temp->submit = submit_match;
+  temp->free = free_user_data;
+
+  return (0);
+} /* int tail_match_add_match */
+
+int tail_match_add_match_simple (cu_tail_match_t *obj,
+    const char *regex, const char *excluderegex, int ds_type,
+    const char *plugin, const char *plugin_instance,
+    const char *type, const char *type_instance, const cdtime_t interval)
+{
+  cu_match_t *match;
+  cu_tail_match_simple_t *user_data;
+  int status;
+
+  match = match_create_simple (regex, excluderegex, ds_type);
+  if (match == NULL)
+    return (-1);
+
+  user_data = (cu_tail_match_simple_t *) malloc (sizeof (cu_tail_match_simple_t));
+  if (user_data == NULL)
+  {
+    match_destroy (match);
+    return (-1);
+  }
+  memset (user_data, '\0', sizeof (cu_tail_match_simple_t));
+
+  sstrncpy (user_data->plugin, plugin, sizeof (user_data->plugin));
+  if (plugin_instance != NULL)
+    sstrncpy (user_data->plugin_instance, plugin_instance,
+       sizeof (user_data->plugin_instance));
+
+  sstrncpy (user_data->type, type, sizeof (user_data->type));
+  if (type_instance != NULL)
+    sstrncpy (user_data->type_instance, type_instance,
+       sizeof (user_data->type_instance));
+
+  user_data->interval = interval;
+
+  status = tail_match_add_match (obj, match, simple_submit_match,
+      user_data, free);
+
+  if (status != 0)
+  {
+    match_destroy (match);
+    sfree (user_data);
+  }
+
+  return (status);
+} /* int tail_match_add_match_simple */
+
+int tail_match_read (cu_tail_match_t *obj)
+{
+  char buffer[4096];
+  int status;
+  size_t i;
+
+  status = cu_tail_read (obj->tail, buffer, sizeof (buffer), tail_callback,
+      (void *) obj);
+  if (status != 0)
+  {
+    ERROR ("tail_match: cu_tail_read failed.");
+    return (status);
+  }
+
+  for (i = 0; i < obj->matches_num; i++)
+  {
+    cu_tail_match_match_t *lt_match = obj->matches + i;
+
+    if (lt_match->submit == NULL)
+      continue;
+
+    (*lt_match->submit) (lt_match->match, lt_match->user_data);
+  }
+
+  return (0);
+} /* int tail_match_read */
+
+/* vim: set sw=2 sts=2 ts=8 : */
diff --git a/src/daemon/utils_tail_match.h b/src/daemon/utils_tail_match.h
new file mode 100644 (file)
index 0000000..0404de2
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * collectd - src/utils_tail_match.h
+ * Copyright (C) 2007-2008  C-Ware, Inc.
+ * Copyright (C) 2008       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:
+ *   Luke Heberling <lukeh at c-ware.com>
+ *   Florian Forster <octo at collectd.org>
+ *
+ * Description:
+ *   `tail_match' uses `utils_tail' and `utils_match' to tail a file and try to
+ *   match it using several regular expressions. Matches are then passed to
+ *   user-provided callback functions or default handlers. This should keep all
+ *   of the parsing logic out of the actual plugin, which only operate with
+ *   regular expressions.
+ */
+
+#include "utils_match.h"
+
+struct cu_tail_match_s;
+typedef struct cu_tail_match_s cu_tail_match_t;
+
+/*
+ * NAME
+ *   tail_match_create
+ *
+ * DESCRIPTION
+ *   Allocates, initializes and returns a new `cu_tail_match_t' object.
+ *
+ * PARAMETERS
+ *   `filename'  The name to read data from.
+ *
+ * RETURN VALUE
+ *   Returns NULL upon failure, non-NULL otherwise.
+ */
+cu_tail_match_t *tail_match_create (const char *filename);
+
+/*
+ * NAME
+ *   tail_match_destroy
+ *
+ * DESCRIPTION
+ *   Releases resources used by the `cu_tail_match_t' object.
+ *
+ * PARAMETERS
+ *   The object to destroy.
+ */
+void tail_match_destroy (cu_tail_match_t *obj);
+
+/*
+ * NAME
+ *   tail_match_add_match
+ *
+ * DESCRIPTION
+ *   Adds a match, in form of a `cu_match_t' object, to the object.
+ *   After data has been read from the logfile (using utils_tail) the callback
+ *   function `submit_match' is called with the match object and the user
+ *   supplied data.
+ *   Please note that his function is called regardless whether this match
+ *   matched any lines recently or not.
+ *   When `tail_match_destroy' is called the `user_data' pointer is freed using
+ *   the `free_user_data' callback - if it is not NULL.
+ *   When using this interface the `tail_match' module doesn't dispatch any values
+ *   itself - all that has to happen in either the match-callbacks or the
+ *   submit_match callback.
+ *
+ * RETURN VALUE
+ *   Zero upon success, non-zero otherwise.
+ */
+int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
+    int (*submit_match) (cu_match_t *match, void *user_data),
+    void *user_data,
+    void (*free_user_data) (void *user_data));
+
+/*
+ * NAME
+ *  tail_match_add_match_simple
+ *
+ * DESCRIPTION
+ *  A simplified version of `tail_match_add_match'. The regular expressen `regex'
+ *  must match a number, which is then dispatched according to `ds_type'. See
+ *  the `match_create_simple' function in utils_match.h for a description how
+ *  this flag effects calculation of a new value.
+ *  The values gathered are dispatched by the tail_match module in this case. The
+ *  passed `plugin', `plugin_instance', `type', and `type_instance' are
+ *  directly used when submitting these values.
+ *  With excluderegex it is possible to exlude lines from the match.
+ *
+ * RETURN VALUE
+ *   Zero upon success, non-zero otherwise.
+ */
+int tail_match_add_match_simple (cu_tail_match_t *obj,
+    const char *regex, const char *excluderegex, int ds_type,
+    const char *plugin, const char *plugin_instance,
+    const char *type, const char *type_instance, const cdtime_t interval);
+
+/*
+ * NAME
+ *   tail_match_read
+ *
+ * DESCRIPTION
+ *   This function should be called periodically by plugins. It reads new lines
+ *   from the logfile using `utils_tail' and tries to match them using all
+ *   added `utils_match' objects.
+ *   After all lines have been read and processed, the submit_match callback is
+ *   called or, in case of tail_match_add_match_simple, the data is dispatched to
+ *   the daemon directly.
+ *
+ * RETURN VALUE
+ *   Zero on success, nonzero on failure.
+*/
+int tail_match_read (cu_tail_match_t *obj);
+
+/* vim: set sw=2 sts=2 ts=8 : */
diff --git a/src/daemon/utils_threshold.c b/src/daemon/utils_threshold.c
new file mode 100644 (file)
index 0000000..4a8df89
--- /dev/null
@@ -0,0 +1,143 @@
+/**
+ * collectd - src/utils_threshold.c
+ * Copyright (C) 2014       Pierre-Yves Ritschard
+ *
+ * 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:
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_avltree.h"
+#include "utils_threshold.h"
+
+#include <pthread.h>
+
+/*
+ * Exported symbols
+ * {{{ */
+c_avl_tree_t   *threshold_tree = NULL;
+pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
+/* }}} */
+
+/*
+ * threshold_t *threshold_get
+ *
+ * Retrieve one specific threshold configuration. For looking up a threshold
+ * matching a value_list_t, see "threshold_search" below. Returns NULL if the
+ * specified threshold doesn't exist.
+ */
+threshold_t *threshold_get (const char *hostname,
+    const char *plugin, const char *plugin_instance,
+    const char *type, const char *type_instance)
+{ /* {{{ */
+  char name[6 * DATA_MAX_NAME_LEN];
+  threshold_t *th = NULL;
+
+  format_name (name, sizeof (name),
+      (hostname == NULL) ? "" : hostname,
+      (plugin == NULL) ? "" : plugin, plugin_instance,
+      (type == NULL) ? "" : type, type_instance);
+  name[sizeof (name) - 1] = '\0';
+
+  if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
+    return (th);
+  else
+    return (NULL);
+} /* }}} threshold_t *threshold_get */
+
+/*
+ * threshold_t *threshold_search
+ *
+ * Searches for a threshold configuration using all the possible variations of
+ * "Host", "Plugin" and "Type" blocks. Returns NULL if no threshold could be
+ * found.
+ * XXX: This is likely the least efficient function in collectd.
+ */
+threshold_t *threshold_search (const value_list_t *vl)
+{ /* {{{ */
+  threshold_t *th;
+
+  if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
+         vl->type, NULL)) != NULL)
+    return (th);
+  else if ((th = threshold_get (vl->host, vl->plugin, NULL,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get (vl->host, vl->plugin, NULL,
+         vl->type, NULL)) != NULL)
+    return (th);
+  else if ((th = threshold_get (vl->host, "", NULL,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get (vl->host, "", NULL,
+         vl->type, NULL)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
+         vl->type, NULL)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", vl->plugin, NULL,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", vl->plugin, NULL,
+         vl->type, NULL)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", "", NULL,
+         vl->type, vl->type_instance)) != NULL)
+    return (th);
+  else if ((th = threshold_get ("", "", NULL,
+         vl->type, NULL)) != NULL)
+    return (th);
+
+  return (NULL);
+} /* }}} threshold_t *threshold_search */
+
+int ut_search_threshold (const value_list_t *vl, /* {{{ */
+    threshold_t *ret_threshold)
+{
+  threshold_t *t;
+
+  if (vl == NULL)
+    return (EINVAL);
+
+       /* Is this lock really necessary? */
+       pthread_mutex_lock (&threshold_lock);
+  t = threshold_search (vl);
+  if (t == NULL) {
+               pthread_mutex_unlock (&threshold_lock);
+    return (ENOENT);
+       }
+
+  memcpy (ret_threshold, t, sizeof (*ret_threshold));
+       pthread_mutex_unlock (&threshold_lock);
+
+  ret_threshold->next = NULL;
+
+  return (0);
+} /* }}} int ut_search_threshold */
+
+
diff --git a/src/daemon/utils_threshold.h b/src/daemon/utils_threshold.h
new file mode 100644 (file)
index 0000000..bf097fa
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * collectd - src/utils_threshold.h
+ * Copyright (C) 2014       Pierre-Yves Ritschard
+ *
+ * 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:
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ **/
+
+#ifndef UTILS_THRESHOLD_H
+#define UTILS_THRESHOLD_H 1
+
+#define UT_FLAG_INVERT  0x01
+#define UT_FLAG_PERSIST 0x02
+#define UT_FLAG_PERCENTAGE 0x04
+#define UT_FLAG_INTERESTING 0x08
+#define UT_FLAG_PERSIST_OK 0x10
+typedef struct threshold_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];
+  char data_source[DATA_MAX_NAME_LEN];
+  gauge_t warning_min;
+  gauge_t warning_max;
+  gauge_t failure_min;
+  gauge_t failure_max;
+  gauge_t hysteresis;
+  unsigned int flags;
+  int hits;
+  struct threshold_s *next;
+} threshold_t;
+
+extern c_avl_tree_t   *threshold_tree;
+extern pthread_mutex_t threshold_lock;
+
+threshold_t *threshold_get (const char *hostname,
+    const char *plugin, const char *plugin_instance,
+    const char *type, const char *type_instance);
+
+threshold_t *threshold_search (const value_list_t *vl);
+
+int ut_search_threshold (const value_list_t *vl, 
+  threshold_t *ret_threshold);
+
+#endif /* UTILS_THRESHOLD_H */
+
+/* vim: set sw=2 sts=2 ts=8 : */
diff --git a/src/daemon/utils_time.c b/src/daemon/utils_time.c
new file mode 100644 (file)
index 0000000..6603c15
--- /dev/null
@@ -0,0 +1,104 @@
+/**
+ * collectd - src/utils_time.c
+ * Copyright (C) 2010       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 <ff at octo.it>
+ **/
+
+#include "collectd.h"
+#include "utils_time.h"
+#include "plugin.h"
+#include "common.h"
+
+#if HAVE_CLOCK_GETTIME
+cdtime_t cdtime (void) /* {{{ */
+{
+  int status;
+  struct timespec ts = { 0, 0 };
+
+  status = clock_gettime (CLOCK_REALTIME, &ts);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("cdtime: clock_gettime failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (0);
+  }
+
+  return (TIMESPEC_TO_CDTIME_T (&ts));
+} /* }}} cdtime_t cdtime */
+#else
+/* Work around for Mac OS X which doesn't have clock_gettime(2). *sigh* */
+cdtime_t cdtime (void) /* {{{ */
+{
+  int status;
+  struct timeval tv = { 0, 0 };
+
+  status = gettimeofday (&tv, /* struct timezone = */ NULL);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("cdtime: gettimeofday failed: %s",
+        sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (0);
+  }
+
+  return (TIMEVAL_TO_CDTIME_T (&tv));
+} /* }}} 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 : */
diff --git a/src/daemon/utils_time.h b/src/daemon/utils_time.h
new file mode 100644 (file)
index 0000000..9b08e8e
--- /dev/null
@@ -0,0 +1,81 @@
+/**
+ * collectd - src/utils_time.h
+ * Copyright (C) 2010       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 <ff at octo.it>
+ **/
+
+#ifndef UTILS_TIME_H
+#define UTILS_TIME_H 1
+
+#include "collectd.h"
+
+/*
+ * "cdtime_t" is a 64bit unsigned integer. The time is stored at a 2^-30 second
+ * resolution, i.e. the most significant 34 bit are used to store the time in
+ * seconds, the least significant bits store the sub-second part in something
+ * very close to nanoseconds. *The* big advantage of storing time in this
+ * manner is that comparing times and calculating differences is as simple as
+ * it is with "time_t", i.e. a simple integer comparison / subtraction works.
+ */
+/* 
+ * cdtime_t is defined in "collectd.h" */
+/* typedef uint64_t cdtime_t; */
+
+/* 2^30 = 1073741824 */
+#define TIME_T_TO_CDTIME_T(t) (((cdtime_t) (t)) * 1073741824)
+#define CDTIME_T_TO_TIME_T(t) ((time_t) ((t) / 1073741824))
+
+#define CDTIME_T_TO_DOUBLE(t) (((double) (t)) / 1073741824.0)
+#define DOUBLE_TO_CDTIME_T(d) ((cdtime_t) ((d) * 1073741824.0))
+
+#define MS_TO_CDTIME_T(ms) ((cdtime_t)    (((double) (ms)) * 1073741.824))
+#define CDTIME_T_TO_MS(t)  ((long)        (((double) (t))  / 1073741.824))
+#define US_TO_CDTIME_T(us) ((cdtime_t)    (((double) (us)) * 1073.741824))
+#define CDTIME_T_TO_US(t)  ((suseconds_t) (((double) (t))  / 1073.741824))
+#define NS_TO_CDTIME_T(ns) ((cdtime_t)    (((double) (ns)) * 1.073741824))
+#define CDTIME_T_TO_NS(t)  ((long)        (((double) (t))  / 1.073741824))
+
+#define CDTIME_T_TO_TIMEVAL(cdt,tvp) do {                                    \
+        (tvp)->tv_sec = CDTIME_T_TO_TIME_T (cdt);                            \
+        (tvp)->tv_usec = CDTIME_T_TO_US ((cdt) % 1073741824);                \
+} while (0)
+#define TIMEVAL_TO_CDTIME_T(tv) (TIME_T_TO_CDTIME_T ((tv)->tv_sec)           \
+    + US_TO_CDTIME_T ((tv)->tv_usec))
+
+#define CDTIME_T_TO_TIMESPEC(cdt,tsp) do {                                   \
+  (tsp)->tv_sec = CDTIME_T_TO_TIME_T (cdt);                                  \
+  (tsp)->tv_nsec = CDTIME_T_TO_NS ((cdt) % 1073741824);                      \
+} while (0)
+#define TIMESPEC_TO_CDTIME_T(ts) (TIME_T_TO_CDTIME_T ((ts)->tv_sec)           \
+    + NS_TO_CDTIME_T ((ts)->tv_nsec))
+
+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 : */
index cdf2816..51fbc02 100644 (file)
@@ -272,6 +272,26 @@ static void disk_submit (const char *plugin_instance,
 } /* void disk_submit */
 
 #if KERNEL_LINUX
+static void submit_in_progress (char const *disk_name, gauge_t in_progress)
+{
+       value_t v;
+       value_list_t vl = VALUE_LIST_INIT;
+
+       if (ignorelist_match (ignorelist, disk_name) != 0)
+         return;
+
+       v.gauge = in_progress;
+
+       vl.values = &v;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "disk", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, disk_name, sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "pending_operations", sizeof (vl.type));
+
+       plugin_dispatch_values (&vl);
+}
+
 static counter_t disk_calc_time_incr (counter_t delta_time, counter_t delta_ops)
 {
        double interval = CDTIME_T_TO_DOUBLE (plugin_get_interval ());
@@ -539,6 +559,7 @@ static int disk_read (void)
        derive_t write_ops     = 0;
        derive_t write_merged  = 0;
        derive_t write_time    = 0;
+       gauge_t in_progress    = NAN;
        int is_disk = 0;
 
        diskstats_t *ds, *pre_ds;
@@ -620,6 +641,8 @@ static int disk_read (void)
                                read_time    = atoll (fields[6 + fieldshift]);
                                write_merged = atoll (fields[8 + fieldshift]);
                                write_time   = atoll (fields[10+ fieldshift]);
+
+                               in_progress = atof (fields[11 + fieldshift]);
                        }
                }
                else
@@ -743,6 +766,7 @@ static int disk_read (void)
                {
                        disk_submit (output_name, "disk_merged",
                                        read_merged, write_merged);
+                       submit_in_progress (output_name, in_progress);
                } /* if (is_disk) */
 
                /* release udev-based alternate name, if allocated */
@@ -807,7 +831,12 @@ static int disk_read (void)
 
 #elif defined(HAVE_LIBSTATGRAB)
        sg_disk_io_stats *ds;
-       int disks, counter;
+# if HAVE_LIBSTATGRAB_GET_DISK_IO_STATS_SIZET
+       size_t disks;
+# else
+       int disks;
+#endif
+       int counter;
        char name[DATA_MAX_NAME_LEN];
        
        if ((ds = sg_get_disk_io_stats(&disks)) == NULL)
index 00fcff1..fd75dc9 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -21,6 +21,7 @@
  *   Mirko Buffoni <briareos at eswat.org>
  **/
 
+#define _DEFAULT_SOURCE
 #define _BSD_SOURCE
 
 #include "collectd.h"
index cb6844b..a2b0915 100644 (file)
@@ -23,6 +23,7 @@
  *   Peter Holik <peter at holik.at>
  **/
 
+#define _DEFAULT_SOURCE
 #define _BSD_SOURCE /* For setgroups */
 
 #include "collectd.h"
diff --git a/src/filter_chain.c b/src/filter_chain.c
deleted file mode 100644 (file)
index c87b877..0000000
+++ /dev/null
@@ -1,1058 +0,0 @@
-/**
- * collectd - src/filter_chain.c
- * Copyright (C) 2008-2010  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 "collectd.h"
-#include "configfile.h"
-#include "plugin.h"
-#include "utils_complain.h"
-#include "common.h"
-#include "filter_chain.h"
-
-/*
- * Data types
- */
-/* List of matches, used in fc_rule_t and for the global `match_list_head'
- * variable. */
-struct fc_match_s;
-typedef struct fc_match_s fc_match_t; /* {{{ */
-struct fc_match_s
-{
-  char name[DATA_MAX_NAME_LEN];
-  match_proc_t proc;
-  void *user_data;
-  fc_match_t *next;
-}; /* }}} */
-
-/* List of targets, used in fc_rule_t and for the global `target_list_head'
- * variable. */
-struct fc_target_s;
-typedef struct fc_target_s fc_target_t; /* {{{ */
-struct fc_target_s
-{
-  char name[DATA_MAX_NAME_LEN];
-  void *user_data;
-  target_proc_t proc;
-  fc_target_t *next;
-}; /* }}} */
-
-/* List of rules, used in fc_chain_t */
-struct fc_rule_s;
-typedef struct fc_rule_s fc_rule_t; /* {{{ */
-struct fc_rule_s
-{
-  char name[DATA_MAX_NAME_LEN];
-  fc_match_t  *matches;
-  fc_target_t *targets;
-  fc_rule_t *next;
-}; /* }}} */
-
-/* List of chains, used for `chain_list_head' */
-struct fc_chain_s /* {{{ */
-{
-  char name[DATA_MAX_NAME_LEN];
-  fc_rule_t   *rules;
-  fc_target_t *targets;
-  fc_chain_t  *next;
-}; /* }}} */
-
-/*
- * Global variables
- */
-static fc_match_t  *match_list_head;
-static fc_target_t *target_list_head;
-static fc_chain_t  *chain_list_head;
-
-/*
- * Private functions
- */
-static void fc_free_matches (fc_match_t *m) /* {{{ */
-{
-  if (m == NULL)
-    return;
-
-  if (m->proc.destroy != NULL)
-    (*m->proc.destroy) (&m->user_data);
-  else if (m->user_data != NULL)
-  {
-    ERROR ("Filter subsystem: fc_free_matches: There is user data, but no "
-        "destroy functions has been specified. "
-        "Memory will probably be lost!");
-  }
-
-  if (m->next != NULL)
-    fc_free_matches (m->next);
-
-  free (m);
-} /* }}} void fc_free_matches */
-
-static void fc_free_targets (fc_target_t *t) /* {{{ */
-{
-  if (t == NULL)
-    return;
-
-  if (t->proc.destroy != NULL)
-    (*t->proc.destroy) (&t->user_data);
-  else if (t->user_data != NULL)
-  {
-    ERROR ("Filter subsystem: fc_free_targets: There is user data, but no "
-        "destroy functions has been specified. "
-        "Memory will probably be lost!");
-  }
-
-  if (t->next != NULL)
-    fc_free_targets (t->next);
-
-  free (t);
-} /* }}} void fc_free_targets */
-
-static void fc_free_rules (fc_rule_t *r) /* {{{ */
-{
-  if (r == NULL)
-    return;
-
-  fc_free_matches (r->matches);
-  fc_free_targets (r->targets);
-
-  if (r->next != NULL)
-    fc_free_rules (r->next);
-
-  free (r);
-} /* }}} void fc_free_rules */
-
-static void fc_free_chains (fc_chain_t *c) /* {{{ */
-{
-  if (c == NULL)
-    return;
-
-  fc_free_rules (c->rules);
-  fc_free_targets (c->targets);
-
-  if (c->next != NULL)
-    fc_free_chains (c->next);
-
-  free (c);
-} /* }}} void fc_free_chains */
-
-static char *fc_strdup (const char *orig) /* {{{ */
-{
-  size_t sz;
-  char *dest;
-
-  if (orig == NULL)
-    return (NULL);
-
-  sz = strlen (orig) + 1;
-  dest = (char *) malloc (sz);
-  if (dest == NULL)
-    return (NULL);
-
-  memcpy (dest, orig, sz);
-
-  return (dest);
-} /* }}} char *fc_strdup */
-
-/*
- * Configuration.
- *
- * The configuration looks somewhat like this:
- *
- *  <Chain "PreCache">
- *    <Rule>
- *      <Match "regex">
- *        Plugin "^mysql$"
- *        Type "^mysql_command$"
- *        TypeInstance "^show_"
- *      </Match>
- *      <Target "drop">
- *      </Target>
- *    </Rule>
- *
- *    <Target "write">
- *      Plugin "rrdtool"
- *    </Target>
- *  </Chain>
- */
-static int fc_config_add_match (fc_match_t **matches_head, /* {{{ */
-    oconfig_item_t *ci)
-{
-  fc_match_t *m;
-  fc_match_t *ptr;
-  int status;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("Filter subsystem: `Match' blocks require "
-        "exactly one string argument.");
-    return (-1);
-  }
-
-  ptr = match_list_head;
-  while (ptr != NULL)
-  {
-    if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
-      break;
-    ptr = ptr->next;
-  }
-
-  if (ptr == NULL)
-  {
-    WARNING ("Filter subsystem: Cannot find a \"%s\" match. "
-        "Did you load the appropriate plugin?",
-        ci->values[0].value.string);
-    return (-1);
-  }
-
-  m = (fc_match_t *) malloc (sizeof (*m));
-  if (m == NULL)
-  {
-    ERROR ("fc_config_add_match: malloc failed.");
-    return (-1);
-  }
-  memset (m, 0, sizeof (*m));
-
-  sstrncpy (m->name, ptr->name, sizeof (m->name));
-  memcpy (&m->proc, &ptr->proc, sizeof (m->proc));
-  m->user_data = NULL;
-  m->next = NULL;
-
-  if (m->proc.create != NULL)
-  {
-    status = (*m->proc.create) (ci, &m->user_data);
-    if (status != 0)
-    {
-      WARNING ("Filter subsystem: Failed to create a %s match.",
-          m->name);
-      fc_free_matches (m);
-      return (-1);
-    }
-  }
-
-  if (*matches_head != NULL)
-  {
-    ptr = *matches_head;
-    while (ptr->next != NULL)
-      ptr = ptr->next;
-
-    ptr->next = m;
-  }
-  else
-  {
-    *matches_head = m;
-  }
-
-  return (0);
-} /* }}} int fc_config_add_match */
-
-static int fc_config_add_target (fc_target_t **targets_head, /* {{{ */
-    oconfig_item_t *ci)
-{
-  fc_target_t *t;
-  fc_target_t *ptr;
-  int status;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("Filter subsystem: `Target' blocks require "
-        "exactly one string argument.");
-    return (-1);
-  }
-
-  ptr = target_list_head;
-  while (ptr != NULL)
-  {
-    if (strcasecmp (ptr->name, ci->values[0].value.string) == 0)
-      break;
-    ptr = ptr->next;
-  }
-
-  if (ptr == NULL)
-  {
-    WARNING ("Filter subsystem: Cannot find a \"%s\" target. "
-        "Did you load the appropriate plugin?",
-        ci->values[0].value.string);
-    return (-1);
-  }
-
-  t = (fc_target_t *) malloc (sizeof (*t));
-  if (t == NULL)
-  {
-    ERROR ("fc_config_add_target: malloc failed.");
-    return (-1);
-  }
-  memset (t, 0, sizeof (*t));
-
-  sstrncpy (t->name, ptr->name, sizeof (t->name));
-  memcpy (&t->proc, &ptr->proc, sizeof (t->proc));
-  t->user_data = NULL;
-  t->next = NULL;
-
-  if (t->proc.create != NULL)
-  {
-    status = (*t->proc.create) (ci, &t->user_data);
-    if (status != 0)
-    {
-      WARNING ("Filter subsystem: Failed to create a %s target.",
-          t->name);
-      fc_free_targets (t);
-      return (-1);
-    }
-  }
-  else
-  {
-    t->user_data = NULL;
-  }
-  
-  if (*targets_head != NULL)
-  {
-    ptr = *targets_head;
-    while (ptr->next != NULL)
-      ptr = ptr->next;
-
-    ptr->next = t;
-  }
-  else
-  {
-    *targets_head = t;
-  }
-
-  return (0);
-} /* }}} int fc_config_add_target */
-
-static int fc_config_add_rule (fc_chain_t *chain, /* {{{ */
-    oconfig_item_t *ci)
-{
-  fc_rule_t *rule;
-  char rule_name[2*DATA_MAX_NAME_LEN] = "Unnamed rule";
-  int status = 0;
-  int i;
-
-  if (ci->values_num > 1)
-  {
-    WARNING ("Filter subsystem: `Rule' blocks have at most one argument.");
-    return (-1);
-  }
-  else if ((ci->values_num == 1)
-      && (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("Filter subsystem: `Rule' blocks expect one string argument "
-        "or no argument at all.");
-    return (-1);
-  }
-
-  rule = (fc_rule_t *) malloc (sizeof (*rule));
-  if (rule == NULL)
-  {
-    ERROR ("fc_config_add_rule: malloc failed.");
-    return (-1);
-  }
-  memset (rule, 0, sizeof (*rule));
-  rule->next = NULL;
-
-  if (ci->values_num == 1)
-  {
-    sstrncpy (rule->name, ci->values[0].value.string, sizeof (rule->name));
-    ssnprintf (rule_name, sizeof (rule_name), "Rule \"%s\"",
-        ci->values[0].value.string);
-  }
-
-  for (i = 0; i < ci->children_num; i++)
-  {
-    oconfig_item_t *option = ci->children + i;
-    status = 0;
-
-    if (strcasecmp ("Match", option->key) == 0)
-      status = fc_config_add_match (&rule->matches, option);
-    else if (strcasecmp ("Target", option->key) == 0)
-      status = fc_config_add_target (&rule->targets, option);
-    else
-    {
-      WARNING ("Filter subsystem: %s: Option `%s' not allowed "
-          "inside a <Rule> block.", rule_name, option->key);
-      status = -1;
-    }
-
-    if (status != 0)
-      break;
-  } /* for (ci->children) */
-
-  /* Additional sanity checking. */
-  while (status == 0)
-  {
-    if (rule->targets == NULL)
-    {
-      WARNING ("Filter subsystem: %s: No target has been specified.",
-          rule_name);
-      status = -1;
-      break;
-    }
-
-    break;
-  } /* while (status == 0) */
-
-  if (status != 0)
-  {
-    fc_free_rules (rule);
-    return (-1);
-  }
-
-  if (chain->rules != NULL)
-  {
-    fc_rule_t *ptr;
-
-    ptr = chain->rules;
-    while (ptr->next != NULL)
-      ptr = ptr->next;
-
-    ptr->next = rule;
-  }
-  else
-  {
-    chain->rules = rule;
-  }
-
-  return (0);
-} /* }}} int fc_config_add_rule */
-
-static int fc_config_add_chain (const oconfig_item_t *ci) /* {{{ */
-{
-  fc_chain_t *chain = NULL;
-  int status = 0;
-  int i;
-  int new_chain = 1;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("Filter subsystem: <Chain> blocks require exactly one "
-        "string argument.");
-    return (-1);
-  }
-
-  if (chain_list_head != NULL)
-  {
-    if ((chain = fc_chain_get_by_name (ci->values[0].value.string)) != NULL)
-      new_chain = 0;
-  }
-
-  if (chain == NULL)
-  {
-    chain = (fc_chain_t *) malloc (sizeof (*chain));
-    if (chain == NULL)
-    {
-      ERROR ("fc_config_add_chain: malloc failed.");
-      return (-1);
-    }
-    memset (chain, 0, sizeof (*chain));
-    sstrncpy (chain->name, ci->values[0].value.string, sizeof (chain->name));
-    chain->rules = NULL;
-    chain->targets = NULL;
-    chain->next = NULL;
-  }
-
-  for (i = 0; i < ci->children_num; i++)
-  {
-    oconfig_item_t *option = ci->children + i;
-    status = 0;
-
-    if (strcasecmp ("Rule", option->key) == 0)
-      status = fc_config_add_rule (chain, option);
-    else if (strcasecmp ("Target", option->key) == 0)
-      status = fc_config_add_target (&chain->targets, option);
-    else
-    {
-      WARNING ("Filter subsystem: Chain %s: Option `%s' not allowed "
-          "inside a <Chain> block.", chain->name, option->key);
-      status = -1;
-    }
-
-    if (status != 0)
-      break;
-  } /* for (ci->children) */
-
-  if (status != 0)
-  {
-    fc_free_chains (chain);
-    return (-1);
-  }
-
-  if (chain_list_head != NULL)
-  {
-    if (!new_chain)
-      return (0);
-
-    fc_chain_t *ptr;
-
-    ptr = chain_list_head;
-    while (ptr->next != NULL)
-      ptr = ptr->next;
-
-    ptr->next = chain;
-  }
-  else
-  {
-    chain_list_head = chain;
-  }
-
-  return (0);
-} /* }}} int fc_config_add_chain */
-
-/*
- * Built-in target "jump"
- *
- * Prefix `bit' like `_b_uilt-_i_n _t_arget'
- */
-static int fc_bit_jump_create (const oconfig_item_t *ci, /* {{{ */
-    void **user_data)
-{
-  oconfig_item_t *ci_chain;
-
-  if (ci->children_num != 1)
-  {
-    ERROR ("Filter subsystem: The built-in target `jump' needs exactly "
-        "one `Chain' argument!");
-    return (-1);
-  }
-
-  ci_chain = ci->children;
-  if (strcasecmp ("Chain", ci_chain->key) != 0)
-  {
-    ERROR ("Filter subsystem: The built-in target `jump' does not "
-        "support the configuration option `%s'.",
-        ci_chain->key);
-    return (-1);
-  }
-
-  if ((ci_chain->values_num != 1)
-      || (ci_chain->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    ERROR ("Filter subsystem: Built-in target `jump': The `Chain' option "
-        "needs exactly one string argument.");
-    return (-1);
-  }
-
-  *user_data = fc_strdup (ci_chain->values[0].value.string);
-  if (*user_data == NULL)
-  {
-    ERROR ("fc_bit_jump_create: fc_strdup failed.");
-    return (-1);
-  }
-
-  return (0);
-} /* }}} int fc_bit_jump_create */
-
-static int fc_bit_jump_destroy (void **user_data) /* {{{ */
-{
-  if (user_data != NULL)
-  {
-    free (*user_data);
-    *user_data = NULL;
-  }
-
-  return (0);
-} /* }}} int fc_bit_jump_destroy */
-
-static int fc_bit_jump_invoke (const data_set_t *ds, /* {{{ */
-    value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
-    void **user_data)
-{
-  char *chain_name;
-  fc_chain_t *chain;
-  int status;
-
-  chain_name = *user_data;
-
-  for (chain = chain_list_head; chain != NULL; chain = chain->next)
-    if (strcasecmp (chain_name, chain->name) == 0)
-      break;
-
-  if (chain == NULL)
-  {
-    ERROR ("Filter subsystem: Built-in target `jump': There is no chain "
-        "named `%s'.", chain_name);
-    return (-1);
-  }
-
-  status = fc_process_chain (ds, vl, chain);
-  if (status < 0)
-    return (status);
-  else if (status == FC_TARGET_STOP)
-    return (FC_TARGET_STOP);
-  else
-    return (FC_TARGET_CONTINUE);
-} /* }}} int fc_bit_jump_invoke */
-
-static int fc_bit_stop_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */
-    value_list_t __attribute__((unused)) *vl,
-    notification_meta_t __attribute__((unused)) **meta,
-    void __attribute__((unused)) **user_data)
-{
-  return (FC_TARGET_STOP);
-} /* }}} int fc_bit_stop_invoke */
-
-static int fc_bit_return_invoke (const data_set_t __attribute__((unused)) *ds, /* {{{ */
-    value_list_t __attribute__((unused)) *vl,
-    notification_meta_t __attribute__((unused)) **meta,
-    void __attribute__((unused)) **user_data)
-{
-  return (FC_TARGET_RETURN);
-} /* }}} int fc_bit_return_invoke */
-
-static int fc_bit_write_create (const oconfig_item_t *ci, /* {{{ */
-    void **user_data)
-{
-  int i;
-
-  char **plugin_list;
-  size_t plugin_list_len;
-
-  plugin_list = NULL;
-  plugin_list_len = 0;
-
-  for (i = 0; i < ci->children_num; i++)
-  {
-    oconfig_item_t *child = ci->children + i;
-    char **temp;
-    int j;
-
-    if (strcasecmp ("Plugin", child->key) != 0)
-    {
-      ERROR ("Filter subsystem: The built-in target `write' does not "
-          "support the configuration option `%s'.",
-          child->key);
-      continue;
-    }
-
-    for (j = 0; j < child->values_num; j++)
-    {
-      if (child->values[j].type != OCONFIG_TYPE_STRING)
-      {
-        ERROR ("Filter subsystem: Built-in target `write': "
-            "The `Plugin' option accepts only string arguments.");
-        continue;
-      }
-
-      temp = (char **) realloc (plugin_list, (plugin_list_len + 2)
-          * (sizeof (*plugin_list)));
-      if (temp == NULL)
-      {
-        ERROR ("fc_bit_write_create: realloc failed.");
-        continue;
-      }
-      plugin_list = temp;
-
-      plugin_list[plugin_list_len] = fc_strdup (child->values[j].value.string);
-      if (plugin_list[plugin_list_len] == NULL)
-      {
-        ERROR ("fc_bit_write_create: fc_strdup failed.");
-        continue;
-      }
-      plugin_list_len++;
-      plugin_list[plugin_list_len] = NULL;
-    } /* for (j = 0; j < child->values_num; j++) */
-  } /* for (i = 0; i < ci->children_num; i++) */
-
-  *user_data = plugin_list;
-
-  return (0);
-} /* }}} int fc_bit_write_create */
-
-static int fc_bit_write_destroy (void **user_data) /* {{{ */
-{
-  char **plugin_list;
-  size_t i;
-
-  if ((user_data == NULL) || (*user_data == NULL))
-    return (0);
-
-  plugin_list = *user_data;
-
-  for (i = 0; plugin_list[i] != NULL; i++)
-    free (plugin_list[i]);
-  free (plugin_list);
-
-  return (0);
-} /* }}} int fc_bit_write_destroy */
-
-static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */
-    value_list_t *vl, notification_meta_t __attribute__((unused)) **meta,
-    void **user_data)
-{
-  char **plugin_list;
-  int status;
-
-  plugin_list = NULL;
-  if (user_data != NULL)
-    plugin_list = *user_data;
-
-  if ((plugin_list == NULL) || (plugin_list[0] == NULL))
-  {
-    static c_complain_t enoent_complaint = C_COMPLAIN_INIT_STATIC;
-
-    status = plugin_write (/* plugin = */ NULL, ds, vl);
-    if (status == ENOENT)
-    {
-      /* in most cases this is a permanent error, so use the complain
-       * mechanism rather than spamming the logs */
-      c_complain (LOG_INFO, &enoent_complaint,
-          "Filter subsystem: Built-in target `write': Dispatching value to "
-          "all write plugins failed with status %i (ENOENT). "
-          "Most likely this means you didn't load any write plugins.",
-          status);
-    }
-    else if (status != 0)
-    {
-      INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
-          "all write plugins failed with status %i.", status);
-    }
-    else
-    {
-      assert (status == 0);
-      c_release (LOG_INFO, &enoent_complaint, "Filter subsystem: "
-          "Built-in target `write': Some write plugin is back to normal "
-          "operation. `write' succeeded.");
-    }
-  }
-  else
-  {
-    size_t i;
-
-    for (i = 0; plugin_list[i] != NULL; i++)
-    {
-      status = plugin_write (plugin_list[i], ds, vl);
-      if (status != 0)
-      {
-        INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
-            "the `%s' plugin failed with status %i.", plugin_list[i], status);
-      }
-    } /* for (i = 0; plugin_list[i] != NULL; i++) */
-  }
-
-  return (FC_TARGET_CONTINUE);
-} /* }}} int fc_bit_write_invoke */
-
-static int fc_init_once (void) /* {{{ */
-{
-  static int done = 0;
-  target_proc_t tproc;
-
-  if (done != 0)
-    return (0);
-
-  memset (&tproc, 0, sizeof (tproc));
-  tproc.create  = fc_bit_jump_create;
-  tproc.destroy = fc_bit_jump_destroy;
-  tproc.invoke  = fc_bit_jump_invoke;
-  fc_register_target ("jump", tproc);
-
-  memset (&tproc, 0, sizeof (tproc));
-  tproc.create  = NULL;
-  tproc.destroy = NULL;
-  tproc.invoke  = fc_bit_stop_invoke;
-  fc_register_target ("stop", tproc);
-
-  memset (&tproc, 0, sizeof (tproc));
-  tproc.create  = NULL;
-  tproc.destroy = NULL;
-  tproc.invoke  = fc_bit_return_invoke;
-  fc_register_target ("return", tproc);
-
-  memset (&tproc, 0, sizeof (tproc));
-  tproc.create  = fc_bit_write_create;
-  tproc.destroy = fc_bit_write_destroy;
-  tproc.invoke  = fc_bit_write_invoke;
-  fc_register_target ("write", tproc);
-
-  done++;
-  return (0);
-} /* }}} int fc_init_once */
-
-/*
- * Public functions
- */
-/* Add a match to list of available matches. */
-int fc_register_match (const char *name, match_proc_t proc) /* {{{ */
-{
-  fc_match_t *m;
-
-  DEBUG ("fc_register_match (%s);", name);
-
-  m = (fc_match_t *) malloc (sizeof (*m));
-  if (m == NULL)
-    return (-ENOMEM);
-  memset (m, 0, sizeof (*m));
-
-  sstrncpy (m->name, name, sizeof (m->name));
-  memcpy (&m->proc, &proc, sizeof (m->proc));
-  m->next = NULL;
-
-  if (match_list_head == NULL)
-  {
-    match_list_head = m;
-  }
-  else
-  {
-    fc_match_t *ptr;
-
-    ptr = match_list_head;
-    while (ptr->next != NULL)
-      ptr = ptr->next;
-
-    ptr->next = m;
-  }
-
-  return (0);
-} /* }}} int fc_register_match */
-
-/* Add a target to list of available targets. */
-int fc_register_target (const char *name, target_proc_t proc) /* {{{ */
-{
-  fc_target_t *t;
-
-  DEBUG ("fc_register_target (%s);", name);
-
-  t = (fc_target_t *) malloc (sizeof (*t));
-  if (t == NULL)
-    return (-ENOMEM);
-  memset (t, 0, sizeof (*t));
-
-  sstrncpy (t->name, name, sizeof (t->name));
-  memcpy (&t->proc, &proc, sizeof (t->proc));
-  t->next = NULL;
-
-  if (target_list_head == NULL)
-  {
-    target_list_head = t;
-  }
-  else
-  {
-    fc_target_t *ptr;
-
-    ptr = target_list_head;
-    while (ptr->next != NULL)
-      ptr = ptr->next;
-
-    ptr->next = t;
-  }
-
-  return (0);
-} /* }}} int fc_register_target */
-
-fc_chain_t *fc_chain_get_by_name (const char *chain_name) /* {{{ */
-{
-  fc_chain_t *chain;
-
-  if (chain_name == NULL)
-    return (NULL);
-
-  for (chain = chain_list_head; chain != NULL; chain = chain->next)
-    if (strcasecmp (chain_name, chain->name) == 0)
-      return (chain);
-
-  return (NULL);
-} /* }}} int fc_chain_get_by_name */
-
-int fc_process_chain (const data_set_t *ds, value_list_t *vl, /* {{{ */
-    fc_chain_t *chain)
-{
-  fc_rule_t *rule;
-  fc_target_t *target;
-  int status;
-
-  if (chain == NULL)
-    return (-1);
-
-  DEBUG ("fc_process_chain (chain = %s);", chain->name);
-
-  status = FC_TARGET_CONTINUE;
-  for (rule = chain->rules; rule != NULL; rule = rule->next)
-  {
-    fc_match_t *match;
-
-    if (rule->name[0] != 0)
-    {
-      DEBUG ("fc_process_chain (%s): Testing the `%s' rule.",
-          chain->name, rule->name);
-    }
-
-    /* N. B.: rule->matches may be NULL. */
-    for (match = rule->matches; match != NULL; match = match->next)
-    {
-      /* FIXME: Pass the meta-data to match targets here (when implemented). */
-      status = (*match->proc.match) (ds, vl, /* meta = */ NULL,
-          &match->user_data);
-      if (status < 0)
-      {
-        WARNING ("fc_process_chain (%s): A match failed.", chain->name);
-        break;
-      }
-      else if (status != FC_MATCH_MATCHES)
-        break;
-    }
-
-    /* for-loop has been aborted: Either error or no match. */
-    if (match != NULL)
-    {
-      status = FC_TARGET_CONTINUE;
-      continue;
-    }
-
-    if (rule->name[0] != 0)
-    {
-      DEBUG ("fc_process_chain (%s): Rule `%s' matches.",
-          chain->name, rule->name);
-    }
-
-    for (target = rule->targets; target != NULL; target = target->next)
-    {
-      /* If we get here, all matches have matched the value. Execute the
-       * target. */
-      /* FIXME: Pass the meta-data to match targets here (when implemented). */
-      status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
-          &target->user_data);
-      if (status < 0)
-      {
-        WARNING ("fc_process_chain (%s): A target failed.", chain->name);
-        continue;
-      }
-      else if (status == FC_TARGET_CONTINUE)
-        continue;
-      else if (status == FC_TARGET_STOP)
-        break;
-      else if (status == FC_TARGET_RETURN)
-        break;
-      else
-      {
-        WARNING ("fc_process_chain (%s): Unknown return value "
-            "from target `%s': %i",
-            chain->name, target->name, status);
-      }
-    }
-
-    if ((status == FC_TARGET_STOP)
-        || (status == FC_TARGET_RETURN))
-    {
-      if (rule->name[0] != 0)
-      {
-        DEBUG ("fc_process_chain (%s): Rule `%s' signaled "
-            "the %s condition.",
-            chain->name, rule->name,
-            (status == FC_TARGET_STOP) ? "stop" : "return");
-      }
-      break;
-    }
-    else
-    {
-      status = FC_TARGET_CONTINUE;
-    }
-  } /* for (rule) */
-
-  if (status == FC_TARGET_STOP)
-    return (FC_TARGET_STOP);
-  else if (status == FC_TARGET_RETURN)
-    return (FC_TARGET_CONTINUE);
-
-  /* for-loop has been aborted: A target returned `FC_TARGET_STOP' */
-  if (rule != NULL)
-    return (FC_TARGET_CONTINUE);
-
-  DEBUG ("fc_process_chain (%s): Executing the default targets.",
-      chain->name);
-
-  status = FC_TARGET_CONTINUE;
-  for (target = chain->targets; target != NULL; target = target->next)
-  {
-    /* If we get here, all matches have matched the value. Execute the
-     * target. */
-    /* FIXME: Pass the meta-data to match targets here (when implemented). */
-    status = (*target->proc.invoke) (ds, vl, /* meta = */ NULL,
-        &target->user_data);
-    if (status < 0)
-    {
-      WARNING ("fc_process_chain (%s): The default target failed.",
-          chain->name);
-    }
-    else if (status == FC_TARGET_CONTINUE)
-      continue;
-    else if (status == FC_TARGET_STOP)
-      break;
-    else if (status == FC_TARGET_RETURN)
-      break;
-    else
-    {
-      WARNING ("fc_process_chain (%s): Unknown return value "
-          "from target `%s': %i",
-          chain->name, target->name, status);
-    }
-  }
-
-  if ((status == FC_TARGET_STOP)
-      || (status == FC_TARGET_RETURN))
-  {
-    assert (target != NULL);
-    DEBUG ("fc_process_chain (%s): Default target `%s' signaled "
-        "the %s condition.",
-        chain->name, target->name,
-        (status == FC_TARGET_STOP) ? "stop" : "return");
-    if (status == FC_TARGET_STOP)
-      return (FC_TARGET_STOP);
-    else
-      return (FC_TARGET_CONTINUE);
-  }
-
-  DEBUG ("fc_process_chain (%s): Signaling `continue' at end of chain.",
-      chain->name);
-
-  return (FC_TARGET_CONTINUE);
-} /* }}} int fc_process_chain */
-
-/* Iterate over all rules in the chain and execute all targets for which all
- * matches match. */
-int fc_default_action (const data_set_t *ds, value_list_t *vl) /* {{{ */
-{
-  /* FIXME: Pass the meta-data to match targets here (when implemented). */
-  return (fc_bit_write_invoke (ds, vl,
-        /* meta = */ NULL, /* user_data = */ NULL));
-} /* }}} int fc_default_action */
-
-int fc_configure (const oconfig_item_t *ci) /* {{{ */
-{
-  fc_init_once ();
-
-  if (ci == NULL)
-    return (-EINVAL);
-
-  if (strcasecmp ("Chain", ci->key) == 0)
-    return (fc_config_add_chain (ci));
-
-  WARNING ("Filter subsystem: Unknown top level config option `%s'.",
-      ci->key);
-
-  return (-1);
-} /* }}} int fc_configure */
-
-/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/filter_chain.h b/src/filter_chain.h
deleted file mode 100644 (file)
index 2db90db..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/**
- * collectd - src/filter_chain.h
- * Copyright (C) 2008,2009  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 FILTER_CHAIN_H
-#define FILTER_CHAIN_H 1
-
-#include "collectd.h"
-#include "plugin.h"
-
-#define FC_MATCH_NO_MATCH  0
-#define FC_MATCH_MATCHES   1
-
-#define FC_TARGET_CONTINUE 0
-#define FC_TARGET_STOP     1
-#define FC_TARGET_RETURN   2
-
-/*
- * Match functions
- */
-struct match_proc_s
-{
-  int (*create) (const oconfig_item_t *ci, void **user_data);
-  int (*destroy) (void **user_data);
-  int (*match) (const data_set_t *ds, const value_list_t *vl,
-      notification_meta_t **meta, void **user_data);
-};
-typedef struct match_proc_s match_proc_t;
-
-int fc_register_match (const char *name, match_proc_t proc);
-
-/*
- * Target functions
- */
-struct target_proc_s
-{
-  int (*create) (const oconfig_item_t *ci, void **user_data);
-  int (*destroy) (void **user_data);
-  int (*invoke) (const data_set_t *ds, value_list_t *vl,
-      notification_meta_t **meta, void **user_data);
-};
-typedef struct target_proc_s target_proc_t;
-
-struct fc_chain_s;
-typedef struct fc_chain_s fc_chain_t;
-
-int fc_register_target (const char *name, target_proc_t proc);
-
-/*
- * TODO: Chain management
- */
-#if 0
-int fc_chain_add (const char *chain_name,
-    const char *target_name, int target_argc, char **target_argv);
-int fc_chain_delete (const char *chain_name);
-#endif
-
-/*
- * TODO: Rule management
- */
-#if 0
-int fc_rule_add (const char *chain_name, int position,
-    int match_num, const char **match_name, int *match_argc, char ***match_argv,
-    const char *target_name, int target_argc, char **target_argv);
-int fc_rule_delete (const char *chain_name, int position);
-#endif
-
-/*
- * Processing function
- */
-fc_chain_t *fc_chain_get_by_name (const char *chain_name);
-
-int fc_process_chain (const data_set_t *ds, value_list_t *vl,
-    fc_chain_t *chain);
-
-int fc_default_action (const data_set_t *ds, value_list_t *vl);
-
-/* 
- * Shortcut for global configuration
- */
-int fc_configure (const oconfig_item_t *ci);
-
-#endif /* FILTER_CHAIN_H */
-/* vim: set sw=2 sts=2 et : */
index 8de59d2..d0423be 100644 (file)
@@ -2142,7 +2142,7 @@ static int cjni_thread_detach (void) /* {{{ */
   cjni_env->jvm_env = NULL;
 
   return (0);
-} /* }}} JNIEnv *cjni_thread_attach */
+} /* }}} int cjni_thread_detach */
 
 static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */
 {
@@ -2495,7 +2495,6 @@ static int cjni_read (user_data_t *ud) /* {{{ */
 {
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2519,13 +2518,7 @@ static int cjni_read (user_data_t *ud) /* {{{ */
   ret_status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object,
       cbi->method);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_read */
 
@@ -2536,7 +2529,6 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
   jobject vl_java;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2561,6 +2553,7 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
   if (vl_java == NULL)
   {
     ERROR ("java plugin: cjni_write: ctoj_value_list failed.");
+    cjni_thread_detach ();
     return (-1);
   }
 
@@ -2569,13 +2562,7 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
 
   (*jvm_env)->DeleteLocalRef (jvm_env, vl_java);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_write: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_write */
 
@@ -2587,7 +2574,6 @@ static int cjni_flush (cdtime_t timeout, const char *identifier, /* {{{ */
   cjni_callback_info_t *cbi;
   jobject o_timeout;
   jobject o_identifier;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2614,6 +2600,7 @@ static int cjni_flush (cdtime_t timeout, const char *identifier, /* {{{ */
   {
     ERROR ("java plugin: cjni_flush: Converting double "
         "to Number object failed.");
+    cjni_thread_detach ();
     return (-1);
   }
 
@@ -2625,6 +2612,7 @@ static int cjni_flush (cdtime_t timeout, const char *identifier, /* {{{ */
     {
       (*jvm_env)->DeleteLocalRef (jvm_env, o_timeout);
       ERROR ("java plugin: cjni_flush: NewStringUTF failed.");
+      cjni_thread_detach ();
       return (-1);
     }
   }
@@ -2635,13 +2623,7 @@ static int cjni_flush (cdtime_t timeout, const char *identifier, /* {{{ */
   (*jvm_env)->DeleteLocalRef (jvm_env, o_identifier);
   (*jvm_env)->DeleteLocalRef (jvm_env, o_timeout);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_flush: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_flush */
 
@@ -2667,7 +2649,10 @@ static void cjni_log (int severity, const char *message, /* {{{ */
 
   o_message = (*jvm_env)->NewStringUTF (jvm_env, message);
   if (o_message == NULL)
+  {
+    cjni_thread_detach ();
     return;
+  }
 
   (*jvm_env)->CallVoidMethod (jvm_env,
       cbi->object, cbi->method, (jint) severity, o_message);
@@ -2685,7 +2670,6 @@ static int cjni_notification (const notification_t *n, /* {{{ */
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
   jobject o_notification;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2710,6 +2694,7 @@ static int cjni_notification (const notification_t *n, /* {{{ */
   if (o_notification == NULL)
   {
     ERROR ("java plugin: cjni_notification: ctoj_notification failed.");
+    cjni_thread_detach ();
     return (-1);
   }
 
@@ -2718,13 +2703,7 @@ static int cjni_notification (const notification_t *n, /* {{{ */
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_notification */
 
@@ -2752,24 +2731,20 @@ static int cjni_match_target_create (const oconfig_item_t *ci, /* {{{ */
       (*jvm_env)->DeleteLocalRef (jvm_env, cbi_ret->object); \
   } \
   free (cbi_ret); \
-  if (jvm_env != NULL) { \
-    if (o_ci != NULL) \
-      (*jvm_env)->DeleteLocalRef (jvm_env, o_ci); \
-    cjni_thread_detach (); \
-  } \
+  if (o_ci != NULL) \
+    (*jvm_env)->DeleteLocalRef (jvm_env, o_ci); \
+  cjni_thread_detach (); \
   return (status)
 
   if (jvm == NULL)
   {
     ERROR ("java plugin: cjni_read: jvm == NULL");
-    BAIL_OUT (-1);
+    return (-1);
   }
 
   jvm_env = cjni_thread_attach ();
   if (jvm_env == NULL)
-  {
-    BAIL_OUT (-1);
-  }
+    return (-1);
 
   /* Find out whether to create a match or a target. */
   if (strcasecmp ("Match", ci->key) == 0)
@@ -2963,10 +2938,7 @@ static int cjni_match_target_invoke (const data_set_t *ds, /* {{{ */
     }
   } /* if (cbi->type == CB_TYPE_TARGET) */
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-    ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_match_target_invoke */
 
index 1d4dff5..5abee2f 100644 (file)
@@ -11,7 +11,10 @@ nodist_pkgconfig_DATA = libcollectdclient.pc
 BUILT_SOURCES = collectd/lcc_features.h
 
 libcollectdclient_la_SOURCES = client.c network.c network_buffer.c
-libcollectdclient_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/libcollectdclient/collectd -I$(top_srcdir)/src
+libcollectdclient_la_CPPFLAGS = $(AM_CPPFLAGS) \
+                               -I$(top_srcdir)/src/libcollectdclient/collectd \
+                               -I$(top_builddir)/src/libcollectdclient/collectd \
+                               -I$(top_srcdir)/src/daemon
 libcollectdclient_la_LDFLAGS = -version-info 1:0:0
 libcollectdclient_la_LIBADD = 
 if BUILD_WITH_LIBGCRYPT
index 7b06620..61c7c22 100644 (file)
@@ -54,7 +54,9 @@
 /* Re enable deprecation warnings */
 #  pragma GCC diagnostic warning "-Wdeprecated-declarations"
 # endif
+# if GCRYPT_VERSION_NUMBER < 0x010600
 GCRY_THREAD_OPTION_PTHREAD_IMPL;
+# endif
 #endif
 
 #include "collectd/network_buffer.h"
@@ -131,7 +133,9 @@ static _Bool have_gcrypt (void) /* {{{ */
   need_init = 0;
 
 #if HAVE_LIBGCRYPT
+# if GCRYPT_VERSION_NUMBER < 0x010600
   gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+# endif
 
   if (!gcry_check_version (GCRYPT_VERSION))
     return (0);
index b6d757e..cb3754d 100644 (file)
@@ -65,9 +65,20 @@ BOOL_TRUE (true|yes|on)
 BOOL_FALSE (false|no|off)
 COMMENT #.*
 PORT (6(5(5(3[0-5]|[0-2][0-9])|[0-4][0-9][0-9])|[0-4][0-9][0-9][0-9])|[1-5][0-9][0-9][0-9][0-9]|[1-9][0-9]?[0-9]?[0-9]?)
+
 IP_BYTE (2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])
 IPV4_ADDR {IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}(:{PORT})?
 
+/* IPv6 address according to http://www.ietf.org/rfc/rfc2373.txt
+ * This supports embedded IPv4 addresses as well but does not strictly check
+ * for the right prefix (::0:<v4> or ::FFFF:<v4>) because there are too many
+ * ways to correctly represent the zero bytes. It's up to the user to check
+ * for valid addresses. */
+HEX16 ([0-9A-Fa-f]{1,4})
+V6_PART ({HEX16}:{HEX16}|{IPV4_ADDR})
+IPV6_BASE ({HEX16}:){6}{V6_PART}|::({HEX16}:){5}{V6_PART}|({HEX16})?::({HEX16}:){4}{V6_PART}|(({HEX16}:){0,1}{HEX16})?::({HEX16}:){3}{V6_PART}|(({HEX16}:){0,2}{HEX16})?::({HEX16}:){2}{V6_PART}|(({HEX16}:){0,3}{HEX16})?::{HEX16}:{V6_PART}|(({HEX16}:){0,4}{HEX16})?::{V6_PART}|(({HEX16}:){0,5}{HEX16})?::{HEX16}|(({HEX16}:){0,6}{HEX16})?::
+IPV6_ADDR ({IPV6_BASE})|(\[{IPV6_BASE}\](:{PORT})?)
+
 %%
 {WHITE_SPACE}          |
 {COMMENT}              {/* ignore */}
@@ -82,6 +93,7 @@ IPV4_ADDR {IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}(:{PORT})?
 {BOOL_FALSE}           {yylval.boolean = 0; return (BFALSE);}
 
 {IPV4_ADDR}            {yylval.string = yytext; return (UNQUOTED_STRING);}
+{IPV6_ADDR}            {yylval.string = yytext; return (UNQUOTED_STRING);}
 
 {NUMBER}               {yylval.number = strtod (yytext, NULL); return (NUMBER);}
 
diff --git a/src/libvirt.c b/src/libvirt.c
deleted file mode 100644 (file)
index b0c694a..0000000
+++ /dev/null
@@ -1,1004 +0,0 @@
-/**
- * collectd - src/libvirt.c
- * Copyright (C) 2006-2008  Red Hat Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the license is applicable.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- *
- * Authors:
- *   Richard W.M. Jones <rjones@redhat.com>
- **/
-
-#include "collectd.h"
-#include "common.h"
-#include "plugin.h"
-#include "configfile.h"
-#include "utils_ignorelist.h"
-#include "utils_complain.h"
-
-#include <libvirt/libvirt.h>
-#include <libvirt/virterror.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include <libxml/xpath.h>
-
-/* Plugin name */
-#define PLUGIN_NAME "libvirt"
-
-static const char *config_keys[] = {
-    "Connection",
-
-    "RefreshInterval",
-
-    "Domain",
-    "BlockDevice",
-    "InterfaceDevice",
-    "IgnoreSelected",
-
-    "HostnameFormat",
-    "InterfaceFormat",
-
-    "PluginInstanceFormat",
-
-    NULL
-};
-#define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
-
-/* Connection. */
-static virConnectPtr conn = 0;
-static char *conn_string = NULL;
-static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
-
-/* Seconds between list refreshes, 0 disables completely. */
-static int interval = 60;
-
-/* List of domains, if specified. */
-static ignorelist_t *il_domains = NULL;
-/* List of block devices, if specified. */
-static ignorelist_t *il_block_devices = NULL;
-/* List of network interface devices, if specified. */
-static ignorelist_t *il_interface_devices = NULL;
-
-static int ignore_device_match (ignorelist_t *,
-                                const char *domname, const char *devpath);
-
-/* Actual list of domains found on last refresh. */
-static virDomainPtr *domains = NULL;
-static int nr_domains = 0;
-
-static void free_domains (void);
-static int add_domain (virDomainPtr dom);
-
-/* Actual list of block devices found on last refresh. */
-struct block_device {
-    virDomainPtr dom;           /* domain */
-    char *path;                 /* name of block device */
-};
-
-static struct block_device *block_devices = NULL;
-static int nr_block_devices = 0;
-
-static void free_block_devices (void);
-static int add_block_device (virDomainPtr dom, const char *path);
-
-/* Actual list of network interfaces found on last refresh. */
-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, unsigned int number);
-
-/* HostnameFormat. */
-#define HF_MAX_FIELDS 3
-
-enum hf_field {
-    hf_none = 0,
-    hf_hostname,
-    hf_name,
-    hf_uuid
-};
-
-static enum hf_field hostname_format[HF_MAX_FIELDS] =
-    { hf_name };
-
-/* PluginInstanceFormat */
-#define PLGINST_MAX_FIELDS 2
-
-enum plginst_field {
-    plginst_none = 0,
-    plginst_name,
-    plginst_uuid
-};
-
-static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] =
-    { plginst_name };
-
-/* InterfaceFormat. */
-enum if_field {
-    if_address,
-    if_name,
-    if_number
-};
-
-static enum if_field interface_format = if_name;
-
-/* Time that we last refreshed. */
-static time_t last_refresh = (time_t) 0;
-
-static int refresh_lists (void);
-
-/* ERROR(...) macro for virterrors. */
-#define VIRT_ERROR(conn,s) do {                 \
-        virErrorPtr err;                        \
-        err = (conn) ? virConnGetLastError ((conn)) : virGetLastError (); \
-        if (err) ERROR ("%s: %s", (s), err->message);                   \
-    } while(0)
-
-static void
-init_value_list (value_list_t *vl, virDomainPtr dom)
-{
-    int i, n;
-    const char *name;
-    char uuid[VIR_UUID_STRING_BUFLEN];
-
-    sstrncpy (vl->plugin, PLUGIN_NAME, sizeof (vl->plugin));
-
-    vl->host[0] = '\0';
-
-    /* Construct the hostname field according to HostnameFormat. */
-    for (i = 0; i < HF_MAX_FIELDS; ++i) {
-        if (hostname_format[i] == hf_none)
-            continue;
-
-        n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2;
-
-        if (i > 0 && n >= 1) {
-            strncat (vl->host, ":", 1);
-            n--;
-        }
-
-        switch (hostname_format[i]) {
-        case hf_none: break;
-        case hf_hostname:
-            strncat (vl->host, hostname_g, n);
-            break;
-        case hf_name:
-            name = virDomainGetName (dom);
-            if (name)
-                strncat (vl->host, name, n);
-            break;
-        case hf_uuid:
-            if (virDomainGetUUIDString (dom, uuid) == 0)
-                strncat (vl->host, uuid, n);
-            break;
-        }
-    }
-
-    vl->host[sizeof (vl->host) - 1] = '\0';
-
-    /* Construct the plugin instance field according to PluginInstanceFormat. */
-    for (i = 0; i < PLGINST_MAX_FIELDS; ++i) {
-        if (plugin_instance_format[i] == plginst_none)
-            continue;
-
-        n = sizeof(vl->plugin_instance) - strlen (vl->plugin_instance) - 2;
-
-        if (i > 0 && n >= 1) {
-            strncat (vl->plugin_instance, ":", 1);
-            n--;
-        }
-
-        switch (plugin_instance_format[i]) {
-        case plginst_none: break;
-        case plginst_name:
-            name = virDomainGetName (dom);
-            if (name)
-                strncat (vl->plugin_instance, name, n);
-            break;
-        case plginst_uuid:
-            if (virDomainGetUUIDString (dom, uuid) == 0)
-                strncat (vl->plugin_instance, uuid, n);
-            break;
-        }
-    }
-
-    vl->plugin_instance[sizeof (vl->plugin_instance) - 1] = '\0';
-
-} /* void init_value_list */
-
-static void
-memory_submit (gauge_t memory, virDomainPtr dom)
-{
-    value_t values[1];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, dom);
-
-    values[0].gauge = memory;
-
-    vl.values = values;
-    vl.values_len = 1;
-
-    sstrncpy (vl.type, "memory", sizeof (vl.type));
-    sstrncpy (vl.type_instance, "total", sizeof (vl.type_instance));
-
-    plugin_dispatch_values (&vl);
-}
-
-static void
-memory_stats_submit (gauge_t memory, virDomainPtr dom, int tag_index)
-{
-    static const char *tags[] = { "swap_in", "swap_out", "major_fault", "minor_fault",
-                                    "unused", "available", "actual_balloon", "rss"};
-
-    value_t values[1];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, dom);
-
-    values[0].gauge = memory;
-
-    vl.values = values;
-    vl.values_len = 1;
-
-    sstrncpy (vl.type, "memory", sizeof (vl.type));
-    sstrncpy (vl.type_instance, tags[tag_index], sizeof (vl.type_instance));
-
-    plugin_dispatch_values (&vl);
-}
-
-static void
-cpu_submit (unsigned long long cpu_time,
-            virDomainPtr dom, const char *type)
-{
-    value_t values[1];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, dom);
-
-    values[0].derive = cpu_time;
-
-    vl.values = values;
-    vl.values_len = 1;
-
-    sstrncpy (vl.type, type, sizeof (vl.type));
-
-    plugin_dispatch_values (&vl);
-}
-
-static void
-vcpu_submit (derive_t cpu_time,
-             virDomainPtr dom, int vcpu_nr, const char *type)
-{
-    value_t values[1];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, dom);
-
-    values[0].derive = cpu_time;
-    vl.values = values;
-    vl.values_len = 1;
-
-    sstrncpy (vl.type, type, sizeof (vl.type));
-    ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
-
-    plugin_dispatch_values (&vl);
-}
-
-static void
-submit_derive2 (const char *type, derive_t v0, derive_t v1,
-             virDomainPtr dom, const char *devname)
-{
-    value_t values[2];
-    value_list_t vl = VALUE_LIST_INIT;
-
-    init_value_list (&vl, dom);
-
-    values[0].derive = v0;
-    values[1].derive = v1;
-    vl.values = values;
-    vl.values_len = 2;
-
-    sstrncpy (vl.type, type, sizeof (vl.type));
-    sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
-
-    plugin_dispatch_values (&vl);
-} /* void submit_derive2 */
-
-static int
-lv_init (void)
-{
-    if (virInitialize () != 0)
-        return -1;
-
-       return 0;
-}
-
-static int
-lv_config (const char *key, const char *value)
-{
-    if (virInitialize () != 0)
-        return 1;
-
-    if (il_domains == NULL)
-        il_domains = ignorelist_create (1);
-    if (il_block_devices == NULL)
-        il_block_devices = ignorelist_create (1);
-    if (il_interface_devices == NULL)
-        il_interface_devices = ignorelist_create (1);
-
-    if (strcasecmp (key, "Connection") == 0) {
-        char *tmp = strdup (value);
-        if (tmp == NULL) {
-            ERROR (PLUGIN_NAME " plugin: Connection strdup failed.");
-            return 1;
-        }
-        sfree (conn_string);
-        conn_string = tmp;
-        return 0;
-    }
-
-    if (strcasecmp (key, "RefreshInterval") == 0) {
-        char *eptr = NULL;
-        interval = strtol (value, &eptr, 10);
-        if (eptr == NULL || *eptr != '\0') return 1;
-        return 0;
-    }
-
-    if (strcasecmp (key, "Domain") == 0) {
-        if (ignorelist_add (il_domains, value)) return 1;
-        return 0;
-    }
-    if (strcasecmp (key, "BlockDevice") == 0) {
-        if (ignorelist_add (il_block_devices, value)) return 1;
-        return 0;
-    }
-    if (strcasecmp (key, "InterfaceDevice") == 0) {
-        if (ignorelist_add (il_interface_devices, value)) return 1;
-        return 0;
-    }
-
-    if (strcasecmp (key, "IgnoreSelected") == 0) {
-        if (IS_TRUE (value))
-        {
-            ignorelist_set_invert (il_domains, 0);
-            ignorelist_set_invert (il_block_devices, 0);
-            ignorelist_set_invert (il_interface_devices, 0);
-        }
-        else
-        {
-            ignorelist_set_invert (il_domains, 1);
-            ignorelist_set_invert (il_block_devices, 1);
-            ignorelist_set_invert (il_interface_devices, 1);
-        }
-        return 0;
-    }
-
-    if (strcasecmp (key, "HostnameFormat") == 0) {
-        char *value_copy;
-        char *fields[HF_MAX_FIELDS];
-        int i, n;
-
-        value_copy = strdup (value);
-        if (value_copy == NULL) {
-            ERROR (PLUGIN_NAME " plugin: strdup failed.");
-            return -1;
-        }
-
-        n = strsplit (value_copy, fields, HF_MAX_FIELDS);
-        if (n < 1) {
-            sfree (value_copy);
-            ERROR (PLUGIN_NAME " plugin: HostnameFormat: no fields");
-            return -1;
-        }
-
-        for (i = 0; i < n; ++i) {
-            if (strcasecmp (fields[i], "hostname") == 0)
-                hostname_format[i] = hf_hostname;
-            else if (strcasecmp (fields[i], "name") == 0)
-                hostname_format[i] = hf_name;
-            else if (strcasecmp (fields[i], "uuid") == 0)
-                hostname_format[i] = hf_uuid;
-            else {
-                sfree (value_copy);
-                ERROR (PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", fields[i]);
-                return -1;
-            }
-        }
-        sfree (value_copy);
-
-        for (i = n; i < HF_MAX_FIELDS; ++i)
-            hostname_format[i] = hf_none;
-
-        return 0;
-    }
-
-    if (strcasecmp (key, "PluginInstanceFormat") == 0) {
-        char *value_copy;
-        char *fields[PLGINST_MAX_FIELDS];
-        int i, n;
-
-        value_copy = strdup (value);
-        if (value_copy == NULL) {
-            ERROR (PLUGIN_NAME " plugin: strdup failed.");
-            return -1;
-        }
-
-        n = strsplit (value_copy, fields, PLGINST_MAX_FIELDS);
-        if (n < 1) {
-            sfree (value_copy);
-            ERROR (PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
-            return -1;
-        }
-
-        for (i = 0; i < n; ++i) {
-            if (strcasecmp (fields[i], "name") == 0)
-                plugin_instance_format[i] = plginst_name;
-            else if (strcasecmp (fields[i], "uuid") == 0)
-                plugin_instance_format[i] = plginst_uuid;
-            else {
-                sfree (value_copy);
-                ERROR (PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", fields[i]);
-                return -1;
-            }
-        }
-        sfree (value_copy);
-
-        for (i = n; i < PLGINST_MAX_FIELDS; ++i)
-            plugin_instance_format[i] = plginst_none;
-
-        return 0;
-    }
-
-    if (strcasecmp (key, "InterfaceFormat") == 0) {
-        if (strcasecmp (value, "name") == 0)
-            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 (PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", value);
-            return -1;
-        }
-        return 0;
-    }
-
-    /* Unrecognised option. */
-    return -1;
-}
-
-static int
-lv_read (void)
-{
-    time_t t;
-    int i;
-
-    if (conn == NULL) {
-        /* `conn_string == NULL' is acceptable. */
-        conn = virConnectOpenReadOnly (conn_string);
-        if (conn == NULL) {
-            c_complain (LOG_ERR, &conn_complain,
-                    PLUGIN_NAME " plugin: Unable to connect: "
-                    "virConnectOpenReadOnly failed.");
-            return -1;
-        }
-    }
-    c_release (LOG_NOTICE, &conn_complain,
-            PLUGIN_NAME " plugin: Connection established.");
-
-    time (&t);
-
-    /* Need to refresh domain or device lists? */
-    if ((last_refresh == (time_t) 0) ||
-            ((interval > 0) && ((last_refresh + interval) <= t))) {
-        if (refresh_lists () != 0) {
-            if (conn != NULL)
-                virConnectClose (conn);
-            conn = NULL;
-            return -1;
-        }
-        last_refresh = t;
-    }
-
-#if 0
-    for (i = 0; i < nr_domains; ++i)
-        fprintf (stderr, "domain %s\n", virDomainGetName (domains[i]));
-    for (i = 0; i < nr_block_devices; ++i)
-        fprintf  (stderr, "block device %d %s:%s\n",
-                  i, virDomainGetName (block_devices[i].dom),
-                  block_devices[i].path);
-    for (i = 0; i < nr_interface_devices; ++i)
-        fprintf (stderr, "interface device %d %s:%s\n",
-                 i, virDomainGetName (interface_devices[i].dom),
-                 interface_devices[i].path);
-#endif
-
-    /* Get CPU usage, memory, VCPU usage for each domain. */
-    for (i = 0; i < nr_domains; ++i) {
-        virDomainInfo info;
-        virVcpuInfoPtr vinfo = NULL;
-        virDomainMemoryStatPtr minfo = NULL;
-        int status;
-        int j;
-
-        status = virDomainGetInfo (domains[i], &info);
-        if (status != 0)
-        {
-            ERROR (PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
-                    status);
-            continue;
-        }
-
-        cpu_submit (info.cpuTime, domains[i], "virt_cpu_total");
-        memory_submit ((gauge_t) info.memory * 1024, domains[i]);
-
-        vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0]));
-        if (vinfo == NULL) {
-            ERROR (PLUGIN_NAME " plugin: malloc failed.");
-            continue;
-        }
-
-        status = virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu,
-                /* cpu map = */ NULL, /* cpu map length = */ 0);
-        if (status < 0)
-        {
-            ERROR (PLUGIN_NAME " plugin: virDomainGetVcpus failed with status %i.",
-                    status);
-            sfree (vinfo);
-            continue;
-        }
-
-        for (j = 0; j < info.nrVirtCpu; ++j)
-            vcpu_submit (vinfo[j].cpuTime,
-                    domains[i], vinfo[j].number, "virt_vcpu");
-
-        sfree (vinfo);
-
-        minfo = malloc (VIR_DOMAIN_MEMORY_STAT_NR * sizeof (virDomainMemoryStatStruct));
-        if (minfo == NULL) {
-            ERROR ("libvirt plugin: malloc failed.");
-            continue;
-        }
-
-        status =  virDomainMemoryStats (domains[i], minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
-
-        if (status < 0) {
-            ERROR ("libvirt plugin: virDomainMemoryStats failed with status %i.",
-                    status);
-            sfree (minfo);
-            continue;
-        }
-
-        for (j = 0; j < status; j++) {
-            memory_stats_submit ((gauge_t) minfo[j].val, domains[i], minfo[j].tag);
-        }
-
-        sfree (minfo);
-    }
-
-
-    /* Get block device stats for each domain. */
-    for (i = 0; i < nr_block_devices; ++i) {
-        struct _virDomainBlockStats stats;
-
-        if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path,
-                    &stats, sizeof stats) != 0)
-            continue;
-
-        if ((stats.rd_req != -1) && (stats.wr_req != -1))
-            submit_derive2 ("disk_ops",
-                    (derive_t) stats.rd_req, (derive_t) stats.wr_req,
-                    block_devices[i].dom, block_devices[i].path);
-
-        if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
-            submit_derive2 ("disk_octets",
-                    (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes,
-                    block_devices[i].dom, block_devices[i].path);
-    } /* for (nr_block_devices) */
-
-    /* Get interface stats for each domain. */
-    for (i = 0; i < nr_interface_devices; ++i) {
-        struct _virDomainInterfaceStats stats;
-        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,
-                    &stats, sizeof stats) != 0)
-            continue;
-
-       if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
-           submit_derive2 ("if_octets",
-                   (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes,
-                   interface_devices[i].dom, display_name);
-
-       if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
-           submit_derive2 ("if_packets",
-                   (derive_t) stats.rx_packets, (derive_t) stats.tx_packets,
-                   interface_devices[i].dom, display_name);
-
-       if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
-           submit_derive2 ("if_errors",
-                   (derive_t) stats.rx_errs, (derive_t) stats.tx_errs,
-                   interface_devices[i].dom, display_name);
-
-       if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
-           submit_derive2 ("if_dropped",
-                   (derive_t) stats.rx_drop, (derive_t) stats.tx_drop,
-                   interface_devices[i].dom, display_name);
-    } /* for (nr_interface_devices) */
-
-    return 0;
-}
-
-static int
-refresh_lists (void)
-{
-    int n;
-
-    n = virConnectNumOfDomains (conn);
-    if (n < 0) {
-        VIRT_ERROR (conn, "reading number of domains");
-        return -1;
-    }
-
-    if (n > 0) {
-        int i;
-        int *domids;
-
-        /* Get list of domains. */
-        domids = malloc (sizeof (int) * n);
-        if (domids == 0) {
-            ERROR (PLUGIN_NAME " plugin: malloc failed.");
-            return -1;
-        }
-
-        n = virConnectListDomains (conn, domids, n);
-        if (n < 0) {
-            VIRT_ERROR (conn, "reading list of domains");
-            sfree (domids);
-            return -1;
-        }
-
-        free_block_devices ();
-        free_interface_devices ();
-        free_domains ();
-
-        /* Fetch each domain and add it to the list, unless ignore. */
-        for (i = 0; i < n; ++i) {
-            virDomainPtr dom = NULL;
-            const char *name;
-            char *xml = NULL;
-            xmlDocPtr xml_doc = NULL;
-            xmlXPathContextPtr xpath_ctx = NULL;
-            xmlXPathObjectPtr xpath_obj = NULL;
-            int j;
-
-            dom = virDomainLookupByID (conn, domids[i]);
-            if (dom == NULL) {
-                VIRT_ERROR (conn, "virDomainLookupByID");
-                /* Could be that the domain went away -- ignore it anyway. */
-                continue;
-            }
-
-            name = virDomainGetName (dom);
-            if (name == NULL) {
-                VIRT_ERROR (conn, "virDomainGetName");
-                goto cont;
-            }
-
-            if (il_domains && ignorelist_match (il_domains, name) != 0)
-                goto cont;
-
-            if (add_domain (dom) < 0) {
-                ERROR (PLUGIN_NAME " plugin: malloc failed.");
-                goto cont;
-            }
-
-            /* Get a list of devices for this domain. */
-            xml = virDomainGetXMLDesc (dom, 0);
-            if (!xml) {
-                VIRT_ERROR (conn, "virDomainGetXMLDesc");
-                goto cont;
-            }
-
-            /* Yuck, XML.  Parse out the devices. */
-            xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET);
-            if (xml_doc == NULL) {
-                VIRT_ERROR (conn, "xmlReadDoc");
-                goto cont;
-            }
-
-            xpath_ctx = xmlXPathNewContext (xml_doc);
-
-            /* Block devices. */
-            xpath_obj = xmlXPathEval
-                ((xmlChar *) "/domain/devices/disk/target[@dev]",
-                 xpath_ctx);
-            if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
-                xpath_obj->nodesetval == NULL)
-                goto cont;
-
-            for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
-                xmlNodePtr node;
-                char *path = NULL;
-
-                node = xpath_obj->nodesetval->nodeTab[j];
-                if (!node) continue;
-                path = (char *) xmlGetProp (node, (xmlChar *) "dev");
-                if (!path) continue;
-
-                if (il_block_devices &&
-                    ignore_device_match (il_block_devices, name, path) != 0)
-                    goto cont2;
-
-                add_block_device (dom, path);
-            cont2:
-                if (path) xmlFree (path);
-            }
-            xmlXPathFreeObject (xpath_obj);
-
-            /* Network interfaces. */
-            xpath_obj = xmlXPathEval
-                ((xmlChar *) "/domain/devices/interface[target[@dev]]",
-                 xpath_ctx);
-            if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
-                xpath_obj->nodesetval == NULL)
-                goto cont;
-
-            xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
-
-            for (j = 0; j < xml_interfaces->nodeNr; ++j) {
-                char *path = NULL;
-                char *address = NULL;
-                xmlNodePtr xml_interface;
-
-                xml_interface = xml_interfaces->nodeTab[j];
-                if (!xml_interface) continue;
-                xmlNodePtr child = NULL;
-
-                for (child = xml_interface->children; child; child = child->next) {
-                    if (child->type != XML_ELEMENT_NODE) continue;
-
-                    if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
-                        path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
-                        if (!path) continue;
-                    } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
-                        address = (char *) xmlGetProp (child, (const xmlChar *) "address");
-                        if (!address) continue;
-                    }
-                }
-
-                if (il_interface_devices &&
-                    (ignore_device_match (il_interface_devices, name, path) != 0 ||
-                     ignore_device_match (il_interface_devices, name, address) != 0))
-                    goto cont3;
-
-                add_interface_device (dom, path, address, j+1);
-                cont3:
-                    if (path) xmlFree (path);
-                    if (address) xmlFree (address);
-            }
-
-        cont:
-            if (xpath_obj) xmlXPathFreeObject (xpath_obj);
-            if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
-            if (xml_doc) xmlFreeDoc (xml_doc);
-            sfree (xml);
-        }
-
-        sfree (domids);
-    }
-
-    return 0;
-}
-
-static void
-free_domains ()
-{
-    int i;
-
-    if (domains) {
-        for (i = 0; i < nr_domains; ++i)
-            virDomainFree (domains[i]);
-        sfree (domains);
-    }
-    domains = NULL;
-    nr_domains = 0;
-}
-
-static int
-add_domain (virDomainPtr dom)
-{
-    virDomainPtr *new_ptr;
-    int new_size = sizeof (domains[0]) * (nr_domains+1);
-
-    if (domains)
-        new_ptr = realloc (domains, new_size);
-    else
-        new_ptr = malloc (new_size);
-
-    if (new_ptr == NULL)
-        return -1;
-
-    domains = new_ptr;
-    domains[nr_domains] = dom;
-    return nr_domains++;
-}
-
-static void
-free_block_devices ()
-{
-    int i;
-
-    if (block_devices) {
-        for (i = 0; i < nr_block_devices; ++i)
-            sfree (block_devices[i].path);
-        sfree (block_devices);
-    }
-    block_devices = NULL;
-    nr_block_devices = 0;
-}
-
-static int
-add_block_device (virDomainPtr dom, const char *path)
-{
-    struct block_device *new_ptr;
-    int new_size = sizeof (block_devices[0]) * (nr_block_devices+1);
-    char *path_copy;
-
-    path_copy = strdup (path);
-    if (!path_copy)
-        return -1;
-
-    if (block_devices)
-        new_ptr = realloc (block_devices, new_size);
-    else
-        new_ptr = malloc (new_size);
-
-    if (new_ptr == NULL) {
-        sfree (path_copy);
-        return -1;
-    }
-    block_devices = new_ptr;
-    block_devices[nr_block_devices].dom = dom;
-    block_devices[nr_block_devices].path = path_copy;
-    return nr_block_devices++;
-}
-
-static void
-free_interface_devices ()
-{
-    int i;
-
-    if (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);
-    }
-    interface_devices = NULL;
-    nr_interface_devices = 0;
-}
-
-static int
-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, number_string[15];
-
-    path_copy = strdup (path);
-    if (!path_copy) return -1;
-
-    address_copy = strdup (address);
-    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);
-    else
-        new_ptr = malloc (new_size);
-
-    if (new_ptr == NULL) {
-        sfree (path_copy);
-        sfree (address_copy);
-        return -1;
-    }
-    interface_devices = new_ptr;
-    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++;
-}
-
-static int
-ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
-{
-    char *name;
-    int n, r;
-
-    n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2);
-    name = malloc (n);
-    if (name == NULL) {
-        ERROR (PLUGIN_NAME " plugin: malloc failed.");
-        return 0;
-    }
-    ssnprintf (name, n, "%s:%s", domname, devpath);
-    r = ignorelist_match (il, name);
-    sfree (name);
-    return r;
-}
-
-static int
-lv_shutdown (void)
-{
-    free_block_devices ();
-    free_interface_devices ();
-    free_domains ();
-
-    if (conn != NULL)
-        virConnectClose (conn);
-    conn = NULL;
-
-    ignorelist_free (il_domains);
-    il_domains = NULL;
-    ignorelist_free (il_block_devices);
-    il_block_devices = NULL;
-    ignorelist_free (il_interface_devices);
-    il_interface_devices = NULL;
-
-    return 0;
-}
-
-void
-module_register (void)
-{
-    plugin_register_config (PLUGIN_NAME,
-    lv_config,
-    config_keys, NR_CONFIG_KEYS);
-    plugin_register_init (PLUGIN_NAME, lv_init);
-    plugin_register_read (PLUGIN_NAME, lv_read);
-    plugin_register_shutdown (PLUGIN_NAME, lv_shutdown);
-}
-
-/*
- * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
- */
index f631476..18b5f8e 100644 (file)
@@ -23,6 +23,7 @@
  *   Vedran Bartonicek <vbartoni at gmail.com>
  **/
 
+#define _DEFAULT_SOURCE
 #define _BSD_SOURCE
 
 #include "collectd.h"
index 82d7f6f..9e24542 100644 (file)
--- a/src/lvm.c
+++ b/src/lvm.c
@@ -125,10 +125,6 @@ static void vg_read(vg_t vg, char const *vg_name)
     }
 
     dm_list_iterate_items(lvl, lvs) {
-         lvm_submit(vg_name, lvm_lv_get_name(lvl->lv), lvm_lv_get_size(lvl->lv));
-    }
-
-    dm_list_iterate_items(lvl, lvs) {
         name = lvm_lv_get_name(lvl->lv);
         attrs = get_lv_property_string(lvl->lv, "lv_attr");
         size = lvm_lv_get_size(lvl->lv);
index 93dbdbc..37f1894 100644 (file)
@@ -500,6 +500,7 @@ static int cmc_read_page (web_page_t *wp) /* {{{ */
     }
 
     cmc_submit (wp, wm, mv);
+    match_value_reset (mv);
   } /* for (wm = wp->matches; wm != NULL; wm = wm->next) */
 
   sfree (wp->buffer);
index c503821..0f5e1a0 100644 (file)
@@ -278,11 +278,16 @@ static int memory_read_internal (value_list_t *vl)
        char *fields[8];
        int numfields;
 
+       _Bool detailed_slab_info = 0;
+
        gauge_t mem_total = 0;
        gauge_t mem_used = 0;
        gauge_t mem_buffered = 0;
        gauge_t mem_cached = 0;
        gauge_t mem_free = 0;
+       gauge_t mem_slab_total = 0;
+       gauge_t mem_slab_reclaimable = 0;
+       gauge_t mem_slab_unreclaimable = 0;
 
        if ((fh = fopen ("/proc/meminfo", "r")) == NULL)
        {
@@ -304,6 +309,16 @@ static int memory_read_internal (value_list_t *vl)
                        val = &mem_buffered;
                else if (strncasecmp (buffer, "Cached:", 7) == 0)
                        val = &mem_cached;
+               else if (strncasecmp (buffer, "Slab:", 5) == 0)
+                       val = &mem_slab_total;
+               else if (strncasecmp (buffer, "SReclaimable:", 13) == 0) {
+                       val = &mem_slab_reclaimable;
+                       detailed_slab_info = 1;
+               }
+               else if (strncasecmp (buffer, "SUnreclaim:", 11) == 0) {
+                       val = &mem_slab_unreclaimable;
+                       detailed_slab_info = 1;
+               }
                else
                        continue;
 
@@ -321,14 +336,28 @@ static int memory_read_internal (value_list_t *vl)
                                sstrerror (errno, errbuf, sizeof (errbuf)));
        }
 
-       if (mem_total < (mem_free + mem_buffered + mem_cached))
+       if (mem_total < (mem_free + mem_buffered + mem_cached + mem_slab_total))
                return (-1);
 
-       mem_used = mem_total - (mem_free + mem_buffered + mem_cached);
-       MEMORY_SUBMIT ("used",     mem_used,
-                      "buffered", mem_buffered,
-                      "cached",   mem_cached,
-                      "free",     mem_free);
+       mem_used = mem_total - (mem_free + mem_buffered + mem_cached + mem_slab_total);
+
+       /* SReclaimable and SUnreclaim were introduced in kernel 2.6.19
+        * They sum up to the value of Slab, which is available on older & newer
+        * kernels. So SReclaimable/SUnreclaim are submitted if available, and Slab
+        * if not. */
+       if (detailed_slab_info)
+               MEMORY_SUBMIT ("used",        mem_used,
+                              "buffered",    mem_buffered,
+                              "cached",      mem_cached,
+                              "free",        mem_free,
+                              "slab_unrecl", mem_slab_unreclaimable,
+                              "slab_recl",   mem_slab_reclaimable);
+       else
+               MEMORY_SUBMIT ("used",     mem_used,
+                              "buffered", mem_buffered,
+                              "cached",   mem_cached,
+                              "free",     mem_free,
+                              "slab",     mem_slab_total);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
diff --git a/src/meta_data.c b/src/meta_data.c
deleted file mode 100644 (file)
index fb9b3d4..0000000
+++ /dev/null
@@ -1,631 +0,0 @@
-/**
- * collectd - src/meta_data.c
- * Copyright (C) 2008-2011  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 "collectd.h"
-#include "plugin.h"
-#include "meta_data.h"
-
-#include <pthread.h>
-
-/*
- * Data types
- */
-union meta_value_u
-{
-  char    *mv_string;
-  int64_t  mv_signed_int;
-  uint64_t mv_unsigned_int;
-  double   mv_double;
-  _Bool    mv_boolean;
-};
-typedef union meta_value_u meta_value_t;
-
-struct meta_entry_s;
-typedef struct meta_entry_s meta_entry_t;
-struct meta_entry_s
-{
-  char         *key;
-  meta_value_t  value;
-  int           type;
-  meta_entry_t *next;
-};
-
-struct meta_data_s
-{
-  meta_entry_t   *head;
-  pthread_mutex_t lock;
-};
-
-/*
- * Private functions
- */
-static char *md_strdup (const char *orig) /* {{{ */
-{
-  size_t sz;
-  char *dest;
-
-  if (orig == NULL)
-    return (NULL);
-
-  sz = strlen (orig) + 1;
-  dest = (char *) malloc (sz);
-  if (dest == NULL)
-    return (NULL);
-
-  memcpy (dest, orig, sz);
-
-  return (dest);
-} /* }}} char *md_strdup */
-
-static meta_entry_t *md_entry_alloc (const char *key) /* {{{ */
-{
-  meta_entry_t *e;
-
-  e = (meta_entry_t *) malloc (sizeof (*e));
-  if (e == NULL)
-  {
-    ERROR ("md_entry_alloc: malloc failed.");
-    return (NULL);
-  }
-  memset (e, 0, sizeof (*e));
-
-  e->key = md_strdup (key);
-  if (e->key == NULL)
-  {
-    free (e);
-    ERROR ("md_entry_alloc: md_strdup failed.");
-    return (NULL);
-  }
-
-  e->type = 0;
-  e->next = NULL;
-
-  return (e);
-} /* }}} meta_entry_t *md_entry_alloc */
-
-static meta_entry_t *md_entry_clone (const meta_entry_t *orig) /* {{{ */
-{
-  meta_entry_t *copy;
-
-  if (orig == NULL)
-    return (NULL);
-
-  copy = md_entry_alloc (orig->key);
-  copy->type = orig->type;
-  if (copy->type == MD_TYPE_STRING)
-    copy->value.mv_string = strdup (orig->value.mv_string);
-  else
-    copy->value = orig->value;
-
-  copy->next = md_entry_clone (orig->next);
-  return (copy);
-} /* }}} meta_entry_t *md_entry_clone */
-
-static void md_entry_free (meta_entry_t *e) /* {{{ */
-{
-  if (e == NULL)
-    return;
-
-  free (e->key);
-
-  if (e->type == MD_TYPE_STRING)
-    free (e->value.mv_string);
-
-  if (e->next != NULL)
-    md_entry_free (e->next);
-
-  free (e);
-} /* }}} void md_entry_free */
-
-static int md_entry_insert (meta_data_t *md, meta_entry_t *e) /* {{{ */
-{
-  meta_entry_t *this;
-  meta_entry_t *prev;
-
-  if ((md == NULL) || (e == NULL))
-    return (-EINVAL);
-
-  pthread_mutex_lock (&md->lock);
-
-  prev = NULL;
-  this = md->head;
-  while (this != NULL)
-  {
-    if (strcasecmp (e->key, this->key) == 0)
-      break;
-
-    prev = this;
-    this = this->next;
-  }
-
-  if (this == NULL)
-  {
-    /* This key does not exist yet. */
-    if (md->head == NULL)
-      md->head = e;
-    else
-    {
-      assert (prev != NULL);
-      prev->next = e;
-    }
-
-    e->next = NULL;
-  }
-  else /* (this != NULL) */
-  {
-    if (prev == NULL)
-      md->head = e;
-    else
-      prev->next = e;
-
-    e->next = this->next;
-  }
-
-  pthread_mutex_unlock (&md->lock);
-
-  if (this != NULL)
-  {
-    this->next = NULL;
-    md_entry_free (this);
-  }
-
-  return (0);
-} /* }}} int md_entry_insert */
-
-/* XXX: The lock on md must be held while calling this function! */
-static meta_entry_t *md_entry_lookup (meta_data_t *md, /* {{{ */
-    const char *key)
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL))
-    return (NULL);
-
-  for (e = md->head; e != NULL; e = e->next)
-    if (strcasecmp (key, e->key) == 0)
-      break;
-
-  return (e);
-} /* }}} meta_entry_t *md_entry_lookup */
-
-/*
- * Public functions
- */
-meta_data_t *meta_data_create (void) /* {{{ */
-{
-  meta_data_t *md;
-
-  md = (meta_data_t *) malloc (sizeof (*md));
-  if (md == NULL)
-  {
-    ERROR ("meta_data_create: malloc failed.");
-    return (NULL);
-  }
-  memset (md, 0, sizeof (*md));
-
-  md->head = NULL;
-  pthread_mutex_init (&md->lock, /* attr = */ NULL);
-
-  return (md);
-} /* }}} meta_data_t *meta_data_create */
-
-meta_data_t *meta_data_clone (meta_data_t *orig) /* {{{ */
-{
-  meta_data_t *copy;
-
-  if (orig == NULL)
-    return (NULL);
-
-  copy = meta_data_create ();
-  if (copy == NULL)
-    return (NULL);
-
-  pthread_mutex_lock (&orig->lock);
-  copy->head = md_entry_clone (orig->head);
-  pthread_mutex_unlock (&orig->lock);
-
-  return (copy);
-} /* }}} meta_data_t *meta_data_clone */
-
-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);
-} /* }}} void meta_data_destroy */
-
-int meta_data_exists (meta_data_t *md, const char *key) /* {{{ */
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL))
-    return (-EINVAL);
-
-  pthread_mutex_lock (&md->lock);
-
-  for (e = md->head; e != NULL; e = e->next)
-  {
-    if (strcasecmp (key, e->key) == 0)
-    {
-      pthread_mutex_unlock (&md->lock);
-      return (1);
-    }
-  }
-
-  pthread_mutex_unlock (&md->lock);
-  return (0);
-} /* }}} int meta_data_exists */
-
-int meta_data_type (meta_data_t *md, const char *key) /* {{{ */
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL))
-    return -EINVAL;
-
-  pthread_mutex_lock (&md->lock);
-
-  for (e = md->head; e != NULL; e = e->next)
-  {
-    if (strcasecmp (key, e->key) == 0)
-    {
-      pthread_mutex_unlock (&md->lock);
-      return e->type;
-    }
-  }
-
-  pthread_mutex_unlock (&md->lock);
-  return 0;
-} /* }}} int meta_data_type */
-
-int meta_data_toc (meta_data_t *md, char ***toc) /* {{{ */
-{
-  int i = 0, count = 0;
-  meta_entry_t *e;
-
-  if ((md == NULL) || (toc == NULL))
-    return -EINVAL;
-
-  pthread_mutex_lock (&md->lock);
-
-  for (e = md->head; e != NULL; e = e->next)
-    ++count;    
-
-  *toc = malloc(count * sizeof(**toc));
-  for (e = md->head; e != NULL; e = e->next)
-    (*toc)[i++] = strdup(e->key);
-  
-  pthread_mutex_unlock (&md->lock);
-  return count;
-} /* }}} int meta_data_toc */
-
-int meta_data_delete (meta_data_t *md, const char *key) /* {{{ */
-{
-  meta_entry_t *this;
-  meta_entry_t *prev;
-
-  if ((md == NULL) || (key == NULL))
-    return (-EINVAL);
-
-  pthread_mutex_lock (&md->lock);
-
-  prev = NULL;
-  this = md->head;
-  while (this != NULL)
-  {
-    if (strcasecmp (key, this->key) == 0)
-      break;
-
-    prev = this;
-    this = this->next;
-  }
-
-  if (this == NULL)
-  {
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  if (prev == NULL)
-    md->head = this->next;
-  else
-    prev->next = this->next;
-
-  pthread_mutex_unlock (&md->lock);
-
-  this->next = NULL;
-  md_entry_free (this);
-
-  return (0);
-} /* }}} int meta_data_delete */
-
-/*
- * Add functions
- */
-int meta_data_add_string (meta_data_t *md, /* {{{ */
-    const char *key, const char *value)
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL) || (value == NULL))
-    return (-EINVAL);
-
-  e = md_entry_alloc (key);
-  if (e == NULL)
-    return (-ENOMEM);
-
-  e->value.mv_string = md_strdup (value);
-  if (e->value.mv_string == NULL)
-  {
-    ERROR ("meta_data_add_string: md_strdup failed.");
-    md_entry_free (e);
-    return (-ENOMEM);
-  }
-  e->type = MD_TYPE_STRING;
-
-  return (md_entry_insert (md, e));
-} /* }}} int meta_data_add_string */
-
-int meta_data_add_signed_int (meta_data_t *md, /* {{{ */
-    const char *key, int64_t value)
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL))
-    return (-EINVAL);
-
-  e = md_entry_alloc (key);
-  if (e == NULL)
-    return (-ENOMEM);
-
-  e->value.mv_signed_int = value;
-  e->type = MD_TYPE_SIGNED_INT;
-
-  return (md_entry_insert (md, e));
-} /* }}} int meta_data_add_signed_int */
-
-int meta_data_add_unsigned_int (meta_data_t *md, /* {{{ */
-    const char *key, uint64_t value)
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL))
-    return (-EINVAL);
-
-  e = md_entry_alloc (key);
-  if (e == NULL)
-    return (-ENOMEM);
-
-  e->value.mv_unsigned_int = value;
-  e->type = MD_TYPE_UNSIGNED_INT;
-
-  return (md_entry_insert (md, e));
-} /* }}} int meta_data_add_unsigned_int */
-
-int meta_data_add_double (meta_data_t *md, /* {{{ */
-    const char *key, double value)
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL))
-    return (-EINVAL);
-
-  e = md_entry_alloc (key);
-  if (e == NULL)
-    return (-ENOMEM);
-
-  e->value.mv_double = value;
-  e->type = MD_TYPE_DOUBLE;
-
-  return (md_entry_insert (md, e));
-} /* }}} int meta_data_add_double */
-
-int meta_data_add_boolean (meta_data_t *md, /* {{{ */
-    const char *key, _Bool value)
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL))
-    return (-EINVAL);
-
-  e = md_entry_alloc (key);
-  if (e == NULL)
-    return (-ENOMEM);
-
-  e->value.mv_boolean = value;
-  e->type = MD_TYPE_BOOLEAN;
-
-  return (md_entry_insert (md, e));
-} /* }}} int meta_data_add_boolean */
-
-/*
- * Get functions
- */
-int meta_data_get_string (meta_data_t *md, /* {{{ */
-    const char *key, char **value)
-{
-  meta_entry_t *e;
-  char *temp;
-
-  if ((md == NULL) || (key == NULL) || (value == NULL))
-    return (-EINVAL);
-
-  pthread_mutex_lock (&md->lock);
-
-  e = md_entry_lookup (md, key);
-  if (e == NULL)
-  {
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  if (e->type != MD_TYPE_STRING)
-  {
-    ERROR ("meta_data_get_signed_int: Type mismatch for key `%s'", e->key);
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  temp = md_strdup (e->value.mv_string);
-  if (temp == NULL)
-  {
-    pthread_mutex_unlock (&md->lock);
-    ERROR ("meta_data_get_string: md_strdup failed.");
-    return (-ENOMEM);
-  }
-  pthread_mutex_unlock (&md->lock);
-
-  *value = temp;
-
-  return (0);
-} /* }}} int meta_data_get_string */
-
-int meta_data_get_signed_int (meta_data_t *md, /* {{{ */
-    const char *key, int64_t *value)
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL) || (value == NULL))
-    return (-EINVAL);
-
-  pthread_mutex_lock (&md->lock);
-
-  e = md_entry_lookup (md, key);
-  if (e == NULL)
-  {
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  if (e->type != MD_TYPE_SIGNED_INT)
-  {
-    ERROR ("meta_data_get_signed_int: Type mismatch for key `%s'", e->key);
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  *value = e->value.mv_signed_int;
-
-  pthread_mutex_unlock (&md->lock);
-  return (0);
-} /* }}} int meta_data_get_signed_int */
-
-int meta_data_get_unsigned_int (meta_data_t *md, /* {{{ */
-    const char *key, uint64_t *value)
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL) || (value == NULL))
-    return (-EINVAL);
-
-  pthread_mutex_lock (&md->lock);
-
-  e = md_entry_lookup (md, key);
-  if (e == NULL)
-  {
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  if (e->type != MD_TYPE_UNSIGNED_INT)
-  {
-    ERROR ("meta_data_get_unsigned_int: Type mismatch for key `%s'", e->key);
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  *value = e->value.mv_unsigned_int;
-
-  pthread_mutex_unlock (&md->lock);
-  return (0);
-} /* }}} int meta_data_get_unsigned_int */
-
-int meta_data_get_double (meta_data_t *md, /* {{{ */
-    const char *key, double *value)
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL) || (value == NULL))
-    return (-EINVAL);
-
-  pthread_mutex_lock (&md->lock);
-
-  e = md_entry_lookup (md, key);
-  if (e == NULL)
-  {
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  if (e->type != MD_TYPE_DOUBLE)
-  {
-    ERROR ("meta_data_get_double: Type mismatch for key `%s'", e->key);
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  *value = e->value.mv_double;
-
-  pthread_mutex_unlock (&md->lock);
-  return (0);
-} /* }}} int meta_data_get_double */
-
-int meta_data_get_boolean (meta_data_t *md, /* {{{ */
-    const char *key, _Bool *value)
-{
-  meta_entry_t *e;
-
-  if ((md == NULL) || (key == NULL) || (value == NULL))
-    return (-EINVAL);
-
-  pthread_mutex_lock (&md->lock);
-
-  e = md_entry_lookup (md, key);
-  if (e == NULL)
-  {
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  if (e->type != MD_TYPE_BOOLEAN)
-  {
-    ERROR ("meta_data_get_boolean: Type mismatch for key `%s'", e->key);
-    pthread_mutex_unlock (&md->lock);
-    return (-ENOENT);
-  }
-
-  *value = e->value.mv_boolean;
-
-  pthread_mutex_unlock (&md->lock);
-  return (0);
-} /* }}} int meta_data_get_boolean */
-
-/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/meta_data.h b/src/meta_data.h
deleted file mode 100644 (file)
index fa48df3..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * collectd - src/meta_data.h
- * Copyright (C) 2008-2011  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 META_DATA_H
-#define META_DATA_H
-
-#include "collectd.h"
-
-/*
- * Defines
- */
-#define MD_TYPE_STRING       1
-#define MD_TYPE_SIGNED_INT   2
-#define MD_TYPE_UNSIGNED_INT 3
-#define MD_TYPE_DOUBLE       4
-#define MD_TYPE_BOOLEAN      5
-
-struct meta_data_s;
-typedef struct meta_data_s meta_data_t;
-
-meta_data_t *meta_data_create (void);
-meta_data_t *meta_data_clone (meta_data_t *orig);
-void meta_data_destroy (meta_data_t *md);
-
-int meta_data_exists (meta_data_t *md, const char *key);
-int meta_data_type (meta_data_t *md, const char *key);
-int meta_data_toc (meta_data_t *md, char ***toc);
-int meta_data_delete (meta_data_t *md, const char *key);
-
-int meta_data_add_string (meta_data_t *md,
-    const char *key,
-    const char *value);
-int meta_data_add_signed_int (meta_data_t *md,
-    const char *key,
-    int64_t value);
-int meta_data_add_unsigned_int (meta_data_t *md,
-    const char *key,
-    uint64_t value);
-int meta_data_add_double (meta_data_t *md,
-    const char *key,
-    double value);
-int meta_data_add_boolean (meta_data_t *md,
-    const char *key,
-    _Bool value);
-
-int meta_data_get_string (meta_data_t *md,
-    const char *key,
-    char **value);
-int meta_data_get_signed_int (meta_data_t *md,
-    const char *key,
-    int64_t *value);
-int meta_data_get_unsigned_int (meta_data_t *md,
-    const char *key,
-    uint64_t *value);
-int meta_data_get_double (meta_data_t *md,
-    const char *key,
-    double *value);
-int meta_data_get_boolean (meta_data_t *md,
-    const char *key,
-    _Bool *value);
-
-#endif /* META_DATA_H */
-/* vim: set sw=2 sts=2 et : */
index 7d5eddd..f5ae1ad 100644 (file)
@@ -688,7 +688,7 @@ static int mysql_read (user_data_t *ud)
                        else if (strcmp (key, "Innodb_buffer_pool_pages_dirty") == 0)
                                gauge_submit ("mysql_bpool_pages", "dirty", val, db);
                        else if (strcmp (key, "Innodb_buffer_pool_pages_flushed") == 0)
-                               counter_submit ("mysql_bpool_counters", "flushed", val, db);
+                               counter_submit ("mysql_bpool_pages", "flushed", val, db);
                        else if (strcmp (key, "Innodb_buffer_pool_pages_free") == 0)
                                gauge_submit ("mysql_bpool_pages", "free", val, db);
                        else if (strcmp (key, "Innodb_buffer_pool_pages_misc") == 0)
@@ -716,7 +716,7 @@ static int mysql_read (user_data_t *ud)
                        else if (strcmp (key, "Innodb_data_reads") == 0)
                                counter_submit ("mysql_innodb_data", "reads", val, db);
                        else if (strcmp (key, "Innodb_data_writes") == 0)
-                               counter_submit ("mysql_bpool_counters", "writes", val, db);
+                               counter_submit ("mysql_innodb_data", "writes", val, db);
                        else if (strcmp (key, "Innodb_data_written") == 0)
                                counter_submit ("mysql_innodb_data", "written", val, db);
 
index b191983..551bd5c 100644 (file)
@@ -22,6 +22,7 @@
  *   Aman Gupta <aman at tmm1.net>
  **/
 
+#define _DEFAULT_SOURCE
 #define _BSD_SOURCE /* For struct ip_mreq */
 
 #include "collectd.h"
@@ -76,7 +77,9 @@
 /* Re enable deprecation warnings */
 #  pragma GCC diagnostic warning "-Wdeprecated-declarations"
 # endif
+# if GCRYPT_VERSION_NUMBER < 0x010600
 GCRY_THREAD_OPTION_PTHREAD_IMPL;
+# endif
 #endif
 
 #ifndef IPV6_ADD_MEMBERSHIP
@@ -117,6 +120,8 @@ struct sockent_client
        gcry_cipher_hd_t cypher;
        unsigned char password_hash[32];
 #endif
+       cdtime_t next_resolve_reconnect;
+       cdtime_t resolve_interval;
 };
 
 struct sockent_server
@@ -395,7 +400,7 @@ static _Bool check_send_notify_okay (const notification_t *n) /* {{{ */
   {
     c_complain_once (LOG_ERR, &complain_forwarding,
         "network plugin: A notification has been received via the network "
-        "forwarding if enabled. Forwarding of notifications is currently "
+        "and forwarding is enabled. Forwarding of notifications is currently "
         "not supported, because there is not loop-deteciton available. "
         "Please contact the collectd mailing list if you need this "
         "feature.");
@@ -508,7 +513,9 @@ static void network_init_gcrypt (void) /* {{{ */
   * above doesn't count, as it doesn't implicitly initalize Libgcrypt.
   *
   * tl;dr: keep all these gry_* statements in this exact order please. */
+# if GCRYPT_VERSION_NUMBER < 0x010600
   gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+# endif
   gcry_check_version (NULL);
   gcry_control (GCRYCTL_INIT_SECMEM, 32768);
   gcry_control (GCRYCTL_INITIALIZATION_FINISHED);
@@ -917,15 +924,19 @@ static int parse_part_number (void **ret_buffer, size_t *ret_buffer_len,
 } /* int parse_part_number */
 
 static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
-               char *output, int output_len)
+               char *output, size_t const output_len)
 {
        char *buffer = *ret_buffer;
        size_t buffer_len = *ret_buffer_len;
 
        uint16_t tmp16;
-       size_t header_size = 2 * sizeof (uint16_t);
+       size_t const header_size = 2 * sizeof (uint16_t);
 
        uint16_t pkg_length;
+       size_t payload_size;
+
+       if (output_len <= 0)
+               return (EINVAL);
 
        if (buffer_len < header_size)
        {
@@ -944,6 +955,7 @@ static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
        memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
        buffer += sizeof (tmp16);
        pkg_length = ntohs (tmp16);
+       payload_size = ((size_t) pkg_length) - header_size;
 
        /* Check that packet fits in the input buffer */
        if (pkg_length > buffer_len)
@@ -969,22 +981,24 @@ static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
        /* Check that the package data fits into the output buffer.
         * The previous if-statement ensures that:
         * `pkg_length > header_size' */
-       if ((output_len < 0)
-                       || ((size_t) output_len < ((size_t) pkg_length - header_size)))
+       if (output_len < payload_size)
        {
                WARNING ("network plugin: parse_part_string: "
-                               "Output buffer too small.");
+                               "Buffer too small: "
+                               "Output buffer holds %zu bytes, "
+                               "which is too small to hold the received "
+                               "%zu byte string.",
+                               output_len, payload_size);
                return (-1);
        }
 
        /* All sanity checks successfull, let's copy the data over */
-       output_len = pkg_length - header_size;
-       memcpy ((void *) output, (void *) buffer, output_len);
-       buffer += output_len;
+       memcpy ((void *) output, (void *) buffer, payload_size);
+       buffer += payload_size;
 
        /* For some very weird reason '\0' doesn't do the trick on SPARC in
         * this statement. */
-       if (output[output_len - 1] != 0)
+       if (output[payload_size - 1] != 0)
        {
                WARNING ("network plugin: parse_part_string: "
                                "Received string does not end "
@@ -1992,14 +2006,19 @@ static int network_bind_socket (int fd, const struct addrinfo *ai, const int int
 
 /* Initialize a sockent structure. `type' must be either `SOCKENT_TYPE_CLIENT'
  * or `SOCKENT_TYPE_SERVER' */
-static int sockent_init (sockent_t *se, int type) /* {{{ */
+static sockent_t *sockent_create (int type) /* {{{ */
 {
-       if (se == NULL)
-               return (-1);
+       sockent_t *se;
+
+       if ((type != SOCKENT_TYPE_CLIENT) && (type != SOCKENT_TYPE_SERVER))
+               return (NULL);
 
+       se = malloc (sizeof (*se));
+       if (se == NULL)
+               return (NULL);
        memset (se, 0, sizeof (*se));
 
-       se->type = SOCKENT_TYPE_CLIENT;
+       se->type = type;
        se->node = NULL;
        se->service = NULL;
        se->interface = 0;
@@ -2007,7 +2026,6 @@ static int sockent_init (sockent_t *se, int type) /* {{{ */
 
        if (type == SOCKENT_TYPE_SERVER)
        {
-               se->type = SOCKENT_TYPE_SERVER;
                se->data.server.fd = NULL;
 #if HAVE_LIBGCRYPT
                se->data.server.security_level = SECURITY_LEVEL_NONE;
@@ -2020,6 +2038,8 @@ static int sockent_init (sockent_t *se, int type) /* {{{ */
        {
                se->data.client.fd = -1;
                se->data.client.addr = NULL;
+               se->data.client.resolve_interval = 0;
+               se->data.client.next_resolve_reconnect = 0;
 #if HAVE_LIBGCRYPT
                se->data.client.security_level = SECURITY_LEVEL_NONE;
                se->data.client.username = NULL;
@@ -2028,23 +2048,11 @@ static int sockent_init (sockent_t *se, int type) /* {{{ */
 #endif
        }
 
-       return (0);
-} /* }}} int sockent_init */
+       return (se);
+} /* }}} sockent_t *sockent_create */
 
-/* Open the file descriptors for a initialized sockent structure. */
-static int sockent_open (sockent_t *se) /* {{{ */
+static int sockent_init_crypto (sockent_t *se) /* {{{ */
 {
-       struct addrinfo  ai_hints;
-       struct addrinfo *ai_list, *ai_ptr;
-       int              ai_return;
-
-        const char *node;
-        const char *service;
-
-       if (se == NULL)
-               return (-1);
-
-       /* Set up the security structures. */
 #if HAVE_LIBGCRYPT /* {{{ */
        if (se->type == SOCKENT_TYPE_CLIENT)
        {
@@ -2095,13 +2103,150 @@ static int sockent_open (sockent_t *se) /* {{{ */
        }
 #endif /* }}} HAVE_LIBGCRYPT */
 
+       return (0);
+} /* }}} int sockent_init_crypto */
+
+static int sockent_client_disconnect (sockent_t *se) /* {{{ */
+{
+       struct sockent_client *client;
+
+       if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
+               return (EINVAL);
+
+       client = &se->data.client;
+       if (client->fd >= 0) /* connected */
+       {
+               close (client->fd);
+               client->fd = -1;
+       }
+
+       sfree (client->addr);
+       client->addrlen = 0;
+
+       return (0);
+} /* }}} int sockent_client_disconnect */
+
+static int sockent_client_connect (sockent_t *se) /* {{{ */
+{
+       static c_complain_t complaint = C_COMPLAIN_INIT_STATIC;
+
+       struct sockent_client *client;
+       struct addrinfo  ai_hints;
+       struct addrinfo *ai_list = NULL, *ai_ptr;
+       int status;
+       _Bool reconnect = 0;
+       cdtime_t now;
+
+       if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
+               return (EINVAL);
+
+       client = &se->data.client;
+
+       now = cdtime ();
+       if (client->resolve_interval != 0 && client->next_resolve_reconnect < now) {
+               DEBUG("network plugin: Reconnecting socket, resolve_interval = %lf, next_resolve_reconnect = %lf",
+                       CDTIME_T_TO_DOUBLE(client->resolve_interval), CDTIME_T_TO_DOUBLE(client->next_resolve_reconnect));
+               reconnect = 1;
+       }
+
+       if (client->fd >= 0 && !reconnect) /* already connected and not stale*/
+               return (0);
+
+       memset (&ai_hints, 0, sizeof (ai_hints));
+#ifdef AI_ADDRCONFIG
+       ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+       ai_hints.ai_family   = AF_UNSPEC;
+       ai_hints.ai_socktype = SOCK_DGRAM;
+       ai_hints.ai_protocol = IPPROTO_UDP;
+
+       status = getaddrinfo (se->node,
+                       (se->service != NULL) ? se->service : NET_DEFAULT_PORT,
+                       &ai_hints, &ai_list);
+       if (status != 0)
+       {
+               c_complain (LOG_ERR, &complaint,
+                               "network plugin: getaddrinfo (%s, %s) failed: %s",
+                               (se->node == NULL) ? "(null)" : se->node,
+                               (se->service == NULL) ? "(null)" : se->service,
+                               gai_strerror (status));
+               return (-1);
+       }
+       else
+       {
+               c_release (LOG_NOTICE, &complaint,
+                               "network plugin: Successfully resolved \"%s\".",
+                               se->node);
+       }
+
+       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+       {
+               if (client->fd >= 0) /* when we reconnect */
+                       sockent_client_disconnect(se);
+
+               client->fd = socket (ai_ptr->ai_family,
+                               ai_ptr->ai_socktype,
+                               ai_ptr->ai_protocol);
+               if (client->fd < 0)
+               {
+                       char errbuf[1024];
+                       ERROR ("network plugin: socket(2) failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
+                       continue;
+               }
+
+               client->addr = malloc (sizeof (*client->addr));
+               if (client->addr == NULL)
+               {
+                       ERROR ("network plugin: malloc failed.");
+                       close (client->fd);
+                       client->fd = -1;
+                       continue;
+               }
+
+               memset (client->addr, 0, sizeof (*client->addr));
+               assert (sizeof (*client->addr) >= ai_ptr->ai_addrlen);
+               memcpy (client->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+               client->addrlen = ai_ptr->ai_addrlen;
+
+               network_set_ttl (se, ai_ptr);
+               network_set_interface (se, ai_ptr);
+
+               /* We don't open more than one write-socket per
+                * node/service pair.. */
+               break;
+       }
+
+       freeaddrinfo (ai_list);
+       if (client->fd < 0)
+               return (-1);
+
+       if (client->resolve_interval > 0)
+               client->next_resolve_reconnect = now + client->resolve_interval;
+       return (0);
+} /* }}} int sockent_client_connect */
+
+/* Open the file descriptors for a initialized sockent structure. */
+static int sockent_server_listen (sockent_t *se) /* {{{ */
+{
+       struct addrinfo  ai_hints;
+       struct addrinfo *ai_list, *ai_ptr;
+       int              status;
+
+        const char *node;
+        const char *service;
+
+       if (se == NULL)
+               return (-1);
+
         node = se->node;
         service = se->service;
 
         if (service == NULL)
           service = NET_DEFAULT_PORT;
 
-        DEBUG ("network plugin: sockent_open: node = %s; service = %s;",
+        DEBUG ("network plugin: sockent_server_listen: node = %s; service = %s;",
             node, service);
 
        memset (&ai_hints, 0, sizeof (ai_hints));
@@ -2116,109 +2261,59 @@ static int sockent_open (sockent_t *se) /* {{{ */
        ai_hints.ai_socktype = SOCK_DGRAM;
        ai_hints.ai_protocol = IPPROTO_UDP;
 
-       ai_return = getaddrinfo (node, service, &ai_hints, &ai_list);
-       if (ai_return != 0)
+       status = getaddrinfo (node, service, &ai_hints, &ai_list);
+       if (status != 0)
        {
                ERROR ("network plugin: getaddrinfo (%s, %s) failed: %s",
                                (se->node == NULL) ? "(null)" : se->node,
                                (se->service == NULL) ? "(null)" : se->service,
-                               gai_strerror (ai_return));
+                               gai_strerror (status));
                return (-1);
        }
 
        for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
        {
-               int status;
+               int *tmp;
 
-               if (se->type == SOCKENT_TYPE_SERVER) /* {{{ */
+               tmp = realloc (se->data.server.fd,
+                               sizeof (*tmp) * (se->data.server.fd_num + 1));
+               if (tmp == NULL)
                {
-                       int *tmp;
-
-                       tmp = realloc (se->data.server.fd,
-                                       sizeof (*tmp) * (se->data.server.fd_num + 1));
-                       if (tmp == NULL)
-                       {
-                               ERROR ("network plugin: realloc failed.");
-                               continue;
-                       }
-                       se->data.server.fd = tmp;
-                       tmp = se->data.server.fd + se->data.server.fd_num;
-
-                       *tmp = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
-                                       ai_ptr->ai_protocol);
-                       if (*tmp < 0)
-                       {
-                               char errbuf[1024];
-                               ERROR ("network plugin: socket(2) failed: %s",
-                                               sstrerror (errno, errbuf,
-                                                       sizeof (errbuf)));
-                               continue;
-                       }
-
-                       status = network_bind_socket (*tmp, ai_ptr, se->interface);
-                       if (status != 0)
-                       {
-                               close (*tmp);
-                               *tmp = -1;
-                               continue;
-                       }
-
-                       se->data.server.fd_num++;
+                       ERROR ("network plugin: realloc failed.");
                        continue;
-               } /* }}} if (se->type == SOCKENT_TYPE_SERVER) */
-               else /* if (se->type == SOCKENT_TYPE_CLIENT) {{{ */
-               {
-                       se->data.client.fd = socket (ai_ptr->ai_family,
-                                       ai_ptr->ai_socktype,
-                                       ai_ptr->ai_protocol);
-                       if (se->data.client.fd < 0)
-                       {
-                               char errbuf[1024];
-                               ERROR ("network plugin: socket(2) failed: %s",
-                                               sstrerror (errno, errbuf,
-                                                       sizeof (errbuf)));
-                               continue;
-                       }
-
-                       se->data.client.addr = malloc (sizeof (*se->data.client.addr));
-                       if (se->data.client.addr == NULL)
-                       {
-                               ERROR ("network plugin: malloc failed.");
-                               close (se->data.client.fd);
-                               se->data.client.fd = -1;
-                               continue;
-                       }
+               }
+               se->data.server.fd = tmp;
+               tmp = se->data.server.fd + se->data.server.fd_num;
 
-                       memset (se->data.client.addr, 0, sizeof (*se->data.client.addr));
-                       assert (sizeof (*se->data.client.addr) >= ai_ptr->ai_addrlen);
-                       memcpy (se->data.client.addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
-                       se->data.client.addrlen = ai_ptr->ai_addrlen;
+               *tmp = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
+                               ai_ptr->ai_protocol);
+               if (*tmp < 0)
+               {
+                       char errbuf[1024];
+                       ERROR ("network plugin: socket(2) failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
+                       continue;
+               }
 
-                       network_set_ttl (se, ai_ptr);
-                       network_set_interface (se, ai_ptr);
+               status = network_bind_socket (*tmp, ai_ptr, se->interface);
+               if (status != 0)
+               {
+                       close (*tmp);
+                       *tmp = -1;
+                       continue;
+               }
 
-                       /* We don't open more than one write-socket per
-                        * node/service pair.. */
-                       break;
-               } /* }}} if (se->type == SOCKENT_TYPE_CLIENT) */
+               se->data.server.fd_num++;
+               continue;
        } /* for (ai_list) */
 
        freeaddrinfo (ai_list);
 
-       /* Check if all went well. */
-       if (se->type == SOCKENT_TYPE_SERVER)
-       {
-               if (se->data.server.fd_num <= 0)
-                       return (-1);
-       }
-       else /* if (se->type == SOCKENT_TYPE_CLIENT) */
-       {
-               if (se->data.client.fd < 0)
-                       return (-1);
-       }
-
+       if (se->data.server.fd_num <= 0)
+               return (-1);
        return (0);
-} /* }}} int sockent_open */
+} /* }}} int sockent_server_listen */
 
 /* Add a sockent to the global list of sockets */
 static int sockent_add (sockent_t *se) /* {{{ */
@@ -2486,26 +2581,32 @@ static void network_init_buffer (void)
        memset (&send_buffer_vl, 0, sizeof (send_buffer_vl));
 } /* int network_init_buffer */
 
-static void networt_send_buffer_plain (const sockent_t *se, /* {{{ */
+static void networt_send_buffer_plain (sockent_t *se, /* {{{ */
                const char *buffer, size_t buffer_size)
 {
        int status;
 
        while (42)
        {
+               status = sockent_client_connect (se);
+               if (status != 0)
+                       return;
+
                status = sendto (se->data.client.fd, buffer, buffer_size,
-                    /* flags = */ 0,
-                    (struct sockaddr *) se->data.client.addr,
-                    se->data.client.addrlen);
-                if (status < 0)
+                               /* flags = */ 0,
+                               (struct sockaddr *) se->data.client.addr,
+                               se->data.client.addrlen);
+               if (status < 0)
                {
                        char errbuf[1024];
-                       if (errno == EINTR)
+
+                       if ((errno == EINTR) || (errno == EAGAIN))
                                continue;
-                       ERROR ("network plugin: sendto failed: %s",
-                                       sstrerror (errno, errbuf,
-                                               sizeof (errbuf)));
-                       break;
+
+                       ERROR ("network plugin: sendto failed: %s. Closing sending socket.",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       sockent_client_disconnect (se);
+                       return;
                }
 
                break;
@@ -2518,7 +2619,7 @@ static void networt_send_buffer_plain (const sockent_t *se, /* {{{ */
   buffer_offset += (s); \
 } while (0)
 
-static void networt_send_buffer_signed (const sockent_t *se, /* {{{ */
+static void networt_send_buffer_signed (sockent_t *se, /* {{{ */
                const char *in_buffer, size_t in_buffer_size)
 {
   part_signature_sha256_t ps;
@@ -2906,7 +3007,7 @@ static int network_config_set_ttl (const oconfig_item_t *ci) /* {{{ */
     network_config_ttl = tmp;
   else {
     WARNING ("network plugin: The `TimeToLive' must be between 1 and 255.");
-    return (-1);    
+    return (-1);
   }
 
   return (0);
@@ -3018,13 +3119,12 @@ static int network_config_add_listen (const oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
-  se = malloc (sizeof (*se));
+  se = sockent_create (SOCKENT_TYPE_SERVER);
   if (se == NULL)
   {
-    ERROR ("network plugin: malloc failed.");
+    ERROR ("network plugin: sockent_create failed.");
     return (-1);
   }
-  sockent_init (se, SOCKENT_TYPE_SERVER);
 
   se->node = strdup (ci->values[0].value.string);
   if (ci->values_num >= 2)
@@ -3064,10 +3164,18 @@ static int network_config_add_listen (const oconfig_item_t *ci) /* {{{ */
   }
 #endif /* HAVE_LIBGCRYPT */
 
-  status = sockent_open (se);
+  status = sockent_init_crypto (se);
+  if (status != 0)
+  {
+    ERROR ("network plugin: network_config_add_listen: sockent_init_crypto() failed.");
+    sockent_destroy (se);
+    return (-1);
+  }
+
+  status = sockent_server_listen (se);
   if (status != 0)
   {
-    ERROR ("network plugin: network_config_add_listen: sockent_open failed.");
+    ERROR ("network plugin: network_config_add_server: sockent_server_listen failed.");
     sockent_destroy (se);
     return (-1);
   }
@@ -3098,13 +3206,12 @@ static int network_config_add_server (const oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
-  se = malloc (sizeof (*se));
+  se = sockent_create (SOCKENT_TYPE_CLIENT);
   if (se == NULL)
   {
-    ERROR ("network plugin: malloc failed.");
+    ERROR ("network plugin: sockent_create failed.");
     return (-1);
   }
-  sockent_init (se, SOCKENT_TYPE_CLIENT);
 
   se->node = strdup (ci->values[0].value.string);
   if (ci->values_num >= 2)
@@ -3127,6 +3234,8 @@ static int network_config_add_server (const oconfig_item_t *ci) /* {{{ */
     if (strcasecmp ("Interface", child->key) == 0)
       network_config_set_interface (child,
           &se->interface);
+               else if (strcasecmp ("ResolveInterval", child->key) == 0)
+                       cf_util_get_cdtime(child, &se->data.client.resolve_interval);
     else
     {
       WARNING ("network plugin: Option `%s' is not allowed here.",
@@ -3147,14 +3256,17 @@ static int network_config_add_server (const oconfig_item_t *ci) /* {{{ */
   }
 #endif /* HAVE_LIBGCRYPT */
 
-  status = sockent_open (se);
+  status = sockent_init_crypto (se);
   if (status != 0)
   {
-    ERROR ("network plugin: network_config_add_server: sockent_open failed.");
+    ERROR ("network plugin: network_config_add_server: sockent_init_crypto() failed.");
     sockent_destroy (se);
     return (-1);
   }
 
+  /* No call to sockent_client_connect() here -- it is called from
+   * networt_send_buffer_plain(). */
+
   status = sockent_add (se);
   if (status != 0)
   {
@@ -3281,6 +3393,8 @@ static int network_notification (const notification_t *n,
 
 static int network_shutdown (void)
 {
+       sockent_t *se;
+
        listen_loop++;
 
        /* Kill the listening thread */
@@ -3311,7 +3425,9 @@ static int network_shutdown (void)
 
        sfree (send_buffer);
 
-       /* TODO: Close `sending_sockets' */
+       for (se = sending_sockets; se != NULL; se = se->next)
+               sockent_client_disconnect (se);
+       sockent_destroy (sending_sockets);
 
        plugin_unregister_config ("network");
        plugin_unregister_init ("network");
index 6bed82c..15b12f9 100644 (file)
@@ -24,6 +24,7 @@
  *   Florian octo Forster <octo at collectd.org>
  **/
 
+#define _DEFAULT_SOURCE
 #define _BSD_SOURCE /* For NI_MAXHOST */
 
 #include "collectd.h"
@@ -486,7 +487,7 @@ static int ntpd_receive_response (int *res_items, int *res_size,
                poll_s.fd      = sd;
                poll_s.events  = POLLIN | POLLPRI;
                poll_s.revents = 0;
-               
+
                DEBUG ("Polling for %ims", timeout);
                status = poll (&poll_s, 1, timeout);
 
@@ -526,7 +527,7 @@ static int ntpd_receive_response (int *res_items, int *res_size,
 
                DEBUG ("recv'd %i bytes", status);
 
-               /* 
+               /*
                 * Do some sanity checks first
                 */
                if (status < RESP_HEADER_SIZE)
@@ -736,7 +737,7 @@ static int ntpd_send_request (int req_code, int req_items, int req_size, char *r
 
        req.err_nitems   = ERR_NITEMS (0, req_items);
        req.mbz_itemsize = MBZ_ITEMSIZE (req_size);
-       
+
        if (req_data != NULL)
                memcpy ((void *) req.data, (const void *) req_data, req_data_len);
 
index 6d89b37..0fd9405 100644 (file)
@@ -267,7 +267,7 @@ static int multi1_read (char *name, FILE *fh)
 {
        char buffer[1024];
        char *fields[10];
-       int  fields_num, read = 0, found_header = 0;
+       int  fields_num, found_header = 0;
        long long sum_users = 0;
 
        /* read the file until the "ROUTING TABLE" line is found (no more info after) */
@@ -314,17 +314,15 @@ static int multi1_read (char *name, FILE *fh)
                                                atoll (fields[3])); /* "Bytes Sent" */
                        }
                }
-
-               read = 1;
        }
 
+       if (ferror (fh))
+               return (0);
+
        if (collect_user_count)
-       {
                numusers_submit(name, name, sum_users);
-               read = 1;
-       }
 
-       return (read);
+       return (1);
 } /* int multi1_read */
 
 /* for reading status version 2 */
diff --git a/src/plugin.c b/src/plugin.c
deleted file mode 100644 (file)
index 30a1ff1..0000000
+++ /dev/null
@@ -1,2586 +0,0 @@
-/**
- * collectd - src/plugin.c
- * Copyright (C) 2005-2014  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>
- *   Sebastian Harl <sh at tokkee.org>
- **/
-
-#include "collectd.h"
-#include "common.h"
-#include "plugin.h"
-#include "configfile.h"
-#include "filter_chain.h"
-#include "utils_avltree.h"
-#include "utils_cache.h"
-#include "utils_complain.h"
-#include "utils_llist.h"
-#include "utils_heap.h"
-#include "utils_time.h"
-#include "utils_random.h"
-
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
-
-#include <ltdl.h>
-
-/*
- * Private structures
- */
-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;
-
-#define RF_SIMPLE  0
-#define RF_COMPLEX 1
-#define RF_REMOVE  65535
-struct read_func_s
-{
-       /* `read_func_t' "inherits" from `callback_func_t'.
-        * 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;
-       int rf_type;
-       cdtime_t rf_interval;
-       cdtime_t rf_effective_interval;
-       cdtime_t rf_next_read;
-};
-typedef struct read_func_s read_func_t;
-
-struct write_queue_s;
-typedef struct write_queue_s write_queue_t;
-struct write_queue_s
-{
-       value_list_t *vl;
-       plugin_ctx_t ctx;
-       write_queue_t *next;
-};
-
-/*
- * Private variables
- */
-static c_avl_tree_t *plugins_loaded = NULL;
-
-static llist_t *list_init;
-static llist_t *list_write;
-static llist_t *list_flush;
-static llist_t *list_missing;
-static llist_t *list_shutdown;
-static llist_t *list_log;
-static llist_t *list_notification;
-
-static fc_chain_t *pre_cache_chain = NULL;
-static fc_chain_t *post_cache_chain = NULL;
-
-static c_avl_tree_t *data_sets;
-
-static char *plugindir = NULL;
-
-static c_heap_t       *read_heap = NULL;
-static llist_t        *read_list;
-static int             read_loop = 1;
-static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t  read_cond = PTHREAD_COND_INITIALIZER;
-static pthread_t      *read_threads = NULL;
-static int             read_threads_num = 0;
-
-static write_queue_t  *write_queue_head;
-static write_queue_t  *write_queue_tail;
-static long            write_queue_length = 0;
-static _Bool           write_loop = 1;
-static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t  write_cond = PTHREAD_COND_INITIALIZER;
-static pthread_t      *write_threads = NULL;
-static size_t          write_threads_num = 0;
-
-static pthread_key_t   plugin_ctx_key;
-static _Bool           plugin_ctx_key_initialized = 0;
-
-static long            write_limit_high = 0;
-static long            write_limit_low = 0;
-
-/*
- * Static functions
- */
-static int plugin_dispatch_values_internal (value_list_t *vl);
-
-static const char *plugin_get_dir (void)
-{
-       if (plugindir == NULL)
-               return (PLUGINDIR);
-       else
-               return (plugindir);
-}
-
-static void destroy_callback (callback_func_t *cf) /* {{{ */
-{
-       if (cf == NULL)
-               return;
-
-       if ((cf->cf_udata.data != NULL) && (cf->cf_udata.free_func != NULL))
-       {
-               cf->cf_udata.free_func (cf->cf_udata.data);
-               cf->cf_udata.data = NULL;
-               cf->cf_udata.free_func = NULL;
-       }
-       sfree (cf);
-} /* }}} void destroy_callback */
-
-static void destroy_all_callbacks (llist_t **list) /* {{{ */
-{
-       llentry_t *le;
-
-       if (*list == NULL)
-               return;
-
-       le = llist_head (*list);
-       while (le != NULL)
-       {
-               llentry_t *le_next;
-
-               le_next = le->next;
-
-               sfree (le->key);
-               destroy_callback (le->value);
-               le->value = NULL;
-
-               le = le_next;
-       }
-
-       llist_destroy (*list);
-       *list = NULL;
-} /* }}} void destroy_all_callbacks */
-
-static void destroy_read_heap (void) /* {{{ */
-{
-       if (read_heap == NULL)
-               return;
-
-       while (42)
-       {
-               callback_func_t *cf;
-
-               cf = c_heap_get_root (read_heap);
-               if (cf == NULL)
-                       break;
-
-               destroy_callback (cf);
-       }
-
-       c_heap_destroy (read_heap);
-       read_heap = NULL;
-} /* }}} void destroy_read_heap */
-
-static int register_callback (llist_t **list, /* {{{ */
-               const char *name, callback_func_t *cf)
-{
-       llentry_t *le;
-       char *key;
-
-       if (*list == NULL)
-       {
-               *list = llist_create ();
-               if (*list == NULL)
-               {
-                       ERROR ("plugin: register_callback: "
-                                       "llist_create failed.");
-                       destroy_callback (cf);
-                       return (-1);
-               }
-       }
-
-       key = strdup (name);
-       if (key == NULL)
-       {
-               ERROR ("plugin: register_callback: strdup failed.");
-               destroy_callback (cf);
-               return (-1);
-       }
-
-       le = llist_search (*list, name);
-       if (le == NULL)
-       {
-               le = llentry_create (key, cf);
-               if (le == NULL)
-               {
-                       ERROR ("plugin: register_callback: "
-                                       "llentry_create failed.");
-                       free (key);
-                       destroy_callback (cf);
-                       return (-1);
-               }
-
-               llist_append (*list, le);
-       }
-       else
-       {
-               callback_func_t *old_cf;
-
-               old_cf = le->value;
-               le->value = cf;
-
-               WARNING ("plugin: register_callback: "
-                               "a callback named `%s' already exists - "
-                               "overwriting the old entry!", name);
-
-               destroy_callback (old_cf);
-               sfree (key);
-       }
-
-       return (0);
-} /* }}} int register_callback */
-
-static int create_register_callback (llist_t **list, /* {{{ */
-               const char *name, void *callback, user_data_t *ud)
-{
-       callback_func_t *cf;
-
-       cf = (callback_func_t *) malloc (sizeof (*cf));
-       if (cf == NULL)
-       {
-               ERROR ("plugin: create_register_callback: malloc failed.");
-               return (-1);
-       }
-       memset (cf, 0, sizeof (*cf));
-
-       cf->cf_callback = callback;
-       if (ud == NULL)
-       {
-               cf->cf_udata.data = NULL;
-               cf->cf_udata.free_func = NULL;
-       }
-       else
-       {
-               cf->cf_udata = *ud;
-       }
-
-       cf->cf_ctx = plugin_get_ctx ();
-
-       return (register_callback (list, name, cf));
-} /* }}} int create_register_callback */
-
-static int plugin_unregister (llist_t *list, const char *name) /* {{{ */
-{
-       llentry_t *e;
-
-       if (list == NULL)
-               return (-1);
-
-       e = llist_search (list, name);
-       if (e == NULL)
-               return (-1);
-
-       llist_remove (list, e);
-
-       sfree (e->key);
-       destroy_callback (e->value);
-
-       llentry_destroy (e);
-
-       return (0);
-} /* }}} int plugin_unregister */
-
-/*
- * (Try to) load the shared object `file'. Won't complain if it isn't a shared
- * object, but it will bitch about a shared object not having a
- * ``module_register'' symbol..
- */
-static int plugin_load_file (char *file, uint32_t flags)
-{
-       lt_dlhandle dlh;
-       void (*reg_handle) (void);
-
-       lt_dlinit ();
-       lt_dlerror (); /* clear errors */
-
-#if LIBTOOL_VERSION == 2
-       if (flags & PLUGIN_FLAGS_GLOBAL) {
-               lt_dladvise advise;
-               lt_dladvise_init(&advise);
-               lt_dladvise_global(&advise);
-               dlh = lt_dlopenadvise(file, advise);
-               lt_dladvise_destroy(&advise);
-       } else {
-               dlh = lt_dlopen (file);
-       }
-#else /* if LIBTOOL_VERSION == 1 */
-       if (flags & PLUGIN_FLAGS_GLOBAL)
-               WARNING ("plugin_load_file: The global flag is not supported, "
-                               "libtool 2 is required for this.");
-       dlh = lt_dlopen (file);
-#endif
-
-       if (dlh == NULL)
-       {
-               char errbuf[1024] = "";
-
-               ssnprintf (errbuf, sizeof (errbuf),
-                               "lt_dlopen (\"%s\") failed: %s. "
-                               "The most common cause for this problem are "
-                               "missing dependencies. Use ldd(1) to check "
-                               "the dependencies of the plugin "
-                               "/ shared object.",
-                               file, lt_dlerror ());
-
-               ERROR ("%s", errbuf);
-               /* Make sure this is printed to STDERR in any case, but also
-                * make sure it's printed only once. */
-               if (list_log != NULL)
-                       fprintf (stderr, "ERROR: %s\n", errbuf);
-
-               return (1);
-       }
-
-       if ((reg_handle = (void (*) (void)) lt_dlsym (dlh, "module_register")) == NULL)
-       {
-               WARNING ("Couldn't find symbol \"module_register\" in \"%s\": %s\n",
-                               file, lt_dlerror ());
-               lt_dlclose (dlh);
-               return (-1);
-       }
-
-       (*reg_handle) ();
-
-       return (0);
-}
-
-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.
-                * 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)
-               {
-                       pthread_cond_wait (&read_cond, &read_lock);
-                        pthread_mutex_unlock (&read_lock);
-                       continue;
-               }
-               pthread_mutex_unlock (&read_lock);
-
-               if (rf->rf_interval == 0)
-               {
-                       /* this should not happen, because the interval is set
-                        * for each plugin when loading it
-                        * XXX: issue a warning? */
-                       rf->rf_interval = plugin_get_interval ();
-                       rf->rf_effective_interval = rf->rf_interval;
-
-                       rf->rf_next_read = cdtime ();
-               }
-
-               /* sleep until this entry is due,
-                * using pthread_cond_timedwait */
-               pthread_mutex_lock (&read_lock);
-               /* In pthread_cond_timedwait, spurious wakeups are possible
-                * (and really happen, at least on NetBSD with > 1 CPU), thus
-                * we need to re-evaluate the condition every time
-                * pthread_cond_timedwait returns. */
-               rc = 0;
-               while ((read_loop != 0)
-                               && (cdtime () < rf->rf_next_read)
-                               && rc == 0)
-               {
-                       struct timespec ts = { 0 };
-
-                       CDTIME_T_TO_TIMESPEC (rf->rf_next_read, &ts);
-
-                       rc = pthread_cond_timedwait (&read_cond, &read_lock,
-                               &ts);
-               }
-
-               /* Must hold `read_lock' when accessing `rf->rf_type'. */
-               rf_type = rf->rf_type;
-               pthread_mutex_unlock (&read_lock);
-
-               /* Check if we're supposed to stop.. This may have interrupted
-                * the sleep, too. */
-               if (read_loop == 0)
-               {
-                       /* Insert `rf' again, so it can be free'd correctly */
-                       c_heap_insert (read_heap, rf);
-                       break;
-               }
-
-               /* The entry has been marked for deletion. The linked list
-                * entry has already been removed by `plugin_unregister_read'.
-                * All we have to do here is free the `read_func_t' and
-                * continue. */
-               if (rf_type == RF_REMOVE)
-               {
-                       DEBUG ("plugin_read_thread: Destroying the `%s' "
-                                       "callback.", rf->rf_name);
-                       sfree (rf->rf_name);
-                       destroy_callback ((callback_func_t *) rf);
-                       rf = NULL;
-                       continue;
-               }
-
-               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);
-
-                       callback = rf->rf_callback;
-                       status = (*callback) ();
-               }
-               else
-               {
-                       plugin_read_cb callback;
-
-                       assert (rf_type == RF_COMPLEX);
-
-                       callback = rf->rf_callback;
-                       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)
-               {
-                       rf->rf_effective_interval *= 2;
-                       if (rf->rf_effective_interval > TIME_T_TO_CDTIME_T (86400))
-                               rf->rf_effective_interval = TIME_T_TO_CDTIME_T (86400);
-
-                       NOTICE ("read-function of plugin `%s' failed. "
-                                       "Will suspend it for %.3f seconds.",
-                                       rf->rf_name,
-                                       CDTIME_T_TO_DOUBLE (rf->rf_effective_interval));
-               }
-               else
-               {
-                       /* Success: Restore the interval, if it was changed. */
-                       rf->rf_effective_interval = rf->rf_interval;
-               }
-
-               /* update the ``next read due'' field */
-               now = cdtime ();
-
-               DEBUG ("plugin_read_thread: Effective interval of the "
-                               "%s plugin is %.3f seconds.",
-                               rf->rf_name,
-                               CDTIME_T_TO_DOUBLE (rf->rf_effective_interval));
-
-               /* Calculate the next (absolute) time at which this function
-                * should be called. */
-               rf->rf_next_read += rf->rf_effective_interval;
-
-               /* Check, if `rf_next_read' is in the past. */
-               if (rf->rf_next_read < now)
-               {
-                       /* `rf_next_read' is in the past. Insert `now'
-                        * so this value doesn't trail off into the
-                        * past too much. */
-                       rf->rf_next_read = now;
-               }
-
-               DEBUG ("plugin_read_thread: Next read of the %s plugin at %.3f.",
-                               rf->rf_name,
-                               CDTIME_T_TO_DOUBLE (rf->rf_next_read));
-
-               /* Re-insert this read function into the heap again. */
-               c_heap_insert (read_heap, rf);
-       } /* while (read_loop) */
-
-       pthread_exit (NULL);
-       return ((void *) 0);
-} /* void *plugin_read_thread */
-
-static void start_read_threads (int num)
-{
-       int i;
-
-       if (read_threads != NULL)
-               return;
-
-       read_threads = (pthread_t *) calloc (num, sizeof (pthread_t));
-       if (read_threads == NULL)
-       {
-               ERROR ("plugin: start_read_threads: calloc failed.");
-               return;
-       }
-
-       read_threads_num = 0;
-       for (i = 0; i < num; i++)
-       {
-               if (pthread_create (read_threads + read_threads_num, NULL,
-                                       plugin_read_thread, NULL) == 0)
-               {
-                       read_threads_num++;
-               }
-               else
-               {
-                       ERROR ("plugin: start_read_threads: pthread_create failed.");
-                       return;
-               }
-       } /* for (i) */
-} /* void start_read_threads */
-
-static void stop_read_threads (void)
-{
-       int i;
-
-       if (read_threads == NULL)
-               return;
-
-       INFO ("collectd: Stopping %i read threads.", read_threads_num);
-
-       pthread_mutex_lock (&read_lock);
-       read_loop = 0;
-       DEBUG ("plugin: stop_read_threads: Signalling `read_cond'");
-       pthread_cond_broadcast (&read_cond);
-       pthread_mutex_unlock (&read_lock);
-
-       for (i = 0; i < read_threads_num; i++)
-       {
-               if (pthread_join (read_threads[i], NULL) != 0)
-               {
-                       ERROR ("plugin: stop_read_threads: pthread_join failed.");
-               }
-               read_threads[i] = (pthread_t) 0;
-       }
-       sfree (read_threads);
-       read_threads_num = 0;
-} /* void stop_read_threads */
-
-static void plugin_value_list_free (value_list_t *vl) /* {{{ */
-{
-       if (vl == NULL)
-               return;
-
-       meta_data_destroy (vl->meta);
-       sfree (vl->values);
-       sfree (vl);
-} /* }}} void plugin_value_list_free */
-
-static value_list_t *plugin_value_list_clone (value_list_t const *vl_orig) /* {{{ */
-{
-       value_list_t *vl;
-
-       if (vl_orig == NULL)
-               return (NULL);
-
-       vl = malloc (sizeof (*vl));
-       if (vl == NULL)
-               return (NULL);
-       memcpy (vl, vl_orig, sizeof (*vl));
-
-       vl->values = calloc (vl_orig->values_len, sizeof (*vl->values));
-       if (vl->values == NULL)
-       {
-               plugin_value_list_free (vl);
-               return (NULL);
-       }
-       memcpy (vl->values, vl_orig->values,
-                       vl_orig->values_len * sizeof (*vl->values));
-
-       vl->meta = meta_data_clone (vl->meta);
-       if ((vl_orig->meta != NULL) && (vl->meta == NULL))
-       {
-               plugin_value_list_free (vl);
-               return (NULL);
-       }
-
-       if (vl->time == 0)
-               vl->time = cdtime ();
-
-       /* Fill in the interval from the thread context, if it is zero. */
-       if (vl->interval == 0)
-       {
-               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_value_list_clone: 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 ();
-               }
-       }
-
-       return (vl);
-} /* }}} value_list_t *plugin_value_list_clone */
-
-static int plugin_write_enqueue (value_list_t const *vl) /* {{{ */
-{
-       write_queue_t *q;
-
-       q = malloc (sizeof (*q));
-       if (q == NULL)
-               return (ENOMEM);
-       q->next = NULL;
-
-       q->vl = plugin_value_list_clone (vl);
-       if (q->vl == NULL)
-       {
-               sfree (q);
-               return (ENOMEM);
-       }
-
-       /* Store context of caller (read plugin); otherwise, it would not be
-        * available to the write plugins when actually dispatching the
-        * value-list later on. */
-       q->ctx = plugin_get_ctx ();
-
-       pthread_mutex_lock (&write_lock);
-
-       if (write_queue_tail == NULL)
-       {
-               write_queue_head = q;
-               write_queue_tail = q;
-               write_queue_length = 1;
-       }
-       else
-       {
-               write_queue_tail->next = q;
-               write_queue_tail = q;
-               write_queue_length += 1;
-       }
-
-       pthread_cond_signal (&write_cond);
-       pthread_mutex_unlock (&write_lock);
-
-       return (0);
-} /* }}} int plugin_write_enqueue */
-
-static value_list_t *plugin_write_dequeue (void) /* {{{ */
-{
-       write_queue_t *q;
-       value_list_t *vl;
-
-       pthread_mutex_lock (&write_lock);
-
-       while (write_loop && (write_queue_head == NULL))
-               pthread_cond_wait (&write_cond, &write_lock);
-
-       if (write_queue_head == NULL)
-       {
-               pthread_mutex_unlock (&write_lock);
-               return (NULL);
-       }
-
-       q = write_queue_head;
-       write_queue_head = q->next;
-       write_queue_length -= 1;
-       if (write_queue_head == NULL) {
-               write_queue_tail = NULL;
-               assert(0 == write_queue_length);
-               }
-
-       pthread_mutex_unlock (&write_lock);
-
-       (void) plugin_set_ctx (q->ctx);
-
-       vl = q->vl;
-       sfree (q);
-       return (vl);
-} /* }}} value_list_t *plugin_write_dequeue */
-
-static void *plugin_write_thread (void __attribute__((unused)) *args) /* {{{ */
-{
-       while (write_loop)
-       {
-               value_list_t *vl = plugin_write_dequeue ();
-               if (vl == NULL)
-                       continue;
-
-               plugin_dispatch_values_internal (vl);
-
-               plugin_value_list_free (vl);
-       }
-
-       pthread_exit (NULL);
-       return ((void *) 0);
-} /* }}} void *plugin_write_thread */
-
-static void start_write_threads (size_t num) /* {{{ */
-{
-       size_t i;
-
-       if (write_threads != NULL)
-               return;
-
-       write_threads = (pthread_t *) calloc (num, sizeof (pthread_t));
-       if (write_threads == NULL)
-       {
-               ERROR ("plugin: start_write_threads: calloc failed.");
-               return;
-       }
-
-       write_threads_num = 0;
-       for (i = 0; i < num; i++)
-       {
-               int status;
-
-               status = pthread_create (write_threads + write_threads_num,
-                               /* attr = */ NULL,
-                               plugin_write_thread,
-                               /* arg = */ NULL);
-               if (status != 0)
-               {
-                       char errbuf[1024];
-                       ERROR ("plugin: start_write_threads: pthread_create failed "
-                                       "with status %i (%s).", status,
-                                       sstrerror (status, errbuf, sizeof (errbuf)));
-                       return;
-               }
-
-               write_threads_num++;
-       } /* for (i) */
-} /* }}} void start_write_threads */
-
-static void stop_write_threads (void) /* {{{ */
-{
-       write_queue_t *q;
-       int i;
-
-       if (write_threads == NULL)
-               return;
-
-       INFO ("collectd: Stopping %zu write threads.", write_threads_num);
-
-       pthread_mutex_lock (&write_lock);
-       write_loop = 0;
-       DEBUG ("plugin: stop_write_threads: Signalling `write_cond'");
-       pthread_cond_broadcast (&write_cond);
-       pthread_mutex_unlock (&write_lock);
-
-       for (i = 0; i < write_threads_num; i++)
-       {
-               if (pthread_join (write_threads[i], NULL) != 0)
-               {
-                       ERROR ("plugin: stop_write_threads: pthread_join failed.");
-               }
-               write_threads[i] = (pthread_t) 0;
-       }
-       sfree (write_threads);
-       write_threads_num = 0;
-
-       pthread_mutex_lock (&write_lock);
-       i = 0;
-       for (q = write_queue_head; q != NULL; )
-       {
-               write_queue_t *q1 = q;
-               plugin_value_list_free (q->vl);
-               q = q->next;
-               sfree (q1);
-               i++;
-       }
-       write_queue_head = NULL;
-       write_queue_tail = NULL;
-       write_queue_length = 0;
-       pthread_mutex_unlock (&write_lock);
-
-       if (i > 0)
-       {
-               WARNING ("plugin: %i value list%s left after shutting down "
-                               "the write threads.",
-                               i, (i == 1) ? " was" : "s were");
-       }
-} /* }}} void stop_write_threads */
-
-/*
- * Public functions
- */
-void plugin_set_dir (const char *dir)
-{
-       if (plugindir != NULL)
-               free (plugindir);
-
-       if (dir == NULL)
-               plugindir = NULL;
-       else if ((plugindir = strdup (dir)) == NULL)
-       {
-               char errbuf[1024];
-               ERROR ("strdup failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-       }
-}
-
-static _Bool plugin_is_loaded (char const *name)
-{
-       int status;
-
-       if (plugins_loaded == NULL)
-               plugins_loaded = c_avl_create ((void *) strcasecmp);
-       assert (plugins_loaded != NULL);
-
-       status = c_avl_get (plugins_loaded, name, /* ret_value = */ NULL);
-       return (status == 0);
-}
-
-static int plugin_mark_loaded (char const *name)
-{
-       char *name_copy;
-       int status;
-
-       name_copy = strdup (name);
-       if (name_copy == NULL)
-               return (ENOMEM);
-
-       status = c_avl_insert (plugins_loaded,
-                       /* key = */ name_copy, /* value = */ NULL);
-       return (status);
-}
-
-static void plugin_free_loaded ()
-{
-       void *key;
-       void *value;
-
-       if (plugins_loaded == NULL)
-               return;
-
-       while (c_avl_pick (plugins_loaded, &key, &value) == 0)
-       {
-               sfree (key);
-               assert (value == NULL);
-       }
-
-       c_avl_destroy (plugins_loaded);
-       plugins_loaded = NULL;
-}
-
-#define BUFSIZE 512
-int plugin_load (char const *plugin_name, uint32_t flags)
-{
-       DIR  *dh;
-       const char *dir;
-       char  filename[BUFSIZE] = "";
-       char  typename[BUFSIZE];
-       int   typename_len;
-       int   ret;
-       struct stat    statbuf;
-       struct dirent *de;
-       int status;
-
-       if (plugin_name == NULL)
-               return (EINVAL);
-
-       /* Check if plugin is already loaded and don't do anything in this
-        * case. */
-       if (plugin_is_loaded (plugin_name))
-               return (0);
-
-       dir = plugin_get_dir ();
-       ret = 1;
-
-       /*
-        * XXX: Magic at work:
-        *
-        * Some of the language bindings, for example the Python and Perl
-        * plugins, need to be able to export symbols to the scripts they run.
-        * For this to happen, the "Globals" flag needs to be set.
-        * Unfortunately, this technical detail is hard to explain to the
-        * average user and she shouldn't have to worry about this, ideally.
-        * So in order to save everyone's sanity use a different default for a
-        * handful of special plugins. --octo
-        */
-       if ((strcasecmp ("perl", plugin_name) == 0)
-                       || (strcasecmp ("python", plugin_name) == 0))
-               flags |= PLUGIN_FLAGS_GLOBAL;
-
-       /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
-        * type when matching the filename */
-       status = ssnprintf (typename, sizeof (typename), "%s.so", plugin_name);
-       if ((status < 0) || ((size_t) status >= sizeof (typename)))
-       {
-               WARNING ("plugin_load: Filename too long: \"%s.so\"", plugin_name);
-               return (-1);
-       }
-       typename_len = strlen (typename);
-
-       if ((dh = opendir (dir)) == NULL)
-       {
-               char errbuf[1024];
-               ERROR ("plugin_load: opendir (%s) failed: %s", dir,
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-               return (-1);
-       }
-
-       while ((de = readdir (dh)) != NULL)
-       {
-               if (strncasecmp (de->d_name, typename, typename_len))
-                       continue;
-
-               status = ssnprintf (filename, sizeof (filename),
-                               "%s/%s", dir, de->d_name);
-               if ((status < 0) || ((size_t) status >= sizeof (filename)))
-               {
-                       WARNING ("plugin_load: Filename too long: \"%s/%s\"",
-                                       dir, de->d_name);
-                       continue;
-               }
-
-               if (lstat (filename, &statbuf) == -1)
-               {
-                       char errbuf[1024];
-                       WARNING ("plugin_load: stat (\"%s\") failed: %s",
-                                       filename,
-                                       sstrerror (errno, errbuf, sizeof (errbuf)));
-                       continue;
-               }
-               else if (!S_ISREG (statbuf.st_mode))
-               {
-                       /* don't follow symlinks */
-                       WARNING ("plugin_load: %s is not a regular file.",
-                                       filename);
-                       continue;
-               }
-
-               status = plugin_load_file (filename, flags);
-               if (status == 0)
-               {
-                       /* success */
-                       plugin_mark_loaded (plugin_name);
-                       ret = 0;
-                       break;
-               }
-               else
-               {
-                       ERROR ("plugin_load: Load plugin \"%s\" failed with "
-                                       "status %i.", plugin_name, status);
-               }
-       }
-
-       closedir (dh);
-
-       if (filename[0] == 0)
-               ERROR ("plugin_load: Could not find plugin \"%s\" in %s",
-                               plugin_name, dir);
-
-       return (ret);
-}
-
-/*
- * The `register_*' functions follow
- */
-int plugin_register_config (const char *name,
-               int (*callback) (const char *key, const char *val),
-               const char **keys, int keys_num)
-{
-       cf_register (name, callback, keys, keys_num);
-       return (0);
-} /* int plugin_register_config */
-
-int plugin_register_complex_config (const char *type,
-               int (*callback) (oconfig_item_t *))
-{
-       return (cf_register_complex (type, callback));
-} /* int plugin_register_complex_config */
-
-int plugin_register_init (const char *name,
-               int (*callback) (void))
-{
-       return (create_register_callback (&list_init, name, (void *) callback,
-                               /* user_data = */ NULL));
-} /* plugin_register_init */
-
-static int plugin_compare_read_func (const void *arg0, const void *arg1)
-{
-       const read_func_t *rf0;
-       const read_func_t *rf1;
-
-       rf0 = arg0;
-       rf1 = arg1;
-
-       if (rf0->rf_next_read < rf1->rf_next_read)
-               return (-1);
-       else if (rf0->rf_next_read > rf1->rf_next_read)
-               return (1);
-       else
-               return (0);
-} /* int plugin_compare_read_func */
-
-/* Add a read function to both, the heap and a linked list. The linked list if
- * used to look-up read functions, especially for the remove function. The heap
- * is used to determine which plugin to read next. */
-static int plugin_insert_read (read_func_t *rf)
-{
-       int status;
-       llentry_t *le;
-
-       rf->rf_next_read = cdtime ();
-       rf->rf_effective_interval = rf->rf_interval;
-
-       pthread_mutex_lock (&read_lock);
-
-       if (read_list == NULL)
-       {
-               read_list = llist_create ();
-               if (read_list == NULL)
-               {
-                       pthread_mutex_unlock (&read_lock);
-                       ERROR ("plugin_insert_read: read_list failed.");
-                       return (-1);
-               }
-       }
-
-       if (read_heap == NULL)
-       {
-               read_heap = c_heap_create (plugin_compare_read_func);
-               if (read_heap == NULL)
-               {
-                       pthread_mutex_unlock (&read_lock);
-                       ERROR ("plugin_insert_read: c_heap_create failed.");
-                       return (-1);
-               }
-       }
-
-       le = llist_search (read_list, rf->rf_name);
-       if (le != NULL)
-       {
-               pthread_mutex_unlock (&read_lock);
-               WARNING ("The read function \"%s\" is already registered. "
-                               "Check for duplicate \"LoadPlugin\" lines "
-                               "in your configuration!",
-                               rf->rf_name);
-               return (EINVAL);
-       }
-
-       le = llentry_create (rf->rf_name, rf);
-       if (le == NULL)
-       {
-               pthread_mutex_unlock (&read_lock);
-               ERROR ("plugin_insert_read: llentry_create failed.");
-               return (-1);
-       }
-
-       status = c_heap_insert (read_heap, rf);
-       if (status != 0)
-       {
-               pthread_mutex_unlock (&read_lock);
-               ERROR ("plugin_insert_read: c_heap_insert failed.");
-               llentry_destroy (le);
-               return (-1);
-       }
-
-       /* 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 */
-
-int plugin_register_read (const char *name,
-               int (*callback) (void))
-{
-       read_func_t *rf;
-       int status;
-
-       rf = malloc (sizeof (*rf));
-       if (rf == NULL)
-       {
-               ERROR ("plugin_register_read: malloc failed.");
-               return (ENOMEM);
-       }
-
-       memset (rf, 0, sizeof (read_func_t));
-       rf->rf_callback = (void *) callback;
-       rf->rf_udata.data = NULL;
-       rf->rf_udata.free_func = NULL;
-       rf->rf_ctx = plugin_get_ctx ();
-       rf->rf_group[0] = '\0';
-       rf->rf_name = strdup (name);
-       rf->rf_type = RF_SIMPLE;
-       rf->rf_interval = plugin_get_interval ();
-
-       status = plugin_insert_read (rf);
-       if (status != 0)
-               sfree (rf);
-
-       return (status);
-} /* int plugin_register_read */
-
-int plugin_register_complex_read (const char *group, const char *name,
-               plugin_read_cb callback,
-               const struct timespec *interval,
-               user_data_t *user_data)
-{
-       read_func_t *rf;
-       int status;
-
-       rf = malloc (sizeof (*rf));
-       if (rf == NULL)
-       {
-               ERROR ("plugin_register_complex_read: malloc failed.");
-               return (ENOMEM);
-       }
-
-       memset (rf, 0, sizeof (read_func_t));
-       rf->rf_callback = (void *) callback;
-       if (group != NULL)
-               sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
-       else
-               rf->rf_group[0] = '\0';
-       rf->rf_name = strdup (name);
-       rf->rf_type = RF_COMPLEX;
-       if (interval != NULL)
-               rf->rf_interval = TIMESPEC_TO_CDTIME_T (interval);
-       else
-               rf->rf_interval = plugin_get_interval ();
-
-       /* Set user data */
-       if (user_data == NULL)
-       {
-               rf->rf_udata.data = NULL;
-               rf->rf_udata.free_func = NULL;
-       }
-       else
-       {
-               rf->rf_udata = *user_data;
-       }
-
-       rf->rf_ctx = plugin_get_ctx ();
-
-       status = plugin_insert_read (rf);
-       if (status != 0)
-               sfree (rf);
-
-       return (status);
-} /* int plugin_register_complex_read */
-
-int plugin_register_write (const char *name,
-               plugin_write_cb callback, user_data_t *ud)
-{
-       return (create_register_callback (&list_write, name,
-                               (void *) callback, ud));
-} /* int plugin_register_write */
-
-int plugin_register_flush (const char *name,
-               plugin_flush_cb callback, user_data_t *ud)
-{
-       return (create_register_callback (&list_flush, name,
-                               (void *) callback, ud));
-} /* int plugin_register_flush */
-
-int plugin_register_missing (const char *name,
-               plugin_missing_cb callback, user_data_t *ud)
-{
-       return (create_register_callback (&list_missing, name,
-                               (void *) callback, ud));
-} /* int plugin_register_missing */
-
-int plugin_register_shutdown (const char *name,
-               int (*callback) (void))
-{
-       return (create_register_callback (&list_shutdown, name,
-                               (void *) callback, /* user_data = */ NULL));
-} /* int plugin_register_shutdown */
-
-static void plugin_free_data_sets (void)
-{
-       void *key;
-       void *value;
-
-       if (data_sets == NULL)
-               return;
-
-       while (c_avl_pick (data_sets, &key, &value) == 0)
-       {
-               data_set_t *ds = value;
-               /* key is a pointer to ds->type */
-
-               sfree (ds->ds);
-               sfree (ds);
-       }
-
-       c_avl_destroy (data_sets);
-       data_sets = NULL;
-} /* void plugin_free_data_sets */
-
-int plugin_register_data_set (const data_set_t *ds)
-{
-       data_set_t *ds_copy;
-       int i;
-
-       if ((data_sets != NULL)
-                       && (c_avl_get (data_sets, ds->type, NULL) == 0))
-       {
-               NOTICE ("Replacing DS `%s' with another version.", ds->type);
-               plugin_unregister_data_set (ds->type);
-       }
-       else if (data_sets == NULL)
-       {
-               data_sets = c_avl_create ((int (*) (const void *, const void *)) strcmp);
-               if (data_sets == NULL)
-                       return (-1);
-       }
-
-       ds_copy = (data_set_t *) malloc (sizeof (data_set_t));
-       if (ds_copy == NULL)
-               return (-1);
-       memcpy(ds_copy, ds, sizeof (data_set_t));
-
-       ds_copy->ds = (data_source_t *) malloc (sizeof (data_source_t)
-                       * ds->ds_num);
-       if (ds_copy->ds == NULL)
-       {
-               free (ds_copy);
-               return (-1);
-       }
-
-       for (i = 0; i < ds->ds_num; i++)
-               memcpy (ds_copy->ds + i, ds->ds + i, sizeof (data_source_t));
-
-       return (c_avl_insert (data_sets, (void *) ds_copy->type, (void *) ds_copy));
-} /* int plugin_register_data_set */
-
-int plugin_register_log (const char *name,
-               plugin_log_cb callback, user_data_t *ud)
-{
-       return (create_register_callback (&list_log, name,
-                               (void *) callback, ud));
-} /* int plugin_register_log */
-
-int plugin_register_notification (const char *name,
-               plugin_notification_cb callback, user_data_t *ud)
-{
-       return (create_register_callback (&list_notification, name,
-                               (void *) callback, ud));
-} /* int plugin_register_log */
-
-int plugin_unregister_config (const char *name)
-{
-       cf_unregister (name);
-       return (0);
-} /* int plugin_unregister_config */
-
-int plugin_unregister_complex_config (const char *name)
-{
-       cf_unregister_complex (name);
-       return (0);
-} /* int plugin_unregister_complex_config */
-
-int plugin_unregister_init (const char *name)
-{
-       return (plugin_unregister (list_init, name));
-}
-
-int plugin_unregister_read (const char *name) /* {{{ */
-{
-       llentry_t *le;
-       read_func_t *rf;
-
-       if (name == NULL)
-               return (-ENOENT);
-
-       pthread_mutex_lock (&read_lock);
-
-       if (read_list == NULL)
-       {
-               pthread_mutex_unlock (&read_lock);
-               return (-ENOENT);
-       }
-
-       le = llist_search (read_list, name);
-       if (le == NULL)
-       {
-               pthread_mutex_unlock (&read_lock);
-               WARNING ("plugin_unregister_read: No such read function: %s",
-                               name);
-               return (-ENOENT);
-       }
-
-       llist_remove (read_list, le);
-
-       rf = le->value;
-       assert (rf != NULL);
-       rf->rf_type = RF_REMOVE;
-
-       pthread_mutex_unlock (&read_lock);
-
-       llentry_destroy (le);
-
-       DEBUG ("plugin_unregister_read: Marked `%s' for removal.", name);
-
-       return (0);
-} /* }}} int plugin_unregister_read */
-
-static int compare_read_func_group (llentry_t *e, void *ud) /* {{{ */
-{
-       read_func_t *rf    = e->value;
-       char        *group = ud;
-
-       return strcmp (rf->rf_group, (const char *)group);
-} /* }}} int compare_read_func_group */
-
-int plugin_unregister_read_group (const char *group) /* {{{ */
-{
-       llentry_t *le;
-       read_func_t *rf;
-
-       int found = 0;
-
-       if (group == NULL)
-               return (-ENOENT);
-
-       pthread_mutex_lock (&read_lock);
-
-       if (read_list == NULL)
-       {
-               pthread_mutex_unlock (&read_lock);
-               return (-ENOENT);
-       }
-
-       while (42)
-       {
-               le = llist_search_custom (read_list,
-                               compare_read_func_group, (void *)group);
-
-               if (le == NULL)
-                       break;
-
-               ++found;
-
-               llist_remove (read_list, le);
-
-               rf = le->value;
-               assert (rf != NULL);
-               rf->rf_type = RF_REMOVE;
-
-               llentry_destroy (le);
-
-               DEBUG ("plugin_unregister_read_group: "
-                               "Marked `%s' (group `%s') for removal.",
-                               rf->rf_name, group);
-       }
-
-       pthread_mutex_unlock (&read_lock);
-
-       if (found == 0)
-       {
-               WARNING ("plugin_unregister_read_group: No such "
-                               "group of read function: %s", group);
-               return (-ENOENT);
-       }
-
-       return (0);
-} /* }}} int plugin_unregister_read_group */
-
-int plugin_unregister_write (const char *name)
-{
-       return (plugin_unregister (list_write, name));
-}
-
-int plugin_unregister_flush (const char *name)
-{
-       return (plugin_unregister (list_flush, name));
-}
-
-int plugin_unregister_missing (const char *name)
-{
-       return (plugin_unregister (list_missing, name));
-}
-
-int plugin_unregister_shutdown (const char *name)
-{
-       return (plugin_unregister (list_shutdown, name));
-}
-
-int plugin_unregister_data_set (const char *name)
-{
-       data_set_t *ds;
-
-       if (data_sets == NULL)
-               return (-1);
-
-       if (c_avl_remove (data_sets, name, NULL, (void *) &ds) != 0)
-               return (-1);
-
-       sfree (ds->ds);
-       sfree (ds);
-
-       return (0);
-} /* int plugin_unregister_data_set */
-
-int plugin_unregister_log (const char *name)
-{
-       return (plugin_unregister (list_log, name));
-}
-
-int plugin_unregister_notification (const char *name)
-{
-       return (plugin_unregister (list_notification, name));
-}
-
-void plugin_init_all (void)
-{
-       char const *chain_name;
-       long write_threads_num;
-       llentry_t *le;
-       int status;
-
-       /* Init the value cache */
-       uc_init ();
-
-       chain_name = global_option_get ("PreCacheChain");
-       pre_cache_chain = fc_chain_get_by_name (chain_name);
-
-       chain_name = global_option_get ("PostCacheChain");
-       post_cache_chain = fc_chain_get_by_name (chain_name);
-
-       write_limit_high = global_option_get_long ("WriteQueueLimitHigh",
-                       /* default = */ 0);
-       if (write_limit_high < 0)
-       {
-               ERROR ("WriteQueueLimitHigh must be positive or zero.");
-               write_limit_high = 0;
-       }
-
-       write_limit_low = global_option_get_long ("WriteQueueLimitLow",
-                       /* default = */ write_limit_high / 2);
-       if (write_limit_low < 0)
-       {
-               ERROR ("WriteQueueLimitLow must be positive or zero.");
-               write_limit_low = write_limit_high / 2;
-       }
-       else if (write_limit_low > write_limit_high)
-       {
-               ERROR ("WriteQueueLimitLow must not be larger than "
-                               "WriteQueueLimitHigh.");
-               write_limit_low = write_limit_high;
-       }
-
-       write_threads_num = global_option_get_long ("WriteThreads",
-                       /* default = */ 5);
-       if (write_threads_num < 1)
-       {
-               ERROR ("WriteThreads must be positive.");
-               write_threads_num = 5;
-       }
-
-       start_write_threads ((size_t) write_threads_num);
-
-       if ((list_init == NULL) && (read_heap == NULL))
-               return;
-
-       /* Calling all init callbacks before checking if read callbacks
-        * are available allows the init callbacks to register the read
-        * callback. */
-       le = llist_head (list_init);
-       while (le != NULL)
-       {
-               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)
-               {
-                       ERROR ("Initialization of plugin `%s' "
-                                       "failed with status %i. "
-                                       "Plugin will be unloaded.",
-                                       le->key, status);
-                       /* Plugins that register read callbacks from the init
-                        * callback should take care of appropriate error
-                        * handling themselves. */
-                       /* FIXME: Unload _all_ functions */
-                       plugin_unregister_read (le->key);
-               }
-
-               le = le->next;
-       }
-
-       /* Start read-threads */
-       if (read_heap != NULL)
-       {
-               const char *rt;
-               int num;
-               rt = global_option_get ("ReadThreads");
-               num = atoi (rt);
-               if (num != -1)
-                       start_read_threads ((num > 0) ? num : 5);
-       }
-} /* void plugin_init_all */
-
-/* TODO: Rename this function. */
-void plugin_read_all (void)
-{
-       uc_check_timeout ();
-
-       return;
-} /* void plugin_read_all */
-
-/* Read function called when the `-T' command line argument is given. */
-int plugin_read_all_once (void)
-{
-       int status;
-       int return_status = 0;
-
-       if (read_heap == NULL)
-       {
-               NOTICE ("No read-functions are registered.");
-               return (0);
-       }
-
-       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);
-
-                       callback = rf->rf_callback;
-                       status = (*callback) ();
-               }
-               else
-               {
-                       plugin_read_cb callback;
-
-                       callback = rf->rf_callback;
-                       status = (*callback) (&rf->rf_udata);
-               }
-
-               plugin_set_ctx (old_ctx);
-
-               if (status != 0)
-               {
-                       NOTICE ("read-function of plugin `%s' failed.",
-                                       rf->rf_name);
-                       return_status = -1;
-               }
-
-               destroy_callback ((void *) rf);
-       }
-
-       return (return_status);
-} /* int plugin_read_all_once */
-
-int plugin_write (const char *plugin, /* {{{ */
-               const data_set_t *ds, const value_list_t *vl)
-{
-  llentry_t *le;
-  int status;
-
-  if (vl == NULL)
-    return (EINVAL);
-
-  if (list_write == NULL)
-    return (ENOENT);
-
-  if (ds == NULL)
-  {
-    ds = plugin_get_ds (vl->type);
-    if (ds == NULL)
-    {
-      ERROR ("plugin_write: Unable to lookup type `%s'.", vl->type);
-      return (ENOENT);
-    }
-  }
-
-  if (plugin == NULL)
-  {
-    int success = 0;
-    int failure = 0;
-
-    le = llist_head (list_write);
-    while (le != NULL)
-    {
-      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);
-      if (status != 0)
-        failure++;
-      else
-        success++;
-
-      le = le->next;
-    }
-
-    if ((success == 0) && (failure != 0))
-      status = -1;
-    else
-      status = 0;
-  }
-  else /* plugin != NULL */
-  {
-    callback_func_t *cf;
-    plugin_write_cb callback;
-
-    le = llist_head (list_write);
-    while (le != NULL)
-    {
-      if (strcasecmp (plugin, le->key) == 0)
-        break;
-
-      le = le->next;
-    }
-
-    if (le == NULL)
-      return (ENOENT);
-
-    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);
-  }
-
-  return (status);
-} /* }}} int plugin_write */
-
-int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier)
-{
-  llentry_t *le;
-
-  if (list_flush == NULL)
-    return (0);
-
-  le = llist_head (list_flush);
-  while (le != NULL)
-  {
-    callback_func_t *cf;
-    plugin_flush_cb callback;
-    plugin_ctx_t old_ctx;
-
-    if ((plugin != NULL)
-        && (strcmp (plugin, le->key) != 0))
-    {
-      le = le->next;
-      continue;
-    }
-
-    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);
-} /* int plugin_flush */
-
-void plugin_shutdown_all (void)
-{
-       llentry_t *le;
-
-       stop_read_threads ();
-
-       destroy_all_callbacks (&list_init);
-
-       pthread_mutex_lock (&read_lock);
-       llist_destroy (read_list);
-       read_list = NULL;
-       pthread_mutex_unlock (&read_lock);
-
-       destroy_read_heap ();
-
-       plugin_flush (/* plugin = */ NULL,
-                       /* timeout = */ 0,
-                       /* identifier = */ NULL);
-
-       le = NULL;
-       if (list_shutdown != NULL)
-               le = llist_head (list_shutdown);
-
-       while (le != NULL)
-       {
-               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
-                * shutdown functions to unregister themselves. If done the
-                * other way around the memory `le' points to will be freed
-                * after callback returns. */
-               le = le->next;
-
-               (*callback) ();
-
-               plugin_set_ctx (old_ctx);
-       }
-
-       stop_write_threads ();
-
-       /* Write plugins which use the `user_data' pointer usually need the
-        * same data available to the flush callback. If this is the case, set
-        * the free_function to NULL when registering the flush callback and to
-        * the real free function when registering the write callback. This way
-        * the data isn't freed twice. */
-       destroy_all_callbacks (&list_flush);
-       destroy_all_callbacks (&list_missing);
-       destroy_all_callbacks (&list_write);
-
-       destroy_all_callbacks (&list_notification);
-       destroy_all_callbacks (&list_shutdown);
-       destroy_all_callbacks (&list_log);
-
-       plugin_free_loaded ();
-       plugin_free_data_sets ();
-} /* void plugin_shutdown_all */
-
-int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
-{
-  llentry_t *le;
-
-  if (list_missing == NULL)
-    return (0);
-
-  le = llist_head (list_missing);
-  while (le != NULL)
-  {
-    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)
-      {
-        ERROR ("plugin_dispatch_missing: Callback function \"%s\" "
-            "failed with status %i.",
-            le->key, status);
-        return (status);
-      }
-      else
-      {
-        return (0);
-      }
-    }
-
-    le = le->next;
-  }
-  return (0);
-} /* int }}} plugin_dispatch_missing */
-
-static int plugin_dispatch_values_internal (value_list_t *vl)
-{
-       int status;
-       static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC;
-
-       value_t *saved_values;
-       int      saved_values_len;
-
-       data_set_t *ds;
-
-       int free_meta_data = 0;
-
-       if ((vl == NULL) || (vl->type[0] == 0)
-                       || (vl->values == NULL) || (vl->values_len < 1))
-       {
-               ERROR ("plugin_dispatch_values: Invalid value list "
-                               "from plugin %s.", vl->plugin);
-               return (-1);
-       }
-
-       /* Free meta data only if the calling function didn't specify any. In
-        * this case matches and targets may add some and the calling function
-        * may not expect (and therefore free) that data. */
-       if (vl->meta == NULL)
-               free_meta_data = 1;
-
-       if (list_write == NULL)
-               c_complain_once (LOG_WARNING, &no_write_complaint,
-                               "plugin_dispatch_values: No write callback has been "
-                               "registered. Please load at least one output plugin, "
-                               "if you want the collected data to be stored.");
-
-       if (data_sets == NULL)
-       {
-               ERROR ("plugin_dispatch_values: No data sets registered. "
-                               "Could the types database be read? Check "
-                               "your `TypesDB' setting!");
-               return (-1);
-       }
-
-       if (c_avl_get (data_sets, vl->type, (void *) &ds) != 0)
-       {
-               char ident[6 * DATA_MAX_NAME_LEN];
-
-               FORMAT_VL (ident, sizeof (ident), vl);
-               INFO ("plugin_dispatch_values: Dataset not found: %s "
-                               "(from \"%s\"), check your types.db!",
-                               vl->type, ident);
-               return (-1);
-       }
-
-       /* Assured by plugin_value_list_clone(). The time is determined at
-        * _enqueue_ time. */
-       assert (vl->time != 0);
-       assert (vl->interval != 0);
-
-       DEBUG ("plugin_dispatch_values: time = %.3f; interval = %.3f; "
-                       "host = %s; "
-                       "plugin = %s; plugin_instance = %s; "
-                       "type = %s; type_instance = %s;",
-                       CDTIME_T_TO_DOUBLE (vl->time),
-                       CDTIME_T_TO_DOUBLE (vl->interval),
-                       vl->host,
-                       vl->plugin, vl->plugin_instance,
-                       vl->type, vl->type_instance);
-
-#if COLLECT_DEBUG
-       assert (0 == strcmp (ds->type, vl->type));
-#else
-       if (0 != strcmp (ds->type, vl->type))
-               WARNING ("plugin_dispatch_values: (ds->type = %s) != (vl->type = %s)",
-                               ds->type, vl->type);
-#endif
-
-#if COLLECT_DEBUG
-       assert (ds->ds_num == vl->values_len);
-#else
-       if (ds->ds_num != vl->values_len)
-       {
-               ERROR ("plugin_dispatch_values: ds->type = %s: "
-                               "(ds->ds_num = %i) != "
-                               "(vl->values_len = %i)",
-                               ds->type, ds->ds_num, vl->values_len);
-               return (-1);
-       }
-#endif
-
-       escape_slashes (vl->host, sizeof (vl->host));
-       escape_slashes (vl->plugin, sizeof (vl->plugin));
-       escape_slashes (vl->plugin_instance, sizeof (vl->plugin_instance));
-       escape_slashes (vl->type, sizeof (vl->type));
-       escape_slashes (vl->type_instance, sizeof (vl->type_instance));
-
-       /* Copy the values. This way, we can assure `targets' that they get
-        * dynamically allocated values, which they can free and replace if
-        * they like. */
-       if ((pre_cache_chain != NULL) || (post_cache_chain != NULL))
-       {
-               saved_values     = vl->values;
-               saved_values_len = vl->values_len;
-
-               vl->values = (value_t *) calloc (vl->values_len,
-                               sizeof (*vl->values));
-               if (vl->values == NULL)
-               {
-                       ERROR ("plugin_dispatch_values: calloc failed.");
-                       vl->values = saved_values;
-                       return (-1);
-               }
-               memcpy (vl->values, saved_values,
-                               vl->values_len * sizeof (*vl->values));
-       }
-       else /* if ((pre == NULL) && (post == NULL)) */
-       {
-               saved_values     = NULL;
-               saved_values_len = 0;
-       }
-
-       if (pre_cache_chain != NULL)
-       {
-               status = fc_process_chain (ds, vl, pre_cache_chain);
-               if (status < 0)
-               {
-                       WARNING ("plugin_dispatch_values: Running the "
-                                       "pre-cache chain failed with "
-                                       "status %i (%#x).",
-                                       status, status);
-               }
-               else if (status == FC_TARGET_STOP)
-               {
-                       /* Restore the state of the value_list so that plugins
-                        * don't get confused.. */
-                       if (saved_values != NULL)
-                       {
-                               free (vl->values);
-                               vl->values     = saved_values;
-                               vl->values_len = saved_values_len;
-                       }
-                       return (0);
-               }
-       }
-
-       /* Update the value cache */
-       uc_update (ds, vl);
-
-       if (post_cache_chain != NULL)
-       {
-               status = fc_process_chain (ds, vl, post_cache_chain);
-               if (status < 0)
-               {
-                       WARNING ("plugin_dispatch_values: Running the "
-                                       "post-cache chain failed with "
-                                       "status %i (%#x).",
-                                       status, status);
-               }
-       }
-       else
-               fc_default_action (ds, vl);
-
-       /* Restore the state of the value_list so that plugins don't get
-        * confused.. */
-       if (saved_values != NULL)
-       {
-               free (vl->values);
-               vl->values     = saved_values;
-               vl->values_len = saved_values_len;
-       }
-
-       if ((free_meta_data != 0) && (vl->meta != NULL))
-       {
-               meta_data_destroy (vl->meta);
-               vl->meta = NULL;
-       }
-
-       return (0);
-} /* int plugin_dispatch_values_internal */
-
-static double get_drop_probability (void) /* {{{ */
-{
-       long pos;
-       long size;
-       long wql;
-
-       pthread_mutex_lock (&write_lock);
-       wql = write_queue_length;
-       pthread_mutex_unlock (&write_lock);
-
-       if (wql < write_limit_low)
-               return (0.0);
-       if (wql >= write_limit_high)
-               return (1.0);
-
-       pos = 1 + wql - write_limit_low;
-       size = 1 + write_limit_high - write_limit_low;
-
-       return (((double) pos) / ((double) size));
-} /* }}} double get_drop_probability */
-
-static _Bool check_drop_value (void) /* {{{ */
-{
-       static cdtime_t last_message_time = 0;
-       static pthread_mutex_t last_message_lock = PTHREAD_MUTEX_INITIALIZER;
-
-       double p;
-       double q;
-       int status;
-
-       if (write_limit_high == 0)
-               return (0);
-
-       p = get_drop_probability ();
-       if (p == 0.0)
-               return (0);
-
-       status = pthread_mutex_trylock (&last_message_lock);
-       if (status == 0)
-       {
-               cdtime_t now;
-
-               now = cdtime ();
-               if ((now - last_message_time) > TIME_T_TO_CDTIME_T (1))
-               {
-                       last_message_time = now;
-                       ERROR ("plugin_dispatch_values: Low water mark "
-                                       "reached. Dropping %.0f%% of metrics.",
-                                       100.0 * p);
-               }
-               pthread_mutex_unlock (&last_message_lock);
-       }
-
-       if (p == 1.0)
-               return (1);
-
-       q = cdrand_d ();
-       if (q > p)
-               return (1);
-       else
-               return (0);
-} /* }}} _Bool check_drop_value */
-
-int plugin_dispatch_values (value_list_t const *vl)
-{
-       int status;
-
-       if (check_drop_value ())
-               return (0);
-
-       status = plugin_write_enqueue (vl);
-       if (status != 0)
-       {
-               char errbuf[1024];
-               ERROR ("plugin_dispatch_values: plugin_write_enqueue failed "
-                               "with status %i (%s).", status,
-                               sstrerror (status, errbuf, sizeof (errbuf)));
-               return (status);
-       }
-
-       return (0);
-}
-
-__attribute__((sentinel))
-int plugin_dispatch_multivalue (value_list_t const *template, /* {{{ */
-               _Bool store_percentage, ...)
-{
-       value_list_t *vl;
-       int failed = 0;
-       gauge_t sum = 0.0;
-       va_list ap;
-
-       assert (template->values_len == 1);
-
-       va_start (ap, store_percentage);
-       while (42)
-       {
-               char const *name;
-               gauge_t value;
-
-               name = va_arg (ap, char const *);
-               if (name == NULL)
-                       break;
-
-               value = va_arg (ap, gauge_t);
-               if (!isnan (value))
-                       sum += value;
-       }
-       va_end (ap);
-
-       vl = plugin_value_list_clone (template);
-       /* plugin_value_list_clone makes sure vl->time is set to non-zero. */
-       if (store_percentage)
-               sstrncpy (vl->type, "percent", sizeof (vl->type));
-
-       va_start (ap, store_percentage);
-       while (42)
-       {
-               char const *name;
-               int status;
-
-               /* Set the type instance. */
-               name = va_arg (ap, char const *);
-               if (name == NULL)
-                       break;
-               sstrncpy (vl->type_instance, name, sizeof (vl->type_instance));
-
-               /* Set the value. */
-               vl->values[0].gauge = va_arg (ap, gauge_t);
-               if (store_percentage)
-                       vl->values[0].gauge *= 100.0 / sum;
-
-               status = plugin_write_enqueue (vl);
-               if (status != 0)
-                       failed++;
-       }
-       va_end (ap);
-
-       plugin_value_list_free (vl);
-       return (failed);
-} /* }}} int plugin_dispatch_multivalue */
-
-int plugin_dispatch_notification (const notification_t *notif)
-{
-       llentry_t *le;
-       /* Possible TODO: Add flap detection here */
-
-       DEBUG ("plugin_dispatch_notification: severity = %i; message = %s; "
-                       "time = %.3f; host = %s;",
-                       notif->severity, notif->message,
-                       CDTIME_T_TO_DOUBLE (notif->time), notif->host);
-
-       /* Nobody cares for notifications */
-       if (list_notification == NULL)
-               return (-1);
-
-       le = llist_head (list_notification);
-       while (le != NULL)
-       {
-               callback_func_t *cf;
-               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);
-               if (status != 0)
-               {
-                       WARNING ("plugin_dispatch_notification: Notification "
-                                       "callback %s returned %i.",
-                                       le->key, status);
-               }
-
-               le = le->next;
-       }
-
-       return (0);
-} /* int plugin_dispatch_notification */
-
-void plugin_log (int level, const char *format, ...)
-{
-       char msg[1024];
-       va_list ap;
-       llentry_t *le;
-
-#if !COLLECT_DEBUG
-       if (level >= LOG_DEBUG)
-               return;
-#endif
-
-       va_start (ap, format);
-       vsnprintf (msg, sizeof (msg), format, ap);
-       msg[sizeof (msg) - 1] = '\0';
-       va_end (ap);
-
-       if (list_log == NULL)
-       {
-               fprintf (stderr, "%s\n", msg);
-               return;
-       }
-
-       le = llist_head (list_log);
-       while (le != NULL)
-       {
-               callback_func_t *cf;
-               plugin_log_cb callback;
-
-               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;
-       }
-} /* void plugin_log */
-
-int parse_log_severity (const char *severity)
-{
-       int log_level = -1;
-
-       if ((0 == strcasecmp (severity, "emerg"))
-                       || (0 == strcasecmp (severity, "alert"))
-                       || (0 == strcasecmp (severity, "crit"))
-                       || (0 == strcasecmp (severity, "err")))
-               log_level = LOG_ERR;
-       else if (0 == strcasecmp (severity, "warning"))
-               log_level = LOG_WARNING;
-       else if (0 == strcasecmp (severity, "notice"))
-               log_level = LOG_NOTICE;
-       else if (0 == strcasecmp (severity, "info"))
-               log_level = LOG_INFO;
-#if COLLECT_DEBUG
-       else if (0 == strcasecmp (severity, "debug"))
-               log_level = LOG_DEBUG;
-#endif /* COLLECT_DEBUG */
-
-       return (log_level);
-} /* int parse_log_severity */
-
-int parse_notif_severity (const char *severity)
-{
-       int notif_severity = -1;
-
-       if (strcasecmp (severity, "FAILURE") == 0)
-               notif_severity = NOTIF_FAILURE;
-       else if (strcmp (severity, "OKAY") == 0)
-               notif_severity = NOTIF_OKAY;
-       else if ((strcmp (severity, "WARNING") == 0)
-                       || (strcmp (severity, "WARN") == 0))
-               notif_severity = NOTIF_WARNING;
-
-       return (notif_severity);
-} /* int parse_notif_severity */
-
-const data_set_t *plugin_get_ds (const char *name)
-{
-       data_set_t *ds;
-
-       if (data_sets == NULL)
-       {
-               ERROR ("plugin_get_ds: No data sets are defined yet.");
-               return (NULL);
-       }
-
-       if (c_avl_get (data_sets, name, (void *) &ds) != 0)
-       {
-               DEBUG ("No such dataset registered: %s", name);
-               return (NULL);
-       }
-
-       return (ds);
-} /* data_set_t *plugin_get_ds */
-
-static int plugin_notification_meta_add (notification_t *n,
-    const char *name,
-    enum notification_meta_type_e type,
-    const void *value)
-{
-  notification_meta_t *meta;
-  notification_meta_t *tail;
-
-  if ((n == NULL) || (name == NULL) || (value == NULL))
-  {
-    ERROR ("plugin_notification_meta_add: A pointer is NULL!");
-    return (-1);
-  }
-
-  meta = (notification_meta_t *) malloc (sizeof (notification_meta_t));
-  if (meta == NULL)
-  {
-    ERROR ("plugin_notification_meta_add: malloc failed.");
-    return (-1);
-  }
-  memset (meta, 0, sizeof (notification_meta_t));
-
-  sstrncpy (meta->name, name, sizeof (meta->name));
-  meta->type = type;
-
-  switch (type)
-  {
-    case NM_TYPE_STRING:
-    {
-      meta->nm_value.nm_string = strdup ((const char *) value);
-      if (meta->nm_value.nm_string == NULL)
-      {
-        ERROR ("plugin_notification_meta_add: strdup failed.");
-        sfree (meta);
-        return (-1);
-      }
-      break;
-    }
-    case NM_TYPE_SIGNED_INT:
-    {
-      meta->nm_value.nm_signed_int = *((int64_t *) value);
-      break;
-    }
-    case NM_TYPE_UNSIGNED_INT:
-    {
-      meta->nm_value.nm_unsigned_int = *((uint64_t *) value);
-      break;
-    }
-    case NM_TYPE_DOUBLE:
-    {
-      meta->nm_value.nm_double = *((double *) value);
-      break;
-    }
-    case NM_TYPE_BOOLEAN:
-    {
-      meta->nm_value.nm_boolean = *((_Bool *) value);
-      break;
-    }
-    default:
-    {
-      ERROR ("plugin_notification_meta_add: Unknown type: %i", type);
-      sfree (meta);
-      return (-1);
-    }
-  } /* switch (type) */
-
-  meta->next = NULL;
-  tail = n->meta;
-  while ((tail != NULL) && (tail->next != NULL))
-    tail = tail->next;
-
-  if (tail == NULL)
-    n->meta = meta;
-  else
-    tail->next = meta;
-
-  return (0);
-} /* int plugin_notification_meta_add */
-
-int plugin_notification_meta_add_string (notification_t *n,
-    const char *name,
-    const char *value)
-{
-  return (plugin_notification_meta_add (n, name, NM_TYPE_STRING, value));
-}
-
-int plugin_notification_meta_add_signed_int (notification_t *n,
-    const char *name,
-    int64_t value)
-{
-  return (plugin_notification_meta_add (n, name, NM_TYPE_SIGNED_INT, &value));
-}
-
-int plugin_notification_meta_add_unsigned_int (notification_t *n,
-    const char *name,
-    uint64_t value)
-{
-  return (plugin_notification_meta_add (n, name, NM_TYPE_UNSIGNED_INT, &value));
-}
-
-int plugin_notification_meta_add_double (notification_t *n,
-    const char *name,
-    double value)
-{
-  return (plugin_notification_meta_add (n, name, NM_TYPE_DOUBLE, &value));
-}
-
-int plugin_notification_meta_add_boolean (notification_t *n,
-    const char *name,
-    _Bool value)
-{
-  return (plugin_notification_meta_add (n, name, NM_TYPE_BOOLEAN, &value));
-}
-
-int plugin_notification_meta_copy (notification_t *dst,
-    const notification_t *src)
-{
-  notification_meta_t *meta;
-
-  assert (dst != NULL);
-  assert (src != NULL);
-  assert (dst != src);
-  assert ((src->meta == NULL) || (src->meta != dst->meta));
-
-  for (meta = src->meta; meta != NULL; meta = meta->next)
-  {
-    if (meta->type == NM_TYPE_STRING)
-      plugin_notification_meta_add_string (dst, meta->name,
-          meta->nm_value.nm_string);
-    else if (meta->type == NM_TYPE_SIGNED_INT)
-      plugin_notification_meta_add_signed_int (dst, meta->name,
-          meta->nm_value.nm_signed_int);
-    else if (meta->type == NM_TYPE_UNSIGNED_INT)
-      plugin_notification_meta_add_unsigned_int (dst, meta->name,
-          meta->nm_value.nm_unsigned_int);
-    else if (meta->type == NM_TYPE_DOUBLE)
-      plugin_notification_meta_add_double (dst, meta->name,
-          meta->nm_value.nm_double);
-    else if (meta->type == NM_TYPE_BOOLEAN)
-      plugin_notification_meta_add_boolean (dst, meta->name,
-          meta->nm_value.nm_boolean);
-  }
-
-  return (0);
-} /* int plugin_notification_meta_copy */
-
-int plugin_notification_meta_free (notification_meta_t *n)
-{
-  notification_meta_t *this;
-  notification_meta_t *next;
-
-  if (n == NULL)
-  {
-    ERROR ("plugin_notification_meta_free: n == NULL!");
-    return (-1);
-  }
-
-  this = n;
-  while (this != NULL)
-  {
-    next = this->next;
-
-    if (this->type == NM_TYPE_STRING)
-    {
-      free ((char *)this->nm_value.nm_string);
-      this->nm_value.nm_string = NULL;
-    }
-    sfree (this);
-
-    this = next;
-  }
-
-  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 : */
diff --git a/src/plugin.h b/src/plugin.h
deleted file mode 100644 (file)
index dfc608e..0000000
+++ /dev/null
@@ -1,437 +0,0 @@
-/**
- * collectd - src/plugin.h
- * Copyright (C) 2005-2014  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>
- *   Sebastian Harl <sh at tokkee.org>
- **/
-
-#ifndef PLUGIN_H
-#define PLUGIN_H
-
-#include "collectd.h"
-#include "configfile.h"
-#include "meta_data.h"
-#include "utils_time.h"
-
-#define PLUGIN_FLAGS_GLOBAL 0x0001
-
-#define DATA_MAX_NAME_LEN 64
-
-#define DS_TYPE_COUNTER  0
-#define DS_TYPE_GAUGE    1
-#define DS_TYPE_DERIVE   2
-#define DS_TYPE_ABSOLUTE 3
-
-#define DS_TYPE_TO_STRING(t) (t == DS_TYPE_COUNTER)     ? "counter"  : \
-                               (t == DS_TYPE_GAUGE)    ? "gauge"    : \
-                               (t == DS_TYPE_DERIVE)   ? "derive"   : \
-                               (t == DS_TYPE_ABSOLUTE) ? "absolute" : \
-                               "unknown"
-
-
-#ifndef LOG_ERR
-# define LOG_ERR 3
-#endif
-#ifndef LOG_WARNING
-# define LOG_WARNING 4
-#endif
-#ifndef LOG_NOTICE
-# define LOG_NOTICE 5
-#endif
-#ifndef LOG_INFO
-# define LOG_INFO 6
-#endif
-#ifndef LOG_DEBUG
-# define LOG_DEBUG 7
-#endif
-
-#define NOTIF_MAX_MSG_LEN 256
-
-#define NOTIF_FAILURE 1
-#define NOTIF_WARNING 2
-#define NOTIF_OKAY    4
-
-#define plugin_interval (plugin_get_ctx().interval)
-
-/*
- * Public data types
- */
-typedef unsigned long long counter_t;
-typedef double gauge_t;
-typedef int64_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 value_list_s
-{
-       value_t *values;
-       int      values_len;
-       cdtime_t time;
-       cdtime_t interval;
-       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];
-       meta_data_t *meta;
-};
-typedef struct value_list_s value_list_t;
-
-#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
-{
-       char   name[DATA_MAX_NAME_LEN];
-       int    type;
-       double min;
-       double max;
-};
-typedef struct data_source_s data_source_t;
-
-struct data_set_s
-{
-       char           type[DATA_MAX_NAME_LEN];
-       int            ds_num;
-       data_source_t *ds;
-};
-typedef struct data_set_s data_set_t;
-
-enum notification_meta_type_e
-{
-       NM_TYPE_STRING,
-       NM_TYPE_SIGNED_INT,
-       NM_TYPE_UNSIGNED_INT,
-       NM_TYPE_DOUBLE,
-       NM_TYPE_BOOLEAN
-};
-
-typedef struct notification_meta_s
-{
-       char name[DATA_MAX_NAME_LEN];
-       enum notification_meta_type_e type;
-       union
-       {
-               const char *nm_string;
-               int64_t nm_signed_int;
-               uint64_t nm_unsigned_int;
-               double nm_double;
-               _Bool nm_boolean;
-       } nm_value;
-       struct notification_meta_s *next;
-} notification_meta_t;
-
-typedef struct notification_s
-{
-       int    severity;
-       cdtime_t time;
-       char   message[NOTIF_MAX_MSG_LEN];
-       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];
-       notification_meta_t *meta;
-} notification_t;
-
-struct user_data_s
-{
-       void *data;
-       void (*free_func) (void *);
-};
-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
- */
-typedef int (*plugin_init_cb) (void);
-typedef int (*plugin_read_cb) (user_data_t *);
-typedef int (*plugin_write_cb) (const data_set_t *, const value_list_t *,
-               user_data_t *);
-typedef int (*plugin_flush_cb) (cdtime_t timeout, const char *identifier,
-               user_data_t *);
-/* "missing" callback. Returns less than zero on failure, zero if other
- * callbacks should be called, greater than zero if no more callbacks should be
- * called. */
-typedef int (*plugin_missing_cb) (const value_list_t *, user_data_t *);
-typedef void (*plugin_log_cb) (int severity, const char *message,
-               user_data_t *);
-typedef int (*plugin_shutdown_cb) (void);
-typedef int (*plugin_notification_cb) (const notification_t *,
-               user_data_t *);
-
-/*
- * NAME
- *  plugin_set_dir
- *
- * DESCRIPTION
- *  Sets the current `plugindir'
- *
- * ARGUMENTS
- *  `dir'       Path to the plugin directory
- *
- * NOTES
- *  If `dir' is NULL the compiled in default `PLUGINDIR' is used.
- */
-void plugin_set_dir (const char *dir);
-
-/*
- * NAME
- *  plugin_load
- *
- * DESCRIPTION
- *  Searches the current `plugindir' (see `plugin_set_dir') for the plugin
- *  named $type and loads it. Afterwards the plugin's `module_register'
- *  function is called, which then calls `plugin_register' to register callback
- *  functions.
- *
- * ARGUMENTS
- *  `name'      Name of the plugin to load.
- *  `flags'     Hints on how to handle this plugin.
- *
- * RETURN VALUE
- *  Returns zero upon success, a value greater than zero if no plugin was found
- *  and a value below zero if an error occurs.
- *
- * NOTES
- *  Re-loading an already loaded module is detected and zero is returned in
- *  this case.
- */
-int plugin_load (const char *name, uint32_t flags);
-
-void plugin_init_all (void);
-void plugin_read_all (void);
-int plugin_read_all_once (void);
-void plugin_shutdown_all (void);
-
-/*
- * NAME
- *  plugin_write
- *
- * DESCRIPTION
- *  Calls the write function of the given plugin with the provided data set and
- *  value list. It differs from `plugin_dispatch_value' in that it does not
- *  update the cache, does not do threshold checking, call the chain subsystem
- *  and so on. It looks up the requested plugin and invokes the function, end
- *  of story.
- *
- * ARGUMENTS
- *  plugin     Name of the plugin. If NULL, the value is sent to all registered
- *             write functions.
- *  ds         Pointer to the data_set_t structure. If NULL, the data set is
- *             looked up according to the `type' member in the `vl' argument.
- *  vl         The actual value to be processed. Must not be NULL.
- *
- * RETURN VALUE
- *  Returns zero upon success or non-zero if an error occurred. If `plugin' is
- *  NULL and more than one plugin is called, an error is only returned if *all*
- *  plugins fail.
- *
- * NOTES
- *  This is the function used by the `write' built-in target. May be used by
- *  other target plugins.
- */
-int plugin_write (const char *plugin,
-    const data_set_t *ds, const value_list_t *vl);
-
-int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier);
-
-/*
- * The `plugin_register_*' functions are used to make `config', `init',
- * `read', `write' and `shutdown' functions known to the plugin
- * infrastructure. Also, the data-formats are made public like this.
- */
-int plugin_register_config (const char *name,
-               int (*callback) (const char *key, const char *val),
-               const char **keys, int keys_num);
-int plugin_register_complex_config (const char *type,
-               int (*callback) (oconfig_item_t *));
-int plugin_register_init (const char *name,
-               plugin_init_cb callback);
-int plugin_register_read (const char *name,
-               int (*callback) (void));
-/* "user_data" will be freed automatically, unless
- * "plugin_register_complex_read" returns an error (non-zero). */
-int plugin_register_complex_read (const char *group, const char *name,
-               plugin_read_cb callback,
-               const struct timespec *interval,
-               user_data_t *user_data);
-int plugin_register_write (const char *name,
-               plugin_write_cb callback, user_data_t *user_data);
-int plugin_register_flush (const char *name,
-               plugin_flush_cb callback, user_data_t *user_data);
-int plugin_register_missing (const char *name,
-               plugin_missing_cb callback, user_data_t *user_data);
-int plugin_register_shutdown (const char *name,
-               plugin_shutdown_cb callback);
-int plugin_register_data_set (const data_set_t *ds);
-int plugin_register_log (const char *name,
-               plugin_log_cb callback, user_data_t *user_data);
-int plugin_register_notification (const char *name,
-               plugin_notification_cb callback, user_data_t *user_data);
-
-int plugin_unregister_config (const char *name);
-int plugin_unregister_complex_config (const char *name);
-int plugin_unregister_init (const char *name);
-int plugin_unregister_read (const char *name);
-int plugin_unregister_read_group (const char *group);
-int plugin_unregister_write (const char *name);
-int plugin_unregister_flush (const char *name);
-int plugin_unregister_missing (const char *name);
-int plugin_unregister_shutdown (const char *name);
-int plugin_unregister_data_set (const char *name);
-int plugin_unregister_log (const char *name);
-int plugin_unregister_notification (const char *name);
-
-
-/*
- * NAME
- *  plugin_dispatch_values
- *
- * DESCRIPTION
- *  This function is called by reading processes with the values they've
- *  aquired. The function fetches the data-set definition (that has been
- *  registered using `plugin_register_data_set') and calls _all_ registered
- *  write-functions.
- *
- * ARGUMENTS
- *  `vl'        Value list of the values that have been read by a `read'
- *              function.
- */
-int plugin_dispatch_values (value_list_t const *vl);
-
-/*
- * NAME
- *  plugin_dispatch_multivalue
- *
- * SYNOPSIS
- *  plugin_dispatch_multivalue (vl, 1,
- *                              "free", 42.0,
- *                              "used", 58.0,
- *                              NULL);
- *
- * DESCRIPTION
- *  Takes a list of type instances and values and dispatches that in a batch,
- *  making sure that all values have the same time stamp. If "store_percentage"
- *  is set to true, the "type" is set to "percent" and a percentage is
- *  calculated and dispatched, rather than the absolute values. Values that are
- *  NaN are dispatched as NaN and will not influence the total.
- *
- *  The variadic arguments is a list of type_instance / gauge pairs, that are
- *  interpreted as type "char const *" and "gauge_t". The last argument must be
- *  a NULL pointer to signal end-of-list.
- *
- * RETURNS
- *  The number of values it failed to dispatch (zero on success).
- */
-__attribute__((sentinel))
-int plugin_dispatch_multivalue (value_list_t const *vl,
-               _Bool store_percentage, ...);
-
-int plugin_dispatch_missing (const value_list_t *vl);
-
-int plugin_dispatch_notification (const notification_t *notif);
-
-void plugin_log (int level, const char *format, ...)
-       __attribute__ ((format(printf,2,3)));
-
-/* These functions return the parsed severity or less than zero on failure. */
-int parse_log_severity (const char *severity);
-int parse_notif_severity (const char *severity);
-
-#define ERROR(...)   plugin_log (LOG_ERR,     __VA_ARGS__)
-#define WARNING(...) plugin_log (LOG_WARNING, __VA_ARGS__)
-#define NOTICE(...)  plugin_log (LOG_NOTICE,  __VA_ARGS__)
-#define INFO(...)    plugin_log (LOG_INFO,    __VA_ARGS__)
-#if COLLECT_DEBUG
-# define DEBUG(...)  plugin_log (LOG_DEBUG,   __VA_ARGS__)
-#else /* COLLECT_DEBUG */
-# define DEBUG(...)  /* noop */
-#endif /* ! COLLECT_DEBUG */
-
-const data_set_t *plugin_get_ds (const char *name);
-
-int plugin_notification_meta_add_string (notification_t *n,
-    const char *name,
-    const char *value);
-int plugin_notification_meta_add_signed_int (notification_t *n,
-    const char *name,
-    int64_t value);
-int plugin_notification_meta_add_unsigned_int (notification_t *n,
-    const char *name,
-    uint64_t value);
-int plugin_notification_meta_add_double (notification_t *n,
-    const char *name,
-    double value);
-int plugin_notification_meta_add_boolean (notification_t *n,
-    const char *name,
-    _Bool value);
-
-int plugin_notification_meta_copy (notification_t *dst,
-    const notification_t *src);
-
-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 5601d29..68f30a5 100644 (file)
 #  endif
 /* #endif KERNEL_LINUX */
 
-#elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
+#elif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
 #  include <kvm.h>
 #  include <sys/param.h>
 #  include <sys/sysctl.h>
 #  include <sys/user.h>
 #  include <sys/proc.h>
-/* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
+/* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD) */
 
 #elif HAVE_PROCINFO_H
 #  include <procinfo.h>
 # include <kstat.h>
 #endif
 
-#ifndef ARG_MAX
-#  define ARG_MAX 4096
+#ifndef CMDLINE_BUFFER_SIZE
+# if defined(ARG_MAX) && (ARG_MAX < 4096)
+#  define CMDLINE_BUFFER_SIZE ARG_MAX
+# else
+#  define CMDLINE_BUFFER_SIZE 4096
+# endif
 #endif
 
 typedef struct procstat_entry_s
@@ -210,9 +214,9 @@ static mach_msg_type_number_t     pset_list_len;
 static long pagesize_g;
 /* #endif KERNEL_LINUX */
 
-#elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
+#elif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
 static int pagesize;
-/* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
+/* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD) */
 
 #elif HAVE_PROCINFO_H
 static  struct procentry64 procentry[MAXPROCENTRY];
@@ -619,9 +623,9 @@ static int ps_init (void)
                        pagesize_g, CONFIG_HZ);
 /* #endif KERNEL_LINUX */
 
-#elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
+#elif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
        pagesize = getpagesize();
-/* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
+/* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD) */
 
 #elif HAVE_PROCINFO_H
        pagesize = getpagesize();
@@ -1686,7 +1690,7 @@ static int ps_read (void)
        DIR           *proc;
        int            pid;
 
-       char cmdline[ARG_MAX];
+       char cmdline[CMDLINE_BUFFER_SIZE];
 
        int        status;
        procstat_t ps;
@@ -1824,7 +1828,7 @@ static int ps_read (void)
                 * filter out threads (duplicate PID entries). */
                if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid))
                {
-                       char cmdline[ARG_MAX] = "";
+                       char cmdline[CMDLINE_BUFFER_SIZE] = "";
                        _Bool have_cmdline = 0;
 
                        proc_ptr = &(procs[i]);
@@ -1921,6 +1925,138 @@ static int ps_read (void)
                ps_submit_proc_list (ps_ptr);
 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
 
+#elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD
+       int running  = 0;
+       int sleeping = 0;
+       int zombies  = 0;
+       int stopped  = 0;
+       int onproc   = 0;
+       int idle     = 0;
+       int dead     = 0;
+
+       kvm_t *kd;
+       char errbuf[1024];
+       struct kinfo_proc *procs;          /* array of processes */
+       struct kinfo_proc *proc_ptr = NULL;
+       int count;                         /* returns number of processes */
+       int i;
+
+       procstat_t *ps_ptr;
+       procstat_entry_t pse;
+
+       ps_list_reset ();
+
+       /* Open the kvm interface, get a descriptor */
+       kd = kvm_open (NULL, NULL, NULL, 0, errbuf);
+       if (kd == NULL)
+       {
+               ERROR ("processes plugin: Cannot open kvm interface: %s",
+                               errbuf);
+               return (0);
+       }
+
+       /* Get the list of processes. */
+       procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count);
+       if (procs == NULL)
+       {
+               ERROR ("processes plugin: Cannot get kvm processes list: %s",
+                               kvm_geterr(kd));
+               kvm_close (kd);
+               return (0);
+       }
+
+       /* Iterate through the processes in kinfo_proc */
+       for (i = 0; i < count; i++)
+       {
+               /* Create only one process list entry per _process_, i.e.
+                * filter out threads (duplicate PID entries). */
+               if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid))
+               {
+                       char cmdline[CMDLINE_BUFFER_SIZE] = "";
+                       _Bool have_cmdline = 0;
+
+                       proc_ptr = &(procs[i]);
+                       /* Don't probe zombie processes  */
+                       if (!P_ZOMBIE(proc_ptr))
+                       {
+                               char **argv;
+                               int argc;
+                               int status;
+
+                               /* retrieve the arguments */
+                               argv = kvm_getargv (kd, proc_ptr, /* nchr = */ 0);
+                               argc = 0;
+                               if ((argv != NULL) && (argv[0] != NULL))
+                               {
+                                       while (argv[argc] != NULL)
+                                               argc++;
+
+                                       status = strjoin (cmdline, sizeof (cmdline), argv, argc, " ");
+                                       if (status < 0)
+                                               WARNING ("processes plugin: Command line did not fit into buffer.");
+                                       else
+                                               have_cmdline = 1;
+                               }
+                       } /* if (process has argument list) */
+
+                       pse.id       = procs[i].p_pid;
+                       pse.age      = 0;
+
+                       pse.num_proc = 1;
+                       pse.num_lwp  = 1; /* XXX: accumulate p_tid values for a single p_pid ? */
+
+                       pse.vmem_rss = procs[i].p_vm_rssize * pagesize;
+                       pse.vmem_data = procs[i].p_vm_dsize * pagesize;
+                       pse.vmem_code = procs[i].p_vm_tsize * pagesize;
+                       pse.stack_size = procs[i].p_vm_ssize * pagesize;
+                       pse.vmem_size = pse.stack_size + pse.vmem_code + pse.vmem_data;
+                       pse.vmem_minflt = 0;
+                       pse.vmem_minflt_counter = procs[i].p_uru_minflt;
+                       pse.vmem_majflt = 0;
+                       pse.vmem_majflt_counter = procs[i].p_uru_majflt;
+
+                       pse.cpu_user = 0;
+                       pse.cpu_system = 0;
+                       pse.cpu_user_counter = procs[i].p_uutime_usec +
+                                               (1000000lu * procs[i].p_uutime_sec);
+                       pse.cpu_system_counter = procs[i].p_ustime_usec +
+                                               (1000000lu * procs[i].p_ustime_sec);
+
+                       /* no I/O data */
+                       pse.io_rchar = -1;
+                       pse.io_wchar = -1;
+                       pse.io_syscr = -1;
+                       pse.io_syscw = -1;
+
+                       ps_list_add (procs[i].p_comm, have_cmdline ? cmdline : NULL, &pse);
+               } /* if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) */
+
+               switch (procs[i].p_stat)
+               {
+                       case SSTOP:     stopped++;      break;
+                       case SSLEEP:    sleeping++;     break;
+                       case SRUN:      running++;      break;
+                       case SIDL:      idle++;         break;
+                       case SONPROC:   onproc++;       break;
+                       case SDEAD:     dead++;         break;
+                       case SZOMB:     zombies++;      break;
+               }
+       }
+
+       kvm_close(kd);
+
+       ps_submit_state ("running",  running);
+       ps_submit_state ("sleeping", sleeping);
+       ps_submit_state ("zombies",  zombies);
+       ps_submit_state ("stopped",  stopped);
+       ps_submit_state ("onproc",   onproc);
+       ps_submit_state ("idle",     idle);
+       ps_submit_state ("dead",     dead);
+
+       for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
+               ps_submit_proc_list (ps_ptr);
+/* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD */
+
 #elif HAVE_PROCINFO_H
        /* AIX */
        int running  = 0;
index 10ac8f0..0fad6fa 100644 (file)
@@ -46,6 +46,19 @@ typedef struct cpy_callback_s {
 
 static char log_doc[] = "This function sends a string to all logging plugins.";
 
+static char get_ds_doc[] = "get_dataset(name) -> definition\n"
+               "\n"
+               "Returns the definition of a dataset specified by name.\n"
+               "\n"
+               "'name' is a string specifying the dataset to query.\n"
+               "'definition' is a list of 4-tuples. Every tuple represents a \n"
+               "    data source within the data set and its 4 values are the \n"
+               "    name, type, min and max value.\n"
+               "    'name' is a string.\n"
+               "    'type' is a string that is equal to either DS_TYPE_COUNTER,\n"
+               "        DS_TYPE_GAUGE, DS_TYPE_DERIVE or DS_TYPE_ABSOLUTE.\n"
+               "    'min' and 'max' are either a float or None.";
+
 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
                "\n"
                "Flushes the cache of another plugin.";
@@ -548,7 +561,39 @@ static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args
        return cpy_string_to_unicode_or_bytes(buf);
 }
 
-static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
+static PyObject *float_or_none(float number) {
+       if (isnan(number)) {
+               Py_RETURN_NONE;
+       }
+       return PyFloat_FromDouble(number);
+}
+
+static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
+       int i;
+       char *name;
+       const data_set_t *ds;
+       PyObject *list, *tuple;
+
+       if (PyArg_ParseTuple(args, "et", NULL, &name) == 0) return NULL;
+       ds = plugin_get_ds(name);
+       PyMem_Free(name);
+       if (ds == NULL) {
+               PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
+               return NULL;
+       }
+       list = PyList_New(ds->ds_num); /* New reference. */
+       for (i = 0; i < ds->ds_num; ++i) {
+               tuple = PyTuple_New(4);
+               PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
+               PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
+               PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
+               PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
+               PyList_SET_ITEM(list, i, tuple);
+       }
+       return list;
+}
+
+static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
        int timeout = -1;
        char *plugin = NULL, *identifier = NULL;
        static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
@@ -817,6 +862,7 @@ static PyMethodDef cpy_methods[] = {
        {"notice", cpy_notice, METH_VARARGS, log_doc},
        {"warning", cpy_warning, METH_VARARGS, log_doc},
        {"error", cpy_error, METH_VARARGS, log_doc},
+       {"get_dataset", (PyCFunction) cpy_get_dataset, METH_VARARGS, get_ds_doc},
        {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
        {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
        {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
@@ -1041,6 +1087,10 @@ static int cpy_init_python() {
        PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
        PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
        PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
+       PyModule_AddStringConstant(module, "DS_TYPE_COUNTER", DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
+       PyModule_AddStringConstant(module, "DS_TYPE_GAUGE", DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
+       PyModule_AddStringConstant(module, "DS_TYPE_DERIVE", DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
+       PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE", DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
        return 0;
 }
 
@@ -1103,8 +1153,8 @@ static int cpy_config(oconfig_item_t *ci) {
                                cpy_log_exception("python initialization");
                                continue;
                        }
-                       if (PyList_Append(sys_path, dir_object) != 0) {
-                               ERROR("python plugin: Unable to append \"%s\" to "
+                       if (PyList_Insert(sys_path, 0, dir_object) != 0) {
+                               ERROR("python plugin: Unable to prepend \"%s\" to "
                                      "python module path.", dir);
                                cpy_log_exception("python initialization");
                        }
index 4a658d0..4f5c4ce 100644 (file)
@@ -419,7 +419,7 @@ static meta_data_t *cpy_build_meta(PyObject *meta) {
        meta_data_t *m = NULL;
        PyObject *l;
        
-       if (!meta)
+       if ((meta == NULL) || (meta == Py_None))
                return NULL;
 
        l = PyDict_Items(meta); /* New reference. */
@@ -427,8 +427,12 @@ static meta_data_t *cpy_build_meta(PyObject *meta) {
                cpy_log_exception("building meta data");
                return NULL;
        }
-       m = meta_data_create();
+
        s = PyList_Size(l);
+       if (s < 0)
+               return NULL;
+
+       m = meta_data_create();
        for (i = 0; i < s; ++i) {
                const char *string, *keystring;
                PyObject *key, *value, *item, *tmp;
diff --git a/src/smart.c b/src/smart.c
new file mode 100644 (file)
index 0000000..3b113bd
--- /dev/null
@@ -0,0 +1,269 @@
+/**
+ * collectd - src/smart.c
+ * Copyright (C) 2014       Vincent Bernat
+ *
+ * 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:
+ *   Vincent Bernat <vbe at exoscale.ch>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_ignorelist.h"
+
+#include <atasmart.h>
+#include <libudev.h>
+
+static const char *config_keys[] =
+{
+  "Disk",
+  "IgnoreSelected"
+};
+
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *ignorelist = NULL;
+
+static int smart_config (const char *key, const char *value)
+{
+  if (ignorelist == NULL)
+    ignorelist = ignorelist_create (/* invert = */ 1);
+  if (ignorelist == NULL)
+    return (1);
+
+  if (strcasecmp ("Disk", key) == 0)
+  {
+    ignorelist_add (ignorelist, value);
+  }
+  else if (strcasecmp ("IgnoreSelected", key) == 0)
+  {
+    int invert = 1;
+    if (IS_TRUE (value))
+      invert = 0;
+    ignorelist_set_invert (ignorelist, invert);
+  }
+  else
+  {
+    return (-1);
+  }
+
+  return (0);
+} /* int smart_config */
+
+static void smart_submit (const char *dev, char *type, char *type_inst, double value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "smart", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+static void smart_handle_disk_attribute(SkDisk *d, const SkSmartAttributeParsedData *a,
+                                        void* userdata)
+{
+  const char *dev = userdata;
+  value_t values[4];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  if (!a->current_value_valid || !a->worst_value_valid) return;
+  values[0].gauge = a->current_value;
+  values[1].gauge = a->worst_value;
+  values[2].gauge = a->threshold_valid?a->threshold:0;
+  values[3].gauge = a->pretty_value;
+
+  vl.values = values;
+  vl.values_len = 4;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "smart", sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance));
+  sstrncpy (vl.type, "smart_attribute", sizeof (vl.type));
+  sstrncpy (vl.type_instance, a->name, sizeof (vl.type_instance));
+
+  plugin_dispatch_values (&vl);
+
+  if (a->threshold_valid && a->current_value <= a->threshold)
+  {
+    notification_t notif = { NOTIF_WARNING,
+                             cdtime (),
+                             "",
+                             "",
+                             "smart", "",
+                             "smart_attribute",
+                             "",
+                             NULL };
+    sstrncpy (notif.host, hostname_g, sizeof (notif.host));
+    sstrncpy (notif.plugin_instance, dev, sizeof (notif.plugin_instance));
+    sstrncpy (notif.type_instance, a->name, sizeof (notif.type_instance));
+    ssnprintf (notif.message, sizeof (notif.message),
+               "attribute %s is below allowed threshold (%d < %d)",
+               a->name, a->current_value, a->threshold);
+    plugin_dispatch_notification (&notif);
+  }
+}
+
+static void smart_handle_disk (const char *dev)
+{
+  SkDisk *d = NULL;
+  SkBool awake = FALSE;
+  SkBool available = FALSE;
+  const char *shortname;
+  const SkSmartParsedData *spd;
+  uint64_t poweron, powercycles, badsectors, temperature;
+
+  shortname = strrchr(dev, '/');
+  if (!shortname) return;
+  shortname++;
+  if (ignorelist_match (ignorelist, shortname) != 0) {
+    DEBUG ("smart plugin: ignoring %s.", dev);
+    return;
+  }
+
+  DEBUG ("smart plugin: checking SMART status of %s.",
+         dev);
+
+  if (sk_disk_open (dev, &d) < 0)
+  {
+    ERROR ("smart plugin: unable to open %s.", dev);
+    return;
+  }
+  if (sk_disk_identify_is_available (d, &available) < 0 || !available)
+  {
+    DEBUG ("smart plugin: disk %s cannot be identified.", dev);
+    goto end;
+  }
+  if (sk_disk_smart_is_available (d, &available) < 0 || !available)
+  {
+    DEBUG ("smart plugin: disk %s has no SMART support.", dev);
+    goto end;
+  }
+  if (sk_disk_check_sleep_mode (d, &awake) < 0 || !awake)
+  {
+    DEBUG ("smart plugin: disk %s is sleeping.", dev);
+    goto end;
+  }
+  if (sk_disk_smart_read_data (d) < 0)
+  {
+    ERROR ("smart plugin: unable to get SMART data for disk %s.", dev);
+    goto end;
+  }
+  if (sk_disk_smart_parse (d, &spd) < 0)
+  {
+    ERROR ("smart plugin: unable to parse SMART data for disk %s.", dev);
+    goto end;
+  }
+
+  /* Get some specific values */
+  if (sk_disk_smart_get_power_on (d, &poweron) < 0)
+  {
+    WARNING ("smart plugin: unable to get milliseconds since power on for %s.",
+             dev);
+  }
+  else
+    smart_submit (shortname, "smart_poweron", "", poweron / 1000.);
+
+  if (sk_disk_smart_get_power_cycle (d, &powercycles) < 0)
+  {
+    WARNING ("smart plugin: unable to get number of power cycles for %s.",
+             dev);
+  }
+  else
+    smart_submit (shortname, "smart_powercycles", "", powercycles);
+
+  if (sk_disk_smart_get_bad (d, &badsectors) < 0)
+  {
+    WARNING ("smart plugin: unable to get number of bad sectors for %s.",
+             dev);
+  }
+  else
+    smart_submit (shortname, "smart_badsectors", "", badsectors);
+
+  if (sk_disk_smart_get_temperature (d, &temperature) < 0)
+  {
+    WARNING ("smart plugin: unable to get temperature for %s.",
+             dev);
+  }
+  else
+    smart_submit (shortname, "smart_temperature", "", temperature / 1000. - 273.15);
+
+  /* Grab all attributes */
+  if (sk_disk_smart_parse_attributes(d, smart_handle_disk_attribute,
+                                     (char *)shortname) < 0)
+  {
+    ERROR ("smart plugin: unable to handle SMART attributes for %s.",
+           dev);
+  }
+
+end:
+  sk_disk_free(d);
+}
+
+static int smart_read (void)
+{
+  struct udev *handle_udev;
+  struct udev_enumerate *enumerate;
+  struct udev_list_entry *devices, *dev_list_entry;
+  struct udev_device *dev;
+
+  /* Use udev to get a list of disks */
+  handle_udev = udev_new();
+  if (!handle_udev)
+  {
+    ERROR ("smart plugin: unable to initialize udev.");
+    return (-1);
+  }
+  enumerate = udev_enumerate_new (handle_udev);
+  udev_enumerate_add_match_subsystem (enumerate, "block");
+  udev_enumerate_add_match_property (enumerate, "DEVTYPE", "disk");
+  udev_enumerate_scan_devices (enumerate);
+  devices = udev_enumerate_get_list_entry (enumerate);
+  udev_list_entry_foreach (dev_list_entry, devices)
+  {
+    const char *path, *devpath;
+    path = udev_list_entry_get_name (dev_list_entry);
+    dev = udev_device_new_from_syspath (handle_udev, path);
+    devpath = udev_device_get_devnode (dev);
+
+    /* Query status with libatasmart */
+    smart_handle_disk (devpath);
+  }
+
+  udev_enumerate_unref (enumerate);
+  udev_unref (handle_udev);
+
+  return (0);
+} /* int smart_read */
+
+void module_register (void)
+{
+  plugin_register_config ("smart", smart_config,
+                          config_keys, config_keys_num);
+  plugin_register_read ("smart", smart_read);
+} /* void module_register */
index 508f9d5..0642afa 100644 (file)
@@ -295,11 +295,10 @@ static int swap_read_combined (void) /* {{{ */
        FILE *fh;
        char buffer[1024];
 
-       uint8_t have_data = 0;
-       gauge_t swap_used   = 0.0;
-       gauge_t swap_cached = 0.0;
-       gauge_t swap_free   = 0.0;
-       gauge_t swap_total  = 0.0;
+       gauge_t swap_used   = NAN;
+       gauge_t swap_cached = NAN;
+       gauge_t swap_free   = NAN;
+       gauge_t swap_total  = NAN;
 
        fh = fopen ("/proc/meminfo", "r");
        if (fh == NULL)
@@ -320,35 +319,30 @@ static int swap_read_combined (void) /* {{{ */
                        continue;
 
                if (strcasecmp (fields[0], "SwapTotal:") == 0)
-               {
-                       swap_total = strtod (fields[1], /* endptr = */ NULL);
-                       have_data |= 0x01;
-               }
+                       strtogauge (fields[1], &swap_total);
                else if (strcasecmp (fields[0], "SwapFree:") == 0)
-               {
-                       swap_free = strtod (fields[1], /* endptr = */ NULL);
-                       have_data |= 0x02;
-               }
+                       strtogauge (fields[1], &swap_free);
                else if (strcasecmp (fields[0], "SwapCached:") == 0)
-               {
-                       swap_cached = strtod (fields[1], /* endptr = */ NULL);
-                       have_data |= 0x04;
-               }
+                       strtogauge (fields[1], &swap_cached);
        }
 
        fclose (fh);
 
-       if (have_data != 0x07)
+       if (isnan (swap_total) || isnan (swap_free))
                return (ENOENT);
 
-       if (isnan (swap_total)
-                       || (swap_total <= 0.0)
-                       || ((swap_free + swap_cached) > swap_total))
-               return (EINVAL);
+       /* Some systems, OpenVZ for example, don't provide SwapCached. */
+       if (isnan (swap_cached))
+               swap_used = swap_total - swap_free;
+       else
+               swap_used = swap_total - (swap_free + swap_cached);
+       assert (!isnan (swap_used));
 
-       swap_used = swap_total - (swap_free + swap_cached);
+       if (swap_used < 0.0)
+               return (EINVAL);
 
-       swap_submit_usage (NULL, swap_used, swap_free, "cached", swap_cached);
+       swap_submit_usage (NULL, swap_used, swap_free,
+                       isnan (swap_cached) ? NULL : "cached", swap_cached);
        return (0);
 } /* }}} int swap_read_combined */
 
index 41133e3..daeaf8b 100644 (file)
@@ -38,57 +38,25 @@ struct ts_data_s
 };
 typedef struct ts_data_s ts_data_t;
 
-static char *ts_strdup (const char *orig) /* {{{ */
-{
-  size_t sz;
-  char *dest;
-
-  if (orig == NULL)
-    return (NULL);
-
-  sz = strlen (orig) + 1;
-  dest = (char *) malloc (sz);
-  if (dest == NULL)
-    return (NULL);
-
-  memcpy (dest, orig, sz);
-
-  return (dest);
-} /* }}} char *ts_strdup */
-
 static int ts_config_add_string (char **dest, /* {{{ */
     const oconfig_item_t *ci, int may_be_empty)
 {
-  char *temp;
-
-  if (dest == NULL)
-    return (-EINVAL);
+  char *tmp = NULL;
+  int status;
 
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    ERROR ("Target `set': The `%s' option requires exactly one string "
-        "argument.", ci->key);
-    return (-1);
-  }
+  status = cf_util_get_string (ci, &tmp);
+  if (status != 0)
+    return (status);
 
-  if ((!may_be_empty) && (ci->values[0].value.string[0] == 0))
+  if (!may_be_empty && (strlen (tmp) == 0))
   {
     ERROR ("Target `set': The `%s' option does not accept empty strings.",
         ci->key);
+    sfree (tmp);
     return (-1);
   }
 
-  temp = ts_strdup (ci->values[0].value.string);
-  if (temp == NULL)
-  {
-    ERROR ("ts_config_add_string: ts_strdup failed.");
-    return (-1);
-  }
-
-  free (*dest);
-  *dest = temp;
-
+  *dest = tmp;
   return (0);
 } /* }}} int ts_config_add_string */
 
index 5a04231..f716a10 100644 (file)
@@ -272,12 +272,15 @@ static const char *config_keys[] =
 {
   "ListeningPorts",
   "LocalPort",
-  "RemotePort"
+  "RemotePort",
+  "AllPortsSummary"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
 static int port_collect_listening = 0;
+static int port_collect_total = 0;
 static port_entry_t *port_list_head = NULL;
+static uint32_t count_total[TCP_STATE_MAX + 1];
 
 #if KERNEL_LINUX
 #if HAVE_STRUCT_LINUX_INET_DIAG_REQ
@@ -295,17 +298,22 @@ enum
 } linux_source = SRC_DUNNO;
 #endif
 
+static void conn_prepare_vl (value_list_t *vl, value_t *values)
+{
+  vl->values = values;
+  vl->values_len = 1;
+  sstrncpy (vl->host, hostname_g, sizeof (vl->host));
+  sstrncpy (vl->plugin, "tcpconns", sizeof (vl->plugin));
+  sstrncpy (vl->type, "tcp_connections", sizeof (vl->type));
+}
+
 static void conn_submit_port_entry (port_entry_t *pe)
 {
   value_t values[1];
   value_list_t vl = VALUE_LIST_INIT;
   int i;
 
-  vl.values = values;
-  vl.values_len = 1;
-  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-  sstrncpy (vl.plugin, "tcpconns", sizeof (vl.plugin));
-  sstrncpy (vl.type, "tcp_connections", sizeof (vl.type));
+  conn_prepare_vl (&vl, values);
 
   if (((port_collect_listening != 0) && (pe->flags & PORT_IS_LISTENING))
       || (pe->flags & PORT_COLLECT_LOCAL))
@@ -339,10 +347,33 @@ static void conn_submit_port_entry (port_entry_t *pe)
   }
 } /* void conn_submit */
 
+static void conn_submit_port_total (void)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+  int i;
+
+  conn_prepare_vl (&vl, values);
+
+  sstrncpy (vl.plugin_instance, "all", sizeof (vl.plugin_instance));
+
+  for (i = 1; i <= TCP_STATE_MAX; i++)
+  {
+    vl.values[0].gauge = count_total[i];
+
+    sstrncpy (vl.type_instance, tcp_state[i], sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+  }
+}
+
 static void conn_submit_all (void)
 {
   port_entry_t *pe;
 
+  if (port_collect_total)
+    conn_submit_port_total ();
+
   for (pe = port_list_head; pe != NULL; pe = pe->next)
     conn_submit_port_entry (pe);
 } /* void conn_submit_all */
@@ -381,6 +412,8 @@ static void conn_reset_port_entry (void)
   port_entry_t *prev = NULL;
   port_entry_t *pe = port_list_head;
 
+  memset (&count_total, '\0', sizeof(count_total));
+
   while (pe != NULL)
   {
     /* If this entry was created while reading the files (ant not when handling
@@ -428,6 +461,8 @@ static int conn_handle_ports (uint16_t port_local, uint16_t port_remote, uint8_t
     return (-1);
   }
 
+  count_total[state]++;
+
   /* Listening sockets */
   if ((state == TCP_STATE_LISTEN) && (port_collect_listening != 0))
   {
@@ -704,6 +739,13 @@ static int conn_config (const char *key, const char *value)
       else
        pe->flags |= PORT_COLLECT_REMOTE;
   }
+  else if (strcasecmp (key, "AllPortsSummary") == 0)
+  {
+    if (IS_TRUE (value))
+      port_collect_total = 1;
+    else
+      port_collect_total = 0;
+  }
   else
   {
     return (-1);
@@ -715,7 +757,7 @@ static int conn_config (const char *key, const char *value)
 #if KERNEL_LINUX
 static int conn_init (void)
 {
-  if (port_list_head == NULL)
+  if (port_collect_total == 0 && port_list_head == NULL)
     port_collect_listening = 1;
 
   return (0);
@@ -909,7 +951,9 @@ static int conn_init (void)
 static int conn_read (void)
 {
   struct inpcbtable table;
+#ifdef __OpenBSD__ || __NetBSD_Version__ > 699002700
   struct inpcb *head;
+#endif
   struct inpcb *next;
   struct inpcb inpcb;
   struct tcpcb tcpcb;
@@ -922,18 +966,30 @@ static int conn_read (void)
   if (status != 0)
     return (-1);
 
+#ifdef __OpenBSD__ || __NetBSD_Version__ > 699002700
+  /* inpt_queue is a TAILQ on OpenBSD */
+  /* Get the first pcb */
+  next = (struct inpcb *)TAILQ_FIRST (&table.inpt_queue);
+  while (next)
+#else
   /* Get the `head' pcb */
   head = (struct inpcb *) &(inpcbtable_ptr->inpt_queue);
   /* Get the first pcb */
   next = (struct inpcb *)CIRCLEQ_FIRST (&table.inpt_queue);
 
   while (next != head)
+#endif
   {
     /* Read the pcb pointed to by `next' into `inpcb' */
     kread ((u_long) next, &inpcb, sizeof (inpcb));
 
     /* Advance `next' */
+#ifdef __OpenBSD__ || __NetBSD_Version__ > 699002700
+    /* inpt_queue is a TAILQ on OpenBSD */
+    next = (struct inpcb *)TAILQ_NEXT (&inpcb, inp_queue);
+#else
     next = (struct inpcb *)CIRCLEQ_NEXT (&inpcb, inp_queue);
+#endif
 
     /* Ignore sockets, that are not connected. */
 #ifdef __NetBSD__
index 678a341..7fa621b 100644 (file)
@@ -23,7 +23,6 @@
 #include "plugin.h"
 #include "common.h"
 #include "utils_cache.h"
-#include "utils_parse_option.h"
 
 #include <tcrdb.h>
 
index fcdea23..ec34bd4 100644 (file)
@@ -122,6 +122,7 @@ node_stat           value:DERIVE:0:U
 node_tx_rate           value:GAUGE:0:127
 objects                        value:GAUGE:0:U
 operations             value:DERIVE:0:U
+pending_operations     value:GAUGE:0:U
 percent                        value:GAUGE:0:100.1
 percent_bytes          value:GAUGE:0:100.1
 percent_inodes         value:GAUGE:0:100.1
@@ -167,6 +168,11 @@ serial_octets              rx:DERIVE:0:U, tx:DERIVE:0:U
 signal_noise           value:GAUGE:U:0
 signal_power           value:GAUGE:U:0
 signal_quality         value:GAUGE:0:U
+smart_poweron          value:GAUGE:0:U
+smart_powercycles      value:GAUGE:0:U
+smart_badsectors       value:GAUGE:0:U
+smart_temperature      value:GAUGE:-300:300
+smart_attribute         current:GAUGE:0:255, worst:GAUGE:0:255, threshold:GAUGE:0:255, pretty:GAUGE:0:U
 snr                    value:GAUGE:0:U
 spam_check             value:GAUGE:0:U
 spam_score             value:GAUGE:U:U
diff --git a/src/types_list.c b/src/types_list.c
deleted file mode 100644 (file)
index b3cb8cf..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/**
- * collectd - src/types_list.c
- * Copyright (C) 2007       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 "collectd.h"
-#include "common.h"
-
-#include "plugin.h"
-#include "configfile.h"
-
-static int parse_ds (data_source_t *dsrc, char *buf, size_t buf_len)
-{
-  char *dummy;
-  char *saveptr;
-  char *fields[8];
-  int   fields_num;
-
-  if (buf_len < 11)
-  {
-    ERROR ("parse_ds: (buf_len = %zu) < 11", buf_len);
-    return (-1);
-  }
-
-  if (buf[buf_len - 1] == ',')
-  {
-    buf_len--;
-    buf[buf_len] = '\0';
-  }
-
-  dummy = buf;
-  saveptr = NULL;
-
-  fields_num = 0;
-  while (fields_num < 8)
-  {
-    if ((fields[fields_num] = strtok_r (dummy, ":", &saveptr)) == NULL)
-      break;
-    dummy = NULL;
-    fields_num++;
-  }
-
-  if (fields_num != 4)
-  {
-    ERROR ("parse_ds: (fields_num = %i) != 4", fields_num);
-    return (-1);
-  }
-
-  sstrncpy (dsrc->name, fields[0], sizeof (dsrc->name));
-
-  if (strcasecmp (fields[1], "GAUGE") == 0)
-    dsrc->type = DS_TYPE_GAUGE;
-  else if (strcasecmp (fields[1], "COUNTER") == 0)
-    dsrc->type = DS_TYPE_COUNTER;
-  else if (strcasecmp (fields[1], "DERIVE") == 0)
-    dsrc->type = DS_TYPE_DERIVE;
-  else if (strcasecmp (fields[1], "ABSOLUTE") == 0)
-    dsrc->type = DS_TYPE_ABSOLUTE;
-  else
-  {
-    ERROR ("(fields[1] = %s) != (GAUGE || COUNTER || DERIVE || ABSOLUTE)", fields[1]);
-    return (-1);
-  }
-
-  if (strcasecmp (fields[2], "U") == 0)
-    dsrc->min = NAN;
-  else
-    dsrc->min = atof (fields[2]);
-
-  if (strcasecmp (fields[3], "U") == 0)
-    dsrc->max = NAN;
-  else
-    dsrc->max = atof (fields[3]);
-
-  return (0);
-} /* int parse_ds */
-
-static void parse_line (char *buf)
-{
-  char  *fields[64];
-  size_t fields_num;
-  data_set_t *ds;
-  int i;
-
-  fields_num = strsplit (buf, fields, 64);
-  if (fields_num < 2)
-    return;
-
-  /* Ignore lines which begin with a hash sign. */
-  if (fields[0][0] == '#')
-    return;
-
-  ds = (data_set_t *) malloc (sizeof (data_set_t));
-  if (ds == NULL)
-    return;
-
-  memset (ds, '\0', sizeof (data_set_t));
-
-  sstrncpy (ds->type, fields[0], sizeof (ds->type));
-
-  ds->ds_num = fields_num - 1;
-  ds->ds = (data_source_t *) calloc (ds->ds_num, sizeof (data_source_t));
-  if (ds->ds == NULL)
-    return;
-
-  for (i = 0; i < ds->ds_num; i++)
-    if (parse_ds (ds->ds + i, fields[i + 1], strlen (fields[i + 1])) != 0)
-    {
-      sfree (ds->ds);
-      ERROR ("types_list: parse_line: Cannot parse data source #%i "
-         "of data set %s", i, ds->type);
-      return;
-    }
-
-  plugin_register_data_set (ds);
-
-  sfree (ds->ds);
-  sfree (ds);
-} /* void parse_line */
-
-static void parse_file (FILE *fh)
-{
-  char buf[4096];
-  size_t buf_len;
-
-  while (fgets (buf, sizeof (buf), fh) != NULL)
-  {
-    buf_len = strlen (buf);
-
-    if (buf_len >= 4095)
-    {
-      NOTICE ("Skipping line with more than 4095 characters.");
-      do
-      {
-       if (fgets (buf, sizeof (buf), fh) == NULL)
-         break;
-       buf_len = strlen (buf);
-      } while (buf_len >= 4095);
-      continue;
-    } /* if (buf_len >= 4095) */
-
-    if ((buf_len == 0) || (buf[0] == '#'))
-      continue;
-
-    while ((buf_len > 0) && ((buf[buf_len - 1] == '\n')
-         || (buf[buf_len - 1] == '\n')))
-      buf[--buf_len] = '\0';
-
-    if (buf_len == 0)
-      continue;
-
-    parse_line (buf);
-  } /* while (fgets) */
-} /* void parse_file */
-
-int read_types_list (const char *file)
-{
-  FILE *fh;
-
-  if (file == NULL)
-    return (-1);
-
-  fh = fopen (file, "r");
-  if (fh == NULL)
-  {
-    char errbuf[1024];
-    fprintf (stderr, "Failed to open types database `%s': %s.\n",
-       file, sstrerror (errno, errbuf, sizeof (errbuf)));
-    ERROR ("Failed to open types database `%s': %s",
-       file, sstrerror (errno, errbuf, sizeof (errbuf)));
-    return (-1);
-  }
-
-  parse_file (fh);
-
-  fclose (fh);
-  fh = NULL;
-
-  DEBUG ("Done parsing `%s'", file);
-
-  return (0);
-} /* int read_types_list */
-
-/*
- * vim: shiftwidth=2:softtabstop=2:tabstop=8
- */
diff --git a/src/types_list.h b/src/types_list.h
deleted file mode 100644 (file)
index f375a2f..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * collectd - src/types_list.h
- * Copyright (C) 2007       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 TYPES_LIST_H
-#define TYPES_LIST_H 1
-
-int read_types_list (const char *file);
-
-#endif /* TYPES_LIST_H */
index 781e778..40ffece 100644 (file)
@@ -102,11 +102,21 @@ static int users_read (void)
 #elif HAVE_LIBSTATGRAB
        sg_user_stats *us;
 
+# if HAVE_LIBSTATGRAB_GET_USER_STATS_ARG
+       size_t num_entries;
+       us = sg_get_user_stats (&num_entries);
+# else
        us = sg_get_user_stats ();
+# endif
        if (us == NULL)
                return (-1);   
 
-       users_submit ((gauge_t) us->num_entries);
+       users_submit ((gauge_t)
+# if HAVE_LIBSTATGRAB_GET_USER_STATS_ARG
+                     num_entries);
+# else
+                     us->num_entries);
+# endif
 /* #endif HAVE_LIBSTATGRAB */
 
 #else
diff --git a/src/utils_avltree.c b/src/utils_avltree.c
deleted file mode 100644 (file)
index 04e5403..0000000
+++ /dev/null
@@ -1,730 +0,0 @@
-/**
- * collectd - src/utils_avltree.c
- * Copyright (C) 2006,2007  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 <stdio.h>
-#include <string.h>
-#include <assert.h>
-
-#include "utils_avltree.h"
-
-#define BALANCE(n) ((((n)->left == NULL) ? 0 : (n)->left->height) \
-               - (((n)->right == NULL) ? 0 : (n)->right->height))
-
-/*
- * private data types
- */
-struct c_avl_node_s
-{
-       void *key;
-       void *value;
-
-       int height;
-       struct c_avl_node_s *left;
-       struct c_avl_node_s *right;
-       struct c_avl_node_s *parent;
-};
-typedef struct c_avl_node_s c_avl_node_t;
-
-struct c_avl_tree_s
-{
-       c_avl_node_t *root;
-       int (*compare) (const void *, const void *);
-       int size;
-};
-
-struct c_avl_iterator_s
-{
-       c_avl_tree_t *tree;
-       c_avl_node_t *node;
-};
-
-/*
- * private functions
- */
-#if 0
-static void verify_tree (c_avl_node_t *n)
-{
-       if (n == NULL)
-               return;
-
-       verify_tree (n->left);
-       verify_tree (n->right);
-
-       assert ((BALANCE (n) >= -1) && (BALANCE (n) <= 1));
-       assert ((n->parent == NULL) || (n->parent->right == n) || (n->parent->left == n));
-} /* void verify_tree */
-#else
-# define verify_tree(n) /**/
-#endif
-
-static void free_node (c_avl_node_t *n)
-{
-       if (n == NULL)
-               return;
-
-       if (n->left != NULL)
-               free_node (n->left);
-       if (n->right != NULL)
-               free_node (n->right);
-
-       free (n);
-}
-
-static int calc_height (c_avl_node_t *n)
-{
-       int height_left;
-       int height_right;
-
-       if (n == NULL)
-               return (0);
-
-       height_left  = (n->left == NULL)  ? 0 : n->left->height;
-       height_right = (n->right == NULL) ? 0 : n->right->height;
-
-       return (((height_left > height_right)
-                               ? height_left
-                               : height_right) + 1);
-} /* int calc_height */
-
-static c_avl_node_t *search (c_avl_tree_t *t, const void *key)
-{
-       c_avl_node_t *n;
-       int cmp;
-
-       n = t->root;
-       while (n != NULL)
-       {
-               cmp = t->compare (key, n->key);
-               if (cmp == 0)
-                       return (n);
-               else if (cmp < 0)
-                       n = n->left;
-               else
-                       n = n->right;
-       }
-
-       return (NULL);
-}
-
-/*         (x)             (y)
- *        /   \           /   \
- *     (y)    /\         /\    (x)
- *    /   \  /_c\  ==>  / a\  /   \
- *   /\   /\           /____\/\   /\
- *  / a\ /_b\               /_b\ /_c\
- * /____\
- */
-static c_avl_node_t *rotate_right (c_avl_tree_t *t, c_avl_node_t *x)
-{
-       c_avl_node_t *p;
-       c_avl_node_t *y;
-       c_avl_node_t *b;
-
-       p = x->parent;
-       y = x->left;
-       b = y->right;
-
-       x->left = b;
-       if (b != NULL)
-               b->parent = x;
-
-       x->parent = y;
-       y->right = x;
-
-       y->parent = p;
-       assert ((p == NULL) || (p->left == x) || (p->right == x));
-       if (p == NULL)
-               t->root = y;
-       else if (p->left == x)
-               p->left = y;
-       else
-               p->right = y;
-
-       x->height = calc_height (x);
-       y->height = calc_height (y);
-
-       return (y);
-} /* void rotate_left */
-
-/*
- *    (x)                   (y)
- *   /   \                 /   \
- *  /\    (y)           (x)    /\
- * /_a\  /   \   ==>   /   \  / c\
- *      /\   /\       /\   /\/____\
- *     /_b\ / c\     /_a\ /_b\
- *         /____\
- */
-static c_avl_node_t *rotate_left (c_avl_tree_t *t, c_avl_node_t *x)
-{
-       c_avl_node_t *p;
-       c_avl_node_t *y;
-       c_avl_node_t *b;
-
-       p = x->parent;
-       y = x->right;
-       b = y->left;
-
-       x->right = b;
-       if (b != NULL)
-               b->parent = x;
-
-       x->parent = y;
-       y->left = x;
-
-       y->parent = p;
-       assert ((p == NULL) || (p->left == x) || (p->right == x));
-       if (p == NULL)
-               t->root = y;
-       else if (p->left == x)
-               p->left = y;
-       else
-               p->right = y;
-
-       x->height = calc_height (x);
-       y->height = calc_height (y);
-
-       return (y);
-} /* void rotate_left */
-
-static c_avl_node_t *rotate_left_right (c_avl_tree_t *t, c_avl_node_t *x)
-{
-       rotate_left (t, x->left);
-       return (rotate_right (t, x));
-} /* void rotate_left_right */
-
-static c_avl_node_t *rotate_right_left (c_avl_tree_t *t, c_avl_node_t *x)
-{
-       rotate_right (t, x->right);
-       return (rotate_left (t, x));
-} /* void rotate_right_left */
-
-static void rebalance (c_avl_tree_t *t, c_avl_node_t *n)
-{
-       int b_top;
-       int b_bottom;
-
-       while (n != NULL)
-       {
-               b_top = BALANCE (n);
-               assert ((b_top >= -2) && (b_top <= 2));
-
-               if (b_top == -2)
-               {
-                       assert (n->right != NULL);
-                       b_bottom = BALANCE (n->right);
-                       assert ((b_bottom >= -1) || (b_bottom <= 1));
-                       if (b_bottom == 1)
-                               n = rotate_right_left (t, n);
-                       else
-                               n = rotate_left (t, n);
-               }
-               else if (b_top == 2)
-               {
-                       assert (n->left != NULL);
-                       b_bottom = BALANCE (n->left);
-                       assert ((b_bottom >= -1) || (b_bottom <= 1));
-                       if (b_bottom == -1)
-                               n = rotate_left_right (t, n);
-                       else
-                               n = rotate_right (t, n);
-               }
-               else
-               {
-                       int height = calc_height (n);
-                       if (height == n->height)
-                               break;
-                       n->height = height;
-               }
-
-               assert (n->height == calc_height (n));
-
-               n = n->parent;
-       } /* while (n != NULL) */
-} /* void rebalance */
-
-static c_avl_node_t *c_avl_node_next (c_avl_node_t *n)
-{
-       c_avl_node_t *r; /* return node */
-
-       if (n == NULL)
-       {
-               return (NULL);
-       }
-
-       /* If we can't descent any further, we have to backtrack to the first
-        * parent that's bigger than we, i. e. who's _left_ child we are. */
-       if (n->right == NULL)
-       {
-               r = n->parent;
-               while ((r != NULL) && (r->parent != NULL))
-               {
-                       if (r->left == n)
-                               break;
-                       n = r;
-                       r = n->parent;
-               }
-
-               /* n->right == NULL && r == NULL => t is root and has no next
-                * r->left != n => r->right = n => r->parent == NULL */
-               if ((r == NULL) || (r->left != n))
-               {
-                       assert ((r == NULL) || (r->parent == NULL));
-                       return (NULL);
-               }
-               else
-               {
-                       assert (r->left == n);
-                       return (r);
-               }
-       }
-       else
-       {
-               r = n->right;
-               while (r->left != NULL)
-                       r = r->left;
-       }
-
-       return (r);
-} /* c_avl_node_t *c_avl_node_next */
-
-static c_avl_node_t *c_avl_node_prev (c_avl_node_t *n)
-{
-       c_avl_node_t *r; /* return node */
-
-       if (n == NULL)
-       {
-               return (NULL);
-       }
-
-       /* If we can't descent any further, we have to backtrack to the first
-        * parent that's smaller than we, i. e. who's _right_ child we are. */
-       if (n->left == NULL)
-       {
-               r = n->parent;
-               while ((r != NULL) && (r->parent != NULL))
-               {
-                       if (r->right == n)
-                               break;
-                       n = r;
-                       r = n->parent;
-               }
-
-               /* n->left == NULL && r == NULL => t is root and has no next
-                * r->right != n => r->left = n => r->parent == NULL */
-               if ((r == NULL) || (r->right != n))
-               {
-                       assert ((r == NULL) || (r->parent == NULL));
-                       return (NULL);
-               }
-               else
-               {
-                       assert (r->right == n);
-                       return (r);
-               }
-       }
-       else
-       {
-               r = n->left;
-               while (r->right != NULL)
-                       r = r->right;
-       }
-
-       return (r);
-} /* c_avl_node_t *c_avl_node_prev */
-
-static int _remove (c_avl_tree_t *t, c_avl_node_t *n)
-{
-       assert ((t != NULL) && (n != NULL));
-
-       if ((n->left != NULL) && (n->right != NULL))
-       {
-               c_avl_node_t *r; /* replacement node */
-               if (BALANCE (n) > 0) /* left subtree is higher */
-               {
-                       assert (n->left != NULL);
-                       r = c_avl_node_prev (n);
-                       
-               }
-               else /* right subtree is higher */
-               {
-                       assert (n->right != NULL);
-                       r = c_avl_node_next (n);
-               }
-
-               assert ((r->left == NULL) || (r->right == NULL));
-
-               /* copy content */
-               n->key   = r->key;
-               n->value = r->value;
-
-               n = r;
-       }
-
-       assert ((n->left == NULL) || (n->right == NULL));
-
-       if ((n->left == NULL) && (n->right == NULL))
-       {
-               /* Deleting a leave is easy */
-               if (n->parent == NULL)
-               {
-                       assert (t->root == n);
-                       t->root = NULL;
-               }
-               else
-               {
-                       assert ((n->parent->left == n)
-                                       || (n->parent->right == n));
-                       if (n->parent->left == n)
-                               n->parent->left = NULL;
-                       else
-                               n->parent->right = NULL;
-
-                       rebalance (t, n->parent);
-               }
-
-               free_node (n);
-       }
-       else if (n->left == NULL)
-       {
-               assert (BALANCE (n) == -1);
-               assert ((n->parent == NULL) || (n->parent->left == n) || (n->parent->right == n));
-               if (n->parent == NULL)
-               {
-                       assert (t->root == n);
-                       t->root = n->right;
-               }
-               else if (n->parent->left == n)
-               {
-                       n->parent->left = n->right;
-               }
-               else
-               {
-                       n->parent->right = n->right;
-               }
-               n->right->parent = n->parent;
-
-               if (n->parent != NULL)
-                       rebalance (t, n->parent);
-
-               n->right = NULL;
-               free_node (n);
-       }
-       else if (n->right == NULL)
-       {
-               assert (BALANCE (n) == 1);
-               assert ((n->parent == NULL) || (n->parent->left == n) || (n->parent->right == n));
-               if (n->parent == NULL)
-               {
-                       assert (t->root == n);
-                       t->root = n->left;
-               }
-               else if (n->parent->left == n)
-               {
-                       n->parent->left = n->left;
-               }
-               else
-               {
-                       n->parent->right = n->left;
-               }
-               n->left->parent = n->parent;
-
-               if (n->parent != NULL)
-                       rebalance (t, n->parent);
-
-               n->left = NULL;
-               free_node (n);
-       }
-       else
-       {
-               assert (0);
-       }
-
-       return (0);
-} /* void *_remove */
-
-/*
- * public functions
- */
-c_avl_tree_t *c_avl_create (int (*compare) (const void *, const void *))
-{
-       c_avl_tree_t *t;
-
-       if (compare == NULL)
-               return (NULL);
-
-       if ((t = (c_avl_tree_t *) malloc (sizeof (c_avl_tree_t))) == NULL)
-               return (NULL);
-
-       t->root = NULL;
-       t->compare = compare;
-       t->size = 0;
-
-       return (t);
-}
-
-void c_avl_destroy (c_avl_tree_t *t)
-{
-       if (t == NULL)
-               return;
-       free_node (t->root);
-       free (t);
-}
-
-int c_avl_insert (c_avl_tree_t *t, void *key, void *value)
-{
-       c_avl_node_t *new;
-       c_avl_node_t *nptr;
-       int cmp;
-
-       if ((new = (c_avl_node_t *) malloc (sizeof (c_avl_node_t))) == NULL)
-               return (-1);
-
-       new->key = key;
-       new->value = value;
-       new->height = 1;
-       new->left = NULL;
-       new->right = NULL;
-
-       if (t->root == NULL)
-       {
-               new->parent = NULL;
-               t->root = new;
-               t->size = 1;
-               return (0);
-       }
-
-       nptr = t->root;
-       while (42)
-       {
-               cmp = t->compare (nptr->key, new->key);
-               if (cmp == 0)
-               {
-                       free_node (new);
-                       return (1);
-               }
-               else if (cmp < 0)
-               {
-                       /* nptr < new */
-                       if (nptr->right == NULL)
-                       {
-                               nptr->right = new;
-                               new->parent = nptr;
-                               rebalance (t, nptr);
-                               break;
-                       }
-                       else
-                       {
-                               nptr = nptr->right;
-                       }
-               }
-               else /* if (cmp > 0) */
-               {
-                       /* nptr > new */
-                       if (nptr->left == NULL)
-                       {
-                               nptr->left = new;
-                               new->parent = nptr;
-                               rebalance (t, nptr);
-                               break;
-                       }
-                       else
-                       {
-                               nptr = nptr->left;
-                       }
-               }
-       } /* while (42) */
-
-       verify_tree (t->root);
-       ++t->size;
-       return (0);
-} /* int c_avl_insert */
-
-int c_avl_remove (c_avl_tree_t *t, const void *key, void **rkey, void **rvalue)
-{
-       c_avl_node_t *n;
-       int status;
-
-       assert (t != NULL);
-
-       n = search (t, key);
-       if (n == NULL)
-               return (-1);
-
-       if (rkey != NULL)
-               *rkey = n->key;
-       if (rvalue != NULL)
-               *rvalue = n->value;
-
-       status = _remove (t, n);
-       verify_tree (t->root);
-       --t->size;
-       return (status);
-} /* void *c_avl_remove */
-
-int c_avl_get (c_avl_tree_t *t, const void *key, void **value)
-{
-       c_avl_node_t *n;
-
-       assert (t != NULL);
-
-       n = search (t, key);
-       if (n == NULL)
-               return (-1);
-
-       if (value != NULL)
-               *value = n->value;
-
-       return (0);
-}
-
-int c_avl_pick (c_avl_tree_t *t, void **key, void **value)
-{
-       c_avl_node_t *n;
-       c_avl_node_t *p;
-
-       if ((key == NULL) || (value == NULL))
-               return (-1);
-       if (t->root == NULL)
-               return (-1);
-
-       n = t->root;
-       while ((n->left != NULL) || (n->right != NULL))
-       {
-               int height_left  = (n->left  == NULL) ? 0 : n->left->height;
-               int height_right = (n->right == NULL) ? 0 : n->right->height;
-
-               if (height_left > height_right)
-                       n = n->left;
-               else
-                       n = n->right;
-       }
-
-       p = n->parent;
-       if (p == NULL)
-               t->root = NULL;
-       else if (p->left == n)
-               p->left = NULL;
-       else
-               p->right = NULL;
-
-       *key   = n->key;
-       *value = n->value;
-
-       free_node (n);
-       rebalance (t, p);
-
-       return (0);
-} /* int c_avl_pick */
-
-c_avl_iterator_t *c_avl_get_iterator (c_avl_tree_t *t)
-{
-       c_avl_iterator_t *iter;
-
-       if (t == NULL)
-               return (NULL);
-
-       iter = (c_avl_iterator_t *) malloc (sizeof (c_avl_iterator_t));
-       if (iter == NULL)
-               return (NULL);
-       memset (iter, '\0', sizeof (c_avl_iterator_t));
-       iter->tree = t;
-
-       return (iter);
-} /* c_avl_iterator_t *c_avl_get_iterator */
-
-int c_avl_iterator_next (c_avl_iterator_t *iter, void **key, void **value)
-{
-       c_avl_node_t *n;
-
-       if ((iter == NULL) || (key == NULL) || (value == NULL))
-               return (-1);
-
-       if (iter->node == NULL)
-       {
-               for (n = iter->tree->root; n != NULL; n = n->left)
-                       if (n->left == NULL)
-                               break;
-               iter->node = n;
-       }
-       else
-       {
-               n = c_avl_node_next (iter->node);
-       }
-
-       if (n == NULL)
-               return (-1);
-
-       iter->node = n;
-       *key = n->key;
-       *value = n->value;
-
-       return (0);
-} /* int c_avl_iterator_next */
-
-int c_avl_iterator_prev (c_avl_iterator_t *iter, void **key, void **value)
-{
-       c_avl_node_t *n;
-
-       if ((iter == NULL) || (key == NULL) || (value == NULL))
-               return (-1);
-
-       if (iter->node == NULL)
-       {
-               for (n = iter->tree->root; n != NULL; n = n->left)
-                       if (n->right == NULL)
-                               break;
-               iter->node = n;
-       }
-       else
-       {
-               n = c_avl_node_prev (iter->node);
-       }
-
-       if (n == NULL)
-               return (-1);
-
-       iter->node = n;
-       *key = n->key;
-       *value = n->value;
-
-       return (0);
-} /* int c_avl_iterator_prev */
-
-void c_avl_iterator_destroy (c_avl_iterator_t *iter)
-{
-       free (iter);
-}
-
-int c_avl_size (c_avl_tree_t *t)
-{
-       if (t == NULL)
-               return (0);
-       return (t->size);
-}
diff --git a/src/utils_avltree.h b/src/utils_avltree.h
deleted file mode 100644 (file)
index 1e0f271..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * collectd - src/utils_avltree.h
- * Copyright (C) 2006,2007  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 UTILS_AVLTREE_H
-#define UTILS_AVLTREE_H 1
-
-struct c_avl_tree_s;
-typedef struct c_avl_tree_s c_avl_tree_t;
-
-struct c_avl_iterator_s;
-typedef struct c_avl_iterator_s c_avl_iterator_t;
-
-/*
- * NAME
- *   c_avl_create
- *
- * DESCRIPTION
- *   Allocates a new AVL-tree.
- *
- * PARAMETERS
- *   `compare'  The function-pointer `compare' is used to compare two keys. It
- *              has to return less than zero if it's first argument is smaller
- *              then the second argument, more than zero if the first argument
- *              is bigger than the second argument and zero if they are equal.
- *              If your keys are char-pointers, you can use the `strcmp'
- *              function from the libc here.
- *
- * RETURN VALUE
- *   A c_avl_tree_t-pointer upon success or NULL upon failure.
- */
-c_avl_tree_t *c_avl_create (int (*compare) (const void *, const void *));
-
-
-/*
- * NAME
- *   c_avl_destroy
- *
- * DESCRIPTION
- *   Deallocates an AVL-tree. Stored value- and key-pointer are lost, but of
- *   course not freed.
- */
-void c_avl_destroy (c_avl_tree_t *t);
-
-/*
- * NAME
- *   c_avl_insert
- *
- * DESCRIPTION
- *   Stores the key-value-pair in the AVL-tree pointed to by `t'.
- *
- * PARAMETERS
- *   `t'        AVL-tree to store the data in.
- *   `key'      Key used to store the value under. This is used to get back to
- *              the value again. The pointer is stored in an internal structure
- *              and _not_ copied. So the memory pointed to may _not_ be freed
- *              before this entry is removed. You can use the `rkey' argument
- *              to `avl_remove' to get the original pointer back and free it.
- *   `value'    Value to be stored.
- *
- * RETURN VALUE
- *   Zero upon success, non-zero otherwise. It's less than zero if an error
- *   occurred or greater than zero if the key is already stored in the tree.
- */
-int c_avl_insert (c_avl_tree_t *t, void *key, void *value);
-
-/*
- * NAME
- *   c_avl_remove
- *
- * DESCRIPTION
- *   Removes a key-value-pair from the tree t. The stored key and value may be
- *   returned in `rkey' and `rvalue'.
- *
- * PARAMETERS
- *   `t'       AVL-tree to remove key-value-pair from.
- *   `key'      Key to identify the entry.
- *   `rkey'     Pointer to a pointer in which to store the key. May be NULL.
- *              Since the `key' pointer is not copied when creating an entry,
- *              the pointer may not be available anymore from outside the tree.
- *              You can use this argument to get the actual pointer back and
- *              free the memory pointed to by it.
- *   `rvalue'   Pointer to a pointer in which to store the value. May be NULL.
- *
- * RETURN VALUE
- *   Zero upon success or non-zero if the key isn't found in the tree.
- */
-int c_avl_remove (c_avl_tree_t *t, const void *key, void **rkey, void **rvalue);
-
-/*
- * NAME
- *   c_avl_get
- *
- * DESCRIPTION
- *   Retrieve the `value' belonging to `key'.
- *
- * PARAMETERS
- *   `t'       AVL-tree to get the value from.
- *   `key'      Key to identify the entry.
- *   `value'    Pointer to a pointer in which to store the value. May be NULL.
- *
- * RETURN VALUE
- *   Zero upon success or non-zero if the key isn't found in the tree.
- */
-int c_avl_get (c_avl_tree_t *t, const void *key, void **value);
-
-/*
- * NAME
- *   c_avl_pick
- *
- * DESCRIPTION
- *   Remove a (pseudo-)random element from the tree and return it's `key' and
- *   `value'. Entries are not returned in any particular order. This function
- *   is intended for cache-flushes that don't care about the order but simply
- *   want to remove all elements, one at a time.
- *
- * PARAMETERS
- *   `t'       AVL-tree to get the value from.
- *   `key'      Pointer to a pointer in which to store the key.
- *   `value'    Pointer to a pointer in which to store the value.
- *
- * RETURN VALUE
- *   Zero upon success or non-zero if the tree is empty or key or value is
- *   NULL.
- */
-int c_avl_pick (c_avl_tree_t *t, void **key, void **value);
-
-c_avl_iterator_t *c_avl_get_iterator (c_avl_tree_t *t);
-int c_avl_iterator_next (c_avl_iterator_t *iter, void **key, void **value);
-int c_avl_iterator_prev (c_avl_iterator_t *iter, void **key, void **value);
-void c_avl_iterator_destroy (c_avl_iterator_t *iter);
-
-/*
- * NAME
- *   c_avl_size
- *
- * DESCRIPTION
- *   Return the size (number of nodes) of the specified tree.
- *
- * PARAMETERS
- *   `t'        AVL-tree to get the size of.
- *
- * RETURN VALUE
- *   Number of nodes in the tree, 0 if the tree is empty or NULL.
- */
-int c_avl_size (c_avl_tree_t *t);
-
-#endif /* UTILS_AVLTREE_H */
diff --git a/src/utils_cache.c b/src/utils_cache.c
deleted file mode 100644 (file)
index 9d86781..0000000
+++ /dev/null
@@ -1,978 +0,0 @@
-/**
- * collectd - src/utils_cache.c
- * Copyright (C) 2007-2010  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 "collectd.h"
-#include "common.h"
-#include "plugin.h"
-#include "utils_avltree.h"
-#include "utils_cache.h"
-#include "meta_data.h"
-
-#include <assert.h>
-#include <pthread.h>
-
-typedef struct cache_entry_s
-{
-       char name[6 * DATA_MAX_NAME_LEN];
-       int        values_num;
-       gauge_t   *values_gauge;
-       value_t   *values_raw;
-       /* Time contained in the package
-        * (for calculating rates) */
-       cdtime_t last_time;
-       /* Time according to the local clock
-        * (for purging old entries) */
-       cdtime_t last_update;
-       /* Interval in which the data is collected
-        * (for purding old entries) */
-       cdtime_t interval;
-       int state;
-       int hits;
-
-       /*
-        * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
-        * !  0  !  1  !  2  !  3  !  4  !  5  !  6  !  7  !  8  ! ...
-        * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
-        * ! ds0 ! ds1 ! ds2 ! ds0 ! ds1 ! ds2 ! ds0 ! ds1 ! ds2 ! ...
-        * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
-        * !      t = 0      !      t = 1      !      t = 2      ! ...
-        * +-----------------+-----------------+-----------------+----
-        */
-       gauge_t *history;
-       size_t   history_index; /* points to the next position to write to. */
-       size_t   history_length;
-
-       meta_data_t *meta;
-} cache_entry_t;
-
-static c_avl_tree_t   *cache_tree = NULL;
-static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
-
-static int cache_compare (const cache_entry_t *a, const cache_entry_t *b)
-{
-#if COLLECT_DEBUG
-  assert ((a != NULL) && (b != NULL));
-#endif
-  return (strcmp (a->name, b->name));
-} /* int cache_compare */
-
-static cache_entry_t *cache_alloc (int values_num)
-{
-  cache_entry_t *ce;
-
-  ce = (cache_entry_t *) malloc (sizeof (cache_entry_t));
-  if (ce == NULL)
-  {
-    ERROR ("utils_cache: cache_alloc: malloc failed.");
-    return (NULL);
-  }
-  memset (ce, '\0', sizeof (cache_entry_t));
-  ce->values_num = values_num;
-
-  ce->values_gauge = calloc (values_num, sizeof (*ce->values_gauge));
-  ce->values_raw   = calloc (values_num, sizeof (*ce->values_raw));
-  if ((ce->values_gauge == NULL) || (ce->values_raw == NULL))
-  {
-    sfree (ce->values_gauge);
-    sfree (ce->values_raw);
-    sfree (ce);
-    ERROR ("utils_cache: cache_alloc: calloc failed.");
-    return (NULL);
-  }
-
-  ce->history = NULL;
-  ce->history_length = 0;
-  ce->meta = NULL;
-
-  return (ce);
-} /* cache_entry_t *cache_alloc */
-
-static void cache_free (cache_entry_t *ce)
-{
-  if (ce == NULL)
-    return;
-
-  sfree (ce->values_gauge);
-  sfree (ce->values_raw);
-  sfree (ce->history);
-  if (ce->meta != NULL)
-  {
-    meta_data_destroy (ce->meta);
-    ce->meta = NULL;
-  }
-  sfree (ce);
-} /* void cache_free */
-
-static void uc_check_range (const data_set_t *ds, cache_entry_t *ce)
-{
-  int i;
-
-  for (i = 0; i < ds->ds_num; i++)
-  {
-    if (isnan (ce->values_gauge[i]))
-      continue;
-    else if (ce->values_gauge[i] < ds->ds[i].min)
-      ce->values_gauge[i] = NAN;
-    else if (ce->values_gauge[i] > ds->ds[i].max)
-      ce->values_gauge[i] = NAN;
-  }
-} /* void uc_check_range */
-
-static int uc_insert (const data_set_t *ds, const value_list_t *vl,
-    const char *key)
-{
-  int i;
-  char *key_copy;
-  cache_entry_t *ce;
-
-  /* `cache_lock' has been locked by `uc_update' */
-
-  key_copy = strdup (key);
-  if (key_copy == NULL)
-  {
-    ERROR ("uc_insert: strdup failed.");
-    return (-1);
-  }
-
-  ce = cache_alloc (ds->ds_num);
-  if (ce == NULL)
-  {
-    sfree (key_copy);
-    ERROR ("uc_insert: cache_alloc (%i) failed.", ds->ds_num);
-    return (-1);
-  }
-
-  sstrncpy (ce->name, key, sizeof (ce->name));
-
-  for (i = 0; i < ds->ds_num; i++)
-  {
-    switch (ds->ds[i].type)
-    {
-      case DS_TYPE_COUNTER:
-       ce->values_gauge[i] = NAN;
-       ce->values_raw[i].counter = vl->values[i].counter;
-       break;
-
-      case DS_TYPE_GAUGE:
-       ce->values_gauge[i] = vl->values[i].gauge;
-       ce->values_raw[i].gauge = vl->values[i].gauge;
-       break;
-
-      case DS_TYPE_DERIVE:
-       ce->values_gauge[i] = NAN;
-       ce->values_raw[i].derive = vl->values[i].derive;
-       break;
-
-      case DS_TYPE_ABSOLUTE:
-       ce->values_gauge[i] = NAN;
-       if (vl->interval > 0)
-         ce->values_gauge[i] = ((double) vl->values[i].absolute)
-           / CDTIME_T_TO_DOUBLE (vl->interval);
-       ce->values_raw[i].absolute = vl->values[i].absolute;
-       break;
-       
-      default:
-       /* This shouldn't happen. */
-       ERROR ("uc_insert: Don't know how to handle data source type %i.",
-           ds->ds[i].type);
-       return (-1);
-    } /* switch (ds->ds[i].type) */
-  } /* for (i) */
-
-  /* Prune invalid gauge data */
-  uc_check_range (ds, ce);
-
-  ce->last_time = vl->time;
-  ce->last_update = cdtime ();
-  ce->interval = vl->interval;
-  ce->state = STATE_OKAY;
-
-  if (c_avl_insert (cache_tree, key_copy, ce) != 0)
-  {
-    sfree (key_copy);
-    ERROR ("uc_insert: c_avl_insert failed.");
-    return (-1);
-  }
-
-  DEBUG ("uc_insert: Added %s to the cache.", key);
-  return (0);
-} /* int uc_insert */
-
-int uc_init (void)
-{
-  if (cache_tree == NULL)
-    cache_tree = c_avl_create ((int (*) (const void *, const void *))
-       cache_compare);
-
-  return (0);
-} /* int uc_init */
-
-int uc_check_timeout (void)
-{
-  cdtime_t now;
-  cache_entry_t *ce;
-
-  char **keys = NULL;
-  cdtime_t *keys_time = NULL;
-  cdtime_t *keys_interval = NULL;
-  int keys_len = 0;
-
-  char *key;
-  c_avl_iterator_t *iter;
-
-  int status;
-  int i;
-  
-  pthread_mutex_lock (&cache_lock);
-
-  now = cdtime ();
-
-  /* Build a list of entries to be flushed */
-  iter = c_avl_get_iterator (cache_tree);
-  while (c_avl_iterator_next (iter, (void *) &key, (void *) &ce) == 0)
-  {
-    char **tmp;
-    cdtime_t *tmp_time;
-
-    /* If the entry is fresh enough, continue. */
-    if ((now - ce->last_update) < (ce->interval * timeout_g))
-      continue;
-
-    /* If entry has not been updated, add to `keys' array */
-    tmp = (char **) realloc ((void *) keys,
-       (keys_len + 1) * sizeof (char *));
-    if (tmp == NULL)
-    {
-      ERROR ("uc_check_timeout: realloc failed.");
-      continue;
-    }
-    keys = tmp;
-
-    tmp_time = realloc (keys_time, (keys_len + 1) * sizeof (*keys_time));
-    if (tmp_time == NULL)
-    {
-      ERROR ("uc_check_timeout: realloc failed.");
-      continue;
-    }
-    keys_time = tmp_time;
-
-    tmp_time = realloc (keys_interval, (keys_len + 1) * sizeof (*keys_interval));
-    if (tmp_time == NULL)
-    {
-      ERROR ("uc_check_timeout: realloc failed.");
-      continue;
-    }
-    keys_interval = tmp_time;
-
-    keys[keys_len] = strdup (key);
-    if (keys[keys_len] == NULL)
-    {
-      ERROR ("uc_check_timeout: strdup failed.");
-      continue;
-    }
-    keys_time[keys_len] = ce->last_time;
-    keys_interval[keys_len] = ce->interval;
-
-    keys_len++;
-  } /* while (c_avl_iterator_next) */
-
-  c_avl_iterator_destroy (iter);
-  pthread_mutex_unlock (&cache_lock);
-
-  if (keys_len == 0)
-    return (0);
-
-  /* Call the "missing" callback for each value. Do this before removing the
-   * value from the cache, so that callbacks can still access the data stored,
-   * including plugin specific meta data, rates, history, …. This must be done
-   * without holding the lock, otherwise we will run into a deadlock if a
-   * plugin calls the cache interface. */
-  for (i = 0; i < keys_len; i++)
-  {
-    value_list_t vl = VALUE_LIST_INIT;
-
-    vl.values = NULL;
-    vl.values_len = 0;
-    vl.meta = NULL;
-
-    status = parse_identifier_vl (keys[i], &vl);
-    if (status != 0)
-    {
-      ERROR ("uc_check_timeout: parse_identifier_vl (\"%s\") failed.", keys[i]);
-      cache_free (ce);
-      continue;
-    }
-
-    vl.time = keys_time[i];
-    vl.interval = keys_interval[i];
-
-    plugin_dispatch_missing (&vl);
-  } /* for (i = 0; i < keys_len; i++) */
-
-  /* Now actually remove all the values from the cache. We don't re-evaluate
-   * the timestamp again, so in theory it is possible we remove a value after
-   * it is updated here. */
-  pthread_mutex_lock (&cache_lock);
-  for (i = 0; i < keys_len; i++)
-  {
-    key = NULL;
-    ce = NULL;
-
-    status = c_avl_remove (cache_tree, keys[i],
-       (void *) &key, (void *) &ce);
-    if (status != 0)
-    {
-      ERROR ("uc_check_timeout: c_avl_remove (\"%s\") failed.", keys[i]);
-      sfree (keys[i]);
-      continue;
-    }
-
-    sfree (keys[i]);
-    sfree (key);
-    cache_free (ce);
-  } /* for (i = 0; i < keys_len; i++) */
-  pthread_mutex_unlock (&cache_lock);
-
-  sfree (keys);
-  sfree (keys_time);
-  sfree (keys_interval);
-
-  return (0);
-} /* int uc_check_timeout */
-
-int uc_update (const data_set_t *ds, const value_list_t *vl)
-{
-  char name[6 * DATA_MAX_NAME_LEN];
-  cache_entry_t *ce = NULL;
-  int status;
-  int i;
-
-  if (FORMAT_VL (name, sizeof (name), vl) != 0)
-  {
-    ERROR ("uc_update: FORMAT_VL failed.");
-    return (-1);
-  }
-
-  pthread_mutex_lock (&cache_lock);
-
-  status = c_avl_get (cache_tree, name, (void *) &ce);
-  if (status != 0) /* entry does not yet exist */
-  {
-    status = uc_insert (ds, vl, name);
-    pthread_mutex_unlock (&cache_lock);
-    return (status);
-  }
-
-  assert (ce != NULL);
-  assert (ce->values_num == ds->ds_num);
-
-  if (ce->last_time >= vl->time)
-  {
-    pthread_mutex_unlock (&cache_lock);
-    NOTICE ("uc_update: Value too old: name = %s; value time = %.3f; "
-       "last cache update = %.3f;",
-       name,
-       CDTIME_T_TO_DOUBLE (vl->time),
-       CDTIME_T_TO_DOUBLE (ce->last_time));
-    return (-1);
-  }
-
-  for (i = 0; i < ds->ds_num; i++)
-  {
-    switch (ds->ds[i].type)
-    {
-      case DS_TYPE_COUNTER:
-       {
-         counter_t diff;
-
-         /* check if the counter has wrapped around */
-         if (vl->values[i].counter < ce->values_raw[i].counter)
-         {
-           if (ce->values_raw[i].counter <= 4294967295U)
-             diff = (4294967295U - ce->values_raw[i].counter)
-               + vl->values[i].counter;
-           else
-             diff = (18446744073709551615ULL - ce->values_raw[i].counter)
-               + vl->values[i].counter;
-         }
-         else /* counter has NOT wrapped around */
-         {
-           diff = vl->values[i].counter - ce->values_raw[i].counter;
-         }
-
-         ce->values_gauge[i] = ((double) diff)
-           / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time));
-         ce->values_raw[i].counter = vl->values[i].counter;
-       }
-       break;
-
-      case DS_TYPE_GAUGE:
-       ce->values_raw[i].gauge = vl->values[i].gauge;
-       ce->values_gauge[i] = vl->values[i].gauge;
-       break;
-
-      case DS_TYPE_DERIVE:
-       {
-         derive_t diff;
-
-         diff = vl->values[i].derive - ce->values_raw[i].derive;
-
-         ce->values_gauge[i] = ((double) diff)
-           / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time));
-         ce->values_raw[i].derive = vl->values[i].derive;
-       }
-       break;
-
-      case DS_TYPE_ABSOLUTE:
-       ce->values_gauge[i] = ((double) vl->values[i].absolute)
-         / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time));
-       ce->values_raw[i].absolute = vl->values[i].absolute;
-       break;
-
-      default:
-       /* This shouldn't happen. */
-       pthread_mutex_unlock (&cache_lock);
-       ERROR ("uc_update: Don't know how to handle data source type %i.",
-           ds->ds[i].type);
-       return (-1);
-    } /* switch (ds->ds[i].type) */
-
-    DEBUG ("uc_update: %s: ds[%i] = %lf", name, i, ce->values_gauge[i]);
-  } /* for (i) */
-
-  /* Update the history if it exists. */
-  if (ce->history != NULL)
-  {
-    assert (ce->history_index < ce->history_length);
-    for (i = 0; i < ce->values_num; i++)
-    {
-      size_t hist_idx = (ce->values_num * ce->history_index) + i;
-      ce->history[hist_idx] = ce->values_gauge[i];
-    }
-
-    assert (ce->history_length > 0);
-    ce->history_index = (ce->history_index + 1) % ce->history_length;
-  }
-
-  /* Prune invalid gauge data */
-  uc_check_range (ds, ce);
-
-  ce->last_time = vl->time;
-  ce->last_update = cdtime ();
-  ce->interval = vl->interval;
-
-  pthread_mutex_unlock (&cache_lock);
-
-  return (0);
-} /* int uc_update */
-
-int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_values_num)
-{
-  gauge_t *ret = NULL;
-  size_t ret_num = 0;
-  cache_entry_t *ce = NULL;
-  int status = 0;
-
-  pthread_mutex_lock (&cache_lock);
-
-  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
-  {
-    assert (ce != NULL);
-
-    /* remove missing values from getval */
-    if (ce->state == STATE_MISSING)
-    {
-      status = -1;
-    }
-    else
-    {
-      ret_num = ce->values_num;
-      ret = (gauge_t *) malloc (ret_num * sizeof (gauge_t));
-      if (ret == NULL)
-      {
-        ERROR ("utils_cache: uc_get_rate_by_name: malloc failed.");
-        status = -1;
-      }
-      else
-      {
-        memcpy (ret, ce->values_gauge, ret_num * sizeof (gauge_t));
-      }
-    }
-  }
-  else
-  {
-    DEBUG ("utils_cache: uc_get_rate_by_name: No such value: %s", name);
-    status = -1;
-  }
-
-  pthread_mutex_unlock (&cache_lock);
-
-  if (status == 0)
-  {
-    *ret_values = ret;
-    *ret_values_num = ret_num;
-  }
-
-  return (status);
-} /* gauge_t *uc_get_rate_by_name */
-
-gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl)
-{
-  char name[6 * DATA_MAX_NAME_LEN];
-  gauge_t *ret = NULL;
-  size_t ret_num = 0;
-  int status;
-
-  if (FORMAT_VL (name, sizeof (name), vl) != 0)
-  {
-    ERROR ("utils_cache: uc_get_rate: FORMAT_VL failed.");
-    return (NULL);
-  }
-
-  status = uc_get_rate_by_name (name, &ret, &ret_num);
-  if (status != 0)
-    return (NULL);
-
-  /* This is important - the caller has no other way of knowing how many
-   * values are returned. */
-  if (ret_num != (size_t) ds->ds_num)
-  {
-    ERROR ("utils_cache: uc_get_rate: ds[%s] has %i values, "
-       "but uc_get_rate_by_name returned %zu.",
-       ds->type, ds->ds_num, ret_num);
-    sfree (ret);
-    return (NULL);
-  }
-
-  return (ret);
-} /* gauge_t *uc_get_rate */
-
-int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number)
-{
-  c_avl_iterator_t *iter;
-  char *key;
-  cache_entry_t *value;
-
-  char **names = NULL;
-  cdtime_t *times = NULL;
-  size_t number = 0;
-  size_t size_arrays = 0;
-
-  int status = 0;
-
-  if ((ret_names == NULL) || (ret_number == NULL))
-    return (-1);
-
-  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)
-  {
-    /* remove missing values when list values */
-    if (value->state == STATE_MISSING)
-      continue;
-
-    /* c_avl_size does not return a number smaller than the number of elements
-     * returned by c_avl_iterator_next. */
-    assert (number < size_arrays);
-
-    if (ret_times != NULL)
-      times[number] = value->last_time;
-
-    names[number] = strdup (key);
-    if (names[number] == NULL)
-    {
-      status = -1;
-      break;
-    }
-
-    number++;
-  } /* while (c_avl_iterator_next) */
-
-  c_avl_iterator_destroy (iter);
-  pthread_mutex_unlock (&cache_lock);
-
-  if (status != 0)
-  {
-    size_t i;
-    
-    for (i = 0; i < number; i++)
-    {
-      sfree (names[i]);
-    }
-    sfree (names);
-
-    return (-1);
-  }
-
-  *ret_names = names;
-  if (ret_times != NULL)
-    *ret_times = times;
-  *ret_number = number;
-
-  return (0);
-} /* int uc_get_names */
-
-int uc_get_state (const data_set_t *ds, const value_list_t *vl)
-{
-  char name[6 * DATA_MAX_NAME_LEN];
-  cache_entry_t *ce = NULL;
-  int ret = STATE_ERROR;
-
-  if (FORMAT_VL (name, sizeof (name), vl) != 0)
-  {
-    ERROR ("uc_get_state: FORMAT_VL failed.");
-    return (STATE_ERROR);
-  }
-
-  pthread_mutex_lock (&cache_lock);
-
-  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
-  {
-    assert (ce != NULL);
-    ret = ce->state;
-  }
-
-  pthread_mutex_unlock (&cache_lock);
-
-  return (ret);
-} /* int uc_get_state */
-
-int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state)
-{
-  char name[6 * DATA_MAX_NAME_LEN];
-  cache_entry_t *ce = NULL;
-  int ret = -1;
-
-  if (FORMAT_VL (name, sizeof (name), vl) != 0)
-  {
-    ERROR ("uc_get_state: FORMAT_VL failed.");
-    return (STATE_ERROR);
-  }
-
-  pthread_mutex_lock (&cache_lock);
-
-  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
-  {
-    assert (ce != NULL);
-    ret = ce->state;
-    ce->state = state;
-  }
-
-  pthread_mutex_unlock (&cache_lock);
-
-  return (ret);
-} /* int uc_set_state */
-
-int uc_get_history_by_name (const char *name,
-    gauge_t *ret_history, size_t num_steps, size_t num_ds)
-{
-  cache_entry_t *ce = NULL;
-  size_t i;
-  int status = 0;
-
-  pthread_mutex_lock (&cache_lock);
-
-  status = c_avl_get (cache_tree, name, (void *) &ce);
-  if (status != 0)
-  {
-    pthread_mutex_unlock (&cache_lock);
-    return (-ENOENT);
-  }
-
-  if (((size_t) ce->values_num) != num_ds)
-  {
-    pthread_mutex_unlock (&cache_lock);
-    return (-EINVAL);
-  }
-
-  /* Check if there are enough values available. If not, increase the buffer
-   * size. */
-  if (ce->history_length < num_steps)
-  {
-    gauge_t *tmp;
-    size_t i;
-
-    tmp = realloc (ce->history, sizeof (*ce->history)
-       * num_steps * ce->values_num);
-    if (tmp == NULL)
-    {
-      pthread_mutex_unlock (&cache_lock);
-      return (-ENOMEM);
-    }
-
-    for (i = ce->history_length * ce->values_num;
-       i < (num_steps * ce->values_num);
-       i++)
-      tmp[i] = NAN;
-
-    ce->history = tmp;
-    ce->history_length = num_steps;
-  } /* if (ce->history_length < num_steps) */
-
-  /* Copy the values to the output buffer. */
-  for (i = 0; i < num_steps; i++)
-  {
-    size_t src_index;
-    size_t dst_index;
-
-    if (i < ce->history_index)
-      src_index = ce->history_index - (i + 1);
-    else
-      src_index = ce->history_length + ce->history_index - (i + 1);
-    src_index = src_index * num_ds;
-
-    dst_index = i * num_ds;
-
-    memcpy (ret_history + dst_index, ce->history + src_index,
-       sizeof (*ret_history) * num_ds);
-  }
-
-  pthread_mutex_unlock (&cache_lock);
-
-  return (0);
-} /* int uc_get_history_by_name */
-
-int uc_get_history (const data_set_t *ds, const value_list_t *vl,
-    gauge_t *ret_history, size_t num_steps, size_t num_ds)
-{
-  char name[6 * DATA_MAX_NAME_LEN];
-
-  if (FORMAT_VL (name, sizeof (name), vl) != 0)
-  {
-    ERROR ("utils_cache: uc_get_history: FORMAT_VL failed.");
-    return (-1);
-  }
-
-  return (uc_get_history_by_name (name, ret_history, num_steps, num_ds));
-} /* int uc_get_history */
-
-int uc_get_hits (const data_set_t *ds, const value_list_t *vl)
-{
-  char name[6 * DATA_MAX_NAME_LEN];
-  cache_entry_t *ce = NULL;
-  int ret = STATE_ERROR;
-
-  if (FORMAT_VL (name, sizeof (name), vl) != 0)
-  {
-    ERROR ("uc_get_state: FORMAT_VL failed.");
-    return (STATE_ERROR);
-  }
-
-  pthread_mutex_lock (&cache_lock);
-
-  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
-  {
-    assert (ce != NULL);
-    ret = ce->hits;
-  }
-
-  pthread_mutex_unlock (&cache_lock);
-
-  return (ret);
-} /* int uc_get_hits */
-
-int uc_set_hits (const data_set_t *ds, const value_list_t *vl, int hits)
-{
-  char name[6 * DATA_MAX_NAME_LEN];
-  cache_entry_t *ce = NULL;
-  int ret = -1;
-
-  if (FORMAT_VL (name, sizeof (name), vl) != 0)
-  {
-    ERROR ("uc_get_state: FORMAT_VL failed.");
-    return (STATE_ERROR);
-  }
-
-  pthread_mutex_lock (&cache_lock);
-
-  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
-  {
-    assert (ce != NULL);
-    ret = ce->hits;
-    ce->hits = hits;
-  }
-
-  pthread_mutex_unlock (&cache_lock);
-
-  return (ret);
-} /* int uc_set_hits */
-
-int uc_inc_hits (const data_set_t *ds, const value_list_t *vl, int step)
-{
-  char name[6 * DATA_MAX_NAME_LEN];
-  cache_entry_t *ce = NULL;
-  int ret = -1;
-
-  if (FORMAT_VL (name, sizeof (name), vl) != 0)
-  {
-    ERROR ("uc_get_state: FORMAT_VL failed.");
-    return (STATE_ERROR);
-  }
-
-  pthread_mutex_lock (&cache_lock);
-
-  if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
-  {
-    assert (ce != NULL);
-    ret = ce->hits;
-    ce->hits = ret + step;
-  }
-
-  pthread_mutex_unlock (&cache_lock);
-
-  return (ret);
-} /* int uc_inc_hits */
-
-/*
- * Meta data interface
- */
-/* XXX: This function will acquire `cache_lock' but will not free it! */
-static meta_data_t *uc_get_meta (const value_list_t *vl) /* {{{ */
-{
-  char name[6 * DATA_MAX_NAME_LEN];
-  cache_entry_t *ce = NULL;
-  int status;
-
-  status = FORMAT_VL (name, sizeof (name), vl);
-  if (status != 0)
-  {
-    ERROR ("utils_cache: uc_get_meta: FORMAT_VL failed.");
-    return (NULL);
-  }
-
-  pthread_mutex_lock (&cache_lock);
-
-  status = c_avl_get (cache_tree, name, (void *) &ce);
-  if (status != 0)
-  {
-    pthread_mutex_unlock (&cache_lock);
-    return (NULL);
-  }
-  assert (ce != NULL);
-
-  if (ce->meta == NULL)
-    ce->meta = meta_data_create ();
-
-  if (ce->meta == NULL)
-    pthread_mutex_unlock (&cache_lock);
-
-  return (ce->meta);
-} /* }}} meta_data_t *uc_get_meta */
-
-/* Sorry about this preprocessor magic, but it really makes this file much
- * shorter.. */
-#define UC_WRAP(wrap_function) { \
-  meta_data_t *meta; \
-  int status; \
-  meta = uc_get_meta (vl); \
-  if (meta == NULL) return (-1); \
-  status = wrap_function (meta, key); \
-  pthread_mutex_unlock (&cache_lock); \
-  return (status); \
-}
-int uc_meta_data_exists (const value_list_t *vl, const char *key)
-  UC_WRAP (meta_data_exists)
-
-int uc_meta_data_delete (const value_list_t *vl, const char *key)
-  UC_WRAP (meta_data_delete)
-#undef UC_WRAP
-
-/* We need a new version of this macro because the following functions take
- * two argumetns. */
-#define UC_WRAP(wrap_function) { \
-  meta_data_t *meta; \
-  int status; \
-  meta = uc_get_meta (vl); \
-  if (meta == NULL) return (-1); \
-  status = wrap_function (meta, key, value); \
-  pthread_mutex_unlock (&cache_lock); \
-  return (status); \
-}
-int uc_meta_data_add_string (const value_list_t *vl,
-    const char *key,
-    const char *value)
-  UC_WRAP(meta_data_add_string)
-int uc_meta_data_add_signed_int (const value_list_t *vl,
-    const char *key,
-    int64_t value)
-  UC_WRAP(meta_data_add_signed_int)
-int uc_meta_data_add_unsigned_int (const value_list_t *vl,
-    const char *key,
-    uint64_t value)
-  UC_WRAP(meta_data_add_unsigned_int)
-int uc_meta_data_add_double (const value_list_t *vl,
-    const char *key,
-    double value)
-  UC_WRAP(meta_data_add_double)
-int uc_meta_data_add_boolean (const value_list_t *vl,
-    const char *key,
-    _Bool value)
-  UC_WRAP(meta_data_add_boolean)
-
-int uc_meta_data_get_string (const value_list_t *vl,
-    const char *key,
-    char **value)
-  UC_WRAP(meta_data_get_string)
-int uc_meta_data_get_signed_int (const value_list_t *vl,
-    const char *key,
-    int64_t *value)
-  UC_WRAP(meta_data_get_signed_int)
-int uc_meta_data_get_unsigned_int (const value_list_t *vl,
-    const char *key,
-    uint64_t *value)
-  UC_WRAP(meta_data_get_unsigned_int)
-int uc_meta_data_get_double (const value_list_t *vl,
-    const char *key,
-    double *value)
-  UC_WRAP(meta_data_get_double)
-int uc_meta_data_get_boolean (const value_list_t *vl,
-    const char *key,
-    _Bool *value)
-  UC_WRAP(meta_data_get_boolean)
-#undef UC_WRAP
-
-/* vim: set sw=2 ts=8 sts=2 tw=78 : */
diff --git a/src/utils_cache.h b/src/utils_cache.h
deleted file mode 100644 (file)
index 16039aa..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * collectd - src/utils_cache.h
- * Copyright (C) 2007       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 UTILS_CACHE_H
-#define UTILS_CACHE_H 1
-
-#include "plugin.h"
-
-#define STATE_OKAY     0
-#define STATE_WARNING  1
-#define STATE_ERROR    2
-#define STATE_MISSING 15
-
-int uc_init (void);
-int uc_check_timeout (void);
-int uc_update (const data_set_t *ds, const value_list_t *vl);
-int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_values_num);
-gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl);
-
-int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number);
-
-int uc_get_state (const data_set_t *ds, const value_list_t *vl);
-int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state);
-int uc_get_hits (const data_set_t *ds, const value_list_t *vl);
-int uc_set_hits (const data_set_t *ds, const value_list_t *vl, int hits);
-int uc_inc_hits (const data_set_t *ds, const value_list_t *vl, int step);
-
-int uc_get_history (const data_set_t *ds, const value_list_t *vl,
-    gauge_t *ret_history, size_t num_steps, size_t num_ds);
-int uc_get_history_by_name (const char *name,
-    gauge_t *ret_history, size_t num_steps, size_t num_ds);
-
-/*
- * Meta data interface
- */
-int uc_meta_data_exists (const value_list_t *vl, const char *key);
-int uc_meta_data_delete (const value_list_t *vl, const char *key);
-
-int uc_meta_data_add_string (const value_list_t *vl,
-    const char *key,
-    const char *value);
-int uc_meta_data_add_signed_int (const value_list_t *vl,
-    const char *key,
-    int64_t value);
-int uc_meta_data_add_unsigned_int (const value_list_t *vl,
-    const char *key,
-    uint64_t value);
-int uc_meta_data_add_double (const value_list_t *vl,
-    const char *key,
-    double value);
-int uc_meta_data_add_boolean (const value_list_t *vl,
-    const char *key,
-    _Bool value);
-
-int uc_meta_data_get_string (const value_list_t *vl,
-    const char *key,
-    char **value);
-int uc_meta_data_get_signed_int (const value_list_t *vl,
-    const char *key,
-    int64_t *value);
-int uc_meta_data_get_unsigned_int (const value_list_t *vl,
-    const char *key,
-    uint64_t *value);
-int uc_meta_data_get_double (const value_list_t *vl,
-    const char *key,
-    double *value);
-int uc_meta_data_get_boolean (const value_list_t *vl,
-    const char *key,
-    _Bool *value);
-
-/* vim: set shiftwidth=2 softtabstop=2 tabstop=8 : */
-#endif /* !UTILS_CACHE_H */
diff --git a/src/utils_complain.c b/src/utils_complain.c
deleted file mode 100644 (file)
index 6193614..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * collectd - src/utils_complain.c
- * Copyright (C) 2006-2013  Florian octo Forster
- * Copyright (C) 2008       Sebastian tokkee Harl
- *
- * 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>
- *   Sebastian tokkee Harl <sh at tokkee.org>
- **/
-
-#include "collectd.h"
-#include "utils_complain.h"
-#include "plugin.h"
-
-/* vcomplain returns 0 if it did not report, 1 else */
-static int vcomplain (int level, c_complain_t *c,
-               const char *format, va_list ap)
-{
-       cdtime_t now;
-       char   message[512];
-
-       now = cdtime ();
-
-       if (c->last + c->interval > now)
-               return 0;
-
-       c->last = now;
-
-       if (c->interval < plugin_get_interval ())
-               c->interval = plugin_get_interval ();
-       else
-               c->interval *= 2;
-
-       if (c->interval > TIME_T_TO_CDTIME_T (86400))
-               c->interval = TIME_T_TO_CDTIME_T (86400);
-
-       vsnprintf (message, sizeof (message), format, ap);
-       message[sizeof (message) - 1] = '\0';
-
-       plugin_log (level, "%s", message);
-       return 1;
-} /* vcomplain */
-
-void c_complain (int level, c_complain_t *c, const char *format, ...)
-{
-       va_list ap;
-
-       va_start (ap, format);
-       if (vcomplain (level, c, format, ap))
-               c->complained_once = 1;
-       va_end (ap);
-} /* c_complain */
-
-void c_complain_once (int level, c_complain_t *c, const char *format, ...)
-{
-       va_list ap;
-
-       if (c->complained_once)
-               return;
-
-       va_start (ap, format);
-       if (vcomplain (level, c, format, ap))
-               c->complained_once = 1;
-       va_end (ap);
-} /* c_complain_once */
-
-void c_do_release (int level, c_complain_t *c, const char *format, ...)
-{
-       char message[512];
-       va_list ap;
-
-       if (c->interval == 0)
-               return;
-
-       c->interval = 0;
-       c->complained_once = 0;
-
-       va_start (ap, format);
-       vsnprintf (message, sizeof (message), format, ap);
-       message[sizeof (message) - 1] = '\0';
-       va_end (ap);
-
-       plugin_log (level, "%s", message);
-} /* c_release */
-
-/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
-
diff --git a/src/utils_complain.h b/src/utils_complain.h
deleted file mode 100644 (file)
index 390f961..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/**
- * collectd - src/utils_complain.h
- * Copyright (C) 2006-2013  Florian octo Forster
- * Copyright (C) 2008       Sebastian tokkee Harl
- *
- * 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>
- *   Sebastian tokkee Harl <sh at tokkee.org>
- **/
-
-#ifndef UTILS_COMPLAIN_H
-#define UTILS_COMPLAIN_H 1
-
-#include "utils_time.h"
-
-typedef struct
-{
-       /* time of the last report */
-       cdtime_t last;
-
-       /* How long to wait until reporting again.
-        * 0 indicates that the complaint is no longer valid. */
-       cdtime_t interval;
-
-       _Bool complained_once;
-} c_complain_t;
-
-#define C_COMPLAIN_INIT_STATIC { 0, 0, 0 }
-#define C_COMPLAIN_INIT(c) do { \
-       (c)->last = 0; \
-       (c)->interval = 0; \
-       (c)->complained_once = 0; \
-} while (0)
-
-/*
- * NAME
- *   c_complain
- *
- * DESCRIPTION
- *   Complain about something. This function will report a message (usually
- *   indicating some error condition) using the collectd logging mechanism.
- *   When this function is called again, reporting the message again will be
- *   deferred by an increasing interval (up to one day) to prevent flooding
- *   the logs. A call to `c_release' resets the counter.
- *
- * PARAMETERS
- *   `level'  The log level passed to `plugin_log'.
- *   `c'      Identifier for the complaint.
- *   `format' Message format - see the documentation of printf(3).
- */
-void c_complain (int level, c_complain_t *c, const char *format, ...);
-
-/*
- * NAME
- *   c_complain_once
- *
- * DESCRIPTION
- *   Complain about something once. This function will not report anything
- *   again, unless `c_release' has been called in between. If used after some
- *   calls to `c_complain', it will report again on the next interval and stop
- *   after that.
- *
- *   See `c_complain' for further details and a description of the parameters.
- */
-void c_complain_once (int level, c_complain_t *c, const char *format, ...);
-
-/*
- * NAME
- *   c_would_release
- *
- * DESCRIPTION
- *   Returns true if the specified complaint would be released, false else.
- */
-#define c_would_release(c) ((c)->interval != 0)
-
-/*
- * NAME
- *   c_release
- *
- * DESCRIPTION
- *   Release a complaint. This will report a message once, marking the
- *   complaint as released.
- *
- *   See `c_complain' for a description of the parameters.
- */
-void c_do_release (int level, c_complain_t *c, const char *format, ...);
-#define c_release(level, c, ...) \
-       do { \
-               if (c_would_release (c)) \
-                       c_do_release(level, c, __VA_ARGS__); \
-       } while (0)
-
-#endif /* UTILS_COMPLAIN_H */
-
-/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
-
index 712b1ae..eefde96 100644 (file)
@@ -3,10 +3,10 @@
  * Copyright (C) 2006       Florian octo Forster
  * Copyright (C) 2002       The Measurement Factory, Inc.
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1. Redistributions of source code must retain the above copyright notice,
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
@@ -33,6 +33,7 @@
  *   Florian octo Forster <octo at collectd.org>
  */
 
+#define _DEFAULT_SOURCE
 #define _BSD_SOURCE
 
 #include "collectd.h"
index b7d4494..220258f 100644 (file)
@@ -27,7 +27,6 @@
 
 #include "utils_format_graphite.h"
 #include "utils_cache.h"
-#include "utils_parse_option.h"
 
 #define GRAPHITE_FORBIDDEN " \t\"\\:!/()\n\r"
 
index 355eaf1..699c74e 100644 (file)
@@ -31,7 +31,7 @@
 #include "utils_cache.h"
 #include "utils_format_json.h"
 
-static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
+static int json_escape_string (char *buffer, size_t buffer_size, /* {{{ */
     const char *string)
 {
   size_t src_pos;
@@ -75,7 +75,7 @@ static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
 #undef BUFFER_ADD
 
   return (0);
-} /* }}} int escape_string */
+} /* }}} int json_escape_string */
 
 static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
                 const data_set_t *ds, const value_list_t *vl, int store_rates)
@@ -239,7 +239,10 @@ static int meta_data_to_json (char *buffer, size_t buffer_size, /* {{{ */
   int status;
   int i;
 
-  memset (buffer, 0, buffer_size);
+  buffer[0] = 0;
+
+  if (meta == NULL)
+    return (EINVAL);
 
 #define BUFFER_ADD(...) do { \
   status = ssnprintf (buffer + offset, buffer_size - offset, \
@@ -253,6 +256,12 @@ static int meta_data_to_json (char *buffer, size_t buffer_size, /* {{{ */
 } while (0)
 
   keys_num = meta_data_toc (meta, &keys);
+  if (keys_num == 0)
+  {
+    sfree (keys);
+    return (0);
+  }
+
   for (i = 0; i < keys_num; ++i)
   {
     int type;
@@ -265,7 +274,7 @@ static int meta_data_to_json (char *buffer, size_t buffer_size, /* {{{ */
       if (meta_data_get_string (meta, key, &value) == 0)
       {
         char temp[512] = "";
-        escape_string (temp, sizeof (temp), value);
+        json_escape_string (temp, sizeof (temp), value);
         sfree (value);
         BUFFER_ADD (",\"%s\":%s", key, temp);
       }
@@ -308,7 +317,7 @@ static int meta_data_to_json (char *buffer, size_t buffer_size, /* {{{ */
 #undef BUFFER_ADD
 
   return (0);
-} /* int meta_data_to_json */
+} /* }}} int meta_data_to_json */
 
 static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
                 const data_set_t *ds, const value_list_t *vl, int store_rates)
@@ -353,7 +362,7 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
   BUFFER_ADD (",\"interval\":%.3f", CDTIME_T_TO_DOUBLE (vl->interval));
 
 #define BUFFER_ADD_KEYVAL(key, value) do { \
-  status = escape_string (temp, sizeof (temp), (value)); \
+  status = json_escape_string (temp, sizeof (temp), (value)); \
   if (status != 0) \
     return (status); \
   BUFFER_ADD (",\"%s\":%s", (key), temp); \
diff --git a/src/utils_heap.c b/src/utils_heap.c
deleted file mode 100644 (file)
index 1b5dca7..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-/**
- * collectd - src/utils_heap.c
- * Copyright (C) 2009       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 <string.h>
-#include <errno.h>
-#include <assert.h>
-#include <pthread.h>
-
-#include "utils_heap.h"
-
-struct c_heap_s
-{
-  pthread_mutex_t lock;
-  int (*compare) (const void *, const void *);
-
-  void **list;
-  size_t list_len; /* # entries used */
-  size_t list_size; /* # entries allocated */
-};
-
-enum reheap_direction
-{
-  DIR_UP,
-  DIR_DOWN
-};
-
-static void reheap (c_heap_t *h, size_t root, enum reheap_direction dir)
-{
-  size_t left;
-  size_t right;
-  size_t min;
-  int status;
-
-  /* Calculate the positions of the children */
-  left = (2 * root) + 1;
-  if (left >= h->list_len)
-    left = 0;
-
-  right = (2 * root) + 2;
-  if (right >= h->list_len)
-    right = 0;
-
-  /* Check which one of the children is smaller. */
-  if ((left == 0) && (right == 0))
-    return;
-  else if (left == 0)
-    min = right;
-  else if (right == 0)
-    min = left;
-  else
-  {
-    status = h->compare (h->list[left], h->list[right]);
-    if (status > 0)
-      min = right;
-    else
-      min = left;
-  }
-
-  status = h->compare (h->list[root], h->list[min]);
-  if (status <= 0)
-  {
-    /* We didn't need to change anything, so the rest of the tree should be
-     * okay now. */
-    return;
-  }
-  else /* if (status > 0) */
-  {
-    void *tmp;
-
-    tmp = h->list[root];
-    h->list[root] = h->list[min];
-    h->list[min] = tmp;
-  }
-
-  if ((dir == DIR_UP) && (root == 0))
-    return;
-
-  if (dir == DIR_UP)
-    reheap (h, (root - 1) / 2, dir);
-  else if (dir == DIR_DOWN)
-    reheap (h, min, dir);
-} /* void reheap */
-
-c_heap_t *c_heap_create (int (*compare) (const void *, const void *))
-{
-  c_heap_t *h;
-
-  if (compare == NULL)
-    return (NULL);
-
-  h = malloc (sizeof (*h));
-  if (h == NULL)
-    return (NULL);
-
-  memset (h, 0, sizeof (*h));
-  pthread_mutex_init (&h->lock, /* attr = */ NULL);
-  h->compare = compare;
-  
-  h->list = NULL;
-  h->list_len = 0;
-  h->list_size = 0;
-
-  return (h);
-} /* c_heap_t *c_heap_create */
-
-void c_heap_destroy (c_heap_t *h)
-{
-  if (h == NULL)
-    return;
-
-  h->list_len = 0;
-  h->list_size = 0;
-  free (h->list);
-  h->list = NULL;
-
-  pthread_mutex_destroy (&h->lock);
-
-  free (h);
-} /* void c_heap_destroy */
-
-int c_heap_insert (c_heap_t *h, void *ptr)
-{
-  size_t index;
-
-  if ((h == NULL) || (ptr == NULL))
-    return (-EINVAL);
-
-  pthread_mutex_lock (&h->lock);
-
-  assert (h->list_len <= h->list_size);
-  if (h->list_len == h->list_size)
-  {
-    void **tmp;
-
-    tmp = realloc (h->list, (h->list_size + 16) * sizeof (*h->list));
-    if (tmp == NULL)
-    {
-      pthread_mutex_unlock (&h->lock);
-      return (-ENOMEM);
-    }
-
-    h->list = tmp;
-    h->list_size += 16;
-  }
-
-  /* Insert the new node as a leaf. */
-  index = h->list_len;
-  h->list[index] = ptr;
-  h->list_len++;
-
-  /* Reorganize the heap from bottom up. */
-  reheap (h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
-  
-  pthread_mutex_unlock (&h->lock);
-  return (0);
-} /* int c_heap_insert */
-
-void *c_heap_get_root (c_heap_t *h)
-{
-  void *ret = NULL;
-
-  if (h == NULL)
-    return (NULL);
-
-  pthread_mutex_lock (&h->lock);
-
-  if (h->list_len == 0)
-  {
-    pthread_mutex_unlock (&h->lock);
-    return (NULL);
-  }
-  else if (h->list_len == 1)
-  {
-    ret = h->list[0];
-    h->list[0] = NULL;
-    h->list_len = 0;
-  }
-  else /* if (h->list_len > 1) */
-  {
-    ret = h->list[0];
-    h->list[0] = h->list[h->list_len - 1];
-    h->list[h->list_len - 1] = NULL;
-    h->list_len--;
-
-    reheap (h, /* root = */ 0, DIR_DOWN);
-  }
-
-  /* free some memory */
-  if ((h->list_len + 32) < h->list_size)
-  {
-    void **tmp;
-
-    tmp = realloc (h->list, (h->list_len + 16) * sizeof (*h->list));
-    if (tmp != NULL)
-    {
-      h->list = tmp;
-      h->list_size = h->list_len + 16;
-    }
-  }
-
-  pthread_mutex_unlock (&h->lock);
-
-  return (ret);
-} /* void *c_heap_get_root */
-
-/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/utils_heap.h b/src/utils_heap.h
deleted file mode 100644 (file)
index 6d71c43..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * collectd - src/utils_heap.h
- * Copyright (C) 2009       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 UTILS_HEAP_H
-#define UTILS_HEAP_H 1
-
-struct c_heap_s;
-typedef struct c_heap_s c_heap_t;
-
-/*
- * NAME
- *   c_heap_create
- *
- * DESCRIPTION
- *   Allocates a new heap.
- *
- * PARAMETERS
- *   `compare'  The function-pointer `compare' is used to compare two keys. It
- *              has to return less than zero if it's first argument is smaller
- *              then the second argument, more than zero if the first argument
- *              is bigger than the second argument and zero if they are equal.
- *              If your keys are char-pointers, you can use the `strcmp'
- *              function from the libc here.
- *
- * RETURN VALUE
- *   A c_heap_t-pointer upon success or NULL upon failure.
- */
-c_heap_t *c_heap_create (int (*compare) (const void *, const void *));
-
-/*
- * NAME
- *   c_heap_destroy
- *
- * DESCRIPTION
- *   Deallocates a heap. Stored value- and key-pointer are lost, but of course
- *   not freed.
- */
-void c_heap_destroy (c_heap_t *h);
-
-/*
- * NAME
- *   c_heap_insert
- *
- * DESCRIPTION
- *   Stores the key-value-pair in the heap pointed to by `h'.
- *
- * PARAMETERS
- *   `h'        Heap to store the data in.
- *   `ptr'      Value to be stored. This is typically a pointer to a data
- *              structure. The data structure is of course *not* copied and may
- *              not be free'd before the pointer has been removed from the heap
- *              again.
- *
- * RETURN VALUE
- *   Zero upon success, non-zero otherwise. It's less than zero if an error
- *   occurred or greater than zero if the key is already stored in the tree.
- */
-int c_heap_insert (c_heap_t *h, void *ptr);
-
-/*
- * NAME
- *   c_heap_get_root
- *
- * DESCRIPTION
- *   Removes the value at the root of the heap and returns both, key and value.
- *
- * PARAMETERS
- *   `h'           Heap to remove key-value-pair from.
- *
- * RETURN VALUE
- *   The pointer passed to `c_heap_insert' or NULL if there are no more
- *   elements in the heap (or an error occurred).
- */
-void *c_heap_get_root (c_heap_t *h);
-
-#endif /* UTILS_HEAP_H */
-/* vim: set sw=2 sts=2 et : */
diff --git a/src/utils_llist.c b/src/utils_llist.c
deleted file mode 100644 (file)
index 09c9834..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/**
- * collectd - src/utils_llist.c
- * Copyright (C) 2006       Florian Forster <octo at collectd.org>
- *
- * 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 "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "utils_llist.h"
-
-/*
- * Private data types
- */
-struct llist_s
-{
-       llentry_t *head;
-       llentry_t *tail;
-       int size;
-};
-
-/*
- * Public functions
- */
-llist_t *llist_create (void)
-{
-       llist_t *ret;
-
-       ret = (llist_t *) malloc (sizeof (llist_t));
-       if (ret == NULL)
-               return (NULL);
-
-       memset (ret, '\0', sizeof (llist_t));
-
-       return (ret);
-}
-
-void llist_destroy (llist_t *l)
-{
-       llentry_t *e_this;
-       llentry_t *e_next;
-
-       if (l == NULL)
-               return;
-
-       for (e_this = l->head; e_this != NULL; e_this = e_next)
-       {
-               e_next = e_this->next;
-               llentry_destroy (e_this);
-       }
-
-       free (l);
-}
-
-llentry_t *llentry_create (char *key, void *value)
-{
-       llentry_t *e;
-
-       e = (llentry_t *) malloc (sizeof (llentry_t));
-       if (e)
-       {
-               e->key   = key;
-               e->value = value;
-               e->next  = NULL;
-       }
-
-       return (e);
-}
-
-void llentry_destroy (llentry_t *e)
-{
-       free (e);
-}
-
-void llist_append (llist_t *l, llentry_t *e)
-{
-       e->next = NULL;
-
-       if (l->tail == NULL)
-               l->head = e;
-       else
-               l->tail->next = e;
-
-       l->tail = e;
-
-       ++(l->size);
-}
-
-void llist_prepend (llist_t *l, llentry_t *e)
-{
-       e->next = l->head;
-       l->head = e;
-
-       if (l->tail == NULL)
-               l->tail = e;
-
-       ++(l->size);
-}
-
-void llist_remove (llist_t *l, llentry_t *e)
-{
-       llentry_t *prev;
-
-       prev = l->head;
-       while ((prev != NULL) && (prev->next != e))
-               prev = prev->next;
-
-       if (prev != NULL)
-               prev->next = e->next;
-       if (l->head == e)
-               l->head = e->next;
-       if (l->tail == e)
-               l->tail = prev;
-
-       --(l->size);
-}
-
-int llist_size (llist_t *l)
-{
-       return (l ? l->size : 0);
-}
-
-static int llist_strcmp (llentry_t *e, void *ud)
-{
-       if ((e == NULL) || (ud == NULL))
-               return (-1);
-       return (strcmp (e->key, (const char *)ud));
-}
-
-llentry_t *llist_search (llist_t *l, const char *key)
-{
-       return (llist_search_custom (l, llist_strcmp, (void *)key));
-}
-
-llentry_t *llist_search_custom (llist_t *l,
-               int (*compare) (llentry_t *, void *), void *user_data)
-{
-       llentry_t *e;
-
-       if (l == NULL)
-               return (NULL);
-
-       e = l->head;
-       while (e != NULL) {
-               llentry_t *next = e->next;
-
-               if (compare (e, user_data) == 0)
-                       break;
-
-               e = next;
-       }
-
-       return (e);
-}
-
-llentry_t *llist_head (llist_t *l)
-{
-       if (l == NULL)
-               return (NULL);
-       return (l->head);
-}
-
-llentry_t *llist_tail (llist_t *l)
-{
-       if (l == NULL)
-               return (NULL);
-       return (l->tail);
-}
diff --git a/src/utils_llist.h b/src/utils_llist.h
deleted file mode 100644 (file)
index 59bf2e4..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * collectd - src/utils_llist.h
- * Copyright (C) 2006       Florian Forster <octo at collectd.org>
- *
- * 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_LLIST_H
-#define UTILS_LLIST_H 1
-
-/*
- * Data types
- */
-struct llentry_s
-{
-       char *key;
-       void *value;
-       struct llentry_s *next;
-};
-typedef struct llentry_s llentry_t;
-
-struct llist_s;
-typedef struct llist_s llist_t;
-
-/*
- * Functions
- */
-llist_t *llist_create (void);
-void llist_destroy (llist_t *l);
-
-llentry_t *llentry_create (char *key, void *value);
-void llentry_destroy (llentry_t *e);
-
-void llist_append (llist_t *l, llentry_t *e);
-void llist_prepend (llist_t *l, llentry_t *e);
-void llist_remove (llist_t *l, llentry_t *e);
-
-int llist_size (llist_t *l);
-
-llentry_t *llist_search (llist_t *l, const char *key);
-llentry_t *llist_search_custom (llist_t *l,
-               int (*compare) (llentry_t *, void *), void *user_data);
-
-llentry_t *llist_head (llist_t *l);
-llentry_t *llist_tail (llist_t *l);
-
-#endif /* UTILS_LLIST_H */
diff --git a/src/utils_match.c b/src/utils_match.c
deleted file mode 100644 (file)
index b024c69..0000000
+++ /dev/null
@@ -1,384 +0,0 @@
-/**
- * collectd - src/utils_match.c
- * Copyright (C) 2008       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 "collectd.h"
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_match.h"
-
-#include <regex.h>
-
-#define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
-#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
-
-struct cu_match_s
-{
-  regex_t regex;
-  regex_t excluderegex;
-  int flags;
-
-  int (*callback) (const char *str, char * const *matches, size_t matches_num,
-      void *user_data);
-  void *user_data;
-};
-
-/*
- * Private functions
- */
-static char *match_substr (const char *str, int begin, int end)
-{
-  char *ret;
-  size_t ret_len;
-
-  if ((begin < 0) || (end < 0) || (begin >= end))
-    return (NULL);
-  if ((size_t) end > (strlen (str) + 1))
-  {
-    ERROR ("utils_match: match_substr: `end' points after end of string.");
-    return (NULL);
-  }
-
-  ret_len = end - begin;
-  ret = (char *) malloc (sizeof (char) * (ret_len + 1));
-  if (ret == NULL)
-  {
-    ERROR ("utils_match: match_substr: malloc failed.");
-    return (NULL);
-  }
-
-  sstrncpy (ret, str + begin, ret_len + 1);
-  return (ret);
-} /* char *match_substr */
-
-static int default_callback (const char __attribute__((unused)) *str,
-    char * const *matches, size_t matches_num, void *user_data)
-{
-  cu_match_value_t *data = (cu_match_value_t *) user_data;
-
-  if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
-  {
-    gauge_t value;
-    char *endptr = NULL;
-
-    if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC)
-    {
-      data->value.gauge = isnan (data->value.gauge) ? 1 : data->value.gauge + 1;
-      data->values_num++;
-      return(0);
-    }
-
-    if (matches_num < 2)
-      return (-1);
-
-    value = (gauge_t) strtod (matches[1], &endptr);
-    if (matches[1] == endptr)
-      return (-1);
-
-    if ((data->values_num == 0)
-       || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST))
-    {
-      data->value.gauge = value;
-    }
-    else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE)
-    {
-      double f = ((double) data->values_num)
-       / ((double) (data->values_num + 1));
-      data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
-    }
-    else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN)
-    {
-      if (data->value.gauge > value)
-       data->value.gauge = value;
-    }
-    else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX)
-    {
-      if (data->value.gauge < value)
-       data->value.gauge = value;
-    }
-    else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD)
-    {
-      data->value.gauge += value;
-    }
-    else
-    {
-      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
-      return (-1);
-    }
-
-    data->values_num++;
-  }
-  else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER)
-  {
-    counter_t value;
-    char *endptr = NULL;
-
-    if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
-    {
-      data->value.counter++;
-      data->values_num++;
-      return (0);
-    }
-
-    if (matches_num < 2)
-      return (-1);
-
-    value = (counter_t) strtoull (matches[1], &endptr, 0);
-    if (matches[1] == endptr)
-      return (-1);
-
-    if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
-      data->value.counter = value;
-    else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
-      data->value.counter += value;
-    else
-    {
-      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
-      return (-1);
-    }
-
-    data->values_num++;
-  }
-  else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE)
-  {
-    derive_t value;
-    char *endptr = NULL;
-
-    if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
-    {
-      data->value.counter++;
-      data->values_num++;
-      return (0);
-    }
-
-    if (matches_num < 2)
-      return (-1);
-
-    value = (derive_t) strtoll (matches[1], &endptr, 0);
-    if (matches[1] == endptr)
-      return (-1);
-
-    if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
-      data->value.derive = value;
-    else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
-      data->value.derive += value;
-    else
-    {
-      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
-      return (-1);
-    }
-
-    data->values_num++;
-  }
-  else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE)
-  {
-    absolute_t value;
-    char *endptr = NULL;
-
-    if (matches_num < 2)
-      return (-1);
-
-    value = (absolute_t) strtoull (matches[1], &endptr, 0);
-    if (matches[1] == endptr)
-      return (-1);
-
-    if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
-      data->value.absolute = value;
-    else
-    {
-      ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
-      return (-1);
-    }
-
-    data->values_num++;
-  }
-  else
-  {
-    ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
-    return (-1);
-  }
-
-  return (0);
-} /* int default_callback */
-
-/*
- * Public functions
- */
-cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
-               int (*callback) (const char *str,
-                 char * const *matches, size_t matches_num, void *user_data),
-               void *user_data)
-{
-  cu_match_t *obj;
-  int status;
-
-  DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
-        regex, excluderegex);
-
-  obj = (cu_match_t *) malloc (sizeof (cu_match_t));
-  if (obj == NULL)
-    return (NULL);
-  memset (obj, '\0', sizeof (cu_match_t));
-
-  status = regcomp (&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
-  if (status != 0)
-  {
-    ERROR ("Compiling the regular expression \"%s\" failed.", regex);
-    sfree (obj);
-    return (NULL);
-  }
-
-  if (excluderegex && strcmp(excluderegex, "") != 0) {
-    status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED);
-    if (status != 0)
-    {
-       ERROR ("Compiling the excluding regular expression \"%s\" failed.",
-              excluderegex);
-       sfree (obj);
-       return (NULL);
-    }
-    obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
-  }
-
-  obj->callback = callback;
-  obj->user_data = user_data;
-
-  return (obj);
-} /* cu_match_t *match_create_callback */
-
-cu_match_t *match_create_simple (const char *regex,
-                                const char *excluderegex, int match_ds_type)
-{
-  cu_match_value_t *user_data;
-  cu_match_t *obj;
-
-  user_data = (cu_match_value_t *) malloc (sizeof (cu_match_value_t));
-  if (user_data == NULL)
-    return (NULL);
-  memset (user_data, '\0', sizeof (cu_match_value_t));
-  user_data->ds_type = match_ds_type;
-
-  obj = match_create_callback (regex, excluderegex,
-                              default_callback, user_data);
-  if (obj == NULL)
-  {
-    sfree (user_data);
-    return (NULL);
-  }
-
-  obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
-
-  return (obj);
-} /* cu_match_t *match_create_simple */
-
-void match_destroy (cu_match_t *obj)
-{
-  if (obj == NULL)
-    return;
-
-  if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
-  {
-    sfree (obj->user_data);
-  }
-
-  sfree (obj);
-} /* void match_destroy */
-
-int match_apply (cu_match_t *obj, const char *str)
-{
-  int status;
-  regmatch_t re_match[32];
-  char *matches[32];
-  size_t matches_num;
-  size_t i;
-
-  if ((obj == NULL) || (str == NULL))
-    return (-1);
-
-  if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
-    status = regexec (&obj->excluderegex, str,
-                     STATIC_ARRAY_SIZE (re_match), re_match,
-                     /* eflags = */ 0);
-    /* Regex did match, so exclude this line */
-    if (status == 0) {
-      DEBUG("ExludeRegex matched, don't count that line\n");
-      return (0);
-    }
-  }
-
-  status = regexec (&obj->regex, str,
-      STATIC_ARRAY_SIZE (re_match), re_match,
-      /* eflags = */ 0);
-
-  /* Regex did not match */
-  if (status != 0)
-    return (0);
-
-  memset (matches, '\0', sizeof (matches));
-  for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
-  {
-    if ((re_match[matches_num].rm_so < 0)
-       || (re_match[matches_num].rm_eo < 0))
-      break;
-
-    matches[matches_num] = match_substr (str,
-       re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
-    if (matches[matches_num] == NULL)
-    {
-      status = -1;
-      break;
-    }
-  }
-
-  if (status != 0)
-  {
-    ERROR ("utils_match: match_apply: match_substr failed.");
-  }
-  else
-  {
-    status = obj->callback (str, matches, matches_num, obj->user_data);
-    if (status != 0)
-    {
-      ERROR ("utils_match: match_apply: callback failed.");
-    }
-  }
-
-  for (i = 0; i < matches_num; i++)
-  {
-    sfree (matches[i]);
-  }
-
-  return (status);
-} /* int match_apply */
-
-void *match_get_user_data (cu_match_t *obj)
-{
-  if (obj == NULL)
-    return (NULL);
-  return (obj->user_data);
-} /* void *match_get_user_data */
-
-/* vim: set sw=2 sts=2 ts=8 : */
diff --git a/src/utils_match.h b/src/utils_match.h
deleted file mode 100644 (file)
index 705a609..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/**
- * collectd - src/utils_match.h
- * Copyright (C) 2008       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 UTILS_MATCH_H
-#define UTILS_MATCH_H 1
-
-#include "plugin.h"
-
-/*
- * Each type may have 12 sub-types
- * 0x1000 = 1000000000000
- *          ^             <- Type bit
- *           ^^^^^^^^^^^^ <- Subtype bits
- */
-#define UTILS_MATCH_DS_TYPE_GAUGE    0x1000
-#define UTILS_MATCH_DS_TYPE_COUNTER  0x2000
-#define UTILS_MATCH_DS_TYPE_DERIVE   0x4000
-#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x8000
-
-#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
-#define UTILS_MATCH_CF_GAUGE_MIN     0x02
-#define UTILS_MATCH_CF_GAUGE_MAX     0x04
-#define UTILS_MATCH_CF_GAUGE_LAST    0x08
-#define UTILS_MATCH_CF_GAUGE_INC     0x10
-#define UTILS_MATCH_CF_GAUGE_ADD     0x20
-
-#define UTILS_MATCH_CF_COUNTER_SET   0x01
-#define UTILS_MATCH_CF_COUNTER_ADD   0x02
-#define UTILS_MATCH_CF_COUNTER_INC   0x04
-
-#define UTILS_MATCH_CF_DERIVE_SET   0x01
-#define UTILS_MATCH_CF_DERIVE_ADD   0x02
-#define UTILS_MATCH_CF_DERIVE_INC   0x04
-
-#define UTILS_MATCH_CF_ABSOLUTE_SET   0x01
-#define UTILS_MATCH_CF_ABSOLUTE_ADD   0x02
-#define UTILS_MATCH_CF_ABSOLUTE_INC   0x04
-
-/*
- * Data types
- */
-struct cu_match_s;
-typedef struct cu_match_s cu_match_t;
-
-struct cu_match_value_s
-{
-  int ds_type;
-  value_t value;
-  unsigned int values_num;
-};
-typedef struct cu_match_value_s cu_match_value_t;
-
-/*
- * Prototypes
- */
-/*
- * NAME
- *  match_create_callback
- *
- * DESCRIPTION
- *  Creates a new `cu_match_t' object which will use the regular expression
- *  `regex' to match lines, see the `match_apply' method below. If the line
- *  matches, the callback passed in `callback' will be called along with the
- *  pointer `user_pointer'.
- *  The string that's passed to the callback depends on the regular expression:
- *  If the regular expression includes a sub-match, i. e. something like
- *    "value=([0-9][0-9]*)"
- *  then only the submatch (the part in the parenthesis) will be passed to the
- *  callback. If there is no submatch, then the entire string is passed to the
- *  callback.
- *  The optional `excluderegex' allows to exclude the line from the match, if
- *  the excluderegex matches.
- */
-cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
-               int (*callback) (const char *str,
-                 char * const *matches, size_t matches_num, void *user_data),
-               void *user_data);
-
-/*
- * NAME
- *  match_create_simple
- *
- * DESCRIPTION
- *  Creates a new `cu_match_t' with a default callback. The user data for that
- *  default callback will be a `cu_match_value_t' structure, with
- *  `ds_type' copied to the structure. The default callback will handle the
- *  string as containing a number (see strtoll(3) and strtod(3)) and store that
- *  number in the `value' member. How that is done depends on `ds_type':
- *
- *  UTILS_MATCH_DS_TYPE_GAUGE
- *    The function will search for a floating point number in the string and
- *    store it in value.gauge.
- *  UTILS_MATCH_DS_TYPE_COUNTER_SET
- *    The function will search for an integer in the string and store it in
- *    value.counter.
- *  UTILS_MATCH_DS_TYPE_COUNTER_ADD
- *    The function will search for an integer in the string and add it to the
- *    value in value.counter.
- *  UTILS_MATCH_DS_TYPE_COUNTER_INC
- *    The function will not search for anything in the string and increase
- *    value.counter by one.
- */
-cu_match_t *match_create_simple (const char *regex,
-                                const char *excluderegex, int ds_type);
-
-/*
- * NAME
- *  match_destroy
- *
- * DESCRIPTION
- *  Destroys the object and frees all internal resources.
- */
-void match_destroy (cu_match_t *obj);
-
-/*
- * NAME
- *  match_apply
- *
- * DESCRIPTION
- *  Tries to match the string `str' with the regular expression of `obj'. If
- *  the string matches, calls the callback in `obj' with the (sub-)match.
- *
- *  The user_data pointer passed to `match_create_callback' is NOT freed
- *  automatically. The `cu_match_value_t' structure allocated by
- *  `match_create_callback' is freed automatically.
- */
-int match_apply (cu_match_t *obj, const char *str);
-
-/*
- * NAME
- *  match_get_user_data
- *
- * DESCRIPTION
- *  Returns the pointer passed to `match_create_callback' or a pointer to the
- *  `cu_match_value_t' structure allocated by `match_create_simple'.
- */
-void *match_get_user_data (cu_match_t *obj);
-
-#endif /* UTILS_MATCH_H */
-
-/* vim: set sw=2 sts=2 ts=8 : */
index 56e65ea..7f06f29 100644 (file)
@@ -155,55 +155,4 @@ int parse_option (char **ret_buffer, char **ret_key, char **ret_value)
   return (0);
 } /* int parse_option */
 
-int escape_string (char *buffer, size_t buffer_size)
-{
-  char *temp;
-  size_t i;
-  size_t j;
-
-  /* Check if we need to escape at all first */
-  temp = strpbrk (buffer, " \t\"\\");
-  if (temp == NULL)
-    return (0);
-
-  temp = (char *) malloc (buffer_size);
-  if (temp == NULL)
-    return (-1);
-  memset (temp, 0, buffer_size);
-
-  temp[0] = '"';
-  j = 1;
-
-  for (i = 0; i < buffer_size; i++)
-  {
-    if (buffer[i] == 0)
-    {
-      break;
-    }
-    else if ((buffer[i] == '"') || (buffer[i] == '\\'))
-    {
-      if (j > (buffer_size - 4))
-        break;
-      temp[j] = '\\';
-      temp[j + 1] = buffer[i];
-      j += 2;
-    }
-    else
-    {
-      if (j > (buffer_size - 3))
-        break;
-      temp[j] = buffer[i];
-      j++;
-    }
-  }
-
-  assert ((j + 1) < buffer_size);
-  temp[j] = '"';
-  temp[j + 1] = 0;
-
-  sstrncpy (buffer, temp, buffer_size);
-  sfree (temp);
-  return (0);
-} /* int escape_string */
-
 /* vim: set sw=2 ts=8 tw=78 et : */
index 01b73d1..885a6a3 100644 (file)
@@ -30,8 +30,6 @@
 int parse_string (char **ret_buffer, char **ret_string);
 int parse_option (char **ret_buffer, char **ret_key, char **ret_value);
 
-int escape_string (char *buffer, size_t buffer_size);
-
 #endif /* UTILS_PARSE_OPTION */
 
 /* vim: set sw=2 ts=8 tw=78 et : */
diff --git a/src/utils_random.c b/src/utils_random.c
deleted file mode 100644 (file)
index b873845..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * collectd - src/utils_random.c
- * Copyright (C) 2013       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_time.h"
-
-#include <pthread.h>
-
-static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-static _Bool have_seed = 0;
-static unsigned short seed[3];
-
-static void cdrand_seed (void)
-{
-  cdtime_t t;
-
-  if (have_seed)
-    return;
-
-  t = cdtime();
-
-  seed[0] = (unsigned short) t;
-  seed[1] = (unsigned short) (t >> 16);
-  seed[2] = (unsigned short) (t >> 32);
-
-  have_seed = 1;
-}
-
-double cdrand_d (void)
-{
-  double r;
-
-  pthread_mutex_lock (&lock);
-  cdrand_seed ();
-  r = erand48 (seed);
-  pthread_mutex_unlock (&lock);
-
-  return (r);
-}
-
-long cdrand_range (long min, long max)
-{
-  long range;
-  long r;
-
-  range = 1 + max - min;
-
-  r = (long) (0.5 + (cdrand_d () * range));
-  r += min;
-
-  return (r);
-}
diff --git a/src/utils_random.h b/src/utils_random.h
deleted file mode 100644 (file)
index b05f4c8..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * collectd - src/utils_random.h
- * Copyright (C) 2013       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>
- **/
-
-/**
- * Returns a random double value in the range [0..1), i.e. excluding 1.
- *
- * This function is thread- and reentrant-safe.
- */
-double cdrand_d (void);
-
-/**
- * Returns a random long between min and max, inclusively.
- *
- * If min is larger than max, the result may be rounded incorrectly and may be
- * outside the intended range. This function is thread- and reentrant-safe.
- */
-long cdrand_range (long min, long max);
index de74c0a..0e2d86c 100644 (file)
@@ -104,6 +104,7 @@ static void srrd_create_args_destroy (srrd_create_args_t *args)
       sfree (args->argv[i]);
     sfree (args->argv);
   }
+  sfree (args);
 } /* void srrd_create_args_destroy */
 
 static srrd_create_args_t *srrd_create_args_create (const char *filename,
diff --git a/src/utils_subst.c b/src/utils_subst.c
deleted file mode 100644 (file)
index 2f28eb9..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/**
- * collectd - src/utils_subst.c
- * Copyright (C) 2008       Sebastian Harl
- *
- * 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:
- *   Sebastian "tokkee" Harl <sh at tokkee.org>
- **/
-
-/*
- * This module provides functions for string substitution.
- */
-
-#include "collectd.h"
-#include "common.h"
-
-char *subst (char *buf, size_t buflen, const char *string, int off1, int off2,
-               const char *replacement)
-{
-       char  *buf_ptr = buf;
-       size_t len     = buflen;
-
-       if ((NULL == buf) || (0 >= buflen) || (NULL == string)
-                       || (0 > off1) || (0 > off2) || (off1 > off2)
-                       || (NULL == replacement))
-               return NULL;
-
-       sstrncpy (buf_ptr, string,
-                       ((size_t)off1 + 1 > buflen) ? buflen : (size_t)off1 + 1);
-       buf_ptr += off1;
-       len     -= off1;
-
-       if (0 >= len)
-               return buf;
-
-       sstrncpy (buf_ptr, replacement, len);
-       buf_ptr += strlen (replacement);
-       len     -= strlen (replacement);
-
-       if (0 >= len)
-               return buf;
-
-       sstrncpy (buf_ptr, string + off2, len);
-       return buf;
-} /* subst */
-
-char *asubst (const char *string, int off1, int off2, const char *replacement)
-{
-       char *buf;
-       int   len;
-
-       char *ret;
-
-       if ((NULL == string) || (0 > off1) || (0 > off2) || (off1 > off2)
-                       || (NULL ==replacement))
-               return NULL;
-
-       len = off1 + strlen (replacement) + strlen (string) - off2 + 1;
-
-       buf = (char *)malloc (len);
-       if (NULL == buf)
-               return NULL;
-
-       ret = subst (buf, len, string, off1, off2, replacement);
-       if (NULL == ret)
-               free (buf);
-       return ret;
-} /* asubst */
-
-char *subst_string (char *buf, size_t buflen, const char *string,
-               const char *needle, const char *replacement)
-{
-       char *temp;
-       size_t needle_len;
-       size_t i;
-
-       if ((buf == NULL) || (string == NULL)
-                       || (needle == NULL) || (replacement == NULL))
-               return (NULL);
-
-       temp = (char *) malloc (buflen);
-       if (temp == NULL)
-       {
-               ERROR ("subst_string: malloc failed.");
-               return (NULL);
-       }
-
-       needle_len = strlen (needle);
-       strncpy (buf, string, buflen);
-
-       /* Limit the loop to prevent endless loops. */
-       for (i = 0; i < buflen; i++)
-       {
-               char *begin_ptr;
-               size_t begin;
-
-               /* Find `needle' in `buf'. */
-               begin_ptr = strstr (buf, needle);
-               if (begin_ptr == NULL)
-                       break;
-
-               /* Calculate the start offset. */
-               begin = begin_ptr - buf;
-
-               /* Substitute the region using `subst'. The result is stored in
-                * `temp'. */
-               begin_ptr = subst (temp, buflen, buf,
-                               begin, begin + needle_len,
-                               replacement);
-               if (begin_ptr == NULL)
-               {
-                       WARNING ("subst_string: subst failed.");
-                       break;
-               }
-
-               /* Copy the new string in `temp' to `buf' for the next round. */
-               strncpy (buf, temp, buflen);
-       }
-
-       if (i >= buflen)
-       {
-               WARNING ("subst_string: Loop exited after %zu iterations: "
-                               "string = %s; needle = %s; replacement = %s;",
-                               i, string, needle, replacement);
-       }
-
-       sfree (temp);
-       return (buf);
-} /* char *subst_string */
-
-/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
-
diff --git a/src/utils_subst.h b/src/utils_subst.h
deleted file mode 100644 (file)
index 9085286..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * collectd - src/utils_subst.h
- * Copyright (C) 2008       Sebastian Harl
- *
- * 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:
- *   Sebastian "tokkee" Harl <sh at tokkee.org>
- **/
-
-/*
- * This module provides functions for string substitution.
- */
-
-#ifndef UTILS_SUBST_H
-#define UTILS_SUBST_H 1
-
-#include <stddef.h>
-
-/*
- * subst:
- *
- * Replace a substring of a string with the specified replacement text. The
- * resulting string is stored in the buffer pointed to by 'buf' of length
- * 'buflen'. Upon success, the buffer will always be null-terminated. The
- * result may be truncated if the buffer is too small.
- *
- * The substring to be replaces is identified by the two offsets 'off1' and
- * 'off2' where 'off1' specifies the offset to the beginning of the substring
- * and 'off2' specifies the offset to the first byte after the substring.
- *
- * The minimum buffer size to store the complete return value (including the
- * terminating '\0' character) thus has to be:
- * off1 + strlen(replacement) + strlen(string) - off2 + 1
- *
- * Example:
- *
- *             01234567890
- *   string = "foo_____bar"
- *                ^    ^
- *                |    |
- *              off1  off2
- *
- *   off1 = 3
- *   off2 = 8
- *
- *   replacement = " - "
- *
- *   -> "foo - bar"
- *
- * The function returns 'buf' on success, NULL else.
- */
-char *subst (char *buf, size_t buflen, const char *string, int off1, int off2,
-               const char *replacement);
-
-/*
- * asubst:
- *
- * This function is very similar to subst(). It differs in that it
- * automatically allocates the memory required for the return value which the
- * user then has to free himself.
- *
- * Returns the newly allocated result string on success, NULL else.
- */
-char *asubst (const char *string, int off1, int off2, const char *replacement);
-
-/*
- * subst_string:
- *
- * Works like `subst', but instead of specifying start and end offsets you
- * specify `needle', the string that is to be replaced. If `needle' is found
- * in `string' (using strstr(3)), the offset is calculated and `subst' is
- * called with the determined parameters.
- *
- * If the substring is not found, no error will be indicated and
- * `subst_string' works mostly like `strncpy'.
- *
- * If the substring appears multiple times, all appearances will be replaced.
- * If the substring has been found `buflen' times, an endless loop is assumed
- * and the loop is broken. A warning is printed and the function returns
- * success.
- */
-char *subst_string (char *buf, size_t buflen, const char *string,
-               const char *needle, const char *replacement);
-
-#endif /* UTILS_SUBST_H */
-
-/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
-
diff --git a/src/utils_tail.c b/src/utils_tail.c
deleted file mode 100644 (file)
index fe5dca8..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-/**
- * collectd - src/utils_tail.c
- * Copyright (C) 2007-2008  C-Ware, Inc.
- * Copyright (C) 2008       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.
- *
- * Author:
- *   Luke Heberling <lukeh at c-ware.com>
- *   Florian Forster <octo at collectd.org>
- *
- * Description:
- *   Encapsulates useful code for plugins which must watch for appends to
- *   the end of a file.
- **/
-
-#include "collectd.h"
-#include "common.h"
-#include "utils_tail.h"
-
-struct cu_tail_s
-{
-       char  *file;
-       FILE  *fh;
-       struct stat stat;
-};
-
-static int cu_tail_reopen (cu_tail_t *obj)
-{
-  int seek_end = 0;
-  FILE *fh;
-  struct stat stat_buf;
-  int status;
-
-  memset (&stat_buf, 0, sizeof (stat_buf));
-  status = stat (obj->file, &stat_buf);
-  if (status != 0)
-  {
-    char errbuf[1024];
-    ERROR ("utils_tail: stat (%s) failed: %s", obj->file,
-       sstrerror (errno, errbuf, sizeof (errbuf)));
-    return (-1);
-  }
-
-  /* The file is already open.. */
-  if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino))
-  {
-    /* Seek to the beginning if file was truncated */
-    if (stat_buf.st_size < obj->stat.st_size)
-    {
-      INFO ("utils_tail: File `%s' was truncated.", obj->file);
-      status = fseek (obj->fh, 0, SEEK_SET);
-      if (status != 0)
-      {
-       char errbuf[1024];
-       ERROR ("utils_tail: fseek (%s) failed: %s", obj->file,
-           sstrerror (errno, errbuf, sizeof (errbuf)));
-       fclose (obj->fh);
-       obj->fh = NULL;
-       return (-1);
-      }
-    }
-    memcpy (&obj->stat, &stat_buf, sizeof (struct stat));
-    return (1);
-  }
-
-  /* Seek to the end if we re-open the same file again or the file opened
-   * is the first at all or the first after an error */
-  if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino))
-    seek_end = 1;
-
-  fh = fopen (obj->file, "r");
-  if (fh == NULL)
-  {
-    char errbuf[1024];
-    ERROR ("utils_tail: fopen (%s) failed: %s", obj->file,
-       sstrerror (errno, errbuf, sizeof (errbuf)));
-    return (-1);
-  }
-
-  if (seek_end != 0)
-  {
-    status = fseek (fh, 0, SEEK_END);
-    if (status != 0)
-    {
-      char errbuf[1024];
-      ERROR ("utils_tail: fseek (%s) failed: %s", obj->file,
-         sstrerror (errno, errbuf, sizeof (errbuf)));
-      fclose (fh);
-      return (-1);
-    }
-  }
-
-  if (obj->fh != NULL)
-    fclose (obj->fh);
-  obj->fh = fh;
-  memcpy (&obj->stat, &stat_buf, sizeof (struct stat));
-
-  return (0);
-} /* int cu_tail_reopen */
-
-cu_tail_t *cu_tail_create (const char *file)
-{
-       cu_tail_t *obj;
-
-       obj = (cu_tail_t *) malloc (sizeof (cu_tail_t));
-       if (obj == NULL)
-               return (NULL);
-       memset (obj, '\0', sizeof (cu_tail_t));
-
-       obj->file = strdup (file);
-       if (obj->file == NULL)
-       {
-               free (obj);
-               return (NULL);
-       }
-
-       obj->fh = NULL;
-
-       return (obj);
-} /* cu_tail_t *cu_tail_create */
-
-int cu_tail_destroy (cu_tail_t *obj)
-{
-       if (obj->fh != NULL)
-               fclose (obj->fh);
-       free (obj->file);
-       free (obj);
-
-       return (0);
-} /* int cu_tail_destroy */
-
-int cu_tail_readline (cu_tail_t *obj, char *buf, int buflen)
-{
-  int status;
-
-  if (buflen < 1)
-  {
-    ERROR ("utils_tail: cu_tail_readline: buflen too small: %i bytes.",
-       buflen);
-    return (-1);
-  }
-
-  if (obj->fh == NULL)
-  {
-    status = cu_tail_reopen (obj);
-    if (status < 0)
-      return (status);
-  }
-  assert (obj->fh != NULL);
-
-  /* Try to read from the filehandle. If that succeeds, everything appears to
-   * be fine and we can return. */
-  clearerr (obj->fh);
-  if (fgets (buf, buflen, obj->fh) != NULL)
-  {
-    buf[buflen - 1] = 0;
-    return (0);
-  }
-
-  /* Check if we encountered an error */
-  if (ferror (obj->fh) != 0)
-  {
-    /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */
-    fclose (obj->fh);
-    obj->fh = NULL;
-  }
-  /* else: eof -> check if the file was moved away and reopen the new file if
-   * so.. */
-
-  status = cu_tail_reopen (obj);
-  /* error -> return with error */
-  if (status < 0)
-    return (status);
-  /* file end reached and file not reopened -> nothing more to read */
-  else if (status > 0)
-  {
-    buf[0] = 0;
-    return (0);
-  }
-
-  /* If we get here: file was re-opened and there may be more to read.. Let's
-   * try again. */
-  if (fgets (buf, buflen, obj->fh) != NULL)
-  {
-    buf[buflen - 1] = 0;
-    return (0);
-  }
-
-  if (ferror (obj->fh) != 0)
-  {
-    char errbuf[1024];
-    WARNING ("utils_tail: fgets (%s) returned an error: %s", obj->file,
-       sstrerror (errno, errbuf, sizeof (errbuf)));
-    fclose (obj->fh);
-    obj->fh = NULL;
-    return (-1);
-  }
-
-  /* EOf, well, apparently the new file is empty.. */
-  buf[0] = 0;
-  return (0);
-} /* int cu_tail_readline */
-
-int cu_tail_read (cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
-               void *data)
-{
-       int status;
-
-       while (42)
-       {
-               size_t len;
-
-               status = cu_tail_readline (obj, buf, buflen);
-               if (status != 0)
-               {
-                       ERROR ("utils_tail: cu_tail_read: cu_tail_readline "
-                                       "failed.");
-                       break;
-               }
-
-               /* check for EOF */
-               if (buf[0] == 0)
-                       break;
-
-               len = strlen (buf);
-               while (len > 0) {
-                       if (buf[len - 1] != '\n')
-                               break;
-                       buf[len - 1] = '\0';
-                       len--;
-               }
-
-               status = callback (data, buf, buflen);
-               if (status != 0)
-               {
-                       ERROR ("utils_tail: cu_tail_read: callback returned "
-                                       "status %i.", status);
-                       break;
-               }
-       }
-
-       return status;
-} /* int cu_tail_read */
diff --git a/src/utils_tail.h b/src/utils_tail.h
deleted file mode 100644 (file)
index 6fb7013..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * collectd - src/utils_tail.h
- * Copyright (C) 2007-2008  C-Ware, Inc.
- *
- * 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.
- *
- * Author:
- *   Luke Heberling <lukeh at c-ware.com>
- *
- * DESCRIPTION
- *   Facilitates reading information that is appended to a file, taking into
- *   account that the file may be rotated and a new file created under the
- *   same name.
- **/
-
-#ifndef UTILS_TAIL_H
-#define UTILS_TAIL_H 1
-
-struct cu_tail_s;
-typedef struct cu_tail_s cu_tail_t;
-
-typedef int tailfunc_t(void *data, char *buf, int buflen);
-
-/*
- * NAME
- *   cu_tail_create
- *
- * DESCRIPTION
- *   Allocates a new tail object..
- *
- * PARAMETERS
- *   `file'       The name of the file to be tailed.
- */
-cu_tail_t *cu_tail_create (const char *file);
-
-/*
- * cu_tail_destroy
- *
- * Takes a tail object returned by `cu_tail_create' and destroys it, freeing
- * all internal memory.
- *
- * Returns 0 when successful and non-zero otherwise.
- */
-int cu_tail_destroy (cu_tail_t *obj);
-
-/*
- * cu_tail_readline
- *
- * Reads from the file until `buflen' characters are read, a newline
- * character is read, or an eof condition is encountered. `buf' is
- * always null-terminated on successful return and isn't touched when non-zero
- * is returned.
- *
- * You can check if the EOF condition is reached by looking at the buffer: If
- * the length of the string stored in the buffer is zero, EOF occurred.
- * Otherwise at least the newline character will be in the buffer.
- *
- * Returns 0 when successful and non-zero otherwise.
- */
-int cu_tail_readline (cu_tail_t *obj, char *buf, int buflen);
-
-/*
- * cu_tail_readline
- *
- * Reads from the file until eof condition or an error is encountered.
- *
- * Returns 0 when successful and non-zero otherwise.
- */
-int cu_tail_read (cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
-               void *data);
-
-#endif /* UTILS_TAIL_H */
diff --git a/src/utils_tail_match.c b/src/utils_tail_match.c
deleted file mode 100644 (file)
index 8776ad1..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * collectd - src/utils_tail_match.c
- * Copyright (C) 2007-2008  C-Ware, Inc.
- * Copyright (C) 2008       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.
- *
- * Author:
- *   Luke Heberling <lukeh at c-ware.com>
- *   Florian Forster <octo at collectd.org>
- *
- * Description:
- *   Encapsulates useful code to plugins which must parse a log file.
- */
-
-#include "collectd.h"
-#include "common.h"
-#include "plugin.h"
-#include "utils_match.h"
-#include "utils_tail.h"
-#include "utils_tail_match.h"
-
-struct cu_tail_match_simple_s
-{
-  char plugin[DATA_MAX_NAME_LEN];
-  char plugin_instance[DATA_MAX_NAME_LEN];
-  char type[DATA_MAX_NAME_LEN];
-  char type_instance[DATA_MAX_NAME_LEN];
-  cdtime_t interval;
-};
-typedef struct cu_tail_match_simple_s cu_tail_match_simple_t;
-
-struct cu_tail_match_match_s
-{
-  cu_match_t *match;
-  void *user_data;
-  int (*submit) (cu_match_t *match, void *user_data);
-  void (*free) (void *user_data);
-};
-typedef struct cu_tail_match_match_s cu_tail_match_match_t;
-
-struct cu_tail_match_s
-{
-  int flags;
-  cu_tail_t *tail;
-
-  cdtime_t interval;
-  cu_tail_match_match_t *matches;
-  size_t matches_num;
-};
-
-/*
- * Private functions
- */
-static int simple_submit_match (cu_match_t *match, void *user_data)
-{
-  cu_tail_match_simple_t *data = (cu_tail_match_simple_t *) user_data;
-  cu_match_value_t *match_value;
-  value_list_t vl = VALUE_LIST_INIT;
-  value_t values[1];
-
-  match_value = (cu_match_value_t *) match_get_user_data (match);
-  if (match_value == NULL)
-    return (-1);
-
-  if ((match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
-      && (match_value->values_num == 0))
-    values[0].gauge = NAN;
-  else
-    values[0] = match_value->value;
-
-  vl.values = values;
-  vl.values_len = 1;
-  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-  sstrncpy (vl.plugin, data->plugin, sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, data->plugin_instance,
-      sizeof (vl.plugin_instance));
-  sstrncpy (vl.type, data->type, sizeof (vl.type));
-  sstrncpy (vl.type_instance, data->type_instance,
-      sizeof (vl.type_instance));
-
-  vl.interval = data->interval;
-  plugin_dispatch_values (&vl);
-
-  if (match_value->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
-  {
-    match_value->value.gauge = NAN;
-    match_value->values_num = 0;
-  }
-
-  return (0);
-} /* int simple_submit_match */
-
-static int tail_callback (void *data, char *buf,
-    int __attribute__((unused)) buflen)
-{
-  cu_tail_match_t *obj = (cu_tail_match_t *) data;
-  size_t i;
-
-  for (i = 0; i < obj->matches_num; i++)
-    match_apply (obj->matches[i].match, buf);
-
-  return (0);
-} /* int tail_callback */
-
-/*
- * Public functions
- */
-cu_tail_match_t *tail_match_create (const char *filename)
-{
-  cu_tail_match_t *obj;
-
-  obj = (cu_tail_match_t *) malloc (sizeof (cu_tail_match_t));
-  if (obj == NULL)
-    return (NULL);
-  memset (obj, '\0', sizeof (cu_tail_match_t));
-
-  obj->tail = cu_tail_create (filename);
-  if (obj->tail == NULL)
-  {
-    sfree (obj);
-    return (NULL);
-  }
-
-  return (obj);
-} /* cu_tail_match_t *tail_match_create */
-
-void tail_match_destroy (cu_tail_match_t *obj)
-{
-  size_t i;
-
-  if (obj == NULL)
-    return;
-
-  if (obj->tail != NULL)
-  {
-    cu_tail_destroy (obj->tail);
-    obj->tail = NULL;
-  }
-
-  for (i = 0; i < obj->matches_num; i++)
-  {
-    cu_tail_match_match_t *match = obj->matches + i;
-    if (match->match != NULL)
-    {
-      match_destroy (match->match);
-      match->match = NULL;
-    }
-
-    if ((match->user_data != NULL)
-       && (match->free != NULL))
-      (*match->free) (match->user_data);
-    match->user_data = NULL;
-  }
-
-  sfree (obj->matches);
-  sfree (obj);
-} /* void tail_match_destroy */
-
-int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
-    int (*submit_match) (cu_match_t *match, void *user_data),
-    void *user_data,
-    void (*free_user_data) (void *user_data))
-{
-  cu_tail_match_match_t *temp;
-
-  temp = (cu_tail_match_match_t *) realloc (obj->matches,
-      sizeof (cu_tail_match_match_t) * (obj->matches_num + 1));
-  if (temp == NULL)
-    return (-1);
-
-  obj->matches = temp;
-  obj->matches_num++;
-
-  DEBUG ("tail_match_add_match interval %lf", CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval));
-  temp = obj->matches + (obj->matches_num - 1);
-
-  temp->match = match;
-  temp->user_data = user_data;
-  temp->submit = submit_match;
-  temp->free = free_user_data;
-
-  return (0);
-} /* int tail_match_add_match */
-
-int tail_match_add_match_simple (cu_tail_match_t *obj,
-    const char *regex, const char *excluderegex, int ds_type,
-    const char *plugin, const char *plugin_instance,
-    const char *type, const char *type_instance, const cdtime_t interval)
-{
-  cu_match_t *match;
-  cu_tail_match_simple_t *user_data;
-  int status;
-
-  match = match_create_simple (regex, excluderegex, ds_type);
-  if (match == NULL)
-    return (-1);
-
-  user_data = (cu_tail_match_simple_t *) malloc (sizeof (cu_tail_match_simple_t));
-  if (user_data == NULL)
-  {
-    match_destroy (match);
-    return (-1);
-  }
-  memset (user_data, '\0', sizeof (cu_tail_match_simple_t));
-
-  sstrncpy (user_data->plugin, plugin, sizeof (user_data->plugin));
-  if (plugin_instance != NULL)
-    sstrncpy (user_data->plugin_instance, plugin_instance,
-       sizeof (user_data->plugin_instance));
-
-  sstrncpy (user_data->type, type, sizeof (user_data->type));
-  if (type_instance != NULL)
-    sstrncpy (user_data->type_instance, type_instance,
-       sizeof (user_data->type_instance));
-
-  user_data->interval = interval;
-
-  status = tail_match_add_match (obj, match, simple_submit_match,
-      user_data, free);
-
-  if (status != 0)
-  {
-    match_destroy (match);
-    sfree (user_data);
-  }
-
-  return (status);
-} /* int tail_match_add_match_simple */
-
-int tail_match_read (cu_tail_match_t *obj)
-{
-  char buffer[4096];
-  int status;
-  size_t i;
-
-  status = cu_tail_read (obj->tail, buffer, sizeof (buffer), tail_callback,
-      (void *) obj);
-  if (status != 0)
-  {
-    ERROR ("tail_match: cu_tail_read failed.");
-    return (status);
-  }
-
-  for (i = 0; i < obj->matches_num; i++)
-  {
-    cu_tail_match_match_t *lt_match = obj->matches + i;
-
-    if (lt_match->submit == NULL)
-      continue;
-
-    (*lt_match->submit) (lt_match->match, lt_match->user_data);
-  }
-
-  return (0);
-} /* int tail_match_read */
-
-/* vim: set sw=2 sts=2 ts=8 : */
diff --git a/src/utils_tail_match.h b/src/utils_tail_match.h
deleted file mode 100644 (file)
index 0404de2..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * collectd - src/utils_tail_match.h
- * Copyright (C) 2007-2008  C-Ware, Inc.
- * Copyright (C) 2008       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:
- *   Luke Heberling <lukeh at c-ware.com>
- *   Florian Forster <octo at collectd.org>
- *
- * Description:
- *   `tail_match' uses `utils_tail' and `utils_match' to tail a file and try to
- *   match it using several regular expressions. Matches are then passed to
- *   user-provided callback functions or default handlers. This should keep all
- *   of the parsing logic out of the actual plugin, which only operate with
- *   regular expressions.
- */
-
-#include "utils_match.h"
-
-struct cu_tail_match_s;
-typedef struct cu_tail_match_s cu_tail_match_t;
-
-/*
- * NAME
- *   tail_match_create
- *
- * DESCRIPTION
- *   Allocates, initializes and returns a new `cu_tail_match_t' object.
- *
- * PARAMETERS
- *   `filename'  The name to read data from.
- *
- * RETURN VALUE
- *   Returns NULL upon failure, non-NULL otherwise.
- */
-cu_tail_match_t *tail_match_create (const char *filename);
-
-/*
- * NAME
- *   tail_match_destroy
- *
- * DESCRIPTION
- *   Releases resources used by the `cu_tail_match_t' object.
- *
- * PARAMETERS
- *   The object to destroy.
- */
-void tail_match_destroy (cu_tail_match_t *obj);
-
-/*
- * NAME
- *   tail_match_add_match
- *
- * DESCRIPTION
- *   Adds a match, in form of a `cu_match_t' object, to the object.
- *   After data has been read from the logfile (using utils_tail) the callback
- *   function `submit_match' is called with the match object and the user
- *   supplied data.
- *   Please note that his function is called regardless whether this match
- *   matched any lines recently or not.
- *   When `tail_match_destroy' is called the `user_data' pointer is freed using
- *   the `free_user_data' callback - if it is not NULL.
- *   When using this interface the `tail_match' module doesn't dispatch any values
- *   itself - all that has to happen in either the match-callbacks or the
- *   submit_match callback.
- *
- * RETURN VALUE
- *   Zero upon success, non-zero otherwise.
- */
-int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
-    int (*submit_match) (cu_match_t *match, void *user_data),
-    void *user_data,
-    void (*free_user_data) (void *user_data));
-
-/*
- * NAME
- *  tail_match_add_match_simple
- *
- * DESCRIPTION
- *  A simplified version of `tail_match_add_match'. The regular expressen `regex'
- *  must match a number, which is then dispatched according to `ds_type'. See
- *  the `match_create_simple' function in utils_match.h for a description how
- *  this flag effects calculation of a new value.
- *  The values gathered are dispatched by the tail_match module in this case. The
- *  passed `plugin', `plugin_instance', `type', and `type_instance' are
- *  directly used when submitting these values.
- *  With excluderegex it is possible to exlude lines from the match.
- *
- * RETURN VALUE
- *   Zero upon success, non-zero otherwise.
- */
-int tail_match_add_match_simple (cu_tail_match_t *obj,
-    const char *regex, const char *excluderegex, int ds_type,
-    const char *plugin, const char *plugin_instance,
-    const char *type, const char *type_instance, const cdtime_t interval);
-
-/*
- * NAME
- *   tail_match_read
- *
- * DESCRIPTION
- *   This function should be called periodically by plugins. It reads new lines
- *   from the logfile using `utils_tail' and tries to match them using all
- *   added `utils_match' objects.
- *   After all lines have been read and processed, the submit_match callback is
- *   called or, in case of tail_match_add_match_simple, the data is dispatched to
- *   the daemon directly.
- *
- * RETURN VALUE
- *   Zero on success, nonzero on failure.
-*/
-int tail_match_read (cu_tail_match_t *obj);
-
-/* vim: set sw=2 sts=2 ts=8 : */
diff --git a/src/utils_threshold.c b/src/utils_threshold.c
deleted file mode 100644 (file)
index 4a8df89..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/**
- * collectd - src/utils_threshold.c
- * Copyright (C) 2014       Pierre-Yves Ritschard
- *
- * 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:
- *   Pierre-Yves Ritschard <pyr at spootnik.org>
- **/
-
-#include "collectd.h"
-#include "common.h"
-#include "utils_avltree.h"
-#include "utils_threshold.h"
-
-#include <pthread.h>
-
-/*
- * Exported symbols
- * {{{ */
-c_avl_tree_t   *threshold_tree = NULL;
-pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
-/* }}} */
-
-/*
- * threshold_t *threshold_get
- *
- * Retrieve one specific threshold configuration. For looking up a threshold
- * matching a value_list_t, see "threshold_search" below. Returns NULL if the
- * specified threshold doesn't exist.
- */
-threshold_t *threshold_get (const char *hostname,
-    const char *plugin, const char *plugin_instance,
-    const char *type, const char *type_instance)
-{ /* {{{ */
-  char name[6 * DATA_MAX_NAME_LEN];
-  threshold_t *th = NULL;
-
-  format_name (name, sizeof (name),
-      (hostname == NULL) ? "" : hostname,
-      (plugin == NULL) ? "" : plugin, plugin_instance,
-      (type == NULL) ? "" : type, type_instance);
-  name[sizeof (name) - 1] = '\0';
-
-  if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
-    return (th);
-  else
-    return (NULL);
-} /* }}} threshold_t *threshold_get */
-
-/*
- * threshold_t *threshold_search
- *
- * Searches for a threshold configuration using all the possible variations of
- * "Host", "Plugin" and "Type" blocks. Returns NULL if no threshold could be
- * found.
- * XXX: This is likely the least efficient function in collectd.
- */
-threshold_t *threshold_search (const value_list_t *vl)
-{ /* {{{ */
-  threshold_t *th;
-
-  if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
-         vl->type, NULL)) != NULL)
-    return (th);
-  else if ((th = threshold_get (vl->host, vl->plugin, NULL,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get (vl->host, vl->plugin, NULL,
-         vl->type, NULL)) != NULL)
-    return (th);
-  else if ((th = threshold_get (vl->host, "", NULL,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get (vl->host, "", NULL,
-         vl->type, NULL)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
-         vl->type, NULL)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", vl->plugin, NULL,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", vl->plugin, NULL,
-         vl->type, NULL)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", "", NULL,
-         vl->type, vl->type_instance)) != NULL)
-    return (th);
-  else if ((th = threshold_get ("", "", NULL,
-         vl->type, NULL)) != NULL)
-    return (th);
-
-  return (NULL);
-} /* }}} threshold_t *threshold_search */
-
-int ut_search_threshold (const value_list_t *vl, /* {{{ */
-    threshold_t *ret_threshold)
-{
-  threshold_t *t;
-
-  if (vl == NULL)
-    return (EINVAL);
-
-       /* Is this lock really necessary? */
-       pthread_mutex_lock (&threshold_lock);
-  t = threshold_search (vl);
-  if (t == NULL) {
-               pthread_mutex_unlock (&threshold_lock);
-    return (ENOENT);
-       }
-
-  memcpy (ret_threshold, t, sizeof (*ret_threshold));
-       pthread_mutex_unlock (&threshold_lock);
-
-  ret_threshold->next = NULL;
-
-  return (0);
-} /* }}} int ut_search_threshold */
-
-
diff --git a/src/utils_threshold.h b/src/utils_threshold.h
deleted file mode 100644 (file)
index bf097fa..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * collectd - src/utils_threshold.h
- * Copyright (C) 2014       Pierre-Yves Ritschard
- *
- * 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:
- *   Pierre-Yves Ritschard <pyr at spootnik.org>
- **/
-
-#ifndef UTILS_THRESHOLD_H
-#define UTILS_THRESHOLD_H 1
-
-#define UT_FLAG_INVERT  0x01
-#define UT_FLAG_PERSIST 0x02
-#define UT_FLAG_PERCENTAGE 0x04
-#define UT_FLAG_INTERESTING 0x08
-#define UT_FLAG_PERSIST_OK 0x10
-typedef struct threshold_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];
-  char data_source[DATA_MAX_NAME_LEN];
-  gauge_t warning_min;
-  gauge_t warning_max;
-  gauge_t failure_min;
-  gauge_t failure_max;
-  gauge_t hysteresis;
-  unsigned int flags;
-  int hits;
-  struct threshold_s *next;
-} threshold_t;
-
-extern c_avl_tree_t   *threshold_tree;
-extern pthread_mutex_t threshold_lock;
-
-threshold_t *threshold_get (const char *hostname,
-    const char *plugin, const char *plugin_instance,
-    const char *type, const char *type_instance);
-
-threshold_t *threshold_search (const value_list_t *vl);
-
-int ut_search_threshold (const value_list_t *vl, 
-  threshold_t *ret_threshold);
-
-#endif /* UTILS_THRESHOLD_H */
-
-/* vim: set sw=2 sts=2 ts=8 : */
diff --git a/src/utils_time.c b/src/utils_time.c
deleted file mode 100644 (file)
index 6603c15..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * collectd - src/utils_time.c
- * Copyright (C) 2010       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 <ff at octo.it>
- **/
-
-#include "collectd.h"
-#include "utils_time.h"
-#include "plugin.h"
-#include "common.h"
-
-#if HAVE_CLOCK_GETTIME
-cdtime_t cdtime (void) /* {{{ */
-{
-  int status;
-  struct timespec ts = { 0, 0 };
-
-  status = clock_gettime (CLOCK_REALTIME, &ts);
-  if (status != 0)
-  {
-    char errbuf[1024];
-    ERROR ("cdtime: clock_gettime failed: %s",
-        sstrerror (errno, errbuf, sizeof (errbuf)));
-    return (0);
-  }
-
-  return (TIMESPEC_TO_CDTIME_T (&ts));
-} /* }}} cdtime_t cdtime */
-#else
-/* Work around for Mac OS X which doesn't have clock_gettime(2). *sigh* */
-cdtime_t cdtime (void) /* {{{ */
-{
-  int status;
-  struct timeval tv = { 0, 0 };
-
-  status = gettimeofday (&tv, /* struct timezone = */ NULL);
-  if (status != 0)
-  {
-    char errbuf[1024];
-    ERROR ("cdtime: gettimeofday failed: %s",
-        sstrerror (errno, errbuf, sizeof (errbuf)));
-    return (0);
-  }
-
-  return (TIMEVAL_TO_CDTIME_T (&tv));
-} /* }}} 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 : */
diff --git a/src/utils_time.h b/src/utils_time.h
deleted file mode 100644 (file)
index 9b08e8e..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * collectd - src/utils_time.h
- * Copyright (C) 2010       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 <ff at octo.it>
- **/
-
-#ifndef UTILS_TIME_H
-#define UTILS_TIME_H 1
-
-#include "collectd.h"
-
-/*
- * "cdtime_t" is a 64bit unsigned integer. The time is stored at a 2^-30 second
- * resolution, i.e. the most significant 34 bit are used to store the time in
- * seconds, the least significant bits store the sub-second part in something
- * very close to nanoseconds. *The* big advantage of storing time in this
- * manner is that comparing times and calculating differences is as simple as
- * it is with "time_t", i.e. a simple integer comparison / subtraction works.
- */
-/* 
- * cdtime_t is defined in "collectd.h" */
-/* typedef uint64_t cdtime_t; */
-
-/* 2^30 = 1073741824 */
-#define TIME_T_TO_CDTIME_T(t) (((cdtime_t) (t)) * 1073741824)
-#define CDTIME_T_TO_TIME_T(t) ((time_t) ((t) / 1073741824))
-
-#define CDTIME_T_TO_DOUBLE(t) (((double) (t)) / 1073741824.0)
-#define DOUBLE_TO_CDTIME_T(d) ((cdtime_t) ((d) * 1073741824.0))
-
-#define MS_TO_CDTIME_T(ms) ((cdtime_t)    (((double) (ms)) * 1073741.824))
-#define CDTIME_T_TO_MS(t)  ((long)        (((double) (t))  / 1073741.824))
-#define US_TO_CDTIME_T(us) ((cdtime_t)    (((double) (us)) * 1073.741824))
-#define CDTIME_T_TO_US(t)  ((suseconds_t) (((double) (t))  / 1073.741824))
-#define NS_TO_CDTIME_T(ns) ((cdtime_t)    (((double) (ns)) * 1.073741824))
-#define CDTIME_T_TO_NS(t)  ((long)        (((double) (t))  / 1.073741824))
-
-#define CDTIME_T_TO_TIMEVAL(cdt,tvp) do {                                    \
-        (tvp)->tv_sec = CDTIME_T_TO_TIME_T (cdt);                            \
-        (tvp)->tv_usec = CDTIME_T_TO_US ((cdt) % 1073741824);                \
-} while (0)
-#define TIMEVAL_TO_CDTIME_T(tv) (TIME_T_TO_CDTIME_T ((tv)->tv_sec)           \
-    + US_TO_CDTIME_T ((tv)->tv_usec))
-
-#define CDTIME_T_TO_TIMESPEC(cdt,tsp) do {                                   \
-  (tsp)->tv_sec = CDTIME_T_TO_TIME_T (cdt);                                  \
-  (tsp)->tv_nsec = CDTIME_T_TO_NS ((cdt) % 1073741824);                      \
-} while (0)
-#define TIMESPEC_TO_CDTIME_T(ts) (TIME_T_TO_CDTIME_T ((ts)->tv_sec)           \
-    + NS_TO_CDTIME_T ((ts)->tv_nsec))
-
-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/virt.c b/src/virt.c
new file mode 100644 (file)
index 0000000..6118c0f
--- /dev/null
@@ -0,0 +1,1004 @@
+/**
+ * collectd - src/virt.c
+ * Copyright (C) 2006-2008  Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the license is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Richard W.M. Jones <rjones@redhat.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_ignorelist.h"
+#include "utils_complain.h"
+
+#include <libvirt/libvirt.h>
+#include <libvirt/virterror.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+/* Plugin name */
+#define PLUGIN_NAME "virt"
+
+static const char *config_keys[] = {
+    "Connection",
+
+    "RefreshInterval",
+
+    "Domain",
+    "BlockDevice",
+    "InterfaceDevice",
+    "IgnoreSelected",
+
+    "HostnameFormat",
+    "InterfaceFormat",
+
+    "PluginInstanceFormat",
+
+    NULL
+};
+#define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
+
+/* Connection. */
+static virConnectPtr conn = 0;
+static char *conn_string = NULL;
+static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
+
+/* Seconds between list refreshes, 0 disables completely. */
+static int interval = 60;
+
+/* List of domains, if specified. */
+static ignorelist_t *il_domains = NULL;
+/* List of block devices, if specified. */
+static ignorelist_t *il_block_devices = NULL;
+/* List of network interface devices, if specified. */
+static ignorelist_t *il_interface_devices = NULL;
+
+static int ignore_device_match (ignorelist_t *,
+                                const char *domname, const char *devpath);
+
+/* Actual list of domains found on last refresh. */
+static virDomainPtr *domains = NULL;
+static int nr_domains = 0;
+
+static void free_domains (void);
+static int add_domain (virDomainPtr dom);
+
+/* Actual list of block devices found on last refresh. */
+struct block_device {
+    virDomainPtr dom;           /* domain */
+    char *path;                 /* name of block device */
+};
+
+static struct block_device *block_devices = NULL;
+static int nr_block_devices = 0;
+
+static void free_block_devices (void);
+static int add_block_device (virDomainPtr dom, const char *path);
+
+/* Actual list of network interfaces found on last refresh. */
+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, unsigned int number);
+
+/* HostnameFormat. */
+#define HF_MAX_FIELDS 3
+
+enum hf_field {
+    hf_none = 0,
+    hf_hostname,
+    hf_name,
+    hf_uuid
+};
+
+static enum hf_field hostname_format[HF_MAX_FIELDS] =
+    { hf_name };
+
+/* PluginInstanceFormat */
+#define PLGINST_MAX_FIELDS 2
+
+enum plginst_field {
+    plginst_none = 0,
+    plginst_name,
+    plginst_uuid
+};
+
+static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] =
+    { plginst_name };
+
+/* InterfaceFormat. */
+enum if_field {
+    if_address,
+    if_name,
+    if_number
+};
+
+static enum if_field interface_format = if_name;
+
+/* Time that we last refreshed. */
+static time_t last_refresh = (time_t) 0;
+
+static int refresh_lists (void);
+
+/* ERROR(...) macro for virterrors. */
+#define VIRT_ERROR(conn,s) do {                 \
+        virErrorPtr err;                        \
+        err = (conn) ? virConnGetLastError ((conn)) : virGetLastError (); \
+        if (err) ERROR ("%s: %s", (s), err->message);                   \
+    } while(0)
+
+static void
+init_value_list (value_list_t *vl, virDomainPtr dom)
+{
+    int i, n;
+    const char *name;
+    char uuid[VIR_UUID_STRING_BUFLEN];
+
+    sstrncpy (vl->plugin, PLUGIN_NAME, sizeof (vl->plugin));
+
+    vl->host[0] = '\0';
+
+    /* Construct the hostname field according to HostnameFormat. */
+    for (i = 0; i < HF_MAX_FIELDS; ++i) {
+        if (hostname_format[i] == hf_none)
+            continue;
+
+        n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2;
+
+        if (i > 0 && n >= 1) {
+            strncat (vl->host, ":", 1);
+            n--;
+        }
+
+        switch (hostname_format[i]) {
+        case hf_none: break;
+        case hf_hostname:
+            strncat (vl->host, hostname_g, n);
+            break;
+        case hf_name:
+            name = virDomainGetName (dom);
+            if (name)
+                strncat (vl->host, name, n);
+            break;
+        case hf_uuid:
+            if (virDomainGetUUIDString (dom, uuid) == 0)
+                strncat (vl->host, uuid, n);
+            break;
+        }
+    }
+
+    vl->host[sizeof (vl->host) - 1] = '\0';
+
+    /* Construct the plugin instance field according to PluginInstanceFormat. */
+    for (i = 0; i < PLGINST_MAX_FIELDS; ++i) {
+        if (plugin_instance_format[i] == plginst_none)
+            continue;
+
+        n = sizeof(vl->plugin_instance) - strlen (vl->plugin_instance) - 2;
+
+        if (i > 0 && n >= 1) {
+            strncat (vl->plugin_instance, ":", 1);
+            n--;
+        }
+
+        switch (plugin_instance_format[i]) {
+        case plginst_none: break;
+        case plginst_name:
+            name = virDomainGetName (dom);
+            if (name)
+                strncat (vl->plugin_instance, name, n);
+            break;
+        case plginst_uuid:
+            if (virDomainGetUUIDString (dom, uuid) == 0)
+                strncat (vl->plugin_instance, uuid, n);
+            break;
+        }
+    }
+
+    vl->plugin_instance[sizeof (vl->plugin_instance) - 1] = '\0';
+
+} /* void init_value_list */
+
+static void
+memory_submit (gauge_t memory, virDomainPtr dom)
+{
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].gauge = memory;
+
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, "memory", sizeof (vl.type));
+    sstrncpy (vl.type_instance, "total", sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
+memory_stats_submit (gauge_t memory, virDomainPtr dom, int tag_index)
+{
+    static const char *tags[] = { "swap_in", "swap_out", "major_fault", "minor_fault",
+                                    "unused", "available", "actual_balloon", "rss"};
+
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].gauge = memory;
+
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, "memory", sizeof (vl.type));
+    sstrncpy (vl.type_instance, tags[tag_index], sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
+cpu_submit (unsigned long long cpu_time,
+            virDomainPtr dom, const char *type)
+{
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].derive = cpu_time;
+
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, type, sizeof (vl.type));
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
+vcpu_submit (derive_t cpu_time,
+             virDomainPtr dom, int vcpu_nr, const char *type)
+{
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].derive = cpu_time;
+    vl.values = values;
+    vl.values_len = 1;
+
+    sstrncpy (vl.type, type, sizeof (vl.type));
+    ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
+
+    plugin_dispatch_values (&vl);
+}
+
+static void
+submit_derive2 (const char *type, derive_t v0, derive_t v1,
+             virDomainPtr dom, const char *devname)
+{
+    value_t values[2];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    init_value_list (&vl, dom);
+
+    values[0].derive = v0;
+    values[1].derive = v1;
+    vl.values = values;
+    vl.values_len = 2;
+
+    sstrncpy (vl.type, type, sizeof (vl.type));
+    sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+} /* void submit_derive2 */
+
+static int
+lv_init (void)
+{
+    if (virInitialize () != 0)
+        return -1;
+
+       return 0;
+}
+
+static int
+lv_config (const char *key, const char *value)
+{
+    if (virInitialize () != 0)
+        return 1;
+
+    if (il_domains == NULL)
+        il_domains = ignorelist_create (1);
+    if (il_block_devices == NULL)
+        il_block_devices = ignorelist_create (1);
+    if (il_interface_devices == NULL)
+        il_interface_devices = ignorelist_create (1);
+
+    if (strcasecmp (key, "Connection") == 0) {
+        char *tmp = strdup (value);
+        if (tmp == NULL) {
+            ERROR (PLUGIN_NAME " plugin: Connection strdup failed.");
+            return 1;
+        }
+        sfree (conn_string);
+        conn_string = tmp;
+        return 0;
+    }
+
+    if (strcasecmp (key, "RefreshInterval") == 0) {
+        char *eptr = NULL;
+        interval = strtol (value, &eptr, 10);
+        if (eptr == NULL || *eptr != '\0') return 1;
+        return 0;
+    }
+
+    if (strcasecmp (key, "Domain") == 0) {
+        if (ignorelist_add (il_domains, value)) return 1;
+        return 0;
+    }
+    if (strcasecmp (key, "BlockDevice") == 0) {
+        if (ignorelist_add (il_block_devices, value)) return 1;
+        return 0;
+    }
+    if (strcasecmp (key, "InterfaceDevice") == 0) {
+        if (ignorelist_add (il_interface_devices, value)) return 1;
+        return 0;
+    }
+
+    if (strcasecmp (key, "IgnoreSelected") == 0) {
+        if (IS_TRUE (value))
+        {
+            ignorelist_set_invert (il_domains, 0);
+            ignorelist_set_invert (il_block_devices, 0);
+            ignorelist_set_invert (il_interface_devices, 0);
+        }
+        else
+        {
+            ignorelist_set_invert (il_domains, 1);
+            ignorelist_set_invert (il_block_devices, 1);
+            ignorelist_set_invert (il_interface_devices, 1);
+        }
+        return 0;
+    }
+
+    if (strcasecmp (key, "HostnameFormat") == 0) {
+        char *value_copy;
+        char *fields[HF_MAX_FIELDS];
+        int i, n;
+
+        value_copy = strdup (value);
+        if (value_copy == NULL) {
+            ERROR (PLUGIN_NAME " plugin: strdup failed.");
+            return -1;
+        }
+
+        n = strsplit (value_copy, fields, HF_MAX_FIELDS);
+        if (n < 1) {
+            sfree (value_copy);
+            ERROR (PLUGIN_NAME " plugin: HostnameFormat: no fields");
+            return -1;
+        }
+
+        for (i = 0; i < n; ++i) {
+            if (strcasecmp (fields[i], "hostname") == 0)
+                hostname_format[i] = hf_hostname;
+            else if (strcasecmp (fields[i], "name") == 0)
+                hostname_format[i] = hf_name;
+            else if (strcasecmp (fields[i], "uuid") == 0)
+                hostname_format[i] = hf_uuid;
+            else {
+                sfree (value_copy);
+                ERROR (PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", fields[i]);
+                return -1;
+            }
+        }
+        sfree (value_copy);
+
+        for (i = n; i < HF_MAX_FIELDS; ++i)
+            hostname_format[i] = hf_none;
+
+        return 0;
+    }
+
+    if (strcasecmp (key, "PluginInstanceFormat") == 0) {
+        char *value_copy;
+        char *fields[PLGINST_MAX_FIELDS];
+        int i, n;
+
+        value_copy = strdup (value);
+        if (value_copy == NULL) {
+            ERROR (PLUGIN_NAME " plugin: strdup failed.");
+            return -1;
+        }
+
+        n = strsplit (value_copy, fields, PLGINST_MAX_FIELDS);
+        if (n < 1) {
+            sfree (value_copy);
+            ERROR (PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
+            return -1;
+        }
+
+        for (i = 0; i < n; ++i) {
+            if (strcasecmp (fields[i], "name") == 0)
+                plugin_instance_format[i] = plginst_name;
+            else if (strcasecmp (fields[i], "uuid") == 0)
+                plugin_instance_format[i] = plginst_uuid;
+            else {
+                sfree (value_copy);
+                ERROR (PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", fields[i]);
+                return -1;
+            }
+        }
+        sfree (value_copy);
+
+        for (i = n; i < PLGINST_MAX_FIELDS; ++i)
+            plugin_instance_format[i] = plginst_none;
+
+        return 0;
+    }
+
+    if (strcasecmp (key, "InterfaceFormat") == 0) {
+        if (strcasecmp (value, "name") == 0)
+            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 (PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", value);
+            return -1;
+        }
+        return 0;
+    }
+
+    /* Unrecognised option. */
+    return -1;
+}
+
+static int
+lv_read (void)
+{
+    time_t t;
+    int i;
+
+    if (conn == NULL) {
+        /* `conn_string == NULL' is acceptable. */
+        conn = virConnectOpenReadOnly (conn_string);
+        if (conn == NULL) {
+            c_complain (LOG_ERR, &conn_complain,
+                    PLUGIN_NAME " plugin: Unable to connect: "
+                    "virConnectOpenReadOnly failed.");
+            return -1;
+        }
+    }
+    c_release (LOG_NOTICE, &conn_complain,
+            PLUGIN_NAME " plugin: Connection established.");
+
+    time (&t);
+
+    /* Need to refresh domain or device lists? */
+    if ((last_refresh == (time_t) 0) ||
+            ((interval > 0) && ((last_refresh + interval) <= t))) {
+        if (refresh_lists () != 0) {
+            if (conn != NULL)
+                virConnectClose (conn);
+            conn = NULL;
+            return -1;
+        }
+        last_refresh = t;
+    }
+
+#if 0
+    for (i = 0; i < nr_domains; ++i)
+        fprintf (stderr, "domain %s\n", virDomainGetName (domains[i]));
+    for (i = 0; i < nr_block_devices; ++i)
+        fprintf  (stderr, "block device %d %s:%s\n",
+                  i, virDomainGetName (block_devices[i].dom),
+                  block_devices[i].path);
+    for (i = 0; i < nr_interface_devices; ++i)
+        fprintf (stderr, "interface device %d %s:%s\n",
+                 i, virDomainGetName (interface_devices[i].dom),
+                 interface_devices[i].path);
+#endif
+
+    /* Get CPU usage, memory, VCPU usage for each domain. */
+    for (i = 0; i < nr_domains; ++i) {
+        virDomainInfo info;
+        virVcpuInfoPtr vinfo = NULL;
+        virDomainMemoryStatPtr minfo = NULL;
+        int status;
+        int j;
+
+        status = virDomainGetInfo (domains[i], &info);
+        if (status != 0)
+        {
+            ERROR (PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
+                    status);
+            continue;
+        }
+
+        cpu_submit (info.cpuTime, domains[i], "virt_cpu_total");
+        memory_submit ((gauge_t) info.memory * 1024, domains[i]);
+
+        vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0]));
+        if (vinfo == NULL) {
+            ERROR (PLUGIN_NAME " plugin: malloc failed.");
+            continue;
+        }
+
+        status = virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu,
+                /* cpu map = */ NULL, /* cpu map length = */ 0);
+        if (status < 0)
+        {
+            ERROR (PLUGIN_NAME " plugin: virDomainGetVcpus failed with status %i.",
+                    status);
+            sfree (vinfo);
+            continue;
+        }
+
+        for (j = 0; j < info.nrVirtCpu; ++j)
+            vcpu_submit (vinfo[j].cpuTime,
+                    domains[i], vinfo[j].number, "virt_vcpu");
+
+        sfree (vinfo);
+
+        minfo = malloc (VIR_DOMAIN_MEMORY_STAT_NR * sizeof (virDomainMemoryStatStruct));
+        if (minfo == NULL) {
+            ERROR ("virt plugin: malloc failed.");
+            continue;
+        }
+
+        status =  virDomainMemoryStats (domains[i], minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
+
+        if (status < 0) {
+            ERROR ("virt plugin: virDomainMemoryStats failed with status %i.",
+                    status);
+            sfree (minfo);
+            continue;
+        }
+
+        for (j = 0; j < status; j++) {
+            memory_stats_submit ((gauge_t) minfo[j].val, domains[i], minfo[j].tag);
+        }
+
+        sfree (minfo);
+    }
+
+
+    /* Get block device stats for each domain. */
+    for (i = 0; i < nr_block_devices; ++i) {
+        struct _virDomainBlockStats stats;
+
+        if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path,
+                    &stats, sizeof stats) != 0)
+            continue;
+
+        if ((stats.rd_req != -1) && (stats.wr_req != -1))
+            submit_derive2 ("disk_ops",
+                    (derive_t) stats.rd_req, (derive_t) stats.wr_req,
+                    block_devices[i].dom, block_devices[i].path);
+
+        if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
+            submit_derive2 ("disk_octets",
+                    (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes,
+                    block_devices[i].dom, block_devices[i].path);
+    } /* for (nr_block_devices) */
+
+    /* Get interface stats for each domain. */
+    for (i = 0; i < nr_interface_devices; ++i) {
+        struct _virDomainInterfaceStats stats;
+        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,
+                    &stats, sizeof stats) != 0)
+            continue;
+
+       if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
+           submit_derive2 ("if_octets",
+                   (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes,
+                   interface_devices[i].dom, display_name);
+
+       if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
+           submit_derive2 ("if_packets",
+                   (derive_t) stats.rx_packets, (derive_t) stats.tx_packets,
+                   interface_devices[i].dom, display_name);
+
+       if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
+           submit_derive2 ("if_errors",
+                   (derive_t) stats.rx_errs, (derive_t) stats.tx_errs,
+                   interface_devices[i].dom, display_name);
+
+       if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
+           submit_derive2 ("if_dropped",
+                   (derive_t) stats.rx_drop, (derive_t) stats.tx_drop,
+                   interface_devices[i].dom, display_name);
+    } /* for (nr_interface_devices) */
+
+    return 0;
+}
+
+static int
+refresh_lists (void)
+{
+    int n;
+
+    n = virConnectNumOfDomains (conn);
+    if (n < 0) {
+        VIRT_ERROR (conn, "reading number of domains");
+        return -1;
+    }
+
+    if (n > 0) {
+        int i;
+        int *domids;
+
+        /* Get list of domains. */
+        domids = malloc (sizeof (int) * n);
+        if (domids == 0) {
+            ERROR (PLUGIN_NAME " plugin: malloc failed.");
+            return -1;
+        }
+
+        n = virConnectListDomains (conn, domids, n);
+        if (n < 0) {
+            VIRT_ERROR (conn, "reading list of domains");
+            sfree (domids);
+            return -1;
+        }
+
+        free_block_devices ();
+        free_interface_devices ();
+        free_domains ();
+
+        /* Fetch each domain and add it to the list, unless ignore. */
+        for (i = 0; i < n; ++i) {
+            virDomainPtr dom = NULL;
+            const char *name;
+            char *xml = NULL;
+            xmlDocPtr xml_doc = NULL;
+            xmlXPathContextPtr xpath_ctx = NULL;
+            xmlXPathObjectPtr xpath_obj = NULL;
+            int j;
+
+            dom = virDomainLookupByID (conn, domids[i]);
+            if (dom == NULL) {
+                VIRT_ERROR (conn, "virDomainLookupByID");
+                /* Could be that the domain went away -- ignore it anyway. */
+                continue;
+            }
+
+            name = virDomainGetName (dom);
+            if (name == NULL) {
+                VIRT_ERROR (conn, "virDomainGetName");
+                goto cont;
+            }
+
+            if (il_domains && ignorelist_match (il_domains, name) != 0)
+                goto cont;
+
+            if (add_domain (dom) < 0) {
+                ERROR (PLUGIN_NAME " plugin: malloc failed.");
+                goto cont;
+            }
+
+            /* Get a list of devices for this domain. */
+            xml = virDomainGetXMLDesc (dom, 0);
+            if (!xml) {
+                VIRT_ERROR (conn, "virDomainGetXMLDesc");
+                goto cont;
+            }
+
+            /* Yuck, XML.  Parse out the devices. */
+            xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET);
+            if (xml_doc == NULL) {
+                VIRT_ERROR (conn, "xmlReadDoc");
+                goto cont;
+            }
+
+            xpath_ctx = xmlXPathNewContext (xml_doc);
+
+            /* Block devices. */
+            xpath_obj = xmlXPathEval
+                ((xmlChar *) "/domain/devices/disk/target[@dev]",
+                 xpath_ctx);
+            if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
+                xpath_obj->nodesetval == NULL)
+                goto cont;
+
+            for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
+                xmlNodePtr node;
+                char *path = NULL;
+
+                node = xpath_obj->nodesetval->nodeTab[j];
+                if (!node) continue;
+                path = (char *) xmlGetProp (node, (xmlChar *) "dev");
+                if (!path) continue;
+
+                if (il_block_devices &&
+                    ignore_device_match (il_block_devices, name, path) != 0)
+                    goto cont2;
+
+                add_block_device (dom, path);
+            cont2:
+                if (path) xmlFree (path);
+            }
+            xmlXPathFreeObject (xpath_obj);
+
+            /* Network interfaces. */
+            xpath_obj = xmlXPathEval
+                ((xmlChar *) "/domain/devices/interface[target[@dev]]",
+                 xpath_ctx);
+            if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
+                xpath_obj->nodesetval == NULL)
+                goto cont;
+
+            xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
+
+            for (j = 0; j < xml_interfaces->nodeNr; ++j) {
+                char *path = NULL;
+                char *address = NULL;
+                xmlNodePtr xml_interface;
+
+                xml_interface = xml_interfaces->nodeTab[j];
+                if (!xml_interface) continue;
+                xmlNodePtr child = NULL;
+
+                for (child = xml_interface->children; child; child = child->next) {
+                    if (child->type != XML_ELEMENT_NODE) continue;
+
+                    if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
+                        path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
+                        if (!path) continue;
+                    } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
+                        address = (char *) xmlGetProp (child, (const xmlChar *) "address");
+                        if (!address) continue;
+                    }
+                }
+
+                if (il_interface_devices &&
+                    (ignore_device_match (il_interface_devices, name, path) != 0 ||
+                     ignore_device_match (il_interface_devices, name, address) != 0))
+                    goto cont3;
+
+                add_interface_device (dom, path, address, j+1);
+                cont3:
+                    if (path) xmlFree (path);
+                    if (address) xmlFree (address);
+            }
+
+        cont:
+            if (xpath_obj) xmlXPathFreeObject (xpath_obj);
+            if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
+            if (xml_doc) xmlFreeDoc (xml_doc);
+            sfree (xml);
+        }
+
+        sfree (domids);
+    }
+
+    return 0;
+}
+
+static void
+free_domains ()
+{
+    int i;
+
+    if (domains) {
+        for (i = 0; i < nr_domains; ++i)
+            virDomainFree (domains[i]);
+        sfree (domains);
+    }
+    domains = NULL;
+    nr_domains = 0;
+}
+
+static int
+add_domain (virDomainPtr dom)
+{
+    virDomainPtr *new_ptr;
+    int new_size = sizeof (domains[0]) * (nr_domains+1);
+
+    if (domains)
+        new_ptr = realloc (domains, new_size);
+    else
+        new_ptr = malloc (new_size);
+
+    if (new_ptr == NULL)
+        return -1;
+
+    domains = new_ptr;
+    domains[nr_domains] = dom;
+    return nr_domains++;
+}
+
+static void
+free_block_devices ()
+{
+    int i;
+
+    if (block_devices) {
+        for (i = 0; i < nr_block_devices; ++i)
+            sfree (block_devices[i].path);
+        sfree (block_devices);
+    }
+    block_devices = NULL;
+    nr_block_devices = 0;
+}
+
+static int
+add_block_device (virDomainPtr dom, const char *path)
+{
+    struct block_device *new_ptr;
+    int new_size = sizeof (block_devices[0]) * (nr_block_devices+1);
+    char *path_copy;
+
+    path_copy = strdup (path);
+    if (!path_copy)
+        return -1;
+
+    if (block_devices)
+        new_ptr = realloc (block_devices, new_size);
+    else
+        new_ptr = malloc (new_size);
+
+    if (new_ptr == NULL) {
+        sfree (path_copy);
+        return -1;
+    }
+    block_devices = new_ptr;
+    block_devices[nr_block_devices].dom = dom;
+    block_devices[nr_block_devices].path = path_copy;
+    return nr_block_devices++;
+}
+
+static void
+free_interface_devices ()
+{
+    int i;
+
+    if (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);
+    }
+    interface_devices = NULL;
+    nr_interface_devices = 0;
+}
+
+static int
+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, number_string[15];
+
+    path_copy = strdup (path);
+    if (!path_copy) return -1;
+
+    address_copy = strdup (address);
+    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);
+    else
+        new_ptr = malloc (new_size);
+
+    if (new_ptr == NULL) {
+        sfree (path_copy);
+        sfree (address_copy);
+        return -1;
+    }
+    interface_devices = new_ptr;
+    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++;
+}
+
+static int
+ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
+{
+    char *name;
+    int n, r;
+
+    n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2);
+    name = malloc (n);
+    if (name == NULL) {
+        ERROR (PLUGIN_NAME " plugin: malloc failed.");
+        return 0;
+    }
+    ssnprintf (name, n, "%s:%s", domname, devpath);
+    r = ignorelist_match (il, name);
+    sfree (name);
+    return r;
+}
+
+static int
+lv_shutdown (void)
+{
+    free_block_devices ();
+    free_interface_devices ();
+    free_domains ();
+
+    if (conn != NULL)
+        virConnectClose (conn);
+    conn = NULL;
+
+    ignorelist_free (il_domains);
+    il_domains = NULL;
+    ignorelist_free (il_block_devices);
+    il_block_devices = NULL;
+    ignorelist_free (il_interface_devices);
+    il_interface_devices = NULL;
+
+    return 0;
+}
+
+void
+module_register (void)
+{
+    plugin_register_config (PLUGIN_NAME,
+    lv_config,
+    config_keys, NR_CONFIG_KEYS);
+    plugin_register_init (PLUGIN_NAME, lv_init);
+    plugin_register_read (PLUGIN_NAME, lv_read);
+    plugin_register_shutdown (PLUGIN_NAME, lv_shutdown);
+}
+
+/*
+ * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
+ */
index c17b7f3..d3d5202 100644 (file)
@@ -49,7 +49,6 @@
 
 #include "utils_cache.h"
 #include "utils_complain.h"
-#include "utils_parse_option.h"
 #include "utils_format_graphite.h"
 
 /* Folks without pthread will need to disable this plugin. */
 # define WG_SEND_BUF_SIZE 1428
 #endif
 
+#ifndef WG_MIN_RECONNECT_INTERVAL
+# define WG_MIN_RECONNECT_INTERVAL TIME_T_TO_CDTIME_T (1)
+#endif
+
 /*
  * Private variables
  */
@@ -109,6 +112,7 @@ struct wg_callback
 
     pthread_mutex_t send_lock;
     c_complain_t init_complaint;
+    cdtime_t last_connect_time;
 };
 
 
@@ -186,6 +190,7 @@ static int wg_callback_init (struct wg_callback *cb)
     struct addrinfo ai_hints;
     struct addrinfo *ai_list;
     struct addrinfo *ai_ptr;
+    cdtime_t now;
     int status;
 
     const char *node = cb->node ? cb->node : WG_DEFAULT_NODE;
@@ -195,6 +200,13 @@ static int wg_callback_init (struct wg_callback *cb)
     if (cb->sock_fd > 0)
         return (0);
 
+    /* Don't try to reconnect too often. By default, one reconnection attempt
+     * is made per second. */
+    now = cdtime ();
+    if ((now - cb->last_connect_time) < WG_MIN_RECONNECT_INTERVAL)
+        return (EAGAIN);
+    cb->last_connect_time = now;
+
     memset (&ai_hints, 0, sizeof (ai_hints));
 #ifdef AI_ADDRCONFIG
     ai_hints.ai_flags |= AI_ADDRCONFIG;
index eee5a1c..d97bc9b 100644 (file)
@@ -2,7 +2,7 @@
  * collectd - src/write_http.c
  * Copyright (C) 2009       Paul Sadauskas
  * Copyright (C) 2009       Doug MacEachern
- * Copyright (C) 2007-2009  Florian octo Forster
+ * Copyright (C) 2007-2014  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
@@ -27,7 +27,6 @@
 #include "plugin.h"
 #include "common.h"
 #include "utils_cache.h"
-#include "utils_parse_option.h"
 #include "utils_format_json.h"
 
 #if HAVE_PTHREAD_H
 
 #include <curl/curl.h>
 
+#ifndef WRITE_HTTP_DEFAULT_BUFFER_SIZE
+# define WRITE_HTTP_DEFAULT_BUFFER_SIZE 4096
+#endif
+
 /*
  * Private variables
  */
@@ -63,7 +66,8 @@ struct wh_callback_s
         CURL *curl;
         char curl_errbuf[CURL_ERROR_SIZE];
 
-        char   send_buffer[4096];
+        char  *send_buffer;
+        size_t send_buffer_size;
         size_t send_buffer_free;
         size_t send_buffer_fill;
         cdtime_t send_buffer_init_time;
@@ -74,8 +78,8 @@ typedef struct wh_callback_s wh_callback_t;
 
 static void wh_reset_buffer (wh_callback_t *cb)  /* {{{ */
 {
-        memset (cb->send_buffer, 0, sizeof (cb->send_buffer));
-        cb->send_buffer_free = sizeof (cb->send_buffer);
+        memset (cb->send_buffer, 0, cb->send_buffer_size);
+        cb->send_buffer_free = cb->send_buffer_size;
         cb->send_buffer_fill = 0;
         cb->send_buffer_init_time = cdtime ();
 
@@ -280,7 +284,11 @@ static void wh_callback_free (void *data) /* {{{ */
 
         wh_flush_nolock (/* timeout = */ 0, cb);
 
-        curl_easy_cleanup (cb->curl);
+        if (cb->curl != NULL)
+        {
+                curl_easy_cleanup (cb->curl);
+                cb->curl = NULL;
+        }
         sfree (cb->location);
         sfree (cb->user);
         sfree (cb->pass);
@@ -290,6 +298,7 @@ static void wh_callback_free (void *data) /* {{{ */
         sfree (cb->clientkey);
         sfree (cb->clientcert);
         sfree (cb->clientkeypass);
+        sfree (cb->send_buffer);
 
         sfree (cb);
 } /* }}} void wh_callback_free */
@@ -371,8 +380,8 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{
 
         DEBUG ("write_http plugin: <%s> buffer %zu/%zu (%g%%) \"%s\"",
                         cb->location,
-                        cb->send_buffer_fill, sizeof (cb->send_buffer),
-                        100.0 * ((double) cb->send_buffer_fill) / ((double) sizeof (cb->send_buffer)),
+                        cb->send_buffer_fill, cb->send_buffer_size,
+                        100.0 * ((double) cb->send_buffer_fill) / ((double) cb->send_buffer_size),
                         command);
 
         /* Check if we have enough space for this command. */
@@ -426,8 +435,8 @@ static int wh_write_json (const data_set_t *ds, const value_list_t *vl, /* {{{ *
 
         DEBUG ("write_http plugin: <%s> buffer %zu/%zu (%g%%)",
                         cb->location,
-                        cb->send_buffer_fill, sizeof (cb->send_buffer),
-                        100.0 * ((double) cb->send_buffer_fill) / ((double) sizeof (cb->send_buffer)));
+                        cb->send_buffer_fill, cb->send_buffer_size,
+                        100.0 * ((double) cb->send_buffer_fill) / ((double) cb->send_buffer_size));
 
         /* Check if we have enough space for this command. */
         pthread_mutex_unlock (&cb->send_lock);
@@ -485,6 +494,7 @@ static int config_set_format (wh_callback_t *cb, /* {{{ */
 static int wh_config_url (oconfig_item_t *ci) /* {{{ */
 {
         wh_callback_t *cb;
+        int buffer_size = 0;
         user_data_t user_data;
         int i;
 
@@ -560,6 +570,8 @@ static int wh_config_url (oconfig_item_t *ci) /* {{{ */
                         config_set_format (cb, child);
                 else if (strcasecmp ("StoreRates", child->key) == 0)
                         cf_util_get_boolean (child, &cb->store_rates);
+                else if (strcasecmp ("BufferSize", child->key) == 0)
+                        cf_util_get_int (child, &buffer_size);
                 else
                 {
                         ERROR ("write_http plugin: Invalid configuration "
@@ -567,6 +579,25 @@ static int wh_config_url (oconfig_item_t *ci) /* {{{ */
                 }
         }
 
+        /* Determine send_buffer_size. */
+        cb->send_buffer_size = WRITE_HTTP_DEFAULT_BUFFER_SIZE;
+        if (buffer_size >= 1024)
+                cb->send_buffer_size = (size_t) buffer_size;
+        else if (buffer_size != 0)
+                ERROR ("write_http plugin: Ignoring invalid BufferSize setting (%d).",
+                                buffer_size);
+
+        /* Allocate the buffer. */
+        cb->send_buffer = malloc (cb->send_buffer_size);
+        if (cb->send_buffer == NULL)
+        {
+                ERROR ("write_http plugin: malloc(%zu) failed.", cb->send_buffer_size);
+                wh_callback_free (cb);
+                return (-1);
+        }
+        /* Nulls the buffer and sets ..._free and ..._fill. */
+        wh_reset_buffer (cb);
+
         DEBUG ("write_http: Registering write callback with URL %s",
                         cb->location);
 
index 3e683c8..ba76d71 100644 (file)
@@ -61,7 +61,7 @@ static int kafka_write(const data_set_t *, const value_list_t *, user_data_t *);
 static int32_t kafka_partition(const rd_kafka_topic_t *, const void *, size_t,
                                int32_t, void *, void *);
 
-#ifdef HAVE_LIBRDKAFKA_LOGGER
+#if defined HAVE_LIBRDKAFKA_LOGGER || defined HAVE_LIBRDKAFKA_LOG_CB
 static void kafka_log(const rd_kafka_t *, int, const char *, const char *);
 
 static void kafka_log(const rd_kafka_t *rkt, int level,
index c3740e1..0a8df6f 100644 (file)
 #define RIEMANN_HOST           "localhost"
 #define RIEMANN_PORT           "5555"
 #define RIEMANN_TTL_FACTOR      2.0
+#define RIEMANN_BATCH_MAX      8192
 
 int write_riemann_threshold_check(const data_set_t *, const value_list_t *, int *);
 
 struct riemann_host {
        char                    *name;
        char                    *event_service_prefix;
-#define F_CONNECT               0x01
+#define F_CONNECT       0x01
        uint8_t                  flags;
-       pthread_mutex_t          lock;
-       _Bool                    notifications;
-       _Bool                    check_thresholds;
+       pthread_mutex_t  lock;
+    _Bool            batch_mode;
+       _Bool            notifications;
+       _Bool            check_thresholds;
        _Bool                    store_rates;
        _Bool                    always_append_ds;
        char                    *node;
        char                    *service;
        _Bool                    use_tcp;
-       int                      s;
+       int                          s;
        double                   ttl_factor;
-
-       int                      reference_count;
+    Msg             *batch_msg;
+    cdtime_t         batch_init;
+    int              batch_max;
+       int                          reference_count;
 };
 
 static char    **riemann_tags;
@@ -651,6 +655,103 @@ static Msg *riemann_value_list_to_protobuf (struct riemann_host const *host, /*
        return (msg);
 } /* }}} Msg *riemann_value_list_to_protobuf */
 
+
+/*
+ * Always call while holding host->lock !
+ */
+static int riemann_batch_flush_nolock (cdtime_t timeout,
+                                       struct riemann_host *host)
+{
+    cdtime_t    now;
+    int         status = 0;
+
+    if (timeout > 0) {
+        now = cdtime ();
+        if ((host->batch_init + timeout) > now)
+            return status;
+    }
+    riemann_send_msg(host, host->batch_msg);
+    riemann_msg_protobuf_free(host->batch_msg);
+
+       if (host->use_tcp && ((status = riemann_recv_ack(host)) != 0))
+        riemann_disconnect (host);
+
+    host->batch_init = cdtime();
+    host->batch_msg = NULL;
+    return status;
+}
+
+static int riemann_batch_flush (cdtime_t timeout,
+        const char *identifier __attribute__((unused)),
+        user_data_t *user_data)
+{
+    struct riemann_host *host;
+    int status;
+
+    if (user_data == NULL)
+        return (-EINVAL);
+
+    host = user_data->data;
+    pthread_mutex_lock (&host->lock);
+    status = riemann_batch_flush_nolock (timeout, host);
+    if (status != 0)
+        ERROR ("write_riemann plugin: riemann_send failed with status %i",
+               status);
+
+    pthread_mutex_unlock(&host->lock);
+    return status;
+}
+
+static int riemann_batch_add_value_list (struct riemann_host *host, /* {{{ */
+                                         data_set_t const *ds,
+                                         value_list_t const *vl,
+                                         int *statuses)
+{
+       size_t i;
+    Event **events;
+    Msg *msg;
+    size_t len;
+    int ret;
+
+    msg = riemann_value_list_to_protobuf (host, ds, vl, statuses);
+    if (msg == NULL)
+        return -1;
+
+    pthread_mutex_lock(&host->lock);
+
+    if (host->batch_msg == NULL) {
+        host->batch_msg = msg;
+    } else {
+        len = msg->n_events + host->batch_msg->n_events;
+        events = realloc(host->batch_msg->events,
+                         (len * sizeof(*host->batch_msg->events)));
+        if (events == NULL) {
+            pthread_mutex_unlock(&host->lock);
+            ERROR ("write_riemann plugin: out of memory");
+            riemann_msg_protobuf_free (msg);
+            return -1;
+        }
+        host->batch_msg->events = events;
+
+        for (i = host->batch_msg->n_events; i < len; i++)
+            host->batch_msg->events[i] = msg->events[i - host->batch_msg->n_events];
+
+        host->batch_msg->n_events = len;
+        sfree (msg->events);
+        msg->n_events = 0;
+        sfree (msg);
+    }
+
+       len = msg__get_packed_size(host->batch_msg);
+    ret = 0;
+    if (len >= host->batch_max) {
+        ret = riemann_batch_flush_nolock(0, host);
+    }
+
+    pthread_mutex_unlock(&host->lock);
+    return ret;
+} /* }}} Msg *riemann_batch_add_value_list */
+
 static int riemann_notification(const notification_t *n, user_data_t *ud) /* {{{ */
 {
        int                      status;
@@ -660,6 +761,9 @@ static int riemann_notification(const notification_t *n, user_data_t *ud) /* {{{
        if (!host->notifications)
                return 0;
 
+    /*
+     * Never batch for notifications, send them ASAP
+     */
        msg = riemann_notification_to_protobuf (host, n);
        if (msg == NULL)
                return (-1);
@@ -677,23 +781,32 @@ static int riemann_write(const data_set_t *ds, /* {{{ */
              const value_list_t *vl,
              user_data_t *ud)
 {
-       int                      status;
+       int                      status = 0;
        int                      statuses[vl->values_len];
        struct riemann_host     *host = ud->data;
        Msg                     *msg;
 
        if (host->check_thresholds)
                write_riemann_threshold_check(ds, vl, statuses);
-       msg = riemann_value_list_to_protobuf (host, ds, vl, statuses);
-       if (msg == NULL)
-               return (-1);
 
-       status = riemann_send (host, msg);
-       if (status != 0)
-               ERROR ("write_riemann plugin: riemann_send failed with status %i",
-                               status);
+    if (host->use_tcp == 1 && host->batch_mode) {
 
-       riemann_msg_protobuf_free (msg);
+        riemann_batch_add_value_list (host, ds, vl, statuses);
+
+
+    } else {
+
+        msg = riemann_value_list_to_protobuf (host, ds, vl, statuses);
+        if (msg == NULL)
+            return (-1);
+
+        status = riemann_send (host, msg);
+        if (status != 0)
+            ERROR ("write_riemann plugin: riemann_send failed with status %i",
+                   status);
+
+        riemann_msg_protobuf_free (msg);
+    }
        return status;
 } /* }}} int riemann_write */
 
@@ -742,6 +855,9 @@ static int riemann_config_node(oconfig_item_t *ci) /* {{{ */
        host->store_rates = 1;
        host->always_append_ds = 0;
        host->use_tcp = 0;
+    host->batch_mode = 0;
+    host->batch_max = RIEMANN_BATCH_MAX; /* typical MSS */
+    host->batch_init = cdtime();
        host->ttl_factor = RIEMANN_TTL_FACTOR;
 
        status = cf_util_get_string (ci, &host->name);
@@ -775,6 +891,14 @@ static int riemann_config_node(oconfig_item_t *ci) /* {{{ */
                        status = cf_util_get_boolean(child, &host->check_thresholds);
                        if (status != 0)
                                break;
+        } else if (strcasecmp ("Batch", child->key) == 0) {
+            status = cf_util_get_boolean(child, &host->batch_mode);
+            if (status != 0)
+                break;
+        } else if (strcasecmp("BatchMaxSize", child->key) == 0) {
+            status = cf_util_get_int(child, &host->batch_max);
+            if (status != 0)
+                break;
                } else if (strcasecmp ("Port", child->key) == 0) {
                        status = cf_util_get_service (child, &host->service);
                        if (status != 0) {
@@ -859,6 +983,11 @@ static int riemann_config_node(oconfig_item_t *ci) /* {{{ */
        pthread_mutex_lock (&host->lock);
 
        status = plugin_register_write (callback_name, riemann_write, &ud);
+
+    if (host->use_tcp == 1 && host->batch_mode) {
+        ud.free_func = NULL;
+        plugin_register_flush(callback_name, riemann_batch_flush, &ud);
+    }
        if (status != 0)
                WARNING ("write_riemann plugin: plugin_register_write (\"%s\") "
                                "failed with status %i.",
index 2eca77e..efc7146 100644 (file)
@@ -47,7 +47,6 @@
 #include "configfile.h"
 
 #include "utils_cache.h"
-#include "utils_parse_option.h"
 
 #include <pthread.h>
 #include <sys/socket.h>
@@ -388,8 +387,8 @@ static int wt_format_name(char *ret, int ret_len,
         ssnprintf(ret, ret_len, "%s%s.%s.%s",
                   prefix, vl->plugin, vl->plugin_instance, vl->type);
     } else {
-        ssnprintf(ret, ret_len, "%s%s.%s.%s",
-                  prefix, vl->plugin, vl->plugin_instance, vl->type_instance);
+        ssnprintf(ret, ret_len, "%s%s.%s.%s.%s",
+                  prefix, vl->plugin, vl->plugin_instance, vl->type, vl->type_instance);
     }
 
     sfree(temp);