* Version of collectd:
* Operating system / distribution:
+* Kernel version (if applicable):
## Expected behavior
--- /dev/null
+[submodule "gnulib"]
+ path = gnulib
+ url = git://git.savannah.gnu.org/gnulib.git
script:
- if [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]]; then exit 0; fi
- ./configure
- - make -j 4
+ - make -j $(nproc)
- make check
addons:
description: "Build submitted via Travis CI"
notification_email: collectd-changes@verplant.org
build_command_prepend: "./configure; make clean"
- build_command: "make -j 4"
+ build_command: "make -j $(nproc)"
branch_pattern: coverity_scan
+2018-10-23, Version 5.8.1
+ * collectd: Fix "BaseDir" option. Thanks to Mariusz Białończyk and
+ Pavel Rochnyak. #2857
+ * collectd: improve error handling, check return values. Thanks to
+ Florian Forster.
+ * Build System: use "kstat.h", when available. Thanks to Dagobert
+ Michelsen and Pavel Rochnyak. #2784
+ * Build System: Fix distcheck on MacOS. Thanks to Ruben Kerkhof.
+ * Build System: add missing include of ""collectd.h"" to fix builds on
+ Solaris. Thanks to Pavel Rochnyak.
+ * Build System: add endianess checks for AIX, fix GCC issue on Mac
+ byteorder, fix byteorder on Solaris, add fallback for endianess
+ conversion. Thanks to Dagobert Michelsen (multiple cherry picks from
+ master).
+ * Build System: Out-of-tree builds have been fixed. Thanks to Florian
+ Forster. #2602
+ * Configuration: Error handling in the config parsing code has been
+ improved. Thanks to Florian Forster.
+ * Documentation: Fix typo in collectd.conf(5). Thanks to Pavel Rochnyak.
+ #2760
+ * Documentation: update note on dpdkstat. Thanks to Maryam Tahhan. #2613
+ * Various plugins: Errors found by the static code analysis tool
+ Coverity were fixed. Thanks to Florian Forster. #2559, #2560, #2561,
+ #2562, #2563, #2565, #2568, #2575, #2579, #2580, #2588, #2589
+ * Ceph plugin: A segfault has been fixed. Thanks to Aleksei Zakharov and
+ Matthias Runge. #2572
+ * DF plugin: fix memory leak in error case. Thanks to Takahashi tsc.
+ * Exec plugin: check return value of "plugin_thread_create()". Thanks to
+ Florian Forster.
+ * Exec plugin: Handling of large groups has been fixed. Thanks to
+ Sridhar Mallem. #2696
+ * Exec plugin: Incorrect use of *putenv(3)* has been fixed. Thanks to
+ Daniel Vrátil.
+ * Exec plugin: A deadlock related to setting environment variables after
+ *fork()* has been fixed. Thanks to Daniel Vrátil.
+ * Intel PMU plugin: add core groups feature. Thanks to Kamil Wiatrowski.
+ #2681
+ * Intel PMU plugin: fix compatibility issue with collectd 5.8. Thanks to
+ Kamil Wiatrowski.
+ * Intel PMU plugin: fix possible "NULL" pointer dereference. Thanks to
+ Kamil Wiatrowski. #2676
+ * IPMI plugin: A segfault caused by a wrong data type has been fixed.
+ Thanks to Mariusz Szafrański. #2742
+ * IPMI plugin: The sensor configuration option has been fixed. Thanks to
+ Pavel Rochnyak. #2629
+ * memcached plugin: A deadlock situation has been fixed. Thanks to Pavel
+ Rochnyak. #2612
+ * NFS plugin: Support for NFSv4 has been fixed. Thanks to Jan-Philipp
+ Litza. #2076
+ * NTPd plugin: A memory leak in the error handling path has been fixed.
+ Thanks to Ruben Kerkhof. #2942
+ * OVS Stats plugin: A deadlock situation has been fixed. Thanks to
+ Volodymyr Mytnyk. #2590
+ * OVS Stats plugin: Fix reconnect after thread terminated. Thanks to
+ Volodymyr Mytnyk and Maram Tahhan. #2574
+ * Perl plugin: A compilation failure has been fixed. Thanks to Pavel
+ Rochnyak. #2732
+ * Perl plugin: Fix exporting notification meta data. Thanks to Florian
+ Forster.
+ * RRDtool plugin: Handling of very large "GAUGE" metrics has been fixed.
+ Thanks to Miroslav Lichvar. #2566
+ * Tail plugin: Several regressions have been fixed. Thanks to Pavel
+ Rochnyak. #2535, #2587, #2611
+ * turbostat plugin: A potential segfault due to an incorrect *free()*
+ has been fixed. Thanks to Ruben Kerkhof. #2948
+ * UUID plugin: Fix hostname setting. Thanks to Pavel Rochnyak. #2723
+ * virt plugin: A segfault during error handling has been fixed. Thanks
+ to Ruben Kerkhof. {{Issue|2919]}
+ * Write Kafka plugin: A build failure due to a deprecated API call has
+ been fixed. Thanks to Pavel Rochnyak. #2607, #2628, #2640
+ * Write Prometheus plugin: Fix "MHD_USE_INTERNAL_POLLING_THREAD" flag in
+ newer libmicrohttpd. Thanks to Pavel Rochnyak. #2849
+ * Write Prometheus plugin: set "SO_REUSEADDRESS" on listening socket.
+ Thanks to Pavel Rochnyak. #2570, #2673
+
2017-11-17, Version 5.8.0
* collectd: The core daemon is now completely licensed under the MIT
license.
ACLOCAL_AMFLAGS = -I m4
AM_YFLAGS = -d
+if BUILD_WIN32
+cpkgdatadir=$(datadir)
+cpkglibdir=$(libdir)/plugins
+cpkglocalstatedir=${localstatedir}
+else
+cpkgdatadir=$(pkgdatadir)
+cpkglibdir=$(pkglibdir)
+cpkglocalstatedir=${localstatedir}/lib/${PACKAGE_NAME}
+endif
BUILT_SOURCES = \
src/libcollectdclient/collectd/lcc_features.h \
lib_LTLIBRARIES = libcollectdclient.la
+if BUILD_WIN32
+# TODO: Build all executables on Windows as well.
+sbin_PROGRAMS = \
+ collectd
+bin_PROGRAMS =
+else
sbin_PROGRAMS = \
collectd \
collectdmon
collectd-nagios \
collectd-tg \
collectdctl
+endif # BUILD_WIN32
noinst_LTLIBRARIES = \
LOG_COMPILER = env VALGRIND="@VALGRIND@" $(abs_srcdir)/testwrapper.sh
-jardir = $(pkgdatadir)/java
+jardir = $(cpkgdatadir)/java
pkglib_LTLIBRARIES =
-module \
-avoid-version \
-export-symbols-regex '\<module_register\>'
+if BUILD_WIN32
+PLUGIN_LDFLAGS += -shared -no-undefined -lcollectd -L.
+endif
AM_CPPFLAGS = \
-DPREFIX='"${prefix}"' \
-DCONFIGFILE='"${sysconfdir}/${PACKAGE_NAME}.conf"' \
-DLOCALSTATEDIR='"${localstatedir}"' \
- -DPKGLOCALSTATEDIR='"${localstatedir}/lib/${PACKAGE_NAME}"' \
- -DPLUGINDIR='"${pkglibdir}"' \
- -DPKGDATADIR='"${pkgdatadir}"'
+ -DPKGLOCALSTATEDIR='"${cpkglocalstatedir}"' \
+ -DPLUGINDIR='"${cpkglibdir}"' \
+ -DPKGDATADIR='"${cpkgdatadir}"'
+if BUILD_WIN32
+AM_CPPFLAGS += -DNOGDI
+endif
+COMMON_DEPS =
+if BUILD_WIN32
+COMMON_DEPS += collectd.exe
+endif
# Link to these libraries..
COMMON_LIBS = $(PTHREAD_LIBS)
+if BUILD_WIN32
+COMMON_LIBS += -lws2_32
+endif
+if BUILD_WITH_GNULIB
+COMMON_LIBS += -lgnu
+endif
if BUILD_WITH_CAPABILITY
COMMON_LIBS += -lcap
endif
collectd_SOURCES = \
- src/daemon/cmd.c \
src/daemon/cmd.h \
src/daemon/collectd.c \
src/daemon/collectd.h \
$(COMMON_LIBS) \
$(DLOPEN_LIBS)
+if BUILD_WIN32
+collectd_SOURCES += src/daemon/cmd_windows.c
+collectd_LDFLAGS += -ldl -Wl,--out-implib,libcollectd.a
+else
+collectd_SOURCES += src/daemon/cmd.c
+endif
+
if BUILD_FEATURE_DAEMON
collectd_CPPFLAGS += -DPIDFILE='"${localstatedir}/run/${PACKAGE_NAME}.pid"'
endif
collectd_LDADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
endif
+if BUILD_WIN32
+collectd_LDFLAGS += -Wl,--out-implib,libcollectd.a
+endif
collectdmon_SOURCES = src/collectdmon.c
-I$(srcdir)/src/daemon
libcollectdclient_la_LDFLAGS = -version-info 2:0:1
libcollectdclient_la_LIBADD = -lm
+if BUILD_WIN32
+libcollectdclient_la_LDFLAGS += -shared -no-undefined
+libcollectdclient_la_LIBADD += -lgnu -lws2_32 -liphlpapi
+endif
if BUILD_WITH_LIBGCRYPT
libcollectdclient_la_CPPFLAGS += $(GCRYPT_CPPFLAGS)
libcollectdclient_la_LDFLAGS += $(GCRYPT_LDFLAGS)
liboconfig_la_CPPFLAGS = -I$(srcdir)/src/liboconfig $(AM_CPPFLAGS)
liboconfig_la_LDFLAGS = -avoid-version $(LEXLIB)
+if BUILD_WITH_LIBCURL
+if BUILD_WITH_LIBSSL
+if BUILD_WITH_LIBYAJL2
+noinst_LTLIBRARIES += liboauth.la
+liboauth_la_SOURCES = \
+ src/utils_oauth.c \
+ src/utils_oauth.h
+liboauth_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(BUILD_WITH_LIBCURL_CFLAGS) \
+ $(BUILD_WITH_LIBSSL_CFLAGS) \
+ $(BUILD_WITH_LIBYAJL_CPPFLAGS)
+liboauth_la_LIBADD = \
+ $(BUILD_WITH_LIBCURL_LIBS) \
+ $(BUILD_WITH_LIBSSL_LIBS) \
+ $(BUILD_WITH_LIBYAJL_LIBS)
+
+check_PROGRAMS += test_utils_oauth
+TESTS += test_utils_oauth
+test_utils_oauth_SOURCES = \
+ src/utils_oauth_test.c
+test_utils_oauth_LDADD = \
+ liboauth.la \
+ libcommon.la \
+ libplugin_mock.la
+
+noinst_LTLIBRARIES += libgce.la
+libgce_la_SOURCES = \
+ src/utils_gce.c \
+ src/utils_gce.h
+libgce_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(BUILD_WITH_LIBCURL_CFLAGS)
+libgce_la_LIBADD = \
+ $(BUILD_WITH_LIBCURL_LIBS)
+endif
+endif
+endif
+
+if BUILD_WITH_LIBYAJL2
+noinst_LTLIBRARIES += libformat_stackdriver.la
+libformat_stackdriver_la_SOURCES = \
+ src/utils_format_stackdriver.c \
+ src/utils_format_stackdriver.h
+libformat_stackdriver_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(BUILD_WITH_LIBYAJL_CPPFLAGS)
+libformat_stackdriver_la_LIBADD = \
+ libavltree.la \
+ $(BUILD_WITH_LIBSSL_LIBS) \
+ $(BUILD_WITH_LIBYAJL_LIBS)
+
+check_PROGRAMS += test_format_stackdriver
+TESTS += test_format_stackdriver
+test_format_stackdriver_SOURCES = \
+ src/utils_format_stackdriver_test.c \
+ src/testing.h
+test_format_stackdriver_LDADD = \
+ libformat_stackdriver.la \
+ libplugin_mock.la \
+ -lm
+endif
if BUILD_PLUGIN_AGGREGATION
pkglib_LTLIBRARIES += aggregation.la
gps_la_LIBADD = -lpthread $(BUILD_WITH_LIBGPS_LIBS)
endif
+if BUILD_PLUGIN_GPU_NVIDIA
+pkglib_LTLIBRARIES += gpu_nvidia.la
+gpu_nvidia_la_SOURCES = src/gpu_nvidia.c
+gpu_nvidia_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_GPU_CUDA_LDFLAGS)
+gpu_nvidia_la_LIBADD = $(BUILD_WITH_CUDA_LIBS)
+endif
+
if BUILD_PLUGIN_GRPC
pkglib_LTLIBRARIES += grpc.la
grpc_la_SOURCES = src/grpc.cc
pkglib_LTLIBRARIES += logfile.la
logfile_la_SOURCES = src/logfile.c
logfile_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+logfile_la_DEPENDENCIES = $(COMMON_DEPS)
endif
if BUILD_PLUGIN_LOG_LOGSTASH
ovs_stats_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
endif
+if BUILD_PLUGIN_PCIE_ERRORS
+pkglib_LTLIBRARIES += pcie_errors.la
+pcie_errors_la_SOURCES = src/pcie_errors.c
+pcie_errors_la_CPPFLAGS = $(AM_CPPFLAGS)
+pcie_errors_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+
+test_plugin_pcie_errors_SOURCES = \
+ src/pcie_errors_test.c \
+ src/daemon/utils_llist.c \
+ src/daemon/configfile.c \
+ src/daemon/types_list.c
+test_plugin_pcie_errors_CPPFLAGS = $(AM_CPPFLAGS)
+test_plugin_pcie_errors_LDFLAGS = $(PLUGIN_LDFLAGS)
+test_plugin_pcie_errors_LDADD = liboconfig.la libplugin_mock.la
+check_PROGRAMS += test_plugin_pcie_errors
+TESTS += test_plugin_pcie_errors
+endif
+
if BUILD_PLUGIN_PERL
pkglib_LTLIBRARIES += perl.la
perl_la_SOURCES = src/perl.c
write_sensu_la_LDFLAGS = $(PLUGIN_LDFLAGS)
endif
+if BUILD_PLUGIN_WRITE_STACKDRIVER
+pkglib_LTLIBRARIES += write_stackdriver.la
+write_stackdriver_la_SOURCES = src/write_stackdriver.c
+write_stackdriver_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+write_stackdriver_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
+write_stackdriver_la_LIBADD = libformat_stackdriver.la libgce.la liboauth.la \
+ $(BUILD_WITH_LIBCURL_LIBS)
+endif
+
if BUILD_PLUGIN_WRITE_TSDB
pkglib_LTLIBRARIES += write_tsdb.la
write_tsdb_la_SOURCES = src/write_tsdb.c
else \
$(INSTALL) -m 0640 $(builddir)/src/collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \
fi; \
- $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)
- $(INSTALL) -m 0644 $(srcdir)/src/types.db $(DESTDIR)$(pkgdatadir)/types.db;
+ $(mkinstalldirs) $(DESTDIR)$(cpkgdatadir)
+ $(INSTALL) -m 0644 $(srcdir)/src/types.db $(DESTDIR)$(cpkgdatadir)/types.db;
$(INSTALL) -m 0644 $(srcdir)/src/postgresql_default.conf \
- $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
+ $(DESTDIR)$(cpkgdatadir)/postgresql_default.conf;
uninstall-hook:
- rm -f $(DESTDIR)$(pkgdatadir)/types.db;
+ rm -f $(DESTDIR)$(cpkgdatadir)/types.db;
rm -f $(DESTDIR)$(sysconfdir)/collectd.conf
- rm -f $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
+ rm -f $(DESTDIR)$(cpkgdatadir)/postgresql_default.conf;
all-local: @PERL_BINDINGS@
jar_DATA = collectd-api.jar generic-jmx.jar
endif
+
- gps
Monitor gps related data through gpsd.
+ - gpu_nvidia
+ Monitor NVIDIA GPU statistics available through NVML.
+
- hddtemp
Hard disk temperatures using hddtempd.
OVS documentation.
<http://openvswitch.org/support/dist-docs/INSTALL.rst.html>
+ - pcie_errors
+ Read errors from PCI Express Device Status and AER extended capabilities.
+ <https://www.design-reuse.com/articles/38374/pcie-error-logging-and-handling-on-a-typical-soc.html>
+
- perl
The perl plugin implements a Perl-interpreter into collectd. You can
write your own plugins in Perl and return arbitrary values using this
particular.
<http://developer.apple.com/corefoundation/>
+ * CUDA (optional)
+ Used by the `gpu_nvidia' plugin
+ <https://developer.nvidia.com/cuda-downloads>
+
* libatasmart (optional)
Used by the `smart' plugin.
<http://git.0pointer.de/?p=libatasmart.git>
The `build.sh' script takes no arguments.
+Building on Windows
+-----------------------------------------------
+
+Collectd can be built on Windows using Cygwin, and the result is a binary that
+runs natively on Windows. That is, Cygwin is only needed for building, not running,
+collectd.
+
+You will need to install the following Cygwin packages:
+- automake
+- bison
+- flex
+- git
+- libtool
+- make
+- mingw64-x86_64-dlfcn
+- mingw64-x86_64-gcc-core
+- mingw64-x86_64-zlib
+- pkg-config
+
+To build, just run the `build.sh' script in your Cygwin terminal. By default, it installs
+to "C:/Program Files/collectd". You can change the location by setting the INSTALL_DIR
+variable:
+
+$ export INSTALL_DIR="C:/some/other/install/directory"
+$ ./build.sh
+
+or:
+
+$ INSTALL_DIR="C:/some/other/install/directory" ./build.sh
+
+
Crosscompiling
--------------
done
}
-check_for_application lex bison autoheader aclocal automake autoconf pkg-config
-
-libtoolize=""
-libtoolize --version >/dev/null 2>/dev/null
-if test $? -eq 0; then
- libtoolize=libtoolize
-else
- glibtoolize --version >/dev/null 2>/dev/null
+setup_libtool()
+{
+ libtoolize=""
+ libtoolize --version >/dev/null 2>/dev/null
if test $? -eq 0; then
- libtoolize=glibtoolize
+ libtoolize=libtoolize
else
- cat >&2 <<EOF
+ glibtoolize --version >/dev/null 2>/dev/null
+ if test $? -eq 0; then
+ libtoolize=glibtoolize
+ else
+ cat >&2 <<EOF
WARNING: Neither \`libtoolize' nor \`glibtoolize' have been found!
Please make sure that one of them is installed and is in one of the
directories listed in the PATH environment variable.
EOF
- GLOBAL_ERROR_INDICATOR=1
+ GLOBAL_ERROR_INDICATOR=1
+ fi
fi
- fi
-if test "$GLOBAL_ERROR_INDICATOR" != "0"; then
- exit 1
-fi
+ if test "$GLOBAL_ERROR_INDICATOR" != "0"; then
+ exit 1
+ fi
+}
+
+build()
+{
+ echo "Building..."
+ check_for_application lex bison autoheader aclocal automake autoconf pkg-config
+ setup_libtool
+
+ set -x
+ autoheader \
+ && aclocal -I m4 \
+ && $libtoolize --copy --force \
+ && automake --add-missing --copy \
+ && autoconf
+}
+
+build_cygwin()
+{
+ echo "Building for Cygwin..."
+ check_for_application aclocal autoconf autoheader automake bison flex git make pkg-config x86_64-w64-mingw32-gcc
+ setup_libtool
+
+ set -e
+
+ : ${INSTALL_DIR:="C:/PROGRA~1/collectd"}
+ : ${LIBDIR:="${INSTALL_DIR}"}
+ : ${BINDIR:="${INSTALL_DIR}"}
+ : ${SBINDIR:="${INSTALL_DIR}"}
+ : ${SYSCONFDIR:="${INSTALL_DIR}"}
+ : ${LOCALSTATEDIR:="${INSTALL_DIR}"}
+ : ${DATAROOTDIR:="${INSTALL_DIR}"}
+ : ${DATADIR:="${INSTALL_DIR}"}
-set -x
+ echo "Installing collectd to ${INSTALL_DIR}."
+ TOP_SRCDIR="$(pwd)"
+ MINGW_ROOT="$(x86_64-w64-mingw32-gcc -print-sysroot)/mingw"
+ export GNULIB_DIR="${TOP_SRCDIR}/gnulib/build/gllib"
+
+ export CC="x86_64-w64-mingw32-gcc"
+
+ if [ -d "${TOP_SRCDIR}/gnulib/build" ]; then
+ echo "Assuming that gnulib is already built, because gnulib/build exists."
+ else
+ git submodule init
+ git submodule update
+ cd gnulib
+ ./gnulib-tool \
+ --create-testdir \
+ --source-base=lib \
+ --dir=${TOP_SRCDIR}/gnulib/build \
+ canonicalize-lgpl \
+ fcntl-h \
+ fnmatch \
+ getsockopt \
+ gettimeofday \
+ nanosleep \
+ netdb \
+ net_if \
+ poll \
+ recv \
+ regex \
+ sendto \
+ setlocale \
+ strtok_r \
+ sys_resource \
+ sys_socket \
+ sys_stat \
+ sys_wait \
+ time_r
+
+ cd ${TOP_SRCDIR}/gnulib/build
+ ./configure --host="mingw32" LIBS="-lws2_32 -lpthread"
+ make
+ cd gllib
+
+ # We have to rebuild libgnu.a to get the list of *.o files to build a dll later
+ rm libgnu.a
+ OBJECT_LIST=`make V=1 | grep "ar" | cut -d' ' -f4-`
+ $CC -shared -o libgnu.dll $OBJECT_LIST -lws2_32 -lpthread
+ rm libgnu.a # get rid of it, to use libgnu.dll
+ fi
+ cd "${TOP_SRCDIR}"
+
+ set -x
+ autoreconf --install
+
+ export LDFLAGS="-L${GNULIB_DIR}"
+ export LIBS="-lgnu"
+ export CFLAGS="-Drestrict=__restrict -I${GNULIB_DIR}"
+
+ ./configure \
+ --prefix="${INSTALL_DIR}" \
+ --libdir="${LIBDIR}" \
+ --bindir="${BINDIR}" \
+ --sbindir="${SBINDIR}" \
+ --sysconfdir="${SYSCONFDIR}" \
+ --localstatedir="${LOCALSTATEDIR}" \
+ --datarootdir="${DATAROOTDIR}" \
+ --datarootdir="${DATADIR}" \
+ --disable-all-plugins \
+ --host="mingw32" \
+ --enable-logfile \
+ --enable-match_regex \
+ --enable-target_replace \
+ --enable-target_set
+
+ cp ${GNULIB_DIR}/../config.h src/gnulib_config.h
+ echo "#include <config.h.in>" >> src/gnulib_config.h
+
+ cp libtool libtool_bak
+ sed -i "s%\$LTCC \$LTCFLAGS\(.*cwrapper.*\)%\$LTCC \1%" libtool
+
+ make
+ make install
+
+ cp "${GNULIB_DIR}/libgnu.dll" "${INSTALL_DIR}"
+ cp "${MINGW_ROOT}/bin/zlib1.dll" "${INSTALL_DIR}"
+ cp "${MINGW_ROOT}/bin/libwinpthread-1.dll" "${INSTALL_DIR}"
+ cp "${MINGW_ROOT}/bin/libdl.dll" "${INSTALL_DIR}"
+
+ echo "Done."
+}
+
+os_name="$(uname)"
+if test "${os_name#CYGWIN}" != "$os_name"; then
+ build_cygwin
+else
+ build
+fi
-autoheader \
-&& aclocal -I m4 \
-&& $libtoolize --copy --force \
-&& automake --add-missing --copy \
-&& autoconf
AC_DEFINE([KERNEL_SOLARIS], [1], [True if program is to be compiled for a Solaris kernel])
ac_system="Solaris"
;;
+ *mingw32*)
+ AC_DEFINE([KERNEL_WIN32], [1], [True if program is to be compiled for a Windows kernel])
+ ac_system="Windows"
+ ;;
*)
ac_system="unknown"
;;
AM_CONDITIONAL([BUILD_LINUX], [test "x$ac_system" = "xLinux"])
AM_CONDITIONAL([BUILD_OPENBSD], [test "x$ac_system" = "xOpenBSD"])
AM_CONDITIONAL([BUILD_SOLARIS], [test "x$ac_system" = "xSolaris"])
+AM_CONDITIONAL([BUILD_WIN32], [test "x$ac_system" = "xWindows"])
if test "x$ac_system" = "xSolaris"; then
AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], [1], [Define to enforce POSIX thread semantics under Solaris.])
AC_DEFINE([HAVE_CAPABILITY], [1], [Define to 1 if you have cap_get_proc() (-lcap).])
fi
+ # For pcie_errors plugin
+ AC_CHECK_HEADERS([linux/pci_regs.h],
+ [have_pci_regs_h="yes"],
+ [have_pci_regs_h="no (linux/pci_regs.h not found)"]
+ )
+
else
have_linux_raid_md_u_h="no"
have_linux_wireless_h="no"
getaddrinfo \
getgrnam_r \
getnameinfo \
+ getpwnam \
getpwnam_r \
gettimeofday \
if_indextoname \
SAVE_CFLAGS="$CFLAGS"
CFLAGS="-Wall -Werror"
-SAVE_LDFAGS="$LDFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
LDFLAGS=""
+if test "x$ac_system" = "xWindows"; then
+ # This is exported from build.sh
+ LDFLAGS="$LDFLAGS -L${GNULIB_DIR}"
+fi
AC_CACHE_CHECK([for strtok_r],
[c_cv_have_strtok_r_default],
[
AC_CHECK_LIB([socket], [socket],
[socket_needs_socket="yes"],
- [AC_MSG_ERROR([cannot find socket() in libsocket])]
+ [
+ AC_CHECK_LIB([gnu], [rpl_socket],
+ [socket_needs_gnulib="yes"],
+ [AC_MSG_ERROR([cannot find socket() in libsocket])]
+ )
+ ]
)
]
)
AM_CONDITIONAL([BUILD_WITH_LIBSOCKET], [test "x$socket_needs_socket" = "xyes"])
+AM_CONDITIONAL([BUILD_WITH_GNULIB], [test "x$socket_needs_gnulib" = "xyes"])
clock_gettime_needs_posix4="no"
AC_CHECK_FUNCS([clock_gettime],
with_libkvm="yes"
fi
+# --with-cuda {{{
+# only CUDA provides the nvml.h header
+AC_ARG_WITH([cuda],
+ [AS_HELP_STRING([--with-cuda@<:@=PREFIX@:>@], [Path to cuda.])],
+ [
+ if test "x$withval" = "xyes"; then
+ with_cuda="yes"
+ else if test "x$withval" = "xno"; then
+ with_cuda="no"
+ else
+ with_cuda="yes"
+ CUDA_CFLAGS="$CUDA_CFLAGS -I$withval/include"
+ CUDA_LDFLAGS="$CUDA_LDFLAGS -L$withval/lib"
+ fi; fi
+ ],
+ [ with_cuda="yes"
+ CUDA_CFLAGS="$CUDA_CFLAGS -I/opt/cuda/include"
+ CUDA_LDFLAGS="$CUDA_LDFLAGS -L/opt/cuda/lib64"
+ ]
+)
+
+SAVE_CFLAGS="$CFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CFLAGS="$CFLAGS $CUDA_CFLAGS"
+LDFLAGS="$LDFLAGS $CUDA_LDFLAGS"
+
+if test "x$with_cuda" = "xyes"; then
+ AC_CHECK_HEADERS([nvml.h],
+ [with_cuda="yes"],
+ [with_cuda="no (header file missing)"]
+ )
+fi
+
+if test "x$with_cuda" = "xpkgconfig"; then
+ AC_CHECK_HEADERS([nvml.h],
+ [],
+ [with_cuda="no (header file missing)"]
+ )
+fi
+
+if test "x$with_cuda" = "xyes"; then
+ BUILD_WITH_CUDA_CFLAGS="$CUDA_CFLAGS"
+ BUILD_WITH_CUDA_LDFLAGS="$CUDA_LDFLAGS"
+ BUILD_WITH_CUDA_LIBS="-lnvidia-ml"
+fi
+
+AC_SUBST([BUILD_WITH_CUDA_CFLAGS])
+AC_SUBST([BUILD_WITH_CUDA_LDFLAGS])
+AC_SUBST([BUILD_WITH_CUDA_LIBS])
+
+# }}}
+
# --with-libaquaero5 {{{
AC_ARG_WITH([libaquaero5],
[AS_HELP_STRING([--with-libaquaero5@<:@=PREFIX@:>@], [Path to aquatools-ng source code.])],
AC_SUBST(BUILD_WITH_LIBCURL_CFLAGS)
AC_SUBST(BUILD_WITH_LIBCURL_LIBS)
+
+AM_CONDITIONAL([BUILD_WITH_LIBCURL], [test "x$with_libcurl" = "xyes"])
# }}}
# --with-libdbi {{{
)
# }}}
+# --with-libssl {{{
+with_libssl_cflags=""
+with_libssl_ldflags=""
+AC_ARG_WITH([libssl], [AS_HELP_STRING([--with-libssl@<:@=PREFIX@:>@], [Path to libssl.])],
+[
+ if test "x$withval" != "xno" && test "x$withval" != "xyes"; then
+ with_libssl_cppflags="-I$withval/include"
+ with_libssl_ldflags="-L$withval/lib"
+ with_libssl="yes"
+ else
+ with_libssl="$withval"
+ fi
+],
+[
+ with_libssl="yes"
+])
+if test "x$with_libssl" = "xyes"; then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libssl_cppflags"
+
+ AC_CHECK_HEADERS([openssl/sha.h openssl/blowfish.h openssl/rand.h],
+ [with_libssl="yes"],
+ [with_libssl="no (ssl header not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libssl" = "xyes"; then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ SAVE_LDFLAGS="$LDFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libssl_cppflags"
+ LDFLAGS="$LDFLAGS $with_libssl_ldflags"
+
+ AC_CHECK_LIB([ssl], [OPENSSL_init_ssl], [with_libssl="yes"], [with_libssl="no (Symbol 'SSL_library_init' not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libssl" = "xyes"; then
+ BUILD_WITH_LIBSSL_CFLAGS="$with_libssl_cflags"
+ BUILD_WITH_LIBSSL_LDFLAGS="$with_libssl_ldflags"
+ BUILD_WITH_LIBSSL_LIBS="-lssl -lcrypto"
+ AC_SUBST([BUILD_WITH_LIBSSL_CFLAGS])
+ AC_SUBST([BUILD_WITH_LIBSSL_LDFLAGS])
+ AC_SUBST([BUILD_WITH_LIBSSL_LIBS])
+ AC_DEFINE([HAVE_LIBSSL], [1], [Define if libssl is present and usable.])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBSSL, test "x$with_libssl" = "xyes")
+# }}}
+
# --with-libstatgrab {{{
AC_ARG_WITH([libstatgrab],
[AS_HELP_STRING([--with-libstatgrab@<:@=PREFIX@:>@], [Path to libstatgrab.])],
fi
if test "x$with_libxmms" = "xyes"; then
- SAVE_CFLAGS="$CFLAGS"
- CFLAGS="$with_xmms_cflags"
+ SAVE_CPPFLAGS="$CFLAGS"
+ CPPFLAGS="$with_xmms_cflags"
AC_CHECK_HEADER([xmmsctrl.h],
[with_libxmms="yes"],
[with_libxmms="no"],
)
- CFLAGS="$SAVE_CFLAGS"
+ CPPFLAGS="$SAVE_CPPFLAGS"
fi
if test "x$with_libxmms" = "xyes"; then
AC_SUBST([BUILD_WITH_LIBYAJL_LIBS])
AM_CONDITIONAL([BUILD_WITH_LIBYAJL], [test "x$with_libyajl" = "xyes"])
+AM_CONDITIONAL([BUILD_WITH_LIBYAJL2], [test "x$with_libyajl$with_libyajl2" = "xyesyes"])
# }}}
# --with-mic {{{
AC_SUBST([BUILD_WITH_LIBVARNISH_LIBS])
# }}}
-# pkg-config --exists 'libxml-2.0'; pkg-config --exists libvirt {{{
-$PKG_CONFIG --exists 'libxml-2.0' 2>/dev/null
-if test $? -eq 0; then
- with_libxml2="yes"
-else
- with_libxml2="no (pkg-config doesn't know libxml-2.0)"
-fi
-
-$PKG_CONFIG --exists libvirt 2>/dev/null
-if test $? = 0; then
- with_libvirt="yes"
-else
- with_libvirt="no (pkg-config doesn't know libvirt)"
-fi
-
-if test "x$with_libxml2" = "xyes"; then
- with_libxml2_cflags="`$PKG_CONFIG --cflags libxml-2.0`"
- if test $? -ne 0; then
- with_libxml2="no"
- fi
-
- with_libxml2_ldflags="`$PKG_CONFIG --libs libxml-2.0`"
- if test $? -ne 0; then
- with_libxml2="no"
- fi
-fi
+# --with-libxml2 {{{
+AC_ARG_WITH(libxml2,
+ [AS_HELP_STRING([--with-libxml2@<:@=PREFIX@:>@], [Path to libxml2.])],
+ [
+ if test "x$withval" = "xno"; then
+ with_libxml2="no"
+ else if test "x$withval" = "xyes"; then
+ $PKG_CONFIG --exists 'libxml-2.0' 2>/dev/null
+ if test $? -eq 0; then
+ with_libxml2="yes"
+ with_libxml2_cflags="`$PKG_CONFIG --cflags libxml-2.0`"
+ with_libxml2_ldflags="`$PKG_CONFIG --libs libxml-2.0`"
+ else
+ with_libxml2="no (pkg-config doesn't know libxml-2.0)"
+ fi
+ else
+ with_libxml2="yes"
+ with_libxml2_cflags="-I$withval/include"
+ with_libxml2_ldflags="-L$withval/lib"
+ fi; fi
+ ],
+ dnl if no argument --with-libxml2 was passed, find the library locations
+ dnl with pkg-config just like above, when --with-libxml2=yes.
+ [
+ with_libxml2="yes"
+ $PKG_CONFIG --exists 'libxml-2.0' 2>/dev/null
+ if test $? -eq 0; then
+ with_libxml2="yes"
+ with_libxml2_cflags="`$PKG_CONFIG --cflags libxml-2.0`"
+ with_libxml2_ldflags="`$PKG_CONFIG --libs libxml-2.0`"
+ else
+ with_libxml2="no (pkg-config doesn't know libxml-2.0)"
+ fi
+ ]
+)
if test "x$with_libxml2" = "xyes"; then
SAVE_CPPFLAGS="$CPPFLAGS"
AC_SUBST([BUILD_WITH_LIBXML2_CFLAGS])
AC_SUBST([BUILD_WITH_LIBXML2_LIBS])
+# }}}
+
+# pkg-config --exists libvirt {{{
+$PKG_CONFIG --exists libvirt 2>/dev/null
+if test $? = 0; then
+ with_libvirt="yes"
+else
+ with_libvirt="no (pkg-config doesn't know libvirt)"
+fi
if test "x$with_libvirt" = "xyes"; then
with_libvirt_cflags="`$PKG_CONFIG --cflags libvirt`"
plugin_fhcount="no"
plugin_fscache="no"
plugin_gps="no"
+plugin_gpu_nvidia="no"
plugin_grpc="no"
plugin_hugepages="no"
plugin_intel_pmu="no"
plugin_numa="no"
plugin_ovs_events="no"
plugin_ovs_stats="no"
+plugin_pcie_errors="no"
plugin_perl="no"
plugin_pinba="no"
plugin_processes="no"
plugin_vserver="no"
plugin_wireless="no"
plugin_write_prometheus="no"
+plugin_write_stackdriver="no"
plugin_xencpu="no"
plugin_zfs_arc="no"
plugin_zone="no"
plugin_connectivity="yes"
fi
fi
+
+ if test "x$have_pci_regs_h" = "xyes"; then
+ plugin_pcie_errors="yes"
+ fi
fi
if test "x$ac_system" = "xOpenBSD"; then
plugin_curl_json="yes"
fi
+if test "x$with_libcurl" = "xyes" && test "x$with_libssl" = "xyes" && test "x$with_libyajl" = "xyes" && test "x$with_libyajl2" = "xyes"; then
+ plugin_write_stackdriver="yes"
+fi
+
if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"; then
plugin_curl_xml="yes"
fi
m4_divert_once([HELP_ENABLE], [])
-AC_PLUGIN([aggregation], [yes], [Aggregation plugin])
-AC_PLUGIN([amqp], [$with_librabbitmq], [AMQP output plugin])
-AC_PLUGIN([amqp1], [$with_libqpid_proton], [AMQP 1.0 output plugin])
-AC_PLUGIN([apache], [$with_libcurl], [Apache httpd statistics])
-AC_PLUGIN([apcups], [yes], [Statistics of UPSes by APC])
-AC_PLUGIN([apple_sensors], [$with_libiokit], [Apple hardware sensors])
-AC_PLUGIN([aquaero], [$with_libaquaero5], [Aquaero hardware sensors])
-AC_PLUGIN([ascent], [$plugin_ascent], [AscentEmu player statistics])
-AC_PLUGIN([barometer], [$plugin_barometer], [Barometer sensor on I2C])
-AC_PLUGIN([battery], [$plugin_battery], [Battery statistics])
-AC_PLUGIN([bind], [$plugin_bind], [ISC Bind nameserver statistics])
-AC_PLUGIN([ceph], [$plugin_ceph], [Ceph daemon statistics])
-AC_PLUGIN([cgroups], [$plugin_cgroups], [CGroups CPU usage accounting])
-AC_PLUGIN([chrony], [yes], [Chrony statistics])
-AC_PLUGIN([connectivity], [$plugin_connectivity], [Network interface up/down events])
-AC_PLUGIN([conntrack], [$plugin_conntrack], [nf_conntrack statistics])
-AC_PLUGIN([contextswitch], [$plugin_contextswitch], [context switch statistics])
-AC_PLUGIN([cpu], [$plugin_cpu], [CPU usage statistics])
-AC_PLUGIN([cpufreq], [$plugin_cpufreq], [CPU frequency statistics])
-AC_PLUGIN([cpusleep], [$plugin_cpusleep], [CPU sleep statistics])
-AC_PLUGIN([csv], [yes], [CSV output plugin])
-AC_PLUGIN([curl], [$with_libcurl], [CURL generic web statistics])
-AC_PLUGIN([curl_json], [$plugin_curl_json], [CouchDB statistics])
-AC_PLUGIN([curl_xml], [$plugin_curl_xml], [CURL generic xml statistics])
-AC_PLUGIN([dbi], [$with_libdbi], [General database statistics])
-AC_PLUGIN([df], [$plugin_df], [Filesystem usage statistics])
-AC_PLUGIN([disk], [$plugin_disk], [Disk usage statistics])
-AC_PLUGIN([dns], [$with_libpcap], [DNS traffic analysis])
-AC_PLUGIN([dpdkevents], [$plugin_dpdkevents], [Events from DPDK])
-AC_PLUGIN([dpdkstat], [$plugin_dpdkstat], [Stats from DPDK])
-AC_PLUGIN([drbd], [$plugin_drbd], [DRBD statistics])
-AC_PLUGIN([email], [yes], [EMail statistics])
-AC_PLUGIN([entropy], [$plugin_entropy], [Entropy statistics])
-AC_PLUGIN([ethstat], [$plugin_ethstat], [Stats from NIC driver])
-AC_PLUGIN([exec], [yes], [Execution of external programs])
-AC_PLUGIN([fhcount], [$plugin_fhcount], [File handles statistics])
-AC_PLUGIN([filecount], [yes], [Count files in directories])
-AC_PLUGIN([fscache], [$plugin_fscache], [fscache statistics])
-AC_PLUGIN([gmond], [$with_libganglia], [Ganglia plugin])
-AC_PLUGIN([gps], [$plugin_gps], [GPS plugin])
-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([ipmi], [$plugin_ipmi], [IPMI sensor statistics])
-AC_PLUGIN([iptables], [$with_libiptc], [IPTables rule counters])
-AC_PLUGIN([ipvs], [$plugin_ipvs], [IPVS connection statistics])
-AC_PLUGIN([irq], [$plugin_irq], [IRQ statistics])
-AC_PLUGIN([java], [$with_java], [Embed the Java Virtual Machine])
-AC_PLUGIN([load], [$plugin_load], [System load])
-AC_PLUGIN([log_logstash], [$plugin_log_logstash], [Logstash json_event compatible logging])
-AC_PLUGIN([logfile], [yes], [File logging plugin])
-AC_PLUGIN([lpar], [$with_perfstat], [AIX logical partitions statistics])
-AC_PLUGIN([lua], [$with_liblua], [Lua plugin])
-AC_PLUGIN([lvm], [$with_liblvm2app], [LVM statistics])
-AC_PLUGIN([madwifi], [$have_linux_wireless_h], [Madwifi wireless statistics])
-AC_PLUGIN([match_empty_counter], [yes], [The empty counter match])
-AC_PLUGIN([match_hashed], [yes], [The hashed match])
-AC_PLUGIN([match_regex], [yes], [The regex match])
-AC_PLUGIN([match_timediff], [yes], [The timediff match])
-AC_PLUGIN([match_value], [yes], [The value match])
-AC_PLUGIN([mbmon], [yes], [Query mbmond])
-AC_PLUGIN([mcelog], [$plugin_mcelog], [Machine Check Exceptions notifications])
-AC_PLUGIN([md], [$have_linux_raid_md_u_h], [md (Linux software RAID) devices])
-AC_PLUGIN([memcachec], [$with_libmemcached], [memcachec statistics])
-AC_PLUGIN([memcached], [yes], [memcached statistics])
-AC_PLUGIN([memory], [$plugin_memory], [Memory usage])
-AC_PLUGIN([mic], [$with_mic], [Intel Many Integrated Core stats])
-AC_PLUGIN([modbus], [$with_libmodbus], [Modbus plugin])
-AC_PLUGIN([mqtt], [$with_libmosquitto], [MQTT output plugin])
-AC_PLUGIN([multimeter], [$plugin_multimeter], [Read multimeter values])
-AC_PLUGIN([mysql], [$with_libmysql], [MySQL statistics])
-AC_PLUGIN([netapp], [$with_libnetapp], [NetApp plugin])
-AC_PLUGIN([netlink], [$with_libmnl], [Enhanced Linux network statistics])
-AC_PLUGIN([network], [yes], [Network communication plugin])
-AC_PLUGIN([nfs], [$plugin_nfs], [NFS statistics])
-AC_PLUGIN([nginx], [$with_libcurl], [nginx statistics])
-AC_PLUGIN([notify_desktop], [$with_libnotify], [Desktop notifications])
-AC_PLUGIN([notify_email], [$with_libesmtp], [Email notifier])
-AC_PLUGIN([notify_nagios], [yes], [Nagios notification plugin])
-AC_PLUGIN([ntpd], [yes], [NTPd statistics])
-AC_PLUGIN([numa], [$plugin_numa], [NUMA virtual memory statistics])
-AC_PLUGIN([nut], [$with_libupsclient], [Network UPS tools statistics])
-AC_PLUGIN([olsrd], [yes], [olsrd statistics])
-AC_PLUGIN([onewire], [$with_libowcapi], [OneWire sensor statistics])
-AC_PLUGIN([openldap], [$with_libldap], [OpenLDAP statistics])
-AC_PLUGIN([openvpn], [yes], [OpenVPN client statistics])
-AC_PLUGIN([oracle], [$with_oracle], [Oracle plugin])
-AC_PLUGIN([ovs_events], [$plugin_ovs_events], [OVS events plugin])
-AC_PLUGIN([ovs_stats], [$plugin_ovs_stats], [OVS statistics plugin])
-AC_PLUGIN([perl], [$plugin_perl], [Embed a Perl interpreter])
-AC_PLUGIN([pf], [$have_net_pfvar_h], [BSD packet filter (PF) statistics])
+AC_PLUGIN([aggregation], [yes], [Aggregation plugin])
+AC_PLUGIN([amqp], [$with_librabbitmq], [AMQP output plugin])
+AC_PLUGIN([amqp1], [$with_libqpid_proton], [AMQP 1.0 output plugin])
+AC_PLUGIN([apache], [$with_libcurl], [Apache httpd statistics])
+AC_PLUGIN([apcups], [yes], [Statistics of UPSes by APC])
+AC_PLUGIN([apple_sensors], [$with_libiokit], [Apple hardware sensors])
+AC_PLUGIN([aquaero], [$with_libaquaero5], [Aquaero hardware sensors])
+AC_PLUGIN([ascent], [$plugin_ascent], [AscentEmu player statistics])
+AC_PLUGIN([barometer], [$plugin_barometer], [Barometer sensor on I2C])
+AC_PLUGIN([battery], [$plugin_battery], [Battery statistics])
+AC_PLUGIN([bind], [$plugin_bind], [ISC Bind nameserver statistics])
+AC_PLUGIN([ceph], [$plugin_ceph], [Ceph daemon statistics])
+AC_PLUGIN([cgroups], [$plugin_cgroups], [CGroups CPU usage accounting])
+AC_PLUGIN([chrony], [yes], [Chrony statistics])
+AC_PLUGIN([connectivity], [$plugin_connectivity], [Network interface up/down events])
+AC_PLUGIN([conntrack], [$plugin_conntrack], [nf_conntrack statistics])
+AC_PLUGIN([contextswitch], [$plugin_contextswitch], [context switch statistics])
+AC_PLUGIN([cpu], [$plugin_cpu], [CPU usage statistics])
+AC_PLUGIN([cpufreq], [$plugin_cpufreq], [CPU frequency statistics])
+AC_PLUGIN([cpusleep], [$plugin_cpusleep], [CPU sleep statistics])
+AC_PLUGIN([csv], [yes], [CSV output plugin])
+AC_PLUGIN([curl], [$with_libcurl], [CURL generic web statistics])
+AC_PLUGIN([curl_json], [$plugin_curl_json], [CouchDB statistics])
+AC_PLUGIN([curl_xml], [$plugin_curl_xml], [CURL generic xml statistics])
+AC_PLUGIN([dbi], [$with_libdbi], [General database statistics])
+AC_PLUGIN([df], [$plugin_df], [Filesystem usage statistics])
+AC_PLUGIN([disk], [$plugin_disk], [Disk usage statistics])
+AC_PLUGIN([dns], [$with_libpcap], [DNS traffic analysis])
+AC_PLUGIN([dpdkevents], [$plugin_dpdkevents], [Events from DPDK])
+AC_PLUGIN([dpdkstat], [$plugin_dpdkstat], [Stats from DPDK])
+AC_PLUGIN([drbd], [$plugin_drbd], [DRBD statistics])
+AC_PLUGIN([email], [yes], [EMail statistics])
+AC_PLUGIN([entropy], [$plugin_entropy], [Entropy statistics])
+AC_PLUGIN([ethstat], [$plugin_ethstat], [Stats from NIC driver])
+AC_PLUGIN([exec], [yes], [Execution of external programs])
+AC_PLUGIN([fhcount], [$plugin_fhcount], [File handles statistics])
+AC_PLUGIN([filecount], [yes], [Count files in directories])
+AC_PLUGIN([fscache], [$plugin_fscache], [fscache statistics])
+AC_PLUGIN([gmond], [$with_libganglia], [Ganglia plugin])
+AC_PLUGIN([gps], [$plugin_gps], [GPS plugin])
+AC_PLUGIN([gpu_nvidia], [$with_cuda], [NVIDIA GPU plugin])
+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([ipmi], [$plugin_ipmi], [IPMI sensor statistics])
+AC_PLUGIN([iptables], [$with_libiptc], [IPTables rule counters])
+AC_PLUGIN([ipvs], [$plugin_ipvs], [IPVS connection statistics])
+AC_PLUGIN([irq], [$plugin_irq], [IRQ statistics])
+AC_PLUGIN([java], [$with_java], [Embed the Java Virtual Machine])
+AC_PLUGIN([load], [$plugin_load], [System load])
+AC_PLUGIN([log_logstash], [$plugin_log_logstash], [Logstash json_event compatible logging])
+AC_PLUGIN([logfile], [yes], [File logging plugin])
+AC_PLUGIN([lpar], [$with_perfstat], [AIX logical partitions statistics])
+AC_PLUGIN([lua], [$with_liblua], [Lua plugin])
+AC_PLUGIN([lvm], [$with_liblvm2app], [LVM statistics])
+AC_PLUGIN([madwifi], [$have_linux_wireless_h], [Madwifi wireless statistics])
+AC_PLUGIN([match_empty_counter], [yes], [The empty counter match])
+AC_PLUGIN([match_hashed], [yes], [The hashed match])
+AC_PLUGIN([match_regex], [yes], [The regex match])
+AC_PLUGIN([match_timediff], [yes], [The timediff match])
+AC_PLUGIN([match_value], [yes], [The value match])
+AC_PLUGIN([mbmon], [yes], [Query mbmond])
+AC_PLUGIN([mcelog], [$plugin_mcelog], [Machine Check Exceptions notifications])
+AC_PLUGIN([md], [$have_linux_raid_md_u_h], [md (Linux software RAID) devices])
+AC_PLUGIN([memcachec], [$with_libmemcached], [memcachec statistics])
+AC_PLUGIN([memcached], [yes], [memcached statistics])
+AC_PLUGIN([memory], [$plugin_memory], [Memory usage])
+AC_PLUGIN([mic], [$with_mic], [Intel Many Integrated Core stats])
+AC_PLUGIN([modbus], [$with_libmodbus], [Modbus plugin])
+AC_PLUGIN([mqtt], [$with_libmosquitto], [MQTT output plugin])
+AC_PLUGIN([multimeter], [$plugin_multimeter], [Read multimeter values])
+AC_PLUGIN([mysql], [$with_libmysql], [MySQL statistics])
+AC_PLUGIN([netapp], [$with_libnetapp], [NetApp plugin])
+AC_PLUGIN([netlink], [$with_libmnl], [Enhanced Linux network statistics])
+AC_PLUGIN([network], [yes], [Network communication plugin])
+AC_PLUGIN([nfs], [$plugin_nfs], [NFS statistics])
+AC_PLUGIN([nginx], [$with_libcurl], [nginx statistics])
+AC_PLUGIN([notify_desktop], [$with_libnotify], [Desktop notifications])
+AC_PLUGIN([notify_email], [$with_libesmtp], [Email notifier])
+AC_PLUGIN([notify_nagios], [yes], [Nagios notification plugin])
+AC_PLUGIN([ntpd], [yes], [NTPd statistics])
+AC_PLUGIN([numa], [$plugin_numa], [NUMA virtual memory statistics])
+AC_PLUGIN([nut], [$with_libupsclient], [Network UPS tools statistics])
+AC_PLUGIN([olsrd], [yes], [olsrd statistics])
+AC_PLUGIN([onewire], [$with_libowcapi], [OneWire sensor statistics])
+AC_PLUGIN([openldap], [$with_libldap], [OpenLDAP statistics])
+AC_PLUGIN([openvpn], [yes], [OpenVPN client statistics])
+AC_PLUGIN([oracle], [$with_oracle], [Oracle plugin])
+AC_PLUGIN([ovs_events], [$plugin_ovs_events], [OVS events plugin])
+AC_PLUGIN([ovs_stats], [$plugin_ovs_stats], [OVS statistics plugin])
+AC_PLUGIN([pcie_errors], [$plugin_pcie_errors], [PCIe errors plugin])
+AC_PLUGIN([perl], [$plugin_perl], [Embed a Perl interpreter])
+AC_PLUGIN([pf], [$have_net_pfvar_h], [BSD packet filter (PF) statistics])
# FIXME: Check for libevent, too.
-AC_PLUGIN([pinba], [$plugin_pinba], [Pinba statistics])
-AC_PLUGIN([ping], [$with_liboping], [Network latency statistics])
-AC_PLUGIN([postgresql], [$with_libpq], [PostgreSQL database statistics])
-AC_PLUGIN([powerdns], [yes], [PowerDNS statistics])
-AC_PLUGIN([processes], [$plugin_processes], [Process statistics])
-AC_PLUGIN([protocols], [$plugin_protocols], [Protocol (IP, TCP, ...) statistics])
-AC_PLUGIN([python], [$plugin_python], [Embed a Python interpreter])
-AC_PLUGIN([redis], [$with_libhiredis], [Redis plugin])
-AC_PLUGIN([routeros], [$with_librouteros], [RouterOS plugin])
-AC_PLUGIN([rrdcached], [$librrd_rrdc_update], [RRDTool output plugin])
-AC_PLUGIN([rrdtool], [$with_librrd], [RRDTool output plugin])
-AC_PLUGIN([sensors], [$with_libsensors], [lm_sensors statistics])
-AC_PLUGIN([serial], [$plugin_serial], [serial port traffic])
-AC_PLUGIN([sigrok], [$with_libsigrok], [sigrok acquisition sources])
-AC_PLUGIN([smart], [$plugin_smart], [SMART statistics])
-AC_PLUGIN([snmp], [$with_libnetsnmp], [SNMP querying plugin])
-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_PLUGIN([tail_csv], [yes], [Parsing of CSV files])
-AC_PLUGIN([tape], [$plugin_tape], [Tape drive statistics])
-AC_PLUGIN([target_notification], [yes], [The notification target])
-AC_PLUGIN([target_replace], [yes], [The replace target])
-AC_PLUGIN([target_scale], [yes], [The scale target])
-AC_PLUGIN([target_set], [yes], [The set target])
-AC_PLUGIN([target_v5upgrade], [yes], [The v5upgrade target])
-AC_PLUGIN([tcpconns], [$plugin_tcpconns], [TCP connection statistics])
-AC_PLUGIN([teamspeak2], [yes], [TeamSpeak2 server statistics])
-AC_PLUGIN([ted], [$plugin_ted], [Read The Energy Detective values])
-AC_PLUGIN([thermal], [$plugin_thermal], [Linux ACPI thermal zone statistics])
-AC_PLUGIN([threshold], [yes], [Threshold checking plugin])
-AC_PLUGIN([tokyotyrant], [$with_libtokyotyrant], [TokyoTyrant database statistics])
-AC_PLUGIN([turbostat], [$plugin_turbostat], [Advanced statistic on Intel cpu states])
-AC_PLUGIN([unixsock], [yes], [Unixsock communication plugin])
-AC_PLUGIN([uptime], [$plugin_uptime], [Uptime statistics])
-AC_PLUGIN([users], [$plugin_users], [User statistics])
-AC_PLUGIN([uuid], [yes], [UUID as hostname plugin])
-AC_PLUGIN([varnish], [$with_libvarnish], [Varnish cache statistics])
-AC_PLUGIN([virt], [$plugin_virt], [Virtual machine statistics])
-AC_PLUGIN([vmem], [$plugin_vmem], [Virtual memory statistics])
-AC_PLUGIN([vserver], [$plugin_vserver], [Linux VServer statistics])
-AC_PLUGIN([wireless], [$plugin_wireless], [Wireless statistics])
-AC_PLUGIN([write_graphite], [yes], [Graphite / Carbon output plugin])
-AC_PLUGIN([write_http], [$with_libcurl], [HTTP output plugin])
-AC_PLUGIN([write_kafka], [$with_librdkafka], [Kafka output plugin])
-AC_PLUGIN([write_log], [yes], [Log output plugin])
-AC_PLUGIN([write_mongodb], [$with_libmongoc], [MongoDB output plugin])
-AC_PLUGIN([write_prometheus], [$plugin_write_prometheus], [Prometheus write plugin])
-AC_PLUGIN([write_redis], [$with_libhiredis], [Redis output plugin])
-AC_PLUGIN([write_riemann], [$with_libriemann_client], [Riemann output plugin])
-AC_PLUGIN([write_sensu], [yes], [Sensu output plugin])
-AC_PLUGIN([write_tsdb], [yes], [TSDB output plugin])
-AC_PLUGIN([xencpu], [$plugin_xencpu], [Xen Host CPU usage])
-AC_PLUGIN([xmms], [$with_libxmms], [XMMS statistics])
-AC_PLUGIN([zfs_arc], [$plugin_zfs_arc], [ZFS ARC statistics])
-AC_PLUGIN([zone], [$plugin_zone], [Solaris container statistics])
-AC_PLUGIN([zookeeper], [yes], [Zookeeper statistics])
+AC_PLUGIN([pinba], [$plugin_pinba], [Pinba statistics])
+AC_PLUGIN([ping], [$with_liboping], [Network latency statistics])
+AC_PLUGIN([postgresql], [$with_libpq], [PostgreSQL database statistics])
+AC_PLUGIN([powerdns], [yes], [PowerDNS statistics])
+AC_PLUGIN([processes], [$plugin_processes], [Process statistics])
+AC_PLUGIN([protocols], [$plugin_protocols], [Protocol (IP, TCP, ...) statistics])
+AC_PLUGIN([python], [$plugin_python], [Embed a Python interpreter])
+AC_PLUGIN([redis], [$with_libhiredis], [Redis plugin])
+AC_PLUGIN([routeros], [$with_librouteros], [RouterOS plugin])
+AC_PLUGIN([rrdcached], [$librrd_rrdc_update], [RRDTool output plugin])
+AC_PLUGIN([rrdtool], [$with_librrd], [RRDTool output plugin])
+AC_PLUGIN([sensors], [$with_libsensors], [lm_sensors statistics])
+AC_PLUGIN([serial], [$plugin_serial], [serial port traffic])
+AC_PLUGIN([sigrok], [$with_libsigrok], [sigrok acquisition sources])
+AC_PLUGIN([smart], [$plugin_smart], [SMART statistics])
+AC_PLUGIN([snmp], [$with_libnetsnmp], [SNMP querying plugin])
+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_PLUGIN([tail_csv], [yes], [Parsing of CSV files])
+AC_PLUGIN([tape], [$plugin_tape], [Tape drive statistics])
+AC_PLUGIN([target_notification], [yes], [The notification target])
+AC_PLUGIN([target_replace], [yes], [The replace target])
+AC_PLUGIN([target_scale], [yes], [The scale target])
+AC_PLUGIN([target_set], [yes], [The set target])
+AC_PLUGIN([target_v5upgrade], [yes], [The v5upgrade target])
+AC_PLUGIN([tcpconns], [$plugin_tcpconns], [TCP connection statistics])
+AC_PLUGIN([teamspeak2], [yes], [TeamSpeak2 server statistics])
+AC_PLUGIN([ted], [$plugin_ted], [Read The Energy Detective values])
+AC_PLUGIN([thermal], [$plugin_thermal], [Linux ACPI thermal zone statistics])
+AC_PLUGIN([threshold], [yes], [Threshold checking plugin])
+AC_PLUGIN([tokyotyrant], [$with_libtokyotyrant], [TokyoTyrant database statistics])
+AC_PLUGIN([turbostat], [$plugin_turbostat], [Advanced statistic on Intel cpu states])
+AC_PLUGIN([unixsock], [yes], [Unixsock communication plugin])
+AC_PLUGIN([uptime], [$plugin_uptime], [Uptime statistics])
+AC_PLUGIN([users], [$plugin_users], [User statistics])
+AC_PLUGIN([uuid], [yes], [UUID as hostname plugin])
+AC_PLUGIN([varnish], [$with_libvarnish], [Varnish cache statistics])
+AC_PLUGIN([virt], [$plugin_virt], [Virtual machine statistics])
+AC_PLUGIN([vmem], [$plugin_vmem], [Virtual memory statistics])
+AC_PLUGIN([vserver], [$plugin_vserver], [Linux VServer statistics])
+AC_PLUGIN([wireless], [$plugin_wireless], [Wireless statistics])
+AC_PLUGIN([write_graphite], [yes], [Graphite / Carbon output plugin])
+AC_PLUGIN([write_http], [$with_libcurl], [HTTP output plugin])
+AC_PLUGIN([write_stackdriver], [$plugin_write_stackdriver], [Google Stackdriver Monitoring output plugin])
+AC_PLUGIN([write_kafka], [$with_librdkafka], [Kafka output plugin])
+AC_PLUGIN([write_log], [yes], [Log output plugin])
+AC_PLUGIN([write_mongodb], [$with_libmongoc], [MongoDB output plugin])
+AC_PLUGIN([write_prometheus], [$plugin_write_prometheus], [Prometheus write plugin])
+AC_PLUGIN([write_redis], [$with_libhiredis], [Redis output plugin])
+AC_PLUGIN([write_riemann], [$with_libriemann_client], [Riemann output plugin])
+AC_PLUGIN([write_sensu], [yes], [Sensu output plugin])
+AC_PLUGIN([write_tsdb], [yes], [TSDB output plugin])
+AC_PLUGIN([xencpu], [$plugin_xencpu], [Xen Host CPU usage])
+AC_PLUGIN([xmms], [$with_libxmms], [XMMS statistics])
+AC_PLUGIN([zfs_arc], [$plugin_zfs_arc], [ZFS ARC statistics])
+AC_PLUGIN([zone], [$plugin_zone], [Solaris container statistics])
+AC_PLUGIN([zookeeper], [yes], [Zookeeper statistics])
dnl Default configuration file
# Load either syslog or logfile
AC_MSG_RESULT([ YFLAGS . . . . . . . $YFLAGS])
AC_MSG_RESULT()
AC_MSG_RESULT([ Libraries:])
+AC_MSG_RESULT([ cuda . . . . . . . . $with_cuda])
AC_MSG_RESULT([ intel mic . . . . . . $with_mic])
AC_MSG_RESULT([ libaquaero5 . . . . . $with_libaquaero5])
AC_MSG_RESULT([ libatasmart . . . . . $with_libatasmart])
AC_MSG_RESULT([ librrd . . . . . . . $with_librrd])
AC_MSG_RESULT([ libsensors . . . . . $with_libsensors])
AC_MSG_RESULT([ libsigrok . . . . . $with_libsigrok])
+AC_MSG_RESULT([ libssl . . . . . . . $with_libssl])
AC_MSG_RESULT([ libstatgrab . . . . . $with_libstatgrab])
AC_MSG_RESULT([ libtokyotyrant . . . $with_libtokyotyrant])
AC_MSG_RESULT([ libudev . . . . . . . $with_libudev])
AC_MSG_RESULT([ fscache . . . . . . . $enable_fscache])
AC_MSG_RESULT([ gmond . . . . . . . . $enable_gmond])
AC_MSG_RESULT([ gps . . . . . . . . . $enable_gps])
+AC_MSG_RESULT([ gpu_nvidia . . . . . $enable_gpu_nvidia])
AC_MSG_RESULT([ grpc . . . . . . . . $enable_grpc])
AC_MSG_RESULT([ hddtemp . . . . . . . $enable_hddtemp])
AC_MSG_RESULT([ hugepages . . . . . . $enable_hugepages])
AC_MSG_RESULT([ oracle . . . . . . . $enable_oracle])
AC_MSG_RESULT([ ovs_events . . . . . $enable_ovs_events])
AC_MSG_RESULT([ ovs_stats . . . . . . $enable_ovs_stats])
+AC_MSG_RESULT([ pcie_errors . . . . . $enable_pcie_errors])
AC_MSG_RESULT([ perl . . . . . . . . $enable_perl])
AC_MSG_RESULT([ pf . . . . . . . . . $enable_pf])
AC_MSG_RESULT([ pinba . . . . . . . . $enable_pinba])
AC_MSG_RESULT([ write_redis . . . . . $enable_write_redis])
AC_MSG_RESULT([ write_riemann . . . . $enable_write_riemann])
AC_MSG_RESULT([ write_sensu . . . . . $enable_write_sensu])
+AC_MSG_RESULT([ write_stackdriver . . $enable_write_stackdriver])
AC_MSG_RESULT([ write_tsdb . . . . . $enable_write_tsdb])
AC_MSG_RESULT([ xencpu . . . . . . . $enable_xencpu])
AC_MSG_RESULT([ xmms . . . . . . . . $enable_xmms])
fi
# vim: set fdm=marker sw=2 sts=2 ts=2 et :
+
-APT::Install-Recommends "0";
+APT::Install-Recommends "1";
APT::Install-Suggests "0";
APT::Get::Assume-Yes "1";
APT::Get::AutomaticRemove "1";
+/**
+ * collectd - contrib/docker/rootfs_prefix/rootfs_prefix.c
+ * Copyright (C) 2016-2018 Marc Fournier
+ * Copyright (C) 2016-2018 Ruben Kerkhof
+ *
+ * MIT License:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Marc Fournier <marc.fournier at camptocamp.com>
+ * Ruben Kerkhof <ruben at rubenkerkhof.com>
+ **/
+
#define _GNU_SOURCE
#include <dirent.h>
--- /dev/null
+# Code Review Comments
+
+This is a collection of frequent code review comments, collected here for
+reference and discussed in more depth than a typical code review would allow.
+
+The intended use for this document is to point to it from a code review to make
+a point quickly while still providing the contributor with enough information
+to resolve the issue. For example, a good review comment would be:
+
+![Please initialize variables at declaration. Link to comment.](review_comments_example.png)
+
+A link to each paragraph is provided at the beginning for easy copy'n'pasting.
+
+## Initialize variables
+
+→ [https://collectd.org/review-comments#initialize-variables](https://collectd.org/review-comments#initialize-variables)
+
+Initialize variables when declaring them. By default, C does not initialize
+local variables when they are defined. If a code path ends up reading the
+variable before it is initialized, for example because a loop body is never
+executed, it will read random data, causing undefined behavior. Worst case,
+pointers will point to random memory causing a segmentation fault.
+
+**Examples:**
+
+```c
+/* Initialize scalar with to literal: */
+int status = 0;
+
+/* Initialize pointer with function call: */
+char *buffer = calloc(1, buffer_size);
+
+/* Initialize struct with struct initializer: */
+struct addrinfo ai = {
+ .ai_family = AF_UNSPEC,
+ .ai_flags = AI_ADDRCONFIG,
+ .ai_socktype = SOCK_STREAM,
+};
+
+/* Initialize struct with zero: */
+struct stat statbuf = {0};
+```
+
+In the last example, `{0}` is the universal struct initializer that, in theory,
+should be able to zero-initialize any struct. In practise, however, some
+compilers don't implement this correctly and will get confused when the first
+member is a struct or a union. Our *continuous integration* framework will
+catch these cases.
+
+## Define variables on first use
+
+→ [https://collectd.org/review-comments#define-variables-on-first-use](https://collectd.org/review-comments#define-variables-on-first-use)
+
+Local variables should be defined when they are first used, ideally when they
+can be initialized. For example:
+
+```c
+struct foo *f = calloc(1, sizeof(*f));
+if (f == NULL) {
+ return ENOMEM;
+}
+
+/* GOOD: status defiened and initialized late. */
+int status = function_call(f);
+```
+
+Sometimes variables are initialized by passing a pointer to them to a function.
+In that case define them as close to the function call as you can and
+zero-initialize them. The function may only partially initialize a struct or
+not initialize a struct at all in some circumstances.
+
+**Example:**
+
+```c
+char const *path = determine_path();
+
+struct stat s = {0};
+int status = stat(path, &s);
+```
+
+Old C standards (C89 and ealier) required variables to be defined at the
+beginning of a scope block. The following *bad* style is still frequently
+found:
+
+```c
+/* BAD: local variables defined at beginning of block. */
+struct foo *f;
+int status;
+
+f = calloc(1, sizeof(*f));
+if (f == NULL) {
+ return ENOMEM;
+}
+
+status = function_call(f);
+```
--- /dev/null
+Subproject commit 2f8140bc8ce5501e31dcc665b42b5df64f84c20c
}
cd_message_t *cdm = malloc(sizeof(*cdm));
- if (cdm == NULL ){
+ if (cdm == NULL) {
ERROR("amqp1 plugin: notify failed");
return -1;
}
format_json_initialize((char *)cdm->mbuf.start, &bfill, &bfree);
format_json_value_list((char *)cdm->mbuf.start, &bfill, &bfree, ds, vl,
instance->store_rates);
- status= format_json_finalize((char *)cdm->mbuf.start, &bfill, &bfree);
+ status = format_json_finalize((char *)cdm->mbuf.start, &bfill, &bfree);
if (status != 0) {
- ERROR("amqp1 plugin: format_json_finalize failed with status %i.", status);
+ ERROR("amqp1 plugin: format_json_finalize failed with status %i.",
+ status);
cd_message_free(cdm);
- return(status);
+ return status;
}
cdm->mbuf.size = strlen(cdm->mbuf.start);
if (cdm->mbuf.size >= BUFSIZE) {
- ERROR("amqp1 plugin: format graphite failed");
+ ERROR("amqp1 plugin: format json failed");
cd_message_free(cdm);
return -1;
}
#include <arpa/inet.h> /* ntohs/ntohl */
#endif
+/* AIX doesn't have MSG_DONTWAIT */
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT MSG_NONBLOCK
+#endif
+
#define CONFIG_KEY_HOST "Host"
#define CONFIG_KEY_PORT "Port"
#define CONFIG_KEY_TIMEOUT "Timeout"
}
}
+static void chrony_flush_recv_queue(void) {
+ char buf[1];
+
+ if (g_chrony_is_connected) {
+ while (recv(g_chrony_socket, buf, sizeof(buf), MSG_DONTWAIT) > 0)
+ ;
+ }
+}
+
static int chrony_query(const int p_command, tChrony_Request *p_req,
tChrony_Response *p_resp, size_t *p_resp_size) {
/* Check connection. We simply perform one try as collectd already handles
g_chrony_seq_is_initialized = 1;
}
+ /* Ignore late responses that may have been received */
+ chrony_flush_recv_queue();
+
/* Get daemon stats */
rc = chrony_request_daemon_stats();
if (rc != CHRONY_RC_OK)
=back
+=item B<Import> I<Name>
+
+Imports the python script I<Name> and loads it into the collectd
+python process. If your python script is not found, be sure its
+directory exists in python's B<sys.path>. You can prepend to the
+B<sys.path> using the B<ModulePath> configuration option.
+
=item E<lt>B<Module> I<Name>E<gt> block
This block may be used to pass on configuration settings to a Python module.
#@BUILD_PLUGIN_ORACLE_TRUE@LoadPlugin oracle
#@BUILD_PLUGIN_OVS_EVENTS_TRUE@LoadPlugin ovs_events
#@BUILD_PLUGIN_OVS_STATS_TRUE@LoadPlugin ovs_stats
+#@BUILD_PLUGIN_PCIE_ERRORS_TRUE@LoadPlugin pcie_errors
#@BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl
#@BUILD_PLUGIN_PINBA_TRUE@LoadPlugin pinba
#@BUILD_PLUGIN_PING_TRUE@LoadPlugin ping
#@BUILD_PLUGIN_WRITE_REDIS_TRUE@LoadPlugin write_redis
#@BUILD_PLUGIN_WRITE_RIEMANN_TRUE@LoadPlugin write_riemann
#@BUILD_PLUGIN_WRITE_SENSU_TRUE@LoadPlugin write_sensu
+#@BUILD_PLUGIN_WRITE_STACKDRIVER_TRUE@LoadPlugin write_stackdriver
#@BUILD_PLUGIN_WRITE_TSDB_TRUE@LoadPlugin write_tsdb
#@BUILD_PLUGIN_XENCPU_TRUE@LoadPlugin xencpu
#@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms
# PauseConnect 5
#</Plugin>
+#<Plugin gpu_nvidia>
+# GPUIndex 0
+# GPUIndex 2
+# IgnoreSelected false
+#</Plugin>
+
#<Plugin grpc>
# <Server "example.com" "50051">
# EnableSSL true
# Bridges "br0" "br_ext"
#</Plugin>
+#<Plugin pcie_errors>
+# Source "sysfs"
+# ReportMasked false
+# PersistentNotifications false
+#</Plugin>
+
#<Plugin perl>
# IncludeDir "/my/include/path"
# BaseName "Collectd::Plugins"
# <Node example>
# Host "redis.example.com"
# Port "6379"
+# #Socket "/var/run/redis/redis.sock"
# Timeout 2000
# <Query "LLEN myqueue">
# #Database 0
# CollectMemory true
# CollectDF true
# CollectDisk true
+# CollectHealth true
# </Router>
#</Plugin>
# Attribute "foo" "bar"
#</Plugin>
+#<Plugin write_stackdriver>
+# Project "stackdriver-account"
+# CredentialFile "/path/to/gcp-project-id-12345.json"
+# Email "123456789012@developer.gserviceaccount.com"
+# <Resource "global">
+# Label "project_id" "gcp-project-id"
+# </Resource>
+# Url "https://monitoring.googleapis.com/v3"
+#</Plugin>
+
#<Plugin write_tsdb>
# <Node>
# Host "localhost"
sure B<cpufreqd> (L<http://cpufreqd.sourceforge.net/>) or a similar tool is
installed and an "cpu governor" (that's a kernel module) is loaded.
+If the system has the I<cpufreq-stats> kernel module loaded, this plugin reports
+the rate of p-state (cpu frequency) transitions and the percentage of time spent
+in each p-state.
+
=head2 Plugin C<cpusleep>
This plugin doesn't have any options. It reads CLOCK_BOOTTIME and
B<MeasureResponseCode> options are set to B<true>, B<Match> blocks are
optional.
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+URL. By default the global B<Interval> setting will be used.
+
=item B<Timeout> I<Milliseconds>
The B<Timeout> option sets the overall timeout for HTTP requests to B<URL>, in
May be overridden by B<PluginInstanceFrom> option inside B<XPath> blocks.
Defaults to an empty string (no plugin instance).
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+URL. By default the global B<Interval> setting will be used.
+
=item B<Namespace> I<Prefix> I<URL>
If an XPath expression references namespaces, they must be specified
=back
+=head2 Plugin C<gpu_nvidia>
+
+Efficiently collects various statistics from the system's NVIDIA GPUs using the
+NVML library. Currently collected are fan speed, core temperature, percent
+load, percent memory used, compute and memory frequencies, and power
+consumption.
+
+=over 4
+
+=item B<GPUIndex>
+
+If one or more of these options is specified, only GPUs at that index (as
+determined by nvidia-utils through I<nvidia-smi>) have statistics collected.
+If no instance of this option is specified, all GPUs are monitored.
+
+=item B<IgnoreSelected>
+
+If set to true, all detected GPUs B<except> the ones at indices specified by
+B<GPUIndex> entries are collected. For greater clarity, setting IgnoreSelected
+without any GPUIndex directives will result in B<no> statistics being
+collected.
+
+=back
+
=head2 Plugin C<grpc>
The I<grpc> plugin provides an RPC interface to submit values to or query
=back
+=head2 Plugin C<pcie_errors>
+
+The I<pcie_errors> plugin collects PCI Express errors from Device Status in Capability
+structure and from Advanced Error Reporting Extended Capability where available.
+At every read it polls config space of PCI Express devices and dispatches
+notification for every error that is set. It checks for new errors at every read.
+The device is indicated in plugin_instance according to format "domain:bus:dev.fn".
+Errors are divided into categories indicated by type_instance: "correctable", and
+for uncorrectable errors "non_fatal" or "fatal".
+Fatal errors are reported as I<NOTIF_FAILURE> and all others as I<NOTIF_WARNING>.
+
+B<Synopsis:>
+
+ <Plugin "pcie_errors">
+ Source "sysfs"
+ AccessDir "/sys/bus/pci"
+ ReportMasked false
+ PersistentNotifications false
+ </Plugin>
+
+B<Options:>
+
+=over 4
+
+=item B<Source> B<sysfs>|B<proc>
+
+Use B<sysfs> or B<proc> to read data from /sysfs or /proc.
+The default value is B<sysfs>.
+
+=item B<AccessDir> I<dir>
+
+Directory used to access device config space. It is optional and defaults to
+/sys/bus/pci for B<sysfs> and to /proc/bus/pci for B<proc>.
+
+=item B<ReportMasked> B<false>|B<true>
+
+If true plugin will notify about errors that are set to masked in Error Mask register.
+Such errors are not reported to the PCI Express Root Complex. Defaults to B<false>.
+
+=item B<PersistentNotifications> B<false>|B<true>
+
+If false plugin will dispatch notification only on set/clear of error.
+The ones already reported will be ignored. Defaults to B<false>.
+
+=back
+
=head2 Plugin C<perl>
This plugin embeds a Perl-interpreter into collectd and provides an interface
CollectRegistrationTable true
CollectDF true
CollectDisk true
+ CollectHealth true
</Router>
</Plugin>
When enabled, the number of sectors written and bad blocks will be collected.
Defaults to B<false>.
+=item B<CollectHealth> B<true>|B<false>
+
+When enabled, the health statistics will be collected. This includes the
+voltage and temperature on supported hardware.
+Defaults to B<false>.
+
=back
=head2 Plugin C<redis>
<Node "example">
Host "localhost"
Port "6379"
+ #Socket "/var/run/redis/redis.sock"
Timeout 2000
ReportCommandStats false
ReportCpuUsage true
connections. Either a service name of a port number may be given. Please note
that numerical port numbers must be given as a string, too.
+=item B<Socket> I<Path>
+
+Connect to Redis using the UNIX domain socket at I<Path>. If this
+setting is given, the B<Hostname> and B<Port> settings are ignored.
+
=item B<Password> I<Password>
Use I<Password> to authenticate when connecting to I<Redis>.
<Metric "snort-dropped">
Type "percent"
Instance "dropped"
- Index 1
+ ValueFrom 1
</Metric>
<File "/var/log/snort/snort.stats">
Plugin "snortstats"
Instance "eth0"
Interval 600
Collect "snort-dropped"
+ #TimeFrom 0
</File>
</Plugin>
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>.
+=item B<RestoreAffinityPolicy> I<AllCPUs>|I<Restore>
+
+Reading data from CPU has side-effect: collectd process's CPU affinity mask
+changes. After reading data is completed, affinity mask needs to be restored.
+This option allows to set restore policy.
+
+B<AllCPUs> (the default): Restore the affinity by setting affinity to any/all
+CPUs.
+
+B<Restore>: Save affinity using sched_getaffinity() before reading data and
+restore it after.
+
+On some systems, sched_getaffinity() will fail due to inconsistency of the CPU
+set size between userspace and kernel. In these cases plugin will detect the
+unsuccessful call and fail with an error, preventing data collection.
+Most of configurations does not need to save affinity as Collectd process is
+allowed to run on any/all available CPUs.
+
+If you need to save and restore affinity and get errors like 'Unable to save
+the CPU affinity', setting 'possible_cpus' kernel boot option may also help.
+
+See following links for details:
+
+L<https://github.com/collectd/collectd/issues/1593>
+L<https://sourceware.org/bugzilla/show_bug.cgi?id=15630>
+L<https://bugzilla.kernel.org/show_bug.cgi?id=151821>
+
=back
=head2 Plugin C<unixsock>
Only I<Connection> is required.
+Consider the following example config:
+
+ <Plugin "virt">
+ Connection "qemu:///system"
+ HostnameFormat "hostname"
+ InterfaceFormat "address"
+ PluginInstanceFormat "name"
+ </Plugin>
+
+It will generate the following values:
+
+ node42.example.com/virt-instance-0006f26c/disk_octets-vda
+ node42.example.com/virt-instance-0006f26c/disk_ops-vda
+ node42.example.com/virt-instance-0006f26c/if_dropped-ca:fe:ca:fe:ca:fe
+ node42.example.com/virt-instance-0006f26c/if_errors-ca:fe:ca:fe:ca:fe
+ node42.example.com/virt-instance-0006f26c/if_octets-ca:fe:ca:fe:ca:fe
+ node42.example.com/virt-instance-0006f26c/if_packets-ca:fe:ca:fe:ca:fe
+ node42.example.com/virt-instance-0006f26c/memory-actual_balloon
+ node42.example.com/virt-instance-0006f26c/memory-available
+ node42.example.com/virt-instance-0006f26c/memory-last_update
+ node42.example.com/virt-instance-0006f26c/memory-major_fault
+ node42.example.com/virt-instance-0006f26c/memory-minor_fault
+ node42.example.com/virt-instance-0006f26c/memory-rss
+ node42.example.com/virt-instance-0006f26c/memory-swap_in
+ node42.example.com/virt-instance-0006f26c/memory-swap_out
+ node42.example.com/virt-instance-0006f26c/memory-total
+ node42.example.com/virt-instance-0006f26c/memory-unused
+ node42.example.com/virt-instance-0006f26c/memory-usable
+ node42.example.com/virt-instance-0006f26c/virt_cpu_total
+ node42.example.com/virt-instance-0006f26c/virt_vcpu-0
+
+You can get information on the metric's units from the online libvirt documentation.
+For instance, I<virt_cpu_total> is in nanoseconds.
+
=over 4
=item B<Connection> I<uri>
same guest across migrations.
B<hostname> means to use the global B<Hostname> setting, which is probably not
-useful on its own because all guests will appear to have the same name.
+useful on its own because all guests will appear to have the same name. This is
+useful in conjunction with B<PluginInstanceFormat> though.
You can also specify combinations of these fields. For example B<name uuid>
means to concatenate the guest name and UUID (with a literal colon character
Protocol "tcp"
LogSendErrors true
Prefix "collectd"
+ UseTags false
</Node>
</Plugin>
=item B<Prefix> I<String>
-When set, I<String> is added in front of the host name. Dots and whitespace are
-I<not> escaped in this string (see B<EscapeCharacter> below).
+When B<UseTags> is I<false>, B<Prefix> value is added in front of the host name.
+When B<UseTags> is I<true>, B<Prefix> value is added in front of series name.
+
+Dots and whitespace are I<not> escaped in this string (see B<EscapeCharacter>
+below).
=item B<Postfix> I<String>
-When set, I<String> is appended to the host name. Dots and whitespace are
-I<not> escaped in this string (see B<EscapeCharacter> below).
+When B<UseTags> is I<false>, B<Postfix> value appended to the host name.
+When B<UseTags> is I<true>, B<Postgix> value appended to the end of series name
+(before the first ; that separates the name from the tags).
+
+Dots and whitespace are I<not> escaped in this string (see B<EscapeCharacter>
+below).
=item B<EscapeCharacter> I<Char>
default), the plugin and plugin instance (and likewise the type and type
instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
+Option value is not used when B<UseTags> is I<true>.
+
=item B<AlwaysAppendDS> B<false>|B<true>
If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
I<EscapeCharacter>. Otherwise, if set to B<true>, the C<.> (dot) character
is preserved, i.e. passed through.
+Option value is not used when B<UseTags> is I<true>.
+
=item B<DropDuplicateFields> B<false>|B<true>
If set to B<true>, detect and remove duplicate components in Graphite metric
names. For example, the metric name C<host.load.load.shortterm> will
be shortened to C<host.load.shortterm>.
+=item B<UseTags> B<false>|B<true>
+
+If set to B<true>, Graphite metric names will be generated as tagged series.
+This allows for much more flexibility than the traditional hierarchical layout.
+
+Example:
+C<test.single;host=example.com;plugin=test;plugin_instance=foo;type=single;type_instance=bar>
+
+You can use B<Postfix> option to add more tags by specifying it like
+C<;tag1=value1;tag2=value2>. Note what tagging support was added since Graphite
+version 1.1.x.
+
+If set to B<true>, the B<SeparateInstances> and B<PreserveSeparator> settings
+are not used.
+
+Default value: B<false>.
+
=back
=head2 Plugin C<write_log>
=over 4
+=item B<Host> I<Host>
+
+Bind to the hostname / address I<Host>. By default, the plugin will bind to the
+"any" address, i.e. accept packets sent to any of the hosts addresses.
+
+This option is supported only for libmicrohttpd newer than 0.9.0.
+
=item B<Port> I<Port>
Port the embedded webserver should listen on. Defaults to B<9103>.
=item B<GraphitePrefix> (B<Format>=I<Graphite> only)
A prefix can be added in the metric name when outputting in the I<Graphite>
-format. It's added before the I<Host> name.
+format.
+
+When B<GraphiteUseTags> is I<false>, prefix is added before the I<Host> name.
Metric name will be
C<E<lt>prefixE<gt>E<lt>hostE<gt>E<lt>postfixE<gt>E<lt>pluginE<gt>E<lt>typeE<gt>E<lt>nameE<gt>>
+When B<GraphiteUseTags> is I<true>, prefix is added in front of series name.
+
=item B<GraphitePostfix> (B<Format>=I<Graphite> only)
A postfix can be added in the metric name when outputting in the I<Graphite>
-format. It's added after the I<Host> name.
+format.
+
+When B<GraphiteUseTags> is I<false>, postfix is added after the I<Host> name.
Metric name will be
C<E<lt>prefixE<gt>E<lt>hostE<gt>E<lt>postfixE<gt>E<lt>pluginE<gt>E<lt>typeE<gt>E<lt>nameE<gt>>
+When B<GraphiteUseTags> is I<true>, prefix value appended to the end of series
+name (before the first ; that separates the name from the tags).
+
=item B<GraphiteEscapeChar> (B<Format>=I<Graphite> only)
Specify a character to replace dots (.) in the host part of the metric name.
default), the plugin and plugin instance (and likewise the type and type
instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
+Option value is not used when B<GraphiteUseTags> is I<true>.
+
=item B<GraphiteAlwaysAppendDS> B<true>|B<false>
If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
is preserved, i.e. passed through.
+Option value is not used when B<GraphiteUseTags> is I<true>.
+
+=item B<GraphiteUseTags> B<false>|B<true>
+
+If set to B<true> Graphite metric names will be generated as tagged series.
+
+Default value: B<false>.
+
=item B<StoreRates> B<true>|B<false>
If set to B<true> (the default), convert counter values to rates. If set to
=back
+=head2 Plugin C<write_stackdriver>
+
+The C<write_stackdriver> plugin writes metrics to the
+I<Google Stackdriver Monitoring> service.
+
+This plugin supports two authentication methods: When configured, credentials
+are read from the JSON credentials file specified with B<CredentialFile>.
+Alternatively, when running on
+I<Google Compute Engine> (GCE), an I<OAuth> token is retrieved from the
+I<metadata server> and used to authenticate to GCM.
+
+B<Synopsis:>
+
+ <Plugin write_stackdriver>
+ CredentialFile "/path/to/service_account.json"
+ <Resource "global">
+ Label "project_id" "monitored_project"
+ </Resource>
+ </Plugin>
+
+=over 4
+
+=item B<CredentialFile> I<file>
+
+Path to a JSON credentials file holding the credentials for a GCP service
+account.
+
+If B<CredentialFile> is not specified, the plugin uses I<Application Default
+Credentials>. That means which credentials are used depends on the environment:
+
+=over 4
+
+=item
+
+The environment variable C<GOOGLE_APPLICATION_CREDENTIALS> is checked. If this
+variable is specified it should point to a JSON file that defines the
+credentials.
+
+=item
+
+The path C<${HOME}/.config/gcloud/application_default_credentials.json> is
+checked. This where credentials used by the I<gcloud> command line utility are
+stored. You can use C<gcloud auth application-default login> to create these
+credentials.
+
+Please note that these credentials are often of your personal account, not a
+service account, and are therefore unfit to be used in a production
+environment.
+
+=item
+
+When running on GCE, the built-in service account associated with the virtual
+machine instance is used.
+See also the B<Email> option below.
+
+=back
+
+=item B<Project> I<Project>
+
+The I<Project ID> or the I<Project Number> of the I<Stackdriver Account>. The
+I<Project ID> is a string identifying the GCP project, which you can chose
+freely when creating a new project. The I<Project Number> is a 12-digit decimal
+number. You can look up both on the I<Developer Console>.
+
+This setting is optional. If not set, the project ID is read from the
+credentials file or determined from the GCE's metadata service.
+
+=item B<Email> I<Email> (GCE only)
+
+Choses the GCE I<Service Account> used for authentication.
+
+Each GCE instance has a C<default> I<Service Account> but may also be
+associated with additional I<Service Accounts>. This is often used to restrict
+the permissions of services running on the GCE instance to the required
+minimum. The I<write_stackdriver plugin> requires the
+C<https://www.googleapis.com/auth/monitoring> scope. When multiple I<Service
+Accounts> are available, this option selects which one is used by
+I<write_stackdriver plugin>.
+
+=item B<Resource> I<ResourceType>
+
+Configures the I<Monitored Resource> to use when storing metrics.
+More information on I<Monitored Resources> and I<Monitored Resource Types> are
+available at L<https://cloud.google.com/monitoring/api/resources>.
+
+This block takes one string argument, the I<ResourceType>. Inside the block are
+one or more B<Label> options which configure the resource labels.
+
+This block is optional. The default value depends on the runtime environment:
+on GCE, the C<gce_instance> resource type is used, otherwise the C<global>
+resource type ist used:
+
+=over 4
+
+=item
+
+B<On GCE>, defaults to the equivalent of this config:
+
+ <Resource "gce_instance">
+ Label "project_id" "<project_id>"
+ Label "instance_id" "<instance_id>"
+ Label "zone" "<zone>"
+ </Resource>
+
+The values for I<project_id>, I<instance_id> and I<zone> are read from the GCE
+metadata service.
+
+=item
+
+B<Elsewhere>, i.e. not on GCE, defaults to the equivalent of this config:
+
+ <Resource "global">
+ Label "project_id" "<Project>"
+ </Resource>
+
+Where I<Project> refers to the value of the B<Project> option or the project ID
+inferred from the B<CredentialFile>.
+
+=back
+
+=item B<Url> I<Url>
+
+URL of the I<Stackdriver Monitoring> API. Defaults to
+C<https://monitoring.googleapis.com/v3>.
+
+=back
+
=head2 Plugin C<xencpu>
This plugin collects metrics of hardware CPU load for machine running Xen
#include "common.h"
#include "plugin.h"
+#define MAX_AVAIL_FREQS 20
+
static int num_cpu;
+struct cpu_data_t {
+ value_to_rate_state_t time_state[MAX_AVAIL_FREQS];
+} * cpu_data;
+
+/* Flags denoting capability of reporting CPU frequency statistics. */
+static bool report_p_stats = false;
+
+static void cpufreq_stats_init(void) {
+ cpu_data = calloc(num_cpu, sizeof(struct cpu_data_t));
+ if (cpu_data == NULL)
+ return;
+
+ report_p_stats = true;
+
+ /* Check for stats module and disable if not present. */
+ for (int i = 0; i < num_cpu; i++) {
+ char filename[PATH_MAX];
+
+ snprintf(filename, sizeof(filename),
+ "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", i);
+ if (access(filename, R_OK)) {
+ NOTICE("cpufreq plugin: File %s not exists or no access. P-State "
+ "statistics will not be reported. Check if `cpufreq-stats' kernel "
+ "module is loaded.",
+ filename);
+ report_p_stats = false;
+ break;
+ }
+
+ snprintf(filename, sizeof(filename),
+ "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", i);
+ if (access(filename, R_OK)) {
+ NOTICE("cpufreq plugin: File %s not exists or no access. P-State "
+ "statistics will not be reported. Check if `cpufreq-stats' kernel "
+ "module is loaded.",
+ filename);
+ report_p_stats = false;
+ break;
+ }
+ }
+ return;
+}
+
static int cpufreq_init(void) {
- int status;
- char filename[256];
+ char filename[PATH_MAX];
num_cpu = 0;
while (1) {
- status = snprintf(filename, sizeof(filename),
- "/sys/devices/system/cpu/cpu%d/cpufreq/"
- "scaling_cur_freq",
- num_cpu);
+ int status = snprintf(filename, sizeof(filename),
+ "/sys/devices/system/cpu/cpu%d/cpufreq/"
+ "scaling_cur_freq",
+ num_cpu);
if ((status < 1) || ((unsigned int)status >= sizeof(filename)))
break;
}
INFO("cpufreq plugin: Found %d CPU%s", num_cpu, (num_cpu == 1) ? "" : "s");
+ cpufreq_stats_init();
if (num_cpu == 0)
plugin_unregister_read("cpufreq");
return 0;
} /* int cpufreq_init */
-static void cpufreq_submit(int cpu_num, value_t value) {
+static void cpufreq_submit(int cpu_num, const char *type,
+ const char *type_instance, value_t *value) {
value_list_t vl = VALUE_LIST_INIT;
- vl.values = &value;
+ vl.values = value;
vl.values_len = 1;
sstrncpy(vl.plugin, "cpufreq", sizeof(vl.plugin));
- sstrncpy(vl.type, "cpufreq", sizeof(vl.type));
- snprintf(vl.type_instance, sizeof(vl.type_instance), "%i", cpu_num);
+ snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", cpu_num);
+ if (type != NULL)
+ sstrncpy(vl.type, type, sizeof(vl.type));
+ if (type_instance != NULL)
+ sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
plugin_dispatch_values(&vl);
}
+static void cpufreq_read_stats(int cpu) {
+ char filename[PATH_MAX];
+ /* Read total transitions for cpu frequency */
+ snprintf(filename, sizeof(filename),
+ "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", cpu);
+
+ value_t v;
+ if (parse_value_file(filename, &v, DS_TYPE_DERIVE) != 0) {
+ ERROR("cpufreq plugin: Reading \"%s\" failed.", filename);
+ return;
+ }
+ cpufreq_submit(cpu, "transitions", NULL, &v);
+
+ /* Determine percentage time in each state for cpu during previous
+ * interval. */
+ snprintf(filename, sizeof(filename),
+ "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpu);
+
+ FILE *fh = fopen(filename, "r");
+ if (fh == NULL) {
+ ERROR("cpufreq plugin: Reading \"%s\" failed.", filename);
+ return;
+ }
+
+ int state_index = 0;
+ cdtime_t now = cdtime();
+ char buffer[DATA_MAX_NAME_LEN];
+
+ while (fgets(buffer, sizeof(buffer), fh) != NULL) {
+ unsigned int frequency;
+ unsigned long long time;
+
+ /*
+ * State time units is 10ms. To get rate of seconds per second
+ * we have to divide by 100. To get percents we have to multiply it
+ * by 100 back. So, just use parsed value directly.
+ */
+ if (!sscanf(buffer, "%u%llu", &frequency, &time)) {
+ ERROR("cpufreq plugin: Reading \"%s\" failed.", filename);
+ break;
+ }
+
+ char state[DATA_MAX_NAME_LEN];
+ snprintf(state, sizeof(state), "%u", frequency);
+
+ if (state_index >= MAX_AVAIL_FREQS) {
+ NOTICE("cpufreq plugin: Found too many frequency states (%d > %d). "
+ "Plugin needs to be recompiled. Please open a bug report for "
+ "this.",
+ (state_index + 1), MAX_AVAIL_FREQS);
+ break;
+ }
+
+ gauge_t g;
+ if (value_to_rate(&g, (value_t){.derive = time}, DS_TYPE_DERIVE, now,
+ &(cpu_data[cpu].time_state[state_index])) == 0) {
+ /*
+ * Due to some inaccuracy reported value can be a bit greatrer than 100.1.
+ * That produces gaps on charts.
+ */
+ if (g > 100.1)
+ g = 100.1;
+ cpufreq_submit(cpu, "percent", state, &(value_t){.gauge = g});
+ }
+ state_index++;
+ }
+ fclose(fh);
+}
+
static int cpufreq_read(void) {
- for (int i = 0; i < num_cpu; i++) {
+ for (int cpu = 0; cpu < num_cpu; cpu++) {
char filename[PATH_MAX];
+ /* Read cpu frequency */
snprintf(filename, sizeof(filename),
- "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
+ "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu);
value_t v;
if (parse_value_file(filename, &v, DS_TYPE_GAUGE) != 0) {
/* convert kHz to Hz */
v.gauge *= 1000.0;
- cpufreq_submit(i, v);
- }
+ cpufreq_submit(cpu, "cpufreq", NULL, &v);
+ if (report_p_stats)
+ cpufreq_read_stats(cpu);
+ }
return 0;
} /* int cpufreq_read */
size_t buffer_fill;
web_match_t *matches;
-
- web_page_t *next;
}; /* }}} */
/*
- * Global variables;
- */
-static web_page_t *pages_g;
-
-/*
* Private functions
*/
+static int cc_read_page(user_data_t *ud);
+
static size_t cc_curl_callback(void *buf, /* {{{ */
size_t size, size_t nmemb, void *user_data) {
web_page_t *wp;
sfree(wm);
} /* }}} void cc_web_match_free */
-static void cc_web_page_free(web_page_t *wp) /* {{{ */
+static void cc_web_page_free(void *arg) /* {{{ */
{
+ web_page_t *wp = (web_page_t *)arg;
if (wp == NULL)
return;
sfree(wp->buffer);
cc_web_match_free(wp->matches);
- cc_web_page_free(wp->next);
sfree(wp);
} /* }}} void cc_web_page_free */
static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
{
+ cdtime_t interval = 0;
web_page_t *page;
int status;
status = cc_config_append_string("Header", &page->headers, child);
else if (strcasecmp("Post", child->key) == 0)
status = cf_util_get_string(child, &page->post_body);
+ else if (strcasecmp("Interval", child->key) == 0)
+ status = cf_util_get_cdtime(child, &interval);
else if (strcasecmp("Timeout", child->key) == 0)
status = cf_util_get_int(child, &page->timeout);
else if (strcasecmp("Statistics", child->key) == 0) {
return status;
}
- /* Add the new page to the linked list */
- if (pages_g == NULL)
- pages_g = page;
- else {
- web_page_t *prev;
+ /* If all went well, register this page for reading */
+ char *cb_name = ssnprintf_alloc("curl-%s-%s", page->instance, page->url);
- prev = pages_g;
- while (prev->next != NULL)
- prev = prev->next;
- prev->next = page;
- }
+ plugin_register_complex_read(/* group = */ NULL, cb_name, cc_read_page,
+ interval,
+ &(user_data_t){
+ .data = page, .free_func = cc_web_page_free,
+ });
+ sfree(cb_name);
return 0;
} /* }}} int cc_config_add_page */
static int cc_init(void) /* {{{ */
{
- if (pages_g == NULL) {
- INFO("curl plugin: No pages have been defined.");
- return -1;
- }
curl_global_init(CURL_GLOBAL_SSL);
return 0;
} /* }}} int cc_init */
plugin_dispatch_values(&vl);
} /* }}} void cc_submit_response_time */
-static int cc_read_page(web_page_t *wp) /* {{{ */
+static int cc_read_page(user_data_t *ud) /* {{{ */
{
+
+ if ((ud == NULL) || (ud->data == NULL)) {
+ ERROR("curl plugin: cc_read_page: Invalid user data.");
+ return -1;
+ }
+
+ web_page_t *wp = (web_page_t *)ud->data;
+
int status;
cdtime_t start = 0;
return 0;
} /* }}} int cc_read_page */
-static int cc_read(void) /* {{{ */
-{
- for (web_page_t *wp = pages_g; wp != NULL; wp = wp->next)
- cc_read_page(wp);
-
- return 0;
-} /* }}} int cc_read */
-
-static int cc_shutdown(void) /* {{{ */
-{
- cc_web_page_free(pages_g);
- pages_g = NULL;
-
- return 0;
-} /* }}} int cc_shutdown */
-
void module_register(void) {
plugin_register_complex_config("curl", cc_config);
plugin_register_init("curl", cc_init);
- plugin_register_read("curl", cc_read);
- plugin_register_shutdown("curl", cc_shutdown);
} /* void module_register */
char *cacert;
struct curl_slist *headers;
char *post_body;
- cdtime_t interval;
int timeout;
curl_stats_t *stats;
#ifdef HAVE_CURLOPT_TIMEOUT_MS
if (db->timeout >= 0)
curl_easy_setopt(db->curl, CURLOPT_TIMEOUT_MS, (long)db->timeout);
- else if (db->interval > 0)
- curl_easy_setopt(db->curl, CURLOPT_TIMEOUT_MS,
- (long)CDTIME_T_TO_MS(db->interval));
else
curl_easy_setopt(db->curl, CURLOPT_TIMEOUT_MS,
(long)CDTIME_T_TO_MS(plugin_get_interval()));
{
cj_t *db;
int status = 0;
+ cdtime_t interval = 0;
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
WARNING("curl_json plugin: The `URL' block "
else if (strcasecmp("Key", child->key) == 0)
status = cj_config_add_key(db, child);
else if (strcasecmp("Interval", child->key) == 0)
- status = cf_util_get_cdtime(child, &db->interval);
+ status = cf_util_get_cdtime(child, &interval);
else if (strcasecmp("Timeout", child->key) == 0)
status = cf_util_get_int(child, &db->timeout);
else if (strcasecmp("Statistics", child->key) == 0) {
cb_name = ssnprintf_alloc("curl_json-%s-%s", db->instance,
db->url ? db->url : db->sock);
- plugin_register_complex_read(/* group = */ NULL, cb_name, cj_read,
- /* interval = */ db->interval,
+ plugin_register_complex_read(/* group = */ NULL, cb_name, cj_read, interval,
&(user_data_t){
.data = db, .free_func = cj_free,
});
sstrncpy(vl.plugin_instance, db->instance, sizeof(vl.plugin_instance));
sstrncpy(vl.type, key->type, sizeof(vl.type));
- if (db->interval > 0)
- vl.interval = db->interval;
-
plugin_dispatch_values(&vl);
} /* }}} int cj_submit_impl */
return status;
}
+ cdtime_t interval = 0;
+
/* Fill the `cx_t' structure.. */
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
status = cf_util_get_string(child, &db->post_body);
else if (strcasecmp("Namespace", child->key) == 0)
status = cx_config_add_namespace(db, child);
+ else if (strcasecmp("Interval", child->key) == 0)
+ status = cf_util_get_cdtime(child, &interval);
else if (strcasecmp("Timeout", child->key) == 0)
status = cf_util_get_int(child, &db->timeout);
else if (strcasecmp("Statistics", child->key) == 0) {
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,
+ /* interval = */ interval,
&(user_data_t){
.data = db, .free_func = cx_free,
});
--- /dev/null
+/**\r
+ * collectd - src/collectd_windows.c\r
+ * Copyright (C) 2017 Google LLC\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a\r
+ * copy of this software and associated documentation files (the "Software"),\r
+ * to deal in the Software without restriction, including without limitation\r
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+ * and/or sell copies of the Software, and to permit persons to whom the\r
+ * Software is furnished to do so, subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in\r
+ * all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\r
+ * DEALINGS IN THE SOFTWARE.\r
+ **/\r
+\r
+#include "cmd.h"\r
+#include "plugin.h"\r
+#include <stdio.h>\r
+#include <windows.h>\r
+\r
+int main(int argc, char **argv) {\r
+ WSADATA wsaData;\r
+ WORD wVersionRequested = MAKEWORD(2, 2);\r
+ int err = WSAStartup(wVersionRequested, &wsaData);\r
+ if (err != 0) {\r
+ ERROR("WSAStartup failed with error: %d\n", err);\r
+ return 1;\r
+ }\r
+\r
+ struct cmdline_config config = init_config(argc, argv);\r
+ return run_loop(config.test_readall);\r
+}\r
#define COLLECTD_LOCALE "C"
#endif
+#ifdef WIN32
+#undef COLLECT_DAEMON
+#include <unistd.h>
+#undef gethostname
+#include <locale.h>
+#include <winsock2.h>
+#endif
+
static int loop;
static int init_hostname(void) {
return 0;
}
+#ifdef WIN32
+ long hostname_len = NI_MAXHOST;
+#else
long hostname_len = sysconf(_SC_HOST_NAME_MAX);
if (hostname_len == -1) {
hostname_len = NI_MAXHOST;
}
+#endif /* WIN32 */
char hostname[hostname_len];
if (gethostname(hostname, hostname_len) != 0) {
static void read_cmdline(int argc, char **argv, struct cmdline_config *config) {
/* read options */
while (1) {
- int c = getopt(argc, argv, "htTC:"
+ int c = getopt(argc, argv, "BhtTC:"
#if COLLECT_DAEMON
"fP:"
#endif
#ifndef COLLECTD_H
#define COLLECTD_H
+#ifdef WIN32
+typedef int uid_t;
+#include "gnulib_config.h"
+#endif
+
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/isa_defs.h>
#endif
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
#ifndef BYTE_ORDER
#if defined(_BYTE_ORDER)
#define BYTE_ORDER _BYTE_ORDER
#endif
#endif
-#if HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
#ifndef PACKAGE_NAME
#define PACKAGE_NAME "collectd"
#endif
* Michał Mirosław <mirq-linux at rere.qmqm.pl>
**/
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
#include "collectd.h"
#include "common.h"
extern kstat_ctl_t *kc;
#endif
+#if !defined(MSG_DONTWAIT)
+#if defined(MSG_NONBLOCK)
/* AIX doesn't have MSG_DONTWAIT */
-#ifndef MSG_DONTWAIT
#define MSG_DONTWAIT MSG_NONBLOCK
-#endif
+#else
+/* Windows doesn't have MSG_DONTWAIT or MSG_NONBLOCK */
+#define MSG_DONTWAIT 0
+#endif /* defined(MSG_NONBLOCK) */
+#endif /* !defined(MSG_DONTWAIT) */
-#if !HAVE_GETPWNAM_R
+#if !HAVE_GETPWNAM_R && defined(HAVE_GETPWNAM)
static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER;
#endif
#if !HAVE_GETPWNAM_R
int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
struct passwd **pwbufp) {
+#ifndef HAVE_GETPWNAM
+ return -1;
+#else
int status = 0;
struct passwd *pw;
pthread_mutex_unlock(&getpwnam_r_lock);
return status;
+#endif /* HAVE_GETPWNAM */
} /* int getpwnam_r */
#endif /* !HAVE_GETPWNAM_R */
int parse_value_file(char const *path, value_t *ret_value, int ds_type);
#if !HAVE_GETPWNAM_R
+struct passwd;
int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
struct passwd **pwbufp);
#endif
counter_t counter_diff(counter_t old_value, counter_t new_value);
/* Convert a rate back to a value_t. When converting to a derive_t, counter_t
- * or absoltue_t, take fractional residuals into account. This is important
+ * or absolute_t, take fractional residuals into account. This is important
* when scaling counters, for example.
* Returns zero on success. Returns EAGAIN when called for the first time; in
* this case the value_t is invalid and the next call should succeed. Other
/* default to the global interval set before loading this plugin */
ctx.interval = cf_get_default_interval();
+ ctx.name = strdup(name);
old_ctx = plugin_set_ctx(ctx);
status = plugin_load(name, /* flags = */ false);
#endif
#ifndef PRIsz
+#ifdef WIN32
+#define PRIsz "Iu"
+#else
#define PRIsz "zu"
-#endif /* PRIsz */
+#endif /* WIN32 */
+#endif /* !PRIsz */
/* Type for time as used by "utils_time.h" */
typedef uint64_t cdtime_t;
#include "utils_random.h"
#include "utils_time.h"
+#ifdef WIN32
+#define EXPORT __declspec(dllexport)
+#include <sys/stat.h>
+#include <unistd.h>
+#else
+#define EXPORT
+#endif
+
#if HAVE_PTHREAD_NP_H
#include <pthread_np.h> /* for pthread_set_name_np(3) */
#endif
static long write_limit_high;
static long write_limit_low;
+static pthread_mutex_t statistics_lock = PTHREAD_MUTEX_INITIALIZER;
static derive_t stats_values_dropped;
static bool record_statistics;
old_cf = le->value;
le->value = cf;
- WARNING("plugin: register_callback: "
- "a callback named `%s' already exists - "
- "overwriting the old entry!",
- name);
+ P_WARNING("register_callback: "
+ "a callback named `%s' already exists - "
+ "overwriting the old entry!",
+ name);
destroy_callback(old_cf);
sfree(key);
n = llist_size(*list);
if (n == 0) {
- INFO("%s [none]", comment);
+ INFO("%s: [none]", comment);
return;
}
}
char name[THREAD_NAME_MAX];
- snprintf(name, sizeof(name), "reader#%" PRIsz, read_threads_num);
+ snprintf(name, sizeof(name), "reader#%" PRIu64, (uint64_t)read_threads_num);
set_thread_name(read_threads[read_threads_num], name);
read_threads_num++;
vl->time = cdtime();
/* Fill in the interval from the thread context, if it is zero. */
- if (vl->interval == 0) {
- plugin_ctx_t ctx = plugin_get_ctx();
-
- if (ctx.interval != 0)
- vl->interval = ctx.interval;
- else {
- char name[6 * DATA_MAX_NAME_LEN];
- FORMAT_VL(name, sizeof(name), vl);
- ERROR("plugin_value_list_clone: Unable to determine "
- "interval from context for "
- "value list \"%s\". "
- "This indicates a broken plugin. "
- "Please report this problem to the "
- "collectd mailing list or at "
- "<http://collectd.org/bugs/>.",
- name);
- vl->interval = cf_get_default_interval();
- }
- }
+ if (vl->interval == 0)
+ vl->interval = plugin_get_interval();
return vl;
} /* }}} value_list_t *plugin_value_list_clone */
}
char name[THREAD_NAME_MAX];
- snprintf(name, sizeof(name), "writer#%" PRIsz, write_threads_num);
+ snprintf(name, sizeof(name), "writer#%" PRIu64,
+ (uint64_t)write_threads_num);
set_thread_name(write_threads[write_threads_num], name);
write_threads_num++;
}
#define BUFSIZE 512
+#ifdef WIN32
+#define SHLIB_SUFFIX ".dll"
+#else
+#define SHLIB_SUFFIX ".so"
+#endif
int plugin_load(char const *plugin_name, bool global) {
DIR *dh;
const char *dir;
(strcasecmp("python", plugin_name) == 0))
global = true;
- /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
+ /* `cpu' should not match `cpufreq'. To solve this we add SHLIB_SUFFIX to the
* type when matching the filename */
- status = snprintf(typename, sizeof(typename), "%s.so", plugin_name);
+ status = snprintf(typename, sizeof(typename), "%s" SHLIB_SUFFIX, plugin_name);
if ((status < 0) || ((size_t)status >= sizeof(typename))) {
- WARNING("plugin_load: Filename too long: \"%s.so\"", plugin_name);
+ WARNING("plugin_load: Filename too long: \"%s" SHLIB_SUFFIX "\"",
+ plugin_name);
return -1;
}
/*
* The `register_*' functions follow
*/
-int plugin_register_config(const char *name,
- int (*callback)(const char *key, const char *val),
- const char **keys, int keys_num) {
+EXPORT int plugin_register_config(const char *name,
+ int (*callback)(const char *key,
+ const char *val),
+ const char **keys, int keys_num) {
cf_register(name, callback, keys, keys_num);
return 0;
} /* int plugin_register_config */
-int plugin_register_complex_config(const char *type,
- int (*callback)(oconfig_item_t *)) {
+EXPORT int plugin_register_complex_config(const char *type,
+ int (*callback)(oconfig_item_t *)) {
return cf_register_complex(type, callback);
} /* int plugin_register_complex_config */
-int plugin_register_init(const char *name, int (*callback)(void)) {
+EXPORT int plugin_register_init(const char *name, int (*callback)(void)) {
return create_register_callback(&list_init, name, (void *)callback, NULL);
} /* plugin_register_init */
le = llist_search(read_list, rf->rf_name);
if (le != NULL) {
pthread_mutex_unlock(&read_lock);
- WARNING("The read function \"%s\" is already registered. "
- "Check for duplicates in your configuration!",
- rf->rf_name);
+ P_WARNING("The read function \"%s\" is already registered. "
+ "Check for duplicates in your configuration!",
+ rf->rf_name);
return EINVAL;
}
return 0;
} /* int plugin_insert_read */
-int plugin_register_read(const char *name, int (*callback)(void)) {
+EXPORT int plugin_register_read(const char *name, int (*callback)(void)) {
read_func_t *rf;
int status;
rf->rf_name = strdup(name);
rf->rf_type = RF_SIMPLE;
rf->rf_interval = plugin_get_interval();
+ rf->rf_ctx.interval = rf->rf_interval;
status = plugin_insert_read(rf);
if (status != 0) {
return status;
} /* int plugin_register_read */
-int plugin_register_complex_read(const char *group, const char *name,
- plugin_read_cb callback, cdtime_t interval,
- user_data_t const *user_data) {
+EXPORT int plugin_register_complex_read(const char *group, const char *name,
+ plugin_read_cb callback,
+ cdtime_t interval,
+ user_data_t const *user_data) {
read_func_t *rf;
int status;
}
rf->rf_ctx = plugin_get_ctx();
+ rf->rf_ctx.interval = rf->rf_interval;
status = plugin_insert_read(rf);
if (status != 0) {
return status;
} /* int plugin_register_complex_read */
-int plugin_register_write(const char *name, plugin_write_cb callback,
- user_data_t const *ud) {
+EXPORT int plugin_register_write(const char *name, plugin_write_cb callback,
+ user_data_t const *ud) {
return create_register_callback(&list_write, name, (void *)callback, ud);
} /* int plugin_register_write */
return flush_name;
} /* static char *plugin_flush_callback_name */
-int plugin_register_flush(const char *name, plugin_flush_cb callback,
- user_data_t const *ud) {
+EXPORT int plugin_register_flush(const char *name, plugin_flush_cb callback,
+ user_data_t const *ud) {
int status;
plugin_ctx_t ctx = plugin_get_ctx();
return 0;
} /* int plugin_register_flush */
-int plugin_register_missing(const char *name, plugin_missing_cb callback,
- user_data_t const *ud) {
+EXPORT int plugin_register_missing(const char *name, plugin_missing_cb callback,
+ user_data_t const *ud) {
return create_register_callback(&list_missing, name, (void *)callback, ud);
} /* int plugin_register_missing */
-int plugin_register_shutdown(const char *name, int (*callback)(void)) {
+EXPORT int plugin_register_shutdown(const char *name, int (*callback)(void)) {
return create_register_callback(&list_shutdown, name, (void *)callback, NULL);
} /* int plugin_register_shutdown */
data_sets = NULL;
} /* void plugin_free_data_sets */
-int plugin_register_data_set(const data_set_t *ds) {
+EXPORT int plugin_register_data_set(const data_set_t *ds) {
data_set_t *ds_copy;
if ((data_sets != NULL) && (c_avl_get(data_sets, ds->type, NULL) == 0)) {
return c_avl_insert(data_sets, (void *)ds_copy->type, (void *)ds_copy);
} /* int plugin_register_data_set */
-int plugin_register_log(const char *name, plugin_log_cb callback,
- user_data_t const *ud) {
+EXPORT int plugin_register_log(const char *name, plugin_log_cb callback,
+ user_data_t const *ud) {
return create_register_callback(&list_log, name, (void *)callback, ud);
} /* int plugin_register_log */
-int plugin_register_notification(const char *name,
- plugin_notification_cb callback,
- user_data_t const *ud) {
+EXPORT int plugin_register_notification(const char *name,
+ plugin_notification_cb callback,
+ user_data_t const *ud) {
return create_register_callback(&list_notification, name, (void *)callback,
ud);
} /* int plugin_register_log */
-int plugin_unregister_config(const char *name) {
+EXPORT int plugin_unregister_config(const char *name) {
cf_unregister(name);
return 0;
} /* int plugin_unregister_config */
-int plugin_unregister_complex_config(const char *name) {
+EXPORT int plugin_unregister_complex_config(const char *name) {
cf_unregister_complex(name);
return 0;
} /* int plugin_unregister_complex_config */
-int plugin_unregister_init(const char *name) {
+EXPORT int plugin_unregister_init(const char *name) {
return plugin_unregister(list_init, name);
}
-int plugin_unregister_read(const char *name) /* {{{ */
+EXPORT int plugin_unregister_read(const char *name) /* {{{ */
{
llentry_t *le;
read_func_t *rf;
return 0;
} /* }}} int plugin_unregister_read */
-void plugin_log_available_writers(void) {
+EXPORT void plugin_log_available_writers(void) {
log_list_callbacks(&list_write, "Available write targets:");
}
return strcmp(rf->rf_group, (const char *)group);
} /* }}} int compare_read_func_group */
-int plugin_unregister_read_group(const char *group) /* {{{ */
+EXPORT int plugin_unregister_read_group(const char *group) /* {{{ */
{
llentry_t *le;
read_func_t *rf;
return 0;
} /* }}} int plugin_unregister_read_group */
-int plugin_unregister_write(const char *name) {
+EXPORT int plugin_unregister_write(const char *name) {
return plugin_unregister(list_write, name);
}
-int plugin_unregister_flush(const char *name) {
+EXPORT int plugin_unregister_flush(const char *name) {
plugin_ctx_t ctx = plugin_get_ctx();
if (ctx.flush_interval != 0) {
return plugin_unregister(list_flush, name);
}
-int plugin_unregister_missing(const char *name) {
+EXPORT int plugin_unregister_missing(const char *name) {
return plugin_unregister(list_missing, name);
}
-int plugin_unregister_shutdown(const char *name) {
+EXPORT int plugin_unregister_shutdown(const char *name) {
return plugin_unregister(list_shutdown, name);
}
-int plugin_unregister_data_set(const char *name) {
+EXPORT int plugin_unregister_data_set(const char *name) {
data_set_t *ds;
if (data_sets == NULL)
return 0;
} /* int plugin_unregister_data_set */
-int plugin_unregister_log(const char *name) {
+EXPORT int plugin_unregister_log(const char *name) {
return plugin_unregister(list_log, name);
}
-int plugin_unregister_notification(const char *name) {
+EXPORT int plugin_unregister_notification(const char *name) {
return plugin_unregister(list_notification, name);
}
-int plugin_init_all(void) {
+EXPORT int plugin_init_all(void) {
char const *chain_name;
llentry_t *le;
int status;
} /* void plugin_init_all */
/* TODO: Rename this function. */
-void plugin_read_all(void) {
+EXPORT void plugin_read_all(void) {
uc_check_timeout();
return;
} /* void plugin_read_all */
/* Read function called when the `-T' command line argument is given. */
-int plugin_read_all_once(void) {
+EXPORT int plugin_read_all_once(void) {
int status;
int return_status = 0;
return return_status;
} /* int plugin_read_all_once */
-int plugin_write(const char *plugin, /* {{{ */
- const data_set_t *ds, const value_list_t *vl) {
+EXPORT int plugin_write(const char *plugin, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl) {
llentry_t *le;
int status;
return status;
} /* }}} int plugin_write */
-int plugin_flush(const char *plugin, cdtime_t timeout, const char *identifier) {
+EXPORT int plugin_flush(const char *plugin, cdtime_t timeout,
+ const char *identifier) {
llentry_t *le;
if (list_flush == NULL)
return 0;
} /* int plugin_flush */
-int plugin_shutdown_all(void) {
+EXPORT int plugin_shutdown_all(void) {
llentry_t *le;
int ret = 0; // Assume success.
return ret;
} /* void plugin_shutdown_all */
-int plugin_dispatch_missing(const value_list_t *vl) /* {{{ */
+EXPORT int plugin_dispatch_missing(const value_list_t *vl) /* {{{ */
{
if (list_missing == NULL)
return 0;
return false;
} /* }}} bool check_drop_value */
-int plugin_dispatch_values(value_list_t const *vl) {
+EXPORT int plugin_dispatch_values(value_list_t const *vl) {
int status;
- static pthread_mutex_t statistics_lock = PTHREAD_MUTEX_INITIALIZER;
if (check_drop_value()) {
if (record_statistics) {
gauge_t sum = 0.0;
va_list ap;
+ if (check_drop_value()) {
+ if (record_statistics) {
+ pthread_mutex_lock(&statistics_lock);
+ stats_values_dropped++;
+ pthread_mutex_unlock(&statistics_lock);
+ }
+ return 0;
+ }
+
assert(template->values_len == 1);
/* Calculate sum for Gauge to calculate percent if needed */
return failed;
} /* }}} int plugin_dispatch_multivalue */
-int plugin_dispatch_notification(const notification_t *notif) {
+EXPORT int plugin_dispatch_notification(const notification_t *notif) {
llentry_t *le;
/* Possible TODO: Add flap detection here */
return 0;
} /* int plugin_dispatch_notification */
-void plugin_log(int level, const char *format, ...) {
+EXPORT void plugin_log(int level, const char *format, ...) {
char msg[1024];
va_list ap;
llentry_t *le;
return log_level;
} /* int parse_log_severity */
-int parse_notif_severity(const char *severity) {
+EXPORT int parse_notif_severity(const char *severity) {
int notif_severity = -1;
if (strcasecmp(severity, "FAILURE") == 0)
return notif_severity;
} /* int parse_notif_severity */
-const data_set_t *plugin_get_ds(const char *name) {
+EXPORT const data_set_t *plugin_get_ds(const char *name) {
data_set_t *ds;
if (data_sets == NULL) {
- ERROR("plugin_get_ds: No data sets are defined yet.");
+ P_ERROR("plugin_get_ds: No data sets are defined yet.");
return NULL;
}
return ctx;
} /* int plugin_ctx_create */
-void plugin_init_ctx(void) {
+EXPORT void plugin_init_ctx(void) {
pthread_key_create(&plugin_ctx_key, plugin_ctx_destructor);
plugin_ctx_key_initialized = true;
} /* void plugin_init_ctx */
-plugin_ctx_t plugin_get_ctx(void) {
+EXPORT plugin_ctx_t plugin_get_ctx(void) {
plugin_ctx_t *ctx;
assert(plugin_ctx_key_initialized);
return *ctx;
} /* plugin_ctx_t plugin_get_ctx */
-plugin_ctx_t plugin_set_ctx(plugin_ctx_t ctx) {
+EXPORT plugin_ctx_t plugin_set_ctx(plugin_ctx_t ctx) {
plugin_ctx_t *c;
plugin_ctx_t old;
return old;
} /* void plugin_set_ctx */
-cdtime_t plugin_get_interval(void) {
+EXPORT cdtime_t plugin_get_interval(void) {
cdtime_t interval;
interval = plugin_get_ctx().interval;
if (interval > 0)
return interval;
+ P_ERROR("plugin_get_interval: Unable to determine Interval from context.");
+
return cf_get_default_interval();
} /* cdtime_t plugin_get_interval */
#include "meta_data.h"
#include "utils_time.h"
+#include <inttypes.h>
#include <pthread.h>
#define DS_TYPE_COUNTER 0
*
* DESCRIPTION
* Calls the write function of the given plugin with the provided data set and
- * value list. It differs from `plugin_dispatch_value' in that it does not
+ * value list. It differs from `plugin_dispatch_values' in that it does not
* update the cache, does not do threshold checking, call the chain subsystem
* and so on. It looks up the requested plugin and invokes the function, end
* of story.
return -1;
if (iter->node == NULL) {
- for (n = iter->tree->root; n != NULL; n = n->left)
+ for (n = iter->tree->root; n != NULL; n = n->right)
if (n->right == NULL)
break;
iter->node = n;
return strcmp(v0, v1);
}
+struct kv_t {
+ char *key;
+ char *value;
+};
+
+static int kv_compare(const void *a_ptr, const void *b_ptr) {
+ return strcmp(((struct kv_t *)a_ptr)->key, ((struct kv_t *)b_ptr)->key);
+}
+
DEF_TEST(success) {
- struct {
- char *key;
- char *value;
- } cases[] = {
+ struct kv_t cases[] = {
{"Eeph7chu", "vai1reiV"}, {"igh3Paiz", "teegh1Ee"},
{"caip6Uu8", "ooteQu8n"}, {"Aech6vah", "AijeeT0l"},
{"Xah0et2L", "gah8Taep"}, {"BocaeB8n", "oGaig8io"},
{"ieN5engi", "Aevou1ah"}, {"ooTe4OhP", "aingai5Y"},
};
+ struct kv_t sorted_cases[STATIC_ARRAY_SIZE(cases)];
+ memcpy(sorted_cases, cases, sizeof(cases));
+ qsort(sorted_cases, STATIC_ARRAY_SIZE(cases), sizeof(struct kv_t),
+ kv_compare);
+
c_avl_tree_t *t;
RESET_COUNTS();
EXPECT_EQ_STR(cases[i].value, value_ret);
}
+ /* iterate forward */
+ {
+ c_avl_iterator_t *iter = c_avl_get_iterator(t);
+ char *key;
+ char *value;
+ size_t i = 0;
+ while (c_avl_iterator_next(iter, (void **)&key, (void **)&value) == 0) {
+ EXPECT_EQ_STR(sorted_cases[i].key, key);
+ EXPECT_EQ_STR(sorted_cases[i].value, value);
+ i++;
+ }
+ c_avl_iterator_destroy(iter);
+ EXPECT_EQ_INT(i, STATIC_ARRAY_SIZE(cases));
+ }
+
+ /* iterate backward */
+ {
+ c_avl_iterator_t *iter = c_avl_get_iterator(t);
+ char *key;
+ char *value;
+ size_t i = 0;
+ while (c_avl_iterator_prev(iter, (void **)&key, (void **)&value) == 0) {
+ EXPECT_EQ_STR(sorted_cases[STATIC_ARRAY_SIZE(cases) - 1 - i].key, key);
+ EXPECT_EQ_STR(sorted_cases[STATIC_ARRAY_SIZE(cases) - 1 - i].value,
+ value);
+ i++;
+ }
+ c_avl_iterator_destroy(iter);
+ EXPECT_EQ_INT(i, STATIC_ARRAY_SIZE(cases));
+ }
+
/* remove half */
for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases) / 2; i++) {
char *key = NULL;
ce->last_time = vl->time;
ce->last_update = cdtime();
ce->interval = vl->interval;
- ce->state = STATE_OKAY;
+ ce->state = STATE_UNKNOWN;
if (c_avl_insert(cache_tree, key_copy, ce) != 0) {
sfree(key_copy);
#include "plugin.h"
-#define STATE_OKAY 0
-#define STATE_WARNING 1
-#define STATE_ERROR 2
+#define STATE_UNKNOWN 0
+#define STATE_OKAY 1
+#define STATE_WARNING 2
+#define STATE_ERROR 3
#define STATE_MISSING 15
int uc_init(void);
#include "utils_cache.h"
#include <errno.h>
+#include <errno.h>
+
gauge_t *uc_get_rate(__attribute__((unused)) data_set_t const *ds,
__attribute__((unused)) value_list_t const *vl) {
errno = ENOTSUP;
size_t *ret_values_num) {
return ENOTSUP;
}
+
+int uc_meta_data_get_signed_int(const value_list_t *vl, const char *key,
+ int64_t *value) {
+ return -ENOENT;
+}
+
+int uc_meta_data_get_unsigned_int(const value_list_t *vl, const char *key,
+ uint64_t *value) {
+ return -ENOENT;
+}
+
+int uc_meta_data_add_signed_int(const value_list_t *vl, const char *key,
+ int64_t value) {
+ return 0;
+}
+
+int uc_meta_data_add_unsigned_int(const value_list_t *vl, const char *key,
+ uint64_t value) {
+ return 0;
+}
* Florian octo Forster <octo at collectd.org>
**/
+#include "collectd.h"
+
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <pthread.h>
+#ifdef WIN32
+double erand48(unsigned short unused[3]) {
+ return (double)rand() / (double)RAND_MAX;
+}
+
+long int jrand48(unsigned short unused[3]) { return rand(); }
+#endif
+
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static bool have_seed;
static unsigned short seed[3];
seed[1] = (unsigned short)(t >> 16);
seed[2] = (unsigned short)(t >> 32);
+#ifdef WIN32
+ srand((unsigned)t);
+#endif
+
have_seed = true;
}
char *select_db;
char *plugin_name;
- cdtime_t interval;
-
char *driver;
char *host;
cdbi_driver_option_t *driver_options;
static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */
{
+ cdtime_t interval = 0;
cdbi_database_t *db;
int status;
else if (strcasecmp("Host", child->key) == 0)
status = cf_util_get_string(child, &db->host);
else if (strcasecmp("Interval", child->key) == 0)
- status = cf_util_get_cdtime(child, &db->interval);
+ status = cf_util_get_cdtime(child, &interval);
else if (strcasecmp("Plugin", child->key) == 0)
status = cf_util_get_string(child, &db->plugin_name);
else {
/* group = */ NULL,
/* name = */ name ? name : db->name,
/* callback = */ cdbi_read_database,
- /* interval = */ (db->interval > 0) ? db->interval : 0,
+ /* interval = */ interval,
&(user_data_t){
.data = db,
});
status = udb_query_prepare_result(
q, prep_area, (db->host ? db->host : hostname_g),
/* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "dbi",
- db->name, column_names, column_num,
- /* interval = */ (db->interval > 0) ? db->interval : 0);
+ db->name, column_names, column_num);
if (status != 0) {
ERROR("dbi plugin: udb_query_prepare_result failed with status %i.",
} /* }}} */
/* Get the next row from the database. */
+ if (!dbi_result_has_next_row(res))
+ break;
+
status = dbi_result_next_row(res); /* {{{ */
if (status != 1) {
- if (dbi_conn_error(db->connection, NULL) != 0) {
- char errbuf[1024];
- WARNING("dbi plugin: cdbi_read_database_query (%s, %s): "
- "dbi_result_next_row failed: %s.",
- db->name, udb_query_get_name(q),
- cdbi_strerror(db->connection, errbuf, sizeof(errbuf)));
- }
+ char errbuf[1024];
+ WARNING("dbi plugin: cdbi_read_database_query (%s, %s): "
+ "dbi_result_next_row failed: %s.",
+ db->name, udb_query_get_name(q),
+ cdbi_strerror(db->connection, errbuf, sizeof(errbuf)));
break;
} /* }}} */
} /* }}} while (42) */
plugin_dispatch_values(&vl);
} /* void submit_io_time */
-#endif /* KERNEL_FREEBSD || KERNEL_LINUX */
-#if KERNEL_LINUX
static void submit_in_progress(char const *disk_name, gauge_t in_progress) {
value_list_t vl = VALUE_LIST_INIT;
plugin_dispatch_values(&vl);
}
+#endif /* KERNEL_FREEBSD || KERNEL_LINUX */
+#if KERNEL_LINUX
static counter_t disk_calc_time_incr(counter_t delta_time,
counter_t delta_ops) {
double interval = CDTIME_T_TO_DOUBLE(plugin_get_interval());
const char *disk_name;
long double read_time, write_time, busy_time, total_duration;
+ uint64_t queue_length;
for (retry = 0, dirty = 1; retry < 5 && dirty == 1; retry++) {
if (snap != NULL)
}
if (devstat_compute_statistics(snap_iter, NULL, 1.0, DSM_TOTAL_BUSY_TIME,
&busy_time, DSM_TOTAL_DURATION,
- &total_duration, DSM_NONE) != 0) {
+ &total_duration, DSM_QUEUE_LENGTH,
+ &queue_length, DSM_NONE) != 0) {
WARNING("%s", devstat_errbuf);
} else {
submit_io_time(disk_name, busy_time, total_duration);
+ submit_in_progress(disk_name, (gauge_t)queue_length);
}
}
geom_stats_snapshot_free(snap);
char buffer[1024];
char *fields[32];
- int numfields;
- int fieldshift = 0;
-
- int minor = 0;
+ static unsigned int poll_count = 0;
derive_t read_sectors = 0;
derive_t write_sectors = 0;
diskstats_t *ds, *pre_ds;
if ((fh = fopen("/proc/diskstats", "r")) == NULL) {
- fh = fopen("/proc/partitions", "r");
- if (fh == NULL) {
- ERROR("disk plugin: fopen (/proc/{diskstats,partitions}) failed.");
- return -1;
- }
-
- /* Kernel is 2.4.* */
- fieldshift = 1;
+ ERROR("disk plugin: fopen(\"/proc/diskstats\"): %s", STRERRNO);
+ return -1;
}
+ poll_count++;
while (fgets(buffer, sizeof(buffer), fh) != NULL) {
- char *disk_name;
- char *output_name;
+ int numfields = strsplit(buffer, fields, 32);
- numfields = strsplit(buffer, fields, 32);
-
- if ((numfields != (14 + fieldshift)) && (numfields != 7))
+ /* need either 7 fields (partition) or at least 14 fields */
+ if ((numfields != 7) && (numfields < 14))
continue;
- minor = atoll(fields[1]);
-
- disk_name = fields[2 + fieldshift];
+ char *disk_name = fields[2];
for (ds = disklist, pre_ds = disklist; ds != NULL;
pre_ds = ds, ds = ds->next)
read_sectors = atoll(fields[4]);
write_ops = atoll(fields[5]);
write_sectors = atoll(fields[6]);
- } else if (numfields == (14 + fieldshift)) {
- read_ops = atoll(fields[3 + fieldshift]);
- write_ops = atoll(fields[7 + fieldshift]);
+ } else {
+ assert(numfields >= 14);
+ read_ops = atoll(fields[3]);
+ write_ops = atoll(fields[7]);
- read_sectors = atoll(fields[5 + fieldshift]);
- write_sectors = atoll(fields[9 + fieldshift]);
+ read_sectors = atoll(fields[5]);
+ write_sectors = atoll(fields[9]);
- if ((fieldshift == 0) || (minor == 0)) {
- is_disk = 1;
- read_merged = atoll(fields[4 + fieldshift]);
- read_time = atoll(fields[6 + fieldshift]);
- write_merged = atoll(fields[8 + fieldshift]);
- write_time = atoll(fields[10 + fieldshift]);
+ is_disk = 1;
+ read_merged = atoll(fields[4]);
+ read_time = atoll(fields[6]);
+ write_merged = atoll(fields[8]);
+ write_time = atoll(fields[10]);
- in_progress = atof(fields[11 + fieldshift]);
+ in_progress = atof(fields[11]);
- io_time = atof(fields[12 + fieldshift]);
- weighted_time = atof(fields[13 + fieldshift]);
- }
- } else {
- DEBUG("numfields = %i; => unknown file format.", numfields);
- continue;
+ io_time = atof(fields[12]);
+ weighted_time = atof(fields[13]);
}
{
} /* if (is_disk) */
- /* Don't write to the RRDs if we've just started.. */
- ds->poll_count++;
- if (ds->poll_count <= 2) {
- DEBUG("disk plugin: (ds->poll_count = %i) <= "
- "(min_poll_count = 2); => Not writing.",
- ds->poll_count);
+ /* Skip first cycle for newly-added disk */
+ if (ds->poll_count == 0) {
+ DEBUG("disk plugin: (ds->poll_count = 0) => Skipping.");
+ ds->poll_count = poll_count;
continue;
}
+ ds->poll_count = poll_count;
if ((read_ops == 0) && (write_ops == 0)) {
DEBUG("disk plugin: ((read_ops == 0) && "
continue;
}
- output_name = disk_name;
+ char *output_name = disk_name;
#if HAVE_LIBUDEV_H
char *alt_name = NULL;
#endif
} /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
+ /* Remove disks that have disappeared from diskstats */
+ for (ds = disklist, pre_ds = disklist; ds != NULL;) {
+ /* Disk exists */
+ if (ds->poll_count == poll_count) {
+ pre_ds = ds;
+ ds = ds->next;
+ continue;
+ }
+
+ /* Disk is missing, remove it */
+ diskstats_t *missing_ds = ds;
+ if (ds == disklist) {
+ pre_ds = disklist = ds->next;
+ } else {
+ pre_ds->next = ds->next;
+ }
+ ds = ds->next;
+
+ DEBUG("disk plugin: Disk %s disappeared.", missing_ds->name);
+ free(missing_ds->name);
+ free(missing_ds);
+ }
fclose(fh);
/* #endif defined(KERNEL_LINUX) */
static int dpdk_helper_link_status_get(dpdk_helper_ctx_t *phc) {
dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(phc);
- /* get Link Status values from DPDK */
+/* get Link Status values from DPDK */
+#if RTE_VERSION < RTE_VERSION_NUM(18, 05, 0, 0)
uint8_t nb_ports = rte_eth_dev_count();
+#else
+ uint8_t nb_ports = rte_eth_dev_count_avail();
+#endif
if (nb_ports == 0) {
DPDK_CHILD_LOG("dpdkevent-helper: No DPDK ports available. "
"Check bound devices to DPDK driver.\n");
} program_list_and_notification_t;
/*
+ * constants
+ */
+const long int MAX_GRBUF_SIZE = 65536;
+
+/*
* Private variables
*/
static program_list_t *pl_head;
return 0;
} /* int exec_config }}} */
+#if !defined(HAVE_SETENV)
+static char env_interval[64];
+// max hostname len is 255, so this should be enough
+static char env_hostname[300];
+#endif
+
static void set_environment(void) /* {{{ */
{
+#ifdef HAVE_SETENV
char buffer[1024];
-#ifdef HAVE_SETENV
snprintf(buffer, sizeof(buffer), "%.3f",
CDTIME_T_TO_DOUBLE(plugin_get_interval()));
setenv("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
sstrncpy(buffer, hostname_g, sizeof(buffer));
setenv("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
#else
- snprintf(buffer, sizeof(buffer), "COLLECTD_INTERVAL=%.3f",
+ snprintf(env_interval, sizeof(env_interval), "COLLECTD_INTERVAL=%.3f",
CDTIME_T_TO_DOUBLE(plugin_get_interval()));
- putenv(buffer);
+ putenv(env_interval);
- snprintf(buffer, sizeof(buffer), "COLLECTD_HOSTNAME=%s", hostname_g);
- putenv(buffer);
+ snprintf(env_hostname, sizeof(env_hostname), "COLLECTD_HOSTNAME=%s",
+ hostname_g);
+ putenv(env_hostname);
#endif
} /* }}} void set_environment */
+static void unset_environment(void) /* {{{ */
+{
+#ifdef HAVE_SETENV
+ unsetenv("COLLECTD_INTERVAL");
+ unsetenv("COLLECTD_HOSTNAME");
+#else
+ snprintf(env_interval, sizeof(env_interval), "COLLECTD_INTERVAL");
+ putenv(env_interval);
+ snprintf(env_hostname, sizeof(env_hostname), "COLLECTD_HOSTNAME");
+ putenv(env_hostname);
+#endif
+} /* }}} void unset_environment */
+
__attribute__((noreturn)) static void exec_child(program_list_t *pl, int uid,
int gid, int egid) /* {{{ */
{
} /* }}} void close_pipe */
/*
+ * Get effective group ID from group name.
+ * Input arguments:
+ * pl :program list struct with group name
+ * gid :group id to fallback in case egid cannot be determined.
+ * Returns:
+ * egid effective group id if successfull,
+ * -1 if group is not defined/not found.
+ * -2 for any buffer allocation error.
+ */
+static int getegr_id(program_list_t *pl, int gid) /* {{{ */
+{
+ if (pl->group == NULL) {
+ return -1;
+ }
+ if (strcmp(pl->group, "") == 0) {
+ return gid;
+ }
+ struct group *gr_ptr = NULL;
+ struct group gr;
+
+ 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 *temp = NULL;
+ char *grbuf = NULL;
+
+ do {
+ temp = realloc(grbuf, grbuf_size);
+ if (temp == NULL) {
+ ERROR("exec plugin: getegr_id for %s: realloc buffer[%ld] failed ",
+ pl->group, grbuf_size);
+ sfree(grbuf);
+ return -2;
+ }
+ grbuf = temp;
+ if (getgrnam_r(pl->group, &gr, grbuf, grbuf_size, &gr_ptr) == 0) {
+ sfree(grbuf);
+ if (gr_ptr == NULL) {
+ ERROR("exec plugin: No such group: `%s'", pl->group);
+ return -1;
+ }
+ return gr.gr_gid;
+ } else if (errno == ERANGE) {
+ grbuf_size += grbuf_size; // increment buffer size and try again
+ } else {
+ ERROR("exec plugin: getegr_id failed %s", STRERRNO);
+ sfree(grbuf);
+ return -2;
+ }
+ } while (grbuf_size <= MAX_GRBUF_SIZE);
+ ERROR("exec plugin: getegr_id Max grbuf size reached for %s", pl->group);
+ sfree(grbuf);
+ return -2;
+}
+
+/*
* Creates three pipes (one for reading, one for writing and one for errors),
* forks a child, sets up the pipes so that fd_in is connected to STDIN of
* the child and fd_out is connected to STDOUT and fd_err is connected to STDERR
/* The group configured in the configfile is set as effective group, because
* this way the forked process can (re-)gain the user's primary group. */
- egid = -1;
- if (pl->group != NULL) {
- if (*pl->group != '\0') {
- struct group *gr_ptr = NULL;
- struct group gr;
-
- 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",
- pl->group, STRERROR(status));
- goto failed;
- }
- if (gr_ptr == NULL) {
- ERROR("exec plugin: No such group: `%s'", pl->group);
- goto failed;
- }
+ egid = getegr_id(pl, gid);
+ if (egid == -2) {
+ goto failed;
+ }
- egid = gr.gr_gid;
- } else {
- egid = gid;
- }
- } /* if (pl->group == NULL) */
+ set_environment();
pid = fork();
if (pid < 0) {
close(fd_pipe_err[1]);
}
- set_environment();
-
/* Unblock all signals */
reset_signal_mask();
/* does not return */
}
+ unset_environment();
+
close(fd_pipe_in[0]);
close(fd_pipe_out[1]);
close(fd_pipe_err[1]);
return pid;
failed:
+ unset_environment();
+
close_pipe(fd_pipe_in);
close_pipe(fd_pipe_out);
close_pipe(fd_pipe_err);
sockets_num++;
break;
} else {
- int yes = 1;
-
status = setsockopt(sockets[sockets_num].fd, SOL_SOCKET, SO_REUSEADDR,
- (void *)&yes, sizeof(yes));
+ &(int){1}, sizeof(int));
if (status != 0) {
WARNING("gmond plugin: setsockopt(2) failed: %s", STRERRNO);
}
continue;
}
- if (gps_read(&gpsd_conn) == -1) {
+#if GPSD_API_MAJOR_VERSION > 6
+ if (gps_read(&gpsd_conn, NULL, 0) == -1)
+#else
+ if (gps_read(&gpsd_conn) == -1)
+#endif
+ {
WARNING("gps plugin: incorrect data! (err_count: %d)", err_count);
err_count++;
--- /dev/null
+/*
+Copyright 2018 Evgeny Naumov
+
+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 "daemon/collectd.h"
+#include "daemon/common.h"
+#include "daemon/plugin.h"
+
+#include <nvml.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#define MAX_DEVNAME_LEN 256
+#define PLUGIN_NAME "gpu_nvidia"
+
+static nvmlReturn_t nv_status = NVML_SUCCESS;
+static char *nv_errline = "";
+
+#define TRY_CATCH(f, catch) \
+ if ((nv_status = f) != NVML_SUCCESS) { \
+ nv_errline = #f; \
+ goto catch; \
+ }
+
+#define TRY_CATCH_OPTIONAL(f, catch) \
+ if ((nv_status = f) != NVML_SUCCESS && \
+ nv_status != NVML_ERROR_NOT_SUPPORTED) { \
+ nv_errline = #f; \
+ goto catch; \
+ }
+
+#define TRY(f) TRY_CATCH(f, catch)
+#define TRYOPT(f) TRY_CATCH_OPTIONAL(f, catch)
+
+#define KEY_GPUINDEX "GPUIndex"
+#define KEY_IGNORESELECTED "IgnoreSelected"
+
+static const char *config_keys[] = {
+ KEY_GPUINDEX, KEY_IGNORESELECTED,
+};
+static const unsigned int n_config_keys = STATIC_ARRAY_SIZE(config_keys);
+
+// This is a bitflag, necessitating the (extremely conservative) assumption
+// that there are no more than 64 GPUs on this system.
+static uint64_t conf_match_mask = 0;
+static bool conf_mask_is_exclude = 0;
+
+static int nvml_config(const char *key, const char *value) {
+
+ if (strcasecmp(key, KEY_GPUINDEX) == 0) {
+ char *eptr;
+ unsigned long device_ix = strtoul(value, &eptr, 10);
+ if (eptr == value) {
+ ERROR(PLUGIN_NAME ": Failed to parse GPUIndex value \"%s\"", value);
+ return -1;
+ }
+ if (device_ix >= 64) {
+ ERROR(PLUGIN_NAME
+ ": At most 64 GPUs (0 <= GPUIndex < 64) are supported!");
+ return -2;
+ }
+ conf_match_mask |= (1 << device_ix);
+ } else if (strcasecmp(key, KEY_IGNORESELECTED)) {
+ conf_mask_is_exclude = IS_TRUE(value);
+ } else {
+ ERROR(PLUGIN_NAME ": Unrecognized config option %s", key);
+ return -10;
+ }
+
+ return 0;
+}
+
+static int nvml_init(void) {
+ TRY(nvmlInit());
+ return 0;
+
+ catch : ERROR(PLUGIN_NAME ": NVML init failed with %d", nv_status);
+ return -1;
+}
+
+static int nvml_shutdown(void) {
+ TRY(nvmlShutdown())
+ return 0;
+
+ catch : ERROR(PLUGIN_NAME ": NVML shutdown failed with %d", nv_status);
+ return -1;
+}
+
+static void nvml_submit_gauge(const char *plugin_instance, const char *type,
+ const char *type_instance, gauge_t nvml) {
+
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = &(value_t){.gauge = nvml};
+ vl.values_len = 1;
+
+ sstrncpy(vl.plugin, PLUGIN_NAME, sizeof(vl.plugin));
+ sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
+
+ sstrncpy(vl.type, type, sizeof(vl.type));
+
+ if (type_instance != NULL) {
+ sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+ }
+
+ plugin_dispatch_values(&vl);
+}
+
+static int nvml_read(void) {
+
+ unsigned int device_count;
+ TRY_CATCH(nvmlDeviceGetCount(&device_count), catch_nocount);
+
+ if (device_count > 64) {
+ device_count = 64;
+ }
+
+ for (unsigned int ix = 0; ix < device_count; ix++) {
+
+ unsigned int is_match =
+ ((1 << ix) & conf_match_mask) || (conf_match_mask == 0);
+ if (conf_mask_is_exclude == !!is_match) {
+ continue;
+ }
+
+ nvmlDevice_t dev;
+ TRY(nvmlDeviceGetHandleByIndex(ix, &dev));
+
+ char dev_name[MAX_DEVNAME_LEN + 1] = {0};
+ TRY(nvmlDeviceGetName(dev, dev_name, sizeof(dev_name) - 1));
+
+ // Try to be as lenient as possible with the variety of devices that are
+ // out there, ignoring any NOT_SUPPORTED errors gently.
+ nvmlMemory_t meminfo;
+ TRYOPT(nvmlDeviceGetMemoryInfo(dev, &meminfo))
+ if (nv_status == NVML_SUCCESS) {
+ nvml_submit_gauge(dev_name, "memory", "used", meminfo.used);
+ nvml_submit_gauge(dev_name, "memory", "free", meminfo.free);
+ }
+
+ nvmlUtilization_t utilization;
+ TRYOPT(nvmlDeviceGetUtilizationRates(dev, &utilization))
+ if (nv_status == NVML_SUCCESS)
+ nvml_submit_gauge(dev_name, "percent", "gpu_used", utilization.gpu);
+
+ unsigned int fan_speed;
+ TRYOPT(nvmlDeviceGetFanSpeed(dev, &fan_speed))
+ if (nv_status == NVML_SUCCESS)
+ nvml_submit_gauge(dev_name, "fanspeed", NULL, fan_speed);
+
+ unsigned int core_temp;
+ TRYOPT(nvmlDeviceGetTemperature(dev, NVML_TEMPERATURE_GPU, &core_temp))
+ if (nv_status == NVML_SUCCESS)
+ nvml_submit_gauge(dev_name, "temperature", "core", core_temp);
+
+ unsigned int sm_clk_mhz;
+ TRYOPT(nvmlDeviceGetClockInfo(dev, NVML_CLOCK_SM, &sm_clk_mhz))
+ if (nv_status == NVML_SUCCESS)
+ nvml_submit_gauge(dev_name, "frequency", "multiprocessor",
+ 1e6 * sm_clk_mhz);
+
+ unsigned int mem_clk_mhz;
+ TRYOPT(nvmlDeviceGetClockInfo(dev, NVML_CLOCK_MEM, &mem_clk_mhz))
+ if (nv_status == NVML_SUCCESS)
+ nvml_submit_gauge(dev_name, "frequency", "memory", 1e6 * mem_clk_mhz);
+
+ unsigned int power_mW;
+ TRYOPT(nvmlDeviceGetPowerUsage(dev, &power_mW))
+ if (nv_status == NVML_SUCCESS)
+ nvml_submit_gauge(dev_name, "power", NULL, 1e-3 * power_mW);
+
+ continue;
+
+ // Failures here indicate transient errors or removal of GPU. In either
+ // case it will either be resolved or the GPU will no longer be enumerated
+ // the next time round.
+ catch : WARNING(PLUGIN_NAME
+ ": NVML call \"%s\" failed (%d) on dev at index %d!",
+ nv_errline, nv_status, ix);
+ continue;
+ }
+
+ return 0;
+
+// Failures here indicate serious misconfiguration; we bail out totally.
+catch_nocount:
+ ERROR(PLUGIN_NAME ": Failed to enumerate NVIDIA GPUs (\"%s\" returned %d)",
+ nv_errline, nv_status);
+ return -1;
+}
+
+void module_register(void) {
+ plugin_register_init(PLUGIN_NAME, nvml_init);
+ plugin_register_config(PLUGIN_NAME, nvml_config, config_keys, n_config_keys);
+ plugin_register_read(PLUGIN_NAME, nvml_read);
+ plugin_register_shutdown(PLUGIN_NAME, nvml_shutdown);
+}
* Florian octo Forster <octo at collectd.org>
**/
+#ifdef WIN32
+#include "gnulib_config.h"
+#include <winsock2.h>
+#endif
+
#include "config.h"
#if !defined(__GNUC__) || !__GNUC__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/socket.h>
#include <sys/types.h>
-#include <sys/un.h>
#include <unistd.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
#include "collectd/client.h"
/* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11
#endif
#endif
+#ifdef WIN32
+#define AI_ADDRCONFIG 0
+#endif
+
/* Secure/static macros. They work like `strcpy' and `strcat', but assure null
* termination. They work for static buffers only, because they use `sizeof'.
* The `SSTRCATF' combines the functionality of `snprintf' and `strcat' which
static int lcc_open_unixsocket(lcc_connection_t *c, const char *path) /* {{{ */
{
+#ifdef WIN32
+ lcc_set_errno(c, ENOTSUP);
+ return -1;
+#else
struct sockaddr_un sa = {0};
int fd;
int status;
}
return 0;
+#endif /* WIN32 */
} /* }}} int lcc_open_unixsocket */
static int lcc_open_netsocket(lcc_connection_t *c, /* {{{ */
#include <inttypes.h>
#include <stdint.h>
+#ifdef WIN32
+extern unsigned int if_nametoindex(const char *interface_name);
+#endif
+
#define NET_DEFAULT_V4_ADDR "239.192.74.66"
#define NET_DEFAULT_V6_ADDR "ff18::efc0:4a42"
#define NET_DEFAULT_PORT "25826"
/* Configure servers */
int lcc_server_set_ttl(lcc_server_t *srv, uint8_t ttl);
-int lcc_server_set_interface(lcc_server_t *srv, char const *interface);
+int lcc_server_set_interface(lcc_server_t *srv, char const *iface);
int lcc_server_set_security_level(lcc_server_t *srv, lcc_security_level_t level,
const char *username, const char *password);
/* interface is the name of the interface to use when subscribing to a
* multicast group. Has no effect when using unicast. */
- char *interface;
+ char *iface;
} lcc_listener_t;
/* lcc_listen_and_write listens on the provided UDP socket (or opens one using
#include <net/if.h>
#endif
+#ifdef WIN32
+#define AI_ADDRCONFIG 0
+#endif
+
#include "collectd/network.h"
#include "collectd/network_buffer.h"
return 0;
} /* }}} int lcc_server_set_ttl */
-int lcc_server_set_interface(lcc_server_t *srv, char const *interface) /* {{{ */
+int lcc_server_set_interface(lcc_server_t *srv, char const *iface) /* {{{ */
{
unsigned int if_index;
int status;
- if ((srv == NULL) || (interface == NULL))
+ if ((srv == NULL) || (iface == NULL))
return EINVAL;
- if_index = if_nametoindex(interface);
+ if_index = if_nametoindex(iface);
if (if_index == 0)
return ENOENT;
/* else: Not a multicast interface. */
#if defined(SO_BINDTODEVICE)
- status = setsockopt(srv->fd, SOL_SOCKET, SO_BINDTODEVICE, interface,
- (socklen_t)(strlen(interface) + 1));
+ status = setsockopt(srv->fd, SOL_SOCKET, SO_BINDTODEVICE, iface,
+ (socklen_t)(strlen(iface) + 1));
if (status != 0)
return -1;
#endif
* Florian octo Forster <octo at collectd.org>
**/
+#ifdef WIN32
+#include "gnulib_config.h"
+#endif
+
#include "config.h"
#include <arpa/inet.h> /* htons */
* Florian octo Forster <octo at collectd.org>
**/
+#ifdef WIN32
+#include "gnulib_config.h"
+#endif
+
#include "config.h"
#if !defined(__GNUC__) || !__GNUC__
#include <stdio.h>
#define DEBUG(...) printf(__VA_ARGS__)
+#ifdef WIN32
+#include <ws2tcpip.h>
+#define AI_ADDRCONFIG 0
+#endif
+
static bool is_multicast(struct addrinfo const *ai) {
if (ai->ai_family == AF_INET) {
struct sockaddr_in *addr = (struct sockaddr_in *)ai->ai_addr;
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),
+ .imr_ifindex = if_nametoindex(srv->iface),
};
#else
+#ifdef WIN32
struct ip_mreq mreq = {
+ .imr_interface.s_addr = INADDR_ANY,
.imr_multiaddr.s_addr = sa->sin_addr.s_addr,
};
-#endif
+#else
+ struct ip_mreq mreq = {
+ .imr_multiaddr.s_addr = sa->sin_addr.s_addr,
+ };
+#endif /* WIN32 */
+#endif /* HAVE_STRUCT_IP_MREQN_IMR_IFINDEX */
status = setsockopt(srv->conn, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
if (status == -1)
return errno;
struct ipv6_mreq mreq6 = {
- .ipv6mr_interface = if_nametoindex(srv->interface),
+ .ipv6mr_interface = if_nametoindex(srv->iface),
};
memmove(&mreq6.ipv6mr_multiaddr, &sa->sin6_addr, sizeof(struct in6_addr));
*/
%{
+#ifdef WIN32
+#include "gnulib_config.h"
+#include "config.h"
+#endif
+
#include <stdlib.h>
#include <string.h>
#include "oconfig.h"
int port; /* for Modbus/TCP */
int baudrate; /* for Modbus/RTU */
mb_conntype_t conntype;
- cdtime_t interval;
mb_slave_t *slaves;
size_t slaves_num;
if ((host == NULL) || (slave == NULL) || (data == NULL))
return EINVAL;
- if (host->interval == 0)
- host->interval = plugin_get_interval();
-
if (slave->instance[0] == 0)
snprintf(slave->instance, sizeof(slave->instance), "slave_%i", slave->id);
vl.values = &value;
vl.values_len = 1;
- vl.interval = host->interval;
sstrncpy(vl.host, host->host, sizeof(vl.host));
sstrncpy(vl.plugin, "modbus", sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, slave->instance, sizeof(vl.plugin_instance));
static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */
{
+ cdtime_t interval = 0;
mb_host_t *host;
int status;
} else if (strcasecmp("Baudrate", child->key) == 0)
status = cf_util_get_int(child, &host->baudrate);
else if (strcasecmp("Interval", child->key) == 0)
- status = cf_util_get_cdtime(child, &host->interval);
+ status = cf_util_get_cdtime(child, &interval);
else if (strcasecmp("Slave", child->key) == 0)
/* Don't set status: Gracefully continue if a slave fails. */
mb_config_add_slave(host, child);
plugin_register_complex_read(/* group = */ NULL, name,
/* callback = */ mb_read,
- /* interval = */ host->interval,
+ /* interval = */ interval,
&(user_data_t){
.data = host, .free_func = host_free,
});
/* 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
+#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_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_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
+#define MSR_DRAM_POWER_LIMIT 0x00000618
+#define MSR_DRAM_ENERGY_STATUS 0x00000619
+#define MSR_UNCORE_FREQ_SCALING 0x00000621
+#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_TSC 0x00000010
+#define MSR_SMI_COUNT 0x00000034
-#define MSR_IA32_THERM_STATUS 0x0000019c
+#define MSR_IA32_MPERF 0x000000e7
+#define MSR_IA32_APERF 0x000000e8
-#define MSR_IA32_TEMPERATURE_TARGET 0x000001a2
+#define MSR_IA32_THERM_STATUS 0x0000019c
-#define MSR_IA32_PACKAGE_THERM_STATUS 0x000001b1
+#define MSR_IA32_MISC_ENABLE 0x000001a0
+#define MSR_IA32_TEMPERATURE_TARGET 0x000001a2
+#define MSR_IA32_PACKAGE_THERM_STATUS 0x000001b1
#endif /* _ASM_X86_MSR_INDEX_H */
#else
int loop = 0;
#endif
- int yes = 1;
/* allow multiple sockets to use the same PORT number */
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) == -1) {
ERROR("network plugin: setsockopt (reuseaddr): %s", STRERRNO);
return -1;
}
static void nfs_read_linux(FILE *fh, const char *inst) {
char buffer[1024];
- char *fields[64];
+ // The stats line is prefixed with type and number of fields, thus plus 2
+ char *fields[MAX(NFS4_SERVER_MAX_PROC, NFS4_CLIENT_MAX_PROC) + 2];
int fields_num = 0;
if (fh == NULL)
if (status != 0) {
ERROR("ntpd plugin: ntpd_do_query (REQ_GET_KERNEL) failed with status %i",
status);
+ free(ik);
return status;
} else if ((ik == NULL) || (ik_num == 0) || (ik_size == 0)) {
ERROR("ntpd plugin: ntpd_do_query returned unexpected data. "
"(ik = %p; ik_num = %i; ik_size = %i)",
(void *)ik, ik_num, ik_size);
+ free(ik);
return -1;
}
ERROR(
"ntpd plugin: ntpd_do_query (REQ_PEER_LIST_SUM) failed with status %i",
status);
+ free(ps);
return status;
} else if ((ps == NULL) || (ps_num == 0) || (ps_size == 0)) {
ERROR("ntpd plugin: ntpd_do_query returned unexpected data. "
"(ps = %p; ps_num = %i; ps_size = %i)",
(void *)ps, ps_num, ps_size);
+ free(ps);
return -1;
}
status = udb_query_prepare_result(
q, prep_area, (db->host != NULL) ? db->host : hostname_g,
/* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "oracle",
- db->name, column_names, column_num,
- /* interval = */ 0);
+ db->name, column_names, column_num);
if (status != 0) {
ERROR("oracle plugin: o_read_database_query (%s, %s): "
"udb_query_prepare_result failed.",
struct bridge_list_s *next; /* Next bridge*/
} bridge_list_t;
+#define cnt_str(x) [x] = #x
+
static const char *const iface_counter_table[IFACE_COUNTER_COUNT] = {
- [collisions] = "collisions",
- [rx_bytes] = "rx_bytes",
- [rx_crc_err] = "rx_crc_err",
- [rx_dropped] = "rx_dropped",
- [rx_errors] = "rx_errors",
- [rx_frame_err] = "rx_frame_err",
- [rx_over_err] = "rx_over_err",
- [rx_packets] = "rx_packets",
- [tx_bytes] = "tx_bytes",
- [tx_dropped] = "tx_dropped",
- [tx_errors] = "tx_errors",
- [tx_packets] = "tx_packets",
- [rx_1_to_64_packets] = "rx_1_to_64_packets",
- [rx_65_to_127_packets] = "rx_65_to_127_packets",
- [rx_128_to_255_packets] = "rx_128_to_255_packets",
- [rx_256_to_511_packets] = "rx_256_to_511_packets",
- [rx_512_to_1023_packets] = "rx_512_to_1023_packets",
- [rx_1024_to_1522_packets] = "rx_1024_to_1518_packets",
- [rx_1523_to_max_packets] = "rx_1523_to_max_packets",
- [tx_1_to_64_packets] = "tx_1_to_64_packets",
- [tx_65_to_127_packets] = "tx_65_to_127_packets",
- [tx_128_to_255_packets] = "tx_128_to_255_packets",
- [tx_256_to_511_packets] = "tx_256_to_511_packets",
- [tx_512_to_1023_packets] = "tx_512_to_1023_packets",
- [tx_1024_to_1522_packets] = "tx_1024_to_1518_packets",
- [tx_1523_to_max_packets] = "tx_1523_to_max_packets",
- [tx_multicast_packets] = "tx_multicast_packets",
- [rx_broadcast_packets] = "rx_broadcast_packets",
- [tx_broadcast_packets] = "tx_broadcast_packets",
- [rx_undersized_errors] = "rx_undersized_errors",
- [rx_oversize_errors] = "rx_oversize_errors",
- [rx_fragmented_errors] = "rx_fragmented_errors",
- [rx_jabber_errors] = "rx_jabber_errors",
+ cnt_str(collisions),
+ cnt_str(rx_bytes),
+ cnt_str(rx_crc_err),
+ cnt_str(rx_dropped),
+ cnt_str(rx_errors),
+ cnt_str(rx_frame_err),
+ cnt_str(rx_over_err),
+ cnt_str(rx_packets),
+ cnt_str(tx_bytes),
+ cnt_str(tx_dropped),
+ cnt_str(tx_errors),
+ cnt_str(tx_packets),
+ cnt_str(rx_1_to_64_packets),
+ cnt_str(rx_65_to_127_packets),
+ cnt_str(rx_128_to_255_packets),
+ cnt_str(rx_256_to_511_packets),
+ cnt_str(rx_512_to_1023_packets),
+ cnt_str(rx_1024_to_1522_packets),
+ cnt_str(rx_1523_to_max_packets),
+ cnt_str(tx_1_to_64_packets),
+ cnt_str(tx_65_to_127_packets),
+ cnt_str(tx_128_to_255_packets),
+ cnt_str(tx_256_to_511_packets),
+ cnt_str(tx_512_to_1023_packets),
+ cnt_str(tx_1024_to_1522_packets),
+ cnt_str(tx_1523_to_max_packets),
+ cnt_str(tx_multicast_packets),
+ cnt_str(rx_broadcast_packets),
+ cnt_str(tx_broadcast_packets),
+ cnt_str(rx_undersized_errors),
+ cnt_str(rx_oversize_errors),
+ cnt_str(rx_fragmented_errors),
+ cnt_str(rx_jabber_errors),
};
+#undef cnt_str
+
/* Entry into the list of network bridges */
static bridge_list_t *g_bridge_list_head;
ovs_stats_submit_two(devname, "if_packets", "512_to_1023_packets",
port->stats[rx_512_to_1023_packets],
port->stats[tx_512_to_1023_packets], meta);
- ovs_stats_submit_two(devname, "if_packets", "1024_to_1518_packets",
+ ovs_stats_submit_two(devname, "if_packets", "1024_to_1522_packets",
port->stats[rx_1024_to_1522_packets],
port->stats[tx_1024_to_1522_packets], meta);
ovs_stats_submit_two(devname, "if_packets", "1523_to_max_packets",
--- /dev/null
+/**
+ * collectd - src/pcie_errors.c
+ *
+ * Copyright(c) 2018 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:
+ * Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
+ **/
+
+#include "collectd.h"
+
+#include "common.h"
+#include "utils_llist.h"
+
+#include <linux/pci_regs.h>
+
+#define PCIE_ERRORS_PLUGIN "pcie_errors"
+#define PCIE_DEFAULT_PROCDIR "/proc/bus/pci"
+#define PCIE_DEFAULT_SYSFSDIR "/sys/bus/pci"
+#define PCIE_NAME_LEN 512
+#define PCIE_BUFF_SIZE 1024
+
+#define PCIE_ERROR "pcie_error"
+#define PCIE_SEV_CE "correctable"
+#define PCIE_SEV_FATAL "fatal"
+#define PCIE_SEV_NOFATAL "non_fatal"
+
+#define PCIE_DEV(x) (((x) >> 3) & 0x1f)
+#define PCIE_FN(x) ((x)&0x07)
+
+#define PCIE_ECAP_OFFSET 0x100 /* ECAP always begin at offset 0x100 */
+
+typedef struct pcie_config_s {
+ bool use_sysfs;
+ bool notif_masked;
+ bool persistent;
+ char access_dir[PATH_MAX];
+} pcie_config_t;
+
+typedef struct pcie_device_s {
+ int fd;
+ int domain;
+ uint8_t bus;
+ uint8_t device;
+ uint8_t function;
+ int cap_exp;
+ int ecap_aer;
+ uint16_t device_status;
+ uint32_t correctable_errors;
+ uint32_t uncorrectable_errors;
+} pcie_device_t;
+
+typedef struct pcie_fops_s {
+ int (*list_devices)(llist_t *dev_list);
+ int (*open)(pcie_device_t *dev);
+ void (*close)(pcie_device_t *dev);
+ int (*read)(pcie_device_t *dev, void *buff, int size, int pos);
+} pcie_fops_t;
+
+typedef struct pcie_error_s {
+ int mask;
+ const char *desc;
+} pcie_error_t;
+
+static llist_t *pcie_dev_list;
+static pcie_config_t pcie_config = {.access_dir = "", .use_sysfs = true};
+static pcie_fops_t pcie_fops;
+
+/* Device Error Status */
+static const pcie_error_t pcie_base_errors[] = {
+ {PCI_EXP_DEVSTA_CED, "Correctable Error"},
+ {PCI_EXP_DEVSTA_NFED, "Non-Fatal Error"},
+ {PCI_EXP_DEVSTA_FED, "Fatal Error"},
+ {PCI_EXP_DEVSTA_URD, "Unsupported Request"}};
+static const int pcie_base_errors_num = STATIC_ARRAY_SIZE(pcie_base_errors);
+
+/* Uncorrectable Error Status */
+static const pcie_error_t pcie_aer_ues[] = {
+#ifdef PCI_ERR_UNC_DLP
+ {PCI_ERR_UNC_DLP, "Data Link Protocol"},
+#endif
+#ifdef PCI_ERR_UNC_SURPDN
+ {PCI_ERR_UNC_SURPDN, "Surprise Down"},
+#endif
+#ifdef PCI_ERR_UNC_POISON_TLP
+ {PCI_ERR_UNC_POISON_TLP, "Poisoned TLP"},
+#endif
+#ifdef PCI_ERR_UNC_FCP
+ {PCI_ERR_UNC_FCP, "Flow Control Protocol"},
+#endif
+#ifdef PCI_ERR_UNC_COMP_TIME
+ {PCI_ERR_UNC_COMP_TIME, "Completion Timeout"},
+#endif
+#ifdef PCI_ERR_UNC_COMP_ABORT
+ {PCI_ERR_UNC_COMP_ABORT, "Completer Abort"},
+#endif
+#ifdef PCI_ERR_UNC_UNX_COMP
+ {PCI_ERR_UNC_UNX_COMP, "Unexpected Completion"},
+#endif
+#ifdef PCI_ERR_UNC_RX_OVER
+ {PCI_ERR_UNC_RX_OVER, "Receiver Overflow"},
+#endif
+#ifdef PCI_ERR_UNC_MALF_TLP
+ {PCI_ERR_UNC_MALF_TLP, "Malformed TLP"},
+#endif
+#ifdef PCI_ERR_UNC_ECRC
+ {PCI_ERR_UNC_ECRC, "ECRC Error Status"},
+#endif
+#ifdef PCI_ERR_UNC_UNSUP
+ {PCI_ERR_UNC_UNSUP, "Unsupported Request"},
+#endif
+#ifdef PCI_ERR_UNC_ACSV
+ {PCI_ERR_UNC_ACSV, "ACS Violation"},
+#endif
+#ifdef PCI_ERR_UNC_INTN
+ {PCI_ERR_UNC_INTN, "Internal"},
+#endif
+#ifdef PCI_ERR_UNC_MCBTLP
+ {PCI_ERR_UNC_MCBTLP, "MC blocked TLP"},
+#endif
+#ifdef PCI_ERR_UNC_ATOMEG
+ {PCI_ERR_UNC_ATOMEG, "Atomic egress blocked"},
+#endif
+#ifdef PCI_ERR_UNC_TLPPRE
+ {PCI_ERR_UNC_TLPPRE, "TLP prefix blocked"},
+#endif
+};
+static const int pcie_aer_ues_num = STATIC_ARRAY_SIZE(pcie_aer_ues);
+
+/* Correctable Error Status */
+static const pcie_error_t pcie_aer_ces[] = {
+#ifdef PCI_ERR_COR_RCVR
+ {PCI_ERR_COR_RCVR, "Receiver Error Status"},
+#endif
+#ifdef PCI_ERR_COR_BAD_TLP
+ {PCI_ERR_COR_BAD_TLP, "Bad TLP Status"},
+#endif
+#ifdef PCI_ERR_COR_BAD_DLLP
+ {PCI_ERR_COR_BAD_DLLP, "Bad DLLP Status"},
+#endif
+#ifdef PCI_ERR_COR_REP_ROLL
+ {PCI_ERR_COR_REP_ROLL, "REPLAY_NUM Rollover"},
+#endif
+#ifdef PCI_ERR_COR_REP_TIMER
+ {PCI_ERR_COR_REP_TIMER, "Replay Timer Timeout"},
+#endif
+#ifdef PCI_ERR_COR_ADV_NFAT
+ {PCI_ERR_COR_ADV_NFAT, "Advisory Non-Fatal"},
+#endif
+#ifdef PCI_ERR_COR_INTERNAL
+ {PCI_ERR_COR_INTERNAL, "Corrected Internal"},
+#endif
+#ifdef PCI_ERR_COR_LOG_OVER
+ {PCI_ERR_COR_LOG_OVER, "Header Log Overflow"},
+#endif
+};
+static const int pcie_aer_ces_num = STATIC_ARRAY_SIZE(pcie_aer_ces);
+
+static int pcie_add_device(llist_t *list, int domain, uint8_t bus,
+ uint8_t device, uint8_t fn) {
+ llentry_t *entry;
+ pcie_device_t *dev = calloc(1, sizeof(*dev));
+ if (dev == NULL) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Failed to allocate device");
+ return -ENOMEM;
+ }
+
+ dev->domain = domain;
+ dev->bus = bus;
+ dev->device = device;
+ dev->function = fn;
+ dev->cap_exp = -1;
+ dev->ecap_aer = -1;
+ entry = llentry_create(NULL, dev);
+ if (entry == NULL) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Failed to create llentry");
+ sfree(dev);
+ return -ENOMEM;
+ }
+ llist_append(list, entry);
+
+ DEBUG(PCIE_ERRORS_PLUGIN ": pci device added to list: %04x:%02x:%02x.%d",
+ domain, bus, device, fn);
+ return 0;
+}
+
+static void pcie_clear_list(llist_t *list) {
+ if (list == NULL)
+ return;
+
+ for (llentry_t *e = llist_head(list); e != NULL; e = e->next)
+ sfree(e->value);
+
+ llist_destroy(list);
+}
+
+static int pcie_list_devices_proc(llist_t *dev_list) {
+ FILE *fd;
+ char file_name[PCIE_NAME_LEN];
+ char buf[PCIE_BUFF_SIZE];
+ unsigned int i = 0;
+ int ret = 0;
+
+ if (dev_list == NULL)
+ return -EINVAL;
+
+ ret = snprintf(file_name, sizeof(file_name), "%s/devices",
+ pcie_config.access_dir);
+ if (ret < 1 || (size_t)ret >= sizeof(file_name)) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
+ pcie_config.access_dir, ret);
+ return -EINVAL;
+ }
+ fd = fopen(file_name, "r");
+ if (!fd) {
+ char errbuf[PCIE_BUFF_SIZE];
+ ERROR(PCIE_ERRORS_PLUGIN ": Cannot open file %s to get devices list: %s",
+ file_name, sstrerror(errno, errbuf, sizeof(errbuf)));
+ return -ENOENT;
+ }
+
+ while (fgets(buf, sizeof(buf), fd)) {
+ unsigned int slot;
+
+ if (sscanf(buf, "%x", &slot) != 1) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Failed to read line %u from %s", i + 1,
+ file_name);
+ continue;
+ }
+
+ uint8_t bus = slot >> 8U;
+ uint8_t dev = PCIE_DEV(slot);
+ uint8_t fn = PCIE_FN(slot);
+ ret = pcie_add_device(dev_list, 0, bus, dev, fn);
+ if (ret)
+ break;
+
+ ++i;
+ }
+
+ fclose(fd);
+ return ret;
+}
+
+static int pcie_list_devices_sysfs(llist_t *dev_list) {
+ DIR *dir;
+ struct dirent *item;
+ char dir_name[PCIE_NAME_LEN];
+ int ret = 0;
+
+ if (dev_list == NULL)
+ return -EINVAL;
+
+ ret = snprintf(dir_name, sizeof(dir_name), "%s/devices",
+ pcie_config.access_dir);
+ if (ret < 1 || (size_t)ret >= sizeof(dir_name)) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
+ pcie_config.access_dir, ret);
+ return -EINVAL;
+ }
+ dir = opendir(dir_name);
+ if (!dir) {
+ char errbuf[PCIE_BUFF_SIZE];
+ ERROR(PCIE_ERRORS_PLUGIN ": Cannot open dir %s to get devices list: %s",
+ dir_name, sstrerror(errno, errbuf, sizeof(errbuf)));
+ return -ENOENT;
+ }
+
+ while ((item = readdir(dir))) {
+ unsigned int dom, bus, dev;
+ int fn;
+
+ /* Omit special non-device entries */
+ if (item->d_name[0] == '.')
+ continue;
+
+ if (sscanf(item->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &fn) != 4) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Failed to parse entry %s", item->d_name);
+ continue;
+ }
+
+ ret = pcie_add_device(dev_list, dom, bus, dev, fn);
+ if (ret)
+ break;
+ }
+
+ closedir(dir);
+ return ret;
+}
+
+static void pcie_close(pcie_device_t *dev) {
+ if (close(dev->fd) == -1) {
+ char errbuf[PCIE_BUFF_SIZE];
+ ERROR(PCIE_ERRORS_PLUGIN ": Failed to close %04x:%02x:%02x.%d, fd=%d: %s",
+ dev->domain, dev->bus, dev->device, dev->function, dev->fd,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ }
+
+ dev->fd = -1;
+}
+
+static int pcie_open(pcie_device_t *dev, const char *name) {
+ dev->fd = open(name, O_RDONLY);
+ if (dev->fd == -1) {
+ char errbuf[PCIE_BUFF_SIZE];
+ ERROR(PCIE_ERRORS_PLUGIN ": Failed to open file %s: %s", name,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int pcie_open_proc(pcie_device_t *dev) {
+ char file_name[PCIE_NAME_LEN];
+
+ int ret =
+ snprintf(file_name, sizeof(file_name), "%s/%02x/%02x.%d",
+ pcie_config.access_dir, dev->bus, dev->device, dev->function);
+ if (ret < 1 || (size_t)ret >= sizeof(file_name)) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
+ pcie_config.access_dir, ret);
+ return -EINVAL;
+ }
+
+ return pcie_open(dev, file_name);
+}
+
+static int pcie_open_sysfs(pcie_device_t *dev) {
+ char file_name[PCIE_NAME_LEN];
+
+ int ret =
+ snprintf(file_name, sizeof(file_name),
+ "%s/devices/%04x:%02x:%02x.%d/config", pcie_config.access_dir,
+ dev->domain, dev->bus, dev->device, dev->function);
+ if (ret < 1 || (size_t)ret >= sizeof(file_name)) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
+ pcie_config.access_dir, ret);
+ return -EINVAL;
+ }
+
+ return pcie_open(dev, file_name);
+}
+
+static int pcie_read(pcie_device_t *dev, void *buff, int size, int pos) {
+ int len = pread(dev->fd, buff, size, pos);
+ if (len == size)
+ return 0;
+
+ if (len == -1) {
+ char errbuf[PCIE_BUFF_SIZE];
+ ERROR(PCIE_ERRORS_PLUGIN ": Failed to read %04x:%02x:%02x.%d at pos %d: %s",
+ dev->domain, dev->bus, dev->device, dev->function, pos,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ } else {
+ ERROR(PCIE_ERRORS_PLUGIN
+ ": %04x:%02x:%02x.%d Read only %d bytes, should be %d",
+ dev->domain, dev->bus, dev->device, dev->function, len, size);
+ }
+ return -1;
+}
+
+static uint8_t pcie_read8(pcie_device_t *dev, int pos) {
+ uint8_t value;
+ if (pcie_fops.read(dev, &value, 1, pos))
+ return 0;
+ return value;
+}
+
+static uint16_t pcie_read16(pcie_device_t *dev, int pos) {
+ uint16_t value;
+ if (pcie_fops.read(dev, &value, 2, pos))
+ return 0;
+ return value;
+}
+
+static uint32_t pcie_read32(pcie_device_t *dev, int pos) {
+ uint32_t value;
+ if (pcie_fops.read(dev, &value, 4, pos))
+ return 0;
+ return value;
+}
+
+static void pcie_dispatch_notification(pcie_device_t *dev, notification_t *n,
+ const char *type,
+ const char *type_instance) {
+ sstrncpy(n->host, hostname_g, sizeof(n->host));
+ snprintf(n->plugin_instance, sizeof(n->plugin_instance), "%04x:%02x:%02x.%d",
+ dev->domain, dev->bus, dev->device, dev->function);
+ sstrncpy(n->type, type, sizeof(n->type));
+ sstrncpy(n->type_instance, type_instance, sizeof(n->type_instance));
+
+ plugin_dispatch_notification(n);
+}
+
+/* Report errors found in AER Correctable Error Status register */
+static void pcie_dispatch_correctable_errors(pcie_device_t *dev,
+ uint32_t errors, uint32_t masked) {
+ for (int i = 0; i < pcie_aer_ces_num; i++) {
+ const pcie_error_t *err = pcie_aer_ces + i;
+ notification_t n = {.severity = NOTIF_WARNING,
+ .time = cdtime(),
+ .plugin = PCIE_ERRORS_PLUGIN,
+ .meta = NULL};
+
+ /* If not specifically set by config option omit masked errors */
+ if (!pcie_config.notif_masked && (err->mask & masked))
+ continue;
+
+ if (err->mask & errors) {
+ /* Error already reported, notify only if persistent is set */
+ if (!pcie_config.persistent && (err->mask & dev->correctable_errors))
+ continue;
+
+ DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s set", dev->domain,
+ dev->bus, dev->device, dev->function, err->desc);
+ snprintf(n.message, sizeof(n.message), "Correctable Error set: %s",
+ err->desc);
+ pcie_dispatch_notification(dev, &n, PCIE_ERROR, PCIE_SEV_CE);
+
+ } else if (err->mask & dev->correctable_errors) {
+ DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s cleared", dev->domain,
+ dev->bus, dev->device, dev->function, err->desc);
+
+ n.severity = NOTIF_OKAY;
+ snprintf(n.message, sizeof(n.message), "Correctable Error cleared: %s",
+ err->desc);
+ pcie_dispatch_notification(dev, &n, PCIE_ERROR, PCIE_SEV_CE);
+ }
+ }
+}
+
+/* Report errors found in AER Uncorrectable Error Status register */
+static void pcie_dispatch_uncorrectable_errors(pcie_device_t *dev,
+ uint32_t errors, uint32_t masked,
+ uint32_t severity) {
+ for (int i = 0; i < pcie_aer_ues_num; i++) {
+ const pcie_error_t *err = pcie_aer_ues + i;
+ const char *type_instance =
+ (severity & err->mask) ? PCIE_SEV_FATAL : PCIE_SEV_NOFATAL;
+ notification_t n = {
+ .time = cdtime(), .plugin = PCIE_ERRORS_PLUGIN, .meta = NULL};
+
+ /* If not specifically set by config option omit masked errors */
+ if (!pcie_config.notif_masked && (err->mask & masked))
+ continue;
+
+ if (err->mask & errors) {
+ /* Error already reported, notify only if persistent is set */
+ if (!pcie_config.persistent && (err->mask & dev->uncorrectable_errors))
+ continue;
+
+ DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s(%s) set", dev->domain,
+ dev->bus, dev->device, dev->function, err->desc, type_instance);
+
+ n.severity = (severity & err->mask) ? NOTIF_FAILURE : NOTIF_WARNING;
+ snprintf(n.message, sizeof(n.message), "Uncorrectable(%s) Error set: %s",
+ type_instance, err->desc);
+ pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
+
+ } else if (err->mask & dev->uncorrectable_errors) {
+ DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s(%s) cleared",
+ dev->domain, dev->bus, dev->device, dev->function, err->desc,
+ type_instance);
+
+ n.severity = NOTIF_OKAY;
+ snprintf(n.message, sizeof(n.message),
+ "Uncorrectable(%s) Error cleared: %s", type_instance, err->desc);
+ pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
+ }
+ }
+}
+
+/* Find offset of PCI Express Capability Structure
+ * in PCI configuration space.
+ * Returns offset, -1 if not found.
+**/
+static int pcie_find_cap_exp(pcie_device_t *dev) {
+ int pos = pcie_read8(dev, PCI_CAPABILITY_LIST) & ~3;
+
+ while (pos) {
+ uint8_t id = pcie_read8(dev, pos + PCI_CAP_LIST_ID);
+
+ if (id == 0xff)
+ break;
+ if (id == PCI_CAP_ID_EXP)
+ return pos;
+
+ pos = pcie_read8(dev, pos + PCI_CAP_LIST_NEXT) & ~3;
+ }
+
+ DEBUG(PCIE_ERRORS_PLUGIN ": Cannot find CAP EXP for %04x:%02x:%02x.%d",
+ dev->domain, dev->bus, dev->device, dev->function);
+
+ return -1;
+}
+
+/* Find offset of Advanced Error Reporting Capability.
+ * Returns AER offset, -1 if not found.
+**/
+static int pcie_find_ecap_aer(pcie_device_t *dev) {
+ int pos = PCIE_ECAP_OFFSET;
+ uint32_t header = pcie_read32(dev, pos);
+ int id = PCI_EXT_CAP_ID(header);
+ int next = PCI_EXT_CAP_NEXT(header);
+
+ if (!id && !next)
+ return -1;
+
+ if (id == PCI_EXT_CAP_ID_ERR)
+ return pos;
+
+ while (next) {
+ if (next <= PCIE_ECAP_OFFSET)
+ break;
+
+ header = pcie_read32(dev, next);
+ id = PCI_EXT_CAP_ID(header);
+
+ if (id == PCI_EXT_CAP_ID_ERR)
+ return next;
+
+ next = PCI_EXT_CAP_NEXT(header);
+ }
+
+ return -1;
+}
+
+static void pcie_check_dev_status(pcie_device_t *dev, int pos) {
+ /* Read Device Status register with mask for errors only */
+ uint16_t new_status = pcie_read16(dev, pos + PCI_EXP_DEVSTA) & 0xf;
+
+ /* Check if anything new should be reported */
+ if (!(pcie_config.persistent && new_status) &&
+ (new_status == dev->device_status))
+ return;
+
+ /* Report errors found in Device Status register */
+ for (int i = 0; i < pcie_base_errors_num; i++) {
+ const pcie_error_t *err = pcie_base_errors + i;
+ const char *type_instance = (err->mask == PCI_EXP_DEVSTA_FED)
+ ? PCIE_SEV_FATAL
+ : (err->mask == PCI_EXP_DEVSTA_CED)
+ ? PCIE_SEV_CE
+ : PCIE_SEV_NOFATAL;
+ int severity =
+ (err->mask == PCI_EXP_DEVSTA_FED) ? NOTIF_FAILURE : NOTIF_WARNING;
+ notification_t n = {.severity = severity,
+ .time = cdtime(),
+ .plugin = PCIE_ERRORS_PLUGIN,
+ .meta = NULL};
+
+ if (err->mask & new_status) {
+ /* Error already reported, notify only if persistent is set */
+ if (!pcie_config.persistent && (err->mask & dev->device_status))
+ continue;
+
+ DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s set", dev->domain,
+ dev->bus, dev->device, dev->function, err->desc);
+ snprintf(n.message, sizeof(n.message), "Device Status Error set: %s",
+ err->desc);
+ pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
+
+ } else if (err->mask & dev->device_status) {
+ DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s cleared", dev->domain,
+ dev->bus, dev->device, dev->function, err->desc);
+ n.severity = NOTIF_OKAY;
+ snprintf(n.message, sizeof(n.message), "Device Status Error cleared: %s",
+ err->desc);
+ pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
+ }
+ }
+
+ dev->device_status = new_status;
+}
+
+static void pcie_check_aer(pcie_device_t *dev, int pos) {
+ /* Check for AER uncorrectable errors */
+ uint32_t errors = pcie_read32(dev, pos + PCI_ERR_UNCOR_STATUS);
+
+ if ((pcie_config.persistent && errors) ||
+ (errors != dev->uncorrectable_errors)) {
+ uint32_t masked = pcie_read32(dev, pos + PCI_ERR_UNCOR_MASK);
+ uint32_t severity = pcie_read32(dev, pos + PCI_ERR_UNCOR_SEVER);
+ pcie_dispatch_uncorrectable_errors(dev, errors, masked, severity);
+ }
+ dev->uncorrectable_errors = errors;
+
+ /* Check for AER correctable errors */
+ errors = pcie_read32(dev, pos + PCI_ERR_COR_STATUS);
+ if ((pcie_config.persistent && errors) ||
+ (errors != dev->correctable_errors)) {
+ uint32_t masked = pcie_read32(dev, pos + PCI_ERR_COR_MASK);
+ pcie_dispatch_correctable_errors(dev, errors, masked);
+ }
+ dev->correctable_errors = errors;
+}
+
+static int pcie_process_devices(llist_t *devs) {
+ int ret = 0;
+ if (devs == NULL)
+ return -1;
+
+ for (llentry_t *e = llist_head(devs); e != NULL; e = e->next) {
+ pcie_device_t *dev = e->value;
+
+ if (pcie_fops.open(dev) == 0) {
+ pcie_check_dev_status(dev, dev->cap_exp);
+ if (dev->ecap_aer != -1)
+ pcie_check_aer(dev, dev->ecap_aer);
+
+ pcie_fops.close(dev);
+ } else {
+ notification_t n = {.severity = NOTIF_FAILURE,
+ .time = cdtime(),
+ .message = "Failed to read device status",
+ .plugin = PCIE_ERRORS_PLUGIN,
+ .meta = NULL};
+ pcie_dispatch_notification(dev, &n, "", "");
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+/* This function is to be called during init to filter out no pcie devices */
+static void pcie_preprocess_devices(llist_t *devs) {
+ llentry_t *e_next;
+
+ if (devs == NULL)
+ return;
+
+ for (llentry_t *e = llist_head(devs); e != NULL; e = e_next) {
+ pcie_device_t *dev = e->value;
+ bool del = false;
+
+ if (pcie_fops.open(dev) == 0) {
+ uint16_t status = pcie_read16(dev, PCI_STATUS);
+ if (status & PCI_STATUS_CAP_LIST)
+ dev->cap_exp = pcie_find_cap_exp(dev);
+
+ /* Every PCIe device must have Capability Structure */
+ if (dev->cap_exp == -1) {
+ DEBUG(PCIE_ERRORS_PLUGIN ": Not PCI Express device: %04x:%02x:%02x.%d",
+ dev->domain, dev->bus, dev->device, dev->function);
+ del = true;
+ } else {
+ dev->ecap_aer = pcie_find_ecap_aer(dev);
+ if (dev->ecap_aer == -1)
+ INFO(PCIE_ERRORS_PLUGIN
+ ": Device is not AER capable: %04x:%02x:%02x.%d",
+ dev->domain, dev->bus, dev->device, dev->function);
+ }
+
+ pcie_fops.close(dev);
+ } else {
+ ERROR(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: failed to open",
+ dev->domain, dev->bus, dev->device, dev->function);
+ del = true;
+ }
+
+ e_next = e->next;
+ if (del) {
+ sfree(dev);
+ llist_remove(devs, e);
+ llentry_destroy(e);
+ }
+ }
+}
+
+static int pcie_plugin_read(__attribute__((unused)) user_data_t *ud) {
+
+ if (pcie_process_devices(pcie_dev_list) < 0) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Failed to read devices state");
+ return -1;
+ }
+ return 0;
+}
+
+static void pcie_access_config(void) {
+ /* Set functions for register access to
+ * use proc or sysfs depending on config. */
+ if (pcie_config.use_sysfs) {
+ pcie_fops.list_devices = pcie_list_devices_sysfs;
+ pcie_fops.open = pcie_open_sysfs;
+ if (pcie_config.access_dir[0] == '\0')
+ sstrncpy(pcie_config.access_dir, PCIE_DEFAULT_SYSFSDIR,
+ sizeof(pcie_config.access_dir));
+ } else {
+ /* use proc */
+ pcie_fops.list_devices = pcie_list_devices_proc;
+ pcie_fops.open = pcie_open_proc;
+ if (pcie_config.access_dir[0] == '\0')
+ sstrncpy(pcie_config.access_dir, PCIE_DEFAULT_PROCDIR,
+ sizeof(pcie_config.access_dir));
+ }
+ /* Common functions */
+ pcie_fops.close = pcie_close;
+ pcie_fops.read = pcie_read;
+}
+
+static int pcie_plugin_config(oconfig_item_t *ci) {
+ int status = 0;
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Source", child->key) == 0) {
+ if ((child->values_num != 1) ||
+ (child->values[0].type != OCONFIG_TYPE_STRING)) {
+ status = -1;
+ } else if (strcasecmp("proc", child->values[0].value.string) == 0) {
+ pcie_config.use_sysfs = false;
+ } else if (strcasecmp("sysfs", child->values[0].value.string) != 0) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Allowed sources are 'proc' or 'sysfs'.");
+ status = -1;
+ }
+ } else if (strcasecmp("AccessDir", child->key) == 0) {
+ status = cf_util_get_string_buffer(child, pcie_config.access_dir,
+ sizeof(pcie_config.access_dir));
+ } else if (strcasecmp("ReportMasked", child->key) == 0) {
+ status = cf_util_get_boolean(child, &pcie_config.notif_masked);
+ } else if (strcasecmp("PersistentNotifications", child->key) == 0) {
+ status = cf_util_get_boolean(child, &pcie_config.persistent);
+ } else {
+ ERROR(PCIE_ERRORS_PLUGIN ": Invalid configuration option \"%s\".",
+ child->key);
+ status = -1;
+ break;
+ }
+
+ if (status) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Invalid configuration parameter \"%s\".",
+ child->key);
+ break;
+ }
+ }
+
+ return status;
+}
+
+static int pcie_shutdown(void) {
+ pcie_clear_list(pcie_dev_list);
+ pcie_dev_list = NULL;
+
+ return 0;
+}
+
+static int pcie_init(void) {
+
+ pcie_access_config();
+ pcie_dev_list = llist_create();
+ if (pcie_fops.list_devices(pcie_dev_list) != 0) {
+ ERROR(PCIE_ERRORS_PLUGIN ": Failed to find devices.");
+ pcie_shutdown();
+ return -1;
+ }
+ pcie_preprocess_devices(pcie_dev_list);
+ if (llist_size(pcie_dev_list) == 0) {
+ /* No any PCI Express devices were found on the system */
+ ERROR(PCIE_ERRORS_PLUGIN ": No PCIe devices found in %s",
+ pcie_config.access_dir);
+ pcie_shutdown();
+ return -1;
+ }
+
+ return 0;
+}
+
+void module_register(void) {
+ plugin_register_init(PCIE_ERRORS_PLUGIN, pcie_init);
+ plugin_register_complex_config(PCIE_ERRORS_PLUGIN, pcie_plugin_config);
+ plugin_register_complex_read(NULL, PCIE_ERRORS_PLUGIN, pcie_plugin_read, 0,
+ NULL);
+ plugin_register_shutdown(PCIE_ERRORS_PLUGIN, pcie_shutdown);
+}
--- /dev/null
+/**
+ * collectd - src/pcie_errors.c
+ *
+ * Copyright(c) 2018 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:
+ * Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
+ **/
+
+#define plugin_dispatch_notification plugin_dispatch_notification_pcie_test
+
+#include "pcie_errors.c" /* sic */
+#include "testing.h"
+
+#define TEST_DOMAIN 1
+#define TEST_BUS 5
+#define TEST_DEVICE 0xc
+#define TEST_FUNCTION 2
+#define TEST_DEVICE_STR "0001:05:0c.2"
+
+#define G_BUFF_LEN 4
+
+static notification_t last_notif;
+static char g_buff[G_BUFF_LEN];
+
+/* mock functions */
+int plugin_dispatch_notification_pcie_test(const notification_t *notif) {
+ last_notif = *notif;
+ return ENOTSUP;
+}
+
+ssize_t pread(__attribute__((unused)) int fd, void *buf, size_t count,
+ __attribute__((unused)) off_t offset) {
+ if (count == 0 || count > G_BUFF_LEN)
+ return -1;
+
+ memcpy(buf, g_buff, count);
+ return count;
+}
+/* end mock functions */
+
+DEF_TEST(clear_dev_list) {
+ pcie_clear_list(NULL);
+
+ llist_t *test_list = llist_create();
+ CHECK_NOT_NULL(test_list);
+
+ pcie_device_t *dev = calloc(1, sizeof(*dev));
+ CHECK_NOT_NULL(dev);
+
+ llentry_t *entry = llentry_create(NULL, dev);
+ CHECK_NOT_NULL(entry);
+
+ llist_append(test_list, entry);
+
+ for (llentry_t *e = llist_head(test_list); e != NULL; e = e->next) {
+ EXPECT_EQ_PTR(dev, e->value);
+ }
+
+ pcie_clear_list(test_list);
+
+ return 0;
+}
+
+DEF_TEST(add_to_list) {
+ llist_t *test_list = llist_create();
+ CHECK_NOT_NULL(test_list);
+
+ int ret = pcie_add_device(test_list, TEST_DOMAIN, TEST_BUS, TEST_DEVICE,
+ TEST_FUNCTION);
+ EXPECT_EQ_INT(0, ret);
+
+ llentry_t *e = llist_head(test_list);
+ CHECK_NOT_NULL(e);
+ OK(NULL == e->next);
+
+ pcie_device_t *dev = e->value;
+ CHECK_NOT_NULL(dev);
+ EXPECT_EQ_INT(TEST_DOMAIN, dev->domain);
+ EXPECT_EQ_INT(TEST_BUS, dev->bus);
+ EXPECT_EQ_INT(TEST_DEVICE, dev->device);
+ EXPECT_EQ_INT(TEST_FUNCTION, dev->function);
+ EXPECT_EQ_INT(-1, dev->cap_exp);
+ EXPECT_EQ_INT(-1, dev->ecap_aer);
+
+ pcie_clear_list(test_list);
+
+ return 0;
+}
+
+DEF_TEST(pcie_read) {
+ int ret;
+ pcie_device_t dev = {0};
+ uint32_t val = 0;
+ g_buff[0] = 4;
+ g_buff[1] = 3;
+ g_buff[2] = 2;
+ g_buff[3] = 1;
+
+ ret = pcie_read(&dev, &val, 1, 0);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(4, val);
+
+ ret = pcie_read(&dev, &val, 2, 0);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(0x304, val);
+
+ ret = pcie_read(&dev, &val, 3, 0);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(0x20304, val);
+
+ ret = pcie_read(&dev, &val, 4, 0);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(0x1020304, val);
+
+ ret = pcie_read(&dev, &val, G_BUFF_LEN + 1, 0);
+ EXPECT_EQ_INT(-1, ret);
+
+ pcie_fops.read = pcie_read;
+
+ uint8_t val8 = pcie_read8(&dev, 0);
+ EXPECT_EQ_INT(4, val8);
+
+ uint16_t val16 = pcie_read16(&dev, 0);
+ EXPECT_EQ_INT(0x304, val16);
+
+ uint32_t val32 = pcie_read32(&dev, 0);
+ EXPECT_EQ_INT(0x1020304, val32);
+
+ return 0;
+}
+
+DEF_TEST(dispatch_notification) {
+ pcie_device_t dev = {0, TEST_DOMAIN, TEST_BUS, TEST_DEVICE, TEST_FUNCTION,
+ 0, 0, 0, 0, 0};
+ cdtime_t t = cdtime();
+ notification_t n = {
+ .severity = 1, .time = t, .plugin = "pcie_errors_test", .meta = NULL};
+
+ pcie_dispatch_notification(&dev, &n, "test_type", "test_type_instance");
+ EXPECT_EQ_INT(1, last_notif.severity);
+ EXPECT_EQ_UINT64(t, last_notif.time);
+ EXPECT_EQ_STR("pcie_errors_test", last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(hostname_g, last_notif.host);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR("test_type", last_notif.type);
+ EXPECT_EQ_STR("test_type_instance", last_notif.type_instance);
+
+ return 0;
+}
+
+DEF_TEST(access_config) {
+ pcie_config.use_sysfs = 0;
+ pcie_access_config();
+ EXPECT_EQ_PTR(pcie_list_devices_proc, pcie_fops.list_devices);
+ EXPECT_EQ_PTR(pcie_open_proc, pcie_fops.open);
+ EXPECT_EQ_PTR(pcie_close, pcie_fops.close);
+ EXPECT_EQ_PTR(pcie_read, pcie_fops.read);
+ EXPECT_EQ_STR(PCIE_DEFAULT_PROCDIR, pcie_config.access_dir);
+
+ sstrncpy(pcie_config.access_dir, "Test", sizeof(pcie_config.access_dir));
+ pcie_access_config();
+ EXPECT_EQ_STR("Test", pcie_config.access_dir);
+
+ pcie_config.use_sysfs = 1;
+ pcie_access_config();
+ EXPECT_EQ_PTR(pcie_list_devices_sysfs, pcie_fops.list_devices);
+ EXPECT_EQ_PTR(pcie_open_sysfs, pcie_fops.open);
+ EXPECT_EQ_PTR(pcie_close, pcie_fops.close);
+ EXPECT_EQ_PTR(pcie_read, pcie_fops.read);
+ EXPECT_EQ_STR("Test", pcie_config.access_dir);
+
+ pcie_config.access_dir[0] = '\0';
+ pcie_access_config();
+ EXPECT_EQ_STR(PCIE_DEFAULT_SYSFSDIR, pcie_config.access_dir);
+
+ return 0;
+}
+
+DEF_TEST(plugin_config_fail) {
+ oconfig_item_t test_cfg_parent = {"pcie_errors", NULL, 0, NULL, NULL, 0};
+ char value_buff[256] = "procs";
+ char key_buff[256] = "Sources";
+ oconfig_value_t test_cfg_value = {{value_buff}, OCONFIG_TYPE_STRING};
+ oconfig_item_t test_cfg = {
+ key_buff, &test_cfg_value, 1, &test_cfg_parent, NULL, 0};
+
+ test_cfg_parent.children = &test_cfg;
+ test_cfg_parent.children_num = 1;
+
+ int ret = pcie_plugin_config(&test_cfg_parent);
+ EXPECT_EQ_INT(-1, ret);
+
+ sstrncpy(key_buff, "Source", sizeof(key_buff));
+ ret = pcie_plugin_config(&test_cfg_parent);
+ EXPECT_EQ_INT(-1, ret);
+
+ sstrncpy(value_buff, "proc", sizeof(value_buff));
+ test_cfg_value.type = OCONFIG_TYPE_NUMBER;
+ ret = pcie_plugin_config(&test_cfg_parent);
+ EXPECT_EQ_INT(-1, ret);
+
+ sstrncpy(key_buff, "AccessDir", sizeof(key_buff));
+ ret = pcie_plugin_config(&test_cfg_parent);
+ EXPECT_EQ_INT(-1, ret);
+
+ return 0;
+}
+
+DEF_TEST(plugin_config) {
+ oconfig_item_t test_cfg_parent = {"pcie_errors", NULL, 0, NULL, NULL, 0};
+ char value_buff[256] = "proc";
+ char key_buff[256] = "source";
+ oconfig_value_t test_cfg_value = {{value_buff}, OCONFIG_TYPE_STRING};
+ oconfig_item_t test_cfg = {
+ key_buff, &test_cfg_value, 1, &test_cfg_parent, NULL, 0};
+
+ test_cfg_parent.children = &test_cfg;
+ test_cfg_parent.children_num = 1;
+
+ pcie_config.use_sysfs = 1;
+ int ret = pcie_plugin_config(&test_cfg_parent);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(0, pcie_config.use_sysfs);
+
+ pcie_config.use_sysfs = 1;
+ sstrncpy(value_buff, "sysfs", sizeof(value_buff));
+ ret = pcie_plugin_config(&test_cfg_parent);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(1, pcie_config.use_sysfs);
+
+ sstrncpy(key_buff, "AccessDir", sizeof(key_buff));
+ sstrncpy(value_buff, "some/test/value", sizeof(value_buff));
+ ret = pcie_plugin_config(&test_cfg_parent);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR("some/test/value", pcie_config.access_dir);
+
+ memset(&test_cfg_value.value, 0, sizeof(test_cfg_value.value));
+ test_cfg_value.value.boolean = 1;
+ test_cfg_value.type = OCONFIG_TYPE_BOOLEAN;
+ sstrncpy(key_buff, "ReportMasked", sizeof(key_buff));
+ ret = pcie_plugin_config(&test_cfg_parent);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(1, pcie_config.notif_masked);
+
+ sstrncpy(key_buff, "PersistentNotifications", sizeof(key_buff));
+ ret = pcie_plugin_config(&test_cfg_parent);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(1, pcie_config.persistent);
+
+ return 0;
+}
+
+#define BAD_TLP_SET_MSG "Correctable Error set: Bad TLP Status"
+#define BAD_TLP_CLEAR_MSG "Correctable Error cleared: Bad TLP Status"
+
+DEF_TEST(dispatch_correctable_errors) {
+ pcie_device_t dev = {0, TEST_DOMAIN, TEST_BUS, TEST_DEVICE, TEST_FUNCTION,
+ 0, 0, 0, 0, 0};
+ pcie_config.notif_masked = 0;
+ pcie_config.persistent = 0;
+
+ pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+ ~(PCI_ERR_COR_BAD_TLP));
+ EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+ EXPECT_EQ_STR(BAD_TLP_SET_MSG, last_notif.message);
+
+ memset(&last_notif, 0, sizeof(last_notif));
+ dev.correctable_errors = PCI_ERR_COR_BAD_TLP;
+ pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+ ~(PCI_ERR_COR_BAD_TLP));
+ EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+ pcie_config.persistent = 1;
+ pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+ ~(PCI_ERR_COR_BAD_TLP));
+ EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+ EXPECT_EQ_STR(BAD_TLP_SET_MSG, last_notif.message);
+
+ memset(&last_notif, 0, sizeof(last_notif));
+ pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+ PCI_ERR_COR_BAD_TLP);
+ EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+ pcie_config.notif_masked = 1;
+ pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+ PCI_ERR_COR_BAD_TLP);
+ EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+ EXPECT_EQ_STR(BAD_TLP_SET_MSG, last_notif.message);
+
+ pcie_config.persistent = 0;
+ memset(&last_notif, 0, sizeof(last_notif));
+ pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+ PCI_ERR_COR_BAD_TLP);
+ EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+ dev.correctable_errors = 0;
+ pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+ PCI_ERR_COR_BAD_TLP);
+ EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+ EXPECT_EQ_STR(BAD_TLP_SET_MSG, last_notif.message);
+
+ pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+ ~(PCI_ERR_COR_BAD_TLP));
+ EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+ EXPECT_EQ_STR(BAD_TLP_SET_MSG, last_notif.message);
+
+ pcie_config.notif_masked = 0;
+ dev.correctable_errors = PCI_ERR_COR_BAD_TLP;
+ pcie_dispatch_correctable_errors(&dev, 0, ~(PCI_ERR_COR_BAD_TLP));
+ EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+ EXPECT_EQ_STR(BAD_TLP_CLEAR_MSG, last_notif.message);
+
+ return 0;
+}
+
+#define FCP_NF_SET_MSG \
+ "Uncorrectable(non_fatal) Error set: Flow Control Protocol"
+#define FCP_F_SET_MSG "Uncorrectable(fatal) Error set: Flow Control Protocol"
+#define FCP_NF_CLEAR_MSG \
+ "Uncorrectable(non_fatal) Error cleared: Flow Control Protocol"
+#define FCP_F_CLEAR_MSG \
+ "Uncorrectable(fatal) Error cleared: Flow Control Protocol"
+
+DEF_TEST(dispatch_uncorrectable_errors) {
+ pcie_device_t dev = {0, TEST_DOMAIN, TEST_BUS, TEST_DEVICE, TEST_FUNCTION,
+ 0, 0, 0, 0, 0};
+ pcie_config.notif_masked = 0;
+ pcie_config.persistent = 0;
+
+ pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, ~(PCI_ERR_UNC_FCP),
+ ~(PCI_ERR_UNC_FCP));
+ EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(FCP_NF_SET_MSG, last_notif.message);
+
+ pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, ~(PCI_ERR_UNC_FCP),
+ PCI_ERR_UNC_FCP);
+ EXPECT_EQ_INT(NOTIF_FAILURE, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(FCP_F_SET_MSG, last_notif.message);
+
+ memset(&last_notif, 0, sizeof(last_notif));
+ dev.uncorrectable_errors = PCI_ERR_UNC_FCP;
+ pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, ~(PCI_ERR_UNC_FCP),
+ PCI_ERR_UNC_FCP);
+ EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+ pcie_config.persistent = 1;
+ pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, ~(PCI_ERR_UNC_FCP),
+ PCI_ERR_UNC_FCP);
+ EXPECT_EQ_INT(NOTIF_FAILURE, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(FCP_F_SET_MSG, last_notif.message);
+
+ memset(&last_notif, 0, sizeof(last_notif));
+ pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, PCI_ERR_UNC_FCP,
+ PCI_ERR_UNC_FCP);
+ EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+ pcie_config.notif_masked = 1;
+ pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, PCI_ERR_UNC_FCP,
+ PCI_ERR_UNC_FCP);
+ EXPECT_EQ_INT(NOTIF_FAILURE, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(FCP_F_SET_MSG, last_notif.message);
+
+ pcie_config.persistent = 0;
+ dev.uncorrectable_errors = 0;
+ memset(&last_notif, 0, sizeof(last_notif));
+ pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, ~(PCI_ERR_UNC_FCP),
+ PCI_ERR_UNC_FCP);
+ EXPECT_EQ_INT(NOTIF_FAILURE, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(FCP_F_SET_MSG, last_notif.message);
+
+ pcie_config.notif_masked = 0;
+ dev.uncorrectable_errors = PCI_ERR_UNC_FCP;
+ pcie_dispatch_uncorrectable_errors(&dev, 0, ~(PCI_ERR_UNC_FCP),
+ ~(PCI_ERR_UNC_FCP));
+ EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(FCP_NF_CLEAR_MSG, last_notif.message);
+
+ memset(&last_notif, 0, sizeof(last_notif));
+ pcie_dispatch_uncorrectable_errors(&dev, 0, ~(PCI_ERR_UNC_FCP),
+ PCI_ERR_UNC_FCP);
+ EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(FCP_F_CLEAR_MSG, last_notif.message);
+
+ return 0;
+}
+
+#define UR_SET_MSG "Device Status Error set: Unsupported Request"
+#define UR_CLEAR_MSG "Device Status Error cleared: Unsupported Request"
+#define FE_SET_MSG "Device Status Error set: Fatal Error"
+#define FE_CLEAR_MSG "Device Status Error cleared: Fatal Error"
+
+DEF_TEST(device_status_errors) {
+ pcie_device_t dev = {0, TEST_DOMAIN, TEST_BUS, TEST_DEVICE, TEST_FUNCTION,
+ 0, 0, 0, 0, 0};
+ pcie_config.persistent = 0;
+ g_buff[0] = (PCI_EXP_DEVSTA_URD & 0xff);
+
+ memset(&last_notif, 0, sizeof(last_notif));
+ pcie_check_dev_status(&dev, 0);
+ EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(UR_SET_MSG, last_notif.message);
+
+ memset(&last_notif, 0, sizeof(last_notif));
+ pcie_check_dev_status(&dev, 0);
+ EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+ pcie_config.persistent = 1;
+ pcie_check_dev_status(&dev, 0);
+ EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(UR_SET_MSG, last_notif.message);
+
+ g_buff[0] = 0;
+ pcie_check_dev_status(&dev, 0);
+ EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(UR_CLEAR_MSG, last_notif.message);
+
+ pcie_config.persistent = 0;
+ dev.device_status = PCI_EXP_DEVSTA_URD;
+ pcie_check_dev_status(&dev, 0);
+ EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(UR_CLEAR_MSG, last_notif.message);
+
+ memset(&last_notif, 0, sizeof(last_notif));
+ pcie_check_dev_status(&dev, 0);
+ EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+ g_buff[0] = (PCI_EXP_DEVSTA_FED & 0xff);
+ pcie_check_dev_status(&dev, 0);
+ EXPECT_EQ_INT(NOTIF_FAILURE, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(FE_SET_MSG, last_notif.message);
+
+ g_buff[0] = 0;
+ pcie_check_dev_status(&dev, 0);
+ EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+ EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+ OK(NULL == last_notif.meta);
+ EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+ EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+ EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+ EXPECT_EQ_STR(FE_CLEAR_MSG, last_notif.message);
+
+ return 0;
+}
+
+int main(void) {
+ RUN_TEST(clear_dev_list);
+ RUN_TEST(add_to_list);
+ RUN_TEST(pcie_read);
+ RUN_TEST(dispatch_notification);
+
+ RUN_TEST(access_config);
+ RUN_TEST(plugin_config_fail);
+ RUN_TEST(plugin_config);
+
+ RUN_TEST(dispatch_correctable_errors);
+ RUN_TEST(dispatch_uncorrectable_errors);
+ RUN_TEST(device_status_errors);
+
+ END_TEST;
+}
static int pb_add_socket(pinba_socket_t *s, /* {{{ */
const struct addrinfo *ai) {
- int fd;
- int tmp;
- int status;
if (s->fd_num == PINBA_MAX_SOCKETS) {
WARNING("pinba plugin: Sorry, you have hit the built-in limit of "
return -1;
}
- fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ int fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (fd < 0) {
ERROR("pinba plugin: socket(2) failed: %s", STRERRNO);
return 0;
}
- tmp = 1;
- status = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
+ int status = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
if (status != 0) {
WARNING("pinba plugin: setsockopt(SO_REUSEADDR) failed: %s", STRERRNO);
}
/* make sure we don't access the database object in parallel */
pthread_mutex_t db_lock;
- cdtime_t interval;
-
/* writer "caching" settings */
cdtime_t commit_interval;
cdtime_t next_commit;
pthread_mutex_init(&db->db_lock, /* attrs = */ NULL);
- db->interval = 0;
-
db->commit_interval = 0;
db->next_commit = 0;
db->expire_delay = 0;
break;
case C_PSQL_PARAM_INTERVAL:
snprintf(interval, sizeof(interval), "%.3f",
- (db->interval > 0) ? CDTIME_T_TO_DOUBLE(db->interval)
- : plugin_get_interval());
+ CDTIME_T_TO_DOUBLE(plugin_get_interval()));
params[i] = interval;
break;
case C_PSQL_PARAM_INSTANCE:
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);
+ column_names, (size_t)column_num);
if (0 != status) {
log_err("udb_query_prepare_result failed with status %i.", status);
static int c_psql_config_database(oconfig_item_t *ci) {
c_psql_database_t *db;
+ cdtime_t interval = 0;
char cb_name[DATA_MAX_NAME_LEN];
static bool have_flush;
config_add_writer(c, writers, writers_num, &db->writers,
&db->writers_num);
else if (0 == strcasecmp(c->key, "Interval"))
- cf_util_get_cdtime(c, &db->interval);
+ cf_util_get_cdtime(c, &interval);
else if (strcasecmp("CommitInterval", c->key) == 0)
cf_util_get_cdtime(c, &db->commit_interval);
else if (strcasecmp("ExpireDelay", c->key) == 0)
if (db->queries_num > 0) {
++db->ref_cnt;
- plugin_register_complex_read("postgresql", cb_name, c_psql_read,
- /* interval = */ db->interval, &ud);
+ plugin_register_complex_read("postgresql", cb_name, c_psql_read, interval,
+ &ud);
}
if (db->writers_num > 0) {
++db->ref_cnt;
{"ipv6-questions", "dns_question", "incoming-ipv6"},
{"malloc-bytes", "gauge", "malloc_bytes"},
{"max-mthread-stack", "gauge", "max_mthread_stack"},
- {"no-packet-error", "gauge", "no_packet_error"},
+ {"no-packet-error", "errors", "no_packet_error"},
{"noedns-outqueries", "dns_question", "outgoing-noedns"},
{"noping-outqueries", "dns_question", "outgoing-noping"},
{"over-capacity-drops", "dns_question", "incoming-over_capacity"},
sstrncpy(vl.type, "delay_rate", sizeof(vl.type));
sstrncpy(vl.type_instance, delay_metrics[i].type_instance,
sizeof(vl.type_instance));
- vl.values[0].gauge = delay_metrics[i].rate_ns * delay_factor;
+ vl.values[0].gauge = delay_metrics[i].rate_ns / delay_factor;
vl.values_len = 1;
plugin_dispatch_values(&vl);
}
struct redis_node_s {
char *name;
char *host;
+ char *socket;
char *passwd;
int port;
struct timeval timeout;
rq = next;
}
- redisFree(rn->redisContext);
+ if (rn->redisContext)
+ redisFree(rn->redisContext);
sfree(rn->name);
sfree(rn->host);
+ sfree(rn->socket);
sfree(rn->passwd);
sfree(rn);
} /* void redis_node_free */
rn->port = status;
status = 0;
}
+ } else if (strcasecmp("Socket", option->key) == 0) {
+ status = cf_util_get_string(option, &rn->socket);
} else if (strcasecmp("Query", option->key) == 0) {
redis_query_t *rq = redis_config_query(option);
if (rq == NULL) {
if (rn->redisContext)
return;
- redisContext *rh = redisConnectWithTimeout(rn->host, rn->port, rn->timeout);
+ redisContext *rh;
+ if (rn->socket != NULL)
+ rh = redisConnectUnixWithTimeout(rn->socket, rn->timeout);
+ else
+ rh = redisConnectWithTimeout(rn->host, rn->port, rn->timeout);
if (rh == NULL) {
ERROR("redis plugin: can't allocate redis context");
return;
}
if (rh->err) {
- ERROR("redis plugin: unable to connect to node `%s' (%s:%d): %s.", rn->name,
- rn->host, rn->port, rh->errstr);
+ if (rn->socket)
+ ERROR("redis plugin: unable to connect to node `%s' (%s): %s.", rn->name,
+ rn->socket, rh->errstr);
+ else
+ ERROR("redis plugin: unable to connect to node `%s' (%s:%d): %s.",
+ rn->name, rn->host, rn->port, rh->errstr);
redisFree(rh);
return;
}
"total_connections_received", DS_TYPE_DERIVE);
redis_handle_info(rn->name, rr->str, "total_operations", NULL,
"total_commands_processed", DS_TYPE_DERIVE);
- redis_handle_info(rn->name, rr->str, "operations_per_second", NULL,
- "instantaneous_ops_per_sec", DS_TYPE_GAUGE);
redis_handle_info(rn->name, rr->str, "expired_keys", NULL, "expired_keys",
DS_TYPE_DERIVE);
redis_handle_info(rn->name, rr->str, "evicted_keys", NULL, "evicted_keys",
{
redis_node_t *rn = user_data->data;
- DEBUG("redis plugin: querying info from node `%s' (%s:%d).", rn->name,
- rn->host, rn->port);
+#if COLLECT_DEBUG
+ if (rn->socket)
+ DEBUG("redis plugin: querying info from node `%s' (%s).", rn->name,
+ rn->socket);
+ else
+ DEBUG("redis plugin: querying info from node `%s' (%s:%d).", rn->name,
+ rn->host, rn->port);
+#endif
redis_check_connection(rn);
bool collect_memory;
bool collect_df;
bool collect_disk;
+ bool collect_health;
};
typedef struct cr_data_s cr_data_t;
if (r == NULL)
return;
+ const char *name = r->radio_name;
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 3)
+ if (name == NULL)
+ name = r->mac_address;
+#endif
+ if (name == NULL)
+ name = "default";
+
/*** RX ***/
snprintf(type_instance, sizeof(type_instance), "%s-%s-rx", r->interface,
- r->radio_name ? r->radio_name : "default");
+ name);
cr_submit_gauge(rd, "bitrate", type_instance,
(gauge_t)(1000000.0 * r->rx_rate));
cr_submit_gauge(rd, "signal_power", type_instance,
/*** TX ***/
snprintf(type_instance, sizeof(type_instance), "%s-%s-tx", r->interface,
- r->radio_name ? r->radio_name : "default");
+ name);
cr_submit_gauge(rd, "bitrate", type_instance,
(gauge_t)(1000000.0 * r->tx_rate));
cr_submit_gauge(rd, "signal_power", type_instance,
cr_submit_gauge(rd, "signal_quality", type_instance, (gauge_t)r->tx_ccq);
/*** RX / TX ***/
- snprintf(type_instance, sizeof(type_instance), "%s-%s", r->interface,
- r->radio_name ? r->radio_name : "default");
+ snprintf(type_instance, sizeof(type_instance), "%s-%s", r->interface, name);
cr_submit_io(rd, "if_octets", type_instance, (derive_t)r->rx_bytes,
(derive_t)r->tx_bytes);
cr_submit_gauge(rd, "snr", type_instance, (gauge_t)r->signal_to_noise);
}
if (rd->collect_disk) {
- cr_submit_counter(rd, "counter", "secors_written",
+ cr_submit_counter(rd, "counter", "sectors_written",
(derive_t)r->write_sect_total);
cr_submit_gauge(rd, "gauge", "bad_blocks", (gauge_t)r->bad_blocks);
}
return 0;
} /* }}} int handle_system_resource */
+
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 3)
+static int handle_system_health(__attribute__((unused))
+ ros_connection_t *c, /* {{{ */
+ const ros_system_health_t *r,
+ __attribute__((unused)) void *user_data) {
+
+ if ((r == NULL) || (user_data == NULL))
+ return EINVAL;
+
+ cr_data_t *rd = user_data;
+
+ cr_submit_gauge(rd, "voltage", "system", (gauge_t)r->voltage);
+ cr_submit_gauge(rd, "temperature", "system", (gauge_t)r->temperature);
+
+ return 0;
+} /* }}} int handle_system_health */
+#endif
#endif
static int cr_read(user_data_t *user_data) /* {{{ */
return -1;
}
}
+
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 3)
+ if (rd->collect_health) {
+ status = ros_system_health(rd->connection, handle_system_health,
+ /* user data = */ rd);
+ if (status != 0) {
+ ERROR("routeros plugin: ros_system_health failed: %s", STRERROR(status));
+ ros_disconnect(rd->connection);
+ rd->connection = NULL;
+ return -1;
+ }
+ }
+#endif
#endif
return 0;
cf_util_get_boolean(child, &router_data->collect_df);
else if (strcasecmp("CollectDisk", child->key) == 0)
cf_util_get_boolean(child, &router_data->collect_disk);
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 3)
+ else if (strcasecmp("CollectHealth", child->key) == 0)
+ cf_util_get_boolean(child, &router_data->collect_health);
+#endif
#endif
else {
WARNING("routeros plugin: Unknown config option `%s'.", child->key);
status = -1;
}
- if (!router_data->collect_interface && !router_data->collect_regtable) {
+ int report = 0;
+ if (router_data->collect_interface)
+ report++;
+ if (router_data->collect_regtable)
+ report++;
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0)
+ if (router_data->collect_cpu_load)
+ report++;
+ if (router_data->collect_memory)
+ report++;
+ if (router_data->collect_df)
+ report++;
+ if (router_data->collect_disk)
+ report++;
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 3)
+ if (router_data->collect_health)
+ report++;
+#endif
+#endif
+
+ if (!report) {
ERROR("routeros plugin: No `Collect*' option within a `Router' block. "
"What statistics should I collect?");
status = -1;
#if SENSORS_API_VERSION >= 0x402
(feature->type != SENSORS_FEATURE_CURR) &&
#endif
+#if SENSORS_API_VERSION >= 0x431
+ (feature->type != SENSORS_FEATURE_HUMIDITY) &&
+#endif
(feature->type != SENSORS_FEATURE_POWER)) {
DEBUG("sensors plugin: sensors_load_conf: "
"Ignoring feature `%s', "
#if SENSORS_API_VERSION >= 0x402
(subfeature->type != SENSORS_SUBFEATURE_CURR_INPUT) &&
#endif
+#if SENSORS_API_VERSION >= 0x431
+ (subfeature->type != SENSORS_SUBFEATURE_HUMIDITY_INPUT) &&
+#endif
(subfeature->type != SENSORS_SUBFEATURE_POWER_INPUT))
continue;
else if (fl->feature->type == SENSORS_FEATURE_CURR)
type = "current";
#endif
+#if SENSORS_API_VERSION >= 0x431
+ else if (fl->feature->type == SENSORS_FEATURE_HUMIDITY)
+ type = "humidity";
+#endif
else
continue;
void *sess_handle;
c_complain_t complaint;
- cdtime_t interval;
data_definition_t **data_list;
int data_list_len;
};
int status = 0;
/* Registration stuff. */
+ cdtime_t interval = 0;
char cb_name[DATA_MAX_NAME_LEN];
hd = calloc(1, sizeof(*hd));
}
hd->sess_handle = NULL;
- hd->interval = 0;
/* These mean that we have not set a timeout or retry value */
hd->timeout = 0;
else if (strcasecmp("Collect", option->key) == 0)
status = csnmp_config_add_host_collect(hd, option);
else if (strcasecmp("Interval", option->key) == 0)
- status = cf_util_get_cdtime(option, &hd->interval);
+ status = cf_util_get_cdtime(option, &interval);
else if (strcasecmp("Username", option->key) == 0)
status = cf_util_get_string(option, &hd->username);
else if (strcasecmp("AuthProtocol", option->key) == 0)
snprintf(cb_name, sizeof(cb_name), "snmp-%s", hd->name);
status = plugin_register_complex_read(
- /* group = */ NULL, cb_name, csnmp_read_host, hd->interval,
+ /* group = */ NULL, cb_name, csnmp_read_host, interval,
&(user_data_t){
.data = hd, .free_func = csnmp_host_definition_destroy,
});
sstrncpy(vl.plugin, data->plugin_name, sizeof(vl.plugin));
sstrncpy(vl.type, data->type, sizeof(vl.type));
- vl.interval = host->interval;
-
have_more = 1;
while (have_more) {
bool suffix_skipped = 0;
value_cell_ptr[0] = value_cell_ptr[0]->next;
} /* while (have_more) */
- return (0);
+ return 0;
} /* int csnmp_dispatch_table */
static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
sstrncpy(vl.plugin_instance, data->plugin_instance.value,
sizeof(vl.plugin_instance));
- vl.interval = host->interval;
-
req = snmp_pdu_create(SNMP_MSG_GET);
if (req == NULL) {
ERROR("snmp plugin: snmp_pdu_create failed.");
host = ud->data;
- if (host->interval == 0)
- host->interval = plugin_get_interval();
-
if (host->sess_handle == NULL)
csnmp_host_open_session(host);
continue;
}
+ /* allow multiple sockets to use the same PORT number */
+ int yes = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
+ ERROR("statsd plugin: setsockopt (reuseaddr): %s", STRERRNO);
+ close(fd);
+ continue;
+ }
+
getnameinfo(ai_ptr->ai_addr, ai_ptr->ai_addrlen, str_node, sizeof(str_node),
str_service, sizeof(str_service),
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV);
memset(tmp, 0, sizeof(*tmp));
tmp->fd = fd;
tmp->events = POLLIN | POLLPRI;
+ INFO("statsd plugin: Listening on [%s]:%s.", str_node, str_service);
}
freeaddrinfo(ai_list);
static int swap_read_io(void) /* {{{ */
{
- FILE *fh;
char buffer[1024];
- bool old_kernel = false;
-
uint8_t have_data = 0;
derive_t swap_in = 0;
derive_t swap_out = 0;
- fh = fopen("/proc/vmstat", "r");
+ FILE *fh = fopen("/proc/vmstat", "r");
if (fh == NULL) {
- /* /proc/vmstat does not exist in kernels <2.6 */
- fh = fopen("/proc/stat", "r");
- if (fh == NULL) {
- WARNING("swap: fopen: %s", STRERRNO);
- return -1;
- } else
- old_kernel = true;
+ WARNING("swap: fopen(/proc/vmstat): %s", STRERRNO);
+ return -1;
}
while (fgets(buffer, sizeof(buffer), fh) != NULL) {
char *fields[8];
- int numfields;
+ int numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
- numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
+ if (numfields != 2)
+ continue;
- if (!old_kernel) {
- if (numfields != 2)
- continue;
-
- if (strcasecmp("pswpin", fields[0]) == 0) {
- strtoderive(fields[1], &swap_in);
- have_data |= 0x01;
- } else if (strcasecmp("pswpout", fields[0]) == 0) {
- strtoderive(fields[1], &swap_out);
- have_data |= 0x02;
- }
- } else /* if (old_kernel) */
- {
- if (numfields != 3)
- continue;
-
- if (strcasecmp("page", fields[0]) == 0) {
- strtoderive(fields[1], &swap_in);
- strtoderive(fields[2], &swap_out);
- }
+ if (strcasecmp("pswpin", fields[0]) == 0) {
+ strtoderive(fields[1], &swap_in);
+ have_data |= 0x01;
+ } else if (strcasecmp("pswpout", fields[0]) == 0) {
+ strtoderive(fields[1], &swap_out);
+ have_data |= 0x02;
}
} /* while (fgets) */
int flags;
char *type;
char *type_instance;
- cdtime_t interval;
latency_config_t latency;
};
typedef struct ctail_config_match_s ctail_config_match_t;
-static cu_tail_match_t **tail_match_list;
-static size_t tail_match_list_num;
-static cdtime_t tail_match_list_intervals[255];
+static size_t tail_file_num;
+
+static int ctail_read(user_data_t *ud);
+
+static void ctail_match_free(void *arg) {
+ tail_match_destroy((cu_tail_match_t *)arg);
+} /* void ctail_match_free */
static int ctail_config_add_match_dstype(ctail_config_match_t *cm,
oconfig_item_t *ci) {
} else if (strcasecmp("Distribution", ds_type) == 0) {
cm->flags = UTILS_MATCH_DS_TYPE_GAUGE | UTILS_MATCH_CF_GAUGE_DIST;
- int status = latency_config(&cm->latency, ci, "tail");
+ int status = latency_config(&cm->latency, ci);
if (status != 0)
return status;
} else if (strncasecmp("Counter", ds_type, strlen("Counter")) == 0) {
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) {
+ oconfig_item_t *ci) {
ctail_config_match_t cm = {0};
int status;
status = tail_match_add_match_simple(
tm, cm.regex, cm.excluderegex, cm.flags,
(plugin_name != NULL) ? plugin_name : "tail", plugin_instance, cm.type,
- cm.type_instance, cm.latency, interval);
+ cm.type_instance, cm.latency);
if (status != 0)
ERROR("tail plugin: tail_match_add_match_simple failed.");
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_name, plugin_instance, option,
- interval);
+ status = ctail_config_add_match(tm, plugin_name, plugin_instance, option);
if (status == 0)
num_matches++;
/* Be mild with failed matches.. */
ci->values[0].value.string);
tail_match_destroy(tm);
return -1;
- } else {
- cu_tail_match_t **temp;
-
- temp = realloc(tail_match_list,
- sizeof(cu_tail_match_t *) * (tail_match_list_num + 1));
- if (temp == NULL) {
- ERROR("tail plugin: realloc failed.");
- tail_match_destroy(tm);
- return -1;
- }
-
- tail_match_list = temp;
- tail_match_list[tail_match_list_num] = tm;
- tail_match_list_intervals[tail_match_list_num] = interval;
- tail_match_list_num++;
}
+ char str[255];
+ snprintf(str, sizeof(str), "tail-%zu", tail_file_num++);
+
+ plugin_register_complex_read(
+ NULL, str, ctail_read, interval,
+ &(user_data_t){.data = tm, .free_func = ctail_match_free});
+
return 0;
} /* int ctail_config_add_file */
return 0;
} /* int ctail_read */
-static int ctail_init(void) {
- char str[255];
-
- if (tail_match_list_num == 0) {
- WARNING("tail plugin: File list is empty. Returning an error.");
- return -1;
- }
-
- for (size_t i = 0; i < tail_match_list_num; i++) {
- snprintf(str, sizeof(str), "tail-%zu", i);
-
- plugin_register_complex_read(NULL, str, ctail_read,
- tail_match_list_intervals[i],
- &(user_data_t){
- .data = tail_match_list[i],
- });
- }
-
- return 0;
-} /* int ctail_init */
-
-static int ctail_shutdown(void) {
- for (size_t i = 0; i < tail_match_list_num; i++) {
- tail_match_destroy(tail_match_list[i]);
- tail_match_list[i] = NULL;
- }
- sfree(tail_match_list);
- tail_match_list_num = 0;
-
- return 0;
-} /* int ctail_shutdown */
-
void module_register(void) {
plugin_register_complex_config("tail", ctail_config);
- plugin_register_init("tail", ctail_init);
- plugin_register_shutdown("tail", ctail_shutdown);
} /* void module_register */
cu_tail_t *tail;
metric_definition_t **metric_list;
size_t metric_list_len;
- cdtime_t interval;
ssize_t time_from;
struct instance_definition_s *next;
};
sstrncpy(vl.type_instance, md->instance, sizeof(vl.type_instance));
vl.time = t;
- vl.interval = id->interval;
return plugin_dispatch_values(&vl);
}
int status = 0;
/* Registration variables */
+ cdtime_t interval = 0;
char cb_name[DATA_MAX_NAME_LEN];
id = calloc(1, sizeof(*id));
return status;
}
- /* Use default interval. */
- id->interval = plugin_get_interval();
-
for (int i = 0; i < ci->children_num; ++i) {
oconfig_item_t *option = ci->children + i;
status = 0;
else if (strcasecmp("Collect", option->key) == 0)
status = tcsv_config_add_instance_collect(id, option);
else if (strcasecmp("Interval", option->key) == 0)
- cf_util_get_cdtime(option, &id->interval);
+ cf_util_get_cdtime(option, &interval);
else if (strcasecmp("TimeFrom", option->key) == 0)
status = tcsv_config_get_index(option, &id->time_from);
else if (strcasecmp("Plugin", option->key) == 0)
snprintf(cb_name, sizeof(cb_name), "tail_csv/%s", id->path);
status = plugin_register_complex_read(
- NULL, cb_name, tcsv_read, id->interval,
+ NULL, cb_name, tcsv_read, interval,
&(user_data_t){
.data = id, .free_func = tcsv_instance_definition_destroy,
});
printf("ok %i - %s = %" PRIu64 "\n", ++check_count__, #actual, got__); \
} while (0)
+#define EXPECT_EQ_PTR(expect, actual) \
+ do { \
+ void *want__ = expect; \
+ void *got__ = actual; \
+ if (got__ != want__) { \
+ printf("not ok %i - %s = %p, want %p\n", ++check_count__, #actual, \
+ got__, want__); \
+ return -1; \
+ } \
+ printf("ok %i - %s = %p\n", ++check_count__, #actual, got__); \
+ } while (0)
+
#define EXPECT_EQ_DOUBLE(expect, actual) \
do { \
double want__ = (double)expect; \
/* If the state didn't change, report if `persistent' is specified. If the
* state is `okay', then only report if `persist_ok` flag is set. */
if (state == state_old) {
- if ((th->flags & UT_FLAG_PERSIST) == 0)
+ if (state == STATE_UNKNOWN) {
+ /* From UNKNOWN to UNKNOWN. Persist doesn't apply here. */
+ return 0;
+ } else if ((th->flags & UT_FLAG_PERSIST) == 0)
return 0;
else if ((state == STATE_OKAY) && ((th->flags & UT_FLAG_PERSIST_OK) == 0))
return 0;
snprintf(buf, bufsize, ": All data sources are within range again. "
"Current value of \"%s\" is %f.",
ds->ds[ds_index].name, values[ds_index]);
+ } else if (state == STATE_UNKNOWN) {
+ ERROR("ut_report_state: metric transition to UNKNOWN from a different "
+ "state. This shouldn't happen.");
+ return 0;
} else {
double min;
double max;
if (ds != NULL) {
ds_name = ds->ds[ds_index].name;
if ((th->data_source[0] != 0) && (strcmp(ds_name, th->data_source) != 0))
- return STATE_OKAY;
+ return STATE_UNKNOWN;
}
if ((th->flags & UT_FLAG_INVERT) != 0) {
case STATE_WARNING:
hysteresis_for_warning = th->hysteresis;
break;
+ case STATE_UNKNOWN:
case STATE_OKAY:
/* do nothing -- the hysteresis only applies to the non-normal states */
break;
*
* Checks all data sources of a value list against the given threshold, using
* the ut_check_one_data_source function above. Returns the worst status,
- * which is `okay' if nothing has failed.
+ * which is `okay' if nothing has failed or `unknown' if no valid datasource was
+ * defined.
* Returns less than zero if the data set doesn't have any data sources.
*/
static int ut_check_one_threshold(const data_set_t *ds, const value_list_t *vl,
#define PLUGIN_NAME "turbostat"
+typedef enum affinity_policy_enum {
+ policy_restore_affinity, /* restore cpu affinity to whatever it was before */
+ policy_allcpus_affinity /* do not restore affinity, set to all cpus */
+} affinity_policy_t;
+
+/* the default is to set cpu affinity to all cpus */
+static affinity_policy_t affinity_policy = policy_allcpus_affinity;
+
/*
* This tool uses the Model-Specific Registers (MSRs) present on Intel
* processors.
*/
static unsigned int tcc_activation_temp;
+static unsigned int do_power_fields;
+#define UFS_PLATFORM (1 << 0)
+#define TURBO_PLATFORM (1 << 1)
+#define PSTATES_PLATFORM (1 << 2)
+
static unsigned int do_rapl;
static unsigned int config_rapl;
static bool apply_config_rapl;
static double rapl_energy_units;
+static double rapl_power_units;
#define RAPL_PKG (1 << 0)
/* 0x610 MSR_PKG_POWER_LIMIT */
/* 0x611 MSR_PKG_ENERGY_STATUS */
+/* 0x614 MSR_PKG_POWER_INFO */
#define RAPL_DRAM (1 << 1)
/* 0x618 MSR_DRAM_POWER_LIMIT */
/* 0x619 MSR_DRAM_ENERGY_STATUS */
uint32_t energy_dram; /* MSR_DRAM_ENERGY_STATUS */
uint32_t energy_cores; /* MSR_PP0_ENERGY_STATUS */
uint32_t energy_gfx; /* MSR_PP1_ENERGY_STATUS */
+ uint32_t tdp;
+ uint8_t turbo_enabled;
+ uint8_t pstates_enabled;
+ uint32_t uncore;
unsigned int tcc_activation_temp;
unsigned int pkg_temp_c;
} * package_delta, *package_even, *package_odd;
"TCCActivationTemp",
"RunningAveragePowerLimit",
"LogicalCoreNames",
+ "RestoreAffinityPolicy",
};
static const int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
if (do_rapl & RAPL_PKG) {
READ_MSR(MSR_PKG_ENERGY_STATUS, &msr);
p->energy_pkg = msr & 0xFFFFFFFF;
+ READ_MSR(MSR_PKG_POWER_INFO, &msr);
+ p->tdp = msr & 0x7FFF;
}
if (do_rapl & RAPL_CORES) {
READ_MSR(MSR_PP0_ENERGY_STATUS, &msr);
READ_MSR(MSR_IA32_PACKAGE_THERM_STATUS, &msr);
p->pkg_temp_c = p->tcc_activation_temp - ((msr >> 16) & 0x7F);
}
+ if (do_power_fields & TURBO_PLATFORM) {
+ READ_MSR(MSR_IA32_MISC_ENABLE, &msr);
+ p->turbo_enabled = !((msr >> 38) & 0x1);
+ }
+ if (do_power_fields & PSTATES_PLATFORM) {
+ READ_MSR(MSR_IA32_MISC_ENABLE, &msr);
+ p->pstates_enabled = (msr >> 16) & 0x1;
+ }
+ if (do_power_fields & UFS_PLATFORM) {
+ READ_MSR(MSR_UNCORE_FREQ_SCALING, &msr);
+ p->uncore = msr & 0x1F;
+ }
out:
close(msr_fd);
delta->energy_cores = new->energy_cores - old->energy_cores;
delta->energy_gfx = new->energy_gfx - old->energy_gfx;
delta->energy_dram = new->energy_dram - old->energy_dram;
+ delta->tdp = new->tdp;
+ delta->turbo_enabled = new->turbo_enabled;
+ delta->pstates_enabled = new->pstates_enabled;
+ delta->tcc_activation_temp = new->tcc_activation_temp;
+ delta->uncore = new->uncore;
}
/*
turbostat_submit(name, "percent", "pc10", 100.0 * p->pc10 / t->tsc);
if (do_rapl) {
- if (do_rapl & RAPL_PKG)
+ if (do_rapl & RAPL_PKG) {
turbostat_submit(name, "power", "pkg",
p->energy_pkg * rapl_energy_units / interval_float);
+ turbostat_submit(name, "tdp", "pkg", p->tdp * rapl_power_units);
+ }
if (do_rapl & RAPL_CORES)
turbostat_submit(name, "power", "cores",
p->energy_cores * rapl_energy_units / interval_float);
turbostat_submit(name, "power", "DRAM",
p->energy_dram * rapl_energy_units / interval_float);
}
+
+ if (do_power_fields & TURBO_PLATFORM) {
+ turbostat_submit(name, "turbo_enabled", NULL, p->turbo_enabled);
+ }
+ if (do_power_fields & PSTATES_PLATFORM) {
+ turbostat_submit(name, "pstates_enabled", NULL, p->pstates_enabled);
+ }
+ if (do_power_fields & UFS_PLATFORM) {
+ turbostat_submit(name, "uncore_ratio", NULL, p->uncore);
+ }
+ turbostat_submit(name, "temperature", "tcc_activation",
+ p->tcc_activation_temp);
done:
return 0;
}
case 0x4F: /* BDX */
case 0x56: /* BDX-DE */
do_rapl = RAPL_PKG | RAPL_DRAM;
+ do_power_fields = TURBO_PLATFORM | UFS_PLATFORM | PSTATES_PLATFORM;
break;
case 0x2D: /* SNB Xeon */
case 0x3E: /* IVB Xeon */
do_rapl = RAPL_PKG | RAPL_CORES | RAPL_DRAM;
+ do_power_fields = TURBO_PLATFORM | PSTATES_PLATFORM;
break;
case 0x37: /* BYT */
case 0x4D: /* AVN */
if (get_msr(0, MSR_RAPL_POWER_UNIT, &msr))
return 0;
+ rapl_power_units = 1.0 / (1 << (msr & 0xF));
if (model == 0x37)
rapl_energy_units = 1.0 * (1 << (msr >> 8 & 0x1F)) / 1000000;
else
return ret;
}
+int save_affinity(void) {
+ if (affinity_policy == policy_restore_affinity) {
+ /* Try to save the scheduling affinity, as it will be modified by
+ * get_counters().
+ */
+ if (sched_getaffinity(0, cpu_saved_affinity_setsize,
+ cpu_saved_affinity_set) != 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+void restore_affinity(void) {
+ /* Let's restore the affinity to the value saved in save_affinity */
+ if (affinity_policy == policy_restore_affinity)
+ (void)sched_setaffinity(0, cpu_saved_affinity_setsize,
+ cpu_saved_affinity_set);
+ else {
+ /* reset the affinity to all present cpus */
+ (void)sched_setaffinity(0, cpu_present_setsize, cpu_present_set);
+ }
+}
+
static int turbostat_read(void) {
int ret;
}
}
- /* Saving the scheduling affinity, as it will be modified by get_counters */
- if (sched_getaffinity(0, cpu_saved_affinity_setsize,
- cpu_saved_affinity_set) != 0) {
- ERROR("turbostat plugin: Unable to save the CPU affinity");
+ if (save_affinity() != 0) {
+ ERROR("turbostat plugin: Unable to save the CPU affinity. Please read the "
+ "docs about RestoreAffinityPolicy option.");
return -1;
}
}
ret = 0;
out:
- /*
- * Let's restore the affinity
- * This might fail if the number of CPU changed, but we can't do anything in
- * that case..
- */
- (void)sched_setaffinity(0, cpu_saved_affinity_setsize,
- cpu_saved_affinity_set);
+ restore_affinity();
+
return ret;
}
return -1;
}
tcc_activation_temp = (unsigned int)tmp_val;
+ } else if (strcasecmp("RestoreAffinityPolicy", key) == 0) {
+ if (strcasecmp("Restore", value) == 0)
+ affinity_policy = policy_restore_affinity;
+ else if (strcasecmp("AllCPUs", value) == 0)
+ affinity_policy = policy_allcpus_affinity;
+ else {
+ ERROR("turbostat plugin: Invalid RestoreAffinityPolicy '%s'", value);
+ return -1;
+ }
} else {
ERROR("turbostat plugin: Invalid configuration option '%s'", key);
return -1;
ps_stacksize value:GAUGE:0:9223372036854775807
ps_state value:GAUGE:0:65535
ps_vm value:GAUGE:0:9223372036854775807
+pstates_enabled value:GAUGE:0:1
pubsub value:GAUGE:0:U
queue_length value:GAUGE:0:U
records value:GAUGE:0:U
swap value:GAUGE:0:1099511627776
swap_io value:DERIVE:0:U
tcp_connections value:GAUGE:0:4294967295
+tdp value:GAUGE:U:U
temperature value:GAUGE:U:U
threads value:GAUGE:0:U
time_dispersion value:GAUGE:-1000000:1000000
total_threads value:DERIVE:0:U
total_time_in_ms value:DERIVE:0:U
total_values value:DERIVE:0:U
+turbo_enabled value:GAUGE:0:1
+transitions value:DERIVE:0:U
uptime value:GAUGE:0:4294967295
+uncore_ratio value:GAUGE:0:U
users value:GAUGE:0:65535
vcl value:GAUGE:0:65535
vcpu value:GAUGE:0:U
char *plugin;
char *db_name;
- cdtime_t interval;
-
udb_result_preparation_area_t *result_prep_areas;
}; /* }}} */
}
}
- if (q_area->interval > 0)
- vl.interval = q_area->interval;
-
sstrncpy(vl.host, q_area->host, sizeof(vl.host));
sstrncpy(vl.plugin, q_area->plugin, sizeof(vl.plugin));
sstrncpy(vl.type, r->type, sizeof(vl.type));
sfree(prep_area->plugin);
sfree(prep_area->db_name);
- prep_area->interval = 0;
-
for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
r = r->next, r_area = r_area->next) {
/* this may happen during error conditions of the caller */
udb_query_preparation_area_t *prep_area,
const char *host, const char *plugin,
const char *db_name, char **column_names,
- size_t column_num, cdtime_t interval) {
+ size_t column_num) {
udb_result_preparation_area_t *r_area;
udb_result_t *r;
int status;
assert(prep_area->host == NULL);
assert(prep_area->plugin == NULL);
assert(prep_area->db_name == NULL);
- assert(prep_area->interval == 0);
#endif
prep_area->column_num = column_num;
prep_area->plugin = strdup(plugin);
prep_area->db_name = strdup(db_name);
- prep_area->interval = interval;
-
if ((prep_area->host == NULL) || (prep_area->plugin == NULL) ||
(prep_area->db_name == NULL)) {
P_ERROR("Query `%s': Prepare failed: Out of memory.", q->name);
udb_query_preparation_area_t *prep_area,
const char *host, const char *plugin,
const char *db_name, char **column_names,
- size_t column_num, cdtime_t interval);
+ size_t column_num);
int udb_query_handle_result(udb_query_t const *q,
udb_query_preparation_area_t *prep_area,
char **column_values);
#include "common.h"
#include "utils_dpdk.h"
+#if RTE_VERSION <= RTE_VERSION_NUM(18, 5, 0, 0)
#define DPDK_DEFAULT_RTE_CONFIG "/var/run/.rte_config"
+#else
+#define DPDK_DEFAULT_RTE_CONFIG "/var/run/dpdk/rte/config"
+#endif
#define DPDK_EAL_ARGC 10
// Complete trace should fit into 1024 chars. Trace contain some headers
// and text together with traced data from pipe. This is the reason why
status = cf_util_get_string_buffer(child, prefix, sizeof(prefix));
if (status == 0) {
+#if RTE_VERSION <= RTE_VERSION_NUM(18, 5, 0, 0)
snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN,
"/var/run/.%s_config", prefix);
+#else
+ snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN,
+ "/var/run/dpdk/%s/config", prefix);
+#endif
DEBUG("dpdk_common: EAL:File prefix %s", phc->eal_config.file_prefix);
}
} else if (strcasecmp("LogLevel", child->key) == 0) {
}
uint8_t dpdk_helper_eth_dev_count(void) {
+#if RTE_VERSION < RTE_VERSION_NUM(18, 05, 0, 0)
uint8_t ports = rte_eth_dev_count();
+#else
+ uint8_t ports = rte_eth_dev_count_avail();
+#endif
if (ports == 0) {
ERROR(
"%s:%d: No DPDK ports available. Check bound devices to DPDK driver.\n",
}
}
+static int gr_format_name_tagged(char *ret, int ret_len, value_list_t const *vl,
+ char const *ds_name, char const *prefix,
+ char const *postfix, char const escape_char,
+ unsigned int flags) {
+ char n_host[DATA_MAX_NAME_LEN];
+ char n_plugin[DATA_MAX_NAME_LEN];
+ char n_plugin_instance[DATA_MAX_NAME_LEN];
+ char n_type[DATA_MAX_NAME_LEN];
+ char n_type_instance[DATA_MAX_NAME_LEN];
+
+ char tmp_plugin[DATA_MAX_NAME_LEN + 8];
+ char tmp_plugin_instance[DATA_MAX_NAME_LEN + 17];
+ char tmp_type[DATA_MAX_NAME_LEN + 6];
+ char tmp_type_instance[DATA_MAX_NAME_LEN + 15];
+ char tmp_metric[3 * DATA_MAX_NAME_LEN + 2];
+ char tmp_ds_name[DATA_MAX_NAME_LEN + 9];
+
+ if (prefix == NULL)
+ prefix = "";
+
+ if (postfix == NULL)
+ postfix = "";
+
+ gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, 1);
+ gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char, 1);
+ gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
+ sizeof(n_plugin_instance), escape_char, 1);
+ gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char, 1);
+ gr_copy_escape_part(n_type_instance, vl->type_instance,
+ sizeof(n_type_instance), escape_char, 1);
+
+ snprintf(tmp_plugin, sizeof(tmp_plugin), ";plugin=%s", n_plugin);
+
+ if (n_plugin_instance[0] != '\0')
+ snprintf(tmp_plugin_instance, sizeof(tmp_plugin_instance),
+ ";plugin_instance=%s", n_plugin_instance);
+ else
+ tmp_plugin_instance[0] = '\0';
+
+ if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) || strcmp(n_plugin, n_type) != 0)
+ snprintf(tmp_type, sizeof(tmp_type), ";type=%s", n_type);
+ else
+ tmp_type[0] = '\0';
+
+ if (n_type_instance[0] != '\0') {
+ if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) ||
+ strcmp(n_plugin_instance, n_type_instance) != 0)
+ snprintf(tmp_type_instance, sizeof(tmp_type_instance),
+ ";type_instance=%s", n_type_instance);
+ else
+ tmp_type_instance[0] = '\0';
+ } else
+ tmp_type_instance[0] = '\0';
+
+ /* Assert always_append_ds -> ds_name */
+ assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
+ if (ds_name != NULL) {
+ snprintf(tmp_ds_name, sizeof(tmp_ds_name), ";ds_name=%s", ds_name);
+
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, ds_name);
+ else
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s.%s", n_plugin, n_type,
+ ds_name);
+ } else {
+ tmp_ds_name[0] = '\0';
+
+ if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s", n_plugin);
+ else
+ snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, n_type);
+ }
+
+ snprintf(ret, ret_len, "%s%s%s;host=%s%s%s%s%s%s", prefix, tmp_metric,
+ postfix, n_host, tmp_plugin, tmp_plugin_instance, tmp_type,
+ tmp_type_instance, tmp_ds_name);
+
+ return 0;
+}
+
static int gr_format_name(char *ret, int ret_len, value_list_t const *vl,
char const *ds_name, char const *prefix,
char const *postfix, char const escape_char,
ds_name = ds->ds[i].name;
/* Copy the identifier to `key' and escape it. */
- status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix,
- escape_char, flags);
- if (status != 0) {
- P_ERROR("format_graphite: error with gr_format_name");
- sfree(rates);
- return status;
+ if (flags & GRAPHITE_USE_TAGS) {
+ status = gr_format_name_tagged(key, sizeof(key), vl, ds_name, prefix,
+ postfix, escape_char, flags);
+ if (status != 0) {
+ P_ERROR("format_graphite: error with gr_format_name_tagged");
+ sfree(rates);
+ return status;
+ }
+ } else {
+ status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix,
+ escape_char, flags);
+ if (status != 0) {
+ P_ERROR("format_graphite: error with gr_format_name");
+ sfree(rates);
+ return status;
+ }
}
escape_graphite_string(key, escape_char);
+
/* Convert the values to an ASCII representation and put that into
* `values'. */
status = gr_format_values(values, sizeof(values), i, ds, vl, rates);
#define GRAPHITE_ALWAYS_APPEND_DS 0x04
#define GRAPHITE_DROP_DUPE_FIELDS 0x08
#define GRAPHITE_PRESERVE_SEPARATOR 0x10
+#define GRAPHITE_USE_TAGS 0x20
int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds,
const value_list_t *vl, const char *prefix,
.suffix = NULL,
.want_name = "foo.example@com.test.single",
},
+ /* flag GRAPHITE_USE_TAGS */
+ {.flags = GRAPHITE_USE_TAGS,
+ .want_name = "test.single;host=example.com;plugin=test;type=single"},
+ {.plugin_instance = "f.o.o",
+ .type_instance = "b.a.r",
+ .flags = GRAPHITE_USE_TAGS,
+ .want_name = "test.single;host=example.com;plugin=test;plugin_instance="
+ "f.o.o;type=single;type_instance=b.a.r"},
+ {.flags = GRAPHITE_USE_TAGS ^ GRAPHITE_ALWAYS_APPEND_DS,
+ .want_name = "test.single.value;host=example.com;plugin=test;type="
+ "single;ds_name=value"},
+ {.plugin_instance = "foo",
+ .type_instance = "foo",
+ .flags = GRAPHITE_USE_TAGS ^ GRAPHITE_DROP_DUPE_FIELDS,
+ .want_name = "test.single;host=example.com;plugin=test;plugin_instance="
+ "foo;type=single"},
};
for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
#undef BUFFER_ADD
- DEBUG("format_json: values_to_json: buffer = %s;", buffer);
sfree(rates);
return 0;
} /* }}} int values_to_json */
#undef BUFFER_ADD
- DEBUG("format_json: dstypes_to_json: buffer = %s;", buffer);
-
return 0;
} /* }}} int dstypes_to_json */
#undef BUFFER_ADD
- DEBUG("format_json: dsnames_to_json: buffer = %s;", buffer);
-
return 0;
} /* }}} int dsnames_to_json */
#undef BUFFER_ADD_KEYVAL
#undef BUFFER_ADD
- DEBUG("format_json: value_list_to_json: buffer = %s;", buffer);
-
return 0;
} /* }}} int value_list_to_json */
(*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 : */
--- /dev/null
+/**
+ * collectd - src/utils_format_stackdriver.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "utils_format_stackdriver.h"
+
+#include "common.h"
+#include "plugin.h"
+#include "utils_avltree.h"
+#include "utils_cache.h"
+#include "utils_time.h"
+
+#include <yajl/yajl_gen.h>
+#include <yajl/yajl_parse.h>
+#if HAVE_YAJL_YAJL_VERSION_H
+#include <yajl/yajl_version.h>
+#endif
+
+struct sd_output_s {
+ sd_resource_t *res;
+ yajl_gen gen;
+ c_avl_tree_t *staged;
+ c_avl_tree_t *metric_descriptors;
+};
+
+struct sd_label_s {
+ char *key;
+ char *value;
+};
+typedef struct sd_label_s sd_label_t;
+
+struct sd_resource_s {
+ char *type;
+
+ sd_label_t *labels;
+ size_t labels_num;
+};
+
+static int json_string(yajl_gen gen, char const *s) /* {{{ */
+{
+ yajl_gen_status status =
+ yajl_gen_string(gen, (unsigned char const *)s, strlen(s));
+ if (status != yajl_gen_status_ok)
+ return (int)status;
+
+ return 0;
+} /* }}} int json_string */
+
+static int json_time(yajl_gen gen, cdtime_t t) {
+ char buffer[64];
+
+ size_t status = rfc3339(buffer, sizeof(buffer), t);
+ if (status != 0) {
+ return status;
+ }
+
+ return json_string(gen, buffer);
+} /* }}} int json_time */
+
+/* MonitoredResource
+ *
+ * {
+ * "type": "library.googleapis.com/book",
+ * "labels": {
+ * "/genre": "fiction",
+ * "/media": "paper"
+ * "/title": "The Old Man and the Sea"
+ * }
+ * }
+ */
+static int format_gcm_resource(yajl_gen gen, sd_resource_t *res) /* {{{ */
+{
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "type") || json_string(gen, res->type);
+ if (status != 0)
+ return status;
+
+ if (res->labels_num != 0) {
+ status = json_string(gen, "labels");
+ if (status != 0)
+ return status;
+
+ yajl_gen_map_open(gen);
+ for (size_t i = 0; i < res->labels_num; i++) {
+ status = json_string(gen, res->labels[i].key) ||
+ json_string(gen, res->labels[i].value);
+ if (status != 0)
+ return status;
+ }
+ yajl_gen_map_close(gen);
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_gcm_resource */
+
+/* TypedValue
+ *
+ * {
+ * // Union field, only one of the following:
+ * "int64Value": string,
+ * "doubleValue": number,
+ * }
+ */
+static int format_typed_value(yajl_gen gen, int ds_type, value_t v,
+ int64_t start_value) {
+ char integer[32];
+
+ yajl_gen_map_open(gen);
+
+ switch (ds_type) {
+ case DS_TYPE_GAUGE: {
+ int status = json_string(gen, "doubleValue");
+ if (status != 0)
+ return status;
+
+ status = (int)yajl_gen_double(gen, (double)v.gauge);
+ if (status != yajl_gen_status_ok)
+ return status;
+
+ yajl_gen_map_close(gen);
+ return 0;
+ }
+ case DS_TYPE_DERIVE: {
+ derive_t diff = v.derive - (derive_t)start_value;
+ snprintf(integer, sizeof(integer), "%" PRIi64, diff);
+ break;
+ }
+ case DS_TYPE_COUNTER: {
+ counter_t diff = counter_diff((counter_t)start_value, v.counter);
+ snprintf(integer, sizeof(integer), "%llu", diff);
+ break;
+ }
+ case DS_TYPE_ABSOLUTE: {
+ snprintf(integer, sizeof(integer), "%" PRIu64, v.absolute);
+ break;
+ }
+ default: {
+ ERROR("format_typed_value: unknown value type %d.", ds_type);
+ return EINVAL;
+ }
+ }
+
+ int status = json_string(gen, "int64Value") || json_string(gen, integer);
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_typed_value */
+
+/* MetricKind
+ *
+ * enum(
+ * "CUMULATIVE",
+ * "GAUGE"
+ * )
+*/
+static int format_metric_kind(yajl_gen gen, int ds_type) {
+ switch (ds_type) {
+ case DS_TYPE_GAUGE:
+ case DS_TYPE_ABSOLUTE:
+ return json_string(gen, "GAUGE");
+ case DS_TYPE_COUNTER:
+ case DS_TYPE_DERIVE:
+ return json_string(gen, "CUMULATIVE");
+ default:
+ ERROR("format_metric_kind: unknown value type %d.", ds_type);
+ return EINVAL;
+ }
+}
+
+/* ValueType
+ *
+ * enum(
+ * "DOUBLE",
+ * "INT64"
+ * )
+*/
+static int format_value_type(yajl_gen gen, int ds_type) {
+ return json_string(gen, (ds_type == DS_TYPE_GAUGE) ? "DOUBLE" : "INT64");
+}
+
+static int metric_type(char *buffer, size_t buffer_size, data_set_t const *ds,
+ value_list_t const *vl, int ds_index) {
+ /* {{{ */
+ char const *ds_name = ds->ds[ds_index].name;
+
+#define GCM_PREFIX "custom.googleapis.com/collectd/"
+ if ((ds_index != 0) || strcmp("value", ds_name) != 0) {
+ snprintf(buffer, buffer_size, GCM_PREFIX "%s/%s_%s", vl->plugin, vl->type,
+ ds_name);
+ } else {
+ snprintf(buffer, buffer_size, GCM_PREFIX "%s/%s", vl->plugin, vl->type);
+ }
+
+ char const *whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789_/";
+ char *ptr = buffer + strlen(GCM_PREFIX);
+ size_t ok_len;
+ while ((ok_len = strspn(ptr, whitelist)) != strlen(ptr)) {
+ ptr[ok_len] = '_';
+ ptr += ok_len;
+ }
+
+ return 0;
+} /* }}} int metric_type */
+
+/* The metric type, including its DNS name prefix. The type is not URL-encoded.
+ * All user-defined custom metric types have the DNS name custom.googleapis.com.
+ * Metric types should use a natural hierarchical grouping. */
+static int format_metric_type(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index) {
+ /* {{{ */
+ char buffer[4 * DATA_MAX_NAME_LEN];
+ metric_type(buffer, sizeof(buffer), ds, vl, ds_index);
+
+ return json_string(gen, buffer);
+} /* }}} int format_metric_type */
+
+/* TimeInterval
+ *
+ * {
+ * "endTime": string,
+ * "startTime": string,
+ * }
+ */
+static int format_time_interval(yajl_gen gen, int ds_type,
+ value_list_t const *vl, cdtime_t start_time) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "endTime") || json_time(gen, vl->time);
+ if (status != 0)
+ return status;
+
+ if ((ds_type == DS_TYPE_DERIVE) || (ds_type == DS_TYPE_COUNTER)) {
+ int status = json_string(gen, "startTime") || json_time(gen, start_time);
+ if (status != 0)
+ return status;
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_time_interval */
+
+/* read_cumulative_state reads the start time and start value of cumulative
+ * (i.e. DERIVE or COUNTER) metrics from the cache. If a metric is seen for the
+ * first time, or when a DERIVE metric is reset, the start time is (re)set to
+ * vl->time. */
+static int read_cumulative_state(data_set_t const *ds, value_list_t const *vl,
+ int ds_index, cdtime_t *ret_start_time,
+ int64_t *ret_start_value) {
+ int ds_type = ds->ds[ds_index].type;
+ if ((ds_type != DS_TYPE_DERIVE) && (ds_type != DS_TYPE_COUNTER)) {
+ return 0;
+ }
+
+ char start_value_key[DATA_MAX_NAME_LEN];
+ snprintf(start_value_key, sizeof(start_value_key),
+ "stackdriver:start_value[%d]", ds_index);
+
+ int status =
+ uc_meta_data_get_signed_int(vl, start_value_key, ret_start_value);
+ if ((status == 0) && ((ds_type != DS_TYPE_DERIVE) ||
+ (*ret_start_value <= vl->values[ds_index].derive))) {
+ return uc_meta_data_get_unsigned_int(vl, "stackdriver:start_time",
+ ret_start_time);
+ }
+
+ if (ds_type == DS_TYPE_DERIVE) {
+ *ret_start_value = vl->values[ds_index].derive;
+ } else {
+ *ret_start_value = (int64_t)vl->values[ds_index].counter;
+ }
+ *ret_start_time = vl->time;
+
+ status = uc_meta_data_add_signed_int(vl, start_value_key, *ret_start_value);
+ if (status != 0) {
+ return status;
+ }
+ return uc_meta_data_add_unsigned_int(vl, "stackdriver:start_time",
+ *ret_start_time);
+} /* int read_cumulative_state */
+
+/* Point
+ *
+ * {
+ * "interval": {
+ * object(TimeInterval)
+ * },
+ * "value": {
+ * object(TypedValue)
+ * },
+ * }
+ */
+static int format_point(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index,
+ cdtime_t start_time, int64_t start_value) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int ds_type = ds->ds[ds_index].type;
+
+ int status =
+ json_string(gen, "interval") ||
+ format_time_interval(gen, ds_type, vl, start_time) ||
+ json_string(gen, "value") ||
+ format_typed_value(gen, ds_type, vl->values[ds_index], start_value);
+ if (status != 0)
+ return status;
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_point */
+
+/* Metric
+ *
+ * {
+ * "type": string,
+ * "labels": {
+ * string: string,
+ * ...
+ * },
+ * }
+ */
+static int format_metric(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "type") ||
+ format_metric_type(gen, ds, vl, ds_index) ||
+ json_string(gen, "labels");
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_map_open(gen);
+ status = json_string(gen, "host") || json_string(gen, vl->host) ||
+ json_string(gen, "plugin_instance") ||
+ json_string(gen, vl->plugin_instance) ||
+ json_string(gen, "type_instance") ||
+ json_string(gen, vl->type_instance);
+ if (status != 0) {
+ return status;
+ }
+ yajl_gen_map_close(gen);
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_metric */
+
+/* TimeSeries
+ *
+ * {
+ * "metric": {
+ * object(Metric)
+ * },
+ * "resource": {
+ * object(MonitoredResource)
+ * },
+ * "metricKind": enum(MetricKind),
+ * "valueType": enum(ValueType),
+ * "points": [
+ * {
+ * object(Point)
+ * }
+ * ],
+ * }
+ */
+/* format_time_series formats a TimeSeries object. Returns EAGAIN when a
+ * cumulative metric is seen for the first time and cannot be sent to
+ * Stackdriver due to lack of state. */
+static int format_time_series(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index,
+ sd_resource_t *res) {
+ int ds_type = ds->ds[ds_index].type;
+
+ cdtime_t start_time = 0;
+ int64_t start_value = 0;
+ int status =
+ read_cumulative_state(ds, vl, ds_index, &start_time, &start_value);
+ if (status != 0) {
+ return status;
+ }
+ if (start_time == vl->time) {
+ /* for cumulative metrics, the interval must not be zero. */
+ return EAGAIN;
+ }
+
+ yajl_gen_map_open(gen);
+
+ status = json_string(gen, "metric") || format_metric(gen, ds, vl, ds_index) ||
+ json_string(gen, "resource") || format_gcm_resource(gen, res) ||
+ json_string(gen, "metricKind") || format_metric_kind(gen, ds_type) ||
+ json_string(gen, "valueType") || format_value_type(gen, ds_type) ||
+ json_string(gen, "points");
+ if (status != 0)
+ return status;
+
+ yajl_gen_array_open(gen);
+
+ status = format_point(gen, ds, vl, ds_index, start_time, start_value);
+ if (status != 0)
+ return status;
+
+ yajl_gen_array_close(gen);
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_time_series */
+
+/* Request body
+ *
+ * {
+ * "timeSeries": [
+ * {
+ * object(TimeSeries)
+ * }
+ * ],
+ * }
+ */
+static int sd_output_initialize(sd_output_t *out) /* {{{ */
+{
+ yajl_gen_map_open(out->gen);
+
+ int status = json_string(out->gen, "timeSeries");
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_array_open(out->gen);
+ return 0;
+} /* }}} int sd_output_initialize */
+
+static int sd_output_finalize(sd_output_t *out) /* {{{ */
+{
+ yajl_gen_array_close(out->gen);
+ yajl_gen_map_close(out->gen);
+
+ return 0;
+} /* }}} int sd_output_finalize */
+
+static void sd_output_reset_staged(sd_output_t *out) /* {{{ */
+{
+ void *key = NULL;
+
+ while (c_avl_pick(out->staged, &key, &(void *){NULL}) == 0)
+ sfree(key);
+} /* }}} void sd_output_reset_staged */
+
+sd_output_t *sd_output_create(sd_resource_t *res) /* {{{ */
+{
+ sd_output_t *out = calloc(1, sizeof(*out));
+ if (out == NULL)
+ return NULL;
+
+ out->res = res;
+
+ out->gen = yajl_gen_alloc(/* funcs = */ NULL);
+ if (out->gen == NULL) {
+ sd_output_destroy(out);
+ return NULL;
+ }
+
+ out->staged = c_avl_create((void *)strcmp);
+ if (out->staged == NULL) {
+ sd_output_destroy(out);
+ return NULL;
+ }
+
+ out->metric_descriptors = c_avl_create((void *)strcmp);
+ if (out->metric_descriptors == NULL) {
+ sd_output_destroy(out);
+ return NULL;
+ }
+
+ sd_output_initialize(out);
+
+ return out;
+} /* }}} sd_output_t *sd_output_create */
+
+void sd_output_destroy(sd_output_t *out) /* {{{ */
+{
+ if (out == NULL)
+ return;
+
+ if (out->metric_descriptors != NULL) {
+ void *key = NULL;
+ while (c_avl_pick(out->metric_descriptors, &key, &(void *){NULL}) == 0) {
+ sfree(key);
+ }
+ c_avl_destroy(out->metric_descriptors);
+ out->metric_descriptors = NULL;
+ }
+
+ if (out->staged != NULL) {
+ sd_output_reset_staged(out);
+ c_avl_destroy(out->staged);
+ out->staged = NULL;
+ }
+
+ if (out->gen != NULL) {
+ yajl_gen_free(out->gen);
+ out->gen = NULL;
+ }
+
+ if (out->res != NULL) {
+ sd_resource_destroy(out->res);
+ out->res = NULL;
+ }
+
+ sfree(out);
+} /* }}} void sd_output_destroy */
+
+int sd_output_add(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl) /* {{{ */
+{
+ /* first, check that we have all appropriate metric descriptors. */
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ char buffer[4 * DATA_MAX_NAME_LEN];
+ metric_type(buffer, sizeof(buffer), ds, vl, i);
+
+ if (c_avl_get(out->metric_descriptors, buffer, NULL) != 0) {
+ return ENOENT;
+ }
+ }
+
+ char key[6 * DATA_MAX_NAME_LEN];
+ int status = FORMAT_VL(key, sizeof(key), vl);
+ if (status != 0) {
+ ERROR("sd_output_add: FORMAT_VL failed with status %d.", status);
+ return status;
+ }
+
+ if (c_avl_get(out->staged, key, NULL) == 0) {
+ return EEXIST;
+ }
+
+ _Bool staged = 0;
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ int status = format_time_series(out->gen, ds, vl, i, out->res);
+ if (status == EAGAIN) {
+ /* first instance of a cumulative metric */
+ continue;
+ }
+ if (status != 0) {
+ ERROR("sd_output_add: format_time_series failed with status %d.", status);
+ return status;
+ }
+ staged = 1;
+ }
+
+ if (staged) {
+ c_avl_insert(out->staged, strdup(key), NULL);
+ }
+
+ size_t json_buffer_size = 0;
+ yajl_gen_get_buf(out->gen, &(unsigned char const *){NULL}, &json_buffer_size);
+ if (json_buffer_size > 65535)
+ return ENOBUFS;
+
+ return 0;
+} /* }}} int sd_output_add */
+
+int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl) {
+ /* {{{ */
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ char buffer[4 * DATA_MAX_NAME_LEN];
+ metric_type(buffer, sizeof(buffer), ds, vl, i);
+
+ char *key = strdup(buffer);
+ int status = c_avl_insert(out->metric_descriptors, key, NULL);
+ if (status != 0) {
+ sfree(key);
+ return status;
+ }
+ }
+
+ return 0;
+} /* }}} int sd_output_register_metric */
+
+char *sd_output_reset(sd_output_t *out) /* {{{ */
+{
+ sd_output_finalize(out);
+
+ unsigned char const *json_buffer = NULL;
+ yajl_gen_get_buf(out->gen, &json_buffer, &(size_t){0});
+ char *ret = strdup((void const *)json_buffer);
+
+ sd_output_reset_staged(out);
+
+ yajl_gen_free(out->gen);
+ out->gen = yajl_gen_alloc(/* funcs = */ NULL);
+
+ sd_output_initialize(out);
+
+ return ret;
+} /* }}} char *sd_output_reset */
+
+sd_resource_t *sd_resource_create(char const *type) /* {{{ */
+{
+ sd_resource_t *res = malloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+ memset(res, 0, sizeof(*res));
+
+ res->type = strdup(type);
+ if (res->type == NULL) {
+ sfree(res);
+ return NULL;
+ }
+
+ res->labels = NULL;
+ res->labels_num = 0;
+
+ return res;
+} /* }}} sd_resource_t *sd_resource_create */
+
+void sd_resource_destroy(sd_resource_t *res) /* {{{ */
+{
+ if (res == NULL)
+ return;
+
+ for (size_t i = 0; i < res->labels_num; i++) {
+ sfree(res->labels[i].key);
+ sfree(res->labels[i].value);
+ }
+ sfree(res->labels);
+ sfree(res->type);
+ sfree(res);
+} /* }}} void sd_resource_destroy */
+
+int sd_resource_add_label(sd_resource_t *res, char const *key,
+ char const *value) /* {{{ */
+{
+ if ((res == NULL) || (key == NULL) || (value == NULL))
+ return EINVAL;
+
+ sd_label_t *l =
+ realloc(res->labels, sizeof(*res->labels) * (res->labels_num + 1));
+ if (l == NULL)
+ return ENOMEM;
+
+ res->labels = l;
+ l = res->labels + res->labels_num;
+
+ l->key = strdup(key);
+ l->value = strdup(value);
+ if ((l->key == NULL) || (l->value == NULL)) {
+ sfree(l->key);
+ sfree(l->value);
+ return ENOMEM;
+ }
+
+ res->labels_num++;
+ return 0;
+} /* }}} int sd_resource_add_label */
+
+/* LabelDescriptor
+ *
+ * {
+ * "key": string,
+ * "valueType": enum(ValueType),
+ * "description": string,
+ * }
+ */
+static int format_label_descriptor(yajl_gen gen, char const *key) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "key") || json_string(gen, key) ||
+ json_string(gen, "valueType") || json_string(gen, "STRING");
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_label_descriptor */
+
+/* MetricDescriptor
+ *
+ * {
+ * "name": string,
+ * "type": string,
+ * "labels": [
+ * {
+ * object(LabelDescriptor)
+ * }
+ * ],
+ * "metricKind": enum(MetricKind),
+ * "valueType": enum(ValueType),
+ * "unit": string,
+ * "description": string,
+ * "displayName": string,
+ * }
+ */
+int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
+ data_set_t const *ds, value_list_t const *vl,
+ int ds_index) {
+ /* {{{ */
+ yajl_gen gen = yajl_gen_alloc(/* funcs = */ NULL);
+ if (gen == NULL) {
+ return ENOMEM;
+ }
+
+ int ds_type = ds->ds[ds_index].type;
+
+ yajl_gen_map_open(gen);
+
+ int status =
+ json_string(gen, "type") || format_metric_type(gen, ds, vl, ds_index) ||
+ json_string(gen, "metricKind") || format_metric_kind(gen, ds_type) ||
+ json_string(gen, "valueType") || format_value_type(gen, ds_type) ||
+ json_string(gen, "labels");
+ if (status != 0) {
+ yajl_gen_free(gen);
+ return status;
+ }
+
+ char const *labels[] = {"host", "plugin_instance", "type_instance"};
+ yajl_gen_array_open(gen);
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(labels); i++) {
+ int status = format_label_descriptor(gen, labels[i]);
+ if (status != 0) {
+ yajl_gen_free(gen);
+ return status;
+ }
+ }
+
+ yajl_gen_array_close(gen);
+ yajl_gen_map_close(gen);
+
+ unsigned char const *tmp = NULL;
+ yajl_gen_get_buf(gen, &tmp, &(size_t){0});
+ sstrncpy(buffer, (void const *)tmp, buffer_size);
+
+ yajl_gen_free(gen);
+ return 0;
+} /* }}} int sd_format_metric_descriptor */
--- /dev/null
+/**
+ * collectd - src/utils_format_stackdriver.h
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_FORMAT_STACKDRIVER_H
+#define UTILS_FORMAT_STACKDRIVER_H 1
+
+#include "collectd.h"
+#include "plugin.h"
+
+/* sd_output_t is a buffer to which value_list_t* can be added and from which
+ * an appropriately formatted char* can be read. */
+struct sd_output_s;
+typedef struct sd_output_s sd_output_t;
+
+/* sd_resource_t represents a MonitoredResource. */
+struct sd_resource_s;
+typedef struct sd_resource_s sd_resource_t;
+
+sd_output_t *sd_output_create(sd_resource_t *res);
+
+/* sd_output_destroy frees all memory used by out, including the
+ * sd_resource_t* passed to sd_output_create. */
+void sd_output_destroy(sd_output_t *out);
+
+/* sd_output_add adds a value_list_t* to "out".
+ *
+ * Return values:
+ * - 0 Success
+ * - ENOBUFS Success, but the buffer should be flushed soon.
+ * - EEXIST The value list is already encoded in the buffer.
+ * Flush the buffer, then call sd_output_add again.
+ * - ENOENT First time we encounter this metric. Create a metric descriptor
+ * using the Stackdriver API and then call
+ * sd_output_register_metric.
+ */
+int sd_output_add(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl);
+
+/* sd_output_register_metric adds the metric descriptor which vl maps to, to
+ * the list of known metric descriptors. */
+int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl);
+
+/* sd_output_reset resets the output and returns the previous content of the
+ * buffer. It is the caller's responsibility to call free() with the returned
+ * pointer. */
+char *sd_output_reset(sd_output_t *out);
+
+sd_resource_t *sd_resource_create(char const *type);
+void sd_resource_destroy(sd_resource_t *res);
+int sd_resource_add_label(sd_resource_t *res, char const *key,
+ char const *value);
+
+/* sd_format_metric_descriptor creates the payload for a
+ * projects.metricDescriptors.create() request. */
+int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
+ data_set_t const *ds, value_list_t const *vl,
+ int ds_index);
+
+#endif /* UTILS_FORMAT_STACKDRIVER_H */
--- /dev/null
+/**
+ * collectd - src/utils_format_stackdriver_test.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "testing.h"
+#include "utils_format_stackdriver.h"
+
+DEF_TEST(sd_format_metric_descriptor) {
+ value_list_t vl = {
+ .host = "example.com", .plugin = "unit-test", .type = "example",
+ };
+ char got[1024];
+
+ data_set_t ds_single = {
+ .type = "example",
+ .ds_num = 1,
+ .ds =
+ &(data_source_t){
+ .name = "value", .type = DS_TYPE_GAUGE, .min = NAN, .max = NAN,
+ },
+ };
+ EXPECT_EQ_INT(
+ 0, sd_format_metric_descriptor(got, sizeof(got), &ds_single, &vl, 0));
+ char const *want_single =
+ "{\"type\":\"custom.googleapis.com/collectd/unit_test/"
+ "example\",\"metricKind\":\"GAUGE\",\"valueType\":\"DOUBLE\",\"labels\":["
+ "{\"key\":\"host\",\"valueType\":\"STRING\"},{\"key\":\"plugin_"
+ "instance\",\"valueType\":\"STRING\"},{\"key\":\"type_instance\","
+ "\"valueType\":\"STRING\"}]}";
+ EXPECT_EQ_STR(want_single, got);
+
+ data_set_t ds_double = {
+ .type = "example",
+ .ds_num = 2,
+ .ds =
+ (data_source_t[]){
+ {.name = "one", .type = DS_TYPE_DERIVE, .min = 0, .max = NAN},
+ {.name = "two", .type = DS_TYPE_DERIVE, .min = 0, .max = NAN},
+ },
+ };
+ EXPECT_EQ_INT(
+ 0, sd_format_metric_descriptor(got, sizeof(got), &ds_double, &vl, 0));
+ char const *want_double =
+ "{\"type\":\"custom.googleapis.com/collectd/unit_test/"
+ "example_one\",\"metricKind\":\"CUMULATIVE\",\"valueType\":\"INT64\","
+ "\"labels\":[{\"key\":\"host\",\"valueType\":\"STRING\"},{\"key\":"
+ "\"plugin_instance\",\"valueType\":\"STRING\"},{\"key\":\"type_"
+ "instance\",\"valueType\":\"STRING\"}]}";
+ EXPECT_EQ_STR(want_double, got);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ RUN_TEST(sd_format_metric_descriptor);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_gce.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "common.h"
+#include "plugin.h"
+#include "utils_gce.h"
+#include "utils_oauth.h"
+#include "utils_time.h"
+
+#include <curl/curl.h>
+
+#ifndef GCP_METADATA_PREFIX
+#define GCP_METADATA_PREFIX "http://metadata.google.internal/computeMetadata/v1"
+#endif
+#ifndef GCE_METADATA_HEADER
+#define GCE_METADATA_HEADER "Metadata-Flavor: Google"
+#endif
+
+#ifndef GCE_INSTANCE_ID_URL
+#define GCE_INSTANCE_ID_URL GCP_METADATA_PREFIX "/instance/id"
+#endif
+#ifndef GCE_PROJECT_NUM_URL
+#define GCE_PROJECT_NUM_URL GCP_METADATA_PREFIX "/project/numeric-project-id"
+#endif
+#ifndef GCE_PROJECT_ID_URL
+#define GCE_PROJECT_ID_URL GCP_METADATA_PREFIX "/project/project-id"
+#endif
+#ifndef GCE_ZONE_URL
+#define GCE_ZONE_URL GCP_METADATA_PREFIX "/instance/zone"
+#endif
+
+#ifndef GCE_DEFAULT_SERVICE_ACCOUNT
+#define GCE_DEFAULT_SERVICE_ACCOUNT "default"
+#endif
+
+#ifndef GCE_SCOPE_URL
+#define GCE_SCOPE_URL_FORMAT \
+ GCP_METADATA_PREFIX "/instance/service-accounts/%s/scopes"
+#endif
+#ifndef GCE_TOKEN_URL
+#define GCE_TOKEN_URL_FORMAT \
+ GCP_METADATA_PREFIX "/instance/service-accounts/%s/token"
+#endif
+
+struct blob_s {
+ char *data;
+ size_t size;
+};
+typedef struct blob_s blob_t;
+
+static int on_gce = -1;
+
+static char *token = NULL;
+static char *token_email = NULL;
+static cdtime_t token_valid_until = 0;
+static pthread_mutex_t token_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static size_t write_callback(void *contents, size_t size, size_t nmemb,
+ void *ud) /* {{{ */
+{
+ size_t realsize = size * nmemb;
+ blob_t *blob = ud;
+
+ if ((0x7FFFFFF0 < blob->size) || (0x7FFFFFF0 - blob->size < realsize)) {
+ ERROR("utils_gce: write_callback: integer overflow");
+ return 0;
+ }
+
+ blob->data = realloc(blob->data, blob->size + realsize + 1);
+ if (blob->data == NULL) {
+ /* out of memory! */
+ ERROR(
+ "utils_gce: write_callback: not enough memory (realloc returned NULL)");
+ return 0;
+ }
+
+ memcpy(blob->data + blob->size, contents, realsize);
+ blob->size += realsize;
+ blob->data[blob->size] = 0;
+
+ return realsize;
+} /* }}} size_t write_callback */
+
+/* read_url will issue a GET request for the given URL, setting the magic GCE
+ * metadata header in the process. On success, the response body is returned
+ * and it's the caller's responsibility to free it. On failure, an error is
+ * logged and NULL is returned. */
+static char *read_url(char const *url) /* {{{ */
+{
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ ERROR("utils_gce: curl_easy_init failed.");
+ return NULL;
+ }
+
+ struct curl_slist *headers = curl_slist_append(NULL, GCE_METADATA_HEADER);
+
+ char curl_errbuf[CURL_ERROR_SIZE];
+ blob_t blob = {0};
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &blob);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+
+ int status = curl_easy_perform(curl);
+ if (status != CURLE_OK) {
+ ERROR("utils_gce: fetching %s failed: %s", url, curl_errbuf);
+ sfree(blob.data);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ return NULL;
+ }
+
+ long http_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+ if ((http_code < 200) || (http_code >= 300)) {
+ ERROR("write_gcm plugin: fetching %s failed: HTTP error %ld", url,
+ http_code);
+ sfree(blob.data);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ return NULL;
+ }
+
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ return blob.data;
+} /* }}} char *read_url */
+
+_Bool gce_check(void) /* {{{ */
+{
+ if (on_gce != -1)
+ return on_gce == 1;
+
+ DEBUG("utils_gce: Checking whether I'm running on GCE ...");
+
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ ERROR("utils_gce: curl_easy_init failed.");
+ return 0;
+ }
+
+ struct curl_slist *headers = curl_slist_append(NULL, GCE_METADATA_HEADER);
+
+ char curl_errbuf[CURL_ERROR_SIZE];
+ blob_t blob = {NULL, 0};
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_callback);
+ curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &blob);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_URL, GCP_METADATA_PREFIX "/");
+
+ int status = curl_easy_perform(curl);
+ if ((status != CURLE_OK) || (blob.data == NULL) ||
+ (strstr(blob.data, "Metadata-Flavor: Google") == NULL)) {
+ DEBUG("utils_gce: ... no (%s)",
+ (status != CURLE_OK)
+ ? "curl_easy_perform failed"
+ : (blob.data == NULL) ? "blob.data == NULL"
+ : "Metadata-Flavor header not found");
+ sfree(blob.data);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ on_gce = 0;
+ return 0;
+ }
+ sfree(blob.data);
+
+ long http_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+ if ((http_code < 200) || (http_code >= 300)) {
+ DEBUG("utils_gce: ... no (HTTP status %ld)", http_code);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ on_gce = 0;
+ return 0;
+ }
+
+ DEBUG("utils_gce: ... yes");
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ on_gce = 1;
+ return 1;
+} /* }}} _Bool gce_check */
+
+char *gce_project_id(void) /* {{{ */
+{
+ return read_url(GCE_PROJECT_ID_URL);
+} /* }}} char *gce_project_id */
+
+char *gce_instance_id(void) /* {{{ */
+{
+ return read_url(GCE_INSTANCE_ID_URL);
+} /* }}} char *gce_instance_id */
+
+char *gce_zone(void) /* {{{ */
+{
+ return read_url(GCE_ZONE_URL);
+} /* }}} char *gce_instance_id */
+
+char *gce_scope(char const *email) /* {{{ */
+{
+ char url[1024];
+
+ snprintf(url, sizeof(url), GCE_SCOPE_URL_FORMAT,
+ (email != NULL) ? email : GCE_DEFAULT_SERVICE_ACCOUNT);
+
+ return read_url(url);
+} /* }}} char *gce_scope */
+
+int gce_access_token(char const *email, char *buffer,
+ size_t buffer_size) /* {{{ */
+{
+ char url[1024];
+ char *json;
+ cdtime_t now = cdtime();
+
+ pthread_mutex_lock(&token_lock);
+
+ if (email == NULL)
+ email = GCE_DEFAULT_SERVICE_ACCOUNT;
+
+ if ((token_email != NULL) && (strcmp(email, token_email) == 0) &&
+ (token_valid_until > now)) {
+ sstrncpy(buffer, token, buffer_size);
+ pthread_mutex_unlock(&token_lock);
+ return 0;
+ }
+
+ snprintf(url, sizeof(url), GCE_TOKEN_URL_FORMAT, email);
+ json = read_url(url);
+ if (json == NULL) {
+ pthread_mutex_unlock(&token_lock);
+ return -1;
+ }
+
+ char tmp[256];
+ cdtime_t expires_in = 0;
+ int status = oauth_parse_json_token(json, tmp, sizeof(tmp), &expires_in);
+ sfree(json);
+ if (status != 0) {
+ pthread_mutex_unlock(&token_lock);
+ return status;
+ }
+
+ sfree(token);
+ token = strdup(tmp);
+
+ sfree(token_email);
+ token_email = strdup(email);
+
+ /* let tokens expire a bit early */
+ expires_in = (expires_in * 95) / 100;
+ token_valid_until = now + expires_in;
+
+ sstrncpy(buffer, token, buffer_size);
+ pthread_mutex_unlock(&token_lock);
+ return 0;
+} /* }}} char *gce_token */
--- /dev/null
+/**
+ * collectd - src/utils_gce.h
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_GCE_H
+#define UTILS_GCE_H 1
+
+/* gce_check returns 1 when running on Google Compute Engine (GCE) and 0
+ * otherwise. */
+_Bool gce_check(void);
+
+/* gce_project_id returns the project ID of the instance, as configured when
+ * creating the project.
+ * For example "example-project-a". */
+char *gce_project_id(void);
+
+/* gce_instance_id returns the unique ID of the GCE instance. */
+char *gce_instance_id(void);
+
+/* gce_zone returns the zone in which the GCE instance runs. */
+char *gce_zone(void);
+
+/* gce_scope returns the list of scopes for the given service account (or the
+ * default service account when NULL is passed). */
+char *gce_scope(char const *email);
+
+/* gce_access_token acquires an OAuth access token for the given service account
+ * (or
+ * the default service account when NULL is passed) and stores it in buffer.
+ * Access tokens are automatically cached and renewed when they expire. Returns
+ * zero on success, non-zero otherwise. */
+int gce_access_token(char const *email, char *buffer, size_t buffer_size);
+
+#endif
change_bin_width(lc, latency);
bin = (latency - 1) / lc->bin_width;
if (bin >= HISTOGRAM_NUM_BINS) {
- ERROR("utils_latency: latency_counter_add: Invalid bin: %" PRIu64, bin);
+ P_ERROR("latency_counter_add: Invalid bin: %" PRIu64, bin);
return;
}
}
* Pavel Rochnyack <pavel2000 at ngs.ru>
*/
-#include "utils_latency_config.h"
-#include "common.h"
#include "collectd.h"
+#include "common.h"
+#include "utils_latency_config.h"
static int latency_config_add_percentile(latency_config_t *conf,
- oconfig_item_t *ci,
- const char *plugin) {
+ oconfig_item_t *ci) {
double percent;
int status = cf_util_get_double(ci, &percent);
if (status != 0)
return status;
if ((percent <= 0.0) || (percent >= 100)) {
- ERROR("%s plugin: The value for \"%s\" must be between 0 and 100, "
- "exclusively.",
- plugin, ci->key);
+ P_ERROR("The value for \"%s\" must be between 0 and 100, "
+ "exclusively.",
+ ci->key);
return ERANGE;
}
double *tmp = realloc(conf->percentile,
sizeof(*conf->percentile) * (conf->percentile_num + 1));
if (tmp == NULL) {
- ERROR("%s plugin: realloc failed.", plugin);
+ P_ERROR("realloc failed.");
return ENOMEM;
}
conf->percentile = tmp;
return 0;
} /* int latency_config_add_percentile */
-static int latency_config_add_bucket(latency_config_t *conf, oconfig_item_t *ci,
- const char *plugin) {
+static int latency_config_add_bucket(latency_config_t *conf,
+ oconfig_item_t *ci) {
if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_NUMBER) ||
(ci->values[1].type != OCONFIG_TYPE_NUMBER)) {
- ERROR("%s plugin: \"%s\" requires exactly two numeric arguments.", plugin,
- ci->key);
+ P_ERROR("\"%s\" requires exactly two numeric arguments.", ci->key);
return EINVAL;
}
if (ci->values[1].value.number &&
ci->values[1].value.number <= ci->values[0].value.number) {
- ERROR("%s plugin: MIN must be less than MAX in \"%s\".", plugin, ci->key);
+ P_ERROR("MIN must be less than MAX in \"%s\".", ci->key);
return ERANGE;
}
if (ci->values[0].value.number < 0) {
- ERROR("%s plugin: MIN must be greater then or equal to zero in \"%s\".",
- plugin, ci->key);
+ P_ERROR("MIN must be greater then or equal to zero in \"%s\".", ci->key);
return ERANGE;
}
latency_bucket_t *tmp =
realloc(conf->buckets, sizeof(*conf->buckets) * (conf->buckets_num + 1));
if (tmp == NULL) {
- ERROR("%s plugin: realloc failed.", plugin);
+ P_ERROR("realloc failed.");
return ENOMEM;
}
conf->buckets = tmp;
return 0;
} /* int latency_config_add_bucket */
-int latency_config(latency_config_t *conf, oconfig_item_t *ci,
- char const *plugin) {
+int latency_config(latency_config_t *conf, oconfig_item_t *ci) {
int status = 0;
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
if (strcasecmp("Percentile", child->key) == 0)
- status = latency_config_add_percentile(conf, child, plugin);
+ status = latency_config_add_percentile(conf, child);
else if (strcasecmp("Bucket", child->key) == 0)
- status = latency_config_add_bucket(conf, child, plugin);
+ status = latency_config_add_bucket(conf, child);
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);
+ P_WARNING("\"%s\" is not a valid option within a \"%s\" block.",
+ child->key, ci->key);
if (status != 0)
return status;
if ((status == 0) && (conf->percentile_num == 0) &&
(conf->buckets_num == 0)) {
- ERROR("%s plugin: The \"%s\" block must contain at least one "
- "\"Percentile\" or \"Bucket\" option.",
- plugin, ci->key);
+ P_ERROR("The \"%s\" block must contain at least one "
+ "\"Percentile\" or \"Bucket\" option.",
+ ci->key);
return EINVAL;
}
*/
} latency_config_t;
-int latency_config(latency_config_t *conf, oconfig_item_t *ci,
- char const *plugin);
+int latency_config(latency_config_t *conf, oconfig_item_t *ci);
int latency_config_copy(latency_config_t *dst, const latency_config_t src);
new = cu_mount_gen_getmntent();
#elif HAVE_SEQ_GETMNTENT
#error "This version of `getmntent' hat not yet been implemented!"
+#elif HAVE_GETMNTENT_R
+ new = cu_mount_getmntent();
#elif HAVE_ONE_GETMNTENT
new = cu_mount_getmntent();
#else
--- /dev/null
+/**
+ * collectd - src/utils_oauth.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "common.h"
+#include "plugin.h"
+#include "utils_oauth.h"
+
+#include <curl/curl.h>
+
+#include <yajl/yajl_tree.h>
+#include <yajl/yajl_version.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/sha.h>
+
+/*
+ * Private variables
+ */
+#define GOOGLE_TOKEN_URL "https://accounts.google.com/o/oauth2/token"
+
+/* Max send buffer size, since there will be only one writer thread and
+ * monitoring api supports up to 100K bytes in one request, 64K is reasonable
+ */
+#define MAX_BUFFER_SIZE 65536
+#define MAX_ENCODE_SIZE 2048
+
+struct oauth_s {
+ char *url;
+ char *iss;
+ char *aud;
+ char *scope;
+
+ EVP_PKEY *key;
+
+ char *token;
+ cdtime_t valid_until;
+};
+
+struct memory_s {
+ char *memory;
+ size_t size;
+};
+typedef struct memory_s memory_t;
+
+#define OAUTH_GRANT_TYPE "urn:ietf:params:oauth:grant-type:jwt-bearer"
+#define OAUTH_EXPIRATION_TIME TIME_T_TO_CDTIME_T(3600)
+#define OAUTH_HEADER "{\"alg\":\"RS256\",\"typ\":\"JWT\"}"
+
+static const char OAUTH_CLAIM_FORMAT[] = "{"
+ "\"iss\":\"%s\","
+ "\"scope\":\"%s\","
+ "\"aud\":\"%s\","
+ "\"exp\":%lu,"
+ "\"iat\":%lu"
+ "}";
+
+static size_t write_memory(void *contents, size_t size, size_t nmemb, /* {{{ */
+ void *userp) {
+ size_t realsize = size * nmemb;
+ memory_t *mem = (memory_t *)userp;
+ char *tmp;
+
+ if (0x7FFFFFF0 < mem->size || 0x7FFFFFF0 - mem->size < realsize) {
+ ERROR("integer overflow");
+ return 0;
+ }
+
+ tmp = (char *)realloc((void *)mem->memory, mem->size + realsize + 1);
+ if (tmp == NULL) {
+ /* out of memory! */
+ ERROR("write_memory: not enough memory (realloc returned NULL)");
+ return 0;
+ }
+ mem->memory = tmp;
+
+ memcpy(&(mem->memory[mem->size]), contents, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+
+ return realsize;
+} /* }}} size_t write_memory */
+
+/* Base64-encodes "s" and stores the result in buffer.
+ * Returns zero on success, non-zero otherwise. */
+static int base64_encode_n(char const *s, size_t s_size, /* {{{ */
+ char *buffer, size_t buffer_size) {
+ BIO *b64;
+ BUF_MEM *bptr;
+ int status;
+ size_t i;
+
+ /* Set up the memory-base64 chain */
+ b64 = BIO_new(BIO_f_base64());
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ b64 = BIO_push(b64, BIO_new(BIO_s_mem()));
+
+ /* Write data to the chain */
+ BIO_write(b64, (void const *)s, s_size);
+ status = BIO_flush(b64);
+ if (status != 1) {
+ ERROR("utils_oauth: base64_encode: BIO_flush() failed.");
+ BIO_free_all(b64);
+ return -1;
+ }
+
+ /* Never fails */
+ BIO_get_mem_ptr(b64, &bptr);
+
+ if (buffer_size <= bptr->length) {
+ ERROR("utils_oauth: base64_encode: Buffer too small.");
+ BIO_free_all(b64);
+ return -1;
+ }
+
+ /* Copy data to buffer. */
+ memcpy(buffer, bptr->data, bptr->length);
+ buffer[bptr->length] = 0;
+
+ /* replace + with -, / with _ and remove padding = at the end */
+ for (i = 0; i < bptr->length; i++) {
+ if (buffer[i] == '+') {
+ buffer[i] = '-';
+ } else if (buffer[i] == '/') {
+ buffer[i] = '_';
+ } else if (buffer[i] == '=') {
+ buffer[i] = 0;
+ }
+ }
+
+ BIO_free_all(b64);
+ return 0;
+} /* }}} int base64_encode_n */
+
+/* Base64-encodes "s" and stores the result in buffer.
+ * Returns zero on success, non-zero otherwise. */
+static int base64_encode(char const *s, /* {{{ */
+ char *buffer, size_t buffer_size) {
+ return base64_encode_n(s, strlen(s), buffer, buffer_size);
+} /* }}} int base64_encode */
+
+/* get_header returns the base64 encoded OAuth header. */
+static int get_header(char *buffer, size_t buffer_size) /* {{{ */
+{
+ char header[] = OAUTH_HEADER;
+
+ return base64_encode(header, buffer, buffer_size);
+} /* }}} int get_header */
+
+/* get_claim constructs an OAuth claim and returns it as base64 encoded string.
+ */
+static int get_claim(oauth_t *auth, char *buffer, size_t buffer_size) /* {{{ */
+{
+ char claim[buffer_size];
+ cdtime_t exp;
+ cdtime_t iat;
+ int status;
+
+ iat = cdtime();
+ exp = iat + OAUTH_EXPIRATION_TIME;
+
+ /* create the claim set */
+ status =
+ snprintf(claim, sizeof(claim), OAUTH_CLAIM_FORMAT, auth->iss, auth->scope,
+ auth->aud, (unsigned long)CDTIME_T_TO_TIME_T(exp),
+ (unsigned long)CDTIME_T_TO_TIME_T(iat));
+ if (status < 1)
+ return -1;
+ else if ((size_t)status >= sizeof(claim))
+ return ENOMEM;
+
+ DEBUG("utils_oauth: get_claim() = %s", claim);
+
+ return base64_encode(claim, buffer, buffer_size);
+} /* }}} int get_claim */
+
+/* get_signature signs header and claim with pkey and returns the signature in
+ * buffer. */
+static int get_signature(char *buffer, size_t buffer_size, /* {{{ */
+ char const *header, char const *claim,
+ EVP_PKEY *pkey) {
+ char payload[buffer_size];
+ size_t payload_len;
+ char signature[buffer_size];
+ unsigned int signature_size;
+ int status;
+
+ /* Make the string to sign */
+ payload_len = snprintf(payload, sizeof(payload), "%s.%s", header, claim);
+ if (payload_len < 1) {
+ return -1;
+ } else if (payload_len >= sizeof(payload)) {
+ return ENOMEM;
+ }
+
+ /* Create the signature */
+ signature_size = EVP_PKEY_size(pkey);
+ if (signature_size > sizeof(signature)) {
+ ERROR("utils_oauth: Signature is too large (%u bytes).", signature_size);
+ return -1;
+ }
+
+ EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+
+ /* EVP_SignInit(3SSL) claims this is a void function, but in fact it returns
+ * an int. We're not going to rely on this, though. */
+ EVP_SignInit(ctx, EVP_sha256());
+
+ status = EVP_SignUpdate(ctx, payload, payload_len);
+ if (status != 1) {
+ char errbuf[1024];
+ ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+ ERROR("utils_oauth: EVP_SignUpdate failed: %s", errbuf);
+
+ EVP_MD_CTX_free(ctx);
+ return -1;
+ }
+
+ status =
+ EVP_SignFinal(ctx, (unsigned char *)signature, &signature_size, pkey);
+ if (status != 1) {
+ char errbuf[1024];
+ ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+ ERROR("utils_oauth: EVP_SignFinal failed: %s", errbuf);
+
+ EVP_MD_CTX_free(ctx);
+ return -1;
+ }
+
+ EVP_MD_CTX_free(ctx);
+
+ return base64_encode_n(signature, (size_t)signature_size, buffer,
+ buffer_size);
+} /* }}} int get_signature */
+
+static int get_assertion(oauth_t *auth, char *buffer,
+ size_t buffer_size) /* {{{ */
+{
+ char header[buffer_size];
+ char claim[buffer_size];
+ char signature[buffer_size];
+ int status;
+
+ status = get_header(header, sizeof(header));
+ if (status != 0)
+ return -1;
+
+ status = get_claim(auth, claim, sizeof(claim));
+ if (status != 0)
+ return -1;
+
+ status =
+ get_signature(signature, sizeof(signature), header, claim, auth->key);
+ if (status != 0)
+ return -1;
+
+ status = snprintf(buffer, buffer_size, "%s.%s.%s", header, claim, signature);
+ if (status < 1)
+ return -1;
+ else if (status >= buffer_size)
+ return ENOMEM;
+
+ return 0;
+} /* }}} int get_assertion */
+
+int oauth_parse_json_token(char const *json, /* {{{ */
+ char *out_access_token, size_t access_token_size,
+ cdtime_t *expires_in) {
+ time_t expire_in_seconds = 0;
+ yajl_val root;
+ yajl_val token_val;
+ yajl_val expire_val;
+ char errbuf[1024];
+ const char *token_path[] = {"access_token", NULL};
+ const char *expire_path[] = {"expires_in", NULL};
+
+ root = yajl_tree_parse(json, errbuf, sizeof(errbuf));
+ if (root == NULL) {
+ ERROR("utils_oauth: oauth_parse_json_token: parse error %s", errbuf);
+ return -1;
+ }
+
+ token_val = yajl_tree_get(root, token_path, yajl_t_string);
+ if (token_val == NULL) {
+ ERROR("utils_oauth: oauth_parse_json_token: access token field not found");
+ yajl_tree_free(root);
+ return -1;
+ }
+ sstrncpy(out_access_token, YAJL_GET_STRING(token_val), access_token_size);
+
+ expire_val = yajl_tree_get(root, expire_path, yajl_t_number);
+ if (expire_val == NULL) {
+ ERROR("utils_oauth: oauth_parse_json_token: expire field found");
+ yajl_tree_free(root);
+ return -1;
+ }
+ expire_in_seconds = (time_t)YAJL_GET_INTEGER(expire_val);
+ DEBUG("oauth_parse_json_token: expires_in %lu",
+ (unsigned long)expire_in_seconds);
+
+ *expires_in = TIME_T_TO_CDTIME_T(expire_in_seconds);
+ yajl_tree_free(root);
+ return 0;
+} /* }}} int oauth_parse_json_token */
+
+static int new_token(oauth_t *auth) /* {{{ */
+{
+ CURL *curl;
+ char assertion[1024];
+ char post_data[1024];
+ memory_t data;
+ char access_token[256];
+ cdtime_t expires_in;
+ cdtime_t now;
+ char curl_errbuf[CURL_ERROR_SIZE];
+ int status = 0;
+
+ data.size = 0;
+ data.memory = NULL;
+
+ now = cdtime();
+
+ status = get_assertion(auth, assertion, sizeof(assertion));
+ if (status != 0) {
+ ERROR("utils_oauth: Failed to get token using service account %s.",
+ auth->iss);
+ return -1;
+ }
+
+ snprintf(post_data, sizeof(post_data), "grant_type=%s&assertion=%s",
+ OAUTH_GRANT_TYPE, assertion);
+
+ curl = curl_easy_init();
+ if (curl == NULL) {
+ ERROR("utils_oauth: curl_easy_init failed.");
+ return -1;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
+ curl_easy_setopt(curl, CURLOPT_URL, auth->url);
+
+ status = curl_easy_perform(curl);
+ if (status != CURLE_OK) {
+ ERROR("utils_oauth: curl_easy_perform failed with status %i: %s", status,
+ curl_errbuf);
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return -1;
+ } else {
+ long http_code = 0;
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+ if ((http_code < 200) || (http_code >= 300)) {
+ ERROR("utils_oauth: POST request to %s failed: HTTP error %ld", auth->url,
+ http_code);
+ if (data.memory != NULL)
+ INFO("utils_oauth: Server replied: %s", data.memory);
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return -1;
+ }
+ }
+
+ status = oauth_parse_json_token(data.memory, access_token,
+ sizeof(access_token), &expires_in);
+ if (status != 0) {
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return -1;
+ }
+
+ sfree(auth->token);
+ auth->token = strdup(access_token);
+ if (auth->token == NULL) {
+ ERROR("utils_oauth: strdup failed");
+ auth->valid_until = 0;
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+ return -1;
+ }
+
+ INFO("utils_oauth: OAuth2 access token is valid for %.3fs",
+ CDTIME_T_TO_DOUBLE(expires_in));
+ auth->valid_until = now + expires_in;
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return 0;
+} /* }}} int new_token */
+
+static int renew_token(oauth_t *auth) /* {{{ */
+{
+ /* Renew OAuth token 30 seconds *before* it expires. */
+ cdtime_t const slack = TIME_T_TO_CDTIME_T(30);
+
+ if (auth->valid_until > (cdtime() + slack))
+ return 0;
+
+ return new_token(auth);
+} /* }}} int renew_token */
+
+static oauth_t *oauth_create(char const *url, char const *iss,
+ char const *scope, char const *aud,
+ EVP_PKEY *key) /* {{{ */
+{
+ oauth_t *auth;
+
+ if ((url == NULL) || (iss == NULL) || (scope == NULL) || (aud == NULL) ||
+ (key == NULL))
+ return NULL;
+
+ auth = malloc(sizeof(*auth));
+ if (auth == NULL)
+ return NULL;
+ memset(auth, 0, sizeof(*auth));
+
+ auth->url = strdup(url);
+ auth->iss = strdup(iss);
+ auth->scope = strdup(scope);
+ auth->aud = strdup(aud);
+
+ if ((auth->url == NULL) || (auth->iss == NULL) || (auth->scope == NULL) ||
+ (auth->aud == NULL)) {
+ oauth_destroy(auth);
+ return NULL;
+ }
+
+ auth->key = key;
+
+ return auth;
+} /* }}} oauth_t *oauth_create */
+
+/*
+ * Public
+ */
+oauth_google_t oauth_create_google_json(char const *buffer, char const *scope) {
+ char errbuf[1024];
+ yajl_val root = yajl_tree_parse(buffer, errbuf, sizeof(errbuf));
+ if (root == NULL) {
+ ERROR("utils_oauth: oauth_create_google_json: parse error %s", errbuf);
+ return (oauth_google_t){NULL};
+ }
+
+ yajl_val field_project =
+ yajl_tree_get(root, (char const *[]){"project_id", NULL}, yajl_t_string);
+ if (field_project == NULL) {
+ ERROR("utils_oauth: oauth_create_google_json: project_id field not found");
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+ char const *project_id = YAJL_GET_STRING(field_project);
+
+ yajl_val field_iss = yajl_tree_get(
+ root, (char const *[]){"client_email", NULL}, yajl_t_string);
+ if (field_iss == NULL) {
+ ERROR(
+ "utils_oauth: oauth_create_google_json: client_email field not found");
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ yajl_val field_token_uri =
+ yajl_tree_get(root, (char const *[]){"token_uri", NULL}, yajl_t_string);
+ char const *token_uri = (field_token_uri != NULL)
+ ? YAJL_GET_STRING(field_token_uri)
+ : GOOGLE_TOKEN_URL;
+
+ yajl_val field_priv_key =
+ yajl_tree_get(root, (char const *[]){"private_key", NULL}, yajl_t_string);
+ if (field_priv_key == NULL) {
+ ERROR("utils_oauth: oauth_create_google_json: private_key field not found");
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ BIO *bp = BIO_new_mem_buf(YAJL_GET_STRING(field_priv_key), -1);
+ EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
+ if (pkey == NULL) {
+ char errbuf[1024];
+ ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+ ERROR(
+ "utils_oauth: oauth_create_google_json: parsing private key failed: %s",
+ errbuf);
+ BIO_free(bp);
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ BIO_free(bp);
+
+ oauth_t *oauth = oauth_create(token_uri, YAJL_GET_STRING(field_iss), scope,
+ token_uri, pkey);
+ if (oauth == NULL) {
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ oauth_google_t ret = {
+ .project_id = strdup(project_id), .oauth = oauth,
+ };
+
+ yajl_tree_free(root);
+ return ret;
+} /* oauth_google_t oauth_create_google_json */
+
+oauth_google_t oauth_create_google_file(char const *path,
+ char const *scope) { /* {{{ */
+ int fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return (oauth_google_t){NULL};
+
+ struct stat st = {0};
+ if (fstat(fd, &st) != 0) {
+ close(fd);
+ return (oauth_google_t){NULL};
+ }
+
+ size_t buf_size = (size_t)st.st_size;
+ char *buf = calloc(1, buf_size + 1);
+ if (buf == NULL) {
+ close(fd);
+ return (oauth_google_t){NULL};
+ }
+
+ if (sread(fd, buf, buf_size) != 0) {
+ free(buf);
+ close(fd);
+ return (oauth_google_t){NULL};
+ }
+ close(fd);
+ buf[buf_size] = 0;
+
+ oauth_google_t ret = oauth_create_google_json(buf, scope);
+
+ free(buf);
+ return ret;
+} /* }}} oauth_google_t oauth_create_google_file */
+
+/* oauth_create_google_default checks for JSON credentials in well-known
+ * positions, similar to gcloud and other tools. */
+oauth_google_t oauth_create_google_default(char const *scope) {
+ char const *app_creds;
+ if ((app_creds = getenv("GOOGLE_APPLICATION_CREDENTIALS")) != NULL) {
+ oauth_google_t ret = oauth_create_google_file(app_creds, scope);
+ if (ret.oauth == NULL) {
+ ERROR("The environment variable GOOGLE_APPLICATION_CREDENTIALS is set to "
+ "\"%s\" but that file could not be read.",
+ app_creds);
+ } else {
+ return ret;
+ }
+ }
+
+ char const *home;
+ if ((home = getenv("HOME")) != NULL) {
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path),
+ "%s/.config/gcloud/application_default_credentials.json", home);
+
+ oauth_google_t ret = oauth_create_google_file(path, scope);
+ if (ret.oauth != NULL) {
+ return ret;
+ }
+ }
+
+ return (oauth_google_t){NULL};
+} /* }}} oauth_google_t oauth_create_google_default */
+
+void oauth_destroy(oauth_t *auth) /* {{{ */
+{
+ if (auth == NULL)
+ return;
+
+ sfree(auth->url);
+ sfree(auth->iss);
+ sfree(auth->scope);
+ sfree(auth->aud);
+
+ if (auth->key != NULL) {
+ EVP_PKEY_free(auth->key);
+ auth->key = NULL;
+ }
+
+ sfree(auth);
+} /* }}} void oauth_destroy */
+
+int oauth_access_token(oauth_t *auth, char *buffer,
+ size_t buffer_size) /* {{{ */
+{
+ int status;
+
+ if (auth == NULL)
+ return EINVAL;
+
+ status = renew_token(auth);
+ if (status != 0)
+ return status;
+ assert(auth->token != NULL);
+
+ sstrncpy(buffer, auth->token, buffer_size);
+ return 0;
+} /* }}} int oauth_access_token */
--- /dev/null
+/**
+ * collectd - src/utils_oauth.h
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_OAUTH_H
+#define UTILS_OAUTH_H
+
+#include "collectd.h"
+#include "utils_time.h"
+
+#ifndef GOOGLE_OAUTH_URL
+#define GOOGLE_OAUTH_URL "https://www.googleapis.com/oauth2/v3/token"
+#endif
+
+struct oauth_s;
+typedef struct oauth_s oauth_t;
+
+int oauth_parse_json_token(char const *json, char *out_access_token,
+ size_t access_token_size, cdtime_t *expires_in);
+
+typedef struct {
+ char *project_id;
+ oauth_t *oauth;
+} oauth_google_t;
+
+/* oauth_create_google_json creates an OAuth object from JSON encoded
+ * credentials. */
+oauth_google_t oauth_create_google_json(char const *json, char const *scope);
+
+/* oauth_create_google_file reads path, which contains JSON encoded service
+ * account credentials, and returns an OAuth object. */
+oauth_google_t oauth_create_google_file(char const *path, char const *scope);
+
+/* oauth_create_google_default looks for service account credentials in a couple
+ * of well-known places and returns an OAuth object if found. The well known
+ * locations are:
+ *
+ * - ${GOOGLE_APPLICATION_CREDENTIALS}
+ * - ${HOME}/.config/gcloud/application_default_credentials.json
+ */
+oauth_google_t oauth_create_google_default(char const *scope);
+
+/* oauth_destroy frees all resources associated with an OAuth object. */
+void oauth_destroy(oauth_t *auth);
+
+int oauth_access_token(oauth_t *auth, char *buffer, size_t buffer_size);
+
+#endif
--- /dev/null
+/**
+ * collectd - src/tests/utils_oauth_test.c
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at google.com>
+ **/
+
+#include "testing.h"
+#include "utils_oauth.h"
+
+struct {
+ char *json;
+ int status;
+ char *access_token;
+ cdtime_t expires_in;
+} cases[] = {
+ {
+ "{\"access_token\":\"MaeC6kaePhie1ree\",\"expires_in\":3600}",
+ /* status = */ 0, "MaeC6kaePhie1ree", TIME_T_TO_CDTIME_T_STATIC(3600),
+ },
+ {
+ "{\"token_type\":\"Bearer\",\"expires_in\":1800,\"access_token\":"
+ "\"aeThiebee2gushuY\"}",
+ /* status = */ 0, "aeThiebee2gushuY", TIME_T_TO_CDTIME_T_STATIC(1800),
+ },
+ {
+ "{\"ignored_key\":\"uaph5aewaeghi1Ge\",\"expires_in\":3600}",
+ /* status = */ -1, NULL, 0,
+ },
+ {
+ /* expires_in missing */
+ "{\"access_token\":\"shaephohbie9Ahch\"}",
+ /* status = */ -1, NULL, 0,
+ },
+};
+
+DEF_TEST(simple) /* {{{ */
+{
+ size_t i;
+ _Bool success = 1;
+
+ for (i = 0; i < (sizeof(cases) / sizeof(cases[0])); i++) {
+ char buffer[1024];
+ cdtime_t expires_in;
+
+ EXPECT_EQ_INT(cases[i].status,
+ oauth_parse_json_token(cases[i].json, buffer, sizeof(buffer),
+ &expires_in));
+ if (cases[i].status != 0)
+ continue;
+
+ EXPECT_EQ_STR(cases[i].access_token, buffer);
+ EXPECT_EQ_UINT64(cases[i].expires_in, expires_in);
+ }
+
+ return success ? 0 : -1;
+} /* }}} simple */
+
+DEF_TEST(oauth_create_google_json) {
+ char const *in =
+ "{\"type\": \"service_account\","
+ "\"project_id\":\"collectd.org:unit-test\","
+ "\"private_key_id\": \"ed7b4eb6c1b61a7bedab5bcafff374f7fc820698\","
+ "\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n"
+ "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNvS71Lr2WIEqx\\n"
+ "U766iJGORVVib0FnHhOf/0FEI4Hw+tF11vP3LZj0AyQFIi/h2l2EDXOr43C6Gt+K\\n"
+ "0stsyaWvRNzeQa+dUFY5A/ZEtdvYVPq7KudML5Hs9DNmWFlM/iIfQyIUJ+vHv7fe\\n"
+ "pJGgu4ZgSkNWehmWj3qiRzIvYxKvDIQizqPZNlTh+33KQcT2x+ErkuB3snQu8hSK\\n"
+ "HAg2sCvORqKGOvN9F4bAqXt5T0NVjGy4YXeuif1p/Np/GH6Ys1p+etgGwvIimXIv\\n"
+ "jFL9K/ZtrTOcFdy4R5bwrj2piCZa2T5H6fupVp2tVgIuS53r2fEaBMLD97oAvwZ3\\n"
+ "9XPxG1NLAgMBAAECggEACgHroKcrN1FkdgyzSIKFG1evCBCOV17kqHyI5wYXzNTT\\n"
+ "zyNrZDjBFGQkt+U0/AucTznnnahSCZNuD+QiBgLRqYgJevwp99Z6YzVDS438Xsuq\\n"
+ "Ezmf3O+sGEu78Pys11cTP38LT3yuS4iSqo9Jus5JrTG05dDJoYO4J4rxW3xlDRj8\\n"
+ "lQUimXI+S9skaSusf0oErDrjuQG9dxmhnGcSEX+rIe9G0UygTNuI0KKGJ8jmnPz5\\n"
+ "OS+sM8qrKcnjrvENFWKLb11HlliHkh6dILoO5rvf5DR+XGKM7BFAsdWg6oI7SFGh\\n"
+ "S6zGZ0jUR7QAugrjbTlDOCnAuZ+Mbc/4yHZ3u5PlcQKBgQDuvH1ds1YmmbOllOK5\\n"
+ "JtkdjCUUyH1bgkMrmcg/KkRARPRHQvfAioZsC6d0fa6jq0kTW/3Zu14IsVXgM8xK\\n"
+ "fuNSp8LdY+NCtJnfvdLaChgAwZaQLX4qgV0qYw8iLv5ifa4ZY0qaZioJCzkv57y1\\n"
+ "KkavYvITboO7aUSa441Zko9c+wKBgQDcndg0QpWH6JMz/FkCf/KDyW/cUODfKXhP\\n"
+ "5p9eTcVlfDL2sAb2RzVhvKZcuWXVwnfaDP0oBj2/SBLGx0idUb+VHdM/IGiLroyK\\n"
+ "pAHpNM//dowiGL1qPPOLXrzF/vn+w4t2Dqggfcqu52SzRiyaxUtSMnNyyyU19cO+\\n"
+ "pb7wAS5x8QKBgCW7WL0UeQtEw6Xp8CN/RlVrLvkn7tglsGQVvBZvobXesBULOokN\\n"
+ "28z70o2Qx6dKjRQoN+jPuj75eC8lQKaNg3Qu25eOD/8c+CzqnYakjcKg1iEXb5dc\\n"
+ "NtNaMKwgbUg3wOp2TPY2K3KeeX1ezO59LgrOQqBbmSpnqtYoHNEJXus9AoGAWl/y\\n"
+ "9J2eIdm9i5tBX0vIrgHz5/3d0K1tUtX3zSrwxT0Wp4W+pF7RWGNuhyePtvx+Gn4d\\n"
+ "qqq72sMMpg93CLM3Vz+rjP2atjXf7t92xPDUkCMhDsqxtXaYkixSCo4EHUA/vjIM\\n"
+ "35qIUBQMZYBGv3Q5AcgXERx09uDhuhSt3iWtwBECgYAHFnCh8fKsJbQrVN10tU/h\\n"
+ "ofVx0KZkUpBz8eNQPuxt4aY+LyWsKVKtnduw2WdumuOY66cUN1lsi8Bz/cq1dhPt\\n"
+ "Oc2S7pqjbu2Q1Oqx+/yr6jqsvKaSxHmcpbWQBsGn6UaWZgYZcAtQBcqDAp7pylwj\\n"
+ "tejRh0NB8d81H5Dli1Qfzw==\\n"
+ "-----END PRIVATE KEY-----\\n\","
+ "\"client_email\":\"example-sacct@unit-test.iam.gserviceaccount.com\", "
+ "\"client_id\": \"109958449193027604084\","
+ "\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\","
+ "\"token_uri\":\"https://accounts.google.com/o/oauth2/token\","
+ "\"auth_provider_x509_cert_url\":"
+ "\"https://www.googleapis.com/oauth2/v1/certs\","
+ "\"client_x509_cert_url\":\"https://www.googleapis.com/robot/v1/"
+ "metadata/x509/example-sacct%40ssc-serv-dev.iam.gserviceaccount.com\"}";
+
+ oauth_google_t ret =
+ oauth_create_google_json(in, "https://collectd.org/example.scope");
+
+ EXPECT_EQ_STR("collectd.org:unit-test", ret.project_id);
+
+ CHECK_NOT_NULL(ret.oauth);
+ struct {
+ char *url;
+ char *iss;
+ char *aud;
+ char *scope;
+ } *obj = (void *)ret.oauth;
+
+ EXPECT_EQ_STR("https://accounts.google.com/o/oauth2/token", obj->url);
+ EXPECT_EQ_STR("example-sacct@unit-test.iam.gserviceaccount.com", obj->iss);
+ EXPECT_EQ_STR("https://collectd.org/example.scope", obj->scope);
+
+ free(ret.project_id);
+ oauth_destroy(ret.oauth);
+
+ return 0;
+}
+
+int main(int argc, char **argv) /* {{{ */
+{
+ RUN_TEST(simple);
+ RUN_TEST(oauth_create_google_json);
+
+ END_TEST;
+} /* }}} int main */
static int cu_tail_reopen(cu_tail_t *obj) {
int seek_end = 0;
- FILE *fh;
struct stat stat_buf = {0};
- int status;
- status = stat(obj->file, &stat_buf);
+ int status = stat(obj->file, &stat_buf);
if (status != 0) {
- ERROR("utils_tail: stat (%s) failed: %s", obj->file, STRERRNO);
+ P_ERROR("utils_tail: stat (%s) failed: %s", obj->file, STRERRNO);
return -1;
}
if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino)) {
/* Seek to the beginning if file was truncated */
if (stat_buf.st_size < obj->stat.st_size) {
- INFO("utils_tail: File `%s' was truncated.", obj->file);
+ P_INFO("utils_tail: File `%s' was truncated.", obj->file);
status = fseek(obj->fh, 0, SEEK_SET);
if (status != 0) {
- ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
+ P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
fclose(obj->fh);
obj->fh = NULL;
return -1;
if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino))
seek_end = 1;
- fh = fopen(obj->file, "r");
+ FILE *fh = fopen(obj->file, "r");
if (fh == NULL) {
- ERROR("utils_tail: fopen (%s) failed: %s", obj->file, STRERRNO);
+ P_ERROR("utils_tail: fopen (%s) failed: %s", obj->file, STRERRNO);
return -1;
}
if (seek_end != 0) {
status = fseek(fh, 0, SEEK_END);
if (status != 0) {
- ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
+ P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
fclose(fh);
return -1;
}
char plugin_instance[DATA_MAX_NAME_LEN];
char type[DATA_MAX_NAME_LEN];
char type_instance[DATA_MAX_NAME_LEN];
- cdtime_t interval;
latency_config_t latency_config;
};
typedef struct cu_tail_match_simple_s cu_tail_match_simple_t;
typedef struct cu_tail_match_match_s cu_tail_match_match_t;
struct cu_tail_match_s {
- int flags;
cu_tail_t *tail;
-
- cdtime_t interval;
cu_tail_match_match_t *matches;
size_t matches_num;
};
sstrncpy(vl.type, data->type, sizeof(vl.type));
sstrncpy(vl.type_instance, data->type_instance, sizeof(vl.type_instance));
- vl.interval = data->interval;
plugin_dispatch_values(&vl);
match_value_reset(match_value);
sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin));
sstrncpy(vl.plugin_instance, data->plugin_instance,
sizeof(vl.plugin_instance));
- vl.interval = data->interval;
vl.time = cdtime();
/* Submit percentiles */
obj->matches = temp;
obj->matches_num++;
- DEBUG("tail_match_add_match interval %lf",
- CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval));
temp = obj->matches + (obj->matches_num - 1);
temp->match = match;
const char *excluderegex, int ds_type,
const char *plugin, const char *plugin_instance,
const char *type, const char *type_instance,
- const latency_config_t latency_cfg,
- const cdtime_t interval) {
+ const latency_config_t latency_cfg) {
cu_match_t *match;
cu_tail_match_simple_t *user_data;
int status;
sstrncpy(user_data->type_instance, type_instance,
sizeof(user_data->type_instance));
- user_data->interval = interval;
-
if ((ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
(ds_type & UTILS_MATCH_CF_GAUGE_DIST)) {
status = latency_config_copy(&user_data->latency_config, latency_cfg);
* When `tail_match_destroy' is called the `user_data' pointer is freed using
* the `free_user_data' callback - if it is not NULL.
* When using this interface the `tail_match' module doesn't dispatch any
- * values
- * itself - all that has to happen in either the match-callbacks or the
- * submit_match callback.
+ * values itself - all that has to happen in either the match-callbacks or
+ * the submit_match callback.
*
* RETURN VALUE
* Zero upon success, non-zero otherwise.
* tail_match_add_match_simple
*
* DESCRIPTION
- * A simplified version of `tail_match_add_match'. The regular expressen
- * `regex'
- * must match a number, which is then dispatched according to `ds_type'. See
- * the `match_create_simple' function in utils_match.h for a description how
- * this flag effects calculation of a new value.
+ * A simplified version of `tail_match_add_match'. The regular expression
+ * `regex' must match a number, which is then dispatched according to
+ * `ds_type'.
+ * See the `match_create_simple' function in utils_match.h for a description
+ * how this flag effects calculation of a new value.
* The values gathered are dispatched by the tail_match module in this case.
- * The
- * passed `plugin', `plugin_instance', `type', and `type_instance' are
+ * The passed `plugin', `plugin_instance', `type', and `type_instance' are
* directly used when submitting these values.
* With excluderegex it is possible to exlude lines from the match.
* The `latency_cfg' specifies configuration for submitting latency.
const char *excluderegex, int ds_type,
const char *plugin, const char *plugin_instance,
const char *type, const char *type_instance,
- const latency_config_t latency_cfg,
- const cdtime_t interval);
+ const latency_config_t latency_cfg);
/*
* NAME
* added `utils_match' objects.
* After all lines have been read and processed, the submit_match callback is
* called or, in case of tail_match_add_match_simple, the data is dispatched
- * to
- * the daemon directly.
+ * to the daemon directly.
*
* RETURN VALUE
* Zero on success, nonzero on failure.
do { \
status = _f(__VA_ARGS__); \
if (status != 0) \
- ERROR(PLUGIN_NAME ": Failed to get " _name); \
+ ERROR(PLUGIN_NAME " plugin: Failed to get " _name); \
} while (0)
/* Connection. */
static int refresh_lists(struct lv_read_instance *inst);
-struct lv_info {
- virDomainInfo di;
- unsigned long long total_user_cpu_time;
- unsigned long long total_syst_cpu_time;
-};
-
struct lv_block_info {
virDomainBlockStatsStruct bi;
virErrorPtr err; \
err = (conn) ? virConnGetLastError((conn)) : virGetLastError(); \
if (err) \
- ERROR("%s: %s", (s), err->message); \
+ ERROR(PLUGIN_NAME " plugin: %s failed: %s", (s), err->message); \
} while (0)
-static void init_lv_info(struct lv_info *info) {
- if (info != NULL)
- memset(info, 0, sizeof(*info));
-}
-
-static int lv_domain_info(virDomainPtr dom, struct lv_info *info) {
-#ifdef HAVE_CPU_STATS
- virTypedParameterPtr param = NULL;
- int nparams = 0;
-#endif /* HAVE_CPU_STATS */
- int ret = virDomainGetInfo(dom, &(info->di));
- if (ret != 0) {
- return ret;
- }
-
-#ifdef HAVE_CPU_STATS
- nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0);
- if (nparams < 0) {
- VIRT_ERROR(conn, "getting the CPU params count");
- return -1;
- }
-
- param = calloc(nparams, sizeof(virTypedParameter));
- if (param == NULL) {
- ERROR("virt plugin: alloc(%i) for cpu parameters failed.", nparams);
- return -1;
- }
-
- ret = virDomainGetCPUStats(dom, param, nparams, -1, 1, 0); // total stats.
- if (ret < 0) {
- virTypedParamsClear(param, nparams);
- sfree(param);
- VIRT_ERROR(conn, "getting the disk params values");
- return -1;
- }
-
- for (int i = 0; i < nparams; ++i) {
- if (!strcmp(param[i].field, "user_time"))
- info->total_user_cpu_time = param[i].value.ul;
- else if (!strcmp(param[i].field, "system_time"))
- info->total_syst_cpu_time = param[i].value.ul;
- }
-
- virTypedParamsClear(param, nparams);
- sfree(param);
-#endif /* HAVE_CPU_STATS */
-
- return 0;
-}
-
static void init_value_list(value_list_t *vl, virDomainPtr dom) {
const char *name;
char uuid[VIR_UUID_STRING_BUFLEN];
value_list_t vl = VALUE_LIST_INIT;
if (!notif) {
- ERROR(PLUGIN_NAME ": init_notif: NULL pointer");
+ ERROR(PLUGIN_NAME " plugin: init_notif: NULL pointer");
return -1;
}
submit(dom, type, devname, values, STATIC_ARRAY_SIZE(values));
} /* void submit_derive2 */
-static void pcpu_submit(virDomainPtr dom, struct lv_info *info) {
-#ifdef HAVE_CPU_STATS
- if (extra_stats & ex_stats_pcpu)
- submit_derive2("ps_cputime", info->total_user_cpu_time,
- info->total_syst_cpu_time, dom, NULL);
-#endif /* HAVE_CPU_STATS */
-}
-
static double cpu_ns_to_percent(unsigned int node_cpus,
unsigned long long cpu_time_old,
unsigned long long cpu_time_new) {
(time_diff_sec * node_cpus * NANOSEC_IN_SEC);
}
- DEBUG(PLUGIN_NAME ": node_cpus=%u cpu_time_old=%" PRIu64
+ DEBUG(PLUGIN_NAME " plugin: node_cpus=%u cpu_time_old=%" PRIu64
" cpu_time_new=%" PRIu64 "cpu_time_diff=%" PRIu64
" time_diff_sec=%f percent=%f",
node_cpus, (uint64_t)cpu_time_old, (uint64_t)cpu_time_new,
}
if (ex_stats_table[j + 1].name == NULL) {
- ERROR(PLUGIN_NAME ": Unmatched ExtraStats option: %s", exstats[i]);
+ ERROR(PLUGIN_NAME " plugin: Unmatched ExtraStats option: %s",
+ exstats[i]);
}
}
}
static void domain_state_submit_notif(virDomainPtr dom, int state, int reason) {
if ((state < 0) || ((size_t)state >= STATIC_ARRAY_SIZE(domain_states))) {
- ERROR(PLUGIN_NAME ": Array index out of bounds: state=%d", state);
+ ERROR(PLUGIN_NAME " plugin: Array index out of bounds: state=%d", state);
return;
}
#ifdef HAVE_DOM_REASON
if ((reason < 0) ||
((size_t)reason >= STATIC_ARRAY_SIZE(domain_reasons[0]))) {
- ERROR(PLUGIN_NAME ": Array index out of bounds: reason=%d", reason);
+ ERROR(PLUGIN_NAME " plugin: Array index out of bounds: reason=%d", reason);
return;
}
* have different number of reasons. We need to check if reason was
* successfully parsed */
if (!reason_str) {
- ERROR(PLUGIN_NAME ": Invalid reason (%d) for domain state: %s", reason,
- state_str);
+ ERROR(PLUGIN_NAME " plugin: Invalid reason (%d) for domain state: %s",
+ reason, state_str);
return;
}
#else
severity = NOTIF_FAILURE;
break;
default:
- ERROR(PLUGIN_NAME ": Unrecognized domain state (%d)", state);
+ ERROR(PLUGIN_NAME " plugin: Unrecognized domain state (%d)", state);
return;
}
submit_notif(dom, severity, msg, "domain_state", NULL);
}
if (strcasecmp(key, "HostnameFormat") == 0) {
- char *value_copy;
- char *fields[HF_MAX_FIELDS];
- int n;
-
- value_copy = strdup(value);
+ char *value_copy = strdup(value);
if (value_copy == NULL) {
ERROR(PLUGIN_NAME " plugin: strdup failed.");
return -1;
}
- n = strsplit(value_copy, fields, HF_MAX_FIELDS);
+ char *fields[HF_MAX_FIELDS];
+ int n = strsplit(value_copy, fields, HF_MAX_FIELDS);
if (n < 1) {
sfree(value_copy);
ERROR(PLUGIN_NAME " plugin: HostnameFormat: no fields");
}
if (strcasecmp(key, "PluginInstanceFormat") == 0) {
- char *value_copy;
- char *fields[PLGINST_MAX_FIELDS];
- int n;
-
- value_copy = strdup(value);
+ char *value_copy = strdup(value);
if (value_copy == NULL) {
ERROR(PLUGIN_NAME " plugin: strdup failed.");
return -1;
}
- n = strsplit(value_copy, fields, PLGINST_MAX_FIELDS);
+ char *fields[PLGINST_MAX_FIELDS];
+ int n = strsplit(value_copy, fields, PLGINST_MAX_FIELDS);
if (n < 1) {
sfree(value_copy);
ERROR(PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
}
int status = virNodeGetInfo(conn, &nodeinfo);
if (status != 0) {
- ERROR(PLUGIN_NAME ": virNodeGetInfo failed");
+ ERROR(PLUGIN_NAME " plugin: virNodeGetInfo failed");
return -1;
}
}
virVcpuInfoPtr vinfo = calloc(nr_virt_cpu, sizeof(vinfo[0]));
if (vinfo == NULL) {
- ERROR(PLUGIN_NAME " plugin: malloc failed.");
+ ERROR(PLUGIN_NAME " plugin: calloc failed.");
return -1;
}
unsigned char *cpumaps = calloc(nr_virt_cpu, cpu_map_len);
if (cpumaps == NULL) {
- ERROR(PLUGIN_NAME " plugin: malloc failed.");
+ ERROR(PLUGIN_NAME " plugin: calloc failed.");
sfree(vinfo);
return -1;
}
return 0;
}
+#ifdef HAVE_CPU_STATS
+static int get_pcpu_stats(virDomainPtr dom) {
+ int nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0);
+ if (nparams < 0) {
+ VIRT_ERROR(conn, "getting the CPU params count");
+ return -1;
+ }
+
+ virTypedParameterPtr param = calloc(nparams, sizeof(virTypedParameter));
+ if (param == NULL) {
+ ERROR(PLUGIN_NAME " plugin: alloc(%i) for cpu parameters failed.", nparams);
+ return -1;
+ }
+
+ int ret = virDomainGetCPUStats(dom, param, nparams, -1, 1, 0); // total stats.
+ if (ret < 0) {
+ virTypedParamsClear(param, nparams);
+ sfree(param);
+ VIRT_ERROR(conn, "getting the CPU params values");
+ return -1;
+ }
+
+ unsigned long long total_user_cpu_time = 0;
+ unsigned long long total_syst_cpu_time = 0;
+
+ for (int i = 0; i < nparams; ++i) {
+ if (!strcmp(param[i].field, "user_time"))
+ total_user_cpu_time = param[i].value.ul;
+ else if (!strcmp(param[i].field, "system_time"))
+ total_syst_cpu_time = param[i].value.ul;
+ }
+
+ if (total_user_cpu_time > 0 || total_syst_cpu_time > 0)
+ submit_derive2("ps_cputime", total_user_cpu_time, total_syst_cpu_time, dom,
+ NULL);
+
+ virTypedParamsClear(param, nparams);
+ sfree(param);
+
+ return 0;
+}
+#endif /* HAVE_CPU_STATS */
+
#ifdef HAVE_DOM_REASON
static void domain_state_submit(virDomainPtr dom, int state, int reason) {
#endif /* HAVE_JOB_STATS */
static int get_domain_metrics(domain_t *domain) {
- struct lv_info info;
-
if (!domain || !domain->ptr) {
- ERROR(PLUGIN_NAME ": get_domain_metrics: NULL pointer");
+ ERROR(PLUGIN_NAME " plugin: get_domain_metrics: NULL pointer");
return -1;
}
- init_lv_info(&info);
- int status = lv_domain_info(domain->ptr, &info);
+ virDomainInfo info;
+ int status = virDomainGetInfo(domain->ptr, &info);
if (status != 0) {
ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
status);
}
/* Gather remaining stats only for running domains */
- if (info.di.state != VIR_DOMAIN_RUNNING)
+ if (info.state != VIR_DOMAIN_RUNNING)
return 0;
- pcpu_submit(domain->ptr, &info);
- cpu_submit(domain, info.di.cpuTime);
+#ifdef HAVE_CPU_STATS
+ if (extra_stats & ex_stats_pcpu)
+ get_pcpu_stats(domain->ptr);
+#endif
+
+ cpu_submit(domain, info.cpuTime);
- memory_submit(domain->ptr, (gauge_t)info.di.memory * 1024);
+ memory_submit(domain->ptr, (gauge_t)info.memory * 1024);
- GET_STATS(get_vcpu_stats, "vcpu stats", domain->ptr, info.di.nrVirtCpu);
+ GET_STATS(get_vcpu_stats, "vcpu stats", domain->ptr, info.nrVirtCpu);
GET_STATS(get_memory_stats, "memory stats", domain->ptr);
#ifdef HAVE_PERF_STATS
#endif
/* Update cached virDomainInfo. It has to be done after cpu_submit */
- memcpy(&domain->info, &info.di, sizeof(domain->info));
+ memcpy(&domain->info, &info, sizeof(domain->info));
return 0;
}
assert(thread_data != NULL);
ret = pthread_mutex_init(&thread_data->active_mutex, NULL);
if (ret != 0) {
- ERROR(PLUGIN_NAME ": Failed to initialize mutex, err %u", ret);
+ ERROR(PLUGIN_NAME " plugin: Failed to initialize mutex, err %u", ret);
return ret;
}
#endif
if (status != 0)
- ERROR(PLUGIN_NAME " failed to get metrics for domain=%s",
+ ERROR(PLUGIN_NAME " plugin: failed to get metrics for domain=%s",
virDomainGetName(dom->ptr));
}
int status = get_block_stats(&state->block_devices[i]);
if (status != 0)
ERROR(PLUGIN_NAME
- " failed to get stats for block device (%s) in domain %s",
+ " plugin: failed to get stats for block device (%s) in domain %s",
state->block_devices[i].path,
- virDomainGetName(state->domains[i].ptr));
+ virDomainGetName(state->block_devices[i].dom));
}
/* Get interface stats for each domain. */
for (int i = 0; i < state->nr_interface_devices; ++i) {
int status = get_if_dev_stats(&state->interface_devices[i]);
if (status != 0)
- ERROR(PLUGIN_NAME
- " failed to get interface stats for device (%s) in domain %s",
- state->interface_devices[i].path,
- virDomainGetName(state->interface_devices[i].dom));
+ ERROR(
+ PLUGIN_NAME
+ " plugin: failed to get interface stats for device (%s) in domain %s",
+ state->interface_devices[i].path,
+ virDomainGetName(state->interface_devices[i].dom));
}
return 0;
static int add_domain(struct lv_read_state *state, virDomainPtr dom,
bool active) {
- domain_t *new_ptr;
- int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1);
- if (state->domains)
- new_ptr = realloc(state->domains, new_size);
- else
- new_ptr = malloc(new_size);
+ int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1);
+ domain_t *new_ptr = realloc(state->domains, new_size);
if (new_ptr == NULL)
return -1;
static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
const char *path) {
- struct block_device *new_ptr;
- int new_size =
- sizeof(state->block_devices[0]) * (state->nr_block_devices + 1);
- char *path_copy;
- path_copy = strdup(path);
+ char *path_copy = strdup(path);
if (!path_copy)
return -1;
- if (state->block_devices)
- new_ptr = realloc(state->block_devices, new_size);
- else
- new_ptr = malloc(new_size);
+ int new_size =
+ sizeof(state->block_devices[0]) * (state->nr_block_devices + 1);
+ struct block_device *new_ptr = realloc(state->block_devices, new_size);
if (new_ptr == NULL) {
sfree(path_copy);
return -1;
static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
const char *path, const char *address,
unsigned int number) {
- struct interface_device *new_ptr;
- int new_size =
- sizeof(state->interface_devices[0]) * (state->nr_interface_devices + 1);
- char *path_copy, *address_copy, number_string[21];
if ((path == NULL) || (address == NULL))
return EINVAL;
- path_copy = strdup(path);
+ char *path_copy = strdup(path);
if (!path_copy)
return -1;
- address_copy = strdup(address);
+ char *address_copy = strdup(address);
if (!address_copy) {
sfree(path_copy);
return -1;
}
+ char number_string[21];
snprintf(number_string, sizeof(number_string), "interface-%u", number);
+ char *number_copy = strdup(number_string);
+ if (!number_copy) {
+ sfree(path_copy);
+ sfree(address_copy);
+ return -1;
+ }
- if (state->interface_devices)
- new_ptr = realloc(state->interface_devices, new_size);
- else
- new_ptr = malloc(new_size);
+ int new_size =
+ sizeof(state->interface_devices[0]) * (state->nr_interface_devices + 1);
+ struct interface_device *new_ptr =
+ realloc(state->interface_devices, new_size);
if (new_ptr == NULL) {
sfree(path_copy);
sfree(address_copy);
+ sfree(number_copy);
return -1;
}
+
state->interface_devices = new_ptr;
state->interface_devices[state->nr_interface_devices].dom = dom;
state->interface_devices[state->nr_interface_devices].path = path_copy;
state->interface_devices[state->nr_interface_devices].address = address_copy;
- state->interface_devices[state->nr_interface_devices].number =
- strdup(number_string);
+ state->interface_devices[state->nr_interface_devices].number = number_copy;
return state->nr_interface_devices++;
}
/**
* collectd - src/wireless.c
- * Copyright (C) 2006,2007 Florian octo Forster
+ * Copyright (C) 2006-2018 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"),
#include "common.h"
#include "plugin.h"
-#if !KERNEL_LINUX
+#if KERNEL_LINUX
+#include <linux/if.h>
+#include <linux/wireless.h>
+#include <sys/ioctl.h>
+#else
#error "No applicable input method."
#endif
/* there are a variety of names for the wireless device */
if ((fh = fopen(WIRELESS_PROC_FILE, "r")) == NULL) {
- WARNING("wireless: fopen: %s", STRERRNO);
+ ERROR("wireless plugin: fopen: %s", STRERRNO);
+ return -1;
+ }
+
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ ERROR("wireless plugin: socket: %s", STRERRNO);
+ fclose(fh);
return -1;
}
wireless_submit(device, "signal_power", power);
wireless_submit(device, "signal_noise", noise);
+ struct iwreq req = {
+ .ifr_ifrn.ifrn_name = {0},
+ };
+ sstrncpy(req.ifr_ifrn.ifrn_name, device, sizeof(req.ifr_ifrn.ifrn_name));
+ if (ioctl(sock, SIOCGIWRATE, &req) == -1) {
+ WARNING("wireless plugin: ioctl(SIOCGIWRATE): %s", STRERRNO);
+ } else {
+ wireless_submit(device, "bitrate", (double)req.u.bitrate.value);
+ }
+
devices_found++;
}
+ close(sock);
fclose(fh);
/* If no wireless devices are present return an error, so the plugin
* Protocol "udp"
* LogSendErrors true
* Prefix "collectd"
+ * UseTags true
* </Carbon>
* </Plugin>
*/
cf_util_get_flag(child, &cb->format_flags, GRAPHITE_PRESERVE_SEPARATOR);
else if (strcasecmp("DropDuplicateFields", child->key) == 0)
cf_util_get_flag(child, &cb->format_flags, GRAPHITE_DROP_DUPE_FIELDS);
+ else if (strcasecmp("UseTags", child->key) == 0)
+ cf_util_get_flag(child, &cb->format_flags, GRAPHITE_USE_TAGS);
else if (strcasecmp("EscapeCharacter", child->key) == 0)
config_set_char(&cb->escape_char, child);
else {
status = cf_util_get_flag(child, &tctx->graphite_flags,
GRAPHITE_PRESERVE_SEPARATOR);
+ } else if (strcasecmp("GraphiteUseTags", child->key) == 0) {
+ status =
+ cf_util_get_flag(child, &tctx->graphite_flags, GRAPHITE_USE_TAGS);
+
} else if (strcasecmp("GraphitePrefix", child->key) == 0) {
status = cf_util_get_string(child, &tctx->prefix);
} else if (strcasecmp("GraphitePostfix", child->key) == 0) {
static c_avl_tree_t *metrics;
static pthread_mutex_t metrics_lock = PTHREAD_MUTEX_INITIALIZER;
+static char *httpd_host = NULL;
static unsigned short httpd_port = 9103;
static struct MHD_Daemon *httpd;
snprintf(service, sizeof(service), "%hu", httpd_port);
struct addrinfo *res;
- int status = getaddrinfo(NULL, service,
+ int status = getaddrinfo(httpd_host, service,
&(struct addrinfo){
.ai_flags = AI_PASSIVE | AI_ADDRCONFIG,
.ai_family = addrfamily,
if (fd == -1)
continue;
- int tmp = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)) != 0) {
- WARNING("write_prometheus: setsockopt(SO_REUSEADDR) failed: %s",
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) != 0) {
+ WARNING("write_prometheus plugin: setsockopt(SO_REUSEADDR) failed: %s",
STRERRNO);
close(fd);
fd = -1;
continue;
}
+ char str_node[NI_MAXHOST];
+ char str_service[NI_MAXSERV];
+
+ getnameinfo(ai->ai_addr, ai->ai_addrlen, str_node, sizeof(str_node),
+ str_service, sizeof(str_service),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ INFO("write_prometheus plugin: Listening on [%s]:%s.", str_node,
+ str_service);
break;
}
if (fd == -1)
fd = prom_open_socket(PF_INET);
if (fd == -1) {
- ERROR("write_prometheus plugin: Opening a listening socket failed.");
+ ERROR("write_prometheus plugin: Opening a listening socket for [%s]:%hu "
+ "failed.",
+ (httpd_host != NULL) ? httpd_host : "::", httpd_port);
return NULL;
}
+ unsigned int flags = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG;
+#if MHD_VERSION >= 0x00095300
+ flags |= MHD_USE_INTERNAL_POLLING_THREAD;
+#endif
+
struct MHD_Daemon *d = MHD_start_daemon(
- MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, httpd_port,
+ flags, httpd_port,
/* MHD_AcceptPolicyCallback = */ NULL,
/* MHD_AcceptPolicyCallback arg = */ NULL, http_handler, NULL,
MHD_OPTION_LISTEN_SOCKET, fd, MHD_OPTION_EXTERNAL_LOGGER, prom_logger,
for (int i = 0; i < ci->children_num; i++) {
oconfig_item_t *child = ci->children + i;
- if (strcasecmp("Port", child->key) == 0) {
+ if (strcasecmp("Host", child->key) == 0) {
+#if MHD_VERSION >= 0x00090000
+ cf_util_get_string(child, &httpd_host);
+#else
+ ERROR("write_prometheus plugin: Option `Host' not supported. Please "
+ "upgrade libmicrohttpd to at least 0.9.0");
+ return -1;
+#endif
+ } else if (strcasecmp("Port", child->key) == 0) {
int status = cf_util_get_port_number(child);
if (status > 0)
httpd_port = (unsigned short)status;
if (httpd == NULL) {
httpd = prom_start_daemon();
if (httpd == NULL) {
- ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
return -1;
}
DEBUG("write_prometheus plugin: Successfully started microhttpd %s",
}
pthread_mutex_unlock(&metrics_lock);
+ sfree(httpd_host);
+
return 0;
}
if (ds != NULL) {
ds_name = ds->ds[ds_index].name;
if ((th->data_source[0] != 0) && (strcmp(ds_name, th->data_source) != 0))
- return STATE_OKAY;
+ return STATE_UNKNOWN;
}
if ((th->flags & UT_FLAG_INVERT) != 0) {
/* XXX: This is an experimental code, not optimized, not fast, not reliable,
* and probably, do not work as you expect. Enjoy! :D */
- if ((th->hysteresis > 0) &&
- ((prev_state = uc_get_state(ds, vl)) != STATE_OKAY)) {
+ prev_state = uc_get_state(ds, vl);
+ if ((th->hysteresis > 0) && (prev_state != STATE_OKAY) &&
+ (prev_state != STATE_UNKNOWN)) {
switch (prev_state) {
case STATE_ERROR:
if ((!isnan(th->failure_min) &&
--- /dev/null
+/**
+ * collectd - src/write_stackdriver.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "common.h"
+#include "configfile.h"
+#include "plugin.h"
+#include "utils_format_stackdriver.h"
+#include "utils_gce.h"
+#include "utils_oauth.h"
+
+#include <curl/curl.h>
+#include <pthread.h>
+#include <yajl/yajl_tree.h>
+
+/*
+ * Private variables
+ */
+#ifndef GCM_API_URL
+#define GCM_API_URL "https://monitoring.googleapis.com/v3"
+#endif
+
+#ifndef MONITORING_SCOPE
+#define MONITORING_SCOPE "https://www.googleapis.com/auth/monitoring"
+#endif
+
+struct wg_callback_s {
+ /* config */
+ char *email;
+ char *project;
+ char *url;
+ sd_resource_t *resource;
+
+ /* runtime */
+ oauth_t *auth;
+ sd_output_t *formatter;
+ CURL *curl;
+ char curl_errbuf[CURL_ERROR_SIZE];
+ /* used by flush */
+ size_t timeseries_count;
+ cdtime_t send_buffer_init_time;
+
+ pthread_mutex_t lock;
+};
+typedef struct wg_callback_s wg_callback_t;
+
+struct wg_memory_s {
+ char *memory;
+ size_t size;
+};
+typedef struct wg_memory_s wg_memory_t;
+
+static size_t wg_write_memory_cb(void *contents, size_t size,
+ size_t nmemb, /* {{{ */
+ void *userp) {
+ size_t realsize = size * nmemb;
+ wg_memory_t *mem = (wg_memory_t *)userp;
+
+ if (0x7FFFFFF0 < mem->size || 0x7FFFFFF0 - mem->size < realsize) {
+ ERROR("integer overflow");
+ return 0;
+ }
+
+ mem->memory = (char *)realloc((void *)mem->memory, mem->size + realsize + 1);
+ if (mem->memory == NULL) {
+ /* out of memory! */
+ ERROR("wg_write_memory_cb: not enough memory (realloc returned NULL)");
+ return 0;
+ }
+
+ memcpy(&(mem->memory[mem->size]), contents, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ return realsize;
+} /* }}} size_t wg_write_memory_cb */
+
+static char *wg_get_authorization_header(wg_callback_t *cb) { /* {{{ */
+ int status = 0;
+ char access_token[256];
+ char authorization_header[256];
+
+ assert((cb->auth != NULL) || gce_check());
+ if (cb->auth != NULL)
+ status = oauth_access_token(cb->auth, access_token, sizeof(access_token));
+ else
+ status = gce_access_token(cb->email, access_token, sizeof(access_token));
+ if (status != 0) {
+ ERROR("write_stackdriver plugin: Failed to get access token");
+ return NULL;
+ }
+
+ status = snprintf(authorization_header, sizeof(authorization_header),
+ "Authorization: Bearer %s", access_token);
+ if ((status < 1) || ((size_t)status >= sizeof(authorization_header)))
+ return NULL;
+
+ return strdup(authorization_header);
+} /* }}} char *wg_get_authorization_header */
+
+typedef struct {
+ int code;
+ char *message;
+} api_error_t;
+
+static api_error_t *parse_api_error(char const *body) {
+ char errbuf[1024];
+ yajl_val root = yajl_tree_parse(body, errbuf, sizeof(errbuf));
+ if (root == NULL) {
+ ERROR("write_stackdriver plugin: yajl_tree_parse failed: %s", errbuf);
+ return NULL;
+ }
+
+ api_error_t *err = calloc(1, sizeof(*err));
+ if (err == NULL) {
+ ERROR("write_stackdriver plugin: calloc failed");
+ yajl_tree_free(root);
+ return NULL;
+ }
+
+ yajl_val code = yajl_tree_get(root, (char const *[]){"error", "code", NULL},
+ yajl_t_number);
+ if (YAJL_IS_INTEGER(code)) {
+ err->code = YAJL_GET_INTEGER(code);
+ }
+
+ yajl_val message = yajl_tree_get(
+ root, (char const *[]){"error", "message", NULL}, yajl_t_string);
+ if (YAJL_IS_STRING(message)) {
+ char const *m = YAJL_GET_STRING(message);
+ if (m != NULL) {
+ err->message = strdup(m);
+ }
+ }
+
+ return err;
+}
+
+static char *api_error_string(api_error_t *err, char *buffer,
+ size_t buffer_size) {
+ if (err == NULL) {
+ strncpy(buffer, "Unknown error (API error is NULL)", buffer_size);
+ } else if (err->message == NULL) {
+ snprintf(buffer, buffer_size, "API error %d", err->code);
+ } else {
+ snprintf(buffer, buffer_size, "API error %d: %s", err->code, err->message);
+ }
+
+ return buffer;
+}
+#define API_ERROR_STRING(err) api_error_string(err, (char[1024]){""}, 1024)
+
+// do_post does a HTTP POST request, assuming a JSON payload and using OAuth
+// authentication. Returns -1 on error and the HTTP status code otherwise.
+// ret_content, if not NULL, will contain the server's response.
+// If ret_content is provided and the server responds with a 4xx or 5xx error,
+// an appropriate message will be logged.
+static int do_post(wg_callback_t *cb, char const *url, void const *payload,
+ wg_memory_t *ret_content) {
+ if (cb->curl == NULL) {
+ cb->curl = curl_easy_init();
+ if (cb->curl == NULL) {
+ ERROR("write_stackdriver plugin: curl_easy_init() failed");
+ return -1;
+ }
+
+ curl_easy_setopt(cb->curl, CURLOPT_ERRORBUFFER, cb->curl_errbuf);
+ curl_easy_setopt(cb->curl, CURLOPT_NOSIGNAL, 1L);
+ }
+
+ curl_easy_setopt(cb->curl, CURLOPT_POST, 1L);
+ curl_easy_setopt(cb->curl, CURLOPT_URL, url);
+
+ long timeout_ms = 2 * CDTIME_T_TO_MS(plugin_get_interval());
+ if (timeout_ms < 10000) {
+ timeout_ms = 10000;
+ }
+ curl_easy_setopt(cb->curl, CURLOPT_TIMEOUT_MS, timeout_ms);
+
+ /* header */
+ char *auth_header = wg_get_authorization_header(cb);
+ if (auth_header == NULL) {
+ ERROR("write_stackdriver plugin: getting access token failed with");
+ return -1;
+ }
+
+ struct curl_slist *headers =
+ curl_slist_append(NULL, "Content-Type: application/json");
+ headers = curl_slist_append(headers, auth_header);
+ curl_easy_setopt(cb->curl, CURLOPT_HTTPHEADER, headers);
+
+ curl_easy_setopt(cb->curl, CURLOPT_POSTFIELDS, payload);
+
+ curl_easy_setopt(cb->curl, CURLOPT_WRITEFUNCTION,
+ ret_content ? wg_write_memory_cb : NULL);
+ curl_easy_setopt(cb->curl, CURLOPT_WRITEDATA, ret_content);
+
+ int status = curl_easy_perform(cb->curl);
+
+ /* clean up that has to happen in any case */
+ curl_slist_free_all(headers);
+ sfree(auth_header);
+ curl_easy_setopt(cb->curl, CURLOPT_HTTPHEADER, NULL);
+ curl_easy_setopt(cb->curl, CURLOPT_WRITEFUNCTION, NULL);
+ curl_easy_setopt(cb->curl, CURLOPT_WRITEDATA, NULL);
+
+ if (status != CURLE_OK) {
+ ERROR("write_stackdriver plugin: POST %s failed: %s", url, cb->curl_errbuf);
+ if (ret_content != NULL) {
+ sfree(ret_content->memory);
+ ret_content->size = 0;
+ }
+ return -1;
+ }
+
+ long http_code = 0;
+ curl_easy_getinfo(cb->curl, CURLINFO_RESPONSE_CODE, &http_code);
+
+ if (ret_content != NULL) {
+ if ((http_code >= 400) && (http_code < 500)) {
+ ERROR("write_stackdriver plugin: POST %s: %s", url,
+ API_ERROR_STRING(parse_api_error(ret_content->memory)));
+ } else if (http_code >= 500) {
+ WARNING("write_stackdriver plugin: POST %s: %s", url,
+ ret_content->memory);
+ }
+ }
+
+ return (int)http_code;
+} /* int do_post */
+
+static int wg_call_metricdescriptor_create(wg_callback_t *cb,
+ char const *payload) {
+ char url[1024];
+ snprintf(url, sizeof(url), "%s/projects/%s/metricDescriptors", cb->url,
+ cb->project);
+ wg_memory_t response = {0};
+
+ int status = do_post(cb, url, payload, &response);
+ if (status == -1) {
+ ERROR("write_stackdriver plugin: POST %s failed", url);
+ return -1;
+ }
+ sfree(response.memory);
+
+ if (status != 200) {
+ ERROR("write_stackdriver plugin: POST %s: unexpected response code: got "
+ "%d, want 200",
+ url, status);
+ return -1;
+ }
+ return 0;
+} /* int wg_call_metricdescriptor_create */
+
+static int wg_call_timeseries_write(wg_callback_t *cb, char const *payload) {
+ char url[1024];
+ snprintf(url, sizeof(url), "%s/projects/%s/timeSeries", cb->url, cb->project);
+ wg_memory_t response = {0};
+
+ int status = do_post(cb, url, payload, &response);
+ if (status == -1) {
+ ERROR("write_stackdriver plugin: POST %s failed", url);
+ return -1;
+ }
+ sfree(response.memory);
+
+ if (status != 200) {
+ ERROR("write_stackdriver plugin: POST %s: unexpected response code: got "
+ "%d, want 200",
+ url, status);
+ return -1;
+ }
+ return 0;
+} /* int wg_call_timeseries_write */
+
+static void wg_reset_buffer(wg_callback_t *cb) /* {{{ */
+{
+ cb->timeseries_count = 0;
+ cb->send_buffer_init_time = cdtime();
+} /* }}} wg_reset_buffer */
+
+static int wg_callback_init(wg_callback_t *cb) /* {{{ */
+{
+ if (cb->curl != NULL)
+ return 0;
+
+ cb->formatter = sd_output_create(cb->resource);
+ if (cb->formatter == NULL) {
+ ERROR("write_stackdriver plugin: sd_output_create failed.");
+ return -1;
+ }
+
+ cb->curl = curl_easy_init();
+ if (cb->curl == NULL) {
+ ERROR("write_stackdriver plugin: curl_easy_init failed.");
+ return -1;
+ }
+
+ curl_easy_setopt(cb->curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(cb->curl, CURLOPT_USERAGENT,
+ PACKAGE_NAME "/" PACKAGE_VERSION);
+ curl_easy_setopt(cb->curl, CURLOPT_ERRORBUFFER, cb->curl_errbuf);
+ wg_reset_buffer(cb);
+
+ return 0;
+} /* }}} int wg_callback_init */
+
+static int wg_flush_nolock(cdtime_t timeout, wg_callback_t *cb) /* {{{ */
+{
+ if (cb->timeseries_count == 0) {
+ cb->send_buffer_init_time = cdtime();
+ return 0;
+ }
+
+ /* timeout == 0 => flush unconditionally */
+ if (timeout > 0) {
+ cdtime_t now = cdtime();
+
+ if ((cb->send_buffer_init_time + timeout) > now)
+ return 0;
+ }
+
+ char *payload = sd_output_reset(cb->formatter);
+ int status = wg_call_timeseries_write(cb, payload);
+ wg_reset_buffer(cb);
+ return status;
+} /* }}} wg_flush_nolock */
+
+static int wg_flush(cdtime_t timeout, /* {{{ */
+ const char *identifier __attribute__((unused)),
+ user_data_t *user_data) {
+ wg_callback_t *cb;
+ int status;
+
+ if (user_data == NULL)
+ return -EINVAL;
+
+ cb = user_data->data;
+
+ pthread_mutex_lock(&cb->lock);
+
+ if (cb->curl == NULL) {
+ status = wg_callback_init(cb);
+ if (status != 0) {
+ ERROR("write_stackdriver plugin: wg_callback_init failed.");
+ pthread_mutex_unlock(&cb->lock);
+ return -1;
+ }
+ }
+
+ status = wg_flush_nolock(timeout, cb);
+ pthread_mutex_unlock(&cb->lock);
+
+ return status;
+} /* }}} int wg_flush */
+
+static void wg_callback_free(void *data) /* {{{ */
+{
+ wg_callback_t *cb = data;
+ if (cb == NULL)
+ return;
+
+ sd_output_destroy(cb->formatter);
+ cb->formatter = NULL;
+
+ sfree(cb->email);
+ sfree(cb->project);
+ sfree(cb->url);
+
+ oauth_destroy(cb->auth);
+ if (cb->curl) {
+ curl_easy_cleanup(cb->curl);
+ }
+
+ sfree(cb);
+} /* }}} void wg_callback_free */
+
+static int wg_metric_descriptors_create(wg_callback_t *cb, const data_set_t *ds,
+ const value_list_t *vl) {
+ /* {{{ */
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ char buffer[4096];
+
+ int status = sd_format_metric_descriptor(buffer, sizeof(buffer), ds, vl, i);
+ if (status != 0) {
+ ERROR("write_stackdriver plugin: sd_format_metric_descriptor failed "
+ "with status "
+ "%d",
+ status);
+ return status;
+ }
+
+ status = wg_call_metricdescriptor_create(cb, buffer);
+ if (status != 0) {
+ ERROR("write_stackdriver plugin: wg_call_metricdescriptor_create failed "
+ "with "
+ "status %d",
+ status);
+ return status;
+ }
+ }
+
+ return sd_output_register_metric(cb->formatter, ds, vl);
+} /* }}} int wg_metric_descriptors_create */
+
+static int wg_write(const data_set_t *ds, const value_list_t *vl, /* {{{ */
+ user_data_t *user_data) {
+ wg_callback_t *cb = user_data->data;
+ if (cb == NULL)
+ return EINVAL;
+
+ pthread_mutex_lock(&cb->lock);
+
+ if (cb->curl == NULL) {
+ int status = wg_callback_init(cb);
+ if (status != 0) {
+ ERROR("write_stackdriver plugin: wg_callback_init failed.");
+ pthread_mutex_unlock(&cb->lock);
+ return status;
+ }
+ }
+
+ int status;
+ while (42) {
+ status = sd_output_add(cb->formatter, ds, vl);
+ if (status == 0) { /* success */
+ break;
+ } else if (status == ENOBUFS) { /* success, flush */
+ wg_flush_nolock(0, cb);
+ status = 0;
+ break;
+ } else if (status == EEXIST) {
+ /* metric already in the buffer; flush and retry */
+ wg_flush_nolock(0, cb);
+ continue;
+ } else if (status == ENOENT) {
+ /* new metric, create metric descriptor first */
+ status = wg_metric_descriptors_create(cb, ds, vl);
+ if (status != 0) {
+ break;
+ }
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if (status == 0) {
+ cb->timeseries_count++;
+ }
+
+ pthread_mutex_unlock(&cb->lock);
+ return status;
+} /* }}} int wg_write */
+
+static void wg_check_scope(char const *email) /* {{{ */
+{
+ char *scope = gce_scope(email);
+ if (scope == NULL) {
+ WARNING("write_stackdriver plugin: Unable to determine scope of this "
+ "instance.");
+ return;
+ }
+
+ if (strstr(scope, MONITORING_SCOPE) == NULL) {
+ size_t scope_len;
+
+ /* Strip trailing newline characers for printing. */
+ scope_len = strlen(scope);
+ while ((scope_len > 0) && (iscntrl((int)scope[scope_len - 1])))
+ scope[--scope_len] = 0;
+
+ WARNING("write_stackdriver plugin: The determined scope of this instance "
+ "(\"%s\") does not contain the monitoring scope (\"%s\"). You need "
+ "to add this scope to the list of scopes passed to gcutil with "
+ "--service_account_scopes when creating the instance. "
+ "Alternatively, to use this plugin on an instance which does not "
+ "have this scope, use a Service Account.",
+ scope, MONITORING_SCOPE);
+ }
+
+ sfree(scope);
+} /* }}} void wg_check_scope */
+
+static int wg_config_resource(oconfig_item_t *ci, wg_callback_t *cb) /* {{{ */
+{
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+ ERROR("write_stackdriver plugin: The \"%s\" option requires exactly one "
+ "string "
+ "argument.",
+ ci->key);
+ return EINVAL;
+ }
+ char *resource_type = ci->values[0].value.string;
+
+ if (cb->resource != NULL) {
+ sd_resource_destroy(cb->resource);
+ }
+
+ cb->resource = sd_resource_create(resource_type);
+ if (cb->resource == NULL) {
+ ERROR("write_stackdriver plugin: sd_resource_create(\"%s\") failed.",
+ resource_type);
+ return ENOMEM;
+ }
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp("Label", child->key) == 0) {
+ if ((child->values_num != 2) ||
+ (child->values[0].type != OCONFIG_TYPE_STRING) ||
+ (child->values[1].type != OCONFIG_TYPE_STRING)) {
+ ERROR("write_stackdriver plugin: The \"Label\" option needs exactly "
+ "two string arguments.");
+ continue;
+ }
+
+ sd_resource_add_label(cb->resource, child->values[0].value.string,
+ child->values[1].value.string);
+ }
+ }
+
+ return 0;
+} /* }}} int wg_config_resource */
+
+static int wg_config(oconfig_item_t *ci) /* {{{ */
+{
+ if (ci == NULL) {
+ return EINVAL;
+ }
+
+ wg_callback_t *cb = calloc(1, sizeof(*cb));
+ if (cb == NULL) {
+ ERROR("write_stackdriver plugin: calloc failed.");
+ return ENOMEM;
+ }
+ cb->url = strdup(GCM_API_URL);
+ pthread_mutex_init(&cb->lock, /* attr = */ NULL);
+
+ char *credential_file = NULL;
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+ if (strcasecmp("Project", child->key) == 0)
+ cf_util_get_string(child, &cb->project);
+ else if (strcasecmp("Email", child->key) == 0)
+ cf_util_get_string(child, &cb->email);
+ else if (strcasecmp("Url", child->key) == 0)
+ cf_util_get_string(child, &cb->url);
+ else if (strcasecmp("CredentialFile", child->key) == 0)
+ cf_util_get_string(child, &credential_file);
+ else if (strcasecmp("Resource", child->key) == 0)
+ wg_config_resource(child, cb);
+ else {
+ ERROR("write_stackdriver plugin: Invalid configuration option: %s.",
+ child->key);
+ wg_callback_free(cb);
+ return EINVAL;
+ }
+ }
+
+ /* Set up authentication */
+ /* Option 1: Credentials file given => use service account */
+ if (credential_file != NULL) {
+ oauth_google_t cfg =
+ oauth_create_google_file(credential_file, MONITORING_SCOPE);
+ if (cfg.oauth == NULL) {
+ ERROR("write_stackdriver plugin: oauth_create_google_file failed");
+ wg_callback_free(cb);
+ return EINVAL;
+ }
+ cb->auth = cfg.oauth;
+
+ if (cb->project == NULL) {
+ cb->project = cfg.project_id;
+ INFO("write_stackdriver plugin: Automatically detected project ID: "
+ "\"%s\"",
+ cb->project);
+ } else {
+ sfree(cfg.project_id);
+ }
+ }
+ /* Option 2: Look for credentials in well-known places */
+ if (cb->auth == NULL) {
+ oauth_google_t cfg = oauth_create_google_default(MONITORING_SCOPE);
+ cb->auth = cfg.oauth;
+
+ if (cb->project == NULL) {
+ cb->project = cfg.project_id;
+ INFO("write_stackdriver plugin: Automatically detected project ID: "
+ "\"%s\"",
+ cb->project);
+ } else {
+ sfree(cfg.project_id);
+ }
+ }
+
+ if ((cb->auth != NULL) && (cb->email != NULL)) {
+ NOTICE("write_stackdriver plugin: A service account email was configured "
+ "but is "
+ "not used for authentication because %s used instead.",
+ (credential_file != NULL) ? "a credential file was"
+ : "application default credentials were");
+ }
+
+ /* Option 3: Running on GCE => use metadata service */
+ if ((cb->auth == NULL) && gce_check()) {
+ wg_check_scope(cb->email);
+ } else if (cb->auth == NULL) {
+ ERROR("write_stackdriver plugin: Unable to determine credentials. Please "
+ "either "
+ "specify the \"Credentials\" option or set up Application Default "
+ "Credentials.");
+ wg_callback_free(cb);
+ return EINVAL;
+ }
+
+ if ((cb->project == NULL) && gce_check()) {
+ cb->project = gce_project_id();
+ }
+ if (cb->project == NULL) {
+ ERROR("write_stackdriver plugin: Unable to determine the project number. "
+ "Please specify the \"Project\" option manually.");
+ wg_callback_free(cb);
+ return EINVAL;
+ }
+
+ if ((cb->resource == NULL) && gce_check()) {
+ /* TODO(octo): add error handling */
+ cb->resource = sd_resource_create("gce_instance");
+ sd_resource_add_label(cb->resource, "project_id", gce_project_id());
+ sd_resource_add_label(cb->resource, "instance_id", gce_instance_id());
+ sd_resource_add_label(cb->resource, "zone", gce_zone());
+ }
+ if (cb->resource == NULL) {
+ /* TODO(octo): add error handling */
+ cb->resource = sd_resource_create("global");
+ sd_resource_add_label(cb->resource, "project_id", cb->project);
+ }
+
+ DEBUG("write_stackdriver plugin: Registering write callback with URL %s",
+ cb->url);
+ assert((cb->auth != NULL) || gce_check());
+
+ user_data_t user_data = {
+ .data = cb,
+ };
+ plugin_register_flush("write_stackdriver", wg_flush, &user_data);
+
+ user_data.free_func = wg_callback_free;
+ plugin_register_write("write_stackdriver", wg_write, &user_data);
+
+ return 0;
+} /* }}} int wg_config */
+
+static int wg_init(void) {
+ /* {{{ */
+ /* Call this while collectd is still single-threaded to avoid
+ * initialization issues in libgcrypt. */
+ curl_global_init(CURL_GLOBAL_SSL);
+
+ return 0;
+} /* }}} int wg_init */
+
+void module_register(void) /* {{{ */
+{
+ plugin_register_complex_config("write_stackdriver", wg_config);
+ plugin_register_init("write_stackdriver", wg_init);
+} /* }}} void module_register */
return -1;
}
+ // Ignore the first two lines because they contain information about
+ // the rest of the file.
+ // See kstat_seq_show_headers module/spl/spl-kstat.c of the spl kernel
+ // module.
+ if (fgets(buffer, sizeof(buffer), fh) == NULL) {
+ ERROR("zfs_arc plugin: \"%s\" does not contain a single line.",
+ ZOL_ARCSTATS_FILE);
+ fclose(fh);
+ return (-1);
+ }
+ if (fgets(buffer, sizeof(buffer), fh) == NULL) {
+ ERROR("zfs_arc plugin: \"%s\" does not contain at least two lines.",
+ ZOL_ARCSTATS_FILE);
+ fclose(fh);
+ return (-1);
+ }
+
while (fgets(buffer, sizeof(buffer), fh) != NULL) {
char *fields[3];
value_t v;
#!/bin/sh
-DEFAULT_VERSION="5.8.0.git"
+DEFAULT_VERSION="5.8.1.git"
if [ -d .git ]; then
VERSION="`git describe --dirty=+ --abbrev=7 2> /dev/null | grep collectd | sed -e 's/^collectd-//' -e 's/-/./g'`"