Merge remote-tracking branch 'github/pr/734'
authorFlorian Forster <octo@collectd.org>
Tue, 23 Sep 2014 14:09:47 +0000 (07:09 -0700)
committerFlorian Forster <octo@collectd.org>
Tue, 23 Sep 2014 14:09:47 +0000 (07:09 -0700)
96 files changed:
COPYING
configure.ac
src/Makefile.am
src/bind.c
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/csv.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/filter_chain.c [deleted file]
src/filter_chain.h [deleted file]
src/libcollectdclient/Makefile.am
src/libvirt.c [deleted file]
src/memory.c
src/meta_data.c [deleted file]
src/meta_data.h [deleted file]
src/plugin.c [deleted file]
src/plugin.h [deleted file]
src/tokyotyrant.c
src/types_list.c [deleted file]
src/types_list.h [deleted file]
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_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_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_tsdb.c

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
 
index bee5f72..9dd30c7 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
 
@@ -4972,7 +4972,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"
@@ -4988,8 +4987,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"
@@ -5214,11 +5214,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"
@@ -5283,6 +5278,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:])
 
@@ -5341,7 +5342,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])
@@ -5417,6 +5417,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])
@@ -5579,7 +5580,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" \
@@ -5706,7 +5707,6 @@ Configuration:
     ipvs  . . . . . . . . $enable_ipvs
     irq . . . . . . . . . $enable_irq
     java  . . . . . . . . $enable_java
-    libvirt . . . . . . . $enable_libvirt
     load  . . . . . . . . $enable_load
     logfile . . . . . . . $enable_logfile
     lpar  . . . . . . . . $enable_lpar
@@ -5781,6 +5781,7 @@ Configuration:
     users . . . . . . . . $enable_users
     uuid  . . . . . . . . $enable_uuid
     varnish . . . . . . . $enable_varnish
+    virt  . . . . . . . . $enable_virt
     vmem  . . . . . . . . $enable_vmem
     vserver . . . . . . . $enable_vserver
     wireless  . . . . . . $enable_wireless
index b862365..16f458e 100644 (file)
@@ -2,12 +2,14 @@ SUBDIRS = libcollectdclient
 if BUILD_WITH_OWN_LIBOCONFIG
 SUBDIRS += liboconfig
 endif
+SUBDIRS += daemon
 
 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,78 +21,9 @@ 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)
 
@@ -121,7 +54,7 @@ collectdctl_LDADD += libcollectdclient/libcollectdclient.la
 collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
 collectd_tg_SOURCES = collectd-tg.c \
-                     utils_heap.c utils_heap.h
+                     daemon/utils_heap.c daemon/utils_heap.h
 collectd_tg_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/src/libcollectdclient/collectd
 collectd_tg_LDADD =
 if BUILD_WITH_LIBSOCKET
@@ -148,8 +81,6 @@ aggregation_la_SOURCES = aggregation.c \
                          utils_vl_lookup.c utils_vl_lookup.h
 aggregation_la_LDFLAGS = -module -avoid-version
 aggregation_la_LIBADD =
-collectd_LDADD += "-dlopen" aggregation.la
-collectd_DEPENDENCIES += aggregation.la
 endif
 
 if BUILD_PLUGIN_AMQP
@@ -161,8 +92,6 @@ amqp_la_SOURCES = amqp.c \
 amqp_la_LDFLAGS = -module -avoid-version $(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
@@ -171,12 +100,10 @@ apache_la_SOURCES = apache.c
 apache_la_LDFLAGS = -module -avoid-version
 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
@@ -187,8 +114,6 @@ 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
@@ -196,8 +121,6 @@ pkglib_LTLIBRARIES += apple_sensors.la
 apple_sensors_la_SOURCES = apple_sensors.c
 apple_sensors_la_LDFLAGS = -module -avoid-version
 apple_sensors_la_LDFLAGS += -framework IOKit
-collectd_LDADD += "-dlopen" apple_sensors.la
-collectd_DEPENDENCIES += apple_sensors.la
 endif
 
 if BUILD_PLUGIN_AQUAERO
@@ -206,8 +129,6 @@ aquaero_la_SOURCES = aquaero.c
 aquaero_la_LDFLAGS = -module -avoid-version
 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
@@ -217,8 +138,6 @@ ascent_la_LDFLAGS = -module -avoid-version
 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
@@ -226,8 +145,6 @@ pkglib_LTLIBRARIES += barometer.la
 barometer_la_SOURCES = barometer.c
 barometer_la_LDFLAGS = -module -avoid-version
 barometer_la_LIBADD = -lm
-collectd_LDADD += "-dlopen" barometer.la
-collectd_DEPENDENCIES += barometer.la
 endif
 
 if BUILD_PLUGIN_BATTERY
@@ -238,8 +155,6 @@ 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
@@ -247,26 +162,22 @@ pkglib_LTLIBRARIES += bind.la
 bind_la_SOURCES = bind.c
 bind_la_LDFLAGS = -module -avoid-version
 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_SOURCES = cgroups.c \
+                    utils_ignorelist.c utils_ignorelist.h \
+                    utils_mount.c utils_mount.h
 cgroups_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" cgroups.la
-collectd_DEPENDENCIES += cgroups.la
 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
 endif
 
 if BUILD_PLUGIN_CONTEXTSWITCH
@@ -277,8 +188,6 @@ 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
@@ -300,24 +209,18 @@ 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
 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
 endif
 
 if BUILD_PLUGIN_CURL
@@ -326,12 +229,10 @@ curl_la_SOURCES = curl.c
 curl_la_LDFLAGS = -module -avoid-version
 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
@@ -339,14 +240,12 @@ 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_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
@@ -356,8 +255,6 @@ curl_xml_la_LDFLAGS = -module -avoid-version
 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
@@ -367,21 +264,20 @@ dbi_la_SOURCES = dbi.c \
 dbi_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBDBI_CPPFLAGS)
 dbi_la_LDFLAGS = -module -avoid-version $(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_SOURCES = df.c \
+               utils_ignorelist.c utils_ignorelist.h \
+               utils_mount.c utils_mount.h
 df_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" df.la
-collectd_DEPENDENCIES += df.la
 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_LIBADD =
@@ -404,8 +300,6 @@ endif
 if BUILD_WITH_PERFSTAT
 disk_la_LIBADD += -lperfstat
 endif
-collectd_LDADD += "-dlopen" disk.la
-collectd_DEPENDENCIES += disk.la
 endif
 
 if BUILD_PLUGIN_DNS
@@ -413,8 +307,6 @@ pkglib_LTLIBRARIES += dns.la
 dns_la_SOURCES = dns.c utils_dns.c utils_dns.h
 dns_la_LDFLAGS = -module -avoid-version
 dns_la_LIBADD = -lpcap -lpthread
-collectd_LDADD += "-dlopen" dns.la
-collectd_DEPENDENCIES += dns.la
 endif
 
 if BUILD_PLUGIN_DRBD
@@ -422,8 +314,6 @@ pkglib_LTLIBRARIES += drbd.la
 drbd_la_SOURCES = drbd.c
 drbd_la_LDFLAGS = -module -avoid-version
 drbd_la_LIBADD = -lpthread
-collectd_LDADD += "-dlopen" drbd.la
-collectd_DEPENDENCIES += drbd.la
 endif
 
 if BUILD_PLUGIN_EMAIL
@@ -431,43 +321,34 @@ pkglib_LTLIBRARIES += email.la
 email_la_SOURCES = email.c
 email_la_LDFLAGS = -module -avoid-version
 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
 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
+                 utils_cmd_putval.c utils_cmd_putval.h \
+                 utils_parse_option.h utils_parse_option.c
 exec_la_LDFLAGS = -module -avoid-version
 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
 endif
 
 if BUILD_PLUGIN_FILECOUNT
 pkglib_LTLIBRARIES += filecount.la
 filecount_la_SOURCES = filecount.c
 filecount_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" filecount.la
-collectd_DEPENDENCIES += filecount.la
 endif
 
 if BUILD_PLUGIN_GMOND
@@ -476,8 +357,6 @@ gmond_la_SOURCES = gmond.c
 gmond_la_CPPFLAGS = $(AM_CPPFLAGS) $(GANGLIA_CPPFLAGS)
 gmond_la_LDFLAGS = -module -avoid-version $(GANGLIA_LDFLAGS)
 gmond_la_LIBADD = $(GANGLIA_LIBS)
-collectd_LDADD += "-dlopen" gmond.la
-collectd_DEPENDENCIES += gmond.la
 endif
 
 if BUILD_PLUGIN_HDDTEMP
@@ -488,18 +367,15 @@ 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_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)
@@ -522,18 +398,15 @@ iptables_la_SOURCES = iptables.c
 iptables_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBIPTC_CPPFLAGS)
 iptables_la_LDFLAGS = -module -avoid-version
 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_LIBADD = $(BUILD_WITH_OPENIPMI_LIBS)
-collectd_LDADD += "-dlopen" ipmi.la
-collectd_DEPENDENCIES += ipmi.la
 endif
 
 if BUILD_PLUGIN_IPVS
@@ -543,16 +416,13 @@ 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
 endif
 
 if BUILD_PLUGIN_IRQ
 pkglib_LTLIBRARIES += irq.la
-irq_la_SOURCES = irq.c
+irq_la_SOURCES = irq.c \
+                utils_ignorelist.c utils_ignorelist.h
 irq_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" irq.la
-collectd_DEPENDENCIES += irq.la
 endif
 
 if BUILD_PLUGIN_JAVA
@@ -562,19 +432,6 @@ java_la_CPPFLAGS = $(AM_CPPFLAGS) $(JAVA_CPPFLAGS)
 java_la_CFLAGS = $(AM_CFLAGS) $(JAVA_CFLAGS)
 java_la_LDFLAGS = -module -avoid-version $(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
@@ -583,8 +440,6 @@ load_la_SOURCES = load.c
 load_la_CFLAGS = $(AM_CFLAGS)
 load_la_LDFLAGS = -module -avoid-version
 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)
@@ -598,8 +453,6 @@ 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
 endif
 
 if BUILD_PLUGIN_LOG_LOGSTASH
@@ -609,16 +462,12 @@ log_logstash_la_CFLAGS = $(AM_CFLAGS)
 log_logstash_la_LDFLAGS = -module -avoid-version $(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_LIBADD = -lperfstat
 endif
 
@@ -627,56 +476,43 @@ pkglib_LTLIBRARIES += lvm.la
 lvm_la_SOURCES = lvm.c
 lvm_la_LDFLAGS = -module -avoid-version
 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_SOURCES = madwifi.c madwifi.h \
+                    utils_ignorelist.c utils_ignorelist.h
 madwifi_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" madwifi.la
-collectd_DEPENDENCIES += madwifi.la
 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
 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
 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
 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
 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
 endif
 
 if BUILD_PLUGIN_MBMON
@@ -687,26 +523,21 @@ 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_SOURCES = md.c \
+               utils_ignorelist.c utils_ignorelist.h
 md_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" md.la
-collectd_DEPENDENCIES += md.la
 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_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
@@ -717,8 +548,6 @@ 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
@@ -727,8 +556,6 @@ memory_la_SOURCES = memory.c
 memory_la_CFLAGS = $(AM_CFLAGS)
 memory_la_LDFLAGS = -module -avoid-version
 memory_la_LIBADD =
-collectd_LDADD += "-dlopen" memory.la
-collectd_DEPENDENCIES += memory.la
 if BUILD_WITH_LIBKSTAT
 memory_la_LIBADD += -lkstat
 endif
@@ -744,22 +571,27 @@ 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 = -module -avoid-version $(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_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
 endif
 
 if BUILD_PLUGIN_MYSQL
@@ -768,22 +600,19 @@ mysql_la_SOURCES = mysql.c
 mysql_la_LDFLAGS = -module -avoid-version
 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_LIBADD = $(LIBNETAPP_LIBS)
-collectd_LDADD += "-dlopen" netapp.la
-collectd_DEPENDENCIES += netapp.la
 endif
 
 if BUILD_PLUGIN_NETLINK
@@ -792,8 +621,6 @@ netlink_la_SOURCES = netlink.c
 netlink_la_LDFLAGS = -module -avoid-version
 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
@@ -811,24 +638,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
 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
 endif
 
 if BUILD_PLUGIN_NGINX
@@ -841,8 +662,6 @@ 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
@@ -851,8 +670,6 @@ 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_LIBADD = $(LIBNOTIFY_LIBS)
-collectd_LDADD += "-dlopen" notify_desktop.la
-collectd_DEPENDENCIES += notify_desktop.la
 endif
 
 if BUILD_PLUGIN_NOTIFY_EMAIL
@@ -860,8 +677,6 @@ pkglib_LTLIBRARIES += notify_email.la
 notify_email_la_SOURCES = notify_email.c
 notify_email_la_LDFLAGS = -module -avoid-version
 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
@@ -872,16 +687,12 @@ 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
 endif
 
 if BUILD_PLUGIN_NUT
@@ -890,8 +701,6 @@ nut_la_SOURCES = nut.c
 nut_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBUPSCLIENT_CFLAGS)
 nut_la_LDFLAGS = -module -avoid-version
 nut_la_LIBADD = -lpthread $(BUILD_WITH_LIBUPSCLIENT_LIBS)
-collectd_LDADD += "-dlopen" nut.la
-collectd_DEPENDENCIES += nut.la
 endif
 
 if BUILD_PLUGIN_OLSRD
@@ -902,19 +711,16 @@ 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
 endif
 
 if BUILD_PLUGIN_OPENVPN
@@ -922,8 +728,6 @@ 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
 endif
 
 if BUILD_PLUGIN_ORACLE
@@ -931,11 +735,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
 endif
 
 if BUILD_PLUGIN_PERL
@@ -954,16 +756,12 @@ perl_la_CFLAGS += -Wno-nonnull
 endif
 perl_la_LDFLAGS = -module -avoid-version \
                $(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
 endif
 
 if BUILD_PLUGIN_PINBA
@@ -972,8 +770,6 @@ 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_LIBADD = -lprotobuf-c
-collectd_LDADD += "-dlopen" pinba.la
-collectd_DEPENDENCIES += pinba.la
 endif
 
 if BUILD_PLUGIN_PING
@@ -982,8 +778,6 @@ 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_LIBADD = -loping -lm
-collectd_LDADD += "-dlopen" ping.la
-collectd_DEPENDENCIES += ping.la
 endif
 
 if BUILD_PLUGIN_POSTGRESQL
@@ -994,16 +788,12 @@ postgresql_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPQ_CPPFLAGS)
 postgresql_la_LDFLAGS = -module -avoid-version \
                $(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
 endif
 
 if BUILD_PLUGIN_PYTHON
@@ -1016,8 +806,6 @@ python_la_CFLAGS += -fno-strict-aliasing -Wno-strict-aliasing
 endif
 python_la_LDFLAGS = -module -avoid-version $(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
@@ -1025,8 +813,6 @@ pkglib_LTLIBRARIES += processes.la
 processes_la_SOURCES = processes.c
 processes_la_LDFLAGS = -module -avoid-version
 processes_la_LIBADD =
-collectd_LDADD += "-dlopen" processes.la
-collectd_DEPENDENCIES += processes.la
 if BUILD_WITH_LIBKVM_GETPROCS
 processes_la_LIBADD += -lkvm
 endif
@@ -1034,10 +820,9 @@ endif
 
 if BUILD_PLUGIN_PROTOCOLS
 pkglib_LTLIBRARIES += protocols.la
-protocols_la_SOURCES = protocols.c
+protocols_la_SOURCES = protocols.c \
+                      utils_ignorelist.c utils_ignorelist.h
 protocols_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" protocols.la
-collectd_DEPENDENCIES += protocols.la
 endif
 
 if BUILD_PLUGIN_REDIS
@@ -1046,18 +831,14 @@ redis_la_SOURCES = redis.c
 redis_la_LDFLAGS = -module -avoid-version $(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_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBROUTEROS_CPPFLAGS)
 routeros_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBROUTEROS_LDFLAGS)
 routeros_la_LIBADD = -lrouteros
-collectd_LDADD += "-dlopen" routeros.la
-collectd_DEPENDENCIES += routeros.la
 endif
 
 if BUILD_PLUGIN_RRDCACHED
@@ -1066,8 +847,6 @@ rrdcached_la_SOURCES = rrdcached.c utils_rrdcreate.c utils_rrdcreate.h
 rrdcached_la_LDFLAGS = -module -avoid-version
 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
@@ -1076,26 +855,21 @@ rrdtool_la_SOURCES = rrdtool.c utils_rrdcreate.c utils_rrdcreate.h
 rrdtool_la_LDFLAGS = -module -avoid-version
 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_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
 endif
 
 if BUILD_PLUGIN_SIGROK
@@ -1104,8 +878,6 @@ 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_LIBADD = -lsigrok
-collectd_LDADD += "-dlopen" sigrok.la
-collectd_DEPENDENCIES += sigrok.la
 endif
 
 if BUILD_PLUGIN_SNMP
@@ -1121,8 +893,6 @@ endif
 if BUILD_WITH_LIBPTHREAD
 snmp_la_LIBADD += -lpthread
 endif
-collectd_LDADD += "-dlopen" snmp.la
-collectd_DEPENDENCIES += snmp.la
 endif
 
 if BUILD_PLUGIN_STATSD
@@ -1131,8 +901,6 @@ statsd_la_SOURCES = statsd.c \
                     utils_latency.h utils_latency.c
 statsd_la_LDFLAGS = -module -avoid-version
 statsd_la_LIBADD = -lpthread
-collectd_LDADD += "-dlopen" statsd.la
-collectd_DEPENDENCIES += statsd.la
 endif
 
 if BUILD_PLUGIN_SWAP
@@ -1141,8 +909,6 @@ swap_la_SOURCES = swap.c
 swap_la_CFLAGS = $(AM_CFLAGS)
 swap_la_LDFLAGS = -module -avoid-version
 swap_la_LIBADD =
-collectd_LDADD += "-dlopen" swap.la
-collectd_DEPENDENCIES += swap.la
 if BUILD_WITH_LIBKSTAT
 swap_la_LIBADD += -lkstat
 endif
@@ -1166,32 +932,24 @@ 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
 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
 endif
 
 if BUILD_PLUGIN_TAIL
 pkglib_LTLIBRARIES += tail.la
 tail_la_SOURCES = tail.c
 tail_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" tail.la
-collectd_DEPENDENCIES += tail.la
 endif
 
 if BUILD_PLUGIN_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
 endif
 
 if BUILD_PLUGIN_TAPE
@@ -1199,48 +957,36 @@ pkglib_LTLIBRARIES += tape.la
 tape_la_SOURCES = tape.c
 tape_la_LDFLAGS = -module -avoid-version
 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
 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
 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
 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
 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
 endif
 
 if BUILD_PLUGIN_TCPCONNS
@@ -1248,8 +994,6 @@ pkglib_LTLIBRARIES += tcpconns.la
 tcpconns_la_SOURCES = tcpconns.c
 tcpconns_la_LDFLAGS = -module -avoid-version
 tcpconns_la_LIBADD =
-collectd_LDADD += "-dlopen" tcpconns.la
-collectd_DEPENDENCIES += tcpconns.la
 if BUILD_WITH_LIBKVM_NLIST
 tcpconns_la_LIBADD += -lkvm
 endif
@@ -1259,32 +1003,25 @@ if BUILD_PLUGIN_TEAMSPEAK2
 pkglib_LTLIBRARIES += teamspeak2.la
 teamspeak2_la_SOURCES = teamspeak2.c
 teamspeak2_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" teamspeak2.la
-collectd_DEPENDENCIES += teamspeak2.la
 endif
 
 if BUILD_PLUGIN_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
 endif
 
 if BUILD_PLUGIN_THERMAL
 pkglib_LTLIBRARIES += thermal.la
-thermal_la_SOURCES = thermal.c
+thermal_la_SOURCES = thermal.c \
+                    utils_ignorelist.c utils_ignorelist.h
 thermal_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" thermal.la
-collectd_DEPENDENCIES += thermal.la
 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
 endif
 
 if BUILD_PLUGIN_TOKYOTYRANT
@@ -1296,8 +1033,6 @@ 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
@@ -1308,11 +1043,10 @@ 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
+                     utils_cmd_putnotif.h utils_cmd_putnotif.c \
+                     utils_parse_option.h utils_parse_option.c
 unixsock_la_LDFLAGS = -module -avoid-version
 unixsock_la_LIBADD = -lpthread
-collectd_LDADD += "-dlopen" unixsock.la
-collectd_DEPENDENCIES += unixsock.la
 endif
 
 if BUILD_PLUGIN_UPTIME
@@ -1327,8 +1061,6 @@ endif
 if BUILD_WITH_PERFSTAT
 uptime_la_LIBADD += -lperfstat
 endif
-collectd_LDADD += "-dlopen" uptime.la
-collectd_DEPENDENCIES += uptime.la
 endif
 
 if BUILD_PLUGIN_USERS
@@ -1341,8 +1073,6 @@ 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
@@ -1351,18 +1081,6 @@ 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
 endif
 
 if BUILD_PLUGIN_VARNISH
@@ -1371,32 +1089,34 @@ varnish_la_SOURCES = varnish.c
 varnish_la_LDFLAGS = -module -avoid-version
 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 = -module -avoid-version
 endif
 
 if BUILD_PLUGIN_VMEM
 pkglib_LTLIBRARIES += vmem.la
 vmem_la_SOURCES = vmem.c
 vmem_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" vmem.la
-collectd_DEPENDENCIES += vmem.la
 endif
 
 if BUILD_PLUGIN_VSERVER
 pkglib_LTLIBRARIES += vserver.la
 vserver_la_SOURCES = vserver.c
 vserver_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" vserver.la
-collectd_DEPENDENCIES += vserver.la
 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
 endif
 
 if BUILD_PLUGIN_WRITE_GRAPHITE
@@ -1405,8 +1125,6 @@ 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
 endif
 
 if BUILD_PLUGIN_WRITE_HTTP
@@ -1416,12 +1134,10 @@ write_http_la_SOURCES = write_http.c \
 write_http_la_LDFLAGS = -module -avoid-version
 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
@@ -1433,8 +1149,6 @@ write_kafka_la_SOURCES = write_kafka.c \
                         utils_crc32.c utils_crc32.h
 write_kafka_la_LDFLAGS = -module -avoid-version $(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
@@ -1443,8 +1157,6 @@ 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_LIBADD = -lmongoc
-collectd_LDADD += "-dlopen" write_mongodb.la
-collectd_DEPENDENCIES += write_mongodb.la
 endif
 
 if BUILD_PLUGIN_WRITE_REDIS
@@ -1453,8 +1165,6 @@ write_redis_la_SOURCES = write_redis.c
 write_redis_la_LDFLAGS = -module -avoid-version $(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
@@ -1463,16 +1173,12 @@ 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_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
 endif
 
 if BUILD_PLUGIN_XMMS
@@ -1481,8 +1187,6 @@ xmms_la_SOURCES = xmms.c
 xmms_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBXMMS_CFLAGS)
 xmms_la_LDFLAGS = -module -avoid-version
 xmms_la_LIBADD = $(BUILD_WITH_LIBXMMS_LIBS)
-collectd_LDADD += "-dlopen" xmms.la
-collectd_DEPENDENCIES += xmms.la
 endif
 
 if BUILD_PLUGIN_ZFS_ARC
@@ -1500,8 +1204,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)
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)
   {
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 894c64b..80e1d5c 100644 (file)
 #@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_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
 #      </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>
 #   </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>
index d2e6843..ac0ff2a 100644 (file)
@@ -860,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
@@ -973,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).
@@ -1810,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
@@ -2454,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
@@ -6528,6 +6433,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 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<vmem>
 
 The C<vmem> plugin collects information about the usage of virtual memory.
diff --git a/src/collectd.h b/src/collectd.h
deleted file mode 100644 (file)
index 6886c12..0000000
+++ /dev/null
@@ -1,315 +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
-
-/* 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/common.c b/src/common.c
deleted file mode 100644 (file)
index 5386739..0000000
+++ /dev/null
@@ -1,1596 +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 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/common.h b/src/common.h
deleted file mode 100644 (file)
index 434ed01..0000000
+++ /dev/null
@@ -1,357 +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);
-
-/** 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/configfile.c b/src/configfile.c
deleted file mode 100644 (file)
index fbb3cd3..0000000
+++ /dev/null
@@ -1,1342 +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"},
-       {"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;
-
-       /* 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 (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/configfile.h b/src/configfile.h
deleted file mode 100644 (file)
index 5bc9b30..0000000
+++ /dev/null
@@ -1,141 +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 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) */
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
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
new file mode 100644 (file)
index 0000000..d6067d7
--- /dev/null
@@ -0,0 +1,84 @@
+if COMPILER_IS_GCC
+AM_CFLAGS = -Wall -Werror
+endif
+
+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..2e2d821
--- /dev/null
@@ -0,0 +1,637 @@
+/**
+ * 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 <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 ())
+       {
+               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 */
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..cd99204
--- /dev/null
@@ -0,0 +1,2656 @@
+/**
+ * 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;
+
+#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..dfc608e
--- /dev/null
@@ -0,0 +1,437 @@
+/**
+ * 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 */
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 : */
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 1d4dff5..2fc3152 100644 (file)
@@ -11,7 +11,7 @@ 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_builddir)/src/libcollectdclient/collectd -I$(top_srcdir)/src/daemon
 libcollectdclient_la_LDFLAGS = -version-info 1:0:0
 libcollectdclient_la_LIBADD = 
 if BUILD_WITH_LIBGCRYPT
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 c503821..8166acc 100644 (file)
@@ -283,6 +283,8 @@ static int memory_read_internal (value_list_t *vl)
        gauge_t mem_buffered = 0;
        gauge_t mem_cached = 0;
        gauge_t mem_free = 0;
+       gauge_t mem_slab_reclaimable = 0;
+       gauge_t mem_slab_unreclaimable = 0;
 
        if ((fh = fopen ("/proc/meminfo", "r")) == NULL)
        {
@@ -304,6 +306,10 @@ 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, "SReclaimable:", 13) == 0)
+                       val = &mem_slab_reclaimable;
+               else if (strncasecmp (buffer, "SUnreclaim:", 11) == 0)
+                       val = &mem_slab_unreclaimable;
                else
                        continue;
 
@@ -324,11 +330,13 @@ static int memory_read_internal (value_list_t *vl)
        if (mem_total < (mem_free + mem_buffered + mem_cached))
                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_unreclaimable + mem_slab_reclaimable);
+       MEMORY_SUBMIT ("used",        mem_used,
+                      "buffered",    mem_buffered,
+                      "cached",      mem_cached,
+                      "free",        mem_free,
+                      "slab_unrecl", mem_slab_unreclaimable,
+                      "slab_recl",   mem_slab_reclaimable);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
diff --git a/src/meta_data.c b/src/meta_data.c
deleted file mode 100644 (file)
index d3da9bb..0000000
+++ /dev/null
@@ -1,637 +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;    
-
-  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/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 : */
diff --git a/src/plugin.c b/src/plugin.c
deleted file mode 100644 (file)
index cd99204..0000000
+++ /dev/null
@@ -1,2656 +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;
-
-#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/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 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>
 
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 */
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 fe22f21..0000000
+++ /dev/null
@@ -1,988 +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 */
-
-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/utils_cache.h b/src/utils_cache.h
deleted file mode 100644 (file)
index ea3eb2f..0000000
+++ /dev/null
@@ -1,96 +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);
-
-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/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 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 31b83c3..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)
@@ -274,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);
       }
@@ -362,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 5083b05..0000000
+++ /dev/null
@@ -1,396 +0,0 @@
-/**
- * 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/utils_match.h b/src/utils_match.h
deleted file mode 100644 (file)
index a1d1002..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/**
- * 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 : */
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);
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 7a1fbd0..d97bc9b 100644 (file)
@@ -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
index 2eca77e..aeed635 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>