Merge remote-tracking branch 'github/pr/697'
authorFlorian Forster <octo@collectd.org>
Tue, 23 Sep 2014 14:13:52 +0000 (07:13 -0700)
committerFlorian Forster <octo@collectd.org>
Tue, 23 Sep 2014 14:13:52 +0000 (07:13 -0700)
94 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/cpu.c
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/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/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 bcd8b60..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
 
@@ -5580,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" \
index 340c792..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,8 +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_LOAD
@@ -572,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)
@@ -587,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
@@ -598,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
 
@@ -616,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
@@ -676,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
@@ -706,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
@@ -716,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
@@ -733,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
@@ -757,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
@@ -781,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
@@ -800,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
@@ -830,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
@@ -840,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
@@ -849,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
@@ -861,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
@@ -879,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
@@ -891,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
@@ -911,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
@@ -920,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
@@ -943,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
@@ -961,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
@@ -971,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
@@ -983,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
@@ -1005,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
@@ -1014,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
@@ -1023,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
@@ -1035,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
@@ -1055,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
@@ -1065,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
@@ -1093,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
@@ -1110,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
@@ -1120,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
@@ -1130,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
@@ -1155,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
@@ -1188,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
@@ -1237,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
@@ -1248,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
@@ -1285,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
@@ -1297,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
@@ -1316,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
@@ -1330,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
@@ -1340,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
@@ -1360,43 +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
+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
-collectd_LDADD += "-dlopen" virt.la
-collectd_DEPENDENCIES += virt.la
 endif
 
 if BUILD_PLUGIN_VMEM
 pkglib_LTLIBRARIES += vmem.la
 vmem_la_SOURCES = vmem.c
 vmem_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" vmem.la
-collectd_DEPENDENCIES += vmem.la
 endif
 
 if BUILD_PLUGIN_VSERVER
 pkglib_LTLIBRARIES += vserver.la
 vserver_la_SOURCES = vserver.c
 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 8e1bae7..80e1d5c 100644 (file)
 #</Plugin>
 
 #<Plugin cpu>
-#  ReportActive false
 #  ReportByCpu true
+#  ReportByState true
 #  ValuesPercentage false
 #</Plugin>
 #
index 939cfda..ac0ff2a 100644 (file)
@@ -1207,29 +1207,53 @@ F</proc/sys/net/ipv4/netfilter> instead of F</proc/sys/net/netfilter/>.
 
 =head2 Plugin C<cpu>
 
-The I<CPU plugin> collects CPU usage metrics.
+The I<CPU plugin> collects CPU usage metrics. By default, CPU usage is reported
+as Jiffies, using the C<cpu> type. Two aggregations are available:
+
+=over 4
+
+=item
+
+Sum, per-state, over all CPUs installed in the system; and
+
+=item
+
+Sum, per-CPU, over all non-idle states of a CPU, creating an "active" state.
+
+=back
+
+The two aggregations can be combined, leading to I<collectd> only emitting a
+single "active" metric for the entire system. As soon as one of these
+aggregations (or both) is enabled, the I<cpu plugin> will report a percentage,
+rather than Jiffies. In addition, you can request individual, per-state,
+per-CPU metrics to be reported as percentage.
 
 The following configuration options are available:
 
 =over 4
 
-=item B<ReportActive> B<false>|B<true>
+=item B<ReportByState> B<true>|B<false>
 
-Reports non-idle CPU usage as the "active" value. Defaults to false.
+When set to B<true>, the default, reports per-state metrics, e.g. "system",
+"user" and "idle".
+When set to B<false>, aggregates (sums) all I<non-idle> states into one
+"active" metric.
 
-=item B<ReportByCpu> B<false>|B<true>
+=item B<ReportByCpu> B<true>|B<false>
 
-When true reports usage for all cores. When false, reports cpu usage
-aggregated over all cores.
-Defaults to true.
+When set to B<true>, the default, reports per-CPU (per-core) metrics.
+When set to B<false>, instead of reporting metrics for individual CPUs, only a
+global sum of CPU states is emitted.
 
 =item B<ValuesPercentage> B<false>|B<true>
 
-When true report percentage usage instead of tick values. Defaults to false.
+This option is only considered when both, B<ReportByCpu> and B<ReportByState>
+are set to B<true>. In this case, by default, metrics will be reported as
+Jiffies. By setting this option to B<true>, you can request percentage values
+in the un-aggregated (per-CPU, per-state) mode as well.
 
 =back
 
-
 =head2 Plugin C<cpufreq>
 
 This plugin doesn't have any options. It reads
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 02fd96f..0000000
+++ /dev/null
@@ -1,1353 +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;
-       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/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 b4ceff6..475d18a 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -1,9 +1,10 @@
 /**
  * collectd - src/cpu.c
- * Copyright (C) 2005-2010  Florian octo Forster
- * Copyright (C) 2008      Oleg King
- * Copyright (C) 2009      Simon Kuhnle
- * Copyright (C) 2009      Manuel Sanmartin
+ * Copyright (C) 2005-2014  Florian octo Forster
+ * Copyright (C) 2008       Oleg King
+ * Copyright (C) 2009       Simon Kuhnle
+ * Copyright (C) 2009       Manuel Sanmartin
+ * Copyright (C) 2013-2014  Pierre-Yves Ritschard
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -11,7 +12,7 @@
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License along
@@ -23,6 +24,7 @@
  *   Oleg King <king2 at kaluga.ru>
  *   Simon Kuhnle <simon at blarzwurst.de>
  *   Manuel Sanmartin
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
  **/
 
 #include "collectd.h"
 # define CAN_USE_SYSCTL 0
 #endif
 
-#define CPU_SUBMIT_USER 0
-#define CPU_SUBMIT_SYSTEM 1
-#define CPU_SUBMIT_WAIT 2
-#define CPU_SUBMIT_NICE 3
-#define CPU_SUBMIT_SWAP 4
-#define CPU_SUBMIT_INTERRUPT 5
-#define CPU_SUBMIT_SOFTIRQ 6
-#define CPU_SUBMIT_STEAL 7
-#define CPU_SUBMIT_IDLE 8
-#define CPU_SUBMIT_ACTIVE 9
-#define CPU_SUBMIT_MAX 10
+#define CPU_STATE_USER 0
+#define CPU_STATE_SYSTEM 1
+#define CPU_STATE_WAIT 2
+#define CPU_STATE_NICE 3
+#define CPU_STATE_SWAP 4
+#define CPU_STATE_INTERRUPT 5
+#define CPU_STATE_SOFTIRQ 6
+#define CPU_STATE_STEAL 7
+#define CPU_STATE_IDLE 8
+#define CPU_STATE_ACTIVE 9 /* sum of (!idle) */
+#define CPU_STATE_MAX 10 /* #states */
 
 #if HAVE_STATGRAB_H
 # include <statgrab.h>
@@ -132,8 +134,8 @@ static mach_msg_type_number_t cpu_list_len;
 
 #if PROCESSOR_TEMPERATURE
 static int cpu_temp_retry_counter = 0;
-static int cpu_temp_retry_step   = 1;
-static int cpu_temp_retry_max    = 1;
+static int cpu_temp_retry_step    = 1;
+static int cpu_temp_retry_max     = 1;
 #endif /* PROCESSOR_TEMPERATURE */
 /* #endif PROCESSOR_CPU_LOAD_INFO */
 
@@ -170,82 +172,53 @@ static int numcpu;
 static int pnumcpu;
 #endif /* HAVE_PERFSTAT */
 
-static value_to_rate_state_t *values = NULL;
-static gauge_t agg_values[CPU_SUBMIT_MAX] = {
-       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+#define RATE_ADD(sum, val) do { \
+       if (isnan (sum))        \
+       (sum) = (val);          \
+       else if (!isnan (val))  \
+       (sum) += (val);         \
+} while (0)
 
+struct cpu_state_s
+{
+       value_to_rate_state_t conv;
+       gauge_t rate;
+       _Bool has_value;
 };
-static int cpu_cells = 0;
-static int cpu_count = 0;
+typedef struct cpu_state_s cpu_state_t;
+
+static cpu_state_t *cpu_states = NULL;
+static size_t cpu_states_num = 0; /* #cpu_states allocated */
 
+/* Highest CPU number in the current iteration. Used by the dispatch logic to
+ * determine how many CPUs there were. Reset to 0 by cpu_reset(). */
+static size_t global_cpu_num = 0;
 
 static _Bool report_by_cpu = 1;
+static _Bool report_by_state = 1;
 static _Bool report_percent = 0;
-static _Bool report_active = 0;
 
 static const char *config_keys[] =
 {
        "ReportByCpu",
-       "ReportActive",
+       "ReportByState",
        "ValuesPercentage"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-
-static int cpu_config (const char *key, const char *value)
+static int cpu_config (char const *key, char const *value) /* {{{ */
 {
-       if (strcasecmp (key, "ReportByCpu") == 0) {
+       if (strcasecmp (key, "ReportByCpu") == 0)
                report_by_cpu = IS_TRUE (value) ? 1 : 0;
-       }
-       if (strcasecmp (key, "ValuesPercentage") == 0) {
+       else if (strcasecmp (key, "ValuesPercentage") == 0)
                report_percent = IS_TRUE (value) ? 1 : 0;
-       }
-       if (strcasecmp (key, "ReportActive") == 0)
-               report_active = IS_TRUE (value) ? 1 : 0;
-       return (-1);
-}
-
-static int cpu_states_grow (void)
-{
-  void *tmp;
-  int size;
-  int i;
-
-  size = cpu_count * CPU_SUBMIT_MAX; /* always alloc for all states */
-
-  if (size <= 0)
-         return 0;
-
-  if (cpu_cells >= size)
-         return 0;
-
-  if (values == NULL) {
-         values = malloc(size * sizeof(*values));
-         if (values == NULL)
-                 return -1;
-         for (i = 0; i < size; i++)
-                 memset(&values[i], 0, sizeof(*values));
-         cpu_cells = size;
-         return 0;
-  }
-
-  tmp = realloc(values, size * sizeof(*values));
-
-  if (tmp == NULL) {
-         ERROR ("cpu plugin: could not reserve enough space to hold states");
-         values = NULL;
-         return -1;
-  }
-
-  values = tmp;
-
-  for (i = cpu_cells ; i < size; i++)
-         memset(&values[i], 0, sizeof(*values));
-
-  cpu_cells = size;
-  return 0;
-} /* cpu_states_grow */
+       else if (strcasecmp (key, "ReportByState") == 0)
+               report_by_state = IS_TRUE (value) ? 1 : 0;
+       else
+               return (-1);
 
+       return (0);
+} /* }}} int cpu_config */
 
 static int init (void)
 {
@@ -357,11 +330,11 @@ static void submit_value (int cpu_num, int cpu_state, const char *type, value_t
        sstrncpy (vl.plugin, "cpu", sizeof (vl.plugin));
        sstrncpy (vl.type, type, sizeof (vl.type));
        sstrncpy (vl.type_instance, cpu_state_names[cpu_state],
-                 sizeof (vl.type_instance));
+                       sizeof (vl.type_instance));
 
        if (cpu_num >= 0) {
                ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
-                          "%i", cpu_num);
+                               "%i", cpu_num);
        }
        plugin_dispatch_values (&vl);
 }
@@ -382,144 +355,223 @@ static void submit_derive(int cpu_num, int cpu_state, derive_t derive)
        submit_value (cpu_num, cpu_state, "cpu", value);
 }
 
-static void submit_flush (void)
+/* Takes the zero-index number of a CPU and makes sure that the module-global
+ * cpu_states buffer is large enough. Returne ENOMEM on erorr. */
+static int cpu_states_alloc (size_t cpu_num) /* {{{ */
 {
-       int i = 0;
-       int cpu_submit_max = CPU_SUBMIT_MAX;
+       cpu_state_t *tmp;
+       size_t sz;
 
-       if (report_by_cpu) {
-               cpu_count = 0;
-               return;
-       }
+       if (cpu_num < 0)
+               return (EINVAL);
 
-       if (report_active)
-               cpu_submit_max = CPU_SUBMIT_MAX;
-       else
-               cpu_submit_max = CPU_SUBMIT_ACTIVE;
-       for (i = 0; i < cpu_submit_max; i++) {
-               if (agg_values[i] == -1)
-                       continue;
+       sz = (((size_t) cpu_num) + 1) * CPU_STATE_MAX;
+       assert (sz > 0);
 
-               if (report_percent)
-                       submit_percent(-1, i, agg_values[i] / cpu_count);
-               else
-                       submit_derive(-1, i, agg_values[i]);
-               agg_values[i] = -1;
+       /* We already have enough space. */
+       if (cpu_states_num >= sz)
+               return 0;
+
+       tmp = realloc (cpu_states, sz * sizeof (*cpu_states));
+       if (tmp == NULL)
+       {
+               ERROR ("cpu plugin: realloc failed.");
+               return (ENOMEM);
        }
-       cpu_count = 0;
-}
+       cpu_states = tmp;
+       tmp = cpu_states + cpu_states_num;
+
+       memset (tmp, 0, (sz - cpu_states_num) * sizeof (*cpu_states));
+       cpu_states_num = sz;
+       return 0;
+} /* }}} cpu_states_alloc */
 
-static void submit (int cpu_num, derive_t *derives)
+static cpu_state_t *get_cpu_state (size_t cpu_num, size_t state) /* {{{ */
 {
+       size_t index = ((cpu_num * CPU_STATE_MAX) + state);
 
-       int i = 0;
-       int cpu_submit_max = CPU_SUBMIT_MAX;
+       if (index >= cpu_states_num)
+               return (NULL);
 
-       if (report_active)
-               cpu_submit_max = CPU_SUBMIT_MAX;
-       else
-               cpu_submit_max = CPU_SUBMIT_ACTIVE;
+       return (&cpu_states[index]);
+} /* }}} cpu_state_t *get_cpu_state */
+
+/* Populates the per-CPU CPU_STATE_ACTIVE rate and the global rate_by_state
+ * array. */
+static void aggregate (gauge_t *sum_by_state) /* {{{ */
+{
+       size_t cpu_num;
+       size_t state;
 
-       if (!report_percent && report_by_cpu) {
-               derive_t cpu_active = 0;
-               for (i = 0; i < CPU_SUBMIT_ACTIVE; i++)
+       for (state = 0; state < CPU_STATE_MAX; state++)
+               sum_by_state[state] = NAN;
+
+       for (cpu_num = 0; cpu_num < global_cpu_num; cpu_num++)
+       {
+               cpu_state_t *this_cpu_states = get_cpu_state (cpu_num, 0);
+
+               this_cpu_states[CPU_STATE_ACTIVE].rate = NAN;
+
+               for (state = 0; state < CPU_STATE_ACTIVE; state++)
                {
-                       if (derives[i] == -1)
+                       if (!this_cpu_states[state].has_value)
                                continue;
 
-                       if (i != CPU_SUBMIT_IDLE)
-                               cpu_active += derives[i];
-
-                       submit_derive(cpu_num, i, derives[i]);
+                       RATE_ADD (sum_by_state[state], this_cpu_states[state].rate);
+                       if (state != CPU_STATE_IDLE)
+                               RATE_ADD (this_cpu_states[CPU_STATE_ACTIVE].rate, this_cpu_states[state].rate);
                }
-               if (report_active)
-                       submit_derive(cpu_num, CPU_SUBMIT_ACTIVE, cpu_active);
+
+               RATE_ADD (sum_by_state[CPU_STATE_ACTIVE], this_cpu_states[CPU_STATE_ACTIVE].rate);
+       }
+} /* }}} void aggregate */
+
+/* Commits (dispatches) the values for one CPU or the global aggregation.
+ * cpu_num is the index of the CPU to be committed or -1 in case of the global
+ * aggregation. rates is a pointer to CPU_STATE_MAX gauge_t values holding the
+ * current rate; each rate may be NAN. Calculates the percentage of each state
+ * and dispatches the metric. */
+static void cpu_commit_one (int cpu_num, /* {{{ */
+               gauge_t rates[static CPU_STATE_MAX])
+{
+       size_t state;
+       gauge_t sum;
+
+       sum = rates[CPU_STATE_ACTIVE];
+       RATE_ADD (sum, rates[CPU_STATE_IDLE]);
+
+       if (!report_by_state)
+       {
+               gauge_t percent = 100.0 * rates[CPU_STATE_ACTIVE] / sum;
+               submit_percent (cpu_num, CPU_STATE_ACTIVE, percent);
+               return;
        }
-       else {
-               cdtime_t cdt;
-               gauge_t value;
-               gauge_t cpu_total = 0;
-               gauge_t cpu_active = 0;
-               gauge_t local_rates[CPU_SUBMIT_MAX];
-
-               cpu_count++;
-               if (cpu_states_grow())
-                       return;
-
-               memset(local_rates, 0, sizeof(local_rates));
-
-               cdt = cdtime();
-               for (i = 0; i < CPU_SUBMIT_ACTIVE; i++) {
-                       if (report_percent) {
-                               value_t rate;
-                               int index;
-
-                               if (derives[i] == -1)
-                                       continue;
-
-                               index = (cpu_num * CPU_SUBMIT_MAX) + i;
-                               if (value_to_rate(&rate, derives[i], &values[index],
-                                                       DS_TYPE_DERIVE, cdt) != 0) {
-                                       local_rates[i] = -1;
-                                       continue;
-                               }
-
-                               local_rates[i] = rate.gauge;
-                               cpu_total += rate.gauge;
-                               if (i != CPU_SUBMIT_IDLE)
-                                       cpu_active += rate.gauge;
-                       }
-                       else {
-                               cpu_total += derives[i];
-                               if (i != CPU_SUBMIT_IDLE)
-                                       cpu_active += derives[i];
-                       }
-               }
-               if (cpu_total == 0.0)
-                       return;
 
-               if (report_active)
-                       local_rates[CPU_SUBMIT_ACTIVE] = cpu_active;
+       for (state = 0; state < CPU_STATE_ACTIVE; state++)
+       {
+               gauge_t percent = 100.0 * rates[state] / sum;
+               submit_percent (cpu_num, state, percent);
+       }
+} /* }}} void cpu_commit_one */
+
+/* Resets the internal aggregation. This is called by the read callback after
+ * each iteration / after each call to cpu_commit(). */
+static void cpu_reset (void) /* {{{ */
+{
+       size_t i;
+
+       for (i = 0; i < cpu_states_num; i++)
+               cpu_states[i].has_value = 0;
+
+       global_cpu_num = 0;
+} /* }}} void cpu_reset */
+
+/* Legacy behavior: Dispatches the raw derive values without any aggregation. */
+static void cpu_commit_without_aggregation (void) /* {{{ */
+{
+       size_t cpu_num;
 
-               for (i = 0; i < cpu_submit_max; i++) {
-                       if (local_rates[i] == -1)
+       for (cpu_num = 0; cpu_num < global_cpu_num; cpu_num++)
+       {
+               int state;
+
+               for (state = 0; state < CPU_STATE_ACTIVE; state++)
+               {
+                       cpu_state_t *s = get_cpu_state (cpu_num, state);
+
+                       if (!s->has_value)
                                continue;
 
-                       if (report_percent)
-                               value = (local_rates[i] / cpu_total) * 100;
-                       else
-                               value = derives[i];
-                       if (report_by_cpu) {
-                               if (report_percent) {
-                                       submit_percent (cpu_num, i, value);
-                               } else {
-                                       submit_derive(cpu_num, i, value);
-                               }
-                       }
-                       else {
-                               if (agg_values[i] == -1)
-                                       agg_values[i] = value;
-                               else
-                                       agg_values[i] += value;
-                       }
+                       submit_derive ((int) cpu_num, (int) state, s->conv.last_value.derive);
                }
        }
-}
+} /* }}} void cpu_commit_without_aggregation */
+
+/* Aggregates the internal state and dispatches the metrics. */
+static void cpu_commit (void) /* {{{ */
+{
+       gauge_t global_rates[CPU_STATE_MAX] = {
+               NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN
+       };
+       size_t cpu_num;
+
+       if (report_by_cpu && report_by_state && !report_percent)
+       {
+               cpu_commit_without_aggregation ();
+               return;
+       }
+
+       aggregate (global_rates);
+
+       if (!report_by_cpu)
+       {
+               cpu_commit_one (-1, global_rates);
+               return;
+       }
+
+       for (cpu_num = 0; cpu_num < global_cpu_num; cpu_num++)
+       {
+               cpu_state_t *this_cpu_states = get_cpu_state (cpu_num, 0);
+               gauge_t local_rates[CPU_STATE_MAX] = {
+                       NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN
+               };
+               size_t state;
+
+               for (state = 0; state < CPU_STATE_ACTIVE; state++)
+                       if (this_cpu_states[state].has_value)
+                               local_rates[state] = this_cpu_states[state].rate;
+
+               cpu_commit_one ((int) cpu_num, local_rates);
+       }
+} /* }}} void cpu_commit */
+
+/* Adds a derive value to the internal state. This should be used by each read
+ * function for each state. At the end of the iteration, the read function
+ * should call cpu_commit(). */
+static int cpu_stage (size_t cpu_num, size_t state, derive_t value, cdtime_t now) /* {{{ */
+{
+       int status;
+       cpu_state_t *s;
+       value_t v;
+
+       if (state >= CPU_STATE_ACTIVE)
+               return (EINVAL);
+
+       status = cpu_states_alloc (cpu_num);
+       if (status != 0)
+               return (status);
+
+       if (global_cpu_num <= cpu_num)
+               global_cpu_num = cpu_num + 1;
+
+       s = get_cpu_state (cpu_num, state);
+
+       v.gauge = NAN;
+       status = value_to_rate (&v, value, &s->conv, DS_TYPE_DERIVE, now);
+       if (status != 0)
+               return (status);
+
+       s->rate = v.gauge;
+       s->has_value = 1;
+       return (0);
+} /* }}} int cpu_stage */
 
 static int cpu_read (void)
 {
-#if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE
+       cdtime_t now = cdtime ();
+
+#if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE /* {{{ */
        int cpu;
 
        kern_return_t status;
 
 #if PROCESSOR_CPU_LOAD_INFO
        processor_cpu_load_info_data_t cpu_info;
-       mach_msg_type_number_t         cpu_info_len;
+       mach_msg_type_number_t         cpu_info_len;
 #endif
 #if PROCESSOR_TEMPERATURE
-       processor_info_data_t          cpu_temp;
-       mach_msg_type_number_t         cpu_temp_len;
+       processor_info_data_t          cpu_temp;
+       mach_msg_type_number_t         cpu_temp_len;
 #endif
 
        host_t cpu_host;
@@ -527,10 +579,6 @@ static int cpu_read (void)
        for (cpu = 0; cpu < cpu_list_len; cpu++)
        {
 #if PROCESSOR_CPU_LOAD_INFO
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-               memset(derives, -1, sizeof(derives));
                cpu_host = 0;
                cpu_info_len = PROCESSOR_BASIC_INFO_COUNT;
 
@@ -548,13 +596,12 @@ static int cpu_read (void)
                        continue;
                }
 
-               derives[CPU_SUBMIT_USER] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_USER];
-               derives[CPU_SUBMIT_NICE] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_NICE];
-               derives[CPU_SUBMIT_SYSTEM] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM];
-               derives[CPU_SUBMIT_IDLE] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_IDLE];
-               submit (cpu, derives);
-
+               cpu_stage (cpu, CPU_STATE_USER,   (derive_t) cpu_info.cpu_ticks[CPU_STATE_USER],   now);
+               cpu_stage (cpu, CPU_STATE_NICE,   (derive_t) cpu_info.cpu_ticks[CPU_STATE_NICE],   now);
+               cpu_stage (cpu, CPU_STATE_SYSTEM, (derive_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM], now);
+               cpu_stage (cpu, CPU_STATE_IDLE,   (derive_t) cpu_info.cpu_ticks[CPU_STATE_IDLE],   now);
 #endif /* PROCESSOR_CPU_LOAD_INFO */
+
 #if PROCESSOR_TEMPERATURE
                /*
                 * Not all Apple computers do have this ability. To minimize
@@ -598,10 +645,9 @@ static int cpu_read (void)
                cpu_temp_retry_step    = 1;
 #endif /* PROCESSOR_TEMPERATURE */
        }
-       submit_flush ();
-/* #endif PROCESSOR_CPU_LOAD_INFO */
+/* }}} #endif PROCESSOR_CPU_LOAD_INFO */
 
-#elif defined(KERNEL_LINUX)
+#elif defined(KERNEL_LINUX) /* {{{ */
        int cpu;
        FILE *fh;
        char buf[1024];
@@ -619,10 +665,6 @@ static int cpu_read (void)
 
        while (fgets (buf, 1024, fh) != NULL)
        {
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-
                if (strncmp (buf, "cpu", 3))
                        continue;
                if ((buf[3] < '0') || (buf[3] > '9'))
@@ -633,28 +675,26 @@ static int cpu_read (void)
                        continue;
 
                cpu = atoi (fields[0] + 3);
-               derives[CPU_SUBMIT_USER] = atoll(fields[1]);
-               derives[CPU_SUBMIT_NICE] = atoll(fields[2]);
-               derives[CPU_SUBMIT_SYSTEM] = atoll(fields[3]);
-               derives[CPU_SUBMIT_IDLE] = atoll(fields[4]);
+
+               cpu_stage (cpu, CPU_STATE_USER,   (derive_t) atoll(fields[1]), now);
+               cpu_stage (cpu, CPU_STATE_NICE,   (derive_t) atoll(fields[2]), now);
+               cpu_stage (cpu, CPU_STATE_SYSTEM, (derive_t) atoll(fields[3]), now);
+               cpu_stage (cpu, CPU_STATE_IDLE,   (derive_t) atoll(fields[4]), now);
 
                if (numfields >= 8)
                {
-                       derives[CPU_SUBMIT_WAIT] = atoll(fields[5]);
-                       derives[CPU_SUBMIT_INTERRUPT] = atoll(fields[6]);
-                       derives[CPU_SUBMIT_SOFTIRQ] = atoll(fields[7]);
+                       cpu_stage (cpu, CPU_STATE_WAIT,      (derive_t) atoll(fields[5]), now);
+                       cpu_stage (cpu, CPU_STATE_INTERRUPT, (derive_t) atoll(fields[6]), now);
+                       cpu_stage (cpu, CPU_STATE_SOFTIRQ,   (derive_t) atoll(fields[7]), now);
 
                        if (numfields >= 9)
-                               derives[CPU_SUBMIT_STEAL] = atoll(fields[8]);
+                               cpu_stage (cpu, CPU_STATE_STEAL, (derive_t) atoll(fields[8]), now);
                }
-               submit(cpu, derives);
        }
-       submit_flush();
-
        fclose (fh);
-/* #endif defined(KERNEL_LINUX) */
+/* }}} #endif defined(KERNEL_LINUX) */
 
-#elif defined(HAVE_LIBKSTAT)
+#elif defined(HAVE_LIBKSTAT) /* {{{ */
        int cpu;
        static cpu_stat_t cs;
 
@@ -663,24 +703,17 @@ static int cpu_read (void)
 
        for (cpu = 0; cpu < numcpu; cpu++)
        {
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-
                if (kstat_read (kc, ksp[cpu], &cs) == -1)
                        continue; /* error message? */
 
-               memset(derives, -1, sizeof(derives));
-               derives[CPU_SUBMIT_IDLE] = cs.cpu_sysinfo.cpu[CPU_IDLE];
-               derives[CPU_SUBMIT_USER] = cs.cpu_sysinfo.cpu[CPU_USER];
-               derives[CPU_SUBMIT_SYSTEM] = cs.cpu_sysinfo.cpu[CPU_KERNEL];
-               derives[CPU_SUBMIT_WAIT] = cs.cpu_sysinfo.cpu[CPU_WAIT];
-               submit (ksp[cpu]->ks_instance, derives);
+               cpu_stage (ksp[cpu]->ks_instance, CPU_STATE_IDLE,   (derive_t) cs.cpu_sysinfo.cpu[CPU_IDLE],   now);
+               cpu_stage (ksp[cpu]->ks_instance, CPU_STATE_USER,   (derive_t) cs.cpu_sysinfo.cpu[CPU_USER],   now);
+               cpu_stage (ksp[cpu]->ks_instance, CPU_STATE_SYSTEM, (derive_t) cs.cpu_sysinfo.cpu[CPU_KERNEL], now);
+               cpu_stage (ksp[cpu]->ks_instance, CPU_STATE_WAIT,   (derive_t) cs.cpu_sysinfo.cpu[CPU_WAIT],   now);
        }
-       submit_flush ();
-/* #endif defined(HAVE_LIBKSTAT) */
+/* }}} #endif defined(HAVE_LIBKSTAT) */
 
-#elif CAN_USE_SYSCTL
+#elif CAN_USE_SYSCTL /* {{{ */
        uint64_t cpuinfo[numcpu][CPUSTATES];
        size_t cpuinfo_size;
        int status;
@@ -736,20 +769,15 @@ static int cpu_read (void)
        }
 
        for (i = 0; i < numcpu; i++) {
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-
-               derives[CPU_SUBMIT_USER] = cpuinfo[i][CP_USER];
-               derives[CPU_SUBMIT_NICE] = cpuinfo[i][CP_NICE];
-               derives[CPU_SUBMIT_SYSTEM] = cpuinfo[i][CP_SYS];
-               derives[CPU_SUBMIT_IDLE] = cpuinfo[i][CP_IDLE];
-               derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[i][CP_INTR];
-               submit(i, derives);
+               cpu_state (i, CPU_STATE_USER,      (derive_t) cpuinfo[i][CP_USER]);
+               cpu_state (i, CPU_STATE_NICE,      (derive_t) cpuinfo[i][CP_NICE]);
+               cpu_state (i, CPU_STATE_SYSTEM,    (derive_t) cpuinfo[i][CP_SYS]);
+               cpu_state (i, CPU_STATE_IDLE,      (derive_t) cpuinfo[i][CP_IDLE]);
+               cpu_state (i, CPU_STATE_INTERRUPT, (derive_t) cpuinfo[i][CP_INTR]);
        }
-       submit_flush();
-/* #endif CAN_USE_SYSCTL */
-#elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES)
+/* }}} #endif CAN_USE_SYSCTL */
+
+#elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES) /* {{{ */
        long cpuinfo[maxcpu][CPUSTATES];
        size_t cpuinfo_size;
        int i;
@@ -766,26 +794,17 @@ static int cpu_read (void)
        }
 
        for (i = 0; i < numcpu; i++) {
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-
-               derives[CPU_SUBMIT_USER] = cpuinfo[i][CP_USER];
-               derives[CPU_SUBMIT_NICE] = cpuinfo[i][CP_NICE];
-               derives[CPU_SUBMIT_SYSTEM] = cpuinfo[i][CP_SYS];
-               derives[CPU_SUBMIT_IDLE] = cpuinfo[i][CP_IDLE];
-               derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[i][CP_INTR];
-               submit(i, derives);
+               cpu_state (i, CPU_STATE_USER,      (derive_t) cpuinfo[i][CP_USER]);
+               cpu_state (i, CPU_STATE_NICE,      (derive_t) cpuinfo[i][CP_NICE]);
+               cpu_state (i, CPU_STATE_SYSTEM,    (derive_t) cpuinfo[i][CP_SYS]);
+               cpu_state (i, CPU_STATE_IDLE,      (derive_t) cpuinfo[i][CP_IDLE]);
+               cpu_state (i, CPU_STATE_INTERRUPT, (derive_t) cpuinfo[i][CP_INTR]);
        }
-       submit_flush();
+/* }}} #endif HAVE_SYSCTL_KERN_CP_TIMES */
 
-/* #endif HAVE_SYSCTL_KERN_CP_TIMES */
-#elif defined(HAVE_SYSCTLBYNAME)
+#elif defined(HAVE_SYSCTLBYNAME) /* {{{ */
        long cpuinfo[CPUSTATES];
        size_t cpuinfo_size;
-       derive_t derives[CPU_SUBMIT_MAX] = {
-               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-       };
 
        cpuinfo_size = sizeof (cpuinfo);
 
@@ -797,21 +816,15 @@ static int cpu_read (void)
                return (-1);
        }
 
-       derives[CPU_SUBMIT_USER] = cpuinfo[CP_USER];
-       derives[CPU_SUBMIT_SYSTEM] = cpuinfo[CP_SYS];
-       derives[CPU_SUBMIT_NICE] = cpuinfo[CP_NICE];
-       derives[CPU_SUBMIT_IDLE] = cpuinfo[CP_IDLE];
-       derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[CP_INTR];
-       submit(0, derives);
-       submit_flush();
-
-/* #endif HAVE_SYSCTLBYNAME */
+       cpu_state (0, CPU_STATE_USER,      (derive_t) cpuinfo[CP_USER]);
+       cpu_state (0, CPU_STATE_NICE,      (derive_t) cpuinfo[CP_NICE]);
+       cpu_state (0, CPU_STATE_SYSTEM,    (derive_t) cpuinfo[CP_SYS]);
+       cpu_state (0, CPU_STATE_IDLE,      (derive_t) cpuinfo[CP_IDLE]);
+       cpu_state (0, CPU_STATE_INTERRUPT, (derive_t) cpuinfo[CP_INTR]);
+/* }}} #endif HAVE_SYSCTLBYNAME */
 
-#elif defined(HAVE_LIBSTATGRAB)
+#elif defined(HAVE_LIBSTATGRAB) /* {{{ */
        sg_cpu_stats *cs;
-       derive_t derives[CPU_SUBMIT_MAX] = {
-               -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-       };
        cs = sg_get_cpu_stats ();
 
        if (cs == NULL)
@@ -820,17 +833,15 @@ static int cpu_read (void)
                return (-1);
        }
 
-       derives[CPU_SUBMIT_IDLE] = (derive_t) cs->idle;
-       derives[CPU_SUBMIT_NICE] = (derive_t) cs->nice;
-       derives[CPU_SUBMIT_SWAP] = (derive_t) cs->swap;
-       derives[CPU_SUBMIT_SYSTEM] = (derive_t) cs->kernel;
-       derives[CPU_SUBMIT_USER] = (derive_t) cs->user;
-       derives[CPU_SUBMIT_WAIT] = (derive_t) cs->iowait;
-       submit(0, derives);
-       submit_flush();
-/* #endif HAVE_LIBSTATGRAB */
+       cpu_state (0, CPU_STATE_IDLE,   (derive_t) cs->idle);
+       cpu_state (0, CPU_STATE_NICE,   (derive_t) cs->nice);
+       cpu_state (0, CPU_STATE_SWAP,   (derive_t) cs->swap);
+       cpu_state (0, CPU_STATE_SYSTEM, (derive_t) cs->kernel);
+       cpu_state (0, CPU_STATE_USER,   (derive_t) cs->user);
+       cpu_state (0, CPU_STATE_WAIT,   (derive_t) cs->iowait);
+/* }}} #endif HAVE_LIBSTATGRAB */
 
-#elif defined(HAVE_PERFSTAT)
+#elif defined(HAVE_PERFSTAT) /* {{{ */
        perfstat_id_t id;
        int i, cpus;
 
@@ -862,18 +873,15 @@ static int cpu_read (void)
 
        for (i = 0; i < cpus; i++)
        {
-               derive_t derives[CPU_SUBMIT_MAX] = {
-                       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-               };
-               derives[CPU_SUBMIT_IDLE] = perfcpu[i].idle;
-               derives[CPU_SUBMIT_SYSTEM] = perfcpu[i].sys;
-               derives[CPU_SUBMIT_USER] = perfcpu[i].user;
-               derives[CPU_SUBMIT_WAIT] = perfcpu[i].wait;
-               submit(i, derives);
+               cpu_stage (i, CPU_STATE_IDLE,   (derive_t) perfcpu[i].idle, now);
+               cpu_stage (i, CPU_STATE_SYSTEM, (derive_t) perfcpu[i].sys,  now);
+               cpu_stage (i, CPU_STATE_USER,   (derive_t) perfcpu[i].user, now);
+               cpu_stage (i, CPU_STATE_WAIT,   (derive_t) perfcpu[i].wait, now);
        }
-       submit_flush();
-#endif /* HAVE_PERFSTAT */
+#endif /* }}} HAVE_PERFSTAT */
 
+       cpu_commit ();
+       cpu_reset ();
        return (0);
 }
 
@@ -883,3 +891,5 @@ void module_register (void)
        plugin_register_config ("cpu", cpu_config, config_keys, config_keys_num);
        plugin_register_read ("cpu", cpu_read);
 } /* void module_register */
+
+/* vim: set sw=8 sts=8 noet fdm=marker : */
index e9a409d..b6e535e 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -25,7 +25,6 @@
 #include "plugin.h"
 #include "common.h"
 #include "utils_cache.h"
-#include "utils_parse_option.h"
 
 /*
  * Private variables
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/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 : */
index 5c7bbd5..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. */
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>