* 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
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.
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.])
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_vserver="no"
plugin_wireless="no"
plugin_write_prometheus="no"
+plugin_write_stackdriver="no"
plugin_xencpu="no"
plugin_zfs_arc="no"
plugin_zone="no"
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([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([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])
+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([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([ 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_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
# <Node example>
# Host "redis.example.com"
# Port "6379"
+# #Socket "/var/run/redis/redis.sock"
# Timeout 2000
# <Query "LLEN myqueue">
# #Database 0
# 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
=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
<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>.
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
=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>.
=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 */
--- /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) {
#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
#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;
}
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++;
}
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 */
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;
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;
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) {
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;
#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;
}
} /* }}} */
/* 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) */
} 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"
/* 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;
}
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",
iface->stats[rx_512_to_1023_packets],
iface->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",
iface->stats[rx_1024_to_1522_packets],
iface->stats[tx_1024_to_1522_packets], meta);
ovs_stats_submit_two(devname, "if_packets", "1523_to_max_packets",
ovs_stats_get_port_stat_value(port, rx_512_to_1023_packets),
ovs_stats_get_port_stat_value(port, tx_512_to_1023_packets), meta);
ovs_stats_submit_two(
- devname, "if_packets", "1024_to_1518_packets",
+ devname, "if_packets", "1024_to_1522_packets",
ovs_stats_get_port_stat_value(port, rx_1024_to_1522_packets),
ovs_stats_get_port_stat_value(port, tx_1024_to_1522_packets), meta);
ovs_stats_submit_two(
* is called after Interface Table update callback but before
* Port table Update callback. Will add this port on next read */
continue;
-
+
ovs_stats_submit_port(bridge, port);
if (interface_stats)
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);
}
{"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;
}
{
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);
#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;
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) {
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) */
} 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) {
/* 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: %s", STRERRNO);
+ 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
#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) {
#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;
}
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
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;
}
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'`"