Add CollectdError exception which can be thrown without causing a stacktrace to be logged.
David Bacher <drbacher at gmail.com>
- serial plugin.
+Denis Pompilio <denis.pompilio at gmail.com>
+ - Improvements to the write_http plugin.
+
Doug MacEachern <dougm at hyperic.com>
- The `-T' option (config testing mode).
- OpenVPN plugin.
Sebastien Pahl <sebastien.pahl at dotcloud.com>
- AMQP plugin.
+Serhiy Pshyk <serhiyx.pshyk at intel.com>
+ - intel_pmu plugin
+ - intel_rdt plugin
+ - snmp_agent plugin
+
Simon Kuhnle <simon at blarzwurst.de>
- OpenBSD code for the cpu and memory plugins.
embedded HTTP server, in a format compatible with Prometheus'
collectd_exporter. Thanks to Florian Forster. #1967
+2017-10-06, Version 5.6.3
+ * collectd: support for boolean string config values has been
+ reintroduced. Thanks to Sebastian Harl. #2083, #2098
+ * collectd: The capability checking has been changed to use
+ "cap_get_proc()". Thanks to Marc Fournier. #2151
+ * Documentation: A section documenting ignore lists has been added to
+ collectd.conf(5). Thanks to Florian Forster.
+ * AMQP plugin: The "ExchangeType" option is now also valid for
+ publishers. Thanks to Florian Forster. #2286
+ * Apache, Ascent, BIND, cURL, cURL-JSON, cURL-XML, nginx, Write HTTP
+ plugins: Handling of URLs that redirect elsewhere has been fixed.
+ Thanks to Pavel Rochnyack. #2328
+ * BIND plugin: Fix parsing of the sample time provided by BIND.
+ Previously, the time was assumed to be in the local time zone when in
+ fact it was in UTC. Thanks to Ed Ravin. #1268
+ * BIND plugin: Memory leaks have been fixed. Thanks to Ruben Kerkhof.
+ #2303
+ * Chrony plugin: Build flags have been fixed. Thanks to Thomas Jost and
+ Marc Fournier. #2133
+ * cURL-JSON plugin: The timeout value has been changed to default to the
+ collection interval. This fixes a regression. Thanks to Marc Fournier.
+ * cURL-JSON plugin: Handling of arrays has been fixed. Thanks to Florian
+ Forster. #2266
+ * DBI plugin: Memory leaks at shutdown have been fixes. Thanks to Pavel
+ Rochnyack and Florian Forster.
+ * E-Mail, Exec, UnixSock plugins: Group ID lookup on systems with many
+ groups has been fixed. Thanks to Ruben Kerkhof and Florian Forster.
+ #2208
+ * IPC plugin: A compilation error on AIX has been fixed. Thanks to Pavel
+ Rochnyack. #2305
+ * LogFile plugin: If writing to the file fails, print log messages on
+ "STDERR" instead. Thanks to Marc Fournier.
+ * Log Logstash plugin: If writing the log entry fails, print it to
+ "STDERR" instead. Thanks to Marc Fournier.
+ * memcachec, Tail plugins: A resource leak in the matching
+ infrastructure has been fixed. Thanks to Krzysztof Matczak. #2192
+ * MQTT plugin: Invalid symbols in topic names are now replaced and a
+ resource leak has been fixed. Thanks to Denys Fedoryshchenko. #2123
+ * Network plugin: A potential endless-loop has been fixed. This can be
+ triggered remotely by sending a signed network packet to a server
+ which is not set up to check signatures. Thanks to Marcin Kozlowski
+ and Pavel Rochnyack. #2174, #2233, CVE-2017-7401
+ * Network plugin: A use-after-free has been fixed. Thanks to Pavel
+ Rochnyack. #2375
+ * Notify Email plugin: The plugin is no longer explicitly linked against
+ libssl and libcrypto, relies on libesmtp being linked correctly.
+ Thanks to Marc Fournier. Debian#852924
+ * NTPd plugin: Calculation of loop offset and error has been fixed.
+ Thanks to Neil Wilson. #2188
+ * OpenLDAP plugin: An incorrect use of the ldap library, leading to a
+ crash, has been fixed. Thanks to Marc Fournier. #2331
+ * Perl plugin: A potential double-free has been fixed. Thanks to Florian
+ Forster. #2278
+ * Perl plugin: Print an error when an incorrect configuration is
+ encountered. Thanks to Pavel Rochnyack. #927
+ * RRDtool plugin: Incorrect handling of the flushes timeout option has
+ been fixed. Handling of the "RandomTimeout" has been fixed. Thanks to
+ Pavel Rochnyack. #2363
+ * SMART plugin: Some warning messages have been removed and the code has
+ been cleaned up. Thanks to Florian Forster. #2062
+ * SMART plugin: A check for the "CAP_SYS_RAWIO" capability has been
+ added. Thanks to Marc Fournier.
+ * SNMP plugin: A double free has been fixed. Thanks to Pavel Rochnyack.
+ #2291
+ * Write Graphite plugin: Error handling in the case that calculating a
+ metric's rate fails has been improved. Previously, the raw counter
+ values were sent to Graphite. Thanks to Iain Buclaw. #2209
+ * Write Kafka plugin: A 32 bit random number is now used when formatting
+ a random key. Thanks to Florian Forster. #2074
+
+
2016-11-30, Version 5.6.2
* collectd: A compile error on AIX has been fixed: "MSG_DONTWAIT" is not
available on AIX. Thanks to Chao Yang.
pkginclude_HEADERS = \
src/libcollectdclient/collectd/client.h \
- src/libcollectdclient/collectd/network.h \
+ src/libcollectdclient/collectd/lcc_features.h \
src/libcollectdclient/collectd/network_buffer.h \
- src/libcollectdclient/collectd/lcc_features.h
+ src/libcollectdclient/collectd/network.h \
+ src/libcollectdclient/collectd/network_parse.h \
+ src/libcollectdclient/collectd/server.h \
+ src/libcollectdclient/collectd/types.h
lib_LTLIBRARIES = libcollectdclient.la
test_utils_mount \
test_utils_subst \
test_utils_time \
- test_utils_vl_lookup
+ test_utils_vl_lookup \
+ test_libcollectd_network_parse
TESTS = $(check_PROGRAMS)
src/daemon/configfile.h \
src/daemon/filter_chain.c \
src/daemon/filter_chain.h \
+ src/daemon/globals.c \
+ src/daemon/globals.h \
src/daemon/meta_data.c \
src/daemon/meta_data.h \
src/daemon/plugin.c \
collectd_nagios_SOURCES = src/collectd-nagios.c
collectd_nagios_CPPFLAGS = $(AM_CPPFLAGS) \
- -I$(srcdir)/src/libcollectdclient/collectd \
- -I$(top_builddir)/src/libcollectdclient/collectd
+ -I$(srcdir)/src/libcollectdclient \
+ -I$(top_builddir)/src/libcollectdclient
collectd_nagios_LDADD = libcollectdclient.la
if BUILD_WITH_LIBSOCKET
collectd_nagios_LDADD += -lsocket
collectdctl_SOURCES = src/collectdctl.c
collectdctl_CPPFLAGS = $(AM_CPPFLAGS) \
- -I$(srcdir)/src/libcollectdclient/collectd \
- -I$(top_builddir)/src/libcollectdclient/collectd
+ -I$(srcdir)/src/libcollectdclient \
+ -I$(top_builddir)/src/libcollectdclient
collectdctl_LDADD = libcollectdclient.la
if BUILD_WITH_LIBSOCKET
collectdctl_LDADD += -lsocket
collectd_tg_SOURCES = src/collectd-tg.c
collectd_tg_CPPFLAGS = $(AM_CPPFLAGS) \
- -I$(srcdir)/src/libcollectdclient/collectd \
- -I$(top_builddir)/src/libcollectdclient/collectd
+ -I$(srcdir)/src/libcollectdclient \
+ -I$(top_builddir)/src/libcollectdclient
collectd_tg_LDADD = \
$(PTHREAD_LIBS) \
libheap.la \
libcollectdclient_la_SOURCES = \
src/libcollectdclient/client.c \
src/libcollectdclient/network.c \
- src/libcollectdclient/network_buffer.c
+ src/libcollectdclient/network_buffer.c \
+ src/libcollectdclient/network_parse.c \
+ src/libcollectdclient/server.c
libcollectdclient_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
- -I$(srcdir)/src/libcollectdclient/collectd \
- -I$(top_builddir)/src/libcollectdclient/collectd \
+ -I$(srcdir)/src/libcollectdclient \
+ -I$(top_builddir)/src/libcollectdclient \
-I$(srcdir)/src/daemon
-libcollectdclient_la_LDFLAGS = -version-info 1:0:0
-libcollectdclient_la_LIBADD =
+libcollectdclient_la_LDFLAGS = -version-info 2:0:1
+libcollectdclient_la_LIBADD = -lm
if BUILD_WITH_LIBGCRYPT
libcollectdclient_la_CPPFLAGS += $(GCRYPT_CPPFLAGS)
libcollectdclient_la_LDFLAGS += $(GCRYPT_LDFLAGS)
libcollectdclient_la_LIBADD += $(GCRYPT_LIBS)
endif
+# network_parse_test.c includes network_parse.c, so no need to link with
+# libcollectdclient.so.
+test_libcollectd_network_parse_SOURCES = src/libcollectdclient/network_parse_test.c
+test_libcollectd_network_parse_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(srcdir)/src/libcollectdclient \
+ -I$(top_builddir)/src/libcollectdclient
+if BUILD_WITH_LIBGCRYPT
+test_libcollectd_network_parse_CPPFLAGS += $(GCRYPT_CPPFLAGS)
+test_libcollectd_network_parse_LDFLAGS = $(GCRYPT_LDFLAGS)
+test_libcollectd_network_parse_LDADD = $(GCRYPT_LIBS)
+endif
liboconfig_la_SOURCES = \
src/liboconfig/oconfig.c \
pkglib_LTLIBRARIES += dpdkevents.la
dpdkevents_la_SOURCES = src/dpdkevents.c src/utils_dpdk.c src/utils_dpdk.h
dpdkevents_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS)
+dpdkevents_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS)
dpdkevents_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS)
-dpdkevents_la_LIBADD = -ldpdk
+dpdkevents_la_LIBADD = $(LIBDPDK_LIBS)
endif
if BUILD_PLUGIN_DPDKSTAT
pkglib_LTLIBRARIES += dpdkstat.la
dpdkstat_la_SOURCES = src/dpdkstat.c src/utils_dpdk.c src/utils_dpdk.h
dpdkstat_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS)
+dpdkstat_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS)
dpdkstat_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS)
-dpdkstat_la_LIBADD = -ldpdk
+dpdkstat_la_LIBADD = $(LIBDPDK_LIBS)
endif
if BUILD_PLUGIN_DRBD
hugepages_la_LDFLAGS = $(PLUGIN_LDFLAGS)
endif
+if BUILD_PLUGIN_INTEL_PMU
+pkglib_LTLIBRARIES += intel_pmu.la
+intel_pmu_la_SOURCES = src/intel_pmu.c
+intel_pmu_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBJEVENTS_CPPFLAGS)
+intel_pmu_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBJEVENTS_LDFLAGS)
+intel_pmu_la_LIBADD = $(BUILD_WITH_LIBJEVENTS_LIBS)
+endif
+
if BUILD_PLUGIN_INTEL_RDT
pkglib_LTLIBRARIES += intel_rdt.la
intel_rdt_la_SOURCES = src/intel_rdt.c
endif
+if BUILD_PLUGIN_SYNPROXY
+pkglib_LTLIBRARIES += synproxy.la
+synproxy_la_SOURCES = src/synproxy.c
+synproxy_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+endif
+
if BUILD_PLUGIN_SYSLOG
pkglib_LTLIBRARIES += syslog.la
syslog_la_SOURCES = src/syslog.c
if BUILD_PLUGIN_TURBOSTAT
pkglib_LTLIBRARIES += turbostat.la
-turbostat_la_SOURCES = src/turbostat.c
+turbostat_la_SOURCES = \
+ src/turbostat.c \
+ src/msr-index.h
turbostat_la_LDFLAGS = $(PLUGIN_LDFLAGS)
endif
hugepages can be found here:
https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt.
+ - intel_pmu
+ The intel_pmu plugin reads performance counters provided by the Linux
+ kernel perf interface. The plugin uses jevents library to resolve named
+ events to perf events and access perf interface.
+
- intel_rdt
The intel_rdt plugin collects information provided by monitoring features
of Intel Resource Director Technology (Intel(R) RDT) like Cache Monitoring
- netapp
Plugin to query performance values from a NetApp storage system using the
- “Manage ONTAP” SDK provided by NetApp.
+ “Manage ONTAP” SDK provided by NetApp.
- netlink
Very detailed Linux network interface and routing statistics. You can get
plugin of choice for that.
- nfs
- NFS Procedures: Which NFS command were called how often. Only NFSv2 and
- NFSv3 right now.
+ NFS Procedures: Which NFS command were called how often.
- nginx
Collects statistics from `nginx' (speak: engine X), a HTTP and mail
For querying iptables counters.
<http://netfilter.org/>
+ * libjevents (optional)
+ The jevents library is used by the `intel_pmu' plugin to access the Linux
+ kernel perf interface.
+ Note: the library should be build with -fPIC flag to be linked with
+ intel_pmu shared object correctly.
+ <https://github.com/andikleen/pmu-tools>
+
* libjvm (optional)
Library that encapsulates the `Java Virtual Machine' (JVM). This library is
used by the `java' plugin to execute Java bytecode.
pthread_np.h \
pwd.h \
regex.h \
+ sys/endian.h \
sys/fs_types.h \
sys/fstyp.h \
sys/ioctl.h \
]]
)
# For the turbostat plugin
- AC_CHECK_HEADERS([asm/msr-index.h],
- [have_asm_msrindex_h="yes"],
- [have_asm_msrindex_h="no"]
- )
-
- if test "x$have_asm_msrindex_h" = "xyes"; then
- AC_CACHE_CHECK([whether asm/msr-index.h has MSR_PKG_C10_RESIDENCY],
- [c_cv_have_usable_asm_msrindex_h],
- [
- AC_COMPILE_IFELSE(
- [
- AC_LANG_PROGRAM(
- [[#include<asm/msr-index.h>]],
- [[
- int y = MSR_PKG_C10_RESIDENCY;
- return(y);
- ]]
- )
- ],
- [c_cv_have_usable_asm_msrindex_h="yes"],
- [c_cv_have_usable_asm_msrindex_h="no"],
- )
- ]
- )
- fi
-
AC_CHECK_HEADERS([cpuid.h],
[have_cpuid_h="yes"],
[have_cpuid_h="no (cpuid.h not found)"]
# --with-libdpdk {{{
AC_ARG_VAR([LIBDPDK_CPPFLAGS], [Preprocessor flags for libdpdk])
+AC_ARG_VAR([LIBDPDK_CFLAGS], [Compiler flags for libdpdk])
AC_ARG_VAR([LIBDPDK_LDFLAGS], [Linker flags for libdpdk])
+AC_ARG_VAR([LIBDPDK_LIBS], [Libraries to link for libdpdk])
AC_ARG_WITH([libdpdk],
[AS_HELP_STRING([--without-libdpdk], [Disable libdpdk.])],
)
if test "x$with_libdpdk" != "xno"; then
+ PKG_CHECK_MODULES([DPDK], [libdpdk], [],
+ [AC_MSG_NOTICE([no DPDK pkg-config, using defaults])])
if test "x$LIBDPDK_CPPFLAGS" = "x"; then
LIBDPDK_CPPFLAGS="-I/usr/include/dpdk"
fi
+ if test "x$LIBDPDK_CFLAGS" = "x"; then
+ LIBDPDK_CFLAGS="$DPDK_CFLAGS"
+ LIBDPDK_CPPFLAGS="$LIBDPDK_CPPFLAGS $DPDK_CFLAGS"
+ fi
+ if test "x$LIBDPDK_LIBS" = "x"; then
+ if test "x$DPDK_LIBS" != "x"; then
+ LIBDPDK_LIBS="$DPDK_LIBS"
+ else
+ LIBDPDK_LIBS="-ldpdk"
+ fi
+ fi
SAVE_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$LIBDPDK_CPPFLAGS $CPPFLAGS"
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$LIBDPDK_CFLAGS $CFLAGS"
AC_CHECK_HEADERS([rte_config.h],
[
with_libdpdk="yes"
[with_libdpdk="no (rte_config.h not found)"]
)
CPPFLAGS="$SAVE_CPPFLAGS"
+ CFLAGS="$SAVE_CFLAGS"
fi
if test "x$with_libdpdk" = "xyes"; then
AC_SUBST([BUILD_WITH_LIBPQOS_LIBS])
# }}}
+# --with-libjevents {{{
+with_libjevents_cppflags=""
+with_libjevents_ldflags=""
+AC_ARG_WITH([libjevents],
+ [AS_HELP_STRING([--with-libjevents@<:@=PREFIX@:>@], [Path to libjevents.])],
+ [
+ if test "x$withval" != "xno" && test "x$withval" != "xyes"; then
+ with_libjevents_cppflags="-I$withval/include"
+ with_libjevents_ldflags="-L$withval/lib"
+ with_libjevents="yes"
+ else
+ with_libjevents="$withval"
+ fi
+ ],
+ [with_libjevents="yes"]
+)
+
+if test "x$with_libjevents" = "xyes"; then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libjevents_cppflags"
+
+ AC_CHECK_HEADERS([jevents.h], [with_libjevents="yes"], [with_libjevents="no (jevents.h not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libjevents" = "xyes"; then
+ SAVE_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $with_libjevents_ldflags"
+
+ AC_CHECK_LIB([jevents], [json_events], [with_libjevents="yes"], [with_libjevents="no (Can't find libjevents)"])
+
+ LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libjevents" = "xyes"; then
+ BUILD_WITH_LIBJEVENTS_CPPFLAGS="$with_libjevents_cppflags"
+ BUILD_WITH_LIBJEVENTS_LDFLAGS="$with_libjevents_ldflags"
+ BUILD_WITH_LIBJEVENTS_LIBS="-ljevents"
+fi
+AC_SUBST([BUILD_WITH_LIBJEVENTS_CPPFLAGS])
+AC_SUBST([BUILD_WITH_LIBJEVENTS_LDFLAGS])
+AC_SUBST([BUILD_WITH_LIBJEVENTS_LIBS])
+# }}}
+
# --with-libprotobuf {{{
with_libprotobuf_cppflags=""
with_libprotobuf_ldflags=""
plugin_gps="no"
plugin_grpc="no"
plugin_hugepages="no"
+plugin_intel_pmu="no"
plugin_intel_rdt="no"
plugin_interface="no"
plugin_ipc="no"
plugin_serial="no"
plugin_smart="no"
plugin_swap="no"
+plugin_synproxy="no"
plugin_tape="no"
plugin_tcpconns="no"
plugin_ted="no"
plugin_protocols="yes"
plugin_serial="yes"
plugin_swap="yes"
+ plugin_synproxy="yes"
plugin_tcpconns="yes"
plugin_thermal="yes"
plugin_uptime="yes"
plugin_ipvs="yes"
fi
- if test "x$c_cv_have_usable_asm_msrindex_h" = "xyes" && test "x$have_cpuid_h" = "xyes"; then
+ if test "x$have_cpuid_h" = "xyes"; then
plugin_turbostat="yes"
fi
plugin_xencpu="yes"
fi
-if test "x$with_libdpdk" = "xyes"
-then
+if test "x$with_libdpdk" = "xyes"; then
plugin_dpdkevents="$dpdk_keepalive"
plugin_dpdkstat="yes"
fi
AC_PLUGIN([grpc], [$plugin_grpc], [gRPC plugin])
AC_PLUGIN([hddtemp], [yes], [Query hddtempd])
AC_PLUGIN([hugepages], [$plugin_hugepages], [Hugepages statistics])
+AC_PLUGIN([intel_pmu], [$with_libjevents], [Intel performance monitor plugin])
AC_PLUGIN([intel_rdt], [$with_libpqos], [Intel RDT monitor plugin])
AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics])
AC_PLUGIN([ipc], [$plugin_ipc], [IPC statistics])
AC_PLUGIN([snmp_agent], [$with_libnetsnmpagent], [SNMP agent plugin])
AC_PLUGIN([statsd], [yes], [StatsD plugin])
AC_PLUGIN([swap], [$plugin_swap], [Swap usage statistics])
+AC_PLUGIN([synproxy], [$plugin_synproxy], [Synproxy stats plugin])
AC_PLUGIN([syslog], [$have_syslog], [Syslog logging plugin])
AC_PLUGIN([table], [yes], [Parsing of tabular data])
AC_PLUGIN([tail], [yes], [Parsing of logfiles])
AC_MSG_RESULT([ libi2c-dev . . . . . $with_libi2c])
AC_MSG_RESULT([ libiokit . . . . . . $with_libiokit])
AC_MSG_RESULT([ libiptc . . . . . . . $with_libiptc])
+AC_MSG_RESULT([ libjevents . . . . . $with_libjevents])
AC_MSG_RESULT([ libjvm . . . . . . . $with_java])
AC_MSG_RESULT([ libkstat . . . . . . $with_kstat])
AC_MSG_RESULT([ libkvm . . . . . . . $with_libkvm])
AC_MSG_RESULT([ grpc . . . . . . . . $enable_grpc])
AC_MSG_RESULT([ hddtemp . . . . . . . $enable_hddtemp])
AC_MSG_RESULT([ hugepages . . . . . . $enable_hugepages])
+AC_MSG_RESULT([ intel_pmu . . . . . . $enable_intel_pmu])
AC_MSG_RESULT([ intel_rdt . . . . . . $enable_intel_rdt])
AC_MSG_RESULT([ interface . . . . . . $enable_interface])
AC_MSG_RESULT([ ipc . . . . . . . . . $enable_ipc])
AC_MSG_RESULT([ snmp_agent . . . . . $enable_snmp_agent])
AC_MSG_RESULT([ statsd . . . . . . . $enable_statsd])
AC_MSG_RESULT([ swap . . . . . . . . $enable_swap])
+AC_MSG_RESULT([ synproxy . . . . . . $enable_synproxy])
AC_MSG_RESULT([ syslog . . . . . . . $enable_syslog])
AC_MSG_RESULT([ table . . . . . . . . $enable_table])
AC_MSG_RESULT([ tail_csv . . . . . . $enable_tail_csv])
--- /dev/null
+# Example configuration for PHP-FPM
+<Plugin "curl_json">
+ <URL "http://nginx-status/php-fpm-status?json">
+ Plugin "phpfpm"
+ Instance "main"
+ <Key "accepted conn">
+ Type "total_requests"
+ Instance "accepted"
+ </Key>
+ <Key "slow requests">
+ Type "total_requests"
+ Instance "slow"
+ </Key>
+ <Key "listen queue">
+ Type "queue_length"
+ Instance "listen"
+ </Key>
+ <Key "active processes">
+ Type "vs_processes"
+ Instance "active"
+ </Key>
+ <Key "total processes">
+ Type "vs_processes"
+ Instance "total"
+ </Key>
+ </URL>
+</Plugin>
#
# - fetch the desired collectd release file from https://collectd.org/files/
# and save it in your ~/rpmbuild/SOURCES/ directory (or build your own out of
-# the git repository: ./build.sh && ./configure && make-dist-bz2)
+# the git repository: ./build.sh && ./configure && make dist)
#
# - copy this file in your ~/rpmbuild/SPECS/ directory. Make sure the
# "Version:" tag matches the version from the tarball.
%define with_grpc 0%{!?_without_grpc:0}
# plugin lpar disabled, requires AIX
%define with_lpar 0%{!?_without_lpar:0}
+# plugin intel_pmu disabled, requires libjevents
+%define with_intel_pmu 0%{!?_without_intel_pmu:0}
# plugin intel_rdt disabled, requires intel-cmt-cat
%define with_intel_rdt 0%{!?_without_intel_rdt:0}
# plugin mic disabled, requires Mic
Summary: Statistics collection and monitoring daemon
Name: collectd
Version: 5.7.1
-Release: 6%{?dist}
+Release: 7%{?dist}
URL: https://collectd.org
Source: https://collectd.org/files/%{name}-%{version}.tar.bz2
License: GPLv2
provided via SMART and queried by the external hddtemp daemon.
%endif
+%if %{with_intel_pmu}
+%package intel_pmu
+Summary: Intel PMU plugin for collectd
+Group: System Environment/Daemons
+Requires: %{name}%{?_isa} = %{version}-%{release}
+%description intel_pmu
+The intel_pmu plugin reads performance counters provided by the Linux
+kernel perf interface.
+%endif
+
%if %{with_intel_rdt}
%package intel_rdt
Summary: Intel RDT plugin for collectd
Summary: Java plugin for collectd
Group: System Environment/Daemons
Requires: %{name}%{?_isa} = %{version}-%{release}
-BuildRequires: java-devel, jpackage-utils
-Requires: java, jpackage-utils
+BuildRequires: java-devel >= 1.6, jpackage-utils >= 1.6
+Requires: java >= 1.6, jpackage-utils >= 1.6
%description java
This plugin for collectd allows plugins to be written in Java and executed
in an embedded JVM.
%define _with_hugepages --disable-hugepages
%endif
+%if %{with_intel_pmu}
+%define _with_intel_pmu --enable-intel_pmu
+%else
+%define _with_intel_pmu --disable-intel_pmu
+%endif
+
%if %{with_intel_rdt}
%define _with_intel_rdt --enable-intel_rdt
%else
%{?_with_grpc} \
%{?_with_hddtemp} \
%{?_with_hugepages} \
+ %{?_with_intel_pmu} \
%{?_with_intel_rdt} \
%{?_with_interface} \
%{?_with_ipc} \
%{_libdir}/%{name}/hddtemp.so
%endif
+%if %{with_intel_pmu}
+%files intel_pmu
+%{_libdir}/%{name}/intel_pmu.so
+%endif
+
%if %{with_intel_rdt}
%files intel_rdt
%{_libdir}/%{name}/intel_rdt.so
%doc contrib/
%changelog
+* Fri Aug 18 2017 Ruben Kerkhof <ruben@rubenkerkhof.com> - 5.7.1-7
+- Add new intel_pmu plugin
+
* Sun Mar 05 2017 Ruben Kerkhof <ruben@rubenkerkhof.com> - 5.7.1-6
- Move recently added plugins to subpackages
# dns CAP_NET_RAW
# exec CAP_SETUID CAP_SETGID
# intel_rdt CAP_SYS_RAWIO
+# intel_pmu CAP_SYS_ADMIN
# iptables CAP_NET_ADMIN
# ping CAP_NET_RAW
# smart CAP_SYS_RAWIO
string type_instance = 5;
}
+message MetadataValue {
+ oneof value {
+ string string_value = 1;
+ int64 int64_value = 2;
+ uint64 uint64_value = 3;
+ double double_value = 4;
+ bool bool_value = 5;
+ };
+}
+
message Value {
oneof value {
uint64 counter = 1;
Identifier identifier = 4;
repeated string ds_names = 5;
-}
+ map<string, MetadataValue> meta_data = 6;
+}
\ No newline at end of file
status = -1;
}
- if (status == 0) {
- char callback_name[3 * DATA_MAX_NAME_LEN];
-
- snprintf(callback_name, sizeof(callback_name), "apache/%s/%s",
- (st->host != NULL) ? st->host : hostname_g,
- (st->name != NULL) ? st->name : "default");
-
- status = plugin_register_complex_read(
- /* group = */ NULL,
- /* name = */ callback_name,
- /* callback = */ apache_read_host,
- /* interval = */ 0,
- &(user_data_t){
- .data = st, .free_func = apache_free,
- });
- }
-
if (status != 0) {
apache_free(st);
return -1;
}
- return 0;
+ char callback_name[3 * DATA_MAX_NAME_LEN];
+
+ snprintf(callback_name, sizeof(callback_name), "apache/%s/%s",
+ (st->host != NULL) ? st->host : hostname_g,
+ (st->name != NULL) ? st->name : "default");
+
+ return plugin_register_complex_read(
+ /* group = */ NULL,
+ /* name = */ callback_name,
+ /* callback = */ apache_read_host,
+ /* interval = */ 0,
+ &(user_data_t){
+ .data = st, .free_func = apache_free,
+ });
} /* int config_add */
static int config(oconfig_item_t *ci) {
}
/* compact_ds_name removed the special characters ":", "_", "-" and "+" from the
- * intput string. Characters following these special characters are capitalized.
+ * input string. Characters following these special characters are capitalized.
* Trailing "+" and "-" characters are replaces with the strings "Plus" and
* "Minus". */
static int compact_ds_name(char *buffer, size_t buffer_size, char const *src) {
#endif
#endif /* NAN_ZERO_ZERO */
-#include "libcollectdclient/collectd/client.h"
+#include "collectd/client.h"
#define RET_OKAY 0
#define RET_WARNING 1
=over 4
-=item B<dispatch>([type][, values][, plugin_instance][, type_instance][, plugin][, host][, time][, interval]) -> None. Dispatch a value list.
+=item B<dispatch>([type][, message][, plugin_instance][, type_instance][, plugin][, host][, time][, severity][, meta]) -> None. Dispatch a notification.
Dispatch this instance to the collectd process. The object has members for each
of the possible arguments for this method. For a detailed explanation of these
The severity of this notification. Assign or compare to I<NOTIF_FAILURE>,
I<NOTIF_WARNING> or I<NOTIF_OKAY>.
+=item meta
+
+These are the meta data for the Notification object.
+It has to be a dictionary of numbers, strings or bools. All keys must be
+strings. I<int> and I<long> objects will be dispatched as signed integers unless
+they are between 2**63 and 2**64-1, which will result in a unsigned integer.
+One of these storage classes can be forced by using the classes
+B<collectd.Signed> and B<collectd.Unsigned>. A meta object received by a
+notification callback will always contain B<Signed> or B<Unsigned> objects.
+
=back
=head1 FUNCTIONS
Community "community_string"
Collect "std_traffic"
Interval 120
+ Timeout 10
+ Retries 1
</Host>
<Host "some.server.mydomain.org">
Address "192.168.0.42"
Community "more_communities"
Collect "powerplus_voltge_input"
Interval 300
+ Timeout 5
+ Retries 5
</Host>
</Plugin>
=head1 CONFIGURATION
Since the aim of the C<snmp plugin> is to provide a generic interface to SNMP,
-it's configuration is not trivial and may take some time.
+its configuration is not trivial and may take some time.
Since the C<Net-SNMP> library is used you can use all the environment variables
that are interpreted by that package. See L<snmpcmd(1)> for more details.
B<Step> of generated RRD files depends on this setting it's wise to select a
reasonable value once and never change it.
+=item B<Timeout> I<Seconds>
+
+How long to wait for a response. The C<Net-SNMP> library default is 1 second.
+
+=item B<Retries> I<Integer>
+
+The number of times that a query should be retried after the Timeout expires.
+The C<Net-SNMP> library default is 5.
+
=back
=head1 SEE ALSO
#include "utils_heap.h"
-#include "libcollectdclient/collectd/client.h"
-#include "libcollectdclient/collectd/network.h"
+#include "collectd/client.h"
+#include "collectd/network.h"
#define DEF_NUM_HOSTS 1000
#define DEF_NUM_PLUGINS 20
#@BUILD_PLUGIN_GRPC_TRUE@LoadPlugin grpc
#@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
#@BUILD_PLUGIN_HUGEPAGES_TRUE@LoadPlugin hugepages
+#@BUILD_PLUGIN_INTEL_PMU_TRUE@LoadPlugin intel_pmu
#@BUILD_PLUGIN_INTEL_RDT_TRUE@LoadPlugin intel_rdt
@BUILD_PLUGIN_INTERFACE_TRUE@@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
#@BUILD_PLUGIN_IPC_TRUE@LoadPlugin ipc
# ReportByCpu true
# ReportByState true
# ValuesPercentage false
+# ReportNumCpu false
+# ReportGuestState false
+# SubtractGuestState true
#</Plugin>
#
#<Plugin csv>
#<Plugin curl_xml>
# <URL "http://localhost/stats.xml">
# Host "my_host"
+# #Plugin "stats"
# Instance "some_instance"
# User "collectd"
# Password "thaiNg0I"
# Type "magic_level"
# #InstancePrefix "prefix-"
# InstanceFrom "td[1]"
+# #PluginInstanceFrom "td[1]"
# ValuesFrom "td[2]/span[@class=\"level\"]"
# </XPath>
# </URL>
# </Result>
# </Query>
# <Database "customers_db">
+# #Plugin "mycompany"
# Driver "mysql"
# DriverOption "host" "localhost"
# DriverOption "username" "collectd"
#<Plugin filecount>
# <Directory "/path/to/dir">
+# #Plugin "foo"
# Instance "foodir"
# Name "*.conf"
# MTime "-5m"
# Size "+10k"
# Recursive true
# IncludeHidden false
+# RegularOnly true
+# #FilesSizeType "bytes"
+# #FilesCountType "files"
+# #TypeInstance "instance"
# </Directory>
#</Plugin>
# ValuesPercentage false
#</Plugin>
+#<Plugin intel_pmu>
+# ReportHardwareCacheEvents true
+# ReportKernelPMUEvents true
+# ReportSoftwareEvents true
+# EventList "/var/cache/pmu/GenuineIntel-6-2D-core.json"
+# HardwareEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS" "L2_RQSTS.ALL_CODE_RD"
+#</Plugin>
+
#<Plugin "intel_rdt">
# Cores "0-2"
#</Plugin>
#</Plugin>
#<Plugin ipmi>
-# Sensor "some_sensor"
-# Sensor "another_one"
-# IgnoreSelected false
-# NotifySensorAdd false
-# NotifySensorRemove true
-# NotifySensorNotPresent false
+# <Instance "local">
+# Sensor "some_sensor"
+# Sensor "another_one"
+# IgnoreSelected false
+# NotifySensorAdd false
+# NotifySensorRemove true
+# NotifySensorNotPresent false
+# NotifyIPMIConnectionState false
+# SELEnabled false
+# SELClearEvent false
+# </Instance>
+# <Instance "remote">
+# Host "server.example.com"
+# Address "1.2.3.4"
+# Username "user"
+# Password "secret"
+# #AuthType "md5"
+# Sensor "some_sensor"
+# Sensor "another_one"
+# IgnoreSelected false
+# NotifySensorAdd false
+# NotifySensorRemove true
+# NotifySensorNotPresent false
+# NotifyIPMIConnectionState false
+# SELEnabled false
+# SELClearEvent false
+# </Instance>
#</Plugin>
#<Plugin iptables>
#</Plugin>
#<Plugin mcelog>
-# McelogClientSocket "/var/run/mcelog-client"
-# McelogLogfile "/var/log/mcelog"
+# <Memory>
+# McelogClientSocket "/var/run/mcelog-client"
+# PersistentNotification false
+# </Memory>
+# McelogLogfile "/var/log/mcelog"
#</Plugin>
#<Plugin md>
# QoS 2
# Topic "collectd/#"
# CleanSession true
+# CACert "/etc/ssl/ca.crt"
+# CertificateFile "/etc/ssl/client.crt"
+# CertificateKeyFile "/etc/ssl/client.pem"
+# TLSProtocol "tlsv1.2"
+# CipherSuite "ciphers"
# </Subscribe>
#</Plugin>
# CacheFlush 1800
@LOAD_PLUGIN_NETWORK@</Plugin>
+#<Plugin nfs>
+# ReportV2 false
+# #ReportV3 false
+# #ReportV4 false
+#</Plugin>
+
#<Plugin nginx>
# URL "http://localhost/status?auto"
# User "www-user"
# </Result>
# </Query>
# <Database "product_information">
+# #Plugin "warehouse"
# ConnectID "db01"
# Username "oracle"
# Password "secret"
# StoreRates true
# </Writer>
# <Database foo>
+# #Plugin "kingdom"
# Host "hostname"
# Port "5432"
# User "username"
#<Plugin processes>
# CollectFileDescriptor true
# CollectContextSwitch true
+# CollectMemoryMaps true
# Process "name"
# ProcessMatch "name" "regex"
# <Process "collectd">
# Community "community_string"
# Collect "std_traffic"
# Interval 120
+# Timeout 10
+# Retries 1
# </Host>
# <Host "some.server.mydomain.org">
# Address "192.168.0.42"
# Community "more_communities"
# Collect "powerplus_voltge_input"
# Interval 300
+# Timeout 5
+# Retries 5
# </Host>
#</Plugin>
# ReportBytes true
# ValuesAbsolute true
# ValuesPercentage false
+# ReportIO true
#</Plugin>
#<Plugin table>
# <Table "/proc/slabinfo">
+# #Plugin "table"
# Instance "slabinfo"
# Separator " "
# <Result>
# Bucket 0.5 1.0 # -> bucket-latency-foo-0.5_1
# Bucket 1.0 2.0 # -> bucket-latency-foo-1_2
# Bucket 2.0 0 # -> bucket-latency-foo-2_inf
+# #BucketType "bucket"
# </DSType>
# Type "latency"
# Instance "foo"
# SystemManagementInterrupt true
# DigitalTemperatureSensor true
# PackageThermalManagement true
-# RunningAveragePowerLimit "7"
+# RunningAveragePowerLimit "7"
#</Plugin>
#<Plugin unixsock>
# CollectPurge false # Varnish 2 only
# CollectSession false
# CollectSHM true
-# CollectSMA false # Varnish 2 only
+# CollectSMA false # Varnish 2 & 4 only
# CollectSMS false
# CollectSM false # Varnish 2 only
# CollectStruct false
# CollectVCL false
# CollectVSM false # Varnish 4 only
# CollectWorkers false
+# CollectLock false # Varnish 4 only
+# CollectMempool false # Varnish 4 only
+# CollectManagement false # Varnish 4 only
+# CollectSMF false # Varnish 4 only
+# CollectVBE false # Varnish 4 only
+# CollectMSE false # Varnish-Plus 4 only
# </Instance>
#</Plugin>
# Header "X-Custom-Header: custom_value"
# SSLVersion "TLSv1"
# Format "Command"
+# Prefix "collectd" # metric prefix, only available for KAIROSDB format
# Attribute "key" "value" # only available for KAIROSDB format
# TTL 0 # data ttl, only available for KAIROSDB format
# Metrics true
When set to B<true>, reports the number of available CPUs.
Defaults to B<false>.
+=item B<ReportGuestState> B<false>|B<true>
+
+When set to B<true>, reports the "guest" and "guest_nice" CPU states.
+Defaults to B<false>.
+
+=item B<SubtractGuestState> B<false>|B<true>
+
+This option is only considered when B<ReportGuestState> is set to B<true>.
+"guest" and "guest_nice" are included in respectively "user" and "nice".
+If set to B<true>, "guest" will be subtracted from "user" and "guest_nice"
+will be subtracted from "nice".
+Defaults to B<true>.
+
=back
=head2 Plugin C<cpufreq>
<Plugin curl>
<Page "stock_quotes">
+ Plugin "quotes"
URL "http://finance.google.com/finance?q=NYSE%3AAMD"
User "foo"
Password "bar"
=over 4
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to C<curl>.
+
=item B<URL> I<URL>
URL of the web site to retrieve. Since a regular expression will be used to
Use I<Name> as the host name when submitting values. Defaults to the global
host name setting.
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to C<curl_json>.
+
=item B<Instance> I<Instance>
Sets the plugin instance to I<Instance>.
<Plugin "curl_xml">
<URL "http://localhost/stats.xml">
Host "my_host"
+ #Plugin "curl_xml"
Instance "some_instance"
User "collectd"
Password "thaiNg0I"
Type "magic_level"
#InstancePrefix "prefix-"
InstanceFrom "td[1]"
+ #PluginInstanceFrom "td[1]"
ValuesFrom "td[2]/span[@class=\"level\"]"
</XPath>
</URL>
Use I<Name> as the host name when submitting values. Defaults to the global
host name setting.
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to 'curl_xml'.
+
=item B<Instance> I<Instance>
-Use I<Instance> as the plugin instance when submitting values. Defaults to an
-empty string (no plugin instance).
+Use I<Instance> as the plugin instance when submitting values.
+May be overridden by B<PluginInstanceFrom> option inside B<XPath> blocks.
+Defaults to an empty string (no plugin instance).
=item B<Namespace> I<Prefix> I<URL>
XPath expression must return exactly one element. The element's value is then
used as I<type instance>, possibly prefixed with I<InstancePrefix> (see above).
-This value is required. As a special exception, if the "base XPath expression"
-(the argument to the B<XPath> block) returns exactly one argument, then this
-option may be omitted.
+=item B<PluginInstanceFrom> I<PluginInstanceFrom>
+
+Specifies a XPath expression to use for determining the I<plugin instance>. The
+XPath expression must return exactly one element. The element's value is then
+used as I<plugin instance>.
+
+=back
+
+If the "base XPath expression" (the argument to the B<XPath> block) returns
+exactly one argument, then I<InstanceFrom> and I<PluginInstanceFrom> may be omitted.
+Otherwise, at least one of I<InstanceFrom> or I<PluginInstanceFrom> is required.
+
+=over 4
=item B<ValuesFrom> I<ValuesFrom> [I<ValuesFrom> ...]
I<type> specified with B<Type> (see above). Each XPath expression must return
exactly one element. The element's value is then parsed as a number and used as
value for the appropriate value in the value list dispatched to the daemon.
+This option is required.
=back
</Result>
</Query>
<Database "product_information">
+ #Plugin "warehouse"
Driver "mysql"
Interval 120
DriverOption "host" "localhost"
=over 4
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting query results from
+this B<Database>. Defaults to C<dbi>.
+
=item B<Interval> I<Interval>
Sets the interval (in seconds) in which the values will be collected from this
=over 4
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to B<filecount>.
+
=item B<Instance> I<Instance>
-Sets the plugin instance to I<Instance>. That instance name must be unique, but
-it's your responsibility, the plugin doesn't check for that. If not given, the
-instance is set to the directory name with all slashes replaced by underscores
-and all leading underscores removed.
+Sets the plugin instance to I<Instance>. If not given, the instance is set to
+the directory name with all slashes replaced by underscores and all leading
+underscores removed. Empty value is allowed.
=item B<Name> I<Pattern>
"Hidden" files and directories are those, whose name begins with a dot.
Defaults to I<false>, i.e. by default hidden files and directories are ignored.
+=item B<RegularOnly> I<true>|I<false>
+
+Controls whether or not to include only regular files in the count.
+Defaults to I<true>, i.e. by default non regular files are ignored.
+
+=item B<FilesSizeType> I<Type>
+
+Sets the type used to dispatch files combined size. Empty value ("") disables
+reporting. Defaults to B<bytes>.
+
+=item B<FilesCountType> I<Type>
+
+Sets the type used to dispatch number of files. Empty value ("") disables
+reporting. Defaults to B<files>.
+
+=item B<TypeInstance> I<Instance>
+
+Sets the I<type instance> used to dispatch values. Defaults to an empty string
+(no plugin instance).
+
=back
=head2 Plugin C<GenericJMX>
=back
+=head2 Plugin C<intel_pmu>
+
+The I<intel_pmu> plugin collects performance counters data on Intel CPUs using
+Linux perf interface. All events are reported on a per core basis.
+
+B<Synopsis:>
+
+ <Plugin intel_pmu>
+ ReportHardwareCacheEvents true
+ ReportKernelPMUEvents true
+ ReportSoftwareEvents true
+ EventList "/var/cache/pmu/GenuineIntel-6-2D-core.json"
+ HardwareEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS" "L2_RQSTS.ALL_CODE_RD"
+ </Plugin>
+
+B<Options:>
+
+=over 4
+
+=item B<ReportHardwareCacheEvents> B<false>|B<true>
+
+Enable or disable measuring of hardware CPU cache events:
+ - L1-dcache-loads
+ - L1-dcache-load-misses
+ - L1-dcache-stores
+ - L1-dcache-store-misses
+ - L1-dcache-prefetches
+ - L1-dcache-prefetch-misses
+ - L1-icache-loads
+ - L1-icache-load-misses
+ - L1-icache-prefetches
+ - L1-icache-prefetch-misses
+ - LLC-loads
+ - LLC-load-misses
+ - LLC-stores
+ - LLC-store-misses
+ - LLC-prefetches
+ - LLC-prefetch-misses
+ - dTLB-loads
+ - dTLB-load-misses
+ - dTLB-stores
+ - dTLB-store-misses
+ - dTLB-prefetches
+ - dTLB-prefetch-misses
+ - iTLB-loads
+ - iTLB-load-misses
+ - branch-loads
+ - branch-load-misses
+
+=item B<ReportKernelPMUEvents> B<false>|B<true>
+
+Enable or disable measuring of the following events:
+ - cpu-cycles
+ - instructions
+ - cache-references
+ - cache-misses
+ - branches
+ - branch-misses
+ - bus-cycles
+
+=item B<ReportSoftwareEvents> B<false>|B<true>
+
+Enable or disable measuring of software events provided by kernel:
+ - cpu-clock
+ - task-clock
+ - context-switches
+ - cpu-migrations
+ - page-faults
+ - minor-faults
+ - major-faults
+ - alignment-faults
+ - emulation-faults
+
+=item B<EventList> I<filename>
+
+JSON performance counter event list file name. To be able to monitor all Intel
+CPU specific events JSON event list file should be downloaded. Use the pmu-tools
+event_download.py script to download event list for current CPU.
+
+=item B<HardwareEvents> I<events>
+
+This field is a list of event names or groups of comma separated event names.
+This option requires B<EventList> option to be configured.
+
+=back
+
=head2 Plugin C<intel_rdt>
The I<intel_rdt> plugin collects information provided by monitoring features of
=head2 Plugin C<ipmi>
+The B<ipmi plugin> allows to monitor server platform status using the Intelligent
+Platform Management Interface (IPMI). Local and remote interfaces are supported.
+
+The plugin configuration consists of one or more B<Instance> blocks which
+specify one I<ipmi> connection each. Each block requires one unique string
+argument as the instance name. If instances are not configured, an instance with
+the default option values will be created.
+
+For backwards compatibility, any option other than B<Instance> block will trigger
+legacy config handling and it will be treated as an option within B<Instance>
+block. This support will go away in the next major version of Collectd.
+
+Within the B<Instance> blocks, the following options are allowed:
+
=over 4
+=item B<Address> I<Address>
+
+Hostname or IP to connect to. If not specified, plugin will try to connect to
+local management controller (BMC).
+
+=item B<Username> I<Username>
+
+=item B<Password> I<Password>
+
+The username and the password to use for the connection to remote BMC.
+
+=item B<AuthType> I<MD5>|I<rmcp+>
+
+Forces the authentication type to use for the connection to remote BMC.
+By default most secure type is seleted.
+
+=item B<Host> I<Hostname>
+
+Sets the B<host> field of dispatched values. Defaults to the global hostname
+setting.
+
=item B<Sensor> I<Sensor>
Selects sensors to collect or to ignore, depending on B<IgnoreSelected>.
If you have for example dual power supply and one of them is (un)plugged then
a notification is sent.
+=item B<NotifyIPMIConnectionState> I<true>|I<false>
+
+If a IPMI connection state changes after initialization time of a minute
+a notification is sent. Defaults to B<false>.
+
+=item B<SELEnabled> I<true>|I<false>
+
+If system event log (SEL) is enabled, plugin will listen for sensor threshold
+and discrete events. When event is received the notification is sent.
+Defaults to B<false>.
+
+=item B<SELClearEvent> I<true>|I<false>
+
+If SEL clear event is enabled, plugin will delete event from SEL list after
+it is received and successfully handled. In this case other tools that are
+subscribed for SEL events will receive an empty event.
+Defaults to B<false>.
+
=back
=head2 Plugin C<iptables>
mcelog server is running. When the server is running, the plugin will tail the
specified logfile to retrieve machine check exception information and send a
notification with the details from the logfile. The plugin will use the mcelog
-client protocol to retrieve memory related machine check exceptions.
+client protocol to retrieve memory related machine check exceptions. Note that
+for memory exceptions, notifications are only sent when there is a change in
+the number of corrected/uncorrected memory errors.
-=over 4
+=head3 The Memory block
+
+Note: these options cannot be used in conjunction with the logfile options, they are mutually
+exclusive.
+
+=over 3
=item B<McelogClientSocket> I<Path>
Connect to the mcelog client socket using the UNIX domain socket at I<Path>.
Defaults to B<"/var/run/mcelog-client">.
+=item B<PersistentNotification> B<true>|B<false>
+Override default configuration to only send notifications when sent when there
+is a change in the number of corrected/uncorrected memory errors. When set to
+true notifications will be sent for every read cycle. Default is false. Does
+not affect the stats being dispatched.
+
+=back
+
+=over 4
+
=item B<McelogLogfile> I<Path>
-The mcelog file to parse. Defaults to B<"/var/log/mcelog">.
+The mcelog file to parse. Defaults to B<"/var/log/mcelog">. Note: this option
+cannot be used in conjunction with the memory block options, they are mutually
+exclusive.
=back
<Page "plugin_instance">
Server "localhost"
Key "page_key"
+ Plugin "plugin_name"
<Match>
Regex "(\\d+) bytes sent"
DSType CounterAdd
When connected to the memcached server, asks for the page I<Key>.
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to C<memcachec>.
+
=item E<lt>B<Match>E<gt>
Match blocks define which strings to look for and how matches substrings are
Path to the PEM-encoded CA certificate file. Setting this option enables TLS
communication with the MQTT broker, and as such, B<Port> should be the TLS-enabled
port of the MQTT broker.
-A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
+This option enables the use of TLS.
=item B<CertificateFile> I<file>
Path to the PEM-encoded certificate file to use as client certificate when
connecting to the MQTT broker.
-A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
+Only valid if B<CACert> and B<CertificateKeyFile> are also set.
=item B<CertificateKeyFile> I<file>
Path to the unencrypted PEM-encoded key file corresponding to B<CertificateFile>.
-A valid TLS configuration requires B<CACert>, B<CertificateFile> and B<CertificateKeyFile>.
+Only valid if B<CACert> and B<CertificateFile> are also set.
=item B<TLSProtocol> I<protocol>
C<tlsv1.2>) to use for the TLS connection to the broker. If not set a default
version is used which depends on the version of OpenSSL the Mosquitto library
was linked against.
+Only valid if B<CACert> is set.
=item B<CipherSuite> I<ciphersuite>
A string describing the ciphers available for use. See L<ciphers(1)> and the
C<openssl ciphers> utility for more information. If unset, the default ciphers
will be used.
-
+Only valid if B<CACert> is set.
=back
or SQL threads are not running. Defaults to B<false>.
=item B<WsrepStats> I<true|false>
-
+
Enable the collection of wsrep plugin statistics, used in Master-Master
replication setups like in MySQL Galera/Percona XtraDB Cluster.
User needs only privileges to execute 'SHOW GLOBAL STATUS'
-
+
=item B<ConnectTimeout> I<Seconds>
Sets the connect timeout for the MySQL client.
=back
+=head2 Plugin C<nfs>
+
+The I<nfs plugin> collects information about the usage of the Network File
+System (NFS). It counts the number of procedure calls for each procedure,
+grouped by version and whether the system runs as server or client.
+
+It is possibly to omit metrics for a specific NFS version by setting one or
+more of the following options to B<false> (all of them default to B<true>).
+
+=over 4
+
+=item B<ReportV2> B<true>|B<false>
+
+=item B<ReportV3> B<true>|B<false>
+
+=item B<ReportV4> B<true>|B<false>
+
+=back
+
=head2 Plugin C<nginx>
This plugin collects the number of connections and requests handled by the
traffic statistics about connected clients.
To set up OpenVPN to write to the status file periodically, use the
-B<--status> option of OpenVPN. Since OpenVPN can write two different formats,
-you need to set the required format, too. This is done by setting
-B<--status-version> to B<2>.
+B<--status> option of OpenVPN.
So, in a nutshell you need:
openvpn $OTHER_OPTIONS \
- --status "/var/run/openvpn-status" 10 \
- --status-version 2
+ --status "/var/run/openvpn-status" 10
Available options:
</Result>
</Query>
<Database "product_information">
+ #Plugin "warehouse"
ConnectID "db01"
Username "oracle"
Password "secret"
=over 4
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting query results from
+this B<Database>. Defaults to C<oracle>.
+
=item B<ConnectID> I<ID>
Defines the "database alias" or "service name" to connect to. Usually, these
enable the interface, OVS DB daemon should be running with C<--remote=ptcp:>
option. See L<ovsdb-server(1)> for more details. The option may be either
network hostname, IPv4 numbers-and-dots notation or IPv6 hexadecimal string
-format. Defaults to B<'localhost'>.
+format. Defaults to C<localhost>.
=item B<Port> I<service>
enable the interface, OVS DB daemon should be running with C<--remote=ptcp:>
option. See L<ovsdb-server(1)> for more details. The option may be either
network hostname, IPv4 numbers-and-dots notation or IPv6 hexadecimal string
-format. Defaults to B<'localhost'>.
+format. Defaults to C<localhost>.
=item B<Port> I<service>
</Writer>
<Database foo>
+ Plugin "kingdom"
Host "hostname"
Port "5432"
User "username"
amount of time will be lost, for example, if a single statement within the
transaction fails or if the database server crashes.
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting query results from
+this B<Database>. Defaults to C<postgresql>.
+
=item B<Instance> I<name>
Specify the plugin instance name that should be used instead of the database
=head2 Plugin C<processes>
-=over 4
+Collects information about processes of local system.
-=item B<Process> I<Name>
+By default, with no process matches configured, only general statistics is
+collected: the number of processes in each state and fork rate.
-Select more detailed statistics of processes matching this name. The statistics
-collected for these selected processes are:
+Process matches can be configured by B<Process> and B<ProcessMatch> options.
+These may also be a block in which further options may be specified.
+
+The statistics collected for matched processes are:
- size of the resident segment size (RSS)
- user- and system-time used
- number of processes
- number of threads
- number of open files (under Linux)
+ - number of memory mapped files (under Linux)
- io data (where available)
- context switches (under Linux)
- minor and major pagefaults.
-Some platforms have a limit on the length of process names. I<Name> must stay
-below this limit.
+B<Synopsis:>
+
+ <Plugin processes>
+ CollectFileDescriptor true
+ CollectContextSwitch true
+ Process "name"
+ ProcessMatch "name" "regex"
+ <Process "collectd">
+ CollectFileDescriptor false
+ CollectContextSwitch false
+ </Process>
+ <ProcessMatch "name" "regex">
+ CollectFileDescriptor false
+ CollectContextSwitch true
+ </Process>
+ </Plugin>
+
+=over 4
+
+=item B<Process> I<Name>
+
+Select more detailed statistics of processes matching this name.
+
+Some platforms have a limit on the length of process names.
+I<Name> must stay below this limit.
=item B<ProcessMatch> I<name> I<regex>
-Similar to the B<Process> option this allows one to select more detailed
-statistics of processes matching the specified I<regex> (see L<regex(7)> for
-details). The statistics of all matching processes are summed up and
-dispatched to the daemon using the specified I<name> as an identifier. This
-allows one to "group" several processes together. I<name> must not contain
-slashes.
+Select more detailed statistics of processes matching the specified I<regex>
+(see L<regex(7)> for details). The statistics of all matching processes are
+summed up and dispatched to the daemon using the specified I<name> as an
+identifier. This allows one to "group" several processes together.
+I<name> must not contain slashes.
=item B<CollectContextSwitch> I<Boolean>
-Collect context switch of the process.
+Collect the number of context switches for matched processes.
+Disabled by default.
+
+=item B<CollectFileDescriptor> I<Boolean>
+
+Collect number of file descriptors of matched processes.
+Disabled by default.
+
+=item B<CollectMemoryMaps> I<Boolean>
+
+Collect the number of memory mapped files of the process.
+The limit for this number is configured via F</proc/sys/vm/max_map_count> in
+the Linux kernel.
=back
+Options B<CollectContextSwitch> and B<CollectFileDescriptor> may be used inside
+B<Process> and B<ProcessMatch> blocks - then they affect corresponding match
+only. Otherwise they set the default value for subsequent matches.
+
=head2 Plugin C<protocols>
Collects a lot of information about various network protocols, such as I<IP>,
When the C<rrdtool> plugin uses a cache (by setting B<CacheTimeout>, see below)
it writes all values for a certain RRD-file if the oldest value is older than
-(or equal to) the number of seconds specified. If some RRD-file is not updated
+(or equal to) the number of seconds specified by B<CacheTimeout>.
+That check happens on new values arriwal. If some RRD-file is not updated
anymore for some reason (the computer was shut down, the network is broken,
-etc.) some values may still be in the cache. If B<CacheFlush> is set, then the
-entire cache is searched for entries older than B<CacheTimeout> seconds and
-written to disk every I<Seconds> seconds. Since this is kind of expensive and
-does nothing under normal circumstances, this value should not be too small.
-900 seconds might be a good value, though setting this to 7200 seconds doesn't
-normally do much harm either.
+etc.) some values may still be in the cache. If B<CacheFlush> is set, then
+every I<Seconds> seconds the entire cache is searched for entries older than
+B<CacheTimeout> + B<RandomTimeout> seconds. The entries found are written to
+disk. Since scanning the entire cache is kind of expensive and does nothing
+under normal circumstances, this value should not be too small. 900 seconds
+might be a good value, though setting this to 7200 seconds doesn't normally
+do much harm either.
+
+Defaults to 10x B<CacheTimeout>.
+B<CacheFlush> must be larger than or equal to B<CacheTimeout>, otherwise the
+above default is used.
=item B<CacheTimeout> I<Seconds>
This is useful for deploying I<collectd> in a heterogeneous environment, where
swap sizes differ and you want to specify generic thresholds or similar.
+=item B<ReportIO> B<true>|B<false>
+
+Enables or disables reporting swap IO. Defaults to B<true>.
+
+This is useful for the cases when swap IO is not neccessary, is not available,
+or is not reliable.
+
=back
=head2 Plugin C<syslog>
<Plugin table>
<Table "/proc/slabinfo">
+ #Plugin "slab"
Instance "slabinfo"
Separator " "
<Result>
=over 4
+=item B<Plugin> I<Plugin>
+
+If specified, I<Plugin> is used as the plugin name when submitting values.
+Defaults to B<table>.
+
=item B<Instance> I<instance>
-If specified, I<instance> is used as the plugin instance. So, in the above
-example, the plugin name C<table-slabinfo> would be used. If omitted, the
+If specified, I<instance> is used as the plugin instance. If omitted, the
filename of the table is used instead, with all special characters replaced
with an underscore (C<_>).
<Plugin "tail">
<File "/var/log/exim4/mainlog">
+ Plugin "mail"
Instance "exim"
Interval 60
<Match>
<DSType "Distribution">
Percentile 99
Bucket 0 100
+ #BucketType "bucket"
</DSType>
Type "latency"
Instance "foo"
logfile to parse. Within each B<File> block, there are one or more B<Match>
blocks, which configure a regular expression to search for.
-The B<Instance> option in the B<File> block may be used to set the plugin
-instance. So in the above example the plugin name C<tail-foo> would be used.
-This plugin instance is for all B<Match> blocks that B<follow> it, until the
-next B<Instance> option. This way you can extract several plugin instances from
-one logfile, handy when parsing syslog and the like.
+The B<Plugin> and B<Instance> options in the B<File> block may be used to set
+the plugin name and instance respectively. So in the above example the plugin name
+C<mail-exim> would be used.
+
+These options are applied for all B<Match> blocks that B<follow> it, until the
+next B<Plugin> or B<Instance> option. This way you can extract several plugin
+instances from one logfile, handy when parsing syslog and the like.
The B<Interval> option allows you to define the length of time between reads. If
this is not set, the default Interval will be used.
<DSType "Distribution">
Percentile 99
Bucket 0 100
+ BucketType "bucket"
</DSType>
=over 4
Bucket 20 50
Bucket 50 0
-Metrics are reported with the I<type> C<bucket> and the I<type instance>
+Metrics are reported with the I<type> set by B<BucketType> option (C<bucket>
+by default) and the I<type instance>
C<E<lt>TypeE<gt>[-E<lt>InstanceE<gt>]-E<lt>lower_boundE<gt>_E<lt>upper_boundE<gt>>.
This option may be repeated to calculate more than one rate.
+=item B<BucketType> I<Type>
+
+Sets the type used to dispatch B<Bucket> metrics.
+Optional, by default C<bucket> will be used.
+
=back
=back
Index 1
</Metric>
<File "/var/log/snort/snort.stats">
- Instance "snort-eth0"
+ Plugin "snortstats"
+ Instance "eth0"
Interval 600
Collect "snort-dropped"
</File>
=over 4
+=item B<Plugin> I<Plugin>
+
+Use I<Plugin> as the plugin name when submitting values.
+Defaults to C<tail_csv>.
+
=item B<Instance> I<PluginInstance>
Sets the I<plugin instance> used when dispatching the values.
Boolean enabling the use of logical core numbering for per core statistics.
When enabled, C<cpuE<lt>nE<gt>> is used as plugin instance, where I<n> is a
-sequential number assigned by the kernel. Otherwise, C<coreE<lt>nE<gt>> is used
-where I<n> is the n-th core of the socket, causing name conflicts when there is
-more than one socket.
+dynamic number assigned by the kernel. Otherwise, C<coreE<lt>nE<gt>> is used
+if there is only one package and C<pkgE<lt>nE<gt>-coreE<lt>mE<gt>> if there is
+more than one, where I<n> is the n-th core of package I<m>.
=back
CollectVCL false
CollectVSM false
CollectWorkers false
+ CollectLock false
+ CollectMempool false
+ CollectManagement false
+ CollectSMF false
+ CollectVBE false
+ CollectMSE false
</Instance>
</Plugin>
=item B<CollectSMA> B<true>|B<false>
malloc or umem (umem_alloc(3MALLOC) based) storage statistics. The umem storage
-component is Solaris specific. Only available with Varnish 2.x. False by
-default.
+component is Solaris specific. Note: SMA, SMF and MSE share counters, enable
+only the one used by the Varnish instance. Only available with Varnish 2.x.
+False by default.
=item B<CollectSMS> B<true>|B<false>
=item B<CollectSM> B<true>|B<false>
-file (memory mapped file) storage statistics. Only available with Varnish 2.x.
+file (memory mapped file) storage statistics. Only available with Varnish 2.x.,
+in varnish 4.x. use CollectSMF.
False by default.
=item B<CollectStruct> B<true>|B<false>
Collect statistics about worker threads. False by default.
+=item B<CollectVBE> B<true>|B<false>
+
+Backend counters. Only available with Varnish 4.x. False by default.
+
+=item B<CollectSMF> B<true>|B<false>
+
+file (memory mapped file) storage statistics. Only available with Varnish 4.x.
+Note: SMA, SMF and MSE share counters, enable only the one used by the Varnish
+instance. Used to be called SM in Varnish 2.x. False by default.
+
+=item B<CollectManagement> B<true>|B<false>
+
+Management process counters. Only available with Varnish 4.x. False by default.
+
+=item B<CollectLock> B<true>|B<false>
+
+Lock counters. Only available with Varnish 4.x. False by default.
+
+=item B<CollectMempool> B<true>|B<false>
+
+Memory pool counters. Only available with Varnish 4.x. False by default.
+
+=item B<CollectMSE> B<true>|B<false>
+
+Varnish Massive Storage Engine 2.0 (MSE2) is an improved storage backend for
+Varnish, replacing the traditional malloc and file storages. Only available
+with Varnish-Plus 4.x. Note: SMA, SMF and MSE share counters, enable only the
+one used by the Varnish instance. False by default.
+
=back
=head2 Plugin C<virt>
=item B<BlockDeviceFormat> B<target>|B<source>
If I<BlockDeviceFormat> is set to B<target>, the default, then the device name
-seen by the guest will be used for reporting metrics.
+seen by the guest will be used for reporting metrics.
This corresponds to the C<E<lt>targetE<gt>> node in the XML definition of the
domain.
Please refer to L<http://kairosdb.github.io/docs/build/html/restapi/AddDataPoints.html?highlight=ttl>
+=item B<Prefix> I<String>
+
+Only available for the KAIROSDB output format.
+
+Sets the metrics prefix I<string>. Defaults to I<collectd>.
+
=item B<Metrics> B<true>|B<false>
Controls whether I<metrics> are POSTed to this location. Defaults to B<true>.
Prefix "collectd/"
Database 1
MaxSetSize -1
+ MaxSetDuration -1
StoreRates true
</Node>
</Plugin>
The B<MaxSetSize> option limits the number of items that the I<Sorted Sets> can
hold. Negative values for I<Items> sets no limit, which is the default behavior.
+=item B<MaxSetDuration> I<Seconds>
+
+The B<MaxSetDuration> option limits the duration of items that the
+I<Sorted Sets> can hold. Negative values for I<Items> sets no duration, which
+is the default behavior.
+
=item B<StoreRates> B<true>|B<false>
If set to B<true> (the default), convert counter values to rates. If set to
=item B<-P> I<E<lt>pid-fileE<gt>>
-Specify an alternative pid file. This overwrites any settings in the config
+Specify an alternative pid file. This overwrites any settings in the config
file. This is thought for init-scripts that require the PID-file in a certain
directory to work correctly. For everyday-usage use the B<PIDFile>
config-option.
+=item B<-B>
+
+If set, collectd will I<not> try to create its base directory. If the base
+directory does not exist, it will exit rather than trying to create the
+directory.
+
=item B<-f>
Don't fork to the background. I<collectd> will also B<not> close standard file
#endif
#endif /* NAN_ZERO_ZERO */
-#include "libcollectdclient/collectd/client.h"
+#include "collectd/client.h"
#ifndef PREFIX
#define PREFIX "/opt/" PACKAGE_NAME
#define COLLECTD_CPU_STATE_INTERRUPT 5
#define COLLECTD_CPU_STATE_SOFTIRQ 6
#define COLLECTD_CPU_STATE_STEAL 7
-#define COLLECTD_CPU_STATE_IDLE 8
-#define COLLECTD_CPU_STATE_ACTIVE 9 /* sum of (!idle) */
-#define COLLECTD_CPU_STATE_MAX 10 /* #states */
+#define COLLECTD_CPU_STATE_GUEST 8
+#define COLLECTD_CPU_STATE_GUEST_NICE 9
+#define COLLECTD_CPU_STATE_IDLE 10
+#define COLLECTD_CPU_STATE_ACTIVE 11 /* sum of (!idle) */
+#define COLLECTD_CPU_STATE_MAX 12 /* #states */
#if HAVE_STATGRAB_H
#include <statgrab.h>
static const char *cpu_state_names[] = {"user", "system", "wait", "nice",
"swap", "interrupt", "softirq", "steal",
- "idle", "active"};
+ "guest", "guest_nice", "idle", "active"};
#ifdef PROCESSOR_CPU_LOAD_INFO
static mach_port_t port_host;
static _Bool report_by_state = 1;
static _Bool report_percent = 0;
static _Bool report_num_cpu = 0;
+static _Bool report_guest = 0;
+static _Bool subtract_guest = 1;
static const char *config_keys[] = {"ReportByCpu", "ReportByState",
- "ReportNumCpu", "ValuesPercentage"};
+ "ReportNumCpu", "ValuesPercentage",
+ "ReportGuestState", "SubtractGuestState"};
static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
static int cpu_config(char const *key, char const *value) /* {{{ */
report_by_state = IS_TRUE(value) ? 1 : 0;
else if (strcasecmp(key, "ReportNumCpu") == 0)
report_num_cpu = IS_TRUE(value) ? 1 : 0;
+ else if (strcasecmp(key, "ReportGuestState") == 0)
+ report_guest = IS_TRUE(value) ? 1 : 0;
+ else if (strcasecmp(key, "SubtractGuestState") == 0)
+ subtract_guest = IS_TRUE(value) ? 1 : 0;
else
return -1;
static void cpu_commit(void) /* {{{ */
{
gauge_t global_rates[COLLECTD_CPU_STATE_MAX] = {
- NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN /* Batman! */
+ NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN /* Batman! */
};
if (report_num_cpu)
for (size_t cpu_num = 0; cpu_num < global_cpu_num; cpu_num++) {
cpu_state_t *this_cpu_states = get_cpu_state(cpu_num, 0);
gauge_t local_rates[COLLECTD_CPU_STATE_MAX] = {NAN, NAN, NAN, NAN, NAN,
- NAN, NAN, NAN, NAN, NAN};
+ NAN, NAN, NAN, NAN, NAN,
+ NAN, NAN };
for (size_t state = 0; state < COLLECTD_CPU_STATE_MAX; state++)
if (this_cpu_states[state].has_value)
FILE *fh;
char buf[1024];
- char *fields[9];
+ char *fields[11];
int numfields;
if ((fh = fopen("/proc/stat", "r")) == NULL) {
if ((buf[3] < '0') || (buf[3] > '9'))
continue;
- numfields = strsplit(buf, fields, 9);
+ numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE(fields));
if (numfields < 5)
continue;
cpu = atoi(fields[0] + 3);
- cpu_stage(cpu, COLLECTD_CPU_STATE_USER, (derive_t)atoll(fields[1]), now);
- cpu_stage(cpu, COLLECTD_CPU_STATE_NICE, (derive_t)atoll(fields[2]), now);
+ /* Do not stage User and Nice immediately: we may need to alter them later: */
+ long long user_value = atoll(fields[1]);
+ long long nice_value = atoll(fields[2]);
cpu_stage(cpu, COLLECTD_CPU_STATE_SYSTEM, (derive_t)atoll(fields[3]), now);
cpu_stage(cpu, COLLECTD_CPU_STATE_IDLE, (derive_t)atoll(fields[4]), now);
now);
cpu_stage(cpu, COLLECTD_CPU_STATE_SOFTIRQ, (derive_t)atoll(fields[7]),
now);
+ }
- if (numfields >= 9)
- cpu_stage(cpu, COLLECTD_CPU_STATE_STEAL, (derive_t)atoll(fields[8]),
- now);
+ if (numfields >= 9) { /* Steal (since Linux 2.6.11) */
+ cpu_stage(cpu, COLLECTD_CPU_STATE_STEAL, (derive_t)atoll(fields[8]), now);
}
+
+ if (numfields >= 10) { /* Guest (since Linux 2.6.24) */
+ if (report_guest) {
+ long long value = atoll(fields[9]);
+ cpu_stage(cpu, COLLECTD_CPU_STATE_GUEST, (derive_t)value, now);
+ /* Guest is included in User; optionally subtract Guest from User: */
+ if (subtract_guest) {
+ user_value -= value;
+ if (user_value < 0) user_value = 0;
+ }
+ }
+ }
+
+ if (numfields >= 11) { /* Guest_nice (since Linux 2.6.33) */
+ if (report_guest) {
+ long long value = atoll(fields[10]);
+ cpu_stage(cpu, COLLECTD_CPU_STATE_GUEST_NICE, (derive_t)value, now);
+ /* Guest_nice is included in Nice; optionally subtract Guest_nice from
+ Nice: */
+ if (subtract_guest) {
+ nice_value -= value;
+ if (nice_value < 0) nice_value = 0;
+ }
+ }
+ }
+
+ /* Eventually stage User and Nice: */
+ cpu_stage(cpu, COLLECTD_CPU_STATE_USER, (derive_t)user_value, now);
+ cpu_stage(cpu, COLLECTD_CPU_STATE_NICE, (derive_t)nice_value, now);
}
fclose(fh);
/* }}} #endif defined(KERNEL_LINUX) */
typedef struct {
PluginData data;
+ PyObject *meta; /* dict */
int severity;
char message[NOTIF_MAX_MSG_LEN];
} Notification;
typedef struct web_page_s web_page_t;
struct web_page_s /* {{{ */
{
+ char *plugin_name;
char *instance;
char *url;
curl_easy_cleanup(wp->curl);
wp->curl = NULL;
+ sfree(wp->plugin_name);
sfree(wp->instance);
sfree(wp->url);
ERROR("curl plugin: calloc failed.");
return -1;
}
+ page->plugin_name = NULL;
page->url = NULL;
page->user = NULL;
page->pass = NULL;
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
- if (strcasecmp("URL", child->key) == 0)
+ if (strcasecmp("Plugin", child->key) == 0)
+ status = cf_util_get_string(child, &page->plugin_name);
+ else if (strcasecmp("URL", child->key) == 0)
status = cf_util_get_string(child, &page->url);
else if (strcasecmp("User", child->key) == 0)
status = cf_util_get_string(child, &page->user);
vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.plugin, "curl", sizeof(vl.plugin));
+ sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "curl",
+ sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, wm->type, sizeof(vl.type));
if (wm->instance != NULL)
vl.values = &(value_t){.gauge = (gauge_t)code};
vl.values_len = 1;
- sstrncpy(vl.plugin, "curl", sizeof(vl.plugin));
+ sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "curl",
+ sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "response_code", sizeof(vl.type));
vl.values = &(value_t){.gauge = response_time};
vl.values_len = 1;
- sstrncpy(vl.plugin, "curl", sizeof(vl.plugin));
+ sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "curl",
+ sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, "response_time", sizeof(vl.type));
if (wp->response_time)
cc_submit_response_time(wp, CDTIME_T_TO_DOUBLE(cdtime() - start));
if (wp->stats != NULL)
- curl_stats_dispatch(wp->stats, wp->curl, hostname_g, "curl", wp->instance);
+ curl_stats_dispatch(wp->stats, wp->curl, NULL, "curl", wp->instance);
if (wp->response_code) {
long response_code = 0;
struct cj_s /* {{{ */
{
char *instance;
+ char *plugin_name;
char *host;
char *sock;
#define CJ_CB_ABORT 0
#define CJ_CB_CONTINUE 1
-static int cj_cb_boolean(void *ctx, int boolVal) {
- cj_advance_array(ctx);
- return CJ_CB_CONTINUE;
-}
-
static int cj_cb_null(void *ctx) {
cj_advance_array(ctx);
return CJ_CB_CONTINUE;
return cj_cb_number(ctx, (const char *)val, len);
} /* int cj_cb_string */
+static int cj_cb_boolean(void *ctx, int boolVal) {
+ if (boolVal)
+ return cj_cb_number(ctx, "1", 1);
+ else
+ return cj_cb_number(ctx, "0", 1);
+} /* int cj_cb_boolean */
+
static int cj_cb_end(void *ctx) {
cj_t *db = (cj_t *)ctx;
memset(&db->state[db->depth], 0, sizeof(db->state[db->depth]));
db->tree = NULL;
sfree(db->instance);
+ sfree(db->plugin_name);
sfree(db->host);
sfree(db->sock);
if (strcasecmp("Instance", child->key) == 0)
status = cf_util_get_string(child, &db->instance);
+ else if (strcasecmp("Plugin", child->key) == 0)
+ status = cf_util_get_string(child, &db->plugin_name);
else if (strcasecmp("Host", child->key) == 0)
status = cf_util_get_string(child, &db->host);
else if (db->url && strcasecmp("User", child->key) == 0)
sstrncpy(vl.type_instance, key->instance, sizeof(vl.type_instance));
sstrncpy(vl.host, cj_host(db), sizeof(vl.host));
- sstrncpy(vl.plugin, "curl_json", sizeof(vl.plugin));
+ sstrncpy(vl.plugin, (db->plugin_name != NULL) ? db->plugin_name : "curl_json",
+ sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, db->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, key->type, sizeof(vl.type));
size_t values_len;
char *instance_prefix;
char *instance;
+ char *plugin_instance_from;
int is_table;
unsigned long magic;
};
struct cx_s /* {{{ */
{
char *instance;
+ char *plugin_name;
char *host;
char *url;
size_t buffer_size;
size_t buffer_fill;
- llist_t *list; /* list of xpath blocks */
+ llist_t *xpath_list; /* list of xpath blocks */
};
typedef struct cx_s cx_t; /* }}} */
static size_t cx_curl_callback(void *buf, /* {{{ */
size_t size, size_t nmemb, void *user_data) {
size_t len = size * nmemb;
- cx_t *db;
- db = user_data;
+ cx_t *db = user_data;
if (db == NULL) {
ERROR("curl_xml plugin: cx_curl_callback: "
"user_data pointer is NULL.");
return len;
if ((db->buffer_fill + len) >= db->buffer_size) {
- char *temp;
-
- temp = realloc(db->buffer, db->buffer_fill + len + 1);
+ char *temp = realloc(db->buffer, db->buffer_fill + len + 1);
if (temp == NULL) {
ERROR("curl_xml plugin: realloc failed.");
return 0;
sfree(xpath->path);
sfree(xpath->type);
sfree(xpath->instance_prefix);
+ sfree(xpath->plugin_instance_from);
sfree(xpath->instance);
sfree(xpath->values);
sfree(xpath);
} /* }}} void cx_xpath_free */
-static void cx_list_free(llist_t *list) /* {{{ */
+static void cx_xpath_list_free(llist_t *list) /* {{{ */
{
llentry_t *le;
le = llist_head(list);
while (le != NULL) {
- llentry_t *le_next;
+ llentry_t *le_next = le->next;
- le_next = le->next;
-
- sfree(le->key);
+ /* this also frees xpath->path used for le->key */
cx_xpath_free(le->value);
le = le_next;
}
llist_destroy(list);
-} /* }}} void cx_list_free */
+} /* }}} void cx_xpath_list_free */
static void cx_free(void *arg) /* {{{ */
{
curl_easy_cleanup(db->curl);
db->curl = NULL;
- if (db->list != NULL)
- cx_list_free(db->list);
+ if (db->xpath_list != NULL)
+ cx_xpath_list_free(db->xpath_list);
sfree(db->buffer);
sfree(db->instance);
+ sfree(db->plugin_name);
sfree(db->host);
sfree(db->url);
sfree(db);
} /* }}} void cx_free */
-static const char *cx_host(cx_t *db) /* {{{ */
+static const char *cx_host(const cx_t *db) /* {{{ */
{
if (db->host == NULL)
return hostname_g;
return 0;
} /* }}} cx_check_type */
-static xmlXPathObjectPtr
-cx_evaluate_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
- xmlChar *expr) {
- xmlXPathObjectPtr xpath_obj;
-
- /* XXX: When to free this? */
- xpath_obj = xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
+static xmlXPathObjectPtr cx_evaluate_xpath(xmlXPathContextPtr xpath_ctx,
+ char *expr) /* {{{ */
+{
+ xmlXPathObjectPtr xpath_obj =
+ xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
if (xpath_obj == NULL) {
WARNING("curl_xml plugin: "
"Error unable to evaluate xpath expression \"%s\". Skipping...",
return -1;
} /* }}} cx_if_not_text_node */
-static int cx_handle_single_value_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
- cx_xpath_t *xpath, const data_set_t *ds,
- value_list_t *vl, int index) {
- xmlXPathObjectPtr values_node_obj;
- xmlNodeSetPtr values_node;
- int tmp_size;
- char *node_value;
-
- values_node_obj =
- cx_evaluate_xpath(xpath_ctx, BAD_CAST xpath->values[index].path);
+/*
+ * Returned value should be freed with xmlFree().
+ */
+static char *cx_get_text_node_value(xmlXPathContextPtr xpath_ctx, /* {{{ */
+ char *expr, const char *from_option) {
+ xmlXPathObjectPtr values_node_obj = cx_evaluate_xpath(xpath_ctx, expr);
if (values_node_obj == NULL)
- return -1; /* Error already logged. */
+ return NULL; /* Error already logged. */
- values_node = values_node_obj->nodesetval;
- tmp_size = (values_node) ? values_node->nodeNr : 0;
+ xmlNodeSetPtr values_node = values_node_obj->nodesetval;
+ size_t tmp_size = (values_node) ? values_node->nodeNr : 0;
if (tmp_size == 0) {
WARNING("curl_xml plugin: "
- "relative xpath expression \"%s\" doesn't match any of the nodes. "
- "Skipping...",
- xpath->values[index].path);
+ "relative xpath expression \"%s\" from '%s' doesn't match "
+ "any of the nodes.",
+ expr, from_option);
xmlXPathFreeObject(values_node_obj);
- return -1;
+ return NULL;
}
if (tmp_size > 1) {
WARNING("curl_xml plugin: "
- "relative xpath expression \"%s\" is expected to return "
- "only one node. Skipping...",
- xpath->values[index].path);
+ "relative xpath expression \"%s\" from '%s' is expected to return "
+ "only one text node. Skipping the node.",
+ expr, from_option);
xmlXPathFreeObject(values_node_obj);
- return -1;
+ return NULL;
}
/* ignoring the element if other than textnode/attribute*/
if (cx_if_not_text_node(values_node->nodeTab[0])) {
WARNING("curl_xml plugin: "
- "relative xpath expression \"%s\" is expected to return "
- "only text/attribute node which is not the case. Skipping...",
- xpath->values[index].path);
+ "relative xpath expression \"%s\" from '%s' is expected to return "
+ "only text/attribute node which is not the case. "
+ "Skipping the node.",
+ expr, from_option);
xmlXPathFreeObject(values_node_obj);
- return -1;
+ return NULL;
}
- node_value = (char *)xmlNodeGetContent(values_node->nodeTab[0]);
+ char *node_value = (char *)xmlNodeGetContent(values_node->nodeTab[0]);
+
+ /* free up object */
+ xmlXPathFreeObject(values_node_obj);
+
+ return node_value;
+} /* }}} char * cx_get_text_node_value */
+
+static int cx_handle_single_value_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath, const data_set_t *ds,
+ value_list_t *vl, int index) {
+
+ char *node_value = cx_get_text_node_value(
+ xpath_ctx, xpath->values[index].path, "ValuesFrom");
+
+ if (node_value == NULL)
+ return -1;
+
switch (ds->ds[index].type) {
case DS_TYPE_COUNTER:
vl->values[index].counter =
/* endptr = */ NULL);
}
- /* free up object */
- xmlXPathFreeObject(values_node_obj);
- sfree(node_value);
+ xmlFree(node_value);
/* We have reached here which means that
* we have got something to work */
cx_xpath_t *xpath, const data_set_t *ds,
value_list_t *vl) {
value_t values[xpath->values_len];
- int status;
assert(xpath->values_len > 0);
assert(xpath->values_len == vl->values_len);
vl->values = values;
for (size_t i = 0; i < xpath->values_len; i++) {
- status = cx_handle_single_value_xpath(xpath_ctx, xpath, ds, vl, i);
- if (status != 0)
+ if (cx_handle_single_value_xpath(xpath_ctx, xpath, ds, vl, i) != 0)
return -1; /* An error has been printed. */
} /* for (i = 0; i < xpath->values_len; i++) */
} /* }}} int cx_handle_all_value_xpaths */
static int cx_handle_instance_xpath(xmlXPathContextPtr xpath_ctx, /* {{{ */
- cx_xpath_t *xpath, value_list_t *vl,
- _Bool is_table) {
- xmlXPathObjectPtr instance_node_obj = NULL;
- xmlNodeSetPtr instance_node = NULL;
-
- memset(vl->type_instance, 0, sizeof(vl->type_instance));
-
- /* If the base xpath returns more than one block, the result is assumed to be
- * a table. The `Instance' option is not optional in this case. Check for the
- * condition and inform the user. */
- if (is_table && (xpath->instance == NULL)) {
- WARNING("curl_xml plugin: "
- "Base-XPath %s is a table (more than one result was returned), "
- "but no instance-XPath has been defined.",
- xpath->path);
- return -1;
- }
+ cx_xpath_t *xpath, value_list_t *vl) {
- /* instance has to be an xpath expression */
+ /* Handle type instance */
if (xpath->instance != NULL) {
- int tmp_size;
-
- instance_node_obj = cx_evaluate_xpath(xpath_ctx, BAD_CAST xpath->instance);
- if (instance_node_obj == NULL)
- return -1; /* error is logged already */
-
- instance_node = instance_node_obj->nodesetval;
- tmp_size = (instance_node) ? instance_node->nodeNr : 0;
-
- if (tmp_size <= 0) {
- WARNING(
- "curl_xml plugin: "
- "relative xpath expression for 'InstanceFrom' \"%s\" doesn't match "
- "any of the nodes. Skipping the node.",
- xpath->instance);
- xmlXPathFreeObject(instance_node_obj);
+ char *node_value =
+ cx_get_text_node_value(xpath_ctx, xpath->instance, "InstanceFrom");
+ if (node_value == NULL)
return -1;
- }
- if (tmp_size > 1) {
- WARNING("curl_xml plugin: "
- "relative xpath expression for 'InstanceFrom' \"%s\" is expected "
- "to return only one text node. Skipping the node.",
- xpath->instance);
- xmlXPathFreeObject(instance_node_obj);
- return -1;
- }
-
- /* ignoring the element if other than textnode/attribute */
- if (cx_if_not_text_node(instance_node->nodeTab[0])) {
- WARNING("curl_xml plugin: "
- "relative xpath expression \"%s\" is expected to return only "
- "text node "
- "which is not the case. Skipping the node.",
- xpath->instance);
- xmlXPathFreeObject(instance_node_obj);
- return -1;
- }
- } /* if (xpath->instance != NULL) */
-
- if (xpath->instance_prefix != NULL) {
- if (instance_node != NULL) {
- char *node_value = (char *)xmlNodeGetContent(instance_node->nodeTab[0]);
+ if (xpath->instance_prefix != NULL)
snprintf(vl->type_instance, sizeof(vl->type_instance), "%s%s",
xpath->instance_prefix, node_value);
- sfree(node_value);
- } else
- sstrncpy(vl->type_instance, xpath->instance_prefix,
- sizeof(vl->type_instance));
- } else {
- /* If instance_prefix and instance_node are NULL, then
- * don't set the type_instance */
- if (instance_node != NULL) {
- char *node_value = (char *)xmlNodeGetContent(instance_node->nodeTab[0]);
+ else
sstrncpy(vl->type_instance, node_value, sizeof(vl->type_instance));
- sfree(node_value);
- }
- }
- /* Free `instance_node_obj' this late, because `instance_node' points to
- * somewhere inside this structure. */
- xmlXPathFreeObject(instance_node_obj);
+ xmlFree(node_value);
+ } else if (xpath->instance_prefix != NULL)
+ sstrncpy(vl->type_instance, xpath->instance_prefix,
+ sizeof(vl->type_instance));
+
+ /* Handle plugin instance */
+ if (xpath->plugin_instance_from != NULL) {
+ char *node_value = cx_get_text_node_value(
+ xpath_ctx, xpath->plugin_instance_from, "PluginInstanceFrom");
+
+ if (node_value == NULL)
+ return -1;
+
+ sstrncpy(vl->plugin_instance, node_value, sizeof(vl->plugin_instance));
+ xmlFree(node_value);
+ }
return 0;
} /* }}} int cx_handle_instance_xpath */
-static int cx_handle_base_xpath(char const *plugin_instance, /* {{{ */
- char const *host, xmlXPathContextPtr xpath_ctx,
- const data_set_t *ds, char *base_xpath,
- cx_xpath_t *xpath) {
- int total_nodes;
+static int cx_handle_xpath(const cx_t *db, /* {{{ */
+ xmlXPathContextPtr xpath_ctx, cx_xpath_t *xpath) {
- xmlXPathObjectPtr base_node_obj = NULL;
- xmlNodeSetPtr base_nodes = NULL;
-
- value_list_t vl = VALUE_LIST_INIT;
+ const data_set_t *ds = plugin_get_ds(xpath->type);
+ if (cx_check_type(ds, xpath) != 0)
+ return -1;
- base_node_obj = cx_evaluate_xpath(xpath_ctx, BAD_CAST base_xpath);
+ xmlXPathObjectPtr base_node_obj = cx_evaluate_xpath(xpath_ctx, xpath->path);
if (base_node_obj == NULL)
return -1; /* error is logged already */
- base_nodes = base_node_obj->nodesetval;
- total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
+ xmlNodeSetPtr base_nodes = base_node_obj->nodesetval;
+ int total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
if (total_nodes == 0) {
ERROR("curl_xml plugin: "
"xpath expression \"%s\" doesn't match any of the nodes. "
"Skipping the xpath block...",
- base_xpath);
+ xpath->path);
xmlXPathFreeObject(base_node_obj);
return -1;
}
/* If base_xpath returned multiple results, then */
- /* Instance in the xpath block is required */
- if (total_nodes > 1 && xpath->instance == NULL) {
+ /* InstanceFrom or PluginInstanceFrom in the xpath block is required */
+ if (total_nodes > 1 && xpath->instance == NULL &&
+ xpath->plugin_instance_from == NULL) {
ERROR("curl_xml plugin: "
- "InstanceFrom is must in xpath block since the base xpath expression "
- "\"%s\" "
+ "InstanceFrom or PluginInstanceFrom is must in xpath block "
+ "since the base xpath expression \"%s\" "
"returned multiple results. Skipping the xpath block...",
- base_xpath);
+ xpath->path);
+ xmlXPathFreeObject(base_node_obj);
return -1;
}
+ value_list_t vl = VALUE_LIST_INIT;
+
/* set the values for the value_list */
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, sizeof(vl.host));
- if (plugin_instance != NULL)
- sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
+ sstrncpy(vl.plugin, (db->plugin_name != NULL) ? db->plugin_name : "curl_xml",
+ sizeof(vl.plugin));
+ sstrncpy(vl.host, cx_host(db), sizeof(vl.host));
for (int i = 0; i < total_nodes; i++) {
- int status;
-
xpath_ctx->node = base_nodes->nodeTab[i];
- status = cx_handle_instance_xpath(xpath_ctx, xpath, &vl,
- /* is_table = */ (total_nodes > 1));
- if (status != 0)
+ if (db->instance != NULL)
+ sstrncpy(vl.plugin_instance, db->instance, sizeof(vl.plugin_instance));
+
+ if (cx_handle_instance_xpath(xpath_ctx, xpath, &vl) != 0)
continue; /* An error has already been reported. */
- status = cx_handle_all_value_xpaths(xpath_ctx, xpath, ds, &vl);
- if (status != 0)
+ if (cx_handle_all_value_xpaths(xpath_ctx, xpath, ds, &vl) != 0)
continue; /* An error has been logged. */
} /* for (i = 0; i < total_nodes; i++) */
xmlXPathFreeObject(base_node_obj);
return 0;
-} /* }}} cx_handle_base_xpath */
+} /* }}} cx_handle_xpath */
-static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
- xmlXPathContextPtr xpath_ctx, cx_t *db) {
- llentry_t *le;
- const data_set_t *ds;
- cx_xpath_t *xpath;
+static int cx_handle_parsed_xml(cx_t *db, xmlDocPtr doc, /* {{{ */
+ xmlXPathContextPtr xpath_ctx) {
int status = -1;
- le = llist_head(db->list);
+ llentry_t *le = llist_head(db->xpath_list);
while (le != NULL) {
- /* get the ds */
- xpath = (cx_xpath_t *)le->value;
- ds = plugin_get_ds(xpath->type);
+ cx_xpath_t *xpath = (cx_xpath_t *)le->value;
- if ((cx_check_type(ds, xpath) == 0) &&
- (cx_handle_base_xpath(db->instance, cx_host(db), xpath_ctx, ds, le->key,
- xpath) == 0))
+ if (cx_handle_xpath(db, xpath_ctx, xpath) == 0)
status = 0; /* we got atleast one success */
le = le->next;
return status;
} /* }}} cx_handle_parsed_xml */
-static int cx_parse_stats_xml(xmlChar *xml, cx_t *db) /* {{{ */
+static int cx_parse_xml(cx_t *db, char *xml) /* {{{ */
{
- int status;
- xmlDocPtr doc;
- xmlXPathContextPtr xpath_ctx;
-
/* Load the XML */
- doc = xmlParseDoc(xml);
+ xmlDocPtr doc = xmlParseDoc(BAD_CAST xml);
if (doc == NULL) {
ERROR("curl_xml plugin: Failed to parse the xml document - %s", xml);
return -1;
}
- xpath_ctx = xmlXPathNewContext(doc);
+ xmlXPathContextPtr xpath_ctx = xmlXPathNewContext(doc);
if (xpath_ctx == NULL) {
ERROR("curl_xml plugin: Failed to create the xml context");
xmlFreeDoc(doc);
for (size_t i = 0; i < db->namespaces_num; i++) {
cx_namespace_t const *ns = db->namespaces + i;
- status =
+ int status =
xmlXPathRegisterNs(xpath_ctx, BAD_CAST ns->prefix, BAD_CAST ns->url);
if (status != 0) {
ERROR("curl_xml plugin: "
}
}
- status = cx_handle_parsed_xml(doc, xpath_ctx, db);
+ int status = cx_handle_parsed_xml(db, doc, xpath_ctx);
/* Cleanup */
xmlXPathFreeContext(xpath_ctx);
xmlFreeDoc(doc);
return status;
-} /* }}} cx_parse_stats_xml */
+} /* }}} cx_parse_xml */
-static int cx_curl_perform(cx_t *db, CURL *curl) /* {{{ */
+static int cx_read(user_data_t *ud) /* {{{ */
{
- int status;
+ if ((ud == NULL) || (ud->data == NULL)) {
+ ERROR("curl_xml plugin: cx_read: Invalid user data.");
+ return -1;
+ }
+
long rc;
- char *ptr;
char *url;
+ cx_t *db = (cx_t *)ud->data;
db->buffer_fill = 0;
curl_easy_setopt(db->curl, CURLOPT_URL, db->url);
- status = curl_easy_perform(curl);
+ int status = curl_easy_perform(db->curl);
if (status != CURLE_OK) {
ERROR("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
status, db->curl_errbuf, db->url);
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);
+ curl_easy_getinfo(db->curl, CURLINFO_EFFECTIVE_URL, &url);
+ curl_easy_getinfo(db->curl, CURLINFO_RESPONSE_CODE, &rc);
/* The response code is zero if a non-HTTP transport was used. */
if ((rc != 0) && (rc != 200)) {
return -1;
}
- ptr = db->buffer;
-
- status = cx_parse_stats_xml(BAD_CAST ptr, db);
+ status = cx_parse_xml(db, db->buffer);
db->buffer_fill = 0;
return status;
-} /* }}} int cx_curl_perform */
-
-static int cx_read(user_data_t *ud) /* {{{ */
-{
- cx_t *db;
-
- if ((ud == NULL) || (ud->data == NULL)) {
- ERROR("curl_xml plugin: cx_read: Invalid user data.");
- return -1;
- }
-
- db = (cx_t *)ud->data;
-
- return cx_curl_perform(db, db->curl);
} /* }}} int cx_read */
/* Configuration handling functions {{{ */
static int cx_config_add_xpath(cx_t *db, oconfig_item_t *ci) /* {{{ */
{
- cx_xpath_t *xpath;
- char *name;
- llentry_t *le;
- int status;
-
- xpath = calloc(1, sizeof(*xpath));
+ cx_xpath_t *xpath = calloc(1, sizeof(*xpath));
if (xpath == NULL) {
ERROR("curl_xml plugin: calloc failed.");
return -1;
}
- status = cf_util_get_string(ci, &xpath->path);
+ int status = cf_util_get_string(ci, &xpath->path);
if (status != 0) {
cx_xpath_free(xpath);
return status;
status = cf_util_get_string(child, &xpath->instance_prefix);
else if (strcasecmp("InstanceFrom", child->key) == 0)
status = cf_util_get_string(child, &xpath->instance);
+ else if (strcasecmp("PluginInstanceFrom", child->key) == 0)
+ status = cf_util_get_string(child, &xpath->plugin_instance_from);
else if (strcasecmp("ValuesFrom", child->key) == 0)
status = cx_config_add_values("ValuesFrom", xpath, child);
else {
return -1;
}
- if (db->list == NULL) {
- db->list = llist_create();
- if (db->list == NULL) {
- ERROR("curl_xml plugin: list creation failed.");
- cx_xpath_free(xpath);
- return -1;
- }
- }
-
- name = strdup(xpath->path);
- if (name == NULL) {
- ERROR("curl_xml plugin: strdup failed.");
+ if (xpath->values_len == 0) {
+ WARNING("curl_xml plugin: `ValuesFrom' missing in `xpath' block.");
cx_xpath_free(xpath);
return -1;
}
- le = llentry_create(name, xpath);
+ llentry_t *le = llentry_create(xpath->path, xpath);
if (le == NULL) {
ERROR("curl_xml plugin: llentry_create failed.");
cx_xpath_free(xpath);
- sfree(name);
return -1;
}
- llist_append(db->list, le);
+ llist_append(db->xpath_list, le);
return 0;
} /* }}} int cx_config_add_xpath */
static int cx_config_add_namespace(cx_t *db, /* {{{ */
oconfig_item_t *ci) {
- cx_namespace_t *ns;
if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
(ci->values[1].type != OCONFIG_TYPE_STRING)) {
return EINVAL;
}
- ns = realloc(db->namespaces,
- sizeof(*db->namespaces) * (db->namespaces_num + 1));
+ cx_namespace_t *ns = realloc(
+ db->namespaces, sizeof(*db->namespaces) * (db->namespaces_num + 1));
if (ns == NULL) {
ERROR("curl_xml plugin: realloc failed.");
return ENOMEM;
static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
{
- cx_t *db;
- int status = 0;
-
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
WARNING("curl_xml plugin: The `URL' block "
"needs exactly one string argument.");
return -1;
}
- db = calloc(1, sizeof(*db));
+ cx_t *db = calloc(1, sizeof(*db));
if (db == NULL) {
ERROR("curl_xml plugin: calloc failed.");
return -1;
}
- db->timeout = -1;
+ db->instance = strdup("default");
+ if (db->instance == NULL) {
+ ERROR("curl_xml plugin: strdup failed.");
+ sfree(db);
+ return -1;
+ }
- if (strcasecmp("URL", ci->key) == 0) {
- status = cf_util_get_string(ci, &db->url);
- if (status != 0) {
- sfree(db);
- return status;
- }
- } else {
- ERROR("curl_xml plugin: cx_config: "
- "Invalid key: %s",
- ci->key);
- cx_free(db);
+ db->xpath_list = llist_create();
+ if (db->xpath_list == NULL) {
+ ERROR("curl_xml plugin: list creation failed.");
+ sfree(db->instance);
+ sfree(db);
return -1;
}
+ db->timeout = -1;
+
+ int status = cf_util_get_string(ci, &db->url);
+ if (status != 0) {
+ llist_destroy(db->xpath_list);
+ sfree(db->instance);
+ sfree(db);
+ return status;
+ }
+
/* Fill the `cx_t' structure.. */
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
if (strcasecmp("Instance", child->key) == 0)
status = cf_util_get_string(child, &db->instance);
+ else if (strcasecmp("Plugin", child->key) == 0)
+ status = cf_util_get_string(child, &db->plugin_name);
else if (strcasecmp("Host", child->key) == 0)
status = cf_util_get_string(child, &db->host);
else if (strcasecmp("User", child->key) == 0)
break;
}
- if (status == 0) {
- if (db->list == NULL) {
- WARNING("curl_xml plugin: No (valid) `Key' block "
- "within `URL' block `%s'.",
- db->url);
- status = -1;
- }
- if (status == 0)
- status = cx_init_curl(db);
+ if (status != 0) {
+ cx_free(db);
+ return status;
}
- /* If all went well, register this database for reading */
- if (status == 0) {
- char *cb_name;
-
- if (db->instance == NULL)
- db->instance = strdup("default");
-
- DEBUG("curl_xml plugin: Registering new read callback: %s", db->instance);
-
- cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url);
+ if (llist_size(db->xpath_list) == 0) {
+ WARNING("curl_xml plugin: No `xpath' block within `URL' block `%s'.",
+ db->url);
+ cx_free(db);
+ return -1;
+ }
- plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read,
- /* interval = */ 0,
- &(user_data_t){
- .data = db, .free_func = cx_free,
- });
- sfree(cb_name);
- } else {
+ if (cx_init_curl(db) != 0) {
cx_free(db);
return -1;
}
+ /* If all went well, register this database for reading */
+ DEBUG("curl_xml plugin: Registering new read callback: %s", db->instance);
+
+ char *cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url);
+
+ plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read,
+ /* interval = */ 0,
+ &(user_data_t){
+ .data = db, .free_func = cx_free,
+ });
+ sfree(cb_name);
return 0;
} /* }}} int cx_config_add_url */
static int cx_config(oconfig_item_t *ci) /* {{{ */
{
- int success;
- int errors;
- int status;
-
- success = 0;
- errors = 0;
+ int success = 0;
+ int errors = 0;
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
if (strcasecmp("URL", child->key) == 0) {
- status = cx_config_add_url(child);
- if (status == 0)
+ if (cx_config_add_url(child) == 0)
success++;
else
errors++;
#define COLLECTD_LOCALE "C"
#endif
-/*
- * Global variables
- */
-char hostname_g[DATA_MAX_NAME_LEN];
-cdtime_t interval_g;
-int timeout_g;
-#if HAVE_LIBKSTAT
-kstat_ctl_t *kc;
-#endif /* HAVE_LIBKSTAT */
-
static int loop = 0;
static void *do_flush(void __attribute__((unused)) * arg) {
struct addrinfo *ai_list;
int status;
+ long hostname_len = sysconf(_SC_HOST_NAME_MAX);
+ if (hostname_len == -1) {
+ hostname_len = NI_MAXHOST;
+ }
+ char hostname[hostname_len];
+
str = global_option_get("Hostname");
if ((str != NULL) && (str[0] != 0)) {
- sstrncpy(hostname_g, str, sizeof(hostname_g));
+ hostname_set(str);
return 0;
}
- if (gethostname(hostname_g, sizeof(hostname_g)) != 0) {
+ if (gethostname(hostname, hostname_len) != 0) {
fprintf(stderr, "`gethostname' failed and no "
"hostname was configured.\n");
return -1;
struct addrinfo ai_hints = {.ai_flags = AI_CANONNAME};
- status = getaddrinfo(hostname_g, NULL, &ai_hints, &ai_list);
+ status = getaddrinfo(hostname, NULL, &ai_hints, &ai_list);
if (status != 0) {
ERROR("Looking up \"%s\" failed. You have set the "
"\"FQDNLookup\" option, but I cannot resolve "
"my hostname to a fully qualified domain "
"name. Please fix the network "
"configuration.",
- hostname_g);
+ hostname);
return -1;
}
if (ai_ptr->ai_canonname == NULL)
continue;
- sstrncpy(hostname_g, ai_ptr->ai_canonname, sizeof(hostname_g));
+ hostname_set(ai_ptr->ai_canonname);
break;
}
return 0;
} /* int init_global_variables */
-static int change_basedir(const char *orig_dir) {
+static int change_basedir(const char *orig_dir, _Bool create) {
char *dir;
size_t dirlen;
int status;
if (status == 0) {
free(dir);
return 0;
- } else if (errno != ENOENT) {
+ } else if (!create || (errno != ENOENT)) {
char errbuf[1024];
ERROR("change_basedir: chdir (%s): %s", dir,
sstrerror(errno, errbuf, sizeof(errbuf)));
#if COLLECT_DAEMON
" -f Don't fork to the background.\n"
#endif
+ " -B Don't create the BaseDir\n"
" -h Display help (this message)\n"
"\nBuiltin defaults:\n"
" Config file " CONFIGFILE "\n"
}
#endif /* KERNEL_LINUX */
-int main(int argc, char **argv) {
- const char *configfile = CONFIGFILE;
- int test_config = 0;
- int test_readall = 0;
- const char *basedir;
-#if COLLECT_DAEMON
- pid_t pid;
- int daemonize = 1;
-#endif
- int exit_status = 0;
+struct cmdline_config {
+ _Bool test_config;
+ _Bool test_readall;
+ _Bool create_basedir;
+ const char *configfile;
+ _Bool daemonize;
+};
+void read_cmdline(int argc, char **argv, struct cmdline_config *config) {
/* read options */
while (1) {
int c;
-
c = getopt(argc, argv, "htTC:"
#if COLLECT_DAEMON
"fP:"
break;
switch (c) {
+ case 'B':
+ config->create_basedir = 0;
+ break;
case 'C':
- configfile = optarg;
+ config->configfile = optarg;
break;
case 't':
- test_config = 1;
+ config->test_config = 1;
break;
case 'T':
- test_readall = 1;
+ config->test_readall = 1;
global_option_set("ReadThreads", "-1", 1);
#if COLLECT_DAEMON
- daemonize = 0;
+ config->daemonize = 0;
#endif /* COLLECT_DAEMON */
break;
#if COLLECT_DAEMON
global_option_set("PIDFile", optarg, 1);
break;
case 'f':
- daemonize = 0;
+ config->daemonize = 0;
break;
#endif /* COLLECT_DAEMON */
case 'h':
exit_usage(1);
} /* switch (c) */
} /* while (1) */
+}
- if (optind < argc)
- exit_usage(1);
-
- plugin_init_ctx();
-
+int configure_collectd(struct cmdline_config *config) {
+ const char *basedir;
/*
* Read options from the config file, the environment and the command
* line (in that order, with later options overwriting previous ones in
* general).
* Also, this will automatically load modules.
*/
- if (cf_read(configfile)) {
+ if (cf_read(config->configfile)) {
fprintf(stderr, "Error: Reading the config file failed!\n"
"Read the logs for details.\n");
return 1;
fprintf(stderr,
"Don't have a basedir to use. This should not happen. Ever.");
return 1;
- } else if (change_basedir(basedir)) {
+ } else if (change_basedir(basedir, config->create_basedir)) {
fprintf(stderr, "Error: Unable to change to directory `%s'.\n", basedir);
return 1;
}
/*
- * Set global variables or, if that failes, exit. We cannot run with
+ * Set global variables or, if that fails, exit. We cannot run with
* them being uninitialized. If nothing is configured, then defaults
* are being used. So this means that the user has actually done
* something wrong.
*/
if (init_global_variables() != 0)
- exit(EXIT_FAILURE);
+ return 1;
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+#if COLLECT_DAEMON
+ pid_t pid;
+#endif
+ int exit_status = 0;
- if (test_config)
+ struct cmdline_config config = {
+ .daemonize = 1, .create_basedir = 1, .configfile = CONFIGFILE,
+ };
+
+ read_cmdline(argc, argv, &config);
+
+ if (config.test_config)
return 0;
+ if (optind < argc)
+ exit_usage(1);
+
+ plugin_init_ctx();
+
+ int status;
+ if ((status = configure_collectd(&config)) != 0)
+ exit(EXIT_FAILURE);
+
#if COLLECT_DAEMON
/*
* fork off child
* Only daemonize if we're not being supervised
* by upstart or systemd (when using Linux).
*/
- if (daemonize
+ if (config.daemonize
#ifdef KERNEL_LINUX
&& notify_upstart() == 0 && notify_systemd() == 0
#endif
status);
return 1;
}
- } /* if (daemonize) */
+ } /* if (config.daemonize) */
#endif /* COLLECT_DAEMON */
struct sigaction sig_pipe_action = {.sa_handler = SIG_IGN};
exit_status = 1;
}
- if (test_readall) {
+ if (config.test_readall) {
if (plugin_read_all_once() != 0) {
ERROR("Error: one or more plugin read callbacks failed.");
exit_status = 1;
}
#if COLLECT_DAEMON
- if (daemonize)
+ if (config.daemonize)
pidfile_remove();
#endif /* COLLECT_DAEMON */
#include <sys/param.h>
#endif
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#endif
-
#ifndef PACKAGE_NAME
#define PACKAGE_NAME "collectd"
#endif
#define GAUGE_FORMAT "%.15g"
#endif
-/* Type for time as used by "utils_time.h" */
-typedef uint64_t cdtime_t;
-
-extern char hostname_g[];
-extern cdtime_t interval_g;
-extern int timeout_g;
+#include "globals.h"
#endif /* COLLECTD_H */
}
#endif
-ssize_t sread(int fd, void *buf, size_t count) {
+int sread(int fd, void *buf, size_t count) {
char *ptr;
size_t nleft;
ssize_t status;
return status;
if (status == 0) {
- DEBUG("Received EOF from fd %i. "
- "Closing fd and returning error.",
- fd);
- close(fd);
+ DEBUG("Received EOF from fd %i. ", fd);
return -1;
}
return 0;
}
-ssize_t swrite(int fd, const void *buf, size_t count) {
+int swrite(int fd, const void *buf, size_t count) {
const char *ptr;
size_t nleft;
ssize_t status;
if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
/* if recv returns zero (even though poll() said there is data to be
* read), that means the connection has been closed */
- return errno ? errno : -1;
+ errno = ECONNRESET;
+ return -1;
}
}
*
* DESCRIPTION
* Reads exactly `n' bytes or fails. Syntax and other behavior is analogous
- * to `read(2)'. If EOF is received the file descriptor is closed and an
- * error is returned.
+ * to `read(2)'.
*
* PARAMETERS
* `fd' File descriptor to write to.
* Zero upon success or non-zero if an error occurred. `errno' is set in this
* case.
*/
-ssize_t sread(int fd, void *buf, size_t count);
+int sread(int fd, void *buf, size_t count);
/*
* NAME
* Zero upon success or non-zero if an error occurred. `errno' is set in this
* case.
*/
-ssize_t swrite(int fd, const void *buf, size_t count);
+int swrite(int fd, const void *buf, size_t count);
/*
* NAME
return 0;
}
- temp =
- realloc(dst->children, sizeof(oconfig_item_t) *
- (dst->children_num + src->children_num - 1));
+ temp = realloc(dst->children,
+ sizeof(oconfig_item_t) *
+ (dst->children_num + src->children_num - 1));
if (temp == NULL) {
ERROR("configfile: realloc failed.");
return -1;
if ((src == NULL) || (src->children_num == 0))
return 0;
- temp = realloc(dst->children, sizeof(oconfig_item_t) *
- (dst->children_num + src->children_num));
+ temp =
+ realloc(dst->children,
+ sizeof(oconfig_item_t) * (dst->children_num + src->children_num));
if (temp == NULL) {
ERROR("configfile: realloc failed.");
return -1;
return NULL;
}
- return (cf_global_options[i].value != NULL) ? cf_global_options[i].value : cf_global_options[i].def;
+ return (cf_global_options[i].value != NULL) ? cf_global_options[i].value
+ : cf_global_options[i].def;
} /* char *global_option_get */
long global_option_get_long(const char *option, long default_value) {
}
return ret;
+
} /* int cf_read */
/* Assures the config option is a string, duplicates it and returns the copy in
--- /dev/null
+/**
+ * collectd - src/globals.c
+ * Copyright (C) 2017 Google LLC
+ *
+ * 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.
+ **/
+
+#include "common.h"
+#include "globals.h"
+
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#endif
+
+/*
+ * Global variables
+ */
+char *hostname_g;
+cdtime_t interval_g;
+int timeout_g;
+#if HAVE_KSTAT_H
+kstat_ctl_t *kc;
+#endif
+
+void hostname_set(char const *hostname) {
+ char *h = strdup(hostname);
+ if (h == NULL)
+ return;
+
+ sfree(hostname_g);
+ hostname_g = h;
+}
--- /dev/null
+/**
+ * collectd - src/globals.h
+ * Copyright (C) 2017 Google LLC
+ *
+ * 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.
+ **/
+
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+#include <inttypes.h>
+
+#ifndef DATA_MAX_NAME_LEN
+#define DATA_MAX_NAME_LEN 128
+#endif
+
+/* Type for time as used by "utils_time.h" */
+typedef uint64_t cdtime_t;
+
+/* hostname_set updates hostname_g */
+void hostname_set(char const *hostname);
+
+extern char *hostname_g;
+extern cdtime_t interval_g;
+extern int pidfile_from_cli;
+extern int timeout_g;
+#endif /* GLOBALS_H */
return 0;
} /* }}} int plugin_update_internal_statistics */
-static void destroy_callback(callback_func_t *cf) /* {{{ */
+static void free_userdata(user_data_t const *ud) /* {{{ */
{
- if (cf == NULL)
+ if (ud == NULL)
return;
- if ((cf->cf_udata.data != NULL) && (cf->cf_udata.free_func != NULL)) {
- cf->cf_udata.free_func(cf->cf_udata.data);
- cf->cf_udata.data = NULL;
- cf->cf_udata.free_func = NULL;
+ if ((ud->data != NULL) && (ud->free_func != NULL)) {
+ ud->free_func(ud->data);
}
+} /* }}} void free_userdata */
+
+static void destroy_callback(callback_func_t *cf) /* {{{ */
+{
+ if (cf == NULL)
+ return;
+ free_userdata(&cf->cf_udata);
sfree(cf);
} /* }}} void destroy_callback */
cf = calloc(1, sizeof(*cf));
if (cf == NULL) {
+ free_userdata(ud);
ERROR("plugin: create_register_callback: calloc failed.");
return -1;
}
if (le != NULL) {
pthread_mutex_unlock(&read_lock);
WARNING("The read function \"%s\" is already registered. "
- "Check for duplicate \"LoadPlugin\" lines "
- "in your configuration!",
+ "Check for duplicates in your configuration!",
rf->rf_name);
return EINVAL;
}
rf = calloc(1, sizeof(*rf));
if (rf == NULL) {
+ free_userdata(user_data);
ERROR("plugin_register_complex_read: calloc failed.");
return ENOMEM;
}
status = plugin_insert_read(rf);
if (status != 0) {
+ free_userdata(&rf->rf_udata);
sfree(rf->rf_name);
sfree(rf);
}
});
sfree(flush_name);
- if (status != 0) {
- sfree(cb->name);
- sfree(cb);
- return status;
- }
+ return status;
}
return 0;
#include <pthread.h>
-#ifndef DATA_MAX_NAME_LEN
-#define DATA_MAX_NAME_LEN 128
-#endif
-
#define DS_TYPE_COUNTER 0
#define DS_TYPE_GAUGE 1
#define DS_TYPE_DERIVE 2
#define DS_TYPE_ABSOLUTE 3
#define DS_TYPE_TO_STRING(t) \
- (t == DS_TYPE_COUNTER) ? "counter" : (t == DS_TYPE_GAUGE) \
- ? "gauge" \
- : (t == DS_TYPE_DERIVE) \
- ? "derive" \
- : (t == DS_TYPE_ABSOLUTE) \
- ? "absolute" \
- : "unknown"
+ (t == DS_TYPE_COUNTER) \
+ ? "counter" \
+ : (t == DS_TYPE_GAUGE) \
+ ? "gauge" \
+ : (t == DS_TYPE_DERIVE) \
+ ? "derive" \
+ : (t == DS_TYPE_ABSOLUTE) ? "absolute" : "unknown"
#ifndef LOG_ERR
#define LOG_ERR 3
kstat_ctl_t *kc = NULL;
#endif /* HAVE_LIBKSTAT */
-char hostname_g[] = "example.com";
+char *hostname_g = "example.com";
void plugin_set_dir(const char *dir) { /* nop */
}
return 0;
} /* int uc_iterator_get_name */
+int uc_iterator_get_meta(uc_iter_t *iter, meta_data_t **ret_meta) {
+ if ((iter == NULL) || (iter->entry == NULL) || (ret_meta == NULL))
+ return -1;
+
+ *ret_meta = meta_data_clone(iter->entry->meta);
+
+ return 0;
+} /* int uc_iterator_get_meta */
+
/*
* Meta data interface
*/
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);
+/* Return the metadata for the value at the current position. */
+int uc_iterator_get_meta(uc_iter_t *iter, meta_data_t **ret_meta);
/*
* Meta data interface
{
char *name;
char *select_db;
+ char *plugin_name;
cdtime_t interval;
return;
sfree(db->name);
+ sfree(db->select_db);
+ sfree(db->plugin_name);
sfree(db->driver);
+ sfree(db->host);
for (size_t i = 0; i < db->driver_options_num; i++) {
sfree(db->driver_options[i].key);
if (db->q_prep_areas)
for (size_t i = 0; i < db->queries_num; ++i)
udb_query_delete_preparation_area(db->q_prep_areas[i]);
- free(db->q_prep_areas);
+ sfree(db->q_prep_areas);
+ /* N.B.: db->queries references objects "owned" by the global queries
+ * variable. Free the array here, but not the content. */
+ sfree(db->queries);
sfree(db);
} /* }}} void cdbi_database_free */
status = cf_util_get_string(child, &db->host);
else if (strcasecmp("Interval", child->key) == 0)
status = cf_util_get_cdtime(child, &db->interval);
+ else if (strcasecmp("Plugin", child->key) == 0)
+ status = cf_util_get_string(child, &db->plugin_name);
else {
WARNING("dbi plugin: Option `%s' not allowed here.", child->key);
status = -1;
udb_query_prepare_result(
q, prep_area, (db->host ? db->host : hostname_g),
- /* plugin = */ "dbi", db->name, column_names, column_num,
+ /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "dbi",
+ db->name, column_names, column_num,
/* interval = */ (db->interval > 0) ? db->interval : 0);
/* 0 = error; 1 = success; */
.plugin = DPDK_EVENTS_PLUGIN,
.type = "gauge",
.meta = NULL};
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
plugin_dispatch_values(&vl);
vl.values = &(value_t){.derive = value};
vl.values_len = 1;
vl.time = port_read_time;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, DPDK_STATS_PLUGIN, sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
dpdk_stats_resolve_cnt_type(vl.type, sizeof(vl.type), cnt_name);
{
struct group sg;
struct group *grp;
- char grbuf[4096];
int status;
+ long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (grbuf_size <= 0)
+ grbuf_size = sysconf(_SC_PAGESIZE);
+ if (grbuf_size <= 0)
+ grbuf_size = 4096;
+ char grbuf[grbuf_size];
+
grp = NULL;
status = getgrnam_r(group, &sg, grbuf, sizeof(grbuf), &grp);
if (status != 0) {
struct passwd *sp_ptr;
struct passwd sp;
- char nambuf[4096];
if (pl->pid != 0)
return -1;
+ long int nambuf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (nambuf_size <= 0)
+ nambuf_size = sysconf(_SC_PAGESIZE);
+ if (nambuf_size <= 0)
+ nambuf_size = 4096;
+ char nambuf[nambuf_size];
+
if ((create_pipe(fd_pipe_in) == -1) || (create_pipe(fd_pipe_out) == -1) ||
(create_pipe(fd_pipe_err) == -1))
goto failed;
struct group *gr_ptr = NULL;
struct group gr;
- status = getgrnam_r(pl->group, &gr, nambuf, sizeof(nambuf), &gr_ptr);
+ long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (grbuf_size <= 0)
+ grbuf_size = sysconf(_SC_PAGESIZE);
+ if (grbuf_size <= 0)
+ grbuf_size = 4096;
+ char grbuf[grbuf_size];
+
+ status = getgrnam_r(pl->group, &gr, grbuf, sizeof(grbuf), &gr_ptr);
if (status != 0) {
ERROR("exec plugin: Failed to get group information "
"for group ``%s'': %s",
#define FC_RECURSIVE 1
#define FC_HIDDEN 2
+#define FC_REGULAR 4
struct fc_directory_conf_s {
char *path;
+ char *plugin_name;
char *instance;
+ char *files_size_type;
+ char *files_num_type;
+ char *type_instance;
int options;
static fc_directory_conf_t **directories = NULL;
static size_t directories_num = 0;
+void fc_free_dir(fc_directory_conf_t *dir) {
+ sfree(dir->path);
+ sfree(dir->plugin_name);
+ sfree(dir->instance);
+ sfree(dir->files_size_type);
+ sfree(dir->files_num_type);
+ sfree(dir->type_instance);
+ sfree(dir->name);
+
+ sfree(dir);
+} /* void fc_free_dir */
+
static void fc_submit_dir(const fc_directory_conf_t *dir) {
value_list_t vl = VALUE_LIST_INIT;
- vl.values = &(value_t){.gauge = (gauge_t)dir->files_num};
- vl.values_len = 1;
- sstrncpy(vl.plugin, "filecount", sizeof(vl.plugin));
- sstrncpy(vl.plugin_instance, dir->instance, sizeof(vl.plugin_instance));
- sstrncpy(vl.type, "files", sizeof(vl.type));
+ sstrncpy(vl.plugin, dir->plugin_name, sizeof(vl.plugin));
+ if (dir->instance != NULL)
+ sstrncpy(vl.plugin_instance, dir->instance, sizeof(vl.plugin_instance));
+ if (dir->type_instance != NULL)
+ sstrncpy(vl.type_instance, dir->type_instance, sizeof(vl.type_instance));
- plugin_dispatch_values(&vl);
+ vl.values_len = 1;
- vl.values = &(value_t){.gauge = (gauge_t)dir->files_size};
- sstrncpy(vl.type, "bytes", sizeof(vl.type));
+ if (dir->files_num_type != NULL) {
+ vl.values = &(value_t){.gauge = (gauge_t)dir->files_num};
+ sstrncpy(vl.type, dir->files_num_type, sizeof(vl.type));
+ plugin_dispatch_values(&vl);
+ }
- plugin_dispatch_values(&vl);
+ if (dir->files_size_type != NULL) {
+ vl.values = &(value_t){.gauge = (gauge_t)dir->files_size};
+ sstrncpy(vl.type, dir->files_size_type, sizeof(vl.type));
+ plugin_dispatch_values(&vl);
+ }
} /* void fc_submit_dir */
/*
* Config:
* <Plugin filecount>
* <Directory /path/to/dir>
+ * Plugin "foo"
* Instance "foobar"
* Name "*.conf"
* MTime -3600
* Size "+10M"
+ * Recursive true
+ * IncludeHidden false
+ * FilesSizeType "bytes"
+ * FilesCountType "files"
+ * TypeInstance "instance"
* </Directory>
* </Plugin>
*
static int fc_config_set_instance(fc_directory_conf_t *dir, const char *str) {
char buffer[1024];
char *ptr;
- char *copy;
sstrncpy(buffer, str, sizeof(buffer));
for (ptr = buffer; *ptr != 0; ptr++)
for (ptr = buffer; *ptr == '_'; ptr++)
/* do nothing */;
- if (*ptr == 0)
- return -1;
-
- copy = strdup(ptr);
+ char *copy = strdup(ptr);
if (copy == NULL)
return -1;
static int fc_config_add_dir_name(fc_directory_conf_t *dir,
oconfig_item_t *ci) {
- char *temp;
-
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
WARNING("filecount plugin: The `Name' config option needs exactly one "
"string argument.");
return -1;
}
- temp = strdup(ci->values[0].value.string);
+ char *temp = strdup(ci->values[0].value.string);
if (temp == NULL) {
ERROR("filecount plugin: strdup failed.");
return -1;
static int fc_config_add_dir_mtime(fc_directory_conf_t *dir,
oconfig_item_t *ci) {
- char *endptr;
- double temp;
-
if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_STRING) &&
(ci->values[0].type != OCONFIG_TYPE_NUMBER))) {
WARNING("filecount plugin: The `MTime' config option needs exactly one "
}
errno = 0;
- endptr = NULL;
- temp = strtod(ci->values[0].value.string, &endptr);
+ char *endptr = NULL;
+ double temp = strtod(ci->values[0].value.string, &endptr);
if ((errno != 0) || (endptr == NULL) ||
(endptr == ci->values[0].value.string)) {
WARNING("filecount plugin: Converting `%s' to a number failed.",
static int fc_config_add_dir_size(fc_directory_conf_t *dir,
oconfig_item_t *ci) {
- char *endptr;
- double temp;
-
if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_STRING) &&
(ci->values[0].type != OCONFIG_TYPE_NUMBER))) {
WARNING("filecount plugin: The `Size' config option needs exactly one "
}
errno = 0;
- endptr = NULL;
- temp = strtod(ci->values[0].value.string, &endptr);
+ char *endptr = NULL;
+ double temp = strtod(ci->values[0].value.string, &endptr);
if ((errno != 0) || (endptr == NULL) ||
(endptr == ci->values[0].value.string)) {
WARNING("filecount plugin: Converting `%s' to a number failed.",
} /* int fc_config_add_dir_option */
static int fc_config_add_dir(oconfig_item_t *ci) {
- fc_directory_conf_t *dir;
- int status;
-
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
WARNING("filecount plugin: `Directory' needs exactly one string "
"argument.");
}
/* Initialize `dir' */
- dir = calloc(1, sizeof(*dir));
+ fc_directory_conf_t *dir = calloc(1, sizeof(*dir));
if (dir == NULL) {
ERROR("filecount plugin: calloc failed.");
return -1;
dir->path = strdup(ci->values[0].value.string);
if (dir->path == NULL) {
ERROR("filecount plugin: strdup failed.");
- sfree(dir);
+ fc_free_dir(dir);
return -1;
}
- fc_config_set_instance(dir, dir->path);
-
- dir->options = FC_RECURSIVE;
+ dir->options = FC_RECURSIVE | FC_REGULAR;
dir->name = NULL;
+ dir->plugin_name = strdup("filecount");
+ dir->instance = NULL;
+ dir->type_instance = NULL;
dir->mtime = 0;
dir->size = 0;
- status = 0;
+ dir->files_size_type = strdup("bytes");
+ dir->files_num_type = strdup("files");
+
+ if (dir->plugin_name == NULL || dir->files_size_type == NULL ||
+ dir->files_num_type == NULL) {
+ ERROR("filecount plugin: strdup failed.");
+ fc_free_dir(dir);
+ return -1;
+ }
+
+ int status = 0;
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *option = ci->children + i;
- if (strcasecmp("Instance", option->key) == 0)
+ if (strcasecmp("Plugin", option->key) == 0)
+ status = cf_util_get_string(option, &dir->plugin_name);
+ else if (strcasecmp("Instance", option->key) == 0)
status = fc_config_add_dir_instance(dir, option);
else if (strcasecmp("Name", option->key) == 0)
status = fc_config_add_dir_name(dir, option);
status = fc_config_add_dir_option(dir, option, FC_RECURSIVE);
else if (strcasecmp("IncludeHidden", option->key) == 0)
status = fc_config_add_dir_option(dir, option, FC_HIDDEN);
+ else if (strcasecmp("RegularOnly", option->key) == 0)
+ status = fc_config_add_dir_option(dir, option, FC_REGULAR);
+ else if (strcasecmp("FilesSizeType", option->key) == 0)
+ status = cf_util_get_string(option, &dir->files_size_type);
+ else if (strcasecmp("FilesCountType", option->key) == 0)
+ status = cf_util_get_string(option, &dir->files_num_type);
+ else if (strcasecmp("TypeInstance", option->key) == 0)
+ status = cf_util_get_string(option, &dir->type_instance);
else {
WARNING("filecount plugin: fc_config_add_dir: "
"Option `%s' not allowed here.",
break;
} /* for (ci->children) */
- if (status == 0) {
- fc_directory_conf_t **temp;
+ if (status != 0) {
+ fc_free_dir(dir);
+ return -1;
+ }
- temp = realloc(directories, sizeof(*directories) * (directories_num + 1));
- if (temp == NULL) {
- ERROR("filecount plugin: realloc failed.");
- status = -1;
- } else {
- directories = temp;
- directories[directories_num] = dir;
- directories_num++;
+ /* Set default plugin instance */
+ if (dir->instance == NULL) {
+ fc_config_set_instance(dir, dir->path);
+ if (dir->instance == NULL || strlen(dir->instance) == 0) {
+ ERROR("filecount plugin: failed to build plugin instance name.");
+ fc_free_dir(dir);
+ return -1;
}
}
- if (status != 0) {
- sfree(dir->name);
+ /* Handle disabled types */
+ if (strlen(dir->instance) == 0)
sfree(dir->instance);
- sfree(dir->path);
- sfree(dir);
+
+ if (strlen(dir->files_size_type) == 0)
+ sfree(dir->files_size_type);
+
+ if (strlen(dir->files_num_type) == 0)
+ sfree(dir->files_num_type);
+
+ if (dir->files_size_type == NULL && dir->files_num_type == NULL) {
+ WARNING("filecount plugin: Both `FilesSizeType' and `FilesCountType ' "
+ "are disabled for '%s'. There's no types to report.",
+ dir->path);
+ fc_free_dir(dir);
return -1;
}
+ /* Ready to add it to list */
+ fc_directory_conf_t **temp =
+ realloc(directories, sizeof(*directories) * (directories_num + 1));
+ if (temp == NULL) {
+ ERROR("filecount plugin: realloc failed.");
+ fc_free_dir(dir);
+ return -1;
+ }
+
+ directories = temp;
+ directories[directories_num] = dir;
+ directories_num++;
+
return 0;
} /* int fc_config_add_dir */
fc_directory_conf_t *dir = user_data;
char abs_path[PATH_MAX];
struct stat statbuf;
- int status;
if (dir == NULL)
return -1;
snprintf(abs_path, sizeof(abs_path), "%s/%s", dirname, filename);
- status = lstat(abs_path, &statbuf);
+ int status = lstat(abs_path, &statbuf);
if (status != 0) {
ERROR("filecount plugin: stat (%s) failed.", abs_path);
return -1;
abs_path, fc_read_dir_callback, dir,
/* include hidden = */ (dir->options & FC_HIDDEN) ? 1 : 0);
return status;
- } else if (!S_ISREG(statbuf.st_mode)) {
+ } else if ((dir->options & FC_REGULAR) && !S_ISREG(statbuf.st_mode)) {
return 0;
}
return 0;
}
+ if (!S_ISREG(statbuf.st_mode)) {
+ dir->files_num++;
+ return 0;
+ }
+
if (dir->mtime != 0) {
time_t mtime = dir->now;
} /* int fc_read_dir_callback */
static int fc_read_dir(fc_directory_conf_t *dir) {
- int status;
-
dir->files_num = 0;
dir->files_size = 0;
if (dir->mtime != 0)
dir->now = time(NULL);
- status =
+ int status =
walk_directory(dir->path, fc_read_dir_callback, dir,
/* include hidden */ (dir->options & FC_HIDDEN) ? 1 : 0);
if (status != 0) {
using google::protobuf::util::TimeUtil;
+typedef google::protobuf::Map<grpc::string, collectd::types::MetadataValue> grpcMetadata;
+
/*
* private types
*/
return grpc::Status::OK;
} /* unmarshal_ident() */
+static grpc::Status marshal_meta_data(meta_data_t *meta,
+ grpcMetadata *mutable_meta_data) {
+ char **meta_data_keys = nullptr;
+ int meta_data_keys_len = meta_data_toc(meta, &meta_data_keys);
+ if (meta_data_keys_len < 0) {
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ grpc::string("error getting metadata keys"));
+ }
+
+ for (int i = 0; i < meta_data_keys_len; i++) {
+ char *key = meta_data_keys[i];
+ int md_type = meta_data_type(meta, key);
+
+ collectd::types::MetadataValue md_value;
+ md_value.Clear();
+
+ switch (md_type) {
+ case MD_TYPE_STRING:
+ char *md_string;
+ if (meta_data_get_string(meta, key, &md_string) != 0 || md_string == nullptr) {
+ strarray_free(meta_data_keys, meta_data_keys_len);
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ grpc::string("missing metadata"));
+ }
+ md_value.set_string_value(md_string);
+ free(md_string);
+ break;
+ case MD_TYPE_SIGNED_INT:
+ int64_t int64_value;
+ if (meta_data_get_signed_int(meta, key, &int64_value) != 0) {
+ strarray_free(meta_data_keys, meta_data_keys_len);
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ grpc::string("missing metadata"));
+ }
+ md_value.set_int64_value(int64_value);
+ break;
+ case MD_TYPE_UNSIGNED_INT:
+ uint64_t uint64_value;
+ if (meta_data_get_unsigned_int(meta, key, &uint64_value) != 0) {
+ strarray_free(meta_data_keys, meta_data_keys_len);
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ grpc::string("missing metadata"));
+ }
+ md_value.set_uint64_value(uint64_value);
+ break;
+ case MD_TYPE_DOUBLE:
+ double double_value;
+ if (meta_data_get_double(meta, key, &double_value) != 0) {
+ strarray_free(meta_data_keys, meta_data_keys_len);
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ grpc::string("missing metadata"));
+ }
+ md_value.set_double_value(double_value);
+ break;
+ case MD_TYPE_BOOLEAN:
+ bool bool_value;
+ if (meta_data_get_boolean(meta, key, &bool_value) != 0) {
+ strarray_free(meta_data_keys, meta_data_keys_len);
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ grpc::string("missing metadata"));
+ }
+ md_value.set_bool_value(bool_value);
+ break;
+ default:
+ strarray_free(meta_data_keys, meta_data_keys_len);
+ ERROR("grpc: invalid metadata type (%d)", md_type);
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ grpc::string("unknown metadata type"));
+ }
+
+ (*mutable_meta_data)[grpc::string(key)] = md_value;
+
+ strarray_free(meta_data_keys, meta_data_keys_len);
+ }
+
+ return grpc::Status::OK;
+}
+
+static grpc::Status unmarshal_meta_data(const grpcMetadata &rpc_metadata,
+ meta_data_t **md_out) {
+ *md_out = meta_data_create();
+ if (*md_out == nullptr) {
+ return grpc::Status(grpc::StatusCode::RESOURCE_EXHAUSTED,
+ grpc::string("failed to metadata list"));
+ }
+ for (auto kv: rpc_metadata) {
+ auto k = kv.first.c_str();
+ auto v = kv.second;
+
+ // The meta_data collection individually allocates copies of the keys and
+ // string values for each entry, so it's safe for us to pass a reference
+ // to our short-lived strings.
+
+ switch (v.value_case()) {
+ case collectd::types::MetadataValue::ValueCase::kStringValue:
+ meta_data_add_string(*md_out, k, v.string_value().c_str());
+ break;
+ case collectd::types::MetadataValue::ValueCase::kInt64Value:
+ meta_data_add_signed_int(*md_out, k, v.int64_value());
+ break;
+ case collectd::types::MetadataValue::ValueCase::kUint64Value:
+ meta_data_add_unsigned_int(*md_out, k, v.uint64_value());
+ break;
+ case collectd::types::MetadataValue::ValueCase::kDoubleValue:
+ meta_data_add_double(*md_out, k, v.double_value());
+ break;
+ case collectd::types::MetadataValue::ValueCase::kBoolValue:
+ meta_data_add_boolean(*md_out, k, v.bool_value());
+ break;
+ default:
+ meta_data_destroy(*md_out);
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ grpc::string("Metadata of unknown type"));
+ }
+ }
+ return grpc::Status::OK;
+}
+
static grpc::Status marshal_value_list(const value_list_t *vl,
collectd::types::ValueList *msg) {
auto id = msg->mutable_identifier();
msg->set_allocated_time(new google::protobuf::Timestamp(t));
msg->set_allocated_interval(new google::protobuf::Duration(d));
+ msg->clear_meta_data();
+ if (vl->meta != nullptr) {
+ grpc::Status status = marshal_meta_data(vl->meta, msg->mutable_meta_data());
+ if (!status.ok()) {
+ return status;
+ }
+ }
+
for (size_t i = 0; i < vl->values_len; ++i) {
auto v = msg->add_values();
- switch (ds->ds[i].type) {
+ int value_type = ds->ds[i].type;
+ switch (value_type) {
case DS_TYPE_COUNTER:
v->set_counter(vl->values[i].counter);
break;
v->set_absolute(vl->values[i].absolute);
break;
default:
+ ERROR("grpc: invalid value type (%d)", value_type);
return grpc::Status(grpc::StatusCode::INTERNAL,
grpc::string("unknown value type"));
}
if (!status.ok())
return status;
+ status = unmarshal_meta_data(msg.meta_data(), &vl->meta);
+ if (!status.ok())
+ return status;
+
value_t *values = NULL;
size_t values_len = 0;
if (status.ok()) {
vl->values = values;
vl->values_len = values_len;
- } else if (values) {
+ } else {
+ meta_data_destroy(vl->meta);
free(values);
}
auto vl = value_lists.front();
value_lists.pop();
sfree(vl.values);
+ meta_data_destroy(vl.meta);
}
return status;
if (!ident_matches(&vl, match))
continue;
-
if (uc_iterator_get_time(iter, &vl.time) < 0) {
status =
grpc::Status(grpc::StatusCode::INTERNAL,
grpc::string("failed to retrieve values"));
break;
}
+ if (uc_iterator_get_meta(iter, &vl.meta) < 0) {
+ status = grpc::Status(grpc::StatusCode::INTERNAL,
+ grpc::string("failed to retrieve value metadata"));
+ }
value_lists->push(vl);
} // while (uc_iterator_next(iter, &name) == 0)
--- /dev/null
+/**
+ * collectd - src/intel_pmu.c
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * 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:
+ * Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+
+#include <jevents.h>
+#include <jsession.h>
+
+#define PMU_PLUGIN "intel_pmu"
+
+#define HW_CACHE_READ_ACCESS \
+ (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
+
+#define HW_CACHE_WRITE_ACCESS \
+ (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
+
+#define HW_CACHE_PREFETCH_ACCESS \
+ (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_ACCESS) << 16))
+
+#define HW_CACHE_READ_MISS \
+ (((PERF_COUNT_HW_CACHE_OP_READ) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
+
+#define HW_CACHE_WRITE_MISS \
+ (((PERF_COUNT_HW_CACHE_OP_WRITE) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
+
+#define HW_CACHE_PREFETCH_MISS \
+ (((PERF_COUNT_HW_CACHE_OP_PREFETCH) << 8) | \
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))
+
+struct event_info {
+ char *name;
+ uint64_t config;
+};
+typedef struct event_info event_info_t;
+
+struct intel_pmu_ctx_s {
+ _Bool hw_cache_events;
+ _Bool kernel_pmu_events;
+ _Bool sw_events;
+ char event_list_fn[PATH_MAX];
+ char **hw_events;
+ size_t hw_events_count;
+ struct eventlist *event_list;
+};
+typedef struct intel_pmu_ctx_s intel_pmu_ctx_t;
+
+event_info_t g_kernel_pmu_events[] = {
+ {.name = "cpu-cycles", .config = PERF_COUNT_HW_CPU_CYCLES},
+ {.name = "instructions", .config = PERF_COUNT_HW_INSTRUCTIONS},
+ {.name = "cache-references", .config = PERF_COUNT_HW_CACHE_REFERENCES},
+ {.name = "cache-misses", .config = PERF_COUNT_HW_CACHE_MISSES},
+ {.name = "branches", .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS},
+ {.name = "branch-misses", .config = PERF_COUNT_HW_BRANCH_MISSES},
+ {.name = "bus-cycles", .config = PERF_COUNT_HW_BUS_CYCLES},
+};
+
+event_info_t g_hw_cache_events[] = {
+
+ {.name = "L1-dcache-loads",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_ACCESS)},
+ {.name = "L1-dcache-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_READ_MISS)},
+ {.name = "L1-dcache-stores",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_ACCESS)},
+ {.name = "L1-dcache-store-misses",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_WRITE_MISS)},
+ {.name = "L1-dcache-prefetches",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_ACCESS)},
+ {.name = "L1-dcache-prefetch-misses",
+ .config = (PERF_COUNT_HW_CACHE_L1D | HW_CACHE_PREFETCH_MISS)},
+
+ {.name = "L1-icache-loads",
+ .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_ACCESS)},
+ {.name = "L1-icache-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_READ_MISS)},
+ {.name = "L1-icache-prefetches",
+ .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_ACCESS)},
+ {.name = "L1-icache-prefetch-misses",
+ .config = (PERF_COUNT_HW_CACHE_L1I | HW_CACHE_PREFETCH_MISS)},
+
+ {.name = "LLC-loads",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_ACCESS)},
+ {.name = "LLC-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_READ_MISS)},
+ {.name = "LLC-stores",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_ACCESS)},
+ {.name = "LLC-store-misses",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_WRITE_MISS)},
+ {.name = "LLC-prefetches",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_ACCESS)},
+ {.name = "LLC-prefetch-misses",
+ .config = (PERF_COUNT_HW_CACHE_LL | HW_CACHE_PREFETCH_MISS)},
+
+ {.name = "dTLB-loads",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_ACCESS)},
+ {.name = "dTLB-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_READ_MISS)},
+ {.name = "dTLB-stores",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_ACCESS)},
+ {.name = "dTLB-store-misses",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_WRITE_MISS)},
+ {.name = "dTLB-prefetches",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_ACCESS)},
+ {.name = "dTLB-prefetch-misses",
+ .config = (PERF_COUNT_HW_CACHE_DTLB | HW_CACHE_PREFETCH_MISS)},
+
+ {.name = "iTLB-loads",
+ .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_ACCESS)},
+ {.name = "iTLB-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_ITLB | HW_CACHE_READ_MISS)},
+
+ {.name = "branch-loads",
+ .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_ACCESS)},
+ {.name = "branch-load-misses",
+ .config = (PERF_COUNT_HW_CACHE_BPU | HW_CACHE_READ_MISS)},
+};
+
+event_info_t g_sw_events[] = {
+ {.name = "cpu-clock", .config = PERF_COUNT_SW_CPU_CLOCK},
+
+ {.name = "task-clock", .config = PERF_COUNT_SW_TASK_CLOCK},
+
+ {.name = "context-switches", .config = PERF_COUNT_SW_CONTEXT_SWITCHES},
+
+ {.name = "cpu-migrations", .config = PERF_COUNT_SW_CPU_MIGRATIONS},
+
+ {.name = "page-faults", .config = PERF_COUNT_SW_PAGE_FAULTS},
+
+ {.name = "minor-faults", .config = PERF_COUNT_SW_PAGE_FAULTS_MIN},
+
+ {.name = "major-faults", .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ},
+
+ {.name = "alignment-faults", .config = PERF_COUNT_SW_ALIGNMENT_FAULTS},
+
+ {.name = "emulation-faults", .config = PERF_COUNT_SW_EMULATION_FAULTS},
+};
+
+static intel_pmu_ctx_t g_ctx;
+
+#if COLLECT_DEBUG
+static void pmu_dump_events() {
+
+ DEBUG(PMU_PLUGIN ": Events:");
+
+ struct event *e;
+
+ for (e = g_ctx.event_list->eventlist; e; e = e->next) {
+ DEBUG(PMU_PLUGIN ": event : %s", e->event);
+ DEBUG(PMU_PLUGIN ": group_lead: %d", e->group_leader);
+ DEBUG(PMU_PLUGIN ": end_group : %d", e->end_group);
+ DEBUG(PMU_PLUGIN ": type : %#x", e->attr.type);
+ DEBUG(PMU_PLUGIN ": config : %#x", (unsigned)e->attr.config);
+ DEBUG(PMU_PLUGIN ": size : %d", e->attr.size);
+ }
+}
+
+static void pmu_dump_config(void) {
+
+ DEBUG(PMU_PLUGIN ": Config:");
+ DEBUG(PMU_PLUGIN ": hw_cache_events : %d", g_ctx.hw_cache_events);
+ DEBUG(PMU_PLUGIN ": kernel_pmu_events : %d", g_ctx.kernel_pmu_events);
+ DEBUG(PMU_PLUGIN ": software_events : %d", g_ctx.sw_events);
+
+ for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
+ DEBUG(PMU_PLUGIN ": hardware_events[%zu]: %s", i, g_ctx.hw_events[i]);
+ }
+}
+
+#endif /* COLLECT_DEBUG */
+
+static int pmu_config_hw_events(oconfig_item_t *ci) {
+
+ if (strcasecmp("HardwareEvents", ci->key) != 0) {
+ return -EINVAL;
+ }
+
+ g_ctx.hw_events = calloc(ci->values_num, sizeof(char *));
+ if (g_ctx.hw_events == NULL) {
+ ERROR(PMU_PLUGIN ": Failed to allocate hw events.");
+ return -ENOMEM;
+ }
+
+ for (int i = 0; i < ci->values_num; i++) {
+ if (ci->values[i].type != OCONFIG_TYPE_STRING) {
+ WARNING(PMU_PLUGIN ": The %s option requires string arguments.", ci->key);
+ continue;
+ }
+
+ g_ctx.hw_events[g_ctx.hw_events_count] = strdup(ci->values[i].value.string);
+ if (g_ctx.hw_events[g_ctx.hw_events_count] == NULL) {
+ ERROR(PMU_PLUGIN ": Failed to allocate hw events entry.");
+ return -ENOMEM;
+ }
+
+ g_ctx.hw_events_count++;
+ }
+
+ return 0;
+}
+
+static int pmu_config(oconfig_item_t *ci) {
+
+ DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
+
+ for (int i = 0; i < ci->children_num; i++) {
+ int ret = 0;
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("ReportHardwareCacheEvents", child->key) == 0) {
+ ret = cf_util_get_boolean(child, &g_ctx.hw_cache_events);
+ } else if (strcasecmp("ReportKernelPMUEvents", child->key) == 0) {
+ ret = cf_util_get_boolean(child, &g_ctx.kernel_pmu_events);
+ } else if (strcasecmp("EventList", child->key) == 0) {
+ ret = cf_util_get_string_buffer(child, g_ctx.event_list_fn,
+ sizeof(g_ctx.event_list_fn));
+ } else if (strcasecmp("HardwareEvents", child->key) == 0) {
+ ret = pmu_config_hw_events(child);
+ } else if (strcasecmp("ReportSoftwareEvents", child->key) == 0) {
+ ret = cf_util_get_boolean(child, &g_ctx.sw_events);
+ } else {
+ ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
+ ret = -1;
+ }
+
+ if (ret != 0) {
+ DEBUG(PMU_PLUGIN ": %s:%d ret=%d", __FUNCTION__, __LINE__, ret);
+ return ret;
+ }
+ }
+
+#if COLLECT_DEBUG
+ pmu_dump_config();
+#endif
+
+ return 0;
+}
+
+static void pmu_submit_counter(int cpu, char *event, counter_t value,
+ meta_data_t *meta) {
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = &(value_t){.counter = value};
+ vl.values_len = 1;
+
+ sstrncpy(vl.plugin, PMU_PLUGIN, sizeof(vl.plugin));
+ if (cpu == -1) {
+ snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all");
+ } else {
+ vl.meta = meta;
+ snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%d", cpu);
+ }
+ sstrncpy(vl.type, "counter", sizeof(vl.type));
+ sstrncpy(vl.type_instance, event, sizeof(vl.type_instance));
+
+ plugin_dispatch_values(&vl);
+}
+
+meta_data_t *pmu_meta_data_create(const struct efd *efd) {
+ meta_data_t *meta = NULL;
+
+ /* create meta data only if value was scaled */
+ if (efd->val[1] == efd->val[2] || !efd->val[2]) {
+ return NULL;
+ }
+
+ meta = meta_data_create();
+ if (meta == NULL) {
+ ERROR(PMU_PLUGIN ": meta_data_create failed.");
+ return NULL;
+ }
+
+ meta_data_add_unsigned_int(meta, "intel_pmu:raw_count", efd->val[0]);
+ meta_data_add_unsigned_int(meta, "intel_pmu:time_enabled", efd->val[1]);
+ meta_data_add_unsigned_int(meta, "intel_pmu:time_running", efd->val[2]);
+
+ return meta;
+}
+
+static void pmu_dispatch_data(void) {
+
+ struct event *e;
+
+ for (e = g_ctx.event_list->eventlist; e; e = e->next) {
+ uint64_t all_value = 0;
+ int event_enabled = 0;
+ for (int i = 0; i < g_ctx.event_list->num_cpus; i++) {
+
+ if (e->efd[i].fd < 0)
+ continue;
+
+ event_enabled++;
+
+ /* If there are more events than counters, the kernel uses time
+ * multiplexing. With multiplexing, at the end of the run,
+ * the counter is scaled basing on total time enabled vs time running.
+ * final_count = raw_count * time_enabled/time_running
+ */
+ uint64_t value = event_scaled_value(e, i);
+ all_value += value;
+
+ /* get meta data with information about scaling */
+ meta_data_t *meta = pmu_meta_data_create(&e->efd[i]);
+
+ /* dispatch per CPU value */
+ pmu_submit_counter(i, e->event, value, meta);
+
+ meta_data_destroy(meta);
+ }
+
+ if (event_enabled > 0) {
+ DEBUG(PMU_PLUGIN ": %-20s %'10lu", e->event, all_value);
+ /* dispatch all CPU value */
+ pmu_submit_counter(-1, e->event, all_value, NULL);
+ }
+ }
+}
+
+static int pmu_read(__attribute__((unused)) user_data_t *ud) {
+ int ret;
+
+ DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
+
+ ret = read_all_events(g_ctx.event_list);
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to read values of all events.");
+ return ret;
+ }
+
+ pmu_dispatch_data();
+
+ return 0;
+}
+
+static int pmu_add_events(struct eventlist *el, uint32_t type,
+ event_info_t *events, size_t count) {
+
+ for (size_t i = 0; i < count; i++) {
+ /* Allocate memory for event struct that contains array of efd structs
+ for all cores */
+ struct event *e =
+ calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1);
+ if (e == NULL) {
+ ERROR(PMU_PLUGIN ": Failed to allocate event structure");
+ return -ENOMEM;
+ }
+
+ e->attr.type = type;
+ e->attr.config = events[i].config;
+ e->attr.size = PERF_ATTR_SIZE_VER0;
+ if (!el->eventlist)
+ el->eventlist = e;
+ if (el->eventlist_last)
+ el->eventlist_last->next = e;
+ el->eventlist_last = e;
+ e->event = strdup(events[i].name);
+ }
+
+ return 0;
+}
+
+static int pmu_add_hw_events(struct eventlist *el, char **e, size_t count) {
+
+ for (size_t i = 0; i < count; i++) {
+
+ size_t group_events_count = 0;
+
+ char *events = strdup(e[i]);
+ if (!events)
+ return -1;
+
+ char *s, *tmp;
+ for (s = strtok_r(events, ",", &tmp); s; s = strtok_r(NULL, ",", &tmp)) {
+
+ /* Multiple events parsed in one entry */
+ if (group_events_count == 1) {
+ /* Mark previously added event as group leader */
+ el->eventlist_last->group_leader = 1;
+ }
+
+ /* Allocate memory for event struct that contains array of efd structs
+ for all cores */
+ struct event *e =
+ calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1);
+ if (e == NULL) {
+ free(events);
+ return -ENOMEM;
+ }
+
+ if (resolve_event(s, &e->attr) == 0) {
+ e->next = NULL;
+ if (!el->eventlist)
+ el->eventlist = e;
+ if (el->eventlist_last)
+ el->eventlist_last->next = e;
+ el->eventlist_last = e;
+ e->event = strdup(s);
+ } else {
+ DEBUG(PMU_PLUGIN ": Cannot resolve %s", s);
+ sfree(e);
+ }
+
+ group_events_count++;
+ }
+
+ /* Multiple events parsed in one entry */
+ if (group_events_count > 1) {
+ /* Mark last added event as group end */
+ el->eventlist_last->end_group = 1;
+ }
+
+ free(events);
+ }
+
+ return 0;
+}
+
+static void pmu_free_events(struct eventlist *el) {
+
+ if (el == NULL)
+ return;
+
+ struct event *e = el->eventlist;
+
+ while (e) {
+ struct event *next = e->next;
+ sfree(e);
+ e = next;
+ }
+
+ el->eventlist = NULL;
+}
+
+static int pmu_setup_events(struct eventlist *el, bool measure_all,
+ int measure_pid) {
+ struct event *e, *leader = NULL;
+ int ret = -1;
+
+ for (e = el->eventlist; e; e = e->next) {
+
+ for (int i = 0; i < el->num_cpus; i++) {
+ if (setup_event(e, i, leader, measure_all, measure_pid) < 0) {
+ WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).",
+ e->event, i);
+ } else {
+ /* success if at least one event was set */
+ ret = 0;
+ }
+ }
+
+ if (e->group_leader)
+ leader = e;
+ if (e->end_group)
+ leader = NULL;
+ }
+
+ return ret;
+}
+
+static int pmu_init(void) {
+ int ret;
+
+ DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
+
+ g_ctx.event_list = alloc_eventlist();
+ if (g_ctx.event_list == NULL) {
+ ERROR(PMU_PLUGIN ": Failed to allocate event list.");
+ return -ENOMEM;
+ }
+
+ if (g_ctx.hw_cache_events) {
+ ret =
+ pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE, g_hw_cache_events,
+ STATIC_ARRAY_SIZE(g_hw_cache_events));
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to add hw cache events.");
+ goto init_error;
+ }
+ }
+
+ if (g_ctx.kernel_pmu_events) {
+ ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_HARDWARE,
+ g_kernel_pmu_events,
+ STATIC_ARRAY_SIZE(g_kernel_pmu_events));
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to add kernel PMU events.");
+ goto init_error;
+ }
+ }
+
+ /* parse events names if config option is present and is not empty */
+ if (g_ctx.hw_events_count) {
+
+ ret = read_events(g_ctx.event_list_fn);
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to read event list file '%s'.",
+ g_ctx.event_list_fn);
+ return ret;
+ }
+
+ ret = pmu_add_hw_events(g_ctx.event_list, g_ctx.hw_events,
+ g_ctx.hw_events_count);
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to add hardware events.");
+ goto init_error;
+ }
+ }
+
+ if (g_ctx.sw_events) {
+ ret = pmu_add_events(g_ctx.event_list, PERF_TYPE_SOFTWARE, g_sw_events,
+ STATIC_ARRAY_SIZE(g_sw_events));
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to add software events.");
+ goto init_error;
+ }
+ }
+
+#if COLLECT_DEBUG
+ pmu_dump_events();
+#endif
+
+ if (g_ctx.event_list->eventlist != NULL) {
+ /* measure all processes */
+ ret = pmu_setup_events(g_ctx.event_list, true, -1);
+ if (ret != 0) {
+ ERROR(PMU_PLUGIN ": Failed to setup perf events for the event list.");
+ goto init_error;
+ }
+ } else {
+ WARNING(PMU_PLUGIN
+ ": Events list is empty. No events were setup for monitoring.");
+ }
+
+ return 0;
+
+init_error:
+
+ pmu_free_events(g_ctx.event_list);
+ sfree(g_ctx.event_list);
+ for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
+ sfree(g_ctx.hw_events[i]);
+ }
+ sfree(g_ctx.hw_events);
+ g_ctx.hw_events_count = 0;
+
+ return ret;
+}
+
+static int pmu_shutdown(void) {
+
+ DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
+
+ pmu_free_events(g_ctx.event_list);
+ sfree(g_ctx.event_list);
+ for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
+ sfree(g_ctx.hw_events[i]);
+ }
+ sfree(g_ctx.hw_events);
+ g_ctx.hw_events_count = 0;
+
+ return 0;
+}
+
+void module_register(void) {
+ plugin_register_init(PMU_PLUGIN, pmu_init);
+ plugin_register_complex_config(PMU_PLUGIN, pmu_config);
+ plugin_register_complex_read(NULL, PMU_PLUGIN, pmu_read, 0, NULL);
+ plugin_register_shutdown(PMU_PLUGIN, pmu_shutdown);
+}
* Florian octo Forster <octo at collectd.org>
* Peter Holik <peter at holik.at>
* Bruno Prémont <bonbons at linux-vserver.org>
+ * Pavel Rochnyak <pavel2000 ngs.ru>
**/
#include "collectd.h"
#include "plugin.h"
#include "utils_ignorelist.h"
+#include <OpenIPMI/ipmi_auth.h>
#include <OpenIPMI/ipmi_conn.h>
#include <OpenIPMI/ipmi_err.h>
+#include <OpenIPMI/ipmi_lan.h>
#include <OpenIPMI/ipmi_posix.h>
#include <OpenIPMI/ipmi_smi.h>
#include <OpenIPMI/ipmiif.h>
+#define ERR_BUF_SIZE 1024
+
/*
* Private data types
*/
struct c_ipmi_sensor_list_s;
typedef struct c_ipmi_sensor_list_s c_ipmi_sensor_list_t;
+struct c_ipmi_instance_s {
+ char *name;
+ ignorelist_t *ignorelist;
+ _Bool notify_add;
+ _Bool notify_remove;
+ _Bool notify_notpresent;
+ _Bool notify_conn;
+ _Bool sel_enabled;
+ _Bool sel_clear_event;
+
+ char *host;
+ char *connaddr;
+ char *username;
+ char *password;
+ unsigned int authtype;
+
+ _Bool connected;
+ ipmi_con_t *connection;
+ pthread_mutex_t sensor_list_lock;
+ c_ipmi_sensor_list_t *sensor_list;
+
+ _Bool active;
+ pthread_t thread_id;
+ int init_in_progress;
+
+ struct c_ipmi_instance_s *next;
+};
+typedef struct c_ipmi_instance_s c_ipmi_instance_t;
+
struct c_ipmi_sensor_list_s {
ipmi_sensor_id_t sensor_id;
char sensor_name[DATA_MAX_NAME_LEN];
char sensor_type[DATA_MAX_NAME_LEN];
+ char type_instance[DATA_MAX_NAME_LEN];
int sensor_not_present;
c_ipmi_sensor_list_t *next;
+ c_ipmi_instance_t *instance;
+ unsigned int use;
};
+struct c_ipmi_db_type_map_s {
+ enum ipmi_unit_type_e type;
+ const char *type_name;
+};
+typedef struct c_ipmi_db_type_map_s c_ipmi_db_type_map_t;
+
/*
* Module global variables
*/
-static pthread_mutex_t sensor_list_lock = PTHREAD_MUTEX_INITIALIZER;
-static c_ipmi_sensor_list_t *sensor_list = NULL;
-
-static int c_ipmi_init_in_progress = 0;
-static int c_ipmi_active = 0;
-static pthread_t thread_id = (pthread_t)0;
-
-static const char *config_keys[] = {"Sensor", "IgnoreSelected",
- "NotifySensorAdd", "NotifySensorRemove",
- "NotifySensorNotPresent"};
-static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
-
-static ignorelist_t *ignorelist = NULL;
-
-static int c_ipmi_nofiy_add = 0;
-static int c_ipmi_nofiy_remove = 0;
-static int c_ipmi_nofiy_notpresent = 0;
+static os_handler_t *os_handler = NULL;
+static c_ipmi_instance_t *instances = NULL;
/*
* Misc private functions
*/
-static void c_ipmi_error(const char *func, int status) {
- char errbuf[4096] = {0};
+static void c_ipmi_error(c_ipmi_instance_t *st, const char *func, int status) {
+ char errbuf[ERR_BUF_SIZE] = {0};
- if (IPMI_IS_OS_ERR(status)) {
- sstrerror(IPMI_GET_OS_ERR(status), errbuf, sizeof(errbuf));
- } else if (IPMI_IS_IPMI_ERR(status)) {
- ipmi_get_error_string(IPMI_GET_IPMI_ERR(status), errbuf, sizeof(errbuf));
+ if (IPMI_IS_OS_ERR(status) || IPMI_IS_RMCPP_ERR(status) ||
+ IPMI_IS_IPMI_ERR(status)) {
+ ipmi_get_error_string(status, errbuf, sizeof(errbuf));
}
if (errbuf[0] == 0) {
}
errbuf[sizeof(errbuf) - 1] = 0;
- ERROR("ipmi plugin: %s failed: %s", func, errbuf);
+ ERROR("ipmi plugin: %s failed for `%s`: %s", func, st->name, errbuf);
} /* void c_ipmi_error */
+static void c_ipmi_log(os_handler_t *handler, const char *format,
+ enum ipmi_log_type_e log_type, va_list ap) {
+ char msg[ERR_BUF_SIZE];
+
+ vsnprintf(msg, sizeof(msg), format, ap);
+
+ switch (log_type) {
+ case IPMI_LOG_INFO:
+ INFO("ipmi plugin: %s", msg);
+ break;
+ case IPMI_LOG_WARNING:
+ NOTICE("ipmi plugin: %s", msg);
+ break;
+ case IPMI_LOG_SEVERE:
+ WARNING("ipmi plugin: %s", msg);
+ break;
+ case IPMI_LOG_FATAL:
+ ERROR("ipmi plugin: %s", msg);
+ break;
+ case IPMI_LOG_ERR_INFO:
+ ERROR("ipmi plugin: %s", msg);
+ break;
+#if COLLECT_DEBUG
+ case IPMI_LOG_DEBUG_START:
+ case IPMI_LOG_DEBUG:
+ DEBUG("ipmi plugin: %s", msg);
+ break;
+ case IPMI_LOG_DEBUG_CONT:
+ case IPMI_LOG_DEBUG_END:
+ DEBUG("%s", msg);
+ break;
+#else
+ case IPMI_LOG_DEBUG_START:
+ case IPMI_LOG_DEBUG:
+ case IPMI_LOG_DEBUG_CONT:
+ case IPMI_LOG_DEBUG_END:
+ break;
+#endif
+ }
+} /* void c_ipmi_log */
+
+static notification_t c_ipmi_notification_init(c_ipmi_instance_t const *st,
+ int severity) {
+ notification_t n = {severity, cdtime(), "", "", "ipmi", "", "", "", NULL};
+
+ sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g, sizeof(n.host));
+ return n;
+} /* notification_t c_ipmi_notification_init */
+
/*
* Sensor handlers
*/
/* Prototype for sensor_list_remove, so sensor_read_handler can call it. */
-static int sensor_list_remove(ipmi_sensor_t *sensor);
+static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor);
static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
enum ipmi_value_present_e value_present,
unsigned int __attribute__((unused)) raw_value,
- double value,
- ipmi_states_t __attribute__((unused)) * states,
+ double value, ipmi_states_t *states,
void *user_data) {
value_list_t vl = VALUE_LIST_INIT;
- c_ipmi_sensor_list_t *list_item = (c_ipmi_sensor_list_t *)user_data;
+ c_ipmi_sensor_list_t *list_item = user_data;
+ c_ipmi_instance_t *st = list_item->instance;
+
+ list_item->use--;
if (err != 0) {
- if ((err & 0xff) == IPMI_NOT_PRESENT_CC) {
+ if (IPMI_IS_IPMI_ERR(err) &&
+ IPMI_GET_IPMI_ERR(err) == IPMI_NOT_PRESENT_CC) {
if (list_item->sensor_not_present == 0) {
list_item->sensor_not_present = 1;
- INFO("ipmi plugin: sensor_read_handler: sensor %s "
+ INFO("ipmi plugin: sensor_read_handler: sensor `%s` of `%s` "
"not present.",
- list_item->sensor_name);
+ list_item->sensor_name, st->name);
- if (c_ipmi_nofiy_notpresent) {
- notification_t n = {
- NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "", NULL};
+ if (st->notify_notpresent) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_WARNING);
- sstrncpy(n.host, hostname_g, sizeof(n.host));
- sstrncpy(n.type_instance, list_item->sensor_name,
+ sstrncpy(n.type_instance, list_item->type_instance,
sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
snprintf(n.message, sizeof(n.message), "sensor %s not present",
} else if (IPMI_IS_IPMI_ERR(err) &&
IPMI_GET_IPMI_ERR(err) ==
IPMI_NOT_SUPPORTED_IN_PRESENT_STATE_CC) {
- INFO("ipmi plugin: sensor_read_handler: Sensor %s not ready",
- list_item->sensor_name);
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` not ready.",
+ list_item->sensor_name, st->name);
+ } else if (IPMI_IS_IPMI_ERR(err) &&
+ IPMI_GET_IPMI_ERR(err) == IPMI_TIMEOUT_CC) {
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` timed out.",
+ list_item->sensor_name, st->name);
} else {
+ char errbuf[ERR_BUF_SIZE] = {0};
+ ipmi_get_error_string(err, errbuf, sizeof(errbuf) - 1);
+
if (IPMI_IS_IPMI_ERR(err))
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with IPMI error %#x.",
- list_item->sensor_name, IPMI_GET_IPMI_ERR(err));
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+ "%s.",
+ list_item->sensor_name, st->name, errbuf);
else if (IPMI_IS_OS_ERR(err))
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with OS error %#x.",
- list_item->sensor_name, IPMI_GET_OS_ERR(err));
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+ "%s (%#x).",
+ list_item->sensor_name, st->name, errbuf, IPMI_GET_OS_ERR(err));
else if (IPMI_IS_RMCPP_ERR(err))
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with RMCPP error %#x.",
- list_item->sensor_name, IPMI_GET_RMCPP_ERR(err));
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+ "%s.",
+ list_item->sensor_name, st->name, errbuf);
else if (IPMI_IS_SOL_ERR(err))
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with RMCPP error %#x.",
- list_item->sensor_name, IPMI_GET_SOL_ERR(err));
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
+ "%s (%#x).",
+ list_item->sensor_name, st->name, errbuf, IPMI_GET_SOL_ERR(err));
else
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
- "because it failed with error %#x. of class %#x",
- list_item->sensor_name, err & 0xff, err & 0xffffff00);
- sensor_list_remove(sensor);
+ INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed "
+ "with error %#x. of class %#x",
+ list_item->sensor_name, st->name, err & 0xff, err & 0xffffff00);
}
return;
} else if (list_item->sensor_not_present == 1) {
list_item->sensor_not_present = 0;
- INFO("ipmi plugin: sensor_read_handler: sensor %s present.",
- list_item->sensor_name);
+ INFO("ipmi plugin: sensor_read_handler: sensor `%s` of `%s` present.",
+ list_item->sensor_name, st->name);
- if (c_ipmi_nofiy_notpresent) {
- notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi",
- "", "", "", NULL};
+ if (st->notify_notpresent) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
- sstrncpy(n.host, hostname_g, sizeof(n.host));
- sstrncpy(n.type_instance, list_item->sensor_name,
+ sstrncpy(n.type_instance, list_item->type_instance,
sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
snprintf(n.message, sizeof(n.message), "sensor %s present",
}
if (value_present != IPMI_BOTH_VALUES_PRESENT) {
- INFO("ipmi plugin: sensor_read_handler: Removing sensor %s, "
+ INFO("ipmi plugin: sensor_read_handler: Removing sensor `%s` of `%s`, "
"because it provides %s. If you need this sensor, "
"please file a bug report.",
- list_item->sensor_name,
+ list_item->sensor_name, st->name,
(value_present == IPMI_RAW_VALUE_PRESENT) ? "only the raw value"
: "no value");
- sensor_list_remove(sensor);
+ sensor_list_remove(st, sensor);
+ return;
+ }
+
+ if (!ipmi_is_sensor_scanning_enabled(states)) {
+ DEBUG("ipmi plugin: sensor_read_handler: Skipping sensor `%s` of `%s`, "
+ "it is in 'scanning disabled' state.",
+ list_item->sensor_name, st->name);
+ return;
+ }
+
+ if (ipmi_is_initial_update_in_progress(states)) {
+ DEBUG("ipmi plugin: sensor_read_handler: Skipping sensor `%s` of `%s`, "
+ "it is in 'initial update in progress' state.",
+ list_item->sensor_name, st->name);
return;
}
vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
+ if (st->host != NULL)
+ sstrncpy(vl.host, st->host, sizeof(vl.host));
sstrncpy(vl.plugin, "ipmi", sizeof(vl.plugin));
sstrncpy(vl.type, list_item->sensor_type, sizeof(vl.type));
- sstrncpy(vl.type_instance, list_item->sensor_name, sizeof(vl.type_instance));
+ sstrncpy(vl.type_instance, list_item->type_instance,
+ sizeof(vl.type_instance));
plugin_dispatch_values(&vl);
} /* void sensor_read_handler */
-static int sensor_list_add(ipmi_sensor_t *sensor) {
+static void sensor_get_name(ipmi_sensor_t *sensor, char *buffer, int buf_len) {
+ char temp[DATA_MAX_NAME_LEN] = {0};
+ ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
+ const char *entity_id_string = ipmi_entity_get_entity_id_string(ent);
+ char sensor_name[DATA_MAX_NAME_LEN] = "";
+ char *sensor_name_ptr;
+
+ if ((buffer == NULL) || (buf_len == 0))
+ return;
+
+ ipmi_sensor_get_name(sensor, temp, sizeof(temp));
+ temp[sizeof(temp) - 1] = 0;
+
+ if (entity_id_string != NULL && strlen(temp))
+ snprintf(sensor_name, sizeof(sensor_name), "%s %s", temp, entity_id_string);
+ else if (entity_id_string != NULL)
+ sstrncpy(sensor_name, entity_id_string, sizeof(sensor_name));
+ else
+ sstrncpy(sensor_name, temp, sizeof(sensor_name));
+
+ if (strlen(temp)) {
+ sstrncpy(temp, sensor_name, sizeof(temp));
+ sensor_name_ptr = strstr(temp, ").");
+ if (sensor_name_ptr != NULL) {
+ /* If name is something like "foo (123).bar",
+ * change that to "bar (123)".
+ * Both, sensor_name_ptr and sensor_id_ptr point to memory within the
+ * `temp' array, which holds a copy of the current `sensor_name'. */
+ char *sensor_id_ptr;
+
+ /* `sensor_name_ptr' points to ").bar". */
+ sensor_name_ptr[1] = 0;
+ /* `temp' holds "foo (123)\0bar\0". */
+ sensor_name_ptr += 2;
+ /* `sensor_name_ptr' now points to "bar". */
+
+ sensor_id_ptr = strstr(temp, "(");
+ if (sensor_id_ptr != NULL) {
+ /* `sensor_id_ptr' now points to "(123)". */
+ snprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr,
+ sensor_id_ptr);
+ }
+ /* else: don't touch sensor_name. */
+ }
+ }
+ sstrncpy(buffer, sensor_name, buf_len);
+}
+
+static const char *sensor_unit_to_type(ipmi_sensor_t *sensor) {
+ static const c_ipmi_db_type_map_t ipmi_db_type_map[] = {
+ {IPMI_UNIT_TYPE_WATTS, "power"}, {IPMI_UNIT_TYPE_CFM, "flow"}};
+
+ /* check the modifier and rate of the sensor value */
+ if ((ipmi_sensor_get_modifier_unit_use(sensor) != IPMI_MODIFIER_UNIT_NONE) ||
+ (ipmi_sensor_get_rate_unit(sensor) != IPMI_RATE_UNIT_NONE))
+ return NULL;
+
+ /* find the db type by using sensor base unit type */
+ enum ipmi_unit_type_e ipmi_type = ipmi_sensor_get_base_unit(sensor);
+ for (int i = 0; i < STATIC_ARRAY_SIZE(ipmi_db_type_map); i++)
+ if (ipmi_db_type_map[i].type == ipmi_type)
+ return ipmi_db_type_map[i].type_name;
+
+ return NULL;
+} /* const char* sensor_unit_to_type */
+
+static int sensor_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
ipmi_sensor_id_t sensor_id;
c_ipmi_sensor_list_t *list_item;
c_ipmi_sensor_list_t *list_prev;
char buffer[DATA_MAX_NAME_LEN] = {0};
- const char *entity_id_string;
- char sensor_name[DATA_MAX_NAME_LEN];
- char *sensor_name_ptr;
+ char *sensor_name_ptr = buffer;
int sensor_type;
const char *type;
- ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
sensor_id = ipmi_sensor_convert_to_id(sensor);
+ sensor_get_name(sensor, buffer, sizeof(buffer));
+
+ DEBUG("ipmi plugin: sensor_list_add: Found sensor `%s` of `%s`,"
+ " Type: %#x"
+ " Event reading type: %#x"
+ " Direction: %#x"
+ " Event support: %#x",
+ sensor_name_ptr, st->name, ipmi_sensor_get_sensor_type(sensor),
+ ipmi_sensor_get_event_reading_type(sensor),
+ ipmi_sensor_get_sensor_direction(sensor),
+ ipmi_sensor_get_event_support(sensor));
+
+ /* Both `ignorelist' and `sensor_name_ptr' may be NULL. */
+ if (ignorelist_match(st->ignorelist, sensor_name_ptr) != 0)
+ return 0;
- ipmi_sensor_get_name(sensor, buffer, sizeof(buffer));
- buffer[sizeof(buffer) - 1] = 0;
+ /* FIXME: Use rate unit or base unit to scale the value */
- entity_id_string = ipmi_entity_get_entity_id_string(ent);
+ sensor_type = ipmi_sensor_get_sensor_type(sensor);
- if (entity_id_string == NULL)
- sstrncpy(sensor_name, buffer, sizeof(sensor_name));
- else
- snprintf(sensor_name, sizeof(sensor_name), "%s %s", buffer,
- entity_id_string);
-
- sstrncpy(buffer, sensor_name, sizeof(buffer));
- sensor_name_ptr = strstr(buffer, ").");
- if (sensor_name_ptr != NULL) {
- /* If name is something like "foo (123).bar",
- * change that to "bar (123)".
- * Both, sensor_name_ptr and sensor_id_ptr point to memory within the
- * `buffer' array, which holds a copy of the current `sensor_name'. */
- char *sensor_id_ptr;
-
- /* `sensor_name_ptr' points to ").bar". */
- sensor_name_ptr[1] = 0;
- /* `buffer' holds "foo (123)\0bar\0". */
- sensor_name_ptr += 2;
- /* `sensor_name_ptr' now points to "bar". */
-
- sensor_id_ptr = strstr(buffer, "(");
- if (sensor_id_ptr != NULL) {
- /* `sensor_id_ptr' now points to "(123)". */
- snprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr,
- sensor_id_ptr);
- }
- /* else: don't touch sensor_name. */
+ /*
+ * ipmitool/lib/ipmi_sdr.c sdr_sensor_has_analog_reading() has a notice
+ * about 'Threshold sensors' and 'analog readings'. Discrete sensor may
+ * have analog data, but discrete sensors support is not implemented
+ * in Collectd yet.
+ *
+ * ipmi_sensor_id_get_reading() supports only 'Threshold' sensors.
+ * See lib/sensor.c:4842, stand_ipmi_sensor_get_reading() for details.
+ */
+ if (!ipmi_sensor_get_is_readable(sensor)) {
+ INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
+ "because it isn't readable! Its type: (%#x, %s). ",
+ sensor_name_ptr, st->name, sensor_type,
+ ipmi_sensor_get_sensor_type_string(sensor));
+ return -1;
}
- sensor_name_ptr = sensor_name;
-
- /* Both `ignorelist' and `plugin_instance' may be NULL. */
- if (ignorelist_match(ignorelist, sensor_name_ptr) != 0)
- return 0;
- /* FIXME: Use rate unit or base unit to scale the value */
+ if (ipmi_sensor_get_event_reading_type(sensor) !=
+ IPMI_EVENT_READING_TYPE_THRESHOLD) {
+ INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
+ "because it is discrete (%#x)! Its type: (%#x, %s). ",
+ sensor_name_ptr, st->name, sensor_type,
+ ipmi_sensor_get_event_reading_type(sensor),
+ ipmi_sensor_get_sensor_type_string(sensor));
+ return -1;
+ }
- sensor_type = ipmi_sensor_get_sensor_type(sensor);
switch (sensor_type) {
case IPMI_SENSOR_TYPE_TEMPERATURE:
type = "temperature";
type = "fanspeed";
break;
+ case IPMI_SENSOR_TYPE_MEMORY:
+ type = "memory";
+ break;
+
default: {
- const char *sensor_type_str;
+ /* try to get collectd DB type based on sensor base unit type */
+ if ((type = sensor_unit_to_type(sensor)) != NULL)
+ break;
- sensor_type_str = ipmi_sensor_get_sensor_type_string(sensor);
- INFO("ipmi plugin: sensor_list_add: Ignore sensor %s, "
- "because I don't know how to handle its type (%#x, %s). "
- "If you need this sensor, please file a bug report.",
- sensor_name_ptr, sensor_type, sensor_type_str);
+ INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
+ "because I don't know how to handle its units (%#x, %#x, %#x). "
+ "Sensor type: (%#x, %s). If you need this sensor, please file "
+ "a bug report at http://collectd.org/.",
+ sensor_name_ptr, st->name, ipmi_sensor_get_base_unit(sensor),
+ ipmi_sensor_get_modifier_unit(sensor),
+ ipmi_sensor_get_rate_unit(sensor), sensor_type,
+ ipmi_sensor_get_sensor_type_string(sensor));
return -1;
}
} /* switch (sensor_type) */
- pthread_mutex_lock(&sensor_list_lock);
+ pthread_mutex_lock(&st->sensor_list_lock);
list_prev = NULL;
- for (list_item = sensor_list; list_item != NULL;
+ for (list_item = st->sensor_list; list_item != NULL;
list_item = list_item->next) {
if (ipmi_cmp_sensor_id(sensor_id, list_item->sensor_id) == 0)
break;
} /* for (list_item) */
if (list_item != NULL) {
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
return 0;
}
list_item = (c_ipmi_sensor_list_t *)calloc(1, sizeof(c_ipmi_sensor_list_t));
if (list_item == NULL) {
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
return -1;
}
+ list_item->instance = st;
list_item->sensor_id = ipmi_sensor_convert_to_id(sensor);
if (list_prev != NULL)
list_prev->next = list_item;
else
- sensor_list = list_item;
+ st->sensor_list = list_item;
+
+ /* if sensor provides the percentage value, use "percent" collectd type
+ and add the `percent` to the type instance of the reported value */
+ if (ipmi_sensor_get_percentage(sensor)) {
+ snprintf(list_item->type_instance, sizeof(list_item->type_instance),
+ "percent-%s", sensor_name_ptr);
+ type = "percent";
+ } else {
+ /* use type instance as a name of the sensor */
+ sstrncpy(list_item->type_instance, sensor_name_ptr,
+ sizeof(list_item->type_instance));
+ }
sstrncpy(list_item->sensor_name, sensor_name_ptr,
sizeof(list_item->sensor_name));
sstrncpy(list_item->sensor_type, type, sizeof(list_item->sensor_type));
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
- if (c_ipmi_nofiy_add && (c_ipmi_init_in_progress == 0)) {
- notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
+ if (st->notify_add && (st->init_in_progress == 0)) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
- sstrncpy(n.host, hostname_g, sizeof(n.host));
- sstrncpy(n.type_instance, list_item->sensor_name, sizeof(n.type_instance));
+ sstrncpy(n.type_instance, list_item->type_instance,
+ sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
snprintf(n.message, sizeof(n.message), "sensor %s added",
list_item->sensor_name);
return 0;
} /* int sensor_list_add */
-static int sensor_list_remove(ipmi_sensor_t *sensor) {
+static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
ipmi_sensor_id_t sensor_id;
c_ipmi_sensor_list_t *list_item;
c_ipmi_sensor_list_t *list_prev;
sensor_id = ipmi_sensor_convert_to_id(sensor);
- pthread_mutex_lock(&sensor_list_lock);
+ pthread_mutex_lock(&st->sensor_list_lock);
list_prev = NULL;
- for (list_item = sensor_list; list_item != NULL;
+ for (list_item = st->sensor_list; list_item != NULL;
list_item = list_item->next) {
if (ipmi_cmp_sensor_id(sensor_id, list_item->sensor_id) == 0)
break;
} /* for (list_item) */
if (list_item == NULL) {
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
return -1;
}
if (list_prev == NULL)
- sensor_list = list_item->next;
+ st->sensor_list = list_item->next;
else
list_prev->next = list_item->next;
list_prev = NULL;
list_item->next = NULL;
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
- if (c_ipmi_nofiy_remove && c_ipmi_active) {
- notification_t n = {NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "",
- NULL};
+ if (st->notify_remove && st->active) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_WARNING);
- sstrncpy(n.host, hostname_g, sizeof(n.host));
- sstrncpy(n.type_instance, list_item->sensor_name, sizeof(n.type_instance));
+ sstrncpy(n.type_instance, list_item->type_instance,
+ sizeof(n.type_instance));
sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
snprintf(n.message, sizeof(n.message), "sensor %s removed",
list_item->sensor_name);
return 0;
} /* int sensor_list_remove */
-static int sensor_list_read_all(void) {
- pthread_mutex_lock(&sensor_list_lock);
+static int sensor_list_read_all(c_ipmi_instance_t *st) {
+ pthread_mutex_lock(&st->sensor_list_lock);
- for (c_ipmi_sensor_list_t *list_item = sensor_list; list_item != NULL;
+ for (c_ipmi_sensor_list_t *list_item = st->sensor_list; list_item != NULL;
list_item = list_item->next) {
+ DEBUG("ipmi plugin: try read sensor `%s` of `%s`, use: %d",
+ list_item->sensor_name, st->name, list_item->use);
+
+ /* Reading already initiated */
+ if (list_item->use)
+ continue;
+
+ list_item->use++;
ipmi_sensor_id_get_reading(list_item->sensor_id, sensor_read_handler,
- /* user data = */ list_item);
+ /* user data = */ (void *)list_item);
} /* for (list_item) */
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
return 0;
} /* int sensor_list_read_all */
-static int sensor_list_remove_all(void) {
+static int sensor_list_remove_all(c_ipmi_instance_t *st) {
c_ipmi_sensor_list_t *list_item;
- pthread_mutex_lock(&sensor_list_lock);
+ pthread_mutex_lock(&st->sensor_list_lock);
- list_item = sensor_list;
- sensor_list = NULL;
+ list_item = st->sensor_list;
+ st->sensor_list = NULL;
- pthread_mutex_unlock(&sensor_list_lock);
+ pthread_mutex_unlock(&st->sensor_list_lock);
while (list_item != NULL) {
c_ipmi_sensor_list_t *list_next = list_item->next;
return 0;
} /* int sensor_list_remove_all */
+static int sensor_convert_threshold_severity(enum ipmi_thresh_e severity) {
+ switch (severity) {
+ case IPMI_LOWER_NON_CRITICAL:
+ case IPMI_UPPER_NON_CRITICAL:
+ return NOTIF_OKAY;
+ case IPMI_LOWER_CRITICAL:
+ case IPMI_UPPER_CRITICAL:
+ return NOTIF_WARNING;
+ case IPMI_LOWER_NON_RECOVERABLE:
+ case IPMI_UPPER_NON_RECOVERABLE:
+ return NOTIF_FAILURE;
+ default:
+ return NOTIF_OKAY;
+ } /* switch (severity) */
+} /* int sensor_convert_threshold_severity */
+
+static void add_event_common_data(notification_t *n, ipmi_sensor_t *sensor,
+ enum ipmi_event_dir_e dir,
+ ipmi_event_t *event) {
+ ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
+
+ plugin_notification_meta_add_string(n, "entity_name",
+ ipmi_entity_get_entity_id_string(ent));
+ plugin_notification_meta_add_signed_int(n, "entity_id",
+ ipmi_entity_get_entity_id(ent));
+ plugin_notification_meta_add_signed_int(n, "entity_instance",
+ ipmi_entity_get_entity_instance(ent));
+ plugin_notification_meta_add_boolean(n, "assert", dir == IPMI_ASSERTION);
+
+ if (event)
+ plugin_notification_meta_add_signed_int(n, "event_type",
+ ipmi_event_get_type(event));
+} /* void add_event_sensor_meta_data */
+
+static int sensor_threshold_event_handler(
+ ipmi_sensor_t *sensor, enum ipmi_event_dir_e dir,
+ enum ipmi_thresh_e threshold, enum ipmi_event_value_dir_e high_low,
+ enum ipmi_value_present_e value_present, unsigned int raw_value,
+ double value, void *cb_data, ipmi_event_t *event) {
+
+ c_ipmi_instance_t *st = cb_data;
+
+ /* From the IPMI specification Chapter 2: Events.
+ * If a callback handles the event, then all future callbacks called due to
+ * the event will receive a NULL for the event. So be ready to handle a NULL
+ * event in all your event handlers. A NULL may also be passed to an event
+ * handler if the callback was not due to an event. */
+ if (event == NULL)
+ return IPMI_EVENT_NOT_HANDLED;
+
+ notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
+ /* offset is a table index and it's represented as enum of strings that are
+ organized in the way - high and low for each threshold severity level */
+ unsigned int offset = (2 * threshold) + high_low;
+ unsigned int event_type = ipmi_sensor_get_event_reading_type(sensor);
+ unsigned int sensor_type = ipmi_sensor_get_sensor_type(sensor);
+ const char *event_state =
+ ipmi_get_reading_name(event_type, sensor_type, offset);
+ sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance));
+ if (value_present != IPMI_NO_VALUES_PRESENT)
+ snprintf(n.message, sizeof(n.message),
+ "sensor %s received event: %s, value is %f", n.type_instance,
+ event_state, value);
+ else
+ snprintf(n.message, sizeof(n.message),
+ "sensor %s received event: %s, value not provided",
+ n.type_instance, event_state);
+
+ DEBUG("Threshold event received for sensor %s", n.type_instance);
+
+ sstrncpy(n.type, ipmi_sensor_get_sensor_type_string(sensor), sizeof(n.type));
+ n.severity = sensor_convert_threshold_severity(threshold);
+ n.time = NS_TO_CDTIME_T(ipmi_event_get_timestamp(event));
+
+ plugin_notification_meta_add_string(&n, "severity",
+ ipmi_get_threshold_string(threshold));
+ plugin_notification_meta_add_string(&n, "direction",
+ ipmi_get_value_dir_string(high_low));
+
+ switch (value_present) {
+ case IPMI_BOTH_VALUES_PRESENT:
+ plugin_notification_meta_add_double(&n, "val", value);
+ /* both values present, so fall-through to add raw value too */
+ case IPMI_RAW_VALUE_PRESENT: {
+ char buf[DATA_MAX_NAME_LEN] = {0};
+ snprintf(buf, sizeof(buf), "0x%2.2x", raw_value);
+ plugin_notification_meta_add_string(&n, "raw", buf);
+ } break;
+ default:
+ break;
+ } /* switch (value_present) */
+
+ add_event_common_data(&n, sensor, dir, event);
+
+ plugin_dispatch_notification(&n);
+ plugin_notification_meta_free(n.meta);
+
+ /* Delete handled ipmi event from the list */
+ if (st->sel_clear_event) {
+ ipmi_event_delete(event, NULL, NULL);
+ return IPMI_EVENT_HANDLED;
+ }
+
+ return IPMI_EVENT_NOT_HANDLED;
+} /* int sensor_threshold_event_handler */
+
+static int sensor_discrete_event_handler(ipmi_sensor_t *sensor,
+ enum ipmi_event_dir_e dir, int offset,
+ int severity, int prev_severity,
+ void *cb_data, ipmi_event_t *event) {
+
+ c_ipmi_instance_t *st = cb_data;
+
+ /* From the IPMI specification Chapter 2: Events.
+ * If a callback handles the event, then all future callbacks called due to
+ * the event will receive a NULL for the event. So be ready to handle a NULL
+ * event in all your event handlers. A NULL may also be passed to an event
+ * handler if the callback was not due to an event. */
+ if (event == NULL)
+ return IPMI_EVENT_NOT_HANDLED;
+
+ notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
+ unsigned int event_type = ipmi_sensor_get_event_reading_type(sensor);
+ unsigned int sensor_type = ipmi_sensor_get_sensor_type(sensor);
+ const char *event_state =
+ ipmi_get_reading_name(event_type, sensor_type, offset);
+ sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance));
+ snprintf(n.message, sizeof(n.message), "sensor %s received event: %s",
+ n.type_instance, event_state);
+
+ DEBUG("Discrete event received for sensor %s", n.type_instance);
+
+ sstrncpy(n.type, ipmi_sensor_get_sensor_type_string(sensor), sizeof(n.type));
+ n.time = NS_TO_CDTIME_T(ipmi_event_get_timestamp(event));
+
+ plugin_notification_meta_add_signed_int(&n, "offset", offset);
+
+ if (severity != -1)
+ plugin_notification_meta_add_signed_int(&n, "severity", severity);
+
+ if (prev_severity != -1)
+ plugin_notification_meta_add_signed_int(&n, "prevseverity", prev_severity);
+
+ add_event_common_data(&n, sensor, dir, event);
+
+ plugin_dispatch_notification(&n);
+ plugin_notification_meta_free(n.meta);
+
+ /* Delete handled ipmi event from the list */
+ if (st->sel_clear_event) {
+ ipmi_event_delete(event, NULL, NULL);
+ return IPMI_EVENT_HANDLED;
+ }
+
+ return IPMI_EVENT_NOT_HANDLED;
+} /* int sensor_discrete_event_handler */
+
/*
* Entity handlers
*/
-static void entity_sensor_update_handler(
- enum ipmi_update_e op, ipmi_entity_t __attribute__((unused)) * entity,
- ipmi_sensor_t *sensor, void __attribute__((unused)) * user_data) {
- /* TODO: Ignore sensors we cannot read */
+static void
+entity_sensor_update_handler(enum ipmi_update_e op,
+ ipmi_entity_t __attribute__((unused)) * entity,
+ ipmi_sensor_t *sensor, void *user_data) {
+ c_ipmi_instance_t *st = user_data;
if ((op == IPMI_ADDED) || (op == IPMI_CHANGED)) {
/* Will check for duplicate entries.. */
- sensor_list_add(sensor);
+ sensor_list_add(st, sensor);
+
+ if (st->sel_enabled) {
+ int status = 0;
+ /* register threshold event if threshold sensor support events */
+ if ((ipmi_sensor_get_event_reading_type(sensor) ==
+ IPMI_EVENT_READING_TYPE_THRESHOLD) &&
+ (ipmi_sensor_get_threshold_access(sensor) !=
+ IPMI_THRESHOLD_ACCESS_SUPPORT_NONE))
+ status = ipmi_sensor_add_threshold_event_handler(
+ sensor, sensor_threshold_event_handler, st);
+ /* register discrete handler if discrete/specific sensor support events */
+ else if (ipmi_sensor_get_event_support(sensor) != IPMI_EVENT_SUPPORT_NONE)
+ status = ipmi_sensor_add_discrete_event_handler(
+ sensor, sensor_discrete_event_handler, st);
+
+ if (status) {
+ char buf[DATA_MAX_NAME_LEN] = {0};
+ sensor_get_name(sensor, buf, sizeof(buf));
+ ERROR("Unable to add sensor %s event handler, status: %d", buf, status);
+ }
+ }
} else if (op == IPMI_DELETED) {
- sensor_list_remove(sensor);
+ sensor_list_remove(st, sensor);
+
+ if (st->sel_enabled) {
+ if (ipmi_sensor_get_event_reading_type(sensor) ==
+ IPMI_EVENT_READING_TYPE_THRESHOLD)
+ ipmi_sensor_remove_threshold_event_handler(
+ sensor, sensor_threshold_event_handler, st);
+ else
+ ipmi_sensor_remove_discrete_event_handler(
+ sensor, sensor_discrete_event_handler, st);
+ }
}
} /* void entity_sensor_update_handler */
/*
* Domain handlers
*/
-static void domain_entity_update_handler(
- enum ipmi_update_e op, ipmi_domain_t __attribute__((unused)) * domain,
- ipmi_entity_t *entity, void __attribute__((unused)) * user_data) {
+static void
+domain_entity_update_handler(enum ipmi_update_e op,
+ ipmi_domain_t __attribute__((unused)) * domain,
+ ipmi_entity_t *entity, void *user_data) {
int status;
+ c_ipmi_instance_t *st = user_data;
if (op == IPMI_ADDED) {
status = ipmi_entity_add_sensor_update_handler(
- entity, entity_sensor_update_handler, /* user data = */ NULL);
+ entity, entity_sensor_update_handler, /* user data = */ (void *)st);
if (status != 0) {
- c_ipmi_error("ipmi_entity_add_sensor_update_handler", status);
+ c_ipmi_error(st, "ipmi_entity_add_sensor_update_handler", status);
}
} else if (op == IPMI_DELETED) {
status = ipmi_entity_remove_sensor_update_handler(
- entity, entity_sensor_update_handler, /* user data = */ NULL);
+ entity, entity_sensor_update_handler, /* user data = */ (void *)st);
if (status != 0) {
- c_ipmi_error("ipmi_entity_remove_sensor_update_handler", status);
+ c_ipmi_error(st, "ipmi_entity_remove_sensor_update_handler", status);
}
}
} /* void domain_entity_update_handler */
+static void smi_event_handler(ipmi_con_t __attribute__((unused)) * ipmi,
+ const ipmi_addr_t __attribute__((unused)) * addr,
+ unsigned int __attribute__((unused)) addr_len,
+ ipmi_event_t *event, void *cb_data) {
+ unsigned int type = ipmi_event_get_type(event);
+ ipmi_domain_t *domain = cb_data;
+
+ DEBUG("%s: Event received: type %u", __FUNCTION__, type);
+
+ if (type != 0x02)
+ /* It's not a standard IPMI event. */
+ return;
+
+ /* force domain to reread SELs */
+ ipmi_domain_reread_sels(domain, NULL, NULL);
+}
+
static void domain_connection_change_handler(ipmi_domain_t *domain, int err,
unsigned int conn_num,
unsigned int port_num,
int still_connected,
void *user_data) {
- int status;
DEBUG("domain_connection_change_handler (domain = %p, err = %i, "
"conn_num = %u, port_num = %u, still_connected = %i, "
- "user_data = %p);\n",
+ "user_data = %p);",
(void *)domain, err, conn_num, port_num, still_connected, user_data);
- status = ipmi_domain_add_entity_update_handler(
- domain, domain_entity_update_handler, /* user data = */ NULL);
- if (status != 0) {
- c_ipmi_error("ipmi_domain_add_entity_update_handler", status);
+ c_ipmi_instance_t *st = user_data;
+
+ if (err != 0)
+ c_ipmi_error(st, "domain_connection_change_handler", err);
+
+ if (!still_connected) {
+
+ if (st->notify_conn && st->connected && st->init_in_progress == 0) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_FAILURE);
+
+ sstrncpy(n.message, "IPMI connection lost", sizeof(n.plugin));
+
+ plugin_dispatch_notification(&n);
+ }
+
+ st->connected = 0;
+ return;
}
-} /* void domain_connection_change_handler */
-static int thread_init(os_handler_t **ret_os_handler) {
- os_handler_t *os_handler;
- ipmi_con_t *smi_connection = NULL;
- ipmi_domain_id_t domain_id;
- int status;
+ if (st->notify_conn && !st->connected && st->init_in_progress == 0) {
+ notification_t n = c_ipmi_notification_init(st, NOTIF_OKAY);
- os_handler = ipmi_posix_thread_setup_os_handler(SIGIO);
- if (os_handler == NULL) {
- ERROR("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
- return -1;
+ sstrncpy(n.message, "IPMI connection restored", sizeof(n.plugin));
+
+ plugin_dispatch_notification(&n);
}
- ipmi_init(os_handler);
+ st->connected = 1;
- status = ipmi_smi_setup_con(/* if_num = */ 0, os_handler,
- /* user data = */ NULL, &smi_connection);
+ int status = ipmi_domain_add_entity_update_handler(
+ domain, domain_entity_update_handler, /* user data = */ st);
if (status != 0) {
- c_ipmi_error("ipmi_smi_setup_con", status);
- return -1;
+ c_ipmi_error(st, "ipmi_domain_add_entity_update_handler", status);
}
- ipmi_open_option_t open_option[1] = {[0] = {.option = IPMI_OPEN_OPTION_ALL,
- {.ival = 1}}};
+ status = st->connection->add_event_handler(st->connection, smi_event_handler,
+ (void *)domain);
+
+ if (status != 0)
+ c_ipmi_error(st, "Failed to register smi event handler", status);
+} /* void domain_connection_change_handler */
+
+static int c_ipmi_thread_init(c_ipmi_instance_t *st) {
+ ipmi_domain_id_t domain_id;
+ int status;
+
+ if (st->connaddr != NULL) {
+ status = ipmi_ip_setup_con(
+ &st->connaddr, &(char *){IPMI_LAN_STD_PORT_STR}, 1, st->authtype,
+ (unsigned int)IPMI_PRIVILEGE_USER, st->username, strlen(st->username),
+ st->password, strlen(st->password), os_handler,
+ /* user data = */ NULL, &st->connection);
+ if (status != 0) {
+ c_ipmi_error(st, "ipmi_ip_setup_con", status);
+ return -1;
+ }
+ } else {
+ status = ipmi_smi_setup_con(/* if_num = */ 0, os_handler,
+ /* user data = */ NULL, &st->connection);
+ if (status != 0) {
+ c_ipmi_error(st, "ipmi_smi_setup_con", status);
+ return -1;
+ }
+ }
+ ipmi_open_option_t opts[] = {
+ {.option = IPMI_OPEN_OPTION_ALL, {.ival = 1}},
+#ifdef IPMI_OPEN_OPTION_USE_CACHE
+ /* OpenIPMI-2.0.17 and later: Disable SDR cache in local file */
+ {.option = IPMI_OPEN_OPTION_USE_CACHE, {.ival = 0}},
+#endif
+ };
+
+ /*
+ * NOTE: Domain names must be unique. There is static `domains_list` common
+ * to all threads inside lib/domain.c and some ops are done by name.
+ */
status = ipmi_open_domain(
- "mydomain", &smi_connection, /* num_con = */ 1,
- domain_connection_change_handler, /* user data = */ NULL,
- /* domain_fully_up_handler = */ NULL, /* user data = */ NULL, open_option,
- sizeof(open_option) / sizeof(open_option[0]), &domain_id);
+ st->name, &st->connection, /* num_con = */ 1,
+ domain_connection_change_handler, /* user data = */ (void *)st,
+ /* domain_fully_up_handler = */ NULL, /* user data = */ NULL, opts,
+ STATIC_ARRAY_SIZE(opts), &domain_id);
if (status != 0) {
- c_ipmi_error("ipmi_open_domain", status);
+ c_ipmi_error(st, "ipmi_open_domain", status);
return -1;
}
- *ret_os_handler = os_handler;
return 0;
-} /* int thread_init */
+} /* int c_ipmi_thread_init */
-static void *thread_main(void __attribute__((unused)) * user_data) {
- int status;
- os_handler_t *os_handler = NULL;
+static void *c_ipmi_thread_main(void *user_data) {
+ c_ipmi_instance_t *st = user_data;
- status = thread_init(&os_handler);
+ int status = c_ipmi_thread_init(st);
if (status != 0) {
- ERROR("ipmi plugin: thread_init failed.\n");
+ ERROR("ipmi plugin: c_ipmi_thread_init failed.");
+ st->active = 0;
return (void *)-1;
}
- while (c_ipmi_active != 0) {
+ while (st->active != 0) {
struct timeval tv = {1, 0};
os_handler->perform_one_op(os_handler, &tv);
}
+ return (void *)0;
+} /* void *c_ipmi_thread_main */
- ipmi_posix_thread_free_os_handler(os_handler);
+static c_ipmi_instance_t *c_ipmi_init_instance() {
+ c_ipmi_instance_t *st;
- return (void *)0;
-} /* void *thread_main */
-
-static int c_ipmi_config(const char *key, const char *value) {
- if (ignorelist == NULL)
- ignorelist = ignorelist_create(/* invert = */ 1);
- if (ignorelist == NULL)
- return 1;
-
- if (strcasecmp("Sensor", key) == 0) {
- ignorelist_add(ignorelist, value);
- } else if (strcasecmp("IgnoreSelected", key) == 0) {
- int invert = 1;
- if (IS_TRUE(value))
- invert = 0;
- ignorelist_set_invert(ignorelist, invert);
- } else if (strcasecmp("NotifySensorAdd", key) == 0) {
- if (IS_TRUE(value))
- c_ipmi_nofiy_add = 1;
- } else if (strcasecmp("NotifySensorRemove", key) == 0) {
- if (IS_TRUE(value))
- c_ipmi_nofiy_remove = 1;
- } else if (strcasecmp("NotifySensorNotPresent", key) == 0) {
- if (IS_TRUE(value))
- c_ipmi_nofiy_notpresent = 1;
- } else {
- return -1;
+ st = calloc(1, sizeof(*st));
+ if (st == NULL) {
+ ERROR("ipmi plugin: calloc failed.");
+ return NULL;
}
- return 0;
-} /* int c_ipmi_config */
+ st->name = strdup("main");
+ if (st->name == NULL) {
+ sfree(st);
+ ERROR("ipmi plugin: strdup() failed.");
+ return NULL;
+ }
-static int c_ipmi_init(void) {
- int status;
+ st->ignorelist = ignorelist_create(/* invert = */ 1);
+ if (st->ignorelist == NULL) {
+ sfree(st->name);
+ sfree(st);
+ ERROR("ipmi plugin: ignorelist_create() failed.");
+ return NULL;
+ }
- /* Don't send `ADD' notifications during startup (~ 1 minute) */
- time_t iv = CDTIME_T_TO_TIME_T(plugin_get_interval());
- c_ipmi_init_in_progress = 1 + (60 / iv);
+ st->sensor_list = NULL;
+ pthread_mutex_init(&st->sensor_list_lock, /* attr = */ NULL);
+
+ st->host = NULL;
+ st->connaddr = NULL;
+ st->username = NULL;
+ st->password = NULL;
+ st->authtype = IPMI_AUTHTYPE_DEFAULT;
+
+ st->next = NULL;
+
+ return st;
+} /* c_ipmi_instance_t *c_ipmi_init_instance */
+
+static void c_ipmi_free_instance(c_ipmi_instance_t *st) {
+ if (st == NULL)
+ return;
- c_ipmi_active = 1;
+ assert(st->next == NULL);
+
+ sfree(st->name);
+ sfree(st->host);
+ sfree(st->connaddr);
+ sfree(st->username);
+ sfree(st->password);
+
+ ignorelist_free(st->ignorelist);
+ pthread_mutex_destroy(&st->sensor_list_lock);
+ sfree(st);
+} /* void c_ipmi_free_instance */
+
+static void c_ipmi_add_instance(c_ipmi_instance_t *instance) {
+ if (instances == NULL) {
+ instances = instance;
+ return;
+ }
+
+ c_ipmi_instance_t *last = instances;
+
+ while (last->next != NULL)
+ last = last->next;
+
+ last->next = instance;
+} /* void c_ipmi_add_instance */
+
+static int c_ipmi_config_add_instance(oconfig_item_t *ci) {
+ int status = 0;
+ c_ipmi_instance_t *st = c_ipmi_init_instance();
+ if (st == NULL)
+ return ENOMEM;
+
+ if (strcasecmp(ci->key, "Instance") == 0)
+ status = cf_util_get_string(ci, &st->name);
- status = plugin_thread_create(&thread_id, /* attr = */ NULL, thread_main,
- /* user data = */ NULL, "ipmi");
if (status != 0) {
- c_ipmi_active = 0;
- thread_id = (pthread_t)0;
- ERROR("ipmi plugin: pthread_create failed.");
- return -1;
+ c_ipmi_free_instance(st);
+ return status;
+ }
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Sensor", child->key) == 0)
+ ignorelist_add(st->ignorelist, ci->values[0].value.string);
+ else if (strcasecmp("IgnoreSelected", child->key) == 0) {
+ _Bool t;
+ status = cf_util_get_boolean(child, &t);
+ if (status != 0)
+ break;
+ ignorelist_set_invert(st->ignorelist, /* invert = */ !t);
+ } else if (strcasecmp("NotifyIPMIConnectionState", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->notify_conn);
+ } else if (strcasecmp("NotifySensorAdd", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->notify_add);
+ } else if (strcasecmp("NotifySensorRemove", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->notify_remove);
+ } else if (strcasecmp("NotifySensorNotPresent", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->notify_notpresent);
+ } else if (strcasecmp("SELEnabled", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->sel_enabled);
+ } else if (strcasecmp("SELClearEvent", child->key) == 0) {
+ status = cf_util_get_boolean(child, &st->sel_clear_event);
+ } else if (strcasecmp("Host", child->key) == 0)
+ status = cf_util_get_string(child, &st->host);
+ else if (strcasecmp("Address", child->key) == 0)
+ status = cf_util_get_string(child, &st->connaddr);
+ else if (strcasecmp("Username", child->key) == 0)
+ status = cf_util_get_string(child, &st->username);
+ else if (strcasecmp("Password", child->key) == 0)
+ status = cf_util_get_string(child, &st->password);
+ else if (strcasecmp("AuthType", child->key) == 0) {
+ char tmp[8];
+ status = cf_util_get_string_buffer(child, tmp, sizeof(tmp));
+ if (status != 0)
+ break;
+
+ if (strcasecmp("MD5", tmp) == 0)
+ st->authtype = IPMI_AUTHTYPE_MD5;
+ else if (strcasecmp("rmcp+", tmp) == 0)
+ st->authtype = IPMI_AUTHTYPE_RMCP_PLUS;
+ else
+ WARNING("ipmi plugin: The value \"%s\" is not valid for the "
+ "\"AuthType\" option.",
+ tmp);
+ } else {
+ WARNING("ipmi plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ if (status != 0) {
+ c_ipmi_free_instance(st);
+ return status;
}
+ c_ipmi_add_instance(st);
+
return 0;
-} /* int c_ipmi_init */
+} /* int c_ipmi_config_add_instance */
+
+static int c_ipmi_config(oconfig_item_t *ci) {
+ _Bool have_instance_block = 0;
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Instance", child->key) == 0) {
+ int status = c_ipmi_config_add_instance(child);
+ if (status != 0)
+ return status;
+
+ have_instance_block = 1;
+ } else if (!have_instance_block) {
+ /* Non-instance option: Assume legacy configuration (without <Instance />
+ * blocks) and call c_ipmi_config_add_instance with the <Plugin /> block.
+ */
+ WARNING("ipmi plugin: Legacy configuration found! Please update your "
+ "config file.");
+ return c_ipmi_config_add_instance(ci);
+ } else {
+ WARNING("ipmi plugin: The configuration option "
+ "\"%s\" is not allowed here. Did you "
+ "forget to add an <Instance /> block "
+ "around the configuration?",
+ child->key);
+ return -1;
+ }
+ } /* for (ci->children) */
+
+ return 0;
+} /* int c_ipmi_config */
-static int c_ipmi_read(void) {
- if ((c_ipmi_active == 0) || (thread_id == (pthread_t)0)) {
+static int c_ipmi_read(user_data_t *user_data) {
+ c_ipmi_instance_t *st = user_data->data;
+
+ if (st->active == 0) {
INFO("ipmi plugin: c_ipmi_read: I'm not active, returning false.");
return -1;
}
- sensor_list_read_all();
+ if (st->connected == 0)
+ return 0;
+
+ sensor_list_read_all(st);
- if (c_ipmi_init_in_progress > 0)
- c_ipmi_init_in_progress--;
+ if (st->init_in_progress > 0)
+ st->init_in_progress--;
else
- c_ipmi_init_in_progress = 0;
+ st->init_in_progress = 0;
return 0;
} /* int c_ipmi_read */
+static int c_ipmi_init(void) {
+ c_ipmi_instance_t *st;
+ char callback_name[3 * DATA_MAX_NAME_LEN];
+
+ if (os_handler != NULL) {
+ return 0;
+ }
+
+ os_handler = ipmi_posix_thread_setup_os_handler(SIGIO);
+ if (os_handler == NULL) {
+ ERROR("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
+ return -1;
+ }
+
+ os_handler->set_log_handler(os_handler, c_ipmi_log);
+
+ if (ipmi_init(os_handler) != 0) {
+ ERROR("ipmi plugin: ipmi_init() failed.");
+ os_handler->free_os_handler(os_handler);
+ return -1;
+ };
+
+ if (instances == NULL) {
+ /* No instances were configured, let's start a default instance. */
+ st = c_ipmi_init_instance();
+ if (st == NULL)
+ return ENOMEM;
+
+ c_ipmi_add_instance(st);
+ }
+
+ /* Don't send `ADD' notifications during startup (~ 1 minute) */
+ int cycles = 1 + (60 / CDTIME_T_TO_TIME_T(plugin_get_interval()));
+
+ st = instances;
+ while (NULL != st) {
+ /* The `st->name` is used as "domain name" for ipmi_open_domain().
+ * That value should be unique, so we do plugin_register_complex_read()
+ * at first as it checks the uniqueness. */
+ snprintf(callback_name, sizeof(callback_name), "ipmi/%s", st->name);
+
+ user_data_t ud = {
+ .data = st,
+ };
+
+ int status = plugin_register_complex_read(
+ /* group = */ "ipmi",
+ /* name = */ callback_name,
+ /* callback = */ c_ipmi_read,
+ /* interval = */ 0,
+ /* user_data = */ &ud);
+
+ if (status != 0) {
+ st = st->next;
+ continue;
+ }
+
+ st->init_in_progress = cycles;
+ st->active = 1;
+
+ status = plugin_thread_create(&st->thread_id, /* attr = */ NULL,
+ c_ipmi_thread_main,
+ /* user data = */ (void *)st, "ipmi");
+
+ if (status != 0) {
+ st->active = 0;
+ st->thread_id = (pthread_t){0};
+
+ plugin_unregister_read(callback_name);
+
+ ERROR("ipmi plugin: pthread_create failed for `%s`.", callback_name);
+ }
+
+ st = st->next;
+ }
+
+ return 0;
+} /* int c_ipmi_init */
+
static int c_ipmi_shutdown(void) {
- c_ipmi_active = 0;
+ c_ipmi_instance_t *st = instances;
+ instances = NULL;
+
+ while (st != NULL) {
+ c_ipmi_instance_t *next = st->next;
+
+ st->next = NULL;
+ st->active = 0;
+
+ if (!pthread_equal(st->thread_id, (pthread_t){0})) {
+ pthread_join(st->thread_id, NULL);
+ st->thread_id = (pthread_t){0};
+ }
+
+ sensor_list_remove_all(st);
+ c_ipmi_free_instance(st);
- if (thread_id != (pthread_t)0) {
- pthread_join(thread_id, NULL);
- thread_id = (pthread_t)0;
+ st = next;
}
- sensor_list_remove_all();
+ os_handler->free_os_handler(os_handler);
+ os_handler = NULL;
return 0;
} /* int c_ipmi_shutdown */
void module_register(void) {
- plugin_register_config("ipmi", c_ipmi_config, config_keys, config_keys_num);
+ plugin_register_complex_config("ipmi", c_ipmi_config);
plugin_register_init("ipmi", c_ipmi_init);
- plugin_register_read("ipmi", c_ipmi_read);
plugin_register_shutdown("ipmi", c_ipmi_shutdown);
} /* void module_register */
* Florian octo Forster <octo at collectd.org>
**/
-#if HAVE_CONFIG_H
#include "config.h"
-#endif
#if !defined(__GNUC__) || !__GNUC__
#define __attribute__(x) /**/
#ifndef LIBCOLLECTD_COLLECTDCLIENT_H
#define LIBCOLLECTD_COLLECTDCLIENT_H 1
-#include "lcc_features.h"
+#include "collectd/lcc_features.h"
+#include "collectd/types.h"
/* COLLECTD_TRACE is the environment variable used to control trace output. When
* set to something non-zero, all lines sent to / received from the daemon are
/*
* Includes (for data types)
*/
-#include <stdint.h>
#include <inttypes.h>
+#include <stdint.h>
#include <time.h>
-/*
- * Defines
- */
-#define LCC_NAME_LEN 64
-#define LCC_DEFAULT_PORT "25826"
-
-/*
- * Types
- */
-#define LCC_TYPE_COUNTER 0
-#define LCC_TYPE_GAUGE 1
-#define LCC_TYPE_DERIVE 2
-#define LCC_TYPE_ABSOLUTE 3
-
LCC_BEGIN_DECLS
-typedef uint64_t counter_t;
-typedef double gauge_t;
-typedef uint64_t derive_t;
-typedef uint64_t absolute_t;
-
-union value_u {
- counter_t counter;
- gauge_t gauge;
- derive_t derive;
- absolute_t absolute;
-};
-typedef union value_u value_t;
-
-struct lcc_identifier_s {
- char host[LCC_NAME_LEN];
- char plugin[LCC_NAME_LEN];
- char plugin_instance[LCC_NAME_LEN];
- char type[LCC_NAME_LEN];
- char type_instance[LCC_NAME_LEN];
-};
-typedef struct lcc_identifier_s lcc_identifier_t;
-#define LCC_IDENTIFIER_INIT \
- { "localhost", "", "", "", "" }
-
-struct lcc_value_list_s {
- value_t *values;
- int *values_types;
- size_t values_len;
- double time;
- double interval;
- lcc_identifier_t identifier;
-};
-typedef struct lcc_value_list_s lcc_value_list_t;
-#define LCC_VALUE_LIST_INIT \
- { NULL, NULL, 0, 0, 0, LCC_IDENTIFIER_INIT }
-
struct lcc_connection_s;
typedef struct lcc_connection_s lcc_connection_t;
#ifndef LIBCOLLECTDCLIENT_NETWORK_H
#define LIBCOLLECTDCLIENT_NETWORK_H 1
+#include "collectd/client.h"
+
#include <inttypes.h>
#include <stdint.h>
-#include "client.h"
-
#define NET_DEFAULT_V4_ADDR "239.192.74.66"
#define NET_DEFAULT_V6_ADDR "ff18::efc0:4a42"
#define NET_DEFAULT_PORT "25826"
#ifndef LIBCOLLECTDCLIENT_NETWORK_BUFFER_H
#define LIBCOLLECTDCLIENT_NETWORK_BUFFER_H 1
-/* FIXME */
-#include "client.h"
-#include "network.h"
+#include "collectd/network.h" /* for lcc_security_level_t */
+#include "collectd/types.h"
/* Ethernet frame - (IPv6 header + UDP header) */
#define LCC_NETWORK_BUFFER_SIZE_DEFAULT 1452
--- /dev/null
+/**
+ * Copyright 2017 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef LIBCOLLECTD_NETWORK_PARSE_H
+#define LIBCOLLECTD_NETWORK_PARSE_H 1
+
+#include "collectd/lcc_features.h"
+
+#include "collectd/network.h" /* for lcc_security_level_t */
+#include "collectd/types.h"
+
+#include <stdint.h>
+
+LCC_BEGIN_DECLS
+
+typedef struct {
+ /* writer is the callback used to send incoming lcc_value_list_t to. */
+ lcc_value_list_writer_t writer;
+
+ /* password_lookup is used to look up the password for a given username. */
+ lcc_password_lookup_t password_lookup;
+
+ /* security_level is the minimal required security level. */
+ lcc_security_level_t security_level;
+} lcc_network_parse_options_t;
+
+/* lcc_network_parse parses data received from the network and calls "w" with
+ * the parsed lcc_value_list_ts. */
+int lcc_network_parse(void *buffer, size_t buffer_size,
+ lcc_network_parse_options_t opts);
+
+LCC_END_DECLS
+
+#endif /* LIBCOLLECTD_NETWORK_PARSE_H */
--- /dev/null
+/**
+ * Copyright 2017 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef LIBCOLLECTD_SERVER_H
+#define LIBCOLLECTD_SERVER_H 1
+
+#include "collectd/lcc_features.h"
+
+#include "collectd/network.h" /* for lcc_security_level_t */
+#include "collectd/network_parse.h" /* for lcc_network_parse_options_t */
+#include "collectd/types.h"
+
+#include <stdint.h>
+
+#ifndef LCC_NETWORK_BUFFER_SIZE
+#define LCC_NETWORK_BUFFER_SIZE 1452
+#endif
+
+LCC_BEGIN_DECLS
+
+/* lcc_network_parser_t is a callback that parses received network packets. It
+ * is expected to call lcc_network_parse_options_t.writer with each
+ * lcc_value_list_t it parses that has the required security level. */
+typedef int (*lcc_network_parser_t)(void *payload, size_t payload_size,
+ lcc_network_parse_options_t opts);
+
+/* lcc_listener_t holds parameters for running a collectd server. */
+typedef struct {
+ /* conn is a UDP socket for the server to listen on. If set to <0 node and
+ * service will be used to open a new UDP socket. If >=0, it is the caller's
+ * job to clean up the socket. */
+ int conn;
+
+ /* node is the local address to listen on if conn is <0. Defaults to "::" (any
+ * address). */
+ char *node;
+
+ /* service is the local address to listen on if conn is <0. Defaults to
+ * LCC_DEFAULT_PORT. */
+ char *service;
+
+ /* parser is the callback used to parse incoming network packets. Defaults to
+ * lcc_network_parse() if set to NULL. */
+ lcc_network_parser_t parser;
+
+ /* parse_options contains options for parser and is passed on verbatimely. */
+ lcc_network_parse_options_t parse_options;
+
+ /* buffer_size determines the maximum packet size to accept. Defaults to
+ * LCC_NETWORK_BUFFER_SIZE if set to zero. */
+ uint16_t buffer_size;
+
+ /* interface is the name of the interface to use when subscribing to a
+ * multicast group. Has no effect when using unicast. */
+ char *interface;
+} lcc_listener_t;
+
+/* lcc_listen_and_write listens on the provided UDP socket (or opens one using
+ * srv.addr if srv.conn is less than zero), parses the received packets and
+ * writes them to the provided lcc_value_list_writer_t. Returns non-zero on
+ * failure and does not return otherwise. */
+int lcc_listen_and_write(lcc_listener_t srv);
+
+LCC_END_DECLS
+
+#endif /* LIBCOLLECTD_SERVER_H */
--- /dev/null
+/**
+ * libcollectdclient - src/libcollectdclient/collectd/types.h
+ * Copyright (C) 2008-2017 Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#ifndef LIBCOLLECTD_COLLECTD_TYPES_H
+#define LIBCOLLECTD_COLLECTD_TYPES_H 1
+
+#include "collectd/lcc_features.h"
+
+#include <stdint.h> /* for uint64_t */
+#include <sys/types.h> /* for size_t */
+
+/*
+ * Defines
+ */
+#define LCC_NAME_LEN 64
+#define LCC_DEFAULT_PORT "25826"
+
+/*
+ * Types
+ */
+#define LCC_TYPE_COUNTER 0
+#define LCC_TYPE_GAUGE 1
+#define LCC_TYPE_DERIVE 2
+#define LCC_TYPE_ABSOLUTE 3
+
+LCC_BEGIN_DECLS
+
+typedef uint64_t counter_t;
+typedef double gauge_t;
+typedef uint64_t derive_t;
+typedef uint64_t absolute_t;
+
+union value_u {
+ counter_t counter;
+ gauge_t gauge;
+ derive_t derive;
+ absolute_t absolute;
+};
+typedef union value_u value_t;
+
+struct lcc_identifier_s {
+ char host[LCC_NAME_LEN];
+ char plugin[LCC_NAME_LEN];
+ char plugin_instance[LCC_NAME_LEN];
+ char type[LCC_NAME_LEN];
+ char type_instance[LCC_NAME_LEN];
+};
+typedef struct lcc_identifier_s lcc_identifier_t;
+#define LCC_IDENTIFIER_INIT \
+ { "localhost", "", "", "", "" }
+
+struct lcc_value_list_s {
+ value_t *values;
+ int *values_types;
+ size_t values_len;
+ double time;
+ double interval;
+ lcc_identifier_t identifier;
+};
+typedef struct lcc_value_list_s lcc_value_list_t;
+#define LCC_VALUE_LIST_INIT \
+ { NULL, NULL, 0, 0, 0, LCC_IDENTIFIER_INIT }
+
+/* lcc_value_list_writer_t is a write callback to which value lists are
+ * dispatched. */
+typedef int (*lcc_value_list_writer_t)(lcc_value_list_t const *);
+
+/* lcc_password_lookup_t is a callback for looking up the password for a given
+ * user. Must return NULL if the user is not known. */
+typedef char const *(*lcc_password_lookup_t)(char const *);
+
+LCC_END_DECLS
+
+#endif /* LIBCOLLECTD_COLLECTD_TYPES_H */
--- /dev/null
+/**
+ * Copyright 2017 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "config.h"
+
+#if !defined(__GNUC__) || !__GNUC__
+#define __attribute__(x) /**/
+#endif
+
+#include "collectd/lcc_features.h"
+#include "collectd/network_parse.h"
+
+#include <errno.h>
+#include <math.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* for be{16,64}toh */
+#if HAVE_ENDIAN_H
+#include <endian.h>
+#elif HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#endif
+
+#if HAVE_GCRYPT_H
+#define GCRYPT_NO_DEPRECATED
+#include <gcrypt.h>
+#endif
+
+#include <stdio.h>
+#define DEBUG(...) printf(__VA_ARGS__)
+
+#if HAVE_GCRYPT_H
+#if GCRYPT_VERSION_NUMBER < 0x010600
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+#endif
+
+/* forward declaration because parse_sign_sha256()/parse_encrypt_aes256() and
+ * network_parse() need to call each other. */
+static int network_parse(void *data, size_t data_size, lcc_security_level_t sl,
+ lcc_network_parse_options_t const *opts);
+
+#if HAVE_GCRYPT_H
+static int init_gcrypt() {
+ /* http://lists.gnupg.org/pipermail/gcrypt-devel/2003-August/000458.html
+ * Because you can't know in a library whether another library has
+ * already initialized the library */
+ if (gcry_control(GCRYCTL_ANY_INITIALIZATION_P))
+ return (0);
+
+/* http://www.gnupg.org/documentation/manuals/gcrypt/Multi_002dThreading.html
+ * To ensure thread-safety, it's important to set GCRYCTL_SET_THREAD_CBS
+ * *before* initalizing Libgcrypt with gcry_check_version(), which itself must
+ * be called before any other gcry_* function. GCRYCTL_ANY_INITIALIZATION_P
+ * above doesn't count, as it doesn't implicitly initalize Libgcrypt.
+ *
+ * tl;dr: keep all these gry_* statements in this exact order please. */
+#if GCRYPT_VERSION_NUMBER < 0x010600
+ if (gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread)) {
+ return -1;
+ }
+#endif
+
+ gcry_check_version(NULL);
+
+ if (gcry_control(GCRYCTL_INIT_SECMEM, 32768)) {
+ return -1;
+ }
+
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED);
+ return 0;
+}
+#endif
+
+typedef struct {
+ uint8_t *data;
+ size_t len;
+} buffer_t;
+
+static int buffer_next(buffer_t *b, void *out, size_t n) {
+ if (b->len < n) {
+ return -1;
+ }
+ memmove(out, b->data, n);
+
+ b->data += n;
+ b->len -= n;
+
+ return 0;
+}
+
+static int buffer_uint16(buffer_t *b, uint16_t *out) {
+ uint16_t tmp;
+ if (buffer_next(b, &tmp, sizeof(tmp)) != 0)
+ return -1;
+
+ *out = be16toh(tmp);
+ return 0;
+}
+
+#define TYPE_HOST 0x0000
+#define TYPE_TIME 0x0001
+#define TYPE_TIME_HR 0x0008
+#define TYPE_PLUGIN 0x0002
+#define TYPE_PLUGIN_INSTANCE 0x0003
+#define TYPE_TYPE 0x0004
+#define TYPE_TYPE_INSTANCE 0x0005
+#define TYPE_VALUES 0x0006
+#define TYPE_INTERVAL 0x0007
+#define TYPE_INTERVAL_HR 0x0009
+#define TYPE_SIGN_SHA256 0x0200
+#define TYPE_ENCR_AES256 0x0210
+
+static int parse_int(void *payload, size_t payload_size, uint64_t *out) {
+ uint64_t tmp;
+
+ if (payload_size != sizeof(tmp))
+ return EINVAL;
+
+ memmove(&tmp, payload, sizeof(tmp));
+ *out = be64toh(tmp);
+ return 0;
+}
+
+static int parse_string(void *payload, size_t payload_size, char *out,
+ size_t out_size) {
+ char *in = payload;
+
+ if ((payload_size < 1) || (in[payload_size - 1] != 0) ||
+ (payload_size > out_size))
+ return EINVAL;
+
+ strncpy(out, in, out_size);
+ return 0;
+}
+
+static int parse_identifier(uint16_t type, void *payload, size_t payload_size,
+ lcc_value_list_t *state) {
+ char buf[LCC_NAME_LEN];
+
+ if (parse_string(payload, payload_size, buf, sizeof(buf)) != 0)
+ return EINVAL;
+
+ switch (type) {
+ case TYPE_HOST:
+ memmove(state->identifier.host, buf, LCC_NAME_LEN);
+ break;
+ case TYPE_PLUGIN:
+ memmove(state->identifier.plugin, buf, LCC_NAME_LEN);
+ break;
+ case TYPE_PLUGIN_INSTANCE:
+ memmove(state->identifier.plugin_instance, buf, LCC_NAME_LEN);
+ break;
+ case TYPE_TYPE:
+ memmove(state->identifier.type, buf, LCC_NAME_LEN);
+ break;
+ case TYPE_TYPE_INSTANCE:
+ memmove(state->identifier.type_instance, buf, LCC_NAME_LEN);
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+static int parse_time(uint16_t type, void *payload, size_t payload_size,
+ lcc_value_list_t *state) {
+ uint64_t tmp = 0;
+ if (parse_int(payload, payload_size, &tmp))
+ return EINVAL;
+
+ double t = (double)tmp;
+ switch (type) {
+ case TYPE_INTERVAL:
+ state->interval = t;
+ break;
+ case TYPE_INTERVAL_HR:
+ state->interval = t / 1073741824.0;
+ break;
+ case TYPE_TIME:
+ state->time = t;
+ break;
+ case TYPE_TIME_HR:
+ state->time = t / 1073741824.0;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+static double ntohd(double val) /* {{{ */
+{
+ static int config = 0;
+
+ union {
+ uint8_t byte[8];
+ double floating;
+ } in = {
+ .floating = val,
+ };
+ union {
+ uint8_t byte[8];
+ double floating;
+ } out = {
+ .byte = {0},
+ };
+
+ if (config == 0) {
+ double d = 8.642135e130;
+ uint8_t b[8];
+
+ memcpy(b, &d, sizeof(b));
+
+ if ((b[0] == 0x2f) && (b[1] == 0x25) && (b[2] == 0xc0) && (b[3] == 0xc7) &&
+ (b[4] == 0x43) && (b[5] == 0x2b) && (b[6] == 0x1f) && (b[7] == 0x5b))
+ config = 1; /* need nothing */
+ else if ((b[7] == 0x2f) && (b[6] == 0x25) && (b[5] == 0xc0) &&
+ (b[4] == 0xc7) && (b[3] == 0x43) && (b[2] == 0x2b) &&
+ (b[1] == 0x1f) && (b[0] == 0x5b))
+ config = 2; /* endian flip */
+ else if ((b[4] == 0x2f) && (b[5] == 0x25) && (b[6] == 0xc0) &&
+ (b[7] == 0xc7) && (b[0] == 0x43) && (b[1] == 0x2b) &&
+ (b[2] == 0x1f) && (b[3] == 0x5b))
+ config = 3; /* int swap */
+ else
+ config = 4;
+ }
+
+ if (memcmp((char[]){0, 0, 0, 0, 0, 0, 0xf8, 0x7f}, in.byte, 8) == 0) {
+ return NAN;
+ } else if (config == 1) {
+ return val;
+ } else if (config == 2) {
+ in.floating = val;
+ out.byte[0] = in.byte[7];
+ out.byte[1] = in.byte[6];
+ out.byte[2] = in.byte[5];
+ out.byte[3] = in.byte[4];
+ out.byte[4] = in.byte[3];
+ out.byte[5] = in.byte[2];
+ out.byte[6] = in.byte[1];
+ out.byte[7] = in.byte[0];
+ return (out.floating);
+ } else if (config == 3) {
+ in.floating = val;
+ out.byte[0] = in.byte[4];
+ out.byte[1] = in.byte[5];
+ out.byte[2] = in.byte[6];
+ out.byte[3] = in.byte[7];
+ out.byte[4] = in.byte[0];
+ out.byte[5] = in.byte[1];
+ out.byte[6] = in.byte[2];
+ out.byte[7] = in.byte[3];
+ return out.floating;
+ } else {
+ /* If in doubt, just copy the value back to the caller. */
+ return val;
+ }
+} /* }}} double ntohd */
+
+static int parse_values(void *payload, size_t payload_size,
+ lcc_value_list_t *state) {
+ buffer_t *b = &(buffer_t){
+ .data = payload, .len = payload_size,
+ };
+
+ uint16_t n;
+ if (buffer_uint16(b, &n))
+ return EINVAL;
+
+ if (((size_t)n * 9) != b->len)
+ return EINVAL;
+
+ state->values_len = (size_t)n;
+ state->values = calloc(sizeof(*state->values), state->values_len);
+ state->values_types = calloc(sizeof(*state->values_types), state->values_len);
+ if ((state->values == NULL) || (state->values_types == NULL)) {
+ return ENOMEM;
+ }
+
+ for (uint16_t i = 0; i < n; i++) {
+ uint8_t tmp;
+ if (buffer_next(b, &tmp, sizeof(tmp)))
+ return EINVAL;
+ state->values_types[i] = (int)tmp;
+ }
+
+ for (uint16_t i = 0; i < n; i++) {
+ uint64_t tmp;
+ if (buffer_next(b, &tmp, sizeof(tmp)))
+ return EINVAL;
+
+ if (state->values_types[i] == LCC_TYPE_GAUGE) {
+ union {
+ uint64_t i;
+ double d;
+ } conv = {.i = tmp};
+ state->values[i].gauge = ntohd(conv.d);
+ continue;
+ }
+
+ tmp = be64toh(tmp);
+ switch (state->values_types[i]) {
+ case LCC_TYPE_COUNTER:
+ state->values[i].counter = (counter_t)tmp;
+ break;
+ case LCC_TYPE_DERIVE:
+ state->values[i].derive = (derive_t)tmp;
+ break;
+ case LCC_TYPE_ABSOLUTE:
+ state->values[i].absolute = (absolute_t)tmp;
+ break;
+ default:
+ return EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+#if HAVE_GCRYPT_H
+static int verify_sha256(void *payload, size_t payload_size,
+ char const *username, char const *password,
+ uint8_t hash_provided[32]) {
+ gcry_md_hd_t hd = NULL;
+
+ gcry_error_t err = gcry_md_open(&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err != 0) {
+ return (int)err;
+ }
+
+ err = gcry_md_setkey(hd, password, strlen(password));
+ if (err != 0) {
+ gcry_md_close(hd);
+ return (int)err;
+ }
+
+ gcry_md_write(hd, username, strlen(username));
+ gcry_md_write(hd, payload, payload_size);
+
+ unsigned char *hash_calculated = gcry_md_read(hd, GCRY_MD_SHA256);
+ if (!hash_calculated) {
+ gcry_md_close(hd);
+ return -1;
+ }
+
+ int ret = memcmp(hash_provided, hash_calculated, 32);
+
+ gcry_md_close(hd);
+ hash_calculated = NULL;
+
+ return !!ret;
+}
+#else /* !HAVE_GCRYPT_H */
+static int verify_sha256(void *payload, size_t payload_size,
+ char const *username, char const *password,
+ uint8_t hash_provided[32]) {
+ return ENOTSUP;
+}
+#endif
+
+static int parse_sign_sha256(void *signature, size_t signature_len,
+ void *payload, size_t payload_size,
+ lcc_network_parse_options_t const *opts) {
+ if (opts->password_lookup == NULL) {
+ /* The sender signed the packet but we can't verify it. Handle it as if it
+ * were unsigned, i.e. security level NONE. */
+ return network_parse(payload, payload_size, NONE, opts);
+ }
+
+ buffer_t *b = &(buffer_t){
+ .data = signature, .len = signature_len,
+ };
+
+ uint8_t hash[32];
+ if (buffer_next(b, hash, sizeof(hash)))
+ return EINVAL;
+
+ char username[b->len + 1];
+ memset(username, 0, sizeof(username));
+ if (buffer_next(b, username, sizeof(username) - 1)) {
+ return EINVAL;
+ }
+
+ char const *password = opts->password_lookup(username);
+ if (!password)
+ return network_parse(payload, payload_size, NONE, opts);
+
+ int status = verify_sha256(payload, payload_size, username, password, hash);
+ if (status != 0)
+ return status;
+
+ return network_parse(payload, payload_size, SIGN, opts);
+}
+
+#if HAVE_GCRYPT_H
+static int decrypt_aes256(buffer_t *b, void *iv, size_t iv_size,
+ char const *password) {
+ gcry_cipher_hd_t cipher = NULL;
+
+ if (gcry_cipher_open(&cipher, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB,
+ /* flags = */ 0))
+ return -1;
+
+ uint8_t pwhash[32] = {0};
+ gcry_md_hash_buffer(GCRY_MD_SHA256, pwhash, password, strlen(password));
+
+ fprintf(stderr, "sizeof(iv) = %zu\n", sizeof(iv));
+ if (gcry_cipher_setkey(cipher, pwhash, sizeof(pwhash)) ||
+ gcry_cipher_setiv(cipher, iv, iv_size) ||
+ gcry_cipher_decrypt(cipher, b->data, b->len, /* in = */ NULL,
+ /* in_size = */ 0)) {
+ gcry_cipher_close(cipher);
+ return -1;
+ }
+
+ gcry_cipher_close(cipher);
+ return 0;
+}
+
+static int parse_encrypt_aes256(void *data, size_t data_size,
+ lcc_network_parse_options_t const *opts) {
+ if (opts->password_lookup == NULL) {
+ /* Without a password source it's (hopefully) impossible to decrypt the
+ * network packet. */
+ return ENOENT;
+ }
+
+ buffer_t *b = &(buffer_t){
+ .data = data, .len = data_size,
+ };
+
+ uint16_t username_len;
+ if (buffer_uint16(b, &username_len))
+ return EINVAL;
+ if ((size_t)username_len > data_size)
+ return ENOMEM;
+ char username[((size_t)username_len) + 1];
+ memset(username, 0, sizeof(username));
+ if (buffer_next(b, username, (size_t)username_len))
+ return EINVAL;
+
+ char const *password = opts->password_lookup(username);
+ if (!password)
+ return ENOENT;
+
+ uint8_t iv[16];
+ if (buffer_next(b, iv, sizeof(iv)))
+ return EINVAL;
+
+ int status = decrypt_aes256(b, iv, sizeof(iv), password);
+ if (status != 0)
+ return status;
+
+ uint8_t hash_provided[20];
+ if (buffer_next(b, hash_provided, sizeof(hash_provided))) {
+ return -1;
+ }
+
+ uint8_t hash_calculated[20];
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hash_calculated, b->data, b->len);
+
+ if (memcmp(hash_provided, hash_calculated, sizeof(hash_provided)) != 0) {
+ return -1;
+ }
+
+ return network_parse(b->data, b->len, ENCRYPT, opts);
+}
+#else /* !HAVE_GCRYPT_H */
+static int parse_encrypt_aes256(void *data, size_t data_size,
+ lcc_network_parse_options_t const *opts) {
+ return ENOTSUP;
+}
+#endif
+
+static int network_parse(void *data, size_t data_size, lcc_security_level_t sl,
+ lcc_network_parse_options_t const *opts) {
+ buffer_t *b = &(buffer_t){
+ .data = data, .len = data_size,
+ };
+
+ lcc_value_list_t state = {0};
+
+ while (b->len > 0) {
+ uint16_t type = 0, sz = 0;
+ if (buffer_uint16(b, &type) || buffer_uint16(b, &sz)) {
+ DEBUG("lcc_network_parse(): reading type and/or length failed.\n");
+ return EINVAL;
+ }
+
+ if ((sz < 5) || (((size_t)sz - 4) > b->len)) {
+ DEBUG("lcc_network_parse(): invalid 'sz' field: sz = %" PRIu16
+ ", b->len = %zu\n",
+ sz, b->len);
+ return EINVAL;
+ }
+ sz -= 4;
+
+ uint8_t payload[sz];
+ if (buffer_next(b, payload, sizeof(payload)))
+ return EINVAL;
+
+ switch (type) {
+ case TYPE_HOST:
+ case TYPE_PLUGIN:
+ case TYPE_PLUGIN_INSTANCE:
+ case TYPE_TYPE:
+ case TYPE_TYPE_INSTANCE: {
+ if (parse_identifier(type, payload, sizeof(payload), &state)) {
+ DEBUG("lcc_network_parse(): parse_identifier failed.\n");
+ return EINVAL;
+ }
+ break;
+ }
+
+ case TYPE_INTERVAL:
+ case TYPE_INTERVAL_HR:
+ case TYPE_TIME:
+ case TYPE_TIME_HR: {
+ if (parse_time(type, payload, sizeof(payload), &state)) {
+ DEBUG("lcc_network_parse(): parse_time failed.\n");
+ return EINVAL;
+ }
+ break;
+ }
+
+ case TYPE_VALUES: {
+ lcc_value_list_t vl = state;
+ if (parse_values(payload, sizeof(payload), &vl)) {
+ free(vl.values);
+ free(vl.values_types);
+ DEBUG("lcc_network_parse(): parse_values failed.\n");
+ return EINVAL;
+ }
+
+ int status = 0;
+
+ /* Write metrics if they have the required security level. */
+ if (sl >= opts->security_level)
+ status = opts->writer(&vl);
+
+ free(vl.values);
+ free(vl.values_types);
+
+ if (status != 0)
+ return status;
+ break;
+ }
+
+ case TYPE_SIGN_SHA256: {
+ int status =
+ parse_sign_sha256(payload, sizeof(payload), b->data, b->len, opts);
+ if (status != 0) {
+ DEBUG("lcc_network_parse(): parse_sign_sha256() = %d\n", status);
+ return -1;
+ }
+ /* parse_sign_sha256, if successful, consumes all remaining data. */
+ b->data = NULL;
+ b->len = 0;
+ break;
+ }
+
+ case TYPE_ENCR_AES256: {
+ int status = parse_encrypt_aes256(payload, sizeof(payload), opts);
+ if (status != 0) {
+ DEBUG("lcc_network_parse(): parse_encrypt_aes256() = %d\n", status);
+ return -1;
+ }
+ break;
+ }
+
+ default: {
+ DEBUG("lcc_network_parse(): ignoring unknown type %" PRIu16 "\n", type);
+ return EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int lcc_network_parse(void *data, size_t data_size,
+ lcc_network_parse_options_t opts) {
+ if (opts.password_lookup) {
+#if HAVE_GCRYPT_H
+ int status;
+ if ((status = init_gcrypt())) {
+ return status;
+ }
+#else
+ return ENOTSUP;
+#endif
+ }
+
+ return network_parse(data, data_size, NONE, &opts);
+}
--- /dev/null
+/**
+ * Copyright 2017 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd/lcc_features.h"
+
+#include "collectd/network_buffer.h" /* for LCC_NETWORK_BUFFER_SIZE_DEFAULT */
+
+#include <assert.h>
+
+#include "network_parse.c" /* sic */
+
+char *raw_packet_data[] = {
+ "0000000e6c6f63616c686f7374000008000c1513676ac3a6e0970009000c00000002800000"
+ "000002000973776170000004000973776170000005000966726565000006000f0001010000"
+ "0080ff610f420008000c1513676ac3a8fc120004000c737761705f696f0000050007696e00"
+ "0006000f00010200000000000000000008000c1513676ac3a9077d000500086f7574000006"
+ "000f00010200000000000000000008000c1513676ac3bd2a8c0002000e696e746572666163"
+ "65000003000965746830000004000e69665f6f637465747300000500050000060018000202"
+ "02000000000000000000000000000000000008000c1513676ac3bd5a970004000e69665f65"
+ "72726f7273000006001800020202000000000000000000000000000000000008000c151367"
+ "6ac3bd7fea000300076c6f000004000e69665f6f6374657473000006001800020202000000"
+ "000009e79c000000000009e79c0008000c1513676ac3bdaae60003000a776c616e30000006"
+ "001800020202000000001009fa5400000000011cf6670008000c1513676ac3bdb0e0000400"
+ "0e69665f6572726f7273000006001800020202000000000000000000000000000000000008"
+ "000c1513676ac3bd3d6d0003000965746830000004000f69665f7061636b65747300000600"
+ "1800020202000000000000000000000000000000000008000c1513676ac3bdae290003000a"
+ "776c616e300000060018000202020000000000032f8f00000000000205e50008000c151367"
+ "6ac3bdbb7b0003000c646f636b657230000006001800020202000000000000000000000000"
+ "000000000008000c1513676ac3bda0db000300076c6f000004000e69665f6572726f727300"
+ "0006001800020202000000000000000000000000000000000008000c1513676ac3bdbde800"
+ "03000c646f636b657230000006001800020202000000000000000000000000000000000008"
+ "000c1513676ac3bd8d8e000300076c6f000004000f69665f7061636b657473000006001800"
+ "0202020000000000000c9c0000000000000c9c0008000c1513676ac3bdb90b0003000c646f"
+ "636b657230000004000e69665f6f6374657473000006001800020202000000000000000000"
+ "000000000000000008000c1513676ac469b10f0002000e70726f6365737365730000030005"
+ "000004000d70735f7374617465000005000c7a6f6d62696573000006000f00010100000000"
+ "000000000008000c1513676ac469a4a30005000d736c656570696e67000006000f00010100"
+ "00000000006e400008000c1513676ac469c6320005000b706167696e67000006000f000101"
+ "00000000000000000008000c1513676ac469f06e0005000c626c6f636b6564000006000f00"
+ "010100000000000000000008000c1513676ac4698af40005000c72756e6e696e6700000600"
+ "0f00010100000000000000000008000c1513676ac469bbe10005000c73746f707065640000"
+ "06000f00010100000000000000000008000c1513676ac46b8e710004000e666f726b5f7261"
+ "74650000050005000006000f0001020000000000001bcf0008000c1513676d437f12960002"
+ "00086370750000030006300000040008637075000005000b73797374656d000006000f0001"
+ "0200000000000021870008000c1513676d437f36020005000969646c65000006000f000102"
+ "000000000005847a0008000c1513676d437f979b0005000977616974000006000f00010200"
+ "000000000005210008000c1513676d43802ff60005000c736f6674697271000006000f0001"
+ "02000000000000001f0008000c1513676d43803b3a0005000a737465616c000006000f0001"
+ "020000000000000000",
+ "0000000e6c6f63616c686f7374000008000c1513676d4380551f0009000c00000002800000"
+ "00000200086370750000030006310000040008637075000005000975736572000006000f00"
+ "01020000000000007cad0008000c1513676d43805dbe000500096e696365000006000f0001"
+ "0200000000000001de0008000c1513676d4380697d0005000b73797374656d000006000f00"
+ "01020000000000001ce80008000c1513676d438072bd0005000969646c65000006000f0001"
+ "02000000000005931c0008000c1513676d43807c430005000977616974000006000f000102"
+ "000000000000094b0008000c1513676d43808cee0005000c736f6674697271000006000f00"
+ "010200000000000000120008000c1513676d4380843a0005000e696e746572727570740000"
+ "06000f00010200000000000000000008000c1513676d438096230005000a737465616c0000"
+ "06000f00010200000000000000000008000c1513676d4380aa9c0003000632000005000975"
+ "736572000006000f00010200000000000089580008000c1513676d4380b29f000500096e69"
+ "6365000006000f00010200000000000003610008000c1513676d4380c44c0005000969646c"
+ "65000006000f000102000000000005873d0008000c1513676d4380bc0f0005000b73797374"
+ "656d000006000f000102000000000000201d0008000c1513676d4380cea400050009776169"
+ "74000006000f00010200000000000005810008000c1513676d4380d7370005000e696e7465"
+ "7272757074000006000f00010200000000000000000008000c1513676d4380ea830005000a"
+ "737465616c000006000f00010200000000000000000008000c1513676d437eef6200030006"
+ "3000000500096e696365000006000f00010200000000000003920008000c1513676d4380e0"
+ "260003000632000005000c736f6674697271000006000f0001020000000000000016000800"
+ "0c1513676d438101410003000633000005000975736572000006000f000102000000000000"
+ "7d8a0008000c1513676d438109f5000500096e696365000006000f00010200000000000004"
+ "350008000c1513676d4380244b0003000630000005000e696e74657272757074000006000f"
+ "00010200000000000000000008000c1513676d438122070003000633000005000969646c65"
+ "000006000f0001020000000000058eb60008000c1513676d43812e83000500097761697400"
+ "0006000f0001020000000000000ca80008000c1513676d438141480005000c736f66746972"
+ "71000006000f000102000000000000001e0008000c1513676d43814a5d0005000a73746561"
+ "6c000006000f00010200000000000000000008000c1513676d4381149e0005000b73797374"
+ "656d000006000f0001020000000000001b9a0008000c1513676d437ea86000030006300000"
+ "05000975736572000006000f00010200000000000089a80008000c1513676d438138190003"
+ "000633000005000e696e74657272757074000006000f00010200000000000000000008000c"
+ "1513676d438a9ca00002000e696e74657266616365000003000965746830000004000e6966"
+ "5f6f6374657473000005000500000600180002020200000000000000000000000000000000"
+ "0008000c1513676d438aea760004000f69665f7061636b6574730000060018000202020000"
+ "00000000000000000000000000000008000c1513676d438b214d0004000e69665f6572726f"
+ "727300000600180002020200000000000000000000000000000000",
+ "0000000e6c6f63616c686f7374000008000c1513676d438aac590009000c00000002800000"
+ "000002000764660000030009726f6f74000004000f64665f636f6d706c6578000005000966"
+ "726565000006000f0001010000004c077e57420008000c1513676d438b6ada0005000d7265"
+ "736572766564000006000f00010100000000338116420008000c1513676d438b7a17000200"
+ "0e696e7465726661636500000300076c6f000004000e69665f6f6374657473000005000500"
+ "0006001800020202000000000009ecf5000000000009ecf50008000c1513676d438b757800"
+ "02000764660000030009726f6f74000004000f64665f636f6d706c65780000050009757365"
+ "64000006000f000101000000e0a41b26420008000c1513676d438b8ed20002000e696e7465"
+ "726661636500000300076c6f000004000e69665f6572726f72730000050005000006001800"
+ "020202000000000000000000000000000000000008000c1513676d438b86bf0004000f6966"
+ "5f7061636b6574730000060018000202020000000000000c9d0000000000000c9d0008000c"
+ "1513676d438bb3e60003000a776c616e300000060018000202020000000000032fab000000"
+ "00000205ed0008000c1513676d438bd62e0003000c646f636b657230000004000e69665f6f"
+ "6374657473000006001800020202000000000000000000000000000000000008000c151367"
+ "6d438bbc8f0003000a776c616e30000004000e69665f6572726f7273000006001800020202"
+ "000000000000000000000000000000000008000c1513676d438bdf030003000c646f636b65"
+ "7230000004000f69665f7061636b6574730000060018000202020000000000000000000000"
+ "00000000000008000c1513676d438baaf10003000a776c616e30000004000e69665f6f6374"
+ "65747300000600180002020200000000100a042300000000011cfa460008000c1513676d43"
+ "8c5f100002000764660000030009626f6f74000004000f64665f636f6d706c657800000500"
+ "0966726565000006000f0001010000000010e198410008000c1513676d438c689c0005000d"
+ "7265736572766564000006000f00010100000000804c68410008000c1513676d438c70ce00"
+ "05000975736564000006000f0001010000000020ea9e410008000c1513676d438be7bc0002"
+ "000e696e74657266616365000003000c646f636b657230000004000e69665f6572726f7273"
+ "0000050005000006001800020202000000000000000000000000000000000008000c151367"
+ "6d43beca8c0002000c656e74726f70790000030005000004000c656e74726f707900000600"
+ "0f0001010000000000088f400008000c1513676d43bf1d13000200096c6f61640000040009"
+ "6c6f6164000006002100030101019a9999999999a93f666666666666d63f5c8fc2f5285cdf"
+ "3f0008000c1513676d43c02b85000200096469736b00000300087364610000040010646973"
+ "6b5f6f63746574730000060018000202020000000075887800000000005b6f3c000008000c"
+ "1513676d43c06d1f0004000d6469736b5f6f7073000006001800020202000000000003cbbd"
+ "000000000001c0510008000c1513676d43c08b6a0004000e6469736b5f74696d6500000600"
+ "1800020202000000000000003f00000000000001720008000c1513676d43c0a5fb00040010"
+ "6469736b5f6d65726765640000060018000202020000000000001285000000000000f80100"
+ "08000c1513676d43c0c8b4000300097364613100000400106469736b5f6f63746574730000"
+ "060018000202020000000001107c000000000000003c00",
+ "0000000e6c6f63616c686f7374000008000c1513676d43c0d00a0009000c00000002800000"
+ "00000200096469736b000003000973646131000004000d6469736b5f6f7073000006001800"
+ "020202000000000000029b00000000000000080008000c1513676d43c0d7b20004000e6469"
+ "736b5f74696d650000060018000202020000000000000004000000000000000f0008000c15"
+ "13676d43c0df73000400106469736b5f6d6572676564000006001800020202000000000000"
+ "0fb400000000000000010008000c1513676d43c0f87c000300097364613200000400106469"
+ "736b5f6f637465747300000600180002020200000000000008000000000000000000000800"
+ "0c1513676d43c1003e0004000d6469736b5f6f707300000600180002020200000000000000"
+ "0200000000000000000008000c1513676d43c107bf000400106469736b5f6d657267656400"
+ "0006001800020202000000000000000000000000000000000008000c1513676d43c12fa400"
+ "03000973646135000004000d6469736b5f6f7073000006001800020202000000000003c867"
+ "000000000001aef20008000c1513676d43c13d5e000400106469736b5f6d65726765640000"
+ "0600180002020200000000000002d1000000000000f8000008000c1513676d43c136a90004"
+ "000e6469736b5f74696d65000006001800020202000000000000003f000000000000011c00"
+ "08000c1513676d43c1740500030009646d2d3000000400106469736b5f6f63746574730000"
+ "060018000202020000000074596400000000005b6f00000008000c1513676d43c179c70004"
+ "000d6469736b5f6f7073000006001800020202000000000003cae4000000000002b0f30008"
+ "000c1513676d43c18abe000400106469736b5f6d6572676564000006001800020202000000"
+ "000000000000000000000000000008000c1513676d43c181b90004000e6469736b5f74696d"
+ "650000060018000202020000000000000040000000000000013e0008000c1513676d43c1a9"
+ "5e00030009646d2d3100000400106469736b5f6f6374657473000006001800020202000000"
+ "00000e000000000000000000000008000c1513676d43c1b7ea0004000e6469736b5f74696d"
+ "65000006001800020202000000000000000200000000000000000008000c1513676d43c1b0"
+ "3e0004000d6469736b5f6f707300000600180002020200000000000000e000000000000000"
+ "000008000c1513676d43c1c00d000400106469736b5f6d6572676564000006001800020202"
+ "000000000000000000000000000000000008000c1513676d43c12818000300097364613500"
+ "000400106469736b5f6f637465747300000600180002020200000000746c6400000000005b"
+ "6f00000008000c1513676d43d320a80002000c62617474657279000003000630000004000b"
+ "636861726765000006000f0001018fc2f5285c2f58400008000c1513676d43d36fd6000400"
+ "0c63757272656e74000006000f00010100000000000000800008000c1513676d43d3cdb600"
+ "04000c766f6c74616765000006000f000101736891ed7cbf28400008000c1513676d43d59d"
+ "d60002000869727100000300050000040008697271000005000630000006000f0001020000"
+ "0000000000110008000c1513676d43d5d2cf0005000631000006000f000102000000000000"
+ "00100008000c1513676d43d5fe820005000638000006000f00010200000000000000010008"
+ "000c1513676d43d635440005000639000006000f00010200000000000035210008000c1513"
+ "676d43d66265000500073132000006000f0001020000000000000790",
+ "0000000e6c6f63616c686f7374000008000c1513676d43d68e940009000c00000002800000"
+ "0000020008697271000004000869727100000500073136000006000f000102000000000000"
+ "00210008000c1513676d43d69be20002000a7573657273000004000a757365727300000500"
+ "05000006000f00010100000000000010400008000c1513676d43d6aa5d0002000869727100"
+ "0004000869727100000500073233000006000f00010200000000000000250008000c151367"
+ "6d43d6c7dc000500073431000006000f000102000000000000ff7d0008000c1513676d43d6"
+ "e23d000500073432000006000f00010200000000000008070008000c1513676d43d9aa3a00"
+ "0500073437000006000f0001020000000000079a260008000c1513676d43d9cca900050007"
+ "3438000006000f00010200000000000000c70008000c1513676d43d9ea5d00050007343900"
+ "0006000f00010200000000000004c20008000c1513676d43da050e00050007353000000600"
+ "0f000102000000000000001c0008000c1513676d43da1efa000500084e4d49000006000f00"
+ "010200000000000000000008000c1513676d43da3c82000500084c4f43000006000f000102"
+ "000000000018d3080008000c1513676d43da544e00050008535055000006000f0001020000"
+ "0000000000000008000c1513676d43da6cca00050008504d49000006000f00010200000000"
+ "000000000008000c1513676d43da885400050008495749000006000f000102000000000000"
+ "a9da0008000c1513676d43daa23a00050008525452000006000f0001020000000000000003"
+ "0008000c1513676d43dabaed00050008524553000006000f00010200000000000ac8360008"
+ "000c1513676d43dad4150005000843414c000006000f000102000000000000191f0008000c"
+ "1513676d43daeef300050008544c42000006000f000102000000000003dbdc0008000c1513"
+ "676d43db11410005000854524d000006000f00010200000000000000000008000c1513676d"
+ "43db292c00050008544852000006000f00010200000000000000000008000c1513676d43db"
+ "411d000500084d4345000006000f00010200000000000000000008000c1513676d43db5b59"
+ "000500084d4350000006000f000102000000000000003c0008000c1513676d43db68010005"
+ "0008455252000006000f00010200000000000000000008000c1513676d43db758a00050008"
+ "4d4953000006000f00010200000000000000000008000c1513676d43dd2e800002000b6d65"
+ "6d6f7279000004000b6d656d6f7279000005000975736564000006000f00010100000000fe"
+ "bbe0410008000c1513676d43dd3f4b0005000d6275666665726564000006000f0001010000"
+ "000070fbc8410008000c1513676d43dd48700005000b636163686564000006000f00010100"
+ "000000c008df410008000c1513676d43dd51c60005000966726565000006000f0001010000"
+ "0080481d05420008000c1513676d43dec7e300020009737761700000040009737761700000"
+ "05000975736564000006000f00010100000000000000000008000c1513676d43ded4490005"
+ "000966726565000006000f00010100000080ff610f420008000c1513676d43dedcfd000500"
+ "0b636163686564000006000f00010100000000000000000008000c1513676d43d715e30002"
+ "0008697271000004000869727100000500073434000006000f0001020000000000031b6100"
+ "08000c1513676d43d73116000500073435000006000f00010200000000000000180008000c"
+ "1513676d43ee00150002000973776170000004000c737761705f696f0000050007696e0000"
+ "06000f0001020000000000000000",
+};
+
+static int decode_string(char const *in, uint8_t *out, size_t *out_size) {
+ size_t in_size = strlen(in);
+ if (*out_size < (in_size / 2))
+ return -1;
+ *out_size = in_size / 2;
+
+ for (size_t i = 0; i < *out_size; i++) {
+ char tmp[] = {in[2 * i], in[2 * i + 1], 0};
+ out[i] = (uint8_t)strtoul(tmp, NULL, 16);
+ }
+
+ return 0;
+}
+
+static int nop_writer(lcc_value_list_t const *vl) {
+ if (!strlen(vl->identifier.host) || !strlen(vl->identifier.plugin) ||
+ !strlen(vl->identifier.type)) {
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int test_network_parse() {
+ int ret = 0;
+
+ for (size_t i = 0; i < sizeof(raw_packet_data) / sizeof(raw_packet_data[0]);
+ i++) {
+ uint8_t buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
+ size_t buffer_size = sizeof(buffer);
+ if (decode_string(raw_packet_data[i], buffer, &buffer_size)) {
+ fprintf(
+ stderr,
+ "lcc_network_parse(raw_packet_data[%zu]): decoding string failed\n",
+ i);
+ return -1;
+ }
+
+ int status =
+ lcc_network_parse(buffer, buffer_size, (lcc_network_parse_options_t){
+ .writer = nop_writer,
+ });
+ if (status != 0) {
+ fprintf(stderr, "lcc_network_parse(raw_packet_data[%zu]) = %d, want 0\n",
+ i, status);
+ ret = status;
+ }
+
+ printf("ok - lcc_network_parse(raw_packet_data[%zu])\n", i);
+ }
+
+ return ret;
+}
+
+static int test_parse_time() {
+ int ret = 0;
+
+ struct {
+ uint64_t in;
+ double want;
+ } cases[] = {
+ {1439980823, 1439980823.0},
+ {1439981005, 1439981005.0},
+ {1439981150, 1439981150.0},
+ };
+
+ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+ lcc_value_list_t vl = LCC_VALUE_LIST_INIT;
+
+ uint64_t be = htobe64(cases[i].in);
+ int status = parse_time(TYPE_TIME, &be, sizeof(be), &vl);
+ if ((status != 0) || (vl.time != cases[i].want)) {
+ fprintf(stderr, "parse_time(%" PRIu64 ") = (%.0f, %d), want (%.0f, 0)\n",
+ cases[i].in, vl.time, status, cases[i].want);
+ ret = -1;
+ }
+ }
+
+ struct {
+ uint64_t in;
+ double want;
+ } cases_hr[] = {
+ {1546167635576736987, 1439980823.152453627},
+ {1546167831554815222, 1439981005.671262017},
+ {1546167986577716567, 1439981150.047589622},
+ };
+
+ for (size_t i = 0; i < sizeof(cases_hr) / sizeof(cases_hr[0]); i++) {
+ lcc_value_list_t vl = LCC_VALUE_LIST_INIT;
+
+ uint64_t be = htobe64(cases_hr[i].in);
+ int status = parse_time(TYPE_TIME_HR, &be, sizeof(be), &vl);
+ if ((status != 0) || (vl.time != cases_hr[i].want)) {
+ fprintf(stderr, "parse_time(%" PRIu64 ") = (%.9f, %d), want (%.9f, 0)\n",
+ cases_hr[i].in, vl.time, status, cases_hr[i].want);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static int test_parse_string() {
+ int ret = 0;
+
+ struct {
+ uint8_t *in;
+ size_t in_len;
+ char *want;
+ } cases[] = {
+ {(uint8_t[]){0}, 1, ""},
+ {(uint8_t[]){'t', 'e', 's', 't', 0}, 5, "test"},
+ {(uint8_t[]){'t', 'e', 's', 't'}, 4, NULL}, // null byte missing
+ {(uint8_t[]){'t', 'e', 's', 't', 'x', 0}, 6,
+ NULL}, // output buffer too small
+ };
+
+ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+ char got[5] = {0};
+
+ int status = parse_string(cases[i].in, cases[i].in_len, got, sizeof(got));
+ if (cases[i].want == NULL) {
+ if (status == 0) {
+ fprintf(stderr, "parse_string() = (\"%s\", 0), want error\n", got);
+ ret = -1;
+ }
+ } else /* if cases[i].want != NULL */ {
+ if (status != 0) {
+ fprintf(stderr, "parse_string() = %d, want 0\n", status);
+ ret = -1;
+ } else if (strcmp(got, cases[i].want) != 0) {
+ fprintf(stderr, "parse_string() = (\"%s\", 0), want (\"%s\", 0)\n", got,
+ cases[i].want);
+ ret = -1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int test_parse_values() {
+ int ret = 0;
+
+ uint8_t testcase[] = {
+ // 0, 6, // pkg type
+ // 0, 33, // pkg len
+ 0, 3, // num values
+ 1, 2, 1, // gauge, derive, gauge
+ 0, 0, 0, 0, 0, 0, 0x45, 0x40, // 42.0
+ 0, 0, 0, 0, 0, 0, 0x7a, 0x69, // 31337
+ 0, 0, 0, 0, 0, 0, 0xf8, 0x7f, // NaN
+ };
+
+ lcc_value_list_t vl = LCC_VALUE_LIST_INIT;
+ int status = parse_values(testcase, sizeof(testcase), &vl);
+ if (status != 0) {
+ fprintf(stderr, "parse_values() = %d, want 0\n", status);
+ return -1;
+ }
+
+ if (vl.values_len != 3) {
+ fprintf(stderr, "parse_values(): vl.values_len = %zu, want 3\n",
+ vl.values_len);
+ return -1;
+ }
+
+ int want_types[] = {LCC_TYPE_GAUGE, LCC_TYPE_DERIVE, LCC_TYPE_GAUGE};
+ for (size_t i = 0; i < sizeof(want_types) / sizeof(want_types[0]); i++) {
+ if (vl.values_types[i] != want_types[i]) {
+ fprintf(stderr, "parse_values(): vl.values_types[%zu] = %d, want %d\n", i,
+ vl.values_types[i], want_types[i]);
+ ret = -1;
+ }
+ }
+
+ if (vl.values[0].gauge != 42.0) {
+ fprintf(stderr, "parse_values(): vl.values[0] = %g, want 42\n",
+ vl.values[0].gauge);
+ ret = -1;
+ }
+ if (vl.values[1].derive != 31337) {
+ fprintf(stderr, "parse_values(): vl.values[1] = %" PRIu64 ", want 31337\n",
+ vl.values[1].derive);
+ ret = -1;
+ }
+ if (!isnan(vl.values[2].gauge)) {
+ fprintf(stderr, "parse_values(): vl.values[2] = %g, want NaN\n",
+ vl.values[2].gauge);
+ ret = -1;
+ }
+
+ free(vl.values);
+ free(vl.values_types);
+
+ return ret;
+}
+
+static int test_verify_sha256() {
+ int ret = 0;
+
+ int status = verify_sha256(
+ (char[]){'c', 'o', 'l', 'l', 'e', 'c', 't', 'd'}, 8, "admin", "admin",
+ (uint8_t[]){
+ 0xcd, 0xa5, 0x9a, 0x37, 0xb0, 0x81, 0xc2, 0x31, 0x24, 0x2a, 0x6d,
+ 0xbd, 0xfb, 0x44, 0xdb, 0xd7, 0x41, 0x2a, 0xf4, 0x29, 0x83, 0xde,
+ 0xa5, 0x11, 0x96, 0xd2, 0xe9, 0x30, 0x21, 0xae, 0xc5, 0x45,
+ });
+ if (status != 0) {
+ fprintf(stderr, "verify_sha256() = %d, want 0\n", status);
+ ret = -1;
+ }
+
+ status = verify_sha256(
+ (char[]){'c', 'o', 'l', 'l', 'E', 'c', 't', 'd'}, 8, "admin", "admin",
+ (uint8_t[]){
+ 0xcd, 0xa5, 0x9a, 0x37, 0xb0, 0x81, 0xc2, 0x31, 0x24, 0x2a, 0x6d,
+ 0xbd, 0xfb, 0x44, 0xdb, 0xd7, 0x41, 0x2a, 0xf4, 0x29, 0x83, 0xde,
+ 0xa5, 0x11, 0x96, 0xd2, 0xe9, 0x30, 0x21, 0xae, 0xc5, 0x45,
+ });
+ if (status != 1) {
+ fprintf(stderr, "verify_sha256() = %d, want 1\n", status);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int test_decrypt_aes256() {
+ char const *iv_str = "4cbe2a747c9f9dcfa0e66f0c2fa74875";
+ uint8_t iv[16] = {0};
+ size_t iv_len = sizeof(iv);
+
+ char const *ciphertext_str =
+ "8f023b0b15178f8428da1221a5f653e840f065db4aff032c22e5a3df";
+ uint8_t ciphertext[28] = {0};
+ size_t ciphertext_len = sizeof(ciphertext);
+
+ if (decode_string(iv_str, iv, &iv_len) ||
+ decode_string(ciphertext_str, ciphertext, &ciphertext_len)) {
+ fprintf(stderr, "test_decrypt_aes256: decode_string failed.\n");
+ return -1;
+ }
+ assert(iv_len == sizeof(iv));
+ assert(ciphertext_len == sizeof(ciphertext));
+
+ int status = decrypt_aes256(
+ &(buffer_t){
+ .data = ciphertext, .len = ciphertext_len,
+ },
+ iv, iv_len, "admin");
+ if (status != 0) {
+ fprintf(stderr, "decrypt_aes256() = %d, want 0\n", status);
+ return -1;
+ }
+
+ char const *want = "collectd";
+ char got[9] = {0};
+ memmove(got, &ciphertext[20], sizeof(got) - 1);
+ if (strcmp(got, want) != 0) {
+ fprintf(stderr, "decrypt_aes256() = \"%s\", want \"%s\"\n", got, want);
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(void) {
+ int ret = 0;
+
+ printf("libcollectdclient/server_test.c\n");
+
+ int status;
+ if ((status = test_network_parse())) {
+ ret = status;
+ }
+ if ((status = test_parse_time())) {
+ ret = status;
+ }
+ if ((status = test_parse_string())) {
+ ret = status;
+ }
+ if ((status = test_parse_values())) {
+ ret = status;
+ }
+
+ if ((status = test_verify_sha256())) {
+ ret = status;
+ }
+ if ((status = test_decrypt_aes256())) {
+ ret = status;
+ }
+
+ return ret;
+}
--- /dev/null
+/**
+ * Copyright 2017 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "config.h"
+
+#if !defined(__GNUC__) || !__GNUC__
+#define __attribute__(x) /**/
+#endif
+
+#include "collectd/lcc_features.h"
+#include "collectd/network_parse.h" /* for lcc_network_parse_options_t */
+#include "collectd/server.h"
+
+#include <errno.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <stdio.h>
+#define DEBUG(...) printf(__VA_ARGS__)
+
+static _Bool is_multicast(struct addrinfo const *ai) {
+ if (ai->ai_family == AF_INET) {
+ struct sockaddr_in *addr = (struct sockaddr_in *)ai->ai_addr;
+ return IN_MULTICAST(ntohl(addr->sin_addr.s_addr));
+ } else if (ai->ai_family == AF_INET6) {
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *)ai->ai_addr;
+ return IN6_IS_ADDR_MULTICAST(&addr->sin6_addr);
+ }
+ return 0;
+}
+
+static int server_multicast_join(lcc_listener_t *srv,
+ struct sockaddr_storage *group, int loop_back,
+ int ttl) {
+ if (group->ss_family == AF_INET) {
+ struct sockaddr_in *sa = (struct sockaddr_in *)group;
+
+ int status = setsockopt(srv->conn, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &loop_back, sizeof(loop_back));
+ if (status == -1) {
+ DEBUG("setsockopt(IP_MULTICAST_LOOP, %d) = %d\n", loop_back, errno);
+ return errno;
+ }
+
+ status =
+ setsockopt(srv->conn, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
+ if (status == -1)
+ return errno;
+
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ struct ip_mreqn mreq = {
+ .imr_address.s_addr = INADDR_ANY,
+ .imr_multiaddr.s_addr = sa->sin_addr.s_addr,
+ .imr_ifindex = if_nametoindex(srv->interface),
+ };
+#else
+ struct ip_mreq mreq = {
+ .imr_address.s_addr = INADDR_ANY, .imr_multiaddr.s_addr = sa->s_addr,
+ };
+#endif
+ status = setsockopt(srv->conn, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq));
+ if (status == -1)
+ return errno;
+ } else if (group->ss_family == AF_INET6) {
+ struct sockaddr_in6 *sa = (struct sockaddr_in6 *)group;
+
+ int status = setsockopt(srv->conn, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &loop_back, sizeof(loop_back));
+ if (status == -1)
+ return errno;
+
+ status = setsockopt(srv->conn, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl,
+ sizeof(ttl));
+ if (status == -1)
+ return errno;
+
+ struct ipv6_mreq mreq6 = {
+ .ipv6mr_interface = if_nametoindex(srv->interface),
+ };
+ memmove(&mreq6.ipv6mr_multiaddr, &sa->sin6_addr, sizeof(struct in6_addr));
+
+ status = setsockopt(srv->conn, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
+ sizeof(mreq6));
+ if (status == -1)
+ return errno;
+ } else {
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+static int server_bind_socket(lcc_listener_t *srv, struct addrinfo const *ai) {
+ /* allow multiple sockets to use the same PORT number */
+ if (setsockopt(srv->conn, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) ==
+ -1) {
+ return errno;
+ }
+
+ if (bind(srv->conn, ai->ai_addr, ai->ai_addrlen) == -1) {
+ return -1;
+ }
+
+ if (is_multicast(ai)) {
+ int status = server_multicast_join(srv, (void *)ai->ai_addr, /* loop = */ 1,
+ /* ttl = */ 16);
+ if (status != 0)
+ return status;
+ }
+
+ return 0;
+}
+
+static int server_open(lcc_listener_t *srv) {
+ struct addrinfo *res = NULL;
+ int status = getaddrinfo(srv->node ? srv->node : "::",
+ srv->service ? srv->service : LCC_DEFAULT_PORT,
+ &(struct addrinfo){
+ .ai_flags = AI_ADDRCONFIG,
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ },
+ &res);
+ if (status != 0)
+ return status;
+
+ for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) {
+ srv->conn = socket(ai->ai_family, ai->ai_socktype, 0);
+ if (srv->conn == -1)
+ continue;
+
+ status = server_bind_socket(srv, ai);
+ if (status != 0) {
+ close(srv->conn);
+ srv->conn = -1;
+ continue;
+ }
+
+ break;
+ }
+
+ freeaddrinfo(res);
+
+ if (srv->conn >= 0)
+ return 0;
+ return status != 0 ? status : -1;
+}
+
+int lcc_listen_and_write(lcc_listener_t srv) {
+ _Bool close_socket = 0;
+
+ if (srv.conn < 0) {
+ int status = server_open(&srv);
+ if (status != 0)
+ return status;
+ close_socket = 1;
+ }
+
+ if (srv.buffer_size == 0)
+ srv.buffer_size = LCC_NETWORK_BUFFER_SIZE;
+
+ if (srv.parser == NULL)
+ srv.parser = lcc_network_parse;
+
+ int ret = 0;
+ while (42) {
+ char buffer[srv.buffer_size];
+ ssize_t len = recv(srv.conn, buffer, sizeof(buffer), /* flags = */ 0);
+ if (len == -1) {
+ ret = errno;
+ break;
+ } else if (len == 0) {
+ break;
+ }
+
+ (void)srv.parser(buffer, (size_t)len, srv.parse_options);
+ }
+
+ if (close_socket) {
+ close(srv.conn);
+ srv.conn = -1;
+ }
+
+ return ret;
+}
* collectd - src/mcelog.c
* MIT License
*
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* Krzysztof Matczak <krzysztofx.matczak@intel.com>
*/
-#include "common.h"
#include "collectd.h"
+#include "common.h"
+#include "utils_llist.h"
+
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#define MCELOG_DIMM_NAME "DMI_NAME"
#define MCELOG_CORRECTED_ERR "corrected memory errors"
#define MCELOG_UNCORRECTED_ERR "uncorrected memory errors"
+#define MCELOG_CORRECTED_ERR_TIMED "corrected memory timed errors"
+#define MCELOG_UNCORRECTED_ERR_TIMED "uncorrected memory timed errors"
+#define MCELOG_CORRECTED_ERR_TYPE_INS "corrected_memory_errors"
+#define MCELOG_UNCORRECTED_ERR_TYPE_INS "uncorrected_memory_errors"
typedef struct mcelog_config_s {
char logfile[PATH_MAX]; /* mcelog logfile */
pthread_t tid; /* poll thread id */
+ llist_t *dimms_list; /* DIMMs list */
+ pthread_mutex_t dimms_lock; /* lock for dimms cache */
+ _Bool persist;
} mcelog_config_t;
typedef struct socket_adapter_s socket_adapter_t;
static int socket_reinit(socket_adapter_t *self);
static int socket_receive(socket_adapter_t *self, FILE **p_file);
-static mcelog_config_t g_mcelog_config = {.logfile = "/var/log/mcelog"};
+static mcelog_config_t g_mcelog_config = {
+ .logfile = "/var/log/mcelog", .persist = 0,
+};
static socket_adapter_t socket_adapter = {
.sock_fd = -1,
};
static _Bool mcelog_thread_running;
+static _Bool mcelog_apply_defaults;
+
+static void mcelog_free_dimms_list_records(llist_t *dimms_list) {
+
+ for (llentry_t *e = llist_head(dimms_list); e != NULL; e = e->next) {
+ sfree(e->key);
+ sfree(e->value);
+ }
+}
+
+/* Create or get dimm by dimm name/location */
+static llentry_t *mcelog_dimm(const mcelog_memory_rec_t *rec,
+ llist_t *dimms_list) {
+
+ char dimm_name[DATA_MAX_NAME_LEN];
+
+ if (strlen(rec->dimm_name) > 0) {
+ snprintf(dimm_name, sizeof(dimm_name), "%s_%s", rec->location,
+ rec->dimm_name);
+ } else
+ sstrncpy(dimm_name, rec->location, sizeof(dimm_name));
+
+ llentry_t *dimm_le = llist_search(g_mcelog_config.dimms_list, dimm_name);
+
+ if (dimm_le != NULL)
+ return dimm_le;
+
+ /* allocate new linked list entry */
+ mcelog_memory_rec_t *dimm_mr = calloc(1, sizeof(*dimm_mr));
+ if (dimm_mr == NULL) {
+ ERROR(MCELOG_PLUGIN ": Error allocating dimm memory item");
+ return NULL;
+ }
+ char *p_name = strdup(dimm_name);
+ if (p_name == NULL) {
+ ERROR(MCELOG_PLUGIN ": strdup: error");
+ free(dimm_mr);
+ return NULL;
+ }
+
+ /* add new dimm */
+ dimm_le = llentry_create(p_name, dimm_mr);
+ if (dimm_le == NULL) {
+ ERROR(MCELOG_PLUGIN ": llentry_create(): error");
+ free(dimm_mr);
+ free(p_name);
+ return NULL;
+ }
+ pthread_mutex_lock(&g_mcelog_config.dimms_lock);
+ llist_append(g_mcelog_config.dimms_list, dimm_le);
+ pthread_mutex_unlock(&g_mcelog_config.dimms_lock);
+
+ return dimm_le;
+}
+
+static void mcelog_update_dimm_stats(llentry_t *dimm,
+ const mcelog_memory_rec_t *rec) {
+ pthread_mutex_lock(&g_mcelog_config.dimms_lock);
+ memcpy(dimm->value, rec, sizeof(mcelog_memory_rec_t));
+ pthread_mutex_unlock(&g_mcelog_config.dimms_lock);
+}
static int mcelog_config(oconfig_item_t *ci) {
+ int use_logfile = 0, use_memory = 0;
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
- if (strcasecmp("McelogClientSocket", child->key) == 0) {
- if (cf_util_get_string_buffer(child, socket_adapter.unix_sock.sun_path,
- sizeof(socket_adapter.unix_sock.sun_path)) <
- 0) {
- ERROR(MCELOG_PLUGIN ": Invalid configuration option: \"%s\".",
+ if (strcasecmp("McelogLogfile", child->key) == 0) {
+ use_logfile = 1;
+ if (use_memory) {
+ ERROR(MCELOG_PLUGIN ": Invalid configuration option: \"%s\", Memory "
+ "option is already configured.",
child->key);
return -1;
}
- } else if (strcasecmp("McelogLogfile", child->key) == 0) {
if (cf_util_get_string_buffer(child, g_mcelog_config.logfile,
sizeof(g_mcelog_config.logfile)) < 0) {
ERROR(MCELOG_PLUGIN ": Invalid configuration option: \"%s\".",
child->key);
return -1;
}
+ memset(socket_adapter.unix_sock.sun_path, 0,
+ sizeof(socket_adapter.unix_sock.sun_path));
+ } else if (strcasecmp("Memory", child->key) == 0) {
+ if (use_logfile) {
+ ERROR(MCELOG_PLUGIN ": Invalid configuration option: \"%s\", Logfile "
+ "option is already configured.",
+ child->key);
+ return -1;
+ }
+ use_memory = 1;
+ for (int j = 0; j < child->children_num; j++) {
+ oconfig_item_t *mem_child = child->children + j;
+ if (strcasecmp("McelogClientSocket", mem_child->key) == 0) {
+ if (cf_util_get_string_buffer(
+ mem_child, socket_adapter.unix_sock.sun_path,
+ sizeof(socket_adapter.unix_sock.sun_path)) < 0) {
+ ERROR(MCELOG_PLUGIN ": Invalid configuration option: \"%s\".",
+ mem_child->key);
+ return -1;
+ }
+ } else if (strcasecmp("PersistentNotification", mem_child->key) == 0) {
+ if (cf_util_get_boolean(mem_child, &g_mcelog_config.persist) < 0) {
+ ERROR(MCELOG_PLUGIN ": Invalid configuration option: \"%s\".",
+ mem_child->key);
+ return -1;
+ }
+ } else {
+ ERROR(MCELOG_PLUGIN ": Invalid Memory configuration option: \"%s\".",
+ mem_child->key);
+ return -1;
+ }
+ }
+ memset(g_mcelog_config.logfile, 0, sizeof(g_mcelog_config.logfile));
} else {
ERROR(MCELOG_PLUGIN ": Invalid configuration option: \"%s\".",
child->key);
return -1;
}
}
+
+ if (!use_logfile && !use_memory)
+ mcelog_apply_defaults = 1;
+
return 0;
}
const size_t len) {
int ret = 0;
pthread_rwlock_rdlock(&self->lock);
- if (swrite(self->sock_fd, msg, len) < 0)
+ if (swrite(self->sock_fd, msg, len) != 0)
ret = -1;
pthread_rwlock_unlock(&self->lock);
return ret;
return ret;
}
-static int mcelog_prepare_notification(notification_t *n,
- const mcelog_memory_rec_t *mr) {
- if (n == NULL || mr == NULL)
- return -1;
+static int mcelog_dispatch_mem_notifications(const mcelog_memory_rec_t *mr) {
+ notification_t n = {.severity = NOTIF_WARNING,
+ .time = cdtime(),
+ .plugin = MCELOG_PLUGIN,
+ .type = "errors"};
- if ((mr->location[0] != '\0') &&
- (plugin_notification_meta_add_string(n, MCELOG_SOCKET_STR, mr->location) <
- 0)) {
- ERROR(MCELOG_PLUGIN ": add memory location meta data failed");
- return -1;
- }
- if ((mr->dimm_name[0] != '\0') &&
- (plugin_notification_meta_add_string(n, MCELOG_DIMM_NAME, mr->dimm_name) <
- 0)) {
- ERROR(MCELOG_PLUGIN ": add DIMM name meta data failed");
- plugin_notification_meta_free(n->meta);
- return -1;
- }
- if (plugin_notification_meta_add_signed_int(n, MCELOG_CORRECTED_ERR,
- mr->corrected_err_total) < 0) {
- ERROR(MCELOG_PLUGIN ": add corrected errors meta data failed");
- plugin_notification_meta_free(n->meta);
- return -1;
- }
- if (plugin_notification_meta_add_signed_int(
- n, "corrected memory timed errors", mr->corrected_err_timed) < 0) {
- ERROR(MCELOG_PLUGIN ": add corrected timed errors meta data failed");
- plugin_notification_meta_free(n->meta);
+ int dispatch_corrected_notifs = 0, dispatch_uncorrected_notifs = 0;
+
+ if (mr == NULL)
return -1;
- }
- if ((mr->corrected_err_timed_period[0] != '\0') &&
- (plugin_notification_meta_add_string(n, "corrected errors time period",
- mr->corrected_err_timed_period) <
- 0)) {
- ERROR(MCELOG_PLUGIN ": add corrected errors period meta data failed");
- plugin_notification_meta_free(n->meta);
+
+ llentry_t *dimm = mcelog_dimm(mr, g_mcelog_config.dimms_list);
+ if (dimm == NULL) {
+ ERROR(MCELOG_PLUGIN
+ ": Error adding/getting dimm memory item to/from cache");
return -1;
}
- if (plugin_notification_meta_add_signed_int(n, MCELOG_UNCORRECTED_ERR,
- mr->uncorrected_err_total) < 0) {
- ERROR(MCELOG_PLUGIN ": add corrected errors meta data failed");
- plugin_notification_meta_free(n->meta);
- return -1;
+ mcelog_memory_rec_t *mr_old = dimm->value;
+ if (!g_mcelog_config.persist) {
+
+ if (mr_old->corrected_err_total != mr->corrected_err_total ||
+ mr_old->corrected_err_timed != mr->corrected_err_timed)
+ dispatch_corrected_notifs = 1;
+
+ if (mr_old->uncorrected_err_total != mr->uncorrected_err_total ||
+ mr_old->uncorrected_err_timed != mr->uncorrected_err_timed)
+ dispatch_uncorrected_notifs = 1;
+
+ if (!dispatch_corrected_notifs && !dispatch_uncorrected_notifs) {
+ DEBUG("%s: No new notifications to dispatch", MCELOG_PLUGIN);
+ return 0;
+ }
+ } else {
+ dispatch_corrected_notifs = 1;
+ dispatch_uncorrected_notifs = 1;
}
- if (plugin_notification_meta_add_signed_int(n,
- "uncorrected memory timed errors",
- mr->uncorrected_err_timed) < 0) {
- ERROR(MCELOG_PLUGIN ": add corrected timed errors meta data failed");
- plugin_notification_meta_free(n->meta);
- return -1;
+
+ sstrncpy(n.host, hostname_g, sizeof(n.host));
+
+ if (mr->dimm_name[0] != '\0')
+ snprintf(n.plugin_instance, sizeof(n.plugin_instance), "%s_%s",
+ mr->location, mr->dimm_name);
+ else
+ sstrncpy(n.plugin_instance, mr->location, sizeof(n.plugin_instance));
+
+ if (dispatch_corrected_notifs &&
+ (mr->corrected_err_total > 0 || mr->corrected_err_timed > 0)) {
+ /* Corrected Error Notifications */
+ plugin_notification_meta_add_signed_int(&n, MCELOG_CORRECTED_ERR,
+ mr->corrected_err_total);
+ plugin_notification_meta_add_signed_int(&n, MCELOG_CORRECTED_ERR_TIMED,
+ mr->corrected_err_timed);
+ snprintf(n.message, sizeof(n.message), MCELOG_CORRECTED_ERR);
+ sstrncpy(n.type_instance, MCELOG_CORRECTED_ERR_TYPE_INS,
+ sizeof(n.type_instance));
+ plugin_dispatch_notification(&n);
+ if (n.meta)
+ plugin_notification_meta_free(n.meta);
+ n.meta = NULL;
}
- if ((mr->uncorrected_err_timed_period[0] != '\0') &&
- (plugin_notification_meta_add_string(n, "uncorrected errors time period",
- mr->uncorrected_err_timed_period) <
- 0)) {
- ERROR(MCELOG_PLUGIN ": add corrected errors period meta data failed");
- plugin_notification_meta_free(n->meta);
- return -1;
+
+ if (dispatch_uncorrected_notifs &&
+ (mr->uncorrected_err_total > 0 || mr->uncorrected_err_timed > 0)) {
+ /* Uncorrected Error Notifications */
+ plugin_notification_meta_add_signed_int(&n, MCELOG_UNCORRECTED_ERR,
+ mr->uncorrected_err_total);
+ plugin_notification_meta_add_signed_int(&n, MCELOG_UNCORRECTED_ERR_TIMED,
+ mr->uncorrected_err_timed);
+ snprintf(n.message, sizeof(n.message), MCELOG_UNCORRECTED_ERR);
+ sstrncpy(n.type_instance, MCELOG_UNCORRECTED_ERR_TYPE_INS,
+ sizeof(n.type_instance));
+ n.severity = NOTIF_FAILURE;
+ plugin_dispatch_notification(&n);
+ if (n.meta)
+ plugin_notification_meta_free(n.meta);
+ n.meta = NULL;
}
return 0;
return -1;
}
+ llentry_t *dimm = mcelog_dimm(mr, g_mcelog_config.dimms_list);
+ if (dimm == NULL) {
+ ERROR(MCELOG_PLUGIN
+ ": Error adding/getting dimm memory item to/from cache");
+ return -1;
+ }
+
value_list_t vl = {
.values_len = 1,
.values = &(value_t){.derive = (derive_t)mr->corrected_err_total},
.time = cdtime(),
.plugin = MCELOG_PLUGIN,
.type = "errors",
- .type_instance = "corrected_memory_errors"};
+ .type_instance = MCELOG_CORRECTED_ERR_TYPE_INS};
+
+ mcelog_update_dimm_stats(dimm, mr);
if (mr->dimm_name[0] != '\0')
snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s_%s",
vl.values = &(value_t){.derive = (derive_t)mr->corrected_err_timed};
plugin_dispatch_values(&vl);
- sstrncpy(vl.type_instance, "uncorrected_memory_errors",
+ sstrncpy(vl.type_instance, MCELOG_UNCORRECTED_ERR_TYPE_INS,
sizeof(vl.type_instance));
vl.values = &(value_t){.derive = (derive_t)mr->uncorrected_err_total};
plugin_dispatch_values(&vl);
continue;
}
- notification_t n = {.severity = NOTIF_OKAY,
- .time = cdtime(),
- .message = "Got memory errors info.",
- .plugin = MCELOG_PLUGIN,
- .type_instance = "memory_erros"};
-
- if (mcelog_prepare_notification(&n, &memory_record) == 0)
- mcelog_dispatch_notification(&n);
+ if (mcelog_dispatch_mem_notifications(&memory_record) != 0)
+ ERROR(MCELOG_PLUGIN ": Failed to submit memory errors notification");
if (mcelog_submit(&memory_record) != 0)
ERROR(MCELOG_PLUGIN ": Failed to submit memory errors");
memset(&memory_record, 0, sizeof(memory_record));
}
static int mcelog_init(void) {
+ if (mcelog_apply_defaults) {
+ INFO(MCELOG_PLUGIN
+ ": No configuration selected defaulting to memory errors.");
+ memset(g_mcelog_config.logfile, 0, sizeof(g_mcelog_config.logfile));
+ }
+ g_mcelog_config.dimms_list = llist_create();
+ int err = pthread_mutex_init(&g_mcelog_config.dimms_lock, NULL);
+ if (err < 0) {
+ ERROR(MCELOG_PLUGIN ": plugin: failed to initialize cache lock");
+ return -1;
+ }
+
if (socket_adapter.reinit(&socket_adapter) != 0) {
ERROR(MCELOG_PLUGIN ": Cannot connect to client socket");
return -1;
}
- if (plugin_thread_create(&g_mcelog_config.tid, NULL, poll_worker, NULL,
- NULL) != 0) {
- ERROR(MCELOG_PLUGIN ": Error creating poll thread.");
- return -1;
+ if (strlen(socket_adapter.unix_sock.sun_path)) {
+ if (plugin_thread_create(&g_mcelog_config.tid, NULL, poll_worker, NULL,
+ NULL) != 0) {
+ ERROR(MCELOG_PLUGIN ": Error creating poll thread.");
+ return -1;
+ }
}
return 0;
}
ret = -1;
}
}
-
+ pthread_mutex_lock(&g_mcelog_config.dimms_lock);
+ mcelog_free_dimms_list_records(g_mcelog_config.dimms_list);
+ llist_destroy(g_mcelog_config.dimms_list);
+ g_mcelog_config.dimms_list = NULL;
+ pthread_mutex_unlock(&g_mcelog_config.dimms_lock);
+ pthread_mutex_destroy(&g_mcelog_config.dimms_lock);
ret = socket_adapter.close(&socket_adapter) || ret;
pthread_rwlock_destroy(&(socket_adapter.lock));
return -ret;
typedef struct web_page_s web_page_t;
struct web_page_s /* {{{ */
{
+ char *plugin_name;
char *instance;
char *server;
memcached_free(wp->memc);
wp->memc = NULL;
+ sfree(wp->plugin_name);
sfree(wp->instance);
sfree(wp->server);
sfree(wp->key);
status = cmc_config_add_string("Server", &page->server, child);
else if (strcasecmp("Key", child->key) == 0)
status = cmc_config_add_string("Key", &page->key, child);
+ else if (strcasecmp("Plugin", child->key) == 0)
+ status = cmc_config_add_string("Plugin", &page->plugin_name, child);
else if (strcasecmp("Match", child->key) == 0)
/* Be liberal with failing matches => don't set `status'. */
cmc_config_add_match(page, child);
vl.values = &value;
vl.values_len = 1;
- sstrncpy(vl.plugin, "memcachec", sizeof(vl.plugin));
+ sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "memcachec",
+ sizeof (vl.plugin));
sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, wm->type, sizeof(vl.type));
sstrncpy(vl.type_instance, wm->instance, sizeof(vl.type_instance));
* Copyright (C) 2009 Doug MacEachern
* Copyright (C) 2009 Franck Lombardi
* Copyright (C) 2012 Nicolas Szalay
+ * Copyright (C) 2017 Pavel Rochnyak
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Doug MacEachern <dougm at hyperic.com>
* Franck Lombardi
* Nicolas Szalay
+ * Pavel Rochnyak <pavel2000 ngs.ru>
**/
#include "collectd.h"
#include <netinet/tcp.h>
#include <sys/un.h>
+#include <poll.h>
+
#define MEMCACHED_DEF_HOST "127.0.0.1"
#define MEMCACHED_DEF_PORT "11211"
+#define MEMCACHED_CONNECT_TIMEOUT 10000
+#define MEMCACHED_IO_TIMEOUT 5000
+
+struct prev_s {
+ derive_t hits;
+ derive_t gets;
+ derive_t incr_hits;
+ derive_t incr_misses;
+ derive_t decr_hits;
+ derive_t decr_misses;
+};
+
+typedef struct prev_s prev_t;
struct memcached_s {
char *name;
char *socket;
char *connhost;
char *connport;
+ int fd;
+ prev_t prev;
};
typedef struct memcached_s memcached_t;
if (st == NULL)
return;
+ if (st->fd >= 0) {
+ shutdown(st->fd, SHUT_RDWR);
+ close(st->fd);
+ st->fd = -1;
+ }
+
sfree(st->name);
sfree(st->host);
sfree(st->socket);
static int memcached_connect_unix(memcached_t *st) {
struct sockaddr_un serv_addr = {0};
- int fd;
serv_addr.sun_family = AF_UNIX;
sstrncpy(serv_addr.sun_path, st->socket, sizeof(serv_addr.sun_path));
/* create our socket descriptor */
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
char errbuf[1024];
ERROR("memcached plugin: memcached_connect_unix: socket(2) failed: %s",
if (status != 0) {
shutdown(fd, SHUT_RDWR);
close(fd);
- fd = -1;
+ return -1;
+ }
+
+ /* switch to non-blocking mode */
+ int flags = fcntl(fd, F_GETFL);
+ status = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (status != 0) {
+ close(fd);
+ return -1;
}
return fd;
static int memcached_connect_inet(memcached_t *st) {
struct addrinfo *ai_list;
- int status;
int fd = -1;
struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
.ai_flags = AI_ADDRCONFIG,
.ai_socktype = SOCK_STREAM};
- status = getaddrinfo(st->connhost, st->connport, &ai_hints, &ai_list);
+ int status = getaddrinfo(st->connhost, st->connport, &ai_hints, &ai_list);
if (status != 0) {
char errbuf[1024];
ERROR("memcached plugin: memcached_connect_inet: "
continue;
}
+ /* switch socket to non-blocking mode */
+ int flags = fcntl(fd, F_GETFL);
+ status = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (status != 0) {
+ close(fd);
+ fd = -1;
+ continue;
+ }
+
/* connect to the memcached daemon */
status = (int)connect(fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
- if (status != 0) {
+ if (status != 0 && errno != EINPROGRESS) {
shutdown(fd, SHUT_RDWR);
close(fd);
fd = -1;
continue;
}
- /* A socket could be opened and connecting succeeded. We're done. */
+ /* Wait until connection establishes */
+ struct pollfd pollfd = {
+ .fd = fd, .events = POLLOUT,
+ };
+ do
+ status = poll(&pollfd, 1, MEMCACHED_CONNECT_TIMEOUT);
+ while (status < 0 && errno == EINTR);
+ if (status <= 0) {
+ close(fd);
+ fd = -1;
+ continue;
+ }
+
+ /* Check if all is good */
+ int socket_error;
+ status = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&socket_error,
+ &(socklen_t){sizeof(socket_error)});
+ if (status != 0 || socket_error != 0) {
+ close(fd);
+ fd = -1;
+ continue;
+ }
+ /* A socket is opened and connection succeeded. We're done. */
break;
}
return fd;
} /* int memcached_connect_inet */
-static int memcached_connect(memcached_t *st) {
+static void memcached_connect(memcached_t *st) {
+ if (st->fd >= 0)
+ return;
+
if (st->socket != NULL)
- return memcached_connect_unix(st);
+ st->fd = memcached_connect_unix(st);
else
- return memcached_connect_inet(st);
+ st->fd = memcached_connect_inet(st);
+
+ if (st->fd >= 0)
+ INFO("memcached plugin: Instance \"%s\": connection established.",
+ st->name);
}
static int memcached_query_daemon(char *buffer, size_t buffer_size,
memcached_t *st) {
- int fd, status;
+ int status;
size_t buffer_fill;
- fd = memcached_connect(st);
- if (fd < 0) {
+ memcached_connect(st);
+ if (st->fd < 0) {
ERROR("memcached plugin: Instance \"%s\" could not connect to daemon.",
st->name);
return -1;
}
- status = (int)swrite(fd, "stats\r\n", strlen("stats\r\n"));
+ struct pollfd pollfd = {
+ .fd = st->fd, .events = POLLOUT,
+ };
+
+ do
+ status = poll(&pollfd, 1, MEMCACHED_IO_TIMEOUT);
+ while (status < 0 && errno == EINTR);
+
+ if (status <= 0) {
+ ERROR("memcached plugin: poll() failed for write() call.");
+ close(st->fd);
+ st->fd = -1;
+ return -1;
+ }
+
+ status = (int)swrite(st->fd, "stats\r\n", strlen("stats\r\n"));
if (status != 0) {
char errbuf[1024];
- ERROR("memcached plugin: write(2) failed: %s",
+ ERROR("memcached plugin: Instance \"%s\": write(2) failed: %s", st->name,
sstrerror(errno, errbuf, sizeof(errbuf)));
- shutdown(fd, SHUT_RDWR);
- close(fd);
+ shutdown(st->fd, SHUT_RDWR);
+ close(st->fd);
+ st->fd = -1;
return -1;
}
memset(buffer, 0, buffer_size);
buffer_fill = 0;
- while ((status = (int)recv(fd, buffer + buffer_fill,
- buffer_size - buffer_fill, /* flags = */ 0)) !=
- 0) {
+ pollfd.events = POLLIN;
+ while (1) {
+ do
+ status = poll(&pollfd, 1, MEMCACHED_IO_TIMEOUT);
+ while (status < 0 && errno == EINTR);
+
+ if (status <= 0) {
+ ERROR("memcached plugin: Instance \"%s\": Timeout reading from socket",
+ st->name);
+ close(st->fd);
+ st->fd = -1;
+ return -1;
+ }
+
+ do
+ status = (int)recv(st->fd, buffer + buffer_fill,
+ buffer_size - buffer_fill, /* flags = */ 0);
+ while (status < 0 && errno == EINTR);
+
char const end_token[5] = {'E', 'N', 'D', '\r', '\n'};
if (status < 0) {
char errbuf[1024];
- if ((errno == EAGAIN) || (errno == EINTR))
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
continue;
- ERROR("memcached: Error reading from socket: %s",
- sstrerror(errno, errbuf, sizeof(errbuf)));
- shutdown(fd, SHUT_RDWR);
- close(fd);
+ ERROR("memcached plugin: Instance \"%s\": Error reading from socket: %s",
+ st->name, sstrerror(errno, errbuf, sizeof(errbuf)));
+ shutdown(st->fd, SHUT_RDWR);
+ close(st->fd);
+ st->fd = -1;
return -1;
}
buffer_fill += (size_t)status;
if (buffer_fill > buffer_size) {
buffer_fill = buffer_size;
- WARNING("memcached plugin: Message was truncated.");
+ WARNING("memcached plugin: Instance \"%s\": Message was truncated.",
+ st->name);
+ shutdown(st->fd, SHUT_RDWR);
+ close(st->fd);
+ st->fd = -1;
break;
}
status = 0;
if (buffer_fill == 0) {
- WARNING("memcached plugin: No data returned by memcached.");
+ WARNING("memcached plugin: Instance \"%s\": No data returned by memcached.",
+ st->name);
status = -1;
}
- shutdown(fd, SHUT_RDWR);
- close(fd);
return status;
} /* int memcached_query_daemon */
plugin_dispatch_values(&vl);
}
+static gauge_t calculate_ratio_percent(derive_t part, derive_t total,
+ derive_t *prev_part,
+ derive_t *prev_total) {
+ if ((*prev_part == 0) || (*prev_total == 0) || (part < *prev_part) ||
+ (total < *prev_total)) {
+ *prev_part = part;
+ *prev_total = total;
+ return NAN;
+ }
+
+ derive_t num = part - *prev_part;
+ derive_t denom = total - *prev_total;
+
+ *prev_part = part;
+ *prev_total = total;
+
+ if (denom == 0)
+ return NAN;
+
+ if (num == 0)
+ return 0;
+
+ return 100.0 * (gauge_t)num / (gauge_t)denom;
+}
+
+static gauge_t calculate_ratio_percent2(derive_t part1, derive_t part2,
+ derive_t *prev1, derive_t *prev2) {
+ if ((*prev1 == 0) || (*prev2 == 0) || (part1 < *prev1) || (part2 < *prev2)) {
+ *prev1 = part1;
+ *prev2 = part2;
+ return NAN;
+ }
+
+ derive_t num = part1 - *prev1;
+ derive_t denom = part2 - *prev2 + num;
+
+ *prev1 = part1;
+ *prev2 = part2;
+
+ if (denom == 0)
+ return NAN;
+
+ if (num == 0)
+ return 0;
+
+ return 100.0 * (gauge_t)num / (gauge_t)denom;
+}
+
static int memcached_read(user_data_t *user_data) {
char buf[4096];
char *fields[3];
- char *ptr;
char *line;
- char *saveptr;
- int fields_num;
-
- gauge_t bytes_used = NAN;
- gauge_t bytes_total = NAN;
- gauge_t hits = NAN;
- gauge_t gets = NAN;
- gauge_t incr_hits = NAN;
- derive_t incr = 0;
- gauge_t decr_hits = NAN;
- derive_t decr = 0;
+
+ derive_t bytes_used = 0;
+ derive_t bytes_total = 0;
+ derive_t get_hits = 0;
+ derive_t cmd_get = 0;
+ derive_t incr_hits = 0;
+ derive_t incr_misses = 0;
+ derive_t decr_hits = 0;
+ derive_t decr_misses = 0;
derive_t rusage_user = 0;
derive_t rusage_syst = 0;
derive_t octets_rx = 0;
derive_t octets_tx = 0;
- memcached_t *st;
- st = user_data->data;
+ memcached_t *st = user_data->data;
+ prev_t *prev = &st->prev;
/* get data from daemon */
if (memcached_query_daemon(buf, sizeof(buf), st) < 0) {
#define FIELD_IS(cnst) \
(((sizeof(cnst) - 1) == name_len) && (strcmp(cnst, fields[1]) == 0))
- ptr = buf;
- saveptr = NULL;
+ char *ptr = buf;
+ char *saveptr = NULL;
while ((line = strtok_r(ptr, "\n\r", &saveptr)) != NULL) {
- int name_len;
-
ptr = NULL;
- fields_num = strsplit(line, fields, 3);
- if (fields_num != 3)
+ if (strsplit(line, fields, 3) != 3)
continue;
- name_len = strlen(fields[1]);
+ int name_len = strlen(fields[1]);
if (name_len == 0)
continue;
* CPU time consumed by the memcached process
*/
if (FIELD_IS("rusage_user")) {
- rusage_user = atoll(fields[2]);
+ /* Convert to useconds */
+ rusage_user = atof(fields[2]) * 1000000;
} else if (FIELD_IS("rusage_system")) {
- rusage_syst = atoll(fields[2]);
+ rusage_syst = atof(fields[2]) * 1000000;
}
/*
* Number of bytes used and available (total - used)
*/
else if (FIELD_IS("bytes")) {
- bytes_used = atof(fields[2]);
+ bytes_used = atoll(fields[2]);
} else if (FIELD_IS("limit_maxbytes")) {
- bytes_total = atof(fields[2]);
+ bytes_total = atoll(fields[2]);
}
/*
else if (FIELD_IS("curr_connections")) {
submit_gauge("memcached_connections", "current", atof(fields[2]), st);
} else if (FIELD_IS("listen_disabled_num")) {
- submit_derive("connections", "listen_disabled", atof(fields[2]), st);
+ submit_derive("connections", "listen_disabled", atoll(fields[2]), st);
+ }
+ /*
+ * Total number of connections opened since the server started running
+ * Report this as connection rate.
+ */
+ else if (FIELD_IS("total_connections")) {
+ submit_derive("connections", "opened", atoll(fields[2]), st);
}
/*
const char *name = fields[1] + 4;
submit_derive("memcached_command", name, atoll(fields[2]), st);
if (strcmp(name, "get") == 0)
- gets = atof(fields[2]);
+ cmd_get = atoll(fields[2]);
}
/*
* Increment/Decrement
*/
else if (FIELD_IS("incr_misses")) {
- derive_t incr_count = atoll(fields[2]);
- submit_derive("memcached_ops", "incr_misses", incr_count, st);
- incr += incr_count;
+ incr_misses = atoll(fields[2]);
+ submit_derive("memcached_ops", "incr_misses", incr_misses, st);
} else if (FIELD_IS("incr_hits")) {
- derive_t incr_count = atoll(fields[2]);
- submit_derive("memcached_ops", "incr_hits", incr_count, st);
- incr_hits = atof(fields[2]);
- incr += incr_count;
+ incr_hits = atoll(fields[2]);
+ submit_derive("memcached_ops", "incr_hits", incr_hits, st);
} else if (FIELD_IS("decr_misses")) {
- derive_t decr_count = atoll(fields[2]);
- submit_derive("memcached_ops", "decr_misses", decr_count, st);
- decr += decr_count;
+ decr_misses = atoll(fields[2]);
+ submit_derive("memcached_ops", "decr_misses", decr_misses, st);
} else if (FIELD_IS("decr_hits")) {
- derive_t decr_count = atoll(fields[2]);
- submit_derive("memcached_ops", "decr_hits", decr_count, st);
- decr_hits = atof(fields[2]);
- decr += decr_count;
+ decr_hits = atoll(fields[2]);
+ submit_derive("memcached_ops", "decr_hits", decr_hits, st);
}
/*
* - evictions
*/
else if (FIELD_IS("get_hits")) {
- submit_derive("memcached_ops", "hits", atoll(fields[2]), st);
- hits = atof(fields[2]);
+ get_hits = atoll(fields[2]);
+ submit_derive("memcached_ops", "hits", get_hits, st);
} else if (FIELD_IS("get_misses")) {
submit_derive("memcached_ops", "misses", atoll(fields[2]), st);
} else if (FIELD_IS("evictions")) {
}
} /* while ((line = strtok_r (ptr, "\n\r", &saveptr)) != NULL) */
- if (!isnan(bytes_used) && !isnan(bytes_total) && (bytes_used <= bytes_total))
+ if ((bytes_total > 0) && (bytes_used <= bytes_total))
submit_gauge2("df", "cache", bytes_used, bytes_total - bytes_used, st);
if ((rusage_user != 0) || (rusage_syst != 0))
if ((octets_rx != 0) || (octets_tx != 0))
submit_derive2("memcached_octets", NULL, octets_rx, octets_tx, st);
- if (!isnan(gets) && !isnan(hits)) {
- gauge_t rate = NAN;
-
- if (gets != 0.0)
- rate = 100.0 * hits / gets;
-
- submit_gauge("percent", "hitratio", rate, st);
+ if ((cmd_get != 0) && (get_hits != 0)) {
+ gauge_t ratio =
+ calculate_ratio_percent(get_hits, cmd_get, &prev->hits, &prev->gets);
+ submit_gauge("percent", "hitratio", ratio, st);
}
- if (!isnan(incr_hits) && incr != 0) {
- gauge_t incr_rate = 100.0 * incr_hits / incr;
- submit_gauge("percent", "incr_hitratio", incr_rate, st);
- submit_derive("memcached_ops", "incr", incr, st);
+ if ((incr_hits != 0) && (incr_misses != 0)) {
+ gauge_t ratio = calculate_ratio_percent2(
+ incr_hits, incr_misses, &prev->incr_hits, &prev->incr_misses);
+ submit_gauge("percent", "incr_hitratio", ratio, st);
+ submit_derive("memcached_ops", "incr", incr_hits + incr_misses, st);
}
- if (!isnan(decr_hits) && decr != 0) {
- gauge_t decr_rate = 100.0 * decr_hits / decr;
- submit_gauge("percent", "decr_hitratio", decr_rate, st);
- submit_derive("memcached_ops", "decr", decr, st);
+ if ((decr_hits != 0) && (decr_misses != 0)) {
+ gauge_t ratio = calculate_ratio_percent2(
+ decr_hits, decr_misses, &prev->decr_hits, &prev->decr_misses);
+ submit_gauge("percent", "decr_hitratio", ratio, st);
+ submit_derive("memcached_ops", "decr", decr_hits + decr_misses, st);
}
return 0;
} /* int memcached_read */
-static int memcached_add_read_callback(memcached_t *st) {
- char callback_name[3 * DATA_MAX_NAME_LEN];
- int status;
-
- snprintf(callback_name, sizeof(callback_name), "memcached/%s",
- (st->name != NULL) ? st->name : "__legacy__");
-
+static int memcached_set_defaults(memcached_t *st) {
/* If no <Address> used then:
* - Connect to the destination specified by <Host>, if present.
* If not, use the default address.
assert(st->connhost != NULL);
assert(st->connport != NULL);
- status = plugin_register_complex_read(
+ st->prev.hits = 0;
+ st->prev.gets = 0;
+ st->prev.incr_hits = 0;
+ st->prev.incr_misses = 0;
+ st->prev.decr_hits = 0;
+ st->prev.decr_misses = 0;
+
+ return 0;
+} /* int memcached_set_defaults */
+
+static int memcached_add_read_callback(memcached_t *st) {
+ char callback_name[3 * DATA_MAX_NAME_LEN];
+
+ if (memcached_set_defaults(st) != 0) {
+ memcached_free(st);
+ return -1;
+ }
+
+ snprintf(callback_name, sizeof(callback_name), "memcached/%s",
+ (st->name != NULL) ? st->name : "__legacy__");
+
+ return plugin_register_complex_read(
/* group = */ "memcached",
/* name = */ callback_name,
/* callback = */ memcached_read,
&(user_data_t){
.data = st, .free_func = memcached_free,
});
-
- return status;
} /* int memcached_add_read_callback */
/* Configuration handling functiions
* </Plugin>
*/
static int config_add_instance(oconfig_item_t *ci) {
- memcached_t *st;
int status = 0;
/* Disable automatic generation of default instance in the init callback. */
memcached_have_instances = 1;
- st = calloc(1, sizeof(*st));
+ memcached_t *st = calloc(1, sizeof(*st));
if (st == NULL) {
ERROR("memcached plugin: calloc failed.");
return ENOMEM;
st->connhost = NULL;
st->connport = NULL;
+ st->fd = -1;
+
if (strcasecmp(ci->key, "Instance") == 0)
status = cf_util_get_string(ci, &st->name);
break;
}
- if (status == 0)
- status = memcached_add_read_callback(st);
-
if (status != 0) {
memcached_free(st);
return -1;
}
- return 0;
-}
+ return memcached_add_read_callback(st);
+} /* int config_add_instance */
static int memcached_config(oconfig_item_t *ci) {
- int status = 0;
_Bool have_instance_block = 0;
for (int i = 0; i < ci->children_num; i++) {
child->key);
} /* for (ci->children) */
- return status;
-}
+ return 0;
+} /* int memcached_config */
static int memcached_init(void) {
- memcached_t *st;
- int status;
if (memcached_have_instances)
return 0;
/* No instances were configured, lets start a default instance. */
- st = calloc(1, sizeof(*st));
+ memcached_t *st = calloc(1, sizeof(*st));
if (st == NULL)
return ENOMEM;
st->name = NULL;
st->connhost = NULL;
st->connport = NULL;
- status = memcached_add_read_callback(st);
+ st->fd = -1;
+
+ int status = memcached_add_read_callback(st);
if (status == 0)
memcached_have_instances = 1;
- else
- memcached_free(st);
return status;
} /* int memcached_init */
vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- strncpy(vl.host, hostname_g, sizeof(vl.host));
strncpy(vl.plugin, "mic", sizeof(vl.plugin));
snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber);
strncpy(vl.type, "temperature", sizeof(vl.type));
vl.values = &(value_t){.derive = value};
vl.values_len = 1;
- strncpy(vl.host, hostname_g, sizeof(vl.host));
strncpy(vl.plugin, "mic", sizeof(vl.plugin));
if (core < 0) /* global aggregation */
snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber);
vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- strncpy(vl.host, hostname_g, sizeof(vl.host));
strncpy(vl.plugin, "mic", sizeof(vl.plugin));
snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber);
strncpy(vl.type, type, sizeof(vl.type));
* StoreRates true
* Retain false
* QoS 0
- * CACert "ca.pem" Enables TLS if set
- * CertificateFile "client-cert.pem" optional
- * CertificateKeyFile "client-key.pem" optional
- * TLSProtocol "tlsv1.2" optional
+ * CACert "ca.pem" Enables TLS if set
+ * CertificateFile "client-cert.pem" optional
+ * CertificateKeyFile "client-key.pem" optional
+ * TLSProtocol "tlsv1.2" optional
* </Publish>
*/
static int mqtt_config_publisher(oconfig_item_t *ci) {
* User "guest"
* Password "secret"
* Topic "collectd/#"
+ * CACert "ca.pem" Enables TLS if set
+ * CertificateFile "client-cert.pem" optional
+ * CertificateKeyFile "client-key.pem" optional
+ * TLSProtocol "tlsv1.2" optional
* </Subscribe>
*/
static int mqtt_config_subscriber(oconfig_item_t *ci) {
cf_util_get_string(child, &conf->topic);
else if (strcasecmp("CleanSession", child->key) == 0)
cf_util_get_boolean(child, &conf->clean_session);
+ else if (strcasecmp("CACert", child->key) == 0)
+ cf_util_get_string(child, &conf->cacertificatefile);
+ else if (strcasecmp("CertificateFile", child->key) == 0)
+ cf_util_get_string(child, &conf->certificatefile);
+ else if (strcasecmp("CertificateKeyFile", child->key) == 0)
+ cf_util_get_string(child, &conf->certificatekeyfile);
+ else if (strcasecmp("TLSProtocol", child->key) == 0)
+ cf_util_get_string(child, &conf->tlsprotocol);
+ else if (strcasecmp("CipherSuite", child->key) == 0)
+ cf_util_get_string(child, &conf->ciphersuite);
else
ERROR("mqtt plugin: Unknown config option: %s", child->key);
}
--- /dev/null
+/*
+ * Partial header file imported from the linux kernel
+ * (arch/x86/include/asm/msr-index.h)
+ * as it is not provided by the kernel sources anymore
+ *
+ * Only the minimal blocks of macro have been included
+ * ----
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ * ----
+ */
+
+#ifndef _ASM_X86_MSR_INDEX_H
+#define _ASM_X86_MSR_INDEX_H
+
+/*
+ * CPU model specific register (MSR) numbers.
+ *
+ * Do not add new entries to this file unless the definitions are shared
+ * between multiple compilation units.
+ */
+
+/* Intel MSRs. Some also available on other CPUs */
+
+/* C-state Residency Counters */
+#define MSR_PKG_C3_RESIDENCY 0x000003f8
+#define MSR_PKG_C6_RESIDENCY 0x000003f9
+#define MSR_ATOM_PKG_C6_RESIDENCY 0x000003fa
+#define MSR_PKG_C7_RESIDENCY 0x000003fa
+#define MSR_CORE_C3_RESIDENCY 0x000003fc
+#define MSR_CORE_C6_RESIDENCY 0x000003fd
+#define MSR_CORE_C7_RESIDENCY 0x000003fe
+#define MSR_KNL_CORE_C6_RESIDENCY 0x000003ff
+#define MSR_PKG_C2_RESIDENCY 0x0000060d
+#define MSR_PKG_C8_RESIDENCY 0x00000630
+#define MSR_PKG_C9_RESIDENCY 0x00000631
+#define MSR_PKG_C10_RESIDENCY 0x00000632
+
+/* Run Time Average Power Limiting (RAPL) Interface */
+
+#define MSR_RAPL_POWER_UNIT 0x00000606
+
+#define MSR_PKG_POWER_LIMIT 0x00000610
+#define MSR_PKG_ENERGY_STATUS 0x00000611
+#define MSR_PKG_PERF_STATUS 0x00000613
+#define MSR_PKG_POWER_INFO 0x00000614
+
+#define MSR_DRAM_POWER_LIMIT 0x00000618
+#define MSR_DRAM_ENERGY_STATUS 0x00000619
+#define MSR_DRAM_PERF_STATUS 0x0000061b
+#define MSR_DRAM_POWER_INFO 0x0000061c
+
+#define MSR_PP0_POWER_LIMIT 0x00000638
+#define MSR_PP0_ENERGY_STATUS 0x00000639
+#define MSR_PP0_POLICY 0x0000063a
+#define MSR_PP0_PERF_STATUS 0x0000063b
+
+#define MSR_PP1_POWER_LIMIT 0x00000640
+#define MSR_PP1_ENERGY_STATUS 0x00000641
+#define MSR_PP1_POLICY 0x00000642
+
+
+
+/* Intel defined MSRs. */
+#define MSR_IA32_TSC 0x00000010
+#define MSR_SMI_COUNT 0x00000034
+
+#define MSR_IA32_MPERF 0x000000e7
+#define MSR_IA32_APERF 0x000000e8
+
+#define MSR_IA32_THERM_STATUS 0x0000019c
+
+#define MSR_IA32_TEMPERATURE_TARGET 0x000001a2
+
+#define MSR_IA32_PACKAGE_THERM_STATUS 0x000001b1
+
+
+#endif /* _ASM_X86_MSR_INDEX_H */
struct timeval time_now;
status = swrite(fd, "D", 1);
- if (status < 0) {
+ if (status != 0) {
ERROR("multimeter plugin: swrite failed.");
return -1;
}
part_size - buffer_offset,
/* in = */ NULL, /* in len = */ 0);
if (err != 0) {
- sfree(pea.username);
ERROR("network plugin: gcry_cipher_decrypt returned: %s. Username: %s",
gcry_strerror(err), pea.username);
+ sfree(pea.username);
return -1;
}
parse_packet(se, buffer + buffer_offset, payload_len, flags | PP_ENCRYPTED,
pea.username);
- /* XXX: Free pea.username?!? */
-
/* Update return values */
*ret_buffer = buffer + part_size;
*ret_buffer_len = buffer_len - part_size;
pthread_mutex_lock(&send_buffer_lock);
- status = add_to_buffer(send_buffer_ptr,
- network_config_packet_size -
- (send_buffer_fill + BUFF_SIG_SIZE),
- &send_buffer_vl, ds, vl);
+ status =
+ add_to_buffer(send_buffer_ptr, network_config_packet_size -
+ (send_buffer_fill + BUFF_SIG_SIZE),
+ &send_buffer_vl, ds, vl);
if (status >= 0) {
/* status == bytes added to the buffer */
send_buffer_fill += status;
} else {
flush_buffer();
- status = add_to_buffer(send_buffer_ptr,
- network_config_packet_size -
- (send_buffer_fill + BUFF_SIG_SIZE),
- &send_buffer_vl, ds, vl);
+ status =
+ add_to_buffer(send_buffer_ptr, network_config_packet_size -
+ (send_buffer_fill + BUFF_SIG_SIZE),
+ &send_buffer_vl, ds, vl);
if (status >= 0) {
send_buffer_fill += status;
#include <kstat.h>
#endif
+static const char *config_keys[] = {"ReportV2", "ReportV3", "ReportV4"};
+static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
+static _Bool report_v2 = 1;
+static _Bool report_v3 = 1;
+static _Bool report_v4 = 1;
+
/*
see /proc/net/rpc/nfs
see http://www.missioncriticallinux.com/orph/NFS-Statistics
static kstat_t *nfs4_ksp_server;
#endif
+static int nfs_config(const char *key, const char *value) {
+ if (strcasecmp(key, "ReportV2") == 0)
+ report_v2 = IS_TRUE(value);
+ else if (strcasecmp(key, "ReportV3") == 0)
+ report_v3 = IS_TRUE(value);
+ else if (strcasecmp(key, "ReportV4") == 0)
+ report_v4 = IS_TRUE(value);
+ else
+ return -1;
+
+ return 0;
+}
+
#if KERNEL_LINUX
static int nfs_init(void) { return 0; }
/* #endif KERNEL_LINUX */
if (fields_num < 3)
continue;
- if (strcmp(fields[0], "proc2") == 0) {
+ if (strcmp(fields[0], "proc2") == 0 && report_v2) {
nfs_submit_fields_safe(/* version = */ 2, inst, fields + 2,
(size_t)(fields_num - 2), nfs2_procedures_names,
nfs2_procedures_names_num);
- } else if (strncmp(fields[0], "proc3", 5) == 0) {
+ } else if (strncmp(fields[0], "proc3", 5) == 0 && report_v3) {
nfs_submit_fields_safe(/* version = */ 3, inst, fields + 2,
(size_t)(fields_num - 2), nfs3_procedures_names,
nfs3_procedures_names_num);
- } else if (strcmp(fields[0], "proc4ops") == 0) {
+ } else if (strcmp(fields[0], "proc4ops") == 0 && report_v4) {
if (inst[0] == 's')
nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
- } else if (strcmp(fields[0], "proc4") == 0) {
+ } else if (strcmp(fields[0], "proc4") == 0 && report_v4) {
if (inst[0] == 'c')
nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
}
#elif HAVE_LIBKSTAT
static int nfs_read(void) {
- nfs_read_kstat(nfs2_ksp_client, /* version = */ 2, "client",
- nfs2_procedures_names, nfs2_procedures_names_num);
- nfs_read_kstat(nfs2_ksp_server, /* version = */ 2, "server",
- nfs2_procedures_names, nfs2_procedures_names_num);
- nfs_read_kstat(nfs3_ksp_client, /* version = */ 3, "client",
- nfs3_procedures_names, nfs3_procedures_names_num);
- nfs_read_kstat(nfs3_ksp_server, /* version = */ 3, "server",
- nfs3_procedures_names, nfs3_procedures_names_num);
- nfs_read_kstat(nfs4_ksp_client, /* version = */ 4, "client",
- nfs4_procedures_names, nfs4_procedures_names_num);
- nfs_read_kstat(nfs4_ksp_server, /* version = */ 4, "server",
- nfs4_procedures_names, nfs4_procedures_names_num);
+ if (report_v2) {
+ nfs_read_kstat(nfs2_ksp_client, /* version = */ 2, "client",
+ nfs2_procedures_names, nfs2_procedures_names_num);
+ nfs_read_kstat(nfs2_ksp_server, /* version = */ 2, "server",
+ nfs2_procedures_names, nfs2_procedures_names_num);
+ }
+ if (report_v3) {
+ nfs_read_kstat(nfs3_ksp_client, /* version = */ 3, "client",
+ nfs3_procedures_names, nfs3_procedures_names_num);
+ nfs_read_kstat(nfs3_ksp_server, /* version = */ 3, "server",
+ nfs3_procedures_names, nfs3_procedures_names_num);
+ }
+ if (report_v4) {
+ nfs_read_kstat(nfs4_ksp_client, /* version = */ 4, "client",
+ nfs4_procedures_names, nfs4_procedures_names_num);
+ nfs_read_kstat(nfs4_ksp_server, /* version = */ 4, "server",
+ nfs4_procedures_names, nfs4_procedures_names_num);
+ }
return 0;
}
#endif /* HAVE_LIBKSTAT */
void module_register(void) {
+ plugin_register_config("nfs", nfs_config, config_keys, config_keys_num);
plugin_register_init("nfs", nfs_init);
plugin_register_read("nfs", nfs_read);
} /* void module_register */
(void *)req_data);
status = swrite(sd, (const char *)&req, REQ_LEN_NOMAC);
- if (status < 0) {
+ if (status != 0) {
DEBUG("`swrite' failed. Closing socket #%i", sd);
close(sd);
sock_descr = sd = -1;
}
/* kerninfo -> estimated error */
- offset_loop = scale_loop * ((gauge_t)ntohl(ik->offset));
+ offset_loop = (gauge_t)((int32_t)ntohl(ik->offset) * scale_loop);
freq_loop = ntpd_read_fp(ik->freq);
- offset_error = scale_error * ((gauge_t)ntohl(ik->esterror));
+ offset_error = (gauge_t)((int32_t)ntohl(ik->esterror) * scale_error);
DEBUG("info_kernel:\n"
" pll offset = %.8g\n"
cb_name = ssnprintf_alloc("nut/%s", name);
status = plugin_register_complex_read(
- /* group = */ "nut",
- /* name = */ cb_name,
- /* callback = */ nut_read,
- /* interval = */ 0,
- /* user_data = */ &(user_data_t){
- .data = ups, .free_func = free_nut_ups_t,
- });
+ /* group = */ "nut",
+ /* name = */ cb_name,
+ /* callback = */ nut_read,
+ /* interval = */ 0,
+ /* user_data = */ &(user_data_t){
+ .data = ups, .free_func = free_nut_ups_t,
+ });
sfree(cb_name);
vl.values = &(value_t){.gauge = value};
vl.values_len = 1;
- sstrncpy(vl.host,
- (strcasecmp(ups->hostname, "localhost") == 0) ? hostname_g
- : ups->hostname,
- sizeof(vl.host));
+ if (strcasecmp(ups->hostname, "localhost") != 0)
+ sstrncpy(vl.host, ups->hostname, sizeof(vl.host));
sstrncpy(vl.plugin, "nut", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, ups->upsname, sizeof(vl.plugin_instance));
sstrncpy(vl.type, type, sizeof(vl.type));
tv.tv_sec = connect_timeout / 1000;
tv.tv_usec = connect_timeout % 1000;
- status = upscli_tryconnect(ups->conn, ups->hostname, ups->port, ssl_flags,
- &tv);
+ status =
+ upscli_tryconnect(ups->conn, ups->hostname, ups->port, ssl_flags, &tv);
#else /* #if HAVE_UPSCLI_TRYCONNECT */
status = upscli_connect(ups->conn, ups->hostname, ups->port, ssl_flags);
#endif
char *password;
char *cacert;
char *host;
- int state;
_Bool starttls;
int timeout;
char *url;
};
typedef struct cldap_s cldap_t; /* }}} */
-static cldap_t **databases = NULL;
-static size_t databases_num = 0;
-
-static void cldap_free(cldap_t *st) /* {{{ */
+static void cldap_free(void *arg) /* {{{ */
{
+ cldap_t *st = arg;
+
if (st == NULL)
return;
sfree(st->name);
sfree(st->url);
if (st->ld)
- ldap_memfree(st->ld);
+ ldap_unbind_ext_s(st->ld, NULL, NULL);
+
sfree(st);
} /* }}} void cldap_free */
/* initialize ldap for each host */
static int cldap_init_host(cldap_t *st) /* {{{ */
{
- LDAP *ld;
int rc;
- if (st->state && st->ld) {
+ if (st->ld) {
DEBUG("openldap plugin: Already connected to %s", st->url);
return 0;
}
- rc = ldap_initialize(&ld, st->url);
+ rc = ldap_initialize(&st->ld, st->url);
if (rc != LDAP_SUCCESS) {
ERROR("openldap plugin: ldap_initialize failed: %s", ldap_err2string(rc));
- st->state = 0;
- if (ld != NULL)
- ldap_unbind_ext_s(ld, NULL, NULL);
+ if (st->ld != NULL)
+ ldap_unbind_ext_s(st->ld, NULL, NULL);
+ st->ld = NULL;
return (-1);
}
- st->ld = ld;
-
ldap_set_option(st->ld, LDAP_OPT_PROTOCOL_VERSION, &st->version);
ldap_set_option(st->ld, LDAP_OPT_TIMEOUT,
}
if (st->starttls != 0) {
- rc = ldap_start_tls_s(ld, NULL, NULL);
+ rc = ldap_start_tls_s(st->ld, NULL, NULL);
if (rc != LDAP_SUCCESS) {
ERROR("openldap plugin: Failed to start tls on %s: %s", st->url,
ldap_err2string(rc));
- st->state = 0;
- if (st->ld != NULL)
- ldap_unbind_ext_s(st->ld, NULL, NULL);
+ ldap_unbind_ext_s(st->ld, NULL, NULL);
+ st->ld = NULL;
return (-1);
}
}
if (rc != LDAP_SUCCESS) {
ERROR("openldap plugin: Failed to bind to %s: %s", st->url,
ldap_err2string(rc));
- st->state = 0;
- if (st->ld != NULL)
- ldap_unbind_ext_s(st->ld, NULL, NULL);
+ ldap_unbind_ext_s(st->ld, NULL, NULL);
+ st->ld = NULL;
return (-1);
} else {
DEBUG("openldap plugin: Successfully connected to %s", st->url);
- st->state = 1;
return 0;
}
} /* }}} static cldap_init_host */
if (rc != LDAP_SUCCESS) {
ERROR("openldap plugin: Failed to execute search: %s", ldap_err2string(rc));
ldap_msgfree(result);
- st->state = 0;
- if (st->ld != NULL)
- ldap_unbind_ext_s(st->ld, NULL, NULL);
+ ldap_unbind_ext_s(st->ld, NULL, NULL);
+ st->ld = NULL;
return (-1);
}
ldap_free_urldesc(ludpp);
}
- if (status == 0) {
- cldap_t **temp;
-
- temp = (cldap_t **)realloc(databases,
- sizeof(*databases) * (databases_num + 1));
-
- if (temp == NULL) {
- ERROR("openldap plugin: realloc failed");
- status = -1;
- } else {
- char callback_name[3 * DATA_MAX_NAME_LEN] = {0};
-
- databases = temp;
- databases[databases_num] = st;
- databases_num++;
-
- snprintf(callback_name, sizeof(callback_name), "openldap/%s/%s",
- (st->host != NULL) ? st->host : hostname_g,
- (st->name != NULL) ? st->name : "default");
-
- status = plugin_register_complex_read(/* group = */ NULL,
- /* name = */ callback_name,
- /* callback = */ cldap_read_host,
- /* interval = */ 0,
- &(user_data_t){
- .data = st,
- });
- }
- }
-
if (status != 0) {
cldap_free(st);
return -1;
}
- return 0;
+ char callback_name[3 * DATA_MAX_NAME_LEN] = {0};
+
+ snprintf(callback_name, sizeof(callback_name), "openldap/%s/%s",
+ (st->host != NULL) ? st->host : hostname_g,
+ (st->name != NULL) ? st->name : "default");
+
+ return plugin_register_complex_read(/* group = */ NULL,
+ /* name = */ callback_name,
+ /* callback = */ cldap_read_host,
+ /* interval = */ 0,
+ &(user_data_t){
+ .data = st, .free_func = cldap_free,
+ });
} /* }}} int cldap_config_add */
static int cldap_config(oconfig_item_t *ci) /* {{{ */
return 0;
} /* }}} int cldap_init */
-static int cldap_shutdown(void) /* {{{ */
-{
- for (size_t i = 0; i < databases_num; i++)
- if (databases[i]->ld != NULL)
- ldap_unbind_ext_s(databases[i]->ld, NULL, NULL);
- sfree(databases);
- databases_num = 0;
-
- return 0;
-} /* }}} int cldap_shutdown */
-
void module_register(void) /* {{{ */
{
plugin_register_complex_config("openldap", cldap_config);
plugin_register_init("openldap", cldap_init);
- plugin_register_shutdown("openldap", cldap_shutdown);
} /* }}} void module_register */
#if defined(__APPLE__)
* Florian octo Forster <octo at collectd.org>
* Marco Chiappero <marco at absence.it>
* Fabian Schuh <mail at xeroc.org>
+ * Pavel Rochnyak <pavel2000 ngs.ru>
**/
#include "collectd.h"
#include "common.h"
#include "plugin.h"
-#define V1STRING \
+/**
+ * There is two main kinds of OpenVPN status file:
+ * - for 'single' mode (point-to-point or client mode)
+ * - for 'multi' mode (server with multiple clients)
+ *
+ * For 'multi' there is 3 versions of status file format:
+ * - version 1 - First version of status file: without line type tokens,
+ * comma delimited for easy machine parsing. Currently used by default.
+ * Added in openvpn-2.0-beta3.
+ * - version 2 - with line type tokens, with 'HEADER' line type, uses comma
+ * as a delimiter.
+ * Added in openvpn-2.0-beta15.
+ * - version 3 - The only difference from version 2 is delimiter: in version 3
+ * tabs are used instead of commas. Set of fields is the same.
+ * Added in openvpn-2.1_rc14.
+ *
+ * For versions 2/3 there may be different sets of fields in different
+ * OpenVPN versions.
+ *
+ * Versions 2.0, 2.1, 2.2:
+ * Common Name,Real Address,Virtual Address,
+ * Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t)
+ *
+ * Version 2.3:
+ * Common Name,Real Address,Virtual Address,
+ * Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username
+ *
+ * Version 2.4:
+ * Common Name,Real Address,Virtual Address,Virtual IPv6 Address,
+ * Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username,
+ * Client ID,Peer ID
+ *
+ * Current Collectd code tries to handle changes in this field set,
+ * if they are backward-compatible.
+ **/
+
+#define TITLE_SINGLE "OpenVPN STATISTICS\n"
+#define TITLE_V1 "OpenVPN CLIENT LIST\n"
+#define TITLE_V2 "TITLE"
+
+#define V1HEADER \
"Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since\n"
-#define V2STRING \
- "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes " \
- "Received,Bytes Sent,Connected Since,Connected Since (time_t)\n"
-#define V3STRING \
- "HEADER CLIENT_LIST Common Name Real Address Virtual Address Bytes " \
- "Received Bytes Sent Connected Since Connected Since (time_t)\n"
-#define V4STRING \
- "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes " \
- "Received,Bytes Sent,Connected Since,Connected Since (time_t),Username\n"
-#define VSSTRING "OpenVPN STATISTICS\n"
struct vpn_status_s {
char *file;
- enum {
- MULTI1 = 1, /* status-version 1 */
- MULTI2, /* status-version 2 */
- MULTI3, /* status-version 3 */
- MULTI4, /* status-version 4 */
- SINGLE = 10 /* currently no versions for single mode, maybe in the future */
- } version;
char *name;
};
typedef struct vpn_status_s vpn_status_t;
-static vpn_status_t **vpn_list = NULL;
-static int vpn_num = 0;
-
static _Bool new_naming_schema = 0;
static _Bool collect_compression = 1;
static _Bool collect_user_count = 0;
static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
/* Helper function
- * copy-n-pasted from common.c - changed delim to "," */
+ * copy-n-pasted from common.c - changed delim to ",\t" */
static int openvpn_strsplit(char *string, char **fields, size_t size) {
- size_t i;
- char *ptr;
- char *saveptr;
-
- i = 0;
- ptr = string;
- saveptr = NULL;
- while ((fields[i] = strtok_r(ptr, ",", &saveptr)) != NULL) {
+ size_t i = 0;
+ char *ptr = string;
+ char *saveptr = NULL;
+
+ while ((fields[i] = strtok_r(ptr, ",\t", &saveptr)) != NULL) {
ptr = NULL;
i++;
return i;
} /* int openvpn_strsplit */
+static void openvpn_free(void *arg) {
+ vpn_status_t *st = arg;
+
+ sfree(st->file);
+ sfree(st);
+} /* void openvpn_free */
+
/* dispatches number of users */
static void numusers_submit(const char *pinst, const char *tinst,
gauge_t value) {
char buffer[1024];
char *fields[4];
const int max_fields = STATIC_ARRAY_SIZE(fields);
- int fields_num, read = 0;
-
- derive_t link_rx, link_tx;
- derive_t tun_rx, tun_tx;
- derive_t pre_compress, post_compress;
- derive_t pre_decompress, post_decompress;
- derive_t overhead_rx, overhead_tx;
-
- link_rx = 0;
- link_tx = 0;
- tun_rx = 0;
- tun_tx = 0;
- pre_compress = 0;
- post_compress = 0;
- pre_decompress = 0;
- post_decompress = 0;
+
+ derive_t link_rx = 0, link_tx = 0;
+ derive_t tun_rx = 0, tun_tx = 0;
+ derive_t pre_compress = 0, post_compress = 0;
+ derive_t pre_decompress = 0, post_decompress = 0;
while (fgets(buffer, sizeof(buffer), fh) != NULL) {
- fields_num = openvpn_strsplit(buffer, fields, max_fields);
+ int fields_num = openvpn_strsplit(buffer, fields, max_fields);
/* status file is generated by openvpn/sig.c:print_status()
* http://svn.openvpn.net/projects/openvpn/trunk/openvpn/sig.c
iostats_submit(name, "traffic", link_rx, link_tx);
/* we need to force this order to avoid negative values with these unsigned */
- overhead_rx = (((link_rx - pre_decompress) + post_decompress) - tun_rx);
- overhead_tx = (((link_tx - post_compress) + pre_compress) - tun_tx);
+ derive_t overhead_rx =
+ (((link_rx - pre_decompress) + post_decompress) - tun_rx);
+ derive_t overhead_tx = (((link_tx - post_compress) + pre_compress) - tun_tx);
iostats_submit(name, "overhead", overhead_rx, overhead_tx);
compression_submit(name, "data_out", pre_compress, post_compress);
}
- read = 1;
-
- return read;
+ return 0;
} /* int single_read */
/* for reading status version 1 */
static int multi1_read(const char *name, FILE *fh) {
char buffer[1024];
char *fields[10];
- int fields_num, found_header = 0;
+ const int max_fields = STATIC_ARRAY_SIZE(fields);
long long sum_users = 0;
+ _Bool found_header = 0;
/* read the file until the "ROUTING TABLE" line is found (no more info after)
*/
if (strcmp(buffer, "ROUTING TABLE\n") == 0)
break;
- if (strcmp(buffer, V1STRING) == 0) {
+ if (strcmp(buffer, V1HEADER) == 0) {
found_header = 1;
continue;
}
/* we can't start reading data until this string is found */
continue;
- fields_num = openvpn_strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
+ int fields_num = openvpn_strsplit(buffer, fields, max_fields);
if (fields_num < 4)
continue;
}
if (ferror(fh))
- return 0;
+ return -1;
+
+ if (found_header == 0) {
+ NOTICE("openvpn plugin: Unknown file format in instance %s, please "
+ "report this as bug. Make sure to include "
+ "your status file, so the plugin can "
+ "be adapted.",
+ name);
+ return -1;
+ }
if (collect_user_count)
numusers_submit(name, name, sum_users);
- return 1;
+ return 0;
} /* int multi1_read */
-/* for reading status version 2 */
+/* for reading status version 2 / version 3
+ * status file is generated by openvpn/multi.c:multi_print_status()
+ * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
+ */
static int multi2_read(const char *name, FILE *fh) {
char buffer[1024];
- char *fields[10];
+ /* OpenVPN-2.4 has 11 fields of data + 2 fields for "HEADER" and "CLIENT_LIST"
+ * So, set array size to 20 elements, to support future extensions.
+ */
+ char *fields[20];
const int max_fields = STATIC_ARRAY_SIZE(fields);
- int fields_num, read = 0;
long long sum_users = 0;
- while (fgets(buffer, sizeof(buffer), fh) != NULL) {
- fields_num = openvpn_strsplit(buffer, fields, max_fields);
-
- /* status file is generated by openvpn/multi.c:multi_print_status()
- * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
- *
- * The line we're expecting has 8 fields. We ignore all lines
- * with more or less fields.
- */
- if (fields_num != 8)
- continue;
-
- if (strcmp(fields[0], "CLIENT_LIST") != 0)
- continue;
-
- if (collect_user_count)
- /* If so, sum all users, ignore the individuals*/
- {
- sum_users += 1;
- }
- if (collect_individual_users) {
- if (new_naming_schema) {
- /* plugin inst = file name, type inst = fields[1] */
- iostats_submit(name, /* vpn instance */
- fields[1], /* "Common Name" */
- atoll(fields[4]), /* "Bytes Received" */
- atoll(fields[5])); /* "Bytes Sent" */
- } else {
- /* plugin inst = fields[1], type inst = "" */
- iostats_submit(fields[1], /* "Common Name" */
- NULL, /* unused when in multimode */
- atoll(fields[4]), /* "Bytes Received" */
- atoll(fields[5])); /* "Bytes Sent" */
- }
- }
-
- read = 1;
- }
-
- if (collect_user_count) {
- numusers_submit(name, name, sum_users);
- read = 1;
- }
-
- return read;
-} /* int multi2_read */
-
-/* for reading status version 3 */
-static int multi3_read(const char *name, FILE *fh) {
- char buffer[1024];
- char *fields[15];
- const int max_fields = STATIC_ARRAY_SIZE(fields);
- int fields_num, read = 0;
- long long sum_users = 0;
+ _Bool found_header = 0;
+ int idx_cname = 0;
+ int idx_bytes_recv = 0;
+ int idx_bytes_sent = 0;
+ int columns = 0;
while (fgets(buffer, sizeof(buffer), fh) != NULL) {
- fields_num = strsplit(buffer, fields, max_fields);
+ int fields_num = openvpn_strsplit(buffer, fields, max_fields);
- /* status file is generated by openvpn/multi.c:multi_print_status()
- * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
- *
- * The line we're expecting has 12 fields. We ignore all lines
- * with more or less fields.
- */
- if (fields_num != 12) {
- continue;
- } else {
- if (strcmp(fields[0], "CLIENT_LIST") != 0)
+ /* Try to find section header */
+ if (found_header == 0) {
+ if (fields_num < 2)
+ continue;
+ if (strcmp(fields[0], "HEADER") != 0)
+ continue;
+ if (strcmp(fields[1], "CLIENT_LIST") != 0)
continue;
- if (collect_user_count)
- /* If so, sum all users, ignore the individuals*/
- {
- sum_users += 1;
- }
-
- if (collect_individual_users) {
- if (new_naming_schema) {
- iostats_submit(name, /* vpn instance */
- fields[1], /* "Common Name" */
- atoll(fields[4]), /* "Bytes Received" */
- atoll(fields[5])); /* "Bytes Sent" */
- } else {
- iostats_submit(fields[1], /* "Common Name" */
- NULL, /* unused when in multimode */
- atoll(fields[4]), /* "Bytes Received" */
- atoll(fields[5])); /* "Bytes Sent" */
+ for (int i = 2; i < fields_num; i++) {
+ if (strcmp(fields[i], "Common Name") == 0) {
+ idx_cname = i - 1;
+ } else if (strcmp(fields[i], "Bytes Received") == 0) {
+ idx_bytes_recv = i - 1;
+ } else if (strcmp(fields[i], "Bytes Sent") == 0) {
+ idx_bytes_sent = i - 1;
}
}
- read = 1;
- }
- }
-
- if (collect_user_count) {
- numusers_submit(name, name, sum_users);
- read = 1;
- }
+ DEBUG("openvpn plugin: found MULTI v2/v3 HEADER. "
+ "Column idx: cname: %d, bytes_recv: %d, bytes_sent: %d",
+ idx_cname, idx_bytes_recv, idx_bytes_sent);
- return read;
-} /* int multi3_read */
+ if (idx_cname == 0 || idx_bytes_recv == 0 || idx_bytes_sent == 0)
+ break;
-/* for reading status version 4 */
-static int multi4_read(const char *name, FILE *fh) {
- char buffer[1024];
- char *fields[11];
- const int max_fields = STATIC_ARRAY_SIZE(fields);
- int fields_num, read = 0;
- long long sum_users = 0;
+ /* Data row has 1 field ("HEADER") less than header row */
+ columns = fields_num - 1;
- while (fgets(buffer, sizeof(buffer), fh) != NULL) {
- fields_num = openvpn_strsplit(buffer, fields, max_fields);
+ found_header = 1;
+ continue;
+ }
- /* status file is generated by openvpn/multi.c:multi_print_status()
- * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
- *
- * The line we're expecting has 9 fields. We ignore all lines
- * with more or less fields.
+ /* Header already found. Check if the line is the section data.
+ * If no match, then section was finished and there is no more data.
+ * Empty section is OK too.
*/
- if (fields_num != 9)
- continue;
+ if (fields_num == 0 || strcmp(fields[0], "CLIENT_LIST") != 0)
+ break;
- if (strcmp(fields[0], "CLIENT_LIST") != 0)
- continue;
+ /* Check if the data line fields count matches header line. */
+ if (fields_num != columns) {
+ ERROR("openvpn plugin: File format error in instance %s: Fields count "
+ "mismatch.",
+ name);
+ return -1;
+ }
+
+ DEBUG("openvpn plugin: found MULTI v2/v3 CLIENT_LIST. "
+ "Columns: cname: %s, bytes_recv: %s, bytes_sent: %s",
+ fields[idx_cname], fields[idx_bytes_recv], fields[idx_bytes_sent]);
if (collect_user_count)
- /* If so, sum all users, ignore the individuals*/
- {
sum_users += 1;
- }
+
if (collect_individual_users) {
if (new_naming_schema) {
/* plugin inst = file name, type inst = fields[1] */
- iostats_submit(name, /* vpn instance */
- fields[1], /* "Common Name" */
- atoll(fields[4]), /* "Bytes Received" */
- atoll(fields[5])); /* "Bytes Sent" */
+ iostats_submit(name, /* vpn instance */
+ fields[idx_cname], /* "Common Name" */
+ atoll(fields[idx_bytes_recv]), /* "Bytes Received" */
+ atoll(fields[idx_bytes_sent])); /* "Bytes Sent" */
} else {
- /* plugin inst = fields[1], type inst = "" */
- iostats_submit(fields[1], /* "Common Name" */
- NULL, /* unused when in multimode */
- atoll(fields[4]), /* "Bytes Received" */
- atoll(fields[5])); /* "Bytes Sent" */
+ /* plugin inst = fields[idx_cname], type inst = "" */
+ iostats_submit(fields[idx_cname], /* "Common Name" */
+ NULL, /* unused when in multimode */
+ atoll(fields[idx_bytes_recv]), /* "Bytes Received" */
+ atoll(fields[idx_bytes_sent])); /* "Bytes Sent" */
}
}
+ }
+
+ if (ferror(fh))
+ return -1;
- read = 1;
+ if (found_header == 0) {
+ NOTICE("openvpn plugin: Unknown file format in instance %s, please "
+ "report this as bug. Make sure to include "
+ "your status file, so the plugin can "
+ "be adapted.",
+ name);
+ return -1;
}
if (collect_user_count) {
numusers_submit(name, name, sum_users);
- read = 1;
}
- return read;
-} /* int multi4_read */
+ return 0;
+} /* int multi2_read */
/* read callback */
-static int openvpn_read(void) {
- FILE *fh;
- int read;
-
- read = 0;
-
- if (vpn_num == 0)
- return 0;
-
- /* call the right read function for every status entry in the list */
- for (int i = 0; i < vpn_num; i++) {
- int vpn_read = 0;
-
- fh = fopen(vpn_list[i]->file, "r");
- if (fh == NULL) {
- char errbuf[1024];
- WARNING("openvpn plugin: fopen(%s) failed: %s", vpn_list[i]->file,
- sstrerror(errno, errbuf, sizeof(errbuf)));
-
- continue;
- }
-
- switch (vpn_list[i]->version) {
- case SINGLE:
- vpn_read = single_read(vpn_list[i]->name, fh);
- break;
-
- case MULTI1:
- vpn_read = multi1_read(vpn_list[i]->name, fh);
- break;
-
- case MULTI2:
- vpn_read = multi2_read(vpn_list[i]->name, fh);
- break;
-
- case MULTI3:
- vpn_read = multi3_read(vpn_list[i]->name, fh);
- break;
-
- case MULTI4:
- vpn_read = multi4_read(vpn_list[i]->name, fh);
- break;
- }
-
- fclose(fh);
- read += vpn_read;
- }
-
- return read ? 0 : -1;
-} /* int openvpn_read */
-
-static int version_detect(const char *filename) {
- FILE *fh;
+static int openvpn_read(user_data_t *user_data) {
char buffer[1024];
- int version = 0;
+ int read = 0;
- /* Sanity checking. We're called from the config handling routine, so
- * better play it save. */
- if ((filename == NULL) || (*filename == 0))
- return 0;
+ vpn_status_t *st = user_data->data;
- fh = fopen(filename, "r");
+ FILE *fh = fopen(st->file, "r");
if (fh == NULL) {
char errbuf[1024];
- WARNING("openvpn plugin: Unable to read \"%s\": %s", filename,
+ WARNING("openvpn plugin: fopen(%s) failed: %s", st->file,
sstrerror(errno, errbuf, sizeof(errbuf)));
- return 0;
+
+ return -1;
}
- /* now search for the specific multimode data format */
- while ((fgets(buffer, sizeof(buffer), fh)) != NULL) {
- /* we look at the first line searching for SINGLE mode configuration */
- if (strcmp(buffer, VSSTRING) == 0) {
- DEBUG("openvpn plugin: found status file version SINGLE");
- version = SINGLE;
- break;
- }
- /* searching for multi version 1 */
- else if (strcmp(buffer, V1STRING) == 0) {
- DEBUG("openvpn plugin: found status file version MULTI1");
- version = MULTI1;
- break;
- }
- /* searching for multi version 2 */
- else if (strcmp(buffer, V2STRING) == 0) {
- DEBUG("openvpn plugin: found status file version MULTI2");
- version = MULTI2;
- break;
- }
- /* searching for multi version 3 */
- else if (strcmp(buffer, V3STRING) == 0) {
- DEBUG("openvpn plugin: found status file version MULTI3");
- version = MULTI3;
- break;
- }
- /* searching for multi version 4 */
- else if (strcmp(buffer, V4STRING) == 0) {
- DEBUG("openvpn plugin: found status file version MULTI4");
- version = MULTI4;
- break;
- }
+ // Try to detect file format by its first line
+ if ((fgets(buffer, sizeof(buffer), fh)) == NULL) {
+ WARNING("openvpn plugin: failed to get data from: %s", st->file);
+ fclose(fh);
+ return -1;
}
- if (version == 0) {
- /* This is only reached during configuration, so complaining to
- * the user is in order. */
+ if (strcmp(buffer, TITLE_SINGLE) == 0) { // OpenVPN STATISTICS
+ DEBUG("openvpn plugin: found status file SINGLE");
+ read = single_read(st->name, fh);
+ } else if (strcmp(buffer, TITLE_V1) == 0) { // OpenVPN CLIENT LIST
+ DEBUG("openvpn plugin: found status file MULTI version 1");
+ read = multi1_read(st->name, fh);
+ } else if (strncmp(buffer, TITLE_V2, strlen(TITLE_V2)) == 0) { // TITLE
+ DEBUG("openvpn plugin: found status file MULTI version 2/3");
+ read = multi2_read(st->name, fh);
+ } else {
NOTICE("openvpn plugin: %s: Unknown file format, please "
"report this as bug. Make sure to include "
"your status file, so the plugin can "
"be adapted.",
- filename);
+ st->file);
+ read = -1;
}
-
fclose(fh);
-
- return version;
-} /* int version_detect */
+ return read;
+} /* int openvpn_read */
static int openvpn_config(const char *key, const char *value) {
if (strcasecmp("StatusFile", key) == 0) {
- char *status_file, *status_name, *filename;
- int status_version;
- vpn_status_t *temp;
+ char callback_name[3 * DATA_MAX_NAME_LEN];
+ char *status_name;
- /* try to detect the status file format */
- status_version = version_detect(value);
-
- if (status_version == 0) {
- WARNING("openvpn plugin: unable to detect status version, "
- "discarding status file \"%s\".",
- value);
- return 1;
- }
-
- status_file = sstrdup(value);
+ char *status_file = strdup(value);
if (status_file == NULL) {
char errbuf[1024];
- WARNING("openvpn plugin: sstrdup failed: %s",
- sstrerror(errno, errbuf, sizeof(errbuf)));
+ ERROR("openvpn plugin: strdup failed: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
return 1;
}
/* it determines the file name as string starting at location filename + 1
*/
- filename = strrchr(status_file, (int)'/');
+ char *filename = strrchr(status_file, (int)'/');
if (filename == NULL) {
/* status_file is already the file name only */
status_name = status_file;
status_name = filename + 1;
}
- /* scan the list looking for a clone */
- for (int i = 0; i < vpn_num; i++) {
- if (strcasecmp(vpn_list[i]->name, status_name) == 0) {
- WARNING("openvpn plugin: status filename \"%s\" "
- "already used, please choose a "
- "different one.",
- status_name);
- sfree(status_file);
- return 1;
- }
- }
-
- /* create a new vpn element since file, version and name are ok */
- temp = malloc(sizeof(*temp));
- if (temp == NULL) {
+ /* create a new vpn element */
+ vpn_status_t *instance = calloc(1, sizeof(*instance));
+ if (instance == NULL) {
char errbuf[1024];
ERROR("openvpn plugin: malloc failed: %s",
sstrerror(errno, errbuf, sizeof(errbuf)));
sfree(status_file);
return 1;
}
- temp->file = status_file;
- temp->version = status_version;
- temp->name = status_name;
-
- vpn_status_t **tmp_list =
- realloc(vpn_list, (vpn_num + 1) * sizeof(*vpn_list));
- if (tmp_list == NULL) {
- char errbuf[1024];
- ERROR("openvpn plugin: realloc failed: %s",
- sstrerror(errno, errbuf, sizeof(errbuf)));
-
- sfree(vpn_list);
- sfree(temp->file);
- sfree(temp);
- return 1;
+ instance->file = status_file;
+ instance->name = status_name;
+
+ snprintf(callback_name, sizeof(callback_name), "openvpn/%s", status_name);
+
+ int status = plugin_register_complex_read(
+ /* group = */ "openvpn",
+ /* name = */ callback_name,
+ /* callback = */ openvpn_read,
+ /* interval = */ 0,
+ &(user_data_t){
+ .data = instance, .free_func = openvpn_free,
+ });
+
+ if (status == EINVAL) {
+ WARNING("openvpn plugin: status filename \"%s\" "
+ "already used, please choose a "
+ "different one.",
+ status_name);
+ return -1;
}
- vpn_list = tmp_list;
-
- vpn_list[vpn_num] = temp;
- vpn_num++;
-
- DEBUG("openvpn plugin: status file \"%s\" added", temp->file);
+ DEBUG("openvpn plugin: status file \"%s\" added", instance->file);
} /* if (strcasecmp ("StatusFile", key) == 0) */
else if ((strcasecmp("CollectCompression", key) == 0) ||
(strcasecmp("Compression", key) == 0)) /* old, deprecated name */
return 0;
} /* int openvpn_config */
-/* shutdown callback */
-static int openvpn_shutdown(void) {
- for (int i = 0; i < vpn_num; i++) {
- sfree(vpn_list[i]->file);
- sfree(vpn_list[i]);
- }
-
- sfree(vpn_list);
-
- return 0;
-} /* int openvpn_shutdown */
-
static int openvpn_init(void) {
if (!collect_individual_users && !collect_compression &&
!collect_user_count) {
return -1;
}
- plugin_register_read("openvpn", openvpn_read);
- plugin_register_shutdown("openvpn", openvpn_shutdown);
-
return 0;
} /* int openvpn_init */
char *connect_id;
char *username;
char *password;
+ char *plugin_name;
udb_query_preparation_area_t **q_prep_areas;
udb_query_t **queries;
sfree(db->username);
sfree(db->password);
sfree(db->queries);
+ sfree(db->plugin_name);
if (db->q_prep_areas != NULL)
for (size_t i = 0; i < db->queries_num; ++i)
db->connect_id = NULL;
db->username = NULL;
db->password = NULL;
+ db->plugin_name = NULL;
status = cf_util_get_string(ci, &db->name);
if (status != 0) {
status = cf_util_get_string(child, &db->username);
else if (strcasecmp("Password", child->key) == 0)
status = cf_util_get_string(child, &db->password);
+ else if (strcasecmp("Plugin", child->key) == 0)
+ status = cf_util_get_string(child, &db->plugin_name);
else if (strcasecmp("Query", child->key) == 0)
status = udb_query_pick_from_list(child, queries, queries_num,
&db->queries, &db->queries_num);
status = udb_query_prepare_result(
q, prep_area, (db->host != NULL) ? db->host : hostname_g,
- /* plugin = */ "oracle", db->name, column_names, column_num,
+ /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "oracle",
+ db->name, column_names, column_num,
/* interval = */ 0);
if (status != 0) {
ERROR("oracle plugin: o_read_database_query (%s, %s): "
{"Collectd::NOTIF_WARNING", NOTIF_WARNING},
{"Collectd::NOTIF_OKAY", NOTIF_OKAY},
{"", 0}};
-
-struct {
- char name[64];
- char *var;
-} g_strings[] = {{"Collectd::hostname_g", hostname_g}, {"", NULL}};
-
/*
* Helper functions for data type conversion.
*/
if (NULL != (tmp = hv_fetch(hash, "host", 4, 0)))
sstrncpy(vl->host, SvPV_nolen(*tmp), sizeof(vl->host));
- else
- sstrncpy(vl->host, hostname_g, sizeof(vl->host));
if (NULL != (tmp = hv_fetch(hash, "plugin", 6, 0)))
sstrncpy(vl->plugin, SvPV_nolen(*tmp), sizeof(vl->plugin));
ret = plugin_register_flush("perl", perl_flush, /* user_data = */ NULL);
}
- if (0 == ret)
+ if (0 == ret) {
ret = plugin_register_flush(pluginname, perl_flush, &userdata);
+ } else {
+ free(userdata.data);
+ }
} else {
ret = -1;
}
if (0 == ret)
XSRETURN_YES;
- else {
- free(userdata.data);
+ else
XSRETURN_EMPTY;
- }
} /* static void _plugin_register_generic_userdata ( ... ) */
/*
/* 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);
/* 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.
- */
+ */
if (aTHX == perl_threads->head->interp)
pthread_mutex_lock(&perl_threads->mutex);
* accessing any such variable (this is basically the same as using
* tie() in Perl) */
/* global strings */
+ struct {
+ char name[64];
+ char *var;
+ } g_strings[] = {{"Collectd::hostname_g", hostname_g}, {"", NULL}};
+
for (int i = 0; '\0' != g_strings[i].name[0]; ++i) {
tmp = get_sv(g_strings[i].name, 1);
sv_magicext(tmp, NULL, PERL_MAGIC_ext, &g_pv_vtbl, g_strings[i].var, 0);
char *plugin;
HV *config;
+ if (NULL == perl_threads) {
+ log_err("A `Plugin' block was encountered but no plugin was loaded yet. "
+ "Put the appropriate `LoadPlugin' option in front of it.");
+ return -1;
+ }
+
dSP;
if ((1 != ci->values_num) || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
char *password;
char *instance;
+ char *plugin_name;
char *sslmode;
db->instance = sstrdup(name);
+ db->plugin_name = NULL;
+
db->sslmode = NULL;
db->krbsrvname = NULL;
sfree(db->instance);
+ sfree(db->plugin_name);
+
sfree(db->sslmode);
sfree(db->krbsrvname);
else
host = db->host;
- status =
- udb_query_prepare_result(q, prep_area, host, "postgresql", db->instance,
- column_names, (size_t)column_num, db->interval);
+ status = udb_query_prepare_result(
+ q, prep_area, host,
+ (db->plugin_name != NULL) ? db->plugin_name : "postgresql",
+ db->instance, column_names, (size_t)column_num, db->interval);
+
if (0 != status) {
log_err("udb_query_prepare_result failed with status %i.", status);
BAIL_OUT(-1);
cf_util_get_string(c, &db->password);
else if (0 == strcasecmp(c->key, "Instance"))
cf_util_get_string(c, &db->instance);
+ else if (0 == strcasecmp (c->key, "Plugin"))
+ cf_util_get_string (c, &db->plugin_name);
else if (0 == strcasecmp(c->key, "SSLMode"))
cf_util_get_string(c, &db->sslmode);
else if (0 == strcasecmp(c->key, "KRBSrvName"))
* Clément Stenac <clement.stenac at diwi.org>
* Cosmin Ioiart <cioiart at gmail.com>
* Pavel Rochnyack <pavel2000 at ngs.ru>
+ * Wilfried Goesgens <dothebart at citadel.org>
**/
#include "collectd.h"
unsigned long num_proc;
unsigned long num_lwp;
unsigned long num_fd;
+ unsigned long num_maps;
unsigned long vmem_size;
unsigned long vmem_rss;
unsigned long vmem_data;
derive_t io_wchar;
derive_t io_syscr;
derive_t io_syscw;
+ derive_t io_diskr;
+ derive_t io_diskw;
_Bool has_io;
derive_t cswitch_vol;
_Bool has_cswitch;
_Bool has_fd;
+
+ _Bool has_maps;
} process_entry_t;
typedef struct procstat_entry_s {
derive_t io_wchar;
derive_t io_syscr;
derive_t io_syscw;
+ derive_t io_diskr;
+ derive_t io_diskw;
derive_t cswitch_vol;
derive_t cswitch_invol;
unsigned long num_proc;
unsigned long num_lwp;
unsigned long num_fd;
+ unsigned long num_maps;
unsigned long vmem_size;
unsigned long vmem_rss;
unsigned long vmem_data;
derive_t io_wchar;
derive_t io_syscr;
derive_t io_syscw;
+ derive_t io_diskr;
+ derive_t io_diskw;
derive_t cswitch_vol;
derive_t cswitch_invol;
_Bool report_fd_num;
+ _Bool report_maps_num;
_Bool report_ctx_switch;
struct procstat *next;
static _Bool want_init = 1;
static _Bool report_ctx_switch = 0;
static _Bool report_fd_num = 0;
+static _Bool report_maps_num = 0;
#if HAVE_THREAD_INFO
static mach_port_t port_host_self;
new->io_wchar = -1;
new->io_syscr = -1;
new->io_syscw = -1;
+ new->io_diskr = -1;
+ new->io_diskw = -1;
new->cswitch_vol = -1;
new->cswitch_invol = -1;
new->report_fd_num = report_fd_num;
+ new->report_maps_num = report_maps_num;
new->report_ctx_switch = report_ctx_switch;
#if HAVE_REGEX_H
ps->num_proc += entry->num_proc;
ps->num_lwp += entry->num_lwp;
ps->num_fd += entry->num_fd;
+ ps->num_maps += entry->num_maps;
ps->vmem_size += entry->vmem_size;
ps->vmem_rss += entry->vmem_rss;
ps->vmem_data += entry->vmem_data;
ps_update_counter(&ps->io_syscw, &pse->io_syscw, entry->io_syscw);
}
+ if ((entry->io_diskr != -1) && (entry->io_diskw != -1)) {
+ ps_update_counter(&ps->io_diskr, &pse->io_diskr, entry->io_diskr);
+ ps_update_counter(&ps->io_diskw, &pse->io_diskw, entry->io_diskw);
+ }
+
if ((entry->cswitch_vol != -1) && (entry->cswitch_vol != -1)) {
ps_update_counter(&ps->cswitch_vol, &pse->cswitch_vol,
entry->cswitch_vol);
ps->num_proc = 0;
ps->num_lwp = 0;
ps->num_fd = 0;
+ ps->num_maps = 0;
ps->vmem_size = 0;
ps->vmem_rss = 0;
ps->vmem_data = 0;
cf_util_get_boolean(c, &ps->report_ctx_switch);
else if (strcasecmp(c->key, "CollectFileDescriptor") == 0)
cf_util_get_boolean(c, &ps->report_fd_num);
+ else if (strcasecmp(c->key, "CollectMemoryMaps") == 0)
+ cf_util_get_boolean(c, &ps->report_maps_num);
else {
ERROR("processes plugin: Option `%s' not allowed here.", c->key);
}
cf_util_get_boolean(c, &report_ctx_switch);
} else if (strcasecmp(c->key, "CollectFileDescriptor") == 0) {
cf_util_get_boolean(c, &report_fd_num);
+ } else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) {
+ cf_util_get_boolean(c, &report_maps_num);
} else {
ERROR("processes plugin: The `%s' configuration option is not "
"understood and will be ignored.",
plugin_dispatch_values(&vl);
if ((ps->io_rchar != -1) && (ps->io_wchar != -1)) {
- sstrncpy(vl.type, "ps_disk_octets", sizeof(vl.type));
+ sstrncpy(vl.type, "io_octets", sizeof(vl.type));
vl.values[0].derive = ps->io_rchar;
vl.values[1].derive = ps->io_wchar;
vl.values_len = 2;
}
if ((ps->io_syscr != -1) && (ps->io_syscw != -1)) {
- sstrncpy(vl.type, "ps_disk_ops", sizeof(vl.type));
+ sstrncpy(vl.type, "io_ops", sizeof(vl.type));
vl.values[0].derive = ps->io_syscr;
vl.values[1].derive = ps->io_syscw;
vl.values_len = 2;
plugin_dispatch_values(&vl);
}
+ if ((ps->io_diskr != -1) && (ps->io_diskw != -1)) {
+ sstrncpy(vl.type, "disk_octets", sizeof(vl.type));
+ vl.values[0].derive = ps->io_diskr;
+ vl.values[1].derive = ps->io_diskw;
+ vl.values_len = 2;
+ plugin_dispatch_values(&vl);
+ }
+
if (ps->num_fd > 0) {
sstrncpy(vl.type, "file_handles", sizeof(vl.type));
vl.values[0].gauge = ps->num_fd;
plugin_dispatch_values(&vl);
}
+ if (ps->num_maps > 0) {
+ sstrncpy(vl.type, "file_handles", sizeof(vl.type));
+ sstrncpy(vl.type_instance, "mapped", sizeof(vl.type_instance));
+ vl.values[0].gauge = ps->num_maps;
+ vl.values_len = 1;
+ plugin_dispatch_values(&vl);
+ }
+
if ((ps->cswitch_vol != -1) && (ps->cswitch_invol != -1)) {
sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
sstrncpy(vl.type_instance, "voluntary", sizeof(vl.type_instance));
plugin_dispatch_values(&vl);
}
- DEBUG("name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; "
- "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
- "vmem_code = %lu; "
- "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
- "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
- "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
- "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
- "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";",
- ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->vmem_size,
- ps->vmem_rss, ps->vmem_data, ps->vmem_code, ps->vmem_minflt_counter,
- ps->vmem_majflt_counter, ps->cpu_user_counter, ps->cpu_system_counter,
- ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw, ps->cswitch_vol,
- ps->cswitch_invol);
+ DEBUG(
+ "name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; num_maps = %lu; "
+ "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
+ "vmem_code = %lu; "
+ "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
+ "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
+ "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
+ "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
+ "io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; "
+ "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";",
+ ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->num_maps,
+ ps->vmem_size, ps->vmem_rss, ps->vmem_data, ps->vmem_code,
+ ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter,
+ ps->cpu_system_counter, ps->io_rchar, ps->io_wchar, ps->io_syscr,
+ ps->io_syscw, ps->io_diskr, ps->io_diskw, ps->cswitch_vol,
+ ps->cswitch_invol);
+
} /* void ps_submit_proc_list */
#if KERNEL_LINUX || KERNEL_SOLARIS
val = &(ps->io_syscr);
else if (strncasecmp(buffer, "syscw:", 6) == 0)
val = &(ps->io_syscw);
+ else if (strncasecmp(buffer, "read_bytes:", 11) == 0)
+ val = &(ps->io_diskr);
+ else if (strncasecmp(buffer, "write_bytes:", 12) == 0)
+ val = &(ps->io_diskw);
else
continue;
return 0;
} /* int ps_read_io (...) */
+static int ps_count_maps(pid_t pid) {
+ FILE *fh;
+ char buffer[1024];
+ char filename[64];
+ int count = 0;
+
+ snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
+ if ((fh = fopen(filename, "r")) == NULL) {
+ DEBUG("ps_count_maps: Failed to open file `%s'", filename);
+ return -1;
+ }
+
+ while (fgets(buffer, sizeof(buffer), fh) != NULL) {
+ if (strchr(buffer, '\n')) {
+ count++;
+ }
+ } /* while (fgets) */
+
+ if (fclose(fh)) {
+ char errbuf[1024];
+ WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+ }
+ return count;
+} /* int ps_count_maps (...) */
+
static int ps_count_fd(int pid) {
char dirname[64];
DIR *dh;
}
}
+ if (ps->report_maps_num) {
+ int num_maps;
+ if (entry->has_maps == 0 && (num_maps = ps_count_maps(entry->id)) > 0) {
+ entry->num_maps = num_maps;
+ }
+ entry->has_maps = 1;
+ }
+
if (ps->report_fd_num) {
int num_fd;
if (entry->has_fd == 0 && (num_fd = ps_count_fd(entry->id)) > 0) {
ps->io_wchar = -1;
ps->io_syscr = -1;
ps->io_syscw = -1;
+ ps->io_diskr = -1;
+ ps->io_diskw = -1;
ps->cswitch_vol = -1;
ps->cswitch_invol = -1;
*/
ps->num_fd = 0;
+ /* Number of memory mappings */
+ ps->num_maps = 0;
+
/*
* Calculating input/ouput chars
* Formula used is total chars / total blocks => chars/block
ps->io_wchar = myUsage->pr_oublk * chars_per_block;
ps->io_syscr = myUsage->pr_sysc;
ps->io_syscw = myUsage->pr_sysc;
+ ps->io_diskr = -1;
+ ps->io_diskw = -1;
/*
* TODO: context switch counters for Solaris
pse.io_wchar = -1;
pse.io_syscr = -1;
pse.io_syscw = -1;
+ pse.io_diskr = -1;
+ pse.io_diskw = -1;
/* File descriptor count not implemented */
pse.num_fd = 0;
+ /* Number of memory mappings */
+ pse.num_maps = 0;
+
pse.vmem_minflt_counter = task_events_info.cow_faults;
pse.vmem_majflt_counter = task_events_info.faults;
pse.io_wchar = -1;
pse.io_syscr = -1;
pse.io_syscw = -1;
+ pse.io_diskr = -1;
+ pse.io_diskw = -1;
/* file descriptor count not implemented */
pse.num_fd = 0;
+ /* Number of memory mappings */
+ pse.num_maps = 0;
+
/* context switch counters not implemented */
pse.cswitch_vol = -1;
pse.cswitch_invol = -1;
pse.io_wchar = -1;
pse.io_syscr = -1;
pse.io_syscw = -1;
+ pse.io_diskr = -1;
+ pse.io_diskw = -1;
/* file descriptor count not implemented */
pse.num_fd = 0;
+ /* Number of memory mappings */
+ pse.num_maps = 0;
+
/* context switch counters not implemented */
pse.cswitch_vol = -1;
pse.cswitch_invol = -1;
pse.io_wchar = -1;
pse.io_syscr = -1;
pse.io_syscw = -1;
+ pse.io_diskr = -1;
+ pse.io_diskw = -1;
pse.num_fd = 0;
+ pse.num_maps = 0;
pse.cswitch_vol = -1;
pse.cswitch_invol = -1;
}
dict = PyDict_New(); /* New reference. */
if (value_list->meta) {
- char **table;
+ char **table = NULL;
meta_data_t *meta = value_list->meta;
int num = meta_data_toc(meta, &table);
} else if (type == MD_TYPE_SIGNED_INT) {
if (meta_data_get_signed_int(meta, table[i], &si))
continue;
- temp = PyObject_CallFunctionObjArgs((void *)&SignedType,
- PyLong_FromLongLong(si),
+ PyObject *sival = PyLong_FromLongLong(si); /* New reference */
+ temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
(void *)0); /* New reference. */
PyDict_SetItemString(dict, table[i], temp);
Py_XDECREF(temp);
+ Py_XDECREF(sival);
} else if (type == MD_TYPE_UNSIGNED_INT) {
if (meta_data_get_unsigned_int(meta, table[i], &ui))
continue;
- temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType,
- PyLong_FromUnsignedLongLong(ui),
+ PyObject *uval = PyLong_FromUnsignedLongLong(ui); /* New reference */
+ temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
(void *)0); /* New reference. */
PyDict_SetItemString(dict, table[i], temp);
Py_XDECREF(temp);
+ Py_XDECREF(uval);
} else if (type == MD_TYPE_DOUBLE) {
if (meta_data_get_double(meta, table[i], &d))
continue;
Notification *n;
CPY_LOCK_THREADS
+ PyObject *dict = PyDict_New(); /* New reference. */
+ for (notification_meta_t *meta = notification->meta; meta != NULL;
+ meta = meta->next) {
+ PyObject *temp = NULL;
+ if (meta->type == NM_TYPE_STRING) {
+ temp = cpy_string_to_unicode_or_bytes(
+ meta->nm_value.nm_string); /* New reference. */
+ PyDict_SetItemString(dict, meta->name, temp);
+ Py_XDECREF(temp);
+ } else if (meta->type == NM_TYPE_SIGNED_INT) {
+ PyObject *sival = PyLong_FromLongLong(meta->nm_value.nm_signed_int);
+ temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
+ (void *)0); /* New reference. */
+ PyDict_SetItemString(dict, meta->name, temp);
+ Py_XDECREF(temp);
+ Py_XDECREF(sival);
+ } else if (meta->type == NM_TYPE_UNSIGNED_INT) {
+ PyObject *uval =
+ PyLong_FromUnsignedLongLong(meta->nm_value.nm_unsigned_int);
+ temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
+ (void *)0); /* New reference. */
+ PyDict_SetItemString(dict, meta->name, temp);
+ Py_XDECREF(temp);
+ Py_XDECREF(uval);
+ } else if (meta->type == NM_TYPE_DOUBLE) {
+ temp = PyFloat_FromDouble(meta->nm_value.nm_double); /* New reference. */
+ PyDict_SetItemString(dict, meta->name, temp);
+ Py_XDECREF(temp);
+ } else if (meta->type == NM_TYPE_BOOLEAN) {
+ PyDict_SetItemString(dict, meta->name,
+ meta->nm_value.nm_boolean ? Py_True : Py_False);
+ }
+ }
notify = Notification_New(); /* New reference. */
n = (Notification *)notify;
sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
sstrncpy(n->message, notification->message, sizeof(n->message));
n->severity = notification->severity;
+ Py_CLEAR(n->meta);
+ n->meta = dict; /* Steals a reference. */
ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data,
(void *)0); /* New reference. */
Py_XDECREF(notify);
for (size_t i = 0; i < ds->ds_num; ++i) {
tuple = PyTuple_New(4);
PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
- PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(
- DS_TYPE_TO_STRING(ds->ds[i].type)));
+ PyTuple_SET_ITEM(
+ tuple, 1,
+ cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
PyList_SET_ITEM(list, i, tuple);
register_function(buf, handler,
&(user_data_t){
- .data = c, .free_func = cpy_destroy_user_data,
+ .data = c,
+ .free_func = cpy_destroy_user_data,
});
++cpy_num_callbacks;
/* group = */ "python", buf, cpy_read_callback,
DOUBLE_TO_CDTIME_T(interval),
&(user_data_t){
- .data = c, .free_func = cpy_destroy_user_data,
+ .data = c,
+ .free_func = cpy_destroy_user_data,
});
++cpy_num_callbacks;
return cpy_string_to_unicode_or_bytes(buf);
values = PyTuple_New(ci->values_num); /* New reference. */
for (int i = 0; i < ci->values_num; ++i) {
if (ci->values[i].type == OCONFIG_TYPE_STRING) {
- PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(
- ci->values[i].value.string));
+ PyTuple_SET_ITEM(
+ values, i,
+ cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
} else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
PyTuple_SET_ITEM(values, i,
PyFloat_FromDouble(ci->values[i].value.number));
#include "cpython.h"
+typedef struct {
+ int (*add_string)(void *, const char *, const char *);
+ int (*add_signed_int)(void *, const char *, int64_t);
+ int (*add_unsigned_int)(void *, const char *, uint64_t);
+ int (*add_double)(void *, const char *, double);
+ int (*add_boolean)(void *, const char *, _Bool);
+} cpy_build_meta_handler_t;
+
#define FreeAll() \
do { \
PyMem_Free(type); \
return 0;
}
-static meta_data_t *cpy_build_meta(PyObject *meta) {
+static int cpy_build_meta_generic(PyObject *meta,
+ cpy_build_meta_handler_t *meta_func,
+ void *m) {
int s;
- meta_data_t *m = NULL;
PyObject *l;
if ((meta == NULL) || (meta == Py_None))
- return NULL;
+ return -1;
l = PyDict_Items(meta); /* New reference. */
if (!l) {
cpy_log_exception("building meta data");
- return NULL;
+ return -1;
}
s = PyList_Size(l);
if (s <= 0) {
Py_XDECREF(l);
- return NULL;
+ return -1;
}
- m = meta_data_create();
for (int i = 0; i < s; ++i) {
const char *string, *keystring;
PyObject *key, *value, *item, *tmp;
value = PyTuple_GET_ITEM(item, 1);
Py_INCREF(value);
if (value == Py_True) {
- meta_data_add_boolean(m, keystring, 1);
+ meta_func->add_boolean(m, keystring, 1);
} else if (value == Py_False) {
- meta_data_add_boolean(m, keystring, 0);
+ meta_func->add_boolean(m, keystring, 0);
} else if (PyFloat_Check(value)) {
- meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
+ meta_func->add_double(m, keystring, PyFloat_AsDouble(value));
} else if (PyObject_TypeCheck(value, &SignedType)) {
long long int lli;
lli = PyLong_AsLongLong(value);
if (!PyErr_Occurred() && (lli == (int64_t)lli))
- meta_data_add_signed_int(m, keystring, lli);
+ meta_func->add_signed_int(m, keystring, lli);
} else if (PyObject_TypeCheck(value, &UnsignedType)) {
long long unsigned llu;
llu = PyLong_AsUnsignedLongLong(value);
if (!PyErr_Occurred() && (llu == (uint64_t)llu))
- meta_data_add_unsigned_int(m, keystring, llu);
+ meta_func->add_unsigned_int(m, keystring, llu);
} else if (PyNumber_Check(value)) {
long long int lli;
long long unsigned llu;
tmp = PyNumber_Long(value);
lli = PyLong_AsLongLong(tmp);
if (!PyErr_Occurred() && (lli == (int64_t)lli)) {
- meta_data_add_signed_int(m, keystring, lli);
+ meta_func->add_signed_int(m, keystring, lli);
} else {
PyErr_Clear();
llu = PyLong_AsUnsignedLongLong(tmp);
if (!PyErr_Occurred() && (llu == (uint64_t)llu))
- meta_data_add_unsigned_int(m, keystring, llu);
+ meta_func->add_unsigned_int(m, keystring, llu);
}
Py_XDECREF(tmp);
} else {
string = cpy_unicode_or_bytes_to_string(&value);
if (string) {
- meta_data_add_string(m, keystring, string);
+ meta_func->add_string(m, keystring, string);
} else {
PyErr_Clear();
tmp = PyObject_Str(value);
string = cpy_unicode_or_bytes_to_string(&tmp);
if (string)
- meta_data_add_string(m, keystring, string);
+ meta_func->add_string(m, keystring, string);
Py_XDECREF(tmp);
}
}
Py_DECREF(key);
}
Py_XDECREF(l);
+ return 0;
+}
+
+#define CPY_BUILD_META_FUNC(meta_type, func, val_type) \
+ static int cpy_##func(void *meta, const char *key, val_type val) { \
+ return func((meta_type *)meta, key, val); \
+ }
+
+#define CPY_BUILD_META_HANDLER(func_prefix, meta_type) \
+ CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_string, const char *) \
+ CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_signed_int, int64_t) \
+ CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_unsigned_int, uint64_t) \
+ CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_double, double) \
+ CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_boolean, _Bool) \
+ \
+ static cpy_build_meta_handler_t cpy_##func_prefix = { \
+ .add_string = cpy_##func_prefix##_add_string, \
+ .add_signed_int = cpy_##func_prefix##_add_signed_int, \
+ .add_unsigned_int = cpy_##func_prefix##_add_unsigned_int, \
+ .add_double = cpy_##func_prefix##_add_double, \
+ .add_boolean = cpy_##func_prefix##_add_boolean}
+
+CPY_BUILD_META_HANDLER(meta_data, meta_data_t);
+CPY_BUILD_META_HANDLER(plugin_notification_meta, notification_t);
+
+static meta_data_t *cpy_build_meta(PyObject *meta) {
+ meta_data_t *m = meta_data_create();
+ if (cpy_build_meta_generic(meta, &cpy_meta_data, (void *)m) < 0) {
+ meta_data_destroy(m);
+ return NULL;
+ }
return m;
}
+static void cpy_build_notification_meta(notification_t *n, PyObject *meta) {
+ cpy_build_meta_generic(meta, &cpy_plugin_notification_meta, (void *)n);
+}
+
static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
int ret;
const data_set_t *ds;
Values_new /* tp_new */
};
+static char notification_meta_doc[] =
+ "These are the meta data for the Notification object.\n"
+ "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
+ "strings. int and long objects will be dispatched as signed integers "
+ "unless\n"
+ "they are between 2**63 and 2**64-1, which will result in an unsigned "
+ "integer.\n"
+ "One of these storage classes can be forced by using the classes\n"
+ "collectd.Signed and collectd.Unsigned. A meta object received by a\n"
+ "notification callback will always contain Signed or Unsigned objects.";
+
static char severity_doc[] =
"The severity of this notification. Assign or compare to\n"
"NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
int severity = 0;
double time = 0;
char *message = NULL;
+ PyObject *meta = NULL;
char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
*plugin = NULL, *host = NULL;
- static char *kwlist[] = {"type", "message", "plugin_instance",
- "type_instance", "plugin", "host",
- "time", "severity", NULL};
+ static char *kwlist[] = {
+ "type", "message", "plugin_instance", "type_instance", "plugin",
+ "host", "time", "severity", "meta", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist, NULL,
- &type, NULL, &message, NULL,
- &plugin_instance, NULL, &type_instance, NULL,
- &plugin, NULL, &host, &time, &severity))
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "|etetetetetetdiO", kwlist, NULL, &type, NULL, &message,
+ NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL,
+ &host, &time, &severity, &meta))
return -1;
if (type && plugin_get_ds(type) == NULL) {
FreeAll();
PyMem_Free(message);
+
+ if (meta == NULL) {
+ meta = PyDict_New();
+ PyErr_Clear();
+ } else {
+ Py_INCREF(meta);
+ }
+
+ PyObject *tmp = self->meta;
+ self->meta = meta;
+ Py_XDECREF(tmp);
+
return 0;
}
const data_set_t *ds;
notification_t notification;
double t = self->data.time;
+ PyObject *meta = self->meta;
int severity = self->severity;
char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
*type_instance = NULL;
char *message = NULL;
- static char *kwlist[] = {"type", "message", "plugin_instance",
- "type_instance", "plugin", "host",
- "time", "severity", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist, NULL,
+ static char *kwlist[] = {
+ "type", "message", "plugin_instance", "type_instance", "plugin",
+ "host", "time", "severity", "meta", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdiO", kwlist, NULL,
&type, NULL, &message, NULL,
&plugin_instance, NULL, &type_instance, NULL,
- &plugin, NULL, &host, &t, &severity))
+ &plugin, NULL, &host, &t, &severity, &meta))
return NULL;
notification.time = DOUBLE_TO_CDTIME_T(t);
PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
return NULL;
}
+ if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
+ PyErr_Format(PyExc_TypeError, "meta must be a dict");
+ return NULL;
+ }
+ cpy_build_notification_meta(¬ification, meta);
if (notification.time == 0)
notification.time = cdtime();
sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
Py_BEGIN_ALLOW_THREADS;
ret = plugin_dispatch_notification(¬ification);
+ if (notification.meta)
+ plugin_notification_meta_free(notification.meta);
Py_END_ALLOW_THREADS;
if (ret != 0) {
PyErr_SetString(PyExc_RuntimeError,
if (self == NULL)
return NULL;
+ self->meta = PyDict_New();
self->message[0] = 0;
self->severity = 0;
return (PyObject *)self;
static PyObject *Notification_repr(PyObject *s) {
PyObject *ret, *tmp;
- static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
+ static PyObject *l_severity = NULL, *l_message = NULL, *l_meta = NULL,
+ *l_closing = NULL;
Notification *self = (Notification *)s;
if (l_severity == NULL)
l_severity = cpy_string_to_unicode_or_bytes(",severity=");
if (l_message == NULL)
l_message = cpy_string_to_unicode_or_bytes(",message=");
+ if (l_meta == NULL)
+ l_meta = cpy_string_to_unicode_or_bytes(",meta=");
if (l_closing == NULL)
l_closing = cpy_string_to_unicode_or_bytes(")");
- if (l_severity == NULL || l_message == NULL || l_closing == NULL)
+ if (l_severity == NULL || l_message == NULL || l_meta == NULL ||
+ l_closing == NULL)
return NULL;
ret = cpy_common_repr(s);
CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
CPY_STRCAT_AND_DEL(&ret, tmp);
}
+ if (self->meta &&
+ (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
+ CPY_STRCAT(&ret, l_meta);
+ tmp = PyObject_Repr(self->meta);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
CPY_STRCAT(&ret, l_closing);
return ret;
}
+static int Notification_traverse(PyObject *self, visitproc visit, void *arg) {
+ Notification *n = (Notification *)self;
+ Py_VISIT(n->meta);
+ return 0;
+}
+
+static int Notification_clear(PyObject *self) {
+ Notification *n = (Notification *)self;
+ Py_CLEAR(n->meta);
+ return 0;
+}
+
+static void Notification_dealloc(PyObject *self) {
+ Notification_clear(self);
+ self->ob_type->tp_free(self);
+}
+
static PyMethodDef Notification_methods[] = {
{"dispatch", (PyCFunction)Notification_dispatch,
METH_VARARGS | METH_KEYWORDS, dispatch_doc},
static PyMemberDef Notification_members[] = {
{"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
+ {"meta", T_OBJECT_EX, offsetof(Notification, meta), 0,
+ notification_meta_doc},
{NULL}};
static PyGetSetDef Notification_getseters[] = {
{NULL}};
PyTypeObject NotificationType = {
- CPY_INIT_TYPE "collectd.Notification", /* tp_name */
- sizeof(Notification), /* tp_basicsize */
- 0, /* Will be filled in later */
- 0, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- Notification_repr, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
- Notification_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- Notification_methods, /* tp_methods */
- Notification_members, /* tp_members */
- Notification_getseters, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- Notification_init, /* tp_init */
- 0, /* tp_alloc */
- Notification_new /* tp_new */
+ CPY_INIT_TYPE "collectd.Notification", /* tp_name */
+ sizeof(Notification), /* tp_basicsize */
+ 0, /* Will be filled in later */
+ Notification_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ Notification_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ Notification_doc, /* tp_doc */
+ Notification_traverse, /* tp_traverse */
+ Notification_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Notification_methods, /* tp_methods */
+ Notification_members, /* tp_members */
+ Notification_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ Notification_init, /* tp_init */
+ 0, /* tp_alloc */
+ Notification_new /* tp_new */
};
static char Signed_doc[] =
}
}
- snprintf(read_name, sizeof(read_name), "routeros/%s", router_data->node);
- if (status == 0)
- status = plugin_register_complex_read(
- /* group = */ NULL, read_name, cr_read, /* interval = */ 0,
- &(user_data_t){
- .data = router_data, .free_func = (void *)cr_free_data,
- });
-
- if (status != 0)
+ if (status != 0) {
cr_free_data(router_data);
+ return status;
+ }
- return status;
+ snprintf(read_name, sizeof(read_name), "routeros/%s", router_data->node);
+ return plugin_register_complex_read(
+ /* group = */ NULL, read_name, cr_read, /* interval = */ 0,
+ &(user_data_t){
+ .data = router_data, .free_func = (void *)cr_free_data,
+ });
} /* }}} int cr_config_router */
static int cr_config(oconfig_item_t *ci) {
/*
* Private types
*/
-struct rrd_cache_s {
+typedef struct rrd_cache_s {
int values_num;
char **values;
cdtime_t first_value;
cdtime_t last_value;
int64_t random_variation;
enum { FLAG_NONE = 0x00, FLAG_QUEUED = 0x01, FLAG_FLUSHQ = 0x02 } flags;
-};
-typedef struct rrd_cache_s rrd_cache_t;
+} rrd_cache_t;
enum rrd_queue_dir_e { QUEUE_INSERT_FRONT, QUEUE_INSERT_BACK };
typedef enum rrd_queue_dir_e rrd_queue_dir_t;
* ALWAYS lock `cache_lock' first! */
static cdtime_t cache_timeout = 0;
static cdtime_t cache_flush_timeout = 0;
-static cdtime_t random_timeout = TIME_T_TO_CDTIME_T_STATIC(1);
+static cdtime_t random_timeout = 0;
static cdtime_t cache_flush_last;
static c_avl_tree_t *cache = NULL;
static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
#if HAVE_THREADSAFE_LIBRRD
static int srrd_update(char *filename, char *template, int argc,
const char **argv) {
- int status;
-
optind = 0; /* bug in librrd? */
rrd_clear_error();
- status = rrd_update_r(filename, template, argc, (void *)argv);
-
+ int status = rrd_update_r(filename, template, argc, (void *)argv);
if (status != 0) {
WARNING("rrdtool plugin: rrd_update_r (%s) failed: %s", filename,
rrd_get_error());
CDTIME_T_TO_DOUBLE(timeout));
now = cdtime();
- timeout = TIME_T_TO_CDTIME_T(timeout);
/* Build a list of entries to be flushed */
iter = c_avl_get_iterator(cache);
} /* int rrd_cache_flush_identifier */
static int64_t rrd_get_random_variation(void) {
- long min;
- long max;
-
if (random_timeout == 0)
return 0;
- /* Assure that "cache_timeout + random_variation" is never negative. */
- if (random_timeout > cache_timeout) {
- INFO("rrdtool plugin: Adjusting \"RandomTimeout\" to %.3f seconds.",
- CDTIME_T_TO_DOUBLE(cache_timeout));
- random_timeout = cache_timeout;
- }
-
- max = (long)(random_timeout / 2);
- min = max - ((long)random_timeout);
-
- return (int64_t)cdrand_range(min, max);
+ return (int64_t)cdrand_range(-random_timeout, random_timeout);
} /* int64_t rrd_get_random_variation */
static int rrd_cache_insert(const char *filename, const char *value,
if ((cache_timeout > 0) &&
((cdtime() - cache_flush_last) > cache_flush_timeout))
- rrd_cache_flush(cache_flush_timeout);
+ rrd_cache_flush(cache_timeout + random_timeout);
pthread_mutex_unlock(&cache_lock);
static int rrd_write(const data_set_t *ds, const value_list_t *vl,
user_data_t __attribute__((unused)) * user_data) {
- struct stat statbuf;
- char filename[512];
- char values[512];
- int status;
if (do_shutdown)
return 0;
return -1;
}
+ char filename[PATH_MAX];
if (value_list_to_filename(filename, sizeof(filename), vl) != 0)
return -1;
+ char values[32 * ds->ds_num];
if (value_list_to_string(values, sizeof(values), ds, vl) != 0)
return -1;
+ struct stat statbuf = {0};
if (stat(filename, &statbuf) == -1) {
if (errno == ENOENT) {
- status = cu_rrd_create_file(filename, ds, vl, &rrdcreate_config);
- if (status != 0)
+ if (cu_rrd_create_file(filename, ds, vl, &rrdcreate_config) != 0) {
return -1;
- else if (rrdcreate_config.async)
+ } else if (rrdcreate_config.async) {
return 0;
+ }
} else {
char errbuf[1024];
- ERROR("stat(%s) failed: %s", filename,
+ ERROR("rrdtool plugin: stat(%s) failed: %s", filename,
sstrerror(errno, errbuf, sizeof(errbuf)));
return -1;
}
} else if (!S_ISREG(statbuf.st_mode)) {
- ERROR("stat(%s): Not a regular file!", filename);
+ ERROR("rrdtool plugin: stat(%s): Not a regular file!", filename);
return -1;
}
- status = rrd_cache_insert(filename, values, vl->time);
-
- return status;
+ return rrd_cache_insert(filename, values, vl->time);
} /* int rrd_write */
static int rrd_flush(cdtime_t timeout, const char *identifier,
}
cache_timeout = DOUBLE_TO_CDTIME_T(tmp);
} else if (strcasecmp("CacheFlush", key) == 0) {
- int tmp = atoi(value);
+ double tmp = atof(value);
if (tmp < 0) {
fprintf(stderr, "rrdtool: `CacheFlush' must "
"be greater than 0.\n");
"be greater than 0.\n");
return 1;
}
- cache_flush_timeout = tmp;
+ cache_flush_timeout = DOUBLE_TO_CDTIME_T(tmp);
} else if (strcasecmp("DataDir", key) == 0) {
char *tmp;
size_t len;
static int rrd_init(void) {
static int init_once = 0;
- int status;
if (init_once != 0)
return 0;
cache_flush_last = cdtime();
if (cache_timeout == 0) {
+ random_timeout = 0;
cache_flush_timeout = 0;
- } else if (cache_flush_timeout < cache_timeout)
+ } else if (cache_flush_timeout < cache_timeout) {
+ INFO("rrdtool plugin: \"CacheFlush %.3f\" is less than \"CacheTimeout "
+ "%.3f\". "
+ "Ajusting \"CacheFlush\" to %.3f seconds.",
+ CDTIME_T_TO_DOUBLE(cache_flush_timeout),
+ CDTIME_T_TO_DOUBLE(cache_timeout),
+ CDTIME_T_TO_DOUBLE(cache_timeout * 10));
cache_flush_timeout = 10 * cache_timeout;
+ }
+
+ /* Assure that "cache_timeout + random_variation" is never negative. */
+ if (random_timeout > cache_timeout) {
+ INFO("rrdtool plugin: Adjusting \"RandomTimeout\" to %.3f seconds.",
+ CDTIME_T_TO_DOUBLE(cache_timeout));
+ random_timeout = cache_timeout;
+ }
pthread_mutex_unlock(&cache_lock);
- status =
+ int status =
plugin_thread_create(&queue_thread, /* attr = */ NULL, rrd_queue_thread,
/* args = */ NULL, "rrdtool queue");
if (status != 0) {
struct data_definition_s *next;
char **ignores;
size_t ignores_len;
- int invert_match;
+ _Bool invert_match;
};
typedef struct data_definition_s data_definition_t;
char *name;
char *address;
int version;
+ cdtime_t timeout;
+ int retries;
/* snmpv1/2 options */
char *community;
return 0;
} /* int csnmp_config_add_data_blacklist */
-static int csnmp_config_add_data_blacklist_match_inverted(data_definition_t *dd,
- oconfig_item_t *ci) {
- if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) {
- WARNING("snmp plugin: `InvertMatch' needs exactly one boolean argument.");
- return -1;
- }
-
- dd->invert_match = ci->values[0].value.boolean ? 1 : 0;
-
- return 0;
-} /* int csnmp_config_add_data_blacklist_match_inverted */
-
static int csnmp_config_add_data(oconfig_item_t *ci) {
- data_definition_t *dd;
- int status = 0;
-
- dd = calloc(1, sizeof(*dd));
+ data_definition_t *dd = calloc(1, sizeof(*dd));
if (dd == NULL)
return -1;
- status = cf_util_get_string(ci, &dd->name);
+ int status = cf_util_get_string(ci, &dd->name);
if (status != 0) {
- free(dd);
+ sfree(dd);
return -1;
}
else if (strcasecmp("Ignore", option->key) == 0)
status = csnmp_config_add_data_blacklist(dd, option);
else if (strcasecmp("InvertMatch", option->key) == 0)
- status = csnmp_config_add_data_blacklist_match_inverted(dd, option);
+ status = cf_util_get_boolean(option, &dd->invert_match);
else {
WARNING("snmp plugin: Option `%s' not allowed here.", option->key);
status = -1;
hd->sess_handle = NULL;
hd->interval = 0;
+ /* These mean that we have not set a timeout or retry value */
+ hd->timeout = 0;
+ hd->retries = -1;
+
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *option = ci->children + i;
status = 0;
status = cf_util_get_string(option, &hd->community);
else if (strcasecmp("Version", option->key) == 0)
status = csnmp_config_add_host_version(hd, option);
+ else if (strcasecmp("Timeout", option->key) == 0)
+ cf_util_get_cdtime(option, &hd->timeout);
+ else if (strcasecmp("Retries", option->key) == 0)
+ cf_util_get_int(option, &hd->retries);
else if (strcasecmp("Collect", option->key) == 0)
csnmp_config_add_host_collect(hd, option);
else if (strcasecmp("Interval", option->key) == 0)
});
if (status != 0) {
ERROR("snmp plugin: Registering complex read function failed.");
- csnmp_host_definition_destroy(hd);
return -1;
}
sess.community_len = strlen(host->community);
}
+ /* Set timeout & retries, if they have been changed from the default */
+ if (host->timeout != 0) {
+ /* net-snmp expects microseconds */
+ sess.timeout = CDTIME_T_TO_US(host->timeout);
+ }
+ if (host->retries >= 0) {
+ sess.retries = host->retries;
+ }
+
/* snmp_sess_open will copy the `struct snmp_session *'. */
host->sess_handle = snmp_sess_open(&sess);
struct variable_list *vb;
oid_t vb_name;
int status;
- uint32_t is_matched;
/* Set vb on the last variable */
for (vb = res->variables; (vb != NULL) && (vb->next_variable != NULL);
char *ptr;
csnmp_strvbcopy(il->instance, vb, sizeof(il->instance));
- is_matched = 0;
+ _Bool is_matched = 0;
for (uint32_t i = 0; i < dd->ignores_len; i++) {
status = fnmatch(dd->ignores[i], il->instance, 0);
if (status == 0) {
- if (dd->invert_match == 0) {
+ if (!dd->invert_match) {
sfree(il);
return 0;
} else {
}
}
}
- if (dd->invert_match != 0 && is_matched == 0) {
+ if (dd->invert_match && !is_matched) {
sfree(il);
return 0;
}
status = 0;
while (status == 0) {
- int oid_list_todo_num;
-
req = snmp_pdu_create(SNMP_MSG_GETNEXT);
if (req == NULL) {
ERROR("snmp plugin: snmp_pdu_create failed.");
break;
}
- oid_list_todo_num = 0;
+ size_t oid_list_todo_num = 0;
+ size_t var_idx[oid_list_len];
+ memset(var_idx, 0, sizeof(var_idx));
+
for (i = 0; i < oid_list_len; i++) {
/* Do not rerequest already finished OIDs */
if (!oid_list_todo[i])
continue;
- oid_list_todo_num++;
snmp_add_null_var(req, oid_list[i].oid, oid_list[i].oid_len);
+ var_idx[oid_list_todo_num] = i;
+ oid_list_todo_num++;
}
if (oid_list_todo_num == 0) {
/* The request is still empty - so we are finished */
DEBUG("snmp plugin: all variables have left their subtree");
+ snmp_free_pdu(req);
status = 0;
break;
}
res = NULL;
status = snmp_sess_synch_response(host->sess_handle, req, &res);
+
+ /* snmp_sess_synch_response always frees our req PDU */
+ req = NULL;
+
if ((status != STAT_SUCCESS) || (res == NULL)) {
char *errstr = NULL;
snmp_free_pdu(res);
res = NULL;
- /* snmp_synch_response already freed our PDU */
- req = NULL;
sfree(errstr);
csnmp_host_close_session(host);
break;
}
+ if (res->errstat != SNMP_ERR_NOERROR) {
+ if (res->errindex != 0) {
+ /* Find the OID which caused error */
+ for (i = 1, vb = res->variables; vb != NULL && i != res->errindex;
+ vb = vb->next_variable, i++)
+ /* do nothing */;
+ }
+
+ if ((res->errindex == 0) || (vb == NULL)) {
+ ERROR("snmp plugin: host %s; data %s: response error: %s (%li) ",
+ host->name, data->name, snmp_errstring(res->errstat),
+ res->errstat);
+ status = -1;
+ break;
+ }
+
+ char oid_buffer[1024] = {0};
+ snprint_objid(oid_buffer, sizeof(oid_buffer) - 1, vb->name,
+ vb->name_length);
+ NOTICE("snmp plugin: host %s; data %s: OID `%s` failed: %s", host->name,
+ data->name, oid_buffer, snmp_errstring(res->errstat));
+
+ /* Get value index from todo list and skip OID found */
+ assert(res->errindex <= oid_list_todo_num);
+ i = var_idx[res->errindex - 1];
+ assert(i < oid_list_len);
+ oid_list_todo[i] = 0;
+
+ snmp_free_pdu(res);
+ res = NULL;
+ continue;
+ }
+
for (vb = res->variables, i = 0; (vb != NULL);
vb = vb->next_variable, i++) {
/* Calculate value index from todo list */
snmp_free_pdu(res);
res = NULL;
- if (req != NULL)
- snmp_free_pdu(req);
- req = NULL;
-
if (status == 0)
csnmp_dispatch_table(host, data, instance_list_head, value_list_head);
static _Bool values_absolute = 1;
static _Bool values_percentage = 0;
+static _Bool report_io = 1;
static int swap_config(oconfig_item_t *ci) /* {{{ */
{
cf_util_get_boolean(child, &values_absolute);
else if (strcasecmp("ValuesPercentage", child->key) == 0)
cf_util_get_boolean(child, &values_percentage);
+ else if (strcasecmp("ReportIO", child->key) == 0)
+ cf_util_get_boolean(child, &report_io);
else
WARNING("swap plugin: Unknown config option: \"%s\"", child->key);
}
else
swap_read_combined();
- swap_read_io();
+ if (report_io)
+ swap_read_io();
return 0;
} /* }}} int swap_read */
reserved = (gauge_t)(pmemory.pgsp_rsvd * pagesize);
swap_submit_usage(NULL, total - free, free, "reserved", reserved);
- swap_submit_derive("in", (derive_t)pmemory.pgspins * pagesize);
- swap_submit_derive("out", (derive_t)pmemory.pgspouts * pagesize);
+
+ if (report_io) {
+ swap_submit_derive("in", (derive_t)pmemory.pgspins * pagesize);
+ swap_submit_derive("out", (derive_t)pmemory.pgspouts * pagesize);
+ }
return 0;
} /* }}} int swap_read */
--- /dev/null
+/**
+ * collectd - src/synproxy.c
+ * Copyright (C) 2017 Marek Becka
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Marek Becka <https://github.com/marekbecka>
+ **/
+
+#include "collectd.h"
+
+#include "common.h"
+#include "plugin.h"
+
+#if !KERNEL_LINUX
+#error "No applicable input method."
+#endif
+
+#define SYNPROXY_FIELDS 6
+
+static const char *synproxy_stat_path = "/proc/net/stat/synproxy";
+
+static const char *column_names[SYNPROXY_FIELDS] = {
+ "entries", "syn_received", "invalid",
+ "valid", "retransmission", "reopened"};
+static const char *column_types[SYNPROXY_FIELDS] = {
+ "current_connections", "connections", "cookies", "cookies", "cookies",
+ "connections"};
+
+static void synproxy_submit(value_t *results) {
+ value_list_t vl = VALUE_LIST_INIT;
+
+ /* 1st column (entries) is hardcoded to 0 in kernel code */
+ for (size_t n = 1; n < SYNPROXY_FIELDS; n++) {
+ vl.values = &results[n];
+ vl.values_len = 1;
+
+ sstrncpy(vl.plugin, "synproxy", sizeof(vl.plugin));
+ sstrncpy(vl.type, column_types[n], sizeof(vl.type));
+ sstrncpy(vl.type_instance, column_names[n], sizeof(vl.type_instance));
+
+ plugin_dispatch_values(&vl);
+ }
+}
+
+static int synproxy_read(void) {
+ char buf[1024];
+ value_t results[SYNPROXY_FIELDS];
+ int is_header = 1, status = 0;
+
+ FILE *fh = fopen(synproxy_stat_path, "r");
+ if (fh == NULL) {
+ ERROR("synproxy plugin: unable to open %s", synproxy_stat_path);
+ return -1;
+ }
+
+ memset(results, 0, sizeof(results));
+
+ while (fgets(buf, sizeof(buf), fh) != NULL) {
+ char *fields[SYNPROXY_FIELDS], *endprt;
+
+ if (is_header) {
+ is_header = 0;
+ continue;
+ }
+
+ int numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE(fields));
+ if (numfields != SYNPROXY_FIELDS) {
+ ERROR("synproxy plugin: unexpected number of columns in %s",
+ synproxy_stat_path);
+ status = -1;
+ break;
+ }
+
+ /* 1st column (entries) is hardcoded to 0 in kernel code */
+ for (size_t n = 1; n < SYNPROXY_FIELDS; n++) {
+ char *endptr = NULL;
+ errno = 0;
+
+ results[n].derive += strtoull(fields[n], &endprt, 16);
+ if ((endptr == fields[n]) || errno != 0) {
+ ERROR("synproxy plugin: unable to parse value: %s", fields[n]);
+ fclose(fh);
+ return -1;
+ }
+ }
+ }
+
+ fclose(fh);
+
+ if (status == 0) {
+ synproxy_submit(results);
+ }
+
+ return status;
+}
+
+void module_register(void) {
+ plugin_register_read("synproxy", synproxy_read);
+} /* void module_register */
typedef struct {
char *file;
char *sep;
+ char *plugin_name;
char *instance;
tbl_result_t *results;
static void tbl_setup(tbl_t *tbl, char *file) {
tbl->file = sstrdup(file);
tbl->sep = NULL;
+ tbl->plugin_name = NULL;
tbl->instance = NULL;
tbl->results = NULL;
static void tbl_clear(tbl_t *tbl) {
sfree(tbl->file);
sfree(tbl->sep);
+ sfree(tbl->plugin_name);
sfree(tbl->instance);
for (size_t i = 0; i < tbl->results_num; ++i)
if (0 == strcasecmp(c->key, "Separator"))
tbl_config_set_s(c->key, &tbl->sep, c);
+ else if (0 == strcasecmp(c->key, "Plugin"))
+ tbl_config_set_s(c->key, &tbl->plugin_name, c);
else if (0 == strcasecmp(c->key, "Instance"))
tbl_config_set_s(c->key, &tbl->instance, c);
else if (0 == strcasecmp(c->key, "Result"))
vl.values = values;
vl.values_len = STATIC_ARRAY_SIZE(values);
- sstrncpy(vl.plugin, "table", sizeof(vl.plugin));
+ sstrncpy(vl.plugin, (tbl->plugin_name != NULL) ? tbl->plugin_name : "table",
+ sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, tbl->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, res->type, sizeof(vl.type));
/*
* <Plugin tail>
* <File "/var/log/exim4/mainlog">
- * Instance "exim"
+ * Plugin "mail"
+ * Instance "exim"
* Interval 60
* <Match>
* Regex "S=([1-9][0-9]*)"
} /* int ctail_config_add_match_dstype */
static int ctail_config_add_match(cu_tail_match_t *tm,
+ const char *plugin_name,
const char *plugin_instance,
oconfig_item_t *ci, cdtime_t interval) {
ctail_config_match_t cm = {0};
if (status == 0) {
// TODO(octo): there's nothing "simple" about the latency stuff …
status = tail_match_add_match_simple(
- tm, cm.regex, cm.excluderegex, cm.flags, "tail", plugin_instance,
+ tm, cm.regex, cm.excluderegex, cm.flags,
+ (plugin_name != NULL) ? plugin_name : "tail", plugin_instance,
cm.type, cm.type_instance, cm.latency, interval);
if (status != 0)
static int ctail_config_add_file(oconfig_item_t *ci) {
cu_tail_match_t *tm;
cdtime_t interval = 0;
+ char *plugin_name = NULL;
char *plugin_instance = NULL;
int num_matches = 0;
oconfig_item_t *option = ci->children + i;
int status = 0;
- if (strcasecmp("Instance", option->key) == 0)
+ if (strcasecmp("Plugin", option->key) == 0)
+ status = cf_util_get_string (option, &plugin_name);
+ else if (strcasecmp("Instance", option->key) == 0)
status = cf_util_get_string(option, &plugin_instance);
else if (strcasecmp("Interval", option->key) == 0)
cf_util_get_cdtime(option, &interval);
else if (strcasecmp("Match", option->key) == 0) {
- status = ctail_config_add_match(tm, plugin_instance, option, interval);
+ status = ctail_config_add_match(tm, plugin_name, plugin_instance, option,
+ interval);
if (status == 0)
num_matches++;
/* Be mild with failed matches.. */
break;
} /* for (i = 0; i < ci->children_num; i++) */
+ sfree(plugin_name);
sfree(plugin_instance);
if (num_matches == 0) {
typedef struct metric_definition_s metric_definition_t;
struct instance_definition_s {
+ char *plugin_name;
char *instance;
char *path;
cu_tail_t *tail;
vl.values_len = 1;
vl.values = &v;
- sstrncpy(vl.plugin, "tail_csv", sizeof(vl.plugin));
+ sstrncpy(vl.plugin, (id->plugin_name != NULL) ? id->plugin_name : "tail_csv",
+ sizeof(vl.plugin));
if (id->instance != NULL)
sstrncpy(vl.plugin_instance, id->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, md->type, sizeof(vl.type));
cu_tail_destroy(id->tail);
id->tail = NULL;
+ sfree(id->plugin_name);
sfree(id->instance);
sfree(id->path);
sfree(id->metric_list);
id = calloc(1, sizeof(*id));
if (id == NULL)
return -1;
+ id->plugin_name = NULL;
id->instance = NULL;
id->path = NULL;
id->metric_list = NULL;
cf_util_get_cdtime(option, &id->interval);
else if (strcasecmp("TimeFrom", option->key) == 0)
status = tcsv_config_get_index(option, &id->time_from);
+ else if (strcasecmp("Plugin", option->key) == 0)
+ status = cf_util_get_string(option, &id->plugin_name);
else {
WARNING("tail_csv plugin: Option `%s' not allowed here.", option->key);
status = -1;
});
if (status != 0) {
ERROR("tail_csv plugin: Registering complex read function failed.");
- tcsv_instance_definition_destroy(id);
return -1;
}
status = write(fd, pkt_request, sizeof(pkt_request));
if (status <= 0) {
- ERROR("ted plugin: swrite failed.");
+ ERROR("ted plugin: write failed.");
return -1;
}
#include "plugin.h"
#include "utils_time.h"
-#include <asm/msr-index.h>
+#include "msr-index.h"
#include <cpuid.h>
#ifdef HAVE_SYS_CAPABILITY_H
#include <sys/capability.h>
/* If not using logical core numbering, set core id */
if (!config_lcn) {
- snprintf(name, sizeof(name), "core%02d", c->core_id);
+ if (topology.num_packages > 1)
+ snprintf(name, sizeof(name), "pkg%02d-core%02d", p->package_id, c->core_id);
+ else
+ snprintf(name, sizeof(name), "core%02d", c->core_id);
}
if (do_core_cstate & (1 << 3))
connections value:DERIVE:0:U
conntrack value:GAUGE:0:4294967295
contextswitch value:DERIVE:0:U
+cookies value:DERIVE:0:U
count value:GAUGE:0:U
counter value:COUNTER:U:U
cpu value:DERIVE:0:U
if_tx_packets value:DERIVE:0:U
invocations value:DERIVE:0:U
io_octets rx:DERIVE:0:U, tx:DERIVE:0:U
+io_ops read:DERIVE:0:U, write:DERIVE:0:U
io_packets rx:DERIVE:0:U, tx:DERIVE:0:U
ipc value:GAUGE:0:U
ipt_bytes value:DERIVE:0:U
ping_droprate value:GAUGE:0:100
ping_stddev value:GAUGE:0:65535
players value:GAUGE:0:1000000
+pools value:GAUGE:0:U
power value:GAUGE:U:U
pressure value:GAUGE:0:U
protocol_counter value:DERIVE:0:U
const char *grpname;
struct group *g;
struct group sg;
- char grbuf[4096];
+
+ long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (grbuf_size <= 0)
+ grbuf_size = sysconf(_SC_PAGESIZE);
+ if (grbuf_size <= 0)
+ grbuf_size = 4096;
+ char grbuf[grbuf_size];
grpname = (sock_group != NULL) ? sock_group : COLLECTD_GRP_NAME;
g = NULL;
#include "plugin.h"
#if KERNEL_LINUX
-#define STAT_FILE "/proc/stat"
-/* Using /proc filesystem to retrieve the boot time, Linux only. */
+#include <sys/sysinfo.h>
/* #endif KERNEL_LINUX */
#elif HAVE_LIBKSTAT
/*
* Global variables
*/
-/* boottime always used, no OS distinction */
-static time_t boottime;
#if HAVE_LIBKSTAT
extern kstat_ctl_t *kc;
plugin_dispatch_values(&vl);
}
-static int uptime_init(void) /* {{{ */
-{
/*
* On most unix systems the uptime is calculated by looking at the boot
* time (stored in unix time, since epoch) and the current one. We are
* the boot time, the plugin is unregistered and there is no chance to
* try again later. Nevertheless, this is very unlikely to happen.
*/
-
+static time_t uptime_get_sys(void) { /* {{{ */
+ time_t result;
#if KERNEL_LINUX
- unsigned long starttime;
- char buffer[1024];
- int ret;
- FILE *fh;
-
- ret = 0;
-
- fh = fopen(STAT_FILE, "r");
+ struct sysinfo info;
+ int status;
- if (fh == NULL) {
+ status = sysinfo(&info);
+ if (status != 0) {
char errbuf[1024];
- ERROR("uptime plugin: Cannot open " STAT_FILE ": %s",
+ ERROR("uptime plugin: Error calling sysinfo: %s",
sstrerror(errno, errbuf, sizeof(errbuf)));
return -1;
}
- while (fgets(buffer, 1024, fh) != NULL) {
- /* look for the btime string and read the value */
- ret = sscanf(buffer, "btime %lu", &starttime);
- /* avoid further loops if btime has been found and read
- * correctly (hopefully) */
- if (ret == 1)
- break;
- }
-
- fclose(fh);
-
- /* loop done, check if no value has been found/read */
- if (ret != 1) {
- ERROR("uptime plugin: No value read from " STAT_FILE "");
- return -1;
- }
-
- boottime = (time_t)starttime;
-
- if (boottime == 0) {
- ERROR("uptime plugin: btime read from " STAT_FILE ", "
- "but `boottime' is zero!");
- return -1;
- }
+ result = (time_t)info.uptime;
/* #endif KERNEL_LINUX */
#elif HAVE_LIBKSTAT
return -1;
}
- boottime = (time_t)knp->value.ui32;
-
- if (boottime == 0) {
+ if (knp->value.ui32 == 0) {
ERROR("uptime plugin: kstat_data_lookup returned success, "
"but `boottime' is zero!");
return -1;
}
+
+ result = time(NULL) - (time_t)knp->value.ui32;
/* #endif HAVE_LIBKSTAT */
#elif HAVE_SYS_SYSCTL_H
return -1;
}
- boottime = boottv.tv_sec;
-
- if (boottime == 0) {
+ if (boottv.tv_sec == 0) {
ERROR("uptime plugin: sysctl(3) returned success, "
"but `boottime' is zero!");
return -1;
}
+
+ result = time(NULL) - boottv.tv_sec;
/* #endif HAVE_SYS_SYSCTL_H */
#elif HAVE_PERFSTAT
if (hertz <= 0)
hertz = HZ;
- boottime = time(NULL) - cputotal.lbolt / hertz;
+ result = cputotal.lbolt / hertz;
#endif /* HAVE_PERFSTAT */
- return 0;
-} /* }}} int uptime_init */
+ return result;
+} /* }}} int uptime_get_sys */
static int uptime_read(void) {
gauge_t uptime;
time_t elapsed;
/* calculate the amount of time elapsed since boot, AKA uptime */
- elapsed = time(NULL) - boottime;
+ elapsed = uptime_get_sys();
uptime = (gauge_t)elapsed;
}
void module_register(void) {
- plugin_register_init("uptime", uptime_init);
plugin_register_read("uptime", uptime_read);
} /* void module_register */
if (s == NULL)
return 0;
- if ((curl == NULL) || (hostname == NULL) || (plugin == NULL)) {
+ if ((curl == 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);
+ "(curl=%p; plugin=%s)",
+ curl, plugin == NULL ? "<NULL>" : plugin);
return -1;
}
- sstrncpy(vl.host, hostname, sizeof(vl.host));
+ if (hostname != NULL)
+ 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));
const data_set_t *ds, const value_list_t *vl,
int store_rates,
char const *const *http_attrs,
- size_t http_attrs_num, int data_ttl) {
+ size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix) {
char temp[512];
size_t offset = 0;
int status;
for (size_t i = 0; i < ds->ds_num; i++) {
/* All value lists have a leading comma. The first one will be replaced with
* a square bracket in `format_kairosdb_finalize'. */
- BUFFER_ADD(",{");
+ BUFFER_ADD(",{\"name\":\"");
- BUFFER_ADD("\"name\":\"collectd");
+ if (metrics_prefix != NULL) {
+ BUFFER_ADD("%s.", metrics_prefix);
+ }
- BUFFER_ADD(".%s", vl->plugin);
+ BUFFER_ADD("%s", vl->plugin);
status = values_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates, i);
if (status != 0)
char *buffer, /* {{{ */
size_t *ret_buffer_fill, size_t *ret_buffer_free, const data_set_t *ds,
const value_list_t *vl, int store_rates, size_t temp_size,
- char const *const *http_attrs, size_t http_attrs_num, int data_ttl) {
+ char const *const *http_attrs, size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix) {
char temp[temp_size];
int status;
status = value_list_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates,
- http_attrs, http_attrs_num, data_ttl);
+ http_attrs, http_attrs_num, data_ttl,
+ metrics_prefix);
if (status != 0)
return status;
temp_size = strlen(temp);
size_t *ret_buffer_fill, size_t *ret_buffer_free,
const data_set_t *ds, const value_list_t *vl,
int store_rates, char const *const *http_attrs,
- size_t http_attrs_num, int data_ttl) {
+ size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix) {
if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
(ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL))
return -EINVAL;
return format_kairosdb_value_list_nocheck(
buffer, ret_buffer_fill, ret_buffer_free, ds, vl, store_rates,
- (*ret_buffer_free) - 2, http_attrs, http_attrs_num, data_ttl);
+ (*ret_buffer_free) - 2, http_attrs, http_attrs_num, data_ttl,
+ metrics_prefix);
} /* }}} int format_kairosdb_value_list */
/* vim: set sw=2 sts=2 et fdm=marker : */
size_t *ret_buffer_free, const data_set_t *ds,
const value_list_t *vl, int store_rates,
char const *const *http_attrs,
- size_t http_attrs_num, int data_ttl);
+ size_t http_attrs_num, int data_ttl,
+ char const *metrics_prefix);
int format_kairosdb_finalize(char *buffer, size_t *ret_buffer_fill,
size_t *ret_buffer_free);
status = latency_config_add_percentile(conf, child, plugin);
else if (strcasecmp("Bucket", child->key) == 0)
status = latency_config_add_bucket(conf, child, plugin);
+ else if (strcasecmp("BucketType", child->key) == 0)
+ status = cf_util_get_string(child, &conf->bucket_type);
else
WARNING("%s plugin: \"%s\" is not a valid option within a \"%s\" block.",
plugin, child->key, ci->key);
return ENOMEM;
}
+ if (src.bucket_type != NULL) {
+ dst->bucket_type = strdup(src.bucket_type);
+ if (dst->bucket_type == NULL) {
+ latency_config_free(*dst);
+ return ENOMEM;
+ }
+ }
+
memmove(dst->percentile, src.percentile,
dst->percentile_num * sizeof(*dst->percentile));
memmove(dst->buckets, src.buckets, dst->buckets_num * sizeof(*dst->buckets));
void latency_config_free(latency_config_t conf) {
sfree(conf.percentile);
sfree(conf.buckets);
+ sfree(conf.bucket_type);
} /* void latency_config_free */
latency_bucket_t *buckets;
size_t buckets_num;
+ char *bucket_type;
/*
_Bool lower;
/* Reset GAUGE metrics only and except GAUGE_PERSIST. */
if ((mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
!(mv->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) {
- mv->value.gauge = NAN;
+ mv->value.gauge = (mv->ds_type & UTILS_MATCH_CF_GAUGE_INC) ? 0 : NAN;
mv->values_num = 0;
}
} /* }}} void match_value_reset */
if (match_value == NULL)
return -1;
- sstrncpy(vl.host, hostname_g, sizeof(vl.host));
sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, data->plugin_instance,
sizeof(vl.plugin_instance));
}
/* Submit buckets */
- sstrncpy(vl.type, "bucket", sizeof(vl.type));
+ if (data->latency_config.bucket_type != NULL)
+ sstrncpy(vl.type, data->latency_config.bucket_type, sizeof(vl.type));
+ else
+ sstrncpy(vl.type, "bucket", sizeof(vl.type));
+
for (size_t i = 0; i < data->latency_config.buckets_num; i++) {
latency_bucket_t bucket = data->latency_config.buckets[i];
* Jérôme Renard <jerome.renard at gmail.com>
* Marc Fournier <marc.fournier at camptocamp.com>
* Florian octo Forster <octo at collectd.org>
+ * Denes Matetelki <dmatetelki at varnish-software.com>
**/
#include "collectd.h"
_Bool collect_sms;
#if HAVE_VARNISH_V2
_Bool collect_sm;
+#endif
+#if HAVE_VARNISH_V2 || HAVE_VARNISH_V4
_Bool collect_sma;
#endif
_Bool collect_struct;
_Bool collect_workers;
#if HAVE_VARNISH_V4
_Bool collect_vsm;
+ _Bool collect_lck;
+ _Bool collect_mempool;
+ _Bool collect_mgt;
+ _Bool collect_smf;
+ _Bool collect_vbe;
+ _Bool collect_mse;
#endif
};
typedef struct user_config_s user_config_t; /* }}} */
else if (strcmp(name, "client_req") == 0)
return varnish_submit_derive(conf->instance, "connections", "connections",
"received", val);
+#ifdef HAVE_VARNISH_V4
+ else if (strcmp(name, "client_req_400") == 0)
+ return varnish_submit_derive(conf->instance, "connections", "connections",
+ "error_400", val);
+ else if (strcmp(name, "client_req_417") == 0)
+ return varnish_submit_derive(conf->instance, "connections", "connections",
+ "error_417", val);
+#endif
}
#ifdef HAVE_VARNISH_V3
else if (strcmp(name, "esi_warnings") == 0)
return varnish_submit_derive(conf->instance, "esi", "total_operations",
"warning", val);
+ else if (strcmp(name, "esi_maxdepth") == 0)
+ return varnish_submit_derive(conf->instance, "esi", "total_operations",
+ "max_depth", val);
}
if (conf->collect_backend) {
else if (strcmp(name, "fetch_304") == 0)
return varnish_submit_derive(conf->instance, "fetch", "http_requests",
"no_body_304", val);
+#if HAVE_VARNISH_V4
+ else if (strcmp(name, "fetch_no_thread") == 0)
+ return varnish_submit_derive(conf->instance, "fetch", "http_requests",
+ "no_thread", val);
+ else if (strcmp(name, "fetch_none") == 0)
+ return varnish_submit_derive(conf->instance, "fetch", "http_requests",
+ "none", val);
+ else if (strcmp(name, "busy_sleep") == 0)
+ return varnish_submit_derive(conf->instance, "fetch", "http_requests",
+ "busy_sleep", val);
+ else if (strcmp(name, "busy_wakeup") == 0)
+ return varnish_submit_derive(conf->instance, "fetch", "http_requests",
+ "busy_wakeup", val);
+#endif
}
if (conf->collect_hcb) {
else if (strcmp(name, "n_objoverflow") == 0)
return varnish_submit_derive(conf->instance, "objects", "total_objects",
"workspace_overflow", val);
+#if HAVE_VARNISH_V4
+ else if (strcmp(name, "exp_mailed") == 0)
+ return varnish_submit_gauge(conf->instance, "struct", "objects",
+ "exp_mailed", val);
+ else if (strcmp(name, "exp_received") == 0)
+ return varnish_submit_gauge(conf->instance, "struct", "objects",
+ "exp_received", val);
+#endif
}
#if HAVE_VARNISH_V3
else if (strcmp(name, "bans_dups") == 0)
return varnish_submit_derive(conf->instance, "ban", "total_operations",
"duplicate", val);
+ else if (strcmp(name, "bans_tested") == 0)
+ return varnish_submit_derive(conf->instance, "ban", "total_operations",
+ "tested", val);
+ else if (strcmp(name, "bans_lurker_contention") == 0)
+ return varnish_submit_derive(conf->instance, "ban", "total_operations",
+ "lurker_contention", val);
+ else if (strcmp(name, "bans_lurker_obj_killed") == 0)
+ return varnish_submit_derive(conf->instance, "ban", "total_operations",
+ "lurker_obj_killed", val);
+ else if (strcmp(name, "bans_lurker_tested") == 0)
+ return varnish_submit_derive(conf->instance, "ban", "total_operations",
+ "lurker_tested", val);
+ else if (strcmp(name, "bans_lurker_tests_tested") == 0)
+ return varnish_submit_derive(conf->instance, "ban", "total_operations",
+ "lurker_tests_tested", val);
+ else if (strcmp(name, "bans_obj_killed") == 0)
+ return varnish_submit_derive(conf->instance, "ban", "total_operations",
+ "obj_killed", val);
+ else if (strcmp(name, "bans_persisted_bytes") == 0)
+ return varnish_submit_derive(conf->instance, "ban", "total_bytes",
+ "persisted_bytes", val);
+ else if (strcmp(name, "bans_persisted_fragmentation") == 0)
+ return varnish_submit_derive(conf->instance, "ban", "total_bytes",
+ "persisted_fragmentation", val);
+ else if (strcmp(name, "bans_tests_tested") == 0)
+ return varnish_submit_derive(conf->instance, "ban", "total_operations",
+ "tests_tested", val);
+
}
#endif
else if (strcmp(name, "sess_herd") == 0)
return varnish_submit_derive(conf->instance, "session",
"total_operations", "herd", val);
+#if HAVE_VARNISH_V4
+ else if (strcmp(name, "sess_closed_err") == 0)
+ return varnish_submit_derive(conf->instance, "session",
+ "total_operations", "closed_err", val);
+ else if (strcmp(name, "sess_dropped") == 0)
+ return varnish_submit_derive(conf->instance, "session",
+ "total_operations", "dropped_for_thread", val);
+#endif
}
if (conf->collect_shm) {
else if (strcmp(name, "s_req_bodybytes") == 0)
return varnish_submit_derive(conf->instance, "totals", "total_bytes",
"req_body", val);
+ else if (strcmp(name, "s_req_protobytes") == 0)
+ return varnish_submit_derive(conf->instance, "totals", "total_bytes",
+ "req_proto", val);
else if (strcmp(name, "s_resp_hdrbytes") == 0)
return varnish_submit_derive(conf->instance, "totals", "total_bytes",
"resp_header", val);
else if (strcmp(name, "s_resp_bodybytes") == 0)
return varnish_submit_derive(conf->instance, "totals", "total_bytes",
"resp_body", val);
+ else if (strcmp(name, "s_resp_protobytes") == 0)
+ return varnish_submit_derive(conf->instance, "totals", "total_bytes",
+ "resp_proto", val);
else if (strcmp(name, "s_pipe_hdrbytes") == 0)
return varnish_submit_derive(conf->instance, "totals", "total_bytes",
"pipe_header", val);
return varnish_submit_derive(conf->instance, "workers", "total_threads",
"dropped", val);
else if (strcmp(name, "thread_queue_len") == 0)
- return varnish_submit_derive(conf->instance, "workers", "queue_length",
+ return varnish_submit_gauge(conf->instance, "workers", "queue_length",
"threads", val);
else if (strcmp(name, "n_wrk") == 0)
return varnish_submit_gauge(conf->instance, "workers", "threads",
else if (strcmp(name, "n_wrk_lqueue") == 0)
return varnish_submit_derive(conf->instance, "workers", "total_requests",
"queue_length", val);
+#if HAVE_VARNISH_V4
+ else if (strcmp(name, "pools") == 0)
+ return varnish_submit_gauge(conf->instance, "workers", "pools",
+ "pools", val);
+ else if (strcmp(name, "busy_killed") == 0)
+ return varnish_submit_derive(conf->instance, "workers", "http_requests",
+ "busy_killed", val);
+#endif
}
#if HAVE_VARNISH_V4
return varnish_submit_derive(conf->instance, "vsm", "total_bytes",
"overflowed", val);
}
+
+ if (conf->collect_vbe) {
+ /* @TODO figure out the collectd type for bitmap
+ if (strcmp(name, "happy") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "bitmap", "happy_hprobes", val);
+ */
+ if (strcmp(name, "bereq_hdrbytes") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "bereq_hdrbytes", val);
+ else if (strcmp(name, "bereq_bodybytes") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "bereq_bodybytes", val);
+ else if (strcmp(name, "bereq_protobytes") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "bereq_protobytes", val);
+ else if (strcmp(name, "beresp_hdrbytes") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "beresp_hdrbytes", val);
+ else if (strcmp(name, "beresp_bodybytes") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "beresp_bodybytes", val);
+ else if (strcmp(name, "beresp_protobytes") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "beresp_protobytes", val);
+ else if (strcmp(name, "pipe_hdrbytes") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "pipe_hdrbytes", val);
+ else if (strcmp(name, "pipe_out") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "pipe_out", val);
+ else if (strcmp(name, "pipe_in") == 0)
+ return varnish_submit_derive(conf->instance, "vbe",
+ "total_bytes", "pipe_in", val);
+ else if (strcmp(name, "conn") == 0)
+ return varnish_submit_derive(conf->instance, "vbe", "connections",
+ "c_conns", val);
+ else if (strcmp(name, "req") == 0)
+ return varnish_submit_derive(conf->instance, "vbe", "http_requests",
+ "b_reqs", val);
+ }
+
+ /* All Stevedores support these counters */
+ if (conf->collect_sma || conf->collect_smf || conf->collect_mse) {
+
+ char category[4];
+ if (conf->collect_sma)
+ strncpy(category, "sma", 4);
+ else if (conf->collect_smf)
+ strncpy(category, "smf", 4);
+ else
+ strncpy(category, "mse", 4);
+
+ if (strcmp(name, "c_req") == 0)
+ return varnish_submit_derive(conf->instance, category,
+ "total_operations", "alloc_req", val);
+ else if (strcmp(name, "c_fail") == 0)
+ return varnish_submit_derive(conf->instance, category,
+ "total_operations", "alloc_fail", val);
+ else if (strcmp(name, "c_bytes") == 0)
+ return varnish_submit_derive(conf->instance, category,
+ "total_bytes", "bytes_allocated", val);
+ else if (strcmp(name, "c_freed") == 0)
+ return varnish_submit_derive(conf->instance, category,
+ "total_bytes", "bytes_freed", val);
+ else if (strcmp(name, "g_alloc") == 0)
+ return varnish_submit_derive(conf->instance, category,
+ "total_operations", "alloc_outstanding", val);
+ else if (strcmp(name, "g_bytes") == 0)
+ return varnish_submit_gauge(conf->instance, category, "bytes",
+ "bytes_outstanding", val);
+ else if (strcmp(name, "g_space") == 0)
+ return varnish_submit_gauge(conf->instance, category, "bytes",
+ "bytes_available", val);
+ }
+
+ /* No SMA specific counters */
+
+ if (conf->collect_smf) {
+ if (strcmp(name, "g_smf") == 0)
+ return varnish_submit_gauge(conf->instance, "smf", "objects",
+ "n_struct_smf", val);
+ else if (strcmp(name, "g_smf_frag") == 0)
+ return varnish_submit_gauge(conf->instance, "smf", "objects",
+ "n_small_free_smf", val);
+ else if (strcmp(name, "g_smf_large") == 0)
+ return varnish_submit_gauge(conf->instance, "smf", "objects",
+ "n_large_free_smf", val);
+ }
+
+ if (conf->collect_mgt) {
+ if (strcmp(name, "uptime") == 0)
+ return varnish_submit_gauge(conf->instance, "mgt", "uptime",
+ "mgt_proc_uptime", val);
+ else if (strcmp(name, "child_start") == 0)
+ return varnish_submit_derive(conf->instance, "mgt",
+ "total_operations", "child_start", val);
+ else if (strcmp(name, "child_exit") == 0)
+ return varnish_submit_derive(conf->instance, "mgt",
+ "total_operations", "child_exit", val);
+ else if (strcmp(name, "child_stop") == 0)
+ return varnish_submit_derive(conf->instance, "mgt",
+ "total_operations", "child_stop", val);
+ else if (strcmp(name, "child_died") == 0)
+ return varnish_submit_derive(conf->instance, "mgt",
+ "total_operations", "child_died", val);
+ else if (strcmp(name, "child_dump") == 0)
+ return varnish_submit_derive(conf->instance, "mgt",
+ "total_operations", "child_dump", val);
+ else if (strcmp(name, "child_panic") == 0)
+ return varnish_submit_derive(conf->instance, "mgt",
+ "total_operations", "child_panic", val);
+ }
+
+ if (conf->collect_lck) {
+ if (strcmp(name, "creat") == 0)
+ return varnish_submit_gauge(conf->instance, "lck", "objects",
+ "created", val);
+ else if (strcmp(name, "destroy") == 0)
+ return varnish_submit_gauge(conf->instance, "lck", "objects",
+ "destroyed", val);
+ else if (strcmp(name, "locks") == 0)
+ return varnish_submit_derive(conf->instance, "lck", "total_operations",
+ "lock_ops", val);
+ }
+
+ if (conf->collect_mempool) {
+ if (strcmp(name, "live") == 0)
+ return varnish_submit_gauge(conf->instance, "mempool", "objects",
+ "in_use", val);
+ else if (strcmp(name, "pool") == 0)
+ return varnish_submit_gauge(conf->instance, "mempool", "objects",
+ "in_pool", val);
+ else if (strcmp(name, "sz_wanted") == 0)
+ return varnish_submit_gauge(conf->instance, "mempool", "bytes",
+ "size_requested", val);
+ else if (strcmp(name, "sz_actual") == 0)
+ return varnish_submit_gauge(conf->instance, "mempool", "bytes",
+ "size_allocated", val);
+ else if (strcmp(name, "allocs") == 0)
+ return varnish_submit_derive(conf->instance, "mempool",
+ "total_operations", "allocations", val);
+ else if (strcmp(name, "frees") == 0)
+ return varnish_submit_derive(conf->instance, "mempool",
+ "total_operations", "frees", val);
+ else if (strcmp(name, "recycle") == 0)
+ return varnish_submit_gauge(conf->instance, "mempool",
+ "objects", "recycled", val);
+ else if (strcmp(name, "timeout") == 0)
+ return varnish_submit_gauge(conf->instance, "mempool",
+ "objects", "timed_out", val);
+ else if (strcmp(name, "toosmall") == 0)
+ return varnish_submit_gauge(conf->instance, "mempool",
+ "objects", "too_small", val);
+ else if (strcmp(name, "surplus") == 0)
+ return varnish_submit_gauge(conf->instance, "mempool",
+ "objects", "surplus", val);
+ else if (strcmp(name, "randry") == 0)
+ return varnish_submit_gauge(conf->instance, "mempool",
+ "objects", "ran_dry", val);
+ }
+
+ if (conf->collect_mse) {
+ if (strcmp(name, "c_full") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "full_allocs", val);
+ else if (strcmp(name, "c_truncated") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "truncated_allocs", val);
+ else if (strcmp(name, "c_expanded") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "expanded_allocs", val);
+ else if (strcmp(name, "c_failed") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "failed_allocs", val);
+ else if (strcmp(name, "c_bytes") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_bytes", "bytes_allocated", val);
+ else if (strcmp(name, "c_freed") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_bytes", "bytes_freed", val);
+ else if (strcmp(name, "g_fo_alloc") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "fo_allocs_outstanding", val);
+ else if (strcmp(name, "g_fo_bytes") == 0)
+ return varnish_submit_gauge(conf->instance, "mse",
+ "bytes", "fo_bytes_outstanding", val);
+ else if (strcmp(name, "g_membuf_alloc") == 0)
+ return varnish_submit_gauge(conf->instance, "mse",
+ "objects", "membufs_allocated", val);
+ else if (strcmp(name, "g_membuf_inuse") == 0)
+ return varnish_submit_gauge(conf->instance, "mse",
+ "objects", "membufs_inuse", val);
+ else if (strcmp(name, "g_bans_bytes") == 0)
+ return varnish_submit_gauge(conf->instance, "mse",
+ "bytes", "persisted_banspace_used", val);
+ else if (strcmp(name, "g_bans_space") == 0)
+ return varnish_submit_gauge(conf->instance, "mse",
+ "bytes", "persisted_banspace_available", val);
+ else if (strcmp(name, "g_bans_persisted") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "bans_persisted", val);
+ else if (strcmp(name, "g_bans_lost") == 0)
+ return varnish_submit_derive(conf->instance, "mse",
+ "total_operations", "bans_lost", val);
+
+ /* mse seg */
+ else if (strcmp(name, "g_journal_bytes") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_reg",
+ "bytes", "journal_bytes_used", val);
+ else if (strcmp(name, "g_journal_space") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_reg",
+ "bytes", "journal_bytes_free", val);
+
+ /* mse segagg */
+ else if (strcmp(name, "g_bigspace") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "bytes", "big_extents_bytes_available", val);
+ else if (strcmp(name, "g_extfree") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "objects", "free_extents", val);
+ else if (strcmp(name, "g_sparenode") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "objects", "spare_nodes_available", val);
+ else if (strcmp(name, "g_objnode") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "objects", "object_nodes_in_use", val);
+ else if (strcmp(name, "g_extnode") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "objects", "extent_nodes_in_use", val);
+ else if (strcmp(name, "g_bigextfree") == 0)
+ return varnish_submit_gauge(conf->instance, "mse_segagg",
+ "objects", "free_big_extents", val);
+ else if (strcmp(name, "c_pruneloop") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_operations", "prune_loops", val);
+ else if (strcmp(name, "c_pruned") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_objects", "pruned_objects", val);
+ else if (strcmp(name, "c_spared") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_operations", "spared_objects", val);
+ else if (strcmp(name, "c_skipped") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_operations", "missed_objects", val);
+ else if (strcmp(name, "c_nuked") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_operations", "nuked_objects", val);
+ else if (strcmp(name, "c_sniped") == 0)
+ return varnish_submit_derive(conf->instance, "mse_segagg",
+ "total_operations", "sniped_objects", val);
+
+ }
+
#endif
return 0;
conf->collect_shm = 1;
#if HAVE_VARNISH_V2
conf->collect_sm = 0;
+#endif
+#if HAVE_VARNISH_V2 || HAVE_VARNISH_V4
conf->collect_sma = 0;
#endif
conf->collect_sms = 0;
conf->collect_workers = 0;
#if HAVE_VARNISH_V4
conf->collect_vsm = 0;
+ conf->collect_lck = 0;
+ conf->collect_mempool = 0;
+ conf->collect_mgt = 0;
+ conf->collect_smf = 0;
+ conf->collect_vbe = 0;
+ conf->collect_mse = 0;
#endif
return 0;
else if (strcasecmp("CollectSMS", child->key) == 0)
cf_util_get_boolean(child, &conf->collect_sms);
else if (strcasecmp("CollectSMA", child->key) == 0)
-#if HAVE_VARNISH_V2
+#if HAVE_VARNISH_V2 || HAVE_VARNISH_V4
cf_util_get_boolean(child, &conf->collect_sma);
#else
WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
- child->key, "v2");
+ child->key, "v2 and v4");
#endif
else if (strcasecmp("CollectSM", child->key) == 0)
#if HAVE_VARNISH_V2
WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
child->key, "v4");
#endif
+ else if (strcasecmp("CollectLock", child->key) == 0)
+#if HAVE_VARNISH_V4
+ cf_util_get_boolean(child, &conf->collect_lck);
+#else
+ WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
+ child->key, "v4");
+#endif
+ else if (strcasecmp("CollectMempool", child->key) == 0)
+#if HAVE_VARNISH_V4
+ cf_util_get_boolean(child, &conf->collect_mempool);
+#else
+ WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
+ child->key, "v4");
+#endif
+ else if (strcasecmp("CollectManagement", child->key) == 0)
+#if HAVE_VARNISH_V4
+ cf_util_get_boolean(child, &conf->collect_mgt);
+#else
+ WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
+ child->key, "v4");
+#endif
+ else if (strcasecmp("CollectSMF", child->key) == 0)
+#if HAVE_VARNISH_V4
+ cf_util_get_boolean(child, &conf->collect_smf);
+#else
+ WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
+ child->key, "v4");
+#endif
+ else if (strcasecmp("CollectSMF", child->key) == 0)
+#if HAVE_VARNISH_V4
+ cf_util_get_boolean(child, &conf->collect_smf);
+#else
+ WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
+ child->key, "v4");
+#endif
+ else if (strcasecmp("CollectVBE", child->key) == 0)
+#if HAVE_VARNISH_V4
+ cf_util_get_boolean(child, &conf->collect_vbe);
+#else
+ WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
+ child->key, "v4");
+#endif
+ else if (strcasecmp("CollectMSE", child->key) == 0)
+#if HAVE_VARNISH_V4
+ cf_util_get_boolean(child, &conf->collect_mse);
+#else
+ WARNING("Varnish plugin: \"%s\" is available for Varnish %s only.",
+ child->key, "Plus v4");
+#endif
else {
WARNING("Varnish plugin: Ignoring unknown "
"configuration option: \"%s\". Did "
#endif
&& !conf->collect_session && !conf->collect_shm && !conf->collect_sms
#if HAVE_VARNISH_V2
- && !conf->collect_sma && !conf->collect_sm
+ && !conf->collect_sm
+#endif
+#if HAVE_VARNISH_V2 || HAVE_VARNISH_V4
+ && !conf->collect_sma
#endif
&& !conf->collect_struct && !conf->collect_totals
#if HAVE_VARNISH_V3 || HAVE_VARNISH_V4
#endif
&& !conf->collect_vcl && !conf->collect_workers
#if HAVE_VARNISH_V4
- && !conf->collect_vsm
+ && !conf->collect_vsm && !conf->collect_vbe && !conf->collect_smf
+ && !conf->collect_mgt && !conf->collect_lck && !conf->collect_mempool
+ && !conf->collect_mse
#endif
) {
WARNING("Varnish plugin: No metric has been configured for "
#define WRITE_HTTP_DEFAULT_BUFFER_SIZE 4096
#endif
+#ifndef WRITE_HTTP_DEFAULT_PREFIX
+#define WRITE_HTTP_DEFAULT_PREFIX "collectd"
+#endif
+
/*
* Private variables
*/
pthread_mutex_t send_lock;
int data_ttl;
+ char *metrics_prefix;
};
typedef struct wh_callback_s wh_callback_t;
sfree(cb->clientcert);
sfree(cb->clientkeypass);
sfree(cb->send_buffer);
+ sfree(cb->metrics_prefix);
sfree(cb);
} /* }}} void wh_callback_free */
status = format_kairosdb_value_list(
cb->send_buffer, &cb->send_buffer_fill, &cb->send_buffer_free, ds, vl,
cb->store_rates, (char const *const *)http_attrs, http_attrs_num,
- cb->data_ttl);
+ cb->data_ttl, cb->metrics_prefix);
if (status == -ENOMEM) {
status = wh_flush_nolock(/* timeout = */ 0, cb);
if (status != 0) {
status = format_kairosdb_value_list(
cb->send_buffer, &cb->send_buffer_fill, &cb->send_buffer_free, ds, vl,
cb->store_rates, (char const *const *)http_attrs, http_attrs_num,
- cb->data_ttl);
+ cb->data_ttl, cb->metrics_prefix);
}
if (status != 0) {
pthread_mutex_unlock(&cb->send_lock);
cb->send_metrics = 1;
cb->send_notifications = 0;
cb->data_ttl = 0;
+ cb->metrics_prefix = strdup(WRITE_HTTP_DEFAULT_PREFIX);
+
+ if (cb->metrics_prefix == NULL) {
+ ERROR("write_http plugin: strdup failed.");
+ sfree(cb);
+ return -1;
+ }
pthread_mutex_init(&cb->send_lock, /* attr = */ NULL);
sfree(val);
} else if (strcasecmp("TTL", child->key) == 0) {
status = cf_util_get_int(child, &cb->data_ttl);
+ } else if (strcasecmp("Prefix", child->key) == 0) {
+ status = cf_util_get_string(child, &cb->metrics_prefix);
} else {
ERROR("write_http plugin: Invalid configuration "
"option: %s.",
return -1;
}
+ if (strlen(cb->metrics_prefix) == 0)
+ sfree(cb->metrics_prefix);
+
if (cb->low_speed_limit > 0)
cb->low_speed_time = CDTIME_T_TO_TIME_T(plugin_get_interval());
#include <microhttpd.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
#ifndef PROMETHEUS_DEFAULT_STALENESS_DELTA
#define PROMETHEUS_DEFAULT_STALENESS_DELTA TIME_T_TO_CDTIME_T_STATIC(300)
#endif
}
/* }}} */
+#if MHD_VERSION >= 0x00090000
+static int prom_open_socket(int addrfamily) {
+ /* {{{ */
+ char service[NI_MAXSERV];
+ snprintf(service, sizeof(service), "%hu", httpd_port);
+
+ struct addrinfo *res;
+ int status = getaddrinfo(NULL, service,
+ &(struct addrinfo){
+ .ai_flags = AI_PASSIVE | AI_ADDRCONFIG,
+ .ai_family = addrfamily,
+ .ai_socktype = SOCK_STREAM,
+ },
+ &res);
+ if (status != 0) {
+ return -1;
+ }
+
+ int fd = -1;
+ for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) {
+ fd = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC, 0);
+ if (fd == -1)
+ continue;
+
+ if (bind(fd, ai->ai_addr, ai->ai_addrlen) != 0) {
+ close(fd);
+ fd = -1;
+ continue;
+ }
+
+ if (listen(fd, /* backlog = */ 16) != 0) {
+ close(fd);
+ fd = -1;
+ continue;
+ }
+
+ break;
+ }
+
+ freeaddrinfo(res);
+
+ return fd;
+} /* }}} int prom_open_socket */
+
+static struct MHD_Daemon *prom_start_daemon() {
+ /* {{{ */
+ int fd = prom_open_socket(PF_INET6);
+ if (fd == -1)
+ fd = prom_open_socket(PF_INET);
+ if (fd == -1) {
+ ERROR("write_prometheus plugin: Opening a listening socket failed.");
+ return NULL;
+ }
+
+ struct MHD_Daemon *d =
+ MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, 0,
+ /* MHD_AcceptPolicyCallback = */ NULL,
+ /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler,
+ NULL, MHD_OPTION_LISTEN_SOCKET, fd, MHD_OPTION_END);
+ if (d == NULL) {
+ ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
+ close(fd);
+ return NULL;
+ }
+
+ return d;
+} /* }}} struct MHD_Daemon *prom_start_daemon */
+#else /* if MHD_VERSION < 0x00090000 */
+static struct MHD_Daemon *prom_start_daemon() {
+ /* {{{ */
+ struct MHD_Daemon *d =
+ MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, 0,
+ /* MHD_AcceptPolicyCallback = */ NULL,
+ /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler,
+ NULL, MHD_OPTION_END);
+ if (d == NULL) {
+ ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
+ return NULL;
+ }
+
+ return d;
+} /* }}} struct MHD_Daemon *prom_start_daemon */
+#endif
+
/*
* collectd callbacks
*/
}
if (httpd == NULL) {
- unsigned int flags = MHD_USE_THREAD_PER_CONNECTION;
-#if MHD_VERSION >= 0x00093300
- flags |= MHD_USE_DUAL_STACK;
-#endif
-
- httpd = MHD_start_daemon(flags, httpd_port,
- /* MHD_AcceptPolicyCallback = */ NULL,
- /* MHD_AcceptPolicyCallback arg = */ NULL,
- http_handler, NULL, MHD_OPTION_END);
+ httpd = prom_start_daemon();
if (httpd == NULL) {
ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
return -1;
char *prefix;
int database;
int max_set_size;
+ int max_set_duration;
_Bool store_rates;
redisContext *conn;
freeReplyObject(rr);
}
+ if (node->max_set_duration > 0) {
+ /*
+ * remove element, scored less than 'current-max_set_duration'
+ * '(%d' indicates 'less than' in redis CLI.
+ */
+ rr = redisCommand(node->conn, "ZREMRANGEBYSCORE %s -1 (%d", key,
+ (time - node->max_set_duration) + 1);
+ if (rr == NULL)
+ WARNING("ZREMRANGEBYSCORE command error. key:%s message:%s", key,
+ node->conn->errstr);
+ else
+ freeReplyObject(rr);
+ }
+
/* TODO(octo): This is more overhead than necessary. Use the cache and
* metadata to determine if it is a new metric and call SADD only once for
* each metric. */
node->prefix = NULL;
node->database = 0;
node->max_set_size = -1;
+ node->max_set_duration = -1;
node->store_rates = 1;
pthread_mutex_init(&node->lock, /* attr = */ NULL);
status = cf_util_get_int(child, &node->database);
} else if (strcasecmp("MaxSetSize", child->key) == 0) {
status = cf_util_get_int(child, &node->max_set_size);
+ } else if (strcasecmp("MaxSetDuration", child->key) == 0) {
+ status = cf_util_get_int(child, &node->max_set_duration);
} else if (strcasecmp("StoreRates", child->key) == 0) {
status = cf_util_get_boolean(child, &node->store_rates);
} else
ssize_t status = 0;
status = swrite(cb->sock_fd, cb->send_buf, strlen(cb->send_buf));
- if (status < 0) {
+ if (status != 0) {
char errbuf[1024];
ERROR("write_tsdb plugin: send failed with status %zi (%s)", status,
sstrerror(errno, errbuf, sizeof(errbuf)));
/*
* Global variables
*/
+static value_to_rate_state_t arc_hits_state;
+static value_to_rate_state_t arc_misses_state;
+static value_to_rate_state_t l2_hits_state;
+static value_to_rate_state_t l2_misses_state;
#if defined(KERNEL_LINUX)
#include "utils_llist.h"
za_read_derive(ksp, "mru_hits", "cache_result", "mru-hit");
za_read_derive(ksp, "mru_ghost_hits", "cache_result", "mru_ghost-hit");
+ cdtime_t now = cdtime();
+
/* Ratios */
- arc_hits = (gauge_t)get_zfs_value(ksp, "hits");
- arc_misses = (gauge_t)get_zfs_value(ksp, "misses");
- l2_hits = (gauge_t)get_zfs_value(ksp, "l2_hits");
- l2_misses = (gauge_t)get_zfs_value(ksp, "l2_misses");
+ if ((value_to_rate(&arc_hits, (value_t){.derive = get_zfs_value(ksp, "hits")},
+ DS_TYPE_DERIVE, now, &arc_hits_state) == 0) &&
+ (value_to_rate(&arc_misses,
+ (value_t){.derive = get_zfs_value(ksp, "misses")},
+ DS_TYPE_DERIVE, now, &arc_misses_state) == 0)) {
+ za_submit_ratio("arc", arc_hits, arc_misses);
+ }
- za_submit_ratio("arc", arc_hits, arc_misses);
- za_submit_ratio("L2", l2_hits, l2_misses);
+ if ((value_to_rate(&l2_hits,
+ (value_t){.derive = get_zfs_value(ksp, "l2_hits")},
+ DS_TYPE_DERIVE, now, &l2_hits_state) == 0) &&
+ (value_to_rate(&l2_misses,
+ (value_t){.derive = get_zfs_value(ksp, "l2_misses")},
+ DS_TYPE_DERIVE, now, &l2_misses_state) == 0)) {
+ za_submit_ratio("L2", l2_hits, l2_misses);
+ }
/* I/O */
value_t l2_io[] = {