Merge branch 'collectd-5.4' into collectd-5.5
authorFlorian Forster <octo@collectd.org>
Sat, 5 Dec 2015 21:06:35 +0000 (22:06 +0100)
committerFlorian Forster <octo@collectd.org>
Sat, 5 Dec 2015 21:06:35 +0000 (22:06 +0100)
1  2 
configure.ac
src/bind.c
src/curl_xml.c
src/daemon/utils_cache.c
src/libcollectdclient/client.c
src/statsd.c
src/unixsock.c
src/varnish.c

diff --combined configure.ac
@@@ -1,6 -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])
  
@@@ -88,11 -88,9 +88,11 @@@ case $host_os i
        ac_system="Solaris"
        ;;
        *darwin*)
 +      AC_DEFINE([KERNEL_DARWIN], 1, [True if program is to be compiled for a Darwin kernel])
        ac_system="Darwin"
        ;;
        *openbsd*)
 +      AC_DEFINE([KERNEL_OPENBSD], 1, [True if program is to be compiled for an OpenBSD kernel])
        ac_system="OpenBSD"
        ;;
        *aix*)
  esac
  AC_MSG_RESULT([$ac_system])
  
 +AM_CONDITIONAL([BUILD_LINUX],[test "x$ac_system" = "xLinux"])
 +AM_CONDITIONAL([BUILD_SOLARIS],[test "x$ac_system" = "xSolaris"])
 +AM_CONDITIONAL([BUILD_DARWIN],[test "x$ac_system" = "xDarwin"])
 +AM_CONDITIONAL([BUILD_OPENBSD],[test "x$ac_system" = "xOpenBSD"])
 +AM_CONDITIONAL([BUILD_AIX],[test "x$ac_system" = "xAIX"])
 +AM_CONDITIONAL([BUILD_FREEBSD],[test "x$ac_system" = "xFreeBSD"])
 +
  if test "x$ac_system" = "xLinux"
  then
        AC_ARG_VAR([KERNEL_DIR], [path to Linux kernel sources])
@@@ -131,20 -122,7 +131,20 @@@ if test "x$ac_system" = "xSolaris
  then
        AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Define to enforce POSIX thread semantics under Solaris.])
        AC_DEFINE(_REENTRANT,               1, [Define to enable reentrancy interfaces.])
 +
 +      AC_MSG_CHECKING([whether compiler builds 64bit binaries])
 +      AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
 +                         #ifndef _LP64
 +                         # error "Compiler not in 64bit mode."
 +                         #endif
 +                         ])],
 +                         [AC_MSG_RESULT([yes])],
 +                         [
 +                          AC_MSG_RESULT([no])
 +                          AC_MSG_NOTICE([Solaris detected. Please consider building a 64-bit binary.])
 +                         ])
  fi
 +
  if test "x$ac_system" = "xAIX"
  then
        AC_DEFINE(_THREAD_SAFE_ERRNO, 1, [Define to use the thread-safe version of errno under AIX.])
@@@ -455,6 -433,18 +455,6 @@@ AC_CHECK_HEADERS(sys/swap.h vm/anon.h, 
  #endif
  ])
  
 -if test "x$have_sys_swap_h$ac_system" = "xnoSolaris"
 -then
 -      hint_64=""
 -      if test "x$GCC" = "xyes"
 -      then
 -              hint_64="CFLAGS='-m64'"
 -      else
 -              hint_64="CFLAGS='-xarch=v9'"
 -      fi
 -      AC_MSG_NOTICE([Solaris detected and sys/swap.h not usable. Try building a 64-bit binary ($hint_64 ./configure).])
 -fi
 -
  # For load module
  # For the processes plugin
  # For users module
@@@ -594,7 -584,7 +594,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 sys/vmmeter.h kvm.h wordexp.h locale.h)
  
  # For the dns plugin
  AC_CHECK_HEADERS(arpa/nameser.h)
@@@ -659,51 -649,20 +659,51 @@@ AC_CHECK_HEADERS(net/pfvar.h
  have_termios_h="no"
  AC_CHECK_HEADERS(termios.h, [have_termios_h="yes"])
  
 +# For the turbostat plugin
 +have_asm_msrindex_h="no"
 +AC_CHECK_HEADERS(asm/msr-index.h, [have_asm_msrindex_h="yes"])
 +
 +if test "x$have_asm_msrindex_h" = "xyes"
 +then
 +  AC_CACHE_CHECK([whether asm/msr-index.h has MSR_PKG_C10_RESIDENCY],
 +                 [c_cv_have_usable_asm_msrindex_h],
 +                 AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
 +[[[
 +#include<asm/msr-index.h>
 +]]],
 +[[[
 +int y = MSR_PKG_C10_RESIDENCY;
 +return(y);
 +]]]
 +  )],
 +                 [c_cv_have_usable_asm_msrindex_h="yes"],
 +                 [c_cv_have_usable_asm_msrindex_h="no"],
 +                                  )
 +                 )
 +fi
 +
 +have_cpuid_h="no"
 +AC_CHECK_HEADERS(cpuid.h, [have_cpuid_h="yes"])
 +
 +AC_CHECK_HEADERS(sys/capability.h)
  #
  # Checks for typedefs, structures, and compiler characteristics.
  #
  AC_C_CONST
 +AC_C_INLINE
 +AC_TYPE_OFF_T
  AC_TYPE_PID_T
  AC_TYPE_SIZE_T
 +AC_TYPE_SSIZE_T
  AC_TYPE_UID_T
 +AC_TYPE_UINT32_T
  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
  
@@@ -910,9 -869,7 +910,9 @@@ if test "x$have_swapctl" = "xyes"; the
  #  undef _LARGEFILE64_SOURCE
  #endif
  #include <sys/stat.h>
 +#include <sys/param.h>
  #include <sys/swap.h>
 +#include <unistd.h>
  ]]],
  [[[
  int num = swapctl(0, NULL);
  #  undef _LARGEFILE64_SOURCE
  #endif
  #include <sys/stat.h>
 +#include <sys/param.h>
  #include <sys/swap.h>
 +#include <unistd.h>
  ]]],
  [[[
  int num = swapctl(0, NULL, 0);
@@@ -1107,7 -1062,7 +1107,7 @@@ if test "x$fp_layout_type" = "xunknown"
        uint8_t c[8];
        double d;
  
 -      d = 8.642135e130; 
 +      d = 8.642135e130;
        memcpy ((void *) &i0, (void *) &d, 8);
  
        i1 = i0;
@@@ -1162,7 -1117,7 +1162,7 @@@ if test "x$fp_layout_type" = "xunknown"
        uint8_t c[8];
        double d;
  
 -      d = 8.642135e130; 
 +      d = 8.642135e130;
        memcpy ((void *) &i0, (void *) &d, 8);
  
        i1 = endianflip (i0);
@@@ -1211,7 -1166,7 +1211,7 @@@ if test "x$fp_layout_type" = "xunknown"
        uint8_t c[8];
        double d;
  
 -      d = 8.642135e130; 
 +      d = 8.642135e130;
        memcpy ((void *) &i0, (void *) &d, 8);
  
        i1 = intswap (i0);
    AC_MSG_ERROR([Didn't find out how doubles are stored in memory. Sorry.])
  fi; fi; fi
  
 +# --with-useragent {{{
 +AC_ARG_WITH(useragent, [AS_HELP_STRING([--with-useragent@<:@=AGENT@:>@], [User agent to use on http requests])],
 +[
 +    if test "x$withval" != "xno" && test "x$withval" != "xyes"
 +    then
 +        AC_DEFINE_UNQUOTED(COLLECTD_USERAGENT, ["$withval"], [User agent for http requests])
 +    fi
 +])
 +
 +# }}}
 +
  have_getfsstat="no"
  AC_CHECK_FUNCS(getfsstat, [have_getfsstat="yes"])
  have_getvfsstat="no"
@@@ -1364,7 -1308,7 +1364,7 @@@ AC_CACHE_CHECK([if have htonll defined]
      )],
      [c_cv_have_htonll="yes"],
      [c_cv_have_htonll="no"]
 -       )
 +  )
  )
  if test "x$c_cv_have_htonll" = "xyes"
  then
@@@ -1420,7 -1364,7 +1420,7 @@@ AC_CHECK_MEMBERS([struct kinfo_proc.ki_
  #include <sys/user.h>
        ])
  
 -AC_CHECK_MEMBERS([struct kinfo_proc.kp_proc, struct kinfo_proc.kp_eproc],
 +AC_CHECK_MEMBERS([struct kinfo_proc.p_pid, struct kinfo_proc.p_vm_rssize],
        [
                AC_DEFINE(HAVE_STRUCT_KINFO_PROC_OPENBSD, 1,
                        [Define if struct kinfo_proc exists in the OpenBSD variant.])
  
  AC_CHECK_MEMBERS([struct udphdr.uh_dport, struct udphdr.uh_sport], [], [],
  [#define _BSD_SOURCE
 +#define _DEFAULT_SOURCE
  #if HAVE_STDINT_H
  # include <stdint.h>
  #endif
  ])
  AC_CHECK_MEMBERS([struct udphdr.dest, struct udphdr.source], [], [],
  [#define _BSD_SOURCE
 +#define _DEFAULT_SOURCE
  #if HAVE_STDINT_H
  # include <stdint.h>
  #endif
  m4_divert_once([HELP_WITH], [
  collectd additional packages:])
  
 -AM_CONDITIONAL([BUILD_FREEBSD],[test "x$x$ac_system" = "xFreeBSD"])
 -
 -AM_CONDITIONAL([BUILD_AIX],[test "x$x$ac_system" = "xAIX"]) 
 -
  if test "x$ac_system" = "xAIX"
  then
        with_perfstat="yes"
  AM_CONDITIONAL(BUILD_WITH_LIBAQUAERO5, test "x$with_libaquaero5" = "xyes")
  # }}}
  
 -# --with-libcredis {{{
 -AC_ARG_WITH(libcredis, [AS_HELP_STRING([--with-libcredis@<:@=PREFIX@:>@], [Path to libcredis.])],
 +# --with-libhiredis {{{
 +AC_ARG_WITH(libhiredis, [AS_HELP_STRING([--with-libhiredis@<:@=PREFIX@:>@],
 +      [Path to libhiredis.])],
  [
   if test "x$withval" = "xyes"
   then
 -       with_libcredis="yes"
 +       with_libhiredis="yes"
   else if test "x$withval" = "xno"
   then
 -       with_libcredis="no"
 +       with_libhiredis="no"
   else
 -       with_libcredis="yes"
 -       LIBCREDIS_CPPFLAGS="$LIBCREDIS_CPPFLAGS -I$withval/include"
 -       LIBCREDIS_LDFLAGS="$LIBCREDIS_LDFLAGS -L$withval/lib"
 +       with_libhiredis="yes"
 +       LIBHIREDIS_CPPFLAGS="$LIBHIREDIS_CPPFLAGS -I$withval/include"
 +       LIBHIREDIS_LDFLAGS="$LIBHIREDIS_LDFLAGS -L$withval/lib"
   fi; fi
  ],
 -[with_libcredis="yes"])
 +[with_libhiredis="yes"])
  
  SAVE_CPPFLAGS="$CPPFLAGS"
  SAVE_LDFLAGS="$LDFLAGS"
  
 -CPPFLAGS="$CPPFLAGS $LIBCREDIS_CPPFLAGS"
 -LDFLAGS="$LDFLAGS $LIBCREDIS_LDFLAGS"
 +CPPFLAGS="$CPPFLAGS $LIBHIREDIS_CPPFLAGS"
 +LDFLAGS="$LDFLAGS $LIBHIREDIS_LDFLAGS"
  
 -if test "x$with_libcredis" = "xyes"
 +if test "x$with_libhiredis" = "xyes"
  then
 -      if test "x$LIBCREDIS_CPPFLAGS" != "x"
 +      if test "x$LIBHIREDIS_CPPFLAGS" != "x"
        then
 -              AC_MSG_NOTICE([libcredis CPPFLAGS: $LIBCREDIS_CPPFLAGS])
 +              AC_MSG_NOTICE([libhiredis CPPFLAGS: $LIBHIREDIS_CPPFLAGS])
        fi
 -      AC_CHECK_HEADERS(credis.h,
 -      [with_libcredis="yes"],
 -      [with_libcredis="no (credis.h not found)"])
 +      AC_CHECK_HEADERS(hiredis/hiredis.h,
 +      [with_libhiredis="yes"],
 +      [with_libhiredis="no (hiredis.h not found)"])
  fi
 -if test "x$with_libcredis" = "xyes"
 +if test "x$with_libhiredis" = "xyes"
  then
 -      if test "x$LIBCREDIS_LDFLAGS" != "x"
 +      if test "x$LIBHIREDIS_LDFLAGS" != "x"
        then
 -              AC_MSG_NOTICE([libcredis LDFLAGS: $LIBCREDIS_LDFLAGS])
 +              AC_MSG_NOTICE([libhiredis LDFLAGS: $LIBHIREDIS_LDFLAGS])
        fi
 -      AC_CHECK_LIB(credis, credis_info,
 -      [with_libcredis="yes"],
 -      [with_libcredis="no (symbol 'credis_info' not found)"])
 +      AC_CHECK_LIB(hiredis, redisCommand,
 +      [with_libhiredis="yes"],
 +      [with_libhiredis="no (symbol 'redisCommand' not found)"])
  
  fi
  
  CPPFLAGS="$SAVE_CPPFLAGS"
  LDFLAGS="$SAVE_LDFLAGS"
  
 -if test "x$with_libcredis" = "xyes"
 +if test "x$with_libhiredis" = "xyes"
  then
 -      BUILD_WITH_LIBCREDIS_CPPFLAGS="$LIBCREDIS_CPPFLAGS"
 -      BUILD_WITH_LIBCREDIS_LDFLAGS="$LIBCREDIS_LDFLAGS"
 -      AC_SUBST(BUILD_WITH_LIBCREDIS_CPPFLAGS)
 -      AC_SUBST(BUILD_WITH_LIBCREDIS_LDFLAGS)
 +      BUILD_WITH_LIBHIREDIS_CPPFLAGS="$LIBHIREDIS_CPPFLAGS"
 +      BUILD_WITH_LIBHIREDIS_LDFLAGS="$LIBHIREDIS_LDFLAGS"
 +      AC_SUBST(BUILD_WITH_LIBHIREDIS_CPPFLAGS)
 +      AC_SUBST(BUILD_WITH_LIBHIREDIS_LDFLAGS)
  fi
 -AM_CONDITIONAL(BUILD_WITH_LIBCREDIS, test "x$with_libcredis" = "xyes")
 +AM_CONDITIONAL(BUILD_WITH_LIBHIREDIS, test "x$with_libhiredis" = "xyes")
  # }}}
  
  # --with-libcurl {{{
                 [with_libcurl="yes"],
                 [with_libcurl="no (symbol 'curl_easy_init' not found)"],
                 [$with_curl_libs])
 +              AC_CHECK_DECL(CURLOPT_USERNAME,
 +               [have_curlopt_username="yes"],
 +               [have_curlopt_username="no"],
 +               [[#include <curl/curl.h>]])
 +              AC_CHECK_DECL(CURLOPT_TIMEOUT_MS,
 +               [have_curlopt_timeout="yes"],
 +               [have_curlopt_timeout="no"],
 +               [[#include <curl/curl.h>]])
        fi
  fi
  if test "x$with_libcurl" = "xyes"
        BUILD_WITH_LIBCURL_LIBS="$with_curl_libs"
        AC_SUBST(BUILD_WITH_LIBCURL_CFLAGS)
        AC_SUBST(BUILD_WITH_LIBCURL_LIBS)
 +
 +      if test "x$have_curlopt_username" = "xyes"
 +      then
 +              AC_DEFINE(HAVE_CURLOPT_USERNAME, 1, [Define if libcurl supports CURLOPT_USERNAME option.])
 +      fi
 +
 +      if test "x$have_curlopt_timeout" = "xyes"
 +      then
 +              AC_DEFINE(HAVE_CURLOPT_TIMEOUT_MS, 1, [Define if libcurl supports CURLOPT_TIMEOUT_MS option.])
 +      fi
  fi
  AM_CONDITIONAL(BUILD_WITH_LIBCURL, test "x$with_libcurl" = "xyes")
  # }}}
@@@ -2091,7 -2018,10 +2091,7 @@@ AM_CONDITIONAL(BUILD_WITH_LIBGCRYPT, te
  # --with-libiptc {{{
  AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])],
  [
 -      if test "x$withval" = "xshipped"
 -      then
 -              with_libiptc="own"
 -      else if test "x$withval" = "xyes"
 +      if test "x$withval" = "xyes"
        then
                with_libiptc="pkgconfig"
        else if test "x$withval" = "xno"
                with_libiptc="yes"
                with_libiptc_cflags="-I$withval/include"
                with_libiptc_libs="-L$withval/lib"
 -      fi; fi; fi
 +      fi; fi
  ],
  [
        if test "x$ac_system" = "xLinux"
  
  CPPFLAGS="$SAVE_CPPFLAGS"
  
 -if test "x$with_libiptc" = "xown"
 -then
 -      with_libiptc_cflags=""
 -      with_libiptc_libs=""
 -fi
 -if test "x$with_libiptc" = "xown"
 -then
 -      AC_CHECK_HEADERS(linux/netfilter_ipv4/ip_tables.h linux/netfilter_ipv6/ip6_tables.h linux/netfilter/x_tables.h, [],
 -      [
 -              with_libiptc="no (Linux iptables headers not found)"
 -      ],
 -      [
 -#include "$srcdir/src/owniptc/ipt_kernel_headers.h"
 -      ])
 -fi
 -AM_CONDITIONAL(BUILD_WITH_OWN_LIBIPTC, test "x$with_libiptc" = "xown")
 -if test "x$with_libiptc" = "xown"
 -then
 -      AC_DEFINE(OWN_LIBIPTC, 1, [Define to 1 if we use the shipped iptc library.])
 -      with_libiptc="yes"
 -fi
 -
  AM_CONDITIONAL(BUILD_WITH_LIBIPTC, test "x$with_libiptc" = "xyes")
  if test "x$with_libiptc" = "xyes"
  then
@@@ -2233,7 -2185,7 +2233,7 @@@ the
                fi
  
                AC_MSG_CHECKING([for libjvm.so])
-               TMPVAR=`find -L "$with_java_home" -name libjvm.so -type f -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1`
+               TMPVAR=`find -L "$with_java_home" -type f -name libjvm.so -o -name libjvm.dylib -exec 'dirname' '{}' ';' 2>/dev/null | head -n 1`
                if test "x$TMPVAR" != "x"
                then
                        AC_MSG_RESULT([found in $TMPVAR])
@@@ -2356,64 -2308,6 +2356,64 @@@ AC_SUBST(JAVA_LIBS
  AM_CONDITIONAL(BUILD_WITH_JAVA, test "x$with_java" = "xyes")
  # }}}
  
 +# --with-libldap {{{
 +AC_ARG_WITH(libldap, [AS_HELP_STRING([--with-libldap@<:@=PREFIX@:>@], [Path to libldap.])],
 +[
 + if test "x$withval" = "xyes"
 + then
 +       with_libldap="yes"
 + else if test "x$withval" = "xno"
 + then
 +       with_libldap="no"
 + else
 +       with_libldap="yes"
 +       LIBLDAP_CPPFLAGS="$LIBLDAP_CPPFLAGS -I$withval/include"
 +       LIBLDAP_LDFLAGS="$LIBLDAP_LDFLAGS -L$withval/lib"
 + fi; fi
 +],
 +[with_libldap="yes"])
 +
 +SAVE_CPPFLAGS="$CPPFLAGS"
 +SAVE_LDFLAGS="$LDFLAGS"
 +
 +CPPFLAGS="$CPPFLAGS $LIBLDAP_CPPFLAGS"
 +LDFLAGS="$LDFLAGS $LIBLDAP_LDFLAGS"
 +
 +if test "x$with_libldap" = "xyes"
 +then
 +      if test "x$LIBLDAP_CPPFLAGS" != "x"
 +      then
 +              AC_MSG_NOTICE([libldap CPPFLAGS: $LIBLDAP_CPPFLAGS])
 +      fi
 +      AC_CHECK_HEADERS(ldap.h,
 +      [with_libldap="yes"],
 +      [with_libldap="no ('ldap.h' not found)"])
 +fi
 +if test "x$with_libldap" = "xyes"
 +then
 +      if test "x$LIBLDAP_LDFLAGS" != "x"
 +      then
 +              AC_MSG_NOTICE([libldap LDFLAGS: $LIBLDAP_LDFLAGS])
 +      fi
 +      AC_CHECK_LIB(ldap, ldap_initialize,
 +      [with_libldap="yes"],
 +      [with_libldap="no (symbol 'ldap_initialize' not found)"])
 +
 +fi
 +
 +CPPFLAGS="$SAVE_CPPFLAGS"
 +LDFLAGS="$SAVE_LDFLAGS"
 +
 +if test "x$with_libldap" = "xyes"
 +then
 +      BUILD_WITH_LIBLDAP_CPPFLAGS="$LIBLDAP_CPPFLAGS"
 +      BUILD_WITH_LIBLDAP_LDFLAGS="$LIBLDAP_LDFLAGS"
 +      AC_SUBST(BUILD_WITH_LIBLDAP_CPPFLAGS)
 +      AC_SUBST(BUILD_WITH_LIBLDAP_LDFLAGS)
 +fi
 +AM_CONDITIONAL(BUILD_WITH_LIBLDAP, test "x$with_libldap" = "xyes")
 +# }}}
 +
  # --with-liblvm2app {{{
  with_liblvm2app_cppflags=""
  with_liblvm2app_ldflags=""
@@@ -2448,7 -2342,7 +2448,7 @@@ the
          CPPFLAGS="$CPPFLAGS $with_liblvm2app_cppflags"
          LDFLAGS="$LDFLAGS $with_liblvm2app_ldflags"
  
 -        AC_CHECK_LIB(lvm2app, lvm_init, [with_liblvm2app="yes"], [with_liblvm2app="no (Symbol 'lvm_init' not found)"])
 +        AC_CHECK_LIB(lvm2app, lvm_lv_get_property, [with_liblvm2app="yes"], [with_liblvm2app="no (Symbol 'lvm_lv_get_property' not found)"])
  
          CPPFLAGS="$SAVE_CPPFLAGS"
          LDFLAGS="$SAVE_LDFLAGS"
@@@ -2852,15 -2746,6 +2852,15 @@@ return (retval)
  fi
  if test "x$with_libmnl" = "xyes"
  then
 +      AC_CHECK_MEMBERS([struct rtnl_link_stats64.tx_window_errors],
 +      [AC_DEFINE(HAVE_RTNL_LINK_STATS64, 1, [Define if struct rtnl_link_stats64 exists and is usable.])],
 +      [],
 +      [
 +      #include <linux/if_link.h>
 +      ])
 +fi
 +if test "x$with_libmnl" = "xyes"
 +then
        AC_CHECK_LIB(mnl, mnl_nlmsg_get_payload,
                     [with_libmnl="yes"],
                     [with_libmnl="no (symbol 'mnl_nlmsg_get_payload' not found)"],
@@@ -2984,7 -2869,7 +2984,7 @@@ the
        else
                SAVE_CPPFLAGS="$CPPFLAGS"
                CPPFLAGS="$CPPFLAGS $with_snmp_cflags"
 -              
 +
                AC_CHECK_HEADERS(net-snmp/net-snmp-config.h, [], [with_libnetsnmp="no (net-snmp/net-snmp-config.h not found)"])
  
                CPPFLAGS="$SAVE_CPPFLAGS"
@@@ -3211,7 -3096,7 +3211,7 @@@ if test "x$with_libowcapi" = "xyes
  then
        SAVE_CPPFLAGS="$CPPFLAGS"
        CPPFLAGS="$with_libowcapi_cppflags"
 -      
 +
        AC_CHECK_HEADERS(owcapi.h, [with_libowcapi="yes"], [with_libowcapi="no (owcapi.h not found)"])
  
        CPPFLAGS="$SAVE_CPPFLAGS"
@@@ -3222,7 -3107,7 +3222,7 @@@ the
        SAVE_CPPFLAGS="$CPPFLAGS"
        LDFLAGS="$with_libowcapi_libs"
        CPPFLAGS="$with_libowcapi_cppflags"
 -      
 +
        AC_CHECK_LIB(owcapi, OW_get, [with_libowcapi="yes"], [with_libowcapi="no (libowcapi not found)"])
  
        LDFLAGS="$SAVE_LDFLAGS"
  fi
  # }}}
  
 +# --with-librdkafka {{{
 +AC_ARG_WITH(librdkafka, [AS_HELP_STRING([--with-librdkafka@<:@=PREFIX@:>@], [Path to librdkafka.])],
 +[
 +  if test "x$withval" != "xno" && test "x$withval" != "xyes"
 +  then
 +    with_librdkafka_cppflags="-I$withval/include"
 +    with_librdkafka_ldflags="-L$withval/lib"
 +    with_librdkafka_rpath="$withval/lib"
 +    with_librdkafka="yes"
 +  else
 +    with_librdkafka="$withval"
 +  fi
 +],
 +[
 +  with_librdkafka="yes"
 +])
 +SAVE_CPPFLAGS="$CPPFLAGS"
 +SAVE_LDFLAGS="$LDFLAGS"
 +
 +CPPFLAGS="$CPPFLAGS $with_librdkafka_cppflags"
 +LDFLAGS="$LDFLAGS $with_librdkafka_ldflags"
 +
 +if test "x$with_librdkafka" = "xyes"
 +then
 +      AC_CHECK_HEADERS(librdkafka/rdkafka.h, [with_librdkafka="yes"], [with_librdkafka="no (librdkafka/rdkafka.h not found)"])
 +fi
 +
 +if test "x$with_librdkafka" = "xyes"
 +then
 +      AC_CHECK_LIB(rdkafka, rd_kafka_new, [with_librdkafka="yes"], [with_librdkafka="no (Symbol 'rd_kafka_new' not found)"])
 +  AC_CHECK_LIB(rdkafka, rd_kafka_conf_set_log_cb, [with_librdkafka_log_cb="yes"], [with_librdkafka_log_cb="no"])
 +  AC_CHECK_LIB(rdkafka, rd_kafka_set_logger, [with_librdkafka_logger="yes"], [with_librdkafka_logger="no"])
 +fi
 +if test "x$with_librdkafka" = "xyes"
 +then
 +      BUILD_WITH_LIBRDKAFKA_CPPFLAGS="$with_librdkafka_cppflags"
 +      BUILD_WITH_LIBRDKAFKA_LDFLAGS="$with_librdkafka_ldflags"
 +      if test "x$with_librdkafka_rpath" != "x"
 +      then
 +              BUILD_WITH_LIBRDKAFKA_LIBS="-Wl,-rpath,$with_librdkafka_rpath -lrdkafka"
 +      else
 +              BUILD_WITH_LIBRDKAFKA_LIBS="-lrdkafka"
 +      fi
 +      AC_SUBST(BUILD_WITH_LIBRDKAFKA_CPPFLAGS)
 +      AC_SUBST(BUILD_WITH_LIBRDKAFKA_LDFLAGS)
 +      AC_SUBST(BUILD_WITH_LIBRDKAFKA_LIBS)
 +      AC_DEFINE(HAVE_LIBRDKAFKA, 1, [Define if librdkafka is present and usable.])
 +  if test "x$with_librdkafka_log_cb" = "xyes"
 +  then
 +        AC_DEFINE(HAVE_LIBRDKAFKA_LOG_CB, 1, [Define if librdkafka log facility is present and usable.])
 +  fi
 +  if test "x$with_librdkafka_logger" = "xyes"
 +  then
 +        AC_DEFINE(HAVE_LIBRDKAFKA_LOGGER, 1, [Define if librdkafka log facility is present and usable.])
 +  fi
 +fi
 +CPPFLAGS="$SAVE_CPPFLAGS"
 +LDFLAGS="$SAVE_LDFLAGS"
 +AM_CONDITIONAL(BUILD_WITH_LIBRDKAFKA, test "x$with_librdkafka" = "xyes")
 +
 +# }}}
 +
  # --with-librouteros {{{
  AC_ARG_WITH(librouteros, [AS_HELP_STRING([--with-librouteros@<:@=PREFIX@:>@], [Path to librouteros.])],
  [
    LDFLAGS="$SAVE_LDFLAGS"
  fi
  
 +if test "x$with_libstatgrab" = "xyes"
 +then
 +  SAVE_CFLAGS="$CFLAGS"
 +  SAVE_LIBS="$LIBS"
 +
 +  CFLAGS="$CFLAGS $with_libstatgrab_cflags"
 +  LDFLAGS="$LDFLAGS $with_libstatgrab_ldflags"
 +  LIBS="-lstatgrab $LIBS"
 +
 +  AC_CACHE_CHECK([if libstatgrab >= 0.90],
 +          [c_cv_have_libstatgrab_0_90],
 +          AC_LINK_IFELSE([AC_LANG_PROGRAM(
 +[[[
 +#include <stdio.h>
 +#include <statgrab.h>
 +]]],
 +[[[
 +      if (sg_init()) return 0;
 +]]]
 +    )],
 +    [c_cv_have_libstatgrab_0_90="no"],
 +    [c_cv_have_libstatgrab_0_90="yes"]
 +          )
 +  )
 +
 +  CFLAGS="$SAVE_CFLAGS"
 +  LDFLAGS="$SAVE_LDFLAGS"
 +  LIBS="$SAVE_LIBS"
 +fi
 +
  AM_CONDITIONAL(BUILD_WITH_LIBSTATGRAB, test "x$with_libstatgrab" = "xyes")
  if test "x$with_libstatgrab" = "xyes"
  then
    BUILD_WITH_LIBSTATGRAB_LDFLAGS="$with_libstatgrab_ldflags"
    AC_SUBST(BUILD_WITH_LIBSTATGRAB_CFLAGS)
    AC_SUBST(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
 +  if test "x$c_cv_have_libstatgrab_0_90" = "xyes"
 +  then
 +        AC_DEFINE(HAVE_LIBSTATGRAB_0_90, 1, [Define to 1 if libstatgrab version >= 0.90])
 +  fi
  fi
  # }}}
  
@@@ -4358,7 -4147,7 +4358,7 @@@ CPPFLAGS="$SAVE_CPPFLAGS
  LDFLAGS="$SAVE_LDFLAGS"
  
  if test "x$with_libtokyotyrant" = "xyes"
 -then 
 +then
    BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS="$with_libtokyotyrant_cppflags"
    BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS="$with_libtokyotyrant_ldflags"
    BUILD_WITH_LIBTOKYOTYRANT_LIBS="$with_libtokyotyrant_libs"
  AM_CONDITIONAL(BUILD_WITH_LIBTOKYOTYRANT, test "x$with_libtokyotyrant" = "xyes")
  # }}}
  
 +# --with-libudev {{{
 +with_libudev_cflags=""
 +with_libudev_ldflags=""
 +AC_ARG_WITH(libudev, [AS_HELP_STRING([--with-libudev@<:@=PREFIX@:>@], [Path to libudev.])],
 +[
 +      if test "x$withval" = "xno"
 +      then
 +              with_libudev="no"
 +      else
 +              with_libudev="yes"
 +              if test "x$withval" != "xyes"
 +              then
 +                      with_libudev_cflags="-I$withval/include"
 +                      with_libudev_ldflags="-L$withval/lib"
 +                      with_libudev="yes"
 +              fi
 +      fi
 +],
 +[
 +      if test "x$ac_system" = "xLinux"
 +      then
 +              with_libudev="yes"
 +      else
 +              with_libudev="no (Linux only library)"
 +      fi
 +])
 +if test "x$with_libudev" = "xyes"
 +then
 +      SAVE_CPPFLAGS="$CPPFLAGS"
 +      CPPFLAGS="$CPPFLAGS $with_libudev_cflags"
 +
 +      AC_CHECK_HEADERS(libudev.h, [], [with_libudev="no (libudev.h not found)"])
 +
 +      CPPFLAGS="$SAVE_CPPFLAGS"
 +fi
 +if test "x$with_libudev" = "xyes"
 +then
 +      SAVE_CPPFLAGS="$CPPFLAGS"
 +      SAVE_LDFLAGS="$LDFLAGS"
 +      CPPFLAGS="$CPPFLAGS $with_libudev_cflags"
 +      LDFLAGS="$LDFLAGS $with_libudev_ldflags"
 +
 +      AC_CHECK_LIB(udev, udev_new,
 +      [
 +              AC_DEFINE(HAVE_LIBUDEV, 1, [Define to 1 if you have the udev library (-ludev).])
 +      ],
 +      [with_libudev="no (libudev not found)"])
 +
 +      CPPFLAGS="$SAVE_CPPFLAGS"
 +      LDFLAGS="$SAVE_LDFLAGS"
 +fi
 +if test "x$with_libudev" = "xyes"
 +then
 +      BUILD_WITH_LIBUDEV_CFLAGS="$with_libudev_cflags"
 +      BUILD_WITH_LIBUDEV_LDFLAGS="$with_libudev_ldflags"
 +      AC_SUBST(BUILD_WITH_LIBUDEV_CFLAGS)
 +      AC_SUBST(BUILD_WITH_LIBUDEV_LDFLAGS)
 +fi
 +AM_CONDITIONAL(BUILD_WITH_LIBUDEV, test "x$with_libudev" = "xyes")
 +# }}}
 +
  # --with-libupsclient {{{
  with_libupsclient_config=""
  with_libupsclient_cflags=""
  if test "x$with_libvarnish" = "xyes"
  then
        SAVE_CPPFLAGS="$CPPFLAGS"
 -      CPPFLAGS="$CPPFLAGS $with_libvarnish_cflags"
 -      AC_CHECK_HEADERS(varnish/varnishapi.h, [], [with_libvarnish="no (varnish/varnishapi.h not found)"])
 -
 -      CPPFLAGS="$SAVE_CPPFLAGS"
 -fi
 -if test "x$with_libvarnish" = "xyes"
 -then
 -      SAVE_CPPFLAGS="$CPPFLAGS"
 -      #SAVE_LDFLAGS="$LDFLAGS"
  
        CPPFLAGS="$CPPFLAGS $with_libvarnish_cflags"
 -      #LDFLAGS="$LDFLAGS $with_libvarnish_libs"
  
 -    AC_CHECK_HEADERS(varnish/vsc.h,
 -        [AC_DEFINE([HAVE_VARNISH_V3], [1], [Varnish 3 API support])],
 -        [AC_DEFINE([HAVE_VARNISH_V2], [1], [Varnish 2 API support])])
 +      AC_CHECK_HEADERS(vapi/vsc.h,
 +              [AC_DEFINE([HAVE_VARNISH_V4], [1], [Varnish 4 API support])],
 +              [AC_CHECK_HEADERS(vsc.h,
 +                      [AC_DEFINE([HAVE_VARNISH_V3], [1], [Varnish 3 API support])],
 +                      [AC_CHECK_HEADERS(varnishapi.h,
 +                              [AC_DEFINE([HAVE_VARNISH_V2], [1], [Varnish 2 API support])],
 +                              [with_libvarnish="no (found none of the varnish header files)"])])])
  
        CPPFLAGS="$SAVE_CPPFLAGS"
 -      #LDFLAGS="$SAVE_LDFLAGS"
  fi
  if test "x$with_libvarnish" = "xyes"
  then
  fi
  # }}}
  
 +# --with-libatasmart {{{
 +with_libatasmart_cppflags=""
 +with_libatasmart_ldflags=""
 +AC_ARG_WITH(libatasmart, [AS_HELP_STRING([--with-libatasmart@<:@=PREFIX@:>@], [Path to libatasmart.])],
 +[
 +      if test "x$withval" != "xno" && test "x$withval" != "xyes"
 +      then
 +              with_libatasmart_cppflags="-I$withval/include"
 +              with_libatasmart_ldflags="-L$withval/lib"
 +              with_libatasmart="yes"
 +      else
 +              with_libatasmart="$withval"
 +      fi
 +],
 +[
 +      if test "x$ac_system" = "xLinux"
 +      then
 +              with_libatasmart="yes"
 +      else
 +              with_libatasmart="no (Linux only library)"
 +      fi
 +])
 +if test "x$with_libatasmart" = "xyes"
 +then
 +      SAVE_CPPFLAGS="$CPPFLAGS"
 +      CPPFLAGS="$CPPFLAGS $with_libatasmart_cppflags"
 +
 +      AC_CHECK_HEADERS(atasmart.h, [with_libatasmart="yes"], [with_libatasmart="no (atasmart.h not found)"])
 +
 +      CPPFLAGS="$SAVE_CPPFLAGS"
 +fi
 +if test "x$with_libatasmart" = "xyes"
 +then
 +      SAVE_CPPFLAGS="$CPPFLAGS"
 +      SAVE_LDFLAGS="$LDFLAGS"
 +      CPPFLAGS="$CPPFLAGS $with_libatasmart_cppflags"
 +      LDFLAGS="$LDFLAGS $with_libatasmart_ldflags"
 +
 +      AC_CHECK_LIB(atasmart, sk_disk_open, [with_libatasmart="yes"], [with_libatasmart="no (Symbol 'sk_disk_open' not found)"])
 +
 +      CPPFLAGS="$SAVE_CPPFLAGS"
 +      LDFLAGS="$SAVE_LDFLAGS"
 +fi
 +if test "x$with_libatasmart" = "xyes"
 +then
 +      BUILD_WITH_LIBATASMART_CPPFLAGS="$with_libatasmart_cppflags"
 +      BUILD_WITH_LIBATASMART_LDFLAGS="$with_libatasmart_ldflags"
 +      BUILD_WITH_LIBATASMART_LIBS="-latasmart"
 +      AC_SUBST(BUILD_WITH_LIBATASMART_CPPFLAGS)
 +      AC_SUBST(BUILD_WITH_LIBATASMART_LDFLAGS)
 +      AC_SUBST(BUILD_WITH_LIBATASMART_LIBS)
 +      AC_DEFINE(HAVE_LIBATASMART, 1, [Define if libatasmart is present and usable.])
 +fi
 +AM_CONDITIONAL(BUILD_WITH_LIBATASMART, test "x$with_libatasmart" = "xyes")
 +# }}}
 +
  PKG_CHECK_MODULES([LIBNOTIFY], [libnotify],
                [with_libnotify="yes"],
                [if test "x$LIBNOTIFY_PKG_ERRORS" = "x"; then
@@@ -5215,10 -4894,8 +5215,10 @@@ dependency_warning="no
  dependency_error="no"
  
  plugin_ascent="no"
 +plugin_barometer="no"
  plugin_battery="no"
  plugin_bind="no"
 +plugin_ceph="no"
  plugin_cgroups="no"
  plugin_conntrack="no"
  plugin_contextswitch="no"
@@@ -5228,17 -4905,15 +5228,17 @@@ plugin_curl_json="no
  plugin_curl_xml="no"
  plugin_df="no"
  plugin_disk="no"
 +plugin_drbd="no"
  plugin_entropy="no"
  plugin_ethstat="no"
 +plugin_fhcount="no"
  plugin_fscache="no"
  plugin_interface="no"
  plugin_ipmi="no"
  plugin_ipvs="no"
  plugin_irq="no"
 -plugin_libvirt="no"
  plugin_load="no"
 +plugin_log_logstash="no"
  plugin_memory="no"
  plugin_multimeter="no"
  plugin_nfs="no"
@@@ -5252,15 -4927,12 +5252,15 @@@ plugin_tape="no
  plugin_tcpconns="no"
  plugin_ted="no"
  plugin_thermal="no"
 -plugin_users="no"
 +plugin_turbostat="no"
  plugin_uptime="no"
 +plugin_users="no"
 +plugin_virt="no"
  plugin_vmem="no"
  plugin_vserver="no"
  plugin_wireless="no"
  plugin_zfs_arc="no"
 +plugin_zookeeper="no"
  
  # Linux
  if test "x$ac_system" = "xLinux"
        plugin_cpu="yes"
        plugin_cpufreq="yes"
        plugin_disk="yes"
 +      plugin_drbd="yes"
        plugin_entropy="yes"
 +      plugin_fhcount="yes"
        plugin_fscache="yes"
        plugin_interface="yes"
 +      plugin_ipc="yes"
        plugin_irq="yes"
        plugin_load="yes"
        plugin_lvm="yes"
        plugin_vmem="yes"
        plugin_vserver="yes"
        plugin_wireless="yes"
 +      plugin_zfs_arc="yes"
  
        if test "x$have_linux_ip_vs_h" = "xyes" || test "x$have_net_ip_vs_h" = "xyes" || test "x$have_ip_vs_h" = "xyes"
        then
                plugin_ipvs="yes"
        fi
 +      if test "x$c_cv_have_usable_asm_msrindex_h" = "xyes" && test "x$have_cpuid_h" = "xyes"
 +      then
 +              plugin_turbostat="yes"
 +      fi
  fi
  
  if test "x$ac_system" = "xOpenBSD"
  
  if test "x$ac_system" = "xAIX"
  then
 -        plugin_tcpconns="yes"
 +      plugin_tcpconns="yes"
 +      plugin_ipc="yes"
  fi
  
  # FreeBSD
  
  if test "x$ac_system" = "xFreeBSD"
  then
 -        plugin_zfs_arc="yes"
 +      plugin_zfs_arc="yes"
  fi
  
  
        plugin_tape="yes"
  fi
  
 +# libi2c-dev
 +with_libi2c="no"
 +if test "x$ac_system" = "xLinux"
 +then
 +AC_CHECK_DECL(i2c_smbus_read_i2c_block_data,
 +      [with_libi2c="yes"],
 +      [with_libi2c="no (symbol i2c_smbus_read_i2c_block_data not found - have you installed libi2c-dev ?)"],
 +      [[#include <stdlib.h>
 +      #include <linux/i2c-dev.h>]])
 +fi
 +
 +if test "x$with_libi2c" = "xyes"
 +then
 +      plugin_barometer="yes"
 +fi
 +
 +
  # libstatgrab
  if test "x$with_libstatgrab" = "xyes"
  then
        plugin_curl_xml="yes"
  fi
  
 +if test "x$with_libyajl" = "xyes"
 +then
 +      plugin_ceph="yes"
 +fi
 +
  if test "x$have_processor_info" = "xyes"
  then
        plugin_cpu="yes"
        plugin_interface="yes"
  fi
  
 -if test "x$with_libxml2" = "xyes" && test "x$with_libvirt" = "xyes"
 +if test "x$have_getloadavg" = "xyes"
  then
 -      plugin_libvirt="yes"
 +      plugin_load="yes"
  fi
  
 -if test "x$have_getloadavg" = "xyes"
 +if test "x$with_libyajl" = "xyes"
  then
 -      plugin_load="yes"
 +      plugin_log_logstash="yes"
  fi
  
  if test "x$c_cv_have_libperl$c_cv_have_perl_ithreads" = "xyesyes"
  
  if test "x$have_termios_h" = "xyes"
  then
 -      plugin_multimeter="yes"
 +      if test "x$ac_system" != "xAIX"
 +      then
 +              plugin_multimeter="yes"
 +      fi
        plugin_ted="yes"
  fi
  
        plugin_processes="yes"
  fi
  
 +if test "x$with_kvm_getprocs" = "xyes" && test "x$have_struct_kinfo_proc_openbsd" = "xyes"
 +then
 +      plugin_processes="yes"
 +fi
 +
  if test "x$with_kvm_getswapinfo" = "xyes"
  then
        plugin_swap="yes"
        plugin_users="yes"
  fi
  
 +if test "x$with_libxml2" = "xyes" && test "x$with_libvirt" = "xyes"
 +then
 +      plugin_virt="yes"
 +fi
 +
 +
  m4_divert_once([HELP_ENABLE], [
  collectd plugins:])
  
@@@ -5601,10 -5228,8 +5601,10 @@@ AC_PLUGIN([apcups],      [yes]
  AC_PLUGIN([apple_sensors], [$with_libiokit],   [Apple's hardware sensors])
  AC_PLUGIN([aquaero],     [$with_libaquaero5],  [Aquaero's hardware sensors])
  AC_PLUGIN([ascent],      [$plugin_ascent],     [AscentEmu player statistics])
 +AC_PLUGIN([barometer],   [$plugin_barometer],  [Barometer sensor on I2C])
  AC_PLUGIN([battery],     [$plugin_battery],    [Battery statistics])
  AC_PLUGIN([bind],        [$plugin_bind],       [ISC Bind nameserver statistics])
 +AC_PLUGIN([ceph],        [$plugin_ceph],       [Ceph daemon statistics])
  AC_PLUGIN([conntrack],   [$plugin_conntrack],  [nf_conntrack statistics])
  AC_PLUGIN([contextswitch], [$plugin_contextswitch], [context switch statistics])
  AC_PLUGIN([cpufreq],     [$plugin_cpufreq],    [CPU frequency statistics])
@@@ -5617,27 -5242,24 +5617,27 @@@ AC_PLUGIN([cgroups],     [$plugin_cgrou
  AC_PLUGIN([dbi],         [$with_libdbi],       [General database statistics])
  AC_PLUGIN([df],          [$plugin_df],         [Filesystem usage statistics])
  AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
 +AC_PLUGIN([drbd],        [$plugin_drbd],       [DRBD statistics])
  AC_PLUGIN([dns],         [$with_libpcap],      [DNS traffic analysis])
  AC_PLUGIN([email],       [yes],                [EMail statistics])
  AC_PLUGIN([entropy],     [$plugin_entropy],    [Entropy statistics])
  AC_PLUGIN([ethstat],     [$plugin_ethstat],    [Stats from NIC driver])
  AC_PLUGIN([exec],        [yes],                [Execution of external programs])
 +AC_PLUGIN([fhcount],     [$plugin_fhcount],    [File handles statistics])
  AC_PLUGIN([filecount],   [yes],                [Count files in directories])
  AC_PLUGIN([fscache],     [$plugin_fscache],    [fscache statistics])
  AC_PLUGIN([gmond],       [$with_libganglia],   [Ganglia plugin])
  AC_PLUGIN([hddtemp],     [yes],                [Query hddtempd])
  AC_PLUGIN([interface],   [$plugin_interface],  [Interface traffic statistics])
 +AC_PLUGIN([ipc],         [$plugin_ipc],        [IPC statistics])
  AC_PLUGIN([ipmi],        [$plugin_ipmi],       [IPMI sensor statistics])
  AC_PLUGIN([iptables],    [$with_libiptc],      [IPTables rule counters])
  AC_PLUGIN([ipvs],        [$plugin_ipvs],       [IPVS connection statistics])
  AC_PLUGIN([irq],         [$plugin_irq],        [IRQ statistics])
  AC_PLUGIN([java],        [$with_java],         [Embed the Java Virtual Machine])
 -AC_PLUGIN([libvirt],     [$plugin_libvirt],    [Virtual machine statistics])
  AC_PLUGIN([load],        [$plugin_load],       [System load])
  AC_PLUGIN([logfile],     [yes],                [File logging plugin])
 +AC_PLUGIN([log_logstash], [$plugin_log_logstash], [Logstash json_event compatible logging])
  AC_PLUGIN([lpar],        [$with_perfstat],     [AIX logical partitions statistics])
  AC_PLUGIN([lvm],         [$with_liblvm2app],   [LVM statistics])
  AC_PLUGIN([madwifi],     [$have_linux_wireless_h], [Madwifi wireless statistics])
@@@ -5667,7 -5289,6 +5667,7 @@@ AC_PLUGIN([numa],        [$plugin_numa]
  AC_PLUGIN([nut],         [$with_libupsclient], [Network UPS tools statistics])
  AC_PLUGIN([olsrd],       [yes],                [olsrd statistics])
  AC_PLUGIN([onewire],     [$with_libowcapi],    [OneWire sensor statistics])
 +AC_PLUGIN([openldap],    [$with_libldap],      [OpenLDAP statistics])
  AC_PLUGIN([openvpn],     [yes],                [OpenVPN client statistics])
  AC_PLUGIN([oracle],      [$with_oracle],       [Oracle plugin])
  AC_PLUGIN([perl],        [$plugin_perl],       [Embed a Perl interpreter])
@@@ -5680,14 -5301,13 +5680,14 @@@ AC_PLUGIN([powerdns],    [yes]
  AC_PLUGIN([processes],   [$plugin_processes],  [Process statistics])
  AC_PLUGIN([protocols],   [$plugin_protocols],  [Protocol (IP, TCP, ...) statistics])
  AC_PLUGIN([python],      [$with_python],       [Embed a Python interpreter])
 -AC_PLUGIN([redis],       [$with_libcredis],    [Redis plugin])
 +AC_PLUGIN([redis],       [$with_libhiredis],    [Redis plugin])
  AC_PLUGIN([routeros],    [$with_librouteros],  [RouterOS plugin])
  AC_PLUGIN([rrdcached],   [$librrd_rrdc_update], [RRDTool output plugin])
  AC_PLUGIN([rrdtool],     [$with_librrd],       [RRDTool output plugin])
  AC_PLUGIN([sensors],     [$with_libsensors],   [lm_sensors statistics])
  AC_PLUGIN([serial],      [$plugin_serial],     [serial port traffic])
  AC_PLUGIN([sigrok],      [$with_libsigrok],    [sigrok acquisition sources])
 +AC_PLUGIN([smart],       [$with_libatasmart],  [SMART statistics])
  AC_PLUGIN([snmp],        [$with_libnetsnmp],   [SNMP querying plugin])
  AC_PLUGIN([statsd],      [yes],                [StatsD plugin])
  AC_PLUGIN([swap],        [$plugin_swap],       [Swap usage statistics])
@@@ -5707,34 -5327,26 +5707,34 @@@ AC_PLUGIN([ted],         [$plugin_ted]
  AC_PLUGIN([thermal],     [$plugin_thermal],    [Linux ACPI thermal zone statistics])
  AC_PLUGIN([threshold],   [yes],                [Threshold checking plugin])
  AC_PLUGIN([tokyotyrant], [$with_libtokyotyrant],  [TokyoTyrant database statistics])
 +AC_PLUGIN([turbostat],   [$plugin_turbostat],  [Advanced statistic on Intel cpu states])
  AC_PLUGIN([unixsock],    [yes],                [Unixsock communication plugin])
  AC_PLUGIN([uptime],      [$plugin_uptime],     [Uptime statistics])
  AC_PLUGIN([users],       [$plugin_users],      [User statistics])
  AC_PLUGIN([uuid],        [yes],                [UUID as hostname plugin])
  AC_PLUGIN([varnish],     [$with_libvarnish],   [Varnish cache statistics])
 +AC_PLUGIN([virt],        [$plugin_virt],       [Virtual machine statistics])
  AC_PLUGIN([vmem],        [$plugin_vmem],       [Virtual memory statistics])
  AC_PLUGIN([vserver],     [$plugin_vserver],    [Linux VServer statistics])
  AC_PLUGIN([wireless],    [$plugin_wireless],   [Wireless statistics])
  AC_PLUGIN([write_graphite], [yes],             [Graphite / Carbon output plugin])
  AC_PLUGIN([write_http],  [$with_libcurl],      [HTTP output plugin])
 +AC_PLUGIN([write_kafka],  [$with_librdkafka],  [Kafka output plugin])
 +AC_PLUGIN([write_log], [yes],                  [Log output plugin])
  AC_PLUGIN([write_mongodb], [$with_libmongoc],  [MongoDB output plugin])
 -AC_PLUGIN([write_redis], [$with_libcredis],    [Redis output plugin])
 +AC_PLUGIN([write_redis], [$with_libhiredis],    [Redis output plugin])
  AC_PLUGIN([write_riemann], [$have_protoc_c],   [Riemann output plugin])
 +AC_PLUGIN([write_sensu], [yes],                [Sensu output plugin])
 +AC_PLUGIN([write_tsdb],  [yes],                [TSDB output plugin])
  AC_PLUGIN([xmms],        [$with_libxmms],      [XMMS statistics])
  AC_PLUGIN([zfs_arc],     [$plugin_zfs_arc],    [ZFS ARC statistics])
 +AC_PLUGIN([zookeeper],   [yes],              [Zookeeper statistics])
  
  dnl Default configuration file
  # Load either syslog or logfile
  LOAD_PLUGIN_SYSLOG=""
  LOAD_PLUGIN_LOGFILE=""
 +LOAD_PLUGIN_LOG_LOGSTASH=""
  
  AC_MSG_CHECKING([which default log plugin to load])
  default_log_plugin="none"
  else
        LOAD_PLUGIN_LOGFILE="##"
  fi
 +
 +if test "x$enable_log_logstash" = "xyes"
 +then
 +  LOAD_PLUGIN_LOG_LOGSTASH="#"
 +else
 +  LOAD_PLUGIN_LOG_LOGSTASH="##"
 +fi
 +
 +
  AC_MSG_RESULT([$default_log_plugin])
  
  AC_SUBST(LOAD_PLUGIN_SYSLOG)
  AC_SUBST(LOAD_PLUGIN_LOGFILE)
 +AC_SUBST(LOAD_PLUGIN_LOG_LOGSTASH)
  
  DEFAULT_LOG_LEVEL="info"
  if test "x$enable_debug" = "xyes"
@@@ -5879,7 -5481,7 +5879,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" \
@@@ -5912,21 -5514,18 +5912,21 @@@ Configuration
    Libraries:
      intel mic . . . . . . $with_mic
      libaquaero5 . . . . . $with_libaquaero5
 -    libcredis . . . . . . $with_libcredis
 +    libatasmart . . . . . $with_libatasmart
      libcurl . . . . . . . $with_libcurl
      libdbi  . . . . . . . $with_libdbi
      libesmtp  . . . . . . $with_libesmtp
      libganglia  . . . . . $with_libganglia
      libgcrypt . . . . . . $with_libgcrypt
      libhal  . . . . . . . $with_libhal
 +    libhiredis  . . . . . $with_libhiredis
 +    libi2c-dev  . . . . . $with_libi2c
      libiokit  . . . . . . $with_libiokit
      libiptc . . . . . . . $with_libiptc
      libjvm  . . . . . . . $with_java
      libkstat  . . . . . . $with_kstat
      libkvm  . . . . . . . $with_libkvm
 +    libldap . . . . . . . $with_libldap
      liblvm2app  . . . . . $with_liblvm2app
      libmemcached  . . . . $with_libmemcached
      libmnl  . . . . . . . $with_libmnl
      libpq . . . . . . . . $with_libpq
      libpthread  . . . . . $with_libpthread
      librabbitmq . . . . . $with_librabbitmq
 +    librdkafka  . . . . . $with_librdkafka
      librouteros . . . . . $with_librouteros
      librrd  . . . . . . . $with_librrd
      libsensors  . . . . . $with_libsensors
      libsigrok   . . . . . $with_libsigrok
      libstatgrab . . . . . $with_libstatgrab
      libtokyotyrant  . . . $with_libtokyotyrant
 +    libudev . . . . . . . $with_libudev
      libupsclient  . . . . $with_libupsclient
      libvarnish  . . . . . $with_libvarnish
      libvirt . . . . . . . $with_libvirt
      amqp    . . . . . . . $enable_amqp
      apache  . . . . . . . $enable_apache
      apcups  . . . . . . . $enable_apcups
 -    aquaero . . . . . . . $enable_aquaero
      apple_sensors . . . . $enable_apple_sensors
 +    aquaero . . . . . . . $enable_aquaero
      ascent  . . . . . . . $enable_ascent
 +    barometer . . . . . . $enable_barometer
      battery . . . . . . . $enable_battery
      bind  . . . . . . . . $enable_bind
 +    ceph  . . . . . . . . $enable_ceph
 +    cgroups . . . . . . . $enable_cgroups
      conntrack . . . . . . $enable_conntrack
      contextswitch . . . . $enable_contextswitch
 -    cgroups . . . . . . . $enable_cgroups
      cpu . . . . . . . . . $enable_cpu
      cpufreq . . . . . . . $enable_cpufreq
      csv . . . . . . . . . $enable_csv
      df  . . . . . . . . . $enable_df
      disk  . . . . . . . . $enable_disk
      dns . . . . . . . . . $enable_dns
 +    drbd  . . . . . . . . $enable_drbd
      email . . . . . . . . $enable_email
      entropy . . . . . . . $enable_entropy
      ethstat . . . . . . . $enable_ethstat
      exec  . . . . . . . . $enable_exec
 +    fhcount . . . . . . . $enable_fhcount
      filecount . . . . . . $enable_filecount
      fscache . . . . . . . $enable_fscache
      gmond . . . . . . . . $enable_gmond
      hddtemp . . . . . . . $enable_hddtemp
      interface . . . . . . $enable_interface
 +    ipc . . . . . . . . . $enable_ipc
      ipmi  . . . . . . . . $enable_ipmi
      iptables  . . . . . . $enable_iptables
      ipvs  . . . . . . . . $enable_ipvs
      irq . . . . . . . . . $enable_irq
      java  . . . . . . . . $enable_java
 -    libvirt . . . . . . . $enable_libvirt
      load  . . . . . . . . $enable_load
      logfile . . . . . . . $enable_logfile
 +    log_logstash  . . . . $enable_log_logstash
      lpar  . . . . . . . . $enable_lpar
      lvm . . . . . . . . . $enable_lvm
      madwifi . . . . . . . $enable_madwifi
      nut . . . . . . . . . $enable_nut
      olsrd . . . . . . . . $enable_olsrd
      onewire . . . . . . . $enable_onewire
 +    openldap  . . . . . . $enable_openldap
      openvpn . . . . . . . $enable_openvpn
      oracle  . . . . . . . $enable_oracle
      perl  . . . . . . . . $enable_perl
      sensors . . . . . . . $enable_sensors
      serial  . . . . . . . $enable_serial
      sigrok  . . . . . . . $enable_sigrok
 +    smart . . . . . . . . $enable_smart
      snmp  . . . . . . . . $enable_snmp
      statsd  . . . . . . . $enable_statsd
      swap  . . . . . . . . $enable_swap
      syslog  . . . . . . . $enable_syslog
      table . . . . . . . . $enable_table
 -    tail  . . . . . . . . $enable_tail
      tail_csv  . . . . . . $enable_tail_csv
 +    tail  . . . . . . . . $enable_tail
      tape  . . . . . . . . $enable_tape
      target_notification . $enable_target_notification
      target_replace  . . . $enable_target_replace
      thermal . . . . . . . $enable_thermal
      threshold . . . . . . $enable_threshold
      tokyotyrant . . . . . $enable_tokyotyrant
 +    turbostat . . . . . . $enable_turbostat
      unixsock  . . . . . . $enable_unixsock
      uptime  . . . . . . . $enable_uptime
      users . . . . . . . . $enable_users
      uuid  . . . . . . . . $enable_uuid
      varnish . . . . . . . $enable_varnish
 +    virt  . . . . . . . . $enable_virt
      vmem  . . . . . . . . $enable_vmem
      vserver . . . . . . . $enable_vserver
      wireless  . . . . . . $enable_wireless
      write_graphite  . . . $enable_write_graphite
      write_http  . . . . . $enable_write_http
 +    write_kafka . . . . . $enable_write_kafka
 +    write_log . . . . . . $enable_write_log
      write_mongodb . . . . $enable_write_mongodb
      write_redis . . . . . $enable_write_redis
      write_riemann . . . . $enable_write_riemann
 +    write_sensu . . . . . $enable_write_sensu
 +    write_tsdb  . . . . . $enable_write_tsdb
      xmms  . . . . . . . . $enable_xmms
      zfs_arc . . . . . . . $enable_zfs_arc
 +    zookeeper . . . . . . $enable_zookeeper
  
  EOF
  
diff --combined src/bind.c
@@@ -109,7 -109,6 +109,7 @@@ static int global_server_stats     = 1
  static int global_zone_maint_stats = 1;
  static int global_resolver_stats   = 0;
  static int global_memory_stats     = 1;
 +static int timeout                 = -1;
  
  static cb_view_t *views = NULL;
  static size_t     views_num = 0;
@@@ -370,11 -369,9 +370,11 @@@ static int bind_xml_read_derive (xmlDo
    {
      ERROR ("bind plugin: Parsing string \"%s\" to derive value failed.",
          str_ptr);
 +    xmlFree(str_ptr);
      return (-1);
    }
  
 +  xmlFree(str_ptr);
    *ret_value = value.derive;
    return (0);
  } /* }}} int bind_xml_read_derive */
@@@ -630,84 -627,6 +630,84 @@@ static int bind_parse_generic_value_lis
    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)
    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);
 +    char *n = (char *) xmlGetProp (node, BAD_CAST "name");
 +    char *c = (char *) xmlGetProp (node, BAD_CAST "rdataclass");
 +    if (n && c)
 +    {
 +      zone_name = (char *) xmlMalloc(strlen(n) + strlen(c) + 2);
 +      snprintf(zone_name, strlen(n) + strlen(c) + 2, "%s/%s", n, c);
 +    }
 +    xmlFree(n);
 +    xmlFree(c);
    }
 -
 -  for (i = 0; path_obj->nodesetval && (i < path_obj->nodesetval->nodeNr); i++)
 +  else
    {
 -    zone_name = (char *) xmlNodeListGetString (doc,
 -        path_obj->nodesetval->nodeTab[i]->xmlChildrenNode, 1);
 -    if (zone_name != NULL)
 -      break;
 +    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++)
 +    {
 +      zone_name = (char *) xmlNodeListGetString (doc,
 +          path_obj->nodesetval->nodeTab[i]->xmlChildrenNode, 1);
 +      if (zone_name != NULL)
 +        break;
 +    }
 +    xmlXPathFreeObject (path_obj);
    }
  
    if (zone_name == NULL)
    {
      ERROR ("bind plugin: Could not determine zone name.");
 -    xmlXPathFreeObject (path_obj);
      return (-1);
    }
  
    xmlFree (zone_name);
    zone_name = NULL;
  
 -  if (j >= views_num)
 -  {
 -    xmlXPathFreeObject (path_obj);
 +  if (j >= view->zones_num)
      return (0);
 -  }
  
    zone_name = view->zones[j];
  
      ssnprintf (plugin_instance, sizeof (plugin_instance), "%s-zone-%s",
          view->name, zone_name);
  
 -    bind_parse_generic_value_list (/* xpath = */ "counters",
 +    if (version == 3)
 +    {
 +      list_info_ptr_t list_info =
 +      {
 +        plugin_instance,
 +        /* type = */ "dns_qtype"
 +      };
 +      bind_parse_generic_name_attr_value_list (/* xpath = */ "counters[@type='rcode']",
          /* callback = */ bind_xml_table_callback,
          /* user_data = */ &table_ptr,
          doc, path_ctx, current_time, DS_TYPE_COUNTER);
 +      bind_parse_generic_name_attr_value_list (/* xpath = */ "counters[@type='qtype']",
 +        /* callback = */ bind_xml_list_callback,
 +        /* user_data = */ &list_info,
 +        doc, path_ctx, current_time, DS_TYPE_COUNTER);
 +    }
 +    else
 +    {
 +      bind_parse_generic_value_list (/* xpath = */ "counters",
 +          /* callback = */ bind_xml_table_callback,
 +          /* user_data = */ &table_ptr,
 +          doc, path_ctx, current_time, DS_TYPE_COUNTER);
 +    }
    } /* }}} */
  
 -  xmlXPathFreeObject (path_obj);
 -
    return (0);
  } /* }}} int bind_xml_stats_handle_zone */
  
@@@ -855,68 -745,45 +855,68 @@@ static int bind_xml_stats_search_zones 
  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);
  
      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) /* {{{ */
  
      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 */
@@@ -1047,145 -896,28 +1047,145 @@@ static int bind_xml_stats_search_views 
    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>
          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>
          /* 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,
    if (global_server_stats)
    {
      translation_table_ptr_t table_ptr =
 -    { 
 +    {
        nsstats_translation_table,
        nsstats_translation_table_length,
        /* plugin_instance = */ "global-server_stats"
      }
    }
  
 -  /* 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
    if (global_zone_maint_stats)
    {
      translation_table_ptr_t table_ptr =
 -    { 
 +    {
        zonestats_translation_table,
        zonestats_translation_table_length,
        /* plugin_instance = */ "global-zone_maint_stats"
      }
    }
  
 -  /* 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,
    if (global_resolver_stats != 0)
    {
      translation_table_ptr_t table_ptr =
 -    { 
 +    {
        resstats_translation_table,
        resstats_translation_table_length,
        /* plugin_instance = */ "global-resolver_stats"
            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>
@@@ -1460,64 -1163,6 +1460,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)
    {
@@@ -1709,6 -1354,7 +1709,7 @@@ static int bind_config (oconfig_item_t 
          return (-1);
        }
  
+       sfree (url);
        url = strdup (child->values[0].value.string);
      } else if (strcasecmp ("OpCodes", child->key) == 0)
        bind_config_set_bool ("OpCodes", &global_opcodes, child);
        bind_config_add_view (child);
      else if (strcasecmp ("ParseTime", child->key) == 0)
        cf_util_get_boolean (child, &config_parse_time);
 +    else if (strcasecmp ("Timeout", child->key) == 0)
 +      cf_util_get_int (child, &timeout);
      else
      {
        WARNING ("bind plugin: Unknown configuration option "
@@@ -1752,16 -1396,11 +1753,16 @@@ static int bind_init (void) /* {{{ *
  
    curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, bind_curl_callback);
 -  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
 +  curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
    curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, bind_curl_error);
    curl_easy_setopt (curl, CURLOPT_URL, (url != NULL) ? url : BIND_DEFAULT_URL);
    curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 50L);
 +#ifdef HAVE_CURLOPT_TIMEOUT_MS
 +  curl_easy_setopt (curl, CURLOPT_TIMEOUT_MS, (timeout >= 0) ?
 +      (long) timeout : CDTIME_T_TO_MS(plugin_get_interval()));
 +#endif
 +
  
    return (0);
  } /* }}} int bind_init */
diff --combined src/curl_xml.c
@@@ -76,12 -76,10 +76,12 @@@ struct cx_s /* {{{ *
    char *user;
    char *pass;
    char *credentials;
 +  _Bool digest;
    _Bool verify_peer;
    _Bool verify_host;
    char *cacert;
    char *post_body;
 +  int timeout;
    struct curl_slist *headers;
  
    cx_namespace_t *namespaces;
@@@ -698,7 -696,7 +698,7 @@@ static int cx_config_add_values (const 
      sstrncpy (xpath->values[i].path, ci->values[i].value.string, sizeof (xpath->values[i].path));
    }
  
-   return (0); 
+   return (0);
  } /* }}} cx_config_add_values */
  
  static int cx_config_add_xpath (cx_t *db, /* {{{ */
    {
      ERROR ("curl_xml plugin: invalid xpath. "
             "xpath value can't be an empty string");
+     sfree (xpath);
      return (-1);
    }
  
        if (db->list == NULL)
        {
          ERROR ("curl_xml plugin: list creation failed.");
+         sfree (xpath->path);
+         sfree (xpath);
          return (-1);
        }
      }
      if (name == NULL)
      {
          ERROR ("curl_xml plugin: strdup failed.");
+         sfree (xpath->path);
+         sfree (xpath);
          return (-1);
      }
  
      if (le == NULL)
      {
        ERROR ("curl_xml plugin: llentry_create failed.");
+       sfree (xpath->path);
+       sfree (xpath);
        return (-1);
      }
  
@@@ -848,19 -853,13 +855,19 @@@ static int cx_init_curl (cx_t *db) /* {
    curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cx_curl_callback);
    curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
 -  curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
 -                    PACKAGE_NAME"/"PACKAGE_VERSION);
 +  curl_easy_setopt (db->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
    curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
    curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
 +  curl_easy_setopt (db->curl, CURLOPT_FOLLOWLOCATION, 1L);
 +  curl_easy_setopt (db->curl, CURLOPT_MAXREDIRS, 50L);
  
    if (db->user != NULL)
    {
 +#ifdef HAVE_CURLOPT_USERNAME
 +    curl_easy_setopt (db->curl, CURLOPT_USERNAME, db->user);
 +    curl_easy_setopt (db->curl, CURLOPT_PASSWORD,
 +        (db->pass == NULL) ? "" : db->pass);
 +#else
      size_t credentials_size;
  
      credentials_size = strlen (db->user) + 2;
      ssnprintf (db->credentials, credentials_size, "%s:%s",
                 db->user, (db->pass == NULL) ? "" : db->pass);
      curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
 +#endif
 +
 +    if (db->digest)
 +      curl_easy_setopt (db->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
    }
  
    curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer ? 1L : 0L);
    if (db->post_body != NULL)
      curl_easy_setopt (db->curl, CURLOPT_POSTFIELDS, db->post_body);
  
 +#ifdef HAVE_CURLOPT_TIMEOUT_MS
 +  if (db->timeout >= 0)
 +    curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS, (long) db->timeout);
 +  else
 +    curl_easy_setopt (db->curl, CURLOPT_TIMEOUT_MS,
 +       CDTIME_T_TO_MS(plugin_get_interval()));
 +#endif
 +
    return (0);
  } /* }}} int cx_init_curl */
  
@@@ -926,8 -913,6 +933,8 @@@ static int cx_config_add_url (oconfig_i
    }
    memset (db, 0, sizeof (*db));
  
 +  db->timeout = -1;
 +
    if (strcasecmp ("URL", ci->key) == 0)
    {
      status = cf_util_get_string (ci, &db->url);
        status = cf_util_get_string (child, &db->user);
      else if (strcasecmp ("Password", child->key) == 0)
        status = cf_util_get_string (child, &db->pass);
 +    else if (strcasecmp ("Digest", child->key) == 0)
 +      status = cf_util_get_boolean (child, &db->digest);
      else if (strcasecmp ("VerifyPeer", child->key) == 0)
        status = cf_util_get_boolean (child, &db->verify_peer);
      else if (strcasecmp ("VerifyHost", child->key) == 0)
        status = cf_util_get_string (child, &db->post_body);
      else if (strcasecmp ("Namespace", child->key) == 0)
        status = cx_config_add_namespace (db, child);
 +    else if (strcasecmp ("Timeout", child->key) == 0)
 +      status = cf_util_get_int (child, &db->timeout);
      else
      {
        WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
diff --combined src/daemon/utils_cache.c
index 7621395,0000000..00de85a
mode 100644,000000..100644
--- /dev/null
@@@ -1,997 -1,0 +1,998 @@@
-       
 +/**
 + * 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);
 +      sfree (key_copy);
++      cache_free (ce);
 +      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)
 +  {
 +    /* realloc() may have been called for these. */
 +    sfree (keys);
 +    sfree (keys_time);
 +    sfree (keys_interval);
 +    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]);
 +      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);
 +    sfree (times);
 +
 +    return (-1);
 +  }
 +
 +  *ret_names = names;
 +  if (ret_times != NULL)
 +    *ret_times = times;
 +  else
 +    sfree (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 : */
@@@ -84,7 -84,7 +84,7 @@@
      _b[sizeof (_b) - 1] = 0; \
      SSTRCAT ((d), _b); \
    } while (0)
 -    
 +
  
  #define LCC_SET_ERRSTR(c, ...) do { \
    snprintf ((c)->errbuf, sizeof ((c)->errbuf), __VA_ARGS__); \
@@@ -526,9 -526,11 +526,11 @@@ static int lcc_open_netsocket (lcc_conn
    if (status != 0)
    {
      lcc_set_errno (c, status);
+     freeaddrinfo (ai_res);
      return (-1);
    }
  
+   freeaddrinfo (ai_res);
    return (0);
  } /* }}} int lcc_open_netsocket */
  
diff --combined src/statsd.c
@@@ -1,24 -1,19 +1,24 @@@
  /**
   * collectd - src/statsd.c
 - *
   * Copyright (C) 2013       Florian octo Forster
   *
 - * Permission to use, copy, modify, and distribute this software for any
 - * purpose with or without fee is hereby granted, provided that the above
 - * copyright notice and this permission notice appear in all copies.
 + * 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" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 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>
@@@ -562,6 -557,7 +562,7 @@@ static int statsd_network_init (struct 
      if (tmp == NULL)
      {
        ERROR ("statsd plugin: realloc failed.");
+       close (fd);
        continue;
      }
      fds = tmp;
diff --combined src/unixsock.c
@@@ -2,26 -2,21 +2,26 @@@
   * collectd - src/unixsock.c
   * Copyright (C) 2007,2008  Florian octo Forster
   *
 - * This program is free software; you can redistribute it and/or modify it
 - * under the terms of the GNU General Public License as published by the
 - * Free Software Foundation; only version 2 of the License is applicable.
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
   *
 - * This program is distributed in the hope that it will be useful, but
 - * WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * General Public License for more details.
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
   *
 - * You should have received a copy of the GNU General Public License along
 - * with this program; if not, write to the Free Software Foundation, Inc.,
 - * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
   *
 - * Author:
 - *   Florian octo Forster <octo at verplant.org>
 + * Authors:
 + *   Florian octo Forster <octo at collectd.org>
   **/
  
  #include "collectd.h"
@@@ -31,7 -26,6 +31,7 @@@
  
  #include "utils_cmd_flush.h"
  #include "utils_cmd_getval.h"
 +#include "utils_cmd_getthreshold.h"
  #include "utils_cmd_listval.h"
  #include "utils_cmd_putval.h"
  #include "utils_cmd_putnotif.h"
@@@ -128,7 -122,16 +128,16 @@@ static int us_open_socket (void
                return (-1);
        }
  
-       chmod (sa.sun_path, sock_perms);
+       status = chmod (sa.sun_path, sock_perms);
+       if (status == -1)
+       {
+               char errbuf[1024];
+               ERROR ("unixsock plugin: chmod failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               close (sock_fd);
+               sock_fd = -1;
+               return (-1);
+       }
  
        status = listen (sock_fd, 8);
        if (status != 0)
@@@ -287,10 -290,6 +296,10 @@@ static void *us_handle_client (void *ar
                {
                        handle_getval (fhout, buffer);
                }
 +              else if (strcasecmp (fields[0], "getthreshold") == 0)
 +              {
 +                      handle_getthreshold (fhout, buffer);
 +              }
                else if (strcasecmp (fields[0], "putval") == 0)
                {
                        handle_putval (fhout, buffer);
diff --combined src/varnish.c
  #include "plugin.h"
  #include "configfile.h"
  
 -#include <varnish/varnishapi.h>
 +#if HAVE_VARNISH_V4
 +#include <vapi/vsm.h>
 +#include <vapi/vsc.h>
 +typedef struct VSC_C_main c_varnish_stats_t;
 +#endif
  
  #if HAVE_VARNISH_V3
 -# include <varnish/vsc.h>
 +#include <varnishapi.h>
 +#include <vsc.h>
  typedef struct VSC_C_main c_varnish_stats_t;
  #endif
  
  #if HAVE_VARNISH_V2
 +#include <varnishapi.h>
  typedef struct varnish_stats c_varnish_stats_t;
  #endif
  
@@@ -73,14 -67,11 +73,14 @@@ struct user_config_s 
  #endif
        _Bool collect_struct;
        _Bool collect_totals;
 -#ifdef HAVE_VARNISH_V3
 +#if HAVE_VARNISH_V3 || HAVE_VARNISH_V4
        _Bool collect_uptime;
  #endif
        _Bool collect_vcl;
        _Bool collect_workers;
 +#if HAVE_VARNISH_V4
 +      _Bool collect_vsm;
 +#endif
  };
  typedef struct user_config_s user_config_t; /* }}} */
  
@@@ -135,397 -126,6 +135,397 @@@ static int varnish_submit_derive (cons
        return (varnish_submit (plugin_instance, category, type, type_instance, value));
  } /* }}} int varnish_submit_derive */
  
 +#if HAVE_VARNISH_V3 || HAVE_VARNISH_V4
 +static int varnish_monitor (void *priv, const struct VSC_point * const pt) /* {{{ */
 +{
 +      uint64_t val;
 +      const user_config_t *conf;
 +      const char *class;
 +      const char *name;
 +
 +      if (pt == NULL)
 +              return (0);
 +
 +      conf = priv;
 +
 +#if HAVE_VARNISH_V4
 +      class = pt->section->fantom->type;
 +      name  = pt->desc->name;
 +
 +      if (strcmp(class, "MAIN") != 0)
 +              return (0);
 +
 +#elif HAVE_VARNISH_V3
 +      class = pt->class;
 +      name  = pt->name;
 +
 +      if (strcmp(class, "") != 0)
 +              return (0);
 +#endif
 +
 +      val = *(const volatile uint64_t*) pt->ptr;
 +
 +      if (conf->collect_cache)
 +      {
 +              if (strcmp(name, "cache_hit") == 0)
 +                      return varnish_submit_derive (conf->instance, "cache", "cache_result", "hit",     val);
 +              else if (strcmp(name, "cache_miss") == 0)
 +                      return varnish_submit_derive (conf->instance, "cache", "cache_result", "miss",    val);
 +              else if (strcmp(name, "cache_hitpass") == 0)
 +                      return varnish_submit_derive (conf->instance, "cache", "cache_result", "hitpass", val);
 +      }
 +
 +      if (conf->collect_connections)
 +      {
 +              if (strcmp(name, "client_conn") == 0)
 +                      return varnish_submit_derive (conf->instance, "connections", "connections", "accepted", val);
 +              else if (strcmp(name, "client_drop") == 0)
 +                      return varnish_submit_derive (conf->instance, "connections", "connections", "dropped" , val);
 +              else if (strcmp(name, "client_req") == 0)
 +                      return varnish_submit_derive (conf->instance, "connections", "connections", "received", val);
 +      }
 +
 +#ifdef HAVE_VARNISH_V3
 +      if (conf->collect_dirdns)
 +      {
 +              if (strcmp(name, "dir_dns_lookups") == 0)
 +                      return varnish_submit_derive (conf->instance, "dirdns", "cache_operation", "lookups",    val);
 +              else if (strcmp(name, "dir_dns_failed") == 0)
 +                      return varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "failed",     val);
 +              else if (strcmp(name, "dir_dns_hit") == 0)
 +                      return varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "hits",       val);
 +              else if (strcmp(name, "dir_dns_cache_full") == 0)
 +                      return varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "cache_full", val);
 +      }
 +#endif
 +
 +      if (conf->collect_esi)
 +      {
 +              if (strcmp(name, "esi_errors") == 0)
 +                      return varnish_submit_derive (conf->instance, "esi", "total_operations", "error",   val);
 +              else if (strcmp(name, "esi_parse") == 0)
 +                      return varnish_submit_derive (conf->instance, "esi", "total_operations", "parsed",  val);
 +              else if (strcmp(name, "esi_warnings") == 0)
 +                      return varnish_submit_derive (conf->instance, "esi", "total_operations", "warning", val);
 +      }
 +
 +      if (conf->collect_backend)
 +      {
 +              if (strcmp(name, "backend_conn") == 0)
 +                      return varnish_submit_derive (conf->instance, "backend", "connections", "success",       val);
 +              else if (strcmp(name, "backend_unhealthy") == 0)
 +                      return varnish_submit_derive (conf->instance, "backend", "connections", "not-attempted", val);
 +              else if (strcmp(name, "backend_busy") == 0)
 +                      return varnish_submit_derive (conf->instance, "backend", "connections", "too-many",      val);
 +              else if (strcmp(name, "backend_fail") == 0)
 +                      return varnish_submit_derive (conf->instance, "backend", "connections", "failures",      val);
 +              else if (strcmp(name, "backend_reuse") == 0)
 +                      return varnish_submit_derive (conf->instance, "backend", "connections", "reuses",        val);
 +              else if (strcmp(name, "backend_toolate") == 0)
 +                      return varnish_submit_derive (conf->instance, "backend", "connections", "was-closed",    val);
 +              else if (strcmp(name, "backend_recycle") == 0)
 +                      return varnish_submit_derive (conf->instance, "backend", "connections", "recycled",      val);
 +              else if (strcmp(name, "backend_unused") == 0)
 +                      return varnish_submit_derive (conf->instance, "backend", "connections", "unused",        val);
 +              else if (strcmp(name, "backend_retry") == 0)
 +                      return varnish_submit_derive (conf->instance, "backend", "connections", "retries",       val);
 +              else if (strcmp(name, "backend_req") == 0)
 +                      return varnish_submit_derive (conf->instance, "backend", "http_requests", "requests",    val);
 +              else if (strcmp(name, "n_backend") == 0)
 +                      return varnish_submit_gauge  (conf->instance, "backend", "backends", "n_backends",       val);
 +      }
 +
 +      if (conf->collect_fetch)
 +      {
 +              if (strcmp(name, "fetch_head") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "head",        val);
 +              else if (strcmp(name, "fetch_length") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "length",      val);
 +              else if (strcmp(name, "fetch_chunked") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "chunked",     val);
 +              else if (strcmp(name, "fetch_eof") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "eof",         val);
 +              else if (strcmp(name, "fetch_bad") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "bad_headers", val);
 +              else if (strcmp(name, "fetch_close") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "close",       val);
 +              else if (strcmp(name, "fetch_oldhttp") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "oldhttp",     val);
 +              else if (strcmp(name, "fetch_zero") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "zero",        val);
 +              else if (strcmp(name, "fetch_failed") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "failed",      val);
 +              else if (strcmp(name, "fetch_1xx") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_1xx", val);
 +              else if (strcmp(name, "fetch_204") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_204", val);
 +              else if (strcmp(name, "fetch_304") == 0)
 +                      return varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_304", val);
 +      }
 +
 +      if (conf->collect_hcb)
 +      {
 +              if (strcmp(name, "hcb_nolock") == 0)
 +                      return varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_nolock", val);
 +              else if (strcmp(name, "hcb_lock") == 0)
 +                      return varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_lock",   val);
 +              else if (strcmp(name, "hcb_insert") == 0)
 +                      return varnish_submit_derive (conf->instance, "hcb", "cache_operation", "insert",        val);
 +      }
 +
 +      if (conf->collect_objects)
 +      {
 +              if (strcmp(name, "n_expired") == 0)
 +                      return varnish_submit_derive (conf->instance, "objects", "total_objects", "expired",            val);
 +              else if (strcmp(name, "n_lru_nuked") == 0)
 +                      return varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_nuked",          val);
 +              else if (strcmp(name, "n_lru_saved") == 0)
 +                      return varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_saved",          val);
 +              else if (strcmp(name, "n_lru_moved") == 0)
 +                      return varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_moved",          val);
 +              else if (strcmp(name, "n_deathrow") == 0)
 +                      return varnish_submit_derive (conf->instance, "objects", "total_objects", "deathrow",           val);
 +              else if (strcmp(name, "losthdr") == 0)
 +                      return varnish_submit_derive (conf->instance, "objects", "total_objects", "header_overflow",    val);
 +              else if (strcmp(name, "n_obj_purged") == 0)
 +                      return varnish_submit_derive (conf->instance, "objects", "total_objects", "purged",             val);
 +              else if (strcmp(name, "n_objsendfile") == 0)
 +                      return varnish_submit_derive (conf->instance, "objects", "total_objects", "sent_sendfile",      val);
 +              else if (strcmp(name, "n_objwrite") == 0)
 +                      return varnish_submit_derive (conf->instance, "objects", "total_objects", "sent_write",         val);
 +              else if (strcmp(name, "n_objoverflow") == 0)
 +                      return varnish_submit_derive (conf->instance, "objects", "total_objects", "workspace_overflow", val);
 +      }
 +
 +#if HAVE_VARNISH_V3
 +      if (conf->collect_ban)
 +      {
 +              if (strcmp(name, "n_ban") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "total",          val);
 +              else if (strcmp(name, "n_ban_add") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "added",          val);
 +              else if (strcmp(name, "n_ban_retire") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "deleted",        val);
 +              else if (strcmp(name, "n_ban_obj_test") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "objects_tested", val);
 +              else if (strcmp(name, "n_ban_re_test") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "regexps_tested", val);
 +              else if (strcmp(name, "n_ban_dups") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "duplicate",      val);
 +      }
 +#endif
 +#if HAVE_VARNISH_V4
 +      if (conf->collect_ban)
 +      {
 +              if (strcmp(name, "bans") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "total",     val);
 +              else if (strcmp(name, "bans_added") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "added",     val);
 +              else if (strcmp(name, "bans_obj") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "obj",       val);
 +              else if (strcmp(name, "bans_req") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "req",       val);
 +              else if (strcmp(name, "bans_completed") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "completed", val);
 +              else if (strcmp(name, "bans_deleted") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "deleted",   val);
 +              else if (strcmp(name, "bans_tested") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "tested",    val);
 +              else if (strcmp(name, "bans_dups") == 0)
 +                      return varnish_submit_derive (conf->instance, "ban", "total_operations", "duplicate", val);
 +      }
 +#endif
 +
 +      if (conf->collect_session)
 +      {
 +              if (strcmp(name, "sess_closed") == 0)
 +                      return varnish_submit_derive (conf->instance, "session", "total_operations", "closed",    val);
 +              else if (strcmp(name, "sess_pipeline") == 0)
 +                      return varnish_submit_derive (conf->instance, "session", "total_operations", "pipeline",  val);
 +              else if (strcmp(name, "sess_readahead") == 0)
 +                      return varnish_submit_derive (conf->instance, "session", "total_operations", "readahead", val);
 +              else if (strcmp(name, "sess_conn") == 0)
 +                      return varnish_submit_derive (conf->instance, "session", "total_operations", "accepted",  val);
 +              else if (strcmp(name, "sess_drop") == 0)
 +                      return varnish_submit_derive (conf->instance, "session", "total_operations", "dropped",   val);
 +              else if (strcmp(name, "sess_fail") == 0)
 +                      return varnish_submit_derive (conf->instance, "session", "total_operations", "failed",    val);
 +              else if (strcmp(name, "sess_pipe_overflow") == 0)
 +                      return varnish_submit_derive (conf->instance, "session", "total_operations", "overflow",  val);
 +              else if (strcmp(name, "sess_queued") == 0)
 +                      return varnish_submit_derive (conf->instance, "session", "total_operations", "queued",    val);
 +              else if (strcmp(name, "sess_linger") == 0)
 +                      return varnish_submit_derive (conf->instance, "session", "total_operations", "linger",    val);
 +              else if (strcmp(name, "sess_herd") == 0)
 +                      return varnish_submit_derive (conf->instance, "session", "total_operations", "herd",      val);
 +      }
 +
 +      if (conf->collect_shm)
 +      {
 +              if (strcmp(name, "shm_records") == 0)
 +                      return varnish_submit_derive (conf->instance, "shm", "total_operations", "records",    val);
 +              else if (strcmp(name, "shm_writes") == 0)
 +                      return varnish_submit_derive (conf->instance, "shm", "total_operations", "writes",     val);
 +              else if (strcmp(name, "shm_flushes") == 0)
 +                      return varnish_submit_derive (conf->instance, "shm", "total_operations", "flushes",    val);
 +              else if (strcmp(name, "shm_cont") == 0)
 +                      return varnish_submit_derive (conf->instance, "shm", "total_operations", "contention", val);
 +              else if (strcmp(name, "shm_cycles") == 0)
 +                      return varnish_submit_derive (conf->instance, "shm", "total_operations", "cycles",     val);
 +      }
 +
 +      if (conf->collect_sms)
 +      {
 +              if (strcmp(name, "sms_nreq") == 0)
 +                      return varnish_submit_derive (conf->instance, "sms", "total_requests", "allocator", val);
 +              else if (strcmp(name, "sms_nobj") == 0)
 +                      return varnish_submit_gauge (conf->instance,  "sms", "requests", "outstanding",     val);
 +              else if (strcmp(name, "sms_nbytes") == 0)
 +                      return varnish_submit_gauge (conf->instance,  "sms", "bytes", "outstanding",        val);
 +              else if (strcmp(name, "sms_balloc") == 0)
 +                      return varnish_submit_derive (conf->instance,  "sms", "total_bytes", "allocated",   val);
 +              else if (strcmp(name, "sms_bfree") == 0)
 +                      return varnish_submit_derive (conf->instance,  "sms", "total_bytes", "free",        val);
 +      }
 +
 +      if (conf->collect_struct)
 +      {
 +              if (strcmp(name, "n_sess_mem") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "current_sessions", "sess_mem",  val);
 +              else if (strcmp(name, "n_sess") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "current_sessions", "sess",      val);
 +              else if (strcmp(name, "n_object") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "objects", "object",             val);
 +              else if (strcmp(name, "n_vampireobject") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "objects", "vampireobject",      val);
 +              else if (strcmp(name, "n_objectcore") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "objects", "objectcore",         val);
 +              else if (strcmp(name, "n_waitinglist") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "objects", "waitinglist",        val);
 +              else if (strcmp(name, "n_objecthead") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "objects", "objecthead",         val);
 +              else if (strcmp(name, "n_smf") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "objects", "smf",                val);
 +              else if (strcmp(name, "n_smf_frag") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "objects", "smf_frag",           val);
 +              else if (strcmp(name, "n_smf_large") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "objects", "smf_large",          val);
 +              else if (strcmp(name, "n_vbe_conn") == 0)
 +                      return varnish_submit_gauge (conf->instance, "struct", "objects", "vbe_conn",           val);
 +      }
 +
 +      if (conf->collect_totals)
 +      {
 +              if (strcmp(name, "s_sess") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_sessions", "sessions",  val);
 +              else if (strcmp(name, "s_req") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_requests", "requests",  val);
 +              else if (strcmp(name, "s_pipe") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_operations", "pipe",    val);
 +              else if (strcmp(name, "s_pass") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_operations", "pass",    val);
 +              else if (strcmp(name, "s_fetch") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_operations", "fetches", val);
 +              else if (strcmp(name, "s_synth") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_bytes", "synth",        val);
 +              else if (strcmp(name, "s_req_hdrbytes") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_bytes", "req_header",   val);
 +              else if (strcmp(name, "s_req_bodybytes") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_bytes", "req_body",     val);
 +              else if (strcmp(name, "s_resp_hdrbytes") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_bytes", "resp_header",  val);
 +              else if (strcmp(name, "s_resp_bodybytes") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_bytes", "resp_body",    val);
 +              else if (strcmp(name, "s_pipe_hdrbytes") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_bytes", "pipe_header",  val);
 +              else if (strcmp(name, "s_pipe_in") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_bytes", "pipe_in",      val);
 +              else if (strcmp(name, "s_pipe_out") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_bytes", "pipe_out",     val);
 +              else if (strcmp(name, "n_purges") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_operations", "purges",  val);
 +              else if (strcmp(name, "s_hdrbytes") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_bytes", "header-bytes", val);
 +              else if (strcmp(name, "s_bodybytes") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_bytes", "body-bytes",   val);
 +              else if (strcmp(name, "n_gzip") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_operations", "gzip",    val);
 +              else if (strcmp(name, "n_gunzip") == 0)
 +                      return varnish_submit_derive (conf->instance, "totals", "total_operations", "gunzip",  val);
 +      }
 +
 +      if (conf->collect_uptime)
 +      {
 +              if (strcmp(name, "uptime") == 0)
 +                      return varnish_submit_gauge (conf->instance, "uptime", "uptime", "client_uptime", val);
 +      }
 +
 +      if (conf->collect_vcl)
 +      {
 +              if (strcmp(name, "n_vcl") == 0)
 +                      return varnish_submit_gauge (conf->instance, "vcl", "vcl", "total_vcl",     val);
 +              else if (strcmp(name, "n_vcl_avail") == 0)
 +                      return varnish_submit_gauge (conf->instance, "vcl", "vcl", "avail_vcl",     val);
 +              else if (strcmp(name, "n_vcl_discard") == 0)
 +                      return varnish_submit_gauge (conf->instance, "vcl", "vcl", "discarded_vcl", val);
 +              else if (strcmp(name, "vmods") == 0)
 +                      return varnish_submit_gauge (conf->instance, "vcl", "objects", "vmod",      val);
 +      }
 +
 +      if (conf->collect_workers)
 +      {
 +              if (strcmp(name, "threads") == 0)
 +                      return varnish_submit_gauge (conf->instance, "workers", "threads", "worker",               val);
 +              else if (strcmp(name, "threads_created") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_threads", "created",       val);
 +              else if (strcmp(name, "threads_failed") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_threads", "failed",        val);
 +              else if (strcmp(name, "threads_limited") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_threads", "limited",       val);
 +              else if (strcmp(name, "threads_destroyed") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_threads", "dropped",       val);
 +              else if (strcmp(name, "thread_queue_len") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "queue_length",  "threads",       val);
 +              else if (strcmp(name, "n_wrk") == 0)
 +                      return varnish_submit_gauge (conf->instance, "workers", "threads", "worker",               val);
 +              else if (strcmp(name, "n_wrk_create") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_threads", "created",       val);
 +              else if (strcmp(name, "n_wrk_failed") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_threads", "failed",        val);
 +              else if (strcmp(name, "n_wrk_max") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_threads", "limited",       val);
 +              else if (strcmp(name, "n_wrk_drop") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_threads", "dropped",       val);
 +              else if (strcmp(name, "n_wrk_queue") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_requests", "queued",       val);
 +              else if (strcmp(name, "n_wrk_overflow") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_requests", "overflowed",   val);
 +              else if (strcmp(name, "n_wrk_queued") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_requests", "queued",       val);
 +              else if (strcmp(name, "n_wrk_lqueue") == 0)
 +                      return varnish_submit_derive (conf->instance, "workers", "total_requests", "queue_length", val);
 +      }
 +
 +#if HAVE_VARNISH_V4
 +      if (conf->collect_vsm)
 +      {
 +              if (strcmp(name, "vsm_free") == 0)
 +                      return varnish_submit_gauge (conf->instance, "vsm", "bytes", "free",              val);
 +              else if (strcmp(name, "vsm_used") == 0)
 +                      return varnish_submit_gauge (conf->instance, "vsm", "bytes", "used",              val);
 +              else if (strcmp(name, "vsm_cooling") == 0)
 +                      return varnish_submit_gauge (conf->instance, "vsm", "bytes", "cooling",           val);
 +              else if (strcmp(name, "vsm_overflow") == 0)
 +                      return varnish_submit_gauge (conf->instance, "vsm", "bytes", "overflow",          val);
 +              else if (strcmp(name, "vsm_overflowed") == 0)
 +                      return varnish_submit_derive (conf->instance, "vsm", "total_bytes", "overflowed", val);
 +      }
 +#endif
 +
 +      return (0);
 +
 +} /* }}} static int varnish_monitor */
 +#else /* if HAVE_VARNISH_V2 */
  static void varnish_monitor (const user_config_t *conf, /* {{{ */
                const c_varnish_stats_t *stats)
  {
                varnish_submit_derive (conf->instance, "connections", "connections", "received", stats->client_req);
        }
  
 -#ifdef HAVE_VARNISH_V3
 -      if (conf->collect_dirdns)
 -      {
 -              /* DNS director lookups */
 -              varnish_submit_derive (conf->instance, "dirdns", "cache_operation", "lookups",    stats->dir_dns_lookups);
 -              /* DNS director failed lookups */
 -              varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "failed",     stats->dir_dns_failed);
 -              /* DNS director cached lookups hit */
 -              varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "hits",       stats->dir_dns_hit);
 -              /* DNS director full dnscache */
 -              varnish_submit_derive (conf->instance, "dirdns", "cache_result",    "cache_full", stats->dir_dns_cache_full);
 -      }
 -#endif
 -
        if (conf->collect_esi)
        {
                /* ESI parse errors (unlock)   */
                varnish_submit_derive (conf->instance, "esi", "total_operations", "error",   stats->esi_errors);
 -#if HAVE_VARNISH_V2
                /* Objects ESI parsed (unlock) */
                varnish_submit_derive (conf->instance, "esi", "total_operations", "parsed",  stats->esi_parse);
 -#else
 -              /* ESI parse warnings (unlock) */
 -              varnish_submit_derive (conf->instance, "esi", "total_operations", "warning", stats->esi_warnings);
 -#endif
        }
  
        if (conf->collect_backend)
                varnish_submit_derive (conf->instance, "backend", "connections", "was-closed"   , stats->backend_toolate);
                /* Backend conn. recycles      */
                varnish_submit_derive (conf->instance, "backend", "connections", "recycled"     , stats->backend_recycle);
 -#if HAVE_VARNISH_V2
                /* Backend conn. unused        */
                varnish_submit_derive (conf->instance, "backend", "connections", "unused"       , stats->backend_unused);
 -#else
 -              /* Backend conn. retry         */
 -              varnish_submit_derive (conf->instance, "backend", "connections", "retries"      , stats->backend_retry);
 -#endif
                /* Backend requests mades      */
                varnish_submit_derive (conf->instance, "backend", "http_requests", "requests"   , stats->backend_req);
                /* N backends                  */
                varnish_submit_derive (conf->instance, "fetch", "http_requests", "zero"       , stats->fetch_zero);
                /* Fetch failed              */
                varnish_submit_derive (conf->instance, "fetch", "http_requests", "failed"     , stats->fetch_failed);
 -#if HAVE_VARNISH_V3
 -              /* Fetch no body (1xx)       */
 -              varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_1xx", stats->fetch_1xx);
 -              /* Fetch no body (204)       */
 -              varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_204", stats->fetch_204);
 -              /* Fetch no body (304)       */
 -              varnish_submit_derive (conf->instance, "fetch", "http_requests", "no_body_304", stats->fetch_304);
 -#endif
        }
  
        if (conf->collect_hcb)
                varnish_submit_derive (conf->instance, "objects", "total_objects", "expired",            stats->n_expired);
                /* N LRU nuked objects           */
                varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_nuked",          stats->n_lru_nuked);
 -#if HAVE_VARNISH_V2
                /* N LRU saved objects           */
                varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_saved",          stats->n_lru_saved);
 -#endif
                /* N LRU moved objects           */
                varnish_submit_derive (conf->instance, "objects", "total_objects", "lru_moved",          stats->n_lru_moved);
 -#if HAVE_VARNISH_V2
                /* N objects on deathrow         */
                varnish_submit_derive (conf->instance, "objects", "total_objects", "deathrow",           stats->n_deathrow);
 -#endif
                /* HTTP header overflows         */
                varnish_submit_derive (conf->instance, "objects", "total_objects", "header_overflow",    stats->losthdr);
                /* Objects sent with sendfile    */
                varnish_submit_derive (conf->instance, "objects", "total_objects", "workspace_overflow", stats->n_objoverflow);
        }
  
 -#if HAVE_VARNISH_V2
        if (conf->collect_purge)
        {
                /* N total active purges      */
                /* N duplicate purges removed */
                varnish_submit_derive (conf->instance, "purge", "total_operations", "duplicate",        stats->n_purge_dups);
        }
 -#else
 -      if (conf->collect_ban)
 -      {
 -              /* N total active bans      */
 -              varnish_submit_derive (conf->instance, "ban", "total_operations", "total",          stats->n_ban);
 -              /* N new bans added         */
 -              varnish_submit_derive (conf->instance, "ban", "total_operations", "added",          stats->n_ban_add);
 -              /* N old bans deleted       */
 -              varnish_submit_derive (conf->instance, "ban", "total_operations", "deleted",        stats->n_ban_retire);
 -              /* N objects tested         */
 -              varnish_submit_derive (conf->instance, "ban", "total_operations", "objects_tested", stats->n_ban_obj_test);
 -              /* N regexps tested against */
 -              varnish_submit_derive (conf->instance, "ban", "total_operations", "regexps_tested", stats->n_ban_re_test);
 -              /* N duplicate bans removed */
 -              varnish_submit_derive (conf->instance, "ban", "total_operations", "duplicate",      stats->n_ban_dups);
 -      }
 -#endif
  
        if (conf->collect_session)
        {
                varnish_submit_derive (conf->instance, "shm", "total_operations", "cycles"    , stats->shm_cycles);
        }
  
 -#if HAVE_VARNISH_V2
        if (conf->collect_sm)
        {
                /* allocator requests */
                /* SMA bytes free */
                varnish_submit_derive (conf->instance,  "sma", "total_bytes", "free" ,     stats->sma_bfree);
        }
 -#endif
  
        if (conf->collect_sms)
        {
                varnish_submit_gauge (conf->instance, "struct", "current_sessions", "sess",      stats->n_sess);
                /* N struct object         */
                varnish_submit_gauge (conf->instance, "struct", "objects", "object",             stats->n_object);
 -#ifdef HAVE_VARNISH_V3
 -              /* N unresurrected objects */
 -              varnish_submit_gauge (conf->instance, "struct", "objects", "vampireobject",      stats->n_vampireobject);
 -              /* N struct objectcore     */
 -              varnish_submit_gauge (conf->instance, "struct", "objects", "objectcore",         stats->n_objectcore);
 -#endif
                /* N struct objecthead     */
                varnish_submit_gauge (conf->instance, "struct", "objects", "objecthead",         stats->n_objecthead);
 -#ifdef HAVE_VARNISH_V2
                /* N struct smf            */
                varnish_submit_gauge (conf->instance, "struct", "objects", "smf",                stats->n_smf);
                /* N small free smf         */
                varnish_submit_gauge (conf->instance, "struct", "objects", "smf_large",          stats->n_smf_large);
                /* N struct vbe_conn        */
                varnish_submit_gauge (conf->instance, "struct", "objects", "vbe_conn",           stats->n_vbe_conn);
 -#endif
        }
  
        if (conf->collect_totals)
                varnish_submit_derive (conf->instance, "totals", "total_bytes", "body-bytes",   stats->s_bodybytes);
        }
  
 -#ifdef HAVE_VARNISH_V3
 -      if (conf->collect_uptime)
 -      {
 -              /* Client uptime */
 -              varnish_submit_gauge (conf->instance, "uptime", "uptime", "client_uptime", stats->uptime);
 -      }
 -#endif
 -
        if (conf->collect_vcl)
        {
                /* N vcl total     */
                /* worker threads limited */
                varnish_submit_derive (conf->instance, "workers", "total_threads", "limited",     stats->n_wrk_max);
                /* dropped work requests */
 -              varnish_submit_derive (conf->instance, "workers", "total_requests", "dropped",    stats->n_wrk_drop);
 -#ifdef HAVE_VARNISH_V2
 +              varnish_submit_derive (conf->instance, "workers", "total_threads", "dropped",     stats->n_wrk_drop);
                /* queued work requests */
                varnish_submit_derive (conf->instance, "workers", "total_requests", "queued",     stats->n_wrk_queue);
                /* overflowed work requests */
                varnish_submit_derive (conf->instance, "workers", "total_requests", "overflowed", stats->n_wrk_overflow);
 -#else
 -              /* queued work requests */
 -              varnish_submit_derive (conf->instance, "workers", "total_requests", "queued",       stats->n_wrk_queued);
 -              /* work request queue length */
 -              varnish_submit_derive (conf->instance, "workers", "total_requests", "queue_length", stats->n_wrk_lqueue);
 -#endif
        }
 +
  } /* }}} void varnish_monitor */
 +#endif
  
 -#if HAVE_VARNISH_V3
 +#if HAVE_VARNISH_V3 || HAVE_VARNISH_V4
  static int varnish_read (user_data_t *ud) /* {{{ */
  {
        struct VSM_data *vd;
        conf = ud->data;
  
        vd = VSM_New();
 +#if HAVE_VARNISH_V3
        VSC_Setup(vd);
 +#endif
  
        if (conf->instance != NULL)
        {
                status = VSM_n_Arg (vd, conf->instance);
                if (status < 0)
                {
 +                      VSM_Delete (vd);
                        ERROR ("varnish plugin: VSM_n_Arg (\"%s\") failed "
                                        "with status %i.",
                                        conf->instance, status);
                }
        }
  
 +#if HAVE_VARNISH_V3
        if (VSC_Open (vd, /* diag = */ 1))
 +#else /* if HAVE_VARNISH_V4 */
 +      if (VSM_Open (vd))
 +#endif
        {
 -              ERROR ("varnish plugin: Unable to load statistics.");
 +              VSM_Delete (vd);
 +              ERROR ("varnish plugin: Unable to open connection.");
  
                return (-1);
        }
  
 +#if HAVE_VARNISH_V3
        stats = VSC_Main(vd);
 +#else /* if HAVE_VARNISH_V4 */
 +      stats = VSC_Main(vd, NULL);
 +#endif
 +      if (!stats)
 +      {
 +              VSM_Delete (vd);
 +              ERROR ("varnish plugin: Unable to get statistics.");
  
 -      varnish_monitor (conf, stats);
 -      VSM_Close (vd);
 +              return (-1);
 +      }
 +
 +#if HAVE_VARNISH_V3
 +      VSC_Iter (vd, varnish_monitor, conf);
 +#else /* if HAVE_VARNISH_V4 */
 +      VSC_Iter (vd, NULL, varnish_monitor, conf);
 +#endif
 +      VSM_Delete (vd);
  
        return (0);
  } /* }}} */
@@@ -921,14 -575,11 +921,14 @@@ static int varnish_config_apply_defaul
        conf->collect_sms         = 0;
        conf->collect_struct      = 0;
        conf->collect_totals      = 0;
 -#ifdef HAVE_VARNISH_V3
 +#if HAVE_VARNISH_V3 || HAVE_VARNISH_V4
        conf->collect_uptime      = 0;
  #endif
        conf->collect_vcl         = 0;
        conf->collect_workers     = 0;
 +#if HAVE_VARNISH_V4
 +      conf->collect_vsm         = 0;
 +#endif
  
        return (0);
  } /* }}} int varnish_config_apply_default */
@@@ -1000,6 -651,7 +1000,7 @@@ static int varnish_config_instance (con
        {
                WARNING ("Varnish plugin: \"Instance\" blocks accept only "
                                "one argument.");
+               sfree (conf);
                return (EINVAL);
        }
  
                        cf_util_get_boolean (child, &conf->collect_struct);
                else if (strcasecmp ("CollectTotals", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_totals);
 -#ifdef HAVE_VARNISH_V3
 +#if HAVE_VARNISH_V3 || HAVE_VARNISH_V4
                else if (strcasecmp ("CollectUptime", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_uptime);
  #endif
                        cf_util_get_boolean (child, &conf->collect_vcl);
                else if (strcasecmp ("CollectWorkers", child->key) == 0)
                        cf_util_get_boolean (child, &conf->collect_workers);
 +#if HAVE_VARNISH_V4
 +              else if (strcasecmp ("CollectVSM", child->key) == 0)
 +                      cf_util_get_boolean (child, &conf->collect_vsm);
 +#endif
                else
                {
                        WARNING ("Varnish plugin: Ignoring unknown "
  #endif
                        && !conf->collect_struct
                        && !conf->collect_totals
 -#ifdef HAVE_VARNISH_V3
 +#if HAVE_VARNISH_V3 || HAVE_VARNISH_V4
                        && !conf->collect_uptime
  #endif
                        && !conf->collect_vcl
 -                      && !conf->collect_workers)
 +                      && !conf->collect_workers
 +#if HAVE_VARNISH_V4
 +                      && !conf->collect_vsm
 +#endif
 +      )
        {
                WARNING ("Varnish plugin: No metric has been configured for "
                                "instance \"%s\". Disabling this instance.",
                                (conf->instance == NULL) ? "localhost" : conf->instance);
+               sfree (conf);
                return (EINVAL);
        }