fix configure.ac
authorRinigus <rinigus.git@gmail.com>
Sun, 24 Jul 2016 11:38:08 +0000 (14:38 +0300)
committerRinigus <rinigus.git@gmail.com>
Sun, 24 Jul 2016 11:38:08 +0000 (14:38 +0300)
92 files changed:
.gitignore
AUTHORS
Makefile.am
README
configure.ac
contrib/redhat/collectd.spec
proto/Makefile.am [new file with mode: 0644]
proto/collectd.proto [new file with mode: 0644]
proto/types.proto [new file with mode: 0644]
src/Makefile.am
src/aggregation.c
src/amqp.c
src/apcups.c
src/ceph_test.c
src/chrony.c [new file with mode: 0644]
src/collectd-email.pod
src/collectd-exec.pod
src/collectd-nagios.c
src/collectd-perl.pod
src/collectd-python.pod
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.pod
src/conntrack.c
src/curl.c
src/curl_json.c
src/curl_xml.c
src/daemon/Makefile.am
src/daemon/collectd.c
src/daemon/collectd.h
src/daemon/common.c
src/daemon/configfile.c
src/daemon/configfile.h
src/daemon/plugin.h
src/daemon/utils_cache.c
src/daemon/utils_cache.h
src/daemon/utils_threshold.c
src/disk.c
src/dns.c
src/email.c
src/ethstat.c
src/exec.c
src/gmond.c
src/grpc.cc [new file with mode: 0644]
src/ipmi.c
src/java.c
src/libcollectdclient/client.c
src/log_logstash.c
src/logfile.c
src/mqtt.c
src/mysql.c
src/network.c
src/notify_email.c
src/ntpd.c
src/nut.c
src/perl.c
src/pinba.c
src/ping.c
src/postgresql.c
src/processes.c
src/python.c
src/redis.c
src/rrdtool.c
src/sigrok.c
src/smart.c
src/snmp.c
src/statsd.c
src/teamspeak2.c
src/testing.h
src/threshold.c
src/types.db
src/unixsock.c
src/utils_cmd_putval.c
src/utils_curl_stats.c [new file with mode: 0644]
src/utils_curl_stats.h [new file with mode: 0644]
src/utils_db_query.c
src/utils_mount_test.c
src/utils_vl_lookup.c
src/valgrind.FreeBSD.suppress [new file with mode: 0644]
src/virt.c
src/write_graphite.c
src/write_http.c
src/write_kafka.c
src/write_log.c
src/write_mongodb.c
src/write_redis.c
src/write_riemann.c
src/write_riemann_threshold.c
src/write_sensu.c
src/write_tsdb.c
src/zookeeper.c
testwrapper.sh [new file with mode: 0755]

index b14cc7a..3e26ccd 100644 (file)
@@ -52,6 +52,9 @@ src/liboconfig/scanner.c
 
 # protobuf stuff:
 src/*.pb-c.[ch]
+src/*.grpc.pb.cc
+src/*.pb.cc
+src/*.pb.h
 
 # make dist stuff:
 /collectd-*.tar.gz
@@ -74,8 +77,11 @@ bindings/java/org/collectd/java/*.class
 
 # backup stuff
 *~
+
 # Unit tests
+src/daemon/test-suite.log
 src/tests/
+src/test-suite.log
 test_*
 
 # src/daemon/...
diff --git a/AUTHORS b/AUTHORS
index 02b1256..bdffa92 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,9 +4,31 @@ Permanent project members
 Florian "octo" Forster <octo at verplant.org>
  - Initial author.
 
+Marc Fournier <marc.fournier at camptocamp.com>
+ - Various fixes to the varnish plugin.
+ - RPM specfile maintenance.
+ - libmnl support in the netlink plugin.
+ - linux support in the zfs_arc plugin.
+ - openldap plugin.
+ - continuous integration system.
+
+Pierre-Yves Ritschard <pyr at spootnik.org>
+ - Write-Riemann plugin.
+ - Write-Graphite plugin: Notification support.
+ - Write-Kafka plugin.
+ - Log-Logstash plugin.
+ - Normalization in the CPU plugin.
+ - Relative values in the Load plugin.
+
+Ruben Kerkhof <ruben@rubenkerkhof.com>
+ - Bugfixes and enhancements in many places all around the project.
+ - Fedora package.
+
 Sebastian "tokkee" Harl <sh at tokkee.org>
  - Bugfixes and enhancements in many places all around the project.
+ - gprc plugin.
  - perl plugin.
+ - postgresql plugin.
  - users plugin.
  - vserver plugin.
  - Debian package.
@@ -75,11 +97,15 @@ Christophe Kalt <collectd at klb.taranis.org>
  - The version 3 `log' mode.
  - Many Solaris related hints and fixes.
 
+Claudius Zingerli <gitmail at zeuz.ch>
+ - chrony plugin.
+
 Cyril Feraudet <cyril at feraudet.com>
  - ethstat plugin.
 
 Dagobert Michelsen <dam at opencsw.org>
  - zone plugin.
+ - Many Solaris related hints and fixes.
 
 Dan Berrange <berrange at redhat.com>
  - uuid plugin.
@@ -118,6 +144,9 @@ Flavio Stanchina <flavio at stanchina.net>
 Franck Lombardi
  - UNIX socket code for the memcached plugin.
 
+Gergely Nagy <algernon at madhouse-project.org>
+ - Write-Riemann plugin.
+
 Jason Pepas <cell at ices.utexas.edu>
  - nfs plugin.
 
@@ -165,13 +194,6 @@ Manuel Sanmartin
    + swap
  - Various AIX-related fixes and hacks.
 
-Marc Fournier <marc.fournier at camptocamp.com>
- - Various fixes to the varnish plugin.
- - RPM specfile update.
- - libmnl support in the netlink plugin.
- - linux support in the zfs_arc plugin.
- - openldap plugin.
-
 Marco Chiappero <marco at absence.it>
  - uptime plugin.
  - ip6tables support in the iptables plugin.
@@ -221,6 +243,10 @@ Paul Sadauskas <psadauskas at gmail.com>
  - `ReportByDevice' option of the df plugin.
  - write_http plugin.
 
+Pavel Rochnyack <pavel2000 at ngs.ru>
+ - xencpu plugin.
+ - Bugfixes and enhancements in many places all around the project.
+
 Peter Holik <peter at holik.at>
  - cpufreq plugin.
  - multimeter plugin.
@@ -231,14 +257,6 @@ Peter Holik <peter at holik.at>
 Phoenix Kayo <kayo.k11.4 at gmail.com>
  - pinba plugin.
 
-Pierre-Yves Ritschard <pyr at spootnik.org>
- - Write-Riemann plugin.
- - Write-Graphite plugin: Notification support.
- - Write-Kafka plugin.
- - Log-Logstash plugin.
- - Normalization in the CPU plugin.
- - Relative values in the Load plugin.
-
 Piotr Hosowicz <the55 at wp.pl>
  - SMF manifest for collectd.
 
index b79ea1c..03bdd39 100644 (file)
@@ -6,11 +6,11 @@ if BUILD_INCLUDED_LTDL
 SUBDIRS += libltdl
 endif
 
-SUBDIRS += src bindings .
+SUBDIRS += proto src bindings .
 
 AM_CPPFLAGS = $(LTDLINCL)
 
-EXTRA_DIST = contrib version-gen.sh
+EXTRA_DIST = contrib version-gen.sh testwrapper.sh
 
 install-exec-hook:
        $(mkinstalldirs) $(DESTDIR)$(localstatedir)/run
diff --git a/README b/README
index c915a53..e5d5c37 100644 (file)
--- a/README
+++ b/README
@@ -1,6 +1,6 @@
  collectd - System information collection daemon
 =================================================
-http://collectd.org/
+https://collectd.org/
 
 About
 -----
@@ -51,6 +51,9 @@ Features
     - cgroups
       CPU accounting information for process groups under Linux.
 
+    - chrony
+      Chrony daemon statistics: Local clock drift, offset to peers, etc.
+
     - conntrack
       Number of nf_conntrack entries.
 
@@ -122,6 +125,9 @@ Features
     - gmond
       Receive multicast traffic from Ganglia instances.
 
+    - grpc
+      Receive values over the network using the gRPC framework.
+
     - hddtemp
       Hard disk temperatures using hddtempd.
 
@@ -385,6 +391,9 @@ Features
     - wireless
       Link quality of wireless cards. Linux only.
 
+    - xencpu
+      XEN Hypervisor CPU stats.
+
     - xmms
       Bitrate and frequency of music played with XMMS.
 
@@ -505,6 +514,9 @@ Features
       Send an E-mail with the notification message to the configured
       recipients.
 
+    - notify_nagios
+      Submit notifications as passive check results to a local nagios instance.
+
     - exec
       Execute a program or script to handle the notification.
       See collectd-exec(5).
@@ -679,6 +691,11 @@ Prerequisites
     Used by the `gmond' plugin to process data received from Ganglia.
     <http://ganglia.info/>
 
+  * libgrpc (optional)
+    Used by the `grpc' plugin. gRPC requires a C++ compiler supporting the
+    C++11 standard.
+    <https://grpc.io/>
+
   * libgcrypt (optional)
     Used by the `network' plugin for encryption and authentication.
     <http://www.gnupg.org/>
@@ -768,6 +785,11 @@ Prerequisites
     The PostgreSQL C client library used by the `postgresql' plugin.
     <http://www.postgresql.org/>
 
+  * libprotobuf, protoc 3.0+ (optional)
+    Used by the `grpc' plugin to generate service stubs and code to handle
+    network packets of collectd's protobuf-based network protocol.
+    <https://developers.google.com/protocol-buffers/>
+
   * libprotobuf-c, protoc-c (optional)
     Used by the `pinba' plugin to generate a parser for the network packets
     sent by the Pinba PHP extension.
@@ -832,6 +854,10 @@ Prerequisites
     `virt' plugins.
     <http://xmlsoft.org/>
 
+  * libxen (optional)
+    Used by the `xencpu' plugin.
+    <http://xenbits.xensource.com/>
+
   * libxmms (optional)
     <http://www.xmms.org/>
 
index 8be837f..949f040 100644 (file)
@@ -31,7 +31,7 @@ m4_ifdef([LT_PACKAGE_VERSION],
 
 AM_CONDITIONAL([BUILD_INCLUDED_LTDL], [test "x$LTDLDEPS" != "x"])
 
-AM_INIT_AUTOMAKE([subdir-objects tar-pax dist-bzip2 foreign])
+AM_INIT_AUTOMAKE([subdir-objects tar-pax dist-bzip2 no-dist-gzip foreign])
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 AC_LANG(C)
 
@@ -43,6 +43,7 @@ AC_SYS_LARGEFILE
 # Checks for programs.
 #
 AC_PROG_CC
+AC_PROG_CXX
 AC_PROG_CPP
 AC_PROG_EGREP
 AC_PROG_INSTALL
@@ -56,6 +57,8 @@ AC_PROG_LIBTOOL
 AC_PROG_LEX
 AC_PROG_YACC
 
+AC_PATH_PROG([VALGRIND], [valgrind])
+
 # Warn when pkg.m4 is missing
 m4_pattern_forbid([^_?PKG_[A-Z_]+$],[*** pkg.m4 missing, please install pkg-config])
 
@@ -73,6 +76,23 @@ then
        AC_MSG_ERROR([bison is missing and you do not have ${srcdir}/src/liboconfig/parser.c. Please install bison])
 fi
 
+AC_PATH_PROG([PROTOC], [protoc])
+have_protoc3="no"
+if test "x$PROTOC" != "x"; then
+       AC_MSG_CHECKING([for protoc 3.0.0+])
+       if $PROTOC --version | grep -q libprotoc.3; then
+               protoc3="yes (`$PROTOC --version`)"
+               have_protoc3="yes"
+       else
+               protoc3="no (`$PROTOC --version`)"
+       fi
+       AC_MSG_RESULT([$protoc3])
+fi
+AM_CONDITIONAL(HAVE_PROTOC3, test "x$have_protoc3" = "xyes")
+
+AC_PATH_PROG([GRPC_CPP_PLUGIN], [grpc_cpp_plugin])
+AM_CONDITIONAL(HAVE_GRPC_CPP, test "x$GRPC_CPP_PLUGIN" != "x")
+
 AC_CHECK_PROG([have_protoc_c], [protoc-c], [yes], [no])
 if test "x$have_protoc_c" = "xno"
 then
@@ -129,12 +149,12 @@ case $host_os in
 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"])
+AM_CONDITIONAL([BUILD_AIX], [test "x$ac_system" = "xAIX"])
+AM_CONDITIONAL([BUILD_DARWIN], [test "x$ac_system" = "xDarwin"])
+AM_CONDITIONAL([BUILD_FREEBSD], [test "x$ac_system" = "xFreeBSD"])
+AM_CONDITIONAL([BUILD_LINUX], [test "x$ac_system" = "xLinux"])
+AM_CONDITIONAL([BUILD_OPENBSD], [test "x$ac_system" = "xOpenBSD"])
+AM_CONDITIONAL([BUILD_SOLARIS], [test "x$ac_system" = "xSolaris"])
 
 if test "x$ac_system" = "xLinux"
 then
@@ -791,11 +811,22 @@ 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 setlocale)
 
 AC_FUNC_STRERROR_R
 
+test_cxx_flags() {
+       AC_LANG_PUSH([C++])
+       AC_LANG_CONFTEST([
+               AC_LANG_SOURCE([[int main(void){}]])
+       ])
+       $CXX -c conftest.cpp $CXXFLAGS $@ > /dev/null 2> /dev/null
+       ret=$?
+       rm -f conftest.o
+       AC_LANG_POP([C++])
+       return $ret
+}
+
 SAVE_CFLAGS="$CFLAGS"
 # Emulate behavior of src/Makefile.am
 if test "x$GCC" = "xyes"
@@ -1620,45 +1651,21 @@ if test "x$with_libhal" = "xyes"; then
        fi
 fi
 
-# --with-libpthread {{{
-AC_ARG_WITH(libpthread, [AS_HELP_STRING([--with-libpthread=@<:@=PREFIX@:>@], [Path to libpthread.])],
-[      if test "x$withval" != "xno" \
-               && test "x$withval" != "xyes"
-       then
-               LDFLAGS="$LDFLAGS -L$withval/lib"
-               CPPFLAGS="$CPPFLAGS -I$withval/include"
-               with_libpthread="yes"
-       else
-               if test "x$withval" = "xno"
-               then
-                       with_libpthread="no (disabled)"
-               fi
-       fi
-], [with_libpthread="yes"])
+SAVE_LIBS="$LIBS"
+AC_CHECK_LIB([pthread],
+  [pthread_create],
+  [],
+  [AC_MSG_ERROR([Symbol 'pthread_create' not found in libpthread"])],
+  []
+)
+PTHREAD_LIBS="$LIBS"
+LIBS="$SAVE_LIBS"
 
+AC_CHECK_HEADERS([pthread.h],
+  [],
+  [AC_MSG_ERROR([pthread.h not found])]
+)
 AC_SUBST([PTHREAD_LIBS])
-if test "x$with_libpthread" = "xyes"
-then
-       SAVE_LIBS="$LIBS"
-       AC_CHECK_LIB(pthread, pthread_create, [], [with_libpthread="no (Symbol 'pthread_create' not found)"], [])
-       PTHREAD_LIBS="$LIBS"
-       LIBS="$SAVE_LIBS"
-fi
-
-if test "x$with_libpthread" = "xyes"
-then
-       AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"])
-fi
-if test "x$with_libpthread" = "xyes"
-then
-       collect_pthread=1
-else
-       collect_pthread=0
-fi
-AC_DEFINE_UNQUOTED(HAVE_LIBPTHREAD, [$collect_pthread],
-       [Wether or not to use pthread (POSIX threads) library])
-AM_CONDITIONAL(BUILD_WITH_LIBPTHREAD, test "x$with_libpthread" = "xyes")
-# }}}
 
 m4_divert_once([HELP_WITH], [
 collectd additional packages:])
@@ -1958,6 +1965,36 @@ then
 fi
 if test "x$with_libcurl" = "xyes"
 then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_curl_cflags"
+       LDFLAGS="$LDFLAGS $with_curl_libs"
+       AC_CACHE_CHECK([for CURLINFO_APPCONNECT_TIME],
+               [c_cv_have_curlinfo_appconnect_time],
+               AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[
+#include <curl/curl.h>
+]],
+[[
+int val = CURLINFO_APPCONNECT_TIME;
+return val;
+]]
+                       )],
+                       [c_cv_have_curlinfo_appconnect_time="yes"],
+                       [c_cv_have_curlinfo_appconnect_time="no"]
+               )
+       )
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBCURL, test "x$with_libcurl" = "xyes")
+if test "x$c_cv_have_curlinfo_appconnect_time" = "xyes"
+then
+       AC_DEFINE(HAVE_CURLINFO_APPCONNECT_TIME, 1, [Define if curl.h defines CURLINFO_APPCONNECT_TIME.])
+fi
+
+if test "x$with_libcurl" = "xyes"
+then
        BUILD_WITH_LIBCURL_CFLAGS="$with_curl_cflags"
        BUILD_WITH_LIBCURL_LIBS="$with_curl_libs"
        AC_SUBST(BUILD_WITH_LIBCURL_CFLAGS)
@@ -1973,7 +2010,6 @@ 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")
 # }}}
 
 # --with-libdbi {{{
@@ -2227,6 +2263,62 @@ AC_SUBST(GCRYPT_LIBS)
 AM_CONDITIONAL(BUILD_WITH_LIBGCRYPT, test "x$with_libgcrypt" = "xyes")
 # }}}
 
+# --with-grpc {{{
+AC_ARG_WITH(grpc, [AS_HELP_STRING([--without-grpc], [Disable gRPC (default: autodetect).])],
+[
+       with_grpc="$withval"
+],
+[
+       with_grpc="yes"
+])
+
+if test "x$with_grpc" = "xyes"
+then
+       if test "x$have_protoc3" != "xyes"
+       then
+               with_grpc="no (requires protoc 3.0.0+)"
+       else if test "x$GRPC_CPP_PLUGIN" = "x"
+       then
+               with_grpc"no (requires grpc_cpp_plugin)"
+       fi; fi
+fi
+
+if test "x$with_grpc" = "xyes"
+then
+       AC_MSG_CHECKING([whether $CXX accepts -std=c++11])
+       if test_cxx_flags -std=c++11; then
+               AC_MSG_RESULT([yes])
+       else
+               AC_MSG_RESULT([no])
+               with_grpc="no (requires C++11 support)"
+       fi
+fi
+
+if test "x$with_grpc" = "xyes"
+then
+       AC_LANG_PUSH(C++)
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_CXXFLAGS="$CXXFLAGS"
+       CPPFLAGS="$CPPFLAGS -std=c++11"
+       CXXFLAGS="$CXXFLAGS -std=c++11"
+       AC_CHECK_HEADERS([grpc++/grpc++.h], [],
+                       [with_grpc="no (grpc++/grpc++.h not found)"])
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       CXXFLAGS="$SAVE_CXXFLAGS"
+       AC_LANG_POP(C++)
+fi
+with_libgrpc="no"
+if test "x$with_grpc" = "xyes"
+then
+       AC_LANG_PUSH(C++)
+       AC_CHECK_LIB([grpc], [grpc_register_plugin],
+                       [with_libgrpc="yes"],
+                       [with_grpc="no (libgrpc not found)"],
+                       [-lgpr -lprotobuf])
+       AC_LANG_POP(C++)
+fi
+# }}}
+
 # --with-libiptc {{{
 AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])],
 [
@@ -3383,9 +3475,9 @@ then
 fi
 if test "x$with_oracle" = "xyes"
 then
-       BUILD_WITH_ORACLE_CFLAGS="$with_oracle_cppflags"
+       BUILD_WITH_ORACLE_CPPFLAGS="$with_oracle_cppflags"
        BUILD_WITH_ORACLE_LIBS="$with_oracle_libs"
-       AC_SUBST(BUILD_WITH_ORACLE_CFLAGS)
+       AC_SUBST(BUILD_WITH_ORACLE_CPPFLAGS)
        AC_SUBST(BUILD_WITH_ORACLE_LIBS)
 fi
 # }}}
@@ -4792,7 +4884,12 @@ then
 
        CPPFLAGS="$SAVE_CPPFLAGS"
        LDFLAGS="$SAVE_LDFLAGS"
+       LIBXENCTL_CPPFLAGS="$with_libxenctl_cppflags"
+       LIBXENCTL_LDFLAGS="$with_libxenctl_ldflags"
+       AC_SUBST(LIBXENCTL_CPPFLAGS)
+       AC_SUBST(LIBXENCTL_LDFLAGS)
 fi
+# }}}
 
 # --with-libxmms {{{
 with_xmms_config="xmms-config"
@@ -5411,7 +5508,7 @@ AC_DEFUN(
             then
                     enable_plugin="yes"
             else
-                    enable_plugin="no"
+                    enable_plugin="$2"
             fi
         else
             enable_plugin="$enable_all_plugins"
@@ -5428,7 +5525,7 @@ AC_DEFUN(
                    fi
            else # User passed "yes" but dependency checking yielded "no" => Dependency problem.
                    dependency_error="yes"
-                   enable_plugin="no (dependency error)"
+                   enable_plugin="$2 (dependency error)"
            fi
     fi
     AM_CONDITIONAL([BUILD_PLUGIN_]my_toupper([$1]), test "x$enable_plugin" = "xyes")
@@ -5873,6 +5970,7 @@ 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([cgroups],             [$plugin_cgroups],         [CGroups CPU usage accounting])
+AC_PLUGIN([chrony],              [yes],                     [Chrony statistics])
 AC_PLUGIN([conntrack],           [$plugin_conntrack],       [nf_conntrack statistics])
 AC_PLUGIN([contextswitch],       [$plugin_contextswitch],   [context switch statistics])
 AC_PLUGIN([cpu],                 [$plugin_cpu],             [CPU usage statistics])
@@ -5895,6 +5993,7 @@ AC_PLUGIN([fhcount],             [$plugin_fhcount],         [File handles statis
 AC_PLUGIN([filecount],           [yes],                     [Count files in directories])
 AC_PLUGIN([fscache],             [$plugin_fscache],         [fscache statistics])
 AC_PLUGIN([gmond],               [$with_libganglia],        [Ganglia plugin])
+AC_PLUGIN([grpc],                [$with_grpc],              [gRPC plugin])
 AC_PLUGIN([hddtemp],             [yes],                     [Query hddtempd])
 AC_PLUGIN([interface],           [$plugin_interface],       [Interface traffic statistics])
 AC_PLUGIN([ipc],                 [$plugin_ipc],             [IPC statistics])
@@ -6152,13 +6251,16 @@ AC_SUBST(LCC_VERSION_STRING)
 AC_CONFIG_FILES(src/libcollectdclient/collectd/lcc_features.h)
 
 AM_CFLAGS="-Wall"
+AM_CXXFLAGS="-Wall"
 if test "x$enable_werror" != "xno"
 then
         AM_CFLAGS="$AM_CFLAGS -Werror"
+        AM_CXXFLAGS="$AM_CXXFLAGS -Werror"
 fi
 AC_SUBST([AM_CFLAGS])
+AC_SUBST([AM_CXXFLAGS])
 
-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_CONFIG_FILES([Makefile proto/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" \
@@ -6185,227 +6287,231 @@ then
        with_perl_bindings="yes ($PERL_BINDINGS_OPTIONS)"
 fi
 
-cat <<EOF;
-
-Configuration:
-  Build:
-    Platform  . . . . . . $ac_system
-    CC  . . . . . . . . . $CC
-    CFLAGS  . . . . . . . $AM_CFLAGS $CFLAGS
-    CPP . . . . . . . . . $CPP
-    CPPFLAGS  . . . . . . $CPPFLAGS
-    LD  . . . . . . . . . $LD
-    LDFLAGS . . . . . . . $LDFLAGS
-    YACC  . . . . . . . . $YACC
-    YFLAGS  . . . . . . . $YFLAGS
-
-  Libraries:
-    intel mic . . . . . . $with_mic
-    libaquaero5 . . . . . $with_libaquaero5
-    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
-    libmodbus . . . . . . $with_libmodbus
-    libmongoc . . . . . . $with_libmongoc
-    libmosquitto  . . . . $with_libmosquitto
-    libmysql  . . . . . . $with_libmysql
-    libnetapp . . . . . . $with_libnetapp
-    libnetsnmp  . . . . . $with_libnetsnmp
-    libnotify . . . . . . $with_libnotify
-    liboconfig  . . . . . $with_liboconfig
-    libopenipmi . . . . . $with_libopenipmipthread
-    liboping  . . . . . . $with_liboping
-    libowcapi . . . . . . $with_libowcapi
-    libpcap . . . . . . . $with_libpcap
-    libperfstat . . . . . $with_perfstat
-    libperl . . . . . . . $with_libperl
-    libpq . . . . . . . . $with_libpq
-    libpthread  . . . . . $with_libpthread
-    librabbitmq . . . . . $with_librabbitmq
-    libriemann-client . . $with_libriemann_client
-    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
-    libxenctrl  . . . . . $with_libxenctrl
-    libxml2 . . . . . . . $with_libxml2
-    libxmms . . . . . . . $with_libxmms
-    libyajl . . . . . . . $with_libyajl
-    oracle  . . . . . . . $with_oracle
-    protobuf-c  . . . . . $have_protoc_c
-    python  . . . . . . . $with_python
-
-  Features:
-    daemon mode . . . . . $enable_daemon
-    debug . . . . . . . . $enable_debug
-
-  Bindings:
-    perl  . . . . . . . . $with_perl_bindings
-
-  Modules:
-    aggregation . . . . . $enable_aggregation
-    amqp    . . . . . . . $enable_amqp
-    apache  . . . . . . . $enable_apache
-    apcups  . . . . . . . $enable_apcups
-    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
-    cpu . . . . . . . . . $enable_cpu
-    cpufreq . . . . . . . $enable_cpufreq
-    cpusleep  . . . . . . $enable_cpusleep
-    csv . . . . . . . . . $enable_csv
-    curl  . . . . . . . . $enable_curl
-    curl_json . . . . . . $enable_curl_json
-    curl_xml  . . . . . . $enable_curl_xml
-    dbi . . . . . . . . . $enable_dbi
-    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
-    load  . . . . . . . . $enable_load
-    logfile . . . . . . . $enable_logfile
-    log_logstash  . . . . $enable_log_logstash
-    lpar  . . . . . . . . $enable_lpar
-    lvm . . . . . . . . . $enable_lvm
-    madwifi . . . . . . . $enable_madwifi
-    match_empty_counter . $enable_match_empty_counter
-    match_hashed  . . . . $enable_match_hashed
-    match_regex . . . . . $enable_match_regex
-    match_timediff  . . . $enable_match_timediff
-    match_value . . . . . $enable_match_value
-    mbmon . . . . . . . . $enable_mbmon
-    md  . . . . . . . . . $enable_md
-    memcachec . . . . . . $enable_memcachec
-    memcached . . . . . . $enable_memcached
-    memory  . . . . . . . $enable_memory
-    mic . . . . . . . . . $enable_mic
-    modbus  . . . . . . . $enable_modbus
-    mqtt  . . . . . . . . $enable_mqtt
-    multimeter  . . . . . $enable_multimeter
-    mysql . . . . . . . . $enable_mysql
-    netapp  . . . . . . . $enable_netapp
-    netlink . . . . . . . $enable_netlink
-    network . . . . . . . $enable_network
-    nfs . . . . . . . . . $enable_nfs
-    nginx . . . . . . . . $enable_nginx
-    notify_desktop  . . . $enable_notify_desktop
-    notify_email  . . . . $enable_notify_email
-    notify_nagios . . . . $enable_notify_nagios
-    ntpd  . . . . . . . . $enable_ntpd
-    numa  . . . . . . . . $enable_numa
-    nut . . . . . . . . . $enable_nut
-    olsrd . . . . . . . . $enable_olsrd
-    onewire . . . . . . . $enable_onewire
-    openldap  . . . . . . $enable_openldap
-    openvpn . . . . . . . $enable_openvpn
-    oracle  . . . . . . . $enable_oracle
-    perl  . . . . . . . . $enable_perl
-    pf  . . . . . . . . . $enable_pf
-    pinba . . . . . . . . $enable_pinba
-    ping  . . . . . . . . $enable_ping
-    postgresql  . . . . . $enable_postgresql
-    powerdns  . . . . . . $enable_powerdns
-    processes . . . . . . $enable_processes
-    protocols . . . . . . $enable_protocols
-    python  . . . . . . . $enable_python
-    redis . . . . . . . . $enable_redis
-    routeros  . . . . . . $enable_routeros
-    rrdcached . . . . . . $enable_rrdcached
-    rrdtool . . . . . . . $enable_rrdtool
-    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_csv  . . . . . . $enable_tail_csv
-    tail  . . . . . . . . $enable_tail
-    tape  . . . . . . . . $enable_tape
-    target_notification . $enable_target_notification
-    target_replace  . . . $enable_target_replace
-    target_scale  . . . . $enable_target_scale
-    target_set  . . . . . $enable_target_set
-    target_v5upgrade  . . $enable_target_v5upgrade
-    tcpconns  . . . . . . $enable_tcpconns
-    teamspeak2  . . . . . $enable_teamspeak2
-    ted . . . . . . . . . $enable_ted
-    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
-    xencpu  . . . . . . . $enable_xencpu
-    xmms  . . . . . . . . $enable_xmms
-    zfs_arc . . . . . . . $enable_zfs_arc
-    zone  . . . . . . . . $enable_zone
-    zookeeper . . . . . . $enable_zookeeper
-
-EOF
+AC_MSG_RESULT()
+AC_MSG_RESULT([Configuration:])
+AC_MSG_RESULT([  Build:])
+AC_MSG_RESULT([    Platform  . . . . . . $ac_system])
+AC_MSG_RESULT([    CC  . . . . . . . . . $CC])
+AC_MSG_RESULT([    CFLAGS  . . . . . . . $AM_CFLAGS $CFLAGS])
+AC_MSG_RESULT([    CXXFLAGS  . . . . . . $AM_CXXFLAGS $CXXFLAGS])
+AC_MSG_RESULT([    CPP . . . . . . . . . $CPP])
+AC_MSG_RESULT([    CPPFLAGS  . . . . . . $CPPFLAGS])
+AC_MSG_RESULT([    GRPC_CPP_PLUGIN . . . $GRPC_CPP_PLUGIN])
+AC_MSG_RESULT([    LD  . . . . . . . . . $LD])
+AC_MSG_RESULT([    LDFLAGS . . . . . . . $LDFLAGS])
+AC_MSG_RESULT([    PROTOC  . . . . . . . $PROTOC])
+AC_MSG_RESULT([    YACC  . . . . . . . . $YACC])
+AC_MSG_RESULT([    YFLAGS  . . . . . . . $YFLAGS])
+AC_MSG_RESULT()
+AC_MSG_RESULT([  Libraries:])
+AC_MSG_RESULT([    intel mic . . . . . . $with_mic])
+AC_MSG_RESULT([    libaquaero5 . . . . . $with_libaquaero5])
+AC_MSG_RESULT([    libatasmart . . . . . $with_libatasmart])
+AC_MSG_RESULT([    libcurl . . . . . . . $with_libcurl])
+AC_MSG_RESULT([    libdbi  . . . . . . . $with_libdbi])
+AC_MSG_RESULT([    libesmtp  . . . . . . $with_libesmtp])
+AC_MSG_RESULT([    libganglia  . . . . . $with_libganglia])
+AC_MSG_RESULT([    libgcrypt . . . . . . $with_libgcrypt])
+AC_MSG_RESULT([    libgrpc . . . . . . . $with_libgrpc])
+AC_MSG_RESULT([    libhal  . . . . . . . $with_libhal])
+AC_MSG_RESULT([    libhiredis  . . . . . $with_libhiredis])
+AC_MSG_RESULT([    libi2c-dev  . . . . . $with_libi2c])
+AC_MSG_RESULT([    libiokit  . . . . . . $with_libiokit])
+AC_MSG_RESULT([    libiptc . . . . . . . $with_libiptc])
+AC_MSG_RESULT([    libjvm  . . . . . . . $with_java])
+AC_MSG_RESULT([    libkstat  . . . . . . $with_kstat])
+AC_MSG_RESULT([    libkvm  . . . . . . . $with_libkvm])
+AC_MSG_RESULT([    libldap . . . . . . . $with_libldap])
+AC_MSG_RESULT([    liblvm2app  . . . . . $with_liblvm2app])
+AC_MSG_RESULT([    libmemcached  . . . . $with_libmemcached])
+AC_MSG_RESULT([    libmnl  . . . . . . . $with_libmnl])
+AC_MSG_RESULT([    libmodbus . . . . . . $with_libmodbus])
+AC_MSG_RESULT([    libmongoc . . . . . . $with_libmongoc])
+AC_MSG_RESULT([    libmosquitto  . . . . $with_libmosquitto])
+AC_MSG_RESULT([    libmysql  . . . . . . $with_libmysql])
+AC_MSG_RESULT([    libnetapp . . . . . . $with_libnetapp])
+AC_MSG_RESULT([    libnetsnmp  . . . . . $with_libnetsnmp])
+AC_MSG_RESULT([    libnotify . . . . . . $with_libnotify])
+AC_MSG_RESULT([    liboconfig  . . . . . $with_liboconfig])
+AC_MSG_RESULT([    libopenipmi . . . . . $with_libopenipmipthread])
+AC_MSG_RESULT([    liboping  . . . . . . $with_liboping])
+AC_MSG_RESULT([    libowcapi . . . . . . $with_libowcapi])
+AC_MSG_RESULT([    libpcap . . . . . . . $with_libpcap])
+AC_MSG_RESULT([    libperfstat . . . . . $with_perfstat])
+AC_MSG_RESULT([    libperl . . . . . . . $with_libperl])
+AC_MSG_RESULT([    libpq . . . . . . . . $with_libpq])
+AC_MSG_RESULT([    librabbitmq . . . . . $with_librabbitmq])
+AC_MSG_RESULT([    libriemann-client . . $with_libriemann_client])
+AC_MSG_RESULT([    librdkafka  . . . . . $with_librdkafka])
+AC_MSG_RESULT([    librouteros . . . . . $with_librouteros])
+AC_MSG_RESULT([    librrd  . . . . . . . $with_librrd])
+AC_MSG_RESULT([    libsensors  . . . . . $with_libsensors])
+AC_MSG_RESULT([    libsigrok   . . . . . $with_libsigrok])
+AC_MSG_RESULT([    libstatgrab . . . . . $with_libstatgrab])
+AC_MSG_RESULT([    libtokyotyrant  . . . $with_libtokyotyrant])
+AC_MSG_RESULT([    libudev . . . . . . . $with_libudev])
+AC_MSG_RESULT([    libupsclient  . . . . $with_libupsclient])
+AC_MSG_RESULT([    libvarnish  . . . . . $with_libvarnish])
+AC_MSG_RESULT([    libvirt . . . . . . . $with_libvirt])
+AC_MSG_RESULT([    libxenctrl  . . . . . $with_libxenctrl])
+AC_MSG_RESULT([    libxml2 . . . . . . . $with_libxml2])
+AC_MSG_RESULT([    libxmms . . . . . . . $with_libxmms])
+AC_MSG_RESULT([    libyajl . . . . . . . $with_libyajl])
+AC_MSG_RESULT([    oracle  . . . . . . . $with_oracle])
+AC_MSG_RESULT([    protobuf-c  . . . . . $have_protoc_c])
+AC_MSG_RESULT([    protoc 3  . . . . . . $protoc3])
+AC_MSG_RESULT([    python  . . . . . . . $with_python])
+AC_MSG_RESULT()
+AC_MSG_RESULT([  Features:])
+AC_MSG_RESULT([    daemon mode . . . . . $enable_daemon])
+AC_MSG_RESULT([    debug . . . . . . . . $enable_debug])
+AC_MSG_RESULT()
+AC_MSG_RESULT([  Bindings:])
+AC_MSG_RESULT([    perl  . . . . . . . . $with_perl_bindings])
+AC_MSG_RESULT()
+AC_MSG_RESULT([  Modules:])
+AC_MSG_RESULT([    aggregation . . . . . $enable_aggregation])
+AC_MSG_RESULT([    amqp    . . . . . . . $enable_amqp])
+AC_MSG_RESULT([    apache  . . . . . . . $enable_apache])
+AC_MSG_RESULT([    apcups  . . . . . . . $enable_apcups])
+AC_MSG_RESULT([    apple_sensors . . . . $enable_apple_sensors])
+AC_MSG_RESULT([    aquaero . . . . . . . $enable_aquaero])
+AC_MSG_RESULT([    ascent  . . . . . . . $enable_ascent])
+AC_MSG_RESULT([    barometer . . . . . . $enable_barometer])
+AC_MSG_RESULT([    battery . . . . . . . $enable_battery])
+AC_MSG_RESULT([    bind  . . . . . . . . $enable_bind])
+AC_MSG_RESULT([    ceph  . . . . . . . . $enable_ceph])
+AC_MSG_RESULT([    cgroups . . . . . . . $enable_cgroups])
+AC_MSG_RESULT([    chrony. . . . . . . . $enable_chrony])
+AC_MSG_RESULT([    conntrack . . . . . . $enable_conntrack])
+AC_MSG_RESULT([    contextswitch . . . . $enable_contextswitch])
+AC_MSG_RESULT([    cpu . . . . . . . . . $enable_cpu])
+AC_MSG_RESULT([    cpufreq . . . . . . . $enable_cpufreq])
+AC_MSG_RESULT([    cpusleep  . . . . . . $enable_cpusleep])
+AC_MSG_RESULT([    csv . . . . . . . . . $enable_csv])
+AC_MSG_RESULT([    curl  . . . . . . . . $enable_curl])
+AC_MSG_RESULT([    curl_json . . . . . . $enable_curl_json])
+AC_MSG_RESULT([    curl_xml  . . . . . . $enable_curl_xml])
+AC_MSG_RESULT([    dbi . . . . . . . . . $enable_dbi])
+AC_MSG_RESULT([    df  . . . . . . . . . $enable_df])
+AC_MSG_RESULT([    disk  . . . . . . . . $enable_disk])
+AC_MSG_RESULT([    dns . . . . . . . . . $enable_dns])
+AC_MSG_RESULT([    drbd  . . . . . . . . $enable_drbd])
+AC_MSG_RESULT([    email . . . . . . . . $enable_email])
+AC_MSG_RESULT([    entropy . . . . . . . $enable_entropy])
+AC_MSG_RESULT([    ethstat . . . . . . . $enable_ethstat])
+AC_MSG_RESULT([    exec  . . . . . . . . $enable_exec])
+AC_MSG_RESULT([    fhcount . . . . . . . $enable_fhcount])
+AC_MSG_RESULT([    filecount . . . . . . $enable_filecount])
+AC_MSG_RESULT([    fscache . . . . . . . $enable_fscache])
+AC_MSG_RESULT([    gmond . . . . . . . . $enable_gmond])
+AC_MSG_RESULT([    grpc  . . . . . . . . $enable_grpc])
+AC_MSG_RESULT([    hddtemp . . . . . . . $enable_hddtemp])
+AC_MSG_RESULT([    interface . . . . . . $enable_interface])
+AC_MSG_RESULT([    ipc . . . . . . . . . $enable_ipc])
+AC_MSG_RESULT([    ipmi  . . . . . . . . $enable_ipmi])
+AC_MSG_RESULT([    iptables  . . . . . . $enable_iptables])
+AC_MSG_RESULT([    ipvs  . . . . . . . . $enable_ipvs])
+AC_MSG_RESULT([    irq . . . . . . . . . $enable_irq])
+AC_MSG_RESULT([    java  . . . . . . . . $enable_java])
+AC_MSG_RESULT([    load  . . . . . . . . $enable_load])
+AC_MSG_RESULT([    logfile . . . . . . . $enable_logfile])
+AC_MSG_RESULT([    log_logstash  . . . . $enable_log_logstash])
+AC_MSG_RESULT([    lpar  . . . . . . . . $enable_lpar])
+AC_MSG_RESULT([    lvm . . . . . . . . . $enable_lvm])
+AC_MSG_RESULT([    madwifi . . . . . . . $enable_madwifi])
+AC_MSG_RESULT([    match_empty_counter . $enable_match_empty_counter])
+AC_MSG_RESULT([    match_hashed  . . . . $enable_match_hashed])
+AC_MSG_RESULT([    match_regex . . . . . $enable_match_regex])
+AC_MSG_RESULT([    match_timediff  . . . $enable_match_timediff])
+AC_MSG_RESULT([    match_value . . . . . $enable_match_value])
+AC_MSG_RESULT([    mbmon . . . . . . . . $enable_mbmon])
+AC_MSG_RESULT([    md  . . . . . . . . . $enable_md])
+AC_MSG_RESULT([    memcachec . . . . . . $enable_memcachec])
+AC_MSG_RESULT([    memcached . . . . . . $enable_memcached])
+AC_MSG_RESULT([    memory  . . . . . . . $enable_memory])
+AC_MSG_RESULT([    mic . . . . . . . . . $enable_mic])
+AC_MSG_RESULT([    modbus  . . . . . . . $enable_modbus])
+AC_MSG_RESULT([    mqtt  . . . . . . . . $enable_mqtt])
+AC_MSG_RESULT([    multimeter  . . . . . $enable_multimeter])
+AC_MSG_RESULT([    mysql . . . . . . . . $enable_mysql])
+AC_MSG_RESULT([    netapp  . . . . . . . $enable_netapp])
+AC_MSG_RESULT([    netlink . . . . . . . $enable_netlink])
+AC_MSG_RESULT([    network . . . . . . . $enable_network])
+AC_MSG_RESULT([    nfs . . . . . . . . . $enable_nfs])
+AC_MSG_RESULT([    nginx . . . . . . . . $enable_nginx])
+AC_MSG_RESULT([    notify_desktop  . . . $enable_notify_desktop])
+AC_MSG_RESULT([    notify_email  . . . . $enable_notify_email])
+AC_MSG_RESULT([    notify_nagios . . . . $enable_notify_nagios])
+AC_MSG_RESULT([    ntpd  . . . . . . . . $enable_ntpd])
+AC_MSG_RESULT([    numa  . . . . . . . . $enable_numa])
+AC_MSG_RESULT([    nut . . . . . . . . . $enable_nut])
+AC_MSG_RESULT([    olsrd . . . . . . . . $enable_olsrd])
+AC_MSG_RESULT([    onewire . . . . . . . $enable_onewire])
+AC_MSG_RESULT([    openldap  . . . . . . $enable_openldap])
+AC_MSG_RESULT([    openvpn . . . . . . . $enable_openvpn])
+AC_MSG_RESULT([    oracle  . . . . . . . $enable_oracle])
+AC_MSG_RESULT([    perl  . . . . . . . . $enable_perl])
+AC_MSG_RESULT([    pf  . . . . . . . . . $enable_pf])
+AC_MSG_RESULT([    pinba . . . . . . . . $enable_pinba])
+AC_MSG_RESULT([    ping  . . . . . . . . $enable_ping])
+AC_MSG_RESULT([    postgresql  . . . . . $enable_postgresql])
+AC_MSG_RESULT([    powerdns  . . . . . . $enable_powerdns])
+AC_MSG_RESULT([    processes . . . . . . $enable_processes])
+AC_MSG_RESULT([    protocols . . . . . . $enable_protocols])
+AC_MSG_RESULT([    python  . . . . . . . $enable_python])
+AC_MSG_RESULT([    redis . . . . . . . . $enable_redis])
+AC_MSG_RESULT([    routeros  . . . . . . $enable_routeros])
+AC_MSG_RESULT([    rrdcached . . . . . . $enable_rrdcached])
+AC_MSG_RESULT([    rrdtool . . . . . . . $enable_rrdtool])
+AC_MSG_RESULT([    sensors . . . . . . . $enable_sensors])
+AC_MSG_RESULT([    serial  . . . . . . . $enable_serial])
+AC_MSG_RESULT([    sigrok  . . . . . . . $enable_sigrok])
+AC_MSG_RESULT([    smart . . . . . . . . $enable_smart])
+AC_MSG_RESULT([    snmp  . . . . . . . . $enable_snmp])
+AC_MSG_RESULT([    statsd  . . . . . . . $enable_statsd])
+AC_MSG_RESULT([    swap  . . . . . . . . $enable_swap])
+AC_MSG_RESULT([    syslog  . . . . . . . $enable_syslog])
+AC_MSG_RESULT([    table . . . . . . . . $enable_table])
+AC_MSG_RESULT([    tail_csv  . . . . . . $enable_tail_csv])
+AC_MSG_RESULT([    tail  . . . . . . . . $enable_tail])
+AC_MSG_RESULT([    tape  . . . . . . . . $enable_tape])
+AC_MSG_RESULT([    target_notification . $enable_target_notification])
+AC_MSG_RESULT([    target_replace  . . . $enable_target_replace])
+AC_MSG_RESULT([    target_scale  . . . . $enable_target_scale])
+AC_MSG_RESULT([    target_set  . . . . . $enable_target_set])
+AC_MSG_RESULT([    target_v5upgrade  . . $enable_target_v5upgrade])
+AC_MSG_RESULT([    tcpconns  . . . . . . $enable_tcpconns])
+AC_MSG_RESULT([    teamspeak2  . . . . . $enable_teamspeak2])
+AC_MSG_RESULT([    ted . . . . . . . . . $enable_ted])
+AC_MSG_RESULT([    thermal . . . . . . . $enable_thermal])
+AC_MSG_RESULT([    threshold . . . . . . $enable_threshold])
+AC_MSG_RESULT([    tokyotyrant . . . . . $enable_tokyotyrant])
+AC_MSG_RESULT([    turbostat . . . . . . $enable_turbostat])
+AC_MSG_RESULT([    unixsock  . . . . . . $enable_unixsock])
+AC_MSG_RESULT([    uptime  . . . . . . . $enable_uptime])
+AC_MSG_RESULT([    users . . . . . . . . $enable_users])
+AC_MSG_RESULT([    uuid  . . . . . . . . $enable_uuid])
+AC_MSG_RESULT([    varnish . . . . . . . $enable_varnish])
+AC_MSG_RESULT([    virt  . . . . . . . . $enable_virt])
+AC_MSG_RESULT([    vmem  . . . . . . . . $enable_vmem])
+AC_MSG_RESULT([    vserver . . . . . . . $enable_vserver])
+AC_MSG_RESULT([    wireless  . . . . . . $enable_wireless])
+AC_MSG_RESULT([    write_graphite  . . . $enable_write_graphite])
+AC_MSG_RESULT([    write_http  . . . . . $enable_write_http])
+AC_MSG_RESULT([    write_kafka . . . . . $enable_write_kafka])
+AC_MSG_RESULT([    write_log . . . . . . $enable_write_log])
+AC_MSG_RESULT([    write_mongodb . . . . $enable_write_mongodb])
+AC_MSG_RESULT([    write_redis . . . . . $enable_write_redis])
+AC_MSG_RESULT([    write_riemann . . . . $enable_write_riemann])
+AC_MSG_RESULT([    write_sensu . . . . . $enable_write_sensu])
+AC_MSG_RESULT([    write_tsdb  . . . . . $enable_write_tsdb])
+AC_MSG_RESULT([    xencpu  . . . . . . . $enable_xencpu])
+AC_MSG_RESULT([    xmms  . . . . . . . . $enable_xmms])
+AC_MSG_RESULT([    zfs_arc . . . . . . . $enable_zfs_arc])
+AC_MSG_RESULT([    zone  . . . . . . . . $enable_zone])
+AC_MSG_RESULT([    zookeeper . . . . . . $enable_zookeeper])
+AC_MSG_RESULT()
 
 if test "x$dependency_error" = "xyes"; then
        AC_MSG_ERROR("Some plugins are missing dependencies - see the summary above for details")
index b3fe2bf..bf9b320 100644 (file)
 %global _hardened_build 1
 %{?perl_default_filter}
 
-# plugins only buildable on RHEL6
-# (NB: %{elN} macro is not available on RHEL < 6)
-%{?el6:%global _has_libyajl 1}
-%{?el6:%global _has_recent_libpcap 1}
-%{?el6:%global _has_recent_sockios_h 1}
-%{?el6:%global _has_recent_libganglia 1}
-%{?el6:%global _has_working_libiptc 1}
-%{?el6:%global _has_ip_vs_h 1}
-%{?el6:%global _has_lvm2app_h 1}
-%{?el6:%global _has_libmodbus 1}
-%{?el6:%global _has_libudev 1}
-%{?el6:%global _has_iproute 1}
-%{?el6:%global _has_atasmart 1}
-%{?el6:%global _has_hiredis 1}
-%{?el6:%global _has_asm_msr_index 1}
-
-%{?el7:%global _has_libyajl 1}
-%{?el7:%global _has_recent_libpcap 1}
-%{?el7:%global _has_recent_sockios_h 1}
-%{?el7:%global _has_working_libiptc 1}
-%{?el7:%global _has_ip_vs_h 1}
-%{?el7:%global _has_lvm2app_h 1}
-%{?el7:%global _has_libudev 1}
-%{?el7:%global _has_recent_librrd 1}
-%{?el7:%global _has_iproute 1}
-%{?el7:%global _has_atasmart 1}
-%{?el7:%global _has_hiredis 1}
-%{?el7:%global _has_asm_msr_index 1}
-%{?el7:%global _has_libmosquitto 1}
-%{?el7:%global _has_libmodbus 1}
-%{?el7:%global _has_xmms 1}
-
 # plugins enabled by default
 %define with_aggregation 0%{!?_without_aggregation:1}
 %define with_amqp 0%{!?_without_amqp:1}
 %define with_ascent 0%{!?_without_ascent:1}
 %define with_battery 0%{!?_without_battery:1}
 %define with_bind 0%{!?_without_bind:1}
-%define with_ceph 0%{!?_without_ceph:0%{?_has_libyajl}}
+%define with_ceph 0%{!?_without_ceph:1}
 %define with_cgroups 0%{!?_without_cgroups:1}
+%define with_chrony 0%{!?_without_chrony:1}
 %define with_conntrack 0%{!?_without_conntrack:1}
 %define with_contextswitch 0%{!?_without_contextswitch:1}
 %define with_cpu 0%{!?_without_cpu:1}
 %define with_cpufreq 0%{!?_without_cpufreq:1}
 %define with_csv 0%{!?_without_csv:1}
 %define with_curl 0%{!?_without_curl:1}
-%define with_curl_json 0%{!?_without_curl_json:0%{?_has_libyajl}}
+%define with_curl_json 0%{!?_without_curl_json:1}
 %define with_curl_xml 0%{!?_without_curl_xml:1}
 %define with_dbi 0%{!?_without_dbi:1}
 %define with_df 0%{!?_without_df:1}
 %define with_disk 0%{!?_without_disk:1}
-%define with_dns 0%{!?_without_dns:0%{?_has_recent_libpcap}}
+%define with_dns 0%{!?_without_dns:1}
 %define with_drbd 0%{!?_without_drbd:1}
 %define with_email 0%{!?_without_email:1}
 %define with_entropy 0%{!?_without_entropy:1}
-%define with_ethstat 0%{!?_without_ethstat:0%{?_has_recent_sockios_h}}
+%define with_ethstat 0%{!?_without_ethstat:1}
 %define with_exec 0%{!?_without_exec:1}
 %define with_fhcount 0%{!?_without_fhcount:1}
 %define with_filecount 0%{!?_without_filecount:1}
 %define with_fscache 0%{!?_without_fscache:1}
-%define with_gmond 0%{!?_without_gmond:0%{?_has_recent_libganglia}}
+%define with_gmond 0%{!?_without_gmond:1}
 %define with_hddtemp 0%{!?_without_hddtemp:1}
 %define with_interface 0%{!?_without_interface:1}
 %define with_ipc 0%{!?_without_ipc:1}
 %define with_ipmi 0%{!?_without_ipmi:1}
-%define with_iptables 0%{!?_without_iptables:0%{?_has_working_libiptc}}
-%define with_ipvs 0%{!?_without_ipvs:0%{?_has_ip_vs_h}}
+%define with_iptables 0%{!?_without_iptables:1}
+%define with_ipvs 0%{!?_without_ipvs:1}
 %define with_irq 0%{!?_without_irq:1}
 %define with_java 0%{!?_without_java:1}
-%define with_virt 0%{!?_without_virt:1}
 %define with_load 0%{!?_without_load:1}
+%define with_log_logstash 0%{!?_without_log_logstash:1}
 %define with_logfile 0%{!?_without_logfile:1}
-%define with_log_logstash 0%{!?_without_log_logstash:0%{?_has_libyajl}}
-%define with_lvm 0%{!?_without_lvm:0%{?_has_lvm2app_h}}
+%define with_lvm 0%{!?_without_lvm:1}
 %define with_madwifi 0%{!?_without_madwifi:1}
 %define with_mbmon 0%{!?_without_mbmon:1}
 %define with_md 0%{!?_without_md:1}
 %define with_memcachec 0%{!?_without_memcachec:1}
 %define with_memcached 0%{!?_without_memcached:1}
 %define with_memory 0%{!?_without_memory:1}
+%define with_modbus 0%{!?_without_modbus:1}
+%define with_mqtt 0%{!?_without_mqtt:1}
 %define with_multimeter 0%{!?_without_multimeter:1}
-%define with_modbus 0%{!?_without_modbus:0%{?_has_libmodbus}}
-%define with_mqtt 0%{!?_without_mqtt:0%{?_has_libmosquitto}}
 %define with_mysql 0%{!?_without_mysql:1}
-%define with_netlink 0%{!?_without_netlink:0%{?_has_iproute}}
+%define with_netlink 0%{!?_without_netlink:1}
 %define with_network 0%{!?_without_network:1}
 %define with_nfs 0%{!?_without_nfs:1}
 %define with_nginx 0%{!?_without_nginx:1}
 %define with_processes 0%{!?_without_processes:1}
 %define with_protocols 0%{!?_without_protocols:1}
 %define with_python 0%{!?_without_python:1}
-%define with_redis 0%{!?_without_redis:0%{?_has_hiredis}}
-%define with_rrdcached 0%{!?_without_rrdcached:0%{?_has_recent_librrd}}
+%define with_redis 0%{!?_without_redis:1}
+%define with_rrdcached 0%{!?_without_rrdcached:1}
 %define with_rrdtool 0%{!?_without_rrdtool:1}
 %define with_sensors 0%{!?_without_sensors:1}
 %define with_serial 0%{!?_without_serial:1}
-%define with_smart 0%{!?_without_smart:0%{?_has_atasmart}}
+%define with_smart 0%{!?_without_smart:1}
 %define with_snmp 0%{!?_without_snmp:1}
 %define with_statsd 0%{!?_without_statsd:1}
 %define with_swap 0%{!?_without_swap:1}
 %define with_ted 0%{!?_without_ted:1}
 %define with_thermal 0%{!?_without_thermal:1}
 %define with_threshold 0%{!?_without_threshold:1}
-%define with_turbostat 0%{!?_without_turbostat:0%{?_has_asm_msr_index}}
+%define with_turbostat 0%{!?_without_turbostat:1}
 %define with_unixsock 0%{!?_without_unixsock:1}
 %define with_uptime 0%{!?_without_uptime:1}
 %define with_users 0%{!?_without_users:1}
 %define with_uuid 0%{!?_without_uuid:1}
 %define with_varnish 0%{!?_without_varnish:1}
+%define with_virt 0%{!?_without_virt:1}
 %define with_vmem 0%{!?_without_vmem:1}
 %define with_vserver 0%{!?_without_vserver:1}
 %define with_wireless 0%{!?_without_wireless:1}
 %define with_write_graphite 0%{!?_without_write_graphite:1}
 %define with_write_http 0%{!?_without_write_http:1}
 %define with_write_log 0%{!?_without_write_log:1}
-%define with_write_redis 0%{!?_without_write_redis:0%{?_has_hiredis}}
-%define with_write_riemann 0%{!?_without_write_riemann:0%{?_has_recent_riemann_c_client}}
+%define with_write_redis 0%{!?_without_write_redis:1}
 %define with_write_sensu 0%{!?_without_write_sensu:1}
 %define with_write_tsdb 0%{!?_without_write_tsdb:1}
 %define with_xmms 0%{!?_without_xmms:0%{?_has_xmms}}
 %define with_aquaero 0%{!?_without_aquaero:0}
 # plugin barometer disabled, requires a libi2c
 %define with_barometer 0%{!?_without_barometer:0}
+# plugin grpc disabled, requires protobuf-compiler >= 3.0
+%define with_grpc 0%{!?_without_grpc:0}
 # plugin lpar disabled, requires AIX
 %define with_lpar 0%{!?_without_lpar:0}
 # plugin mic disabled, requires Mic
 %define with_write_kafka 0%{!?_without_write_kafka:0}
 # plugin write_mongodb disabled, requires libmongoc
 %define with_write_mongodb 0%{!?_without_write_mongodb:0}
+# plugin write_riemann disabled, requires a new enough riemann_c_client
+%define with_write_riemann 0%{!?_without_write_riemann:0}
 # plugin xencpu disabled, requires xen-devel from non-default repo
 %define with_xencpu 0%{!?_without_xencpu:0}
 # plugin zone disabled, requires Solaris
 %define with_zone 0%{!?_without_zone:0}
 
-Summary:       statistics collection and monitoring daemon
+# Plugins not buildable on RHEL < 6
+%if 0%{?rhel} && 0%{?rhel} < 6
+%define with_ceph 0
+%define with_curl_json 0
+%define with_log_logstash 0
+%define with_dns 0
+%define with_ethstat 0
+%define with_gmond 0
+%define with_iptables 0
+%define with_ipvs 0
+%define with_lvm 0
+%define with_modbus 0
+%define with_netlink 0
+%define with_redis 0
+%define with_smart 0
+%define with_turbostat 0
+%define with_write_redis 0
+%endif
+
+# Plugins not buildable on RHEL < 7
+%if 0%{?rhel} && 0%{?rhel} < 7
+%define with_mqtt 0
+%define with_rrdcached 0
+%define with_xmms 0
+%endif
+
+Summary:       Statistics collection and monitoring daemon
 Name:          collectd
 Version:       5.5.1
 Release:       1%{?dist}
@@ -233,11 +231,9 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-root
 BuildRequires: libgcrypt-devel, kernel-headers, libtool-ltdl-devel, libcap-devel
 Vendor:                collectd development team <collectd@verplant.org>
 
-%if 0%{?el7:1}
-Requires(pre):         initscripts
-Requires(post):                systemd
-Requires(preun):       systemd
-Requires(postun):      systemd
+%if 0%{?fedora} || 0%{?rhel} >= 7
+%{?systemd_requires}
+BuildRequires:         systemd
 %else
 Requires(post):                chkconfig
 Requires(preun):       chkconfig, initscripts
@@ -324,6 +320,15 @@ BuildRequires: yajl-devel
 Ceph plugin for collectd
 %endif
 
+%if %{with_chrony}
+%package chrony
+Summary:       Chrony plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+%description chrony
+Chrony plugin for collectd
+%endif
+
 %if %{with_curl}
 %package curl
 Summary:       Curl plugin for collectd
@@ -411,6 +416,16 @@ The gmond plugin subscribes to a Multicast group to receive data from gmond,
 the client daemon of the Ganglia project.
 %endif
 
+%if %{with_grpc}
+%package grpc
+Summary:       GRPC plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: protobuf-compiler
+%description grpc
+This plugin embeds a gRPC server into Collectd.
+%endif
+
 %if %{with_hddtemp}
 %package hddtemp
 Summary:       Hddtemp plugin for collectd
@@ -598,10 +613,10 @@ Summary:  Perl plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
 Requires:      perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
-       %if 0%{?rhel} >= 6
-BuildRequires: perl-ExtUtils-Embed
-       %else
+       %if 0%{?rhel} && 0%{?rhel} < 6
 BuildRequires: perl
+       %else
+BuildRequires: perl-ExtUtils-Embed
        %endif
 %description perl
 The Perl plugin embeds a Perl interpreter into collectd and exposes the
@@ -646,10 +661,10 @@ database.
 Summary:       Python plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
-       %if 0%{?rhel} >= 6
-BuildRequires: python-devel
-       %else
+       %if 0%{?rhel} && 0%{?rhel} < 6
 BuildRequires: python26-devel
+       %else
+BuildRequires: python-devel
        %endif
 %description python
 The Python plugin embeds a Python interpreter into collectd and exposes the
@@ -766,7 +781,7 @@ using HTTP POST requests.
 Summary:       Write-kafka plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
-BuildRequires: rdkafka-devel
+BuildRequires: librdkafka-devel
 %description write_kafka
 The write_kafka plugin sends values to kafka, a distributed messaging system.
 %endif
@@ -931,6 +946,12 @@ Collectd utilities
 %define _with_cgroups --disable-cgroups
 %endif
 
+%if %{with_chrony}
+%define _with_chrony --enable-chrony
+%else
+%define _with_chrony --disable-chrony
+%endif
+
 %if %{with_conntrack}
 %define _with_conntrack --enable-conntrack
 %else
@@ -1063,6 +1084,12 @@ Collectd utilities
 %define _with_gmond --disable-gmond
 %endif
 
+%if %{with_grpc}
+%define _with_grpc --enable-grpc
+%else
+%define _with_grpc --disable-grpc
+%endif
+
 %if %{with_hddtemp}
 %define _with_hddtemp --enable-hddtemp
 %else
@@ -1358,10 +1385,10 @@ Collectd utilities
 %endif
 
 %if %{with_python}
-       %if 0%{?rhel} >= 6
-%define _with_python --enable-python
-       %else
+       %if 0%{?rhel} && 0%{?rhel} < 6
 %define _with_python --enable-python --with-python=%{_bindir}/python2.6
+       %else
+%define _with_python --enable-python
        %endif
 %else
 %define _with_python --disable-python
@@ -1663,14 +1690,15 @@ Collectd utilities
        %{?_with_bind} \
        %{?_with_ceph} \
        %{?_with_cgroups} \
+       %{?_with_chrony} \
        %{?_with_conntrack} \
        %{?_with_contextswitch} \
-       %{?_with_cpu} \
        %{?_with_cpufreq} \
+       %{?_with_cpu} \
        %{?_with_csv} \
-       %{?_with_curl} \
        %{?_with_curl_json} \
        %{?_with_curl_xml} \
+       %{?_with_curl} \
        %{?_with_dbi} \
        %{?_with_df} \
        %{?_with_disk} \
@@ -1684,102 +1712,103 @@ Collectd utilities
        %{?_with_filecount} \
        %{?_with_fscache} \
        %{?_with_gmond} \
+       %{?_with_grpc} \
        %{?_with_hddtemp} \
        %{?_with_interface} \
        %{?_with_ipc} \
        %{?_with_ipmi} \
        %{?_with_iptables} \
        %{?_with_ipvs} \
+       %{?_with_irq} \
        %{?_with_java} \
-       %{?_with_virt} \
+       %{?_with_load} \
        %{?_with_log_logstash} \
+       %{?_with_logfile} \
        %{?_with_lpar} \
        %{?_with_lvm} \
+       %{?_with_madwifi} \
+       %{?_with_mbmon} \
+       %{?_with_md} \
        %{?_with_memcachec} \
+       %{?_with_memcached} \
+       %{?_with_memory} \
        %{?_with_mic} \
        %{?_with_modbus} \
-       %{?_with_multimeter} \
        %{?_with_mqtt} \
+       %{?_with_multimeter} \
        %{?_with_mysql} \
        %{?_with_netapp} \
        %{?_with_netlink} \
+       %{?_with_network} \
+       %{?_with_nfs} \
        %{?_with_nginx} \
        %{?_with_notify_desktop} \
        %{?_with_notify_email} \
+       %{?_with_notify_nagios} \
+       %{?_with_ntpd} \
+       %{?_with_numa} \
        %{?_with_nut} \
+       %{?_with_olsrd} \
        %{?_with_onewire} \
        %{?_with_openldap} \
+       %{?_with_openvpn} \
        %{?_with_oracle} \
        %{?_with_perl} \
        %{?_with_pf} \
        %{?_with_pinba} \
        %{?_with_ping} \
        %{?_with_postgresql} \
+       %{?_with_powerdns} \
+       %{?_with_processes} \
+       %{?_with_protocols} \
        %{?_with_python} \
        %{?_with_redis} \
        %{?_with_routeros} \
        %{?_with_rrdcached} \
        %{?_with_rrdtool} \
        %{?_with_sensors} \
+       %{?_with_serial} \
        %{?_with_sigrok} \
        %{?_with_smart} \
        %{?_with_snmp} \
-       %{?_with_tape} \
-       %{?_with_tokyotyrant} \
-       %{?_with_varnish} \
-       %{?_with_write_http} \
-       %{?_with_write_kafka} \
-       %{?_with_write_mongodb} \
-       %{?_with_write_redis} \
-       %{?_with_xencpu} \
-       %{?_with_xmms} \
-       %{?_with_zfs_arc} \
-       %{?_with_zone} \
-       %{?_with_zookeeper} \
-       %{?_with_irq} \
-       %{?_with_load} \
-       %{?_with_logfile} \
-       %{?_with_madwifi} \
-       %{?_with_mbmon} \
-       %{?_with_md} \
-       %{?_with_memcached} \
-       %{?_with_memory} \
-       %{?_with_network} \
-       %{?_with_nfs} \
-       %{?_with_notify_nagios} \
-       %{?_with_ntpd} \
-       %{?_with_numa} \
-       %{?_with_olsrd} \
-       %{?_with_openvpn} \
-       %{?_with_powerdns} \
-       %{?_with_processes} \
-       %{?_with_protocols} \
-       %{?_with_serial} \
        %{?_with_statsd} \
        %{?_with_swap} \
        %{?_with_syslog} \
        %{?_with_table} \
-       %{?_with_tail} \
        %{?_with_tail_csv} \
+       %{?_with_tail} \
+       %{?_with_tape} \
        %{?_with_tcpconns} \
        %{?_with_teamspeak2} \
        %{?_with_ted} \
        %{?_with_thermal} \
        %{?_with_threshold} \
+       %{?_with_tokyotyrant} \
        %{?_with_turbostat} \
        %{?_with_unixsock} \
        %{?_with_uptime} \
        %{?_with_users} \
        %{?_with_uuid} \
+       %{?_with_varnish} \
+       %{?_with_virt} \
        %{?_with_vmem} \
        %{?_with_vserver} \
        %{?_with_wireless}\
        %{?_with_write_graphite} \
        %{?_with_write_http} \
+       %{?_with_write_http} \
+       %{?_with_write_kafka} \
        %{?_with_write_log} \
+       %{?_with_write_mongodb} \
+       %{?_with_write_redis} \
        %{?_with_write_riemann} \
        %{?_with_write_sensu} \
-       %{?_with_write_tsdb}
+       %{?_with_write_tsdb} \
+       %{?_with_xencpu} \
+       %{?_with_xmms} \
+       %{?_with_zfs_arc} \
+       %{?_with_zone} \
+       %{?_with_zookeeper}
 
 
 %{__make} %{?_smp_mflags}
@@ -1788,7 +1817,7 @@ Collectd utilities
 %install
 rm -rf %{buildroot}
 %{__make} install DESTDIR=%{buildroot}
-%if 0%{?el7:1}
+%if 0%{?fedora} || 0%{?rhel} >= 7
 %{__install} -Dp -m0644 contrib/systemd.collectd.service %{buildroot}%{_unitdir}/collectd.service
 %else
 %{__install} -Dp -m0755 contrib/redhat/init.d-collectd %{buildroot}%{_initrddir}/collectd
@@ -1844,26 +1873,15 @@ rm -f %{buildroot}%{_mandir}/man5/collectd-snmp.5*
 %clean
 rm -rf %{buildroot}
 
-%pre
-%if 0%{?el7:1}
-# stop sysv-based instance before upgrading to systemd
-if [ $1 -eq 2 ] && [ -f /var/lock/subsys/collectd ]; then
-       SYSTEMCTL_SKIP_REDIRECT=1 %{_initddir}/collectd stop >/dev/null 2>&1 || :
-fi
-%endif
-
 %post
-%if 0%{?el7:1}
-if [ $1 -eq 2 ]; then
-       /usr/bin/systemctl daemon-reload >/dev/null 2>&1 || :
-fi
+%if 0%{?fedora} || 0%{?rhel} >= 7
 %systemd_post collectd.service
 %else
 /sbin/chkconfig --add collectd || :
 %endif
 
 %preun
-%if 0%{?el7:1}
+%if 0%{?fedora} || 0%{?rhel} >= 7
 %systemd_preun collectd.service
 %else
 # stop collectd only when uninstalling
@@ -1874,7 +1892,7 @@ fi
 %endif
 
 %postun
-%if 0%{?el7:1}
+%if 0%{?fedora} || 0%{?rhel} >= 7
 %systemd_postun_with_restart collectd.service
 %else
 # restart collectd only when upgrading
@@ -1890,7 +1908,7 @@ fi
 %files
 %doc AUTHORS COPYING ChangeLog README
 %config(noreplace) %{_sysconfdir}/collectd.conf
-%if 0%{?el7:1}
+%if 0%{?fedora} || 0%{?rhel} >= 7
 %{_unitdir}/collectd.service
 %else
 %{_initrddir}/collectd
@@ -2170,6 +2188,11 @@ fi
 %{_libdir}/%{name}/ceph.so
 %endif
 
+%if %{with_chrony}
+%files chrony
+%{_libdir}/%{name}/chrony.so
+%endif
+
 %if %{with_curl}
 %files curl
 %{_libdir}/%{name}/curl.so
@@ -2210,6 +2233,11 @@ fi
 %{_libdir}/%{name}/gmond.so
 %endif
 
+%if %{with_grpc}
+%files grpc
+%{_libdir}/%{name}/grpc.so
+%endif
+
 %if %{with_hddtemp}
 %files hddtemp
 %{_libdir}/%{name}/hddtemp.so
@@ -2417,11 +2445,11 @@ fi
 %doc contrib/
 
 %changelog
-#* TODO: next feature release changelog
-#- New upstream version
-#- New plugins enabled by default: mqtt, notify_nagios
-#- New plugins disabled by default: zone, xencpu
-#
+* Sat Jun 04 2016 Ruben Kerkhof <ruben@rubenkerkhof.com> 5.5.1-1
+- New upstream version
+- New plugins enabled by default: chrony, mqtt, notify_nagios
+- New plugins disabled by default: grpc, zone, xencpu
+
 * Wed May 27 2015 Marc Fournier <marc.fournier@camptocamp.com> 5.5.0-1
 - New upstream version
 - New plugins enabled by default: ceph, drbd, log_logstash, write_tsdb, smart,
diff --git a/proto/Makefile.am b/proto/Makefile.am
new file mode 100644 (file)
index 0000000..3c0bfd7
--- /dev/null
@@ -0,0 +1 @@
+EXTRA_DIST = collectd.proto types.proto
diff --git a/proto/collectd.proto b/proto/collectd.proto
new file mode 100644 (file)
index 0000000..608fcbb
--- /dev/null
@@ -0,0 +1,60 @@
+// collectd - proto/collectd.proto
+// Copyright (C) 2015-2016 Sebastian Harl
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+// Authors:
+//   Sebastian Harl <sh at tokkee.org>
+
+syntax = "proto3";
+
+package collectd;
+
+import "types.proto";
+import "google/protobuf/timestamp.proto";
+
+service Collectd {
+       // Dispatch collected values to collectd.
+       rpc DispatchValues(DispatchValuesRequest) returns (DispatchValuesReply);
+
+       // Query a list of values available from collectd's value cache.
+       rpc QueryValues(QueryValuesRequest) returns (QueryValuesReply);
+}
+
+// The arguments to DispatchValues.
+message DispatchValuesRequest {
+       collectd.types.ValueList values = 1;
+}
+
+// The response from DispatchValues.
+message DispatchValuesReply {
+}
+
+// The arguments to QueryValues.
+message QueryValuesRequest {
+       // Query by the fields of the identifier. Only return values matching the
+       // specified shell wildcard patterns (see fnmatch(3)). Use '*' to match
+       // any value.
+       collectd.types.Identifier identifier = 1;
+}
+
+// The response from QueryValues.
+message QueryValuesReply {
+       repeated collectd.types.ValueList values = 1;
+}
diff --git a/proto/types.proto b/proto/types.proto
new file mode 100644 (file)
index 0000000..4a852e4
--- /dev/null
@@ -0,0 +1,56 @@
+// collectd - proto/types.proto
+// Copyright (C) 2015-2016 Sebastian Harl
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+// Authors:
+//   Sebastian Harl <sh at tokkee.org>
+
+syntax = "proto3";
+
+package collectd.types;
+
+import "google/protobuf/duration.proto";
+import "google/protobuf/timestamp.proto";
+
+message Identifier {
+       string host = 1;
+       string plugin = 2;
+       string plugin_instance = 3;
+       string type = 4;
+       string type_instance = 5;
+}
+
+message Value {
+       oneof value {
+               uint64 counter = 1;
+               double gauge = 2;
+               int64 derive = 3;
+               uint64 absolute = 4;
+       };
+}
+
+message ValueList {
+       repeated Value value = 1;
+
+       google.protobuf.Timestamp time = 2;
+       google.protobuf.Duration interval = 3;
+
+       Identifier identifier = 4;
+}
index 3774511..dc25215 100644 (file)
@@ -17,6 +17,12 @@ endif
 AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"'
 AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"'
 
+LOG_COMPILER = env VALGRIND="@VALGRIND@" $(abs_top_srcdir)/testwrapper.sh
+
+V_PROTOC = $(v_protoc_@AM_V@)
+v_protoc_ = $(v_protoc_@AM_DEFAULT_V@)
+v_protoc_0 = @echo "  PROTOC  " $@;
+
 noinst_LTLIBRARIES =
 check_PROGRAMS =
 TESTS =
@@ -53,11 +59,12 @@ sbin_PROGRAMS = collectdmon
 bin_PROGRAMS = collectd-nagios collectdctl collectd-tg
 
 collectdmon_SOURCES = collectdmon.c
-collectdmon_CPPFLAGS = $(AM_CPPFLAGS)
 
 collectd_nagios_SOURCES = collectd-nagios.c
-collectd_nagios_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/libcollectdclient/collectd -I$(top_builddir)/src/libcollectdclient/collectd
-collectd_nagios_LDADD =
+collectd_nagios_CPPFLAGS = $(AM_CPPFLAGS) \
+       -I$(top_srcdir)/src/libcollectdclient/collectd \
+       -I$(top_builddir)/src/libcollectdclient/collectd
+collectd_nagios_LDADD = libcollectdclient/libcollectdclient.la
 if BUILD_WITH_LIBSOCKET
 collectd_nagios_LDADD += -lsocket
 endif
@@ -65,26 +72,28 @@ if BUILD_AIX
 collectd_nagios_LDADD += -lm
 endif
 
-collectd_nagios_LDADD += libcollectdclient/libcollectdclient.la
-collectd_nagios_DEPENDENCIES = libcollectdclient/libcollectdclient.la
-
 
 collectdctl_SOURCES = collectdctl.c
-collectdctl_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/libcollectdclient/collectd -I$(top_builddir)/src/libcollectdclient/collectd
-collectdctl_LDADD =
+collectdctl_CPPFLAGS = $(AM_CPPFLAGS) \
+       -I$(top_srcdir)/src/libcollectdclient/collectd \
+       -I$(top_builddir)/src/libcollectdclient/collectd
+collectdctl_LDADD = libcollectdclient/libcollectdclient.la
 if BUILD_WITH_LIBSOCKET
 collectdctl_LDADD += -lsocket
 endif
 if BUILD_AIX
 collectdctl_LDADD += -lm
 endif
-collectdctl_LDADD += libcollectdclient/libcollectdclient.la
-collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la
+
 
 collectd_tg_SOURCES = collectd-tg.c
 collectd_tg_CPPFLAGS = $(AM_CPPFLAGS) \
-                      -I$(top_srcdir)/src/libcollectdclient/collectd -I$(top_builddir)/src/libcollectdclient/collectd
-collectd_tg_LDADD = daemon/libheap.la
+       -I$(top_srcdir)/src/libcollectdclient/collectd \
+       -I$(top_builddir)/src/libcollectdclient/collectd
+collectd_tg_LDADD = \
+       $(PTHREAD_LIBS) \
+       daemon/libheap.la \
+       libcollectdclient/libcollectdclient.la
 if BUILD_WITH_LIBSOCKET
 collectd_tg_LDADD += -lsocket
 endif
@@ -94,11 +103,6 @@ endif
 if BUILD_AIX
 collectd_tg_LDADD += -lm
 endif
-if BUILD_WITH_LIBPTHREAD
-collectd_tg_LDADD += $(PTHREAD_LIBS)
-endif
-collectd_tg_LDADD += libcollectdclient/libcollectdclient.la
-collectd_tg_DEPENDENCIES = libcollectdclient/libcollectdclient.la
 
 
 pkglib_LTLIBRARIES =
@@ -106,6 +110,24 @@ pkglib_LTLIBRARIES =
 BUILT_SOURCES =
 CLEANFILES =
 
+if HAVE_PROTOC3
+if HAVE_GRPC_CPP
+BUILT_SOURCES += collectd.grpc.pb.cc collectd.pb.cc types.pb.cc
+CLEANFILES += collectd.grpc.pb.cc collectd.pb.cc types.pb.cc \
+               collectd.grpc.pb.h collectd.pb.h types.pb.h
+
+collectd.grpc.pb.cc: $(top_srcdir)/proto/collectd.proto $(top_srcdir)/proto/types.proto
+       $(V_PROTOC)@PROTOC@ -I$(top_srcdir)/proto \
+               --grpc_out=$(builddir) --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN) $<
+
+collectd.pb.cc: $(top_srcdir)/proto/collectd.proto $(top_srcdir)/proto/types.proto
+       $(V_PROTOC)@PROTOC@ -I$(top_srcdir)/proto --cpp_out=$(builddir) $<
+
+types.pb.cc: $(top_srcdir)/proto/types.proto
+       $(V_PROTOC)@PROTOC@ -I$(top_srcdir)/proto --cpp_out=$(builddir) $<
+endif
+endif
+
 if BUILD_PLUGIN_AGGREGATION
 pkglib_LTLIBRARIES += aggregation.la
 aggregation_la_SOURCES = aggregation.c \
@@ -130,12 +152,8 @@ if BUILD_PLUGIN_APACHE
 pkglib_LTLIBRARIES += apache.la
 apache_la_SOURCES = apache.c
 apache_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-apache_la_CFLAGS = $(AM_CFLAGS)
-apache_la_LIBADD =
-if BUILD_WITH_LIBCURL
-apache_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
-apache_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
-endif
+apache_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
+apache_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS)
 endif
 
 if BUILD_PLUGIN_APCUPS
@@ -151,16 +169,15 @@ endif
 if BUILD_PLUGIN_APPLE_SENSORS
 pkglib_LTLIBRARIES += apple_sensors.la
 apple_sensors_la_SOURCES = apple_sensors.c
-apple_sensors_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-apple_sensors_la_LDFLAGS += -framework IOKit
+apple_sensors_la_LDFLAGS = $(PLUGIN_LDFLAGS) -framework IOKit
 endif
 
 if BUILD_PLUGIN_AQUAERO
 pkglib_LTLIBRARIES += aquaero.la
 aquaero_la_SOURCES = aquaero.c
-aquaero_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 aquaero_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBAQUAERO5_CFLAGS)
-aquaero_la_LIBADD = $(BUILD_WITH_LIBAQUAERO5_LDFLAGS) -laquaero5
+aquaero_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBAQUAERO5_LDFLAGS)
+aquaero_la_LIBADD = -laquaero5
 endif
 
 if BUILD_PLUGIN_ASCENT
@@ -183,7 +200,6 @@ if BUILD_PLUGIN_BATTERY
 pkglib_LTLIBRARIES += battery.la
 battery_la_SOURCES = battery.c
 battery_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-battery_la_LIBADD =
 if BUILD_WITH_LIBIOKIT
 battery_la_LDFLAGS += -framework IOKit
 endif
@@ -214,6 +230,12 @@ cgroups_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 cgroups_la_LIBADD = libmount.la
 endif
 
+if BUILD_PLUGIN_CHRONY
+pkglib_LTLIBRARIES += chrony.la
+chrony_la_SOURCES = chrony.c
+chrony_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+endif
+
 if BUILD_PLUGIN_CONNTRACK
 pkglib_LTLIBRARIES += conntrack.la
 conntrack_la_SOURCES = conntrack.c
@@ -271,32 +293,27 @@ endif
 
 if BUILD_PLUGIN_CURL
 pkglib_LTLIBRARIES += curl.la
-curl_la_SOURCES = curl.c
+curl_la_SOURCES = curl.c \
+                 utils_curl_stats.c utils_curl_stats.h
 curl_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-curl_la_CFLAGS = $(AM_CFLAGS)
-curl_la_LIBADD =
-if BUILD_WITH_LIBCURL
-curl_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
-curl_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
-endif
+curl_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
+curl_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS)
 endif
 
 if BUILD_PLUGIN_CURL_JSON
 pkglib_LTLIBRARIES += curl_json.la
-curl_json_la_SOURCES = curl_json.c
-curl_json_la_CFLAGS = $(AM_CFLAGS)
-curl_json_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
+curl_json_la_SOURCES = curl_json.c \
+                 utils_curl_stats.c utils_curl_stats.h
+curl_json_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
 curl_json_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
-curl_json_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
-if BUILD_WITH_LIBCURL
-curl_json_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
-curl_json_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
-endif
+curl_json_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
+curl_json_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBYAJL_LIBS)
 endif
 
 if BUILD_PLUGIN_CURL_XML
 pkglib_LTLIBRARIES += curl_xml.la
-curl_xml_la_SOURCES = curl_xml.c
+curl_xml_la_SOURCES = curl_xml.c \
+                 utils_curl_stats.c utils_curl_stats.h
 curl_xml_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 curl_xml_la_CFLAGS = $(AM_CFLAGS) \
                $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
@@ -355,7 +372,7 @@ if BUILD_PLUGIN_DNS
 pkglib_LTLIBRARIES += dns.la
 dns_la_SOURCES = dns.c utils_dns.c utils_dns.h
 dns_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-dns_la_LIBADD = $(PTHREAD_LIBS) -lpcap
+dns_la_LIBADD = -lpcap
 endif
 
 if BUILD_PLUGIN_DRBD
@@ -368,7 +385,6 @@ if BUILD_PLUGIN_EMAIL
 pkglib_LTLIBRARIES += email.la
 email_la_SOURCES = email.c
 email_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-email_la_LIBADD = $(PTHREAD_LIBS)
 endif
 
 if BUILD_PLUGIN_ENTROPY
@@ -384,7 +400,6 @@ exec_la_SOURCES = exec.c \
                  utils_cmd_putval.c utils_cmd_putval.h \
                  utils_parse_option.h utils_parse_option.c
 exec_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-exec_la_LIBADD = $(PTHREAD_LIBS)
 endif
 
 if BUILD_PLUGIN_ETHSTAT
@@ -413,6 +428,16 @@ gmond_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(GANGLIA_LDFLAGS)
 gmond_la_LIBADD = $(GANGLIA_LIBS)
 endif
 
+if BUILD_PLUGIN_GRPC
+pkglib_LTLIBRARIES += grpc.la
+grpc_la_SOURCES = grpc.cc
+nodist_grpc_la_SOURCES = collectd.grpc.pb.cc collectd.pb.cc types.pb.cc
+grpc_la_CPPFLAGS = $(AM_CPPFLAGS) -std=c++11
+grpc_la_CXXFLAGS = $(AM_CXXFLAGS) -std=c++11
+grpc_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+grpc_la_LIBADD = -lgrpc++ -lgrpc -lgpr -lprotobuf
+endif
+
 if BUILD_PLUGIN_HDDTEMP
 pkglib_LTLIBRARIES += hddtemp.la
 hddtemp_la_SOURCES = hddtemp.c
@@ -448,7 +473,6 @@ endif # BUILD_PLUGIN_INTERFACE
 if BUILD_PLUGIN_IPC
 pkglib_LTLIBRARIES += ipc.la
 ipc_la_SOURCES = ipc.c
-ipc_la_CFLAGS = $(AM_CFLAGS)
 ipc_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
@@ -471,10 +495,9 @@ endif
 if BUILD_PLUGIN_IPVS
 pkglib_LTLIBRARIES += ipvs.la
 ipvs_la_SOURCES = ipvs.c
-if IP_VS_H_NEEDS_KERNEL_CFLAGS
-ipvs_la_CFLAGS = $(AM_CFLAGS) $(KERNEL_CFLAGS)
-else
 ipvs_la_CFLAGS = $(AM_CFLAGS)
+if IP_VS_H_NEEDS_KERNEL_CFLAGS
+ipvs_la_CFLAGS += $(KERNEL_CFLAGS)
 endif
 ipvs_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
@@ -491,7 +514,7 @@ java_la_SOURCES = java.c
 java_la_CPPFLAGS = $(AM_CPPFLAGS) $(JAVA_CPPFLAGS)
 java_la_CFLAGS = $(AM_CFLAGS) $(JAVA_CFLAGS)
 java_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(JAVA_LDFLAGS)
-java_la_LIBADD = $(PTHREAD_LIBS) $(JAVA_LIBS)
+java_la_LIBADD = $(JAVA_LIBS)
 endif
 
 if BUILD_PLUGIN_LOAD
@@ -518,7 +541,6 @@ endif
 if BUILD_PLUGIN_LOG_LOGSTASH
 pkglib_LTLIBRARIES += log_logstash.la
 log_logstash_la_SOURCES = log_logstash.c
-log_logstash_la_CFLAGS = $(AM_CFLAGS)
 log_logstash_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
 log_logstash_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
 log_logstash_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
@@ -534,7 +556,8 @@ endif
 if BUILD_PLUGIN_LVM
 pkglib_LTLIBRARIES += lvm.la
 lvm_la_SOURCES = lvm.c
-lvm_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+lvm_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBLVM2APP_CPPFLAGS)
+lvm_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBLVM2APP_LDFLAGS)
 lvm_la_LIBADD = $(BUILD_WITH_LIBLVM2APP_LIBS)
 endif
 
@@ -662,13 +685,9 @@ endif
 if BUILD_PLUGIN_MYSQL
 pkglib_LTLIBRARIES += mysql.la
 mysql_la_SOURCES = mysql.c
+mysql_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMYSQL_CFLAGS)
 mysql_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-mysql_la_CFLAGS = $(AM_CFLAGS)
-mysql_la_LIBADD =
-if BUILD_WITH_LIBMYSQL
-mysql_la_CFLAGS += $(BUILD_WITH_LIBMYSQL_CFLAGS)
-mysql_la_LIBADD += $(BUILD_WITH_LIBMYSQL_LIBS)
-endif
+mysql_la_LIBADD = $(BUILD_WITH_LIBMYSQL_LIBS)
 endif
 
 if BUILD_PLUGIN_NETAPP
@@ -693,7 +712,7 @@ network_la_SOURCES = network.c network.h \
                     utils_fbhash.c utils_fbhash.h
 network_la_CPPFLAGS = $(AM_CPPFLAGS)
 network_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-network_la_LIBADD = $(PTHREAD_LIBS)
+network_la_LIBADD =
 if BUILD_WITH_LIBSOCKET
 network_la_LIBADD += -lsocket
 endif
@@ -719,13 +738,9 @@ endif
 if BUILD_PLUGIN_NGINX
 pkglib_LTLIBRARIES += nginx.la
 nginx_la_SOURCES = nginx.c
-nginx_la_CFLAGS = $(AM_CFLAGS)
-nginx_la_LIBADD =
+nginx_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
 nginx_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-if BUILD_WITH_LIBCURL
-nginx_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
-nginx_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
-endif
+nginx_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS)
 endif
 
 if BUILD_PLUGIN_NOTIFY_DESKTOP
@@ -740,7 +755,7 @@ if BUILD_PLUGIN_NOTIFY_EMAIL
 pkglib_LTLIBRARIES += notify_email.la
 notify_email_la_SOURCES = notify_email.c
 notify_email_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-notify_email_la_LIBADD = $(PTHREAD_LIBS) -lesmtp -lssl -lcrypto
+notify_email_la_LIBADD = -lesmtp -lssl -lcrypto
 endif
 
 if BUILD_PLUGIN_NOTIFY_NAGIOS
@@ -770,7 +785,7 @@ pkglib_LTLIBRARIES += nut.la
 nut_la_SOURCES = nut.c
 nut_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBUPSCLIENT_CFLAGS)
 nut_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-nut_la_LIBADD = $(PTHREAD_LIBS) $(BUILD_WITH_LIBUPSCLIENT_LIBS)
+nut_la_LIBADD = $(BUILD_WITH_LIBUPSCLIENT_LIBS)
 endif
 
 if BUILD_PLUGIN_OLSRD
@@ -786,7 +801,6 @@ endif
 if BUILD_PLUGIN_ONEWIRE
 pkglib_LTLIBRARIES += onewire.la
 onewire_la_SOURCES = onewire.c
-onewire_la_CFLAGS = $(AM_CFLAGS)
 onewire_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBOWCAPI_CPPFLAGS)
 onewire_la_LIBADD = $(BUILD_WITH_LIBOWCAPI_LIBS)
 onewire_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBOWCAPI_LDFLAGS)
@@ -795,15 +809,14 @@ endif
 if BUILD_PLUGIN_OPENLDAP
 pkglib_LTLIBRARIES += openldap.la
 openldap_la_SOURCES = openldap.c
-openldap_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBLDAP_LDFLAGS)
-openldap_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBLDAP_CPPFLAGS)
+openldap_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBLDAP_CPPFLAGS)
+openldap_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBLDAP_LDFLAGS)
 openldap_la_LIBADD = -lldap
 endif
 
 if BUILD_PLUGIN_OPENVPN
 pkglib_LTLIBRARIES += openvpn.la
 openvpn_la_SOURCES = openvpn.c
-openvpn_la_CFLAGS = $(AM_CFLAGS)
 openvpn_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
@@ -811,8 +824,7 @@ if BUILD_PLUGIN_ORACLE
 pkglib_LTLIBRARIES += oracle.la
 oracle_la_SOURCES = oracle.c \
        utils_db_query.c utils_db_query.h
-oracle_la_CFLAGS = $(AM_CFLAGS)
-oracle_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_ORACLE_CFLAGS)
+oracle_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_ORACLE_CPPFLAGS)
 oracle_la_LIBADD = $(BUILD_WITH_ORACLE_LIBS)
 oracle_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
@@ -976,16 +988,13 @@ if BUILD_WITH_LIBNETSNMP
 snmp_la_CFLAGS += $(BUILD_WITH_LIBSNMP_CFLAGS)
 snmp_la_LIBADD += $(BUILD_WITH_LIBSNMP_LIBS)
 endif
-if BUILD_WITH_LIBPTHREAD
-snmp_la_LIBADD += $(PTHREAD_LIBS)
-endif
 endif
 
 if BUILD_PLUGIN_STATSD
 pkglib_LTLIBRARIES += statsd.la
 statsd_la_SOURCES = statsd.c
 statsd_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-statsd_la_LIBADD = $(PTHREAD_LIBS) liblatency.la -lm
+statsd_la_LIBADD = liblatency.la -lm
 endif
 
 if BUILD_PLUGIN_SWAP
@@ -1136,7 +1145,6 @@ unixsock_la_SOURCES = unixsock.c \
                      utils_cmd_putnotif.h utils_cmd_putnotif.c \
                      utils_parse_option.h utils_parse_option.c
 unixsock_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-unixsock_la_LIBADD = $(PTHREAD_LIBS)
 endif
 
 if BUILD_PLUGIN_UPTIME
@@ -1220,13 +1228,9 @@ if BUILD_PLUGIN_WRITE_HTTP
 pkglib_LTLIBRARIES += write_http.la
 write_http_la_SOURCES = write_http.c \
                        utils_format_json.c utils_format_json.h
+write_http_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
 write_http_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-write_http_la_CFLAGS = $(AM_CFLAGS)
-write_http_la_LIBADD =
-if BUILD_WITH_LIBCURL
-write_http_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
-write_http_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
-endif
+write_http_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS)
 endif
 
 if BUILD_PLUGIN_WRITE_KAFKA
@@ -1260,7 +1264,7 @@ if BUILD_PLUGIN_WRITE_REDIS
 pkglib_LTLIBRARIES += write_redis.la
 write_redis_la_SOURCES = write_redis.c
 write_redis_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBHIREDIS_LDFLAGS)
-write_redis_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBHIREDIS_CPPFLAGS)
+write_redis_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBHIREDIS_CPPFLAGS)
 write_redis_la_LIBADD = -lhiredis
 endif
 
@@ -1286,7 +1290,8 @@ endif
 if BUILD_PLUGIN_XENCPU
 pkglib_LTLIBRARIES += xencpu.la
 xencpu_la_SOURCES = xencpu.c
-xencpu_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+xencpu_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBXENCTL_CPPFLAGS)
+xencpu_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBXENCTL_LDFLAGS)
 xencpu_la_LIBADD = -lxenctrl
 endif
 
@@ -1301,17 +1306,12 @@ endif
 if BUILD_PLUGIN_ZFS_ARC
 pkglib_LTLIBRARIES += zfs_arc.la
 zfs_arc_la_SOURCES = zfs_arc.c
-zfs_arc_la_CFLAGS = $(AM_CFLAGS)
 zfs_arc_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 if BUILD_FREEBSD
 zfs_arc_la_LIBADD = -lm
-else
-if BUILD_LINUX
-# zfs_arc requires no library on linux
-else
-# solaris
-zfs_arc_la_LIBADD = -lkstat
 endif
+if BUILD_SOLARIS
+zfs_arc_la_LIBADD = -lkstat
 endif
 endif
 
@@ -1326,7 +1326,6 @@ BUILT_SOURCES += $(dist_man_MANS)
 if BUILD_PLUGIN_ZONE
 pkglib_LTLIBRARIES += zone.la
 zone_la_SOURCES = zone.c
-zone_la_CFLAGS = $(AM_CFLAGS)
 zone_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
@@ -1346,11 +1345,7 @@ dist_man_MANS = collectd.1 \
                collectd-unixsock.5 \
                types.db.5
 
-#collectd_1_SOURCES = collectd.pod
-
-EXTRA_DIST = types.db
-
-EXTRA_DIST +=   collectd.conf.pod \
+EXTRA_DIST =   collectd.conf.pod \
                collectd-email.pod \
                collectd-exec.pod \
                collectdctl.pod \
@@ -1365,10 +1360,17 @@ EXTRA_DIST +=   collectd.conf.pod \
                collectd-threshold.pod \
                collectd-unixsock.pod \
                postgresql_default.conf \
-               types.db.pod
+               types.db \
+               types.db.pod \
+               valgrind.FreeBSD.suppress
+
+AM_V_POD2MAN_C = $(am__v_POD2MAN_C_@AM_V@)
+am__v_POD2MAN_C_ = $(am__v_POD2MAN_C_@AM_DEFAULT_V@)
+am__v_POD2MAN_C_0 = @echo "  POD2MAN " $@;
+am__v_POD2MAN_C_1 =
 
 .pod.1:
-       pod2man --release=$(VERSION) --center=$(PACKAGE) $< \
+       $(AM_V_POD2MAN_C)pod2man --release=$(VERSION) --center=$(PACKAGE) $< \
                >.pod2man.tmp.$$$$ 2>/dev/null && mv -f .pod2man.tmp.$$$$ $@ || true
        @if grep '\<POD ERRORS\>' $@ >/dev/null 2>&1; \
        then \
@@ -1376,7 +1378,7 @@ EXTRA_DIST +=   collectd.conf.pod \
        fi
 
 .pod.5:
-       pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< \
+       $(AM_V_POD2MAN_C)pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< \
                >.pod2man.tmp.$$$$ 2>/dev/null && mv -f .pod2man.tmp.$$$$ $@ || true
        @if grep '\<POD ERRORS\>' $@ >/dev/null 2>&1; \
        then \
@@ -1385,7 +1387,7 @@ EXTRA_DIST +=   collectd.conf.pod \
 
 AM_V_PROTOC_C = $(am__v_PROTOC_C_@AM_V@)
 am__v_PROTOC_C_ = $(am__v_PROTOC_C_@AM_DEFAULT_V@)
-am__v_PROTOC_C_0 = @echo "  PROTOC-C    " $@;
+am__v_PROTOC_C_0 = @echo "  PROTOC-C" $@;
 am__v_PROTOC_C_1 =
 
 # Protocol buffer for the "pinba" plugin.
index 3d5f396..c4c1627 100644 (file)
@@ -26,8 +26,6 @@
 
 #include "collectd.h"
 
-#include <pthread.h>
-
 #include "plugin.h"
 #include "common.h"
 #include "configfile.h"
index fc69e93..99dca90 100644 (file)
@@ -33,8 +33,6 @@
 #include "utils_format_json.h"
 #include "utils_format_graphite.h"
 
-#include <pthread.h>
-
 #include <amqp.h>
 #include <amqp_framing.h>
 
index 2d1c16b..9f7476b 100644 (file)
@@ -89,7 +89,7 @@ static int net_shutdown (int *fd)
        if ((fd == NULL) || (*fd < 0))
                return (EINVAL);
 
-       swrite (*fd, (void *) &packet_size, sizeof (packet_size));
+       (void)swrite (*fd, (void *) &packet_size, sizeof (packet_size));
        close (*fd);
        *fd = -1;
 
index 1141140..199d40e 100644 (file)
@@ -144,6 +144,7 @@ DEF_TEST(traverse_json)
   CHECK_ZERO (yajl_parse_complete (hndl));
 #endif
 
+  yajl_free (hndl);
   return 0;
 }
 
diff --git a/src/chrony.c b/src/chrony.c
new file mode 100644 (file)
index 0000000..23ad992
--- /dev/null
@@ -0,0 +1,1125 @@
+/* chrony plugin for collectd (monitoring of chrony time server daemon)
+ **********************************************************************
+ * Copyright (C) Claudius M Zingerli, ZSeng, 2015-2016 
+ *
+ * Internals roughly based on the ntpd plugin
+ * Some functions copied from chronyd/web (as marked)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * 
+ * TODO:
+ *     - More robust udp parsing (using offsets instead of structs?)
+ *       -> Currently chrony parses its data the same way as we do (using structs)
+ *     - Plausibility checks on values received
+ *       -> Done at higher levels
+ */
+
+#include "collectd.h"
+#include "common.h"             /* auxiliary functions */
+#include "plugin.h"             /* plugin_register_*, plugin_dispatch_values */
+
+#if HAVE_NETDB_H
+#  include <netdb.h>            /* struct addrinfo */
+#endif
+#if HAVE_ARPA_INET_H
+#  include <arpa/inet.h>        /* ntohs/ntohl */
+#endif
+
+
+#define CONFIG_KEY_HOST    "Host"
+#define CONFIG_KEY_PORT    "Port"
+#define CONFIG_KEY_TIMEOUT "Timeout"
+
+#define URAND_DEVICE_PATH  "/dev/urandom"       /* Used to initialize seq nr generator */
+#define RAND_DEVICE_PATH   "/dev/random"        /* Used to initialize seq nr generator (fall back) */
+
+static const char *g_config_keys[] = {
+  CONFIG_KEY_HOST,
+  CONFIG_KEY_PORT,
+  CONFIG_KEY_TIMEOUT
+};
+
+static int       g_config_keys_num = STATIC_ARRAY_SIZE(g_config_keys);
+static int       g_chrony_is_connected;
+static int       g_chrony_socket  = -1;
+static time_t    g_chrony_timeout = -1;
+static char     *g_chrony_plugin_instance;
+static char     *g_chrony_host;
+static char     *g_chrony_port;
+static uint32_t  g_chrony_rand    = 1;
+static uint32_t  g_chrony_seq_is_initialized;
+
+#define PLUGIN_NAME_SHORT "chrony"
+#define PLUGIN_NAME       PLUGIN_NAME_SHORT " plugin"
+#define DAEMON_NAME       PLUGIN_NAME_SHORT
+#define CHRONY_DEFAULT_HOST "localhost"
+#define CHRONY_DEFAULT_PORT "323"
+#define CHRONY_DEFAULT_TIMEOUT 2
+
+/* Return codes (collectd expects non-zero on errors) */
+#define CHRONY_RC_OK    0
+#define CHRONY_RC_FAIL  1
+
+/* Chronyd command packet variables adapted from chrony/candm.h (GPL2) */
+#define PROTO_VERSION_NUMBER 6
+#define IPADDR_UNSPEC 0
+#define IPADDR_INET4  1
+#define IPADDR_INET6  2
+#define IPV6_STR_MAX_SIZE (8*4+7+1)
+
+typedef enum
+{
+  PKT_TYPE_CMD_REQUEST = 1,
+  PKT_TYPE_CMD_REPLY   = 2
+} ePacketType;
+
+typedef enum
+{
+  REQ_N_SOURCES    = 14,
+  REQ_SOURCE_DATA  = 15,
+  REQ_TRACKING     = 33,
+  REQ_SOURCE_STATS = 34
+} eDaemonRequests;
+
+
+typedef enum
+{
+  RPY_NULL             = 1,
+  RPY_N_SOURCES        = 2,
+  RPY_SOURCE_DATA      = 3,
+  RPY_MANUAL_TIMESTAMP = 4,
+  RPY_TRACKING         = 5,
+  RPY_SOURCE_STATS     = 6,
+  RPY_RTC              = 7
+} eDaemonReplies;
+
+
+#if defined(__GNUC__)
+#  /* GNU gcc extension to enforce struct packing. */
+#  define ATTRIB_PACKED __attribute__((packed))
+#else
+#  error Not defining packed attribute (unknown compiler)
+#  define ATTRIB_PACKED 
+#endif
+
+typedef struct ATTRIB_PACKED
+{
+  int32_t value;
+} tFloat;
+
+typedef struct ATTRIB_PACKED
+{
+  uint32_t tv_sec_high;
+  uint32_t tv_sec_low;
+  uint32_t tv_nsec;
+} tTimeval;
+
+typedef enum
+{
+  STT_SUCCESS        =  0,
+  STT_FAILED         =  1,
+  STT_UNAUTH         =  2,
+  STT_INVALID        =  3,
+  STT_NOSUCHSOURCE   =  4,
+  STT_INVALIDTS      =  5,
+  STT_NOTENABLED     =  6,
+  STT_BADSUBNET      =  7,
+  STT_ACCESSALLOWED  =  8,
+  STT_ACCESSDENIED   =  9,
+  STT_NOHOSTACCESS   = 10,
+  STT_SOURCEALREADYKNOWN = 11,
+  STT_TOOMANYSOURCES = 12,
+  STT_NORTC          = 13,
+  STT_BADRTCFILE     = 14,
+  STT_INACTIVE       = 15,
+  STT_BADSAMPLE      = 16,
+  STT_INVALIDAF      = 17,
+  STT_BADPKTVERSION  = 18,
+  STT_BADPKTLENGTH   = 19
+} eChrony_Status;
+
+/* Chrony client request packets */
+typedef struct ATTRIB_PACKED
+{
+  uint8_t f_dummy0[80];         /* Chrony expects 80bytes dummy data (Avoiding UDP Amplification) */
+} tChrony_Req_Tracking;
+
+typedef struct ATTRIB_PACKED
+{
+  uint32_t f_n_sources;
+} tChrony_Req_N_Sources;
+
+typedef struct ATTRIB_PACKED
+{
+  int32_t f_index;
+  uint8_t f_dummy0[44];
+} tChrony_Req_Source_data;
+
+typedef struct ATTRIB_PACKED
+{
+  int32_t f_index;
+  uint8_t f_dummy0[56];
+} tChrony_Req_Source_stats;
+
+typedef struct ATTRIB_PACKED
+{
+  struct
+  {
+    uint8_t f_version;
+    uint8_t f_type;
+    uint8_t f_dummy0;
+    uint8_t f_dummy1;
+    uint16_t f_cmd;
+    uint16_t f_cmd_try;
+    uint32_t f_seq;
+
+    uint32_t f_dummy2;
+    uint32_t f_dummy3;
+  } header;                     /* Packed: 20Bytes */
+  union
+  {
+    tChrony_Req_N_Sources n_sources;
+    tChrony_Req_Source_data source_data;
+    tChrony_Req_Source_stats source_stats;
+    tChrony_Req_Tracking tracking;
+  } body;
+  uint8_t padding[4 + 16];      /* Padding to match minimal response size */
+} tChrony_Request;
+
+/* Chrony daemon response packets */
+typedef struct ATTRIB_PACKED
+{
+  uint32_t f_n_sources;
+} tChrony_Resp_N_Sources;
+
+typedef struct ATTRIB_PACKED
+{
+  union
+  {
+    uint32_t ip4;
+    uint8_t ip6[16];
+  } addr;
+  uint16_t f_family;
+} tChrony_IPAddr;
+
+typedef struct ATTRIB_PACKED
+{
+  tChrony_IPAddr addr;
+  uint16_t dummy;               /* FIXME: Strange dummy space. Needed on gcc 4.8.3/clang 3.4.1 on x86_64 */
+  int16_t f_poll;               /* 2^f_poll = Time between polls (s) */
+  uint16_t f_stratum;           /* Remote clock stratum */
+  uint16_t f_state;             /* 0 = RPY_SD_ST_SYNC,    1 = RPY_SD_ST_UNREACH,   2 = RPY_SD_ST_FALSETICKER */
+                                /* 3 = RPY_SD_ST_JITTERY, 4 = RPY_SD_ST_CANDIDATE, 5 = RPY_SD_ST_OUTLIER     */
+  uint16_t f_mode;              /* 0 = RPY_SD_MD_CLIENT,  1 = RPY_SD_MD_PEER,      2 = RPY_SD_MD_REF         */
+  uint16_t f_flags;             /* unused */
+  uint16_t f_reachability;      /* Bit mask of successfull tries to reach the source */
+
+  uint32_t f_since_sample;      /* Time since last sample (s) */
+  tFloat f_origin_latest_meas;  /*  */
+  tFloat f_latest_meas;         /*  */
+  tFloat f_latest_meas_err;     /*  */
+} tChrony_Resp_Source_data;
+
+typedef struct ATTRIB_PACKED
+{
+  uint32_t f_ref_id;
+  tChrony_IPAddr addr;
+  uint16_t dummy;               /* FIXME: Strange dummy space. Needed on gcc 4.8.3/clang 3.4.1 on x86_64 */
+  uint32_t f_n_samples;         /* Number of measurements done   */
+  uint32_t f_n_runs;            /* How many measurements to come */
+  uint32_t f_span_seconds;      /* For how long we're measuring  */
+  tFloat f_rtc_seconds_fast;    /* ??? */
+  tFloat f_rtc_gain_rate_ppm;   /* Estimated relative frequency error */
+  tFloat f_skew_ppm;            /* Clock skew (ppm) (worst case freq est error (skew: peak2peak)) */
+  tFloat f_est_offset;          /* Estimated offset of source */
+  tFloat f_est_offset_err;      /* Error of estimation        */
+} tChrony_Resp_Source_stats;
+
+typedef struct ATTRIB_PACKED
+{
+  uint32_t f_ref_id;
+  tChrony_IPAddr addr;
+  uint16_t dummy;               /* FIXME: Strange dummy space. Needed on gcc 4.8.3/clang 3.4.1 on x86_64 */
+  uint16_t f_stratum;
+  uint16_t f_leap_status;
+  tTimeval f_ref_time;
+  tFloat f_current_correction;
+  tFloat f_last_offset;
+  tFloat f_rms_offset;
+  tFloat f_freq_ppm;
+  tFloat f_resid_freq_ppm;
+  tFloat f_skew_ppm;
+  tFloat f_root_delay;
+  tFloat f_root_dispersion;
+  tFloat f_last_update_interval;
+} tChrony_Resp_Tracking;
+
+typedef struct ATTRIB_PACKED
+{
+  struct
+  {
+    uint8_t f_version;
+    uint8_t f_type;
+    uint8_t f_dummy0;
+    uint8_t f_dummy1;
+    uint16_t f_cmd;
+    uint16_t f_reply;
+    uint16_t f_status;
+    uint16_t f_dummy2;
+    uint16_t f_dummy3;
+    uint16_t f_dummy4;
+    uint32_t f_seq;
+    uint32_t f_dummy5;
+    uint32_t f_dummy6;
+  } header;                     /* Packed: 28 Bytes */
+
+  union
+  {
+    tChrony_Resp_N_Sources n_sources;
+    tChrony_Resp_Source_data source_data;
+    tChrony_Resp_Source_stats source_stats;
+    tChrony_Resp_Tracking tracking;
+  } body;
+
+  uint8_t padding[1024];
+} tChrony_Response;
+
+
+/*****************************************************************************/
+/* Internal functions */
+/*****************************************************************************/
+
+/* connect_client code adapted from: http://long.ccaba.upc.edu/long/045Guidelines/eva/ipv6.html#daytimeClient6 */
+/* License granted by Eva M Castro via e-mail on 2016-02-18 under the terms of GPLv3 */
+static int
+connect_client(const char *p_hostname,
+               const char *p_service, int p_family, int p_socktype)
+{
+  struct addrinfo hints, *res = NULL, *ressave = NULL;
+  int n, sockfd;
+
+  memset(&hints, 0, sizeof(struct addrinfo));
+
+  hints.ai_family = p_family;
+  hints.ai_socktype = p_socktype;
+
+  n = getaddrinfo(p_hostname, p_service, &hints, &res);
+
+  if (n < 0)
+  {
+    ERROR(PLUGIN_NAME ": getaddrinfo error:: [%s]", gai_strerror(n));
+    return -1;
+  }
+
+  ressave = res;
+
+  sockfd = -1;
+  while (res)
+  {
+    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+
+    if (!(sockfd < 0))
+    {
+      if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
+      {
+        /* Success */
+        break;
+      }
+
+      close(sockfd);
+      sockfd = -1;
+    }
+    res = res->ai_next;
+  }
+
+  freeaddrinfo(ressave);
+  return sockfd;
+}
+
+
+/* niptoha code originally from: git://git.tuxfamily.org/gitroot/chrony/chrony.git:util.c */
+/* Original code licensed as GPLv2, by Richard P. Purnow, Miroslav Lichvar */
+/* Original name: char * UTI_IPToString(IPAddr *addr)*/
+static char *
+niptoha(const tChrony_IPAddr * addr, char *p_buf, size_t p_buf_size)
+{
+  int rc = 1;
+  unsigned long a, b, c, d, ip;
+
+  switch (ntohs(addr->f_family))
+  {
+  case IPADDR_UNSPEC:
+    rc = snprintf(p_buf, p_buf_size, "[UNSPEC]");
+    break;
+  case IPADDR_INET4:
+    ip = ntohl(addr->addr.ip4);
+    a = (ip >> 24) & 0xff;
+    b = (ip >> 16) & 0xff;
+    c = (ip >> 8) & 0xff;
+    d = (ip >> 0) & 0xff;
+    rc = snprintf(p_buf, p_buf_size, "%ld.%ld.%ld.%ld", a, b, c, d);
+    break;
+  case IPADDR_INET6:
+  {
+    const char *rp = inet_ntop(AF_INET6, addr->addr.ip6, p_buf, p_buf_size);
+    if (rp == NULL)
+    {
+      ERROR(PLUGIN_NAME ": Error converting ipv6 address to string. Errno = %d", errno);
+      rc = snprintf(p_buf, p_buf_size, "[UNKNOWN]");
+    }
+    break;
+  }
+  default:
+    rc = snprintf(p_buf, p_buf_size, "[UNKNOWN]");
+  }
+  assert(rc > 0);
+  return p_buf;
+}
+
+
+static int
+chrony_set_timeout(void)
+{
+  /* Set the socket's  timeout to g_chrony_timeout; a value of 0 signals infinite timeout */
+  /* Returns 0 on success, !0 on error (check errno) */
+
+  struct timeval tv;
+  tv.tv_sec = g_chrony_timeout;
+  tv.tv_usec = 0;
+
+  assert(g_chrony_socket >= 0);
+  if (setsockopt(g_chrony_socket, SOL_SOCKET,
+      SO_RCVTIMEO, (char *) &tv, sizeof(struct timeval)) < 0)
+  {
+    return CHRONY_RC_FAIL;
+  }
+  return CHRONY_RC_OK;
+}
+
+
+static int
+chrony_connect(void)
+{
+  /* Connects to the chrony daemon */
+  /* Returns 0 on success, !0 on error (check errno) */
+  int socket;
+
+  if (g_chrony_host == NULL)
+  {
+    g_chrony_host = strdup(CHRONY_DEFAULT_HOST);
+    if (g_chrony_host == NULL)
+    {
+      ERROR(PLUGIN_NAME ": Error duplicating chrony host name");
+      return CHRONY_RC_FAIL;
+    }
+  }
+  if (g_chrony_port == NULL)
+  {
+    g_chrony_port = strdup(CHRONY_DEFAULT_PORT);
+    if (g_chrony_port == NULL)
+    {
+      ERROR(PLUGIN_NAME ": Error duplicating chrony port string");
+      return CHRONY_RC_FAIL;
+    }
+  }
+  if (g_chrony_timeout < 0)
+  {
+    g_chrony_timeout = CHRONY_DEFAULT_TIMEOUT;
+    assert(g_chrony_timeout >= 0);
+  }
+
+  DEBUG(PLUGIN_NAME ": Connecting to %s:%s", g_chrony_host, g_chrony_port);
+  socket = connect_client(g_chrony_host, g_chrony_port, AF_UNSPEC, SOCK_DGRAM);
+  if (socket < 0)
+  {
+    ERROR(PLUGIN_NAME ": Error connecting to daemon. Errno = %d", errno);
+    return CHRONY_RC_FAIL;
+  }
+  DEBUG(PLUGIN_NAME ": Connected");
+  g_chrony_socket = socket;
+
+  if (chrony_set_timeout())
+  {
+    ERROR(PLUGIN_NAME ": Error setting timeout to %lds. Errno = %d",
+          g_chrony_timeout, errno);
+    return CHRONY_RC_FAIL;
+  }
+  return CHRONY_RC_OK;
+}
+
+
+static int
+chrony_send_request(const tChrony_Request * p_req, size_t p_req_size)
+{
+  if (send(g_chrony_socket, p_req, p_req_size, 0) < 0)
+  {
+    ERROR(PLUGIN_NAME ": Error sending packet. Errno = %d", errno);
+    return CHRONY_RC_FAIL;
+  }
+  return CHRONY_RC_OK;
+}
+
+
+static int
+chrony_recv_response(tChrony_Response * p_resp, size_t p_resp_max_size,
+                     size_t * p_resp_size)
+{
+  ssize_t rc = recv(g_chrony_socket, p_resp, p_resp_max_size, 0);
+  if (rc <= 0)
+  {
+    ERROR(PLUGIN_NAME ": Error receiving packet: %s (%d)", strerror(errno),
+          errno);
+    return CHRONY_RC_FAIL;
+  }
+  else
+  {
+    *p_resp_size = rc;
+    return CHRONY_RC_OK;
+  }
+}
+
+
+static int
+chrony_query(const int p_command, tChrony_Request * p_req,
+             tChrony_Response * p_resp, size_t * p_resp_size)
+{
+  /* Check connection. We simply perform one try as collectd already handles retries */
+  assert(p_req);
+  assert(p_resp);
+  assert(p_resp_size);
+
+  if (g_chrony_is_connected == 0)
+  {
+    if (chrony_connect() == CHRONY_RC_OK)
+    {
+      g_chrony_is_connected = 1;
+    }
+    else
+    {
+      ERROR(PLUGIN_NAME ": Unable to connect. Errno = %d", errno);
+      return CHRONY_RC_FAIL;
+    }
+  }
+
+  do
+  {
+    int valid_command = 0;
+    size_t req_size  = sizeof(p_req->header) + sizeof(p_req->padding);
+    size_t resp_size = sizeof(p_resp->header);
+    uint16_t resp_code = RPY_NULL;
+    switch (p_command)
+    {
+    case REQ_TRACKING:
+      req_size  += sizeof(p_req->body.tracking);
+      resp_size += sizeof(p_resp->body.tracking);
+      resp_code = RPY_TRACKING;
+      valid_command = 1;
+      break;
+    case REQ_N_SOURCES:
+      req_size  += sizeof(p_req->body.n_sources);
+      resp_size += sizeof(p_resp->body.n_sources);
+      resp_code = RPY_N_SOURCES;
+      valid_command = 1;
+      break;
+    case REQ_SOURCE_DATA:
+      req_size  += sizeof(p_req->body.source_data);
+      resp_size += sizeof(p_resp->body.source_data);
+      resp_code = RPY_SOURCE_DATA;
+      valid_command = 1;
+      break;
+    case REQ_SOURCE_STATS:
+      req_size  += sizeof(p_req->body.source_stats);
+      resp_size += sizeof(p_resp->body.source_stats);
+      resp_code = RPY_SOURCE_STATS;
+      valid_command = 1;
+      break;
+    default:
+      ERROR(PLUGIN_NAME ": Unknown request command (Was: %d)", p_command);
+      break;
+    }
+
+    if (valid_command == 0)
+      break;
+
+    uint32_t seq_nr = rand_r(&g_chrony_rand);
+    p_req->header.f_cmd = htons(p_command);
+    p_req->header.f_cmd_try = 0;
+    p_req->header.f_seq = seq_nr;
+
+    DEBUG(PLUGIN_NAME ": Sending request (.cmd = %d, .seq = %d)", p_command,
+          seq_nr);
+    if (chrony_send_request(p_req, req_size) != 0)
+      break;
+
+    DEBUG(PLUGIN_NAME ": Waiting for response");
+    if (chrony_recv_response(p_resp, resp_size, p_resp_size) != 0)
+      break;
+
+    DEBUG(PLUGIN_NAME
+          ": Received response: .version = %u, .type = %u, .cmd = %u, .reply = %u, .status = %u, .seq = %u",
+          p_resp->header.f_version, p_resp->header.f_type,
+          ntohs(p_resp->header.f_cmd), ntohs(p_resp->header.f_reply),
+          ntohs(p_resp->header.f_status), p_resp->header.f_seq);
+
+    if (p_resp->header.f_version != p_req->header.f_version)
+    {
+      ERROR(PLUGIN_NAME ": Wrong protocol version (Was: %d, expected: %d)",
+            p_resp->header.f_version, p_req->header.f_version);
+      return CHRONY_RC_FAIL;
+    }
+    if (p_resp->header.f_type != PKT_TYPE_CMD_REPLY)
+    {
+      ERROR(PLUGIN_NAME ": Wrong packet type (Was: %d, expected: %d)",
+            p_resp->header.f_type, PKT_TYPE_CMD_REPLY);
+      return CHRONY_RC_FAIL;
+    }
+    if (p_resp->header.f_seq != seq_nr)
+    {
+      /* FIXME: Implement sequence number handling */
+      ERROR(PLUGIN_NAME
+            ": Unexpected sequence number (Was: %d, expected: %d)",
+            p_resp->header.f_seq, p_req->header.f_seq);
+      return CHRONY_RC_FAIL;
+    }
+    if (p_resp->header.f_cmd != p_req->header.f_cmd)
+    {
+      ERROR(PLUGIN_NAME ": Wrong reply command (Was: %d, expected: %d)",
+            p_resp->header.f_cmd, p_req->header.f_cmd);
+      return CHRONY_RC_FAIL;
+    }
+
+    if (ntohs(p_resp->header.f_reply) != resp_code)
+    {
+      ERROR(PLUGIN_NAME ": Wrong reply code (Was: %d, expected: %d)",
+            ntohs(p_resp->header.f_reply), resp_code);
+      return CHRONY_RC_FAIL;
+    }
+
+    switch (p_resp->header.f_status)
+    {
+    case STT_SUCCESS:
+      DEBUG(PLUGIN_NAME ": Reply packet status STT_SUCCESS");
+      break;
+    default:
+      ERROR(PLUGIN_NAME
+            ": Reply packet contains error status: %d (expected: %d)",
+            p_resp->header.f_status, STT_SUCCESS);
+      return CHRONY_RC_FAIL;
+    }
+
+    /* Good result */
+    return CHRONY_RC_OK;
+  }
+  while (0);
+
+  /* Some error occured */
+  return CHRONY_RC_FAIL;
+}
+
+
+static void
+chrony_init_req(tChrony_Request * p_req)
+{
+  memset(p_req, 0, sizeof(*p_req));
+  p_req->header.f_version = PROTO_VERSION_NUMBER;
+  p_req->header.f_type    = PKT_TYPE_CMD_REQUEST;
+  p_req->header.f_dummy0  = 0;
+  p_req->header.f_dummy1  = 0;
+  p_req->header.f_dummy2  = 0;
+  p_req->header.f_dummy3  = 0;
+}
+
+
+/* ntohf code originally from: git://git.tuxfamily.org/gitroot/chrony/chrony.git:util.c */
+/* Original code licensed as GPLv2, by Richard P. Purnow, Miroslav Lichvar */
+/* Original name: double UTI_tFloatNetworkToHost(tFloat f) */
+static double
+ntohf(tFloat p_float)
+{
+  /* Convert tFloat in Network-bit-order to double in host-bit-order */
+
+#define FLOAT_EXP_BITS 7
+#define FLOAT_EXP_MIN (-(1 << (FLOAT_EXP_BITS - 1)))
+#define FLOAT_EXP_MAX (-FLOAT_EXP_MIN - 1)
+#define FLOAT_COEF_BITS ((int)sizeof (int32_t) * 8 - FLOAT_EXP_BITS)
+#define FLOAT_COEF_MIN (-(1 << (FLOAT_COEF_BITS - 1)))
+#define FLOAT_COEF_MAX (-FLOAT_COEF_MIN - 1)
+
+  int32_t exp, coef;
+  uint32_t uval;
+
+  uval = ntohl(p_float.value);
+  exp = (uval >> FLOAT_COEF_BITS) - FLOAT_COEF_BITS;
+  if (exp >= 1 << (FLOAT_EXP_BITS - 1))
+    exp -= 1 << FLOAT_EXP_BITS;
+
+  /* coef = (x << FLOAT_EXP_BITS) >> FLOAT_EXP_BITS; */
+  coef = uval % (1U << FLOAT_COEF_BITS);
+  if (coef >= 1 << (FLOAT_COEF_BITS - 1))
+    coef -= 1 << FLOAT_COEF_BITS;
+
+  return coef * pow(2.0, exp);
+}
+
+
+static void
+chrony_push_data(const char *p_type, const char *p_type_inst, double p_value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = p_value;    /* TODO: Check type??? (counter, gauge, derive, absolute) */
+
+  vl.values = values;
+  vl.values_len = 1;
+
+  /* XXX: Shall g_chrony_host/g_chrony_port be reflected in the plugin's output? */
+  /* hostname_g is set in daemon/collectd.c (from config, via gethostname or by resolving localhost) */
+  /* defined as: char hostname_g[DATA_MAX_NAME_LEN]; (never NULL) */
+  sstrncpy(vl.host, hostname_g, sizeof(vl.host));
+  sstrncpy(vl.plugin, PLUGIN_NAME_SHORT, sizeof(vl.plugin));
+  if (g_chrony_plugin_instance != NULL)
+  {
+    sstrncpy(vl.plugin_instance, g_chrony_plugin_instance,
+             sizeof(vl.plugin_instance));
+  }
+  if (p_type != NULL)
+    sstrncpy(vl.type, p_type, sizeof(vl.type));
+
+  if (p_type_inst != NULL)
+    sstrncpy(vl.type_instance, p_type_inst, sizeof(vl.type_instance));
+
+  plugin_dispatch_values(&vl);
+}
+
+
+static void
+chrony_push_data_valid(const char *p_type, const char *p_type_inst, const int p_is_valid,
+                       double p_value)
+{
+  /* Push real value if p_is_valid is true, push NAN if p_is_valid is not true (idea from ntp plugin) */
+  if (p_is_valid == 0)
+    p_value = NAN;
+
+  chrony_push_data(p_type, p_type_inst, p_value);
+}
+
+
+static int
+chrony_init_seq(void)
+{
+  /* Initialize the sequence number generator from /dev/urandom */
+  /* Fallbacks: /dev/random and time(NULL) */
+
+  int fh;
+
+  /* Try urandom */
+  fh = open(URAND_DEVICE_PATH, O_RDONLY);
+  if (fh >= 0)
+  {
+    ssize_t rc = read(fh, &g_chrony_rand, sizeof(g_chrony_rand));
+    if (rc != sizeof(g_chrony_rand))
+    {
+      ERROR(PLUGIN_NAME ": Reading from random source \'%s\'failed: %s (%d)",
+            URAND_DEVICE_PATH, strerror(errno), errno);
+      close(fh);
+      return CHRONY_RC_FAIL;
+    }
+    close(fh);
+    DEBUG(PLUGIN_NAME ": Seeding RNG from " URAND_DEVICE_PATH);
+  }
+  else
+  {
+    if (errno == ENOENT)
+    {
+      /* URAND_DEVICE_PATH device not found. Try RAND_DEVICE_PATH as fall-back */
+      fh = open(RAND_DEVICE_PATH, O_RDONLY);
+      if (fh >= 0)
+      {
+        ssize_t rc = read(fh, &g_chrony_rand, sizeof(g_chrony_rand));
+        if (rc != sizeof(g_chrony_rand))
+        {
+          ERROR(PLUGIN_NAME
+                ": Reading from random source \'%s\'failed: %s (%d)",
+                RAND_DEVICE_PATH, strerror(errno), errno);
+          close(fh);
+          return CHRONY_RC_FAIL;
+        }
+        close(fh);
+        DEBUG(PLUGIN_NAME ": Seeding RNG from " RAND_DEVICE_PATH);
+      }
+      else
+      {
+        /* Error opening RAND_DEVICE_PATH. Try time(NULL) as fall-back */
+        DEBUG(PLUGIN_NAME ": Seeding RNG from time(NULL)");
+        g_chrony_rand = time(NULL) ^ getpid();
+      }
+    }
+    else
+    {
+      ERROR(PLUGIN_NAME ": Opening random source \'%s\' failed: %s (%d)",
+            URAND_DEVICE_PATH, strerror(errno), errno);
+      return CHRONY_RC_FAIL;
+    }
+  }
+
+  return CHRONY_RC_OK;
+}
+
+
+/*****************************************************************************/
+/* Exported functions */
+/*****************************************************************************/
+static int
+chrony_config(const char *p_key, const char *p_value)
+{
+  assert(p_key);
+  assert(p_value);
+
+  /* Parse config variables */
+  if (strcasecmp(p_key, CONFIG_KEY_HOST) == 0)
+  {
+    if (g_chrony_host != NULL)
+      free(g_chrony_host);
+
+    if ((g_chrony_host = strdup(p_value)) == NULL)
+    {
+      ERROR(PLUGIN_NAME ": Error duplicating host name");
+      return CHRONY_RC_FAIL;
+    }
+  }
+  else
+  {
+    if (strcasecmp(p_key, CONFIG_KEY_PORT) == 0)
+    {
+      if (g_chrony_port != NULL)
+        free(g_chrony_port);
+
+      if ((g_chrony_port = strdup(p_value)) == NULL)
+      {
+        ERROR(PLUGIN_NAME ": Error duplicating port name");
+        return CHRONY_RC_FAIL;
+      }
+    }
+    else
+    {
+      if (strcasecmp(p_key, CONFIG_KEY_TIMEOUT) == 0)
+      {
+        time_t tosec = strtol(p_value, NULL, 0);
+        g_chrony_timeout = tosec;
+      }
+      else
+      {
+        WARNING(PLUGIN_NAME ": Unknown configuration variable: %s %s", p_key, p_value);
+        return CHRONY_RC_FAIL;
+      }
+    }
+  }
+  /* XXX: We could set g_chrony_plugin_instance here to "g_chrony_host-g_chrony_port", but as multiple instances aren't yet supported, we skip this for now */
+
+  return CHRONY_RC_OK;
+}
+
+
+static int
+chrony_request_daemon_stats(void)
+{
+  /* Perform Tracking request */
+  int rc;
+  size_t chrony_resp_size;
+  tChrony_Request chrony_req;
+  tChrony_Response chrony_resp;
+
+  chrony_init_req(&chrony_req);
+  rc =
+    chrony_query(REQ_TRACKING, &chrony_req, &chrony_resp, &chrony_resp_size);
+  if (rc != 0)
+  {
+    ERROR(PLUGIN_NAME ": chrony_query (REQ_TRACKING) failed with status %i",
+          rc);
+    return rc;
+  }
+#if COLLECT_DEBUG
+  {
+    char src_addr[IPV6_STR_MAX_SIZE];
+    memset(src_addr, 0, sizeof(src_addr));
+    niptoha(&chrony_resp.body.tracking.addr, src_addr, sizeof(src_addr));
+    DEBUG(PLUGIN_NAME ": Daemon stat: .addr = %s, .ref_id= %u, .stratum = %u, .leap_status = %u, .ref_time = %u:%u:%u, .current_correction = %f, .last_offset = %f, .rms_offset = %f, .freq_ppm = %f, .skew_ppm = %f, .root_delay = %f, .root_dispersion = %f, .last_update_interval = %f", src_addr, ntohs(chrony_resp.body.tracking.f_ref_id),  
+          ntohs(chrony_resp.body.tracking.f_stratum),
+          ntohs(chrony_resp.body.tracking.f_leap_status),
+          ntohl(chrony_resp.body.tracking.f_ref_time.tv_sec_high),
+          ntohl(chrony_resp.body.tracking.f_ref_time.tv_sec_low),
+          ntohl(chrony_resp.body.tracking.f_ref_time.tv_nsec),
+          ntohf(chrony_resp.body.tracking.f_current_correction),
+          ntohf(chrony_resp.body.tracking.f_last_offset),
+          ntohf(chrony_resp.body.tracking.f_rms_offset),
+          ntohf(chrony_resp.body.tracking.f_freq_ppm),
+          ntohf(chrony_resp.body.tracking.f_skew_ppm),
+          ntohf(chrony_resp.body.tracking.f_root_delay),
+          ntohf(chrony_resp.body.tracking.f_root_dispersion),
+          ntohf(chrony_resp.body.tracking.f_last_update_interval));
+  }
+#endif
+
+  double time_ref = ntohl(chrony_resp.body.tracking.f_ref_time.tv_nsec);
+  time_ref /= 1000000000.0;
+  time_ref += ntohl(chrony_resp.body.tracking.f_ref_time.tv_sec_low);
+  if (chrony_resp.body.tracking.f_ref_time.tv_sec_high)
+  {
+    double secs_high =
+      ntohl(chrony_resp.body.tracking.f_ref_time.tv_sec_high);
+    secs_high *= 4294967296.0;
+    time_ref += secs_high;
+  }
+
+  /* Forward results to collectd-daemon */
+  /* Type_instance is always 'chrony' to tag daemon-wide data */
+  /*                Type                Type_instan  Value */
+  chrony_push_data("clock_stratum",     DAEMON_NAME, ntohs(chrony_resp.body.tracking.f_stratum));
+  chrony_push_data("time_ref",          DAEMON_NAME, time_ref);  /* unit: s */
+  chrony_push_data("time_offset_ntp",   DAEMON_NAME, ntohf(chrony_resp.body.tracking.f_current_correction));      /* Offset between system time and NTP, unit: s */
+  chrony_push_data("time_offset",       DAEMON_NAME, ntohf(chrony_resp.body.tracking.f_last_offset)); /* Estimated Offset of the NTP time, unit: s */
+  chrony_push_data("time_offset_rms",   DAEMON_NAME, ntohf(chrony_resp.body.tracking.f_rms_offset));  /* averaged value of the above, unit: s */
+  chrony_push_data("frequency_error",   DAEMON_NAME, ntohf(chrony_resp.body.tracking.f_freq_ppm));    /* Frequency error of the local osc, unit: ppm */
+  chrony_push_data("clock_skew_ppm",    DAEMON_NAME, ntohf(chrony_resp.body.tracking.f_skew_ppm));
+  chrony_push_data("root_delay",        DAEMON_NAME, ntohf(chrony_resp.body.tracking.f_root_delay));  /* Network latency between local daemon and the current source */
+  chrony_push_data("root_dispersion",   DAEMON_NAME, ntohf(chrony_resp.body.tracking.f_root_dispersion));
+  chrony_push_data("clock_last_update", DAEMON_NAME, ntohf(chrony_resp.body.tracking.f_last_update_interval));
+
+  return CHRONY_RC_OK;
+}
+
+
+static int
+chrony_request_sources_count(unsigned int *p_count)
+{
+  /* Requests the number of time sources from the chrony daemon */
+  int rc;
+  size_t chrony_resp_size;
+  tChrony_Request chrony_req;
+  tChrony_Response chrony_resp;
+
+  DEBUG(PLUGIN_NAME ": Requesting data");
+  chrony_init_req(&chrony_req);
+  rc =
+    chrony_query(REQ_N_SOURCES, &chrony_req, &chrony_resp, &chrony_resp_size);
+  if (rc != 0)
+  {
+    ERROR(PLUGIN_NAME ": chrony_query (REQ_N_SOURCES) failed with status %i",
+          rc);
+    return rc;
+  }
+
+  *p_count = ntohl(chrony_resp.body.n_sources.f_n_sources);
+  DEBUG(PLUGIN_NAME ": Getting data of %d clock sources", *p_count);
+
+  return CHRONY_RC_OK;
+}
+
+
+static int
+chrony_request_source_data(int p_src_idx, int *p_is_reachable)
+{
+  /* Perform Source data request for source #p_src_idx */
+  int rc;
+  size_t chrony_resp_size;
+  tChrony_Request chrony_req;
+  tChrony_Response chrony_resp;
+
+  char src_addr[IPV6_STR_MAX_SIZE];
+  memset(src_addr, 0, sizeof(src_addr));
+
+  chrony_init_req(&chrony_req);
+  chrony_req.body.source_data.f_index = htonl(p_src_idx);
+  rc =
+    chrony_query(REQ_SOURCE_DATA, &chrony_req, &chrony_resp,
+                 &chrony_resp_size);
+  if (rc != 0)
+  {
+    ERROR(PLUGIN_NAME
+          ": chrony_query (REQ_SOURCE_DATA) failed with status %i", rc);
+    return rc;
+  }
+
+  niptoha(&chrony_resp.body.source_data.addr, src_addr, sizeof(src_addr));
+  DEBUG(PLUGIN_NAME
+        ": Source[%d] data: .addr = %s, .poll = %u, .stratum = %u, .state = %u, .mode = %u, .flags = %u, .reach = %u, .latest_meas_ago = %u, .orig_latest_meas = %f, .latest_meas = %f, .latest_meas_err = %f",
+        p_src_idx, src_addr, ntohs(chrony_resp.body.source_data.f_poll),
+        ntohs(chrony_resp.body.source_data.f_stratum),
+        ntohs(chrony_resp.body.source_data.f_state),
+        ntohs(chrony_resp.body.source_data.f_mode),
+        ntohs(chrony_resp.body.source_data.f_flags),
+        ntohs(chrony_resp.body.source_data.f_reachability),
+        ntohl(chrony_resp.body.source_data.f_since_sample),
+        ntohf(chrony_resp.body.source_data.f_origin_latest_meas),
+        ntohf(chrony_resp.body.source_data.f_latest_meas),
+        ntohf(chrony_resp.body.source_data.f_latest_meas_err));
+
+  /* Push NaN if source is currently not reachable */
+  int is_reachable =
+    ntohs(chrony_resp.body.source_data.f_reachability) & 0x01;
+  *p_is_reachable = is_reachable;
+
+  /* Forward results to collectd-daemon */
+  chrony_push_data_valid("clock_stratum", src_addr, is_reachable,
+                         ntohs(chrony_resp.body.source_data.f_stratum));
+  chrony_push_data_valid("clock_state", src_addr, is_reachable,
+                         ntohs(chrony_resp.body.source_data.f_state));
+  chrony_push_data_valid("clock_mode", src_addr, is_reachable,
+                         ntohs(chrony_resp.body.source_data.f_mode));
+  chrony_push_data_valid("clock_reachability", src_addr, is_reachable,
+                         ntohs(chrony_resp.body.source_data.f_reachability));
+  chrony_push_data_valid("clock_last_meas", src_addr, is_reachable,
+                         ntohs(chrony_resp.body.source_data.f_since_sample));
+
+  return CHRONY_RC_OK;
+}
+
+
+static int
+chrony_request_source_stats(int p_src_idx, const int *p_is_reachable)
+{
+  /* Perform Source stats request for source #p_src_idx */
+  int rc;
+  size_t chrony_resp_size;
+  tChrony_Request chrony_req;
+  tChrony_Response chrony_resp;
+  double skew_ppm, frequency_error, time_offset;
+
+  char src_addr[IPV6_STR_MAX_SIZE];
+  memset(src_addr, 0, sizeof(src_addr));
+
+  if (*p_is_reachable == 0)
+  {
+    skew_ppm = 0;
+    frequency_error = 0;
+    time_offset = 0;
+  }
+  else
+  {
+    chrony_init_req(&chrony_req);
+    chrony_req.body.source_stats.f_index = htonl(p_src_idx);
+    rc =
+      chrony_query(REQ_SOURCE_STATS, &chrony_req, &chrony_resp,
+                   &chrony_resp_size);
+    if (rc != 0)
+    {
+      ERROR(PLUGIN_NAME
+            ": chrony_query (REQ_SOURCE_STATS) failed with status %i", rc);
+      return rc;
+    }
+
+    skew_ppm = ntohf(chrony_resp.body.source_stats.f_skew_ppm);
+    frequency_error =
+      ntohf(chrony_resp.body.source_stats.f_rtc_gain_rate_ppm);
+    time_offset = ntohf(chrony_resp.body.source_stats.f_est_offset);
+
+    niptoha(&chrony_resp.body.source_stats.addr, src_addr, sizeof(src_addr));
+    DEBUG(PLUGIN_NAME
+          ": Source[%d] stat: .addr = %s, .ref_id= %u, .n_samples = %u, "
+          ".n_runs = %u, .span_seconds = %u, .rtc_seconds_fast = %f, "
+          ".rtc_gain_rate_ppm = %f, .skew_ppm= %f, .est_offset = %f, .est_offset_err = %f",
+          p_src_idx, src_addr,
+          ntohl(chrony_resp.body.source_stats.f_ref_id),
+          ntohl(chrony_resp.body.source_stats.f_n_samples),
+          ntohl(chrony_resp.body.source_stats.f_n_runs),
+          ntohl(chrony_resp.body.source_stats.f_span_seconds),
+          ntohf(chrony_resp.body.source_stats.f_rtc_seconds_fast),
+          frequency_error, skew_ppm, time_offset,
+          ntohf(chrony_resp.body.source_stats.f_est_offset_err));
+
+  } /* if (*is_reachable) */
+
+  /* Forward results to collectd-daemon */
+  chrony_push_data_valid("clock_skew_ppm", src_addr, *p_is_reachable, skew_ppm);
+  chrony_push_data_valid("frequency_error", src_addr, *p_is_reachable, frequency_error); /* unit: ppm */
+  chrony_push_data_valid("time_offset", src_addr, *p_is_reachable, time_offset);         /* unit: s */
+
+  return CHRONY_RC_OK;
+}
+
+
+static int
+chrony_read(void)
+{
+  /* collectd read callback: Perform data acquisition */
+  int rc;
+  unsigned int now_src, n_sources;
+
+  if (g_chrony_seq_is_initialized == 0)
+  {
+    /* Seed RNG for sequence number generation */
+    rc = chrony_init_seq();
+    if (rc != CHRONY_RC_OK)
+      return rc;
+
+    g_chrony_seq_is_initialized = 1;
+  }
+
+  /* Get daemon stats */
+  rc = chrony_request_daemon_stats();
+  if (rc != CHRONY_RC_OK)
+    return rc;
+
+  /* Get number of time sources, then check every source for status */
+  rc = chrony_request_sources_count(&n_sources);
+  if (rc != CHRONY_RC_OK)
+    return rc;
+
+  for (now_src = 0; now_src < n_sources; ++now_src)
+  {
+    int is_reachable;
+    rc = chrony_request_source_data(now_src, &is_reachable);
+    if (rc != CHRONY_RC_OK)
+      return rc;
+
+    rc = chrony_request_source_stats(now_src, &is_reachable);
+    if (rc != CHRONY_RC_OK)
+      return rc;
+
+  }
+  return CHRONY_RC_OK;
+}
+
+
+static int
+chrony_shutdown(void)
+{
+  /* Collectd shutdown callback: Free mem */
+  if (g_chrony_is_connected != 0)
+  {
+    close(g_chrony_socket);
+    g_chrony_is_connected = 0;
+  }
+  if (g_chrony_host != NULL)
+    sfree(g_chrony_host);
+
+  if (g_chrony_port != NULL)
+    sfree(g_chrony_port);
+  
+  if (g_chrony_plugin_instance != NULL)
+    sfree(g_chrony_plugin_instance);
+  
+  return CHRONY_RC_OK;
+}
+
+
+void
+module_register(void)
+{
+  plugin_register_config(PLUGIN_NAME_SHORT, chrony_config, g_config_keys,
+                         g_config_keys_num);
+  plugin_register_read(PLUGIN_NAME_SHORT, chrony_read);
+  plugin_register_shutdown(PLUGIN_NAME_SHORT, chrony_shutdown);
+}
index 282098e..20375e2 100644 (file)
@@ -33,7 +33,7 @@ with the plugin:
 
 =over 4
 
-=item
+=item *
 
 E-Mail type (e.g. "ham", "spam", "virus", ...) and size (bytes):
 
@@ -41,13 +41,13 @@ E-Mail type (e.g. "ham", "spam", "virus", ...) and size (bytes):
 
 If C<size> is less than or equal to zero, C<size> is ignored.
 
-=item
+=item *
 
 Spam score:
 
   s:<value>
 
-=item
+=item *
 
 Successful spam checks (e.g. "BAYES_99", "SUBJECT_DRUG_GAP_C", ...):
 
index 654db40..c65966b 100644 (file)
@@ -281,12 +281,12 @@ to make use of collectd's more powerful interface.
 
 =over 4
 
-=item
+=item *
 
 The user, the binary is executed as, may not have root privileges, i.E<nbsp>e.
 must have an UID that is non-zero. This is for your own good.
 
-=item
+=item *
 
 Early versions of the plugin did not use a command but treated all lines as if
 they were arguments to the I<PUTVAL> command. When the I<PUTNOTIF> command was
index fe56d5d..dd90737 100644 (file)
@@ -618,9 +618,9 @@ static int do_check (lcc_connection_t *connection)
 
        status = RET_UNKNOWN;
        if (consolitation_g == CON_NONE)
-               status =  do_check_con_none (values_num, values, values_names);
+               status = do_check_con_none (values_num, values, values_names);
        else if (consolitation_g == CON_AVERAGE)
-               status =  do_check_con_average (values_num, values, values_names);
+               status = do_check_con_average (values_num, values, values_names);
        else if (consolitation_g == CON_SUM)
                status = do_check_con_sum (values_num, values, values_names);
        else if (consolitation_g == CON_PERCENTAGE)
index 7308648..0102e92 100644 (file)
@@ -705,7 +705,7 @@ types used by the read, write and match functions.
 
 =over 4
 
-=item
+=item *
 
 Please feel free to send in new plugins to collectd's mailing list at
 E<lt>collectdE<nbsp>atE<nbsp>collectd.orgE<gt> for review and, possibly,
@@ -721,7 +721,7 @@ L<http://collectd.org/dev-info.shtml>.
 
 =over 4
 
-=item
+=item *
 
 collectd is heavily multi-threaded. Each collectd thread accessing the perl
 plugin will be mapped to a Perl interpreter thread (see L<threads(3perl)>).
@@ -733,7 +733,7 @@ registered callback may be called more than once in parallel). Please note
 that no data is shared between threads by default. You have to use the
 B<threads::shared> module to do so.
 
-=item
+=item *
 
 Each function name registered with collectd has to be available before the
 first thread has been created (i.E<nbsp>e. basically at compile time). This
@@ -742,14 +742,14 @@ C<*foo = \&bar; plugin_register (TYPE_READ, "plugin", "foo");> most likely
 will not work. This is due to the fact that the symbol table is not shared
 across different threads.
 
-=item
+=item *
 
 Each plugin is usually only loaded once and kept in memory for performance
 reasons. Therefore, END blocks are only executed once when collectd shuts
 down. You should not rely on END blocks anyway - use B<shutdown functions>
 instead.
 
-=item
+=item *
 
 The perl plugin exports the internal API of collectd which is considered
 unstable and subject to change at any time. We try hard to not break backwards
@@ -763,7 +763,7 @@ dispatched by the perl plugin after upgrades.
 
 =over 4
 
-=item
+=item *
 
 Currently, it is not possible to flush a single Perl plugin only. You can
 either flush all Perl plugins or none at all and you have to use C<perl> as
index 0da891f..773a6cd 100644 (file)
@@ -87,12 +87,12 @@ collectd script as well as from a normal Python interpreter:
 
 =over 4
 
-=item
+=item *
 
 B<1.> collectd will try to import the B<readline> module to give you a decent
 way of entering your commands. The daemonized collectd won't do that.
 
-=item
+=item *
 
 B<2.> collectd will block I<SIGINT>. Pressing I<Ctrl+C> will usually cause
 collectd to shut down. This would be problematic in an interactive session,
@@ -102,7 +102,7 @@ exception either.
 
 To quit collectd send I<EOF> (press I<Ctrl+D> at the beginning of a new line).
 
-=item
+=item *
 
 B<3.> collectd handles I<SIGCHLD>. This means that Python won't be able to
 determine the return code of spawned processes with system(), popen() and
@@ -515,18 +515,18 @@ different events. With one exception all of them are called as shown above.
 
 =over 4
 
-=item
+=item *
 
 I<callback> is a callable object that will be called every time the event is
 triggered.
 
-=item
+=item *
 
 I<data> is an optional object that will be passed back to the callback function
 every time it is called. If you omit this parameter no object is passed back to
 your callback, not even None.
 
-=item
+=item *
 
 I<name> is an optional identifier for this callback. The default name is
 B<python>.I<module>. I<module> is taken from the B<__module__> attribute of
@@ -534,7 +534,7 @@ your callback function. Every callback needs a unique identifier, so if you
 want to register the same callback multiple times in the same module you need to
 specify a name here. Otherwise it's safe to ignore this parameter.
 
-=item
+=item *
 
 I<identifier> is the full identifier assigned to this callback.
 
@@ -637,7 +637,7 @@ A float or None, the maximum value.
 
 =back
 
-=item B<flush>(I<plugin>[, I<timeout>][, I<identifier>]) -> None
+=item B<flush>(I<plugin[, timeout][, identifier]) -> None
 
 Flush one or all plugins. I<timeout> and the specified I<identifiers> are
 passed on to the registered flush-callbacks. If omitted, the timeout defaults
@@ -681,7 +681,7 @@ types used by the read, write and match functions.
 
 =over 4
 
-=item
+=item *
 
 Please feel free to send in new plugins to collectd's mailing list at
 E<lt>collectdE<nbsp>atE<nbsp>collectd.orgE<gt> for review and, possibly,
@@ -697,7 +697,7 @@ L<http://collectd.org/dev-info.shtml>.
 
 =over 4
 
-=item
+=item *
 
 collectd is heavily multi-threaded. Each collectd thread accessing the Python
 plugin will be mapped to a Python interpreter thread. Any such thread will be
@@ -707,13 +707,13 @@ Hence, any plugin has to be thread-safe if it provides several entry points
 from collectd (i.E<nbsp>e. if it registers more than one callback or if a
 registered callback may be called more than once in parallel).
 
-=item
+=item *
 
 The Python thread module is initialized just before calling the init callbacks.
 This means you must not use Python's threading module prior to this point. This
 includes all config and possibly other callback as well.
 
-=item
+=item *
 
 The python plugin exports the internal API of collectd which is considered
 unstable and subject to change at any time. We try hard to not break backwards
@@ -727,7 +727,7 @@ dispatched by the python plugin after upgrades.
 
 =over 4
 
-=item
+=item *
 
 Not all aspects of the collectd API are accessible from Python. This includes
 but is not limited to filters and data sets.
index bd8b256..aeddc05 100644 (file)
 #@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind
 #@BUILD_PLUGIN_CEPH_TRUE@LoadPlugin ceph
 #@BUILD_PLUGIN_CGROUPS_TRUE@LoadPlugin cgroups
+#@BUILD_PLUGIN_CHRONY_TRUE@LoadPlugin chrony
 #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack
 #@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 #@BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount
 #@BUILD_PLUGIN_FSCACHE_TRUE@LoadPlugin fscache
 #@BUILD_PLUGIN_GMOND_TRUE@LoadPlugin gmond
+#@BUILD_PLUGIN_GRPC_TRUE@LoadPlugin grpc
 #@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
 @BUILD_PLUGIN_INTERFACE_TRUE@@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
 #@BUILD_PLUGIN_IPC_TRUE@LoadPlugin ipc
 #  </Daemon>
 #</Plugin>
 
+#<Plugin chrony>
+#      Host    "localhost"
+#      Port    "323"
+#      Timeout "2"
+#</Plugin>
+
 #<Plugin cgroups>
 #  CGroup "libvirt"
 #  IgnoreSelected false
 #  </Metric>
 #</Plugin>
 
+#<Plugin grpc>
+#      WorkerThreads 5
+#      <Listen "0.0.0.0" "50051">
+#              EnableSSL true
+#              SSLRootCerts "/path/to/root.pem"
+#              SSLServerCert "/path/to/server.pem"
+#              SSLServerKey "/path/to/server.key"
+#      </Listen>
+#</Plugin>
+
 #<Plugin hddtemp>
 #  Host "127.0.0.1"
 #  Port "7634"
index e18ef45..c3e239b 100644 (file)
@@ -404,13 +404,13 @@ There are a couple of limitations you should be aware of:
 
 =over 4
 
-=item
+=item *
 
 The I<Type> cannot be left unspecified, because it is not reasonable to add
 apples to oranges. Also, the internal lookup structure won't work if you try
 to group by type.
 
-=item
+=item *
 
 There must be at least one unspecified, ungrouped field. Otherwise nothing
 will be aggregated.
@@ -485,19 +485,19 @@ This will create the files:
 
 =over 4
 
-=item
+=item *
 
 foo.example.com/cpu-even-average/cpu-idle
 
-=item
+=item *
 
 foo.example.com/cpu-even-average/cpu-system
 
-=item
+=item *
 
 foo.example.com/cpu-even-average/cpu-user
 
-=item
+=item *
 
 ...
 
@@ -1361,6 +1361,32 @@ at all, B<all> cgroups are selected.
 
 =back
 
+=head2 Plugin C<chrony>
+
+The C<chrony> plugin collects ntp data from a B<chronyd> server, such as clock
+skew and per-peer stratum.
+
+For talking to B<chronyd>, it mimics what the B<chronyc> control program does
+on the wire.
+
+Available configuration options for the C<chrony> plugin:
+
+=over 4
+
+=item B<Host> I<Hostname>
+
+Hostname of the host running B<chronyd>. Defaults to B<localhost>.
+
+=item B<Port> I<Port>
+
+UDP-Port to connect to. Defaults to B<323>.
+
+=item B<Timeout> I<Timeout>
+
+Connection timeout in seconds. Defaults to B<2>.
+
+=back
+
 =head2 Plugin C<conntrack>
 
 This plugin collects IP conntrack statistics.
@@ -1381,11 +1407,11 @@ as Jiffies, using the C<cpu> type. Two aggregations are available:
 
 =over 4
 
-=item
+=item *
 
 Sum, per-state, over all CPUs installed in the system; and
 
-=item
+=item *
 
 Sum, per-CPU, over all non-idle states of a CPU, creating an "active" state.
 
@@ -1460,6 +1486,92 @@ number.
 
 =back
 
+=head2 cURL Statistics
+
+All cURL-based plugins support collection of generic, request-based
+statistics. These are disabled by default and can be enabled selectively for
+each page or URL queried from the curl, curl_json, or curl_xml plugins. See
+the documentation of those plugins for specific information. This section
+describes the available metrics that can be configured for each plugin. All
+options are disabled by default.
+
+See L<http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html> for more details.
+
+=over 4
+
+=item B<TotalTime> B<true|false>
+
+Total time of the transfer, including name resolving, TCP connect, etc.
+
+=item B<NamelookupTime> B<true|false>
+
+Time it took from the start until name resolving was completed.
+
+=item B<ConnectTime> B<true|false>
+
+Time it took from the start until the connect to the remote host (or proxy)
+was completed.
+
+=item B<AppconnectTime> B<true|false>
+
+Time it took from the start until the SSL/SSH connect/handshake to the remote
+host was completed.
+
+=item B<PretransferTime> B<true|false>
+
+Time it took from the start until just before the transfer begins.
+
+=item B<StarttransferTime> B<true|false>
+
+Time it took from the start until the first byte was received.
+
+=item B<RedirectTime> B<true|false>
+
+Time it took for all redirection steps include name lookup, connect,
+pre-transfer and transfer before final transaction was started.
+
+=item B<RedirectCount> B<true|false>
+
+The total number of redirections that were actually followed.
+
+=item B<SizeUpload> B<true|false>
+
+The total amount of bytes that were uploaded.
+
+=item B<SizeDownload> B<true|false>
+
+The total amount of bytes that were downloaded.
+
+=item B<SpeedDownload> B<true|false>
+
+The average download speed that curl measured for the complete download.
+
+=item B<SpeedUpload> B<true|false>
+
+The average upload speed that curl measured for the complete upload.
+
+=item B<HeaderSize> B<true|false>
+
+The total size of all the headers received.
+
+=item B<RequestSize> B<true|false>
+
+The total size of the issued requests.
+
+=item B<ContentLengthDownload> B<true|false>
+
+The content-length of the download.
+
+=item B<ContentLengthUpload> B<true|false>
+
+The specified size of the upload.
+
+=item B<NumConnects> B<true|false>
+
+The number of new connections that were created to achieve the transfer.
+
+=back
+
 =head2 Plugin C<curl>
 
 The curl plugin uses the B<libcurl> (L<http://curl.haxx.se/>) to read web pages
@@ -1560,11 +1672,21 @@ Beware that requests will get aborted if they take too long to complete. Adjust
 B<Timeout> accordingly if you expect B<MeasureResponseTime> to report such slow
 requests.
 
+This option is similar to enabling the B<TotalTime> statistic but it's
+measured by collectd instead of cURL.
+
 =item B<MeasureResponseCode> B<true>|B<false>
 
 Measure response code for the request. If this setting is enabled, B<Match>
 blocks (see below) are optional. Disabled by default.
 
+=item B<E<lt>StatisticsE<gt>>
+
+One B<Statistics> block can be used to specify cURL statistics to be collected
+for each request to the remote web site. See the section "cURL Statistics"
+above for details. If this setting is enabled, B<Match> blocks (see below) are
+optional.
+
 =item B<E<lt>MatchE<gt>>
 
 One or more B<Match> blocks that define how to match information in the data
@@ -1677,6 +1799,12 @@ URL. By default the global B<Interval> setting will be used.
 These options behave exactly equivalent to the appropriate options of the
 I<cURL> plugin. Please see there for a detailed description.
 
+=item B<E<lt>StatisticsE<gt>>
+
+One B<Statistics> block can be used to specify cURL statistics to be collected
+for each request to the remote URL. See the section "cURL Statistics" above
+for details.
+
 =back
 
 The following options are valid within B<Key> blocks:
@@ -1779,6 +1907,12 @@ Examples:
 These options behave exactly equivalent to the appropriate options of the
 I<cURL plugin>. Please see there for a detailed description.
 
+=item B<E<lt>StatisticsE<gt>>
+
+One B<Statistics> block can be used to specify cURL statistics to be collected
+for each request to the remote URL. See the section "cURL Statistics" above
+for details.
+
 =item E<lt>B<XPath> I<XPath-expression>E<gt>
 
 Within each B<URL> block, there must be one or more B<XPath> blocks. Each
@@ -2506,6 +2640,51 @@ source, this is optional. Otherwise the option is required.
 
 =back
 
+=head2 Plugin C<grpc>
+
+The I<grpc> plugin provides an RPC interface to submit values to or query
+values from collectd based on the open source gRPC framework. It exposes an
+end-point for dispatching values to the daemon.
+
+The B<gRPC> homepage can be found at L<https://grpc.io/>.
+
+=over 4
+
+=item B<Listen> I<Host> I<Port>
+
+The B<Listen> statement sets the network address to bind to. When multiple
+statements are specified, the daemon will bind to all of them. If none are
+specified, it defaults to B<0.0.0.0:50051>.
+
+The argument I<Host> may be a hostname, an IPv4 address, or an IPv6 address.
+
+Optionally, B<Listen> may be specified as a configuration block which
+supports the following options:
+
+=over 4
+
+=item B<EnableSSL> I<true>|I<false>
+
+Whether to enable SSL for incoming connections. Default: false.
+
+=item B<SSLRootCerts> I<Filename>
+
+=item B<SSLServerKey> I<Filename>
+
+=item B<SSLServerCert> I<Filename>
+
+Filenames specifying SSL certificate and key material to be used with SSL
+connections.
+
+=back
+
+=item B<WorkerThreads> I<Num>
+
+Number of threads to start for handling incoming connections. The default
+value is B<5>.
+
+=back
+
 =head2 Plugin C<hddtemp>
 
 To get values from B<hddtemp> collectd connects to B<localhost> (127.0.0.1),
@@ -4509,11 +4688,11 @@ Sets the I<command file> to write to. Defaults to F</usr/local/nagios/var/rw/nag
 
 =head2 Plugin C<ntpd>
 
-The C<ntpd> plugin collects per-peer ntpd data such as time offset and time
+The C<ntpd> plugin collects per-peer ntp data such as time offset and time
 dispersion.
 
 For talking to B<ntpd>, it mimics what the B<ntpdc> control program does on
-wire - using B<mode 7> specific requests. This mode is deprecated with
+the wire - using B<mode 7> specific requests. This mode is deprecated with
 newer B<ntpd> releases (4.2.7p230 and later). For the C<ntpd> plugin to work
 correctly with them, the ntp daemon must be explicitly configured to
 enable B<mode 7> (which is disabled by default). Refer to the I<ntp.conf(5)>
@@ -5140,13 +5319,12 @@ The B<Query> block defines one database query which may later be used by a
 database definition. It accepts a single mandatory argument which specifies
 the name of the query. The names of all queries have to be unique (see the
 B<MinVersion> and B<MaxVersion> options below for an exception to this
-rule). The following configuration options are available to define the query:
+rule).
 
-In each B<Query> block, there is one or more B<Result> blocks. B<Result>
-blocks define how to handle the values returned from the query. They define
-which column holds which value and how to dispatch that value to the daemon.
-Multiple B<Result> blocks may be used to extract multiple values from a single
-query.
+In each B<Query> block, there is one or more B<Result> blocks. Multiple
+B<Result> blocks may be used to extract multiple values from a single query.
+
+The following configuration options are available to define the query:
 
 =over 4
 
@@ -5164,7 +5342,7 @@ allowed as long as a single non-empty command has been specified only.
 
 The returned lines will be handled separately one after another.
 
-=item B<Param> I<hostname>|I<database>|I<username>|I<interval>
+=item B<Param> I<hostname>|I<database>|I<instance>|I<username>|I<interval>
 
 Specify the parameters which should be passed to the SQL query. The parameters
 are referred to in the SQL query as B<$1>, B<$2>, etc. in the same order as
@@ -5201,6 +5379,34 @@ specific or global B<Interval> options).
 Please note that parameters are only supported by PostgreSQL's protocol
 version 3 and above which was introduced in version 7.4 of PostgreSQL.
 
+=item B<PluginInstanceFrom> I<column>
+
+Specify how to create the "PluginInstance" for reporting this query results.
+Only one column is supported. You may concatenate fields and string values in
+the query statement to get the required results.
+
+=item B<MinVersion> I<version>
+
+=item B<MaxVersion> I<version>
+
+Specify the minimum or maximum version of PostgreSQL that this query should be
+used with. Some statistics might only be available with certain versions of
+PostgreSQL. This allows you to specify multiple queries with the same name but
+which apply to different versions, thus allowing you to use the same
+configuration in a heterogeneous environment.
+
+The I<version> has to be specified as the concatenation of the major, minor
+and patch-level versions, each represented as two-decimal-digit numbers. For
+example, version 8.2.3 will become 80203.
+
+=back
+
+The B<Result> block defines how to handle the values returned from the query.
+It defines which column holds which value and how to dispatch that value to
+the daemon.
+
+=over 4
+
 =item B<Type> I<type>
 
 The I<type> name to be used when dispatching the values. The type describes
@@ -5208,7 +5414,7 @@ how to handle the data and where to store it. See L<types.db(5)> for more
 details on types and their configuration. The number and type of values (as
 selected by the B<ValuesFrom> option) has to match the type of the given name.
 
-This option is required inside a B<Result> block.
+This option is mandatory.
 
 =item B<InstancePrefix> I<prefix>
 
@@ -5244,20 +5450,6 @@ This option is required inside a B<Result> block and may be specified multiple
 times. If multiple B<ValuesFrom> options are specified, the columns are read
 in the given order.
 
-=item B<MinVersion> I<version>
-
-=item B<MaxVersion> I<version>
-
-Specify the minimum or maximum version of PostgreSQL that this query should be
-used with. Some statistics might only be available with certain versions of
-PostgreSQL. This allows you to specify multiple queries with the same name but
-which apply to different versions, thus allowing you to use the same
-configuration in a heterogeneous environment.
-
-The I<version> has to be specified as the concatenation of the major, minor
-and patch-level versions, each represented as two-decimal-digit numbers. For
-example, version 8.2.3 will become 80203.
-
 =back
 
 The following predefined queries are available (the definitions can be found
@@ -5427,6 +5619,8 @@ Specify the plugin instance name that should be used instead of the database
 name (which is the default, if this option has not been specified). This
 allows to query multiple databases of the same name on the same host (e.g.
 when running multiple database server versions in parallel).
+The plugin instance name can also be set from the query result using
+the B<PluginInstanceFrom> option in B<Query> block.
 
 =item B<Host> I<hostname>
 
@@ -6271,6 +6465,22 @@ collected. If at least one B<Disk> option is given and no B<IgnoreSelected> or
 set to B<false>, B<only> matching disks will be collected. If B<IgnoreSelected>
 is set to B<true>, all disks are collected B<except> the ones matched.
 
+=item B<IgnoreSleepMode> B<true>|B<false>
+
+Normally, the C<smart> plugin will ignore disks that are reported to be asleep.
+This option disables the sleep mode check and allows the plugin to collect data
+from these disks anyway. This is useful in cases where libatasmart mistakenly
+reports disks as asleep because it has not been updated to incorporate support
+for newer idle states in the ATA spec.
+
+=item B<UseSerial> B<true>|B<false>
+
+A disk's kernel name (e.g., sda) can change from one boot to the next. If this
+option is enabled, the C<smart> plugin will use the disk's serial number (e.g.,
+HGST_HUH728080ALE600_2EJ8VH8X) instead of the kernel name as the key for
+storing data. This ensures that the data for a given disk will be kept together
+even if the kernel name changes.
+
 =back
 
 =head2 Plugin C<snmp>
@@ -7008,20 +7218,20 @@ The following methods are used to find the machine's UUID, in order:
 
 =over 4
 
-=item
+=item *
 
 Check I</etc/uuid> (or I<UUIDFile>).
 
-=item
+=item *
 
 Check for UUID from HAL (L<http://www.freedesktop.org/wiki/Software/hal>) if
 present.
 
-=item
+=item *
 
 Check for UUID from C<dmidecode> / SMBIOS.
 
-=item
+=item *
 
 Check for UUID from Xen hypervisor.
 
index fb0403b..8e68fc0 100644 (file)
@@ -72,7 +72,7 @@ There are two big groups of plugins, B<input> and B<output> plugins:
 
 =over 4
 
-=item
+=item *
 
 Input plugins are queried periodically. They somehow acquire the current value
 of whatever they where designed to work with and submit these values back to
@@ -80,7 +80,7 @@ the daemon, i. e. they "dispatch" the values. As an example, the C<cpu plugin>
 reads the current cpu-counters of time spent in the various modes (user,
 system, nice, ...) and dispatches these counters to the daemon.
 
-=item
+=item *
 
 Output plugins get the dispatched values from the daemon and does something
 with them. Common applications are writing to RRD-files, CSV-files or sending
index 49a3355..486b001 100644 (file)
@@ -135,7 +135,7 @@ static int conntrack_read (void)
 
 void module_register (void)
 {
-    plugin_register_config ("conntrack", conntrack_config,
+       plugin_register_config ("conntrack", conntrack_config,
                             config_keys, config_keys_num);
        plugin_register_read ("conntrack", conntrack_read);
 } /* void module_register */
index 7f298a2..cf2e816 100644 (file)
@@ -25,6 +25,7 @@
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
+#include "utils_curl_stats.h"
 #include "utils_match.h"
 #include "utils_time.h"
 
@@ -67,6 +68,7 @@ struct web_page_s /* {{{ */
   _Bool response_time;
   _Bool response_code;
   int timeout;
+  curl_stats_t *stats;
 
   CURL *curl;
   char curl_errbuf[CURL_ERROR_SIZE];
@@ -156,6 +158,7 @@ static void cc_web_page_free (web_page_t *wp) /* {{{ */
   sfree (wp->cacert);
   sfree (wp->post_body);
   curl_slist_free_all (wp->headers);
+  curl_stats_destroy (wp->stats);
 
   sfree (wp->buffer);
 
@@ -453,6 +456,7 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
   page->response_time = 0;
   page->response_code = 0;
   page->timeout = -1;
+  page->stats = NULL;
 
   page->instance = strdup (ci->values[0].value.string);
   if (page->instance == NULL)
@@ -495,6 +499,11 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string (child, &page->post_body);
     else if (strcasecmp ("Timeout", child->key) == 0)
       status = cf_util_get_int (child, &page->timeout);
+    else if (strcasecmp ("Statistics", child->key) == 0) {
+      page->stats = curl_stats_from_config (child);
+      if (page->stats == NULL)
+        status = -1;
+    }
     else
     {
       WARNING ("curl plugin: Option `%s' not allowed here.", child->key);
@@ -514,12 +523,13 @@ static int cc_config_add_page (oconfig_item_t *ci) /* {{{ */
       status = -1;
     }
 
-    if (page->matches == NULL && !page->response_time && !page->response_code)
+    if (page->matches == NULL && page->stats == NULL
+        && !page->response_time && !page->response_code)
     {
       assert (page->instance != NULL);
       WARNING ("curl plugin: No (valid) `Match' block "
-          "or MeasureResponseTime or MeasureResponseCode within "
-          "`Page' block `%s'.", page->instance);
+          "or Statistics or MeasureResponseTime or MeasureResponseCode "
+          "within `Page' block `%s'.", page->instance);
       status = -1;
     }
 
@@ -675,6 +685,8 @@ static int cc_read_page (web_page_t *wp) /* {{{ */
 
   if (wp->response_time)
     cc_submit_response_time (wp, cdtime() - start);
+  if (wp->stats != NULL)
+    curl_stats_dispatch (wp->stats, wp->curl, hostname_g, "curl", wp->instance);
 
   if(wp->response_code)
   {
index b19730b..55db387 100644 (file)
@@ -27,6 +27,7 @@
 #include "configfile.h"
 #include "utils_avltree.h"
 #include "utils_complain.h"
+#include "utils_curl_stats.h"
 
 #include <sys/types.h>
 #include <sys/un.h>
@@ -78,6 +79,7 @@ struct cj_s /* {{{ */
   char *post_body;
   cdtime_t interval;
   int timeout;
+  curl_stats_t *stats;
 
   CURL *curl;
   char curl_errbuf[CURL_ERROR_SIZE];
@@ -432,6 +434,7 @@ static void cj_free (void *arg) /* {{{ */
   sfree (db->cacert);
   sfree (db->post_body);
   curl_slist_free_all (db->headers);
+  curl_stats_destroy (db->stats);
 
   sfree (db);
 } /* }}} void cj_free */
@@ -726,6 +729,12 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_cdtime(child, &db->interval);
     else if (strcasecmp ("Timeout", child->key) == 0)
       status = cf_util_get_int (child, &db->timeout);
+    else if (strcasecmp ("Statistics", child->key) == 0)
+    {
+      db->stats = curl_stats_from_config (child);
+      if (db->stats == NULL)
+        status = -1;
+    }
     else
     {
       WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
@@ -823,21 +832,22 @@ static int cj_config (oconfig_item_t *ci) /* {{{ */
 
 /* }}} End of configuration handling functions */
 
+static const char *cj_host (cj_t *db) /* {{{ */
+{
+  if ((db->host == NULL)
+      || (strcmp ("", db->host) == 0)
+      || (strcmp (CJ_DEFAULT_HOST, db->host) == 0))
+    return hostname_g;
+  return db->host;
+} /* }}} cj_host */
+
 static void cj_submit (cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
 {
   value_list_t vl = VALUE_LIST_INIT;
-  char *host;
 
   vl.values     = value;
   vl.values_len = 1;
 
-  if ((db->host == NULL)
-      || (strcmp ("", db->host) == 0)
-      || (strcmp (CJ_DEFAULT_HOST, db->host) == 0))
-    host = hostname_g;
-  else
-    host = db->host;
-
   if (key->instance == NULL)
   {
     int i, len = 0;
@@ -848,7 +858,7 @@ static void cj_submit (cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
   else
     sstrncpy (vl.type_instance, key->instance, sizeof (vl.type_instance));
 
-  sstrncpy (vl.host, host, sizeof (vl.host));
+  sstrncpy (vl.host, cj_host (db), sizeof (vl.host));
   sstrncpy (vl.plugin, "curl_json", sizeof (vl.plugin));
   sstrncpy (vl.plugin_instance, db->instance, sizeof (vl.plugin_instance));
   sstrncpy (vl.type, key->type, sizeof (vl.type));
@@ -911,6 +921,8 @@ static int cj_curl_perform(cj_t *db) /* {{{ */
            status, db->curl_errbuf, url);
     return (-1);
   }
+  if (db->stats != NULL)
+    curl_stats_dispatch (db->stats, db->curl, cj_host (db), "curl_json", db->instance);
 
   curl_easy_getinfo(db->curl, CURLINFO_EFFECTIVE_URL, &url);
   curl_easy_getinfo(db->curl, CURLINFO_RESPONSE_CODE, &rc);
@@ -956,9 +968,9 @@ static int cj_perform (cj_t *db) /* {{{ */
   }
 
 #if HAVE_YAJL_V2
-    status = yajl_complete_parse(db->yajl);
+  status = yajl_complete_parse(db->yajl);
 #else
-    status = yajl_parse_complete(db->yajl);
+  status = yajl_parse_complete(db->yajl);
 #endif
   if (status != yajl_status_ok)
   {
index 0f2b92b..e0e4a62 100644 (file)
@@ -23,6 +23,7 @@
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
+#include "utils_curl_stats.h"
 #include "utils_llist.h"
 
 #include <libxml/parser.h>
@@ -83,6 +84,7 @@ struct cx_s /* {{{ */
   char *post_body;
   int timeout;
   struct curl_slist *headers;
+  curl_stats_t *stats;
 
   cx_namespace_t *namespaces;
   size_t namespaces_num;
@@ -114,7 +116,7 @@ static size_t cx_curl_callback (void *buf, /* {{{ */
     return (0);
   }
 
-   if (len == 0)
+  if (len == 0)
     return (len);
 
   if ((db->buffer_fill + len) >= db->buffer_size)
@@ -202,6 +204,7 @@ static void cx_free (void *arg) /* {{{ */
   sfree (db->cacert);
   sfree (db->post_body);
   curl_slist_free_all (db->headers);
+  curl_stats_destroy (db->stats);
 
   for (i = 0; i < db->namespaces_num; i++)
   {
@@ -213,6 +216,13 @@ static void cx_free (void *arg) /* {{{ */
   sfree (db);
 } /* }}} void cx_free */
 
+static const char *cx_host (cx_t *db) /* {{{ */
+{
+  if (db->host == NULL)
+    return hostname_g;
+  return db->host;
+} /* }}} cx_host */
+
 static int cx_config_append_string (const char *name, struct curl_slist **dest, /* {{{ */
     oconfig_item_t *ci)
 {
@@ -515,7 +525,7 @@ static int  cx_handle_base_xpath (char const *plugin_instance, /* {{{ */
   vl.values_len = ds->ds_num;
   sstrncpy (vl.type, xpath->type, sizeof (vl.type));
   sstrncpy (vl.plugin, "curl_xml", sizeof (vl.plugin));
-  sstrncpy (vl.host, (host != NULL) ? host : hostname_g, sizeof (vl.host));
+  sstrncpy (vl.host, host, sizeof (vl.host));
   if (plugin_instance != NULL)
     sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
 
@@ -558,7 +568,7 @@ static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
     ds = plugin_get_ds (xpath->type);
 
     if ( (cx_check_type(ds, xpath) == 0) &&
-         (cx_handle_base_xpath(db->instance, db->host,
+         (cx_handle_base_xpath(db->instance, cx_host (db),
                                xpath_ctx, ds, le->key, xpath) == 0) )
       status = 0; /* we got atleast one success */
 
@@ -630,6 +640,8 @@ static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */
            status, db->curl_errbuf, url);
     return (-1);
   }
+  if (db->stats != NULL)
+    curl_stats_dispatch (db->stats, db->curl, cx_host (db), "curl_xml", db->instance);
 
   curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
   curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
@@ -981,6 +993,12 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
       status = cx_config_add_namespace (db, child);
     else if (strcasecmp ("Timeout", child->key) == 0)
       status = cf_util_get_int (child, &db->timeout);
+    else if (strcasecmp ("Statistics", child->key) == 0)
+    {
+      db->stats = curl_stats_from_config (child);
+      if (db->stats == NULL)
+        status = -1;
+    }
     else
     {
       WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
index 2fe42ae..632872a 100644 (file)
@@ -10,7 +10,7 @@ AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"'
 AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"'
 
 # Link to these libraries..
-COMMON_LIBS =
+COMMON_LIBS = $(PTHREAD_LIBS)
 if BUILD_WITH_LIBRT
 COMMON_LIBS += -lrt
 endif
@@ -23,9 +23,6 @@ endif
 if BUILD_WITH_LIBRESOLV
 COMMON_LIBS += -lresolv
 endif
-if BUILD_WITH_LIBPTHREAD
-COMMON_LIBS += -lpthread
-endif
 if BUILD_WITH_LIBKSTAT
 COMMON_LIBS += -lkstat
 endif
@@ -90,6 +87,8 @@ else
 collectd_LDADD += -loconfig
 endif
 
+LOG_COMPILER = env VALGRIND="@VALGRIND@" $(abs_top_srcdir)/testwrapper.sh
+
 check_PROGRAMS = test_common test_meta_data test_utils_avltree test_utils_heap test_utils_time test_utils_subst
 TESTS          = test_common test_meta_data test_utils_avltree test_utils_heap test_utils_time test_utils_subst
 
index a72ff35..ce4922d 100644 (file)
@@ -54,7 +54,6 @@
  */
 char hostname_g[DATA_MAX_NAME_LEN];
 cdtime_t interval_g;
-int  pidfile_from_cli = 0;
 int  timeout_g;
 #if HAVE_LIBKSTAT
 kstat_ctl_t *kc;
@@ -551,15 +550,14 @@ int main (int argc, char **argv)
                                break;
                        case 'T':
                                test_readall = 1;
-                               global_option_set ("ReadThreads", "-1");
+                               global_option_set ("ReadThreads", "-1", 1);
 #if COLLECT_DAEMON
                                daemonize = 0;
 #endif /* COLLECT_DAEMON */
                                break;
 #if COLLECT_DAEMON
                        case 'P':
-                               global_option_set ("PIDFile", optarg);
-                               pidfile_from_cli = 1;
+                               global_option_set ("PIDFile", optarg, 1);
                                break;
                        case 'f':
                                daemonize = 0;
index 9002187..3cb0c1b 100644 (file)
@@ -227,39 +227,39 @@ typedef int _Bool;
 #endif
 
 #ifndef PACKAGE_NAME
-#define PACKAGE_NAME "collectd"
+# define PACKAGE_NAME "collectd"
 #endif
 
 #ifndef PREFIX
-#define PREFIX "/opt/" PACKAGE_NAME
+# define PREFIX "/opt/" PACKAGE_NAME
 #endif
 
 #ifndef SYSCONFDIR
-#define SYSCONFDIR PREFIX "/etc"
+# define SYSCONFDIR PREFIX "/etc"
 #endif
 
 #ifndef CONFIGFILE
-#define CONFIGFILE SYSCONFDIR"/collectd.conf"
+# define CONFIGFILE SYSCONFDIR"/collectd.conf"
 #endif
 
 #ifndef LOCALSTATEDIR
-#define LOCALSTATEDIR PREFIX "/var"
+# define LOCALSTATEDIR PREFIX "/var"
 #endif
 
 #ifndef PKGLOCALSTATEDIR
-#define PKGLOCALSTATEDIR PREFIX "/var/lib/" PACKAGE_NAME
+# define PKGLOCALSTATEDIR PREFIX "/var/lib/" PACKAGE_NAME
 #endif
 
 #ifndef PIDFILE
-#define PIDFILE PREFIX "/var/run/" PACKAGE_NAME ".pid"
+# define PIDFILE PREFIX "/var/run/" PACKAGE_NAME ".pid"
 #endif
 
 #ifndef PLUGINDIR
-#define PLUGINDIR PREFIX "/lib/" PACKAGE_NAME
+# define PLUGINDIR PREFIX "/lib/" PACKAGE_NAME
 #endif
 
 #ifndef PKGDATADIR
-#define PKGDATADIR PREFIX "/share/" PACKAGE_NAME
+# define PKGDATADIR PREFIX "/share/" PACKAGE_NAME
 #endif
 
 #ifndef COLLECTD_GRP_NAME
@@ -270,21 +270,15 @@ typedef int _Bool;
 # define COLLECTD_DEFAULT_INTERVAL 10.0
 #endif
 
- #ifndef COLLECTD_USERAGENT
- # define COLLECTD_USERAGENT PACKAGE_NAME"/"PACKAGE_VERSION
- #endif
+#ifndef COLLECTD_USERAGENT
+# define COLLECTD_USERAGENT PACKAGE_NAME "/" PACKAGE_VERSION
+#endif
 
 /* Only enable __attribute__() for compilers known to support it. */
-#if defined(__clang__)
-# define clang_attr(x) __attribute__(x)
-# define gcc_attr(x) /**/
-#elif __GNUC__
-# define clang_attr(x) /**/
-# define gcc_attr(x) __attribute__(x)
-#else
-# define clang_attr(x) /**/
-# define gcc_attr(x) /**/
-# define __attribute__(x) /**/
+#if !defined(__clang__) && !defined(__GNUC__)
+# if !defined(__attribute__)
+#  define __attribute__(x) /**/
+# endif
 #endif
 
 #if defined(COLLECT_DEBUG) && COLLECT_DEBUG && defined(__GNUC__) && __GNUC__
@@ -316,7 +310,6 @@ typedef uint64_t cdtime_t;
 
 extern char     hostname_g[];
 extern cdtime_t interval_g;
-extern int      pidfile_from_cli;
 extern int      timeout_g;
 
 #endif /* COLLECTD_H */
index 546da3b..4c271d3 100644 (file)
@@ -36,9 +36,7 @@
 #include "plugin.h"
 #include "utils_cache.h"
 
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
+#include <pthread.h>
 
 #ifdef HAVE_MATH_H
 # include <math.h>
@@ -275,6 +273,9 @@ ssize_t swrite (int fd, const void *buf, size_t count)
        ptr   = (const char *) buf;
        nleft = count;
        
+       if (fd < 0)
+               return (-1);
+
        /* checking for closed peer connection */
        pfd.fd = fd;
        pfd.events = POLLIN | POLLHUP;
index 34c23cd..b9860bd 100644 (file)
@@ -80,6 +80,7 @@ typedef struct cf_global_option_s
 {
        const char *key;
        char *value;
+       _Bool from_cli; /* value set from CLI */
        const char *def;
 } cf_global_option_t;
 
@@ -108,21 +109,21 @@ static int cf_value_map_num = STATIC_ARRAY_SIZE (cf_value_map);
 
 static cf_global_option_t cf_global_options[] =
 {
-       {"BaseDir",     NULL, PKGLOCALSTATEDIR},
-       {"PIDFile",     NULL, PIDFILE},
-       {"Hostname",    NULL, NULL},
-       {"FQDNLookup",  NULL, "true"},
-       {"Interval",    NULL, NULL},
-       {"ReadThreads", NULL, "5"},
-       {"WriteThreads", NULL, "5"},
-       {"WriteQueueLimitHigh", NULL, NULL},
-       {"WriteQueueLimitLow", NULL, NULL},
-       {"Timeout",     NULL, "2"},
-       {"AutoLoadPlugin", NULL, "false"},
-       {"CollectInternalStats", NULL, "false"},
-       {"PreCacheChain",  NULL, "PreCache"},
-       {"PostCacheChain", NULL, "PostCache"},
-       {"MaxReadInterval", NULL, "86400"}
+       {"BaseDir",              NULL, 0, PKGLOCALSTATEDIR},
+       {"PIDFile",              NULL, 0, PIDFILE},
+       {"Hostname",             NULL, 0, NULL},
+       {"FQDNLookup",           NULL, 0, "true"},
+       {"Interval",             NULL, 0, NULL},
+       {"ReadThreads",          NULL, 0, "5"},
+       {"WriteThreads",         NULL, 0, "5"},
+       {"WriteQueueLimitHigh",  NULL, 0, NULL},
+       {"WriteQueueLimitLow",   NULL, 0, NULL},
+       {"Timeout",              NULL, 0, "2"},
+       {"AutoLoadPlugin",       NULL, 0, "false"},
+       {"CollectInternalStats", NULL, 0, "false"},
+       {"PreCacheChain",        NULL, 0, "PreCache"},
+       {"PostCacheChain",       NULL, 0, "PostCache"},
+       {"MaxReadInterval",      NULL, 0, "86400"}
 };
 static int cf_global_options_num = STATIC_ARRAY_SIZE (cf_global_options);
 
@@ -210,19 +211,19 @@ static int dispatch_global_option (const oconfig_item_t *ci)
        if (ci->values_num != 1)
                return (-1);
        if (ci->values[0].type == OCONFIG_TYPE_STRING)
-               return (global_option_set (ci->key, ci->values[0].value.string));
+               return (global_option_set (ci->key, ci->values[0].value.string, 0));
        else if (ci->values[0].type == OCONFIG_TYPE_NUMBER)
        {
                char tmp[128];
                ssnprintf (tmp, sizeof (tmp), "%lf", ci->values[0].value.number);
-               return (global_option_set (ci->key, tmp));
+               return (global_option_set (ci->key, tmp, 0));
        }
        else if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
        {
                if (ci->values[0].value.boolean)
-                       return (global_option_set (ci->key, "true"));
+                       return (global_option_set (ci->key, "true", 0));
                else
-                       return (global_option_set (ci->key, "false"));
+                       return (global_option_set (ci->key, "false", 0));
        }
 
        return (-1);
@@ -927,7 +928,7 @@ static oconfig_item_t *cf_read_generic (const char *path,
 /*
  * Public functions
  */
-int global_option_set (const char *option, const char *value)
+int global_option_set (const char *option, const char *value, _Bool from_cli)
 {
        int i;
 
@@ -938,12 +939,16 @@ int global_option_set (const char *option, const char *value)
                        break;
 
        if (i >= cf_global_options_num)
+       {
+               ERROR ("configfile: Cannot set unknown global option `%s'.", option);
                return (-1);
+       }
 
-       if (strcasecmp (option, "PIDFile") == 0 && pidfile_from_cli == 1)
+       if (cf_global_options[i].from_cli && (! from_cli))
        {
-               DEBUG ("Configfile: Ignoring `PIDFILE' option because "
-                       "command-line option `-P' take precedence.");
+               DEBUG ("configfile: Ignoring %s `%s' option because "
+                               "it was overriden by a command-line option.",
+                               option, value);
                return (0);
        }
 
@@ -954,6 +959,8 @@ int global_option_set (const char *option, const char *value)
        else
                cf_global_options[i].value = NULL;
 
+       cf_global_options[i].from_cli = from_cli;
+
        return (0);
 }
 
@@ -966,7 +973,10 @@ const char *global_option_get (const char *option)
                        break;
 
        if (i >= cf_global_options_num)
+       {
+               ERROR ("configfile: Cannot get unknown global option `%s'.", option);
                return (NULL);
+       }
 
        return ((cf_global_options[i].value != NULL)
                        ? cf_global_options[i].value
index 09dbe58..924d97c 100644 (file)
@@ -89,7 +89,7 @@ int cf_register_complex (const char *type, int (*callback) (oconfig_item_t *));
  */
 int cf_read (const char *filename);
 
-int global_option_set (const char *option, const char *value);
+int global_option_set (const char *option, const char *value, _Bool from_cli);
 const char *global_option_get (const char *option);
 long global_option_get_long (const char *option, long default_value);
 long global_option_get_long_in_range (const char *option, long default_value, long min, long max);
index 7398227..75498c8 100644 (file)
@@ -33,9 +33,7 @@
 #include "meta_data.h"
 #include "utils_time.h"
 
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
+#include <pthread.h>
 
 #define PLUGIN_FLAGS_GLOBAL 0x0001
 
index d525570..bf8c901 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/utils_cache.c
  * Copyright (C) 2007-2010  Florian octo Forster
+ * Copyright (C) 2016       Sebastian tokkee Harl
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -22,6 +23,7 @@
  *
  * Authors:
  *   Florian octo Forster <octo at collectd.org>
+ *   Sebastian tokkee Harl <sh at tokkee.org>
  **/
 
 #include "collectd.h"
@@ -47,7 +49,7 @@ typedef struct cache_entry_s
         * (for purging old entries) */
        cdtime_t last_update;
        /* Interval in which the data is collected
-        * (for purding old entries) */
+        * (for purging old entries) */
        cdtime_t interval;
        int state;
        int hits;
@@ -68,6 +70,13 @@ typedef struct cache_entry_s
        meta_data_t *meta;
 } cache_entry_t;
 
+struct uc_iter_s {
+  c_avl_iterator_t *iter;
+
+  char *name;
+  cache_entry_t *entry;
+};
+
 static c_avl_tree_t   *cache_tree = NULL;
 static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
 
@@ -868,6 +877,104 @@ int uc_inc_hits (const data_set_t *ds, const value_list_t *vl, int step)
 } /* int uc_inc_hits */
 
 /*
+ * Iterator interface
+ */
+uc_iter_t *uc_get_iterator (void)
+{
+  uc_iter_t *iter;
+
+  iter = (uc_iter_t *) calloc(1, sizeof (*iter));
+  if (iter == NULL)
+    return (NULL);
+
+  pthread_mutex_lock (&cache_lock);
+
+  iter->iter = c_avl_get_iterator (cache_tree);
+  if (iter->iter == NULL)
+  {
+    free (iter);
+    return (NULL);
+  }
+
+  return (iter);
+} /* uc_iter_t *uc_get_iterator */
+
+int uc_iterator_next (uc_iter_t *iter, char **ret_name)
+{
+  int status;
+
+  if (iter == NULL)
+    return (-1);
+
+  while ((status = c_avl_iterator_next (iter->iter,
+         (void *) &iter->name, (void *) &iter->entry)) == 0)
+  {
+    if (iter->entry->state == STATE_MISSING)
+      continue;
+
+    break;
+  }
+  if (status != 0) {
+    iter->name = NULL;
+    iter->entry = NULL;
+    return (-1);
+  }
+
+  if (ret_name != NULL)
+    *ret_name = iter->name;
+
+  return (0);
+} /* int uc_iterator_next */
+
+void uc_iterator_destroy (uc_iter_t *iter)
+{
+  if (iter == NULL)
+    return;
+
+  c_avl_iterator_destroy (iter->iter);
+  pthread_mutex_unlock (&cache_lock);
+
+  free (iter);
+} /* void uc_iterator_destroy */
+
+int uc_iterator_get_time (uc_iter_t *iter, cdtime_t *ret_time)
+{
+  if ((iter == NULL) || (iter->entry == NULL) || (ret_time == NULL))
+    return (-1);
+
+  *ret_time = iter->entry->last_time;
+  return (0);
+} /* int uc_iterator_get_name */
+
+int uc_iterator_get_values (uc_iter_t *iter, value_t **ret_values, size_t *ret_num)
+{
+  size_t i;
+
+  if ((iter == NULL) || (iter->entry == NULL)
+      || (ret_values == NULL) || (ret_num == NULL))
+    return (-1);
+
+  *ret_values = calloc (iter->entry->values_num, sizeof(*iter->entry->values_raw));
+  if (*ret_values == NULL)
+    return (-1);
+  for (i = 0; i < iter->entry->values_num; ++i)
+    *ret_values[i] = iter->entry->values_raw[i];
+
+  *ret_num = iter->entry->values_num;
+
+  return (0);
+} /* int uc_iterator_get_values */
+
+int uc_iterator_get_interval (uc_iter_t *iter, cdtime_t *ret_interval)
+{
+  if ((iter == NULL) || (iter->entry == NULL) || (ret_interval == NULL))
+    return (-1);
+
+  *ret_interval = iter->entry->interval;
+  return (0);
+} /* int uc_iterator_get_name */
+
+/*
  * Meta data interface
  */
 /* XXX: This function will acquire `cache_lock' but will not free it! */
index 75bfc42..6c6a883 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/utils_cache.h
  * Copyright (C) 2007       Florian octo Forster
+ * Copyright (C) 2016       Sebastian tokkee Harl
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -22,6 +23,7 @@
  *
  * Authors:
  *   Florian octo Forster <octo at collectd.org>
+ *   Sebastian tokkee Harl <sh at tokkee.org>
  **/
 
 #ifndef UTILS_CACHE_H
@@ -55,6 +57,53 @@ int uc_get_history_by_name (const char *name,
     gauge_t *ret_history, size_t num_steps, size_t num_ds);
 
 /*
+ * Iterator interface
+ */
+struct uc_iter_s;
+typedef struct uc_iter_s uc_iter_t;
+
+/*
+ * NAME
+ *   uc_get_iterator
+ *
+ * DESCRIPTION
+ *   Create an iterator for the cache. It will hold the cache lock until it's
+ *   destroyed.
+ *
+ * RETURN VALUE
+ *   An iterator object on success or NULL else.
+ */
+uc_iter_t *uc_get_iterator (void);
+
+/*
+ * NAME
+ *   uc_iterator_next
+ *
+ * DESCRIPTION
+ *   Advance the iterator to the next positiion and (optionally) returns the
+ *   name of the entry.
+ *
+ * PARAMETERS
+ *   `iter'     The iterator object to advance.
+ *   `ret_name' Pointer to a string where to store the name. The returned
+ *              value is a copy of the value and has to be freed by the
+ *              caller.
+ *
+ * RETURN VALUE
+ *   Zero upon success or non-zero if the iterator ie NULL or no further
+ *   values are available.
+ */
+int uc_iterator_next (uc_iter_t *iter, char **ret_name);
+void uc_iterator_destroy (uc_iter_t *iter);
+
+/* Return the timestamp of the value at the current position. */
+int uc_iterator_get_time (uc_iter_t *iter, cdtime_t *ret_time);
+/* Return the (raw) value at the current position. */
+int uc_iterator_get_values (uc_iter_t *iter, value_t **ret_values, size_t *ret_num);
+/* Return the interval of the value at the current position. */
+int uc_iterator_get_interval (uc_iter_t *iter, cdtime_t *ret_interval);
+
+/*
  * Meta data interface
  */
 int uc_meta_data_exists (const value_list_t *vl, const char *key);
index 4a8df89..cb1ea34 100644 (file)
@@ -124,16 +124,16 @@ int ut_search_threshold (const value_list_t *vl, /* {{{ */
   if (vl == NULL)
     return (EINVAL);
 
-       /* Is this lock really necessary? */
-       pthread_mutex_lock (&threshold_lock);
+  /* Is this lock really necessary? */
+  pthread_mutex_lock (&threshold_lock);
   t = threshold_search (vl);
   if (t == NULL) {
-               pthread_mutex_unlock (&threshold_lock);
+    pthread_mutex_unlock (&threshold_lock);
     return (ENOENT);
-       }
+  }
 
   memcpy (ret_threshold, t, sizeof (*ret_threshold));
-       pthread_mutex_unlock (&threshold_lock);
+  pthread_mutex_unlock (&threshold_lock);
 
   ret_threshold->next = NULL;
 
index 255d4db..9965bca 100644 (file)
@@ -893,7 +893,13 @@ static int disk_read (void)
 #endif
 
                if (ignorelist_match (ignorelist, output_name) != 0)
+               {
+#if HAVE_LIBUDEV
+                       /* release udev-based alternate name, if allocated */
+                       sfree (alt_name);
+#endif
                        continue;
+               }
 
                if ((ds->read_bytes != 0) || (ds->write_bytes != 0))
                        disk_submit (output_name, "disk_octets",
index 53fa725..2c3a904 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -30,7 +30,6 @@
 #include "configfile.h"
 
 #include "utils_dns.h"
-#include <pthread.h>
 #include <poll.h>
 
 #include <pcap.h>
index ea4e785..8a1ac2e 100644 (file)
 
 #include <stddef.h>
 
-#if HAVE_LIBPTHREAD
-# include <pthread.h>
-#endif
-
 #include <sys/un.h>
 #include <sys/select.h>
 
index 8971f4d..e10021c 100644 (file)
@@ -311,9 +311,13 @@ static int ethstat_read_interface (char *device)
 
   for (i = 0; i < n_stats; i++)
   {
-    const char *stat_name;
+    char *stat_name;
 
     stat_name = (void *) &strings->data[i * ETH_GSTRING_LEN];
+    /* Remove leading spaces in key name */
+    while (isspace ((int) *stat_name))
+        stat_name++;
+
     DEBUG("ethstat plugin: device = \"%s\": %s = %"PRIu64,
         device, stat_name, (uint64_t) stats->data[i]);
     ethstat_submit_value (device,
index 6d5908c..cdaf79a 100644 (file)
@@ -38,8 +38,6 @@
 #include <grp.h>
 #include <signal.h>
 
-#include <pthread.h>
-
 #define PL_NORMAL        0x01
 #define PL_NOTIF_ACTION  0x02
 
index 67e0406..53cdcbf 100644 (file)
@@ -30,9 +30,6 @@
 #include "configfile.h"
 #include "utils_avltree.h"
 
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
 #if HAVE_NETDB_H
 # include <netdb.h>
 #endif
diff --git a/src/grpc.cc b/src/grpc.cc
new file mode 100644 (file)
index 0000000..7517bb7
--- /dev/null
@@ -0,0 +1,653 @@
+/**
+ * collectd - src/grpc.cc
+ * Copyright (C) 2015-2016 Sebastian Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sebastian Harl <sh at tokkee.org>
+ **/
+
+#include <grpc++/grpc++.h>
+#include <google/protobuf/util/time_util.h>
+
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include "collectd.grpc.pb.h"
+
+extern "C" {
+#include <fnmatch.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#include "collectd.h"
+#include "common.h"
+#include "configfile.h"
+#include "plugin.h"
+
+#include "daemon/utils_cache.h"
+}
+
+using collectd::Collectd;
+
+using collectd::DispatchValuesRequest;
+using collectd::DispatchValuesReply;
+using collectd::QueryValuesRequest;
+using collectd::QueryValuesReply;
+
+using google::protobuf::util::TimeUtil;
+
+/*
+ * private types
+ */
+
+struct Listener {
+       grpc::string addr;
+       grpc::string port;
+
+       grpc::SslServerCredentialsOptions *ssl;
+};
+static std::vector<Listener> listeners;
+static grpc::string default_addr("0.0.0.0:50051");
+
+/*
+ * helper functions
+ */
+
+static bool ident_matches(const value_list_t *vl, const value_list_t *matcher)
+{
+       if (fnmatch(matcher->host, vl->host, 0))
+               return false;
+
+       if (fnmatch(matcher->plugin, vl->plugin, 0))
+               return false;
+       if (fnmatch(matcher->plugin_instance, vl->plugin_instance, 0))
+               return false;
+
+       if (fnmatch(matcher->type, vl->type, 0))
+               return false;
+       if (fnmatch(matcher->type_instance, vl->type_instance, 0))
+               return false;
+
+       return true;
+} /* ident_matches */
+
+static grpc::string read_file(const char *filename)
+{
+       std::ifstream f;
+       grpc::string s, content;
+
+       f.open(filename);
+       if (!f.is_open()) {
+               ERROR("grpc: Failed to open '%s'", filename);
+               return "";
+       }
+
+       while (std::getline(f, s)) {
+               content += s;
+               content.push_back('\n');
+       }
+       f.close();
+       return content;
+} /* read_file */
+
+/*
+ * proto conversion
+ */
+
+static void marshal_ident(const value_list_t *vl, collectd::types::Identifier *msg)
+{
+       msg->set_host(vl->host);
+       msg->set_plugin(vl->plugin);
+       if (vl->plugin_instance[0] != '\0')
+               msg->set_plugin_instance(vl->plugin_instance);
+       msg->set_type(vl->type);
+       if (vl->type_instance[0] != '\0')
+               msg->set_type_instance(vl->type_instance);
+} /* marshal_ident */
+
+static grpc::Status unmarshal_ident(const collectd::types::Identifier &msg, value_list_t *vl,
+               bool require_fields)
+{
+       std::string s;
+
+       s = msg.host();
+       if (!s.length() && require_fields)
+               return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+                               grpc::string("missing host name"));
+       sstrncpy(vl->host, s.c_str(), sizeof(vl->host));
+
+       s = msg.plugin();
+       if (!s.length() && require_fields)
+               return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+                               grpc::string("missing plugin name"));
+       sstrncpy(vl->plugin, s.c_str(), sizeof(vl->plugin));
+
+       s = msg.type();
+       if (!s.length() && require_fields)
+               return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+                               grpc::string("missing type name"));
+       sstrncpy(vl->type, s.c_str(), sizeof(vl->type));
+
+       s = msg.plugin_instance();
+       sstrncpy(vl->plugin_instance, s.c_str(), sizeof(vl->plugin_instance));
+
+       s = msg.type_instance();
+       sstrncpy(vl->type_instance, s.c_str(), sizeof(vl->type_instance));
+
+       return grpc::Status::OK;
+} /* unmarshal_ident() */
+
+static grpc::Status marshal_value_list(const value_list_t *vl, collectd::types::ValueList *msg)
+{
+       auto id = msg->mutable_identifier();
+       marshal_ident(vl, id);
+
+       auto ds = plugin_get_ds(vl->type);
+       if ((ds == NULL) || (ds->ds_num != vl->values_len)) {
+               return grpc::Status(grpc::StatusCode::INTERNAL,
+                               grpc::string("failed to retrieve data-set for values"));
+       }
+
+       auto t = TimeUtil::NanosecondsToTimestamp(CDTIME_T_TO_NS(vl->time));
+       auto d = TimeUtil::NanosecondsToDuration(CDTIME_T_TO_NS(vl->interval));
+       msg->set_allocated_time(new google::protobuf::Timestamp(t));
+       msg->set_allocated_interval(new google::protobuf::Duration(d));
+
+       for (size_t i = 0; i < vl->values_len; ++i) {
+               auto v = msg->add_value();
+               switch (ds->ds[i].type) {
+                       case DS_TYPE_COUNTER:
+                               v->set_counter(vl->values[i].counter);
+                               break;
+                       case DS_TYPE_GAUGE:
+                               v->set_gauge(vl->values[i].gauge);
+                               break;
+                       case DS_TYPE_DERIVE:
+                               v->set_derive(vl->values[i].derive);
+                               break;
+                       case DS_TYPE_ABSOLUTE:
+                               v->set_absolute(vl->values[i].absolute);
+                               break;
+                       default:
+                               return grpc::Status(grpc::StatusCode::INTERNAL,
+                                               grpc::string("unknown value type"));
+               }
+       }
+
+       return grpc::Status::OK;
+} /* marshal_value_list */
+
+static grpc::Status unmarshal_value_list(const collectd::types::ValueList &msg, value_list_t *vl)
+{
+       vl->time = NS_TO_CDTIME_T(TimeUtil::TimestampToNanoseconds(msg.time()));
+       vl->interval = NS_TO_CDTIME_T(TimeUtil::DurationToNanoseconds(msg.interval()));
+
+       auto status = unmarshal_ident(msg.identifier(), vl, true);
+       if (!status.ok())
+               return status;
+
+       value_t *values = NULL;
+       size_t values_len = 0;
+
+       status = grpc::Status::OK;
+       for (auto v : msg.value()) {
+               value_t *val = (value_t *)realloc(values, (values_len + 1) * sizeof(*values));
+               if (!val) {
+                       status = grpc::Status(grpc::StatusCode::RESOURCE_EXHAUSTED,
+                                       grpc::string("failed to allocate values array"));
+                       break;
+               }
+
+               values = val;
+               val = values + values_len;
+               values_len++;
+
+               switch (v.value_case()) {
+               case collectd::types::Value::ValueCase::kCounter:
+                       val->counter = counter_t(v.counter());
+                       break;
+               case collectd::types::Value::ValueCase::kGauge:
+                       val->gauge = gauge_t(v.gauge());
+                       break;
+               case collectd::types::Value::ValueCase::kDerive:
+                       val->derive = derive_t(v.derive());
+                       break;
+               case collectd::types::Value::ValueCase::kAbsolute:
+                       val->absolute = absolute_t(v.absolute());
+                       break;
+               default:
+                       status = grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+                                       grpc::string("unknown value type"));
+                       break;
+               }
+
+               if (!status.ok())
+                       break;
+       }
+       if (status.ok()) {
+               vl->values = values;
+               vl->values_len = values_len;
+       }
+       else if (values) {
+               free(values);
+       }
+
+       return status;
+} /* unmarshal_value_list() */
+
+/*
+ * request call-backs and call objects
+ */
+
+static grpc::Status Process(grpc::ServerContext *ctx,
+               DispatchValuesRequest request, DispatchValuesReply *reply)
+{
+       value_list_t vl = VALUE_LIST_INIT;
+       auto status = unmarshal_value_list(request.values(), &vl);
+       if (!status.ok())
+               return status;
+
+       if (plugin_dispatch_values(&vl))
+               status = grpc::Status(grpc::StatusCode::INTERNAL,
+                               grpc::string("failed to enqueue values for writing"));
+       return status;
+} /* Process(): DispatchValues */
+
+static grpc::Status Process(grpc::ServerContext *ctx,
+               QueryValuesRequest request, QueryValuesReply *reply)
+{
+       uc_iter_t *iter;
+       char *name = NULL;
+
+       value_list_t matcher;
+       auto status = unmarshal_ident(request.identifier(), &matcher, false);
+       if (!status.ok())
+               return status;
+
+       if ((iter = uc_get_iterator()) == NULL) {
+               return grpc::Status(grpc::StatusCode::INTERNAL,
+                               grpc::string("failed to query values: cannot create iterator"));
+       }
+
+       while (uc_iterator_next(iter, &name) == 0) {
+               value_list_t res;
+               if (parse_identifier_vl(name, &res) != 0)
+                       return grpc::Status(grpc::StatusCode::INTERNAL,
+                                       grpc::string("failed to parse identifier"));
+
+               if (!ident_matches(&res, &matcher))
+                       continue;
+
+               if (uc_iterator_get_time(iter, &res.time) < 0)
+                       return grpc::Status(grpc::StatusCode::INTERNAL,
+                                       grpc::string("failed to retrieve value timestamp"));
+               if (uc_iterator_get_interval(iter, &res.interval) < 0)
+                       return grpc::Status(grpc::StatusCode::INTERNAL,
+                                       grpc::string("failed to retrieve value interval"));
+               if (uc_iterator_get_values(iter, &res.values, &res.values_len) < 0)
+                       return grpc::Status(grpc::StatusCode::INTERNAL,
+                                       grpc::string("failed to retrieve values"));
+
+               auto vl = reply->add_values();
+               status = marshal_value_list(&res, vl);
+               free(res.values);
+               if (!status.ok())
+                       return status;
+       }
+
+       uc_iterator_destroy(iter);
+
+       return grpc::Status::OK;
+} /* Process(): QueryValues */
+
+class Call
+{
+public:
+       Call(Collectd::AsyncService *service, grpc::ServerCompletionQueue *cq)
+               : service_(service), cq_(cq), status_(CREATE)
+       { }
+
+       virtual ~Call()
+       { }
+
+       void Handle()
+       {
+               if (status_ == CREATE) {
+                       Create();
+                       status_ = PROCESS;
+               }
+               else if (status_ == PROCESS) {
+                       Process();
+                       status_ = FINISH;
+               }
+               else {
+                       GPR_ASSERT(status_ == FINISH);
+                       Finish();
+               }
+       } /* Handle() */
+
+protected:
+       virtual void Create() = 0;
+       virtual void Process() = 0;
+       virtual void Finish() = 0;
+
+       Collectd::AsyncService *service_;
+       grpc::ServerCompletionQueue *cq_;
+       grpc::ServerContext ctx_;
+
+private:
+       enum CallStatus { CREATE, PROCESS, FINISH };
+       CallStatus status_;
+}; /* class Call */
+
+template<typename RequestT, typename ReplyT>
+class RpcCall final : public Call
+{
+       typedef void (Collectd::AsyncService::*CreatorT)(grpc::ServerContext *,
+                       RequestT *, grpc::ServerAsyncResponseWriter<ReplyT> *,
+                       grpc::CompletionQueue *, grpc::ServerCompletionQueue *, void *);
+
+public:
+       RpcCall(Collectd::AsyncService *service,
+                       CreatorT creator, grpc::ServerCompletionQueue *cq)
+               : Call(service, cq), creator_(creator), responder_(&ctx_)
+       {
+               Handle();
+       } /* RpcCall() */
+
+       virtual ~RpcCall()
+       { }
+
+private:
+       void Create()
+       {
+               (service_->*creator_)(&ctx_, &request_, &responder_, cq_, cq_, this);
+       } /* Create() */
+
+       void Process()
+       {
+               // Add a new request object to the queue.
+               new RpcCall<RequestT, ReplyT>(service_, creator_, cq_);
+               grpc::Status status = ::Process(&ctx_, request_, &reply_);
+               responder_.Finish(reply_, status, this);
+       } /* Process() */
+
+       void Finish()
+       {
+               delete this;
+       } /* Finish() */
+
+       CreatorT creator_;
+
+       RequestT request_;
+       ReplyT reply_;
+
+       grpc::ServerAsyncResponseWriter<ReplyT> responder_;
+}; /* class RpcCall */
+
+/*
+ * gRPC server implementation
+ */
+
+class CollectdServer final
+{
+public:
+       void Start()
+       {
+               auto auth = grpc::InsecureServerCredentials();
+
+               grpc::ServerBuilder builder;
+
+               if (listeners.empty()) {
+                       builder.AddListeningPort(default_addr, auth);
+                       INFO("grpc: Listening on %s", default_addr.c_str());
+               }
+               else {
+                       for (auto l : listeners) {
+                               grpc::string addr = l.addr + ":" + l.port;
+
+                               auto use_ssl = grpc::string("");
+                               auto a = auth;
+                               if (l.ssl != nullptr) {
+                                       use_ssl = grpc::string(" (SSL enabled)");
+                                       a = grpc::SslServerCredentials(*l.ssl);
+                               }
+
+                               builder.AddListeningPort(addr, a);
+                               INFO("grpc: Listening on %s%s", addr.c_str(), use_ssl.c_str());
+                       }
+               }
+
+               builder.RegisterService(&service_);
+               cq_ = builder.AddCompletionQueue();
+               server_ = builder.BuildAndStart();
+       } /* Start() */
+
+       void Shutdown()
+       {
+               server_->Shutdown();
+               cq_->Shutdown();
+       } /* Shutdown() */
+
+       void Mainloop()
+       {
+               // Register request types.
+               new RpcCall<DispatchValuesRequest, DispatchValuesReply>(&service_,
+                               &Collectd::AsyncService::RequestDispatchValues, cq_.get());
+               new RpcCall<QueryValuesRequest, QueryValuesReply>(&service_,
+                               &Collectd::AsyncService::RequestQueryValues, cq_.get());
+
+               while (true) {
+                       void *req = NULL;
+                       bool ok = false;
+
+                       if (!cq_->Next(&req, &ok))
+                               break; // Queue shut down.
+                       if (!ok) {
+                               ERROR("grpc: Failed to read from queue");
+                               break;
+                       }
+
+                       static_cast<Call *>(req)->Handle();
+               }
+       } /* Mainloop() */
+
+private:
+       Collectd::AsyncService service_;
+
+       std::unique_ptr<grpc::Server> server_;
+       std::unique_ptr<grpc::ServerCompletionQueue> cq_;
+}; /* class CollectdServer */
+
+static CollectdServer *server = nullptr;
+
+/*
+ * collectd plugin interface
+ */
+
+extern "C" {
+       static pthread_t *workers;
+       static size_t workers_num = 5;
+
+       static void *worker_thread(void *arg)
+       {
+               CollectdServer *s = (CollectdServer *)arg;
+               s->Mainloop();
+               return NULL;
+       } /* worker_thread() */
+
+       static int c_grpc_config_listen(oconfig_item_t *ci)
+       {
+               if ((ci->values_num != 2)
+                               || (ci->values[0].type != OCONFIG_TYPE_STRING)
+                               || (ci->values[1].type != OCONFIG_TYPE_STRING)) {
+                       ERROR("grpc: The `%s` config option needs exactly "
+                                       "two string argument (address and port).", ci->key);
+                       return -1;
+               }
+
+               auto listener = Listener();
+               listener.addr = grpc::string(ci->values[0].value.string);
+               listener.port = grpc::string(ci->values[1].value.string);
+               listener.ssl = nullptr;
+
+               auto ssl_opts = new(grpc::SslServerCredentialsOptions);
+               grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp = {};
+               bool use_ssl = false;
+
+               for (int i = 0; i < ci->children_num; i++) {
+                       oconfig_item_t *child = ci->children + i;
+
+                       if (!strcasecmp("EnableSSL", child->key)) {
+                               if (cf_util_get_boolean(child, &use_ssl)) {
+                                       ERROR("grpc: Option `%s` expects a boolean value",
+                                                       child->key);
+                                       return -1;
+                               }
+                       }
+                       else if (!strcasecmp("SSLRootCerts", child->key)) {
+                               char *certs = NULL;
+                               if (cf_util_get_string(child, &certs)) {
+                                       ERROR("grpc: Option `%s` expects a string value",
+                                                       child->key);
+                                       return -1;
+                               }
+                               ssl_opts->pem_root_certs = read_file(certs);
+                       }
+                       else if (!strcasecmp("SSLServerKey", child->key)) {
+                               char *key = NULL;
+                               if (cf_util_get_string(child, &key)) {
+                                       ERROR("grpc: Option `%s` expects a string value",
+                                                       child->key);
+                                       return -1;
+                               }
+                               pkcp.private_key = read_file(key);
+                       }
+                       else if (!strcasecmp("SSLServerCert", child->key)) {
+                               char *cert = NULL;
+                               if (cf_util_get_string(child, &cert)) {
+                                       ERROR("grpc: Option `%s` expects a string value",
+                                                       child->key);
+                                       return -1;
+                               }
+                               pkcp.cert_chain = read_file(cert);
+                       }
+                       else {
+                               WARNING("grpc: Option `%s` not allowed in <%s> block.",
+                                               child->key, ci->key);
+                       }
+               }
+
+               ssl_opts->pem_key_cert_pairs.push_back(pkcp);
+               if (use_ssl)
+                       listener.ssl = ssl_opts;
+               else
+                       delete(ssl_opts);
+
+               listeners.push_back(listener);
+               return 0;
+       } /* c_grpc_config_listen() */
+
+       static int c_grpc_config(oconfig_item_t *ci)
+       {
+               int i;
+
+               for (i = 0; i < ci->children_num; i++) {
+                       oconfig_item_t *child = ci->children + i;
+
+                       if (!strcasecmp("Listen", child->key)) {
+                               if (c_grpc_config_listen(child))
+                                       return -1;
+                       }
+                       else if (!strcasecmp("WorkerThreads", child->key)) {
+                               int n;
+                               if (cf_util_get_int(child, &n))
+                                       return -1;
+                               workers_num = (size_t)n;
+                       }
+                       else {
+                               WARNING("grpc: Option `%s` not allowed here.", child->key);
+                       }
+               }
+
+               return 0;
+       } /* c_grpc_config() */
+
+       static int c_grpc_init(void)
+       {
+               server = new CollectdServer();
+               size_t i;
+
+               if (! server) {
+                       ERROR("grpc: Failed to create server");
+                       return -1;
+               }
+
+               workers = (pthread_t *)calloc(workers_num, sizeof(*workers));
+               if (! workers) {
+                       delete server;
+                       server = nullptr;
+
+                       ERROR("grpc: Failed to allocate worker threads");
+                       return -1;
+               }
+
+               server->Start();
+               for (i = 0; i < workers_num; i++) {
+                       plugin_thread_create(&workers[i], /* attr = */ NULL,
+                                       worker_thread, server);
+               }
+               INFO("grpc: Started %zu workers", workers_num);
+               return 0;
+       } /* c_grpc_init() */
+
+       static int c_grpc_shutdown(void)
+       {
+               size_t i;
+
+               if (!server)
+                       return -1;
+
+               server->Shutdown();
+
+               INFO("grpc: Waiting for %zu workers to terminate", workers_num);
+               for (i = 0; i < workers_num; i++)
+                       pthread_join(workers[i], NULL);
+               free(workers);
+               workers = NULL;
+               workers_num = 0;
+
+               delete server;
+               server = nullptr;
+
+               return 0;
+       } /* c_grpc_shutdown() */
+
+       void module_register(void)
+       {
+               plugin_register_complex_config("grpc", c_grpc_config);
+               plugin_register_init("grpc", c_grpc_init);
+               plugin_register_shutdown("grpc", c_grpc_shutdown);
+       } /* module_register() */
+} /* extern "C" */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
index 76dd9cb..c3aa558 100644 (file)
@@ -28,8 +28,6 @@
 #include "plugin.h"
 #include "utils_ignorelist.h"
 
-#include <pthread.h>
-
 #include <OpenIPMI/ipmiif.h>
 #include <OpenIPMI/ipmi_err.h>
 #include <OpenIPMI/ipmi_posix.h>
index 4187e7c..01dc1b8 100644 (file)
@@ -26,7 +26,6 @@
 #include "common.h"
 #include "filter_chain.h"
 
-#include <pthread.h>
 #include <jni.h>
 
 #if !defined(JNI_VERSION_1_2)
index 8b5ac0b..26a5929 100644 (file)
@@ -693,7 +693,7 @@ int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */
 
   if (ret_values_names != NULL)
   {
-    values_names = (char **) calloc (values_num, sizeof (*values_names));
+    values_names = calloc (values_num, sizeof (*values_names));
     if (values_names == NULL)
       BAIL_OUT (ENOMEM);
   }
index 2d5f620..3cf636e 100644 (file)
@@ -31,7 +31,6 @@
 #include "plugin.h"
 
 #include <sys/types.h>
-#include <pthread.h>
 #include <yajl/yajl_common.h>
 #include <yajl/yajl_gen.h>
 #if HAVE_YAJL_YAJL_VERSION_H
@@ -65,12 +64,12 @@ static int log_logstash_config (const char *key, const char *value)
 
        if (0 == strcasecmp (key, "LogLevel")) {
                log_level = parse_log_severity(value);
-        if (log_level < 0) {
-            log_level = LOG_INFO;
-            ERROR("log_logstash: invalid loglevel [%s] defaulting to 'info'",
-                  value);
-            return 1;
-        }
+               if (log_level < 0) {
+                       log_level = LOG_INFO;
+                       ERROR("log_logstash: invalid loglevel [%s] defaulting to 'info'",
+                               value);
+                       return 1;
+               }
        }
        else if (0 == strcasecmp (key, "File")) {
                sfree (log_file);
index 1179718..334226b 100644 (file)
@@ -30,8 +30,6 @@
 #include "common.h"
 #include "plugin.h"
 
-#include <pthread.h>
-
 #define DEFAULT_LOGFILE LOCALSTATEDIR"/log/collectd.log"
 
 #if COLLECT_DEBUG
index 0b00bab..dbef915 100644 (file)
@@ -36,8 +36,6 @@
 #include "utils_cache.h"
 #include "utils_complain.h"
 
-#include <pthread.h>
-
 #include <mosquitto.h>
 
 #define MQTT_MAX_TOPIC_SIZE         1024
index a239413..9b8d6cf 100644 (file)
@@ -904,6 +904,10 @@ static int mysql_read (user_data_t *ud)
                                counter_submit ("mysql_sort", "scan", val, db);
 
                }
+               else if (strncmp (key, "Slow_queries", strlen ("Slow_queries")) == 0) 
+               {
+                       counter_submit ("mysql_slow_queries", NULL , val, db);
+               }
        }
        mysql_free_result (res); res = NULL;
 
index bf1f2ab..ac076cc 100644 (file)
@@ -36,9 +36,6 @@
 
 #include "network.h"
 
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
 #if HAVE_NETDB_H
 # include <netdb.h>
 #endif
@@ -56,7 +53,6 @@
 #endif
 
 #if HAVE_LIBGCRYPT
-# include <pthread.h>
 # if defined __APPLE__
 /* default xcode compiler throws warnings even when deprecated functionality
  * is not used. -Werror breaks the build because of erroneous warnings.
@@ -1443,6 +1439,7 @@ static int parse_packet (sockent_t *se, /* {{{ */
                                printed_ignore_warning = 1;
                        }
                        buffer = ((char *) buffer) + pkg_length;
+                       buffer_size -= (size_t) pkg_length;
                        continue;
                }
 #endif /* HAVE_LIBGCRYPT */
@@ -1470,6 +1467,7 @@ static int parse_packet (sockent_t *se, /* {{{ */
                                printed_ignore_warning = 1;
                        }
                        buffer = ((char *) buffer) + pkg_length;
+                       buffer_size -= (size_t) pkg_length;
                        continue;
                }
 #endif /* HAVE_LIBGCRYPT */
@@ -1611,6 +1609,7 @@ static int parse_packet (sockent_t *se, /* {{{ */
                        DEBUG ("network plugin: parse_packet: Unknown part"
                                        " type: 0x%04hx", pkg_type);
                        buffer = ((char *) buffer) + pkg_length;
+                       buffer_size -= (size_t) pkg_length;
                }
        } /* while (buffer_size > sizeof (part_header_t)) */
 
index c77b2d1..0120a40 100644 (file)
@@ -28,7 +28,6 @@
 
 #include <auth-client.h>
 #include <libesmtp.h>
-#include <pthread.h>
 
 #define MAXSTRING               256
 
index 1071d0b..f8f4f04 100644 (file)
 # include <poll.h>
 #endif
 
+#ifndef STA_NANO
+# define STA_NANO 0x2000
+#endif
+
 static const char *config_keys[] =
 {
        "Host",
@@ -505,7 +509,7 @@ static int ntpd_receive_response (int *res_items, int *res_size,
                        break;
                }
 
-               memset ((void *) &res, '\0', sizeof (res));
+               memset (&res, '\0', sizeof (res));
                status = recv (sd, (void *) &res, sizeof (res), 0 /* no flags */);
 
                if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
@@ -720,7 +724,7 @@ static int ntpd_send_request (int req_code, int req_items, int req_size, char *r
        if ((sd = ntpd_connect ()) < 0)
                return (-1);
 
-       memset ((void *) &req, '\0', sizeof (req));
+       memset (&req, '\0', sizeof (req));
        req.rm_vn_mode = RM_VN_MODE(0, 0, 0);
        req.auth_seq   = AUTH_SEQ (0, 0);
        req.implementation = IMPL_XNTPD;
@@ -910,8 +914,7 @@ static int ntpd_read (void)
        int i;
 
        /* On Linux, if the STA_NANO bit is set in ik->status, then ik->offset
-        * is is nanoseconds, otherwise it's microseconds.
-        * TODO(octo): STA_NANO is defined in the Linux specific <sys/timex.h> header. */
+        * is is nanoseconds, otherwise it's microseconds. */
        double scale_loop  = 1e-6;
        double scale_error = 1e-6;
 
@@ -936,6 +939,11 @@ static int ntpd_read (void)
                return (-1);
        }
 
+       if (ntohs(ik->status) & STA_NANO) {
+               scale_loop  = 1e-9;
+               scale_error = 1e-9;
+       }
+
        /* kerninfo -> estimated error */
        offset_loop  = scale_loop * ((gauge_t) ntohl (ik->offset));
        freq_loop    = ntpd_read_fp (ik->freq);
index 17eb1b9..a033e2f 100644 (file)
--- a/src/nut.c
+++ b/src/nut.c
@@ -28,7 +28,6 @@
 #include "common.h"
 #include "plugin.h"
 
-#include <pthread.h>
 #include <upsclient.h>
 
 #if HAVE_UPSCONN_T
index 92a162b..7d78630 100644 (file)
@@ -29,7 +29,7 @@
  * interface for collectd plugins written in perl.
  */
 
-/* do not automatically get the thread specific perl interpreter */
+/* do not automatically get the thread specific Perl interpreter */
 #define PERL_NO_GET_CONTEXT
 
 #define DONT_POISON_SPRINTF_YET 1
@@ -63,8 +63,6 @@
 
 #include "filter_chain.h"
 
-#include <pthread.h>
-
 #if !defined(USE_ITHREADS)
 # error "Perl does not support ithreads!"
 #endif /* !defined(USE_ITHREADS) */
@@ -123,6 +121,9 @@ static XS (Collectd_call_by_name);
 typedef struct c_ithread_s {
        /* the thread's Perl interpreter */
        PerlInterpreter *interp;
+       _Bool running;  /* thread is inside Perl interpreter */
+       _Bool shutdown;
+       pthread_t pthread;
 
        /* double linked list of threads */
        struct c_ithread_s *prev;
@@ -139,6 +140,7 @@ typedef struct {
 #endif /* COLLECT_DEBUG */
 
        pthread_mutex_t mutex;
+       pthread_mutexattr_t mutexattr;
 } c_ithread_list_t;
 
 /* name / user_data for Perl matches / targets */
@@ -999,6 +1001,32 @@ static int pplugin_dispatch_notification (pTHX_ HV *notif)
 } /* static int pplugin_dispatch_notification (HV *) */
 
 /*
+ * Call perl sub with thread locking flags handled.
+ */
+static int call_pv_locked (pTHX_ const char* sub_name)
+{
+       _Bool old_running;
+       int ret;
+
+       c_ithread_t *t = (c_ithread_t *)pthread_getspecific(perl_thr_key);
+       if (t == NULL) /* thread destroyed */
+               return 0;
+
+       old_running = t->running;
+       t->running = 1;
+
+       if (t->shutdown) {
+               t->running = old_running;
+               return 0;
+       }
+
+       ret = call_pv (sub_name, G_SCALAR);
+
+       t->running = old_running;
+       return ret;
+} /* static int call_pv_locked (pTHX, *sub_name) */
+
+/*
  * Call all working functions of the given type.
  */
 static int pplugin_call_all (pTHX_ int type, ...)
@@ -1127,7 +1155,7 @@ static int pplugin_call_all (pTHX_ int type, ...)
 
        PUTBACK;
 
-       retvals = call_pv ("Collectd::plugin_call_all", G_SCALAR);
+       retvals = call_pv_locked (aTHX_ "Collectd::plugin_call_all");
 
        SPAGAIN;
        if (0 < retvals) {
@@ -1145,7 +1173,7 @@ static int pplugin_call_all (pTHX_ int type, ...)
 } /* static int pplugin_call_all (int, ...) */
 
 /*
- * collectd's perl interpreter based thread implementation.
+ * collectd's Perl interpreter based thread implementation.
  *
  * This has been inspired by Perl's ithreads introduced in version 5.6.0.
  */
@@ -1248,6 +1276,9 @@ static c_ithread_t *c_ithread_create (PerlInterpreter *base)
                t->prev = perl_threads->tail;
        }
 
+       t->pthread = pthread_self();
+       t->running = 0;
+       t->shutdown = 0;
        perl_threads->tail = t;
 
        pthread_setspecific (perl_thr_key, (const void *)t);
@@ -1368,7 +1399,7 @@ static int fc_call (pTHX_ int type, int cb_type, pfc_user_data_t *data, ...)
 
        PUTBACK;
 
-       retvals = call_pv ("Collectd::fc_call", G_SCALAR);
+       retvals = call_pv_locked (aTHX_ "Collectd::fc_call");
 
        if ((FC_CB_EXEC == cb_type) && (meta != NULL)) {
                assert (pmeta != NULL);
@@ -1908,6 +1939,7 @@ static XS (Collectd_call_by_name)
 
 static int perl_init (void)
 {
+       int status;
        dTHX;
 
        if (NULL == perl_threads)
@@ -1925,7 +1957,19 @@ static int perl_init (void)
 
        log_debug ("perl_init: c_ithread: interp = %p (active threads: %i)",
                        aTHX, perl_threads->number_of_threads);
-       return pplugin_call_all (aTHX_ PLUGIN_INIT);
+
+       /* Lock the base thread to avoid race conditions with c_ithread_create().
+        * See https://github.com/collectd/collectd/issues/9 and
+        *     https://github.com/collectd/collectd/issues/1706 for details.
+       */
+       assert (aTHX == perl_threads->head->interp);
+       pthread_mutex_lock (&perl_threads->mutex);
+
+       status = pplugin_call_all (aTHX_ PLUGIN_INIT);
+
+       pthread_mutex_unlock (&perl_threads->mutex);
+
+       return status;
 } /* static int perl_init (void) */
 
 static int perl_read (void)
@@ -2010,7 +2054,9 @@ static void perl_log (int level, const char *msg,
 
        /* Lock the base thread if this is not called from one of the read threads
         * to avoid race conditions with c_ithread_create(). See
-        * https://github.com/collectd/collectd/issues/9 for details. */
+        * https://github.com/collectd/collectd/issues/9 for details.
+       */
+
        if (aTHX == perl_threads->head->interp)
                pthread_mutex_lock (&perl_threads->mutex);
 
@@ -2098,17 +2144,31 @@ static int perl_shutdown (void)
        t = perl_threads->tail;
 
        while (NULL != t) {
+               struct timespec ts_wait;
                c_ithread_t *thr = t;
 
                /* the pointer has to be advanced before destroying
                 * the thread as this will free the memory */
                t = t->prev;
 
+               thr->shutdown = 1;
+               if (thr->running) {
+                       /* Give some time to thread to exit from Perl interpreter */
+                       WARNING ("perl shutdown: Thread is running inside Perl. Waiting.");
+                       ts_wait.tv_sec = 0;
+                       ts_wait.tv_nsec = 500000;
+                       nanosleep (&ts_wait, NULL);
+               }
+               if (thr->running) {
+                       pthread_kill (thr->pthread, SIGTERM);
+                       ERROR ("perl shutdown: Thread hangs inside Perl. Thread killed.");
+               }
                c_ithread_destroy (thr);
        }
 
        pthread_mutex_unlock (&perl_threads->mutex);
        pthread_mutex_destroy (&perl_threads->mutex);
+       pthread_mutexattr_destroy (&perl_threads->mutexattr);
 
        sfree (perl_threads);
 
@@ -2252,7 +2312,9 @@ static int init_pi (int argc, char **argv)
        perl_threads = smalloc (sizeof (*perl_threads));
        memset (perl_threads, 0, sizeof (c_ithread_list_t));
 
-       pthread_mutex_init (&perl_threads->mutex, NULL);
+       pthread_mutexattr_init(&perl_threads->mutexattr);
+       pthread_mutexattr_settype(&perl_threads->mutexattr, PTHREAD_MUTEX_RECURSIVE);
+       pthread_mutex_init (&perl_threads->mutex, &perl_threads->mutexattr);
        /* locking the mutex should not be necessary at this point
         * but let's just do it for the sake of completeness */
        pthread_mutex_lock (&perl_threads->mutex);
@@ -2333,7 +2395,7 @@ static int perl_config_loadplugin (pTHX_ oconfig_item_t *ci)
 
        aTHX = perl_threads->head->interp;
 
-       log_debug ("perl_config: loading perl plugin \"%s\"", value);
+       log_debug ("perl_config: Loading Perl plugin \"%s\"", value);
        load_module (PERL_LOADMOD_NOIMPORT,
                        newSVpv (module_name, strlen (module_name)), Nullsv);
        return 0;
index 980fbf2..cfc0777 100644 (file)
@@ -28,7 +28,6 @@
 #include "plugin.h"
 #include "configfile.h"
 
-#include <pthread.h>
 #include <netdb.h>
 #include <poll.h>
 
index 88661e9..e16ba07 100644 (file)
@@ -30,7 +30,6 @@
 #include "configfile.h"
 #include "utils_complain.h"
 
-#include <pthread.h>
 #include <netinet/in.h>
 #if HAVE_NETDB_H
 # include <netdb.h> /* NI_MAXHOST */
index 759560e..41ce95c 100644 (file)
 #include "utils_db_query.h"
 #include "utils_complain.h"
 
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
-
 #include <pg_config_manual.h>
 #include <libpq-fe.h>
 
index 5bacd5b..f029a6c 100644 (file)
@@ -366,29 +366,25 @@ static int ps_list_match (const char *name, const char *cmdline, procstat_t *ps)
        return (0);
 } /* int ps_list_match */
 
-static void ps_update_counter (
-        _Bool init,
-        derive_t *group_counter,
-        derive_t *curr_counter, unsigned long *curr_value,
-        derive_t new_counter, unsigned long new_value) {
-    if (init)
-    {
-        *curr_value = new_value;
-        *curr_counter += new_value;
-        *group_counter += new_value;
-        return;
-    }
-
-    if (new_counter < *curr_counter)
-    {
-        *curr_value = new_counter + (ULONG_MAX - *curr_counter);
-    }
-    else
-    {
-        *curr_value = new_counter - *curr_counter;
-    }
-    *curr_counter = new_counter;
-    *group_counter += *curr_value;
+static void ps_update_counter (_Bool init, derive_t *group_counter,
+                               derive_t *curr_counter, unsigned long *curr_value,
+                               derive_t new_counter, unsigned long new_value)
+{
+       if (init)
+       {
+               *curr_value = new_value;
+               *curr_counter += new_value;
+               *group_counter += new_value;
+               return;
+       }
+
+       if (new_counter < *curr_counter)
+               *curr_value = new_counter + (ULONG_MAX - *curr_counter);
+       else
+               *curr_value = new_counter - *curr_counter;
+
+       *curr_counter = new_counter;
+       *group_counter += *curr_value;
 }
 
 /* add process entry to 'instances' of process 'name' (or refresh it) */
@@ -402,7 +398,7 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
 
        for (ps = list_head_g; ps != NULL; ps = ps->next)
        {
-        _Bool want_init;
+               _Bool want_init;
 
                if ((ps_list_match (name, cmdline, ps)) == 0)
                        continue;
@@ -882,8 +878,8 @@ static procstat_t *ps_read_tasks_status (long pid, procstat_t *ps)
                if (fclose (fh))
                {
                        char errbuf[1024];
-                               WARNING ("processes: fclose: %s",
-                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       WARNING ("processes: fclose: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                }
        }
        closedir (dh);
index 1ed6dc0..61d464d 100644 (file)
@@ -28,9 +28,6 @@
 #include <structmember.h>
 
 #include <signal.h>
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
 
 #include "collectd.h"
 #include "common.h"
index 919a831..0814126 100644 (file)
@@ -25,7 +25,6 @@
 #include "plugin.h"
 #include "configfile.h"
 
-#include <pthread.h>
 #include <sys/time.h>
 #include <hiredis/hiredis.h>
 
index 3cd6d6b..21b01d9 100644 (file)
 
 #include <rrd.h>
 
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
-
 /*
  * Private types
  */
index 6c781f8..d8c65c0 100644 (file)
@@ -27,7 +27,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
-#include <pthread.h>
 
 #include <glib.h>
 #include <libsigrok/libsigrok.h>
index 7b39aae..33b9751 100644 (file)
 static const char *config_keys[] =
 {
   "Disk",
-  "IgnoreSelected"
+  "IgnoreSelected",
+  "IgnoreSleepMode",
+  "UseSerial"
 };
 
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
 static ignorelist_t *ignorelist = NULL;
+static int ignore_sleep_mode = 0;
+static int use_serial = 0;
 
 static int smart_config (const char *key, const char *value)
 {
@@ -60,6 +64,16 @@ static int smart_config (const char *key, const char *value)
       invert = 0;
     ignorelist_set_invert (ignorelist, invert);
   }
+  else if (strcasecmp ("IgnoreSleepMode", key) == 0)
+  {
+    if (IS_TRUE (value))
+      ignore_sleep_mode = 1;
+  }
+  else if (strcasecmp ("UseSerial", key) == 0)
+  {
+    if (IS_TRUE (value))
+      use_serial = 1;
+  }
   else
   {
     return (-1);
@@ -130,7 +144,7 @@ static void smart_handle_disk_attribute(SkDisk *d, const SkSmartAttributeParsedD
   }
 }
 
-static void smart_handle_disk (const char *dev)
+static void smart_handle_disk (const char *dev, const char *serial)
 {
   SkDisk *d = NULL;
   SkBool awake = FALSE;
@@ -139,9 +153,16 @@ static void smart_handle_disk (const char *dev)
   const SkSmartParsedData *spd;
   uint64_t poweron, powercycles, badsectors, temperature;
 
-  shortname = strrchr(dev, '/');
-  if (!shortname) return;
-  shortname++;
+  if (use_serial && serial)
+  {
+    shortname = serial;
+  }
+  else
+  {
+    shortname = strrchr(dev, '/');
+    if (!shortname) return;
+    shortname++;
+  }
   if (ignorelist_match (ignorelist, shortname) != 0) {
     DEBUG ("smart plugin: ignoring %s.", dev);
     return;
@@ -165,10 +186,13 @@ static void smart_handle_disk (const char *dev)
     DEBUG ("smart plugin: disk %s has no SMART support.", dev);
     goto end;
   }
-  if (sk_disk_check_sleep_mode (d, &awake) < 0 || !awake)
+  if (!ignore_sleep_mode)
   {
-    DEBUG ("smart plugin: disk %s is sleeping.", dev);
-    goto end;
+    if (sk_disk_check_sleep_mode (d, &awake) < 0 || !awake)
+    {
+      DEBUG ("smart plugin: disk %s is sleeping.", dev);
+      goto end;
+    }
   }
   if (sk_disk_smart_read_data (d) < 0)
   {
@@ -247,13 +271,14 @@ static int smart_read (void)
   devices = udev_enumerate_get_list_entry (enumerate);
   udev_list_entry_foreach (dev_list_entry, devices)
   {
-    const char *path, *devpath;
+    const char *path, *devpath, *serial;
     path = udev_list_entry_get_name (dev_list_entry);
     dev = udev_device_new_from_syspath (handle_udev, path);
     devpath = udev_device_get_devnode (dev);
+    serial = udev_device_get_property_value (dev, "ID_SERIAL");
 
     /* Query status with libatasmart */
-    smart_handle_disk (devpath);
+    smart_handle_disk (devpath, serial);
     udev_device_unref (dev);
   }
 
index 89cbc46..6a5bb8b 100644 (file)
@@ -29,8 +29,6 @@
 #include "plugin.h"
 #include "utils_complain.h"
 
-#include <pthread.h>
-
 #include <net-snmp/net-snmp-config.h>
 #include <net-snmp/net-snmp-includes.h>
 
index 3c08861..1819b3e 100644 (file)
@@ -32,8 +32,6 @@
 #include "utils_complain.h"
 #include "utils_latency.h"
 
-#include <pthread.h>
-
 #include <sys/types.h>
 #include <netdb.h>
 #include <poll.h>
index 41f58e5..638109c 100644 (file)
@@ -723,7 +723,7 @@ static int tss2_config (const char *key, const char *value)
        /*
         * Interpret configuration values
         */
-    if (strcasecmp ("Host", key) == 0)
+       if (strcasecmp ("Host", key) == 0)
        {
                char *temp;
 
@@ -806,7 +806,7 @@ static int tss2_read (void)
 
        if (success == 0)
                return (-1);
-    return (0);
+       return (0);
 } /* int tss2_read */
 
 static int tss2_shutdown(void)
index 2e4bf05..1bcc276 100644 (file)
@@ -56,12 +56,14 @@ static int check_count__ = 0;
 #define OK(cond) OK1(cond, #cond)
 
 #define EXPECT_EQ_STR(expect, actual) do { \
-  if (strcmp (expect, actual) != 0) { \
+  /* Evaluate 'actual' only once. */ \
+  const char *got__ = actual; \
+  if (strcmp (expect, got__) != 0) { \
     printf ("not ok %i - %s = \"%s\", want \"%s\"\n", \
-        ++check_count__, #actual, actual, expect); \
+        ++check_count__, #actual, got__, expect); \
     return (-1); \
   } \
-  printf ("ok %i - %s = \"%s\"\n", ++check_count__, #actual, actual); \
+  printf ("ok %i - %s = \"%s\"\n", ++check_count__, #actual, got__); \
 } while (0)
 
 #define EXPECT_EQ_INT(expect, actual) do { \
index e849b1d..94a4f02 100644 (file)
@@ -31,7 +31,6 @@
 #include "utils_threshold.h"
 
 #include <assert.h>
-#include <pthread.h>
 
 /*
  * Threshold management
index e0d5806..e5719bc 100644 (file)
@@ -21,6 +21,13 @@ ceph_latency            value:GAUGE:U:U
 ceph_rate               value:DERIVE:0:U
 changes_since_last_save value:GAUGE:0:U
 charge                  value:GAUGE:0:U
+clock_last_meas         value:GAUGE:0:U
+clock_last_update       value:GAUGE:U:U
+clock_mode              value:GAUGE:0:U
+clock_reachability      value:GAUGE:0:U
+clock_skew_ppm          value:GAUGE:-2:2
+clock_state             value:GAUGE:0:U
+clock_stratum           value:GAUGE:0:U
 compression             uncompressed:DERIVE:0:U, compressed:DERIVE:0:U
 compression_ratio       value:GAUGE:0:2
 connections             value:DERIVE:0:U
@@ -77,6 +84,7 @@ files                   value:GAUGE:0:U
 flow                    value:GAUGE:0:U
 fork_rate               value:DERIVE:0:U
 frequency               value:GAUGE:0:U
+frequency_error         value:GAUGE:-2:2 
 frequency_offset        value:GAUGE:-1000000:1000000
 fscache_stat            value:DERIVE:0:U
 gauge                   value:GAUGE:U:U
@@ -133,6 +141,7 @@ mysql_select            value:DERIVE:0:U
 mysql_sort              value:DERIVE:0:U
 mysql_sort_merge_passes value:DERIVE:0:U
 mysql_sort_rows         value:DERIVE:0:U
+mysql_slow_queries      value:DERIVE:0:U
 nfs_procedure           value:DERIVE:0:U
 nginx_connections       value:GAUGE:0:U
 nginx_requests          value:DERIVE:0:U
@@ -183,6 +192,8 @@ records                 value:GAUGE:0:U
 requests                value:GAUGE:0:U
 response_code           value:GAUGE:0:U
 response_time           value:GAUGE:0:U
+root_delay              value:GAUGE:U:U
+root_dispersion         value:GAUGE:U:U
 route_etx               value:GAUGE:0:U
 route_metric            value:GAUGE:0:U
 routes                  value:GAUGE:0:U
@@ -207,6 +218,9 @@ temperature             value:GAUGE:U:U
 threads                 value:GAUGE:0:U
 time_dispersion         value:GAUGE:-1000000:1000000
 time_offset             value:GAUGE:-1000000:1000000
+time_offset_ntp         value:GAUGE:-1000000:1000000
+time_offset_rms         value:GAUGE:-1000000:1000000
+time_ref                value:GAUGE:0:U
 timeleft                value:GAUGE:0:U
 total_bytes             value:DERIVE:0:U
 total_connections       value:DERIVE:0:U
index 1840e34..d4bf004 100644 (file)
@@ -36,9 +36,6 @@
 #include "utils_cmd_putval.h"
 #include "utils_cmd_putnotif.h"
 
-/* Folks without pthread will need to disable this plugin. */
-#include <pthread.h>
-
 #include <sys/stat.h>
 #include <sys/un.h>
 
index 009ab3d..4c40e55 100644 (file)
@@ -218,8 +218,8 @@ int handle_putval (FILE *fh, char *buffer)
        } /* while (*buffer != 0) */
        /* Done parsing the options. */
 
-    if (fh!=stdout)
-           print_to_socket (fh, "0 Success: %i %s been dispatched.\n",
+       if (fh!=stdout)
+               print_to_socket (fh, "0 Success: %i %s been dispatched.\n",
                        values_submitted,
                        (values_submitted == 1) ? "value has" : "values have");
 
diff --git a/src/utils_curl_stats.c b/src/utils_curl_stats.c
new file mode 100644 (file)
index 0000000..258c5cf
--- /dev/null
@@ -0,0 +1,249 @@
+/**
+ * collectd - src/utils_curl_stats.c
+ * Copyright (C) 2015       Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sebastian Harl <sh@tokkee.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_curl_stats.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+
+struct curl_stats_s
+{
+       bool total_time;
+       bool namelookup_time;
+       bool connect_time;
+       bool pretransfer_time;
+       bool size_upload;
+       bool size_download;
+       bool speed_download;
+       bool speed_upload;
+       bool header_size;
+       bool request_size;
+       bool content_length_download;
+       bool content_length_upload;
+       bool starttransfer_time;
+       bool redirect_time;
+       bool redirect_count;
+       bool num_connects;
+       bool appconnect_time;
+};
+
+/*
+ * Private functions
+ */
+
+static int dispatch_gauge (CURL *curl, CURLINFO info, value_list_t *vl)
+{
+       CURLcode code;
+       value_t v;
+
+       code = curl_easy_getinfo (curl, info, &v.gauge);
+       if (code != CURLE_OK)
+               return -1;
+
+       vl->values = &v;
+       vl->values_len = 1;
+
+       return plugin_dispatch_values (vl);
+} /* dispatch_gauge */
+
+/* dispatch a speed, in bytes/second */
+static int dispatch_speed (CURL *curl, CURLINFO info, value_list_t *vl)
+{
+       CURLcode code;
+       value_t v;
+
+       code = curl_easy_getinfo (curl, info, &v.gauge);
+       if (code != CURLE_OK)
+               return -1;
+
+       v.gauge *= 8;
+
+       vl->values = &v;
+       vl->values_len = 1;
+
+       return plugin_dispatch_values (vl);
+} /* dispatch_speed */
+
+/* dispatch a size/count, reported as a long value */
+static int dispatch_size (CURL *curl, CURLINFO info, value_list_t *vl)
+{
+       CURLcode code;
+       value_t v;
+       long raw;
+
+       code = curl_easy_getinfo (curl, info, &raw);
+       if (code != CURLE_OK)
+               return -1;
+
+       v.gauge = (double)raw;
+
+       vl->values = &v;
+       vl->values_len = 1;
+
+       return plugin_dispatch_values (vl);
+} /* dispatch_size */
+
+static struct {
+       const char *name;
+       const char *config_key;
+       size_t offset;
+
+       int (*dispatcher)(CURL *, CURLINFO, value_list_t *);
+       const char *type;
+       CURLINFO info;
+} field_specs[] = {
+#define SPEC(name, config_key, dispatcher, type, info) \
+       { #name, config_key, offsetof (curl_stats_t, name), dispatcher, type, info }
+
+       SPEC (total_time,              "TotalTime",              dispatch_gauge, "duration", CURLINFO_TOTAL_TIME),
+       SPEC (namelookup_time,         "NamelookupTime",         dispatch_gauge, "duration", CURLINFO_NAMELOOKUP_TIME),
+       SPEC (connect_time,            "ConnectTime",            dispatch_gauge, "duration", CURLINFO_CONNECT_TIME),
+       SPEC (pretransfer_time,        "PretransferTime",        dispatch_gauge, "duration", CURLINFO_PRETRANSFER_TIME),
+       SPEC (size_upload,             "SizeUpload",             dispatch_gauge, "bytes",    CURLINFO_SIZE_UPLOAD),
+       SPEC (size_download,           "SizeDownload",           dispatch_gauge, "bytes",    CURLINFO_SIZE_DOWNLOAD),
+       SPEC (speed_download,          "SpeedDownload",          dispatch_speed, "bitrate",  CURLINFO_SPEED_DOWNLOAD),
+       SPEC (speed_upload,            "SpeedUpload",            dispatch_speed, "bitrate",  CURLINFO_SPEED_UPLOAD),
+       SPEC (header_size,             "HeaderSize",             dispatch_size,  "bytes",    CURLINFO_HEADER_SIZE),
+       SPEC (request_size,            "RequestSize",            dispatch_size,  "bytes",    CURLINFO_REQUEST_SIZE),
+       SPEC (content_length_download, "ContentLengthDownload",  dispatch_gauge, "bytes",    CURLINFO_CONTENT_LENGTH_DOWNLOAD),
+       SPEC (content_length_upload,   "ContentLengthUpload",    dispatch_gauge, "bytes",    CURLINFO_CONTENT_LENGTH_UPLOAD),
+       SPEC (starttransfer_time,      "StarttransferTime",      dispatch_gauge, "duration", CURLINFO_STARTTRANSFER_TIME),
+       SPEC (redirect_time,           "RedirectTime",           dispatch_gauge, "duration", CURLINFO_REDIRECT_TIME),
+       SPEC (redirect_count,          "RedirectCount",          dispatch_size,  "count",    CURLINFO_REDIRECT_COUNT),
+       SPEC (num_connects,            "NumConnects",            dispatch_size,  "count",    CURLINFO_NUM_CONNECTS),
+#ifdef HAVE_CURLINFO_APPCONNECT_TIME
+       SPEC (appconnect_time,         "AppconnectTime",         dispatch_gauge, "duration", CURLINFO_APPCONNECT_TIME),
+#endif
+
+#undef SPEC
+};
+
+static void enable_field (curl_stats_t *s, size_t offset)
+{
+       *(bool *)((char *)s + offset) = true;
+} /* enable_field */
+
+static bool field_enabled (curl_stats_t *s, size_t offset)
+{
+       return *(bool *)((char *)s + offset);
+} /* field_enabled */
+
+/*
+ * Public API
+ */
+curl_stats_t *curl_stats_from_config (oconfig_item_t *ci)
+{
+       curl_stats_t *s;
+       int i;
+
+       if (ci == NULL)
+               return NULL;
+
+       s = calloc (1, sizeof (*s));
+       if (s == NULL)
+               return NULL;
+
+       for (i = 0; i < ci->children_num; ++i)
+       {
+               oconfig_item_t *c = ci->children + i;
+               size_t field;
+
+               _Bool enabled = 0;
+
+               for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field) {
+                       if (! strcasecmp (c->key, field_specs[field].config_key))
+                               break;
+                       if (! strcasecmp (c->key, field_specs[field].name))
+                               break;
+               }
+               if (field >= STATIC_ARRAY_SIZE (field_specs))
+               {
+                       ERROR ("curl stats: Unknown field name %s", c->key);
+                       free (s);
+                       return NULL;
+               }
+
+
+               if (cf_util_get_boolean (c, &enabled) != 0) {
+                       free (s);
+                       return NULL;
+               }
+               if (enabled)
+                       enable_field (s, field_specs[field].offset);
+       }
+
+       return s;
+} /* curl_stats_from_config */
+
+void curl_stats_destroy (curl_stats_t *s)
+{
+       if (s != NULL)
+               free (s);
+} /* curl_stats_destroy */
+
+int curl_stats_dispatch (curl_stats_t *s, CURL *curl,
+               const char *hostname, const char *plugin, const char *plugin_instance)
+{
+       value_list_t vl = VALUE_LIST_INIT;
+       size_t field;
+
+       if (s == NULL)
+               return 0;
+       if ((curl == NULL) || (hostname == NULL) || (plugin == NULL))
+       {
+               ERROR ("curl stats: dispatch() called with missing arguments "
+                               "(curl=%p; hostname=%s; plugin=%s)", curl,
+                               hostname == NULL ? "<NULL>" : hostname,
+                               plugin == NULL ? "<NULL>" : plugin);
+               return -1;
+       }
+
+       sstrncpy (vl.host, hostname, sizeof (vl.host));
+       sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
+       if (plugin_instance != NULL)
+               sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+
+       for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field)
+       {
+               int status;
+
+               if (! field_enabled (s, field_specs[field].offset))
+                       continue;
+
+               sstrncpy (vl.type, field_specs[field].type, sizeof (vl.type));
+               sstrncpy (vl.type_instance, field_specs[field].name, sizeof (vl.type_instance));
+
+               vl.values = NULL;
+               vl.values_len = 0;
+               status = field_specs[field].dispatcher (curl, field_specs[field].info, &vl);
+               if (status < 0)
+                       return status;
+       }
+
+       return 0;
+} /* curl_stats_dispatch */
diff --git a/src/utils_curl_stats.h b/src/utils_curl_stats.h
new file mode 100644 (file)
index 0000000..f708c62
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * collectd - src/utils_curl_stats.h
+ * Copyright (C) 2015       Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sebastian Harl <sh@tokkee.org>
+ **/
+
+#ifndef UTILS_CURL_STATS_H
+#define UTILS_CURL_STATS_H 1
+
+#include "configfile.h"
+#include "plugin.h"
+
+#include <curl/curl.h>
+
+struct curl_stats_s;
+typedef struct curl_stats_s curl_stats_t;
+
+/*
+ * curl_stats_from_config allocates and constructs a cURL statistics object
+ * from the specified configuration which is expected to be a single block of
+ * boolean options named after cURL information fields. The boolean value
+ * indicates whether to collect the respective information.
+ *
+ * See http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html
+ */
+curl_stats_t *curl_stats_from_config (oconfig_item_t *ci);
+
+void curl_stats_destroy (curl_stats_t *s);
+
+/*
+ * curl_stats_dispatch dispatches performance values from the the specified
+ * cURL session to the daemon.
+ */
+int curl_stats_dispatch (curl_stats_t *s, CURL *curl,
+               const char *hostname, const char *plugin, const char *plugin_instance);
+
+#endif /* UTILS_CURL_STATS_H */
index 91fc142..f683fb5 100644 (file)
@@ -54,6 +54,7 @@ struct udb_query_s /* {{{ */
   char *name;
   char *statement;
   void *user_data;
+  char *plugin_instance_from;
 
   unsigned int min_version;
   unsigned int max_version;
@@ -70,6 +71,7 @@ struct udb_result_preparation_area_s /* {{{ */
   char  **instances_buffer;
   char  **values_buffer;
   char  **metadata_buffer;
+  char   *plugin_instance;
 
   struct udb_result_preparation_area_s *next;
 }; /* }}} */
@@ -78,6 +80,7 @@ typedef struct udb_result_preparation_area_s udb_result_preparation_area_t;
 struct udb_query_preparation_area_s /* {{{ */
 {
   size_t column_num;
+  size_t plugin_instance_pos;
   char *host;
   char *plugin;
   char *db_name;
@@ -231,9 +234,16 @@ static int udb_result_submit (udb_result_t *r, /* {{{ */
 
   sstrncpy (vl.host, q_area->host, sizeof (vl.host));
   sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.plugin_instance));
   sstrncpy (vl.type, r->type, sizeof (vl.type));
 
+  /* Set vl.plugin_instance */
+  if (q->plugin_instance_from != NULL) {
+    sstrncpy (vl.plugin_instance, r_area->plugin_instance, sizeof (vl.plugin_instance));
+  }
+  else {
+    sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.plugin_instance));
+  }
+
   /* Set vl.type_instance {{{ */
   if (r->instances_num == 0)
   {
@@ -334,6 +344,9 @@ static int udb_result_handle_result (udb_result_t *r, /* {{{ */
   for (i = 0; i < r->metadata_num; i++)
     r_area->metadata_buffer[i] = column_values[r_area->metadata_pos[i]];
 
+  if (q->plugin_instance_from)
+    r_area->plugin_instance = column_values[q_area->plugin_instance_pos];
+
   return udb_result_submit (r, r_area, q, q_area);
 } /* }}} int udb_result_handle_result */
 
@@ -438,7 +451,7 @@ static int udb_result_prepare_result (udb_result_t const *r, /* {{{ */
 
   /* }}} */
 
-  /* Determine the position of the instance columns {{{ */
+  /* Determine the position of the plugin instance column {{{ */
   for (i = 0; i < r->instances_num; i++)
   {
     size_t j;
@@ -461,6 +474,7 @@ static int udb_result_prepare_result (udb_result_t const *r, /* {{{ */
     }
   } /* }}} for (i = 0; i < r->instances_num; i++) */
 
+
   /* Determine the position of the value columns {{{ */
   for (i = 0; i < r->values_num; i++)
   {
@@ -519,6 +533,7 @@ static void udb_result_free (udb_result_t *r) /* {{{ */
     return;
 
   sfree (r->type);
+  sfree (r->instance_prefix);
 
   for (i = 0; i < r->instances_num; i++)
     sfree (r->instances[i]);
@@ -645,6 +660,7 @@ static void udb_query_free_one (udb_query_t *q) /* {{{ */
 
   sfree (q->name);
   sfree (q->statement);
+  sfree (q->plugin_instance_from);
 
   udb_result_free (q->results);
 
@@ -686,6 +702,9 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */
   }
   q->min_version = 0;
   q->max_version = UINT_MAX;
+  q->statement = NULL;
+  q->results = NULL;
+  q->plugin_instance_from = NULL;
 
   status = udb_config_set_string (&q->name, ci);
   if (status != 0)
@@ -707,6 +726,8 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */
       status = udb_config_set_uint (&q->min_version, child);
     else if (strcasecmp ("MaxVersion", child->key) == 0)
       status = udb_config_set_uint (&q->max_version, child);
+    else if (strcasecmp ("PluginInstanceFrom", child->key) == 0)
+      status = udb_config_set_string (&q->plugin_instance_from, child);
 
     /* Call custom callbacks */
     else if (cb != NULL)
@@ -1042,6 +1063,31 @@ int udb_query_prepare_result (udb_query_t const *q, /* {{{ */
   } while (0);
 #endif
 
+  /* Determine the position of the PluginInstance column {{{ */
+  if (q->plugin_instance_from != NULL)
+  {
+    size_t i;
+
+    for (i = 0; i < column_num; i++)
+    {
+      if (strcasecmp (q->plugin_instance_from, column_names[i]) == 0)
+      {
+        prep_area->plugin_instance_pos = i;
+        break;
+      }
+    }
+
+    if (i >= column_num)
+    {
+      ERROR ("db query utils: udb_query_prepare_result: "
+          "Column `%s' from `PluginInstanceFrom' could not be found.",
+          q->plugin_instance_from);
+      udb_query_finish_result (q, prep_area);
+      return (-ENOENT);
+    }
+  }
+  /* }}} */
+
   for (r = q->results, r_area = prep_area->result_prep_areas;
       r != NULL; r = r->next, r_area = r_area->next)
   {
index d9d685a..dc6f79f 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 #include "collectd.h"
+#include "common.h"
 #include "testing.h"
 #include "utils_mount.h"
 
@@ -80,16 +81,25 @@ DEF_TEST(cu_mount_getoptionvalue)
 {
   char line_opts[] = "foo=one,bar=two,qux=three";
   char line_bool[] = "one,two,three";
-
-  EXPECT_EQ_STR ("one", cu_mount_getoptionvalue (line_opts, "foo="));
-  EXPECT_EQ_STR ("two", cu_mount_getoptionvalue (line_opts, "bar="));
-  EXPECT_EQ_STR ("three", cu_mount_getoptionvalue (line_opts, "qux="));
-  OK (NULL == cu_mount_getoptionvalue (line_opts, "unknown="));
-
-  EXPECT_EQ_STR ("", cu_mount_getoptionvalue (line_bool, "one"));
-  EXPECT_EQ_STR ("", cu_mount_getoptionvalue (line_bool, "two"));
-  EXPECT_EQ_STR ("", cu_mount_getoptionvalue (line_bool, "three"));
-  OK (NULL == cu_mount_getoptionvalue (line_bool, "four"));
+  char *v;
+
+  EXPECT_EQ_STR ("one", v = cu_mount_getoptionvalue (line_opts, "foo="));
+  sfree (v);
+  EXPECT_EQ_STR ("two", v = cu_mount_getoptionvalue (line_opts, "bar="));
+  sfree (v);
+  EXPECT_EQ_STR ("three", v = cu_mount_getoptionvalue (line_opts, "qux="));
+  sfree (v);
+  OK (NULL == (v = cu_mount_getoptionvalue (line_opts, "unknown=")));
+  sfree (v);
+
+  EXPECT_EQ_STR ("", v = cu_mount_getoptionvalue (line_bool, "one"));
+  sfree (v);
+  EXPECT_EQ_STR ("", v = cu_mount_getoptionvalue (line_bool, "two"));
+  sfree (v);
+  EXPECT_EQ_STR ("", v = cu_mount_getoptionvalue (line_bool, "three"));
+  sfree (v);
+  OK (NULL == (v = cu_mount_getoptionvalue (line_bool, "four")));
+  sfree (v);
 
   return (0);
 }
index a023784..8a2e567 100644 (file)
@@ -495,6 +495,21 @@ static void lu_destroy_user_class_list (lookup_t *obj, /* {{{ */
       obj->cb_free_class (user_class_list->entry.user_class);
     user_class_list->entry.user_class = NULL;
 
+#define CLEAR_FIELD(field) do { \
+    if (user_class_list->entry.match.field.is_regex) { \
+      regfree (&user_class_list->entry.match.field.regex); \
+      user_class_list->entry.match.field.is_regex = 0; \
+    } \
+} while (0)
+
+    CLEAR_FIELD (host);
+    CLEAR_FIELD (plugin);
+    CLEAR_FIELD (plugin_instance);
+    CLEAR_FIELD (type);
+    CLEAR_FIELD (type_instance);
+
+#undef CLEAR_FIELD
+
     lu_destroy_user_obj (obj, user_class_list->entry.user_obj_list);
     user_class_list->entry.user_obj_list = NULL;
     pthread_mutex_destroy (&user_class_list->entry.lock);
diff --git a/src/valgrind.FreeBSD.suppress b/src/valgrind.FreeBSD.suppress
new file mode 100644 (file)
index 0000000..28ea677
--- /dev/null
@@ -0,0 +1,8 @@
+{
+   strlen_bogus_invalid_read_after_strdup
+   Memcheck:Addr4
+   fun:parse_value
+   fun:parse_values
+   fun:test_parse_values
+   fun:main
+}
index e5a8725..16bb77b 100644 (file)
@@ -587,7 +587,7 @@ lv_read (void)
             continue;
         }
 
-        status =  virDomainMemoryStats (domains[i], minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
+        status = virDomainMemoryStats (domains[i], minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
 
         if (status < 0) {
             ERROR ("virt plugin: virDomainMemoryStats failed with status %i.",
index 0b8ab41..0f9a8be 100644 (file)
@@ -51,9 +51,6 @@
 #include "utils_complain.h"
 #include "utils_format_graphite.h"
 
-/* Folks without pthread will need to disable this plugin. */
-#include <pthread.h>
-
 #include <netdb.h>
 
 #define WG_DEFAULT_NODE "localhost"
@@ -138,7 +135,10 @@ static void wg_reset_buffer (struct wg_callback *cb)
 
 static int wg_send_buffer (struct wg_callback *cb)
 {
-    ssize_t status = 0;
+    ssize_t status;
+
+    if (cb->sock_fd < 0)
+        return (-1);
 
     status = swrite (cb->sock_fd, cb->send_buf, strlen (cb->send_buf));
     if (status != 0)
index 8dd37c4..779c94f 100644 (file)
 #include "utils_cache.h"
 #include "utils_format_json.h"
 
-#if HAVE_PTHREAD_H
-# include <pthread.h>
-#endif
-
 #include <curl/curl.h>
 
 #ifndef WRITE_HTTP_DEFAULT_BUFFER_SIZE
index 10ae5a5..614ce0f 100644 (file)
@@ -36,7 +36,6 @@
 
 #include <stdint.h>
 #include <librdkafka/rdkafka.h>
-#include <pthread.h>
 #include <zlib.h>
 #include <errno.h>
 
@@ -74,11 +73,19 @@ static void kafka_log(const rd_kafka_t *rkt, int level,
 }
 #endif
 
+static uint32_t kafka_hash(const char *keydata, size_t keylen)
+{
+    uint32_t hash = 5381;
+    for (; keylen > 0; keylen--)
+        hash = ((hash << 5) + hash) + keydata[keylen - 1];
+    return hash;
+}
+
 static int32_t kafka_partition(const rd_kafka_topic_t *rkt,
                                const void *keydata, size_t keylen,
                                int32_t partition_cnt, void *p, void *m)
 {
-    uint32_t key = *((uint32_t *)keydata );
+    uint32_t key = kafka_hash(keydata, keylen);
     uint32_t target = key % partition_cnt;
     int32_t  i = partition_cnt;
 
index ebf0e12..db09849 100644 (file)
@@ -32,9 +32,6 @@
 
 #include "utils_format_graphite.h"
 
-/* Folks without pthread will need to disable this plugin. */
-#include <pthread.h>
-
 #include <netdb.h>
 
 #define WL_BUF_SIZE 8192
index 9a6fdf2..fb80ba2 100644 (file)
@@ -34,8 +34,6 @@
 #include "configfile.h"
 #include "utils_cache.h"
 
-#include <pthread.h>
-
 #if HAVE_STDINT_H
 # define MONGO_HAVE_STDINT 1
 #else
index a79eb36..5c299a4 100644 (file)
@@ -29,7 +29,6 @@
 #include "common.h"
 #include "configfile.h"
 
-#include <pthread.h>
 #include <sys/time.h>
 #include <hiredis/hiredis.h>
 
index 2936dfa..ac6b8fb 100644 (file)
  *   Gergely Nagy <algernon at madhouse-project.org>
  */
 
-#include <riemann/riemann-client.h>
-#include <errno.h>
-#include <pthread.h>
-
 #include "collectd.h"
+
+
 #include "plugin.h"
 #include "common.h"
 #include "configfile.h"
@@ -40,6 +38,9 @@
 #include "utils_complain.h"
 #include "write_riemann_threshold.h"
 
+#include <errno.h>
+#include <riemann/riemann-client.h>
+
 #define RIEMANN_HOST           "localhost"
 #define RIEMANN_PORT           5555
 #define RIEMANN_TTL_FACTOR      2.0
index 8567288..a09ae5f 100644 (file)
  *   Andrés J. Díaz <ajdiaz at connectical.com>
  **/
 
-#include <assert.h>
-#include <ltdl.h>
-#include <pthread.h>
-
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 #include "utils_threshold.h"
 #include "write_riemann_threshold.h"
 
+#include <assert.h>
+#include <ltdl.h>
+#include <pthread.h>
+
 /*
  * Threshold management
  * ====================
index 475354c..d37792b 100644 (file)
@@ -35,7 +35,6 @@
 #include <errno.h>
 #include <netdb.h>
 #include <inttypes.h>
-#include <pthread.h>
 #include <stddef.h>
 
 #include <stdlib.h>
index 46028ba..2bd2707 100644 (file)
@@ -48,7 +48,6 @@
 
 #include "utils_cache.h"
 
-#include <pthread.h>
 #include <netdb.h>
 
 #ifndef WT_DEFAULT_NODE
index 89ec6d2..860e93d 100644 (file)
@@ -111,7 +111,7 @@ static int zookeeper_connect (void)
        const char *host;
        const char *port;
 
-       memset ((void *) &ai_hints, '\0', sizeof (ai_hints));
+       memset (&ai_hints, '\0', sizeof (ai_hints));
        ai_hints.ai_family   = AF_UNSPEC;
        ai_hints.ai_socktype = SOCK_STREAM;
 
diff --git a/testwrapper.sh b/testwrapper.sh
new file mode 100755 (executable)
index 0000000..a86705f
--- /dev/null
@@ -0,0 +1,30 @@
+#! /bin/sh
+#
+# collectd -- testwrapper.sh
+#
+# A wrapper script for running tests. If valgrind is available, memory
+# checking will be enabled for all tests.
+
+set -e
+
+MEMCHECK=""
+
+if test -n "$VALGRIND"; then
+       MEMCHECK="$VALGRIND --quiet --tool=memcheck --error-exitcode=1"
+       MEMCHECK="$MEMCHECK --trace-children=yes"
+       MEMCHECK="$MEMCHECK --leak-check=full"
+       MEMCHECK="$MEMCHECK --gen-suppressions=all"
+
+       for f in "valgrind.$( uname -s ).suppress" "valgrind.suppress"; do
+               filename="$( dirname "$0" )/src/$f"
+               if test -e "$filename"; then
+                       # Valgrind supports up to 100 suppression files.
+                       MEMCHECK="$MEMCHECK --suppressions=$filename"
+               fi
+       done
+fi
+
+exec $MEMCHECK "$@"
+
+# vim: set tw=78 sw=4 ts=4 noexpandtab :
+